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 * 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.
32 */
33
34#ifndef DEBUG
35#define	NDEBUG	1
36#endif
37
38#include "tnfctl_int.h"
39#include "dbg.h"
40
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>
49
50#include "tnf_buf.h"
51/*
52 * Defines - Project private interfaces in libtnfprobe.so
53 */
54
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"
59
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"
64
65#define	PROBE_LIST_HEAD		"__tnf_probe_list_head"
66#define	PROBE_LIST_VALID	"__tnf_probe_list_valid"
67
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"
71
72#define	MEMSEG_PTR		"__tnf_probe_memseg_p"
73
74/* Project private interfaces in libthread.so */
75#define	LIBTHREAD_PRESENT	"thr_probe_getfunc_addr"
76
77/*
78 * Local declarations
79 */
80
81static tnfctl_errcode_t find_test_func(tnfctl_handle_t *hndl);
82static tnfctl_errcode_t find_target_syms(tnfctl_handle_t *hndl);
83static tnfctl_errcode_t find_trace_file_info(tnfctl_handle_t *hndl);
84static tnfctl_errcode_t check_trace_error(tnfctl_handle_t *hndl);
85
86/*
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.
90 */
91
92tnfctl_errcode_t
93_tnfctl_refresh_process(tnfctl_handle_t *hndl, boolean_t *lmap_ok,
94			enum event_op_t *dl_evt)
95{
96	tnfctl_errcode_t	prexstat = TNFCTL_ERR_NONE;
97	boolean_t	release_lock;
98
99	assert(hndl->mode != KERNEL_MODE);
100
101	/*LINTED statement has no consequent: else*/
102	LOCK(hndl, prexstat, release_lock);
103
104	prexstat = check_trace_error(hndl);
105	if (prexstat)
106		goto finish_func;
107
108	/*
109	 * update the link map. caller decides what to do on
110	 * inconsistent link map
111	 */
112	prexstat = _tnfctl_lmap_update(hndl, lmap_ok, dl_evt);
113	if (prexstat)
114		goto finish_func;
115
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;
124	}
125
126finish_func:
127	/*LINTED statement has no consequent: else*/
128	UNLOCK(hndl, release_lock);
129
130	return (prexstat);
131}
132
133/*
134 * initialize tnfctl handle for a new target
135 */
136tnfctl_errcode_t
137_tnfctl_set_state(tnfctl_handle_t *hndl)
138{
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;
143
144	hndl->targ_pid = hndl->p_getpid(hndl->proc_p);
145
146	/*LINTED statement has no consequent: else*/
147	LOCK(hndl, prexstat, release_lock);
148
149	/*
150	 * initialize the link map table. If link map is not ok, it is an
151	 * error.
152	 */
153	prexstat = _tnfctl_lmap_update(hndl, &lmap_ok, &dl_evt);
154	if (prexstat)
155		goto end_func;
156
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;
162	}
163
164	prexstat = find_trace_file_info(hndl);
165	if (prexstat)
166		goto end_func;
167
168	prexstat = find_test_func(hndl);
169	if (prexstat)
170		goto end_func;
171
172	prexstat = _tnfctl_find_all_probes(hndl);
173	if (prexstat)
174		goto end_func;
175
176	prexstat = check_trace_error(hndl);
177	/* fall into end_func */
178
179end_func:
180	/*LINTED statement has no consequent: else*/
181	UNLOCK(hndl, release_lock);
182
183	return (prexstat);
184}
185
186/*
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
189 */
190static tnfctl_errcode_t
191find_test_func(tnfctl_handle_t *hndl)
192{
193	long		thr_sync;
194	int		miscstat;
195
196	if (hndl->mt_target == B_FALSE) {
197		/* no libthread linked in */
198		hndl->testfunc = hndl->nonthread_test;
199	} else {
200		/*
201		 * check whether libthread/libtnfw have synced up.
202		 * If not yet synced up, use non-threaded test function
203		 */
204
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;
214		}
215	}
216
217	/*
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()
226	 */
227	return (TNFCTL_ERR_NONE);
228}
229
230/*
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
235 */
236tnfctl_errcode_t
237check_trace_error(tnfctl_handle_t *hndl)
238{
239	int		miscstat;
240	uintptr_t	trace_error_ptr;
241	TNFW_B_CONTROL	trace_error_rec;
242
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);
248
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);
254
255	if (trace_error_rec.tnf_state == TNFW_B_NOBUFFER) {
256		/*
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.
260		 */
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;
269	}
270
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;
275
276	return (TNFCTL_ERR_NONE);
277
278}				/* end find_alloc_func */
279
280/*
281 * find_target_syms() - finds needed target functions
282 * 	sideffects allocfunc, commitfunc, endfunc, rollbackfunc in hndl
283 */
284static tnfctl_errcode_t
285find_target_syms(tnfctl_handle_t *hndl)
286{
287	tnfctl_errcode_t	prexstat;
288	uintptr_t		temp_addr;
289	int			miscstat;
290
291	prexstat = _tnfctl_sym_find(hndl, TRACE_ALLOC, &hndl->allocfunc);
292	if (prexstat)
293		goto end_of_func;
294
295	prexstat = _tnfctl_sym_find(hndl, TRACE_COMMIT, &hndl->commitfunc);
296	if (prexstat)
297		goto end_of_func;
298
299	prexstat = _tnfctl_sym_find(hndl, TRACE_END_FUNC, &hndl->endfunc);
300	if (prexstat)
301		goto end_of_func;
302
303	prexstat = _tnfctl_sym_find(hndl, TRACE_ROLLBACK, &hndl->rollbackfunc);
304	if (prexstat)
305		goto end_of_func;
306
307	prexstat = _tnfctl_sym_find(hndl, PROBE_LIST_HEAD,
308					&hndl->probelist_head);
309	if (prexstat)
310		goto end_of_func;
311
312	prexstat = _tnfctl_sym_find(hndl, TRACE_ERROR, &hndl->trace_error);
313	if (prexstat)
314		goto end_of_func;
315
316	prexstat = _tnfctl_sym_find(hndl, MEMSEG_PTR, &temp_addr);
317	if (prexstat)
318		goto end_of_func;
319
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);
325
326	prexstat = _tnfctl_sym_find(hndl, PROBE_LIST_VALID,
327					&hndl->probelist_valid);
328	if (prexstat)
329		goto end_of_func;
330
331	prexstat = _tnfctl_sym_find(hndl, NONTHREAD_TEST, &temp_addr);
332	if (prexstat)
333		goto end_of_func;
334
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);
340
341	prexstat = _tnfctl_sym_find(hndl, THREAD_TEST, &temp_addr);
342	if (prexstat)
343		goto end_of_func;
344
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);
350
351	prexstat = _tnfctl_sym_find(hndl, PROBE_THR_SYNC, &hndl->thread_sync);
352	if (prexstat)
353		goto end_of_func;
354
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);
364		}
365	} else {
366		hndl->mt_target = B_TRUE;
367	}
368
369end_of_func:
370	if (prexstat == TNFCTL_ERR_BADARG)
371		prexstat = TNFCTL_ERR_NOLIBTNFPROBE;
372
373	return (prexstat);
374}
375
376/*
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
380 */
381
382#define	ZBUFSZ		(64 * 1024)
383
384tnfctl_errcode_t
385_tnfctl_create_tracefile(tnfctl_handle_t *hndl, const char *trace_file_name,
386			uint_t trace_file_size)
387{
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;
396
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);
403	}
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);
409	}
410
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);
418	}
419
420	/* There better not be a file name there yet */
421	assert(preexisting[0] == '\0');
422
423	/* paranoia - for optimized compilation */
424	if (preexisting[0] != '\0')
425		return (TNFCTL_ERR_BUFEXISTS);
426
427	/* free memory in preexisting string */
428	if (preexisting)
429		free(preexisting);
430
431	if (trace_file_size < hndl->trace_min_size) {
432		return (TNFCTL_ERR_SIZETOOSMALL);
433	}
434
435	/* do we have an absolute, relative or no pathname specified? */
436	if (trace_file_name == NULL) {
437		return (TNFCTL_ERR_BADARG);
438	}
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);
444		}
445		(void) strcpy(path, trace_file_name);
446	} else {
447		char		   *cwd;
448
449		/* relative path to tracefile specified */
450		cwd = getcwd(NULL, MAXPATHLEN);
451		if (!cwd) {
452			return (tnfctl_status_map(errno));
453		}
454		if ((strlen(cwd) + 1 + strlen(trace_file_name) + 1) >
455			(size_t) MAXPATHLEN) {
456			/* path name too long */
457			return (TNFCTL_ERR_BADARG);
458		}
459		(void) sprintf(path, "%s/%s", cwd, trace_file_name);
460
461		free(cwd);
462	}
463
464	outsize = trace_file_size;
465
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);
470
471	/* unlink a previous tracefile (if one exists) */
472	(void) unlink(path);
473
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));
478	}
479
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;
485
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));
491		}
492	}
493
494	/* close the file */
495	(void) close(fd);
496
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);
506
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 */
514
515/*
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
519 */
520
521static tnfctl_errcode_t
522find_trace_file_info(tnfctl_handle_t *hndl)
523{
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;
529
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);
536	}
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);
542	}
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);
548	}
549
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);
557	}
558
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;
565
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;
578	}
579
580	return (TNFCTL_ERR_NONE);
581}				/* end find_trace_file_info */
582
583/*
584 * wrapper functions over native /proc functions implemented by proc
585 * layer
586 */
587int
588_tnfctl_read_targ(void *proc_p, uintptr_t addr, void *buf, size_t size)
589{
590	return (prb_proc_read(proc_p, addr, buf, size));
591}
592
593int
594_tnfctl_write_targ(void *proc_p, uintptr_t addr, void *buf, size_t size)
595{
596	return (prb_proc_write(proc_p, addr, buf, size));
597}
598
599int
600_tnfctl_loadobj_iter(void *proc_p, tnfctl_ind_obj_f *func, void *client_data)
601{
602	prb_loadobj_f *same_func = (prb_loadobj_f *) func;
603
604	return (prb_loadobj_iter(proc_p, same_func, client_data));
605}
606
607pid_t
608_tnfctl_pid_get(void *proc_p)
609{
610	return (prb_proc_pid_get(proc_p));
611}
612
613/*
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.
618 */
619
620#define	BUFSZ	256
621
622tnfctl_errcode_t
623_tnfctl_readstr_targ(tnfctl_handle_t *hndl, uintptr_t addr, char **outstr_pp)
624{
625	int		retstat;
626	int		bufsz = BUFSZ;
627	char		buffer[BUFSZ + 1];
628	offset_t	offset;
629	char		*ptr, *orig_ptr;
630
631	*outstr_pp = NULL;
632	offset = 0;
633
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);
640	}
641	/*LINTED constant in conditional context*/
642	while (1) {
643		int			 i;
644
645		/* read a chunk into our buffer */
646		retstat = hndl->p_read(hndl->proc_p, addr + offset, buffer,
647								bufsz);
648		if (retstat != 0) {
649
650			/*
651			 * if we get into trouble with a large read, try again
652			 * with a single byte.  Subsequent failiure is real ...
653			 */
654			if (bufsz > 1) {
655				bufsz = 1;
656				continue;
657			}
658
659			DBG((void) fprintf(stderr,
660			    "_tnfctl_readstr_targ: target read failed: \n"));
661			free(ptr);
662			return (TNFCTL_ERR_INTERNAL);
663		}
664		/* copy the chracters into the return buffer */
665		for (i = 0; i < bufsz; i++) {
666			char			c = buffer[i];
667
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);
673			}
674		}
675
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);
685		}
686	}
687
688#if defined(lint)
689	return (TNFCTL_ERR_NONE);
690#endif
691
692}
693