linux/tools/perf/util/srccode.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Manage printing of source lines
   4 * Copyright (c) 2017, Intel Corporation.
   5 * Author: Andi Kleen
   6 */
   7#include <linux/list.h>
   8#include <linux/zalloc.h>
   9#include <stdlib.h>
  10#include <sys/mman.h>
  11#include <sys/stat.h>
  12#include <fcntl.h>
  13#include <unistd.h>
  14#include <assert.h>
  15#include <string.h>
  16#include "srccode.h"
  17#include "debug.h"
  18#include <internal/lib.h> // page_size
  19#include "fncache.h"
  20
  21#define MAXSRCCACHE (32*1024*1024)
  22#define MAXSRCFILES     64
  23#define SRC_HTAB_SZ     64
  24
  25struct srcfile {
  26        struct hlist_node hash_nd;
  27        struct list_head nd;
  28        char *fn;
  29        char **lines;
  30        char *map;
  31        unsigned numlines;
  32        size_t maplen;
  33};
  34
  35static struct hlist_head srcfile_htab[SRC_HTAB_SZ];
  36static LIST_HEAD(srcfile_list);
  37static long map_total_sz;
  38static int num_srcfiles;
  39
  40static int countlines(char *map, int maplen)
  41{
  42        int numl;
  43        char *end = map + maplen;
  44        char *p = map;
  45
  46        if (maplen == 0)
  47                return 0;
  48        numl = 0;
  49        while (p < end && (p = memchr(p, '\n', end - p)) != NULL) {
  50                numl++;
  51                p++;
  52        }
  53        if (p < end)
  54                numl++;
  55        return numl;
  56}
  57
  58static void fill_lines(char **lines, int maxline, char *map, int maplen)
  59{
  60        int l;
  61        char *end = map + maplen;
  62        char *p = map;
  63
  64        if (maplen == 0 || maxline == 0)
  65                return;
  66        l = 0;
  67        lines[l++] = map;
  68        while (p < end && (p = memchr(p, '\n', end - p)) != NULL) {
  69                if (l >= maxline)
  70                        return;
  71                lines[l++] = ++p;
  72        }
  73        if (p < end)
  74                lines[l] = p;
  75}
  76
  77static void free_srcfile(struct srcfile *sf)
  78{
  79        list_del_init(&sf->nd);
  80        hlist_del(&sf->hash_nd);
  81        map_total_sz -= sf->maplen;
  82        munmap(sf->map, sf->maplen);
  83        zfree(&sf->lines);
  84        zfree(&sf->fn);
  85        free(sf);
  86        num_srcfiles--;
  87}
  88
  89static struct srcfile *find_srcfile(char *fn)
  90{
  91        struct stat st;
  92        struct srcfile *h;
  93        int fd;
  94        unsigned long sz;
  95        unsigned hval = shash((unsigned char *)fn) % SRC_HTAB_SZ;
  96
  97        hlist_for_each_entry (h, &srcfile_htab[hval], hash_nd) {
  98                if (!strcmp(fn, h->fn)) {
  99                        /* Move to front */
 100                        list_move(&h->nd, &srcfile_list);
 101                        return h;
 102                }
 103        }
 104
 105        /* Only prune if there is more than one entry */
 106        while ((num_srcfiles > MAXSRCFILES || map_total_sz > MAXSRCCACHE) &&
 107               srcfile_list.next != &srcfile_list) {
 108                assert(!list_empty(&srcfile_list));
 109                h = list_entry(srcfile_list.prev, struct srcfile, nd);
 110                free_srcfile(h);
 111        }
 112
 113        fd = open(fn, O_RDONLY);
 114        if (fd < 0 || fstat(fd, &st) < 0) {
 115                pr_debug("cannot open source file %s\n", fn);
 116                return NULL;
 117        }
 118
 119        h = malloc(sizeof(struct srcfile));
 120        if (!h)
 121                return NULL;
 122
 123        h->fn = strdup(fn);
 124        if (!h->fn)
 125                goto out_h;
 126
 127        h->maplen = st.st_size;
 128        sz = (h->maplen + page_size - 1) & ~(page_size - 1);
 129        h->map = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
 130        close(fd);
 131        if (h->map == (char *)-1) {
 132                pr_debug("cannot mmap source file %s\n", fn);
 133                goto out_fn;
 134        }
 135        h->numlines = countlines(h->map, h->maplen);
 136        h->lines = calloc(h->numlines, sizeof(char *));
 137        if (!h->lines)
 138                goto out_map;
 139        fill_lines(h->lines, h->numlines, h->map, h->maplen);
 140        list_add(&h->nd, &srcfile_list);
 141        hlist_add_head(&h->hash_nd, &srcfile_htab[hval]);
 142        map_total_sz += h->maplen;
 143        num_srcfiles++;
 144        return h;
 145
 146out_map:
 147        munmap(h->map, sz);
 148out_fn:
 149        zfree(&h->fn);
 150out_h:
 151        free(h);
 152        return NULL;
 153}
 154
 155/* Result is not 0 terminated */
 156char *find_sourceline(char *fn, unsigned line, int *lenp)
 157{
 158        char *l, *p;
 159        struct srcfile *sf = find_srcfile(fn);
 160        if (!sf)
 161                return NULL;
 162        line--;
 163        if (line >= sf->numlines)
 164                return NULL;
 165        l = sf->lines[line];
 166        if (!l)
 167                return NULL;
 168        p = memchr(l, '\n', sf->map + sf->maplen - l);
 169        *lenp = p - l;
 170        return l;
 171}
 172