summaryrefslogtreecommitdiffstats
path: root/runtime
diff options
context:
space:
mode:
Diffstat (limited to 'runtime')
-rw-r--r--runtime/user/README1
-rw-r--r--runtime/user/alloc.c96
-rw-r--r--runtime/user/emul.h9
-rw-r--r--runtime/user/print.c161
-rwxr-xr-xruntime/user/recreate_links8
-rw-r--r--runtime/user/runtime.h39
-rw-r--r--runtime/user/test.h247
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