1 | /*
|
---|
2 | * CDDL HEADER START
|
---|
3 | *
|
---|
4 | * The contents of this file are subject to the terms of the
|
---|
5 | * Common Development and Distribution License (the "License").
|
---|
6 | * You may not use this file except in compliance with the License.
|
---|
7 | *
|
---|
8 | * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
---|
9 | * or http://www.opensolaris.org/os/licensing.
|
---|
10 | * See the License for the specific language governing permissions
|
---|
11 | * and limitations under the License.
|
---|
12 | *
|
---|
13 | * When distributing Covered Code, include this CDDL HEADER in each
|
---|
14 | * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
---|
15 | * If applicable, add the following below this CDDL HEADER, with the
|
---|
16 | * fields enclosed by brackets "[]" replaced with your own identifying
|
---|
17 | * information: Portions Copyright [yyyy] [name of copyright owner]
|
---|
18 | *
|
---|
19 | * CDDL HEADER END
|
---|
20 | */
|
---|
21 | /*
|
---|
22 | * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
|
---|
23 | * Use is subject to license terms.
|
---|
24 | */
|
---|
25 |
|
---|
26 |
|
---|
27 | #include <sys/modctl.h>
|
---|
28 | #include <sys/dtrace.h>
|
---|
29 | #include <sys/kobj.h>
|
---|
30 | #include <sys/stat.h>
|
---|
31 | #include <sys/ddi.h>
|
---|
32 | #include <sys/sunddi.h>
|
---|
33 | #include <sys/conf.h>
|
---|
34 |
|
---|
35 | #define FBT_PUSHL_EBP 0x55
|
---|
36 | #define FBT_MOVL_ESP_EBP0_V0 0x8b
|
---|
37 | #define FBT_MOVL_ESP_EBP1_V0 0xec
|
---|
38 | #define FBT_MOVL_ESP_EBP0_V1 0x89
|
---|
39 | #define FBT_MOVL_ESP_EBP1_V1 0xe5
|
---|
40 | #define FBT_REX_RSP_RBP 0x48
|
---|
41 |
|
---|
42 | #define FBT_POPL_EBP 0x5d
|
---|
43 | #define FBT_RET 0xc3
|
---|
44 | #define FBT_RET_IMM16 0xc2
|
---|
45 | #define FBT_LEAVE 0xc9
|
---|
46 |
|
---|
47 | #ifdef __amd64
|
---|
48 | #define FBT_PATCHVAL 0xcc
|
---|
49 | #else
|
---|
50 | #define FBT_PATCHVAL 0xf0
|
---|
51 | #endif
|
---|
52 |
|
---|
53 | #define FBT_ENTRY "entry"
|
---|
54 | #define FBT_RETURN "return"
|
---|
55 | #define FBT_ADDR2NDX(addr) ((((uintptr_t)(addr)) >> 4) & fbt_probetab_mask)
|
---|
56 | #define FBT_PROBETAB_SIZE 0x8000 /* 32k entries -- 128K total */
|
---|
57 |
|
---|
58 | typedef struct fbt_probe {
|
---|
59 | struct fbt_probe *fbtp_hashnext;
|
---|
60 | uint8_t *fbtp_patchpoint;
|
---|
61 | int8_t fbtp_rval;
|
---|
62 | uint8_t fbtp_patchval;
|
---|
63 | uint8_t fbtp_savedval;
|
---|
64 | uintptr_t fbtp_roffset;
|
---|
65 | dtrace_id_t fbtp_id;
|
---|
66 | char *fbtp_name;
|
---|
67 | struct modctl *fbtp_ctl;
|
---|
68 | int fbtp_loadcnt;
|
---|
69 | int fbtp_symndx;
|
---|
70 | int fbtp_primary;
|
---|
71 | struct fbt_probe *fbtp_next;
|
---|
72 | } fbt_probe_t;
|
---|
73 |
|
---|
74 | static dev_info_t *fbt_devi;
|
---|
75 | static dtrace_provider_id_t fbt_id;
|
---|
76 | static fbt_probe_t **fbt_probetab;
|
---|
77 | static int fbt_probetab_size;
|
---|
78 | static int fbt_probetab_mask;
|
---|
79 | static int fbt_verbose = 0;
|
---|
80 |
|
---|
81 | static int
|
---|
82 | fbt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t rval)
|
---|
83 | {
|
---|
84 | uintptr_t stack0, stack1, stack2, stack3, stack4;
|
---|
85 | fbt_probe_t *fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
|
---|
86 |
|
---|
87 | for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
|
---|
88 | if ((uintptr_t)fbt->fbtp_patchpoint == addr) {
|
---|
89 | if (fbt->fbtp_roffset == 0) {
|
---|
90 | int i = 0;
|
---|
91 | /*
|
---|
92 | * When accessing the arguments on the stack,
|
---|
93 | * we must protect against accessing beyond
|
---|
94 | * the stack. We can safely set NOFAULT here
|
---|
95 | * -- we know that interrupts are already
|
---|
96 | * disabled.
|
---|
97 | */
|
---|
98 | DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
|
---|
99 | CPU->cpu_dtrace_caller = stack[i++];
|
---|
100 | #ifdef __amd64
|
---|
101 | /*
|
---|
102 | * On amd64, stack[0] contains the dereferenced
|
---|
103 | * stack pointer, stack[1] contains savfp,
|
---|
104 | * stack[2] contains savpc. We want to step
|
---|
105 | * over these entries.
|
---|
106 | */
|
---|
107 | i += 2;
|
---|
108 | #endif
|
---|
109 | stack0 = stack[i++];
|
---|
110 | stack1 = stack[i++];
|
---|
111 | stack2 = stack[i++];
|
---|
112 | stack3 = stack[i++];
|
---|
113 | stack4 = stack[i++];
|
---|
114 | DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
|
---|
115 | CPU_DTRACE_BADADDR);
|
---|
116 |
|
---|
117 | dtrace_probe(fbt->fbtp_id, stack0, stack1,
|
---|
118 | stack2, stack3, stack4);
|
---|
119 |
|
---|
120 | CPU->cpu_dtrace_caller = NULL;
|
---|
121 | } else {
|
---|
122 | #ifdef __amd64
|
---|
123 | /*
|
---|
124 | * On amd64, we instrument the ret, not the
|
---|
125 | * leave. We therefore need to set the caller
|
---|
126 | * to assure that the top frame of a stack()
|
---|
127 | * action is correct.
|
---|
128 | */
|
---|
129 | DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
|
---|
130 | CPU->cpu_dtrace_caller = stack[0];
|
---|
131 | DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
|
---|
132 | CPU_DTRACE_BADADDR);
|
---|
133 | #endif
|
---|
134 |
|
---|
135 | dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
|
---|
136 | rval, 0, 0, 0);
|
---|
137 | CPU->cpu_dtrace_caller = NULL;
|
---|
138 | }
|
---|
139 |
|
---|
140 | return (fbt->fbtp_rval);
|
---|
141 | }
|
---|
142 | }
|
---|
143 |
|
---|
144 | return (0);
|
---|
145 | }
|
---|
146 |
|
---|
147 | /*ARGSUSED*/
|
---|
148 | static void
|
---|
149 | fbt_provide_module(void *arg, struct modctl *ctl)
|
---|
150 | {
|
---|
151 | struct module *mp = ctl->mod_mp;
|
---|
152 | char *str = mp->strings;
|
---|
153 | int nsyms = mp->nsyms;
|
---|
154 | Shdr *symhdr = mp->symhdr;
|
---|
155 | char *modname = ctl->mod_modname;
|
---|
156 | char *name;
|
---|
157 | fbt_probe_t *fbt, *retfbt;
|
---|
158 | size_t symsize;
|
---|
159 | int i, size;
|
---|
160 |
|
---|
161 | /*
|
---|
162 | * Employees of dtrace and their families are ineligible. Void
|
---|
163 | * where prohibited.
|
---|
164 | */
|
---|
165 | if (strcmp(modname, "dtrace") == 0)
|
---|
166 | return;
|
---|
167 |
|
---|
168 | if (ctl->mod_requisites != NULL) {
|
---|
169 | struct modctl_list *list;
|
---|
170 |
|
---|
171 | list = (struct modctl_list *)ctl->mod_requisites;
|
---|
172 |
|
---|
173 | for (; list != NULL; list = list->modl_next) {
|
---|
174 | if (strcmp(list->modl_modp->mod_modname, "dtrace") == 0)
|
---|
175 | return;
|
---|
176 | }
|
---|
177 | }
|
---|
178 |
|
---|
179 | /*
|
---|
180 | * KMDB is ineligible for instrumentation -- it may execute in
|
---|
181 | * any context, including probe context.
|
---|
182 | */
|
---|
183 | if (strcmp(modname, "kmdbmod") == 0)
|
---|
184 | return;
|
---|
185 |
|
---|
186 | if (str == NULL || symhdr == NULL || symhdr->sh_addr == NULL) {
|
---|
187 | /*
|
---|
188 | * If this module doesn't (yet) have its string or symbol
|
---|
189 | * table allocated, clear out.
|
---|
190 | */
|
---|
191 | return;
|
---|
192 | }
|
---|
193 |
|
---|
194 | symsize = symhdr->sh_entsize;
|
---|
195 |
|
---|
196 | if (mp->fbt_nentries) {
|
---|
197 | /*
|
---|
198 | * This module has some FBT entries allocated; we're afraid
|
---|
199 | * to screw with it.
|
---|
200 | */
|
---|
201 | return;
|
---|
202 | }
|
---|
203 |
|
---|
204 | for (i = 1; i < nsyms; i++) {
|
---|
205 | uint8_t *instr, *limit;
|
---|
206 | Sym *sym = (Sym *)(symhdr->sh_addr + i * symsize);
|
---|
207 | int j;
|
---|
208 |
|
---|
209 | if (ELF_ST_TYPE(sym->st_info) != STT_FUNC)
|
---|
210 | continue;
|
---|
211 |
|
---|
212 | /*
|
---|
213 | * Weak symbols are not candidates. This could be made to
|
---|
214 | * work (where weak functions and their underlying function
|
---|
215 | * appear as two disjoint probes), but it's not simple.
|
---|
216 | */
|
---|
217 | if (ELF_ST_BIND(sym->st_info) == STB_WEAK)
|
---|
218 | continue;
|
---|
219 |
|
---|
220 | name = str + sym->st_name;
|
---|
221 |
|
---|
222 | if (strstr(name, "dtrace_") == name &&
|
---|
223 | strstr(name, "dtrace_safe_") != name) {
|
---|
224 | /*
|
---|
225 | * Anything beginning with "dtrace_" may be called
|
---|
226 | * from probe context unless it explitly indicates
|
---|
227 | * that it won't be called from probe context by
|
---|
228 | * using the prefix "dtrace_safe_".
|
---|
229 | */
|
---|
230 | continue;
|
---|
231 | }
|
---|
232 |
|
---|
233 | if (strstr(name, "kdi_") == name ||
|
---|
234 | strstr(name, "_kdi_") != NULL) {
|
---|
235 | /*
|
---|
236 | * Any function name beginning with "kdi_" or
|
---|
237 | * containing the string "_kdi_" is a part of the
|
---|
238 | * kernel debugger interface and may be called in
|
---|
239 | * arbitrary context -- including probe context.
|
---|
240 | */
|
---|
241 | continue;
|
---|
242 | }
|
---|
243 |
|
---|
244 | /*
|
---|
245 | * Due to 4524008, _init and _fini may have a bloated st_size.
|
---|
246 | * While this bug was fixed quite some time ago, old drivers
|
---|
247 | * may be lurking. We need to develop a better solution to
|
---|
248 | * this problem, such that correct _init and _fini functions
|
---|
249 | * (the vast majority) may be correctly traced. One solution
|
---|
250 | * may be to scan through the entire symbol table to see if
|
---|
251 | * any symbol overlaps with _init. If none does, set a bit in
|
---|
252 | * the module structure that this module has correct _init and
|
---|
253 | * _fini sizes. This will cause some pain the first time a
|
---|
254 | * module is scanned, but at least it would be O(N) instead of
|
---|
255 | * O(N log N)...
|
---|
256 | */
|
---|
257 | if (strcmp(name, "_init") == 0)
|
---|
258 | continue;
|
---|
259 |
|
---|
260 | if (strcmp(name, "_fini") == 0)
|
---|
261 | continue;
|
---|
262 |
|
---|
263 | /*
|
---|
264 | * In order to be eligible, the function must begin with the
|
---|
265 | * following sequence:
|
---|
266 | *
|
---|
267 | * pushl %esp
|
---|
268 | * movl %esp, %ebp
|
---|
269 | *
|
---|
270 | * Note that there are two variants of encodings that generate
|
---|
271 | * the movl; we must check for both. For 64-bit, we would
|
---|
272 | * normally insist that a function begin with the following
|
---|
273 | * sequence:
|
---|
274 | *
|
---|
275 | * pushq %rbp
|
---|
276 | * movq %rsp, %rbp
|
---|
277 | *
|
---|
278 | * However, the compiler for 64-bit often splits these two
|
---|
279 | * instructions -- and the first instruction in the function
|
---|
280 | * is often not the pushq. As a result, on 64-bit we look
|
---|
281 | * for any "pushq %rbp" in the function and we instrument
|
---|
282 | * this with a breakpoint instruction.
|
---|
283 | */
|
---|
284 | instr = (uint8_t *)sym->st_value;
|
---|
285 | limit = (uint8_t *)(sym->st_value + sym->st_size);
|
---|
286 |
|
---|
287 | #ifdef __amd64
|
---|
288 | while (instr < limit) {
|
---|
289 | if (*instr == FBT_PUSHL_EBP)
|
---|
290 | break;
|
---|
291 |
|
---|
292 | if ((size = dtrace_instr_size(instr)) <= 0)
|
---|
293 | break;
|
---|
294 |
|
---|
295 | instr += size;
|
---|
296 | }
|
---|
297 |
|
---|
298 | if (instr >= limit || *instr != FBT_PUSHL_EBP) {
|
---|
299 | /*
|
---|
300 | * We either don't save the frame pointer in this
|
---|
301 | * function, or we ran into some disassembly
|
---|
302 | * screw-up. Either way, we bail.
|
---|
303 | */
|
---|
304 | continue;
|
---|
305 | }
|
---|
306 | #else
|
---|
307 | if (instr[0] != FBT_PUSHL_EBP)
|
---|
308 | continue;
|
---|
309 |
|
---|
310 | if (!(instr[1] == FBT_MOVL_ESP_EBP0_V0 &&
|
---|
311 | instr[2] == FBT_MOVL_ESP_EBP1_V0) &&
|
---|
312 | !(instr[1] == FBT_MOVL_ESP_EBP0_V1 &&
|
---|
313 | instr[2] == FBT_MOVL_ESP_EBP1_V1))
|
---|
314 | continue;
|
---|
315 | #endif
|
---|
316 |
|
---|
317 | fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
|
---|
318 | fbt->fbtp_name = name;
|
---|
319 | fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
|
---|
320 | name, FBT_ENTRY, 3, fbt);
|
---|
321 | fbt->fbtp_patchpoint = instr;
|
---|
322 | fbt->fbtp_ctl = ctl;
|
---|
323 | fbt->fbtp_loadcnt = ctl->mod_loadcnt;
|
---|
324 | fbt->fbtp_rval = DTRACE_INVOP_PUSHL_EBP;
|
---|
325 | fbt->fbtp_savedval = *instr;
|
---|
326 | fbt->fbtp_patchval = FBT_PATCHVAL;
|
---|
327 |
|
---|
328 | fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
|
---|
329 | fbt->fbtp_symndx = i;
|
---|
330 | fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
|
---|
331 |
|
---|
332 | mp->fbt_nentries++;
|
---|
333 |
|
---|
334 | retfbt = NULL;
|
---|
335 | again:
|
---|
336 | if (instr >= limit)
|
---|
337 | continue;
|
---|
338 |
|
---|
339 | /*
|
---|
340 | * If this disassembly fails, then we've likely walked off into
|
---|
341 | * a jump table or some other unsuitable area. Bail out of the
|
---|
342 | * disassembly now.
|
---|
343 | */
|
---|
344 | if ((size = dtrace_instr_size(instr)) <= 0)
|
---|
345 | continue;
|
---|
346 |
|
---|
347 | #ifdef __amd64
|
---|
348 | /*
|
---|
349 | * We only instrument "ret" on amd64 -- we don't yet instrument
|
---|
350 | * ret imm16, largely because the compiler doesn't seem to
|
---|
351 | * (yet) emit them in the kernel...
|
---|
352 | */
|
---|
353 | if (*instr != FBT_RET) {
|
---|
354 | instr += size;
|
---|
355 | goto again;
|
---|
356 | }
|
---|
357 | #else
|
---|
358 | if (!(size == 1 &&
|
---|
359 | (*instr == FBT_POPL_EBP || *instr == FBT_LEAVE) &&
|
---|
360 | (*(instr + 1) == FBT_RET ||
|
---|
361 | *(instr + 1) == FBT_RET_IMM16))) {
|
---|
362 | instr += size;
|
---|
363 | goto again;
|
---|
364 | }
|
---|
365 | #endif
|
---|
366 |
|
---|
367 | /*
|
---|
368 | * We (desperately) want to avoid erroneously instrumenting a
|
---|
369 | * jump table, especially given that our markers are pretty
|
---|
370 | * short: two bytes on x86, and just one byte on amd64. To
|
---|
371 | * determine if we're looking at a true instruction sequence
|
---|
372 | * or an inline jump table that happens to contain the same
|
---|
373 | * byte sequences, we resort to some heuristic sleeze: we
|
---|
374 | * treat this instruction as being contained within a pointer,
|
---|
375 | * and see if that pointer points to within the body of the
|
---|
376 | * function. If it does, we refuse to instrument it.
|
---|
377 | */
|
---|
378 | for (j = 0; j < sizeof (uintptr_t); j++) {
|
---|
379 | uintptr_t check = (uintptr_t)instr - j;
|
---|
380 | uint8_t *ptr;
|
---|
381 |
|
---|
382 | if (check < sym->st_value)
|
---|
383 | break;
|
---|
384 |
|
---|
385 | if (check + sizeof (uintptr_t) > (uintptr_t)limit)
|
---|
386 | continue;
|
---|
387 |
|
---|
388 | ptr = *(uint8_t **)check;
|
---|
389 |
|
---|
390 | if (ptr >= (uint8_t *)sym->st_value && ptr < limit) {
|
---|
391 | instr += size;
|
---|
392 | goto again;
|
---|
393 | }
|
---|
394 | }
|
---|
395 |
|
---|
396 | /*
|
---|
397 | * We have a winner!
|
---|
398 | */
|
---|
399 | fbt = kmem_zalloc(sizeof (fbt_probe_t), KM_SLEEP);
|
---|
400 | fbt->fbtp_name = name;
|
---|
401 |
|
---|
402 | if (retfbt == NULL) {
|
---|
403 | fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
|
---|
404 | name, FBT_RETURN, 3, fbt);
|
---|
405 | } else {
|
---|
406 | retfbt->fbtp_next = fbt;
|
---|
407 | fbt->fbtp_id = retfbt->fbtp_id;
|
---|
408 | }
|
---|
409 |
|
---|
410 | retfbt = fbt;
|
---|
411 | fbt->fbtp_patchpoint = instr;
|
---|
412 | fbt->fbtp_ctl = ctl;
|
---|
413 | fbt->fbtp_loadcnt = ctl->mod_loadcnt;
|
---|
414 |
|
---|
415 | #ifndef __amd64
|
---|
416 | if (*instr == FBT_POPL_EBP) {
|
---|
417 | fbt->fbtp_rval = DTRACE_INVOP_POPL_EBP;
|
---|
418 | } else {
|
---|
419 | ASSERT(*instr == FBT_LEAVE);
|
---|
420 | fbt->fbtp_rval = DTRACE_INVOP_LEAVE;
|
---|
421 | }
|
---|
422 | fbt->fbtp_roffset =
|
---|
423 | (uintptr_t)(instr - (uint8_t *)sym->st_value) + 1;
|
---|
424 |
|
---|
425 | #else
|
---|
426 | ASSERT(*instr == FBT_RET);
|
---|
427 | fbt->fbtp_rval = DTRACE_INVOP_RET;
|
---|
428 | fbt->fbtp_roffset =
|
---|
429 | (uintptr_t)(instr - (uint8_t *)sym->st_value);
|
---|
430 | #endif
|
---|
431 |
|
---|
432 | fbt->fbtp_savedval = *instr;
|
---|
433 | fbt->fbtp_patchval = FBT_PATCHVAL;
|
---|
434 | fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
|
---|
435 | fbt->fbtp_symndx = i;
|
---|
436 | fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
|
---|
437 |
|
---|
438 | mp->fbt_nentries++;
|
---|
439 |
|
---|
440 | instr += size;
|
---|
441 | goto again;
|
---|
442 | }
|
---|
443 | }
|
---|
444 |
|
---|
445 | /*ARGSUSED*/
|
---|
446 | static void
|
---|
447 | fbt_destroy(void *arg, dtrace_id_t id, void *parg)
|
---|
448 | {
|
---|
449 | fbt_probe_t *fbt = parg, *next, *hash, *last;
|
---|
450 | struct modctl *ctl = fbt->fbtp_ctl;
|
---|
451 | int ndx;
|
---|
452 |
|
---|
453 | do {
|
---|
454 | if (ctl != NULL && ctl->mod_loadcnt == fbt->fbtp_loadcnt) {
|
---|
455 | if ((ctl->mod_loadcnt == fbt->fbtp_loadcnt &&
|
---|
456 | ctl->mod_loaded)) {
|
---|
457 | ((struct module *)
|
---|
458 | (ctl->mod_mp))->fbt_nentries--;
|
---|
459 | }
|
---|
460 | }
|
---|
461 |
|
---|
462 | /*
|
---|
463 | * Now we need to remove this probe from the fbt_probetab.
|
---|
464 | */
|
---|
465 | ndx = FBT_ADDR2NDX(fbt->fbtp_patchpoint);
|
---|
466 | last = NULL;
|
---|
467 | hash = fbt_probetab[ndx];
|
---|
468 |
|
---|
469 | while (hash != fbt) {
|
---|
470 | ASSERT(hash != NULL);
|
---|
471 | last = hash;
|
---|
472 | hash = hash->fbtp_hashnext;
|
---|
473 | }
|
---|
474 |
|
---|
475 | if (last != NULL) {
|
---|
476 | last->fbtp_hashnext = fbt->fbtp_hashnext;
|
---|
477 | } else {
|
---|
478 | fbt_probetab[ndx] = fbt->fbtp_hashnext;
|
---|
479 | }
|
---|
480 |
|
---|
481 | next = fbt->fbtp_next;
|
---|
482 | kmem_free(fbt, sizeof (fbt_probe_t));
|
---|
483 |
|
---|
484 | fbt = next;
|
---|
485 | } while (fbt != NULL);
|
---|
486 | }
|
---|
487 |
|
---|
488 | /*ARGSUSED*/
|
---|
489 | static int
|
---|
490 | fbt_enable(void *arg, dtrace_id_t id, void *parg)
|
---|
491 | {
|
---|
492 | fbt_probe_t *fbt = parg;
|
---|
493 | struct modctl *ctl = fbt->fbtp_ctl;
|
---|
494 |
|
---|
495 | ctl->mod_nenabled++;
|
---|
496 |
|
---|
497 | if (!ctl->mod_loaded) {
|
---|
498 | if (fbt_verbose) {
|
---|
499 | cmn_err(CE_NOTE, "fbt is failing for probe %s "
|
---|
500 | "(module %s unloaded)",
|
---|
501 | fbt->fbtp_name, ctl->mod_modname);
|
---|
502 | }
|
---|
503 |
|
---|
504 | return (0);
|
---|
505 | }
|
---|
506 |
|
---|
507 | /*
|
---|
508 | * Now check that our modctl has the expected load count. If it
|
---|
509 | * doesn't, this module must have been unloaded and reloaded -- and
|
---|
510 | * we're not going to touch it.
|
---|
511 | */
|
---|
512 | if (ctl->mod_loadcnt != fbt->fbtp_loadcnt) {
|
---|
513 | if (fbt_verbose) {
|
---|
514 | cmn_err(CE_NOTE, "fbt is failing for probe %s "
|
---|
515 | "(module %s reloaded)",
|
---|
516 | fbt->fbtp_name, ctl->mod_modname);
|
---|
517 | }
|
---|
518 |
|
---|
519 | return (0);
|
---|
520 | }
|
---|
521 |
|
---|
522 | for (; fbt != NULL; fbt = fbt->fbtp_next)
|
---|
523 | *fbt->fbtp_patchpoint = fbt->fbtp_patchval;
|
---|
524 |
|
---|
525 | return (0);
|
---|
526 | }
|
---|
527 |
|
---|
528 | /*ARGSUSED*/
|
---|
529 | static void
|
---|
530 | fbt_disable(void *arg, dtrace_id_t id, void *parg)
|
---|
531 | {
|
---|
532 | fbt_probe_t *fbt = parg;
|
---|
533 | struct modctl *ctl = fbt->fbtp_ctl;
|
---|
534 |
|
---|
535 | ASSERT(ctl->mod_nenabled > 0);
|
---|
536 | ctl->mod_nenabled--;
|
---|
537 |
|
---|
538 | if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt))
|
---|
539 | return;
|
---|
540 |
|
---|
541 | for (; fbt != NULL; fbt = fbt->fbtp_next)
|
---|
542 | *fbt->fbtp_patchpoint = fbt->fbtp_savedval;
|
---|
543 | }
|
---|
544 |
|
---|
545 | /*ARGSUSED*/
|
---|
546 | static void
|
---|
547 | fbt_suspend(void *arg, dtrace_id_t id, void *parg)
|
---|
548 | {
|
---|
549 | fbt_probe_t *fbt = parg;
|
---|
550 | struct modctl *ctl = fbt->fbtp_ctl;
|
---|
551 |
|
---|
552 | ASSERT(ctl->mod_nenabled > 0);
|
---|
553 |
|
---|
554 | if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt))
|
---|
555 | return;
|
---|
556 |
|
---|
557 | for (; fbt != NULL; fbt = fbt->fbtp_next)
|
---|
558 | *fbt->fbtp_patchpoint = fbt->fbtp_savedval;
|
---|
559 | }
|
---|
560 |
|
---|
561 | /*ARGSUSED*/
|
---|
562 | static void
|
---|
563 | fbt_resume(void *arg, dtrace_id_t id, void *parg)
|
---|
564 | {
|
---|
565 | fbt_probe_t *fbt = parg;
|
---|
566 | struct modctl *ctl = fbt->fbtp_ctl;
|
---|
567 |
|
---|
568 | ASSERT(ctl->mod_nenabled > 0);
|
---|
569 |
|
---|
570 | if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt))
|
---|
571 | return;
|
---|
572 |
|
---|
573 | for (; fbt != NULL; fbt = fbt->fbtp_next)
|
---|
574 | *fbt->fbtp_patchpoint = fbt->fbtp_patchval;
|
---|
575 | }
|
---|
576 |
|
---|
577 | /*ARGSUSED*/
|
---|
578 | static void
|
---|
579 | fbt_getargdesc(void *arg, dtrace_id_t id, void *parg, dtrace_argdesc_t *desc)
|
---|
580 | {
|
---|
581 | fbt_probe_t *fbt = parg;
|
---|
582 | struct modctl *ctl = fbt->fbtp_ctl;
|
---|
583 | struct module *mp = ctl->mod_mp;
|
---|
584 | ctf_file_t *fp = NULL, *pfp;
|
---|
585 | ctf_funcinfo_t f;
|
---|
586 | int error;
|
---|
587 | ctf_id_t argv[32], type;
|
---|
588 | int argc = sizeof (argv) / sizeof (ctf_id_t);
|
---|
589 | const char *parent;
|
---|
590 |
|
---|
591 | if (!ctl->mod_loaded || (ctl->mod_loadcnt != fbt->fbtp_loadcnt))
|
---|
592 | goto err;
|
---|
593 |
|
---|
594 | if (fbt->fbtp_roffset != 0 && desc->dtargd_ndx == 0) {
|
---|
595 | (void) strcpy(desc->dtargd_native, "int");
|
---|
596 | return;
|
---|
597 | }
|
---|
598 |
|
---|
599 | if ((fp = ctf_modopen(mp, &error)) == NULL) {
|
---|
600 | /*
|
---|
601 | * We have no CTF information for this module -- and therefore
|
---|
602 | * no args[] information.
|
---|
603 | */
|
---|
604 | goto err;
|
---|
605 | }
|
---|
606 |
|
---|
607 | /*
|
---|
608 | * If we have a parent container, we must manually import it.
|
---|
609 | */
|
---|
610 | if ((parent = ctf_parent_name(fp)) != NULL) {
|
---|
611 | struct modctl *mp = &modules;
|
---|
612 | struct modctl *mod = NULL;
|
---|
613 |
|
---|
614 | /*
|
---|
615 | * We must iterate over all modules to find the module that
|
---|
616 | * is our parent.
|
---|
617 | */
|
---|
618 | do {
|
---|
619 | if (strcmp(mp->mod_modname, parent) == 0) {
|
---|
620 | mod = mp;
|
---|
621 | break;
|
---|
622 | }
|
---|
623 | } while ((mp = mp->mod_next) != &modules);
|
---|
624 |
|
---|
625 | if (mod == NULL)
|
---|
626 | goto err;
|
---|
627 |
|
---|
628 | if ((pfp = ctf_modopen(mod->mod_mp, &error)) == NULL) {
|
---|
629 | goto err;
|
---|
630 | }
|
---|
631 |
|
---|
632 | if (ctf_import(fp, pfp) != 0) {
|
---|
633 | ctf_close(pfp);
|
---|
634 | goto err;
|
---|
635 | }
|
---|
636 |
|
---|
637 | ctf_close(pfp);
|
---|
638 | }
|
---|
639 |
|
---|
640 | if (ctf_func_info(fp, fbt->fbtp_symndx, &f) == CTF_ERR)
|
---|
641 | goto err;
|
---|
642 |
|
---|
643 | if (fbt->fbtp_roffset != 0) {
|
---|
644 | if (desc->dtargd_ndx > 1)
|
---|
645 | goto err;
|
---|
646 |
|
---|
647 | ASSERT(desc->dtargd_ndx == 1);
|
---|
648 | type = f.ctc_return;
|
---|
649 | } else {
|
---|
650 | if (desc->dtargd_ndx + 1 > f.ctc_argc)
|
---|
651 | goto err;
|
---|
652 |
|
---|
653 | if (ctf_func_args(fp, fbt->fbtp_symndx, argc, argv) == CTF_ERR)
|
---|
654 | goto err;
|
---|
655 |
|
---|
656 | type = argv[desc->dtargd_ndx];
|
---|
657 | }
|
---|
658 |
|
---|
659 | if (ctf_type_name(fp, type, desc->dtargd_native,
|
---|
660 | DTRACE_ARGTYPELEN) != NULL) {
|
---|
661 | ctf_close(fp);
|
---|
662 | return;
|
---|
663 | }
|
---|
664 | err:
|
---|
665 | if (fp != NULL)
|
---|
666 | ctf_close(fp);
|
---|
667 |
|
---|
668 | desc->dtargd_ndx = DTRACE_ARGNONE;
|
---|
669 | }
|
---|
670 |
|
---|
671 | static dtrace_pattr_t fbt_attr = {
|
---|
672 | { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
|
---|
673 | { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
|
---|
674 | { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_UNKNOWN },
|
---|
675 | { DTRACE_STABILITY_EVOLVING, DTRACE_STABILITY_EVOLVING, DTRACE_CLASS_ISA },
|
---|
676 | { DTRACE_STABILITY_PRIVATE, DTRACE_STABILITY_PRIVATE, DTRACE_CLASS_ISA },
|
---|
677 | };
|
---|
678 |
|
---|
679 | static dtrace_pops_t fbt_pops = {
|
---|
680 | NULL,
|
---|
681 | fbt_provide_module,
|
---|
682 | fbt_enable,
|
---|
683 | fbt_disable,
|
---|
684 | fbt_suspend,
|
---|
685 | fbt_resume,
|
---|
686 | fbt_getargdesc,
|
---|
687 | NULL,
|
---|
688 | NULL,
|
---|
689 | fbt_destroy
|
---|
690 | };
|
---|
691 |
|
---|
692 | static void
|
---|
693 | fbt_cleanup(dev_info_t *devi)
|
---|
694 | {
|
---|
695 | dtrace_invop_remove(fbt_invop);
|
---|
696 | ddi_remove_minor_node(devi, NULL);
|
---|
697 | kmem_free(fbt_probetab, fbt_probetab_size * sizeof (fbt_probe_t *));
|
---|
698 | fbt_probetab = NULL;
|
---|
699 | fbt_probetab_mask = 0;
|
---|
700 | }
|
---|
701 |
|
---|
702 | static int
|
---|
703 | fbt_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
|
---|
704 | {
|
---|
705 | switch (cmd) {
|
---|
706 | case DDI_ATTACH:
|
---|
707 | break;
|
---|
708 | case DDI_RESUME:
|
---|
709 | return (DDI_SUCCESS);
|
---|
710 | default:
|
---|
711 | return (DDI_FAILURE);
|
---|
712 | }
|
---|
713 |
|
---|
714 | if (fbt_probetab_size == 0)
|
---|
715 | fbt_probetab_size = FBT_PROBETAB_SIZE;
|
---|
716 |
|
---|
717 | fbt_probetab_mask = fbt_probetab_size - 1;
|
---|
718 | fbt_probetab =
|
---|
719 | kmem_zalloc(fbt_probetab_size * sizeof (fbt_probe_t *), KM_SLEEP);
|
---|
720 |
|
---|
721 | dtrace_invop_add(fbt_invop);
|
---|
722 |
|
---|
723 | if (ddi_create_minor_node(devi, "fbt", S_IFCHR, 0,
|
---|
724 | DDI_PSEUDO, NULL) == DDI_FAILURE ||
|
---|
725 | dtrace_register("fbt", &fbt_attr, DTRACE_PRIV_KERNEL, NULL,
|
---|
726 | &fbt_pops, NULL, &fbt_id) != 0) {
|
---|
727 | fbt_cleanup(devi);
|
---|
728 | return (DDI_FAILURE);
|
---|
729 | }
|
---|
730 |
|
---|
731 | ddi_report_dev(devi);
|
---|
732 | fbt_devi = devi;
|
---|
733 |
|
---|
734 | return (DDI_SUCCESS);
|
---|
735 | }
|
---|
736 |
|
---|
737 | static int
|
---|
738 | fbt_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
|
---|
739 | {
|
---|
740 | switch (cmd) {
|
---|
741 | case DDI_DETACH:
|
---|
742 | break;
|
---|
743 | case DDI_SUSPEND:
|
---|
744 | return (DDI_SUCCESS);
|
---|
745 | default:
|
---|
746 | return (DDI_FAILURE);
|
---|
747 | }
|
---|
748 |
|
---|
749 | if (dtrace_unregister(fbt_id) != 0)
|
---|
750 | return (DDI_FAILURE);
|
---|
751 |
|
---|
752 | fbt_cleanup(devi);
|
---|
753 |
|
---|
754 | return (DDI_SUCCESS);
|
---|
755 | }
|
---|
756 |
|
---|
757 | /*ARGSUSED*/
|
---|
758 | static int
|
---|
759 | fbt_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
|
---|
760 | {
|
---|
761 | int error;
|
---|
762 |
|
---|
763 | switch (infocmd) {
|
---|
764 | case DDI_INFO_DEVT2DEVINFO:
|
---|
765 | *result = (void *)fbt_devi;
|
---|
766 | error = DDI_SUCCESS;
|
---|
767 | break;
|
---|
768 | case DDI_INFO_DEVT2INSTANCE:
|
---|
769 | *result = (void *)0;
|
---|
770 | error = DDI_SUCCESS;
|
---|
771 | break;
|
---|
772 | default:
|
---|
773 | error = DDI_FAILURE;
|
---|
774 | }
|
---|
775 | return (error);
|
---|
776 | }
|
---|
777 |
|
---|
778 | /*ARGSUSED*/
|
---|
779 | static int
|
---|
780 | fbt_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
|
---|
781 | {
|
---|
782 | return (0);
|
---|
783 | }
|
---|
784 |
|
---|
785 | static struct cb_ops fbt_cb_ops = {
|
---|
786 | fbt_open, /* open */
|
---|
787 | nodev, /* close */
|
---|
788 | nulldev, /* strategy */
|
---|
789 | nulldev, /* print */
|
---|
790 | nodev, /* dump */
|
---|
791 | nodev, /* read */
|
---|
792 | nodev, /* write */
|
---|
793 | nodev, /* ioctl */
|
---|
794 | nodev, /* devmap */
|
---|
795 | nodev, /* mmap */
|
---|
796 | nodev, /* segmap */
|
---|
797 | nochpoll, /* poll */
|
---|
798 | ddi_prop_op, /* cb_prop_op */
|
---|
799 | 0, /* streamtab */
|
---|
800 | D_NEW | D_MP /* Driver compatibility flag */
|
---|
801 | };
|
---|
802 |
|
---|
803 | static struct dev_ops fbt_ops = {
|
---|
804 | DEVO_REV, /* devo_rev */
|
---|
805 | 0, /* refcnt */
|
---|
806 | fbt_info, /* get_dev_info */
|
---|
807 | nulldev, /* identify */
|
---|
808 | nulldev, /* probe */
|
---|
809 | fbt_attach, /* attach */
|
---|
810 | fbt_detach, /* detach */
|
---|
811 | nodev, /* reset */
|
---|
812 | &fbt_cb_ops, /* driver operations */
|
---|
813 | NULL, /* bus operations */
|
---|
814 | nodev, /* dev power */
|
---|
815 | ddi_quiesce_not_needed, /* quiesce */
|
---|
816 | };
|
---|
817 |
|
---|
818 | /*
|
---|
819 | * Module linkage information for the kernel.
|
---|
820 | */
|
---|
821 | static struct modldrv modldrv = {
|
---|
822 | &mod_driverops, /* module type (this is a pseudo driver) */
|
---|
823 | "Function Boundary Tracing", /* name of module */
|
---|
824 | &fbt_ops, /* driver ops */
|
---|
825 | };
|
---|
826 |
|
---|
827 | static struct modlinkage modlinkage = {
|
---|
828 | MODREV_1,
|
---|
829 | (void *)&modldrv,
|
---|
830 | NULL
|
---|
831 | };
|
---|
832 |
|
---|
833 | int
|
---|
834 | _init(void)
|
---|
835 | {
|
---|
836 | return (mod_install(&modlinkage));
|
---|
837 | }
|
---|
838 |
|
---|
839 | int
|
---|
840 | _info(struct modinfo *modinfop)
|
---|
841 | {
|
---|
842 | return (mod_info(&modlinkage, modinfop));
|
---|
843 | }
|
---|
844 |
|
---|
845 | int
|
---|
846 | _fini(void)
|
---|
847 | {
|
---|
848 | return (mod_remove(&modlinkage));
|
---|
849 | }
|
---|