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;