* Added classes to represent source code (SourceCode, Statement).

* Added x86 disassembler (via libudis86).
* Added Architecture::DisassembleCode() to disassemble a function to SourceCode.
* Added virtual DebugInfo::LoadSourceCode() to retrieve the source code for a
  given function. The implementation in DebuggerDebugInfo disassembles the
  function.
* Added source code info to StackFrame. Also added a listener mechanism to get
  notified on source code changes.
* Added job to load the source code for a stack frame.
* Added (very basic) source code view and wired everything so that when a stack
  frame is selected the source code (respectively disassembly) for the
  underlying function is retrieved and shown.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31158 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2009-06-21 13:17:21 +00:00
parent b3601d823c
commit 0bcacd22ab
27 changed files with 1359 additions and 15 deletions
+6
View File
@@ -51,6 +51,7 @@ Application Debugger :
# gui/team_window
ImageListView.cpp
RegisterView.cpp
SourceView.cpp
StackTraceView.cpp
TeamWindow.cpp
ThreadListView.cpp
@@ -58,8 +59,10 @@ Application Debugger :
# model
Image.cpp
ImageInfo.cpp
SourceCode.cpp
StackFrame.cpp
StackTrace.cpp
Statement.cpp
SymbolInfo.cpp
Team.cpp
TeamDebugModel.cpp
@@ -77,8 +80,10 @@ Application Debugger :
:
<nogrist>Debugger_demangler.o
<nogrist>Debugger_disasm_x86.o
<nogrist>DebugAnalyzer_gui_table.o
libudis86.a
<bin>debug_utils.a
libcolumnlistview.a
libshared.a
@@ -89,5 +94,6 @@ Application Debugger :
: Debugger.rdef
;
HaikuSubInclude arch x86 disasm ;
HaikuSubInclude demangler ;
HaikuSubInclude gui running_teams_window ;
+55 -1
View File
@@ -12,8 +12,11 @@
#include "Architecture.h"
#include "CpuState.h"
#include "DebuggerInterface.h"
#include "DebugInfo.h"
#include "FunctionDebugInfo.h"
#include "Image.h"
#include "ImageDebugInfo.h"
#include "SourceCode.h"
#include "StackTrace.h"
#include "Team.h"
#include "Thread.h"
@@ -170,7 +173,7 @@ GetStackTraceJob::GetImageDebugInfo(Image* image, ImageDebugInfo*& _info)
}
// #pragma mark - GetStackTraceJob
// #pragma mark - LoadImageDebugInfoJob
LoadImageDebugInfoJob::LoadImageDebugInfoJob(
@@ -224,3 +227,54 @@ LoadImageDebugInfoJob::Do()
return B_OK;
}
// #pragma mark - LoadSourceCodeJob
LoadSourceCodeJob::LoadSourceCodeJob(
DebuggerInterface* debuggerInterface, Architecture* architecture,
Team* team, StackFrame* stackFrame)
:
fDebuggerInterface(debuggerInterface),
fArchitecture(architecture),
fTeam(team),
fStackFrame(stackFrame)
{
fStackFrame->AddReference();
}
LoadSourceCodeJob::~LoadSourceCodeJob()
{
fStackFrame->RemoveReference();
}
JobKey
LoadSourceCodeJob::Key() const
{
return JobKey(fStackFrame, JOB_TYPE_LOAD_SOURCE_CODE);
}
status_t
LoadSourceCodeJob::Do()
{
// load the source code, if we can
SourceCode* sourceCode = NULL;
status_t error = B_BAD_VALUE;
FunctionDebugInfo* function = fStackFrame->Function();
if (function != NULL)
error = function->GetDebugInfo()->LoadSourceCode(function, sourceCode);
// set the result
AutoLocker<Team> locker(fTeam);
if (error == B_OK) {
fStackFrame->SetSourceCode(sourceCode, STACK_SOURCE_LOADED);
sourceCode->RemoveReference();
} else
fStackFrame->SetSourceCode(NULL, STACK_SOURCE_UNAVAILABLE);
return error;
}
+23 -1
View File
@@ -13,6 +13,8 @@ class Architecture;
class CpuState;
class DebuggerInterface;
class Image;
class StackFrame;
class Team;
class Thread;
@@ -20,7 +22,8 @@ class Thread;
enum {
JOB_TYPE_GET_CPU_STATE,
JOB_TYPE_GET_STACK_TRACE,
JOB_TYPE_LOAD_IMAGE_DEBUG_INFO
JOB_TYPE_LOAD_IMAGE_DEBUG_INFO,
JOB_TYPE_LOAD_SOURCE_CODE
};
@@ -81,4 +84,23 @@ private:
};
class LoadSourceCodeJob : public Job {
public:
LoadSourceCodeJob(
DebuggerInterface* debuggerInterface,
Architecture* architecture,
Team* team, StackFrame* stackFrame);
virtual ~LoadSourceCodeJob();
virtual JobKey Key() const;
virtual status_t Do();
private:
DebuggerInterface* fDebuggerInterface;
Architecture* fArchitecture;
Team* fTeam;
StackFrame* fStackFrame;
};
#endif // JOBS_H
+9 -8
View File
@@ -7,15 +7,16 @@
enum {
MSG_THREAD_RUN = 'run_',
MSG_THREAD_STOP = 'stop',
MSG_THREAD_STEP_OVER = 'stov',
MSG_THREAD_STEP_INTO = 'stin',
MSG_THREAD_STEP_OUT = 'stou',
MSG_THREAD_RUN = 'run_',
MSG_THREAD_STOP = 'stop',
MSG_THREAD_STEP_OVER = 'stov',
MSG_THREAD_STEP_INTO = 'stin',
MSG_THREAD_STEP_OUT = 'stou',
MSG_THREAD_STATE_CHANGED = 'tsch',
MSG_THREAD_CPU_STATE_CHANGED = 'tcsc',
MSG_THREAD_STACK_TRACE_CHANGED = 'tstc'
MSG_THREAD_STATE_CHANGED = 'tsch',
MSG_THREAD_CPU_STATE_CHANGED = 'tcsc',
MSG_THREAD_STACK_TRACE_CHANGED = 'tstc',
MSG_STACK_FRAME_SOURCE_CODE_CHANGED = 'sfsc'
};
+26
View File
@@ -233,6 +233,30 @@ TeamDebugger::MessageReceived(BMessage* message)
}
void
TeamDebugger::StackFrameSourceCodeRequested(TeamWindow* window,
StackFrame* frame)
{
// mark loading
AutoLocker< ::Team> locker(fTeam);
if (frame->SourceCodeState() != STACK_SOURCE_NOT_LOADED)
return;
frame->SetSourceCode(NULL, STACK_SOURCE_LOADING);
locker.Unlock();
// schedule the job
if (fWorker->ScheduleJob(
new(std::nothrow) LoadSourceCodeJob(fDebuggerInterface,
fDebuggerInterface->GetArchitecture(), fTeam, frame),
this) != B_OK) {
// scheduling failed -- mark unavailable
locker.Lock();
frame->SetSourceCode(NULL, STACK_SOURCE_UNAVAILABLE);
locker.Unlock();
}
}
void
TeamDebugger::ThreadActionRequested(TeamWindow* window, thread_id threadID,
uint32 action)
@@ -269,6 +293,8 @@ void
TeamDebugger::JobAborted(Job* job)
{
printf("TeamDebugger::JobAborted(%p)\n", job);
// TODO: For a stack frame source loader thread we should reset the
// loading state! Asynchronously due to locking order.
}
+2
View File
@@ -35,6 +35,8 @@ private:
virtual void MessageReceived(BMessage* message);
// TeamWindow::Listener
virtual void StackFrameSourceCodeRequested(
TeamWindow* window, StackFrame* frame);
virtual void ThreadActionRequested(TeamWindow* window,
thread_id threadID, uint32 action);
virtual bool TeamWindowQuitRequested(TeamWindow* window);
+4
View File
@@ -16,6 +16,7 @@ class FunctionDebugInfo;
class Image;
class ImageDebugInfoProvider;
class Register;
class SourceCode;
class StackFrame;
class StackTrace;
class Team;
@@ -42,6 +43,9 @@ public:
// returns reference to previous frame
// and CPU state; returned CPU state
// can be NULL
virtual status_t DisassembleCode(FunctionDebugInfo* function,
const void* buffer, size_t bufferSize,
SourceCode*& _sourceCode) = 0;
status_t CreateStackTrace(Team* team,
ImageDebugInfoProvider* imageInfoProvider,
@@ -0,0 +1,87 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef TARGET_ADDRESS_RANGE_H
#define TARGET_ADDRESS_RANGE_H
#include "ArchitectureTypes.h"
class TargetAddressRange {
public:
TargetAddressRange()
:
fStart(0),
fSize(0)
{
}
TargetAddressRange(target_addr_t start, target_size_t size)
:
fStart(start),
fSize(size)
{
}
TargetAddressRange(const TargetAddressRange& other)
:
fStart(other.fStart),
fSize(other.fSize)
{
}
TargetAddressRange& operator=(const TargetAddressRange& other)
{
fStart = other.fStart;
fSize = other.fSize;
return *this;
}
bool operator==(const TargetAddressRange& other) const
{
return fStart == other.fStart && fSize == other.fSize;
}
bool operator!=(const TargetAddressRange& other) const
{
return !(*this == other);
}
target_addr_t Start() const
{
return fStart;
}
target_size_t Size() const
{
return fSize;
}
target_addr_t End() const
{
return fStart + fSize;
}
bool Contains(target_addr_t address) const
{
return address >= Start() && address < End();
}
bool Contains(const TargetAddressRange& other) const
{
return Start() <= other.Start() && End() >= other.End();
}
bool IntersectsWith(const TargetAddressRange& other) const
{
return Contains(other.Start()) || other.Contains(Start());
}
private:
target_addr_t fStart;
target_size_t fSize;
};
#endif // TARGET_ADDRESS_RANGE_H
@@ -7,11 +7,18 @@
#include <new>
#include <String.h>
#include <AutoDeleter.h>
#include "CpuStateX86.h"
#include "DebuggerInterface.h"
#include "FunctionDebugInfo.h"
#include "SourceCode.h"
#include "StackFrame.h"
#include "Statement.h"
#include "disasm/DisassemblerX86.h"
ArchitectureX86::ArchitectureX86(DebuggerInterface* debuggerInterface)
@@ -139,6 +146,51 @@ ArchitectureX86::CreateStackFrame(Image* image, FunctionDebugInfo* function,
}
status_t
ArchitectureX86::DisassembleCode(FunctionDebugInfo* function,
const void* buffer, size_t bufferSize, SourceCode*& _sourceCode)
{
SourceCode* source = new(std::nothrow) SourceCode;
if (source == NULL)
return B_NO_MEMORY;
Reference<SourceCode> sourceReference(source, true);
// init disassembler
DisassemblerX86 disassembler;
status_t error = disassembler.Init(function->Address(), buffer, bufferSize);
if (error != B_OK)
return error;
// add a function name line
BString functionName(function->PrettyName());
if (!source->AddLine((functionName << ':').String()))
return B_NO_MEMORY;
uint32 lineIndex = 1;
// disassemble the instructions
BString line;
target_addr_t instructionAddress;
target_size_t instructionSize;
bool breakpointAllowed;
while (disassembler.GetNextInstruction(line, instructionAddress,
instructionSize, breakpointAllowed) == B_OK) {
Statement* statement = new(std::nothrow) ContiguousStatement(
SourceLocation(lineIndex), SourceLocation(lineIndex + 1),
TargetAddressRange(instructionAddress, instructionAddress));
if (statement == NULL)
return B_NO_MEMORY;
if (!source->AddStatement(statement) || !source->AddLine(line.String()))
return B_NO_MEMORY;
lineIndex++;
}
_sourceCode = sourceReference.Detach();
return B_OK;
}
void
ArchitectureX86::_AddRegister(int32 index, const char* name,
register_format format, uint32 bitSize, register_type type)
@@ -28,6 +28,9 @@ public:
CpuState* cpuState,
StackFrame*& _previousFrame,
CpuState*& _previousCpuState);
virtual status_t DisassembleCode(FunctionDebugInfo* function,
const void* buffer, size_t bufferSize,
SourceCode*& _sourceCode);
private:
void _AddRegister(int32 index, const char* name,
@@ -0,0 +1,88 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Copyright 2008, François Revol, [email protected]
* Distributed under the terms of the MIT License.
*/
#include "DisassemblerX86.h"
#include <new>
#include "udis86.h"
#include <OS.h>
struct DisassemblerX86::UdisData : ud_t {
};
DisassemblerX86::DisassemblerX86()
:
fAddress(0),
fCode(NULL),
fCodeSize(0),
fUdisData(NULL)
{
}
DisassemblerX86::~DisassemblerX86()
{
delete fUdisData;
}
status_t
DisassemblerX86::Init(target_addr_t address, const void* code, size_t codeSize)
{
// unset old data
delete fUdisData;
fUdisData = NULL;
// set new data
fUdisData = new(std::nothrow) UdisData;
if (fUdisData == NULL)
return B_NO_MEMORY;
fAddress = address;
fCode = (const uint8*)code;
fCodeSize = codeSize;
// init udis
ud_init(fUdisData);
ud_set_input_buffer(fUdisData, (unsigned char*)fCode, fCodeSize);
ud_set_mode(fUdisData, 32);
ud_set_pc(fUdisData, (uint64_t)fAddress);
ud_set_syntax(fUdisData, UD_SYN_ATT);
ud_set_vendor(fUdisData, UD_VENDOR_INTEL);
// TODO: Set the correct vendor!
return B_OK;
}
status_t
DisassemblerX86::GetNextInstruction(BString& line, target_addr_t& _address,
target_size_t& _size, bool& _breakpointAllowed)
{
unsigned int size = ud_disassemble(fUdisData);
if (size < 1)
return B_ENTRY_NOT_FOUND;
uint32 address = (uint32)ud_insn_off(fUdisData);
char buffer[256];
snprintf(buffer, sizeof(buffer), "0x%08lx: %16.16s %s", address,
ud_insn_hex(fUdisData), ud_insn_asm(fUdisData));
// TODO: Resolve symbols!
line = buffer;
_address = address;
_size = size;
_breakpointAllowed = true;
// TODO: Implement (rep!)!
return B_OK;
}
@@ -0,0 +1,37 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef DISASSEMBLER_X86_H
#define DISASSEMBLER_X86_H
#include <String.h>
#include "ArchitectureTypes.h"
class DisassemblerX86 {
public:
DisassemblerX86();
virtual ~DisassemblerX86();
virtual status_t Init(target_addr_t address, const void* code,
size_t codeSize);
virtual status_t GetNextInstruction(BString& line,
target_addr_t& _address,
target_size_t& _size,
bool& _breakpointAllowed);
private:
struct UdisData;
private:
target_addr_t fAddress;
const uint8* fCode;
size_t fCodeSize;
UdisData* fUdisData;
};
#endif // DISASSEMBLER_X86_H
+15
View File
@@ -0,0 +1,15 @@
SubDir HAIKU_TOP src apps debugger arch x86 disasm ;
CCFLAGS += -Werror ;
C++FLAGS += -Werror ;
UseHeaders [ LibraryHeaders udis86 ] ;
UseHeaders [ LibraryHeaders [ FDirName udis86 libudis86 ] ] ;
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) $(DOTDOT) ] ;
MergeObject Debugger_disasm_x86.o
:
DisassemblerX86.cpp
;
+4
View File
@@ -15,6 +15,7 @@ class CpuState;
class DebuggerInterface;
class FunctionDebugInfo;
class Image;
class SourceCode;
class StackFrame;
@@ -33,6 +34,9 @@ public:
// returns reference to previous frame
// and CPU state; returned CPU state
// can be NULL; can return B_UNSUPPORTED
virtual status_t LoadSourceCode(FunctionDebugInfo* function,
SourceCode*& _sourceCode) = 0;
// returns reference
};
@@ -5,8 +5,12 @@
#include "DebuggerDebugInfo.h"
#include <algorithm>
#include <new>
#include <AutoDeleter.h>
#include "Architecture.h"
#include "BasicFunctionDebugInfo.h"
#include "DebuggerInterface.h"
#include "Demangler.h"
@@ -87,6 +91,29 @@ DebuggerDebugInfo::CreateFrame(Image* image, FunctionDebugInfo* function,
}
status_t
DebuggerDebugInfo::LoadSourceCode(FunctionDebugInfo* function,
SourceCode*& _sourceCode)
{
// allocate a buffer for the function code
static const target_size_t kMaxBufferSize = 64 * 1024;
target_size_t bufferSize = std::min(function->Size(), kMaxBufferSize);
void* buffer = malloc(bufferSize);
if (buffer == NULL)
return B_NO_MEMORY;
MemoryDeleter bufferDeleter(buffer);
// read the function code
ssize_t bytesRead = fDebuggerInterface->ReadMemory(function->Address(),
buffer, bufferSize);
if (bytesRead < 0)
return bytesRead;
return fArchitecture->DisassembleCode(function, buffer, bytesRead,
_sourceCode);
}
SymbolInfo*
DebuggerDebugInfo::_FindSymbol(target_addr_t address)
{
@@ -34,6 +34,8 @@ public:
CpuState* cpuState,
StackFrame*& _previousFrame,
CpuState*& _previousCpuState);
virtual status_t LoadSourceCode(FunctionDebugInfo* function,
SourceCode*& _sourceCode);
private:
typedef BObjectList<SymbolInfo> SymbolList;
@@ -0,0 +1,368 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#include "SourceView.h"
#include <algorithm>
#include <new>
#include <stdio.h>
#include <LayoutUtils.h>
#include <ScrollBar.h>
#include <AutoLocker.h>
#include "SourceCode.h"
#include "StackFrame.h"
#include "TeamDebugModel.h"
static const int32 kLeftTextMargin = 3;
class SourceView::MarkerView : public BView {
public:
MarkerView(SourceView* sourceView, FontInfo* fontInfo)
:
BView("source marker view", B_WILL_DRAW),
fSourceView(sourceView),
fFontInfo(fontInfo)
{
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
}
virtual BSize MinSize()
{
return BSize(40, 10);
}
virtual BSize MaxSize()
{
return BSize(MinSize().width, B_SIZE_UNLIMITED);
}
virtual BSize PreferredSize()
{
return MinSize();
}
virtual void Draw(BRect updateRect)
{
}
private:
SourceView* fSourceView;
FontInfo* fFontInfo;
};
class SourceView::TextView : public BView {
public:
TextView(SourceView* sourceView, FontInfo* fontInfo)
:
BView("source text view", B_WILL_DRAW),
fSourceView(sourceView),
fFontInfo(fontInfo),
fSourceCode(NULL),
fMaxLineWidth(-1)
{
SetViewColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR));
fTextColor = ui_color(B_DOCUMENT_TEXT_COLOR);
}
void SetSourceCode(SourceCode* sourceCode)
{
fSourceCode = sourceCode;
fMaxLineWidth = -1;
InvalidateLayout();
Invalidate();
}
virtual BSize MinSize()
{
float height = _LineCount() * fFontInfo->lineHeight;
return BSize(kLeftTextMargin + _MaxLineWidth() - 1,
std::max(height - 1, 100.0f));
}
virtual BSize MaxSize()
{
return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
}
virtual BSize PreferredSize()
{
return MinSize();
}
virtual void Draw(BRect updateRect)
{
if (fSourceCode == NULL)
return;
// get the lines intersecting with the update rect
int32 lineHeight = (int32)fFontInfo->lineHeight;
int32 minLine = (int32)updateRect.top / lineHeight;
int32 maxLine = ((int32)ceilf(updateRect.bottom) + lineHeight - 1)
/ lineHeight;
minLine = std::max(minLine, 0L);
maxLine = std::min(maxLine, fSourceCode->CountLines() - 1);
// draw the affected lines
SetHighColor(fTextColor);
SetFont(&fFontInfo->font);
for (int32 i = minLine; i <= maxLine; i++) {
float y = (float)(i + 1) * fFontInfo->lineHeight
- fFontInfo->fontHeight.descent;
DrawString(fSourceCode->LineAt(i), BPoint(kLeftTextMargin, y));
}
}
private:
int32 _LineCount() const
{
return fSourceCode != NULL ? fSourceCode->CountLines() : 0;
}
float _MaxLineWidth()
{
if (fMaxLineWidth >= 0)
return fMaxLineWidth;
fMaxLineWidth = 0;
if (fSourceCode != NULL) {
for (int32 i = 0; const char* line = fSourceCode->LineAt(i); i++) {
fMaxLineWidth = std::max(fMaxLineWidth,
fFontInfo->font.StringWidth(line));
}
}
return fMaxLineWidth;
}
private:
SourceView* fSourceView;
FontInfo* fFontInfo;
SourceCode* fSourceCode;
float fMaxLineWidth;
rgb_color fTextColor;
};
// #pragma mark - SourceView
SourceView::SourceView(TeamDebugModel* debugModel, Listener* listener)
:
BView("source view", 0),
fDebugModel(debugModel),
fStackFrame(NULL),
fSourceCode(NULL),
fMarkerView(NULL),
fTextView(NULL),
fListener(listener)
{
// init font info
fFontInfo.font = *be_fixed_font;
fFontInfo.font.GetHeight(&fFontInfo.fontHeight);
fFontInfo.lineHeight = ceilf(fFontInfo.fontHeight.ascent)
+ ceilf(fFontInfo.fontHeight.descent);
}
SourceView::~SourceView()
{
SetStackFrame(NULL);
}
/*static*/ SourceView*
SourceView::Create(TeamDebugModel* debugModel, Listener* listener)
{
SourceView* self = new SourceView(debugModel, listener);
try {
self->_Init();
} catch (...) {
delete self;
throw;
}
return self;
}
void
SourceView::UnsetListener()
{
fListener = NULL;
}
void
SourceView::SetStackFrame(StackFrame* frame)
{
printf("SourceView::SetStackFrame(%p)\n", frame);
if (frame == fStackFrame)
return;
if (fStackFrame != NULL)
fStackFrame->RemoveReference();
fStackFrame = frame;
if (fStackFrame != NULL)
fStackFrame->AddReference();
UpdateSourceCode();
}
void
SourceView::UpdateSourceCode()
{
// get a reference to the source code
AutoLocker<TeamDebugModel> locker(fDebugModel);
SourceCode* sourceCode = fStackFrame != NULL
? fStackFrame->GetSourceCode() : NULL;
Reference<SourceCode> sourceCodeReference(sourceCode);
locker.Unlock();
// set the source code, if it changed
if (sourceCode == fSourceCode)
return;
if (fSourceCode != NULL) {
fTextView->SetSourceCode(NULL);
fSourceCode->RemoveReference();
}
fSourceCode = sourceCodeReference.Detach();
fTextView->SetSourceCode(fSourceCode);
_UpdateScrollBars();
}
void
SourceView::TargetedByScrollView(BScrollView* scrollView)
{
_UpdateScrollBars();
}
BSize
SourceView::MinSize()
{
// BSize markerSize(fMarkerView->MinSize());
// BSize textSize(fTextView->MinSize());
// return BSize(BLayoutUtils::AddDistances(markerSize.width, textSize.width),
// std::max(markerSize.height, textSize.height));
return BSize(10, 10);
}
BSize
SourceView::MaxSize()
{
// BSize markerSize(fMarkerView->MaxSize());
// BSize textSize(fTextView->MaxSize());
// return BSize(BLayoutUtils::AddDistances(markerSize.width, textSize.width),
// std::min(markerSize.height, textSize.height));
return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
}
BSize
SourceView::PreferredSize()
{
BSize markerSize(fMarkerView->PreferredSize());
BSize textSize(fTextView->PreferredSize());
return BSize(BLayoutUtils::AddDistances(markerSize.width, textSize.width),
std::max(markerSize.height, textSize.height));
// return MinSize();
}
void
SourceView::DoLayout()
{
BSize size = _DataRectSize();
float markerWidth = fMarkerView->MinSize().width;
fMarkerView->MoveTo(0, 0);
fMarkerView->ResizeTo(markerWidth, size.height);
fTextView->MoveTo(markerWidth + 1, 0);
fTextView->ResizeTo(size.width - markerWidth - 1, size.height);
_UpdateScrollBars();
}
void
SourceView::_Init()
{
AddChild(fMarkerView = new MarkerView(this, &fFontInfo));
AddChild(fTextView = new TextView(this, &fFontInfo));
}
void
SourceView::_UpdateScrollBars()
{
BSize dataRectSize = _DataRectSize();
BSize size = Frame().Size();
if (BScrollBar* scrollBar = ScrollBar(B_HORIZONTAL)) {
float range = dataRectSize.width - size.width;
if (range > 0) {
scrollBar->SetRange(0, range);
scrollBar->SetProportion(
(size.width + 1) / (dataRectSize.width + 1));
scrollBar->SetSteps(fFontInfo.lineHeight, size.width + 1);
} else {
scrollBar->SetRange(0, 0);
scrollBar->SetProportion(1);
}
}
if (BScrollBar* scrollBar = ScrollBar(B_VERTICAL)) {
float range = dataRectSize.height - size.height;
if (range > 0) {
scrollBar->SetRange(0, range);
scrollBar->SetProportion(
(size.height + 1) / (dataRectSize.height + 1));
scrollBar->SetSteps(fFontInfo.lineHeight, size.height + 1);
} else {
scrollBar->SetRange(0, 0);
scrollBar->SetProportion(1);
}
}
}
BSize
SourceView::_DataRectSize() const
{
float width = fMarkerView->MinSize().width + fTextView->MinSize().width + 1;
float height = std::max(fMarkerView->MinSize().height,
fTextView->MinSize().height);
BSize size = Frame().Size();
return BSize(std::max(size.width, width), std::max(size.height, height));
}
// #pragma mark - Listener
SourceView::Listener::~Listener()
{
}
@@ -0,0 +1,78 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef SOURCE_VIEW_H
#define SOURCE_VIEW_H
#include <Font.h>
#include <View.h>
class SourceCode;
class StackFrame;
class TeamDebugModel;
class SourceView : public BView {
public:
class Listener;
public:
SourceView(TeamDebugModel* debugModel,
Listener* listener);
~SourceView();
static SourceView* Create(TeamDebugModel* debugModel,
Listener* listener);
// throws
void UnsetListener();
void SetStackFrame(StackFrame* frame);
void UpdateSourceCode();
virtual void TargetedByScrollView(BScrollView* scrollView);
virtual BSize MinSize();
virtual BSize MaxSize();
virtual BSize PreferredSize();
virtual void DoLayout();
private:
class MarkerView;
class TextView;
struct FontInfo {
BFont font;
font_height fontHeight;
float lineHeight;
};
private:
void _Init();
void _UpdateScrollBars();
BSize _DataRectSize() const;
private:
TeamDebugModel* fDebugModel;
StackFrame* fStackFrame;
SourceCode* fSourceCode;
MarkerView* fMarkerView;
TextView* fTextView;
FontInfo fFontInfo;
Listener* fListener;
};
class SourceView::Listener {
public:
virtual ~Listener();
// virtual void StackFrameSelectionChanged(
// StackFrame* frame) = 0;
};
#endif // SOURCE_VIEW_H
@@ -11,6 +11,7 @@
#include <LayoutBuilder.h>
#include <Message.h>
#include <TabView.h>
#include <ScrollView.h>
#include <SplitView.h>
#include <TextView.h>
@@ -42,6 +43,7 @@ TeamWindow::TeamWindow(TeamDebugModel* debugModel, Listener* listener)
fImageListView(NULL),
fRegisterView(NULL),
fStackTraceView(NULL),
fSourceView(NULL),
fRunButton(NULL),
fStepOverButton(NULL),
fStepIntoButton(NULL),
@@ -63,6 +65,8 @@ TeamWindow::~TeamWindow()
fThreadListView->UnsetListener();
if (fStackTraceView != NULL)
fStackTraceView->UnsetListener();
if (fSourceView != NULL)
fSourceView->UnsetListener();
fDebugModel->GetTeam()->RemoveListener(this);
}
@@ -128,6 +132,11 @@ TeamWindow::MessageReceived(BMessage* message)
break;
}
case MSG_STACK_FRAME_SOURCE_CODE_CHANGED:
{
_HandleSourceCodeChanged();
}
default:
BWindow::MessageReceived(message);
break;
@@ -183,9 +192,20 @@ TeamWindow::ThreadStackTraceChanged(const Team::ThreadEvent& event)
}
void
TeamWindow::StackFrameSourceCodeChanged(StackFrame* frame)
{
printf("TeamWindow::StackFrameSourceCodeChanged(%p): source: %p, state: %d\n",
frame, frame->GetSourceCode(), frame->SourceCodeState());
PostMessage(MSG_STACK_FRAME_SOURCE_CODE_CHANGED);
}
void
TeamWindow::_Init()
{
BScrollView* sourceScrollView;
BLayoutBuilder::Group<>(this, B_VERTICAL)
.AddSplit(B_VERTICAL, 3.0f)
.AddGroup(B_VERTICAL, 0.0f, 0.4f)
@@ -202,7 +222,8 @@ TeamWindow::_Init()
.SetInsets(4.0f, 0.0f, 4.0f, 0.0f)
.End()
.AddSplit(B_HORIZONTAL, 3.0f)
.Add(new BTextView("source view"), 3.0f)
.Add(sourceScrollView = new BScrollView("source scroll",
NULL, 0, true, true), 3.0f)
.AddGroup(B_VERTICAL)
.Add(fLocalsTabView = new BTabView("locals view"))
.SetInsets(0.0f, 0.0f, 4.0f, 4.0f)
@@ -211,6 +232,10 @@ TeamWindow::_Init()
.End()
.End();
// add source view
sourceScrollView->SetTarget(fSourceView = SourceView::Create(fDebugModel,
this));
// add threads tab
BSplitView* threadGroup = new BSplitView(B_HORIZONTAL);
threadGroup->SetName("Threads");
@@ -281,15 +306,29 @@ TeamWindow::_SetActiveStackFrame(StackFrame* frame)
if (frame == fActiveStackFrame)
return;
if (fActiveStackFrame != NULL)
AutoLocker<TeamDebugModel> locker(fDebugModel);
if (fActiveStackFrame != NULL) {
fActiveStackFrame->RemoveListener(this);
fActiveStackFrame->RemoveReference();
}
fActiveStackFrame = frame;
if (fActiveStackFrame != NULL)
if (fActiveStackFrame != NULL) {
fActiveStackFrame->AddReference();
fActiveStackFrame->AddListener(this);
// If the source code is not loaded yet, request it.
if (fActiveStackFrame->SourceCodeState() == STACK_SOURCE_NOT_LOADED)
fListener->StackFrameSourceCodeRequested(this, fActiveStackFrame);
}
_UpdateCpuState();
locker.Unlock();
fSourceView->SetStackFrame(fActiveStackFrame);
}
@@ -392,6 +431,13 @@ TeamWindow::_HandleStackTraceChanged(thread_id threadID)
}
void
TeamWindow::_HandleSourceCodeChanged()
{
fSourceView->UpdateSourceCode();
}
// #pragma mark - Listener
+14 -1
View File
@@ -8,6 +8,8 @@
#include <String.h>
#include <Window.h>
#include "SourceView.h"
#include "StackFrame.h"
#include "StackTraceView.h"
#include "Team.h"
#include "ThreadListView.h"
@@ -21,7 +23,8 @@ class TeamDebugModel;
class TeamWindow : public BWindow, private ThreadListView::Listener,
StackTraceView::Listener, Team::Listener {
StackTraceView::Listener, SourceView::Listener, Team::Listener,
StackFrame::Listener {
public:
class Listener;
@@ -44,6 +47,9 @@ private:
// StackTraceView::Listener
virtual void StackFrameSelectionChanged(StackFrame* frame);
// StackTraceView::Listener
// nothing yet
// Team::Listener
virtual void ThreadStateChanged(
const Team::ThreadEvent& event);
@@ -52,6 +58,9 @@ private:
virtual void ThreadStackTraceChanged(
const Team::ThreadEvent& event);
// StackFrame::Listener
virtual void StackFrameSourceCodeChanged(StackFrame* frame);
void _Init();
void _SetActiveThread(::Thread* thread);
@@ -62,6 +71,7 @@ private:
void _HandleThreadStateChanged(thread_id threadID);
void _HandleCpuStateChanged(thread_id threadID);
void _HandleStackTraceChanged(thread_id threadID);
void _HandleSourceCodeChanged();
private:
TeamDebugModel* fDebugModel;
@@ -74,6 +84,7 @@ private:
ImageListView* fImageListView;
RegisterView* fRegisterView;
StackTraceView* fStackTraceView;
SourceView* fSourceView;
BButton* fRunButton;
BButton* fStepOverButton;
BButton* fStepIntoButton;
@@ -85,6 +96,8 @@ class TeamWindow::Listener {
public:
virtual ~Listener();
virtual void StackFrameSourceCodeRequested(
TeamWindow* window, StackFrame* frame) = 0;
virtual void ThreadActionRequested(TeamWindow* window,
thread_id threadID, uint32 action) = 0;
virtual bool TeamWindowQuitRequested(TeamWindow* window) = 0;
+80
View File
@@ -0,0 +1,80 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#include "SourceCode.h"
#include <stdlib.h>
#include <string.h>
#include "Statement.h"
SourceCode::SourceCode()
{
}
SourceCode::~SourceCode()
{
for (int32 i = 0; char* line = fLines.ItemAt(i); i++)
free(line);
for (int32 i = 0; Statement* statement = fStatements.ItemAt(i); i++)
statement->RemoveReference();
}
int32
SourceCode::CountLines() const
{
return fLines.CountItems();
}
const char*
SourceCode::LineAt(int32 index) const
{
return fLines.ItemAt(index);
}
int32
SourceCode::CountStatements() const
{
return fStatements.CountItems();
}
Statement*
SourceCode::StatementAt(int32 index) const
{
return fStatements.ItemAt(index);
}
bool
SourceCode::AddLine(const char* _line)
{
char* line = strdup(_line);
if (line == NULL)
return false;
if (!fLines.AddItem(line)) {
free(line);
return false;
}
return true;
}
bool
SourceCode::AddStatement(Statement* statement)
{
if (!fStatements.AddItem(statement)) {
statement->RemoveReference();
return false;
}
return true;
}
+43
View File
@@ -0,0 +1,43 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef SOURCE_CODE_H
#define SOURCE_CODE_H
#include <ObjectList.h>
#include <Referenceable.h>
#include "ArchitectureTypes.h"
class Statement;
class SourceCode : public Referenceable {
public:
SourceCode();
~SourceCode();
int32 CountLines() const;
const char* LineAt(int32 index) const;
int32 CountStatements() const;
Statement* StatementAt(int32 index) const;
bool AddLine(const char* line);
// clones
bool AddStatement(Statement* statement);
// takes over reference
private:
typedef BObjectList<char> LineList;
typedef BObjectList<Statement> StatementList;
private:
LineList fLines;
StatementList fStatements;
};
#endif // SOURCE_CODE_H
+60
View File
@@ -0,0 +1,60 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef SOURCE_LOCATION_H
#define SOURCE_LOCATION_H
#include <SupportDefs.h>
class SourceLocation {
public:
SourceLocation(uint32 line = 0, uint32 column = 0)
:
fLine(line),
fColumn(column)
{
}
SourceLocation(const SourceLocation& other)
:
fLine(other.fLine),
fColumn(other.fColumn)
{
}
SourceLocation& operator=(const SourceLocation& other)
{
fLine = other.fLine;
fColumn = other.fColumn;
return *this;
}
bool operator==(const SourceLocation& other) const
{
return fLine == other.fLine && fColumn == other.fColumn;
}
bool operator!=(const SourceLocation& other) const
{
return !(*this == other);
}
uint32 Line() const
{
return fLine;
}
uint32 Column() const
{
return fColumn;
}
private:
uint32 fLine;
uint32 fColumn;
};
#endif // SOURCE_LOCATION_H
+56 -1
View File
@@ -8,6 +8,10 @@
#include "CpuState.h"
#include "FunctionDebugInfo.h"
#include "Image.h"
#include "SourceCode.h"
// #pragma mark - StackFrame
StackFrame::StackFrame(stack_frame_type type, CpuState* cpuState,
@@ -18,7 +22,9 @@ StackFrame::StackFrame(stack_frame_type type, CpuState* cpuState,
fFrameAddress(frameAddress),
fReturnAddress(0),
fImage(NULL),
fFunction(NULL)
fFunction(NULL),
fSourceCode(NULL),
fSourceCodeState(STACK_SOURCE_NOT_LOADED)
{
fCpuState->AddReference();
}
@@ -26,6 +32,7 @@ StackFrame::StackFrame(stack_frame_type type, CpuState* cpuState,
StackFrame::~StackFrame()
{
SetSourceCode(NULL, STACK_SOURCE_NOT_LOADED);
SetImage(NULL);
SetFunction(NULL);
fCpuState->RemoveReference();
@@ -70,3 +77,51 @@ StackFrame::SetFunction(FunctionDebugInfo* function)
if (fFunction != NULL)
fFunction->AddReference();
}
void
StackFrame::SetSourceCode(SourceCode* source, stack_frame_source_state state)
{
if (fSourceCode != NULL)
fSourceCode->RemoveReference();
fSourceCode = source;
fSourceCodeState = state;
if (fSourceCode != NULL)
fSourceCode->AddReference();
// notify listeners
for (ListenerList::Iterator it = fListeners.GetIterator();
Listener* listener = it.Next();) {
listener->StackFrameSourceCodeChanged(this);
}
}
void
StackFrame::AddListener(Listener* listener)
{
fListeners.Add(listener);
}
void
StackFrame::RemoveListener(Listener* listener)
{
fListeners.Remove(listener);
}
// #pragma mark - Listener
StackFrame::Listener::~Listener()
{
}
void
StackFrame::Listener::StackFrameSourceCodeChanged(StackFrame* frame)
{
}
+39
View File
@@ -8,6 +8,7 @@
#include <OS.h>
#include <Referenceable.h>
#include <util/DoublyLinkedList.h>
#include "ArchitectureTypes.h"
@@ -20,12 +21,24 @@ enum stack_frame_type {
};
enum stack_frame_source_state {
STACK_SOURCE_NOT_LOADED,
STACK_SOURCE_LOADING,
STACK_SOURCE_LOADED,
STACK_SOURCE_UNAVAILABLE
};
class CpuState;
class Image;
class FunctionDebugInfo;
class SourceCode;
class StackFrame : public Referenceable {
public:
class Listener;
public:
StackFrame(stack_frame_type type,
CpuState* cpuState,
@@ -46,6 +59,19 @@ public:
FunctionDebugInfo* Function() const { return fFunction; }
void SetFunction(FunctionDebugInfo* function);
// mutable attributes follow (locking required)
SourceCode* GetSourceCode() const { return fSourceCode; }
stack_frame_source_state SourceCodeState() const
{ return fSourceCodeState; }
void SetSourceCode(SourceCode* source,
stack_frame_source_state state);
void AddListener(Listener* listener);
void RemoveListener(Listener* listener);
private:
typedef DoublyLinkedList<Listener> ListenerList;
private:
stack_frame_type fType;
CpuState* fCpuState;
@@ -53,6 +79,19 @@ private:
target_addr_t fReturnAddress;
Image* fImage;
FunctionDebugInfo* fFunction;
// mutable
SourceCode* fSourceCode;
stack_frame_source_state fSourceCodeState;
ListenerList fListeners;
};
class StackFrame::Listener : public DoublyLinkedListLinkImpl<Listener> {
public:
virtual ~Listener();
virtual void StackFrameSourceCodeChanged(StackFrame* frame);
// called with lock held
};
+73
View File
@@ -0,0 +1,73 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#include "Statement.h"
// #pragma mark - Statement
Statement::~Statement()
{
}
// #pragma mark - AbstractStatement
AbstractStatement::AbstractStatement(const SourceLocation& start,
const SourceLocation& end)
:
fStart(start),
fEnd(end)
{
}
SourceLocation
AbstractStatement::StartSourceLocation() const
{
return fStart;
}
SourceLocation
AbstractStatement::EndSourceLocation() const
{
return fEnd;
}
// #pragma mark - ContiguousStatement
ContiguousStatement::ContiguousStatement(const SourceLocation& start,
const SourceLocation& end, const TargetAddressRange& range)
:
AbstractStatement(start, end),
fRange(range)
{
}
TargetAddressRange
ContiguousStatement::CoveringAddressRange() const
{
return fRange;
}
int32
ContiguousStatement::CountAddressRanges() const
{
return 1;
}
TargetAddressRange
ContiguousStatement::AddressRangeAt(int32 index) const
{
return index == 0 ? fRange : TargetAddressRange();
}
+59
View File
@@ -0,0 +1,59 @@
/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef STATEMENT_H
#define STATEMENT_H
#include <Referenceable.h>
#include "ArchitectureTypes.h"
#include "SourceLocation.h"
#include "TargetAddressRange.h"
class Statement : public Referenceable {
public:
virtual ~Statement();
virtual SourceLocation StartSourceLocation() const = 0;
virtual SourceLocation EndSourceLocation() const = 0;
virtual TargetAddressRange CoveringAddressRange() const = 0;
virtual int32 CountAddressRanges() const = 0;
virtual TargetAddressRange AddressRangeAt(int32 index) const = 0;
};
class AbstractStatement : public Statement {
public:
AbstractStatement(const SourceLocation& start,
const SourceLocation& end);
virtual SourceLocation StartSourceLocation() const;
virtual SourceLocation EndSourceLocation() const;
protected:
SourceLocation fStart;
SourceLocation fEnd;
};
class ContiguousStatement : public AbstractStatement {
public:
ContiguousStatement(const SourceLocation& start,
const SourceLocation& end,
const TargetAddressRange& range);
virtual TargetAddressRange CoveringAddressRange() const;
virtual int32 CountAddressRanges() const;
virtual TargetAddressRange AddressRangeAt(int32 index) const;
protected:
TargetAddressRange fRange;
};
#endif // STATEMENT_H