From 6381d6cc4ee3d8c4efc92523538d63e16cf0b22f Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Mon, 22 Jun 2009 21:51:32 +0000 Subject: [PATCH] * Added Breakpoint class and extended TeamDebugModel to manage the objects. Extended the event mechanism respectively. * Added support to SourceView to display breakpoints and manipulate them. * Extended TeamDebugger to install/uninstall breakpoints. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31189 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/debugger/Jamfile | 1 + src/apps/debugger/MessageCodes.h | 5 +- src/apps/debugger/TeamDebugger.cpp | 170 ++++++++ src/apps/debugger/TeamDebugger.h | 13 + .../debugger/gui/team_window/SourceView.cpp | 403 ++++++++++++++++-- .../debugger/gui/team_window/SourceView.h | 10 +- .../debugger/gui/team_window/TeamWindow.cpp | 45 +- .../debugger/gui/team_window/TeamWindow.h | 21 +- src/apps/debugger/model/Breakpoint.cpp | 93 ++++ src/apps/debugger/model/Breakpoint.h | 68 +++ src/apps/debugger/model/DisassembledCode.cpp | 17 +- src/apps/debugger/model/DisassembledCode.h | 2 + src/apps/debugger/model/SourceCode.h | 4 +- src/apps/debugger/model/Statement.cpp | 17 +- src/apps/debugger/model/Statement.h | 11 +- src/apps/debugger/model/TeamDebugModel.cpp | 152 +++++++ src/apps/debugger/model/TeamDebugModel.h | 60 ++- 17 files changed, 1031 insertions(+), 61 deletions(-) create mode 100644 src/apps/debugger/model/Breakpoint.cpp create mode 100644 src/apps/debugger/model/Breakpoint.h diff --git a/src/apps/debugger/Jamfile b/src/apps/debugger/Jamfile index 825c366501..f33bf5b343 100644 --- a/src/apps/debugger/Jamfile +++ b/src/apps/debugger/Jamfile @@ -57,6 +57,7 @@ Application Debugger : ThreadListView.cpp # model + Breakpoint.cpp DisassembledCode.cpp Image.cpp ImageInfo.cpp diff --git a/src/apps/debugger/MessageCodes.h b/src/apps/debugger/MessageCodes.h index 41b71b845a..7fca6562a0 100644 --- a/src/apps/debugger/MessageCodes.h +++ b/src/apps/debugger/MessageCodes.h @@ -12,11 +12,14 @@ enum { MSG_THREAD_STEP_OVER = 'stov', MSG_THREAD_STEP_INTO = 'stin', MSG_THREAD_STEP_OUT = 'stou', + MSG_SET_BREAKPONT = 'sbrk', + MSG_CLEAR_BREAKPONT = 'cbrk', MSG_THREAD_STATE_CHANGED = 'tsch', MSG_THREAD_CPU_STATE_CHANGED = 'tcsc', MSG_THREAD_STACK_TRACE_CHANGED = 'tstc', - MSG_STACK_FRAME_SOURCE_CODE_CHANGED = 'sfsc' + MSG_STACK_FRAME_SOURCE_CODE_CHANGED = 'sfsc', + MSG_USER_BREAKPOINT_CHANGED = 'ubrc' }; diff --git a/src/apps/debugger/TeamDebugger.cpp b/src/apps/debugger/TeamDebugger.cpp index fa32f21e7a..40ffa4b05e 100644 --- a/src/apps/debugger/TeamDebugger.cpp +++ b/src/apps/debugger/TeamDebugger.cpp @@ -5,10 +5,12 @@ #include "TeamDebugger.h" +#include #include #include +#include #include #include @@ -19,6 +21,7 @@ #include "DebuggerInterface.h" #include "Jobs.h" #include "MessageCodes.h" +#include "Statement.h" #include "TeamDebugModel.h" @@ -198,6 +201,24 @@ TeamDebugger::MessageReceived(BMessage* message) break; } + case MSG_SET_BREAKPONT: + case MSG_CLEAR_BREAKPONT: + { + uint64 address; + if (message->FindUInt64("address", &address) != B_OK) + break; + + if (message->what == MSG_SET_BREAKPONT) { + bool enabled; + if (message->FindBool("enabled", &enabled) != B_OK) + enabled = true; + + _HandleSetUserBreakpoint(address, enabled); + } else + _HandleClearUserBreakpoint(address); + break; + } + case MSG_THREAD_STATE_CHANGED: { int32 threadID; @@ -267,6 +288,25 @@ TeamDebugger::ThreadActionRequested(TeamWindow* window, thread_id threadID, } +void +TeamDebugger::SetBreakpointRequested(target_addr_t address, bool enabled) +{ + BMessage message(MSG_SET_BREAKPONT); + message.AddUInt64("address", (uint64)address); + message.AddBool("enabled", enabled); + PostMessage(&message); +} + + +void +TeamDebugger::ClearBreakpointRequested(target_addr_t address) +{ + BMessage message(MSG_CLEAR_BREAKPONT); + message.AddUInt64("address", (uint64)address); + PostMessage(&message); +} + + bool TeamDebugger::TeamWindowQuitRequested(TeamWindow* window) { @@ -569,6 +609,73 @@ TeamDebugger::_SetThreadState(::Thread* thread, uint32 state, } +status_t +TeamDebugger::_SetUserBreakpoint(target_addr_t address, bool enabled) +{ + user_breakpoint_state state = enabled + ? USER_BREAKPOINT_ENABLED : USER_BREAKPOINT_DISABLED; + + AutoLocker locker(fDebugModel); + + // If there already is a breakpoint, it might already have the requested + // state. + Breakpoint* breakpoint = fDebugModel->BreakpointAtAddress(address); + if (breakpoint != NULL && breakpoint->UserState() == state) + return B_OK; + + // create a breakpoint, if it doesn't exist yet + if (breakpoint == NULL) { + Image* image = fTeam->ImageByAddress(address); + if (image == NULL) + return B_OK; + + breakpoint = new(std::nothrow) Breakpoint(image, address); + if (breakpoint == NULL) + return B_NO_MEMORY; + + if (!fDebugModel->AddBreakpoint(breakpoint)) + return B_NO_MEMORY; + } + + user_breakpoint_state oldState = breakpoint->UserState(); + + // set the breakpoint state + breakpoint->SetUserState(state); + fDebugModel->NotifyUserBreakpointChanged(breakpoint); + + bool install = breakpoint->ShouldBeInstalled(); + if (breakpoint->IsInstalled() == install) + return B_OK; + + // The breakpoint needs to be installed/uninstalled. + locker.Unlock(); + + status_t error = install + ? fDebuggerInterface->InstallBreakpoint(address) + : fDebuggerInterface->UninstallBreakpoint(address); + + locker.Lock(); + + breakpoint = fDebugModel->BreakpointAtAddress(address); + + // Mark the breakpoint installed/uninstalled, if everything went fine. + if (error == B_OK) { + breakpoint->SetInstalled(install); +printf("-> breakpoint %sinstalled successfully!\n", install ? "" : "un"); + return B_OK; + } + + // revert on error + breakpoint->SetUserState(oldState); + fDebugModel->NotifyUserBreakpointChanged(breakpoint); + + if (breakpoint->IsUnused()) + fDebugModel->RemoveBreakpoint(breakpoint); + + return error; +} + + void TeamDebugger::_HandleThreadAction(thread_id threadID, uint32 action) { @@ -619,6 +726,47 @@ printf("MSG_THREAD_STEP_OUT\n"); } +void +TeamDebugger::_HandleSetUserBreakpoint(target_addr_t address, bool enabled) +{ +printf("TeamDebugger::_HandleSetUserBreakpoint(%#llx, %d)\n", address, enabled); + status_t error = _SetUserBreakpoint(address, enabled); + if (error != B_OK) { + _NotifyUser("Install Breakpoint", "Failed to install breakpoint: %s", + strerror(error)); + } +} + + +void +TeamDebugger::_HandleClearUserBreakpoint(target_addr_t address) +{ +printf("TeamDebugger::_HandleClearUserBreakpoint(%#llx)\n", address); + AutoLocker locker(fDebugModel); + + Breakpoint* breakpoint = fDebugModel->BreakpointAtAddress(address); + if (breakpoint == NULL || breakpoint->UserState() == USER_BREAKPOINT_NONE) + return; + + // set the breakpoint state + breakpoint->SetUserState(USER_BREAKPOINT_NONE); + fDebugModel->NotifyUserBreakpointChanged(breakpoint); + + // check whether the breakpoint needs to be uninstalled + bool uninstall = !breakpoint->ShouldBeInstalled() + && breakpoint->IsInstalled(); + + // if unused remove it + if (breakpoint->IsUnused()) + fDebugModel->RemoveBreakpoint(breakpoint); + + locker.Unlock(); + + if (uninstall) + fDebuggerInterface->UninstallBreakpoint(address); +} + + void TeamDebugger::_HandleThreadStateChanged(thread_id threadID) { @@ -669,3 +817,25 @@ TeamDebugger::_HandleStackTraceChanged(thread_id threadID) { printf("TeamDebugger::_HandleStackTraceChanged()\n"); } + + +void +TeamDebugger::_NotifyUser(const char* title, const char* text,...) +{ + // print the message + char buffer[1024]; + va_list args; + va_start(args, text); + vsnprintf(buffer, sizeof(buffer), text, args); + va_end(args); + + // show the alert + BAlert* alert = new(std::nothrow) BAlert(title, buffer, "OK", + NULL, NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); + if (alert != NULL) + alert->Go(NULL); + + // TODO: We need to let the alert run asynchronously, but we shouldn't just + // create it and don't care anymore. Maybe an error window, which can + // display a list of errors would be the better choice. +} diff --git a/src/apps/debugger/TeamDebugger.h b/src/apps/debugger/TeamDebugger.h index 9b3087d49a..2bf1e7e17b 100644 --- a/src/apps/debugger/TeamDebugger.h +++ b/src/apps/debugger/TeamDebugger.h @@ -39,6 +39,9 @@ private: TeamWindow* window, StackFrame* frame); virtual void ThreadActionRequested(TeamWindow* window, thread_id threadID, uint32 action); + virtual void SetBreakpointRequested(target_addr_t address, + bool enabled); + virtual void ClearBreakpointRequested(target_addr_t address); virtual bool TeamWindowQuitRequested(TeamWindow* window); // JobListener @@ -88,13 +91,23 @@ private: void _SetThreadState(::Thread* thread, uint32 state, CpuState* cpuState); + status_t _SetUserBreakpoint(target_addr_t address, + bool enabled); + void _HandleThreadAction(thread_id threadID, uint32 action); + void _HandleSetUserBreakpoint(target_addr_t address, + bool enabled); + void _HandleClearUserBreakpoint( + target_addr_t address); void _HandleThreadStateChanged(thread_id threadID); void _HandleCpuStateChanged(thread_id threadID); void _HandleStackTraceChanged(thread_id threadID); + void _NotifyUser(const char* title, + const char* text,...); + private: ::Team* fTeam; TeamDebugModel* fDebugModel; diff --git a/src/apps/debugger/gui/team_window/SourceView.cpp b/src/apps/debugger/gui/team_window/SourceView.cpp index b12d7cb6ee..cd00fdf66a 100644 --- a/src/apps/debugger/gui/team_window/SourceView.cpp +++ b/src/apps/debugger/gui/team_window/SourceView.cpp @@ -11,12 +11,15 @@ #include #include +#include +#include #include #include #include #include +#include "Breakpoint.h" #include "SourceCode.h" #include "StackTrace.h" #include "Statement.h" @@ -41,9 +44,10 @@ protected: inline float TotalHeight() const; + int32 LineAtOffset(float yOffset) const; void GetLineRange(BRect rect, int32& minLine, - int32& maxLine); - + int32& maxLine) const; + BRect LineRect(uint32 line) const; protected: SourceView* fSourceView; @@ -55,7 +59,8 @@ protected: class SourceView::MarkerView : public BaseView { public: MarkerView(SourceView* sourceView, - FontInfo* fontInfo); + TeamDebugModel* debugModel, + Listener* listener, FontInfo* fontInfo); ~MarkerView(); virtual void SetSourceCode(SourceCode* sourceCode); @@ -63,25 +68,52 @@ public: void SetStackTrace(StackTrace* stackTrace); void SetStackFrame(StackFrame* stackFrame); + void UserBreakpointChanged(target_addr_t address); + virtual BSize MinSize(); virtual BSize MaxSize(); virtual void Draw(BRect updateRect); + virtual void MouseDown(BPoint where); + private: struct Marker; struct InstructionPointerMarker; + struct BreakpointMarker; + + template struct MarkerByLinePredicate; typedef BObjectList MarkerList; + typedef BObjectList BreakpointMarkerList; private: - void _UpdateMarkers(); + void _InvalidateIPMarkers(); + void _InvalidateBreakpointMarkers(); + void _UpdateIPMarkers(); + void _UpdateBreakpointMarkers(); + void _GetMarkers(uint32 minLine, uint32 maxLine, + MarkerList& markers); + BreakpointMarker* _BreakpointMarkerAtLine(uint32 line); + + template + static int _CompareMarkers(const MarkerType* a, + const MarkerType* b); + + template + static int _CompareLineMarker(const uint32* line, + const MarkerType* marker); private: + TeamDebugModel* fDebugModel; + Listener* fListener; StackTrace* fStackTrace; StackFrame* fStackFrame; - MarkerList fMarkers; - bool fMarkersValid; + MarkerList fIPMarkers; + BreakpointMarkerList fBreakpointMarkers; + bool fIPMarkersValid; + bool fBreakpointMarkersValid; + rgb_color fBreakpointOptionMarker; }; @@ -115,6 +147,40 @@ private: }; +struct SourceView::MarkerView::BreakpointMarker : Marker { + BreakpointMarker(uint32 line, + target_addr_t address, bool enabled); + + target_addr_t Address() const { return fAddress; } + bool IsEnabled() const { return fEnabled; } + + virtual void Draw(MarkerView* view, BRect rect); + +private: + target_addr_t fAddress; + bool fEnabled; +}; + + +template +struct SourceView::MarkerView::MarkerByLinePredicate + : UnaryPredicate { + MarkerByLinePredicate(uint32 line) + : + fLine(line) + { + } + + virtual int operator()(const MarkerType* marker) const + { + return -_CompareLineMarker(&fLine, marker); + } + +private: + uint32 fLine; +}; + + class SourceView::TextView : public BaseView { public: TextView(SourceView* sourceView, @@ -181,8 +247,21 @@ SourceView::BaseView::TotalHeight() const } +int32 +SourceView::BaseView::LineAtOffset(float yOffset) const +{ + int32 lineCount = LineCount(); + if (yOffset < 0 || lineCount == 0) + return -1; + + int32 line = (int32)yOffset / (int32)fFontInfo->lineHeight; + return line < lineCount ? line : -1; +} + + void -SourceView::BaseView::GetLineRange(BRect rect, int32& minLine, int32& maxLine) +SourceView::BaseView::GetLineRange(BRect rect, int32& minLine, + int32& maxLine) const { int32 lineHeight = (int32)fFontInfo->lineHeight; minLine = (int32)rect.top / lineHeight; @@ -192,6 +271,14 @@ SourceView::BaseView::GetLineRange(BRect rect, int32& minLine, int32& maxLine) } +BRect +SourceView::BaseView::LineRect(uint32 line) const +{ + float y = (float)line * fFontInfo->lineHeight; + return BRect(0, y, Bounds().right, y + fFontInfo->lineHeight - 1); +} + + // #pragma mark - MarkerView::Marker @@ -282,18 +369,52 @@ SourceView::MarkerView::InstructionPointerMarker::_DrawArrow(BView* view, } +// #pragma mark - MarkerView::BreakpointMarker + + +SourceView::MarkerView::BreakpointMarker::BreakpointMarker(uint32 line, + target_addr_t address, bool enabled) + : + Marker(line), + fAddress(address), + fEnabled(enabled) +{ +} + + +void +SourceView::MarkerView::BreakpointMarker::Draw(MarkerView* view, BRect rect) +{ + float y = (rect.top + rect.bottom) / 2; + view->SetHighColor((rgb_color){255, 0, 0, 255}); + if (fEnabled) + view->FillEllipse(BPoint(rect.right - 8, y), 4, 4); + else + view->StrokeEllipse(BPoint(rect.right - 8, y), 3.5f, 3.5f); +} + + // #pragma mark - MarkerView -SourceView::MarkerView::MarkerView(SourceView* sourceView, FontInfo* fontInfo) +SourceView::MarkerView::MarkerView(SourceView* sourceView, + TeamDebugModel* debugModel, Listener* listener, FontInfo* fontInfo) : BaseView("source marker view", sourceView, fontInfo), + fDebugModel(debugModel), + fListener(listener), fStackTrace(NULL), fStackFrame(NULL), - fMarkers(20, true), - fMarkersValid(false) + fIPMarkers(10, true), + fBreakpointMarkers(20, true), + fIPMarkersValid(false), + fBreakpointMarkersValid(false) + { - SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR); + fBreakpointOptionMarker = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), + B_DARKEN_1_TINT); + SetViewColor(background); } @@ -305,8 +426,8 @@ SourceView::MarkerView::~MarkerView() void SourceView::MarkerView::SetSourceCode(SourceCode* sourceCode) { - fMarkers.MakeEmpty(); - fMarkersValid = false; + _InvalidateIPMarkers(); + _InvalidateBreakpointMarkers(); BaseView::SetSourceCode(sourceCode); } @@ -315,8 +436,7 @@ void SourceView::MarkerView::SetStackTrace(StackTrace* stackTrace) { fStackTrace = stackTrace; - fMarkers.MakeEmpty(); - fMarkersValid = false; + _InvalidateIPMarkers(); Invalidate(); } @@ -325,8 +445,15 @@ void SourceView::MarkerView::SetStackFrame(StackFrame* stackFrame) { fStackFrame = stackFrame; - fMarkers.MakeEmpty(); - fMarkersValid = false; + _InvalidateIPMarkers(); + Invalidate(); +} + + +void +SourceView::MarkerView::UserBreakpointChanged(target_addr_t address) +{ + _InvalidateBreakpointMarkers(); Invalidate(); } @@ -347,40 +474,112 @@ SourceView::MarkerView::MaxSize() void SourceView::MarkerView::Draw(BRect updateRect) { - _UpdateMarkers(); - - if (fSourceCode == NULL || fMarkers.IsEmpty()) + if (fSourceCode == NULL) return; // get the lines intersecting with the update rect int32 minLine, maxLine; GetLineRange(updateRect, minLine, maxLine); + if (minLine > maxLine) + return; + + // get the markers in that range + MarkerList markers; + _GetMarkers(minLine, maxLine, markers); - // 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) + + int32 markerIndex = 0; + for (int32 line = minLine; line <= maxLine; line++) { + bool drawBreakpointOptionMarker = true; + + Marker* marker; + while ((marker = markers.ItemAt(markerIndex)) != NULL + && marker->Line() == (uint32)line) { + marker->Draw(this, LineRect(line)); + drawBreakpointOptionMarker = false; + markerIndex++; + } + + if (!drawBreakpointOptionMarker) continue; - float y = (float)line * fFontInfo->lineHeight; - BRect rect(0, y, width, y + fFontInfo->lineHeight - 1); - marker->Draw(this, rect); - } + Statement* statement = fSourceCode->StatementAtLine(line); + if (statement == NULL + || statement->StartSourceLocation().Line() != (uint32)line + || !statement->BreakpointAllowed()) { + continue; + } - // TODO: Draw possible breakpoint marks! + float y = ((float)line + 0.5f) * fFontInfo->lineHeight; + SetHighColor(fBreakpointOptionMarker); + FillEllipse(BPoint(width - 8, y), 2, 2); + } } void -SourceView::MarkerView::_UpdateMarkers() +SourceView::MarkerView::MouseDown(BPoint where) { - if (fMarkersValid) + if (fSourceCode == NULL) return; - fMarkers.MakeEmpty(); + int32 line = LineAtOffset(where.y); + if (line < 0) + return; + + Statement* statement = fSourceCode->StatementAtLine(line); + if (statement == NULL + || statement->StartSourceLocation().Line() != (uint32)line + || !statement->BreakpointAllowed()) { + return; + } + + int32 modifiers; + if (Looper()->CurrentMessage()->FindInt32("modifiers", &modifiers) != B_OK) + modifiers = 0; + + BreakpointMarker* marker = _BreakpointMarkerAtLine(line); + target_addr_t address = marker != NULL + ? marker->Address() : statement->CoveringAddressRange().Start(); + + if ((modifiers & B_SHIFT_KEY) != 0) { + if (marker != NULL && !marker->IsEnabled()) + fListener->ClearBreakpointRequested(address); + else + fListener->SetBreakpointRequested(address, false); + } else { + if (marker != NULL && marker->IsEnabled()) + fListener->ClearBreakpointRequested(address); + else + fListener->SetBreakpointRequested(address, true); + } +} + + +void +SourceView::MarkerView::_InvalidateIPMarkers() +{ + fIPMarkersValid = false; + fIPMarkers.MakeEmpty(); +} + + +void +SourceView::MarkerView::_InvalidateBreakpointMarkers() +{ + fBreakpointMarkersValid = false; + fBreakpointMarkers.MakeEmpty(); +} + + +void +SourceView::MarkerView::_UpdateIPMarkers() +{ + if (fIPMarkersValid) + return; + + fIPMarkers.MakeEmpty(); if (fSourceCode != NULL && fStackTrace != NULL) { for (int32 i = 0; StackFrame* frame = fStackTrace->FrameAt(i); @@ -395,15 +594,132 @@ SourceView::MarkerView::_UpdateMarkers() Marker* marker = new(std::nothrow) InstructionPointerMarker( line, i == 0, frame == fStackFrame); - if (marker == NULL || !fMarkers.AddItem(marker)) { + if (marker == NULL || !fIPMarkers.AddItem(marker)) { delete marker; break; } } - } - // TODO: Filter duplicate IP markers (recursive functions)! - fMarkersValid = true; + // sort by line + fIPMarkers.SortItems(&_CompareMarkers); + + // TODO: Filter duplicate IP markers (recursive functions)! + } + + fIPMarkersValid = true; +} + + +void +SourceView::MarkerView::_UpdateBreakpointMarkers() +{ + if (fBreakpointMarkersValid) + return; + + fBreakpointMarkers.MakeEmpty(); + + if (fSourceCode != NULL) { + AutoLocker locker(fDebugModel); + + // get the breakpoints in our source code range + BObjectList breakpoints; + fDebugModel->GetBreakpointsInAddressRange( + fSourceCode->StatementAddressRange(), breakpoints); + + for (int32 i = 0; Breakpoint* breakpoint = breakpoints.ItemAt(i); i++) { + if (breakpoint->UserState() == USER_BREAKPOINT_NONE) + continue; + + Statement* statement = fSourceCode->StatementAtAddress( + breakpoint->Address()); + if (statement == NULL) + continue; + uint32 line = statement->StartSourceLocation().Line(); + if (line >= (uint32)LineCount()) + continue; + + BreakpointMarker* marker = new(std::nothrow) BreakpointMarker( + line, breakpoint->Address(), + breakpoint->UserState() == USER_BREAKPOINT_ENABLED); + if (marker == NULL || !fBreakpointMarkers.AddItem(marker)) { + delete marker; + break; + } + } + + // sort by line + fBreakpointMarkers.SortItems(&_CompareMarkers); + } + + fBreakpointMarkersValid = true; +} + + +void +SourceView::MarkerView::_GetMarkers(uint32 minLine, uint32 maxLine, + MarkerList& markers) +{ + _UpdateIPMarkers(); + _UpdateBreakpointMarkers(); + + int32 ipIndex = fIPMarkers.FindBinaryInsertionIndex( + MarkerByLinePredicate(minLine)); + int32 breakpointIndex = fBreakpointMarkers.FindBinaryInsertionIndex( + MarkerByLinePredicate(minLine)); + + Marker* ipMarker = fIPMarkers.ItemAt(ipIndex); + Marker* breakpointMarker = fBreakpointMarkers.ItemAt(breakpointIndex); + + while (ipMarker != NULL && breakpointMarker != NULL + && ipMarker->Line() <= maxLine && breakpointMarker->Line() <= maxLine) { + if (breakpointMarker->Line() <= ipMarker->Line()) { + markers.AddItem(breakpointMarker); + breakpointMarker = fBreakpointMarkers.ItemAt(++breakpointIndex); + } else { + markers.AddItem(ipMarker); + ipMarker = fIPMarkers.ItemAt(++ipIndex); + } + } + + while (breakpointMarker != NULL && breakpointMarker->Line() <= maxLine) { + markers.AddItem(breakpointMarker); + breakpointMarker = fBreakpointMarkers.ItemAt(++breakpointIndex); + } + + while (ipMarker != NULL && ipMarker->Line() <= maxLine) { + markers.AddItem(ipMarker); + ipMarker = fIPMarkers.ItemAt(++ipIndex); + } +} + + +SourceView::MarkerView::BreakpointMarker* +SourceView::MarkerView::_BreakpointMarkerAtLine(uint32 line) +{ + return fBreakpointMarkers.BinarySearchByKey(line, + &_CompareLineMarker); +} + + +template +/*static*/ int +SourceView::MarkerView::_CompareMarkers(const MarkerType* a, + const MarkerType* b) +{ + if (a->Line() < b->Line()) + return -1; + return a->Line() == b->Line() ? 0 : 1; +} + + +template +/*static*/ int +SourceView::MarkerView::_CompareLineMarker(const uint32* line, + const MarkerType* marker) +{ + if (*line < marker->Line()) + return -1; + return *line == marker->Line() ? 0 : 1; } @@ -605,6 +921,13 @@ SourceView::SetSourceCode(SourceCode* sourceCode) } +void +SourceView::UserBreakpointChanged(target_addr_t address) +{ + fMarkerView->UserBreakpointChanged(address); +} + + bool SourceView::ScrollToAddress(target_addr_t address) { @@ -622,7 +945,6 @@ SourceView::ScrollToAddress(target_addr_t address) bool SourceView::ScrollToLine(uint32 line) { -printf("SourceView::ScrollToLine(%lu)\n", line); if (fSourceCode == NULL || line >= (uint32)fSourceCode->CountLines()) return false; @@ -703,7 +1025,8 @@ SourceView::DoLayout() void SourceView::_Init() { - AddChild(fMarkerView = new MarkerView(this, &fFontInfo)); + AddChild(fMarkerView = new MarkerView(this, fDebugModel, fListener, + &fFontInfo)); AddChild(fTextView = new TextView(this, &fFontInfo)); } diff --git a/src/apps/debugger/gui/team_window/SourceView.h b/src/apps/debugger/gui/team_window/SourceView.h index bca38e93dd..80afd09258 100644 --- a/src/apps/debugger/gui/team_window/SourceView.h +++ b/src/apps/debugger/gui/team_window/SourceView.h @@ -11,9 +11,11 @@ #include "ArchitectureTypes.h" +class Breakpoint; class SourceCode; class StackFrame; class StackTrace; +class Statement; class TeamDebugModel; @@ -36,6 +38,8 @@ public: void SetStackFrame(StackFrame* stackFrame); void SetSourceCode(SourceCode* sourceCode); + void UserBreakpointChanged(target_addr_t address); + bool ScrollToAddress(target_addr_t address); bool ScrollToLine(uint32 line); @@ -80,8 +84,10 @@ class SourceView::Listener { public: virtual ~Listener(); -// virtual void StackFrameSelectionChanged( -// StackFrame* frame) = 0; + virtual void SetBreakpointRequested( + target_addr_t address, bool enabled) = 0; + virtual void ClearBreakpointRequested( + target_addr_t address) = 0; }; diff --git a/src/apps/debugger/gui/team_window/TeamWindow.cpp b/src/apps/debugger/gui/team_window/TeamWindow.cpp index d5b5003e7e..5b2f89b476 100644 --- a/src/apps/debugger/gui/team_window/TeamWindow.cpp +++ b/src/apps/debugger/gui/team_window/TeamWindow.cpp @@ -24,7 +24,6 @@ #include "SourceCode.h" #include "StackTrace.h" #include "StackTraceView.h" -#include "TeamDebugModel.h" // #pragma mark - TeamWindow @@ -58,6 +57,7 @@ TeamWindow::TeamWindow(TeamDebugModel* debugModel, Listener* listener) name << " (" << team->ID() << ")"; SetTitle(name.String()); + fDebugModel->AddListener(this); team->AddListener(this); } @@ -72,6 +72,7 @@ TeamWindow::~TeamWindow() fSourceView->UnsetListener(); fDebugModel->GetTeam()->RemoveListener(this); + fDebugModel->RemoveListener(this); if (fActiveSourceCode != NULL) fActiveSourceCode->RemoveReference(); @@ -144,9 +145,20 @@ TeamWindow::MessageReceived(BMessage* message) break; } + case MSG_USER_BREAKPOINT_CHANGED: + { + uint64 address; + if (message->FindUInt64("address", &address) != B_OK) + break; + + _HandleUserBreakpointChanged(address); + break; + } + case MSG_STACK_FRAME_SOURCE_CODE_CHANGED: { _HandleSourceCodeChanged(); + break; } default: @@ -177,6 +189,20 @@ TeamWindow::StackFrameSelectionChanged(StackFrame* frame) } +void +TeamWindow::SetBreakpointRequested(target_addr_t address, bool enabled) +{ + fListener->SetBreakpointRequested(address, enabled); +} + + +void +TeamWindow::ClearBreakpointRequested(target_addr_t address) +{ + fListener->ClearBreakpointRequested(address); +} + + void TeamWindow::ThreadStateChanged(const Team::ThreadEvent& event) { @@ -204,6 +230,16 @@ TeamWindow::ThreadStackTraceChanged(const Team::ThreadEvent& event) } + +void +TeamWindow::UserBreakpointChanged(const TeamDebugModel::BreakpointEvent& event) +{ + BMessage message(MSG_USER_BREAKPOINT_CHANGED); + message.AddUInt64("address", event.GetBreakpoint()->Address()); + PostMessage(&message); +} + + void TeamWindow::StackFrameSourceCodeChanged(StackFrame* frame) { @@ -520,6 +556,13 @@ TeamWindow::_HandleSourceCodeChanged() } +void +TeamWindow::_HandleUserBreakpointChanged(target_addr_t address) +{ + fSourceView->UserBreakpointChanged(address); +} + + // #pragma mark - Listener diff --git a/src/apps/debugger/gui/team_window/TeamWindow.h b/src/apps/debugger/gui/team_window/TeamWindow.h index 8c89454592..75217c2d5b 100644 --- a/src/apps/debugger/gui/team_window/TeamWindow.h +++ b/src/apps/debugger/gui/team_window/TeamWindow.h @@ -12,6 +12,7 @@ #include "StackFrame.h" #include "StackTraceView.h" #include "Team.h" +#include "TeamDebugModel.h" #include "ThreadListView.h" @@ -20,12 +21,11 @@ class BTabView; class ImageListView; class RegisterView; class SourceCode; -class TeamDebugModel; class TeamWindow : public BWindow, private ThreadListView::Listener, StackTraceView::Listener, SourceView::Listener, Team::Listener, - StackFrame::Listener { + private TeamDebugModel::Listener, StackFrame::Listener { public: class Listener; @@ -48,8 +48,10 @@ private: // StackTraceView::Listener virtual void StackFrameSelectionChanged(StackFrame* frame); - // StackTraceView::Listener - // nothing yet + // SourceView::Listener + virtual void SetBreakpointRequested(target_addr_t address, + bool enabled); + virtual void ClearBreakpointRequested(target_addr_t address); // Team::Listener virtual void ThreadStateChanged( @@ -59,6 +61,11 @@ private: virtual void ThreadStackTraceChanged( const Team::ThreadEvent& event); + // TeamDebugModel::Listener + virtual void UserBreakpointChanged( + const TeamDebugModel::BreakpointEvent& + event); + // StackFrame::Listener virtual void StackFrameSourceCodeChanged(StackFrame* frame); @@ -75,6 +82,8 @@ private: void _HandleCpuStateChanged(thread_id threadID); void _HandleStackTraceChanged(thread_id threadID); void _HandleSourceCodeChanged(); + void _HandleUserBreakpointChanged( + target_addr_t address); private: TeamDebugModel* fDebugModel; @@ -105,6 +114,10 @@ public: TeamWindow* window, StackFrame* frame) = 0; virtual void ThreadActionRequested(TeamWindow* window, thread_id threadID, uint32 action) = 0; + virtual void SetBreakpointRequested(target_addr_t address, + bool enabled) = 0; + virtual void ClearBreakpointRequested( + target_addr_t address) = 0; virtual bool TeamWindowQuitRequested(TeamWindow* window) = 0; }; diff --git a/src/apps/debugger/model/Breakpoint.cpp b/src/apps/debugger/model/Breakpoint.cpp new file mode 100644 index 0000000000..1b8ab65fee --- /dev/null +++ b/src/apps/debugger/model/Breakpoint.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "Breakpoint.h" + + +// #pragma mark - BreakpointClient + + +BreakpointClient::~BreakpointClient() +{ +} + + +// #pragma mark - Breakpoint + + +Breakpoint::Breakpoint(Image* image, target_addr_t address) + : + fAddress(address), + fImage(image), + fUserState(USER_BREAKPOINT_NONE), + fInstalled(false) +{ +} + + +Breakpoint::~Breakpoint() +{ +} + + +void +Breakpoint::SetUserState(user_breakpoint_state state) +{ + fUserState = state; +} + + +void +Breakpoint::SetInstalled(bool installed) +{ + fInstalled = installed; +} + + +bool +Breakpoint::ShouldBeInstalled() const +{ + return fUserState == USER_BREAKPOINT_ENABLED || !fClients.IsEmpty(); +} + + +bool +Breakpoint::IsUnused() const +{ + return fUserState == USER_BREAKPOINT_NONE && fClients.IsEmpty(); +} + + +bool +Breakpoint::AddClient(BreakpointClient* client) +{ + return fClients.AddItem(client); +} + + +void +Breakpoint::RemoveClient(BreakpointClient* client) +{ + fClients.RemoveItem(client); +} + + +/*static*/ int +Breakpoint::CompareBreakpoints(const Breakpoint* a, const Breakpoint* b) +{ + if (a->Address() < b->Address()) + return -1; + return a->Address() == b->Address() ? 0 : 1; +} + + +/*static*/ int +Breakpoint::CompareAddressBreakpoint(const target_addr_t* address, + const Breakpoint* breakpoint) +{ + if (*address < breakpoint->Address()) + return -1; + return *address == breakpoint->Address() ? 0 : 1; +} diff --git a/src/apps/debugger/model/Breakpoint.h b/src/apps/debugger/model/Breakpoint.h new file mode 100644 index 0000000000..1ccdfc8195 --- /dev/null +++ b/src/apps/debugger/model/Breakpoint.h @@ -0,0 +1,68 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef BREAKPOINT_H +#define BREAKPOINT_H + +#include +#include + +#include "ArchitectureTypes.h" + + +enum user_breakpoint_state { + USER_BREAKPOINT_NONE, + USER_BREAKPOINT_ENABLED, + USER_BREAKPOINT_DISABLED +}; + + +class Image; + + +class BreakpointClient { +public: + virtual ~BreakpointClient(); +}; + + +class Breakpoint : public Referenceable { +public: + Breakpoint(Image* image, target_addr_t address); + ~Breakpoint(); + + Image* GetImage() const { return fImage; } + target_addr_t Address() const { return fAddress; } + + user_breakpoint_state UserState() const { return fUserState; } + void SetUserState(user_breakpoint_state state); + + bool IsInstalled() const { return fInstalled; } + void SetInstalled(bool installed); + + bool ShouldBeInstalled() const; + bool IsUnused() const; + + bool AddClient(BreakpointClient* client); + void RemoveClient(BreakpointClient* client); + + static int CompareBreakpoints(const Breakpoint* a, + const Breakpoint* b); + static int CompareAddressBreakpoint( + const target_addr_t* address, + const Breakpoint* breakpoint); + +private: + typedef BObjectList ClientList; + +private: + target_addr_t fAddress; + Image* fImage; + ClientList fClients; + user_breakpoint_state fUserState; + bool fInstalled; +}; + + +#endif // BREAKPOINT_H diff --git a/src/apps/debugger/model/DisassembledCode.cpp b/src/apps/debugger/model/DisassembledCode.cpp index 68e92b9553..353dd519fb 100644 --- a/src/apps/debugger/model/DisassembledCode.cpp +++ b/src/apps/debugger/model/DisassembledCode.cpp @@ -86,6 +86,20 @@ DisassembledCode::StatementAtAddress(target_addr_t address) const } +TargetAddressRange +DisassembledCode::StatementAddressRange() const +{ + if (fStatements.IsEmpty()) + return TargetAddressRange(); + + ContiguousStatement* first = fStatements.ItemAt(0); + ContiguousStatement* last + = fStatements.ItemAt(fStatements.CountItems() - 1); + return TargetAddressRange(first->AddressRange().Start(), + last->AddressRange().End()); +} + + bool DisassembledCode::AddCommentLine(const BString& line) { @@ -101,8 +115,7 @@ DisassembledCode::AddInstructionLine(const BString& line, target_addr_t address, ContiguousStatement* statement = new(std::nothrow) ContiguousStatement( SourceLocation(lineIndex), SourceLocation(lineIndex + 1), - TargetAddressRange(address, size)); - // TODO: breakpointAllowed! + TargetAddressRange(address, size), breakpointAllowed); if (statement == NULL) return false; diff --git a/src/apps/debugger/model/DisassembledCode.h b/src/apps/debugger/model/DisassembledCode.h index 031aafe29c..a27d011b48 100644 --- a/src/apps/debugger/model/DisassembledCode.h +++ b/src/apps/debugger/model/DisassembledCode.h @@ -27,6 +27,8 @@ public: virtual Statement* StatementAtLine(int32 index) const; virtual Statement* StatementAtAddress(target_addr_t address) const; + virtual TargetAddressRange StatementAddressRange() const; + public: bool AddCommentLine(const BString& line); bool AddInstructionLine(const BString& line, diff --git a/src/apps/debugger/model/SourceCode.h b/src/apps/debugger/model/SourceCode.h index b9c24b3a6e..f93ef9c9bb 100644 --- a/src/apps/debugger/model/SourceCode.h +++ b/src/apps/debugger/model/SourceCode.h @@ -7,7 +7,7 @@ #include -#include "ArchitectureTypes.h" +#include "TargetAddressRange.h" class Statement; @@ -25,6 +25,8 @@ public: virtual Statement* StatementAtLine(int32 index) const = 0; virtual Statement* StatementAtAddress(target_addr_t address) const = 0; + + virtual TargetAddressRange StatementAddressRange() const = 0; }; diff --git a/src/apps/debugger/model/Statement.cpp b/src/apps/debugger/model/Statement.cpp index 592e3ca0e3..41687aa10f 100644 --- a/src/apps/debugger/model/Statement.cpp +++ b/src/apps/debugger/model/Statement.cpp @@ -18,10 +18,11 @@ Statement::~Statement() AbstractStatement::AbstractStatement(const SourceLocation& start, - const SourceLocation& end) + const SourceLocation& end, bool breakpointAllowed) : fStart(start), - fEnd(end) + fEnd(end), + fBreakpointAllowed(breakpointAllowed) { } @@ -40,13 +41,21 @@ AbstractStatement::EndSourceLocation() const } +bool +AbstractStatement::BreakpointAllowed() const +{ + return fBreakpointAllowed; +} + + // #pragma mark - ContiguousStatement ContiguousStatement::ContiguousStatement(const SourceLocation& start, - const SourceLocation& end, const TargetAddressRange& range) + const SourceLocation& end, const TargetAddressRange& range, + bool breakpointAllowed) : - AbstractStatement(start, end), + AbstractStatement(start, end, breakpointAllowed), fRange(range) { } diff --git a/src/apps/debugger/model/Statement.h b/src/apps/debugger/model/Statement.h index 760acf284f..586a301190 100644 --- a/src/apps/debugger/model/Statement.h +++ b/src/apps/debugger/model/Statement.h @@ -26,20 +26,26 @@ public: virtual bool ContainsAddress(target_addr_t address) const = 0; + + virtual bool BreakpointAllowed() const = 0; }; class AbstractStatement : public Statement { public: AbstractStatement(const SourceLocation& start, - const SourceLocation& end); + const SourceLocation& end, + bool breakpointAllowed); virtual SourceLocation StartSourceLocation() const; virtual SourceLocation EndSourceLocation() const; + virtual bool BreakpointAllowed() const; + protected: SourceLocation fStart; SourceLocation fEnd; + bool fBreakpointAllowed; }; @@ -47,7 +53,8 @@ class ContiguousStatement : public AbstractStatement { public: ContiguousStatement(const SourceLocation& start, const SourceLocation& end, - const TargetAddressRange& range); + const TargetAddressRange& range, + bool breakpointAllowed); const TargetAddressRange& AddressRange() const { return fRange; } diff --git a/src/apps/debugger/model/TeamDebugModel.cpp b/src/apps/debugger/model/TeamDebugModel.cpp index 743d3f41ee..4a87f8fb67 100644 --- a/src/apps/debugger/model/TeamDebugModel.cpp +++ b/src/apps/debugger/model/TeamDebugModel.cpp @@ -9,6 +9,30 @@ #include +#include "Breakpoint.h" + + +// #pragma mark - BreakpointByAddressPredicate + + +struct TeamDebugModel::BreakpointByAddressPredicate + : UnaryPredicate { + BreakpointByAddressPredicate(target_addr_t address) + : + fAddress(address) + { + } + + virtual int operator()(const Breakpoint* breakpoint) const + { + return -Breakpoint::CompareAddressBreakpoint(&fAddress, breakpoint); + } + +private: + target_addr_t fAddress; +}; + + // #pragma mark - TeamDebugModel @@ -25,6 +49,8 @@ TeamDebugModel::TeamDebugModel(Team* team, DebuggerInterface* debuggerInterface, TeamDebugModel::~TeamDebugModel() { + for (int32 i = 0; Breakpoint* breakpoint = fBreakpoints.ItemAt(i); i++) + breakpoint->RemoveReference(); } @@ -35,6 +61,66 @@ TeamDebugModel::Init() } +bool +TeamDebugModel::AddBreakpoint(Breakpoint* breakpoint) +{ + if (fBreakpoints.BinaryInsert(breakpoint, &Breakpoint::CompareBreakpoints)) + return true; + + breakpoint->RemoveReference(); + return false; +} + + +void +TeamDebugModel::RemoveBreakpoint(Breakpoint* breakpoint) +{ + int32 index = fBreakpoints.BinarySearchIndex(*breakpoint, + &Breakpoint::CompareBreakpoints); + if (index < 0) + return; + + fBreakpoints.RemoveItemAt(index); + breakpoint->RemoveReference(); +} + + +int32 +TeamDebugModel::CountBreakpoints() const +{ + return fBreakpoints.CountItems(); +} + + +Breakpoint* +TeamDebugModel::BreakpointAt(int32 index) const +{ + return fBreakpoints.ItemAt(index); +} + + +Breakpoint* +TeamDebugModel::BreakpointAtAddress(target_addr_t address) const +{ + return fBreakpoints.BinarySearchByKey(address, + &Breakpoint::CompareAddressBreakpoint); +} + + +void +TeamDebugModel::GetBreakpointsInAddressRange(TargetAddressRange range, + BObjectList& breakpoints) const +{ + int32 index = fBreakpoints.FindBinaryInsertionIndex( + BreakpointByAddressPredicate(range.Start())); + for (; Breakpoint* breakpoint = fBreakpoints.ItemAt(index); index++) { + if (breakpoint->Address() > range.End()) + break; + breakpoints.AddItem(breakpoint); + } +} + + void TeamDebugModel::AddListener(Listener* listener) { @@ -51,6 +137,39 @@ TeamDebugModel::RemoveListener(Listener* listener) } +void +TeamDebugModel::NotifyUserBreakpointChanged(Breakpoint* breakpoint) +{ + for (ListenerList::Iterator it = fListeners.GetIterator(); + Listener* listener = it.Next();) { + listener->UserBreakpointChanged(BreakpointEvent( + TEAM_DEBUG_MODEL_EVENT_USER_BREAKPOINT_CHANGED, this, breakpoint)); + } +} + + +void +TeamDebugModel::_NotifyBreakpointAdded(Breakpoint* breakpoint) +{ + for (ListenerList::Iterator it = fListeners.GetIterator(); + Listener* listener = it.Next();) { + listener->BreakpointAdded(BreakpointEvent( + TEAM_DEBUG_MODEL_EVENT_BREAKPOINT_ADDED, this, breakpoint)); + } +} + + +void +TeamDebugModel::_NotifyBreakpointRemoved(Breakpoint* breakpoint) +{ + for (ListenerList::Iterator it = fListeners.GetIterator(); + Listener* listener = it.Next();) { + listener->BreakpointRemoved(BreakpointEvent( + TEAM_DEBUG_MODEL_EVENT_BREAKPOINT_REMOVED, this, breakpoint)); + } +} + + // #pragma mark - Event @@ -62,9 +181,42 @@ TeamDebugModel::Event::Event(uint32 type, TeamDebugModel* model) } +// #pragma mark - ThreadEvent + + +TeamDebugModel::BreakpointEvent::BreakpointEvent(uint32 type, + TeamDebugModel* model, Breakpoint* breakpoint) + : + Event(type, model), + fBreakpoint(breakpoint) +{ +} + + // #pragma mark - Listener TeamDebugModel::Listener::~Listener() { } + + +void +TeamDebugModel::Listener::BreakpointAdded( + const TeamDebugModel::BreakpointEvent& event) +{ +} + + +void +TeamDebugModel::Listener::BreakpointRemoved( + const TeamDebugModel::BreakpointEvent& event) +{ +} + + +void +TeamDebugModel::Listener::UserBreakpointChanged( + const TeamDebugModel::BreakpointEvent& event) +{ +} diff --git a/src/apps/debugger/model/TeamDebugModel.h b/src/apps/debugger/model/TeamDebugModel.h index 936a7f8864..c1d9383131 100644 --- a/src/apps/debugger/model/TeamDebugModel.h +++ b/src/apps/debugger/model/TeamDebugModel.h @@ -5,22 +5,30 @@ #ifndef TEAM_DEBUG_MODEL_H #define TEAM_DEBUG_MODEL_H +#include + +#include "Breakpoint.h" +#include "TargetAddressRange.h" #include "Team.h" // team debug model event types -//enum { -// TEAM_EVENT_THREAD_ADDED -//}; +enum { + TEAM_DEBUG_MODEL_EVENT_BREAKPOINT_ADDED, + TEAM_DEBUG_MODEL_EVENT_BREAKPOINT_REMOVED, + TEAM_DEBUG_MODEL_EVENT_USER_BREAKPOINT_CHANGED +}; class Architecture; +class Breakpoint; class DebuggerInterface; class TeamDebugModel { public: class Event; + class BreakpointEvent; class Listener; public: @@ -40,16 +48,40 @@ public: Architecture* GetArchitecture() const { return fArchitecture; } + bool AddBreakpoint(Breakpoint* breakpoint); + // takes over reference (also on error) + void RemoveBreakpoint(Breakpoint* breakpoint); + // releases its own reference + int32 CountBreakpoints() const; + Breakpoint* BreakpointAt(int32 index) const; + Breakpoint* BreakpointAtAddress( + target_addr_t address) const; + void GetBreakpointsInAddressRange( + TargetAddressRange range, + BObjectList& breakpoints) const; + void AddListener(Listener* listener); void RemoveListener(Listener* listener); + void NotifyUserBreakpointChanged( + Breakpoint* breakpoint); + private: + struct BreakpointByAddressPredicate; + + typedef BObjectList BreakpointList; typedef DoublyLinkedList ListenerList; +private: + void _NotifyBreakpointAdded(Breakpoint* breakpoint); + void _NotifyBreakpointRemoved( + Breakpoint* breakpoint); + private: Team* fTeam; DebuggerInterface* fDebuggerInterface; Architecture* fArchitecture; + BreakpointList fBreakpoints; ListenerList fListeners; }; @@ -67,13 +99,33 @@ protected: }; +class TeamDebugModel::BreakpointEvent : public Event { +public: + BreakpointEvent(uint32 type, + TeamDebugModel* model, + Breakpoint* breakpoint); + + Breakpoint* GetBreakpoint() const { return fBreakpoint; } + +protected: + Breakpoint* fBreakpoint; +}; + class TeamDebugModel::Listener : public DoublyLinkedListLinkImpl { public: virtual ~Listener(); -// virtual void ThreadAdded(const Team::ThreadEvent& event); + virtual void BreakpointAdded( + const TeamDebugModel::BreakpointEvent& + event); + virtual void BreakpointRemoved( + const TeamDebugModel::BreakpointEvent& + event); + virtual void UserBreakpointChanged( + const TeamDebugModel::BreakpointEvent& + event); };