Update version to v2.1.0-rc5
[lttng-tools.git] / tests / run-report.py
1 #!/usr/bin/env python2
2
3 import os, sys
4 import subprocess
5 import threading
6 import Queue
7 import time
8
9 from signal import signal, SIGTERM, SIGINT
10
11 SESSIOND_BIN_NAME = "lttng-sessiond"
12 SESSIOND_BIN_PATH = "src/bin/lttng-sessiond/.libs/"
13 TESTDIR_PATH = ""
14
15 PRINT_BRACKET = "\033[1;34m[\033[1;33m+\033[1;34m]\033[00m"
16 PRINT_RED_BRACKET = "\033[1;31m[+]\033[00m"
17 PRINT_GREEN_BRACKET = "\033[1;32m[+]\033[00m"
18 PRINT_ARROW = "\033[1;32m-->\033[00m"
19
20 is_root = 1
21 no_stats = 0
22 stop_sampling = 1
23
24 top_cpu_legend = { 'us': "User CPU time", 'sy': "System CPU time",
25 'id': "Idle CPU time", 'ni': "Nice CPU time", 'wa': "iowait",
26 'hi': "Hardware IRQ", 'si': "Software Interrupts", 'st': "Steal Time", }
27
28 cpu_ret_q = Queue.Queue()
29 mem_ret_q = Queue.Queue()
30 test_ret_q = Queue.Queue()
31
32 global sdaemon_proc
33 global worker_proc
34
35 def cpu_create_usage_dict(top_line):
36 """
37 Return a dictionnary from a 'top' cpu line.
38 Ex: Cpu(s): 2.1%us, 1.2%sy, 0.0%ni, 96.2%id, 0.4%wa, 0.0%hi, 0.0%si, 0.0%st
39 """
40 top_dict = {'us': 0, 'sy': 0, 'ni': 0, 'id': 0, 'wa': 0, 'hi': 0, 'si': 0, 'st': 0}
41
42 # Split expression and remove first value which is "Cpu(s)"
43 top_line = top_line.replace(",","")
44 words = top_line.split()[1:]
45
46 for word in words:
47 index = word.find('%')
48 # Add the value to the dictionnary
49 top_dict[word[index + 1:]] = float(word[:index])
50
51 return top_dict
52
53 def cpu_average_usage(top_lines):
54 """
55 Return a dictionnary of 'top' CPU stats but averaging all values.
56 """
57 avg_dict = {'us': 0, 'sy': 0, 'ni': 0, 'id': 0, 'wa': 0, 'hi': 0, 'si': 0, 'st': 0}
58 # Average count
59 count = 0.0
60
61 for line in top_lines:
62 tmp_dict = cpu_create_usage_dict(line)
63 # Add value to avg dictionnary
64 for key in tmp_dict:
65 avg_dict[key] += tmp_dict[key]
66
67 count += 1.0
68
69 for key in avg_dict:
70 avg_dict[key] = avg_dict[key] / count
71
72 return (count, avg_dict)
73
74 def cpu_sample_usage(pid=None):
75 """
76 Sample CPU usage for num iterations.
77 If num is greater than 1, the average will be computed.
78 """
79 args = ["top", "-b", "-n", "1"]
80 if pid:
81 args.append("-p")
82 args.append(str(pid))
83
84 # Spawn top process
85 top = subprocess.Popen(args, stdout = subprocess.PIPE)
86
87 grep = subprocess.Popen(["grep", "^Cpu"], stdin = top.stdout,
88 stdout = subprocess.PIPE)
89 top.stdout.close()
90
91 return grep.communicate()[0].strip("\n")
92
93 def mem_sample_usage(pid):
94 """
95 Sample memory usage using /proc and a pid
96 """
97 args = ["cat", "/proc/" + str(pid) + "/status"]
98
99 if not os.path.isfile(args[1]):
100 return -1
101
102 mem_proc = subprocess.Popen(args, stdout = subprocess.PIPE)
103
104 grep = subprocess.Popen(["grep", "^VmRSS"], stdin = mem_proc.stdout,
105 stdout = subprocess.PIPE)
106 mem_proc.stdout.close()
107
108 # Return virtual memory size in kilobytes (kB)
109 #ret = grep.communicate()[0].split()
110 ret = grep.communicate()[0].split()
111
112 if len(ret) > 1:
113 ret = ret[1]
114 else:
115 ret = 0
116
117 return int(ret)
118
119 class SamplingWorker(threading.Thread):
120 def __init__(self, s_type, worker = None, delay = 0.2, pid = 0):
121 threading.Thread.__init__ (self)
122 self.s_type = s_type
123 self.delay = delay
124 self.pid = pid
125 self.worker = worker
126
127 def run(self):
128 count = 1
129 lines = []
130
131 if self.s_type == "cpu":
132 while 1:
133 if self.worker == None:
134 cpu_line = cpu_sample_usage(self.pid)
135 lines.append(cpu_line)
136 break
137 elif self.worker.is_alive():
138 cpu_line = cpu_sample_usage(self.pid)
139 lines.append(cpu_line)
140 else:
141 break
142
143 # Delay sec per memory sampling
144 time.sleep(self.delay)
145
146 count, stats = cpu_average_usage(lines)
147 cpu_ret_q.put((count, stats))
148 # grep process has ended here
149
150 elif self.s_type == "mem":
151 count = 0
152 mem_stat = 0
153
154 while 1:
155 if self.worker == None:
156 cpu_line = cpu_sample_usage(self.pid)
157 lines.append(cpu_line)
158 break
159 elif self.worker.is_alive():
160 mem_stat += get_mem_usage(self.pid)
161 count += 1
162 else:
163 break
164
165 # Delay sec per memory sampling
166 time.sleep(self.delay)
167
168 mem_ret_q.put((count, mem_stat))
169
170 class TestWorker(threading.Thread):
171 def __init__(self, path, name):
172 threading.Thread.__init__(self)
173 self.path = path
174 self.name = name
175
176 def run(self):
177 bin_path_name = os.path.join(self.path, self.name)
178
179 env = os.environ
180 env['TEST_NO_SESSIOND'] = '1'
181
182 test = subprocess.Popen([bin_path_name], env=env)
183 test.wait()
184
185 # Send ret value to main thread
186 test_ret_q.put(test.returncode)
187
188 def get_pid(procname):
189 """
190 Return pid of process name using 'pidof' command
191 """
192 pidof = subprocess.Popen(["pidof", procname], stdout = subprocess.PIPE)
193 pid = pidof.communicate()[0].split()
194
195 if pid == []:
196 return 0
197
198 return int(pid[0])
199
200 def spawn_session_daemon():
201 """
202 Exec the session daemon and return PID
203 """
204 global sdaemon_proc
205
206 pid = get_pid(SESSIOND_BIN_NAME)
207 if pid != 0:
208 os.kill(pid, SIGTERM)
209
210 bin_path = os.path.join(TESTDIR_PATH, "..", SESSIOND_BIN_PATH, SESSIOND_BIN_NAME)
211
212 if not os.path.isfile(bin_path):
213 print "Error: No session daemon binary found. Compiled?"
214 return 0
215
216 try:
217 sdaemon_proc = subprocess.Popen([bin_path, "-d"], shell=False,
218 stderr = subprocess.PIPE)
219 except OSError, e:
220 print e
221 return 0
222
223 return get_pid(SESSIOND_BIN_NAME)
224
225 def start_test(name):
226 """
227 Spawn test and return exit code
228 """
229 tw = TestWorker(".", name)
230 tw.start()
231
232 return test_ret_q.get(True)
233
234 def print_cpu_stats(stats, count):
235 """
236 Pretty print on one line the CPU stats
237 """
238 sys.stdout.write(PRINT_ARROW + " Cpu [sampled %d time(s)]:\n " % (count))
239 for stat in stats:
240 sys.stdout.write(" %s: %.2f, " % (stat, stats[stat]))
241 print ""
242
243 def get_cpu_usage(delay=1, pid=0):
244 """
245 Spawn a worker thread to sample cpu usage.
246 """
247 sw = SamplingWorker("cpu", delay = delay, pid = pid)
248 sw.start()
249
250 return cpu_ret_q.get(True)
251
252 def get_mem_usage(pid):
253 """
254 Get memory usage for PID
255 """
256 return mem_sample_usage(pid)
257
258 def print_test_success(ret, expect):
259 """
260 Print if test has failed or pass according to the expected value.
261 """
262 if ret != expect:
263 print "\n" + PRINT_RED_BRACKET + \
264 " Failed: ret = %d (expected %d)" % (ret, expect)
265 return 1
266 else:
267 print "\n" + PRINT_BRACKET + \
268 " Passed: ret = %d (expected %d)" % (ret, expect)
269 return 0
270
271 def run_test(test):
272 """
273 Run test 'name' and output report of the test with stats.
274 """
275 global worker_proc
276 global sdaemon_proc
277 dem_pid = 0 # Session daemon pid
278
279 print PRINT_BRACKET + " %s" % (test['name'])
280 print PRINT_ARROW + " %s" % (test['desc'])
281 if no_stats:
282 print PRINT_ARROW + " Statistics will NOT be collected"
283 else:
284 print PRINT_ARROW + " Statistics of the session daemon will be collected"
285
286 if test['kern'] and not is_root:
287 print "Needs root for kernel tracing. Skipping"
288 return 0
289
290 if not os.path.isfile(test['bin']):
291 print "Unable to find test file '%s'. Skipping" % (test['bin'])
292 return 0
293
294 # No session daemon needed
295 if not test['daemon']:
296 print PRINT_ARROW + " No session daemon needed"
297 ret = start_test(test['bin'])
298 print_test_success(ret, test['success'])
299 return 0
300 else:
301 print PRINT_ARROW + " Session daemon needed"
302
303 dem_pid = spawn_session_daemon()
304 if dem_pid <= 0:
305 print "Unable to start %s. Stopping" % (SESSIOND_BIN_NAME)
306 print sdaemon_proc.communicate()[1]
307 return 0
308
309 print PRINT_BRACKET + " Session daemon spawned (pid: %d)\n" % (dem_pid)
310
311 if not no_stats:
312 mem_before = get_mem_usage(dem_pid)
313 print PRINT_BRACKET + " Stats *before* test:"
314 print PRINT_ARROW + " Mem (kB): %d" % (mem_before)
315 cpu_count, cpu_stats = get_cpu_usage(pid = dem_pid)
316 print_cpu_stats(cpu_stats, cpu_count)
317
318 tw = TestWorker(".", test['bin'])
319 tw.start()
320
321 if not no_stats:
322 # Start CPU sampling for test
323 sw_cpu = SamplingWorker("cpu", worker = tw, pid = dem_pid)
324 sw_cpu.start()
325 sw_mem = SamplingWorker("mem", worker = tw, pid = dem_pid)
326 sw_mem.start()
327
328 ret = test_ret_q.get(True)
329
330 if not no_stats:
331 time.sleep(2)
332 # Compute memory average
333 mem_count, mem_during = mem_ret_q.get(True)
334 mem_during = float(mem_during) / float(mem_count)
335 cpu_count, cpu_stats = cpu_ret_q.get(True)
336
337 print "\n" + PRINT_BRACKET + " Stats *during* test:"
338 print PRINT_ARROW + " Mem (kB): %.0f [sampled %d time(s)]" % (mem_during, mem_count)
339 print_cpu_stats(cpu_stats, cpu_count)
340
341 mem_after = get_mem_usage(dem_pid)
342 print "\n" + PRINT_BRACKET + " Stats *after* test:"
343 print PRINT_ARROW + " Mem (kB): %d" % (mem_after)
344 cpu_count, cpu_stats = get_cpu_usage(pid = dem_pid)
345 print_cpu_stats(cpu_stats, cpu_count)
346
347 print "\n" + PRINT_BRACKET + " Memory usage differences:"
348 print PRINT_ARROW + " Diff during and before (kB): %d" % (mem_during - mem_before)
349 print PRINT_ARROW + " Diff during and after (kB): %d" % (mem_during - mem_after)
350 print PRINT_ARROW + " Diff before and after (kB): %d" % (mem_after - mem_before)
351
352 # Return value of 0 means that is passed else it failed
353 ret = print_test_success(ret, test['success'])
354
355 # Stop session daemon
356 if dem_pid > 0:
357 print PRINT_BRACKET + " Stopping session daemon (pid: %d)..." % (dem_pid)
358 try:
359 os.kill(dem_pid, SIGTERM)
360 # This call simply does not work... It seems python does not relay the signal
361 # to the child processes of sdaemon_proc.
362 # sdaemon_proc.terminate()
363 if ret != 0:
364 print sdaemon_proc.communicate()[1]
365 elif sdaemon_proc.returncode == None:
366 sdaemon_proc.communicate()
367 except OSError, e:
368 print e
369
370 # Make sure all thread are released
371 if not no_stats:
372 tw.join()
373 sw_cpu.join()
374 sw_mem.join()
375
376 return ret
377
378 def main():
379 for test in Tests:
380 if not test['enabled']:
381 continue
382
383 ret = run_test(test)
384 if ret != 0:
385 # Stop all tests, the last one failed
386 return
387 print ""
388
389 def cleanup(signo, stack):
390 """ Cleanup function """
391 sys.exit(0)
392
393 if __name__ == "__main__":
394 if not os.getuid() == 0:
395 is_root = 0
396 print "NOTICE: Not root. No kernel tracing will be tested\n"
397
398 if os.path.isfile("test_list.py"):
399 from test_list import Tests
400 else:
401 print "No test_list.py found. Stopping"
402 cleanup(0, 0)
403
404 TESTDIR_PATH = os.getcwd()
405
406 if len(sys.argv) > 1:
407 if sys.argv[1] == "--no-stats":
408 no_stats = 1
409
410 try:
411 signal(SIGTERM, cleanup)
412 signal(SIGINT, cleanup)
413 main()
414 cleanup(0, 0)
415 except KeyboardInterrupt:
416 cleanup(0, 0)
This page took 0.037382 seconds and 4 git commands to generate.