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