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
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]
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.
38 #include "tnfctl_int.h"
48 #include <sys/param.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"
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.
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
);
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
);
116 /* link map is ok now */
117 prexstat
= find_test_func(hndl
);
120 if (*dl_evt
!= EVT_NONE
) {
121 prexstat
= _tnfctl_find_all_probes(hndl
);
127 /*LINTED statement has no consequent: else*/
128 UNLOCK(hndl
, release_lock
);
134 * initialize tnfctl handle for a new target
137 _tnfctl_set_state(tnfctl_handle_t
*hndl
)
139 tnfctl_errcode_t prexstat
= TNFCTL_ERR_NONE
;
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
153 prexstat
= _tnfctl_lmap_update(hndl
, &lmap_ok
, &dl_evt
);
157 /* find the needed target symbols */
158 prexstat
= find_target_syms(hndl
);
160 /* is libtnfprobe.so loaded in target ? */
164 prexstat
= find_trace_file_info(hndl
);
168 prexstat
= find_test_func(hndl
);
172 prexstat
= _tnfctl_find_all_probes(hndl
);
176 prexstat
= check_trace_error(hndl
);
177 /* fall into end_func */
180 /*LINTED statement has no consequent: else*/
181 UNLOCK(hndl
, release_lock
);
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
)
196 if (hndl
->mt_target
== B_FALSE
) {
197 /* no libthread linked in */
198 hndl
->testfunc
= hndl
->nonthread_test
;
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
));
210 return (TNFCTL_ERR_INTERNAL
);
211 /* if not yet synced up, change test func to non-threaded one */
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
237 check_trace_error(tnfctl_handle_t
*hndl
)
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
));
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
));
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
;
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
;
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
;
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
;
291 prexstat
= _tnfctl_sym_find(hndl
, TRACE_ALLOC
, &hndl
->allocfunc
);
295 prexstat
= _tnfctl_sym_find(hndl
, TRACE_COMMIT
, &hndl
->commitfunc
);
299 prexstat
= _tnfctl_sym_find(hndl
, TRACE_END_FUNC
, &hndl
->endfunc
);
303 prexstat
= _tnfctl_sym_find(hndl
, TRACE_ROLLBACK
, &hndl
->rollbackfunc
);
307 prexstat
= _tnfctl_sym_find(hndl
, PROBE_LIST_HEAD
,
308 &hndl
->probelist_head
);
312 prexstat
= _tnfctl_sym_find(hndl
, TRACE_ERROR
, &hndl
->trace_error
);
316 prexstat
= _tnfctl_sym_find(hndl
, MEMSEG_PTR
, &temp_addr
);
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
));
324 return (TNFCTL_ERR_INTERNAL
);
326 prexstat
= _tnfctl_sym_find(hndl
, PROBE_LIST_VALID
,
327 &hndl
->probelist_valid
);
331 prexstat
= _tnfctl_sym_find(hndl
, NONTHREAD_TEST
, &temp_addr
);
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
));
339 return (TNFCTL_ERR_INTERNAL
);
341 prexstat
= _tnfctl_sym_find(hndl
, THREAD_TEST
, &temp_addr
);
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
));
349 return (TNFCTL_ERR_INTERNAL
);
351 prexstat
= _tnfctl_sym_find(hndl
, PROBE_THR_SYNC
, &hndl
->thread_sync
);
355 prexstat
= _tnfctl_sym_find(hndl
, LIBTHREAD_PRESENT
, &temp_addr
);
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
;
366 hndl
->mt_target
= B_TRUE
;
370 if (prexstat
== TNFCTL_ERR_BADARG
)
371 prexstat
= TNFCTL_ERR_NOLIBTNFPROBE
;
377 * _tnfctl_create_tracefile() - initializes tracefile, sets the tracefile name
379 * side effects trace_file_name and trace_buf_size in hndl
382 #define ZBUFSZ (64 * 1024)
385 _tnfctl_create_tracefile(tnfctl_handle_t
*hndl
, const char *trace_file_name
,
386 uint_t trace_file_size
)
389 tnfctl_errcode_t prexstat
;
391 char path
[MAXPATHLEN
];
392 uintptr_t name_addr
, size_addr
;
394 char zerobuf
[ZBUFSZ
];
397 /* find the neccessary symbols in the target */
398 prexstat
= _tnfctl_sym_find(hndl
, TRACEFILE_NAME
, &name_addr
);
400 if (prexstat
== TNFCTL_ERR_BADARG
)
401 prexstat
= TNFCTL_ERR_INTERNAL
;
404 prexstat
= _tnfctl_sym_find(hndl
, TRACEFILE_SIZE
, &size_addr
);
406 if (prexstat
== TNFCTL_ERR_BADARG
)
407 prexstat
= TNFCTL_ERR_INTERNAL
;
411 /* Double check that a file name doesn't already exist */
413 prexstat
= _tnfctl_readstr_targ(hndl
, name_addr
, &preexisting
);
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 */
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
);
449 /* relative path to tracefile specified */
450 cwd
= getcwd(NULL
, MAXPATHLEN
);
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
);
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) */
474 /* create the new tracefile */
475 fd
= open(path
, O_CREAT
| O_RDWR
| O_TRUNC
, 0644);
477 return (tnfctl_status_map(errno
));
480 /* zero fill the file */
481 (void) memset(zerobuf
, 0, ZBUFSZ
);
483 for (i
= 0; i
< outsize
; i
+= sz
) {
486 sz
= ((outsize
- i
) > ZBUFSZ
) ? ZBUFSZ
: (outsize
- i
);
487 retval
= write(fd
, zerobuf
, sz
);
489 /* trouble zeroing tracefile */
490 return (tnfctl_status_map(errno
));
497 /* write the tracefile name and size into the target process */
498 miscstat
= hndl
->p_write(hndl
->proc_p
, name_addr
, path
,
501 return (TNFCTL_ERR_INTERNAL
);
502 miscstat
= hndl
->p_write(hndl
->proc_p
, size_addr
, &outsize
,
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
;
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
);
533 if (prexstat
== TNFCTL_ERR_BADARG
)
534 prexstat
= TNFCTL_ERR_INTERNAL
;
537 prexstat
= _tnfctl_sym_find(hndl
, TRACEFILE_SIZE
, &size_addr
);
539 if (prexstat
== TNFCTL_ERR_BADARG
)
540 prexstat
= TNFCTL_ERR_INTERNAL
;
543 prexstat
= _tnfctl_sym_find(hndl
, TRACEFILE_MIN
, &min_addr
);
545 if (prexstat
== TNFCTL_ERR_BADARG
)
546 prexstat
= TNFCTL_ERR_INTERNAL
;
552 prexstat
= _tnfctl_readstr_targ(hndl
, name_addr
, &preexisting
);
559 /* read the minimum file size from the target */
560 miscstat
= hndl
->p_read(hndl
->proc_p
, min_addr
, &minoutsize
,
561 sizeof (minoutsize
));
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;
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
));
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
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
));
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.
623 _tnfctl_readstr_targ(tnfctl_handle_t
*hndl
, uintptr_t addr
, char **outstr_pp
)
627 char buffer
[BUFSZ
+ 1];
629 char *ptr
, *orig_ptr
;
634 /* allocate an inital return buffer */
635 ptr
= (char *) malloc(BUFSZ
);
637 DBG((void) fprintf(stderr
,
638 "_tnfctl_readstr_targ: malloc failed\n"));
639 return (TNFCTL_ERR_ALLOCFAIL
);
641 /*LINTED constant in conditional context*/
645 /* read a chunk into our buffer */
646 retstat
= hndl
->p_read(hndl
->proc_p
, addr
+ offset
, buffer
,
651 * if we get into trouble with a large read, try again
652 * with a single byte. Subsequent failiure is real ...
659 DBG((void) fprintf(stderr
,
660 "_tnfctl_readstr_targ: target read failed: \n"));
662 return (TNFCTL_ERR_INTERNAL
);
664 /* copy the chracters into the return buffer */
665 for (i
= 0; i
< bufsz
; i
++) {
670 /* hooray! we saw the end of the string */
672 return (TNFCTL_ERR_NONE
);
676 /* bummer, need to grab another bufsz characters */
679 ptr
= (char *) realloc(ptr
, offset
+ bufsz
);
682 DBG((void) fprintf(stderr
,
683 "_tnfctl_readstr_targ: realloc failed\n"));
684 return (TNFCTL_ERR_ALLOCFAIL
);
689 return (TNFCTL_ERR_NONE
);