18c2ecf20Sopenharmony_ci/** 28c2ecf20Sopenharmony_ci * @file oprof.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * @remark Copyright 2002 OProfile authors 58c2ecf20Sopenharmony_ci * @remark Read the file COPYING 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * @author John Levon <levon@movementarian.org> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/oprofile.h> 148c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 158c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 168c2ecf20Sopenharmony_ci#include <linux/time.h> 178c2ecf20Sopenharmony_ci#include <linux/mutex.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "oprof.h" 208c2ecf20Sopenharmony_ci#include "event_buffer.h" 218c2ecf20Sopenharmony_ci#include "cpu_buffer.h" 228c2ecf20Sopenharmony_ci#include "buffer_sync.h" 238c2ecf20Sopenharmony_ci#include "oprofile_stats.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct oprofile_operations oprofile_ops; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ciunsigned long oprofile_started; 288c2ecf20Sopenharmony_ciunsigned long oprofile_backtrace_depth; 298c2ecf20Sopenharmony_cistatic unsigned long is_setup; 308c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(start_mutex); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* timer 338c2ecf20Sopenharmony_ci 0 - use performance monitoring hardware if available 348c2ecf20Sopenharmony_ci 1 - use the timer int mechanism regardless 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_cistatic int timer = 0; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ciint oprofile_setup(void) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci int err; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci mutex_lock(&start_mutex); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if ((err = alloc_cpu_buffers())) 458c2ecf20Sopenharmony_ci goto out; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if ((err = alloc_event_buffer())) 488c2ecf20Sopenharmony_ci goto out1; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (oprofile_ops.setup && (err = oprofile_ops.setup())) 518c2ecf20Sopenharmony_ci goto out2; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* Note even though this starts part of the 548c2ecf20Sopenharmony_ci * profiling overhead, it's necessary to prevent 558c2ecf20Sopenharmony_ci * us missing task deaths and eventually oopsing 568c2ecf20Sopenharmony_ci * when trying to process the event buffer. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci if (oprofile_ops.sync_start) { 598c2ecf20Sopenharmony_ci int sync_ret = oprofile_ops.sync_start(); 608c2ecf20Sopenharmony_ci switch (sync_ret) { 618c2ecf20Sopenharmony_ci case 0: 628c2ecf20Sopenharmony_ci goto post_sync; 638c2ecf20Sopenharmony_ci case 1: 648c2ecf20Sopenharmony_ci goto do_generic; 658c2ecf20Sopenharmony_ci case -1: 668c2ecf20Sopenharmony_ci goto out3; 678c2ecf20Sopenharmony_ci default: 688c2ecf20Sopenharmony_ci goto out3; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci } 718c2ecf20Sopenharmony_cido_generic: 728c2ecf20Sopenharmony_ci if ((err = sync_start())) 738c2ecf20Sopenharmony_ci goto out3; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cipost_sync: 768c2ecf20Sopenharmony_ci is_setup = 1; 778c2ecf20Sopenharmony_ci mutex_unlock(&start_mutex); 788c2ecf20Sopenharmony_ci return 0; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ciout3: 818c2ecf20Sopenharmony_ci if (oprofile_ops.shutdown) 828c2ecf20Sopenharmony_ci oprofile_ops.shutdown(); 838c2ecf20Sopenharmony_ciout2: 848c2ecf20Sopenharmony_ci free_event_buffer(); 858c2ecf20Sopenharmony_ciout1: 868c2ecf20Sopenharmony_ci free_cpu_buffers(); 878c2ecf20Sopenharmony_ciout: 888c2ecf20Sopenharmony_ci mutex_unlock(&start_mutex); 898c2ecf20Sopenharmony_ci return err; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#ifdef CONFIG_OPROFILE_EVENT_MULTIPLEX 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic void switch_worker(struct work_struct *work); 958c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(switch_work, switch_worker); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic void start_switch_worker(void) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci if (oprofile_ops.switch_events) 1008c2ecf20Sopenharmony_ci schedule_delayed_work(&switch_work, oprofile_time_slice); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic void stop_switch_worker(void) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&switch_work); 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic void switch_worker(struct work_struct *work) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci if (oprofile_ops.switch_events()) 1118c2ecf20Sopenharmony_ci return; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci atomic_inc(&oprofile_stats.multiplex_counter); 1148c2ecf20Sopenharmony_ci start_switch_worker(); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* User inputs in ms, converts to jiffies */ 1188c2ecf20Sopenharmony_ciint oprofile_set_timeout(unsigned long val_msec) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci int err = 0; 1218c2ecf20Sopenharmony_ci unsigned long time_slice; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci mutex_lock(&start_mutex); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (oprofile_started) { 1268c2ecf20Sopenharmony_ci err = -EBUSY; 1278c2ecf20Sopenharmony_ci goto out; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (!oprofile_ops.switch_events) { 1318c2ecf20Sopenharmony_ci err = -EINVAL; 1328c2ecf20Sopenharmony_ci goto out; 1338c2ecf20Sopenharmony_ci } 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci time_slice = msecs_to_jiffies(val_msec); 1368c2ecf20Sopenharmony_ci if (time_slice == MAX_JIFFY_OFFSET) { 1378c2ecf20Sopenharmony_ci err = -EINVAL; 1388c2ecf20Sopenharmony_ci goto out; 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci oprofile_time_slice = time_slice; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ciout: 1448c2ecf20Sopenharmony_ci mutex_unlock(&start_mutex); 1458c2ecf20Sopenharmony_ci return err; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci#else 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic inline void start_switch_worker(void) { } 1528c2ecf20Sopenharmony_cistatic inline void stop_switch_worker(void) { } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci#endif 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* Actually start profiling (echo 1>/dev/oprofile/enable) */ 1578c2ecf20Sopenharmony_ciint oprofile_start(void) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci int err = -EINVAL; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci mutex_lock(&start_mutex); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (!is_setup) 1648c2ecf20Sopenharmony_ci goto out; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci err = 0; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (oprofile_started) 1698c2ecf20Sopenharmony_ci goto out; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci oprofile_reset_stats(); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if ((err = oprofile_ops.start())) 1748c2ecf20Sopenharmony_ci goto out; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci start_switch_worker(); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci oprofile_started = 1; 1798c2ecf20Sopenharmony_ciout: 1808c2ecf20Sopenharmony_ci mutex_unlock(&start_mutex); 1818c2ecf20Sopenharmony_ci return err; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* echo 0>/dev/oprofile/enable */ 1868c2ecf20Sopenharmony_civoid oprofile_stop(void) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci mutex_lock(&start_mutex); 1898c2ecf20Sopenharmony_ci if (!oprofile_started) 1908c2ecf20Sopenharmony_ci goto out; 1918c2ecf20Sopenharmony_ci oprofile_ops.stop(); 1928c2ecf20Sopenharmony_ci oprofile_started = 0; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci stop_switch_worker(); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* wake up the daemon to read what remains */ 1978c2ecf20Sopenharmony_ci wake_up_buffer_waiter(); 1988c2ecf20Sopenharmony_ciout: 1998c2ecf20Sopenharmony_ci mutex_unlock(&start_mutex); 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_civoid oprofile_shutdown(void) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci mutex_lock(&start_mutex); 2068c2ecf20Sopenharmony_ci if (oprofile_ops.sync_stop) { 2078c2ecf20Sopenharmony_ci int sync_ret = oprofile_ops.sync_stop(); 2088c2ecf20Sopenharmony_ci switch (sync_ret) { 2098c2ecf20Sopenharmony_ci case 0: 2108c2ecf20Sopenharmony_ci goto post_sync; 2118c2ecf20Sopenharmony_ci case 1: 2128c2ecf20Sopenharmony_ci goto do_generic; 2138c2ecf20Sopenharmony_ci default: 2148c2ecf20Sopenharmony_ci goto post_sync; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci } 2178c2ecf20Sopenharmony_cido_generic: 2188c2ecf20Sopenharmony_ci sync_stop(); 2198c2ecf20Sopenharmony_cipost_sync: 2208c2ecf20Sopenharmony_ci if (oprofile_ops.shutdown) 2218c2ecf20Sopenharmony_ci oprofile_ops.shutdown(); 2228c2ecf20Sopenharmony_ci is_setup = 0; 2238c2ecf20Sopenharmony_ci free_event_buffer(); 2248c2ecf20Sopenharmony_ci free_cpu_buffers(); 2258c2ecf20Sopenharmony_ci mutex_unlock(&start_mutex); 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ciint oprofile_set_ulong(unsigned long *addr, unsigned long val) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci int err = -EBUSY; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci mutex_lock(&start_mutex); 2338c2ecf20Sopenharmony_ci if (!oprofile_started) { 2348c2ecf20Sopenharmony_ci *addr = val; 2358c2ecf20Sopenharmony_ci err = 0; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci mutex_unlock(&start_mutex); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return err; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic int timer_mode; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int __init oprofile_init(void) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci int err; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* always init architecture to setup backtrace support */ 2498c2ecf20Sopenharmony_ci timer_mode = 0; 2508c2ecf20Sopenharmony_ci err = oprofile_arch_init(&oprofile_ops); 2518c2ecf20Sopenharmony_ci if (!err) { 2528c2ecf20Sopenharmony_ci if (!timer && !oprofilefs_register()) 2538c2ecf20Sopenharmony_ci return 0; 2548c2ecf20Sopenharmony_ci oprofile_arch_exit(); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* setup timer mode: */ 2588c2ecf20Sopenharmony_ci timer_mode = 1; 2598c2ecf20Sopenharmony_ci /* no nmi timer mode if oprofile.timer is set */ 2608c2ecf20Sopenharmony_ci if (timer || op_nmi_timer_init(&oprofile_ops)) { 2618c2ecf20Sopenharmony_ci err = oprofile_timer_init(&oprofile_ops); 2628c2ecf20Sopenharmony_ci if (err) 2638c2ecf20Sopenharmony_ci return err; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return oprofilefs_register(); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic void __exit oprofile_exit(void) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci oprofilefs_unregister(); 2738c2ecf20Sopenharmony_ci if (!timer_mode) 2748c2ecf20Sopenharmony_ci oprofile_arch_exit(); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cimodule_init(oprofile_init); 2798c2ecf20Sopenharmony_cimodule_exit(oprofile_exit); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cimodule_param_named(timer, timer, int, 0644); 2828c2ecf20Sopenharmony_ciMODULE_PARM_DESC(timer, "force use of timer interrupt"); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2858c2ecf20Sopenharmony_ciMODULE_AUTHOR("John Levon <levon@movementarian.org>"); 2868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OProfile system profiler"); 287