diff options
author | Robert Richter <robert.richter@amd.com> | 2008-12-25 11:26:07 -0500 |
---|---|---|
committer | Robert Richter <robert.richter@amd.com> | 2009-01-07 16:40:47 -0500 |
commit | ae735e9964b4584923f2997d98a8d80ae9c1a75c (patch) | |
tree | 0fc72d18bcc5951f9dd519e8a4527593724b816f | |
parent | 2d87b14cf8d0b07720de26d90789d02124141616 (diff) |
oprofile: rework implementation of cpu buffer events
Special events such as task or context switches are marked with an
escape code in the cpu buffer followed by an event code or a task
identifier. There is one escape code per event. To make escape
sequences also available for data samples the internal cpu buffer
format must be changed. The current implementation does not allow the
extension of event codes since this would lead to collisions with the
task identifiers. To avoid this, this patch introduces an event mask
that allows the storage of multiple events with one escape code. Now,
task identifiers are stored in the data section of the sample. The
implementation also allows the usage of custom data in a sample. As a
side effect the new code is much more readable and easier to
understand.
Signed-off-by: Robert Richter <robert.richter@amd.com>
-rw-r--r-- | arch/x86/oprofile/op_model_amd.c | 8 | ||||
-rw-r--r-- | drivers/oprofile/buffer_sync.c | 42 | ||||
-rw-r--r-- | drivers/oprofile/cpu_buffer.c | 139 | ||||
-rw-r--r-- | drivers/oprofile/cpu_buffer.h | 12 |
4 files changed, 106 insertions, 95 deletions
diff --git a/arch/x86/oprofile/op_model_amd.c b/arch/x86/oprofile/op_model_amd.c index 423a95438cbc..f101724db80a 100644 --- a/arch/x86/oprofile/op_model_amd.c +++ b/arch/x86/oprofile/op_model_amd.c | |||
@@ -2,7 +2,7 @@ | |||
2 | * @file op_model_amd.c | 2 | * @file op_model_amd.c |
3 | * athlon / K7 / K8 / Family 10h model-specific MSR operations | 3 | * athlon / K7 / K8 / Family 10h model-specific MSR operations |
4 | * | 4 | * |
5 | * @remark Copyright 2002-2008 OProfile authors | 5 | * @remark Copyright 2002-2009 OProfile authors |
6 | * @remark Read the file COPYING | 6 | * @remark Read the file COPYING |
7 | * | 7 | * |
8 | * @author John Levon | 8 | * @author John Levon |
@@ -10,7 +10,7 @@ | |||
10 | * @author Graydon Hoare | 10 | * @author Graydon Hoare |
11 | * @author Robert Richter <robert.richter@amd.com> | 11 | * @author Robert Richter <robert.richter@amd.com> |
12 | * @author Barry Kasindorf | 12 | * @author Barry Kasindorf |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #include <linux/oprofile.h> | 15 | #include <linux/oprofile.h> |
16 | #include <linux/device.h> | 16 | #include <linux/device.h> |
@@ -62,8 +62,8 @@ static unsigned long reset_value[NUM_COUNTERS]; | |||
62 | 62 | ||
63 | /* Codes used in cpu_buffer.c */ | 63 | /* Codes used in cpu_buffer.c */ |
64 | /* This produces duplicate code, need to be fixed */ | 64 | /* This produces duplicate code, need to be fixed */ |
65 | #define IBS_FETCH_BEGIN 3 | 65 | #define IBS_FETCH_BEGIN (1UL << 4) |
66 | #define IBS_OP_BEGIN 4 | 66 | #define IBS_OP_BEGIN (1UL << 5) |
67 | 67 | ||
68 | /* | 68 | /* |
69 | * The function interface needs to be fixed, something like add | 69 | * The function interface needs to be fixed, something like add |
diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index 908202afbae9..d969bb13a252 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c | |||
@@ -1,11 +1,12 @@ | |||
1 | /** | 1 | /** |
2 | * @file buffer_sync.c | 2 | * @file buffer_sync.c |
3 | * | 3 | * |
4 | * @remark Copyright 2002 OProfile authors | 4 | * @remark Copyright 2002-2009 OProfile authors |
5 | * @remark Read the file COPYING | 5 | * @remark Read the file COPYING |
6 | * | 6 | * |
7 | * @author John Levon <levon@movementarian.org> | 7 | * @author John Levon <levon@movementarian.org> |
8 | * @author Barry Kasindorf | 8 | * @author Barry Kasindorf |
9 | * @author Robert Richter <robert.richter@amd.com> | ||
9 | * | 10 | * |
10 | * This is the core of the buffer management. Each | 11 | * This is the core of the buffer management. Each |
11 | * CPU buffer is processed and entered into the | 12 | * CPU buffer is processed and entered into the |
@@ -529,6 +530,7 @@ void sync_buffer(int cpu) | |||
529 | sync_buffer_state state = sb_buffer_start; | 530 | sync_buffer_state state = sb_buffer_start; |
530 | unsigned int i; | 531 | unsigned int i; |
531 | unsigned long available; | 532 | unsigned long available; |
533 | unsigned long flags; | ||
532 | struct op_entry entry; | 534 | struct op_entry entry; |
533 | struct op_sample *sample; | 535 | struct op_sample *sample; |
534 | 536 | ||
@@ -545,38 +547,34 @@ void sync_buffer(int cpu) | |||
545 | break; | 547 | break; |
546 | 548 | ||
547 | if (is_code(sample->eip)) { | 549 | if (is_code(sample->eip)) { |
548 | switch (sample->event) { | 550 | flags = sample->event; |
549 | case 0: | 551 | if (flags & TRACE_BEGIN) { |
550 | case CPU_IS_KERNEL: | 552 | state = sb_bt_start; |
553 | add_trace_begin(); | ||
554 | } | ||
555 | if (flags & KERNEL_CTX_SWITCH) { | ||
551 | /* kernel/userspace switch */ | 556 | /* kernel/userspace switch */ |
552 | in_kernel = sample->event; | 557 | in_kernel = flags & IS_KERNEL; |
553 | if (state == sb_buffer_start) | 558 | if (state == sb_buffer_start) |
554 | state = sb_sample_start; | 559 | state = sb_sample_start; |
555 | add_kernel_ctx_switch(sample->event); | 560 | add_kernel_ctx_switch(flags & IS_KERNEL); |
556 | break; | 561 | } |
557 | case CPU_TRACE_BEGIN: | 562 | if (flags & USER_CTX_SWITCH) { |
558 | state = sb_bt_start; | ||
559 | add_trace_begin(); | ||
560 | break; | ||
561 | #ifdef CONFIG_OPROFILE_IBS | ||
562 | case IBS_FETCH_BEGIN: | ||
563 | add_ibs_begin(cpu, IBS_FETCH_CODE, mm); | ||
564 | break; | ||
565 | case IBS_OP_BEGIN: | ||
566 | add_ibs_begin(cpu, IBS_OP_CODE, mm); | ||
567 | break; | ||
568 | #endif | ||
569 | default: | ||
570 | /* userspace context switch */ | 563 | /* userspace context switch */ |
571 | oldmm = mm; | 564 | oldmm = mm; |
572 | new = (struct task_struct *)sample->event; | 565 | new = (struct task_struct *)sample->data[0]; |
573 | release_mm(oldmm); | 566 | release_mm(oldmm); |
574 | mm = take_tasks_mm(new); | 567 | mm = take_tasks_mm(new); |
575 | if (mm != oldmm) | 568 | if (mm != oldmm) |
576 | cookie = get_exec_dcookie(mm); | 569 | cookie = get_exec_dcookie(mm); |
577 | add_user_ctx_switch(new, cookie); | 570 | add_user_ctx_switch(new, cookie); |
578 | break; | ||
579 | } | 571 | } |
572 | #ifdef CONFIG_OPROFILE_IBS | ||
573 | if (flags & IBS_FETCH_BEGIN) | ||
574 | add_ibs_begin(cpu, IBS_FETCH_CODE, mm); | ||
575 | if (flags & IBS_OP_BEGIN) | ||
576 | add_ibs_begin(cpu, IBS_OP_CODE, mm); | ||
577 | #endif | ||
580 | continue; | 578 | continue; |
581 | } | 579 | } |
582 | 580 | ||
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) |
diff --git a/drivers/oprofile/cpu_buffer.h b/drivers/oprofile/cpu_buffer.h index d7c0545ef8b2..e634dcf2f26f 100644 --- a/drivers/oprofile/cpu_buffer.h +++ b/drivers/oprofile/cpu_buffer.h | |||
@@ -78,10 +78,12 @@ int op_cpu_buffer_write_commit(struct op_entry *entry); | |||
78 | struct op_sample *op_cpu_buffer_read_entry(struct op_entry *entry, int cpu); | 78 | struct op_sample *op_cpu_buffer_read_entry(struct op_entry *entry, int cpu); |
79 | unsigned long op_cpu_buffer_entries(int cpu); | 79 | unsigned long op_cpu_buffer_entries(int cpu); |
80 | 80 | ||
81 | /* transient events for the CPU buffer -> event buffer */ | 81 | /* extra data flags */ |
82 | #define CPU_IS_KERNEL 1 | 82 | #define KERNEL_CTX_SWITCH (1UL << 0) |
83 | #define CPU_TRACE_BEGIN 2 | 83 | #define IS_KERNEL (1UL << 1) |
84 | #define IBS_FETCH_BEGIN 3 | 84 | #define TRACE_BEGIN (1UL << 2) |
85 | #define IBS_OP_BEGIN 4 | 85 | #define USER_CTX_SWITCH (1UL << 3) |
86 | #define IBS_FETCH_BEGIN (1UL << 4) | ||
87 | #define IBS_OP_BEGIN (1UL << 5) | ||
86 | 88 | ||
87 | #endif /* OPROFILE_CPU_BUFFER_H */ | 89 | #endif /* OPROFILE_CPU_BUFFER_H */ |