diff --git a/src/apps/debugger/gui/team_window/SourceView.cpp b/src/apps/debugger/gui/team_window/SourceView.cpp index 2d5097135e..849a533dd7 100644 --- a/src/apps/debugger/gui/team_window/SourceView.cpp +++ b/src/apps/debugger/gui/team_window/SourceView.cpp @@ -11,32 +11,118 @@ #include #include +#include #include #include +#include #include "SourceCode.h" -#include "StackFrame.h" +#include "StackTrace.h" +#include "Statement.h" #include "TeamDebugModel.h" static const int32 kLeftTextMargin = 3; +static const float kMinViewHeight = 80.0f; -class SourceView::MarkerView : public BView { +class SourceView::BaseView : public BView { +public: + BaseView(const char* name, SourceView* sourceView, FontInfo* fontInfo) + : + BView(name, B_WILL_DRAW | B_SUBPIXEL_PRECISE), + fSourceView(sourceView), + fFontInfo(fontInfo), + fSourceCode(NULL) + { + } + + virtual void SetSourceCode(SourceCode* sourceCode) + { + fSourceCode = sourceCode; + + InvalidateLayout(); + Invalidate(); + } + + virtual BSize PreferredSize() + { + return MinSize(); + } + +protected: + int32 LineCount() const + { + return fSourceCode != NULL ? fSourceCode->CountLines() : 0; + } + + float TotalHeight() const + { + float height = LineCount() * fFontInfo->lineHeight - 1; + return std::max(height, kMinViewHeight); + } + + void GetLineRange(BRect rect, int32& minLine, int32& maxLine) + { + int32 lineHeight = (int32)fFontInfo->lineHeight; + minLine = (int32)rect.top / lineHeight; + maxLine = ((int32)ceilf(rect.bottom) + lineHeight - 1) / lineHeight; + minLine = std::max(minLine, 0L); + maxLine = std::min(maxLine, fSourceCode->CountLines() - 1); + } + + +protected: + SourceView* fSourceView; + FontInfo* fFontInfo; + SourceCode* fSourceCode; +}; + + +class SourceView::MarkerView : public BaseView { public: MarkerView(SourceView* sourceView, FontInfo* fontInfo) : - BView("source marker view", B_WILL_DRAW), - fSourceView(sourceView), - fFontInfo(fontInfo) + BaseView("source marker view", sourceView, fontInfo), + fStackTrace(NULL), + fStackFrame(NULL), + fMarkers(20, true), + fMarkersValid(false) { SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); } + ~MarkerView() + { + } + + virtual void SetSourceCode(SourceCode* sourceCode) + { + fMarkers.MakeEmpty(); + fMarkersValid = false; + BaseView::SetSourceCode(sourceCode); + } + + void SetStackTrace(StackTrace* stackTrace) + { + fStackTrace = stackTrace; + fMarkers.MakeEmpty(); + fMarkersValid = false; + Invalidate(); + } + + void SetStackFrame(StackFrame* stackFrame) + { + fStackFrame = stackFrame; + fMarkers.MakeEmpty(); + fMarkersValid = false; + Invalidate(); + } + virtual BSize MinSize() { - return BSize(40, 10); + return BSize(40, TotalHeight()); } virtual BSize MaxSize() @@ -44,49 +130,184 @@ public: return BSize(MinSize().width, B_SIZE_UNLIMITED); } - virtual BSize PreferredSize() - { - return MinSize(); - } - virtual void Draw(BRect updateRect) { + _UpdateMarkers(); + + if (fSourceCode == NULL || fMarkers.IsEmpty()) + return; + + // get the lines intersecting with the update rect + int32 minLine, maxLine; + GetLineRange(updateRect, minLine, maxLine); + + // draw the markers + float width = Bounds().Width(); + // TODO: The markers should be sorted, so we don't need to iterate over + // all of them. + for (int32 i = 0; Marker* marker = fMarkers.ItemAt(i); i++) { + int32 line = marker->Line(); + if (line < minLine || line > maxLine) + continue; + + float y = (float)line * fFontInfo->lineHeight; + BRect rect(0, y, width, y + fFontInfo->lineHeight - 1); + marker->Draw(this, rect); + } + + // TODO: Draw possible breakpoint marks! } private: - SourceView* fSourceView; - FontInfo* fFontInfo; + struct Marker { + Marker(uint32 line) + : + fLine(line) + { + } + + virtual ~Marker() + { + } + + uint32 Line() const + { + return fLine; + } + + virtual void Draw(MarkerView* view, BRect rect) = 0; + + private: + uint32 fLine; + }; + + struct InstructionPointerMarker : Marker { + InstructionPointerMarker(uint32 line, bool topIP, bool currentIP) + : + Marker(line), + fIsTopIP(topIP), + fIsCurrentIP(currentIP) + { + } + + virtual void Draw(MarkerView* view, BRect rect) + { + // Get the arrow color -- for the top IP, if current, we use blue, + // otherwise a gray. + rgb_color color; + if (fIsCurrentIP && fIsTopIP) { + color.set_to(0, 0, 255, 255); + } else { + color = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), + B_DARKEN_3_TINT); + } + + // Draw a filled array for the current IP, otherwise just an + // outline. + BPoint tip(rect.right - 3.5f, floorf((rect.top + rect.bottom) / 2)); + if (fIsCurrentIP) { + _DrawArrow(view, tip, BSize(10, 10), BSize(5, 5), color, true); + } else { + _DrawArrow(view, tip + BPoint(-0.5f, 0), BSize(9, 8), + BSize(5, 4), color, false); + } + } + + private: + void _DrawArrow(BView* view, BPoint tip, BSize size, BSize base, + const rgb_color& color, bool fill) + { + view->SetHighColor(color); + + float baseTop = tip.y - base.height / 2; + float baseBottom = tip.y + base.height / 2; + float top = tip.y - size.height / 2; + float bottom = tip.y + size.height / 2; + float left = tip.x - size.width; + float middle = left + base.width; + + BPoint points[7]; + points[0].Set(tip.x, tip.y); + points[1].Set(middle, top); + points[2].Set(middle, baseTop); + points[3].Set(left, baseTop); + points[4].Set(left, baseBottom); + points[5].Set(middle, baseBottom); + points[6].Set(middle, bottom); + + if (fill) + view->FillPolygon(points, 7); + else + view->StrokePolygon(points, 7); + } + + private: + bool fIsTopIP; + bool fIsCurrentIP; + }; + + typedef BObjectList MarkerList; + +private: + void _UpdateMarkers() + { + if (fMarkersValid) + return; + + fMarkers.MakeEmpty(); + + if (fSourceCode != NULL && fStackTrace != NULL) { + for (int32 i = 0; StackFrame* frame = fStackTrace->FrameAt(i); + i++) { + target_addr_t ip = frame->InstructionPointer(); + Statement* statement = fSourceCode->StatementAtAddress(ip); + if (statement == NULL) + continue; + uint32 line = statement->StartSourceLocation().Line(); + if (line >= (uint32)LineCount()) + continue; + + Marker* marker = new(std::nothrow) InstructionPointerMarker( + line, i == 0, frame == fStackFrame); + if (marker == NULL || !fMarkers.AddItem(marker)) { + delete marker; + break; + } + } + } + // TODO: Filter duplicate IP markers (recursive functions)! + + fMarkersValid = true; + } + +private: + StackTrace* fStackTrace; + StackFrame* fStackFrame; + MarkerList fMarkers; + bool fMarkersValid; }; -class SourceView::TextView : public BView { +class SourceView::TextView : public BaseView { public: TextView(SourceView* sourceView, FontInfo* fontInfo) : - BView("source text view", B_WILL_DRAW), - fSourceView(sourceView), - fFontInfo(fontInfo), - fSourceCode(NULL), + BaseView("source text view", sourceView, fontInfo), fMaxLineWidth(-1) { SetViewColor(ui_color(B_DOCUMENT_BACKGROUND_COLOR)); fTextColor = ui_color(B_DOCUMENT_TEXT_COLOR); } - void SetSourceCode(SourceCode* sourceCode) + virtual void SetSourceCode(SourceCode* sourceCode) { - fSourceCode = sourceCode; fMaxLineWidth = -1; - - InvalidateLayout(); - Invalidate(); + BaseView::SetSourceCode(sourceCode); } virtual BSize MinSize() { - float height = _LineCount() * fFontInfo->lineHeight; - return BSize(kLeftTextMargin + _MaxLineWidth() - 1, - std::max(height - 1, 100.0f)); + return BSize(kLeftTextMargin + _MaxLineWidth() - 1, TotalHeight()); } virtual BSize MaxSize() @@ -94,23 +315,14 @@ public: 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); + int32 minLine, maxLine; + GetLineRange(updateRect, minLine, maxLine); // draw the affected lines SetHighColor(fTextColor); @@ -123,11 +335,6 @@ public: } private: - int32 _LineCount() const - { - return fSourceCode != NULL ? fSourceCode->CountLines() : 0; - } - float _MaxLineWidth() { if (fMaxLineWidth >= 0) @@ -145,9 +352,6 @@ private: } private: - SourceView* fSourceView; - FontInfo* fFontInfo; - SourceCode* fSourceCode; float fMaxLineWidth; rgb_color fTextColor; }; @@ -160,6 +364,7 @@ SourceView::SourceView(TeamDebugModel* debugModel, Listener* listener) : BView("source view", 0), fDebugModel(debugModel), + fStackTrace(NULL), fStackFrame(NULL), fSourceCode(NULL), fMarkerView(NULL), @@ -177,6 +382,8 @@ SourceView::SourceView(TeamDebugModel* debugModel, Listener* listener) SourceView::~SourceView() { SetStackFrame(NULL); + SetStackTrace(NULL); + SetSourceCode(NULL); } @@ -204,49 +411,112 @@ SourceView::UnsetListener() void -SourceView::SetStackFrame(StackFrame* frame) +SourceView::SetStackTrace(StackTrace* stackTrace) { -printf("SourceView::SetStackFrame(%p)\n", frame); - if (frame == fStackFrame) +printf("SourceView::SetStackTrace(%p)\n", stackTrace); + if (stackTrace == fStackTrace) return; - if (fStackFrame != NULL) - fStackFrame->RemoveReference(); + if (fStackTrace != NULL) { + fMarkerView->SetStackTrace(NULL); + fStackTrace->RemoveReference(); + } - fStackFrame = frame; + fStackTrace = stackTrace; - if (fStackFrame != NULL) - fStackFrame->AddReference(); + if (fStackTrace != NULL) + fStackTrace->AddReference(); - UpdateSourceCode(); + fMarkerView->SetStackTrace(fStackTrace); } void -SourceView::UpdateSourceCode() +SourceView::SetStackFrame(StackFrame* stackFrame) { - // get a reference to the source code - AutoLocker locker(fDebugModel); + if (stackFrame == fStackFrame) + return; - SourceCode* sourceCode = fStackFrame != NULL - ? fStackFrame->GetSourceCode() : NULL; - Reference sourceCodeReference(sourceCode); + if (fStackFrame != NULL) { + fMarkerView->SetStackFrame(NULL); + fStackFrame->RemoveReference(); + } - locker.Unlock(); + fStackFrame = stackFrame; + if (fStackFrame != NULL) + fStackFrame->AddReference(); + + fMarkerView->SetStackFrame(fStackFrame); + + if (fStackFrame != NULL) + ScrollToAddress(fStackFrame->InstructionPointer()); +} + + +void +SourceView::SetSourceCode(SourceCode* sourceCode) +{ // set the source code, if it changed if (sourceCode == fSourceCode) return; if (fSourceCode != NULL) { fTextView->SetSourceCode(NULL); + fMarkerView->SetSourceCode(NULL); fSourceCode->RemoveReference(); } - fSourceCode = sourceCodeReference.Detach(); + fSourceCode = sourceCode; + + if (fSourceCode != NULL) + fSourceCode->AddReference(); fTextView->SetSourceCode(fSourceCode); + fMarkerView->SetSourceCode(fSourceCode); _UpdateScrollBars(); + + if (fStackFrame != NULL) + ScrollToAddress(fStackFrame->InstructionPointer()); +} + + +bool +SourceView::ScrollToAddress(target_addr_t address) +{ + if (fSourceCode == NULL) + return false; + + Statement* statement = fSourceCode->StatementAtAddress(address); + if (statement == NULL) + return false; + + return ScrollToLine(statement->StartSourceLocation().Line()); +} + + +bool +SourceView::ScrollToLine(uint32 line) +{ +printf("SourceView::ScrollToLine(%lu)\n", line); + if (fSourceCode == NULL || line >= (uint32)fSourceCode->CountLines()) + return false; + + float top = (float)line * fFontInfo.lineHeight; + float bottom = top + fFontInfo.lineHeight - 1; + + BRect visible = _VisibleRect(); + + // If not visible at all, scroll to the center, otherwise scroll so that at + // least one more line is visible. + if (top >= visible.bottom || bottom <= visible.top) + ScrollTo(visible.left, top - (visible.Height() + 1) / 2); + else if (top - fFontInfo.lineHeight < visible.top) + ScrollBy(0, top - fFontInfo.lineHeight - visible.top); + else if (bottom + fFontInfo.lineHeight > visible.bottom) + ScrollBy(0, bottom + fFontInfo.lineHeight - visible.bottom); + + return true; } @@ -360,6 +630,13 @@ SourceView::_DataRectSize() const } +BRect +SourceView::_VisibleRect() const +{ + return BRect(Bounds().LeftTop(), Frame().Size()); +} + + // #pragma mark - Listener diff --git a/src/apps/debugger/gui/team_window/SourceView.h b/src/apps/debugger/gui/team_window/SourceView.h index 5ac83e00bb..bca38e93dd 100644 --- a/src/apps/debugger/gui/team_window/SourceView.h +++ b/src/apps/debugger/gui/team_window/SourceView.h @@ -8,9 +8,12 @@ #include #include +#include "ArchitectureTypes.h" + class SourceCode; class StackFrame; +class StackTrace; class TeamDebugModel; @@ -29,8 +32,12 @@ public: void UnsetListener(); - void SetStackFrame(StackFrame* frame); - void UpdateSourceCode(); + void SetStackTrace(StackTrace* stackTrace); + void SetStackFrame(StackFrame* stackFrame); + void SetSourceCode(SourceCode* sourceCode); + + bool ScrollToAddress(target_addr_t address); + bool ScrollToLine(uint32 line); virtual void TargetedByScrollView(BScrollView* scrollView); @@ -41,6 +48,7 @@ public: virtual void DoLayout(); private: + class BaseView; class MarkerView; class TextView; @@ -54,9 +62,11 @@ private: void _Init(); void _UpdateScrollBars(); BSize _DataRectSize() const; + BRect _VisibleRect() const; private: TeamDebugModel* fDebugModel; + StackTrace* fStackTrace; StackFrame* fStackFrame; SourceCode* fSourceCode; MarkerView* fMarkerView; diff --git a/src/apps/debugger/gui/team_window/StackTraceView.cpp b/src/apps/debugger/gui/team_window/StackTraceView.cpp index b8b9f1239e..da18fb067a 100644 --- a/src/apps/debugger/gui/team_window/StackTraceView.cpp +++ b/src/apps/debugger/gui/team_window/StackTraceView.cpp @@ -209,6 +209,22 @@ StackTraceView::SetStackTrace(StackTrace* stackTrace) } +void +StackTraceView::SetStackFrame(StackFrame* stackFrame) +{ + if (fStackTrace != NULL && stackFrame != NULL) { + for (int32 i = 0; StackFrame* other = fStackTrace->FrameAt(i); i++) { + if (stackFrame == other) { + fFramesTable->SelectRow(i, false); + return; + } + } + } + + fFramesTable->DeselectAllRows(); +} + + void StackTraceView::TableSelectionChanged(Table* table) { diff --git a/src/apps/debugger/gui/team_window/StackTraceView.h b/src/apps/debugger/gui/team_window/StackTraceView.h index 8a890e1c4d..fc751f2eff 100644 --- a/src/apps/debugger/gui/team_window/StackTraceView.h +++ b/src/apps/debugger/gui/team_window/StackTraceView.h @@ -28,6 +28,7 @@ public: void UnsetListener(); void SetStackTrace(StackTrace* stackTrace); + void SetStackFrame(StackFrame* stackFrame); private: class FramesTableModel; diff --git a/src/apps/debugger/gui/team_window/TeamWindow.cpp b/src/apps/debugger/gui/team_window/TeamWindow.cpp index 01c4392bfb..d5b5003e7e 100644 --- a/src/apps/debugger/gui/team_window/TeamWindow.cpp +++ b/src/apps/debugger/gui/team_window/TeamWindow.cpp @@ -21,6 +21,7 @@ #include "ImageListView.h" #include "MessageCodes.h" #include "RegisterView.h" +#include "SourceCode.h" #include "StackTrace.h" #include "StackTraceView.h" #include "TeamDebugModel.h" @@ -35,7 +36,9 @@ TeamWindow::TeamWindow(TeamDebugModel* debugModel, Listener* listener) B_ASYNCHRONOUS_CONTROLS), fDebugModel(debugModel), fActiveThread(NULL), + fActiveStackTrace(NULL), fActiveStackFrame(NULL), + fActiveSourceCode(NULL), fListener(listener), fTabView(NULL), fLocalsTabView(NULL), @@ -69,6 +72,15 @@ TeamWindow::~TeamWindow() fSourceView->UnsetListener(); fDebugModel->GetTeam()->RemoveListener(this); + + if (fActiveSourceCode != NULL) + fActiveSourceCode->RemoveReference(); + if (fActiveStackFrame != NULL) + fActiveStackFrame->RemoveReference(); + if (fActiveStackTrace != NULL) + fActiveStackTrace->RemoveReference(); + if (fActiveThread != NULL) + fActiveThread->RemoveReference(); } @@ -283,8 +295,14 @@ TeamWindow::_SetActiveThread(::Thread* thread) if (thread == fActiveThread) return; + if (fActiveThread != NULL) + fActiveThread->RemoveReference(); + fActiveThread = thread; + if (fActiveThread != NULL) + fActiveThread->AddReference(); + AutoLocker locker(fDebugModel); _UpdateRunButtons(); @@ -295,11 +313,33 @@ TeamWindow::_SetActiveThread(::Thread* thread) locker.Unlock(); - fStackTraceView->SetStackTrace(stackTrace); + _SetActiveStackTrace(stackTrace); _UpdateCpuState(); } +void +TeamWindow::_SetActiveStackTrace(StackTrace* stackTrace) +{ + if (stackTrace == fActiveStackTrace) + return; + + if (fActiveStackTrace != NULL) + fActiveStackTrace->RemoveReference(); + + fActiveStackTrace = stackTrace; + + if (fActiveStackTrace != NULL) + fActiveStackTrace->AddReference(); + + fStackTraceView->SetStackTrace(fActiveStackTrace); + fSourceView->SetStackTrace(fActiveStackTrace); + + if (fActiveStackTrace != NULL) + _SetActiveStackFrame(fActiveStackTrace->FrameAt(0)); +} + + void TeamWindow::_SetActiveStackFrame(StackFrame* frame) { @@ -315,10 +355,18 @@ TeamWindow::_SetActiveStackFrame(StackFrame* frame) fActiveStackFrame = frame; + SourceCode* sourceCode = NULL; + Reference sourceCodeReference; + bool setSourceCode = false; + if (fActiveStackFrame != NULL) { fActiveStackFrame->AddReference(); fActiveStackFrame->AddListener(this); + sourceCode = fActiveStackFrame->GetSourceCode(); + sourceCodeReference.SetTo(sourceCode); + setSourceCode = true; + // If the source code is not loaded yet, request it. if (fActiveStackFrame->SourceCodeState() == STACK_SOURCE_NOT_LOADED) fListener->StackFrameSourceCodeRequested(this, fActiveStackFrame); @@ -328,10 +376,32 @@ TeamWindow::_SetActiveStackFrame(StackFrame* frame) locker.Unlock(); + if (setSourceCode) + _SetActiveSourceCode(sourceCode); + + fStackTraceView->SetStackFrame(fActiveStackFrame); fSourceView->SetStackFrame(fActiveStackFrame); } +void +TeamWindow::_SetActiveSourceCode(SourceCode* sourceCode) +{ + if (sourceCode == fActiveSourceCode) + return; + + if (fActiveSourceCode != NULL) + fActiveSourceCode->RemoveReference(); + + fActiveSourceCode = sourceCode; + + if (fActiveSourceCode != NULL) + fActiveSourceCode->AddReference(); + + fSourceView->SetSourceCode(fActiveSourceCode); +} + + void TeamWindow::_UpdateCpuState() { @@ -427,14 +497,26 @@ TeamWindow::_HandleStackTraceChanged(thread_id threadID) locker.Unlock(); - fStackTraceView->SetStackTrace(stackTrace); + _SetActiveStackTrace(stackTrace); } void TeamWindow::_HandleSourceCodeChanged() { - fSourceView->UpdateSourceCode(); + // If we don't have an active stack frame anymore, the message is obsolete. + if (fActiveStackFrame == NULL) + return; + + // get a reference to the source code + AutoLocker locker(fDebugModel); + + SourceCode* sourceCode = fActiveStackFrame->GetSourceCode(); + Reference sourceCodeReference(sourceCode); + + locker.Unlock(); + + _SetActiveSourceCode(sourceCode); } diff --git a/src/apps/debugger/gui/team_window/TeamWindow.h b/src/apps/debugger/gui/team_window/TeamWindow.h index ef3af6e8df..8c89454592 100644 --- a/src/apps/debugger/gui/team_window/TeamWindow.h +++ b/src/apps/debugger/gui/team_window/TeamWindow.h @@ -19,6 +19,7 @@ class BButton; class BTabView; class ImageListView; class RegisterView; +class SourceCode; class TeamDebugModel; @@ -64,7 +65,9 @@ private: void _Init(); void _SetActiveThread(::Thread* thread); + void _SetActiveStackTrace(StackTrace* stackTrace); void _SetActiveStackFrame(StackFrame* frame); + void _SetActiveSourceCode(SourceCode* sourceCode); void _UpdateCpuState(); void _UpdateRunButtons(); @@ -76,7 +79,9 @@ private: private: TeamDebugModel* fDebugModel; ::Thread* fActiveThread; + StackTrace* fActiveStackTrace; StackFrame* fActiveStackFrame; + SourceCode* fActiveSourceCode; Listener* fListener; BTabView* fTabView; BTabView* fLocalsTabView;