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]
23 * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
27 * Routines to provide profiling of shared libraries required by the called
36 #include <sys/types.h>
43 #include <sys/param.h>
51 static char Profile
[MAXPATHLEN
]; /* Profile buffer pathname */
52 static char *pname
= 0; /* name of object to profile */
53 static L_hdr
*Hptr
; /* profile buffer header pointer */
54 static L_cgarc
*Cptr
; /* profile buffer call graph pointer */
55 static caddr_t Hpc
, Lpc
; /* Range of addresses being monitored */
56 static size_t Fsize
; /* Size of mapped in profile buffer */
57 uintptr_t profcookie
= 0;
60 * When handling mutex's locally we need to mask signals. The signal
61 * mask is for everything except SIGWAITING.
63 static const sigset_t iset
= { ~0U, ~0U, ~0U, ~0U };
65 static lwp_mutex_t sharedmutex
= SHAREDMUTEX
;
68 prof_mutex_init(lwp_mutex_t
*mp
)
70 (void) memcpy(mp
, &sharedmutex
, sizeof (lwp_mutex_t
));
75 prof_mutex_lock(lwp_mutex_t
*mp
, sigset_t
*oset
)
78 (void) sigprocmask(SIG_BLOCK
, &iset
, oset
);
79 (void) _lwp_mutex_lock(mp
);
84 prof_mutex_unlock(mutex_t
*mp
, sigset_t
*oset
)
86 (void) _lwp_mutex_unlock(mp
);
88 (void) sigprocmask(SIG_SETMASK
, oset
, NULL
);
95 return (dgettext(MSG_ORIG(MSG_SUNW_OST_SGS
), MSG_ORIG(mid
)));
99 * Determine whether a set (of arbitrary size) is in use - used to analyze proc
100 * status information.
103 setisinuse(uint32_t *sp
, uint_t n
)
111 #define prisinuse(sp) \
112 setisinuse((uint32_t *)(sp), \
113 (uint_t)(sizeof (*(sp)) / sizeof (uint32_t)))
116 la_version(uint_t version
)
122 if (version
< LAV_CURRENT
) {
123 (void) fprintf(stderr
, MSG_INTL(MSG_GEN_AUDITVERSION
),
124 LAV_CURRENT
, version
);
125 return (LAV_CURRENT
);
129 * To reduce the potential for deadlock conditions that can arise from
130 * being monitored (say by truss(1)) while setting a lock in the profile
131 * buffer, determine if someone is monitoring us. If so silently
134 if ((fd
= open(MSG_ORIG(MSG_FMT_PROCSELF
), O_RDONLY
)) < 0)
135 return (LAV_CURRENT
);
137 num
= read(fd
, &status
, sizeof (status
));
140 if ((num
!= sizeof (status
)) ||
141 prisinuse(&status
.pr_sigtrace
) || prisinuse(&status
.pr_flttrace
) ||
142 prisinuse(&status
.pr_sysentry
) || prisinuse(&status
.pr_sysexit
)) {
143 return (LAV_CURRENT
);
147 * We're presently not being monitored (although there's no control of
148 * someone attaching to us later), so retrieve the profile target name.
150 if (dlinfo((void *)NULL
, RTLD_DI_PROFILENAME
, &pname
) == -1)
151 (void) fprintf(stderr
, MSG_INTL(MSG_GEN_PROFNOTSET
));
153 return (LAV_CURRENT
);
158 profile_open(const char *fname
, Link_map
*lmp
)
160 size_t hsize
; /* struct hdr size */
161 size_t psize
; /* profile histogram size */
162 size_t csize
; /* call graph array size */
163 size_t msize
; /* size of memory being profiled */
164 int i
, fd
, fixed
= 0;
172 Ehdr
* ehdr
; /* ELF header for file */
173 Phdr
* phdr
; /* program headers for file */
174 Dyn
* dynp
= 0; /* Dynamic section */
175 Word nsym
= 0; /* no. of symtab ntries */
177 if (*Profile
== '\0') {
178 const char *dir
, *suf
;
182 * From the basename of the specified filename generate the
183 * appropriate profile buffer name. The profile file is created
184 * if it does not already exist.
186 if (((tmp
= strrchr(fname
, '/')) != 0) && (*(++tmp
)))
190 suf
= MSG_ORIG(MSG_SUF_PROFILE_64
);
192 suf
= MSG_ORIG(MSG_SUF_PROFILE
);
194 if (dlinfo((void *)NULL
, RTLD_DI_PROFILEOUT
, &dir
) == -1)
195 dir
= MSG_ORIG(MSG_PTH_VARTMP
);
197 (void) snprintf(Profile
, MAXPATHLEN
, MSG_ORIG(MSG_FMT_PROFILE
),
201 if ((fd
= open(Profile
, (O_RDWR
| O_CREAT
), 0666)) == -1) {
203 (void) fprintf(stderr
, MSG_INTL(MSG_SYS_OPEN
), Profile
,
209 * Now we determine the valid pc range for this object. The lpc is easy
210 * (lmp->l_addr), to determine the hpc we must examine the Phdrs.
212 lpc
= hpc
= (caddr_t
)lmp
->l_addr
;
215 if (ehdr
->e_phnum
== 0) {
219 if (ehdr
->e_type
== ET_EXEC
)
222 phdr
= (Phdr
*)(ehdr
->e_phoff
+ lpc
);
223 for (i
= 0; i
< ehdr
->e_phnum
; i
++, phdr
++) {
226 if (phdr
->p_type
== PT_DYNAMIC
) {
227 dynp
= (Dyn
*)phdr
->p_vaddr
;
229 dynp
= (Dyn
*)((unsigned long)dynp
+
235 if (phdr
->p_type
!= PT_LOAD
)
238 _hpc
= (caddr_t
)(phdr
->p_vaddr
+ phdr
->p_memsz
);
240 _hpc
= (caddr_t
)((unsigned long)_hpc
+
252 * In order to determine the number of symbols in the object scan the
253 * dynamic section until we find the DT_HASH entry (hash[1] == symcnt).
256 for (; dynp
->d_tag
!= DT_NULL
; dynp
++) {
259 if (dynp
->d_tag
!= DT_HASH
)
262 hashp
= (unsigned int *)dynp
->d_un
.d_ptr
;
264 hashp
= (unsigned int *)((unsigned long)hashp
+
273 * Determine the (minimum) size of the buffer to allocate
275 Lpc
= lpc
= (caddr_t
)PRF_ROUNDWN((long)lpc
, sizeof (long));
276 Hpc
= hpc
= (caddr_t
)PRF_ROUNDUP((long)hpc
, sizeof (long));
278 hsize
= sizeof (L_hdr
);
279 msize
= (size_t)(hpc
- lpc
);
280 psize
= (size_t)PRF_ROUNDUP((msize
/ PRF_BARSIZE
), sizeof (long));
281 csize
= (nsym
+ 1) * PRF_CGINIT
* sizeof (L_cgarc
);
282 Fsize
= (hsize
+ psize
+ csize
);
285 * If the file size is zero (ie. we just created it), truncate it
286 * to the minimum size.
288 (void) fstat(fd
, &status
);
289 if (status
.st_size
== 0) {
290 if (ftruncate(fd
, Fsize
) == -1) {
292 (void) fprintf(stderr
, MSG_INTL(MSG_SYS_FTRUNC
),
293 Profile
, strerror(err
));
299 Fsize
= status
.st_size
;
304 if ((addr
= (caddr_t
)mmap(0, Fsize
, (PROT_READ
| PROT_WRITE
),
305 MAP_SHARED
, fd
, 0)) == (char *)-1) {
307 (void) fprintf(stderr
, MSG_INTL(MSG_SYS_MMAP
), Profile
,
315 * Initialize the remaining elements of the header. All pc addresses
316 * that are recorded are relative to zero thus allowing the recorded
317 * entries to be correlated with the symbols in the original file,
318 * and to compensate for any differences in where the file is mapped.
319 * If the high pc address has been initialized from a previous run,
320 * and the new entry is different from the original then a new library
321 * must have been installed. In this case bale out.
324 Hptr
= (L_hdr
*)addr
;
327 (void) prof_mutex_init((lwp_mutex_t
*)&Hptr
->hd_mutex
);
329 (void) prof_mutex_lock((mutex_t
*)&Hptr
->hd_mutex
, &mask
);
331 if (Hptr
->hd_hpc
!= (caddr_t
)(hpc
- lpc
)) {
332 (void) fprintf(stderr
, MSG_INTL(MSG_GEN_PROFSZCHG
),
334 (void) prof_mutex_unlock((mutex_t
*)&Hptr
->
336 (void) munmap((caddr_t
)Hptr
, Fsize
);
341 * Initialize the header information as we must have just
342 * created the output file.
344 Hptr
->hd_magic
= (unsigned int)PRF_MAGIC
;
346 Hptr
->hd_version
= (unsigned int)PRF_VERSION_64
;
348 Hptr
->hd_version
= (unsigned int)PRF_VERSION
;
350 Hptr
->hd_hpc
= (caddr_t
)(hpc
- lpc
);
352 Hptr
->hd_psize
= (unsigned int)psize
;
354 Hptr
->hd_fsize
= (unsigned int)Fsize
;
355 Hptr
->hd_ncndx
= nsym
;
356 Hptr
->hd_lcndx
= (nsym
+ 1) * PRF_CGINIT
;
359 (void) prof_mutex_unlock((mutex_t
*)&Hptr
->hd_mutex
, &mask
);
361 Cptr
= (L_cgarc
*)(addr
+ hsize
+ psize
);
367 profil((unsigned short *)(addr
+ hsize
),
368 psize
, (unsigned long)lpc
, (unsigned int) PRF_SCALE
);
376 la_objopen(Link_map
*lmp
, Lmid_t lmid
, uintptr_t *cookie
)
381 * This would only occur if the getenv() in la_version() failed.
382 * at this point there is nothing for us to do.
388 * Just grab the 'basename' of the object current object for
389 * comparing against the 'profiled object name'
391 if (((objname
= strrchr(lmp
->l_name
, '/')) == 0) ||
393 objname
= lmp
->l_name
;
396 * Is this the object we are going to profile. If not
397 * just set the 'BINDFROM' flag for this object.
399 if ((strcmp(pname
, objname
) != 0) &&
400 (strcmp(pname
, lmp
->l_name
) != 0))
401 return (LA_FLG_BINDFROM
);
404 * Don't even try to profile an object that does not have
405 * auditing enabled on it's link-map. This catches 'ld.so.1'.
407 if (LIST(LINKMAP_TO_RTMAP(lmp
))->lm_tflags
& LML_TFLG_NOAUDIT
)
408 return (LA_FLG_BINDFROM
);
410 if (profile_open(pname
, lmp
) == 0)
413 profcookie
= *cookie
;
415 return (LA_FLG_BINDFROM
| LA_FLG_BINDTO
);
421 la_objclose(uintptr_t *cookie
)
423 if (*cookie
!= profcookie
)
431 (void) munmap((caddr_t
)Hptr
, Fsize
);
437 remap_profile(int fd
)
442 l_fsize
= Hptr
->hd_fsize
;
444 if ((addr
= (caddr_t
)mmap(0, l_fsize
, (PROT_READ
| PROT_WRITE
),
445 MAP_SHARED
, fd
, 0)) == (char *)-1) {
448 (void) fprintf(stderr
, MSG_INTL(MSG_SYS_MMAP
), Profile
,
452 (void) munmap((caddr_t
)Hptr
, Fsize
);
456 Hptr
= (L_hdr
*) addr
;
458 Cptr
= (L_cgarc
*)(addr
+ sizeof (L_hdr
) + Hptr
->hd_psize
);
464 * Update a call graph arc entry. This routine can be called three ways;
465 * o On initialization from one of the bndr() functions.
466 * In this case the `to' address is known, and may be used to
467 * initialize the call graph entry if this function has not
468 * been entered before.
469 * o On initial relocation (ie. LD_BIND_NOW). In this case the `to'
470 * address is known but the `from' isn't. The call graph entry
471 * is initialized to hold this dummy `to' address, but will be
472 * re-initialized later when a function is first called.
473 * o From an initialized plt entry. When profiling, the plt entries
474 * are filled in with the calling functions symbol index and
475 * the plt_cg_elf interface function. This interface function
476 * calls here to determine the `to' functions address, and in so
477 * doing increments the call count.
480 plt_cg_interp(uint_t ndx
, caddr_t from
, caddr_t to
)
482 L_cgarc
* cptr
, cbucket
;
486 * If the from address is outside of the address range being profiled,
487 * simply assign it to the `outside' address.
489 if (from
!= PRF_UNKNOWN
) {
490 if ((from
> Hpc
) || (from
< Lpc
))
493 from
= (caddr_t
)(from
- Lpc
);
496 (void) prof_mutex_lock((mutex_t
*)&Hptr
->hd_mutex
, &mask
);
498 * Has the buffer grown since last we looked at it (another processes
499 * could have grown it...).
502 if (Hptr
->hd_fsize
!= (unsigned int)Fsize
) {
504 fd
= open(Profile
, O_RDWR
, 0);
505 if (remap_profile(fd
) == 0) {
506 (void) prof_mutex_unlock((mutex_t
*)&Hptr
->hd_mutex
,
515 if (cptr
->cg_to
== 0) {
517 * If this is the first time this function has been called we
518 * got here from one of the binders or an initial relocation
519 * (ie. LD_BIND_NOW). In this case the `to' address is
520 * provided. Initialize this functions call graph entry with
521 * the functions address (retained as a relative offset).
522 * If we know where the function call originated from
523 * initialize the count field.
525 cptr
->cg_to
= (caddr_t
)(to
- Lpc
);
526 cptr
->cg_from
= from
;
527 if (from
!= PRF_UNKNOWN
)
531 * If a function has been called from a previous run, but we
532 * don't know where we came from (ie. LD_BIND_NOW), then later
533 * calls through the plt will be able to obtain the required
534 * functions address, thus there is no need to proceed further.
536 if (from
!= PRF_UNKNOWN
) {
538 * If the from addresses match simply bump the count.
539 * If not scan the link list to find a match for this
540 * `from' address. If one doesn't exit create a new
541 * entry and link it in.
543 while ((cptr
->cg_from
!= from
) &&
544 (cptr
->cg_from
!= PRF_UNKNOWN
)) {
545 if (cptr
->cg_next
!= 0)
546 cptr
= &Cptr
[cptr
->cg_next
];
549 cptr
->cg_next
= Hptr
->hd_ncndx
++;
550 cptr
= &Cptr
[cptr
->cg_next
];
552 * If we've run out of file, extend it.
554 if (Hptr
->hd_ncndx
== Hptr
->hd_lcndx
) {
559 Hptr
->hd_fsize
+= (unsigned int)
562 fd
= open(Profile
, O_RDWR
, 0);
564 Hptr
->hd_fsize
) == -1) {
567 (void) fprintf(stderr
,
576 * Since the buffer will be
577 * remapped, we need to be
578 * prepared to adjust cptr.
580 addr
= (caddr_t
)((Addr
)cptr
-
582 if (remap_profile(fd
) == 0) {
584 (void) prof_mutex_unlock(
589 cptr
= (L_cgarc
*)((Addr
)addr
+
592 Hptr
->hd_lcndx
+= PRF_CGNUMB
;
594 cptr
->cg_from
= from
;
599 * If we're updating an entry from an unknown call
600 * address initialize this element, otherwise
601 * increment the call count.
603 if (cptr
->cg_from
== PRF_UNKNOWN
) {
604 cptr
->cg_from
= from
;
611 * Return the real address of the function.
613 (void) prof_mutex_unlock((mutex_t
*)&Hptr
->hd_mutex
, &mask
);
615 return ((uintptr_t)((Addr
)cptr
->cg_to
+ (Addr
)Lpc
));
619 #if defined(__sparcv9)
621 la_sparcv9_pltenter(Elf64_Sym
*symp
, uint_t symndx
, uintptr_t *refcookie
,
622 uintptr_t *defcookie
, La_sparcv9_regs
*regset
, uint_t
*sbflags
,
623 const char *sym_name
)
624 #elif defined(__sparc)
626 la_sparcv8_pltenter(Elf32_Sym
*symp
, uint_t symndx
, uintptr_t *refcookie
,
627 uintptr_t *defcookie
, La_sparcv8_regs
*regset
, uint_t
*sbflags
)
628 #elif defined(__amd64)
630 la_amd64_pltenter(Elf64_Sym
*symp
, uint_t symndx
, uintptr_t *refcookie
,
631 uintptr_t *defcookie
, La_amd64_regs
*regset
, uint_t
*sbflags
,
632 const char *sym_name
)
633 #elif defined(__i386)
635 la_i86_pltenter(Elf32_Sym
*symp
, uint_t symndx
, uintptr_t *refcookie
,
636 uintptr_t *defcookie
, La_i86_regs
*regset
, uint_t
*sbflags
)
638 #error unexpected architecture!
644 * profiling has been disabled.
647 return (symp
->st_value
);
650 * The callers return address is currently stored in O7 (which
651 * will become I7 when the window shift occurs).
653 from
= (caddr_t
)regset
->lr_rego7
;
654 #elif defined(__amd64)
656 * The callers return address is on the top of the stack for amd64
658 from
= *(caddr_t
*)(regset
->lr_rsp
);
659 #elif defined(__i386)
661 * The callers return address is on the top of the stack for i386
663 from
= *(caddr_t
*)(regset
->lr_esp
);
665 #error unexpected architecture!
667 return (plt_cg_interp(symndx
, (caddr_t
)from
, (caddr_t
)symp
->st_value
));