www.pudn.com > Linux2410_kernel.rar > trace.c


/*
 * linux/kernel/trace.c
 *
 * (C) Copyright 1999, 2000, 2001, 2002 - Karim Yaghmour (karym@opersys.com)
 *
 * This code is distributed under the GPL license
 *
 * Tracing management
 *
 */

#include 
#include      /* For __init */
#include     /* Tracing definitions */
#include     /* Miscellaneous error codes */
#include    /* NULL */
#include      /* kmalloc() */
#include    /* EXPORT_SYMBOL */
#include     /* pid_t */
#ifdef CONFIG_ARM
#include 
#include 
#include 
#else
#include       /* irq_desc */
#endif

/* Local variables */
static int        tracer_registered = 0;   /* Is there a tracer registered */
struct tracer *   tracer = NULL;           /* The registered tracer */

/* Registration lock */
rwlock_t tracer_register_lock = RW_LOCK_UNLOCKED;

/* Trace callback table entry */
struct trace_callback_table_entry
{
  tracer_call                         callback;    /* The callback function */

  struct trace_callback_table_entry*  next;        /* Next entry */
};

/* Trace callback table */
struct trace_callback_table_entry trace_callback_table[TRACE_EV_MAX];

/* Custom event description */
struct custom_event_desc
{
  /* The event itself */
  trace_new_event              event;

  /* PID of event owner, if any */
  pid_t                        owner_pid;

  /* List links */
  struct custom_event_desc*    next;
  struct custom_event_desc*    prev;
};

/* Next event ID to be used */
int next_event_id;

/* Circular list of custom events */
struct custom_event_desc  custom_events_head;
struct custom_event_desc* custom_events;

/* Circular list lock */
rwlock_t custom_list_lock = RW_LOCK_UNLOCKED;

/****************************************************
 * Register the tracer to the kernel
 * Return values :
 *   0, all is OK
 *   -EBUSY, there already is a registered tracer
 *   -ENOMEM, couldn't allocate memory
 ****************************************************/
int register_tracer(tracer_call pm_trace_function)
{
  unsigned long    l_flags;   /* Flags for irqsave */

  /* Is there a tracer already registered */
  if(tracer_registered == 1)
    return -EBUSY;

  /* Allocate memory for the tracer */
  if((tracer = (struct tracer *) kmalloc(sizeof(struct tracer), GFP_ATOMIC)) == NULL)
    /* We couldn't allocate any memory */
    return -ENOMEM;

  /* Lock registration variables */
  write_lock_irqsave(&tracer_register_lock, l_flags);

  /* There is a tracer registered */
  tracer_registered = 1;

  /* Set the tracer to the one being passed by the caller */
  tracer->trace = pm_trace_function;

  /* Unlock registration variables */
  write_unlock_irqrestore(&tracer_register_lock, l_flags);

  /* Initialize the tracer settings */
  tracer->fetch_syscall_eip_use_bounds = 0;
  tracer->fetch_syscall_eip_use_depth  = 0;

  /* Tell the caller that everything went fine */
  return 0;
}

/***************************************************
 * Unregister the currently registered tracer
 * Return values :
 *   0, all is OK
 *   -ENOMEDIUM, there isn't a registered tracer
 *   -ENXIO, unregestering wrong tracer
 ***************************************************/
int unregister_tracer(tracer_call pm_trace_function)
{
  unsigned long    l_flags;   /* Flags for irqsave */

  /* Is there a tracer already registered */
  if(tracer_registered == 0)
    /* Nothing to unregister */
    return -ENOMEDIUM;

  /* Lock registration variables */
  write_lock_irqsave(&tracer_register_lock, l_flags);

  /* Is it the tracer that was registered */
  if(tracer->trace == pm_trace_function)
    /* There isn't any tracer in here */
    tracer_registered = 0;
  else
    {
    /* Unlock registration variables */
    write_unlock_irqrestore(&tracer_register_lock, l_flags);

    /* We're done here */
    return -ENXIO;
    }

  /* Free the memory used by the tracing structure */
  kfree(tracer);
  tracer = NULL;

  /* Unlock registration variables */
  write_unlock_irqrestore(&tracer_register_lock, l_flags);

  /* Tell the caller that everything went OK */
  return 0;
}

