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
This commit is contained in:
Ingo Weinhold
2009-06-19 22:13:32 +00:00
parent 1ae2cffc83
commit 15f040e596
19 changed files with 727 additions and 37 deletions
+9 -9
View File
@@ -13,6 +13,8 @@
#include <Application.h>
#include <Message.h>
#include <ObjectList.h>
#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<TeamDebugger> TeamDebuggerList;
typedef BObjectList<TeamDebugger> 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;
}
+3
View File
@@ -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
+63
View File
@@ -7,12 +7,17 @@
#include <AutoLocker.h>
#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<StackTrace> stackTraceReference(stackTrace, true);
// set the stack trace, unless something has changed
AutoLocker<Team> locker(fThread->GetTeam());
if (fThread->GetCpuState() == fCpuState)
fThread->SetStackTrace(stackTrace);
return B_OK;
}
+23 -1
View File
@@ -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
+111 -18
View File
@@ -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");
}
+15 -4
View File
@@ -9,20 +9,19 @@
#include <Looper.h>
#include <debug_support.h>
#include <util/DoublyLinkedList.h>
#include "DebugEvent.h"
#include "Team.h"
#include "TeamWindow.h"
#include "Worker.h"
class DebuggerInterface;
class Team;
class TeamDebugModel;
class TeamDebugger : public DoublyLinkedListLinkImpl<TeamDebugger>,
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;
+5
View File
@@ -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;
+1 -1
View File
@@ -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,
+11
View File
@@ -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;
+64 -1
View File
@@ -7,7 +7,12 @@
#include <new>
#include <AutoDeleter.h>
#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<CpuStateX86*>(_cpuState);
// create the object
StackTrace* stackTrace = new(std::nothrow) StackTrace;
if (stackTrace == NULL)
return B_NO_MEMORY;
ObjectDeleter<StackTrace> 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<CpuState> 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)
@@ -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,
@@ -8,6 +8,13 @@
#include "Register.h"
CpuStateX86::CpuStateX86()
:
fSetRegisters()
{
}
CpuStateX86::CpuStateX86(const debug_cpu_state_x86& state)
:
fSetRegisters()
+1
View File
@@ -42,6 +42,7 @@ enum {
class CpuStateX86 : public CpuState {
public:
CpuStateX86();
CpuStateX86(const debug_cpu_state_x86& state);
virtual ~CpuStateX86();
@@ -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;
}
@@ -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
@@ -0,0 +1,212 @@
/*
* Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include "StackTraceView.h"
#include <stdio.h>
#include <new>
#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<StackFrame> 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);
}
@@ -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 <GroupView.h>
#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
@@ -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<CpuState> reference(cpuState);
Reference<CpuState> cpuStateReference(cpuState);
// hold a reference until the register view has one
StackTrace* stackTrace = fActiveThread != NULL
? fActiveThread->GetStackTrace() : NULL;
Reference<StackTrace> 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<TeamDebugModel> locker(fDebugModel);
StackTrace* stackTrace = fActiveThread != NULL
? fActiveThread->GetStackTrace() : NULL;
Reference<StackTrace> stackTraceReference(stackTrace);
// hold a reference until the register view has one
locker.Unlock();
fStackTraceView->SetStackTrace(stackTrace);
}
// #pragma mark - Listener
@@ -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;