| /** | |
| * @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 <linux/workqueue.h> | |
| #include <linux/time.h> | |
| #include <linux/mutex.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 oprofile_backtrace_depth; | |
| static unsigned long is_setup; | |
| static DEFINE_MUTEX(start_mutex); | |
| /* 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; | |
| mutex_lock(&start_mutex); | |
| 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 (oprofile_ops.sync_start) { | |
| int sync_ret = oprofile_ops.sync_start(); | |
| switch (sync_ret) { | |
| case 0: | |
| goto post_sync; | |
| case 1: | |
| goto do_generic; | |
| case -1: | |
| goto out3; | |
| default: | |
| goto out3; | |
| } | |
| } | |
| do_generic: | |
| if ((err = sync_start())) | |
| goto out3; | |
| post_sync: | |
| is_setup = 1; | |
| mutex_unlock(&start_mutex); | |
| return 0; | |
| out3: | |
| if (oprofile_ops.shutdown) | |
| oprofile_ops.shutdown(); | |
| out2: | |
| free_event_buffer(); | |
| out1: | |
| free_cpu_buffers(); | |
| out: | |
| mutex_unlock(&start_mutex); | |
| return err; | |
| } | |
| #ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX | |
| static void switch_worker(struct work_struct *work); | |
| static DECLARE_DELAYED_WORK(switch_work, switch_worker); | |
| static void start_switch_worker(void) | |
| { | |
| if (oprofile_ops.switch_events) | |
| schedule_delayed_work(&switch_work, oprofile_time_slice); | |
| } | |
| static void stop_switch_worker(void) | |
| { | |
| cancel_delayed_work_sync(&switch_work); | |
| } | |
| static void switch_worker(struct work_struct *work) | |
| { | |
| if (oprofile_ops.switch_events()) | |
| return; | |
| atomic_inc(&oprofile_stats.multiplex_counter); | |
| start_switch_worker(); | |
| } | |
| /* User inputs in ms, converts to jiffies */ | |
| int oprofile_set_timeout(unsigned long val_msec) | |
| { | |
| int err = 0; | |
| unsigned long time_slice; | |
| mutex_lock(&start_mutex); | |
| if (oprofile_started) { | |
| err = -EBUSY; | |
| goto out; | |
| } | |
| if (!oprofile_ops.switch_events) { | |
| err = -EINVAL; | |
| goto out; | |
| } | |
| time_slice = msecs_to_jiffies(val_msec); | |
| if (time_slice == MAX_JIFFY_OFFSET) { | |
| err = -EINVAL; | |
| goto out; | |
| } | |
| oprofile_time_slice = time_slice; | |
| out: | |
| mutex_unlock(&start_mutex); | |
| return err; | |
| } | |
| #else | |
| static inline void start_switch_worker(void) { } | |
| static inline void stop_switch_worker(void) { } | |
| #endif | |
| /* Actually start profiling (echo 1>/dev/oprofile/enable) */ | |
| int oprofile_start(void) | |
| { | |
| int err = -EINVAL; | |
| mutex_lock(&start_mutex); | |
| if (!is_setup) | |
| goto out; | |
| err = 0; | |
| if (oprofile_started) | |
| goto out; | |
| oprofile_reset_stats(); | |
| if ((err = oprofile_ops.start())) | |
| goto out; | |
| start_switch_worker(); | |
| oprofile_started = 1; | |
| out: | |
| mutex_unlock(&start_mutex); | |
| return err; | |
| } | |
| /* echo 0>/dev/oprofile/enable */ | |
| void oprofile_stop(void) | |
| { | |
| mutex_lock(&start_mutex); | |
| if (!oprofile_started) | |
| goto out; | |
| oprofile_ops.stop(); | |
| oprofile_started = 0; | |
| stop_switch_worker(); | |
| /* wake up the daemon to read what remains */ | |
| wake_up_buffer_waiter(); | |
| out: | |
| mutex_unlock(&start_mutex); | |
| } | |
| void oprofile_shutdown(void) | |
| { | |
| mutex_lock(&start_mutex); | |
| if (oprofile_ops.sync_stop) { | |
| int sync_ret = oprofile_ops.sync_stop(); | |
| switch (sync_ret) { | |
| case 0: | |
| goto post_sync; | |
| case 1: | |
| goto do_generic; | |
| default: | |
| goto post_sync; | |
| } | |
| } | |
| do_generic: | |
| sync_stop(); | |
| post_sync: | |
| if (oprofile_ops.shutdown) | |
| oprofile_ops.shutdown(); | |
| is_setup = 0; | |
| free_event_buffer(); | |
| free_cpu_buffers(); | |
| mutex_unlock(&start_mutex); | |
| } | |
| int oprofile_set_ulong(unsigned long *addr, unsigned long val) | |
| { | |
| int err = -EBUSY; | |
| mutex_lock(&start_mutex); | |
| if (!oprofile_started) { | |
| *addr = val; | |
| err = 0; | |
| } | |
| mutex_unlock(&start_mutex); | |
| return err; | |
| } | |
| static int timer_mode; | |
| static int __init oprofile_init(void) | |
| { | |
| int err; | |
| /* always init architecture to setup backtrace support */ | |
| timer_mode = 0; | |
| err = oprofile_arch_init(&oprofile_ops); | |
| if (!err) { | |
| if (!timer && !oprofilefs_register()) | |
| return 0; | |
| oprofile_arch_exit(); | |
| } | |
| /* setup timer mode: */ | |
| timer_mode = 1; | |
| /* no nmi timer mode if oprofile.timer is set */ | |
| if (timer || op_nmi_timer_init(&oprofile_ops)) { | |
| err = oprofile_timer_init(&oprofile_ops); | |
| if (err) | |
| return err; | |
| } | |
| return oprofilefs_register(); | |
| } | |
| static void __exit oprofile_exit(void) | |
| { | |
| oprofilefs_unregister(); | |
| if (!timer_mode) | |
| 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"); |