Debugger CLI: Move more stuff to and extend CliContext

* Move the libedit interface there and provide nicer to use methods.
* Also start adding utility methods for the input loop. It is going to
  manage all interactions of the input loop with outside events.
* Fix the "quit" command. The user is now prompted what to do with the
  debugged team and the input loop thread avoids reentering the input
  loop.
This commit is contained in:
Ingo Weinhold 2012-07-27 23:41:11 +02:00
parent eba38eb503
commit 8fe9f8b2d0
5 changed files with 204 additions and 58 deletions

View File

@ -6,27 +6,160 @@
#include "CliContext.h" #include "CliContext.h"
#include <AutoLocker.h>
#include "UserInterface.h" #include "UserInterface.h"
// NOTE: This is a simple work-around for EditLine not having any kind of user
// data field. Hence in _GetPrompt() we don't have access to the context object.
// ATM only one CLI is possible in Debugger, so a static variable works well
// enough. Should that ever change, we would need a thread-safe
// EditLine* -> CliContext* map.
static CliContext* sCurrentContext;
CliContext::CliContext() CliContext::CliContext()
: :
fLock("CliContext"),
fTeam(NULL), fTeam(NULL),
fListener(NULL) fListener(NULL),
fEditLine(NULL),
fHistory(NULL),
fPrompt(NULL),
fBlockingSemaphore(-1),
fInputLoopWaiting(false),
fTerminating(false)
{ {
sCurrentContext = this;
}
CliContext::~CliContext()
{
Cleanup();
sCurrentContext = NULL;
} }
void status_t
CliContext::Init(Team* team, UserInterfaceListener* listener) CliContext::Init(Team* team, UserInterfaceListener* listener)
{ {
fTeam = team; fTeam = team;
fListener = listener; fListener = listener;
status_t error = fLock.InitCheck();
if (error != B_OK)
return error;
fBlockingSemaphore = create_sem(0, "CliContext block");
if (fBlockingSemaphore < 0)
return fBlockingSemaphore;
fEditLine = el_init("Debugger", stdin, stdout, stderr);
if (fEditLine == NULL)
return B_ERROR;
fHistory = history_init();
if (fHistory == NULL)
return B_ERROR;
HistEvent historyEvent;
history(fHistory, &historyEvent, H_SETSIZE, 100);
el_set(fEditLine, EL_HIST, &history, fHistory);
el_set(fEditLine, EL_EDITOR, "emacs");
el_set(fEditLine, EL_PROMPT, &_GetPrompt);
return B_OK;
} }
void void
CliContext::QuitSession() CliContext::Cleanup()
{ {
fListener->UserInterfaceQuitRequested(); Terminating();
if (fEditLine != NULL) {
el_end(fEditLine);
fEditLine = NULL;
}
if (fHistory != NULL) {
history_end(fHistory);
fHistory = NULL;
}
}
void
CliContext::Terminating()
{
AutoLocker<BLocker> locker(fLock);
fTerminating = true;
if (fBlockingSemaphore >= 0) {
delete_sem(fBlockingSemaphore);
fBlockingSemaphore = -1;
}
fInputLoopWaiting = false;
// TODO: Signal the input loop, should it be in PromptUser()!
}
const char*
CliContext::PromptUser(const char* prompt)
{
fPrompt = prompt;
int count;
const char* line = el_gets(fEditLine, &count);
fPrompt = NULL;
return line;
}
void
CliContext::AddLineToInputHistory(const char* line)
{
HistEvent historyEvent;
history(fHistory, &historyEvent, H_ENTER, line);
}
void
CliContext::QuitSession(bool killTeam)
{
AutoLocker<BLocker> locker(fLock);
sem_id blockingSemaphore = fBlockingSemaphore;
fInputLoopWaiting = true;
locker.Unlock();
fListener->UserInterfaceQuitRequested(
killTeam
? UserInterfaceListener::QUIT_OPTION_ASK_KILL_TEAM
: UserInterfaceListener::QUIT_OPTION_ASK_RESUME_TEAM);
while (acquire_sem(blockingSemaphore) == B_INTERRUPTED) {
}
}
void
CliContext::WaitForThreadOrUser()
{
// TODO:...
}
/*static*/ const char*
CliContext::_GetPrompt(EditLine* editLine)
{
return sCurrentContext != NULL ? sCurrentContext->fPrompt : NULL;
} }

View File

@ -6,6 +6,13 @@
#define CLI_CONTEXT_H #define CLI_CONTEXT_H
#include <sys/cdefs.h>
// Needed in histedit.h.
#include <histedit.h>
#include <Locker.h>
class Team; class Team;
class UserInterfaceListener; class UserInterfaceListener;
@ -13,18 +20,38 @@ class UserInterfaceListener;
class CliContext { class CliContext {
public: public:
CliContext(); CliContext();
~CliContext();
void Init(Team* team, status_t Init(Team* team,
UserInterfaceListener* listener); UserInterfaceListener* listener);
void Cleanup();
void Terminating();
// service methods for the input loop thread follow
Team* GetTeam() const { return fTeam; } Team* GetTeam() const { return fTeam; }
void QuitSession(); const char* PromptUser(const char* prompt);
void AddLineToInputHistory(const char* line);
void QuitSession(bool killTeam);
void WaitForThreadOrUser();
private: private:
static const char* _GetPrompt(EditLine* editLine);
private:
BLocker fLock;
Team* fTeam; Team* fTeam;
UserInterfaceListener* fListener; UserInterfaceListener* fListener;
EditLine* fEditLine;
History* fHistory;
const char* fPrompt;
sem_id fBlockingSemaphore;
bool fInputLoopWaiting;
volatile bool fTerminating;
}; };

