* Implemented parse_expression(). The back-end is an expression parser

that is a little more powerful than BeOS'. It features:
  - Persistent and temporary uint64 variables. The former kind is set
    only by the user. The latter (those prefixed "_") can be set
    automatically by commands, thus e.g. making it easier to access
    members of a dumped structure. They are unset when the next command
    is invoked. The special temporary variable "_" is defined as a
    command's return value.
  - Expressions can contain nested command invocations using brackets
    ("[ ... ]").
  - Command lines are parsed by the expression parser, too. They can
    contain command invocations (in brackets) and expressions (in
    parentheses).
* Added debugger commands:
  - expr: Evaluates the given expression and prints the result.
  - unset: Undefines a variable.
  - vars: Prints the values of all defined variables.
* Moved debugger command code into its own source file.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23546 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2008-01-16 00:01:20 +00:00
parent 5d24ef2e15
commit 3b4fa1664e
8 changed files with 1537 additions and 279 deletions
+225
View File
@@ -0,0 +1,225 @@
/*
* Copyright 2008, Ingo Weinhold, [email protected]
* Copyright 2002-2007, Axel Dörfler, [email protected]
* Distributed under the terms of the MIT License.
*
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include "debug_commands.h"
#include <setjmp.h>
#include <string.h>
#include <KernelExport.h>
#include <debug.h>
#include <lock.h>
#include <thread.h>
#include "debug_variables.h"
static spinlock sSpinlock = 0;
static struct debugger_command *sCommands;
static jmp_buf sInvokeCommandEnv;
static bool sInvokeCommandDirectly = false;
debugger_command*
next_debugger_command(debugger_command* command, const char* prefix, int prefixLen)
{
if (command == NULL)
command = sCommands;
else
command = command->next;
while (command != NULL && !strncmp(prefix, command->name, prefixLen) == 0)
command = command->next;
return command;
}
debugger_command *
find_debugger_command(const char *name, bool partialMatch, bool& ambiguous)
{
debugger_command *command;
ambiguous = false;
// search command by full name
for (command = sCommands; command != NULL; command = command->next) {
if (strcmp(name, command->name) == 0)
return command;
}
// if it couldn't be found, search for a partial match
if (partialMatch) {
int length = strlen(name);
command = next_debugger_command(NULL, name, length);
if (command != NULL) {
if (next_debugger_command(command, name, length) == NULL)
return command;
ambiguous = true;
}
}
return NULL;
}
/*! This function is a safe gate through which debugger commands are invoked.
It sets a fault handler before invoking the command, so that an invalid
memory access will not result in another KDL session on top of this one
(and "cont" not to work anymore). We use setjmp() + longjmp() to "unwind"
the stack after catching a fault.
*/
int
invoke_debugger_command(struct debugger_command *command, int argc, char** argv)
{
// remove the temporary variables of the previously executed command, if
// this command sets a temporary variable
mark_temporary_debug_variables_obsolete();
struct thread* thread = thread_get_current_thread();
addr_t oldFaultHandler = thread->fault_handler;
// replace argv[0] with the actual command name
argv[0] = (char *)command->name;
// Invoking the command directly might be useful when debugging debugger
// commands.
if (sInvokeCommandDirectly)
return command->func(argc, argv);
if (setjmp(sInvokeCommandEnv) == 0) {
int result;
thread->fault_handler = (addr_t)&&error;
// Fake goto to trick the compiler not to optimize the code at the label
// away.
if (!thread)
goto error;
result = command->func(argc, argv);
thread->fault_handler = oldFaultHandler;
return result;
error:
longjmp(sInvokeCommandEnv, 1);
// jump into the else branch
} else {
kprintf("\n[*** READ/WRITE FAULT ***]\n");
}
thread->fault_handler = oldFaultHandler;
return 0;
}
debugger_command*
get_debugger_commands()
{
return sCommands;
}
void
sort_debugger_commands()
{
// bubble sort the commands
debugger_command* stopCommand = NULL;
while (stopCommand != sCommands) {
debugger_command** command = &sCommands;
while (true) {
debugger_command* nextCommand = (*command)->next;
if (nextCommand == stopCommand) {
stopCommand = *command;
break;
}
if (strcmp((*command)->name, nextCommand->name) > 0) {
debugger_command* tmpCommand = nextCommand->next;
(*command)->next = nextCommand->next;
nextCommand->next = *command;
*command = nextCommand;
}
command = &(*command)->next;
}
}
}
// #pragma mark - public API
int
add_debugger_command(char *name, int (*func)(int, char **), char *desc)
{
cpu_status state;
struct debugger_command *cmd;
cmd = (struct debugger_command *)malloc(sizeof(struct debugger_command));
if (cmd == NULL)
return ENOMEM;
cmd->func = func;
cmd->name = name;
cmd->description = desc;
state = disable_interrupts();
acquire_spinlock(&sSpinlock);
cmd->next = sCommands;
sCommands = cmd;
release_spinlock(&sSpinlock);
restore_interrupts(state);
return B_NO_ERROR;
}
int
remove_debugger_command(char * name, int (*func)(int, char **))
{
struct debugger_command *cmd = sCommands;
struct debugger_command *prev = NULL;
cpu_status state;
state = disable_interrupts();
acquire_spinlock(&sSpinlock);
while (cmd) {
if (!strcmp(cmd->name, name) && cmd->func == func)
break;
prev = cmd;
cmd = cmd->next;
}
if (cmd) {
if (cmd == sCommands)
sCommands = cmd->next;
else
prev->next = cmd->next;
}
release_spinlock(&sSpinlock);
restore_interrupts(state);
if (cmd) {
free(cmd);
return B_NO_ERROR;
}
return B_NAME_NOT_FOUND;
}