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 23 struct 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 33 static struct hlist_head srcfile_htab[SRC_HTAB_SZ]; 34 static LIST_HEAD(srcfile_list); 35 static long map_total_sz; 36 static int num_srcfiles; 37 38 static 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 46 static 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 64 static 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 83 static 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 95 static 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 153 out_map: 154 munmap(h->map, sz); 155 out_fn: 156 free(h->fn); 157 out_h: 158 free(h); 159 return NULL; 160 } 161 162 /* Result is not 0 terminated */ 163 char *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