/*******************************************************
 * Set the tracing configuration
 * Parameters :
 *   pm_trace_function, the trace function.
 *   pm_fetch_syscall_use_depth, Use depth to fetch eip
 *   pm_fetch_syscall_use_bounds, Use bounds to fetch eip
 *   pm_syscall_eip_depth, Detph to fetch eip
 *   pm_syscall_lower_bound, Lower bound eip address
 *   pm_syscall_upper_bound, Upper bound eip address
 * Return values : 
 *   0, all is OK 
 *   -ENOMEDIUM, there isn't a registered tracer
 *   -ENXIO, wrong tracer
 *   -EINVAL, invalid configuration
 *******************************************************/
int trace_set_config(tracer_call pm_trace_function,
		     int         pm_fetch_syscall_use_depth,
		     int         pm_fetch_syscall_use_bounds,
		     int         pm_syscall_eip_depth,
		     void*       pm_syscall_lower_bound,
		     void*       pm_syscall_upper_bound)
{
  /* Is there a tracer already registered */
  if(tracer_registered == 0)
    return -ENOMEDIUM;

  /* Is it the tracer that was registered */
  if(tracer->trace != pm_trace_function)
    return -ENXIO;

  /* Is this a valid configuration */
  if((pm_fetch_syscall_use_depth && pm_fetch_syscall_use_bounds)
   ||(pm_syscall_lower_bound > pm_syscall_upper_bound)
   ||(pm_syscall_eip_depth < 0))
    return -EINVAL;

  /* Set the configuration */
  tracer->fetch_syscall_eip_use_depth  = pm_fetch_syscall_use_depth;
  tracer->fetch_syscall_eip_use_bounds = pm_fetch_syscall_use_bounds;
  tracer->syscall_eip_depth = pm_syscall_eip_depth;
  tracer->syscall_lower_eip_bound = pm_syscall_lower_bound;
  tracer->syscall_upper_eip_bound = pm_syscall_upper_bound;

  /* Tell the caller that everything was OK */
  return 0;
}

/*******************************************************
 * Get the tracing configuration
 * Parameters :
 *   pm_fetch_syscall_use_depth, Use depth to fetch eip
 *   pm_fetch_syscall_use_bounds, Use bounds to fetch eip
 *   pm_syscall_eip_depth, Detph to fetch eip
 *   pm_syscall_lower_bound, Lower bound eip address
 *   pm_syscall_upper_bound, Upper bound eip address
 * Return values :
 *   0, all is OK 
 *   -ENOMEDIUM, there isn't a registered tracer
 *******************************************************/
int trace_get_config(int*        pm_fetch_syscall_use_depth,
		     int*        pm_fetch_syscall_use_bounds,
		     int*        pm_syscall_eip_depth,
		     void**      pm_syscall_lower_bound,
		     void**      pm_syscall_upper_bound)
{
  /* Is there a tracer already registered */
  if(tracer_registered == 0)
    return -ENOMEDIUM;

  /* Get the configuration */
  *pm_fetch_syscall_use_depth  = tracer->fetch_syscall_eip_use_depth;
  *pm_fetch_syscall_use_bounds = tracer->fetch_syscall_eip_use_bounds;
  *pm_syscall_eip_depth = tracer->syscall_eip_depth;
  *pm_syscall_lower_bound = tracer->syscall_lower_eip_bound;
  *pm_syscall_upper_bound = tracer->syscall_upper_eip_bound;

  /* Tell the caller that everything was OK */
  return 0;
}

