From 2d9d01e2e849737a184f83779311a04346a5be98 Mon Sep 17 00:00:00 2001 From: Rene Gollent Date: Fri, 21 Aug 2015 23:45:28 -0400 Subject: [PATCH] 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. --- src/apps/debugger/Debugger.cpp | 108 +++++++- src/apps/debugger/Jamfile | 4 + .../user_interface/cli/CliContext.cpp | 18 +- .../debugger/user_interface/cli/CliContext.h | 6 +- .../cli/CommandLineUserInterface.cpp | 76 +----- .../cli/CommandLineUserInterface.h | 19 +- .../report/ReportUserInterface.cpp | 233 ++++++++++++++++++ .../report/ReportUserInterface.h | 66 +++++ 8 files changed, 416 insertions(+), 114 deletions(-) create mode 100644 src/apps/debugger/user_interface/report/ReportUserInterface.cpp create mode 100644 src/apps/debugger/user_interface/report/ReportUserInterface.h diff --git a/src/apps/debugger/Debugger.cpp b/src/apps/debugger/Debugger.cpp index 5b72949475..603a2124b9 100644 --- a/src/apps/debugger/Debugger.cpp +++ b/src/apps/debugger/Debugger.cpp @@ -1,6 +1,6 @@ /* * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de. - * Copyright 2011-2014, Rene Gollent, rene@gollent.com. + * Copyright 2011-2015, Rene Gollent, rene@gollent.com. * 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 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; diff --git a/src/apps/debugger/Jamfile b/src/apps/debugger/Jamfile index 0985601c1f..773da5f236 100644 --- a/src/apps/debugger/Jamfile +++ b/src/apps/debugger/Jamfile @@ -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 diff --git a/src/apps/debugger/user_interface/cli/CliContext.cpp b/src/apps/debugger/user_interface/cli/CliContext.cpp index 7c97d2d674..9a66b1a309 100644 --- a/src/apps/debugger/user_interface/cli/CliContext.cpp +++ b/src/apps/debugger/user_interface/cli/CliContext.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2012-2014, Rene Gollent, rene@gollent.com. + * Copyright 2012-2015, Rene Gollent, rene@gollent.com. * Copyright 2012, Ingo Weinhold, ingo_weinhold@gmx.de. * 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; - } } } diff --git a/src/apps/debugger/user_interface/cli/CliContext.h b/src/apps/debugger/user_interface/cli/CliContext.h index 8232c783c0..66f35e22d8 100644 --- a/src/apps/debugger/user_interface/cli/CliContext.h +++ b/src/apps/debugger/user_interface/cli/CliContext.h @@ -1,6 +1,6 @@ /* * 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. */ #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; diff --git a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp index 93d1d45b5e..faecbe7601 100644 --- a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp +++ b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp @@ -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. */ @@ -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 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()); -} diff --git a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h index bb59ce3750..e908e2dcd1 100644 --- a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h +++ b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h @@ -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 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; diff --git a/src/apps/debugger/user_interface/report/ReportUserInterface.cpp b/src/apps/debugger/user_interface/report/ReportUserInterface.cpp new file mode 100644 index 0000000000..57fcdf7534 --- /dev/null +++ b/src/apps/debugger/user_interface/report/ReportUserInterface.cpp @@ -0,0 +1,233 @@ +/* + * Copyright 2015, Rene Gollent, rene@gollent.com. + * Distributed under the terms of the MIT License. + */ + + +#include "ReportUserInterface.h" + +#include + +#include +#include +#include + +#include + +#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); +} diff --git a/src/apps/debugger/user_interface/report/ReportUserInterface.h b/src/apps/debugger/user_interface/report/ReportUserInterface.h new file mode 100644 index 0000000000..8b06d30e3a --- /dev/null +++ b/src/apps/debugger/user_interface/report/ReportUserInterface.h @@ -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 +#include + +#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