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:
+198
-61
@@ -88,6 +88,13 @@ struct Options {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct DebuggedProgramInfo {
|
||||||
|
team_id team;
|
||||||
|
thread_id thread;
|
||||||
|
bool stopInMain;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_arguments(int argc, const char* const* argv, bool noOutput,
|
parse_arguments(int argc, const char* const* argv, bool noOutput,
|
||||||
Options& options)
|
Options& options)
|
||||||
@@ -174,25 +181,104 @@ parse_arguments(int argc, const char* const* argv, bool noOutput,
|
|||||||
return true;
|
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*
|
static TeamDebugger*
|
||||||
start_team_debugger(team_id teamID, SettingsManager* settingsManager,
|
start_team_debugger(team_id teamID, SettingsManager* settingsManager,
|
||||||
TeamDebugger::Listener* listener, thread_id threadID = -1,
|
TeamDebugger::Listener* listener, thread_id threadID = -1,
|
||||||
bool stopInMain = false, bool useCLI = false)
|
bool stopInMain = false, UserInterface* userInterface = NULL)
|
||||||
{
|
{
|
||||||
if (teamID < 0)
|
if (teamID < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
UserInterface* userInterface = useCLI
|
BReference<UserInterface> userInterfaceReference;
|
||||||
? (UserInterface*)new(std::nothrow) CommandLineUserInterface
|
|
||||||
: (UserInterface*)new(std::nothrow) GraphicalUserInterface;
|
|
||||||
|
|
||||||
if (userInterface == NULL) {
|
if (userInterface == NULL) {
|
||||||
// TODO: Notify the user!
|
userInterface = new(std::nothrow) GraphicalUserInterface;
|
||||||
fprintf(stderr, "Error: Out of memory!\n");
|
if (userInterface == NULL) {
|
||||||
return 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;
|
status_t error = B_NO_MEMORY;
|
||||||
|
|
||||||
@@ -213,6 +299,7 @@ start_team_debugger(team_id teamID, SettingsManager* settingsManager,
|
|||||||
return debugger;
|
return debugger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - Debugger application class
|
// #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()
|
Debugger::Debugger()
|
||||||
:
|
:
|
||||||
BApplication(kDebuggerSignature),
|
BApplication(kDebuggerSignature),
|
||||||
@@ -266,11 +373,7 @@ Debugger::~Debugger()
|
|||||||
status_t
|
status_t
|
||||||
Debugger::Init()
|
Debugger::Init()
|
||||||
{
|
{
|
||||||
status_t error = TypeHandlerRoster::CreateDefault();
|
status_t error = global_init();
|
||||||
if (error != B_OK)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
error = ValueHandlerRoster::CreateDefault();
|
|
||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
@@ -348,63 +451,22 @@ Debugger::ArgvReceived(int32 argc, char** argv)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
team_id team = options.team;
|
DebuggedProgramInfo programInfo;
|
||||||
thread_id thread = options.thread;
|
if (!get_debugged_program(options, programInfo))
|
||||||
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)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If we've got
|
TeamDebugger* debugger = _FindTeamDebugger(programInfo.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
team = threadInfo.team;
|
|
||||||
}
|
|
||||||
printf("team: %ld, thread: %ld\n", team, thread);
|
|
||||||
|
|
||||||
TeamDebugger* debugger = _FindTeamDebugger(team);
|
|
||||||
if (debugger != NULL) {
|
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();
|
debugger->Activate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
start_team_debugger(team, &fSettingsManager, this, thread, stopInMain,
|
start_team_debugger(programInfo.team, &fSettingsManager, this,
|
||||||
options.useCLI);
|
programInfo.thread, programInfo.stopInMain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TeamDebugger::Listener
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Debugger::TeamDebuggerStarted(TeamDebugger* debugger)
|
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 -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
@@ -495,6 +627,11 @@ main(int argc, const char* const* argv)
|
|||||||
Options options;
|
Options options;
|
||||||
parse_arguments(argc, argv, false, options);
|
parse_arguments(argc, argv, false, options);
|
||||||
|
|
||||||
|
if (options.useCLI) {
|
||||||
|
CliDebugger debugger;
|
||||||
|
return debugger.Run(options) ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
Debugger app;
|
Debugger app;
|
||||||
status_t error = app.Init();
|
status_t error = app.Init();
|
||||||
if (error != B_OK) {
|
if (error != B_OK) {
|
||||||
|
|||||||
@@ -96,10 +96,11 @@ private:
|
|||||||
|
|
||||||
CommandLineUserInterface::CommandLineUserInterface()
|
CommandLineUserInterface::CommandLineUserInterface()
|
||||||
:
|
:
|
||||||
fThread(-1),
|
|
||||||
fTeam(NULL),
|
fTeam(NULL),
|
||||||
fListener(NULL),
|
fListener(NULL),
|
||||||
fCommands(20, true),
|
fCommands(20, true),
|
||||||
|
fShowSemaphore(-1),
|
||||||
|
fShown(false),
|
||||||
fTerminating(false)
|
fTerminating(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -107,6 +108,8 @@ CommandLineUserInterface::CommandLineUserInterface()
|
|||||||
|
|
||||||
CommandLineUserInterface::~CommandLineUserInterface()
|
CommandLineUserInterface::~CommandLineUserInterface()
|
||||||
{
|
{
|
||||||
|
if (fShowSemaphore >= 0)
|
||||||
|
delete_sem(fShowSemaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -127,9 +130,9 @@ CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener)
|
|||||||
if (error != B_OK)
|
if (error != B_OK)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
fThread = spawn_thread(&_InputLoopEntry, "CLI", B_NORMAL_PRIORITY, this);
|
fShowSemaphore = create_sem(0, "show CLI");
|
||||||
if (fThread < 0)
|
if (fShowSemaphore < 0)
|
||||||
return fThread;
|
return fShowSemaphore;
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -138,7 +141,8 @@ CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener)
|
|||||||
void
|
void
|
||||||
CommandLineUserInterface::Show()
|
CommandLineUserInterface::Show()
|
||||||
{
|
{
|
||||||
resume_thread(fThread);
|
fShown = true;
|
||||||
|
release_sem(fShowSemaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -146,8 +150,18 @@ void
|
|||||||
CommandLineUserInterface::Terminate()
|
CommandLineUserInterface::Terminate()
|
||||||
{
|
{
|
||||||
fTerminating = true;
|
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
|
/*static*/ status_t
|
||||||
CommandLineUserInterface::_InputLoopEntry(void* data)
|
CommandLineUserInterface::_InputLoopEntry(void* data)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ public:
|
|||||||
const char* message, const char* choice1,
|
const char* message, const char* choice1,
|
||||||
const char* choice2, const char* choice3);
|
const char* choice2, const char* choice3);
|
||||||
|
|
||||||
|
void Run();
|
||||||
|
// Called by the main thread, when
|
||||||
|
// everything has been set up. Enters the
|
||||||
|
// input loop.
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct CommandEntry;
|
struct CommandEntry;
|
||||||
typedef BObjectList<CommandEntry> CommandList;
|
typedef BObjectList<CommandEntry> CommandList;
|
||||||
@@ -63,10 +68,11 @@ private:
|
|||||||
void _PrintHelp();
|
void _PrintHelp();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
thread_id fThread;
|
|
||||||
Team* fTeam;
|
Team* fTeam;
|
||||||
UserInterfaceListener* fListener;
|
UserInterfaceListener* fListener;
|
||||||
CommandList fCommands;
|
CommandList fCommands;
|
||||||
|
sem_id fShowSemaphore;
|
||||||
|
bool fShown;
|
||||||
bool fTerminating;
|
bool fTerminating;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user