1. Remi Meier
  2. pypy-stm-logging

Source

pypy-stm-logging / pypy / tool / plot_log.py

#!/usr/bin/python

##########################################################
""" TODO: print thread-descriptor info on commit/abort """
##########################################################

import matplotlib
matplotlib.use('gtkagg')

args = None
import matplotlib.pyplot as plt
# import pprint - slow as hell
from stmlogparser import parse, EventTypes


########## DRAWING STUFF ##########

BOX_HEIGHT = 0.8
HALF_HEIGHT = 0.1 + BOX_HEIGHT / 2
QUARTER_HEIGHT = 0.1 + BOX_HEIGHT / 4

def plot_boxes(boxes, y, ax):
    coords = [(x, w) for x, w, c, i in boxes]
    colors = [c for x, w, c, i in boxes]
    bars = ax.broken_barh(coords, (y+0.1, BOX_HEIGHT),
                          facecolors=colors,
                          picker=True, antialiased=False, rasterized=True)

    bars.boxes = boxes

def plot_hlines(hlines, y, ax):
    args = [[[x1, x2], [y+HALF_HEIGHT, y+HALF_HEIGHT], color] for
            x1, x2, color in hlines]
    # flatten:
    args = [item for sublist in args for item in sublist]
    ax.plot(*args, linewidth=3, antialiased=False, rasterized=True)

####################################

def add_box(boxes, x1, x2, color, info):
    boxes.append((x1, x2-x1, color, info))

def add_hline(hlines, x1, x2, color):
    hlines.append((x1, x2, color))


def add_transaction(boxes, hlines, inited, inevitabled,
                     ended, aborted, atomics, info=""):
    assert inited != None

    if inevitabled != None:
        add_box(boxes, inited, inevitabled, 'b', info)
        add_box(boxes, inevitabled, ended, 'orange', info)
    elif not aborted:
        add_box(boxes, inited, ended, 'g', info)
    else:
        add_box(boxes, inited, ended, 'r', info)

    for start, end in atomics:
        add_hline(hlines, start, end, 'magenta')


def plot_logs(logs, ax):
    plt.ion()
    for tid in logs.keys():
        plt.draw()
        plt.show()

        inited = None
        inevitabled = None
        info = []
        start_time = 0
        end_time = 0
        boxes = []
        hlines = []
        atomics = []
        should_commits = []
        current_atomic = 0
        program_aborted = None
        for time, kind, line_dict in logs[tid]:
            if args.atomic or kind not in (EventTypes.INC_ATOMIC,
                                           EventTypes.DEC_ATOMIC):
                info.append(str(line_dict))

            if kind == EventTypes.INIT_TRANSACTION:
                inited = time
                info = ["Thread: %s\n%s" % (tid, str(line_dict))]
            elif kind == EventTypes.MAKE_INEVITABLE:
                if not inevitabled: inevitabled = time
            elif kind == EventTypes.COMMIT_TRANSACTION:
                add_transaction(boxes, hlines, inited, inevitabled,
                                time, False,
                                atomics, "\n".join(info))
                inited, inevitabled = None, None
            elif kind in (EventTypes.ABORT_TRANSACTION,
                          EventTypes.INEVITABLE_INSIDE_ATOMIC):
                if kind == EventTypes.INEVITABLE_INSIDE_ATOMIC:
                    program_aborted = time
                if current_atomic and args.atomic:
                    atomics[-1] = (atomics[-1][0], time)
                    current_atomic = 0
                add_transaction(boxes, hlines, inited, inevitabled,
                                time, True,
                                atomics, "\n".join(info))
                inited, inevitabled = None, None
            elif kind == EventTypes.INC_ATOMIC:
                if current_atomic == 0 and args.atomic:
                    atomics.append((time,))
                current_atomic += 1
            elif kind == EventTypes.DEC_ATOMIC:
                current_atomic -= 1
                if current_atomic == 0 and args.atomic:
                    atomics[-1] = (atomics[-1][0], time)
            elif kind == EventTypes.DESCRIPTOR_INIT:
                start_time = time
            elif kind == EventTypes.DESCRIPTOR_DONE:
                k = ax.plot(start_time, tid+HALF_HEIGHT, 'c>',
                            time, tid+HALF_HEIGHT, 'c<', markersize=8,
                            picker=True, antialiased=False, rasterized=True)
                [setattr(m, "info", str(tid)) for m in k]
                end_time = time
            elif kind == EventTypes.SHOULD_COMMIT:
                should_commits.append(time)

        if current_atomic and args.atomic:
            print "ERROR: Finished transaction is still atomic"

        plot_boxes(boxes, tid, ax)
        plot_hlines(hlines, tid, ax)

        if should_commits:
            ax.plot(should_commits, [tid+QUARTER_HEIGHT] * len(should_commits),
                    'y+', mew=2, markersize=8,
                    antialiased=False, rasterized=True)

        if end_time == 0:
            k = ax.plot(start_time, tid+HALF_HEIGHT, 'c>',
                        time, tid+HALF_HEIGHT, 'y*', markersize=8,
                        picker=True, antialiased=False, rasterized=True)
            [setattr(m, "info", str(tid)) for m in k]

        if program_aborted is not None:
            k = ax.plot(program_aborted, tid+HALF_HEIGHT, 'wo',
                        markersize=15, mew=3, mec='r',
                        picker=True, antialiased=False, rasterized=True)
            [setattr(m, "info", str(tid)) for m in k]

    plt.ioff()


