/** * @file daemon/opd_trans.c * Processing the sample buffer * * @remark Copyright 2002 OProfile authors * @remark Read the file COPYING * * @author John Levon * @author Philippe Elie * Modified by Aravind Menon for Xen * These modifications are: * Copyright (C) 2005 Hewlett-Packard Co. * * Modified by Maynard Johnson <maynardj@us.ibm.com> * These modifications are: * (C) Copyright IBM Corporation 2007 */ #include "opd_trans.h" #include "opd_kernel.h" #include "opd_sfile.h" #include "opd_anon.h" #include "opd_stats.h" #include "opd_printf.h" #include "opd_interface.h" #include <limits.h> #include <string.h> #include <stdlib.h> #include <stdint.h> #include <stdio.h> #include <errno.h> extern size_t kernel_pointer_size; void clear_trans_last(struct transient * trans) { trans->last = NULL; trans->last_anon = NULL; } void clear_trans_current(struct transient * trans) { trans->current = NULL; trans->anon = NULL; } uint64_t pop_buffer_value(struct transient * trans) { uint64_t val; if (!trans->remaining) { fprintf(stderr, "BUG: popping empty buffer !\n"); abort(); } if (kernel_pointer_size == 4) { uint32_t const * lbuf = (void const *)trans->buffer; val = *lbuf; } else { uint64_t const * lbuf = (void const *)trans->buffer; val = *lbuf; } trans->remaining--; trans->buffer += kernel_pointer_size; return val; } int enough_remaining(struct transient * trans, size_t size) { if (trans->remaining >= size) return 1; verbprintf(vmisc, "Dangling ESCAPE_CODE.\n"); opd_stats[OPD_DANGLING_CODE]++; return 0; } static void opd_put_sample(struct transient * trans, unsigned long long pc) { unsigned long long event; if (!enough_remaining(trans, 1)) { trans->remaining = 0; return; } event = pop_buffer_value(trans); if (trans->tracing != TRACING_ON) trans->event = event; trans->pc = pc; /* sfile can change at each sample for kernel */ if (trans->in_kernel != 0) clear_trans_current(trans); if (!trans->in_kernel && trans->cookie == NO_COOKIE) trans->anon = find_anon_mapping(trans); /* get the current sfile if needed */ if (!trans->current) trans->current = sfile_find(trans); /* * can happen if kernel sample falls through the cracks, or if * it's a sample from an anon region we couldn't find */ if (!trans->current) goto out; /* FIXME: this logic is perhaps too harsh? */ if (trans->current->ignored || (trans->last && trans->last->ignored)) goto out; /* log the sample or arc */ sfile_log_sample(trans); out: /* switch to trace mode */ if (trans->tracing == TRACING_START) trans->tracing = TRACING_ON; update_trans_last(trans); } static void code_unknown(struct transient * trans __attribute__((unused))) { fprintf(stderr, "Unknown code !\n"); abort(); } static void code_ctx_switch(struct transient * trans) { clear_trans_current(trans); if (!enough_remaining(trans, 5)) { trans->remaining = 0; return; } trans->tid = pop_buffer_value(trans); trans->app_cookie = pop_buffer_value(trans); /* must be ESCAPE_CODE, CTX_TGID_CODE, tgid. Like this * because tgid was added later in a compatible manner. */ pop_buffer_value(trans); pop_buffer_value(trans); trans->tgid = pop_buffer_value(trans); if (vmisc) { char const * app = find_cookie(trans->app_cookie); printf("CTX_SWITCH to tid %lu, tgid %lu, cookie %llx(%s)\n", (unsigned long)trans->tid, (unsigned long)trans->tgid, trans->app_cookie, app ? app : "none"); } } static void code_cpu_switch(struct transient * trans) { clear_trans_current(trans); if (!enough_remaining(trans, 1)) { trans->remaining = 0; return; } trans->cpu = pop_buffer_value(trans); verbprintf(vmisc, "CPU_SWITCH to %lu\n", trans->cpu); } static void code_cookie_switch(struct transient * trans) { clear_trans_current(trans); if (!enough_remaining(trans, 1)) { trans->remaining = 0; return; } trans->cookie = pop_buffer_value(trans); if (vmisc) { char const * name = verbose_cookie(trans->cookie); verbprintf(vmisc, "COOKIE_SWITCH to cookie %s(%llx)\n", name, trans->cookie); } } static void code_kernel_enter(struct transient * trans) { verbprintf(vmisc, "KERNEL_ENTER_SWITCH to kernel\n"); trans->in_kernel = 1; clear_trans_current(trans); /* subtlety: we must keep trans->cookie cached, * even though it's meaningless for the kernel - * we won't necessarily get a cookie switch on * kernel exit. See comments in opd_sfile.c */ } static void code_user_enter(struct transient * trans) { verbprintf(vmisc, "USER_ENTER_SWITCH to user-space\n"); trans->in_kernel = 0; clear_trans_current(trans); clear_trans_last(trans); } static void code_module_loaded(struct transient * trans __attribute__((unused))) { verbprintf(vmodule, "MODULE_LOADED_CODE\n"); opd_reread_module_info(); clear_trans_current(trans); clear_trans_last(trans); } /* * This also implicitly signals the end of the previous * trace, so we never explicitly set TRACING_OFF when * processing a buffer. */ static void code_trace_begin(struct transient * trans) { verbprintf(varcs, "TRACE_BEGIN\n"); trans->tracing = TRACING_START; } static void code_xen_enter(struct transient * trans) { verbprintf(vmisc, "XEN_ENTER_SWITCH to xen\n"); trans->in_kernel = 1; trans->current = NULL; /* subtlety: we must keep trans->cookie cached, even though it's * meaningless for Xen - we won't necessarily get a cookie switch * on Xen exit. See comments in opd_sfile.c. It seems that we can * get away with in_kernel = 1 as long as we supply the correct * Xen image, and its address range in startup find_kernel_image * is modified to look in the Xen image also */ } extern void code_spu_profiling(struct transient * trans); extern void code_spu_ctx_switch(struct transient * trans); extern void code_ibs_fetch_sample(struct transient * trans); extern void code_ibs_op_sample(struct transient * trans); handler_t handlers[LAST_CODE + 1] = { &code_unknown, &code_ctx_switch, &code_cpu_switch, &code_cookie_switch, &code_kernel_enter, &code_user_enter, &code_module_loaded, /* tgid handled differently */ &code_unknown, &code_trace_begin, &code_unknown, &code_xen_enter, #if defined(__powerpc__) &code_spu_profiling, &code_spu_ctx_switch, #else &code_unknown, &code_unknown, #endif &code_ibs_fetch_sample, &code_ibs_op_sample, }; extern void (*special_processor)(struct transient *); void opd_process_samples(char const * buffer, size_t count) { struct transient trans = { .buffer = buffer, .remaining = count, .tracing = TRACING_OFF, .current = NULL, .last = NULL, .cookie = INVALID_COOKIE, .app_cookie = INVALID_COOKIE, .anon = NULL, .last_anon = NULL, .pc = 0, .last_pc = 0, .event = 0, .in_kernel = -1, .cpu = -1, .tid = -1, .embedded_offset = UNUSED_EMBEDDED_OFFSET, .tgid = -1, .ext = NULL }; /* FIXME: was uint64_t but it can't compile on alpha where uint64_t * is an unsigned long and below the printf("..." %llu\n", code) * generate a warning, this look like a stopper to use c98 types :/ */ unsigned long long code; if (special_processor) { special_processor(&trans); return; } while (trans.remaining) { code = pop_buffer_value(&trans); if (!is_escape_code(code)) { opd_put_sample(&trans, code); continue; } if (!trans.remaining) { verbprintf(vmisc, "Dangling ESCAPE_CODE.\n"); opd_stats[OPD_DANGLING_CODE]++; break; } // started with ESCAPE_CODE, next is type code = pop_buffer_value(&trans); if (code >= LAST_CODE) { fprintf(stderr, "Unknown code %llu\n", code); abort(); } handlers[code](&trans); } }