Commit 519ce9fe authored by Ingo Molnar's avatar Ingo Molnar

Merge tag 'perf-core-for-mingo' of...

Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

User visible changes:

  - Fix 'perf probe' segfault when glob matching function without debuginfo (Wang Nan)

  - Remove newline char when reading event scale and unit (Madhavan Srinivasan)

  - Deal with kernel module names in '[]' correctly (Wang Nan)

Infrastructure changes:

  - Fix the search for the kernel DSO on the unified list (Arnaldo Carvalho de Melo)

  - Move tools/perf/util/include/linux/{kernel.h,list.h,poison.h} to tools/include,
    to be used in tools/lib/bpf/ (Wang Nan)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 5c9b9bc6 1f121b03
#ifndef PERF_LINUX_KERNEL_H_
#define PERF_LINUX_KERNEL_H_
#ifndef __TOOLS_LINUX_KERNEL_H
#define __TOOLS_LINUX_KERNEL_H
#include <stdarg.h>
#include <stdio.h>
......
#include <linux/kernel.h>
#include <linux/types.h>
#include "../../../../include/linux/list.h"
#include "../../../include/linux/list.h"
#ifndef PERF_LIST_H
#define PERF_LIST_H
#ifndef TOOLS_LIST_H
#define TOOLS_LIST_H
/**
* list_del_range - deletes range of entries from list.
* @begin: first element in the range to delete from the list.
......
#include "../../../include/linux/poison.h"
......@@ -40,7 +40,10 @@ tools/include/linux/bitops.h
tools/include/linux/compiler.h
tools/include/linux/export.h
tools/include/linux/hash.h
tools/include/linux/kernel.h
tools/include/linux/list.h
tools/include/linux/log2.h
tools/include/linux/poison.h
tools/include/linux/types.h
include/asm-generic/bitops/arch_hweight.h
include/asm-generic/bitops/const_hweight.h
......
......@@ -34,9 +34,21 @@ static int test(const char *path, bool alloc_name, bool alloc_ext,
return 0;
}
static int test_is_kernel_module(const char *path, int cpumode, bool expect)
{
TEST_ASSERT_VAL("is_kernel_module",
(!!is_kernel_module(path, cpumode)) == (!!expect));
pr_debug("%s (cpumode: %d) - is_kernel_module: %s\n",
path, cpumode, expect ? "true" : "false");
return 0;
}
#define T(path, an, ae, k, c, n, e) \
TEST_ASSERT_VAL("failed", !test(path, an, ae, k, c, n, e))
#define M(path, c, e) \
TEST_ASSERT_VAL("failed", !test_is_kernel_module(path, c, e))
int test__kmod_path__parse(void)
{
/* path alloc_name alloc_ext kmod comp name ext */
......@@ -44,30 +56,90 @@ int test__kmod_path__parse(void)
T("/xxxx/xxxx/x-x.ko", false , true , true, false, NULL , NULL);
T("/xxxx/xxxx/x-x.ko", true , false , true, false, "[x_x]", NULL);
T("/xxxx/xxxx/x-x.ko", false , false , true, false, NULL , NULL);
M("/xxxx/xxxx/x-x.ko", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true);
M("/xxxx/xxxx/x-x.ko", PERF_RECORD_MISC_KERNEL, true);
M("/xxxx/xxxx/x-x.ko", PERF_RECORD_MISC_USER, false);
/* path alloc_name alloc_ext kmod comp name ext */
T("/xxxx/xxxx/x.ko.gz", true , true , true, true, "[x]", "gz");
T("/xxxx/xxxx/x.ko.gz", false , true , true, true, NULL , "gz");
T("/xxxx/xxxx/x.ko.gz", true , false , true, true, "[x]", NULL);
T("/xxxx/xxxx/x.ko.gz", false , false , true, true, NULL , NULL);
M("/xxxx/xxxx/x.ko.gz", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true);
M("/xxxx/xxxx/x.ko.gz", PERF_RECORD_MISC_KERNEL, true);
M("/xxxx/xxxx/x.ko.gz", PERF_RECORD_MISC_USER, false);
/* path alloc_name alloc_ext kmod comp name ext */
T("/xxxx/xxxx/x.gz", true , true , false, true, "x.gz" ,"gz");
T("/xxxx/xxxx/x.gz", false , true , false, true, NULL ,"gz");
T("/xxxx/xxxx/x.gz", true , false , false, true, "x.gz" , NULL);
T("/xxxx/xxxx/x.gz", false , false , false, true, NULL , NULL);
M("/xxxx/xxxx/x.gz", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false);
M("/xxxx/xxxx/x.gz", PERF_RECORD_MISC_KERNEL, false);
M("/xxxx/xxxx/x.gz", PERF_RECORD_MISC_USER, false);
/* path alloc_name alloc_ext kmod comp name ext */
T("x.gz", true , true , false, true, "x.gz", "gz");
T("x.gz", false , true , false, true, NULL , "gz");
T("x.gz", true , false , false, true, "x.gz", NULL);
T("x.gz", false , false , false, true, NULL , NULL);
M("x.gz", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false);
M("x.gz", PERF_RECORD_MISC_KERNEL, false);
M("x.gz", PERF_RECORD_MISC_USER, false);
/* path alloc_name alloc_ext kmod comp name ext */
T("x.ko.gz", true , true , true, true, "[x]", "gz");
T("x.ko.gz", false , true , true, true, NULL , "gz");
T("x.ko.gz", true , false , true, true, "[x]", NULL);
T("x.ko.gz", false , false , true, true, NULL , NULL);
M("x.ko.gz", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true);
M("x.ko.gz", PERF_RECORD_MISC_KERNEL, true);
M("x.ko.gz", PERF_RECORD_MISC_USER, false);
/* path alloc_name alloc_ext kmod comp name ext */
T("[test_module]", true , true , true, false, "[test_module]", NULL);
T("[test_module]", false , true , true, false, NULL , NULL);
T("[test_module]", true , false , true, false, "[test_module]", NULL);
T("[test_module]", false , false , true, false, NULL , NULL);
M("[test_module]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true);
M("[test_module]", PERF_RECORD_MISC_KERNEL, true);
M("[test_module]", PERF_RECORD_MISC_USER, false);
/* path alloc_name alloc_ext kmod comp name ext */
T("[test.module]", true , true , true, false, "[test.module]", NULL);
T("[test.module]", false , true , true, false, NULL , NULL);
T("[test.module]", true , false , true, false, "[test.module]", NULL);
T("[test.module]", false , false , true, false, NULL , NULL);
M("[test.module]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, true);
M("[test.module]", PERF_RECORD_MISC_KERNEL, true);
M("[test.module]", PERF_RECORD_MISC_USER, false);
/* path alloc_name alloc_ext kmod comp name ext */
T("[vdso]", true , true , false, false, "[vdso]", NULL);
T("[vdso]", false , true , false, false, NULL , NULL);
T("[vdso]", true , false , false, false, "[vdso]", NULL);
T("[vdso]", false , false , false, false, NULL , NULL);
M("[vdso]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false);
M("[vdso]", PERF_RECORD_MISC_KERNEL, false);
M("[vdso]", PERF_RECORD_MISC_USER, false);
/* path alloc_name alloc_ext kmod comp name ext */
T("[vsyscall]", true , true , false, false, "[vsyscall]", NULL);
T("[vsyscall]", false , true , false, false, NULL , NULL);
T("[vsyscall]", true , false , false, false, "[vsyscall]", NULL);
T("[vsyscall]", false , false , false, false, NULL , NULL);
M("[vsyscall]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false);
M("[vsyscall]", PERF_RECORD_MISC_KERNEL, false);
M("[vsyscall]", PERF_RECORD_MISC_USER, false);
/* path alloc_name alloc_ext kmod comp name ext */
T("[kernel.kallsyms]", true , true , false, false, "[kernel.kallsyms]", NULL);
T("[kernel.kallsyms]", false , true , false, false, NULL , NULL);
T("[kernel.kallsyms]", true , false , false, false, "[kernel.kallsyms]", NULL);
T("[kernel.kallsyms]", false , false , false, false, NULL , NULL);
M("[kernel.kallsyms]", PERF_RECORD_MISC_CPUMODE_UNKNOWN, false);
M("[kernel.kallsyms]", PERF_RECORD_MISC_KERNEL, false);
M("[kernel.kallsyms]", PERF_RECORD_MISC_USER, false);
return 0;
}
......@@ -166,12 +166,28 @@ bool is_supported_compression(const char *ext)
return false;
}
bool is_kernel_module(const char *pathname)
bool is_kernel_module(const char *pathname, int cpumode)
{
struct kmod_path m;
if (kmod_path__parse(&m, pathname))
return NULL;
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;
}
......@@ -215,12 +231,33 @@ int __kmod_path__parse(struct kmod_path *m, const char *path,
{
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) {
if ((ext == NULL) || is_simple_name) {
if (alloc_name) {
m->name = strdup(name);
return m->name ? 0 : -ENOMEM;
......
......@@ -220,7 +220,7 @@ char dso__symtab_origin(const struct dso *dso);
int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type,
char *root_dir, char *filename, size_t size);
bool is_supported_compression(const char *ext);
bool is_kernel_module(const char *pathname);
bool is_kernel_module(const char *pathname, int cpumode);
bool decompress_to_file(const char *ext, const char *filename, int output_fd);
bool dso__needs_decompress(struct dso *dso);
......
......@@ -1239,7 +1239,7 @@ static int __event_process_build_id(struct build_id_event *bev,
{
int err = -1;
struct machine *machine;
u16 misc;
u16 cpumode;
struct dso *dso;
enum dso_kernel_type dso_type;
......@@ -1247,9 +1247,9 @@ static int __event_process_build_id(struct build_id_event *bev,
if (!machine)
goto out;
misc = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
cpumode = bev->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
switch (misc) {
switch (cpumode) {
case PERF_RECORD_MISC_KERNEL:
dso_type = DSO_TYPE_KERNEL;
break;
......@@ -1270,7 +1270,7 @@ static int __event_process_build_id(struct build_id_event *bev,
dso__set_build_id(dso, &bev->build_id);
if (!is_kernel_module(filename))
if (!is_kernel_module(filename, cpumode))
dso->kernel = dso_type;
build_id__sprintf(dso->build_id, sizeof(dso->build_id),
......
#include "../../../../include/linux/poison.h"
......@@ -1149,9 +1149,29 @@ static int machine__process_kernel_mmap_event(struct machine *machine,
struct dso *dso;
list_for_each_entry(dso, &machine->dsos.head, node) {
if (dso->kernel && is_kernel_module(dso->long_name))
/*
* The cpumode passed to is_kernel_module is not the
* cpumode of *this* event. If we insist on passing
* correct cpumode to is_kernel_module, we should
* record the cpumode when we adding this dso to the
* linked list.
*
* However we don't really need passing correct
* cpumode. We know the correct cpumode must be kernel
* mode (if not, we should not link it onto kernel_dsos
* list).
*
* Therefore, we pass PERF_RECORD_MISC_CPUMODE_UNKNOWN.
* is_kernel_module() treats it as a kernel cpumode.
*/
if (!dso->kernel ||
is_kernel_module(dso->long_name,
PERF_RECORD_MISC_CPUMODE_UNKNOWN))
continue;
kernel = dso;
break;
}
......
......@@ -112,7 +112,11 @@ static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *
if (sret < 0)
goto error;
scale[sret] = '\0';
if (scale[sret - 1] == '\n')
scale[sret - 1] = '\0';
else
scale[sret] = '\0';
/*
* save current locale
*/
......@@ -154,7 +158,10 @@ static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *n
close(fd);
alias->unit[sret] = '\0';
if (alias->unit[sret - 1] == '\n')
alias->unit[sret - 1] = '\0';
else
alias->unit[sret] = '\0';
return 0;
error:
......
......@@ -2494,7 +2494,8 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
return ret;
}
static int find_probe_functions(struct map *map, char *name)
static int find_probe_functions(struct map *map, char *name,
struct symbol **syms)
{
int found = 0;
struct symbol *sym;
......@@ -2504,8 +2505,11 @@ static int find_probe_functions(struct map *map, char *name)
return 0;
map__for_each_symbol(map, sym, tmp) {
if (strglobmatch(sym->name, name))
if (strglobmatch(sym->name, name)) {
found++;
if (syms && found < probe_conf.max_probes)
syms[found - 1] = sym;
}
}
return found;
......@@ -2528,11 +2532,12 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
struct map *map = NULL;
struct ref_reloc_sym *reloc_sym = NULL;
struct symbol *sym;
struct symbol **syms = NULL;
struct probe_trace_event *tev;
struct perf_probe_point *pp = &pev->point;
struct probe_trace_point *tp;
int num_matched_functions;
int ret, i;
int ret, i, j;
map = get_target_map(pev->target, pev->uprobes);
if (!map) {
......@@ -2540,11 +2545,17 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
goto out;
}
syms = malloc(sizeof(struct symbol *) * probe_conf.max_probes);
if (!syms) {
ret = -ENOMEM;
goto out;
}
/*
* Load matched symbols: Since the different local symbols may have
* same name but different addresses, this lists all the symbols.
*/
num_matched_functions = find_probe_functions(map, pp->function);
num_matched_functions = find_probe_functions(map, pp->function, syms);
if (num_matched_functions == 0) {
pr_err("Failed to find symbol %s in %s\n", pp->function,
pev->target ? : "kernel");
......@@ -2575,7 +2586,9 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
ret = 0;
map__for_each_symbol_by_name(map, pp->function, sym) {
for (j = 0; j < num_matched_functions; j++) {
sym = syms[j];
tev = (*tevs) + ret;
tp = &tev->point;
if (ret == num_matched_functions) {
......@@ -2599,6 +2612,8 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
tp->symbol = strdup_or_goto(sym->name, nomem_out);
tp->offset = pp->offset;
}
tp->realname = strdup_or_goto(sym->name, nomem_out);
tp->retprobe = pp->retprobe;
if (pev->target)
tev->point.module = strdup_or_goto(pev->target,
......@@ -2629,6 +2644,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
out:
put_target_map(map, pev->uprobes);
free(syms);
return ret;
nomem_out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment