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_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 146 out_map: 147 munmap(h->map, sz); 148 out_fn: 149 zfree(&h->fn); 150 out_h: 151 free(h); 152 return NULL; 153 } 154 155 /* Result is not 0 terminated */ 156 char *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