* 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:
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user