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