aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/oprofile
diff options
context:
space:
mode:
authorRobert Richter <robert.richter@amd.com>2008-12-25 11:26:07 -0500
committerRobert Richter <robert.richter@amd.com>2009-01-07 16:40:47 -0500
commitae735e9964b4584923f2997d98a8d80ae9c1a75c (patch)
tree0fc72d18bcc5951f9dd519e8a4527593724b816f /drivers/oprofile
parent2d87b14cf8d0b07720de26d90789d02124141616 (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>
Diffstat (limited to 'drivers/oprofile')
-rw-r--r--drivers/oprofile/buffer_sync.c42
-rw-r--r--drivers/oprofile/cpu_buffer.c139
-rw-r--r--drivers/oprofile/cpu_buffer.h12
3 files changed, 102 insertions, 91 deletions
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
215static int
216op_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
215static inline int 268static inline int
216op_add_sample(struct oprofile_cpu_buffer *cpu_buf, 269op_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
232static inline int 285/*
233add_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 */
247static int log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc, 293static int
248 int is_kernel, unsigned long event) 294log_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
287static inline void oprofile_begin_trace(struct oprofile_cpu_buffer *cpu_buf) 317static 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
378fail:
379 cpu_buf->sample_lost_overflow++;
380} 391}
381 392
382#endif 393#endif
@@ -384,7 +395,7 @@ fail:
384void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event) 395void 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
390void oprofile_add_trace(unsigned long pc) 401void 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);
78struct op_sample *op_cpu_buffer_read_entry(struct op_entry *entry, int cpu); 78struct op_sample *op_cpu_buffer_read_entry(struct op_entry *entry, int cpu);
79unsigned long op_cpu_buffer_entries(int cpu); 79unsigned 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 */