From 93b6484548d66736b83a5fe17a46c74d53236789 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Sat, 27 Jun 2009 16:49:55 +0000 Subject: [PATCH] * Added support for loading the debug info for an image lazily and adjusted the jobs accordingly. * Added a view to list the source files and functions for an image. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@31271 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/debugger/Jamfile | 1 + src/apps/debugger/Jobs.cpp | 89 +++- src/apps/debugger/Jobs.h | 13 + src/apps/debugger/MessageCodes.h | 1 + src/apps/debugger/TeamDebugger.cpp | 8 + src/apps/debugger/TeamDebugger.h | 2 + .../gui/team_window/ImageFunctionsView.cpp | 400 ++++++++++++++++++ .../gui/team_window/ImageFunctionsView.h | 60 +++ .../gui/team_window/ImageListView.cpp | 88 ++-- .../debugger/gui/team_window/ImageListView.h | 23 +- .../gui/team_window/StackTraceView.cpp | 2 +- .../debugger/gui/team_window/TeamWindow.cpp | 118 +++++- .../debugger/gui/team_window/TeamWindow.h | 26 +- src/apps/debugger/model/Image.cpp | 41 +- src/apps/debugger/model/Image.h | 20 +- src/apps/debugger/model/Team.cpp | 17 + src/apps/debugger/model/Team.h | 10 +- 17 files changed, 835 insertions(+), 84 deletions(-) create mode 100644 src/apps/debugger/gui/team_window/ImageFunctionsView.cpp create mode 100644 src/apps/debugger/gui/team_window/ImageFunctionsView.h diff --git a/src/apps/debugger/Jamfile b/src/apps/debugger/Jamfile index 61f79dcade..5594501c54 100644 --- a/src/apps/debugger/Jamfile +++ b/src/apps/debugger/Jamfile @@ -52,6 +52,7 @@ Application Debugger : DebuggerInterface.cpp # gui/team_window + ImageFunctionsView.cpp ImageListView.cpp RegisterView.cpp SourceView.cpp diff --git a/src/apps/debugger/Jobs.cpp b/src/apps/debugger/Jobs.cpp index 587061adf8..878f26e419 100644 --- a/src/apps/debugger/Jobs.cpp +++ b/src/apps/debugger/Jobs.cpp @@ -180,28 +180,22 @@ GetStackTraceJob::GetImageDebugInfo(Image* image, ImageDebugInfo*& _info) AutoLocker teamLocker(fThread->GetTeam()); while (image->GetImageDebugInfo() == NULL) { - // The info has not yet been loaded -- check whether a job has already - // been scheduled. - AutoLocker workerLocker(GetWorker()); - JobKey key(image, JOB_TYPE_LOAD_IMAGE_DEBUG_INFO); - Job* loadImageDebugInfoJob = GetWorker()->GetJob(key); - if (loadImageDebugInfoJob == NULL) { - // no job yet -- schedule one - loadImageDebugInfoJob = new(std::nothrow) LoadImageDebugInfoJob( - fDebuggerInterface, fArchitecture, image); - if (loadImageDebugInfoJob == NULL) - return B_NO_MEMORY; + // schedule a job, if not loaded + ImageDebugInfo* info; + status_t error = LoadImageDebugInfoJob::ScheduleIfNecessary( + fDebuggerInterface, fArchitecture, GetWorker(), image, &info); + if (error != B_OK) + return error; - status_t error = GetWorker()->ScheduleJob(loadImageDebugInfoJob); - if (error != B_OK) - return error; + if (info != NULL) { + _info = info; + return B_OK; } - workerLocker.Unlock(); teamLocker.Unlock(); // wait for the job to finish - switch (WaitFor(key)) { + switch (WaitFor(JobKey(image, JOB_TYPE_LOAD_IMAGE_DEBUG_INFO))) { case JOB_DEPENDENCY_SUCCEEDED: case JOB_DEPENDENCY_NOT_FOUND: // "Not found" can happen due to a race condition between @@ -262,19 +256,70 @@ LoadImageDebugInfoJob::Do() // create the debug info ImageDebugInfo* debugInfo = new(std::nothrow) ImageDebugInfo(imageInfo, fDebuggerInterface, fArchitecture); - if (debugInfo == NULL) + + status_t error;; + if (debugInfo != NULL) + error = debugInfo->Init(); + else + error = B_NO_MEMORY; + + // set the result + locker.Lock(); + if (error == B_OK) { + fImage->SetImageDebugInfo(debugInfo, IMAGE_DEBUG_INFO_LOADED); + debugInfo->RemoveReference(); + } else { + fImage->SetImageDebugInfo(NULL, IMAGE_DEBUG_INFO_UNAVAILABLE); + delete debugInfo; + } + + return error; +} + + +/*static*/ status_t +LoadImageDebugInfoJob::ScheduleIfNecessary(DebuggerInterface* debuggerInterface, + Architecture* architecture, Worker* worker, Image* image, + ImageDebugInfo** _imageDebugInfo) +{ + AutoLocker teamLocker(image->GetTeam()); + + // If already loaded, we're done. + if (image->GetImageDebugInfo() != NULL) { + if (_imageDebugInfo != NULL) { + *_imageDebugInfo = image->GetImageDebugInfo(); + (*_imageDebugInfo)->AddReference(); + } + return B_OK; + } + + // If already loading, the caller has to wait, if desired. + if (image->ImageDebugInfoState() == IMAGE_DEBUG_INFO_LOADING) { + if (_imageDebugInfo != NULL) + *_imageDebugInfo = NULL; + return B_OK; + } + + // If an earlier load attempt failed, bail out. + if (image->ImageDebugInfoState() != IMAGE_DEBUG_INFO_NOT_LOADED) + return B_ERROR; + + // schedule a job + LoadImageDebugInfoJob* job = new(std::nothrow) LoadImageDebugInfoJob( + debuggerInterface, architecture, image); + if (job == NULL) return B_NO_MEMORY; - status_t error = debugInfo->Init(); + status_t error = worker->ScheduleJob(job); if (error != B_OK) { - delete debugInfo; + image->SetImageDebugInfo(NULL, IMAGE_DEBUG_INFO_UNAVAILABLE); return error; } - // set the info - locker.Lock(); - fImage->SetImageDebugInfo(debugInfo); + image->SetImageDebugInfo(NULL, IMAGE_DEBUG_INFO_LOADING); + if (_imageDebugInfo != NULL) + *_imageDebugInfo = NULL; return B_OK; } diff --git a/src/apps/debugger/Jobs.h b/src/apps/debugger/Jobs.h index 93351f618d..d268b69735 100644 --- a/src/apps/debugger/Jobs.h +++ b/src/apps/debugger/Jobs.h @@ -95,6 +95,19 @@ public: virtual JobKey Key() const; virtual status_t Do(); + static status_t ScheduleIfNecessary( + DebuggerInterface* debuggerInterface, + Architecture* architecture, Worker* worker, + Image* image, + ImageDebugInfo** _imageDebugInfo = NULL); + // If already loaded returns a + // reference, if desired. If not loaded + // schedules a job, but does not wait; + // returns B_OK and NULL. An error, + // if scheduling the job failed, or the + // debug info already failed to load + // earlier. + private: DebuggerInterface* fDebuggerInterface; Architecture* fArchitecture; diff --git a/src/apps/debugger/MessageCodes.h b/src/apps/debugger/MessageCodes.h index a3d0e23c00..cbbddc222c 100644 --- a/src/apps/debugger/MessageCodes.h +++ b/src/apps/debugger/MessageCodes.h @@ -18,6 +18,7 @@ enum { MSG_THREAD_STATE_CHANGED = 'tsch', MSG_THREAD_CPU_STATE_CHANGED = 'tcsc', MSG_THREAD_STACK_TRACE_CHANGED = 'tstc', + MSG_IMAGE_DEBUG_INFO_CHANGED = 'idic', MSG_FUNCTION_SOURCE_CODE_CHANGED = 'fnsc', MSG_USER_BREAKPOINT_CHANGED = 'ubrc', MSG_DEBUGGER_EVENT = 'dbge', diff --git a/src/apps/debugger/TeamDebugger.cpp b/src/apps/debugger/TeamDebugger.cpp index 4aca1c37fa..6398d786dd 100644 --- a/src/apps/debugger/TeamDebugger.cpp +++ b/src/apps/debugger/TeamDebugger.cpp @@ -348,6 +348,14 @@ TeamDebugger::FunctionSourceCodeRequested(TeamWindow* window, } +void +TeamDebugger::ImageDebugInfoRequested(TeamWindow* window, Image* image) +{ + LoadImageDebugInfoJob::ScheduleIfNecessary(fDebuggerInterface, + fDebuggerInterface->GetArchitecture(), fWorker, image); +} + + void TeamDebugger::ThreadActionRequested(TeamWindow* window, thread_id threadID, uint32 action) diff --git a/src/apps/debugger/TeamDebugger.h b/src/apps/debugger/TeamDebugger.h index 7b35e64862..b1ae083e12 100644 --- a/src/apps/debugger/TeamDebugger.h +++ b/src/apps/debugger/TeamDebugger.h @@ -41,6 +41,8 @@ private: // TeamWindow::Listener virtual void FunctionSourceCodeRequested(TeamWindow* window, FunctionDebugInfo* function); + virtual void ImageDebugInfoRequested(TeamWindow* window, + Image* image); virtual void ThreadActionRequested(TeamWindow* window, thread_id threadID, uint32 action); virtual void SetBreakpointRequested(target_addr_t address, diff --git a/src/apps/debugger/gui/team_window/ImageFunctionsView.cpp b/src/apps/debugger/gui/team_window/ImageFunctionsView.cpp new file mode 100644 index 0000000000..9d670324ec --- /dev/null +++ b/src/apps/debugger/gui/team_window/ImageFunctionsView.cpp @@ -0,0 +1,400 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "ImageFunctionsView.h" + +#include + +#include +#include + +#include + +#include "table/TableColumns.h" + +#include "FunctionDebugInfo.h" +#include "Image.h" +#include "ImageDebugInfo.h" + + +// #pragma mark - FunctionsTableModel + + +class ImageFunctionsView::FunctionsTableModel : public TreeTableModel { +public: + FunctionsTableModel() + : + fImageDebugInfo(NULL), + fFunctions(NULL), + fFunctionCount(0), + fSourceFileIndices(NULL), + fSourceFileCount(0) + { + } + + ~FunctionsTableModel() + { + SetImageDebugInfo(NULL); + } + + void SetImageDebugInfo(ImageDebugInfo* imageDebugInfo) + { + // unset old functions + if (fSourceFileIndices != NULL) { + NotifyNodesRemoved(TreeTablePath(), 0, fSourceFileCount); + + delete[] fFunctions; + fFunctions = NULL; + fFunctionCount = 0; + + delete[] fSourceFileIndices; + fSourceFileIndices = NULL; + fSourceFileCount = 0; + } + + fImageDebugInfo = imageDebugInfo; + + // set new functions + if (fImageDebugInfo == NULL || fImageDebugInfo->CountFunctions() == 0) + return; + + // create an array with the functions + int32 functionCount = fImageDebugInfo->CountFunctions(); + FunctionDebugInfo** functions + = new(std::nothrow) FunctionDebugInfo*[functionCount]; + if (functions == NULL) + return; + ArrayDeleter functionsDeleter(functions); + + for (int32 i = 0; i < functionCount; i++) + functions[i] = fImageDebugInfo->FunctionAt(i); + + // sort them + std::sort(functions, functions + functionCount, &_FunctionLess); + + // count the different source files + int32 sourceFileCount = 1; + for (int32 i = 1; i < functionCount; i++) { + if (_CompareSourceFileNames(functions[i - 1]->SourceFileName(), + functions[i]->SourceFileName()) != 0) { + sourceFileCount++; + } + } + + // allocate and init the indices for the source files + fSourceFileIndices = new(std::nothrow) int32[sourceFileCount]; + if (fSourceFileIndices == NULL) + return; + fSourceFileCount = sourceFileCount; + + fSourceFileIndices[0] = 0; + + int32 sourceFileIndex = 1; + for (int32 i = 1; i < functionCount; i++) { + if (_CompareSourceFileNames(functions[i - 1]->SourceFileName(), + functions[i]->SourceFileName()) != 0) { + fSourceFileIndices[sourceFileIndex++] = 1; + } + } + + fFunctions = functionsDeleter.Detach(); + fFunctionCount = functionCount; + + NotifyNodesAdded(TreeTablePath(), 0, fSourceFileCount); + } + + virtual int32 CountColumns() const + { + return 1; + } + + virtual void* Root() const + { + return (void*)this; + } + + virtual int32 CountChildren(void* parent) const + { + if (parent == this) + return fSourceFileCount; + + if (parent >= fSourceFileIndices + && parent < fSourceFileIndices + fSourceFileCount) { + int32 sourceIndex = (int32*)parent - fSourceFileIndices; + int32 count; + if (sourceIndex + 1 < fSourceFileCount) { + count = fSourceFileIndices[sourceIndex + 1] + - fSourceFileIndices[sourceIndex]; + } else + count = fFunctionCount - fSourceFileIndices[sourceIndex]; + + return count; + } + + return 0; + } + + virtual void* ChildAt(void* parent, int32 index) const + { + if (parent == this) { + return index >= 0 && index < fSourceFileCount + ? fSourceFileIndices + index : NULL; + } + + if (parent >= fSourceFileIndices + && parent < fSourceFileIndices + fSourceFileCount) { + int32 sourceIndex = (int32*)parent - fSourceFileIndices; + int32 count; + if (sourceIndex + 1 < fSourceFileCount) { + count = fSourceFileIndices[sourceIndex + 1] + - fSourceFileIndices[sourceIndex]; + } else + count = fFunctionCount - fSourceFileIndices[sourceIndex]; + + return index >= 0 && index < count ? fFunctions[index] : NULL; + } + + return NULL; + } + + virtual bool GetValueAt(void* object, int32 columnIndex, BVariant& value) + { + if (columnIndex != 0) + return false; + + if (object == this) + return false; + + if (object >= fSourceFileIndices + && object < fSourceFileIndices + fSourceFileCount) { + const char* name = fFunctions[*(int32*)object]->SourceFileName(); + value.SetTo(name != NULL ? name : "", + B_VARIANT_DONT_COPY_DATA); + return true; + } + + FunctionDebugInfo* function = (FunctionDebugInfo*)object; + value.SetTo(function->PrettyName(), B_VARIANT_DONT_COPY_DATA); + return true; + } + + bool GetFunctionPath(FunctionDebugInfo* function, TreeTablePath& _path) + { + int32 index = -1; + for (int32 i = 0; i < fFunctionCount; i++) { + if (fFunctions[i] == function) { + index = i; + break; + } + } + + if (index < 0) + return false; + + int32 sourceIndex = fSourceFileCount - 1; + while (fSourceFileIndices[sourceIndex] > index) + sourceIndex--; + + _path.Clear(); + return _path.AddComponent(sourceIndex) + && _path.AddComponent(index); + } + + bool GetObjectForPath(const TreeTablePath& path, const char*& _sourceFile, + FunctionDebugInfo*& _function) + { + int32 componentCount = path.CountComponents(); + if (componentCount == 0 || componentCount > 2) + return false; + + int32 sourceIndex = path.ComponentAt(0); + if (sourceIndex < 0 || sourceIndex >= fSourceFileCount) + return false; + + _sourceFile = fFunctions[fSourceFileIndices[sourceIndex]] + ->SourceFileName(); + + _function = NULL; + + if (componentCount == 2) { + int32 index = path.ComponentAt(1); + if (index >= 0 && index < fFunctionCount) + _function = fFunctions[index]; + } + + return true; + } + + int32 CountSourceFiles() const + { + return fSourceFileCount; + } + +private: + static int _CompareSourceFileNames(const char* a, const char* b) + { + if (a == b) + return 0; + + if (a == NULL) + return false; + if (b == NULL) + return true; + + return strcmp(a, b); + } + + static bool _FunctionLess(const FunctionDebugInfo* a, + const FunctionDebugInfo* b) + { + // compare source file name first + int compared = _CompareSourceFileNames(a->SourceFileName(), + b->SourceFileName()); + if (compared != 0) + return compared < 0; + + // source file names are equal -- compare the function names + return strcasecmp(a->PrettyName(), b->PrettyName()) < 0; + } + +private: + ImageDebugInfo* fImageDebugInfo; + FunctionDebugInfo** fFunctions; + int32 fFunctionCount; + int32* fSourceFileIndices; + int32 fSourceFileCount; +}; + + +// #pragma mark - ImageFunctionsView + + +ImageFunctionsView::ImageFunctionsView(Listener* listener) + : + BGroupView(B_VERTICAL), + fImageDebugInfo(NULL), + fFunctionsTable(NULL), + fFunctionsTableModel(NULL), + fListener(listener) +{ + SetName("Functions"); +} + + +ImageFunctionsView::~ImageFunctionsView() +{ + SetImageDebugInfo(NULL); + fFunctionsTable->SetTreeTableModel(NULL); + delete fFunctionsTableModel; +} + + +/*static*/ ImageFunctionsView* +ImageFunctionsView::Create(Listener* listener) +{ + ImageFunctionsView* self = new ImageFunctionsView(listener); + + try { + self->_Init(); + } catch (...) { + delete self; + throw; + } + + return self; +} + + +void +ImageFunctionsView::UnsetListener() +{ + fListener = NULL; +} + + +void +ImageFunctionsView::SetImageDebugInfo(ImageDebugInfo* imageDebugInfo) +{ + if (imageDebugInfo == fImageDebugInfo) + return; +printf("ImageFunctionsView::SetImageDebugInfo(%p)\n", imageDebugInfo); + + if (fImageDebugInfo != NULL) + fImageDebugInfo->RemoveReference(); + + fImageDebugInfo = imageDebugInfo; + + if (fImageDebugInfo != NULL) + fImageDebugInfo->AddReference(); + + fFunctionsTableModel->SetImageDebugInfo(fImageDebugInfo); + + // If there's only one source file (i.e. "no source file"), expand the item. + if (fImageDebugInfo != NULL + && fFunctionsTableModel->CountSourceFiles() == 1) { + TreeTablePath path; + path.AddComponent(0); + fFunctionsTable->SetNodeExpanded(path, true, false); + } + +printf("ImageFunctionsView::SetImageDebugInfo(%p) done\n", imageDebugInfo); +} + + +void +ImageFunctionsView::SetFunction(FunctionDebugInfo* function) +{ +printf("ImageFunctionsView::SetFunction(%p)\n", function); + TreeTablePath path; + if (fFunctionsTableModel->GetFunctionPath(function, path)) { + fFunctionsTable->SetNodeExpanded(path, true, true); + fFunctionsTable->SelectNode(path, false); + fFunctionsTable->ScrollToNode(path); + } else + fFunctionsTable->DeselectAllNodes(); +} + + +void +ImageFunctionsView::TreeTableSelectionChanged(TreeTable* table) +{ + if (fListener == NULL) + return; + + const char* sourceFile = NULL; + FunctionDebugInfo* function = NULL; + TreeTablePath path; + if (table->SelectionModel()->GetPathAt(0, path)) + fFunctionsTableModel->GetObjectForPath(path, sourceFile, function); + + fListener->FunctionSelectionChanged(function); +} + + +void +ImageFunctionsView::_Init() +{ + fFunctionsTable = new TreeTable("functions", 0, B_FANCY_BORDER); + AddChild(fFunctionsTable->ToView()); + fFunctionsTable->SetSortingEnabled(false); + + // columns + fFunctionsTable->AddColumn(new StringTableColumn(0, "File/Function", 300, + 100, 1000, B_TRUNCATE_END, B_ALIGN_LEFT)); + + fFunctionsTableModel = new FunctionsTableModel(); + fFunctionsTable->SetTreeTableModel(fFunctionsTableModel); + + fFunctionsTable->SetSelectionMode(B_SINGLE_SELECTION_LIST); + fFunctionsTable->AddTreeTableListener(this); +} + + +// #pragma mark - Listener + + +ImageFunctionsView::Listener::~Listener() +{ +} diff --git a/src/apps/debugger/gui/team_window/ImageFunctionsView.h b/src/apps/debugger/gui/team_window/ImageFunctionsView.h new file mode 100644 index 0000000000..c9ff01cda4 --- /dev/null +++ b/src/apps/debugger/gui/team_window/ImageFunctionsView.h @@ -0,0 +1,60 @@ +/* + * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef IMAGE_FUNCTIONS_VIEW_H +#define IMAGE_FUNCTIONS_VIEW_H + +#include + +#include "table/TreeTable.h" +#include "Team.h" + + +class FunctionDebugInfo; + + +class ImageFunctionsView : public BGroupView, private TreeTableListener { +public: + class Listener; + +public: + ImageFunctionsView(Listener* listener); + ~ImageFunctionsView(); + + static ImageFunctionsView* Create(Listener* listener); + // throws + + void UnsetListener(); + + void SetImageDebugInfo( + ImageDebugInfo* imageDebugInfo); + void SetFunction(FunctionDebugInfo* function); + +private: + class FunctionsTableModel; + +private: + // TreeTableListener + virtual void TreeTableSelectionChanged(TreeTable* table); + + void _Init(); + +private: + ImageDebugInfo* fImageDebugInfo; + TreeTable* fFunctionsTable; + FunctionsTableModel* fFunctionsTableModel; + Listener* fListener; +}; + + +class ImageFunctionsView::Listener { +public: + virtual ~Listener(); + + virtual void FunctionSelectionChanged( + FunctionDebugInfo* function) = 0; +}; + + +#endif // IMAGE_FUNCTIONS_VIEW_H diff --git a/src/apps/debugger/gui/team_window/ImageListView.cpp b/src/apps/debugger/gui/team_window/ImageListView.cpp index ec9d9fd4ac..580b812505 100644 --- a/src/apps/debugger/gui/team_window/ImageListView.cpp +++ b/src/apps/debugger/gui/team_window/ImageListView.cpp @@ -129,29 +129,34 @@ private: // #pragma mark - ImageListView -ImageListView::ImageListView() +ImageListView::ImageListView(Team* team, Listener* listener) : BGroupView(B_VERTICAL), - fTeam(NULL), + fTeam(team), + fImage(NULL), fImagesTable(NULL), - fImagesTableModel(NULL) + fImagesTableModel(NULL), + fListener(listener) { SetName("Images"); + + fTeam->AddListener(this); } ImageListView::~ImageListView() { - SetTeam(NULL); + SetImage(NULL); + fTeam->RemoveListener(this); fImagesTable->SetTableModel(NULL); delete fImagesTableModel; } /*static*/ ImageListView* -ImageListView::Create() +ImageListView::Create(Team* team, Listener* listener) { - ImageListView* self = new ImageListView; + ImageListView* self = new ImageListView(team, listener); try { self->_Init(); @@ -165,26 +170,38 @@ ImageListView::Create() void -ImageListView::SetTeam(Team* team) +ImageListView::UnsetListener() { - if (team == fTeam) + fListener = NULL; +} + + +void +ImageListView::SetImage(Image* image) +{ + if (image == fImage) return; +printf("ImageListView::SetImage(%p)\n", image); - if (fTeam != NULL) { - fTeam->RemoveListener(this); - fImagesTable->SetTableModel(NULL); - delete fImagesTableModel; - fImagesTableModel = NULL; + if (fImage != NULL) + fImage->RemoveReference(); + + fImage = image; + + if (fImage != NULL) { + fImage->AddReference(); + + for (int32 i = 0; Image* other = fImagesTableModel->ImageAt(i); i++) { + if (fImage == other) { + fImagesTable->SelectRow(i, false); +printf("ImageListView::SetImage() done\n"); + return; + } + } } - fTeam = team; - - if (fTeam != NULL) { - fImagesTableModel = new(std::nothrow) ImagesTableModel(fTeam); - fImagesTable->SetTableModel(fImagesTableModel); - fImagesTable->ResizeAllColumnsToPreferred(); - fTeam->AddListener(this); - } + fImagesTable->DeselectAllRows(); +printf("ImageListView::SetImage() done\n"); } @@ -218,13 +235,18 @@ ImageListView::ImageRemoved(const Team::ImageEvent& event) void -ImageListView::TableRowInvoked(Table* table, int32 rowIndex) +ImageListView::TableSelectionChanged(Table* table) { -// if (fImagesTableModel != NULL) { -// Image* image = fImagesTableModel->ImageAt(rowIndex); -// if (image != NULL) -// fParent->OpenImageWindow(image); -// } + if (fListener == NULL) + return; + + Image* image = NULL; + if (fImagesTableModel != NULL) { + TableSelectionModel* selectionModel = table->SelectionModel(); + image = fImagesTableModel->ImageAt(selectionModel->RowAt(0)); + } + + fListener->ImageSelectionChanged(image); } @@ -241,4 +263,16 @@ ImageListView::_Init() B_TRUNCATE_END, B_ALIGN_LEFT)); fImagesTable->AddTableListener(this); + + fImagesTableModel = new ImagesTableModel(fTeam); + fImagesTable->SetTableModel(fImagesTableModel); + fImagesTable->ResizeAllColumnsToPreferred(); +} + + +// #pragma mark - Listener + + +ImageListView::Listener::~Listener() +{ } diff --git a/src/apps/debugger/gui/team_window/ImageListView.h b/src/apps/debugger/gui/team_window/ImageListView.h index 341c4e03cd..a27c20b7b6 100644 --- a/src/apps/debugger/gui/team_window/ImageListView.h +++ b/src/apps/debugger/gui/team_window/ImageListView.h @@ -14,13 +14,18 @@ class ImageListView : public BGroupView, private Team::Listener, private TableListener { public: - ImageListView(); + class Listener; + +public: + ImageListView(Team* team, Listener* listener); ~ImageListView(); - static ImageListView* Create(); + static ImageListView* Create(Team* team, Listener* listener); // throws - void SetTeam(Team* team); + void UnsetListener(); + + void SetImage(Image* image); virtual void MessageReceived(BMessage* message); @@ -33,14 +38,24 @@ private: virtual void ImageRemoved(const Team::ImageEvent& event); // TableListener - virtual void TableRowInvoked(Table* table, int32 rowIndex); + virtual void TableSelectionChanged(Table* table); void _Init(); private: Team* fTeam; + Image* fImage; Table* fImagesTable; ImagesTableModel* fImagesTableModel; + Listener* fListener; +}; + + +class ImageListView::Listener { +public: + virtual ~Listener(); + + virtual void ImageSelectionChanged(Image* image) = 0; }; diff --git a/src/apps/debugger/gui/team_window/StackTraceView.cpp b/src/apps/debugger/gui/team_window/StackTraceView.cpp index da18fb067a..dc1c660d62 100644 --- a/src/apps/debugger/gui/team_window/StackTraceView.cpp +++ b/src/apps/debugger/gui/team_window/StackTraceView.cpp @@ -241,7 +241,7 @@ StackTraceView::TableSelectionChanged(Table* table) void StackTraceView::_Init() { - fFramesTable = new Table("register list", 0, B_FANCY_BORDER); + fFramesTable = new Table("stack trace", 0, B_FANCY_BORDER); AddChild(fFramesTable->ToView()); fFramesTable->SetSortingEnabled(false); diff --git a/src/apps/debugger/gui/team_window/TeamWindow.cpp b/src/apps/debugger/gui/team_window/TeamWindow.cpp index b4c76eef16..630f5925a3 100644 --- a/src/apps/debugger/gui/team_window/TeamWindow.cpp +++ b/src/apps/debugger/gui/team_window/TeamWindow.cpp @@ -18,7 +18,8 @@ #include #include "CpuState.h" -#include "ImageListView.h" +#include "Image.h" +#include "ImageDebugInfo.h" #include "MessageCodes.h" #include "RegisterView.h" #include "SourceCode.h" @@ -35,6 +36,7 @@ TeamWindow::TeamWindow(TeamDebugModel* debugModel, Listener* listener) B_ASYNCHRONOUS_CONTROLS | B_AUTO_UPDATE_SIZE_LIMITS), fDebugModel(debugModel), fActiveThread(NULL), + fActiveImage(NULL), fActiveStackTrace(NULL), fActiveStackFrame(NULL), fActiveFunction(NULL), @@ -44,6 +46,7 @@ TeamWindow::TeamWindow(TeamDebugModel* debugModel, Listener* listener) fLocalsTabView(NULL), fThreadListView(NULL), fImageListView(NULL), + fImageFunctionsView(NULL), fRegisterView(NULL), fStackTraceView(NULL), fSourceView(NULL), @@ -79,6 +82,7 @@ TeamWindow::~TeamWindow() _SetActiveFunction(NULL); _SetActiveStackFrame(NULL); _SetActiveStackTrace(NULL); + _SetActiveImage(NULL); _SetActiveThread(NULL); } @@ -143,6 +147,16 @@ TeamWindow::MessageReceived(BMessage* message) break; } + case MSG_IMAGE_DEBUG_INFO_CHANGED: + { + int32 imageID; + if (message->FindInt32("image", &imageID) != B_OK) + break; + + _HandleImageDebugInfoChanged(imageID); + break; + } + case MSG_USER_BREAKPOINT_CHANGED: { uint64 address; @@ -180,6 +194,13 @@ TeamWindow::ThreadSelectionChanged(::Thread* thread) } +void +TeamWindow::ImageSelectionChanged(Image* image) +{ + _SetActiveImage(image); +} + + void TeamWindow::StackFrameSelectionChanged(StackFrame* frame) { @@ -187,6 +208,13 @@ TeamWindow::StackFrameSelectionChanged(StackFrame* frame) } +void +TeamWindow::FunctionSelectionChanged(FunctionDebugInfo* function) +{ + _SetActiveFunction(function); +} + + void TeamWindow::SetBreakpointRequested(target_addr_t address, bool enabled) { @@ -228,6 +256,15 @@ TeamWindow::ThreadStackTraceChanged(const Team::ThreadEvent& event) } +void +TeamWindow::ImageDebugInfoChanged(const Team::ImageEvent& event) +{ + BMessage message(MSG_IMAGE_DEBUG_INFO_CHANGED); + message.AddInt32("image", event.GetImage()->ID()); + PostMessage(&message); +} + + void TeamWindow::UserBreakpointChanged(const TeamDebugModel::BreakpointEvent& event) { @@ -288,8 +325,9 @@ TeamWindow::_Init() imagesGroup->SetName("Images"); fTabView->AddTab(imagesGroup); BLayoutBuilder::Split<>(imagesGroup) - .Add(fImageListView = ImageListView::Create()) - .Add(new BTextView("source files")); + .Add(fImageListView = ImageListView::Create(fDebugModel->GetTeam(), + this)) + .Add(fImageFunctionsView = ImageFunctionsView::Create(this)); // add local variables tab BView* tab = new BTextView("Variables"); @@ -300,7 +338,6 @@ TeamWindow::_Init() fLocalsTabView->AddTab(tab); fThreadListView->SetTeam(fDebugModel->GetTeam()); - fImageListView->SetTeam(fDebugModel->GetTeam()); fRunButton->SetMessage(new BMessage(MSG_THREAD_RUN)); fStepOverButton->SetMessage(new BMessage(MSG_THREAD_STEP_OVER)); @@ -336,7 +373,7 @@ TeamWindow::_SetActiveThread(::Thread* thread) StackTrace* stackTrace = fActiveThread != NULL ? fActiveThread->GetStackTrace() : NULL; Reference stackTraceReference(stackTrace); - // hold a reference until the register view has one + // hold a reference until we've set it locker.Unlock(); @@ -345,6 +382,40 @@ TeamWindow::_SetActiveThread(::Thread* thread) } +void +TeamWindow::_SetActiveImage(Image* image) +{ + if (image == fActiveImage) + return; + + if (fActiveImage != NULL) + fActiveImage->RemoveReference(); + + fActiveImage = image; + + AutoLocker locker(fDebugModel); + + ImageDebugInfo* imageDebugInfo = NULL; + Reference imageDebugInfoReference; + + if (fActiveImage != NULL) { + fActiveImage->AddReference(); + + imageDebugInfo = fActiveImage->GetImageDebugInfo(); + imageDebugInfoReference.SetTo(imageDebugInfo); + + // If the debug info is not loaded yet, request it. + if (fActiveImage->ImageDebugInfoState() == IMAGE_DEBUG_INFO_NOT_LOADED) + fListener->ImageDebugInfoRequested(this, fActiveImage); + } + + locker.Unlock(); + + fImageListView->SetImage(fActiveImage); + fImageFunctionsView->SetImageDebugInfo(imageDebugInfo); +} + + void TeamWindow::_SetActiveStackTrace(StackTrace* stackTrace) { @@ -403,8 +474,21 @@ TeamWindow::_SetActiveFunction(FunctionDebugInfo* function) fActiveFunction->RemoveReference(); } + // to avoid listener feedback problems, first unset the active function and + // set the new image, if any + locker.Unlock(); + + fActiveFunction = NULL; + + if (function != NULL) { + _SetActiveImage(fDebugModel->GetTeam()->ImageByAddress( + function->Address())); + } + fActiveFunction = function; + locker.Lock(); + SourceCode* sourceCode = NULL; Reference sourceCodeReference; @@ -423,6 +507,8 @@ TeamWindow::_SetActiveFunction(FunctionDebugInfo* function) locker.Unlock(); _SetActiveSourceCode(sourceCode); + + fImageFunctionsView->SetFunction(fActiveFunction); } @@ -543,6 +629,28 @@ TeamWindow::_HandleStackTraceChanged(thread_id threadID) } +void +TeamWindow::_HandleImageDebugInfoChanged(image_id imageID) +{ +printf("TeamWindow::_HandleImageDebugInfoChanged(%ld)\n", imageID); + // We're only interested in the currently selected thread + if (fActiveImage == NULL || imageID != fActiveImage->ID()) + return; + + AutoLocker locker(fDebugModel); + + ImageDebugInfo* imageDebugInfo = fActiveImage != NULL + ? fActiveImage->GetImageDebugInfo() : NULL; +printf(" image debug info: %p\n", imageDebugInfo); + Reference imageDebugInfoReference(imageDebugInfo); + // hold a reference until we've set it + + locker.Unlock(); + + fImageFunctionsView->SetImageDebugInfo(imageDebugInfo); +} + + void TeamWindow::_HandleSourceCodeChanged() { diff --git a/src/apps/debugger/gui/team_window/TeamWindow.h b/src/apps/debugger/gui/team_window/TeamWindow.h index ca3c926074..ca3ecaa69c 100644 --- a/src/apps/debugger/gui/team_window/TeamWindow.h +++ b/src/apps/debugger/gui/team_window/TeamWindow.h @@ -10,6 +10,8 @@ #include "SourceView.h" #include "FunctionDebugInfo.h" +#include "ImageFunctionsView.h" +#include "ImageListView.h" #include "StackTraceView.h" #include "Team.h" #include "TeamDebugModel.h" @@ -19,15 +21,16 @@ class BButton; class BTabView; class FunctionDebugInfo; -class ImageListView; +class Image; class RegisterView; class SourceCode; class StackFrame; -class TeamWindow : public BWindow, private ThreadListView::Listener, - StackTraceView::Listener, SourceView::Listener, Team::Listener, - private TeamDebugModel::Listener, FunctionDebugInfo::Listener { +class TeamWindow : public BWindow, ThreadListView::Listener, + ImageListView::Listener, StackTraceView::Listener, + ImageFunctionsView::Listener, SourceView::Listener, Team::Listener, + TeamDebugModel::Listener, FunctionDebugInfo::Listener { public: class Listener; @@ -47,9 +50,16 @@ private: // ThreadListView::Listener virtual void ThreadSelectionChanged(::Thread* thread); + // ImageListView::Listener + virtual void ImageSelectionChanged(Image* image); + // StackTraceView::Listener virtual void StackFrameSelectionChanged(StackFrame* frame); + // ImageFunctionsView::Listener + virtual void FunctionSelectionChanged( + FunctionDebugInfo* function); + // SourceView::Listener virtual void SetBreakpointRequested(target_addr_t address, bool enabled); @@ -62,6 +72,8 @@ private: const Team::ThreadEvent& event); virtual void ThreadStackTraceChanged( const Team::ThreadEvent& event); + virtual void ImageDebugInfoChanged( + const Team::ImageEvent& event); // TeamDebugModel::Listener virtual void UserBreakpointChanged( @@ -75,6 +87,7 @@ private: void _Init(); void _SetActiveThread(::Thread* thread); + void _SetActiveImage(Image* image); void _SetActiveStackTrace(StackTrace* stackTrace); void _SetActiveStackFrame(StackFrame* frame); void _SetActiveFunction(FunctionDebugInfo* function); @@ -85,6 +98,7 @@ private: void _HandleThreadStateChanged(thread_id threadID); void _HandleCpuStateChanged(thread_id threadID); void _HandleStackTraceChanged(thread_id threadID); + void _HandleImageDebugInfoChanged(image_id imageID); void _HandleSourceCodeChanged(); void _HandleUserBreakpointChanged( target_addr_t address); @@ -92,6 +106,7 @@ private: private: TeamDebugModel* fDebugModel; ::Thread* fActiveThread; + Image* fActiveImage; StackTrace* fActiveStackTrace; StackFrame* fActiveStackFrame; FunctionDebugInfo* fActiveFunction; @@ -101,6 +116,7 @@ private: BTabView* fLocalsTabView; ThreadListView* fThreadListView; ImageListView* fImageListView; + ImageFunctionsView* fImageFunctionsView; RegisterView* fRegisterView; StackTraceView* fStackTraceView; SourceView* fSourceView; @@ -117,6 +133,8 @@ public: virtual void FunctionSourceCodeRequested(TeamWindow* window, FunctionDebugInfo* function) = 0; + virtual void ImageDebugInfoRequested(TeamWindow* window, + Image* image) = 0; virtual void ThreadActionRequested(TeamWindow* window, thread_id threadID, uint32 action) = 0; virtual void SetBreakpointRequested(target_addr_t address, diff --git a/src/apps/debugger/model/Image.cpp b/src/apps/debugger/model/Image.cpp index 5987a992e5..3ad42ba7e5 100644 --- a/src/apps/debugger/model/Image.cpp +++ b/src/apps/debugger/model/Image.cpp @@ -6,13 +6,15 @@ #include "Image.h" #include "ImageDebugInfo.h" +#include "Team.h" Image::Image(Team* team,const ImageInfo& imageInfo) : fTeam(team), fInfo(imageInfo), - fDebugInfo(NULL) + fDebugInfo(NULL), + fDebugInfoState(IMAGE_DEBUG_INFO_NOT_LOADED) { } @@ -31,22 +33,6 @@ Image::Init() } -void -Image::SetImageDebugInfo(ImageDebugInfo* debugInfo) -{ - if (debugInfo == fDebugInfo) - return; - - if (fDebugInfo != NULL) - fDebugInfo->RemoveReference(); - - fDebugInfo = debugInfo; - - if (fDebugInfo != NULL) - fDebugInfo->AddReference(); -} - - bool Image::ContainsAddress(target_addr_t address) const { @@ -55,3 +41,24 @@ Image::ContainsAddress(target_addr_t address) const || (address >= fInfo.DataBase() && address < fInfo.DataBase() + fInfo.DataSize()); } + + +void +Image::SetImageDebugInfo(ImageDebugInfo* debugInfo, + image_debug_info_state state) +{ + if (debugInfo == fDebugInfo && state == fDebugInfoState) + return; + + if (fDebugInfo != NULL) + fDebugInfo->RemoveReference(); + + fDebugInfo = debugInfo; + fDebugInfoState = state; + + if (fDebugInfo != NULL) + fDebugInfo->AddReference(); + + // notify listeners + fTeam->NotifyImageDebugInfoChanged(this); +} diff --git a/src/apps/debugger/model/Image.h b/src/apps/debugger/model/Image.h index feeb96bc70..286789be14 100644 --- a/src/apps/debugger/model/Image.h +++ b/src/apps/debugger/model/Image.h @@ -13,6 +13,14 @@ #include "ImageInfo.h" +enum image_debug_info_state { + IMAGE_DEBUG_INFO_NOT_LOADED, + IMAGE_DEBUG_INFO_LOADING, + IMAGE_DEBUG_INFO_LOADED, + IMAGE_DEBUG_INFO_UNAVAILABLE +}; + + class ImageDebugInfo; class Team; @@ -30,15 +38,21 @@ public: const char* Name() const { return fInfo.Name(); } const ImageInfo& Info() const { return fInfo; } - ImageDebugInfo* GetImageDebugInfo() const { return fDebugInfo; } - void SetImageDebugInfo(ImageDebugInfo* debugInfo); - bool ContainsAddress(target_addr_t address) const; + // mutable attributes follow (locking required) + ImageDebugInfo* GetImageDebugInfo() const { return fDebugInfo; } + image_debug_info_state ImageDebugInfoState() const + { return fDebugInfoState; } + void SetImageDebugInfo(ImageDebugInfo* debugInfo, + image_debug_info_state state); + private: Team* fTeam; ImageInfo fInfo; + // mutable ImageDebugInfo* fDebugInfo; + image_debug_info_state fDebugInfoState; }; diff --git a/src/apps/debugger/model/Team.cpp b/src/apps/debugger/model/Team.cpp index 528584f7ad..d0c46ae670 100644 --- a/src/apps/debugger/model/Team.cpp +++ b/src/apps/debugger/model/Team.cpp @@ -250,6 +250,17 @@ Team::NotifyThreadStackTraceChanged(Thread* thread) } +void +Team::NotifyImageDebugInfoChanged(Image* image) +{ + for (ListenerList::Iterator it = fListeners.GetIterator(); + Listener* listener = it.Next();) { + listener->ImageDebugInfoChanged( + ImageEvent(TEAM_EVENT_IMAGE_DEBUG_INFO_CHANGED, image)); + } +} + + void Team::_NotifyThreadAdded(Thread* thread) { @@ -371,3 +382,9 @@ void Team::Listener::ThreadStackTraceChanged(const Team::ThreadEvent& event) { } + + +void +Team::Listener::ImageDebugInfoChanged(const Team::ImageEvent& event) +{ +} diff --git a/src/apps/debugger/model/Team.h b/src/apps/debugger/model/Team.h index 28f36a9d67..85419c12e7 100644 --- a/src/apps/debugger/model/Team.h +++ b/src/apps/debugger/model/Team.h @@ -22,7 +22,9 @@ enum { TEAM_EVENT_THREAD_STATE_CHANGED, TEAM_EVENT_THREAD_CPU_STATE_CHANGED, - TEAM_EVENT_THREAD_STACK_TRACE_CHANGED + TEAM_EVENT_THREAD_STACK_TRACE_CHANGED, + + TEAM_EVENT_IMAGE_DEBUG_INFO_CHANGED }; @@ -69,6 +71,9 @@ public: void NotifyThreadCpuStateChanged(Thread* thread); void NotifyThreadStackTraceChanged(Thread* thread); + // service methods for Image + void NotifyImageDebugInfoChanged(Image* image); + private: typedef DoublyLinkedList ListenerList; @@ -138,6 +143,9 @@ public: const Team::ThreadEvent& event); virtual void ThreadStackTraceChanged( const Team::ThreadEvent& event); + + virtual void ImageDebugInfoChanged( + const Team::ImageEvent& event); };