LTT core implementation
PATCH 1/4 LTT core implementation
Andrew,
Per our discussion a while ago, this is an updated set of patches for LTT with a cleanup of the namespace to avoid the use of upper-case macros. All four patches are against rc3-bk8. Your feedback is greatly appreciated.
This is based on the 2.6.9 set of patches shipped with LTT 0.9.6pre4 which was released earlier today.
Karim
Signed-off-by Karim Yaghmour <[email protected]>
kernel/ltt-core.c +2589 -0
@@ -0,0 +1,2589 @@ +/* +* ltt-core.c +* +* (C) Copyright, 1999, 2000, 2001, 2002, 2003, 2004 - +* Karim Yaghmour ([email protected]) +* +* Contains the kernel code for the Linux Trace Toolkit. +* +* Author: +* Karim Yaghmour ([email protected]) +* +* Changelog: +* 14/12/04, Renamed trace macros and variables to avoid namespace +* pollution (i.e. TRACE_XXX is now ltt_ev_xxx, etc.) +* 24/01/04, Revamped tracer to rely entirely on relayfs, no sys_trace. +* Renamed all relevant files and functions from trace* to ltt*. +* 14/03/03, Modified to use relayfs ([email protected]) +* 15/10/02, Changed tracer from device to kernel subsystem and added +* custom trace system call (sys_trace). +* 01/10/02, Coding style change to fit with kernel coding style. +* 16/02/02, Added Tom Zanussi's implementation of K42's lockless logging. +* K42 tracing guru Robert Wisniewski participated in the +* discussions surrounding this implementation. A big thanks to +* the IBM folks. +* 03/12/01, Added user event support. +* 05/01/01, Modified PPC bit manipulation functions for x86 +* compatibility ([email protected]). +* 15/11/00, Finally fixed memory allocation and remapping method. Now +* using BTTV-driver-inspired code. +* 13/03/00, Modified tracer so that the daemon mmaps the tracer's buffers +* in it's address space rather than use "read". +* 26/01/00, Added support for standardized buffer sizes and extensibility +* of events. +* 01/10/99, Modified tracer in order to used double-buffering. +* 28/09/99, Adding tracer configuration support. +* 09/09/99, Changing the format of an event record in order to reduce the +* size of the traces. +* 04/03/99, Initial typing. +* +* Note: +* The sizes of the variables used to store the details of an event are +* planned for a system who gets at least one clock tick every 10 +* milli-seconds. There has to be at least one event every 2^32-1 +* microseconds, otherwise the size of the variable holding the time +* doesn't work anymore. +*/ + +#include <linux/init.h> +#include <linux/ltt-core.h> +#include <linux/ltt-events.h> +#include <linux/errno.h> +#include <linux/stddef.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/time.h> +#include <linux/vmalloc.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/delay.h> +#include <linux/proc_fs.h> + +#include <asm/io.h> +#include <asm/current.h> +#include <asm/uaccess.h> +#include <asm/bitops.h> +#include <asm/pgtable.h> +#include <asm/relay.h> +#include <asm/ltt.h> + +/* Tracer configuration */ +static int num_cpus; + +/* System call tracing behavior */ +unsigned int syscall_entry_trace_active =0 ; +unsigned int syscall_exit_trace_active =0 ; +static int use_syscall_eip_bounds; +static int lower_eip_bound_set; +static int upper_eip_bound_set; +static void * lower_eip_bound; +static void * upper_eip_bound; +static int fetch_syscall_eip_use_depth; +static int fetch_syscall_eip_use_bounds; +static void * syscall_lower_eip_bound; +static void * syscall_upper_eip_bound; +static int syscall_eip_depth; +static int syscall_eip_depth_set; + +/* Data buffer management */ +static struct ltt_trace_struct current_traces[NR_TRACES ]; +static u32 start_reserve =LTT_TRACER_FIRST_EVENT_SIZE ; +static u32 end_reserve =LTT_TRACER_LAST_EVENT_SIZE ; +static u32 trace_start_reserve =LTT_TRACER_START_TRACE_EVENT_SIZE ; +static struct ltt_arch_info ltt_arch_info; +static struct ltt_buf_control_info shared_buf_ctl; +static char * user_event_data =NULL ; + +/* Relayfs interaction */ +static struct rchan_callbacks ltt_callbacks;/* relayfs callbacks */ +static char relay_file_name[PATH_MAX ];/* scratch area */ + +/* Timer management */ +static struct timer_list heartbeat_timer; +static struct timer_list percpu_timer[NR_CPUS] __cacheline_aligned; + +/* /proc variables */ +static struct proc_dir_entry * ltt_proc_root_entry;/* proc/ltt */ +static int tmp_rchan_handles[NR_CPUS ]; +static char relayfs_path[PATH_MAX ];/* path to attribs */ +static int control_channel;/* LTT control channel */ + +/* Forward declarations */ +static struct proc_dir_entry *create_handle_proc_dir (unsigned trace_handle); +static void remove_handle_proc_dir (struct proc_dir_entry *handle_dir, +unsigned trace_handle); + +/* Size of statically defined events */ +int event_struct_size[LTT_EV_MAX +1 ] = +{ +sizeof (ltt_trace_start), +sizeof (ltt_syscall_entry), +0 ,/* LTT_SYSCALL_EXIT */ +sizeof (ltt_trap_entry), +0 ,/* LTT_TRAP_EXIT */ +sizeof (ltt_irq_entry), +0 ,/* LTT_IRQ_EXIT */ +sizeof (ltt_schedchange), +0 ,/* LTT_KERNEL_TIMER */ +sizeof (ltt_soft_irq), +sizeof (ltt_process), +sizeof (ltt_file_system), +sizeof (ltt_timer), +sizeof (ltt_memory), +sizeof (ltt_socket), +sizeof (ltt_ipc), +sizeof (ltt_network), +sizeof (ltt_buffer_start), +sizeof (ltt_buffer_end), +sizeof (ltt_new_event), +sizeof (ltt_custom), +sizeof (ltt_change_mask), +0 /* LTT_HEARTBEAT */ +}; + +/* Custom event description */ +struct custom_event_desc { +ltt_new_event event; + +pid_t owner_pid; + +struct custom_event_desc *next; +struct custom_event_desc *prev; +}; + +/* Custom event management */ +static int next_event_id =LTT_EV_MAX +1 ; +static rwlock_t custom_list_lock =RW_LOCK_UNLOCKED ; +static rwlock_t trace_handle_table_lock =RW_LOCK_UNLOCKED ; +static struct custom_event_desc custom_events_head; +static struct custom_event_desc *custom_events =NULL ; + +/* Handle management */ +struct trace_handle_struct { +struct task_struct *owner; +}; +static struct trace_handle_struct trace_handle_table[LTT_MAX_HANDLES ]; + +/* +* Helper functions +*/ + +/** +* set_waiting_for_cpu_async: - Utility function for setting wait flags +* @cpu_id: which CPU to set flag on +* @bit: which bit to set +*/ +static inline void set_waiting_for_cpu_async (unsigned int trace_handle,u8 cpu_id,int bit) +{ +atomic_set (&waiting_for_cpu_async (trace_handle, cpu_id), +atomic_read (&waiting_for_cpu_async (trace_handle, cpu_id)) | bit); +} + +/** +* clear_waiting_for_cpu_async: - Utility function for clearing wait flags +* @cpu_id: which CPU to clear flag on +* @bit: which bit to clear +*/ +static inline void clear_waiting_for_cpu_async (unsigned int trace_handle,u8 cpu_id,int bit) +{ +atomic_set (&waiting_for_cpu_async (trace_handle, cpu_id), +atomic_read (&waiting_for_cpu_async (trace_handle, cpu_id)) & ~bit); +} + +/* +* Trace heartbeat +*/ + +/** +* write_heartbeat_event: - Timer function generating hearbeat event. +* @data: unused +* +* Guarantees at least 1 event is logged before low word of TSC wraps. +*/ +static void write_heartbeat_event (unsigned long data) +{ +unsigned long int flags; +int i, j; + +local_irq_save (flags); +for (i =0 ; i <NR_TRACES ; i++) { +if (current_traces[i].active && current_traces[i].using_tsc) { +for (j =0 ; j < num_cpus; j++) +set_waiting_for_cpu_async (i, j,LTT_TRACE_HEARTBEAT ); + } + } +local_irq_restore (flags); + +del_timer (&heartbeat_timer); + heartbeat_timer.expires = jiffies +0xffffffffUL /loops_per_jiffy -1 ; +add_timer (&heartbeat_timer); +} + +/** +* need_heartbeat: - If any active trace uses TSC timestamping, return 1 +* +* Returns the number of active traces using TSC timestamping +* +* Needed for starting/stopping the heartbeat timer depending on whether +* any trace needs it or not. +*/ +int need_heartbeat (void ) +{ +int i, retval =0 ; +struct ltt_trace_struct *trace; + +for (i =0 ; i <NR_TRACES ; i++) { + trace = ¤t_traces[i]; +if (trace->active && trace->using_tsc) + retval++; + } + +return retval; +} + +/** +* init_heartbeat_timer: - Start timer generating hearbeat events. +*/ +static void init_heartbeat_timer (void ) +{ +if (loops_per_jiffy >0 ) { +init_timer (&heartbeat_timer); + heartbeat_timer.function = write_heartbeat_event; + heartbeat_timer.expires = jiffies + +0xffffffffUL /loops_per_jiffy -1 ; +add_timer (&heartbeat_timer); + } +else +printk (KERN_ALERT "LTT: No TSC for heartbeat timer - continuing without one \n" ); +} + +/** +* delete_heartbeat_timer: - Stop timer generating hearbeat events. +*/ +static void delete_heartbeat_timer (void ) +{ +if (loops_per_jiffy >0 ) +del_timer (&heartbeat_timer); +} + +/* +* Tasks and timers for trace finalization +*/ + +/** +* all_channels_finalized: - Verify that all channels have been finalized. +* @trace_handle: the trace containing the channels to be tested +* +* Returns 1 if channels on all CPUs are complete, 0 otherwise. +*/ +static int all_channels_finalized (unsigned int trace_handle) +{ +int i; + +for (i =0 ; i < num_cpus; i++) +if (atomic_read (&waiting_for_cpu_async (trace_handle, i)) <T_FINALIZE_TRACE ) +return 0 ; + +return 1 ; +} + +/** +* active_traces: - The number of currently active traces +* +* Returns the number of active traces +*/ +static inline int active_traces (void ) +{ +int i, nr_active =0 ; + +for (i =0 ; i <NR_TRACES ; i++) +if (current_traces[i].active) + nr_active++; + +return nr_active; +} + +/** +* del_percpu_timers: - Delete all per_cpu timers. +*/ +static inline void del_percpu_timers (void ) +{ +int i; + +for (i =0 ; i < num_cpus; i++) +del_timer_sync (&percpu_timer[i]); +} + +/** +* remove_readers_async: - Remove all map readers asynchronously. +* @private: the trace_handle containing the readers to be removed +*/ +static void remove_readers_async (void *private) +{ +int i; +unsigned int trace_handle = (unsigned int )private; + +for (i =0 ; i < num_cpus; i++) { +remove_map_reader (trace_channel_reader (trace_handle, i)); +trace_channel_reader (trace_handle, i) =NULL ; + } +} + +/** +* remove_readers: - Remove all map readers. +* @trace_handle: the trace containing the readers to be removed +*/ +static inline void remove_readers (unsigned int trace_handle) +{ +int i; + +for (i =0 ; i < num_cpus; i++) { +remove_map_reader (trace_channel_reader (trace_handle, i)); +trace_channel_reader (trace_handle, i) =NULL ; + } +} + +/** +* do_waiting_async_tasks: - perform asynchronous per-CPU tasks. +* @trace_handle: the trace handle +* @cpu_id: the CPU the tasks should be executed on +*/ +static void do_waiting_async_tasks (unsigned int trace_handle,u8 cpu_id) +{ +unsigned long int flags; +int tasks; +struct ltt_trace_struct *trace; + + trace = ¤t_traces[trace_handle]; + +local_irq_save (flags); + tasks =atomic_read (&waiting_for_cpu_async (trace_handle, cpu_id)); + +if (tasks ==0 ) { +local_irq_restore (flags); +return ; + } + +if (trace->using_tsc && trace->tracer_started && (tasks <T_TRACE_HEARTBEAT )) { +clear_waiting_for_cpu_async (trace_handle, cpu_id,LTT_TRACE_HEARTBEAT ); +ltt_ev_heartbeat (); + } + +if (trace->tracer_stopping && (tasks <T_FINALIZE_TRACE )) { +clear_waiting_for_cpu_async (trace_handle, cpu_id,LTT_FINALIZE_TRACE ); +if (relay_close (trace_channel_handle (trace_handle, cpu_id)) !=0 ) +printk (KERN_ALERT "LTT: Couldn't close trace channel %d\n" ,trace_channel_handle (trace_handle, cpu_id)); + +set_bit (cpu_id, &trace->buffer_switches_pending); + +if (all_channels_finalized (trace_handle)) { +PREPARE_WORK (&trace->work, remove_readers_async, (void *)trace_handle); +schedule_work (&trace->work); + + trace->tracer_stopping =0 ; + } + } + +local_irq_restore (flags); +} + +/** +* check_waiting_async_tasks: - Timer function checking for async tasks. +* @data: unused +*/ +static void check_waiting_async_tasks (unsigned long data) +{ +int i; +int cpu =smp_processor_id (); + +for (i =0 ; i <NR_TRACES ; i++) { +if (atomic_read (&waiting_for_cpu_async (i, cpu)) !=0 ) +do_waiting_async_tasks (i, cpu); + } + +del_timer (&percpu_timer[cpu]); + percpu_timer[cpu].expires = jiffies +LTT_PERCPU_TIMER_FREQ ; +add_timer (&percpu_timer[cpu]); +} + +/** +* _init_percpu_timer: - Start timer checking for async tasks. +*/ +void _init_percpu_timer (void * dummy) +{ +int cpu =smp_processor_id (); + +init_timer (&percpu_timer[cpu]); + percpu_timer[cpu].function = check_waiting_async_tasks; + percpu_timer[cpu].expires = jiffies +LTT_PERCPU_TIMER_FREQ ; +add_timer (&percpu_timer[cpu]); +} + +static inline void init_percpu_timers (void ) +{ +_init_percpu_timer (NULL ); + +if (smp_call_function (_init_percpu_timer,NULL ,1 ,1 ) !=0 ) +printk (KERN_ALERT "LTT: Couldn't initialize all per-CPU timers\n" ); +} + +/* +* User-kernel interface for tracer +*/ + +/** +* update_shared_buffer_control: - prepare for GET_BUFFER_CONTROL ioctl +* @trace: the trace instance +* @cpu_id: the CPU associated with the ioctl +*/ +static inline void update_shared_buffer_control (struct ltt_trace_struct *trace,u8 cpu_id) +{ +struct rchan_info channel_info; +int i; +int channel_handle =trace_channel_handle (trace->trace_handle, cpu_id); + +if (relay_info (channel_handle, &channel_info) ==-1 ) { + shared_buf_ctl.buffer_control_valid =0 ; +return ; + } + + shared_buf_ctl.cpu_id = cpu_id; + shared_buf_ctl.buffer_switches_pending = trace->buffer_switches_pending & ~(1UL << cpu_id); + shared_buf_ctl.buffer_control_valid =1 ; + shared_buf_ctl.buf_size = channel_info.buf_size; + shared_buf_ctl.n_buffers = channel_info.n_bufs; + shared_buf_ctl.cur_idx = channel_info.cur_idx; + shared_buf_ctl.buffers_produced = channel_info.bufs_produced; + shared_buf_ctl.buffers_consumed = channel_info.bufs_consumed; + +if (channel_info.flags &RELAY_SCHEME_LOCKLESS ) { +for (i =0 ; i < channel_info.n_bufs; i++) { + shared_buf_ctl.buffer_complete[i] = + channel_info.buffer_complete[i]; + } + } +} + +/** +* ltt_flight_pause: - pause the flight recorder +* +* Allows for external control of flight recorder e.g. for crashdump +*/ +void ltt_flight_pause (void ) +{ +struct ltt_trace_struct *trace; + + trace = ¤t_traces[FLIGHT_HANDLE ]; +if (!trace->active) +return ; + + trace->paused =1 ; +} + +/** +* ltt_flight_unpause: - unpause the flight recorder +* +* Allows for external control of flight recorder e.g. for crashdump +*/ +void ltt_flight_unpause (void ) +{ +struct ltt_trace_struct *trace; + + trace = ¤t_traces[FLIGHT_HANDLE ]; +if (!trace->active) +return ; + + trace->paused =0 ; +} + +/** +* ltt_ioctl: - Tracing kernel-user control interface +* @rchan_id: rchan id ioctl occurred on +* @tracer_command: command given by the caller +* @command_arg: argument to the command +* +* Returns: +* >0, In case the caller requested the number of events lost. +* 0, Everything went OK +* -ENOSYS, no such command +* -EINVAL, tracer not properly configured +* -EBUSY, tracer can't be reconfigured while in operation +* -ENOMEM, no more memory +* -EFAULT, unable to access user space memory +* -EACCES, invalid tracer handle +*/ +static int ltt_ioctl (int rchan_id, +unsigned int tracer_command, +unsigned long arg) +{ +int retval; +int new_user_event_id; +unsigned long int flags; +u8 cpu_id; +u8 i; +u32 buffers_consumed; +ltt_custom user_event; +ltt_change_mask trace_mask; +ltt_new_event new_user_event; +struct ltt_buffers_committed buffers_committed; +struct ltt_trace_struct *trace =NULL ; +struct ltt_tracer_status tracer_status; +unsigned int tracer_handle; +unsigned long command_arg; + +if (copy_from_user (&tracer_handle, (void *)arg,sizeof (unsigned int ))) +return -EFAULT ; + +if (copy_from_user (&command_arg, (void *)(arg +sizeof (tracer_handle)),sizeof (unsigned long ))) +return -EFAULT ; + +if (tracer_command ==LTT_TRACER_ALLOC_HANDLE ) +return ltt_alloc_trace_handle (tracer_handle); + +if (!ltt_valid_trace_handle (tracer_handle)) +return -EACCES ; + +if (tracer_handle <NR_TRACES ) + trace = ¤t_traces[tracer_handle]; +else if (tracer_handle >=NR_TRACES ) { +if (current_traces[TRACE_HANDLE ].active) + trace = ¤t_traces[TRACE_HANDLE ]; +if (trace ==NULL && tracer_command !=LTT_TRACER_GET_STATUS ) +return -EACCES ; + } + +if ((tracer_handle <NR_TRACES ) + && (trace->tracer_started ==1 ) + && (tracer_command !=LTT_TRACER_STOP ) + && (tracer_command !=LTT_TRACER_PAUSE ) + && (tracer_command !=LTT_TRACER_UNPAUSE ) + && (tracer_command !=LTT_TRACER_DATA_COMITTED ) + && (tracer_command !=LTT_TRACER_GET_ARCH_INFO ) + && (tracer_command !=LTT_TRACER_GET_BUFFER_CONTROL ) + && (tracer_command !=LTT_TRACER_GET_START_INFO )) +return -EBUSY ; + +if ((tracer_handle >=NR_TRACES ) + && (tracer_command !=LTT_TRACER_CREATE_USER_EVENT ) + && (tracer_command !=LTT_TRACER_DESTROY_USER_EVENT ) + && (tracer_command !=LTT_TRACER_TRACE_USER_EVENT ) + && (tracer_command !=LTT_TRACER_FREE_HANDLE ) + && (tracer_command !=LTT_TRACER_GET_STATUS ) + && (tracer_command !=LTT_TRACER_SET_EVENT_MASK ) + && (tracer_command !=LTT_TRACER_GET_EVENT_MASK )) +return -ENOSYS ; + +switch (tracer_command) { +case LTT_TRACER_START : +if (trace->using_tsc && (need_heartbeat () ==1 )) +init_heartbeat_timer (); +if (active_traces () ==1 ) +init_percpu_timers (); + +if (((use_syscall_eip_bounds ==1 ) + && (syscall_eip_depth_set ==1 )) + || ((use_syscall_eip_bounds ==1 ) + && ((lower_eip_bound_set !=1 ) + || (upper_eip_bound_set !=1 ))) + || ((trace->tracing_pid ==1 ) + && (trace->tracing_pgrp ==1 ))) +return -EINVAL ; + +if (ltt_set_trace_config (syscall_eip_depth_set, + use_syscall_eip_bounds, + syscall_eip_depth, + lower_eip_bound, + upper_eip_bound) <0 ) +return -EINVAL ; + +if (trace->flight_recorder) +ltt_set_flight_recorder_config (trace); + +ltt_set_bit (LTT_EV_BUFFER_START , &trace->traced_events); +ltt_set_bit (LTT_EV_BUFFER_START , &trace->log_event_details_mask); +ltt_set_bit (LTT_EV_START , &trace->traced_events); +ltt_set_bit (LTT_EV_START , &trace->log_event_details_mask); +ltt_set_bit (LTT_EV_CHANGE_MASK , &trace->traced_events); +ltt_set_bit (LTT_EV_CHANGE_MASK , &trace->log_event_details_mask); + + syscall_entry_trace_active =ltt_syscall_active (LTT_EV_SYSCALL_ENTRY ); + syscall_exit_trace_active =ltt_syscall_active (LTT_EV_SYSCALL_EXIT ); + + trace->tracer_stopping =0 ; + trace->tracer_started =1 ; + +ltt_reregister_custom_events (); +break ; + +case LTT_TRACER_STOP : +if (trace->flight_recorder) { +for (i =0 ; i < num_cpus; i++) + tmp_rchan_handles[i] =trace_channel_handle (tracer_handle, i); +ltt_free_all_handles (NULL ); + }else { + trace->tracer_stopping =1 ; + trace->tracer_started =0 ; + } + + syscall_entry_trace_active =ltt_syscall_active (LTT_EV_SYSCALL_ENTRY ); + syscall_exit_trace_active =ltt_syscall_active (LTT_EV_SYSCALL_EXIT ); + +if (trace->flight_recorder) { +for (i =0 ; i < num_cpus; i++) { +if (relay_close (tmp_rchan_handles[i]) !=0 ) +printk (KERN_ALERT "LTT: Couldn't close trace channel %d\n" ,trace_channel_handle (tracer_handle, i)); + } +remove_readers (tracer_handle); + }else { +for (i =0 ; i < num_cpus; i++) +set_waiting_for_cpu_async (tracer_handle, i,LTT_FINALIZE_TRACE ); + } +break ; + +case LTT_TRACER_PAUSE : + trace->paused =1 ; +break ; + +case LTT_TRACER_UNPAUSE : + trace->paused =0 ; +break ; + +case LTT_TRACER_CONFIG_DEFAULT : +ltt_set_default_config (trace); +break ; + +case LTT_TRACER_CONFIG_MEMORY_BUFFERS : +if (trace->use_locking ==1 ) { +if (command_arg <LTT_TRACER_MIN_BUF_SIZE ) +return -EINVAL ; + } +else { +if ((command_arg <LTT_TRACER_LOCKLESS_MIN_BUF_SIZE ) || + (command_arg >LTT_TRACER_LOCKLESS_MAX_BUF_SIZE )) +return -EINVAL ; + } + +return ltt_set_buffer_size (trace, command_arg, relayfs_path); +break ; + +case LTT_TRACER_CONFIG_N_MEMORY_BUFFERS : +if (command_arg <LTT_TRACER_MIN_BUFFERS || + command_arg >LTT_TRACER_MAX_BUFFERS ) +return -EINVAL ; + +return ltt_set_n_buffers (trace, command_arg); +break ; + +case LTT_TRACER_CONFIG_USE_LOCKING : + trace->use_locking = command_arg; + +if ((trace->use_locking ==0 ) && (have_cmpxchg () ==0 )) +return -EINVAL ; +break ; + +case LTT_TRACER_CONFIG_EVENTS : +if (copy_from_user (&trace->traced_events, (void *) command_arg,sizeof (trace->traced_events))) +return -EFAULT ; +break ; + +case LTT_TRACER_CONFIG_TIMESTAMP : + trace->using_tsc = command_arg; + +if ((trace->using_tsc ==1 ) && (have_tsc () ==0 )) { + trace->using_tsc =0 ; +return -EINVAL ; + } + +break ; + +case LTT_TRACER_CONFIG_DETAILS : +if (copy_from_user (&trace->log_event_details_mask, (void *) command_arg,sizeof (trace->log_event_details_mask))) +return -EFAULT ; +break ; + +case LTT_TRACER_CONFIG_CPUID : + trace->log_cpuid =0 ;/* disabled*/ +break ; + +case LTT_TRACER_CONFIG_PID : + trace->tracing_pid =1 ; + trace->traced_pid = command_arg; +break ; + +case LTT_TRACER_CONFIG_PGRP : + trace->tracing_pgrp =1 ; + trace->traced_pgrp = command_arg; +break ; + +case LTT_TRACER_CONFIG_GID : + trace->tracing_gid =1 ; + trace->traced_gid = command_arg; +break ; + +case LTT_TRACER_CONFIG_UID : + trace->tracing_uid =1 ; + trace->traced_uid = command_arg; +break ; + +case LTT_TRACER_CONFIG_SYSCALL_EIP_DEPTH : + syscall_eip_depth_set =1 ; + syscall_eip_depth = command_arg; +break ; + +case LTT_TRACER_CONFIG_SYSCALL_EIP_LOWER : + use_syscall_eip_bounds =1 ; + lower_eip_bound = (void *) command_arg; + lower_eip_bound_set =1 ; +break ; + +case LTT_TRACER_CONFIG_SYSCALL_EIP_UPPER : + use_syscall_eip_bounds =1 ; + upper_eip_bound = (void *) command_arg; + upper_eip_bound_set =1 ; +break ; + +case LTT_TRACER_DATA_COMITTED : +if (copy_from_user (&buffers_committed, (void *)command_arg, +sizeof (buffers_committed))) +return -EFAULT ; + + cpu_id = buffers_committed.cpu_id; + buffers_consumed = buffers_committed.buffers_consumed; +clear_bit (cpu_id, &trace->buffer_switches_pending); + +local_irq_save (flags); +relay_buffers_consumed (trace_channel_reader (tracer_handle, cpu_id), + buffers_consumed); +local_irq_restore (flags); + +break ; + +case LTT_TRACER_GET_EVENTS_LOST : +return events_lost (tracer_handle, command_arg); +break ; + +case LTT_TRACER_CREATE_USER_EVENT : +if (copy_from_user (&new_user_event, (void *) command_arg,sizeof (new_user_event))) +return -EFAULT ; + + new_user_event_id =ltt_create_owned_event (new_user_event.type, + new_user_event.desc, + new_user_event.format_type, + new_user_event.form, + current->pid); +if (new_user_event_id >=0 ) { + new_user_event.id = new_user_event_id; +if (copy_to_user ((void *) command_arg, &new_user_event,sizeof (new_user_event))) { +ltt_destroy_event (new_user_event_id); +return -EFAULT ; + } + } +else +return new_user_event_id; +break ; + +case LTT_TRACER_DESTROY_USER_EVENT : +ltt_destroy_event ((int ) command_arg); +break ; + +case LTT_TRACER_TRACE_USER_EVENT : +if (copy_from_user (&user_event, (void *) command_arg,sizeof (user_event))) +return -EFAULT ; + +if ((user_event_data ==NULL ) + && (user_event_data =vmalloc (LTT_CUSTOM_EV_MAX_SIZE )) <0 ) +return -ENOMEM ; + +if (copy_from_user (user_event_data, user_event.data, user_event.data_size)) +return -EFAULT ; + + retval =ltt_log_raw_event (user_event.id, + user_event.data_size, + user_event_data); + +if (retval <0 ) +return retval; +break ; + +case LTT_TRACER_SET_EVENT_MASK : +if (copy_from_user (&(trace_mask.mask), (void *) command_arg,sizeof (trace_mask.mask))) +return -EFAULT ; + + retval =_ltt_log_event (trace, +LTT_EV_CHANGE_MASK , + &trace_mask, +smp_processor_id ()); + +memcpy (&trace->traced_events, &(trace_mask.mask),sizeof (trace_mask.mask)); + + syscall_entry_trace_active =ltt_syscall_active (LTT_EV_SYSCALL_ENTRY ); + syscall_exit_trace_active =ltt_syscall_active (LTT_EV_SYSCALL_EXIT ); + +ltt_set_bit (LTT_EV_BUFFER_START , &trace->traced_events); +ltt_set_bit (LTT_EV_START , &trace->traced_events); +ltt_set_bit (LTT_EV_CHANGE_MASK , &trace->traced_events); + +return retval; +break ; + +case LTT_TRACER_GET_EVENT_MASK : +if (copy_to_user ((void *) command_arg, &trace->traced_events,sizeof (trace->traced_events))) +return -EFAULT ; +break ; + +case LTT_TRACER_GET_ARCH_INFO : + ltt_arch_info.n_cpus = num_cpus; + ltt_arch_info.page_shift =PAGE_SHIFT ; + +if (copy_to_user ((void *) command_arg, + <t_arch_info, +sizeof (ltt_arch_info))) +return -EFAULT ; +break ; + +case LTT_TRACER_GET_START_INFO : +if (trace->trace_start_data) { +if (copy_to_user ((void *)command_arg, + trace->trace_start_data, +sizeof (ltt_trace_start))) +return -EFAULT ; + }else +return -EINVAL ; +break ; + +case LTT_TRACER_GET_STATUS : +if (ltt_get_status (&tracer_status)) +return -EINVAL ; + +if (copy_to_user ((void *)command_arg, + &tracer_status, +sizeof (struct ltt_tracer_status ))) +return -EFAULT ; +break ; + +case LTT_TRACER_GET_BUFFER_CONTROL : +if (copy_from_user (&shared_buf_ctl, (void *)command_arg,sizeof (shared_buf_ctl))) +return -EFAULT ; + +if (shared_buf_ctl.cpu_id ==-1 ) { +for (i =0 ; i < num_cpus; i++) { +if (trace->buffer_switches_pending & (1UL << i)) { +update_shared_buffer_control (trace, i); +if (copy_to_user ((void *)command_arg, + &shared_buf_ctl, +sizeof (struct ltt_buf_control_info ))) +return -EFAULT ; +return 0 ; + } + } + } +else { +update_shared_buffer_control (trace, (u8 )shared_buf_ctl.cpu_id); +if (copy_to_user ((void *)command_arg, + &shared_buf_ctl, +sizeof (struct ltt_buf_control_info ))) +return -EFAULT ; +return 0 ; + } + + shared_buf_ctl.cpu_id =0 ; + shared_buf_ctl.buffer_control_valid =0 ; + +if (copy_to_user ((void *) command_arg, + &shared_buf_ctl, +sizeof (struct ltt_buf_control_info ))) +return -EFAULT ; +break ; + +case LTT_TRACER_FREE_HANDLE : +return ltt_free_trace_handle (tracer_handle); +break ; + +case LTT_TRACER_FREE_DAEMON_HANDLE : +return ltt_free_daemon_handle (trace); +break ; + +case LTT_TRACER_FREE_ALL_HANDLES : +ltt_free_all_handles (current); +break ; + +case LTT_TRACER_MAP_BUFFER : +return -EFAULT ; +break ; + +default : +return -ENOSYS ; +break ; + } + +return 0 ; +} + +/* +* Trace Handles +*/ + +/** +* ltt_valid_trace_handle: - Validate tracer handle. +* @tracer_handle: handle to be validated +* +* Returns: +* 1, if handle is valid +* 0, if handle is invalid +*/ +int ltt_valid_trace_handle (unsigned int tracer_handle) +{ +int retval =0 ; +struct ltt_trace_struct *trace; + +if (tracer_handle <NR_TRACES ) { + trace = ¤t_traces[tracer_handle]; +if (!trace->active) + retval =0 ; +else if (!trace->flight_recorder) { +if (trace->daemon_task_struct == current) + retval =1 ; + }else + retval =1 ; + }else { +read_lock (&trace_handle_table_lock); +if (trace_handle_table[tracer_handle -NR_TRACES ].owner == current) + retval =1 ; +else + retval =0 ; +read_unlock (&trace_handle_table_lock); + } + +return retval; +} + +/** +* ltt_alloc_trace_handle: - Allocate trace handle to caller. +* @tracer_handle: handle requested by process +* +* Returns: +* Handle ID, everything went OK +* -ENODEV, no more free handles. +* -EBUSY, daemon handle already in use. +*/ +int ltt_alloc_trace_handle (unsigned int tracer_handle) +{ +int i; +int retval; +struct ltt_trace_struct *trace =NULL ; + +if (tracer_handle <NR_TRACES ) { + trace = ¤t_traces[tracer_handle]; +if (trace ==NULL ) +return -ENODEV ; +if (trace->active) +return -EBUSY ; + } + +if (tracer_handle >=NR_TRACES ) { +write_lock (&trace_handle_table_lock); +for (i =0 ; i <LTT_MAX_HANDLES ; i++) +if (trace_handle_table[i].owner ==NULL ) { + trace_handle_table[i].owner = current; +break ; + } +write_unlock (&trace_handle_table_lock); +if (i ==LTT_MAX_HANDLES ) + retval = -ENODEV ; +else + retval = (i +NR_TRACES ); + } +else { + trace->active = trace; + trace->tracer_started =0 ; + trace->tracer_stopping =0 ; +if (tracer_handle ==TRACE_HANDLE ) { + trace->flight_recorder =0 ; + trace->daemon_task_struct = current; + }else { +if ((trace->trace_start_data = (struct _ltt_trace_start *)kmalloc (sizeof (struct _ltt_trace_start ),GFP_ATOMIC )) ==NULL ) +return -ENOMEM ; + } + + trace->proc_dir_entry =create_handle_proc_dir (tracer_handle); +ltt_set_default_config (trace); + retval = trace->trace_handle = tracer_handle; + } + +return retval; +} + +/** +* ltt_free_trace_handle: - Free a single handle. +* tracer_handle: handle to be freed. +* +* Returns: +* 0, everything went OK +* -ENODEV, no such handle. +* -EACCES, handle doesn't belong to caller. +*/ +int ltt_free_trace_handle (unsigned int tracer_handle) +{ +int retval; + +if ((tracer_handle <NR_TRACES ) || (tracer_handle >=LTT_MAX_HANDLES )) +return -ENODEV ; + +write_lock (&trace_handle_table_lock); + +if (trace_handle_table[tracer_handle -NR_TRACES ].owner == current) { + trace_handle_table[tracer_handle -NR_TRACES ].owner =NULL ; + retval =0 ; + } +else + retval = -EACCES ; + +write_unlock (&trace_handle_table_lock); + +return retval; +} + +/** +* ltt_free_daemon_handle: - Free the daemon's handle. +* @trace: the trace instance +* +* Returns: +* 0, everything went OK +* -EACCES, handle doesn't belong to caller. +* -EBUSY, there are still event writes in progress so the buffer can't +* be released. +*/ +int ltt_free_daemon_handle (struct ltt_trace_struct *trace) +{ +int i; + +if (!trace->flight_recorder) { +if (trace->daemon_task_struct != current) +return -EACCES ; + +for (i =0 ; i < num_cpus; i++) { +if (events_lost (trace->trace_handle, i) >0 ) +printk (KERN_ALERT "LTT: Lost %d events on cpu %d\n" , +events_lost (trace->trace_handle, i), i); + } + trace->daemon_task_struct =NULL ; + } + + trace->active =NULL ; + +if (!active_traces ()) +del_percpu_timers (); + +if (trace->using_tsc && !need_heartbeat ()) +delete_heartbeat_timer (); + +for (i =0 ; i < num_cpus; i++) { +if (trace_channel_handle (trace->trace_handle, i) !=-1 ) { +trace_channel_handle (trace->trace_handle, i) =-1 ; +trace_channel_reader (trace->trace_handle, i) =NULL ; + } + } + +remove_handle_proc_dir (trace->proc_dir_entry,0 ); + + trace->use_locking =1 ; +ltt_set_default_config (trace); + trace->tracer_started =0 ; + trace->tracer_stopping =0 ; +if (trace->trace_start_data) +kfree (trace->trace_start_data); + +return 0 ; +} + +/** +* ltt_free_all_handles: - Free all handles taken. +* @task_ptr: pointer to exiting task. +* +* Free all handles taken against a given channel. If task_ptr is NULL, +* it means there is no daemon, i.e. free all handles taken agains the +* flight recorder channel, otherwise task_ptr refers to a trace daemon. +*/ +void ltt_free_all_handles (struct task_struct * task_ptr) +{ +int i; +struct ltt_trace_struct *trace; + +if (task_ptr ==NULL ) { +if (current_traces[FLIGHT_HANDLE ].active) { +ltt_free_daemon_handle (¤t_traces[FLIGHT_HANDLE ]); +return ; + } + } +else { + trace = ¤t_traces[TRACE_HANDLE ]; +if (trace->active && trace->daemon_task_struct == task_ptr) +ltt_free_daemon_handle (trace); + } + +write_lock (&trace_handle_table_lock); +for (i =0 ; i <LTT_MAX_HANDLES ; i++) +if (trace_handle_table[i].owner == current) + trace_handle_table[i].owner =NULL ; +write_unlock (&trace_handle_table_lock); +} + +/* +* Tracer Configuration +*/ + +/** +* init_trace: - Initialize a trace/flight recorder instance +* @trace_struct: trace/flight recorder struct +* +* Initialize a trace instance to default values. +*/ +static void init_trace (struct ltt_trace_struct *trace) +{ + trace->trace_handle =0 ; + + trace->active =NULL ; + trace->paused =0 ; + trace->flight_recorder =1 ; + trace->daemon_task_struct =NULL ; + trace->trace_start_data =NULL ; + + trace->tracer_started =0 ; + trace->tracer_stopping =0 ; + trace->proc_dir_entry =NULL ; + trace->traced_events =0 ; + trace->log_event_details_mask =0 ; + trace->log_cpuid =0 ; + trace->tracing_pid =0 ; + trace->tracing_pgrp =0 ; + trace->tracing_gid =0 ; + trace->tracing_uid =0 ; + trace->traced_pid =0 ; + trace->traced_pgrp =0 ; + trace->traced_gid =0 ; + trace->traced_uid =0 ; + trace->use_locking =1 ; + trace->n_buffers =0 ; + trace->buf_size =0 ; + trace->using_tsc =0 ; + + trace->buffer_switches_pending =0 ; +INIT_WORK (&trace->work,NULL ,NULL ); +} + +/** +* ltt_syscall_active: - If any active trace is logging syscalls, return 1 +* @syscall_type: either SYSCALL_ENTRY or SYSCALL_EXIT +* +* Returns 1 if any channel is tracing syscalls, 0 otherwise +* +* Needed for setting/clearing the global syscall...active variables +* in order to reflect the needs of all traces. +*/ +int ltt_syscall_active (int syscall_type) +{ +int i, retval =0 ; +struct ltt_trace_struct *trace; + +for (i =0 ; i <NR_TRACES ; i++) { + trace = ¤t_traces[i]; +if (!trace->active) +continue ; +if (ltt_test_bit (syscall_type, &trace->traced_events)) + retval =1 ; + } + +return retval; +} + +/** +* init_channel_data: - Init channel-associated data for new tracing run. +* @trace: the trace to be initialized +*/ +static void init_channel_data (struct ltt_trace_struct *trace) +{ +unsigned i; + + trace->buffer_switches_pending =0 ; + +for (i =0 ; i < num_cpus; i++) { +trace_channel_handle (trace->trace_handle, i) =-1 ; +trace_channel_reader (trace->trace_handle, i) =NULL ; +atomic_set (&waiting_for_cpu_async (trace->trace_handle, i),LTT_NOTHING_TO_DO ); +events_lost (trace->trace_handle, i) =0 ; + } +} + +/** +* ltt_set_n_buffers: - Sets the number of buffers. +* @trace: the trace instance +* @no_buffers: number of buffers +* +* For lockless only, must be a power of 2. +* +* Returns: +* +* 0, Size setting went OK +* -EINVAL, not a power of 2 +*/ +int ltt_set_n_buffers (struct ltt_trace_struct *trace,int no_buffers) +{ +if (hweight32 (no_buffers) !=1 ) +return -EINVAL ; + + trace->n_buffers = no_buffers; + +return 0 ; +} + +/** +* ltt_set_buffer_size: - Sets size of and creates buffers. +* @buf_size: Size of sub-buffers +* @dirname: name of the relayfs directory to contain trace files +* +* Note: dirname should be well-formed before it gets here e.g. +* trailing slashes should be removed. +* +* Returns: +* 0, Size setting went OK +* -ENOMEM, unable to get a hold of memory for tracer +* -EINVAL, tracer not properly configured +*/ +int ltt_set_buffer_size (struct ltt_trace_struct *trace,int buffer_size,char * dirname) +{ +int i; +u32 flags; + +if (trace->flight_recorder) + flags =RELAY_DELIVERY_BULK |RELAY_USAGE_SMP |RELAY_MODE_CONTINUOUS ; +else + flags =RELAY_DELIVERY_BULK |RELAY_USAGE_SMP |RELAY_MODE_NO_OVERWRITE ; + +if ((dirname ==NULL ) || (strlen (dirname) ==0 )) +return -EINVAL ; + +if (trace->using_tsc) + flags |=RELAY_TIMESTAMP_TSC ; +else + flags |=RELAY_TIMESTAMP_GETTIMEOFDAY ; + +if (trace->use_locking) + flags |=RELAY_SCHEME_LOCKING ; +else + flags |=RELAY_SCHEME_LOCKLESS ; + + num_cpus =num_online_cpus (); + +init_channel_data (trace); + + trace->buf_size = buffer_size; + +for (i =0 ; i < num_cpus; i++) { +sprintf (relay_file_name,"%s/cpu%d" , dirname, i); +trace_channel_handle (trace->trace_handle, i) =relay_open (relay_file_name, + buffer_size, + trace->n_buffers, + flags, + <t_callbacks, + start_reserve, + end_reserve, + trace_start_reserve, +0 , +0 , +0 , +NULL , +0 ); +if (trace_channel_handle (trace->trace_handle, i) <0 ) +return -ENOMEM ; + } + +return 0 ; +} + +/** +* ltt_set_default_config: - Sets the tracer in its default config +* +* Returns: +* 0, everything went OK +* -ENOMEM, unable to get a hold of memory for tracer +*/ +int ltt_set_default_config (struct ltt_trace_struct *trace) +{ +int i; +int retval =0 ; + + trace->traced_events =0 ; + +for (i =0 ; i <=LTT_EV_MAX ; i++) { +ltt_set_bit (i, &trace->traced_events); +ltt_set_bit (i, &trace->log_event_details_mask); + } + + syscall_entry_trace_active =ltt_syscall_active (LTT_EV_SYSCALL_ENTRY ); + syscall_exit_trace_active =ltt_syscall_active (LTT_EV_SYSCALL_EXIT ); + + trace->log_cpuid =0 ; + trace->tracing_pid =0 ; + trace->tracing_pgrp =0 ; + trace->tracing_gid =0 ; + trace->tracing_uid =0 ; + trace->using_tsc =0 ; + + syscall_eip_depth_set =0 ; + use_syscall_eip_bounds =0 ; + lower_eip_bound_set =0 ; + upper_eip_bound_set =0 ; + +ltt_set_trace_config (syscall_eip_depth_set, + use_syscall_eip_bounds, +0 , +0 , +0 ); + +return retval; +} + +/** +* ltt_set_trace_config: - Set the tracing configuration +* @do_syscall_depth: Use depth to fetch eip +* @do_syscall_bounds: Use bounds to fetch eip +* @eip_depth: Detph to fetch eip +* @eip_lower_bound: Lower bound eip address +* @eip_upper_bound: Upper bound eip address +* +* Returns: +* 0, all is OK +* -ENOMEDIUM, there isn't a registered tracer +* -ENXIO, wrong tracer +* -EINVAL, invalid configuration +*/ +int ltt_set_trace_config (int do_syscall_depth, +int do_syscall_bounds, +int eip_depth, +void *eip_lower_bound, +void *eip_upper_bound) +{ +if ((do_syscall_depth && do_syscall_bounds) + || (eip_lower_bound > eip_upper_bound) + || (eip_depth <0 )) +return -EINVAL ; + + fetch_syscall_eip_use_depth = do_syscall_depth; + fetch_syscall_eip_use_bounds = do_syscall_bounds; + + syscall_eip_depth = eip_depth; + syscall_lower_eip_bound = eip_lower_bound; + syscall_upper_eip_bound = eip_upper_bound; + +return 0 ; +} + +/** +* ltt_get_trace_config: - Get the tracing configuration +* @do_syscall_depth: Use depth to fetch eip +* @do_syscall_bounds: Use bounds to fetch eip +* @eip_depth: Detph to fetch eip +* @eip_lower_bound: Lower bound eip address +* @eip_upper_bound: Upper bound eip address +* +* Returns: +* 0, all is OK +* -ENOMEDIUM, there isn't a registered tracer +*/ +int ltt_get_trace_config (int *do_syscall_depth, +int *do_syscall_bounds, +int *eip_depth, +void **eip_lower_bound, +void **eip_upper_bound) +{ + *do_syscall_depth = fetch_syscall_eip_use_depth; + *do_syscall_bounds = fetch_syscall_eip_use_bounds; + *eip_depth = syscall_eip_depth; + *eip_lower_bound = syscall_lower_eip_bound; + *eip_upper_bound = syscall_upper_eip_bound; + +return 0 ; +} + +/** +* ltt_set_flight_recorder_config: - set flight recorder defaults +* @trace: the trace struct +*/ +void ltt_set_flight_recorder_config (struct ltt_trace_struct *trace) +{ + trace->traced_events =0 ; + trace->log_event_details_mask =0 ; + +ltt_set_bit (LTT_EV_BUFFER_START , &trace->traced_events); +ltt_set_bit (LTT_EV_BUFFER_START , &trace->log_event_details_mask); +ltt_set_bit (LTT_EV_START , &trace->traced_events); +ltt_set_bit (LTT_EV_START , &trace->log_event_details_mask); +ltt_set_bit (LTT_EV_CHANGE_MASK , &trace->traced_events); +ltt_set_bit (LTT_EV_CHANGE_MASK , &trace->log_event_details_mask); + +ltt_set_bit (LTT_EV_SYSCALL_ENTRY , &trace->traced_events); +ltt_set_bit (LTT_EV_SYSCALL_ENTRY , &trace->log_event_details_mask); +ltt_set_bit (LTT_EV_SYSCALL_EXIT , &trace->traced_events); +ltt_set_bit (LTT_EV_SYSCALL_EXIT , &trace->log_event_details_mask); +ltt_set_bit (LTT_EV_TRAP_ENTRY , &trace->traced_events); +ltt_set_bit (LTT_EV_TRAP_ENTRY , &trace->log_event_details_mask); +ltt_set_bit (LTT_EV_TRAP_EXIT , &trace->traced_events); +ltt_set_bit (LTT_EV_TRAP_EXIT , &trace->log_event_details_mask); +ltt_set_bit (LTT_EV_IRQ_ENTRY , &trace->traced_events); +ltt_set_bit (LTT_EV_IRQ_ENTRY , &trace->log_event_details_mask); +ltt_set_bit (LTT_EV_IRQ_EXIT , &trace->traced_events); +ltt_set_bit (LTT_EV_IRQ_EXIT , &trace->log_event_details_mask); +ltt_set_bit (LTT_EV_SCHEDCHANGE , &trace->traced_events); +ltt_set_bit (LTT_EV_SCHEDCHANGE , &trace->log_event_details_mask); + +ltt_set_bit (LTT_EV_KERNEL_TIMER , &trace->traced_events); +ltt_set_bit (LTT_EV_KERNEL_TIMER , &trace->log_event_details_mask); +ltt_set_bit (LTT_EV_SOFT_IRQ , &trace->traced_events); +ltt_set_bit (LTT_EV_SOFT_IRQ , &trace->log_event_details_mask); +ltt_set_bit (LTT_EV_PROCESS , &trace->traced_events); +ltt_set_bit (LTT_EV_PROCESS , &trace->log_event_details_mask); +} + +/** +* ltt_get_status: - fill in a status struct covering all traces +* @tracer_status: the tracer_status struct +*/ +int ltt_get_status (struct ltt_tracer_status *tracer_status) +{ +int i, j, rchan_handle, retval =0 ; +struct ltt_trace_struct *trace; +struct ltt_trace_info *info; +struct rchan_info rchan_info; + + tracer_status->num_cpus = num_cpus; + +for (i =0 ; i <NR_TRACES ; i++) { + trace = ¤t_traces[i]; + info = &tracer_status->traces[i]; + info->active = trace->active && trace->tracer_started ?1 :0 ; +if (!info->active) +continue ; + info->trace_handle = trace->trace_handle; + info->paused = trace->paused; + info->flight_recorder = trace->flight_recorder; + info->use_locking = trace->use_locking; + info->using_tsc = trace->using_tsc; + info->n_buffers = trace->n_buffers; + info->buf_size = trace->buf_size; + info->traced_events = trace->traced_events; + info->log_event_details_mask = trace->log_event_details_mask; +for (j =0 ; j < num_cpus; j++) { + rchan_handle =trace_channel_handle (trace->trace_handle, j); + retval =relay_info (rchan_handle, &rchan_info); +if (retval) +return retval; + info->buffers_produced[j] = rchan_info.bufs_produced; + } + } + +return retval; +} + +/* +* Custom Events +*/ + +/** +* init_custom_events: - Initialize custom events +*/ +static inline void init_custom_events (void ) +{ + custom_events = &custom_events_head; + custom_events->next = custom_events; + custom_events->prev = custom_events; +} + +/** +* _ltt_create_event: - Create a new traceable event type +* @event_type: string describing event type +* @event_desc: string used for standard formatting +* @format_type: type of formatting used to log event data +* @format_data: data specific to format +* @owner_pid: PID of event's owner (0 if none) +* +* Returns: +* New Event ID if all is OK +* -ENOMEM, Unable to allocate new event +*/ +int _ltt_create_event (char *event_type, +char *event_desc, +int format_type, +char *format_data, +pid_t owner_pid) +{ +ltt_new_event *new_event; +struct custom_event_desc *new_event_desc; + +if ((new_event_desc = (struct custom_event_desc *)kmalloc (sizeof (struct custom_event_desc ),GFP_ATOMIC )) ==NULL ) +return -ENOMEM ; + + new_event = &(new_event_desc->event); + new_event->type[0 ] ='\0' ; + new_event->desc[0 ] ='\0' ; + new_event->form[0 ] ='\0' ; + +if (event_type !=NULL ) +strncpy (new_event->type, event_type,LTT_CUSTOM_EV_TYPE_STR_LEN ); +if (event_desc !=NULL ) +strncpy (new_event->desc, event_desc,LTT_CUSTOM_EV_DESC_STR_LEN ); +if (format_data !=NULL ) +strncpy (new_event->form, format_data,LTT_CUSTOM_EV_FORM_STR_LEN ); + + new_event->type[LTT_CUSTOM_EV_TYPE_STR_LEN -1 ] ='\0' ; + new_event->desc[LTT_CUSTOM_EV_DESC_STR_LEN -1 ] ='\0' ; + new_event->form[LTT_CUSTOM_EV_FORM_STR_LEN -1 ] ='\0' ; + + new_event->format_type = format_type; + new_event->id = next_event_id; + + next_event_id++; + + new_event_desc->owner_pid = owner_pid; + +write_lock (&custom_list_lock); + +if (custom_events ==NULL ) +init_custom_events (); + + new_event_desc->next = custom_events; + new_event_desc->prev = custom_events->prev; + custom_events->prev->next = new_event_desc; + custom_events->prev = new_event_desc; +write_unlock (&custom_list_lock); + +ltt_log_event (LTT_EV_NEW_EVENT , &(new_event_desc->event)); + +return new_event->id; +} + +int ltt_create_event (char *event_type, +char *event_desc, +int format_type, +char *format_data) +{ +return _ltt_create_event (event_type, event_desc, format_type, format_data,0 ); +} + +int ltt_create_owned_event (char *event_type, +char *event_desc, +int format_type, +char *format_data, +pid_t owner_pid) +{ +return _ltt_create_event (event_type, event_desc, format_type, format_data, owner_pid); +} + +/** +* ltt_destroy_event: - Destroy a created event type +* @event_id, the Id returned by ltt_create_event() +*/ +void ltt_destroy_event (int event_id) +{ +struct custom_event_desc *event_desc; + +write_lock (&custom_list_lock); + +if (custom_events ==NULL ) +init_custom_events (); + +for (event_desc = custom_events->next; + event_desc != custom_events; + event_desc = event_desc->next) +if (event_desc->event.id == event_id) +break ; + +if (event_desc != custom_events) { + event_desc->next->prev = event_desc->prev; + event_desc->prev->next = event_desc->next; +kfree (event_desc); + } + +write_unlock (&custom_list_lock); +} + +/** +* ltt_destroy_owners_events: Destroy an owner's events +* @owner_pid: the PID of the owner who's events are to be deleted. +*/ +void ltt_destroy_owners_events (pid_t owner_pid) +{ +struct custom_event_desc *temp_event; +struct custom_event_desc *event_desc; + +write_lock (&custom_list_lock); + +if (custom_events ==NULL ) +init_custom_events (); + + event_desc = custom_events->next; + +while (event_desc != custom_events) { + temp_event = event_desc->next; +if (event_desc->owner_pid == owner_pid) { + event_desc->next->prev = event_desc->prev; + event_desc->prev->next = event_desc->next; +kfree (event_desc); + } + event_desc = temp_event; + } + +write_unlock (&custom_list_lock); +} + +/** +* ltt_reregister_custom_events: - Relogs event creations. +*/ +void ltt_reregister_custom_events (void ) +{ +struct custom_event_desc *event_desc; + +read_lock (&custom_list_lock); + +if (custom_events ==NULL ) +init_custom_events (); + +for (event_desc = custom_events->next; + event_desc != custom_events; + event_desc = event_desc->next) +ltt_log_event (LTT_EV_NEW_EVENT , &(event_desc->event)); + +read_unlock (&custom_list_lock); +} + +/* +* Event logging primitives +*/ + +/** +* _ltt_log_event: - Tracing function per se. +* @trace: the trace instance +* @event_id: ID of event as defined in linux/ltt.h +* @event_struct: struct describing the event +* @cpu_id: the CPU associated with the event +* +* Returns: +* 0, if everything went OK (event got registered) +* -ENODEV, no tracing daemon opened the driver. +* -ENOMEM, no more memory to store events. +* -EBUSY, tracer not started yet. +*/ +int _ltt_log_event (struct ltt_trace_struct *trace, +u8 event_id, +void *event_struct, +u8 cpu_id) +{ +int var_data_len =0 ; +void *var_data_beg =NULL ; +uint16_t data_size; +struct task_struct *incoming_process =NULL ; +unsigned long flags; +char * reserved; +int bytes_written =0 ; +int reserve_code, interrupting; +struct timeval time_stamp; +u32 time_delta; +int channel_handle; +struct rchan *rchan; +unsigned int tracer_handle; + +if (!trace) +return -ENOMEDIUM ; + +if (trace->paused) +return -EBUSY ; + + tracer_handle = trace->trace_handle; + +if (!trace->flight_recorder && (trace->daemon_task_struct ==NULL )) +return -ENODEV ; + + channel_handle =trace_channel_handle (tracer_handle, cpu_id); + +if ((trace->tracer_started ==1 ) || (event_id ==LTT_EV_START ) || (event_id ==LTT_EV_BUFFER_START )) + goto trace_event; + +return -EBUSY ; + +trace_event: +if (!ltt_test_bit (event_id, &trace->traced_events)) +return 0 ; + +if ((event_id !=LTT_EV_START ) && (event_id !=LTT_EV_BUFFER_START )) { +if (event_id ==LTT_EV_SCHEDCHANGE ) + incoming_process = (struct task_struct *) (((ltt_schedchange *) event_struct)->in); +if ((trace->tracing_pid ==1 ) && (current->pid != trace->traced_pid)) { +if (incoming_process ==NULL ) +return 0 ; +else if (incoming_process->pid != trace->traced_pid) +return 0 ; + } +if ((trace->tracing_pgrp ==1 ) && (process_group (current) != trace->traced_pgrp)) { +if (incoming_process ==NULL ) +return 0 ; +else if (process_group (incoming_process) != trace->traced_pgrp) +return 0 ; + } +if ((trace->tracing_gid ==1 ) && (current->egid != trace->traced_gid)) { +if (incoming_process ==NULL ) +return 0 ; +else if (incoming_process->egid != trace->traced_gid) +return 0 ; + } +if ((trace->tracing_uid ==1 ) && (current->euid != trace->traced_uid)) { +if (incoming_process ==NULL ) +return 0 ; +else if (incoming_process->euid != trace->traced_uid) +return 0 ; + } +if (event_id ==LTT_EV_SCHEDCHANGE ) + (((ltt_schedchange *) event_struct)->in) = incoming_process->pid; + } + + data_size =sizeof (event_id) +sizeof (time_delta) +sizeof (data_size); + +if (ltt_test_bit (event_id, &trace->log_event_details_mask)) { + data_size += event_struct_size[event_id]; +switch (event_id) { +case LTT_EV_FILE_SYSTEM : +if ((((ltt_file_system *) event_struct)->event_sub_id ==LTT_EV_FILE_SYSTEM_EXEC ) + || (((ltt_file_system *) event_struct)->event_sub_id ==LTT_EV_FILE_SYSTEM_OPEN )) { + var_data_beg = ((ltt_file_system *) event_struct)->file_name; + var_data_len = ((ltt_file_system *) event_struct)->event_data2 +1 ; + data_size += (uint16_t ) var_data_len; + } +break ; +case LTT_EV_CUSTOM : + var_data_beg = ((ltt_custom *) event_struct)->data; + var_data_len = ((ltt_custom *) event_struct)->data_size; + data_size += (uint16_t ) var_data_len; +break ; + } + } + +if ((trace->log_cpuid ==1 ) && (event_id !=LTT_EV_START ) && (event_id !=LTT_EV_BUFFER_START )) + data_size +=sizeof (cpu_id); + + rchan =rchan_get (channel_handle); +if (rchan ==NULL ) +return -ENODEV ; + +relay_lock_channel (rchan, flags);/* nop for lockless */ + reserved =relay_reserve (rchan, data_size, &time_stamp, &time_delta, &reserve_code, &interrupting); + +if (reserve_code &RELAY_WRITE_DISCARD ) { +events_lost (trace->trace_handle, cpu_id)++; + bytes_written =0 ; + goto check_buffer_switch; + } + +if ((trace->log_cpuid ==1 ) && (event_id !=LTT_EV_START ) + && (event_id !=LTT_EV_BUFFER_START )) +relay_write_direct (reserved, + &cpu_id, +sizeof (cpu_id)); + +relay_write_direct (reserved, + &event_id, +sizeof (event_id)); + +relay_write_direct (reserved, + &time_delta, +sizeof (time_delta)); + +if (ltt_test_bit (event_id, &trace->log_event_details_mask)) { +relay_write_direct (reserved, + event_struct, + event_struct_size[event_id]); +if (var_data_len) +relay_write_direct (reserved, + var_data_beg, + var_data_len); + } + +relay_write_direct (reserved, + &data_size, +sizeof (data_size)); + + bytes_written = data_size; + +check_buffer_switch: +if ((event_id ==LTT_EV_SCHEDCHANGE ) && (tracer_handle ==TRACE_HANDLE ) && current_traces[FLIGHT_HANDLE ].active) + (((ltt_schedchange *) event_struct)->in) = (u32 )incoming_process; + +/* We need to commit even if we didn't write anything because +that's how the deliver callback is invoked. */ +relay_commit (rchan, reserved, bytes_written, reserve_code, interrupting); + +relay_unlock_channel (rchan, flags); +rchan_put (rchan); + +return 0 ; +} + +/** +* ltt_log_event: - Trace an event +* @event_id, the event's ID (check out ltt.h) +* @event_struct, the structure describing the event +* +* Returns: +* Trace fct return code if OK. +* -ENOMEDIUM, there is no registered tracer +* -ENOMEM, couldn't access ltt_info +*/ +int ltt_log_event (u8 event_id, +void *event_struct) +{ +int i; +static int err[NR_TRACES ]; +struct ltt_trace_struct *trace; +u32 cpu =smp_processor_id (); + +for (i =0 ; i <NR_TRACES ; i++) { + trace = current_traces[i].active; + err[i] =_ltt_log_event (trace, + event_id, + event_struct, + cpu); + } + +return err[0 ] == -ENOMEDIUM ? err[1 ] : err[0 ]; +} + +/** +* ltt_log_std_formatted_event: - Trace a formatted event +* @event_id: the event Id provided upon creation +* @...: printf-like data that will be used to fill the event string. +* +* Returns: +* Trace fct return code if OK. +* -ENOMEDIUM, there is no registered tracer or event doesn't exist. +*/ +int ltt_log_std_formatted_event (int event_id,...) +{ +int string_size; +char final_string[LTT_CUSTOM_EV_FINAL_STR_LEN ]; +va_list vararg_list; +ltt_custom custom_event; +struct custom_event_desc *event_desc; + +read_lock (&custom_list_lock); + +if (custom_events ==NULL ) +init_custom_events (); + +for (event_desc = custom_events->next; + event_desc != custom_events; + event_desc = event_desc->next) +if (event_desc->event.id == event_id) +break ; + +if (event_desc == custom_events) { +read_unlock (&custom_list_lock); +return -ENOMEDIUM ; + } + + custom_event.id = event_id; + +va_start (vararg_list, event_id); + string_size =vsprintf (final_string, event_desc->event.desc, vararg_list); +read_unlock (&custom_list_lock); +va_end (vararg_list); + + custom_event.data_size = (u32) (string_size +1 ); + custom_event.data = final_string; + +return ltt_log_event (LTT_EV_CUSTOM , &custom_event); +} + +/** +* ltt_log_raw_event: - Trace a raw event +* @event_id, the event Id provided upon creation +* @event_size, the size of the data provided +* @event_data, data buffer describing event +* +* Returns: +* Trace fct return code if OK. +* -ENOMEDIUM, there is no registered tracer or event doesn't exist. +*/ +int ltt_log_raw_event (int event_id,int event_size,void *event_data) +{ +ltt_custom custom_event; +struct custom_event_desc *event_desc; + +read_lock (&custom_list_lock); + +if (custom_events ==NULL ) +init_custom_events (); + +for (event_desc = custom_events->next; + event_desc != custom_events; + event_desc = event_desc->next) +if (event_desc->event.id == event_id) +break ; + +read_unlock (&custom_list_lock); + +if (event_desc == custom_events) +return -ENOMEDIUM ; + + custom_event.id = event_id; + +if (event_size <=LTT_CUSTOM_EV_MAX_SIZE ) + custom_event.data_size = (u32 ) event_size; +else + custom_event.data_size = (u32 )LTT_CUSTOM_EV_MAX_SIZE ; + + custom_event.data = event_data; + +return ltt_log_event (LTT_EV_CUSTOM , &custom_event); +} + +/* +* Relayfs callback implementations. +*/ + +/** +* _ltt_channel_cpuid: - Get CPU id given channel handle, for given trace. +* @tracer_handle: trace handle. +* @channel_handle: relay channel handle. +* +* Returns: +* +* CPU id +* -1, channel_handle, thus CPU id, not found +*/ +static int _ltt_channel_cpuid (int tracer_handle,int channel_handle) +{ +int i; + +for (i =0 ; i < num_cpus; i++) +if (trace_channel_handle (tracer_handle, i) == channel_handle) +return i; + +return -1 ; +} + +/** +* ltt_channel_cpuid: - Get CPU id given channel handle. +* @channel_handle: relay channel handle. +* +* Returns: +* +* CPU id +* -1, channel_handle, thus CPU id, not found +*/ +static int ltt_channel_cpuid (int channel_handle) +{ +int i, cpuid; + +for (i =0 ; i <NR_TRACES ; i++) { + cpuid =_ltt_channel_cpuid (i, channel_handle); +if (cpuid !=-1 ) +return cpuid; + } + +return -1 ; +} + +/** +* ltt_channel_trace: - Get trace struct given channel handle. +* @channel_handle: relay channel handle. +* +* Returns: +* +* trace struct * +* NULL, channel_handle, thus trace_struct *, not found +*/ +static struct ltt_trace_struct *ltt_channel_trace (int channel_handle) +{ +int i; + +for (i =0 ; i <NR_TRACES ; i++) { +if (_ltt_channel_cpuid (i, channel_handle) !=-1 ) +return ¤t_traces[i]; + } + +return NULL ; +} + +/** +* ltt_channel_trace_handle: - Get trace handle given channel handle. +* @channel_handle: relay channel handle. +* +* Returns: +* +* trace handle +* -1, channel_handle, thus trace handle, not found +*/ +static int ltt_channel_trace_handle (int channel_handle) +{ +unsigned int i; + +for (i =0 ; i <NR_TRACES ; i++) +if (_ltt_channel_cpuid (i, channel_handle) !=-1 ) +return i; + +return -1 ; +} + +/** +* write_start_event: - Initialize a trace session for a given CPU. +* @cpu_id: the CPU id to initialize a trace for +*/ +static inline int write_start_event (struct ltt_trace_struct *trace, +int channel, +char * current_write_pos, +u32 start_tsc, +int using_tsc) +{ +struct rchan_info channel_info; +u32 time_delta; +ltt_trace_start start_event; +u8 event_id; +uint16_t data_size; + +relay_info (channel, &channel_info); + + start_event.magic_number =LTT_TRACER_MAGIC_NUMBER ; + start_event.arch_type =LTT_ARCH_TYPE ; + start_event.arch_variant =LTT_ARCH_VARIANT ; + start_event.system_type =LTT_SYS_TYPE_VANILLA_LINUX ; + start_event.major_version =LTT_TRACER_VERSION_MAJOR ; + start_event.minor_version =LTT_TRACER_VERSION_MINOR ; + start_event.buffer_size = channel_info.buf_size; + start_event.event_mask = trace->traced_events; + start_event.details_mask = trace->log_event_details_mask; + start_event.log_cpuid = trace->log_cpuid; + start_event.use_tsc = trace->using_tsc; + start_event.flight_recorder = trace->flight_recorder; + + event_id =LTT_EV_START ; +relay_write_direct (current_write_pos, + &event_id, +sizeof (event_id)); + + time_delta =switch_time_delta (start_tsc, using_tsc); +relay_write_direct (current_write_pos, + &time_delta, +sizeof (time_delta)); + +relay_write_direct (current_write_pos, + &start_event, +sizeof (ltt_trace_start)); + + data_size =sizeof (event_id) + +sizeof (time_delta) + +sizeof (ltt_trace_start) + +sizeof (data_size); + +relay_write_direct (current_write_pos, + &data_size, +sizeof (data_size)); + +if (trace->trace_start_data) +memcpy (trace->trace_start_data, &start_event,sizeof (start_event)); + +return (int )data_size; +} + +/** +* buffer_start_callback: - Write start-buffer event to start of buffer. +* @channel_handle: the channel id +* @current_write_pos: position in sub-buffer client should write to +* @buffer_id: the id of the new sub-buffer +* @start_time: the timestamp associated with the start of sub-buffer +* @start_tsc: the TSC associated with the timestamp, if using_tsc +* @using_tsc: boolean, indicates whether start_tsc is valid +* +* This is the relayfs buffer_start() callback implementation for +* the tracer. We write the start event directly to the address +* contained in the current_write_pos param. If this is the first +* sub-buffer, we also write the start event. Of course we reserved +* the number of bytes we're writing when we opened the channel, which +* is the number we return. +*/ +static int buffer_start_callback (int channel_handle, +char * current_write_pos, +u32 buffer_id, +struct timeval start_time, +u32 start_tsc, +int using_tsc) +{ +ltt_buffer_start start_buffer_event; +u8 event_id; +u32 time_delta; +uint16_t data_size; +struct ltt_trace_struct *trace =ltt_channel_trace (channel_handle); + +if (!trace) +return 0 ; + + start_buffer_event.id = buffer_id; + start_buffer_event.time = start_time; + start_buffer_event.tsc = start_tsc; + + event_id =LTT_EV_BUFFER_START ; +relay_write_direct (current_write_pos, + &event_id, +sizeof (event_id)); + + time_delta =switch_time_delta (start_tsc, using_tsc); +relay_write_direct (current_write_pos, + &time_delta, +sizeof (time_delta)); + +relay_write_direct (current_write_pos, + &start_buffer_event, +sizeof (start_buffer_event)); + + data_size =sizeof (event_id) + +sizeof (time_delta) + +sizeof (start_buffer_event) + +sizeof (data_size); + +relay_write_direct (current_write_pos, + &data_size, +sizeof (data_size)); + +if (buffer_id ==0 )/* first buffer */ + data_size +=write_start_event (trace, channel_handle, current_write_pos, start_tsc, using_tsc); + +return (int )data_size; +} + +/** +* buffer_end_callback - called at the end of a sub-buffer +* @channel_handle: the channel id +* @current_write_pos: position in sub-buffer of end of data +* @end_of_buffer: the position of the end of the sub-buffer +* @end_time: the timestamp associated with the end of the sub-buffer +* @end_tsc: the TSC associated with the end_time, if using_tsc +* @using_tsc: boolean, indicates whether end_tsc is valid +* +* This is the relayfs buffer_end() callback implementation for +* the tracer. We write the end event directly to the address +* contained in the current_write_pos param. We also calculate +* the 'size_lost' or unused bytes at the end of the sub-buffer +* and write that value to the very end of the sub-buffer for +* post-processing. Of course we reserved the number of bytes +* we're writing when we opened the channel, which is the number +* we return. +*/ +static int buffer_end_callback (int channel_handle, +char * current_write_pos, +char * end_of_buffer, +struct timeval end_time, +u32 end_tsc, +int using_tsc) +{ +ltt_buffer_end end_buffer_event; +u8 event_id; +u32 time_delta; +char * init_write_pos = current_write_pos; +uint16_t data_size; +u32 size_lost; +u8 cpu_id; +struct ltt_trace_struct *trace; + + end_buffer_event.time = end_time; + end_buffer_event.tsc = end_tsc; + + cpu_id = (u8 )ltt_channel_cpuid (channel_handle); + trace =ltt_channel_trace (channel_handle); +if (!trace) +return 0 ; + +if (trace->log_cpuid ==1 ) +relay_write_direct (current_write_pos, + &cpu_id, +sizeof (cpu_id)); + + event_id =LTT_EV_BUFFER_END ; +relay_write_direct (current_write_pos, + &event_id, +sizeof (event_id)); + + time_delta =switch_time_delta (end_tsc, using_tsc); +relay_write_direct (current_write_pos, + &time_delta, +sizeof (time_delta)); + +relay_write_direct (current_write_pos, + &end_buffer_event, +sizeof (end_buffer_event)); + + data_size =sizeof (event_id) + +sizeof (time_delta) + +sizeof (end_buffer_event) + +sizeof (data_size); + +relay_write_direct (current_write_pos, + &data_size, +sizeof (data_size)); + +/* size lost includes size of end buffer event */ + size_lost = end_of_buffer - init_write_pos; + *((u32 *) (end_of_buffer -sizeof (size_lost))) = size_lost; + +return (int )data_size; +} + +/** +* deliver_callback - called when data is ready for the tracer +* @channel_handle: the channel id +* @from: the start of the delivered data +* @len: the length of the delivered data +* +* This is the relayfs deliver() callback implementation for +* the tracer. We simply set the send_signal flag, which will +* be checked when the current write is finished, at which +* point the daemon will be signaled to read the buffer. +*/ +void deliver_callback (int channel_handle, +char * from, +u32 len) +{ +struct ltt_trace_struct *trace; +int cpu_id; + + trace =ltt_channel_trace (channel_handle); +if (!trace) +return ; + + cpu_id =ltt_channel_cpuid (channel_handle); +if (cpu_id ==-1 ) +return ; + +set_bit (cpu_id, &trace->buffer_switches_pending); +} + +/** +* fileop_notify - called when change to trace file status +* @rchan_id: the rchan id +* @filp: the file +* @fileop: the file operation +* +* This is the relayfs fileop_notify() callback implementation for +* the tracer. We use it to take care of trace file mapping and +* unmapping tasks. +*/ +static int fileop_notify (int rchan_id, +struct file *filp, +enum relay_fileop fileop) +{ +struct rchan_reader *map_reader; +struct rchan_reader *open_file_reader; +struct rchan *rchan; +u8 cpu_id; +int trace_handle; + + trace_handle =ltt_channel_trace_handle (rchan_id); +if (trace_handle ==-1 ) +return 0 ; + +if (fileop ==RELAY_FILE_MAP ) { + cpu_id = (u8 )ltt_channel_cpuid (rchan_id); + open_file_reader = (struct rchan_reader *)filp->private_data; + rchan = open_file_reader->rchan; +if (atomic_read (&rchan->mapped)) +return -EBUSY ; + map_reader =add_map_reader (rchan_id); +trace_channel_reader (trace_handle, cpu_id) = map_reader; + } +else if (fileop ==RELAY_FILE_UNMAP ) { + cpu_id = (u8 )ltt_channel_cpuid (rchan_id); +remove_map_reader (trace_channel_reader (trace_handle, cpu_id)); +trace_channel_reader (trace_handle, cpu_id) =NULL ; + } + +return 0 ; +} + +static struct rchan_callbacks ltt_callbacks = { + .buffer_start = buffer_start_callback, + .buffer_end = buffer_end_callback, + .deliver = deliver_callback, + .fileop_notify = fileop_notify, + .ioctl = ltt_ioctl, +}; + +/* +* Procfs kernel-user interface +*/ + +/** +* proc_read_relayfs_path - procfs read callback for relayfs_path attr +*/ +static int proc_read_relayfs_path (char *page,char **start,off_t off, +int count,int *eof,void *data) +{ +return sprintf (page,"%s" , relayfs_path); +} + +/** +* proc_write_relayfs_path - procfs write callback for relayfs_path attr +* +* Sets the path to the trace files within relayfs for the current trace. +*/ +static int proc_write_relayfs_path (struct file *filp,const char *buffer, +unsigned long count,void *data) +{ +unsigned long len; + +if (count >PATH_MAX ) + len =PATH_MAX ; +else + len = count; + +if (copy_from_user (relayfs_path, buffer, len)) +return -EFAULT ; + +if (len !=PATH_MAX ) + relayfs_path[len] ='\0' ; + +return len; +} + +/** +* populate_handle_proc_dir - populate proc dir with trace attributes +* @trace_handle: the trace handle for this trace run +* @handle_dir: the directory to populate +* +* This function populates the handle dir with attribute files. +* +* Returns 0 if successful, negative if not. +*/ +static int populate_handle_proc_dir (unsigned int trace_handle, +struct proc_dir_entry *handle_dir) +{ +struct proc_dir_entry * file_entry; +int err =0 ; + + file_entry =create_proc_entry ("relayfs_path" ,0666 , handle_dir); + +if (file_entry ==NULL ) { + err = -ENOMEM ; +return err; + } + + file_entry->read_proc = proc_read_relayfs_path; + file_entry->write_proc = proc_write_relayfs_path; + file_entry->data = (void *)trace_handle; + file_entry->owner =THIS_MODULE ; + +return err; +} + +/** +* create_handle_proc_dir - create proc dir for trace attributes +* @trace_handle: the trace handle for this trace run +* +* This function creates a proc dir to communicate trace attribute +* values between the daemon and the tracer. It also populates the +* new dir with the attribute files. +* +* Retruns the proc dir entry if successful, NULL otherwise. +*/ +static struct proc_dir_entry *create_handle_proc_dir (unsigned int trace_handle) +{ +char handle_dir_name[22 ]; +struct proc_dir_entry *handle_dir; + +sprintf (handle_dir_name,"%u" , trace_handle); + + handle_dir =proc_mkdir (handle_dir_name, ltt_proc_root_entry); + +if (handle_dir ==NULL ) +return NULL ; +else + handle_dir->owner =THIS_MODULE ; + +if (populate_handle_proc_dir (trace_handle, handle_dir)) { +remove_proc_entry (handle_dir_name, ltt_proc_root_entry); + handle_dir =NULL ; + } + +return handle_dir; +} + +/** +* depopulate_handle_proc_dir - remove proc dir entries for handle_dir +* @handle_dir: the directory to depopulate +* +* This function removes the attribute files from the handle dir. +*/ +static void depopulate_handle_proc_dir (struct proc_dir_entry *handle_dir) +{ +remove_proc_entry ("relayfs_path" , handle_dir); +} + +/** +* remove_handle_proc_dir - remove proc dir for trace attributes +* @handle_dir: the directory +* @trace_handle: the trace handle for this trace run +* +* This function removes a trace handle's proc dir. It first +* depopulates the dir of attribute files. +*/ +static void remove_handle_proc_dir (struct proc_dir_entry *handle_dir, +unsigned int trace_handle) +{ +char handle_dir_name[22 ]; + +depopulate_handle_proc_dir (handle_dir); + +sprintf (handle_dir_name,"%u" , trace_handle); +remove_proc_entry (handle_dir_name, ltt_proc_root_entry); +} + + +/* +* Initialization and finalization +*/ + +static struct rchan_callbacks control_callbacks = { + .ioctl = ltt_ioctl, +}; + +/** +* create_control_channel - creates channel /mnt/relay/ltt/control +* +* Returns channel id on success, negative otherwise. +*/ +static int create_control_channel (void ) +{ +u32 bufsize, nbufs; +u32 channel_flags; +int control; + +sprintf (relay_file_name,"%s/%s" ,LTT_RELAYFS_ROOT ,LTT_CONTROL_FILE ); + + channel_flags =RELAY_DELIVERY_PACKET |RELAY_USAGE_GLOBAL ; + channel_flags |=RELAY_SCHEME_ANY |RELAY_TIMESTAMP_ANY ; + + bufsize =4096 ; + nbufs =4 ; + + control =relay_open (relay_file_name, + bufsize, + nbufs, + channel_flags, + &control_callbacks, +0 , +0 , +0 , +0 , +0 , +0 , +NULL , +0 ); + +return control; +} + +/** +* proc_read_init_ltt - procfs read callback for init attr +*/ +static int proc_read_init_ltt (char *page,char **start,off_t off, +int count,int *eof,void *data) +{ +return sprintf (page,"%d" , control_channel ==-1 ?0 :1 ); +} + +/** +* proc_write_init_ltt - procfs write callback for init attr +*/ +static int proc_write_init_ltt (struct file *filp,const char *buffer, +unsigned long count,void *data) +{ +if (control_channel ==-1 ) { + control_channel =create_control_channel (); + +if (control_channel <0 ) +printk ("LTT control channel creation failed, errcode: %d\n" , control_channel); +else +printk ("LTT control channel created\n" ); + } + +return 1 ; +} + +/** +* remove_control_channel - destroys channel /mnt/relay/ltt/control +* +* Returns 0, negative otherwise. +*/ +static int remove_control_channel (void ) +{ +if (control_channel !=-1 ) +return relay_close (control_channel); + +return -ENODEV ; +} + +static int __init init_ltt (void ) +{ +int i; +int err =0 ; +struct proc_dir_entry *init_entry; + + ltt_proc_root_entry =proc_mkdir ("ltt" ,NULL ); + +if (ltt_proc_root_entry ==NULL ) + err = -ENOMEM ; +else + ltt_proc_root_entry->owner =THIS_MODULE ; + + control_channel =-1 ; + + init_entry =create_proc_entry ("init" ,0666 , ltt_proc_root_entry); +if (init_entry ==NULL ) { + err = -ENOMEM ; +return err; + } + + init_entry->read_proc = proc_read_init_ltt; + init_entry->write_proc = proc_write_init_ltt; + init_entry->owner =THIS_MODULE ; + +for (i =0 ; i <NR_TRACES ; i++) +init_trace (¤t_traces[i]); + +return err; +} + +static void __exit exit_ltt(void) +{ +remove_proc_entry ("init" , ltt_proc_root_entry); +remove_proc_entry ("ltt" ,NULL ); + +remove_control_channel (); +} + +module_init(init_ltt) +module_exit(exit_ltt) + +EXPORT_SYMBOL(ltt_set_trace_config); +EXPORT_SYMBOL(ltt_get_trace_config); +EXPORT_SYMBOL(ltt_create_event); +EXPORT_SYMBOL(ltt_create_owned_event); +EXPORT_SYMBOL(ltt_destroy_event); +EXPORT_SYMBOL(ltt_destroy_owners_events); +EXPORT_SYMBOL(ltt_log_std_formatted_event); +EXPORT_SYMBOL(ltt_log_raw_event); +EXPORT_SYMBOL(ltt_log_event); +EXPORT_SYMBOL(syscall_entry_trace_active); +EXPORT_SYMBOL(syscall_exit_trace_active); +EXPORT_SYMBOL(ltt_flight_pause); +EXPORT_SYMBOL(ltt_flight_unpause); + +MODULE_AUTHOR("Karim Yaghmour, Tom Zanussi, Bob Wisniewski") +MODULE_DESCRIPTION("Linux Trace Toolkit kernel core") +MODULE_LICENSE ("GPL" );