* 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
+65 -278
View File
@@ -28,26 +28,20 @@
#include <syslog_daemon.h>
#include <ctype.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include "debug_commands.h"
#include "debug_variables.h"
static const char* const kKDLPrompt = "kdebug> ";
extern "C" int kgets(char *buffer, int length);
static int invoke_command(struct debugger_command *command, int argc,
char** argv);
typedef struct debugger_command {
struct debugger_command *next;
int (*func)(int, char **);
const char *name;
const char *description;
} debugger_command;
int dbg_register_file[B_MAX_CPU_COUNT][14];
/* XXXmpetit -- must be made generic */
@@ -66,11 +60,6 @@ static struct syslog_message *sSyslogMessage;
static struct ring_buffer *sSyslogBuffer;
static bool sSyslogDropped = false;
static struct debugger_command *sCommands;
static jmp_buf sInvokeCommandEnv;
static bool sInvokeCommandDirectly = false;
static const char* sCurrentKernelDebuggerMessage;
#define SYSLOG_BUFFER_SIZE 65536
@@ -97,52 +86,6 @@ static char *sArguments[MAX_ARGS] = { NULL, };
#define distance(a, b) ((a) < (b) ? (b) - (a) : (a) - (b))
static debugger_command*
next_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;
}
static debugger_command *
find_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_command(NULL, name, length);
if (command != NULL) {
if (next_command(command, name, length) == NULL)
return command;
ambiguous = true;
}
}
return NULL;
}
static void
kputchar(char c)
{
@@ -256,14 +199,14 @@ public:
tmpChar = *firstSpace;
*firstSpace = '\0';
bool ambiguous;
debugger_command* command = find_command(buffer, true, ambiguous);
debugger_command* command = find_debugger_command(buffer, true, ambiguous);
*firstSpace = tmpChar;
if (command != NULL) {
kputchar('\n');
char* args[3] = { NULL, "--help", NULL };
invoke_command(command, 2, args);
invoke_debugger_command(command, 2, args);
} else {
if (ambiguous)
kprintf("\nambiguous command\n");
@@ -281,7 +224,7 @@ public:
debugger_command* command = NULL;
int32 longestCommonPrefix = 0;
const char* previousCommandName = NULL;
while ((command = next_command(command, buffer, position))
while ((command = next_debugger_command(command, buffer, position))
!= NULL) {
count++;
int32 nameLength = strlen(command->name);
@@ -312,7 +255,7 @@ public:
reprintLine = true;
} else if (count == 1) {
// exactly one completion
command = next_command(NULL, buffer, position);
command = next_debugger_command(NULL, buffer, position);
// check for sufficient space in the buffer
int32 neededSpace = longestName - position + 1;
@@ -345,7 +288,7 @@ public:
int columns = 80 / (longestName + 2);
debugger_command* command = NULL;
int column = 0;
while ((command = next_command(command, buffer, position))
while ((command = next_debugger_command(command, buffer, position))
!= NULL) {
// spacing
if (column > 0 && column % columns == 0)
@@ -548,101 +491,6 @@ kgets(char *buffer, int length)
}
static int
parse_line(const char *buffer, char **argv, int *_argc, int32 maxArgs)
{
char *string = sParseLine;
int32 index = 0;
strcpy(string, buffer);
for (; index < maxArgs && string[0]; index++) {
char quoted;
char c;
// skip white space
while ((c = string[0]) != '\0' && isspace(c)) {
string++;
}
if (!c)
break;
if (c == '\'' || c == '"') {
argv[index] = ++string;
quoted = c;
} else {
argv[index] = string;
quoted = 0;
}
// find end of string
while (string[0]
&& ((quoted && string[0] != quoted)
|| (!quoted && !isspace(string[0])))) {
if (string[0] == '\\') {
// filter out backslashes
strcpy(string, string + 1);
string++;
}
string++;
}
if (string[0]) {
// terminate string
string[0] = '\0';
string++;
}
}
return *_argc = index;
}
/*! 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.
*/
static int
invoke_command(struct debugger_command *command, int argc, char** argv)
{
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;
}
static void
kernel_debugger_loop(void)
{
@@ -652,44 +500,43 @@ kernel_debugger_loop(void)
kprintf("Welcome to Kernel Debugging Land...\n");
kprintf("Running on CPU %ld\n", sDebuggerOnCPU);
for (;;) {
struct debugger_command *cmd = NULL;
int argc;
int32 continuableLine = -1;
// Index of the previous command line, if the command returned
// B_KDEBUG_CONT, i.e. asked to be repeatable, -1 otherwise.
for (;;) {
CommandLineEditingHelper editingHelper;
kprintf(kKDLPrompt);
read_line(sLineBuffer[sCurrentLine], LINE_BUFFER_SIZE, &editingHelper);
parse_line(sLineBuffer[sCurrentLine], sArguments, &argc, MAX_ARGS);
char* line = sLineBuffer[sCurrentLine];
read_line(line, LINE_BUFFER_SIZE, &editingHelper);
// We support calling last executed command again if
// B_KDEDUG_CONT was returned last time, so cmd != NULL
if (argc <= 0 && cmd == NULL)
continue;
// check, if the line is empty or whitespace only
bool whiteSpaceOnly = true;
for (int i = 0 ; line[i] != '\0'; i++) {
if (!isspace(line[i])) {
whiteSpaceOnly = false;
break;
}
}
if (whiteSpaceOnly) {
if (continuableLine < 0)
continue;
// the previous command can be repeated
sCurrentLine = continuableLine;
line = sLineBuffer[sCurrentLine];
}
sDebuggerOnCPU = smp_get_current_cpu();
bool ambiguous;
if (argc > 0)
cmd = find_command(sArguments[0], true, ambiguous);
int rc = evaluate_debug_command(line);
if (cmd == NULL) {
if (ambiguous) {
kprintf("Ambiguous command. Use tab completion to get a list "
"of matching commands. Enter \"help\" to get a list of "
"all supported commands.\n");
} else {
kprintf("Unknown command. Enter \"help\" to get a list of all "
"supported commands.\n");
}
} else {
int rc = invoke_command(cmd, argc, sArguments);
if (rc == B_KDEBUG_QUIT)
break; // okay, exit now.
if (rc == B_KDEBUG_QUIT)
break; // okay, exit now.
if (rc != B_KDEBUG_CONT)
cmd = NULL; // forget last command executed...
}
// If the command is continuable, remember the current line index.
continuableLine = (rc == B_KDEBUG_CONT ? sCurrentLine : -1);
if (++sCurrentLine >= HISTORY_SIZE)
sCurrentLine = 0;
@@ -725,7 +572,7 @@ cmd_help(int argc, char **argv)
bool ambiguous;
if (argc > 1) {
specified = find_command(argv[1], false, ambiguous);
specified = find_debugger_command(argv[1], false, ambiguous);
if (specified == NULL) {
start = argv[1];
startLength = strlen(start);
@@ -740,7 +587,8 @@ cmd_help(int argc, char **argv)
else
kprintf("debugger commands:\n");
for (command = sCommands; command != NULL; command = command->next) {
for (command = get_debugger_commands(); command != NULL;
command = command->next) {
if (specified && command->func != specified->func)
continue;
if (start != NULL && strncmp(start, command->name, startLength))
@@ -771,6 +619,25 @@ cmd_dump_kdl_message(int argc, char **argv)
return 0;
}
static int
cmd_expr(int argc, char **argv)
{
static const char* usage = "usage: expr <expression>\n"
"Evaluates the given expression and prints the result.\n";
if (argc != 2 || strcmp(argv[1], "--help") == 0) {
kprintf(usage);
return 0;
}
uint64 result;
if (evaluate_debug_expression(argv[1], &result, false)) {
kprintf("%llu (0x%llx)\n", result, result);
set_debug_variable("_", result);
}
return 0;
}
static status_t
syslog_sender(void *data)
@@ -1017,7 +884,10 @@ debug_init_post_vm(kernel_args *args)
add_debugger_command("continue", &cmd_continue, "Leave kernel debugger");
add_debugger_command("message", &cmd_dump_kdl_message,
"Reprint the message printed when entering KDL");
add_debugger_command("expr", &cmd_expr,
"Evaluates the given expression and prints the result");
debug_variables_init();
frame_buffer_console_init(args);
arch_debug_console_init_settings(args);
tracing_init();
@@ -1089,75 +959,11 @@ debug_init_post_modules(struct kernel_args *args)
// #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;
}
uint32
parse_expression(const char *expression)
{
// TODO: Implement expression parser (cf. BeBook).
return strtoul(expression, NULL, 0);
uint64 result;
return (evaluate_debug_expression(expression, &result, true) ? result : 0);
}
@@ -1207,27 +1013,8 @@ kernel_debugger(const char *message)
sCurrentKernelDebuggerMessage = message;
// 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;
}
}
// sort the commands
sort_debugger_commands();
kernel_debugger_loop();