]> git.saurik.com Git - apple/system_cmds.git/blobdiff - kdprof/kdprof.cpp
system_cmds-643.30.1.tar.gz
[apple/system_cmds.git] / kdprof / kdprof.cpp
diff --git a/kdprof/kdprof.cpp b/kdprof/kdprof.cpp
new file mode 100644 (file)
index 0000000..cd14502
--- /dev/null
@@ -0,0 +1,444 @@
+//
+//  main.cpp
+//  kdprof
+//
+//  Created by James McIlree on 4/15/13.
+//  Copyright (c) 2013 Apple. All rights reserved.
+//
+
+#include "global.h"
+
+// Generated by agvtool
+extern const unsigned char __kdprofVersionString[];
+
+bool shouldPrintVersion = false;
+
+__attribute__((noreturn)) void usage(const char *errorMsg) {
+       if (errorMsg) {
+               fprintf(stderr, "%s\n", errorMsg);
+               exit(1);
+       }
+
+       const char* BOLD = "\033[1m";
+       const char* UNBOLD = "\033[0m";
+
+       // printf("01234567890123456789012345678901234567890123456789012345678901234567890123456789\n");
+       printf("kdprof [options] [path/trace.codes ...] [path/data.trace ...]\n\n");
+       printf("  GLOBAL OPTIONS\n\n");
+       printf("    -h, --help                 Print this message\n");
+       printf("        --version              Print version info\n");
+       printf("    -v, --verbose              Print additional information\n");
+       printf("        --presort-events       Sort events before processing. IOP workaround\n");
+       printf("    -N, --no-default-codes     Do not read the default trace codes\n");
+       printf("\n");
+       printf("  LIVE TRACING OPTIONS\n\n");
+       printf("    -i, --intialize [#]        Initialize the trace buffer, with opt buf count\n");
+       printf("    -r, --remove               Remove the trace buffer\n");
+       printf("    -n, --no-wrap              Do not allow the trace buffer to wrap\n");
+       printf("    -g, --print-kdbg-state     Print the current kdbg state\n");
+       printf("    -e, --enable               Enable collection of events\n");
+       printf("    -d, --disable              Disable collection of events\n");
+       printf("    -t, --collect              Collect and print the trace buffer\n");
+       printf("        --save path            Collect and save the trace buffer to path\n");
+       printf("    -S, --sleep #              Wait for a specified interval\n");
+       printf("\n");
+       printf("  OUTPUT OPTIONS\n\n");
+       printf("    -o, --output path          Print output to path\n");
+       printf("        --summary              Print calculated data (default true)\n");
+       printf("        --no-summary           Do not print calculated data\n");
+       printf("        --csv                  Print a csv formatted summary for use in numbers\n");
+       printf("        --no-csv               Do not print a csv formatted summary\n");
+       printf("        --step #               Step by # time units in summary output\n");
+       printf("        --process              Include per-process summary data\n");
+       printf("        --no-process           Do not include per-process summary data\n");
+       printf("        --thread               Include per-thread summary data\n");
+       printf("        --cpu                  Include per-cpu summary data\n");
+       printf("        --sort-by-cpu          Sort process/thread lists by cpu usage\n");
+       printf("        --sort-by-vmfault      Sort process/thread lists by vmfault time\n");
+       printf("        --sort-by-io-wait      Sort process/thread lists by IO time\n");
+       printf("        --sort-by-io-ops       Sort process/thread lists by # IO Ops\n");
+       printf("        --sort-by-io-size      Sort process/thread lists by IO bytes\n");
+       printf("        --sort-by-pid          Sort process/thread lists by pid/tid\n");
+       printf("        --events               Enable individual event printing\n");
+       printf("        --no-events            Disable individual event printing\n");
+       printf("        --raw-timestamps       Print timestamps as raw values, not deltas\n");
+       printf("        --mach-absolute-time   Print timestamps in mach absolute time\n");
+       printf("        --event-index          Print the index of each event\n");
+       printf("        --no-codes             Print hex trace codes, not symbolic\n");
+       printf("        --process-start-stop   Print start/stop information about each process\n");
+       printf("\n");
+       printf("  DEPRECATED OPTIONS\n\n");
+       printf("    -X, --k32                  Trace data is from a 32 bit kernel\n");
+       printf("        --k64                  Trace data is from a 64 bit kernel\n");
+       printf("        --codes path           read trace codes from path\n");
+       printf("        --trace path           read trace data from path\n");
+       printf("        --ios                  Treat data as coming from an iOS device\n");
+       printf("        --timebase #/#         Set the mach_timebase\n");
+       printf("        --cpus #               Set the # of cpus.\n");
+       printf("        --iops #               Set the # of iops.\n");
+       printf("\n");
+       printf("  OPTION ARGUMENTS\n\n");
+       printf("      All arguments that specifiy a time value may use the following postfixes\n\n");
+       printf("        s                      Seconds\n");
+       printf("        ms                     Milliseconds\n");
+       printf("        us                     Microseconds\n");
+       printf("        ns                     Nanoseconds\n");
+       printf("        mabs                   Mach Absolute Time Units\n");
+       printf("\n");
+       // printf("01234567890123456789012345678901234567890123456789012345678901234567890123456789\n");
+       printf("  USAGE\n");
+       printf("\n");
+       printf("      Arguments are parsed in order. Long form flags are not case sensitive.\n");
+       printf("    Live tracing and trace file arguments are pushed onto an execution stack\n");
+       printf("    and processed after argument parsing has completed.\n");
+       printf("\n");
+       printf("      Files ending in .trace or .codes may omit the --trace or --codes flag\n");
+       printf("    In most cases, you do not need to specify a kernel size or timebase, it is\n");
+       printf("    determined automatically.\n");
+       printf("\n");
+       printf("      Modern trace(s) have an embedded ap/iop cpu count. If you need to parse\n");
+       printf("    an older file, you will want to set these. Typically you would set the AP\n");
+       printf("    cpu count to the number of active cpus, and the IOP cpu count to zero.\n");
+       printf("\n");
+       printf("  EXAMPLES\n");
+       printf("\n");
+       printf("    %skdprof InterestingData.trace%s\n", BOLD, UNBOLD);
+       printf("        Print a summary of per process cpu usage in InterestingData.trace\n");
+       printf("\n");
+       printf("    %skdprof --step 100ms InterestingData.trace%s\n", BOLD, UNBOLD);
+       printf("        Print summaries of the per process cpu usage in InterestingData.trace,\n");
+       printf("        one for each 100ms of time\n");
+       printf("\n");
+       printf("    %skdprof --thread --step 100ms InterestingData.trace%s\n", BOLD, UNBOLD);
+       printf("        Print summaries of the per process and per thread cpu usage in\n");
+       printf("        InterestingData.trace, one for each 100ms of time\n");
+       printf("\n");
+       printf("    %skdprof -r -i 100000 -e -S 1 -d -t%s\n", BOLD, UNBOLD);
+       printf("        Reinit the trace buffer with 100000 entries, enable it, wait 1 second,\n");
+       printf("        and then collect/print the trace buffer\n");
+       printf("\n");
+       printf("    %skdprof --events foo.trace%s\n", BOLD, UNBOLD);
+       printf("        Print the events in foo.trace\n");
+       printf("\n");
+       exit(1);
+}
+
+static void add_trace_codes_path(const char* path, Globals& globals) {
+       if (Path::is_file(path, true)) {
+               char resolved_path[PATH_MAX];
+               if (realpath(path, resolved_path)) {
+                       globals.append_trace_codes_at_path(resolved_path);
+                       return;
+               }
+       }
+       char* errmsg = NULL;
+       asprintf(&errmsg, "Trace codes path %s is not valid", path);
+       usage(errmsg);
+}
+
+static std::unique_ptr<Action> create_trace_file_action(const char* trace_file_path) {
+       if (Path::is_file(trace_file_path, true)) {
+               char resolved_path[PATH_MAX];
+               if (realpath(trace_file_path, resolved_path)) {
+                       return std::make_unique<TraceFileAction>(resolved_path);
+               }
+       }
+       char* errmsg = NULL;
+       asprintf(&errmsg, "Trace data path %s is not valid", trace_file_path);
+       usage(errmsg);
+}
+
+//
+// Must take globals so it can do the timebase conversions for mabs values!
+//
+static NanoTime parse_time(Globals& globals, const char* arg) {
+       
+       char* units;
+       uint64_t value = strtoull(arg, &units, 0);
+
+       // Unspecified units are treated as seconds
+       if (*units == 0 || strcmp(units, "s") == 0) {
+               return NanoTime(value * NANOSECONDS_PER_SECOND);
+       }
+
+       if (strcmp(units, "ms") == 0)
+               return NanoTime(value * NANOSECONDS_PER_MILLISECOND);
+
+       if (strcmp(units, "us") == 0)
+               return NanoTime(value * NANOSECONDS_PER_MICROSECOND);
+
+       if (strcmp(units, "ns") == 0)
+               return NanoTime(value);
+
+       if (strcmp(units, "mabs") == 0) {
+               return AbsTime(value).nano_time(globals.timebase());
+       }
+
+       usage("Unable to parse units on time value");
+}
+
+static std::vector<std::unique_ptr<Action>> parse_arguments(int argc, const char* argv[], Globals& globals) {
+       int i = 1;
+       bool cpus_set = false;
+       bool iops_set = false;
+       
+       std::vector<std::unique_ptr<Action>> actions;
+
+       while (i < argc) {
+               const char* arg = argv[i];
+               if ((strcmp(arg, "-h") == 0) || (strcasecmp(arg, "--help") == 0)) {
+                       usage(NULL);
+               } else if ((strcasecmp(arg, "--version") == 0)) {
+                       shouldPrintVersion = true;
+               } else if ((strcmp(arg, "-v") == 0) || strcasecmp(arg, "--verbose") == 0) {
+                       globals.set_is_verbose(true);
+               } else if ((strcasecmp(arg, "--summary") == 0)) {
+                       globals.set_should_print_summary(true);
+               } else if ((strcasecmp(arg, "--no-summary") == 0)) {
+                       globals.set_should_print_summary(false);
+               } else if ((strcasecmp(arg, "--csv") == 0)) {
+                       globals.set_should_print_csv_summary(true);
+               } else if ((strcasecmp(arg, "--no-csv") == 0)) {
+                       globals.set_should_print_csv_summary(false);
+               } else if (strcasecmp(arg, "--step") == 0) {
+                       if (++i >= argc)
+                               usage("--step requires an argument");
+
+                       globals.set_summary_step(argv[i]);
+                       // Force a blow up now if the arg is unparseable
+                       globals.summary_step(AbsInterval(AbsTime(1), AbsTime(1)));
+               } else if (strcasecmp(arg, "--start") == 0) {
+                       if (++i >= argc)
+                               usage("--start requires an argument");
+
+                       globals.set_summary_start(argv[i]);
+                       // Force a blow up now if the arg is unparseable
+                       globals.summary_start(AbsInterval(AbsTime(1), AbsTime(1)));
+               } else if (strcasecmp(arg, "--stop") == 0) {
+                       if (++i >= argc)
+                               usage("--stop requires an argument");
+
+                       globals.set_summary_stop(argv[i]);
+                       // Force a blow up now if the arg is unparseable
+                       globals.summary_stop(AbsInterval(AbsTime(1), AbsTime(1)));
+               } else if ((strcasecmp(arg, "--cpu") == 0)) {
+                       globals.set_should_print_cpu_summaries(true);
+               } else if ((strcasecmp(arg, "--processes") == 0) || (strcasecmp(arg, "--process") == 0)) {
+                       globals.set_should_print_process_summaries(true);
+               } else if ((strcasecmp(arg, "--no-processes") == 0) || (strcasecmp(arg, "--no-process") == 0)) {
+                       globals.set_should_print_process_summaries(false);
+               } else if ((strcasecmp(arg, "--threads") == 0) || (strcasecmp(arg, "--thread") == 0)) {
+                       globals.set_should_print_thread_summaries(true);
+               } else if ((strcasecmp(arg, "--sort-by-cpu") == 0)) {
+                       globals.set_sort_key(kSortKey::CPU);
+               } else if ((strcasecmp(arg, "--sort-by-pid") == 0)) {
+                       globals.set_sort_key(kSortKey::ID);
+               } else if ((strcasecmp(arg, "--sort-by-vmfault") == 0)) {
+                       globals.set_sort_key(kSortKey::VMFault);
+               } else if ((strcasecmp(arg, "--sort-by-io") == 0)) {
+                       globals.set_sort_key(kSortKey::IO_Wait);
+               } else if ((strcasecmp(arg, "--sort-by-io-wait") == 0)) {
+                       globals.set_sort_key(kSortKey::IO_Wait);
+               } else if ((strcasecmp(arg, "--sort-by-io-ops") == 0)) {
+                       globals.set_sort_key(kSortKey::IO_Ops);
+               } else if ((strcasecmp(arg, "--sort-by-io-size") == 0)) {
+                       globals.set_sort_key(kSortKey::IO_Size);
+               } else if ((strcasecmp(arg, "--events") == 0)) {
+                       globals.set_should_print_events(true);
+               } else if ((strcasecmp(arg, "--no-events") == 0)) {
+                       globals.set_should_print_events(false);
+               } else if ((strcasecmp(arg, "--presort-events") == 0)) {
+                       globals.set_should_presort_events(true);
+               } else if ((strcmp(arg, "-N") == 0) || strcasecmp(arg, "--no-default-codes") == 0) {
+                       globals.set_should_read_default_trace_codes(false);
+               } else if (strcasecmp(arg, "--codes") == 0) {
+                       if (++i >= argc)
+                               usage("--codes requires an argument");
+                       add_trace_codes_path(argv[i], globals);
+               } else if (strcasecmp(arg, "--trace") == 0) {
+                       if (++i >= argc)
+                               usage("--trace requires an argument");
+
+                       actions.push_back(create_trace_file_action(argv[i]));
+               } else if ((strcmp(arg, "-i") == 0) || strcasecmp(arg, "--initialize") == 0) {
+                       // The buffers argument is optional
+                       uint32_t buffers_default = 0;
+                       
+                       if (i + 1 < argc) {
+                               arg = argv[i+1];
+                               char* endptr;
+                               uint32_t temp = (uint32_t)strtoul(arg, &endptr, 0);
+                               if (*endptr == 0) {
+                                       // Consume the following argument if the conversion worked
+                                       buffers_default = temp;
+                                       i++;
+                               }
+
+                       }
+                       actions.push_back(std::make_unique<InitializeAction>(buffers_default));
+               } else if ((strcmp(arg, "-r") == 0) || strcasecmp(arg, "--remove") == 0) {
+                       actions.push_back(std::make_unique<RemoveAction>());
+               } else if ((strcmp(arg, "-n") == 0) || strcasecmp(arg, "--no-wrap") == 0) {
+                       actions.push_back(std::make_unique<NoWrapAction>());
+               } else if ((strcmp(arg, "-g") == 0) || strcasecmp(arg, "--print-kdbg-state") == 0) {
+                       actions.push_back(std::make_unique<PrintStateAction>());
+               } else if ((strcmp(arg, "-e") == 0) || strcasecmp(arg, "--enable") == 0) {
+                       actions.push_back(std::make_unique<EnableAction>());
+               } else if ((strcmp(arg, "-d") == 0) || strcasecmp(arg, "--disable") == 0) {
+                       actions.push_back(std::make_unique<DisableAction>());
+               } else if ((strcmp(arg, "-t") == 0) || strcasecmp(arg, "--collect") == 0) {
+                       actions.push_back(std::make_unique<CollectAction>());
+               } else if (strcasecmp(arg, "--save") == 0) {
+                       if (++i >= argc)
+                               usage("--save requires an argument");
+
+                       FileDescriptor desc(argv[i], O_WRONLY | O_CREAT | O_TRUNC, 0644);
+                       if (!desc.is_open()) {
+                               char* errmsg = NULL;
+                               asprintf(&errmsg, "Unable to create save file at %s", argv[i]);
+                               usage(errmsg);
+                       }
+                       actions.push_back(std::make_unique<SaveTraceAction>(std::move(desc)));
+               } else if ((strcmp(arg, "-S") == 0) || strcasecmp(arg, "--sleep") == 0) {
+                       if (++i >= argc)
+                               usage("--sleep requires an argument");
+
+                       actions.push_back(std::make_unique<SleepAction>(parse_time(globals, argv[i])));
+               } else if (strcasecmp(arg, "--ios") == 0) {
+                       globals.set_timebase({ 125, 3 }, true);
+                       /*
+                       if (!cpus_set && !iops_set) {
+                               globals.set_default_cpu_count(2); // Good guess for most devices
+                               globals.set_default_iop_count(4); // Pure speculation...
+                       }*/
+               } else if ((strcmp(arg, "-X") == 0) || strcasecmp(arg, "--k32") == 0) {
+                       globals.set_kernel_size(KernelSize::k32);
+               } else if (strcasecmp(arg, "--k64") == 0) {
+                       globals.set_kernel_size(KernelSize::k64);
+               } else if (strcasecmp(arg, "--timebase") == 0) {
+                       if (++i >= argc)
+                               usage("--timebase requires an argument");
+                       arg = argv[i];
+
+                       mach_timebase_info_data_t timebase;
+                       if (sscanf(arg, "%u/%u", &timebase.numer, &timebase.denom) != 2) {
+                               usage("Unable to parse --timebase argument");
+                       }
+                       globals.set_timebase(timebase, true);
+               } else if (strcasecmp(arg, "--cpus") == 0) {
+                       cpus_set = true;
+                       if (++i >= argc)
+                               usage("--cpus requires an argument");
+                       char* endptr;
+                       uint32_t cpus = (uint32_t)strtoul(argv[i], &endptr, 0);
+                       if (*endptr != 0)
+                               usage("Unable to parse --cpus argument");
+                       globals.set_cpu_count(cpus);
+               } else if (strcasecmp(arg, "--iops") == 0) {
+                       iops_set = true;
+                       if (++i >= argc)
+                               usage("--iops requires an argument");
+                       char* endptr;
+                       uint32_t iops = (uint32_t)strtoul(argv[i], &endptr, 0);
+                       if (*endptr != 0)
+                               usage("Unable to parse --iops argument");
+                       globals.set_iop_count(iops);
+               } else if (strcasecmp(arg, "--raw-timestamps") == 0) {
+                       globals.set_should_zero_base_timestamps(false);
+               } else if (strcasecmp(arg, "--mach-absolute-time") == 0) {
+                       globals.set_should_print_mach_absolute_timestamps(true);
+               } else if (strcasecmp(arg, "--event-index") == 0) {
+                       globals.set_should_print_event_index(true);
+               } else if (strcasecmp(arg, "--no-codes") == 0) {
+                       globals.set_should_print_symbolic_event_codes(false);
+               } else if ((strcasecmp(arg, "--process-start-stop") == 0) || (strcasecmp(arg, "--process-start-stops") == 0)) {
+                       globals.set_should_print_process_start_stop_timestamps(true);
+               } else if ((strcmp(arg, "-o") == 0) || strcasecmp(arg, "--output") == 0) {
+                       if (++i >= argc)
+                               usage("--output requires an argument");
+
+                       FileDescriptor desc(argv[i], O_WRONLY | O_CREAT | O_TRUNC, 0644);
+                       if (!desc.is_open()) {
+                               char* errmsg = NULL;
+                               asprintf(&errmsg, "Unable to create output file at %s", argv[i]);
+                               usage(errmsg);
+                       }
+                       globals.set_output_fd(std::move(desc));
+               }  else {
+                       //
+                       // Last attempts to divine argument type/intent.
+                       //
+                       std::string temp(arg);
+
+                       // Is it a .codes file?
+                       if (ends_with(temp, ".codes")) {
+                               add_trace_codes_path(arg, globals);
+                               goto no_error;
+                       }
+
+                       if (ends_with(temp, ".trace")) {
+                               actions.push_back(create_trace_file_action(argv[i]));
+                               goto no_error;
+                       }
+
+                       //
+                       // ERROR!
+                       //
+                       char error_buffer[PATH_MAX];
+                       snprintf(error_buffer, sizeof(error_buffer), "Unhandled argument: %s", arg);
+                       usage(error_buffer);
+               }
+       no_error:
+
+               i++;
+       }
+       
+       return actions;
+}
+
+int main (int argc, const char * argv[])
+{
+       //
+       // Use host values as defaults.
+       // User overrides as needed via flags.
+       //
+       Globals globals;
+       auto actions = parse_arguments(argc, argv, globals);
+
+       if (shouldPrintVersion) {
+               printf("%s version:             %s", argv[0], __kdprofVersionString);
+               exit(0);
+       }
+
+       globals.resolve_trace_codes();
+
+       // 0x24000004   PPT_test
+
+       // Validate start/stop, if they are both set.
+       //
+       // The timebase isn't set for the tracefile at this point. This
+       // can sometimes fail when using a desktop timebase and mixed
+       // units (ms & mabs, for example)
+       if (globals.is_summary_start_set() && globals.is_summary_stop_set()) {
+               AbsInterval checker(AbsTime(1), AbsTime(1));
+               if (globals.summary_stop(checker) <= globals.summary_start(checker)) {
+                       usage("The current --stop value is less than or equal to the --start value");
+               }
+       }
+
+       // If the user didn't ask for anything, set them up with a basic full trace summary
+       if (!globals.should_print_summary() &&
+           !globals.should_print_events() &&
+           !globals.should_print_csv_summary() &&
+           !globals.should_print_process_start_stop_timestamps() &&
+           !globals.is_should_print_summary_set())
+       {
+               globals.set_should_print_summary(true);
+       }
+       
+       for (auto& action : actions) {
+               action->execute(globals);
+       }
+
+       return 0;
+}