aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--kernel/trace/Makefile1
-rw-r--r--kernel/trace/trace.c275
-rw-r--r--kernel/trace/trace.h8
-rw-r--r--kernel/trace/trace_boot.c1
-rw-r--r--kernel/trace/trace_functions_graph.c1
-rw-r--r--kernel/trace/trace_hw_branches.c1
-rw-r--r--kernel/trace/trace_mmiotrace.c1
-rw-r--r--kernel/trace/trace_output.c365
-rw-r--r--kernel/trace/trace_output.h43
-rw-r--r--kernel/trace/trace_power.c1
10 files changed, 416 insertions, 281 deletions
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index 349d5a93653f..549f93c9b393 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_FUNCTION_TRACER) += libftrace.o
19obj-$(CONFIG_RING_BUFFER) += ring_buffer.o 19obj-$(CONFIG_RING_BUFFER) += ring_buffer.o
20 20
21obj-$(CONFIG_TRACING) += trace.o 21obj-$(CONFIG_TRACING) += trace.o
22obj-$(CONFIG_TRACING) += trace_output.o
22obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o 23obj-$(CONFIG_CONTEXT_SWITCH_TRACER) += trace_sched_switch.o
23obj-$(CONFIG_SYSPROF_TRACER) += trace_sysprof.o 24obj-$(CONFIG_SYSPROF_TRACER) += trace_sysprof.o
24obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o 25obj-$(CONFIG_FUNCTION_TRACER) += trace_functions.o
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index fca0233f1d73..90ce0c1d437b 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -38,6 +38,7 @@
38#include <linux/irqflags.h> 38#include <linux/irqflags.h>
39 39
40#include "trace.h" 40#include "trace.h"
41#include "trace_output.h"
41 42
42#define TRACE_BUFFER_FLAGS (RB_FL_OVERWRITE) 43#define TRACE_BUFFER_FLAGS (RB_FL_OVERWRITE)
43 44
@@ -330,132 +331,6 @@ __update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu)
330 tracing_record_cmdline(current); 331 tracing_record_cmdline(current);
331} 332}
332 333
333/**
334 * trace_seq_printf - sequence printing of trace information
335 * @s: trace sequence descriptor
336 * @fmt: printf format string
337 *
338 * The tracer may use either sequence operations or its own
339 * copy to user routines. To simplify formating of a trace
340 * trace_seq_printf is used to store strings into a special
341 * buffer (@s). Then the output may be either used by
342 * the sequencer or pulled into another buffer.
343 */
344int
345trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
346{
347 int len = (PAGE_SIZE - 1) - s->len;
348 va_list ap;
349 int ret;
350
351 if (!len)
352 return 0;
353
354 va_start(ap, fmt);
355 ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
356 va_end(ap);
357
358 /* If we can't write it all, don't bother writing anything */
359 if (ret >= len)
360 return 0;
361
362 s->len += ret;
363
364 return len;
365}
366
367/**
368 * trace_seq_puts - trace sequence printing of simple string
369 * @s: trace sequence descriptor
370 * @str: simple string to record
371 *
372 * The tracer may use either the sequence operations or its own
373 * copy to user routines. This function records a simple string
374 * into a special buffer (@s) for later retrieval by a sequencer
375 * or other mechanism.
376 */
377static int
378trace_seq_puts(struct trace_seq *s, const char *str)
379{
380 int len = strlen(str);
381
382 if (len > ((PAGE_SIZE - 1) - s->len))
383 return 0;
384
385 memcpy(s->buffer + s->len, str, len);
386 s->len += len;
387
388 return len;
389}
390
391static int
392trace_seq_putc(struct trace_seq *s, unsigned char c)
393{
394 if (s->len >= (PAGE_SIZE - 1))
395 return 0;
396
397 s->buffer[s->len++] = c;
398
399 return 1;
400}
401
402static int
403trace_seq_putmem(struct trace_seq *s, void *mem, size_t len)
404{
405 if (len > ((PAGE_SIZE - 1) - s->len))
406 return 0;
407
408 memcpy(s->buffer + s->len, mem, len);
409 s->len += len;
410
411 return len;
412}
413
414#define MAX_MEMHEX_BYTES 8
415#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1)
416
417static int
418trace_seq_putmem_hex(struct trace_seq *s, void *mem, size_t len)
419{
420 unsigned char hex[HEX_CHARS];
421 unsigned char *data = mem;
422 int i, j;
423
424#ifdef __BIG_ENDIAN
425 for (i = 0, j = 0; i < len; i++) {
426#else
427 for (i = len-1, j = 0; i >= 0; i--) {
428#endif
429 hex[j++] = hex_asc_hi(data[i]);
430 hex[j++] = hex_asc_lo(data[i]);
431 }
432 hex[j++] = ' ';
433
434 return trace_seq_putmem(s, hex, j);
435}
436
437static int
438trace_seq_path(struct trace_seq *s, struct path *path)
439{
440 unsigned char *p;
441
442 if (s->len >= (PAGE_SIZE - 1))
443 return 0;
444 p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len);
445 if (!IS_ERR(p)) {
446 p = mangle_path(s->buffer + s->len, p, "\n");
447 if (p) {
448 s->len = p - s->buffer;
449 return 1;
450 }
451 } else {
452 s->buffer[s->len++] = '?';
453 return 1;
454 }
455
456 return 0;
457}
458
459static void 334static void
460trace_seq_reset(struct trace_seq *s) 335trace_seq_reset(struct trace_seq *s)
461{ 336{
@@ -1473,154 +1348,6 @@ static void s_stop(struct seq_file *m, void *p)
1473 mutex_unlock(&trace_types_lock); 1348 mutex_unlock(&trace_types_lock);
1474} 1349}
1475 1350
1476#ifdef CONFIG_KRETPROBES
1477static inline const char *kretprobed(const char *name)
1478{
1479 static const char tramp_name[] = "kretprobe_trampoline";
1480 int size = sizeof(tramp_name);
1481
1482 if (strncmp(tramp_name, name, size) == 0)
1483 return "[unknown/kretprobe'd]";
1484 return name;
1485}
1486#else
1487static inline const char *kretprobed(const char *name)
1488{
1489 return name;
1490}
1491#endif /* CONFIG_KRETPROBES */
1492
1493static int
1494seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address)
1495{
1496#ifdef CONFIG_KALLSYMS
1497 char str[KSYM_SYMBOL_LEN];
1498 const char *name;
1499
1500 kallsyms_lookup(address, NULL, NULL, NULL, str);
1501
1502 name = kretprobed(str);
1503
1504 return trace_seq_printf(s, fmt, name);
1505#endif
1506 return 1;
1507}
1508
1509static int
1510seq_print_sym_offset(struct trace_seq *s, const char *fmt,
1511 unsigned long address)
1512{
1513#ifdef CONFIG_KALLSYMS
1514 char str[KSYM_SYMBOL_LEN];
1515 const char *name;
1516
1517 sprint_symbol(str, address);
1518 name = kretprobed(str);
1519
1520 return trace_seq_printf(s, fmt, name);
1521#endif
1522 return 1;
1523}
1524
1525#ifndef CONFIG_64BIT
1526# define IP_FMT "%08lx"
1527#else
1528# define IP_FMT "%016lx"
1529#endif
1530
1531int
1532seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags)
1533{
1534 int ret;
1535
1536 if (!ip)
1537 return trace_seq_printf(s, "0");
1538
1539 if (sym_flags & TRACE_ITER_SYM_OFFSET)
1540 ret = seq_print_sym_offset(s, "%s", ip);
1541 else
1542 ret = seq_print_sym_short(s, "%s", ip);
1543
1544 if (!ret)
1545 return 0;
1546
1547 if (sym_flags & TRACE_ITER_SYM_ADDR)
1548 ret = trace_seq_printf(s, " <" IP_FMT ">", ip);
1549 return ret;
1550}
1551
1552static inline int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm,
1553 unsigned long ip, unsigned long sym_flags)
1554{
1555 struct file *file = NULL;
1556 unsigned long vmstart = 0;
1557 int ret = 1;
1558
1559 if (mm) {
1560 const struct vm_area_struct *vma;
1561
1562 down_read(&mm->mmap_sem);
1563 vma = find_vma(mm, ip);
1564 if (vma) {
1565 file = vma->vm_file;
1566 vmstart = vma->vm_start;
1567 }
1568 if (file) {
1569 ret = trace_seq_path(s, &file->f_path);
1570 if (ret)
1571 ret = trace_seq_printf(s, "[+0x%lx]", ip - vmstart);
1572 }
1573 up_read(&mm->mmap_sem);
1574 }
1575 if (ret && ((sym_flags & TRACE_ITER_SYM_ADDR) || !file))
1576 ret = trace_seq_printf(s, " <" IP_FMT ">", ip);
1577 return ret;
1578}
1579
1580static int
1581seq_print_userip_objs(const struct userstack_entry *entry, struct trace_seq *s,
1582 unsigned long sym_flags)
1583{
1584 struct mm_struct *mm = NULL;
1585 int ret = 1;
1586 unsigned int i;
1587
1588 if (trace_flags & TRACE_ITER_SYM_USEROBJ) {
1589 struct task_struct *task;
1590 /*
1591 * we do the lookup on the thread group leader,
1592 * since individual threads might have already quit!
1593 */
1594 rcu_read_lock();
1595 task = find_task_by_vpid(entry->ent.tgid);
1596 if (task)
1597 mm = get_task_mm(task);
1598 rcu_read_unlock();
1599 }
1600
1601 for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
1602 unsigned long ip = entry->caller[i];
1603
1604 if (ip == ULONG_MAX || !ret)
1605 break;
1606 if (i && ret)
1607 ret = trace_seq_puts(s, " <- ");
1608 if (!ip) {
1609 if (ret)
1610 ret = trace_seq_puts(s, "??");
1611 continue;
1612 }
1613 if (!ret)
1614 break;
1615 if (ret)
1616 ret = seq_print_user_ip(s, mm, ip, sym_flags);
1617 }
1618
1619 if (mm)
1620 mmput(mm);
1621 return ret;
1622}
1623
1624static void print_lat_help_header(struct seq_file *m) 1351static void print_lat_help_header(struct seq_file *m)
1625{ 1352{
1626 seq_puts(m, "# _------=> CPU# \n"); 1353 seq_puts(m, "# _------=> CPU# \n");
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 3a357382cce6..6bd71fa1e1c7 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -30,7 +30,7 @@ enum trace_type {
30 TRACE_HW_BRANCHES, 30 TRACE_HW_BRANCHES,
31 TRACE_POWER, 31 TRACE_POWER,
32 32
33 __TRACE_LAST_TYPE 33 __TRACE_LAST_TYPE,
34}; 34};
35 35
36/* 36/*
@@ -484,12 +484,6 @@ extern int trace_selftest_startup_branch(struct tracer *trace,
484#endif /* CONFIG_FTRACE_STARTUP_TEST */ 484#endif /* CONFIG_FTRACE_STARTUP_TEST */
485 485
486extern void *head_page(struct trace_array_cpu *data); 486extern void *head_page(struct trace_array_cpu *data);
487extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...);
488extern int
489seq_print_ip_sym(struct trace_seq *s, unsigned long ip,
490 unsigned long sym_flags);
491extern ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf,
492 size_t cnt);
493extern long ns2usecs(cycle_t nsec); 487extern long ns2usecs(cycle_t nsec);
494extern int 488extern int
495trace_vprintk(unsigned long ip, int depth, const char *fmt, va_list args); 489trace_vprintk(unsigned long ip, int depth, const char *fmt, va_list args);
diff --git a/kernel/trace/trace_boot.c b/kernel/trace/trace_boot.c
index 3ccebde28482..cb2ff3e297b1 100644
--- a/kernel/trace/trace_boot.c
+++ b/kernel/trace/trace_boot.c
@@ -11,6 +11,7 @@
11#include <linux/kallsyms.h> 11#include <linux/kallsyms.h>
12 12
13#include "trace.h" 13#include "trace.h"
14#include "trace_output.h"
14 15
15static struct trace_array *boot_trace; 16static struct trace_array *boot_trace;
16static bool pre_initcalls_finished; 17static bool pre_initcalls_finished;
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index f261966e5b6c..f8ac5417afc8 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -12,6 +12,7 @@
12#include <linux/fs.h> 12#include <linux/fs.h>
13 13
14#include "trace.h" 14#include "trace.h"
15#include "trace_output.h"
15 16
16#define TRACE_GRAPH_INDENT 2 17#define TRACE_GRAPH_INDENT 2
17 18
diff --git a/kernel/trace/trace_hw_branches.c b/kernel/trace/trace_hw_branches.c
index b6a3e20a49a9..879752b006b3 100644
--- a/kernel/trace/trace_hw_branches.c
+++ b/kernel/trace/trace_hw_branches.c
@@ -14,6 +14,7 @@
14#include <asm/ds.h> 14#include <asm/ds.h>
15 15
16#include "trace.h" 16#include "trace.h"
17#include "trace_output.h"
17 18
18 19
19#define SIZEOF_BTS (1 << 13) 20#define SIZEOF_BTS (1 << 13)
diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
index 83f20ae6bd68..fcec59ff2355 100644
--- a/kernel/trace/trace_mmiotrace.c
+++ b/kernel/trace/trace_mmiotrace.c
@@ -11,6 +11,7 @@
11#include <linux/pci.h> 11#include <linux/pci.h>
12 12
13#include "trace.h" 13#include "trace.h"
14#include "trace_output.h"
14 15
15struct header_iter { 16struct header_iter {
16 struct pci_dev *dev; 17 struct pci_dev *dev;
diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c
new file mode 100644
index 000000000000..1f3f80002b5e
--- /dev/null
+++ b/kernel/trace/trace_output.c
@@ -0,0 +1,365 @@
1/*
2 * trace_output.c
3 *
4 * Copyright (C) 2008 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
5 *
6 */
7
8#include <linux/module.h>
9#include <linux/mutex.h>
10#include <linux/ftrace.h>
11
12#include "trace_output.h"
13
14/* must be a power of 2 */
15#define EVENT_HASHSIZE 128
16
17static DEFINE_MUTEX(trace_event_mutex);
18static struct hlist_head event_hash[EVENT_HASHSIZE] __read_mostly;
19
20static int next_event_type = __TRACE_LAST_TYPE + 1;
21
22/**
23 * trace_seq_printf - sequence printing of trace information
24 * @s: trace sequence descriptor
25 * @fmt: printf format string
26 *
27 * The tracer may use either sequence operations or its own
28 * copy to user routines. To simplify formating of a trace
29 * trace_seq_printf is used to store strings into a special
30 * buffer (@s). Then the output may be either used by
31 * the sequencer or pulled into another buffer.
32 */
33int
34trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
35{
36 int len = (PAGE_SIZE - 1) - s->len;
37 va_list ap;
38 int ret;
39
40 if (!len)
41 return 0;
42
43 va_start(ap, fmt);
44 ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
45 va_end(ap);
46
47 /* If we can't write it all, don't bother writing anything */
48 if (ret >= len)
49 return 0;
50
51 s->len += ret;
52
53 return len;
54}
55
56/**
57 * trace_seq_puts - trace sequence printing of simple string
58 * @s: trace sequence descriptor
59 * @str: simple string to record
60 *
61 * The tracer may use either the sequence operations or its own
62 * copy to user routines. This function records a simple string
63 * into a special buffer (@s) for later retrieval by a sequencer
64 * or other mechanism.
65 */
66int trace_seq_puts(struct trace_seq *s, const char *str)
67{
68 int len = strlen(str);
69
70 if (len > ((PAGE_SIZE - 1) - s->len))
71 return 0;
72
73 memcpy(s->buffer + s->len, str, len);
74 s->len += len;
75
76 return len;
77}
78
79int trace_seq_putc(struct trace_seq *s, unsigned char c)
80{
81 if (s->len >= (PAGE_SIZE - 1))
82 return 0;
83
84 s->buffer[s->len++] = c;
85
86 return 1;
87}
88
89int trace_seq_putmem(struct trace_seq *s, void *mem, size_t len)
90{
91 if (len > ((PAGE_SIZE - 1) - s->len))
92 return 0;
93
94 memcpy(s->buffer + s->len, mem, len);
95 s->len += len;
96
97 return len;
98}
99
100int trace_seq_putmem_hex(struct trace_seq *s, void *mem, size_t len)
101{
102 unsigned char hex[HEX_CHARS];
103 unsigned char *data = mem;
104 int i, j;
105
106#ifdef __BIG_ENDIAN
107 for (i = 0, j = 0; i < len; i++) {
108#else
109 for (i = len-1, j = 0; i >= 0; i--) {
110#endif
111 hex[j++] = hex_asc_hi(data[i]);
112 hex[j++] = hex_asc_lo(data[i]);
113 }
114 hex[j++] = ' ';
115
116 return trace_seq_putmem(s, hex, j);
117}
118
119int trace_seq_path(struct trace_seq *s, struct path *path)
120{
121 unsigned char *p;
122
123 if (s->len >= (PAGE_SIZE - 1))
124 return 0;
125 p = d_path(path, s->buffer + s->len, PAGE_SIZE - s->len);
126 if (!IS_ERR(p)) {
127 p = mangle_path(s->buffer + s->len, p, "\n");
128 if (p) {
129 s->len = p - s->buffer;
130 return 1;
131 }
132 } else {
133 s->buffer[s->len++] = '?';
134 return 1;
135 }
136
137 return 0;
138}
139
140#ifdef CONFIG_KRETPROBES
141static inline const char *kretprobed(const char *name)
142{
143 static const char tramp_name[] = "kretprobe_trampoline";
144 int size = sizeof(tramp_name);
145
146 if (strncmp(tramp_name, name, size) == 0)
147 return "[unknown/kretprobe'd]";
148 return name;
149}
150#else
151static inline const char *kretprobed(const char *name)
152{
153 return name;
154}
155#endif /* CONFIG_KRETPROBES */
156
157static int
158seq_print_sym_short(struct trace_seq *s, const char *fmt, unsigned long address)
159{
160#ifdef CONFIG_KALLSYMS
161 char str[KSYM_SYMBOL_LEN];
162 const char *name;
163
164 kallsyms_lookup(address, NULL, NULL, NULL, str);
165
166 name = kretprobed(str);
167
168 return trace_seq_printf(s, fmt, name);
169#endif
170 return 1;
171}
172
173static int
174seq_print_sym_offset(struct trace_seq *s, const char *fmt,
175 unsigned long address)
176{
177#ifdef CONFIG_KALLSYMS
178 char str[KSYM_SYMBOL_LEN];
179 const char *name;
180
181 sprint_symbol(str, address);
182 name = kretprobed(str);
183
184 return trace_seq_printf(s, fmt, name);
185#endif
186 return 1;
187}
188
189#ifndef CONFIG_64BIT
190# define IP_FMT "%08lx"
191#else
192# define IP_FMT "%016lx"
193#endif
194
195int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm,
196 unsigned long ip, unsigned long sym_flags)
197{
198 struct file *file = NULL;
199 unsigned long vmstart = 0;
200 int ret = 1;
201
202 if (mm) {
203 const struct vm_area_struct *vma;
204
205 down_read(&mm->mmap_sem);
206 vma = find_vma(mm, ip);
207 if (vma) {
208 file = vma->vm_file;
209 vmstart = vma->vm_start;
210 }
211 if (file) {
212 ret = trace_seq_path(s, &file->f_path);
213 if (ret)
214 ret = trace_seq_printf(s, "[+0x%lx]",
215 ip - vmstart);
216 }
217 up_read(&mm->mmap_sem);
218 }
219 if (ret && ((sym_flags & TRACE_ITER_SYM_ADDR) || !file))
220 ret = trace_seq_printf(s, " <" IP_FMT ">", ip);
221 return ret;
222}
223
224int
225seq_print_userip_objs(const struct userstack_entry *entry, struct trace_seq *s,
226 unsigned long sym_flags)
227{
228 struct mm_struct *mm = NULL;
229 int ret = 1;
230 unsigned int i;
231
232 if (trace_flags & TRACE_ITER_SYM_USEROBJ) {
233 struct task_struct *task;
234 /*
235 * we do the lookup on the thread group leader,
236 * since individual threads might have already quit!
237 */
238 rcu_read_lock();
239 task = find_task_by_vpid(entry->ent.tgid);
240 if (task)
241 mm = get_task_mm(task);
242 rcu_read_unlock();
243 }
244
245 for (i = 0; i < FTRACE_STACK_ENTRIES; i++) {
246 unsigned long ip = entry->caller[i];
247
248 if (ip == ULONG_MAX || !ret)
249 break;
250 if (i && ret)
251 ret = trace_seq_puts(s, " <- ");
252 if (!ip) {
253 if (ret)
254 ret = trace_seq_puts(s, "??");
255 continue;
256 }
257 if (!ret)
258 break;
259 if (ret)
260 ret = seq_print_user_ip(s, mm, ip, sym_flags);
261 }
262
263 if (mm)
264 mmput(mm);
265 return ret;
266}
267
268int
269seq_print_ip_sym(struct trace_seq *s, unsigned long ip, unsigned long sym_flags)
270{
271 int ret;
272
273 if (!ip)
274 return trace_seq_printf(s, "0");
275
276 if (sym_flags & TRACE_ITER_SYM_OFFSET)
277 ret = seq_print_sym_offset(s, "%s", ip);
278 else
279 ret = seq_print_sym_short(s, "%s", ip);
280
281 if (!ret)
282 return 0;
283
284 if (sym_flags & TRACE_ITER_SYM_ADDR)
285 ret = trace_seq_printf(s, " <" IP_FMT ">", ip);
286 return ret;
287}
288
289/**
290 * ftrace_find_event - find a registered event
291 * @type: the type of event to look for
292 *
293 * Returns an event of type @type otherwise NULL
294 */
295struct trace_event *ftrace_find_event(int type)
296{
297 struct trace_event *event;
298 struct hlist_node *n;
299 unsigned key;
300
301 key = type & (EVENT_HASHSIZE - 1);
302
303 hlist_for_each_entry_rcu(event, n, &event_hash[key], node) {
304 if (event->type == type)
305 return event;
306 }
307
308 return NULL;
309}
310
311/**
312 * register_ftrace_event - register output for an event type
313 * @event: the event type to register
314 *
315 * Event types are stored in a hash and this hash is used to
316 * find a way to print an event. If the @event->type is set
317 * then it will use that type, otherwise it will assign a
318 * type to use.
319 *
320 * If you assign your own type, please make sure it is added
321 * to the trace_type enum in trace.h, to avoid collisions
322 * with the dynamic types.
323 *
324 * Returns the event type number or zero on error.
325 */
326int register_ftrace_event(struct trace_event *event)
327{
328 unsigned key;
329 int ret = 0;
330
331 mutex_lock(&trace_event_mutex);
332
333 if (!event->type)
334 event->type = next_event_type++;
335 else if (event->type > __TRACE_LAST_TYPE) {
336 printk(KERN_WARNING "Need to add type to trace.h\n");
337 WARN_ON(1);
338 }
339
340 if (ftrace_find_event(event->type))
341 goto out;
342
343 key = event->type & (EVENT_HASHSIZE - 1);
344
345 hlist_add_head_rcu(&event->node, &event_hash[key]);
346
347 ret = event->type;
348 out:
349 mutex_unlock(&trace_event_mutex);
350
351 return ret;
352}
353
354/**
355 * unregister_ftrace_event - remove a no longer used event
356 * @event: the event to remove
357 */
358int unregister_ftrace_event(struct trace_event *event)
359{
360 mutex_lock(&trace_event_mutex);
361 hlist_del(&event->node);
362 mutex_unlock(&trace_event_mutex);
363
364 return 0;
365}
diff --git a/kernel/trace/trace_output.h b/kernel/trace/trace_output.h
new file mode 100644
index 000000000000..1fcc76e1378e
--- /dev/null
+++ b/kernel/trace/trace_output.h
@@ -0,0 +1,43 @@
1#ifndef __TRACE_EVENTS_H
2#define __TRACE_EVENTS_H
3
4#include "trace.h"
5
6typedef int (*trace_print_func)(struct trace_seq *s, struct trace_entry *entry,
7 int flags);
8
9struct trace_event {
10 struct hlist_node node;
11 int type;
12 trace_print_func trace;
13 trace_print_func latency_trace;
14 trace_print_func raw;
15 trace_print_func hex;
16 trace_print_func binary;
17};
18
19extern int trace_seq_printf(struct trace_seq *s, const char *fmt, ...);
20extern int
21seq_print_ip_sym(struct trace_seq *s, unsigned long ip,
22 unsigned long sym_flags);
23extern ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf,
24 size_t cnt);
25int trace_seq_puts(struct trace_seq *s, const char *str);
26int trace_seq_putc(struct trace_seq *s, unsigned char c);
27int trace_seq_putmem(struct trace_seq *s, void *mem, size_t len);
28int trace_seq_putmem_hex(struct trace_seq *s, void *mem, size_t len);
29int trace_seq_path(struct trace_seq *s, struct path *path);
30int seq_print_userip_objs(const struct userstack_entry *entry,
31 struct trace_seq *s, unsigned long sym_flags);
32int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm,
33 unsigned long ip, unsigned long sym_flags);
34
35struct trace_event *ftrace_find_event(int type);
36int register_ftrace_event(struct trace_event *event);
37int unregister_ftrace_event(struct trace_event *event);
38
39#define MAX_MEMHEX_BYTES 8
40#define HEX_CHARS (MAX_MEMHEX_BYTES*2 + 1)
41
42#endif
43
diff --git a/kernel/trace/trace_power.c b/kernel/trace/trace_power.c
index a7172a352f62..b9b13c39b4bb 100644
--- a/kernel/trace/trace_power.c
+++ b/kernel/trace/trace_power.c
@@ -16,6 +16,7 @@
16#include <linux/module.h> 16#include <linux/module.h>
17 17
18#include "trace.h" 18#include "trace.h"
19#include "trace_output.h"
19 20
20static struct trace_array *power_trace; 21static struct trace_array *power_trace;
21static int __read_mostly trace_power_enabled; 22static int __read_mostly trace_power_enabled;