cregit-Linux how code gets into the kernel

Release 4.10 tools/perf/util/dso.c

Directory: tools/perf/util
#include <asm/bug.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "symbol.h"
#include "dso.h"
#include "machine.h"
#include "auxtrace.h"
#include "util.h"
#include "debug.h"
#include "vdso.h"


char dso__symtab_origin(const struct dso *dso) { static const char origin[] = { [DSO_BINARY_TYPE__KALLSYMS] = 'k', [DSO_BINARY_TYPE__VMLINUX] = 'v', [DSO_BINARY_TYPE__JAVA_JIT] = 'j', [DSO_BINARY_TYPE__DEBUGLINK] = 'l', [DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B', [DSO_BINARY_TYPE__FEDORA_DEBUGINFO] = 'f', [DSO_BINARY_TYPE__UBUNTU_DEBUGINFO] = 'u', [DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO] = 'o', [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP] = 'm', [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', [DSO_BINARY_TYPE__GUEST_KMODULE_COMP] = 'M', [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', }; if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND) return '!'; return origin[dso->symtab_type]; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa12086.96%133.33%
namhyung kimnamhyung kim128.70%133.33%
ricardo ribaldaricardo ribalda64.35%133.33%
Total138100.00%3100.00%


int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, char *root_dir, char *filename, size_t size) { char build_id_hex[SBUILD_ID_SIZE]; int ret = 0; size_t len; switch (type) { case DSO_BINARY_TYPE__DEBUGLINK: { char *debuglink; len = __symbol__join_symfs(filename, size, dso->long_name); debuglink = filename + len; while (debuglink != filename && *debuglink != '/') debuglink--; if (*debuglink == '/') debuglink++; ret = -1; if (!is_regular_file(filename)) break; ret = filename__read_debuglink(filename, debuglink, size - (debuglink - filename)); } break; case DSO_BINARY_TYPE__BUILD_ID_CACHE: if (dso__build_id_filename(dso, filename, size) == NULL) ret = -1; break; case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: len = __symbol__join_symfs(filename, size, "/usr/lib/debug"); snprintf(filename + len, size - len, "%s.debug", dso->long_name); break; case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: len = __symbol__join_symfs(filename, size, "/usr/lib/debug"); snprintf(filename + len, size - len, "%s", dso->long_name); break; case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: { const char *last_slash; size_t dir_size; last_slash = dso->long_name + dso->long_name_len; while (last_slash != dso->long_name && *last_slash != '/') last_slash--; len = __symbol__join_symfs(filename, size, ""); dir_size = last_slash - dso->long_name + 2; if (dir_size > (size - len)) { ret = -1; break; } len += scnprintf(filename + len, dir_size, "%s", dso->long_name); len += scnprintf(filename + len , size - len, ".debug%s", last_slash); break; } case DSO_BINARY_TYPE__BUILDID_DEBUGINFO: if (!dso->has_build_id) { ret = -1; break; } build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id_hex); len = __symbol__join_symfs(filename, size, "/usr/lib/debug/.build-id/"); snprintf(filename + len, size - len, "%.2s/%s.debug", build_id_hex, build_id_hex + 2); break; case DSO_BINARY_TYPE__VMLINUX: case DSO_BINARY_TYPE__GUEST_VMLINUX: case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: __symbol__join_symfs(filename, size, dso->long_name); break; case DSO_BINARY_TYPE__GUEST_KMODULE: case DSO_BINARY_TYPE__GUEST_KMODULE_COMP: path__join3(filename, size, symbol_conf.symfs, root_dir, dso->long_name); break; case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP: __symbol__join_symfs(filename, size, dso->long_name); break; case DSO_BINARY_TYPE__KCORE: case DSO_BINARY_TYPE__GUEST_KCORE: snprintf(filename, size, "%s", dso->long_name); break; default: case DSO_BINARY_TYPE__KALLSYMS: case DSO_BINARY_TYPE__GUEST_KALLSYMS: case DSO_BINARY_TYPE__JAVA_JIT: case DSO_BINARY_TYPE__NOT_FOUND: ret = -1; break; } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa27655.31%214.29%
ricardo ribaldaricardo ribalda10721.44%17.14%
arnaldo carvalho de meloarnaldo carvalho de melo7515.03%535.71%
adrian hunteradrian hunter255.01%214.29%
victor kamenskyvictor kamensky71.40%17.14%
namhyung kimnamhyung kim61.20%17.14%
stephane eranianstephane eranian20.40%17.14%
masami hiramatsumasami hiramatsu10.20%17.14%
Total499100.00%14100.00%

static const struct { const char *fmt; int (*decompress)(const char *input, int output); } compressions[] = { #ifdef HAVE_ZLIB_SUPPORT { "gz", gzip_decompress_to_file }, #endif #ifdef HAVE_LZMA_SUPPORT { "xz", lzma_decompress_to_file }, #endif { NULL, NULL }, };
bool is_supported_compression(const char *ext) { unsigned i; for (i = 0; compressions[i].fmt; i++) { if (!strcmp(ext, compressions[i].fmt)) return true; } return false; }

Contributors

PersonTokensPropCommitsCommitProp
namhyung kimnamhyung kim52100.00%1100.00%
Total52100.00%1100.00%


bool is_kernel_module(const char *pathname, int cpumode) { struct kmod_path m; int mode = cpumode & PERF_RECORD_MISC_CPUMODE_MASK; WARN_ONCE(mode != cpumode, "Internal error: passing unmasked cpumode (%x) to is_kernel_module", cpumode); switch (mode) { case PERF_RECORD_MISC_USER: case PERF_RECORD_MISC_HYPERVISOR: case PERF_RECORD_MISC_GUEST_USER: return false; /* Treat PERF_RECORD_MISC_CPUMODE_UNKNOWN as kernel */ default: if (kmod_path__parse(&m, pathname)) { pr_err("Failed to check whether %s is a kernel module or not. Assume it is.", pathname); return true; } } return m.kmod; }

Contributors

PersonTokensPropCommitsCommitProp
wang nanwang nan5162.20%133.33%
namhyung kimnamhyung kim1821.95%133.33%
jiri olsajiri olsa1315.85%133.33%
Total82100.00%3100.00%


bool decompress_to_file(const char *ext, const char *filename, int output_fd) { unsigned i; for (i = 0; compressions[i].fmt; i++) { if (!strcmp(ext, compressions[i].fmt)) return !compressions[i].decompress(filename, output_fd); } return false; }

Contributors

PersonTokensPropCommitsCommitProp
namhyung kimnamhyung kim71100.00%1100.00%
Total71100.00%1100.00%


bool dso__needs_decompress(struct dso *dso) { return dso->symtab_type == DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP || dso->symtab_type == DSO_BINARY_TYPE__GUEST_KMODULE_COMP; }

Contributors

PersonTokensPropCommitsCommitProp
namhyung kimnamhyung kim23100.00%1100.00%
Total23100.00%1100.00%

/* * Parses kernel module specified in @path and updates * @m argument like: * * @comp - true if @path contains supported compression suffix, * false otherwise * @kmod - true if @path contains '.ko' suffix in right position, * false otherwise * @name - if (@alloc_name && @kmod) is true, it contains strdup-ed base name * of the kernel module without suffixes, otherwise strudup-ed * base name of @path * @ext - if (@alloc_ext && @comp) is true, it contains strdup-ed string * the compression suffix * * Returns 0 if there's no strdup error, -ENOMEM otherwise. */
int __kmod_path__parse(struct kmod_path *m, const char *path, bool alloc_name, bool alloc_ext) { const char *name = strrchr(path, '/'); const char *ext = strrchr(path, '.'); bool is_simple_name = false; memset(m, 0x0, sizeof(*m)); name = name ? name + 1 : path; /* * '.' is also a valid character for module name. For example: * [aaa.bbb] is a valid module name. '[' should have higher * priority than '.ko' suffix. * * The kernel names are from machine__mmap_name. Such * name should belong to kernel itself, not kernel module. */ if (name[0] == '[') { is_simple_name = true; if ((strncmp(name, "[kernel.kallsyms]", 17) == 0) || (strncmp(name, "[guest.kernel.kallsyms", 22) == 0) || (strncmp(name, "[vdso]", 6) == 0) || (strncmp(name, "[vsyscall]", 10) == 0)) { m->kmod = false; } else m->kmod = true; } /* No extension, just return name. */ if ((ext == NULL) || is_simple_name) { if (alloc_name) { m->name = strdup(name); return m->name ? 0 : -ENOMEM; } return 0; } if (is_supported_compression(ext + 1)) { m->comp = true; ext -= 3; } /* Check .ko extension only if there's enough name left. */ if (ext > name) m->kmod = !strncmp(ext, ".ko", 3); if (alloc_name) { if (m->kmod) { if (asprintf(&m->name, "[%.*s]", (int) (ext - name), name) == -1) return -ENOMEM; } else { if (asprintf(&m->name, "%s", name) == -1) return -ENOMEM; } strxfrchar(m->name, '-', '_'); } if (alloc_ext && m->comp) { m->ext = strdup(ext + 4); if (!m->ext) { free((void *) m->name); return -ENOMEM; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa27474.46%150.00%
wang nanwang nan9425.54%150.00%
Total368100.00%2100.00%

/* * Global list of open DSOs and the counter. */ static LIST_HEAD(dso__data_open); static long dso__data_open_cnt; static pthread_mutex_t dso__data_open_lock = PTHREAD_MUTEX_INITIALIZER;
static void dso__list_add(struct dso *dso) { list_add_tail(&dso->data.open_entry, &dso__data_open); dso__data_open_cnt++; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa27100.00%2100.00%
Total27100.00%2100.00%


static void dso__list_del(struct dso *dso) { list_del(&dso->data.open_entry); WARN_ONCE(dso__data_open_cnt <= 0, "DSO data fd counter out of bounds."); dso__data_open_cnt--; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa33100.00%2100.00%
Total33100.00%2100.00%

static void close_first_dso(void);
static int do_open(char *name) { int fd; char sbuf[STRERR_BUFSIZE]; do { fd = open(name, O_RDONLY); if (fd >= 0) return fd; pr_debug("dso open failed: %s\n", str_error_r(errno, sbuf, sizeof(sbuf))); if (!dso__data_open_cnt || errno != EMFILE) break; close_first_dso(); } while (1); return -1; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa6481.01%125.00%
masami hiramatsumasami hiramatsu1316.46%125.00%
arnaldo carvalho de meloarnaldo carvalho de melo11.27%125.00%
namhyung kimnamhyung kim11.27%125.00%
Total79100.00%4100.00%


static int __open_dso(struct dso *dso, struct machine *machine) { int fd; char *root_dir = (char *)""; char *name = malloc(PATH_MAX); if (!name) return -ENOMEM; if (machine) root_dir = machine->root_dir; if (dso__read_binary_type_filename(dso, dso->binary_type, root_dir, name, PATH_MAX)) { free(name); return -EINVAL; } if (!is_regular_file(name)) return -EINVAL; fd = do_open(name); free(name); return fd; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa10593.75%466.67%
arnaldo carvalho de meloarnaldo carvalho de melo76.25%233.33%
Total112100.00%6100.00%

static void check_data_close(void); /** * dso_close - Open DSO data file * @dso: dso object * * Open @dso's data file descriptor and updates * list/count of open DSO objects. */
static int open_dso(struct dso *dso, struct machine *machine) { int fd = __open_dso(dso, machine); if (fd >= 0) { dso__list_add(dso); /* * Check if we crossed the allowed number * of opened DSOs and close one if needed. */ check_data_close(); } return fd; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa4597.83%266.67%
adrian hunteradrian hunter12.17%133.33%
Total46100.00%3100.00%


static void close_data_fd(struct dso *dso) { if (dso->data.fd >= 0) { close(dso->data.fd); dso->data.fd = -1; dso->data.file_size = 0; dso__list_del(dso); } }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa54100.00%3100.00%
Total54100.00%3100.00%

/** * dso_close - Close DSO data file * @dso: dso object * * Close @dso's data file descriptor and updates * list/count of open DSO objects. */
static void close_dso(struct dso *dso) { close_data_fd(dso); }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa16100.00%1100.00%
Total16100.00%1100.00%


static void close_first_dso(void) { struct dso *dso; dso = list_first_entry(&dso__data_open, struct dso, data.open_entry); close_dso(dso); }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa33100.00%1100.00%
Total33100.00%1100.00%


static rlim_t get_fd_limit(void) { struct rlimit l; rlim_t limit = 0; /* Allow half of the current open fd limit. */ if (getrlimit(RLIMIT_NOFILE, &l) == 0) { if (l.rlim_cur == RLIM_INFINITY) limit = l.rlim_cur; else limit = l.rlim_cur / 2; } else { pr_err("failed to get fd limit\n"); limit = 1; } return limit; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa70100.00%1100.00%
Total70100.00%1100.00%

static rlim_t fd_limit; /* * Used only by tests/dso-data.c to reset the environment * for tests. I dont expect we should change this during * standard runtime. */
void reset_fd_limit(void) { fd_limit = 0; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa11100.00%1100.00%
Total11100.00%1100.00%


static bool may_cache_fd(void) { if (!fd_limit) fd_limit = get_fd_limit(); if (fd_limit == RLIM_INFINITY) return true; return fd_limit > (rlim_t) dso__data_open_cnt; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa35100.00%2100.00%
Total35100.00%2100.00%

/* * Check and close LRU dso if we crossed allowed limit * for opened dso file descriptors. The limit is half * of the RLIMIT_NOFILE files opened. */
static void check_data_close(void) { bool cache_fd = may_cache_fd(); if (!cache_fd) close_first_dso(); }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa22100.00%1100.00%
Total22100.00%1100.00%

/** * dso__data_close - Close DSO data file * @dso: dso object * * External interface to close @dso's data file descriptor. */
void dso__data_close(struct dso *dso) { pthread_mutex_lock(&dso__data_open_lock); close_dso(dso); pthread_mutex_unlock(&dso__data_open_lock); }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa1555.56%150.00%
namhyung kimnamhyung kim1244.44%150.00%
Total27100.00%2100.00%


static void try_to_open_dso(struct dso *dso, struct machine *machine) { enum dso_binary_type binary_type_data[] = { DSO_BINARY_TYPE__BUILD_ID_CACHE, DSO_BINARY_TYPE__SYSTEM_PATH_DSO, DSO_BINARY_TYPE__NOT_FOUND, }; int i = 0; if (dso->data.fd >= 0) return; if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) { dso->data.fd = open_dso(dso, machine); goto out; } do { dso->binary_type = binary_type_data[i++]; dso->data.fd = open_dso(dso, machine); if (dso->data.fd >= 0) goto out; } while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND); out: if (dso->data.fd >= 0) dso->data.status = DSO_DATA_STATUS_OK; else dso->data.status = DSO_DATA_STATUS_ERROR; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa9564.19%240.00%
adrian hunteradrian hunter4530.41%120.00%
namhyung kimnamhyung kim53.38%120.00%
arnaldo carvalho de meloarnaldo carvalho de melo32.03%120.00%
Total148100.00%5100.00%

/** * dso__data_get_fd - Get dso's data file descriptor * @dso: dso object * @machine: machine object * * External interface to find dso's file, open it and * returns file descriptor. It should be paired with * dso__data_put_fd() if it returns non-negative value. */
int dso__data_get_fd(struct dso *dso, struct machine *machine) { if (dso->data.status == DSO_DATA_STATUS_ERROR) return -1; if (pthread_mutex_lock(&dso__data_open_lock) < 0) return -1; try_to_open_dso(dso, machine); if (dso->data.fd < 0) pthread_mutex_unlock(&dso__data_open_lock); return dso->data.fd; }

Contributors

PersonTokensPropCommitsCommitProp
namhyung kimnamhyung kim6589.04%360.00%
adrian hunteradrian hunter56.85%120.00%
jiri olsajiri olsa34.11%120.00%
Total73100.00%5100.00%


void dso__data_put_fd(struct dso *dso __maybe_unused) { pthread_mutex_unlock(&dso__data_open_lock); }

Contributors

PersonTokensPropCommitsCommitProp
namhyung kimnamhyung kim17100.00%1100.00%
Total17100.00%1100.00%


bool dso__data_status_seen(struct dso *dso, enum dso_data_status_seen by) { u32 flag = 1 << by; if (dso->data.status_seen & flag) return true; dso->data.status_seen |= flag; return false; }

Contributors

PersonTokensPropCommitsCommitProp
adrian hunteradrian hunter45100.00%1100.00%
Total45100.00%1100.00%


static void dso_cache__free(struct dso *dso) { struct rb_root *root = &dso->data.cache; struct rb_node *next = rb_first(root); pthread_mutex_lock(&dso->lock); while (next) { struct dso_cache *cache; cache = rb_entry(next, struct dso_cache, rb_node); next = rb_next(&cache->rb_node); rb_erase(&cache->rb_node, root); free(cache); } pthread_mutex_unlock(&dso->lock); }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa6769.07%150.00%
namhyung kimnamhyung kim3030.93%150.00%
Total97100.00%2100.00%


static struct dso_cache *dso_cache__find(struct dso *dso, u64 offset) { const struct rb_root *root = &dso->data.cache; struct rb_node * const *p = &root->rb_node; const struct rb_node *parent = NULL; struct dso_cache *cache; while (*p != NULL) { u64 end; parent = *p; cache = rb_entry(parent, struct dso_cache, rb_node); end = cache->offset + DSO__DATA_CACHE_SIZE; if (offset < cache->offset) p = &(*p)->rb_left; else if (offset >= end) p = &(*p)->rb_right; else return cache; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa11687.22%133.33%
namhyung kimnamhyung kim1511.28%133.33%
arnaldo carvalho de meloarnaldo carvalho de melo21.50%133.33%
Total133100.00%3100.00%


static struct dso_cache * dso_cache__insert(struct dso *dso, struct dso_cache *new) { struct rb_root *root = &dso->data.cache; struct rb_node **p = &root->rb_node; struct rb_node *parent = NULL; struct dso_cache *cache; u64 offset = new->offset; pthread_mutex_lock(&dso->lock); while (*p != NULL) { u64 end; parent = *p; cache = rb_entry(parent, struct dso_cache, rb_node); end = cache->offset + DSO__DATA_CACHE_SIZE; if (offset < cache->offset) p = &(*p)->rb_left; else if (offset >= end) p = &(*p)->rb_right; else goto out; } rb_link_node(&new->rb_node, parent, p); rb_insert_color(&new->rb_node, root); cache = NULL; out: pthread_mutex_unlock(&dso->lock); return cache; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa13774.86%150.00%
namhyung kimnamhyung kim4625.14%150.00%
Total183100.00%2100.00%


static ssize_t dso_cache__memcpy(struct dso_cache *cache, u64 offset, u8 *data, u64 size) { u64 cache_offset = offset - cache->offset; u64 cache_size = min(cache->size - cache_offset, size); memcpy(data, cache->data + cache_offset, cache_size); return cache_size; }

Contributors

PersonTokensPropCommitsCommitProp
jiri olsajiri olsa60100.00%1100.00%
Total60100.00%1100.00%


static ssize_t dso_cache__read(struct dso *dso, struct machine *machine, u64 offset, u8 *data, ssize_t size) { struct dso_cache *cache; struct dso_cache *old; ssize_t ret; do { u64 cache_offset; cache = zalloc(sizeof(*cache) + DSO__DATA_CACHE_SIZE); if (!cache) return -ENOMEM; pthread_mutex_lock(&dso__data_open_lock); /* * dso->data.fd might be closed if other thread opened another * file (dso) due to open file limit (RLIMIT_NOFILE). */ try_to_open_dso(dso, machine); if (dso->data.fd < 0) { ret = -errno; dso->data.status = DSO_DATA_STATUS_ERROR; break; } cache_offset = offset & DSO__DATA_CACHE_MASK; ret = pread(dso->data.fd, cache->data, DSO__DATA_CACHE_SIZE, cache_offset); if (ret <= 0) break; cache->offset = cache_offset; cache->size = ret; } while (0); pthread_mutex_unlock(&dso__data_open_lock); if (ret > 0) { old = dso_cache__insert(dso, cache); if (old) { /* we lose the race */ free(cache); cache = old