| 1 | # Copyright (C) 2018 - Francis Deslauriers <francis.deslauriers@efficios.com> |
| 2 | # |
| 3 | # This program is free software: you can redistribute it and/or modify |
| 4 | # it under the terms of the GNU General Public License as published by |
| 5 | # the Free Software Foundation, either version 3 of the License, or |
| 6 | # (at your option) any later version. |
| 7 | # |
| 8 | # This program is distributed in the hope that it will be useful, |
| 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 11 | # GNU General Public License for more details. |
| 12 | # |
| 13 | # You should have received a copy of the GNU General Public License |
| 14 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 15 | |
| 16 | import datetime |
| 17 | import gzip |
| 18 | import os |
| 19 | import pprint |
| 20 | import subprocess |
| 21 | import sys |
| 22 | |
| 23 | NB_KPROBES_PER_ITER=500 |
| 24 | NB_KPROBES_PER_ROUND=20000 |
| 25 | |
| 26 | def load_instr_points(instr_points_archive): |
| 27 | print('Reading instrumentation points from \'{}\'.'.format(instr_points_archive), end='') |
| 28 | sys.stdout.flush() |
| 29 | |
| 30 | with gzip.open(instr_points_archive, 'r') as f: |
| 31 | data = f.read() |
| 32 | print(' Done.') |
| 33 | |
| 34 | return [x.decode('utf-8') for x in data.split()] |
| 35 | |
| 36 | def enable_kprobe_events(instr_points): |
| 37 | print('Enabling events from {} to {}...'.format(instr_points[0], instr_points[-1]), end='') |
| 38 | sys.stdout.flush() |
| 39 | |
| 40 | # Use os module directly, because this is a sysfs file and seeking inside |
| 41 | # the file is not supported. The python open() function with the append |
| 42 | # ('a') flag uses lseek(, SEEK_END) to move the write pointer to the end. |
| 43 | fd = os.open('/sys/kernel/debug/tracing/kprobe_events', os.O_WRONLY|os.O_CREAT|os.O_APPEND) |
| 44 | for i, point in enumerate(instr_points): |
| 45 | |
| 46 | kprobe_cmd = 'r:event_{} {}\n'.format(i, point).encode('utf-8') |
| 47 | try: |
| 48 | os.write(fd, kprobe_cmd) |
| 49 | except OSError: |
| 50 | continue |
| 51 | os.close(fd) |
| 52 | print(' Done.') |
| 53 | |
| 54 | def set_kprobe_tracing_state(state): |
| 55 | if state not in (0 ,1): |
| 56 | raise ValueError |
| 57 | |
| 58 | try: |
| 59 | with open('/sys/kernel/debug/tracing/events/kprobes/enable', 'w') as enable_kprobe_file: |
| 60 | enable_kprobe_file.write('{}\n'.format(state)) |
| 61 | except IOError: |
| 62 | print('kprobes/enable file does not exist') |
| 63 | |
| 64 | if state == 0: |
| 65 | # Clear the content of the trace. |
| 66 | open('/sys/kernel/debug/tracing/trace', 'w').close() |
| 67 | # Clear all the events. |
| 68 | open('/sys/kernel/debug/tracing/kprobe_events', 'w').close() |
| 69 | |
| 70 | def run_workload(): |
| 71 | print('Running workload...', end='') |
| 72 | sys.stdout.flush() |
| 73 | workload = ['stress', '--cpu', '2', '--io', '4', '--vm', '2', |
| 74 | '--vm-bytes', '128M', '--hdd', '3', '--timeout', '3s'] |
| 75 | try: |
| 76 | with open(os.devnull) as devnull: |
| 77 | subprocess.call(workload, stdout=devnull, stderr=devnull) |
| 78 | except OSError as e: |
| 79 | print("Workload execution failed:", e, file=sys.stderr) |
| 80 | pprint.pprint(workload) |
| 81 | |
| 82 | print(' Done.') |
| 83 | |
| 84 | def mount_tracingfs(): |
| 85 | with open(os.devnull) as devnull: |
| 86 | subprocess.call(['mount', '-t', 'debugfs', 'nodev', '/sys/kernel/debug/'], |
| 87 | stdout=devnull, stderr=devnull) |
| 88 | |
| 89 | def print_dashed_line(): |
| 90 | print('-'*100) |
| 91 | |
| 92 | def main(): |
| 93 | assert(len(sys.argv) == 3) |
| 94 | |
| 95 | instr_point_archive = sys.argv[1] |
| 96 | round_nb = int(sys.argv[2]) |
| 97 | # Load instrumentation points to disk and attach it to lava test run. |
| 98 | instrumentation_points = load_instr_points(instr_point_archive) |
| 99 | |
| 100 | # We are past the end of the instrumentation point list. |
| 101 | if len(instrumentation_points)/NB_KPROBES_PER_ROUND <= round_nb: |
| 102 | print('No instrumentation point for round {}.'.format(round_nb)) |
| 103 | return |
| 104 | |
| 105 | mount_tracingfs() |
| 106 | |
| 107 | # Loop over the list by enabling ranges of NB_KPROBES_PER_ITER kprobes. |
| 108 | for i in range(int(NB_KPROBES_PER_ROUND/NB_KPROBES_PER_ITER)): |
| 109 | print_dashed_line() |
| 110 | lower_bound = (round_nb * NB_KPROBES_PER_ROUND) + (i * NB_KPROBES_PER_ITER) |
| 111 | upper_bound = lower_bound + NB_KPROBES_PER_ITER |
| 112 | print('Time now: {}, {} to {}'.format(datetime.datetime.now(), lower_bound , upper_bound)) |
| 113 | enable_kprobe_events(instrumentation_points[lower_bound:upper_bound]) |
| 114 | set_kprobe_tracing_state(1) |
| 115 | run_workload() |
| 116 | print('\n') |
| 117 | set_kprobe_tracing_state(0) |
| 118 | |
| 119 | if __name__ == "__main__": |
| 120 | main() |