File: dt_prov_sdt.c

package info (click to toggle)
dtrace 2.0.5-1
  • links: PTS
  • area: main
  • in suites: sid
  • size: 24,408 kB
  • sloc: ansic: 61,247; sh: 17,997; asm: 1,717; lex: 947; awk: 754; yacc: 695; perl: 37; sed: 17; makefile: 15
file content (305 lines) | stat: -rw-r--r-- 7,496 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
/*
 * Oracle Linux DTrace.
 * Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * http://oss.oracle.com/licenses/upl.
 *
 * The Statically Defined Tracepoint (SDT) provider for DTrace.
 *
 * SDT probes are exposed by the kernel as tracepoint events.  They are listed
 * in the TRACEFS/available_events file.
 *
 * Mapping from event name to DTrace probe name:
 *
 *	<group>:<name>				sdt:<group>::<name>
 */
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/bpf.h>
#include <linux/perf_event.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <bpf_asm.h>

#include "dt_dctx.h"
#include "dt_cg.h"
#include "dt_provider_tp.h"
#include "dt_probe.h"
#include "dt_pt_regs.h"

static const char		prvname[] = "sdt";
static const char		modname[] = "vmlinux";

#define PROBE_LIST		TRACEFS "available_events"

#define KPROBES			"kprobes"
#define SYSCALLS		"syscalls"
#define UPROBES			"uprobes"
#define PID			"dt_pid"

static const dtrace_pattr_t	pattr = {
{ DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
{ DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
};

/*
 * The PROBE_LIST file lists all tracepoints in a <group>:<name> format.
 * We need to ignore these groups:
 *   - PROBE_SFMT
 *   - kprobes and uprobes
 *   - syscalls (handled by a different provider)
 *   - pid and usdt probes (ditto)
 */
static int populate(dtrace_hdl_t *dtp)
{
	dt_provider_t	*prv;
	FILE		*f;
	char		*buf = NULL;
	char		*p;
	size_t		n;

	prv = dt_provider_create(dtp, prvname, &dt_sdt, &pattr, NULL);
	if (prv == NULL)
		return -1;			/* errno already set */

	f = fopen(PROBE_LIST, "r");
	if (f == NULL)
		return 0;

	while (getline(&buf, &n, f) >= 0) {
		p = strchr(buf, '\n');
		if (p)
			*p = '\0';

		p = strchr(buf, ':');
		if (p != NULL) {
			int	dummy;
			char	*str;

			*p++ = '\0';

			if (sscanf(buf, PROBE_SFMT, &dummy, &str) == 2) {
				free(str);
				continue;
			}
			else if (strcmp(buf, KPROBES) == 0)
				continue;
			else if (strcmp(buf, SYSCALLS) == 0)
				continue;
			else if (strcmp(buf, UPROBES) == 0)
				continue;
			else if (strcmp(buf, PID) == 0)
				continue;

			if (dt_tp_probe_insert(dtp, prv, prvname, buf, "", p))
				n++;
		} else {
			if (dt_tp_probe_insert(dtp, prv, prvname, modname, "",
					    buf))
				n++;
		}
	}

	free(buf);
	fclose(f);

	return n;
}

/*
 * Generate a BPF trampoline for a SDT probe.
 *
 * The trampoline function is called when a SDT probe triggers, and it must
 * satisfy the following prototype:
 *
 *	int dt_sdt(void *data)
 *
 * The trampoline will populate a dt_dctx_t struct and then call the function
 * that implements the compiled D clause.  It returns the value that it gets
 * back from that function.
 */
static int trampoline(dt_pcb_t *pcb, uint_t exitlbl)
{
	int		i;
	dt_irlist_t	*dlp = &pcb->pcb_ir;
	dt_probe_t	*prp = pcb->pcb_probe;
	ssize_t		off;

	dt_cg_tramp_prologue(pcb);

	/*
	 * After the dt_cg_tramp_prologue() call, we have:
	 *				//     (%r7 = dctx->mst)
	 *				//     (%r8 = dctx->ctx)
	 */

	dt_cg_tramp_clear_regs(pcb);

	/*
	 * Decode the arguments of the underlying tracepoint (from the context)
	 * and store them in the cached argument list in the mstate.
	 *
	 * Skip the first argument (a private pointer that we are not allowed
	 * to access from BPF).
	 */
	off = sizeof(uint64_t);
	for (i = 0; i < prp->argc; i++) {
		dt_node_t	*anp = prp->xargv[i];
		ssize_t		size = ctf_type_size(anp->dn_ctfp,
						     anp->dn_type);
		ssize_t		align = ctf_type_align(anp->dn_ctfp,
						       anp->dn_type);

		off = P2ROUNDUP(off, align);
		if (dt_node_is_scalar(anp)) {
			uint_t	ldsz = dt_cg_ldsize(anp, anp->dn_ctfp,
						    anp->dn_type, NULL);

			emit(dlp, BPF_LOAD(ldsz, BPF_REG_0, BPF_REG_8, off));
			emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(i), BPF_REG_0));
		} else {
			emit(dlp, BPF_MOV_REG(BPF_REG_0, BPF_REG_8));
			emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, off));
			emit(dlp, BPF_STORE(BPF_DW, BPF_REG_7, DMST_ARG(i), BPF_REG_0));
		}
		off += size;
	}

	/* Clear the remainder of the cached arguments.  */
	dt_cg_tramp_clear_argv(pcb, i);

	dt_cg_tramp_epilogue(pcb);

	return 0;
}

