From 15f040e596308c36903c8381112ef04280b95963 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Fri, 19 Jun 2009 22:13:32 +0000 Subject: [PATCH] Some work towards getting stack traces: * Added a virtual Architecture::CreateStackTrace() and added a basic implementation in ArchitectureX86. Fleshed out StackTrace/StackFrame a bit and added StackFrameX86. This needs to be organized differently, though, so that we can get the maximum available information for each stack frame, depending on what info is available for the respective function. * Added job to get the stack trace for a thread. * Added stack trace related handling in TeamDebugger. Reorganized the thread state/CPU state/stack trace change handling a bit -- we're using a Team::Listener now, and do things asynchronously. * Added a StackTraceView to display the stack trace of the current thread. No function name available yet, otherwise working fine. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31126 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/debugger/Debugger.cpp | 18 +- src/apps/debugger/Jamfile | 3 + src/apps/debugger/Jobs.cpp | 63 ++++++ src/apps/debugger/Jobs.h | 24 +- src/apps/debugger/TeamDebugger.cpp | 129 +++++++++-- src/apps/debugger/TeamDebugger.h | 19 +- src/apps/debugger/arch/Architecture.h | 5 + src/apps/debugger/arch/Register.h | 2 +- src/apps/debugger/arch/StackFrame.h | 11 + .../debugger/arch/x86/ArchitectureX86.cpp | 65 +++++- src/apps/debugger/arch/x86/ArchitectureX86.h | 2 + src/apps/debugger/arch/x86/CpuStateX86.cpp | 7 + src/apps/debugger/arch/x86/CpuStateX86.h | 1 + src/apps/debugger/arch/x86/StackFrameX86.cpp | 76 +++++++ src/apps/debugger/arch/x86/StackFrameX86.h | 41 ++++ .../gui/team_window/StackTraceView.cpp | 212 ++++++++++++++++++ .../debugger/gui/team_window/StackTraceView.h | 40 ++++ .../debugger/gui/team_window/TeamWindow.cpp | 43 +++- .../debugger/gui/team_window/TeamWindow.h | 3 + 19 files changed, 727 insertions(+), 37 deletions(-) create mode 100644 src/apps/debugger/arch/x86/StackFrameX86.cpp create mode 100644 src/apps/debugger/arch/x86/StackFrameX86.h create mode 100644 src/apps/debugger/gui/team_window/StackTraceView.cpp create mode 100644 src/apps/debugger/gui/team_window/StackTraceView.h diff --git a/src/apps/debugger/Debugger.cpp b/src/apps/debugger/Debugger.cpp index ac662cd986..a21741af8a 100644 --- a/src/apps/debugger/Debugger.cpp +++ b/src/apps/debugger/Debugger.cpp @@ -13,6 +13,8 @@ #include #include +#include + #include "debug_utils.h" #include "TeamDebugger.h" @@ -216,7 +218,7 @@ printf("loading program: \"%s\" ...\n", options.commandLineArgv[0]); stopInMain = true; } - // If we've got + // If we've got if (team < 0) { printf("no team yet, getting thread info...\n"); thread_info threadInfo; @@ -245,12 +247,10 @@ printf("There's already a debugger for team: %ld\n", team); fprintf(stderr, "Error: Out of memory!\n"); } - if (debugger->Init(team, thread, stopInMain) == B_OK) -{ + if (debugger->Init(team, thread, stopInMain) == B_OK + && fTeamDebuggers.AddItem(debugger)) { printf("debugger for team %ld created and initialized successfully!\n", team); - fTeamDebuggers.Add(debugger); -} - else + } else delete debugger; } @@ -261,13 +261,13 @@ printf("debugger for team %ld created and initialized successfully!\n", team); } private: - typedef DoublyLinkedList TeamDebuggerList; + typedef BObjectList TeamDebuggerList; private: TeamDebugger* _TeamDebuggerForTeam(team_id teamID) const { - for (TeamDebuggerList::ConstIterator it = fTeamDebuggers.GetIterator(); - TeamDebugger* debugger = it.Next();) { + for (int32 i = 0; TeamDebugger* debugger = fTeamDebuggers.ItemAt(i); + i++) { if (debugger->TeamID() == teamID) return debugger; } diff --git a/src/apps/debugger/Jamfile b/src/apps/debugger/Jamfile index 1c312e606a..1a45a595c1 100644 --- a/src/apps/debugger/Jamfile +++ b/src/apps/debugger/Jamfile @@ -41,6 +41,7 @@ Application Debugger : # arch/x86 ArchitectureX86.cpp CpuStateX86.cpp + StackFrameX86.cpp # debugger_interface DebugEvent.cpp @@ -48,6 +49,8 @@ Application Debugger : # gui/team_window ImageListView.cpp + RegisterView.cpp + StackTraceView.cpp TeamWindow.cpp ThreadListView.cpp diff --git a/src/apps/debugger/Jobs.cpp b/src/apps/debugger/Jobs.cpp index b4f01a3b3d..2fe12efd2e 100644 --- a/src/apps/debugger/Jobs.cpp +++ b/src/apps/debugger/Jobs.cpp @@ -7,12 +7,17 @@ #include +#include "Architecture.h" #include "CpuState.h" #include "DebuggerInterface.h" +#include "StackTrace.h" #include "Team.h" #include "Thread.h" +// #pragma mark - GetCpuStateJob + + GetCpuStateJob::GetCpuStateJob(DebuggerInterface* debuggerInterface, Thread* thread) : @@ -52,3 +57,61 @@ GetCpuStateJob::Do() return B_OK; } + + +// #pragma mark - GetStackTraceJob + + +GetStackTraceJob::GetStackTraceJob(DebuggerInterface* debuggerInterface, + Architecture* architecture, Thread* thread) + : + fDebuggerInterface(debuggerInterface), + fArchitecture(architecture), + fThread(thread) +{ + fThread->AddReference(); + + fCpuState = fThread->GetCpuState(); + if (fCpuState != NULL) + fCpuState->AddReference(); +} + + +GetStackTraceJob::~GetStackTraceJob() +{ + if (fCpuState != NULL) + fCpuState->RemoveReference(); + + fThread->RemoveReference(); +} + + +JobKey +GetStackTraceJob::Key() const +{ + return JobKey(fThread, JOB_TYPE_GET_CPU_STATE); +} + + +status_t +GetStackTraceJob::Do() +{ + if (fCpuState == NULL) + return B_BAD_VALUE; + + // get the stack trace + StackTrace* stackTrace; + status_t error = fArchitecture->CreateStackTrace(fThread->GetTeam(), + fCpuState, stackTrace); + if (error != B_OK) + return error; + Reference stackTraceReference(stackTrace, true); + + // set the stack trace, unless something has changed + AutoLocker locker(fThread->GetTeam()); + + if (fThread->GetCpuState() == fCpuState) + fThread->SetStackTrace(stackTrace); + + return B_OK; +} diff --git a/src/apps/debugger/Jobs.h b/src/apps/debugger/Jobs.h index b494d74c79..37ae58d8cb 100644 --- a/src/apps/debugger/Jobs.h +++ b/src/apps/debugger/Jobs.h @@ -8,13 +8,16 @@ #include "Worker.h" +class Architecture; +class CpuState; class DebuggerInterface; class Thread; // job types enum { - JOB_TYPE_GET_CPU_STATE + JOB_TYPE_GET_CPU_STATE, + JOB_TYPE_GET_STACK_TRACE }; @@ -34,4 +37,23 @@ private: }; +class GetStackTraceJob : public Job { +public: + GetStackTraceJob( + DebuggerInterface* debuggerInterface, + Architecture* architecture, + Thread* thread); + virtual ~GetStackTraceJob(); + + virtual JobKey Key() const; + virtual status_t Do(); + +private: + DebuggerInterface* fDebuggerInterface; + Architecture* fArchitecture; + Thread* fThread; + CpuState* fCpuState; +}; + + #endif // JOBS_H diff --git a/src/apps/debugger/TeamDebugger.cpp b/src/apps/debugger/TeamDebugger.cpp index 0195870e53..ffcd05cbe2 100644 --- a/src/apps/debugger/TeamDebugger.cpp +++ b/src/apps/debugger/TeamDebugger.cpp @@ -19,7 +19,6 @@ #include "DebuggerInterface.h" #include "Jobs.h" #include "MessageCodes.h" -#include "Team.h" #include "TeamDebugModel.h" @@ -81,6 +80,8 @@ TeamDebugger::Init(team_id teamID, thread_id threadID, bool stopInMain) fTeam->SetName(teamInfo.args); // TODO: Set a better name! + fTeam->AddListener(this); + // create our worker fWorker = new(std::nothrow) Worker; if (fWorker == NULL) @@ -196,6 +197,35 @@ TeamDebugger::MessageReceived(BMessage* message) _HandleThreadAction(threadID, message->what); break; } + + case MSG_THREAD_STATE_CHANGED: + { + int32 threadID; + if (message->FindInt32("thread", &threadID) != B_OK) + break; + + _HandleThreadStateChanged(threadID); + break; + } + case MSG_THREAD_CPU_STATE_CHANGED: + { + int32 threadID; + if (message->FindInt32("thread", &threadID) != B_OK) + break; + + _HandleCpuStateChanged(threadID); + break; + } + case MSG_THREAD_STACK_TRACE_CHANGED: + { + int32 threadID; + if (message->FindInt32("thread", &threadID) != B_OK) + break; + + _HandleStackTraceChanged(threadID); + break; + } + default: BLooper::MessageReceived(message); break; @@ -242,6 +272,33 @@ printf("TeamDebugger::JobAborted(%p)\n", job); } +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); +} + + /*static*/ status_t TeamDebugger::_DebugEventListenerEntry(void* data) { @@ -481,24 +538,8 @@ void TeamDebugger::_SetThreadState(::Thread* thread, uint32 state, CpuState* cpuState) { - // update the thread state - uint32 oldState = thread->State(); thread->SetState(state); - - // cancel jobs for this thread - if (oldState == THREAD_STATE_STOPPED) - fWorker->AbortJob(JobKey(thread, JOB_TYPE_GET_CPU_STATE)); - - if (state == THREAD_STATE_STOPPED) { - if (cpuState != NULL) { - thread->SetCpuState(cpuState); - } else { - // trigger updating the CPU state - fWorker->ScheduleJob(new(std::nothrow) GetCpuStateJob( - fDebuggerInterface, thread), - this); - } - } + thread->SetCpuState(cpuState); } @@ -550,3 +591,55 @@ printf("MSG_THREAD_STEP_OUT\n"); // TODO: Handle stepping correctly! } } + + +void +TeamDebugger::_HandleThreadStateChanged(thread_id threadID) +{ + AutoLocker< ::Team> teamLocker(fTeam); + + ::Thread* thread = fTeam->ThreadByID(threadID); + if (thread == NULL) + return; + + // cancel jobs for this thread + fWorker->AbortJob(JobKey(thread, JOB_TYPE_GET_CPU_STATE)); + fWorker->AbortJob(JobKey(thread, JOB_TYPE_GET_STACK_TRACE)); + + // If the thread is stopped and has no CPU state yet, schedule a job. + if (thread->State() == THREAD_STATE_STOPPED + && thread->GetCpuState() == NULL) { + fWorker->ScheduleJob( + new(std::nothrow) GetCpuStateJob(fDebuggerInterface, thread), + this); + } +} + + +void +TeamDebugger::_HandleCpuStateChanged(thread_id threadID) +{ + AutoLocker< ::Team> teamLocker(fTeam); + + ::Thread* thread = fTeam->ThreadByID(threadID); + if (thread == NULL) + return; + + // cancel stack trace job for this thread + fWorker->AbortJob(JobKey(thread, JOB_TYPE_GET_STACK_TRACE)); + + // If the thread has a CPU state, but no stack trace yet, schedule a job. + if (thread->GetCpuState() != NULL && thread->GetStackTrace() == NULL) { + fWorker->ScheduleJob( + new(std::nothrow) GetStackTraceJob(fDebuggerInterface, + fDebuggerInterface->GetArchitecture(), thread), + this); + } +} + + +void +TeamDebugger::_HandleStackTraceChanged(thread_id threadID) +{ +printf("TeamDebugger::_HandleStackTraceChanged()\n"); +} diff --git a/src/apps/debugger/TeamDebugger.h b/src/apps/debugger/TeamDebugger.h index cac5f0934a..9f5b41c781 100644 --- a/src/apps/debugger/TeamDebugger.h +++ b/src/apps/debugger/TeamDebugger.h @@ -9,20 +9,19 @@ #include #include -#include #include "DebugEvent.h" +#include "Team.h" #include "TeamWindow.h" #include "Worker.h" class DebuggerInterface; -class Team; class TeamDebugModel; -class TeamDebugger : public DoublyLinkedListLinkImpl, - private BLooper, private TeamWindow::Listener, private JobListener { +class TeamDebugger : private BLooper, private TeamWindow::Listener, + private JobListener, private Team::Listener { public: TeamDebugger(); ~TeamDebugger(); @@ -45,6 +44,14 @@ private: virtual void JobFailed(Job* job); virtual void JobAborted(Job* job); + // Team::Listener + virtual void ThreadStateChanged( + const ::Team::ThreadEvent& event); + virtual void ThreadCpuStateChanged( + const ::Team::ThreadEvent& event); + virtual void ThreadStackTraceChanged( + const ::Team::ThreadEvent& event); + private: static status_t _DebugEventListenerEntry(void* data); status_t _DebugEventListener(); @@ -82,6 +89,10 @@ private: void _HandleThreadAction(thread_id threadID, uint32 action); + void _HandleThreadStateChanged(thread_id threadID); + void _HandleCpuStateChanged(thread_id threadID); + void _HandleStackTraceChanged(thread_id threadID); + private: ::Team* fTeam; TeamDebugModel* fDebugModel; diff --git a/src/apps/debugger/arch/Architecture.h b/src/apps/debugger/arch/Architecture.h index 692fdca476..64adbb3a99 100644 --- a/src/apps/debugger/arch/Architecture.h +++ b/src/apps/debugger/arch/Architecture.h @@ -13,6 +13,8 @@ class CpuState; class DebuggerInterface; class Register; +class StackTrace; +class Team; class Architecture : public Referenceable { @@ -28,6 +30,9 @@ public: virtual status_t CreateCpuState(const void* cpuStateData, size_t size, CpuState*& _state) = 0; + virtual status_t CreateStackTrace(Team* team, CpuState* cpuState, + StackTrace*& _stackTrace) = 0; + // team is not locked protected: DebuggerInterface* fDebuggerInterface; diff --git a/src/apps/debugger/arch/Register.h b/src/apps/debugger/arch/Register.h index a6b621895b..e69e700780 100644 --- a/src/apps/debugger/arch/Register.h +++ b/src/apps/debugger/arch/Register.h @@ -14,7 +14,7 @@ enum register_format { }; enum register_type { - REGISTER_TYPE_PROGRAM_COUNTER, + REGISTER_TYPE_INSTRUCTION_POINTER, REGISTER_TYPE_STACK_POINTER, REGISTER_TYPE_GENERAL_PURPOSE, REGISTER_TYPE_SPECIAL_PURPOSE, diff --git a/src/apps/debugger/arch/StackFrame.h b/src/apps/debugger/arch/StackFrame.h index f600dc4a2a..4017b18808 100644 --- a/src/apps/debugger/arch/StackFrame.h +++ b/src/apps/debugger/arch/StackFrame.h @@ -13,6 +13,14 @@ #include "ArchitectureTypes.h" +enum stack_frame_type { + STACK_FRAME_TYPE_TOP, // top-most frame + STACK_FRAME_TYPE_STANDARD, // non-top-most standard frame + STACK_FRAME_TYPE_SIGNAL, // signal handler frame + STACK_FRAME_TYPE_FRAMELESS // dummy frame for a frameless function +}; + + class CpuState; @@ -21,8 +29,11 @@ class StackFrame : public Referenceable, public: virtual ~StackFrame(); + virtual stack_frame_type Type() const = 0; + virtual CpuState* GetCpuState() const = 0; + virtual target_addr_t InstructionPointer() const = 0; virtual target_addr_t FrameAddress() const = 0; virtual target_addr_t ReturnAddress() const = 0; virtual target_addr_t PreviousFrameAddress() const = 0; diff --git a/src/apps/debugger/arch/x86/ArchitectureX86.cpp b/src/apps/debugger/arch/x86/ArchitectureX86.cpp index 5b00983c22..289a7df385 100644 --- a/src/apps/debugger/arch/x86/ArchitectureX86.cpp +++ b/src/apps/debugger/arch/x86/ArchitectureX86.cpp @@ -7,7 +7,12 @@ #include +#include + #include "CpuStateX86.h" +#include "DebuggerInterface.h" +#include "StackFrameX86.h" +#include "StackTrace.h" ArchitectureX86::ArchitectureX86(DebuggerInterface* debuggerInterface) @@ -27,7 +32,7 @@ ArchitectureX86::Init() { try { _AddIntegerRegister(X86_REGISTER_EIP, "eip", 32, - REGISTER_TYPE_PROGRAM_COUNTER); + REGISTER_TYPE_INSTRUCTION_POINTER); _AddIntegerRegister(X86_REGISTER_ESP, "esp", 32, REGISTER_TYPE_STACK_POINTER); _AddIntegerRegister(X86_REGISTER_EBP, "ebp", 32, @@ -98,6 +103,64 @@ ArchitectureX86::CreateCpuState(const void* cpuStateData, size_t size, } +status_t +ArchitectureX86::CreateStackTrace(Team* team, CpuState* _cpuState, + StackTrace*& _stackTrace) +{ + CpuStateX86* cpuState = dynamic_cast(_cpuState); + + // create the object + StackTrace* stackTrace = new(std::nothrow) StackTrace; + if (stackTrace == NULL) + return B_NO_MEMORY; + ObjectDeleter stackTraceDeleter(stackTrace); + + // create the top frame + StackFrameX86* frame = new StackFrameX86(STACK_FRAME_TYPE_TOP, cpuState); + if (frame == NULL) + return B_NO_MEMORY; + stackTrace->AddFrame(frame); + + while (true) { + uint32 framePointer = (uint32)frame->FrameAddress(); + if (framePointer == 0) + break; + + // get previous frame and return address + uint32 frameData[2]; + ssize_t bytesRead = fDebuggerInterface->ReadMemory(framePointer, + frameData, 8); + if (bytesRead != 8) + break; + + frame->SetPreviousAddresses(frameData[0], frameData[1]); + + if (frameData[0] == 0 || frameData[1] == 0) + break; + + // prepare the previous CPU state + cpuState = new(std::nothrow) CpuStateX86; + if (cpuState == NULL) + return B_NO_MEMORY; + Reference cpuStateReference(cpuState, true); + + cpuState->SetIntRegister(X86_REGISTER_EBP, frameData[0]); + cpuState->SetIntRegister(X86_REGISTER_EIP, frameData[1]); + // TODO: Actually it's the instruction before! + + // create the next frame + frame = new StackFrameX86(STACK_FRAME_TYPE_STANDARD, cpuState); + if (frame == NULL) + return B_NO_MEMORY; + stackTrace->AddFrame(frame); + } + + stackTraceDeleter.Detach(); + _stackTrace = stackTrace; + return B_OK; +} + + void ArchitectureX86::_AddRegister(int32 index, const char* name, register_format format, uint32 bitSize, register_type type) diff --git a/src/apps/debugger/arch/x86/ArchitectureX86.h b/src/apps/debugger/arch/x86/ArchitectureX86.h index 7ef7638206..bb3657b408 100644 --- a/src/apps/debugger/arch/x86/ArchitectureX86.h +++ b/src/apps/debugger/arch/x86/ArchitectureX86.h @@ -23,6 +23,8 @@ public: virtual status_t CreateCpuState(const void* cpuStateData, size_t size, CpuState*& _state); + virtual status_t CreateStackTrace(Team* team, CpuState* cpuState, + StackTrace*& _stackTrace); private: void _AddRegister(int32 index, const char* name, diff --git a/src/apps/debugger/arch/x86/CpuStateX86.cpp b/src/apps/debugger/arch/x86/CpuStateX86.cpp index 7a8513f5fe..7455d0853a 100644 --- a/src/apps/debugger/arch/x86/CpuStateX86.cpp +++ b/src/apps/debugger/arch/x86/CpuStateX86.cpp @@ -8,6 +8,13 @@ #include "Register.h" +CpuStateX86::CpuStateX86() + : + fSetRegisters() +{ +} + + CpuStateX86::CpuStateX86(const debug_cpu_state_x86& state) : fSetRegisters() diff --git a/src/apps/debugger/arch/x86/CpuStateX86.h b/src/apps/debugger/arch/x86/CpuStateX86.h index c83e0a717d..4e176a75b4 100644 --- a/src/apps/debugger/arch/x86/CpuStateX86.h +++ b/src/apps/debugger/arch/x86/CpuStateX86.h @@ -42,6 +42,7 @@ enum { class CpuStateX86 : public CpuState { public: + CpuStateX86(); CpuStateX86(const debug_cpu_state_x86& state); virtual ~CpuStateX86(); diff --git a/src/apps/debugger/arch/x86/StackFrameX86.cpp b/src/apps/debugger/arch/x86/StackFrameX86.cpp new file mode 100644 index 0000000000..c077df327c --- /dev/null +++ b/src/apps/debugger/arch/x86/StackFrameX86.cpp @@ -0,0 +1,76 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "StackFrameX86.h" + +#include "CpuStateX86.h" + + +StackFrameX86::StackFrameX86(stack_frame_type type, CpuStateX86* cpuState) + : + fType(type), + fCpuState(cpuState), + fPreviousFrameAddress(0), + fReturnAddress(0) +{ + fCpuState->AddReference(); +} + + +StackFrameX86::~StackFrameX86() +{ + fCpuState->RemoveReference(); +} + + +void +StackFrameX86::SetPreviousAddresses(target_addr_t previousFrameAddress, + target_addr_t returnAddress) +{ + fPreviousFrameAddress = previousFrameAddress; + fReturnAddress = returnAddress; +} + + +stack_frame_type +StackFrameX86::Type() const +{ + return fType; +} + + +CpuState* +StackFrameX86::GetCpuState() const +{ + return fCpuState; +} + + +target_addr_t +StackFrameX86::InstructionPointer() const +{ + return fCpuState->IntRegisterValue(X86_REGISTER_EIP); +} + + +target_addr_t +StackFrameX86::FrameAddress() const +{ + return fCpuState->IntRegisterValue(X86_REGISTER_EBP); +} + + +target_addr_t +StackFrameX86::ReturnAddress() const +{ + return fReturnAddress; +} + + +target_addr_t +StackFrameX86::PreviousFrameAddress() const +{ + return fPreviousFrameAddress; +} diff --git a/src/apps/debugger/arch/x86/StackFrameX86.h b/src/apps/debugger/arch/x86/StackFrameX86.h new file mode 100644 index 0000000000..82fdda977c --- /dev/null +++ b/src/apps/debugger/arch/x86/StackFrameX86.h @@ -0,0 +1,41 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef STACK_FRAME_X86_H +#define STACK_FRAME_X86_H + +#include "StackFrame.h" + + +class CpuStateX86; + + +class StackFrameX86 : public StackFrame { +public: + StackFrameX86(stack_frame_type type, + CpuStateX86* cpuState); + virtual ~StackFrameX86(); + + void SetPreviousAddresses( + target_addr_t previousFrameAddress, + target_addr_t returnAddress); + + virtual stack_frame_type Type() const; + + virtual CpuState* GetCpuState() const; + + virtual target_addr_t InstructionPointer() const; + virtual target_addr_t FrameAddress() const; + virtual target_addr_t ReturnAddress() const; + virtual target_addr_t PreviousFrameAddress() const; + +private: + stack_frame_type fType; + CpuStateX86* fCpuState; + target_addr_t fPreviousFrameAddress; + target_addr_t fReturnAddress; +}; + + +#endif // STACK_FRAME_X86_H diff --git a/src/apps/debugger/gui/team_window/StackTraceView.cpp b/src/apps/debugger/gui/team_window/StackTraceView.cpp new file mode 100644 index 0000000000..412bd7c681 --- /dev/null +++ b/src/apps/debugger/gui/team_window/StackTraceView.cpp @@ -0,0 +1,212 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "StackTraceView.h" + +#include + +#include + +#include "table/TableColumns.h" + +#include "StackTrace.h" + + +// #pragma mark - TargetAddressValueColumn + + +class TargetAddressValueColumn : public StringTableColumn { +public: + TargetAddressValueColumn(int32 modelIndex, const char* title, float width, + float minWidth, float maxWidth, uint32 truncate = B_TRUNCATE_MIDDLE, + alignment align = B_ALIGN_RIGHT) + : + StringTableColumn(modelIndex, title, width, minWidth, maxWidth, + truncate, align) + { + } + +protected: + virtual BField* PrepareField(const Variant& value) const + { + char buffer[64]; + snprintf(buffer, sizeof(buffer), "%#llx", value.ToUInt64()); + + return StringTableColumn::PrepareField( + Variant(buffer, VARIANT_DONT_COPY_DATA)); + } + + virtual int CompareValues(const Variant& a, const Variant& b) + { + uint64 valueA = a.ToUInt64(); + uint64 valueB = b.ToUInt64(); + return valueA < valueB ? -1 : (valueA == valueB ? 0 : 1); + } +}; + + +// #pragma mark - FramesTableModel + + +class StackTraceView::FramesTableModel : public TableModel { +public: + FramesTableModel() + : + fStackTrace(NULL) + { + } + + ~FramesTableModel() + { + SetStackTrace(NULL); + } + + void SetStackTrace(StackTrace* stackTrace) + { + // unset old frames + if (fFrames.CountItems() > 0) { + NotifyRowsRemoved(0, fFrames.CountItems()); + + for (int32 i = 0; StackFrame* frame = fFrames.ItemAt(i); i++) + frame->RemoveReference(); + + fFrames.MakeEmpty(); + } + + fStackTrace = stackTrace; + + // set new frames + if (fStackTrace != NULL) { + for (StackFrameList::ConstIterator it + = fStackTrace->Frames().GetIterator(); + StackFrame* frame = it.Next();) { + if (!fFrames.AddItem(frame)) + return; + frame->AddReference(); + } + + if (fFrames.CountItems() > 0) + NotifyRowsAdded(0, fFrames.CountItems()); + } + } + + virtual int32 CountColumns() const + { + return 3; + } + + virtual int32 CountRows() const + { + return fFrames.CountItems(); + } + + virtual bool GetValueAt(int32 rowIndex, int32 columnIndex, Variant& value) + { + StackFrame* frame = fFrames.ItemAt(rowIndex); + if (frame == NULL) + return false; + + switch (columnIndex) { + case 0: + value.SetTo(frame->FrameAddress()); + return true; + case 1: + value.SetTo(frame->InstructionPointer()); + return true; + case 2: + // TODO: function name... + return false; + default: + return false; + } + } + +private: + StackTrace* fStackTrace; + BObjectList fFrames; +}; + + +// #pragma mark - StackTraceView + + +StackTraceView::StackTraceView() + : + BGroupView(B_VERTICAL), + fFramesTable(NULL), + fFramesTableModel(NULL) +{ + SetName("Stack Trace"); +} + + +StackTraceView::~StackTraceView() +{ + SetStackTrace(NULL); + fFramesTable->SetTableModel(NULL); + delete fFramesTableModel; +} + + +/*static*/ StackTraceView* +StackTraceView::Create() +{ + StackTraceView* self = new StackTraceView(); + + try { + self->_Init(); + } catch (...) { + delete self; + throw; + } + + return self; +} + + +void +StackTraceView::SetStackTrace(StackTrace* stackTrace) +{ + if (stackTrace == fStackTrace) + return; + + if (fStackTrace != NULL) + fStackTrace->RemoveReference(); + + fStackTrace = stackTrace; + + if (fStackTrace != NULL) + fStackTrace->AddReference(); + + fFramesTableModel->SetStackTrace(fStackTrace); +} + + +void +StackTraceView::TableRowInvoked(Table* table, int32 rowIndex) +{ +} + + +void +StackTraceView::_Init() +{ + fFramesTable = new Table("register list", 0); + AddChild(fFramesTable->ToView()); + fFramesTable->SetSortingEnabled(false); + + // columns + fFramesTable->AddColumn(new TargetAddressValueColumn(0, "Frame", 80, 40, + 1000, B_TRUNCATE_END, B_ALIGN_RIGHT)); + fFramesTable->AddColumn(new TargetAddressValueColumn(1, "IP", 80, 40, 1000, + B_TRUNCATE_END, B_ALIGN_RIGHT)); + fFramesTable->AddColumn(new StringTableColumn(2, "Function", 80, 40, 1000, + B_TRUNCATE_END, B_ALIGN_LEFT)); + + fFramesTableModel = new FramesTableModel(); + fFramesTable->SetTableModel(fFramesTableModel); + + fFramesTable->AddTableListener(this); +} diff --git a/src/apps/debugger/gui/team_window/StackTraceView.h b/src/apps/debugger/gui/team_window/StackTraceView.h new file mode 100644 index 0000000000..a55d56b97c --- /dev/null +++ b/src/apps/debugger/gui/team_window/StackTraceView.h @@ -0,0 +1,40 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef STACK_TRACE_VIEW_H +#define STACK_TRACE_VIEW_H + +#include + +#include "table/Table.h" +#include "Team.h" + + +class StackTraceView : public BGroupView, private TableListener { +public: + StackTraceView(); + ~StackTraceView(); + + static StackTraceView* Create(); + // throws + + void SetStackTrace(StackTrace* stackTrace); + +private: + class FramesTableModel; + +private: + // TableListener + virtual void TableRowInvoked(Table* table, int32 rowIndex); + + void _Init(); + +private: + StackTrace* fStackTrace; + Table* fFramesTable; + FramesTableModel* fFramesTableModel; +}; + + +#endif // STACK_TRACE_VIEW_H diff --git a/src/apps/debugger/gui/team_window/TeamWindow.cpp b/src/apps/debugger/gui/team_window/TeamWindow.cpp index a4e68fa4b9..a68cdea277 100644 --- a/src/apps/debugger/gui/team_window/TeamWindow.cpp +++ b/src/apps/debugger/gui/team_window/TeamWindow.cpp @@ -20,6 +20,8 @@ #include "ImageListView.h" #include "MessageCodes.h" #include "RegisterView.h" +#include "StackTrace.h" +#include "StackTraceView.h" #include "TeamDebugModel.h" @@ -38,6 +40,7 @@ TeamWindow::TeamWindow(TeamDebugModel* debugModel, Listener* listener) fThreadListView(NULL), fImageListView(NULL), fRegisterView(NULL), + fStackTraceView(NULL), fRunButton(NULL), fStepOverButton(NULL), fStepIntoButton(NULL), @@ -109,7 +112,15 @@ TeamWindow::MessageReceived(BMessage* message) break; } -// case MSG_THREAD_STACK_TRACE_CHANGED: + case MSG_THREAD_STACK_TRACE_CHANGED: + { + int32 threadID; + if (message->FindInt32("thread", &threadID) != B_OK) + break; + + _HandleStackTraceChanged(threadID); + break; + } default: BWindow::MessageReceived(message); @@ -186,7 +197,7 @@ TeamWindow::_Init() fTabView->AddTab(threadGroup); BLayoutBuilder::Split<>(threadGroup) .Add(fThreadListView = ThreadListView::Create(this)) - .Add(new BTextView("stack frames")); + .Add(fStackTraceView = StackTraceView::Create()); // add images tab BSplitView* imagesGroup = new BSplitView(B_HORIZONTAL); @@ -234,12 +245,18 @@ TeamWindow::_SetActiveThread(::Thread* thread) CpuState* cpuState = fActiveThread != NULL ? fActiveThread->GetCpuState() : NULL; - Reference reference(cpuState); + Reference cpuStateReference(cpuState); + // hold a reference until the register view has one + + StackTrace* stackTrace = fActiveThread != NULL + ? fActiveThread->GetStackTrace() : NULL; + Reference stackTraceReference(stackTrace); // hold a reference until the register view has one locker.Unlock(); fRegisterView->SetCpuState(cpuState); + fStackTraceView->SetStackTrace(stackTrace); } @@ -308,6 +325,26 @@ TeamWindow::_HandleCpuStateChanged(thread_id threadID) } +void +TeamWindow::_HandleStackTraceChanged(thread_id threadID) +{ + // We're only interested in the currently selected thread + if (fActiveThread == NULL || threadID != fActiveThread->ID()) + return; + + AutoLocker locker(fDebugModel); + + StackTrace* stackTrace = fActiveThread != NULL + ? fActiveThread->GetStackTrace() : NULL; + Reference stackTraceReference(stackTrace); + // hold a reference until the register view has one + + locker.Unlock(); + + fStackTraceView->SetStackTrace(stackTrace); +} + + // #pragma mark - Listener diff --git a/src/apps/debugger/gui/team_window/TeamWindow.h b/src/apps/debugger/gui/team_window/TeamWindow.h index 53c2fb664f..3c97303668 100644 --- a/src/apps/debugger/gui/team_window/TeamWindow.h +++ b/src/apps/debugger/gui/team_window/TeamWindow.h @@ -16,6 +16,7 @@ class BButton; class BTabView; class ImageListView; class RegisterView; +class StackTraceView; class TeamDebugModel; @@ -55,6 +56,7 @@ private: void _HandleThreadStateChanged(thread_id threadID); void _HandleCpuStateChanged(thread_id threadID); + void _HandleStackTraceChanged(thread_id threadID); private: TeamDebugModel* fDebugModel; @@ -65,6 +67,7 @@ private: ThreadListView* fThreadListView; ImageListView* fImageListView; RegisterView* fRegisterView; + StackTraceView* fStackTraceView; BButton* fRunButton; BButton* fStepOverButton; BButton* fStepIntoButton;