/**
* @file daemon/opd_spu.c
* Processing the sample buffer for Cell BE SPU profile
*
* @remark Copyright 2007 OProfile authors
* @remark Read the file COPYING
*
* @author Maynard Johnson
* (C) Copyright IBM Corporation 2007
*/
#include "opd_interface.h"
#include "opd_printf.h"
#include "opd_sfile.h"
#include "opd_stats.h"
#include "opd_trans.h"
#include "op_libiberty.h"
#include <stdlib.h>
#include <stdio.h>
struct spu_context_info {
pid_t tid;
pid_t tgid;
cookie_t app_cookie;
uint64_t embedded_offset;
cookie_t spu_cookie;
};
static struct spu_context_info * spu_context_cache;
/* Forward declaration */
static void process_spu_samples(struct transient * trans);
void (*special_processor)(struct transient *);
/*
* This function is called when the first value found in the
* buffer (after the beginning ESCAPE_CODE) is SPU_PROFILING_CODE.
* Once we get here, the rest of the processing of the buffer is
* Cell-SPU-specific, so we do not need to return until the
* trans.buffer is empty.
*/
void code_spu_profiling(struct transient * trans)
{
/* Next value in buffer is the number of SPUs. */
unsigned long long num_spus = pop_buffer_value(trans);
/* Free the cache from previous run */
free(spu_context_cache);
spu_context_cache = xmalloc(sizeof(struct spu_context_info) * num_spus);
special_processor = process_spu_samples;
process_spu_samples(trans);
}
void code_spu_ctx_switch(struct transient * trans)
{
clear_trans_current(trans);
if (!enough_remaining(trans, 6)) {
trans->remaining = 0;
return;
}
/* First value in the buffer for an SPU context switch is
* the SPU number. For SPU profiling, 'cpu' = 'spu'.
*/
trans->cpu = pop_buffer_value(trans);
trans->tid = pop_buffer_value(trans);
trans->tgid = pop_buffer_value(trans);
trans->app_cookie = pop_buffer_value(trans);
if (vmisc) {
char const * app = find_cookie(trans->app_cookie);
printf("SPU_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");
}
/* The trans->cookie will point to the binary file where the SPU ELF
* can be found. If the SPU ELF is embedded, it may be embedded in
* either the executable application binary or a shared lib. If shared
* library, then trans->cookie will differ from the previously obtained
* trans->app_cookie. For the non-embedded case, trans->cookie always
* points to a separate binary file.
*/
trans->cookie = pop_buffer_value(trans);
trans->embedded_offset = pop_buffer_value(trans);
}
static void cache_spu_context_info(struct transient * trans)
{
int i = trans->cpu;
spu_context_cache[i].tid = trans->tid;
spu_context_cache[i].tgid = trans->tgid;
spu_context_cache[i].app_cookie = trans->app_cookie;
spu_context_cache[i].embedded_offset = trans->embedded_offset;
spu_context_cache[i].spu_cookie = trans->cookie;
}
static void update_trans_for_spu(struct transient * trans)
{
int i = trans->cpu;
trans->tid = spu_context_cache[i].tid;
trans->tgid = spu_context_cache[i].tgid;
trans->app_cookie = spu_context_cache[i].app_cookie;
trans->embedded_offset = spu_context_cache[i].embedded_offset;
trans->cookie = spu_context_cache[i].spu_cookie;
}
#define SPU_NUM_MASK 0xFFFFFFFF00000000ULL
#define SPU_CYCLES_COUNTER 0
static void opd_put_spu_sample
(struct transient * trans, unsigned long long pc)
{
unsigned long spu_number = (pc & SPU_NUM_MASK) >> 32;
if (trans->cpu != spu_number) {
trans->cpu = spu_number;
clear_trans_current(trans);
update_trans_for_spu(trans);
}
/* get the current sfile if needed */
if (!trans->current)
trans->current = sfile_find(trans);
if (trans->tracing != TRACING_ON)
trans->event = SPU_CYCLES_COUNTER;
trans->pc = (pc & ~SPU_NUM_MASK);
/* log the sample or arc */
sfile_log_sample(trans);
/* switch to trace mode */
if (trans->tracing == TRACING_START)
trans->tracing = TRACING_ON;
update_trans_last(trans);
}
/*
* This function processes SPU context switches and
* SPU program counter samples. After processing a
* context switch (via handlers[code)), we cache the
* SPU context information that has been temporarily
* stored in trans.
*/
static void process_spu_samples(struct transient * trans)
{
unsigned long long code;
trans->in_kernel = 0;
while (trans->remaining) {
code = pop_buffer_value(trans);
if (!is_escape_code(code)) {
opd_put_spu_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);
cache_spu_context_info(trans);
}
}