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