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, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright (c) 1994, by Sun Microsytems, Inc.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 /*
29 * Interfaces to control kernel tracing and kernel probes
30 */
31
32 #ifndef DEBUG
33 #define NDEBUG 1
34 #endif
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <string.h> /* for strerror() */
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/tnf.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 #include <signal.h>
46 #include <assert.h>
47
48 #include "tnfctl_int.h"
49 #include "kernel_int.h"
50
51 /* The TNF pseudo-device */
52 #define TNFDRIVER "/dev/tnfctl"
53
54 /* Dummy "test" function -- just used to flag enabled probes */
55 #define PRBK_DUMMY_TEST ((tnf_probe_test_func_t) 4)
56
57 /* Dummy "commit" function -- just used to flag trace enabled */
58 #define PRBK_DUMMY_COMMIT ((tnf_probe_func_t) 8)
59
60 /* Dummy "rollback" function -- just used to flag trace disabled */
61 #define PRBK_DUMMY_ROLLBACK ((tnf_probe_func_t) 12)
62
63 /* Dummy "end" function */
64 #define PRBK_DUMMY_END ((uintptr_t) 16)
65
66 /* Dummy "alloc" function */
67 #define PRBK_DUMMY_ALLOC ((uintptr_t) 20)
68
69 /* Minimum and maximum allowed buffer sizes. */
70 /* XXX -- maximum should be some function of physmem. */
71 #define KERNEL_MINBUF_SIZE (128 * 1024)
72 #define KERNEL_MAXBUF_SIZE (128 * 1024 * 1024)
73
74 static tnfctl_errcode_t prbk_get_buf_attrs(tnfctl_handle_t *hdl);
75 static tnfctl_errcode_t alloc_probe_space(tnfctl_handle_t *hndl, int maxprobe);
76
77 /*
78 * Initialize the kernel interface: Open the TNF control device,
79 * and determine the current kernel probes state, including the
80 * current pidfilter list.
81 */
82 tnfctl_errcode_t
_tnfctl_prbk_init(tnfctl_handle_t * hdl)83 _tnfctl_prbk_init(tnfctl_handle_t *hdl)
84 {
85 tnfctl_errcode_t prexstat;
86 tifiocstate_t kstate;
87 int kfd;
88
89 kfd = open(TNFDRIVER, O_RDWR);
90 if (kfd < 0) {
91 return (tnfctl_status_map(errno));
92 }
93 if (ioctl(kfd, TIFIOCGSTATE, &kstate) < 0)
94 return (tnfctl_status_map(errno));
95
96 hdl->kfd = kfd;
97 hdl->kpidfilter_state = kstate.pidfilter_mode;
98 hdl->trace_state = !kstate.trace_stopped;
99 hdl->trace_min_size = KERNEL_MINBUF_SIZE;
100 prexstat = prbk_get_buf_attrs(hdl);
101 if (prexstat)
102 return (prexstat);
103
104 return (TNFCTL_ERR_NONE);
105 }
106
107 /*
108 * Close the TNF control device.
109 */
110 tnfctl_errcode_t
_tnfctl_prbk_close(tnfctl_handle_t * hdl)111 _tnfctl_prbk_close(tnfctl_handle_t *hdl)
112 {
113 if (hdl == NULL)
114 return (TNFCTL_ERR_NONE);
115
116 if (close(hdl->kfd) == -1) {
117 return (tnfctl_status_map(errno));
118 }
119 return (TNFCTL_ERR_NONE);
120 }
121
122 /*
123 * Returns function addresses that can be plugged into function pointers
124 * in kernel probes. These are actually dummy values that get
125 * interpreted by a routine in this file when a probe is flushed.
126 */
127 void
_tnfctl_prbk_get_other_funcs(uintptr_t * allocp,uintptr_t * commitp,uintptr_t * rollbackp,uintptr_t * endp)128 _tnfctl_prbk_get_other_funcs(uintptr_t *allocp, uintptr_t *commitp,
129 uintptr_t *rollbackp, uintptr_t *endp)
130 {
131 *allocp = PRBK_DUMMY_ALLOC;
132 *commitp = (uintptr_t) PRBK_DUMMY_COMMIT;
133 *rollbackp = (uintptr_t) PRBK_DUMMY_ROLLBACK;
134 *endp = PRBK_DUMMY_END;
135 }
136
137
138 /*
139 * Returns test function address
140 */
141 void
_tnfctl_prbk_test_func(uintptr_t * outp)142 _tnfctl_prbk_test_func(uintptr_t *outp)
143 {
144 *outp = (uintptr_t) PRBK_DUMMY_TEST;
145 }
146
147 /*
148 * Allocate a trace buffer. Check for reasonable size; reject if there's
149 * already a buffer.
150 */
151 tnfctl_errcode_t
_tnfctl_prbk_buffer_alloc(tnfctl_handle_t * hdl,int size)152 _tnfctl_prbk_buffer_alloc(tnfctl_handle_t *hdl, int size)
153 {
154 tifiocstate_t bufstat;
155 tnfctl_errcode_t prexstat;
156 int saved_val;
157
158 if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) {
159 return (tnfctl_status_map(errno));
160 }
161 if (bufstat.buffer_state != TIFIOCBUF_NONE) {
162 return (TNFCTL_ERR_BUFEXISTS);
163 }
164 if (size < KERNEL_MINBUF_SIZE) {
165 return (TNFCTL_ERR_SIZETOOSMALL);
166 } else if (size > KERNEL_MAXBUF_SIZE) {
167 /* REMIND: make this an error ? */
168 size = KERNEL_MAXBUF_SIZE;
169 }
170 if (ioctl(hdl->kfd, TIFIOCALLOCBUF, size) < 0) {
171 saved_val = errno;
172 (void) prbk_get_buf_attrs(hdl);
173 return (tnfctl_status_map(saved_val));
174 }
175
176 prexstat = prbk_get_buf_attrs(hdl);
177 if (prexstat)
178 return (prexstat);
179
180 return (TNFCTL_ERR_NONE);
181 }
182
183 /*
184 * Deallocate the kernel's trace buffer.
185 */
186 tnfctl_errcode_t
_tnfctl_prbk_buffer_dealloc(tnfctl_handle_t * hdl)187 _tnfctl_prbk_buffer_dealloc(tnfctl_handle_t *hdl)
188 {
189 tifiocstate_t bufstat;
190 tnfctl_errcode_t prexstat;
191 int saved_val;
192
193 if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) {
194 return (tnfctl_status_map(errno));
195 }
196 if (bufstat.buffer_state == TIFIOCBUF_NONE) {
197 return (TNFCTL_ERR_NOBUF);
198 }
199
200 if (bufstat.buffer_state == TIFIOCBUF_OK && !bufstat.trace_stopped) {
201 return (TNFCTL_ERR_BADDEALLOC);
202 }
203 if (ioctl(hdl->kfd, TIFIOCDEALLOCBUF) < 0) {
204 saved_val = errno;
205 (void) prbk_get_buf_attrs(hdl);
206 return (tnfctl_status_map(saved_val));
207 }
208
209 prexstat = prbk_get_buf_attrs(hdl);
210 if (prexstat)
211 return (prexstat);
212
213 return (TNFCTL_ERR_NONE);
214 }
215
216 /*
217 * Turns kernel global tracing on or off.
218 */
219 tnfctl_errcode_t
_tnfctl_prbk_set_tracing(tnfctl_handle_t * hdl,boolean_t onoff)220 _tnfctl_prbk_set_tracing(tnfctl_handle_t *hdl, boolean_t onoff)
221 {
222 if (hdl->trace_state != onoff &&
223 ioctl(hdl->kfd, TIFIOCSTRACING, onoff) < 0) {
224 if (errno == ENOMEM && onoff)
225 return (TNFCTL_ERR_NOBUF);
226 else
227 return (tnfctl_status_map(errno));
228 }
229 hdl->trace_state = onoff;
230 return (TNFCTL_ERR_NONE);
231 }
232
233 /*
234 * Turn process filter mode on or off. The process filter is maintained
235 * even when process filtering is off, but has no effect: all processes
236 * are traced.
237 */
238 tnfctl_errcode_t
_tnfctl_prbk_set_pfilter_mode(tnfctl_handle_t * hdl,boolean_t onoff)239 _tnfctl_prbk_set_pfilter_mode(tnfctl_handle_t *hdl, boolean_t onoff)
240 {
241 if (hdl->kpidfilter_state != onoff &&
242 ioctl(hdl->kfd, TIFIOCSPIDFILTER, onoff) < 0) {
243 return (tnfctl_status_map(errno));
244 }
245 hdl->kpidfilter_state = onoff;
246 return (TNFCTL_ERR_NONE);
247 }
248
249 /*
250 * Return the process filter list.
251 */
252 tnfctl_errcode_t
_tnfctl_prbk_get_pfilter_list(tnfctl_handle_t * hdl,pid_t ** ret_list_p,int * ret_count)253 _tnfctl_prbk_get_pfilter_list(tnfctl_handle_t *hdl, pid_t **ret_list_p,
254 int *ret_count)
255 {
256 tifiocstate_t kstate;
257 int *filterset;
258 int i;
259 pid_t *ret_list;
260
261 if (ioctl(hdl->kfd, TIFIOCGSTATE, &kstate) < 0)
262 return (tnfctl_status_map(errno));
263
264 if (kstate.pidfilter_size == 0) {
265 *ret_count = 0;
266 *ret_list_p = NULL;
267 return (TNFCTL_ERR_NONE);
268 }
269
270 filterset = (int *) malloc((kstate.pidfilter_size + 1) *
271 sizeof (pid_t));
272 if (filterset == NULL)
273 return (TNFCTL_ERR_ALLOCFAIL);
274 if (ioctl(hdl->kfd, TIFIOCPIDFILTERGET, filterset) < 0)
275 return (tnfctl_status_map(errno));
276
277 /* filterset[0] contains size of array */
278 ret_list = malloc(filterset[0] * sizeof (pid_t));
279 if (ret_list == NULL)
280 return (TNFCTL_ERR_ALLOCFAIL);
281
282 for (i = 1; i <= filterset[0]; ++i)
283 ret_list[i - 1] = filterset[i];
284
285 *ret_count = filterset[0];
286 (void) free(filterset);
287 *ret_list_p = ret_list;
288 return (TNFCTL_ERR_NONE);
289 }
290
291 /*
292 * Add the pid to the process filter list.
293 * check whether it's already in the filter list,
294 * and whether the process exists.
295 */
296 tnfctl_errcode_t
_tnfctl_prbk_pfilter_add(tnfctl_handle_t * hdl,pid_t pid_to_add)297 _tnfctl_prbk_pfilter_add(tnfctl_handle_t *hdl, pid_t pid_to_add)
298 {
299 if (ioctl(hdl->kfd, TIFIOCSPIDON, pid_to_add) < 0) {
300 return (tnfctl_status_map(errno));
301 }
302 return (TNFCTL_ERR_NONE);
303 }
304
305 /*
306 * Drop the pid from the process filter list.
307 */
308 tnfctl_errcode_t
_tnfctl_prbk_pfilter_delete(tnfctl_handle_t * hdl,pid_t pid_to_del)309 _tnfctl_prbk_pfilter_delete(tnfctl_handle_t *hdl, pid_t pid_to_del)
310 {
311 if (ioctl(hdl->kfd, TIFIOCSPIDOFF, pid_to_del) < 0) {
312 if (errno == ESRCH) {
313 return (TNFCTL_ERR_NOPROCESS);
314 } else {
315 return (tnfctl_status_map(errno));
316 }
317 }
318 return (TNFCTL_ERR_NONE);
319 }
320
321 /*
322 * get the buffer attributes - side effect tnfctl handle
323 */
324 static tnfctl_errcode_t
prbk_get_buf_attrs(tnfctl_handle_t * hdl)325 prbk_get_buf_attrs(tnfctl_handle_t *hdl)
326 {
327 tifiocstate_t bufstat;
328
329 if (ioctl(hdl->kfd, TIFIOCGSTATE, &bufstat) < 0) {
330 return (tnfctl_status_map(errno));
331 }
332
333 hdl->trace_file_name = NULL;
334 hdl->trace_buf_size = bufstat.buffer_size;
335 if (bufstat.buffer_state == TIFIOCBUF_NONE)
336 hdl->trace_buf_state = TNFCTL_BUF_NONE;
337 else if (bufstat.buffer_state == TIFIOCBUF_BROKEN)
338 hdl->trace_buf_state = TNFCTL_BUF_BROKEN;
339 else
340 hdl->trace_buf_state = TNFCTL_BUF_OK;
341 return (TNFCTL_ERR_NONE);
342 }
343
344 /*
345 * "Flush" a probe: i.e., sync up the kernel state with the
346 * (desired) state stored in our data structure.
347 */
348 tnfctl_errcode_t
_tnfctl_prbk_flush(tnfctl_handle_t * hndl,prbctlref_t * p)349 _tnfctl_prbk_flush(tnfctl_handle_t *hndl, prbctlref_t *p)
350 {
351 tnf_probevals_t probebuf;
352
353 probebuf.probenum = p->probe_id;
354 probebuf.enabled = (p->wrkprbctl.test_func != NULL);
355 probebuf.traced = (p->wrkprbctl.commit_func == PRBK_DUMMY_COMMIT);
356 if (ioctl(hndl->kfd, TIFIOCSPROBEVALS, &probebuf) < 0)
357 return (tnfctl_status_map(errno));
358 return (TNFCTL_ERR_NONE);
359 }
360
361 /*
362 * Refresh our understanding of the existing probes in the kernel.
363 */
364 tnfctl_errcode_t
_tnfctl_refresh_kernel(tnfctl_handle_t * hndl)365 _tnfctl_refresh_kernel(tnfctl_handle_t *hndl)
366 {
367 int maxprobe, i;
368 int pos;
369 tnfctl_errcode_t prexstat;
370 tnf_probevals_t probebuf;
371 objlist_t *obj_p;
372 prbctlref_t *p = NULL;
373
374 prexstat = prbk_get_buf_attrs(hndl);
375 if (prexstat)
376 return (prexstat);
377 /*
378 * Here is where you'd set obj_p->new to B_FALSE and obj_p->old to
379 * B_TRUE for all existing objects. We currently don't need
380 * it until we get modload/unload working correctly with probes
381 */
382 if (ioctl(hndl->kfd, TIFIOCGMAXPROBE, &maxprobe) < 0)
383 return (tnfctl_status_map(errno));
384 if (maxprobe == hndl->num_probes) {
385 /* XXX Inadequate in the presence of module unloading */
386 return (TNFCTL_ERR_NONE);
387 }
388
389 prexstat = alloc_probe_space(hndl, maxprobe);
390 if (prexstat)
391 return (prexstat);
392
393 NOTE(NO_COMPETING_THREADS_NOW)
394 obj_p = hndl->objlist;
395 NOTE(COMPETING_THREADS_NOW)
396 assert((obj_p != NULL) && (obj_p->probes != NULL));
397
398 for (i = 1; i <= maxprobe; ++i) {
399
400 if (i >= (obj_p->min_probe_num + obj_p->probecnt)) {
401 obj_p = obj_p->next;
402 }
403
404 /* make sure we are in the correct object */
405 assert(obj_p != NULL);
406 assert((i >= obj_p->min_probe_num) &&
407 (i < (obj_p->min_probe_num + obj_p->probecnt)));
408
409 /* get a pointer to correct probe */
410 pos = i - obj_p->min_probe_num;
411 p = &(obj_p->probes[pos]);
412 assert((p != NULL) && (p->probe_id == i) && (p->probe_handle));
413
414 probebuf.probenum = i;
415 if (ioctl(hndl->kfd, TIFIOCGPROBEVALS, &probebuf) < 0) {
416 if (errno == ENOENT) {
417 /*
418 * This probe has vanished due to a module
419 * unload.
420 */
421 p->probe_handle->valid = B_FALSE;
422 } else {
423 return (tnfctl_status_map(errno));
424 }
425 } else {
426 if (p->probe_handle->valid == B_FALSE) {
427 /*
428 * seeing this probe for the first time
429 * (alloc_probe_space() initialized this
430 * "valid" field to B_FALSE)
431 */
432 /* Update our info about this probe */
433 p->wrkprbctl.test_func = (probebuf.enabled) ?
434 PRBK_DUMMY_TEST : NULL;
435 p->wrkprbctl.commit_func = (probebuf.traced) ?
436 PRBK_DUMMY_COMMIT : PRBK_DUMMY_ROLLBACK;
437 p->probe_handle->valid = B_TRUE;
438 if (probebuf.attrsize < sizeof (probebuf))
439 probebuf.attrsize = sizeof (probebuf);
440 p->attr_string = malloc(probebuf.attrsize);
441 if (p->attr_string == NULL)
442 return (TNFCTL_ERR_ALLOCFAIL);
443 /*
444 * NOTE: the next statement is a structure
445 * copy and *not* a pointer assignment
446 */
447 /* LINTED pointer cast may result in improper alignment */
448 *(tnf_probevals_t *) p->attr_string = probebuf;
449 if (ioctl(hndl->kfd, TIFIOCGPROBESTRING,
450 p->attr_string) < 0)
451 return (tnfctl_status_map(errno));
452 if (hndl->create_func) {
453 p->probe_handle->client_registered_data =
454 hndl->create_func(hndl,
455 p->probe_handle);
456 }
457 }
458 }
459 }
460 hndl->num_probes = maxprobe;
461 return (TNFCTL_ERR_NONE);
462 }
463
464 /*
465 * check if there are any new probes in the kernel that we aren't aware of.
466 * If so, allocate space for those probes in our data structure.
467 */
468 static tnfctl_errcode_t
alloc_probe_space(tnfctl_handle_t * hndl,int maxprobe)469 alloc_probe_space(tnfctl_handle_t *hndl, int maxprobe)
470 {
471 objlist_t **o_pp;
472 objlist_t *obj_p, *nobj_p;
473 int min_probe_num, i;
474 prbctlref_t *probe_p;
475
476 /* we know that: hndl->maxprobe != maxprobe */
477 NOTE(NO_COMPETING_THREADS_NOW)
478 obj_p = hndl->objlist;
479 NOTE(COMPETING_THREADS_NOW)
480 if (obj_p == NULL) {
481 /* no objects allocated */
482 o_pp = &(hndl->objlist);
483 min_probe_num = 1;
484 } else {
485 /* find last object */
486 while (obj_p->next != NULL) {
487 /* reset new_probe field on modload/unload */
488 obj_p->new_probe = B_FALSE;
489 obj_p = obj_p->next;
490 }
491 o_pp = &(obj_p->next);
492 min_probe_num = obj_p->min_probe_num + obj_p->probecnt;
493 }
494
495 nobj_p = calloc(1, sizeof (objlist_t));
496 if (nobj_p == NULL)
497 return (TNFCTL_ERR_ALLOCFAIL);
498 /* add to the linked list */
499 *o_pp = nobj_p;
500 /* NULL, B_FALSE, or 0's not explicitly initialized */
501 nobj_p->new_probe = B_TRUE;
502 nobj_p->new = B_TRUE;
503 nobj_p->objfd = -1;
504 nobj_p->min_probe_num = min_probe_num;
505 nobj_p->probecnt = maxprobe - min_probe_num + 1;
506 nobj_p->probes = calloc(nobj_p->probecnt, sizeof (prbctlref_t));
507 if (nobj_p->probes == NULL) {
508 free(nobj_p);
509 return (TNFCTL_ERR_ALLOCFAIL);
510 }
511
512 probe_p = &(nobj_p->probes[0]);
513 for (i = min_probe_num; i <= maxprobe; i++) {
514 NOTE(NO_COMPETING_THREADS_NOW)
515 probe_p->obj = nobj_p;
516 NOTE(COMPETING_THREADS_NOW)
517 probe_p->probe_id = i;
518 probe_p->probe_handle = calloc(1, sizeof (tnfctl_probe_t));
519 if (probe_p->probe_handle == NULL) {
520 if (nobj_p->probes)
521 free(nobj_p->probes);
522 free(nobj_p);
523 return (TNFCTL_ERR_ALLOCFAIL);
524 }
525 probe_p->probe_handle->valid = B_FALSE;
526 probe_p->probe_handle->probe_p = probe_p;
527 /* link in probe handle into chain off tnfctl_handle_t */
528 probe_p->probe_handle->next = hndl->probe_handle_list_head;
529 hndl->probe_handle_list_head = probe_p->probe_handle;
530
531 probe_p++;
532 }
533
534 hndl->num_probes = maxprobe;
535 return (TNFCTL_ERR_NONE);
536 }
537