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_del(&h->nd);
 101                        list_add(&h->nd, &srcfile_list);
 102                        return h;
 103                }
 104        }
 105
 106        /* Only prune if there is more than one entry */
 107        while ((num_srcfiles > MAXSRCFILES || map_total_sz > MAXSRCCACHE) &&
 108               srcfile_list.next != &srcfile_list) {
 109                assert(!list_empty(&srcfile_list));
 110                h = list_entry(srcfile_list.prev, struct srcfile, nd);
 111                free_srcfile(h);
 112        }
 113
 114        fd = open(fn, O_RDONLY);
 115        if (fd < 0 || fstat(fd, &st) < 0) {
 116                pr_debug("cannot open source file %s\n", fn);
 117                return NULL;
 118        }
 119
 120        h = malloc(sizeof(struct srcfile));
 121        if (!h)
 122                return NULL;
 123
 124        h->fn = strdup(fn);
 125        if (!h->fn)
 126                goto out_h;
 127
 128        h->maplen = st.st_size;
 129        sz = (h->maplen + page_size - 1) & ~(page_size - 1);
 130        h->map = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
 131        close(fd);
 132        if (h->map == (char *)-1) {
 133                pr_debug("cannot mmap source file %s\n", fn);
 134                goto out_fn;
 135        }
 136        h->numlines = countlines(h->map, h->maplen);
 137        h->lines = calloc(h->numlines, sizeof(char *));
 138        if (!h->lines)
 139                goto out_map;
 140        fill_lines(h->lines, h->numlines, h->map, h->maplen);
 141        list_add(&h->nd, &srcfile_list);
 142        hlist_add_head(&h->hash_nd, &srcfile_htab[hval]);
 143        map_total_sz += h->maplen;
 144        num_srcfiles++;
 145        return h;
 146
 147out_map:
 148        munmap(h->map, sz);
 149out_fn:
 150        zfree(&h->fn);
 151out_h:
 152        free(h);
 153        return NULL;
 154}
 155
 156/* Result is not 0 terminated */
 157char *find_sourceline(char *fn, unsigned line, int *lenp)
 158{
 159        char *l, *p;
 160        struct srcfile *sf = find_srcfile(fn);
 161        if (!sf)
 162                return NULL;
 163        line--;
 164        if (line >= sf->numlines)
 165                return NULL;
 166        l = sf->lines[line];
 167        if (!l)
 168                return NULL;
 169        p = memchr(l, '\n', sf->map + sf->maplen - l);
 170        *lenp = p - l;
 171        return l;
 172}
 173