Debugger: Rework CLI setup to no longer create a BApplication
The main thread does now serve the CLI input loop.
This commit is contained in:
parent
5ba5e31f8a
commit
0a592099a9
@ -88,6 +88,13 @@ struct Options {
|
||||
};
|
||||
|
||||
|
||||
struct DebuggedProgramInfo {
|
||||
team_id team;
|
||||
thread_id thread;
|
||||
bool stopInMain;
|
||||
};
|
||||
|
||||
|
||||
static bool
|
||||
parse_arguments(int argc, const char* const* argv, bool noOutput,
|
||||
Options& options)
|
||||
@ -174,25 +181,104 @@ parse_arguments(int argc, const char* const* argv, bool noOutput,
|
||||
return true;
|
||||
}
|
||||
|
||||
static status_t
|
||||
global_init()
|
||||
{
|
||||
status_t error = TypeHandlerRoster::CreateDefault();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
error = ValueHandlerRoster::CreateDefault();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds or runs the program to debug, depending on the command line options.
|
||||
* @param options The parsed command line options.
|
||||
* @param _info The info for the program to fill in. Will only be filled in
|
||||
* if successful.
|
||||
* @return \c true, if the program has been found or ran.
|
||||
*/
|
||||
static bool
|
||||
get_debugged_program(const Options& options, DebuggedProgramInfo& _info)
|
||||
{
|
||||
team_id team = options.team;
|
||||
thread_id thread = options.thread;
|
||||
bool stopInMain = false;
|
||||
|
||||
// If command line arguments were given, start the program.
|
||||
if (options.commandLineArgc > 0) {
|
||||
printf("loading program: \"%s\" ...\n", options.commandLineArgv[0]);
|
||||
// TODO: What about the CWD?
|
||||
thread = load_program(options.commandLineArgv,
|
||||
options.commandLineArgc, false);
|
||||
if (thread < 0) {
|
||||
// TODO: Notify the user!
|
||||
fprintf(stderr, "Error: Failed to load program \"%s\": %s\n",
|
||||
options.commandLineArgv[0], strerror(thread));
|
||||
return false;
|
||||
}
|
||||
|
||||
team = thread;
|
||||
// main thread ID == team ID
|
||||
stopInMain = true;
|
||||
}
|
||||
|
||||
// no parameters given, prompt the user to attach to a team
|
||||
if (team < 0 && thread < 0)
|
||||
return false;
|
||||
|
||||
// no team, but a thread -- get team
|
||||
if (team < 0) {
|
||||
printf("no team yet, getting thread info...\n");
|
||||
thread_info threadInfo;
|
||||
status_t error = get_thread_info(thread, &threadInfo);
|
||||
if (error != B_OK) {
|
||||
// TODO: Notify the user!
|
||||
fprintf(stderr, "Error: Failed to get info for thread \"%ld\": "
|
||||
"%s\n", thread, strerror(error));
|
||||
return false;
|
||||
}
|
||||
|
||||
team = threadInfo.team;
|
||||
}
|
||||
printf("team: %ld, thread: %ld\n", team, thread);
|
||||
|
||||
_info.team = team;
|
||||
_info.thread = thread;
|
||||
_info.stopInMain = stopInMain;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a TeamDebugger for the given team. If userInterface is given,
|
||||
* that user interface is used (the caller retains its reference), otherwise
|
||||
* a graphical user interface is created.
|
||||
*/
|
||||
static TeamDebugger*
|
||||
start_team_debugger(team_id teamID, SettingsManager* settingsManager,
|
||||
TeamDebugger::Listener* listener, thread_id threadID = -1,
|
||||
bool stopInMain = false, bool useCLI = false)
|
||||
bool stopInMain = false, UserInterface* userInterface = NULL)
|
||||
{
|
||||
if (teamID < 0)
|
||||
return NULL;
|
||||
|
||||
UserInterface* userInterface = useCLI
|
||||
? (UserInterface*)new(std::nothrow) CommandLineUserInterface
|
||||
: (UserInterface*)new(std::nothrow) GraphicalUserInterface;
|
||||
|
||||
BReference<UserInterface> userInterfaceReference;
|
||||
if (userInterface == NULL) {
|
||||
// TODO: Notify the user!
|
||||
fprintf(stderr, "Error: Out of memory!\n");
|
||||
return NULL;
|
||||
userInterface = new(std::nothrow) GraphicalUserInterface;
|
||||
if (userInterface == NULL) {
|
||||
// TODO: Notify the user!
|
||||
fprintf(stderr, "Error: Out of memory!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
userInterfaceReference.SetTo(userInterface, true);
|
||||
}
|
||||
BReference<UserInterface> userInterfaceReference(userInterface, true);
|
||||
|
||||
status_t error = B_NO_MEMORY;
|
||||
|
||||
@ -213,6 +299,7 @@ start_team_debugger(team_id teamID, SettingsManager* settingsManager,
|
||||
return debugger;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Debugger application class
|
||||
|
||||
|
||||
@ -247,6 +334,26 @@ private:
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - CliDebugger
|
||||
|
||||
|
||||
class CliDebugger : private TeamDebugger::Listener {
|
||||
public:
|
||||
CliDebugger();
|
||||
~CliDebugger();
|
||||
|
||||
bool Run(const Options& options);
|
||||
|
||||
private:
|
||||
// TeamDebugger::Listener
|
||||
virtual void TeamDebuggerStarted(TeamDebugger* debugger);
|
||||
virtual void TeamDebuggerQuit(TeamDebugger* debugger);
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - Debugger application class
|
||||
|
||||
|
||||
Debugger::Debugger()
|
||||
:
|
||||
BApplication(kDebuggerSignature),
|
||||
@ -266,11 +373,7 @@ Debugger::~Debugger()
|
||||
status_t
|
||||
Debugger::Init()
|
||||
{
|
||||
status_t error = TypeHandlerRoster::CreateDefault();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
error = ValueHandlerRoster::CreateDefault();
|
||||
status_t error = global_init();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
@ -348,63 +451,22 @@ Debugger::ArgvReceived(int32 argc, char** argv)
|
||||
return;
|
||||
}
|
||||
|
||||
team_id team = options.team;
|
||||
thread_id thread = options.thread;
|
||||
bool stopInMain = false;
|
||||
|
||||
// If command line arguments were given, start the program.
|
||||
if (options.commandLineArgc > 0) {
|
||||
printf("loading program: \"%s\" ...\n", options.commandLineArgv[0]);
|
||||
// TODO: What about the CWD?
|
||||
thread = load_program(options.commandLineArgv,
|
||||
options.commandLineArgc, false);
|
||||
if (thread < 0) {
|
||||
// TODO: Notify the user!
|
||||
fprintf(stderr, "Error: Failed to load program \"%s\": %s\n",
|
||||
options.commandLineArgv[0], strerror(thread));
|
||||
return;
|
||||
}
|
||||
|
||||
team = thread;
|
||||
// main thread ID == team ID
|
||||
stopInMain = true;
|
||||
}
|
||||
|
||||
// no parameters given, prompt the user to attach to a team
|
||||
if (team < 0 && thread < 0)
|
||||
DebuggedProgramInfo programInfo;
|
||||
if (!get_debugged_program(options, programInfo))
|
||||
return;
|
||||
|
||||
// If we've got
|
||||
if (team < 0) {
|
||||
printf("no team yet, getting thread info...\n");
|
||||
thread_info threadInfo;
|
||||
status_t error = get_thread_info(thread, &threadInfo);
|
||||
if (error != B_OK) {
|
||||
// TODO: Notify the user!
|
||||
fprintf(stderr, "Error: Failed to get info for thread \"%ld\": "
|
||||
"%s\n", thread, strerror(error));
|
||||
return;
|
||||
}
|
||||
|
||||
team = threadInfo.team;
|
||||
}
|
||||
printf("team: %ld, thread: %ld\n", team, thread);
|
||||
|
||||
TeamDebugger* debugger = _FindTeamDebugger(team);
|
||||
TeamDebugger* debugger = _FindTeamDebugger(programInfo.team);
|
||||
if (debugger != NULL) {
|
||||
printf("There's already a debugger for team: %ld\n", team);
|
||||
printf("There's already a debugger for team: %ld\n", programInfo.team);
|
||||
debugger->Activate();
|
||||
return;
|
||||
}
|
||||
|
||||
start_team_debugger(team, &fSettingsManager, this, thread, stopInMain,
|
||||
options.useCLI);
|
||||
start_team_debugger(programInfo.team, &fSettingsManager, this,
|
||||
programInfo.thread, programInfo.stopInMain);
|
||||
}
|
||||
|
||||
|
||||
// TeamDebugger::Listener
|
||||
|
||||
|
||||
void
|
||||
Debugger::TeamDebuggerStarted(TeamDebugger* debugger)
|
||||
{
|
||||
@ -480,6 +542,76 @@ Debugger::_FindTeamDebugger(team_id teamID) const
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - CliDebugger
|
||||
|
||||
|
||||
CliDebugger::CliDebugger()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
CliDebugger::~CliDebugger()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
CliDebugger::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 command line UI
|
||||
CommandLineUserInterface* userInterface
|
||||
= new(std::nothrow) CommandLineUserInterface;
|
||||
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;
|
||||
|
||||
if (start_team_debugger(programInfo.team, &settingsManager, this,
|
||||
programInfo.thread, programInfo.stopInMain, userInterface)
|
||||
== NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
userInterface->Run();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CliDebugger::TeamDebuggerStarted(TeamDebugger* debugger)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CliDebugger::TeamDebuggerQuit(TeamDebugger* debugger)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
@ -495,6 +627,11 @@ main(int argc, const char* const* argv)
|
||||
Options options;
|
||||
parse_arguments(argc, argv, false, options);
|
||||
|
||||
if (options.useCLI) {
|
||||
CliDebugger debugger;
|
||||
return debugger.Run(options) ? 0 : 1;
|
||||
}
|
||||
|
||||
Debugger app;
|
||||
status_t error = app.Init();
|
||||
if (error != B_OK) {
|
||||
|
@ -96,10 +96,11 @@ private:
|
||||
|
||||
CommandLineUserInterface::CommandLineUserInterface()
|
||||
:
|
||||
fThread(-1),
|
||||
fTeam(NULL),
|
||||
fListener(NULL),
|
||||
fCommands(20, true),
|
||||
fShowSemaphore(-1),
|
||||
fShown(false),
|
||||
fTerminating(false)
|
||||
{
|
||||
}
|
||||
@ -107,6 +108,8 @@ CommandLineUserInterface::CommandLineUserInterface()
|
||||
|
||||
CommandLineUserInterface::~CommandLineUserInterface()
|
||||
{
|
||||
if (fShowSemaphore >= 0)
|
||||
delete_sem(fShowSemaphore);
|
||||
}
|
||||
|
||||
|
||||
@ -127,9 +130,9 @@ CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener)
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
fThread = spawn_thread(&_InputLoopEntry, "CLI", B_NORMAL_PRIORITY, this);
|
||||
if (fThread < 0)
|
||||
return fThread;
|
||||
fShowSemaphore = create_sem(0, "show CLI");
|
||||
if (fShowSemaphore < 0)
|
||||
return fShowSemaphore;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
@ -138,7 +141,8 @@ CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener)
|
||||
void
|
||||
CommandLineUserInterface::Show()
|
||||
{
|
||||
resume_thread(fThread);
|
||||
fShown = true;
|
||||
release_sem(fShowSemaphore);
|
||||
}
|
||||
|
||||
|
||||
@ -146,8 +150,18 @@ void
|
||||
CommandLineUserInterface::Terminate()
|
||||
{
|
||||
fTerminating = true;
|
||||
// TODO: Signal the thread so it wakes up!
|
||||
wait_for_thread(fThread, NULL);
|
||||
|
||||
if (fShown) {
|
||||
// TODO: Signal the thread so it wakes up!
|
||||
|
||||
// Wait for input loop to finish.
|
||||
while (acquire_sem(fShowSemaphore) == B_INTERRUPTED) {
|
||||
}
|
||||
} else {
|
||||
// The main thread will still be blocked in Run(). Unblock it.
|
||||
delete_sem(fShowSemaphore);
|
||||
fShowSemaphore = -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -181,6 +195,25 @@ CommandLineUserInterface::SynchronouslyAskUser(const char* title,
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
CommandLineUserInterface::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;
|
||||
|
||||
_InputLoop();
|
||||
|
||||
// Release the Show() semaphore to signal Terminate().
|
||||
release_sem(fShowSemaphore);
|
||||
}
|
||||
|
||||
|
||||
/*static*/ status_t
|
||||
CommandLineUserInterface::_InputLoopEntry(void* data)
|
||||
{
|
||||
|
@ -40,6 +40,11 @@ public:
|
||||
const char* message, const char* choice1,
|
||||
const char* choice2, const char* choice3);
|
||||
|
||||
void Run();
|
||||
// Called by the main thread, when
|
||||
// everything has been set up. Enters the
|
||||
// input loop.
|
||||
|
||||
private:
|
||||
struct CommandEntry;
|
||||
typedef BObjectList<CommandEntry> CommandList;
|
||||
@ -63,10 +68,11 @@ private:
|
||||
void _PrintHelp();
|
||||
|
||||
private:
|
||||
thread_id fThread;
|
||||
Team* fTeam;
|
||||
UserInterfaceListener* fListener;
|
||||
CommandList fCommands;
|
||||
sem_id fShowSemaphore;
|
||||
bool fShown;
|
||||
bool fTerminating;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user