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