-
Notifications
You must be signed in to change notification settings - Fork 3
/
msprint.py
268 lines (229 loc) · 8.57 KB
/
msprint.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#!/usr/bin/env python3
# Copyright (c) 2011 Mathieu Turcotte
# Licensed under the MIT license.
import json
import msparser
import optparse
import os
import re
import sys
import traceback
def inst_unit_scaling(peak):
"""
Given the peak instruction value to plot, get a scaling factor which will
divide the data point and the resulting unit name. These information are
used to scale the data and set the axis label in the print_gnuplot_script
function.
"""
unit_table = [
(2 ** 0, "i"),
(2 ** 10, "ki"),
(2 ** 20, "Mi"),
(2 ** 30, "Gi"),
(2 ** 40, "Ti"),
(2 ** 50, "Pi"),
(2 ** 60, "Ei")
]
for value, name in unit_table:
if peak // value < 2 ** 10:
return (value, name)
def time_unit_scaling(peak):
"""
Given the peak time value to plot, get a scaling factor which will divide
the data point and the resulting unit name. These information are used to
scale the data and set the axis label in the print_gnuplot_script function.
"""
if peak // 1000 < 1:
return (1, "milliseconds (ms)")
elif 1 <= peak // 1000 < 60:
return (1000, "seconds (s)")
else:
return (60000, "minutes (m)")
def memory_unit_scaling(peak):
"""
Given the peak memory value to plot, get a scaling factor which will divide
the data point and the resulting unit name. These information are used to
scale the data and set the axis label in the print_gnuplot_script function.
"""
unit_table = [
(2 ** 0, "bytes (B)"),
(2 ** 10, "kibibytes (KiB)"),
(2 ** 20, "mebibytes (MiB)"),
(2 ** 30, "gibibytes (GiB)"),
(2 ** 40, "tebibytes (TiB)"),
(2 ** 50, "pebibytes (PiB)"),
(2 ** 60, "exbibytes (EiB)")
]
for value, name in unit_table:
if peak // value < 2 ** 10:
return (value, name)
def print_as_json(mdata, indent):
"""
Print mdata as json. If indent is true, the outputed json is indented.
"""
if indent:
print(json.dumps(mdata, indent=1))
else:
print(json.dumps(mdata))
def print_gnuplot_dtable(mdata):
"""
Print mdata as a data table ready for gnuplot consumption.
"""
print("# ms_processor.py - (C) Mathieu Turcotte, 2011")
print("# valgrind --tool=massif", mdata["desc"], mdata["cmd"])
print("# id", "time", "heap", "extra", "total", "stack", sep="\t")
for snapshot in mdata["snapshots"]:
id = snapshot["id"]
time = snapshot["time"]
heap = snapshot["mem_heap"]
extra = snapshot["mem_heap_extra"]
total = heap + extra
stack = snapshot["mem_stack"]
print(" " + str(id), time, heap, extra, total, stack, sep="\t")
GNUPLOT_HEADER = """\
# msprint.py - (C) Mathieu Turcotte, 2011
# yscale: {yscale}
# xscale: {xscale}
set terminal {format} giant size {xsize}, {ysize}
set output "{filename}.{extension}"
set title "valgrind --tool=massif {description} {command}"
set xlabel "{xlabel}"
set ylabel "{ylabel}"
set mxtics 10
set mytics 10
set grid
plot "-" using 1:2 title "Useful Heap",\\
"-" using 1:2 title "Wasted Heap",\\
"-" using 1:2 title "Total Heap"
"""
def print_gnuplot_script(mdata, filename, format="png", xsize=1024, ysize=768):
"""
Print mdata as a gnuplot batch script which, when executed, will produce a
plot of the massif.out data.
"""
# Retrieve the time peak and determine the y axis scale and label.
peak_snapshot_id = mdata["peak_snapshot"]
peak_snapshot = mdata["snapshots"][peak_snapshot_id]
memory_peak = peak_snapshot["mem_heap"] + peak_snapshot["mem_heap_extra"]
(yscale, ylabel) = memory_unit_scaling(memory_peak)
# Retrieve the time peak and the time unit in order to compute the x
# axis scale and label.
time_peak = mdata["snapshots"][-1]["time"]
time_unit = mdata["time_unit"]
if time_unit == "B":
(xscale, xlabel) = memory_unit_scaling(time_peak)
elif time_unit == "ms":
(xscale, xlabel) = time_unit_scaling(time_peak)
elif time_unit == "i":
(xscale, xlabel) = inst_unit_scaling(time_peak)
else:
raise Exception("Can't handle time unit.")
# Output the gnuplot script header.
print(GNUPLOT_HEADER.format(
format=format,
filename=filename,
extension=format,
description=mdata["desc"],
command=mdata["cmd"],
xsize=xsize,
ysize=ysize,
xscale=xscale,
yscale=yscale,
xlabel=xlabel,
ylabel=ylabel
))
# Output the useful heap data.
for snapshot in mdata["snapshots"]:
print(snapshot["time"] / xscale,
snapshot["mem_heap"] / yscale, sep="\t")
print("end")
# Then, output the wasted heap data.
for snapshot in mdata["snapshots"]:
print(snapshot["time"] / xscale,
snapshot["mem_heap_extra"] / yscale, sep="\t")
print("end")
# Finally, output the total heap data.
for snapshot in mdata["snapshots"]:
total = snapshot["mem_heap"] + snapshot["mem_heap_extra"]
print(snapshot["time"] / xscale,
total / yscale, sep="\t")
print("end")
def parse_args():
usage = "usage: %prog [options] massif-out-file"
description = "Extraction utility for the massif.out data format."
version = "%prog 1.0"
argparser = optparse.OptionParser(description=description,
usage=usage, version=version)
argparser.add_option("-o", "--output",
dest="output",
default="table",
choices=["json", "gnuplot", "table", "graphviz"],
metavar="F",
help="specify the output format: "
"json, gnuplot, graphviz or table")
json_group = optparse.OptionGroup(argparser, "JSON Options")
json_group.add_option("-i", "--indent",
action="store_true",
dest="indent",
help="indent the json output")
argparser.add_option_group(json_group)
graphviz_group = optparse.OptionGroup(argparser, "Graphviz Options")
graphviz_group.add_option("--snapshot",
type="int",
dest="snapshot",
metavar="ID",
help="output Graphviz script for a given "
"snapshot")
argparser.add_option_group(graphviz_group)
gnuplot_group = optparse.OptionGroup(argparser, "GNUPlot Options")
gnuplot_group.add_option("-f", "--format",
dest="format",
default="png",
choices=["png", "gif", "jpeg"],
metavar="F",
help="specify the plot output format: "
"png, jpeg or gif")
gnuplot_group.add_option("-x", "--xsize",
type="int",
dest="xsize",
default=1024,
metavar="X",
help="plot horizontal size")
gnuplot_group.add_option("-y", "--ysize",
type="int",
dest="ysize",
default=768,
metavar="Y",
help="plot vertical size")
argparser.add_option_group(gnuplot_group)
# - options contains optional arguments
# - args contains positional arguments
(options, args) = argparser.parse_args()
if len(args) == 0:
argparser.error("No input file !")
for path in args[0:]:
if os.path.isfile(path) is False:
argparser.error(path)
return (options, args)
def main():
(options, args) = parse_args()
for path in args[0:]:
try:
mdata = msparser.parse_file(path)
if options.output == "json":
print_as_json(mdata, options.indent)
elif options.output == "gnuplot":
print_gnuplot_script(mdata, os.path.basename(path),
options.format, options.xsize,
options.ysize)
elif options.output == "table":
print_gnuplot_dtable(mdata)
except ParseError as perr:
print(perr, file=sys.stderr)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
pass
except Exception as err:
traceback.print_exc(file=sys.stdout)