From 3b4fa1664e6fb9ba781ee509bf725774937eeaeb Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Wed, 16 Jan 2008 00:01:20 +0000 Subject: [PATCH] * 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 --- headers/private/kernel/debug.h | 13 +- src/system/kernel/debug/Jamfile | 3 + src/system/kernel/debug/debug.cpp | 343 ++------ src/system/kernel/debug/debug_commands.cpp | 225 +++++ src/system/kernel/debug/debug_commands.h | 38 + src/system/kernel/debug/debug_parser.cpp | 929 ++++++++++++++++++++ src/system/kernel/debug/debug_variables.cpp | 239 +++++ src/system/kernel/debug/debug_variables.h | 26 + 8 files changed, 1537 insertions(+), 279 deletions(-) create mode 100644 src/system/kernel/debug/debug_commands.cpp create mode 100644 src/system/kernel/debug/debug_commands.h create mode 100644 src/system/kernel/debug/debug_parser.cpp create mode 100644 src/system/kernel/debug/debug_variables.cpp create mode 100644 src/system/kernel/debug/debug_variables.h diff --git a/headers/private/kernel/debug.h b/headers/private/kernel/debug.h index 373fc3fc20..95dc253897 100644 --- a/headers/private/kernel/debug.h +++ b/headers/private/kernel/debug.h @@ -59,7 +59,18 @@ extern void debug_stop_screen_debug_output(void); extern void dprintf_no_syslog(const char *format, ...) __attribute__ ((format (__printf__, 1, 2))); -extern void _user_debug_output(const char *userString); +extern bool is_debug_variable_defined(const char* variableName); +extern bool set_debug_variable(const char* variableName, uint64 value); +extern uint64 get_debug_variable(const char* variableName, + uint64 defaultValue); +extern bool remove_debug_variable(const char* variableName); +extern void remove_all_temporary_debug_variables(); + +extern bool evaluate_debug_expression(const char* expression, + uint64* result, bool silent); +extern int evaluate_debug_command(const char* command); + +extern void _user_debug_output(const char *userString); #ifdef __cplusplus } diff --git a/src/system/kernel/debug/Jamfile b/src/system/kernel/debug/Jamfile index f0922aacf9..d3c30fdada 100644 --- a/src/system/kernel/debug/Jamfile +++ b/src/system/kernel/debug/Jamfile @@ -5,6 +5,9 @@ UsePrivateHeaders [ FDirName kernel debug ] syslog_daemon ; KernelMergeObject kernel_debug.o : blue_screen.cpp debug.cpp + debug_commands.cpp + debug_parser.cpp + debug_variables.cpp frame_buffer_console.cpp gdb.c tracing.cpp diff --git a/src/system/kernel/debug/debug.cpp b/src/system/kernel/debug/debug.cpp index 3aeb32cafe..5f0739081e 100644 --- a/src/system/kernel/debug/debug.cpp +++ b/src/system/kernel/debug/debug.cpp @@ -28,26 +28,20 @@ #include #include -#include #include #include #include #include #include +#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 \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(); diff --git a/src/system/kernel/debug/debug_commands.cpp b/src/system/kernel/debug/debug_commands.cpp new file mode 100644 index 0000000000..a03735e0ba --- /dev/null +++ b/src/system/kernel/debug/debug_commands.cpp @@ -0,0 +1,225 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de + * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de + * 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 +#include + +#include + +#include +#include +#include + +#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; +} + diff --git a/src/system/kernel/debug/debug_commands.h b/src/system/kernel/debug/debug_commands.h new file mode 100644 index 0000000000..6ece288497 --- /dev/null +++ b/src/system/kernel/debug/debug_commands.h @@ -0,0 +1,38 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_DEBUG_COMMANDS_H +#define _KERNEL_DEBUG_COMMANDS_H + + +#include + + +struct debugger_command { + struct debugger_command *next; + int (*func)(int, char **); + const char *name; + const char *description; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +debugger_command* next_debugger_command(debugger_command* command, + const char* prefix, int prefixLen); +debugger_command* find_debugger_command(const char* name, bool partialMatch, + bool& ambiguous); +int invoke_debugger_command(struct debugger_command *command, int argc, + char** argv); + +debugger_command* get_debugger_commands(); +void sort_debugger_commands(); + +#ifdef __cplusplus +} // extern "C" +#endif + + +#endif // _KERNEL_DEBUG_COMMANDS_H diff --git a/src/system/kernel/debug/debug_parser.cpp b/src/system/kernel/debug/debug_parser.cpp new file mode 100644 index 0000000000..ece6880cba --- /dev/null +++ b/src/system/kernel/debug/debug_parser.cpp @@ -0,0 +1,929 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de + * Copyright 2006, Stephan Aßmus, superstippi@gmx.de + * Distributed under the terms of the MIT License. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include "debug_commands.h" +#include "debug_variables.h" + + +/* + Grammar: + + commandLine := command | ( "(" expression ")" ) + expression := term | assignment + assignment := variable ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" ) + expression + term := sum + sum := product ( ( "+" | "-" ) product )* + product := unary ( ( "*" | "/" | "%" ) unary )* + unary := [ "-" ] atom + atom := variable | ( "(" expression ")" ) | ( "[" command "]" ) + variable := identifier + identifier := ( "_" | "a" - "z" | "A" - "Z" ) + ( "_" | "a" - "z" | "A" - "Z" | "0" - "9" )* + command := identifier argument* + argument := ( "(" expression ")" ) | ( "[" commandLine "]" ) + | unquotedString | quotedString +*/ + + +static const int kMaxTokenLength = 128; +static const int kJumpBufferCount = 10; + +static const int kMaxArgumentCount = 16; +static const size_t kTemporaryStorageSize = 10240; + +static jmp_buf sJumpBuffers[kJumpBufferCount]; +static int sNextJumpBufferIndex = 0; + +static char sExceptionMessage[128]; +static int sExceptionPosition; + +static char sTempBuffer[128]; + // for composing debug output etc. + +// temporary storage for command argument vectors and the arguments itself +static uint8 sTemporaryStorage[kTemporaryStorageSize]; +static size_t sTemporaryStorageUsed = 0; + +enum { + TOKEN_ASSIGN_FLAG = 0x100, + TOKEN_FLAGS = TOKEN_ASSIGN_FLAG, + + TOKEN_IDENTIFIER = 'a', + + TOKEN_CONSTANT = '0', + + TOKEN_PLUS = '+', + TOKEN_MINUS = '-', + + TOKEN_STAR = '*', + TOKEN_SLASH = '/', + TOKEN_MODULO = '%', + + TOKEN_ASSIGN = '=' | TOKEN_ASSIGN_FLAG, + TOKEN_PLUS_ASSIGN = TOKEN_PLUS | TOKEN_ASSIGN_FLAG, + TOKEN_MINUS_ASSIGN = TOKEN_MINUS | TOKEN_ASSIGN_FLAG, + TOKEN_STAR_ASSIGN = TOKEN_STAR | TOKEN_ASSIGN_FLAG, + TOKEN_SLASH_ASSIGN = TOKEN_SLASH | TOKEN_ASSIGN_FLAG, + TOKEN_MODULO_ASSIGN = TOKEN_MODULO | TOKEN_ASSIGN_FLAG, + + TOKEN_OPENING_PARENTHESIS = '(', + TOKEN_CLOSING_PARENTHESIS = ')', + TOKEN_OPENING_BRACKET = '[', + TOKEN_CLOSING_BRACKET = ']', + + TOKEN_STRING = '"', + TOKEN_UNKNOWN = '?', + TOKEN_NONE = ' ', + TOKEN_END_OF_LINE = '\n', +}; + +struct Token { + char string[kMaxTokenLength]; + uint64 value; + int32 type; + int32 position; + + void SetTo(const char* string, int32 length, int32 position, int32 type) + { + length = min_c((size_t)length, (sizeof(this->string) - 1)); + strlcpy(this->string, string, length + 1); + this->type = type; + this->value = 0; + this->position = position; + } + + void Unset() + { + string[0] = '\0'; + value = 0; + type = TOKEN_NONE; + position = 0; + } +}; + + +// #pragma mark - exceptions + + +static void +parse_exception(const char* message, int32 position) +{ + if (sNextJumpBufferIndex == 0) { + kprintf("parse_exception(): No jump buffer!\n"); + kprintf("exception: \"%s\", position: %lu\n", message, position); + return; + } + + strlcpy(sExceptionMessage, message, sizeof(sExceptionMessage)); + sExceptionPosition = position; + + longjmp(sJumpBuffers[sNextJumpBufferIndex - 1], 1); +} + + +// #pragma mark - temporary storage + + +static void* +allocate_temp_storage(size_t size) +{ + // 8 byte align + size = (size + 7) & ~7; + + if (sTemporaryStorageUsed + size > kTemporaryStorageSize) { + parse_exception("out of temporary storage for command execution", -1); + return NULL; + } + + void* buffer = sTemporaryStorage + sTemporaryStorageUsed; + sTemporaryStorageUsed += size; + + return buffer; +} + + +static void +free_temp_storage(void* _buffer) +{ + uint8* buffer = (uint8*)_buffer; + + if (buffer == NULL) + return; + + // must be freed in the reverse allocation order + if (buffer < sTemporaryStorage + || buffer > sTemporaryStorage + sTemporaryStorageUsed) { + panic("Invalid pointer passed to free_temp_storage(): %p, temp " + "storage base: %p", buffer, sTemporaryStorage); + return; + } + + sTemporaryStorageUsed = buffer - sTemporaryStorage; +} + + +// #pragma mark - Tokenizer + + +class Tokenizer { +public: + Tokenizer(const char* string) + : fCommandMode(false) + { + SetTo(string); + } + + void SetTo(const char* string) + { + fString = fCurrentChar = string; + fCurrentToken.Unset(); + fReuseToken = false; + } + + void SetPosition(int32 position) + { + fCurrentChar = fString + position; + fCurrentToken.Unset(); + fReuseToken = false; + } + + void SetCommandMode(bool commandMode) + { + fCommandMode = commandMode; + } + + const Token& NextToken() + { + if (fCurrentToken.type == TOKEN_END_OF_LINE) + return fCurrentToken; + + if (fReuseToken) { + fReuseToken = false; + return fCurrentToken; + } + + while (*fCurrentChar != 0 && isspace(*fCurrentChar)) + fCurrentChar++; + + if (*fCurrentChar == 0) { + fCurrentToken.SetTo("", 0, _CurrentPos(), TOKEN_END_OF_LINE); + return fCurrentToken; + } + + return (fCommandMode ? _NextTokenCommand() : _NextTokenExpression()); + } + + void RewindToken() + { + fReuseToken = true; + } + + private: + const Token& _NextTokenExpression() + { + if (isdigit(*fCurrentChar)) { + // number + const char* begin = fCurrentChar++; + + if (*fCurrentChar == 'x') { + // hex number + fCurrentChar++; + while (*fCurrentChar != 0 + && (isdigit(*fCurrentChar) + || strchr("abcdeABCDE", *fCurrentChar))) { + fCurrentChar++; + } + + if (fCurrentChar - begin == 2) + parse_exception("invalid hex number", begin - fString); + + } else { + // decimal number + while (*fCurrentChar != 0 && isdigit(*fCurrentChar)) + fCurrentChar++; + } + + int32 length = fCurrentChar - begin; + fCurrentToken.SetTo(begin, length, _CurrentPos() - length, + TOKEN_CONSTANT); + fCurrentToken.value = strtoull(fCurrentToken.string, NULL, 0); + + } else if (isalpha(*fCurrentChar) || *fCurrentChar == '_') { + // identifier + const char* begin = fCurrentChar; + while (*fCurrentChar != 0 + && (isalpha(*fCurrentChar) || *fCurrentChar == '_' + || isdigit(*fCurrentChar))) { + fCurrentChar++; + } + + int32 length = fCurrentChar - begin; + fCurrentToken.SetTo(begin, length, _CurrentPos() - length, + TOKEN_IDENTIFIER); + + } else { + const char* begin = fCurrentChar; + char c = *fCurrentChar; + fCurrentChar++; + int32 flags = 0; + + switch (c) { + case '=': + fCurrentChar--; + case '+': + case '-': + case '*': + case '/': + case '%': + if (*fCurrentChar == '=') { + fCurrentChar++; + flags = TOKEN_ASSIGN_FLAG; + } + + case '(': + case ')': + case '[': + case ']': + { + int32 length = fCurrentChar - begin; + fCurrentToken.SetTo(begin, length, _CurrentPos() - length, + c | flags); + break; + } + + case '"': + { + _QuotedString(); + break; + } + + default: + { + _UnquotedString(); + break; + } + } + } + + return fCurrentToken; + } + + const Token& _NextTokenCommand() + { + switch (*fCurrentChar) { + case '(': + case ')': + case '[': + case ']': + fCurrentToken.SetTo(fCurrentChar, 1, _CurrentPos(), + *fCurrentChar); + fCurrentChar++; + return fCurrentToken; + case '"': + return _QuotedString(); + + default: + return _UnquotedString(); + } + } + + const Token& _QuotedString() + { + const char* begin = fCurrentChar++; + int32 length = 0; + + while (*fCurrentChar != '\0' && *fCurrentChar != '"') { + char c = *fCurrentChar; + fCurrentChar++; + + if (c == '\\') { + // an escaped char + c = *fCurrentChar; + fCurrentChar++; + + if (c == '\0') + break; + } + + if ((size_t)length + >= sizeof(fCurrentToken.string) - 1) { + parse_exception("quoted string too long", begin - fString); + } + + fCurrentToken.string[length++] = c; + } + + if (*fCurrentChar == '\0') { + parse_exception("unexpected end of line while " + "parsing quoted string", begin - fString); + } + + fCurrentChar++; + + fCurrentToken.string[length] = '\0'; + fCurrentToken.value = 0; + fCurrentToken.type = TOKEN_STRING; + fCurrentToken.position = begin - fString; + + return fCurrentToken; + } + + const Token& _UnquotedString() + { + const char* begin = fCurrentChar; + + while (*fCurrentChar != 0 && !_IsUnquotedDelimitingChar(*fCurrentChar)) + fCurrentChar++; + + int32 length = fCurrentChar - begin; + fCurrentToken.SetTo(begin, length, _CurrentPos() - length, + TOKEN_UNKNOWN); + + return fCurrentToken; + } + + bool _IsUnquotedDelimitingChar(char c) + { + if (isspace(c)) + return true; + + switch (c) { + case '(': + case ')': + case '[': + case ']': + case '"': + return true; + + case '=': + case '+': + case '-': + case '*': + case '/': + case '%': + return !fCommandMode; + + default: + return false; + } + } + + int32 _CurrentPos() const + { + return fCurrentChar - fString; + } + +private: + const char* fString; + const char* fCurrentChar; + Token fCurrentToken; + bool fReuseToken; + bool fCommandMode; +}; + + +// #pragma mark - ExpressionParser + + +class ExpressionParser { + public: + ExpressionParser(); + ~ExpressionParser(); + + uint64 EvaluateExpression( + const char* expressionString); + uint64 EvaluateCommand( + const char* expressionString, + int& returnCode); + + private: + uint64 _ParseExpression(); + uint64 _ParseCommand(int& returnCode); + bool _ParseArgument(int& argc, char** argv); + void _AddArgument(int& argc, char** argv, + const char* argument); + uint64 _ParseSum(); + uint64 _ParseProduct(); + uint64 _ParsePower(); + uint64 _ParseUnary(); + uint64 _ParseAtom(); + + const Token& _EatToken(int32 type); + + Tokenizer fTokenizer; +}; + + +ExpressionParser::ExpressionParser() + : fTokenizer("") +{ +} + + +ExpressionParser::~ExpressionParser() +{ +} + + +uint64 +ExpressionParser::EvaluateExpression(const char* expressionString) +{ + fTokenizer.SetTo(expressionString); + + uint64 value = _ParseExpression(); + const Token& token = fTokenizer.NextToken(); + if (token.type != TOKEN_END_OF_LINE) + parse_exception("parse error", token.position); + + return value; +} + + +uint64 +ExpressionParser::EvaluateCommand(const char* expressionString, + int& returnCode) +{ + fTokenizer.SetTo(expressionString); + + // Allowed is not only a command, but also an assignment. Either starts with + // an identifier. + _EatToken(TOKEN_IDENTIFIER); + + uint64 value; + const Token& token = fTokenizer.NextToken(); + if (token.type & TOKEN_ASSIGN_FLAG) { + // an assignment + fTokenizer.SetTo(expressionString); + value = _ParseExpression(); + returnCode = 0; + } else { + // no assignment, so let's assume it's a command + fTokenizer.SetTo(expressionString); + fTokenizer.SetCommandMode(true); + value = _ParseCommand(returnCode); + } + + if (token.type != TOKEN_END_OF_LINE) + parse_exception("parse error", token.position); + + return value; +} + + +uint64 +ExpressionParser::_ParseExpression() +{ + const Token& token = fTokenizer.NextToken(); + int32 position = token.position; + if (token.type == TOKEN_IDENTIFIER) { + char variable[MAX_DEBUG_VARIABLE_NAME_LEN]; + strlcpy(variable, token.string, sizeof(variable)); + + int32 assignmentType = fTokenizer.NextToken().type; + if (assignmentType & TOKEN_ASSIGN_FLAG) { + // an assignment + uint64 rhs = _ParseExpression(); + + // handle the standard assignment separately -- the other kinds + // need the variable to be defined + if (assignmentType == TOKEN_ASSIGN) { + if (!set_debug_variable(variable, rhs)) { + snprintf(sTempBuffer, sizeof(sTempBuffer), + "failed to set value for variable \"%s\"", + variable); + parse_exception(sTempBuffer, position); + } + + return rhs; + } + + // variable must be defined + if (!is_debug_variable_defined(variable)) { + snprintf(sTempBuffer, sizeof(sTempBuffer), + "variable \"%s\" not defined in modifying assignment", + variable); + parse_exception(sTempBuffer, position); + } + + uint64 variableValue = get_debug_variable(variable, 0); + + // check for division by zero for the respective assignment types + if ((assignmentType == TOKEN_SLASH_ASSIGN + || assignmentType == TOKEN_MODULO_ASSIGN) + && rhs == 0) { + parse_exception("division by zero", position); + } + + // compute the new variable value + switch (assignmentType) { + case TOKEN_PLUS_ASSIGN: + variableValue += rhs; + break; + case TOKEN_MINUS_ASSIGN: + variableValue -= rhs; + break; + case TOKEN_STAR_ASSIGN: + variableValue *= rhs; + break; + case TOKEN_SLASH_ASSIGN: + variableValue /= rhs; + break; + case TOKEN_MODULO_ASSIGN: + variableValue %= rhs; + break; + default: + fTokenizer.SetPosition(position); + return _ParseSum(); + } + + set_debug_variable(variable, variableValue); + return variableValue; + } + } + + // no assignment -- reset to the identifier position and parse a sum + fTokenizer.SetPosition(position); + return _ParseSum(); +} + + +uint64 +ExpressionParser::_ParseCommand(int& returnCode) +{ + fTokenizer.SetCommandMode(false); + const Token& token = _EatToken(TOKEN_IDENTIFIER); + fTokenizer.SetCommandMode(true); + + bool ambiguous; + debugger_command* command = find_debugger_command(token.string, true, + ambiguous); + + if (command == NULL) { + if (ambiguous) { + snprintf(sTempBuffer, sizeof(sTempBuffer), + "Ambiguous command \"%s\". Use tab " + "completion to get a list of matching commands. Enter \"help\" " + "to get a list of all supported commands.\n", token.string); + } else { + snprintf(sTempBuffer, sizeof(sTempBuffer), + "Unknown command \"%s\". Enter \"help\" to get a list of " + "all supported commands.\n", token.string); + } + + parse_exception(sTempBuffer, -1); + } + + // allocate temporary buffer for the argument vector + char** argv = (char**)allocate_temp_storage( + kMaxArgumentCount * sizeof(char*)); + int argc = 0; + argv[argc++] = (char*)command->name; + + // get the arguments + while (fTokenizer.NextToken().type != TOKEN_END_OF_LINE) { + fTokenizer.RewindToken(); + if (!_ParseArgument(argc, argv)) + break; + } + + // invoke the command + returnCode = invoke_debugger_command(command, argc, argv); + + free_temp_storage(argv); + + return get_debug_variable("_", 0); +} + + +bool +ExpressionParser::_ParseArgument(int& argc, char** argv) +{ + const Token& token = fTokenizer.NextToken(); + switch (token.type) { + case TOKEN_OPENING_PARENTHESIS: + { + // this starts an expression + fTokenizer.SetCommandMode(false); + uint64 value = _ParseExpression(); + fTokenizer.SetCommandMode(true); + _EatToken(TOKEN_CLOSING_PARENTHESIS); + + snprintf(sTempBuffer, sizeof(sTempBuffer), "%llu", value); + _AddArgument(argc, argv, sTempBuffer); + return true; + } + + case TOKEN_OPENING_BRACKET: + { + // this starts a sub command + int returnValue; + uint64 value = _ParseCommand(returnValue); + _EatToken(TOKEN_CLOSING_BRACKET); + + snprintf(sTempBuffer, sizeof(sTempBuffer), "%llu", value); + _AddArgument(argc, argv, sTempBuffer); + return true; + } + + case TOKEN_STRING: + case TOKEN_UNKNOWN: + _AddArgument(argc, argv, token.string); + return true; + + case TOKEN_CLOSING_PARENTHESIS: + case TOKEN_CLOSING_BRACKET: + // those don't belong to us + fTokenizer.RewindToken(); + return false; + + default: + { + snprintf(sTempBuffer, sizeof(sTempBuffer), "unexpected token " + "\"%s\"", token.string); + parse_exception(sTempBuffer, token.position); + return false; + } + } +} + + +void +ExpressionParser::_AddArgument(int& argc, char** argv, const char* argument) +{ + if (argc == kMaxArgumentCount) + parse_exception("too many arguments for command", 0); + + size_t length = strlen(argument) + 1; + char* buffer = (char*)allocate_temp_storage(length); + memcpy(buffer, argument, length); + + argv[argc++] = buffer; +} + + +uint64 +ExpressionParser::_ParseSum() +{ + uint64 value = _ParseProduct(); + + while (true) { + const Token& token = fTokenizer.NextToken(); + switch (token.type) { + case TOKEN_PLUS: + value = value + _ParseProduct(); + break; + case TOKEN_MINUS: + value = value - _ParseProduct(); + break; + + default: + fTokenizer.RewindToken(); + return value; + } + } +} + + +uint64 +ExpressionParser::_ParseProduct() +{ + uint64 value = _ParseUnary(); + + while (true) { + Token token = fTokenizer.NextToken(); + switch (token.type) { + case TOKEN_STAR: + value = value * _ParseUnary(); + break; + case TOKEN_SLASH: { + uint64 rhs = _ParseUnary(); + if (rhs == 0) + parse_exception("division by zero", token.position); + value = value / rhs; + break; + } + case TOKEN_MODULO: { + uint64 rhs = _ParseUnary(); + if (rhs == 0) + parse_exception("modulo by zero", token.position); + value = value % rhs; + break; + } + + default: + fTokenizer.RewindToken(); + return value; + } + } +} + + +uint64 +ExpressionParser::_ParseUnary() +{ + const Token& token = fTokenizer.NextToken(); + if (token.type == TOKEN_END_OF_LINE) + parse_exception("unexpected end of expression", token.position); + + switch (token.type) { + case TOKEN_MINUS: + return -_ParseUnary(); + + default: + fTokenizer.RewindToken(); + return _ParseAtom(); + } + + return 0; +} + + +uint64 +ExpressionParser::_ParseAtom() +{ + const Token& token = fTokenizer.NextToken(); + if (token.type == TOKEN_END_OF_LINE) + parse_exception("unexpected end of expression", token.position); + + if (token.type == TOKEN_CONSTANT) + return token.value; + + if (token.type == TOKEN_IDENTIFIER) { + if (!is_debug_variable_defined(token.string)) { + snprintf(sTempBuffer, sizeof(sTempBuffer), + "variable '%s' undefined", token.string); + parse_exception(sTempBuffer, token.position); + } + + return get_debug_variable(token.string, 0); + } + + if (token.type == TOKEN_OPENING_PARENTHESIS) { + uint64 value = _ParseExpression(); + + _EatToken(TOKEN_CLOSING_PARENTHESIS); + + return value; + } + + // it can only be a "[ command ]" expression now + fTokenizer.RewindToken(); + + _EatToken(TOKEN_OPENING_BRACKET); + + fTokenizer.SetCommandMode(true); + int returnValue; + uint64 value = _ParseCommand(returnValue); + fTokenizer.SetCommandMode(false); + + _EatToken(TOKEN_CLOSING_BRACKET); + + return value; +} + + +const Token& +ExpressionParser::_EatToken(int32 type) +{ + const Token& token = fTokenizer.NextToken(); + if (token.type != type) { + snprintf(sTempBuffer, sizeof(sTempBuffer), "expected token type '%c', " + "got token '%s'", char(type & ~TOKEN_FLAGS), token.string); + parse_exception(sTempBuffer, token.position); + } + + return token; +} + + + +// #pragma mark - + + +bool +evaluate_debug_expression(const char* expression, uint64* _result, bool silent) +{ + if (sNextJumpBufferIndex >= kJumpBufferCount) { + kprintf("evaluate_debug_expression(): Out of jump buffers for " + "exception handling\n"); + return 0; + } + + bool success; + uint64 result; + void* temporaryStorageMark = allocate_temp_storage(0); + // get a temporary storage mark, so we can cleanup everything that + // is allocated during the evaluation + + if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) { + result = ExpressionParser().EvaluateExpression(expression); + success = true; + } else { + result = 0; + success = false; + if (!silent) { + if (sExceptionPosition >= 0) { + kprintf("%s, at position: %d, in expression: %s\n", + sExceptionMessage, sExceptionPosition, expression); + } else + kprintf("%s", sExceptionMessage); + } + } + + sNextJumpBufferIndex--; + + // cleanup temp allocations + free_temp_storage(temporaryStorageMark); + + if (success && _result != NULL) + *_result = result; + + return success; +} + + +int +evaluate_debug_command(const char* commandLine) +{ + if (sNextJumpBufferIndex >= kJumpBufferCount) { + kprintf("evaluate_debug_command(): Out of jump buffers for " + "exception handling\n"); + return 0; + } + + int returnCode = 0; + void* temporaryStorageMark = allocate_temp_storage(0); + // get a temporary storage mark, so we can cleanup everything that + // is allocated during the evaluation + + if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) { + ExpressionParser().EvaluateCommand(commandLine, returnCode); + } else { + if (sExceptionPosition >= 0) { + kprintf("%s, at position: %d, in command line: %s\n", + sExceptionMessage, sExceptionPosition, commandLine); + } else + kprintf("%s", sExceptionMessage); + } + + sNextJumpBufferIndex--; + + // cleanup temp allocations + free_temp_storage(temporaryStorageMark); + + return returnCode; +} diff --git a/src/system/kernel/debug/debug_variables.cpp b/src/system/kernel/debug/debug_variables.cpp new file mode 100644 index 0000000000..3897db06b8 --- /dev/null +++ b/src/system/kernel/debug/debug_variables.cpp @@ -0,0 +1,239 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de + * Distributed under the terms of the MIT License. + */ + +#include "debug_variables.h" + +#include + +#include + +#include + + +static const int kVariableCount = 64; +static const int kTemporaryVariableCount = 32; +static const char kTemporaryVariablePrefix = '_'; +static const char* const kCommandReturnValueVariable = "_"; + +struct Variable { + char name[MAX_DEBUG_VARIABLE_NAME_LEN]; + uint64 value; + + inline bool IsUsed() const + { + return name[0] != '\0'; + } + + void Init(const char* variableName) + { + strlcpy(name, variableName, sizeof(name)); + } + + void Uninit() + { + name[0] = '\0'; + } + + inline bool HasName(const char* variableName) const + { + return strncmp(name, variableName, sizeof(name)) == 0; + } +}; + +static Variable sVariables[kVariableCount]; +static Variable sTemporaryVariables[kTemporaryVariableCount]; + +static bool sTemporaryVariablesObsolete = false; + + +static inline bool +is_temporary_variable(const char* variableName) +{ + return variableName[0] == kTemporaryVariablePrefix; +} + + +static Variable* +get_variable(const char* variableName, bool create) +{ + Variable* variables; + int variableCount; + + // get the variable domain (persistent or temporary) + if (is_temporary_variable(variableName)) { + variables = sTemporaryVariables; + variableCount = kTemporaryVariableCount; + } else { + variables = sVariables; + variableCount = kVariableCount; + } + + Variable* freeSlot = NULL; + + for (int i = 0; i < variableCount; i++) { + Variable* variable = variables + i; + + if (!variable->IsUsed()) { + if (freeSlot == NULL) + freeSlot = variable; + } else if (variable->HasName(variableName)) + return variable; + } + + if (create && freeSlot != NULL) { + freeSlot->Init(variableName); + return freeSlot; + } + + return NULL; +} + + +// #pragma mark - debugger commands + + +static int +cmd_unset_variable(int argc, char **argv) +{ + static const char* usage = "usage: unset \n" + "Unsets the given variable, if it exists.\n"; + if (argc != 2 || strcmp(argv[1], "--help") == 0) { + kprintf(usage); + return 0; + } + + remove_debug_variable(argv[2]); + + return 0; +} + + +static int +cmd_variables(int argc, char **argv) +{ + static const char* usage = "usage: vars\n" + "Unsets the given variable, if it exists.\n"; + if (argc != 1) { + kprintf(usage); + return 0; + } + + // persistent variables + for (int i = 0; i < kVariableCount; i++) { + Variable& variable = sVariables[i]; + if (variable.IsUsed()) { + kprintf("%16s: %llu (0x%llx)\n", variable.name, variable.value, + variable.value); + } + } + + // temporary variables + for (int i = 0; i < kTemporaryVariableCount; i++) { + Variable& variable = sTemporaryVariables[i]; + if (variable.IsUsed()) { + kprintf("%16s: %llu (0x%llx)\n", variable.name, variable.value, + variable.value); + } + } + + return 0; +} + + +// #pragma mark - kernel public functions + + +bool +is_debug_variable_defined(const char* variableName) +{ + return get_variable(variableName, false) != NULL; +} + + +bool +set_debug_variable(const char* variableName, uint64 value) +{ + if (sTemporaryVariablesObsolete && is_temporary_variable(variableName) + && strcmp(variableName, kCommandReturnValueVariable) != 0) { + remove_all_temporary_debug_variables(); + } + + + if (Variable* variable = get_variable(variableName, true)) { + variable->value = value; + return true; + } + + return false; +} + + +uint64 +get_debug_variable(const char* variableName, uint64 defaultValue) +{ + if (Variable* variable = get_variable(variableName, false)) + return variable->value; + + return defaultValue; +} + + +bool +remove_debug_variable(const char* variableName) +{ + // We don't allow explicit removal of inidividual temporary variables. + // This speeds up removing them all. + if (is_temporary_variable(variableName)) + return false; + + if (Variable* variable = get_variable(variableName, false)) { + variable->Uninit(); + return true; + } + + return false; +} + + +void +remove_all_temporary_debug_variables() +{ + for (int i = 0; i < kTemporaryVariableCount; i++) { + Variable& variable = sTemporaryVariables[i]; + if (!variable.IsUsed()) + return; + + variable.Uninit(); + } + + // always keep the return value variable defined + set_debug_variable(kCommandReturnValueVariable, 0); + + sTemporaryVariablesObsolete = false; +} + + +/*! Schedules all temporary variables for removal. + They will be removed, the next time set_debug_variable() is invoked + for a temporary variable other than the command return value variable. +*/ +void +mark_temporary_debug_variables_obsolete() +{ + sTemporaryVariablesObsolete = true; +} + + +void +debug_variables_init() +{ + // always keep the return value variable defined + set_debug_variable(kCommandReturnValueVariable, 0); + + add_debugger_command("unset", &cmd_unset_variable, + "Unsets the given variable"); + add_debugger_command("vars", &cmd_variables, + "Lists all defined variables with their values"); +} diff --git a/src/system/kernel/debug/debug_variables.h b/src/system/kernel/debug/debug_variables.h new file mode 100644 index 0000000000..8b29907e95 --- /dev/null +++ b/src/system/kernel/debug/debug_variables.h @@ -0,0 +1,26 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_DEBUG_VARIABLES_H +#define _KERNEL_DEBUG_VARIABLES_H + +#include + + +#define MAX_DEBUG_VARIABLE_NAME_LEN 24 + +#ifdef __cplusplus +extern "C" { +#endif + +void mark_temporary_debug_variables_obsolete(); + +void debug_variables_init(); + +#ifdef __cplusplus +} // extern "C" +#endif + + +#endif // _KERNEL_DEBUG_VARIABLES_H