Moved several classes into their own files.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27779 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
+3
-12
@@ -1,21 +1,12 @@
|
||||
SubDir HAIKU_TOP src bin debug ;
|
||||
|
||||
UsePrivateHeaders debug ;
|
||||
UsePrivateHeaders kernel ;
|
||||
UsePrivateHeaders libroot ;
|
||||
#UsePrivateHeaders kernel ;
|
||||
#UsePrivateHeaders libroot ;
|
||||
UsePrivateHeaders shared ;
|
||||
UsePrivateSystemHeaders ;
|
||||
|
||||
StaticLibrary <bin>debug_utils.a : debug_utils.cpp ;
|
||||
|
||||
BinCommand profile
|
||||
: profile.cpp
|
||||
:
|
||||
<bin>debug_utils.a
|
||||
libdebug.so
|
||||
$(TARGET_LIBSTDC++)
|
||||
be
|
||||
;
|
||||
|
||||
|
||||
HaikuSubInclude profile ;
|
||||
HaikuSubInclude strace ;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "Image.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <new>
|
||||
|
||||
#include <debug_support.h>
|
||||
#include <ObjectList.h>
|
||||
|
||||
#include "Options.h"
|
||||
|
||||
|
||||
Image::Image(const image_info& info, team_id owner, int32 creationEvent)
|
||||
:
|
||||
fInfo(info),
|
||||
fOwner(owner),
|
||||
fSymbols(NULL),
|
||||
fSymbolCount(0),
|
||||
fCreationEvent(creationEvent),
|
||||
fDeletionEvent(-1)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Image::~Image()
|
||||
{
|
||||
if (fSymbols != NULL) {
|
||||
for (int32 i = 0; i < fSymbolCount; i++)
|
||||
delete fSymbols[i];
|
||||
delete[] fSymbols;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Image::LoadSymbols(debug_symbol_lookup_context* lookupContext)
|
||||
{
|
||||
// fprintf(sOptions.output, "Loading symbols of image \"%s\" (%ld)...\n",
|
||||
// fInfo.name, fInfo.id);
|
||||
|
||||
// create symbol iterator
|
||||
debug_symbol_iterator* iterator;
|
||||
status_t error = debug_create_image_symbol_iterator(lookupContext,
|
||||
fInfo.id, &iterator);
|
||||
if (error != B_OK) {
|
||||
fprintf(stderr, "Failed to init symbol iterator: %s\n",
|
||||
strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
// iterate through the symbols
|
||||
BObjectList<Symbol> symbols(512, true);
|
||||
char symbolName[1024];
|
||||
int32 symbolType;
|
||||
void* symbolLocation;
|
||||
size_t symbolSize;
|
||||
while (debug_next_image_symbol(iterator, symbolName, sizeof(symbolName),
|
||||
&symbolType, &symbolLocation, &symbolSize) == B_OK) {
|
||||
// printf(" %s %p (%6lu) %s\n",
|
||||
// symbolType == B_SYMBOL_TYPE_TEXT ? "text" : "data",
|
||||
// symbolLocation, symbolSize, symbolName);
|
||||
if (symbolSize > 0 && symbolType == B_SYMBOL_TYPE_TEXT) {
|
||||
Symbol* symbol = new(std::nothrow) Symbol(this,
|
||||
(addr_t)symbolLocation, symbolSize, symbolName);
|
||||
if (symbol == NULL || !symbols.AddItem(symbol)) {
|
||||
delete symbol;
|
||||
fprintf(stderr, "%s: Out of memory\n", kCommandName);
|
||||
debug_delete_image_symbol_iterator(iterator);
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug_delete_image_symbol_iterator(iterator);
|
||||
|
||||
// sort the symbols
|
||||
fSymbolCount = symbols.CountItems();
|
||||
fSymbols = new(std::nothrow) Symbol*[fSymbolCount];
|
||||
if (fSymbols == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
for (int32 i = fSymbolCount - 1; i >= 0 ; i--)
|
||||
fSymbols[i] = symbols.RemoveItemAt(i);
|
||||
|
||||
std::sort(fSymbols, fSymbols + fSymbolCount, SymbolComparator());
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
Image::FindSymbol(addr_t address) const
|
||||
{
|
||||
// binary search the function
|
||||
int32 lower = 0;
|
||||
int32 upper = fSymbolCount;
|
||||
|
||||
while (lower < upper) {
|
||||
int32 mid = (lower + upper) / 2;
|
||||
if (address >= fSymbols[mid]->base + fSymbols[mid]->size)
|
||||
lower = mid + 1;
|
||||
else
|
||||
upper = mid;
|
||||
}
|
||||
|
||||
if (lower == fSymbolCount)
|
||||
return -1;
|
||||
|
||||
const Symbol* symbol = fSymbols[lower];
|
||||
if (address >= symbol->base && address < symbol->base + symbol->size)
|
||||
return lower;
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef IMAGE_H
|
||||
#define IMAGE_H
|
||||
|
||||
#include <image.h>
|
||||
#include <OS.h>
|
||||
#include <String.h>
|
||||
|
||||
#include "Referenceable.h"
|
||||
|
||||
|
||||
class debug_symbol_lookup_context;
|
||||
class Image;
|
||||
|
||||
|
||||
class Symbol {
|
||||
public:
|
||||
Symbol(Image* image, addr_t base, size_t size, const char* name)
|
||||
:
|
||||
image(image),
|
||||
base(base),
|
||||
size(size),
|
||||
name(name)
|
||||
{
|
||||
}
|
||||
|
||||
const char* Name() const { return name.String(); }
|
||||
|
||||
Image* image;
|
||||
addr_t base;
|
||||
size_t size;
|
||||
BString name;
|
||||
};
|
||||
|
||||
|
||||
struct SymbolComparator {
|
||||
inline bool operator()(const Symbol* a, const Symbol* b) const
|
||||
{
|
||||
return a->base < b->base;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Image : public Referenceable {
|
||||
public:
|
||||
Image(const image_info& info, team_id owner,
|
||||
int32 creationEvent);
|
||||
~Image();
|
||||
|
||||
inline const image_id ID() const;
|
||||
inline const image_info& Info() const;
|
||||
inline team_id Owner() const;
|
||||
|
||||
inline int32 CreationEvent() const;
|
||||
inline int32 DeletionEvent() const;
|
||||
inline void SetDeletionEvent(int32 event);
|
||||
|
||||
status_t LoadSymbols(
|
||||
debug_symbol_lookup_context* lookupContext);
|
||||
|
||||
inline Symbol** Symbols() const;
|
||||
inline int32 SymbolCount() const;
|
||||
|
||||
inline bool ContainsAddress(addr_t address) const;
|
||||
int32 FindSymbol(addr_t address) const;
|
||||
|
||||
private:
|
||||
image_info fInfo;
|
||||
team_id fOwner;
|
||||
Symbol** fSymbols;
|
||||
int32 fSymbolCount;
|
||||
int32 fCreationEvent;
|
||||
int32 fDeletionEvent;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
const image_id
|
||||
Image::ID() const
|
||||
{
|
||||
return fInfo.id;
|
||||
}
|
||||
|
||||
|
||||
const image_info&
|
||||
Image::Info() const
|
||||
{
|
||||
return fInfo;
|
||||
}
|
||||
|
||||
|
||||
team_id
|
||||
Image::Owner() const
|
||||
{
|
||||
return fOwner;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
Image::CreationEvent() const
|
||||
{
|
||||
return fCreationEvent;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
Image::DeletionEvent() const
|
||||
{
|
||||
return fDeletionEvent;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Image::SetDeletionEvent(int32 event)
|
||||
{
|
||||
fDeletionEvent = event;
|
||||
}
|
||||
|
||||
|
||||
Symbol**
|
||||
Image::Symbols() const
|
||||
{
|
||||
return fSymbols;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
Image::SymbolCount() const
|
||||
{
|
||||
return fSymbolCount;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Image::ContainsAddress(addr_t address) const
|
||||
{
|
||||
return address >= (addr_t)fInfo.text
|
||||
&& address <= (addr_t)fInfo.data + fInfo.data_size - 1;
|
||||
}
|
||||
|
||||
|
||||
#endif // IMAGE_H
|
||||
@@ -0,0 +1,22 @@
|
||||
SubDir HAIKU_TOP src bin debug profile ;
|
||||
|
||||
UsePrivateHeaders debug ;
|
||||
UsePrivateHeaders kernel ;
|
||||
UsePrivateHeaders libroot ;
|
||||
UsePrivateHeaders shared ;
|
||||
UsePrivateSystemHeaders ;
|
||||
|
||||
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ;
|
||||
|
||||
BinCommand profile
|
||||
:
|
||||
Image.cpp
|
||||
Team.cpp
|
||||
Thread.cpp
|
||||
profile.cpp
|
||||
:
|
||||
<bin>debug_utils.a
|
||||
libdebug.so
|
||||
$(TARGET_LIBSTDC++)
|
||||
be
|
||||
;
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef OPTIONS_H
|
||||
#define OPTIONS_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
|
||||
struct Options {
|
||||
Options()
|
||||
:
|
||||
interval(1000),
|
||||
stack_depth(5),
|
||||
output(NULL),
|
||||
profile_kernel(true),
|
||||
profile_loading(false),
|
||||
profile_teams(true),
|
||||
profile_threads(true),
|
||||
analyze_full_stack(false)
|
||||
{
|
||||
}
|
||||
|
||||
bigtime_t interval;
|
||||
int32 stack_depth;
|
||||
FILE* output;
|
||||
bool profile_kernel;
|
||||
bool profile_loading;
|
||||
bool profile_teams;
|
||||
bool profile_threads;
|
||||
bool analyze_full_stack;
|
||||
};
|
||||
|
||||
|
||||
extern Options gOptions;
|
||||
extern const char* kCommandName;
|
||||
|
||||
|
||||
|
||||
#endif // OPTIONS_H
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "Team.h"
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <image.h>
|
||||
|
||||
#include <debug_support.h>
|
||||
|
||||
#include "debug_utils.h"
|
||||
|
||||
#include "Options.h"
|
||||
|
||||
|
||||
enum {
|
||||
SAMPLE_AREA_SIZE = 128 * 1024,
|
||||
};
|
||||
|
||||
|
||||
Team::Team()
|
||||
:
|
||||
fNubPort(-1),
|
||||
fThreads(),
|
||||
fImages(20, false)
|
||||
{
|
||||
fInfo.team = -1;
|
||||
fDebugContext.nub_port = -1;
|
||||
}
|
||||
|
||||
|
||||
Team::~Team()
|
||||
{
|
||||
if (fDebugContext.nub_port >= 0)
|
||||
destroy_debug_context(&fDebugContext);
|
||||
|
||||
if (fNubPort >= 0)
|
||||
remove_team_debugger(fInfo.team);
|
||||
|
||||
for (int32 i = 0; Image* image = fImages.ItemAt(i); i++)
|
||||
image->RemoveReference();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Team::Init(team_id teamID, port_id debuggerPort)
|
||||
{
|
||||
// get team info
|
||||
status_t error = get_team_info(teamID, &fInfo);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// install ourselves as the team debugger
|
||||
fNubPort = install_team_debugger(teamID, debuggerPort);
|
||||
if (fNubPort < 0) {
|
||||
fprintf(stderr, "%s: Failed to install as debugger for team %ld: "
|
||||
"%s\n", kCommandName, teamID, strerror(fNubPort));
|
||||
return fNubPort;
|
||||
}
|
||||
|
||||
// init debug context
|
||||
error = init_debug_context(&fDebugContext, teamID, fNubPort);
|
||||
if (error != B_OK) {
|
||||
fprintf(stderr, "%s: Failed to init debug context for team %ld: "
|
||||
"%s\n", kCommandName, teamID, strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
// create symbol lookup context
|
||||
debug_symbol_lookup_context* lookupContext;
|
||||
error = debug_create_symbol_lookup_context(&fDebugContext,
|
||||
&lookupContext);
|
||||
if (error != B_OK) {
|
||||
fprintf(stderr, "%s: Failed to create symbol lookup context for "
|
||||
"team %ld: %s\n", kCommandName, teamID, strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
// load the team's images and their symbols
|
||||
error = _LoadSymbols(lookupContext, ID());
|
||||
debug_delete_symbol_lookup_context(lookupContext);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// also try to load the kernel images and symbols
|
||||
if (gOptions.profile_kernel) {
|
||||
// fake a debug context -- it's not really needed anyway
|
||||
debug_context debugContext;
|
||||
debugContext.team = B_SYSTEM_TEAM;
|
||||
debugContext.nub_port = -1;
|
||||
debugContext.reply_port = -1;
|
||||
|
||||
// create symbol lookup context
|
||||
error = debug_create_symbol_lookup_context(&debugContext,
|
||||
&lookupContext);
|
||||
if (error != B_OK) {
|
||||
fprintf(stderr, "%s: Failed to create symbol lookup context "
|
||||
"for the kernel team: %s\n", kCommandName, strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
// load the kernel's images and their symbols
|
||||
_LoadSymbols(lookupContext, B_SYSTEM_TEAM);
|
||||
debug_delete_symbol_lookup_context(lookupContext);
|
||||
}
|
||||
|
||||
// set team debugging flags
|
||||
int32 teamDebugFlags = B_TEAM_DEBUG_THREADS
|
||||
| B_TEAM_DEBUG_TEAM_CREATION | B_TEAM_DEBUG_IMAGES;
|
||||
set_team_debugging_flags(fNubPort, teamDebugFlags);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Team::InitThread(Thread* thread)
|
||||
{
|
||||
// create the sample area
|
||||
char areaName[B_OS_NAME_LENGTH];
|
||||
snprintf(areaName, sizeof(areaName), "profiling samples %ld",
|
||||
thread->ID());
|
||||
void* samples;
|
||||
area_id sampleArea = create_area(areaName, &samples, B_ANY_ADDRESS,
|
||||
SAMPLE_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
|
||||
if (sampleArea < 0) {
|
||||
fprintf(stderr, "%s: Failed to create sample area for thread %ld: "
|
||||
"%s\n", kCommandName, thread->ID(), strerror(sampleArea));
|
||||
return sampleArea;
|
||||
}
|
||||
|
||||
thread->SetSampleArea(sampleArea, (addr_t*)samples);
|
||||
|
||||
// add the current images to the thread
|
||||
int32 imageCount = fImages.CountItems();
|
||||
for (int32 i = 0; i < imageCount; i++) {
|
||||
status_t error = thread->AddImage(fImages.ItemAt(i));
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
}
|
||||
|
||||
// set thread debugging flags and start profiling
|
||||
int32 threadDebugFlags = 0;
|
||||
// if (!traceTeam) {
|
||||
// threadDebugFlags = B_THREAD_DEBUG_POST_SYSCALL
|
||||
// | (traceChildThreads
|
||||
// ? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0);
|
||||
// }
|
||||
set_thread_debugging_flags(fNubPort, thread->ID(), threadDebugFlags);
|
||||
|
||||
// start profiling
|
||||
debug_nub_start_profiler message;
|
||||
message.reply_port = fDebugContext.reply_port;
|
||||
message.thread = thread->ID();
|
||||
message.interval = gOptions.interval;
|
||||
message.sample_area = sampleArea;
|
||||
message.stack_depth = gOptions.stack_depth;
|
||||
message.variable_stack_depth = gOptions.analyze_full_stack;
|
||||
|
||||
debug_nub_start_profiler_reply reply;
|
||||
status_t error = send_debug_message(&fDebugContext,
|
||||
B_DEBUG_START_PROFILER, &message, sizeof(message), &reply,
|
||||
sizeof(reply));
|
||||
if (error != B_OK || (error = reply.error) != B_OK) {
|
||||
fprintf(stderr, "%s: Failed to start profiler for thread %ld: %s\n",
|
||||
kCommandName, thread->ID(), strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
thread->SetInterval(reply.interval);
|
||||
|
||||
fThreads.Add(thread);
|
||||
|
||||
// resume the target thread to be sure, it's running
|
||||
resume_thread(thread->ID());
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Team::RemoveThread(Thread* thread)
|
||||
{
|
||||
fThreads.Remove(thread);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Team::Exec(int32 event)
|
||||
{
|
||||
// remove all non-kernel images
|
||||
int32 imageCount = fImages.CountItems();
|
||||
for (int32 i = imageCount - 1; i >= 0; i--) {
|
||||
Image* image = fImages.ItemAt(i);
|
||||
if (image->Owner() == ID())
|
||||
_RemoveImage(i, event);
|
||||
}
|
||||
|
||||
// update the main thread
|
||||
ThreadList::Iterator it = fThreads.GetIterator();
|
||||
while (Thread* thread = it.Next()) {
|
||||
if (thread->ID() == ID()) {
|
||||
thread->UpdateInfo();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Team::AddImage(const image_info& imageInfo, int32 event)
|
||||
{
|
||||
// create symbol lookup context
|
||||
debug_symbol_lookup_context* lookupContext;
|
||||
status_t error = debug_create_symbol_lookup_context(&fDebugContext,
|
||||
&lookupContext);
|
||||
if (error != B_OK) {
|
||||
fprintf(stderr, "%s: Failed to create symbol lookup context for "
|
||||
"team %ld: %s\n", kCommandName, ID(), strerror(error));
|
||||
return error;
|
||||
}
|
||||
|
||||
Image* image;
|
||||
error = _LoadImageSymbols(lookupContext, imageInfo, ID(), event,
|
||||
&image);
|
||||
debug_delete_symbol_lookup_context(lookupContext);
|
||||
|
||||
// Although we generally synchronize the threads' images lazily, we have
|
||||
// to add new images at least, since otherwise images could be added
|
||||
// and removed again, and the hits inbetween could never be matched.
|
||||
if (error == B_OK) {
|
||||
ThreadList::Iterator it = fThreads.GetIterator();
|
||||
while (Thread* thread = it.Next())
|
||||
thread->AddImage(image);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Team::RemoveImage(const image_info& imageInfo, int32 event)
|
||||
{
|
||||
for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) {
|
||||
if (image->ID() == imageInfo.id) {
|
||||
_RemoveImage(i, event);
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
Image*
|
||||
Team::FindImage(image_id id) const
|
||||
{
|
||||
for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) {
|
||||
if (image->ID() == id)
|
||||
return image;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Team::_LoadSymbols(debug_symbol_lookup_context* lookupContext,
|
||||
team_id owner)
|
||||
{
|
||||
// iterate through the team's images and collect the symbols
|
||||
image_info imageInfo;
|
||||
int32 cookie = 0;
|
||||
while (get_next_image_info(owner, &cookie, &imageInfo) == B_OK) {
|
||||
status_t error = _LoadImageSymbols(lookupContext, imageInfo,
|
||||
owner, 0);
|
||||
if (error == B_NO_MEMORY)
|
||||
return error;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Team::_LoadImageSymbols(debug_symbol_lookup_context* lookupContext,
|
||||
const image_info& imageInfo, team_id owner, int32 event,
|
||||
Image** _image = NULL)
|
||||
{
|
||||
Image* image = new(std::nothrow) Image(imageInfo, owner, event);
|
||||
if (image == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t error = image->LoadSymbols(lookupContext);
|
||||
if (error != B_OK) {
|
||||
delete image;
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!fImages.AddItem(image)) {
|
||||
delete image;
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (_image != NULL)
|
||||
*_image = image;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Team::_RemoveImage(int32 index, int32 event)
|
||||
{
|
||||
Image* image = fImages.RemoveItemAt(index);
|
||||
if (image == NULL)
|
||||
return;
|
||||
|
||||
// Note: We don't tell the threads that the image has been removed. They
|
||||
// will be updated lazily when their next profiler update arrives. This
|
||||
// is necessary, since the update might contain samples hitting that
|
||||
// image.
|
||||
|
||||
image->SetDeletionEvent(event);
|
||||
image->RemoveReference();
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef TEAM_H
|
||||
#define TEAM_H
|
||||
|
||||
#include <debug_support.h>
|
||||
#include <ObjectList.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
|
||||
class Team {
|
||||
public:
|
||||
Team();
|
||||
~Team();
|
||||
|
||||
status_t Init(team_id teamID, port_id debuggerPort);
|
||||
status_t InitThread(Thread* thread);
|
||||
|
||||
void RemoveThread(Thread* thread);
|
||||
|
||||
void Exec(int32 event);
|
||||
|
||||
status_t AddImage(const image_info& imageInfo,
|
||||
int32 event);
|
||||
status_t RemoveImage(const image_info& imageInfo,
|
||||
int32 event);
|
||||
|
||||
inline const BObjectList<Image>& Images() const;
|
||||
Image* FindImage(image_id id) const;
|
||||
|
||||
inline team_id ID() const;
|
||||
|
||||
private:
|
||||
status_t _LoadSymbols(
|
||||
debug_symbol_lookup_context* lookupContext,
|
||||
team_id owner);
|
||||
status_t _LoadImageSymbols(
|
||||
debug_symbol_lookup_context* lookupContext,
|
||||
const image_info& imageInfo, team_id owner,
|
||||
int32 event, Image** _image = NULL);
|
||||
void _RemoveImage(int32 index, int32 event);
|
||||
|
||||
private:
|
||||
typedef DoublyLinkedList<Thread> ThreadList;
|
||||
|
||||
team_info fInfo;
|
||||
port_id fNubPort;
|
||||
debug_context fDebugContext;
|
||||
ThreadList fThreads;
|
||||
BObjectList<Image> fImages;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
const BObjectList<Image>&
|
||||
Team::Images() const
|
||||
{
|
||||
return fImages;
|
||||
}
|
||||
|
||||
|
||||
team_id
|
||||
Team::ID() const
|
||||
{
|
||||
return fInfo.team;
|
||||
}
|
||||
|
||||
|
||||
#endif // TEAM_H
|
||||
@@ -0,0 +1,517 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <new>
|
||||
|
||||
#include <debug_support.h>
|
||||
|
||||
#include "debug_utils.h"
|
||||
|
||||
#include "Options.h"
|
||||
#include "Team.h"
|
||||
|
||||
|
||||
struct HitSymbol {
|
||||
int64 hits;
|
||||
Symbol* symbol;
|
||||
|
||||
inline bool operator<(const HitSymbol& other) const
|
||||
{
|
||||
return hits > other.hits;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ThreadImage : public DoublyLinkedListLinkImpl<ThreadImage> {
|
||||
public:
|
||||
ThreadImage(Image* image)
|
||||
:
|
||||
fImage(image),
|
||||
fSymbolHits(NULL),
|
||||
fTotalHits(0),
|
||||
fUnknownHits(0)
|
||||
{
|
||||
fImage->AddReference();
|
||||
}
|
||||
|
||||
~ThreadImage()
|
||||
{
|
||||
fImage->RemoveReference();
|
||||
}
|
||||
|
||||
status_t Init()
|
||||
{
|
||||
int32 symbolCount = fImage->SymbolCount();
|
||||
fSymbolHits = new(std::nothrow) int64[symbolCount];
|
||||
if (fSymbolHits == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
memset(fSymbolHits, 0, 8 * symbolCount);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
image_id ID() const
|
||||
{
|
||||
return fImage->ID();
|
||||
}
|
||||
|
||||
bool ContainsAddress(addr_t address) const
|
||||
{
|
||||
return fImage->ContainsAddress(address);
|
||||
}
|
||||
|
||||
void AddHit(addr_t address)
|
||||
{
|
||||
int32 symbolIndex = fImage->FindSymbol(address);
|
||||
if (symbolIndex >= 0)
|
||||
fSymbolHits[symbolIndex]++;
|
||||
else
|
||||
fUnknownHits++;
|
||||
|
||||
fTotalHits++;
|
||||
}
|
||||
|
||||
void AddSymbolHit(int32 symbolIndex)
|
||||
{
|
||||
fSymbolHits[symbolIndex]++;
|
||||
}
|
||||
|
||||
void AddImageHit()
|
||||
{
|
||||
fTotalHits++;
|
||||
}
|
||||
|
||||
Image* GetImage() const
|
||||
{
|
||||
return fImage;
|
||||
}
|
||||
|
||||
const int64* SymbolHits() const
|
||||
{
|
||||
return fSymbolHits;
|
||||
}
|
||||
|
||||
int64 TotalHits() const
|
||||
{
|
||||
return fTotalHits;
|
||||
}
|
||||
|
||||
int64 UnknownHits() const
|
||||
{
|
||||
return fUnknownHits;
|
||||
}
|
||||
|
||||
private:
|
||||
Image* fImage;
|
||||
int64* fSymbolHits;
|
||||
int64 fTotalHits;
|
||||
int64 fUnknownHits;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
Thread::Thread(const thread_info& info, Team* team)
|
||||
:
|
||||
fInfo(info),
|
||||
fTeam(team),
|
||||
fSampleArea(-1),
|
||||
fSamples(NULL),
|
||||
fImages(),
|
||||
fOldImages(),
|
||||
fTotalTicks(0),
|
||||
fUnkownTicks(0),
|
||||
fDroppedTicks(0),
|
||||
fInterval(1),
|
||||
fTotalSampleCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
while (ThreadImage* image = fImages.RemoveHead())
|
||||
delete image;
|
||||
while (ThreadImage* image = fOldImages.RemoveHead())
|
||||
delete image;
|
||||
|
||||
if (fSampleArea >= 0)
|
||||
delete_area(fSampleArea);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Thread::UpdateInfo()
|
||||
{
|
||||
thread_info info;
|
||||
if (get_thread_info(ID(), &info) == B_OK)
|
||||
fInfo = info;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Thread::SetSampleArea(area_id area, addr_t* samples)
|
||||
{
|
||||
fSampleArea = area;
|
||||
fSamples = samples;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Thread::SetInterval(bigtime_t interval)
|
||||
{
|
||||
fInterval = interval;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
Thread::AddImage(Image* image)
|
||||
{
|
||||
ThreadImage* threadImage = new(std::nothrow) ThreadImage(image);
|
||||
if (threadImage == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
status_t error = threadImage->Init();
|
||||
if (error != B_OK) {
|
||||
delete threadImage;
|
||||
return error;
|
||||
}
|
||||
|
||||
fImages.Add(threadImage);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Thread::ImageRemoved(Image* image)
|
||||
{
|
||||
ImageList::Iterator it = fImages.GetIterator();
|
||||
while (ThreadImage* threadImage = it.Next()) {
|
||||
if (threadImage->GetImage() == image) {
|
||||
fImages.Remove(threadImage);
|
||||
if (threadImage->TotalHits() > 0)
|
||||
fOldImages.Add(threadImage);
|
||||
else
|
||||
delete threadImage;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ThreadImage*
|
||||
Thread::FindImage(addr_t address) const
|
||||
{
|
||||
ImageList::ConstIterator it = fImages.GetIterator();
|
||||
while (ThreadImage* image = it.Next()) {
|
||||
if (image->ContainsAddress(address))
|
||||
return image;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Thread::AddSamples(int32 count, int32 dropped, int32 stackDepth,
|
||||
bool variableStackDepth, int32 event)
|
||||
{
|
||||
_RemoveObsoleteImages(event);
|
||||
|
||||
// Temporarily remove images that have been created after the given
|
||||
// event.
|
||||
ImageList newImages;
|
||||
ImageList::Iterator it = fImages.GetIterator();
|
||||
while (ThreadImage* image = it.Next()) {
|
||||
if (image->GetImage()->CreationEvent() > event) {
|
||||
it.Remove();
|
||||
newImages.Add(image);
|
||||
}
|
||||
}
|
||||
|
||||
if (variableStackDepth) {
|
||||
// Variable stack depth means we have full caller stack analyzes
|
||||
// enabled.
|
||||
int32 totalSampleCount = 0;
|
||||
int32 tickCount = 0;
|
||||
addr_t* samples = fSamples;
|
||||
|
||||
while (count > 0) {
|
||||
addr_t sampleCount = *(samples++);
|
||||
|
||||
if (sampleCount >= B_DEBUG_PROFILE_EVENT_BASE) {
|
||||
int32 eventParameterCount
|
||||
= sampleCount & B_DEBUG_PROFILE_EVENT_PARAMETER_MASK;
|
||||
if (sampleCount == B_DEBUG_PROFILE_IMAGE_EVENT) {
|
||||
int32 imageEvent = (int32)samples[0];
|
||||
_RemoveObsoleteImages(imageEvent);
|
||||
_AddNewImages(newImages, imageEvent);
|
||||
} else {
|
||||
fprintf(stderr, "unknown profile event: %#lx\n",
|
||||
sampleCount);
|
||||
}
|
||||
|
||||
samples += eventParameterCount;
|
||||
count -= eventParameterCount + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sort the samples. This way hits of the same symbol are
|
||||
// successive and we can avoid incrementing the hit count of the
|
||||
// same symbol twice. Same for images.
|
||||
std::sort(samples, samples + sampleCount);
|
||||
|
||||
int32 unknownSamples = 0;
|
||||
ThreadImage* previousImage = NULL;
|
||||
int32 previousSymbol = -1;
|
||||
|
||||
for (uint32 i = 0; i < sampleCount; i++) {
|
||||
addr_t address = samples[i];
|
||||
ThreadImage* image = FindImage(address);
|
||||
int32 symbol = -1;
|
||||
if (image != NULL) {
|
||||
symbol = image->GetImage()->FindSymbol(address);
|
||||
if (symbol < 0) {
|
||||
// TODO: Count unknown image hits?
|
||||
} else if (symbol != previousSymbol)
|
||||
image->AddSymbolHit(symbol);
|
||||
|
||||
if (image != previousImage)
|
||||
image->AddImageHit();
|
||||
} else
|
||||
unknownSamples++;
|
||||
|
||||
previousImage = image;
|
||||
previousSymbol = symbol;
|
||||
}
|
||||
|
||||
if (unknownSamples == (int32)sampleCount)
|
||||
fUnkownTicks++;
|
||||
|
||||
samples += sampleCount;
|
||||
count -= sampleCount + 1;
|
||||
tickCount++;
|
||||
totalSampleCount += sampleCount;
|
||||
}
|
||||
|
||||
fTotalTicks += tickCount;
|
||||
fTotalSampleCount += totalSampleCount;
|
||||
} else {
|
||||
count = count / stackDepth * stackDepth;
|
||||
|
||||
for (int32 i = 0; i < count; i += stackDepth) {
|
||||
ThreadImage* image = NULL;
|
||||
for (int32 k = 0; k < stackDepth; k++) {
|
||||
addr_t address = fSamples[i + k];
|
||||
image = FindImage(address);
|
||||
if (image != NULL) {
|
||||
image->AddHit(address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (image == NULL)
|
||||
fUnkownTicks++;
|
||||
}
|
||||
|
||||
fTotalTicks += count / stackDepth;
|
||||
}
|
||||
|
||||
fDroppedTicks += dropped;
|
||||
|
||||
// re-add the new images
|
||||
fImages.MoveFrom(&newImages);
|
||||
|
||||
_SynchronizeImages();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Thread::PrintResults() const
|
||||
{
|
||||
// count images and symbols
|
||||
int32 imageCount = 0;
|
||||
int32 symbolCount = 0;
|
||||
|
||||
ImageList::ConstIterator it = fOldImages.GetIterator();
|
||||
while (ThreadImage* image = it.Next()) {
|
||||
if (image->TotalHits() > 0) {
|
||||
imageCount++;
|
||||
if (image->TotalHits() > image->UnknownHits())
|
||||
symbolCount += image->GetImage()->SymbolCount();
|
||||
}
|
||||
}
|
||||
|
||||
it = fImages.GetIterator();
|
||||
while (ThreadImage* image = it.Next()) {
|
||||
if (image->TotalHits() > 0) {
|
||||
imageCount++;
|
||||
if (image->TotalHits() > image->UnknownHits())
|
||||
symbolCount += image->GetImage()->SymbolCount();
|
||||
}
|
||||
}
|
||||
|
||||
ThreadImage* images[imageCount];
|
||||
imageCount = 0;
|
||||
|
||||
it = fOldImages.GetIterator();
|
||||
while (ThreadImage* image = it.Next()) {
|
||||
if (image->TotalHits() > 0)
|
||||
images[imageCount++] = image;
|
||||
}
|
||||
|
||||
it = fImages.GetIterator();
|
||||
while (ThreadImage* image = it.Next()) {
|
||||
if (image->TotalHits() > 0)
|
||||
images[imageCount++] = image;
|
||||
}
|
||||
|
||||
// find and sort the hit symbols
|
||||
HitSymbol hitSymbols[symbolCount];
|
||||
int32 hitSymbolCount = 0;
|
||||
|
||||
for (int32 k = 0; k < imageCount; k++) {
|
||||
ThreadImage* image = images[k];
|
||||
if (image->TotalHits() > image->UnknownHits()) {
|
||||
Symbol** symbols = image->GetImage()->Symbols();
|
||||
const int64* symbolHits = image->SymbolHits();
|
||||
int32 imageSymbolCount = image->GetImage()->SymbolCount();
|
||||
for (int32 i = 0; i < imageSymbolCount; i++) {
|
||||
if (symbolHits[i] > 0) {
|
||||
HitSymbol& hitSymbol = hitSymbols[hitSymbolCount++];
|
||||
hitSymbol.hits = symbolHits[i];
|
||||
hitSymbol.symbol = symbols[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hitSymbolCount > 1)
|
||||
std::sort(hitSymbols, hitSymbols + hitSymbolCount);
|
||||
|
||||
int64 totalTicks = fTotalTicks;
|
||||
fprintf(gOptions.output, "\nprofiling results for thread \"%s\" "
|
||||
"(%ld):\n", Name(), ID());
|
||||
fprintf(gOptions.output, " tick interval: %lld us\n", fInterval);
|
||||
fprintf(gOptions.output, " total ticks: %lld (%lld us)\n",
|
||||
totalTicks, totalTicks * fInterval);
|
||||
if (totalTicks == 0)
|
||||
totalTicks = 1;
|
||||
fprintf(gOptions.output, " unknown ticks: %lld (%lld us, %6.2f%%)\n",
|
||||
fUnkownTicks, fUnkownTicks * fInterval,
|
||||
100.0 * fUnkownTicks / totalTicks);
|
||||
fprintf(gOptions.output, " dropped ticks: %lld (%lld us, %6.2f%%)\n",
|
||||
fDroppedTicks, fDroppedTicks * fInterval,
|
||||
100.0 * fDroppedTicks / totalTicks);
|
||||
if (gOptions.analyze_full_stack) {
|
||||
fprintf(gOptions.output, " samples/tick: %.1f\n",
|
||||
(double)fTotalSampleCount / totalTicks);
|
||||
}
|
||||
|
||||
if (imageCount > 0) {
|
||||
fprintf(gOptions.output, "\n");
|
||||
fprintf(gOptions.output, " hits unknown image\n");
|
||||
fprintf(gOptions.output, " ---------------------------------------"
|
||||
"---------------------------------------\n");
|
||||
for (int32 k = 0; k < imageCount; k++) {
|
||||
ThreadImage* image = images[k];
|
||||
const image_info& imageInfo = image->GetImage()->Info();
|
||||
fprintf(gOptions.output, " %10lld %10lld %7ld %s\n",
|
||||
image->TotalHits(), image->UnknownHits(), imageInfo.id,
|
||||
imageInfo.name);
|
||||
}
|
||||
}
|
||||
|
||||
if (hitSymbolCount > 0) {
|
||||
fprintf(gOptions.output, "\n");
|
||||
fprintf(gOptions.output, " hits in us in %% "
|
||||
"image function\n");
|
||||
fprintf(gOptions.output, " ---------------------------------------"
|
||||
"---------------------------------------\n");
|
||||
for (int32 i = 0; i < hitSymbolCount; i++) {
|
||||
const HitSymbol& hitSymbol = hitSymbols[i];
|
||||
const Symbol* symbol = hitSymbol.symbol;
|
||||
fprintf(gOptions.output, " %10lld %10lld %6.2f %6ld %s\n",
|
||||
hitSymbol.hits, hitSymbol.hits * fInterval,
|
||||
100.0 * hitSymbol.hits / totalTicks, symbol->image->ID(),
|
||||
symbol->Name());
|
||||
}
|
||||
} else
|
||||
fprintf(gOptions.output, " no functions were hit\n");
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Thread::_SynchronizeImages()
|
||||
{
|
||||
const BObjectList<Image>& teamImages = fTeam->Images();
|
||||
|
||||
// remove obsolete images
|
||||
ImageList::Iterator it = fImages.GetIterator();
|
||||
while (ThreadImage* image = it.Next()) {
|
||||
if (fTeam->FindImage(image->ID()) == NULL) {
|
||||
it.Remove();
|
||||
if (image->TotalHits() > 0)
|
||||
fOldImages.Add(image);
|
||||
else
|
||||
delete image;
|
||||
}
|
||||
}
|
||||
|
||||
// add new images
|
||||
for (int32 i = 0; Image* image = teamImages.ItemAt(i); i++) {
|
||||
if (_FindImageByID(image->ID()) == NULL)
|
||||
AddImage(image);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Thread::_AddNewImages(ImageList& newImages, int32 event)
|
||||
{
|
||||
ImageList::Iterator it = newImages.GetIterator();
|
||||
while (ThreadImage* image = it.Next()) {
|
||||
if (image->GetImage()->CreationEvent() >= event) {
|
||||
it.Remove();
|
||||
fImages.Add(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Thread::_RemoveObsoleteImages(int32 event)
|
||||
{
|
||||
// remove obsolete images
|
||||
ImageList::Iterator it = fImages.GetIterator();
|
||||
while (ThreadImage* image = it.Next()) {
|
||||
int32 deleted = image->GetImage()->DeletionEvent();
|
||||
if (deleted >= 0 && event >= deleted) {
|
||||
it.Remove();
|
||||
if (image->TotalHits() > 0)
|
||||
fOldImages.Add(image);
|
||||
else
|
||||
delete image;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ThreadImage*
|
||||
Thread::_FindImageByID(image_id id) const
|
||||
{
|
||||
ImageList::ConstIterator it = fImages.GetIterator();
|
||||
while (ThreadImage* image = it.Next()) {
|
||||
if (image->ID() == id)
|
||||
return image;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef THREAD_H
|
||||
#define THREAD_H
|
||||
|
||||
#include <util/DoublyLinkedList.h>
|
||||
|
||||
#include "Image.h"
|
||||
|
||||
|
||||
class Team;
|
||||
class ThreadImage;
|
||||
|
||||
|
||||
class Thread : public DoublyLinkedListLinkImpl<Thread> {
|
||||
public:
|
||||
Thread(const thread_info& info, Team* team);
|
||||
~Thread();
|
||||
|
||||
inline thread_id ID() const;
|
||||
inline const char* Name() const;
|
||||
inline addr_t* Samples() const;
|
||||
inline Team* GetTeam() const;
|
||||
|
||||
void UpdateInfo();
|
||||
|
||||
void SetSampleArea(area_id area, addr_t* samples);
|
||||
void SetInterval(bigtime_t interval);
|
||||
|
||||
status_t AddImage(Image* image);
|
||||
void ImageRemoved(Image* image);
|
||||
ThreadImage* FindImage(addr_t address) const;
|
||||
|
||||
void AddSamples(int32 count, int32 dropped,
|
||||
int32 stackDepth, bool variableStackDepth,
|
||||
int32 event);
|
||||
void PrintResults() const;
|
||||
|
||||
private:
|
||||
typedef DoublyLinkedList<ThreadImage> ImageList;
|
||||
|
||||
void _SynchronizeImages();
|
||||
|
||||
void _AddNewImages(ImageList& newImages,
|
||||
int32 event);
|
||||
void _RemoveObsoleteImages(int32 event);
|
||||
|
||||
ThreadImage* _FindImageByID(image_id id) const;
|
||||
|
||||
private:
|
||||
thread_info fInfo;
|
||||
::Team* fTeam;
|
||||
area_id fSampleArea;
|
||||
addr_t* fSamples;
|
||||
ImageList fImages;
|
||||
ImageList fOldImages;
|
||||
int64 fTotalTicks;
|
||||
int64 fUnkownTicks;
|
||||
int64 fDroppedTicks;
|
||||
bigtime_t fInterval;
|
||||
int64 fTotalSampleCount;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
thread_id
|
||||
Thread::ID() const
|
||||
{
|
||||
return fInfo.thread;
|
||||
}
|
||||
|
||||
|
||||
const char*
|
||||
Thread::Name() const
|
||||
{
|
||||
return fInfo.name;
|
||||
}
|
||||
|
||||
|
||||
addr_t*
|
||||
Thread::Samples() const
|
||||
{
|
||||
return fSamples;
|
||||
}
|
||||
|
||||
|
||||
Team*
|
||||
Thread::GetTeam() const
|
||||
{
|
||||
return fTeam;
|
||||
}
|
||||
|
||||
|
||||
#endif // THREAD_H
|
||||
@@ -0,0 +1,415 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* 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 <new>
|
||||
|
||||
#include <debugger.h>
|
||||
#include <OS.h>
|
||||
#include <String.h>
|
||||
|
||||
#include <debug_support.h>
|
||||
#include <ObjectList.h>
|
||||
#include <Referenceable.h>
|
||||
|
||||
#include <util/DoublyLinkedList.h>
|
||||
|
||||
#include "debug_utils.h"
|
||||
#include "Image.h"
|
||||
#include "Options.h"
|
||||
#include "Team.h"
|
||||
|
||||
|
||||
extern const char* __progname;
|
||||
const char* kCommandName = __progname;
|
||||
|
||||
|
||||
class Image;
|
||||
class Team;
|
||||
class Thread;
|
||||
|
||||
|
||||
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"
|
||||
" -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"
|
||||
" -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"
|
||||
;
|
||||
|
||||
|
||||
Options gOptions;
|
||||
|
||||
|
||||
class ThreadManager {
|
||||
public:
|
||||
ThreadManager(port_id debuggerPort)
|
||||
:
|
||||
fTeams(20, true),
|
||||
fThreads(20, true),
|
||||
fDebuggerPort(debuggerPort)
|
||||
{
|
||||
}
|
||||
|
||||
status_t AddTeam(team_id teamID, 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 = team->Init(teamID, fDebuggerPort);
|
||||
if (error != B_OK) {
|
||||
delete team;
|
||||
return error;
|
||||
}
|
||||
|
||||
fTeams.AddItem(team);
|
||||
|
||||
if (_team != NULL)
|
||||
*_team = team;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
status_t AddThread(thread_id threadID)
|
||||
{
|
||||
if (FindThread(threadID) != NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
thread_info threadInfo;
|
||||
status_t error = get_thread_info(threadID, &threadInfo);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
Team* team = FindTeam(threadInfo.team);
|
||||
if (team == NULL)
|
||||
return B_BAD_TEAM_ID;
|
||||
|
||||
Thread* thread = new(std::nothrow) Thread(threadInfo, team);
|
||||
if (thread == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
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))
|
||||
fTeams.RemoveItem(team, true);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
BObjectList<Team> fTeams;
|
||||
BObjectList<Thread> fThreads;
|
||||
port_id fDebuggerPort;
|
||||
};
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
int
|
||||
main(int argc, const char* const* argv)
|
||||
{
|
||||
int32 stackDepth = 0;
|
||||
const char* outputFile = NULL;
|
||||
|
||||
while (true) {
|
||||
static struct option sLongOptions[] = {
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
opterr = 0; // don't print errors
|
||||
int c = getopt_long(argc, (char**)argv, "+cCfhi:klo:s:", sLongOptions,
|
||||
NULL);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
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 's':
|
||||
stackDepth = atol(optarg);
|
||||
break;
|
||||
default:
|
||||
print_usage_and_exit(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind >= argc)
|
||||
print_usage_and_exit(true);
|
||||
|
||||
if (stackDepth != 0)
|
||||
gOptions.stack_depth = stackDepth;
|
||||
|
||||
const char* const* programArgs = argv + optind;
|
||||
int programArgCount = argc - optind;
|
||||
|
||||
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;
|
||||
|
||||
// get thread/team to be debugged
|
||||
thread_id threadID = -1;
|
||||
team_id teamID = -1;
|
||||
// if (programArgCount > 1
|
||||
// || !get_id(*programArgs, (traceTeam ? teamID : thread))) {
|
||||
// we've been given an executable and need to load it
|
||||
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, 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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
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 (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
|
||||
quitLoop = message.origin.team == teamID;
|
||||
break;
|
||||
case B_DEBUGGER_MESSAGE_TEAM_EXEC:
|
||||
if (Team* team = threadManager.FindTeam(message.origin.team))
|
||||
team->Exec(message.team_exec.image_event);
|
||||
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:
|
||||
if (!gOptions.profile_teams)
|
||||
break;
|
||||
|
||||
if (Team* team = threadManager.FindTeam(message.origin.team)) {
|
||||
team->AddImage(message.image_created.info,
|
||||
message.image_created.image_event);
|
||||
}
|
||||
break;
|
||||
case B_DEBUGGER_MESSAGE_IMAGE_DELETED:
|
||||
if (Team* team = threadManager.FindTeam(message.origin.team)) {
|
||||
team->RemoveImage(message.image_deleted.info,
|
||||
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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user