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 2011-2014, Rene Gollent, [email protected].
|
||||
* Copyright 2011-2015, Rene Gollent, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "GraphicalUserInterface.h"
|
||||
#include "ImageDebugLoadingStateHandlerRoster.h"
|
||||
#include "MessageCodes.h"
|
||||
#include "ReportUserInterface.h"
|
||||
#include "SettingsManager.h"
|
||||
#include "SignalSet.h"
|
||||
#include "StartTeamWindow.h"
|
||||
@@ -143,7 +144,6 @@ parse_arguments(int argc, const char* const* argv, bool noOutput,
|
||||
|
||||
case 's':
|
||||
{
|
||||
options.useCLI = true;
|
||||
options.saveReport = true;
|
||||
options.reportPath = optarg;
|
||||
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
|
||||
|
||||
|
||||
@@ -723,8 +738,7 @@ CliDebugger::Run(const Options& options)
|
||||
|
||||
// create the command line UI
|
||||
CommandLineUserInterface* userInterface
|
||||
= new(std::nothrow) CommandLineUserInterface(options.saveReport,
|
||||
options.reportPath, options.thread);
|
||||
= new(std::nothrow) CommandLineUserInterface();
|
||||
if (userInterface == NULL) {
|
||||
fprintf(stderr, "Error: Out of memory!\n");
|
||||
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 -
|
||||
|
||||
|
||||
@@ -792,6 +889,9 @@ main(int argc, const char* const* argv)
|
||||
if (options.useCLI) {
|
||||
CliDebugger debugger;
|
||||
return debugger.Run(options) ? 0 : 1;
|
||||
} else if (options.saveReport) {
|
||||
ReportDebugger debugger;
|
||||
return debugger.Run(options) ? 0 : 1;
|
||||
}
|
||||
|
||||
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 util ] ;
|
||||
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) util ] ;
|
||||
SEARCH_SOURCE += [ FDirName $(SUBDIR) value ] ;
|
||||
@@ -321,6 +322,9 @@ local sources =
|
||||
TableCellValueRenderer.cpp
|
||||
TableCellValueRendererUtils.cpp
|
||||
|
||||
# user_interface/report
|
||||
ReportUserInterface.cpp
|
||||
|
||||
# user_interface/util
|
||||
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].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
@@ -98,7 +98,6 @@ CliContext::CliContext()
|
||||
fInputLoopWaitingForEvents(0),
|
||||
fEventsOccurred(0),
|
||||
fInputLoopWaiting(false),
|
||||
fInteractive(true),
|
||||
fTerminating(false),
|
||||
fCurrentThread(NULL),
|
||||
fCurrentStackTrace(NULL),
|
||||
@@ -219,13 +218,6 @@ CliContext::Terminating()
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CliContext::SetInteractive(bool interactive)
|
||||
{
|
||||
fInteractive = interactive;
|
||||
}
|
||||
|
||||
|
||||
thread_id
|
||||
CliContext::CurrentThreadID() const
|
||||
{
|
||||
@@ -415,6 +407,7 @@ CliContext::ProcessPendingEvents()
|
||||
|
||||
switch (event->Type()) {
|
||||
case EVENT_QUIT:
|
||||
case EVENT_DEBUG_REPORT_CHANGED:
|
||||
case EVENT_USER_INTERRUPT:
|
||||
break;
|
||||
case EVENT_THREAD_ADDED:
|
||||
@@ -453,13 +446,6 @@ CliContext::ProcessPendingEvents()
|
||||
if (fExpressionValue != NULL)
|
||||
fExpressionValue->AcquireReference();
|
||||
break;
|
||||
case EVENT_DEBUG_REPORT_CHANGED:
|
||||
if (!IsInteractive()) {
|
||||
Terminating();
|
||||
QuitSession(true);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright 2012, Ingo Weinhold, [email protected].
|
||||
* Copyright 2014, Rene Gollent, [email protected].
|
||||
* Copyright 2014-2015, Rene Gollent, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef CLI_CONTEXT_H
|
||||
@@ -57,9 +57,6 @@ public:
|
||||
|
||||
bool IsTerminating() const { return fTerminating; }
|
||||
|
||||
bool IsInteractive() const { return fInteractive; }
|
||||
void SetInteractive(bool interactive);
|
||||
|
||||
// service methods for the input loop thread follow
|
||||
|
||||
Team* GetTeam() const { return fTeam; }
|
||||
@@ -150,7 +147,6 @@ private:
|
||||
uint32 fInputLoopWaitingForEvents;
|
||||
uint32 fEventsOccurred;
|
||||
bool fInputLoopWaiting;
|
||||
bool fInteractive;
|
||||
volatile bool fTerminating;
|
||||
|
||||
Thread* fCurrentThread;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014, Rene Gollent, [email protected].
|
||||
* Copyright 2011-2015, Rene Gollent, [email protected].
|
||||
* Copyright 2012, Ingo Weinhold, [email protected].
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
@@ -93,13 +93,9 @@ private:
|
||||
// #pragma mark - CommandLineUserInterface
|
||||
|
||||
|
||||
CommandLineUserInterface::CommandLineUserInterface(bool saveReport,
|
||||
const char* reportPath, thread_id reportTargetThread)
|
||||
CommandLineUserInterface::CommandLineUserInterface()
|
||||
:
|
||||
fCommands(20, true),
|
||||
fReportPath(reportPath),
|
||||
fSaveReport(saveReport),
|
||||
fReportTargetThread(reportTargetThread),
|
||||
fShowSemaphore(-1),
|
||||
fShown(false),
|
||||
fTerminating(false)
|
||||
@@ -128,8 +124,6 @@ CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener)
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
fContext.SetInteractive(!fSaveReport);
|
||||
|
||||
error = _RegisterCommands();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
@@ -138,8 +132,6 @@ CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener)
|
||||
if (fShowSemaphore < 0)
|
||||
return fShowSemaphore;
|
||||
|
||||
team->AddListener(this);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
@@ -176,9 +168,7 @@ CommandLineUserInterface::Terminate()
|
||||
bool
|
||||
CommandLineUserInterface::IsInteractive() const
|
||||
{
|
||||
// if we were invoked solely for the purpose of saving a crash report,
|
||||
// then we're not taking user input into account.
|
||||
return !fSaveReport;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -237,43 +227,12 @@ CommandLineUserInterface::Run()
|
||||
if (error != B_OK)
|
||||
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();
|
||||
// Release the Show() semaphore to signal Terminate().
|
||||
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
|
||||
CommandLineUserInterface::_InputLoopEntry(void* data)
|
||||
{
|
||||
@@ -473,32 +432,3 @@ CommandLineUserInterface::_CompareCommandEntries(const CommandEntry* command1,
|
||||
{
|
||||
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.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
@@ -17,12 +17,9 @@
|
||||
class CliCommand;
|
||||
|
||||
|
||||
class CommandLineUserInterface : public UserInterface,
|
||||
public ::Team::Listener {
|
||||
class CommandLineUserInterface : public UserInterface {
|
||||
public:
|
||||
CommandLineUserInterface(bool saveReport,
|
||||
const char* reportPath,
|
||||
thread_id reportTargetThread);
|
||||
CommandLineUserInterface();
|
||||
virtual ~CommandLineUserInterface();
|
||||
|
||||
virtual const char* ID() const;
|
||||
@@ -54,10 +51,6 @@ public:
|
||||
// everything has been set up. Enters the
|
||||
// input loop.
|
||||
|
||||
// Team::Listener
|
||||
virtual void ThreadStateChanged(
|
||||
const Team::ThreadEvent& event);
|
||||
|
||||
private:
|
||||
struct CommandEntry;
|
||||
typedef BObjectList<CommandEntry> CommandList;
|
||||
@@ -82,15 +75,9 @@ private:
|
||||
const CommandEntry* command1,
|
||||
const CommandEntry* command2);
|
||||
|
||||
bool _ReportTargetThreadStopNeeded() const;
|
||||
void _SubmitSaveReport();
|
||||
|
||||
private:
|
||||
CliContext fContext;
|
||||
CommandList fCommands;
|
||||
const char* fReportPath;
|
||||
bool fSaveReport;
|
||||
thread_id fReportTargetThread;
|
||||
sem_id fShowSemaphore;
|
||||
bool fShown;
|
||||
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