linux/drivers/oprofile/oprof.c
<<
>>
Prefs
   1/**
   2 * @file oprof.c
   3 *
   4 * @remark Copyright 2002 OProfile authors
   5 * @remark Read the file COPYING
   6 *
   7 * @author John Levon <levon@movementarian.org>
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/init.h>
  13#include <linux/oprofile.h>
  14#include <linux/moduleparam.h>
  15#include <linux/workqueue.h>
  16#include <linux/time.h>
  17#include <linux/mutex.h>
  18
  19#include "oprof.h"
  20#include "event_buffer.h"
  21#include "cpu_buffer.h"
  22#include "buffer_sync.h"
  23#include "oprofile_stats.h"
  24
  25struct oprofile_operations oprofile_ops;
  26
  27unsigned long oprofile_started;
  28unsigned long oprofile_backtrace_depth;
  29static unsigned long is_setup;
  30static DEFINE_MUTEX(start_mutex);
  31
  32/* timer
  33   0 - use performance monitoring hardware if available
  34   1 - use the timer int mechanism regardless
  35 */
  36static int timer = 0;
  37
  38int oprofile_setup(void)
  39{
  40        int err;
  41
  42        mutex_lock(&start_mutex);
  43
  44        if ((err = alloc_cpu_buffers()))
  45                goto out;
  46
  47        if ((err = alloc_event_buffer()))
  48                goto out1;
  49
  50        if (oprofile_ops.setup && (err = oprofile_ops.setup()))
  51                goto out2;
  52
  53        /* Note even though this starts part of the
  54         * profiling overhead, it's necessary to prevent
  55         * us missing task deaths and eventually oopsing
  56         * when trying to process the event buffer.
  57         */
  58        if (oprofile_ops.sync_start) {
  59                int sync_ret = oprofile_ops.sync_start();
  60                switch (sync_ret) {
  61                case 0:
  62                        goto post_sync;
  63                case 1:
  64                        goto do_generic;
  65                case -1:
  66                        goto out3;
  67                default:
  68                        goto out3;
  69                }
  70        }
  71do_generic:
  72        if ((err = sync_start()))
  73                goto out3;
  74
  75post_sync:
  76        is_setup = 1;
  77        mutex_unlock(&start_mutex);
  78        return 0;
  79
  80out3:
  81        if (oprofile_ops.shutdown)
  82                oprofile_ops.shutdown();
  83out2:
  84        free_event_buffer();
  85out1:
  86        free_cpu_buffers();
  87out:
  88        mutex_unlock(&start_mutex);
  89        return err;
  90}
  91
  92#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX
  93
  94static void switch_worker(struct work_struct *work);
  95static DECLARE_DELAYED_WORK(switch_work, switch_worker);
  96
  97static void start_switch_worker(void)
  98{
  99        if (oprofile_ops.switch_events)
 100                schedule_delayed_work(&switch_work, oprofile_time_slice);
 101}
 102
 103static void stop_switch_worker(void)
 104{
 105        cancel_delayed_work_sync(&switch_work);
 106}
 107
 108static void switch_worker(struct work_struct *work)
 109{
 110        if (oprofile_ops.switch_events())
 111                return;
 112
 113        atomic_inc(&oprofile_stats.multiplex_counter);
 114        start_switch_worker();
 115}
 116
 117/* User inputs in ms, converts to jiffies */
 118int oprofile_set_timeout(unsigned long val_msec)
 119{
 120        int err = 0;
 121        unsigned long time_slice;
 122
 123        mutex_lock(&start_mutex);
 124
 125        if (oprofile_started) {
 126                err = -EBUSY;
 127                goto out;
 128        }
 129
 130        if (!oprofile_ops.switch_events) {
 131                err = -EINVAL;
 132                goto out;
 133        }
 134
 135        time_slice = msecs_to_jiffies(val_msec);
 136        if (time_slice == MAX_JIFFY_OFFSET) {
 137                err = -EINVAL;
 138                goto out;
 139        }
 140
 141        oprofile_time_slice = time_slice;
 142
 143out:
 144        mutex_unlock(&start_mutex);
 145        return err;
 146
 147}
 148
 149#else
 150
 151static inline void start_switch_worker(void) { }
 152static inline void stop_switch_worker(void) { }
 153
 154#endif
 155
 156/* Actually start profiling (echo 1>/dev/oprofile/enable) */
 157int oprofile_start(void)
 158{
 159        int err = -EINVAL;
 160
 161        mutex_lock(&start_mutex);
 162
 163        if (!is_setup)
 164                goto out;
 165
 166        err = 0;
 167
 168        if (oprofile_started)
 169                goto out;
 170
 171        oprofile_reset_stats();
 172
 173        if ((err = oprofile_ops.start()))
 174                goto out;
 175
 176        start_switch_worker();
 177
 178        oprofile_started = 1;
 179out:
 180        mutex_unlock(&start_mutex);
 181        return err;
 182}
 183
 184
 185/* echo 0>/dev/oprofile/enable */
 186void oprofile_stop(void)
 187{
 188        mutex_lock(&start_mutex);
 189        if (!oprofile_started)
 190                goto out;
 191        oprofile_ops.stop();
 192        oprofile_started = 0;
 193
 194        stop_switch_worker();
 195
 196        /* wake up the daemon to read what remains */
 197        wake_up_buffer_waiter();
 198out:
 199        mutex_unlock(&start_mutex);
 200}
 201
 202
 203void oprofile_shutdown(void)
 204{
 205        mutex_lock(&start_mutex);
 206        if (oprofile_ops.sync_stop) {
 207                int sync_ret = oprofile_ops.sync_stop();
 208                switch (sync_ret) {
 209                case 0:
 210                        goto post_sync;
 211                case 1:
 212                        goto do_generic;
 213                default:
 214                        goto post_sync;
 215                }
 216        }
 217do_generic:
 218        sync_stop();
 219post_sync:
 220        if (oprofile_ops.shutdown)
 221                oprofile_ops.shutdown();
 222        is_setup = 0;
 223        free_event_buffer();
 224        free_cpu_buffers();
 225        mutex_unlock(&start_mutex);
 226}
 227
 228int oprofile_set_ulong(unsigned long *addr, unsigned long val)
 229{
 230        int err = -EBUSY;
 231
 232        mutex_lock(&start_mutex);
 233        if (!oprofile_started) {
 234                *addr = val;
 235                err = 0;
 236        }
 237        mutex_unlock(&start_mutex);
 238
 239        return err;
 240}
 241
 242static int timer_mode;
 243
 244static int __init oprofile_init(void)
 245{
 246        int err;
 247
 248        /* always init architecture to setup backtrace support */
 249        timer_mode = 0;
 250        err = oprofile_arch_init(&oprofile_ops);
 251        if (!err) {
 252                if (!timer && !oprofilefs_register())
 253                        return 0;
 254                oprofile_arch_exit();
 255        }
 256
 257        /* setup timer mode: */
 258        timer_mode = 1;
 259        /* no nmi timer mode if oprofile.timer is set */
 260        if (timer || op_nmi_timer_init(&oprofile_ops)) {
 261                err = oprofile_timer_init(&oprofile_ops);
 262                if (err)
 263                        return err;
 264        }
 265
 266        return oprofilefs_register();
 267}
 268
 269
 270static void __exit oprofile_exit(void)
 271{
 272        oprofilefs_unregister();
 273        if (!timer_mode)
 274                oprofile_arch_exit();
 275}
 276
 277
 278module_init(oprofile_init);
 279module_exit(oprofile_exit);
 280
 281module_param_named(timer, timer, int, 0644);
 282MODULE_PARM_DESC(timer, "force use of timer interrupt");
 283
 284MODULE_LICENSE("GPL");
 285MODULE_AUTHOR("John Levon <levon@movementarian.org>");
 286MODULE_DESCRIPTION("OProfile system profiler");
 287