aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/litmus/debug_trace.h40
-rw-r--r--kernel/printk.c13
-rw-r--r--litmus/Kconfig53
-rw-r--r--litmus/Makefile1
-rw-r--r--litmus/sched_trace.c251
5 files changed, 357 insertions, 1 deletions
diff --git a/include/litmus/debug_trace.h b/include/litmus/debug_trace.h
new file mode 100644
index 000000000000..1266ac6a760c
--- /dev/null
+++ b/include/litmus/debug_trace.h
@@ -0,0 +1,40 @@
1#ifndef LITMUS_DEBUG_TRACE_H
2#define LITMUS_DEBUG_TRACE_H
3
4#ifdef CONFIG_SCHED_DEBUG_TRACE
5void sched_trace_log_message(const char* fmt, ...);
6void dump_trace_buffer(int max);
7#else
8
9#define sched_trace_log_message(fmt, ...)
10
11#endif
12
13extern atomic_t __log_seq_no;
14
15#ifdef CONFIG_SCHED_DEBUG_TRACE_CALLER
16#define TRACE_PREFIX "%d P%d [%s@%s:%d]: "
17#define TRACE_ARGS atomic_add_return(1, &__log_seq_no), \
18 raw_smp_processor_id(), \
19 __FUNCTION__, __FILE__, __LINE__
20#else
21#define TRACE_PREFIX "%d P%d: "
22#define TRACE_ARGS atomic_add_return(1, &__log_seq_no), \
23 raw_smp_processor_id()
24#endif
25
26#define TRACE(fmt, args...) \
27 sched_trace_log_message(TRACE_PREFIX fmt, \
28 TRACE_ARGS, ## args)
29
30#define TRACE_TASK(t, fmt, args...) \
31 TRACE("(%s/%d:%d) " fmt, \
32 t ? (t)->comm : "null", \
33 t ? (t)->pid : 0, \
34 t ? (t)->rt_param.job_params.job_no : 0, \
35 ##args)
36
37#define TRACE_CUR(fmt, args...) \
38 TRACE_TASK(current, fmt, ## args)
39
40#endif
diff --git a/kernel/printk.c b/kernel/printk.c
index d37d45c90ae6..5616e59096a5 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -66,6 +66,13 @@ int console_printk[4] = {
66}; 66};
67 67
68/* 68/*
69 * divert printk() messages when there is a LITMUS^RT debug listener
70 */
71#include <litmus/debug_trace.h>
72int trace_override = 0;
73int trace_recurse = 0;
74
75/*
69 * Low level drivers may need that to know if they can schedule in 76 * Low level drivers may need that to know if they can schedule in
70 * their unblank() callback or not. So let's export it. 77 * their unblank() callback or not. So let's export it.
71 */ 78 */
@@ -1552,6 +1559,10 @@ asmlinkage int vprintk_emit(int facility, int level,
1552 */ 1559 */
1553 text_len = vscnprintf(text, sizeof(textbuf), fmt, args); 1560 text_len = vscnprintf(text, sizeof(textbuf), fmt, args);
1554 1561
1562 /* if LITMUS^RT tracer is active divert printk() msgs */
1563 if (trace_override && !trace_recurse)
1564 TRACE("%s", text);
1565
1555 /* mark and strip a trailing newline */ 1566 /* mark and strip a trailing newline */
1556 if (text_len && text[text_len-1] == '\n') { 1567 if (text_len && text[text_len-1] == '\n') {
1557 text_len--; 1568 text_len--;
@@ -2478,7 +2489,7 @@ static DEFINE_PER_CPU(struct irq_work, wake_up_klogd_work) = {
2478void wake_up_klogd(void) 2489void wake_up_klogd(void)
2479{ 2490{
2480 preempt_disable(); 2491 preempt_disable();
2481 if (waitqueue_active(&log_wait)) { 2492 if (!trace_override && waitqueue_active(&log_wait)) {
2482 this_cpu_or(printk_pending, PRINTK_PENDING_WAKEUP); 2493 this_cpu_or(printk_pending, PRINTK_PENDING_WAKEUP);
2483 irq_work_queue(&__get_cpu_var(wake_up_klogd_work)); 2494 irq_work_queue(&__get_cpu_var(wake_up_klogd_work));
2484 } 2495 }
diff --git a/litmus/Kconfig b/litmus/Kconfig
index 0c7e06b922a1..e4624ee20adc 100644
--- a/litmus/Kconfig
+++ b/litmus/Kconfig
@@ -49,6 +49,59 @@ config SCHED_OVERHEAD_TRACE_SHIFT
49 24 => 512 MB 49 24 => 512 MB
50 26 => 2G MB 50 26 => 2G MB
51 51
52config SCHED_DEBUG_TRACE
53 bool "TRACE() debugging"
54 default n
55 help
56 Include support for sched_trace_log_messageg(), which is used to
57 implement TRACE(). If disabled, no TRACE() messages will be included
58 in the kernel, and no overheads due to debugging statements will be
59 incurred by the scheduler. Disable if the overhead is not acceptable
60 (e.g. benchmarking).
61
62 Say Yes for debugging.
63 Say No for overhead tracing.
64
65config SCHED_DEBUG_TRACE_SHIFT
66 int "Buffer size for TRACE() buffer"
67 depends on SCHED_DEBUG_TRACE
68 range 14 22
69 default 18
70 help
71
72 Select the amount of memory needed per for the TRACE() buffer, as a
73 power of two. The TRACE() buffer is global and statically allocated. If
74 the buffer is too small, there will be holes in the TRACE() log if the
75 buffer-flushing task is starved.
76
77 The default should be sufficient for most systems. Increase the buffer
78 size if the log contains holes. Reduce the buffer size when running on
79 a memory-constrained system.
80
81 Examples: 14 => 16KB
82 18 => 256KB
83 20 => 1MB
84
85 This buffer is exported to usespace using a misc device as
86 'litmus/log'. On a system with default udev rules, a corresponding
87 character device node should be created at /dev/litmus/log. The buffer
88 can be flushed using cat, e.g., 'cat /dev/litmus/log > my_log_file.txt'.
89
90config SCHED_DEBUG_TRACE_CALLER
91 bool "Include [function@file:line] tag in TRACE() log"
92 depends on SCHED_DEBUG_TRACE
93 default n
94 help
95 With this option enabled, TRACE() prepends
96
97 "[<function name>@<filename>:<line number>]"
98
99 to each message in the debug log. Enable this to aid in figuring out
100 what was called in which order. The downside is that it adds a lot of
101 clutter.
102
103 If unsure, say No.
104
52endmenu 105endmenu
53 106
54endmenu 107endmenu
diff --git a/litmus/Makefile b/litmus/Makefile
index 99f90c34f141..07f065f9748f 100644
--- a/litmus/Makefile
+++ b/litmus/Makefile
@@ -3,4 +3,5 @@
3# 3#
4 4
5obj-$(CONFIG_FEATHER_TRACE) += ft_event.o ftdev.o 5obj-$(CONFIG_FEATHER_TRACE) += ft_event.o ftdev.o
6obj-$(CONFIG_SCHED_DEBUG_TRACE) += sched_trace.o
6obj-$(CONFIG_SCHED_OVERHEAD_TRACE) += trace.o 7obj-$(CONFIG_SCHED_OVERHEAD_TRACE) += trace.o
diff --git a/litmus/sched_trace.c b/litmus/sched_trace.c
new file mode 100644
index 000000000000..426a9dd6af65
--- /dev/null
+++ b/litmus/sched_trace.c
@@ -0,0 +1,251 @@
1/*
2 * sched_trace.c -- record scheduling events to a byte stream.
3 */
4#include <linux/spinlock.h>
5#include <linux/mutex.h>
6
7#include <linux/fs.h>
8#include <linux/slab.h>
9#include <linux/miscdevice.h>
10#include <asm/uaccess.h>
11#include <linux/module.h>
12#include <linux/sysrq.h>
13#include <linux/sched.h>
14#include <linux/kfifo.h>
15
16atomic_t __log_seq_no = ATOMIC_INIT(0);
17
18#define SCHED_TRACE_NAME "litmus/log"
19
20/* Compute size of TRACE() buffer */
21#define LITMUS_TRACE_BUF_SIZE (1 << CONFIG_SCHED_DEBUG_TRACE_SHIFT)
22
23/* Max length of one read from the buffer */
24#define MAX_READ_LEN (64 * 1024)
25
26/* Max length for one write --- by TRACE() --- to the buffer. This is used to
27 * allocate a per-cpu buffer for printf() formatting. */
28#define MSG_SIZE 255
29
30
31static DEFINE_MUTEX(reader_mutex);
32static atomic_t reader_cnt = ATOMIC_INIT(0);
33static DEFINE_KFIFO(debug_buffer, char, LITMUS_TRACE_BUF_SIZE);
34
35
36static DEFINE_RAW_SPINLOCK(log_buffer_lock);
37static DEFINE_PER_CPU(char[MSG_SIZE], fmt_buffer);
38
39/*
40 * sched_trace_log_message - Write to the trace buffer (log_buffer)
41 *
42 * This is the only function accessing the log_buffer from inside the
43 * kernel for writing.
44 * Concurrent access to sched_trace_log_message must be serialized using
45 * log_buffer_lock
46 * The maximum length of a formatted message is 255
47 */
48void sched_trace_log_message(const char* fmt, ...)
49{
50 unsigned long flags;
51 va_list args;
52 size_t len;
53 char* buf;
54
55 if (!atomic_read(&reader_cnt))
56 /* early exit if nobody is listening */
57 return;
58
59 va_start(args, fmt);
60 local_irq_save(flags);
61
62 /* format message */
63 buf = __get_cpu_var(fmt_buffer);
64 len = vscnprintf(buf, MSG_SIZE, fmt, args);
65
66 raw_spin_lock(&log_buffer_lock);
67 /* Don't copy the trailing null byte, we don't want null bytes in a
68 * text file.
69 */
70 kfifo_in(&debug_buffer, buf, len);
71 raw_spin_unlock(&log_buffer_lock);
72
73 local_irq_restore(flags);
74 va_end(args);
75}
76
77
78/*
79 * log_read - Read the trace buffer
80 *
81 * This function is called as a file operation from userspace.
82 * Readers can sleep. Access is serialized through reader_mutex
83 */
84static ssize_t log_read(struct file *filp,
85 char __user *to, size_t len,
86 loff_t *f_pos)
87{
88 /* we ignore f_pos, this is strictly sequential */
89
90 ssize_t error = -EINVAL;
91 char* mem;
92
93 if (mutex_lock_interruptible(&reader_mutex)) {
94 error = -ERESTARTSYS;
95 goto out;
96 }
97
98 if (len > MAX_READ_LEN)
99 len = MAX_READ_LEN;
100
101 mem = kmalloc(len, GFP_KERNEL);
102 if (!mem) {
103 error = -ENOMEM;
104 goto out_unlock;
105 }
106
107 error = kfifo_out(&debug_buffer, mem, len);
108 while (!error) {
109 set_current_state(TASK_INTERRUPTIBLE);
110 schedule_timeout(110);
111 if (signal_pending(current))
112 error = -ERESTARTSYS;
113 else
114 error = kfifo_out(&debug_buffer, mem, len);
115 }
116
117 if (error > 0 && copy_to_user(to, mem, error))
118 error = -EFAULT;
119
120 kfree(mem);
121 out_unlock:
122 mutex_unlock(&reader_mutex);
123 out:
124 return error;
125}
126
127/*
128 * Enable redirection of printk() messages to the trace buffer.
129 * Defined in kernel/printk.c
130 */
131extern int trace_override;
132extern int trace_recurse;
133
134/*
135 * log_open - open the global log message ring buffer.
136 */
137static int log_open(struct inode *in, struct file *filp)
138{
139 int error = -EINVAL;
140
141 if (mutex_lock_interruptible(&reader_mutex)) {
142 error = -ERESTARTSYS;
143 goto out;
144 }
145
146 atomic_inc(&reader_cnt);
147 error = 0;
148
149 printk(KERN_DEBUG
150 "sched_trace kfifo with buffer starting at: 0x%p\n",
151 debug_buffer.buf);
152
153 /* override printk() */
154 trace_override++;
155
156 mutex_unlock(&reader_mutex);
157 out:
158 return error;
159}
160
161static int log_release(struct inode *in, struct file *filp)
162{
163 int error = -EINVAL;
164
165 if (mutex_lock_interruptible(&reader_mutex)) {
166 error = -ERESTARTSYS;
167 goto out;
168 }
169
170 atomic_dec(&reader_cnt);
171
172 /* release printk() overriding */
173 trace_override--;
174
175 printk(KERN_DEBUG "sched_trace kfifo released\n");
176
177 mutex_unlock(&reader_mutex);
178 out:
179 return error;
180}
181
182/*
183 * log_fops - The file operations for accessing the global LITMUS log message
184 * buffer.
185 *
186 * Except for opening the device file it uses the same operations as trace_fops.
187 */
188static struct file_operations log_fops = {
189 .owner = THIS_MODULE,
190 .open = log_open,
191 .release = log_release,
192 .read = log_read,
193};
194
195static struct miscdevice litmus_log_dev = {
196 .name = SCHED_TRACE_NAME,
197 .minor = MISC_DYNAMIC_MINOR,
198 .fops = &log_fops,
199};
200
201#ifdef CONFIG_MAGIC_SYSRQ
202void dump_trace_buffer(int max)
203{
204 char line[80];
205 int len;
206 int count = 0;
207
208 /* potential, but very unlikely, race... */
209 trace_recurse = 1;
210 while ((max == 0 || count++ < max) &&
211 (len = kfifo_out(&debug_buffer, line, sizeof(line - 1))) > 0) {
212 line[len] = '\0';
213 printk("%s", line);
214 }
215 trace_recurse = 0;
216}
217
218static void sysrq_dump_trace_buffer(int key)
219{
220 dump_trace_buffer(100);
221}
222
223static struct sysrq_key_op sysrq_dump_trace_buffer_op = {
224 .handler = sysrq_dump_trace_buffer,
225 .help_msg = "dump-trace-buffer(Y)",
226 .action_msg = "writing content of TRACE() buffer",
227};
228#endif
229
230static int __init init_sched_trace(void)
231{
232 printk("Initializing TRACE() device\n");
233
234#ifdef CONFIG_MAGIC_SYSRQ
235 /* offer some debugging help */
236 if (!register_sysrq_key('y', &sysrq_dump_trace_buffer_op))
237 printk("Registered dump-trace-buffer(Y) magic sysrq.\n");
238 else
239 printk("Could not register dump-trace-buffer(Y) magic sysrq.\n");
240#endif
241
242 return misc_register(&litmus_log_dev);
243}
244
245static void __exit exit_sched_trace(void)
246{
247 misc_deregister(&litmus_log_dev);
248}
249
250module_init(init_sched_trace);
251module_exit(exit_sched_trace);