Add sysprof support, from fedora's kernel

Andres Salomon dilinger at debian.org
Tue Nov 7 22:38:29 EST 2006


Commit:     0bdb74daf661e4ba4015becc7a4a9a21664f97a0
Parent:     48e92963dadd0f35903a4999e87224f3667d43d5
commit 0bdb74daf661e4ba4015becc7a4a9a21664f97a0
Author:     Andres Salomon <dilinger at debian.org>
AuthorDate: Tue Oct 31 15:57:45 2006 -0500
Commit:     Andres Salomon <dilinger at debian.org>
CommitDate: Tue Oct 31 15:57:45 2006 -0500

    Add sysprof support, from fedora's kernel
---
 drivers/Kconfig                  |    2 
 drivers/Makefile                 |    2 
 drivers/sysprof/Kconfig          |   12 ++
 drivers/sysprof/Makefile         |    1 
 drivers/sysprof/config.h         |   23 +++
 drivers/sysprof/sysprof-module.c |  251 ++++++++++++++++++++++++++++++++++++++
 drivers/sysprof/sysprof-module.h |   37 ++++++
 7 files changed, 328 insertions(+), 0 deletions(-)

diff --git a/drivers/Kconfig b/drivers/Kconfig
index f394634..50fd8c8 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -78,4 +78,6 @@ source "drivers/rtc/Kconfig"
 
 source "drivers/dma/Kconfig"
 
+source "drivers/sysprof/Kconfig"
+
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 4ac14da..01ecb45 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -21,6 +21,8 @@ obj-y				+= char/
 
 obj-$(CONFIG_CONNECTOR)		+= connector/
 
+obj-$(CONFIG_SYSPROF)		+= sysprof/
+
 # i810fb and intelfb depend on char/agp/
 obj-$(CONFIG_FB_I810)           += video/i810/
 obj-$(CONFIG_FB_INTEL)          += video/intelfb/
