From 24a9c1bbba752056902b93c3a79997b6972030e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Mon, 20 Jul 2009 14:50:14 +0000 Subject: [PATCH] * Added option "-r, --recorded" to profile: this will now retrieve the kernel profile data area, and evaluate its data - it doesn't produce any output yet, though. * _user_system_profiler_recorded() now also makes sure the userland app can read from the buffer area. * Fixed leak in SharedImage::Init(). * Made the symbol retriever more smart when it deals with kernel images; if the image ID is no longer available, it will now use the path based image symbol iterator (and also adds the boot kernel path, in case the module don't have one). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31653 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/bin/debug/profile/Options.h | 2 +- src/bin/debug/profile/SharedImage.cpp | 9 +- src/bin/debug/profile/profile.cpp | 291 +++++++++++++------- src/system/kernel/debug/system_profiler.cpp | 9 +- 4 files changed, 199 insertions(+), 112 deletions(-) diff --git a/src/bin/debug/profile/Options.h b/src/bin/debug/profile/Options.h index 22f2aa6e60..b4cc6eaaae 100644 --- a/src/bin/debug/profile/Options.h +++ b/src/bin/debug/profile/Options.h @@ -5,6 +5,7 @@ #ifndef OPTIONS_H #define OPTIONS_H + #include #include @@ -43,6 +44,5 @@ extern Options gOptions; extern const char* kCommandName; - #endif // OPTIONS_H diff --git a/src/bin/debug/profile/SharedImage.cpp b/src/bin/debug/profile/SharedImage.cpp index e4c05a0695..62140d3ac6 100644 --- a/src/bin/debug/profile/SharedImage.cpp +++ b/src/bin/debug/profile/SharedImage.cpp @@ -55,8 +55,9 @@ SharedImage::Init(team_id owner, image_id imageID) error = debug_create_image_symbol_iterator(lookupContext, imageID, &iterator); if (error != B_OK) { - fprintf(stderr, "Failed to init symbol iterator: %s\n", - strerror(error)); + fprintf(stderr, "Failed to init symbol iterator for image %ld: %s\n", + imageID, strerror(error)); + debug_delete_symbol_lookup_context(lookupContext); return error; } @@ -78,8 +79,8 @@ SharedImage::Init(const char* path) debug_symbol_iterator* iterator; status_t error = debug_create_file_symbol_iterator(path, &iterator); if (error != B_OK) { - fprintf(stderr, "Failed to init symbol iterator: %s\n", - strerror(error)); + fprintf(stderr, "Failed to init symbol iterator for \"%s\": %s\n", + path, strerror(error)); return error; } diff --git a/src/bin/debug/profile/profile.cpp b/src/bin/debug/profile/profile.cpp index d008ee8b56..a7518ce935 100644 --- a/src/bin/debug/profile/profile.cpp +++ b/src/bin/debug/profile/profile.cpp @@ -3,6 +3,7 @@ * Distributed under the terms of the MIT License. */ + #include #include #include @@ -77,6 +78,8 @@ static const char* kUsage = " -k - Don't check kernel images for hits.\n" " -l - Also profile loading the executable.\n" " -o - Print the results to file .\n" + " -r, --recorded - Don't profile, but evaluate a recorded kernel profile\n" + " data.\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" @@ -370,9 +373,22 @@ private: ObjectDeleter imageDeleter(sharedImage); // load the symbols - status_t error = teamID == B_SYSTEM_TEAM - ? sharedImage->Init(teamID, imageInfo.id) - : sharedImage->Init(imageInfo.name); + status_t error; + if (teamID == B_SYSTEM_TEAM) { + error = sharedImage->Init(teamID, imageInfo.id); + if (error != B_OK) { + // The image has obviously been unloaded already, try to get + // it by path. + BString name = imageInfo.name; + if (name.FindFirst('/') == -1) { + // modules without a path are likely to be boot modules + name.Prepend("/system/add-ons/kernel/boot/"); + } + + error = sharedImage->Init(name.String()); + } + } else + error = sharedImage->Init(imageInfo.name); if (error != B_OK) return error; @@ -665,119 +681,86 @@ profile_all(const char* const* programArgs, int programArgCount) } -int -main(int argc, const char* const* argv) +static void +dump_recorded() { - int32 stackDepth = 0; - const char* outputFile = NULL; + // retrieve recorded samples and parameters + system_profiler_parameters profilerParameters; + status_t status = _kern_system_profiler_recorded(&profilerParameters); + if (status != B_OK) { + fprintf(stderr, "%s: Failed to get recorded profiling buffer: %s\n", + kCommandName, strerror(status)); + exit(1); + } - while (true) { - static struct option sLongOptions[] = { - { "all", no_argument, 0, 'a' }, - { "help", no_argument, 0, 'h' }, - { 0, 0, 0, 0 } - }; + // set global options to those of the profiler parameters + gOptions.interval = profilerParameters.interval; + gOptions.stack_depth = profilerParameters.stack_depth; - opterr = 0; // don't print errors - int c = getopt_long(argc, (char**)argv, "+acCfhi:klo:s:v:", - sLongOptions, NULL); - if (c == -1) - break; + // create an area for the sample buffer + area_info info; + status = get_area_info(profilerParameters.buffer_area, &info); + if (status != B_OK) { + fprintf(stderr, "%s: Recorded profiling buffer invalid: %s\n", + kCommandName, strerror(status)); + exit(1); + } - switch (c) { - case 'a': - gOptions.profile_all = true; - break; - case 'c': - gOptions.profile_threads = false; - break; - case 'C': - gOptions.profile_teams = false; - break; - case 'f': - gOptions.stack_depth = 64; - gOptions.analyze_full_stack = true; - break; - case 'h': - print_usage_and_exit(false); - break; - case 'i': - gOptions.interval = atol(optarg); - break; - case 'k': - gOptions.profile_kernel = false; - break; - case 'l': - gOptions.profile_loading = true; - break; - case 'o': - outputFile = optarg; - break; - case 's': - stackDepth = atol(optarg); - break; - case 'v': - gOptions.callgrind_directory = optarg; - gOptions.analyze_full_stack = true; - gOptions.stack_depth = 64; - break; - default: - print_usage_and_exit(true); - break; + system_profiler_buffer_header* bufferHeader + = (system_profiler_buffer_header*)info.address; + + uint8* bufferBase = (uint8*)(bufferHeader + 1); + size_t totalBufferSize = info.size - (bufferBase - (uint8*)bufferHeader); + + // create a thread manager + ThreadManager threadManager(-1); // TODO: We don't need a debugger port! + + // get the current buffer + size_t bufferStart = bufferHeader->start; + size_t bufferSize = bufferHeader->size; + uint8* buffer = bufferBase + bufferStart; + + if (bufferStart + bufferSize <= totalBufferSize) { + process_event_buffer(threadManager, buffer, bufferSize, -1); + } else { + size_t remainingSize = bufferStart + bufferSize - totalBufferSize; + if (!process_event_buffer(threadManager, buffer, + bufferSize - remainingSize, -1)) { + process_event_buffer(threadManager, bufferBase, remainingSize, -1); } } - if (!gOptions.profile_all && optind >= argc) - print_usage_and_exit(true); - - if (stackDepth != 0) - gOptions.stack_depth = stackDepth; - - if (outputFile != NULL) { - gOptions.output = fopen(outputFile, "w+"); - if (gOptions.output == NULL) { - fprintf(stderr, "%s: Failed to open output file \"%s\": %s\n", - kCommandName, outputFile, strerror(errno)); - exit(1); - } - } else - gOptions.output = stdout; - - const char* const* programArgs = argv + optind; - int programArgCount = argc - optind; - - if (gOptions.profile_all) { - profile_all(programArgs, programArgCount); - return 0; + // print results + int32 threadCount = threadManager.CountThreads(); + for (int32 i = 0; i < threadCount; i++) { + Thread* thread = threadManager.ThreadAt(i); + thread->PrintResults(); } +} + +static void +profile_single(const char* const* programArgs, int programArgCount) +{ // 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, - gOptions.profile_loading); - if (threadID < 0) { - fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName, - programArgs[0], strerror(threadID)); - exit(1); - } -// } - - // get the team ID, if we have none yet - if (teamID < 0) { - thread_info threadInfo; - status_t error = get_thread_info(threadID, &threadInfo); - if (error != B_OK) { - fprintf(stderr, "%s: Failed to get info for thread %ld: %s\n", - kCommandName, threadID, strerror(error)); - exit(1); - } - teamID = threadInfo.team; + thread_id threadID = load_program(programArgs, programArgCount, + gOptions.profile_loading); + if (threadID < 0) { + fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName, + programArgs[0], strerror(threadID)); + exit(1); } + // get the team ID + thread_info threadInfo; + status_t error = get_thread_info(threadID, &threadInfo); + if (error != B_OK) { + fprintf(stderr, "%s: Failed to get info for thread %ld: %s\n", + kCommandName, threadID, strerror(error)); + exit(1); + } + team_id teamID = threadInfo.team; + // create a debugger port port_id debuggerPort = create_port(10, "debugger port"); if (debuggerPort < 0) { @@ -899,6 +882,106 @@ main(int argc, const char* const* argv) if (message.origin.thread >= 0 && message.origin.nub_port >= 0) continue_thread(message.origin.nub_port, message.origin.thread); } +} + +int +main(int argc, const char* const* argv) +{ + int32 stackDepth = 0; + bool dumpRecorded = false; + const char* outputFile = NULL; + + while (true) { + static struct option sLongOptions[] = { + { "all", no_argument, 0, 'a' }, + { "help", no_argument, 0, 'h' }, + { "recorded", no_argument, 0, 'r' }, + { 0, 0, 0, 0 } + }; + + opterr = 0; // don't print errors + int c = getopt_long(argc, (char**)argv, "+acCfhi:klo:rs:v:", + sLongOptions, NULL); + if (c == -1) + break; + + switch (c) { + case 'a': + gOptions.profile_all = true; + break; + case 'c': + gOptions.profile_threads = false; + break; + case 'C': + gOptions.profile_teams = false; + break; + case 'f': + gOptions.stack_depth = 64; + gOptions.analyze_full_stack = true; + break; + case 'h': + print_usage_and_exit(false); + break; + case 'i': + gOptions.interval = atol(optarg); + break; + case 'k': + gOptions.profile_kernel = false; + break; + case 'l': + gOptions.profile_loading = true; + break; + case 'o': + outputFile = optarg; + break; + case 'r': + dumpRecorded = true; + break; + case 's': + stackDepth = atol(optarg); + break; + case 'v': + gOptions.callgrind_directory = optarg; + gOptions.analyze_full_stack = true; + gOptions.stack_depth = 64; + break; + default: + print_usage_and_exit(true); + break; + } + } + + if ((!gOptions.profile_all && !dumpRecorded && optind >= argc) + || (dumpRecorded && optind != argc)) + print_usage_and_exit(true); + + if (stackDepth != 0) + gOptions.stack_depth = stackDepth; + + if (outputFile != NULL) { + gOptions.output = fopen(outputFile, "w+"); + if (gOptions.output == NULL) { + fprintf(stderr, "%s: Failed to open output file \"%s\": %s\n", + kCommandName, outputFile, strerror(errno)); + exit(1); + } + } else + gOptions.output = stdout; + + if (dumpRecorded) { + dump_recorded(); + return 0; + } + + const char* const* programArgs = argv + optind; + int programArgCount = argc - optind; + + if (gOptions.profile_all) { + profile_all(programArgs, programArgCount); + return 0; + } + + profile_single(programArgs, programArgCount); return 0; } diff --git a/src/system/kernel/debug/system_profiler.cpp b/src/system/kernel/debug/system_profiler.cpp index 8a6c29aa6d..637f4f327f 100644 --- a/src/system/kernel/debug/system_profiler.cpp +++ b/src/system/kernel/debug/system_profiler.cpp @@ -1362,10 +1362,13 @@ _user_system_profiler_recorded(struct system_profiler_parameters* userParameters if (newArea < 0) return newArea; - sRecordedParameters->buffer_area = newArea; + status_t status = set_area_protection(newArea, B_READ_AREA); + if (status == B_OK) { + sRecordedParameters->buffer_area = newArea; - status_t status = user_memcpy(userParameters, sRecordedParameters, - sizeof(system_profiler_parameters)); + status = user_memcpy(userParameters, sRecordedParameters, + sizeof(system_profiler_parameters)); + } if (status != B_OK) delete_area(newArea);