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 <internal/lib.h> // page_size 19 #include "fncache.h" 20 21 #define MAXSRCCACHE (32*1024*1024) 22 #define MAXSRCFILES 64 23 #define SRC_HTAB_SZ 64 24 25 struct 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 35 static struct hlist_head srcfile_htab[SRC_HTAB_SZ]; 36 static LIST_HEAD(srcfile_list); 37 static long map_total_sz; 38 static int num_srcfiles; 39 40 static 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 58 static 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 77 static 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 89 static 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_del(&h->nd); 101 list_add(&h->nd, &srcfile_list); 102 return h; 103 } 104 } 105 106 /* Only prune if there is more than one entry */ 107 while ((num_srcfiles > MAXSRCFILES || map_total_sz > MAXSRCCACHE) && 108 srcfile_list.next != &srcfile_list) { 109 assert(!list_empty(&srcfile_list)); 110 h = list_entry(srcfile_list.prev, struct srcfile, nd); 111 free_srcfile(h); 112 } 113 114 fd = open(fn, O_RDONLY); 115 if (fd < 0 || fstat(fd, &st) < 0) { 116 pr_debug("cannot open source file %s\n", fn); 117 return NULL; 118 } 119 120 h = malloc(sizeof(struct srcfile)); 121 if (!h) 122 return NULL; 123 124 h->fn = strdup(fn); 125 if (!h->fn) 126 goto out_h; 127 128 h->maplen = st.st_size; 129 sz = (h->maplen + page_size - 1) & ~(page_size - 1); 130 h->map = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0); 131 close(fd); 132 if (h->map == (char *)-1) { 133 pr_debug("cannot mmap source file %s\n", fn); 134 goto out_fn; 135 } 136 h->numlines = countlines(h->map, h->maplen); 137 h->lines = calloc(h->numlines, sizeof(char *)); 138 if (!h->lines) 139 goto out_map; 140 fill_lines(h->lines, h->numlines, h->map, h->maplen); 141 list_add(&h->nd, &srcfile_list); 142 hlist_add_head(&h->hash_nd, &srcfile_htab[hval]); 143 map_total_sz += h->maplen; 144 num_srcfiles++; 145 return h; 146 147 out_map: 148 munmap(h->map, sz); 149 out_fn: 150 zfree(&h->fn); 151 out_h: 152 free(h); 153 return NULL; 154 } 155 156 /* Result is not 0 terminated */ 157 char *find_sourceline(char *fn, unsigned line, int *lenp) 158 { 159 char *l, *p; 160 struct srcfile *sf = find_srcfile(fn); 161 if (!sf) 162 return NULL; 163 line--; 164 if (line >= sf->numlines) 165 return NULL; 166 l = sf->lines[line]; 167 if (!l) 168 return NULL; 169 p = memchr(l, '\n', sf->map + sf->maplen - l); 170 *lenp = p - l; 171 return l; 172 } 173