diff options
author | behlendo <behlendo@7e1ea52c-4ff2-0310-8f11-9dd32ca42a1c> | 2008-04-18 23:39:58 +0000 |
---|---|---|
committer | behlendo <behlendo@7e1ea52c-4ff2-0310-8f11-9dd32ca42a1c> | 2008-04-18 23:39:58 +0000 |
commit | 57d1b1885897c9b079a6355a6d54d684ad003d46 (patch) | |
tree | 941352e2acfedadd3b97f0af780a2a7a2e910540 /include/sys/debug.h | |
parent | 55152ebbb46fb8e382ed18e58cc570c61e224c21 (diff) |
First commit of lustre style internal debug support. These
changes bring over everything lustre had for debugging with
two exceptions. I dropped by the debug daemon and upcalls
just because it made things a little easier. They can be
readded easily enough if we feel they are needed.
Everything compiles and seems to work on first inspection
but I suspect there are a handful of issues still lingering
which I'll be sorting out right away. I just wanted to get
all these changes commited and safe. I'm getting a little
paranoid about losing them.
git-svn-id: https://outreach.scidac.gov/svn/spl/trunk@75 7e1ea52c-4ff2-0310-8f11-9dd32ca42a1c
Diffstat (limited to 'include/sys/debug.h')
-rw-r--r-- | include/sys/debug.h | 313 |
1 files changed, 310 insertions, 3 deletions
diff --git a/include/sys/debug.h b/include/sys/debug.h index 4a6ed5c3d..a670c0374 100644 --- a/include/sys/debug.h +++ b/include/sys/debug.h @@ -1,11 +1,318 @@ #ifndef _SPL_DEBUG_H #define _SPL_DEBUG_H +#include <linux/sched.h> /* THREAD_SIZE */ +#include <linux/proc_fs.h> + extern unsigned long spl_debug_mask; extern unsigned long spl_debug_subsys; -void __dprintf(const char *file, const char *func, int line, const char *fmt, ...); -void spl_set_debug_mask(unsigned long mask); -void spl_set_debug_subsys(unsigned long mask); +#define S_UNDEFINED 0x00000001 +#define S_ATOMIC 0x00000002 +#define S_KOBJ 0x00000004 +#define S_VNODE 0x00000008 +#define S_TIME 0x00000010 +#define S_RWLOCK 0x00000020 +#define S_THREAD 0x00000040 +#define S_CONDVAR 0x00000080 +#define S_MUTEX 0x00000100 +#define S_RNG 0x00000200 +#define S_TASKQ 0x00000400 +#define S_KMEM 0x00000800 +#define S_DEBUG 0x00001000 +#define S_GENERIC 0x00002000 +#define S_PROC 0x00004000 + +#define D_TRACE 0x00000001 +#define D_INFO 0x00000002 +#define D_WARNING 0x00000004 +#define D_ERROR 0x00000008 +#define D_EMERG 0x00000010 +#define D_CONSOLE 0x00000020 +#define D_IOCTL 0x00000040 +#define D_DPRINTF 0x00000080 +#define D_OTHER 0x00000100 + +#define D_CANTMASK (D_ERROR | D_EMERG | D_WARNING | D_CONSOLE) +#define DEBUG_SUBSYSTEM S_UNDEFINED + +int debug_init(void); +void debug_fini(void); +int spl_debug_mask2str(char *str, int size, unsigned long mask, int is_subsys); +int spl_debug_str2mask(unsigned long *mask, const char *str, int is_subsys); + +extern unsigned long spl_debug_subsys; +extern unsigned long spl_debug_mask; +extern unsigned long spl_debug_printk; +extern int spl_debug_mb; +extern unsigned int spl_debug_binary; +extern unsigned int spl_debug_catastrophe; +extern unsigned int spl_debug_panic_on_bug; +extern char spl_debug_file_path[PATH_MAX]; +extern unsigned int spl_console_ratelimit; +extern long spl_console_max_delay; +extern long spl_console_min_delay; +extern unsigned int spl_console_backoff; +extern unsigned int spl_debug_stack; + +#define TCD_MAX_PAGES (5 << (20 - PAGE_SHIFT)) +#define TCD_STOCK_PAGES (TCD_MAX_PAGES) +#define TRACE_CONSOLE_BUFFER_SIZE 1024 + +#define SPL_DEFAULT_MAX_DELAY (600 * HZ) +#define SPL_DEFAULT_MIN_DELAY ((HZ + 1) / 2) +#define SPL_DEFAULT_BACKOFF 2 + +typedef struct { + unsigned long cdls_next; + int cdls_count; + long cdls_delay; +} spl_debug_limit_state_t; + +/* Three trace data types */ +typedef enum { + TCD_TYPE_PROC, + TCD_TYPE_SOFTIRQ, + TCD_TYPE_IRQ, + TCD_TYPE_MAX +} tcd_type_t; + +union trace_data_union { + struct trace_cpu_data { + /* pages with trace records not yet processed by tracefiled */ + struct list_head tcd_pages; + /* number of pages on ->tcd_pages */ + unsigned long tcd_cur_pages; + /* Max number of pages allowed on ->tcd_pages */ + unsigned long tcd_max_pages; + + /* + * preallocated pages to write trace records into. Pages from + * ->tcd_stock_pages are moved to ->tcd_pages by spl_debug_msg(). + * + * This list is necessary, because on some platforms it's + * impossible to perform efficient atomic page allocation in a + * non-blockable context. + * + * Such platforms fill ->tcd_stock_pages "on occasion", when + * tracing code is entered in blockable context. + * + * trace_get_tage_try() tries to get a page from + * ->tcd_stock_pages first and resorts to atomic page + * allocation only if this queue is empty. ->tcd_stock_pages + * is replenished when tracing code is entered in blocking + * context (darwin-tracefile.c:trace_get_tcd()). We try to + * maintain TCD_STOCK_PAGES (40 by default) pages in this + * queue. Atomic allocation is only required if more than + * TCD_STOCK_PAGES pagesful are consumed by trace records all + * emitted in non-blocking contexts. Which is quite unlikely. + */ + struct list_head tcd_stock_pages; + /* number of pages on ->tcd_stock_pages */ + unsigned long tcd_cur_stock_pages; + + unsigned short tcd_shutting_down; + unsigned short tcd_cpu; + unsigned short tcd_type; + /* The factors to share debug memory. */ + unsigned short tcd_pages_factor; + } tcd; + char __pad[L1_CACHE_ALIGN(sizeof(struct trace_cpu_data))]; +}; + +extern union trace_data_union (*trace_data[TCD_TYPE_MAX])[NR_CPUS]; + +#define tcd_for_each(tcd, i, j) \ + for (i = 0; trace_data[i] != NULL; i++) \ + for (j = 0, ((tcd) = &(*trace_data[i])[j].tcd); \ + j < num_possible_cpus(); j++, (tcd) = &(*trace_data[i])[j].tcd) + +#define tcd_for_each_type_lock(tcd, i) \ + for (i = 0; trace_data[i] && \ + (tcd = &(*trace_data[i])[smp_processor_id()].tcd) && \ + trace_lock_tcd(tcd); trace_unlock_tcd(tcd), i++) + +struct trace_page { + struct page * page; /* page itself */ + struct list_head linkage; /* Used by lists in trace_data_union */ + unsigned int used; /* number of bytes used within this page */ + unsigned short cpu; /* cpu that owns this page */ + unsigned short type; /* type(context) of this page */ +}; + +struct page_collection { + struct list_head pc_pages; + spinlock_t pc_lock; + int pc_want_daemon_pages; +}; + +/* ASSERTION that is safe to use within the debug system */ +#define __ASSERT(cond) \ +do { \ + if (unlikely(!(cond))) { \ + printk(KERN_ERR "ASSERTION("#cond") failed"); \ + BUG(); \ + } \ +} while (0) + +#define __ASSERT_TAGE_INVARIANT(tage) \ +do { \ + __ASSERT(tage != NULL); \ + __ASSERT(tage->page != NULL); \ + __ASSERT(tage->used <= PAGE_SIZE); \ + __ASSERT(page_count(tage->page) > 0); \ +} while(0) + +/* ASSERTION that will debug log used outside the debug sysytem */ +#define ASSERT(cond) \ +do { \ + if (unlikely(!(cond))) { \ + spl_debug_msg(NULL, DEBUG_SUBSYSTEM, D_EMERG, \ + __FILE__, __FUNCTION__, __LINE__, \ + "ASSERTION(" #cond ") failed\n"); \ + spl_debug_bug(__FILE__, __FUNCTION__, __LINE__); \ + } \ +} while (0) + +#define ASSERTF(cond, fmt, a...) \ +do { \ + if (unlikely(!(cond))) { \ + spl_debug_msg(NULL, DEBUG_SUBSYSTEM, D_EMERG, \ + __FILE__, __FUNCTION__, __LINE__, \ + "ASSERTION(" #cond ") failed:" fmt, \ + ## a); \ + spl_debug_bug(__FILE__, __FUNCTION__, __LINE__) \ + } \ +} while (0) + +#define VERIFY3_IMPL(LEFT, OP, RIGHT, TYPE, FMT, CAST) \ +do { \ + const TYPE __left = (TYPE)(LEFT); \ + const TYPE __right = (TYPE)(RIGHT); \ + if (!(__left OP __right)) { \ + spl_debug_msg(NULL, DEBUG_SUBSYSTEM, D_EMERG, \ + __FILE__, __FUNCTION__, __LINE__, \ + "VERIFY3(" FMT " " #OP " " FMT ")\n", \ + CAST __left, CAST __right); \ + spl_debug_bug(__FILE__, __FUNCTION__, __LINE__) \ + } \ +} while (0) + +#define VERIFY3S(x,y,z) VERIFY3_IMPL(x, y, z, int64_t, "%ld", (long)) +#define VERIFY3U(x,y,z) VERIFY3_IMPL(x, y, z, uint64_t, "%lu", (unsigned long)) +#define VERIFY3P(x,y,z) VERIFY3_IMPL(x, y, z, uintptr_t, "%p", (void *)) + +#define ASSERT3S(x,y,z) VERIFY3S(x, y, z) +#define ASSERT3U(x,y,z) VERIFY3U(x, y, z) +#define ASSERT3P(x,y,z) VERIFY3P(x, y, z) + +#define VERIFY(x) ASSERT(x) +#define SBUG() spl_debug_bug(__FILE__, __FUNCTION__, __LINE__); + +#define spl_debug_msg(cdls, subsys, mask, file, fn, line, format, a...) \ + spl_debug_vmsg(cdls, subsys, mask, file, fn, \ + line, NULL, NULL, format, ##a) + +#ifdef __ia64__ +#define CDEBUG_STACK() (THREAD_SIZE - \ + ((unsigned long)__builtin_dwarf_cfa() & \ + (THREAD_SIZE - 1))) +#else +#define CDEBUG_STACK() (THREAD_SIZE - \ + ((unsigned long)__builtin_frame_address(0) & \ + (THREAD_SIZE - 1))) +# endif /* __ia64__ */ + +#define __CHECK_STACK(file, func, line) \ +do { \ + unsigned long _stack = CDEBUG_STACK(); \ + \ + if (_stack > (3*THREAD_SIZE/4) && _stack > spl_debug_stack) { \ + spl_debug_stack = _stack; \ + spl_debug_msg(NULL, D_TRACE, D_WARNING, \ + file, func, line, \ + "Exceeded maximum safe stack " \ + "%lu/%lu\n", _stack, THREAD_SIZE); \ + __ASSERT(0); \ + } \ +} while (0) + +#define CHECK_STACK()__CHECK_STACK(__FILE__, __func__, __LINE__) + +#define __CDEBUG(cdls, subsys, mask, format, a...) \ +do { \ + CHECK_STACK(); \ + \ + if (((mask) & D_CANTMASK) != 0 || \ + ((spl_debug_mask & (mask)) != 0 && \ + (spl_debug_subsys & (subsys)) != 0)) \ + spl_debug_msg(cdls, subsys, mask, \ + __FILE__, __FUNCTION__, __LINE__, \ + format, ## a); \ +} while (0) + +#define CDEBUG(mask, format, a...) \ + __CDEBUG(NULL, DEBUG_SUBSYSTEM, mask, format, ## a) + +#define __CDEBUG_LIMIT(subsys, mask, format, a...) \ +do { \ + static spl_debug_limit_state_t cdls; \ + \ + __CDEBUG(&cdls, subsys, mask, format, ## a); \ +} while (0) + +#define CDEBUG_LIMIT(mask, format, a...) \ + __CDEBUG_LIMIT(DEBUG_SUBSYSTEM, mask, format, ## a) + +#define dprintf(fmt, a...) CDEBUG_LIMIT(D_INFO, fmt, ## a) +#define CWARN(fmt, a...) CDEBUG_LIMIT(D_WARNING, fmt, ## a) +#define CERROR(fmt, a...) CDEBUG_LIMIT(D_ERROR, fmt, ## a) +#define CEMERG(fmt, a...) CDEBUG_LIMIT(D_EMERG, fmt, ## a) +#define CONSOLE(mask, fmt, a...) CDEBUG(D_CONSOLE | (mask), fmt, ## a) + +#define GOTO(label, rc) \ +do { \ + long GOTO__ret = (long)(rc); \ + CDEBUG(D_TRACE,"Process leaving via %s (rc=%lu : %ld : %lx)\n", \ + #label, (unsigned long)GOTO__ret, (signed long)GOTO__ret,\ + (signed long)GOTO__ret); \ + goto label; \ +} while (0) + +#define RETURN(rc) \ +do { \ + typeof(rc) RETURN__ret = (rc); \ + CDEBUG(D_TRACE, "Process leaving (rc=%lu : %ld : %lx)\n", \ + (long)RETURN__ret, (long)RETURN__ret, (long)RETURN__ret);\ + return RETURN__ret; \ +} while (0) + +#define ENTRY \ +do { \ + CDEBUG(D_TRACE, "Process entered\n"); \ +} while (0) + +#define EXIT \ +do { \ + CDEBUG(D_TRACE, "Process leaving\n"); \ +} while(0) + +extern int spl_debug_vmsg(spl_debug_limit_state_t *cdls, int subsys, int mask, + const char *file, const char *fn, const int line, + const char *format1, va_list args, const char *format2, ...); + +extern unsigned long spl_debug_set_mask(unsigned long mask); +extern unsigned long spl_debug_get_mask(void); +extern unsigned long spl_debug_set_subsys(unsigned long mask); +extern unsigned long spl_debug_get_subsys(void); +extern int spl_debug_set_mb(int mb); +extern int spl_debug_get_mb(void); + +extern int spl_debug_dumplog(void); +extern void spl_debug_dumpstack(struct task_struct *tsk); +extern void spl_debug_bug(char *file, const char *func, const int line); + +extern int spl_debug_clear_buffer(void); +extern int spl_debug_mark_buffer(char *text); #endif /* SPL_DEBUG_H */ |