Files
haiku-beta6/src/apps/debugger/Debugger.cpp
T

298 lines
6.1 KiB
C++
Raw Normal View History

/*
* Copyright 2009, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <new>
#include <Application.h>
#include <Message.h>
#include "debug_utils.h"
#include "TeamDebugger.h"
extern const char* __progname;
const char* kProgramName = __progname;
static const char* const kDebuggerSignature
= "application/x-vnd.Haiku-Debugger";
static const char* kUsage =
"Usage: %s [ <options> ]\n"
" %s [ <options> ] <command line>\n"
" %s [ <options> ] --team <team>\n"
" %s [ <options> ] --thread <thread>\n"
"\n"
"The first form starts the debugger displaying a requester to choose a\n"
"running team to debug respectively to specify the program to run and\n"
"debug.\n"
"\n"
"The second form runs the given command line and attaches the debugger to\n"
"the new team. Unless specified otherwise the program will be stopped at\n"
"the beginning of its main() function.\n"
"\n"
"The third and fourth forms attach the debugger to a running team. The\n"
"fourth form additionally stops the specified thread.\n"
"\n"
"Options:\n"
" -h, --help - Print this usage info and exit.\n"
;
static void
print_usage_and_exit(bool error)
{
fprintf(error ? stderr : stdout, kUsage, kProgramName, kProgramName,
kProgramName, kProgramName);
exit(error ? 1 : 0);
}
struct Options {
int commandLineArgc;
const char* const* commandLineArgv;
team_id team;
thread_id thread;
Options()
:
commandLineArgc(0),
commandLineArgv(NULL),
team(-1),
thread(-1)
{
}
};
static bool
parse_arguments(int argc, const char* const* argv, bool noOutput,
Options& options)
{
optind = 1;
while (true) {
static struct option sLongOptions[] = {
{ "help", no_argument, 0, 'h' },
{ "team", required_argument, 0, 't' },
{ "thread", required_argument, 0, 'T' },
{ 0, 0, 0, 0 }
};
opterr = 0; // don't print errors
int c = getopt_long(argc, (char**)argv, "+h", sLongOptions, NULL);
if (c == -1)
break;
switch (c) {
case 'h':
if (noOutput)
return false;
print_usage_and_exit(false);
break;
case 't':
{
options.team = strtol(optarg, NULL, 0);
if (options.team <= 0) {
if (noOutput)
return false;
print_usage_and_exit(true);
}
break;
}
case 'T':
{
options.thread = strtol(optarg, NULL, 0);
if (options.thread <= 0) {
if (noOutput)
return false;
print_usage_and_exit(true);
}
break;
}
default:
if (noOutput)
return false;
print_usage_and_exit(true);
break;
}
}
if (optind < argc) {
options.commandLineArgc = argc - optind;
options.commandLineArgv = argv + optind;
}
int exclusiveParams = 0;
if (options.team > 0)
exclusiveParams++;
if (options.thread > 0)
exclusiveParams++;
if (options.commandLineArgc > 0)
exclusiveParams++;
if (exclusiveParams == 0) {
// TODO: Support!
if (noOutput)
return false;
fprintf(stderr, "Sorry, running without team/thread to debug not "
"supported yet.\n");
exit(1);
} else if (exclusiveParams != 1) {
if (noOutput)
return false;
print_usage_and_exit(true);
}
return true;
}
class Debugger : public BApplication {
public:
Debugger()
:
BApplication(kDebuggerSignature)
{
}
~Debugger()
{
}
virtual void MessageReceived(BMessage* message)
{
switch (message->what) {
default:
BApplication::MessageReceived(message);
break;
}
}
virtual void ReadyToRun()
{
}
virtual void ArgvReceived(int32 argc, char** argv)
{
Options options;
if (!parse_arguments(argc, argv, true, options))
{
printf("Debugger::ArgvReceived(): parsing args failed!\n");
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;
}
// 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 = _TeamDebuggerForTeam(team);
if (debugger != NULL) {
// TODO: Activate the respective window!
printf("There's already a debugger for team: %ld\n", team);
return;
}
debugger = new(std::nothrow) TeamDebugger;
if (debugger == NULL) {
// TODO: Notify the user!
fprintf(stderr, "Error: Out of memory!\n");
}
if (debugger->Init(team, thread, stopInMain) == B_OK)
{
printf("debugger for team %ld created and initialized successfully!\n", team);
fTeamDebuggers.Add(debugger);
}
else
delete debugger;
}
virtual bool QuitRequested()
{
// TODO:...
return true;
}
private:
typedef DoublyLinkedList<TeamDebugger> TeamDebuggerList;
private:
TeamDebugger* _TeamDebuggerForTeam(team_id teamID) const
{
for (TeamDebuggerList::ConstIterator it = fTeamDebuggers.GetIterator();
TeamDebugger* debugger = it.Next();) {
if (debugger->TeamID() == teamID)
return debugger;
}
return NULL;
}
private:
TeamDebuggerList fTeamDebuggers;
};
int
main(int argc, const char* const* argv)
{
// We test-parse the arguments here, so, when we're started from the
// terminal and there's an instance already running, we can print an error
// message to the terminal, if something's wrong with the arguments.
{
Options options;
parse_arguments(argc, argv, false, options);
}
Debugger app;
app.Run();
return 0;
}