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