* Introduced pipes in the kernel debugger. The syntax is similar to
pipes in the shell, though the semantics is a little different: The
second command is invoked whenever the first command has written a
complete line. The line is passed as last argument to the second
command. The new command flag B_KDEBUG_PIPE_FINAL_RERUN causes the
second command to be invoked again (with NULL argument) after the
first command is done.
* Added kprintf_unfiltered() and kputs_unfiltered() which bypass the
pipe mechanism and directly print to the bluescreen/serial output.
* Moved most commands from debug.cpp to the new
debug_builtin_commands.cpp.
* B_KDEBUG_DONT_PARSE_ARGUMENTS commands don't get an argument anymore,
if it would consist of white space only.
* Added new debugger command return value B_KDEBUG_ERROR, which
indicates that executing the command failed. This return code will
abort a complete pipe.
* Since debugger commands can nest (i.e. one command can invoke another
one) the setjmp()/longjmp() mechanism to restore the stack after a
page fault in a command needs more than one jump buffer.
* Added abort_debugger_command(), which longjmp()s out of the currently
executed command. This will also abort the current pipe.
* When pagination is enabled pressing "a" will abort the running command
(as opposed to "q" which only disables the blue screen output, but
lets the command continue).
* Added debugger commands:
- "grep" which can be used to filter output by pattern. Removed the
"filter" command and the underlying mechanism that did that before.
- "head" which prints only the first lines of output of another
command.
- "wc" counts lines, words, and characters of another command's
output.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25744 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -42,7 +42,12 @@
|
|||||||
# define ASSERT_PRINT(x, format...) do { } while(0)
|
# define ASSERT_PRINT(x, format...) do { } while(0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// command return value
|
||||||
|
#define B_KDEBUG_ERROR 4
|
||||||
|
|
||||||
|
// command flags
|
||||||
#define B_KDEBUG_DONT_PARSE_ARGUMENTS (0x01)
|
#define B_KDEBUG_DONT_PARSE_ARGUMENTS (0x01)
|
||||||
|
#define B_KDEBUG_PIPE_FINAL_RERUN (0x02)
|
||||||
|
|
||||||
struct debugger_module_info {
|
struct debugger_module_info {
|
||||||
module_info 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 debug_stop_screen_debug_output(void);
|
||||||
|
|
||||||
extern void kputs(const char *string);
|
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, ...)
|
extern void dprintf_no_syslog(const char *format, ...)
|
||||||
__attribute__ ((format (__printf__, 1, 2)));
|
__attribute__ ((format (__printf__, 1, 2)));
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ UsePrivateHeaders [ FDirName graphics vesa ] ;
|
|||||||
KernelMergeObject kernel_debug.o :
|
KernelMergeObject kernel_debug.o :
|
||||||
blue_screen.cpp
|
blue_screen.cpp
|
||||||
debug.cpp
|
debug.cpp
|
||||||
|
debug_builtin_commands.cpp
|
||||||
debug_commands.cpp
|
debug_commands.cpp
|
||||||
debug_paranoia.cpp
|
debug_paranoia.cpp
|
||||||
debug_parser.cpp
|
debug_parser.cpp
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ next_line(void)
|
|||||||
// Use the paging mechanism: either, we're in the debugger, and a
|
// Use the paging mechanism: either, we're in the debugger, and a
|
||||||
// command is being executed, or we're currently showing boot debug
|
// command is being executed, or we're currently showing boot debug
|
||||||
// output
|
// 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);
|
int32 length = strlen(text);
|
||||||
if (sScreen.x + length > sScreen.columns) {
|
if (sScreen.x + length > sScreen.columns) {
|
||||||
// make sure we don't overwrite too much
|
// make sure we don't overwrite too much
|
||||||
@@ -136,8 +136,13 @@ next_line(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char c = blue_screen_getchar();
|
char c = blue_screen_getchar();
|
||||||
if (c == 'q')
|
if (c == 'q') {
|
||||||
sScreen.ignore_output = true;
|
sScreen.ignore_output = true;
|
||||||
|
} else if (c == 'a') {
|
||||||
|
abort_debugger_command();
|
||||||
|
// should not return
|
||||||
|
sScreen.ignore_output = true;
|
||||||
|
}
|
||||||
|
|
||||||
// remove on screen text again
|
// remove on screen text again
|
||||||
sModule->fill_glyph(sScreen.columns - length, sScreen.y, length,
|
sModule->fill_glyph(sScreen.columns - length, sScreen.y, length,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
/*! This file contains the debugger and debug output facilities */
|
/*! This file contains the debugger and debug output facilities */
|
||||||
|
|
||||||
#include "blue_screen.h"
|
#include "blue_screen.h"
|
||||||
#include "gdb.h"
|
|
||||||
|
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
#include <debug_paranoia.h>
|
#include <debug_paranoia.h>
|
||||||
@@ -35,7 +34,9 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
|
|
||||||
|
#include "debug_builtin_commands.h"
|
||||||
#include "debug_commands.h"
|
#include "debug_commands.h"
|
||||||
|
#include "debug_output_filter.h"
|
||||||
#include "debug_variables.h"
|
#include "debug_variables.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -67,6 +68,8 @@ static const char* sCurrentKernelDebuggerMessage;
|
|||||||
#define OUTPUT_BUFFER_SIZE 1024
|
#define OUTPUT_BUFFER_SIZE 1024
|
||||||
static char sOutputBuffer[OUTPUT_BUFFER_SIZE];
|
static char sOutputBuffer[OUTPUT_BUFFER_SIZE];
|
||||||
static char sLastOutputBuffer[OUTPUT_BUFFER_SIZE];
|
static char sLastOutputBuffer[OUTPUT_BUFFER_SIZE];
|
||||||
|
static DebugOutputFilter* sDebugOutputFilter = NULL;
|
||||||
|
DefaultDebugOutputFilter gDefaultDebugOutputFilter;
|
||||||
|
|
||||||
static void flush_pending_repeats(void);
|
static void flush_pending_repeats(void);
|
||||||
static void check_pending_repeats(void *data, int iter);
|
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 sLineBuffer[HISTORY_SIZE][LINE_BUFFER_SIZE] = { "", };
|
||||||
static char sParseLine[LINE_BUFFER_SIZE];
|
static char sParseLine[LINE_BUFFER_SIZE];
|
||||||
static char sFilter[64];
|
|
||||||
static int32 sCurrentLine = 0;
|
static int32 sCurrentLine = 0;
|
||||||
|
|
||||||
#define distance(a, b) ((a) < (b) ? (b) - (a) : (a) - (b))
|
#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
|
static void
|
||||||
kputchar(char c)
|
kputchar(char c)
|
||||||
{
|
{
|
||||||
uint32 i;
|
|
||||||
|
|
||||||
if (sSerialDebugEnabled)
|
if (sSerialDebugEnabled)
|
||||||
arch_debug_serial_putchar(c);
|
arch_debug_serial_putchar(c);
|
||||||
if (sBlueScreenEnabled || sDebugScreenEnabled)
|
if (sBlueScreenEnabled || sDebugScreenEnabled)
|
||||||
blue_screen_putchar(c);
|
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)
|
if (sDebuggerModules[i] && sDebuggerModules[i]->debugger_puts)
|
||||||
sDebuggerModules[i]->debugger_puts(&c, sizeof(c));
|
sDebuggerModules[i]->debugger_puts(&c, sizeof(c));
|
||||||
}
|
}
|
||||||
@@ -108,15 +169,15 @@ kputchar(char c)
|
|||||||
void
|
void
|
||||||
kputs(const char *s)
|
kputs(const char *s)
|
||||||
{
|
{
|
||||||
uint32 i;
|
if (sDebugOutputFilter != NULL)
|
||||||
|
sDebugOutputFilter->PrintString(s);
|
||||||
|
}
|
||||||
|
|
||||||
if (sSerialDebugEnabled)
|
|
||||||
arch_debug_serial_puts(s);
|
void
|
||||||
if (sBlueScreenEnabled || sDebugScreenEnabled)
|
kputs_unfiltered(const char *s)
|
||||||
blue_screen_puts(s);
|
{
|
||||||
for (i = 0; sSerialDebugEnabled && i < kMaxDebuggerModules; i++)
|
gDefaultDebugOutputFilter.PrintString(s);
|
||||||
if (sDebuggerModules[i] && sDebuggerModules[i]->debugger_puts)
|
|
||||||
sDebuggerModules[i]->debugger_puts(s, strlen(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
|
static int
|
||||||
cmd_dump_kdl_message(int argc, char **argv)
|
cmd_dump_kdl_message(int argc, char **argv)
|
||||||
{
|
{
|
||||||
if (sCurrentKernelDebuggerMessage) {
|
if (sCurrentKernelDebuggerMessage) {
|
||||||
kputs(sCurrentKernelDebuggerMessage);
|
kputs(sCurrentKernelDebuggerMessage);
|
||||||
kputchar('\n');
|
kputs("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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
|
static status_t
|
||||||
syslog_sender(void *data)
|
syslog_sender(void *data)
|
||||||
@@ -1000,6 +954,8 @@ debug_early_boot_message(const char *string)
|
|||||||
status_t
|
status_t
|
||||||
debug_init(kernel_args *args)
|
debug_init(kernel_args *args)
|
||||||
{
|
{
|
||||||
|
new(&gDefaultDebugOutputFilter) DefaultDebugOutputFilter;
|
||||||
|
|
||||||
debug_paranoia_init();
|
debug_paranoia_init();
|
||||||
return arch_debug_console_init(args);
|
return arch_debug_console_init(args);
|
||||||
}
|
}
|
||||||
@@ -1008,43 +964,12 @@ debug_init(kernel_args *args)
|
|||||||
status_t
|
status_t
|
||||||
debug_init_post_vm(kernel_args *args)
|
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,
|
add_debugger_command_etc("message", &cmd_dump_kdl_message,
|
||||||
"Reprint the message printed when entering KDL",
|
"Reprint the message printed when entering KDL",
|
||||||
"\n"
|
"\n"
|
||||||
"Reprints the message printed when entering KDL.\n", 0);
|
"Reprints the message printed when entering KDL.\n", 0);
|
||||||
add_debugger_command_etc("expr", &cmd_expr,
|
|
||||||
"Evaluates the given expression and prints the result",
|
debug_builtin_commands_init();
|
||||||
"<expression>\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",
|
|
||||||
"<pattern>\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",
|
|
||||||
"<error>\n"
|
|
||||||
"Prints a human-readable description for the given numeric error\n"
|
|
||||||
"code.\n"
|
|
||||||
" <error> - The numeric error code.\n", 0);
|
|
||||||
|
|
||||||
debug_variables_init();
|
debug_variables_init();
|
||||||
frame_buffer_console_init(args);
|
frame_buffer_console_init(args);
|
||||||
@@ -1181,6 +1106,8 @@ kernel_debugger(const char *message)
|
|||||||
sBlueScreenEnabled = true;
|
sBlueScreenEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sDebugOutputFilter = &gDefaultDebugOutputFilter;
|
||||||
|
|
||||||
if (message)
|
if (message)
|
||||||
kprintf("PANIC: %s\n", message);
|
kprintf("PANIC: %s\n", message);
|
||||||
|
|
||||||
@@ -1196,6 +1123,8 @@ kernel_debugger(const char *message)
|
|||||||
call_modules_hook(false);
|
call_modules_hook(false);
|
||||||
set_dprintf_enabled(dprintfState);
|
set_dprintf_enabled(dprintfState);
|
||||||
|
|
||||||
|
sDebugOutputFilter = NULL;
|
||||||
|
|
||||||
sBlueScreenEnabled = false;
|
sBlueScreenEnabled = false;
|
||||||
atomic_add(&inDebugger, -1);
|
atomic_add(&inDebugger, -1);
|
||||||
restore_interrupts(state);
|
restore_interrupts(state);
|
||||||
@@ -1354,21 +1283,22 @@ dprintf_no_syslog(const char *format, ...)
|
|||||||
void
|
void
|
||||||
kprintf(const char *format, ...)
|
kprintf(const char *format, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
if (sDebugOutputFilter != NULL) {
|
||||||
|
va_list args;
|
||||||
// ToDo: don't print anything if the debugger is not running!
|
va_start(args, format);
|
||||||
|
sDebugOutputFilter->Print(format, args);
|
||||||
va_start(args, format);
|
va_end(args);
|
||||||
vsnprintf(sOutputBuffer, OUTPUT_BUFFER_SIZE, format, args);
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
if (in_command_invocation() && sFilter[0]) {
|
|
||||||
if (strstr(sOutputBuffer, sFilter) == NULL)
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,318 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, Ingo Weinhold, [email protected]
|
||||||
|
* Copyright 2002-2008, 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_builtin_commands.h"
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <debug.h>
|
||||||
|
#include <kernel.h>
|
||||||
|
|
||||||
|
#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",
|
||||||
|
"<expression>\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",
|
||||||
|
"<error>\n"
|
||||||
|
"Prints a human-readable description for the given numeric error\n"
|
||||||
|
"code.\n"
|
||||||
|
" <error> - The numeric error code.\n", 0);
|
||||||
|
add_debugger_command_etc("head", &cmd_head,
|
||||||
|
"Prints only the first lines of output from another command",
|
||||||
|
"<maxLines>\n"
|
||||||
|
"Should be used in a command pipe. It prints only the first\n"
|
||||||
|
"<maxLines> 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 ] <pattern>\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",
|
||||||
|
"<maxLines>\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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, Ingo Weinhold, [email protected]
|
||||||
|
* 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
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "debug_commands.h"
|
#include "debug_commands.h"
|
||||||
|
|
||||||
#include <setjmp.h>
|
#include <setjmp.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <KernelExport.h>
|
#include <KernelExport.h>
|
||||||
@@ -19,16 +20,172 @@
|
|||||||
#include <thread.h>
|
#include <thread.h>
|
||||||
#include <util/AutoLock.h>
|
#include <util/AutoLock.h>
|
||||||
|
|
||||||
|
#include "debug_output_filter.h"
|
||||||
#include "debug_variables.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 spinlock sSpinlock = 0;
|
||||||
|
|
||||||
static struct debugger_command *sCommands;
|
static struct debugger_command *sCommands;
|
||||||
|
|
||||||
static jmp_buf sInvokeCommandEnv;
|
static jmp_buf sInvokeCommandEnv[kMaxInvokeCommandDepth];
|
||||||
|
static int32 sInvokeCommandLevel = 0;
|
||||||
static bool sInvokeCommandDirectly = false;
|
static bool sInvokeCommandDirectly = false;
|
||||||
static bool sInCommand = 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*
|
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
|
// intercept invocations with "--help" and directly print the usage text
|
||||||
// If we know the command's usage text, intercept "--help" invocations
|
// If we know the command's usage text, intercept "--help" invocations
|
||||||
// and print it directly.
|
// and print it directly.
|
||||||
if (argc == 2 && strcmp(argv[1], "--help") == 0 && command->usage != NULL) {
|
if (argc == 2 && argv[1] != NULL && strcmp(argv[1], "--help") == 0
|
||||||
kprintf("usage: %s ", command->name);
|
&& command->usage != NULL) {
|
||||||
kputs(command->usage);
|
kprintf_unfiltered("usage: %s ", command->name);
|
||||||
|
kputs_unfiltered(command->usage);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,30 +275,96 @@ invoke_debugger_command(struct debugger_command *command, int argc, char** argv)
|
|||||||
|
|
||||||
sInCommand = true;
|
sInCommand = true;
|
||||||
|
|
||||||
if (setjmp(sInvokeCommandEnv) == 0) {
|
switch (setjmp(sInvokeCommandEnv[sInvokeCommandLevel++])) {
|
||||||
int result;
|
case 0:
|
||||||
thread->fault_handler = (addr_t)&&error;
|
int result;
|
||||||
// Fake goto to trick the compiler not to optimize the code at the label
|
thread->fault_handler = (addr_t)&&error;
|
||||||
// away.
|
// Fake goto to trick the compiler not to optimize the code at the label
|
||||||
if (!thread)
|
// away.
|
||||||
goto error;
|
if (!thread)
|
||||||
|
goto error;
|
||||||
|
|
||||||
result = command->func(argc, argv);
|
result = command->func(argc, argv);
|
||||||
|
|
||||||
thread->fault_handler = oldFaultHandler;
|
thread->fault_handler = oldFaultHandler;
|
||||||
sInCommand = false;
|
sInvokeCommandLevel--;
|
||||||
return result;
|
sInCommand = false;
|
||||||
|
return result;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
longjmp(sInvokeCommandEnv, 1);
|
// jump to INVOKE_COMMAND_FAULT case, cleaning up the stack
|
||||||
// jump into the else branch
|
longjmp(sInvokeCommandEnv[--sInvokeCommandLevel],
|
||||||
} else {
|
INVOKE_COMMAND_FAULT);
|
||||||
kprintf("\n[*** READ/WRITE FAULT ***]\n");
|
|
||||||
|
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;
|
thread->fault_handler = oldFaultHandler;
|
||||||
sInCommand = false;
|
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
|
// directly print the usage text, if we know it, otherwise invoke the
|
||||||
// command with "--help"
|
// command with "--help"
|
||||||
if (command->usage != NULL) {
|
if (command->usage != NULL) {
|
||||||
kprintf("usage: %s ", command->name);
|
kprintf_unfiltered("usage: %s ", command->name);
|
||||||
kputs(command->usage);
|
kputs_unfiltered(command->usage);
|
||||||
} else {
|
} else {
|
||||||
char* args[3] = { NULL, "--help", NULL };
|
char* args[3] = { NULL, "--help", NULL };
|
||||||
invoke_debugger_command(command, 2, args);
|
invoke_debugger_command(command, 2, args);
|
||||||
|
|||||||
@@ -9,14 +9,33 @@
|
|||||||
#include <SupportDefs.h>
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
|
||||||
struct debugger_command {
|
#define MAX_DEBUGGER_COMMAND_PIPE_LENGTH 8
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct debugger_command {
|
||||||
struct debugger_command* next;
|
struct debugger_command* next;
|
||||||
int (*func)(int, char **);
|
int (*func)(int, char **);
|
||||||
const char* name;
|
const char* name;
|
||||||
const char* description;
|
const char* description;
|
||||||
const char* usage;
|
const char* usage;
|
||||||
uint32 flags;
|
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
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -30,6 +49,11 @@ bool in_command_invocation(void);
|
|||||||
|
|
||||||
int invoke_debugger_command(struct debugger_command *command, int argc,
|
int invoke_debugger_command(struct debugger_command *command, int argc,
|
||||||
char** argv);
|
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();
|
debugger_command* get_debugger_commands();
|
||||||
void sort_debugger_commands();
|
void sort_debugger_commands();
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008, Ingo Weinhold, [email protected]
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef _KERNEL_DEBUG_OUTPUT_FILTER_H
|
||||||
|
#define _KERNEL_DEBUG_OUTPUT_FILTER_H
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
/*
|
/*
|
||||||
Grammar:
|
Grammar:
|
||||||
|
|
||||||
commandLine := command | ( "(" expression ")" )
|
commandLine := commandPipe | ( "(" expression ")" )
|
||||||
expression := term | assignment
|
expression := term | assignment
|
||||||
assignment := variable ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" )
|
assignment := variable ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" )
|
||||||
expression
|
expression
|
||||||
@@ -33,6 +33,7 @@
|
|||||||
variable := identifier
|
variable := identifier
|
||||||
identifier := ( "_" | "a" - "z" | "A" - "Z" )
|
identifier := ( "_" | "a" - "z" | "A" - "Z" )
|
||||||
( "_" | "a" - "z" | "A" - "Z" | "0" - "9" )*
|
( "_" | "a" - "z" | "A" - "Z" | "0" - "9" )*
|
||||||
|
commandPipe := command ( "|" command )*
|
||||||
command := identifier argument*
|
command := identifier argument*
|
||||||
argument := ( "(" expression ")" ) | ( "[" commandLine "]" )
|
argument := ( "(" expression ")" ) | ( "[" commandLine "]" )
|
||||||
| unquotedString | quotedString
|
| unquotedString | quotedString
|
||||||
@@ -87,6 +88,8 @@ enum {
|
|||||||
TOKEN_OPENING_BRACE = '{',
|
TOKEN_OPENING_BRACE = '{',
|
||||||
TOKEN_CLOSING_BRACE = '}',
|
TOKEN_CLOSING_BRACE = '}',
|
||||||
|
|
||||||
|
TOKEN_PIPE = '|',
|
||||||
|
|
||||||
TOKEN_STRING = '"',
|
TOKEN_STRING = '"',
|
||||||
TOKEN_UNKNOWN = '?',
|
TOKEN_UNKNOWN = '?',
|
||||||
TOKEN_NONE = ' ',
|
TOKEN_NONE = ' ',
|
||||||
@@ -125,8 +128,9 @@ static void
|
|||||||
parse_exception(const char* message, int32 position)
|
parse_exception(const char* message, int32 position)
|
||||||
{
|
{
|
||||||
if (sNextJumpBufferIndex == 0) {
|
if (sNextJumpBufferIndex == 0) {
|
||||||
kprintf("parse_exception(): No jump buffer!\n");
|
kprintf_unfiltered("parse_exception(): No jump buffer!\n");
|
||||||
kprintf("exception: \"%s\", position: %lu\n", message, position);
|
kprintf_unfiltered("exception: \"%s\", position: %lu\n", message,
|
||||||
|
position);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,6 +349,7 @@ public:
|
|||||||
case ')':
|
case ')':
|
||||||
case '[':
|
case '[':
|
||||||
case ']':
|
case ']':
|
||||||
|
case '|':
|
||||||
fCurrentToken.SetTo(fCurrentChar, 1, _CurrentPos(),
|
fCurrentToken.SetTo(fCurrentChar, 1, _CurrentPos(),
|
||||||
*fCurrentChar);
|
*fCurrentChar);
|
||||||
fCurrentChar++;
|
fCurrentChar++;
|
||||||
@@ -470,7 +475,9 @@ class ExpressionParser {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
uint64 _ParseExpression();
|
uint64 _ParseExpression();
|
||||||
uint64 _ParseCommand(int& returnCode);
|
uint64 _ParseCommandPipe(int& returnCode);
|
||||||
|
void _ParseCommand(
|
||||||
|
debugger_command_pipe_segment& segment);
|
||||||
bool _ParseArgument(int& argc, char** argv);
|
bool _ParseArgument(int& argc, char** argv);
|
||||||
void _GetUnparsedArgument(int& argc, char** argv);
|
void _GetUnparsedArgument(int& argc, char** argv);
|
||||||
void _AddArgument(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
|
// no assignment, so let's assume it's a command
|
||||||
fTokenizer.SetTo(expressionString);
|
fTokenizer.SetTo(expressionString);
|
||||||
fTokenizer.SetCommandMode(true);
|
fTokenizer.SetCommandMode(true);
|
||||||
value = _ParseCommand(returnCode);
|
value = _ParseCommandPipe(returnCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (token.type != TOKEN_END_OF_LINE)
|
if (token.type != TOKEN_END_OF_LINE)
|
||||||
@@ -621,7 +628,39 @@ ExpressionParser::_ParseExpression()
|
|||||||
|
|
||||||
|
|
||||||
uint64
|
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);
|
fTokenizer.SetCommandMode(false);
|
||||||
const Token& token = _EatToken(TOKEN_IDENTIFIER);
|
const Token& token = _EatToken(TOKEN_IDENTIFIER);
|
||||||
@@ -663,12 +702,17 @@ ExpressionParser::_ParseCommand(int& returnCode)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// invoke the command
|
if (segment.index > 0) {
|
||||||
returnCode = invoke_debugger_command(command, argc, argv);
|
if (argc >= kMaxArgumentCount)
|
||||||
|
parse_exception("too many arguments for command", 0);
|
||||||
|
else
|
||||||
|
argc++;
|
||||||
|
}
|
||||||
|
|
||||||
free_temp_storage(argv);
|
segment.command = command;
|
||||||
|
segment.argc = argc;
|
||||||
return get_debug_variable("_", 0);
|
segment.argv = argv;
|
||||||
|
segment.invocations = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -694,7 +738,7 @@ ExpressionParser::_ParseArgument(int& argc, char** argv)
|
|||||||
{
|
{
|
||||||
// this starts a sub command
|
// this starts a sub command
|
||||||
int returnValue;
|
int returnValue;
|
||||||
uint64 value = _ParseCommand(returnValue);
|
uint64 value = _ParseCommandPipe(returnValue);
|
||||||
_EatToken(TOKEN_CLOSING_BRACKET);
|
_EatToken(TOKEN_CLOSING_BRACKET);
|
||||||
|
|
||||||
snprintf(sTempBuffer, sizeof(sTempBuffer), "%llu", value);
|
snprintf(sTempBuffer, sizeof(sTempBuffer), "%llu", value);
|
||||||
@@ -709,6 +753,7 @@ ExpressionParser::_ParseArgument(int& argc, char** argv)
|
|||||||
|
|
||||||
case TOKEN_CLOSING_PARENTHESIS:
|
case TOKEN_CLOSING_PARENTHESIS:
|
||||||
case TOKEN_CLOSING_BRACKET:
|
case TOKEN_CLOSING_BRACKET:
|
||||||
|
case TOKEN_PIPE:
|
||||||
// those don't belong to us
|
// those don't belong to us
|
||||||
fTokenizer.RewindToken();
|
fTokenizer.RewindToken();
|
||||||
return false;
|
return false;
|
||||||
@@ -755,6 +800,10 @@ ExpressionParser::_GetUnparsedArgument(int& argc, char** argv)
|
|||||||
else
|
else
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
|
case TOKEN_PIPE:
|
||||||
|
if (parentheses == 0 && brackets == 0)
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
case TOKEN_END_OF_LINE:
|
case TOKEN_END_OF_LINE:
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
@@ -764,8 +813,15 @@ ExpressionParser::_GetUnparsedArgument(int& argc, char** argv)
|
|||||||
int32 endPosition = fTokenizer.CurrentToken().position;
|
int32 endPosition = fTokenizer.CurrentToken().position;
|
||||||
fTokenizer.RewindToken();
|
fTokenizer.RewindToken();
|
||||||
|
|
||||||
_AddArgument(argc, argv, fTokenizer.String() + startPosition,
|
// add the argument only, if it's not just all spaces
|
||||||
endPosition - startPosition);
|
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);
|
fTokenizer.SetCommandMode(true);
|
||||||
int returnValue;
|
int returnValue;
|
||||||
uint64 value = _ParseCommand(returnValue);
|
uint64 value = _ParseCommandPipe(returnValue);
|
||||||
fTokenizer.SetCommandMode(false);
|
fTokenizer.SetCommandMode(false);
|
||||||
|
|
||||||
_EatToken(TOKEN_CLOSING_BRACKET);
|
_EatToken(TOKEN_CLOSING_BRACKET);
|
||||||
@@ -975,8 +1031,8 @@ bool
|
|||||||
evaluate_debug_expression(const char* expression, uint64* _result, bool silent)
|
evaluate_debug_expression(const char* expression, uint64* _result, bool silent)
|
||||||
{
|
{
|
||||||
if (sNextJumpBufferIndex >= kJumpBufferCount) {
|
if (sNextJumpBufferIndex >= kJumpBufferCount) {
|
||||||
kprintf("evaluate_debug_expression(): Out of jump buffers for "
|
kprintf_unfiltered("evaluate_debug_expression(): Out of jump buffers "
|
||||||
"exception handling\n");
|
"for exception handling\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -994,10 +1050,10 @@ evaluate_debug_expression(const char* expression, uint64* _result, bool silent)
|
|||||||
success = false;
|
success = false;
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
if (sExceptionPosition >= 0) {
|
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);
|
sExceptionMessage, sExceptionPosition, expression);
|
||||||
} else
|
} else
|
||||||
kprintf("%s", sExceptionMessage);
|
kprintf_unfiltered("%s", sExceptionMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1017,7 +1073,7 @@ int
|
|||||||
evaluate_debug_command(const char* commandLine)
|
evaluate_debug_command(const char* commandLine)
|
||||||
{
|
{
|
||||||
if (sNextJumpBufferIndex >= kJumpBufferCount) {
|
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");
|
"exception handling\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1031,10 +1087,10 @@ evaluate_debug_command(const char* commandLine)
|
|||||||
ExpressionParser().EvaluateCommand(commandLine, returnCode);
|
ExpressionParser().EvaluateCommand(commandLine, returnCode);
|
||||||
} else {
|
} else {
|
||||||
if (sExceptionPosition >= 0) {
|
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);
|
sExceptionMessage, sExceptionPosition, commandLine);
|
||||||
} else
|
} else
|
||||||
kprintf("%s", sExceptionMessage);
|
kprintf_unfiltered("%s", sExceptionMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
sNextJumpBufferIndex--;
|
sNextJumpBufferIndex--;
|
||||||
|
|||||||
Reference in New Issue
Block a user