/**
* @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);
}
}