162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Manage printing of source lines 462306a36Sopenharmony_ci * Copyright (c) 2017, Intel Corporation. 562306a36Sopenharmony_ci * Author: Andi Kleen 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/list.h> 862306a36Sopenharmony_ci#include <linux/zalloc.h> 962306a36Sopenharmony_ci#include <stdlib.h> 1062306a36Sopenharmony_ci#include <sys/mman.h> 1162306a36Sopenharmony_ci#include <sys/stat.h> 1262306a36Sopenharmony_ci#include <fcntl.h> 1362306a36Sopenharmony_ci#include <unistd.h> 1462306a36Sopenharmony_ci#include <assert.h> 1562306a36Sopenharmony_ci#include <string.h> 1662306a36Sopenharmony_ci#include "srccode.h" 1762306a36Sopenharmony_ci#include "debug.h" 1862306a36Sopenharmony_ci#include <internal/lib.h> // page_size 1962306a36Sopenharmony_ci#include "fncache.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define MAXSRCCACHE (32*1024*1024) 2262306a36Sopenharmony_ci#define MAXSRCFILES 64 2362306a36Sopenharmony_ci#define SRC_HTAB_SZ 64 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistruct srcfile { 2662306a36Sopenharmony_ci struct hlist_node hash_nd; 2762306a36Sopenharmony_ci struct list_head nd; 2862306a36Sopenharmony_ci char *fn; 2962306a36Sopenharmony_ci char **lines; 3062306a36Sopenharmony_ci char *map; 3162306a36Sopenharmony_ci unsigned numlines; 3262306a36Sopenharmony_ci size_t maplen; 3362306a36Sopenharmony_ci}; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic struct hlist_head srcfile_htab[SRC_HTAB_SZ]; 3662306a36Sopenharmony_cistatic LIST_HEAD(srcfile_list); 3762306a36Sopenharmony_cistatic long map_total_sz; 3862306a36Sopenharmony_cistatic int num_srcfiles; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int countlines(char *map, int maplen) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci int numl; 4362306a36Sopenharmony_ci char *end = map + maplen; 4462306a36Sopenharmony_ci char *p = map; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (maplen == 0) 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci numl = 0; 4962306a36Sopenharmony_ci while (p < end && (p = memchr(p, '\n', end - p)) != NULL) { 5062306a36Sopenharmony_ci numl++; 5162306a36Sopenharmony_ci p++; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci if (p < end) 5462306a36Sopenharmony_ci numl++; 5562306a36Sopenharmony_ci return numl; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic void fill_lines(char **lines, int maxline, char *map, int maplen) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci int l; 6162306a36Sopenharmony_ci char *end = map + maplen; 6262306a36Sopenharmony_ci char *p = map; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (maplen == 0 || maxline == 0) 6562306a36Sopenharmony_ci return; 6662306a36Sopenharmony_ci l = 0; 6762306a36Sopenharmony_ci lines[l++] = map; 6862306a36Sopenharmony_ci while (p < end && (p = memchr(p, '\n', end - p)) != NULL) { 6962306a36Sopenharmony_ci if (l >= maxline) 7062306a36Sopenharmony_ci return; 7162306a36Sopenharmony_ci lines[l++] = ++p; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci if (p < end) 7462306a36Sopenharmony_ci lines[l] = p; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic void free_srcfile(struct srcfile *sf) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci list_del_init(&sf->nd); 8062306a36Sopenharmony_ci hlist_del(&sf->hash_nd); 8162306a36Sopenharmony_ci map_total_sz -= sf->maplen; 8262306a36Sopenharmony_ci munmap(sf->map, sf->maplen); 8362306a36Sopenharmony_ci zfree(&sf->lines); 8462306a36Sopenharmony_ci zfree(&sf->fn); 8562306a36Sopenharmony_ci free(sf); 8662306a36Sopenharmony_ci num_srcfiles--; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic struct srcfile *find_srcfile(char *fn) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct stat st; 9262306a36Sopenharmony_ci struct srcfile *h; 9362306a36Sopenharmony_ci int fd; 9462306a36Sopenharmony_ci unsigned long sz; 9562306a36Sopenharmony_ci unsigned hval = shash((unsigned char *)fn) % SRC_HTAB_SZ; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci hlist_for_each_entry (h, &srcfile_htab[hval], hash_nd) { 9862306a36Sopenharmony_ci if (!strcmp(fn, h->fn)) { 9962306a36Sopenharmony_ci /* Move to front */ 10062306a36Sopenharmony_ci list_move(&h->nd, &srcfile_list); 10162306a36Sopenharmony_ci return h; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* Only prune if there is more than one entry */ 10662306a36Sopenharmony_ci while ((num_srcfiles > MAXSRCFILES || map_total_sz > MAXSRCCACHE) && 10762306a36Sopenharmony_ci srcfile_list.next != &srcfile_list) { 10862306a36Sopenharmony_ci assert(!list_empty(&srcfile_list)); 10962306a36Sopenharmony_ci h = list_entry(srcfile_list.prev, struct srcfile, nd); 11062306a36Sopenharmony_ci free_srcfile(h); 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci fd = open(fn, O_RDONLY); 11462306a36Sopenharmony_ci if (fd < 0 || fstat(fd, &st) < 0) { 11562306a36Sopenharmony_ci pr_debug("cannot open source file %s\n", fn); 11662306a36Sopenharmony_ci return NULL; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci h = malloc(sizeof(struct srcfile)); 12062306a36Sopenharmony_ci if (!h) 12162306a36Sopenharmony_ci return NULL; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci h->fn = strdup(fn); 12462306a36Sopenharmony_ci if (!h->fn) 12562306a36Sopenharmony_ci goto out_h; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci h->maplen = st.st_size; 12862306a36Sopenharmony_ci sz = (h->maplen + page_size - 1) & ~(page_size - 1); 12962306a36Sopenharmony_ci h->map = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0); 13062306a36Sopenharmony_ci close(fd); 13162306a36Sopenharmony_ci if (h->map == (char *)-1) { 13262306a36Sopenharmony_ci pr_debug("cannot mmap source file %s\n", fn); 13362306a36Sopenharmony_ci goto out_fn; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci h->numlines = countlines(h->map, h->maplen); 13662306a36Sopenharmony_ci h->lines = calloc(h->numlines, sizeof(char *)); 13762306a36Sopenharmony_ci if (!h->lines) 13862306a36Sopenharmony_ci goto out_map; 13962306a36Sopenharmony_ci fill_lines(h->lines, h->numlines, h->map, h->maplen); 14062306a36Sopenharmony_ci list_add(&h->nd, &srcfile_list); 14162306a36Sopenharmony_ci hlist_add_head(&h->hash_nd, &srcfile_htab[hval]); 14262306a36Sopenharmony_ci map_total_sz += h->maplen; 14362306a36Sopenharmony_ci num_srcfiles++; 14462306a36Sopenharmony_ci return h; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ciout_map: 14762306a36Sopenharmony_ci munmap(h->map, sz); 14862306a36Sopenharmony_ciout_fn: 14962306a36Sopenharmony_ci zfree(&h->fn); 15062306a36Sopenharmony_ciout_h: 15162306a36Sopenharmony_ci free(h); 15262306a36Sopenharmony_ci return NULL; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci/* Result is not 0 terminated */ 15662306a36Sopenharmony_cichar *find_sourceline(char *fn, unsigned line, int *lenp) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci char *l, *p; 15962306a36Sopenharmony_ci struct srcfile *sf = find_srcfile(fn); 16062306a36Sopenharmony_ci if (!sf) 16162306a36Sopenharmony_ci return NULL; 16262306a36Sopenharmony_ci line--; 16362306a36Sopenharmony_ci if (line >= sf->numlines) 16462306a36Sopenharmony_ci return NULL; 16562306a36Sopenharmony_ci l = sf->lines[line]; 16662306a36Sopenharmony_ci if (!l) 16762306a36Sopenharmony_ci return NULL; 16862306a36Sopenharmony_ci p = memchr(l, '\n', sf->map + sf->maplen - l); 16962306a36Sopenharmony_ci *lenp = p - l; 17062306a36Sopenharmony_ci return l; 17162306a36Sopenharmony_ci} 172