Debugger: Cleanups and improvements for status notification.

Worker/Job:
- Add job listener hooks for when work actually begins for a job,
  and when a job is suspended to wait for user input.
- Add hook for setting a job description string, and implement in several
  subclasses.

LoadImageDebugInfoJob:
- Get rid of ImageDebugInfoJobListener since its functionality can be
  handled via the more general job wait for user input hook. Refactor
  accordingly.

TeamDebugger:
- Adjust to use new job hooks. When a worker job is initiated, we now
  check if the job has a description, and if so pass it on to the UI
  to display a notification.

DwarfLoadingStateHandler:
- Notify the UI when a package download is in progress.

With these changes, the status bar now notifies the user if any of the
following actions are in flight:

1) Loading/parsing debug information
2) Stack trace retrieval
3) Source code retrieval
4) Downloading a debug info package
This commit is contained in:
Rene Gollent 2015-08-15 16:41:24 -04:00
parent 4c7fff8044
commit 9d9c74ecdb
11 changed files with 110 additions and 106 deletions

View File

@ -13,6 +13,7 @@
#include <new> #include <new>
#include <Entry.h> #include <Entry.h>
#include <InterfaceDefs.h>
#include <Message.h> #include <Message.h>
#include <StringList.h> #include <StringList.h>
@ -1369,6 +1370,17 @@ TeamDebugger::UserInterfaceQuitRequested(QuitOption quitOption)
} }
void
TeamDebugger::JobStarted(Job* job)
{
BString description(job->GetDescription());
if (!description.IsEmpty()) {
description.Append(B_UTF8_ELLIPSIS);
fUserInterface->NotifyBackgroundWorkStatus(description.String());
}
}
void void
TeamDebugger::JobDone(Job* job) TeamDebugger::JobDone(Job* job)
{ {
@ -1377,6 +1389,21 @@ TeamDebugger::JobDone(Job* job)
} }
void
TeamDebugger::JobWaitingForInput(Job* job)
{
LoadImageDebugInfoJob* infoJob = dynamic_cast<LoadImageDebugInfoJob*>(job);
if (infoJob == NULL)
return;
BMessage message(MSG_DEBUG_INFO_NEEDS_USER_INPUT);
message.AddPointer("job", infoJob);
message.AddPointer("state", infoJob->GetLoadingState());
PostMessage(&message);
}
void void
TeamDebugger::JobFailed(Job* job) TeamDebugger::JobFailed(Job* job)
{ {
@ -1396,30 +1423,6 @@ TeamDebugger::JobAborted(Job* job)
} }
void
TeamDebugger::ImageDebugInfoJobNeedsUserInput(Job* job,
ImageDebugInfoLoadingState* state)
{
TRACE_JOBS("TeamDebugger::DebugInfoJobNeedsUserInput(%p, %p)\n",
job, state);
BMessage message(MSG_DEBUG_INFO_NEEDS_USER_INPUT);
message.AddPointer("job", job);
message.AddPointer("state", state);
PostMessage(&message);
}
void
TeamDebugger::ImageDebugInfoJobInProgress(Image* image)
{
BString message;
message.SetToFormat("Loading debug information for %s...",
image->Name().String());
fUserInterface->NotifyBackgroundWorkStatus(message.String());
}
void void
TeamDebugger::ThreadStateChanged(const ::Team::ThreadEvent& event) TeamDebugger::ThreadStateChanged(const ::Team::ThreadEvent& event)
{ {

View File

@ -32,8 +32,7 @@ class WatchpointManager;
class TeamDebugger : public BLooper, private UserInterfaceListener, class TeamDebugger : public BLooper, private UserInterfaceListener,
private JobListener, private ImageDebugInfoJobListener, private JobListener, private Team::Listener {
private Team::Listener {
public: public:
class Listener; class Listener;
@ -134,14 +133,12 @@ private:
QuitOption quitOption); QuitOption quitOption);
// JobListener // JobListener
virtual void JobStarted(Job* job);
virtual void JobDone(Job* job); virtual void JobDone(Job* job);
virtual void JobWaitingForInput(Job* job);
virtual void JobFailed(Job* job); virtual void JobFailed(Job* job);
virtual void JobAborted(Job* job); virtual void JobAborted(Job* job);
virtual void ImageDebugInfoJobNeedsUserInput(Job* job,
ImageDebugInfoLoadingState* state);
virtual void ImageDebugInfoJobInProgress(Image* image);
// Team::Listener // Team::Listener
virtual void ThreadStateChanged( virtual void ThreadStateChanged(
const ::Team::ThreadEvent& event); const ::Team::ThreadEvent& event);

View File

@ -76,14 +76,13 @@ private:
ThreadHandler::ThreadHandler(Thread* thread, Worker* worker, ThreadHandler::ThreadHandler(Thread* thread, Worker* worker,
DebuggerInterface* debuggerInterface, DebuggerInterface* debuggerInterface, JobListener* jobListener,
ImageDebugInfoJobListener* listener,
BreakpointManager* breakpointManager) BreakpointManager* breakpointManager)
: :
fThread(thread), fThread(thread),
fWorker(worker), fWorker(worker),
fDebuggerInterface(debuggerInterface), fDebuggerInterface(debuggerInterface),
fDebugInfoJobListener(listener), fJobListener(jobListener),
fBreakpointManager(breakpointManager), fBreakpointManager(breakpointManager),
fStepMode(STEP_NONE), fStepMode(STEP_NONE),
fStepStatement(NULL), fStepStatement(NULL),
@ -113,7 +112,7 @@ void
ThreadHandler::Init() ThreadHandler::Init()
{ {
fWorker->ScheduleJob(new(std::nothrow) GetThreadStateJob(fDebuggerInterface, fWorker->ScheduleJob(new(std::nothrow) GetThreadStateJob(fDebuggerInterface,
fThread)); fThread), fJobListener);
fConditionWaitSem = create_sem(0, "breakpoint condition waiter"); fConditionWaitSem = create_sem(0, "breakpoint condition waiter");
} }
@ -458,7 +457,8 @@ ThreadHandler::HandleThreadStateChanged()
if (fThread->State() == THREAD_STATE_STOPPED if (fThread->State() == THREAD_STATE_STOPPED
&& fThread->GetCpuState() == NULL) { && fThread->GetCpuState() == NULL) {
fWorker->ScheduleJob( fWorker->ScheduleJob(
new(std::nothrow) GetCpuStateJob(fDebuggerInterface, fThread)); new(std::nothrow) GetCpuStateJob(fDebuggerInterface, fThread),
fJobListener);
} }
} }
@ -475,8 +475,8 @@ ThreadHandler::HandleCpuStateChanged()
if (fThread->GetCpuState() != NULL && fThread->GetStackTrace() == NULL) { if (fThread->GetCpuState() != NULL && fThread->GetStackTrace() == NULL) {
fWorker->ScheduleJob( fWorker->ScheduleJob(
new(std::nothrow) GetStackTraceJob(fDebuggerInterface, new(std::nothrow) GetStackTraceJob(fDebuggerInterface,
fDebugInfoJobListener, fDebuggerInterface->GetArchitecture(), fJobListener, fDebuggerInterface->GetArchitecture(),
fThread)); fThread), fJobListener);
} }
} }
@ -954,7 +954,8 @@ ThreadHandler::_HandleBreakpointConditionIfNeeded(CpuState* cpuState)
status_t error = fWorker->ScheduleJob( status_t error = fWorker->ScheduleJob(
new(std::nothrow) ExpressionEvaluationJob(fThread->GetTeam(), new(std::nothrow) ExpressionEvaluationJob(fThread->GetTeam(),
fDebuggerInterface, language, expressionInfo, frame, fThread)); fDebuggerInterface, language, expressionInfo, frame, fThread),
fJobListener);
BPrivate::ObjectDeleter<ExpressionEvaluationListener> deleter( BPrivate::ObjectDeleter<ExpressionEvaluationListener> deleter(
listener); listener);

