18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Manage printing of source lines 48c2ecf20Sopenharmony_ci * Copyright (c) 2017, Intel Corporation. 58c2ecf20Sopenharmony_ci * Author: Andi Kleen 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <linux/list.h> 88c2ecf20Sopenharmony_ci#include <linux/zalloc.h> 98c2ecf20Sopenharmony_ci#include <stdlib.h> 108c2ecf20Sopenharmony_ci#include <sys/mman.h> 118c2ecf20Sopenharmony_ci#include <sys/stat.h> 128c2ecf20Sopenharmony_ci#include <fcntl.h> 138c2ecf20Sopenharmony_ci#include <unistd.h> 148c2ecf20Sopenharmony_ci#include <assert.h> 158c2ecf20Sopenharmony_ci#include <string.h> 168c2ecf20Sopenharmony_ci#include "srccode.h" 178c2ecf20Sopenharmony_ci#include "debug.h" 188c2ecf20Sopenharmony_ci#include <internal/lib.h> // page_size 198c2ecf20Sopenharmony_ci#include "fncache.h" 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define MAXSRCCACHE (32*1024*1024) 228c2ecf20Sopenharmony_ci#define MAXSRCFILES 64 238c2ecf20Sopenharmony_ci#define SRC_HTAB_SZ 64 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct srcfile { 268c2ecf20Sopenharmony_ci struct hlist_node hash_nd; 278c2ecf20Sopenharmony_ci struct list_head nd; 288c2ecf20Sopenharmony_ci char *fn; 298c2ecf20Sopenharmony_ci char **lines; 308c2ecf20Sopenharmony_ci char *map; 318c2ecf20Sopenharmony_ci unsigned numlines; 328c2ecf20Sopenharmony_ci size_t maplen; 338c2ecf20Sopenharmony_ci}; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct hlist_head srcfile_htab[SRC_HTAB_SZ]; 368c2ecf20Sopenharmony_cistatic LIST_HEAD(srcfile_list); 378c2ecf20Sopenharmony_cistatic long map_total_sz; 388c2ecf20Sopenharmony_cistatic int num_srcfiles; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int countlines(char *map, int maplen) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci int numl; 438c2ecf20Sopenharmony_ci char *end = map + maplen; 448c2ecf20Sopenharmony_ci char *p = map; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci if (maplen == 0) 478c2ecf20Sopenharmony_ci return 0; 488c2ecf20Sopenharmony_ci numl = 0; 498c2ecf20Sopenharmony_ci while (p < end && (p = memchr(p, '\n', end - p)) != NULL) { 508c2ecf20Sopenharmony_ci numl++; 518c2ecf20Sopenharmony_ci p++; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci if (p < end) 548c2ecf20Sopenharmony_ci numl++; 558c2ecf20Sopenharmony_ci return numl; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic void fill_lines(char **lines, int maxline, char *map, int maplen) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci int l; 618c2ecf20Sopenharmony_ci char *end = map + maplen; 628c2ecf20Sopenharmony_ci char *p = map; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (maplen == 0 || maxline == 0) 658c2ecf20Sopenharmony_ci return; 668c2ecf20Sopenharmony_ci l = 0; 678c2ecf20Sopenharmony_ci lines[l++] = map; 688c2ecf20Sopenharmony_ci while (p < end && (p = memchr(p, '\n', end - p)) != NULL) { 698c2ecf20Sopenharmony_ci if (l >= maxline) 708c2ecf20Sopenharmony_ci return; 718c2ecf20Sopenharmony_ci lines[l++] = ++p; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci if (p < end) 748c2ecf20Sopenharmony_ci lines[l] = p; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic void free_srcfile(struct srcfile *sf) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci list_del_init(&sf->nd); 808c2ecf20Sopenharmony_ci hlist_del(&sf->hash_nd); 818c2ecf20Sopenharmony_ci map_total_sz -= sf->maplen; 828c2ecf20Sopenharmony_ci munmap(sf->map, sf->maplen); 838c2ecf20Sopenharmony_ci zfree(&sf->lines); 848c2ecf20Sopenharmony_ci zfree(&sf->fn); 858c2ecf20Sopenharmony_ci free(sf); 868c2ecf20Sopenharmony_ci num_srcfiles--; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic struct srcfile *find_srcfile(char *fn) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct stat st; 928c2ecf20Sopenharmony_ci struct srcfile *h; 938c2ecf20Sopenharmony_ci int fd; 948c2ecf20Sopenharmony_ci unsigned long sz; 958c2ecf20Sopenharmony_ci unsigned hval = shash((unsigned char *)fn) % SRC_HTAB_SZ; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci hlist_for_each_entry (h, &srcfile_htab[hval], hash_nd) { 988c2ecf20Sopenharmony_ci if (!strcmp(fn, h->fn)) { 998c2ecf20Sopenharmony_ci /* Move to front */ 1008c2ecf20Sopenharmony_ci list_del(&h->nd); 1018c2ecf20Sopenharmony_ci list_add(&h->nd, &srcfile_list); 1028c2ecf20Sopenharmony_ci return h; 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* Only prune if there is more than one entry */ 1078c2ecf20Sopenharmony_ci while ((num_srcfiles > MAXSRCFILES || map_total_sz > MAXSRCCACHE) && 1088c2ecf20Sopenharmony_ci srcfile_list.next != &srcfile_list) { 1098c2ecf20Sopenharmony_ci assert(!list_empty(&srcfile_list)); 1108c2ecf20Sopenharmony_ci h = list_entry(srcfile_list.prev, struct srcfile, nd); 1118c2ecf20Sopenharmony_ci free_srcfile(h); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci fd = open(fn, O_RDONLY); 1158c2ecf20Sopenharmony_ci if (fd < 0 || fstat(fd, &st) < 0) { 1168c2ecf20Sopenharmony_ci pr_debug("cannot open source file %s\n", fn); 1178c2ecf20Sopenharmony_ci return NULL; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci h = malloc(sizeof(struct srcfile)); 1218c2ecf20Sopenharmony_ci if (!h) 1228c2ecf20Sopenharmony_ci return NULL; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci h->fn = strdup(fn); 1258c2ecf20Sopenharmony_ci if (!h->fn) 1268c2ecf20Sopenharmony_ci goto out_h; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci h->maplen = st.st_size; 1298c2ecf20Sopenharmony_ci sz = (h->maplen + page_size - 1) & ~(page_size - 1); 1308c2ecf20Sopenharmony_ci h->map = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0); 1318c2ecf20Sopenharmony_ci close(fd); 1328c2ecf20Sopenharmony_ci if (h->map == (char *)-1) { 1338c2ecf20Sopenharmony_ci pr_debug("cannot mmap source file %s\n", fn); 1348c2ecf20Sopenharmony_ci goto out_fn; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci h->numlines = countlines(h->map, h->maplen); 1378c2ecf20Sopenharmony_ci h->lines = calloc(h->numlines, sizeof(char *)); 1388c2ecf20Sopenharmony_ci if (!h->lines) 1398c2ecf20Sopenharmony_ci goto out_map; 1408c2ecf20Sopenharmony_ci fill_lines(h->lines, h->numlines, h->map, h->maplen); 1418c2ecf20Sopenharmony_ci list_add(&h->nd, &srcfile_list); 1428c2ecf20Sopenharmony_ci hlist_add_head(&h->hash_nd, &srcfile_htab[hval]); 1438c2ecf20Sopenharmony_ci map_total_sz += h->maplen; 1448c2ecf20Sopenharmony_ci num_srcfiles++; 1458c2ecf20Sopenharmony_ci return h; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ciout_map: 1488c2ecf20Sopenharmony_ci munmap(h->map, sz); 1498c2ecf20Sopenharmony_ciout_fn: 1508c2ecf20Sopenharmony_ci zfree(&h->fn); 1518c2ecf20Sopenharmony_ciout_h: 1528c2ecf20Sopenharmony_ci free(h); 1538c2ecf20Sopenharmony_ci return NULL; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci/* Result is not 0 terminated */ 1578c2ecf20Sopenharmony_cichar *find_sourceline(char *fn, unsigned line, int *lenp) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci char *l, *p; 1608c2ecf20Sopenharmony_ci struct srcfile *sf = find_srcfile(fn); 1618c2ecf20Sopenharmony_ci if (!sf) 1628c2ecf20Sopenharmony_ci return NULL; 1638c2ecf20Sopenharmony_ci line--; 1648c2ecf20Sopenharmony_ci if (line >= sf->numlines) 1658c2ecf20Sopenharmony_ci return NULL; 1668c2ecf20Sopenharmony_ci l = sf->lines[line]; 1678c2ecf20Sopenharmony_ci if (!l) 1688c2ecf20Sopenharmony_ci return NULL; 1698c2ecf20Sopenharmony_ci p = memchr(l, '\n', sf->map + sf->maplen - l); 1708c2ecf20Sopenharmony_ci *lenp = p - l; 1718c2ecf20Sopenharmony_ci return l; 1728c2ecf20Sopenharmony_ci} 173