new file mode 100644
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2009 Jiri Olsa <jo...@redhat.com>
+ *
+ * This file is released under GPL version 2.
+ */
+
+#ifndef _LINUX_SIGNALTRACE_H
+#define _LINUX_SIGNALTRACE_H
+
+#ifdef __KERNEL__
+
+#ifdef CONFIG_SIGNAL_TRACER
+void trace_signal(struct task_struct *to, int sig);
+#else
+static inline void trace_signal(struct task_struct *to, int sig)
+{
+}
+#endif
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SIGNALTRACE_H */
@@ -49,6 +49,7 @@
#include <linux/sched.h>
#include <linux/ptrace.h>
#include <linux/security.h>
+#include <linux/signaltrace.h>
struct linux_binprm;
/**
@@ -462,6 +463,19 @@ static inline int tracehook_get_signal(struct task_struct *task,
}
/**
+ * tracehook_send_signal
+ * @to: @current
+ * @sig: number of signal being sent
+ */
+static inline int tracehook_send_signal(struct task_struct *to, int sig)
+{
+#ifdef CONFIG_SIGNAL_TRACER
+ trace_signal(to, sig);
+#endif
+ return 0;
+}
+
+/**
* tracehook_notify_jctl - report about job control stop/continue
* @notify: nonzero if this is the last thread in the group to stop
* @why: %CLD_STOPPED or %CLD_CONTINUED
@@ -842,6 +842,9 @@ static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
return 0;
pending = group ? &t->signal->shared_pending : &t->pending;
+
+ tracehook_send_signal(t, sig);
+
/*
* Short-circuit ignored signals and support queuing
* exactly one non-rt signal, so that we can get more
@@ -396,6 +396,17 @@ config HW_BRANCH_TRACER
This tracer records all branches on the system in a circular
buffer giving access to the last N branches for each cpu.
+config SIGNAL_TRACER
+ bool "Trace signals"
+ select TRACING
+ help
+ This tracer records sent signals in a circular buffer.
+ It is possible to narrow the trace using the "set_ftrace_pid"
+ pid filter file.
+
+ If unsure, say N.
+
+
config KMEMTRACE
bool "Trace SLAB allocations"
select GENERIC_TRACER
@@ -55,5 +55,6 @@ obj-$(CONFIG_FTRACE_SYSCALLS) += trace_syscalls.o
obj-$(CONFIG_EVENT_PROFILE) += trace_event_profile.o
obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o
obj-$(CONFIG_KSYM_TRACER) += trace_ksym.o
+obj-$(CONFIG_SIGNAL_TRACER) += trace_signal.o
libftrace-y := ftrace.o
@@ -44,6 +44,7 @@ enum trace_type {
TRACE_POWER,
TRACE_BLK,
TRACE_KSYM,
+ TRACE_SIGNAL,
__TRACE_LAST_TYPE,
};
@@ -218,6 +219,7 @@ extern void __ftrace_bad_type(void);
IF_ASSIGN(var, ent, struct kmemtrace_free_entry, \
TRACE_KMEM_FREE); \
IF_ASSIGN(var, ent, struct ksym_trace_entry, TRACE_KSYM);\
+ IF_ASSIGN(var, ent, struct signal_trace_entry, TRACE_SIGNAL);\
__ftrace_bad_type(); \
} while (0)
@@ -254,6 +256,13 @@ struct ksym_trace_entry {
char cmd[TASK_COMM_LEN];
};
+/* Signal trace entry. */
+struct signal_trace_entry {
+ struct trace_entry ent;
+ int pid;
+ int sig;
+};
+
/**
* struct tracer - a specific tracer and its callbacks to interact with debugfs
* @name: the name chosen to select it on the available_tracers file
new file mode 100644
@@ -0,0 +1,132 @@
+/*
+ * signal tracer
+ *
+ * Copyright (C) 2009 Jiri Olsa <jo...@redhat.com>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/ftrace.h>
+#include <linux/signaltrace.h>
+
+#include "trace.h"
+
+static char* signal_str[SIGRTMIN + 1] =
+{
+ "UNKNOWN", "SIGHUP", "SIGINT", "SIGQUIT", "SIGILL", "SIGTRAP",
+ "SIGABRT", "SIGBUS", "SIGFPE", "SIGKILL", "SIGUSR1", "SIGSEGV",
+ "SIGUSR2", "SIGPIPE", "SIGALRM", "SIGTERM", "SIGSTKFLT", "SIGCHLD",
+ "SIGCONT", "SIGSTOP", "SIGTSTP", "SIGTTIN", "SIGTTOU", "SIGURG",
+ "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF", "SIGWINCH", "SIGIO",
+ "SIGPWR", "SIGSYS"
+};
+
+static struct trace_array *ctx_trace;
+static int signal_trace_enabled = 0;
+
+void trace_signal(struct task_struct *to, int sig)
+{
+ struct ring_buffer_event *event;
+ struct signal_trace_entry *entry;
+ struct trace_array *tr = ctx_trace;
+
+ if (!ftrace_trace_task(current))
+ return;
+
+ if (!signal_trace_enabled)
+ return;
+
+ event = trace_buffer_lock_reserve(tr->buffer, TRACE_SIGNAL,
+ sizeof(*entry), 0, 0);
+ if (!event)
+ return;
+
+ tracing_record_cmdline(current);
+ tracing_record_cmdline(to);
+
+ entry = ring_buffer_event_data(event);
+ entry->pid = to->pid;
+ entry->sig = sig;
+
+ trace_buffer_unlock_commit(tr->buffer, event, 0, 0);
+}
+
+static void start_signal_trace(void)
+{
+ signal_trace_enabled = 1;
+}
+
+static void stop_signal_trace(void)
+{
+ signal_trace_enabled = 0;
+}
+
+static int signal_trace_init(struct trace_array *tr)
+{
+ ctx_trace = tr;
+ start_signal_trace();
+ return 0;
+}
+
+static void signal_trace_reset(struct trace_array *tr)
+{
+ stop_signal_trace();
+}
+
+static void trace_signal_print_header(struct seq_file *m)
+{
+ seq_printf(m, "# %12s %5s %16s-%-5s %16s-%-5s %14s-%-4s\n",
+ "TIMESTAMP", "CPU#", "FROM", "PID", "TO", "PID", "SIGNAL", "NUM");
+ seq_printf(m, "# %12s %5s %16s %-5s %16s %-5s %14s %-4s\n",
+ " | ", " | ", " |", "| ", " |", "| ", " |", "| ");
+}
+
+static enum print_line_t trace_signal_print_line(struct trace_iterator *iter)
+{
+ char to_comm[TASK_COMM_LEN], from_comm[TASK_COMM_LEN];
+ struct trace_seq *s = &iter->seq;
+ struct trace_entry *tr_entry = iter->ent;
+ struct signal_trace_entry *sig_entry = NULL;
+ int sig;
+ unsigned long long t = ns2usecs(iter->ts);
+ unsigned long usec_rem = do_div(t, USEC_PER_SEC);
+ unsigned long secs = (unsigned long)t;
+
+ trace_assign_type(sig_entry, iter->ent);
+ if (!sig_entry)
+ return TRACE_TYPE_UNHANDLED;
+
+ sig = sig_entry->sig < 0 ? 0 : sig_entry->sig;
+ if (sig > SIGRTMIN)
+ sig = 0;
+
+ trace_find_cmdline(tr_entry->pid, from_comm);
+ trace_find_cmdline(sig_entry->pid, to_comm);
+
+ trace_seq_printf(s, " %5lu.%06lu [%03d] %16s-%-5d %16s-%-5d %14s-%-4d\n",
+ secs, usec_rem, iter->cpu,
+ from_comm, tr_entry->pid,
+ to_comm, sig_entry->pid,
+ signal_str[sig], sig);
+
+ return TRACE_TYPE_HANDLED;
+}
+
+struct tracer signal_trace __read_mostly =
+{
+ .name = "signal",
+ .init = signal_trace_init,
+ .reset = signal_trace_reset,
+ .wait_pipe = poll_wait_pipe,
+ .print_header = trace_signal_print_header,
+ .print_line = trace_signal_print_line,
+};
+
+static __init int init_signal_trace(void)
+{
+ return register_tracer(&signal_trace);
+}
+
+device_initcall(init_signal_trace);