aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAbhishek Sagar <sagar.abhishek@gmail.com>2008-06-21 14:17:53 -0400
committerIngo Molnar <mingo@elte.hu>2008-06-23 16:10:58 -0400
commitecea656d1d5e912d2f3d332657ea4a6d8380f891 (patch)
treefd139cdd762230bf4e55a1341ba29e2b309968b1
parent785656a41f9a9c0e843a23d1ae05d900b5158f8f (diff)
ftrace: freeze kprobe'd records
Let records identified as being kprobe'd be marked as "frozen". The trouble with records which have a kprobe installed on their mcount call-site is that they don't get updated. So if such a function which is currently being traced gets its tracing disabled due to a new filter rule (or because it was added to the notrace list) then it won't be updated and continue being traced. This patch allows scanning of all frozen records during tracing to check if they should be traced. Signed-off-by: Abhishek Sagar <sagar.abhishek@gmail.com> Cc: Steven Rostedt <rostedt@goodmis.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--include/linux/ftrace.h6
-rw-r--r--kernel/trace/ftrace.c72
-rw-r--r--kernel/trace/trace.c3
3 files changed, 79 insertions, 2 deletions
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 366098d591d..3121b95443d 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -49,6 +49,7 @@ enum {
49 FTRACE_FL_ENABLED = (1 << 3), 49 FTRACE_FL_ENABLED = (1 << 3),
50 FTRACE_FL_NOTRACE = (1 << 4), 50 FTRACE_FL_NOTRACE = (1 << 4),
51 FTRACE_FL_CONVERTED = (1 << 5), 51 FTRACE_FL_CONVERTED = (1 << 5),
52 FTRACE_FL_FROZEN = (1 << 6),
52}; 53};
53 54
54struct dyn_ftrace { 55struct dyn_ftrace {
@@ -73,15 +74,18 @@ extern void ftrace_caller(void);
73extern void ftrace_call(void); 74extern void ftrace_call(void);
74extern void mcount_call(void); 75extern void mcount_call(void);
75 76
77extern int skip_trace(unsigned long ip);
78
76void ftrace_disable_daemon(void); 79void ftrace_disable_daemon(void);
77void ftrace_enable_daemon(void); 80void ftrace_enable_daemon(void);
78 81
79#else 82#else
83# define skip_trace(ip) ({ 0; })
80# define ftrace_force_update() ({ 0; }) 84# define ftrace_force_update() ({ 0; })
81# define ftrace_set_filter(buf, len, reset) do { } while (0) 85# define ftrace_set_filter(buf, len, reset) do { } while (0)
82# define ftrace_disable_daemon() do { } while (0) 86# define ftrace_disable_daemon() do { } while (0)
83# define ftrace_enable_daemon() do { } while (0) 87# define ftrace_enable_daemon() do { } while (0)
84#endif 88#endif /* CONFIG_DYNAMIC_FTRACE */
85 89
86/* totally disable ftrace - can not re-enable after this */ 90/* totally disable ftrace - can not re-enable after this */
87void ftrace_kill(void); 91void ftrace_kill(void);
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index f1e9e5c74e6..d1238163155 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -163,6 +163,8 @@ enum {
163}; 163};
164 164
165static int ftrace_filtered; 165static int ftrace_filtered;
166static int tracing_on;
167static int frozen_record_count;
166 168
167static struct hlist_head ftrace_hash[FTRACE_HASHSIZE]; 169static struct hlist_head ftrace_hash[FTRACE_HASHSIZE];
168 170
@@ -195,6 +197,71 @@ static int ftrace_record_suspend;
195 197
196static struct dyn_ftrace *ftrace_free_records; 198static struct dyn_ftrace *ftrace_free_records;
197 199
200
201#ifdef CONFIG_KPROBES
202static inline void freeze_record(struct dyn_ftrace *rec)
203{
204 if (!(rec->flags & FTRACE_FL_FROZEN)) {
205 rec->flags |= FTRACE_FL_FROZEN;
206 frozen_record_count++;
207 }
208}
209
210static inline void unfreeze_record(struct dyn_ftrace *rec)
211{
212 if (rec->flags & FTRACE_FL_FROZEN) {
213 rec->flags &= ~FTRACE_FL_FROZEN;
214 frozen_record_count--;
215 }
216}
217
218static inline int record_frozen(struct dyn_ftrace *rec)
219{
220 return rec->flags & FTRACE_FL_FROZEN;
221}
222#else
223# define freeze_record(rec) ({ 0; })
224# define unfreeze_record(rec) ({ 0; })
225# define record_frozen(rec) ({ 0; })
226#endif /* CONFIG_KPROBES */
227
228int skip_trace(unsigned long ip)
229{
230 unsigned long fl;
231 struct dyn_ftrace *rec;
232 struct hlist_node *t;
233 struct hlist_head *head;
234
235 if (frozen_record_count == 0)
236 return 0;
237
238 head = &ftrace_hash[hash_long(ip, FTRACE_HASHBITS)];
239 hlist_for_each_entry_rcu(rec, t, head, node) {
240 if (rec->ip == ip) {
241 if (record_frozen(rec)) {
242 if (rec->flags & FTRACE_FL_FAILED)
243 return 1;
244
245 if (!(rec->flags & FTRACE_FL_CONVERTED))
246 return 1;
247
248 if (!tracing_on || !ftrace_enabled)
249 return 1;
250
251 if (ftrace_filtered) {
252 fl = rec->flags & (FTRACE_FL_FILTER |
253 FTRACE_FL_NOTRACE);
254 if (!fl || (fl & FTRACE_FL_NOTRACE))
255 return 1;
256 }
257 }
258 break;
259 }
260 }
261
262 return 0;
263}
264
198static inline int 265static inline int
199ftrace_ip_in_hash(unsigned long ip, unsigned long key) 266ftrace_ip_in_hash(unsigned long ip, unsigned long key)
200{ 267{
@@ -489,8 +556,11 @@ static int __ftrace_modify_code(void *data)
489 */ 556 */
490 __ftrace_update_code(NULL); 557 __ftrace_update_code(NULL);
491 ftrace_replace_code(1); 558 ftrace_replace_code(1);
492 } else if (*command & FTRACE_DISABLE_CALLS) 559 tracing_on = 1;
560 } else if (*command & FTRACE_DISABLE_CALLS) {
493 ftrace_replace_code(0); 561 ftrace_replace_code(0);
562 tracing_on = 0;
563 }
494 564
495 if (*command & FTRACE_UPDATE_TRACE_FUNC) 565 if (*command & FTRACE_UPDATE_TRACE_FUNC)
496 ftrace_update_ftrace_func(ftrace_trace_function); 566 ftrace_update_ftrace_func(ftrace_trace_function);
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 6e9dae7eb41..9ade79369bf 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -988,6 +988,9 @@ function_trace_call(unsigned long ip, unsigned long parent_ip)
988 if (unlikely(!tracer_enabled)) 988 if (unlikely(!tracer_enabled))
989 return; 989 return;
990 990
991 if (skip_trace(ip))
992 return;
993
991 local_irq_save(flags); 994 local_irq_save(flags);
992 cpu = raw_smp_processor_id(); 995 cpu = raw_smp_processor_id();
993 data = tr->data[cpu]; 996 data = tr->data[cpu];