Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-block.git] / tools / perf / util / srccode.c
1 /*
2  * Manage printing of source lines
3  * Copyright (c) 2017, Intel Corporation.
4  * Author: Andi Kleen
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  */
15 #include "linux/list.h"
16 #include <stdlib.h>
17 #include <sys/mman.h>
18 #include <sys/stat.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <assert.h>
22 #include <string.h>
23 #include "srccode.h"
24 #include "debug.h"
25 #include "util.h"
26
27 #define MAXSRCCACHE (32*1024*1024)
28 #define MAXSRCFILES     64
29 #define SRC_HTAB_SZ     64
30
31 struct srcfile {
32         struct hlist_node hash_nd;
33         struct list_head nd;
34         char *fn;
35         char **lines;
36         char *map;
37         unsigned numlines;
38         size_t maplen;
39 };
40
41 static struct hlist_head srcfile_htab[SRC_HTAB_SZ];
42 static LIST_HEAD(srcfile_list);
43 static long map_total_sz;
44 static int num_srcfiles;
45
46 static unsigned shash(unsigned char *s)
47 {
48         unsigned h = 0;
49         while (*s)
50                 h = 65599 * h + *s++;
51         return h ^ (h >> 16);
52 }
53
54 static int countlines(char *map, int maplen)
55 {
56         int numl;
57         char *end = map + maplen;
58         char *p = map;
59
60         if (maplen == 0)
61                 return 0;
62         numl = 0;
63         while (p < end && (p = memchr(p, '\n', end - p)) != NULL) {
64                 numl++;
65                 p++;
66         }
67         if (p < end)
68                 numl++;
69         return numl;
70 }
71
72 static void fill_lines(char **lines, int maxline, char *map, int maplen)
73 {
74         int l;
75         char *end = map + maplen;
76         char *p = map;
77
78         if (maplen == 0 || maxline == 0)
79                 return;
80         l = 0;
81         lines[l++] = map;
82         while (p < end && (p = memchr(p, '\n', end - p)) != NULL) {
83                 if (l >= maxline)
84                         return;
85                 lines[l++] = ++p;
86         }
87         if (p < end)
88                 lines[l] = p;
89 }
90
91 static void free_srcfile(struct srcfile *sf)
92 {
93         list_del(&sf->nd);
94         hlist_del(&sf->hash_nd);
95         map_total_sz -= sf->maplen;
96         munmap(sf->map, sf->maplen);
97         free(sf->lines);
98         free(sf->fn);
99         free(sf);
100         num_srcfiles--;
101 }
102
103 static struct srcfile *find_srcfile(char *fn)
104 {
105         struct stat st;
106         struct srcfile *h;
107         int fd;
108         unsigned long sz;
109         unsigned hval = shash((unsigned char *)fn) % SRC_HTAB_SZ;
110
111         hlist_for_each_entry (h, &srcfile_htab[hval], hash_nd) {
112                 if (!strcmp(fn, h->fn)) {
113                         /* Move to front */
114                         list_del(&h->nd);
115                         list_add(&h->nd, &srcfile_list);
116                         return h;
117                 }
118         }
119
120         /* Only prune if there is more than one entry */
121         while ((num_srcfiles > MAXSRCFILES || map_total_sz > MAXSRCCACHE) &&
122                srcfile_list.next != &srcfile_list) {
123                 assert(!list_empty(&srcfile_list));
124                 h = list_entry(srcfile_list.prev, struct srcfile, nd);
125                 free_srcfile(h);
126         }
127
128         fd = open(fn, O_RDONLY);
129         if (fd < 0 || fstat(fd, &st) < 0) {
130                 pr_debug("cannot open source file %s\n", fn);
131                 return NULL;
132         }
133
134         h = malloc(sizeof(struct srcfile));
135         if (!h)
136                 return NULL;
137
138         h->fn = strdup(fn);
139         if (!h->fn)
140                 goto out_h;
141
142         h->maplen = st.st_size;
143         sz = (h->maplen + page_size - 1) & ~(page_size - 1);
144         h->map = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0);
145         close(fd);
146         if (h->map == (char *)-1) {
147                 pr_debug("cannot mmap source file %s\n", fn);
148                 goto out_fn;
149         }
150         h->numlines = countlines(h->map, h->maplen);
151         h->lines = calloc(h->numlines, sizeof(char *));
152         if (!h->lines)
153                 goto out_map;
154         fill_lines(h->lines, h->numlines, h->map, h->maplen);
155         list_add(&h->nd, &srcfile_list);
156         hlist_add_head(&h->hash_nd, &srcfile_htab[hval]);
157         map_total_sz += h->maplen;
158         num_srcfiles++;
159         return h;
160
161 out_map:
162         munmap(h->map, sz);
163 out_fn:
164         free(h->fn);
165 out_h:
166         free(h);
167         return NULL;
168 }
169
170 /* Result is not 0 terminated */
171 char *find_sourceline(char *fn, unsigned line, int *lenp)
172 {
173         char *l, *p;
174         struct srcfile *sf = find_srcfile(fn);
175         if (!sf)
176                 return NULL;
177         line--;
178         if (line >= sf->numlines)
179                 return NULL;
180         l = sf->lines[line];
181         if (!l)
182                 return NULL;
183         p = memchr(l, '\n', sf->map + sf->maplen - l);
184         *lenp = p - l;
185         return l;
186 }