diff options
author | Steven Rostedt (Red Hat) <rostedt@goodmis.org> | 2013-03-12 13:26:18 -0400 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2013-03-15 00:36:03 -0400 |
commit | 417944c4c7a0f657158d0515f3b8e8c043fd788f (patch) | |
tree | 838e9bb09f8df63af3adf5b865d3781eeac8d6ba /kernel/trace | |
parent | 7818b3886545f89549185e4023743e2df91d1fa1 (diff) |
tracing: Add a way to soft disable trace events
In order to let triggers enable or disable events, we need a 'soft'
method for doing so. For example, if a function probe is added that
lets a user enable or disable events when a function is called, that
change must be done without taking locks or a mutex, and definitely
it can't sleep. But the full enabling of a tracepoint is expensive.
By adding a 'SOFT_DISABLE' flag, and converting the flags to be updated
without the protection of a mutex (using set/clear_bit()), this soft
disable flag can be used to allow critical sections to enable or disable
events from being traced (after the event has been placed into "SOFT_MODE").
Some caveats though: The comm recorder (to map pids with a comm) can not
be soft disabled (yet). If you disable an event with with a "soft"
disable and wait a while before reading the trace, the comm cache may be
replaced and you'll get a bunch of <...> for comms in the trace.
Reading the "enable" file for an event that is disabled will now give
you "0*" where the '*' denotes that the tracepoint is still active but
the event itself is "disabled".
[ fixed _BIT used in & operation : thanks to Dan Carpenter and smatch ]
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Cc: Tom Zanussi <tom.zanussi@linux.intel.com>
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'kernel/trace')
-rw-r--r-- | kernel/trace/trace_events.c | 75 |
1 files changed, 62 insertions, 13 deletions
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 38b54c5edeb9..106640b0df4a 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c | |||
@@ -205,37 +205,77 @@ void trace_event_enable_cmd_record(bool enable) | |||
205 | 205 | ||
206 | if (enable) { | 206 | if (enable) { |
207 | tracing_start_cmdline_record(); | 207 | tracing_start_cmdline_record(); |
208 | file->flags |= FTRACE_EVENT_FL_RECORDED_CMD; | 208 | set_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags); |
209 | } else { | 209 | } else { |
210 | tracing_stop_cmdline_record(); | 210 | tracing_stop_cmdline_record(); |
211 | file->flags &= ~FTRACE_EVENT_FL_RECORDED_CMD; | 211 | clear_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags); |
212 | } | 212 | } |
213 | } while_for_each_event_file(); | 213 | } while_for_each_event_file(); |
214 | mutex_unlock(&event_mutex); | 214 | mutex_unlock(&event_mutex); |
215 | } | 215 | } |
216 | 216 | ||
217 | static int ftrace_event_enable_disable(struct ftrace_event_file *file, | 217 | static int __ftrace_event_enable_disable(struct ftrace_event_file *file, |
218 | int enable) | 218 | int enable, int soft_disable) |
219 | { | 219 | { |
220 | struct ftrace_event_call *call = file->event_call; | 220 | struct ftrace_event_call *call = file->event_call; |
221 | int ret = 0; | 221 | int ret = 0; |
222 | int disable; | ||
222 | 223 | ||
223 | switch (enable) { | 224 | switch (enable) { |
224 | case 0: | 225 | case 0: |
225 | if (file->flags & FTRACE_EVENT_FL_ENABLED) { | 226 | /* |
226 | file->flags &= ~FTRACE_EVENT_FL_ENABLED; | 227 | * When soft_disable is set and enable is cleared, we want |
228 | * to clear the SOFT_DISABLED flag but leave the event in the | ||
229 | * state that it was. That is, if the event was enabled and | ||
230 | * SOFT_DISABLED isn't set, then do nothing. But if SOFT_DISABLED | ||
231 | * is set we do not want the event to be enabled before we | ||
232 | * clear the bit. | ||
233 | * | ||
234 | * When soft_disable is not set but the SOFT_MODE flag is, | ||
235 | * we do nothing. Do not disable the tracepoint, otherwise | ||
236 | * "soft enable"s (clearing the SOFT_DISABLED bit) wont work. | ||
237 | */ | ||
238 | if (soft_disable) { | ||
239 | disable = file->flags & FTRACE_EVENT_FL_SOFT_DISABLED; | ||
240 | clear_bit(FTRACE_EVENT_FL_SOFT_MODE_BIT, &file->flags); | ||
241 | } else | ||
242 | disable = !(file->flags & FTRACE_EVENT_FL_SOFT_MODE); | ||
243 | |||
244 | if (disable && (file->flags & FTRACE_EVENT_FL_ENABLED)) { | ||
245 | clear_bit(FTRACE_EVENT_FL_ENABLED_BIT, &file->flags); | ||
227 | if (file->flags & FTRACE_EVENT_FL_RECORDED_CMD) { | 246 | if (file->flags & FTRACE_EVENT_FL_RECORDED_CMD) { |
228 | tracing_stop_cmdline_record(); | 247 | tracing_stop_cmdline_record(); |
229 | file->flags &= ~FTRACE_EVENT_FL_RECORDED_CMD; | 248 | clear_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags); |
230 | } | 249 | } |
231 | call->class->reg(call, TRACE_REG_UNREGISTER, file); | 250 | call->class->reg(call, TRACE_REG_UNREGISTER, file); |
232 | } | 251 | } |
252 | /* If in SOFT_MODE, just set the SOFT_DISABLE_BIT */ | ||
253 | if (file->flags & FTRACE_EVENT_FL_SOFT_MODE) | ||
254 | set_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags); | ||
233 | break; | 255 | break; |
234 | case 1: | 256 | case 1: |
257 | /* | ||
258 | * When soft_disable is set and enable is set, we want to | ||
259 | * register the tracepoint for the event, but leave the event | ||
260 | * as is. That means, if the event was already enabled, we do | ||
261 | * nothing (but set SOFT_MODE). If the event is disabled, we | ||
262 | * set SOFT_DISABLED before enabling the event tracepoint, so | ||
263 | * it still seems to be disabled. | ||
264 | */ | ||
265 | if (!soft_disable) | ||
266 | clear_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags); | ||
267 | else | ||
268 | set_bit(FTRACE_EVENT_FL_SOFT_MODE_BIT, &file->flags); | ||
269 | |||
235 | if (!(file->flags & FTRACE_EVENT_FL_ENABLED)) { | 270 | if (!(file->flags & FTRACE_EVENT_FL_ENABLED)) { |
271 | |||
272 | /* Keep the event disabled, when going to SOFT_MODE. */ | ||
273 | if (soft_disable) | ||
274 | set_bit(FTRACE_EVENT_FL_SOFT_DISABLED_BIT, &file->flags); | ||
275 | |||
236 | if (trace_flags & TRACE_ITER_RECORD_CMD) { | 276 | if (trace_flags & TRACE_ITER_RECORD_CMD) { |
237 | tracing_start_cmdline_record(); | 277 | tracing_start_cmdline_record(); |
238 | file->flags |= FTRACE_EVENT_FL_RECORDED_CMD; | 278 | set_bit(FTRACE_EVENT_FL_RECORDED_CMD_BIT, &file->flags); |
239 | } | 279 | } |
240 | ret = call->class->reg(call, TRACE_REG_REGISTER, file); | 280 | ret = call->class->reg(call, TRACE_REG_REGISTER, file); |
241 | if (ret) { | 281 | if (ret) { |
@@ -244,7 +284,7 @@ static int ftrace_event_enable_disable(struct ftrace_event_file *file, | |||
244 | "%s\n", call->name); | 284 | "%s\n", call->name); |
245 | break; | 285 | break; |
246 | } | 286 | } |
247 | file->flags |= FTRACE_EVENT_FL_ENABLED; | 287 | set_bit(FTRACE_EVENT_FL_ENABLED_BIT, &file->flags); |
248 | 288 | ||
249 | /* WAS_ENABLED gets set but never cleared. */ | 289 | /* WAS_ENABLED gets set but never cleared. */ |
250 | call->flags |= TRACE_EVENT_FL_WAS_ENABLED; | 290 | call->flags |= TRACE_EVENT_FL_WAS_ENABLED; |
@@ -255,6 +295,12 @@ static int ftrace_event_enable_disable(struct ftrace_event_file *file, | |||
255 | return ret; | 295 | return ret; |
256 | } | 296 | } |
257 | 297 | ||
298 | static int ftrace_event_enable_disable(struct ftrace_event_file *file, | ||
299 | int enable) | ||
300 | { | ||
301 | return __ftrace_event_enable_disable(file, enable, 0); | ||
302 | } | ||
303 | |||
258 | static void ftrace_clear_events(struct trace_array *tr) | 304 | static void ftrace_clear_events(struct trace_array *tr) |
259 | { | 305 | { |
260 | struct ftrace_event_file *file; | 306 | struct ftrace_event_file *file; |
@@ -547,12 +593,15 @@ event_enable_read(struct file *filp, char __user *ubuf, size_t cnt, | |||
547 | struct ftrace_event_file *file = filp->private_data; | 593 | struct ftrace_event_file *file = filp->private_data; |
548 | char *buf; | 594 | char *buf; |
549 | 595 | ||
550 | if (file->flags & FTRACE_EVENT_FL_ENABLED) | 596 | if (file->flags & FTRACE_EVENT_FL_ENABLED) { |
551 | buf = "1\n"; | 597 | if (file->flags & FTRACE_EVENT_FL_SOFT_DISABLED) |
552 | else | 598 | buf = "0*\n"; |
599 | else | ||
600 | buf = "1\n"; | ||
601 | } else | ||
553 | buf = "0\n"; | 602 | buf = "0\n"; |
554 | 603 | ||
555 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, 2); | 604 | return simple_read_from_buffer(ubuf, cnt, ppos, buf, strlen(buf)); |
556 | } | 605 | } |
557 | 606 | ||
558 | static ssize_t | 607 | static ssize_t |