/*******************************************************
 * Register a callback function to be called on occurence
 * of given event
 * Parameters :
 *   pm_trace_function, the callback function.
 *   pm_event_id, the event ID to be monitored.
 * Return values :
 *   0, all is OK
 *   -ENOMEM, unable to allocate memory for callback
 *******************************************************/
int trace_register_callback(tracer_call pm_trace_function,
			    uint8_t     pm_event_id)
{
  struct trace_callback_table_entry*  p_tct_entry;

  /* Search for an empty entry in the callback table */
  for(p_tct_entry = &(trace_callback_table[pm_event_id - 1]);
      p_tct_entry->next != NULL;
      p_tct_entry = p_tct_entry->next);

  /* Allocate a new callback */
  if((p_tct_entry->next = kmalloc(sizeof(struct trace_callback_table_entry), GFP_ATOMIC)) == NULL)
    return -ENOMEM;

  /* Setup the new callback */
  p_tct_entry->next->callback = pm_trace_function;
  p_tct_entry->next->next     = NULL;

  /* Tell the caller everything is ok */
  return 0;
}

/*******************************************************
 * UnRegister a callback function.
 * Parameters :
 *   pm_trace_function, the callback function.
 *   pm_event_id, the event ID that had to be monitored.
 * Return values :
 *   0, all is OK
 *   -ENOMEDIUM, no such callback resigtered
 *******************************************************/
int trace_unregister_callback(tracer_call pm_trace_function,
			      uint8_t     pm_event_id)
{
  struct trace_callback_table_entry*  p_tct_entry;  /* Pointer to trace callback table entry */
  struct trace_callback_table_entry*  p_temp_entry; /* Pointer to trace callback table entry */

  /* Search for the callback in the callback table */
  for(p_tct_entry = &(trace_callback_table[pm_event_id - 1]);
      ((p_tct_entry->next != NULL) && (p_tct_entry->next->callback != pm_trace_function));
      p_tct_entry = p_tct_entry->next);

  /* Did we find anything */
  if(p_tct_entry == NULL)
    return -ENOMEDIUM;

  /* Free the callback entry */
  p_temp_entry = p_tct_entry->next->next;
  kfree(p_tct_entry->next);
  p_tct_entry->next = p_temp_entry;

  /* Tell the caller everything is ok */
  return 0;
}

/*******************************************************
 * Create a new traceable event type
 * Parameters :
 *   pm_event_type, string describing event type
 *   pm_event_desc, string used for standard formatting
 *   pm_format_type, type of formatting used to log event
 *                 data
 *   pm_format_data, data specific to format
 *   pm_owner_pid, PID of event's owner (0 if none)
 * Return values :
 *   New Event ID if all is OK
 *   -ENOMEM, Unable to allocate new event
 *******************************************************/
