Unleashed v1.4
[unleashed.git] / usr / src / cmd / ptools / pstack / pstack.c
blob0515ff62d0814cad89b53fee9433e53d4c447ef4
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/isa_defs.h>
28 #include <stdio.h>
29 #include <stdio_ext.h>
30 #include <fcntl.h>
31 #include <ctype.h>
32 #include <string.h>
33 #include <signal.h>
34 #include <dirent.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #include <unistd.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/stack.h>
42 #include <link.h>
43 #include <limits.h>
44 #include <libelf.h>
45 #include <thread_db.h>
46 #include <libproc.h>
47 #include <setjmp.h>
49 static char *command;
50 static int Fflag;
51 static int is64;
52 static GElf_Sym sigh;
55 * To keep the list of user-level threads for a multithreaded process.
57 struct threadinfo {
58 struct threadinfo *next;
59 id_t threadid;
60 id_t lwpid;
61 td_thr_state_e state;
62 uintptr_t startfunc;
63 uintptr_t exitval;
64 prgregset_t regs;
67 static struct threadinfo *thr_head, *thr_tail;
69 #define TRUE 1
70 #define FALSE 0
72 #define MAX_ARGS 8
75 * To support debugging java programs, we display java frames within a stack.
76 * The logic to walk the java frames is contained in libjvm_db.so, which is
77 * found in the same directory as libjvm.so, linked with the program. If we are
78 * debugging a 32-bit app with a 64-binary, then the debugging library is found
79 * in the '64' subdirectory. If we find libjvm_db.so, then we fill in these
80 * stub routines.
82 typedef struct jvm_agent jvm_agent_t;
83 typedef int java_stack_f(void *, prgregset_t, const char *, int, int, void *);
86 * The j_agent_create function takes a version parameter. This ensures that the
87 * interface can evolve appropriately.
89 #define JVM_DB_VERSION 1
90 static void *libjvm;
91 typedef jvm_agent_t *(*j_agent_create_f)(struct ps_prochandle *, int);
92 typedef void (*j_agent_destroy_f)(jvm_agent_t *);
93 typedef int (*j_frame_iter_f)(jvm_agent_t *, prgregset_t, java_stack_f *,
94 void *);
96 static j_agent_create_f j_agent_create;
97 static j_agent_destroy_f j_agent_destroy;
98 static j_frame_iter_f j_frame_iter;
100 static jvm_agent_t *load_libjvm(struct ps_prochandle *P);
101 static void reset_libjvm(jvm_agent_t *);
104 * Similar to what's done for debugging java programs, here are prototypes for
105 * the library that allows us to debug Python programs.
107 #define PYDB_VERSION 1
108 static void *libpython;
110 typedef struct pydb_agent pydb_agent_t;
112 typedef pydb_agent_t *(*pydb_agent_create_f)(struct ps_prochandle *P, int vers);
113 typedef void (*pydb_agent_destroy_f)(pydb_agent_t *py);
114 typedef int (*pydb_pc_frameinfo_f)(pydb_agent_t *py, uintptr_t pc,
115 uintptr_t frame_addr, char *fbuf, size_t bufsz);
117 static pydb_agent_create_f pydb_agent_create;
118 static pydb_agent_destroy_f pydb_agent_destroy;
119 static pydb_pc_frameinfo_f pydb_pc_frameinfo;
121 static pydb_agent_t *load_libpython(struct ps_prochandle *P);
122 static void reset_libpython(pydb_agent_t *);
124 * Since we must maintain both a proc handle and a jvm handle, this structure
125 * is the basic type that gets passed around.
127 typedef struct pstack_handle {
128 struct ps_prochandle *proc;
129 jvm_agent_t *jvm;
130 int ignore_frame;
131 const char *lwps;
132 int count;
133 pydb_agent_t *pydb;
134 } pstack_handle_t;
136 static int thr_stack(const td_thrhandle_t *, void *);
137 static void free_threadinfo(void);
138 static struct threadinfo *find_thread(id_t);
139 static int all_call_stacks(pstack_handle_t *, int);
140 static void tlhead(id_t, id_t);
141 static int print_frame(void *, prgregset_t, uint_t, const long *);
142 static void print_zombie(struct ps_prochandle *, struct threadinfo *);
143 static void print_syscall(const lwpstatus_t *, prgregset_t);
144 static void call_stack(pstack_handle_t *, const lwpstatus_t *);
147 * The number of active and zombie threads.
149 static int nthreads;
152 main(int argc, char **argv)
154 int retc = 0;
155 int opt;
156 int errflg = FALSE;
157 core_content_t content = CC_CONTENT_DATA | CC_CONTENT_ANON |
158 CC_CONTENT_STACK;
159 struct rlimit rlim;
161 if ((command = strrchr(argv[0], '/')) != NULL)
162 command++;
163 else
164 command = argv[0];
166 /* options */
167 while ((opt = getopt(argc, argv, "F")) != EOF) {
168 switch (opt) {
169 case 'F':
171 * If the user specifies the force option, we'll
172 * consent to printing out other threads' stacks
173 * even if the main stack is absent.
175 content &= ~CC_CONTENT_STACK;
176 Fflag = PGRAB_FORCE;
177 break;
178 default:
179 errflg = TRUE;
180 break;
184 argc -= optind;
185 argv += optind;
187 if (errflg || argc <= 0) {
188 (void) fprintf(stderr,
189 "usage:\t%s [-F] { pid | core }[/lwps] ...\n", command);
190 (void) fprintf(stderr, " (show process call stack)\n");
191 (void) fprintf(stderr,
192 " -F: force grabbing of the target process\n");
193 exit(2);
197 * Make sure we'll have enough file descriptors to handle a target
198 * that has many many mappings.
200 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
201 rlim.rlim_cur = rlim.rlim_max;
202 (void) setrlimit(RLIMIT_NOFILE, &rlim);
203 (void) enable_extended_FILE_stdio(-1, -1);
206 (void) proc_initstdio();
208 while (--argc >= 0) {
209 int gcode;
210 psinfo_t psinfo;
211 const psinfo_t *tpsinfo;
212 struct ps_prochandle *Pr = NULL;
213 td_thragent_t *Tap;
214 int threaded;
215 pstack_handle_t handle;
216 const char *lwps, *arg;
218 (void) proc_flushstdio();
220 arg = *argv++;
222 if ((Pr = proc_arg_xgrab(arg, NULL, PR_ARG_ANY,
223 Fflag, &gcode, &lwps)) == NULL) {
224 (void) fprintf(stderr, "%s: cannot examine %s: %s\n",
225 command, arg, Pgrab_error(gcode));
226 retc++;
227 continue;
230 if ((tpsinfo = Ppsinfo(Pr)) == NULL) {
231 (void) fprintf(stderr, "%s: cannot examine %s: "
232 "lost control of process\n", command, arg);
233 Prelease(Pr, 0);
234 retc++;
235 continue;
237 (void) memcpy(&psinfo, tpsinfo, sizeof (psinfo_t));
238 proc_unctrl_psinfo(&psinfo);
240 if (Pstate(Pr) == PS_DEAD) {
241 if ((Pcontent(Pr) & content) != content) {
242 (void) fprintf(stderr, "%s: core '%s' has "
243 "insufficient content\n", command, arg);
244 retc++;
245 continue;
247 (void) printf("core '%s' of %d:\t%.70s\n",
248 arg, (int)psinfo.pr_pid, psinfo.pr_psargs);
249 } else {
250 (void) printf("%d:\t%.70s\n",
251 (int)psinfo.pr_pid, psinfo.pr_psargs);
254 is64 = (psinfo.pr_dmodel == PR_MODEL_LP64);
256 if (Pgetauxval(Pr, AT_BASE) != -1L && Prd_agent(Pr) == NULL) {
257 (void) fprintf(stderr, "%s: warning: librtld_db failed "
258 "to initialize; symbols from shared libraries will "
259 "not be available\n", command);
263 * First we need to get a thread agent handle.
265 if (td_init() != TD_OK ||
266 td_ta_new(Pr, &Tap) != TD_OK) /* no libc */
267 threaded = FALSE;
268 else {
270 * Iterate over all threads, calling:
271 * thr_stack(td_thrhandle_t *Thp, NULL);
272 * for each one to generate the list of threads.
274 nthreads = 0;
275 (void) td_ta_thr_iter(Tap, thr_stack, NULL,
276 TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
277 TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
279 (void) td_ta_delete(Tap);
280 threaded = TRUE;
283 handle.proc = Pr;
284 handle.jvm = load_libjvm(Pr);
285 handle.pydb = load_libpython(Pr);
286 handle.lwps = lwps;
287 handle.count = 0;
289 if (all_call_stacks(&handle, threaded) != 0)
290 retc++;
291 if (threaded)
292 free_threadinfo();
294 reset_libjvm(handle.jvm);
295 reset_libpython(handle.pydb);
296 Prelease(Pr, 0);
298 if (handle.count == 0)
299 (void) fprintf(stderr, "%s: no matching LWPs found\n",
300 command);
303 (void) proc_finistdio();
305 return (retc);
309 * Thread iteration call-back function.
310 * Called once for each user-level thread.
311 * Used to build the list of all threads.
313 /* ARGSUSED1 */
314 static int
315 thr_stack(const td_thrhandle_t *Thp, void *cd)
317 td_thrinfo_t thrinfo;
318 struct threadinfo *tip;
319 td_err_e error;
321 if (td_thr_get_info(Thp, &thrinfo) != TD_OK)
322 return (0);
324 tip = malloc(sizeof (struct threadinfo));
325 tip->next = NULL;
326 tip->threadid = thrinfo.ti_tid;
327 tip->lwpid = thrinfo.ti_lid;
328 tip->state = thrinfo.ti_state;
329 tip->startfunc = thrinfo.ti_startfunc;
330 tip->exitval = (uintptr_t)thrinfo.ti_exitval;
331 nthreads++;
333 if (thrinfo.ti_state == TD_THR_ZOMBIE ||
334 ((error = td_thr_getgregs(Thp, tip->regs)) != TD_OK &&
335 error != TD_PARTIALREG))
336 (void) memset(tip->regs, 0, sizeof (prgregset_t));
338 if (thr_tail)
339 thr_tail->next = tip;
340 else
341 thr_head = tip;
342 thr_tail = tip;
344 return (0);
347 static void
348 free_threadinfo()
350 struct threadinfo *tip = thr_head;
351 struct threadinfo *next;
353 while (tip) {
354 next = tip->next;
355 free(tip);
356 tip = next;
359 thr_head = thr_tail = NULL;
363 * Find and eliminate the thread corresponding to the given lwpid.
365 static struct threadinfo *
366 find_thread(id_t lwpid)
368 struct threadinfo *tip;
370 for (tip = thr_head; tip; tip = tip->next) {
371 if (lwpid == tip->lwpid) {
372 tip->lwpid = 0;
373 return (tip);
376 return (NULL);
379 static int
380 thread_call_stack(void *data, const lwpstatus_t *psp,
381 const lwpsinfo_t *pip)
383 pstack_handle_t *h = data;
384 lwpstatus_t lwpstatus;
385 struct threadinfo *tip;
387 if (!proc_lwp_in_set(h->lwps, pip->pr_lwpid))
388 return (0);
389 h->count++;
391 if ((tip = find_thread(pip->pr_lwpid)) == NULL)
392 return (0);
394 tlhead(tip->threadid, pip->pr_lwpid);
395 tip->threadid = 0; /* finish eliminating tid */
396 if (psp)
397 call_stack(h, psp);
398 else {
399 if (tip->state == TD_THR_ZOMBIE)
400 print_zombie(h->proc, tip);
401 else {
402 (void) memset(&lwpstatus, 0, sizeof (lwpstatus));
403 (void) memcpy(lwpstatus.pr_reg, tip->regs,
404 sizeof (prgregset_t));
405 call_stack(h, &lwpstatus);
408 return (0);
411 static int
412 lwp_call_stack(void *data,
413 const lwpstatus_t *psp, const lwpsinfo_t *pip)
415 pstack_handle_t *h = data;
417 if (!proc_lwp_in_set(h->lwps, pip->pr_lwpid))
418 return (0);
419 h->count++;
421 tlhead(0, pip->pr_lwpid);
422 if (psp)
423 call_stack(h, psp);
424 else
425 (void) printf("\t** zombie "
426 "(exited, not detached, not yet joined) **\n");
427 return (0);
430 static int
431 all_call_stacks(pstack_handle_t *h, int dothreads)
433 struct ps_prochandle *Pr = h->proc;
434 pstatus_t status = *Pstatus(Pr);
436 (void) memset(&sigh, 0, sizeof (GElf_Sym));
437 (void) Plookup_by_name(Pr, "libc.so", "sigacthandler", &sigh);
439 if ((status.pr_nlwp + status.pr_nzomb) <= 1 &&
440 !(dothreads && nthreads > 1)) {
441 if (proc_lwp_in_set(h->lwps, status.pr_lwp.pr_lwpid)) {
442 call_stack(h, &status.pr_lwp);
443 h->count++;
445 } else {
446 lwpstatus_t lwpstatus;
447 struct threadinfo *tip;
448 id_t tid;
450 if (dothreads)
451 (void) Plwp_iter_all(Pr, thread_call_stack, h);
452 else
453 (void) Plwp_iter_all(Pr, lwp_call_stack, h);
455 /* for each remaining thread w/o an lwp */
456 (void) memset(&lwpstatus, 0, sizeof (lwpstatus));
457 for (tip = thr_head; tip; tip = tip->next) {
459 if (!proc_lwp_in_set(h->lwps, tip->lwpid))
460 tip->threadid = 0;
462 if ((tid = tip->threadid) != 0) {
463 (void) memcpy(lwpstatus.pr_reg, tip->regs,
464 sizeof (prgregset_t));
465 tlhead(tid, tip->lwpid);
466 if (tip->state == TD_THR_ZOMBIE)
467 print_zombie(Pr, tip);
468 else
469 call_stack(h, &lwpstatus);
471 tip->threadid = 0;
472 tip->lwpid = 0;
475 return (0);
478 static void
479 tlhead(id_t threadid, id_t lwpid)
481 if (threadid == 0 && lwpid == 0)
482 return;
484 (void) printf("-----------------");
486 if (threadid && lwpid)
487 (void) printf(" lwp# %d / thread# %d ",
488 (int)lwpid, (int)threadid);
489 else if (threadid)
490 (void) printf("--------- thread# %d ", (int)threadid);
491 else if (lwpid)
492 (void) printf(" lwp# %d ------------", (int)lwpid);
494 (void) printf("--------------------\n");
497 /*ARGSUSED*/
498 static int
499 print_java_frame(void *cld, prgregset_t gregs, const char *name, int bci,
500 int line, void *handle)
502 int length = (is64 ? 16 : 8);
504 (void) printf(" %.*lx * %s", length, (long)gregs[R_PC], name);
506 if (bci != -1) {
507 (void) printf("+%d", bci);
508 if (line)
509 (void) printf(" (line %d)", line);
511 (void) printf("\n");
513 return (0);
516 static sigjmp_buf jumpbuf;
518 /*ARGSUSED*/
519 static void
520 fatal_signal(int signo)
522 siglongjmp(jumpbuf, 1);
525 static int
526 print_frame(void *cd, prgregset_t gregs, uint_t argc, const long *argv)
528 pstack_handle_t *h = cd;
529 struct ps_prochandle *Pr = h->proc;
530 uintptr_t pc = gregs[R_PC];
531 char buff[255];
532 GElf_Sym sym;
533 uintptr_t start;
534 int length = (is64? 16 : 8);
535 int i;
538 * If we are in a system call, we display the entry frame in a more
539 * readable manner, using the name of the system call. In this case, we
540 * want to ignore this first frame, since we already displayed it
541 * separately.
543 if (h->ignore_frame) {
544 h->ignore_frame = 0;
545 return (0);
548 (void) sprintf(buff, "%.*lx", length, (long)pc);
549 (void) strcpy(buff + length, " ????????");
550 if (Plookup_by_addr(Pr, pc,
551 buff + 1 + length, sizeof (buff) - 1 - length, &sym) == 0) {
552 start = sym.st_value;
553 } else if (h->jvm != NULL) {
554 int ret;
555 void (*segv)(int), (*bus)(int), (*ill)(int);
557 segv = signal(SIGSEGV, fatal_signal);
558 bus = signal(SIGBUS, fatal_signal);
559 ill = signal(SIGILL, fatal_signal);
561 /* Insure against a bad libjvm_db */
562 if (sigsetjmp(jumpbuf, 0) == 0)
563 ret = j_frame_iter(h->jvm, gregs, print_java_frame,
564 NULL);
565 else
566 ret = -1;
568 (void) signal(SIGSEGV, segv);
569 (void) signal(SIGBUS, bus);
570 (void) signal(SIGILL, ill);
572 if (ret == 0)
573 return (ret);
574 } else {
575 start = pc;
578 (void) printf(" %-17s (", buff);
579 for (i = 0; i < argc && i < MAX_ARGS; i++)
580 (void) printf((i+1 == argc) ? "%lx" : "%lx, ", argv[i]);
581 if (i != argc)
582 (void) printf("...");
583 (void) printf((start != pc) ? ") + %lx\n" : ")\n", (long)(pc - start));
585 if (h->pydb != NULL && argc > 0) {
586 char buf_py[1024];
587 int rc;
589 rc = pydb_pc_frameinfo(h->pydb, pc, argv[0], buf_py,
590 sizeof (buf_py));
591 if (rc == 0) {
592 (void) printf(" %s", buf_py);
597 * If the frame's pc is in the "sigh" (a.k.a. signal handler, signal
598 * hack, or *sigh* ...) range, then we're about to cross a signal
599 * frame. The signal number is the first argument to this function.
601 if (pc - sigh.st_value < sigh.st_size) {
602 if (sig2str((int)argv[0], buff) == -1)
603 (void) strcpy(buff, " Unknown");
604 (void) printf(" --- called from signal handler with "
605 "signal %d (SIG%s) ---\n", (int)argv[0], buff);
608 return (0);
611 static void
612 print_zombie(struct ps_prochandle *Pr, struct threadinfo *tip)
614 char buff[255];
615 GElf_Sym sym;
616 uintptr_t start;
617 int length = (is64? 16 : 8);
619 (void) sprintf(buff, "%.*lx", length, (long)tip->startfunc);
620 (void) strcpy(buff + length, " ????????");
621 if (Plookup_by_addr(Pr, tip->startfunc,
622 buff + 1 + length, sizeof (buff) - 1 - length, &sym) == 0)
623 start = sym.st_value;
624 else
625 start = tip->startfunc;
626 (void) printf(" %s()", buff);
627 if (start != tip->startfunc) /* doesn't happen? */
628 (void) printf("+%lx", (long)(tip->startfunc - start));
629 (void) printf(", exit value = 0x%.*lx\n", length, (long)tip->exitval);
630 (void) printf("\t** zombie "
631 "(exited, not detached, not yet joined) **\n");
634 static void
635 print_syscall(const lwpstatus_t *psp, prgregset_t reg)
637 char sname[32];
638 int length = (is64? 16 : 8);
639 uint_t i;
641 (void) proc_sysname(psp->pr_syscall, sname, sizeof (sname));
642 (void) printf(" %.*lx %-8s (", length, (long)reg[R_PC], sname);
643 for (i = 0; i < psp->pr_nsysarg; i++)
644 (void) printf((i+1 == psp->pr_nsysarg)? "%lx" : "%lx, ",
645 (long)psp->pr_sysarg[i]);
646 (void) printf(")\n");
649 static void
650 call_stack(pstack_handle_t *h, const lwpstatus_t *psp)
652 prgregset_t reg;
654 (void) memcpy(reg, psp->pr_reg, sizeof (reg));
656 if ((psp->pr_flags & (PR_ASLEEP|PR_VFORKP)) ||
657 ((psp->pr_flags & PR_ISTOP) &&
658 (psp->pr_why == PR_SYSENTRY ||
659 psp->pr_why == PR_SYSEXIT))) {
660 print_syscall(psp, reg);
661 h->ignore_frame = 1;
662 } else {
663 h->ignore_frame = 0;
666 (void) Pstack_iter(h->proc, reg, print_frame, h);
669 /*ARGSUSED*/
670 static int
671 jvm_object_iter(void *cd, const prmap_t *pmp, const char *obj)
673 char path[PATH_MAX];
674 char *name;
675 char *s1, *s2;
676 struct ps_prochandle *Pr = cd;
678 if ((name = strstr(obj, "/libjvm.so")) == NULL)
679 name = strstr(obj, "/libjvm_g.so");
681 if (name) {
682 (void) strcpy(path, obj);
683 if (Pstatus(Pr)->pr_dmodel != PR_MODEL_NATIVE) {
684 s1 = name;
685 s2 = path + (s1 - obj);
686 (void) strcpy(s2, "/64");
687 s2 += 3;
688 (void) strcpy(s2, s1);
691 s1 = strstr(obj, ".so");
692 s2 = strstr(path, ".so");
693 (void) strcpy(s2, "_db");
694 s2 += 3;
695 (void) strcpy(s2, s1);
697 if ((libjvm = dlopen(path, RTLD_LAZY|RTLD_GLOBAL)) != NULL)
698 return (1);
701 return (0);
704 static jvm_agent_t *
705 load_libjvm(struct ps_prochandle *Pr)
707 jvm_agent_t *ret;
710 * Iterate through all the loaded objects in the target, looking
711 * for libjvm.so. If we find libjvm.so we'll try to load the
712 * corresponding libjvm_db.so that lives in the same directory.
714 * At first glance it seems like we'd want to use
715 * Pobject_iter_resolved() here since we'd want to make sure that
716 * we have the full path to the libjvm.so. But really, we don't
717 * want that since we're going to be dlopen()ing a library and
718 * executing code from that path, and therefore we don't want to
719 * load any library code that could be from a zone since it could
720 * have been replaced with a trojan. Hence, we use Pobject_iter().
721 * So if we're debugging java processes in a zone from the global
722 * zone, and we want to get proper java stack stack frames, then
723 * the same jvm that is running within the zone needs to be
724 * installed in the global zone.
726 (void) Pobject_iter(Pr, jvm_object_iter, Pr);
728 if (libjvm) {
729 j_agent_create = (j_agent_create_f)
730 dlsym(libjvm, "Jagent_create");
731 j_agent_destroy = (j_agent_destroy_f)
732 dlsym(libjvm, "Jagent_destroy");
733 j_frame_iter = (j_frame_iter_f)
734 dlsym(libjvm, "Jframe_iter");
736 if (j_agent_create == NULL || j_agent_destroy == NULL ||
737 j_frame_iter == NULL ||
738 (ret = j_agent_create(Pr, JVM_DB_VERSION)) == NULL) {
739 reset_libjvm(NULL);
740 return (NULL);
743 return (ret);
746 return (NULL);
749 static void
750 reset_libjvm(jvm_agent_t *agent)
752 if (libjvm) {
753 if (agent)
754 j_agent_destroy(agent);
756 (void) dlclose(libjvm);
759 j_agent_create = NULL;
760 j_agent_destroy = NULL;
761 j_frame_iter = NULL;
762 libjvm = NULL;
765 /*ARGSUSED*/
766 static int
767 python_object_iter(void *cd, const prmap_t *pmp, const char *obj)
769 char path[PATH_MAX];
770 char *name;
771 char *s1, *s2;
772 struct ps_prochandle *Pr = cd;
774 name = strstr(obj, "/libpython");
776 if (name) {
777 (void) strcpy(path, obj);
778 if (Pstatus(Pr)->pr_dmodel != PR_MODEL_NATIVE) {
779 s1 = name;
780 s2 = path + (s1 - obj);
781 (void) strcpy(s2, "/64");
782 s2 += 3;
783 (void) strcpy(s2, s1);
786 s1 = strstr(obj, ".so");
787 s2 = strstr(path, ".so");
788 (void) strcpy(s2, "_db");
789 s2 += 3;
790 (void) strcpy(s2, s1);
792 if ((libpython = dlopen(path, RTLD_LAZY|RTLD_GLOBAL)) != NULL)
793 return (1);
796 return (0);
799 static pydb_agent_t *
800 load_libpython(struct ps_prochandle *Pr)
802 pydb_agent_t *pdb;
804 (void) Pobject_iter(Pr, python_object_iter, Pr);
806 if (libpython) {
807 pydb_agent_create = (pydb_agent_create_f)
808 dlsym(libpython, "pydb_agent_create");
809 pydb_agent_destroy = (pydb_agent_destroy_f)
810 dlsym(libpython, "pydb_agent_destroy");
811 pydb_pc_frameinfo = (pydb_pc_frameinfo_f)
812 dlsym(libpython, "pydb_pc_frameinfo");
814 if (pydb_agent_create == NULL || pydb_agent_destroy == NULL ||
815 pydb_pc_frameinfo == NULL) {
816 (void) dlclose(libpython);
817 libpython = NULL;
818 return (NULL);
821 pdb = pydb_agent_create(Pr, PYDB_VERSION);
822 if (pdb == NULL) {
823 (void) dlclose(libpython);
824 libpython = NULL;
825 return (NULL);
827 return (pdb);
830 return (NULL);
833 static void
834 reset_libpython(pydb_agent_t *pdb)
836 if (libpython != NULL) {
837 if (pdb != NULL) {
838 pydb_agent_destroy(pdb);
840 (void) dlclose(libpython);
843 libpython = NULL;
844 pydb_agent_create = NULL;
845 pydb_agent_destroy = NULL;
846 pydb_pc_frameinfo = NULL;