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