int _trace_create_event(char*            pm_event_type,
			char*            pm_event_desc,
			int              pm_format_type,
			char*            pm_format_data,
			pid_t            pm_owner_pid)
{
  struct custom_event_desc* p_new_event;          /* Newly created event */

  /* Create event */
  if((p_new_event = (struct custom_event_desc*) kmalloc(sizeof(struct custom_event_desc), GFP_ATOMIC)) == NULL)
    return -ENOMEM;

  /* Initialize event properties */
  p_new_event->event.type[0] = '\0';
  p_new_event->event.desc[0] = '\0';
  p_new_event->event.form[0] = '\0';

  /* Set basic event properties */
  if(pm_event_type != NULL)
    strncpy(p_new_event->event.type, pm_event_type, CUSTOM_EVENT_TYPE_STR_LEN);
  if(pm_event_desc != NULL)
    strncpy(p_new_event->event.desc, pm_event_desc, CUSTOM_EVENT_DESC_STR_LEN);
  if(pm_format_data != NULL)
    strncpy(p_new_event->event.form, pm_format_data, CUSTOM_EVENT_FORM_STR_LEN);

  /* Ensure that strings are bound */
  p_new_event->event.type[CUSTOM_EVENT_TYPE_STR_LEN - 1] = '\0';
  p_new_event->event.desc[CUSTOM_EVENT_DESC_STR_LEN - 1] = '\0';
  p_new_event->event.form[CUSTOM_EVENT_FORM_STR_LEN - 1] = '\0';

  /* Set format type */
  p_new_event->event.format_type = pm_format_type;

  /* Give the new event a unique event ID */
  p_new_event->event.id = next_event_id;
  next_event_id++;

  /* Set event's owner */
  p_new_event->owner_pid = pm_owner_pid;

  /* Insert new event in event list */
  write_lock(&custom_list_lock);
  p_new_event->next = custom_events;
  p_new_event->prev = custom_events->prev;
  custom_events->prev->next = p_new_event;
  custom_events->prev = p_new_event;
  write_unlock(&custom_list_lock);

  /* Log the event creation event */
  trace_event(TRACE_EV_NEW_EVENT, &(p_new_event->event));

  /* Return new event ID */
  return p_new_event->event.id;
}
int trace_create_event(char*            pm_event_type,
		       char*            pm_event_desc,
		       int              pm_format_type,
		       char*            pm_format_data)
{
  return _trace_create_event(pm_event_type, pm_event_desc, pm_format_type, pm_format_data, 0);
}
int trace_create_owned_event(char*            pm_event_type,
			     char*            pm_event_desc,
			     int              pm_format_type,
			     char*            pm_format_data,
			     pid_t            pm_owner_pid)
{
  return _trace_create_event(pm_event_type, pm_event_desc, pm_format_type, pm_format_data, pm_owner_pid);
}

/*******************************************************
 * Destroy a created event type
 * Parameters :
 *   pm_event_id, the Id returned by trace_create_event()
 * Return values :
 *   NONE
 *******************************************************/
void trace_destroy_event(int pm_event_id)
{
  struct custom_event_desc*   p_event_desc;   /* Generic event description pointer */

  /* Lock the table for writting */
  write_lock(&custom_list_lock);

  /* Go through the event description list */
  for(p_event_desc = custom_events->next;
      p_event_desc != custom_events;
      p_event_desc = p_event_desc->next)
    if(p_event_desc->event.id == pm_event_id)
      break;

  /* If we found something */
  if(p_event_desc != custom_events)
    {
    /* Remove the event fromt the list */
    p_event_desc->next->prev = p_event_desc->prev;
    p_event_desc->prev->next = p_event_desc->next;

    /* Free the memory used by this event */
    kfree(p_event_desc);
    }

  /* Unlock the table for writting */
  write_unlock(&custom_list_lock);
}

/*******************************************************
 * Destroy an owner's events
 * Parameters :
 *   pm_owner_pid, the PID of the owner who's events are to
 *               be deleted.
 * Return values :
 *   NONE
 *******************************************************/
void trace_destroy_owners_events(pid_t pm_owner_pid)
{
  struct custom_event_desc*   p_temp_event;   /* Temporary event */
  struct custom_event_desc*   p_event_desc;   /* Generic event description pointer */

  /* Lock the table for writting */
  write_lock(&custom_list_lock);

  /* Start at the first event in the list */
  p_event_desc = custom_events->next;

  /* Go through the event description list */
  while(p_event_desc != custom_events)
    {
    /* Keep pointer to next event */
    p_temp_event = p_event_desc->next;

    /* Does this event belong to the same owner */
    if(p_event_desc->owner_pid == pm_owner_pid)
      {
      /* Remove the event fromt the list */
      p_event_desc->next->prev = p_event_desc->prev;
      p_event_desc->prev->next = p_event_desc->next;

      /* Free the memory used by this event */
      kfree(p_event_desc);
      }

    /* Go to next event */
    p_event_desc = p_temp_event;
    }

  /* Unlock the table for writting */
  write_unlock(&custom_list_lock);
}

