Commit | Line | Data |
---|---|---|
1b09221e | 1 | #!/usr/bin/env python3 |
d03b82af JD |
2 | # |
3 | # Permission is hereby granted, free of charge, to any person obtaining a copy | |
4 | # of this software and associated documentation files (the "Software"), to deal | |
5 | # in the Software without restriction, including without limitation the rights | |
6 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
7 | # copies of the Software, and to permit persons to whom the Software is | |
8 | # furnished to do so, subject to the following conditions: | |
9 | # | |
10 | # The above copyright notice and this permission notice shall be included in | |
11 | # all copies or substantial portions of the Software. | |
12 | ||
13 | import sys | |
fd893e53 | 14 | import argparse |
0790ad37 JD |
15 | import shutil |
16 | import time | |
d03b82af | 17 | from babeltrace import * |
b0e6c900 JD |
18 | from LTTngAnalyzes.common import * |
19 | from LTTngAnalyzes.jsonreport import * | |
20 | from LTTngAnalyzes.textreport import * | |
0790ad37 | 21 | from LTTngAnalyzes.graphitereport import * |
34a788b4 | 22 | from LTTngAnalyzes.sched import * |
1595fe2d | 23 | from LTTngAnalyzes.syscalls import * |
4957863f | 24 | from LTTngAnalyzes.block_bio import * |
eb9efdcb | 25 | from LTTngAnalyzes.net import * |
87ea2bb1 | 26 | from LTTngAnalyzes.statedump import * |
65d937db | 27 | |
4957863f | 28 | class Analyzes(): |
d03b82af | 29 | def __init__(self, traces): |
fd893e53 JD |
30 | self.trace_start_ts = 0 |
31 | self.trace_end_ts = 0 | |
d03b82af JD |
32 | self.traces = traces |
33 | self.tids = {} | |
646f0fe6 | 34 | self.cpus = {} |
b0e6c900 | 35 | self.syscalls = {} |
4957863f | 36 | self.disks = {} |
eb9efdcb | 37 | self.ifaces = {} |
646f0fe6 | 38 | |
a0da5181 | 39 | def output(self, args, begin_ns, end_ns, final=0): |
fd893e53 | 40 | if args.text: |
0790ad37 | 41 | r = TextReport(self.trace_start_ts, self.trace_end_ts, |
f1839094 JD |
42 | self.cpus, self.tids, self.syscalls, self.disks, |
43 | self.ifaces) | |
0790ad37 | 44 | r.report(begin_ns, end_ns, final, args) |
eb9efdcb | 45 | if not final and (args.cpu or args.tid or args.disk or args.net): |
a0da5181 | 46 | print("") |
fd893e53 | 47 | if args.json: |
0790ad37 | 48 | r = JsonReport(self.trace_start_ts, self.trace_end_ts, |
b0e6c900 | 49 | self.cpus, self.tids) |
0790ad37 JD |
50 | r.report(begin_ns, end_ns, final, args) |
51 | if args.graphite: | |
52 | r = GraphiteReport(self.trace_start_ts, self.trace_end_ts, | |
eb9efdcb | 53 | self.cpus, self.tids, self.syscalls, self.disks, self.ifaces) |
0790ad37 | 54 | r.report(begin_ns, end_ns, final, args) |
fd893e53 JD |
55 | |
56 | def check_refresh(self, args, event): | |
57 | """Check if we need to output something""" | |
58 | if args.refresh == 0: | |
59 | return | |
60 | event_sec = event.timestamp / NSEC_PER_SEC | |
61 | if self.current_sec == 0: | |
62 | self.current_sec = event_sec | |
63 | elif self.current_sec != event_sec and \ | |
64 | (self.current_sec + args.refresh) <= event_sec: | |
65 | self.compute_stats() | |
a0da5181 | 66 | self.output(args, self.start_ns, event.timestamp) |
fd893e53 JD |
67 | self.reset_total(event.timestamp) |
68 | self.current_sec = event_sec | |
69 | self.start_ns = event.timestamp | |
8e784ad6 | 70 | |
65d937db | 71 | def reset_total(self, start_ts): |
8e784ad6 | 72 | for cpu in self.cpus.keys(): |
a0da5181 JD |
73 | current_cpu = self.cpus[cpu] |
74 | current_cpu.cpu_ns = 0 | |
75 | if current_cpu.start_task_ns != 0: | |
76 | current_cpu.start_task_ns = start_ts | |
77 | if current_cpu.current_tid >= 0: | |
78 | self.tids[current_cpu.current_tid].last_sched = start_ts | |
65d937db | 79 | |
d03b82af | 80 | for tid in self.tids.keys(): |
8e784ad6 | 81 | self.tids[tid].cpu_ns = 0 |
b4376047 | 82 | self.tids[tid].migrate_count = 0 |
b1dd3851 JD |
83 | self.tids[tid].read = 0 |
84 | self.tids[tid].write = 0 | |
ffd315c0 JD |
85 | for syscall in self.tids[tid].syscalls.keys(): |
86 | self.tids[tid].syscalls[syscall].count = 0 | |
8e784ad6 | 87 | |
1595fe2d JD |
88 | for syscall in self.syscalls.keys(): |
89 | self.syscalls[syscall].count = 0 | |
90 | ||
4957863f JD |
91 | for dev in self.disks.keys(): |
92 | self.disks[dev].nr_sector = 0 | |
93 | self.disks[dev].nr_requests = 0 | |
94 | self.disks[dev].completed_requests = 0 | |
95 | self.disks[dev].request_time = 0 | |
96 | ||
eb9efdcb JD |
97 | for iface in self.ifaces.keys(): |
98 | self.ifaces[iface].recv_bytes = 0 | |
99 | self.ifaces[iface].recv_packets = 0 | |
100 | self.ifaces[iface].send_bytes = 0 | |
101 | self.ifaces[iface].send_packets = 0 | |
102 | ||
103 | def clear(self): | |
104 | self.trace_start_ts = 0 | |
105 | self.trace_end_ts = 0 | |
106 | self.traces = traces | |
107 | self.tids = {} | |
108 | self.cpus = {} | |
109 | self.syscalls = {} | |
110 | self.disks = {} | |
111 | self.ifaces = {} | |
112 | ||
fd893e53 | 113 | def compute_stats(self): |
65d937db | 114 | for cpu in self.cpus.keys(): |
a0da5181 | 115 | current_cpu = self.cpus[cpu] |
fd893e53 | 116 | total_ns = self.end_ns - self.start_ns |
a0da5181 JD |
117 | if current_cpu.start_task_ns != 0: |
118 | current_cpu.cpu_ns += self.end_ns - current_cpu.start_task_ns | |
119 | cpu_total_ns = current_cpu.cpu_ns | |
120 | current_cpu.cpu_pc = (cpu_total_ns * 100)/total_ns | |
121 | if current_cpu.current_tid >= 0: | |
122 | self.tids[current_cpu.current_tid].cpu_ns += \ | |
123 | self.end_ns - current_cpu.start_task_ns | |
65d937db | 124 | |
fd893e53 | 125 | def run(self, args): |
8e784ad6 | 126 | """Process the trace""" |
fd893e53 JD |
127 | self.current_sec = 0 |
128 | self.start_ns = 0 | |
129 | self.end_ns = 0 | |
1595fe2d | 130 | |
34a788b4 | 131 | sched = Sched(self.cpus, self.tids) |
1595fe2d | 132 | syscall = Syscalls(self.cpus, self.tids, self.syscalls) |
4957863f | 133 | block_bio = BlockBio(self.cpus, self.disks) |
eb9efdcb | 134 | net = Net(self.ifaces) |
87ea2bb1 | 135 | statedump = Statedump(self.tids) |
b99fb31f | 136 | |
8e784ad6 | 137 | for event in self.traces.events: |
fd893e53 JD |
138 | if self.start_ns == 0: |
139 | self.start_ns = event.timestamp | |
140 | if self.trace_start_ts == 0: | |
141 | self.trace_start_ts = event.timestamp | |
142 | self.end_ns = event.timestamp | |
fd893e53 | 143 | self.check_refresh(args, event) |
fd893e53 | 144 | self.trace_end_ts = event.timestamp |
8e784ad6 JD |
145 | |
146 | if event.name == "sched_switch": | |
34a788b4 | 147 | sched.switch(event) |
b4376047 | 148 | elif event.name == "sched_migrate_task": |
34a788b4 | 149 | sched.migrate_task(event) |
da2b3096 JD |
150 | elif event.name == "sched_process_fork": |
151 | sched.process_fork(event) | |
152 | elif event.name == "sched_process_exec": | |
153 | sched.process_exec(event) | |
ffd315c0 | 154 | elif event.name[0:4] == "sys_" and \ |
b9922d40 JD |
155 | (args.global_syscalls or args.tid_syscalls or |
156 | args.fds): | |
b99fb31f | 157 | syscall.entry(event) |
ffd315c0 | 158 | elif event.name == "exit_syscall" and \ |
b9922d40 JD |
159 | (args.global_syscalls or args.tid_syscalls or |
160 | args.fds): | |
b99fb31f | 161 | syscall.exit(event) |
acf4f8f1 JD |
162 | elif event.name == "block_bio_complete" or \ |
163 | event.name == "block_rq_complete": | |
4957863f JD |
164 | block_bio.complete(event) |
165 | elif event.name == "block_bio_queue": | |
166 | block_bio.queue(event) | |
eb9efdcb JD |
167 | elif event.name == "netif_receive_skb": |
168 | net.recv(event) | |
169 | elif event.name == "net_dev_xmit": | |
170 | net.send(event) | |
87ea2bb1 JD |
171 | elif event.name == "lttng_statedump_process_state": |
172 | statedump.process_state(event) | |
173 | elif event.name == "lttng_statedump_file_descriptor": | |
174 | statedump.file_descriptor(event) | |
fd893e53 | 175 | if args.refresh == 0: |
a0da5181 | 176 | # stats for the whole trace |
fd893e53 | 177 | self.compute_stats() |
a0da5181 | 178 | self.output(args, self.trace_start_ts, self.trace_end_ts, final=1) |
4021d2e7 | 179 | else: |
a0da5181 | 180 | # stats only for the last segment |
fd893e53 | 181 | self.compute_stats() |
a0da5181 | 182 | self.output(args, self.start_ns, self.trace_end_ts, |
fd893e53 | 183 | final=1) |
d03b82af JD |
184 | |
185 | if __name__ == "__main__": | |
fd893e53 JD |
186 | parser = argparse.ArgumentParser(description='CPU usage analysis') |
187 | parser.add_argument('path', metavar="<path/to/trace>", help='Trace path') | |
188 | parser.add_argument('-r', '--refresh', type=int, | |
189 | help='Refresh period in seconds', default=0) | |
1595fe2d JD |
190 | parser.add_argument('--text', action="store_true", |
191 | help='Output in text (default)') | |
192 | parser.add_argument('--json', action="store_true", | |
193 | help='Output in JSON') | |
0790ad37 JD |
194 | parser.add_argument('--graphite', action="store_true", |
195 | help='Output to graphite') | |
1595fe2d JD |
196 | parser.add_argument('--cpu', action="store_true", |
197 | help='Per-CPU stats (default)') | |
4957863f JD |
198 | parser.add_argument('--disk', action="store_true", |
199 | help='Per-Disk stats (default)') | |
1595fe2d JD |
200 | parser.add_argument('--tid', action="store_true", |
201 | help='Per-TID stats (default)') | |
eb9efdcb JD |
202 | parser.add_argument('--net', action="store_true", |
203 | help='Per-interface network stats (default)') | |
1595fe2d JD |
204 | parser.add_argument('--global-syscalls', action="store_true", |
205 | help='Global syscalls (default)') | |
206 | parser.add_argument('--tid-syscalls', action="store_true", | |
207 | help='Per-TID syscalls (default)') | |
b9922d40 JD |
208 | parser.add_argument('--fds', action="store_true", |
209 | help='Per-PID FD stats (default)') | |
1595fe2d JD |
210 | parser.add_argument('--overall', action="store_true", |
211 | help='Overall CPU Usage (default)') | |
212 | parser.add_argument('--info', action="store_true", | |
213 | help='Trace info (default)') | |
214 | parser.add_argument('--top', type=int, default=0, | |
215 | help='Limit to top X TIDs') | |
216 | parser.add_argument('--name', type=str, default=0, | |
217 | help='Show results only for the list of processes') | |
fd893e53 JD |
218 | args = parser.parse_args() |
219 | ||
0790ad37 | 220 | if not args.json and not args.graphite: |
fd893e53 JD |
221 | args.text = True |
222 | ||
b9922d40 | 223 | if args.tid_syscalls or args.fds: |
ffd315c0 | 224 | args.tid = True |
1595fe2d | 225 | if not (args.cpu or args.tid or args.overall or args.info or \ |
eb9efdcb | 226 | args.global_syscalls or args.tid_syscalls or args.disk \ |
b9922d40 | 227 | or args.net or args.fds): |
fd893e53 JD |
228 | args.cpu = True |
229 | args.tid = True | |
230 | args.overall = True | |
4957863f | 231 | args.disk = True |
fd893e53 | 232 | args.info = True |
1595fe2d JD |
233 | args.global_syscalls = True |
234 | args.tid_syscalls = True | |
eb9efdcb | 235 | args.net = True |
b9922d40 | 236 | args.fds = True |
ffd315c0 JD |
237 | if args.name: |
238 | args.global_syscalls = False | |
6260255a JD |
239 | args.display_proc_list = [] |
240 | if args.name: | |
241 | args.display_proc_list = args.name.split(",") | |
d03b82af | 242 | |
0790ad37 JD |
243 | while True: |
244 | if args.graphite: | |
eb9efdcb JD |
245 | events="sched_switch,block_bio_complete,block_bio_queue," \ |
246 | "netif_receive_skb,net_dev_xmit" | |
0790ad37 | 247 | os.system("lttng create graphite -o graphite-live >/dev/null") |
4957863f | 248 | os.system("lttng enable-event -k %s -s graphite >/dev/null" % events) |
0790ad37 JD |
249 | os.system("lttng start graphite >/dev/null") |
250 | time.sleep(2) | |
251 | os.system("lttng stop graphite >/dev/null") | |
252 | os.system("lttng destroy graphite >/dev/null") | |
253 | traces = TraceCollection() | |
254 | handle = traces.add_trace(args.path, "ctf") | |
255 | if handle is None: | |
256 | sys.exit(1) | |
257 | ||
4957863f | 258 | c = Analyzes(traces) |
0790ad37 | 259 | c.run(args) |
eb9efdcb | 260 | c.clear() |
0790ad37 JD |
261 | |
262 | traces.remove_trace(handle) | |
263 | ||
264 | if not args.graphite: | |
265 | break |