/** * @file oprof.c * * @remark Copyright 2002 OProfile authors * @remark Read the file COPYING * * @author John Levon <levon@movementarian.org> */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/oprofile.h> #include <linux/moduleparam.h> #include <asm/semaphore.h> #include "oprof.h" #include "event_buffer.h" #include "cpu_buffer.h" #include "buffer_sync.h" #include "oprofile_stats.h" struct oprofile_operations oprofile_ops; unsigned long oprofile_started; unsigned long backtrace_depth; static unsigned long is_setup; static DECLARE_MUTEX(start_sem); /* timer 0 - use performance monitoring hardware if available 1 - use the timer int mechanism regardless */ static int timer = 0; int oprofile_setup(void) { int err; down(&start_sem); if ((err = alloc_cpu_buffers())) goto out; if ((err = alloc_event_buffer())) goto out1; if (oprofile_ops.setup && (err = oprofile_ops.setup())) goto out2; /* Note even though this starts part of the * profiling overhead, it's necessary to prevent * us missing task deaths and eventually oopsing * when trying to process the event buffer. */ if ((err = sync_start())) goto out3; is_setup = 1; up(&start_sem); return 0; out3: if (oprofile_ops.shutdown) oprofile_ops.shutdown(); out2: free_event_buffer(); out1: free_cpu_buffers(); out: up(&start_sem); return err; } /* Actually start profiling (echo 1>/dev/oprofile/enable) */ int oprofile_start(void) { int err = -EINVAL; down(&start_sem); if (!is_setup) goto out; err = 0; if (oprofile_started) goto out; oprofile_reset_stats(); if ((err = oprofile_ops.start())) goto out; oprofile_started = 1; out: up(&start_sem); return err; } /* echo 0>/dev/oprofile/enable */ void oprofile_stop(void) { down(&start_sem); if (!oprofile_started) goto out; oprofile_ops.stop(); oprofile_started = 0; /* wake up the daemon to read what remains */ wake_up_buffer_waiter(); out: up(&start_sem); } void oprofile_shutdown(void) { down(&start_sem); sync_stop(); if (oprofile_ops.shutdown) oprofile_ops.shutdown(); is_setup = 0; free_event_buffer(); free_cpu_buffers(); up(&start_sem); } int oprofile_set_backtrace(unsigned long val) { int err = 0; down(&start_sem); if (oprofile_started) { err = -EBUSY; goto out; } if (!oprofile_ops.backtrace) { err = -EINVAL; goto out; } backtrace_depth = val; out: up(&start_sem); return err; } static int __init oprofile_init(void) { int err; err = oprofile_arch_init(&oprofile_ops); if (err < 0 || timer) { printk(KERN_INFO "oprofile: using timer interrupt.\n"); oprofile_timer_init(&oprofile_ops); } err = oprofilefs_register(); if (err) oprofile_arch_exit(); return err; } static void __exit oprofile_exit(void) { oprofilefs_unregister(); oprofile_arch_exit(); } module_init(oprofile_init); module_exit(oprofile_exit); module_param_named(timer, timer, int, 0644); MODULE_PARM_DESC(timer, "force use of timer interrupt"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("John Levon <levon@movementarian.org>"); MODULE_DESCRIPTION("OProfile system profiler");