/*******************************************************
 * Relog the declarations of custom events. This is
 * necessary to make sure that even though the event
 * creation might not have taken place during a trace,
 * that all custom events be part of all traces. Hence,
 * if a custom event occurs during a trace, we can be
 * sure that it's definition is part of the trace.
 * Parameters :
 *    NONE
 * Return values :
 *    NONE
 *******************************************************/
void trace_reregister_custom_events(void)
{
  struct custom_event_desc*   p_event_desc;   /* Generic event description pointer */

  /* Lock the table for reading */
  read_lock(&custom_list_lock);

  /* Go through the event description list */
  for(p_event_desc = custom_events->next;
      p_event_desc != custom_events;
      p_event_desc = p_event_desc->next)
    /* Log the event creation event */
    trace_event(TRACE_EV_NEW_EVENT, &(p_event_desc->event));

  /* Unlock the table for reading */
  read_unlock(&custom_list_lock);
}

/*******************************************************
 * Trace a formatted event
 * Parameters :
 *   pm_event_id, the event Id provided upon creation
 *   ..., printf-like data that will be used to fill the
 *        event string.
 * Return values :
 *   0, all is OK
 *   -ENOMEDIUM, there isn't a registered tracer or this
 *               event doesn't exist.
 *   -EBUSY, tracing hasn't started yet
 *******************************************************/
int trace_std_formatted_event(int pm_event_id, ...)
{
  int                         l_string_size;   /* Size of the string outputed by vsprintf() */
  char                        l_string[CUSTOM_EVENT_FINAL_STR_LEN];  /* Final formatted string */
  va_list                     l_var_arg_list;  /* Variable argument list */
  trace_custom                l_custom;        /* Custom event */
  struct custom_event_desc*   p_event_desc;    /* Generic event description pointer */

  /* Lock the table for reading */
  read_lock(&custom_list_lock);

  /* Go through the event description list */
  for(p_event_desc = custom_events->next;
      p_event_desc != custom_events;
      p_event_desc = p_event_desc->next)
    if(p_event_desc->event.id == pm_event_id)
      break;

  /* If we haven't found anything */
  if(p_event_desc == custom_events)
    {
    /* Unlock the table for reading */
    read_unlock(&custom_list_lock);

    /* No such thing */
    return -ENOMEDIUM;
    }

  /* Set custom event Id */
  l_custom.id = pm_event_id;

  /* Initialize variable argument list access */
  va_start(l_var_arg_list, pm_event_id);

  /* Print the description out to the temporary buffer */
  l_string_size = vsprintf(l_string, p_event_desc->event.desc, l_var_arg_list);

  /* Unlock the table for reading */
  read_unlock(&custom_list_lock);

  /* Facilitate return to caller */
  va_end(l_var_arg_list);

  /* Set the size of the event */
  l_custom.data_size = (uint32_t) (l_string_size + 1);

  /* Set the pointer to the event data */
  l_custom.data = l_string;

  /* Log the custom event */
  return trace_event(TRACE_EV_CUSTOM, &l_custom);
}

/*******************************************************
 * Trace a raw event
 * Parameters :
 *   pm_event_id, the event Id provided upon creation
 *   pm_event_size, the size of the data provided
 *   pm_event_data, data buffer describing event
 * Return values :
 *   0, all is OK
 *   -ENOMEDIUM, there isn't a registered tracer or this
 *               event doesn't exist.
 *   -EBUSY, tracing hasn't started yet
 *******************************************************/
