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:
+120
-50
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user