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