1 /* 2 * Manage printing of source lines 3 * Copyright (c) 2017, Intel Corporation. 4 * Author: Andi Kleen 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms and conditions of the GNU General Public License, 8 * version 2, as published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 * more details. 14 */ 15 #include "linux/list.h" 16 #include <stdlib.h> 17 #include <sys/mman.h> 18 #include <sys/stat.h> 19 #include <fcntl.h> 20 #include <unistd.h> 21 #include <assert.h> 22 #include <string.h> 23 #include "srccode.h" 24 #include "debug.h" 25 #include "util.h" 26 27 #define MAXSRCCACHE (32*1024*1024) 28 #define MAXSRCFILES 64 29 #define SRC_HTAB_SZ 64 30 31 struct srcfile { 32 struct hlist_node hash_nd; 33 struct list_head nd; 34 char *fn; 35 char **lines; 36 char *map; 37 unsigned numlines; 38 size_t maplen; 39 }; 40 41 static struct hlist_head srcfile_htab[SRC_HTAB_SZ]; 42 static LIST_HEAD(srcfile_list); 43 static long map_total_sz; 44 static int num_srcfiles; 45 46 static unsigned shash(unsigned char *s) 47 { 48 unsigned h = 0; 49 while (*s) 50 h = 65599 * h + *s++; 51 return h ^ (h >> 16); 52 } 53 54 static int countlines(char *map, int maplen) 55 { 56 int numl; 57 char *end = map + maplen; 58 char *p = map; 59 60 if (maplen == 0) 61 return 0; 62 numl = 0; 63 while (p < end && (p = memchr(p, '\n', end - p)) != NULL) { 64 numl++; 65 p++; 66 } 67 if (p < end) 68 numl++; 69 return numl; 70 } 71 72 static void fill_lines(char **lines, int maxline, char *map, int maplen) 73 { 74 int l; 75 char *end = map + maplen; 76 char *p = map; 77 78 if (maplen == 0 || maxline == 0) 79 return; 80 l = 0; 81 lines[l++] = map; 82 while (p < end && (p = memchr(p, '\n', end - p)) != NULL) { 83 if (l >= maxline) 84 return; 85 lines[l++] = ++p; 86 } 87 if (p < end) 88 lines[l] = p; 89 } 90 91 static void free_srcfile(struct srcfile *sf) 92 { 93 list_del(&sf->nd); 94 hlist_del(&sf->hash_nd); 95 map_total_sz -= sf->maplen; 96 munmap(sf->map, sf->maplen); 97 free(sf->lines); 98 free(sf->fn); 99 free(sf); 100 num_srcfiles--; 101 } 102 103 static struct srcfile *find_srcfile(char *fn) 104 { 105 struct stat st; 106 struct srcfile *h; 107 int fd; 108 unsigned long sz; 109 unsigned hval = shash((unsigned char *)fn) % SRC_HTAB_SZ; 110 111 hlist_for_each_entry (h, &srcfile_htab[hval], hash_nd) { 112 if (!strcmp(fn, h->fn)) { 113 /* Move to front */ 114 list_del(&h->nd); 115 list_add(&h->nd, &srcfile_list); 116 return h; 117 } 118 } 119 120 /* Only prune if there is more than one entry */ 121 while ((num_srcfiles > MAXSRCFILES || map_total_sz > MAXSRCCACHE) && 122 srcfile_list.next != &srcfile_list) { 123 assert(!list_empty(&srcfile_list)); 124 h = list_entry(srcfile_list.prev, struct srcfile, nd); 125 free_srcfile(h); 126 } 127 128 fd = open(fn, O_RDONLY); 129 if (fd < 0 || fstat(fd, &st) < 0) { 130 pr_debug("cannot open source file %s\n", fn); 131 return NULL; 132 } 133 134 h = malloc(sizeof(struct srcfile)); 135 if (!h) 136 return NULL; 137 138 h->fn = strdup(fn); 139 if (!h->fn) 140 goto out_h; 141 142 h->maplen = st.st_size; 143 sz = (h->maplen + page_size - 1) & ~(page_size - 1); 144 h->map = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0); 145 close(fd); 146 if (h->map == (char *)-1) { 147 pr_debug("cannot mmap source file %s\n", fn); 148 goto out_fn; 149 } 150 h->numlines = countlines(h->map, h->maplen); 151 h->lines = calloc(h->numlines, sizeof(char *)); 152 if (!h->lines) 153 goto out_map; 154 fill_lines(h->lines, h->numlines, h->map, h->maplen); 155 list_add(&h->nd, &srcfile_list); 156 hlist_add_head(&h->hash_nd, &srcfile_htab[hval]); 157 map_total_sz += h->maplen; 158 num_srcfiles++; 159 return h; 160 161 out_map: 162 munmap(h->map, sz); 163 out_fn: 164 free(h->fn); 165 out_h: 166 free(h); 167 return NULL; 168 } 169 170 /* Result is not 0 terminated */ 171 char *find_sourceline(char *fn, unsigned line, int *lenp) 172 { 173 char *l, *p; 174 struct srcfile *sf = find_srcfile(fn); 175 if (!sf) 176 return NULL; 177 line--; 178 if (line >= sf->numlines) 179 return NULL; 180 l = sf->lines[line]; 181 if (!l) 182 return NULL; 183 p = memchr(l, '\n', sf->map + sf->maplen - l); 184 *lenp = p - l; 185 return l; 186 } 187