* Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2002-2015, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
* Distributed under the terms of the NewOS License.
*/
#include "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 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;
}
}
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) {
smp_send_broadcast_ici_interrupts_disabled(cpu, 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;
}
context.
Invoked in the kernel debugger only.
\param address The start address of the memory range to be checked.
\param size The size of the memory range to be checked.
\param protection The area protection for which to check. Valid is a bitwise
or of one or more of \c B_KERNEL_READ_AREA or \c B_KERNEL_WRITE_AREA.
\return \c true, if the complete memory range can be accessed in all ways
specified by \a protection, \c false otherwise.
*/
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;
}
May only be used in the kernel debugger.
\param jumpBuffer Buffer to be used for setjmp()/longjmp().
\param function The function to be called.
\param parameter The parameter to be passed to the function to be called.
\return
- \c 0, when the function executed without causing a page fault or
calling longjmp().
- \c 1, when the function caused a page fault.
- Any other value the function passes to longjmp().
*/
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;
}
debugger (and must not be used outside).
The supplied \a teamID specifies the address space in which to interpret
the addresses. It can be \c B_CURRENT_TEAM for debug_get_debugged_thread(),
or any valid team ID. If the addresses are both kernel addresses, the
argument is ignored and the current address space is used.
*/
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;
}
debugger (and must not be used outside).
The supplied \a teamID specifies the address space in which to interpret
the addresses. It can be \c B_CURRENT_TEAM for debug_get_debugged_thread(),
or any valid team ID. If the addresses are both kernel addresses, the
argument is ignored and the current address space is used.
*/
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);
}
debugger only (it doesn't lock).
*/
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();
}
debugged thread (debug_get_debugged_thread()) belongs to.
Always returns \c true, if \c B_CURRENT_TEAM is given.
*/
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);
}
}