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 <Entry.h>
#include <InterfaceDefs.h>
#include <Message.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
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
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
TeamDebugger::ThreadStateChanged(const ::Team::ThreadEvent& event)
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -30,6 +30,9 @@ LoadSourceCodeJob::LoadSourceCodeJob(
fLoadForFunction(loadForFunction)
{
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
JobListener::JobDone(Job* job)
{
}
void
JobListener::JobWaitingForInput(Job* job)
{
}
void
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
Job::SetWorker(Worker* worker)
{
@ -179,6 +200,13 @@ Job::NotifyListeners()
for (int32 i = count - 1; i >= 0; i--) {
JobListener* listener = fListeners.ItemAt(i);
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:
listener->JobDone(this);
break;
@ -403,6 +431,7 @@ Worker::WaitForUserInput(Job* waitingJob)
return B_INTERRUPTED;
waitingJob->SetWaitStatus(JOB_USER_INPUT_WAITING);
waitingJob->NotifyListeners();
fSuspendedJobs.Add(waitingJob);
return B_OK;
@ -459,6 +488,7 @@ Worker::_ProcessJobs()
// process the next job
if (Job* job = fUnscheduledJobs.RemoveHead()) {
job->SetState(JOB_STATE_ACTIVE);
job->NotifyListeners();
locker.Unlock();
status_t error = job->Do();

View File

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