diff options
| author | Ingo Molnar <mingo@elte.hu> | 2009-02-17 05:31:01 -0500 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-02-17 05:31:01 -0500 |
| commit | f492d3f8385a98f828e8220d14492337dc29e07b (patch) | |
| tree | 31f22c83f7e45388955aacb45c425d7cb6a4d78c | |
| parent | c4e2b432d5b57e2faaeea048079b31c243079647 (diff) | |
| parent | e110e3d1eaa0f9628918be67ddd32e8ad65a2871 (diff) | |
Merge branch 'tip/tracing/ftrace' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-2.6-trace into tracing/ftrace
| -rw-r--r-- | include/linux/ftrace.h | 40 | ||||
| -rw-r--r-- | include/linux/ring_buffer.h | 2 | ||||
| -rw-r--r-- | kernel/trace/ftrace.c | 929 | ||||
| -rw-r--r-- | kernel/trace/ring_buffer.c | 9 | ||||
| -rw-r--r-- | kernel/trace/trace_functions.c | 163 |
5 files changed, 946 insertions, 197 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index 106b7909d500..b331e216d8a1 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h | |||
| @@ -95,10 +95,41 @@ stack_trace_sysctl(struct ctl_table *table, int write, | |||
| 95 | loff_t *ppos); | 95 | loff_t *ppos); |
| 96 | #endif | 96 | #endif |
| 97 | 97 | ||
| 98 | struct ftrace_func_command { | ||
| 99 | struct list_head list; | ||
| 100 | char *name; | ||
| 101 | int (*func)(char *func, char *cmd, | ||
| 102 | char *params, int enable); | ||
| 103 | }; | ||
| 104 | |||
| 98 | #ifdef CONFIG_DYNAMIC_FTRACE | 105 | #ifdef CONFIG_DYNAMIC_FTRACE |
| 99 | /* asm/ftrace.h must be defined for archs supporting dynamic ftrace */ | 106 | /* asm/ftrace.h must be defined for archs supporting dynamic ftrace */ |
| 100 | #include <asm/ftrace.h> | 107 | #include <asm/ftrace.h> |
| 101 | 108 | ||
| 109 | struct seq_file; | ||
| 110 | |||
| 111 | struct ftrace_hook_ops { | ||
| 112 | void (*func)(unsigned long ip, | ||
| 113 | unsigned long parent_ip, | ||
| 114 | void **data); | ||
| 115 | int (*callback)(unsigned long ip, void **data); | ||
| 116 | void (*free)(void **data); | ||
| 117 | int (*print)(struct seq_file *m, | ||
| 118 | unsigned long ip, | ||
| 119 | struct ftrace_hook_ops *ops, | ||
| 120 | void *data); | ||
| 121 | }; | ||
| 122 | |||
| 123 | extern int | ||
| 124 | register_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops, | ||
| 125 | void *data); | ||
| 126 | extern void | ||
| 127 | unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops, | ||
| 128 | void *data); | ||
| 129 | extern void | ||
| 130 | unregister_ftrace_function_hook_func(char *glob, struct ftrace_hook_ops *ops); | ||
| 131 | extern void unregister_ftrace_function_hook_all(char *glob); | ||
| 132 | |||
| 102 | enum { | 133 | enum { |
| 103 | FTRACE_FL_FREE = (1 << 0), | 134 | FTRACE_FL_FREE = (1 << 0), |
| 104 | FTRACE_FL_FAILED = (1 << 1), | 135 | FTRACE_FL_FAILED = (1 << 1), |
| @@ -119,6 +150,9 @@ struct dyn_ftrace { | |||
| 119 | int ftrace_force_update(void); | 150 | int ftrace_force_update(void); |
| 120 | void ftrace_set_filter(unsigned char *buf, int len, int reset); | 151 | void ftrace_set_filter(unsigned char *buf, int len, int reset); |
| 121 | 152 | ||
| 153 | int register_ftrace_command(struct ftrace_func_command *cmd); | ||
| 154 | int unregister_ftrace_command(struct ftrace_func_command *cmd); | ||
| 155 | |||
| 122 | /* defined in arch */ | 156 | /* defined in arch */ |
| 123 | extern int ftrace_ip_converted(unsigned long ip); | 157 | extern int ftrace_ip_converted(unsigned long ip); |
| 124 | extern int ftrace_dyn_arch_init(void *data); | 158 | extern int ftrace_dyn_arch_init(void *data); |
| @@ -202,6 +236,12 @@ extern void ftrace_enable_daemon(void); | |||
| 202 | # define ftrace_disable_daemon() do { } while (0) | 236 | # define ftrace_disable_daemon() do { } while (0) |
| 203 | # define ftrace_enable_daemon() do { } while (0) | 237 | # define ftrace_enable_daemon() do { } while (0) |
| 204 | static inline void ftrace_release(void *start, unsigned long size) { } | 238 | static inline void ftrace_release(void *start, unsigned long size) { } |
| 239 | static inline int register_ftrace_command(struct ftrace_func_command *cmd) | ||
| 240 | { | ||
| 241 | } | ||
| 242 | static inline int unregister_ftrace_command(char *cmd_name) | ||
| 243 | { | ||
| 244 | } | ||
| 205 | #endif /* CONFIG_DYNAMIC_FTRACE */ | 245 | #endif /* CONFIG_DYNAMIC_FTRACE */ |
| 206 | 246 | ||
| 207 | /* totally disable ftrace - can not re-enable after this */ | 247 | /* totally disable ftrace - can not re-enable after this */ |
diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h index 8e6646a54acf..f5e793d69bd3 100644 --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h | |||
| @@ -128,10 +128,12 @@ void ring_buffer_normalize_time_stamp(int cpu, u64 *ts); | |||
| 128 | void tracing_on(void); | 128 | void tracing_on(void); |
| 129 | void tracing_off(void); | 129 | void tracing_off(void); |
| 130 | void tracing_off_permanent(void); | 130 | void tracing_off_permanent(void); |
| 131 | int tracing_is_on(void); | ||
| 131 | #else | 132 | #else |
| 132 | static inline void tracing_on(void) { } | 133 | static inline void tracing_on(void) { } |
| 133 | static inline void tracing_off(void) { } | 134 | static inline void tracing_off(void) { } |
| 134 | static inline void tracing_off_permanent(void) { } | 135 | static inline void tracing_off_permanent(void) { } |
| 136 | static inline int tracing_is_on(void) { return 0; } | ||
| 135 | #endif | 137 | #endif |
| 136 | 138 | ||
| 137 | void *ring_buffer_alloc_read_page(struct ring_buffer *buffer); | 139 | void *ring_buffer_alloc_read_page(struct ring_buffer *buffer); |
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 1796e018fbff..6533c1d20155 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include <linux/sysctl.h> | 27 | #include <linux/sysctl.h> |
| 28 | #include <linux/ctype.h> | 28 | #include <linux/ctype.h> |
| 29 | #include <linux/list.h> | 29 | #include <linux/list.h> |
| 30 | #include <linux/hash.h> | ||
| 30 | 31 | ||
| 31 | #include <asm/ftrace.h> | 32 | #include <asm/ftrace.h> |
| 32 | 33 | ||
| @@ -44,14 +45,14 @@ | |||
| 44 | ftrace_kill(); \ | 45 | ftrace_kill(); \ |
| 45 | } while (0) | 46 | } while (0) |
| 46 | 47 | ||
| 48 | /* hash bits for specific function selection */ | ||
| 49 | #define FTRACE_HASH_BITS 7 | ||
| 50 | #define FTRACE_FUNC_HASHSIZE (1 << FTRACE_HASH_BITS) | ||
| 51 | |||
| 47 | /* ftrace_enabled is a method to turn ftrace on or off */ | 52 | /* ftrace_enabled is a method to turn ftrace on or off */ |
| 48 | int ftrace_enabled __read_mostly; | 53 | int ftrace_enabled __read_mostly; |
| 49 | static int last_ftrace_enabled; | 54 | static int last_ftrace_enabled; |
| 50 | 55 | ||
| 51 | /* set when tracing only a pid */ | ||
| 52 | struct pid *ftrace_pid_trace; | ||
| 53 | static struct pid * const ftrace_swapper_pid = &init_struct_pid; | ||
| 54 | |||
| 55 | /* Quick disabling of function tracer. */ | 56 | /* Quick disabling of function tracer. */ |
| 56 | int function_trace_stop; | 57 | int function_trace_stop; |
| 57 | 58 | ||
| @@ -61,9 +62,7 @@ int function_trace_stop; | |||
| 61 | */ | 62 | */ |
| 62 | static int ftrace_disabled __read_mostly; | 63 | static int ftrace_disabled __read_mostly; |
| 63 | 64 | ||
| 64 | static DEFINE_SPINLOCK(ftrace_lock); | 65 | static DEFINE_MUTEX(ftrace_lock); |
| 65 | static DEFINE_MUTEX(ftrace_sysctl_lock); | ||
| 66 | static DEFINE_MUTEX(ftrace_start_lock); | ||
| 67 | 66 | ||
| 68 | static struct ftrace_ops ftrace_list_end __read_mostly = | 67 | static struct ftrace_ops ftrace_list_end __read_mostly = |
| 69 | { | 68 | { |
| @@ -134,9 +133,6 @@ static void ftrace_test_stop_func(unsigned long ip, unsigned long parent_ip) | |||
| 134 | 133 | ||
| 135 | static int __register_ftrace_function(struct ftrace_ops *ops) | 134 | static int __register_ftrace_function(struct ftrace_ops *ops) |
| 136 | { | 135 | { |
| 137 | /* should not be called from interrupt context */ | ||
| 138 | spin_lock(&ftrace_lock); | ||
| 139 | |||
| 140 | ops->next = ftrace_list; | 136 | ops->next = ftrace_list; |
| 141 | /* | 137 | /* |
| 142 | * We are entering ops into the ftrace_list but another | 138 | * We are entering ops into the ftrace_list but another |
| @@ -172,18 +168,12 @@ static int __register_ftrace_function(struct ftrace_ops *ops) | |||
| 172 | #endif | 168 | #endif |
| 173 | } | 169 | } |
| 174 | 170 | ||
| 175 | spin_unlock(&ftrace_lock); | ||
| 176 | |||
| 177 | return 0; | 171 | return 0; |
| 178 | } | 172 | } |
| 179 | 173 | ||
| 180 | static int __unregister_ftrace_function(struct ftrace_ops *ops) | 174 | static int __unregister_ftrace_function(struct ftrace_ops *ops) |
| 181 | { | 175 | { |
| 182 | struct ftrace_ops **p; | 176 | struct ftrace_ops **p; |
| 183 | int ret = 0; | ||
| 184 | |||
| 185 | /* should not be called from interrupt context */ | ||
| 186 | spin_lock(&ftrace_lock); | ||
| 187 | 177 | ||
| 188 | /* | 178 | /* |
| 189 | * If we are removing the last function, then simply point | 179 | * If we are removing the last function, then simply point |
| @@ -192,17 +182,15 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) | |||
| 192 | if (ftrace_list == ops && ops->next == &ftrace_list_end) { | 182 | if (ftrace_list == ops && ops->next == &ftrace_list_end) { |
| 193 | ftrace_trace_function = ftrace_stub; | 183 | ftrace_trace_function = ftrace_stub; |
| 194 | ftrace_list = &ftrace_list_end; | 184 | ftrace_list = &ftrace_list_end; |
| 195 | goto out; | 185 | return 0; |
| 196 | } | 186 | } |
| 197 | 187 | ||
| 198 | for (p = &ftrace_list; *p != &ftrace_list_end; p = &(*p)->next) | 188 | for (p = &ftrace_list; *p != &ftrace_list_end; p = &(*p)->next) |
| 199 | if (*p == ops) | 189 | if (*p == ops) |
| 200 | break; | 190 | break; |
| 201 | 191 | ||
| 202 | if (*p != ops) { | 192 | if (*p != ops) |
| 203 | ret = -1; | 193 | return -1; |
| 204 | goto out; | ||
| 205 | } | ||
| 206 | 194 | ||
| 207 | *p = (*p)->next; | 195 | *p = (*p)->next; |
| 208 | 196 | ||
| @@ -223,18 +211,14 @@ static int __unregister_ftrace_function(struct ftrace_ops *ops) | |||
| 223 | } | 211 | } |
| 224 | } | 212 | } |
| 225 | 213 | ||
| 226 | out: | 214 | return 0; |
| 227 | spin_unlock(&ftrace_lock); | ||
| 228 | |||
| 229 | return ret; | ||
| 230 | } | 215 | } |
| 231 | 216 | ||
| 232 | static void ftrace_update_pid_func(void) | 217 | static void ftrace_update_pid_func(void) |
| 233 | { | 218 | { |
| 234 | ftrace_func_t func; | 219 | ftrace_func_t func; |
| 235 | 220 | ||
| 236 | /* should not be called from interrupt context */ | 221 | mutex_lock(&ftrace_lock); |
| 237 | spin_lock(&ftrace_lock); | ||
| 238 | 222 | ||
| 239 | if (ftrace_trace_function == ftrace_stub) | 223 | if (ftrace_trace_function == ftrace_stub) |
| 240 | goto out; | 224 | goto out; |
| @@ -256,7 +240,7 @@ static void ftrace_update_pid_func(void) | |||
| 256 | #endif | 240 | #endif |
| 257 | 241 | ||
| 258 | out: | 242 | out: |
| 259 | spin_unlock(&ftrace_lock); | 243 | mutex_unlock(&ftrace_lock); |
| 260 | } | 244 | } |
| 261 | 245 | ||
| 262 | #ifdef CONFIG_DYNAMIC_FTRACE | 246 | #ifdef CONFIG_DYNAMIC_FTRACE |
| @@ -264,6 +248,21 @@ static void ftrace_update_pid_func(void) | |||
| 264 | # error Dynamic ftrace depends on MCOUNT_RECORD | 248 | # error Dynamic ftrace depends on MCOUNT_RECORD |
| 265 | #endif | 249 | #endif |
| 266 | 250 | ||
| 251 | /* set when tracing only a pid */ | ||
| 252 | struct pid *ftrace_pid_trace; | ||
| 253 | static struct pid * const ftrace_swapper_pid = &init_struct_pid; | ||
| 254 | static struct hlist_head ftrace_func_hash[FTRACE_FUNC_HASHSIZE] __read_mostly; | ||
| 255 | |||
| 256 | struct ftrace_func_hook { | ||
| 257 | struct hlist_node node; | ||
| 258 | struct ftrace_hook_ops *ops; | ||
| 259 | unsigned long flags; | ||
| 260 | unsigned long ip; | ||
| 261 | void *data; | ||
| 262 | struct rcu_head rcu; | ||
| 263 | }; | ||
| 264 | |||
| 265 | |||
| 267 | enum { | 266 | enum { |
| 268 | FTRACE_ENABLE_CALLS = (1 << 0), | 267 | FTRACE_ENABLE_CALLS = (1 << 0), |
| 269 | FTRACE_DISABLE_CALLS = (1 << 1), | 268 | FTRACE_DISABLE_CALLS = (1 << 1), |
| @@ -297,6 +296,19 @@ static struct ftrace_page *ftrace_pages; | |||
| 297 | 296 | ||
| 298 | static struct dyn_ftrace *ftrace_free_records; | 297 | static struct dyn_ftrace *ftrace_free_records; |
| 299 | 298 | ||
| 299 | /* | ||
| 300 | * This is a double for. Do not use 'break' to break out of the loop, | ||
| 301 | * you must use a goto. | ||
| 302 | */ | ||
| 303 | #define do_for_each_ftrace_rec(pg, rec) \ | ||
| 304 | for (pg = ftrace_pages_start; pg; pg = pg->next) { \ | ||
| 305 | int _____i; \ | ||
| 306 | for (_____i = 0; _____i < pg->index; _____i++) { \ | ||
| 307 | rec = &pg->records[_____i]; | ||
| 308 | |||
| 309 | #define while_for_each_ftrace_rec() \ | ||
| 310 | } \ | ||
| 311 | } | ||
| 300 | 312 | ||
| 301 | #ifdef CONFIG_KPROBES | 313 | #ifdef CONFIG_KPROBES |
| 302 | 314 | ||
| @@ -341,23 +353,16 @@ void ftrace_release(void *start, unsigned long size) | |||
| 341 | struct ftrace_page *pg; | 353 | struct ftrace_page *pg; |
| 342 | unsigned long s = (unsigned long)start; | 354 | unsigned long s = (unsigned long)start; |
| 343 | unsigned long e = s + size; | 355 | unsigned long e = s + size; |
| 344 | int i; | ||
| 345 | 356 | ||
| 346 | if (ftrace_disabled || !start) | 357 | if (ftrace_disabled || !start) |
| 347 | return; | 358 | return; |
| 348 | 359 | ||
| 349 | /* should not be called from interrupt context */ | 360 | mutex_lock(&ftrace_lock); |
| 350 | spin_lock(&ftrace_lock); | 361 | do_for_each_ftrace_rec(pg, rec) { |
| 351 | 362 | if ((rec->ip >= s) && (rec->ip < e)) | |
| 352 | for (pg = ftrace_pages_start; pg; pg = pg->next) { | 363 | ftrace_free_rec(rec); |
| 353 | for (i = 0; i < pg->index; i++) { | 364 | } while_for_each_ftrace_rec(); |
| 354 | rec = &pg->records[i]; | 365 | mutex_unlock(&ftrace_lock); |
| 355 | |||
| 356 | if ((rec->ip >= s) && (rec->ip < e)) | ||
| 357 | ftrace_free_rec(rec); | ||
| 358 | } | ||
| 359 | } | ||
| 360 | spin_unlock(&ftrace_lock); | ||
| 361 | } | 366 | } |
| 362 | 367 | ||
| 363 | static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) | 368 | static struct dyn_ftrace *ftrace_alloc_dyn_node(unsigned long ip) |
| @@ -523,41 +528,37 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable) | |||
| 523 | 528 | ||
| 524 | static void ftrace_replace_code(int enable) | 529 | static void ftrace_replace_code(int enable) |
| 525 | { | 530 | { |
| 526 | int i, failed; | 531 | int failed; |
| 527 | struct dyn_ftrace *rec; | 532 | struct dyn_ftrace *rec; |
| 528 | struct ftrace_page *pg; | 533 | struct ftrace_page *pg; |
| 529 | 534 | ||
| 530 | for (pg = ftrace_pages_start; pg; pg = pg->next) { | 535 | do_for_each_ftrace_rec(pg, rec) { |
| 531 | for (i = 0; i < pg->index; i++) { | 536 | /* |
| 532 | rec = &pg->records[i]; | 537 | * Skip over free records and records that have |
| 533 | 538 | * failed. | |
| 534 | /* | 539 | */ |
| 535 | * Skip over free records and records that have | 540 | if (rec->flags & FTRACE_FL_FREE || |
| 536 | * failed. | 541 | rec->flags & FTRACE_FL_FAILED) |
| 537 | */ | 542 | continue; |
| 538 | if (rec->flags & FTRACE_FL_FREE || | ||
| 539 | rec->flags & FTRACE_FL_FAILED) | ||
| 540 | continue; | ||
| 541 | 543 | ||
| 542 | /* ignore updates to this record's mcount site */ | 544 | /* ignore updates to this record's mcount site */ |
| 543 | if (get_kprobe((void *)rec->ip)) { | 545 | if (get_kprobe((void *)rec->ip)) { |
| 544 | freeze_record(rec); | 546 | freeze_record(rec); |
| 545 | continue; | 547 | continue; |
| 546 | } else { | 548 | } else { |
| 547 | unfreeze_record(rec); | 549 | unfreeze_record(rec); |
| 548 | } | 550 | } |
| 549 | 551 | ||
| 550 | failed = __ftrace_replace_code(rec, enable); | 552 | failed = __ftrace_replace_code(rec, enable); |
| 551 | if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { | 553 | if (failed && (rec->flags & FTRACE_FL_CONVERTED)) { |
| 552 | rec->flags |= FTRACE_FL_FAILED; | 554 | rec->flags |= FTRACE_FL_FAILED; |
| 553 | if ((system_state == SYSTEM_BOOTING) || | 555 | if ((system_state == SYSTEM_BOOTING) || |
| 554 | !core_kernel_text(rec->ip)) { | 556 | !core_kernel_text(rec->ip)) { |
| 555 | ftrace_free_rec(rec); | 557 | ftrace_free_rec(rec); |
| 556 | } else | 558 | } else |
| 557 | ftrace_bug(failed, rec->ip); | 559 | ftrace_bug(failed, rec->ip); |
| 558 | } | ||
| 559 | } | 560 | } |
| 560 | } | 561 | } while_for_each_ftrace_rec(); |
| 561 | } | 562 | } |
| 562 | 563 | ||
| 563 | static int | 564 | static int |
| @@ -623,13 +624,10 @@ static void ftrace_startup(int command) | |||
| 623 | if (unlikely(ftrace_disabled)) | 624 | if (unlikely(ftrace_disabled)) |
| 624 | return; | 625 | return; |
| 625 | 626 | ||
| 626 | mutex_lock(&ftrace_start_lock); | ||
| 627 | ftrace_start_up++; | 627 | ftrace_start_up++; |
| 628 | command |= FTRACE_ENABLE_CALLS; | 628 | command |= FTRACE_ENABLE_CALLS; |
| 629 | 629 | ||
| 630 | ftrace_startup_enable(command); | 630 | ftrace_startup_enable(command); |
| 631 | |||
| 632 | mutex_unlock(&ftrace_start_lock); | ||
| 633 | } | 631 | } |
| 634 | 632 | ||
| 635 | static void ftrace_shutdown(int command) | 633 | static void ftrace_shutdown(int command) |
| @@ -637,7 +635,6 @@ static void ftrace_shutdown(int command) | |||
| 637 | if (unlikely(ftrace_disabled)) | 635 | if (unlikely(ftrace_disabled)) |
| 638 | return; | 636 | return; |
| 639 | 637 | ||
| 640 | mutex_lock(&ftrace_start_lock); | ||
| 641 | ftrace_start_up--; | 638 | ftrace_start_up--; |
| 642 | if (!ftrace_start_up) | 639 | if (!ftrace_start_up) |
| 643 | command |= FTRACE_DISABLE_CALLS; | 640 | command |= FTRACE_DISABLE_CALLS; |
| @@ -648,11 +645,9 @@ static void ftrace_shutdown(int command) | |||
| 648 | } | 645 | } |
| 649 | 646 | ||
| 650 | if (!command || !ftrace_enabled) | 647 | if (!command || !ftrace_enabled) |
| 651 | goto out; | 648 | return; |
| 652 | 649 | ||
| 653 | ftrace_run_update_code(command); | 650 | ftrace_run_update_code(command); |
| 654 | out: | ||
| 655 | mutex_unlock(&ftrace_start_lock); | ||
| 656 | } | 651 | } |
| 657 | 652 | ||
| 658 | static void ftrace_startup_sysctl(void) | 653 | static void ftrace_startup_sysctl(void) |
| @@ -662,7 +657,6 @@ static void ftrace_startup_sysctl(void) | |||
| 662 | if (unlikely(ftrace_disabled)) | 657 | if (unlikely(ftrace_disabled)) |
| 663 | return; | 658 | return; |
| 664 | 659 | ||
| 665 | mutex_lock(&ftrace_start_lock); | ||
| 666 | /* Force update next time */ | 660 | /* Force update next time */ |
| 667 | saved_ftrace_func = NULL; | 661 | saved_ftrace_func = NULL; |
| 668 | /* ftrace_start_up is true if we want ftrace running */ | 662 | /* ftrace_start_up is true if we want ftrace running */ |
| @@ -670,7 +664,6 @@ static void ftrace_startup_sysctl(void) | |||
| 670 | command |= FTRACE_ENABLE_CALLS; | 664 | command |= FTRACE_ENABLE_CALLS; |
| 671 | 665 | ||
| 672 | ftrace_run_update_code(command); | 666 | ftrace_run_update_code(command); |
| 673 | mutex_unlock(&ftrace_start_lock); | ||
| 674 | } | 667 | } |
| 675 | 668 | ||
| 676 | static void ftrace_shutdown_sysctl(void) | 669 | static void ftrace_shutdown_sysctl(void) |
| @@ -680,13 +673,11 @@ static void ftrace_shutdown_sysctl(void) | |||
| 680 | if (unlikely(ftrace_disabled)) | 673 | if (unlikely(ftrace_disabled)) |
| 681 | return; | 674 | return; |
| 682 | 675 | ||
| 683 | mutex_lock(&ftrace_start_lock); | ||
| 684 | /* ftrace_start_up is true if ftrace is running */ | 676 | /* ftrace_start_up is true if ftrace is running */ |
| 685 | if (ftrace_start_up) | 677 | if (ftrace_start_up) |
| 686 | command |= FTRACE_DISABLE_CALLS; | 678 | command |= FTRACE_DISABLE_CALLS; |
| 687 | 679 | ||
| 688 | ftrace_run_update_code(command); | 680 | ftrace_run_update_code(command); |
| 689 | mutex_unlock(&ftrace_start_lock); | ||
| 690 | } | 681 | } |
| 691 | 682 | ||
| 692 | static cycle_t ftrace_update_time; | 683 | static cycle_t ftrace_update_time; |
| @@ -773,12 +764,15 @@ enum { | |||
| 773 | FTRACE_ITER_CONT = (1 << 1), | 764 | FTRACE_ITER_CONT = (1 << 1), |
| 774 | FTRACE_ITER_NOTRACE = (1 << 2), | 765 | FTRACE_ITER_NOTRACE = (1 << 2), |
| 775 | FTRACE_ITER_FAILURES = (1 << 3), | 766 | FTRACE_ITER_FAILURES = (1 << 3), |
| 767 | FTRACE_ITER_PRINTALL = (1 << 4), | ||
| 768 | FTRACE_ITER_HASH = (1 << 5), | ||
| 776 | }; | 769 | }; |
| 777 | 770 | ||
| 778 | #define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */ | 771 | #define FTRACE_BUFF_MAX (KSYM_SYMBOL_LEN+4) /* room for wildcards */ |
| 779 | 772 | ||
| 780 | struct ftrace_iterator { | 773 | struct ftrace_iterator { |
| 781 | struct ftrace_page *pg; | 774 | struct ftrace_page *pg; |
| 775 | int hidx; | ||
| 782 | int idx; | 776 | int idx; |
| 783 | unsigned flags; | 777 | unsigned flags; |
| 784 | unsigned char buffer[FTRACE_BUFF_MAX+1]; | 778 | unsigned char buffer[FTRACE_BUFF_MAX+1]; |
| @@ -787,15 +781,89 @@ struct ftrace_iterator { | |||
| 787 | }; | 781 | }; |
| 788 | 782 | ||
| 789 | static void * | 783 | static void * |
| 784 | t_hash_next(struct seq_file *m, void *v, loff_t *pos) | ||
| 785 | { | ||
| 786 | struct ftrace_iterator *iter = m->private; | ||
| 787 | struct hlist_node *hnd = v; | ||
| 788 | struct hlist_head *hhd; | ||
| 789 | |||
| 790 | WARN_ON(!(iter->flags & FTRACE_ITER_HASH)); | ||
| 791 | |||
| 792 | (*pos)++; | ||
| 793 | |||
| 794 | retry: | ||
| 795 | if (iter->hidx >= FTRACE_FUNC_HASHSIZE) | ||
| 796 | return NULL; | ||
| 797 | |||
| 798 | hhd = &ftrace_func_hash[iter->hidx]; | ||
| 799 | |||
| 800 | if (hlist_empty(hhd)) { | ||
| 801 | iter->hidx++; | ||
| 802 | hnd = NULL; | ||
| 803 | goto retry; | ||
| 804 | } | ||
| 805 | |||
| 806 | if (!hnd) | ||
| 807 | hnd = hhd->first; | ||
| 808 | else { | ||
| 809 | hnd = hnd->next; | ||
| 810 | if (!hnd) { | ||
| 811 | iter->hidx++; | ||
| 812 | goto retry; | ||
| 813 | } | ||
| 814 | } | ||
| 815 | |||
| 816 | return hnd; | ||
| 817 | } | ||
| 818 | |||
| 819 | static void *t_hash_start(struct seq_file *m, loff_t *pos) | ||
| 820 | { | ||
| 821 | struct ftrace_iterator *iter = m->private; | ||
| 822 | void *p = NULL; | ||
| 823 | |||
| 824 | iter->flags |= FTRACE_ITER_HASH; | ||
| 825 | |||
| 826 | return t_hash_next(m, p, pos); | ||
| 827 | } | ||
| 828 | |||
| 829 | static int t_hash_show(struct seq_file *m, void *v) | ||
| 830 | { | ||
| 831 | struct ftrace_func_hook *rec; | ||
| 832 | struct hlist_node *hnd = v; | ||
| 833 | char str[KSYM_SYMBOL_LEN]; | ||
| 834 | |||
| 835 | rec = hlist_entry(hnd, struct ftrace_func_hook, node); | ||
| 836 | |||
| 837 | if (rec->ops->print) | ||
| 838 | return rec->ops->print(m, rec->ip, rec->ops, rec->data); | ||
| 839 | |||
| 840 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | ||
| 841 | seq_printf(m, "%s:", str); | ||
| 842 | |||
| 843 | kallsyms_lookup((unsigned long)rec->ops->func, NULL, NULL, NULL, str); | ||
| 844 | seq_printf(m, "%s", str); | ||
| 845 | |||
| 846 | if (rec->data) | ||
| 847 | seq_printf(m, ":%p", rec->data); | ||
| 848 | seq_putc(m, '\n'); | ||
| 849 | |||
| 850 | return 0; | ||
| 851 | } | ||
| 852 | |||
| 853 | static void * | ||
| 790 | t_next(struct seq_file *m, void *v, loff_t *pos) | 854 | t_next(struct seq_file *m, void *v, loff_t *pos) |
| 791 | { | 855 | { |
| 792 | struct ftrace_iterator *iter = m->private; | 856 | struct ftrace_iterator *iter = m->private; |
| 793 | struct dyn_ftrace *rec = NULL; | 857 | struct dyn_ftrace *rec = NULL; |
| 794 | 858 | ||
| 859 | if (iter->flags & FTRACE_ITER_HASH) | ||
| 860 | return t_hash_next(m, v, pos); | ||
| 861 | |||
| 795 | (*pos)++; | 862 | (*pos)++; |
| 796 | 863 | ||
| 797 | /* should not be called from interrupt context */ | 864 | if (iter->flags & FTRACE_ITER_PRINTALL) |
| 798 | spin_lock(&ftrace_lock); | 865 | return NULL; |
| 866 | |||
| 799 | retry: | 867 | retry: |
| 800 | if (iter->idx >= iter->pg->index) { | 868 | if (iter->idx >= iter->pg->index) { |
| 801 | if (iter->pg->next) { | 869 | if (iter->pg->next) { |
| @@ -824,7 +892,6 @@ t_next(struct seq_file *m, void *v, loff_t *pos) | |||
| 824 | goto retry; | 892 | goto retry; |
| 825 | } | 893 | } |
| 826 | } | 894 | } |
| 827 | spin_unlock(&ftrace_lock); | ||
| 828 | 895 | ||
| 829 | return rec; | 896 | return rec; |
| 830 | } | 897 | } |
| @@ -834,6 +901,23 @@ static void *t_start(struct seq_file *m, loff_t *pos) | |||
| 834 | struct ftrace_iterator *iter = m->private; | 901 | struct ftrace_iterator *iter = m->private; |
| 835 | void *p = NULL; | 902 | void *p = NULL; |
| 836 | 903 | ||
| 904 | mutex_lock(&ftrace_lock); | ||
| 905 | /* | ||
| 906 | * For set_ftrace_filter reading, if we have the filter | ||
| 907 | * off, we can short cut and just print out that all | ||
| 908 | * functions are enabled. | ||
| 909 | */ | ||
| 910 | if (iter->flags & FTRACE_ITER_FILTER && !ftrace_filtered) { | ||
| 911 | if (*pos > 0) | ||
| 912 | return t_hash_start(m, pos); | ||
| 913 | iter->flags |= FTRACE_ITER_PRINTALL; | ||
| 914 | (*pos)++; | ||
| 915 | return iter; | ||
| 916 | } | ||
| 917 | |||
| 918 | if (iter->flags & FTRACE_ITER_HASH) | ||
| 919 | return t_hash_start(m, pos); | ||
| 920 | |||
| 837 | if (*pos > 0) { | 921 | if (*pos > 0) { |
| 838 | if (iter->idx < 0) | 922 | if (iter->idx < 0) |
| 839 | return p; | 923 | return p; |
| @@ -843,18 +927,31 @@ static void *t_start(struct seq_file *m, loff_t *pos) | |||
| 843 | 927 | ||
| 844 | p = t_next(m, p, pos); | 928 | p = t_next(m, p, pos); |
| 845 | 929 | ||
| 930 | if (!p) | ||
| 931 | return t_hash_start(m, pos); | ||
| 932 | |||
| 846 | return p; | 933 | return p; |
| 847 | } | 934 | } |
| 848 | 935 | ||
| 849 | static void t_stop(struct seq_file *m, void *p) | 936 | static void t_stop(struct seq_file *m, void *p) |
| 850 | { | 937 | { |
| 938 | mutex_unlock(&ftrace_lock); | ||
| 851 | } | 939 | } |
| 852 | 940 | ||
| 853 | static int t_show(struct seq_file *m, void *v) | 941 | static int t_show(struct seq_file *m, void *v) |
| 854 | { | 942 | { |
| 943 | struct ftrace_iterator *iter = m->private; | ||
| 855 | struct dyn_ftrace *rec = v; | 944 | struct dyn_ftrace *rec = v; |
| 856 | char str[KSYM_SYMBOL_LEN]; | 945 | char str[KSYM_SYMBOL_LEN]; |
| 857 | 946 | ||
| 947 | if (iter->flags & FTRACE_ITER_HASH) | ||
| 948 | return t_hash_show(m, v); | ||
| 949 | |||
| 950 | if (iter->flags & FTRACE_ITER_PRINTALL) { | ||
| 951 | seq_printf(m, "#### all functions enabled ####\n"); | ||
| 952 | return 0; | ||
| 953 | } | ||
| 954 | |||
| 858 | if (!rec) | 955 | if (!rec) |
| 859 | return 0; | 956 | return 0; |
| 860 | 957 | ||
| @@ -933,23 +1030,16 @@ static void ftrace_filter_reset(int enable) | |||
| 933 | struct ftrace_page *pg; | 1030 | struct ftrace_page *pg; |
| 934 | struct dyn_ftrace *rec; | 1031 | struct dyn_ftrace *rec; |
| 935 | unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | 1032 | unsigned long type = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; |
| 936 | unsigned i; | ||
| 937 | 1033 | ||
| 938 | /* should not be called from interrupt context */ | 1034 | mutex_lock(&ftrace_lock); |
| 939 | spin_lock(&ftrace_lock); | ||
| 940 | if (enable) | 1035 | if (enable) |
| 941 | ftrace_filtered = 0; | 1036 | ftrace_filtered = 0; |
| 942 | pg = ftrace_pages_start; | 1037 | do_for_each_ftrace_rec(pg, rec) { |
| 943 | while (pg) { | 1038 | if (rec->flags & FTRACE_FL_FAILED) |
| 944 | for (i = 0; i < pg->index; i++) { | 1039 | continue; |
| 945 | rec = &pg->records[i]; | 1040 | rec->flags &= ~type; |
| 946 | if (rec->flags & FTRACE_FL_FAILED) | 1041 | } while_for_each_ftrace_rec(); |
| 947 | continue; | 1042 | mutex_unlock(&ftrace_lock); |
| 948 | rec->flags &= ~type; | ||
| 949 | } | ||
| 950 | pg = pg->next; | ||
| 951 | } | ||
| 952 | spin_unlock(&ftrace_lock); | ||
| 953 | } | 1043 | } |
| 954 | 1044 | ||
| 955 | static int | 1045 | static int |
| @@ -1030,86 +1120,533 @@ enum { | |||
| 1030 | MATCH_END_ONLY, | 1120 | MATCH_END_ONLY, |
| 1031 | }; | 1121 | }; |
| 1032 | 1122 | ||
| 1033 | static void | 1123 | /* |
| 1034 | ftrace_match(unsigned char *buff, int len, int enable) | 1124 | * (static function - no need for kernel doc) |
| 1125 | * | ||
| 1126 | * Pass in a buffer containing a glob and this function will | ||
| 1127 | * set search to point to the search part of the buffer and | ||
| 1128 | * return the type of search it is (see enum above). | ||
| 1129 | * This does modify buff. | ||
| 1130 | * | ||
| 1131 | * Returns enum type. | ||
| 1132 | * search returns the pointer to use for comparison. | ||
| 1133 | * not returns 1 if buff started with a '!' | ||
| 1134 | * 0 otherwise. | ||
| 1135 | */ | ||
| 1136 | static int | ||
| 1137 | ftrace_setup_glob(char *buff, int len, char **search, int *not) | ||
| 1035 | { | 1138 | { |
| 1036 | char str[KSYM_SYMBOL_LEN]; | ||
| 1037 | char *search = NULL; | ||
| 1038 | struct ftrace_page *pg; | ||
| 1039 | struct dyn_ftrace *rec; | ||
| 1040 | int type = MATCH_FULL; | 1139 | int type = MATCH_FULL; |
| 1041 | unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | 1140 | int i; |
| 1042 | unsigned i, match = 0, search_len = 0; | ||
| 1043 | int not = 0; | ||
| 1044 | 1141 | ||
| 1045 | if (buff[0] == '!') { | 1142 | if (buff[0] == '!') { |
| 1046 | not = 1; | 1143 | *not = 1; |
| 1047 | buff++; | 1144 | buff++; |
| 1048 | len--; | 1145 | len--; |
| 1049 | } | 1146 | } else |
| 1147 | *not = 0; | ||
| 1148 | |||
| 1149 | *search = buff; | ||
| 1050 | 1150 | ||
| 1051 | for (i = 0; i < len; i++) { | 1151 | for (i = 0; i < len; i++) { |
| 1052 | if (buff[i] == '*') { | 1152 | if (buff[i] == '*') { |
| 1053 | if (!i) { | 1153 | if (!i) { |
| 1054 | search = buff + i + 1; | 1154 | *search = buff + 1; |
| 1055 | type = MATCH_END_ONLY; | 1155 | type = MATCH_END_ONLY; |
| 1056 | search_len = len - (i + 1); | ||
| 1057 | } else { | 1156 | } else { |
| 1058 | if (type == MATCH_END_ONLY) { | 1157 | if (type == MATCH_END_ONLY) |
| 1059 | type = MATCH_MIDDLE_ONLY; | 1158 | type = MATCH_MIDDLE_ONLY; |
| 1060 | } else { | 1159 | else |
| 1061 | match = i; | ||
| 1062 | type = MATCH_FRONT_ONLY; | 1160 | type = MATCH_FRONT_ONLY; |
| 1063 | } | ||
| 1064 | buff[i] = 0; | 1161 | buff[i] = 0; |
| 1065 | break; | 1162 | break; |
| 1066 | } | 1163 | } |
| 1067 | } | 1164 | } |
| 1068 | } | 1165 | } |
| 1069 | 1166 | ||
| 1070 | /* should not be called from interrupt context */ | 1167 | return type; |
| 1071 | spin_lock(&ftrace_lock); | 1168 | } |
| 1072 | if (enable) | 1169 | |
| 1073 | ftrace_filtered = 1; | 1170 | static int ftrace_match(char *str, char *regex, int len, int type) |
| 1074 | pg = ftrace_pages_start; | 1171 | { |
| 1075 | while (pg) { | 1172 | int matched = 0; |
| 1076 | for (i = 0; i < pg->index; i++) { | 1173 | char *ptr; |
| 1077 | int matched = 0; | 1174 | |
| 1078 | char *ptr; | 1175 | switch (type) { |
| 1079 | 1176 | case MATCH_FULL: | |
| 1080 | rec = &pg->records[i]; | 1177 | if (strcmp(str, regex) == 0) |
| 1081 | if (rec->flags & FTRACE_FL_FAILED) | 1178 | matched = 1; |
| 1179 | break; | ||
| 1180 | case MATCH_FRONT_ONLY: | ||
| 1181 | if (strncmp(str, regex, len) == 0) | ||
| 1182 | matched = 1; | ||
| 1183 | break; | ||
| 1184 | case MATCH_MIDDLE_ONLY: | ||
| 1185 | if (strstr(str, regex)) | ||
| 1186 | matched = 1; | ||
| 1187 | break; | ||
| 1188 | case MATCH_END_ONLY: | ||
| 1189 | ptr = strstr(str, regex); | ||
| 1190 | if (ptr && (ptr[len] == 0)) | ||
| 1191 | matched = 1; | ||
| 1192 | break; | ||
| 1193 | } | ||
| 1194 | |||
| 1195 | return matched; | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | static int | ||
| 1199 | ftrace_match_record(struct dyn_ftrace *rec, char *regex, int len, int type) | ||
| 1200 | { | ||
| 1201 | char str[KSYM_SYMBOL_LEN]; | ||
| 1202 | |||
| 1203 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | ||
| 1204 | return ftrace_match(str, regex, len, type); | ||
| 1205 | } | ||
| 1206 | |||
| 1207 | static void ftrace_match_records(char *buff, int len, int enable) | ||
| 1208 | { | ||
| 1209 | char *search; | ||
| 1210 | struct ftrace_page *pg; | ||
| 1211 | struct dyn_ftrace *rec; | ||
| 1212 | int type; | ||
| 1213 | unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | ||
| 1214 | unsigned search_len; | ||
| 1215 | int not; | ||
| 1216 | |||
| 1217 | type = ftrace_setup_glob(buff, len, &search, ¬); | ||
| 1218 | |||
| 1219 | search_len = strlen(search); | ||
| 1220 | |||
| 1221 | mutex_lock(&ftrace_lock); | ||
| 1222 | do_for_each_ftrace_rec(pg, rec) { | ||
| 1223 | |||
| 1224 | if (rec->flags & FTRACE_FL_FAILED) | ||
| 1225 | continue; | ||
| 1226 | |||
| 1227 | if (ftrace_match_record(rec, search, search_len, type)) { | ||
| 1228 | if (not) | ||
| 1229 | rec->flags &= ~flag; | ||
| 1230 | else | ||
| 1231 | rec->flags |= flag; | ||
| 1232 | } | ||
| 1233 | /* | ||
| 1234 | * Only enable filtering if we have a function that | ||
| 1235 | * is filtered on. | ||
| 1236 | */ | ||
| 1237 | if (enable && (rec->flags & FTRACE_FL_FILTER)) | ||
| 1238 | ftrace_filtered = 1; | ||
| 1239 | } while_for_each_ftrace_rec(); | ||
| 1240 | mutex_unlock(&ftrace_lock); | ||
| 1241 | } | ||
| 1242 | |||
| 1243 | static int | ||
| 1244 | ftrace_match_module_record(struct dyn_ftrace *rec, char *mod, | ||
| 1245 | char *regex, int len, int type) | ||
| 1246 | { | ||
| 1247 | char str[KSYM_SYMBOL_LEN]; | ||
| 1248 | char *modname; | ||
| 1249 | |||
| 1250 | kallsyms_lookup(rec->ip, NULL, NULL, &modname, str); | ||
| 1251 | |||
| 1252 | if (!modname || strcmp(modname, mod)) | ||
| 1253 | return 0; | ||
| 1254 | |||
| 1255 | /* blank search means to match all funcs in the mod */ | ||
| 1256 | if (len) | ||
| 1257 | return ftrace_match(str, regex, len, type); | ||
| 1258 | else | ||
| 1259 | return 1; | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | static void ftrace_match_module_records(char *buff, char *mod, int enable) | ||
| 1263 | { | ||
| 1264 | char *search = buff; | ||
| 1265 | struct ftrace_page *pg; | ||
| 1266 | struct dyn_ftrace *rec; | ||
| 1267 | int type = MATCH_FULL; | ||
| 1268 | unsigned long flag = enable ? FTRACE_FL_FILTER : FTRACE_FL_NOTRACE; | ||
| 1269 | unsigned search_len = 0; | ||
| 1270 | int not = 0; | ||
| 1271 | |||
| 1272 | /* blank or '*' mean the same */ | ||
| 1273 | if (strcmp(buff, "*") == 0) | ||
| 1274 | buff[0] = 0; | ||
| 1275 | |||
| 1276 | /* handle the case of 'dont filter this module' */ | ||
| 1277 | if (strcmp(buff, "!") == 0 || strcmp(buff, "!*") == 0) { | ||
| 1278 | buff[0] = 0; | ||
| 1279 | not = 1; | ||
| 1280 | } | ||
| 1281 | |||
| 1282 | if (strlen(buff)) { | ||
| 1283 | type = ftrace_setup_glob(buff, strlen(buff), &search, ¬); | ||
| 1284 | search_len = strlen(search); | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | mutex_lock(&ftrace_lock); | ||
| 1288 | do_for_each_ftrace_rec(pg, rec) { | ||
| 1289 | |||
| 1290 | if (rec->flags & FTRACE_FL_FAILED) | ||
| 1291 | continue; | ||
| 1292 | |||
| 1293 | if (ftrace_match_module_record(rec, mod, | ||
| 1294 | search, search_len, type)) { | ||
| 1295 | if (not) | ||
| 1296 | rec->flags &= ~flag; | ||
| 1297 | else | ||
| 1298 | rec->flags |= flag; | ||
| 1299 | } | ||
| 1300 | if (enable && (rec->flags & FTRACE_FL_FILTER)) | ||
| 1301 | ftrace_filtered = 1; | ||
| 1302 | |||
| 1303 | } while_for_each_ftrace_rec(); | ||
| 1304 | mutex_unlock(&ftrace_lock); | ||
| 1305 | } | ||
| 1306 | |||
| 1307 | /* | ||
| 1308 | * We register the module command as a template to show others how | ||
| 1309 | * to register the a command as well. | ||
| 1310 | */ | ||
| 1311 | |||
| 1312 | static int | ||
| 1313 | ftrace_mod_callback(char *func, char *cmd, char *param, int enable) | ||
| 1314 | { | ||
| 1315 | char *mod; | ||
| 1316 | |||
| 1317 | /* | ||
| 1318 | * cmd == 'mod' because we only registered this func | ||
| 1319 | * for the 'mod' ftrace_func_command. | ||
| 1320 | * But if you register one func with multiple commands, | ||
| 1321 | * you can tell which command was used by the cmd | ||
| 1322 | * parameter. | ||
| 1323 | */ | ||
| 1324 | |||
| 1325 | /* we must have a module name */ | ||
| 1326 | if (!param) | ||
| 1327 | return -EINVAL; | ||
| 1328 | |||
| 1329 | mod = strsep(¶m, ":"); | ||
| 1330 | if (!strlen(mod)) | ||
| 1331 | return -EINVAL; | ||
| 1332 | |||
| 1333 | ftrace_match_module_records(func, mod, enable); | ||
| 1334 | return 0; | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | static struct ftrace_func_command ftrace_mod_cmd = { | ||
| 1338 | .name = "mod", | ||
| 1339 | .func = ftrace_mod_callback, | ||
| 1340 | }; | ||
| 1341 | |||
| 1342 | static int __init ftrace_mod_cmd_init(void) | ||
| 1343 | { | ||
| 1344 | return register_ftrace_command(&ftrace_mod_cmd); | ||
| 1345 | } | ||
| 1346 | device_initcall(ftrace_mod_cmd_init); | ||
| 1347 | |||
| 1348 | static void | ||
| 1349 | function_trace_hook_call(unsigned long ip, unsigned long parent_ip) | ||
| 1350 | { | ||
| 1351 | struct ftrace_func_hook *entry; | ||
| 1352 | struct hlist_head *hhd; | ||
| 1353 | struct hlist_node *n; | ||
| 1354 | unsigned long key; | ||
| 1355 | int resched; | ||
| 1356 | |||
| 1357 | key = hash_long(ip, FTRACE_HASH_BITS); | ||
| 1358 | |||
| 1359 | hhd = &ftrace_func_hash[key]; | ||
| 1360 | |||
| 1361 | if (hlist_empty(hhd)) | ||
| 1362 | return; | ||
| 1363 | |||
| 1364 | /* | ||
| 1365 | * Disable preemption for these calls to prevent a RCU grace | ||
| 1366 | * period. This syncs the hash iteration and freeing of items | ||
| 1367 | * on the hash. rcu_read_lock is too dangerous here. | ||
| 1368 | */ | ||
| 1369 | resched = ftrace_preempt_disable(); | ||
| 1370 | hlist_for_each_entry_rcu(entry, n, hhd, node) { | ||
| 1371 | if (entry->ip == ip) | ||
| 1372 | entry->ops->func(ip, parent_ip, &entry->data); | ||
| 1373 | } | ||
| 1374 | ftrace_preempt_enable(resched); | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | static struct ftrace_ops trace_hook_ops __read_mostly = | ||
| 1378 | { | ||
| 1379 | .func = function_trace_hook_call, | ||
| 1380 | }; | ||
| 1381 | |||
| 1382 | static int ftrace_hook_registered; | ||
| 1383 | |||
| 1384 | static void __enable_ftrace_function_hook(void) | ||
| 1385 | { | ||
| 1386 | int i; | ||
| 1387 | |||
| 1388 | if (ftrace_hook_registered) | ||
| 1389 | return; | ||
| 1390 | |||
| 1391 | for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) { | ||
| 1392 | struct hlist_head *hhd = &ftrace_func_hash[i]; | ||
| 1393 | if (hhd->first) | ||
| 1394 | break; | ||
| 1395 | } | ||
| 1396 | /* Nothing registered? */ | ||
| 1397 | if (i == FTRACE_FUNC_HASHSIZE) | ||
| 1398 | return; | ||
| 1399 | |||
| 1400 | __register_ftrace_function(&trace_hook_ops); | ||
| 1401 | ftrace_startup(0); | ||
| 1402 | ftrace_hook_registered = 1; | ||
| 1403 | } | ||
| 1404 | |||
| 1405 | static void __disable_ftrace_function_hook(void) | ||
| 1406 | { | ||
| 1407 | int i; | ||
| 1408 | |||
| 1409 | if (!ftrace_hook_registered) | ||
| 1410 | return; | ||
| 1411 | |||
| 1412 | for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) { | ||
| 1413 | struct hlist_head *hhd = &ftrace_func_hash[i]; | ||
| 1414 | if (hhd->first) | ||
| 1415 | return; | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | /* no more funcs left */ | ||
| 1419 | __unregister_ftrace_function(&trace_hook_ops); | ||
| 1420 | ftrace_shutdown(0); | ||
| 1421 | ftrace_hook_registered = 0; | ||
| 1422 | } | ||
| 1423 | |||
| 1424 | |||
| 1425 | static void ftrace_free_entry_rcu(struct rcu_head *rhp) | ||
| 1426 | { | ||
| 1427 | struct ftrace_func_hook *entry = | ||
| 1428 | container_of(rhp, struct ftrace_func_hook, rcu); | ||
| 1429 | |||
| 1430 | if (entry->ops->free) | ||
| 1431 | entry->ops->free(&entry->data); | ||
| 1432 | kfree(entry); | ||
| 1433 | } | ||
| 1434 | |||
| 1435 | |||
| 1436 | int | ||
| 1437 | register_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops, | ||
| 1438 | void *data) | ||
| 1439 | { | ||
| 1440 | struct ftrace_func_hook *entry; | ||
| 1441 | struct ftrace_page *pg; | ||
| 1442 | struct dyn_ftrace *rec; | ||
| 1443 | unsigned long key; | ||
| 1444 | int type, len, not; | ||
| 1445 | int count = 0; | ||
| 1446 | char *search; | ||
| 1447 | |||
| 1448 | type = ftrace_setup_glob(glob, strlen(glob), &search, ¬); | ||
| 1449 | len = strlen(search); | ||
| 1450 | |||
| 1451 | /* we do not support '!' for function hooks */ | ||
| 1452 | if (WARN_ON(not)) | ||
| 1453 | return -EINVAL; | ||
| 1454 | |||
| 1455 | mutex_lock(&ftrace_lock); | ||
| 1456 | do_for_each_ftrace_rec(pg, rec) { | ||
| 1457 | |||
| 1458 | if (rec->flags & FTRACE_FL_FAILED) | ||
| 1459 | continue; | ||
| 1460 | |||
| 1461 | if (!ftrace_match_record(rec, search, len, type)) | ||
| 1462 | continue; | ||
| 1463 | |||
| 1464 | entry = kmalloc(sizeof(*entry), GFP_KERNEL); | ||
| 1465 | if (!entry) { | ||
| 1466 | /* If we did not hook to any, then return error */ | ||
| 1467 | if (!count) | ||
| 1468 | count = -ENOMEM; | ||
| 1469 | goto out_unlock; | ||
| 1470 | } | ||
| 1471 | |||
| 1472 | count++; | ||
| 1473 | |||
| 1474 | entry->data = data; | ||
| 1475 | |||
| 1476 | /* | ||
| 1477 | * The caller might want to do something special | ||
| 1478 | * for each function we find. We call the callback | ||
| 1479 | * to give the caller an opportunity to do so. | ||
| 1480 | */ | ||
| 1481 | if (ops->callback) { | ||
| 1482 | if (ops->callback(rec->ip, &entry->data) < 0) { | ||
| 1483 | /* caller does not like this func */ | ||
| 1484 | kfree(entry); | ||
| 1082 | continue; | 1485 | continue; |
| 1083 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | ||
| 1084 | switch (type) { | ||
| 1085 | case MATCH_FULL: | ||
| 1086 | if (strcmp(str, buff) == 0) | ||
| 1087 | matched = 1; | ||
| 1088 | break; | ||
| 1089 | case MATCH_FRONT_ONLY: | ||
| 1090 | if (memcmp(str, buff, match) == 0) | ||
| 1091 | matched = 1; | ||
| 1092 | break; | ||
| 1093 | case MATCH_MIDDLE_ONLY: | ||
| 1094 | if (strstr(str, search)) | ||
| 1095 | matched = 1; | ||
| 1096 | break; | ||
| 1097 | case MATCH_END_ONLY: | ||
| 1098 | ptr = strstr(str, search); | ||
| 1099 | if (ptr && (ptr[search_len] == 0)) | ||
| 1100 | matched = 1; | ||
| 1101 | break; | ||
| 1102 | } | 1486 | } |
| 1103 | if (matched) { | 1487 | } |
| 1104 | if (not) | 1488 | |
| 1105 | rec->flags &= ~flag; | 1489 | entry->ops = ops; |
| 1106 | else | 1490 | entry->ip = rec->ip; |
| 1107 | rec->flags |= flag; | 1491 | |
| 1492 | key = hash_long(entry->ip, FTRACE_HASH_BITS); | ||
| 1493 | hlist_add_head_rcu(&entry->node, &ftrace_func_hash[key]); | ||
| 1494 | |||
| 1495 | } while_for_each_ftrace_rec(); | ||
| 1496 | __enable_ftrace_function_hook(); | ||
| 1497 | |||
| 1498 | out_unlock: | ||
| 1499 | mutex_unlock(&ftrace_lock); | ||
| 1500 | |||
| 1501 | return count; | ||
| 1502 | } | ||
| 1503 | |||
| 1504 | enum { | ||
| 1505 | HOOK_TEST_FUNC = 1, | ||
| 1506 | HOOK_TEST_DATA = 2 | ||
| 1507 | }; | ||
| 1508 | |||
| 1509 | static void | ||
| 1510 | __unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops, | ||
| 1511 | void *data, int flags) | ||
| 1512 | { | ||
| 1513 | struct ftrace_func_hook *entry; | ||
| 1514 | struct hlist_node *n, *tmp; | ||
| 1515 | char str[KSYM_SYMBOL_LEN]; | ||
| 1516 | int type = MATCH_FULL; | ||
| 1517 | int i, len = 0; | ||
| 1518 | char *search; | ||
| 1519 | |||
| 1520 | if (glob && (strcmp(glob, "*") || !strlen(glob))) | ||
| 1521 | glob = NULL; | ||
| 1522 | else { | ||
| 1523 | int not; | ||
| 1524 | |||
| 1525 | type = ftrace_setup_glob(glob, strlen(glob), &search, ¬); | ||
| 1526 | len = strlen(search); | ||
| 1527 | |||
| 1528 | /* we do not support '!' for function hooks */ | ||
| 1529 | if (WARN_ON(not)) | ||
| 1530 | return; | ||
| 1531 | } | ||
| 1532 | |||
| 1533 | mutex_lock(&ftrace_lock); | ||
| 1534 | for (i = 0; i < FTRACE_FUNC_HASHSIZE; i++) { | ||
| 1535 | struct hlist_head *hhd = &ftrace_func_hash[i]; | ||
| 1536 | |||
| 1537 | hlist_for_each_entry_safe(entry, n, tmp, hhd, node) { | ||
| 1538 | |||
| 1539 | /* break up if statements for readability */ | ||
| 1540 | if ((flags & HOOK_TEST_FUNC) && entry->ops != ops) | ||
| 1541 | continue; | ||
| 1542 | |||
| 1543 | if ((flags & HOOK_TEST_DATA) && entry->data != data) | ||
| 1544 | continue; | ||
| 1545 | |||
| 1546 | /* do this last, since it is the most expensive */ | ||
| 1547 | if (glob) { | ||
| 1548 | kallsyms_lookup(entry->ip, NULL, NULL, | ||
| 1549 | NULL, str); | ||
| 1550 | if (!ftrace_match(str, glob, len, type)) | ||
| 1551 | continue; | ||
| 1108 | } | 1552 | } |
| 1553 | |||
| 1554 | hlist_del(&entry->node); | ||
| 1555 | call_rcu(&entry->rcu, ftrace_free_entry_rcu); | ||
| 1109 | } | 1556 | } |
| 1110 | pg = pg->next; | ||
| 1111 | } | 1557 | } |
| 1112 | spin_unlock(&ftrace_lock); | 1558 | __disable_ftrace_function_hook(); |
| 1559 | mutex_unlock(&ftrace_lock); | ||
| 1560 | } | ||
| 1561 | |||
| 1562 | void | ||
| 1563 | unregister_ftrace_function_hook(char *glob, struct ftrace_hook_ops *ops, | ||
| 1564 | void *data) | ||
| 1565 | { | ||
| 1566 | __unregister_ftrace_function_hook(glob, ops, data, | ||
| 1567 | HOOK_TEST_FUNC | HOOK_TEST_DATA); | ||
| 1568 | } | ||
| 1569 | |||
| 1570 | void | ||
| 1571 | unregister_ftrace_function_hook_func(char *glob, struct ftrace_hook_ops *ops) | ||
| 1572 | { | ||
| 1573 | __unregister_ftrace_function_hook(glob, ops, NULL, HOOK_TEST_FUNC); | ||
| 1574 | } | ||
| 1575 | |||
| 1576 | void unregister_ftrace_function_hook_all(char *glob) | ||
| 1577 | { | ||
| 1578 | __unregister_ftrace_function_hook(glob, NULL, NULL, 0); | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | static LIST_HEAD(ftrace_commands); | ||
| 1582 | static DEFINE_MUTEX(ftrace_cmd_mutex); | ||
| 1583 | |||
| 1584 | int register_ftrace_command(struct ftrace_func_command *cmd) | ||
| 1585 | { | ||
| 1586 | struct ftrace_func_command *p; | ||
| 1587 | int ret = 0; | ||
| 1588 | |||
| 1589 | mutex_lock(&ftrace_cmd_mutex); | ||
| 1590 | list_for_each_entry(p, &ftrace_commands, list) { | ||
| 1591 | if (strcmp(cmd->name, p->name) == 0) { | ||
| 1592 | ret = -EBUSY; | ||
| 1593 | goto out_unlock; | ||
| 1594 | } | ||
| 1595 | } | ||
| 1596 | list_add(&cmd->list, &ftrace_commands); | ||
| 1597 | out_unlock: | ||
| 1598 | mutex_unlock(&ftrace_cmd_mutex); | ||
| 1599 | |||
| 1600 | return ret; | ||
| 1601 | } | ||
| 1602 | |||
| 1603 | int unregister_ftrace_command(struct ftrace_func_command *cmd) | ||
| 1604 | { | ||
| 1605 | struct ftrace_func_command *p, *n; | ||
| 1606 | int ret = -ENODEV; | ||
| 1607 | |||
| 1608 | mutex_lock(&ftrace_cmd_mutex); | ||
| 1609 | list_for_each_entry_safe(p, n, &ftrace_commands, list) { | ||
| 1610 | if (strcmp(cmd->name, p->name) == 0) { | ||
| 1611 | ret = 0; | ||
| 1612 | list_del_init(&p->list); | ||
| 1613 | goto out_unlock; | ||
| 1614 | } | ||
| 1615 | } | ||
| 1616 | out_unlock: | ||
| 1617 | mutex_unlock(&ftrace_cmd_mutex); | ||
| 1618 | |||
| 1619 | return ret; | ||
| 1620 | } | ||
| 1621 | |||
| 1622 | static int ftrace_process_regex(char *buff, int len, int enable) | ||
| 1623 | { | ||
| 1624 | struct ftrace_func_command *p; | ||
| 1625 | char *func, *command, *next = buff; | ||
| 1626 | int ret = -EINVAL; | ||
| 1627 | |||
| 1628 | func = strsep(&next, ":"); | ||
| 1629 | |||
| 1630 | if (!next) { | ||
| 1631 | ftrace_match_records(func, len, enable); | ||
| 1632 | return 0; | ||
| 1633 | } | ||
| 1634 | |||
| 1635 | /* command found */ | ||
| 1636 | |||
| 1637 | command = strsep(&next, ":"); | ||
| 1638 | |||
| 1639 | mutex_lock(&ftrace_cmd_mutex); | ||
| 1640 | list_for_each_entry(p, &ftrace_commands, list) { | ||
| 1641 | if (strcmp(p->name, command) == 0) { | ||
| 1642 | ret = p->func(func, command, next, enable); | ||
| 1643 | goto out_unlock; | ||
| 1644 | } | ||
| 1645 | } | ||
| 1646 | out_unlock: | ||
| 1647 | mutex_unlock(&ftrace_cmd_mutex); | ||
| 1648 | |||
| 1649 | return ret; | ||
| 1113 | } | 1650 | } |
| 1114 | 1651 | ||
| 1115 | static ssize_t | 1652 | static ssize_t |
| @@ -1179,7 +1716,10 @@ ftrace_regex_write(struct file *file, const char __user *ubuf, | |||
| 1179 | if (isspace(ch)) { | 1716 | if (isspace(ch)) { |
| 1180 | iter->filtered++; | 1717 | iter->filtered++; |
| 1181 | iter->buffer[iter->buffer_idx] = 0; | 1718 | iter->buffer[iter->buffer_idx] = 0; |
| 1182 | ftrace_match(iter->buffer, iter->buffer_idx, enable); | 1719 | ret = ftrace_process_regex(iter->buffer, |
| 1720 | iter->buffer_idx, enable); | ||
| 1721 | if (ret) | ||
| 1722 | goto out; | ||
| 1183 | iter->buffer_idx = 0; | 1723 | iter->buffer_idx = 0; |
| 1184 | } else | 1724 | } else |
| 1185 | iter->flags |= FTRACE_ITER_CONT; | 1725 | iter->flags |= FTRACE_ITER_CONT; |
| @@ -1218,7 +1758,7 @@ ftrace_set_regex(unsigned char *buf, int len, int reset, int enable) | |||
| 1218 | if (reset) | 1758 | if (reset) |
| 1219 | ftrace_filter_reset(enable); | 1759 | ftrace_filter_reset(enable); |
| 1220 | if (buf) | 1760 | if (buf) |
| 1221 | ftrace_match(buf, len, enable); | 1761 | ftrace_match_records(buf, len, enable); |
| 1222 | mutex_unlock(&ftrace_regex_lock); | 1762 | mutex_unlock(&ftrace_regex_lock); |
| 1223 | } | 1763 | } |
| 1224 | 1764 | ||
| @@ -1268,15 +1808,13 @@ ftrace_regex_release(struct inode *inode, struct file *file, int enable) | |||
| 1268 | if (iter->buffer_idx) { | 1808 | if (iter->buffer_idx) { |
| 1269 | iter->filtered++; | 1809 | iter->filtered++; |
| 1270 | iter->buffer[iter->buffer_idx] = 0; | 1810 | iter->buffer[iter->buffer_idx] = 0; |
| 1271 | ftrace_match(iter->buffer, iter->buffer_idx, enable); | 1811 | ftrace_match_records(iter->buffer, iter->buffer_idx, enable); |
| 1272 | } | 1812 | } |
| 1273 | 1813 | ||
| 1274 | mutex_lock(&ftrace_sysctl_lock); | 1814 | mutex_lock(&ftrace_lock); |
| 1275 | mutex_lock(&ftrace_start_lock); | ||
| 1276 | if (ftrace_start_up && ftrace_enabled) | 1815 | if (ftrace_start_up && ftrace_enabled) |
| 1277 | ftrace_run_update_code(FTRACE_ENABLE_CALLS); | 1816 | ftrace_run_update_code(FTRACE_ENABLE_CALLS); |
| 1278 | mutex_unlock(&ftrace_start_lock); | 1817 | mutex_unlock(&ftrace_lock); |
| 1279 | mutex_unlock(&ftrace_sysctl_lock); | ||
| 1280 | 1818 | ||
| 1281 | kfree(iter); | 1819 | kfree(iter); |
| 1282 | mutex_unlock(&ftrace_regex_lock); | 1820 | mutex_unlock(&ftrace_regex_lock); |
| @@ -1429,36 +1967,33 @@ ftrace_set_func(unsigned long *array, int idx, char *buffer) | |||
| 1429 | struct dyn_ftrace *rec; | 1967 | struct dyn_ftrace *rec; |
| 1430 | struct ftrace_page *pg; | 1968 | struct ftrace_page *pg; |
| 1431 | int found = 0; | 1969 | int found = 0; |
| 1432 | int i, j; | 1970 | int j; |
| 1433 | 1971 | ||
| 1434 | if (ftrace_disabled) | 1972 | if (ftrace_disabled) |
| 1435 | return -ENODEV; | 1973 | return -ENODEV; |
| 1436 | 1974 | ||
| 1437 | /* should not be called from interrupt context */ | 1975 | mutex_lock(&ftrace_lock); |
| 1438 | spin_lock(&ftrace_lock); | 1976 | do_for_each_ftrace_rec(pg, rec) { |
| 1439 | |||
| 1440 | for (pg = ftrace_pages_start; pg; pg = pg->next) { | ||
| 1441 | for (i = 0; i < pg->index; i++) { | ||
| 1442 | rec = &pg->records[i]; | ||
| 1443 | 1977 | ||
| 1444 | if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE)) | 1978 | if (rec->flags & (FTRACE_FL_FAILED | FTRACE_FL_FREE)) |
| 1445 | continue; | 1979 | continue; |
| 1446 | 1980 | ||
| 1447 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); | 1981 | kallsyms_lookup(rec->ip, NULL, NULL, NULL, str); |
| 1448 | if (strcmp(str, buffer) == 0) { | 1982 | if (strcmp(str, buffer) == 0) { |
| 1449 | found = 1; | 1983 | /* Return 1 if we add it to the array */ |
| 1450 | for (j = 0; j < idx; j++) | 1984 | found = 1; |
| 1451 | if (array[j] == rec->ip) { | 1985 | for (j = 0; j < idx; j++) |
| 1452 | found = 0; | 1986 | if (array[j] == rec->ip) { |
| 1453 | break; | 1987 | found = 0; |
| 1454 | } | 1988 | break; |
| 1455 | if (found) | 1989 | } |
| 1456 | array[idx] = rec->ip; | 1990 | if (found) |
| 1457 | break; | 1991 | array[idx] = rec->ip; |
| 1458 | } | 1992 | goto out; |
| 1459 | } | 1993 | } |
| 1460 | } | 1994 | } while_for_each_ftrace_rec(); |
| 1461 | spin_unlock(&ftrace_lock); | 1995 | out: |
| 1996 | mutex_unlock(&ftrace_lock); | ||
| 1462 | 1997 | ||
| 1463 | return found ? 0 : -EINVAL; | 1998 | return found ? 0 : -EINVAL; |
| 1464 | } | 1999 | } |
| @@ -1596,7 +2131,7 @@ static int ftrace_convert_nops(struct module *mod, | |||
| 1596 | unsigned long addr; | 2131 | unsigned long addr; |
| 1597 | unsigned long flags; | 2132 | unsigned long flags; |
| 1598 | 2133 | ||
| 1599 | mutex_lock(&ftrace_start_lock); | 2134 | mutex_lock(&ftrace_lock); |
| 1600 | p = start; | 2135 | p = start; |
| 1601 | while (p < end) { | 2136 | while (p < end) { |
| 1602 | addr = ftrace_call_adjust(*p++); | 2137 | addr = ftrace_call_adjust(*p++); |
| @@ -1615,7 +2150,7 @@ static int ftrace_convert_nops(struct module *mod, | |||
| 1615 | local_irq_save(flags); | 2150 | local_irq_save(flags); |
| 1616 | ftrace_update_code(mod); | 2151 | ftrace_update_code(mod); |
| 1617 | local_irq_restore(flags); | 2152 | local_irq_restore(flags); |
| 1618 | mutex_unlock(&ftrace_start_lock); | 2153 | mutex_unlock(&ftrace_lock); |
| 1619 | 2154 | ||
| 1620 | return 0; | 2155 | return 0; |
| 1621 | } | 2156 | } |
| @@ -1788,7 +2323,7 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf, | |||
| 1788 | if (ret < 0) | 2323 | if (ret < 0) |
| 1789 | return ret; | 2324 | return ret; |
| 1790 | 2325 | ||
| 1791 | mutex_lock(&ftrace_start_lock); | 2326 | mutex_lock(&ftrace_lock); |
| 1792 | if (val < 0) { | 2327 | if (val < 0) { |
| 1793 | /* disable pid tracing */ | 2328 | /* disable pid tracing */ |
| 1794 | if (!ftrace_pid_trace) | 2329 | if (!ftrace_pid_trace) |
| @@ -1827,7 +2362,7 @@ ftrace_pid_write(struct file *filp, const char __user *ubuf, | |||
| 1827 | ftrace_startup_enable(0); | 2362 | ftrace_startup_enable(0); |
| 1828 | 2363 | ||
| 1829 | out: | 2364 | out: |
| 1830 | mutex_unlock(&ftrace_start_lock); | 2365 | mutex_unlock(&ftrace_lock); |
| 1831 | 2366 | ||
| 1832 | return cnt; | 2367 | return cnt; |
| 1833 | } | 2368 | } |
| @@ -1890,12 +2425,12 @@ int register_ftrace_function(struct ftrace_ops *ops) | |||
| 1890 | if (unlikely(ftrace_disabled)) | 2425 | if (unlikely(ftrace_disabled)) |
| 1891 | return -1; | 2426 | return -1; |
| 1892 | 2427 | ||
| 1893 | mutex_lock(&ftrace_sysctl_lock); | 2428 | mutex_lock(&ftrace_lock); |
| 1894 | 2429 | ||
| 1895 | ret = __register_ftrace_function(ops); | 2430 | ret = __register_ftrace_function(ops); |
| 1896 | ftrace_startup(0); | 2431 | ftrace_startup(0); |
| 1897 | 2432 | ||
| 1898 | mutex_unlock(&ftrace_sysctl_lock); | 2433 | mutex_unlock(&ftrace_lock); |
| 1899 | return ret; | 2434 | return ret; |
| 1900 | } | 2435 | } |
| 1901 | 2436 | ||
| @@ -1909,10 +2444,10 @@ int unregister_ftrace_function(struct ftrace_ops *ops) | |||
| 1909 | { | 2444 | { |
| 1910 | int ret; | 2445 | int ret; |
| 1911 | 2446 | ||
| 1912 | mutex_lock(&ftrace_sysctl_lock); | 2447 | mutex_lock(&ftrace_lock); |
| 1913 | ret = __unregister_ftrace_function(ops); | 2448 | ret = __unregister_ftrace_function(ops); |
| 1914 | ftrace_shutdown(0); | 2449 | ftrace_shutdown(0); |
| 1915 | mutex_unlock(&ftrace_sysctl_lock); | 2450 | mutex_unlock(&ftrace_lock); |
| 1916 | 2451 | ||
| 1917 | return ret; | 2452 | return ret; |
| 1918 | } | 2453 | } |
| @@ -1927,7 +2462,7 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, | |||
| 1927 | if (unlikely(ftrace_disabled)) | 2462 | if (unlikely(ftrace_disabled)) |
| 1928 | return -ENODEV; | 2463 | return -ENODEV; |
| 1929 | 2464 | ||
| 1930 | mutex_lock(&ftrace_sysctl_lock); | 2465 | mutex_lock(&ftrace_lock); |
| 1931 | 2466 | ||
| 1932 | ret = proc_dointvec(table, write, file, buffer, lenp, ppos); | 2467 | ret = proc_dointvec(table, write, file, buffer, lenp, ppos); |
| 1933 | 2468 | ||
| @@ -1956,7 +2491,7 @@ ftrace_enable_sysctl(struct ctl_table *table, int write, | |||
| 1956 | } | 2491 | } |
| 1957 | 2492 | ||
| 1958 | out: | 2493 | out: |
| 1959 | mutex_unlock(&ftrace_sysctl_lock); | 2494 | mutex_unlock(&ftrace_lock); |
| 1960 | return ret; | 2495 | return ret; |
| 1961 | } | 2496 | } |
| 1962 | 2497 | ||
| @@ -2068,7 +2603,7 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc, | |||
| 2068 | { | 2603 | { |
| 2069 | int ret = 0; | 2604 | int ret = 0; |
| 2070 | 2605 | ||
| 2071 | mutex_lock(&ftrace_sysctl_lock); | 2606 | mutex_lock(&ftrace_lock); |
| 2072 | 2607 | ||
| 2073 | ftrace_suspend_notifier.notifier_call = ftrace_suspend_notifier_call; | 2608 | ftrace_suspend_notifier.notifier_call = ftrace_suspend_notifier_call; |
| 2074 | register_pm_notifier(&ftrace_suspend_notifier); | 2609 | register_pm_notifier(&ftrace_suspend_notifier); |
| @@ -2086,13 +2621,13 @@ int register_ftrace_graph(trace_func_graph_ret_t retfunc, | |||
| 2086 | ftrace_startup(FTRACE_START_FUNC_RET); | 2621 | ftrace_startup(FTRACE_START_FUNC_RET); |
| 2087 | 2622 | ||
| 2088 | out: | 2623 | out: |
| 2089 | mutex_unlock(&ftrace_sysctl_lock); | 2624 | mutex_unlock(&ftrace_lock); |
| 2090 | return ret; | 2625 | return ret; |
| 2091 | } | 2626 | } |
| 2092 | 2627 | ||
| 2093 | void unregister_ftrace_graph(void) | 2628 | void unregister_ftrace_graph(void) |
| 2094 | { | 2629 | { |
| 2095 | mutex_lock(&ftrace_sysctl_lock); | 2630 | mutex_lock(&ftrace_lock); |
| 2096 | 2631 | ||
| 2097 | atomic_dec(&ftrace_graph_active); | 2632 | atomic_dec(&ftrace_graph_active); |
| 2098 | ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; | 2633 | ftrace_graph_return = (trace_func_graph_ret_t)ftrace_stub; |
| @@ -2100,7 +2635,7 @@ void unregister_ftrace_graph(void) | |||
| 2100 | ftrace_shutdown(FTRACE_STOP_FUNC_RET); | 2635 | ftrace_shutdown(FTRACE_STOP_FUNC_RET); |
| 2101 | unregister_pm_notifier(&ftrace_suspend_notifier); | 2636 | unregister_pm_notifier(&ftrace_suspend_notifier); |
| 2102 | 2637 | ||
| 2103 | mutex_unlock(&ftrace_sysctl_lock); | 2638 | mutex_unlock(&ftrace_lock); |
| 2104 | } | 2639 | } |
| 2105 | 2640 | ||
| 2106 | /* Allocate a return stack for newly created task */ | 2641 | /* Allocate a return stack for newly created task */ |
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 2b4626ce95d6..8f19f1aa42b0 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c | |||
| @@ -98,6 +98,15 @@ void tracing_off_permanent(void) | |||
| 98 | set_bit(RB_BUFFERS_DISABLED_BIT, &ring_buffer_flags); | 98 | set_bit(RB_BUFFERS_DISABLED_BIT, &ring_buffer_flags); |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | /** | ||
| 102 | * tracing_is_on - show state of ring buffers enabled | ||
| 103 | */ | ||
| 104 | int tracing_is_on(void) | ||
| 105 | { | ||
| 106 | return ring_buffer_flags == RB_BUFFERS_ON; | ||
| 107 | } | ||
| 108 | EXPORT_SYMBOL_GPL(tracing_is_on); | ||
| 109 | |||
| 101 | #include "trace.h" | 110 | #include "trace.h" |
| 102 | 111 | ||
| 103 | /* Up this if you want to test the TIME_EXTENTS and normalization */ | 112 | /* Up this if you want to test the TIME_EXTENTS and normalization */ |
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c index 36bf9568ccd9..f520aa419dff 100644 --- a/kernel/trace/trace_functions.c +++ b/kernel/trace/trace_functions.c | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | * Copyright (C) 2004-2006 Ingo Molnar | 9 | * Copyright (C) 2004-2006 Ingo Molnar |
| 10 | * Copyright (C) 2004 William Lee Irwin III | 10 | * Copyright (C) 2004 William Lee Irwin III |
| 11 | */ | 11 | */ |
| 12 | #include <linux/ring_buffer.h> | ||
| 12 | #include <linux/debugfs.h> | 13 | #include <linux/debugfs.h> |
| 13 | #include <linux/uaccess.h> | 14 | #include <linux/uaccess.h> |
| 14 | #include <linux/ftrace.h> | 15 | #include <linux/ftrace.h> |
| @@ -231,9 +232,171 @@ static struct tracer function_trace __read_mostly = | |||
| 231 | #endif | 232 | #endif |
| 232 | }; | 233 | }; |
| 233 | 234 | ||
| 235 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
| 236 | static void | ||
| 237 | ftrace_traceon(unsigned long ip, unsigned long parent_ip, void **data) | ||
| 238 | { | ||
| 239 | long *count = (long *)data; | ||
| 240 | |||
| 241 | if (tracing_is_on()) | ||
| 242 | return; | ||
| 243 | |||
| 244 | if (!*count) | ||
| 245 | return; | ||
| 246 | |||
| 247 | if (*count != -1) | ||
| 248 | (*count)--; | ||
| 249 | |||
| 250 | tracing_on(); | ||
| 251 | } | ||
| 252 | |||
| 253 | static void | ||
| 254 | ftrace_traceoff(unsigned long ip, unsigned long parent_ip, void **data) | ||
| 255 | { | ||
| 256 | long *count = (long *)data; | ||
| 257 | |||
| 258 | if (!tracing_is_on()) | ||
| 259 | return; | ||
| 260 | |||
| 261 | if (!*count) | ||
| 262 | return; | ||
| 263 | |||
| 264 | if (*count != -1) | ||
| 265 | (*count)--; | ||
| 266 | |||
| 267 | tracing_off(); | ||
| 268 | } | ||
| 269 | |||
| 270 | static int | ||
| 271 | ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip, | ||
| 272 | struct ftrace_hook_ops *ops, void *data); | ||
| 273 | |||
| 274 | static struct ftrace_hook_ops traceon_hook_ops = { | ||
| 275 | .func = ftrace_traceon, | ||
| 276 | .print = ftrace_trace_onoff_print, | ||
| 277 | }; | ||
| 278 | |||
| 279 | static struct ftrace_hook_ops traceoff_hook_ops = { | ||
| 280 | .func = ftrace_traceoff, | ||
| 281 | .print = ftrace_trace_onoff_print, | ||
| 282 | }; | ||
| 283 | |||
| 284 | static int | ||
| 285 | ftrace_trace_onoff_print(struct seq_file *m, unsigned long ip, | ||
| 286 | struct ftrace_hook_ops *ops, void *data) | ||
| 287 | { | ||
| 288 | char str[KSYM_SYMBOL_LEN]; | ||
| 289 | long count = (long)data; | ||
| 290 | |||
| 291 | kallsyms_lookup(ip, NULL, NULL, NULL, str); | ||
| 292 | seq_printf(m, "%s:", str); | ||
| 293 | |||
| 294 | if (ops == &traceon_hook_ops) | ||
| 295 | seq_printf(m, "traceon"); | ||
| 296 | else | ||
| 297 | seq_printf(m, "traceoff"); | ||
| 298 | |||
| 299 | if (count != -1) | ||
| 300 | seq_printf(m, ":count=%ld", count); | ||
| 301 | seq_putc(m, '\n'); | ||
| 302 | |||
| 303 | return 0; | ||
| 304 | } | ||
| 305 | |||
| 306 | static int | ||
| 307 | ftrace_trace_onoff_unreg(char *glob, char *cmd, char *param) | ||
| 308 | { | ||
| 309 | struct ftrace_hook_ops *ops; | ||
| 310 | |||
| 311 | /* we register both traceon and traceoff to this callback */ | ||
| 312 | if (strcmp(cmd, "traceon") == 0) | ||
| 313 | ops = &traceon_hook_ops; | ||
| 314 | else | ||
| 315 | ops = &traceoff_hook_ops; | ||
| 316 | |||
| 317 | unregister_ftrace_function_hook_func(glob, ops); | ||
| 318 | |||
| 319 | return 0; | ||
| 320 | } | ||
| 321 | |||
| 322 | static int | ||
| 323 | ftrace_trace_onoff_callback(char *glob, char *cmd, char *param, int enable) | ||
| 324 | { | ||
| 325 | struct ftrace_hook_ops *ops; | ||
| 326 | void *count = (void *)-1; | ||
| 327 | char *number; | ||
| 328 | int ret; | ||
| 329 | |||
| 330 | /* hash funcs only work with set_ftrace_filter */ | ||
| 331 | if (!enable) | ||
| 332 | return -EINVAL; | ||
| 333 | |||
| 334 | if (glob[0] == '!') | ||
| 335 | return ftrace_trace_onoff_unreg(glob+1, cmd, param); | ||
| 336 | |||
| 337 | /* we register both traceon and traceoff to this callback */ | ||
| 338 | if (strcmp(cmd, "traceon") == 0) | ||
| 339 | ops = &traceon_hook_ops; | ||
| 340 | else | ||
| 341 | ops = &traceoff_hook_ops; | ||
| 342 | |||
| 343 | if (!param) | ||
| 344 | goto out_reg; | ||
| 345 | |||
| 346 | number = strsep(¶m, ":"); | ||
| 347 | |||
| 348 | if (!strlen(number)) | ||
| 349 | goto out_reg; | ||
| 350 | |||
| 351 | /* | ||
| 352 | * We use the callback data field (which is a pointer) | ||
| 353 | * as our counter. | ||
| 354 | */ | ||
| 355 | ret = strict_strtoul(number, 0, (unsigned long *)&count); | ||
| 356 | if (ret) | ||
| 357 | return ret; | ||
| 358 | |||
| 359 | out_reg: | ||
| 360 | ret = register_ftrace_function_hook(glob, ops, count); | ||
| 361 | |||
| 362 | return ret; | ||
| 363 | } | ||
| 364 | |||
| 365 | static struct ftrace_func_command ftrace_traceon_cmd = { | ||
| 366 | .name = "traceon", | ||
| 367 | .func = ftrace_trace_onoff_callback, | ||
| 368 | }; | ||
| 369 | |||
| 370 | static struct ftrace_func_command ftrace_traceoff_cmd = { | ||
| 371 | .name = "traceoff", | ||
| 372 | .func = ftrace_trace_onoff_callback, | ||
| 373 | }; | ||
| 374 | |||
| 375 | static int __init init_func_cmd_traceon(void) | ||
| 376 | { | ||
| 377 | int ret; | ||
| 378 | |||
| 379 | ret = register_ftrace_command(&ftrace_traceoff_cmd); | ||
| 380 | if (ret) | ||
| 381 | return ret; | ||
| 382 | |||
| 383 | ret = register_ftrace_command(&ftrace_traceon_cmd); | ||
| 384 | if (ret) | ||
| 385 | unregister_ftrace_command(&ftrace_traceoff_cmd); | ||
| 386 | return ret; | ||
| 387 | } | ||
| 388 | #else | ||
| 389 | static inline int init_func_cmd_traceon(void) | ||
| 390 | { | ||
| 391 | return 0; | ||
| 392 | } | ||
| 393 | #endif /* CONFIG_DYNAMIC_FTRACE */ | ||
| 394 | |||
| 234 | static __init int init_function_trace(void) | 395 | static __init int init_function_trace(void) |
| 235 | { | 396 | { |
| 397 | init_func_cmd_traceon(); | ||
| 236 | return register_tracer(&function_trace); | 398 | return register_tracer(&function_trace); |
| 237 | } | 399 | } |
| 238 | 400 | ||
| 239 | device_initcall(init_function_trace); | 401 | device_initcall(init_function_trace); |
| 402 | |||
