diff --git a/src/apps/debugger/Jamfile b/src/apps/debugger/Jamfile index cea43c57d4..d5110bb21f 100644 --- a/src/apps/debugger/Jamfile +++ b/src/apps/debugger/Jamfile @@ -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 : : Debugger_demangler.o + Debugger_disasm_x86.o DebugAnalyzer_gui_table.o + libudis86.a 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 ; diff --git a/src/apps/debugger/Jobs.cpp b/src/apps/debugger/Jobs.cpp index d1d9c8f79f..0c857e856f 100644 --- a/src/apps/debugger/Jobs.cpp +++ b/src/apps/debugger/Jobs.cpp @@ -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 locker(fTeam); + if (error == B_OK) { + fStackFrame->SetSourceCode(sourceCode, STACK_SOURCE_LOADED); + sourceCode->RemoveReference(); + } else + fStackFrame->SetSourceCode(NULL, STACK_SOURCE_UNAVAILABLE); + + return error; +} diff --git a/src/apps/debugger/Jobs.h b/src/apps/debugger/Jobs.h index b6d1772f7b..1c70faf56d 100644 --- a/src/apps/debugger/Jobs.h +++ b/src/apps/debugger/Jobs.h @@ -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 diff --git a/src/apps/debugger/MessageCodes.h b/src/apps/debugger/MessageCodes.h index 6c21ce91e8..41b71b845a 100644 --- a/src/apps/debugger/MessageCodes.h +++ b/src/apps/debugger/MessageCodes.h @@ -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' }; diff --git a/src/apps/debugger/TeamDebugger.cpp b/src/apps/debugger/TeamDebugger.cpp index ffcd05cbe2..fa32f21e7a 100644 --- a/src/apps/debugger/TeamDebugger.cpp +++ b/src/apps/debugger/TeamDebugger.cpp @@ -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. } diff --git a/src/apps/debugger/TeamDebugger.h b/src/apps/debugger/TeamDebugger.h index 9f5b41c781..9b3087d49a 100644 --- a/src/apps/debugger/TeamDebugger.h +++ b/src/apps/debugger/TeamDebugger.h @@ -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); diff --git a/src/apps/debugger/arch/Architecture.h b/src/apps/debugger/arch/Architecture.h index 5d9d415acd..80d207aecd 100644 --- a/src/apps/debugger/arch/Architecture.h +++ b/src/apps/debugger/arch/Architecture.h @@ -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, diff --git a/src/apps/debugger/arch/TargetAddressRange.h b/src/apps/debugger/arch/TargetAddressRange.h new file mode 100644 index 0000000000..83bb725c7b --- /dev/null +++ b/src/apps/debugger/arch/TargetAddressRange.h @@ -0,0 +1,87 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * 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 diff --git a/src/apps/debugger/arch/x86/ArchitectureX86.cpp b/src/apps/debugger/arch/x86/ArchitectureX86.cpp index 6929756544..78943f356a 100644 --- a/src/apps/debugger/arch/x86/ArchitectureX86.cpp +++ b/src/apps/debugger/arch/x86/ArchitectureX86.cpp @@ -7,11 +7,18 @@ #include +#include + #include #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 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) diff --git a/src/apps/debugger/arch/x86/ArchitectureX86.h b/src/apps/debugger/arch/x86/ArchitectureX86.h index cf7d65f543..d142be72cd 100644 --- a/src/apps/debugger/arch/x86/ArchitectureX86.h +++ b/src/apps/debugger/arch/x86/ArchitectureX86.h @@ -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, diff --git a/src/apps/debugger/arch/x86/disasm/DisassemblerX86.cpp b/src/apps/debugger/arch/x86/disasm/DisassemblerX86.cpp new file mode 100644 index 0000000000..7f026c3e47 --- /dev/null +++ b/src/apps/debugger/arch/x86/disasm/DisassemblerX86.cpp @@ -0,0 +1,88 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Copyright 2008, François Revol, revol@free.fr + * Distributed under the terms of the MIT License. + */ + +#include "DisassemblerX86.h" + +#include + +#include "udis86.h" + +#include + + +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; +} + diff --git a/src/apps/debugger/arch/x86/disasm/DisassemblerX86.h b/src/apps/debugger/arch/x86/disasm/DisassemblerX86.h new file mode 100644 index 0000000000..c93a443303 --- /dev/null +++ b/src/apps/debugger/arch/x86/disasm/DisassemblerX86.h @@ -0,0 +1,37 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef DISASSEMBLER_X86_H +#define DISASSEMBLER_X86_H + +#include + +#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 diff --git a/src/apps/debugger/arch/x86/disasm/Jamfile b/src/apps/debugger/arch/x86/disasm/Jamfile new file mode 100644 index 0000000000..0fd56ee5c7 --- /dev/null +++ b/src/apps/debugger/arch/x86/disasm/Jamfile @@ -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 +; diff --git a/src/apps/debugger/debug_info/DebugInfo.h b/src/apps/debugger/debug_info/DebugInfo.h index 60655d8ae6..2db8fa4f23 100644 --- a/src/apps/debugger/debug_info/DebugInfo.h +++ b/src/apps/debugger/debug_info/DebugInfo.h @@ -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 }; diff --git a/src/apps/debugger/debug_info/DebuggerDebugInfo.cpp b/src/apps/debugger/debug_info/DebuggerDebugInfo.cpp index 6a5ef8ff66..96d6e39cf5 100644 --- a/src/apps/debugger/debug_info/DebuggerDebugInfo.cpp +++ b/src/apps/debugger/debug_info/DebuggerDebugInfo.cpp @@ -5,8 +5,12 @@ #include "DebuggerDebugInfo.h" +#include #include +#include + +#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) { diff --git a/src/apps/debugger/debug_info/DebuggerDebugInfo.h b/src/apps/debugger/debug_info/DebuggerDebugInfo.h index 8d964ede45..11849a2d06 100644 --- a/src/apps/debugger/debug_info/DebuggerDebugInfo.h +++ b/src/apps/debugger/debug_info/DebuggerDebugInfo.h @@ -34,6 +34,8 @@ public: CpuState* cpuState, StackFrame*& _previousFrame, CpuState*& _previousCpuState); + virtual status_t LoadSourceCode(FunctionDebugInfo* function, + SourceCode*& _sourceCode); private: typedef BObjectList SymbolList; diff --git a/src/apps/debugger/gui/team_window/SourceView.cpp b/src/apps/debugger/gui/team_window/SourceView.cpp new file mode 100644 index 0000000000..2d5097135e --- /dev/null +++ b/src/apps/debugger/gui/team_window/SourceView.cpp @@ -0,0 +1,368 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "SourceView.h" + +#include +#include + +#include + +#include +#include + +#include + +#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 locker(fDebugModel); + + SourceCode* sourceCode = fStackFrame != NULL + ? fStackFrame->GetSourceCode() : NULL; + Reference 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() +{ +} diff --git a/src/apps/debugger/gui/team_window/SourceView.h b/src/apps/debugger/gui/team_window/SourceView.h new file mode 100644 index 0000000000..5ac83e00bb --- /dev/null +++ b/src/apps/debugger/gui/team_window/SourceView.h @@ -0,0 +1,78 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef SOURCE_VIEW_H +#define SOURCE_VIEW_H + +#include +#include + + +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 diff --git a/src/apps/debugger/gui/team_window/TeamWindow.cpp b/src/apps/debugger/gui/team_window/TeamWindow.cpp index 878026fcb1..01c4392bfb 100644 --- a/src/apps/debugger/gui/team_window/TeamWindow.cpp +++ b/src/apps/debugger/gui/team_window/TeamWindow.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -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 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 diff --git a/src/apps/debugger/gui/team_window/TeamWindow.h b/src/apps/debugger/gui/team_window/TeamWindow.h index 3a6eda8c55..ef3af6e8df 100644 --- a/src/apps/debugger/gui/team_window/TeamWindow.h +++ b/src/apps/debugger/gui/team_window/TeamWindow.h @@ -8,6 +8,8 @@ #include #include +#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; diff --git a/src/apps/debugger/model/SourceCode.cpp b/src/apps/debugger/model/SourceCode.cpp new file mode 100644 index 0000000000..d93fb2062b --- /dev/null +++ b/src/apps/debugger/model/SourceCode.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "SourceCode.h" + +#include +#include + +#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; +} diff --git a/src/apps/debugger/model/SourceCode.h b/src/apps/debugger/model/SourceCode.h new file mode 100644 index 0000000000..cd81ce27a8 --- /dev/null +++ b/src/apps/debugger/model/SourceCode.h @@ -0,0 +1,43 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef SOURCE_CODE_H +#define SOURCE_CODE_H + +#include +#include + +#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 LineList; + typedef BObjectList StatementList; + +private: + LineList fLines; + StatementList fStatements; +}; + + +#endif // SOURCE_CODE_H diff --git a/src/apps/debugger/model/SourceLocation.h b/src/apps/debugger/model/SourceLocation.h new file mode 100644 index 0000000000..acb1745039 --- /dev/null +++ b/src/apps/debugger/model/SourceLocation.h @@ -0,0 +1,60 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef SOURCE_LOCATION_H +#define SOURCE_LOCATION_H + +#include + + +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 diff --git a/src/apps/debugger/model/StackFrame.cpp b/src/apps/debugger/model/StackFrame.cpp index 6047c40aba..649d6c065e 100644 --- a/src/apps/debugger/model/StackFrame.cpp +++ b/src/apps/debugger/model/StackFrame.cpp @@ -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) +{ +} diff --git a/src/apps/debugger/model/StackFrame.h b/src/apps/debugger/model/StackFrame.h index 4c99c36c27..9f954963ef 100644 --- a/src/apps/debugger/model/StackFrame.h +++ b/src/apps/debugger/model/StackFrame.h @@ -8,6 +8,7 @@ #include #include +#include #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 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 { +public: + virtual ~Listener(); + + virtual void StackFrameSourceCodeChanged(StackFrame* frame); + // called with lock held }; diff --git a/src/apps/debugger/model/Statement.cpp b/src/apps/debugger/model/Statement.cpp new file mode 100644 index 0000000000..539703f4b6 --- /dev/null +++ b/src/apps/debugger/model/Statement.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * 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(); +} diff --git a/src/apps/debugger/model/Statement.h b/src/apps/debugger/model/Statement.h new file mode 100644 index 0000000000..ee802b5d29 --- /dev/null +++ b/src/apps/debugger/model/Statement.h @@ -0,0 +1,59 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef STATEMENT_H +#define STATEMENT_H + +#include + +#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