1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 27*0Sstevel@tonic-gate 28*0Sstevel@tonic-gate /* 29*0Sstevel@tonic-gate * Routines to provide profiling of shared libraries required by the called 30*0Sstevel@tonic-gate * executable. 31*0Sstevel@tonic-gate */ 32*0Sstevel@tonic-gate #include <stdio.h> 33*0Sstevel@tonic-gate #include <fcntl.h> 34*0Sstevel@tonic-gate #include <sys/mman.h> 35*0Sstevel@tonic-gate #include <unistd.h> 36*0Sstevel@tonic-gate #include <stdlib.h> 37*0Sstevel@tonic-gate #include <string.h> 38*0Sstevel@tonic-gate #include <sys/types.h> 39*0Sstevel@tonic-gate #include <sys/stat.h> 40*0Sstevel@tonic-gate #include <synch.h> 41*0Sstevel@tonic-gate #include <signal.h> 42*0Sstevel@tonic-gate #include <synch.h> 43*0Sstevel@tonic-gate #include <link.h> 44*0Sstevel@tonic-gate #include <sys/param.h> 45*0Sstevel@tonic-gate #include <procfs.h> 46*0Sstevel@tonic-gate #include "msg.h" 47*0Sstevel@tonic-gate #include "sgs.h" 48*0Sstevel@tonic-gate #include "profile.h" 49*0Sstevel@tonic-gate #include "_rtld.h" 50*0Sstevel@tonic-gate 51*0Sstevel@tonic-gate 52*0Sstevel@tonic-gate static char Profile[MAXPATHLEN]; /* Profile buffer pathname */ 53*0Sstevel@tonic-gate static char *pname = 0; /* name of object to profile */ 54*0Sstevel@tonic-gate static L_hdr *Hptr; /* profile buffer header pointer */ 55*0Sstevel@tonic-gate static L_cgarc *Cptr; /* profile buffer call graph pointer */ 56*0Sstevel@tonic-gate static caddr_t Hpc, Lpc; /* Range of addresses being monitored */ 57*0Sstevel@tonic-gate static size_t Fsize; /* Size of mapped in profile buffer */ 58*0Sstevel@tonic-gate uintptr_t profcookie = 0; 59*0Sstevel@tonic-gate 60*0Sstevel@tonic-gate /* 61*0Sstevel@tonic-gate * When handling mutex's locally we need to mask signals. The signal 62*0Sstevel@tonic-gate * mask is for everything except SIGWAITING. 63*0Sstevel@tonic-gate */ 64*0Sstevel@tonic-gate static const sigset_t iset = { ~0U, ~0U, ~0U, ~0U }; 65*0Sstevel@tonic-gate 66*0Sstevel@tonic-gate static lwp_mutex_t sharedmutex = SHAREDMUTEX; 67*0Sstevel@tonic-gate 68*0Sstevel@tonic-gate static int 69*0Sstevel@tonic-gate prof_mutex_init(lwp_mutex_t *mp) 70*0Sstevel@tonic-gate { 71*0Sstevel@tonic-gate (void) memcpy(mp, &sharedmutex, sizeof (lwp_mutex_t)); 72*0Sstevel@tonic-gate return (0); 73*0Sstevel@tonic-gate } 74*0Sstevel@tonic-gate 75*0Sstevel@tonic-gate static int 76*0Sstevel@tonic-gate prof_mutex_lock(lwp_mutex_t *mp, sigset_t *oset) 77*0Sstevel@tonic-gate { 78*0Sstevel@tonic-gate if (oset) 79*0Sstevel@tonic-gate (void) sigprocmask(SIG_BLOCK, &iset, oset); 80*0Sstevel@tonic-gate (void) _lwp_mutex_lock(mp); 81*0Sstevel@tonic-gate return (0); 82*0Sstevel@tonic-gate } 83*0Sstevel@tonic-gate 84*0Sstevel@tonic-gate static int 85*0Sstevel@tonic-gate prof_mutex_unlock(mutex_t *mp, sigset_t *oset) 86*0Sstevel@tonic-gate { 87*0Sstevel@tonic-gate (void) _lwp_mutex_unlock(mp); 88*0Sstevel@tonic-gate if (oset) 89*0Sstevel@tonic-gate (void) sigprocmask(SIG_SETMASK, oset, NULL); 90*0Sstevel@tonic-gate return (0); 91*0Sstevel@tonic-gate } 92*0Sstevel@tonic-gate 93*0Sstevel@tonic-gate 94*0Sstevel@tonic-gate extern char *_dgettext(const char *, const char *); 95*0Sstevel@tonic-gate 96*0Sstevel@tonic-gate 97*0Sstevel@tonic-gate const char * 98*0Sstevel@tonic-gate _ldprof_msg(Msg mid) 99*0Sstevel@tonic-gate { 100*0Sstevel@tonic-gate return (_dgettext(MSG_ORIG(MSG_SUNW_OST_SGS), MSG_ORIG(mid))); 101*0Sstevel@tonic-gate } 102*0Sstevel@tonic-gate 103*0Sstevel@tonic-gate /* 104*0Sstevel@tonic-gate * Determine whether a set (of arbitrary size) is in use - used to analyze proc 105*0Sstevel@tonic-gate * status information. 106*0Sstevel@tonic-gate */ 107*0Sstevel@tonic-gate static int 108*0Sstevel@tonic-gate setisinuse(uint32_t *sp, uint_t n) 109*0Sstevel@tonic-gate { 110*0Sstevel@tonic-gate while (n--) 111*0Sstevel@tonic-gate if (*sp++) 112*0Sstevel@tonic-gate return (1); 113*0Sstevel@tonic-gate return (0); 114*0Sstevel@tonic-gate } 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gate #define prisinuse(sp) \ 117*0Sstevel@tonic-gate setisinuse((uint32_t *)(sp), \ 118*0Sstevel@tonic-gate (uint_t)(sizeof (*(sp)) / sizeof (uint32_t))) 119*0Sstevel@tonic-gate 120*0Sstevel@tonic-gate uint_t 121*0Sstevel@tonic-gate la_version(uint_t version) 122*0Sstevel@tonic-gate { 123*0Sstevel@tonic-gate int fd; 124*0Sstevel@tonic-gate ssize_t num; 125*0Sstevel@tonic-gate pstatus_t status; 126*0Sstevel@tonic-gate 127*0Sstevel@tonic-gate if (version < LAV_CURRENT) { 128*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_GEN_AUDITVERSION), 129*0Sstevel@tonic-gate LAV_CURRENT, version); 130*0Sstevel@tonic-gate return (LAV_CURRENT); 131*0Sstevel@tonic-gate } 132*0Sstevel@tonic-gate 133*0Sstevel@tonic-gate /* 134*0Sstevel@tonic-gate * To reduce the potential for deadlock conditions that can arise from 135*0Sstevel@tonic-gate * being monitored (say by truss(1)) while setting a lock in the profile 136*0Sstevel@tonic-gate * buffer, determine if someone is monitoring us. If so silently 137*0Sstevel@tonic-gate * disable profiling. 138*0Sstevel@tonic-gate */ 139*0Sstevel@tonic-gate if ((fd = open(MSG_ORIG(MSG_FMT_PROCSELF), O_RDONLY)) < 0) 140*0Sstevel@tonic-gate return (LAV_CURRENT); 141*0Sstevel@tonic-gate 142*0Sstevel@tonic-gate num = read(fd, &status, sizeof (status)); 143*0Sstevel@tonic-gate (void) close(fd); 144*0Sstevel@tonic-gate 145*0Sstevel@tonic-gate if ((num != sizeof (status)) || 146*0Sstevel@tonic-gate prisinuse(&status.pr_sigtrace) || prisinuse(&status.pr_flttrace) || 147*0Sstevel@tonic-gate prisinuse(&status.pr_sysentry) || prisinuse(&status.pr_sysexit)) { 148*0Sstevel@tonic-gate return (LAV_CURRENT); 149*0Sstevel@tonic-gate } 150*0Sstevel@tonic-gate 151*0Sstevel@tonic-gate /* 152*0Sstevel@tonic-gate * We're presently not being monitored (although there's no control of 153*0Sstevel@tonic-gate * someone attaching to us later), so retrieve the profile target name. 154*0Sstevel@tonic-gate */ 155*0Sstevel@tonic-gate if (dlinfo((void *)NULL, RTLD_DI_PROFILENAME, &pname) == -1) 156*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_GEN_PROFNOTSET)); 157*0Sstevel@tonic-gate 158*0Sstevel@tonic-gate return (LAV_CURRENT); 159*0Sstevel@tonic-gate } 160*0Sstevel@tonic-gate 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate int 163*0Sstevel@tonic-gate profile_open(const char *fname, Link_map *lmp) 164*0Sstevel@tonic-gate { 165*0Sstevel@tonic-gate size_t hsize; /* struct hdr size */ 166*0Sstevel@tonic-gate size_t psize; /* profile histogram size */ 167*0Sstevel@tonic-gate size_t csize; /* call graph array size */ 168*0Sstevel@tonic-gate size_t msize; /* size of memory being profiled */ 169*0Sstevel@tonic-gate int i, fd, fixed = 0; 170*0Sstevel@tonic-gate caddr_t lpc; 171*0Sstevel@tonic-gate caddr_t hpc; 172*0Sstevel@tonic-gate caddr_t addr; 173*0Sstevel@tonic-gate struct stat status; 174*0Sstevel@tonic-gate int new_buffer = 0; 175*0Sstevel@tonic-gate sigset_t mask; 176*0Sstevel@tonic-gate int err; 177*0Sstevel@tonic-gate Ehdr * ehdr; /* ELF header for file */ 178*0Sstevel@tonic-gate Phdr * phdr; /* program headers for file */ 179*0Sstevel@tonic-gate Dyn * dynp = 0; /* Dynamic section */ 180*0Sstevel@tonic-gate Word nsym = 0; /* no. of symtab ntries */ 181*0Sstevel@tonic-gate 182*0Sstevel@tonic-gate if (*Profile == '\0') { 183*0Sstevel@tonic-gate const char *dir, *suf; 184*0Sstevel@tonic-gate char *tmp; 185*0Sstevel@tonic-gate 186*0Sstevel@tonic-gate /* 187*0Sstevel@tonic-gate * From the basename of the specified filename generate the 188*0Sstevel@tonic-gate * appropriate profile buffer name. The profile file is created 189*0Sstevel@tonic-gate * if it does not already exist. 190*0Sstevel@tonic-gate */ 191*0Sstevel@tonic-gate if (((tmp = strrchr(fname, '/')) != 0) && (*(++tmp))) 192*0Sstevel@tonic-gate fname = tmp; 193*0Sstevel@tonic-gate 194*0Sstevel@tonic-gate #if defined(_ELF64) 195*0Sstevel@tonic-gate suf = MSG_ORIG(MSG_SUF_PROFILE_64); 196*0Sstevel@tonic-gate #else 197*0Sstevel@tonic-gate suf = MSG_ORIG(MSG_SUF_PROFILE); 198*0Sstevel@tonic-gate #endif 199*0Sstevel@tonic-gate if (dlinfo((void *)NULL, RTLD_DI_PROFILEOUT, &dir) == -1) 200*0Sstevel@tonic-gate dir = MSG_ORIG(MSG_PTH_VARTMP); 201*0Sstevel@tonic-gate 202*0Sstevel@tonic-gate (void) snprintf(Profile, MAXPATHLEN, MSG_ORIG(MSG_FMT_PROFILE), 203*0Sstevel@tonic-gate dir, fname, suf); 204*0Sstevel@tonic-gate } 205*0Sstevel@tonic-gate 206*0Sstevel@tonic-gate if ((fd = open(Profile, (O_RDWR | O_CREAT), 0666)) == -1) { 207*0Sstevel@tonic-gate err = errno; 208*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), Profile, 209*0Sstevel@tonic-gate strerror(err)); 210*0Sstevel@tonic-gate return (0); 211*0Sstevel@tonic-gate } 212*0Sstevel@tonic-gate 213*0Sstevel@tonic-gate /* 214*0Sstevel@tonic-gate * Now we determine the valid pc range for this object. The lpc is easy 215*0Sstevel@tonic-gate * (lmp->l_addr), to determine the hpc we must examine the Phdrs. 216*0Sstevel@tonic-gate */ 217*0Sstevel@tonic-gate lpc = hpc = (caddr_t)lmp->l_addr; 218*0Sstevel@tonic-gate /* LINTED */ 219*0Sstevel@tonic-gate ehdr = (Ehdr *)lpc; 220*0Sstevel@tonic-gate if (ehdr->e_phnum == 0) { 221*0Sstevel@tonic-gate (void) close(fd); 222*0Sstevel@tonic-gate return (0); 223*0Sstevel@tonic-gate } 224*0Sstevel@tonic-gate if (ehdr->e_type == ET_EXEC) 225*0Sstevel@tonic-gate fixed = 1; 226*0Sstevel@tonic-gate /* LINTED */ 227*0Sstevel@tonic-gate phdr = (Phdr *)(ehdr->e_phoff + lpc); 228*0Sstevel@tonic-gate for (i = 0; i < ehdr->e_phnum; i++, phdr++) { 229*0Sstevel@tonic-gate caddr_t _hpc; 230*0Sstevel@tonic-gate 231*0Sstevel@tonic-gate if (phdr->p_type == PT_DYNAMIC) { 232*0Sstevel@tonic-gate dynp = (Dyn *)phdr->p_vaddr; 233*0Sstevel@tonic-gate if (fixed == 0) { 234*0Sstevel@tonic-gate dynp = (Dyn *)((unsigned long)dynp + 235*0Sstevel@tonic-gate (unsigned long)lpc); 236*0Sstevel@tonic-gate } 237*0Sstevel@tonic-gate continue; 238*0Sstevel@tonic-gate } 239*0Sstevel@tonic-gate 240*0Sstevel@tonic-gate if (phdr->p_type != PT_LOAD) 241*0Sstevel@tonic-gate continue; 242*0Sstevel@tonic-gate 243*0Sstevel@tonic-gate _hpc = (caddr_t)(phdr->p_vaddr + phdr->p_memsz); 244*0Sstevel@tonic-gate if (fixed == 0) { 245*0Sstevel@tonic-gate _hpc = (caddr_t)((unsigned long)_hpc + 246*0Sstevel@tonic-gate (unsigned long)lpc); 247*0Sstevel@tonic-gate } 248*0Sstevel@tonic-gate if (_hpc > hpc) 249*0Sstevel@tonic-gate hpc = _hpc; 250*0Sstevel@tonic-gate } 251*0Sstevel@tonic-gate if (lpc == hpc) { 252*0Sstevel@tonic-gate (void) close(fd); 253*0Sstevel@tonic-gate return (0); 254*0Sstevel@tonic-gate } 255*0Sstevel@tonic-gate 256*0Sstevel@tonic-gate /* 257*0Sstevel@tonic-gate * In order to determine the number of symbols in the object scan the 258*0Sstevel@tonic-gate * dynamic section until we find the DT_HASH entry (hash[1] == symcnt). 259*0Sstevel@tonic-gate */ 260*0Sstevel@tonic-gate if (dynp) { 261*0Sstevel@tonic-gate for (; dynp->d_tag != DT_NULL; dynp++) { 262*0Sstevel@tonic-gate unsigned int *hashp; 263*0Sstevel@tonic-gate 264*0Sstevel@tonic-gate if (dynp->d_tag != DT_HASH) 265*0Sstevel@tonic-gate continue; 266*0Sstevel@tonic-gate 267*0Sstevel@tonic-gate hashp = (unsigned int *)dynp->d_un.d_ptr; 268*0Sstevel@tonic-gate if (fixed == 0) { 269*0Sstevel@tonic-gate hashp = (unsigned int *)((unsigned long)hashp + 270*0Sstevel@tonic-gate (unsigned long)lpc); 271*0Sstevel@tonic-gate } 272*0Sstevel@tonic-gate nsym = hashp[1]; 273*0Sstevel@tonic-gate break; 274*0Sstevel@tonic-gate } 275*0Sstevel@tonic-gate } 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate /* 278*0Sstevel@tonic-gate * Determine the (minimum) size of the buffer to allocate 279*0Sstevel@tonic-gate */ 280*0Sstevel@tonic-gate Lpc = lpc = (caddr_t)PRF_ROUNDWN((long)lpc, sizeof (long)); 281*0Sstevel@tonic-gate Hpc = hpc = (caddr_t)PRF_ROUNDUP((long)hpc, sizeof (long)); 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate hsize = sizeof (L_hdr); 284*0Sstevel@tonic-gate msize = (size_t)(hpc - lpc); 285*0Sstevel@tonic-gate psize = (size_t)PRF_ROUNDUP((msize / PRF_BARSIZE), sizeof (long)); 286*0Sstevel@tonic-gate csize = (nsym + 1) * PRF_CGINIT * sizeof (L_cgarc); 287*0Sstevel@tonic-gate Fsize = (hsize + psize + csize); 288*0Sstevel@tonic-gate 289*0Sstevel@tonic-gate /* 290*0Sstevel@tonic-gate * If the file size is zero (ie. we just created it), truncate it 291*0Sstevel@tonic-gate * to the minimum size. 292*0Sstevel@tonic-gate */ 293*0Sstevel@tonic-gate (void) fstat(fd, &status); 294*0Sstevel@tonic-gate if (status.st_size == 0) { 295*0Sstevel@tonic-gate if (ftruncate(fd, Fsize) == -1) { 296*0Sstevel@tonic-gate err = errno; 297*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_FTRUNC), 298*0Sstevel@tonic-gate Profile, strerror(err)); 299*0Sstevel@tonic-gate (void) close(fd); 300*0Sstevel@tonic-gate return (0); 301*0Sstevel@tonic-gate } 302*0Sstevel@tonic-gate new_buffer++; 303*0Sstevel@tonic-gate } else 304*0Sstevel@tonic-gate Fsize = status.st_size; 305*0Sstevel@tonic-gate 306*0Sstevel@tonic-gate /* 307*0Sstevel@tonic-gate * Map the file in. 308*0Sstevel@tonic-gate */ 309*0Sstevel@tonic-gate if ((addr = (caddr_t)mmap(0, Fsize, (PROT_READ | PROT_WRITE), 310*0Sstevel@tonic-gate MAP_SHARED, fd, 0)) == (char *)-1) { 311*0Sstevel@tonic-gate err = errno; 312*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_MMAP), Profile, 313*0Sstevel@tonic-gate strerror(err)); 314*0Sstevel@tonic-gate (void) close(fd); 315*0Sstevel@tonic-gate return (0); 316*0Sstevel@tonic-gate } 317*0Sstevel@tonic-gate (void) close(fd); 318*0Sstevel@tonic-gate 319*0Sstevel@tonic-gate /* 320*0Sstevel@tonic-gate * Initialize the remaining elements of the header. All pc addresses 321*0Sstevel@tonic-gate * that are recorded are relative to zero thus allowing the recorded 322*0Sstevel@tonic-gate * entries to be correlated with the symbols in the original file, 323*0Sstevel@tonic-gate * and to compensate for any differences in where the file is mapped. 324*0Sstevel@tonic-gate * If the high pc address has been initialized from a previous run, 325*0Sstevel@tonic-gate * and the new entry is different from the original then a new library 326*0Sstevel@tonic-gate * must have been installed. In this case bale out. 327*0Sstevel@tonic-gate */ 328*0Sstevel@tonic-gate /* LINTED */ 329*0Sstevel@tonic-gate Hptr = (L_hdr *)addr; 330*0Sstevel@tonic-gate 331*0Sstevel@tonic-gate if (new_buffer) 332*0Sstevel@tonic-gate (void) prof_mutex_init((lwp_mutex_t *)&Hptr->hd_mutex); 333*0Sstevel@tonic-gate 334*0Sstevel@tonic-gate (void) prof_mutex_lock((mutex_t *)&Hptr->hd_mutex, &mask); 335*0Sstevel@tonic-gate if (Hptr->hd_hpc) { 336*0Sstevel@tonic-gate if (Hptr->hd_hpc != (caddr_t)(hpc - lpc)) { 337*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_GEN_PROFSZCHG), 338*0Sstevel@tonic-gate Profile); 339*0Sstevel@tonic-gate (void) prof_mutex_unlock((mutex_t *)&Hptr-> 340*0Sstevel@tonic-gate hd_mutex, &mask); 341*0Sstevel@tonic-gate (void) munmap((caddr_t)Hptr, Fsize); 342*0Sstevel@tonic-gate return (0); 343*0Sstevel@tonic-gate } 344*0Sstevel@tonic-gate } else { 345*0Sstevel@tonic-gate /* 346*0Sstevel@tonic-gate * Initialize the header information as we must have just 347*0Sstevel@tonic-gate * created the output file. 348*0Sstevel@tonic-gate */ 349*0Sstevel@tonic-gate Hptr->hd_magic = (unsigned int)PRF_MAGIC; 350*0Sstevel@tonic-gate #if defined(_ELF64) 351*0Sstevel@tonic-gate Hptr->hd_version = (unsigned int)PRF_VERSION_64; 352*0Sstevel@tonic-gate #else 353*0Sstevel@tonic-gate Hptr->hd_version = (unsigned int)PRF_VERSION; 354*0Sstevel@tonic-gate #endif 355*0Sstevel@tonic-gate Hptr->hd_hpc = (caddr_t)(hpc - lpc); 356*0Sstevel@tonic-gate /* LINTED */ 357*0Sstevel@tonic-gate Hptr->hd_psize = (unsigned int)psize; 358*0Sstevel@tonic-gate /* LINTED */ 359*0Sstevel@tonic-gate Hptr->hd_fsize = (unsigned int)Fsize; 360*0Sstevel@tonic-gate Hptr->hd_ncndx = nsym; 361*0Sstevel@tonic-gate Hptr->hd_lcndx = (nsym + 1) * PRF_CGINIT; 362*0Sstevel@tonic-gate } 363*0Sstevel@tonic-gate 364*0Sstevel@tonic-gate (void) prof_mutex_unlock((mutex_t *)&Hptr->hd_mutex, &mask); 365*0Sstevel@tonic-gate /* LINTED */ 366*0Sstevel@tonic-gate Cptr = (L_cgarc *)(addr + hsize + psize); 367*0Sstevel@tonic-gate 368*0Sstevel@tonic-gate /* 369*0Sstevel@tonic-gate * Turn on profiling 370*0Sstevel@tonic-gate */ 371*0Sstevel@tonic-gate /* LINTED */ 372*0Sstevel@tonic-gate profil((unsigned short *)(addr + hsize), 373*0Sstevel@tonic-gate psize, (unsigned long)lpc, (unsigned int) PRF_SCALE); 374*0Sstevel@tonic-gate 375*0Sstevel@tonic-gate return (1); 376*0Sstevel@tonic-gate } 377*0Sstevel@tonic-gate 378*0Sstevel@tonic-gate 379*0Sstevel@tonic-gate uint_t 380*0Sstevel@tonic-gate /* ARGSUSED1 */ 381*0Sstevel@tonic-gate la_objopen(Link_map *lmp, Lmid_t lmid, uintptr_t *cookie) 382*0Sstevel@tonic-gate { 383*0Sstevel@tonic-gate char *objname; 384*0Sstevel@tonic-gate 385*0Sstevel@tonic-gate /* 386*0Sstevel@tonic-gate * This would only occur if the getenv() in la_version() failed. 387*0Sstevel@tonic-gate * at this point there is nothing for us to do. 388*0Sstevel@tonic-gate */ 389*0Sstevel@tonic-gate if (pname == 0) 390*0Sstevel@tonic-gate return (0); 391*0Sstevel@tonic-gate 392*0Sstevel@tonic-gate /* 393*0Sstevel@tonic-gate * Just grab the 'basename' of the object current object for 394*0Sstevel@tonic-gate * comparing against the 'profiled object name' 395*0Sstevel@tonic-gate */ 396*0Sstevel@tonic-gate if (((objname = strrchr(lmp->l_name, '/')) == 0) || 397*0Sstevel@tonic-gate (*(++objname) == 0)) 398*0Sstevel@tonic-gate objname = lmp->l_name; 399*0Sstevel@tonic-gate 400*0Sstevel@tonic-gate /* 401*0Sstevel@tonic-gate * Is this the object we are going to profile. If not 402*0Sstevel@tonic-gate * just set the 'BINDFROM' flag for this object. 403*0Sstevel@tonic-gate */ 404*0Sstevel@tonic-gate if ((strcmp(pname, objname) != 0) && 405*0Sstevel@tonic-gate (strcmp(pname, lmp->l_name) != 0)) 406*0Sstevel@tonic-gate return (LA_FLG_BINDFROM); 407*0Sstevel@tonic-gate 408*0Sstevel@tonic-gate /* 409*0Sstevel@tonic-gate * Don't even try to profile an object that does not have 410*0Sstevel@tonic-gate * auditing enabled on it's link-map. This catches 'ld.so.1'. 411*0Sstevel@tonic-gate */ 412*0Sstevel@tonic-gate if (LIST((Rt_map *)lmp)->lm_flags & LML_FLG_NOAUDIT) 413*0Sstevel@tonic-gate return (LA_FLG_BINDFROM); 414*0Sstevel@tonic-gate 415*0Sstevel@tonic-gate if (profile_open(pname, lmp) == 0) 416*0Sstevel@tonic-gate return (0); 417*0Sstevel@tonic-gate 418*0Sstevel@tonic-gate profcookie = *cookie; 419*0Sstevel@tonic-gate 420*0Sstevel@tonic-gate return (LA_FLG_BINDFROM | LA_FLG_BINDTO); 421*0Sstevel@tonic-gate } 422*0Sstevel@tonic-gate 423*0Sstevel@tonic-gate 424*0Sstevel@tonic-gate 425*0Sstevel@tonic-gate uint_t 426*0Sstevel@tonic-gate la_objclose(uintptr_t *cookie) 427*0Sstevel@tonic-gate { 428*0Sstevel@tonic-gate if (*cookie != profcookie) 429*0Sstevel@tonic-gate return (0); 430*0Sstevel@tonic-gate 431*0Sstevel@tonic-gate profcookie = 0; 432*0Sstevel@tonic-gate /* 433*0Sstevel@tonic-gate * Turn profil() off. 434*0Sstevel@tonic-gate */ 435*0Sstevel@tonic-gate profil(0, 0, 0, 0); 436*0Sstevel@tonic-gate (void) munmap((caddr_t)Hptr, Fsize); 437*0Sstevel@tonic-gate return (0); 438*0Sstevel@tonic-gate } 439*0Sstevel@tonic-gate 440*0Sstevel@tonic-gate 441*0Sstevel@tonic-gate static int 442*0Sstevel@tonic-gate remap_profile(int fd) 443*0Sstevel@tonic-gate { 444*0Sstevel@tonic-gate caddr_t addr; 445*0Sstevel@tonic-gate size_t l_fsize; 446*0Sstevel@tonic-gate 447*0Sstevel@tonic-gate l_fsize = Hptr->hd_fsize; 448*0Sstevel@tonic-gate 449*0Sstevel@tonic-gate if ((addr = (caddr_t)mmap(0, l_fsize, (PROT_READ | PROT_WRITE), 450*0Sstevel@tonic-gate MAP_SHARED, fd, 0)) == (char *)-1) { 451*0Sstevel@tonic-gate int err = errno; 452*0Sstevel@tonic-gate 453*0Sstevel@tonic-gate (void) fprintf(stderr, MSG_INTL(MSG_SYS_MMAP), Profile, 454*0Sstevel@tonic-gate strerror(err)); 455*0Sstevel@tonic-gate return (0); 456*0Sstevel@tonic-gate } 457*0Sstevel@tonic-gate (void) munmap((caddr_t)Hptr, Fsize); 458*0Sstevel@tonic-gate 459*0Sstevel@tonic-gate Fsize = l_fsize; 460*0Sstevel@tonic-gate /* LINTED */ 461*0Sstevel@tonic-gate Hptr = (L_hdr*) addr; 462*0Sstevel@tonic-gate /* LINTED */ 463*0Sstevel@tonic-gate Cptr = (L_cgarc *)(addr + sizeof (L_hdr) + Hptr->hd_psize); 464*0Sstevel@tonic-gate return (1); 465*0Sstevel@tonic-gate } 466*0Sstevel@tonic-gate 467*0Sstevel@tonic-gate 468*0Sstevel@tonic-gate /* 469*0Sstevel@tonic-gate * Update a call graph arc entry. This routine can be called three ways; 470*0Sstevel@tonic-gate * o On initialization from one of the bndr() functions. 471*0Sstevel@tonic-gate * In this case the `to' address is known, and may be used to 472*0Sstevel@tonic-gate * initialize the call graph entry if this function has not 473*0Sstevel@tonic-gate * been entered before. 474*0Sstevel@tonic-gate * o On initial relocation (ie. LD_BIND_NOW). In this case the `to' 475*0Sstevel@tonic-gate * address is known but the `from' isn't. The call graph entry 476*0Sstevel@tonic-gate * is initialized to hold this dummy `to' address, but will be 477*0Sstevel@tonic-gate * re-initialized later when a function is first called. 478*0Sstevel@tonic-gate * o From an initialized plt entry. When profiling, the plt entries 479*0Sstevel@tonic-gate * are filled in with the calling functions symbol index and 480*0Sstevel@tonic-gate * the plt_cg_elf interface function. This interface function 481*0Sstevel@tonic-gate * calls here to determine the `to' functions address, and in so 482*0Sstevel@tonic-gate * doing increments the call count. 483*0Sstevel@tonic-gate */ 484*0Sstevel@tonic-gate uintptr_t 485*0Sstevel@tonic-gate plt_cg_interp(uint_t ndx, caddr_t from, caddr_t to) 486*0Sstevel@tonic-gate { 487*0Sstevel@tonic-gate L_cgarc * cptr, cbucket; 488*0Sstevel@tonic-gate sigset_t mask; 489*0Sstevel@tonic-gate 490*0Sstevel@tonic-gate /* 491*0Sstevel@tonic-gate * If the from address is outside of the address range being profiled, 492*0Sstevel@tonic-gate * simply assign it to the `outside' address. 493*0Sstevel@tonic-gate */ 494*0Sstevel@tonic-gate if (from != PRF_UNKNOWN) { 495*0Sstevel@tonic-gate if ((from > Hpc) || (from < Lpc)) 496*0Sstevel@tonic-gate from = PRF_OUTADDR; 497*0Sstevel@tonic-gate else 498*0Sstevel@tonic-gate from = (caddr_t)(from - Lpc); 499*0Sstevel@tonic-gate } 500*0Sstevel@tonic-gate 501*0Sstevel@tonic-gate (void) prof_mutex_lock((mutex_t *)&Hptr->hd_mutex, &mask); 502*0Sstevel@tonic-gate /* 503*0Sstevel@tonic-gate * Has the buffer grown since last we looked at it (another processes 504*0Sstevel@tonic-gate * could have grown it...). 505*0Sstevel@tonic-gate */ 506*0Sstevel@tonic-gate /* LINTED */ 507*0Sstevel@tonic-gate if (Hptr->hd_fsize != (unsigned int)Fsize) { 508*0Sstevel@tonic-gate int fd; 509*0Sstevel@tonic-gate fd = open(Profile, O_RDWR, 0); 510*0Sstevel@tonic-gate if (remap_profile(fd) == 0) { 511*0Sstevel@tonic-gate (void) prof_mutex_unlock((mutex_t *)&Hptr->hd_mutex, 512*0Sstevel@tonic-gate &mask); 513*0Sstevel@tonic-gate exit(1); 514*0Sstevel@tonic-gate } 515*0Sstevel@tonic-gate (void) close(fd); 516*0Sstevel@tonic-gate } 517*0Sstevel@tonic-gate 518*0Sstevel@tonic-gate cptr = &Cptr[ndx]; 519*0Sstevel@tonic-gate 520*0Sstevel@tonic-gate if (cptr->cg_to == 0) { 521*0Sstevel@tonic-gate /* 522*0Sstevel@tonic-gate * If this is the first time this function has been called we 523*0Sstevel@tonic-gate * got here from one of the binders or an initial relocation 524*0Sstevel@tonic-gate * (ie. LD_BIND_NOW). In this case the `to' address is 525*0Sstevel@tonic-gate * provided. Initialize this functions call graph entry with 526*0Sstevel@tonic-gate * the functions address (retained as a relative offset). 527*0Sstevel@tonic-gate * If we know where the function call originated from 528*0Sstevel@tonic-gate * initialize the count field. 529*0Sstevel@tonic-gate */ 530*0Sstevel@tonic-gate cptr->cg_to = (caddr_t)(to - Lpc); 531*0Sstevel@tonic-gate cptr->cg_from = from; 532*0Sstevel@tonic-gate if (from != PRF_UNKNOWN) 533*0Sstevel@tonic-gate cptr->cg_count = 1; 534*0Sstevel@tonic-gate } else { 535*0Sstevel@tonic-gate /* 536*0Sstevel@tonic-gate * If a function has been called from a previous run, but we 537*0Sstevel@tonic-gate * don't know where we came from (ie. LD_BIND_NOW), then later 538*0Sstevel@tonic-gate * calls through the plt will be able to obtain the required 539*0Sstevel@tonic-gate * functions address, thus there is no need to proceed further. 540*0Sstevel@tonic-gate */ 541*0Sstevel@tonic-gate if (from != PRF_UNKNOWN) { 542*0Sstevel@tonic-gate /* 543*0Sstevel@tonic-gate * If the from addresses match simply bump the count. 544*0Sstevel@tonic-gate * If not scan the link list to find a match for this 545*0Sstevel@tonic-gate * `from' address. If one doesn't exit create a new 546*0Sstevel@tonic-gate * entry and link it in. 547*0Sstevel@tonic-gate */ 548*0Sstevel@tonic-gate while ((cptr->cg_from != from) && 549*0Sstevel@tonic-gate (cptr->cg_from != PRF_UNKNOWN)) { 550*0Sstevel@tonic-gate if (cptr->cg_next != 0) 551*0Sstevel@tonic-gate cptr = &Cptr[cptr->cg_next]; 552*0Sstevel@tonic-gate else { 553*0Sstevel@tonic-gate to = cptr->cg_to; 554*0Sstevel@tonic-gate cptr->cg_next = Hptr->hd_ncndx++; 555*0Sstevel@tonic-gate cptr = &Cptr[cptr->cg_next]; 556*0Sstevel@tonic-gate /* 557*0Sstevel@tonic-gate * If we've run out of file, extend it. 558*0Sstevel@tonic-gate */ 559*0Sstevel@tonic-gate if (Hptr->hd_ncndx == Hptr->hd_lcndx) { 560*0Sstevel@tonic-gate caddr_t addr; 561*0Sstevel@tonic-gate int fd; 562*0Sstevel@tonic-gate 563*0Sstevel@tonic-gate /* LINTED */ 564*0Sstevel@tonic-gate Hptr->hd_fsize += (unsigned int) 565*0Sstevel@tonic-gate PRF_CGNUMB * 566*0Sstevel@tonic-gate sizeof (L_cgarc); 567*0Sstevel@tonic-gate fd = open(Profile, O_RDWR, 0); 568*0Sstevel@tonic-gate if (ftruncate(fd, 569*0Sstevel@tonic-gate Hptr->hd_fsize) == -1) { 570*0Sstevel@tonic-gate int err = errno; 571*0Sstevel@tonic-gate 572*0Sstevel@tonic-gate (void) fprintf(stderr, 573*0Sstevel@tonic-gate MSG_INTL( 574*0Sstevel@tonic-gate MSG_SYS_FTRUNC), 575*0Sstevel@tonic-gate Profile, 576*0Sstevel@tonic-gate strerror(err)); 577*0Sstevel@tonic-gate (void) close(fd); 578*0Sstevel@tonic-gate cptr = &cbucket; 579*0Sstevel@tonic-gate } 580*0Sstevel@tonic-gate /* 581*0Sstevel@tonic-gate * Since the buffer will be 582*0Sstevel@tonic-gate * remapped, we need to be 583*0Sstevel@tonic-gate * prepared to adjust cptr. 584*0Sstevel@tonic-gate */ 585*0Sstevel@tonic-gate addr = (caddr_t)((Addr)cptr - 586*0Sstevel@tonic-gate (Addr)Cptr); 587*0Sstevel@tonic-gate if (remap_profile(fd) == 0) { 588*0Sstevel@tonic-gate (void) prof_mutex_unlock( 589*0Sstevel@tonic-gate (mutex_t *)&Hptr-> 590*0Sstevel@tonic-gate hd_mutex, &mask); 591*0Sstevel@tonic-gate exit(1); 592*0Sstevel@tonic-gate } 593*0Sstevel@tonic-gate cptr = (L_cgarc *)((Addr)addr + 594*0Sstevel@tonic-gate (Addr)Cptr); 595*0Sstevel@tonic-gate (void) close(fd); 596*0Sstevel@tonic-gate Hptr->hd_lcndx += PRF_CGNUMB; 597*0Sstevel@tonic-gate } 598*0Sstevel@tonic-gate cptr->cg_from = from; 599*0Sstevel@tonic-gate cptr->cg_to = to; 600*0Sstevel@tonic-gate } 601*0Sstevel@tonic-gate } 602*0Sstevel@tonic-gate /* 603*0Sstevel@tonic-gate * If we're updating an entry from an unknown call 604*0Sstevel@tonic-gate * address initialize this element, otherwise 605*0Sstevel@tonic-gate * increment the call count. 606*0Sstevel@tonic-gate */ 607*0Sstevel@tonic-gate if (cptr->cg_from == PRF_UNKNOWN) { 608*0Sstevel@tonic-gate cptr->cg_from = from; 609*0Sstevel@tonic-gate cptr->cg_count = 1; 610*0Sstevel@tonic-gate } else 611*0Sstevel@tonic-gate cptr->cg_count++; 612*0Sstevel@tonic-gate } 613*0Sstevel@tonic-gate } 614*0Sstevel@tonic-gate /* 615*0Sstevel@tonic-gate * Return the real address of the function. 616*0Sstevel@tonic-gate */ 617*0Sstevel@tonic-gate (void) prof_mutex_unlock((mutex_t *)&Hptr->hd_mutex, &mask); 618*0Sstevel@tonic-gate 619*0Sstevel@tonic-gate return ((uintptr_t)((Addr)cptr->cg_to + (Addr)Lpc)); 620*0Sstevel@tonic-gate } 621*0Sstevel@tonic-gate 622*0Sstevel@tonic-gate /* ARGSUSED2 */ 623*0Sstevel@tonic-gate #if defined(__sparcv9) 624*0Sstevel@tonic-gate uintptr_t 625*0Sstevel@tonic-gate la_sparcv9_pltenter(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcookie, 626*0Sstevel@tonic-gate uintptr_t *defcookie, La_sparcv9_regs *regset, uint_t *sbflags, 627*0Sstevel@tonic-gate const char *sym_name) 628*0Sstevel@tonic-gate #elif defined(__sparc) 629*0Sstevel@tonic-gate uintptr_t 630*0Sstevel@tonic-gate la_sparcv8_pltenter(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcookie, 631*0Sstevel@tonic-gate uintptr_t *defcookie, La_sparcv8_regs *regset, uint_t *sbflags) 632*0Sstevel@tonic-gate #elif defined(__amd64) 633*0Sstevel@tonic-gate uintptr_t 634*0Sstevel@tonic-gate la_amd64_pltenter(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcookie, 635*0Sstevel@tonic-gate uintptr_t *defcookie, La_amd64_regs *regset, uint_t *sbflags, 636*0Sstevel@tonic-gate const char *sym_name) 637*0Sstevel@tonic-gate #elif defined(__i386) 638*0Sstevel@tonic-gate uintptr_t 639*0Sstevel@tonic-gate la_i86_pltenter(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcookie, 640*0Sstevel@tonic-gate uintptr_t *defcookie, La_i86_regs *regset, uint_t *sbflags) 641*0Sstevel@tonic-gate #else 642*0Sstevel@tonic-gate #error unexpected architecture! 643*0Sstevel@tonic-gate #endif 644*0Sstevel@tonic-gate { 645*0Sstevel@tonic-gate caddr_t from; 646*0Sstevel@tonic-gate 647*0Sstevel@tonic-gate /* 648*0Sstevel@tonic-gate * profiling has been disabled. 649*0Sstevel@tonic-gate */ 650*0Sstevel@tonic-gate if (profcookie == 0) 651*0Sstevel@tonic-gate return (symp->st_value); 652*0Sstevel@tonic-gate #if defined(__sparc) || defined(__sparcv9) 653*0Sstevel@tonic-gate /* 654*0Sstevel@tonic-gate * The callers return address is currently stored in O7 (which 655*0Sstevel@tonic-gate * will become I7 when the window shift occurs). 656*0Sstevel@tonic-gate */ 657*0Sstevel@tonic-gate from = (caddr_t)regset->lr_rego7; 658*0Sstevel@tonic-gate #elif defined(__amd64) 659*0Sstevel@tonic-gate /* 660*0Sstevel@tonic-gate * The callers return address is on the top of the stack for amd64 661*0Sstevel@tonic-gate */ 662*0Sstevel@tonic-gate from = *(caddr_t *)(regset->lr_rsp); 663*0Sstevel@tonic-gate #elif defined(__i386) 664*0Sstevel@tonic-gate /* 665*0Sstevel@tonic-gate * The callers return address is on the top of the stack for i386 666*0Sstevel@tonic-gate */ 667*0Sstevel@tonic-gate from = *(caddr_t *)(regset->lr_esp); 668*0Sstevel@tonic-gate #else 669*0Sstevel@tonic-gate #error unexpected architecture! 670*0Sstevel@tonic-gate #endif 671*0Sstevel@tonic-gate return (plt_cg_interp(symndx, (caddr_t)from, (caddr_t)symp->st_value)); 672*0Sstevel@tonic-gate } 673