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 24 struct 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 34 static struct hlist_head srcfile_htab[SRC_HTAB_SZ]; 35 static LIST_HEAD(srcfile_list); 36 static long map_total_sz; 37 static int num_srcfiles; 38 39 static 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 47 static 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 65 static 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 84 static 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 96 static 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 154 out_map: 155 munmap(h->map, sz); 156 out_fn: 157 zfree(&h->fn); 158 out_h: 159 free(h); 160 return NULL; 161 } 162 163 /* Result is not 0 terminated */ 164 char *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