1/*
2 * Copyright (c) 2000, 2013, 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