def onpick(event):
    if hasattr(event.artist, "info"):
        print "== pick ==\n", event.artist.info
    if hasattr(event.artist, "boxes"):
        x = event.mouseevent.xdata
        for x1, w, c, i in event.artist.boxes:
            if x >= x1 and x <= x1+w:
                print "== pick ==\n", i
                break
        else:
            print "== pick ==\nCould not find info"


############## MAIN ################

def main():
    global fig

    fig = plt.figure()
    ax = fig.add_subplot(111)

    print "Parse..."
    logs = parse(args.file)
    print "Parsed."

    print "Draw..."
    plot_logs(logs, ax)
    print "Drawn."

    plt.xlabel("Runtime [s]")
    plt.ylabel("Thread")
    plt.yticks([r+0.5 for r in range(len(logs))],
               range(1, len(logs)+1))

    left = min([l[0][0] for l in logs.values()])
    right = max([l[-1][0] for l in logs.values()])
    #left, right = ax.get_xlim()
    ax.set_xlim((left, right))
    xticks = ax.get_xticks()
    ax.set_xticklabels(["%.2f" % ((t - left) * 1e-6,) for t in xticks])

    # legend-drawing
    from matplotlib.font_manager import FontProperties
    fontP = FontProperties()
    fontP.set_size('small')

    from matplotlib.patches import Rectangle
    from matplotlib.lines import  Line2D
    h = []; l=[]
    l += ['Successful Transaction']; h += [Rectangle((0, 0), 1, 1, fc="g")]
    l += ['Transaction turning inevitable']
    h += [Rectangle((0, 0), 1, 1, fc="b")]
    l += ['Transaction turned inevitable']
    h += [Rectangle((0, 0), 1, 1, fc="orange")]
    l += ['Aborted Transaction']; h += [Rectangle((0, 0), 1, 1, fc="r")]
    l += ['Thread Start']
    h += [Line2D((0,0),(0,0), marker='>', mfc='c', ls='', ms=8)]
    l += ['Thread End']
    h += [Line2D((0,0),(0,0), marker='<', mfc='c', ls='', ms=8)]
    l += ['Thread Continuing']
    h += [Line2D((0,0),(0,0), marker='*', mfc='y', ls='', ms=8)]
    l += ['Program Abort']
    h += [Line2D((0,0),(0,0), marker='o', mfc='w', ls='', ms=15, mew=3,mec='r')]
    l += ['Should Commit']
    h += [Line2D((0,0),(0,0), marker='+', mec='y', ls='', ms=8, mew=2,)]
    if args.atomic:
        l += ['Atomic block']
        h += [Line2D((0,0),(0,0), c='magenta', lw=3)]

    box = ax.get_position()
    ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])

    ax.legend(h, l, numpoints=1,
              bbox_to_anchor=(1.01, 1), loc=2,
              prop=fontP)

    # event connect
    fig.canvas.mpl_connect('pick_event', onpick)

    plt.draw()
    plt.show()


if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description='Plot stm log files')
    parser.add_argument('file', help='logfile to parse')
    parser.add_argument('--atomic', action='store_const',
                        const=True, default=False,
                        help="plot atomic phases (slower)")

    args = parser.parse_args()

    main()