Skip to content

Commit f80e48c

Browse files
kvanheesnickalcock
authored andcommitted
dtrace: USDT and pid providers
For historical reasons, these are provided in a module named fasttrap.ko. Much of this is arch-dependent code for jump-table detection and implementation of globbed pid probes ("probe everything you can in this function"), as well as arch-dependent code to look up arguments and code to ensure that dropping a kprobe in a single process does not affect other processes running from the same binary. Signed-off-by: Kris Van Hees <[email protected]> Signed-off-by: Nick Alcock <[email protected]> Signed-off-by: Tomas Jedlicka <[email protected]> Signed-off-by: Eugene Loh <[email protected]> Signed-off-by: David Mc Lean <[email protected]> Signed-off-by: Vincent Lim <[email protected]>
1 parent 337f3de commit f80e48c

File tree

7 files changed

+2585
-0
lines changed

7 files changed

+2585
-0
lines changed

arch/x86/dtrace/Makefile.arch

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ DTARCHDIR = ../arch/x86/dtrace
77
ccflags-y += -Iarch/x86/dtrace/include -Idtrace
88

99
dtrace-obj += dtrace_asm_x86_64.o dtrace_isa_x86_64.o
10+
fasttrap-obj += fasttrap_x86_64.o
1011
sdt-obj += sdt_x86_64.o
1112

1213
dtrace-y += $(addprefix $(DTARCHDIR)/, $(dtrace-obj))
14+
fasttrap-y += $(addprefix $(DTARCHDIR)/, $(fasttrap-obj))
1315
sdt-y += $(addprefix $(DTARCHDIR)/, $(sdt-obj))

