From: Karim Yaghmour <karim@opersys.com>
To: linux-kernel <linux-kernel@vger.kernel.org>
Cc: LTT-Dev <ltt-dev@shafik.org>
Subject: [PATCH] 2/8 LTT for 2.5.33: Trace driver
Date: Fri, 06 Sep 2002 18:16:06 -0400 [thread overview]
Message-ID: <3D792926.11530513@opersys.com> (raw)
This is LTT's trace driver. This patch adds the following files:
drivers/trace/Config.help
drivers/trace/Config.in
drivers/trace/tracer.c
drivers/trace/tracer.h
The trace driver uses the rvmalloc/rvfree couple to allocate a mem
region it can remap to the daemon's address space.
diff -urN linux-2.5.33/drivers/Makefile linux-2.5.33-ltt/drivers/Makefile
--- linux-2.5.33/drivers/Makefile Sat Aug 31 18:05:31 2002
+++ linux-2.5.33-ltt/drivers/Makefile Fri Sep 6 13:52:32 2002
@@ -41,5 +41,6 @@
obj-$(CONFIG_BLUEZ) += bluetooth/
obj-$(CONFIG_HOTPLUG_PCI) += hotplug/
obj-$(CONFIG_ISDN_BOOL) += isdn/
+obj-$(CONFIG_TRACE) += trace/
include $(TOPDIR)/Rules.make
diff -urN linux-2.5.33/drivers/trace/Config.help linux-2.5.33-ltt/drivers/trace/Config.help
--- linux-2.5.33/drivers/trace/Config.help Wed Dec 31 19:00:00 1969
+++ linux-2.5.33-ltt/drivers/trace/Config.help Fri Sep 6 12:03:21 2002
@@ -0,0 +1,36 @@
+Kernel events tracing support
+CONFIG_TRACE
+ It is possible for the kernel to log important events to a tracing
+ driver. Doing so, enables the use of the generated traces in order
+ to reconstruct the dynamic behavior of the kernel, and hence the
+ whole system.
+
+ The tracing process contains 4 parts :
+ 1) The logging of events by key parts of the kernel.
+ 2) The trace driver that keeps the events in a data buffer.
+ 3) A trace daemon that opens the trace driver and is notified
+ every time there is a certain quantity of data to read
+ from the trace driver (using SIG_IO).
+ 4) A trace event data decoder that reads the accumulated data
+ and formats it in a human-readable format.
+
+ If you say Y or M here, the first part of the tracing process will
+ always take place. That is, critical parts of the kernel will call
+ upon the kernel tracing function. The data generated doesn't go
+ any further until a trace driver registers himself as such with the
+ kernel. Therefore, if you answer Y, then the driver will be part of
+ the kernel and the events will always proceed onto the driver and
+ if you say M, then the events will only proceed onto the driver when
+ it's module is loaded. Note that event's aren't logged in the driver
+ until the profiling daemon opens the device, configures it and
+ issues the "start" command through ioctl().
+
+ The impact of a fully functionnal system (kernel event logging +
+ driver event copying + active trace daemon) is of 2.5% for core events.
+ This means that for a task that took 100 seconds on a normal system, it
+ will take 102.5 seconds on a traced system. This is very low compared
+ to other profiling or tracing methods.
+
+ For more information on kernel tracing, the trace daemon or the event
+ decoder, please check the following address :
+ http://www.opersys.com/LTT
diff -urN linux-2.5.33/drivers/trace/Config.in linux-2.5.33-ltt/drivers/trace/Config.in
--- linux-2.5.33/drivers/trace/Config.in Wed Dec 31 19:00:00 1969
+++ linux-2.5.33-ltt/drivers/trace/Config.in Fri Sep 6 12:03:21 2002
@@ -0,0 +1,4 @@
+mainmenu_option next_comment
+comment 'Kernel tracing'
+tristate 'Kernel events tracing support' CONFIG_TRACE
+endmenu
diff -urN linux-2.5.33/drivers/trace/Makefile linux-2.5.33-ltt/drivers/trace/Makefile
--- linux-2.5.33/drivers/trace/Makefile Wed Dec 31 19:00:00 1969
+++ linux-2.5.33-ltt/drivers/trace/Makefile Fri Sep 6 16:24:49 2002
@@ -0,0 +1,17 @@
+#
+# Makefile for the kernel tracing drivers.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now inherited from the
+# parent makes..
+#
+
+O_TARGET := built-in.o
+
+# Is it loaded as a module or as part of the kernel
+obj-$(CONFIG_TRACE) = tracer.o
+
+include $(TOPDIR)/Rules.make
diff -urN linux-2.5.33/drivers/trace/tracer.c linux-2.5.33-ltt/drivers/trace/tracer.c
--- linux-2.5.33/drivers/trace/tracer.c Wed Dec 31 19:00:00 1969
+++ linux-2.5.33-ltt/drivers/trace/tracer.c Fri Sep 6 16:00:18 2002
@@ -0,0 +1,1321 @@
+/*****************************************************************
+ * File : tracer.c
+ * Description :
+ * Contains the code for the kernel tracing driver (tracer
+ * for short).
+ * Author :
+ * Karim Yaghmour (karim@opersys.com)
+ * Date :
+ * 03/12/01, Added user event support.
+ * 05/01/01, Modified PPC bit manipulation functions for
+ * x86 compatibility. (andy_lowe@mvista.com)
+ * 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, Chaging 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 10milli-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/module.h>
+#include <linux/init.h>
+
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/trace.h>
+#include <linux/wrapper.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+
+#include <asm/io.h>
+#include <asm/current.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+#include <asm/trace.h>
+
+#include "tracer.h"
+
+/* Module information */
+MODULE_AUTHOR("Karim Yaghmour (karim@opersys.com)");
+MODULE_DESCRIPTION("Linux Trace Toolkit (LTT) kernel tracing driver");
+MODULE_LICENSE("GPL");
+
+/* Driver */
+static int sMajorNumber; /* Major number of the tracer */
+static int sOpenCount; /* Number of times device is open */
+/* Locking */
+static int sTracLock; /* Tracer lock used to lock primary buffer */
+static spinlock_t sSpinLock; /* Spinlock in order to lock kernel */
+/* Daemon */
+static int sSignalSent; /* A signal has been sent to the daemon */
+static struct task_struct* sDaemonTaskStruct; /* Task structure of the tracer daemon */
+/* Tracer configuration */
+static int sTracerStarted; /* Is the tracer started */
+static trace_event_mask sTracedEvents; /* Bit-field of events being traced */
+static trace_event_mask sLogEventDetailsMask; /* Log the details of the events mask */
+static int sLogCPUID; /* Log the CPUID associated with each event */
+static int sUseSyscallEIPBounds; /* Use adress bounds to fetch the EIP where call is made */
+static int sLowerEIPBoundSet; /* The lower bound EIP has been set */
+static int sUpperEIPBoundSet; /* The upper bound EIP has been set */
+static void* sLowerEIPBound; /* The lower bound EIP */
+static void* sUpperEIPBound; /* The upper bound EIP */
+static int sTracingPID; /* Tracing only the events for one pid */
+static int sTracingPGRP; /* Tracing only the events for one process group */
+static int sTracingGID; /* Tracing only the events for one gid */
+static int sTracingUID; /* Tracing only the events for one uid */
+static pid_t sTracedPID; /* PID being traced */
+static pid_t sTracedPGRP; /* Process group being traced */
+static gid_t sTracedGID; /* GID being traced */
+static uid_t sTracedUID; /* UID being traced */
+static int sSyscallEIPDepthSet; /* The call depth at which to fetch EIP has been set */
+static int sSyscallEIPDepth; /* The call depth at which to fetch the EIP */
+/* Event data buffers */
+static int sBufReadComplete; /* Number of buffers completely filled */
+static int sSizeReadIncomplete; /* Quantity of data read from incomplete buffers */
+static int sEventsLost; /* Number of events lost because of lack of buffer space */
+static u32 sBufSize; /* Buffer sizes */
+static u32 sAllocSize; /* Size of buffers allocated */
+static u32 sBufferID; /* Unique buffer ID */
+static char* sTracBuf = NULL; /* Trace buffer */
+static char* sWritBuf = NULL; /* Buffer used for writting */
+static char* sReadBuf = NULL; /* Buffer used for reading */
+static char* sWritBufEnd; /* End of write buffer */
+static char* sReadBufEnd; /* End of read buffer */
+static char* sWritPos; /* Current position for writting */
+static char* sReadLimit; /* Limit at which read should stop */
+static char* sWritLimit; /* Limit at which write should stop */
+
+/* Time */
+static struct timeval sBufferStartTime; /* The time at which the buffer was started */
+
+/* Large data components allocated at load time */
+static char *sUserEventData = NULL; /* The data associated with a user event */
+
+
+/* The size of the structures used to describe the events */
+static int sEventStructSize[TRACE_EV_MAX + 1] =
+{
+ sizeof(trace_start) /* TRACE_START */ ,
+ sizeof(trace_syscall_entry) /* TRACE_SYSCALL_ENTRY */ ,
+ 0 /* TRACE_SYSCALL_EXIT */ ,
+ sizeof(trace_trap_entry) /* TRACE_TRAP_ENTRY */ ,
+ 0 /* TRACE_TRAP_EXIT */ ,
+ sizeof(trace_irq_entry) /* TRACE_IRQ_ENTRY */ ,
+ 0 /* TRACE_IRQ_EXIT */ ,
+ sizeof(trace_schedchange) /* TRACE_SCHEDCHANGE */ ,
+ 0 /* TRACE_KERNEL_TIMER */ ,
+ sizeof(trace_soft_irq) /* TRACE_SOFT_IRQ */ ,
+ sizeof(trace_process) /* TRACE_PROCESS */ ,
+ sizeof(trace_file_system) /* TRACE_FILE_SYSTEM */ ,
+ sizeof(trace_timer) /* TRACE_TIMER */ ,
+ sizeof(trace_memory) /* TRACE_MEMORY */ ,
+ sizeof(trace_socket) /* TRACE_SOCKET */ ,
+ sizeof(trace_ipc) /* TRACE_IPC */ ,
+ sizeof(trace_network) /* TRACE_NETWORK */ ,
+ sizeof(trace_buffer_start) /* TRACE_BUFFER_START */ ,
+ 0 /* TRACE_BUFFER_END */ ,
+ sizeof(trace_new_event) /* TRACE_NEW_EVENT */ ,
+ sizeof(trace_custom) /* TRACE_CUSTOM */ ,
+ sizeof(trace_change_mask) /* TRACE_CHANGE_MASK */
+};
+
+/* The file operations available for the tracer */
+static struct file_operations sTracerFileOps =
+{
+ owner: THIS_MODULE,
+ ioctl: tracer_ioctl,
+ mmap: tracer_mmap,
+ open: tracer_open,
+ release: tracer_release,
+ fsync: tracer_fsync,
+};
+
+/************************************************************************************************************/
+/************************************** Code inspired from BTTV driver **************************************/
+/************************************************************************************************************/
+#define FIX_SIZE(x) (((x) - 1) & PAGE_MASK) + PAGE_SIZE /* This inspired by rtai/shmem */
+
+/* Here we want the physical address of the memory.
+ * This is used when initializing the contents of the
+ * area and marking the pages as reserved.
+ */
+static inline unsigned long kvirt_to_pa(unsigned long adr)
+{
+ unsigned long kva, ret;
+
+ kva = (unsigned long) page_address(vmalloc_to_page((void *) adr));
+ kva |= adr & (PAGE_SIZE - 1); /* restore the offset */
+ ret = __pa(kva);
+ return ret;
+}
+
+static void *rvmalloc(unsigned long size)
+{
+ void *mem;
+ unsigned long adr;
+
+ mem = vmalloc_32(size);
+ if (!mem)
+ return NULL;
+
+ memset(mem, 0, size); /* Clear the ram out, no junk to the user */
+ adr = (unsigned long) mem;
+ while (size > 0) {
+ mem_map_reserve(vmalloc_to_page((void *) adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+
+ return mem;
+}
+
+static void rvfree(void *mem, unsigned long size)
+{
+ unsigned long adr;
+
+ if (!mem)
+ return;
+
+ adr = (unsigned long) mem;
+ while ((long) size > 0) {
+ mem_map_unreserve(vmalloc_to_page((void *) adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ vfree(mem);
+}
+
+static int tracer_mmap_region(struct vm_area_struct *vma,
+ const char *adr,
+ const char *start_pos,
+ unsigned long size)
+{
+ unsigned long start = (unsigned long) adr;
+ unsigned long page, pos;
+
+ pos = (unsigned long) start_pos;
+ while (size > 0) {
+ page = kvirt_to_pa(pos);
+ if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+ return 0;
+}
+/************************************************************************************************************/
+/************************************************************************************************************/
+/************************************************************************************************************/
+
+/**************************************************************
+ * Macro : tracer_write_to_buffer()
+ * Description :
+ * Writes data to the destination buffer and updates the
+ * begining the buffer write position.
+ **************************************************************/
+#define tracer_write_to_buffer(DEST, SRC, SIZE) \
+do\
+{\
+ memcpy(DEST, SRC, SIZE);\
+ DEST += SIZE;\
+} while(0);
+
+/**************************************************************
+ * Function : trace()
+ * Description : Tracing function per se.
+ * Parameters :
+ * pmEventID, ID of event as defined in linux/trace.h
+ * pmEventStruct, struct describing the event
+ * Return values :
+ * 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.
+ * Note :
+ * The kernel has to be locked here because trace() could
+ * be called from an interrupt handling routine and from
+ * a process service routine.
+ **************************************************************/
+int trace(u8 pmEventID,
+ void *pmEventStruct)
+{
+ int lVarDataLen = 0; /* Length of variable length data to be copied, if any */
+ void *lVarDataBeg = NULL; /* Begining of variable length data to be copied */
+ int lSendSignal = FALSE; /* Should the daemon be summoned */
+ u8 lCPUID; /* CPUID of currently runing process */
+ uint16_t lDataSize; /* Size of tracing data */
+ struct siginfo lSigInfo; /* Signal information */
+ struct timeval lTime; /* Event time */
+ unsigned long int lFlags; /* CPU flags for lock */
+ trace_time_delta lTimeDelta; /* The time elapsed between now and the last event */
+ struct task_struct *pIncomingProcess = NULL; /* Pointer to incoming process */
+
+ /* Is there a tracing daemon */
+ if (sDaemonTaskStruct == NULL)
+ return -ENODEV;
+
+ /* Is this the exit of a process? */
+ if ((pmEventID == TRACE_EV_PROCESS) &&
+ (pmEventStruct != NULL) &&
+ ((((trace_process *) pmEventStruct)->event_sub_id) == TRACE_EV_PROCESS_EXIT))
+ trace_destroy_owners_events(current->pid);
+
+ /* Do we trace the event */
+ if ((sTracerStarted == TRUE) || (pmEventID == TRACE_EV_START) || (pmEventID == TRACE_EV_BUFFER_START))
+ goto TraceEvent;
+
+ return -EBUSY;
+
+ TraceEvent:
+
+ /* Are we monitoring this event */
+ if (!ltt_test_bit(pmEventID, &sTracedEvents))
+ return 0;
+
+ /* Always let the start event pass, whatever the IDs */
+ if ((pmEventID != TRACE_EV_START) && (pmEventID != TRACE_EV_BUFFER_START)) {
+ /* Is this a scheduling change */
+ if (pmEventID == TRACE_EV_SCHEDCHANGE) {
+ /* Get pointer to incoming process */
+ pIncomingProcess = (struct task_struct *) (((trace_schedchange *) pmEventStruct)->in);
+
+ /* Set PID information in schedchange event */
+ (((trace_schedchange *) pmEventStruct)->in) = pIncomingProcess->pid;
+ }
+ /* Are we monitoring a particular process */
+ if ((sTracingPID == TRUE) && (current->pid != sTracedPID)) {
+ /* Record this event if it is the scheduling change bringing in the traced PID */
+ if (pIncomingProcess == NULL)
+ return 0;
+ else if (pIncomingProcess->pid != sTracedPID)
+ return 0;
+ }
+ /* Are we monitoring a particular process group */
+ if ((sTracingPGRP == TRUE) && (current->pgrp != sTracedPGRP)) {
+ /* Record this event if it is the scheduling change bringing in a process of the traced PGRP */
+ if (pIncomingProcess == NULL)
+ return 0;
+ else if (pIncomingProcess->pgrp != sTracedPGRP)
+ return 0;
+ }
+ /* Are we monitoring the processes of a given group of users */
+ if ((sTracingGID == TRUE) && (current->egid != sTracedGID)) {
+ /* Record this event if it is the scheduling change bringing in a process of the traced GID */
+ if (pIncomingProcess == NULL)
+ return 0;
+ else if (pIncomingProcess->egid != sTracedGID)
+ return 0;
+ }
+ /* Are we monitoring the processes of a given user */
+ if ((sTracingUID == TRUE) && (current->euid != sTracedUID)) {
+ /* Record this event if it is the scheduling change bringing in a process of the traced UID */
+ if (pIncomingProcess == NULL)
+ return 0;
+ else if (pIncomingProcess->euid != sTracedUID)
+ return 0;
+ }
+ }
+ /* Compute size of tracing data */
+ lDataSize = sizeof(pmEventID) + sizeof(lTimeDelta) + sizeof(lDataSize);
+
+ /* Do we log the event details */
+ if (ltt_test_bit(pmEventID, &sLogEventDetailsMask)) {
+ /* Update the size of the data entry */
+ lDataSize += sEventStructSize[pmEventID];
+
+ /* Some events have variable length */
+ switch (pmEventID) {
+ /* Is there a file name in this */
+ case TRACE_EV_FILE_SYSTEM:
+ if ((((trace_file_system *) pmEventStruct)->event_sub_id == TRACE_EV_FILE_SYSTEM_EXEC)
+ || (((trace_file_system *) pmEventStruct)->event_sub_id == TRACE_EV_FILE_SYSTEM_OPEN)) {
+ /* Remember the string's begining and update size variables */
+ lVarDataBeg = ((trace_file_system *) pmEventStruct)->file_name;
+ lVarDataLen = ((trace_file_system *) pmEventStruct)->event_data2 + 1;
+ lDataSize += (uint16_t) lVarDataLen;
+ }
+ break;
+
+ /* Logging of a custom event */
+ case TRACE_EV_CUSTOM:
+ lVarDataBeg = ((trace_custom *) pmEventStruct)->data;
+ lVarDataLen = ((trace_custom *) pmEventStruct)->data_size;
+ lDataSize += (uint16_t) lVarDataLen;
+ break;
+ }
+ }
+ /* Do we record the CPUID */
+ if ((sLogCPUID == TRUE) && (pmEventID != TRACE_EV_START) && (pmEventID != TRACE_EV_BUFFER_START)) {
+ /* Remember the CPUID */
+ lCPUID = smp_processor_id();
+
+ /* Update the size of the data entry */
+ lDataSize += sizeof(lCPUID);
+ }
+ /* Lock the kernel */
+ spin_lock_irqsave(&sSpinLock, lFlags);
+
+ /* The following time calculations have to be done within the spinlock because
+ otherwise the event order could be inverted. */
+
+ /* Get the time of the event */
+ do_gettimeofday(&lTime);
+
+ /* Compute the time delta between this event and the time at which this buffer was started */
+ lTimeDelta = (lTime.tv_sec - sBufferStartTime.tv_sec) * 1000000
+ + (lTime.tv_usec - sBufferStartTime.tv_usec);
+
+ /* Is there enough space left in the write buffer */
+ if (sWritPos + lDataSize > sWritLimit) {
+ /* Have we already switched buffers and informed the daemon of it */
+ if (sSignalSent == TRUE) {
+ /* We've lost another event */
+ sEventsLost++;
+
+ /* Bye, bye, now */
+ spin_unlock_irqrestore(&sSpinLock, lFlags);
+ return -ENOMEM;
+ }
+ /* We need to inform the daemon */
+ lSendSignal = TRUE;
+
+ /* Switch buffers */
+ tracer_switch_buffers(lTime);
+
+ /* Recompute the time delta since sBufferStartTime has changed because of the buffer change */
+ lTimeDelta = (lTime.tv_sec - sBufferStartTime.tv_sec) * 1000000
+ + (lTime.tv_usec - sBufferStartTime.tv_usec);
+ }
+ /* Write the CPUID to the tracing buffer, if required */
+ if ((sLogCPUID == TRUE) && (pmEventID != TRACE_EV_START) && (pmEventID != TRACE_EV_BUFFER_START))
+ tracer_write_to_buffer(sWritPos,
+ &lCPUID,
+ sizeof(lCPUID));
+
+ /* Write event type to tracing buffer */
+ tracer_write_to_buffer(sWritPos,
+ &pmEventID,
+ sizeof(pmEventID));
+
+ /* Write event time delta to tracing buffer */
+ tracer_write_to_buffer(sWritPos,
+ &lTimeDelta,
+ sizeof(lTimeDelta));
+
+ /* Do we log event details */
+ if (ltt_test_bit(pmEventID, &sLogEventDetailsMask)) {
+ /* Write event structure */
+ tracer_write_to_buffer(sWritPos,
+ pmEventStruct,
+ sEventStructSize[pmEventID]);
+
+ /* Write string if any */
+ if (lVarDataLen)
+ tracer_write_to_buffer(sWritPos,
+ lVarDataBeg,
+ lVarDataLen);
+ }
+ /* Write the length of the event description */
+ tracer_write_to_buffer(sWritPos,
+ &lDataSize,
+ sizeof(lDataSize));
+
+ /* Should the tracing daemon be notified */
+ if (lSendSignal == TRUE) {
+ /* Remember that a signal has been sent */
+ sSignalSent = TRUE;
+
+ /* Unlock the kernel */
+ spin_unlock_irqrestore(&sSpinLock, lFlags);
+
+ /* Setup signal information */
+ lSigInfo.si_signo = SIGIO;
+ lSigInfo.si_errno = 0;
+ lSigInfo.si_code = SI_KERNEL;
+
+ /* DEBUG */
+#if 0
+ printk("<1> Sending SIGIO to %d \n", sDaemonTaskStruct->pid);
+#endif
+
+ /* Signal the tracing daemon */
+ send_sig_info(SIGIO, &lSigInfo, sDaemonTaskStruct);
+ } else
+ /* Unlock the kernel */
+ spin_unlock_irqrestore(&sSpinLock, lFlags);
+
+ return 0;
+}
+
+/*************************************************************
+ * Function : tracer_switch_buffers()
+ * Description :
+ * Put the current write buffer to be read and reset put
+ * the old read buffer to be written to. Set the tracer
+ * variables in consequence.
+ * Parameters :
+ * pmTime, current time
+ * Return values :
+ * NONE
+ * Note :
+ * This should be called from within a spin_lock.
+ *************************************************************/
+void tracer_switch_buffers(struct timeval pmTime)
+{
+ char *lTempBuf; /* Temporary buffer pointer */
+ char *lTempBufEnd; /* Temporary buffer end pointer */
+ char *lInitWritPos; /* Initial write position */
+ u8 lEventID; /* Event ID of last event */
+ u8 lCPUID; /* CPUID of currently runing process */
+ uint16_t lDataSize; /* Size of tracing data */
+ u32 lSizeLost; /* Size delta between last event and end of buffer */
+ trace_time_delta lTimeDelta; /* The time elapsed between now and the last event */
+ trace_buffer_start lStartBufferEvent; /* Start of the new buffer event */
+
+ /* Remember initial write position */
+ lInitWritPos = sWritPos;
+
+ /* Write the end event at the write of the buffer */
+
+ /* Write the CPUID to the tracing buffer, if required */
+ if (sLogCPUID == TRUE) {
+ lCPUID = smp_processor_id();
+ tracer_write_to_buffer(sWritPos,
+ &lCPUID,
+ sizeof(lCPUID));
+ }
+ /* Write event type to tracing buffer */
+ lEventID = TRACE_EV_BUFFER_END;
+ tracer_write_to_buffer(sWritPos,
+ &lEventID,
+ sizeof(lEventID));
+
+ /* Write event time delta to tracing buffer */
+ lTimeDelta = 0;
+ tracer_write_to_buffer(sWritPos,
+ &lTimeDelta,
+ sizeof(lTimeDelta));
+
+ /* Get size lost */
+ lSizeLost = sWritBufEnd - lInitWritPos;
+
+ /* Write size lost at the end of the buffer */
+ *((u32 *) (sWritBufEnd - sizeof(lSizeLost))) = lSizeLost;
+
+ /* Switch buffers */
+ lTempBuf = sReadBuf;
+ sReadBuf = sWritBuf;
+ sWritBuf = lTempBuf;
+
+ /* Set buffer ends */
+ lTempBufEnd = sReadBufEnd;
+ sReadBufEnd = sWritBufEnd;
+ sWritBufEnd = lTempBufEnd;
+
+ /* Set read limit */
+ sReadLimit = sReadBufEnd;
+
+ /* Set write limit */
+ sWritLimit = sWritBufEnd - TRACER_LAST_EVENT_SIZE;
+
+ /* Set write position */
+ sWritPos = sWritBuf;
+
+ /* Increment buffer ID */
+ sBufferID++;
+
+ /* Set the time of begining of this buffer */
+ sBufferStartTime = pmTime;
+
+ /* Write the start of buffer event */
+ lStartBufferEvent.ID = sBufferID;
+ lStartBufferEvent.Time = pmTime;
+
+ /* Write event type to tracing buffer */
+ lEventID = TRACE_EV_BUFFER_START;
+ tracer_write_to_buffer(sWritPos,
+ &lEventID,
+ sizeof(lEventID));
+
+ /* Write event time delta to tracing buffer */
+ lTimeDelta = 0;
+ tracer_write_to_buffer(sWritPos,
+ &lTimeDelta,
+ sizeof(lTimeDelta));
+
+ /* Write event structure */
+ tracer_write_to_buffer(sWritPos,
+ &lStartBufferEvent,
+ sizeof(lStartBufferEvent));
+
+ /* Compute the data size */
+ lDataSize = sizeof(lEventID)
+ + sizeof(lTimeDelta)
+ + sizeof(lStartBufferEvent)
+ + sizeof(lDataSize);
+
+ /* Write the length of the event description */
+ tracer_write_to_buffer(sWritPos,
+ &lDataSize,
+ sizeof(lDataSize));
+}
+
+/*************************************************************
+ * Function : tracer_ioctl()
+ * Description : "Ioctl" file op
+ * Parameters :
+ * pmInode, the inode associated with the device
+ * pmFile, file structure given to the acting process
+ * pmCmd, command given by the caller
+ * pmArg, arguments to the command
+ * Return values :
+ * >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
+ * Note :
+ * In the future, this function should check to make sure
+ * that it's the server that make thes ioctl.
+ *************************************************************/
+int tracer_ioctl(struct inode *pmInode,
+ struct file *pmFile,
+ unsigned int pmCmd,
+ unsigned long pmArg)
+{
+ int lRetValue; /* Function return value */
+ int lDevMinor; /* Device minor number */
+ int lNewUserEventID; /* ID of newly created user event */
+ trace_start lStartEvent; /* Event marking the begining of the trace */
+ unsigned long int lFlags; /* CPU flags for lock */
+ trace_custom lUserEvent; /* The user event to be logged */
+ trace_change_mask lTraceMask; /* Event mask */
+ trace_new_event lNewUserEvent; /* The event to be created for the user */
+ trace_buffer_start lStartBufferEvent; /* Start of the new buffer event */
+
+#if 0
+ printk("Tracer: Command %d \n", pmCmd);
+#endif
+
+ /* Get device's minor number */
+ lDevMinor = minor(pmInode->i_rdev) & 0x0f;
+
+ /* If the tracer is started, the daemon can't modify the configuration */
+ if ((lDevMinor == 0)
+ && (sTracerStarted == TRUE) && (pmCmd != TRACER_STOP) && (pmCmd != TRACER_DATA_COMITTED))
+ return -EBUSY;
+
+ /* Only some operations are permitted to user processes trying to log events */
+ if ((lDevMinor == 1)
+ && (pmCmd != TRACER_CREATE_USER_EVENT)
+ && (pmCmd != TRACER_DESTROY_USER_EVENT)
+ && (pmCmd != TRACER_TRACE_USER_EVENT)
+ && (pmCmd != TRACER_SET_EVENT_MASK)
+ && (pmCmd != TRACER_GET_EVENT_MASK))
+ return -ENOSYS;
+
+ /* Depending on the command executed */
+ switch (pmCmd) {
+ /* Start the tracer */
+ case TRACER_START:
+ /* Check if the device has been properly set up */
+ if (((sUseSyscallEIPBounds == TRUE)
+ && (sSyscallEIPDepthSet == TRUE))
+ || ((sUseSyscallEIPBounds == TRUE)
+ && ((sLowerEIPBoundSet != TRUE)
+ || (sUpperEIPBoundSet != TRUE)))
+ || ((sTracingPID == TRUE)
+ && (sTracingPGRP == TRUE)))
+ return -EINVAL;
+
+ /* Set the kernel-side trace configuration */
+ if (trace_set_config(trace,
+ sSyscallEIPDepthSet,
+ sUseSyscallEIPBounds,
+ sSyscallEIPDepth,
+ sLowerEIPBound,
+ sUpperEIPBound) < 0)
+ return -EINVAL;
+
+ /* Always log the start event and the buffer start event */
+ ltt_set_bit(TRACE_EV_BUFFER_START, &sTracedEvents);
+ ltt_set_bit(TRACE_EV_BUFFER_START, &sLogEventDetailsMask);
+ ltt_set_bit(TRACE_EV_START, &sTracedEvents);
+ ltt_set_bit(TRACE_EV_START, &sLogEventDetailsMask);
+ ltt_set_bit(TRACE_EV_CHANGE_MASK, &sTracedEvents);
+ ltt_set_bit(TRACE_EV_CHANGE_MASK, &sLogEventDetailsMask);
+
+ /* Get the time of start */
+ do_gettimeofday(&sBufferStartTime);
+
+ /* Set the event description */
+ lStartBufferEvent.ID = sBufferID;
+ lStartBufferEvent.Time = sBufferStartTime;
+
+ /* Set the event description */
+ lStartEvent.MagicNumber = TRACER_MAGIC_NUMBER;
+ lStartEvent.ArchType = TRACE_ARCH_TYPE;
+ lStartEvent.ArchVariant = TRACE_ARCH_VARIANT;
+ lStartEvent.SystemType = TRACE_SYS_TYPE_VANILLA_LINUX;
+ lStartEvent.MajorVersion = TRACER_VERSION_MAJOR;
+ lStartEvent.MinorVersion = TRACER_VERSION_MINOR;
+ lStartEvent.BufferSize = sBufSize;
+ lStartEvent.EventMask = sTracedEvents;
+ lStartEvent.DetailsMask = sLogEventDetailsMask;
+ lStartEvent.LogCPUID = sLogCPUID;
+
+ /* Trace the buffer start event */
+ trace(TRACE_EV_BUFFER_START, &lStartBufferEvent);
+
+ /* Trace the start event */
+ trace(TRACE_EV_START, &lStartEvent);
+
+ /* Start tapping into Linux's syscall flow */
+ syscall_entry_trace_active = ltt_test_bit(TRACE_EV_SYSCALL_ENTRY, &sTracedEvents);
+ syscall_exit_trace_active = ltt_test_bit(TRACE_EV_SYSCALL_EXIT, &sTracedEvents);
+
+ /* We can start tracing */
+ sTracerStarted = TRUE;
+
+ /* Reregister custom trace events created earlier */
+ trace_reregister_custom_events();
+ break;
+
+ /* Stop the tracer */
+ case TRACER_STOP:
+ /* Stop tracing */
+ sTracerStarted = FALSE;
+
+ /* Stop interrupting the normal flow of system calls */
+ syscall_entry_trace_active = 0;
+ syscall_exit_trace_active = 0;
+
+ /* Acquire the lock to avoid SMP case of where another CPU is writing a trace
+ while buffer is being switched */
+ spin_lock_irqsave(&sSpinLock, lFlags);
+
+ /* Switch the buffers to ensure that the end of the buffer mark is set (time isn't important) */
+ tracer_switch_buffers(sBufferStartTime);
+
+ /* Release lock */
+ spin_unlock_irqrestore(&sSpinLock, lFlags);
+ break;
+
+ /* Set the tracer to the default configuration */
+ case TRACER_CONFIG_DEFAULT:
+ tracer_set_default_config();
+ break;
+
+ /* Set the memory buffers the daemon wants us to use */
+ case TRACER_CONFIG_MEMORY_BUFFERS:
+ /* Is the given size "reasonnable" */
+ if (pmArg < TRACER_MIN_BUF_SIZE)
+ return -EINVAL;
+
+ /* Set the buffer's size */
+ return tracer_set_buffer_size(pmArg);
+ break;
+
+ /* Trace the given events */
+ case TRACER_CONFIG_EVENTS:
+ if (copy_from_user(&sTracedEvents, (void *) pmArg, sizeof(sTracedEvents)))
+ return -EFAULT;
+ break;
+
+ /* Record the details of the event, or not */
+ case TRACER_CONFIG_DETAILS:
+ if (copy_from_user(&sLogEventDetailsMask, (void *) pmArg, sizeof(sLogEventDetailsMask)))
+ return -EFAULT;
+ break;
+
+ /* Record the CPUID associated with the event */
+ case TRACER_CONFIG_CPUID:
+ sLogCPUID = TRUE;
+ break;
+
+ /* Trace only one process */
+ case TRACER_CONFIG_PID:
+ sTracingPID = TRUE;
+ sTracedPID = pmArg;
+ break;
+
+ /* Trace only the given process group */
+ case TRACER_CONFIG_PGRP:
+ sTracingPGRP = TRUE;
+ sTracedPGRP = pmArg;
+ break;
+
+ /* Trace the processes of a given group of users */
+ case TRACER_CONFIG_GID:
+ sTracingGID = TRUE;
+ sTracedGID = pmArg;
+ break;
+
+ /* Trace the processes of a given user */
+ case TRACER_CONFIG_UID:
+ sTracingUID = TRUE;
+ sTracedUID = pmArg;
+ break;
+
+ /* Set the call depth a which the EIP should be fetched on syscall */
+ case TRACER_CONFIG_SYSCALL_EIP_DEPTH:
+ sSyscallEIPDepthSet = TRUE;
+ sSyscallEIPDepth = pmArg;
+ break;
+
+ /* Set the lowerbound address from which EIP is recorded on syscall */
+ case TRACER_CONFIG_SYSCALL_EIP_LOWER:
+ /* We are using bounds for fetching the EIP where syscall was made */
+ sUseSyscallEIPBounds = TRUE;
+
+ /* Set the lower bound */
+ sLowerEIPBound = (void *) pmArg;
+
+ /* The lower bound has been set */
+ sLowerEIPBoundSet = TRUE;
+ break;
+
+ /* Set the upperbound address from which EIP is recorded on syscall */
+ case TRACER_CONFIG_SYSCALL_EIP_UPPER:
+ /* We are using bounds for fetching the EIP where syscall was made */
+ sUseSyscallEIPBounds = TRUE;
+
+ /* Set the upper bound */
+ sUpperEIPBound = (void *) pmArg;
+
+ /* The upper bound has been set */
+ sUpperEIPBoundSet = TRUE;
+ break;
+
+ /* The daemon has comitted the last trace */
+ case TRACER_DATA_COMITTED:
+#if 0
+ printk("Tracer: Data has been comitted \n");
+#endif
+
+ /* Safely set the signal sent flag to FALSE */
+ spin_lock_irqsave(&sSpinLock, lFlags);
+ sSignalSent = FALSE;
+ spin_unlock_irqrestore(&sSpinLock, lFlags);
+ break;
+
+ /* Get the number of events lost */
+ case TRACER_GET_EVENTS_LOST:
+ return sEventsLost;
+ break;
+
+ /* Create a user event */
+ case TRACER_CREATE_USER_EVENT:
+ /* Copy the information from user space */
+ if (copy_from_user(&lNewUserEvent, (void *) pmArg, sizeof(lNewUserEvent)))
+ return -EFAULT;
+
+ /* Create the event */
+ lNewUserEventID = trace_create_owned_event(lNewUserEvent.type,
+ lNewUserEvent.desc,
+ lNewUserEvent.format_type,
+ lNewUserEvent.form,
+ current->pid);
+
+ /* Has the operation succeded */
+ if (lNewUserEventID >= 0) {
+ /* Set the event ID */
+ lNewUserEvent.id = lNewUserEventID;
+
+ /* Copy the event information back to user space */
+ if (copy_to_user((void *) pmArg, &lNewUserEvent, sizeof(lNewUserEvent))) {
+ /* Since we were unable to tell the user about the event, destroy it */
+ trace_destroy_event(lNewUserEventID);
+ return -EFAULT;
+ }
+ } else
+ /* Forward trace_create_event()'s error code */
+ return lNewUserEventID;
+ break;
+
+ /* Destroy a user event */
+ case TRACER_DESTROY_USER_EVENT:
+ /* Pass on the user's request */
+ trace_destroy_event((int) pmArg);
+ break;
+
+ /* Trace a user event */
+ case TRACER_TRACE_USER_EVENT:
+ /* Copy the information from user space */
+ if (copy_from_user(&lUserEvent, (void *) pmArg, sizeof(lUserEvent)))
+ return -EFAULT;
+
+ /* Copy the user event data */
+ if (copy_from_user(sUserEventData, lUserEvent.data, lUserEvent.data_size))
+ return -EFAULT;
+
+ /* Log the raw event */
+ lRetValue = trace_raw_event(lUserEvent.id,
+ lUserEvent.data_size,
+ sUserEventData);
+
+ /* Has the operation failed */
+ if (lRetValue < 0)
+ /* Forward trace_create_event()'s error code */
+ return lRetValue;
+ break;
+
+ /* Set event mask */
+ case TRACER_SET_EVENT_MASK:
+ /* Copy the information from user space */
+ if (copy_from_user(&(lTraceMask.mask), (void *) pmArg, sizeof(lTraceMask.mask)))
+ return -EFAULT;
+
+ /* Trace the event */
+ lRetValue = trace(TRACE_EV_CHANGE_MASK, &lTraceMask);
+
+ /* Change the event mask. (This has to be done second or else may loose the
+ information if the user decides to stop logging "change mask" events) */
+ memcpy(&sTracedEvents, &(lTraceMask.mask), sizeof(lTraceMask.mask));
+ syscall_entry_trace_active = ltt_test_bit(TRACE_EV_SYSCALL_ENTRY, &sTracedEvents);
+ syscall_exit_trace_active = ltt_test_bit(TRACE_EV_SYSCALL_EXIT, &sTracedEvents);
+
+ /* Always trace the buffer start, the trace start and the change mask */
+ ltt_set_bit(TRACE_EV_BUFFER_START, &sTracedEvents);
+ ltt_set_bit(TRACE_EV_START, &sTracedEvents);
+ ltt_set_bit(TRACE_EV_CHANGE_MASK, &sTracedEvents);
+
+ /* Forward trace()'s error code */
+ return lRetValue;
+ break;
+
+ /* Get event mask */
+ case TRACER_GET_EVENT_MASK:
+ /* Copy the information to user space */
+ if (copy_to_user((void *) pmArg, &sTracedEvents, sizeof(sTracedEvents)))
+ return -EFAULT;
+ break;
+
+ /* Unknow command */
+ default:
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+/*************************************************************
+ * Function : tracer_mmap()
+ * Description : "Mmap" file op
+ * Parameters :
+ * pmInode, the inode associated with the device
+ * pmFile, file structure given to the acting process
+ * pmVmArea, Virtual memory area description structure
+ * Return values :
+ * 0 if ok
+ * -EAGAIN, when remap failed
+ * -EACCESS, permission denied
+ ************************************************************/
+int tracer_mmap(struct file *pmFile,
+ struct vm_area_struct *pmVmArea)
+{
+ int lRetValue; /* Function's return value */
+
+ /* Only the trace daemon is allowed access to mmap */
+ if (current != sDaemonTaskStruct)
+ return -EACCES;
+
+ /* Remap trace buffer into the process's memory space */
+ lRetValue = tracer_mmap_region(pmVmArea,
+ (char *) pmVmArea->vm_start,
+ sTracBuf,
+ pmVmArea->vm_end - pmVmArea->vm_start);
+
+#if 0
+ printk("Tracer: Trace buffer virtual address => 0x%08X \n", (u32) sTracBuf);
+ printk("Tracer: Trace buffer physical address => 0x%08X \n", (u32) virt_to_phys(sTracBuf));
+ printk("Tracer: Trace buffer virtual address in daemon space => 0x%08X \n", (u32) pmVmArea->vm_start);
+ printk("Tracer: Trace buffer physical address in daemon space => 0x%08X \n", (u32) virt_to_phys((void *) pmVmArea->vm_start));
+#endif
+
+ return lRetValue;
+}
+
+/*************************************************************
+ * Function : tracer_open()
+ * Description : "Open" file op
+ * Parameters :
+ * pmInode, the inode associated with the device
+ * pmFile, file structure given to the acting process
+ * Return values :
+ * 0, everything went OK
+ * -ENODEV, no such device.
+ * -EBUSY, daemon channel (minor number 0) already in use.
+ ************************************************************/
+int tracer_open(struct inode *pmInode,
+ struct file *pmFile)
+{
+ int lDevMinor = minor(pmInode->i_rdev) & 0x0f; /* Device minor number */
+
+ /* Only minor number 0 and 1 are used */
+ if ((lDevMinor > 0) && (lDevMinor != 1))
+ return -ENODEV;
+
+ /* If the device has already been opened */
+ if (sOpenCount) {
+ /* Is there another process trying to open the daemon's channel (minor number 0) */
+ if (lDevMinor == 0)
+ return -EBUSY;
+ else
+ /* Only increment use, this is just another user process trying to log user events */
+ goto IncrementUse;
+ }
+ /* Fetch the task structure of the process that opened the device */
+ sDaemonTaskStruct = current;
+
+ /* Reset the default configuration since this is the daemon and he will complete the setup */
+ tracer_set_default_config();
+
+#if 0
+ /* DEBUG */
+ printk("<1>Process %d opened the tracing device \n", sDaemonTaskStruct->pid);
+#endif
+
+ IncrementUse:
+ /* Lock the device */
+ sOpenCount++;
+
+#ifdef MODULE
+ /* Increment module usage */
+ MOD_INC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+/*************************************************************
+ * Function : tracer_release()
+ * Description : "Release" file op
+ * Parameters :
+ * pmInode, the inode associated with the device
+ * pmFile, file structure given to the acting process
+ * Return values :
+ * 0, everything went OK
+ * Note :
+ * It is assumed that if the tracing daemon dies, exits
+ * or simply stops existing, the kernel or "someone" will
+ * call tracer_release. Otherwise, we're in trouble ...
+ *************************************************************/
+int tracer_release(struct inode *pmInode,
+ struct file *pmFile)
+{
+ int lDevMinor = minor(pmInode->i_rdev) & 0x0f; /* Device minor number */
+
+ /* Is this a simple user process exiting? */
+ if (lDevMinor != 0)
+ goto DecrementUse;
+
+ /* Did we loose any events */
+ if (sEventsLost > 0)
+ printk(KERN_ALERT "Tracer: Lost %d events \n", sEventsLost);
+
+ /* Reset the daemon PID */
+ sDaemonTaskStruct = NULL;
+
+ /* Free the current buffers, if any */
+ if (sTracBuf != NULL)
+ rvfree(sTracBuf, sAllocSize);
+
+ /* Reset the read and write buffers */
+ sTracBuf = NULL;
+ sWritBuf = NULL;
+ sReadBuf = NULL;
+ sWritBufEnd = NULL;
+ sReadBufEnd = NULL;
+ sWritPos = NULL;
+ sReadLimit = NULL;
+ sWritLimit = NULL;
+
+ /* Reset the tracer's configuration */
+ tracer_set_default_config();
+ sTracerStarted = FALSE;
+
+ /* Reset number of bytes recorded and number of events lost */
+ sBufReadComplete = 0;
+ sSizeReadIncomplete = 0;
+ sEventsLost = 0;
+
+ /* Reset signal sent */
+ sSignalSent = FALSE;
+
+ DecrementUse:
+ /* Unlock the device */
+ sOpenCount--;
+
+#ifdef MODULE
+ /* Decrement module usage */
+ MOD_DEC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+/*************************************************************
+ * Function : tracer_fsync()
+ * Description : "Fsync" file op
+ * Parameters :
+ * pmFile, file structure given to the acting process
+ * pmDEntry, dentry associated with file
+ * Return values :
+ * 0, everything went OK
+ * -EACCESS, permission denied
+ * Note :
+ * We need to look the modifications of the values because
+ * they are read and written by trace().
+ * Sonia : ne m oublie pas, je suis toujours a toi....
+ *************************************************************/
+int tracer_fsync(struct file *pmFile,
+ struct dentry *pmDEntry,
+ int pmDataSync)
+{
+ unsigned long int lFlags;
+
+ /* Only the trace daemon is allowed access to fsync */
+ if (current != sDaemonTaskStruct)
+ return -EACCES;
+
+ /* Lock the kernel */
+ spin_lock_irqsave(&sSpinLock, lFlags);
+
+ /* Reset the write positions */
+ sWritPos = sWritBuf;
+
+ /* Reset read limit */
+ sReadLimit = sReadBuf;
+
+ /* Reset bytes recorded */
+ sBufReadComplete = 0;
+ sSizeReadIncomplete = 0;
+ sEventsLost = 0;
+
+ /* Reset signal sent */
+ sSignalSent = FALSE;
+
+ /* Unlock the kernel */
+ spin_unlock_irqrestore(&sSpinLock, lFlags);
+
+ return 0;
+}
+
+/*************************************************************
+ * Function : tracer_set_buffer_size()
+ * Description :
+ * Sets the size of the buffers containing the trace data.
+ * Parameters :
+ * pmSize, Size of buffers
+ * Return values :
+ * 0, Size setting went OK
+ * -ENOMEM, unable to get a hold of memory for tracer
+ *************************************************************/
+int tracer_set_buffer_size(int pmSize)
+{
+ int lSizeAlloc;
+
+ /* Set size to allocate (= pmSize * 2) and fix it's size to be on a page boundary */
+ lSizeAlloc = FIX_SIZE(pmSize << 1);
+
+ /* Free the current buffers, if any */
+ if (sTracBuf != NULL)
+ rvfree(sTracBuf, sAllocSize);
+
+ /* Allocate space for the tracing buffers */
+ if ((sTracBuf = (char *) rvmalloc(lSizeAlloc)) == NULL)
+ return -ENOMEM;
+
+ /* Remember the size set */
+ sBufSize = pmSize;
+ sAllocSize = lSizeAlloc;
+
+ /* Set the read and write buffers */
+ sWritBuf = sTracBuf;
+ sReadBuf = sTracBuf + sBufSize;
+
+ /* Set end of buffers */
+ sWritBufEnd = sWritBuf + sBufSize;
+ sReadBufEnd = sReadBuf + sBufSize;
+
+ /* Set write position */
+ sWritPos = sWritBuf;
+
+ /* Set read limit */
+ sReadLimit = sReadBuf;
+
+ /* Set write limit */
+ sWritLimit = sWritBufEnd - TRACER_LAST_EVENT_SIZE;
+
+ return 0;
+}
+
+/*************************************************************
+ * Function : tracer_set_default_config()
+ * Description : Sets the tracer in its default config
+ * Parameters :
+ * NONE
+ * Return values :
+ * 0, everything went OK
+ * -ENOMEM, unable to get a hold of memory for tracer
+ *************************************************************/
+int tracer_set_default_config(void)
+{
+ int i;
+ int lError = 0;
+
+ /* Initialize the event mask */
+ sTracedEvents = 0;
+
+ /* Initialize the event mask with all existing events with their details */
+ for (i = 0; i <= TRACE_EV_MAX; i++) {
+ ltt_set_bit(i, &sTracedEvents);
+ ltt_set_bit(i, &sLogEventDetailsMask);
+ }
+
+ /* Do not interfere with Linux's syscall flow until we actually start tracing */
+ syscall_entry_trace_active = 0;
+ syscall_exit_trace_active = 0;
+
+ /* Forget about the CPUID */
+ sLogCPUID = FALSE;
+
+ /* We aren't tracing any PID or GID in particular */
+ sTracingPID = FALSE;
+ sTracingPGRP = FALSE;
+ sTracingGID = FALSE;
+ sTracingUID = FALSE;
+
+ /* We aren't looking for a particular call depth */
+ sSyscallEIPDepthSet = FALSE;
+
+ /* We aren't going to place bounds on syscall EIP fetching */
+ sUseSyscallEIPBounds = FALSE;
+ sLowerEIPBoundSet = FALSE;
+ sUpperEIPBoundSet = FALSE;
+
+ /* Set the kernel trace configuration to it's basics */
+ trace_set_config(trace,
+ sSyscallEIPDepthSet,
+ sUseSyscallEIPBounds,
+ 0,
+ 0,
+ 0);
+
+ return lError;
+}
+
+/**************************************************************
+ * Function : tracer_init()
+ * Description : Tracer initialization function.
+ * Parameters :
+ * NONE
+ * Return values :
+ * 0, everything went OK
+ * -ENONMEM, incapable of allocating necessary memory
+ * Forwarded error code otherwise
+ **************************************************************/
+int __init tracer_init(void)
+{
+ int lError = 0;
+
+ /* Initialize configuration */
+ if ((lError = tracer_set_default_config()) < 0)
+ return lError;
+
+ /* Initialize open count */
+ sOpenCount = 0;
+
+ /* Initialize tracer lock */
+ sTracLock = 0;
+
+ /* Initialize signal sent */
+ sSignalSent = FALSE;
+
+ /* Initialize bytes read and events lost */
+ sBufReadComplete = 0;
+ sSizeReadIncomplete = 0;
+ sEventsLost = 0;
+
+ /* Initialize buffer ID */
+ sBufferID = 0;
+
+ /* Initialize tracing daemon task structure */
+ sDaemonTaskStruct = NULL;
+
+ /* Allocate memory for large data components */
+ if ((sUserEventData = vmalloc(CUSTOM_EVENT_MAX_SIZE)) < 0)
+ return -ENOMEM;
+
+ /* Initialize spin lock */
+ sSpinLock = SPIN_LOCK_UNLOCKED;
+
+ /* Register the tracer as a char device */
+ sMajorNumber = register_chrdev(0, TRACER_NAME, &sTracerFileOps);
+
+ /* Register the tracer with the kernel */
+ if ((lError = register_tracer(trace)) < 0) {
+ /* Tell the user about the problem */
+ printk(KERN_ALERT "Tracer: Unable to register tracer with kernel, tracer disabled \n");
+
+ /* Make sure no one can open this device */
+ sOpenCount = 1;
+ } else
+ printk(KERN_INFO "Tracer: Initialization complete \n");
+
+ return lError;
+}
+
+/* Is this loaded as a module */
+#ifdef MODULE
+/**************************************************************
+ * Function : cleanup_module()
+ * Description : Cleanup of the tracer.
+ * Parameters : NONE
+ * Return values : NONE
+ * Note : The order of the unregesterings is important. First,
+ * rule out any possibility of getting more trace
+ * data. Second, rule out any possibility of being read
+ * by the tracing daemon. Last, free the tracing
+ * buffer.
+ **************************************************************/
+void tracer_exit(void)
+{
+ /* Unregister the tracer from the kernel */
+ unregister_tracer(trace);
+
+ /* Unregister the tracer from being a char device */
+ unregister_chrdev(sMajorNumber, TRACER_NAME);
+
+ /* Free the current buffers, if any */
+ if (sTracBuf != NULL)
+ rvfree(sTracBuf, sAllocSize);
+
+ /* Paranoia */
+ sTracBuf = NULL;
+}
+module_exit(tracer_exit);
+#endif /* #ifdef MODULE */
+
+module_init(tracer_init);
diff -urN linux-2.5.33/drivers/trace/tracer.h linux-2.5.33-ltt/drivers/trace/tracer.h
--- linux-2.5.33/drivers/trace/tracer.h Wed Dec 31 19:00:00 1969
+++ linux-2.5.33-ltt/drivers/trace/tracer.h Fri Sep 6 12:03:21 2002
@@ -0,0 +1,160 @@
+/*
+ * drivers/trace/tracer.h
+ *
+ * Copyright (C) 1999, 2000, 2001, 2002 Karim Yaghmour (karim@opersys.com)
+ * Portions contributed by T. Halloran: (C) Copyright 2002 IBM Poughkeepsie, IBM Corporation
+ *
+ * This contains the necessary definitions the system tracer
+ */
+
+#ifndef _TRACER_H
+#define _TRACER_H
+
+/* Logic values */
+#define FALSE 0
+#define TRUE 1
+
+/* Structure packing within the trace */
+#ifndef LTT_PACKED_STRUCT
+#if LTT_UNPACKED_STRUCTS
+#define LTT_PACKED_STRUCT
+#else /* if LTT_UNPACKED_STRUCTS */
+#define LTT_PACKED_STRUCT __attribute__ ((packed));
+#endif /* if LTT_UNPACKED_STRUCTS */
+#endif /* if LTT_PACKED_STRUCT */
+
+/* Tracer properties */
+#define TRACER_NAME "tracer" /* Name of the device as seen in /proc/devices */
+
+/* Tracer buffer information */
+#define TRACER_DEFAULT_BUF_SIZE 50000 /* Default size of tracing buffer */
+#define TRACER_MIN_BUF_SIZE 1000 /* Minimum size of tracing buffer */
+#define TRACER_MAX_BUF_SIZE 500000 /* Maximum size of tracing buffer */
+
+/* Local definitions */
+typedef u32 trace_time_delta; /* The type used to start the time delta between events */
+
+/* Number of bytes set aside for last event */
+#define TRACER_LAST_EVENT_SIZE (sizeof(u8) + sizeof(u8) + sizeof(trace_time_delta) + sizeof(u32))
+
+/* System types */
+#define TRACE_SYS_TYPE_VANILLA_LINUX 1 /* Vanilla linux kernel */
+
+/* The information logged when the tracing is started */
+#define TRACER_MAGIC_NUMBER 0x00D6B7ED /* That day marks an important historical event ... */
+#define TRACER_VERSION_MAJOR 1 /* Major version number */
+#define TRACER_VERSION_MINOR 14 /* Minor version number */
+typedef struct _trace_start {
+ u32 MagicNumber; /* Magic number to identify a trace */
+ u32 ArchType; /* Type of architecture */
+ u32 ArchVariant; /* Variant of the given type of architecture */
+ u32 SystemType; /* Operating system type */
+ u8 MajorVersion; /* Major version of trace */
+ u8 MinorVersion; /* Minor version of trace */
+
+ u32 BufferSize; /* Size of buffers */
+ trace_event_mask EventMask; /* The event mask */
+ trace_event_mask DetailsMask; /* Are the event details logged */
+ u8 LogCPUID; /* Is the CPUID logged */
+} LTT_PACKED_STRUCT trace_start;
+
+/* Start and end of trace buffer information */
+typedef struct _trace_buffer_start {
+ struct timeval Time; /* Time stamp of this buffer */
+ u32 ID; /* Unique buffer ID */
+} LTT_PACKED_STRUCT trace_buffer_start;
+
+/* The configurations possible */
+#define TRACER_START TRACER_MAGIC_NUMBER + 0 /* Start tracing events using the current configuration */
+#define TRACER_STOP TRACER_MAGIC_NUMBER + 1 /* Stop tracing */
+#define TRACER_CONFIG_DEFAULT TRACER_MAGIC_NUMBER + 2 /* Set the tracer to the default configuration */
+#define TRACER_CONFIG_MEMORY_BUFFERS TRACER_MAGIC_NUMBER + 3 /* Set the memory buffers the daemon wants us to use */
+#define TRACER_CONFIG_EVENTS TRACER_MAGIC_NUMBER + 4 /* Trace the given events */
+#define TRACER_CONFIG_DETAILS TRACER_MAGIC_NUMBER + 5 /* Record the details of the event, or not */
+#define TRACER_CONFIG_CPUID TRACER_MAGIC_NUMBER + 6 /* Record the CPUID associated with the event */
+#define TRACER_CONFIG_PID TRACER_MAGIC_NUMBER + 7 /* Trace only one process */
+#define TRACER_CONFIG_PGRP TRACER_MAGIC_NUMBER + 8 /* Trace only the given process group */
+#define TRACER_CONFIG_GID TRACER_MAGIC_NUMBER + 9 /* Trace the processes of a given group of users */
+#define TRACER_CONFIG_UID TRACER_MAGIC_NUMBER + 10 /* Trace the processes of a given user */
+#define TRACER_CONFIG_SYSCALL_EIP_DEPTH TRACER_MAGIC_NUMBER + 11 /* Set the call depth at which the EIP should be fetched on syscall */
+#define TRACER_CONFIG_SYSCALL_EIP_LOWER TRACER_MAGIC_NUMBER + 12 /* Set the lowerbound address from which EIP is recorded on syscall */
+#define TRACER_CONFIG_SYSCALL_EIP_UPPER TRACER_MAGIC_NUMBER + 13 /* Set the upperbound address from which EIP is recorded on syscall */
+#define TRACER_DATA_COMITTED TRACER_MAGIC_NUMBER + 14 /* The daemon has comitted the last trace */
+#define TRACER_GET_EVENTS_LOST TRACER_MAGIC_NUMBER + 15 /* Get the number of events lost */
+#define TRACER_CREATE_USER_EVENT TRACER_MAGIC_NUMBER + 16 /* Create a user tracable event */
+#define TRACER_DESTROY_USER_EVENT TRACER_MAGIC_NUMBER + 17 /* Destroy a user tracable event */
+#define TRACER_TRACE_USER_EVENT TRACER_MAGIC_NUMBER + 18 /* Trace a user event */
+#define TRACER_SET_EVENT_MASK TRACER_MAGIC_NUMBER + 19 /* Set the trace event mask */
+#define TRACER_GET_EVENT_MASK TRACER_MAGIC_NUMBER + 20 /* Get the trace event mask */
+
+extern __inline__ int ltt_set_bit(int nr, void *addr)
+{
+ unsigned char *p = addr;
+ unsigned char mask = 1 << (nr & 7);
+ unsigned char old;
+
+ p += nr >> 3;
+ old = *p;
+ *p |= mask;
+
+ return ((old & mask) != 0);
+}
+
+extern __inline__ int ltt_clear_bit(int nr, void *addr)
+{
+ unsigned char *p = addr;
+ unsigned char mask = 1 << (nr & 7);
+ unsigned char old;
+
+ p += nr >> 3;
+ old = *p;
+ *p &= ~mask;
+
+ return ((old & mask) != 0);
+}
+
+extern __inline__ int ltt_test_bit(int nr, void *addr)
+{
+ unsigned char *p = addr;
+ unsigned char mask = 1 << (nr & 7);
+
+ p += nr >> 3;
+
+ return ((*p & mask) != 0);
+}
+
+/* Function prototypes */
+int trace
+ (u8,
+ void *);
+void tracer_switch_buffers
+ (struct timeval);
+int tracer_ioctl
+ (struct inode *,
+ struct file *,
+ unsigned int,
+ unsigned long);
+int tracer_mmap
+ (struct file *,
+ struct vm_area_struct *);
+int tracer_open
+ (struct inode *,
+ struct file *);
+int tracer_release
+ (struct inode *,
+ struct file *);
+int tracer_fsync
+ (struct file *,
+ struct dentry *,
+ int);
+#ifdef MODULE
+void tracer_exit
+ (void);
+#endif /* #ifdef MODULE */
+int tracer_set_buffer_size
+ (int);
+int tracer_set_default_config
+ (void);
+int tracer_init
+ (void);
+#endif /* _TRACER_H */
reply other threads:[~2002-09-06 22:08 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=3D792926.11530513@opersys.com \
--to=karim@opersys.com \
--cc=linux-kernel@vger.kernel.org \
--cc=ltt-dev@shafik.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.