version 1.1, 2008/06/03 10:38:46 |
version 1.1.1.1.2.1, 2008/08/13 17:12:31 |
|
|
/*- |
/*- |
* Copyright (c) 2005-2007, Kohsuke Ohtani |
* Copyright (c) 2005-2008, Kohsuke Ohtani |
* All rights reserved. |
* All rights reserved. |
* |
* |
* Redistribution and use in source and binary forms, with or without |
* Redistribution and use in source and binary forms, with or without |
|
|
|
|
#include <kernel.h> |
#include <kernel.h> |
#include <task.h> |
#include <task.h> |
#include <ipc.h> |
|
#include <thread.h> |
#include <thread.h> |
#include <device.h> |
|
#include <page.h> |
|
#include <kmem.h> |
|
#include <vm.h> |
#include <vm.h> |
#include <irq.h> |
#include <irq.h> |
|
|
#ifdef DEBUG |
#ifdef DEBUG |
|
|
#ifdef printk |
/* |
#undef printk |
* diag_print() is provided by architecture dependent layer. |
#endif |
*/ |
#ifdef panic |
typedef void (*prtfn_t)(char *); |
#undef panic |
static prtfn_t print_func = &diag_print; |
#endif |
|
|
|
#ifdef CONFIG_DMESG |
static char dbg_msg[DBGMSG_SIZE]; |
#define LOG_SIZE 2048 /* size of ring buffer for log */ |
|
#define LOG_MASK (LOG_SIZE-1) |
|
|
|
static void log_save(char *buf); |
/* |
|
* dmesg support |
|
*/ |
|
static char log_buf[LOGBUF_SIZE]; |
|
static u_long log_head; |
|
static u_long log_tail; |
|
static u_long log_len; |
|
|
static char log_buf[LOG_SIZE]; /* buffer for message log */ |
#define LOGINDEX(x) ((x) & (LOGBUF_SIZE - 1)) |
static u_long log_start; /* start index of log_buf */ |
|
static u_long log_end; /* end index of log_buf */ |
|
static u_long log_len; /* length of logged char */ |
|
#endif |
|
|
|
static char msg_buf[MSGBUFSZ]; /* temporary buffer for message */ |
|
static void (*alt_print)(char *); /* alternate print handler */ |
|
|
|
/* |
/* |
* Print out the specified string with a variable argument. |
* Print out the specified string. |
* |
* |
* An actual output is displayed via the platform specific device by |
* An actual output is displayed via the platform |
* diag_print() routine in kernel. As an alternate option, the device |
* specific device by diag_print() routine in kernel. |
* driver can replace the print routine by using debug_attach(). |
* As an alternate option, the device driver can |
* All printk() inside the kernel are defined as a macro. |
* replace the print routine by using debug_attach(). |
* The printk() macro is compiled only when the debug option is |
|
* enabled (NDEBUG=0). |
|
*/ |
*/ |
void |
void |
printk(const char *fmt, ...) |
printf(const char *fmt, ...) |
{ |
{ |
va_list args; |
va_list args; |
|
int i; |
|
char c; |
|
|
irq_lock(); |
irq_lock(); |
va_start(args, fmt); |
va_start(args, fmt); |
vsprintf(msg_buf, fmt, args); |
|
#ifdef CONFIG_DMESG |
vsprintf(dbg_msg, fmt, args); |
log_save(msg_buf); |
|
#endif |
/* Print out */ |
if (alt_print != NULL) |
(*print_func)(dbg_msg); |
alt_print(msg_buf); |
|
else |
/* |
diag_print(msg_buf); |
* Record to log buffer |
|
*/ |
|
for (i = 0; i < DBGMSG_SIZE; i++) { |
|
c = dbg_msg[i]; |
|
if (c == '\0') |
|
break; |
|
log_buf[LOGINDEX(log_tail)] = c; |
|
log_tail++; |
|
if (log_len < LOGBUF_SIZE) |
|
log_len++; |
|
else |
|
log_head = log_tail - LOGBUF_SIZE; |
|
} |
va_end(args); |
va_end(args); |
irq_unlock(); |
irq_unlock(); |
} |
} |
|
|
/* |
/* |
* Kernel assertion. |
* Kernel assertion. |
* |
* |
* assert() is called only when the expression is false in ASSERT() |
* assert() is called only when the expression is false in |
* macro. ASSERT() macro is compiled only when the debug option is |
* ASSERT() macro. ASSERT() macro is compiled only when |
* enabled. |
* the debug option is enabled. |
*/ |
*/ |
void |
void |
assert(const char *file, int line, const char *exp) |
assert(const char *file, int line, const char *exp) |
{ |
{ |
|
|
irq_lock(); |
irq_lock(); |
printk("\nAssertion failed: %s line:%d '%s'\n", file, line, exp); |
printf("\nAssertion failed: %s line:%d '%s'\n", file, line, exp); |
BREAKPOINT(); |
BREAKPOINT(); |
for (;;) |
for (;;) |
machine_idle(); |
machine_idle(); |
|
|
/* |
/* |
* Kernel panic. |
* Kernel panic. |
* |
* |
* panic() is called for a fatal kernel error. It shows specified |
* panic() is called for a fatal kernel error. It shows |
* message, and stops CPU. If the kernel is not debug version, |
* specified message, and stops CPU. |
* panic() macro will reset the system instead of calling this |
|
* routine. |
|
*/ |
*/ |
void |
void |
panic(const char *fmt, ...) |
panic(const char *msg) |
{ |
{ |
va_list args; |
|
|
|
irq_lock(); |
irq_lock(); |
printk("\nKernel panic: "); |
printf("\nKernel panic: %s\n", msg); |
va_start(args, fmt); |
|
vsprintf(msg_buf, fmt, args); |
|
printk(msg_buf); |
|
va_end(args); |
|
printk("\n"); |
|
irq_unlock(); |
irq_unlock(); |
BREAKPOINT(); |
BREAKPOINT(); |
for (;;) |
for (;;) |
|
|
/* NOTREACHED */ |
/* NOTREACHED */ |
} |
} |
|
|
#ifdef CONFIG_DMESG |
|
/* |
/* |
* Save diag message to ring buffer |
* Copy log to user buffer. |
*/ |
*/ |
static void |
|
log_save(char *buf) |
|
{ |
|
char *p; |
|
|
|
for (p = buf; *p != '\0'; p++) { |
|
log_buf[log_end & LOG_MASK] = *p; |
|
log_end++; |
|
if (log_end - log_start > LOG_SIZE) |
|
log_start = log_end - LOG_SIZE; |
|
if (log_len < LOG_SIZE) |
|
log_len++; |
|
} |
|
/* Store end tag */ |
|
log_buf[log_end & LOG_MASK] = -1; |
|
} |
|
#endif |
|
|
|
/* |
|
* Return infomation about log |
|
*/ |
|
int |
int |
log_get(char **buf, size_t *size) |
debug_getlog(char *buf) |
{ |
{ |
|
u_long i, len, index; |
#ifdef CONFIG_DMESG |
int err = 0; |
*buf = log_buf; |
|
*size = LOG_SIZE; |
|
return 0; |
|
#else |
|
return -1; |
|
#endif |
|
} |
|
|
|
#if defined(CONFIG_DMESG) && defined (CONFIG_KDUMP) |
|
static void |
|
log_dump(void) |
|
{ |
|
int i, len; |
|
u_long index; |
|
char c; |
char c; |
|
|
index = log_start; |
irq_lock(); |
|
index = log_head; |
len = log_len; |
len = log_len; |
if (log_len == LOG_SIZE) { |
if (len >= LOGBUF_SIZE) { |
/* Skip first line */ |
/* |
while (log_buf[index & LOG_MASK] != '\n') { |
* Overrun found. Discard broken message. |
|
*/ |
|
while (len > 0 && log_buf[LOGINDEX(index)] != '\n') { |
index++; |
index++; |
len--; |
len--; |
} |
} |
} |
} |
for (i = 0; i < len; i++) { |
for (i = 0; i < LOGBUF_SIZE; i++) { |
c = log_buf[index & LOG_MASK]; |
if (i < len) |
printk("%c", c); |
c = log_buf[LOGINDEX(index)]; |
|
else |
|
c = '\0'; |
|
if (umem_copyout(&c, buf, 1)) { |
|
err = EFAULT; |
|
break; |
|
} |
index++; |
index++; |
|
buf++; |
} |
} |
|
irq_unlock(); |
|
return err; |
} |
} |
#endif |
|
|
|
/* |
/* |
* Dump system information. |
* Dump system information. |
* |
* |
* A keyboard driver may call this routine if a user presses |
* A keyboard driver may call this routine if a user |
* a predefined "dump" key. |
* presses a predefined "dump" key. Since interrupt is |
* Since interrupt is locked in this routine, there is no need |
* locked in this routine, there is no need to lock the |
* to lock the interrupt/scheduler in each dump function. |
* interrupt/scheduler in each dump function. |
*/ |
*/ |
int |
int |
debug_dump(int item) |
debug_dump(int item) |
{ |
{ |
#ifdef CONFIG_KDUMP |
|
int err = 0; |
int err = 0; |
|
|
printk("\n"); |
|
irq_lock(); |
irq_lock(); |
switch (item) { |
switch (item) { |
case DUMP_THREAD: |
case DUMP_THREAD: |
|
|
case DUMP_TASK: |
case DUMP_TASK: |
task_dump(); |
task_dump(); |
break; |
break; |
case DUMP_OBJECT: |
|
object_dump(); |
|
break; |
|
case DUMP_TIMER: |
|
timer_dump(); |
|
break; |
|
case DUMP_IRQ: |
|
irq_dump(); |
|
break; |
|
case DUMP_DEVICE: |
|
device_dump(); |
|
break; |
|
case DUMP_VM: |
case DUMP_VM: |
page_dump(); |
|
kmem_dump(); |
|
vm_dump(); |
vm_dump(); |
break; |
break; |
#ifdef CONFIG_DMESG |
|
case DUMP_MSGLOG: |
|
log_dump(); |
|
break; |
|
#endif |
|
default: |
default: |
err = 1; |
err = ENOSYS; |
break; |
break; |
} |
} |
irq_unlock(); |
irq_unlock(); |
return err; |
return err; |
#else |
|
return ENOSYS; |
|
#endif |
|
} |
} |
|
|
/* |
/* |
* Attach an alternate print handler. |
* Attach to a print handler. |
* A device driver can hook the function to display message. |
* A device driver can hook the function to display message. |
*/ |
*/ |
void |
void |
debug_attach(void (*fn)(char *)) |
debug_attach(void (*fn)(char *)) |
{ |
{ |
ASSERT(fn); |
ASSERT(fn); |
alt_print = fn; |
|
} |
|
|
|
#else /* !DEBUG */ |
print_func = fn; |
|
|
/* |
|
* Stubs for the release build. |
|
*/ |
|
int |
|
debug_dump(int item) |
|
{ |
|
return ENOSYS; |
|
} |
} |
|
|
void |
|
debug_attach(void (*fn)(char *)) |
|
{ |
|
} |
|
#endif /* !DEBUG */ |
#endif /* !DEBUG */ |