1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <errno.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <unistd.h> 33 #include <stropts.h> 34 #include <link.h> 35 #include <sys/types.h> 36 #include <sys/regset.h> 37 #include <sys/frame.h> 38 #include <sys/procfs.h> 39 #include <fcntl.h> 40 #include <signal.h> 41 #include "env.h" 42 #include "hash.h" 43 44 45 typedef struct { 46 float d_time; 47 int d_count; 48 const char *d_symname; 49 } d_entry; 50 51 typedef struct list { 52 d_entry *l_dep; 53 struct list *l_next; 54 } List; 55 56 57 static Elist *bindto_list = 0; 58 static Elist *bindfrom_list = 0; 59 60 static int initialized; 61 extern long long gethrvtime(); 62 63 static const char *progname; 64 static long long starts[1000]; 65 static long long accounted[1000]; /* time accounted for */ 66 static int counter = 0; 67 68 static float total_time = 0.0; 69 static List *list_head = 0; 70 71 static hash *tbl; 72 73 static sigset_t iset; 74 75 static void 76 list_insert(d_entry *dep) 77 { 78 List *new_list; 79 List *cur; 80 List *prev; 81 82 if ((new_list = malloc(sizeof (List))) == 0) { 83 (void) printf("libperfcnt.so: malloc failed - " 84 "can't print summary\n"); 85 exit(1); 86 } 87 new_list->l_dep = dep; 88 89 if (list_head == 0) { 90 list_head = new_list; 91 new_list->l_next = 0; 92 return; 93 } 94 for (cur = list_head, prev = 0; 95 (cur && (cur->l_dep->d_time < dep->d_time)); 96 prev = cur, cur = cur->l_next) 97 ; 98 /* 99 * insert at head of list 100 */ 101 if (prev == 0) { 102 new_list->l_next = list_head; 103 list_head = new_list; 104 return; 105 } 106 prev->l_next = new_list; 107 new_list->l_next = cur; 108 } 109 110 uint_t 111 la_version(uint_t version) 112 { 113 int fd; 114 char buffer[100]; 115 116 if (version > LAV_CURRENT) 117 (void) fprintf(stderr, "perfcnt.so.1: unexpected version: %d\n", 118 version); 119 120 (void) sprintf(buffer, "/proc/%d", (int)getpid()); 121 if ((fd = open(buffer, O_RDWR)) >= 0) { 122 long state = PR_MSACCT; 123 if (ioctl(fd, PIOCSET, &state) == -1) 124 perror("PIOCSET"); 125 (void) close(fd); 126 } 127 128 initialized++; 129 tbl = make_hash(213); 130 131 build_env_list(&bindto_list, (const char *)"PERFCNT_BINDTO"); 132 build_env_list(&bindto_list, (const char *)"PERFCNT_BINDFROM"); 133 134 /* 135 * Initalize iset to the full set of signals to be masked durring 136 * pltenter/pltexit 137 */ 138 (void) sigfillset(&iset); 139 140 return (LAV_CURRENT); 141 } 142 143 144 /* ARGSUSED1 */ 145 uint_t 146 la_objopen(Link_map *lmp, Lmid_t lmid, uintptr_t *cookie) 147 { 148 static int first = 1; 149 uint_t flags = 0; 150 151 if (first) { 152 progname = lmp->l_name; 153 first = 0; 154 } 155 156 if (bindto_list == 0) 157 flags = LA_FLG_BINDTO; 158 else { 159 if (check_list(bindto_list, lmp->l_name)) 160 flags = LA_FLG_BINDTO; 161 } 162 if (bindfrom_list == 0) 163 flags |= LA_FLG_BINDFROM; 164 else { 165 if (check_list(bindfrom_list, lmp->l_name)) 166 flags |= LA_FLG_BINDFROM; 167 } 168 169 return (flags); 170 } 171 172 /* ARGSUSED1 */ 173 #if defined(__sparcv9) 174 uintptr_t 175 la_sparcv9_pltenter(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcookie, 176 uintptr_t *defcookie, La_sparcv9_regs *regset, uint_t *sb_flags, 177 const char *sym_name) 178 #elif defined(__sparc) 179 uintptr_t 180 la_sparcv8_pltenter(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcookie, 181 uintptr_t *defcookie, La_sparcv8_regs *regset, uint_t *sb_flags) 182 #elif defined(__amd64) 183 uintptr_t 184 la_amd64_pltenter(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcookie, 185 uintptr_t *defcookie, La_amd64_regs *regset, uint_t *sb_flags, 186 const char *sym_name) 187 #elif defined(__i386) 188 uintptr_t 189 la_i86_pltenter(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcooke, 190 uintptr_t *defcook, La_i86_regs *regset, uint_t *sb_flags) 191 #endif 192 { 193 accounted[counter] = 0; 194 starts[counter] = gethrvtime(); 195 counter++; 196 return (symp->st_value); 197 } 198 199 200 201 /* ARGSUSED1 */ 202 #if defined(_LP64) 203 /* ARGSUSED */ 204 uintptr_t 205 la_pltexit64(Elf64_Sym *symp, uint_t symndx, uintptr_t *refcookie, 206 uintptr_t *defcookie, uintptr_t retval, const char *sym_name) 207 #else 208 uintptr_t 209 la_pltexit(Elf32_Sym *symp, uint_t symndx, uintptr_t *refcookie, 210 uintptr_t *defcookie, uintptr_t retval) 211 #endif 212 { 213 d_entry **dep; 214 long long time_used; 215 sigset_t oset; 216 #if !defined(_LP64) 217 const char *sym_name = (const char *)symp->st_name; 218 #endif 219 220 (void) sigprocmask(SIG_BLOCK, &iset, &oset); 221 222 counter--; 223 time_used = gethrvtime() - starts[counter]; 224 225 dep = (d_entry **)get_hash(tbl, (char *)sym_name); 226 if (*dep == NULL) { 227 char *ptr = malloc(sizeof (d_entry)); 228 /* LINTED */ 229 (*dep) = (d_entry *)ptr; 230 (*dep)->d_count = 0; 231 (*dep)->d_time = 0.0; 232 (*dep)->d_symname = sym_name; 233 } 234 235 if (counter) 236 accounted[counter - 1] += time_used; 237 238 ((*dep)->d_count)++; 239 (*dep)->d_time += (double)((time_used - accounted[counter]) / 1.0e9); 240 241 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 242 243 return (retval); 244 } 245 246 /* ARGSUSED1 */ 247 static void 248 scanlist(d_entry *dep, void *food, char *name) 249 { 250 total_time += dep->d_time; 251 list_insert(dep); 252 } 253 254 #pragma fini(cleanup) 255 static void 256 cleanup() 257 { 258 List *cur; 259 (void) operate_hash(tbl, scanlist, NULL); 260 (void) printf("\n\nPerf Counts for: %s\n\n", progname); 261 (void) printf("%20s\tc_count\t tim\t\tavg. tim\ttot. %%\n", 262 "SYMBOL"); 263 (void) printf("--------------------------------------------------" 264 "-------------------\n"); 265 for (cur = list_head; cur; cur = cur->l_next) { 266 d_entry *dep = cur->l_dep; 267 float tim = dep->d_time * 1000000; 268 269 (void) printf("%20s\t%d\t%8.2f\t%8.2f\t%2.2f%%\n", 270 dep->d_symname, dep->d_count, tim, tim / dep->d_count, 271 ((dep->d_time / total_time) * 100.0)); 272 } 273 (void) printf("--------------------------------------------------" 274 "-------------------\n"); 275 (void) printf("\t\t\t\t\t\tTotal Time: %8.2f\n", 276 total_time * 1000000); 277 } 278