- TeamDebugger's listener interface now exports a TeamDebuggerRestartRequested hook. The latter is used to request starting a new debugger instance with the same arguments/settings as the team it represented. Implemented for the graphical debugger. - When a team terminates, the resulting dialog now allows the user to choose to quit, restart, or simply do nothing. The latter option still needs some work though, as e.g. setting additional breakpoints currently fails since the corresponding debugger interface is no longer around. Implements the main part of #9774.
1862 lines
44 KiB
C++
1862 lines
44 KiB
C++
/*
|
|
* Copyright 2009-2012, Ingo Weinhold, [email protected].
|
|
* Copyright 2010-2013, Rene Gollent, [email protected].
|
|
* Distributed under the terms of the MIT License.
|
|
*/
|
|
|
|
|
|
#include "TeamDebugger.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
|
|
#include <new>
|
|
|
|
#include <Entry.h>
|
|
#include <Message.h>
|
|
|
|
#include <AutoLocker.h>
|
|
|
|
#include "debug_utils.h"
|
|
|
|
#include "BreakpointManager.h"
|
|
#include "BreakpointSetting.h"
|
|
#include "CpuState.h"
|
|
#include "DebuggerInterface.h"
|
|
#include "DebugReportGenerator.h"
|
|
#include "FileManager.h"
|
|
#include "Function.h"
|
|
#include "FunctionID.h"
|
|
#include "ImageDebugInfo.h"
|
|
#include "Jobs.h"
|
|
#include "LocatableFile.h"
|
|
#include "MessageCodes.h"
|
|
#include "SettingsManager.h"
|
|
#include "SourceCode.h"
|
|
#include "SpecificImageDebugInfo.h"
|
|
#include "StackFrame.h"
|
|
#include "StackFrameValues.h"
|
|
#include "Statement.h"
|
|
#include "SymbolInfo.h"
|
|
#include "TeamDebugInfo.h"
|
|
#include "TeamInfo.h"
|
|
#include "TeamMemoryBlock.h"
|
|
#include "TeamMemoryBlockManager.h"
|
|
#include "TeamSettings.h"
|
|
#include "TeamUiSettings.h"
|
|
#include "Tracing.h"
|
|
#include "ValueNode.h"
|
|
#include "ValueNodeContainer.h"
|
|
#include "Variable.h"
|
|
#include "WatchpointManager.h"
|
|
|
|
|
|
// #pragma mark - ImageHandler
|
|
|
|
|
|
struct TeamDebugger::ImageHandler : public BReferenceable,
|
|
private LocatableFile::Listener {
|
|
public:
|
|
ImageHandler(TeamDebugger* teamDebugger, Image* image)
|
|
:
|
|
fTeamDebugger(teamDebugger),
|
|
fImage(image)
|
|
{
|
|
fImage->AcquireReference();
|
|
if (fImage->ImageFile() != NULL)
|
|
fImage->ImageFile()->AddListener(this);
|
|
}
|
|
|
|
~ImageHandler()
|
|
{
|
|
if (fImage->ImageFile() != NULL)
|
|
fImage->ImageFile()->RemoveListener(this);
|
|
fImage->ReleaseReference();
|
|
}
|
|
|
|
Image* GetImage() const
|
|
{
|
|
return fImage;
|
|
}
|
|
|
|
image_id ImageID() const
|
|
{
|
|
return fImage->ID();
|
|
}
|
|
|
|
private:
|
|
// LocatableFile::Listener
|
|
virtual void LocatableFileChanged(LocatableFile* file)
|
|
{
|
|
BMessage message(MSG_IMAGE_FILE_CHANGED);
|
|
message.AddInt32("image", fImage->ID());
|
|
fTeamDebugger->PostMessage(&message);
|
|
}
|
|
|
|
private:
|
|
TeamDebugger* fTeamDebugger;
|
|
Image* fImage;
|
|
|
|
public:
|
|
ImageHandler* fNext;
|
|
};
|
|
|
|
|
|
// #pragma mark - ImageHandlerHashDefinition
|
|
|
|
|
|
struct TeamDebugger::ImageHandlerHashDefinition {
|
|
typedef image_id KeyType;
|
|
typedef ImageHandler ValueType;
|
|
|
|
size_t HashKey(image_id key) const
|
|
{
|
|
return (size_t)key;
|
|
}
|
|
|
|
size_t Hash(const ImageHandler* value) const
|
|
{
|
|
return HashKey(value->ImageID());
|
|
}
|
|
|
|
bool Compare(image_id key, const ImageHandler* value) const
|
|
{
|
|
return value->ImageID() == key;
|
|
}
|
|
|
|
ImageHandler*& GetLink(ImageHandler* value) const
|
|
{
|
|
return value->fNext;
|
|
}
|
|
};
|
|
|
|
|
|
// #pragma mark - ImageInfoPendingThread
|
|
|
|
|
|
struct TeamDebugger::ImageInfoPendingThread {
|
|
public:
|
|
ImageInfoPendingThread(image_id image, thread_id thread)
|
|
:
|
|
fImage(image),
|
|
fThread(thread)
|
|
{
|
|
}
|
|
|
|
~ImageInfoPendingThread()
|
|
{
|
|
}
|
|
|
|
image_id ImageID() const
|
|
{
|
|
return fImage;
|
|
}
|
|
|
|
thread_id ThreadID() const
|
|
{
|
|
return fThread;
|
|
}
|
|
|
|
private:
|
|
image_id fImage;
|
|
thread_id fThread;
|
|
|
|
public:
|
|
ImageInfoPendingThread* fNext;
|
|
};
|
|
|
|
|
|
// #pragma mark - ImageHandlerHashDefinition
|
|
|
|
|
|
struct TeamDebugger::ImageInfoPendingThreadHashDefinition {
|
|
typedef image_id KeyType;
|
|
typedef ImageInfoPendingThread ValueType;
|
|
|
|
size_t HashKey(image_id key) const
|
|
{
|
|
return (size_t)key;
|
|
}
|
|
|
|
size_t Hash(const ImageInfoPendingThread* value) const
|
|
{
|
|
return HashKey(value->ImageID());
|
|
}
|
|
|
|
bool Compare(image_id key, const ImageInfoPendingThread* value) const
|
|
{
|
|
return value->ImageID() == key;
|
|
}
|
|
|
|
ImageInfoPendingThread*& GetLink(ImageInfoPendingThread* value) const
|
|
{
|
|
return value->fNext;
|
|
}
|
|
};
|
|
|
|
|
|
// #pragma mark - TeamDebugger
|
|
|
|
|
|
TeamDebugger::TeamDebugger(Listener* listener, UserInterface* userInterface,
|
|
SettingsManager* settingsManager)
|
|
:
|
|
BLooper("team debugger"),
|
|
fListener(listener),
|
|
fSettingsManager(settingsManager),
|
|
fTeam(NULL),
|
|
fTeamID(-1),
|
|
fImageHandlers(NULL),
|
|
fImageInfoPendingThreads(NULL),
|
|
fDebuggerInterface(NULL),
|
|
fFileManager(NULL),
|
|
fWorker(NULL),
|
|
fBreakpointManager(NULL),
|
|
fWatchpointManager(NULL),
|
|
fMemoryBlockManager(NULL),
|
|
fReportGenerator(NULL),
|
|
fDebugEventListener(-1),
|
|
fUserInterface(userInterface),
|
|
fTerminating(false),
|
|
fKillTeamOnQuit(false),
|
|
fCommandLineArgc(0),
|
|
fCommandLineArgv(NULL)
|
|
{
|
|
fUserInterface->AcquireReference();
|
|
}
|
|
|
|
|
|
TeamDebugger::~TeamDebugger()
|
|
{
|
|
if (fTeam != NULL)
|
|
_SaveSettings();
|
|
|
|
AutoLocker<BLooper> locker(this);
|
|
|
|
fTerminating = true;
|
|
|
|
if (fDebuggerInterface != NULL) {
|
|
fDebuggerInterface->Close(fKillTeamOnQuit);
|
|
fDebuggerInterface->ReleaseReference();
|
|
}
|
|
|
|
if (fWorker != NULL)
|
|
fWorker->ShutDown();
|
|
|
|
locker.Unlock();
|
|
|
|
if (fDebugEventListener >= 0)
|
|
wait_for_thread(fDebugEventListener, NULL);
|
|
|
|
// terminate UI
|
|
if (fUserInterface != NULL) {
|
|
fUserInterface->Terminate();
|
|
fUserInterface->ReleaseReference();
|
|
}
|
|
|
|
ThreadHandler* threadHandler = fThreadHandlers.Clear(true);
|
|
while (threadHandler != NULL) {
|
|
ThreadHandler* next = threadHandler->fNext;
|
|
threadHandler->ReleaseReference();
|
|
threadHandler = next;
|
|
}
|
|
|
|
if (fImageHandlers != NULL) {
|
|
ImageHandler* imageHandler = fImageHandlers->Clear(true);
|
|
while (imageHandler != NULL) {
|
|
ImageHandler* next = imageHandler->fNext;
|
|
imageHandler->ReleaseReference();
|
|
imageHandler = next;
|
|
}
|
|
}
|
|
|
|
delete fImageHandlers;
|
|
|
|
if (fImageInfoPendingThreads != NULL) {
|
|
ImageInfoPendingThread* thread = fImageInfoPendingThreads->Clear(true);
|
|
while (thread != NULL) {
|
|
ImageInfoPendingThread* next = thread->fNext;
|
|
delete thread;
|
|
thread = next;
|
|
}
|
|
}
|
|
|
|
if (fReportGenerator != NULL) {
|
|
fReportGenerator->Lock();
|
|
fReportGenerator->Quit();
|
|
}
|
|
|
|
delete fImageInfoPendingThreads;
|
|
|
|
delete fBreakpointManager;
|
|
delete fWatchpointManager;
|
|
delete fMemoryBlockManager;
|
|
delete fWorker;
|
|
delete fTeam;
|
|
delete fFileManager;
|
|
|
|
for (int i = 0; i < fCommandLineArgc; i++) {
|
|
if (fCommandLineArgv[i] != NULL)
|
|
free(const_cast<char*>(fCommandLineArgv[i]));
|
|
}
|
|
|
|
delete [] fCommandLineArgv;
|
|
|
|
fListener->TeamDebuggerQuit(this);
|
|
}
|
|
|
|
|
|
status_t
|
|
TeamDebugger::Init(team_id teamID, thread_id threadID, int argc,
|
|
const char* const* argv, bool stopInMain)
|
|
{
|
|
bool targetIsLocal = true;
|
|
// TODO: Support non-local targets!
|
|
|
|
// the first thing we want to do when running
|
|
PostMessage(MSG_LOAD_SETTINGS);
|
|
|
|
fTeamID = teamID;
|
|
|
|
status_t error = _HandleSetArguments(argc, argv);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// create debugger interface
|
|
fDebuggerInterface = new(std::nothrow) DebuggerInterface(fTeamID);
|
|
if (fDebuggerInterface == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
error = fDebuggerInterface->Init();
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// create file manager
|
|
fFileManager = new(std::nothrow) FileManager;
|
|
if (fFileManager == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
error = fFileManager->Init(targetIsLocal);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// create team debug info
|
|
TeamDebugInfo* teamDebugInfo = new(std::nothrow) TeamDebugInfo(
|
|
fDebuggerInterface, fDebuggerInterface->GetArchitecture(),
|
|
fFileManager);
|
|
if (teamDebugInfo == NULL)
|
|
return B_NO_MEMORY;
|
|
BReference<TeamDebugInfo> teamDebugInfoReference(teamDebugInfo);
|
|
|
|
error = teamDebugInfo->Init();
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// check whether the team exists at all
|
|
TeamInfo teamInfo;
|
|
error = fDebuggerInterface->GetTeamInfo(teamInfo);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// create a team object
|
|
fTeam = new(std::nothrow) ::Team(fTeamID, fDebuggerInterface,
|
|
fDebuggerInterface->GetArchitecture(), teamDebugInfo,
|
|
teamDebugInfo);
|
|
if (fTeam == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
error = fTeam->Init();
|
|
if (error != B_OK)
|
|
return error;
|
|
fTeam->SetName(teamInfo.Arguments());
|
|
// TODO: Set a better name!
|
|
|
|
fTeam->AddListener(this);
|
|
|
|
// init thread handler table
|
|
error = fThreadHandlers.Init();
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// create image handler table
|
|
fImageHandlers = new(std::nothrow) ImageHandlerTable;
|
|
if (fImageHandlers == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
error = fImageHandlers->Init();
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
fImageInfoPendingThreads = new(std::nothrow) ImageInfoPendingThreadTable;
|
|
if (fImageInfoPendingThreads == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
// create our worker
|
|
fWorker = new(std::nothrow) Worker;
|
|
if (fWorker == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
error = fWorker->Init();
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// create the breakpoint manager
|
|
fBreakpointManager = new(std::nothrow) BreakpointManager(fTeam,
|
|
fDebuggerInterface);
|
|
if (fBreakpointManager == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
error = fBreakpointManager->Init();
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// create the watchpoint manager
|
|
fWatchpointManager = new(std::nothrow) WatchpointManager(fTeam,
|
|
fDebuggerInterface);
|
|
if (fWatchpointManager == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
error = fWatchpointManager->Init();
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// create the memory block manager
|
|
fMemoryBlockManager = new(std::nothrow) TeamMemoryBlockManager();
|
|
if (fMemoryBlockManager == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
error = fMemoryBlockManager->Init();
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// create the debug report generator
|
|
fReportGenerator = new(std::nothrow) DebugReportGenerator(fTeam, this,
|
|
fDebuggerInterface);
|
|
if (fReportGenerator == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
error = fReportGenerator->Init();
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
// set team debugging flags
|
|
fDebuggerInterface->SetTeamDebuggingFlags(
|
|
B_TEAM_DEBUG_THREADS | B_TEAM_DEBUG_IMAGES);
|
|
|
|
// get the initial state of the team
|
|
AutoLocker< ::Team> teamLocker(fTeam);
|
|
|
|
ThreadHandler* mainThreadHandler = NULL;
|
|
{
|
|
BObjectList<ThreadInfo> threadInfos(20, true);
|
|
status_t error = fDebuggerInterface->GetThreadInfos(threadInfos);
|
|
for (int32 i = 0; ThreadInfo* info = threadInfos.ItemAt(i); i++) {
|
|
::Thread* thread;
|
|
error = fTeam->AddThread(*info, &thread);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
ThreadHandler* handler = new(std::nothrow) ThreadHandler(thread,
|
|
fWorker, fDebuggerInterface,
|
|
fBreakpointManager);
|
|
if (handler == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
fThreadHandlers.Insert(handler);
|
|
|
|
if (thread->IsMainThread())
|
|
mainThreadHandler = handler;
|
|
|
|
handler->Init();
|
|
}
|
|
}
|
|
|
|
Image* appImage = NULL;
|
|
{
|
|
BObjectList<ImageInfo> imageInfos(20, true);
|
|
status_t error = fDebuggerInterface->GetImageInfos(imageInfos);
|
|
for (int32 i = 0; ImageInfo* info = imageInfos.ItemAt(i); i++) {
|
|
Image* image;
|
|
error = _AddImage(*info, &image);
|
|
if (error != B_OK)
|
|
return error;
|
|
if (image->Type() == B_APP_IMAGE)
|
|
appImage = image;
|
|
|
|
ImageDebugInfoRequested(image);
|
|
}
|
|
}
|
|
|
|
// create the debug event listener
|
|
char buffer[128];
|
|
snprintf(buffer, sizeof(buffer), "team %" B_PRId32 " debug listener",
|
|
fTeamID);
|
|
fDebugEventListener = spawn_thread(_DebugEventListenerEntry, buffer,
|
|
B_NORMAL_PRIORITY, this);
|
|
if (fDebugEventListener < 0)
|
|
return fDebugEventListener;
|
|
|
|
resume_thread(fDebugEventListener);
|
|
|
|
// run looper
|
|
thread_id looperThread = Run();
|
|
if (looperThread < 0)
|
|
return looperThread;
|
|
|
|
// init the UI
|
|
error = fUserInterface->Init(fTeam, this);
|
|
if (error != B_OK) {
|
|
ERROR("Error: Failed to init the UI: %s\n", strerror(error));
|
|
return error;
|
|
}
|
|
|
|
// if requested, stop the given thread
|
|
if (threadID >= 0) {
|
|
if (stopInMain) {
|
|
SymbolInfo symbolInfo;
|
|
if (appImage != NULL && mainThreadHandler != NULL
|
|
&& fDebuggerInterface->GetSymbolInfo(
|
|
fTeam->ID(), appImage->ID(), "main", B_SYMBOL_TYPE_TEXT,
|
|
symbolInfo) == B_OK) {
|
|
mainThreadHandler->SetBreakpointAndRun(symbolInfo.Address());
|
|
}
|
|
} else {
|
|
debug_thread(threadID);
|
|
// TODO: Superfluous, if the thread is already stopped.
|
|
}
|
|
}
|
|
|
|
fListener->TeamDebuggerStarted(this);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::Activate()
|
|
{
|
|
fUserInterface->Show();
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::MessageReceived(BMessage* message)
|
|
{
|
|
switch (message->what) {
|
|
case MSG_THREAD_RUN:
|
|
case MSG_THREAD_SET_ADDRESS:
|
|
case MSG_THREAD_STOP:
|
|
case MSG_THREAD_STEP_OVER:
|
|
case MSG_THREAD_STEP_INTO:
|
|
case MSG_THREAD_STEP_OUT:
|
|
{
|
|
int32 threadID;
|
|
target_addr_t address;
|
|
if (message->FindInt32("thread", &threadID) != B_OK)
|
|
break;
|
|
|
|
if (message->FindUInt64("address", &address) != B_OK)
|
|
address = 0;
|
|
|
|
if (ThreadHandler* handler = _GetThreadHandler(threadID)) {
|
|
handler->HandleThreadAction(message->what, address);
|
|
handler->ReleaseReference();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MSG_SET_BREAKPOINT:
|
|
case MSG_CLEAR_BREAKPOINT:
|
|
{
|
|
UserBreakpoint* breakpoint = NULL;
|
|
BReference<UserBreakpoint> breakpointReference;
|
|
uint64 address = 0;
|
|
|
|
if (message->FindPointer("breakpoint", (void**)&breakpoint) == B_OK)
|
|
breakpointReference.SetTo(breakpoint, true);
|
|
else if (message->FindUInt64("address", &address) != B_OK)
|
|
break;
|
|
|
|
if (message->what == MSG_SET_BREAKPOINT) {
|
|
bool enabled;
|
|
if (message->FindBool("enabled", &enabled) != B_OK)
|
|
enabled = true;
|
|
|
|
if (breakpoint != NULL)
|
|
_HandleSetUserBreakpoint(breakpoint, enabled);
|
|
else
|
|
_HandleSetUserBreakpoint(address, enabled);
|
|
} else {
|
|
if (breakpoint != NULL)
|
|
_HandleClearUserBreakpoint(breakpoint);
|
|
else
|
|
_HandleClearUserBreakpoint(address);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case MSG_SET_WATCHPOINT:
|
|
case MSG_CLEAR_WATCHPOINT:
|
|
{
|
|
Watchpoint* watchpoint = NULL;
|
|
BReference<Watchpoint> watchpointReference;
|
|
uint64 address = 0;
|
|
uint32 type = 0;
|
|
int32 length = 0;
|
|
|
|
if (message->FindPointer("watchpoint", (void**)&watchpoint)
|
|
== B_OK) {
|
|
watchpointReference.SetTo(watchpoint, true);
|
|
} else if (message->FindUInt64("address", &address) != B_OK)
|
|
break;
|
|
|
|
if (message->what == MSG_SET_WATCHPOINT) {
|
|
if (watchpoint == NULL && (message->FindUInt32("type", &type)
|
|
!= B_OK
|
|
|| message->FindInt32("length", &length) != B_OK)) {
|
|
break;
|
|
}
|
|
|
|
bool enabled;
|
|
if (message->FindBool("enabled", &enabled) != B_OK)
|
|
enabled = true;
|
|
|
|
if (watchpoint != NULL)
|
|
_HandleSetWatchpoint(watchpoint, enabled);
|
|
else
|
|
_HandleSetWatchpoint(address, type, length, enabled);
|
|
} else {
|
|
if (watchpoint != NULL)
|
|
_HandleClearWatchpoint(watchpoint);
|
|
else
|
|
_HandleClearWatchpoint(address);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case MSG_INSPECT_ADDRESS:
|
|
{
|
|
TeamMemoryBlock::Listener* listener;
|
|
if (message->FindPointer("listener",
|
|
reinterpret_cast<void **>(&listener)) != B_OK) {
|
|
break;
|
|
}
|
|
|
|
target_addr_t address;
|
|
if (message->FindUInt64("address",
|
|
&address) == B_OK) {
|
|
_HandleInspectAddress(address, listener);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MSG_GENERATE_DEBUG_REPORT:
|
|
{
|
|
fReportGenerator->PostMessage(message);
|
|
break;
|
|
}
|
|
|
|
case MSG_THREAD_STATE_CHANGED:
|
|
{
|
|
int32 threadID;
|
|
if (message->FindInt32("thread", &threadID) != B_OK)
|
|
break;
|
|
|
|
if (ThreadHandler* handler = _GetThreadHandler(threadID)) {
|
|
handler->HandleThreadStateChanged();
|
|
handler->ReleaseReference();
|
|
}
|
|
break;
|
|
}
|
|
case MSG_THREAD_CPU_STATE_CHANGED:
|
|
{
|
|
int32 threadID;
|
|
if (message->FindInt32("thread", &threadID) != B_OK)
|
|
break;
|
|
|
|
if (ThreadHandler* handler = _GetThreadHandler(threadID)) {
|
|
handler->HandleCpuStateChanged();
|
|
handler->ReleaseReference();
|
|
}
|
|
break;
|
|
}
|
|
case MSG_THREAD_STACK_TRACE_CHANGED:
|
|
{
|
|
int32 threadID;
|
|
if (message->FindInt32("thread", &threadID) != B_OK)
|
|
break;
|
|
|
|
if (ThreadHandler* handler = _GetThreadHandler(threadID)) {
|
|
handler->HandleStackTraceChanged();
|
|
handler->ReleaseReference();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case MSG_IMAGE_DEBUG_INFO_CHANGED:
|
|
{
|
|
int32 imageID;
|
|
if (message->FindInt32("image", &imageID) != B_OK)
|
|
break;
|
|
|
|
_HandleImageDebugInfoChanged(imageID);
|
|
break;
|
|
}
|
|
|
|
case MSG_IMAGE_FILE_CHANGED:
|
|
{
|
|
int32 imageID;
|
|
if (message->FindInt32("image", &imageID) != B_OK)
|
|
break;
|
|
|
|
_HandleImageFileChanged(imageID);
|
|
break;
|
|
}
|
|
|
|
case MSG_DEBUGGER_EVENT:
|
|
{
|
|
DebugEvent* event;
|
|
if (message->FindPointer("event", (void**)&event) != B_OK)
|
|
break;
|
|
|
|
_HandleDebuggerMessage(event);
|
|
delete event;
|
|
break;
|
|
}
|
|
|
|
case MSG_LOAD_SETTINGS:
|
|
_LoadSettings();
|
|
Activate();
|
|
break;
|
|
|
|
case MSG_TEAM_RESTART_REQUESTED:
|
|
{
|
|
if (fCommandLineArgc == 0)
|
|
break;
|
|
|
|
BString argumentString;
|
|
for (int i = 0; i < fCommandLineArgc; i++) {
|
|
if (i > 0)
|
|
argumentString.Append(" ");
|
|
argumentString.Append(fCommandLineArgv[i]);
|
|
}
|
|
|
|
BMessage startMessage(MSG_START_NEW_TEAM);
|
|
startMessage.AddString("path", fCommandLineArgv[0]);
|
|
startMessage.AddString("arguments", argumentString);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
BLooper::MessageReceived(message);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::SourceEntryLocateRequested(const char* sourcePath,
|
|
const char* locatedPath)
|
|
{
|
|
AutoLocker<FileManager> locker(fFileManager);
|
|
fFileManager->SourceEntryLocated(sourcePath, locatedPath);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::FunctionSourceCodeRequested(FunctionInstance* functionInstance)
|
|
{
|
|
Function* function = functionInstance->GetFunction();
|
|
|
|
// mark loading
|
|
AutoLocker< ::Team> locker(fTeam);
|
|
|
|
if (functionInstance->SourceCodeState() != FUNCTION_SOURCE_NOT_LOADED)
|
|
return;
|
|
if (function->SourceCodeState() == FUNCTION_SOURCE_LOADED)
|
|
return;
|
|
|
|
functionInstance->SetSourceCode(NULL, FUNCTION_SOURCE_LOADING);
|
|
|
|
bool loadForFunction = false;
|
|
if (function->SourceCodeState() == FUNCTION_SOURCE_NOT_LOADED) {
|
|
loadForFunction = true;
|
|
function->SetSourceCode(NULL, FUNCTION_SOURCE_LOADING);
|
|
}
|
|
|
|
locker.Unlock();
|
|
|
|
// schedule the job
|
|
if (fWorker->ScheduleJob(
|
|
new(std::nothrow) LoadSourceCodeJob(fDebuggerInterface,
|
|
fDebuggerInterface->GetArchitecture(), fTeam, functionInstance,
|
|
loadForFunction),
|
|
this) != B_OK) {
|
|
// scheduling failed -- mark unavailable
|
|
locker.Lock();
|
|
function->SetSourceCode(NULL, FUNCTION_SOURCE_UNAVAILABLE);
|
|
locker.Unlock();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::ImageDebugInfoRequested(Image* image)
|
|
{
|
|
LoadImageDebugInfoJob::ScheduleIfNecessary(fWorker, image);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::ValueNodeValueRequested(CpuState* cpuState,
|
|
ValueNodeContainer* container, ValueNode* valueNode)
|
|
{
|
|
AutoLocker<ValueNodeContainer> containerLocker(container);
|
|
if (valueNode->Container() != container)
|
|
return;
|
|
|
|
// check whether a job is already in progress
|
|
AutoLocker<Worker> workerLocker(fWorker);
|
|
SimpleJobKey jobKey(valueNode, JOB_TYPE_RESOLVE_VALUE_NODE_VALUE);
|
|
if (fWorker->GetJob(jobKey) != NULL)
|
|
return;
|
|
workerLocker.Unlock();
|
|
|
|
// schedule the job
|
|
status_t error = fWorker->ScheduleJob(
|
|
new(std::nothrow) ResolveValueNodeValueJob(fDebuggerInterface,
|
|
fDebuggerInterface->GetArchitecture(), cpuState,
|
|
fTeam->GetTeamTypeInformation(), container, valueNode), this);
|
|
if (error != B_OK) {
|
|
// scheduling failed -- set the value to invalid
|
|
valueNode->SetLocationAndValue(NULL, NULL, error);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::ThreadActionRequested(thread_id threadID,
|
|
uint32 action, target_addr_t address)
|
|
{
|
|
BMessage message(action);
|
|
message.AddInt32("thread", threadID);
|
|
message.AddUInt64("address", address);
|
|
PostMessage(&message);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::SetBreakpointRequested(target_addr_t address, bool enabled)
|
|
{
|
|
BMessage message(MSG_SET_BREAKPOINT);
|
|
message.AddUInt64("address", (uint64)address);
|
|
message.AddBool("enabled", enabled);
|
|
PostMessage(&message);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::SetBreakpointEnabledRequested(UserBreakpoint* breakpoint,
|
|
bool enabled)
|
|
{
|
|
BMessage message(MSG_SET_BREAKPOINT);
|
|
BReference<UserBreakpoint> breakpointReference(breakpoint);
|
|
if (message.AddPointer("breakpoint", breakpoint) == B_OK
|
|
&& message.AddBool("enabled", enabled) == B_OK
|
|
&& PostMessage(&message) == B_OK) {
|
|
breakpointReference.Detach();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::ClearBreakpointRequested(target_addr_t address)
|
|
{
|
|
BMessage message(MSG_CLEAR_BREAKPOINT);
|
|
message.AddUInt64("address", (uint64)address);
|
|
PostMessage(&message);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::ClearBreakpointRequested(UserBreakpoint* breakpoint)
|
|
{
|
|
BMessage message(MSG_CLEAR_BREAKPOINT);
|
|
BReference<UserBreakpoint> breakpointReference(breakpoint);
|
|
if (message.AddPointer("breakpoint", breakpoint) == B_OK
|
|
&& PostMessage(&message) == B_OK) {
|
|
breakpointReference.Detach();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::SetWatchpointRequested(target_addr_t address, uint32 type,
|
|
int32 length, bool enabled)
|
|
{
|
|
BMessage message(MSG_SET_WATCHPOINT);
|
|
message.AddUInt64("address", (uint64)address);
|
|
message.AddUInt32("type", type);
|
|
message.AddInt32("length", length);
|
|
message.AddBool("enabled", enabled);
|
|
PostMessage(&message);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::SetWatchpointEnabledRequested(Watchpoint* watchpoint,
|
|
bool enabled)
|
|
{
|
|
BMessage message(MSG_SET_WATCHPOINT);
|
|
BReference<Watchpoint> watchpointReference(watchpoint);
|
|
if (message.AddPointer("watchpoint", watchpoint) == B_OK
|
|
&& message.AddBool("enabled", enabled) == B_OK
|
|
&& PostMessage(&message) == B_OK) {
|
|
watchpointReference.Detach();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::ClearWatchpointRequested(target_addr_t address)
|
|
{
|
|
BMessage message(MSG_CLEAR_WATCHPOINT);
|
|
message.AddUInt64("address", (uint64)address);
|
|
PostMessage(&message);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::ClearWatchpointRequested(Watchpoint* watchpoint)
|
|
{
|
|
BMessage message(MSG_CLEAR_WATCHPOINT);
|
|
BReference<Watchpoint> watchpointReference(watchpoint);
|
|
if (message.AddPointer("watchpoint", watchpoint) == B_OK
|
|
&& PostMessage(&message) == B_OK) {
|
|
watchpointReference.Detach();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::InspectRequested(target_addr_t address,
|
|
TeamMemoryBlock::Listener *listener)
|
|
{
|
|
BMessage message(MSG_INSPECT_ADDRESS);
|
|
message.AddUInt64("address", address);
|
|
message.AddPointer("listener", listener);
|
|
PostMessage(&message);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::DebugReportRequested(entry_ref* targetPath)
|
|
{
|
|
BMessage message(MSG_GENERATE_DEBUG_REPORT);
|
|
message.AddRef("target", targetPath);
|
|
PostMessage(&message);
|
|
}
|
|
|
|
|
|
bool
|
|
TeamDebugger::UserInterfaceQuitRequested(QuitOption quitOption)
|
|
{
|
|
bool askUser = false;
|
|
switch (quitOption) {
|
|
case QUIT_OPTION_ASK_USER:
|
|
askUser = true;
|
|
break;
|
|
|
|
case QUIT_OPTION_ASK_KILL_TEAM:
|
|
fKillTeamOnQuit = true;
|
|
break;
|
|
|
|
case QUIT_OPTION_ASK_RESUME_TEAM:
|
|
break;
|
|
}
|
|
|
|
if (askUser) {
|
|
AutoLocker< ::Team> locker(fTeam);
|
|
BString name(fTeam->Name());
|
|
locker.Unlock();
|
|
|
|
BString message;
|
|
message << "What shall be done about the debugged team '";
|
|
message << name;
|
|
message << "'?";
|
|
|
|
name.Remove(0, name.FindLast('/') + 1);
|
|
|
|
BString killLabel("Kill ");
|
|
killLabel << name;
|
|
|
|
BString resumeLabel("Resume ");
|
|
resumeLabel << name;
|
|
|
|
int32 choice = fUserInterface->SynchronouslyAskUser("Quit Debugger",
|
|
message, killLabel, "Cancel", resumeLabel);
|
|
|
|
switch (choice) {
|
|
case 0:
|
|
fKillTeamOnQuit = true;
|
|
break;
|
|
case 1:
|
|
case -1:
|
|
return false;
|
|
case 2:
|
|
// Detach from the team and resume and stopped threads.
|
|
break;
|
|
}
|
|
}
|
|
|
|
PostMessage(B_QUIT_REQUESTED);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::JobDone(Job* job)
|
|
{
|
|
TRACE_JOBS("TeamDebugger::JobDone(%p)\n", job);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::JobFailed(Job* job)
|
|
{
|
|
TRACE_JOBS("TeamDebugger::JobFailed(%p)\n", job);
|
|
// TODO: notify user
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::JobAborted(Job* job)
|
|
{
|
|
TRACE_JOBS("TeamDebugger::JobAborted(%p)\n", job);
|
|
// TODO: For a stack frame source loader thread we should reset the
|
|
// loading state! Asynchronously due to locking order.
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::ThreadStateChanged(const ::Team::ThreadEvent& event)
|
|
{
|
|
BMessage message(MSG_THREAD_STATE_CHANGED);
|
|
message.AddInt32("thread", event.GetThread()->ID());
|
|
PostMessage(&message);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::ThreadCpuStateChanged(const ::Team::ThreadEvent& event)
|
|
{
|
|
BMessage message(MSG_THREAD_CPU_STATE_CHANGED);
|
|
message.AddInt32("thread", event.GetThread()->ID());
|
|
PostMessage(&message);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::ThreadStackTraceChanged(const ::Team::ThreadEvent& event)
|
|
{
|
|
BMessage message(MSG_THREAD_STACK_TRACE_CHANGED);
|
|
message.AddInt32("thread", event.GetThread()->ID());
|
|
PostMessage(&message);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::ImageDebugInfoChanged(const ::Team::ImageEvent& event)
|
|
{
|
|
BMessage message(MSG_IMAGE_DEBUG_INFO_CHANGED);
|
|
message.AddInt32("image", event.GetImage()->ID());
|
|
PostMessage(&message);
|
|
}
|
|
|
|
|
|
/*static*/ status_t
|
|
TeamDebugger::_DebugEventListenerEntry(void* data)
|
|
{
|
|
return ((TeamDebugger*)data)->_DebugEventListener();
|
|
}
|
|
|
|
|
|
status_t
|
|
TeamDebugger::_DebugEventListener()
|
|
{
|
|
while (!fTerminating) {
|
|
// get the next event
|
|
DebugEvent* event;
|
|
status_t error = fDebuggerInterface->GetNextDebugEvent(event);
|
|
if (error != B_OK)
|
|
break;
|
|
// TODO: Error handling!
|
|
|
|
if (event->Team() != fTeamID) {
|
|
TRACE_EVENTS("TeamDebugger for team %" B_PRId32 ": received event "
|
|
"from team %" B_PRId32 "!\n", fTeamID, event->Team());
|
|
continue;
|
|
}
|
|
|
|
BMessage message(MSG_DEBUGGER_EVENT);
|
|
if (message.AddPointer("event", event) != B_OK
|
|
|| PostMessage(&message) != B_OK) {
|
|
// TODO: Continue thread if necessary!
|
|
delete event;
|
|
}
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_HandleDebuggerMessage(DebugEvent* event)
|
|
{
|
|
TRACE_EVENTS("TeamDebugger::_HandleDebuggerMessage(): %" B_PRId32 "\n",
|
|
event->EventType());
|
|
|
|
bool handled = false;
|
|
|
|
ThreadHandler* handler = _GetThreadHandler(event->Thread());
|
|
BReference<ThreadHandler> handlerReference(handler);
|
|
|
|
switch (event->EventType()) {
|
|
case B_DEBUGGER_MESSAGE_THREAD_DEBUGGED:
|
|
TRACE_EVENTS("B_DEBUGGER_MESSAGE_THREAD_DEBUGGED: thread: %"
|
|
B_PRId32 "\n", event->Thread());
|
|
|
|
if (handler != NULL) {
|
|
handled = handler->HandleThreadDebugged(
|
|
dynamic_cast<ThreadDebuggedEvent*>(event));
|
|
}
|
|
break;
|
|
case B_DEBUGGER_MESSAGE_DEBUGGER_CALL:
|
|
TRACE_EVENTS("B_DEBUGGER_MESSAGE_DEBUGGER_CALL: thread: %" B_PRId32
|
|
"\n", event->Thread());
|
|
|
|
if (handler != NULL) {
|
|
handled = handler->HandleDebuggerCall(
|
|
dynamic_cast<DebuggerCallEvent*>(event));
|
|
}
|
|
break;
|
|
case B_DEBUGGER_MESSAGE_BREAKPOINT_HIT:
|
|
TRACE_EVENTS("B_DEBUGGER_MESSAGE_BREAKPOINT_HIT: thread: %" B_PRId32
|
|
"\n", event->Thread());
|
|
|
|
if (handler != NULL) {
|
|
handled = handler->HandleBreakpointHit(
|
|
dynamic_cast<BreakpointHitEvent*>(event));
|
|
}
|
|
break;
|
|
case B_DEBUGGER_MESSAGE_WATCHPOINT_HIT:
|
|
TRACE_EVENTS("B_DEBUGGER_MESSAGE_WATCHPOINT_HIT: thread: %" B_PRId32
|
|
"\n", event->Thread());
|
|
|
|
if (handler != NULL) {
|
|
handled = handler->HandleWatchpointHit(
|
|
dynamic_cast<WatchpointHitEvent*>(event));
|
|
}
|
|
break;
|
|
case B_DEBUGGER_MESSAGE_SINGLE_STEP:
|
|
TRACE_EVENTS("B_DEBUGGER_MESSAGE_SINGLE_STEP: thread: %" B_PRId32
|
|
"\n", event->Thread());
|
|
|
|
if (handler != NULL) {
|
|
handled = handler->HandleSingleStep(
|
|
dynamic_cast<SingleStepEvent*>(event));
|
|
}
|
|
break;
|
|
case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED:
|
|
TRACE_EVENTS("B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED: thread: %"
|
|
B_PRId32 "\n", event->Thread());
|
|
|
|
if (handler != NULL) {
|
|
handled = handler->HandleExceptionOccurred(
|
|
dynamic_cast<ExceptionOccurredEvent*>(event));
|
|
}
|
|
break;
|
|
// case B_DEBUGGER_MESSAGE_TEAM_CREATED:
|
|
//printf("B_DEBUGGER_MESSAGE_TEAM_CREATED: team: %ld\n", message.team_created.new_team);
|
|
// break;
|
|
case B_DEBUGGER_MESSAGE_TEAM_DELETED:
|
|
{
|
|
TRACE_EVENTS("B_DEBUGGER_MESSAGE_TEAM_DELETED: team: %" B_PRId32
|
|
"\n", event->Team());
|
|
TeamDeletedEvent* teamEvent
|
|
= dynamic_cast<TeamDeletedEvent*>(event);
|
|
handled = _HandleTeamDeleted(teamEvent);
|
|
break;
|
|
}
|
|
case B_DEBUGGER_MESSAGE_TEAM_EXEC:
|
|
TRACE_EVENTS("B_DEBUGGER_MESSAGE_TEAM_EXEC: team: %" B_PRId32 "\n",
|
|
event->Team());
|
|
// TODO: Handle!
|
|
break;
|
|
case B_DEBUGGER_MESSAGE_THREAD_CREATED:
|
|
{
|
|
ThreadCreatedEvent* threadEvent
|
|
= dynamic_cast<ThreadCreatedEvent*>(event);
|
|
TRACE_EVENTS("B_DEBUGGER_MESSAGE_THREAD_CREATED: thread: %" B_PRId32
|
|
"\n", threadEvent->NewThread());
|
|
handled = _HandleThreadCreated(threadEvent);
|
|
break;
|
|
}
|
|
case DEBUGGER_MESSAGE_THREAD_RENAMED:
|
|
{
|
|
ThreadRenamedEvent* threadEvent
|
|
= dynamic_cast<ThreadRenamedEvent*>(event);
|
|
TRACE_EVENTS("DEBUGGER_MESSAGE_THREAD_RENAMED: thread: %" B_PRId32
|
|
" (\"%s\")\n",
|
|
threadEvent->RenamedThread(), threadEvent->NewName());
|
|
handled = _HandleThreadRenamed(threadEvent);
|
|
break;
|
|
}
|
|
case DEBUGGER_MESSAGE_THREAD_PRIORITY_CHANGED:
|
|
{
|
|
ThreadPriorityChangedEvent* threadEvent
|
|
= dynamic_cast<ThreadPriorityChangedEvent*>(event);
|
|
TRACE_EVENTS("B_DEBUGGER_MESSAGE_THREAD_PRIORITY_CHANGED: thread:"
|
|
" %" B_PRId32 "\n", threadEvent->ChangedThread());
|
|
handled = _HandleThreadPriorityChanged(threadEvent);
|
|
break;
|
|
}
|
|
case B_DEBUGGER_MESSAGE_THREAD_DELETED:
|
|
TRACE_EVENTS("B_DEBUGGER_MESSAGE_THREAD_DELETED: thread: %" B_PRId32
|
|
"\n", event->Thread());
|
|
handled = _HandleThreadDeleted(
|
|
dynamic_cast<ThreadDeletedEvent*>(event));
|
|
break;
|
|
case B_DEBUGGER_MESSAGE_IMAGE_CREATED:
|
|
{
|
|
ImageCreatedEvent* imageEvent
|
|
= dynamic_cast<ImageCreatedEvent*>(event);
|
|
TRACE_EVENTS("B_DEBUGGER_MESSAGE_IMAGE_CREATED: image: \"%s\" "
|
|
"(%" B_PRId32 ")\n", imageEvent->GetImageInfo().Name().String(),
|
|
imageEvent->GetImageInfo().ImageID());
|
|
handled = _HandleImageCreated(imageEvent);
|
|
break;
|
|
}
|
|
case B_DEBUGGER_MESSAGE_IMAGE_DELETED:
|
|
{
|
|
ImageDeletedEvent* imageEvent
|
|
= dynamic_cast<ImageDeletedEvent*>(event);
|
|
TRACE_EVENTS("B_DEBUGGER_MESSAGE_IMAGE_DELETED: image: \"%s\" "
|
|
"(%" B_PRId32 ")\n", imageEvent->GetImageInfo().Name().String(),
|
|
imageEvent->GetImageInfo().ImageID());
|
|
handled = _HandleImageDeleted(imageEvent);
|
|
break;
|
|
}
|
|
case B_DEBUGGER_MESSAGE_PRE_SYSCALL:
|
|
case B_DEBUGGER_MESSAGE_POST_SYSCALL:
|
|
case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED:
|
|
case B_DEBUGGER_MESSAGE_PROFILER_UPDATE:
|
|
case B_DEBUGGER_MESSAGE_HANDED_OVER:
|
|
// not interested
|
|
break;
|
|
default:
|
|
WARNING("TeamDebugger for team %" B_PRId32 ": unknown event type: "
|
|
"%" B_PRId32 "\n", fTeamID, event->EventType());
|
|
break;
|
|
}
|
|
|
|
if (!handled && event->ThreadStopped())
|
|
fDebuggerInterface->ContinueThread(event->Thread());
|
|
}
|
|
|
|
|
|
bool
|
|
TeamDebugger::_HandleTeamDeleted(TeamDeletedEvent* event)
|
|
{
|
|
char message[64];
|
|
snprintf(message, sizeof(message), "Team %" B_PRId32 " has terminated. ",
|
|
event->Team());
|
|
|
|
int32 result = fUserInterface->SynchronouslyAskUser("Team terminated",
|
|
message, "Do nothing", "Quit", fCommandLineArgc != 0
|
|
? "Restart team" : NULL);
|
|
|
|
if (result == 1)
|
|
PostMessage(B_QUIT_REQUESTED);
|
|
else if (result == 2) {
|
|
_SaveSettings();
|
|
fListener->TeamDebuggerRestartRequested(this);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool
|
|
TeamDebugger::_HandleThreadCreated(ThreadCreatedEvent* event)
|
|
{
|
|
AutoLocker< ::Team> locker(fTeam);
|
|
|
|
ThreadInfo info;
|
|
status_t error = fDebuggerInterface->GetThreadInfo(event->NewThread(),
|
|
info);
|
|
if (error == B_OK) {
|
|
::Thread* thread;
|
|
fTeam->AddThread(info, &thread);
|
|
|
|
ThreadHandler* handler = new(std::nothrow) ThreadHandler(thread,
|
|
fWorker, fDebuggerInterface,
|
|
fBreakpointManager);
|
|
if (handler != NULL) {
|
|
fThreadHandlers.Insert(handler);
|
|
handler->Init();
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool
|
|
TeamDebugger::_HandleThreadRenamed(ThreadRenamedEvent* event)
|
|
{
|
|
AutoLocker< ::Team> locker(fTeam);
|
|
|
|
::Thread* thread = fTeam->ThreadByID(event->RenamedThread());
|
|
|
|
if (thread != NULL)
|
|
thread->SetName(event->NewName());
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool
|
|
TeamDebugger::_HandleThreadPriorityChanged(ThreadPriorityChangedEvent*)
|
|
{
|
|
// TODO: implement once we actually track thread priorities
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool
|
|
TeamDebugger::_HandleThreadDeleted(ThreadDeletedEvent* event)
|
|
{
|
|
AutoLocker< ::Team> locker(fTeam);
|
|
if (ThreadHandler* handler = fThreadHandlers.Lookup(event->Thread())) {
|
|
fThreadHandlers.Remove(handler);
|
|
handler->ReleaseReference();
|
|
}
|
|
fTeam->RemoveThread(event->Thread());
|
|
return false;
|
|
}
|
|
|
|
|
|
bool
|
|
TeamDebugger::_HandleImageCreated(ImageCreatedEvent* event)
|
|
{
|
|
AutoLocker< ::Team> locker(fTeam);
|
|
_AddImage(event->GetImageInfo());
|
|
|
|
ImageInfoPendingThread* info = new(std::nothrow) ImageInfoPendingThread(
|
|
event->GetImageInfo().ImageID(), event->Thread());
|
|
if (info == NULL)
|
|
return false;
|
|
|
|
fImageInfoPendingThreads->Insert(info);
|
|
return true;
|
|
}
|
|
|
|
|
|
bool
|
|
TeamDebugger::_HandleImageDeleted(ImageDeletedEvent* event)
|
|
{
|
|
AutoLocker< ::Team> locker(fTeam);
|
|
fTeam->RemoveImage(event->GetImageInfo().ImageID());
|
|
|
|
ImageHandler* imageHandler = fImageHandlers->Lookup(
|
|
event->GetImageInfo().ImageID());
|
|
if (imageHandler == NULL)
|
|
return false;
|
|
|
|
fImageHandlers->Remove(imageHandler);
|
|
BReference<ImageHandler> imageHandlerReference(imageHandler, true);
|
|
locker.Unlock();
|
|
|
|
// remove breakpoints in the image
|
|
fBreakpointManager->RemoveImageBreakpoints(imageHandler->GetImage());
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_HandleImageDebugInfoChanged(image_id imageID)
|
|
{
|
|
// get the image (via the image handler)
|
|
AutoLocker< ::Team> locker(fTeam);
|
|
ImageHandler* imageHandler = fImageHandlers->Lookup(imageID);
|
|
if (imageHandler == NULL)
|
|
return;
|
|
|
|
Image* image = imageHandler->GetImage();
|
|
BReference<Image> imageReference(image);
|
|
|
|
locker.Unlock();
|
|
|
|
image_debug_info_state state = image->ImageDebugInfoState();
|
|
if (state == IMAGE_DEBUG_INFO_LOADED
|
|
|| state == IMAGE_DEBUG_INFO_UNAVAILABLE) {
|
|
// update breakpoints in the image
|
|
fBreakpointManager->UpdateImageBreakpoints(image);
|
|
|
|
ImageInfoPendingThread* thread = fImageInfoPendingThreads
|
|
->Lookup(imageID);
|
|
if (thread != NULL) {
|
|
fDebuggerInterface->ContinueThread(thread->ThreadID());
|
|
fImageInfoPendingThreads->Remove(thread);
|
|
delete thread;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_HandleImageFileChanged(image_id imageID)
|
|
{
|
|
TRACE_IMAGES("TeamDebugger::_HandleImageFileChanged(%" B_PRId32 ")\n",
|
|
imageID);
|
|
// TODO: Reload the debug info!
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_HandleSetUserBreakpoint(target_addr_t address, bool enabled)
|
|
{
|
|
TRACE_CONTROL("TeamDebugger::_HandleSetUserBreakpoint(%#" B_PRIx64
|
|
", %d)\n", address, enabled);
|
|
|
|
// check whether there already is a breakpoint
|
|
AutoLocker< ::Team> locker(fTeam);
|
|
|
|
Breakpoint* breakpoint = fTeam->BreakpointAtAddress(address);
|
|
UserBreakpoint* userBreakpoint = NULL;
|
|
if (breakpoint != NULL && breakpoint->FirstUserBreakpoint() != NULL)
|
|
userBreakpoint = breakpoint->FirstUserBreakpoint()->GetUserBreakpoint();
|
|
BReference<UserBreakpoint> userBreakpointReference(userBreakpoint);
|
|
|
|
if (userBreakpoint == NULL) {
|
|
TRACE_CONTROL(" no breakpoint yet\n");
|
|
|
|
// get the function at the address
|
|
Image* image = fTeam->ImageByAddress(address);
|
|
|
|
TRACE_CONTROL(" image: %p\n", image);
|
|
|
|
if (image == NULL)
|
|
return;
|
|
ImageDebugInfo* imageDebugInfo = image->GetImageDebugInfo();
|
|
|
|
TRACE_CONTROL(" image debug info: %p\n", imageDebugInfo);
|
|
|
|
if (imageDebugInfo == NULL)
|
|
return;
|
|
// TODO: Handle this case by loading the debug info, if possible!
|
|
FunctionInstance* functionInstance
|
|
= imageDebugInfo->FunctionAtAddress(address);
|
|
|
|
TRACE_CONTROL(" function instance: %p\n", functionInstance);
|
|
|
|
if (functionInstance == NULL)
|
|
return;
|
|
Function* function = functionInstance->GetFunction();
|
|
|
|
TRACE_CONTROL(" function: %p\n", function);
|
|
|
|
// get the source location for the address
|
|
FunctionDebugInfo* functionDebugInfo
|
|
= functionInstance->GetFunctionDebugInfo();
|
|
SourceLocation sourceLocation;
|
|
Statement* breakpointStatement = NULL;
|
|
if (functionDebugInfo->GetSpecificImageDebugInfo()->GetStatement(
|
|
functionDebugInfo, address, breakpointStatement) != B_OK) {
|
|
return;
|
|
}
|
|
|
|
sourceLocation = breakpointStatement->StartSourceLocation();
|
|
breakpointStatement->ReleaseReference();
|
|
|
|
target_addr_t relativeAddress = address - functionInstance->Address();
|
|
|
|
TRACE_CONTROL(" relative address: %#" B_PRIx64 ", source location: "
|
|
"(%" B_PRId32 ", %" B_PRId32 ")\n", relativeAddress,
|
|
sourceLocation.Line(), sourceLocation.Column());
|
|
|
|
// get function id
|
|
FunctionID* functionID = functionInstance->GetFunctionID();
|
|
if (functionID == NULL)
|
|
return;
|
|
BReference<FunctionID> functionIDReference(functionID, true);
|
|
|
|
// create the user breakpoint
|
|
userBreakpoint = new(std::nothrow) UserBreakpoint(
|
|
UserBreakpointLocation(functionID, function->SourceFile(),
|
|
sourceLocation, relativeAddress));
|
|
if (userBreakpoint == NULL)
|
|
return;
|
|
userBreakpointReference.SetTo(userBreakpoint, true);
|
|
|
|
TRACE_CONTROL(" created user breakpoint: %p\n", userBreakpoint);
|
|
|
|
// iterate through all function instances and create
|
|
// UserBreakpointInstances
|
|
for (FunctionInstanceList::ConstIterator it
|
|
= function->Instances().GetIterator();
|
|
FunctionInstance* instance = it.Next();) {
|
|
TRACE_CONTROL(" function instance %p: range: %#" B_PRIx64 " - %#"
|
|
B_PRIx64 "\n", instance, instance->Address(),
|
|
instance->Address() + instance->Size());
|
|
|
|
// get the breakpoint address for the instance
|
|
target_addr_t instanceAddress = 0;
|
|
if (instance == functionInstance) {
|
|
instanceAddress = address;
|
|
} else if (functionInstance->SourceFile() != NULL) {
|
|
// We have a source file, so get the address for the source
|
|
// location.
|
|
Statement* statement = NULL;
|
|
functionDebugInfo = instance->GetFunctionDebugInfo();
|
|
functionDebugInfo->GetSpecificImageDebugInfo()
|
|
->GetStatementAtSourceLocation(functionDebugInfo,
|
|
sourceLocation, statement);
|
|
if (statement != NULL) {
|
|
instanceAddress = statement->CoveringAddressRange().Start();
|
|
// TODO: What about BreakpointAllowed()?
|
|
statement->ReleaseReference();
|
|
}
|
|
}
|
|
|
|
TRACE_CONTROL(" breakpoint address using source info: %" B_PRIx64
|
|
"\n", instanceAddress);
|
|
|
|
if (instanceAddress == 0) {
|
|
// No source file (or we failed getting the statement), so try
|
|
// to use the same relative address.
|
|
if (relativeAddress > instance->Size())
|
|
continue;
|
|
instanceAddress = instance->Address() + relativeAddress;
|
|
}
|
|
|
|
TRACE_CONTROL(" final breakpoint address: %" B_PRIx64 "\n",
|
|
instanceAddress);
|
|
|
|
UserBreakpointInstance* breakpointInstance = new(std::nothrow)
|
|
UserBreakpointInstance(userBreakpoint, instanceAddress);
|
|
if (breakpointInstance == NULL
|
|
|| !userBreakpoint->AddInstance(breakpointInstance)) {
|
|
delete breakpointInstance;
|
|
return;
|
|
}
|
|
|
|
TRACE_CONTROL(" breakpoint instance: %p\n", breakpointInstance);
|
|
}
|
|
}
|
|
|
|
locker.Unlock();
|
|
|
|
_HandleSetUserBreakpoint(userBreakpoint, enabled);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_HandleSetUserBreakpoint(UserBreakpoint* breakpoint, bool enabled)
|
|
{
|
|
status_t error = fBreakpointManager->InstallUserBreakpoint(breakpoint,
|
|
enabled);
|
|
if (error != B_OK) {
|
|
_NotifyUser("Install Breakpoint", "Failed to install breakpoint: %s",
|
|
strerror(error));
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_HandleClearUserBreakpoint(target_addr_t address)
|
|
{
|
|
TRACE_CONTROL("TeamDebugger::_HandleClearUserBreakpoint(%#" B_PRIx64 ")\n",
|
|
address);
|
|
|
|
AutoLocker< ::Team> locker(fTeam);
|
|
|
|
Breakpoint* breakpoint = fTeam->BreakpointAtAddress(address);
|
|
if (breakpoint == NULL || breakpoint->FirstUserBreakpoint() == NULL)
|
|
return;
|
|
UserBreakpoint* userBreakpoint
|
|
= breakpoint->FirstUserBreakpoint()->GetUserBreakpoint();
|
|
BReference<UserBreakpoint> userBreakpointReference(userBreakpoint);
|
|
|
|
locker.Unlock();
|
|
|
|
_HandleClearUserBreakpoint(userBreakpoint);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_HandleClearUserBreakpoint(UserBreakpoint* breakpoint)
|
|
{
|
|
fBreakpointManager->UninstallUserBreakpoint(breakpoint);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_HandleSetWatchpoint(target_addr_t address, uint32 type,
|
|
int32 length, bool enabled)
|
|
{
|
|
Watchpoint* watchpoint = new(std::nothrow) Watchpoint(address, type,
|
|
length);
|
|
|
|
if (watchpoint == NULL)
|
|
return;
|
|
BReference<Watchpoint> watchpointRef(watchpoint, true);
|
|
|
|
_HandleSetWatchpoint(watchpoint, enabled);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_HandleSetWatchpoint(Watchpoint* watchpoint, bool enabled)
|
|
{
|
|
status_t error = fWatchpointManager->InstallWatchpoint(watchpoint,
|
|
enabled);
|
|
if (error != B_OK) {
|
|
_NotifyUser("Install Watchpoint", "Failed to install watchpoint: %s",
|
|
strerror(error));
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_HandleClearWatchpoint(target_addr_t address)
|
|
{
|
|
TRACE_CONTROL("TeamDebugger::_HandleClearWatchpoint(%#" B_PRIx64 ")\n",
|
|
address);
|
|
|
|
AutoLocker< ::Team> locker(fTeam);
|
|
|
|
Watchpoint* watchpoint = fTeam->WatchpointAtAddress(address);
|
|
if (watchpoint == NULL)
|
|
return;
|
|
BReference<Watchpoint> watchpointReference(watchpoint);
|
|
|
|
locker.Unlock();
|
|
|
|
_HandleClearWatchpoint(watchpoint);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_HandleClearWatchpoint(Watchpoint* watchpoint)
|
|
{
|
|
fWatchpointManager->UninstallWatchpoint(watchpoint);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_HandleInspectAddress(target_addr_t address,
|
|
TeamMemoryBlock::Listener* listener)
|
|
{
|
|
TRACE_CONTROL("TeamDebugger::_HandleInspectAddress(%" B_PRIx64 ", %p)\n",
|
|
address, listener);
|
|
|
|
TeamMemoryBlock* memoryBlock = fMemoryBlockManager
|
|
->GetMemoryBlock(address);
|
|
|
|
if (memoryBlock == NULL) {
|
|
_NotifyUser("Inspect Address", "Failed to allocate memory block");
|
|
return;
|
|
}
|
|
|
|
if (!memoryBlock->HasListener(listener))
|
|
memoryBlock->AddListener(listener);
|
|
|
|
if (!memoryBlock->IsValid()) {
|
|
AutoLocker< ::Team> teamLocker(fTeam);
|
|
|
|
TeamMemory* memory = fTeam->GetTeamMemory();
|
|
// schedule the job
|
|
status_t result;
|
|
if ((result = fWorker->ScheduleJob(
|
|
new(std::nothrow) RetrieveMemoryBlockJob(fTeam, memory,
|
|
memoryBlock),
|
|
this)) != B_OK) {
|
|
memoryBlock->ReleaseReference();
|
|
_NotifyUser("Inspect Address", "Failed to retrieve memory data: %s",
|
|
strerror(result));
|
|
}
|
|
} else
|
|
memoryBlock->NotifyDataRetrieved();
|
|
|
|
}
|
|
|
|
|
|
status_t
|
|
TeamDebugger::_HandleSetArguments(int argc, const char* const* argv)
|
|
{
|
|
fCommandLineArgc = argc;
|
|
fCommandLineArgv = new(std::nothrow) const char*[argc];
|
|
if (fCommandLineArgv == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
memset(const_cast<char **>(fCommandLineArgv), 0, sizeof(char*) * argc);
|
|
|
|
for (int i = 0; i < argc; i++) {
|
|
fCommandLineArgv[i] = strdup(argv[i]);
|
|
if (fCommandLineArgv[i] == NULL)
|
|
return B_NO_MEMORY;
|
|
}
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
ThreadHandler*
|
|
TeamDebugger::_GetThreadHandler(thread_id threadID)
|
|
{
|
|
AutoLocker< ::Team> locker(fTeam);
|
|
|
|
ThreadHandler* handler = fThreadHandlers.Lookup(threadID);
|
|
if (handler != NULL)
|
|
handler->AcquireReference();
|
|
return handler;
|
|
}
|
|
|
|
|
|
status_t
|
|
TeamDebugger::_AddImage(const ImageInfo& imageInfo, Image** _image)
|
|
{
|
|
LocatableFile* file = NULL;
|
|
if (strchr(imageInfo.Name(), '/') != NULL)
|
|
file = fFileManager->GetTargetFile(imageInfo.Name());
|
|
BReference<LocatableFile> imageFileReference(file, true);
|
|
|
|
Image* image;
|
|
status_t error = fTeam->AddImage(imageInfo, file, &image);
|
|
if (error != B_OK)
|
|
return error;
|
|
|
|
ImageDebugInfoRequested(image);
|
|
|
|
ImageHandler* imageHandler = new(std::nothrow) ImageHandler(this, image);
|
|
if (imageHandler != NULL)
|
|
fImageHandlers->Insert(imageHandler);
|
|
|
|
if (_image != NULL)
|
|
*_image = image;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_LoadSettings()
|
|
{
|
|
// get the team name
|
|
AutoLocker< ::Team> locker(fTeam);
|
|
BString teamName = fTeam->Name();
|
|
locker.Unlock();
|
|
|
|
// load the settings
|
|
if (fSettingsManager->LoadTeamSettings(teamName, fTeamSettings) != B_OK)
|
|
return;
|
|
|
|
// create the saved breakpoints
|
|
for (int32 i = 0; const BreakpointSetting* breakpointSetting
|
|
= fTeamSettings.BreakpointAt(i); i++) {
|
|
if (breakpointSetting->GetFunctionID() == NULL)
|
|
continue;
|
|
|
|
// get the source file, if any
|
|
LocatableFile* sourceFile = NULL;
|
|
if (breakpointSetting->SourceFile().Length() > 0) {
|
|
sourceFile = fFileManager->GetSourceFile(
|
|
breakpointSetting->SourceFile());
|
|
if (sourceFile == NULL)
|
|
continue;
|
|
}
|
|
BReference<LocatableFile> sourceFileReference(sourceFile, true);
|
|
|
|
// create the breakpoint
|
|
UserBreakpointLocation location(breakpointSetting->GetFunctionID(),
|
|
sourceFile, breakpointSetting->GetSourceLocation(),
|
|
breakpointSetting->RelativeAddress());
|
|
|
|
UserBreakpoint* breakpoint = new(std::nothrow) UserBreakpoint(location);
|
|
if (breakpoint == NULL)
|
|
return;
|
|
BReference<UserBreakpoint> breakpointReference(breakpoint, true);
|
|
|
|
// install it
|
|
fBreakpointManager->InstallUserBreakpoint(breakpoint,
|
|
breakpointSetting->IsEnabled());
|
|
}
|
|
|
|
const TeamUiSettings* uiSettings = fTeamSettings.UiSettingFor(
|
|
fUserInterface->ID());
|
|
if (uiSettings != NULL)
|
|
fUserInterface->LoadSettings(uiSettings);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_SaveSettings()
|
|
{
|
|
// get the settings
|
|
AutoLocker< ::Team> locker(fTeam);
|
|
TeamSettings settings;
|
|
if (settings.SetTo(fTeam) != B_OK)
|
|
return;
|
|
|
|
TeamUiSettings* uiSettings = NULL;
|
|
if (fUserInterface->SaveSettings(uiSettings) != B_OK)
|
|
return;
|
|
if (uiSettings != NULL)
|
|
settings.AddUiSettings(uiSettings);
|
|
|
|
// preserve the UI settings from our cached copy.
|
|
for (int32 i = 0; i < fTeamSettings.CountUiSettings(); i++) {
|
|
const TeamUiSettings* oldUiSettings = fTeamSettings.UiSettingAt(i);
|
|
if (strcmp(oldUiSettings->ID(), fUserInterface->ID()) != 0) {
|
|
TeamUiSettings* clonedSettings = oldUiSettings->Clone();
|
|
if (clonedSettings != NULL)
|
|
settings.AddUiSettings(clonedSettings);
|
|
}
|
|
}
|
|
locker.Unlock();
|
|
|
|
// save the settings
|
|
fSettingsManager->SaveTeamSettings(settings);
|
|
}
|
|
|
|
|
|
void
|
|
TeamDebugger::_NotifyUser(const char* title, const char* text,...)
|
|
{
|
|
// print the message
|
|
char buffer[1024];
|
|
va_list args;
|
|
va_start(args, text);
|
|
vsnprintf(buffer, sizeof(buffer), text, args);
|
|
va_end(args);
|
|
|
|
// notify the user
|
|
fUserInterface->NotifyUser(title, buffer, USER_NOTIFICATION_WARNING);
|
|
}
|
|
|
|
|
|
// #pragma mark - Listener
|
|
|
|
|
|
TeamDebugger::Listener::~Listener()
|
|
{
|
|
}
|