* 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
This commit is contained in:
Axel Dörfler
2009-07-20 14:50:14 +00:00
parent c7544c1390
commit 24a9c1bbba
4 changed files with 199 additions and 112 deletions
+1 -1
View File
@@ -5,6 +5,7 @@
#ifndef OPTIONS_H #ifndef OPTIONS_H
#define OPTIONS_H #define OPTIONS_H
#include <stdio.h> #include <stdio.h>
#include <OS.h> #include <OS.h>
@@ -43,6 +44,5 @@ extern Options gOptions;
extern const char* kCommandName; extern const char* kCommandName;
#endif // OPTIONS_H #endif // OPTIONS_H
+5 -4
View File
@@ -55,8 +55,9 @@ SharedImage::Init(team_id owner, image_id imageID)
error = debug_create_image_symbol_iterator(lookupContext, imageID, error = debug_create_image_symbol_iterator(lookupContext, imageID,
&iterator); &iterator);
if (error != B_OK) { if (error != B_OK) {
fprintf(stderr, "Failed to init symbol iterator: %s\n", fprintf(stderr, "Failed to init symbol iterator for image %ld: %s\n",
strerror(error)); imageID, strerror(error));
debug_delete_symbol_lookup_context(lookupContext);
return error; return error;
} }
@@ -78,8 +79,8 @@ SharedImage::Init(const char* path)
debug_symbol_iterator* iterator; debug_symbol_iterator* iterator;
status_t error = debug_create_file_symbol_iterator(path, &iterator); status_t error = debug_create_file_symbol_iterator(path, &iterator);
if (error != B_OK) { if (error != B_OK) {
fprintf(stderr, "Failed to init symbol iterator: %s\n", fprintf(stderr, "Failed to init symbol iterator for \"%s\": %s\n",
strerror(error)); path, strerror(error));
return error; return error;
} }
+187 -104
View File
@@ -3,6 +3,7 @@
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <getopt.h> #include <getopt.h>
@@ -77,6 +78,8 @@ static const char* kUsage =
" -k - Don't check kernel images for hits.\n" " -k - Don't check kernel images for hits.\n"
" -l - Also profile loading the executable.\n" " -l - Also profile loading the executable.\n"
" -o <output> - Print the results to file <output>.\n" " -o <output> - Print the results to file <output>.\n"
" -r, --recorded - Don't profile, but evaluate a recorded kernel profile\n"
" data.\n"
" -s <depth> - Number of return address samples to take from the\n" " -s <depth> - Number of return address samples to take from the\n"
" caller stack per tick. If the topmost address doesn't\n" " caller stack per tick. If the topmost address doesn't\n"
" hit a known image, the next address will be matched\n" " hit a known image, the next address will be matched\n"
@@ -370,9 +373,22 @@ private:
ObjectDeleter<SharedImage> imageDeleter(sharedImage); ObjectDeleter<SharedImage> imageDeleter(sharedImage);
// load the symbols // load the symbols
status_t error = teamID == B_SYSTEM_TEAM status_t error;
? sharedImage->Init(teamID, imageInfo.id) if (teamID == B_SYSTEM_TEAM) {
: sharedImage->Init(imageInfo.name); 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) if (error != B_OK)
return error; return error;
@@ -665,119 +681,86 @@ profile_all(const char* const* programArgs, int programArgCount)
} }
int static void
main(int argc, const char* const* argv) dump_recorded()
{ {
int32 stackDepth = 0; // retrieve recorded samples and parameters
const char* outputFile = NULL; 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) { // set global options to those of the profiler parameters
static struct option sLongOptions[] = { gOptions.interval = profilerParameters.interval;
{ "all", no_argument, 0, 'a' }, gOptions.stack_depth = profilerParameters.stack_depth;
{ "help", no_argument, 0, 'h' },
{ 0, 0, 0, 0 }
};
opterr = 0; // don't print errors // create an area for the sample buffer
int c = getopt_long(argc, (char**)argv, "+acCfhi:klo:s:v:", area_info info;
sLongOptions, NULL); status = get_area_info(profilerParameters.buffer_area, &info);
if (c == -1) if (status != B_OK) {
break; fprintf(stderr, "%s: Recorded profiling buffer invalid: %s\n",
kCommandName, strerror(status));
exit(1);
}
switch (c) { system_profiler_buffer_header* bufferHeader
case 'a': = (system_profiler_buffer_header*)info.address;
gOptions.profile_all = true;
break; uint8* bufferBase = (uint8*)(bufferHeader + 1);
case 'c': size_t totalBufferSize = info.size - (bufferBase - (uint8*)bufferHeader);
gOptions.profile_threads = false;
break; // create a thread manager
case 'C': ThreadManager threadManager(-1); // TODO: We don't need a debugger port!
gOptions.profile_teams = false;
break; // get the current buffer
case 'f': size_t bufferStart = bufferHeader->start;
gOptions.stack_depth = 64; size_t bufferSize = bufferHeader->size;
gOptions.analyze_full_stack = true; uint8* buffer = bufferBase + bufferStart;
break;
case 'h': if (bufferStart + bufferSize <= totalBufferSize) {
print_usage_and_exit(false); process_event_buffer(threadManager, buffer, bufferSize, -1);
break; } else {
case 'i': size_t remainingSize = bufferStart + bufferSize - totalBufferSize;
gOptions.interval = atol(optarg); if (!process_event_buffer(threadManager, buffer,
break; bufferSize - remainingSize, -1)) {
case 'k': process_event_buffer(threadManager, bufferBase, remainingSize, -1);
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;
} }
} }
if (!gOptions.profile_all && optind >= argc) // print results
print_usage_and_exit(true); int32 threadCount = threadManager.CountThreads();
for (int32 i = 0; i < threadCount; i++) {
if (stackDepth != 0) Thread* thread = threadManager.ThreadAt(i);
gOptions.stack_depth = stackDepth; thread->PrintResults();
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;
} }
}
static void
profile_single(const char* const* programArgs, int programArgCount)
{
// get thread/team to be debugged // get thread/team to be debugged
thread_id threadID = -1; thread_id threadID = load_program(programArgs, programArgCount,
team_id teamID = -1; gOptions.profile_loading);
// if (programArgCount > 1 if (threadID < 0) {
// || !get_id(*programArgs, (traceTeam ? teamID : thread))) { fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName,
// we've been given an executable and need to load it programArgs[0], strerror(threadID));
threadID = load_program(programArgs, programArgCount, exit(1);
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;
} }
// 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 // create a debugger port
port_id debuggerPort = create_port(10, "debugger port"); port_id debuggerPort = create_port(10, "debugger port");
if (debuggerPort < 0) { if (debuggerPort < 0) {
@@ -899,6 +882,106 @@ main(int argc, const char* const* argv)
if (message.origin.thread >= 0 && message.origin.nub_port >= 0) if (message.origin.thread >= 0 && message.origin.nub_port >= 0)
continue_thread(message.origin.nub_port, message.origin.thread); 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; return 0;
} }
+6 -3
View File
@@ -1362,10 +1362,13 @@ _user_system_profiler_recorded(struct system_profiler_parameters* userParameters
if (newArea < 0) if (newArea < 0)
return newArea; 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, status = user_memcpy(userParameters, sRecordedParameters,
sizeof(system_profiler_parameters)); sizeof(system_profiler_parameters));
}
if (status != B_OK) if (status != B_OK)
delete_area(newArea); delete_area(newArea);