View File

@ -6,6 +6,8 @@
#include "CliQuitCommand.h" #include "CliQuitCommand.h"
#include <String.h>
#include "CliContext.h" #include "CliContext.h"
@ -21,5 +23,27 @@ CliQuitCommand::CliQuitCommand()
void void
CliQuitCommand::Execute(int argc, const char* const* argv, CliContext& context) CliQuitCommand::Execute(int argc, const char* const* argv, CliContext& context)
{ {
context.QuitSession(); // Ask the user what to do with the debugged team.
printf("Kill or resume the debugged team?\n");
for (;;) {
const char* line = context.PromptUser("(k)ill, (r)esume, (c)ancel? ");
if (line == NULL)
return;
BString trimmedLine(line);
trimmedLine.Trim();
if (trimmedLine == "k") {
context.QuitSession(true);
break;
}
if (trimmedLine == "r") {
context.QuitSession(false);
break;
}
if (trimmedLine == "d")
break;
}
} }

View File

@ -20,11 +20,7 @@
#include "CliThreadsCommand.h" #include "CliThreadsCommand.h"
static const char* static const char* kDebuggerPrompt = "debugger> ";
get_prompt(EditLine* editLine)
{
return "debugger> ";
}
// #pragma mark - CommandEntry // #pragma mark - CommandEntry
@ -83,8 +79,6 @@ private:
CommandLineUserInterface::CommandLineUserInterface() CommandLineUserInterface::CommandLineUserInterface()
: :
fCommands(20, true), fCommands(20, true),
fEditLine(NULL),
fHistory(NULL),
fShowSemaphore(-1), fShowSemaphore(-1),
fShown(false), fShown(false),
fTerminating(false) fTerminating(false)
@ -96,12 +90,6 @@ CommandLineUserInterface::~CommandLineUserInterface()
{ {
if (fShowSemaphore >= 0) if (fShowSemaphore >= 0)
delete_sem(fShowSemaphore); delete_sem(fShowSemaphore);
if (fEditLine != NULL)
el_end(fEditLine);
if (fHistory != NULL)
history_end(fHistory);
} }
@ -115,9 +103,11 @@ CommandLineUserInterface::ID() const
status_t status_t
CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener) CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener)
{ {
fContext.Init(team, listener); status_t error = fContext.Init(team, listener);
if (error != B_OK)
return error;
status_t error = _RegisterCommands(); error = _RegisterCommands();
if (error != B_OK) if (error != B_OK)
return error; return error;
@ -125,21 +115,6 @@ CommandLineUserInterface::Init(Team* team, UserInterfaceListener* listener)
if (fShowSemaphore < 0) if (fShowSemaphore < 0)
return fShowSemaphore; return fShowSemaphore;
fEditLine = el_init("Debugger", stdin, stdout, stderr);
if (fEditLine == NULL)
return B_ERROR;
fHistory = history_init();
if (fHistory == NULL)
return B_ERROR;
HistEvent historyEvent;
history(fHistory, &historyEvent, H_SETSIZE, 100);
el_set(fEditLine, EL_HIST, &history, fHistory);
el_set(fEditLine, EL_EDITOR, "emacs");
el_set(fEditLine, EL_PROMPT, &get_prompt);
return B_OK; return B_OK;
} }
@ -158,7 +133,7 @@ CommandLineUserInterface::Terminate()
fTerminating = true; fTerminating = true;
if (fShown) { if (fShown) {
// TODO: Signal the thread so it wakes up! fContext.Terminating();
// Wait for input loop to finish. // Wait for input loop to finish.
while (acquire_sem(fShowSemaphore) == B_INTERRUPTED) { while (acquire_sem(fShowSemaphore) == B_INTERRUPTED) {
@ -169,15 +144,7 @@ CommandLineUserInterface::Terminate()
fShowSemaphore = -1; fShowSemaphore = -1;
} }
if (fEditLine != NULL) { fContext.Cleanup();
el_end(fEditLine);
fEditLine = NULL;
}
if (fHistory != NULL) {
history_end(fHistory);
fHistory = NULL;
}
} }
@ -241,9 +208,11 @@ status_t
CommandLineUserInterface::_InputLoop() CommandLineUserInterface::_InputLoop()
{ {
while (!fTerminating) { while (!fTerminating) {
// Wait for a thread or Ctrl-C.
fContext.WaitForThreadOrUser();
// read a command line // read a command line
int count; const char* line = fContext.PromptUser(kDebuggerPrompt);
const char* line = el_gets(fEditLine, &count);
if (line == NULL) if (line == NULL)
break; break;
@ -269,8 +238,7 @@ CommandLineUserInterface::_InputLoop()
continue; continue;
// add line to history // add line to history
HistEvent historyEvent; fContext.AddLineToInputHistory(line);
history(fHistory, &historyEvent, H_ENTER, line);
// execute command // execute command
_ExecuteCommand(args.ArgumentCount(), args.Arguments()); _ExecuteCommand(args.ArgumentCount(), args.Arguments());

View File

@ -7,10 +7,6 @@
#define COMMAND_LINE_USER_INTERFACE_H #define COMMAND_LINE_USER_INTERFACE_H
#include <sys/cdefs.h>
// Needed in histedit.h.
#include <histedit.h>
#include <ObjectList.h> #include <ObjectList.h>
#include <String.h> #include <String.h>
@ -73,11 +69,9 @@ private:
private: private:
CliContext fContext; CliContext fContext;
CommandList fCommands; CommandList fCommands;
EditLine* fEditLine;
History* fHistory;
sem_id fShowSemaphore; sem_id fShowSemaphore;
bool fShown; bool fShown;
bool fTerminating; volatile bool fTerminating;
}; };