main.c revision ddbf0f5b4139c525b411dded33321debf1f3c923
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <string.h>
#include <memory.h>
#include <signal.h>
#include <wait.h>
#include <limits.h>
#include <errno.h>
#include <sys/resource.h>
#include <libproc.h>
#include "ramdata.h"
#include "proto.h"
#include "htbl.h"
/*
* The user can trace individual threads by using the 'pid/1,3-6,8-' syntax.
* specified, then 'lwps' will be NULL.
*/
typedef struct proc_set {
const char *lwps;
} proc_set_t;
/*
* Function prototypes for static routines in this file.
*/
int xcreat(char *);
void setoutput(int);
void prtim(timestruc_t *);
void pids(char *, proc_set_t *);
void intr(int);
int wait4all(void);
void child_to_file();
void file_to_parent();
void per_proc_init();
int lib_sort(const void *, const void *);
int key_sort(const void *, const void *);
void *worker_thread(void *);
void main_thread(int);
/*
* Test for empty set.
* is_empty() should not be called directly.
*/
#define isemptyset(sp) \
/*
* OR the second set into the first set.
* or_set() should not be called directly.
*/
/* fetch or allocate thread-private data */
{
void *value;
}
return (pri);
}
/* destructor function for thread-private data */
void
free_private(void *value)
{
if (pri->sys_string)
if (pri->exec_string)
if (pri->str_buffer)
}
/*
* This is called by the main thread (via create_thread())
* and is also called from other threads in worker_thread()
* while holding truss_lock. No further locking is required.
*/
void
{
int i;
truss_nlwp++;
for (i = 0; i < truss_maxlwp; i++) {
if (truss_lwpid[i] == 0)
break;
}
if (i == truss_maxlwp) {
/* double the size of the array */
truss_maxlwp * sizeof (lwpid_t));
truss_maxlwp *= 2;
}
truss_lwpid[i] = lwpid;
}
/*
* This is called from the first worker thread to encounter one of
* (leave_hung || interrupt || sigusr1). It must notify all other
* worker threads of the same condition. truss_lock is held.
*/
void
broadcast_signals(void)
{
static int int_notified = FALSE;
static int usr1_notified = FALSE;
static int usr2_notified = FALSE;
int i;
if (interrupt && !int_notified) {
int_notified = TRUE;
for (i = 0; i < truss_maxlwp; i++) {
}
}
if (sigusr1 && !usr1_notified) {
for (i = 0; i < truss_maxlwp; i++) {
}
}
if (leave_hung && !usr2_notified) {
for (i = 0; i < truss_maxlwp; i++) {
}
}
}
static struct ps_lwphandle *
{
struct ps_lwphandle *Lwp;
int gcode;
"%s: cannot grab LWP %u in process %d,"
" reason: %s\n",
Lgrab_error(gcode));
}
}
return (Lwp);
}
/*
* Iteration function called for each initial lwp in the controlled process.
*/
/* ARGSUSED */
int
{
struct ps_lwphandle *new_Lwp;
*count += 1;
}
return (0);
}
int
{
int ofd = -1;
int opt;
int i;
int first;
const lwpstatus_t *Lsp;
int sharedmem;
/* a few of these need to be initialized to NULL */
/*
* Make sure fd's 0, 1, and 2 are allocated,
* just in case truss was invoked from init.
*/
;
if (i > 2)
(void) close(i);
/* this should be per-traced-process */
/* command name (e.g., "truss") */
command++;
else
/* set up the initial private data */
pri = get_private();
#define OPTIONS "FpfcaeildDEht:T:v:x:s:S:m:M:u:U:r:w:o:"
switch (opt) {
case 'F': /* force grabbing (no O_EXCL) */
Fflag = PGRAB_FORCE;
break;
case 'p': /* grab processes */
break;
case 'f': /* follow children */
break;
case 'c': /* don't trace, just count */
break;
case 'a': /* display argument lists */
break;
case 'e': /* display environments */
break;
case 'i': /* don't show interruptable syscalls */
break;
case 'l': /* show lwp id for each syscall */
break;
case 'h': /* debugging: report hash stats */
break;
case 'd': /* show time stamps */
break;
case 'D': /* show time deltas */
break;
case 'E':
break;
case 't': /* system calls to trace */
break;
case 'T': /* system calls to hang process */
break;
case 'v': /* verbose interpretation of syscalls */
break;
case 'x': /* raw interpretation of syscalls */
break;
case 's': /* signals to trace */
break;
case 'S': /* signals to hang process */
break;
case 'm': /* machine faults to trace */
break;
case 'M': /* machine faults to hang process */
break;
case 'u': /* user library functions to trace */
break;
case 'U': /* user library functions to hang */
break;
case 'r': /* show contents of read(fd) */
break;
case 'w': /* show contents of write(fd) */
break;
case 'o': /* output file for trace */
if (ofd >= 0)
}
break;
default:
break;
}
}
if (badname)
exit(2);
/* if -a or -e was specified, force tracing of exec() */
}
/*
* Make sure that all system calls, signals, and machine faults
* that hang the process are added to their trace sets.
*/
/* collect the specified process ids */
"memory for process-ids");
while (argc-- > 0)
}
"usage:\t%s [-fcaeildDEF] [-[tTvx] [!]syscalls] [-[sS] [!]signals]\\\n",
command);
"\t[-[mM] [!]faults] [-[rw] [!]fds] [-[uU] [!]libs:[:][!]funcs]\\\n");
"\t[-o outfile] command | -p pid[/lwps] ...\n");
exit(2);
}
if (argc > 0) { /* create the controlled process */
int err;
switch (err) {
case C_PERM:
"%s: cannot trace set-id or "
"unreadable object file: %s\n",
break;
case C_LP64:
"%s: cannot control _LP64 "
"program: %s\n",
break;
case C_NOEXEC:
"%s: cannot execute program: %s\n",
break;
case C_NOENT:
"%s: cannot find program: %s\n",
break;
case C_STRANGE:
break;
default:
break;
}
exit(2);
}
else
make_pname(pri, 0);
}
}
/*
* Now that we have created the victim process,
* give ourself a million file descriptors.
* This is enough to deal with a multithreaded
* victim process that has half a million lwps.
*/
/*
* Failing the million, give ourself as many
* file descriptors as we can get.
*/
}
/*
* Set up signal dispositions.
*/
} else { /* receive interrupts */
}
/* don't accumulate zombie children */
/* create shared mem space for global mutexes */
-1, (off_t)0);
if (gps == MAP_FAILED)
/* config tmp file if counting and following */
if (sfd == -1)
}
if (created) {
} else { /* grab the specified processes */
i = 0;
while (i < ngrab) { /* grab first process */
break;
}
}
if (!gotone)
while (i < ngrab) { /* grab the remainder */
(void) mutex_lock(&truss_lock);
switch (fork()) {
case -1:
"%s: cannot fork to control process, pid# %d\n",
/* FALLTHROUGH */
default:
(void) mutex_unlock(&truss_lock);
continue; /* parent carries on */
case 0: /* child grabs process */
(void) mutex_unlock(&truss_lock);
descendent = TRUE;
break;
}
exit(2);
}
break;
}
}
/*
* If running setuid-root, become root for real to avoid
* affecting the per-user limitation on the maximum number
* of processes (one benefit of running setuid-root).
*/
Flush();
}
/* trace these regardless, even if we don't report results */
/* for I/O buffer dumps, force tracing of read()s and write()s */
if (!isemptyset(&readfd)) {
}
if (!isemptyset(&writefd)) {
}
}
/* special case -- cannot trace sysexit because context is changed */
}
/* special case -- sysexit not traced by OS */
}
/* special case -- trace exec() on entry to get the args */
/* special case -- sysexit never reached */
/* for function call tracing */
/* trace these regardless, to deal with function calls */
/* needed for x86 */
/*
* Find functions and set breakpoints on grabbed process.
* A process stopped on exec() gets its breakpoints set below.
*/
}
}
/*
* Use asynchronous-stop for multithreaded truss.
* truss runs one lwp for each lwp in the target process.
*/
/* flush out all tracing flags now. */
/*
* If we grabbed a running process, set it running again.
* Since we are tracing lwp_create() and lwp_exit(), the
* lwps will not change in the process until we create all
* of the truss worker threads.
* We leave a created process stopped so its exec() can be reported.
*/
if (!created &&
return (0);
}
/*
* Called from main() and from control() after fork().
*/
void
main_thread(int first)
{
int flags;
int retc;
int i;
int count;
/*
* Block all signals in the main thread.
* Some worker thread will receive signals.
*/
/*
* If we are dealing with a previously hung process,
* arrange not to leave it hung on the same system call.
*/
/*
* Create worker threads to match the lwps in the target process.
*/
truss_nlwp = 0;
truss_maxlwp = 1;
truss_lwpid[0] = 0;
count = 0;
if (count == 0) {
(void) printf("(Warning: no matching active LWPs found, "
"waiting)\n");
Flush();
}
/*
* Set all of the truss worker threads running now.
*/
(void) mutex_lock(&truss_lock);
for (i = 0; i < truss_maxlwp; i++) {
if (truss_lwpid[i])
(void) thr_continue(truss_lwpid[i]);
}
(void) mutex_unlock(&truss_lock);
/*
* Wait until all worker threads terminate.
*/
continue;
if (sigusr1)
if (leave_hung)
flags |= PRELEASE_HANG;
procdel();
if (!descendent) {
interrupt = 0; /* another interrupt kills the report */
if (cflag) {
if (fflag)
}
}
}
void *
worker_thread(void *arg)
{
int req_flag = 0;
int leave_it_hung = FALSE;
int reset_traps = FALSE;
int gcode;
int what;
int ow_in_effect = 0;
long ow_syscall = 0;
long ow_subcode = 0;
make_pname(pri, 0);
/* we were created with all signals blocked; unblock them */
/*
* Run this loop until the victim lwp terminates or we receive
* a termination condition (leave_hung | interrupt | sigusr1).
*/
for (;;) {
break;
}
/* millisecond timeout is for sleeping syscalls */
/*
* If we are to leave this lwp stopped in sympathy
* with another lwp that has been left hung, or if
* we have been interrupted or instructed to release
* our victim process, and this lwp is stopped but
* not on an event of interest to /proc, then just
* leave it in that state.
*/
== PR_STOPPED)
break;
(void) mutex_lock(&truss_lock);
else
dotrace);
(void) mutex_unlock(&truss_lock);
}
continue;
}
break;
/*
* After exec(), only one LWP remains in the process.
* /proc makes the thread following that LWP receive
* EAGAIN (PS_LOST) if the program being exec()ed
* is a set-id program. Every other controlling
* thread receives ENOENT (because its LWP vanished).
* We are the controlling thread for the exec()ing LWP.
* We must wait until all of our siblings terminate
* before attempting to reopen the process.
*/
(void) mutex_lock(&truss_lock);
while (truss_nlwp > 1)
/*
* We have to free and re-grab the LWP.
* The process is guaranteed to be at exit
* from exec() or execve() and have only
* one LWP, namely this one, and the LWP
* is guaranteed to have lwpid == 1.
* This "cannot fail".
*/
who = 1;
abend("Lgrab error: ",
Lgrab_error(gcode));
(void) mutex_unlock(&truss_lock);
continue;
}
/* we really lost it */
}
(void) printf(
"%s\t*** cannot trace across exec() of %s ***\n",
else
(void) printf(
"%s\t*** lost control of process ***\n",
Flush();
(void) mutex_unlock(&truss_lock);
break;
}
}
make_pname(pri, 0);
(void) mutex_lock(&truss_lock);
req_flag = 0;
case PR_REQUESTED:
break;
case PR_SIGNALLED:
break;
case PR_FAULTED:
int rval;
if (rval == 1)
if (rval >= 0)
break;
}
break;
case PR_JOBCONTROL: /* can't happen except first time */
break;
case PR_SYSENTRY:
/* protect ourself from operating system error */
/*
* ow_in_effect checks to see whether or not we
* are attempting to quantify the time spent in
* a one way system call. This is necessary as
* some system calls never return, yet it is desireable
* to determine how much time the traced process
* spends in these calls. To do this, a one way
* flag is set on SYSENTRY when the call is recieved.
* After this, the call mask for the SYSENTRY events
* is filled so that the traced process will stop
* on the entry to the very next system call.
* This appears to the the best way to determine
* system time elapsed between a one way system call.
* Once the next call occurs, values that have been
* stashed are used to record the correct syscall
* and time, and the SYSENTRY event mask is restored
* so that the traced process may continue.
*/
if (dotrace && ow_in_effect) {
if (cflag) {
(void) mutex_lock(&count_lock);
if (ow_subcode != -1)
scp += ow_subcode;
(void) mutex_unlock(&count_lock);
} else if (Eflag) {
}
ow_in_effect = 0;
}
/*
* Special cases. Most syscalls are traced on exit.
*/
switch (what) {
case SYS_exit: /* exit() */
case SYS_lwp_exit: /* lwp_exit() */
case SYS_context: /* [get|set]context() */
case SYS_evtrapret: /* evtrapret() */
ow_in_effect = 1;
ow_syscall = what;
ow_in_effect = 1;
pri->sys_string);
} else if (dotrace &&
Flush();
}
exit_called = TRUE;
break;
case SYS_exec:
case SYS_execve:
pri->exec_string =
NULL);
pri->sys_string);
}
break;
default:
}
break;
}
break;
case PR_SYSEXIT:
/* check for write open of a /proc file */
/*
* The process opened itself
* and no -F flag was specified.
* Just print the open() call
* and let go of the process.
*/
(void) printf("%s\n",
pri->sys_string);
Flush();
}
(void) mutex_unlock(
&truss_lock);
goto out;
}
if (rv == 2) {
/*
* Process opened someone else.
* The open is being reissued.
* Don't report this one.
*/
break;
}
}
}
/*
* Refresh the data model on exec() in case it
* is different from the parent. Lwait()
* doesn't update process-wide status, so we
* have to explicitly call Pstopstatus() to get
* the new state.
*/
}
Flush();
struct ps_lwphandle *new_Lwp;
(void) thr_sigsetmask(SIG_SETMASK,
&lwpid) != 0)
abend("cannot create lwp ",
"to follow child lwp");
(void) thr_continue(lwpid);
(void) thr_sigsetmask(SIG_SETMASK,
}
}
/*
* exec() resets the calling LWP's lwpid to 1.
* If the LWP has changed its lwpid, then
* we have to free and re-grab the LWP
* in order to keep libproc consistent.
* This "cannot fail".
*/
/*
* We must wait for all of our
* siblings to terminate.
*/
while (truss_nlwp > 1)
&truss_lock);
abend("Lgrab error: ",
Lgrab_error(gcode));
}
}
break;
default:
req_flag = 0;
"unknown reason for stopping: %d/%d\n",
}
}
(void) mutex_unlock(&truss_lock);
if (!fflag) {
/*
* If this is vfork(), then
* this clears the breakpoints
* in the parent's address space
* as well as in the child's.
*/
_exit(0);
}
/* NOTREACHED */
}
/*
* Here, we are still the parent truss.
* If the child messes with the breakpoints and
* this is vfork(), we have to set them again.
*/
reset_traps = TRUE;
}
}
if (leave_it_hung) {
(void) mutex_unlock(&truss_lock);
break;
}
if (reset_traps) {
/*
* To recover from vfork, we must catch the lwp
* that issued the vfork() when it returns to user
* level, with all other lwps remaining stopped.
* For this purpose, we have directed all lwps to
* stop and we now set the vfork()ing lwp running
* with the PRSTEP flag. We expect to capture it
* when it stops again showing PR_FAULTED/FLTTRACE.
* We are holding truss_lock, so no other threads
* in truss will set any other lwps in the victim
* process running.
*/
reset_traps = FALSE;
do {
} else {
(void) printf("%s\t*** Expected PR_FAULTED/"
"FLTTRACE stop following vfork()\n",
}
}
int flags = 0;
(void) mutex_unlock(&truss_lock);
break;
}
/*
* If we must leave this lwp hung is sympathy with
* another lwp that is being left hung on purpose,
* then push the state onward toward PR_REQUESTED.
*/
if (leave_hung) {
(void) mutex_unlock(&truss_lock);
break;
}
}
(void) mutex_unlock(&truss_lock);
perror("Lsetrun");
/* NOTREACHED */
}
}
(void) mutex_unlock(&truss_lock);
}
out:
/* block all signals in preparation for exiting */
(void) mutex_lock(&truss_lock);
else {
(void) mutex_lock(&truss_lock);
}
if (dotrace && ow_in_effect) {
if (cflag) {
(void) mutex_lock(&count_lock);
if (ow_subcode != -1)
scp += ow_subcode;
(void) mutex_unlock(&count_lock);
} else if (Eflag) {
}
ow_in_effect = 0;
}
/*
* The victim thread has exited or we lost control of
* the process. Remove ourself from the list of all
* truss threads and notify everyone waiting for this.
*/
int i;
for (i = 0; i < truss_maxlwp; i++) {
if (truss_lwpid[i] == my_id) {
truss_lwpid[i] = 0;
break;
}
}
if (--truss_nlwp != 0) {
(void) cond_broadcast(&truss_cv);
} else {
/*
* The last truss worker thread is terminating.
* The address space is gone (UNDEAD) or is
* inaccessible (LOST) so we cannot clear the
* breakpoints. Just report the htable stats.
*/
}
} else {
/*
* The victim thread is not a zombie thread, and we have not
* lost control of the process. We must have gotten here due
* to (leave_hung || leave_it_hung || interrupt || sigusr1).
* In these cases, we must carefully uninstrument the process
* and either set it running or leave it stopped and abandoned.
*/
static int nstopped = 0;
static int cleared = 0;
if (leave_it_hung)
leave_hung = TRUE;
/*
* The first truss thread through here needs to instruct all
* application threads to stop -- they're not necessarily
* going to stop on their own.
*/
if (nstopped++ == 0)
/*
* Notify all other worker threads about the reason
* for being here (leave_hung || interrupt || sigusr1).
*/
/*
* Once the last thread has reached this point, then and
* only then is it safe to remove breakpoints and other
* instrumentation. Since breakpoints are executed without
* truss_lock held, a monitor thread can't exit until all
* breakpoints have been removed, and we can't be sure the
* procedure to execute a breakpoint won't temporarily
* reinstall a breakpont. Accordingly, we need to wait
* until all threads are in a known state.
*/
while (nstopped != truss_nlwp)
/*
* All truss threads have reached this point.
* One of them clears the breakpoints and
* wakes up everybody else to finish up.
*/
if (cleared++ == 0) {
/*
* All threads should already be stopped,
* but just to be safe...
*/
fflag = 0;
(void) cond_broadcast(&truss_cv);
}
}
(void) mutex_unlock(&truss_lock);
return (NULL);
}
/*
* Give a base date for time stamps, adjusted to the
* stop time of the selected (first or created) process.
*/
void
{
(void) mutex_lock(&count_lock);
(void) mutex_unlock(&count_lock);
const char *ptime;
const char *pdst;
if (delta > 0) {
}
}
pdst = "???";
if (dflag) {
(void) printf(
"Base time stamp: %ld.%4.4ld [ %.20s%s %.4s ]\n",
Flush();
}
}
}
/*
* Performs per-process initializations. If truss is following a victim
* process it will fork additional truss processes to follow new processes
* created. Here is where each new truss process gets its per-process data
* initialized.
*/
void
{
void *pmem;
int i;
/* Make sure we only configure the basetime for the first truss proc */
basehrtime = gethrtime();
}
sizeof (struct syscount));
(void) mutex_lock(&count_lock);
for (i = 0; i <= PRMAXSYS; i++) {
}
(void) mutex_unlock(&count_lock);
}
/*
* Writes child state to a tempfile where it can be read and
* accumulated by the parent process. The file descriptor is shared
* among the processes. Ordering of writes does not matter, it is, however,
* necessary to ensure that all writes are atomic.
*/
void
{
char *s = NULL;
char *t = NULL;
size_t i = 0;
size_t j = 0;
/* ensure that we are in fact a child process */
if (!descendent)
return;
/* enumerate fcall_tbl (tbl locked until freed) */
i = strlen(s) + 1;
j = strlen(t) + 1;
NULL);
}
s, i);
}
}
sizeof (struct syscount));
}
/*
* The following reads entries from the tempfile back to the parent
* so that information can be collected and summed for overall statistics.
* This reads records out of the tempfile. If they are hash table entries,
* the record is merged with the hash table kept by the parent process.
*/
void
{
char *s = NULL;
char *t = NULL;
if (descendent)
return;
/* first get hdntry */
sizeof (hdntry_t))
case HD_hashntry:
/* first get lib string */
}
/* now actually get the string */
abend("Unable to perform full read of lib str",
NULL);
/* now get key string */
}
abend("Unable to perform full read of key str",
NULL);
break;
case HD_cts_syscts:
{
* sizeof (struct syscount);
int i;
NULL);
abend("Unable to perform full read of cts",
NULL);
(void) mutex_lock(&count_lock);
}
for (i = 0; i <= PRMAXSYS; i++) {
}
for (i = 0; i <= PRMAXFAULT; i++) {
}
for (i = 0; i <= PRMAXSIG; i++) {
}
for (i = 0; i <= PRMAXSYS; i++) {
int n = nsubcodes(i);
int subcode;
}
}
}
(void) mutex_unlock(&count_lock);
break;
}
default:
break;
}
}
if (s != NULL)
free(s);
if (t != NULL)
free(t);
}
void
{
if (!cflag) {
if (ff)
if (lf)
if (tid)
*s++ = ':', *s++ = '\t';
*s++ = '\t';
*s = '\0';
}
}
}
/*
* Print the pri->pname[] string, if any.
*/
void
{
}
/*
* Print the timestamp, if requested (-d, -D, or -E).
*/
void
{
int seconds;
int fraction;
return;
if (fraction < 0) {
seconds--;
}
/* fraction in 1/10 milliseconds, rounded up */
seconds++;
}
if (dflag) /* time stamp */
if (Dflag) { /* time delta */
if (fraction < 0) {
seconds--;
}
}
if (Eflag) {
if (fraction < 0) {
seconds--;
}
/* fraction in 1/10 milliseconds, rounded up */
seconds++;
}
}
}
/*
* Create output file, being careful about
*/
int
{
int fd;
int mode = 0666;
/* if directory permissions OK, create file & set ownership */
char *dir;
char *p;
char dot[4];
/* generate path for directory containing file */
*p++ = '.'; /* current directory */
*p = '\0';
} else if (p == path) { /* leading '/' */
*p++ = '/'; /* root directory */
*p = '\0';
} else { /* embedded '/' */
*p = '\0';
}
/* not writeable/searchable */
*p = '/';
fd = -1;
} else { /* create file and set ownership correctly */
*p = '/';
}
fd = -1;
else
/*
* Make sure it's not one of 0, 1, or 2.
* This allows truss to work when spawned by init(1m).
*/
}
/*
* Mark it close-on-exec so created processes don't inherit it.
*/
if (fd >= 0)
return (fd);
}
void
{
if (ofd < 0) {
(void) close(1);
} else if (ofd != 1) {
(void) close(1);
/* if no stderr, make it the same file */
else
}
}
/*
* Accumulate time differencies: a += e - s;
*/
void
{
}
}
int
{
int cmpr = 0;
long i;
long j;
if (i > j)
return (-1);
else if (i < j)
return (1);
else {
return (strcmp(p, q));
}
} else
return (cmpr);
}
void
{
int i;
long count;
const char *name;
long error;
long total;
long errtot;
if (descendent)
return;
if (total == 0) /* produce header */
(void) printf("faults -------------\n");
(const char *)"\t" : (const char *)""),
count);
}
}
if (total == 0) /* produce header */
(void) printf("signals ------------\n");
(const char *)"\t" : (const char *)""),
count);
}
}
i = 0;
i++;
}
lib_sort);
(void) printf(
"\n%-20s %-40s %s\n", "Library:", "Function", "calls");
for (i = 0; i < elem; i++) {
}
}
if (!interrupt)
(void) printf(
"\nsyscall seconds calls errors\n");
int n = nsubcodes(i);
int subcode;
(void) printf("%-19.19s ",
}
}
}
if (!interrupt) {
(void) printf(
" -------- ------ ----\n");
(void) printf("sys totals: ");
}
if (!interrupt) {
(void) printf("usr time: ");
}
if (!interrupt) {
(void) printf("elapsed: ");
}
}
void
{
else
(void) printf(" ");
}
/*
* Gather process id's.
* Return 0 on success, != 0 on failure.
*/
void
{
int i;
return;
}
for (i = 0; i < ngrab; i++)
break;
if (i == ngrab) {
ngrab++;
} else {
}
}
/*
* Report psargs string.
*/
void
{
(void) printf("%spsargs: %.64s\n",
else {
perror("psargs()");
(void) printf("%s\t*** Cannot read psinfo file for pid %d\n",
}
}
char *
{
int nbyte;
int leng = 0;
char string[41];
pri->str_buffer =
if (nbyte > 0 &&
pri->str_buffer =
}
}
return (pri->str_buffer);
}
void
{
perror("show_cred()");
return;
}
if (new)
credentials = cred;
(void) printf(
(void) printf(
}
credentials = cred;
}
/*
* Take control of a child process.
* We come here with truss_lock held.
*/
int
{
const lwpstatus_t *Lsp;
long flags;
int rc;
(void) printf("%s\t*** Cannot fork() to control process #%d\n",
Flush();
return (FALSE);
}
if (childpid != 0) {
/*
* The parent carries on, after a brief pause.
* The parent must wait until the child executes procadd(pid).
*/
return (FALSE);
}
descendent = TRUE;
exit_called = FALSE;
/*
* The parent process owns the shared gps->fork_lock.
* The child must grab it again.
*/
/*
* Child grabs the process and retains the tracing flags.
*/
"%s: cannot control child process, pid# %d: %s\n",
exit(2);
}
/*
* Add ourself to the set of truss processes
* and notify the parent to carry on.
*/
/*
* We may have grabbed the child before it is fully stopped on exit
* from fork. Wait one second (at most) for it to settle down.
*/
make_pname(pri, 0);
return (TRUE);
}
/*
* Take control of an existing process.
*/
int
{
const lwpstatus_t *Lsp;
int gcode;
/*
* Don't force the takeover unless the -F option was specified.
*/
return (FALSE);
}
make_pname(pri, 0);
else
return (TRUE);
}
/*
* Release process from control.
*/
void
{
/*
* The process in question is the child of a traced process.
* We are here to turn off the inherited tracing flags.
*/
int fd;
char ctlname[100];
long ctl[2];
/* process is freshly forked, no need for exclusive open */
perror("release()");
(void) printf(
"%s\t*** Cannot release child process, pid# %d\n",
Flush();
}
if (fd >= 0) /* run-on-last-close sets the process running */
}
void
{
/*
* SIGUSR1 is special. It is used by one truss process to tell
* another truss process to release its controlled process.
* SIGUSR2 is also special. It is used to wake up threads waiting
* for a victim lwp to stop after an event that will leave the
* process hung (stopped and abandoned) has occurred.
*/
void *value;
struct ps_lwphandle *Lwp;
} else {
}
}
void
errmsg(const char *s, const char *q)
{
char msg[512];
if (s || q) {
msg[0] = '\0';
if (command) {
}
if (s)
if (q)
}
}
void
abend(const char *s, const char *q)
{
if (Proc) {
Flush();
errmsg(s, q);
procdel();
(void) wait4all();
} else {
errmsg(s, q);
}
exit(2);
}
/*
* Allocate memory.
* If allocation fails then print a message and abort.
*/
void *
{
else
}
return (buf);
}
void *
{
else
}
return (buf);
}
void *
{
}
int
wait4all()
{
int i;
int rc = 0;
int status;
for (i = 0; i < 10; i++) {
/* return exit() code of the created process */
else
}
}
break;
}
if (i >= 10) /* repeated interrupts */
rc = 2;
return (rc);
}
void
{
(void) printf("%s\t*** process otherwise traced, releasing ...\n",
}
/*
* Test for empty set.
* support routine used by isemptyset() macro.
*/
int
size_t n) /* number of int32's in set */
{
if (n) {
do {
if (*sp++)
return (FALSE);
} while (--n);
}
return (TRUE);
}
/*
* OR the second set into the first.
* The sets must be the same size.
*/
void
{
if (n) {
do {
} while (--n);
}
}