forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
3.4 KiB
171 lines
3.4 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* Manage printing of source lines |
|
* Copyright (c) 2017, Intel Corporation. |
|
* Author: Andi Kleen |
|
*/ |
|
#include <linux/list.h> |
|
#include <linux/zalloc.h> |
|
#include <stdlib.h> |
|
#include <sys/mman.h> |
|
#include <sys/stat.h> |
|
#include <fcntl.h> |
|
#include <unistd.h> |
|
#include <assert.h> |
|
#include <string.h> |
|
#include "srccode.h" |
|
#include "debug.h" |
|
#include <internal/lib.h> // page_size |
|
#include "fncache.h" |
|
|
|
#define MAXSRCCACHE (32*1024*1024) |
|
#define MAXSRCFILES 64 |
|
#define SRC_HTAB_SZ 64 |
|
|
|
struct srcfile { |
|
struct hlist_node hash_nd; |
|
struct list_head nd; |
|
char *fn; |
|
char **lines; |
|
char *map; |
|
unsigned numlines; |
|
size_t maplen; |
|
}; |
|
|
|
static struct hlist_head srcfile_htab[SRC_HTAB_SZ]; |
|
static LIST_HEAD(srcfile_list); |
|
static long map_total_sz; |
|
static int num_srcfiles; |
|
|
|
static int countlines(char *map, int maplen) |
|
{ |
|
int numl; |
|
char *end = map + maplen; |
|
char *p = map; |
|
|
|
if (maplen == 0) |
|
return 0; |
|
numl = 0; |
|
while (p < end && (p = memchr(p, '\n', end - p)) != NULL) { |
|
numl++; |
|
p++; |
|
} |
|
if (p < end) |
|
numl++; |
|
return numl; |
|
} |
|
|
|
static void fill_lines(char **lines, int maxline, char *map, int maplen) |
|
{ |
|
int l; |
|
char *end = map + maplen; |
|
char *p = map; |
|
|
|
if (maplen == 0 || maxline == 0) |
|
return; |
|
l = 0; |
|
lines[l++] = map; |
|
while (p < end && (p = memchr(p, '\n', end - p)) != NULL) { |
|
if (l >= maxline) |
|
return; |
|
lines[l++] = ++p; |
|
} |
|
if (p < end) |
|
lines[l] = p; |
|
} |
|
|
|
static void free_srcfile(struct srcfile *sf) |
|
{ |
|
list_del_init(&sf->nd); |
|
hlist_del(&sf->hash_nd); |
|
map_total_sz -= sf->maplen; |
|
munmap(sf->map, sf->maplen); |
|
zfree(&sf->lines); |
|
zfree(&sf->fn); |
|
free(sf); |
|
num_srcfiles--; |
|
} |
|
|
|
static struct srcfile *find_srcfile(char *fn) |
|
{ |
|
struct stat st; |
|
struct srcfile *h; |
|
int fd; |
|
unsigned long sz; |
|
unsigned hval = shash((unsigned char *)fn) % SRC_HTAB_SZ; |
|
|
|
hlist_for_each_entry (h, &srcfile_htab[hval], hash_nd) { |
|
if (!strcmp(fn, h->fn)) { |
|
/* Move to front */ |
|
list_move(&h->nd, &srcfile_list); |
|
return h; |
|
} |
|
} |
|
|
|
/* Only prune if there is more than one entry */ |
|
while ((num_srcfiles > MAXSRCFILES || map_total_sz > MAXSRCCACHE) && |
|
srcfile_list.next != &srcfile_list) { |
|
assert(!list_empty(&srcfile_list)); |
|
h = list_entry(srcfile_list.prev, struct srcfile, nd); |
|
free_srcfile(h); |
|
} |
|
|
|
fd = open(fn, O_RDONLY); |
|
if (fd < 0 || fstat(fd, &st) < 0) { |
|
pr_debug("cannot open source file %s\n", fn); |
|
return NULL; |
|
} |
|
|
|
h = malloc(sizeof(struct srcfile)); |
|
if (!h) |
|
return NULL; |
|
|
|
h->fn = strdup(fn); |
|
if (!h->fn) |
|
goto out_h; |
|
|
|
h->maplen = st.st_size; |
|
sz = (h->maplen + page_size - 1) & ~(page_size - 1); |
|
h->map = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0); |
|
close(fd); |
|
if (h->map == (char *)-1) { |
|
pr_debug("cannot mmap source file %s\n", fn); |
|
goto out_fn; |
|
} |
|
h->numlines = countlines(h->map, h->maplen); |
|
h->lines = calloc(h->numlines, sizeof(char *)); |
|
if (!h->lines) |
|
goto out_map; |
|
fill_lines(h->lines, h->numlines, h->map, h->maplen); |
|
list_add(&h->nd, &srcfile_list); |
|
hlist_add_head(&h->hash_nd, &srcfile_htab[hval]); |
|
map_total_sz += h->maplen; |
|
num_srcfiles++; |
|
return h; |
|
|
|
out_map: |
|
munmap(h->map, sz); |
|
out_fn: |
|
zfree(&h->fn); |
|
out_h: |
|
free(h); |
|
return NULL; |
|
} |
|
|
|
/* Result is not 0 terminated */ |
|
char *find_sourceline(char *fn, unsigned line, int *lenp) |
|
{ |
|
char *l, *p; |
|
struct srcfile *sf = find_srcfile(fn); |
|
if (!sf) |
|
return NULL; |
|
line--; |
|
if (line >= sf->numlines) |
|
return NULL; |
|
l = sf->lines[line]; |
|
if (!l) |
|
return NULL; |
|
p = memchr(l, '\n', sf->map + sf->maplen - l); |
|
*lenp = p - l; |
|
return l; |
|
}
|
|
|