diff options
Diffstat (limited to 'runtime/user')
-rw-r--r-- | runtime/user/README | 1 | ||||
-rw-r--r-- | runtime/user/alloc.c | 96 | ||||
-rw-r--r-- | runtime/user/emul.h | 9 | ||||
-rw-r--r-- | runtime/user/print.c | 161 | ||||
-rwxr-xr-x | runtime/user/recreate_links | 8 | ||||
-rw-r--r-- | runtime/user/runtime.h | 39 | ||||
-rw-r--r-- | runtime/user/test.h | 247 |
7 files changed, 561 insertions, 0 deletions
diff --git a/runtime/user/README b/runtime/user/README new file mode 100644 index 00000000..fcbad004 --- /dev/null +++ b/runtime/user/README @@ -0,0 +1 @@ +These files are for user-space testing of SystemTap code. diff --git a/runtime/user/alloc.c b/runtime/user/alloc.c new file mode 100644 index 00000000..433a3ab0 --- /dev/null +++ b/runtime/user/alloc.c @@ -0,0 +1,96 @@ +#ifndef _ALLOC_C_ +#define _ALLOC_C_ + +/* -*- linux-c -*- */ +/** @file alloc.c + * @brief Memory functions. + */ +/** @addtogroup alloc Memory Functions + * Basic malloc/calloc/free functions. These will be changed so + * that memory allocation errors will call a handler. The default will + * 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. + * @{ + */ + +void *malloc(size_t size); +void free(void *ptr); + +enum errorcode { ERR_NONE=0, ERR_NO_MEM }; +enum errorcode _stp_error = ERR_NONE; + +/** Allocates memory within a probe. + * This is used for small allocations from within a running + * 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). + */ + +void *_stp_alloc(size_t len) +{ + void *ptr = malloc(len); + if (unlikely(ptr == NULL)) + _stp_error = ERR_NO_MEM; + return ptr; +} + +/** Allocates and clears memory within a probe. + * This is used for small allocations from within a running + * 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). + */ + +void *_stp_calloc(size_t len) +{ + void *ptr = malloc(len); + if (likely(ptr)) + memset(ptr, 0, len); + return ptr; +} + +/** Allocates and clears memory outside a probe. + * This is typically used in the module initialization to + * allocate new maps, lists, etc. + * @param len Number of bytes to allocate. + * @return a valid pointer on success or NULL on failure. + */ + +void *_stp_valloc(size_t len) +{ + void *ptr = malloc(len); + if (likely(ptr)) + memset(ptr, 0, len); + else + _stp_error = ERR_NO_MEM; + return ptr; +} + +/** Frees memory allocated by _stp_alloc or _stp_calloc. + * @param ptr pointer to memory to free + */ + +void _stp_free(void *ptr) +{ + if (likely(ptr)) + free(ptr); +} + +/** Frees memory allocated by _stp_valloc. + * @param ptr pointer to memory to free + */ + +void _stp_vfree(void *ptr) +{ + if (likely(ptr)) + free(ptr); +} + +/** @} */ +#endif /* _ALLOC_C_ */ diff --git a/runtime/user/emul.h b/runtime/user/emul.h new file mode 100644 index 00000000..922c6e4c --- /dev/null +++ b/runtime/user/emul.h @@ -0,0 +1,9 @@ +#define vscnprintf vsnprintf +#define _stp_log printf + +#undef smp_processor_id +#define smp_processor_id(x) 0 + +#include <stdarg.h> +unsigned long strtoul(const char *nptr, char **endptr, int base); +#define simple_strtoul strtoul diff --git a/runtime/user/print.c b/runtime/user/print.c new file mode 100644 index 00000000..c8fcf3e1 --- /dev/null +++ b/runtime/user/print.c @@ -0,0 +1,161 @@ +#ifndef _PRINT_C_ /* -*- linux-c -*- */ +#define _PRINT_C_ + +#include <linux/config.h> +#include <stdio.h> + +/** @file print.c + * @addtogroup print Print Buffer + * Print Buffer Functions. + * The print buffer is for collecting output to send to the user daemon. + * This is a per-cpu static buffer. The buffer is sent when + * _stp_print_flush() is called. + * + * The reason to do this is to allow multiple small prints to be combined then + * timestamped and sent together to stpd. It could flush automatically on newlines, + * but what about stack traces which span many lines? So try this and see how it works for us. + * @{ + */ + +/** Size of buffer, not including terminating NULL */ +#define STP_PRINT_BUF_LEN 8000 + +static int _stp_pbuf_len[NR_CPUS]; + +#define STP_PRINT_BUF_START 0 +static char _stp_pbuf[NR_CPUS][STP_PRINT_BUF_LEN + 1]; + +void _stp_print_flush (void) +{ + int cpu = smp_processor_id(); + char *buf = &_stp_pbuf[cpu][0]; + int len = _stp_pbuf_len[cpu]; + + if (len == 0) + return; + + /* enforce newline at end */ + if (buf[len - 1] != '\n') { + buf[len++] = '\n'; + buf[len] = '\0'; + } + + fwrite (buf, len, 1, stdout); + _stp_pbuf_len[cpu] = 0; +} + + +/** 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() + */ + +void _stp_printf (const char *fmt, ...) +{ + int num; + va_list args; + int cpu = smp_processor_id(); + char *buf = &_stp_pbuf[cpu][STP_PRINT_BUF_START] + _stp_pbuf_len[cpu]; + va_start(args, fmt); +// dbug ("%d bytes to %lx %lx\n", STP_PRINT_BUF_LEN - _stp_pbuf_len[cpu], buf, &_stp_pbuf[cpu][STP_PRINT_BUF_START]); + num = vsnprintf(buf, STP_PRINT_BUF_LEN - _stp_pbuf_len[cpu], fmt, args); +// dbug ("num = %d\n", num); + va_end(args); + if (num > 0 && num <= STP_PRINT_BUF_LEN - _stp_pbuf_len[cpu]) + _stp_pbuf_len[cpu] += num; +} + +/** Print into the print buffer. + * Use this if your function already has a va_list. + * You probably want _stp_printf(). + */ + +void _stp_vprintf (const char *fmt, va_list args) +{ + int num; + int cpu = smp_processor_id(); + char *buf = &_stp_pbuf[cpu][STP_PRINT_BUF_START] + _stp_pbuf_len[cpu]; + num = vsnprintf(buf, STP_PRINT_BUF_LEN -_stp_pbuf_len[cpu], fmt, args); + if (num > 0) + _stp_pbuf_len[cpu] += num; +} + +/** 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. + * + * @param str A C string. + * @sa _stp_print + */ + +void _stp_print_cstr (const char *str) +{ + int cpu = smp_processor_id(); + char *buf = &_stp_pbuf[cpu][STP_PRINT_BUF_START] + _stp_pbuf_len[cpu]; + int num = strlen (str); + if (num > STP_PRINT_BUF_LEN - _stp_pbuf_len[cpu]) + num = STP_PRINT_BUF_LEN - _stp_pbuf_len[cpu]; + strncpy (buf, str, num+1); + _stp_pbuf_len[cpu] += num; +} + +/** Clear the scratch buffer. + * This function should be called before anything is written to + * the scratch buffer. Output will accumulate in the buffer + * until this function is called again. + * @returns A pointer to the buffer. + */ + +char *_stp_print_clear (void) +{ + int cpu = smp_processor_id(); + _stp_pbuf_len[cpu] = 0; + return &_stp_pbuf[cpu][STP_PRINT_BUF_START]; +} + +#include "string.c" + +/** 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. + * + * @param str A String. + * @sa _stp_print + */ + +void _stp_print_string (String str) +{ + if (str->len) + _stp_print_cstr (str->buf); +} + +/** Write a String or C string into the print buffer. + * This macro selects the proper function to call. + * @param str A String or C string (char *) + * @sa _stp_print_cstr _stp_print_string + */ + +#define _stp_print(str) \ + ({ \ + if (__builtin_types_compatible_p (typeof (str), char[])) { \ + char *x = (char *)str; \ + _stp_print_cstr(x); \ + } else { \ + String x = (String)str; \ + _stp_print_string(x); \ + } \ + }) + +/** @} */ +#endif /* _PRINT_C_ */ diff --git a/runtime/user/recreate_links b/runtime/user/recreate_links new file mode 100755 index 00000000..080654e9 --- /dev/null +++ b/runtime/user/recreate_links @@ -0,0 +1,8 @@ +#!/bin/bash + +ln -s ../map.h . +ln -s ../map.c . +ln -s ../map-values.c . +ln -s ../map-keys.c . +ln -s ../list.c . +ln -s ../string.c . diff --git a/runtime/user/runtime.h b/runtime/user/runtime.h new file mode 100644 index 00000000..38778b86 --- /dev/null +++ b/runtime/user/runtime.h @@ -0,0 +1,39 @@ +#ifndef _RUNTIME_H_ +#define _RUNTIME_H_ +/** @file runtime.h + * @brief Main include file for runtime functions. + */ + +#define __KERNEL__ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/init.h> +#include <linux/hash.h> +#include <linux/kprobes.h> +#include <linux/proc_fs.h> +#include <linux/vmalloc.h> +#include <linux/time.h> +#include <linux/spinlock.h> +#include <asm/uaccess.h> +#include <linux/kallsyms.h> + + +#ifdef DEBUG +#define dbug(args...) \ + { \ + printf("%s:%d: ", __FUNCTION__, __LINE__); \ + printf(args); \ + } +#else +#define dbug(args...) ; +#endif + +#include "emul.h" + +#undef memcpy +#define memcpy __builtin_memcpy + +#include "print.c" + +#endif /* _RUNTIME_H_ */ diff --git a/runtime/user/test.h b/runtime/user/test.h new file mode 100644 index 00000000..3139cfec --- /dev/null +++ b/runtime/user/test.h @@ -0,0 +1,247 @@ +/* include file for testing maps without running in the kernel */ + +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> + +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) + +static inline unsigned long hash_long(unsigned long val, unsigned int bits) +{ + unsigned long hash = val; + +#if __WORDSIZE == 64 + /* Sigh, gcc can't optimise this alone like it does for 32 bits. */ + unsigned long n = hash; + n <<= 18; + hash -= n; + n <<= 33; + hash -= n; + n <<= 3; + hash += n; + n <<= 3; + hash -= n; + n <<= 4; + hash += n; + n <<= 2; + hash += n; +#else + /* On some cpus multiply is faster, on others gcc will do shifts */ + hash *= 0x9e370001UL; +#endif + + /* High bits are more random, so use them. */ + return hash >> (8*sizeof(long) - bits); +} + + +#define STRINGLEN 128 +#define HASH_ELEM_NUM 5000 +#define HASH_TABLE_BITS 8 +#define HASH_TABLE_SIZE (1<<HASH_TABLE_BITS) +#define BUCKETS 16 /* largest histogram width */ +#define PK 0 /* sprinkle debug printk into probe code */ + +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + + +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +#define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL) + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos; pos = pos->next) + +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = LIST_POISON1; + n->pprev = LIST_POISON2; +} + +static inline void hlist_del_init(struct hlist_node *n) +{ + if (n->pprev) { + __hlist_del(n); + INIT_HLIST_NODE(n); + } +} + +static inline void hlist_add_before(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; + next->pprev = &n->next; + *(n->pprev) = n; +} + +#define GFP_ATOMIC 0 + +void *kmalloc (size_t len, int flags) +{ + return malloc (len); +} + +void *vmalloc (size_t len) +{ + return malloc (len); +} + +#define kfree(x) free(x) +#define vfree(x) free(x) + +/***** END OF KERNEL STUFF ********/ + +#ifdef DEBUG +#define dbug(args...) \ + { \ + printf("%s:%d: ", __FUNCTION__, __LINE__); \ + printf(args); \ + } +#else +#define dbug(args...) ; +#endif + +#define dlog(args...) printf(args); +#define _stp_log(args...) printf(args); + +#define STP_NUM_STRINGS 5 +#define NR_CPUS 2 + +int smp_processor_id(void) +{ + return 0; +} + +#define vscnprintf vsnprintf + +#include "../alloc.c" +#include "../map.h" +#include "../map.c" + +/* handle renamed functions */ +#define map_new _stp_map_new +#define map_key_del _stp_map_key_del +#define map_start _stp_map_start +#define map_iter _stp_map_iter +#define map_get_str _stp_map_get_str +#define map_set_int64 _stp_map_set_int64 +#define map_get_int64 _stp_map_get_int64 +#define map_key_str_str _stp_map_key_str_str +#define map_key_str _stp_map_key_str +#define map_key_long _stp_map_key_long +#define map_key_long_long _stp_map_key_long_long +#define map_set_stat _stp_map_set_stat |