5 // Created by James McIlree on 4/15/13.
6 // Copyright (c) 2013 Apple. All rights reserved.
11 // Generated by agvtool
12 extern const unsigned char __kdprofVersionString
[];
14 bool shouldPrintVersion
= false;
16 __attribute__((noreturn
)) void usage(const char *errorMsg
) {
18 fprintf(stderr
, "%s\n", errorMsg
);
22 const char* BOLD
= "\033[1m";
23 const char* UNBOLD
= "\033[0m";
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");
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");
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");
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");
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");
88 // printf("01234567890123456789012345678901234567890123456789012345678901234567890123456789\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");
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");
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");
103 printf(" EXAMPLES\n");
105 printf(" %skdprof InterestingData.trace%s\n", BOLD
, UNBOLD
);
106 printf(" Print a summary of per process cpu usage in InterestingData.trace\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");
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");
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");
120 printf(" %skdprof --events foo.trace%s\n", BOLD
, UNBOLD
);
121 printf(" Print the events in foo.trace\n");
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
);
135 asprintf(&errmsg
, "Trace codes path %s is not valid", path
);
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
);
147 asprintf(&errmsg
, "Trace data path %s is not valid", trace_file_path
);
152 // Must take globals so it can do the timebase conversions for mabs values!
154 static NanoTime
parse_time(Globals
& globals
, const char* arg
) {
157 uint64_t value
= strtoull(arg
, &units
, 0);
159 // Unspecified units are treated as seconds
160 if (*units
== 0 || strcmp(units
, "s") == 0) {
161 return NanoTime(value
* NANOSECONDS_PER_SECOND
);
164 if (strcmp(units
, "ms") == 0)
165 return NanoTime(value
* NANOSECONDS_PER_MILLISECOND
);
167 if (strcmp(units
, "us") == 0)
168 return NanoTime(value
* NANOSECONDS_PER_MICROSECOND
);
170 if (strcmp(units
, "ns") == 0)
171 return NanoTime(value
);
173 if (strcmp(units
, "mabs") == 0) {
174 return AbsTime(value
).nano_time(globals
.timebase());
177 usage("Unable to parse units on time value");
180 static std::vector
<std::unique_ptr
<Action
>> parse_arguments(int argc
, const char* argv
[], Globals
& globals
) {
182 bool cpus_set
= false;
183 bool iops_set
= false;
185 std::vector
<std::unique_ptr
<Action
>> actions
;
188 const char* arg
= argv
[i
];
189 if ((strcmp(arg
, "-h") == 0) || (strcasecmp(arg
, "--help") == 0)) {
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) {
205 usage("--step requires an argument");
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) {
212 usage("--start requires an argument");
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) {
219 usage("--stop requires an argument");
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) {
256 usage("--codes requires an argument");
257 add_trace_codes_path(argv
[i
], globals
);
258 } else if (strcasecmp(arg
, "--trace") == 0) {
260 usage("--trace requires an argument");
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;
270 uint32_t temp
= (uint32_t)strtoul(arg
, &endptr
, 0);
272 // Consume the following argument if the conversion worked
273 buffers_default
= temp
;
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) {
293 usage("--save requires an argument");
295 FileDescriptor
desc(argv
[i
], O_WRONLY
| O_CREAT
| O_TRUNC
, 0644);
296 if (!desc
.is_open()) {
298 asprintf(&errmsg
, "Unable to create save file at %s", argv
[i
]);
301 actions
.push_back(std::make_unique
<SaveTraceAction
>(std::move(desc
)));
302 } else if ((strcmp(arg
, "-S") == 0) || strcasecmp(arg
, "--sleep") == 0) {
304 usage("--sleep requires an argument");
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);
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...
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) {
320 usage("--timebase requires an argument");
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");
327 globals
.set_timebase(timebase
, true);
328 } else if (strcasecmp(arg
, "--cpus") == 0) {
331 usage("--cpus requires an argument");
333 uint32_t cpus
= (uint32_t)strtoul(argv
[i
], &endptr
, 0);
335 usage("Unable to parse --cpus argument");
336 globals
.set_cpu_count(cpus
);
337 } else if (strcasecmp(arg
, "--iops") == 0) {
340 usage("--iops requires an argument");
342 uint32_t iops
= (uint32_t)strtoul(argv
[i
], &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) {
358 usage("--output requires an argument");
360 FileDescriptor
desc(argv
[i
], O_WRONLY
| O_CREAT
| O_TRUNC
, 0644);
361 if (!desc
.is_open()) {
363 asprintf(&errmsg
, "Unable to create output file at %s", argv
[i
]);
366 globals
.set_output_fd(std::move(desc
));
369 // Last attempts to divine argument type/intent.
371 std::string
temp(arg
);
373 // Is it a .codes file?
374 if (ends_with(temp
, ".codes")) {
375 add_trace_codes_path(arg
, globals
);
379 if (ends_with(temp
, ".trace")) {
380 actions
.push_back(create_trace_file_action(argv
[i
]));
387 char error_buffer
[PATH_MAX
];
388 snprintf(error_buffer
, sizeof(error_buffer
), "Unhandled argument: %s", arg
);
399 int main (int argc
, const char * argv
[])
402 // Use host values as defaults.
403 // User overrides as needed via flags.
406 auto actions
= parse_arguments(argc
, argv
, globals
);
408 if (shouldPrintVersion
) {
409 printf("%s version: %s", argv
[0], __kdprofVersionString
);
413 globals
.resolve_trace_codes();
415 // 0x24000004 PPT_test
417 // Validate start/stop, if they are both set.
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");
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())
436 globals
.set_should_print_summary(true);
439 for (auto& action
: actions
) {
440 action
->execute(globals
);