diff options
| -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 423a95438cb..f101724db80 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 908202afbae..d969bb13a25 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 400f7fcffdb..e859d23cfc5 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 d7c0545ef8b..e634dcf2f26 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 */ |
