xref: /titanic_41/usr/src/cmd/ptools/pstack/pstack.c (revision fd9cb95cbb2f626355a60efb9d02c5f0a33c10e6)
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 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/isa_defs.h>
30 
31 #include <stdio.h>
32 #include <fcntl.h>
33 #include <ctype.h>
34 #include <string.h>
35 #include <signal.h>
36 #include <dirent.h>
37 #include <errno.h>
38 #include <stdlib.h>
39 #include <stdarg.h>
40 #include <unistd.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <sys/stack.h>
44 #include <link.h>
45 #include <limits.h>
46 #include <libelf.h>
47 #include <thread_db.h>
48 #include <libproc.h>
49 #include <setjmp.h>
50 
51 static	char	*command;
52 static	int	Fflag;
53 static	int	is64;
54 static	GElf_Sym sigh;
55 
56 /*
57  * To keep the list of user-level threads for a multithreaded process.
58  */
59 struct threadinfo {
60 	struct threadinfo *next;
61 	id_t	threadid;
62 	id_t	lwpid;
63 	td_thr_state_e state;
64 	uintptr_t startfunc;
65 	uintptr_t exitval;
66 	prgregset_t regs;
67 };
68 
69 static struct threadinfo *thr_head, *thr_tail;
70 
71 #define	TRUE	1
72 #define	FALSE	0
73 
74 #define	MAX_ARGS	8
75 
76 /*
77  * To support debugging java programs, we display java frames within a stack.
78  * The logic to walk the java frames is contained in libjvm_db.so, which is
79  * found in the same directory as libjvm.so, linked with the program.  If we are
80  * debugging a 32-bit app with a 64-binary, then the debugging library is found
81  * in the '64' subdirectory.  If we find libjvm_db.so, then we fill in these
82  * stub routines.
83  */
84 typedef struct jvm_agent jvm_agent_t;
85 typedef int java_stack_f(void *, prgregset_t, const char *, int, int, void *);
86 
87 /*
88  * The j_agent_create function takes a version parameter.  This ensures that the
89  * interface can evolve appropriately.
90  */
91 #define	JVM_DB_VERSION	1
92 static void *libjvm;
93 typedef jvm_agent_t *(*j_agent_create_f)(struct ps_prochandle *, int);
94 typedef void (*j_agent_destroy_f)(jvm_agent_t *);
95 typedef int (*j_frame_iter_f)(jvm_agent_t *, prgregset_t, java_stack_f *,
96     void *);
97 
98 static j_agent_create_f j_agent_create;
99 static j_agent_destroy_f j_agent_destroy;
100 static j_frame_iter_f j_frame_iter;
101 
102 static jvm_agent_t *load_libjvm(struct ps_prochandle *P);
103 static void reset_libjvm(jvm_agent_t *);
104 
105 /*
106  * Since we must maintain both a proc handle and a jvm handle, this structure
107  * is the basic type that gets passed around.
108  */
109 typedef struct pstack_handle {
110 	struct ps_prochandle *proc;
111 	jvm_agent_t *jvm;
112 	int ignore_frame;
113 	const char *lwps;
114 	int count;
115 } pstack_handle_t;
116 
117 static	int	thr_stack(const td_thrhandle_t *, void *);
118 static	void	free_threadinfo(void);
119 static	struct threadinfo *find_thread(id_t);
120 static	int	all_call_stacks(pstack_handle_t *, int);
121 static	void	tlhead(id_t, id_t);
122 static	int	print_frame(void *, prgregset_t, uint_t, const long *);
123 static	void	print_zombie(struct ps_prochandle *, struct threadinfo *);
124 static	void	print_syscall(const lwpstatus_t *, prgregset_t);
125 static	void	call_stack(pstack_handle_t *, const lwpstatus_t *);
126 
127 /*
128  * The number of active and zombie threads.
129  */
130 static	int	nthreads;
131 
132 int
133 main(int argc, char **argv)
134 {
135 	int retc = 0;
136 	int opt;
137 	int errflg = FALSE;
138 	core_content_t content = CC_CONTENT_DATA | CC_CONTENT_ANON |
139 	    CC_CONTENT_STACK;
140 	struct rlimit rlim;
141 
142 	if ((command = strrchr(argv[0], '/')) != NULL)
143 		command++;
144 	else
145 		command = argv[0];
146 
147 	/* options */
148 	while ((opt = getopt(argc, argv, "F")) != EOF) {
149 		switch (opt) {
150 		case 'F':
151 			/*
152 			 * If the user specifies the force option, we'll
153 			 * consent to printing out other threads' stacks
154 			 * even if the main stack is absent.
155 			 */
156 			content &= ~CC_CONTENT_STACK;
157 			Fflag = PGRAB_FORCE;
158 			break;
159 		default:
160 			errflg = TRUE;
161 			break;
162 		}
163 	}
164 
165 	argc -= optind;
166 	argv += optind;
167 
168 	if (errflg || argc <= 0) {
169 		(void) fprintf(stderr,
170 		    "usage:\t%s [-F] { pid | core }[/lwps] ...\n", command);
171 		(void) fprintf(stderr, "  (show process call stack)\n");
172 		(void) fprintf(stderr,
173 			"  -F: force grabbing of the target process\n");
174 		exit(2);
175 	}
176 
177 	/*
178 	 * Make sure we'll have enough file descriptors to handle a target
179 	 * that has many many mappings.
180 	 */
181 	if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
182 		rlim.rlim_cur = rlim.rlim_max;
183 		(void) setrlimit(RLIMIT_NOFILE, &rlim);
184 	}
185 
186 	(void) proc_initstdio();
187 
188 	while (--argc >= 0) {
189 		int gcode;
190 		psinfo_t psinfo;
191 		const psinfo_t *tpsinfo;
192 		struct ps_prochandle *Pr = NULL;
193 		td_thragent_t *Tap;
194 		int threaded;
195 		pstack_handle_t handle;
196 		const char *lwps, *arg;
197 
198 		(void) proc_flushstdio();
199 
200 		arg = *argv++;
201 
202 		if ((Pr = proc_arg_xgrab(arg, NULL, PR_ARG_ANY,
203 		    Fflag, &gcode, &lwps)) == NULL) {
204 			(void) fprintf(stderr, "%s: cannot examine %s: %s\n",
205 			    command, arg, Pgrab_error(gcode));
206 			retc++;
207 			continue;
208 		}
209 
210 		if ((tpsinfo = Ppsinfo(Pr)) == NULL) {
211 			(void) fprintf(stderr, "%s: cannot examine %s: "
212 			    "lost control of process\n", command, arg);
213 			Prelease(Pr, 0);
214 			retc++;
215 			continue;
216 		}
217 		(void) memcpy(&psinfo, tpsinfo, sizeof (psinfo_t));
218 		proc_unctrl_psinfo(&psinfo);
219 
220 		if (Pstate(Pr) == PS_DEAD) {
221 			if ((Pcontent(Pr) & content) != content) {
222 				(void) fprintf(stderr, "%s: core '%s' has "
223 				    "insufficient content\n", command, arg);
224 				retc++;
225 				continue;
226 			}
227 			(void) printf("core '%s' of %d:\t%.70s\n",
228 			    arg, (int)psinfo.pr_pid, psinfo.pr_psargs);
229 		} else {
230 			(void) printf("%d:\t%.70s\n",
231 			    (int)psinfo.pr_pid, psinfo.pr_psargs);
232 		}
233 
234 		is64 = (psinfo.pr_dmodel == PR_MODEL_LP64);
235 
236 		if (Pgetauxval(Pr, AT_BASE) != -1L && Prd_agent(Pr) == NULL) {
237 			(void) fprintf(stderr, "%s: warning: librtld_db failed "
238 			    "to initialize; symbols from shared libraries will "
239 			    "not be available\n", command);
240 		}
241 
242 		/*
243 		 * First we need to get a thread agent handle.
244 		 */
245 		if (td_init() != TD_OK ||
246 		    td_ta_new(Pr, &Tap) != TD_OK)	/* no libc */
247 			threaded = FALSE;
248 		else {
249 			/*
250 			 * Iterate over all threads, calling:
251 			 *   thr_stack(td_thrhandle_t *Thp, NULL);
252 			 * for each one to generate the list of threads.
253 			 */
254 			nthreads = 0;
255 			(void) td_ta_thr_iter(Tap, thr_stack, NULL,
256 			    TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
257 			    TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
258 
259 			(void) td_ta_delete(Tap);
260 			threaded = TRUE;
261 		}
262 
263 		handle.proc = Pr;
264 		handle.jvm = load_libjvm(Pr);
265 		handle.lwps = lwps;
266 		handle.count = 0;
267 
268 		if (all_call_stacks(&handle, threaded) != 0)
269 			retc++;
270 		if (threaded)
271 			free_threadinfo();
272 
273 		reset_libjvm(handle.jvm);
274 		Prelease(Pr, 0);
275 
276 		if (handle.count == 0)
277 			(void) fprintf(stderr, "%s: no matching LWPs found\n",
278 			    command);
279 	}
280 
281 	(void) proc_finistdio();
282 
283 	return (retc);
284 }
285 
286 /*
287  * Thread iteration call-back function.
288  * Called once for each user-level thread.
289  * Used to build the list of all threads.
290  */
291 /* ARGSUSED1 */
292 static int
293 thr_stack(const td_thrhandle_t *Thp, void *cd)
294 {
295 	td_thrinfo_t thrinfo;
296 	struct threadinfo *tip;
297 	td_err_e error;
298 
299 	if (td_thr_get_info(Thp, &thrinfo) != TD_OK)
300 		return (0);
301 
302 	tip = malloc(sizeof (struct threadinfo));
303 	tip->next = NULL;
304 	tip->threadid = thrinfo.ti_tid;
305 	tip->lwpid = thrinfo.ti_lid;
306 	tip->state = thrinfo.ti_state;
307 	tip->startfunc = thrinfo.ti_startfunc;
308 	tip->exitval = (uintptr_t)thrinfo.ti_exitval;
309 	nthreads++;
310 
311 	if (thrinfo.ti_state == TD_THR_ZOMBIE ||
312 	    ((error = td_thr_getgregs(Thp, tip->regs)) != TD_OK &&
313 	    error != TD_PARTIALREG))
314 		(void) memset(tip->regs, 0, sizeof (prgregset_t));
315 
316 	if (thr_tail)
317 		thr_tail->next = tip;
318 	else
319 		thr_head = tip;
320 	thr_tail = tip;
321 
322 	return (0);
323 }
324 
325 static void
326 free_threadinfo()
327 {
328 	struct threadinfo *tip = thr_head;
329 	struct threadinfo *next;
330 
331 	while (tip) {
332 		next = tip->next;
333 		free(tip);
334 		tip = next;
335 	}
336 
337 	thr_head = thr_tail = NULL;
338 }
339 
340 /*
341  * Find and eliminate the thread corresponding to the given lwpid.
342  */
343 static struct threadinfo *
344 find_thread(id_t lwpid)
345 {
346 	struct threadinfo *tip;
347 
348 	for (tip = thr_head; tip; tip = tip->next) {
349 		if (lwpid == tip->lwpid) {
350 			tip->lwpid = 0;
351 			return (tip);
352 		}
353 	}
354 	return (NULL);
355 }
356 
357 static int
358 thread_call_stack(void *data, const lwpstatus_t *psp,
359     const lwpsinfo_t *pip)
360 {
361 	pstack_handle_t *h = data;
362 	lwpstatus_t lwpstatus;
363 	struct threadinfo *tip;
364 
365 	if (!proc_lwp_in_set(h->lwps, pip->pr_lwpid))
366 		return (0);
367 	h->count++;
368 
369 	if ((tip = find_thread(pip->pr_lwpid)) == NULL)
370 		return (0);
371 
372 	tlhead(tip->threadid, pip->pr_lwpid);
373 	tip->threadid = 0;	/* finish eliminating tid */
374 	if (psp)
375 		call_stack(h, psp);
376 	else {
377 		if (tip->state == TD_THR_ZOMBIE)
378 			print_zombie(h->proc, tip);
379 		else {
380 			(void) memset(&lwpstatus, 0, sizeof (lwpstatus));
381 			(void) memcpy(lwpstatus.pr_reg, tip->regs,
382 				sizeof (prgregset_t));
383 			call_stack(h, &lwpstatus);
384 		}
385 	}
386 	return (0);
387 }
388 
389 static int
390 lwp_call_stack(void *data,
391 	const lwpstatus_t *psp, const lwpsinfo_t *pip)
392 {
393 	pstack_handle_t *h = data;
394 
395 	if (!proc_lwp_in_set(h->lwps, pip->pr_lwpid))
396 		return (0);
397 	h->count++;
398 
399 	tlhead(0, pip->pr_lwpid);
400 	if (psp)
401 		call_stack(h, psp);
402 	else
403 		(void) printf("\t** zombie "
404 			"(exited, not detached, not yet joined) **\n");
405 	return (0);
406 }
407 
408 static int
409 all_call_stacks(pstack_handle_t *h, int dothreads)
410 {
411 	struct ps_prochandle *Pr = h->proc;
412 	pstatus_t status = *Pstatus(Pr);
413 
414 	(void) memset(&sigh, 0, sizeof (GElf_Sym));
415 	(void) Plookup_by_name(Pr, "libc.so", "sigacthandler", &sigh);
416 
417 	if ((status.pr_nlwp + status.pr_nzomb) <= 1 &&
418 	    !(dothreads && nthreads > 1)) {
419 		if (proc_lwp_in_set(h->lwps, status.pr_lwp.pr_lwpid)) {
420 			call_stack(h, &status.pr_lwp);
421 			h->count++;
422 		}
423 	} else {
424 		lwpstatus_t lwpstatus;
425 		struct threadinfo *tip;
426 		id_t tid;
427 
428 		if (dothreads)
429 			(void) Plwp_iter_all(Pr, thread_call_stack, h);
430 		else
431 			(void) Plwp_iter_all(Pr, lwp_call_stack, h);
432 
433 		/* for each remaining thread w/o an lwp */
434 		(void) memset(&lwpstatus, 0, sizeof (lwpstatus));
435 		for (tip = thr_head; tip; tip = tip->next) {
436 
437 			if (!proc_lwp_in_set(h->lwps, tip->lwpid))
438 				tip->threadid = 0;
439 
440 			if ((tid = tip->threadid) != 0) {
441 				(void) memcpy(lwpstatus.pr_reg, tip->regs,
442 					sizeof (prgregset_t));
443 				tlhead(tid, tip->lwpid);
444 				if (tip->state == TD_THR_ZOMBIE)
445 					print_zombie(Pr, tip);
446 				else
447 					call_stack(h, &lwpstatus);
448 			}
449 			tip->threadid = 0;
450 			tip->lwpid = 0;
451 		}
452 	}
453 	return (0);
454 }
455 
456 static void
457 tlhead(id_t threadid, id_t lwpid)
458 {
459 	if (threadid == 0 && lwpid == 0)
460 		return;
461 
462 	(void) printf("-----------------");
463 
464 	if (threadid && lwpid)
465 		(void) printf("  lwp# %d / thread# %d  ",
466 			(int)lwpid, (int)threadid);
467 	else if (threadid)
468 		(void) printf("---------  thread# %d  ", (int)threadid);
469 	else if (lwpid)
470 		(void) printf("  lwp# %d  ------------", (int)lwpid);
471 
472 	(void) printf("--------------------\n");
473 }
474 
475 /*ARGSUSED*/
476 static int
477 print_java_frame(void *cld, prgregset_t gregs, const char *name, int bci,
478     int line, void *handle)
479 {
480 	int length = (is64 ? 16 : 8);
481 
482 	(void) printf(" %.*lx * %s", length, (long)gregs[R_PC], name);
483 
484 	if (bci != -1) {
485 		(void) printf("+%d", bci);
486 		if (line)
487 			(void) printf(" (line %d)", line);
488 	}
489 	(void) printf("\n");
490 
491 	return (0);
492 }
493 
494 static sigjmp_buf jumpbuf;
495 
496 /*ARGSUSED*/
497 static void
498 fatal_signal(int signo)
499 {
500 	siglongjmp(jumpbuf, 1);
501 }
502 
503 static int
504 print_frame(void *cd, prgregset_t gregs, uint_t argc, const long *argv)
505 {
506 	pstack_handle_t *h = cd;
507 	struct ps_prochandle *Pr = h->proc;
508 	uintptr_t pc = gregs[R_PC];
509 	char buff[255];
510 	GElf_Sym sym;
511 	uintptr_t start;
512 	int length = (is64? 16 : 8);
513 	int i;
514 
515 	/*
516 	 * If we are in a system call, we display the entry frame in a more
517 	 * readable manner, using the name of the system call.  In this case, we
518 	 * want to ignore this first frame, since we already displayed it
519 	 * separately.
520 	 */
521 	if (h->ignore_frame) {
522 		h->ignore_frame = 0;
523 		return (0);
524 	}
525 
526 	(void) sprintf(buff, "%.*lx", length, (long)pc);
527 	(void) strcpy(buff + length, " ????????");
528 	if (Plookup_by_addr(Pr, pc,
529 	    buff + 1 + length, sizeof (buff) - 1 - length, &sym) == 0) {
530 		start = sym.st_value;
531 	} else if (h->jvm != NULL) {
532 		int ret;
533 		void (*segv)(int), (*bus)(int), (*ill)(int);
534 
535 		segv = signal(SIGSEGV, fatal_signal);
536 		bus = signal(SIGBUS, fatal_signal);
537 		ill = signal(SIGILL, fatal_signal);
538 
539 		/* Insure against a bad libjvm_db */
540 		if (sigsetjmp(jumpbuf, 0) == 0)
541 			ret = j_frame_iter(h->jvm, gregs, print_java_frame,
542 			    NULL);
543 		else
544 			ret = -1;
545 
546 		(void) signal(SIGSEGV, segv);
547 		(void) signal(SIGBUS, bus);
548 		(void) signal(SIGILL, ill);
549 
550 		if (ret == 0)
551 			return (ret);
552 	} else {
553 		start = pc;
554 	}
555 
556 	(void) printf(" %-17s (", buff);
557 	for (i = 0; i < argc && i < MAX_ARGS; i++)
558 		(void) printf((i+1 == argc)? "%lx" : "%lx, ",
559 			argv[i]);
560 	if (i != argc)
561 		(void) printf("...");
562 	(void) printf((start != pc)?
563 		") + %lx\n" : ")\n", (long)(pc - start));
564 
565 	/*
566 	 * If the frame's pc is in the "sigh" (a.k.a. signal handler, signal
567 	 * hack, or *sigh* ...) range, then we're about to cross a signal
568 	 * frame.  The signal number is the first argument to this function.
569 	 */
570 	if (pc - sigh.st_value < sigh.st_size) {
571 		if (sig2str((int)argv[0], buff) == -1)
572 			(void) strcpy(buff, " Unknown");
573 		(void) printf(" --- called from signal handler with "
574 		    "signal %d (SIG%s) ---\n", (int)argv[0], buff);
575 	}
576 
577 	return (0);
578 }
579 
580 static void
581 print_zombie(struct ps_prochandle *Pr, struct threadinfo *tip)
582 {
583 	char buff[255];
584 	GElf_Sym sym;
585 	uintptr_t start;
586 	int length = (is64? 16 : 8);
587 
588 	(void) sprintf(buff, "%.*lx", length, (long)tip->startfunc);
589 	(void) strcpy(buff + length, " ????????");
590 	if (Plookup_by_addr(Pr, tip->startfunc,
591 	    buff + 1 + length, sizeof (buff) - 1 - length, &sym) == 0)
592 		start = sym.st_value;
593 	else
594 		start = tip->startfunc;
595 	(void) printf(" %s()", buff);
596 	if (start != tip->startfunc)	/* doesn't happen? */
597 		(void) printf("+%lx", (long)(tip->startfunc - start));
598 	(void) printf(", exit value = 0x%.*lx\n", length, (long)tip->exitval);
599 	(void) printf("\t** zombie "
600 		"(exited, not detached, not yet joined) **\n");
601 }
602 
603 static void
604 print_syscall(const lwpstatus_t *psp, prgregset_t reg)
605 {
606 	char sname[32];
607 	int length = (is64? 16 : 8);
608 	uint_t i;
609 
610 	(void) proc_sysname(psp->pr_syscall, sname, sizeof (sname));
611 	(void) printf(" %.*lx %-8s (", length, (long)reg[R_PC], sname);
612 	for (i = 0; i < psp->pr_nsysarg; i++)
613 		(void) printf((i+1 == psp->pr_nsysarg)? "%lx" : "%lx, ",
614 			(long)psp->pr_sysarg[i]);
615 	(void) printf(")\n");
616 }
617 
618 static void
619 call_stack(pstack_handle_t *h, const lwpstatus_t *psp)
620 {
621 	prgregset_t reg;
622 
623 	(void) memcpy(reg, psp->pr_reg, sizeof (reg));
624 
625 	if ((psp->pr_flags & (PR_ASLEEP|PR_VFORKP)) ||
626 	    ((psp->pr_flags & PR_ISTOP) &&
627 	    (psp->pr_why == PR_SYSENTRY ||
628 	    psp->pr_why == PR_SYSEXIT))) {
629 		print_syscall(psp, reg);
630 		h->ignore_frame = 1;
631 	} else {
632 		h->ignore_frame = 0;
633 	}
634 
635 	(void) Pstack_iter(h->proc, reg, print_frame, h);
636 }
637 
638 /*ARGSUSED*/
639 static int
640 jvm_object_iter(void *cd, const prmap_t *pmp, const char *obj)
641 {
642 	char path[PATH_MAX];
643 	char *name;
644 	char *s1, *s2;
645 	struct ps_prochandle *Pr = cd;
646 
647 	if ((name = strstr(obj, "/libjvm.so")) == NULL)
648 		name = strstr(obj, "/libjvm_g.so");
649 
650 	if (name) {
651 		(void) strcpy(path, obj);
652 		if (Pstatus(Pr)->pr_dmodel != PR_MODEL_NATIVE) {
653 			s1 = name;
654 			s2 = path + (s1 - obj);
655 			(void) strcpy(s2, "/64");
656 			s2 += 3;
657 			(void) strcpy(s2, s1);
658 		}
659 
660 		s1 = strstr(obj, ".so");
661 		s2 = strstr(path, ".so");
662 		(void) strcpy(s2, "_db");
663 		s2 += 3;
664 		(void) strcpy(s2, s1);
665 
666 		if ((libjvm = dlopen(path, RTLD_LAZY|RTLD_GLOBAL)) != NULL)
667 			return (1);
668 	}
669 
670 	return (0);
671 }
672 
673 static jvm_agent_t *
674 load_libjvm(struct ps_prochandle *Pr)
675 {
676 	jvm_agent_t *ret;
677 
678 	(void) Pobject_iter(Pr, jvm_object_iter, Pr);
679 
680 	if (libjvm) {
681 		j_agent_create = (j_agent_create_f)
682 		    dlsym(libjvm, "Jagent_create");
683 		j_agent_destroy = (j_agent_destroy_f)
684 		    dlsym(libjvm, "Jagent_destroy");
685 		j_frame_iter = (j_frame_iter_f)
686 		    dlsym(libjvm, "Jframe_iter");
687 
688 		if (j_agent_create == NULL || j_agent_destroy == NULL ||
689 		    j_frame_iter == NULL ||
690 		    (ret = j_agent_create(Pr, JVM_DB_VERSION)) == NULL) {
691 			reset_libjvm(NULL);
692 			return (NULL);
693 		}
694 
695 		return (ret);
696 	}
697 
698 	return (NULL);
699 }
700 
701 static void
702 reset_libjvm(jvm_agent_t *agent)
703 {
704 	if (libjvm) {
705 		if (agent)
706 			j_agent_destroy(agent);
707 
708 		(void) dlclose(libjvm);
709 	}
710 
711 	j_agent_create = NULL;
712 	j_agent_destroy = NULL;
713 	j_frame_iter = NULL;
714 	libjvm = NULL;
715 }
716