| /* |
| * Copyright (c) 2000, 2004, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code is distributed in the hope that it will be useful, but WITHOUT |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| #include <stdio.h> |
| #include <sys/mman.h> |
| #include <dlfcn.h> |
| #include <libelf.h> |
| #include <strings.h> |
| #include <fcntl.h> |
| #include <sys/param.h> |
| #include <stdlib.h> |
| #include <thread.h> |
| #include <synch.h> |
| #include <stdarg.h> |
| #include <unistd.h> |
| |
| #define TRUE 1 |
| #define FALSE 0 |
| |
| /* 32/64 bit build issues. */ |
| |
| #ifdef _LP64 |
| #define ElfXX_Sym Elf64_Sym |
| #define ElfXX_Ehdr Elf64_Ehdr |
| #define ElfXX_Shdr Elf64_Shdr |
| #define elfXX_getehdr elf64_getehdr |
| #define ElfXX_Addr Elf64_Addr |
| #define ELFXX_ST_TYPE ELF64_ST_TYPE |
| #define ELFXX_ST_BIND ELF64_ST_BIND |
| #define elfXX_getshdr elf64_getshdr |
| #else |
| #define ElfXX_Sym Elf32_Sym |
| #define ElfXX_Ehdr Elf32_Ehdr |
| #define ElfXX_Shdr Elf32_Shdr |
| #define elfXX_getehdr elf32_getehdr |
| #define ElfXX_Addr Elf32_Addr |
| #define ELFXX_ST_TYPE ELF32_ST_TYPE |
| #define ELFXX_ST_BIND ELF32_ST_BIND |
| #define elfXX_getshdr elf32_getshdr |
| #endif |
| |
| extern void *_getReturnAddr(void); |
| |
| |
| |
| typedef struct StabEntry { |
| unsigned n_strx; |
| unsigned char n_type; |
| char n_other; |
| short n_desc; |
| unsigned n_value; |
| } StabEntry; |
| |
| |
| typedef struct SymChain { |
| struct SymChain *next; |
| ElfXX_Sym *sym; |
| } SymChain; |
| |
| |
| typedef struct ObjFileList { |
| struct ObjFileList *next; |
| const char *objFileName; |
| int nameLen; |
| } ObjFileList; |
| |
| |
| typedef struct ElfInfo { |
| const char *fullName; |
| const char *baseName; |
| FILE *outFile; |
| int fd; |
| Elf *elf; |
| Elf_Data *sectionStringData; |
| Elf_Data *symData; |
| Elf_Data *symStringData; |
| int symCount; |
| SymChain *symChainHead; |
| Elf_Data *stabData; |
| Elf_Data *stabStringData; |
| int stabCount; |
| ObjFileList *objFileList; |
| } ElfInfo; |
| |
| |
| |
| #define COUNT_BUF_SIZE (16*1024*1024) |
| |
| #define ENTRY_CHAIN_BUCKETS 4999 |
| |
| static int *countBuf; |
| static void *textOffset; |
| static const char *libFileName; |
| |
| |
| |
| static void fail(const char *err, ...) |
| { |
| va_list ap; |
| va_start(ap, err); |
| vfprintf(stderr, err, ap); |
| fflush(stderr); |
| va_end(ap); |
| } |
| |
| |
| |
| static const char *getSymString(ElfInfo *elfInfo, int index) |
| { |
| return (const char *)elfInfo->symStringData->d_buf + index; |
| } |
| |
| |
| static const char *getStabString(ElfInfo *elfInfo, int index) |
| { |
| return (const char *)elfInfo->stabStringData->d_buf + index; |
| } |
| |
| |
| static const char *getSectionString(ElfInfo *elfInfo, int index) |
| { |
| return (const char *)elfInfo->sectionStringData->d_buf + index; |
| } |
| |
| |
| static const char *makeObjFileList(ElfInfo *elfInfo) |
| { |
| int i; |
| const char *file; |
| unsigned offset, lastOffset; |
| ObjFileList *objFileList; |
| |
| file = NULL; |
| offset = lastOffset = 0; |
| for (i = 0; i < elfInfo->stabCount; ++i) { |
| StabEntry *stab = ((StabEntry *)elfInfo->stabData->d_buf) + i; |
| |
| if (stab->n_type == 0 /* N_UNDEF */) { |
| offset = lastOffset; |
| lastOffset += stab-> n_value; |
| } |
| else if (stab->n_type == 0x38 /* N_OBJ */) { |
| file = getStabString(elfInfo, stab->n_strx + offset); |
| objFileList = (ObjFileList *)malloc(sizeof (ObjFileList)); |
| objFileList->objFileName = file; |
| /*fprintf(stderr,"new obj file %s.\n", file);*/ |
| objFileList->nameLen = strlen(file); |
| objFileList->next = elfInfo->objFileList; |
| elfInfo->objFileList = objFileList; |
| } |
| } |
| return NULL; |
| } |
| |
| |
| static ElfInfo *createElfInfo(const char *fullName) |
| { |
| ElfInfo *elfInfo; |
| ElfXX_Ehdr *ehdr; |
| Elf_Scn *sectionStringSection; |
| Elf_Scn *stringSection; |
| Elf_Scn *symSection; |
| ElfXX_Shdr *symHeader; |
| Elf_Scn *stabSection; |
| ElfXX_Shdr *stabHeader; |
| ElfXX_Shdr *stringHeader; |
| Elf *elf; |
| const char *p; |
| |
| /*fprintf(stderr, "# mapfile info for %s.\n", fullName);*/ |
| elfInfo = (ElfInfo *)malloc(sizeof (ElfInfo)); |
| memset(elfInfo, 0, sizeof (ElfInfo)); |
| elfInfo->fullName = strdup(fullName); |
| p = rindex(elfInfo->fullName, '/'); |
| elfInfo->baseName = (p == NULL) ? elfInfo->fullName : p + 1; |
| |
| /* Open the ELF file. Get section headers. */ |
| |
| elf_version(EV_CURRENT); |
| elfInfo->fd = open(fullName, O_RDONLY); |
| if (elfInfo->fd < 0) |
| fail("Unable to open ELF file %s.\n", fullName); |
| elf = elf_begin(elfInfo->fd, ELF_C_READ, (Elf *)0); |
| if (elf == NULL) |
| fail("elf_begin failed.\n"); |
| ehdr = elfXX_getehdr(elf); |
| sectionStringSection = elf_getscn(elf, ehdr->e_shstrndx); |
| elfInfo->sectionStringData = elf_getdata(sectionStringSection, NULL); |
| |
| /* Find the symbol table section. */ |
| |
| symSection = NULL; |
| while ((symSection = elf_nextscn(elf, symSection)) != NULL) { |
| symHeader = elfXX_getshdr(symSection); |
| p = getSectionString(elfInfo, symHeader->sh_name); |
| if (strcmp(p, ".symtab") == 0) |
| break; |
| } |
| if (symSection == NULL) |
| fail("Unable to find symbol table.\n"); |
| |
| elfInfo->symData = elf_getdata(symSection, NULL); |
| elfInfo->symCount = elfInfo->symData->d_size / sizeof (ElfXX_Sym); |
| |
| /* Find the string section. */ |
| |
| stringSection = NULL; |
| while ((stringSection = elf_nextscn(elf, stringSection)) != NULL) { |
| stringHeader = elfXX_getshdr(stringSection); |
| p = getSectionString(elfInfo, stringHeader->sh_name); |
| if (strcmp(p, ".strtab") == 0) |
| break; |
| } |
| if (stringSection == NULL) |
| fail("Unable to find string table.\n"); |
| |
| elfInfo->symStringData = elf_getdata(stringSection, NULL); |
| elfInfo->symChainHead = NULL; |
| |
| /* Find the stab section. */ |
| |
| stabSection = NULL; |
| while ((stabSection = elf_nextscn(elf, stabSection)) != NULL) { |
| stabHeader = elfXX_getshdr(stabSection); |
| p = getSectionString(elfInfo, stabHeader->sh_name); |
| if (strcmp(p, ".stab.index") == 0) |
| break; |
| } |
| if (stabSection == NULL) |
| fail("Unable to find .stab.index.\n"); |
| |
| elfInfo->stabData = elf_getdata(stabSection, NULL); |
| elfInfo->stabCount = elfInfo->stabData->d_size / sizeof (StabEntry); |
| |
| /* Find the string section. */ |
| |
| stringSection = NULL; |
| while ((stringSection = elf_nextscn(elf, stringSection)) != NULL) { |
| stringHeader = elfXX_getshdr(stringSection); |
| p = getSectionString(elfInfo, stringHeader->sh_name); |
| if (strcmp(p, ".stab.indexstr") == 0) |
| break; |
| } |
| if (stringSection == NULL) |
| fail("Unable to find .stab.indexstr table.\n"); |
| |
| elfInfo->stabStringData = elf_getdata(stringSection, NULL); |
| makeObjFileList(elfInfo); |
| |
| return elfInfo; |
| } |
| |
| |
| static const char *identifyFile(ElfInfo *elfInfo, const char *name) |
| { |
| int i; |
| const char *file; |
| const char *sourceFile; |
| unsigned offset, lastOffset; |
| const char *lastOptions; |
| char *buf; |
| |
| file = NULL; |
| lastOptions = NULL; |
| offset = lastOffset = 0; |
| for (i = 0; i < elfInfo->stabCount; ++i) { |
| StabEntry *stab = ((StabEntry *)elfInfo->stabData->d_buf) + i; |
| |
| if (stab->n_type == 0 /* N_UNDEF */) { |
| offset = lastOffset; |
| lastOffset += stab-> n_value; |
| file = NULL; /* C++ output files seem not to have N_OBJ fields.*/ |
| lastOptions = NULL; |
| sourceFile = getStabString(elfInfo, stab->n_strx + offset); |
| } |
| else if (stab->n_type == 0x24 /* N_FUN */) { |
| const char *stabName; |
| char *p1, *p2; |
| |
| stabName = getStabString(elfInfo, stab->n_strx + offset); |
| if (strcmp (stabName, name) == 0) { |
| |
| if (file != NULL) |
| return file; |
| |
| if (lastOptions == NULL) |
| return NULL; |
| |
| p1 = strstr(lastOptions, ";ptr"); |
| if (p1 == NULL) |
| return NULL; |
| p1 += 4; |
| p2 = index(p1, ';'); |
| if (p2 == NULL) |
| return NULL; |
| |
| buf = (char *)malloc(p2 - p1 + strlen(sourceFile) + 10); |
| strncpy(buf, p1, p2 - p1); |
| buf[p2-p1] = '/'; |
| strcpy(buf + (p2 - p1) + 1, sourceFile); |
| p1 = rindex(buf, '.'); |
| if (p1 == NULL) |
| return NULL; |
| p1[1] = 'o'; |
| p1[2] = '\0'; |
| return buf; |
| } |
| } |
| else if (stab->n_type == 0x3c /* N_OPT */) { |
| lastOptions = getStabString(elfInfo, stab->n_strx + offset); |
| } |
| else if (stab->n_type == 0x38 /* N_OBJ */) { |
| file = getStabString(elfInfo, stab->n_strx + offset); |
| } |
| } |
| return NULL; |
| } |
| |
| |
| static const char *checkObjFileList(ElfInfo *elfInfo, const char *file) { |
| ObjFileList *objFileList; |
| int len = strlen(file); |
| int nameLen; |
| const char *objFileName; |
| |
| /*fprintf(stderr, "checkObjFileList(%s).\n", file);*/ |
| for (objFileList = elfInfo->objFileList; objFileList != NULL; |
| objFileList = objFileList->next) { |
| |
| objFileName = objFileList->objFileName; |
| nameLen = objFileList->nameLen; |
| if (strcmp(objFileName +nameLen - len, file) != 0) |
| continue; |
| |
| if (len == nameLen) |
| return file; |
| |
| if (len > nameLen) |
| continue; |
| |
| if (*(objFileName + nameLen - len - 1) == '/') |
| return objFileName; |
| } |
| return file; |
| } |
| |
| |
| static void identifySymbol(ElfInfo *elfInfo, ElfXX_Addr value, int count) |
| { |
| int i; |
| ElfXX_Sym *bestFunc = NULL; |
| ElfXX_Sym *bestFile = NULL; |
| ElfXX_Sym *lastFile = NULL; |
| char fileName[MAXPATHLEN]; |
| char buf[4096]; |
| const char *file; |
| SymChain *chain; |
| const char *format; |
| |
| for (i = 0; i < elfInfo->symCount; ++i) { |
| ElfXX_Sym *sym = ((ElfXX_Sym *)elfInfo->symData->d_buf) + i; |
| if (ELFXX_ST_TYPE(sym->st_info) == STT_FUNC) { |
| |
| if (sym->st_shndx == SHN_UNDEF) |
| continue; |
| |
| if (sym->st_value > value) |
| continue; |
| |
| if (bestFunc != NULL) { |
| |
| if (sym->st_value < bestFunc->st_value) |
| continue; |
| |
| /* |
| * If we have two symbols of equal value, we have a problem - |
| * we must pick the "right" one, which is the one the compiler |
| * used to generate the section name with -xF. |
| * |
| * The compiler has the nasty habit of generating two |
| * mangled names for some C++ functions. |
| * |
| * Try - picking the shortest name. |
| */ |
| |
| if (sym->st_value == bestFunc->st_value) { |
| if (strlen(getSymString(elfInfo, bestFunc->st_name)) < |
| strlen(getSymString(elfInfo, sym->st_name))) |
| continue; |
| } |
| |
| } |
| bestFunc = sym; |
| bestFile = lastFile; |
| } |
| else if (ELFXX_ST_TYPE(sym->st_info) == STT_FILE) { |
| lastFile = sym; |
| } |
| } |
| |
| if (bestFunc == NULL) |
| fail("Unable to find symbol for address 0x%x.\n", value); |
| |
| for (chain = elfInfo->symChainHead; chain != NULL; chain = chain->next) { |
| if (chain->sym == bestFunc) |
| return; |
| } |
| chain = (SymChain *)malloc(sizeof (SymChain)); |
| chain->sym = bestFunc; |
| chain->next = elfInfo->symChainHead; |
| elfInfo->symChainHead = chain; |
| |
| |
| if (ELFXX_ST_BIND(bestFunc->st_info) == STB_GLOBAL) |
| file = ""; |
| else { |
| const char *name = getSymString(elfInfo, bestFunc->st_name); |
| file = identifyFile(elfInfo, name); |
| if (file == NULL) { |
| if (bestFile == NULL) { |
| file = "notFound"; |
| fail("Failed to identify %s.\n", name); |
| } else { |
| char *suffix; |
| fileName[0] = ':'; |
| fileName[1] = ' '; |
| file = getSymString(elfInfo, bestFile->st_name); |
| strncpy(fileName+2, file, MAXPATHLEN-3); |
| suffix = rindex(fileName, '.'); |
| if (suffix == NULL) |
| fail("no file name suffix?"); |
| suffix[1] = 'o'; |
| suffix[2] = '\0'; |
| |
| file = checkObjFileList(elfInfo, fileName+2); |
| if (file != fileName+2) |
| strncpy(fileName+2, file, MAXPATHLEN-3); |
| |
| file = fileName; |
| } |
| } else { |
| fileName[0] = ':'; |
| fileName[1] = ' '; |
| strncpy(fileName + 2, file, MAXPATHLEN-3); |
| file = fileName; |
| } |
| } |
| format = "text: .text%%%s%s;\n"; |
| i = snprintf(buf, sizeof buf, format, |
| bestFunc ? getSymString(elfInfo, bestFunc->st_name) : "notFound", |
| file); |
| write(2, buf, i); |
| } |
| |
| |
| static mutex_t mutex; |
| static int orderByCount = FALSE; |
| |
| |
| static void init_mcount(void) |
| { |
| mutex_init(&mutex, USYNC_THREAD, NULL); |
| } |
| |
| #pragma init(init_mcount) |
| |
| |
| typedef struct CountAddrPair { |
| int count; |
| unsigned int addr; |
| } CountAddrPair; |
| |
| |
| static int compareCounts(const void *a, const void *b) { |
| return ((CountAddrPair *)b)->count - ((CountAddrPair *)a)->count; |
| } |
| |
| static int compareCountsReverse(const void *a, const void *b) { |
| return ((CountAddrPair *)a)->count - ((CountAddrPair *)b)->count; |
| } |
| |
| |
| static void doCounts(void) { |
| unsigned int i; |
| int n; |
| int nMethods; |
| int nMethods2; |
| CountAddrPair *pairs; |
| ElfInfo *elfInfo; |
| |
| elfInfo = createElfInfo(libFileName); |
| |
| nMethods = 0; |
| for (i = 0; i < COUNT_BUF_SIZE >> 2; ++i) { |
| n = countBuf[i]; |
| if (n > 0) |
| ++nMethods; |
| } |
| pairs = (CountAddrPair *)malloc(sizeof(CountAddrPair) * nMethods); |
| nMethods2 = 0; |
| for (i = 0; i < COUNT_BUF_SIZE >> 2; ++i) { |
| n = countBuf[i]; |
| if (n > 0) { |
| pairs[nMethods2].count = n; |
| pairs[nMethods2].addr = i << 2; |
| ++nMethods2; |
| if (nMethods2 > nMethods) { |
| fprintf(stderr, "Number of methods detected increased after" |
| " the atexit call.\n"); |
| break; |
| } |
| } |
| } |
| if (orderByCount) { |
| qsort(pairs, nMethods, sizeof pairs[0], &compareCounts); |
| for (i = 0; i < nMethods; ++i) { |
| identifySymbol(elfInfo, pairs[i].addr, pairs[i].count); |
| } |
| } |
| else { |
| qsort(pairs, nMethods, sizeof pairs[0], &compareCountsReverse); |
| for (i = 0; i < nMethods; ++i) { |
| identifySymbol(elfInfo, pairs[i].addr, 0); |
| } |
| } |
| } |
| |
| |
| static void __mcount(void *i0) |
| { |
| Dl_info info; |
| unsigned int offset; |
| int *p; |
| static int callsCounted = 0; |
| static int initialized = FALSE; |
| |
| if (!initialized) { |
| dladdr(i0, &info); |
| libFileName = info.dli_fname; |
| #if 0 |
| fprintf(stderr, "Profiling %s\n", libFileName); |
| #endif |
| textOffset = info.dli_fbase; |
| if (getenv("MCOUNT_ORDER_BY_COUNT") != NULL) { |
| orderByCount = TRUE; |
| } |
| countBuf = (int *)malloc(COUNT_BUF_SIZE); |
| memset(countBuf, 0, COUNT_BUF_SIZE); |
| atexit(&doCounts); |
| initialized = TRUE; |
| } |
| |
| if ((uintptr_t)i0 < (uintptr_t)textOffset) { |
| fprintf(stderr, "mcount: function being profiled out of range????\n"); |
| fprintf(stderr, " profiling more than one library at once????\n"); |
| #if 0 |
| dladdr(i0, &info); |
| fprintf(stderr, "Problem with %s in %s ???\n", |
| info.dli_sname, info.dli_fname); |
| #endif |
| fflush(stderr); |
| exit(666); |
| } |
| offset = ((uintptr_t)i0) - ((uintptr_t)textOffset); |
| if (offset > COUNT_BUF_SIZE) { |
| fprintf(stderr, "mcount: internal buffer too small for test.\n"); |
| fprintf(stderr, " or function being profiled out of range????\n"); |
| fprintf(stderr, " or profiling more than one library at once????\n"); |
| #if 0 |
| dladdr(i0, &info); |
| fprintf(stderr, "Problem with %s in %s ???\n", |
| info.dli_sname, info.dli_fname); |
| #endif |
| fflush(stderr); |
| exit(666); |
| } |
| |
| p = &countBuf[offset >>2]; |
| if (orderByCount) { |
| ++*p; |
| } |
| else { |
| if (*p == 0) { |
| *p = ++callsCounted; |
| } |
| } |
| } |
| |
| |
| void _mcount(void) |
| { |
| __mcount(_getReturnAddr()); |
| } |
| |
| |
| void mcount(void) |
| { |
| __mcount(_getReturnAddr()); |
| } |