hrev44469 adds 5 changesets to branch 'master' old head: 147d65c26dad4ac6db6c2e9936d40382434ce065 new head: 9b008d31ffa35bb2c98bb2accf83e16fb7f2f9ad ---------------------------------------------------------------------------- a7580a8: Debugger: Add CliCommand::PrintUsage() d458618: Debugger CLI: Improve "help" command Given a command name it prints the command's usage. 59dbbd1: Debugger: CliQuitCommand: Fix cancel shortcut 86b1039: Debugger CliContext: More event handling, current thread * Introduce the notion of a current thread. That's the one certain commands will target (by default). * Add more event handling in CliContext. There's now a queue of pending events which are printed in the input loop at convenient times to inform the user about what happened (new/deleted/stopped threads, etc.). 9b008d3: Debugger CLI: Add a bunch of new commands * "thread": prints/sets the current thread. * "continue", "stop": continue/stop the current thread. * "sc"/"bt": Print a stack trace for the current thread. Very basic yet. [ Ingo Weinhold <ingo_weinhold@xxxxxx> ] ---------------------------------------------------------------------------- 16 files changed, 606 insertions(+), 30 deletions(-) src/apps/debugger/Jamfile | 4 + .../debugger/user_interface/cli/CliCommand.cpp | 11 + src/apps/debugger/user_interface/cli/CliCommand.h | 2 + .../debugger/user_interface/cli/CliContext.cpp | 175 +++++++++++++++- src/apps/debugger/user_interface/cli/CliContext.h | 28 ++- .../user_interface/cli/CliContinueCommand.cpp | 45 ++++ .../user_interface/cli/CliContinueCommand.h | 20 ++ .../debugger/user_interface/cli/CliQuitCommand.cpp | 2 +- .../user_interface/cli/CliStackTraceCommand.cpp | 86 ++++++++ .../user_interface/cli/CliStackTraceCommand.h | 20 ++ .../debugger/user_interface/cli/CliStopCommand.cpp | 45 ++++ .../debugger/user_interface/cli/CliStopCommand.h | 20 ++ .../user_interface/cli/CliThreadCommand.cpp | 58 +++++ .../debugger/user_interface/cli/CliThreadCommand.h | 20 ++ .../cli/CommandLineUserInterface.cpp | 97 +++++++-- .../user_interface/cli/CommandLineUserInterface.h | 3 +- ############################################################################ Commit: a7580a8299aacd5d09ce12534f0409144ccfd95a URL: http://cgit.haiku-os.org/haiku/commit/?id=a7580a8 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sat Aug 4 22:55:43 2012 UTC Debugger: Add CliCommand::PrintUsage() ---------------------------------------------------------------------------- diff --git a/src/apps/debugger/user_interface/cli/CliCommand.cpp b/src/apps/debugger/user_interface/cli/CliCommand.cpp index c83c8cf..2df3b38 100644 --- a/src/apps/debugger/user_interface/cli/CliCommand.cpp +++ b/src/apps/debugger/user_interface/cli/CliCommand.cpp @@ -6,6 +6,8 @@ #include "CliCommand.h" +#include <stdio.h> + CliCommand::CliCommand(const char* summary, const char* usage) : @@ -18,3 +20,12 @@ CliCommand::CliCommand(const char* summary, const char* usage) CliCommand::~CliCommand() { } + + +void +CliCommand::PrintUsage(const char* commandName) const +{ + printf("Usage: "); + printf(Usage(), commandName); + printf("\n"); +} diff --git a/src/apps/debugger/user_interface/cli/CliCommand.h b/src/apps/debugger/user_interface/cli/CliCommand.h index 2a65e19..d04d235 100644 --- a/src/apps/debugger/user_interface/cli/CliCommand.h +++ b/src/apps/debugger/user_interface/cli/CliCommand.h @@ -21,6 +21,8 @@ public: const char* Summary() const { return fSummary; } const char* Usage() const { return fUsage; } + void PrintUsage(const char* commandName) const; + virtual void Execute(int argc, const char* const* argv, CliContext& context) = 0; ############################################################################ Commit: d4586180a674df11cbfbea1cf2ce527bf1ed4117 URL: http://cgit.haiku-os.org/haiku/commit/?id=d458618 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sat Aug 4 22:57:57 2012 UTC Debugger CLI: Improve "help" command Given a command name it prints the command's usage. ---------------------------------------------------------------------------- diff --git a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp index c001112..0d1c92b 100644 --- a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp +++ b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp @@ -56,16 +56,23 @@ private: struct CommandLineUserInterface::HelpCommand : CliCommand { HelpCommand(CommandLineUserInterface* userInterface) : - CliCommand("print a list of all commands", - "%s\n" - "Prints a list of all commands."), + CliCommand("print help for a command or a list of all commands", + "%s [ <command> ]\n" + "Prints help for command <command>, if given, or a list of all " + "commands\n" + "otherwise."), fUserInterface(userInterface) { } virtual void Execute(int argc, const char* const* argv, CliContext& context) { - fUserInterface->_PrintHelp(); + if (argc > 2) { + PrintUsage(argv[0]); + return; + } + + fUserInterface->_PrintHelp(argc == 2 ? argv[1] : NULL); } private: @@ -282,33 +289,63 @@ CommandLineUserInterface::_RegisterCommand(const BString& name, void CommandLineUserInterface::_ExecuteCommand(int argc, const char* const* argv) { - const char* commandName = argv[0]; + CommandEntry* commandEntry = _FindCommand(argv[0]); + if (commandEntry != NULL) + commandEntry->Command()->Execute(argc, argv, fContext); +} + + +CommandLineUserInterface::CommandEntry* +CommandLineUserInterface::_FindCommand(const char* commandName) +{ size_t commandNameLength = strlen(commandName); - CommandEntry* firstEntry = NULL; + // try to find an exact match first + CommandEntry* commandEntry = NULL; for (int32 i = 0; CommandEntry* entry = fCommands.ItemAt(i); i++) { - if (entry->Name().Compare(commandName, commandNameLength) == 0) { - if (firstEntry != NULL) { - printf("Ambiguous command \"%s\".\n", commandName); - return; - } + if (entry->Name() == commandName) { + commandEntry = entry; + break; + } + } - firstEntry = entry; + // If nothing found yet, try partial matches, but only, if they are + // unambiguous. + if (commandEntry == NULL) { + for (int32 i = 0; CommandEntry* entry = fCommands.ItemAt(i); i++) { + if (entry->Name().Compare(commandName, commandNameLength) == 0) { + if (commandEntry != NULL) { + printf("Error: Ambiguous command \"%s\".\n", commandName); + return NULL; + } + + commandEntry = entry; + } } } - if (firstEntry == NULL) { - printf("Unknown command \"%s\".\n", commandName); - return; + if (commandEntry == NULL) { + printf("Error: Unknown command \"%s\".\n", commandName); + return NULL; } - firstEntry->Command()->Execute(argc, argv, fContext); + return commandEntry; } void -CommandLineUserInterface::_PrintHelp() +CommandLineUserInterface::_PrintHelp(const char* commandName) { + // If a command name is given, print the usage for that one. + if (commandName != NULL) { + CommandEntry* commandEntry = _FindCommand(commandName); + if (commandEntry != NULL) + commandEntry->Command()->PrintUsage(commandEntry->Name().String()); + return; + } + + // No command name given -- print a list of all commands. + // determine longest command name int32 longestCommandName = 0; for (int32 i = 0; CommandEntry* entry = fCommands.ItemAt(i); i++) { diff --git a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h index 736429b..6bede60 100644 --- a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h +++ b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.h @@ -64,7 +64,8 @@ private: CliCommand* command); void _ExecuteCommand(int argc, const char* const* argv); - void _PrintHelp(); + CommandEntry* _FindCommand(const char* commandName); + void _PrintHelp(const char* commandName); private: CliContext fContext; ############################################################################ Commit: 59dbbd147e91766f53359835c1a7448fac1e36c3 URL: http://cgit.haiku-os.org/haiku/commit/?id=59dbbd1 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sat Aug 4 22:58:30 2012 UTC Debugger: CliQuitCommand: Fix cancel shortcut ---------------------------------------------------------------------------- diff --git a/src/apps/debugger/user_interface/cli/CliQuitCommand.cpp b/src/apps/debugger/user_interface/cli/CliQuitCommand.cpp index 49d8d82..def9368 100644 --- a/src/apps/debugger/user_interface/cli/CliQuitCommand.cpp +++ b/src/apps/debugger/user_interface/cli/CliQuitCommand.cpp @@ -43,7 +43,7 @@ CliQuitCommand::Execute(int argc, const char* const* argv, CliContext& context) break; } - if (trimmedLine == "d") + if (trimmedLine == "c") break; } } ############################################################################ Commit: 86b1039b4240746ffe42bd8563b5333751bd2456 URL: http://cgit.haiku-os.org/haiku/commit/?id=86b1039 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sat Aug 4 23:04:44 2012 UTC Debugger CliContext: More event handling, current thread * Introduce the notion of a current thread. That's the one certain commands will target (by default). * Add more event handling in CliContext. There's now a queue of pending events which are printed in the input loop at convenient times to inform the user about what happened (new/deleted/stopped threads, etc.). ---------------------------------------------------------------------------- diff --git a/src/apps/debugger/user_interface/cli/CliContext.cpp b/src/apps/debugger/user_interface/cli/CliContext.cpp index 68b8e77..6623300 100644 --- a/src/apps/debugger/user_interface/cli/CliContext.cpp +++ b/src/apps/debugger/user_interface/cli/CliContext.cpp @@ -6,6 +6,7 @@ #include "CliContext.h" +#include <AutoDeleter.h> #include <AutoLocker.h> #include "UserInterface.h" @@ -19,6 +20,36 @@ static CliContext* sCurrentContext; +// #pragma mark - Event + + +struct CliContext::Event : DoublyLinkedListLinkImpl<CliContext::Event> { + Event(int type, Thread* thread = NULL) + : + fType(type), + fThreadReference(thread) + { + } + + int Type() const + { + return fType; + } + + Thread* GetThread() const + { + return fThreadReference.Get(); + } + +private: + int fType; + BReference<Thread> fThreadReference; +}; + + +// #pragma mark - CliContext + + CliContext::CliContext() : fLock("CliContext"), @@ -31,7 +62,8 @@ CliContext::CliContext() fInputLoopWaitingForEvents(0), fEventsOccurred(0), fInputLoopWaiting(false), - fTerminating(false) + fTerminating(false), + fCurrentThread(NULL) { sCurrentContext = this; } @@ -87,6 +119,9 @@ CliContext::Cleanup() { Terminating(); + while (Event* event = fPendingEvents.RemoveHead()) + delete event; + if (fEditLine != NULL) { el_end(fEditLine); fEditLine = NULL; @@ -116,6 +151,41 @@ CliContext::Terminating() } +thread_id +CliContext::CurrentThreadID() const +{ + return fCurrentThread != NULL ? fCurrentThread->ID() : -1; +} + + +void +CliContext::SetCurrentThread(Thread* thread) +{ + AutoLocker<BLocker> locker(fLock); + + if (fCurrentThread != NULL) + fCurrentThread->ReleaseReference(); + + fCurrentThread = thread; + + if (fCurrentThread != NULL) + fCurrentThread->AcquireReference(); +} + + +void +CliContext::PrintCurrentThread() +{ + AutoLocker<Team> teamLocker(fTeam); + + if (fCurrentThread != NULL) { + printf("current thread: %" B_PRId32 " \"%s\"\n", fCurrentThread->ID(), + fCurrentThread->Name()); + } else + printf("no current thread\n"); +} + + const char* CliContext::PromptUser(const char* prompt) { @@ -126,6 +196,8 @@ CliContext::PromptUser(const char* prompt) fPrompt = NULL; + ProcessPendingEvents(); + return line; } @@ -155,39 +227,124 @@ CliContext::QuitSession(bool killTeam) void CliContext::WaitForThreadOrUser() { + ProcessPendingEvents(); + // TODO: Deal with SIGINT as well! for (;;) { _PrepareToWaitForEvents( - EVENT_USER_INTERRUPT | EVENT_THREAD_STATE_CHANGED); + EVENT_USER_INTERRUPT | EVENT_THREAD_STOPPED); // check whether there are any threads stopped already - thread_id stoppedThread = -1; + Thread* stoppedThread = NULL; + BReference<Thread> stoppedThreadReference; + AutoLocker<Team> teamLocker(fTeam); for (ThreadList::ConstIterator it = fTeam->Threads().GetIterator(); Thread* thread = it.Next();) { if (thread->State() == THREAD_STATE_STOPPED) { - stoppedThread = thread->ID(); + stoppedThread = thread; + stoppedThreadReference.SetTo(thread); break; } } teamLocker.Unlock(); - if (stoppedThread >= 0) - _SignalInputLoop(EVENT_THREAD_STATE_CHANGED); + if (stoppedThread != NULL) { + if (fCurrentThread == NULL) + fCurrentThread = stoppedThread; + + _SignalInputLoop(EVENT_THREAD_STOPPED); + } uint32 events = _WaitForEvents(); - if ((events & EVENT_QUIT) != 0 || stoppedThread >= 0) + if ((events & EVENT_QUIT) != 0 || stoppedThread != NULL) { + ProcessPendingEvents(); return; + } } } void -CliContext::ThreadStateChanged(const Team::ThreadEvent& event) +CliContext::ProcessPendingEvents() { - _SignalInputLoop(EVENT_THREAD_STATE_CHANGED); + AutoLocker<Team> teamLocker(fTeam); + + for (;;) { + // get the next event + AutoLocker<BLocker> locker(fLock); + Event* event = fPendingEvents.RemoveHead(); + locker.Unlock(); + if (event == NULL) + break; + ObjectDeleter<Event> eventDeleter(event); + + // process the event + Thread* thread = event->GetThread(); + + switch (event->Type()) { + case EVENT_QUIT: + case EVENT_USER_INTERRUPT: + break; + case EVENT_THREAD_ADDED: + printf("[new thread: %" B_PRId32 " \"%s\"]\n", thread->ID(), + thread->Name()); + break; + case EVENT_THREAD_REMOVED: + printf("[thread terminated: %" B_PRId32 " \"%s\"]\n", + thread->ID(), thread->Name()); + break; + case EVENT_THREAD_STOPPED: + printf("[thread stopped: %" B_PRId32 " \"%s\"]\n", + thread->ID(), thread->Name()); + break; + } + } +} + + +void +CliContext::ThreadAdded(const Team::ThreadEvent& threadEvent) +{ + _QueueEvent( + new(std::nothrow) Event(EVENT_THREAD_ADDED, threadEvent.GetThread())); + _SignalInputLoop(EVENT_THREAD_ADDED); +} + + +void +CliContext::ThreadRemoved(const Team::ThreadEvent& threadEvent) +{ + _QueueEvent( + new(std::nothrow) Event(EVENT_THREAD_REMOVED, threadEvent.GetThread())); + _SignalInputLoop(EVENT_THREAD_REMOVED); +} + + +void +CliContext::ThreadStateChanged(const Team::ThreadEvent& threadEvent) +{ + if (threadEvent.GetThread()->State() != THREAD_STATE_STOPPED) + return; + + _QueueEvent( + new(std::nothrow) Event(EVENT_THREAD_STOPPED, threadEvent.GetThread())); + _SignalInputLoop(EVENT_THREAD_STOPPED); +} + + +void +CliContext::_QueueEvent(Event* event) +{ + if (event == NULL) { + // no memory -- can't do anything about it + return; + } + + AutoLocker<BLocker> locker(fLock); + fPendingEvents.Add(event); } diff --git a/src/apps/debugger/user_interface/cli/CliContext.h b/src/apps/debugger/user_interface/cli/CliContext.h index 0d5771e..3a7c98d 100644 --- a/src/apps/debugger/user_interface/cli/CliContext.h +++ b/src/apps/debugger/user_interface/cli/CliContext.h @@ -24,7 +24,9 @@ public: enum { EVENT_QUIT = 0x01, EVENT_USER_INTERRUPT = 0x02, - EVENT_THREAD_STATE_CHANGED = 0x04, + EVENT_THREAD_ADDED = 0x04, + EVENT_THREAD_REMOVED = 0x08, + EVENT_THREAD_STOPPED = 0x10, }; public: @@ -37,9 +39,18 @@ public: void Terminating(); + bool IsTerminating() const { return fTerminating; } + // service methods for the input loop thread follow Team* GetTeam() const { return fTeam; } + UserInterfaceListener* GetUserInterfaceListener() const + { return fListener; } + + Thread* CurrentThread() const { return fCurrentThread; } + thread_id CurrentThreadID() const; + void SetCurrentThread(Thread* thread); + void PrintCurrentThread(); const char* PromptUser(const char* prompt); void AddLineToInputHistory(const char* line); @@ -47,13 +58,24 @@ public: void QuitSession(bool killTeam); void WaitForThreadOrUser(); + void ProcessPendingEvents(); + +private: + struct Event; + + typedef DoublyLinkedList<Event> EventList; private: // Team::Listener + virtual void ThreadAdded(const Team::ThreadEvent& event); + virtual void ThreadRemoved(const Team::ThreadEvent& event); + virtual void ThreadStateChanged( const Team::ThreadEvent& event); private: + void _QueueEvent(Event* event); + void _PrepareToWaitForEvents(uint32 eventMask); uint32 _WaitForEvents(); void _SignalInputLoop(uint32 events); @@ -72,6 +94,10 @@ private: uint32 fEventsOccurred; bool fInputLoopWaiting; volatile bool fTerminating; + + Thread* fCurrentThread; + + EventList fPendingEvents; }; diff --git a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp index 0d1c92b..dcf2c6c 100644 --- a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp +++ b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp @@ -214,9 +214,19 @@ CommandLineUserInterface::_InputLoopEntry(void* data) status_t CommandLineUserInterface::_InputLoop() { + thread_id currentThread = -1; + while (!fTerminating) { // Wait for a thread or Ctrl-C. fContext.WaitForThreadOrUser(); + if (fContext.IsTerminating()) + break; + + // Print the active thread, if it changed. + if (fContext.CurrentThreadID() != currentThread) { + fContext.PrintCurrentThread(); + currentThread = fContext.CurrentThreadID(); + } // read a command line const char* line = fContext.PromptUser(kDebuggerPrompt); ############################################################################ Revision: hrev44469 Commit: 9b008d31ffa35bb2c98bb2accf83e16fb7f2f9ad URL: http://cgit.haiku-os.org/haiku/commit/?id=9b008d3 Author: Ingo Weinhold <ingo_weinhold@xxxxxx> Date: Sat Aug 4 23:06:37 2012 UTC Debugger CLI: Add a bunch of new commands * "thread": prints/sets the current thread. * "continue", "stop": continue/stop the current thread. * "sc"/"bt": Print a stack trace for the current thread. Very basic yet. ---------------------------------------------------------------------------- diff --git a/src/apps/debugger/Jamfile b/src/apps/debugger/Jamfile index 335827b..fca0a83 100644 --- a/src/apps/debugger/Jamfile +++ b/src/apps/debugger/Jamfile @@ -174,6 +174,10 @@ Application Debugger : # user_interface/cli CliCommand.cpp CliContext.cpp + CliContinueCommand.cpp + CliStackTraceCommand.cpp + CliStopCommand.cpp + CliThreadCommand.cpp CliThreadsCommand.cpp CliQuitCommand.cpp CommandLineUserInterface.cpp diff --git a/src/apps/debugger/user_interface/cli/CliContinueCommand.cpp b/src/apps/debugger/user_interface/cli/CliContinueCommand.cpp new file mode 100644 index 0000000..887b770 --- /dev/null +++ b/src/apps/debugger/user_interface/cli/CliContinueCommand.cpp @@ -0,0 +1,45 @@ +/* + * Copyright 2012, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include "CliContinueCommand.h" + +#include <stdio.h> + +#include <AutoLocker.h> + +#include "CliContext.h" +#include "MessageCodes.h" +#include "Team.h" +#include "UserInterface.h" + +CliContinueCommand::CliContinueCommand() + : + CliCommand("continue the current thread", + "%s\n" + "Continues the current thread.") +{ +} + + +void +CliContinueCommand::Execute(int argc, const char* const* argv, + CliContext& context) +{ + AutoLocker<Team> teamLocker(context.GetTeam()); + Thread* thread = context.CurrentThread(); + if (thread == NULL) { + printf("Error: No current thread.\n"); + return; + } + + if (thread->State() != THREAD_STATE_STOPPED) { + printf("Error: The current thread is not stopped.\n"); + return; + } + + context.GetUserInterfaceListener()->ThreadActionRequested(thread->ID(), + MSG_THREAD_RUN); +} diff --git a/src/apps/debugger/user_interface/cli/CliContinueCommand.h b/src/apps/debugger/user_interface/cli/CliContinueCommand.h new file mode 100644 index 0000000..d826d28 --- /dev/null +++ b/src/apps/debugger/user_interface/cli/CliContinueCommand.h @@ -0,0 +1,20 @@ +/* + * Copyright 2012, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef CLI_CONTINUE_COMMAND_H +#define CLI_CONTINUE_COMMAND_H + + +#include "CliCommand.h" + + +class CliContinueCommand : public CliCommand { +public: + CliContinueCommand(); + virtual void Execute(int argc, const char* const* argv, + CliContext& context); +}; + + +#endif // CLI_CONTINUE_COMMAND_H diff --git a/src/apps/debugger/user_interface/cli/CliStackTraceCommand.cpp b/src/apps/debugger/user_interface/cli/CliStackTraceCommand.cpp new file mode 100644 index 0000000..5b5b2e1 --- /dev/null +++ b/src/apps/debugger/user_interface/cli/CliStackTraceCommand.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 2012, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include "CliStackTraceCommand.h" + +#include <stdio.h> + +#include <AutoLocker.h> + +#include "CliContext.h" +#include "FunctionInstance.h" +#include "StackTrace.h" +#include "Team.h" + + +CliStackTraceCommand::CliStackTraceCommand() + : + CliCommand("print a stack trace of the current thread", + "%s\n" + "Prints a stack trace for the current thread.") +{ +} + + +void +CliStackTraceCommand::Execute(int argc, const char* const* argv, + CliContext& context) +{ + // get the current thread + Team* team = context.GetTeam(); + AutoLocker<Team> teamLocker(team); + Thread* thread = context.CurrentThread(); + if (thread == NULL) { + printf("no current thread\n"); + return; + } + + if (thread->State() != THREAD_STATE_STOPPED) { + printf("Current thread is not stopped. Can't get stack trace.\n"); + return; + } + + // get its stack trace + StackTrace* stackTrace = thread->GetStackTrace(); + if (stackTrace == NULL) { + // TODO: Wait for stack trace! + printf("Current thread doesn't have a stack trace. Waiting not " + "implemented yet\n"); + return; + } + BReference<StackTrace> stackTraceReference(stackTrace); + // hold a reference until we're done + + teamLocker.Unlock(); + + // print the stack trace + int32 frameCount = stackTrace->CountFrames(); + for (int32 i = 0; i < frameCount; i++) { + StackFrame* frame = stackTrace->FrameAt(i); + printf("%3" B_PRId32 " %#" B_PRIx64 " %#" B_PRIx64, i, + (uint64)frame->FrameAddress(), (uint64)frame->InstructionPointer()); + + Image* image = frame->GetImage(); + FunctionInstance* function = frame->Function(); + if (image == NULL && function == NULL) { + printf(" ???\n"); + continue; + } + + BString name; + target_addr_t baseAddress; + if (function != NULL) { + name = function->PrettyName(); + baseAddress = function->Address(); + } else { + name = image->Name(); + baseAddress = image->Info().TextBase(); + } + + printf(" %s + %#" B_PRIx64 "\n", name.String(), + uint64(frame->InstructionPointer() - baseAddress)); + } +} diff --git a/src/apps/debugger/user_interface/cli/CliStackTraceCommand.h b/src/apps/debugger/user_interface/cli/CliStackTraceCommand.h new file mode 100644 index 0000000..bf8db06 --- /dev/null +++ b/src/apps/debugger/user_interface/cli/CliStackTraceCommand.h @@ -0,0 +1,20 @@ +/* + * Copyright 2012, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef CLI_STACK_TRACE_COMMAND_H +#define CLI_STACK_TRACE_COMMAND_H + + +#include "CliCommand.h" + + +class CliStackTraceCommand : public CliCommand { +public: + CliStackTraceCommand(); + virtual void Execute(int argc, const char* const* argv, + CliContext& context); +}; + + +#endif // CLI_STACK_TRACE_COMMAND_H diff --git a/src/apps/debugger/user_interface/cli/CliStopCommand.cpp b/src/apps/debugger/user_interface/cli/CliStopCommand.cpp new file mode 100644 index 0000000..40961b4 --- /dev/null +++ b/src/apps/debugger/user_interface/cli/CliStopCommand.cpp @@ -0,0 +1,45 @@ +/* + * Copyright 2012, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include "CliStopCommand.h" + +#include <stdio.h> + +#include <AutoLocker.h> + +#include "CliContext.h" +#include "MessageCodes.h" +#include "Team.h" +#include "UserInterface.h" + +CliStopCommand::CliStopCommand() + : + CliCommand("stop the current thread", + "%s\n" + "Stops the current thread.") +{ +} + + +void +CliStopCommand::Execute(int argc, const char* const* argv, + CliContext& context) +{ + AutoLocker<Team> teamLocker(context.GetTeam()); + Thread* thread = context.CurrentThread(); + if (thread == NULL) { + printf("Error: No current thread.\n"); + return; + } + + if (thread->State() == THREAD_STATE_STOPPED) { + printf("Error: The current thread is already stopped.\n"); + return; + } + + context.GetUserInterfaceListener()->ThreadActionRequested(thread->ID(), + MSG_THREAD_STOP); +} diff --git a/src/apps/debugger/user_interface/cli/CliStopCommand.h b/src/apps/debugger/user_interface/cli/CliStopCommand.h new file mode 100644 index 0000000..f069712 --- /dev/null +++ b/src/apps/debugger/user_interface/cli/CliStopCommand.h @@ -0,0 +1,20 @@ +/* + * Copyright 2012, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef CLI_STOP_COMMAND_H +#define CLI_STOP_COMMAND_H + + +#include "CliCommand.h" + + +class CliStopCommand : public CliCommand { +public: + CliStopCommand(); + virtual void Execute(int argc, const char* const* argv, + CliContext& context); +}; + + +#endif // CLI_STOP_COMMAND_H diff --git a/src/apps/debugger/user_interface/cli/CliThreadCommand.cpp b/src/apps/debugger/user_interface/cli/CliThreadCommand.cpp new file mode 100644 index 0000000..6e8163b --- /dev/null +++ b/src/apps/debugger/user_interface/cli/CliThreadCommand.cpp @@ -0,0 +1,58 @@ +/* + * Copyright 2012, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ + + +#include "CliThreadCommand.h" + +#include <stdio.h> + +#include <AutoLocker.h> + +#include "CliContext.h" +#include "Team.h" + + +CliThreadCommand::CliThreadCommand() + : + CliCommand("set or print the current thread", + "%s [ <thread ID> ]\n" + "Sets the current thread to <thread ID>, if supplied. Otherwise prints " + "the\n" + "current thread.") +{ +} + + +void +CliThreadCommand::Execute(int argc, const char* const* argv, + CliContext& context) +{ + if (argc > 2) { + PrintUsage(argv[0]); + return; + } + + if (argc < 2) { + // no arguments -- print the current thread + context.PrintCurrentThread(); + return; + } + + // parse the argument + char* endPointer; + long threadID = strtol(argv[1], &endPointer, 0); + if (*endPointer != '\0' || threadID < 0) { + printf("Error: Invalid parameter \"%s\"\n", argv[1]); + return; + } + + // get the thread and change the current thread + Team* team = context.GetTeam(); + AutoLocker<Team> teamLocker(team); + if (Thread* thread = team->ThreadByID(threadID)) + context.SetCurrentThread(thread); + else + printf("Error: No thread with ID %ld\n", threadID); +} diff --git a/src/apps/debugger/user_interface/cli/CliThreadCommand.h b/src/apps/debugger/user_interface/cli/CliThreadCommand.h new file mode 100644 index 0000000..d778909 --- /dev/null +++ b/src/apps/debugger/user_interface/cli/CliThreadCommand.h @@ -0,0 +1,20 @@ +/* + * Copyright 2012, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef CLI_THREAD_COMMAND_H +#define CLI_THREAD_COMMAND_H + + +#include "CliCommand.h" + + +class CliThreadCommand : public CliCommand { +public: + CliThreadCommand(); + virtual void Execute(int argc, const char* const* argv, + CliContext& context); +}; + + +#endif // CLI_THREAD_COMMAND_H diff --git a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp index dcf2c6c..1871c45 100644 --- a/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp +++ b/src/apps/debugger/user_interface/cli/CommandLineUserInterface.cpp @@ -16,7 +16,11 @@ #include <Referenceable.h> #include "CliContext.h" +#include "CliContinueCommand.h" #include "CliQuitCommand.h" +#include "CliStackTraceCommand.h" +#include "CliStopCommand.h" +#include "CliThreadCommand.h" #include "CliThreadsCommand.h" @@ -268,8 +272,18 @@ CommandLineUserInterface::_InputLoop() status_t CommandLineUserInterface::_RegisterCommands() { - if (_RegisterCommand("help", new(std::nothrow) HelpCommand(this)) && + BReference<CliCommand> stackTraceCommandReference( + new(std::nothrow) CliStackTraceCommand, true); + BReference<CliCommand> stackTraceCommandReference2( + stackTraceCommandReference.Get()); + + if (_RegisterCommand("bt", stackTraceCommandReference.Detach()) && + _RegisterCommand("continue", new(std::nothrow) CliContinueCommand) && + _RegisterCommand("help", new(std::nothrow) HelpCommand(this)) && _RegisterCommand("quit", new(std::nothrow) CliQuitCommand) && + _RegisterCommand("sc", stackTraceCommandReference2.Detach()) && + _RegisterCommand("stop", new(std::nothrow) CliStopCommand) && + _RegisterCommand("thread", new(std::nothrow) CliThreadCommand) && _RegisterCommand("threads", new(std::nothrow) CliThreadsCommand)) { return B_OK; }