#include "blue_screen.h"
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <algorithm>
#include <AutoDeleter.h>
#include <boot/kernel_args.h>
#include <cpu.h>
#include <debug.h>
#include <debug_heap.h>
#include <debug_paranoia.h>
#include <driver_settings.h>
#include <frame_buffer_console.h>
#include <interrupts.h>
#include <kernel.h>
#include <ksystem_info.h>
#include <safemode.h>
#include <smp.h>
#include <thread.h>
#include <tracing.h>
#include <vm/vm.h>
#include <vm/VMTranslationMap.h>
#include <arch/debug_console.h>
#include <arch/debug.h>
#include <util/AutoLock.h>
#include <util/ring_buffer.h>
#include <syslog_daemon.h>
#include "debug_builtin_commands.h"
#include "debug_commands.h"
#include "debug_output_filter.h"
#include "debug_variables.h"
#if __GNUC__ == 2
# define va_copy(to, from) __va_copy(to, from)
#endif
struct debug_memcpy_parameters {
void* to;
const void* from;
size_t size;
};
struct debug_strlcpy_parameters {
char* to;
const char* from;
size_t size;
size_t result;
};
static const char* const kKDLPrompt = "kdebug> ";
static const char* const kKDLMessageCommandSeparator = "@!";
extern "C" int kgets(char* buffer, int length);
void call_modules_hook(bool enter);
static void syslog_write(const char* text, int32 length, bool notify);
static arch_debug_registers sDebugRegisters[SMP_MAX_CPUS];
static debug_page_fault_info sPageFaultInfo;
static bool sSerialDebugEnabled = true;
static bool sSyslogOutputEnabled = true;
static bool sBlueScreenEnabled = false;
static bool sDebugScreenEnabled = false;
static bool sBlueScreenOutput = true;
static bool sEmergencyKeysEnabled = true;
static spinlock sSpinlock = B_SPINLOCK_INITIALIZER;
static int32 sDebuggerOnCPU = -1;
static sem_id sSyslogNotify = -1;
static thread_id sSyslogWriter = -1;
static port_id sSyslogPort = -1;
static struct syslog_message* sSyslogMessage;
static struct ring_buffer* sSyslogBuffer;
static size_t sSyslogBufferOffset = 0;
static bool sSyslogDropped = false;
static bool sDebugSyslog = false;
static size_t sSyslogDebuggerOffset = 0;
static void* sPreviousSessionSyslogBuffer = NULL;
static size_t sPreviousSessionSyslogBufferSize = 0;
static const char* sCurrentKernelDebuggerMessagePrefix;
static const char* sCurrentKernelDebuggerMessage;
static va_list sCurrentKernelDebuggerMessageArgs;
#define DEFAULT_SYSLOG_BUFFER_SIZE 65536
#define OUTPUT_BUFFER_SIZE 1024
static char sOutputBuffer[OUTPUT_BUFFER_SIZE];
static char sInterruptOutputBuffer[OUTPUT_BUFFER_SIZE];
static char sLastOutputBuffer[OUTPUT_BUFFER_SIZE];
static DebugOutputFilter* sDebugOutputFilter = NULL;
DefaultDebugOutputFilter gDefaultDebugOutputFilter;
static mutex sOutputLock = MUTEX_INITIALIZER("debug output");
static void flush_pending_repeats(bool notifySyslog);
static void check_pending_repeats(void* data, int iter);
static int64 sMessageRepeatFirstTime = 0;
static int64 sMessageRepeatLastTime = 0;
static int32 sMessageRepeatCount = 0;
static debugger_module_info* sDebuggerModules[8];
static const uint32 kMaxDebuggerModules = sizeof(sDebuggerModules)
/ sizeof(sDebuggerModules[0]);
#define LINE_BUFFER_SIZE 1024
#define HISTORY_SIZE 16
static char sLineBuffer[HISTORY_SIZE][LINE_BUFFER_SIZE] = { "", };
static int32 sCurrentLine = 0;
static debugger_demangle_module_info* sDemangleModule;
static Thread* sDebuggedThread;
static int32 sInDebugger = 0;
static bool sPreviousDprintfState;
static volatile bool sHandOverKDL = false;
static int32 sHandOverKDLToCPU = -1;
static bool sCPUTrapped[SMP_MAX_CPUS];
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)
{
size_t length = strlen(string);
if (sSerialDebugEnabled)
arch_debug_serial_puts(string);
if (sSyslogOutputEnabled)
syslog_write(string, length, false);
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, length);
}
}
void
DefaultDebugOutputFilter::Print(const char* format, va_list args)
{
vsnprintf(sInterruptOutputBuffer, OUTPUT_BUFFER_SIZE, format, args);
flush_pending_repeats(false);
PrintString(sInterruptOutputBuffer);
}
DebugOutputFilter*
set_debug_output_filter(DebugOutputFilter* filter)
{
DebugOutputFilter* oldFilter = sDebugOutputFilter;
sDebugOutputFilter = filter;
return oldFilter;
}
static void
kputchar(char c)
{
if (sSerialDebugEnabled)
arch_debug_serial_putchar(c);
if (sBlueScreenEnabled || sDebugScreenEnabled)
blue_screen_putchar(c);
for (uint32 i = 0; sSerialDebugEnabled && i < kMaxDebuggerModules; i++)
if (sDebuggerModules[i] && sDebuggerModules[i]->debugger_puts)
sDebuggerModules[i]->debugger_puts(&c, sizeof(c));
}
void
kputs(const char* s)
{
if (sDebugOutputFilter != NULL)
sDebugOutputFilter->PrintString(s);
}
void
kputs_unfiltered(const char* s)
{
gDefaultDebugOutputFilter.PrintString(s);
}
static void
insert_chars_into_line(char* buffer, int32& position, int32& length,
const char* chars, int32 charCount)
{
if (position < length) {
memmove(buffer + position + charCount, buffer + position,
length - position);
}
memcpy(buffer + position, chars, charCount);
int32 oldPosition = position;
position += charCount;
length += charCount;
kprintf("%.*s", (int)(length - oldPosition),
buffer + oldPosition);
if (position < length)
kprintf("\x1b[%" B_PRId32 "D", 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);
}
static void
remove_char_from_line(char* buffer, int32& position, int32& length)
{
if (position == length)
return;
length--;
if (position < length) {
memmove(buffer + position, buffer + position + 1, length - position);
for (int32 i = position; i < length; i++)
kputchar(buffer[i]);
}
kputchar(' ');
kprintf("\x1b[%" B_PRId32 "D", 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)
{
char tmpChar = buffer[position];
buffer[position] = '\0';
char* firstSpace = strchr(buffer, ' ');
buffer[position] = tmpChar;
bool reprintLine = false;
if (firstSpace != NULL) {
tmpChar = *firstSpace;
*firstSpace = '\0';
bool ambiguous;
debugger_command* command = find_debugger_command(buffer, true, ambiguous);
*firstSpace = tmpChar;
if (command != NULL) {
kputs("\n");
print_debugger_command_usage(command->name);
} else {
if (ambiguous)
kprintf("\nambiguous command\n");
else
kprintf("\nno such command\n");
}
reprintLine = true;
} else {
int32 count = 0;
int32 longestName = 0;
debugger_command* command = NULL;
int32 longestCommonPrefix = 0;
const char* previousCommandName = NULL;
while ((command = next_debugger_command(command, buffer, position))
!= NULL) {
count++;
int32 nameLength = strlen(command->name);
longestName = max_c(longestName, nameLength);
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) {
kprintf("\nno completions\n");
reprintLine = true;
} else if (count == 1) {
command = next_debugger_command(NULL, buffer, position);
int32 neededSpace = longestName - position + 1;
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) {
int32 neededSpace = longestCommonPrefix - position;
if (length + neededSpace + 1 >= capacity)
return;
insert_chars_into_line(buffer, position, length,
previousCommandName + position, neededSpace);
} else {
kprintf("\n");
reprintLine = true;
int columns = 80 / (longestName + 2);
debugger_command* command = NULL;
int column = 0;
while ((command = next_debugger_command(command, buffer, position))
!= NULL) {
if (column > 0 && column % columns == 0)
kputs("\n");
column++;
kprintf(" %-*s", (int)longestName, command->name);
}
kputs("\n");
}
}
if (reprintLine) {
kprintf("%s%.*s", kKDLPrompt, (int)length, buffer);
if (position < length)
kprintf("\x1b[%" B_PRId32 "D", length - position);
}
}
};
static int
read_line(char* buffer, int32 maxLength,
LineEditingHelper* editingHelper = NULL)
{
int32 currentHistoryLine = sCurrentLine;
int32 position = 0;
int32 length = 0;
bool done = false;
char c = 0;
while (!done) {
c = kgetc();
switch (c) {
case '\n':
case '\r':
buffer[length++] = '\0';
kputs("\n");
done = true;
break;
case '\t':
{
if (editingHelper != NULL) {
editingHelper->TabCompletion(buffer, maxLength,
position, length);
}
break;
}
case 8:
case 0x7f:
if (position > 0) {
kputs("\x1b[1D");
position--;
remove_char_from_line(buffer, position, length);
}
break;
case 0x1f & 'D':
length = 0;
buffer[length++] = 'e';
buffer[length++] = 's';
buffer[length++] = '\0';
kputchar('\n');
done = true;
break;
case 0x1f & 'K':
if (position < length) {
for (int32 i = position; i < length; i++)
kputchar(' ');
kprintf("\x1b[%" B_PRId32 "D", length - position);
length = position;
}
break;
case 0x1f & 'L':
if (sBlueScreenOutput) {
blue_screen_clear_screen();
buffer[length] = '\0';
blue_screen_puts(kKDLPrompt);
blue_screen_puts(buffer);
if (position < length) {
for (int i = length; i > position; i--)
blue_screen_puts("\x1b[1D");
}
}
break;
case 27:
c = kgetc();
if (c != '[') {
break;
}
c = kgetc();
switch (c) {
case 'C':
if (position < length) {
kputs("\x1b[1C");
position++;
}
break;
case 'D':
if (position > 0) {
kputs("\x1b[1D");
position--;
}
break;
case 'A':
case 'B':
{
int32 historyLine = 0;
if (c == 'A') {
historyLine = currentHistoryLine - 1;
if (historyLine < 0)
historyLine = HISTORY_SIZE - 1;
} else {
if (currentHistoryLine == sCurrentLine)
break;
historyLine = currentHistoryLine + 1;
if (historyLine >= HISTORY_SIZE)
historyLine = 0;
}
if (historyLine == sCurrentLine) {
sLineBuffer[historyLine][0] = '\0';
} else if (sLineBuffer[historyLine][0] == '\0') {
break;
}
if (position > 0)
kprintf("\x1b[%" B_PRId32 "D", position);
strcpy(buffer, sLineBuffer[historyLine]);
length = position = strlen(buffer);
kprintf("%s\x1b[K", buffer);
currentHistoryLine = historyLine;
break;
}
case '5':
case '6':
{
if (kgetc() != '~')
break;
int32 searchDirection = (c == '5' ? -1 : 1);
bool found = false;
int32 historyLine = currentHistoryLine;
do {
historyLine = (historyLine + searchDirection
+ HISTORY_SIZE) % HISTORY_SIZE;
if (historyLine == sCurrentLine)
break;
if (strncmp(sLineBuffer[historyLine], buffer,
position) == 0) {
found = true;
}
} while (!found);
if (!found || strlen(sLineBuffer[historyLine]) == 0)
break;
strcpy(buffer, sLineBuffer[historyLine]);
length = strlen(buffer);
kprintf("%s\x1b[K", buffer + position);
kprintf("\x1b[%" B_PRId32 "D", length - position);
currentHistoryLine = historyLine;
break;
}
case 'H':
{
if (position > 0) {
kprintf("\x1b[%" B_PRId32 "D", position);
position = 0;
}
break;
}
case 'F':
{
if (position < length) {
kprintf("\x1b[%" B_PRId32 "C", length - position);
position = length;
}
break;
}
case '3':
{
if (kgetc() != '~')
break;
if (position < length)
remove_char_from_line(buffer, position, length);
break;
}
default:
break;
}
break;
case '$':
case '+':
if (!sBlueScreenOutput) {
if (position == 0) {
strcpy(buffer, "gdb");
position = 4;
done = true;
break;
}
}
default:
if (isprint(c))
insert_char_into_line(buffer, position, length, c);
break;
}
if (length >= maxLength - 2) {
buffer[length++] = '\0';
kputs("\n");
done = true;
break;
}
}
return length;
}
char
kgetc(void)
{
while (true) {
int c = arch_debug_serial_try_getchar();
if (c >= 0)
return (char)c;
if (sBlueScreenOutput) {
c = blue_screen_try_getchar();
if (c >= 0)
return (char)c;
}
for (uint32 i = 0; i < kMaxDebuggerModules; i++) {
if (sDebuggerModules[i] && sDebuggerModules[i]->debugger_getchar) {
int getChar = sDebuggerModules[i]->debugger_getchar();
if (getChar >= 0)
return (char)getChar;
}
}
arch_debug_snooze(5000);
}
}
int
kgets(char* buffer, int length)
{
return read_line(buffer, length);
}
static void
print_kernel_debugger_message()
{
if (sCurrentKernelDebuggerMessagePrefix != NULL
|| sCurrentKernelDebuggerMessage != NULL) {
if (sCurrentKernelDebuggerMessagePrefix != NULL)
kprintf("%s", sCurrentKernelDebuggerMessagePrefix);
if (sCurrentKernelDebuggerMessage != NULL
&& sDebugOutputFilter != NULL) {
va_list args;
va_copy(args, sCurrentKernelDebuggerMessageArgs);
if (const char* commandDelimiter = strstr(
sCurrentKernelDebuggerMessage,
kKDLMessageCommandSeparator)) {
if (commandDelimiter != sCurrentKernelDebuggerMessage) {
size_t length = commandDelimiter
- sCurrentKernelDebuggerMessage;
if (char* format = (char*)debug_malloc(length + 1)) {
memcpy(format, sCurrentKernelDebuggerMessage, length);
format[length] = '\0';
sDebugOutputFilter->Print(format, args);
debug_free(format);
} else {
sDebugOutputFilter->Print(sCurrentKernelDebuggerMessage,
args);
}
}
} else
sDebugOutputFilter->Print(sCurrentKernelDebuggerMessage, args);
va_end(args);
}
kprintf("\n");
}
}
static void
execute_panic_commands()
{
if (sCurrentKernelDebuggerMessage == NULL
|| strstr(sCurrentKernelDebuggerMessage,
kKDLMessageCommandSeparator) == NULL) {
return;
}
const size_t kCommandBufferSize = 512;
char* commandBuffer = (char*)debug_malloc(kCommandBufferSize);
if (commandBuffer != NULL) {
va_list tempArgs;
va_copy(tempArgs, sCurrentKernelDebuggerMessageArgs);
if (vsnprintf(commandBuffer, kCommandBufferSize,
sCurrentKernelDebuggerMessage, tempArgs)
< (int)kCommandBufferSize) {
const char* commands = strstr(commandBuffer,
kKDLMessageCommandSeparator);
if (commands != NULL) {
commands += strlen(kKDLMessageCommandSeparator);
kprintf("initial commands: %s\n", commands);
evaluate_debug_command(commands);
}
}
va_end(tempArgs);
debug_free(commandBuffer);
}
}
static void
stack_trace_trampoline(void*)
{
arch_debug_stack_trace();
}
static void
kernel_debugger_loop(const char* messagePrefix, const char* message,
va_list args, int32 cpu)
{
DebugAllocPool* allocPool = create_debug_alloc_pool();
sCurrentKernelDebuggerMessagePrefix = messagePrefix;
sCurrentKernelDebuggerMessage = message;
if (sCurrentKernelDebuggerMessage != NULL)
va_copy(sCurrentKernelDebuggerMessageArgs, args);
sSyslogDebuggerOffset = sSyslogBuffer != NULL
? ring_buffer_readable(sSyslogBuffer) : 0;
print_kernel_debugger_message();
kprintf("Welcome to Kernel Debugging Land...\n");
kprintf("revision: %s\n", get_haiku_revision());
set_debug_variable("_cpu", sDebuggerOnCPU);
Thread* thread = thread_get_current_thread();
if (thread == NULL) {
kprintf("Running on CPU %" B_PRId32 "\n", sDebuggerOnCPU);
} else if (!debug_is_kernel_memory_accessible((addr_t)thread,
sizeof(Thread), B_KERNEL_READ_AREA)) {
kprintf("Running on CPU %" B_PRId32 "\n", sDebuggerOnCPU);
kprintf("Current thread pointer is %p, which is an address we "
"can't read from.\n", thread);
arch_debug_unset_current_thread();
} else {
set_debug_variable("_thread", (uint64)(addr_t)thread);
set_debug_variable("_threadID", thread->id);
kprintf("Thread %" B_PRId32 " \"%.64s\" running on CPU %" B_PRId32 "\n",
thread->id, thread->name, sDebuggerOnCPU);
if (thread->cpu != gCPU + cpu) {
kprintf("The thread's CPU pointer is %p, but should be %p.\n",
thread->cpu, gCPU + cpu);
arch_debug_unset_current_thread();
} else if (thread->team != NULL) {
if (debug_is_kernel_memory_accessible((addr_t)thread->team,
sizeof(Team), B_KERNEL_READ_AREA)) {
set_debug_variable("_team", (uint64)(addr_t)thread->team);
set_debug_variable("_teamID", thread->team->id);
} else {
kprintf("The thread's team pointer is %p, which is an "
"address we can't read from.\n", thread->team);
arch_debug_unset_current_thread();
}
}
}
if (!has_debugger_command("help") || message != NULL) {
jmp_buf* jumpBuffer = (jmp_buf*)debug_malloc(sizeof(jmp_buf));
if (jumpBuffer != NULL) {
debug_call_with_fault_handler(*jumpBuffer, &stack_trace_trampoline,
NULL);
debug_free(jumpBuffer);
} else
arch_debug_stack_trace();
}
if (has_debugger_command("help")) {
bool pagingEnabled = blue_screen_paging_enabled();
blue_screen_set_paging(false);
execute_panic_commands();
blue_screen_set_paging(pagingEnabled);
}
int32 continuableLine = -1;
for (;;) {
CommandLineEditingHelper editingHelper;
kprintf(kKDLPrompt);
char* line = sLineBuffer[sCurrentLine];
read_line(line, LINE_BUFFER_SIZE, &editingHelper);
bool whiteSpaceOnly = true;
for (int i = 0 ; line[i] != '\0'; i++) {
if (!isspace(line[i])) {
whiteSpaceOnly = false;
break;
}
}
if (whiteSpaceOnly) {
if (continuableLine < 0)
continue;
sCurrentLine = continuableLine;
line = sLineBuffer[sCurrentLine];
}
int rc = evaluate_debug_command(line);
if (rc == B_KDEBUG_QUIT) {
break;
}
continuableLine = (rc == B_KDEBUG_CONT ? sCurrentLine : -1);
int previousLine = sCurrentLine - 1;
if (previousLine < 0)
previousLine = HISTORY_SIZE - 1;
if (strcmp(sLineBuffer[sCurrentLine], sLineBuffer[previousLine])) {
if (++sCurrentLine >= HISTORY_SIZE)
sCurrentLine = 0;
}
}
if (sCurrentKernelDebuggerMessage != NULL)
va_end(sCurrentKernelDebuggerMessageArgs);
delete_debug_alloc_pool(allocPool);
}
static void
enter_kernel_debugger(int32 cpu, int32& previousCPU)
{
while (atomic_add(&sInDebugger, 1) > 0) {
atomic_add(&sInDebugger, -1);
if (sDebuggerOnCPU == cpu) {
break;
}
smp_intercpu_interrupt_handler(cpu);
}
arch_debug_save_registers(&sDebugRegisters[cpu]);
sPreviousDprintfState = set_dprintf_enabled(true);
if (!gKernelStartup && sDebuggerOnCPU != cpu && smp_get_num_cpus() > 1) {
CPUSet cpuMask;
cpuMask.SetAll();
cpuMask.ClearBit(cpu);
smp_multicast_ici_interrupts_disabled(cpu, cpuMask, SMP_MSG_CPU_HALT, 0, 0,
0, NULL, SMP_MSG_FLAG_SYNC);
}
previousCPU = sDebuggerOnCPU;
sDebuggerOnCPU = cpu;
if (sBlueScreenOutput) {
if (blue_screen_enter(false) == B_OK)
sBlueScreenEnabled = true;
}
sDebugOutputFilter = &gDefaultDebugOutputFilter;
sDebuggedThread = NULL;
sort_debugger_commands();
call_modules_hook(true);
}
static void
exit_kernel_debugger()
{
call_modules_hook(false);
set_dprintf_enabled(sPreviousDprintfState);
sDebugOutputFilter = NULL;
sBlueScreenEnabled = false;
if (sDebugScreenEnabled)
blue_screen_enter(true);
atomic_add(&sInDebugger, -1);
}
static void
hand_over_kernel_debugger()
{
sHandOverKDL = true;
while (atomic_get(&sHandOverKDLToCPU) >= 0)
cpu_wait(&sHandOverKDLToCPU, -1);
}
static void
kernel_debugger_internal(const char* messagePrefix, const char* message,
va_list args, int32 cpu)
{
while (true) {
int32 previousCPU = -1;
if (sHandOverKDLToCPU == cpu) {
sHandOverKDLToCPU = -1;
sHandOverKDL = false;
previousCPU = sDebuggerOnCPU;
sDebuggerOnCPU = cpu;
} else
enter_kernel_debugger(cpu, previousCPU);
kernel_debugger_loop(messagePrefix, message, args, cpu);
if (sHandOverKDLToCPU < 0 && previousCPU == -1) {
exit_kernel_debugger();
}
sDebuggerOnCPU = previousCPU;
if (sHandOverKDLToCPU < 0)
break;
hand_over_kernel_debugger();
debug_trap_cpu_in_kdl(cpu, true);
if (sHandOverKDLToCPU != cpu)
break;
}
}
static int
cmd_dump_kdl_message(int argc, char** argv)
{
print_kernel_debugger_message();
return 0;
}
static int
cmd_execute_panic_commands(int argc, char** argv)
{
execute_panic_commands();
return 0;
}
static int
cmd_dump_syslog(int argc, char** argv)
{
if (!sSyslogOutputEnabled) {
kprintf("Syslog is not enabled.\n");
return 0;
}
bool unsentOnly = false;
bool ignoreKDLOutput = true;
int argi = 1;
for (; argi < argc; argi++) {
if (strcmp(argv[argi], "-n") == 0)
unsentOnly = true;
else if (strcmp(argv[argi], "-k") == 0)
ignoreKDLOutput = false;
else
break;
}
if (argi < argc) {
print_debugger_command_usage(argv[0]);
return 0;
}
size_t debuggerOffset = sSyslogDebuggerOffset;
size_t start = unsentOnly ? sSyslogBufferOffset : 0;
size_t end = ignoreKDLOutput
? debuggerOffset : ring_buffer_readable(sSyslogBuffer);
size_t bufferSize = 1024;
char* buffer = (char*)debug_malloc(bufferSize);
char stackBuffer[64];
if (buffer == NULL) {
buffer = stackBuffer;
bufferSize = sizeof(stackBuffer);
}
bool newLine = false;
while (start < end) {
size_t bytesRead = ring_buffer_peek(sSyslogBuffer, start, buffer,
std::min(end - start, bufferSize - 1));
if (bytesRead == 0)
break;
start += bytesRead;
size_t toPrint = 0;
for (size_t i = 0; i < bytesRead; i++) {
if (buffer[i] != '\0' && (uint8)buffer[i] != 0xcc)
buffer[toPrint++] = buffer[i];
}
if (toPrint > 0) {
newLine = buffer[toPrint - 1] == '\n';
buffer[toPrint] = '\0';
kputs(buffer);
}
if (debuggerOffset > sSyslogDebuggerOffset) {
size_t diff = debuggerOffset - sSyslogDebuggerOffset;
start -= std::min(start, diff);
end -= std::min(end, diff);
debuggerOffset = sSyslogDebuggerOffset;
}
}
if (!newLine)
kputs("\n");
if (buffer != stackBuffer)
debug_free(buffer);
return 0;
}
static int
cmd_switch_cpu(int argc, char** argv)
{
if (argc > 2) {
print_debugger_command_usage(argv[0]);
return 0;
}
if (argc == 1) {
kprintf("running on CPU %" B_PRId32 "\n", smp_get_current_cpu());
return 0;
}
int32 newCPU = parse_expression(argv[1]);
if (newCPU < 0 || newCPU >= smp_get_num_cpus()) {
kprintf("invalid CPU index\n");
return 0;
}
if (newCPU == smp_get_current_cpu()) {
kprintf("already running on CPU %" B_PRId32 "\n", newCPU);
return 0;
}
sHandOverKDLToCPU = newCPU;
return B_KDEBUG_QUIT;
}
static status_t
syslog_sender(void* data)
{
bool bufferPending = false;
int32 length = 0;
while (true) {
acquire_sem_etc(sSyslogNotify, 1, B_RELATIVE_TIMEOUT, 5000000);
sSyslogMessage->when = real_time_clock();
if (!bufferPending) {
MutexLocker mutexLocker(sOutputLock);
InterruptsSpinLocker spinLocker(sSpinlock);
length = ring_buffer_readable(sSyslogBuffer)
- sSyslogBufferOffset;
if (length > (int32)SYSLOG_MAX_MESSAGE_LENGTH)
length = SYSLOG_MAX_MESSAGE_LENGTH;
uint8* message = (uint8*)sSyslogMessage->message;
if (sSyslogDropped) {
memcpy(message, "<DROP>", 6);
message += 6;
if ((length + 6) > (int32)SYSLOG_MAX_MESSAGE_LENGTH)
length -= 6;
sSyslogDropped = false;
}
length = ring_buffer_peek(sSyslogBuffer, sSyslogBufferOffset,
message, length);
sSyslogBufferOffset += length;
length += (addr_t)message - (addr_t)sSyslogMessage->message;
}
if (length == 0) {
bufferPending = false;
continue;
}
status_t status = write_port_etc(sSyslogPort, SYSLOG_MESSAGE,
sSyslogMessage, sizeof(struct syslog_message) + length,
B_RELATIVE_TIMEOUT, 0);
if (status == B_BAD_PORT_ID) {
sSyslogWriter = -1;
return status;
}
if (status != B_OK) {
bufferPending = true;
continue;
}
if (bufferPending) {
release_sem_etc(sSyslogNotify, 1, B_DO_NOT_RESCHEDULE);
bufferPending = false;
}
}
return 0;
}
static void
syslog_write(const char* text, int32 length, bool notify)
{
if (sSyslogBuffer == NULL)
return;
if (length > sSyslogBuffer->size) {
syslog_write("<TRUNC>", 7, false);
text += length - (sSyslogBuffer->size - 7);
length = sSyslogBuffer->size - 7;
}
int32 writable = ring_buffer_writable(sSyslogBuffer);
if (writable < length) {
size_t toDrop = length - writable;
ring_buffer_flush(sSyslogBuffer, toDrop);
if (toDrop > sSyslogBufferOffset) {
sSyslogBufferOffset = 0;
sSyslogDropped = true;
} else
sSyslogBufferOffset -= toDrop;
sSyslogDebuggerOffset -= std::min(toDrop, sSyslogDebuggerOffset);
}
ring_buffer_write(sSyslogBuffer, (uint8*)text, length);
if (notify)
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 >= 0)
return B_OK;
sSyslogOutputEnabled = false;
if (sSyslogBuffer != NULL) {
if (sDebugSyslog)
delete_area(area_for(sSyslogBuffer));
else
delete_ring_buffer(sSyslogBuffer);
sSyslogBuffer = NULL;
}
free(sSyslogMessage);
delete_sem(sSyslogNotify);
return B_ERROR;
}
static status_t
syslog_init_post_vm(struct kernel_args* args)
{
status_t status;
int32 length = 0;
if (!sSyslogOutputEnabled) {
sSyslogBuffer = NULL;
return B_OK;
}
sSyslogMessage = (syslog_message*)malloc(SYSLOG_MESSAGE_BUFFER_SIZE);
if (sSyslogMessage == NULL) {
status = B_NO_MEMORY;
goto err1;
}
if (sSyslogBuffer == NULL) {
size_t bufferSize = DEFAULT_SYSLOG_BUFFER_SIZE;
void* handle = load_driver_settings("kernel");
if (handle != NULL) {
const char* sizeString = get_driver_parameter(handle,
"syslog_buffer_size", NULL, NULL);
if (sizeString != NULL) {
bufferSize = strtoul(sizeString, NULL, 0);
if (bufferSize > 262144)
bufferSize = 262144;
else if (bufferSize < SYSLOG_MESSAGE_BUFFER_SIZE)
bufferSize = SYSLOG_MESSAGE_BUFFER_SIZE;
}
unload_driver_settings(handle);
}
sSyslogBuffer = create_ring_buffer(bufferSize);
if (sSyslogBuffer == NULL) {
status = B_NO_MEMORY;
goto err2;
}
} else if (args->keep_debug_output_buffer) {
void* base = (void*)ROUNDDOWN((addr_t)(void *)args->debug_output, B_PAGE_SIZE);
size_t size = ROUNDUP(args->debug_size, B_PAGE_SIZE);
area_id area = create_area("syslog debug", &base, B_EXACT_ADDRESS, size,
B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
if (area < 0) {
status = B_NO_MEMORY;
goto err2;
}
}
if (!args->keep_debug_output_buffer && args->debug_output != NULL) {
syslog_write((const char*)args->debug_output.Pointer(),
args->debug_size, false);
}
sSyslogMessage->from = 0;
sSyslogMessage->options = LOG_KERN;
sSyslogMessage->priority = LOG_DEBUG;
sSyslogMessage->ident[0] = '\0';
if (args->previous_debug_output != NULL) {
sPreviousSessionSyslogBuffer = malloc(args->previous_debug_size);
if (sPreviousSessionSyslogBuffer != NULL) {
sPreviousSessionSyslogBufferSize = args->previous_debug_size;
memcpy(sPreviousSessionSyslogBuffer, args->previous_debug_output,
sPreviousSessionSyslogBufferSize);
}
}
char revisionBuffer[64];
length = snprintf(revisionBuffer, sizeof(revisionBuffer),
"Welcome to syslog debug output!\nHaiku revision: %s\n",
get_haiku_revision());
syslog_write(revisionBuffer,
std::min(length, (int32)sizeof(revisionBuffer) - 1), false);
add_debugger_command_etc("syslog", &cmd_dump_syslog,
"Dumps the syslog buffer.",
"[ \"-n\" ] [ \"-k\" ]\n"
"Dumps the whole syslog buffer, or, if -k is specified, only "
"the part that hasn't been sent yet.\n", 0);
return B_OK;
err2:
free(sSyslogMessage);
err1:
sSyslogOutputEnabled = false;
sSyslogBuffer = NULL;
return status;
}
static void
syslog_init_post_modules()
{
if (sPreviousSessionSyslogBuffer == NULL)
return;
void* buffer = sPreviousSessionSyslogBuffer;
size_t bufferSize = sPreviousSessionSyslogBufferSize;
sPreviousSessionSyslogBuffer = NULL;
sPreviousSessionSyslogBufferSize = 0;
MemoryDeleter bufferDeleter(buffer);
int fd = open("/var/log/previous_syslog", O_WRONLY | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (fd < 0) {
dprintf("Failed to open previous syslog file: %s\n", strerror(errno));
return;
}
write(fd, buffer, bufferSize);
close(fd);
}
static status_t
syslog_init(struct kernel_args* args)
{
if (!args->keep_debug_output_buffer || args->debug_output == NULL)
return B_OK;
sSyslogBuffer = create_ring_buffer_etc(args->debug_output, args->debug_size,
RING_BUFFER_INIT_FROM_BUFFER);
sDebugSyslog = true;
return B_OK;
}
static void
debug_memcpy_trampoline(void* _parameters)
{
debug_memcpy_parameters* parameters = (debug_memcpy_parameters*)_parameters;
memcpy(parameters->to, parameters->from, parameters->size);
}
static void
debug_strlcpy_trampoline(void* _parameters)
{
debug_strlcpy_parameters* parameters
= (debug_strlcpy_parameters*)_parameters;
parameters->result = strlcpy(parameters->to, parameters->from,
parameters->size);
}
void
call_modules_hook(bool enter)
{
uint32 index = 0;
while (index < kMaxDebuggerModules && sDebuggerModules[index] != NULL) {
debugger_module_info* module = sDebuggerModules[index];
if (enter && module->enter_debugger != NULL)
module->enter_debugger();
else if (!enter && module->exit_debugger != NULL)
module->exit_debugger();
index++;
}
}
static void
debug_output(const char* string, int32 length, bool notifySyslog)
{
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(notifySyslog);
if (sSerialDebugEnabled)
arch_debug_serial_puts(string);
if (sSyslogOutputEnabled)
syslog_write(string, length, notifySyslog);
if (sBlueScreenEnabled || sDebugScreenEnabled)
blue_screen_puts(string);
if (sSerialDebugEnabled) {
for (uint32 i = 0; i < kMaxDebuggerModules; i++) {
if (sDebuggerModules[i] && sDebuggerModules[i]->debugger_puts)
sDebuggerModules[i]->debugger_puts(string, length);
}
}
memcpy(sLastOutputBuffer, string, length);
sLastOutputBuffer[length] = 0;
}
}
static void
flush_pending_repeats(bool notifySyslog)
{
if (sMessageRepeatCount <= 0)
return;
if (sMessageRepeatCount > 1) {
static char temp[40];
size_t length = snprintf(temp, sizeof(temp),
"Last message repeated %" B_PRId32 " times.\n", sMessageRepeatCount);
length = std::min(length, sizeof(temp) - 1);
if (sSerialDebugEnabled)
arch_debug_serial_puts(temp);
if (sSyslogOutputEnabled)
syslog_write(temp, length, notifySyslog);
if (sBlueScreenEnabled || sDebugScreenEnabled)
blue_screen_puts(temp);
if (sSerialDebugEnabled) {
for (uint32 i = 0; i < kMaxDebuggerModules; i++) {
if (sDebuggerModules[i] && sDebuggerModules[i]->debugger_puts)
sDebuggerModules[i]->debugger_puts(temp, length);
}
}
} else {
size_t length = strlen(sLastOutputBuffer);
if (sSerialDebugEnabled)
arch_debug_serial_puts(sLastOutputBuffer);
if (sSyslogOutputEnabled)
syslog_write(sLastOutputBuffer, length, notifySyslog);
if (sBlueScreenEnabled || sDebugScreenEnabled)
blue_screen_puts(sLastOutputBuffer);
if (sSerialDebugEnabled) {
for (uint32 i = 0; i < kMaxDebuggerModules; i++) {
if (sDebuggerModules[i] && sDebuggerModules[i]->debugger_puts) {
sDebuggerModules[i]->debugger_puts(sLastOutputBuffer,
length);
}
}
}
}
sMessageRepeatFirstTime = 0;
sMessageRepeatCount = 0;
}
static void
check_pending_repeats(void* , int )
{
if (mutex_lock_with_timeout(&sOutputLock, B_RELATIVE_TIMEOUT, 100 * 1000) != B_OK)
return;
MutexLocker locker(sOutputLock, true);
if (sMessageRepeatCount > 0
&& (system_time() - sMessageRepeatLastTime > 1000000
|| system_time() - sMessageRepeatFirstTime > 3000000)) {
cpu_status state = disable_interrupts();
acquire_spinlock(&sSpinlock);
flush_pending_repeats(true);
release_spinlock(&sSpinlock);
restore_interrupts(state);
}
}
static void
dprintf_args(const char* format, va_list args, bool notifySyslog)
{
if (are_interrupts_enabled()) {
MutexLocker locker(sOutputLock);
int32 length = vsnprintf(sOutputBuffer, OUTPUT_BUFFER_SIZE, format,
args);
length = std::min(length, (int32)OUTPUT_BUFFER_SIZE - 1);
InterruptsSpinLocker _(sSpinlock);
debug_output(sOutputBuffer, length, notifySyslog);
} else {
InterruptsSpinLocker _(sSpinlock);
int32 length = vsnprintf(sInterruptOutputBuffer, OUTPUT_BUFFER_SIZE,
format, args);
length = std::min(length, (int32)OUTPUT_BUFFER_SIZE - 1);
debug_output(sInterruptOutputBuffer, length, notifySyslog);
}
}
bool
debug_screen_output_enabled(void)
{
return sDebugScreenEnabled;
}
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)
{
MutexLocker mutexLocker(sOutputLock);
InterruptsSpinLocker _(sSpinlock);
debug_output(string, length, true);
}
void
debug_early_boot_message(const char* string)
{
arch_debug_serial_early_boot_message(string);
}
void
debug_init(kernel_args* args)
{
new(&gDefaultDebugOutputFilter) DefaultDebugOutputFilter;
syslog_init(args);
debug_paranoia_init();
arch_debug_console_init(args);
if (frame_buffer_console_init(args) == B_OK && blue_screen_init_early() == B_OK)
sBlueScreenOutput = true;
}
void
debug_init_post_vm(kernel_args* args)
{
add_debugger_command_etc("cpu", &cmd_switch_cpu,
"Switches to another CPU.",
"<cpu>\n"
"Switches to CPU with the index <cpu>.\n", 0);
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("panic_commands", &cmd_execute_panic_commands,
"Execute commands associated with the panic() that caused "
"entering KDL",
"\n"
"Executes the commands associated with the panic() that caused "
"entering KDL.\n", 0);
debug_builtin_commands_init();
arch_debug_init(args);
debug_heap_init();
debug_variables_init();
frame_buffer_console_init_post_vm(args);
tracing_init();
}
void
debug_init_post_settings(struct kernel_args* args)
{
sSerialDebugEnabled = get_safemode_boolean("serial_debug_output",
sSerialDebugEnabled);
sSyslogOutputEnabled = get_safemode_boolean("syslog_debug_output",
sSyslogOutputEnabled);
sBlueScreenOutput = get_safemode_boolean("bluescreen", true);
sEmergencyKeysEnabled = get_safemode_boolean("emergency_keys",
sEmergencyKeysEnabled);
sDebugScreenEnabled = get_safemode_boolean("debug_screen", false);
if ((sBlueScreenOutput || sDebugScreenEnabled)
&& blue_screen_init() != B_OK)
sBlueScreenOutput = sDebugScreenEnabled = false;
if (sDebugScreenEnabled)
blue_screen_enter(true);
arch_debug_console_init_settings(args);
syslog_init_post_vm(args);
}
void
debug_init_post_modules(struct kernel_args* args)
{
syslog_init_post_modules();
register_kernel_daemon(check_pending_repeats, NULL, 10);
syslog_init_post_threads();
static const char* kDemanglePrefix = "debugger/demangle/";
void* cookie = open_module_list("debugger");
uint32 count = 0;
while (count < kMaxDebuggerModules) {
char name[B_FILE_NAME_LENGTH];
size_t nameLength = sizeof(name);
if (read_next_module_name(cookie, name, &nameLength) != B_OK)
break;
if (!strncmp(name, kDemanglePrefix, strlen(kDemanglePrefix))) {
if (sDemangleModule == NULL)
get_module(name, (module_info**)&sDemangleModule);
continue;
}
if (get_module(name, (module_info**)&sDebuggerModules[count]) == B_OK) {
dprintf("kernel debugger extension \"%s\": loaded\n", name);
count++;
} else
dprintf("kernel debugger extension \"%s\": failed to load\n", name);
}
close_module_list(cookie);
frame_buffer_console_init_post_modules(args);
}
void
debug_set_page_fault_info(addr_t faultAddress, addr_t pc, uint32 flags)
{
sPageFaultInfo.fault_address = faultAddress;
sPageFaultInfo.pc = pc;
sPageFaultInfo.flags = flags;
}
debug_page_fault_info*
debug_get_page_fault_info()
{
return &sPageFaultInfo;
}
void
debug_trap_cpu_in_kdl(int32 cpu, bool returnIfHandedOver)
{
InterruptsLocker locker;
if (sCPUTrapped[cpu])
return;
arch_debug_save_registers(&sDebugRegisters[cpu]);
sCPUTrapped[cpu] = true;
while (sInDebugger != 0) {
arch_debug_snooze(10000);
if (sHandOverKDL && sHandOverKDLToCPU == cpu) {
if (returnIfHandedOver)
break;
kernel_debugger_internal(NULL, NULL,
sCurrentKernelDebuggerMessageArgs, cpu);
} else
smp_intercpu_interrupt_handler(cpu);
}
sCPUTrapped[cpu] = false;
}
void
debug_double_fault(int32 cpu)
{
kernel_debugger_internal("Double Fault!", NULL,
sCurrentKernelDebuggerMessageArgs, cpu);
}
bool
debug_emergency_key_pressed(char key)
{
if (!sEmergencyKeysEnabled)
return false;
if (key == 'd') {
kernel_debugger("Keyboard Requested Halt.");
return true;
}
for (uint32 i = 0; i < kMaxDebuggerModules; i++) {
if (sDebuggerModules[i] && sDebuggerModules[i]->emergency_key_pressed) {
if (sDebuggerModules[i]->emergency_key_pressed(key))
return true;
}
}
return false;
}
bool
debug_is_kernel_memory_accessible(addr_t address, size_t size,
uint32 protection)
{
addr_t endAddress = ROUNDUP(address + size, B_PAGE_SIZE);
address = ROUNDDOWN(address, B_PAGE_SIZE);
if (!IS_KERNEL_ADDRESS(address) || endAddress < address)
return false;
for (; address < endAddress; address += B_PAGE_SIZE) {
if (!arch_vm_translation_map_is_kernel_page_accessible(address,
protection)) {
return false;
}
}
return true;
}
int
debug_call_with_fault_handler(jmp_buf jumpBuffer, void (*function)(void*),
void* parameter)
{
cpu_ent* cpu = gCPU + sDebuggerOnCPU;
addr_t oldFaultHandler = cpu->fault_handler;
addr_t oldFaultHandlerStackPointer = cpu->fault_handler_stack_pointer;
int result = setjmp(jumpBuffer);
if (result == 0) {
arch_debug_call_with_fault_handler(cpu, jumpBuffer, function,
parameter);
}
cpu->fault_handler = oldFaultHandler;
cpu->fault_handler_stack_pointer = oldFaultHandlerStackPointer;
return result;
}
status_t
debug_memcpy(team_id teamID, void* to, const void* from, size_t size)
{
if ((addr_t)from + size < (addr_t)from || (addr_t)to + size < (addr_t)to)
return B_BAD_ADDRESS;
if ((IS_KERNEL_ADDRESS(from) && IS_KERNEL_ADDRESS(to))
|| debug_is_debugged_team(teamID)) {
debug_memcpy_parameters parameters = {to, from, size};
if (debug_call_with_fault_handler(gCPU[sDebuggerOnCPU].fault_jump_buffer,
&debug_memcpy_trampoline, ¶meters) == 0) {
return B_OK;
}
}
while (size > 0) {
uint8 buffer[32];
size_t toCopy = std::min(size, sizeof(buffer));
if (((addr_t)from + toCopy) % B_PAGE_SIZE < toCopy)
toCopy -= ((addr_t)from + toCopy) % B_PAGE_SIZE;
if (((addr_t)to + toCopy) % B_PAGE_SIZE < toCopy)
toCopy -= ((addr_t)to + toCopy) % B_PAGE_SIZE;
if (vm_debug_copy_page_memory(teamID, (void*)from, buffer, toCopy,
false) != B_OK
|| vm_debug_copy_page_memory(teamID, to, buffer, toCopy, true)
!= B_OK) {
return B_BAD_ADDRESS;
}
from = (const uint8*)from + toCopy;
to = (uint8*)to + toCopy;
size -= toCopy;
}
return B_OK;
}
ssize_t
debug_strlcpy(team_id teamID, char* to, const char* from, size_t size)
{
if (from == NULL || (to == NULL && size > 0))
return B_BAD_ADDRESS;
size_t maxSize = std::min((addr_t)size,
~(addr_t)0 - std::max((addr_t)from, (addr_t)to) + 1);
if ((IS_KERNEL_ADDRESS(from) && IS_KERNEL_ADDRESS(to))
|| debug_is_debugged_team(teamID)) {
debug_strlcpy_parameters parameters = {to, from, maxSize};
if (debug_call_with_fault_handler(
gCPU[sDebuggerOnCPU].fault_jump_buffer,
&debug_strlcpy_trampoline, ¶meters) == 0) {
if (parameters.result >= maxSize && maxSize < size)
return B_BAD_ADDRESS;
return parameters.result;
}
}
size_t totalLength = 0;
while (maxSize > 0) {
char buffer[32];
size_t toCopy = std::min(maxSize, sizeof(buffer));
if (((addr_t)from + toCopy) % B_PAGE_SIZE < toCopy)
toCopy -= ((addr_t)from + toCopy) % B_PAGE_SIZE;
if (((addr_t)to + toCopy) % B_PAGE_SIZE < toCopy)
toCopy -= ((addr_t)to + toCopy) % B_PAGE_SIZE;
if (vm_debug_copy_page_memory(teamID, (void*)from, buffer, toCopy,
false) != B_OK) {
return B_BAD_ADDRESS;
}
size_t length = strnlen(buffer, toCopy);
bool endOfString = length < toCopy;
from = (const char*)from + toCopy;
totalLength += length;
maxSize -= length;
if (endOfString) {
toCopy = length + 1;
}
if (size > 0) {
if (size <= length) {
buffer[size - 1] = '\0';
totalLength += length - size;
toCopy = size;
}
if (vm_debug_copy_page_memory(teamID, to, buffer, toCopy, true)
!= B_OK) {
return B_BAD_ADDRESS;
}
to = (char*)to + toCopy;
size -= toCopy;
}
if (endOfString)
return totalLength;
}
return totalLength;
}
uint64
parse_expression(const char* expression)
{
uint64 result;
return evaluate_debug_expression(expression, &result, true) ? result : 0;
}
void
panic(const char* format, ...)
{
va_list args;
va_start(args, format);
cpu_status state = disable_interrupts();
kernel_debugger_internal("PANIC: ", format, args,
thread_get_current_thread() ? smp_get_current_cpu() : 0);
restore_interrupts(state);
va_end(args);
}
void
kernel_debugger(const char* message)
{
cpu_status state = disable_interrupts();
kernel_debugger_internal(message, NULL, sCurrentKernelDebuggerMessageArgs,
smp_get_current_cpu());
restore_interrupts(state);
}
bool
set_dprintf_enabled(bool newState)
{
bool oldState = sSerialDebugEnabled;
sSerialDebugEnabled = newState;
return oldState;
}
void
dprintf(const char* format, ...)
{
va_list args;
if (!sSerialDebugEnabled && !sSyslogOutputEnabled && !sBlueScreenEnabled)
return;
va_start(args, format);
dprintf_args(format, args, true);
va_end(args);
}
void
dvprintf(const char* format, va_list args)
{
if (!sSerialDebugEnabled && !sSyslogOutputEnabled && !sBlueScreenEnabled)
return;
dprintf_args(format, args, true);
}
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);
}
void
kprintf(const char* format, ...)
{
if (sDebugOutputFilter != NULL) {
va_list args;
va_start(args, format);
sDebugOutputFilter->Print(format, args);
va_end(args);
}
}
void
kprintf_unfiltered(const char* format, ...)
{
va_list args;
va_start(args, format);
gDefaultDebugOutputFilter.Print(format, args);
va_end(args);
}
const char*
debug_demangle_symbol(const char* symbol, char* buffer, size_t bufferSize,
bool* _isObjectMethod)
{
if (sDemangleModule != NULL && sDemangleModule->demangle_symbol != NULL) {
return sDemangleModule->demangle_symbol(symbol, buffer, bufferSize,
_isObjectMethod);
}
if (_isObjectMethod != NULL)
*_isObjectMethod = false;
return symbol;
}
status_t
debug_get_next_demangled_argument(uint32* _cookie, const char* symbol,
char* name, size_t nameSize, int32* _type, size_t* _argumentLength)
{
if (sDemangleModule != NULL && sDemangleModule->get_next_argument != NULL) {
return sDemangleModule->get_next_argument(_cookie, symbol, name,
nameSize, _type, _argumentLength);
}
return B_NOT_SUPPORTED;
}
struct arch_debug_registers*
debug_get_debug_registers(int32 cpu)
{
if (cpu < 0 || cpu > smp_get_num_cpus())
return NULL;
return &sDebugRegisters[cpu];
}
Thread*
debug_set_debugged_thread(Thread* thread)
{
Thread* previous = sDebuggedThread;
sDebuggedThread = thread;
return previous;
}
Thread*
debug_get_debugged_thread()
{
return sDebuggedThread != NULL
? sDebuggedThread : thread_get_current_thread();
}
bool
debug_is_debugged_team(team_id teamID)
{
if (teamID == B_CURRENT_TEAM)
return true;
Thread* thread = debug_get_debugged_thread();
return thread != NULL && thread->team != NULL
&& thread->team->id == teamID;
}
status_t
_user_kernel_debugger(const char* userMessage)
{
if (geteuid() != 0)
return B_NOT_ALLOWED;
char message[512];
strcpy(message, "USER: ");
size_t length = strlen(message);
if (userMessage == NULL || !IS_USER_ADDRESS(userMessage) || user_strlcpy(
message + length, userMessage, sizeof(message) - length) < 0) {
return B_BAD_ADDRESS;
}
kernel_debugger(message);
return B_OK;
}
void
_user_register_syslog_daemon(port_id port)
{
if (geteuid() != 0 || !sSyslogOutputEnabled || sSyslogNotify < 0)
return;
sSyslogPort = port;
if (sSyslogWriter < 0) {
sSyslogWriter = spawn_kernel_thread(syslog_sender, "syslog sender",
B_LOW_PRIORITY, NULL);
if (sSyslogWriter >= 0)
resume_thread(sSyslogWriter);
}
}
void
_user_debug_output(const char* userString)
{
if (!sSerialDebugEnabled && !sSyslogOutputEnabled)
return;
if (!IS_USER_ADDRESS(userString))
return;
char string[512];
int32 length;
int32 toWrite;
do {
length = user_strlcpy(string, userString, sizeof(string));
if (length <= 0)
break;
toWrite = std::min(length, (int32)sizeof(string) - 1);
debug_puts(string, toWrite);
userString += toWrite;
} while (length > toWrite);
}
void
dump_block(const char* buffer, int size, const char* prefix)
{
const int DUMPED_BLOCK_SIZE = 16;
int i;
char lineBuffer[3 + DUMPED_BLOCK_SIZE * 4];
for (i = 0; i < size;) {
char* pointer = lineBuffer;
int start = i;
for (; i < start + DUMPED_BLOCK_SIZE; i++) {
if (!(i % 4))
pointer += sprintf(pointer, " ");
if (i >= size)
pointer += sprintf(pointer, " ");
else
pointer += sprintf(pointer, "%02x", *(unsigned char*)(buffer + i));
}
pointer += sprintf(pointer, " ");
for (i = start; i < start + DUMPED_BLOCK_SIZE; i++) {
if (i < size) {
char c = buffer[i];
if (c < 30)
pointer += sprintf(pointer, ".");
else
pointer += sprintf(pointer, "%c", c);
} else
break;
}
dprintf("%s%04x%s\n", prefix, start, lineBuffer);
}
}