/*
* Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2013, Rene Gollent, rene@gollent.com.
* Distributed under the terms of the MIT License.
*/
#include <ctype.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <map>
#include <new>
#include <string>
#include <debugger.h>
#include <FindDirectory.h>
#include <OS.h>
#include <Path.h>
#include <String.h>
#include <syscalls.h>
#include <system_profiler_defs.h>
#include <AutoDeleter.h>
#include <debug_support.h>
#include <ObjectList.h>
#include <Referenceable.h>
#include <util/DoublyLinkedList.h>
#include "BasicProfileResult.h"
#include "CallgrindProfileResult.h"
#include "debug_utils.h"
#include "Image.h"
#include "Options.h"
#include "SummaryProfileResult.h"
#include "Team.h"
// size of the sample buffer area for system profiling
#define PROFILE_ALL_SAMPLE_AREA_SIZE (4 * 1024 * 1024)
extern const char* __progname;
const char* kCommandName = __progname;
class Image;
class Team;
class Thread;
static const char* kUsage =
"Usage: %s [ <options> ] [ <command line> ]\n"
"Profiles threads by periodically sampling the program counter. There are\n"
"two different modes: One profiles the complete system. The other starts\n"
"a program and profiles that and (optionally) its children. When a thread\n"
"terminates, a list of the functions where the thread was encountered is\n"
"printed.\n"
"\n"
"Options:\n"
" -a, --all - Profile all teams.\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"
" -f - Always analyze the full caller stack. The hit count\n"
" for every encountered function will be incremented.\n"
" This increases the default for the caller stack depth\n"
" (\"-s\") to 64.\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"
" -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"
" 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"
" -S - Don't output results for individual threads, but\n"
" produce a combined output at the end.\n"
" -v <directory> - Create valgrind/callgrind output. <directory> is the\n"
" directory where to put the output files.\n"
;
Options gOptions;
static bool sCaughtDeadlySignal = false;
class ThreadManager : private ProfiledEntity {
public:
ThreadManager(port_id debuggerPort)
:
fTeams(20),
fThreads(20, true),
fKernelTeam(NULL),
fDebuggerPort(debuggerPort),
fSummaryProfileResult(NULL)
{
}
virtual ~ThreadManager()
{
// release image references
for (ImageMap::iterator it = fImages.begin(); it != fImages.end(); ++it)
it->second->ReleaseReference();
if (fSummaryProfileResult != NULL)
fSummaryProfileResult->ReleaseReference();
for (int32 i = 0; Team* team = fTeams.ItemAt(i); i++)
team->ReleaseReference();
}
status_t Init()
{
if (!gOptions.summary_result)
return B_OK;
ProfileResult* profileResult;
status_t error = _CreateProfileResult(this, profileResult);
if (error != B_OK)
return error;
BReference<ProfileResult> profileResultReference(profileResult, true);
fSummaryProfileResult = new(std::nothrow) SummaryProfileResult(
profileResult);
if (fSummaryProfileResult == NULL)
return B_NO_MEMORY;
return fSummaryProfileResult->Init(profileResult->Entity());
}
status_t AddTeam(team_id teamID, Team** _team = NULL)
{
return _AddTeam(teamID, NULL, _team);
}
status_t AddTeam(system_profiler_team_added* addedInfo, Team** _team = NULL)
{
return _AddTeam(addedInfo->team, addedInfo, _team);
}
status_t AddThread(thread_id threadID)
{
thread_info threadInfo;
status_t error = get_thread_info(threadID, &threadInfo);
if (error != B_OK)
return error;
return AddThread(threadInfo.team, threadID, threadInfo.name);
}
status_t AddThread(team_id teamID, thread_id threadID, const char* name)
{
if (FindThread(threadID) != NULL)
return B_BAD_VALUE;
Team* team = FindTeam(teamID);
if (team == NULL)
return B_BAD_TEAM_ID;
Thread* thread = new(std::nothrow) Thread(threadID, name, team);
if (thread == NULL)
return B_NO_MEMORY;
status_t error = _CreateThreadProfileResult(thread);
if (error != B_OK) {
delete thread;
return error;
}
error = team->InitThread(thread);
if (error != B_OK) {
delete thread;
return error;
}
fThreads.AddItem(thread);
return B_OK;
}
void RemoveTeam(team_id teamID)
{
if (Team* team = FindTeam(teamID)) {
if (team == fKernelTeam)
fKernelTeam = NULL;
fTeams.RemoveItem(team);
team->ReleaseReference();
}
}
void RemoveThread(thread_id threadID)
{
if (Thread* thread = FindThread(threadID)) {
thread->GetTeam()->RemoveThread(thread);
fThreads.RemoveItem(thread, true);
}
}
Team* FindTeam(team_id teamID) const
{
for (int32 i = 0; Team* team = fTeams.ItemAt(i); i++) {
if (team->ID() == teamID)
return team;
}
return NULL;
}
Thread* FindThread(thread_id threadID) const
{
for (int32 i = 0; Thread* thread = fThreads.ItemAt(i); i++) {
if (thread->ID() == threadID)
return thread;
}
return NULL;
}
int32 CountThreads() const
{
return fThreads.CountItems();
}
Thread* ThreadAt(int32 index) const
{
return fThreads.ItemAt(index);
}
status_t AddImage(team_id teamID, const image_info& imageInfo, int32 event)
{
// get a shared image
SharedImage* sharedImage = NULL;
status_t error = _GetSharedImage(teamID, imageInfo, &sharedImage);
if (error != B_OK)
return error;
if (teamID == B_SYSTEM_TEAM) {
// a kernel image -- add it to all teams
int32 count = fTeams.CountItems();
for (int32 i = 0; i < count; i++) {
fTeams.ItemAt(i)->AddImage(sharedImage, imageInfo, teamID,
event);
}
}
// a userland team image -- add it to that image
if (Team* team = FindTeam(teamID))
return team->AddImage(sharedImage, imageInfo, teamID, event);
return B_BAD_TEAM_ID;
}
void RemoveImage(team_id teamID, image_id imageID, int32 event)
{
if (teamID == B_SYSTEM_TEAM) {
// a kernel image -- remove it from all teams
int32 count = fTeams.CountItems();
for (int32 i = 0; i < count; i++)
fTeams.ItemAt(i)->RemoveImage(imageID, event);
} else {
// a userland team image -- add it to that image
if (Team* team = FindTeam(teamID))
team->RemoveImage(imageID, event);
}
}
void PrintSummaryResults()
{
if (fSummaryProfileResult != NULL)
fSummaryProfileResult->PrintSummaryResults();
}
private:
virtual int32 EntityID() const
{
return 1;
}
virtual const char* EntityName() const
{
return "all";
}
virtual const char* EntityType() const
{
return "summary";
}
private:
status_t _AddTeam(team_id teamID, system_profiler_team_added* addedInfo,
Team** _team = NULL)
{
if (FindTeam(teamID) != NULL)
return B_BAD_VALUE;
Team* team = new(std::nothrow) Team;
if (team == NULL)
return B_NO_MEMORY;
status_t error = addedInfo != NULL
? _InitUndebuggedTeam(team, addedInfo)
: _InitDebuggedTeam(team, teamID);
if (error != B_OK) {
team->ReleaseReference();
return error;
}
fTeams.AddItem(team);
if (teamID == B_SYSTEM_TEAM)
fKernelTeam = team;
if (_team != NULL)
*_team = team;
return B_OK;
}
status_t _InitDebuggedTeam(Team* team, team_id teamID)
{
// init the team
status_t error = team->Init(teamID, fDebuggerPort);
if (error != B_OK)
return error;
// add the team's images
error = _LoadTeamImages(team, teamID);
if (error != B_OK)
return error;
// add the kernel images
return _LoadTeamImages(team, B_SYSTEM_TEAM);
}
status_t _InitUndebuggedTeam(Team* team,
system_profiler_team_added* addedInfo)
{
// init the team
status_t error = team->Init(addedInfo);
if (error != B_OK)
return error;
// in case of a user team, add the kernel images
if (team->ID() == B_SYSTEM_TEAM || fKernelTeam == NULL)
return B_OK;
const BObjectList<Image>& kernelImages = fKernelTeam->Images();
int32 count = kernelImages.CountItems();
for (int32 i = 0; i < count; i++) {
SharedImage* sharedImage = kernelImages.ItemAt(i)->GetSharedImage();
team->AddImage(sharedImage, sharedImage->Info(), B_SYSTEM_TEAM, 0);
}
return B_OK;
}
status_t _LoadTeamImages(Team* team, team_id teamID)
{
// iterate through the team's images and collect the symbols
image_info imageInfo;
int32 cookie = 0;
while (get_next_image_info(teamID, &cookie, &imageInfo) == B_OK) {
// get a shared image
SharedImage* sharedImage;
status_t error = _GetSharedImage(teamID, imageInfo, &sharedImage);
if (error != B_OK)
return error;
// add the image to the team
error = team->AddImage(sharedImage, imageInfo, teamID, 0);
if (error != B_OK)
return error;
}
return B_OK;
}
status_t _CreateThreadProfileResult(Thread* thread)
{
if (fSummaryProfileResult != NULL) {
thread->SetProfileResult(fSummaryProfileResult);
return B_OK;
}
ProfileResult* profileResult;
status_t error = _CreateProfileResult(thread, profileResult);
if (error != B_OK)
return error;
thread->SetProfileResult(profileResult);
return B_OK;
}
status_t _CreateProfileResult(ProfiledEntity* profiledEntity,
ProfileResult*& _profileResult)
{
ProfileResult* profileResult;
if (gOptions.callgrind_directory != NULL)
profileResult = new(std::nothrow) CallgrindProfileResult;
else if (gOptions.analyze_full_stack)
profileResult = new(std::nothrow) InclusiveProfileResult;
else
profileResult = new(std::nothrow) ExclusiveProfileResult;
if (profileResult == NULL)
return B_NO_MEMORY;
BReference<ProfileResult> profileResultReference(profileResult, true);
status_t error = profileResult->Init(profiledEntity);
if (error != B_OK)
return error;
_profileResult = profileResultReference.Detach();
return B_OK;
}
status_t _GetSharedImage(team_id teamID, const image_info& imageInfo,
SharedImage** _sharedImage)
{
// check whether the image has already been loaded
ImageMap::iterator it = fImages.find(imageInfo.name);
if (it != fImages.end()) {
*_sharedImage = it->second;
return B_OK;
}
// create the shared image
SharedImage* sharedImage = new(std::nothrow) SharedImage;
if (sharedImage == NULL)
return B_NO_MEMORY;
ObjectDeleter<SharedImage> imageDeleter(sharedImage);
// load the symbols
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
BPath bootAddonPath;
if (find_directory(B_SYSTEM_ADDONS_DIRECTORY,
&bootAddonPath) == B_OK
&& bootAddonPath.Append("kernel") == B_OK
&& bootAddonPath.Append("boot") == B_OK) {
name = BString(bootAddonPath.Path()) << "/" << name;
}
}
error = sharedImage->Init(name.String());
}
} else if (strcmp(imageInfo.name, "commpage") == 0)
error = sharedImage->Init(teamID, imageInfo.id);
else
error = sharedImage->Init(imageInfo.name);
if (error != B_OK)
return error;
try {
fImages[sharedImage->Name()] = sharedImage;
} catch (std::bad_alloc&) {
return B_NO_MEMORY;
}
imageDeleter.Detach();
*_sharedImage = sharedImage;
return B_OK;
}
private:
typedef std::map<std::string, SharedImage*> ImageMap;
private:
BObjectList<Team> fTeams;
BObjectList<Thread> fThreads;
ImageMap fImages;
Team* fKernelTeam;
port_id fDebuggerPort;
SummaryProfileResult* fSummaryProfileResult;
};
static void
print_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kUsage, __progname);
exit(error ? 1 : 0);
}
/*
// get_id
static bool
get_id(const char *str, int32 &id)
{
int32 len = strlen(str);
for (int32 i = 0; i < len; i++) {
if (!isdigit(str[i]))
return false;
}
id = atol(str);
return true;
}
*/
static bool
process_event_buffer(ThreadManager& threadManager, uint8* buffer,
size_t bufferSize, team_id mainTeam)
{
//printf("process_event_buffer(%p, %lu)\n", buffer, bufferSize);
const uint8* bufferEnd = buffer + bufferSize;
while (buffer < bufferEnd) {
system_profiler_event_header* header
= (system_profiler_event_header*)buffer;
buffer += sizeof(system_profiler_event_header);
switch (header->event) {
case B_SYSTEM_PROFILER_TEAM_ADDED:
{
system_profiler_team_added* event
= (system_profiler_team_added*)buffer;
if (threadManager.AddTeam(event) != B_OK)
exit(1);
break;
}
case B_SYSTEM_PROFILER_TEAM_REMOVED:
{
system_profiler_team_removed* event
= (system_profiler_team_removed*)buffer;
threadManager.RemoveTeam(event->team);
// quit, if the main team we're interested in is gone
if (mainTeam >= 0 && event->team == mainTeam)
return true;
break;
}
case B_SYSTEM_PROFILER_TEAM_EXEC:
{
system_profiler_team_exec* event
= (system_profiler_team_exec*)buffer;
if (Team* team = threadManager.FindTeam(event->team))
team->Exec(0, event->args, event->thread_name);
break;
}
case B_SYSTEM_PROFILER_THREAD_ADDED:
{
system_profiler_thread_added* event
= (system_profiler_thread_added*)buffer;
if (threadManager.AddThread(event->team, event->thread,
event->name) != B_OK) {
exit(1);
}
break;
}
case B_SYSTEM_PROFILER_THREAD_REMOVED:
{
system_profiler_thread_removed* event
= (system_profiler_thread_removed*)buffer;
if (Thread* thread = threadManager.FindThread(event->thread)) {
thread->PrintResults();
threadManager.RemoveThread(event->thread);
}
break;
}
case B_SYSTEM_PROFILER_IMAGE_ADDED:
{
system_profiler_image_added* event
= (system_profiler_image_added*)buffer;
threadManager.AddImage(event->team, event->info, 0);
break;
}
case B_SYSTEM_PROFILER_IMAGE_REMOVED:
{
system_profiler_image_removed* event
= (system_profiler_image_removed*)buffer;
threadManager.RemoveImage(event->team, event->image, 0);
break;
}
case B_SYSTEM_PROFILER_SAMPLES:
{
system_profiler_samples* event
= (system_profiler_samples*)buffer;
Thread* thread = threadManager.FindThread(event->thread);
if (thread != NULL) {
thread->AddSamples(event->samples,
(addr_t*)(buffer + header->size) - event->samples);
}
break;
}
case B_SYSTEM_PROFILER_BUFFER_END:
{
// Marks the end of the ring buffer -- we need to ignore the
// remaining bytes.
return false;
}
}
buffer += header->size;
}
return false;
}
static void
signal_handler(int signal, void* data)
{
sCaughtDeadlySignal = true;
}
static void
profile_all(const char* const* programArgs, int programArgCount)
{
// Load the executable, if we have to.
thread_id threadID = -1;
if (programArgCount >= 1) {
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);
}
}
// install signal handlers so we can exit gracefully
struct sigaction action;
action.sa_handler = (__sighandler_t)signal_handler;
sigemptyset(&action.sa_mask);
action.sa_userdata = NULL;
if (sigaction(SIGHUP, &action, NULL) < 0
|| sigaction(SIGINT, &action, NULL) < 0
|| sigaction(SIGQUIT, &action, NULL) < 0) {
fprintf(stderr, "%s: Failed to install signal handlers: %s\n",
kCommandName, strerror(errno));
exit(1);
}
// create an area for the sample buffer
system_profiler_buffer_header* bufferHeader;
area_id area = create_area("profiling buffer", (void**)&bufferHeader,
B_ANY_ADDRESS, PROFILE_ALL_SAMPLE_AREA_SIZE, B_NO_LOCK,
B_READ_AREA | B_WRITE_AREA);
if (area < 0) {
fprintf(stderr, "%s: Failed to create sample area: %s\n", kCommandName,
strerror(area));
exit(1);
}
uint8* bufferBase = (uint8*)(bufferHeader + 1);
size_t totalBufferSize = PROFILE_ALL_SAMPLE_AREA_SIZE
- (bufferBase - (uint8*)bufferHeader);
// create a thread manager
ThreadManager threadManager(-1); // TODO: We don't need a debugger port!
status_t error = threadManager.Init();
if (error != B_OK) {
fprintf(stderr, "%s: Failed to init thread manager: %s\n", kCommandName,
strerror(error));
exit(1);
}
// start profiling
system_profiler_parameters profilerParameters;
profilerParameters.buffer_area = area;
profilerParameters.flags = B_SYSTEM_PROFILER_TEAM_EVENTS
| B_SYSTEM_PROFILER_THREAD_EVENTS | B_SYSTEM_PROFILER_IMAGE_EVENTS
| B_SYSTEM_PROFILER_SAMPLING_EVENTS;
profilerParameters.interval = gOptions.interval;
profilerParameters.stack_depth = gOptions.stack_depth;
error = _kern_system_profiler_start(&profilerParameters);
if (error != B_OK) {
fprintf(stderr, "%s: Failed to start profiling: %s\n", kCommandName,
strerror(error));
exit(1);
}
// resume the loaded team, if we have one
if (threadID >= 0)
resume_thread(threadID);
// main event loop
while (true) {
// get the current buffer
size_t bufferStart = bufferHeader->start;
size_t bufferSize = bufferHeader->size;
uint8* buffer = bufferBase + bufferStart;
//printf("processing buffer of size %lu bytes\n", bufferSize);
bool quit;
if (bufferStart + bufferSize <= totalBufferSize) {
quit = process_event_buffer(threadManager, buffer, bufferSize,
threadID);
} else {
size_t remainingSize = bufferStart + bufferSize - totalBufferSize;
quit = process_event_buffer(threadManager, buffer,
bufferSize - remainingSize, threadID)
|| process_event_buffer(threadManager, bufferBase,
remainingSize, threadID);
}
if (quit)
break;
// get next buffer
uint64 droppedEvents = 0;
error = _kern_system_profiler_next_buffer(bufferSize, &droppedEvents);
if (error != B_OK) {
if (error == B_INTERRUPTED) {
if (sCaughtDeadlySignal)
break;
continue;
}
fprintf(stderr, "%s: Failed to get next sample buffer: %s\n",
kCommandName, strerror(error));
break;
}
}
// stop profiling
_kern_system_profiler_stop();
// print results
int32 threadCount = threadManager.CountThreads();
for (int32 i = 0; i < threadCount; i++) {
Thread* thread = threadManager.ThreadAt(i);
thread->PrintResults();
}
threadManager.PrintSummaryResults();
}
static void
dump_recorded()
{
// retrieve recorded samples and parameters
system_profiler_parameters profilerParameters;
status_t error = _kern_system_profiler_recorded(&profilerParameters);
if (error != B_OK) {
fprintf(stderr, "%s: Failed to get recorded profiling buffer: %s\n",
kCommandName, strerror(error));
exit(1);
}
// set global options to those of the profiler parameters
gOptions.interval = profilerParameters.interval;
gOptions.stack_depth = profilerParameters.stack_depth;
// create an area for the sample buffer
area_info info;
error = get_area_info(profilerParameters.buffer_area, &info);
if (error != B_OK) {
fprintf(stderr, "%s: Recorded profiling buffer invalid: %s\n",
kCommandName, strerror(error));
exit(1);
}
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!
error = threadManager.Init();
if (error != B_OK) {
fprintf(stderr, "%s: Failed to init thread manager: %s\n", kCommandName,
strerror(error));
exit(1);
}
// 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);
}
}
// print results
int32 threadCount = threadManager.CountThreads();
for (int32 i = 0; i < threadCount; i++) {
Thread* thread = threadManager.ThreadAt(i);
thread->PrintResults();
}
threadManager.PrintSummaryResults();
}
static void
profile_single(const char* const* programArgs, int programArgCount)
{
// get thread/team to be debugged
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) {
fprintf(stderr, "%s: Failed to create debugger port: %s\n",
kCommandName, strerror(debuggerPort));
exit(1);
}
// add team and thread to the thread manager
ThreadManager threadManager(debuggerPort);
error = threadManager.Init();
if (error != B_OK) {
fprintf(stderr, "%s: Failed to init thread manager: %s\n", kCommandName,
strerror(error));
exit(1);
}
if (threadManager.AddTeam(teamID) != B_OK
|| threadManager.AddThread(threadID) != B_OK) {
exit(1);
}
// debug loop
while (true) {
debug_debugger_message_data message;
bool quitLoop = false;
int32 code;
ssize_t messageSize = read_port(debuggerPort, &code, &message,
sizeof(message));
if (messageSize < 0) {
if (messageSize == B_INTERRUPTED)
continue;
fprintf(stderr, "%s: Reading from debugger port failed: %s\n",
kCommandName, strerror(messageSize));
exit(1);
}
switch (code) {
case B_DEBUGGER_MESSAGE_PROFILER_UPDATE:
{
Thread* thread = threadManager.FindThread(
message.profiler_update.origin.thread);
if (thread == NULL)
break;
thread->AddSamples(message.profiler_update.sample_count,
message.profiler_update.dropped_ticks,
message.profiler_update.stack_depth,
message.profiler_update.variable_stack_depth,
message.profiler_update.image_event);
if (message.profiler_update.stopped) {
thread->PrintResults();
threadManager.RemoveThread(thread->ID());
}
break;
}
case B_DEBUGGER_MESSAGE_TEAM_CREATED:
if (!gOptions.profile_teams)
break;
if (threadManager.AddTeam(message.team_created.new_team)
== B_OK) {
threadManager.AddThread(message.team_created.new_team);
}
break;
case B_DEBUGGER_MESSAGE_TEAM_DELETED:
// a debugged team is gone -- quit, if it is our team
threadManager.RemoveTeam(message.origin.team);
quitLoop = message.origin.team == teamID;
break;
case B_DEBUGGER_MESSAGE_TEAM_EXEC:
if (Team* team = threadManager.FindTeam(message.origin.team)) {
team_info teamInfo;
thread_info threadInfo;
if (get_team_info(message.origin.team, &teamInfo) == B_OK
&& get_thread_info(message.origin.team, &threadInfo)
== B_OK) {
team->Exec(message.team_exec.image_event, teamInfo.args,
threadInfo.name);
}
}
break;
case B_DEBUGGER_MESSAGE_THREAD_CREATED:
if (!gOptions.profile_threads)
break;
threadManager.AddThread(message.thread_created.new_thread);
break;
case B_DEBUGGER_MESSAGE_THREAD_DELETED:
threadManager.RemoveThread(message.origin.thread);
break;
case B_DEBUGGER_MESSAGE_IMAGE_CREATED:
threadManager.AddImage(message.origin.team,
message.image_created.info,
message.image_created.image_event);
break;
case B_DEBUGGER_MESSAGE_IMAGE_DELETED:
threadManager.RemoveImage(message.origin.team,
message.image_deleted.info.id,
message.image_deleted.image_event);
break;
case B_DEBUGGER_MESSAGE_POST_SYSCALL:
case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED:
case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED:
case B_DEBUGGER_MESSAGE_DEBUGGER_CALL:
case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT:
case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT:
case B_DEBUGGER_MESSAGE_SINGLE_STEP:
case B_DEBUGGER_MESSAGE_PRE_SYSCALL:
case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED:
break;
}
if (quitLoop)
break;
// tell the thread to continue (only when there is a thread and the
// message was synchronous)
if (message.origin.thread >= 0 && message.origin.nub_port >= 0)
continue_thread(message.origin.nub_port, message.origin.thread);
}
// prints summary results
threadManager.PrintSummaryResults();
}
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:Sv:",
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 'S':
gOptions.summary_result = true;
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;
}
↑ V576 Incorrect format. Consider checking the fourth actual argument of the 'fprintf' function. The memsize type argument is expected.