/*
* Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2010-2017, Rene Gollent, rene@gollent.com.
* Distributed under the terms of the MIT License.
*/
#include "controllers/TeamDebugger.h"
#include <stdarg.h>
#include <stdio.h>
#include <new>
#include <Entry.h>
#include <InterfaceDefs.h>
#include <Message.h>
#include <StringList.h>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include "debug_utils.h"
#include "syscall_numbers.h"
#include "Architecture.h"
#include "BreakpointManager.h"
#include "BreakpointSetting.h"
#include "CpuState.h"
#include "DebugEvent.h"
#include "DebuggerInterface.h"
#include "DebugReportGenerator.h"
#include "ExpressionInfo.h"
#include "FileManager.h"
#include "Function.h"
#include "FunctionID.h"
#include "ImageDebugInfo.h"
#include "ImageDebugInfoLoadingState.h"
#include "ImageDebugLoadingStateHandler.h"
#include "ImageDebugLoadingStateHandlerRoster.h"
#include "Jobs.h"
#include "LocatableFile.h"
#include "MessageCodes.h"
#include "NoOpSettingsManager.h"
#include "SettingsManager.h"
#include "SourceCode.h"
#include "SourceLanguage.h"
#include "SpecificImageDebugInfo.h"
#include "SpecificImageDebugInfoLoadingState.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 "TeamSignalSettings.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),
fIsPostMortem(false),
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),
fExecPending(false)
{
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 fWorker;
delete fImageInfoPendingThreads;
delete fBreakpointManager;
delete fWatchpointManager;
delete fMemoryBlockManager;
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(DebuggerInterface* interface, 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);
status_t error = _HandleSetArguments(argc, argv);
if (error != B_OK)
return error;
if (fSettingsManager == NULL) {
// if we have not been provided with a settings manager,
// simply use the no-op manager by default.
fSettingsManager = new(std::nothrow) NoOpSettingsManager;
if (fSettingsManager == NULL)
return B_NO_MEMORY;
}
fDebuggerInterface = interface;
fDebuggerInterface->AcquireReference();
fTeamID = interface->TeamID();
fIsPostMortem = interface->IsPostMortem();
// 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, true);
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
| B_TEAM_DEBUG_POST_SYSCALL | B_TEAM_DEBUG_SIGNALS
| B_TEAM_DEBUG_TEAM_CREATION);
// 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, this, 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 (for live debugging only)
if (!fDebuggerInterface->IsPostMortem()) {
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 && !fDebuggerInterface->IsPostMortem()) {
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;
bool hidden;
if (message->FindBool("hidden", &hidden) != B_OK)
hidden = false;
if (breakpoint != NULL)
_HandleSetUserBreakpoint(breakpoint, enabled);
else
_HandleSetUserBreakpoint(address, enabled, hidden);
} else {
if (breakpoint != NULL)
_HandleClearUserBreakpoint(breakpoint);
else
_HandleClearUserBreakpoint(address);
}
break;
}
case MSG_SET_BREAKPOINT_CONDITION:
{
UserBreakpoint* breakpoint = NULL;
BReference<UserBreakpoint> breakpointReference;
if (message->FindPointer("breakpoint", (void**)&breakpoint)
!= B_OK) {
break;
}
breakpointReference.SetTo(breakpoint, true);
const char* condition;
if (message->FindString("condition", &condition) != B_OK)
break;
AutoLocker< ::Team> teamLocker(fTeam);
breakpoint->SetCondition(condition);
fTeam->NotifyUserBreakpointChanged(breakpoint);
break;
}
case MSG_CLEAR_BREAKPOINT_CONDITION:
{
UserBreakpoint* breakpoint = NULL;
BReference<UserBreakpoint> breakpointReference;
if (message->FindPointer("breakpoint", (void**)&breakpoint)
!= B_OK)
break;
breakpointReference.SetTo(breakpoint, true);
AutoLocker< ::Team> teamLocker(fTeam);
breakpoint->SetCondition(NULL);
fTeam->NotifyUserBreakpointChanged(breakpoint);
break;
}
case MSG_STOP_ON_IMAGE_LOAD:
{
bool enabled;
bool useNames;
if (message->FindBool("enabled", &enabled) != B_OK)
break;
if (message->FindBool("useNames", &useNames) != B_OK)
break;
AutoLocker< ::Team> teamLocker(fTeam);
fTeam->SetStopOnImageLoad(enabled, useNames);
break;
}
case MSG_ADD_STOP_IMAGE_NAME:
{
BString imageName;
if (message->FindString("name", &imageName) != B_OK)
break;
AutoLocker< ::Team> teamLocker(fTeam);
fTeam->AddStopImageName(imageName);
break;
}
case MSG_REMOVE_STOP_IMAGE_NAME:
{
BString imageName;
if (message->FindString("name", &imageName) != B_OK)
break;
AutoLocker< ::Team> teamLocker(fTeam);
fTeam->RemoveStopImageName(imageName);
break;
}
case MSG_SET_DEFAULT_SIGNAL_DISPOSITION:
{
int32 disposition;
if (message->FindInt32("disposition", &disposition) != B_OK)
break;
AutoLocker< ::Team> teamLocker(fTeam);
fTeam->SetDefaultSignalDisposition(disposition);
break;
}
case MSG_SET_CUSTOM_SIGNAL_DISPOSITION:
{
int32 signal;
int32 disposition;
if (message->FindInt32("signal", &signal) != B_OK
|| message->FindInt32("disposition", &disposition) != B_OK) {
break;
}
AutoLocker< ::Team> teamLocker(fTeam);
fTeam->SetCustomSignalDisposition(signal, disposition);
break;
}
case MSG_REMOVE_CUSTOM_SIGNAL_DISPOSITION:
{
int32 signal;
if (message->FindInt32("signal", &signal) != B_OK)
break;
AutoLocker< ::Team> teamLocker(fTeam);
fTeam->RemoveCustomSignalDisposition(signal);
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_WRITE_TARGET_MEMORY:
{
target_addr_t address;
if (message->FindUInt64("address", &address) != B_OK)
break;
void* data;
if (message->FindPointer("data", &data) != B_OK)
break;
target_size_t size;
if (message->FindUInt64("size", &size) != B_OK)
break;
_HandleWriteMemory(address, data, size);
break;
}
case MSG_EVALUATE_EXPRESSION:
{
SourceLanguage* language;
if (message->FindPointer("language",
reinterpret_cast<void**>(&language)) != B_OK) {
break;
}
// ExpressionEvaluationRequested() acquires a reference
// to both the language and the expression info on our behalf.
BReference<SourceLanguage> reference(language, true);
ExpressionInfo* info;
if (message->FindPointer("info",
reinterpret_cast<void**>(&info)) != B_OK) {
break;
}
BReference<ExpressionInfo> infoReference(info, true);
StackFrame* frame;
if (message->FindPointer("frame",
reinterpret_cast<void**>(&frame)) != B_OK) {
// the stack frame isn't needed, unless variable
// evaluation is desired.
frame = NULL;
}
::Thread* thread;
if (message->FindPointer("thread",
reinterpret_cast<void**>(&thread)) != B_OK) {
// the thread isn't needed, unless variable
// evaluation is desired.
thread = NULL;
}
_HandleEvaluateExpression(language, info, frame, thread);
break;
}
case MSG_GENERATE_DEBUG_REPORT:
{
fReportGenerator->PostMessage(message);
break;
}
case MSG_WRITE_CORE_FILE:
{
entry_ref ref;
if (message->FindRef("target", &ref) != B_OK)
break;
_HandleWriteCoreFile(ref);
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;
_SaveSettings();
fListener->TeamDebuggerRestartRequested(this);
break;
}
case MSG_DEBUG_INFO_NEEDS_USER_INPUT:
{
Job* job;
ImageDebugInfoLoadingState* state;
if (message->FindPointer("job", (void**)&job) != B_OK)
break;
if (message->FindPointer("state", (void**)&state) != B_OK)
break;
_HandleDebugInfoJobUserInput(state);
fWorker->ResumeJob(job);
break;
}
case MSG_RESET_USER_BACKGROUND_STATUS:
{
fUserInterface->NotifyBackgroundWorkStatus("Ready.");
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::SourceEntryInvalidateRequested(LocatableFile* sourceFile)
{
AutoLocker< ::Team> locker(fTeam);
fTeam->DebugInfo()->ClearSourceCode(sourceFile);
}
void
TeamDebugger::FunctionSourceCodeRequested(FunctionInstance* functionInstance,
bool forceDisassembly)
{
Function* function = functionInstance->GetFunction();
// mark loading
AutoLocker< ::Team> locker(fTeam);
if (forceDisassembly && functionInstance->SourceCodeState()
!= FUNCTION_SOURCE_NOT_LOADED) {
return;
} else if (!forceDisassembly && function->SourceCodeState()
== FUNCTION_SOURCE_LOADED) {
return;
}
functionInstance->SetSourceCode(NULL, FUNCTION_SOURCE_LOADING);
bool loadForFunction = false;
if (!forceDisassembly && (function->SourceCodeState()
== FUNCTION_SOURCE_NOT_LOADED
|| function->SourceCodeState() == FUNCTION_SOURCE_SUPPRESSED)) {
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, this);
}
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::ValueNodeWriteRequested(ValueNode* node, CpuState* state,
Value* newValue)
{
// schedule the job
status_t error = fWorker->ScheduleJob(
new(std::nothrow) WriteValueNodeValueJob(fDebuggerInterface,
fDebuggerInterface->GetArchitecture(), state,
fTeam->GetTeamTypeInformation(), node, newValue), this);
if (error != B_OK) {
BString message;
message.SetToFormat("Request to write new value for variable %s "
"failed: %s.\n", node->Name().String(), strerror(error));
fUserInterface->NotifyUser("Error", message.String(),
USER_NOTIFICATION_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,
bool hidden)
{
BMessage message(MSG_SET_BREAKPOINT);
message.AddUInt64("address", (uint64)address);
message.AddBool("enabled", enabled);
message.AddBool("hidden", hidden);
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::SetBreakpointConditionRequested(UserBreakpoint* breakpoint,
const char* condition)
{
BMessage message(MSG_SET_BREAKPOINT_CONDITION);
BReference<UserBreakpoint> breakpointReference(breakpoint);
if (message.AddPointer("breakpoint", breakpoint) == B_OK
&& message.AddString("condition", condition) == B_OK
&& PostMessage(&message) == B_OK) {
breakpointReference.Detach();
}
}
void
TeamDebugger::ClearBreakpointConditionRequested(UserBreakpoint* breakpoint)
{
BMessage message(MSG_CLEAR_BREAKPOINT_CONDITION);
BReference<UserBreakpoint> breakpointReference(breakpoint);
if (message.AddPointer("breakpoint", breakpoint) == 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::SetStopOnImageLoadRequested(bool enabled, bool useImageNames)
{
BMessage message(MSG_STOP_ON_IMAGE_LOAD);
message.AddBool("enabled", enabled);
message.AddBool("useNames", useImageNames);
PostMessage(&message);
}
void
TeamDebugger::AddStopImageNameRequested(const char* name)
{
BMessage message(MSG_ADD_STOP_IMAGE_NAME);
message.AddString("name", name);
PostMessage(&message);
}
void
TeamDebugger::RemoveStopImageNameRequested(const char* name)
{
BMessage message(MSG_REMOVE_STOP_IMAGE_NAME);
message.AddString("name", name);
PostMessage(&message);
}
void
TeamDebugger::SetDefaultSignalDispositionRequested(int32 disposition)
{
BMessage message(MSG_SET_DEFAULT_SIGNAL_DISPOSITION);
message.AddInt32("disposition", disposition);
PostMessage(&message);
}
void
TeamDebugger::SetCustomSignalDispositionRequested(int32 signal,
int32 disposition)
{
BMessage message(MSG_SET_CUSTOM_SIGNAL_DISPOSITION);
message.AddInt32("signal", signal);
message.AddInt32("disposition", disposition);
PostMessage(&message);
}
void
TeamDebugger::RemoveCustomSignalDispositionRequested(int32 signal)
{
BMessage message(MSG_REMOVE_CUSTOM_SIGNAL_DISPOSITION);
message.AddInt32("signal", signal);
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::MemoryWriteRequested(target_addr_t address, const void* data,
target_size_t size)
{
BMessage message(MSG_WRITE_TARGET_MEMORY);
message.AddUInt64("address", address);
message.AddPointer("data", data);
message.AddUInt64("size", size);
PostMessage(&message);
}
void
TeamDebugger::ExpressionEvaluationRequested(SourceLanguage* language,
ExpressionInfo* info, StackFrame* frame, ::Thread* thread)
{
BMessage message(MSG_EVALUATE_EXPRESSION);
message.AddPointer("language", language);
message.AddPointer("info", info);
if (frame != NULL)
message.AddPointer("frame", frame);
if (thread != NULL)
message.AddPointer("thread", thread);
BReference<SourceLanguage> languageReference(language);
BReference<ExpressionInfo> infoReference(info);
if (PostMessage(&message) == B_OK) {
languageReference.Detach();
infoReference.Detach();
}
}
void
TeamDebugger::DebugReportRequested(entry_ref* targetPath)
{
BMessage message(MSG_GENERATE_DEBUG_REPORT);
message.AddRef("target", targetPath);
PostMessage(&message);
}
void
TeamDebugger::WriteCoreFileRequested(entry_ref* targetPath)
{
BMessage message(MSG_WRITE_CORE_FILE);
message.AddRef("target", targetPath);
PostMessage(&message);
}
void
TeamDebugger::TeamRestartRequested()
{
PostMessage(MSG_TEAM_RESTART_REQUESTED);
}
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::JobStarted(Job* job)
{
BString description(job->GetDescription());
if (!description.IsEmpty()) {
description.Append(B_UTF8_ELLIPSIS);
fUserInterface->NotifyBackgroundWorkStatus(description.String());
}
}
void
TeamDebugger::JobDone(Job* job)
{
TRACE_JOBS("TeamDebugger::JobDone(%p)\n", job);
_ResetUserBackgroundStatusIfNeeded();
}
void
TeamDebugger::JobWaitingForInput(Job* job)
{
LoadImageDebugInfoJob* infoJob = dynamic_cast<LoadImageDebugInfoJob*>(job);
if (infoJob == NULL)
return;
BMessage message(MSG_DEBUG_INFO_NEEDS_USER_INPUT);
message.AddPointer("job", infoJob);
message.AddPointer("state", infoJob->GetLoadingState());
PostMessage(&message);
}
void
TeamDebugger::JobFailed(Job* job)
{
TRACE_JOBS("TeamDebugger::JobFailed(%p)\n", job);
// TODO: notify user
_ResetUserBackgroundStatusIfNeeded();
}
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.
_ResetUserBackgroundStatusIfNeeded();
}
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, true);
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());
TeamExecEvent* teamEvent
= dynamic_cast<TeamExecEvent*>(event);
_PrepareForTeamExec(teamEvent);
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_POST_SYSCALL:
{
PostSyscallEvent* postSyscallEvent
= dynamic_cast<PostSyscallEvent*>(event);
TRACE_EVENTS("B_DEBUGGER_MESSAGE_POST_SYSCALL: syscall: %"
B_PRIu32 "\n", postSyscallEvent->GetSyscallInfo().Syscall());
handled = _HandlePostSyscall(postSyscallEvent);
// if a thread was blocked in a syscall when we requested to
// stop it for debugging, then that request will interrupt
// said call, and the post syscall event will be all we get
// in response. Consequently, we need to treat this case as
// equivalent to having received a thread debugged event.
AutoLocker< ::Team> teamLocker(fTeam);
::Thread* thread = fTeam->ThreadByID(event->Thread());
if (handler != NULL && thread != NULL
&& thread->StopRequestPending()) {
handled = handler->HandleThreadDebugged(NULL);
}
break;
}
case B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED:
{
TRACE_EVENTS("B_DEBUGGER_MESSAGE_SIGNAL_RECEIVED: thread: %"
B_PRId32 "\n", event->Thread());
if (handler != NULL) {
handled = handler->HandleSignalReceived(
dynamic_cast<SignalReceivedEvent*>(event));
}
break;
}
case B_DEBUGGER_MESSAGE_PRE_SYSCALL:
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];
fDebuggerInterface->Close(false);
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);
switch (result) {
case 1:
case -1:
{
PostMessage(B_QUIT_REQUESTED);
break;
}
case 2:
{
_SaveSettings();
fListener->TeamDebuggerRestartRequested(this);
break;
}
default:
break;
}
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, this, 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;
}
bool
TeamDebugger::_HandlePostSyscall(PostSyscallEvent* event)
{
const SyscallInfo& info = event->GetSyscallInfo();
switch (info.Syscall()) {
case SYSCALL_WRITE:
{
if ((ssize_t)info.ReturnValue() <= 0)
break;
int32 fd;
target_addr_t address;
size_t size;
// TODO: decoding the syscall arguments should probably be
// factored out into an Architecture method of its own, since
// there's no guarantee the target architecture has the same
// endianness as the host. This could re-use the syscall
// argument parser that strace uses, though that would need to
// be adapted to handle the aforementioned endian differences.
// This works for x86{-64} for now though.
if (fTeam->GetArchitecture()->AddressSize() == 4) {
const uint32* args = (const uint32*)info.Arguments();
fd = args[0];
address = args[3];
size = args[4];
} else {
const uint64* args = (const uint64*)info.Arguments();
fd = args[0];
address = args[2];
size = args[3];
}
if (fd == 1 || fd == 2) {
BString data;
ssize_t result = fDebuggerInterface->ReadMemoryString(
address, size, data);
if (result >= 0)
fTeam->NotifyConsoleOutputReceived(fd, data);
}
break;
}
case SYSCALL_WRITEV:
{
// TODO: handle
}
default:
break;
}
return false;
}
void
TeamDebugger::_PrepareForTeamExec(TeamExecEvent* event)
{
// NB: must be called with team lock held.
_SaveSettings();
// when notified of exec, we need to clear out data related
// to the old team.
const ImageList& images = fTeam->Images();
for (ImageList::ConstIterator it = images.GetIterator();
Image* image = it.Next();) {
fBreakpointManager->RemoveImageBreakpoints(image);
}
BObjectList<UserBreakpoint> breakpointsToRemove(20, false);
const UserBreakpointList& breakpoints = fTeam->UserBreakpoints();
for (UserBreakpointList::ConstIterator it = breakpoints.GetIterator();
UserBreakpoint* breakpoint = it.Next();) {
breakpointsToRemove.AddItem(breakpoint);
breakpoint->AcquireReference();
}
for (int32 i = 0; i < breakpointsToRemove.CountItems(); i++) {
UserBreakpoint* breakpoint = breakpointsToRemove.ItemAt(i);
fTeam->RemoveUserBreakpoint(breakpoint);
fTeam->NotifyUserBreakpointChanged(breakpoint);
breakpoint->ReleaseReference();
}
fTeam->ClearImages();
fTeam->ClearSignalDispositionMappings();
fExecPending = true;
}
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);
image_debug_info_state state = image->ImageDebugInfoState();
bool handlePostExecSetup = fExecPending && image->Type() == B_APP_IMAGE
&& state != IMAGE_DEBUG_INFO_LOADING;
// this needs to be done first so that breakpoints are loaded.
// otherwise, UpdateImageBreakpoints() won't find the appropriate
// UserBreakpoints to create/install instances for.
if (handlePostExecSetup) {
fTeam->SetName(image->Name());
_LoadSettings();
fExecPending = false;
}
locker.Unlock();
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) {
fImageInfoPendingThreads->Remove(thread);
ObjectDeleter<ImageInfoPendingThread> threadDeleter(thread);
locker.Lock();
ThreadHandler* handler = _GetThreadHandler(thread->ThreadID());
BReference<ThreadHandler> handlerReference(handler, true);
if (fTeam->StopOnImageLoad()) {
bool stop = true;
const BString& imageName = image->Name();
// only match on the image filename itself
const char* rawImageName = imageName.String()
+ imageName.FindLast('/') + 1;
if (fTeam->StopImageNameListEnabled()) {
const BStringList& nameList = fTeam->StopImageNames();
stop = nameList.HasString(rawImageName);
}
if (stop && handler != NULL) {
BString stopReason;
stopReason.SetToFormat("Image '%s' loaded.",
rawImageName);
locker.Unlock();
if (handler->HandleThreadDebugged(NULL, stopReason))
return;
} else
locker.Unlock();
} else if (handlePostExecSetup) {
// in the case of an exec(), we can't stop in main() until
// the new app image has been loaded, so we know where to
// set the main breakpoint at.
SymbolInfo symbolInfo;
if (fDebuggerInterface->GetSymbolInfo(fTeam->ID(), image->ID(),
"main", B_SYMBOL_TYPE_TEXT, symbolInfo) == B_OK) {
handler->SetBreakpointAndRun(symbolInfo.Address());
}
} else {
locker.Unlock();
fDebuggerInterface->ContinueThread(thread->ThreadID());
}
}
}
}
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,
bool hidden)
{
TRACE_CONTROL("TeamDebugger::_HandleSetUserBreakpoint(%#" B_PRIx64
", %d, %d)\n", address, enabled, hidden);
// 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);
userBreakpoint->SetHidden(hidden);
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->IsValid()) {
AutoLocker< ::Team> teamLocker(fTeam);
if (!memoryBlock->HasListener(listener))
memoryBlock->AddListener(listener);
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->NotifyDataRetrieved(result);
memoryBlock->ReleaseReference();
_NotifyUser("Inspect Address", "Failed to retrieve memory data: %s",
strerror(result));
}
} else
memoryBlock->NotifyDataRetrieved();
}
void
TeamDebugger::_HandleWriteMemory(target_addr_t address, void* data,
target_size_t size)
{
TRACE_CONTROL("TeamDebugger::_HandleWriteTargetMemory(%" B_PRIx64 ", %p, "
"%" B_PRIu64 ")\n", address, data, size);
AutoLocker< ::Team> teamLocker(fTeam);
TeamMemory* memory = fTeam->GetTeamMemory();
// schedule the job
status_t result;
if ((result = fWorker->ScheduleJob(
new(std::nothrow) WriteMemoryJob(fTeam, memory, address, data, size),
this)) != B_OK) {
_NotifyUser("Write Memory", "Failed to write memory data: %s",
strerror(result));
}
}
void
TeamDebugger::_HandleEvaluateExpression(SourceLanguage* language,
ExpressionInfo* info, StackFrame* frame, ::Thread* thread)
{
status_t result = fWorker->ScheduleJob(
new(std::nothrow) ExpressionEvaluationJob(fTeam, fDebuggerInterface,
language, info, frame, thread));
if (result != B_OK) {
_NotifyUser("Evaluate Expression", "Failed to evaluate expression: %s",
strerror(result));
}
}
void
TeamDebugger::_HandleWriteCoreFile(const entry_ref& targetPath)
{
status_t result = fWorker->ScheduleJob(
new(std::nothrow) WriteCoreFileJob(fTeam, fDebuggerInterface,
targetPath));
if (result != B_OK) {
_NotifyUser("Write Core File", "Failed to write core file: %s",
strerror(result));
}
}
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;
}
void
TeamDebugger::_HandleDebugInfoJobUserInput(ImageDebugInfoLoadingState* state)
{
SpecificImageDebugInfoLoadingState* specificState
= state->GetSpecificDebugInfoLoadingState();
ImageDebugLoadingStateHandler* handler;
if (ImageDebugLoadingStateHandlerRoster::Default()
->FindStateHandler(specificState, handler) != B_OK) {
TRACE_JOBS("TeamDebugger::_HandleDebugInfoJobUserInput(): "
"Failed to find appropriate information handler, aborting.");
return;
}
handler->HandleState(specificState, fUserInterface);
}
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);
breakpoint->SetHidden(breakpointSetting->IsHidden());
breakpoint->SetCondition(breakpointSetting->Condition());
// install it
fBreakpointManager->InstallUserBreakpoint(breakpoint,
breakpointSetting->IsEnabled());
}
fFileManager->LoadLocationMappings(fTeamSettings.FileManagerSettings());
const TeamUiSettings* uiSettings = fTeamSettings.UiSettingFor(
fUserInterface->ID());
if (uiSettings != NULL)
fUserInterface->LoadSettings(uiSettings);
const TeamSignalSettings* signalSettings = fTeamSettings.SignalSettings();
if (signalSettings != NULL) {
fTeam->SetDefaultSignalDisposition(
signalSettings->DefaultSignalDisposition());
int32 signal;
int32 disposition;
for (int32 i = 0; i < signalSettings->CountCustomSignalDispositions();
i++) {
if (signalSettings->GetCustomSignalDispositionAt(i, signal,
disposition) == B_OK) {
fTeam->SetCustomSignalDisposition(signal, disposition);
}
}
}
}
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);
}
}
fFileManager->SaveLocationMappings(settings.FileManagerSettings());
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);
}
void
TeamDebugger::_ResetUserBackgroundStatusIfNeeded()
{
if (!fTerminating && !fWorker->HasPendingJobs())
PostMessage(MSG_RESET_USER_BACKGROUND_STATUS);
}
// #pragma mark - Listener
TeamDebugger::Listener::~Listener()
{
}
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fNext.
↑ V730 Not all members of a class are initialized inside the constructor. Consider inspecting: fTeamDebugInfo.