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:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user