int trace_raw_event(int pm_event_id, int pm_event_size, void* pm_event_data)
{
  trace_custom                l_custom;        /* Custom event */
  struct custom_event_desc*   p_event_desc;    /* Generic event description pointer */

  /* Lock the table for reading */
  read_lock(&custom_list_lock);

  /* Go through the event description list */
  for(p_event_desc = custom_events->next;
      p_event_desc != custom_events;
      p_event_desc = p_event_desc->next)
    if(p_event_desc->event.id == pm_event_id)
      break;

  /* Unlock the table for reading */
  read_unlock(&custom_list_lock);

  /* If we haven't found anything */
  if(p_event_desc == custom_events)
    /* No such thing */
    return -ENOMEDIUM;

  /* Set custom event Id */
  l_custom.id = pm_event_id;

  /* Set the data size */
  if(pm_event_size <= CUSTOM_EVENT_MAX_SIZE)
    l_custom.data_size = (uint32_t) pm_event_size;
  else
    l_custom.data_size = (uint32_t) CUSTOM_EVENT_MAX_SIZE;

  /* Set the pointer to the event data */
  l_custom.data = pm_event_data;

  /* Log the custom event */
  return trace_event(TRACE_EV_CUSTOM, &l_custom);
}

/*******************************************************
 * Trace an event
 * Parameters :
 *   pm_event_id, the event's ID (check out trace.h)
 *   pm_event_struct, the structure describing the event
 * Return values :
 *   0, all is OK
 *   -ENOMEDIUM, there isn't a registered tracer
 *   -EBUSY, tracing hasn't started yet
 *******************************************************/
int trace_event(uint8_t  pm_event_id, 
       	        void*    pm_event_struct)
{
  int                                 l_ret_value; /* The return value */
  struct trace_callback_table_entry*  p_tct_entry; /* Pointer to trace callback table entry */

  /* Lock registration variables */
  read_lock(&tracer_register_lock);

  /* Is there a tracer registered */
  if(tracer_registered != 1)
    l_ret_value = -ENOMEDIUM;
  else
    /* Call the tracer */
    l_ret_value = tracer->trace(pm_event_id, pm_event_struct);

  /* Unlock registration variables */
  read_unlock(&tracer_register_lock);

  /* Is this a native event */
  if(pm_event_id <= TRACE_EV_MAX)
    {
    /* Are there any callbacks to call */
    if(trace_callback_table[pm_event_id - 1].next != NULL)
      {
      /* Call all the callbacks linked to this event */
      for(p_tct_entry = trace_callback_table[pm_event_id - 1].next;
	  p_tct_entry != NULL;
	  p_tct_entry = p_tct_entry->next)
	p_tct_entry->callback(pm_event_id, pm_event_struct);
      }
    }

  /* Give the return value */
  return l_ret_value;
}

/*******************************************************
 * Initialize trace facility
 * Parameters :
 *    NONE
 * Return values :
 *    NONE
 *******************************************************/
static int __init trace_init(void)
{
  int i;  /* Generic index */

  /* Initialize callback table */
  for(i = 0; i < TRACE_EV_MAX; i++)
    {
    trace_callback_table[i].callback = NULL;
    trace_callback_table[i].next     = NULL;
    }

  /* Next event ID to be used */
  next_event_id = TRACE_EV_MAX + 1;

  /* Initialize custom events list */
  custom_events = &custom_events_head;
  custom_events->next = custom_events;
  custom_events->prev = custom_events;

  /* Everything is OK */
  return 0;
}

module_init(trace_init);

/* Export symbols so that can be visible from outside this file */
EXPORT_SYMBOL(register_tracer);
EXPORT_SYMBOL(unregister_tracer);
EXPORT_SYMBOL(trace_set_config);
EXPORT_SYMBOL(trace_get_config);
EXPORT_SYMBOL(trace_register_callback);
EXPORT_SYMBOL(trace_unregister_callback);
EXPORT_SYMBOL(trace_create_event);
EXPORT_SYMBOL(trace_create_owned_event);
EXPORT_SYMBOL(trace_destroy_event);
EXPORT_SYMBOL(trace_destroy_owners_events);
EXPORT_SYMBOL(trace_reregister_custom_events);
EXPORT_SYMBOL(trace_std_formatted_event);
EXPORT_SYMBOL(trace_raw_event);
EXPORT_SYMBOL(trace_event);
#if !defined(CONFIG_PPC) && !defined(__sh__)
EXPORT_SYMBOL(irq_desc);
#endif