Skip to content

Commit 2d8fa20

Browse files
author
Armand Foucault
committed
Prepared flame graph mode
1 parent 2924fd2 commit 2d8fa20

1 file changed

Lines changed: 105 additions & 1 deletion

File tree

mprof.py

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,105 @@ def plot_file(filename, index=0, timestamps=True, children=True, options=None):
456456
return mprofile
457457

458458

459+
def flame_plotter(filename, index=0, timestamps=True, children=True, options=None):
460+
try:
461+
import pylab as pl
462+
except ImportError as e:
463+
print("matplotlib is needed for plotting.")
464+
print(e)
465+
sys.exit(1)
466+
import numpy as np # pylab requires numpy anyway
467+
mprofile = read_mprofile_file(filename)
468+
469+
if len(mprofile['timestamp']) == 0:
470+
print('** No memory usage values have been found in the profile '
471+
'file.**\nFile path: {0}\n'
472+
'File may be empty or invalid.\n'
473+
'It can be deleted with "mprof rm {0}"'.format(
474+
mprofile['filename']))
475+
sys.exit(0)
476+
477+
# Merge function timestamps and memory usage together
478+
ts = mprofile['func_timestamp']
479+
t = mprofile['timestamp']
480+
mem = mprofile['mem_usage']
481+
chld = mprofile['children']
482+
483+
if len(ts) > 0:
484+
for values in ts.values():
485+
for v in values:
486+
t.extend(v[:2])
487+
mem.extend(v[2:4])
488+
489+
mem = np.asarray(mem)
490+
t = np.asarray(t)
491+
ind = t.argsort()
492+
mem = mem[ind]
493+
t = t[ind]
494+
495+
# Plot curves
496+
global_start = float(t[0])
497+
t = t - global_start
498+
499+
max_mem = mem.max()
500+
max_mem_ind = mem.argmax()
501+
502+
all_colors = ("c", "y", "g", "r", "b")
503+
mem_line_colors = ("k", "b", "r", "g", "c", "y", "m")
504+
mem_line_label = time.strftime("%d / %m / %Y - start at %H:%M:%S",
505+
time.localtime(global_start)) \
506+
+ ".{0:03d}".format(int(round(math.modf(global_start)[0] * 1000)))
507+
508+
pl.plot(t, mem, "+-" + mem_line_colors[index % len(mem_line_colors)],
509+
label=mem_line_label)
510+
511+
bottom, top = pl.ylim()
512+
bottom += 0.001
513+
top -= 0.001
514+
515+
# plot children, if any
516+
if len(chld) > 0 and children:
517+
cmpoint = (0,0) # maximal child memory
518+
519+
for idx, (proc, data) in enumerate(chld.items()):
520+
# Create the numpy arrays from the series data
521+
cts = np.asarray([item[1] for item in data]) - global_start
522+
cmem = np.asarray([item[0] for item in data])
523+
524+
# Plot the line to the figure
525+
pl.plot(cts, cmem, "+-" + mem_line_colors[(idx+1) % len(mem_line_colors)],
526+
label="child {}".format(proc))
527+
528+
# Detect the maximal child memory point
529+
cmax_mem = cmem.max()
530+
if cmax_mem > cmpoint[1]:
531+
cmpoint = (cts[cmem.argmax()], cmax_mem)
532+
533+
# Add the marker lines for the maximal child memory usage
534+
pl.vlines(cmpoint[0], pl.ylim()[0]+0.001, pl.ylim()[1] - 0.001, 'r', '--')
535+
pl.hlines(cmpoint[1], pl.xlim()[0]+0.001, pl.xlim()[1] - 0.001, 'r', '--')
536+
537+
# plot timestamps, if any
538+
if len(ts) > 0 and timestamps:
539+
func_num = 0
540+
f_labels = function_labels(ts.keys())
541+
for f, exec_ts in ts.items():
542+
for execution in exec_ts:
543+
add_brackets(execution[:2], execution[2:], xshift=global_start,
544+
color=all_colors[func_num % len(all_colors)],
545+
label=f_labels[f]
546+
+ " %.3fs" % (execution[1] - execution[0]), options=options)
547+
func_num += 1
548+
549+
if timestamps:
550+
pl.hlines(max_mem,
551+
pl.xlim()[0] + 0.001, pl.xlim()[1] - 0.001,
552+
colors="r", linestyles="--")
553+
pl.vlines(t[max_mem_ind], bottom, top,
554+
colors="r", linestyles="--")
555+
return mprofile
556+
557+
459558
def function_labels(dotted_function_names):
460559
state = {}
461560

@@ -507,6 +606,8 @@ def xlim_type(value):
507606
help="Save plot to file instead of displaying it.")
508607
parser.add_argument("--window", "-w", dest="xlim", type=xlim_type,
509608
help="Plot a time-subset of the data. E.g. to plot between 0 and 20.5 seconds: --window 0,20.5")
609+
parser.add_argument("--flame", "-f", dest="flame_mode", action="store_true",
610+
help="Plot the timestamps as a flame-graph instead of the default brackets")
510611
parser.add_argument("--backend",
511612
help="Specify the Matplotlib backend to use")
512613
parser.add_argument("profiles", nargs="*",
@@ -561,8 +662,11 @@ def xlim_type(value):
561662
timestamps = False
562663
else:
563664
timestamps = True
665+
plotter = plot_file
666+
if args.flame_mode:
667+
plotter = flame_plotter
564668
for n, filename in enumerate(filenames):
565-
mprofile = plot_file(filename, index=n, timestamps=timestamps, options=args)
669+
mprofile = plotter(filename, index=n, timestamps=timestamps, options=args)
566670
pl.xlabel("time (in seconds)")
567671
pl.ylabel("memory used (in MiB)")
568672

0 commit comments

Comments
 (0)