8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libtnfctl / util.c
blob04d6fabe1be0dc0b3bee463e932e53001b79ca4d
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright (c) 1994, by Sun Microsytems, Inc.
26 #pragma ident "%Z%%M% %I% %E% SMI"
29 * Utility functions to initialize tnfctl handle, find functions that
30 * can be plugged into probes, find trace file information, and create
31 * a trace file for process tracing.
34 #ifndef DEBUG
35 #define NDEBUG 1
36 #endif
38 #include "tnfctl_int.h"
39 #include "dbg.h"
41 #include <assert.h>
42 #include <errno.h>
43 #include <stdio.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <stdlib.h>
47 #include <fcntl.h>
48 #include <sys/param.h>
50 #include "tnf_buf.h"
52 * Defines - Project private interfaces in libtnfprobe.so
55 #define TRACEFILE_NAME "tnf_trace_file_name"
56 #define TRACEFILE_SIZE "tnf_trace_file_size"
57 #define TRACEFILE_MIN "tnf_trace_file_min"
58 #define TRACE_ERROR "_tnfw_b_control"
60 #define TRACE_ALLOC "tnf_trace_alloc"
61 #define TRACE_COMMIT "tnf_trace_commit"
62 #define TRACE_ROLLBACK "tnf_trace_rollback"
63 #define DEBUG_ENTRY "tnf_probe_debug"
65 #define PROBE_LIST_HEAD "__tnf_probe_list_head"
66 #define PROBE_LIST_VALID "__tnf_probe_list_valid"
68 #define NONTHREAD_TEST "tnf_non_threaded_test_addr"
69 #define THREAD_TEST "tnf_threaded_test_addr"
70 #define PROBE_THR_SYNC "__tnf_probe_thr_sync"
72 #define MEMSEG_PTR "__tnf_probe_memseg_p"
74 /* Project private interfaces in libthread.so */
75 #define LIBTHREAD_PRESENT "thr_probe_getfunc_addr"
78 * Local declarations
81 static tnfctl_errcode_t find_test_func(tnfctl_handle_t *hndl);
82 static tnfctl_errcode_t find_target_syms(tnfctl_handle_t *hndl);
83 static tnfctl_errcode_t find_trace_file_info(tnfctl_handle_t *hndl);
84 static tnfctl_errcode_t check_trace_error(tnfctl_handle_t *hndl);
87 * _tnfctl_refresh_process() - search for new shared objects. If any
88 * found, discover probes in new shared objects.
89 * NOT to be called in kernel mode.
92 tnfctl_errcode_t
93 _tnfctl_refresh_process(tnfctl_handle_t *hndl, boolean_t *lmap_ok,
94 enum event_op_t *dl_evt)
96 tnfctl_errcode_t prexstat = TNFCTL_ERR_NONE;
97 boolean_t release_lock;
99 assert(hndl->mode != KERNEL_MODE);
101 /*LINTED statement has no consequent: else*/
102 LOCK(hndl, prexstat, release_lock);
104 prexstat = check_trace_error(hndl);
105 if (prexstat)
106 goto finish_func;
109 * update the link map. caller decides what to do on
110 * inconsistent link map
112 prexstat = _tnfctl_lmap_update(hndl, lmap_ok, dl_evt);
113 if (prexstat)
114 goto finish_func;
116 /* link map is ok now */
117 prexstat = find_test_func(hndl);
118 if (prexstat)
119 goto finish_func;
120 if (*dl_evt != EVT_NONE) {
121 prexstat = _tnfctl_find_all_probes(hndl);
122 if (prexstat)
123 goto finish_func;
126 finish_func:
127 /*LINTED statement has no consequent: else*/
128 UNLOCK(hndl, release_lock);
130 return (prexstat);
134 * initialize tnfctl handle for a new target
136 tnfctl_errcode_t
137 _tnfctl_set_state(tnfctl_handle_t *hndl)
139 tnfctl_errcode_t prexstat = TNFCTL_ERR_NONE;
140 boolean_t lmap_ok;
141 enum event_op_t dl_evt;
142 boolean_t release_lock;
144 hndl->targ_pid = hndl->p_getpid(hndl->proc_p);
146 /*LINTED statement has no consequent: else*/
147 LOCK(hndl, prexstat, release_lock);
150 * initialize the link map table. If link map is not ok, it is an
151 * error.
153 prexstat = _tnfctl_lmap_update(hndl, &lmap_ok, &dl_evt);
154 if (prexstat)
155 goto end_func;
157 /* find the needed target symbols */
158 prexstat = find_target_syms(hndl);
159 if (prexstat) {
160 /* is libtnfprobe.so loaded in target ? */
161 goto end_func;
164 prexstat = find_trace_file_info(hndl);
165 if (prexstat)
166 goto end_func;
168 prexstat = find_test_func(hndl);
169 if (prexstat)
170 goto end_func;
172 prexstat = _tnfctl_find_all_probes(hndl);
173 if (prexstat)
174 goto end_func;
176 prexstat = check_trace_error(hndl);
177 /* fall into end_func */
179 end_func:
180 /*LINTED statement has no consequent: else*/
181 UNLOCK(hndl, release_lock);
183 return (prexstat);
187 * find the test function for a probe. The test function could change
188 * with time, so we have to repeatedly check for the test function to use
190 static tnfctl_errcode_t
191 find_test_func(tnfctl_handle_t *hndl)
193 long thr_sync;
194 int miscstat;
196 if (hndl->mt_target == B_FALSE) {
197 /* no libthread linked in */
198 hndl->testfunc = hndl->nonthread_test;
199 } else {
201 * check whether libthread/libtnfw have synced up.
202 * If not yet synced up, use non-threaded test function
205 /* assume we are going to use threaded test */
206 hndl->testfunc = hndl->thread_test;
207 miscstat = hndl->p_read(hndl->proc_p, hndl->thread_sync,
208 &thr_sync, sizeof (thr_sync));
209 if (miscstat != 0)
210 return (TNFCTL_ERR_INTERNAL);
211 /* if not yet synced up, change test func to non-threaded one */
212 if (thr_sync == 0) {
213 hndl->testfunc = hndl->nonthread_test;
218 * Note: the testfunc in the target can change underneath us because
219 * in an MT program the init section of libthread changes all the
220 * test functions from the non-threaded one to the threaded one.
221 * So, every time we write out a probe, we have to make sure that
222 * we are using the correct test function by not trusting the test
223 * function in our copy of the probe. A more fool-proof solution
224 * which will allow other fields in the probe to change internally
225 * is to refresh every probe on a _tnfctl_refresh_process()
227 return (TNFCTL_ERR_NONE);
231 * check_trace_error() - checks whether there was an error in tracing
232 * side effects trace_buf_state and trace_state in hndl
233 * note: call this function only after trace_file_name is set up
234 * in hndl
236 tnfctl_errcode_t
237 check_trace_error(tnfctl_handle_t *hndl)
239 int miscstat;
240 uintptr_t trace_error_ptr;
241 TNFW_B_CONTROL trace_error_rec;
243 /* read in the value of the control structure pointer */
244 miscstat = hndl->p_read(hndl->proc_p, hndl->trace_error,
245 &trace_error_ptr, sizeof (trace_error_ptr));
246 if (miscstat != 0)
247 return (TNFCTL_ERR_INTERNAL);
249 /* read in the value of the control structure */
250 miscstat = hndl->p_read(hndl->proc_p, trace_error_ptr, &trace_error_rec,
251 sizeof (trace_error_rec));
252 if (miscstat != 0)
253 return (TNFCTL_ERR_INTERNAL);
255 if (trace_error_rec.tnf_state == TNFW_B_NOBUFFER) {
257 * massage into correct state for caller - the target might
258 * not have hit the first probe and hence we got "no buffer".
259 * So, if the user had given a file name, return BUF_OK.
261 if (hndl->trace_file_name == NULL)
262 hndl->trace_buf_state = TNFCTL_BUF_NONE;
263 else
264 hndl->trace_buf_state = TNFCTL_BUF_OK;
265 } else if (trace_error_rec.tnf_state == TNFW_B_BROKEN) {
266 hndl->trace_buf_state = TNFCTL_BUF_BROKEN;
267 } else {
268 hndl->trace_buf_state = TNFCTL_BUF_OK;
271 if (TNFW_B_IS_STOPPED(trace_error_rec.tnf_state))
272 hndl->trace_state = B_FALSE;
273 else
274 hndl->trace_state = B_TRUE;
276 return (TNFCTL_ERR_NONE);
278 } /* end find_alloc_func */
281 * find_target_syms() - finds needed target functions
282 * sideffects allocfunc, commitfunc, endfunc, rollbackfunc in hndl
284 static tnfctl_errcode_t
285 find_target_syms(tnfctl_handle_t *hndl)
287 tnfctl_errcode_t prexstat;
288 uintptr_t temp_addr;
289 int miscstat;
291 prexstat = _tnfctl_sym_find(hndl, TRACE_ALLOC, &hndl->allocfunc);
292 if (prexstat)
293 goto end_of_func;
295 prexstat = _tnfctl_sym_find(hndl, TRACE_COMMIT, &hndl->commitfunc);
296 if (prexstat)
297 goto end_of_func;
299 prexstat = _tnfctl_sym_find(hndl, TRACE_END_FUNC, &hndl->endfunc);
300 if (prexstat)
301 goto end_of_func;
303 prexstat = _tnfctl_sym_find(hndl, TRACE_ROLLBACK, &hndl->rollbackfunc);
304 if (prexstat)
305 goto end_of_func;
307 prexstat = _tnfctl_sym_find(hndl, PROBE_LIST_HEAD,
308 &hndl->probelist_head);
309 if (prexstat)
310 goto end_of_func;
312 prexstat = _tnfctl_sym_find(hndl, TRACE_ERROR, &hndl->trace_error);
313 if (prexstat)
314 goto end_of_func;
316 prexstat = _tnfctl_sym_find(hndl, MEMSEG_PTR, &temp_addr);
317 if (prexstat)
318 goto end_of_func;
320 /* dereference to get the actual address of structure */
321 miscstat = hndl->p_read(hndl->proc_p, temp_addr, &hndl->memseg_p,
322 sizeof (hndl->memseg_p));
323 if (miscstat != 0)
324 return (TNFCTL_ERR_INTERNAL);
326 prexstat = _tnfctl_sym_find(hndl, PROBE_LIST_VALID,
327 &hndl->probelist_valid);
328 if (prexstat)
329 goto end_of_func;
331 prexstat = _tnfctl_sym_find(hndl, NONTHREAD_TEST, &temp_addr);
332 if (prexstat)
333 goto end_of_func;
335 /* dereference to get the actual function address */
336 miscstat = hndl->p_read(hndl->proc_p, temp_addr, &hndl->nonthread_test,
337 sizeof (hndl->nonthread_test));
338 if (miscstat != 0)
339 return (TNFCTL_ERR_INTERNAL);
341 prexstat = _tnfctl_sym_find(hndl, THREAD_TEST, &temp_addr);
342 if (prexstat)
343 goto end_of_func;
345 /* dereference to get the actual function address */
346 miscstat = hndl->p_read(hndl->proc_p, temp_addr, &hndl->thread_test,
347 sizeof (hndl->thread_test));
348 if (miscstat != 0)
349 return (TNFCTL_ERR_INTERNAL);
351 prexstat = _tnfctl_sym_find(hndl, PROBE_THR_SYNC, &hndl->thread_sync);
352 if (prexstat)
353 goto end_of_func;
355 prexstat = _tnfctl_sym_find(hndl, LIBTHREAD_PRESENT, &temp_addr);
356 if (prexstat) {
357 if (prexstat == TNFCTL_ERR_BADARG) {
358 /* no libthread linked in */
359 hndl->mt_target = B_FALSE;
360 /* this is not an error condition */
361 prexstat = TNFCTL_ERR_NONE;
362 } else {
363 return (prexstat);
365 } else {
366 hndl->mt_target = B_TRUE;
369 end_of_func:
370 if (prexstat == TNFCTL_ERR_BADARG)
371 prexstat = TNFCTL_ERR_NOLIBTNFPROBE;
373 return (prexstat);
377 * _tnfctl_create_tracefile() - initializes tracefile, sets the tracefile name
378 * and size
379 * side effects trace_file_name and trace_buf_size in hndl
382 #define ZBUFSZ (64 * 1024)
384 tnfctl_errcode_t
385 _tnfctl_create_tracefile(tnfctl_handle_t *hndl, const char *trace_file_name,
386 uint_t trace_file_size)
388 char *preexisting;
389 tnfctl_errcode_t prexstat;
390 int miscstat;
391 char path[MAXPATHLEN];
392 uintptr_t name_addr, size_addr;
393 uint_t outsize;
394 char zerobuf[ZBUFSZ];
395 int fd, sz, i;
397 /* find the neccessary symbols in the target */
398 prexstat = _tnfctl_sym_find(hndl, TRACEFILE_NAME, &name_addr);
399 if (prexstat) {
400 if (prexstat == TNFCTL_ERR_BADARG)
401 prexstat = TNFCTL_ERR_INTERNAL;
402 return (prexstat);
404 prexstat = _tnfctl_sym_find(hndl, TRACEFILE_SIZE, &size_addr);
405 if (prexstat) {
406 if (prexstat == TNFCTL_ERR_BADARG)
407 prexstat = TNFCTL_ERR_INTERNAL;
408 return (prexstat);
411 /* Double check that a file name doesn't already exist */
412 preexisting = NULL;
413 prexstat = _tnfctl_readstr_targ(hndl, name_addr, &preexisting);
414 if (prexstat) {
415 if (preexisting)
416 free(preexisting);
417 return (prexstat);
420 /* There better not be a file name there yet */
421 assert(preexisting[0] == '\0');
423 /* paranoia - for optimized compilation */
424 if (preexisting[0] != '\0')
425 return (TNFCTL_ERR_BUFEXISTS);
427 /* free memory in preexisting string */
428 if (preexisting)
429 free(preexisting);
431 if (trace_file_size < hndl->trace_min_size) {
432 return (TNFCTL_ERR_SIZETOOSMALL);
435 /* do we have an absolute, relative or no pathname specified? */
436 if (trace_file_name == NULL) {
437 return (TNFCTL_ERR_BADARG);
439 if (trace_file_name[0] == '/') {
440 /* absolute path to tracefile specified */
441 if ((strlen(trace_file_name) + 1) > (size_t) MAXPATHLEN) {
442 /* directory specification too long */
443 return (TNFCTL_ERR_BADARG);
445 (void) strcpy(path, trace_file_name);
446 } else {
447 char *cwd;
449 /* relative path to tracefile specified */
450 cwd = getcwd(NULL, MAXPATHLEN);
451 if (!cwd) {
452 return (tnfctl_status_map(errno));
454 if ((strlen(cwd) + 1 + strlen(trace_file_name) + 1) >
455 (size_t) MAXPATHLEN) {
456 /* path name too long */
457 return (TNFCTL_ERR_BADARG);
459 (void) sprintf(path, "%s/%s", cwd, trace_file_name);
461 free(cwd);
464 outsize = trace_file_size;
466 DBG_TNF_PROBE_2(_tnfctl_create_tracefile_1, "libtnfctl",
467 "sunw%verbosity 1; sunw%debug 'setting trace file name'",
468 tnf_string, tracefile_name, path,
469 tnf_long, tracefile_size, outsize);
471 /* unlink a previous tracefile (if one exists) */
472 (void) unlink(path);
474 /* create the new tracefile */
475 fd = open(path, O_CREAT | O_RDWR | O_TRUNC, 0644);
476 if (fd < 0) {
477 return (tnfctl_status_map(errno));
480 /* zero fill the file */
481 (void) memset(zerobuf, 0, ZBUFSZ);
482 sz = ZBUFSZ;
483 for (i = 0; i < outsize; i += sz) {
484 ulong_t retval;
486 sz = ((outsize - i) > ZBUFSZ) ? ZBUFSZ : (outsize - i);
487 retval = write(fd, zerobuf, sz);
488 if (retval != sz) {
489 /* trouble zeroing tracefile */
490 return (tnfctl_status_map(errno));
494 /* close the file */
495 (void) close(fd);
497 /* write the tracefile name and size into the target process */
498 miscstat = hndl->p_write(hndl->proc_p, name_addr, path,
499 strlen(path) + 1);
500 if (miscstat != 0)
501 return (TNFCTL_ERR_INTERNAL);
502 miscstat = hndl->p_write(hndl->proc_p, size_addr, &outsize,
503 sizeof (outsize));
504 if (miscstat != 0)
505 return (TNFCTL_ERR_INTERNAL);
507 hndl->trace_file_name = strdup(path);
508 if (hndl->trace_file_name == NULL)
509 return (TNFCTL_ERR_ALLOCFAIL);
510 hndl->trace_buf_size = outsize;
511 hndl->trace_buf_state = TNFCTL_BUF_OK;
512 return (TNFCTL_ERR_NONE);
513 } /* end _tnfctl_create_tracefile */
516 * find_trace_file_info()
517 * finds out information about the trace file.
518 * side effects trace_buf_size, trace_min_size, trace_file_name in hndl
521 static tnfctl_errcode_t
522 find_trace_file_info(tnfctl_handle_t *hndl)
524 tnfctl_errcode_t prexstat;
525 int miscstat;
526 char *preexisting;
527 uintptr_t name_addr, size_addr, min_addr;
528 uint_t outsize, minoutsize;
530 /* find the neccessary symbols in the target */
531 prexstat = _tnfctl_sym_find(hndl, TRACEFILE_NAME, &name_addr);
532 if (prexstat) {
533 if (prexstat == TNFCTL_ERR_BADARG)
534 prexstat = TNFCTL_ERR_INTERNAL;
535 return (prexstat);
537 prexstat = _tnfctl_sym_find(hndl, TRACEFILE_SIZE, &size_addr);
538 if (prexstat) {
539 if (prexstat == TNFCTL_ERR_BADARG)
540 prexstat = TNFCTL_ERR_INTERNAL;
541 return (prexstat);
543 prexstat = _tnfctl_sym_find(hndl, TRACEFILE_MIN, &min_addr);
544 if (prexstat) {
545 if (prexstat == TNFCTL_ERR_BADARG)
546 prexstat = TNFCTL_ERR_INTERNAL;
547 return (prexstat);
550 /* read file name */
551 preexisting = NULL;
552 prexstat = _tnfctl_readstr_targ(hndl, name_addr, &preexisting);
553 if (prexstat) {
554 if (preexisting)
555 free(preexisting);
556 return (prexstat);
559 /* read the minimum file size from the target */
560 miscstat = hndl->p_read(hndl->proc_p, min_addr, &minoutsize,
561 sizeof (minoutsize));
562 if (miscstat != 0)
563 return (TNFCTL_ERR_INTERNAL);
564 hndl->trace_min_size = minoutsize;
566 /* if there is no filename, we are done */
567 if (preexisting[0] == '\0') {
568 hndl->trace_file_name = NULL;
569 hndl->trace_buf_size = 0;
570 } else {
571 hndl->trace_file_name = preexisting;
572 /* read size of file */
573 miscstat = hndl->p_read(hndl->proc_p, size_addr,
574 &outsize, sizeof (outsize));
575 if (miscstat != 0)
576 return (TNFCTL_ERR_INTERNAL);
577 hndl->trace_buf_size = outsize;
580 return (TNFCTL_ERR_NONE);
581 } /* end find_trace_file_info */
584 * wrapper functions over native /proc functions implemented by proc
585 * layer
588 _tnfctl_read_targ(void *proc_p, uintptr_t addr, void *buf, size_t size)
590 return (prb_proc_read(proc_p, addr, buf, size));
594 _tnfctl_write_targ(void *proc_p, uintptr_t addr, void *buf, size_t size)
596 return (prb_proc_write(proc_p, addr, buf, size));
600 _tnfctl_loadobj_iter(void *proc_p, tnfctl_ind_obj_f *func, void *client_data)
602 prb_loadobj_f *same_func = (prb_loadobj_f *) func;
604 return (prb_loadobj_iter(proc_p, same_func, client_data));
607 pid_t
608 _tnfctl_pid_get(void *proc_p)
610 return (prb_proc_pid_get(proc_p));
614 * _tnfctl_readstr_targ() - dereferences a string in the target
615 * NOTE: There is a similar routine called prb_proc_readstr()
616 * used by proc layer. It would be better if there was only
617 * one of these functions defined.
620 #define BUFSZ 256
622 tnfctl_errcode_t
623 _tnfctl_readstr_targ(tnfctl_handle_t *hndl, uintptr_t addr, char **outstr_pp)
625 int retstat;
626 int bufsz = BUFSZ;
627 char buffer[BUFSZ + 1];
628 offset_t offset;
629 char *ptr, *orig_ptr;
631 *outstr_pp = NULL;
632 offset = 0;
634 /* allocate an inital return buffer */
635 ptr = (char *) malloc(BUFSZ);
636 if (!ptr) {
637 DBG((void) fprintf(stderr,
638 "_tnfctl_readstr_targ: malloc failed\n"));
639 return (TNFCTL_ERR_ALLOCFAIL);
641 /*LINTED constant in conditional context*/
642 while (1) {
643 int i;
645 /* read a chunk into our buffer */
646 retstat = hndl->p_read(hndl->proc_p, addr + offset, buffer,
647 bufsz);
648 if (retstat != 0) {
651 * if we get into trouble with a large read, try again
652 * with a single byte. Subsequent failiure is real ...
654 if (bufsz > 1) {
655 bufsz = 1;
656 continue;
659 DBG((void) fprintf(stderr,
660 "_tnfctl_readstr_targ: target read failed: \n"));
661 free(ptr);
662 return (TNFCTL_ERR_INTERNAL);
664 /* copy the chracters into the return buffer */
665 for (i = 0; i < bufsz; i++) {
666 char c = buffer[i];
668 ptr[offset + i] = c;
669 if (c == '\0') {
670 /* hooray! we saw the end of the string */
671 *outstr_pp = ptr;
672 return (TNFCTL_ERR_NONE);
676 /* bummer, need to grab another bufsz characters */
677 offset += bufsz;
678 orig_ptr = ptr;
679 ptr = (char *) realloc(ptr, offset + bufsz);
680 if (!ptr) {
681 free(orig_ptr);
682 DBG((void) fprintf(stderr,
683 "_tnfctl_readstr_targ: realloc failed\n"));
684 return (TNFCTL_ERR_ALLOCFAIL);
688 #if defined(lint)
689 return (TNFCTL_ERR_NONE);
690 #endif