From 061eba4a0ffd423eb6098c4c45abec7b031b71a4 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sun, 21 Sep 2008 22:31:18 +0000 Subject: [PATCH] Implemented various command line options and updated usage text. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27681 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/bin/debug/profile.cpp | 170 +++++++++++++++++++++++++++----------- 1 file changed, 120 insertions(+), 50 deletions(-) diff --git a/src/bin/debug/profile.cpp b/src/bin/debug/profile.cpp index c74a57575a..ac03c16541 100644 --- a/src/bin/debug/profile.cpp +++ b/src/bin/debug/profile.cpp @@ -4,6 +4,7 @@ */ #include +#include #include #include #include @@ -35,10 +36,61 @@ class Team; enum { SAMPLE_AREA_SIZE = 128 * 1024, - SAMPLE_STACK_DEPTH = 5 }; +static const char* kUsage = + "Usage: %s [ ] \n" + "Executes the given command line and periodically samples\n" + "all started threads' program counters. When a thread terminates, a list\n" + "of the functions where the thread was encountered is printed.\n" + "\n" + "Options:\n" + " -c - Don't profile child threads. Default is to\n" + " recursively profile all threads created by a profiled\n" + " thread.\n" + " -C - Don't profile child teams. Default is to recursively\n" + " profile all teams created by a profiled team.\n" + " -h, --help - Print this usage info.\n" + " -i - Use a tick interval of microseconds.\n" + " Default is 1000 (1 ms). On a fast machine, a shorter\n" + " interval might lead to better results, while it might\n" + " make them worse on slow machines.\n" + " -k - Don't check kernel images for hits.\n" + " -l - Also profile loading the executable.\n" + " -o - Print the results to file .\n" + " -s - Number of return address samples to take from the\n" + " caller stack per tick. If the topmost address doesn't\n" + " hit a known image, the next address will be matched\n" + " (and so on).\n" +; + + +struct Options { + Options() + : + interval(1000), + stack_depth(5), + output(NULL), + profile_kernel(true), + profile_loading(false), + profile_teams(true), + profile_threads(true) + { + } + + bigtime_t interval; + int32 stack_depth; + FILE* output; + bool profile_kernel; + bool profile_loading; + bool profile_teams; + bool profile_threads; +}; + +static Options sOptions; + + class Symbol { public: Symbol(Image* image, addr_t base, size_t size, const char* name) @@ -132,15 +184,16 @@ public: status_t LoadSymbols(debug_symbol_lookup_context* lookupContext) { -// printf("Loading symbols of image \"%s\" (%ld)...\n", fInfo.name, -// fInfo.id); +// fprintf(sOptions.output, "Loading symbols of image \"%s\" (%ld)...\n", +// fInfo.name, fInfo.id); // create symbol iterator debug_symbol_iterator* iterator; status_t error = debug_create_image_symbol_iterator(lookupContext, fInfo.id, &iterator); if (error != B_OK) { - printf("Failed to init symbol iterator: %s\n", strerror(error)); + fprintf(stderr, "Failed to init symbol iterator: %s\n", + strerror(error)); return error; } @@ -507,47 +560,50 @@ public: std::sort(hitSymbols, hitSymbols + hitSymbolCount); int64 totalTicks = fTotalTicks; - printf("\nprofiling results for thread \"%s\" (%ld):\n", Name(), ID()); - printf(" tick interval: %lld us\n", fInterval); - printf(" total ticks: %lld (%lld us)\n", totalTicks, - totalTicks * fInterval); + fprintf(sOptions.output, "\nprofiling results for thread \"%s\" " + "(%ld):\n", Name(), ID()); + fprintf(sOptions.output, " tick interval: %lld us\n", fInterval); + fprintf(sOptions.output, " total ticks: %lld (%lld us)\n", + totalTicks, totalTicks * fInterval); if (totalTicks == 0) totalTicks = 1; - printf(" unknown ticks: %lld (%lld us, %6.2f%%)\n", + fprintf(sOptions.output, " unknown ticks: %lld (%lld us, %6.2f%%)\n", fUnkownTicks, fUnkownTicks * fInterval, 100.0 * fUnkownTicks / totalTicks); - printf(" dropped ticks: %lld (%lld us, %6.2f%%)\n", + fprintf(sOptions.output, " dropped ticks: %lld (%lld us, %6.2f%%)\n", fDroppedTicks, fDroppedTicks * fInterval, 100.0 * fDroppedTicks / totalTicks); if (imageCount > 0) { - printf("\n"); - printf(" hits unknown image\n"); - printf(" -------------------------------------------------" - "-----------------------------\n"); + fprintf(sOptions.output, "\n"); + fprintf(sOptions.output, " hits unknown image\n"); + fprintf(sOptions.output, " ---------------------------------------" + "---------------------------------------\n"); for (int32 k = 0; k < imageCount; k++) { ThreadImage* image = images[k]; const image_info& imageInfo = image->GetImage()->Info(); - printf(" %10lld %10lld %7ld %s\n", image->TotalHits(), - image->UnknownHits(), imageInfo.id, imageInfo.name); + fprintf(sOptions.output, " %10lld %10lld %7ld %s\n", + image->TotalHits(), image->UnknownHits(), imageInfo.id, + imageInfo.name); } } if (hitSymbolCount > 0) { - printf("\n"); - printf(" hits in us in %% image function\n"); - printf(" -------------------------------------------------" - "-----------------------------\n"); + fprintf(sOptions.output, "\n"); + fprintf(sOptions.output, " hits in us in %% " + "image function\n"); + fprintf(sOptions.output, " ---------------------------------------" + "---------------------------------------\n"); for (int32 i = 0; i < hitSymbolCount; i++) { const HitSymbol& hitSymbol = hitSymbols[i]; const Symbol* symbol = hitSymbol.symbol; - printf(" %10lld %10lld %6.2f %6ld %s\n", hitSymbol.hits, - hitSymbol.hits * fInterval, + fprintf(sOptions.output, " %10lld %10lld %6.2f %6ld %s\n", + hitSymbol.hits, hitSymbol.hits * fInterval, 100.0 * hitSymbol.hits / totalTicks, symbol->image->ID(), symbol->Name()); } } else - printf(" no functions were hit\n"); + fprintf(sOptions.output, " no functions were hit\n"); } private: @@ -660,7 +716,7 @@ public: return error; // also try to load the kernel images and symbols - { + if (sOptions.profile_kernel) { // fake a debug context -- it's not really needed anyway debug_context debugContext; debugContext.team = B_SYSTEM_TEAM; @@ -727,9 +783,9 @@ public: debug_nub_start_profiler message; message.reply_port = fDebugContext.reply_port; message.thread = thread->ID(); - message.interval = 1000; + message.interval = sOptions.interval; message.sample_area = sampleArea; - message.stack_depth = SAMPLE_STACK_DEPTH; + message.stack_depth = sOptions.stack_depth; debug_nub_start_profiler_reply reply; status_t error = send_debug_message(&fDebugContext, @@ -1030,21 +1086,6 @@ Thread::_SynchronizeImages() } -// TODO: Adjust! -static const char* kUsage = - "Usage: %s [ ] \n" - "Executes the given command line and print an analysis of\n" - "the user and kernel times of all threads that ran during that time.\n" - "\n" - "Options:\n" - " -h, --help - Print this usage info.\n" - " -o - Print the results to file .\n" - " -s - Also perform a scheduling analysis over the time the\n" - " child process ran. This requires that scheduler kernel\n" - " tracing had been enabled at compile time.\n" -; - - static void print_usage_and_exit(bool error) { @@ -1073,33 +1114,45 @@ get_id(const char *str, int32 &id) int main(int argc, const char* const* argv) { -// const char* outputFile = NULL; -// bool schedulingAnalysis = false; + const char* outputFile = NULL; while (true) { static struct option sLongOptions[] = { { "help", no_argument, 0, 'h' }, -// { "output", required_argument, 0, 'o' }, { 0, 0, 0, 0 } }; opterr = 0; // don't print errors - int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL); + int c = getopt_long(argc, (char**)argv, "+cChi:klo:s:", sLongOptions, + NULL); if (c == -1) break; switch (c) { + case 'c': + sOptions.profile_threads = false; + break; + case 'C': + sOptions.profile_teams = false; + break; case 'h': print_usage_and_exit(false); break; -/* case 'o': + case 'i': + sOptions.interval = atol(optarg); + break; + case 'k': + sOptions.profile_kernel = false; + break; + case 'l': + sOptions.profile_loading = true; + break; + case 'o': outputFile = optarg; break; case 's': - schedulingAnalysis = true; + sOptions.stack_depth = atol(optarg); break; -*/ - default: print_usage_and_exit(true); break; @@ -1112,13 +1165,24 @@ main(int argc, const char* const* argv) const char* const* programArgs = argv + optind; int programArgCount = argc - optind; + if (outputFile != NULL) { + sOptions.output = fopen(outputFile, "w+"); + if (sOptions.output == NULL) { + fprintf(stderr, "%s: Failed to open output file \"%s\": %s\n", + kCommandName, outputFile, strerror(errno)); + exit(1); + } + } else + sOptions.output = stdout; + // get thread/team to be debugged thread_id threadID = -1; team_id teamID = -1; // if (programArgCount > 1 // || !get_id(*programArgs, (traceTeam ? teamID : thread))) { // we've been given an executable and need to load it - threadID = load_program(programArgs, programArgCount, false); + threadID = load_program(programArgs, programArgCount, + sOptions.profile_loading); if (threadID < 0) { fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName, programArgs[0], strerror(threadID)); @@ -1206,6 +1270,9 @@ main(int argc, const char* const* argv) break; case B_DEBUGGER_MESSAGE_THREAD_CREATED: + if (!sOptions.profile_threads) + break; + threadManager.AddThread(message.thread_created.new_thread); break; case B_DEBUGGER_MESSAGE_THREAD_DELETED: @@ -1213,6 +1280,9 @@ main(int argc, const char* const* argv) break; case B_DEBUGGER_MESSAGE_IMAGE_CREATED: + if (!sOptions.profile_teams) + break; + if (Team* team = threadManager.FindTeam(message.origin.team)) { team->AddImage(message.image_created.info, message.image_created.image_event);