View File

@ -20,6 +20,7 @@ class BreakpointManager;
class DebuggerInterface; class DebuggerInterface;
class ExpressionResult; class ExpressionResult;
class ImageDebugInfoJobListener; class ImageDebugInfoJobListener;
class JobListener;
class StackFrame; class StackFrame;
class Statement; class Statement;
class Worker; class Worker;
@ -30,7 +31,7 @@ class ThreadHandler : public BReferenceable, private ImageDebugInfoProvider,
public: public:
ThreadHandler(Thread* thread, Worker* worker, ThreadHandler(Thread* thread, Worker* worker,
DebuggerInterface* debuggerInterface, DebuggerInterface* debuggerInterface,
ImageDebugInfoJobListener* listener, JobListener* listener,
BreakpointManager* breakpointManager); BreakpointManager* breakpointManager);
~ThreadHandler(); ~ThreadHandler();
@ -117,7 +118,7 @@ private:
Thread* fThread; Thread* fThread;
Worker* fWorker; Worker* fWorker;
DebuggerInterface* fDebuggerInterface; DebuggerInterface* fDebuggerInterface;
ImageDebugInfoJobListener* fDebugInfoJobListener; JobListener* fJobListener;
BreakpointManager* fBreakpointManager; BreakpointManager* fBreakpointManager;
uint32 fStepMode; uint32 fStepMode;
Statement* fStepStatement; Statement* fStepStatement;

