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