diff options
author | hunt <hunt> | 2005-05-26 07:43:25 +0000 |
---|---|---|
committer | hunt <hunt> | 2005-05-26 07:43:25 +0000 |
commit | abedf3db3774b54ee4ed227e3ae69e55fb0ff76c (patch) | |
tree | 77bf164eda3f977ba0e964c92493ea8c248fb6f3 | |
parent | 3750373a50833fcda902407e5b260cb8c5799ad6 (diff) | |
download | systemtap-steved-abedf3db3774b54ee4ed227e3ae69e55fb0ff76c.tar.gz systemtap-steved-abedf3db3774b54ee4ed227e3ae69e55fb0ff76c.tar.xz systemtap-steved-abedf3db3774b54ee4ed227e3ae69e55fb0ff76c.zip |
2005-05-26 Martin Hunt <hunt@redhat.com>
* current.c (_stp_sprint_regs): Implement for i386.
* sym.c (_stp_symbol_sprint): Check name before trying to
print it.
(_stp_symbol_print): Change to macro that calls _stp_symbol_sprint().
-rw-r--r-- | runtime/ChangeLog | 8 | ||||
-rw-r--r-- | runtime/Doxyfile | 12 | ||||
-rw-r--r-- | runtime/README | 93 | ||||
-rw-r--r-- | runtime/alloc.c | 11 | ||||
-rw-r--r-- | runtime/current.c | 94 | ||||
-rw-r--r-- | runtime/map-keys.c | 4 | ||||
-rw-r--r-- | runtime/map.h | 47 | ||||
-rw-r--r-- | runtime/print.c | 12 | ||||
-rw-r--r-- | runtime/stack.c | 284 | ||||
-rw-r--r-- | runtime/string.c | 3 | ||||
-rw-r--r-- | runtime/sym.c | 30 |
11 files changed, 290 insertions, 308 deletions
diff --git a/runtime/ChangeLog b/runtime/ChangeLog index 7519e701..8ae68395 100644 --- a/runtime/ChangeLog +++ b/runtime/ChangeLog @@ -1,3 +1,11 @@ +2005-05-26 Martin Hunt <hunt@redhat.com> + + * current.c (_stp_sprint_regs): Implement for i386. + + * sym.c (_stp_symbol_sprint): Check name before trying to + print it. + (_stp_symbol_print): Change to macro that calls _stp_symbol_sprint(). + 2005-05-18 Martin Hunt <hunt@redhat.com> * print.c: All functions except _stp_print_flush() are diff --git a/runtime/Doxyfile b/runtime/Doxyfile index 0e8d4f9e..cebc99b3 100644 --- a/runtime/Doxyfile +++ b/runtime/Doxyfile @@ -17,13 +17,13 @@ # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. -PROJECT_NAME = SystemTap +PROJECT_NAME = "SystemTap Runtime" # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = 0.2 +PROJECT_NUMBER = 0.5 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. @@ -432,7 +432,7 @@ INPUT = # *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp # *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm -FILE_PATTERNS = *.c *.h README TODO +FILE_PATTERNS = *.c *.h README TODO map.doc # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. @@ -444,7 +444,7 @@ RECURSIVE = YES # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. -EXCLUDE = docs tests relayfs stpd +EXCLUDE = docs tests relayfs stpd transport user # The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories # that are symbolic links (a Unix filesystem feature) are excluded from the input. @@ -927,7 +927,7 @@ ENABLE_PREPROCESSING = YES # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. -MACRO_EXPANSION = YES +MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the @@ -961,7 +961,7 @@ INCLUDE_FILE_PATTERNS = # undefined via #undef or recursively expanded use the := operator # instead of the = operator. -PREDEFINED = +PREDEFINED = __i386__ # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. diff --git a/runtime/README b/runtime/README index e0e32a37..aecb3282 100644 --- a/runtime/README +++ b/runtime/README @@ -3,8 +3,8 @@ @section intro_sec Introduction This document describes the implementation of the SystemTap Runtime. It is intended for developers -of the SystemTap Language translator or, possibly TapSet authors. These functions are not directly -available from the SystemTap Language. +of the SystemTap Language translator, TapSet authors or kprobes programmers. +These functions are not directly available from the SystemTap Language. The SystemTap Runtime Library consists of all functions and code fragments needed by the compiler/translator to @@ -13,7 +13,7 @@ also include I/O code to transmit its output from the kernel to userspace. In addition to the library, the runtime includes a SystemTap user-space daemon (stpd). Stpd grabs data sent from the I/O code in the runtime and displays it -and/or saves it to files. Stpd (or a script invoking it) will handle other issues like +and/or saves it to files. Stpd will handle other issues like inserting and removing modules. Stpd and the I/O code make use of both relayfs and netlink for communication. For @@ -22,41 +22,58 @@ kernels without relayfs builtin, it is provided as a standalone module under the @section design_sec Design @subsection impl_sec Implementation The library is written in C and is really not a library but a collection of code -That can be conditionally included in a modules. It may become a library later, but for now -there are some advantages to being able to change the sizes of static items with simple #defines. +that can be conditionally included in a modules. This allows for a high degree of +customization and efficiency. @subsection map_sec Maps (Associative Arrays) -Maps are implemented as hash lists. It is not expected that users will -attempt to collect so much data in kernel space that performance problems will require -more complex solutions such as AVL trees. - -Maps are created with _stp_map_new(). Each map can hold only one type of -data; int64, string, or statistics. Each element belonging to a map can have up to 2 keys -and a value. Implemented key types are strings and longs. +Maps are implemented as hash lists. In order to efficiently handle maps with up to five keys of any combination +of int64s and strings, the functions to create maps, set keys, and set and get values +are all generated as needed. To do this, you must simply do some defines and +include the proper files. For example, if you need a map with 3 keys, int64, int64, and string +that stores strings, you need to do +@code +#define KEY1_TYPE INT64 +#define KEY2_TYPE INT64 +#define KEY3_TYPE STRING +#include "map-keys.c" + +#define VALUE_TYPE STRING +#include "map-values.c" + +#include "map.c" +@endcode + +In the above example, maps are created with _stp_map_new_int64_int64_str(). All the +generated functions start with a base name (such as _stp_map_new) and add the key types separated +by underscores. + +Each map can hold only one type of data; int64, string, or statistics. Each element belonging to a map can have up to +5 keys and a value. Implemented key types are strings and int64. To simplify the implementation, the functions to set the key and the functions to set the data are separated. -That means we need only 4 functions to set the key and 3 functions to set the value. - -For example: -\code -/* create a map with a max of 100 elements */ -MAP mymap = map_new(100, INT64); +The map remembers what the current key is. +For example, to continue our previous example, +@code +/* create a map with a max of 100 elements containing strings */ +MAP mymap = _stp_map_new_int64_int64_str(100, STRING); -/* mymap[birth year] = 2000 */ -map_key_str (mymap, "birth year"); -map_set_int64 (mymap, 2000); -\endcode +/* mymap[100, 300, "Ohio"] = "Columbus" */ +_stp_map_key_int64_int64_str (mymap, 100, 300, "Ohio"); +_stp_map_set_str (mymap, "Columbus"); +@endcode All elements have a default value of 0 (or NULL). Elements are only saved to the map when their value is set -to something nonzero. This means that querying for the existance of a key is inexpensive because -no element is created, just a hash table lookup. +to something nonzero. + +For good examples, see the runtime/tests/maps directory or the example probes. @subsection list_sec Lists -A list is a special map which has internally ascending long integer keys. Adding a value to +A list is a special map which has internally ascending int64 keys. Adding a value to a list does not require setting a key first. Create a list with _stp_list_new(). Add to it with _stp_list_add_str() and _stp_list_add_int64(). Clear it with _stp_list_clear(). @subsection string_sec Strings + One of the biggest restrictions the library has is that it cannot allocate things like strings off the stack. It is also not a good idea to dynamically allocate space for strings with kmalloc(). That leaves us with statically allocated space for strings. This is what is implemented in the String module. Strings use @@ -91,12 +108,26 @@ at the top of the module and all output will go over a netlink channel. In the SystemTap language, we will provide some simple functions to control the buffering policy, which will control the use of netlink and parameters to relayfs and stpd. -@section status_sec Status -@li Maps are implemented and tested. Histograms are not yet finished. -@li Copy_From_User functions are done. -@li If maps overflow or memory runs out for some reason, globals are set but nothing is done yet. -I expect to implement a function to tell the system to either ignore it or unload the module and quit. -@li Stack functions need much improvement. + +@section started Getting Started +@verbatim +>cd stp/src/runtime +>cd stpd +> make +>cd ../relayfs +> make +> cd ../transport +make +> cd ../probes/shellsnoop +> make +> su +> ./stp shellsnoop.ko +@endverbatim + +When building the example probes, ignore the warnings about relayfs and +_stp_ctrl_xxx functions undefined. These are in other modules. + +Hit ^C to exit from a probe. @section probe_sec Example Probes diff --git a/runtime/alloc.c b/runtime/alloc.c index 9b348b27..480484d3 100644 --- a/runtime/alloc.c +++ b/runtime/alloc.c @@ -1,7 +1,6 @@ -#ifndef _ALLOC_C_ +#ifndef _ALLOC_C_ /* -*- linux-c -*- */ #define _ALLOC_C_ -/* -*- linux-c -*- */ /** @file alloc.c * @brief Memory functions. */ @@ -11,9 +10,6 @@ * send a signal to the user-space daemon that will trigger the module to * be unloaded. * @todo Need error handling for memory allocations - * @todo Some of these currently use kmalloc (GFP_ATOMIC) for - * small allocations. This should be evaluated for performance - * and stability. * @{ */ @@ -25,7 +21,7 @@ enum errorcode _stp_error = ERR_NONE; * probe where the process cannot sleep. * @param len Number of bytes to allocate. * @return a valid pointer on success or NULL on failure. - * @bug Currently uses kmalloc (GFP_ATOMIC). + * @note Not currently used by the runtime. Deprecate? */ void *_stp_alloc(size_t len) @@ -41,7 +37,7 @@ void *_stp_alloc(size_t len) * probe where the process cannot sleep. * @param len Number of bytes to allocate. * @return a valid pointer on success or NULL on failure. - * @bug Currently uses kmalloc (GFP_ATOMIC). + * @note Not currently used by the runtime. Deprecate? */ void *_stp_calloc(size_t len) @@ -71,6 +67,7 @@ void *_stp_valloc(size_t len) /** Frees memory allocated by _stp_alloc or _stp_calloc. * @param ptr pointer to memory to free + * @note Not currently used by the runtime. Deprecate? */ void _stp_free(void *ptr) diff --git a/runtime/current.c b/runtime/current.c index 618c9fc9..4485ef2c 100644 --- a/runtime/current.c +++ b/runtime/current.c @@ -1,7 +1,8 @@ -#ifndef _CURRENT_C_ +#ifndef _CURRENT_C_ /* -*- linux-c -*- */ #define _CURRENT_C_ -/* -*- linux-c -*- */ +#include "regs.h" + /** @file current.c * @brief Functions to get the current state. */ @@ -14,48 +15,37 @@ /** Get the current return address. * Call from kprobes (not jprobes). * @param regs The pt_regs saved by the kprobe. - * @return The return address saved in esp or rsp. + * @return The return address saved in the stack pointer. * @note i386 and x86_64 only so far. */ unsigned long _stp_ret_addr (struct pt_regs *regs) { -#ifdef __x86_64__ - unsigned long *ra = (unsigned long *)regs->rsp; -#else - unsigned long *ra = (unsigned long *)regs->esp; -#endif - if (ra) - return *ra; - else - return 0; + unsigned long *ra = (unsigned long *)REG_SP(regs); + + if (ra) + return *ra; + else + return 0; } #ifdef __x86_64__ -#include <linux/utsname.h> - -void _stp_print_regs(struct pt_regs * regs) +void _stp_sprint_regs(String str, struct pt_regs * regs) { unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L, fs, gs, shadowgs; unsigned int fsindex,gsindex; unsigned int ds,cs,es; - _stp_printf("\n"); - // print_modules(); - _stp_printf("Pid: %d, comm: %.20s %s\n", - current->pid, current->comm, system_utsname.release); - _stp_printf("RIP: %04lx:[<%016lx>] ", regs->cs & 0xffff, regs->rip); - _stp_symbol_print (regs->rip); - _stp_printf("\nRSP: %04lx:%016lx EFLAGS: %08lx\n", regs->ss, regs->rsp, regs->eflags); - _stp_printf("RAX: %016lx RBX: %016lx RCX: %016lx\n", + _stp_sprintf(str,"RIP: %016lx\nRSP: %016lx EFLAGS: %08lx\n", regs->rip, regs->rsp, regs->eflags); + _stp_sprintf(str,"RAX: %016lx RBX: %016lx RCX: %016lx\n", regs->rax, regs->rbx, regs->rcx); - _stp_printf("RDX: %016lx RSI: %016lx RDI: %016lx\n", + _stp_sprintf(str,"RDX: %016lx RSI: %016lx RDI: %016lx\n", regs->rdx, regs->rsi, regs->rdi); - _stp_printf("RBP: %016lx R08: %016lx R09: %016lx\n", + _stp_sprintf(str,"RBP: %016lx R08: %016lx R09: %016lx\n", regs->rbp, regs->r8, regs->r9); - _stp_printf("R10: %016lx R11: %016lx R12: %016lx\n", + _stp_sprintf(str,"R10: %016lx R11: %016lx R12: %016lx\n", regs->r10, regs->r11, regs->r12); - _stp_printf("R13: %016lx R14: %016lx R15: %016lx\n", + _stp_sprintf(str,"R13: %016lx R14: %016lx R15: %016lx\n", regs->r13, regs->r14, regs->r15); asm("movl %%ds,%0" : "=r" (ds)); @@ -73,15 +63,55 @@ void _stp_print_regs(struct pt_regs * regs) asm("movq %%cr3, %0": "=r" (cr3)); asm("movq %%cr4, %0": "=r" (cr4)); - _stp_printf("FS: %016lx(%04x) GS:%016lx(%04x) knlGS:%016lx\n", + _stp_sprintf(str,"FS: %016lx(%04x) GS:%016lx(%04x) knlGS:%016lx\n", fs,fsindex,gs,gsindex,shadowgs); - _stp_printf("CS: %04x DS: %04x ES: %04x CR0: %016lx\n", cs, ds, es, cr0); - _stp_printf("CR2: %016lx CR3: %016lx CR4: %016lx\n", cr2, cr3, cr4); - _stp_print_flush(); + _stp_sprintf(str,"CS: %04x DS: %04x ES: %04x CR0: %016lx\n", cs, ds, es, cr0); + _stp_sprintf(str,"CR2: %016lx CR3: %016lx CR4: %016lx\n", cr2, cr3, cr4); } -#endif /* __x86_64__ */ +#elif defined (__i386__) +/** Write the registers to a string. + * @param regs The pt_regs saved by the kprobe. + * @note i386 and x86_64 only so far. + */ +void _stp_sprint_regs(String str, struct pt_regs * regs) +{ + unsigned long cr0 = 0L, cr2 = 0L, cr3 = 0L, cr4 = 0L; + + _stp_sprintf (str, "EIP: %08lx\n",regs->eip); + _stp_sprintf (str, "ESP: %08lx\n",regs->esp); + _stp_sprintf (str, "EAX: %08lx EBX: %08lx ECX: %08lx EDX: %08lx\n", + regs->eax,regs->ebx,regs->ecx,regs->edx); + _stp_sprintf (str, "ESI: %08lx EDI: %08lx EBP: %08lx", + regs->esi, regs->edi, regs->ebp); + _stp_sprintf (str, " DS: %04x ES: %04x\n", + 0xffff & regs->xds,0xffff & regs->xes); + + __asm__("movl %%cr0, %0": "=r" (cr0)); + __asm__("movl %%cr2, %0": "=r" (cr2)); + __asm__("movl %%cr3, %0": "=r" (cr3)); + /* This could fault if %cr4 does not exist */ + __asm__("1: movl %%cr4, %0 \n" + "2: \n" + ".section __ex_table,\"a\" \n" + ".long 1b,2b \n" + ".previous \n" + : "=r" (cr4): "0" (0)); + _stp_sprintf (str, "CR0: %08lx CR2: %08lx CR3: %08lx CR4: %08lx\n", cr0, cr2, cr3, cr4); +} + +#endif + +/** Print the registers. + * @param regs The pt_regs saved by the kprobe. + * @note i386 and x86_64 only so far. + */ +#define _stp_print_regs(regs) \ + { \ + _stp_sprint_regs(_stp_stdout,regs); \ + _stp_print_flush(); \ + } /** @} */ #endif /* _CURRENT_C_ */ diff --git a/runtime/map-keys.c b/runtime/map-keys.c index b51118ec..f9221410 100644 --- a/runtime/map-keys.c +++ b/runtime/map-keys.c @@ -271,8 +271,8 @@ static unsigned int KEYSYM(hash) (ALLKEYSD(key)) return (unsigned int) hash; } -/* _stp_map_new_key1_key2 (num, STAT, LINEAR, start, end, interval) */ -/* _stp_map_new_key1_key2 (num, STAT, LOG, buckets) */ +/* _stp_map_new_key1_key2 (num, HSTAT_LINEAR, start, end, interval) */ +/* _stp_map_new_key1_key2 (num, HSTAT_LOG, buckets) */ MAP KEYSYM(_stp_map_new) (unsigned max_entries, int valtype, ...) { diff --git a/runtime/map.h b/runtime/map.h index e48ec574..6de06f17 100644 --- a/runtime/map.h +++ b/runtime/map.h @@ -5,19 +5,25 @@ * @brief Header file for maps and lists */ /** @addtogroup maps - * @todo Needs to be made SMP-safe for when the big lock is removed from kprobes. + * @todo Needs a spinlock variable to help when locks are required on the map. * @{ */ +/** This sets the size of the hash table. */ #ifndef HASH_TABLE_BITS #define HASH_TABLE_BITS 8 +/** This sets the size of the hash table. */ #define HASH_TABLE_SIZE (1<<HASH_TABLE_BITS) #endif +/** The maximum number of keys allowed. Reducing this can save a small +amount of memory. Do not increase above 5. */ #ifndef MAX_KEY_ARITY #define MAX_KEY_ARITY 5 #endif +/** Maximum length of strings in maps. This sets the amount of space reserved + for each string. */ #ifndef MAP_STRING_LENGTH #define MAP_STRING_LENGTH 256 #endif @@ -25,15 +31,19 @@ /** histogram type */ enum histtype { HIST_NONE, HIST_LOG, HIST_LINEAR }; +/** @cond DONT_INCLUDE */ #define INT64 0 #define STRING 1 #define STAT 2 -#define END 3 /* end marker */ +#define END 3 +/** @endcond */ +/** Histogram is log 2 */ #define HSTAT_LOG (STAT | (HIST_LOG << 8)) +/** Histogram is linear */ #define HSTAT_LINEAR (STAT | (HIST_LINEAR << 8)) -/* Statistics are stored in this struct */ +/** Statistics are stored in this struct */ typedef struct { int64_t count; int64_t sum; @@ -42,7 +52,7 @@ typedef struct { } stat; -/* Keys are either int64 or strings */ +/** Keys are either int64 or strings */ typedef union { int64_t val; char *strp; @@ -125,25 +135,23 @@ typedef struct map_root *MAP; /** Macro to call the proper _stp_map_key functions based on the * types of the arguments. - * @note May cause compiler warning on some GCCs */ #define _stp_map_key2(map, key1, key2) \ - ({ \ - if (__builtin_types_compatible_p (typeof (key1), char[])) \ - if (__builtin_types_compatible_p (typeof (key2), char[])) \ - _stp_map_key_str_str (map, (char *)(key1), (char *)(key2)); \ - else \ - _stp_map_key_str_long (map, (char *)(key1), (long)(key2)); \ - else \ - if (__builtin_types_compatible_p (typeof (key2), char[])) \ - _stp_map_key_long_str (map, (long)(key1), (char *)(key2)); \ - else \ - _stp_map_key_long_long (map, (long)(key1), (long)(key2)); \ - }) +({ \ + if (__builtin_types_compatible_p (typeof (key1), char[])) \ + if (__builtin_types_compatible_p (typeof (key2), char[])) \ + _stp_map_key_str_str (map, (char *)(key1), (char *)(key2)); \ + else \ + _stp_map_key_str_int64 (map, (char *)(key1), (int64_t)(key2)); \ + else \ + if (__builtin_types_compatible_p (typeof (key2), char[])) \ + _stp_map_key_int64_str (map, (int64_t)(key1), (char *)(key2)); \ + else \ + _stp_map_key_int64_int64 (map, (int64_t)(key1), (int64_t)(key2)); \ +}) /** Macro to call the proper _stp_map_key function based on the * type of the argument. - * @note May cause compiler warning on some GCCs */ #define _stp_map_key(map, key) \ ({ \ @@ -155,7 +163,6 @@ typedef struct map_root *MAP; /** Macro to call the proper _stp_map_set function based on the * type of the argument. - * @note May cause compiler warning on some GCCs */ #define _stp_map_set(map, val) \ ({ \ @@ -199,6 +206,7 @@ typedef struct map_root *MAP; }) +/** @cond DONT_INCLUDE */ /************* prototypes for map.c ****************/ int int64_eq_p(int64_t key1, int64_t key2); @@ -241,4 +249,5 @@ void _stp_list_add_string(MAP, String); void _stp_map_key_int64(MAP, int64_t); void _stp_map_set_int64(MAP, int64_t); int64_t _stp_map_get_int64(MAP); +/** @endcond */ #endif /* _MAP_H_ */ diff --git a/runtime/print.c b/runtime/print.c index 44776578..e9847ef8 100644 --- a/runtime/print.c +++ b/runtime/print.c @@ -64,9 +64,10 @@ void _stp_print_flush (void) static char _stp_pbuf[NR_CPUS][STP_PRINT_BUF_LEN + STP_PRINT_BUF_START + 1]; -/** Send the print buffer now. - * Output accumulates in the print buffer until this is called. - * Size is limited by length of print buffer, #STP_PRINT_BUF_LEN. +/** Send the print buffer to the transport now. + * Output accumulates in the print buffer until it + * is filled, or this is called. This MUST be called before returning + * from a probe or accumulated output in the print buffer will be lost. */ void _stp_print_flush (void) @@ -91,10 +92,7 @@ void _stp_print_flush (void) /** Print into the print buffer. * Like printf, except output goes to the print buffer. * Safe because overflowing the buffer is not allowed. - * Size is limited by length of print buffer, #STP_PRINT_BUF_LEN. * - * @param fmt A printf-style format string followed by a - * variable number of args. * @sa _stp_print_flush() */ #define _stp_printf(args...) _stp_sprintf(_stp_stdout,args) @@ -109,7 +107,6 @@ void _stp_print_flush (void) /** Write a C string into the print buffer. * Copies a string into a print buffer. * Safe because overflowing the buffer is not allowed. - * Size is limited by length of print buffer, #STP_PRINT_BUF_LEN. * This is more efficient than using _stp_printf() if you don't * need fancy formatting. * @@ -122,7 +119,6 @@ void _stp_print_flush (void) /** Write a String into the print buffer. * Copies a String into a print buffer. * Safe because overflowing the buffer is not allowed. - * Size is limited by length of print buffer, #STP_PRINT_BUF_LEN. * This is more efficient than using _stp_printf() if you don't * need fancy formatting. * diff --git a/runtime/stack.c b/runtime/stack.c index f06475dc..1f7073f5 100644 --- a/runtime/stack.c +++ b/runtime/stack.c @@ -7,104 +7,22 @@ */ /** @addtogroup stack Stack Tracing Functions + * Without frames the best that can be done here is to scan the stack and + * display everything that fits in the range of a valid IP. Things like function pointers + * on the stack will certainly result in bogus addresses in the backtrace. + * + * With debug info, we could get a proper backtrace, but it would be too slow to do + * during a probe. We can eventually make this a postprocessing feature. + * * @{ */ #include "sym.c" +#include "regs.h" static int (*_stp_kta)(unsigned long addr)=(void *)KTA; - -struct frame_head { - struct frame_head * ebp; - unsigned long ret; -} __attribute__((packed)); - -static struct frame_head * -dump_backtrace(struct frame_head * head) -{ - _stp_printf ("db: %lx\n", head->ret); - - /* frame pointers should strictly progress back up the stack - * (towards higher addresses) */ - if (head >= head->ebp) - return NULL; - - return head->ebp; -} - -static int pages_present(struct frame_head * head) -{ - struct mm_struct * mm = current->mm; - - /* FIXME: only necessary once per page */ - if (!check_user_page_readable(mm, (unsigned long)head)) - return 0; - - return check_user_page_readable(mm, (unsigned long)(head + 1)); -} - -static int valid_kernel_stack(struct frame_head * head, struct pt_regs * regs) -{ - unsigned long headaddr = (unsigned long)head; - unsigned long stack = (unsigned long)regs; - unsigned long stack_base = (stack & ~(THREAD_SIZE - 1)) + THREAD_SIZE; - _stp_log ("%lx %lx %lx\n", headaddr, stack, stack_base); - return headaddr < stack_base; -} - -void -x86_backtrace(struct pt_regs * const regs, unsigned int depth) -{ - struct frame_head *head; - -#ifdef CONFIG_X86_64 - head = (struct frame_head *)regs->rbp; -#else - head = (struct frame_head *)regs->ebp; -#endif - - if (!user_mode(regs)) { - _stp_log ("kernel mode\n"); - while (depth-- && valid_kernel_stack(head, regs)) - head = dump_backtrace(head); - _stp_print_flush(); - return; - } - -#ifdef CONFIG_SMP - if (!spin_trylock(¤t->mm->page_table_lock)) - return; -#endif - - while (depth-- && head && pages_present(head)) - head = dump_backtrace(head); - -#ifdef CONFIG_SMP - spin_unlock(¤t->mm->page_table_lock); -#endif - _stp_print_flush(); -} - - -#ifdef __x86_64__ -static void __stp_stack_print (unsigned long *stack, int verbose, int levels) -{ - unsigned long addr; - while (((long) stack & (THREAD_SIZE-1)) != 0) { - addr = *stack; - if (_stp_kta(addr)) { - if (verbose) { - _stp_symbol_print (addr); - _stp_print ("\n"); - } else - _stp_printf ("0x%lx ", addr); - } - stack++; - } - _stp_print_flush(); -} - +#if defined (__x86_64__) static void __stp_stack_sprint (String str, unsigned long *stack, int verbose, int levels) { @@ -113,151 +31,153 @@ static void __stp_stack_sprint (String str, unsigned long *stack, int verbose, i addr = *stack++; if (_stp_kta(addr)) { if (verbose) { + _stp_string_cat(str, " "); _stp_symbol_sprint (str, addr); - _stp_sprintf (str, "\n"); - } else - _stp_sprintf (str, "0x%lx\n", addr); + _stp_string_cat (str, "\n"); + } else + _stp_sprintf (str, " 0x%lx\n", addr); } } } -#else /* i386 */ +#elif defined (__i386__) -static inline int valid_stack_ptr (struct thread_info *tinfo, void *p) +static inline int valid_stack_ptr(struct thread_info *tinfo, void *p) { return p > (void *)tinfo && p < (void *)tinfo + THREAD_SIZE - 3; } -static inline unsigned long _stp_print_context_stack ( - struct thread_info *tinfo, - unsigned long *stack, - unsigned long ebp ) -{ - unsigned long addr; - -#ifdef CONFIG_FRAME_POINTER - while (valid_stack_ptr(tinfo, (void *)ebp)) { - addr = *(unsigned long *)(ebp + 4); - _stp_symbol_print (addr); - _stp_print_cstr("\n"); - ebp = *(unsigned long *)ebp; - } -#else - while (valid_stack_ptr(tinfo, stack)) { - addr = *stack++; - if (_stp_kta (addr)) { - _stp_symbol_print (addr); - _stp_print_cstr ("\n"); - } - } -#endif - _stp_print_flush(); - return ebp; -} - -static inline unsigned long _stp_sprint_context_stack ( - String str, - struct thread_info *tinfo, - unsigned long *stack, - unsigned long ebp ) +static inline unsigned long print_context_stack(String str, struct thread_info *tinfo, + unsigned long *stack, unsigned long ebp) { unsigned long addr; #ifdef CONFIG_FRAME_POINTER while (valid_stack_ptr(tinfo, (void *)ebp)) { addr = *(unsigned long *)(ebp + 4); + _stp_string_cat(str, " "); _stp_symbol_sprint (str, addr); - _stp_string_cat (str, "\n"); + _stp_string_cat(str, "\n"); ebp = *(unsigned long *)ebp; } #else while (valid_stack_ptr(tinfo, stack)) { addr = *stack++; - if (_stp_kta (addr)) { - _stp_symbol_sprint (str, addr); - _stp_string_cat (str, "\n"); + if (_stp_kta(addr)) { + _stp_string_cat(str, " "); + _stp_symbol_sprint(str, addr); + _stp_string_cat(str, "\n"); } } #endif return ebp; } -static void __stp_stack_print (unsigned long *stack, int verbose, int levels) +static void __stp_stack_sprint (String str, unsigned long *stack, int verbose, int levels) { unsigned long ebp; /* Grab ebp right from our regs */ asm ("movl %%ebp, %0" : "=r" (ebp) : ); - while (stack) { - struct thread_info *context = (struct thread_info *) + while (1) { + struct thread_info *context; + context = (struct thread_info *) ((unsigned long)stack & (~(THREAD_SIZE - 1))); - ebp = _stp_print_context_stack (context, stack, ebp); + ebp = print_context_stack(str, context, stack, ebp); stack = (unsigned long*)context->previous_esp; + if (!stack) + break; } - _stp_print_flush (); } -static void __stp_stack_sprint (String str, unsigned long *stack, int verbose, int levels) -{ - unsigned long ebp; - /* Grab ebp right from our regs */ - asm ("movl %%ebp, %0" : "=r" (ebp) : ); - - while (stack) { - struct thread_info *context = (struct thread_info *) - ((unsigned long)stack & (~(THREAD_SIZE - 1))); - ebp = _stp_sprint_context_stack (str, context, stack, ebp); - stack = (unsigned long*)context->previous_esp; - } -} +#else +#error "Unsupported architecture" +#endif -#endif /* i386 */ -/** Print stack dump. - * Prints a stack dump to the print buffer. - * @param verbose Verbosity - * @param levels Number of levels to trace. - * @todo Implement verbosity and levels parameters. - * @bug levels parameter is not functional +/** Writes stack backtrace to a String + * + * @param str String + * @param regs A pointer to the struct pt_regs. + * @returns Same String as was input with trace info appended, */ - -void _stp_stack_jprint (int verbose, int levels) +String _stp_stack_sprint (String str, struct pt_regs *regs) { - unsigned long stack; - __stp_stack_print (&stack, verbose, levels); + _stp_sprintf (str, "trace for %d (%s)\n ", current->pid, current->comm); + _stp_symbol_sprint (str, REG_IP(regs)); + _stp_string_cat(str, "\n"); + __stp_stack_sprint (str, (unsigned long *)®_SP(regs), 1, 0); + return str; } -void _stp_stack_print (struct pt_regs *regs, int verbose, int levels) -{ - if (verbose) { - _stp_printf ("trace for %d (%s)\n", current->pid, current->comm); - _stp_symbol_print (regs->rip); - _stp_print ("\n"); - } else - _stp_printf ("0x%lx ", regs->rip); +/** Prints the stack backtrace + * @param regs A pointer to the struct pt_regs. + * @note Calls _stp_print_flush(). + */ - __stp_stack_print ((unsigned long *)regs->rsp, verbose, levels); -} +#define _stp_stack_print(regs) \ + { \ + (void)_stp_stack_sprint(_stp_stdout,regs); \ + _stp_print_flush(); \ + } -/** Writes stack dump to a String - * +/** Writes stack backtrace to a String. + * Use this when calling from a jprobe. * @param str String - * @param verbose Verbosity - * @param levels Number of levels to trace. - * @returns Same String as was input. - * @todo Implement verbosity and levels parameters. - * @bug levels parameter is not functional + * @returns Same String as was input with trace info appended, + * @sa _stp_stack_sprint() + */ +String _stp_stack_sprintj(String str) +{ + unsigned long stack; + _stp_sprintf (str, "trace for %d (%s)\n", current->pid, current->comm); + __stp_stack_sprint (str, &stack, 1, 0); + return str; +} + +/** Prints the stack backtrace. + * Use this when calling from a jprobe. + * @sa _stp_stack_print() + * @note Calls _stp_print_flush(). */ +#define _stp_stack_printj() \ + { \ + (void)_stp_stack_sprintj(_stp_stdout); \ + _stp_print_flush(); \ + } -String _stp_stack_sprint (String str, int verbose, int levels) +/** Writes the user stack backtrace to a String + * @param str String + * @returns Same String as was input with trace info appended, + * @note Currently limited to a depth of two. Works from jprobes and kprobes. + */ +String _stp_ustack_sprint (String str) { - unsigned long stack; - __stp_stack_sprint (str, &stack, verbose, levels); - return str; + struct pt_regs *nregs = ((struct pt_regs *) (THREAD_SIZE + (unsigned long) current->thread_info)) - 1; +#if BITS_PER_LONG == 64 + _stp_sprintf (str, " 0x%016lx : [user]\n", REG_IP(nregs)); + if (REG_SP(nregs)) + _stp_sprintf (str, " 0x%016lx : [user]\n", *(unsigned long *)REG_SP(nregs)); +#else + _stp_sprintf (str, " 0x%08lx : [user]\n", REG_IP(nregs)); + if (REG_SP(nregs)) + _stp_sprintf (str, " 0x%08lx : [user]\n", *(unsigned long *)REG_SP(nregs)); +#endif + return str; } +/** Prints the user stack backtrace + * @note Currently limited to a depth of two. Works from jprobes and kprobes. + * Calls _stp_print_flush(). + */ +#define _stp_ustack_print() \ + { \ + (void)_stp_ustack_sprint(_stp_stdout); \ + _stp_print_flush(); \ + } + /** @} */ #endif /* _STACK_C_ */ diff --git a/runtime/string.c b/runtime/string.c index 16a20dfb..f079bf89 100644 --- a/runtime/string.c +++ b/runtime/string.c @@ -34,7 +34,7 @@ static struct string _stp_string[STP_NUM_STRINGS][NR_CPUS]; typedef struct string *String; /* set up a special stdout string */ -struct string __stp_stdout; +static struct string __stp_stdout; String _stp_stdout = &__stp_stdout; void _stp_vsprintf (String str, const char *fmt, va_list args); @@ -45,6 +45,7 @@ void _stp_vsprintf (String str, const char *fmt, va_list args); * @param num Number of the preallocated String to use. * #STP_NUM_STRINGS are statically allocated for our use. The * translator (or author) should be sure to grab a free one. + * @returns An empty String. */ String _stp_string_init (int num) diff --git a/runtime/sym.c b/runtime/sym.c index 1eec0d1d..a1c04a65 100644 --- a/runtime/sym.c +++ b/runtime/sym.c @@ -36,11 +36,15 @@ String _stp_symbol_sprint (String str, unsigned long address) name = _stp_kallsyms_lookup(address, &size, &offset, &modname, namebuf); - _stp_sprintf (str, "0x%lx : ", address); - if (modname) - _stp_sprintf (str, "%s+%#lx/%#lx [%s]", name, offset, size, modname); - else - _stp_sprintf (str, "%s+%#lx/%#lx", name, offset, size); + _stp_sprintf (str, "0x%lx", address); + + if (name) { + if (modname) + _stp_sprintf (str, " : %s+%#lx/%#lx [%s]", name, offset, size, modname); + else + _stp_sprintf (str, " : %s+%#lx/%#lx", name, offset, size); + } + return str; } @@ -51,21 +55,7 @@ String _stp_symbol_sprint (String str, unsigned long address) * a probe because it is too time-consuming. Use at module exit time. */ -void _stp_symbol_print (unsigned long address) -{ - char *modname; - const char *name; - unsigned long offset, size; - char namebuf[KSYM_NAME_LEN+1]; - - name = _stp_kallsyms_lookup(address, &size, &offset, &modname, namebuf); - - _stp_printf ("0x%lx : ", address); - if (modname) - _stp_printf ("%s+%#lx/%#lx [%s]", name, offset, size, modname); - else - _stp_printf ("%s+%#lx/%#lx", name, offset, size); -} +#define _stp_symbol_print(address) _stp_symbol_sprint(_stp_stdout,address) /** @} */ #endif /* _SYM_C_ */ |