View File

@ -9,6 +9,7 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <Entry.h> #include <Entry.h>
#include <InterfaceDefs.h>
#include <Path.h> #include <Path.h>
#include <package/solver/Solver.h> #include <package/solver/Solver.h>
#include <package/solver/SolverPackage.h> #include <package/solver/SolverPackage.h>
@ -108,6 +109,10 @@ DwarfLoadingStateHandler::HandleState(
BString command; BString command;
command.SetToFormat("/bin/pkgman install -y %s", command.SetToFormat("/bin/pkgman install -y %s",
requiredPackage.String()); requiredPackage.String());
BString notification;
notification.SetToFormat("Installing package %s" B_UTF8_ELLIPSIS,
requiredPackage.String());
interface->NotifyBackgroundWorkStatus(notification);
int error = system(command.String()); int error = system(command.String());
if (interface->IsInteractive()) { if (interface->IsInteractive()) {
if (WIFEXITED(error)) { if (WIFEXITED(error)) {

View File

@ -18,12 +18,11 @@
GetStackTraceJob::GetStackTraceJob(DebuggerInterface* debuggerInterface, GetStackTraceJob::GetStackTraceJob(DebuggerInterface* debuggerInterface,
ImageDebugInfoJobListener* listener, Architecture* architecture, JobListener* listener, Architecture* architecture, Thread* thread)
Thread* thread)
: :
fKey(thread, JOB_TYPE_GET_STACK_TRACE), fKey(thread, JOB_TYPE_GET_STACK_TRACE),
fDebuggerInterface(debuggerInterface), fDebuggerInterface(debuggerInterface),
fDebugInfoJobListener(listener), fJobListener(listener),
fArchitecture(architecture), fArchitecture(architecture),
fThread(thread) fThread(thread)
{ {
@ -32,6 +31,9 @@ GetStackTraceJob::GetStackTraceJob(DebuggerInterface* debuggerInterface,
fCpuState = fThread->GetCpuState(); fCpuState = fThread->GetCpuState();
if (fCpuState != NULL) if (fCpuState != NULL)
fCpuState->AcquireReference(); fCpuState->AcquireReference();
SetDescription("Retrieving stack trace for thread %" B_PRId32, fThread->ID());
} }
@ -84,7 +86,7 @@ GetStackTraceJob::GetImageDebugInfo(Image* image, ImageDebugInfo*& _info)
// schedule a job, if not loaded // schedule a job, if not loaded
ImageDebugInfo* info; ImageDebugInfo* info;
status_t error = LoadImageDebugInfoJob::ScheduleIfNecessary(GetWorker(), status_t error = LoadImageDebugInfoJob::ScheduleIfNecessary(GetWorker(),
image, fDebugInfoJobListener, &info); image, fJobListener, &info);
if (error != B_OK) if (error != B_OK)
return error; return error;

View File

@ -92,20 +92,11 @@ private:
}; };
class ImageDebugInfoJobListener {
public:
virtual ~ImageDebugInfoJobListener();
virtual void ImageDebugInfoJobNeedsUserInput(Job* job,
ImageDebugInfoLoadingState* state);
virtual void ImageDebugInfoJobInProgress(Image* image);
};
class GetStackTraceJob : public Job, private ImageDebugInfoProvider { class GetStackTraceJob : public Job, private ImageDebugInfoProvider {
public: public:
GetStackTraceJob( GetStackTraceJob(
DebuggerInterface* debuggerInterface, DebuggerInterface* debuggerInterface,
ImageDebugInfoJobListener* listener, JobListener* jobListener,
Architecture* architecture, Thread* thread); Architecture* architecture, Thread* thread);
virtual ~GetStackTraceJob(); virtual ~GetStackTraceJob();
@ -120,7 +111,7 @@ private:
private: private:
SimpleJobKey fKey; SimpleJobKey fKey;
DebuggerInterface* fDebuggerInterface; DebuggerInterface* fDebuggerInterface;
ImageDebugInfoJobListener* fDebugInfoJobListener; JobListener* fJobListener;
Architecture* fArchitecture; Architecture* fArchitecture;
Thread* fThread; Thread* fThread;
CpuState* fCpuState; CpuState* fCpuState;
@ -129,8 +120,7 @@ private:
class LoadImageDebugInfoJob : public Job { class LoadImageDebugInfoJob : public Job {
public: public:
LoadImageDebugInfoJob(Image* image, LoadImageDebugInfoJob(Image* image);
ImageDebugInfoJobListener* listener);
virtual ~LoadImageDebugInfoJob(); virtual ~LoadImageDebugInfoJob();
virtual const JobKey& Key() const; virtual const JobKey& Key() const;
@ -138,7 +128,7 @@ public:
static status_t ScheduleIfNecessary(Worker* worker, static status_t ScheduleIfNecessary(Worker* worker,
Image* image, Image* image,
ImageDebugInfoJobListener* listener, JobListener* jobListener,
ImageDebugInfo** _imageDebugInfo = NULL); ImageDebugInfo** _imageDebugInfo = NULL);
// If already loaded returns a // If already loaded returns a
// reference, if desired. If not loaded // reference, if desired. If not loaded
@ -147,19 +137,16 @@ public:
// if scheduling the job failed, or the // if scheduling the job failed, or the
// debug info already failed to load // debug info already failed to load
// earlier. // earlier.
private:
void NotifyUserInputListener();
private:
typedef BObjectList<ImageDebugInfoJobListener> ListenerList;
ImageDebugInfoLoadingState*
GetLoadingState()
{ return &fState; }
private: private:
SimpleJobKey fKey; SimpleJobKey fKey;
Image* fImage; Image* fImage;
ImageDebugInfoLoadingState ImageDebugInfoLoadingState
fState; fState;
ImageDebugInfoJobListener* fListener;
}; };

View File

@ -14,39 +14,19 @@
#include "Team.h" #include "Team.h"
// #pragma mark - ImageDebugInfoJobListener
ImageDebugInfoJobListener::~ImageDebugInfoJobListener()
{
}
void
ImageDebugInfoJobListener::ImageDebugInfoJobNeedsUserInput(Job* job,
ImageDebugInfoLoadingState* state)
{
}
void
ImageDebugInfoJobListener::ImageDebugInfoJobInProgress(Image* image)
{
}
// #pragma mark - LoadImageDebugInfoJob // #pragma mark - LoadImageDebugInfoJob
LoadImageDebugInfoJob::LoadImageDebugInfoJob(Image* image, LoadImageDebugInfoJob::LoadImageDebugInfoJob(Image* image)
ImageDebugInfoJobListener* listener)
: :
fKey(image, JOB_TYPE_LOAD_IMAGE_DEBUG_INFO), fKey(image, JOB_TYPE_LOAD_IMAGE_DEBUG_INFO),
fImage(image), fImage(image),
fState(), fState()
fListener(listener)
{ {
fImage->AcquireReference(); fImage->AcquireReference();
SetDescription("Loading debugging information for %s",
fImage->Name().String());
} }
@ -71,9 +51,6 @@ LoadImageDebugInfoJob::Do()
ImageInfo imageInfo(fImage->Info()); ImageInfo imageInfo(fImage->Info());
locker.Unlock(); locker.Unlock();
if (fListener != NULL)
fListener->ImageDebugInfoJobInProgress(fImage);
// create the debug info // create the debug info
ImageDebugInfo* debugInfo; ImageDebugInfo* debugInfo;
status_t error = fImage->GetTeam()->DebugInfo()->LoadImageDebugInfo( status_t error = fImage->GetTeam()->DebugInfo()->LoadImageDebugInfo(
@ -83,7 +60,6 @@ LoadImageDebugInfoJob::Do()
locker.Lock(); locker.Lock();
if (fState.UserInputRequired()) { if (fState.UserInputRequired()) {
NotifyUserInputListener();
return WaitForUserInput(); return WaitForUserInput();
} else if (error == B_OK) { } else if (error == B_OK) {
error = fImage->SetImageDebugInfo(debugInfo, IMAGE_DEBUG_INFO_LOADED); error = fImage->SetImageDebugInfo(debugInfo, IMAGE_DEBUG_INFO_LOADED);
@ -97,7 +73,7 @@ LoadImageDebugInfoJob::Do()
/*static*/ status_t /*static*/ status_t
LoadImageDebugInfoJob::ScheduleIfNecessary(Worker* worker, Image* image, LoadImageDebugInfoJob::ScheduleIfNecessary(Worker* worker, Image* image,
ImageDebugInfoJobListener* listener, ImageDebugInfo** _imageDebugInfo) JobListener* listener, ImageDebugInfo** _imageDebugInfo)
{ {
AutoLocker<Team> teamLocker(image->GetTeam()); AutoLocker<Team> teamLocker(image->GetTeam());
@ -122,12 +98,12 @@ LoadImageDebugInfoJob::ScheduleIfNecessary(Worker* worker, Image* image,
return B_ERROR; return B_ERROR;
// schedule a job // schedule a job
LoadImageDebugInfoJob* job = new(std::nothrow) LoadImageDebugInfoJob(image, LoadImageDebugInfoJob* job = new(std::nothrow) LoadImageDebugInfoJob(
listener); image);
if (job == NULL) if (job == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
status_t error = worker->ScheduleJob(job); status_t error = worker->ScheduleJob(job, listener);
if (error != B_OK) { if (error != B_OK) {
image->SetImageDebugInfo(NULL, IMAGE_DEBUG_INFO_UNAVAILABLE); image->SetImageDebugInfo(NULL, IMAGE_DEBUG_INFO_UNAVAILABLE);
return error; return error;
@ -139,12 +115,3 @@ LoadImageDebugInfoJob::ScheduleIfNecessary(Worker* worker, Image* image,
*_imageDebugInfo = NULL; *_imageDebugInfo = NULL;
return B_OK; return B_OK;
} }
void
LoadImageDebugInfoJob::NotifyUserInputListener()
{
if (fListener != NULL)
fListener->ImageDebugInfoJobNeedsUserInput(this, &fState);
}

View File

@ -30,6 +30,9 @@ LoadSourceCodeJob::LoadSourceCodeJob(
fLoadForFunction(loadForFunction) fLoadForFunction(loadForFunction)
{ {
fFunctionInstance->AcquireReference(); fFunctionInstance->AcquireReference();
SetDescription("Loading source code for function %s",
fFunctionInstance->PrettyName().String());
} }

View File

@ -70,12 +70,24 @@ JobListener::~JobListener()
} }
void
JobListener::JobStarted(Job* job)
{
}
void void
JobListener::JobDone(Job* job) JobListener::JobDone(Job* job)
{ {
} }
void
JobListener::JobWaitingForInput(Job* job)
{
}
void void
JobListener::JobFailed(Job* job) JobListener::JobFailed(Job* job)
{ {
@ -121,6 +133,15 @@ Job::WaitForUserInput()
} }
void
Job::SetDescription(const char* format, ...)
{
va_list args;
va_start(args, format);
fDescription.SetToFormatVarArgs(format, args);
}
void void
Job::SetWorker(Worker* worker) Job::SetWorker(Worker* worker)
{ {
@ -179,6 +200,13 @@ Job::NotifyListeners()
for (int32 i = count - 1; i >= 0; i--) { for (int32 i = count - 1; i >= 0; i--) {
JobListener* listener = fListeners.ItemAt(i); JobListener* listener = fListeners.ItemAt(i);
switch (fState) { switch (fState) {
case JOB_STATE_ACTIVE:
listener->JobStarted(this);
break;
case JOB_STATE_WAITING:
if (fWaitStatus == JOB_USER_INPUT_WAITING)
listener->JobWaitingForInput(this);
break;
case JOB_STATE_SUCCEEDED: case JOB_STATE_SUCCEEDED:
listener->JobDone(this); listener->JobDone(this);
break; break;
@ -403,6 +431,7 @@ Worker::WaitForUserInput(Job* waitingJob)
return B_INTERRUPTED; return B_INTERRUPTED;
waitingJob->SetWaitStatus(JOB_USER_INPUT_WAITING); waitingJob->SetWaitStatus(JOB_USER_INPUT_WAITING);
waitingJob->NotifyListeners();
fSuspendedJobs.Add(waitingJob); fSuspendedJobs.Add(waitingJob);
return B_OK; return B_OK;
@ -459,6 +488,7 @@ Worker::_ProcessJobs()
// process the next job // process the next job
if (Job* job = fUnscheduledJobs.RemoveHead()) { if (Job* job = fUnscheduledJobs.RemoveHead()) {
job->SetState(JOB_STATE_ACTIVE); job->SetState(JOB_STATE_ACTIVE);
job->NotifyListeners();
locker.Unlock(); locker.Unlock();
status_t error = job->Do(); status_t error = job->Do();

View File

@ -10,6 +10,7 @@
#include <ObjectList.h> #include <ObjectList.h>
#include <Referenceable.h> #include <Referenceable.h>
#include <String.h>
#include <util/DoublyLinkedList.h> #include <util/DoublyLinkedList.h>
#include <util/OpenHashTable.h> #include <util/OpenHashTable.h>
@ -69,7 +70,9 @@ class JobListener {
public: public:
virtual ~JobListener(); virtual ~JobListener();
virtual void JobStarted(Job* job);
virtual void JobDone(Job* job); virtual void JobDone(Job* job);
virtual void JobWaitingForInput(Job* job);
virtual void JobFailed(Job* job); virtual void JobFailed(Job* job);
virtual void JobAborted(Job* job); virtual void JobAborted(Job* job);
}; };
@ -89,9 +92,13 @@ public:
Worker* GetWorker() const { return fWorker; } Worker* GetWorker() const { return fWorker; }
job_state State() const { return fState; } job_state State() const { return fState; }
const BString& GetDescription() const
{ return fDescription; }
protected: protected:
job_wait_status WaitFor(const JobKey& key); job_wait_status WaitFor(const JobKey& key);
status_t WaitForUserInput(); status_t WaitForUserInput();
void SetDescription(const char* format, ...);
private: private:
friend class Worker; friend class Worker;
@ -122,6 +129,7 @@ private:
JobList fDependentJobs; JobList fDependentJobs;
job_wait_status fWaitStatus; job_wait_status fWaitStatus;
ListenerList fListeners; ListenerList fListeners;
BString fDescription;
public: public:
Job* fNext; Job* fNext;