aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/oprofile/cpu_buffer.c
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/cpu_buffer.c
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/cpu_buffer.c')
-rw-r--r--drivers/oprofile/cpu_buffer.c139
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
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)