Add y scale on the right side of plot
[lttng-ci.git] / scripts / babeltrace-benchmark / time.py
CommitLineData
5c65bbc2
JR
1#!/usr/bin/python3
2# Copyright (C) 2019 - Jonathan Rajotte Julien <jonathan.rajotte-julien@efficios.com>
3#
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8#
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17import argparse
18import os
19import subprocess
20import tempfile
21import json
22from collections import defaultdict
23
24_METRIC = {
25 "User time (seconds)": float,
26 "System time (seconds)": float,
27 "Percent of CPU this job got": percent_parser,
28 "Elapsed (wall clock) time (h:mm:ss or m:ss)": wall_clock_parser,
29 "Average shared text size (kbytes)": int,
30 "Average unshared data size (kbytes)": int,
31 "Average stack size (kbytes)": int,
32 "Average total size (kbytes)": int,
33 "Maximum resident set size (kbytes)": int,
34 "Average resident set size (kbytes)": int,
35 "Major (requiring I/O) page faults": int,
36 "Minor (reclaiming a frame) page faults": int,
37 "Voluntary context switches": int,
38 "Involuntary context switches": int,
39 "Swaps": int,
40 "File system inputs": int,
41 "File system outputs": int,
42 "Socket messages sent": int,
43 "Socket messages received": int,
44 "Signals delivered": int,
45 "Page size (bytes)": int,
46}
47
48
49def wall_clock_parser(value):
50 """
51 Parse /usr/bin/time wall clock value.
52 Wall clock value is expressed in different formats depending on the actual
53 elapsed time.
54 """
55 total = 0.0
56 pos = value.find(".")
57 if value.find("."):
58 total += float(value[pos:])
59 value = value[:pos]
60
61 v_split = value.split(":")
62 if len(v_split) == 2:
63 total += float(v_split[0]) * 60.0
64 total += float(v_split[1]) * 1.0
65 elif len(v_split) == 3:
66 total += float(v_split[0]) * 360.0
67 total += float(v_split[1]) * 60.0
68 total += float(v_split[2]) * 1.0
69 else:
70 return 0.0
71
72 return total
73
74
75def percent_parser(value):
76 """
77 Parse /usr/bin/time percent value.
78 """
79 parsed = value.replace("%", "").replace("?", "")
80 if parsed:
81 return float(parsed)
82 return 0
83
84
85def parse(path, results):
86 """
87 Parser and accumulator for /usr/bin/time results.
88 """
89 with open(path, "r") as data:
90 for line in data:
91 if line.rfind(":") == -1:
92 continue
93 key, value = line.lstrip().rsplit(": ")
94 if key in _METRIC:
95 results[key].append(_METRIC[key](value))
96
97 return results
98
99
100def save(path, results):
101 """
102 Save the result in json format to path.
103 """
104 with open(path, "w") as out:
105 json.dump(results, out, sort_keys=True, indent=4)
106
107
108def run(command, iteration, output, stdout, stderr):
109 """
110 Run the command throught /usr/bin/time n iterations and parse each result.
111 """
112 results = defaultdict(list)
113 for i in range(iteration):
114 time_stdout = tempfile.NamedTemporaryFile(delete=False)
115 # We must delete this file later on.
116 time_stdout.close()
117 with open(stdout, "a+") as out, open(stderr, "a+") as err:
118 cmd = "/usr/bin/time -v --output='{}' {}".format(time_stdout.name, command)
119 ret = subprocess.run(cmd, shell=True, stdout=out, stderr=err)
120 if ret.returncode != 0:
121 print("Iteration: {}, Command failed: {}".format(str(i), cmd))
122 results = parse(time_stdout.name, results)
123 os.remove(time_stdout.name)
124 save(output, results)
125
126
127def main():
128 """
129 Run /usr/bin/time N time and collect the result.
130 The resulting json have the following form:
131 {
132 "/usr/bin/time": {
133 "User time (seconds)": [],
134 "System time (seconds)": [],
135 "Percent of CPU this job got": [],
136 "Elapsed (wall clock) time (h:mm:ss or m:ss)": [],
137 "Average shared text size (kbytes)": [],
138 "Average unshared data size (kbytes)": [],
139 "Average stack size (kbytes)": [],
140 "Average total size (kbytes)": [],
141 "Maximum resident set size (kbytes)": [],
142 "Average resident set size (kbytes)": [],
143 "Major (requiring I/O) page faults": [],
144 "Minor (reclaiming a frame) page faults": [],
145 "Voluntary context switches": [],
146 "Involuntary context switches": [],
147 "Swaps": [],
148 "File system inputs": [],
149 "File system outputs": [],
150 "Socket messages sent": [],
151 "Socket messages received": [],
152 "Signals delivered": [],
153 "Page size (bytes)": [],
154 }
155 }
156 """
157 parser = argparse.ArgumentParser(
158 description="Run command N time using /usr/bin/time and collect the statistics"
159 )
160 parser.add_argument("--output", help="Where to same the result", required=True)
161 parser.add_argument("--command", help="The command to benchmark", required=True)
162 parser.add_argument(
163 "--iteration",
164 type=int,
165 default=5,
166 help="The number of iteration to run the command (default: 5)",
167 required=True,
168 )
169 parser.add_argument(
170 "--stdout",
171 default="/dev/null",
172 help="Where to append the stdout of each command (default: /dev/null)",
173 )
174 parser.add_argument(
175 "--stderr",
176 default=os.path.join(os.getcwd(), "stderr.out"),
177 help="Where to append the stderr of each command (default: $CWD/stderr.out)",
178 )
179
180 args = parser.parse_args()
181 run(args.command, args.iteration, args.output, args.stdout, args.stderr)
182
183
184if __name__ == "__main__":
185 main()
This page took 0.028265 seconds and 4 git commands to generate.