diff --git a/drivers/sysprof/Kconfig b/drivers/sysprof/Kconfig
new file mode 100644
index 0000000..b99c13a
--- /dev/null
+++ b/drivers/sysprof/Kconfig
@@ -0,0 +1,12 @@
+
+menu "Sysprof"
+
+config SYSPROF
+	tristate "Sysprof support"
+	help
+	 Say M here to include the sysprof-module.
+
+	 Sysprof is a sampling profiler that uses a kernel module,
+	 sysprof-module, to generate stacktraces which are then interpreted by
+	 the userspace program "sysprof".
+endmenu
diff --git a/drivers/sysprof/Makefile b/drivers/sysprof/Makefile
new file mode 100644
index 0000000..cd465e9
--- /dev/null
+++ b/drivers/sysprof/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SYSPROF)	+= sysprof-module.o
diff --git a/drivers/sysprof/config.h b/drivers/sysprof/config.h
new file mode 100644
index 0000000..89a3aef
--- /dev/null
+++ b/drivers/sysprof/config.h
@@ -0,0 +1,23 @@
+/* config.h.  Generated by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Look for global separate debug info in this path */
+#define DEBUGDIR "/usr/local/lib/debug"
+
+/* Define to 1 if you have the `iberty' library (-liberty). */
+/* #undef HAVE_LIBIBERTY */
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "sysprof"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "sysprof 1.0.3"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "sysprof"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.0.3"
diff --git a/drivers/sysprof/sysprof-module.c b/drivers/sysprof/sysprof-module.c
new file mode 100644
index 0000000..441f396
--- /dev/null
+++ b/drivers/sysprof/sysprof-module.c
@@ -0,0 +1,251 @@
+/* -*- c-basic-offset: 8 -*- */
+
+/* Sysprof -- Sampling, systemwide CPU profiler
+ * Copyright 2004, Red Hat, Inc.
+ * Copyright 2004, 2005, Soeren Sandmann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <asm/atomic.h>
+#include <linux/kernel.h>  /* Needed for KERN_ALERT */
+#include <linux/module.h>  /* Needed by all modules */
+#include <linux/sched.h>
+
+#if !CONFIG_PROFILING
+# error Sysprof needs a kernel with profiling support compiled in.
+#endif
+#ifdef CONFIG_SMP
+# define __SMP__
+#endif
+
+#include <linux/proc_fs.h>
+#include <asm/uaccess.h>
+#include <linux/poll.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+#include <linux/profile.h>
+
+#include "sysprof-module.h"
+
+#include "config.h"
+
+#include <linux/version.h>
+#if KERNEL_VERSION(2,6,11) > LINUX_VERSION_CODE
+# error Sysprof needs a Linux 2.6.11 kernel or later
+#endif
+#include <linux/kallsyms.h>
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Soeren Sandmann (sandmann at daimi.au.dk)");
+
+#define SAMPLES_PER_SECOND (200)
+#define INTERVAL ((HZ <= SAMPLES_PER_SECOND)? 1 : (HZ / SAMPLES_PER_SECOND))
+#define N_TRACES 256
+
+static SysprofStackTrace	stack_traces[N_TRACES];
+static SysprofStackTrace *	head = &stack_traces[0];
+static SysprofStackTrace *	tail = &stack_traces[0];
+DECLARE_WAIT_QUEUE_HEAD (wait_for_trace);
+DECLARE_WAIT_QUEUE_HEAD (wait_for_exit);
+
+/* Macro the names of the registers that are used on each architecture */
+#if defined(CONFIG_X86_64)
+# define REG_FRAME_PTR rbp
+# define REG_INS_PTR rip
+# define REG_STACK_PTR rsp
+#elif defined(CONFIG_X86)
+# define REG_FRAME_PTR ebp
+# define REG_INS_PTR eip
+# define REG_STACK_PTR esp
+#else
+# error Sysprof only supports the i386 and x86-64 architectures
+#endif
+
+typedef struct userspace_reader userspace_reader;
+struct userspace_reader
+{
+	struct task_struct *task;
+	unsigned long cache_address;
+	unsigned long *cache;
+};
+
+typedef struct StackFrame StackFrame;
+struct StackFrame {
+	unsigned long next;
+	unsigned long return_address;
+};
+
+struct work_struct work;
+
+static int
+read_frame (void *frame_pointer, StackFrame *frame)
+{
+#if 0
+	/* This is commented out because we seem to be called with
+	 * (current_thread_info()->addr_limit.seg)) == 0
+	 * which means access_ok() _always_ fails.
+	 *
+	 * Not sure why (or if) this isn't the case for oprofile
+	 */
+	if (!access_ok(VERIFY_READ, frame_pointer, sizeof(StackFrame)))
+		return 1;
+#endif
+
+	if (__copy_from_user_inatomic (
+		    frame, frame_pointer, sizeof (StackFrame)))
+		return 2;
+
+	return 0;
+}
+
+static int
+timer_notify (struct pt_regs *regs)
+{
+	static int n_samples;
+	struct SysprofStackTrace *trace = head;
+	int i;
+	int is_user;
+
+	if ((n_samples++ % INTERVAL) != 0)
+		return 0;
+
+	is_user = user_mode(regs);
+
+	if (!current || current->pid == 0)
+		return 0;
+	
+	if (is_user && current->state != TASK_RUNNING)
+		return 0;
+
+	if (!is_user)
+	{
+		/* kernel */
+		
+		trace->pid = current->pid;
+		trace->truncated = 0;
+		trace->n_addresses = 1;
+
+		/* 0x1 is taken by sysprof to mean "in kernel" */
+		trace->addresses[0] = (void *)0x1;
+	}
+	else
+	{
+		StackFrame *frame_pointer;
+		StackFrame frame;
+		memset(trace, 0, sizeof (SysprofStackTrace));
+		
+		trace->pid = current->pid;
+		trace->truncated = 0;
+
+		i = 0;
+		
+		trace->addresses[i++] = (void *)regs->REG_INS_PTR;
+		
+		frame_pointer = (void *)regs->REG_FRAME_PTR;
+	
+		while (read_frame (frame_pointer, &frame) == 0		&&
+		       i < SYSPROF_MAX_ADDRESSES			&&
+		       (unsigned long)frame_pointer >= regs->REG_STACK_PTR)
+		{
+			trace->addresses[i++] = (void *)frame.return_address;
+			frame_pointer = (StackFrame *)frame.next;
+		}
+		
+		trace->n_addresses = i;
+
+		if (i == SYSPROF_MAX_ADDRESSES)
+			trace->truncated = 1;
+		else
+			trace->truncated = 0;
+	}
+	
+	if (head++ == &stack_traces[N_TRACES - 1])
+		head = &stack_traces[0];
+	
+	wake_up (&wait_for_trace);
+	
+	return 0;
+}
+
+static int
+procfile_read(char *buffer, 
+	      char **buffer_location, 
+	      off_t offset, 
+	      int buffer_len,
+	      int *eof,
+	      void *data)
+{
+	if (head == tail)
+		return -EWOULDBLOCK;
+	
+	*buffer_location = (char *)tail;
+	
+	if (tail++ == &stack_traces[N_TRACES - 1])
+		tail = &stack_traces[0];
+	
+	return sizeof (SysprofStackTrace);
+}
+
+struct proc_dir_entry *trace_proc_file;
+static unsigned int
+procfile_poll(struct file *filp, poll_table *poll_table)
+{
+	if (head != tail)
+		return POLLIN | POLLRDNORM;
+	
+	poll_wait(filp, &wait_for_trace, poll_table);
+
+	if (head != tail)
+		return POLLIN | POLLRDNORM;
+	
+	return 0;
+}
+
+int
+init_module(void)
+{
+	static struct file_operations fops;
+
+	trace_proc_file =
+		create_proc_entry ("sysprof-trace", S_IFREG | S_IRUGO, &proc_root);
+	
+	if (!trace_proc_file)
+		return 1;
+
+	fops = *trace_proc_file->proc_fops;
+	fops.poll = procfile_poll;
+	
+	trace_proc_file->read_proc = procfile_read;
+	trace_proc_file->proc_fops = &fops;
+	trace_proc_file->size = sizeof (SysprofStackTrace);
+
+	register_timer_hook (timer_notify);
+	
+	printk(KERN_ALERT "sysprof: loaded (%s)\n", PACKAGE_VERSION);
+	
+	return 0;
+}
+
+void
+cleanup_module(void)
+{
+	unregister_timer_hook (timer_notify);
+	
+	remove_proc_entry("sysprof-trace", &proc_root);
+
+	printk(KERN_ALERT "sysprof: unloaded\n");
+}
+
diff --git a/drivers/sysprof/sysprof-module.h b/drivers/sysprof/sysprof-module.h
new file mode 100644
index 0000000..66a11ae
--- /dev/null
+++ b/drivers/sysprof/sysprof-module.h
@@ -0,0 +1,37 @@
+/* Sysprof -- Sampling, systemwide CPU profiler
+ * Copyright 2004, Red Hat, Inc.
+ * Copyright 2004, 2005, Soeren Sandmann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef SYSPROF_MODULE_H
+#define SYSPROF_MODULE_H
+
+typedef struct SysprofStackTrace SysprofStackTrace;
+
+#define SYSPROF_MAX_ADDRESSES 512
+
+struct SysprofStackTrace
+{
+    int	pid;		/* -1 if in kernel */
+    int truncated;
+    int n_addresses;	/* note: this can be 1 if the process was compiled
+			 * with -fomit-frame-pointer or is otherwise weird
+			 */
+    void *addresses[SYSPROF_MAX_ADDRESSES];
+};
+
+#endif


More information about the Commits-kernel mailing list