From 4dd37d092cae14c014b7874e9e107cab839d54de Mon Sep 17 00:00:00 2001 From: Soren Sandmann Date: Mon, 23 Oct 2006 03:46:25 +0000 Subject: [PATCH] Communicate traces to userspace through shared memory instead of copying. 2006-10-22 Soren Sandmann Communicate traces to userspace through shared memory instead of copying. * module/sysprof-module.c: Store the traces in a SysprofMmapArea. (sysprof_mmap): Implement this method. (sysprof_nopage): Implement this. (sysprof_read): Just reset the tail pointer and return zero. * module/sysprof-module.h (struct SysprofMmapArea): New structure. * collector.c (collector_stop): Unmap the device (in_dead_period): New function (on_read): Read the traces out of mmap()ed area instead of reading them. Call read() to prevent poll() from firing. (struct Collector): New members map_area and current. * Makefile.am (insert-module): Prefix modprobe with /sbin * collector.c (open_fd): mmap() the sysprof device. --- ChangeLog | 23 ++++++++++ Makefile.am | 4 +- collector.c | 112 ++++++++++++++++++++++++++++++++---------------- module/sysprof-module.c | 98 +++++++++++++++++++++++++++--------------- module/sysprof-module.h | 12 +++++- 5 files changed, 174 insertions(+), 75 deletions(-) diff --git a/ChangeLog b/ChangeLog index e80e62a..08c4a60 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2006-10-22 Soren Sandmann + + Communicate traces to userspace through shared memory instead of + copying. + + * module/sysprof-module.c: Store the traces in a SysprofMmapArea. + (sysprof_mmap): Implement this method. + (sysprof_nopage): Implement this. + (sysprof_read): Just reset the tail pointer and return zero. + + * module/sysprof-module.h (struct SysprofMmapArea): New + structure. + + * collector.c (collector_stop): Unmap the device + (in_dead_period): New function + (on_read): Read the traces out of mmap()ed area instead of reading + them. Call read() to prevent poll() from firing. + (struct Collector): New members map_area and current. + + * Makefile.am (insert-module): Prefix modprobe with /sbin + + * collector.c (open_fd): mmap() the sysprof device. + 2006-10-11 Soren Sandmann * profile.c (profile_list_callers): Skip nodes that don't have a diff --git a/Makefile.am b/Makefile.am index 6f04893..eeb2a04 100644 --- a/Makefile.am +++ b/Makefile.am @@ -90,5 +90,5 @@ EXTRA_DIST = \ module/Makefile insert-module: - modprobe -r sysprof-module - modprobe sysprof-module + /sbin/modprobe -r sysprof-module + /sbin/modprobe sysprof-module diff --git a/collector.c b/collector.c index d81aeb3..00054a7 100644 --- a/collector.c +++ b/collector.c @@ -29,6 +29,9 @@ #include #include #include +#include + +#define SYSPROF_FILE "/dev/sysprof-trace" static void set_no_module_error (GError **err); static void set_cant_open_error (GError **err, int eno); @@ -42,12 +45,14 @@ struct Collector int fd; GTimeVal latest_reset; int n_samples; + SysprofMmapArea * map_area; + unsigned int current; }; /* callback is called whenever a new sample arrives */ Collector * collector_new (CollectorFunc callback, - gpointer data) + gpointer data) { Collector *collector = g_new0 (Collector, 1); @@ -80,7 +85,7 @@ time_diff (const GTimeVal *first, #define RESET_DEAD_PERIOD 250 static void -add_trace_to_stash (SysprofStackTrace *trace, +add_trace_to_stash (const SysprofStackTrace *trace, StackStash *stash) { int i; @@ -105,46 +110,64 @@ add_trace_to_stash (SysprofStackTrace *trace, g_free (addrs); } -static void -on_read (gpointer data) +static gboolean +in_dead_period (Collector *collector) { - SysprofStackTrace trace; - Collector *collector = data; GTimeVal now; - int rd; double diff; + + g_get_current_time (&now); - rd = read (collector->fd, &trace, sizeof (trace)); + diff = time_diff (&now, &collector->latest_reset); - if (rd == -1 && errno == EWOULDBLOCK) - return; + if (diff >= 0.0 && diff < RESET_DEAD_PERIOD) + return TRUE; - g_get_current_time (&now); + return FALSE; +} + +static void +on_read (gpointer data) +{ + Collector *collector = data; + char c; + /* Make sure poll() doesn't fire immediately again */ + read (collector->fd, &c, 1); + /* After a reset we ignore samples for a short period so that * a reset will actually cause 'samples' to become 0 */ - diff = time_diff (&now, &collector->latest_reset); - - if (diff >= 0.0 && diff < RESET_DEAD_PERIOD) - return; - -#if 0 + if (in_dead_period (collector)) { - int i; - g_print ("pid: %d (%d)\n", trace.pid, trace.n_addresses); - for (i=0; i < trace.n_addresses; ++i) - g_print ("rd: %08x\n", trace.addresses[i]); - g_print ("-=-\n"); + collector->current = collector->map_area->head; + return; } -#endif - if (rd > 0) + while (collector->current != collector->map_area->head) { - add_trace_to_stash (&trace, collector->stash); + const SysprofStackTrace *trace; + + trace = &(collector->map_area->traces[collector->current]); + +#if 0 + { + int i; + g_print ("pid: %d (%d)\n", trace.pid, trace.n_addresses); + for (i=0; i < trace.n_addresses; ++i) + g_print ("rd: %08x\n", trace.addresses[i]); + g_print ("-=-\n"); + } +#endif + + add_trace_to_stash (trace, collector->stash); + + collector->current++; + if (collector->current >= SYSPROF_N_TRACES) + collector->current = 0; collector->n_samples++; - + if (collector->callback) collector->callback (collector->data); } @@ -176,6 +199,7 @@ open_fd (Collector *collector, GError **err) { int fd; + void *map_area; fd = open (SYSPROF_FILE, O_RDONLY); if (fd < 0) @@ -183,16 +207,16 @@ open_fd (Collector *collector, if (load_module()) { GTimer *timer = g_timer_new (); - + while (fd < 0 && g_timer_elapsed (timer, NULL) < 0.5) { /* Wait for udev to discover the new device */ usleep (100000); - + errno = 0; fd = open (SYSPROF_FILE, O_RDONLY); } - + g_timer_destroy (timer); if (fd < 0) @@ -201,7 +225,7 @@ open_fd (Collector *collector, return FALSE; } } - + if (fd < 0) { set_no_module_error (err); @@ -210,6 +234,18 @@ open_fd (Collector *collector, } } + map_area = mmap (NULL, sizeof (SysprofMmapArea), PROT_READ, MAP_SHARED, fd, 0); + + if (map_area == MAP_FAILED) + { + close (fd); + set_cant_open_error (err, errno); + + return FALSE; + } + + collector->map_area = map_area; + collector->current = 0; collector->fd = fd; fd_add_watch (collector->fd, collector); @@ -218,7 +254,7 @@ open_fd (Collector *collector, gboolean collector_start (Collector *collector, - GError **err) + GError **err) { if (collector->fd < 0 && !open_fd (collector, err)) return FALSE; @@ -234,6 +270,10 @@ collector_stop (Collector *collector) { fd_remove_watch (collector->fd); + munmap (collector->map_area, sizeof (SysprofMmapArea)); + collector->map_area = NULL; + collector->current = 0; + close (collector->fd); collector->fd = -1; } @@ -244,7 +284,7 @@ collector_reset (Collector *collector) { if (collector->stash) stack_stash_unref (collector->stash); - + process_flush_caches(); collector->stash = stack_stash_new (NULL); @@ -316,7 +356,7 @@ resolve_symbols (GList *trace, gint size, gpointer data) stack_stash_add_trace (info->resolved_stash, (gulong *)resolved_trace->pdata, resolved_trace->len, size); - + g_ptr_array_free (resolved_trace, TRUE); } @@ -330,13 +370,13 @@ collector_create_profile (Collector *collector) info.unique_symbols = g_hash_table_new (g_direct_hash, g_direct_equal); stack_stash_foreach (collector->stash, resolve_symbols, &info); - + /* FIXME: we are leaking the value strings in info.unique_symbols */ g_hash_table_destroy (info.unique_symbols); - + profile = profile_new (info.resolved_stash); - + stack_stash_unref (info.resolved_stash); return profile; diff --git a/module/sysprof-module.c b/module/sysprof-module.c index edb1a0c..04d667f 100644 --- a/module/sysprof-module.c +++ b/module/sysprof-module.c @@ -53,11 +53,9 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Soeren Sandmann (sandmann@daimi.au.dk)"); -#define N_TRACES 256 - -static SysprofStackTrace stack_traces[N_TRACES]; -static SysprofStackTrace * head = &stack_traces[0]; -static atomic_t client_count = ATOMIC_INIT(0); +static unsigned char area_backing [sizeof (SysprofMmapArea) + PAGE_SIZE]; +static SysprofMmapArea *area; +static atomic_t client_count = ATOMIC_INIT(0); DECLARE_WAIT_QUEUE_HEAD (wait_for_trace); @@ -119,7 +117,7 @@ timer_notify (struct pt_regs *regs) struct pt_regs * regs = (struct pt_regs *)data; #endif void *frame_pointer; - SysprofStackTrace *trace = head; + SysprofStackTrace *trace = &(area->traces[area->head]); int i; int is_user; StackFrame frame; @@ -211,8 +209,8 @@ timer_notify (struct pt_regs *regs) else trace->truncated = 0; - if (head++ == &stack_traces[N_TRACES - 1]) - head = &stack_traces[0]; + if (area->head++ == SYSPROF_N_TRACES - 1) + area->head = 0; wake_up (&wait_for_trace); @@ -232,27 +230,9 @@ static struct notifier_block timer_notifier = { static int sysprof_read(struct file *file, char *buffer, size_t count, loff_t *offset) { - SysprofStackTrace *trace; - SysprofStackTrace *tail = file->private_data; - - if (count < sizeof *tail) - return -EMSGSIZE; - - if (head == tail) - return -EWOULDBLOCK; - - trace = tail; - if (tail++ == &stack_traces[N_TRACES - 1]) - tail = &stack_traces[0]; + file->private_data = &(area->traces[area->head]); - BUG_ON(trace->pid == 0); - - if (copy_to_user(buffer, trace, sizeof *trace)) - return -EFAULT; - - file->private_data = tail; - - return sizeof *tail; + return 0; } static unsigned int @@ -260,12 +240,12 @@ sysprof_poll(struct file *file, poll_table *poll_table) { SysprofStackTrace *tail = file->private_data; - if (head != tail) + if (&(area->traces[area->head]) != tail) return POLLIN | POLLRDNORM; poll_wait(file, &wait_for_trace, poll_table); - if (head != tail) + if (&(area->traces[area->head]) != tail) return POLLIN | POLLRDNORM; return 0; @@ -284,11 +264,56 @@ sysprof_open(struct inode *inode, struct file *file) #endif } - file->private_data = head; + file->private_data = &(area->traces[area->head]); return retval; } +static struct page * +sysprof_nopage(struct vm_area_struct *vma, unsigned long addr, int *type) +{ + unsigned long area_start; + unsigned long virt; + struct page *page_ptr; + +#if 0 + printk (KERN_ALERT "nopage called: %p (offset: %d) area: %p\n", addr, addr - vma->vm_start, area); +#endif + + area_start = (unsigned long)area; + + virt = area_start + (addr - vma->vm_start); + + if (virt > area_start + sizeof (SysprofMmapArea)) + return NOPAGE_SIGBUS; + + page_ptr = vmalloc_to_page ((void *)virt); + + get_page (page_ptr); + + if (type) + *type = VM_FAULT_MINOR; + + return page_ptr; +} + +static int +sysprof_mmap(struct file *filp, struct vm_area_struct *vma) +{ + static struct vm_operations_struct ops = { + .nopage = sysprof_nopage, + }; + + if (vma->vm_flags & (VM_WRITE | VM_EXEC)) + return -EPERM; + + vma->vm_flags |= VM_RESERVED; + + vma->vm_ops = &ops; + + return 0; +} + static int sysprof_release(struct inode *inode, struct file *file) { @@ -309,6 +334,7 @@ static struct file_operations sysprof_fops = { .poll = sysprof_poll, .open = sysprof_open, .release = sysprof_release, + .mmap = sysprof_mmap, }; static struct miscdevice sysprof_miscdev = { @@ -321,6 +347,7 @@ int init_module(void) { int ret; + unsigned long area_page; ret = misc_register(&sysprof_miscdev); if (ret) { @@ -328,8 +355,12 @@ init_module(void) return ret; } + area_page = (unsigned long)&(area_backing[4096]) & PAGE_MASK; + area = (SysprofMmapArea *)area_page; + area->head = 0; + printk(KERN_ALERT "sysprof: loaded (%s)\n", PACKAGE_VERSION); - + return 0; } @@ -342,9 +373,6 @@ cleanup_module(void) } - - - #if 0 /* The old userspace_reader code - someday it may be useful again */ diff --git a/module/sysprof-module.h b/module/sysprof-module.h index ac2f4c6..47268e8 100644 --- a/module/sysprof-module.h +++ b/module/sysprof-module.h @@ -20,10 +20,11 @@ #ifndef SYSPROF_MODULE_H #define SYSPROF_MODULE_H -#define SYSPROF_FILE "/dev/sysprof-trace" - typedef struct SysprofStackTrace SysprofStackTrace; +typedef struct SysprofStackInfo SysprofStackInfo; +typedef struct SysprofMmapArea SysprofMmapArea; +#define SYSPROF_N_TRACES 256 #define SYSPROF_MAX_ADDRESSES 512 struct SysprofStackTrace @@ -36,4 +37,11 @@ struct SysprofStackTrace void *addresses[SYSPROF_MAX_ADDRESSES]; }; +struct SysprofMmapArea +{ + unsigned int head; + + SysprofStackTrace traces[SYSPROF_N_TRACES]; +}; + #endif -- 2.7.4