cregit-Linux how code gets into the kernel

Release 4.10 tools/perf/util/probe-event.c

Directory: tools/perf/util
/*
 * probe-event.c : perf-probe definition to probe_events format converter
 *
 * Written by Masami Hiramatsu <mhiramat@redhat.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 */

#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <limits.h>
#include <elf.h>

#include "util.h"
#include "event.h"
#include "strlist.h"
#include "debug.h"
#include "cache.h"
#include "color.h"
#include "symbol.h"
#include "thread.h"
#include <api/fs/fs.h>
#include "trace-event.h"	/* For __maybe_unused */
#include "probe-event.h"
#include "probe-finder.h"
#include "probe-file.h"
#include "session.h"


#define MAX_CMDLEN 256

#define PERFPROBE_GROUP "probe"


bool probe_event_dry_run;	
/* Dry run flag */

struct probe_conf probe_conf;


#define semantic_error(msg ...) pr_err("Semantic error :" msg)


int e_snprintf(char *str, size_t size, const char *format, ...) { int ret; va_list ap; va_start(ap, format); ret = vsnprintf(str, size, format, ap); va_end(ap); if (ret >= (int)size) ret = -E2BIG; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu67100.00%1100.00%
Total67100.00%1100.00%

static struct machine *host_machine; /* Initialize symbol maps and path of vmlinux/modules */
int init_probe_symbol_maps(bool user_only) { int ret; symbol_conf.sort_by_name = true; symbol_conf.allow_aliases = true; ret = symbol__init(NULL); if (ret < 0) { pr_debug("Failed to init symbol map.\n"); goto out; } if (host_machine || user_only) /* already initialized */ return 0; if (symbol_conf.vmlinux_name) pr_debug("Use vmlinux: %s\n", symbol_conf.vmlinux_name); host_machine = machine__new_host(); if (!host_machine) { pr_debug("machine__new_host() failed.\n"); symbol__exit(); ret = -1; } out: if (ret < 0) pr_warning("Failed to init vmlinux path.\n"); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu9786.61%444.44%
namhyung kimnamhyung kim108.93%333.33%
arnaldo carvalho de meloarnaldo carvalho de melo32.68%111.11%
yanmin zhangyanmin zhang21.79%111.11%
Total112100.00%9100.00%


void exit_probe_symbol_maps(void) { machine__delete(host_machine); host_machine = NULL; symbol__exit(); }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu1894.74%150.00%
namhyung kimnamhyung kim15.26%150.00%
Total19100.00%2100.00%


static struct symbol *__find_kernel_function_by_name(const char *name, struct map **mapp) { return machine__find_kernel_function_by_name(host_machine, name, mapp); }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu29100.00%3100.00%
Total29100.00%3100.00%


static struct symbol *__find_kernel_function(u64 addr, struct map **mapp) { return machine__find_kernel_function(host_machine, addr, mapp); }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu27100.00%1100.00%
Total27100.00%1100.00%


static struct ref_reloc_sym *kernel_get_ref_reloc_sym(void) { /* kmap->ref_reloc_sym should be set if host_machine is initialized */ struct kmap *kmap; struct map *map = machine__kernel_map(host_machine); if (map__load(map) < 0) return NULL; kmap = map__kmap(map); if (!kmap) return NULL; return kmap->ref_reloc_sym; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu3865.52%133.33%
arnaldo carvalho de meloarnaldo carvalho de melo1220.69%133.33%
wang nanwang nan813.79%133.33%
Total58100.00%3100.00%


static int kernel_get_symbol_address_by_name(const char *name, u64 *addr, bool reloc, bool reladdr) { struct ref_reloc_sym *reloc_sym; struct symbol *sym; struct map *map; /* ref_reloc_sym is just a label. Need a special fix*/ reloc_sym = kernel_get_ref_reloc_sym(); if (reloc_sym && strcmp(name, reloc_sym->name) == 0) *addr = (reloc) ? reloc_sym->addr : reloc_sym->unrelocated_addr; else { sym = __find_kernel_function_by_name(name, &map); if (!sym) return -ENOENT; *addr = map->unmap_ip(map, sym->start) - ((reloc) ? 0 : map->reloc) - ((reladdr) ? map->start : 0); } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu13398.52%266.67%
he kuanghe kuang21.48%133.33%
Total135100.00%3100.00%


static struct map *kernel_get_module_map(const char *module) { struct map_groups *grp = &host_machine->kmaps; struct maps *maps = &grp->maps[MAP__FUNCTION]; struct map *pos; /* A file path -- this is an offline module */ if (module && strchr(module, '/')) return dso__new_map(module); if (!module) module = "kernel"; for (pos = maps__first(maps); pos; pos = map__next(pos)) { /* short_name is "[module]" */ if (strncmp(pos->dso->short_name + 1, module, pos->dso->short_name_len - 2) == 0 && module[pos->dso->short_name_len - 2] == '\0') { map__get(pos); return pos; } } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu10272.86%562.50%
arnaldo carvalho de meloarnaldo carvalho de melo2417.14%225.00%
konstantin khlebnikovkonstantin khlebnikov1410.00%112.50%
Total140100.00%8100.00%


struct map *get_target_map(const char *target, bool user) { /* Init maps of given executable or kernel */ if (user) return dso__new_map(target); else return kernel_get_module_map(target); }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu33100.00%1100.00%
Total33100.00%1100.00%


static int convert_exec_to_group(const char *exec, char **result) { char *ptr1, *ptr2, *exec_copy; char buf[64]; int ret; exec_copy = strdup(exec); if (!exec_copy) return -ENOMEM; ptr1 = basename(exec_copy); if (!ptr1) { ret = -EINVAL; goto out; } for (ptr2 = ptr1; *ptr2 != '\0'; ptr2++) { if (!isalnum(*ptr2) && *ptr2 != '_') { *ptr2 = '\0'; break; } } ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1); if (ret < 0) goto out; *result = strdup(buf); ret = *result ? 0 : -ENOMEM; out: free(exec_copy); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu16299.39%375.00%
colin kingcolin king10.61%125.00%
Total163100.00%4100.00%


static void clear_perf_probe_point(struct perf_probe_point *pp) { free(pp->file); free(pp->function); free(pp->lazy_line); }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu32100.00%1100.00%
Total32100.00%1100.00%


static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) { int i; for (i = 0; i < ntevs; i++) clear_probe_trace_event(tevs + i); }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu37100.00%1100.00%
Total37100.00%1100.00%

static bool kprobe_blacklist__listed(unsigned long address);
static bool kprobe_warn_out_range(const char *symbol, unsigned long address) { u64 etext_addr = 0; int ret; /* Get the address of _etext for checking non-probable text symbol */ ret = kernel_get_symbol_address_by_name("_etext", &etext_addr, false, false); if (ret == 0 && etext_addr < address) pr_warning("%s is out of .text, skip it.\n", symbol); else if (kprobe_blacklist__listed(address)) pr_warning("%s is blacklisted function, skip it.\n", symbol); else return false; return true; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu6787.01%266.67%
he kuanghe kuang1012.99%133.33%
Total77100.00%3100.00%

/* * @module can be module name of module file path. In case of path, * inspect elf and find out what is actual module name. * Caller has to free mod_name after using it. */
static char *find_module_name(const char *module) { int fd; Elf *elf; GElf_Ehdr ehdr; GElf_Shdr shdr; Elf_Data *data; Elf_Scn *sec; char *mod_name = NULL; int name_offset; fd = open(module, O_RDONLY); if (fd < 0) return NULL; elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); if (elf == NULL) goto elf_err; if (gelf_getehdr(elf, &ehdr) == NULL) goto ret_err; sec = elf_section_by_name(elf, &ehdr, &shdr, ".gnu.linkonce.this_module", NULL); if (!sec) goto ret_err; data = elf_getdata(sec, NULL); if (!data || !data->d_buf) goto ret_err; /* * NOTE: * '.gnu.linkonce.this_module' section of kernel module elf directly * maps to 'struct module' from linux/module.h. This section contains * actual module name which will be used by kernel after loading it. * But, we cannot use 'struct module' here since linux/module.h is not * exposed to user-space. Offset of 'name' has remained same from long * time, so hardcoding it here. */ if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) name_offset = 12; else /* expect ELFCLASS64 by default */ name_offset = 24; mod_name = strdup((char *)data->d_buf + name_offset); ret_err: elf_end(elf); elf_err: close(fd); return mod_name; }

Contributors

PersonTokensPropCommitsCommitProp
ravi bangoriaravi bangoria17086.73%150.00%
masami hiramatsumasami hiramatsu2613.27%150.00%
Total196100.00%2100.00%

#ifdef HAVE_DWARF_SUPPORT
static int kernel_get_module_dso(const char *module, struct dso **pdso) { struct dso *dso; struct map *map; const char *vmlinux_name; int ret = 0; if (module) { char module_name[128]; snprintf(module_name, sizeof(module_name), "[%s]", module); map = map_groups__find_by_name(&host_machine->kmaps, MAP__FUNCTION, module_name); if (map) { dso = map->dso; goto found; } pr_debug("Failed to find module %s.\n", module); return -ENOENT; } map = machine__kernel_map(host_machine); dso = map->dso; vmlinux_name = symbol_conf.vmlinux_name; dso->load_errno = 0; if (vmlinux_name) ret = dso__load_vmlinux(dso, map, vmlinux_name, false); else ret = dso__load_vmlinux_path(dso, map); found: *pdso = dso; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
wang nanwang nan12776.97%133.33%
arnaldo carvalho de meloarnaldo carvalho de melo3823.03%266.67%
Total165100.00%3100.00%

/* * Some binaries like glibc have special symbols which are on the symbol * table, but not in the debuginfo. If we can find the address of the * symbol from map, we can translate the address back to the probe point. */
static int find_alternative_probe_point(struct debuginfo *dinfo, struct perf_probe_point *pp, struct perf_probe_point *result, const char *target, bool uprobes) { struct map *map = NULL; struct symbol *sym; u64 address = 0; int ret = -ENOENT; /* This can work only for function-name based one */ if (!pp->function || pp->file) return -ENOTSUP; map = get_target_map(target, uprobes); if (!map) return -EINVAL; /* Find the address of given function */ map__for_each_symbol_by_name(map, pp->function, sym) { if (uprobes) address = sym->start; else address = map->unmap_ip(map, sym->start) - map->reloc; break; } if (!address) { ret = -ENOENT; goto out; } pr_debug("Symbol %s address found : %" PRIx64 "\n", pp->function, address); ret = debuginfo__find_probe_point(dinfo, (unsigned long)address, result); if (ret <= 0) ret = (!ret) ? -ENOENT : ret; else { result->offset += pp->offset; result->line += pp->line; result->retprobe = pp->retprobe; ret = 0; } out: map__put(map); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu21795.18%466.67%
he kuanghe kuang83.51%116.67%
wang nanwang nan31.32%116.67%
Total228100.00%6100.00%


static int get_alternative_probe_event(struct debuginfo *dinfo, struct perf_probe_event *pev, struct perf_probe_point *tmp) { int ret; memcpy(tmp, &pev->point, sizeof(*tmp)); memset(&pev->point, 0, sizeof(pev->point)); ret = find_alternative_probe_point(dinfo, tmp, &pev->point, pev->target, pev->uprobes); if (ret < 0) memcpy(&pev->point, tmp, sizeof(*tmp)); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu104100.00%4100.00%
Total104100.00%4100.00%


static int get_alternative_line_range(struct debuginfo *dinfo, struct line_range *lr, const char *target, bool user) { struct perf_probe_point pp = { .function = lr->function, .file = lr->file, .line = lr->start }; struct perf_probe_point result; int ret, len = 0; memset(&result, 0, sizeof(result)); if (lr->end != INT_MAX) len = lr->end - lr->start; ret = find_alternative_probe_point(dinfo, &pp, &result, target, user); if (!ret) { lr->function = result.function; lr->file = result.file; lr->start = result.line; if (lr->end != INT_MAX) lr->end = lr->start + len; clear_perf_probe_point(&pp); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu14183.93%150.00%
david aherndavid ahern2716.07%150.00%
Total168100.00%2100.00%

/* Open new debuginfo of given module */
static struct debuginfo *open_debuginfo(const char *module, bool silent) { const char *path = module; char reason[STRERR_BUFSIZE]; struct debuginfo *ret = NULL; struct dso *dso = NULL; int err; if (!module || !strchr(module, '/')) { err = kernel_get_module_dso(module, &dso); if (err < 0) { if (!dso || dso->load_errno == 0) { if (!str_error_r(-err, reason, STRERR_BUFSIZE)) strcpy(reason, "(unknown)"); } else dso__strerror_load(dso, reason, STRERR_BUFSIZE); if (!silent) pr_err("Failed to find the path for %s: %s\n", module ?: "kernel", reason); return NULL; } path = dso->long_name; } ret = debuginfo__new(path); if (!ret && !silent) { pr_warning("The %s file has no debug information.\n", path); if (!module || !strtailcmp(path, ".ko")) pr_warning("Rebuild with CONFIG_DEBUG_INFO=y, "); else pr_warning("Rebuild with -g, "); pr_warning("or install an appropriate debuginfo package.\n"); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu20399.51%787.50%
arnaldo carvalho de meloarnaldo carvalho de melo10.49%112.50%
Total204100.00%8100.00%

/* For caching the last debuginfo */ static struct debuginfo *debuginfo_cache; static char *debuginfo_cache_path;
static struct debuginfo *debuginfo_cache__open(const char *module, bool silent) { const char *path = module; /* If the module is NULL, it should be the kernel. */ if (!module) path = "kernel"; if (debuginfo_cache_path && !strcmp(debuginfo_cache_path, path)) goto out; /* Copy module path */ free(debuginfo_cache_path); debuginfo_cache_path = strdup(path); if (!debuginfo_cache_path) { debuginfo__delete(debuginfo_cache); debuginfo_cache = NULL; goto out; } debuginfo_cache = open_debuginfo(module, silent); if (!debuginfo_cache) zfree(&debuginfo_cache_path); out: return debuginfo_cache; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu105100.00%2100.00%
Total105100.00%2100.00%


static void debuginfo_cache__exit(void) { debuginfo__delete(debuginfo_cache); debuginfo_cache = NULL; zfree(&debuginfo_cache_path); }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu23100.00%1100.00%
Total23100.00%1100.00%


static int get_text_start_address(const char *exec, unsigned long *address) { Elf *elf; GElf_Ehdr ehdr; GElf_Shdr shdr; int fd, ret = -ENOENT; fd = open(exec, O_RDONLY); if (fd < 0) return -errno; elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); if (elf == NULL) { ret = -EINVAL; goto out_close; } if (gelf_getehdr(elf, &ehdr) == NULL) goto out; if (!elf_section_by_name(elf, &ehdr, &shdr, ".text", NULL)) goto out; *address = shdr.sh_addr - shdr.sh_offset; ret = 0; out: elf_end(elf); out_close: close(fd); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu148100.00%10100.00%
Total148100.00%10100.00%

/* * Convert trace point to probe point with debuginfo */
static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp, struct perf_probe_point *pp, bool is_kprobe) { struct debuginfo *dinfo = NULL; unsigned long stext = 0; u64 addr = tp->address; int ret = -ENOENT; /* convert the address to dwarf address */ if (!is_kprobe) { if (!addr) { ret = -EINVAL; goto error; } ret = get_text_start_address(tp->module, &stext); if (ret < 0) goto error; addr += stext; } else if (tp->symbol) { /* If the module is given, this returns relative address */ ret = kernel_get_symbol_address_by_name(tp->symbol, &addr, false, !!tp->module); if (ret != 0) goto error; addr += tp->offset; } pr_debug("try to find information at %" PRIx64 " in %s\n", addr, tp->module ? : "kernel"); dinfo = debuginfo_cache__open(tp->module, verbose == 0); if (dinfo) ret = debuginfo__find_probe_point(dinfo, (unsigned long)addr, pp); else ret = -ENOENT; if (ret > 0) { pp->retprobe = tp->retprobe; return 0; } error: pr_debug("Failed to find corresponding probes from debuginfo.\n"); return ret ? : -ENOENT; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu21896.89%583.33%
wang nanwang nan73.11%116.67%
Total225100.00%6100.00%

/* Adjust symbol name and address */
static int post_process_probe_trace_point(struct probe_trace_point *tp, struct map *map, unsigned long offs) { struct symbol *sym; u64 addr = tp->address + tp->offset - offs; sym = map__find_symbol(map, addr); if (!sym) return -ENOENT; if (strcmp(sym->name, tp->symbol)) { /* If we have no realname, use symbol for it */ if (!tp->realname) tp->realname = tp->symbol; else free(tp->symbol); tp->symbol = strdup(sym->name); if (!tp->symbol) return -ENOMEM; } tp->offset = addr - sym->start; tp->address -= offs; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
masami hiramatsumasami hiramatsu136100.00%1100.00%
Total136100.00%1100.00%

/* * Rename DWARF symbols to ELF symbols -- gcc sometimes optimizes functions * and generate new symbols with suffixes such as .constprop.N or .isra.N * etc. Since those symbols are not recorded in DWARF, we have to find * correct generated symbols from offline ELF binary. * For online kernel or uprobes we don't need this because those are * rebased on _text, or already a section relative address. */
static int post_process_offline_probe_trace_events(struct probe_trace_event *tevs, int ntevs, const char *pathname) { struct map *map; unsigned long stext = 0; int i, ret = 0; /* Prepare a map for offline binary */ map = dso__new_map(pathname); if (