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
This commit is contained in:
Ingo Weinhold
2008-09-21 22:31:18 +00:00
parent 32f1784d37
commit 061eba4a0f
+120 -50
View File
@@ -4,6 +4,7 @@
*/ */
#include <ctype.h> #include <ctype.h>
#include <errno.h>
#include <getopt.h> #include <getopt.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@@ -35,10 +36,61 @@ class Team;
enum { enum {
SAMPLE_AREA_SIZE = 128 * 1024, SAMPLE_AREA_SIZE = 128 * 1024,
SAMPLE_STACK_DEPTH = 5
}; };
static const char* kUsage =
"Usage: %s [ <options> ] <command line>\n"
"Executes the given command line <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 <interval> - Use a tick interval of <interval> 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 <output> - Print the results to file <output>.\n"
" -s <depth> - 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 { class Symbol {
public: public:
Symbol(Image* image, addr_t base, size_t size, const char* name) 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) status_t LoadSymbols(debug_symbol_lookup_context* lookupContext)
{ {
// printf("Loading symbols of image \"%s\" (%ld)...\n", fInfo.name, // fprintf(sOptions.output, "Loading symbols of image \"%s\" (%ld)...\n",
// fInfo.id); // fInfo.name, fInfo.id);
// create symbol iterator // create symbol iterator
debug_symbol_iterator* iterator; debug_symbol_iterator* iterator;
status_t error = debug_create_image_symbol_iterator(lookupContext, status_t error = debug_create_image_symbol_iterator(lookupContext,
fInfo.id, &iterator); fInfo.id, &iterator);
if (error != B_OK) { 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; return error;
} }
@@ -507,47 +560,50 @@ public:
std::sort(hitSymbols, hitSymbols + hitSymbolCount); std::sort(hitSymbols, hitSymbols + hitSymbolCount);
int64 totalTicks = fTotalTicks; int64 totalTicks = fTotalTicks;
printf("\nprofiling results for thread \"%s\" (%ld):\n", Name(), ID()); fprintf(sOptions.output, "\nprofiling results for thread \"%s\" "
printf(" tick interval: %lld us\n", fInterval); "(%ld):\n", Name(), ID());
printf(" total ticks: %lld (%lld us)\n", totalTicks, fprintf(sOptions.output, " tick interval: %lld us\n", fInterval);
totalTicks * fInterval); fprintf(sOptions.output, " total ticks: %lld (%lld us)\n",
totalTicks, totalTicks * fInterval);
if (totalTicks == 0) if (totalTicks == 0)
totalTicks = 1; 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, fUnkownTicks, fUnkownTicks * fInterval,
100.0 * fUnkownTicks / totalTicks); 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, fDroppedTicks, fDroppedTicks * fInterval,
100.0 * fDroppedTicks / totalTicks); 100.0 * fDroppedTicks / totalTicks);
if (imageCount > 0) { if (imageCount > 0) {
printf("\n"); fprintf(sOptions.output, "\n");
printf(" hits unknown image\n"); fprintf(sOptions.output, " hits unknown image\n");
printf(" -------------------------------------------------" fprintf(sOptions.output, " ---------------------------------------"
"-----------------------------\n"); "---------------------------------------\n");
for (int32 k = 0; k < imageCount; k++) { for (int32 k = 0; k < imageCount; k++) {
ThreadImage* image = images[k]; ThreadImage* image = images[k];
const image_info& imageInfo = image->GetImage()->Info(); const image_info& imageInfo = image->GetImage()->Info();
printf(" %10lld %10lld %7ld %s\n", image->TotalHits(), fprintf(sOptions.output, " %10lld %10lld %7ld %s\n",
image->UnknownHits(), imageInfo.id, imageInfo.name); image->TotalHits(), image->UnknownHits(), imageInfo.id,
imageInfo.name);
} }
} }
if (hitSymbolCount > 0) { if (hitSymbolCount > 0) {
printf("\n"); fprintf(sOptions.output, "\n");
printf(" hits in us in %% image function\n"); fprintf(sOptions.output, " hits in us in %% "
printf(" -------------------------------------------------" "image function\n");
"-----------------------------\n"); fprintf(sOptions.output, " ---------------------------------------"
"---------------------------------------\n");
for (int32 i = 0; i < hitSymbolCount; i++) { for (int32 i = 0; i < hitSymbolCount; i++) {
const HitSymbol& hitSymbol = hitSymbols[i]; const HitSymbol& hitSymbol = hitSymbols[i];
const Symbol* symbol = hitSymbol.symbol; const Symbol* symbol = hitSymbol.symbol;
printf(" %10lld %10lld %6.2f %6ld %s\n", hitSymbol.hits, fprintf(sOptions.output, " %10lld %10lld %6.2f %6ld %s\n",
hitSymbol.hits * fInterval, hitSymbol.hits, hitSymbol.hits * fInterval,
100.0 * hitSymbol.hits / totalTicks, symbol->image->ID(), 100.0 * hitSymbol.hits / totalTicks, symbol->image->ID(),
symbol->Name()); symbol->Name());
} }
} else } else
printf(" no functions were hit\n"); fprintf(sOptions.output, " no functions were hit\n");
} }
private: private:
@@ -660,7 +716,7 @@ public:
return error; return error;
// also try to load the kernel images and symbols // also try to load the kernel images and symbols
{ if (sOptions.profile_kernel) {
// fake a debug context -- it's not really needed anyway // fake a debug context -- it's not really needed anyway
debug_context debugContext; debug_context debugContext;
debugContext.team = B_SYSTEM_TEAM; debugContext.team = B_SYSTEM_TEAM;
@@ -727,9 +783,9 @@ public:
debug_nub_start_profiler message; debug_nub_start_profiler message;
message.reply_port = fDebugContext.reply_port; message.reply_port = fDebugContext.reply_port;
message.thread = thread->ID(); message.thread = thread->ID();
message.interval = 1000; message.interval = sOptions.interval;
message.sample_area = sampleArea; message.sample_area = sampleArea;
message.stack_depth = SAMPLE_STACK_DEPTH; message.stack_depth = sOptions.stack_depth;
debug_nub_start_profiler_reply reply; debug_nub_start_profiler_reply reply;
status_t error = send_debug_message(&fDebugContext, status_t error = send_debug_message(&fDebugContext,
@@ -1030,21 +1086,6 @@ Thread::_SynchronizeImages()
} }
// TODO: Adjust!
static const char* kUsage =
"Usage: %s [ <options> ] <command line>\n"
"Executes the given command line <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 <output> - Print the results to file <output>.\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 static void
print_usage_and_exit(bool error) print_usage_and_exit(bool error)
{ {
@@ -1073,33 +1114,45 @@ get_id(const char *str, int32 &id)
int int
main(int argc, const char* const* argv) main(int argc, const char* const* argv)
{ {
// const char* outputFile = NULL; const char* outputFile = NULL;
// bool schedulingAnalysis = false;
while (true) { while (true) {
static struct option sLongOptions[] = { static struct option sLongOptions[] = {
{ "help", no_argument, 0, 'h' }, { "help", no_argument, 0, 'h' },
// { "output", required_argument, 0, 'o' },
{ 0, 0, 0, 0 } { 0, 0, 0, 0 }
}; };
opterr = 0; // don't print errors 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) if (c == -1)
break; break;
switch (c) { switch (c) {
case 'c':
sOptions.profile_threads = false;
break;
case 'C':
sOptions.profile_teams = false;
break;
case 'h': case 'h':
print_usage_and_exit(false); print_usage_and_exit(false);
break; 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; outputFile = optarg;
break; break;
case 's': case 's':
schedulingAnalysis = true; sOptions.stack_depth = atol(optarg);
break; break;
*/
default: default:
print_usage_and_exit(true); print_usage_and_exit(true);
break; break;
@@ -1112,13 +1165,24 @@ main(int argc, const char* const* argv)
const char* const* programArgs = argv + optind; const char* const* programArgs = argv + optind;
int programArgCount = argc - 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 // get thread/team to be debugged
thread_id threadID = -1; thread_id threadID = -1;
team_id teamID = -1; team_id teamID = -1;
// if (programArgCount > 1 // if (programArgCount > 1
// || !get_id(*programArgs, (traceTeam ? teamID : thread))) { // || !get_id(*programArgs, (traceTeam ? teamID : thread))) {
// we've been given an executable and need to load it // 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) { if (threadID < 0) {
fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName, fprintf(stderr, "%s: Failed to start `%s': %s\n", kCommandName,
programArgs[0], strerror(threadID)); programArgs[0], strerror(threadID));
@@ -1206,6 +1270,9 @@ main(int argc, const char* const* argv)
break; break;
case B_DEBUGGER_MESSAGE_THREAD_CREATED: case B_DEBUGGER_MESSAGE_THREAD_CREATED:
if (!sOptions.profile_threads)
break;
threadManager.AddThread(message.thread_created.new_thread); threadManager.AddThread(message.thread_created.new_thread);
break; break;
case B_DEBUGGER_MESSAGE_THREAD_DELETED: case B_DEBUGGER_MESSAGE_THREAD_DELETED:
@@ -1213,6 +1280,9 @@ main(int argc, const char* const* argv)
break; break;
case B_DEBUGGER_MESSAGE_IMAGE_CREATED: case B_DEBUGGER_MESSAGE_IMAGE_CREATED:
if (!sOptions.profile_teams)
break;
if (Team* team = threadManager.FindTeam(message.origin.team)) { if (Team* team = threadManager.FindTeam(message.origin.team)) {
team->AddImage(message.image_created.info, team->AddImage(message.image_created.info,
message.image_created.image_event); message.image_created.image_event);