Debugger: Refactor non-interactive report handling.
- Add dedicated ReportDebugger application class for the case where we're asked to do nothing more than save a report. Also add a corresponding UserInterface subclass whose sole purpose is to take those necessary actions and then exit. - When the debugger is invoked via the --save-report option, we now start via the aforementioned report/interface rather than piggybacking on the CLI. - Clean up CommandLineUserInterface/CliContext to remove handling for the report saving option. Should hopefully resolve #12155.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2009-2012, Ingo Weinhold, [email protected].
|
* Copyright 2009-2012, Ingo Weinhold, [email protected].
|
||||||
* Copyright 2011-2014, Rene Gollent, [email protected].
|
* Copyright 2011-2015, Rene Gollent, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "GraphicalUserInterface.h"
|
#include "GraphicalUserInterface.h"
|
||||||
#include "ImageDebugLoadingStateHandlerRoster.h"
|
#include "ImageDebugLoadingStateHandlerRoster.h"
|
||||||
#include "MessageCodes.h"
|
#include "MessageCodes.h"
|
||||||
|
#include "ReportUserInterface.h"
|
||||||
#include "SettingsManager.h"
|
#include "SettingsManager.h"
|
||||||
#include "SignalSet.h"
|
#include "SignalSet.h"
|
||||||
#include "StartTeamWindow.h"
|
#include "StartTeamWindow.h"
|
||||||
@@ -143,7 +144,6 @@ parse_arguments(int argc, const char* const* argv, bool noOutput,
|
|||||||
|
|
||||||
case 's':
|
case 's':
|
||||||
{
|
{
|
||||||
options.useCLI = true;
|
|
||||||
options.saveReport = true;
|
options.saveReport = true;
|
||||||
options.reportPath = optarg;
|
options.reportPath = optarg;
|
||||||
break;
|
break;
|
||||||
@@ -393,6 +393,21 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ReportDebugger : private TeamDebugger::Listener {
|
||||||
|
public:
|
||||||
|
ReportDebugger();
|
||||||
|
~ReportDebugger();
|
||||||
|
bool Run(const Options& options);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// TeamDebugger::Listener
|
||||||
|
virtual void TeamDebuggerStarted(TeamDebugger* debugger);
|
||||||
|
virtual void TeamDebuggerRestartRequested(
|
||||||
|
TeamDebugger* debugger);
|
||||||
|
virtual void TeamDebuggerQuit(TeamDebugger* debugger);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - Debugger application class
|
// #pragma mark - Debugger application class
|
||||||
|
|
||||||
|
|
||||||
@@ -723,8 +738,7 @@ CliDebugger::Run(const Options& options)
|
|||||||
|
|
||||||
// create the command line UI
|
// create the command line UI
|
||||||
CommandLineUserInterface* userInterface
|
CommandLineUserInterface* userInterface
|
||||||
= new(std::nothrow) CommandLineUserInterface(options.saveReport,
|
= new(std::nothrow) CommandLineUserInterface();
|
||||||
options.reportPath, options.thread);
|
|
||||||
if (userInterface == NULL) {
|
if (userInterface == NULL) {
|
||||||
fprintf(stderr, "Error: Out of memory!\n");
|
fprintf(stderr, "Error: Out of memory!\n");
|
||||||
return false;
|
return false;
|
||||||
@@ -774,6 +788,89 @@ CliDebugger::TeamDebuggerQuit(TeamDebugger* debugger)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - ReportDebugger
|
||||||
|
|
||||||
|
|
||||||
|
ReportDebugger::ReportDebugger()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ReportDebugger::~ReportDebugger()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
ReportDebugger::Run(const Options& options)
|
||||||
|
{
|
||||||
|
// initialize global objects and settings manager
|
||||||
|
status_t error = global_init();
|
||||||
|
if (error != B_OK) {
|
||||||
|
fprintf(stderr, "Error: Global initialization failed: %s\n",
|
||||||
|
strerror(error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsManager settingsManager;
|
||||||
|
error = settingsManager.Init();
|
||||||
|
if (error != B_OK) {
|
||||||
|
fprintf(stderr, "Error: Settings manager initialization failed: "
|
||||||
|
"%s\n", strerror(error));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the report UI
|
||||||
|
ReportUserInterface* userInterface
|
||||||
|
= new(std::nothrow) ReportUserInterface(options.thread, options.reportPath);
|
||||||
|
if (userInterface == NULL) {
|
||||||
|
fprintf(stderr, "Error: Out of memory!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
BReference<UserInterface> userInterfaceReference(userInterface, true);
|
||||||
|
|
||||||
|
// get/run the program to be debugged and start the team debugger
|
||||||
|
DebuggedProgramInfo programInfo;
|
||||||
|
if (!get_debugged_program(options, programInfo))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
TeamDebugger* teamDebugger = start_team_debugger(programInfo.team,
|
||||||
|
&settingsManager, this, programInfo.thread,
|
||||||
|
programInfo.commandLineArgc, programInfo.commandLineArgv,
|
||||||
|
programInfo.stopInMain, userInterface);
|
||||||
|
if (teamDebugger == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
thread_id teamDebuggerThread = teamDebugger->Thread();
|
||||||
|
|
||||||
|
// run the input loop
|
||||||
|
userInterface->Run();
|
||||||
|
|
||||||
|
// wait for the team debugger thread to terminate
|
||||||
|
wait_for_thread(teamDebuggerThread, NULL);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ReportDebugger::TeamDebuggerStarted(TeamDebugger* debugger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ReportDebugger::TeamDebuggerRestartRequested(TeamDebugger* debugger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ReportDebugger::TeamDebuggerQuit(TeamDebugger* debugger)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
@@ -792,6 +889,9 @@ main(int argc, const char* const* argv)
|
|||||||
if (options.useCLI) {
|
if (options.useCLI) {
|
||||||
CliDebugger debugger;
|
CliDebugger debugger;
|
||||||
return debugger.Run(options) ? 0 : 1;
|
return debugger.Run(options) ? 0 : 1;
|
||||||
|
} else if (options.saveReport) {
|
||||||
|
ReportDebugger debugger;
|
||||||
|
return debugger.Run(options) ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debugger app;
|
Debugger app;
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface gui teams_window ] ;
|
|||||||
SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface gui utility_windows ] ;
|
SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface gui utility_windows ] ;
|
||||||
SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface gui util ] ;
|
SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface gui util ] ;
|
||||||
SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface gui value ] ;
|
SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface gui value ] ;
|
||||||
|
SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface report ] ;
|
||||||
SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface util ] ;
|
SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface util ] ;
|
||||||
SEARCH_SOURCE += [ FDirName $(SUBDIR) util ] ;
|
SEARCH_SOURCE += [ FDirName $(SUBDIR) util ] ;
|
||||||
SEARCH_SOURCE += [ FDirName $(SUBDIR) value ] ;
|
SEARCH_SOURCE += [ FDirName $(SUBDIR) value ] ;
|
||||||
@@ -321,6 +322,9 @@ local sources =
|
|||||||
TableCellValueRenderer.cpp
|
TableCellValueRenderer.cpp
|
||||||
TableCellValueRendererUtils.cpp
|
TableCellValueRendererUtils.cpp
|
||||||
|
|
||||||
|
# user_interface/report
|
||||||
|
ReportUserInterface.cpp
|
||||||
|
|
||||||
# user_interface/util
|
# user_interface/util
|
||||||
UiUtils.cpp
|
UiUtils.cpp
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012-2014, Rene Gollent, [email protected].
|
* Copyright 2012-2015, Rene Gollent, [email protected].
|
||||||
* Copyright 2012, Ingo Weinhold, [email protected].
|
* Copyright 2012, Ingo Weinhold, [email protected].
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
@@ -98,7 +98,6 @@ CliContext::CliContext()
|
|||||||
fInputLoopWaitingForEvents(0),
|
fInputLoopWaitingForEvents(0),
|
||||||
fEventsOccurred(0),
|
fEventsOccurred(0),
|
||||||
fInputLoopWaiting(false),
|
fInputLoopWaiting(false),
|
||||||
fInteractive(true),
|
|
||||||
fTerminating(false),
|
fTerminating(false),
|
||||||
fCurrentThread(NULL),
|
fCurrentThread(NULL),
|
||||||
fCurrentStackTrace(NULL),
|
fCurrentStackTrace(NULL),
|
||||||
@@ -219,13 +218,6 @@ CliContext::Terminating()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
CliContext::SetInteractive(bool interactive)
|
|
||||||
{
|
|
||||||
fInteractive = interactive;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
thread_id
|
thread_id
|
||||||
CliContext::CurrentThreadID() const
|
CliContext::CurrentThreadID() const
|
||||||
{
|
{
|
||||||
@@ -415,6 +407,7 @@ CliContext::ProcessPendingEvents()
|
|||||||
|
|
||||||
switch (event->Type()) {
|
switch (event->Type()) {
|
||||||
case EVENT_QUIT:
|
case EVENT_QUIT:
|
||||||
|
case EVENT_DEBUG_REPORT_CHANGED:
|
||||||
case EVENT_USER_INTERRUPT:
|
case EVENT_USER_INTERRUPT:
|
||||||
break;
|
break;
|
||||||
case EVENT_THREAD_ADDED:
|
case EVENT_THREAD_ADDED:
|
||||||
@@ -453,13 +446,6 @@ CliContext::ProcessPendingEvents()
|
|||||||
if (fExpressionValue != NULL)
|
if (fExpressionValue != NULL)
|
||||||
fExpressionValue->AcquireReference();
|
fExpressionValue->AcquireReference();
|
||||||
break;
|
break;
|
||||||
case EVENT_DEBUG_REPORT_CHANGED:
|
|
||||||
if (!IsInteractive()) {
|
|
||||||
Terminating();
|
|
||||||
QuitSession(true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de.
|
* Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||||
* Copyright 2014, Rene Gollent, rene@gollent.com.
|
* Copyright 2014-2015, Rene Gollent, rene@gollent.com.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
#ifndef CLI_CONTEXT_H
|
#ifndef CLI_CONTEXT_H
|
||||||
@@ -57,9 +57,6 @@ public:
|
|||||||
|
|
||||||
bool IsTerminating() const { return fTerminating; }
|
bool IsTerminating() const { return fTerminating; }
|
||||||
|
|
||||||
bool IsInteractive() const { return fInteractive; }
|
|
||||||
void SetInteractive(bool interactive);
|
|
||||||
|
|
||||||
// service methods for the input loop thread follow
|
// service methods for the input loop thread follow
|
||||||
|
|
||||||
Team* GetTeam() const { return fTeam; }
|
Team* GetTeam() const { return fTeam; }
|
||||||
@@ -150,7 +147,6 @@ private:
|
|||||||
uint32 fInputLoopWaitingForEvents;
|
uint32 fInputLoopWaitingForEvents;
|
||||||
uint32 fEventsOccurred;
|
uint32 fEventsOccurred;
|
||||||
bool fInputLoopWaiting;
|
bool fInputLoopWaiting;
|
||||||
bool fInteractive;
|
|
||||||
volatile bool fTerminating;
|
volatile bool fTerminating;
|
||||||
|
|
||||||
Thread* fCurrentThread;
|
Thread* fCurrentThread;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2011-2014, Rene Gollent, rene@gollent.com.
|
* Copyright 2011-2015, Rene Gollent, rene@gollent.com.
|
||||||
* Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de.
|
* Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
@@ -93,13 +93,9 @@ private:
|
|||||||
// #pragma mark - CommandLineUserInterface
|
// #pragma mark - CommandLineUserInterface
|
||||||
|
|
||||||
|
|
||||||
CommandLineUserInterface::CommandLineUserInterface(bool saveReport,
|
CommandLineUserInterface::CommandLineUserInterface()
|
||||||
const char* reportPath, thread_id reportTargetThread)
|
|
||||||
:
|
:
|
||||||
fCommands(20, true),
|
fCommands(20, true),
|
||||||
fReportPath(reportPath),
|
|
||||||
fSaveReport(saveReport),
|
|
||||||
fReportTargetThread(reportTargetThread),
|
|
||||||
fShowSemaphore(-1),
|
fShowSemaphore(-1),
|
||||||
fShown(false),
|
fShown(false),
|
||||||
fTerminating(false)
|
fTerminating(false)
|
||||||
@@ -128,8 +124,6 @@ CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener)
|
|||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
fContext.SetInteractive(!fSaveReport);
|
|
||||||
|
|
||||||
error = _RegisterCommands();
|
error = _RegisterCommands();
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
@@ -138,8 +132,6 @@ CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener)
|
|||||||
if (fShowSemaphore < 0)
|
if (fShowSemaphore < 0)
|
||||||
return fShowSemaphore;
|
return fShowSemaphore;
|
||||||
|
|
||||||
team->AddListener(this);
|
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,9 +168,7 @@ CommandLineUserInterface::Terminate()
|
|||||||
bool
|
bool
|
||||||
CommandLineUserInterface::IsInteractive() const
|
CommandLineUserInterface::IsInteractive() const
|
||||||
{
|
{
|
||||||
// if we were invoked solely for the purpose of saving a crash report,
|
return true;
|
||||||
// then we're not taking user input into account.
|
|
||||||
return !fSaveReport;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -237,43 +227,12 @@ CommandLineUserInterface::Run()
|
|||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (fSaveReport) {
|
|
||||||
ArgumentVector args;
|
|
||||||
char buffer[256];
|
|
||||||
const char* parseErrorLocation;
|
|
||||||
if (_ReportTargetThreadStopNeeded()) {
|
|
||||||
snprintf(buffer, sizeof(buffer), "stop %" B_PRId32,
|
|
||||||
fReportTargetThread);
|
|
||||||
args.Parse(buffer, &parseErrorLocation);
|
|
||||||
_ExecuteCommand(args.ArgumentCount(), args.Arguments());
|
|
||||||
} else
|
|
||||||
_SubmitSaveReport();
|
|
||||||
}
|
|
||||||
|
|
||||||
_InputLoop();
|
_InputLoop();
|
||||||
// Release the Show() semaphore to signal Terminate().
|
// Release the Show() semaphore to signal Terminate().
|
||||||
release_sem(fShowSemaphore);
|
release_sem(fShowSemaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
CommandLineUserInterface::ThreadStateChanged(const Team::ThreadEvent& event)
|
|
||||||
{
|
|
||||||
if (fSaveReport) {
|
|
||||||
Thread* thread = event.GetThread();
|
|
||||||
// If we were asked to attach/report on a specific thread
|
|
||||||
// rather than a team, and said thread was still
|
|
||||||
// running, when we attached, we need to wait for its corresponding
|
|
||||||
// stop state before generating a report, else we might not get its
|
|
||||||
// stack trace.
|
|
||||||
if (thread->ID() == fReportTargetThread
|
|
||||||
&& thread->State() == THREAD_STATE_STOPPED) {
|
|
||||||
_SubmitSaveReport();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*static*/ status_t
|
/*static*/ status_t
|
||||||
CommandLineUserInterface::_InputLoopEntry(void* data)
|
CommandLineUserInterface::_InputLoopEntry(void* data)
|
||||||
{
|
{
|
||||||
@@ -473,32 +432,3 @@ CommandLineUserInterface::_CompareCommandEntries(const CommandEntry* command1,
|
|||||||
{
|
{
|
||||||
return ::Compare(command1->Name(), command2->Name());
|
return ::Compare(command1->Name(), command2->Name());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
|
||||||
CommandLineUserInterface::_ReportTargetThreadStopNeeded() const
|
|
||||||
{
|
|
||||||
if (fReportTargetThread < 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Team* team = fContext.GetTeam();
|
|
||||||
AutoLocker<Team> teamLocker(team);
|
|
||||||
Thread* thread = team->ThreadByID(fReportTargetThread);
|
|
||||||
if (thread == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return thread->State() != THREAD_STATE_STOPPED;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
CommandLineUserInterface::_SubmitSaveReport()
|
|
||||||
{
|
|
||||||
ArgumentVector args;
|
|
||||||
char buffer[256];
|
|
||||||
const char* parseErrorLocation;
|
|
||||||
snprintf(buffer, sizeof(buffer), "save-report %s",
|
|
||||||
fReportPath != NULL ? fReportPath : "");
|
|
||||||
args.Parse(buffer, &parseErrorLocation);
|
|
||||||
_ExecuteCommand(args.ArgumentCount(), args.Arguments());
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2011-2014, Rene Gollent, rene@gollent.com.
|
* Copyright 2011-2015, Rene Gollent, rene@gollent.com.
|
||||||
* Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de.
|
* Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
@@ -17,12 +17,9 @@
|
|||||||
class CliCommand;
|
class CliCommand;
|
||||||
|
|
||||||
|
|
||||||
class CommandLineUserInterface : public UserInterface,
|
class CommandLineUserInterface : public UserInterface {
|
||||||
public ::Team::Listener {
|
|
||||||
public:
|
public:
|
||||||
CommandLineUserInterface(bool saveReport,
|
CommandLineUserInterface();
|
||||||
const char* reportPath,
|
|
||||||
thread_id reportTargetThread);
|
|
||||||
virtual ~CommandLineUserInterface();
|
virtual ~CommandLineUserInterface();
|
||||||
|
|
||||||
virtual const char* ID() const;
|
virtual const char* ID() const;
|
||||||
@@ -54,10 +51,6 @@ public:
|
|||||||
// everything has been set up. Enters the
|
// everything has been set up. Enters the
|
||||||
// input loop.
|
// input loop.
|
||||||
|
|
||||||
// Team::Listener
|
|
||||||
virtual void ThreadStateChanged(
|
|
||||||
const Team::ThreadEvent& event);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct CommandEntry;
|
struct CommandEntry;
|
||||||
typedef BObjectList<CommandEntry> CommandList;
|
typedef BObjectList<CommandEntry> CommandList;
|
||||||
@@ -82,15 +75,9 @@ private:
|
|||||||
const CommandEntry* command1,
|
const CommandEntry* command1,
|
||||||
const CommandEntry* command2);
|
const CommandEntry* command2);
|
||||||
|
|
||||||
bool _ReportTargetThreadStopNeeded() const;
|
|
||||||
void _SubmitSaveReport();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CliContext fContext;
|
CliContext fContext;
|
||||||
CommandList fCommands;
|
CommandList fCommands;
|
||||||
const char* fReportPath;
|
|
||||||
bool fSaveReport;
|
|
||||||
thread_id fReportTargetThread;
|
|
||||||
sem_id fShowSemaphore;
|
sem_id fShowSemaphore;
|
||||||
bool fShown;
|
bool fShown;
|
||||||
volatile bool fTerminating;
|
volatile bool fTerminating;
|
||||||
|
|||||||
@@ -0,0 +1,233 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015, Rene Gollent, rene@gollent.com.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "ReportUserInterface.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <Entry.h>
|
||||||
|
#include <FindDirectory.h>
|
||||||
|
#include <Path.h>
|
||||||
|
|
||||||
|
#include <AutoLocker.h>
|
||||||
|
|
||||||
|
#include "MessageCodes.h"
|
||||||
|
#include "UiUtils.h"
|
||||||
|
|
||||||
|
|
||||||
|
ReportUserInterface::ReportUserInterface(thread_id targetThread,
|
||||||
|
const char* reportPath)
|
||||||
|
:
|
||||||
|
fTeam(NULL),
|
||||||
|
fListener(NULL),
|
||||||
|
fTargetThread(targetThread),
|
||||||
|
fReportPath(reportPath),
|
||||||
|
fShowSemaphore(-1),
|
||||||
|
fReportSemaphore(-1),
|
||||||
|
fShown(false),
|
||||||
|
fTerminating(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ReportUserInterface::~ReportUserInterface()
|
||||||
|
{
|
||||||
|
if (fShowSemaphore >= 0)
|
||||||
|
delete_sem(fShowSemaphore);
|
||||||
|
|
||||||
|
fTeam->RemoveListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const char*
|
||||||
|
ReportUserInterface::ID() const
|
||||||
|
{
|
||||||
|
return "ReportUserInterface";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ReportUserInterface::Init(Team* team, UserInterfaceListener* listener)
|
||||||
|
{
|
||||||
|
fShowSemaphore = create_sem(0, "show report");
|
||||||
|
if (fShowSemaphore < 0)
|
||||||
|
return fShowSemaphore;
|
||||||
|
|
||||||
|
fReportSemaphore = create_sem(0, "report generator wait");
|
||||||
|
if (fReportSemaphore < 0)
|
||||||
|
return fReportSemaphore;
|
||||||
|
|
||||||
|
fTeam = team;
|
||||||
|
fListener = listener;
|
||||||
|
|
||||||
|
fTeam->AddListener(this);
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ReportUserInterface::Show()
|
||||||
|
{
|
||||||
|
fShown = true;
|
||||||
|
release_sem(fShowSemaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ReportUserInterface::Terminate()
|
||||||
|
{
|
||||||
|
fTerminating = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
ReportUserInterface::IsInteractive() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ReportUserInterface::LoadSettings(const TeamUiSettings* settings)
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ReportUserInterface::SaveSettings(TeamUiSettings*& settings) const
|
||||||
|
{
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ReportUserInterface::NotifyUser(const char* title, const char* message,
|
||||||
|
user_notification_type type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ReportUserInterface::NotifyBackgroundWorkStatus(const char* message)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32
|
||||||
|
ReportUserInterface::SynchronouslyAskUser(const char* title,
|
||||||
|
const char* message, const char* choice1, const char* choice2,
|
||||||
|
const char* choice3)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
status_t
|
||||||
|
ReportUserInterface::SynchronouslyAskUserForFile(entry_ref* _ref)
|
||||||
|
{
|
||||||
|
return B_UNSUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ReportUserInterface::Run()
|
||||||
|
{
|
||||||
|
// Wait for the Show() semaphore to be released.
|
||||||
|
status_t error;
|
||||||
|
do {
|
||||||
|
error = acquire_sem(fShowSemaphore);
|
||||||
|
} while (error == B_INTERRUPTED);
|
||||||
|
|
||||||
|
if (error != B_OK)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool waitNeeded = false;
|
||||||
|
if (fTargetThread > 0) {
|
||||||
|
AutoLocker< ::Team> teamLocker(fTeam);
|
||||||
|
::Thread* thread = fTeam->ThreadByID(fTargetThread);
|
||||||
|
if (thread == NULL)
|
||||||
|
waitNeeded = true;
|
||||||
|
else if (thread->State() != THREAD_STATE_STOPPED) {
|
||||||
|
waitNeeded = true;
|
||||||
|
fListener->ThreadActionRequested(fTargetThread, MSG_THREAD_STOP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waitNeeded) {
|
||||||
|
do {
|
||||||
|
error = acquire_sem(fShowSemaphore);
|
||||||
|
} while (error == B_INTERRUPTED);
|
||||||
|
|
||||||
|
if (error != B_OK)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry_ref ref;
|
||||||
|
if (fReportPath != NULL && fReportPath[0] == '/') {
|
||||||
|
error = get_ref_for_path(fReportPath, &ref);
|
||||||
|
} else {
|
||||||
|
char filename[B_FILE_NAME_LENGTH];
|
||||||
|
if (fReportPath != NULL)
|
||||||
|
strlcpy(filename, fReportPath, sizeof(filename));
|
||||||
|
else
|
||||||
|
UiUtils::ReportNameForTeam(fTeam, filename, sizeof(filename));
|
||||||
|
|
||||||
|
BPath path;
|
||||||
|
error = find_directory(B_DESKTOP_DIRECTORY, &path);
|
||||||
|
if (error == B_OK)
|
||||||
|
error = path.Append(filename);
|
||||||
|
if (error == B_OK)
|
||||||
|
error = get_ref_for_path(path.Path(), &ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != B_OK)
|
||||||
|
printf("Unable to get ref for report path %s\n", strerror(error));
|
||||||
|
else {
|
||||||
|
fListener->DebugReportRequested(&ref);
|
||||||
|
|
||||||
|
do {
|
||||||
|
error = acquire_sem(fReportSemaphore);
|
||||||
|
} while (error == B_INTERRUPTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
fListener->UserInterfaceQuitRequested(
|
||||||
|
UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ReportUserInterface::ThreadAdded(const Team::ThreadEvent& event)
|
||||||
|
{
|
||||||
|
::Thread* thread = event.GetThread();
|
||||||
|
if (thread->ID() != fTargetThread)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (thread->State() != THREAD_STATE_STOPPED)
|
||||||
|
fListener->ThreadActionRequested(thread->ID(), MSG_THREAD_STOP);
|
||||||
|
else
|
||||||
|
release_sem(fShowSemaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ReportUserInterface::ThreadStateChanged(const Team::ThreadEvent& event)
|
||||||
|
{
|
||||||
|
::Thread* thread = event.GetThread();
|
||||||
|
if (thread->ID() != fTargetThread)
|
||||||
|
return;
|
||||||
|
else if (thread->State() == THREAD_STATE_STOPPED)
|
||||||
|
release_sem(fShowSemaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ReportUserInterface::DebugReportChanged(const Team::DebugReportEvent& event)
|
||||||
|
{
|
||||||
|
printf("Debug report saved to %s\n", event.GetReportPath());
|
||||||
|
release_sem(fReportSemaphore);
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2015, Rene Gollent, rene@gollent.com.
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef REPORT_USER_INTERFACE_H
|
||||||
|
#define REPORT_USER_INTERFACE_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <ObjectList.h>
|
||||||
|
#include <String.h>
|
||||||
|
|
||||||
|
#include "UserInterface.h"
|
||||||
|
#include "Team.h"
|
||||||
|
|
||||||
|
|
||||||
|
class ReportUserInterface : public UserInterface,
|
||||||
|
private Team::Listener {
|
||||||
|
public:
|
||||||
|
ReportUserInterface(thread_id targetThread,
|
||||||
|
const char* reportPath);
|
||||||
|
virtual ~ReportUserInterface();
|
||||||
|
|
||||||
|
virtual const char* ID() const;
|
||||||
|
|
||||||
|
virtual status_t Init(Team* team,
|
||||||
|
UserInterfaceListener* listener);
|
||||||
|
virtual void Show();
|
||||||
|
virtual void Terminate();
|
||||||
|
|
||||||
|
virtual bool IsInteractive() const;
|
||||||
|
|
||||||
|
virtual status_t LoadSettings(const TeamUiSettings* settings);
|
||||||
|
virtual status_t SaveSettings(TeamUiSettings*& settings) const;
|
||||||
|
|
||||||
|
virtual void NotifyUser(const char* title,
|
||||||
|
const char* message,
|
||||||
|
user_notification_type type);
|
||||||
|
virtual void NotifyBackgroundWorkStatus(
|
||||||
|
const char* message);
|
||||||
|
virtual int32 SynchronouslyAskUser(const char* title,
|
||||||
|
const char* message, const char* choice1,
|
||||||
|
const char* choice2, const char* choice3);
|
||||||
|
virtual status_t SynchronouslyAskUserForFile(entry_ref* _ref);
|
||||||
|
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
// Team::Listener
|
||||||
|
virtual void ThreadAdded(const Team::ThreadEvent& event);
|
||||||
|
virtual void ThreadStateChanged(
|
||||||
|
const Team::ThreadEvent& event);
|
||||||
|
virtual void DebugReportChanged(
|
||||||
|
const Team::DebugReportEvent& event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
::Team* fTeam;
|
||||||
|
UserInterfaceListener* fListener;
|
||||||
|
thread_id fTargetThread;
|
||||||
|
const char* fReportPath;
|
||||||
|
sem_id fShowSemaphore;
|
||||||
|
sem_id fReportSemaphore;
|
||||||
|
bool fShown;
|
||||||
|
volatile bool fTerminating;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // REPORT_USER_INTERFACE_H
|
||||||
Reference in New Issue
Block a user