/*
 * If there is no trace_event_raw_* struct available in CTF, we can still get
 * argument count and type information from the tracefs data.
 */
static int probe_info_tracefs(dtrace_hdl_t *dtp, const dt_probe_t *prp,
			      int *argcp, dt_argdesc_t **argvp)
{
	FILE				*f;
	char				*fn;
	int				rc;
	const dtrace_probedesc_t	*pdp = prp->desc;

	if (asprintf(&fn, EVENTSFS "%s/%s/format", pdp->mod, pdp->prb) == -1)
		return dt_set_errno(dtp, EDT_NOMEM);

	f = fopen(fn, "r");
	free(fn);
	if (!f)
		return -ENOENT;

	rc = dt_tp_probe_info(dtp, f, NULL, prp, argcp, argvp);
	fclose(f);

	return rc;
}

static int probe_info(dtrace_hdl_t *dtp, const dt_probe_t *prp,
		      int *argcp, dt_argdesc_t **argvp)
{
#ifdef HAVE_LIBCTF
	int			rc, i;
	char			*str;
	ctf_dict_t		*ctfp;
	ctf_id_t		type;
	ctf_next_t		*it = NULL;
	int			argc = 0;
	dt_argdesc_t		*argv = NULL;
	dtrace_typeinfo_t	sym;
	FILE			*f;
	uint32_t		id;

	/* Retrieve the event id. */
	if (asprintf(&str, EVENTSFS "%s/%s/id", prp->desc->mod, prp->desc->prb) == -1)
		return dt_set_errno(dtp, EDT_NOMEM);

	f = fopen(str, "r");
	free(str);
	if (!f)
		return dt_set_errno(dtp, EDT_ENABLING_ERR);

	rc = fscanf(f, "%u", &id);
	fclose(f);

	if (rc < 0 || id == 0)
		return dt_set_errno(dtp, EDT_ENABLING_ERR);

	dt_tp_probe_set_id(prp, id);

	if (asprintf(&str, "struct trace_event_raw_%s", prp->desc->prb) == -1)
		return dt_set_errno(dtp, EDT_NOMEM);
	rc = dtrace_lookup_by_type(dtp, DTRACE_OBJ_EVERY, str, &sym);
	free(str);
	if (rc ||
	    ctf_type_kind(sym.dtt_ctfp, sym.dtt_type) != CTF_K_STRUCT)
		goto use_alt;

	/*
	 * Tracepoints have an extra member at the beginning and end of the
	 * struct.  We need to skip those.  (We also handle the case where one
	 * or both of those members are missing even though that is not
	 * supposed to happen.)
	 */
	ctfp = sym.dtt_ctfp;
	type = sym.dtt_type;
	rc = ctf_member_count(ctfp, type);
	if (rc <= 2)
		goto done;

	rc--;
	argc = rc - 1;
	argv = dt_zalloc(dtp, argc * sizeof(dt_argdesc_t));
	if (argv == NULL)
		return dt_set_errno(dtp, EDT_NOMEM);
	/* Skip first member. */
	ctf_member_next(ctfp, type, &it, NULL, NULL, 0);
	for (i = 0; i < argc; i++) {
		ctf_id_t	mtyp;
		char		n[DT_TYPE_NAMELEN];

		if (ctf_member_next(ctfp, type, &it, NULL, &mtyp, 0) == CTF_ERR) {
			ctf_next_destroy(it);
			return dt_set_errno(dtp, EDT_CTF);
		}
		ctf_type_name(ctfp, mtyp, n, sizeof(n));
		argv[i].mapping = i;
		argv[i].native = strdup(n);
		argv[i].xlate = NULL;
	}
	ctf_next_destroy(it);
done:
	*argcp = argc;
	*argvp = argv;

	return 0;

use_alt:
#endif
	return probe_info_tracefs(dtp, prp, argcp, argvp);
}

dt_provimpl_t	dt_sdt = {
	.name		= prvname,
	.prog_type	= BPF_PROG_TYPE_TRACEPOINT,
	.populate	= &populate,
	.load_prog	= &dt_bpf_prog_load,
	.trampoline	= &trampoline,
	.attach		= &dt_tp_probe_attach,
	.probe_info	= &probe_info,
	.detach		= &dt_tp_probe_detach,
	.probe_destroy	= &dt_tp_probe_destroy,
};