172 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Manage printing of source lines4 * Copyright (c) 2017, Intel Corporation.5 * Author: Andi Kleen6 */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_size19#include "fncache.h"20 21#define MAXSRCCACHE (32*1024*1024)22#define MAXSRCFILES 6423#define SRC_HTAB_SZ 6424 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