diff options
Diffstat (limited to 'drivers/oprofile/cpu_buffer.c')
-rw-r--r-- | drivers/oprofile/cpu_buffer.c | 139 |
1 files changed, 75 insertions, 64 deletions
diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index 400f7fcffdbe..e859d23cfc57 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c | |||
@@ -212,6 +212,59 @@ unsigned long op_cpu_buffer_entries(int cpu) | |||
212 | + ring_buffer_entries_cpu(op_ring_buffer_write, cpu); | 212 | + ring_buffer_entries_cpu(op_ring_buffer_write, cpu); |
213 | } | 213 | } |
214 | 214 | ||
215 | static int | ||
216 | op_add_code(struct oprofile_cpu_buffer *cpu_buf, unsigned long backtrace, | ||
217 | int is_kernel, struct task_struct *task) | ||
218 | { | ||
219 | struct op_entry entry; | ||
220 | struct op_sample *sample; | ||
221 | unsigned long flags; | ||
222 | int size; | ||
223 | |||
224 | flags = 0; | ||
225 | |||
226 | if (backtrace) | ||
227 | flags |= TRACE_BEGIN; | ||
228 | |||
229 | /* notice a switch from user->kernel or vice versa */ | ||
230 | is_kernel = !!is_kernel; | ||
231 | if (cpu_buf->last_is_kernel != is_kernel) { | ||
232 | cpu_buf->last_is_kernel = is_kernel; | ||
233 | flags |= KERNEL_CTX_SWITCH; | ||
234 | if (is_kernel) | ||
235 | flags |= IS_KERNEL; | ||
236 | } | ||
237 | |||
238 | /* notice a task switch */ | ||
239 | if (cpu_buf->last_task != task) { | ||
240 | cpu_buf->last_task = task; | ||
241 | flags |= USER_CTX_SWITCH; | ||
242 | } | ||
243 | |||
244 | if (!flags) | ||
245 | /* nothing to do */ | ||
246 | return 0; | ||
247 | |||
248 | if (flags & USER_CTX_SWITCH) | ||
249 | size = 1; | ||
250 | else | ||
251 | size = 0; | ||
252 | |||
253 | sample = op_cpu_buffer_write_reserve(&entry, size); | ||
254 | if (!sample) | ||
255 | return -ENOMEM; | ||
256 | |||
257 | sample->eip = ESCAPE_CODE; | ||
258 | sample->event = flags; | ||
259 | |||
260 | if (size) | ||
261 | sample->data[0] = (unsigned long)task; | ||
262 | |||
263 | op_cpu_buffer_write_commit(&entry); | ||
264 | |||
265 | return 0; | ||
266 | } | ||
267 | |||
215 | static inline int | 268 | static inline int |
216 | op_add_sample(struct oprofile_cpu_buffer *cpu_buf, | 269 | op_add_sample(struct oprofile_cpu_buffer *cpu_buf, |
217 | unsigned long pc, unsigned long event) | 270 | unsigned long pc, unsigned long event) |
@@ -229,26 +282,18 @@ op_add_sample(struct oprofile_cpu_buffer *cpu_buf, | |||
229 | return op_cpu_buffer_write_commit(&entry); | 282 | return op_cpu_buffer_write_commit(&entry); |
230 | } | 283 | } |
231 | 284 | ||
232 | static inline int | 285 | /* |
233 | add_code(struct oprofile_cpu_buffer *buffer, unsigned long value) | 286 | * This must be safe from any context. |
234 | { | ||
235 | return op_add_sample(buffer, ESCAPE_CODE, value); | ||
236 | } | ||
237 | |||
238 | /* This must be safe from any context. It's safe writing here | ||
239 | * because of the head/tail separation of the writer and reader | ||
240 | * of the CPU buffer. | ||
241 | * | 287 | * |
242 | * is_kernel is needed because on some architectures you cannot | 288 | * is_kernel is needed because on some architectures you cannot |
243 | * tell if you are in kernel or user space simply by looking at | 289 | * tell if you are in kernel or user space simply by looking at |
244 | * pc. We tag this in the buffer by generating kernel enter/exit | 290 | * pc. We tag this in the buffer by generating kernel enter/exit |
245 | * events whenever is_kernel changes | 291 | * events whenever is_kernel changes |
246 | */ | 292 | */ |
247 | static int log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, | 293 | static int |
248 | int is_kernel, unsigned long event) | 294 | log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, |
295 | unsigned long backtrace, int is_kernel, unsigned long event) | ||
249 | { | 296 | { |
250 | struct task_struct *task; | ||
251 | |||
252 | cpu_buf->sample_received++; | 297 | cpu_buf->sample_received++; |
253 | 298 | ||
254 | if (pc == ESCAPE_CODE) { | 299 | if (pc == ESCAPE_CODE) { |
@@ -256,23 +301,8 @@ static int log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, | |||
256 | return 0; | 301 | return 0; |
257 | } | 302 | } |
258 | 303 | ||
259 | is_kernel = !!is_kernel; | 304 | if (op_add_code(cpu_buf, backtrace, is_kernel, current)) |
260 | 305 | goto fail; | |
261 | task = current; | ||
262 | |||
263 | /* notice a switch from user->kernel or vice versa */ | ||
264 | if (cpu_buf->last_is_kernel != is_kernel) { | ||
265 | cpu_buf->last_is_kernel = is_kernel; | ||
266 | if (add_code(cpu_buf, is_kernel)) | ||
267 | goto fail; | ||
268 | } | ||
269 | |||
270 | /* notice a task switch */ | ||
271 | if (cpu_buf->last_task != task) { | ||
272 | cpu_buf->last_task = task; | ||
273 | if (add_code(cpu_buf, (unsigned long)task)) | ||
274 | goto fail; | ||
275 | } | ||
276 | 306 | ||
277 | if (op_add_sample(cpu_buf, pc, event)) | 307 | if (op_add_sample(cpu_buf, pc, event)) |
278 | goto fail; | 308 | goto fail; |
@@ -286,7 +316,6 @@ fail: | |||
286 | 316 | ||
287 | static inline void oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf) | 317 | static inline void oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf) |
288 | { | 318 | { |
289 | add_code(cpu_buf, CPU_TRACE_BEGIN); | ||
290 | cpu_buf->tracing = 1; | 319 | cpu_buf->tracing = 1; |
291 | } | 320 | } |
292 | 321 | ||
@@ -300,21 +329,21 @@ __oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs, | |||
300 | unsigned long event, int is_kernel) | 329 | unsigned long event, int is_kernel) |
301 | { | 330 | { |
302 | struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); | 331 | struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); |
303 | 332 | unsigned long backtrace = oprofile_backtrace_depth; | |
304 | if (!oprofile_backtrace_depth) { | ||
305 | log_sample(cpu_buf, pc, is_kernel, event); | ||
306 | return; | ||
307 | } | ||
308 | |||
309 | oprofile_begin_trace(cpu_buf); | ||
310 | 333 | ||
311 | /* | 334 | /* |
312 | * if log_sample() fail we can't backtrace since we lost the | 335 | * if log_sample() fail we can't backtrace since we lost the |
313 | * source of this event | 336 | * source of this event |
314 | */ | 337 | */ |
315 | if (log_sample(cpu_buf, pc, is_kernel, event)) | 338 | if (!log_sample(cpu_buf, pc, backtrace, is_kernel, event)) |
316 | oprofile_ops.backtrace(regs, oprofile_backtrace_depth); | 339 | /* failed */ |
340 | return; | ||
317 | 341 | ||
342 | if (!backtrace) | ||
343 | return; | ||
344 | |||
345 | oprofile_begin_trace(cpu_buf); | ||
346 | oprofile_ops.backtrace(regs, backtrace); | ||
318 | oprofile_end_trace(cpu_buf); | 347 | oprofile_end_trace(cpu_buf); |
319 | } | 348 | } |
320 | 349 | ||
@@ -339,29 +368,14 @@ void oprofile_add_ibs_sample(struct pt_regs * const regs, | |||
339 | { | 368 | { |
340 | int is_kernel = !user_mode(regs); | 369 | int is_kernel = !user_mode(regs); |
341 | struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); | 370 | struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); |
342 | struct task_struct *task; | ||
343 | int fail = 0; | 371 | int fail = 0; |
344 | 372 | ||
345 | cpu_buf->sample_received++; | 373 | cpu_buf->sample_received++; |
346 | 374 | ||
347 | /* notice a switch from user->kernel or vice versa */ | 375 | /* backtraces disabled for ibs */ |
348 | if (cpu_buf->last_is_kernel != is_kernel) { | 376 | fail = fail || op_add_code(cpu_buf, 0, is_kernel, current); |
349 | if (add_code(cpu_buf, is_kernel)) | ||
350 | goto fail; | ||
351 | cpu_buf->last_is_kernel = is_kernel; | ||
352 | } | ||
353 | 377 | ||
354 | /* notice a task switch */ | 378 | fail = fail || op_add_sample(cpu_buf, ESCAPE_CODE, ibs_code); |
355 | if (!is_kernel) { | ||
356 | task = current; | ||
357 | if (cpu_buf->last_task != task) { | ||
358 | if (add_code(cpu_buf, (unsigned long)task)) | ||
359 | goto fail; | ||
360 | cpu_buf->last_task = task; | ||
361 | } | ||
362 | } | ||
363 | |||
364 | fail = fail || add_code(cpu_buf, ibs_code); | ||
365 | fail = fail || op_add_sample(cpu_buf, ibs_sample[0], ibs_sample[1]); | 379 | fail = fail || op_add_sample(cpu_buf, ibs_sample[0], ibs_sample[1]); |
366 | fail = fail || op_add_sample(cpu_buf, ibs_sample[2], ibs_sample[3]); | 380 | fail = fail || op_add_sample(cpu_buf, ibs_sample[2], ibs_sample[3]); |
367 | fail = fail || op_add_sample(cpu_buf, ibs_sample[4], ibs_sample[5]); | 381 | fail = fail || op_add_sample(cpu_buf, ibs_sample[4], ibs_sample[5]); |
@@ -372,11 +386,8 @@ void oprofile_add_ibs_sample(struct pt_regs * const regs, | |||
372 | fail = fail || op_add_sample(cpu_buf, ibs_sample[10], ibs_sample[11]); | 386 | fail = fail || op_add_sample(cpu_buf, ibs_sample[10], ibs_sample[11]); |
373 | } | 387 | } |
374 | 388 | ||
375 | if (!fail) | 389 | if (fail) |
376 | return; | 390 | cpu_buf->sample_lost_overflow++; |
377 | |||
378 | fail: | ||
379 | cpu_buf->sample_lost_overflow++; | ||
380 | } | 391 | } |
381 | 392 | ||
382 | #endif | 393 | #endif |
@@ -384,7 +395,7 @@ fail: | |||
384 | void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event) | 395 | void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event) |
385 | { | 396 | { |
386 | struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); | 397 | struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer); |
387 | log_sample(cpu_buf, pc, is_kernel, event); | 398 | log_sample(cpu_buf, pc, 0, is_kernel, event); |
388 | } | 399 | } |
389 | 400 | ||
390 | void oprofile_add_trace(unsigned long pc) | 401 | void oprofile_add_trace(unsigned long pc) |