]>
Commit | Line | Data |
---|---|---|
bd6521f0 A |
1 | // |
2 | // main.cpp | |
3 | // kdprof | |
4 | // | |
5 | // Created by James McIlree on 4/15/13. | |
6 | // Copyright (c) 2013 Apple. All rights reserved. | |
7 | // | |
8 | ||
9 | #include "global.h" | |
10 | ||
11 | // Generated by agvtool | |
12 | extern const unsigned char __kdprofVersionString[]; | |
13 | ||
14 | bool shouldPrintVersion = false; | |
15 | ||
16 | __attribute__((noreturn)) void usage(const char *errorMsg) { | |
17 | if (errorMsg) { | |
18 | fprintf(stderr, "%s\n", errorMsg); | |
19 | exit(1); | |
20 | } | |
21 | ||
22 | const char* BOLD = "\033[1m"; | |
23 | const char* UNBOLD = "\033[0m"; | |
24 | ||
25 | // printf("01234567890123456789012345678901234567890123456789012345678901234567890123456789\n"); | |
26 | printf("kdprof [options] [path/trace.codes ...] [path/data.trace ...]\n\n"); | |
27 | printf(" GLOBAL OPTIONS\n\n"); | |
28 | printf(" -h, --help Print this message\n"); | |
29 | printf(" --version Print version info\n"); | |
30 | printf(" -v, --verbose Print additional information\n"); | |
31 | printf(" --presort-events Sort events before processing. IOP workaround\n"); | |
32 | printf(" -N, --no-default-codes Do not read the default trace codes\n"); | |
33 | printf("\n"); | |
34 | printf(" LIVE TRACING OPTIONS\n\n"); | |
35 | printf(" -i, --intialize [#] Initialize the trace buffer, with opt buf count\n"); | |
36 | printf(" -r, --remove Remove the trace buffer\n"); | |
37 | printf(" -n, --no-wrap Do not allow the trace buffer to wrap\n"); | |
38 | printf(" -g, --print-kdbg-state Print the current kdbg state\n"); | |
39 | printf(" -e, --enable Enable collection of events\n"); | |
40 | printf(" -d, --disable Disable collection of events\n"); | |
41 | printf(" -t, --collect Collect and print the trace buffer\n"); | |
42 | printf(" --save path Collect and save the trace buffer to path\n"); | |
43 | printf(" -S, --sleep # Wait for a specified interval\n"); | |
44 | printf("\n"); | |
45 | printf(" OUTPUT OPTIONS\n\n"); | |
46 | printf(" -o, --output path Print output to path\n"); | |
47 | printf(" --summary Print calculated data (default true)\n"); | |
48 | printf(" --no-summary Do not print calculated data\n"); | |
49 | printf(" --csv Print a csv formatted summary for use in numbers\n"); | |
50 | printf(" --no-csv Do not print a csv formatted summary\n"); | |
51 | printf(" --step # Step by # time units in summary output\n"); | |
52 | printf(" --process Include per-process summary data\n"); | |
53 | printf(" --no-process Do not include per-process summary data\n"); | |
54 | printf(" --thread Include per-thread summary data\n"); | |
55 | printf(" --cpu Include per-cpu summary data\n"); | |
56 | printf(" --sort-by-cpu Sort process/thread lists by cpu usage\n"); | |
57 | printf(" --sort-by-vmfault Sort process/thread lists by vmfault time\n"); | |
58 | printf(" --sort-by-io-wait Sort process/thread lists by IO time\n"); | |
59 | printf(" --sort-by-io-ops Sort process/thread lists by # IO Ops\n"); | |
60 | printf(" --sort-by-io-size Sort process/thread lists by IO bytes\n"); | |
61 | printf(" --sort-by-pid Sort process/thread lists by pid/tid\n"); | |
62 | printf(" --events Enable individual event printing\n"); | |
63 | printf(" --no-events Disable individual event printing\n"); | |
64 | printf(" --raw-timestamps Print timestamps as raw values, not deltas\n"); | |
65 | printf(" --mach-absolute-time Print timestamps in mach absolute time\n"); | |
66 | printf(" --event-index Print the index of each event\n"); | |
67 | printf(" --no-codes Print hex trace codes, not symbolic\n"); | |
68 | printf(" --process-start-stop Print start/stop information about each process\n"); | |
69 | printf("\n"); | |
70 | printf(" DEPRECATED OPTIONS\n\n"); | |
71 | printf(" -X, --k32 Trace data is from a 32 bit kernel\n"); | |
72 | printf(" --k64 Trace data is from a 64 bit kernel\n"); | |
73 | printf(" --codes path read trace codes from path\n"); | |
74 | printf(" --trace path read trace data from path\n"); | |
75 | printf(" --ios Treat data as coming from an iOS device\n"); | |
76 | printf(" --timebase #/# Set the mach_timebase\n"); | |
77 | printf(" --cpus # Set the # of cpus.\n"); | |
78 | printf(" --iops # Set the # of iops.\n"); | |
79 | printf("\n"); | |
80 | printf(" OPTION ARGUMENTS\n\n"); | |
81 | printf(" All arguments that specifiy a time value may use the following postfixes\n\n"); | |
82 | printf(" s Seconds\n"); | |
83 | printf(" ms Milliseconds\n"); | |
84 | printf(" us Microseconds\n"); | |
85 | printf(" ns Nanoseconds\n"); | |
86 | printf(" mabs Mach Absolute Time Units\n"); | |
87 | printf("\n"); | |
88 | // printf("01234567890123456789012345678901234567890123456789012345678901234567890123456789\n"); | |
89 | printf(" USAGE\n"); | |
90 | printf("\n"); | |
91 | printf(" Arguments are parsed in order. Long form flags are not case sensitive.\n"); | |
92 | printf(" Live tracing and trace file arguments are pushed onto an execution stack\n"); | |
93 | printf(" and processed after argument parsing has completed.\n"); | |
94 | printf("\n"); | |
95 | printf(" Files ending in .trace or .codes may omit the --trace or --codes flag\n"); | |
96 | printf(" In most cases, you do not need to specify a kernel size or timebase, it is\n"); | |
97 | printf(" determined automatically.\n"); | |
98 | printf("\n"); | |
99 | printf(" Modern trace(s) have an embedded ap/iop cpu count. If you need to parse\n"); | |
100 | printf(" an older file, you will want to set these. Typically you would set the AP\n"); | |
101 | printf(" cpu count to the number of active cpus, and the IOP cpu count to zero.\n"); | |
102 | printf("\n"); | |
103 | printf(" EXAMPLES\n"); | |
104 | printf("\n"); | |
105 | printf(" %skdprof InterestingData.trace%s\n", BOLD, UNBOLD); | |
106 | printf(" Print a summary of per process cpu usage in InterestingData.trace\n"); | |
107 | printf("\n"); | |
108 | printf(" %skdprof --step 100ms InterestingData.trace%s\n", BOLD, UNBOLD); | |
109 | printf(" Print summaries of the per process cpu usage in InterestingData.trace,\n"); | |
110 | printf(" one for each 100ms of time\n"); | |
111 | printf("\n"); | |
112 | printf(" %skdprof --thread --step 100ms InterestingData.trace%s\n", BOLD, UNBOLD); | |
113 | printf(" Print summaries of the per process and per thread cpu usage in\n"); | |
114 | printf(" InterestingData.trace, one for each 100ms of time\n"); | |
115 | printf("\n"); | |
116 | printf(" %skdprof -r -i 100000 -e -S 1 -d -t%s\n", BOLD, UNBOLD); | |
117 | printf(" Reinit the trace buffer with 100000 entries, enable it, wait 1 second,\n"); | |
118 | printf(" and then collect/print the trace buffer\n"); | |
119 | printf("\n"); | |
120 | printf(" %skdprof --events foo.trace%s\n", BOLD, UNBOLD); | |
121 | printf(" Print the events in foo.trace\n"); | |
122 | printf("\n"); | |
123 | exit(1); | |
124 | } | |
125 | ||
126 | static void add_trace_codes_path(const char* path, Globals& globals) { | |
127 | if (Path::is_file(path, true)) { | |
128 | char resolved_path[PATH_MAX]; | |
129 | if (realpath(path, resolved_path)) { | |
130 | globals.append_trace_codes_at_path(resolved_path); | |
131 | return; | |
132 | } | |
133 | } | |
134 | char* errmsg = NULL; | |
135 | asprintf(&errmsg, "Trace codes path %s is not valid", path); | |
136 | usage(errmsg); | |
137 | } | |
138 | ||
139 | static std::unique_ptr<Action> create_trace_file_action(const char* trace_file_path) { | |
140 | if (Path::is_file(trace_file_path, true)) { | |
141 | char resolved_path[PATH_MAX]; | |
142 | if (realpath(trace_file_path, resolved_path)) { | |
143 | return std::make_unique<TraceFileAction>(resolved_path); | |
144 | } | |
145 | } | |
146 | char* errmsg = NULL; | |
147 | asprintf(&errmsg, "Trace data path %s is not valid", trace_file_path); | |
148 | usage(errmsg); | |
149 | } | |
150 | ||
151 | // | |
152 | // Must take globals so it can do the timebase conversions for mabs values! | |
153 | // | |
154 | static NanoTime parse_time(Globals& globals, const char* arg) { | |
155 | ||
156 | char* units; | |
157 | uint64_t value = strtoull(arg, &units, 0); | |
158 | ||
159 | // Unspecified units are treated as seconds | |
160 | if (*units == 0 || strcmp(units, "s") == 0) { | |
161 | return NanoTime(value * NANOSECONDS_PER_SECOND); | |
162 | } | |
163 | ||
164 | if (strcmp(units, "ms") == 0) | |
165 | return NanoTime(value * NANOSECONDS_PER_MILLISECOND); | |
166 | ||
167 | if (strcmp(units, "us") == 0) | |
168 | return NanoTime(value * NANOSECONDS_PER_MICROSECOND); | |
169 | ||
170 | if (strcmp(units, "ns") == 0) | |
171 | return NanoTime(value); | |
172 | ||
173 | if (strcmp(units, "mabs") == 0) { | |
174 | return AbsTime(value).nano_time(globals.timebase()); | |
175 | } | |
176 | ||
177 | usage("Unable to parse units on time value"); | |
178 | } | |
179 | ||
180 | static std::vector<std::unique_ptr<Action>> parse_arguments(int argc, const char* argv[], Globals& globals) { | |
181 | int i = 1; | |
182 | bool cpus_set = false; | |
183 | bool iops_set = false; | |
184 | ||
185 | std::vector<std::unique_ptr<Action>> actions; | |
186 | ||
187 | while (i < argc) { | |
188 | const char* arg = argv[i]; | |
189 | if ((strcmp(arg, "-h") == 0) || (strcasecmp(arg, "--help") == 0)) { | |
190 | usage(NULL); | |
191 | } else if ((strcasecmp(arg, "--version") == 0)) { | |
192 | shouldPrintVersion = true; | |
193 | } else if ((strcmp(arg, "-v") == 0) || strcasecmp(arg, "--verbose") == 0) { | |
194 | globals.set_is_verbose(true); | |
195 | } else if ((strcasecmp(arg, "--summary") == 0)) { | |
196 | globals.set_should_print_summary(true); | |
197 | } else if ((strcasecmp(arg, "--no-summary") == 0)) { | |
198 | globals.set_should_print_summary(false); | |
199 | } else if ((strcasecmp(arg, "--csv") == 0)) { | |
200 | globals.set_should_print_csv_summary(true); | |
201 | } else if ((strcasecmp(arg, "--no-csv") == 0)) { | |
202 | globals.set_should_print_csv_summary(false); | |
203 | } else if (strcasecmp(arg, "--step") == 0) { | |
204 | if (++i >= argc) | |
205 | usage("--step requires an argument"); | |
206 | ||
207 | globals.set_summary_step(argv[i]); | |
208 | // Force a blow up now if the arg is unparseable | |
209 | globals.summary_step(AbsInterval(AbsTime(1), AbsTime(1))); | |
210 | } else if (strcasecmp(arg, "--start") == 0) { | |
211 | if (++i >= argc) | |
212 | usage("--start requires an argument"); | |
213 | ||
214 | globals.set_summary_start(argv[i]); | |
215 | // Force a blow up now if the arg is unparseable | |
216 | globals.summary_start(AbsInterval(AbsTime(1), AbsTime(1))); | |
217 | } else if (strcasecmp(arg, "--stop") == 0) { | |
218 | if (++i >= argc) | |
219 | usage("--stop requires an argument"); | |
220 | ||
221 | globals.set_summary_stop(argv[i]); | |
222 | // Force a blow up now if the arg is unparseable | |
223 | globals.summary_stop(AbsInterval(AbsTime(1), AbsTime(1))); | |
224 | } else if ((strcasecmp(arg, "--cpu") == 0)) { | |
225 | globals.set_should_print_cpu_summaries(true); | |
226 | } else if ((strcasecmp(arg, "--processes") == 0) || (strcasecmp(arg, "--process") == 0)) { | |
227 | globals.set_should_print_process_summaries(true); | |
228 | } else if ((strcasecmp(arg, "--no-processes") == 0) || (strcasecmp(arg, "--no-process") == 0)) { | |
229 | globals.set_should_print_process_summaries(false); | |
230 | } else if ((strcasecmp(arg, "--threads") == 0) || (strcasecmp(arg, "--thread") == 0)) { | |
231 | globals.set_should_print_thread_summaries(true); | |
232 | } else if ((strcasecmp(arg, "--sort-by-cpu") == 0)) { | |
233 | globals.set_sort_key(kSortKey::CPU); | |
234 | } else if ((strcasecmp(arg, "--sort-by-pid") == 0)) { | |
235 | globals.set_sort_key(kSortKey::ID); | |
236 | } else if ((strcasecmp(arg, "--sort-by-vmfault") == 0)) { | |
237 | globals.set_sort_key(kSortKey::VMFault); | |
238 | } else if ((strcasecmp(arg, "--sort-by-io") == 0)) { | |
239 | globals.set_sort_key(kSortKey::IO_Wait); | |
240 | } else if ((strcasecmp(arg, "--sort-by-io-wait") == 0)) { | |
241 | globals.set_sort_key(kSortKey::IO_Wait); | |
242 | } else if ((strcasecmp(arg, "--sort-by-io-ops") == 0)) { | |
243 | globals.set_sort_key(kSortKey::IO_Ops); | |
244 | } else if ((strcasecmp(arg, "--sort-by-io-size") == 0)) { | |
245 | globals.set_sort_key(kSortKey::IO_Size); | |
246 | } else if ((strcasecmp(arg, "--events") == 0)) { | |
247 | globals.set_should_print_events(true); | |
248 | } else if ((strcasecmp(arg, "--no-events") == 0)) { | |
249 | globals.set_should_print_events(false); | |
250 | } else if ((strcasecmp(arg, "--presort-events") == 0)) { | |
251 | globals.set_should_presort_events(true); | |
252 | } else if ((strcmp(arg, "-N") == 0) || strcasecmp(arg, "--no-default-codes") == 0) { | |
253 | globals.set_should_read_default_trace_codes(false); | |
254 | } else if (strcasecmp(arg, "--codes") == 0) { | |
255 | if (++i >= argc) | |
256 | usage("--codes requires an argument"); | |
257 | add_trace_codes_path(argv[i], globals); | |
258 | } else if (strcasecmp(arg, "--trace") == 0) { | |
259 | if (++i >= argc) | |
260 | usage("--trace requires an argument"); | |
261 | ||
262 | actions.push_back(create_trace_file_action(argv[i])); | |
263 | } else if ((strcmp(arg, "-i") == 0) || strcasecmp(arg, "--initialize") == 0) { | |
264 | // The buffers argument is optional | |
265 | uint32_t buffers_default = 0; | |
266 | ||
267 | if (i + 1 < argc) { | |
268 | arg = argv[i+1]; | |
269 | char* endptr; | |
270 | uint32_t temp = (uint32_t)strtoul(arg, &endptr, 0); | |
271 | if (*endptr == 0) { | |
272 | // Consume the following argument if the conversion worked | |
273 | buffers_default = temp; | |
274 | i++; | |
275 | } | |
276 | ||
277 | } | |
278 | actions.push_back(std::make_unique<InitializeAction>(buffers_default)); | |
279 | } else if ((strcmp(arg, "-r") == 0) || strcasecmp(arg, "--remove") == 0) { | |
280 | actions.push_back(std::make_unique<RemoveAction>()); | |
281 | } else if ((strcmp(arg, "-n") == 0) || strcasecmp(arg, "--no-wrap") == 0) { | |
282 | actions.push_back(std::make_unique<NoWrapAction>()); | |
283 | } else if ((strcmp(arg, "-g") == 0) || strcasecmp(arg, "--print-kdbg-state") == 0) { | |
284 | actions.push_back(std::make_unique<PrintStateAction>()); | |
285 | } else if ((strcmp(arg, "-e") == 0) || strcasecmp(arg, "--enable") == 0) { | |
286 | actions.push_back(std::make_unique<EnableAction>()); | |
287 | } else if ((strcmp(arg, "-d") == 0) || strcasecmp(arg, "--disable") == 0) { | |
288 | actions.push_back(std::make_unique<DisableAction>()); | |
289 | } else if ((strcmp(arg, "-t") == 0) || strcasecmp(arg, "--collect") == 0) { | |
290 | actions.push_back(std::make_unique<CollectAction>()); | |
291 | } else if (strcasecmp(arg, "--save") == 0) { | |
292 | if (++i >= argc) | |
293 | usage("--save requires an argument"); | |
294 | ||
295 | FileDescriptor desc(argv[i], O_WRONLY | O_CREAT | O_TRUNC, 0644); | |
296 | if (!desc.is_open()) { | |
297 | char* errmsg = NULL; | |
298 | asprintf(&errmsg, "Unable to create save file at %s", argv[i]); | |
299 | usage(errmsg); | |
300 | } | |
301 | actions.push_back(std::make_unique<SaveTraceAction>(std::move(desc))); | |
302 | } else if ((strcmp(arg, "-S") == 0) || strcasecmp(arg, "--sleep") == 0) { | |
303 | if (++i >= argc) | |
304 | usage("--sleep requires an argument"); | |
305 | ||
306 | actions.push_back(std::make_unique<SleepAction>(parse_time(globals, argv[i]))); | |
307 | } else if (strcasecmp(arg, "--ios") == 0) { | |
308 | globals.set_timebase({ 125, 3 }, true); | |
309 | /* | |
310 | if (!cpus_set && !iops_set) { | |
311 | globals.set_default_cpu_count(2); // Good guess for most devices | |
312 | globals.set_default_iop_count(4); // Pure speculation... | |
313 | }*/ | |
314 | } else if ((strcmp(arg, "-X") == 0) || strcasecmp(arg, "--k32") == 0) { | |
315 | globals.set_kernel_size(KernelSize::k32); | |
316 | } else if (strcasecmp(arg, "--k64") == 0) { | |
317 | globals.set_kernel_size(KernelSize::k64); | |
318 | } else if (strcasecmp(arg, "--timebase") == 0) { | |
319 | if (++i >= argc) | |
320 | usage("--timebase requires an argument"); | |
321 | arg = argv[i]; | |
322 | ||
323 | mach_timebase_info_data_t timebase; | |
324 | if (sscanf(arg, "%u/%u", &timebase.numer, &timebase.denom) != 2) { | |
325 | usage("Unable to parse --timebase argument"); | |
326 | } | |
327 | globals.set_timebase(timebase, true); | |
328 | } else if (strcasecmp(arg, "--cpus") == 0) { | |
329 | cpus_set = true; | |
330 | if (++i >= argc) | |
331 | usage("--cpus requires an argument"); | |
332 | char* endptr; | |
333 | uint32_t cpus = (uint32_t)strtoul(argv[i], &endptr, 0); | |
334 | if (*endptr != 0) | |
335 | usage("Unable to parse --cpus argument"); | |
336 | globals.set_cpu_count(cpus); | |
337 | } else if (strcasecmp(arg, "--iops") == 0) { | |
338 | iops_set = true; | |
339 | if (++i >= argc) | |
340 | usage("--iops requires an argument"); | |
341 | char* endptr; | |
342 | uint32_t iops = (uint32_t)strtoul(argv[i], &endptr, 0); | |
343 | if (*endptr != 0) | |
344 | usage("Unable to parse --iops argument"); | |
345 | globals.set_iop_count(iops); | |
346 | } else if (strcasecmp(arg, "--raw-timestamps") == 0) { | |
347 | globals.set_should_zero_base_timestamps(false); | |
348 | } else if (strcasecmp(arg, "--mach-absolute-time") == 0) { | |
349 | globals.set_should_print_mach_absolute_timestamps(true); | |
350 | } else if (strcasecmp(arg, "--event-index") == 0) { | |
351 | globals.set_should_print_event_index(true); | |
352 | } else if (strcasecmp(arg, "--no-codes") == 0) { | |
353 | globals.set_should_print_symbolic_event_codes(false); | |
354 | } else if ((strcasecmp(arg, "--process-start-stop") == 0) || (strcasecmp(arg, "--process-start-stops") == 0)) { | |
355 | globals.set_should_print_process_start_stop_timestamps(true); | |
356 | } else if ((strcmp(arg, "-o") == 0) || strcasecmp(arg, "--output") == 0) { | |
357 | if (++i >= argc) | |
358 | usage("--output requires an argument"); | |
359 | ||
360 | FileDescriptor desc(argv[i], O_WRONLY | O_CREAT | O_TRUNC, 0644); | |
361 | if (!desc.is_open()) { | |
362 | char* errmsg = NULL; | |
363 | asprintf(&errmsg, "Unable to create output file at %s", argv[i]); | |
364 | usage(errmsg); | |
365 | } | |
366 | globals.set_output_fd(std::move(desc)); | |
367 | } else { | |
368 | // | |
369 | // Last attempts to divine argument type/intent. | |
370 | // | |
371 | std::string temp(arg); | |
372 | ||
373 | // Is it a .codes file? | |
374 | if (ends_with(temp, ".codes")) { | |
375 | add_trace_codes_path(arg, globals); | |
376 | goto no_error; | |
377 | } | |
378 | ||
379 | if (ends_with(temp, ".trace")) { | |
380 | actions.push_back(create_trace_file_action(argv[i])); | |
381 | goto no_error; | |
382 | } | |
383 | ||
384 | // | |
385 | // ERROR! | |
386 | // | |
387 | char error_buffer[PATH_MAX]; | |
388 | snprintf(error_buffer, sizeof(error_buffer), "Unhandled argument: %s", arg); | |
389 | usage(error_buffer); | |
390 | } | |
391 | no_error: | |
392 | ||
393 | i++; | |
394 | } | |
395 | ||
396 | return actions; | |
397 | } | |
398 | ||
399 | int main (int argc, const char * argv[]) | |
400 | { | |
401 | // | |
402 | // Use host values as defaults. | |
403 | // User overrides as needed via flags. | |
404 | // | |
405 | Globals globals; | |
406 | auto actions = parse_arguments(argc, argv, globals); | |
407 | ||
408 | if (shouldPrintVersion) { | |
409 | printf("%s version: %s", argv[0], __kdprofVersionString); | |
410 | exit(0); | |
411 | } | |
412 | ||
413 | globals.resolve_trace_codes(); | |
414 | ||
415 | // 0x24000004 PPT_test | |
416 | ||
417 | // Validate start/stop, if they are both set. | |
418 | // | |
419 | // The timebase isn't set for the tracefile at this point. This | |
420 | // can sometimes fail when using a desktop timebase and mixed | |
421 | // units (ms & mabs, for example) | |
422 | if (globals.is_summary_start_set() && globals.is_summary_stop_set()) { | |
423 | AbsInterval checker(AbsTime(1), AbsTime(1)); | |
424 | if (globals.summary_stop(checker) <= globals.summary_start(checker)) { | |
425 | usage("The current --stop value is less than or equal to the --start value"); | |
426 | } | |
427 | } | |
428 | ||
429 | // If the user didn't ask for anything, set them up with a basic full trace summary | |
430 | if (!globals.should_print_summary() && | |
431 | !globals.should_print_events() && | |
432 | !globals.should_print_csv_summary() && | |
433 | !globals.should_print_process_start_stop_timestamps() && | |
434 | !globals.is_should_print_summary_set()) | |
435 | { | |
436 | globals.set_should_print_summary(true); | |
437 | } | |
438 | ||
439 | for (auto& action : actions) { | |
440 | action->execute(globals); | |
441 | } | |
442 | ||
443 | return 0; | |
444 | } |