mcount.c revision 8845:4be14673b9bf
1/* 2 * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26#include <stdio.h> 27#include <sys/mman.h> 28#include <dlfcn.h> 29#include <libelf.h> 30#include <strings.h> 31#include <fcntl.h> 32#include <sys/param.h> 33#include <stdlib.h> 34#include <thread.h> 35#include <synch.h> 36#include <stdarg.h> 37#include <unistd.h> 38 39#define TRUE 1 40#define FALSE 0 41 42/* 32/64 bit build issues. */ 43 44#ifdef _LP64 45 #define ElfXX_Sym Elf64_Sym 46 #define ElfXX_Ehdr Elf64_Ehdr 47 #define ElfXX_Shdr Elf64_Shdr 48 #define elfXX_getehdr elf64_getehdr 49 #define ElfXX_Addr Elf64_Addr 50 #define ELFXX_ST_TYPE ELF64_ST_TYPE 51 #define ELFXX_ST_BIND ELF64_ST_BIND 52 #define elfXX_getshdr elf64_getshdr 53#else 54 #define ElfXX_Sym Elf32_Sym 55 #define ElfXX_Ehdr Elf32_Ehdr 56 #define ElfXX_Shdr Elf32_Shdr 57 #define elfXX_getehdr elf32_getehdr 58 #define ElfXX_Addr Elf32_Addr 59 #define ELFXX_ST_TYPE ELF32_ST_TYPE 60 #define ELFXX_ST_BIND ELF32_ST_BIND 61 #define elfXX_getshdr elf32_getshdr 62#endif 63 64extern void *_getReturnAddr(void); 65 66 67 68typedef struct StabEntry { 69 unsigned n_strx; 70 unsigned char n_type; 71 char n_other; 72 short n_desc; 73 unsigned n_value; 74} StabEntry; 75 76 77typedef struct SymChain { 78 struct SymChain *next; 79 ElfXX_Sym *sym; 80} SymChain; 81 82 83typedef struct ObjFileList { 84 struct ObjFileList *next; 85 const char *objFileName; 86 int nameLen; 87} ObjFileList; 88 89 90typedef struct ElfInfo { 91 const char *fullName; 92 const char *baseName; 93 FILE *outFile; 94 int fd; 95 Elf *elf; 96 Elf_Data *sectionStringData; 97 Elf_Data *symData; 98 Elf_Data *symStringData; 99 int symCount; 100 SymChain *symChainHead; 101 Elf_Data *stabData; 102 Elf_Data *stabStringData; 103 int stabCount; 104 ObjFileList *objFileList; 105} ElfInfo; 106 107 108 109#define COUNT_BUF_SIZE (16*1024*1024) 110 111#define ENTRY_CHAIN_BUCKETS 4999 112 113static int *countBuf; 114static void *textOffset; 115static const char *libFileName; 116 117 118 119static void fail(const char *err, ...) 120{ 121 va_list ap; 122 va_start(ap, err); 123 vfprintf(stderr, err, ap); 124 fflush(stderr); 125 va_end(ap); 126} 127 128 129 130static const char *getSymString(ElfInfo *elfInfo, int index) 131{ 132 return (const char *)elfInfo->symStringData->d_buf + index; 133} 134 135 136static const char *getStabString(ElfInfo *elfInfo, int index) 137{ 138 return (const char *)elfInfo->stabStringData->d_buf + index; 139} 140 141 142static const char *getSectionString(ElfInfo *elfInfo, int index) 143{ 144 return (const char *)elfInfo->sectionStringData->d_buf + index; 145} 146 147 148static const char *makeObjFileList(ElfInfo *elfInfo) 149{ 150 int i; 151 const char *file; 152 unsigned offset, lastOffset; 153 ObjFileList *objFileList; 154 155 file = NULL; 156 offset = lastOffset = 0; 157 for (i = 0; i < elfInfo->stabCount; ++i) { 158 StabEntry *stab = ((StabEntry *)elfInfo->stabData->d_buf) + i; 159 160 if (stab->n_type == 0 /* N_UNDEF */) { 161 offset = lastOffset; 162 lastOffset += stab-> n_value; 163 } 164 else if (stab->n_type == 0x38 /* N_OBJ */) { 165 file = getStabString(elfInfo, stab->n_strx + offset); 166 objFileList = (ObjFileList *)malloc(sizeof (ObjFileList)); 167 objFileList->objFileName = file; 168 /*fprintf(stderr,"new obj file %s.\n", file);*/ 169 objFileList->nameLen = strlen(file); 170 objFileList->next = elfInfo->objFileList; 171 elfInfo->objFileList = objFileList; 172 } 173 } 174 return NULL; 175} 176 177 178static ElfInfo *createElfInfo(const char *fullName) 179{ 180 ElfInfo *elfInfo; 181 ElfXX_Ehdr *ehdr; 182 Elf_Scn *sectionStringSection; 183 Elf_Scn *stringSection; 184 Elf_Scn *symSection; 185 ElfXX_Shdr *symHeader; 186 Elf_Scn *stabSection; 187 ElfXX_Shdr *stabHeader; 188 ElfXX_Shdr *stringHeader; 189 Elf *elf; 190 const char *p; 191 192 /*fprintf(stderr, "# mapfile info for %s.\n", fullName);*/ 193 elfInfo = (ElfInfo *)malloc(sizeof (ElfInfo)); 194 memset(elfInfo, 0, sizeof (ElfInfo)); 195 elfInfo->fullName = strdup(fullName); 196 p = rindex(elfInfo->fullName, '/'); 197 elfInfo->baseName = (p == NULL) ? elfInfo->fullName : p + 1; 198 199 /* Open the ELF file. Get section headers. */ 200 201 elf_version(EV_CURRENT); 202 elfInfo->fd = open(fullName, O_RDONLY); 203 if (elfInfo->fd < 0) 204 fail("Unable to open ELF file %s.\n", fullName); 205 elf = elf_begin(elfInfo->fd, ELF_C_READ, (Elf *)0); 206 if (elf == NULL) 207 fail("elf_begin failed.\n"); 208 ehdr = elfXX_getehdr(elf); 209 sectionStringSection = elf_getscn(elf, ehdr->e_shstrndx); 210 elfInfo->sectionStringData = elf_getdata(sectionStringSection, NULL); 211 212 /* Find the symbol table section. */ 213 214 symSection = NULL; 215 while ((symSection = elf_nextscn(elf, symSection)) != NULL) { 216 symHeader = elfXX_getshdr(symSection); 217 p = getSectionString(elfInfo, symHeader->sh_name); 218 if (strcmp(p, ".symtab") == 0) 219 break; 220 } 221 if (symSection == NULL) 222 fail("Unable to find symbol table.\n"); 223 224 elfInfo->symData = elf_getdata(symSection, NULL); 225 elfInfo->symCount = elfInfo->symData->d_size / sizeof (ElfXX_Sym); 226 227 /* Find the string section. */ 228 229 stringSection = NULL; 230 while ((stringSection = elf_nextscn(elf, stringSection)) != NULL) { 231 stringHeader = elfXX_getshdr(stringSection); 232 p = getSectionString(elfInfo, stringHeader->sh_name); 233 if (strcmp(p, ".strtab") == 0) 234 break; 235 } 236 if (stringSection == NULL) 237 fail("Unable to find string table.\n"); 238 239 elfInfo->symStringData = elf_getdata(stringSection, NULL); 240 elfInfo->symChainHead = NULL; 241 242 /* Find the stab section. */ 243 244 stabSection = NULL; 245 while ((stabSection = elf_nextscn(elf, stabSection)) != NULL) { 246 stabHeader = elfXX_getshdr(stabSection); 247 p = getSectionString(elfInfo, stabHeader->sh_name); 248 if (strcmp(p, ".stab.index") == 0) 249 break; 250 } 251 if (stabSection == NULL) 252 fail("Unable to find .stab.index.\n"); 253 254 elfInfo->stabData = elf_getdata(stabSection, NULL); 255 elfInfo->stabCount = elfInfo->stabData->d_size / sizeof (StabEntry); 256 257 /* Find the string section. */ 258 259 stringSection = NULL; 260 while ((stringSection = elf_nextscn(elf, stringSection)) != NULL) { 261 stringHeader = elfXX_getshdr(stringSection); 262 p = getSectionString(elfInfo, stringHeader->sh_name); 263 if (strcmp(p, ".stab.indexstr") == 0) 264 break; 265 } 266 if (stringSection == NULL) 267 fail("Unable to find .stab.indexstr table.\n"); 268 269 elfInfo->stabStringData = elf_getdata(stringSection, NULL); 270 makeObjFileList(elfInfo); 271 272 return elfInfo; 273} 274 275 276static const char *identifyFile(ElfInfo *elfInfo, const char *name) 277{ 278 int i; 279 const char *file; 280 const char *sourceFile; 281 unsigned offset, lastOffset; 282 const char *lastOptions; 283 char *buf; 284 285 file = NULL; 286 lastOptions = NULL; 287 offset = lastOffset = 0; 288 for (i = 0; i < elfInfo->stabCount; ++i) { 289 StabEntry *stab = ((StabEntry *)elfInfo->stabData->d_buf) + i; 290 291 if (stab->n_type == 0 /* N_UNDEF */) { 292 offset = lastOffset; 293 lastOffset += stab-> n_value; 294 file = NULL; /* C++ output files seem not to have N_OBJ fields.*/ 295 lastOptions = NULL; 296 sourceFile = getStabString(elfInfo, stab->n_strx + offset); 297 } 298 else if (stab->n_type == 0x24 /* N_FUN */) { 299 const char *stabName; 300 char *p1, *p2; 301 302 stabName = getStabString(elfInfo, stab->n_strx + offset); 303 if (strcmp (stabName, name) == 0) { 304 305 if (file != NULL) 306 return file; 307 308 if (lastOptions == NULL) 309 return NULL; 310 311 p1 = strstr(lastOptions, ";ptr"); 312 if (p1 == NULL) 313 return NULL; 314 p1 += 4; 315 p2 = index(p1, ';'); 316 if (p2 == NULL) 317 return NULL; 318 319 buf = (char *)malloc(p2 - p1 + strlen(sourceFile) + 10); 320 strncpy(buf, p1, p2 - p1); 321 buf[p2-p1] = '/'; 322 strcpy(buf + (p2 - p1) + 1, sourceFile); 323 p1 = rindex(buf, '.'); 324 if (p1 == NULL) 325 return NULL; 326 p1[1] = 'o'; 327 p1[2] = '\0'; 328 return buf; 329 } 330 } 331 else if (stab->n_type == 0x3c /* N_OPT */) { 332 lastOptions = getStabString(elfInfo, stab->n_strx + offset); 333 } 334 else if (stab->n_type == 0x38 /* N_OBJ */) { 335 file = getStabString(elfInfo, stab->n_strx + offset); 336 } 337 } 338 return NULL; 339} 340 341 342static const char *checkObjFileList(ElfInfo *elfInfo, const char *file) { 343 ObjFileList *objFileList; 344 int len = strlen(file); 345 int nameLen; 346 const char *objFileName; 347 348 /*fprintf(stderr, "checkObjFileList(%s).\n", file);*/ 349 for (objFileList = elfInfo->objFileList; objFileList != NULL; 350 objFileList = objFileList->next) { 351 352 objFileName = objFileList->objFileName; 353 nameLen = objFileList->nameLen; 354 if (strcmp(objFileName +nameLen - len, file) != 0) 355 continue; 356 357 if (len == nameLen) 358 return file; 359 360 if (len > nameLen) 361 continue; 362 363 if (*(objFileName + nameLen - len - 1) == '/') 364 return objFileName; 365 } 366 return file; 367} 368 369 370static void identifySymbol(ElfInfo *elfInfo, ElfXX_Addr value, int count) 371{ 372 int i; 373 ElfXX_Sym *bestFunc = NULL; 374 ElfXX_Sym *bestFile = NULL; 375 ElfXX_Sym *lastFile = NULL; 376 char fileName[MAXPATHLEN]; 377 char buf[4096]; 378 const char *file; 379 SymChain *chain; 380 const char *format; 381 382 for (i = 0; i < elfInfo->symCount; ++i) { 383 ElfXX_Sym *sym = ((ElfXX_Sym *)elfInfo->symData->d_buf) + i; 384 if (ELFXX_ST_TYPE(sym->st_info) == STT_FUNC) { 385 386 if (sym->st_shndx == SHN_UNDEF) 387 continue; 388 389 if (sym->st_value > value) 390 continue; 391 392 if (bestFunc != NULL) { 393 394 if (sym->st_value < bestFunc->st_value) 395 continue; 396 397 /* 398 * If we have two symbols of equal value, we have a problem - 399 * we must pick the "right" one, which is the one the compiler 400 * used to generate the section name with -xF. 401 * 402 * The compiler has the nasty habit of generating two 403 * mangled names for some C++ functions. 404 * 405 * Try - picking the shortest name. 406 */ 407 408 if (sym->st_value == bestFunc->st_value) { 409 if (strlen(getSymString(elfInfo, bestFunc->st_name)) < 410 strlen(getSymString(elfInfo, sym->st_name))) 411 continue; 412 } 413 414 } 415 bestFunc = sym; 416 bestFile = lastFile; 417 } 418 else if (ELFXX_ST_TYPE(sym->st_info) == STT_FILE) { 419 lastFile = sym; 420 } 421 } 422 423 if (bestFunc == NULL) 424 fail("Unable to find symbol for address 0x%x.\n", value); 425 426 for (chain = elfInfo->symChainHead; chain != NULL; chain = chain->next) { 427 if (chain->sym == bestFunc) 428 return; 429 } 430 chain = (SymChain *)malloc(sizeof (SymChain)); 431 chain->sym = bestFunc; 432 chain->next = elfInfo->symChainHead; 433 elfInfo->symChainHead = chain; 434 435 436 if (ELFXX_ST_BIND(bestFunc->st_info) == STB_GLOBAL) 437 file = ""; 438 else { 439 const char *name = getSymString(elfInfo, bestFunc->st_name); 440 file = identifyFile(elfInfo, name); 441 if (file == NULL) { 442 if (bestFile == NULL) { 443 file = "notFound"; 444 fail("Failed to identify %s.\n", name); 445 } else { 446 char *suffix; 447 fileName[0] = ':'; 448 fileName[1] = ' '; 449 file = getSymString(elfInfo, bestFile->st_name); 450 strncpy(fileName+2, file, MAXPATHLEN-3); 451 suffix = rindex(fileName, '.'); 452 if (suffix == NULL) 453 fail("no file name suffix?"); 454 suffix[1] = 'o'; 455 suffix[2] = '\0'; 456 457 file = checkObjFileList(elfInfo, fileName+2); 458 if (file != fileName+2) 459 strncpy(fileName+2, file, MAXPATHLEN-3); 460 461 file = fileName; 462 } 463 } else { 464 fileName[0] = ':'; 465 fileName[1] = ' '; 466 strncpy(fileName + 2, file, MAXPATHLEN-3); 467 file = fileName; 468 } 469 } 470 format = "text: .text%%%s%s;\n"; 471 i = snprintf(buf, sizeof buf, format, 472 bestFunc ? getSymString(elfInfo, bestFunc->st_name) : "notFound", 473 file); 474 write(2, buf, i); 475} 476 477 478static mutex_t mutex; 479static int orderByCount = FALSE; 480 481 482static void init_mcount(void) 483{ 484 mutex_init(&mutex, USYNC_THREAD, NULL); 485} 486 487#pragma init(init_mcount) 488 489 490typedef struct CountAddrPair { 491 int count; 492 unsigned int addr; 493} CountAddrPair; 494 495 496static int compareCounts(const void *a, const void *b) { 497 return ((CountAddrPair *)b)->count - ((CountAddrPair *)a)->count; 498} 499 500static int compareCountsReverse(const void *a, const void *b) { 501 return ((CountAddrPair *)a)->count - ((CountAddrPair *)b)->count; 502} 503 504 505static void doCounts(void) { 506 unsigned int i; 507 int n; 508 int nMethods; 509 int nMethods2; 510 CountAddrPair *pairs; 511 ElfInfo *elfInfo; 512 513 elfInfo = createElfInfo(libFileName); 514 515 nMethods = 0; 516 for (i = 0; i < COUNT_BUF_SIZE >> 2; ++i) { 517 n = countBuf[i]; 518 if (n > 0) 519 ++nMethods; 520 } 521 pairs = (CountAddrPair *)malloc(sizeof(CountAddrPair) * nMethods); 522 nMethods2 = 0; 523 for (i = 0; i < COUNT_BUF_SIZE >> 2; ++i) { 524 n = countBuf[i]; 525 if (n > 0) { 526 pairs[nMethods2].count = n; 527 pairs[nMethods2].addr = i << 2; 528 ++nMethods2; 529 if (nMethods2 > nMethods) { 530 fprintf(stderr, "Number of methods detected increased after" 531 " the atexit call.\n"); 532 break; 533 } 534 } 535 } 536 if (orderByCount) { 537 qsort(pairs, nMethods, sizeof pairs[0], &compareCounts); 538 for (i = 0; i < nMethods; ++i) { 539 identifySymbol(elfInfo, pairs[i].addr, pairs[i].count); 540 } 541 } 542 else { 543 qsort(pairs, nMethods, sizeof pairs[0], &compareCountsReverse); 544 for (i = 0; i < nMethods; ++i) { 545 identifySymbol(elfInfo, pairs[i].addr, 0); 546 } 547 } 548} 549 550 551static void __mcount(void *i0) 552{ 553 Dl_info info; 554 unsigned int offset; 555 int *p; 556 static int callsCounted = 0; 557 static int initialized = FALSE; 558 559 if (!initialized) { 560 dladdr(i0, &info); 561 libFileName = info.dli_fname; 562#if 0 563 fprintf(stderr, "Profiling %s\n", libFileName); 564#endif 565 textOffset = info.dli_fbase; 566 if (getenv("MCOUNT_ORDER_BY_COUNT") != NULL) { 567 orderByCount = TRUE; 568 } 569 countBuf = (int *)malloc(COUNT_BUF_SIZE); 570 memset(countBuf, 0, COUNT_BUF_SIZE); 571 atexit(&doCounts); 572 initialized = TRUE; 573 } 574 575 if ((uintptr_t)i0 < (uintptr_t)textOffset) { 576 fprintf(stderr, "mcount: function being profiled out of range????\n"); 577 fprintf(stderr, " profiling more than one library at once????\n"); 578#if 0 579 dladdr(i0, &info); 580 fprintf(stderr, "Problem with %s in %s ???\n", 581 info.dli_sname, info.dli_fname); 582#endif 583 fflush(stderr); 584 exit(666); 585 } 586 offset = ((uintptr_t)i0) - ((uintptr_t)textOffset); 587 if (offset > COUNT_BUF_SIZE) { 588 fprintf(stderr, "mcount: internal buffer too small for test.\n"); 589 fprintf(stderr, " or function being profiled out of range????\n"); 590 fprintf(stderr, " or profiling more than one library at once????\n"); 591#if 0 592 dladdr(i0, &info); 593 fprintf(stderr, "Problem with %s in %s ???\n", 594 info.dli_sname, info.dli_fname); 595#endif 596 fflush(stderr); 597 exit(666); 598 } 599 600 p = &countBuf[offset >>2]; 601 if (orderByCount) { 602 ++*p; 603 } 604 else { 605 if (*p == 0) { 606 *p = ++callsCounted; 607 } 608 } 609} 610 611 612void _mcount(void) 613{ 614 __mcount(_getReturnAddr()); 615} 616 617 618void mcount(void) 619{ 620 __mcount(_getReturnAddr()); 621} 622