-
Notifications
You must be signed in to change notification settings - Fork 2
/
perf.py
255 lines (224 loc) · 9.52 KB
/
perf.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
import subprocess
from logger import create_logger
from datetime import datetime
import sys
from collections import OrderedDict
from timeit import default_timer as timer
from logging import getLogger
from logger import create_logger
from glob import glob
from time import sleep
from threading import Thread
import os
import json
import signal
class ReturnException(Exception):
def __init__(self, return_value):
self.return_value = return_value
def execute(cmd):
popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, bufsize=1)
for stdout_line in iter(popen.stdout.readline, ""):
yield stdout_line
for stderr_line in iter(popen.stderr.readline, ""):
yield stderr_line
popen.stdout.close()
return_code = popen.wait()
raise ReturnException(return_code)
def run_command(command, logger=None):
if logger is None:
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
return process.returncode, process.stdout.read() + process.stderr.read()
else:
output = ""
logger.info("------Executing command " + " ".join(command) + "------")
try:
for line in execute(command):
logger.debug(line.rstrip())
output += line
except ReturnException as re:
logger.info("------Finished command " + " ".join(command) + "-------")
return re.return_value, output
class Perf:
"""
Class to measure performance counters using the perf command in linux
"""
def __init__(self, counters=None, command=None, name=None, log_handle=None):
self.contious_result = {}
self.counters = counters
self.command = command
self.repeat_factor = 5
# logname = "perf_{time}.log"
# now = datetime.now()
# logname = logname.format(time = now.strftime("%m_%d_%Y__%H_%M_%S"))
# if log_handle is None:
# self.logger = create_logger(logname)
# else:
# self.logger = log_handle
self.logger = getLogger("main_log")
self.name = name
self.phase_dir = "phases"
self.interval = 1000 # milli seconds
def set_counters(self, counters):
self.counters = counters
def set_command(self, command):
self.command = command
def set_logger(self, logger):
self.logger = logger
def set_repeat_factor(self, repeat_factor):
self.repeat_factor = repeat_factor
def run(self):
counters_to_measure = list(self.counters)
result = OrderedDict()
while counters_to_measure:
perf_command = ["perf", "stat", "-x", ";", "-r", str(self.repeat_factor), "-e",
",".join(counters_to_measure)]
perf_command.extend(self.command.split(" "))
start = timer()
return_value, output = run_command(perf_command, self.logger)
end = timer()
if return_value != 0:
return None
output = output.split("\n")
for counter in self.counters:
for line in output:
if counter in line:
fields = line.split(";")
value = fields[0]
key = fields[2]
if "<not counted>" in value:
self.logger.info("Counter %s is not counted " % key)
else:
value = int(value.replace(",", ""))
counters_to_measure.remove(key)
result[key] = value
result["time"] = float(end - start) / self.repeat_factor
return result
def poll_output(self, process):
for stdout_line in iter(process.stdout.readline, ""):
self.logger.debug(stdout_line.rstrip())
for stderr_line in iter(process.stderr.readline, ""):
self.logger.debug(stderr_line.rstrip())
def parse_output(self, output, phase_name):
output = output.split("\n")
result = OrderedDict()
for counter in self.counters:
for line in output:
if counter in line:
fields = line.split(";")
value = fields[0]
key = fields[2]
if "<not counted>" in value:
self.logger.info("Counter %s is not counted " % key)
else:
value = int(value.replace(",", ""))
result[key] = value
# result["time"] = float(end - start) / self.repeat_factor
result["name"] = self.name
result["phase"] = phase_name
return result
def set_phase_dir(self, directory):
self.phase_dir = directory
def get_phase_counters(self, cmd):
total_phases = 0
not_counted = False
while cmd.poll() is None:
self.logger.info("Checking file")
while cmd.poll() is None and not glob(self.phase_dir + "/*"):
sleep(1)
if cmd.poll() is not None:
break
total_phases += 1
phase_name = glob(self.phase_dir + "/*")[0]
pid = open(phase_name, "r").read().strip()
if not pid:
pid = cmd.pid
result = None
if phase_name in self.result.keys():
result = self.result[phase_name]
else:
result = OrderedDict()
counters_left = [item for item in self.counters if item not in result.keys()]
self.logger.info("File found")
self.logger.info("Coutners measured are " + ",".join(result.keys()))
self.logger.info("Measuring counters for the phase :" + phase_name)
self.logger.info("counters left to measure are : " + ",".join(counters_left))
cmd_start_perf = ["perf", "stat", "-x", ";", "-e",
",".join(counters_left), "-p", str(pid)]
self.logger.info(" ".join(cmd_start_perf))
start = timer()
perf = subprocess.Popen(cmd_start_perf, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
while cmd.poll() is None and glob(phase_name):
sleep(1)
self.logger.info("File deleted")
end = timer()
os.system("pkill -2 perf")
# perf.send_signal(signal.SIGINT)
output = perf.stdout.read() + perf.stderr.read()
self.logger.info(output)
output = output.split("\n")
for counter in counters_left:
for line in output:
if counter in line:
fields = line.split(";")
value = fields[0]
key = fields[2]
if "<not counted>" in line:
self.logger.info("Counter %s is not counted " % key)
not_counted = True
else:
value = int(value.replace(",", ""))
result[key] = value
result["name"] = self.name
result["phase"] = phase_name
result["time"] = float(end - start)
self.result[phase_name] = result
self.logger.info(json.dumps(self.result, indent=4))
return not_counted
def run_phased(self):
self.result = {}
not_counted = True
while not_counted:
self.logger.info("Executing phases")
cmd = subprocess.Popen(self.command.split(" "), stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True, bufsize=1)
poll = Thread(target=self.poll_output, args=(cmd,))
poll.start()
not_counted = self.get_phase_counters(cmd)
cmd.wait()
return self.result.values()
def run_continuous(self):
cmd = subprocess.Popen(self.command.split(" "), stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
universal_newlines=True, bufsize=1)
pid = cmd.pid
poll = Thread(target=self.poll_output, args=(cmd,))
poll.start()
cmd_start_perf = ["perf", "stat", "-x", ";", "-I", str(self.interval), "-e",
",".join(self.counters), "-o", "perf_outfile", "-p", str(pid)]
self.logger.info(" ".join(cmd_start_perf))
perf = subprocess.Popen(cmd_start_perf, stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
cmd.wait()
perf.send_signal(signal.SIGINT)
perf_out = open("perf_outfile", "r").read()
# perf_out = perf.stdout.read() + perf.stderr.read()
perf_out = [line.strip() for line in perf_out.split("\n")
if line.strip() and line.strip()[0] != "#"]
for counter in self.counters:
self.contious_result[counter] = []
for line in perf_out:
line = line.strip()
fields = line.split(";")
value = float(fields[1])
key = fields[3]
self.contious_result[key].append(value)
return self.contious_result
if __name__ == "__main__":
logger = create_logger()
cmd = "/home/prathyushpv/work/cpu2017/bin/runcpu --config=config1.cfg --nobuild --threads 1 --size test 628.pop2_s"
p = Perf(['LLC-load-misses',
'LLC-store-misses'], cmd)
result = p.run_continuous()
print(result)