* 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:
@@ -59,7 +59,18 @@ extern void debug_stop_screen_debug_output(void);
|
|||||||
extern void dprintf_no_syslog(const char *format, ...)
|
extern void dprintf_no_syslog(const char *format, ...)
|
||||||
__attribute__ ((format (__printf__, 1, 2)));
|
__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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ UsePrivateHeaders [ FDirName kernel debug ] syslog_daemon ;
|
|||||||
KernelMergeObject kernel_debug.o :
|
KernelMergeObject kernel_debug.o :
|
||||||
blue_screen.cpp
|
blue_screen.cpp
|
||||||
debug.cpp
|
debug.cpp
|
||||||
|
debug_commands.cpp
|
||||||
|
debug_parser.cpp
|
||||||
|
debug_variables.cpp
|
||||||
frame_buffer_console.cpp
|
frame_buffer_console.cpp
|
||||||
gdb.c
|
gdb.c
|
||||||
tracing.cpp
|
tracing.cpp
|
||||||
|
|||||||
@@ -28,26 +28,20 @@
|
|||||||
#include <syslog_daemon.h>
|
#include <syslog_daemon.h>
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <setjmp.h>
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
|
|
||||||
|
#include "debug_commands.h"
|
||||||
|
#include "debug_variables.h"
|
||||||
|
|
||||||
|
|
||||||
static const char* const kKDLPrompt = "kdebug> ";
|
static const char* const kKDLPrompt = "kdebug> ";
|
||||||
|
|
||||||
extern "C" int kgets(char *buffer, int length);
|
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];
|
int dbg_register_file[B_MAX_CPU_COUNT][14];
|
||||||
/* XXXmpetit -- must be made generic */
|
/* XXXmpetit -- must be made generic */
|
||||||
@@ -66,11 +60,6 @@ static struct syslog_message *sSyslogMessage;
|
|||||||
static struct ring_buffer *sSyslogBuffer;
|
static struct ring_buffer *sSyslogBuffer;
|
||||||
static bool sSyslogDropped = false;
|
static bool sSyslogDropped = false;
|
||||||
|
|
||||||
static struct debugger_command *sCommands;
|
|
||||||
|
|
||||||
static jmp_buf sInvokeCommandEnv;
|
|
||||||
static bool sInvokeCommandDirectly = false;
|
|
||||||
|
|
||||||
static const char* sCurrentKernelDebuggerMessage;
|
static const char* sCurrentKernelDebuggerMessage;
|
||||||
|
|
||||||
#define SYSLOG_BUFFER_SIZE 65536
|
#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))
|
#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
|
static void
|
||||||
kputchar(char c)
|
kputchar(char c)
|
||||||
{
|
{
|
||||||
@@ -256,14 +199,14 @@ public:
|
|||||||
tmpChar = *firstSpace;
|
tmpChar = *firstSpace;
|
||||||
*firstSpace = '\0';
|
*firstSpace = '\0';
|
||||||
bool ambiguous;
|
bool ambiguous;
|
||||||
debugger_command* command = find_command(buffer, true, ambiguous);
|
debugger_command* command = find_debugger_command(buffer, true, ambiguous);
|
||||||
*firstSpace = tmpChar;
|
*firstSpace = tmpChar;
|
||||||
|
|
||||||
if (command != NULL) {
|
if (command != NULL) {
|
||||||
kputchar('\n');
|
kputchar('\n');
|
||||||
|
|
||||||
char* args[3] = { NULL, "--help", NULL };
|
char* args[3] = { NULL, "--help", NULL };
|
||||||
invoke_command(command, 2, args);
|
invoke_debugger_command(command, 2, args);
|
||||||
} else {
|
} else {
|
||||||
if (ambiguous)
|
if (ambiguous)
|
||||||
kprintf("\nambiguous command\n");
|
kprintf("\nambiguous command\n");
|
||||||
@@ -281,7 +224,7 @@ public:
|
|||||||
debugger_command* command = NULL;
|
debugger_command* command = NULL;
|
||||||
int32 longestCommonPrefix = 0;
|
int32 longestCommonPrefix = 0;
|
||||||
const char* previousCommandName = NULL;
|
const char* previousCommandName = NULL;
|
||||||
while ((command = next_command(command, buffer, position))
|
while ((command = next_debugger_command(command, buffer, position))
|
||||||
!= NULL) {
|
!= NULL) {
|
||||||
count++;
|
count++;
|
||||||
int32 nameLength = strlen(command->name);
|
int32 nameLength = strlen(command->name);
|
||||||
@@ -312,7 +255,7 @@ public:
|
|||||||
reprintLine = true;
|
reprintLine = true;
|
||||||
} else if (count == 1) {
|
} else if (count == 1) {
|
||||||
// exactly one completion
|
// exactly one completion
|
||||||
command = next_command(NULL, buffer, position);
|
command = next_debugger_command(NULL, buffer, position);
|
||||||
|
|
||||||
// check for sufficient space in the buffer
|
// check for sufficient space in the buffer
|
||||||
int32 neededSpace = longestName - position + 1;
|
int32 neededSpace = longestName - position + 1;
|
||||||
@@ -345,7 +288,7 @@ public:
|
|||||||
int columns = 80 / (longestName + 2);
|
int columns = 80 / (longestName + 2);
|
||||||
debugger_command* command = NULL;
|
debugger_command* command = NULL;
|
||||||
int column = 0;
|
int column = 0;
|
||||||
while ((command = next_command(command, buffer, position))
|
while ((command = next_debugger_command(command, buffer, position))
|
||||||
!= NULL) {
|
!= NULL) {
|
||||||
// spacing
|
// spacing
|
||||||
if (column > 0 && column % columns == 0)
|
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
|
static void
|
||||||
kernel_debugger_loop(void)
|
kernel_debugger_loop(void)
|
||||||
{
|
{
|
||||||
@@ -652,44 +500,43 @@ kernel_debugger_loop(void)
|
|||||||
kprintf("Welcome to Kernel Debugging Land...\n");
|
kprintf("Welcome to Kernel Debugging Land...\n");
|
||||||
kprintf("Running on CPU %ld\n", sDebuggerOnCPU);
|
kprintf("Running on CPU %ld\n", sDebuggerOnCPU);
|
||||||
|
|
||||||
for (;;) {
|
int32 continuableLine = -1;
|
||||||
struct debugger_command *cmd = NULL;
|
// Index of the previous command line, if the command returned
|
||||||
int argc;
|
// B_KDEBUG_CONT, i.e. asked to be repeatable, -1 otherwise.
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
CommandLineEditingHelper editingHelper;
|
CommandLineEditingHelper editingHelper;
|
||||||
kprintf(kKDLPrompt);
|
kprintf(kKDLPrompt);
|
||||||
read_line(sLineBuffer[sCurrentLine], LINE_BUFFER_SIZE, &editingHelper);
|
char* line = sLineBuffer[sCurrentLine];
|
||||||
parse_line(sLineBuffer[sCurrentLine], sArguments, &argc, MAX_ARGS);
|
read_line(line, LINE_BUFFER_SIZE, &editingHelper);
|
||||||
|
|
||||||
// We support calling last executed command again if
|
// check, if the line is empty or whitespace only
|
||||||
// B_KDEDUG_CONT was returned last time, so cmd != NULL
|
bool whiteSpaceOnly = true;
|
||||||
if (argc <= 0 && cmd == NULL)
|
for (int i = 0 ; line[i] != '\0'; i++) {
|
||||||
continue;
|
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();
|
sDebuggerOnCPU = smp_get_current_cpu();
|
||||||
|
|
||||||
bool ambiguous;
|
int rc = evaluate_debug_command(line);
|
||||||
if (argc > 0)
|
|
||||||
cmd = find_command(sArguments[0], true, ambiguous);
|
|
||||||
|
|
||||||
if (cmd == NULL) {
|
if (rc == B_KDEBUG_QUIT)
|
||||||
if (ambiguous) {
|
break; // okay, exit now.
|
||||||
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)
|
// If the command is continuable, remember the current line index.
|
||||||
break; // okay, exit now.
|
continuableLine = (rc == B_KDEBUG_CONT ? sCurrentLine : -1);
|
||||||
|
|
||||||
if (rc != B_KDEBUG_CONT)
|
|
||||||
cmd = NULL; // forget last command executed...
|
|
||||||
}
|
|
||||||
|
|
||||||
if (++sCurrentLine >= HISTORY_SIZE)
|
if (++sCurrentLine >= HISTORY_SIZE)
|
||||||
sCurrentLine = 0;
|
sCurrentLine = 0;
|
||||||
@@ -725,7 +572,7 @@ cmd_help(int argc, char **argv)
|
|||||||
bool ambiguous;
|
bool ambiguous;
|
||||||
|
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
specified = find_command(argv[1], false, ambiguous);
|
specified = find_debugger_command(argv[1], false, ambiguous);
|
||||||
if (specified == NULL) {
|
if (specified == NULL) {
|
||||||
start = argv[1];
|
start = argv[1];
|
||||||
startLength = strlen(start);
|
startLength = strlen(start);
|
||||||
@@ -740,7 +587,8 @@ cmd_help(int argc, char **argv)
|
|||||||
else
|
else
|
||||||
kprintf("debugger commands:\n");
|
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)
|
if (specified && command->func != specified->func)
|
||||||
continue;
|
continue;
|
||||||
if (start != NULL && strncmp(start, command->name, startLength))
|
if (start != NULL && strncmp(start, command->name, startLength))
|
||||||
@@ -771,6 +619,25 @@ cmd_dump_kdl_message(int argc, char **argv)
|
|||||||
return 0;
|
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
|
static status_t
|
||||||
syslog_sender(void *data)
|
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("continue", &cmd_continue, "Leave kernel debugger");
|
||||||
add_debugger_command("message", &cmd_dump_kdl_message,
|
add_debugger_command("message", &cmd_dump_kdl_message,
|
||||||
"Reprint the message printed when entering KDL");
|
"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);
|
frame_buffer_console_init(args);
|
||||||
arch_debug_console_init_settings(args);
|
arch_debug_console_init_settings(args);
|
||||||
tracing_init();
|
tracing_init();
|
||||||
@@ -1089,75 +959,11 @@ debug_init_post_modules(struct kernel_args *args)
|
|||||||
// #pragma mark - public API
|
// #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
|
uint32
|
||||||
parse_expression(const char *expression)
|
parse_expression(const char *expression)
|
||||||
{
|
{
|
||||||
// TODO: Implement expression parser (cf. BeBook).
|
uint64 result;
|
||||||
return strtoul(expression, NULL, 0);
|
return (evaluate_debug_expression(expression, &result, true) ? result : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1207,27 +1013,8 @@ kernel_debugger(const char *message)
|
|||||||
|
|
||||||
sCurrentKernelDebuggerMessage = message;
|
sCurrentKernelDebuggerMessage = message;
|
||||||
|
|
||||||
// bubble sort the commands
|
// sort the commands
|
||||||
debugger_command* stopCommand = NULL;
|
sort_debugger_commands();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
kernel_debugger_loop();
|
kernel_debugger_loop();
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, Ingo Weinhold, [email protected]
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef _KERNEL_DEBUG_COMMANDS_H
|
||||||
|
#define _KERNEL_DEBUG_COMMANDS_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
@@ -0,0 +1,929 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, Ingo Weinhold, [email protected]
|
||||||
|
* Copyright 2006, Stephan Aßmus, [email protected]
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <debug.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,239 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, Ingo Weinhold, [email protected]
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "debug_variables.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <KernelExport.h>
|
||||||
|
|
||||||
|
#include <debug.h>
|
||||||
|
|
||||||
|
|
||||||
|
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 <variable>\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");
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, Ingo Weinhold, [email protected]
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef _KERNEL_DEBUG_VARIABLES_H
|
||||||
|
#define _KERNEL_DEBUG_VARIABLES_H
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
|
||||||
|
#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
|
||||||
Reference in New Issue
Block a user