/*
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#define _SYSCALL32
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <string.h>
#include <memory.h>
#include <errno.h>
#include <signal.h>
#include <limits.h>
#include <sys/isa_defs.h>
#include <proc_service.h>
#include <dlfcn.h>
#include <fnmatch.h>
#include <libproc.h>
#include "ramdata.h"
#include "systable.h"
#include "print.h"
#include "proto.h"
#include "htbl.h"
/*
* Functions supporting library function call tracing.
*/
typedef struct {
int nmap;
} ph_map_t;
/*
* static functions in this file.
*/
int object_iter(void *, const prmap_t *, const char *);
int object_present(void *, const prmap_t *, const char *);
int symbol_iter(void *, const GElf_Sym *, const char *);
int get_arguments(long *argp);
void set_deferred_breakpoints(void);
static void
setup_thread_agent(void)
{
return;
else {
}
}
/*
* Delete all breakpoints in the range [base .. base+size)
* from the breakpoint hash table.
*/
static void
{
int i;
if (bpt_hashtable == NULL)
return;
for (i = 0; i < HASHSZ; i++) {
Bpp = &bpt_hashtable[i];
continue;
}
}
}
}
/*
* Establishment of breakpoints on traced library functions.
*/
void
establish_breakpoints(void)
{
return;
/* allocate the breakpoint hash table */
if (bpt_hashtable == NULL) {
NULL);
(void) memset(bpt_hashtable, 0,
}
/*
* Set special rtld_db event breakpoints, first time only.
*/
}
/*
* Set special thread event breakpoint, first time libc is seen.
*/
/*
* Tell libproc to update its mappings.
*/
/*
* If rtld_db told us a library was being deleted,
* first mark all of the dynlibs as not present, then
* iterate over the shared objects, marking only those
* present that really are present, and finally delete
* all of the not-present dynlibs.
*/
if (delete_library) {
continue;
}
}
}
/*
* Iterate over the shared objects, creating breakpoints.
*/
/*
* Now actually set all the breakpoints we just created.
*/
}
/*
* Initial establishment of stacks in a newly-grabbed process.
* establish_breakpoints() has already been called.
*/
void
establish_stacks(void)
{
int mapfd;
int nmap = 0;
nmap = 0;
}
if (mapfd >= 0)
/*
* Iterate over lwps, establishing stacks.
*/
return;
/*
* Iterate over unbound threads, establishing stacks.
*/
}
void
{
/*
* Always search the dynamic symbol table.
*/
symbol_iter, Dyp);
/*
* Search the static symbol table if this is the
* executable file or if we are being asked to
* report internal calls within the library.
*/
symbol_iter, Dyp);
}
/* ARGSUSED */
int
{
const char *str;
char *s;
int i;
return (0);
/*
* Set special thread event breakpoint, first time libc is seen.
*/
break;
} else {
str++;
else
str = object_name;
*s = '\0';
}
}
return (0);
if (hflag && not_consist)
/*
* For every dynlib pattern that matches this library's name,
* iterate through all of the library's symbols looking for
* matching symbol name patterns.
*/
break;
break;
continue; /* no match */
/*
* Require an exact match for the executable (a.out)
* and for the dynamic linker (ld.so.1).
*/
continue;
/*
* Set Dyp->Dp to Dp so symbol_iter() can use it.
*/
}
}
}
/* ARGSUSED */
int
{
}
return (0);
}
/*
* Search for an existing breakpoint at the 'pc' location.
*/
struct bkpt *
{
break;
return (Bp);
}
/*
* Create a breakpoint at 'pc', if one is not there already.
* 'ret' is true when creating a function return breakpoint, in which case
* fail and return NULL if the breakpoint would be created in writeable data.
* If 'set' it true, set the breakpoint in the process now.
*/
struct bkpt *
{
return (Bp);
/*
* Don't set return breakpoints on writeable data
* or on any space other than executable text.
* Don't set breakpoints in the child of a vfork()
* because that would modify the parent's address space.
*/
if (is_vfork_child ||
(ret &&
return (NULL);
/* create a new unnamed breakpoint */
return (Bp);
}
/*
* Set all breakpoints that haven't been set yet.
* Deactivate all breakpoints from modules that are not present any more.
*/
void
set_deferred_breakpoints(void)
{
int i;
if (is_vfork_child)
return;
for (i = 0; i < HASHSZ; i++) {
}
}
}
}
int
{
int i;
/* ignore any undefined symbols */
return (0);
/*
* Arbitrarily omit "_start" from the executable.
* (Avoid indentation before main().)
*/
return (0);
/*
* Arbitrarily omit "_rt_boot" from the dynamic linker.
* (Avoid indentation before main().)
*/
return (0);
/*
* Arbitrarily omit any symbols whose name starts with '.'.
* Apparantly putting a breakpoint on .umul causes a
* fatal error in libthread (%y is not restored correctly
* when a single step is taken). Looks like a /proc bug.
*/
if (*sym_name == '.')
return (0);
/*
* For each pattern in the array of symbol patterns,
* if the pattern matches the symbol name, then
* create a breakpoint at the function in question.
*/
break;
continue;
return (0);
/*
* New breakpoints receive a name now.
* For existing breakpoints, prefer the subset name if possible,
* else prefer the shorter name.
*/
}
return (0);
}
}
/* For debugging only ---- */
void
report_htable_stats(void)
{
uint_t i, j;
return;
for (i = 0; i < HASHSZ; i++) {
j = 0;
j++;
if (j < Min)
Min = j;
if (j > Max)
Max = j;
if (j < HASHSZ)
bucket[j]++;
Total += j;
}
for (i = 0; i < HASHSZ; i++)
if (bucket[i])
bucket[i], i);
" base = 0x%.8lx end = 0x%.8lx size = %ld\n",
}
" base = 0x%.8lx end = 0x%.8lx size = %ld\n",
}
void
{
if (data_model != PR_MODEL_LP64)
/* check to see if we already have this stack */
if (sp == 0)
return;
return;
nstack++;
Stk->nthr_create = 0;
/* primary stack */
return;
}
/* alternate stack */
return;
}
/* thread stacks? */
/* The bloody fools got this backwards! */
return;
}
/* last chance -- try the raw memory map */
return;
}
}
nstack--;
}
void
{
if (data_model != PR_MODEL_LP64)
/* check to see if we already have this stack */
if (sp == 0)
return;
return;
nstack++;
Stk->nthr_create = 0;
/* primary stack */
return;
}
/* The bloody fools got this backwards! */
return;
}
nstack--;
}
struct callstack *
{
int mapfd;
int nmap = 0;
/*
* Get the address space map.
*/
if (mapfd >= 0)
return (NULL);
}
nstack++;
Stk->nthr_create = 0;
break;
}
}
return (Stk);
}
struct callstack *
{
#if defined(__sparc)
#endif
/* primary stack */
nstack++;
Stk->nthr_create = 0;
NULL);
return (Stk);
}
/* alternate stack */
nstack++;
Stk->nthr_create = 0;
NULL);
return (Stk);
}
return (find_lwp_stack(sp));
/* thread stacks? */
if (hflag)
"cannot get thread handle for "
"lwp#%d, error=%d, tref=0x%.8lx\n",
return (NULL);
}
if (hflag)
"cannot get thread info for "
"lwp#%d, error=%d, tref=0x%.8lx\n",
return (NULL);
}
nstack++;
/* The bloody fools got this backwards! */
NULL);
return (Stk);
}
/* stack bounds failure -- complain bitterly */
if (hflag) {
"sp not within thread stack: "
"sp=0x%.8lx stkbase=0x%.8lx stkend=0x%.8lx\n",
/* The bloody fools got this backwards! */
}
return (NULL);
}
void
{
#if defined(__sparc)
#endif
Stk->nthr_create = 0;
return;
}
/*
* Shortcut here --
* If we have a matching tref and no new threads have
* been created since the last time we encountered this
* stack, then we don't have to go through the overhead
* of calling td_ta_map_lwp2thr() to get the thread-id.
*/
return;
if (hflag)
"cannot get thread handle for "
"lwp#%d, error=%d, tref=0x%.8lx\n",
Stk->nthr_create = 0;
if (hflag)
"cannot get thread info for "
"lwp#%d, error=%d, tref=0x%.8lx\n",
Stk->nthr_create = 0;
} else {
}
}
struct callstack *
{
if (sp == 0 ||
return (NULL);
break;
/*
* If we didn't find the stack, do it the hard way.
*/
#ifdef _LP64
if (data_model == PR_MODEL_LP64)
else
#endif
#else
#ifdef _LP64
if (data_model != PR_MODEL_LP64)
else
#else
#endif
#endif /* i386 */
break;
}
}
return (NULL);
/*
* Ensure that there is room for at least one more entry.
*/
}
if (makeid)
return (Stk);
}
/*
* Reset the breakpoint information (called on successful exec()).
*/
void
reset_breakpoints(void)
{
int i;
return;
/* destroy all previous dynamic library information */
}
/* destroy all previous breakpoint trap information */
if (bpt_hashtable != NULL) {
for (i = 0; i < HASHSZ; i++) {
}
}
}
/* destroy all the callstack information */
}
/* we are not a multi-threaded process anymore */
(void) td_ta_delete(Thr_agent);
/* tell libproc to clear out its mapping information */
/* Reestablish the symbols from the executable */
(void) establish_breakpoints();
}
/*
* Clear breakpoints from the process (called before Prelease()).
* Don't actually destroy the breakpoint table;
* threads currently fielding breakpoints will need it.
*/
void
clear_breakpoints(void)
{
int i;
return;
/*
* Change all breakpoint traps back to normal instructions.
* We attempt to remove a breakpoint from every address which
* may have ever contained a breakpoint to protect our victims.
*/
report_htable_stats(); /* report stats first */
for (i = 0; i < HASHSZ; i++) {
}
}
(void) td_ta_delete(Thr_agent);
}
}
/*
* Reestablish the breakpoint traps in the process.
* Called after resuming from a vfork() in the parent.
*/
void
reestablish_traps(void)
{
int i;
return;
for (i = 0; i < HASHSZ; i++) {
}
}
}
void
{
int narg;
int i;
}
}
for (i = 0; i < narg; i++) {
if (i < narg-1) {
}
}
(void) printf(")\n");
Flush();
}
/* ARGSUSED */
void
{
int i;
}
if (stret) {
(void) printf("struct return\n");
} else if (data_model == PR_MODEL_LP64) {
else
} else {
else
}
Flush();
}
/*
* Called to deal with function-call tracing.
* Return 0 on normal success, 1 to indicate a BPT_HANG success,
* and -1 on failure (not tracing functions or unknown breakpoint).
*/
int
{
int active;
int rval = 0;
return (-1);
if (data_model != PR_MODEL_LP64) {
}
if (hflag)
"function_trace(): "
"cannot find breakpoint for pc: 0x%.8lx\n",
return (-1);
}
if (hflag) {
"RD_PREINIT breakpoint\n");
"RD_POSTINIT breakpoint\n");
"RD_DLACTIVITY breakpoint\n");
}
case RD_CONSISTENT:
break;
case RD_ADD:
not_consist = FALSE;
break;
case RD_DELETE:
break;
default:
break;
}
}
if (hflag) {
const char *et;
case RD_NONE:
et = "RD_NONE";
break;
case RD_PREINIT:
et = "RD_PREINIT";
break;
case RD_POSTINIT:
et = "RD_POSTINIT";
break;
case RD_DLACTIVITY:
et = "RD_DLACTIVITY";
break;
default:
break;
}
"event_msg.type = %s ", et);
case RD_NOSTATE:
et = "RD_NOSTATE";
break;
case RD_CONSISTENT:
et = "RD_CONSISTENT";
break;
case RD_ADD:
et = "RD_ADD";
break;
case RD_DELETE:
et = "RD_DELETE";
break;
default:
break;
}
"event_msg.u.state = %s\n", et);
}
}
}
nthr_create++;
if (hflag)
"BPT_TD_CREATE breakpoint\n");
/* we don't care about the event message */
}
if (dotrace) {
if (cflag) {
}
else
rval = 1;
}
} else if (!clear) {
rval = 1;
} else {
}
}
}
/*
* Single-step the traced instruction. Since it's possible that
* another thread has deactivated this breakpoint, we indicate
* that we have reactivated it by virtue of executing it.
*
* To avoid a deadlock with some other thread in the process
* performing a fork() or a thr_suspend() operation, we must
* drop and later reacquire truss_lock. Some fancy dancing here.
*/
(void) mutex_unlock(&truss_lock);
(void) mutex_lock(&truss_lock);
#if defined(__i386)
/*
* Leave it stopped in a state that a stack trace is reasonable.
*/
/* XX64 needs to be updated for amd64 & gcc */
/* step it over the movl %esp,%ebp */
(void) mutex_unlock(&truss_lock);
/* we're wrapping up; wait one second at most */
(void) mutex_lock(&truss_lock);
}
#endif
(void) mutex_unlock(&truss_lock);
/* we're wrapping up; wait one second at most */
(void) mutex_lock(&truss_lock);
} else {
}
}
return (rval);
}
void
{
int i;
#ifdef _LP64
if (data_model != PR_MODEL_LP64) {
}
#endif
/*
* If the sp is not within the stack bounds, forget it.
* If the symbol's 'internal' flag is false,
* don't report internal calls within the library.
*/
return;
break;
}
}
/*
* Breakpoints for function returns are set here
* If we're counting function calls, there is no need to set
* a breakpoint upon return
*/
}
if (cflag) {
(unsigned long)1);
} else {
}
}
/*
* We are here because we hit an unnamed breakpoint.
* Attempt to match this up with a return pc on the stack
* and report the function return.
*/
void
{
int i;
#ifdef _LP64
if (data_model != PR_MODEL_LP64) {
}
#endif
break;
}
}
if (i < 0) {
/* probably __mul64() or friends -- try harder */
int j;
for (j = 0; i < 0 && j < 8; j++) { /* up to 8 args */
sp -= 4;
break;
}
}
}
}
#endif
if ((i >= 0) && (!cflag)) {
}
}
#if defined(__sparc)
#define FPADJUST 0
#endif
void
{
struct {
/*
* Gather stack frames bottom to top.
*/
while (sp != 0) {
maxframe *= 2;
NULL);
}
}
/*
* Scan for function return breakpoints top to bottom.
*/
while (nframe--) {
/* lookup the called function in the symbol tables */
continue;
/* lookup the function in the breakpoint table */
continue;
continue;
continue; /* can't happen? */
}
}
}
int
{
}
/* ARGSUSED */
int
{
/*
* We have already dealt with all the lwps.
* We only care about unbound threads here (TD_PARTIALREG).
*/
return (0);
}
#if defined(__sparc)
{
#ifdef _LP64
if (data_model == PR_MODEL_LP64) {
== sizeof (rwin)) {
}
if (fp != 0 &&
!= sizeof (rwin))
} else {
#else /* _LP64 */
#endif /* _LP64 */
}
if (fp != 0 &&
#ifdef _LP64
}
#endif
if (rpc)
return (fp);
}
/* ARGSUSED */
{
if (data_model != PR_MODEL_LP64)
/* check for structure return (bletch!) */
inst < 0x1000)
return (rpc);
}
int
{
int i;
if (data_model != PR_MODEL_LP64)
for (i = 0; i < 4; i++)
else
for (i = 0; i < 4; i++)
return (4);
}
#endif /* __sparc */
{
(frame[0] != 0 &&
if (rpc)
return (frame[0]);
}
#endif
/*
* Examine the instruction at the return location of a function call
* and return the byte count by which the stack is adjusted on return.
* It the instruction at the return location is an addl, as expected,
* then adjust the return pc by the size of that instruction so that
* we will place the return breakpoint on the following instruction.
* This allows programs that interrogate their own stacks and record
* function calls and arguments to work correctly even while we interfere.
* Return the count on success, -1 on failure.
*/
int
{
int count;
return (-1);
/* find the replaced instruction at pc (if any) */
return (-1);
/*
* A bit of disassembly of the instruction is required here.
*/
count = 0;
*ppc += 6;
*ppc += 3;
} else { /* not an addl inctruction */
count = 0;
}
return (count);
}
{
int count;
return (0);
count = 0;
return (rpc);
}
{
#ifdef _LP64
if (data_model == PR_MODEL_LP64) {
return (0);
/*
* Ignore arguments pushed on the stack. See comments in
* get_arguments().
*/
return (rpc);
} else
#endif
return (get_return_address32(psp));
}
int
{
int narg;
int count;
int i;
if (narg <= 0)
return (0);
/*
* Given the return PC, determine the number of arguments.
*/
narg = 0;
else {
}
for (i = 0; i < narg; i++)
return (narg);
}
int
{
#ifdef _LP64
if (data_model == PR_MODEL_LP64) {
/*
* On amd64, we do not know how many arguments are passed to
* each function. While it may be possible to detect if we
* have more than 6 arguments, it is of marginal value.
* Instead, assume that we always have 6 arguments, which are
* passed via registers.
*/
return (6);
} else
#endif
return (get_arguments32(argp));
}
#endif /* __amd64 || __i386 */