arch/x86/dtrace/fasttrap_x86_64.c

Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* FILE: fasttrap_x86_64.c
4+
* DESCRIPTION: DTrace - fasttrap provider implementation for x86
5+
*
6+
* Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
7+
*
8+
* This program is free software; you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License as published by
10+
* the Free Software Foundation; either version 2 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU General Public License for more details.
17+
*/
18+
19+
#include <asm/insn.h>
20+
#include <linux/kernel.h>
21+
#include <linux/slab.h>
22+
#include <linux/uaccess.h>
23+
24+
#include "dtrace.h"
25+
#include "dtrace_dev.h"
26+
#include "fasttrap_impl.h"
27+
28+
#define DISASM_REX_PREFIX(pfx) (((pfx) & 0xf0) == 0x40)
29+
#define DISASM_MODRM_REG(modrm) (((modrm) >> 3) & 0x07)
30+
31+
static int has_jump_table(const asm_instr_t *addr, size_t size)
32+
{
33+
const asm_instr_t *end = addr + size;
34+
35+
while (addr < end) {
36+
int len;
37+
38+
/*
39+
* Register-dependent jump instructions start with a 0xff byte
40+
* and have the modrm.reg field set to 4. Such instructions
41+
* tend to be used for jump tables.
42+
*/
43+
if ((addr[0] == 0xff && DISASM_MODRM_REG(addr[1]) == 4) ||
44+
(DISASM_REX_PREFIX(addr[0]) && addr[1] == 0xff &&
45+
DISASM_MODRM_REG(addr[2]) == 4))
46+
return 1;
47+
48+
len = dtrace_instr_size(addr);
49+
50+
/*
51+
* If we encounter a problem decoding an instruction, we will
52+
* assume that there might be a jump table. Better safe than
53+
* sorry...
54+
*/
55+
if (len < 0)
56+
return 1;
57+
58+
addr += len;
59+
}
60+
61+
return 0;
62+
}
63+
64+
static uint64_t *fasttrap_all_offsets(asm_instr_t *text, size_t size,
65+
uint64_t *np)
66+
{
67+
uint64_t *offs = NULL;
68+
uint64_t noffs;
69+
asm_instr_t *instr;
70+
asm_instr_t *end;
71+
72+
/*
73+
* Two passes are taken through this section of code. The first time
74+
* around we merely count the number of probe points. The second time,
75+
* we actually record their locations.
76+
*/
77+
again:
78+
noffs = 0;
79+
instr = text;
80+
end = text + size;
81+
82+
while (instr < end) {
83+
int len;
84+
85+
/*
86+
* If we fail to decode an instruction, it is time to give up.
87+
*/
88+
len = dtrace_instr_size(instr);
89+
if (len < 0)
90+
goto fail;
91+
92+
if (offs)
93+
offs[noffs] = (uint64_t)(instr - text);
94+
noffs++;
95+
96+
instr += len;
97+
}
98+
99+
if (offs == NULL) {
100+
/*
101+
* No matching offsets found - we are done.
102+
*/
103+
if (noffs == 0)
104+
goto fail;
105+
106+
/*
107+
* We know how many tracepoint locations there are for this
108+
* probe, so allocate member to record them, and kick off the
109+
* second pass.
110+
*/
111+
offs = kmalloc(sizeof(uint64_t) * noffs, GFP_KERNEL);
112+
if (!offs)
113+
goto fail;
114+
115+
goto again;
116+
}
117+
118+
*np = noffs;
119+
120+
return offs;
121+
122+
fail:
123+
*np = 0;
124+
kfree(offs);
125+
126+
return NULL;
127+
}
128+
129+
uint64_t *fasttrap_glob_offsets(fasttrap_probe_spec_t *probe, uint64_t *np)
130+
{
131+
size_t size = probe->ftps_size;
132+
asm_instr_t *text = NULL;
133+
asm_instr_t *instr;
134+
asm_instr_t *end;
135+
uint64_t *offs = NULL;
136+
uint64_t noffs;
137+
int ret = 0;
138+
char ostr[sizeof(instr) * 2 + 1];
139+
140+
text = kmalloc(size, GFP_KERNEL);
141+
if (!text)
142+
goto fail;
143+
144+
ret = dtrace_copy_code(probe->ftps_pid, (uint8_t *)text,
145+
probe->ftps_pc, size);
146+
if (ret != 0)
147+
goto fail;
148+
149+
if (has_jump_table(text, size))
150+
goto fail;
151+
152+
if (probe->ftps_glen == 1 && probe->ftps_gstr[0] == '*') {
153+
offs = fasttrap_all_offsets(text, size, &noffs);
154+
goto out;
155+
}
156+
157+
/*
158+
* Two passes are taken through this section of code. The first time
159+
* around we merely count the number of probe points. The second time,
160+
* we actually record their locations.
161+
*/
162+
again:
163+
noffs = 0;
164+
instr = text;
165+
end = text + size;
166+
167+
while (instr < end) {
168+
int len;
169+
uint64_t off = (uint64_t)(instr - text);
170+
171+
/*
172+
* If we fail to decode an instruction, it is time to give up.
173+
*/
174+
len = dtrace_instr_size(instr);
175+
if (len < 0)
176+
goto fail;
177+
178+
snprintf(ostr, sizeof(ostr), "%llx", off);
179+
if (dtrace_gmatch(ostr, probe->ftps_gstr)) {
180+
if (offs)
181+
offs[noffs] = off;
182+
noffs++;
183+
}
184+
185+
instr += len;
186+
}
187+
188+
if (offs == NULL) {
189+
/*
190+
* No matching offsets found - we are done.
191+
*/
192+
if (noffs == 0)
193+
goto fail;
194+
195+
/*
196+
* We know how many tracepoint locations there are for this
197+
* probe, so allocate member to record them, and kick off the
198+
* second pass.
199+
*/
200+
offs = kmalloc(sizeof(uint64_t) * noffs, GFP_KERNEL);
201+
if (!offs)
202+
goto fail;
203+
204+
goto again;
205+
}
206+
207+
out:
208+
kfree(text);
209+
210+
*np = noffs;
211+
212+
return offs;
213+
214+
fail:
215+
kfree(offs);
216+
kfree(text);
217+
218+
*np = 0;
219+
return NULL;
220+
}
221+
222+
uint64_t fasttrap_pid_getarg(void *arg, dtrace_id_t id, void *parg, int argno,
223+
int aframes)
224+
{
225+
struct pt_regs *regs = this_cpu_core->cpu_dtrace_regs;
226+
uint64_t *st;
227+
uint64_t val;
228+
229+
if (regs == NULL)
230+
return 0;
231+
232+
switch (argno) {
233+
case 0:
234+
return regs->di;
235+
case 1:
236+
return regs->si;
237+
case 2:
238+
return regs->dx;
239+
case 3:
240+
return regs->cx;
241+
case 4:
242+
return regs->r8;
243+
case 5:
244+
return regs->r9;
245+
}
246+
247+
ASSERT(argno > 5);
248+
249+
pagefault_disable();
250+
st = (uint64_t *)regs->sp;
251+
__copy_from_user_inatomic_nocache(&val, (void *)&st[argno - 6 + 1],
252+
sizeof(st[0]));
253+
pagefault_enable();
254+
255+
return val;
256+
}
257+
258+
uint64_t fasttrap_usdt_getarg(void *arg, dtrace_id_t id, void *parg,
259+
int argno, int aframes)
260+
{
261+
struct pt_regs *regs = this_cpu_core->cpu_dtrace_regs;
262+
uint64_t *st;
263+
uint64_t val;
264+
265+
if (regs == NULL)
266+
return 0;
267+
268+
switch (argno) {
269+
case 0:
270+
return regs->di;
271+
case 1:
272+
return regs->si;
273+
case 2:
274+
return regs->dx;
275+
case 3:
276+
return regs->cx;
277+
case 4:
278+
return regs->r8;
279+
case 5:
280+
return regs->r9;
281+
}
282+
283+
ASSERT(argno > 5);
284+
285+
pagefault_disable();
286+
st = (uint64_t *)regs->sp;
287+
__copy_from_user_inatomic_nocache(&val, (void *)&st[argno - 6],
288+
sizeof(st[0]));
289+
pagefault_enable();
290+
291+
return val;
292+
}
293+
294+
static void fasttrap_map_args(fasttrap_probe_t *probe, struct pt_regs *regs,
295+
int argc, uintptr_t *argv)
296+
{
297+
int i, x, cap = min(argc, (int)probe->ftp_nargs);
298+
uintptr_t *st = (uintptr_t *)regs->sp;
299+
300+
for (i = 0; i < cap; i++) {
301+
switch (x = probe->ftp_argmap[i]) {
302+
case 0:
303+
argv[i] = regs->di;
304+
break;
305+
case 1:
306+
argv[i] = regs->si;
307+
break;
308+
case 2:
309+
argv[i] = regs->dx;
310+
break;
311+
case 3:
312+
argv[i] = regs->cx;
313+
break;
314+
case 4:
315+
argv[i] = regs->r8;
316+
break;
317+
case 5:
318+
argv[i] = regs->r9;
319+
break;
320+
default:
321+
ASSERT(x > 5);
322+
323+
__copy_from_user_inatomic_nocache(&argv[i],
324+
(void *)&st[x - 6],
325+
sizeof(st[0]));
326+
}
327+
}
328+
329+
while (i < argc)
330+
argv[i++] = 0;
331+
}
332+
333+
void fasttrap_pid_probe_arch(fasttrap_probe_t *ftp, struct pt_regs *regs)
334+
{
335+
if (ftp->ftp_argmap == NULL) {
336+
dtrace_probe(ftp->ftp_id, regs->di, regs->si, regs->dx,
337+
regs->cx, regs->r8, regs->r9, 0);
338+
} else {
339+
uintptr_t t[6];
340+
341+
fasttrap_map_args(ftp, regs, sizeof(t) / sizeof(t[0]), t);
342+
dtrace_probe(ftp->ftp_id, t[0], t[1], t[2], t[3],
343+
t[4], t[5], 0);
344+
}
345+
}
346+
347+
void fasttrap_pid_retprobe_arch(fasttrap_probe_t *ftp, struct pt_regs *regs)
348+
{
349+
/*
350+
* FIXME: The first argument to the probe should be the offset in the
351+
* function that the return occured at, but uprobes doesn't give
352+
* us that information (or so it seems).
353+
*/
354+
dtrace_probe(ftp->ftp_id, 0, regs->ax, regs->dx, 0, 0, 0, 0);
355+
}
356+
357+
void fasttrap_set_enabled(struct pt_regs *regs)
358+
{
359+
regs->ax = 1;
360+
}
361+

0 commit comments

Comments
 (0)