Files
haiku-beta6/src/system/kernel/debug/debug.cpp
T

1456 lines
32 KiB
C++
Raw Normal View History

/*
* Copyright 2002-2007, Axel Dörfler, [email protected]
* Distributed under the terms of the MIT License.
*
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
/*! This file contains the debugger and debug output facilities */
#include "blue_screen.h"
#include "gdb.h"
2002-07-09 12:24:59 +00:00
#include <debug.h>
#include <driver_settings.h>
#include <frame_buffer_console.h>
2002-07-09 12:24:59 +00:00
#include <int.h>
#include <kernel.h>
#include <smp.h>
#include <thread.h>
2008-01-12 18:41:35 +00:00
#include <tracing.h>
2003-11-13 22:08:30 +00:00
#include <vm.h>
#include <arch/debug_console.h>
2002-07-09 12:24:59 +00:00
#include <arch/debug.h>
#include <util/ring_buffer.h>
2002-07-09 12:24:59 +00:00
#include <syslog_daemon.h>
#include <ctype.h>
#include <setjmp.h>
2002-07-09 12:24:59 +00:00
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
2002-07-09 12:24:59 +00:00
#include <string.h>
#include <syslog.h>
2002-07-09 12:24:59 +00:00
static const char* const kKDLPrompt = "kdebug> ";
extern "C" int kgets(char *buffer, int length);
static int invoke_command(struct debugger_command *command, int argc,
char** argv);
2006-06-12 21:53:00 +00:00
typedef struct debugger_command {
2002-07-09 12:24:59 +00:00
struct debugger_command *next;
int (*func)(int, char **);
const char *name;
2002-07-09 12:24:59 +00:00
const char *description;
} debugger_command;
2002-07-09 12:24:59 +00:00
int dbg_register_file[B_MAX_CPU_COUNT][14];
/* XXXmpetit -- must be made generic */
static bool sSerialDebugEnabled = true;
static bool sSyslogOutputEnabled = true;
2006-01-24 21:52:53 +00:00
static bool sBlueScreenEnabled = false;
2007-07-19 13:16:08 +00:00
// must always be false on startup
static bool sDebugScreenEnabled = false;
2006-01-24 21:52:53 +00:00
static bool sBlueScreenOutput = true;
static spinlock sSpinlock = 0;
static int32 sDebuggerOnCPU = -1;
static sem_id sSyslogNotify = -1;
static struct syslog_message *sSyslogMessage;
static struct ring_buffer *sSyslogBuffer;
static bool sSyslogDropped = false;
static struct debugger_command *sCommands;
2002-07-09 12:24:59 +00:00
static jmp_buf sInvokeCommandEnv;
static bool sInvokeCommandDirectly = false;
static const char* sCurrentKernelDebuggerMessage;
#define SYSLOG_BUFFER_SIZE 65536
#define OUTPUT_BUFFER_SIZE 1024
static char sOutputBuffer[OUTPUT_BUFFER_SIZE];
static char sLastOutputBuffer[OUTPUT_BUFFER_SIZE];
static void flush_pending_repeats(void);
static void check_pending_repeats(void *data, int iter);
static int64 sMessageRepeatFirstTime = 0;
static int64 sMessageRepeatLastTime = 0;
static int32 sMessageRepeatCount = 0;
#define LINE_BUFFER_SIZE 1024
2002-07-09 12:24:59 +00:00
#define MAX_ARGS 16
#define HISTORY_SIZE 16
static char sLineBuffer[HISTORY_SIZE][LINE_BUFFER_SIZE] = { "", };
static char sParseLine[LINE_BUFFER_SIZE];
static int32 sCurrentLine = 0;
static char *sArguments[MAX_ARGS] = { NULL, };
2002-07-09 12:24:59 +00:00
#define distance(a, b) ((a) < (b) ? (b) - (a) : (a) - (b))
static debugger_command*
next_command(debugger_command* command, const char* prefix, int prefixLen)
{
if (command == NULL)
command = sCommands;
else
command = command->next;
while (command != NULL && !strncmp(prefix, command->name, prefixLen) == 0)
command = command->next;
return command;
}
static debugger_command *
find_command(const char *name, bool partialMatch, bool& ambiguous)
{
debugger_command *command;
ambiguous = false;
// search command by full name
for (command = sCommands; command != NULL; command = command->next) {
if (strcmp(name, command->name) == 0)
return command;
}
// if it couldn't be found, search for a partial match
if (partialMatch) {
int length = strlen(name);
command = next_command(NULL, name, length);
if (command != NULL) {
if (next_command(command, name, length) == NULL)
return command;
ambiguous = true;
}
}
return NULL;
}
static void
kputchar(char c)
{
if (sSerialDebugEnabled)
arch_debug_serial_putchar(c);
if (sBlueScreenEnabled || sDebugScreenEnabled)
blue_screen_putchar(c);
}
static void
kputs(const char *s)
{
if (sSerialDebugEnabled)
arch_debug_serial_puts(s);
if (sBlueScreenEnabled || sDebugScreenEnabled)
blue_screen_puts(s);
}
2008-01-14 12:48:13 +00:00
static void
insert_chars_into_line(char* buffer, int32& position, int32& length,
const char* chars, int32 charCount)
2008-01-14 12:48:13 +00:00
{
// move the following chars to make room for the ones to insert
if (position < length) {
memmove(buffer + position + charCount, buffer + position,
length - position);
}
2008-01-14 12:48:13 +00:00
// insert chars
memcpy(buffer + position, chars, charCount);
int32 oldPosition = position;
position += charCount;
length += charCount;
2008-01-14 12:48:13 +00:00
// print the new chars (and the following ones)
kprintf("%.*s", (int)(length - oldPosition),
buffer + oldPosition);
2008-01-14 12:48:13 +00:00
// reposition cursor, if necessary
if (position < length)
2008-01-14 12:48:13 +00:00
kprintf("\x1b[%ldD", length - position);
}
static void
insert_char_into_line(char* buffer, int32& position, int32& length, char c)
{
insert_chars_into_line(buffer, position, length, &c, 1);
2008-01-14 12:48:13 +00:00
}
static void
remove_char_from_line(char* buffer, int32& position, int32& length)
{
if (position == length)
return;
length--;
if (position < length) {
// move the subsequent chars
memmove(buffer + position, buffer + position + 1, length - position);
// print the rest of the line again, if necessary
for (int32 i = position; i < length; i++)
kputchar(buffer[i]);
}
// visually clear the last char
kputchar(' ');
// reposition the cursor
kprintf("\x1b[%ldD", length - position + 1);
}
class LineEditingHelper {
public:
virtual ~LineEditingHelper() {}
virtual void TabCompletion(char* buffer, int32 capacity, int32& position,
int32& length) = 0;
};
class CommandLineEditingHelper : public LineEditingHelper {
public:
CommandLineEditingHelper()
{
}
virtual ~CommandLineEditingHelper() {}
virtual void TabCompletion(char* buffer, int32 capacity, int32& position,
int32& length)
{
// find the first space
char tmpChar = buffer[position];
buffer[position] = '\0';
char* firstSpace = strchr(buffer, ' ');
buffer[position] = tmpChar;
bool reprintLine = false;
if (firstSpace != NULL) {
// a complete command -- print its help
// get the command
tmpChar = *firstSpace;
*firstSpace = '\0';
bool ambiguous;
debugger_command* command = find_command(buffer, true, ambiguous);
*firstSpace = tmpChar;
if (command != NULL) {
kputchar('\n');
char* args[3] = { NULL, "--help", NULL };
invoke_command(command, 2, args);
} else {
if (ambiguous)
kprintf("\nambiguous command\n");
else
kprintf("\nno such command\n");
}
reprintLine = true;
} else {
// a partial command -- look for completions
// check for possible completions
int32 count = 0;
int32 longestName = 0;
debugger_command* command = NULL;
int32 longestCommonPrefix = 0;
const char* previousCommandName = NULL;
while ((command = next_command(command, buffer, position))
!= NULL) {
count++;
int32 nameLength = strlen(command->name);
longestName = max_c(longestName, nameLength);
// updated the length of the longest common prefix of the
// commands
if (count == 1) {
longestCommonPrefix = longestName;
} else {
longestCommonPrefix = min_c(longestCommonPrefix,
nameLength);
for (int32 i = position; i < longestCommonPrefix; i++) {
if (previousCommandName[i] != command->name[i]) {
longestCommonPrefix = i;
break;
}
}
}
previousCommandName = command->name;
}
if (count == 0) {
// no possible completions
kprintf("\nno completions\n");
reprintLine = true;
} else if (count == 1) {
// exactly one completion
command = next_command(NULL, buffer, position);
// check for sufficient space in the buffer
int32 neededSpace = longestName - position + 1;
// remainder of the name plus one space
// also consider the terminating null char
if (length + neededSpace + 1 >= capacity)
return;
insert_chars_into_line(buffer, position, length,
command->name + position, longestName - position);
insert_char_into_line(buffer, position, length, ' ');
} else if (longestCommonPrefix > position) {
// multiple possible completions with longer common prefix
// -- insert the remainder of the common prefix
// check for sufficient space in the buffer
int32 neededSpace = longestCommonPrefix - position;
// also consider the terminating null char
if (length + neededSpace + 1 >= capacity)
return;
insert_chars_into_line(buffer, position, length,
previousCommandName + position, neededSpace);
} else {
// multiple possible completions without longer common prefix
// -- print them all
kprintf("\n");
reprintLine = true;
int columns = 80 / (longestName + 2);
debugger_command* command = NULL;
int column = 0;
while ((command = next_command(command, buffer, position))
!= NULL) {
// spacing
if (column > 0 && column % columns == 0)
kputchar('\n');
column++;
kprintf(" %-*s", (int)longestName, command->name);
}
kputchar('\n');
}
}
// reprint the editing line, if necessary
if (reprintLine) {
kprintf("%s%.*s", kKDLPrompt, (int)length, buffer);
if (position < length)
kprintf("\x1b[%ldD", length - position);
}
}
};
static int
read_line(char *buffer, int32 maxLength,
LineEditingHelper* editingHelper = NULL)
2002-07-09 12:24:59 +00:00
{
int32 currentHistoryLine = sCurrentLine;
int32 position = 0;
2008-01-14 12:48:13 +00:00
int32 length = 0;
2002-07-09 12:24:59 +00:00
bool done = false;
char c;
2002-07-09 12:24:59 +00:00
char (*readChar)(void);
2006-01-24 21:52:53 +00:00
if (sBlueScreenOutput)
readChar = blue_screen_getchar;
else
readChar = arch_debug_serial_getchar;
while (!done) {
c = readChar();
switch (c) {
2002-07-09 12:24:59 +00:00
case '\n':
case '\r':
2008-01-14 12:48:13 +00:00
buffer[length++] = '\0';
kputchar('\n');
2002-07-09 12:24:59 +00:00
done = true;
break;
case '\t':
{
if (editingHelper != NULL) {
editingHelper->TabCompletion(buffer, maxLength,
position, length);
}
break;
}
2002-07-09 12:24:59 +00:00
case 8: // backspace
if (position > 0) {
kputs("\x1b[1D"); // move to the left one
position--;
2008-01-14 12:48:13 +00:00
remove_char_from_line(buffer, position, length);
}
break;
case 0x1f & 'K': // CTRL-K -- clear line after current position
if (position < length) {
// clear chars
for (int32 i = position; i < length; i++)
kputchar(' ');
// reposition cursor
kprintf("\x1b[%ldD", length - position);
length = position;
2002-07-09 12:24:59 +00:00
}
break;
case 27: // escape sequence
c = readChar();
if (c != '[') {
// ignore broken escape sequence
break;
}
c = readChar();
switch (c) {
2008-01-14 12:48:13 +00:00
case 'C': // right arrow
if (position < length) {
kputs("\x1b[1C"); // move to the right one
position++;
}
2002-07-09 12:24:59 +00:00
break;
2008-01-14 12:48:13 +00:00
case 'D': // left arrow
if (position > 0) {
kputs("\x1b[1D"); // move to the left one
position--;
2002-07-09 12:24:59 +00:00
}
break;
case 'A': // up arrow
case 'B': // down arrow
2002-07-09 12:24:59 +00:00
{
int32 historyLine = 0;
2002-07-09 12:24:59 +00:00
2008-01-14 12:48:13 +00:00
if (c == 'A') {
2002-07-09 12:24:59 +00:00
// up arrow
historyLine = currentHistoryLine - 1;
if (historyLine < 0)
historyLine = HISTORY_SIZE - 1;
2002-07-09 12:24:59 +00:00
} else {
// down arrow
if (currentHistoryLine == sCurrentLine)
break;
historyLine = currentHistoryLine + 1;
if (historyLine >= HISTORY_SIZE)
historyLine = 0;
2002-07-09 12:24:59 +00:00
}
// clear the history again if we're in the current line again
// (the buffer we get just is the current line buffer)
if (historyLine == sCurrentLine)
sLineBuffer[historyLine][0] = '\0';
2002-07-09 12:24:59 +00:00
// swap the current line with something from the history
if (position > 0)
kprintf("\x1b[%ldD", position); // move to beginning of line
2002-07-09 12:24:59 +00:00
strcpy(buffer, sLineBuffer[historyLine]);
2008-01-14 12:48:13 +00:00
length = position = strlen(buffer);
kprintf("%s\x1b[K", buffer); // print the line and clear the rest
currentHistoryLine = historyLine;
2002-07-09 12:24:59 +00:00
break;
}
2008-01-14 12:48:13 +00:00
case 'H': // home
{
if (position > 0) {
kprintf("\x1b[%ldD", position);
position = 0;
}
break;
}
case 'F': // end
{
if (position < length) {
kprintf("\x1b[%ldC", length - position);
position = length;
}
break;
}
case '3': // if "3~", it's DEL
{
c = readChar();
if (c != '~')
break;
if (position < length)
remove_char_from_line(buffer, position, length);
break;
}
2002-07-09 12:24:59 +00:00
default:
break;
}
break;
case '$':
case '+':
2006-01-24 21:52:53 +00:00
if (!sBlueScreenOutput) {
/* HACK ALERT!!!
*
* If we get a $ at the beginning of the line
* we assume we are talking with GDB
*/
if (position == 0) {
strcpy(buffer, "gdb");
position = 4;
done = true;
break;
}
2002-07-09 12:24:59 +00:00
}
/* supposed to fall through */
2002-07-09 12:24:59 +00:00
default:
2008-01-14 12:48:13 +00:00
if (isprint(c))
insert_char_into_line(buffer, position, length, c);
break;
2002-07-09 12:24:59 +00:00
}
2008-01-14 12:48:13 +00:00
if (length >= maxLength - 2) {
buffer[length++] = '\0';
kputchar('\n');
2002-07-09 12:24:59 +00:00
done = true;
break;
}
}
2008-01-14 12:48:13 +00:00
return length;
2002-07-09 12:24:59 +00:00
}
2006-06-12 21:53:00 +00:00
int
kgets(char *buffer, int length)
{
2006-06-12 21:53:00 +00:00
return read_line(buffer, length);
}
static int
parse_line(const char *buffer, char **argv, int *_argc, int32 maxArgs)
2002-07-09 12:24:59 +00:00
{
char *string = sParseLine;
int32 index = 0;
2002-07-09 12:24:59 +00:00
strcpy(string, buffer);
for (; index < maxArgs && string[0]; index++) {
char quoted;
char c;
// skip white space
while ((c = string[0]) != '\0' && isspace(c)) {
string++;
2002-07-09 12:24:59 +00:00
}
if (!c)
break;
if (c == '\'' || c == '"') {
argv[index] = ++string;
quoted = c;
} else {
argv[index] = string;
quoted = 0;
}
// find end of string
while (string[0]
&& ((quoted && string[0] != quoted)
|| (!quoted && !isspace(string[0])))) {
if (string[0] == '\\') {
// filter out backslashes
strcpy(string, string + 1);
string++;
}
string++;
}
if (string[0]) {
// terminate string
string[0] = '\0';
string++;
}
}
2002-07-09 12:24:59 +00:00
return *_argc = index;
2002-07-09 12:24:59 +00:00
}
/*! This function is a safe gate through which debugger commands are invoked.
It sets a fault handler before invoking the command, so that an invalid
memory access will not result in another KDL session on top of this one
(and "cont" not to work anymore). We use setjmp() + longjmp() to "unwind"
the stack after catching a fault.
*/
static int
invoke_command(struct debugger_command *command, int argc, char** argv)
{
struct thread* thread = thread_get_current_thread();
addr_t oldFaultHandler = thread->fault_handler;
// replace argv[0] with the actual command name
argv[0] = (char *)command->name;
// Invoking the command directly might be useful when debugging debugger
// commands.
if (sInvokeCommandDirectly)
return command->func(argc, argv);
if (setjmp(sInvokeCommandEnv) == 0) {
int result;
thread->fault_handler = (addr_t)&&error;
// Fake goto to trick the compiler not to optimize the code at the label
// away.
if (!thread)
goto error;
result = command->func(argc, argv);
thread->fault_handler = oldFaultHandler;
return result;
error:
longjmp(sInvokeCommandEnv, 1);
// jump into the else branch
} else {
kprintf("\n[*** READ/WRITE FAULT ***]\n");
}
thread->fault_handler = oldFaultHandler;
return 0;
}
static void
kernel_debugger_loop(void)
2002-07-09 12:24:59 +00:00
{
int32 previousCPU = sDebuggerOnCPU;
sDebuggerOnCPU = smp_get_current_cpu();
2002-07-09 12:24:59 +00:00
kprintf("Welcome to Kernel Debugging Land...\n");
kprintf("Running on CPU %ld\n", sDebuggerOnCPU);
for (;;) {
struct debugger_command *cmd = NULL;
int argc;
CommandLineEditingHelper editingHelper;
kprintf(kKDLPrompt);
read_line(sLineBuffer[sCurrentLine], LINE_BUFFER_SIZE, &editingHelper);
parse_line(sLineBuffer[sCurrentLine], sArguments, &argc, MAX_ARGS);
// We support calling last executed command again if
// B_KDEDUG_CONT was returned last time, so cmd != NULL
if (argc <= 0 && cmd == NULL)
2002-07-09 12:24:59 +00:00
continue;
sDebuggerOnCPU = smp_get_current_cpu();
2002-07-09 12:24:59 +00:00
bool ambiguous;
if (argc > 0)
cmd = find_command(sArguments[0], true, ambiguous);
if (cmd == NULL) {
if (ambiguous) {
kprintf("Ambiguous command. Use tab completion to get a list "
"of matching commands. Enter \"help\" to get a list of "
"all supported commands.\n");
} else {
kprintf("Unknown command. Enter \"help\" to get a list of all "
"supported commands.\n");
}
} else {
int rc = invoke_command(cmd, argc, sArguments);
if (rc == B_KDEBUG_QUIT)
break; // okay, exit now.
if (rc != B_KDEBUG_CONT)
cmd = NULL; // forget last command executed...
}
if (++sCurrentLine >= HISTORY_SIZE)
sCurrentLine = 0;
2002-07-09 12:24:59 +00:00
}
sDebuggerOnCPU = previousCPU;
2002-07-09 12:24:59 +00:00
}
static int
cmd_reboot(int argc, char **argv)
2002-07-09 12:24:59 +00:00
{
arch_cpu_shutdown(true);
return 0;
// I'll be really suprised if this line ever runs! ;-)
}
2002-07-09 12:24:59 +00:00
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_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 = sCommands; 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 : "-");
2002-07-09 12:24:59 +00:00
}
return 0;
}
static int
cmd_continue(int argc, char **argv)
2002-07-09 12:24:59 +00:00
{
return B_KDEBUG_QUIT;
}
2002-07-09 12:24:59 +00:00
static int
cmd_dump_kdl_message(int argc, char **argv)
{
if (sCurrentKernelDebuggerMessage) {
kputs(sCurrentKernelDebuggerMessage);
kputchar('\n');
}
return 0;
}
static status_t
syslog_sender(void *data)
{
status_t error = B_BAD_PORT_ID;
port_id port = -1;
bool bufferPending = false;
int32 length = 0;
while (true) {
// wait for syslog data to become available
acquire_sem(sSyslogNotify);
sSyslogMessage->when = real_time_clock();
if (error == B_BAD_PORT_ID) {
// last message couldn't be sent, try to locate the syslog_daemon
port = find_port(SYSLOG_PORT_NAME);
}
if (port >= B_OK) {
if (!bufferPending) {
// we need to have exclusive access to our syslog buffer
cpu_status state = disable_interrupts();
acquire_spinlock(&sSpinlock);
length = ring_buffer_readable(sSyslogBuffer);
2008-01-14 09:50:32 +00:00
if (length > (int32)SYSLOG_MAX_MESSAGE_LENGTH)
length = SYSLOG_MAX_MESSAGE_LENGTH;
2006-10-13 11:23:34 +00:00
length = ring_buffer_read(sSyslogBuffer,
(uint8 *)sSyslogMessage->message, length);
if (sSyslogDropped) {
// add drop marker
2008-01-14 09:50:32 +00:00
if (length < (int32)SYSLOG_MAX_MESSAGE_LENGTH - 6)
strlcat(sSyslogMessage->message, "<DROP>", SYSLOG_MAX_MESSAGE_LENGTH);
else if (length > 7)
strcpy(sSyslogMessage->message + length - 7, "<DROP>");
sSyslogDropped = false;
}
release_spinlock(&sSpinlock);
restore_interrupts(state);
}
if (length == 0) {
// the buffer we came here for might have been sent already
bufferPending = false;
continue;
}
error = write_port_etc(port, SYSLOG_MESSAGE, sSyslogMessage,
sizeof(struct syslog_message) + length, B_RELATIVE_TIMEOUT, 0);
if (error < B_OK) {
// sending has failed - just wait, maybe it'll work later.
bufferPending = true;
continue;
}
if (bufferPending) {
// we could write the last pending buffer, try to read more
// from the syslog ring buffer
release_sem_etc(sSyslogNotify, 1, B_DO_NOT_RESCHEDULE);
bufferPending = false;
}
}
}
return 0;
}
static void
syslog_write(const char *text, int32 length)
{
bool trunc = false;
if (sSyslogBuffer == NULL)
return;
2008-01-14 09:50:32 +00:00
if ((int32)ring_buffer_writable(sSyslogBuffer) < length) {
// truncate data
length = ring_buffer_writable(sSyslogBuffer);
if (length > 8) {
trunc = true;
length -= 8;
} else
sSyslogDropped = true;
}
2006-10-13 11:23:34 +00:00
ring_buffer_write(sSyslogBuffer, (uint8 *)text, length);
if (trunc)
2006-10-13 11:23:34 +00:00
ring_buffer_write(sSyslogBuffer, (uint8 *)"<TRUNC>", 7);
release_sem_etc(sSyslogNotify, 1, B_DO_NOT_RESCHEDULE);
}
static status_t
syslog_init_post_threads(void)
{
if (!sSyslogOutputEnabled)
return B_OK;
sSyslogNotify = create_sem(0, "syslog data");
if (sSyslogNotify >= B_OK) {
thread_id thread = spawn_kernel_thread(syslog_sender, "syslog sender",
B_LOW_PRIORITY, NULL);
if (thread >= B_OK && resume_thread(thread) == B_OK)
return B_OK;
}
// initializing kernel syslog service failed -- disable it
sSyslogOutputEnabled = false;
free(sSyslogMessage);
free(sSyslogBuffer);
delete_sem(sSyslogNotify);
return B_ERROR;
}
static status_t
syslog_init(struct kernel_args *args)
{
status_t status;
if (!sSyslogOutputEnabled)
return B_OK;
2008-01-14 09:50:32 +00:00
sSyslogMessage = (syslog_message*)malloc(SYSLOG_MESSAGE_BUFFER_SIZE);
if (sSyslogMessage == NULL) {
status = B_NO_MEMORY;
goto err1;
}
sSyslogBuffer = create_ring_buffer(SYSLOG_BUFFER_SIZE);
if (sSyslogBuffer == NULL) {
status = B_NO_MEMORY;
goto err2;
}
// initialize syslog message
sSyslogMessage->from = 0;
sSyslogMessage->options = LOG_KERN;
sSyslogMessage->priority = LOG_DEBUG;
sSyslogMessage->ident[0] = '\0';
//strcpy(sSyslogMessage->ident, "KERNEL");
if (args->debug_output != NULL)
2008-01-14 09:50:32 +00:00
syslog_write((const char*)args->debug_output, args->debug_size);
return B_OK;
err3:
free(sSyslogBuffer);
err2:
free(sSyslogMessage);
err1:
sSyslogOutputEnabled = false;
return status;
}
// #pragma mark - private kernel API
void
debug_stop_screen_debug_output(void)
{
sDebugScreenEnabled = false;
}
bool
debug_debugger_running(void)
{
return sDebuggerOnCPU != -1;
}
void
debug_puts(const char *string, int32 length)
{
cpu_status state = disable_interrupts();
acquire_spinlock(&sSpinlock);
if (length >= OUTPUT_BUFFER_SIZE)
length = OUTPUT_BUFFER_SIZE - 1;
if (length > 1 && string[length - 1] == '\n'
&& strncmp(string, sLastOutputBuffer, length) == 0) {
sMessageRepeatCount++;
sMessageRepeatLastTime = system_time();
if (sMessageRepeatFirstTime == 0)
sMessageRepeatFirstTime = sMessageRepeatLastTime;
} else {
flush_pending_repeats();
kputs(string);
// kputs() doesn't output to syslog (as it's only used
// from the kernel debugger elsewhere)
if (sSyslogOutputEnabled)
syslog_write(string, length);
memcpy(sLastOutputBuffer, string, length);
sLastOutputBuffer[length] = 0;
}
release_spinlock(&sSpinlock);
restore_interrupts(state);
}
void
debug_early_boot_message(const char *string)
{
arch_debug_serial_early_boot_message(string);
}
status_t
debug_init(kernel_args *args)
{
return arch_debug_console_init(args);
2002-07-09 12:24:59 +00:00
}
status_t
debug_init_post_vm(kernel_args *args)
2002-07-09 12:24:59 +00:00
{
void *handle;
add_debugger_command("help", &cmd_help, "List all debugger commands");
add_debugger_command("reboot", &cmd_reboot, "Reboot the system");
add_debugger_command("shutdown", &cmd_shutdown, "Shut down the system");
add_debugger_command("gdb", &cmd_gdb, "Connect to remote gdb");
add_debugger_command("exit", &cmd_continue, "Same as \"continue\"");
add_debugger_command("es", &cmd_continue, "Same as \"continue\"");
add_debugger_command("continue", &cmd_continue, "Leave kernel debugger");
add_debugger_command("message", &cmd_dump_kdl_message,
"Reprint the message printed when entering KDL");
2002-07-09 12:24:59 +00:00
frame_buffer_console_init(args);
arch_debug_console_init_settings(args);
2008-01-12 18:41:35 +00:00
tracing_init();
// get debug settings
handle = load_driver_settings("kernel");
if (handle != NULL) {
2006-01-24 21:52:53 +00:00
sSerialDebugEnabled = get_driver_boolean_parameter(handle,
2007-07-19 13:16:08 +00:00
"serial_debug_output", sSerialDebugEnabled, sSerialDebugEnabled);
2006-01-24 21:52:53 +00:00
sSyslogOutputEnabled = get_driver_boolean_parameter(handle,
2007-07-19 13:16:08 +00:00
"syslog_debug_output", sSyslogOutputEnabled, sSyslogOutputEnabled);
2006-01-24 21:52:53 +00:00
sBlueScreenOutput = get_driver_boolean_parameter(handle,
"bluescreen", true, true);
unload_driver_settings(handle);
}
2002-07-09 12:24:59 +00:00
handle = load_driver_settings(B_SAFEMODE_DRIVER_SETTINGS);
if (handle != NULL) {
sDebugScreenEnabled = get_driver_boolean_parameter(handle,
"debug_screen", false, false);
unload_driver_settings(handle);
}
if ((sBlueScreenOutput || sDebugScreenEnabled)
&& blue_screen_init() != B_OK)
sBlueScreenOutput = sDebugScreenEnabled = false;
if (sDebugScreenEnabled)
blue_screen_enter(true);
2005-12-27 20:49:28 +00:00
syslog_init(args);
return arch_debug_init(args);
2002-07-09 12:24:59 +00:00
}
status_t
debug_init_post_modules(struct kernel_args *args)
{
void *cookie;
// check for dupped lines every 10/10 second
register_kernel_daemon(check_pending_repeats, NULL, 10);
syslog_init_post_threads();
// load kernel debugger addons
cookie = open_module_list("debugger");
while (true) {
char name[B_FILE_NAME_LENGTH];
size_t nameLength = sizeof(name);
module_info *module;
if (read_next_module_name(cookie, name, &nameLength) != B_OK)
break;
if (get_module(name, &module) == B_OK)
dprintf("kernel debugger extention \"%s\": loaded\n", name);
else
dprintf("kernel debugger extention \"%s\": failed to load\n", name);
}
close_module_list(cookie);
return frame_buffer_console_init_post_modules(args);
}
// #pragma mark - public API
2002-07-09 12:24:59 +00:00
int
add_debugger_command(char *name, int (*func)(int, char **), char *desc)
2002-07-09 12:24:59 +00:00
{
cpu_status state;
2002-07-09 12:24:59 +00:00
struct debugger_command *cmd;
cmd = (struct debugger_command *)malloc(sizeof(struct debugger_command));
if (cmd == NULL)
2002-07-09 12:24:59 +00:00
return ENOMEM;
cmd->func = func;
cmd->name = name;
2002-07-09 12:24:59 +00:00
cmd->description = desc;
state = disable_interrupts();
acquire_spinlock(&sSpinlock);
2002-07-09 12:24:59 +00:00
cmd->next = sCommands;
sCommands = cmd;
2002-07-09 12:24:59 +00:00
release_spinlock(&sSpinlock);
restore_interrupts(state);
2002-07-09 12:24:59 +00:00
return B_NO_ERROR;
}
int
remove_debugger_command(char * name, int (*func)(int, char **))
{
struct debugger_command *cmd = sCommands;
struct debugger_command *prev = NULL;
cpu_status state;
state = disable_interrupts();
acquire_spinlock(&sSpinlock);
while (cmd) {
if (!strcmp(cmd->name, name) && cmd->func == func)
break;
prev = cmd;
cmd = cmd->next;
}
if (cmd) {
if (cmd == sCommands)
sCommands = cmd->next;
else
prev->next = cmd->next;
}
release_spinlock(&sSpinlock);
restore_interrupts(state);
if (cmd) {
free(cmd);
return B_NO_ERROR;
}
return B_NAME_NOT_FOUND;
}
uint32
parse_expression(const char *expression)
{
2007-03-27 13:04:50 +00:00
// TODO: Implement expression parser (cf. BeBook).
return strtoul(expression, NULL, 0);
}
void
panic(const char *format, ...)
2002-07-09 12:24:59 +00:00
{
va_list args;
char temp[128];
va_start(args, format);
vsnprintf(temp, sizeof(temp), format, args);
va_end(args);
kernel_debugger(temp);
}
void
kernel_debugger(const char *message)
{
static vint32 inDebugger;
cpu_status state;
bool dprintfState;
2002-07-09 12:24:59 +00:00
state = disable_interrupts();
atomic_add(&inDebugger, 1);
arch_debug_save_registers(&dbg_register_file[smp_get_current_cpu()][0]);
dprintfState = set_dprintf_enabled(true);
if (sDebuggerOnCPU != smp_get_current_cpu()) {
// First entry, halt all of the other cpus
// XXX need to flush current smp mailbox to make sure this goes
// through. Otherwise it'll hang
smp_send_broadcast_ici(SMP_MSG_CPU_HALT, 0, 0, 0, (void *)&inDebugger,
SMP_MSG_FLAG_SYNC);
}
2002-07-09 12:24:59 +00:00
2006-01-24 21:52:53 +00:00
if (sBlueScreenOutput) {
if (blue_screen_enter(false) == B_OK)
sBlueScreenEnabled = true;
}
if (message)
kprintf("PANIC: %s\n", message);
sCurrentKernelDebuggerMessage = message;
// bubble sort the commands
debugger_command* stopCommand = NULL;
while (stopCommand != sCommands) {
debugger_command** command = &sCommands;
while (true) {
debugger_command* nextCommand = (*command)->next;
if (nextCommand == stopCommand) {
stopCommand = *command;
break;
}
if (strcmp((*command)->name, nextCommand->name) > 0) {
debugger_command* tmpCommand = nextCommand->next;
(*command)->next = nextCommand->next;
nextCommand->next = *command;
*command = nextCommand;
}
command = &(*command)->next;
}
}
kernel_debugger_loop();
set_dprintf_enabled(dprintfState);
2006-01-24 21:52:53 +00:00
sBlueScreenEnabled = false;
atomic_add(&inDebugger, -1);
restore_interrupts(state);
// ToDo: in case we change dbg_register_file - don't we want to restore it?
2002-07-09 12:24:59 +00:00
}
bool
set_dprintf_enabled(bool newState)
2002-07-09 12:24:59 +00:00
{
bool oldState = sSerialDebugEnabled;
sSerialDebugEnabled = newState;
return oldState;
2002-07-09 12:24:59 +00:00
}
static void
flush_pending_repeats(void)
{
if (sMessageRepeatCount > 0) {
int32 length;
if (sMessageRepeatCount > 1) {
static char temp[40];
length = snprintf(temp, sizeof(temp),
"Last message repeated %ld times.\n", sMessageRepeatCount);
if (sSerialDebugEnabled)
arch_debug_serial_puts(temp);
if (sSyslogOutputEnabled)
syslog_write(temp, length);
if (sBlueScreenEnabled || sDebugScreenEnabled)
blue_screen_puts(temp);
} else {
// if we only have one repeat just reprint the last buffer
if (sSerialDebugEnabled)
arch_debug_serial_puts(sLastOutputBuffer);
if (sSyslogOutputEnabled)
syslog_write(sLastOutputBuffer, strlen(sLastOutputBuffer));
if (sBlueScreenEnabled || sDebugScreenEnabled)
blue_screen_puts(sLastOutputBuffer);
}
sMessageRepeatFirstTime = 0;
sMessageRepeatCount = 0;
}
}
static void
check_pending_repeats(void *data, int iter)
{
(void)data;
(void)iter;
if (sMessageRepeatCount > 0
&& ((system_time() - sMessageRepeatLastTime) > 1000000
|| (system_time() - sMessageRepeatFirstTime) > 3000000)) {
cpu_status state = disable_interrupts();
acquire_spinlock(&sSpinlock);
flush_pending_repeats();
release_spinlock(&sSpinlock);
restore_interrupts(state);
}
}
static void
dprintf_args(const char *format, va_list args, bool syslogOutput)
2002-07-09 12:24:59 +00:00
{
cpu_status state;
int32 length;
// ToDo: maybe add a non-interrupt buffer and path that only
// needs to acquire a semaphore instead of needing to disable
// interrupts?
state = disable_interrupts();
acquire_spinlock(&sSpinlock);
length = vsnprintf(sOutputBuffer, OUTPUT_BUFFER_SIZE, format, args);
if (length >= OUTPUT_BUFFER_SIZE)
length = OUTPUT_BUFFER_SIZE - 1;
if (length > 1 && sOutputBuffer[length - 1] == '\n'
&& strncmp(sOutputBuffer, sLastOutputBuffer, length) == 0) {
sMessageRepeatCount++;
sMessageRepeatLastTime = system_time();
if (sMessageRepeatFirstTime == 0)
sMessageRepeatFirstTime = sMessageRepeatLastTime;
} else {
flush_pending_repeats();
if (sSerialDebugEnabled)
arch_debug_serial_puts(sOutputBuffer);
if (syslogOutput)
syslog_write(sOutputBuffer, length);
if (sBlueScreenEnabled || sDebugScreenEnabled)
blue_screen_puts(sOutputBuffer);
memcpy(sLastOutputBuffer, sOutputBuffer, length);
sLastOutputBuffer[length] = 0;
}
release_spinlock(&sSpinlock);
restore_interrupts(state);
2002-07-09 12:24:59 +00:00
}
void
dprintf(const char *format, ...)
{
va_list args;
if (!sSerialDebugEnabled && !sSyslogOutputEnabled && !sBlueScreenEnabled)
return;
va_start(args, format);
dprintf_args(format, args, sSyslogOutputEnabled);
va_end(args);
}
void
dprintf_no_syslog(const char *format, ...)
{
va_list args;
if (!sSerialDebugEnabled && !sBlueScreenEnabled)
return;
va_start(args, format);
dprintf_args(format, args, false);
va_end(args);
}
/** Similar to dprintf() but thought to be used in the kernel
* debugger only (it doesn't lock).
*/
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);
flush_pending_repeats();
kputs(sOutputBuffer);
}
// #pragma mark -
// userland syscalls
void
_user_debug_output(const char *userString)
{
char string[512];
int32 length;
if (!sSerialDebugEnabled)
return;
if (!IS_USER_ADDRESS(userString))
return;
do {
length = user_strlcpy(string, userString, sizeof(string));
debug_puts(string, length);
userString += sizeof(string) - 1;
} while (length >= (ssize_t)sizeof(string));
}
void
dump_block(const char *buffer, int size, const char *prefix)
{
const int DUMPED_BLOCK_SIZE = 16;
int i;
for (i = 0; i < size;) {
int start = i;
dprintf("%s%04x ", prefix, i);
for (; i < start + DUMPED_BLOCK_SIZE; i++) {
if (!(i % 4))
dprintf(" ");
if (i >= size)
dprintf(" ");
else
dprintf("%02x", *(unsigned char *)(buffer + i));
}
dprintf(" ");
for (i = start; i < start + DUMPED_BLOCK_SIZE; i++) {
if (i < size) {
char c = buffer[i];
if (c < 30)
dprintf(".");
else
dprintf("%c", c);
} else
break;
}
dprintf("\n");
}
}