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
34extern size_t kernel_pointer_size;
35
36
37void clear_trans_last(struct transient * trans)
38{
39	trans->last = NULL;
40	trans->last_anon = NULL;
41}
42
43
44void clear_trans_current(struct transient * trans)
45{
46	trans->current = NULL;
47	trans->anon = NULL;
48}
49
50
51uint64_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
74int 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
85static 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
126out:
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
135static void code_unknown(struct transient * trans __attribute__((unused)))
136{
137	fprintf(stderr, "Unknown code !\n");
138	abort();
139}
140
141
142static 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
169static 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
183static 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
202static 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
215static 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
224static 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 */
238static void code_trace_begin(struct transient * trans)
239{
240	verbprintf(varcs, "TRACE_BEGIN\n");
241	trans->tracing = TRACING_START;
242}
243
244static 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
258extern void code_spu_profiling(struct transient * trans);
259extern void code_spu_ctx_switch(struct transient * trans);
260
261extern void code_ibs_fetch_sample(struct transient * trans);
262extern void code_ibs_op_sample(struct transient * trans);
263
264handler_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
288extern void (*special_processor)(struct transient *);
289
290void 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