diff --git a/headers/private/kernel/debug.h b/headers/private/kernel/debug.h index 768c410f50..33d08c2d53 100644 --- a/headers/private/kernel/debug.h +++ b/headers/private/kernel/debug.h @@ -42,7 +42,12 @@ # define ASSERT_PRINT(x, format...) do { } while(0) #endif +// command return value +#define B_KDEBUG_ERROR 4 + +// command flags #define B_KDEBUG_DONT_PARSE_ARGUMENTS (0x01) +#define B_KDEBUG_PIPE_FINAL_RERUN (0x02) struct debugger_module_info { module_info info; @@ -74,6 +79,9 @@ extern bool debug_screen_output_enabled(void); extern void debug_stop_screen_debug_output(void); extern void kputs(const char *string); +extern void kputs_unfiltered(const char *string); +extern void kprintf_unfiltered(const char *format, ...) + __attribute__ ((format (__printf__, 1, 2))); extern void dprintf_no_syslog(const char *format, ...) __attribute__ ((format (__printf__, 1, 2))); diff --git a/src/system/kernel/debug/Jamfile b/src/system/kernel/debug/Jamfile index 963b53fbbf..c6b6ad1c96 100644 --- a/src/system/kernel/debug/Jamfile +++ b/src/system/kernel/debug/Jamfile @@ -6,6 +6,7 @@ UsePrivateHeaders [ FDirName graphics vesa ] ; KernelMergeObject kernel_debug.o : blue_screen.cpp debug.cpp + debug_builtin_commands.cpp debug_commands.cpp debug_paranoia.cpp debug_parser.cpp diff --git a/src/system/kernel/debug/blue_screen.cpp b/src/system/kernel/debug/blue_screen.cpp index ae6920e8dc..8b6a74f863 100644 --- a/src/system/kernel/debug/blue_screen.cpp +++ b/src/system/kernel/debug/blue_screen.cpp @@ -121,7 +121,7 @@ next_line(void) // Use the paging mechanism: either, we're in the debugger, and a // command is being executed, or we're currently showing boot debug // output - const char *text = "Press key to continue, Q to quit"; + const char *text = "Press key to continue, Q to quit, A to abort"; int32 length = strlen(text); if (sScreen.x + length > sScreen.columns) { // make sure we don't overwrite too much @@ -136,8 +136,13 @@ next_line(void) } char c = blue_screen_getchar(); - if (c == 'q') + if (c == 'q') { sScreen.ignore_output = true; + } else if (c == 'a') { + abort_debugger_command(); + // should not return + sScreen.ignore_output = true; + } // remove on screen text again sModule->fill_glyph(sScreen.columns - length, sScreen.y, length, diff --git a/src/system/kernel/debug/debug.cpp b/src/system/kernel/debug/debug.cpp index 375d949736..0691dd1573 100644 --- a/src/system/kernel/debug/debug.cpp +++ b/src/system/kernel/debug/debug.cpp @@ -9,7 +9,6 @@ /*! This file contains the debugger and debug output facilities */ #include "blue_screen.h" -#include "gdb.h" #include #include @@ -35,7 +34,9 @@ #include #include +#include "debug_builtin_commands.h" #include "debug_commands.h" +#include "debug_output_filter.h" #include "debug_variables.h" @@ -67,6 +68,8 @@ static const char* sCurrentKernelDebuggerMessage; #define OUTPUT_BUFFER_SIZE 1024 static char sOutputBuffer[OUTPUT_BUFFER_SIZE]; static char sLastOutputBuffer[OUTPUT_BUFFER_SIZE]; +static DebugOutputFilter* sDebugOutputFilter = NULL; +DefaultDebugOutputFilter gDefaultDebugOutputFilter; static void flush_pending_repeats(void); static void check_pending_repeats(void *data, int iter); @@ -84,22 +87,80 @@ static const uint32 kMaxDebuggerModules = sizeof(sDebuggerModules) static char sLineBuffer[HISTORY_SIZE][LINE_BUFFER_SIZE] = { "", }; static char sParseLine[LINE_BUFFER_SIZE]; -static char sFilter[64]; static int32 sCurrentLine = 0; #define distance(a, b) ((a) < (b) ? (b) - (a) : (a) - (b)) +// #pragma mark - DebugOutputFilter + + +DebugOutputFilter::DebugOutputFilter() +{ +} + + +DebugOutputFilter::~DebugOutputFilter() +{ +} + + +void +DebugOutputFilter::PrintString(const char* string) +{ +} + + +void +DebugOutputFilter::Print(const char* format, va_list args) +{ +} + + +void +DefaultDebugOutputFilter::PrintString(const char* string) +{ + if (sSerialDebugEnabled) + arch_debug_serial_puts(string); + if (sBlueScreenEnabled || sDebugScreenEnabled) + blue_screen_puts(string); + + for (uint32 i = 0; sSerialDebugEnabled && i < kMaxDebuggerModules; i++) { + if (sDebuggerModules[i] && sDebuggerModules[i]->debugger_puts) + sDebuggerModules[i]->debugger_puts(string, strlen(string)); + } +} + + +void +DefaultDebugOutputFilter::Print(const char* format, va_list args) +{ + vsnprintf(sOutputBuffer, OUTPUT_BUFFER_SIZE, format, args); + flush_pending_repeats(); + PrintString(sOutputBuffer); +} + + +// #pragma mark - + + +DebugOutputFilter* +set_debug_output_filter(DebugOutputFilter* filter) +{ + DebugOutputFilter* oldFilter = sDebugOutputFilter; + sDebugOutputFilter = filter; + return oldFilter; +} + + static void kputchar(char c) { - uint32 i; - if (sSerialDebugEnabled) arch_debug_serial_putchar(c); if (sBlueScreenEnabled || sDebugScreenEnabled) blue_screen_putchar(c); - for (i = 0; sSerialDebugEnabled && i < kMaxDebuggerModules; i++) + for (uint32 i = 0; sSerialDebugEnabled && i < kMaxDebuggerModules; i++) if (sDebuggerModules[i] && sDebuggerModules[i]->debugger_puts) sDebuggerModules[i]->debugger_puts(&c, sizeof(c)); } @@ -108,15 +169,15 @@ kputchar(char c) void kputs(const char *s) { - uint32 i; + if (sDebugOutputFilter != NULL) + sDebugOutputFilter->PrintString(s); +} - if (sSerialDebugEnabled) - arch_debug_serial_puts(s); - if (sBlueScreenEnabled || sDebugScreenEnabled) - blue_screen_puts(s); - for (i = 0; sSerialDebugEnabled && i < kMaxDebuggerModules; i++) - if (sDebuggerModules[i] && sDebuggerModules[i]->debugger_puts) - sDebuggerModules[i]->debugger_puts(s, strlen(s)); + +void +kputs_unfiltered(const char *s) +{ + gDefaultDebugOutputFilter.PrintString(s); } @@ -630,124 +691,17 @@ kernel_debugger_loop(void) } -static int -cmd_reboot(int argc, char **argv) -{ - arch_cpu_shutdown(true); - return 0; - // I'll be really suprised if this line ever runs! ;-) -} - - -static int -cmd_shutdown(int argc, char **argv) -{ - arch_cpu_shutdown(false); - return 0; -} - - -static int -cmd_help(int argc, char **argv) -{ - debugger_command *command, *specified = NULL; - const char *start = NULL; - int32 startLength = 0; - bool ambiguous; - - if (argc > 1) { - specified = find_debugger_command(argv[1], false, ambiguous); - if (specified == NULL) { - start = argv[1]; - startLength = strlen(start); - } - } - - if (specified != NULL) { - // only print out the help of the specified command (and all of its aliases) - kprintf("debugger command for \"%s\" and aliases:\n", specified->name); - } else if (start != NULL) - kprintf("debugger commands starting with \"%s\":\n", start); - else - kprintf("debugger commands:\n"); - - 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)) - continue; - - kprintf(" %-20s\t\t%s\n", command->name, command->description ? command->description : "-"); - } - - return 0; -} - - -static int -cmd_continue(int argc, char **argv) -{ - return B_KDEBUG_QUIT; -} - - static int cmd_dump_kdl_message(int argc, char **argv) { if (sCurrentKernelDebuggerMessage) { kputs(sCurrentKernelDebuggerMessage); - kputchar('\n'); + kputs("\n"); } return 0; } -static int -cmd_expr(int argc, char **argv) -{ - if (argc != 2) { - print_debugger_command_usage(argv[0]); - 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 int -cmd_filter(int argc, char **argv) -{ - if (argc != 2) { - sFilter[0] = '\0'; - return 0; - } - - strlcpy(sFilter, argv[1], sizeof(sFilter)); - return 0; -} - - -static int -cmd_error(int argc, char **argv) -{ - if (argc != 2) { - print_debugger_command_usage(argv[0]); - return 0; - } - - int32 error = parse_expression(argv[1]); - kprintf("error 0x%lx: %s\n", error, strerror(error)); - - return 0; -} - static status_t syslog_sender(void *data) @@ -1000,6 +954,8 @@ debug_early_boot_message(const char *string) status_t debug_init(kernel_args *args) { + new(&gDefaultDebugOutputFilter) DefaultDebugOutputFilter; + debug_paranoia_init(); return arch_debug_console_init(args); } @@ -1008,43 +964,12 @@ debug_init(kernel_args *args) status_t debug_init_post_vm(kernel_args *args) { - add_debugger_command_etc("help", &cmd_help, "List all debugger commands", - "[name]\n" - "Lists all debugger commands or those starting with \"name\".\n", 0); - add_debugger_command_etc("reboot", &cmd_reboot, "Reboot the system", - "\n" - "Reboots the system.\n", 0); - add_debugger_command_etc("shutdown", &cmd_shutdown, "Shut down the system", - "\n" - "Shuts down the system.\n", 0); - add_debugger_command_etc("gdb", &cmd_gdb, "Connect to remote gdb", - "\n" - "Connects to a remote gdb connected to the serial port.\n", 0); - add_debugger_command_etc("continue", &cmd_continue, "Leave kernel debugger", - "\n" - "Leaves kernel debugger.\n", 0); - add_debugger_command_alias("exit", "continue", "Same as \"continue\""); - add_debugger_command_alias("es", "continue", "Same as \"continue\""); add_debugger_command_etc("message", &cmd_dump_kdl_message, "Reprint the message printed when entering KDL", "\n" "Reprints the message printed when entering KDL.\n", 0); - add_debugger_command_etc("expr", &cmd_expr, - "Evaluates the given expression and prints the result", - "\n" - "Evaluates the given expression and prints the result.\n", - B_KDEBUG_DONT_PARSE_ARGUMENTS); - add_debugger_command_etc("filter", &cmd_filter, - "Filters output of all debugger commands", - "\n" - "Filters out all debug output of commands that does not match the\n" - "specified pattern. If no pattern is given, it is removed\n", 0); - add_debugger_command_etc("error", &cmd_error, - "Prints a human-readable description for an error code", - "\n" - "Prints a human-readable description for the given numeric error\n" - "code.\n" - " - The numeric error code.\n", 0); + + debug_builtin_commands_init(); debug_variables_init(); frame_buffer_console_init(args); @@ -1181,6 +1106,8 @@ kernel_debugger(const char *message) sBlueScreenEnabled = true; } + sDebugOutputFilter = &gDefaultDebugOutputFilter; + if (message) kprintf("PANIC: %s\n", message); @@ -1196,6 +1123,8 @@ kernel_debugger(const char *message) call_modules_hook(false); set_dprintf_enabled(dprintfState); + sDebugOutputFilter = NULL; + sBlueScreenEnabled = false; atomic_add(&inDebugger, -1); restore_interrupts(state); @@ -1354,21 +1283,22 @@ dprintf_no_syslog(const char *format, ...) void kprintf(const char *format, ...) { - va_list args; - - // ToDo: don't print anything if the debugger is not running! - - va_start(args, format); - vsnprintf(sOutputBuffer, OUTPUT_BUFFER_SIZE, format, args); - va_end(args); - - if (in_command_invocation() && sFilter[0]) { - if (strstr(sOutputBuffer, sFilter) == NULL) - return; + if (sDebugOutputFilter != NULL) { + va_list args; + va_start(args, format); + sDebugOutputFilter->Print(format, args); + va_end(args); } +} - flush_pending_repeats(); - kputs(sOutputBuffer); + +void +kprintf_unfiltered(const char *format, ...) +{ + va_list args; + va_start(args, format); + gDefaultDebugOutputFilter.Print(format, args); + va_end(args); } diff --git a/src/system/kernel/debug/debug_builtin_commands.cpp b/src/system/kernel/debug/debug_builtin_commands.cpp new file mode 100644 index 0000000000..04d615c90a --- /dev/null +++ b/src/system/kernel/debug/debug_builtin_commands.cpp @@ -0,0 +1,318 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de + * Copyright 2002-2008, 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_builtin_commands.h" + +#include +#include +#include + +#include "debug_commands.h" +#include "gdb.h" + + +static int +cmd_reboot(int argc, char **argv) +{ + arch_cpu_shutdown(true); + return 0; + // I'll be really suprised if this line ever runs! ;-) +} + + +static int +cmd_shutdown(int argc, char **argv) +{ + arch_cpu_shutdown(false); + return 0; +} + + +static int +cmd_help(int argc, char **argv) +{ + debugger_command *command, *specified = NULL; + const char *start = NULL; + int32 startLength = 0; + bool ambiguous; + + if (argc > 1) { + specified = find_debugger_command(argv[1], false, ambiguous); + if (specified == NULL) { + start = argv[1]; + startLength = strlen(start); + } + } + + if (specified != NULL) { + // only print out the help of the specified command (and all of its aliases) + kprintf("debugger command for \"%s\" and aliases:\n", specified->name); + } else if (start != NULL) + kprintf("debugger commands starting with \"%s\":\n", start); + else + kprintf("debugger commands:\n"); + + 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)) + continue; + + kprintf(" %-20s\t\t%s\n", command->name, command->description ? command->description : "-"); + } + + return 0; +} + + +static int +cmd_continue(int argc, char **argv) +{ + return B_KDEBUG_QUIT; +} + + +static int +cmd_expr(int argc, char **argv) +{ + if (argc != 2) { + print_debugger_command_usage(argv[0]); + 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 int +cmd_error(int argc, char **argv) +{ + if (argc != 2) { + print_debugger_command_usage(argv[0]); + return 0; + } + + int32 error = parse_expression(argv[1]); + kprintf("error 0x%lx: %s\n", error, strerror(error)); + + return 0; +} + + +static int +cmd_head(int argc, char** argv) +{ + debugger_command_pipe_segment* segment + = get_current_debugger_command_pipe_segment(); + if (segment == NULL) { + kprintf_unfiltered("%s can only be run as part of a pipe!\n", argv[0]); + return B_KDEBUG_ERROR; + } + + struct user_data { + uint64 max_lines; + uint64 lines; + }; + user_data* userData = (user_data*)segment->user_data; + + if (segment->invocations == 0) { + if (argc != 3) { + print_debugger_command_usage(argv[0]); + return B_KDEBUG_ERROR; + } + + if (!evaluate_debug_expression(argv[1], &userData->max_lines, false)) + return B_KDEBUG_ERROR; + userData->lines = 0; + } + + if (++userData->lines <= userData->max_lines) { + kputs(argv[2]); + kputs("\n"); + } + + return 0; +} + + +static int +cmd_grep(int argc, char** argv) +{ + bool caseSensitive = true; + bool inverseMatch = false; + + int argi = 1; + for (; argi < argc; argi++) { + const char* arg = argv[argi]; + if (arg[0] != '-') + break; + + for (int32 i = 1; arg[i] != '\0'; i++) { + if (arg[i] == 'i') { + caseSensitive = false; + } else if (arg[i] == 'v') { + inverseMatch = true; + } else { + print_debugger_command_usage(argv[0]); + return B_KDEBUG_ERROR; + } + } + } + + if (argc - argi != 2) { + print_debugger_command_usage(argv[0]); + return B_KDEBUG_ERROR; + } + + const char* pattern = argv[argi++]; + const char* line = argv[argi++]; + + bool match; + if (caseSensitive) { + match = strstr(line, pattern) != NULL; + } else { + match = false; + int32 lineLen = strlen(line); + int32 patternLen = strlen(pattern); + for (int32 i = 0; i <= lineLen - patternLen; i++) { + // This is rather slow, but should be OK for our purposes. + if (strncasecmp(line + i, pattern, patternLen) == 0) { + match = true; + break; + } + } + } + + if (match != inverseMatch) { + kputs(line); + kputs("\n"); + } + + return 0; +} + + +static int +cmd_wc(int argc, char** argv) +{ + debugger_command_pipe_segment* segment + = get_current_debugger_command_pipe_segment(); + if (segment == NULL) { + kprintf_unfiltered("%s can only be run as part of a pipe!\n", argv[0]); + return B_KDEBUG_ERROR; + } + + struct user_data { + uint64 lines; + uint64 words; + uint64 chars; + }; + user_data* userData = (user_data*)segment->user_data; + + if (segment->invocations == 0) { + if (argc != 2) { + print_debugger_command_usage(argv[0]); + return B_KDEBUG_ERROR; + } + + userData->lines = 0; + userData->words = 0; + userData->chars = 0; + } + + const char* line = argv[1]; + if (line == NULL) { + // last run -- print results + kprintf("%10lld %10lld %10lld\n", userData->lines, userData->words, + userData->chars); + return 0; + } + + userData->lines++; + userData->chars++; + // newline + + // count words and chars in this line + bool inWord = false; + for (; *line != '\0'; line++) { + userData->chars++; + if ((isspace(*line) != 0) == inWord) { + inWord = !inWord; + if (inWord) + userData->words++; + } + } + + return 0; +} + + +// #pragma mark - + + +void +debug_builtin_commands_init() +{ + add_debugger_command_etc("help", &cmd_help, "List all debugger commands", + "[name]\n" + "Lists all debugger commands or those starting with \"name\".\n", 0); + add_debugger_command_etc("reboot", &cmd_reboot, "Reboot the system", + "\n" + "Reboots the system.\n", 0); + add_debugger_command_etc("shutdown", &cmd_shutdown, "Shut down the system", + "\n" + "Shuts down the system.\n", 0); + add_debugger_command_etc("gdb", &cmd_gdb, "Connect to remote gdb", + "\n" + "Connects to a remote gdb connected to the serial port.\n", 0); + add_debugger_command_etc("continue", &cmd_continue, "Leave kernel debugger", + "\n" + "Leaves kernel debugger.\n", 0); + add_debugger_command_alias("exit", "continue", "Same as \"continue\""); + add_debugger_command_alias("es", "continue", "Same as \"continue\""); + add_debugger_command_etc("expr", &cmd_expr, + "Evaluates the given expression and prints the result", + "\n" + "Evaluates the given expression and prints the result.\n", + B_KDEBUG_DONT_PARSE_ARGUMENTS); + add_debugger_command_etc("error", &cmd_error, + "Prints a human-readable description for an error code", + "\n" + "Prints a human-readable description for the given numeric error\n" + "code.\n" + " - The numeric error code.\n", 0); + add_debugger_command_etc("head", &cmd_head, + "Prints only the first lines of output from another command", + "\n" + "Should be used in a command pipe. It prints only the first\n" + " lines of output from the previous command in the pipe and\n" + "silently discards the rest of the output.\n", 0); + add_debugger_command_etc("grep", &cmd_grep, + "Filters output from another command", + "[ -i ] [ -v ] \n" + "Should be used in a command pipe. It filters all output from the\n" + "previous command in the pipe according to the given pattern.\n" + "When \"-v\" is specified, only those lines are printed that don't\n" + "match the given pattern, otherwise only those that do match. When\n" + "\"-i\" is specified, the pattern is matched case insensitive,\n" + "otherwise case sensitive.\n", 0); + add_debugger_command_etc("wc", &cmd_wc, + "Counts the lines, words, and characters of another command's output", + "\n" + "Should be used in a command pipe. It prints how many lines, words,\n" + "and characters the output of the previous command consists of.\n", + B_KDEBUG_PIPE_FINAL_RERUN); +} diff --git a/src/system/kernel/debug/debug_builtin_commands.h b/src/system/kernel/debug/debug_builtin_commands.h new file mode 100644 index 0000000000..d99f16e78f --- /dev/null +++ b/src/system/kernel/debug/debug_builtin_commands.h @@ -0,0 +1,20 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_DEBUG_BUILTIN_COMMANDS_H +#define _KERNEL_DEBUG_BUILTIN_COMMANDS_H + + +#ifdef __cplusplus +extern "C" { +#endif + +void debug_builtin_commands_init(); + +#ifdef __cplusplus +} // extern "C" +#endif + + +#endif // _KERNEL_DEBUG_BUILTIN_COMMANDS_H diff --git a/src/system/kernel/debug/debug_commands.cpp b/src/system/kernel/debug/debug_commands.cpp index b8288dd9e2..414475c01c 100644 --- a/src/system/kernel/debug/debug_commands.cpp +++ b/src/system/kernel/debug/debug_commands.cpp @@ -10,6 +10,7 @@ #include "debug_commands.h" #include +#include #include #include @@ -19,16 +20,172 @@ #include #include +#include "debug_output_filter.h" #include "debug_variables.h" +#define INVOKE_COMMAND_FAULT 1 +#define INVOKE_COMMAND_ERROR 2 + +static const int32 kMaxInvokeCommandDepth = 5; +static const int32 kOutputBufferSize = 1024; + static spinlock sSpinlock = 0; static struct debugger_command *sCommands; -static jmp_buf sInvokeCommandEnv; +static jmp_buf sInvokeCommandEnv[kMaxInvokeCommandDepth]; +static int32 sInvokeCommandLevel = 0; static bool sInvokeCommandDirectly = false; static bool sInCommand = false; +static char sOutputBuffers[MAX_DEBUGGER_COMMAND_PIPE_LENGTH][kOutputBufferSize]; +static debugger_command_pipe* sCurrentPipe; +static int32 sCurrentPipeSegment; + + +static int invoke_pipe_segment(debugger_command_pipe* pipe, int32 index, + char* argument); + + +class PipeDebugOutputFilter : public DebugOutputFilter { +public: + PipeDebugOutputFilter() + { + } + + PipeDebugOutputFilter(debugger_command_pipe* pipe, int32 segment, + char* buffer, size_t bufferSize) + : + fPipe(pipe), + fSegment(segment), + fBuffer(buffer), + fBufferCapacity(bufferSize - 1), + fBufferSize(0) + { + } + + virtual void PrintString(const char* string) + { + if (fPipe->broken) + return; + + size_t size = strlen(string); + while (const char* newLine = strchr(string, '\n')) { + size_t length = newLine - string; + _Append(string, length); + + // invoke command + fBuffer[fBufferSize] = '\0'; + invoke_pipe_segment(fPipe, fSegment + 1, fBuffer); + + fBufferSize = 0; + + string = newLine + 1; + size -= length + 1; + } + + _Append(string, size); + + if (fBufferSize == fBufferCapacity) { + // buffer is full, but contains no newline -- execute anyway + invoke_pipe_segment(fPipe, fSegment + 1, fBuffer); + fBufferSize = 0; + } + } + + virtual void Print(const char* format, va_list args) + { + if (fPipe->broken) + return; + + // print directly into the buffer + fBufferSize += vsnprintf(fBuffer + fBufferSize, + fBufferCapacity - fBufferSize, format, args); + + // execute every complete line + fBuffer[fBufferSize] = '\0'; + char* line = fBuffer; + + while (char* newLine = strchr(line, '\n')) { + // invoke command + *newLine = '\0'; + invoke_pipe_segment(fPipe, fSegment + 1, line); + + line = newLine + 1; + } + + size_t left = fBuffer + fBufferSize - line; + + if (left == fBufferCapacity) { + // buffer is full, but contains no newline -- execute anyway + invoke_pipe_segment(fPipe, fSegment + 1, fBuffer); + left = 0; + } + + if (left > 0) + memmove(fBuffer, line, left); + + fBufferSize = left; + } + +private: + void _Append(const char* string, size_t length) + { + size_t toAppend = min_c(length, fBufferCapacity - fBufferSize); + memcpy(fBuffer + fBufferSize, string, toAppend); + fBufferSize += length; + } + +private: + debugger_command_pipe* fPipe; + int32 fSegment; + char* fBuffer; + size_t fBufferCapacity; + size_t fBufferSize; +}; + + +static PipeDebugOutputFilter sPipeOutputFilters[ + MAX_DEBUGGER_COMMAND_PIPE_LENGTH - 1]; + + +static int +invoke_pipe_segment(debugger_command_pipe* pipe, int32 index, char* argument) +{ + // set debug output + DebugOutputFilter* oldFilter = set_debug_output_filter( + index == pipe->segment_count - 1 + ? &gDefaultDebugOutputFilter : &sPipeOutputFilters[index]); + + // set last command argument + debugger_command_pipe_segment& segment = pipe->segments[index]; + if (index > 0) + segment.argv[segment.argc - 1] = argument; + + // invoke command + int32 oldIndex = sCurrentPipeSegment; + sCurrentPipeSegment = index; + + int result = invoke_debugger_command(segment.command, segment.argc, + segment.argv); + segment.invocations++; + + sCurrentPipeSegment = oldIndex; + + // reset debug output + set_debug_output_filter(oldFilter); + + if (result == B_KDEBUG_ERROR) { + pipe->broken = true; + + // Abort the previous pipe segment execution. The complete pipe is + // aborted iteratively this way. + if (index > 0) + abort_debugger_command(); + } + + return result; +} debugger_command* @@ -98,9 +255,10 @@ invoke_debugger_command(struct debugger_command *command, int argc, char** argv) // intercept invocations with "--help" and directly print the usage text // If we know the command's usage text, intercept "--help" invocations // and print it directly. - if (argc == 2 && strcmp(argv[1], "--help") == 0 && command->usage != NULL) { - kprintf("usage: %s ", command->name); - kputs(command->usage); + if (argc == 2 && argv[1] != NULL && strcmp(argv[1], "--help") == 0 + && command->usage != NULL) { + kprintf_unfiltered("usage: %s ", command->name); + kputs_unfiltered(command->usage); return 0; } @@ -117,30 +275,96 @@ invoke_debugger_command(struct debugger_command *command, int argc, char** argv) sInCommand = true; - 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; + switch (setjmp(sInvokeCommandEnv[sInvokeCommandLevel++])) { + case 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); + result = command->func(argc, argv); - thread->fault_handler = oldFaultHandler; - sInCommand = false; - return result; + thread->fault_handler = oldFaultHandler; + sInvokeCommandLevel--; + sInCommand = false; + return result; error: - longjmp(sInvokeCommandEnv, 1); - // jump into the else branch - } else { - kprintf("\n[*** READ/WRITE FAULT ***]\n"); + // jump to INVOKE_COMMAND_FAULT case, cleaning up the stack + longjmp(sInvokeCommandEnv[--sInvokeCommandLevel], + INVOKE_COMMAND_FAULT); + + case INVOKE_COMMAND_FAULT: + kprintf_unfiltered("\n[*** READ/WRITE FAULT ***]\n"); + break; + case INVOKE_COMMAND_ERROR: + // command aborted (no page fault) + break; } thread->fault_handler = oldFaultHandler; sInCommand = false; - return 0; + return B_KDEBUG_ERROR; +} + + +/*! Aborts the currently executed debugger command (in fact the complete pipe), + unless direct command invocation has been set. If successful, the function + won't return. +*/ +void +abort_debugger_command() +{ + if (!sInvokeCommandDirectly && sInvokeCommandLevel > 0) { + longjmp(sInvokeCommandEnv[--sInvokeCommandLevel], + INVOKE_COMMAND_ERROR); + } +} + + +int +invoke_debugger_command_pipe(debugger_command_pipe* pipe) +{ + debugger_command_pipe* oldPipe = sCurrentPipe; + sCurrentPipe = pipe; + + // prepare outputs + // TODO: If a pipe is invoked in a pipe, outputs will clash. + int32 segments = pipe->segment_count; + for (int32 i = 0; i < segments - 1; i++) { + new(&sPipeOutputFilters[i]) PipeDebugOutputFilter(pipe, i, + sOutputBuffers[i], kOutputBufferSize); + } + + int result = invoke_pipe_segment(pipe, 0, NULL); + + // perform final rerun for all commands that want it + for (int32 i = 1; result != B_KDEBUG_ERROR && i < segments; i++) { + debugger_command_pipe_segment& segment = pipe->segments[i]; + if ((segment.command->flags & B_KDEBUG_PIPE_FINAL_RERUN) != 0) + result = invoke_pipe_segment(pipe, i, NULL); + } + + sCurrentPipe = oldPipe; + + return result; +} + + +debugger_command_pipe* +get_current_debugger_command_pipe() +{ + return sCurrentPipe; +} + + +debugger_command_pipe_segment* +get_current_debugger_command_pipe_segment() +{ + return sCurrentPipe != NULL + ? &sCurrentPipe->segments[sCurrentPipeSegment] : NULL; } @@ -234,8 +458,8 @@ print_debugger_command_usage(const char* commandName) // directly print the usage text, if we know it, otherwise invoke the // command with "--help" if (command->usage != NULL) { - kprintf("usage: %s ", command->name); - kputs(command->usage); + kprintf_unfiltered("usage: %s ", command->name); + kputs_unfiltered(command->usage); } else { char* args[3] = { NULL, "--help", NULL }; invoke_debugger_command(command, 2, args); diff --git a/src/system/kernel/debug/debug_commands.h b/src/system/kernel/debug/debug_commands.h index d7d8497f34..15f6e00809 100644 --- a/src/system/kernel/debug/debug_commands.h +++ b/src/system/kernel/debug/debug_commands.h @@ -9,14 +9,33 @@ #include -struct debugger_command { +#define MAX_DEBUGGER_COMMAND_PIPE_LENGTH 8 + + +typedef struct debugger_command { struct debugger_command* next; int (*func)(int, char **); const char* name; const char* description; const char* usage; uint32 flags; -}; +} debugger_command; + +typedef struct debugger_command_pipe_segment { + int32 index; + debugger_command* command; + int argc; + char** argv; + int32 invocations; + uint32 user_data[8]; // can be used by the command +} debugger_command_pipe_segment; + +typedef struct debugger_command_pipe { + int32 segment_count; + debugger_command_pipe_segment segments[MAX_DEBUGGER_COMMAND_PIPE_LENGTH]; + bool broken; +} debugger_command_pipe; + #ifdef __cplusplus extern "C" { @@ -30,6 +49,11 @@ bool in_command_invocation(void); int invoke_debugger_command(struct debugger_command *command, int argc, char** argv); +void abort_debugger_command(); + +int invoke_debugger_command_pipe(debugger_command_pipe* pipe); +debugger_command_pipe* get_current_debugger_command_pipe(); +debugger_command_pipe_segment* get_current_debugger_command_pipe_segment(); debugger_command* get_debugger_commands(); void sort_debugger_commands(); diff --git a/src/system/kernel/debug/debug_output_filter.h b/src/system/kernel/debug/debug_output_filter.h new file mode 100644 index 0000000000..7a7ede25fc --- /dev/null +++ b/src/system/kernel/debug/debug_output_filter.h @@ -0,0 +1,35 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_DEBUG_OUTPUT_FILTER_H +#define _KERNEL_DEBUG_OUTPUT_FILTER_H + +#include + +#include + + +class DebugOutputFilter { +public: + DebugOutputFilter(); + virtual ~DebugOutputFilter(); + + virtual void PrintString(const char* string); + virtual void Print(const char* format, va_list args); +}; + +class DefaultDebugOutputFilter : public DebugOutputFilter { +public: + virtual void PrintString(const char* string); + virtual void Print(const char* format, va_list args); +}; + + +extern DefaultDebugOutputFilter gDefaultDebugOutputFilter; + + +DebugOutputFilter* set_debug_output_filter(DebugOutputFilter* filter); + + +#endif // _KERNEL_DEBUG_OUTPUT_FILTER_H diff --git a/src/system/kernel/debug/debug_parser.cpp b/src/system/kernel/debug/debug_parser.cpp index 5a69a5ae38..4c5a232610 100644 --- a/src/system/kernel/debug/debug_parser.cpp +++ b/src/system/kernel/debug/debug_parser.cpp @@ -21,7 +21,7 @@ /* Grammar: - commandLine := command | ( "(" expression ")" ) + commandLine := commandPipe | ( "(" expression ")" ) expression := term | assignment assignment := variable ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" ) expression @@ -33,6 +33,7 @@ variable := identifier identifier := ( "_" | "a" - "z" | "A" - "Z" ) ( "_" | "a" - "z" | "A" - "Z" | "0" - "9" )* + commandPipe := command ( "|" command )* command := identifier argument* argument := ( "(" expression ")" ) | ( "[" commandLine "]" ) | unquotedString | quotedString @@ -87,6 +88,8 @@ enum { TOKEN_OPENING_BRACE = '{', TOKEN_CLOSING_BRACE = '}', + TOKEN_PIPE = '|', + TOKEN_STRING = '"', TOKEN_UNKNOWN = '?', TOKEN_NONE = ' ', @@ -125,8 +128,9 @@ 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); + kprintf_unfiltered("parse_exception(): No jump buffer!\n"); + kprintf_unfiltered("exception: \"%s\", position: %lu\n", message, + position); return; } @@ -345,6 +349,7 @@ public: case ')': case '[': case ']': + case '|': fCurrentToken.SetTo(fCurrentChar, 1, _CurrentPos(), *fCurrentChar); fCurrentChar++; @@ -470,7 +475,9 @@ class ExpressionParser { private: uint64 _ParseExpression(); - uint64 _ParseCommand(int& returnCode); + uint64 _ParseCommandPipe(int& returnCode); + void _ParseCommand( + debugger_command_pipe_segment& segment); bool _ParseArgument(int& argc, char** argv); void _GetUnparsedArgument(int& argc, char** argv); void _AddArgument(int& argc, char** argv, @@ -533,7 +540,7 @@ ExpressionParser::EvaluateCommand(const char* expressionString, // no assignment, so let's assume it's a command fTokenizer.SetTo(expressionString); fTokenizer.SetCommandMode(true); - value = _ParseCommand(returnCode); + value = _ParseCommandPipe(returnCode); } if (token.type != TOKEN_END_OF_LINE) @@ -621,7 +628,39 @@ ExpressionParser::_ParseExpression() uint64 -ExpressionParser::_ParseCommand(int& returnCode) +ExpressionParser::_ParseCommandPipe(int& returnCode) +{ + debugger_command_pipe* pipe = (debugger_command_pipe*)allocate_temp_storage( + sizeof(debugger_command_pipe)); + + pipe->segment_count = 0; + pipe->broken = false; + + do { + if (pipe->segment_count >= MAX_DEBUGGER_COMMAND_PIPE_LENGTH) + parse_exception("Pipe too long", fTokenizer.NextToken().position); + + debugger_command_pipe_segment& segment + = pipe->segments[pipe->segment_count]; + segment.index = pipe->segment_count++; + + _ParseCommand(segment); + + } while (fTokenizer.NextToken().type == TOKEN_PIPE); + + fTokenizer.RewindToken(); + + // invoke the pipe + returnCode = invoke_debugger_command_pipe(pipe); + + free_temp_storage(pipe); + + return get_debug_variable("_", 0); +} + + +void +ExpressionParser::_ParseCommand(debugger_command_pipe_segment& segment) { fTokenizer.SetCommandMode(false); const Token& token = _EatToken(TOKEN_IDENTIFIER); @@ -663,12 +702,17 @@ ExpressionParser::_ParseCommand(int& returnCode) } } - // invoke the command - returnCode = invoke_debugger_command(command, argc, argv); + if (segment.index > 0) { + if (argc >= kMaxArgumentCount) + parse_exception("too many arguments for command", 0); + else + argc++; + } - free_temp_storage(argv); - - return get_debug_variable("_", 0); + segment.command = command; + segment.argc = argc; + segment.argv = argv; + segment.invocations = 0; } @@ -694,7 +738,7 @@ ExpressionParser::_ParseArgument(int& argc, char** argv) { // this starts a sub command int returnValue; - uint64 value = _ParseCommand(returnValue); + uint64 value = _ParseCommandPipe(returnValue); _EatToken(TOKEN_CLOSING_BRACKET); snprintf(sTempBuffer, sizeof(sTempBuffer), "%llu", value); @@ -709,6 +753,7 @@ ExpressionParser::_ParseArgument(int& argc, char** argv) case TOKEN_CLOSING_PARENTHESIS: case TOKEN_CLOSING_BRACKET: + case TOKEN_PIPE: // those don't belong to us fTokenizer.RewindToken(); return false; @@ -755,6 +800,10 @@ ExpressionParser::_GetUnparsedArgument(int& argc, char** argv) else done = true; break; + case TOKEN_PIPE: + if (parentheses == 0 && brackets == 0) + done = true; + break; case TOKEN_END_OF_LINE: done = true; break; @@ -764,8 +813,15 @@ ExpressionParser::_GetUnparsedArgument(int& argc, char** argv) int32 endPosition = fTokenizer.CurrentToken().position; fTokenizer.RewindToken(); - _AddArgument(argc, argv, fTokenizer.String() + startPosition, - endPosition - startPosition); + // add the argument only, if it's not just all spaces + const char* arg = fTokenizer.String() + startPosition; + int32 argLen = endPosition - startPosition; + bool allSpaces = true; + for (int32 i = 0; allSpaces && i < argLen; i++) + allSpaces = isspace(arg[i]); + + if (!allSpaces) + _AddArgument(argc, argv, arg, argLen); } @@ -944,7 +1000,7 @@ ExpressionParser::_ParseAtom() fTokenizer.SetCommandMode(true); int returnValue; - uint64 value = _ParseCommand(returnValue); + uint64 value = _ParseCommandPipe(returnValue); fTokenizer.SetCommandMode(false); _EatToken(TOKEN_CLOSING_BRACKET); @@ -975,8 +1031,8 @@ 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"); + kprintf_unfiltered("evaluate_debug_expression(): Out of jump buffers " + "for exception handling\n"); return 0; } @@ -994,10 +1050,10 @@ evaluate_debug_expression(const char* expression, uint64* _result, bool silent) success = false; if (!silent) { if (sExceptionPosition >= 0) { - kprintf("%s, at position: %d, in expression: %s\n", + kprintf_unfiltered("%s, at position: %d, in expression: %s\n", sExceptionMessage, sExceptionPosition, expression); } else - kprintf("%s", sExceptionMessage); + kprintf_unfiltered("%s", sExceptionMessage); } } @@ -1017,7 +1073,7 @@ int evaluate_debug_command(const char* commandLine) { if (sNextJumpBufferIndex >= kJumpBufferCount) { - kprintf("evaluate_debug_command(): Out of jump buffers for " + kprintf_unfiltered("evaluate_debug_command(): Out of jump buffers for " "exception handling\n"); return 0; } @@ -1031,10 +1087,10 @@ evaluate_debug_command(const char* commandLine) ExpressionParser().EvaluateCommand(commandLine, returnCode); } else { if (sExceptionPosition >= 0) { - kprintf("%s, at position: %d, in command line: %s\n", + kprintf_unfiltered("%s, at position: %d, in command line: %s\n", sExceptionMessage, sExceptionPosition, commandLine); } else - kprintf("%s", sExceptionMessage); + kprintf_unfiltered("%s", sExceptionMessage); } sNextJumpBufferIndex--;