* SourceView can now show markers for stack frame instruction pointers.

Furthermore it automatically scrolls to the current IP of the currently
  selected stack frame.
* TeamWindow:
  - Be lazy setting the SourceCode, i.e. as long as no other source shall be
    shown (e.g. another stack frame is selected) we keep the previous one.
  - When a new stack strace is set, automatically select the top-most frame.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31170 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2009-06-21 23:44:18 +00:00
parent 49ad0141cf
commit ede842bb1d
6 changed files with 458 additions and 67 deletions
+339 -62
View File
@@ -11,32 +11,118 @@
#include <stdio.h>
#include <LayoutUtils.h>
#include <Polygon.h>
#include <ScrollBar.h>
#include <AutoLocker.h>
#include <ObjectList.h>
#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<Marker> 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<TeamDebugModel> locker(fDebugModel);
if (stackFrame == fStackFrame)
return;
SourceCode* sourceCode = fStackFrame != NULL
? fStackFrame->GetSourceCode() : NULL;
Reference<SourceCode> 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
+12 -2
View File
@@ -8,9 +8,12 @@
#include <Font.h>
#include <View.h>
#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;
@@ -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)
{
@@ -28,6 +28,7 @@ public:
void UnsetListener();
void SetStackTrace(StackTrace* stackTrace);
void SetStackFrame(StackFrame* stackFrame);
private:
class FramesTableModel;
@@ -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<TeamDebugModel> 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<SourceCode> 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<TeamDebugModel> locker(fDebugModel);
SourceCode* sourceCode = fActiveStackFrame->GetSourceCode();
Reference<SourceCode> sourceCodeReference(sourceCode);
locker.Unlock();
_SetActiveSourceCode(sourceCode);
}
@@ -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;