/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/isa_defs.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <link.h>
#include <limits.h>
#include <libelf.h>
#include <thread_db.h>
#include <libproc.h>
#include <setjmp.h>
static char *command;
static int Fflag;
static int is64;
/*
* To keep the list of user-level threads for a multithreaded process.
*/
struct threadinfo {
};
#define FALSE 0
/*
* To support debugging java programs, we display java frames within a stack.
* The logic to walk the java frames is contained in libjvm_db.so, which is
* found in the same directory as libjvm.so, linked with the program. If we are
* debugging a 32-bit app with a 64-binary, then the debugging library is found
* in the '64' subdirectory. If we find libjvm_db.so, then we fill in these
* stub routines.
*/
/*
* The j_agent_create function takes a version parameter. This ensures that the
* interface can evolve appropriately.
*/
static void *libjvm;
void *);
static void reset_libjvm(jvm_agent_t *);
/*
* Similar to what's done for debugging java programs, here are prototypes for
* the library that allows us to debug Python programs.
*/
static void *libpython;
static void reset_libpython(pydb_agent_t *);
/*
* Since we must maintain both a proc handle and a jvm handle, this structure
* is the basic type that gets passed around.
*/
typedef struct pstack_handle {
int ignore_frame;
const char *lwps;
int count;
static int thr_stack(const td_thrhandle_t *, void *);
static void free_threadinfo(void);
static int all_call_stacks(pstack_handle_t *, int);
/*
* The number of active and zombie threads.
*/
static int nthreads;
int
{
int retc = 0;
int opt;
command++;
else
/* options */
switch (opt) {
case 'F':
/*
* If the user specifies the force option, we'll
* consent to printing out other threads' stacks
* even if the main stack is absent.
*/
content &= ~CC_CONTENT_STACK;
Fflag = PGRAB_FORCE;
break;
default:
break;
}
}
"usage:\t%s [-F] { pid | core }[/lwps] ...\n", command);
" -F: force grabbing of the target process\n");
exit(2);
}
/*
* Make sure we'll have enough file descriptors to handle a target
* that has many many mappings.
*/
}
(void) proc_initstdio();
while (--argc >= 0) {
int gcode;
int threaded;
(void) proc_flushstdio();
retc++;
continue;
}
retc++;
continue;
}
retc++;
continue;
}
(void) printf("core '%s' of %d:\t%.70s\n",
} else {
(void) printf("%d:\t%.70s\n",
}
"to initialize; symbols from shared libraries will "
"not be available\n", command);
}
/*
* First we need to get a thread agent handle.
*/
else {
/*
* Iterate over all threads, calling:
* thr_stack(td_thrhandle_t *Thp, NULL);
* for each one to generate the list of threads.
*/
nthreads = 0;
(void) td_ta_delete(Tap);
}
retc++;
if (threaded)
command);
}
(void) proc_finistdio();
return (retc);
}
/*
* Thread iteration call-back function.
* Called once for each user-level thread.
* Used to build the list of all threads.
*/
/* ARGSUSED1 */
static int
{
return (0);
nthreads++;
error != TD_PARTIALREG))
if (thr_tail)
else
return (0);
}
static void
{
while (tip) {
}
}
/*
* Find and eliminate the thread corresponding to the given lwpid.
*/
static struct threadinfo *
{
return (tip);
}
}
return (NULL);
}
static int
const lwpsinfo_t *pip)
{
pstack_handle_t *h = data;
return (0);
h->count++;
return (0);
if (psp)
call_stack(h, psp);
else {
else {
sizeof (prgregset_t));
call_stack(h, &lwpstatus);
}
}
return (0);
}
static int
{
pstack_handle_t *h = data;
return (0);
h->count++;
if (psp)
call_stack(h, psp);
else
(void) printf("\t** zombie "
"(exited, not detached, not yet joined) **\n");
return (0);
}
static int
{
h->count++;
}
} else {
if (dothreads)
else
/* for each remaining thread w/o an lwp */
sizeof (prgregset_t));
else
call_stack(h, &lwpstatus);
}
}
}
return (0);
}
static void
{
return;
(void) printf("-----------------");
(void) printf(" lwp# %d / thread# %d ",
else if (threadid)
else if (lwpid)
(void) printf("--------------------\n");
}
/*ARGSUSED*/
static int
{
if (bci != -1) {
if (line)
}
(void) printf("\n");
return (0);
}
/*ARGSUSED*/
static void
{
}
static int
{
pstack_handle_t *h = cd;
int i;
/*
* If we are in a system call, we display the entry frame in a more
* readable manner, using the name of the system call. In this case, we
* want to ignore this first frame, since we already displayed it
* separately.
*/
if (h->ignore_frame) {
h->ignore_frame = 0;
return (0);
}
int ret;
/* Insure against a bad libjvm_db */
NULL);
else
ret = -1;
if (ret == 0)
return (ret);
} else {
}
if (i != argc)
(void) printf("...");
int rc;
sizeof (buf_py));
if (rc == 0) {
}
}
/*
* If the frame's pc is in the "sigh" (a.k.a. signal handler, signal
* hack, or *sigh* ...) range, then we're about to cross a signal
* frame. The signal number is the first argument to this function.
*/
(void) printf(" --- called from signal handler with "
}
return (0);
}
static void
{
else
(void) printf("\t** zombie "
"(exited, not detached, not yet joined) **\n");
}
static void
{
uint_t i;
for (i = 0; i < psp->pr_nsysarg; i++)
(void) printf(")\n");
}
static void
{
h->ignore_frame = 1;
} else {
h->ignore_frame = 0;
}
}
/*ARGSUSED*/
static int
{
char *name;
if (name) {
s2 += 3;
}
s2 += 3;
return (1);
}
return (0);
}
static jvm_agent_t *
{
/*
* Iterate through all the loaded objects in the target, looking
* corresponding libjvm_db.so that lives in the same directory.
*
* At first glance it seems like we'd want to use
* Pobject_iter_resolved() here since we'd want to make sure that
* we have the full path to the libjvm.so. But really, we don't
* want that since we're going to be dlopen()ing a library and
* executing code from that path, and therefore we don't want to
* load any library code that could be from a zone since it could
* have been replaced with a trojan. Hence, we use Pobject_iter().
* So if we're debugging java processes in a zone from the global
* zone, and we want to get proper java stack stack frames, then
* the same jvm that is running within the zone needs to be
* installed in the global zone.
*/
if (libjvm) {
j_frame_iter == NULL ||
return (NULL);
}
return (ret);
}
return (NULL);
}
static void
{
if (libjvm) {
if (agent)
}
j_frame_iter = NULL;
}
/*ARGSUSED*/
static int
{
char *name;
if (name) {
s2 += 3;
}
s2 += 3;
return (1);
}
return (0);
}
static pydb_agent_t *
{
if (libpython) {
pydb_pc_frameinfo == NULL) {
return (NULL);
}
return (NULL);
}
return (pdb);
}
return (NULL);
}
static void
{
}
}
}