12025cf9eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2dd2e18e9SAndi Kleen /*
3dd2e18e9SAndi Kleen * Manage printing of source lines
4dd2e18e9SAndi Kleen * Copyright (c) 2017, Intel Corporation.
5dd2e18e9SAndi Kleen * Author: Andi Kleen
6dd2e18e9SAndi Kleen */
7d8f9da24SArnaldo Carvalho de Melo #include <linux/list.h>
8d8f9da24SArnaldo Carvalho de Melo #include <linux/zalloc.h>
9dd2e18e9SAndi Kleen #include <stdlib.h>
10dd2e18e9SAndi Kleen #include <sys/mman.h>
11dd2e18e9SAndi Kleen #include <sys/stat.h>
12dd2e18e9SAndi Kleen #include <fcntl.h>
13dd2e18e9SAndi Kleen #include <unistd.h>
14dd2e18e9SAndi Kleen #include <assert.h>
15dd2e18e9SAndi Kleen #include <string.h>
16dd2e18e9SAndi Kleen #include "srccode.h"
17dd2e18e9SAndi Kleen #include "debug.h"
1820f2be1dSJiri Olsa #include <internal/lib.h> // page_size
19d9664582SAndi Kleen #include "fncache.h"
20dd2e18e9SAndi Kleen
21dd2e18e9SAndi Kleen #define MAXSRCCACHE (32*1024*1024)
22dd2e18e9SAndi Kleen #define MAXSRCFILES 64
23dd2e18e9SAndi Kleen #define SRC_HTAB_SZ 64
24dd2e18e9SAndi Kleen
25dd2e18e9SAndi Kleen struct srcfile {
26dd2e18e9SAndi Kleen struct hlist_node hash_nd;
27dd2e18e9SAndi Kleen struct list_head nd;
28dd2e18e9SAndi Kleen char *fn;
29dd2e18e9SAndi Kleen char **lines;
30dd2e18e9SAndi Kleen char *map;
31dd2e18e9SAndi Kleen unsigned numlines;
32dd2e18e9SAndi Kleen size_t maplen;
33dd2e18e9SAndi Kleen };
34dd2e18e9SAndi Kleen
35dd2e18e9SAndi Kleen static struct hlist_head srcfile_htab[SRC_HTAB_SZ];
36dd2e18e9SAndi Kleen static LIST_HEAD(srcfile_list);
37dd2e18e9SAndi Kleen static long map_total_sz;
38dd2e18e9SAndi Kleen static int num_srcfiles;
39dd2e18e9SAndi Kleen
countlines(char * map,int maplen)40dd2e18e9SAndi Kleen static int countlines(char *map, int maplen)
41dd2e18e9SAndi Kleen {
42dd2e18e9SAndi Kleen int numl;
43dd2e18e9SAndi Kleen char *end = map + maplen;
44dd2e18e9SAndi Kleen char *p = map;
45dd2e18e9SAndi Kleen
46dd2e18e9SAndi Kleen if (maplen == 0)
47dd2e18e9SAndi Kleen return 0;
48dd2e18e9SAndi Kleen numl = 0;
49dd2e18e9SAndi Kleen while (p < end && (p = memchr(p, '\n', end - p)) != NULL) {
50dd2e18e9SAndi Kleen numl++;
51dd2e18e9SAndi Kleen p++;
52dd2e18e9SAndi Kleen }
53dd2e18e9SAndi Kleen if (p < end)
54dd2e18e9SAndi Kleen numl++;
55dd2e18e9SAndi Kleen return numl;
56dd2e18e9SAndi Kleen }
57dd2e18e9SAndi Kleen
fill_lines(char ** lines,int maxline,char * map,int maplen)58dd2e18e9SAndi Kleen static void fill_lines(char **lines, int maxline, char *map, int maplen)
59dd2e18e9SAndi Kleen {
60dd2e18e9SAndi Kleen int l;
61dd2e18e9SAndi Kleen char *end = map + maplen;
62dd2e18e9SAndi Kleen char *p = map;
63dd2e18e9SAndi Kleen
64dd2e18e9SAndi Kleen if (maplen == 0 || maxline == 0)
65dd2e18e9SAndi Kleen return;
66dd2e18e9SAndi Kleen l = 0;
67dd2e18e9SAndi Kleen lines[l++] = map;
68dd2e18e9SAndi Kleen while (p < end && (p = memchr(p, '\n', end - p)) != NULL) {
69dd2e18e9SAndi Kleen if (l >= maxline)
70dd2e18e9SAndi Kleen return;
71dd2e18e9SAndi Kleen lines[l++] = ++p;
72dd2e18e9SAndi Kleen }
73dd2e18e9SAndi Kleen if (p < end)
74dd2e18e9SAndi Kleen lines[l] = p;
75dd2e18e9SAndi Kleen }
76dd2e18e9SAndi Kleen
free_srcfile(struct srcfile * sf)77dd2e18e9SAndi Kleen static void free_srcfile(struct srcfile *sf)
78dd2e18e9SAndi Kleen {
79e56fbc9dSArnaldo Carvalho de Melo list_del_init(&sf->nd);
80dd2e18e9SAndi Kleen hlist_del(&sf->hash_nd);
81dd2e18e9SAndi Kleen map_total_sz -= sf->maplen;
82dd2e18e9SAndi Kleen munmap(sf->map, sf->maplen);
83d8f9da24SArnaldo Carvalho de Melo zfree(&sf->lines);
84d8f9da24SArnaldo Carvalho de Melo zfree(&sf->fn);
85dd2e18e9SAndi Kleen free(sf);
86dd2e18e9SAndi Kleen num_srcfiles--;
87dd2e18e9SAndi Kleen }
88dd2e18e9SAndi Kleen
find_srcfile(char * fn)89dd2e18e9SAndi Kleen static struct srcfile *find_srcfile(char *fn)
90dd2e18e9SAndi Kleen {
91dd2e18e9SAndi Kleen struct stat st;
92dd2e18e9SAndi Kleen struct srcfile *h;
93dd2e18e9SAndi Kleen int fd;
94dd2e18e9SAndi Kleen unsigned long sz;
95dd2e18e9SAndi Kleen unsigned hval = shash((unsigned char *)fn) % SRC_HTAB_SZ;
96dd2e18e9SAndi Kleen
97dd2e18e9SAndi Kleen hlist_for_each_entry (h, &srcfile_htab[hval], hash_nd) {
98dd2e18e9SAndi Kleen if (!strcmp(fn, h->fn)) {
99dd2e18e9SAndi Kleen /* Move to front */
100*f54cad25SZou Wei list_move(&h->nd, &srcfile_list);
101dd2e18e9SAndi Kleen return h;
102dd2e18e9SAndi Kleen }
103dd2e18e9SAndi Kleen }
104dd2e18e9SAndi Kleen
105dd2e18e9SAndi Kleen /* Only prune if there is more than one entry */
106dd2e18e9SAndi Kleen while ((num_srcfiles > MAXSRCFILES || map_total_sz > MAXSRCCACHE) &&
107dd2e18e9SAndi Kleen srcfile_list.next != &srcfile_list) {
108dd2e18e9SAndi Kleen assert(!list_empty(&srcfile_list));
109dd2e18e9SAndi Kleen h = list_entry(srcfile_list.prev, struct srcfile, nd);
110dd2e18e9SAndi Kleen free_srcfile(h);
111dd2e18e9SAndi Kleen }
112dd2e18e9SAndi Kleen
113dd2e18e9SAndi Kleen fd = open(fn, O_RDONLY);
114dd2e18e9SAndi Kleen if (fd < 0 || fstat(fd, &st) < 0) {
115dd2e18e9SAndi Kleen pr_debug("cannot open source file %s\n", fn);
116dd2e18e9SAndi Kleen return NULL;
117dd2e18e9SAndi Kleen }
118dd2e18e9SAndi Kleen
119dd2e18e9SAndi Kleen h = malloc(sizeof(struct srcfile));
120dd2e18e9SAndi Kleen if (!h)
121dd2e18e9SAndi Kleen return NULL;
122dd2e18e9SAndi Kleen
123dd2e18e9SAndi Kleen h->fn = strdup(fn);
124dd2e18e9SAndi Kleen if (!h->fn)
125dd2e18e9SAndi Kleen goto out_h;
126dd2e18e9SAndi Kleen
127dd2e18e9SAndi Kleen h->maplen = st.st_size;
128dd2e18e9SAndi Kleen sz = (h->maplen + page_size - 1) & ~(page_size - 1);
129dd2e18e9SAndi Kleen h->map = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
130dd2e18e9SAndi Kleen close(fd);
131dd2e18e9SAndi Kleen if (h->map == (char *)-1) {
132dd2e18e9SAndi Kleen pr_debug("cannot mmap source file %s\n", fn);
133dd2e18e9SAndi Kleen goto out_fn;
134dd2e18e9SAndi Kleen }
135dd2e18e9SAndi Kleen h->numlines = countlines(h->map, h->maplen);
136dd2e18e9SAndi Kleen h->lines = calloc(h->numlines, sizeof(char *));
137dd2e18e9SAndi Kleen if (!h->lines)
138dd2e18e9SAndi Kleen goto out_map;
139dd2e18e9SAndi Kleen fill_lines(h->lines, h->numlines, h->map, h->maplen);
140dd2e18e9SAndi Kleen list_add(&h->nd, &srcfile_list);
141dd2e18e9SAndi Kleen hlist_add_head(&h->hash_nd, &srcfile_htab[hval]);
142dd2e18e9SAndi Kleen map_total_sz += h->maplen;
143dd2e18e9SAndi Kleen num_srcfiles++;
144dd2e18e9SAndi Kleen return h;
145dd2e18e9SAndi Kleen
146dd2e18e9SAndi Kleen out_map:
147dd2e18e9SAndi Kleen munmap(h->map, sz);
148dd2e18e9SAndi Kleen out_fn:
149d8f9da24SArnaldo Carvalho de Melo zfree(&h->fn);
150dd2e18e9SAndi Kleen out_h:
151dd2e18e9SAndi Kleen free(h);
152dd2e18e9SAndi Kleen return NULL;
153dd2e18e9SAndi Kleen }
154dd2e18e9SAndi Kleen
155dd2e18e9SAndi Kleen /* Result is not 0 terminated */
find_sourceline(char * fn,unsigned line,int * lenp)156dd2e18e9SAndi Kleen char *find_sourceline(char *fn, unsigned line, int *lenp)
157dd2e18e9SAndi Kleen {
158dd2e18e9SAndi Kleen char *l, *p;
159dd2e18e9SAndi Kleen struct srcfile *sf = find_srcfile(fn);
160dd2e18e9SAndi Kleen if (!sf)
161dd2e18e9SAndi Kleen return NULL;
162dd2e18e9SAndi Kleen line--;
163dd2e18e9SAndi Kleen if (line >= sf->numlines)
164dd2e18e9SAndi Kleen return NULL;
165dd2e18e9SAndi Kleen l = sf->lines[line];
166dd2e18e9SAndi Kleen if (!l)
167dd2e18e9SAndi Kleen return NULL;
168dd2e18e9SAndi Kleen p = memchr(l, '\n', sf->map + sf->maplen - l);
169dd2e18e9SAndi Kleen *lenp = p - l;
170dd2e18e9SAndi Kleen return l;
171dd2e18e9SAndi Kleen }
172