opd_trans.c revision cc2ee177dbb3befca43e36cfc56778b006c3d050
1/** 2 * @file daemon/opd_trans.c 3 * Processing the sample buffer 4 * 5 * @remark Copyright 2002 OProfile authors 6 * @remark Read the file COPYING 7 * 8 * @author John Levon 9 * @author Philippe Elie 10 */ 11 12#include "opd_trans.h" 13#include "opd_kernel.h" 14#include "opd_sfile.h" 15#include "opd_anon.h" 16#include "opd_stats.h" 17#include "opd_printf.h" 18#include "opd_interface.h" 19 20#include <limits.h> 21#include <string.h> 22#include <stdlib.h> 23#include <stdint.h> 24#include <stdio.h> 25#include <errno.h> 26 27extern size_t kernel_pointer_size; 28 29 30void clear_trans_last(struct transient * trans) 31{ 32 trans->last = NULL; 33 trans->last_anon = NULL; 34} 35 36 37void clear_trans_current(struct transient * trans) 38{ 39 trans->current = NULL; 40 trans->anon = NULL; 41} 42 43 44void update_trans_last(struct transient * trans) 45{ 46 trans->last = trans->current; 47 trans->last_anon = trans->anon; 48 trans->last_pc = trans->pc; 49} 50 51 52static inline int is_escape_code(uint64_t code) 53{ 54 return kernel_pointer_size == 4 ? code == ~0LU : code == ~0LLU; 55} 56 57 58static uint64_t pop_buffer_value(struct transient * trans) 59{ 60 uint64_t val; 61 62 if (!trans->remaining) { 63 fprintf(stderr, "BUG: popping empty buffer !\n"); 64 abort(); 65 } 66 67 if (kernel_pointer_size == 4) { 68 uint32_t const * lbuf = (void const *)trans->buffer; 69 val = *lbuf; 70 } else { 71 uint64_t const * lbuf = (void const *)trans->buffer; 72 val = *lbuf; 73 } 74 75 trans->remaining--; 76 trans->buffer += kernel_pointer_size; 77 return val; 78} 79 80 81static int enough_remaining(struct transient * trans, size_t size) 82{ 83 if (trans->remaining >= size) 84 return 1; 85 86 verbprintf(vmisc, "Dangling ESCAPE_CODE.\n"); 87 opd_stats[OPD_DANGLING_CODE]++; 88 return 0; 89} 90 91 92static void opd_put_sample(struct transient * trans, unsigned long long pc) 93{ 94 unsigned long long event; 95 96 if (!enough_remaining(trans, 1)) { 97 trans->remaining = 0; 98 return; 99 } 100 101 event = pop_buffer_value(trans); 102 103 if (trans->tracing != TRACING_ON) 104 trans->event = event; 105 106 trans->pc = pc; 107 108 /* sfile can change at each sample for kernel */ 109 if (trans->in_kernel != 0) 110 clear_trans_current(trans); 111 112 if (!trans->in_kernel && trans->cookie == NO_COOKIE) 113 trans->anon = find_anon_mapping(trans); 114 115 /* get the current sfile if needed */ 116 if (!trans->current) 117 trans->current = sfile_find(trans); 118 119 /* 120 * can happen if kernel sample falls through the cracks, or if 121 * it's a sample from an anon region we couldn't find 122 */ 123 if (!trans->current) 124 goto out; 125 126 /* FIXME: this logic is perhaps too harsh? */ 127 if (trans->current->ignored || (trans->last && trans->last->ignored)) 128 goto out; 129 130 /* log the sample or arc */ 131 sfile_log_sample(trans); 132 133out: 134 /* switch to trace mode */ 135 if (trans->tracing == TRACING_START) 136 trans->tracing = TRACING_ON; 137 138 update_trans_last(trans); 139} 140 141 142static void code_unknown(struct transient * trans __attribute__((unused))) 143{ 144 fprintf(stderr, "Unknown code !\n"); 145 abort(); 146} 147 148 149static void code_ctx_switch(struct transient * trans) 150{ 151 clear_trans_current(trans); 152 153 if (!enough_remaining(trans, 5)) { 154 trans->remaining = 0; 155 return; 156 } 157 158 trans->tid = pop_buffer_value(trans); 159 trans->app_cookie = pop_buffer_value(trans); 160 /* must be ESCAPE_CODE, CTX_TGID_CODE, tgid. Like this 161 * because tgid was added later in a compatible manner. 162 */ 163 pop_buffer_value(trans); 164 pop_buffer_value(trans); 165 trans->tgid = pop_buffer_value(trans); 166 167 if (vmisc) { 168 char const * app = find_cookie(trans->app_cookie); 169 printf("CTX_SWITCH to tid %lu, tgid %lu, cookie %llx(%s)\n", 170 (unsigned long)trans->tid, (unsigned long)trans->tgid, 171 trans->app_cookie, app ? app : "none"); 172 } 173} 174 175 176static void code_cpu_switch(struct transient * trans) 177{ 178 clear_trans_current(trans); 179 180 if (!enough_remaining(trans, 1)) { 181 trans->remaining = 0; 182 return; 183 } 184 185 trans->cpu = pop_buffer_value(trans); 186 verbprintf(vmisc, "CPU_SWITCH to %lu\n", trans->cpu); 187} 188 189 190static void code_cookie_switch(struct transient * trans) 191{ 192 clear_trans_current(trans); 193 194 if (!enough_remaining(trans, 1)) { 195 trans->remaining = 0; 196 return; 197 } 198 199 trans->cookie = pop_buffer_value(trans); 200 201 if (vmisc) { 202 char const * name = verbose_cookie(trans->cookie); 203 verbprintf(vmisc, "COOKIE_SWITCH to cookie %s(%llx)\n", 204 name, trans->cookie); 205 } 206} 207 208 209static void code_kernel_enter(struct transient * trans) 210{ 211 verbprintf(vmisc, "KERNEL_ENTER_SWITCH to kernel\n"); 212 trans->in_kernel = 1; 213 clear_trans_current(trans); 214 /* subtlety: we must keep trans->cookie cached, 215 * even though it's meaningless for the kernel - 216 * we won't necessarily get a cookie switch on 217 * kernel exit. See comments in opd_sfile.c 218 */ 219} 220 221 222static void code_kernel_exit(struct transient * trans) 223{ 224 verbprintf(vmisc, "KERNEL_EXIT_SWITCH to user-space\n"); 225 trans->in_kernel = 0; 226 clear_trans_current(trans); 227 clear_trans_last(trans); 228} 229 230 231static void code_module_loaded(struct transient * trans __attribute__((unused))) 232{ 233 verbprintf(vmodule, "MODULE_LOADED_CODE\n"); 234 opd_reread_module_info(); 235 clear_trans_current(trans); 236 clear_trans_last(trans); 237} 238 239 240/* 241 * This also implicitly signals the end of the previous 242 * trace, so we never explicitly set TRACING_OFF when 243 * processing a buffer. 244 */ 245static void code_trace_begin(struct transient * trans) 246{ 247 verbprintf(varcs, "TRACE_BEGIN\n"); 248 trans->tracing = TRACING_START; 249} 250 251 252typedef void (*handler_t)(struct transient *); 253 254static handler_t handlers[LAST_CODE + 1] = { 255 &code_unknown, 256 &code_ctx_switch, 257 &code_cpu_switch, 258 &code_cookie_switch, 259 &code_kernel_enter, 260 &code_kernel_exit, 261 &code_module_loaded, 262 /* tgid handled differently */ 263 &code_unknown, 264 &code_trace_begin, 265}; 266 267 268void opd_process_samples(char const * buffer, size_t count) 269{ 270 struct transient trans = { 271 .buffer = buffer, 272 .remaining = count, 273 .tracing = TRACING_OFF, 274 .current = NULL, 275 .last = NULL, 276 .cookie = INVALID_COOKIE, 277 .app_cookie = INVALID_COOKIE, 278 .anon = NULL, 279 .last_anon = NULL, 280 .pc = 0, 281 .last_pc = 0, 282 .event = 0, 283 .in_kernel = -1, 284 .cpu = -1, 285 .tid = -1, 286 .tgid = -1 287 }; 288 289 /* FIXME: was uint64_t but it can't compile on alpha where uint64_t 290 * is an unsigned long and below the printf("..." %llu\n", code) 291 * generate a warning, this look like a stopper to use c98 types :/ 292 */ 293 unsigned long long code; 294 295 while (trans.remaining) { 296 code = pop_buffer_value(&trans); 297 298 if (!is_escape_code(code)) { 299 opd_put_sample(&trans, code); 300 continue; 301 } 302 303 if (!trans.remaining) { 304 verbprintf(vmisc, "Dangling ESCAPE_CODE.\n"); 305 opd_stats[OPD_DANGLING_CODE]++; 306 break; 307 } 308 309 // started with ESCAPE_CODE, next is type 310 code = pop_buffer_value(&trans); 311 312 if (code >= LAST_CODE) { 313 fprintf(stderr, "Unknown code %llu\n", code); 314 abort(); 315 } 316 317 handlers[code](&trans); 318 } 319} 320