Skip to content

Commit

Permalink
Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux…
Browse files Browse the repository at this point in the history
…/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: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
  • Loading branch information
Ingo Molnar committed Jun 4, 2015
2 parents 5c9b9bc + 1f121b0 commit 519ce9f
Show file tree
Hide file tree
Showing 12 changed files with 179 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#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>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
#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.
Expand Down
1 change: 1 addition & 0 deletions tools/include/linux/poison.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include "../../../include/linux/poison.h"
3 changes: 3 additions & 0 deletions tools/perf/MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
72 changes: 72 additions & 0 deletions tools/perf/tests/kmod-path.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,40 +34,112 @@ 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 */
T("/xxxx/xxxx/x-x.ko", true , true , true, false, "[x_x]", NULL);
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;
}
47 changes: 42 additions & 5 deletions tools/perf/util/dso.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion tools/perf/util/dso.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
8 changes: 4 additions & 4 deletions tools/perf/util/header.c
Original file line number Diff line number Diff line change
Expand Up @@ -1239,17 +1239,17 @@ 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;

machine = perf_session__findnew_machine(session, bev->pid);
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;
Expand All @@ -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),
Expand Down
1 change: 0 additions & 1 deletion tools/perf/util/include/linux/poison.h

This file was deleted.

22 changes: 21 additions & 1 deletion tools/perf/util/machine.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
11 changes: 9 additions & 2 deletions tools/perf/util/pmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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:
Expand Down
26 changes: 21 additions & 5 deletions tools/perf/util/probe-event.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -2528,23 +2532,30 @@ 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) {
ret = -EINVAL;
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");
Expand Down Expand Up @@ -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) {
Expand All @@ -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,
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit 519ce9f

Please sign in to comment.