Commit c50f6245 authored by Ingo Molnar's avatar Ingo Molnar

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

Merge tag 'perf-core-for-mingo-20160704' 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:

Documentation changes:

 - Update android build documentation (Chris Phlipot)

Infrastructure changes:

 - Respect WERROR=0 in libapi and libsubcmd, to allow building on Android (Chris Phlipot)

 - Prep work to support SDT events in probe cache (Masami Hiramatsu)

 - ELF support for SDT (Hemant Kumar)

 - Add feature detection for libelf's elf_getshdrstrndx function (Arnaldo Carvalho de Melo)

 - Fix hist accumulation test (Jiri Olsa)

 - Unwind callchain fixes (Jiri Olsa)

 - Change internal representation of numa nodes obtained from
   perf.data header (Jiri Olsa)

 - Sync copy of syscall_64.tbl with the kernel (Arnaldo Carvalho de Melo)

 - Add LGPL 2.1 license header to libbpf source files (Wang Nan)
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents dc29bb47 f3d082ce
......@@ -40,6 +40,7 @@ FEATURE_TESTS_BASIC := \
libbfd \
libelf \
libelf-getphdrnum \
libelf-getshdrstrndx \
libelf-mmap \
libnuma \
numa_num_possible_cpus \
......
......@@ -17,6 +17,7 @@ FILES= \
test-cplus-demangle.bin \
test-libelf.bin \
test-libelf-getphdrnum.bin \
test-libelf-getshdrstrndx.bin \
test-libelf-mmap.bin \
test-libnuma.bin \
test-numa_num_possible_cpus.bin \
......@@ -98,6 +99,9 @@ $(OUTPUT)test-libelf-mmap.bin:
$(OUTPUT)test-libelf-getphdrnum.bin:
$(BUILD) -lelf
$(OUTPUT)test-libelf-getshdrstrndx.bin:
$(BUILD) -lelf
$(OUTPUT)test-libnuma.bin:
$(BUILD) -lnuma
......
......@@ -49,6 +49,10 @@
# include "test-libelf-getphdrnum.c"
#undef main
#define main main_test_libelf_getshdrstrndx
# include "test-libelf-getshdrstrndx.c"
#undef main
#define main main_test_libunwind
# include "test-libunwind.c"
#undef main
......@@ -149,6 +153,7 @@ int main(int argc, char *argv[])
main_test_dwarf();
main_test_dwarf_getlocations();
main_test_libelf_getphdrnum();
main_test_libelf_getshdrstrndx();
main_test_libunwind();
main_test_libaudit();
main_test_libslang();
......
#include <libelf.h>
int main(void)
{
size_t dst;
return elf_getshdrstrndx(0, &dst);
}
......@@ -17,7 +17,13 @@ MAKEFLAGS += --no-print-directory
LIBFILE = $(OUTPUT)libapi.a
CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC
CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -O6 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC
# Treat warnings as errors unless directed not to
ifneq ($(WERROR),0)
CFLAGS += -Werror
endif
CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
CFLAGS += -I$(srctree)/tools/lib/api
......
......@@ -4,6 +4,19 @@
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
* Copyright (C) 2015 Huawei Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*/
#include <stdlib.h>
......
......@@ -4,6 +4,19 @@
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
* Copyright (C) 2015 Huawei Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*/
#ifndef __BPF_BPF_H
#define __BPF_BPF_H
......
......@@ -4,6 +4,19 @@
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
* Copyright (C) 2015 Huawei Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*/
#include <stdlib.h>
......
......@@ -4,6 +4,19 @@
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
* Copyright (C) 2015 Huawei Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*/
#ifndef __BPF_LIBBPF_H
#define __BPF_LIBBPF_H
......
......@@ -19,7 +19,13 @@ MAKEFLAGS += --no-print-directory
LIBFILE = $(OUTPUT)libsubcmd.a
CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC
CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -O6 -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2 -fPIC
# Treat warnings as errors unless directed not to
ifneq ($(WERROR),0)
CFLAGS += -Werror
endif
CFLAGS += -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
CFLAGS += -I$(srctree)/tools/include/
......
......@@ -12,14 +12,14 @@ Set the NDK variable to point to the path where you installed the NDK:
2. Set cross-compiling environment variables for NDK toolchain and sysroot.
For arm:
export NDK_TOOLCHAIN=${NDK}/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86/bin/arm-linux-androideabi-
export NDK_SYSROOT=${NDK}/platforms/android-9/arch-arm
export NDK_TOOLCHAIN=${NDK}/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-
export NDK_SYSROOT=${NDK}/platforms/android-24/arch-arm
For x86:
export NDK_TOOLCHAIN=${NDK}/toolchains/x86-4.6/prebuilt/linux-x86/bin/i686-linux-android-
export NDK_SYSROOT=${NDK}/platforms/android-9/arch-x86
export NDK_TOOLCHAIN=${NDK}/toolchains/x86-4.9/prebuilt/linux-x86_64/bin/i686-linux-android-
export NDK_SYSROOT=${NDK}/platforms/android-24/arch-x86
This method is not working for Android NDK versions up to Revision 8b.
perf uses some bionic enhancements that are not included in these NDK versions.
This method is only tested for Android NDK versions Revision 11b and later.
perf uses some bionic enhancements that are not included in prior NDK versions.
You can use method (b) described below instead.
(b). Use the Android source tree
......@@ -49,9 +49,9 @@ II. Compile perf for Android
------------------------------------------------
You need to run make with the NDK toolchain and sysroot defined above:
For arm:
make ARCH=arm CROSS_COMPILE=${NDK_TOOLCHAIN} CFLAGS="--sysroot=${NDK_SYSROOT}"
make WERROR=0 ARCH=arm CROSS_COMPILE=${NDK_TOOLCHAIN} EXTRA_CFLAGS="-pie --sysroot=${NDK_SYSROOT}"
For x86:
make ARCH=x86 CROSS_COMPILE=${NDK_TOOLCHAIN} CFLAGS="--sysroot=${NDK_SYSROOT}"
make WERROR=0 ARCH=x86 CROSS_COMPILE=${NDK_TOOLCHAIN} EXTRA_CFLAGS="-pie --sysroot=${NDK_SYSROOT}"
III. Install perf
-----------------------------------------------
......
......@@ -15,6 +15,9 @@ DESCRIPTION
This command manages the build-id cache. It can add, remove, update and purge
files to/from the cache. In the future it should as well set upper limits for
the space used by the cache, etc.
This also scans the target binary for SDT (Statically Defined Tracing) and
record it along with the buildid-cache, which will be used by perf-probe.
For more details, see linkperf:perf-probe[1].
OPTIONS
-------
......
......@@ -67,7 +67,10 @@ OPTIONS
-l::
--list[=[GROUP:]EVENT]::
List up current probe events. This can also accept filtering patterns of event names.
List up current probe events. This can also accept filtering patterns of
event names.
When this is used with --cache, perf shows all cached probes instead of
the live probes.
-L::
--line=::
......@@ -110,8 +113,10 @@ OPTIONS
adding and removal operations.
--cache::
Cache the probes (with --add option). Any events which successfully added
(With --add) Cache the probes. Any events which successfully added
are also stored in the cache file.
(With --list) Show cached probes.
(With --del) Remove cached probes.
--max-probes=NUM::
Set the maximum number of probe points for an event. Default is 128.
......@@ -138,16 +143,18 @@ PROBE SYNTAX
Probe points are defined by following syntax.
1) Define event based on function name
[EVENT=]FUNC[@SRC][:RLN|+OFFS|%return|;PTN] [ARG ...]
[[GROUP:]EVENT=]FUNC[@SRC][:RLN|+OFFS|%return|;PTN] [ARG ...]
2) Define event based on source file with line number
[EVENT=]SRC:ALN [ARG ...]
[[GROUP:]EVENT=]SRC:ALN [ARG ...]
3) Define event based on source file with lazy pattern
[EVENT=]SRC;PTN [ARG ...]
[[GROUP:]EVENT=]SRC;PTN [ARG ...]
'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. Currently, event group name is set as 'probe'.
'EVENT' specifies the name of new event, if omitted, it will be set the name of the probed function. You can also specify a group name by 'GROUP', if omitted, set 'probe' is used for kprobe and 'probe_<bin>' is used for uprobe.
Note that using existing group name can conflict with other events. Especially, using the group name reserved for kernel modules can hide embedded events in the
modules.
'FUNC' specifies a probed function name, and it may have one of the following options; '+OFFS' is the offset from function entry address in bytes, ':RLN' is the relative-line number from function entry line, and '%return' means that it probes function return. And ';PTN' means lazy matching pattern (see LAZY MATCHING). Note that ';PTN' must be the end of the probe point definition. In addition, '@SRC' specifies a source file which has that function.
It is also possible to specify a probe point by the source line number or lazy matching by using 'SRC:ALN' or 'SRC;PTN' syntax, where 'SRC' is the source file path, ':ALN' is the line number and ';PTN' is the lazy matching pattern.
'ARG' specifies the arguments of this probe point, (see PROBE ARGUMENT).
......
......@@ -374,3 +374,5 @@
543 x32 io_setup compat_sys_io_setup
544 x32 io_submit compat_sys_io_submit
545 x32 execveat compat_sys_execveat/ptregs
534 x32 preadv2 compat_sys_preadv2
535 x32 pwritev2 compat_sys_pwritev2
......@@ -44,7 +44,7 @@
#define DEFAULT_VAR_FILTER "!__k???tab_* & !__crc_*"
#define DEFAULT_FUNC_FILTER "!_*"
#define DEFAULT_LIST_FILTER "*:*"
#define DEFAULT_LIST_FILTER "*"
/* Session management structure */
static struct {
......@@ -363,6 +363,32 @@ static int perf_add_probe_events(struct perf_probe_event *pevs, int npevs)
return ret;
}
static int del_perf_probe_caches(struct strfilter *filter)
{
struct probe_cache *cache;
struct strlist *bidlist;
struct str_node *nd;
int ret;
bidlist = build_id_cache__list_all();
if (!bidlist) {
ret = -errno;
pr_debug("Failed to get buildids: %d\n", ret);
return ret ?: -ENOMEM;
}
strlist__for_each_entry(nd, bidlist) {
cache = probe_cache__new(nd->s);
if (!cache)
continue;
if (probe_cache__filter_purge(cache, filter) < 0 ||
probe_cache__commit(cache) < 0)
pr_warning("Failed to remove entries for %s\n", nd->s);
probe_cache__delete(cache);
}
return 0;
}
static int perf_del_probe_events(struct strfilter *filter)
{
int ret, ret2, ufd = -1, kfd = -1;
......@@ -375,6 +401,9 @@ static int perf_del_probe_events(struct strfilter *filter)
pr_debug("Delete filter: \'%s\'\n", str);
if (probe_conf.cache)
return del_perf_probe_caches(filter);
/* Get current event names */
ret = probe_file__open_both(&kfd, &ufd, PF_FL_RW);
if (ret < 0)
......
......@@ -309,6 +309,10 @@ ifndef NO_LIBELF
CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT
endif
ifeq ($(feature-libelf-getshdrstrndx), 1)
CFLAGS += -DHAVE_ELF_GETSHDRSTRNDX_SUPPORT
endif
ifndef NO_DWARF
ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined)
msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled);
......
......@@ -216,6 +216,8 @@ static int do_test(struct hists *hists, struct result *expected, size_t nr_expec
/* check callchain entries */
root = &he->callchain->node.rb_root;
TEST_ASSERT_VAL("callchains expected", !RB_EMPTY_ROOT(root));
cnode = rb_entry(rb_first(root), struct callchain_node, rb_node);
c = 0;
......@@ -666,6 +668,8 @@ static int test4(struct perf_evsel *evsel, struct machine *machine)
perf_evsel__set_sample_bit(evsel, CALLCHAIN);
setup_sorting(NULL);
callchain_param = callchain_param_default;
callchain_register_param(&callchain_param);
err = add_hist_entries(hists, machine);
......
......@@ -17,6 +17,7 @@
#include "tool.h"
#include "header.h"
#include "vdso.h"
#include "probe-file.h"
static bool no_buildid_cache;
......@@ -165,8 +166,7 @@ char *build_id_cache__kallsyms_path(const char *sbuild_id, char *bf,
return NULL;
}
static char *build_id_cache__linkname(const char *sbuild_id, char *bf,
size_t size)
char *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size)
{
char *tmp = bf;
int ret = asnprintf(&bf, size, "%s/.build-id/%.2s/%s", buildid_dir,
......@@ -176,6 +176,36 @@ static char *build_id_cache__linkname(const char *sbuild_id, char *bf,
return bf;
}
char *build_id_cache__origname(const char *sbuild_id)
{
char *linkname;
char buf[PATH_MAX];
char *ret = NULL, *p;
size_t offs = 5; /* == strlen("../..") */
linkname = build_id_cache__linkname(sbuild_id, NULL, 0);
if (!linkname)
return NULL;
if (readlink(linkname, buf, PATH_MAX) < 0)
goto out;
/* The link should be "../..<origpath>/<sbuild_id>" */
p = strrchr(buf, '/'); /* Cut off the "/<sbuild_id>" */
if (p && (p > buf + offs)) {
*p = '\0';
if (buf[offs + 1] == '[')
offs++; /*
* This is a DSO name, like [kernel.kallsyms].
* Skip the first '/', since this is not the
* cache of a regular file.
*/
ret = strdup(buf + offs); /* Skip "../..[/]" */
}
out:
free(linkname);
return ret;
}
static const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso)
{
return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : "elf");
......@@ -387,6 +417,81 @@ void disable_buildid_cache(void)
no_buildid_cache = true;
}
static bool lsdir_bid_head_filter(const char *name __maybe_unused,
struct dirent *d __maybe_unused)
{
return (strlen(d->d_name) == 2) &&
isxdigit(d->d_name[0]) && isxdigit(d->d_name[1]);
}
static bool lsdir_bid_tail_filter(const char *name __maybe_unused,
struct dirent *d __maybe_unused)
{
int i = 0;
while (isxdigit(d->d_name[i]) && i < SBUILD_ID_SIZE - 3)
i++;
return (i == SBUILD_ID_SIZE - 3) && (d->d_name[i] == '\0');
}
struct strlist *build_id_cache__list_all(void)
{
struct strlist *toplist, *linklist = NULL, *bidlist;
struct str_node *nd, *nd2;
char *topdir, *linkdir = NULL;
char sbuild_id[SBUILD_ID_SIZE];
/* Open the top-level directory */
if (asprintf(&topdir, "%s/.build-id/", buildid_dir) < 0)
return NULL;
bidlist = strlist__new(NULL, NULL);
if (!bidlist)
goto out;
toplist = lsdir(topdir, lsdir_bid_head_filter);
if (!toplist) {
pr_debug("Error in lsdir(%s): %d\n", topdir, errno);
/* If there is no buildid cache, return an empty list */
if (errno == ENOENT)
goto out;
goto err_out;
}
strlist__for_each_entry(nd, toplist) {
if (asprintf(&linkdir, "%s/%s", topdir, nd->s) < 0)
goto err_out;
/* Open the lower-level directory */
linklist = lsdir(linkdir, lsdir_bid_tail_filter);
if (!linklist) {
pr_debug("Error in lsdir(%s): %d\n", linkdir, errno);
goto err_out;
}
strlist__for_each_entry(nd2, linklist) {
if (snprintf(sbuild_id, SBUILD_ID_SIZE, "%s%s",
nd->s, nd2->s) != SBUILD_ID_SIZE - 1)
goto err_out;
if (strlist__add(bidlist, sbuild_id) < 0)
goto err_out;
}
strlist__delete(linklist);
zfree(&linkdir);
}
out_free:
strlist__delete(toplist);
out:
free(topdir);
return bidlist;
err_out:
strlist__delete(linklist);
zfree(&linkdir);
strlist__delete(bidlist);
bidlist = NULL;
goto out_free;
}
char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
bool is_kallsyms, bool is_vdso)
{
......@@ -428,6 +533,30 @@ int build_id_cache__list_build_ids(const char *pathname,
return ret;
}
#ifdef HAVE_LIBELF_SUPPORT
static int build_id_cache__add_sdt_cache(const char *sbuild_id,
const char *realname)
{
struct probe_cache *cache;
int ret;
cache = probe_cache__new(sbuild_id);
if (!cache)
return -1;
ret = probe_cache__scan_sdt(cache, realname);
if (ret >= 0) {
pr_debug("Found %d SDTs in %s\n", ret, realname);
if (probe_cache__commit(cache) < 0)
ret = -1;
}
probe_cache__delete(cache);
return ret;
}
#else
#define build_id_cache__add_sdt_cache(sbuild_id, realname) (0)
#endif
int build_id_cache__add_s(const char *sbuild_id, const char *name,
bool is_kallsyms, bool is_vdso)
{
......@@ -485,6 +614,11 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
if (symlink(tmp, linkname) == 0)
err = 0;
/* Update SDT cache : error is just warned */
if (build_id_cache__add_sdt_cache(sbuild_id, realname) < 0)
pr_debug("Failed to update/scan SDT cache for %s\n", realname);
out_free:
if (!is_kallsyms)
free(realname);
......
......@@ -30,8 +30,11 @@ bool perf_session__read_build_ids(struct perf_session *session, bool with_hits);
int perf_session__write_buildid_table(struct perf_session *session, int fd);
int perf_session__cache_build_ids(struct perf_session *session);
char *build_id_cache__origname(const char *sbuild_id);
char *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size);
char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
bool is_kallsyms, bool is_vdso);
struct strlist *build_id_cache__list_all(void);
int build_id_cache__list_build_ids(const char *pathname,
struct strlist **result);
bool build_id_cache__cached(const char *sbuild_id);
......
......@@ -106,6 +106,7 @@ struct callchain_param {
};
extern struct callchain_param callchain_param;
extern struct callchain_param callchain_param_default;
struct callchain_list {
u64 ip;
......
......@@ -18,10 +18,13 @@ void perf_env__exit(struct perf_env *env)
zfree(&env->cmdline_argv);
zfree(&env->sibling_cores);
zfree(&env->sibling_threads);
zfree(&env->numa_nodes);
zfree(&env->pmu_mappings);
zfree(&env->cpu);
for (i = 0; i < env->nr_numa_nodes; i++)
cpu_map__put(env->numa_nodes[i].map);
zfree(&env->numa_nodes);
for (i = 0; i < env->caches_cnt; i++)
cpu_cache_level__free(&env->caches[i]);
zfree(&env->caches);
......
......@@ -2,6 +2,7 @@
#define __PERF_ENV_H
#include <linux/types.h>
#include "cpumap.h"
struct cpu_topology_map {
int socket_id;
......@@ -18,6 +19,13 @@ struct cpu_cache_level {
char *map;
};
struct numa_node {
u32 node;
u64 mem_total;
u64 mem_free;
struct cpu_map *map;
};
struct perf_env {
char *hostname;
char *os_release;
......@@ -40,11 +48,11 @@ struct perf_env {
const char **cmdline_argv;
char *sibling_cores;
char *sibling_threads;
char *numa_nodes;
char *pmu_mappings;
struct cpu_topology_map *cpu;
struct cpu_cache_level *caches;
int caches_cnt;
struct numa_node *numa_nodes;
};
extern struct perf_env perf_env;
......
......@@ -1306,42 +1306,19 @@ static void print_total_mem(struct perf_header *ph, int fd __maybe_unused,
static void print_numa_topology(struct perf_header *ph, int fd __maybe_unused,
FILE *fp)
{
u32 nr, c, i;
char *str, *tmp;
uint64_t mem_total, mem_free;
/* nr nodes */
nr = ph->env.nr_numa_nodes;
str = ph->env.numa_nodes;
for (i = 0; i < nr; i++) {
/* node number */
c = strtoul(str, &tmp, 0);
if (*tmp != ':')
goto error;
str = tmp + 1;
mem_total = strtoull(str, &tmp, 0);
if (*tmp != ':')
goto error;
int i;
struct numa_node *n;
str = tmp + 1;
mem_free = strtoull(str, &tmp, 0);
if (*tmp != ':')
goto error;
for (i = 0; i < ph->env.nr_numa_nodes; i++) {
n = &ph->env.numa_nodes[i];
fprintf(fp, "# node%u meminfo : total = %"PRIu64" kB,"
" free = %"PRIu64" kB\n",
c, mem_total, mem_free);
n->node, n->mem_total, n->mem_free);
str = tmp + 1;
fprintf(fp, "# node%u cpu list : %s\n", c, str);
str += strlen(str) + 1;
fprintf(fp, "# node%u cpu list : ", n->node);
cpu_map__fprintf(n->map, fp);
}
return;
error:
fprintf(fp, "# numa topology : not available\n");
}
static void print_cpuid(struct perf_header *ph, int fd __maybe_unused, FILE *fp)
......@@ -1906,11 +1883,10 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse
struct perf_header *ph, int fd,
void *data __maybe_unused)
{
struct numa_node *nodes, *n;
ssize_t ret;
u32 nr, node, i;
u32 nr, i;
char *str;
uint64_t mem_total, mem_free;
struct strbuf sb;
/* nr nodes */
ret = readn(fd, &nr, sizeof(nr));
......@@ -1921,47 +1897,47 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse
nr = bswap_32(nr);
ph->env.nr_numa_nodes = nr;
if (strbuf_init(&sb, 256) < 0)
return -1;
nodes = zalloc(sizeof(*nodes) * nr);
if (!nodes)
return -ENOMEM;
for (i = 0; i < nr; i++) {
n = &nodes[i];
/* node number */
ret = readn(fd, &node, sizeof(node));
if (ret != sizeof(node))
ret = readn(fd, &n->node, sizeof(u32));
if (ret != sizeof(n->node))
goto error;
ret = readn(fd, &mem_total, sizeof(u64));
ret = readn(fd, &n->mem_total, sizeof(u64));
if (ret != sizeof(u64))
goto error;
ret = readn(fd, &mem_free, sizeof(u64));
ret = readn(fd, &n->mem_free, sizeof(u64));
if (ret != sizeof(u64))
goto error;
if (ph->needs_swap) {
node = bswap_32(node);
mem_total = bswap_64(mem_total);
mem_free = bswap_64(mem_free);
n->node = bswap_32(n->node);
n->mem_total = bswap_64(n->mem_total);
n->mem_free = bswap_64(n->mem_free);
}
if (strbuf_addf(&sb, "%u:%"PRIu64":%"PRIu64":",
node, mem_total, mem_free) < 0)
goto error;
str = do_read_string(fd, ph);
if (!str)
goto error;
/* include a NULL character at the end */
if (strbuf_add(&sb, str, strlen(str) + 1) < 0)
n->map = cpu_map__new(str);
if (!n->map)
goto error;
free(str);
}
ph->env.numa_nodes = strbuf_detach(&sb, NULL);
ph->env.numa_nodes = nodes;
return 0;
error:
strbuf_release(&sb);
free(nodes);
return -1;
}
......
......@@ -15,6 +15,7 @@
#include "debug.h"
#include "machine.h"
#include <linux/string.h>
#include "unwind.h"
static void __maps__insert(struct maps *maps, struct map *map);
......@@ -744,9 +745,10 @@ int map_groups__fixup_overlappings(struct map_groups *mg, struct map *map,
/*
* XXX This should not really _copy_ te maps, but refcount them.
*/
int map_groups__clone(struct map_groups *mg,
int map_groups__clone(struct thread *thread,
struct map_groups *parent, enum map_type type)
{
struct map_groups *mg = thread->mg;
int err = -ENOMEM;
struct map *map;
struct maps *maps = &parent->maps[type];
......@@ -757,6 +759,11 @@ int map_groups__clone(struct map_groups *mg,
struct map *new = map__clone(map);
if (new == NULL)
goto out_unlock;
err = unwind__prepare_access(thread, new, NULL);
if (err)
goto out_unlock;
map_groups__insert(mg, new);
map__put(new);
}
......
......@@ -194,7 +194,7 @@ struct symbol *maps__find_symbol_by_name(struct maps *maps, const char *name,
struct map **mapp, symbol_filter_t filter);
void map_groups__init(struct map_groups *mg, struct machine *machine);
void map_groups__exit(struct map_groups *mg);
int map_groups__clone(struct map_groups *mg,
int map_groups__clone(struct thread *thread,
struct map_groups *parent, enum map_type type);
size_t map_groups__fprintf(struct map_groups *mg, FILE *fp);
......
......@@ -1206,10 +1206,8 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
bool file_spec = false;
/*
* <Syntax>
* perf probe [EVENT=]SRC[:LN|;PTN]
* perf probe [EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT]
*
* TODO:Group name support
* perf probe [GRP:][EVENT=]SRC[:LN|;PTN]
* perf probe [GRP:][EVENT=]FUNC[@SRC][+OFFS|%return|:LN|;PAT]
*/
if (!arg)
return -EINVAL;
......@@ -1218,11 +1216,19 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
if (ptr && *ptr == '=') { /* Event name */
*ptr = '\0';
tmp = ptr + 1;
if (strchr(arg, ':')) {
semantic_error("Group name is not supported yet.\n");
return -ENOTSUP;
}
ptr = strchr(arg, ':');
if (ptr) {
*ptr = '\0';
if (!is_c_func_name(arg))
goto not_fname;
pev->group = strdup(arg);
if (!pev->group)
return -ENOMEM;
arg = ptr + 1;
} else
pev->group = NULL;
if (!is_c_func_name(arg)) {
not_fname:
semantic_error("%s is bad for event name -it must "
"follow C symbol-naming rule.\n", arg);
return -EINVAL;
......@@ -1230,7 +1236,6 @@ static int parse_perf_probe_point(char *arg, struct perf_probe_event *pev)
pev->event = strdup(arg);
if (pev->event == NULL)
return -ENOMEM;
pev->group = NULL;
arg = tmp;
}
......@@ -2366,6 +2371,9 @@ int show_perf_probe_events(struct strfilter *filter)
setup_pager();
if (probe_conf.cache)
return probe_cache__show_all_caches(filter);
ret = init_probe_symbol_maps(false);
if (ret < 0)
return ret;
......@@ -2474,17 +2482,24 @@ static int probe_trace_event__set_name(struct probe_trace_event *tev,
char buf[64];
int ret;
/* If probe_event or trace_event already have the name, reuse it */
if (pev->event)
event = pev->event;
else
else if (tev->event)
event = tev->event;
else {
/* Or generate new one from probe point */
if (pev->point.function &&
(strncmp(pev->point.function, "0x", 2) != 0) &&
!strisglob(pev->point.function))
event = pev->point.function;
else
event = tev->point.realname;
}
if (pev->group)
group = pev->group;
else if (tev->group)
group = tev->group;
else
group = PERFPROBE_GROUP;
......@@ -2531,7 +2546,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
for (i = 0; i < ntevs; i++) {
tev = &tevs[i];
/* Skip if the symbol is out of .text or blacklisted */
if (!tev->point.symbol)
if (!tev->point.symbol && !pev->uprobes)
continue;
/* Set new name for tev (and update namelist) */
......@@ -2844,6 +2859,55 @@ static int try_to_find_absolute_address(struct perf_probe_event *pev,
bool __weak arch__prefers_symtab(void) { return false; }
static int find_probe_trace_events_from_cache(struct perf_probe_event *pev,
struct probe_trace_event **tevs)
{
struct probe_cache *cache;
struct probe_cache_entry *entry;
struct probe_trace_event *tev;
struct str_node *node;
int ret, i;
cache = probe_cache__new(pev->target);
if (!cache)
return 0;
entry = probe_cache__find(cache, pev);
if (!entry) {
ret = 0;
goto out;
}
ret = strlist__nr_entries(entry->tevlist);
if (ret > probe_conf.max_probes) {
pr_debug("Too many entries matched in the cache of %s\n",
pev->target ? : "kernel");
ret = -E2BIG;
goto out;
}
*tevs = zalloc(ret * sizeof(*tev));
if (!*tevs) {
ret = -ENOMEM;
goto out;
}
i = 0;
strlist__for_each_entry(node, entry->tevlist) {
tev = &(*tevs)[i++];
ret = parse_probe_trace_command(node->s, tev);
if (ret < 0)
goto out;
/* Set the uprobes attribute as same as original */
tev->uprobes = pev->uprobes;
}
ret = i;
out:
probe_cache__delete(cache);
return ret;
}
static int convert_to_probe_trace_events(struct perf_probe_event *pev,
struct probe_trace_event **tevs)
{
......@@ -2866,6 +2930,11 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev,
if (ret > 0)
return ret;
/* At first, we need to lookup cache entry */
ret = find_probe_trace_events_from_cache(pev, tevs);
if (ret > 0)
return ret; /* Found in probe cache */
if (arch__prefers_symtab() && !perf_probe_event_need_dwarf(pev)) {
ret = find_probe_trace_events_from_map(pev, tevs);
if (ret > 0)
......
......@@ -367,10 +367,17 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target)
{
char cpath[PATH_MAX];
char sbuildid[SBUILD_ID_SIZE];
char *dir_name;
char *dir_name = NULL;
bool is_kallsyms = !target;
int ret, fd;
if (target && build_id_cache__cached(target)) {
/* This is a cached buildid */
strncpy(sbuildid, target, SBUILD_ID_SIZE);
dir_name = build_id_cache__linkname(sbuildid, NULL, 0);
goto found;
}
if (target)
ret = filename__sprintf_build_id(target, sbuildid);
else {
......@@ -394,8 +401,11 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target)
dir_name = build_id_cache__cachedir(sbuildid, target, is_kallsyms,
false);
if (!dir_name)
found:
if (!dir_name) {
pr_debug("Failed to get cache from %s\n", target);
return -ENOMEM;
}
snprintf(cpath, PATH_MAX, "%s/probes", dir_name);
fd = open(cpath, O_CREAT | O_RDWR, 0644);
......@@ -424,12 +434,15 @@ static int probe_cache__load(struct probe_cache *pcache)
p = strchr(buf, '\n');
if (p)
*p = '\0';
if (buf[0] == '#') { /* #perf_probe_event */
/* #perf_probe_event or %sdt_event */
if (buf[0] == '#' || buf[0] == '%') {
entry = probe_cache_entry__new(NULL);
if (!entry) {
ret = -ENOMEM;
goto out;
}
if (buf[0] == '%')
entry->sdt = true;
entry->spev = strdup(buf + 1);
if (entry->spev)
ret = parse_perf_probe_command(buf + 1,
......@@ -524,7 +537,7 @@ static bool streql(const char *a, const char *b)
return !strcmp(a, b);
}
static struct probe_cache_entry *
struct probe_cache_entry *
probe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev)
{
struct probe_cache_entry *entry = NULL;
......@@ -548,6 +561,24 @@ probe_cache__find(struct probe_cache *pcache, struct perf_probe_event *pev)
return entry;
}
struct probe_cache_entry *
probe_cache__find_by_name(struct probe_cache *pcache,
const char *group, const char *event)
{
struct probe_cache_entry *entry = NULL;
list_for_each_entry(entry, &pcache->entries, node) {
/* Hit if same event name or same command-string */
if (streql(entry->pev.group, group) &&
streql(entry->pev.event, event))
goto found;
}
entry = NULL;
found:
return entry;
}
int probe_cache__add_entry(struct probe_cache *pcache,
struct perf_probe_event *pev,
struct probe_trace_event *tevs, int ntevs)
......@@ -593,19 +624,79 @@ int probe_cache__add_entry(struct probe_cache *pcache,
return ret;
}
static unsigned long long sdt_note__get_addr(struct sdt_note *note)
{
return note->bit32 ? (unsigned long long)note->addr.a32[0]
: (unsigned long long)note->addr.a64[0];
}
int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname)
{
struct probe_cache_entry *entry = NULL;
struct list_head sdtlist;
struct sdt_note *note;
char *buf;
char sdtgrp[64];
int ret;
INIT_LIST_HEAD(&sdtlist);
ret = get_sdt_note_list(&sdtlist, pathname);
if (ret < 0) {
pr_debug("Failed to get sdt note: %d\n", ret);
return ret;
}
list_for_each_entry(note, &sdtlist, note_list) {
ret = snprintf(sdtgrp, 64, "sdt_%s", note->provider);
if (ret < 0)
break;
/* Try to find same-name entry */
entry = probe_cache__find_by_name(pcache, sdtgrp, note->name);
if (!entry) {
entry = probe_cache_entry__new(NULL);
if (!entry) {
ret = -ENOMEM;
break;
}
entry->sdt = true;
ret = asprintf(&entry->spev, "%s:%s=%s", sdtgrp,
note->name, note->name);
if (ret < 0)
break;
entry->pev.event = strdup(note->name);
entry->pev.group = strdup(sdtgrp);
list_add_tail(&entry->node, &pcache->entries);
}
ret = asprintf(&buf, "p:%s/%s %s:0x%llx",
sdtgrp, note->name, pathname,
sdt_note__get_addr(note));
if (ret < 0)
break;
strlist__add(entry->tevlist, buf);
free(buf);
entry = NULL;
}
if (entry) {
list_del_init(&entry->node);
probe_cache_entry__delete(entry);
}
cleanup_sdt_note_list(&sdtlist);
return ret;
}
static int probe_cache_entry__write(struct probe_cache_entry *entry, int fd)
{
struct str_node *snode;
struct stat st;
struct iovec iov[3];
const char *prefix = entry->sdt ? "%" : "#";
int ret;
/* Save stat for rollback */
ret = fstat(fd, &st);
if (ret < 0)
return ret;
pr_debug("Writing cache: #%s\n", entry->spev);
iov[0].iov_base = (void *)"#"; iov[0].iov_len = 1;
pr_debug("Writing cache: %s%s\n", prefix, entry->spev);
iov[0].iov_base = (void *)prefix; iov[0].iov_len = 1;
iov[1].iov_base = entry->spev; iov[1].iov_len = strlen(entry->spev);
iov[2].iov_base = (void *)"\n"; iov[2].iov_len = 1;
ret = writev(fd, iov, 3);
......@@ -655,3 +746,75 @@ int probe_cache__commit(struct probe_cache *pcache)
out:
return ret;
}
static bool probe_cache_entry__compare(struct probe_cache_entry *entry,
struct strfilter *filter)
{
char buf[128], *ptr = entry->spev;
if (entry->pev.event) {
snprintf(buf, 128, "%s:%s", entry->pev.group, entry->pev.event);
ptr = buf;
}
return strfilter__compare(filter, ptr);
}
int probe_cache__filter_purge(struct probe_cache *pcache,
struct strfilter *filter)
{
struct probe_cache_entry *entry, *tmp;
list_for_each_entry_safe(entry, tmp, &pcache->entries, node) {
if (probe_cache_entry__compare(entry, filter)) {
pr_info("Removed cached event: %s\n", entry->spev);
list_del_init(&entry->node);
probe_cache_entry__delete(entry);
}
}
return 0;
}
static int probe_cache__show_entries(struct probe_cache *pcache,
struct strfilter *filter)
{
struct probe_cache_entry *entry;
list_for_each_entry(entry, &pcache->entries, node) {
if (probe_cache_entry__compare(entry, filter))
printf("%s\n", entry->spev);
}
return 0;
}
/* Show all cached probes */
int probe_cache__show_all_caches(struct strfilter *filter)
{
struct probe_cache *pcache;
struct strlist *bidlist;
struct str_node *nd;
char *buf = strfilter__string(filter);
pr_debug("list cache with filter: %s\n", buf);
free(buf);
bidlist = build_id_cache__list_all();
if (!bidlist) {
pr_debug("Failed to get buildids: %d\n", errno);
return -EINVAL;
}
strlist__for_each_entry(nd, bidlist) {
pcache = probe_cache__new(nd->s);
if (!pcache)
continue;
if (!list_empty(&pcache->entries)) {
buf = build_id_cache__origname(nd->s);
printf("%s (%s):\n", buf, nd->s);
free(buf);
probe_cache__show_entries(pcache, filter);
}
probe_cache__delete(pcache);
}
strlist__delete(bidlist);
return 0;
}
......@@ -8,6 +8,7 @@
/* Cache of probe definitions */
struct probe_cache_entry {
struct list_head node;
bool sdt;
struct perf_probe_event pev;
char *spev;
struct strlist *tevlist;
......@@ -35,8 +36,15 @@ struct probe_cache *probe_cache__new(const char *target);
int probe_cache__add_entry(struct probe_cache *pcache,
struct perf_probe_event *pev,
struct probe_trace_event *tevs, int ntevs);
int probe_cache__scan_sdt(struct probe_cache *pcache, const char *pathname);
int probe_cache__commit(struct probe_cache *pcache);
void probe_cache__purge(struct probe_cache *pcache);
void probe_cache__delete(struct probe_cache *pcache);
int probe_cache__filter_purge(struct probe_cache *pcache,
struct strfilter *filter);
struct probe_cache_entry *probe_cache__find(struct probe_cache *pcache,
struct perf_probe_event *pev);
struct probe_cache_entry *probe_cache__find_by_name(struct probe_cache *pcache,
const char *group, const char *event);
int probe_cache__show_all_caches(struct strfilter *filter);
#endif
......@@ -54,6 +54,14 @@ static int elf_getphdrnum(Elf *elf, size_t *dst)
}
#endif
#ifndef HAVE_ELF_GETSHDRSTRNDX_SUPPORT
static int elf_getshdrstrndx(Elf *elf __maybe_unused, size_t *dst __maybe_unused)
{
pr_err("%s: update your libelf to > 0.140, this one lacks elf_getshdrstrndx().\n", __func__);
return -1;
}
#endif
#ifndef NT_GNU_BUILD_ID
#define NT_GNU_BUILD_ID 3
#endif
......@@ -1781,6 +1789,258 @@ void kcore_extract__delete(struct kcore_extract *kce)
unlink(kce->extract_filename);
}
/**
* populate_sdt_note : Parse raw data and identify SDT note
* @elf: elf of the opened file
* @data: raw data of a section with description offset applied
* @len: note description size
* @type: type of the note
* @sdt_notes: List to add the SDT note
*
* Responsible for parsing the @data in section .note.stapsdt in @elf and
* if its an SDT note, it appends to @sdt_notes list.
*/
static int populate_sdt_note(Elf **elf, const char *data, size_t len,
struct list_head *sdt_notes)
{
const char *provider, *name;
struct sdt_note *tmp = NULL;
GElf_Ehdr ehdr;
GElf_Addr base_off = 0;
GElf_Shdr shdr;
int ret = -EINVAL;
union {
Elf64_Addr a64[NR_ADDR];
Elf32_Addr a32[NR_ADDR];
} buf;
Elf_Data dst = {
.d_buf = &buf, .d_type = ELF_T_ADDR, .d_version = EV_CURRENT,
.d_size = gelf_fsize((*elf), ELF_T_ADDR, NR_ADDR, EV_CURRENT),
.d_off = 0, .d_align = 0
};
Elf_Data src = {
.d_buf = (void *) data, .d_type = ELF_T_ADDR,
.d_version = EV_CURRENT, .d_size = dst.d_size, .d_off = 0,
.d_align = 0
};
tmp = (struct sdt_note *)calloc(1, sizeof(struct sdt_note));
if (!tmp) {
ret = -ENOMEM;
goto out_err;
}
INIT_LIST_HEAD(&tmp->note_list);
if (len < dst.d_size + 3)
goto out_free_note;
/* Translation from file representation to memory representation */
if (gelf_xlatetom(*elf, &dst, &src,
elf_getident(*elf, NULL)[EI_DATA]) == NULL) {
pr_err("gelf_xlatetom : %s\n", elf_errmsg(-1));
goto out_free_note;
}
/* Populate the fields of sdt_note */
provider = data + dst.d_size;
name = (const char *)memchr(provider, '\0', data + len - provider);
if (name++ == NULL)
goto out_free_note;
tmp->provider = strdup(provider);
if (!tmp->provider) {
ret = -ENOMEM;
goto out_free_note;
}
tmp->name = strdup(name);
if (!tmp->name) {
ret = -ENOMEM;
goto out_free_prov;
}
if (gelf_getclass(*elf) == ELFCLASS32) {
memcpy(&tmp->addr, &buf, 3 * sizeof(Elf32_Addr));
tmp->bit32 = true;
} else {
memcpy(&tmp->addr, &buf, 3 * sizeof(Elf64_Addr));
tmp->bit32 = false;
}
if (!gelf_getehdr(*elf, &ehdr)) {
pr_debug("%s : cannot get elf header.\n", __func__);
ret = -EBADF;
goto out_free_name;
}
/* Adjust the prelink effect :
* Find out the .stapsdt.base section.
* This scn will help us to handle prelinking (if present).
* Compare the retrieved file offset of the base section with the
* base address in the description of the SDT note. If its different,
* then accordingly, adjust the note location.
*/
if (elf_section_by_name(*elf, &ehdr, &shdr, SDT_BASE_SCN, NULL)) {
base_off = shdr.sh_offset;
if (base_off) {
if (tmp->bit32)
tmp->addr.a32[0] = tmp->addr.a32[0] + base_off -
tmp->addr.a32[1];
else
tmp->addr.a64[0] = tmp->addr.a64[0] + base_off -
tmp->addr.a64[1];
}
}
list_add_tail(&tmp->note_list, sdt_notes);
return 0;
out_free_name:
free(tmp->name);
out_free_prov:
free(tmp->provider);
out_free_note:
free(tmp);
out_err:
return ret;
}
/**
* construct_sdt_notes_list : constructs a list of SDT notes
* @elf : elf to look into
* @sdt_notes : empty list_head
*
* Scans the sections in 'elf' for the section
* .note.stapsdt. It, then calls populate_sdt_note to find
* out the SDT events and populates the 'sdt_notes'.
*/
static int construct_sdt_notes_list(Elf *elf, struct list_head *sdt_notes)
{
GElf_Ehdr ehdr;
Elf_Scn *scn = NULL;
Elf_Data *data;
GElf_Shdr shdr;
size_t shstrndx, next;
GElf_Nhdr nhdr;
size_t name_off, desc_off, offset;
int ret = 0;
if (gelf_getehdr(elf, &ehdr) == NULL) {
ret = -EBADF;
goto out_ret;
}
if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
ret = -EBADF;
goto out_ret;
}
/* Look for the required section */
scn = elf_section_by_name(elf, &ehdr, &shdr, SDT_NOTE_SCN, NULL);
if (!scn) {
ret = -ENOENT;
goto out_ret;
}
if ((shdr.sh_type != SHT_NOTE) || (shdr.sh_flags & SHF_ALLOC)) {
ret = -ENOENT;
goto out_ret;
}
data = elf_getdata(scn, NULL);
/* Get the SDT notes */
for (offset = 0; (next = gelf_getnote(data, offset, &nhdr, &name_off,
&desc_off)) > 0; offset = next) {
if (nhdr.n_namesz == sizeof(SDT_NOTE_NAME) &&
!memcmp(data->d_buf + name_off, SDT_NOTE_NAME,
sizeof(SDT_NOTE_NAME))) {
/* Check the type of the note */
if (nhdr.n_type != SDT_NOTE_TYPE)
goto out_ret;
ret = populate_sdt_note(&elf, ((data->d_buf) + desc_off),
nhdr.n_descsz, sdt_notes);
if (ret < 0)
goto out_ret;
}
}
if (list_empty(sdt_notes))
ret = -ENOENT;
out_ret:
return ret;
}
/**
* get_sdt_note_list : Wrapper to construct a list of sdt notes
* @head : empty list_head
* @target : file to find SDT notes from
*
* This opens the file, initializes
* the ELF and then calls construct_sdt_notes_list.
*/
int get_sdt_note_list(struct list_head *head, const char *target)
{
Elf *elf;
int fd, ret;
fd = open(target, O_RDONLY);
if (fd < 0)
return -EBADF;
elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
if (!elf) {
ret = -EBADF;
goto out_close;
}
ret = construct_sdt_notes_list(elf, head);
elf_end(elf);
out_close:
close(fd);
return ret;
}
/**
* cleanup_sdt_note_list : free the sdt notes' list
* @sdt_notes: sdt notes' list
*
* Free up the SDT notes in @sdt_notes.
* Returns the number of SDT notes free'd.
*/
int cleanup_sdt_note_list(struct list_head *sdt_notes)
{
struct sdt_note *tmp, *pos;
int nr_free = 0;
list_for_each_entry_safe(pos, tmp, sdt_notes, note_list) {
list_del(&pos->note_list);
free(pos->name);
free(pos->provider);
free(pos);
nr_free++;
}
return nr_free;
}
/**
* sdt_notes__get_count: Counts the number of sdt events
* @start: list_head to sdt_notes list
*
* Returns the number of SDT notes in a list
*/
int sdt_notes__get_count(struct list_head *start)
{
struct sdt_note *sdt_ptr;
int count = 0;
list_for_each_entry(sdt_ptr, start, note_list)
count++;
return count;
}
void symbol__elf_init(void)
{
elf_version(EV_CURRENT);
......
......@@ -342,4 +342,26 @@ void arch__sym_update(struct symbol *s, GElf_Sym *sym);
int arch__choose_best_symbol(struct symbol *syma, struct symbol *symb);
/* structure containing an SDT note's info */
struct sdt_note {
char *name; /* name of the note*/
char *provider; /* provider name */
bool bit32; /* whether the location is 32 bits? */
union { /* location, base and semaphore addrs */
Elf64_Addr a64[3];
Elf32_Addr a32[3];
} addr;
struct list_head note_list; /* SDT notes' list */
};
int get_sdt_note_list(struct list_head *head, const char *target);
int cleanup_sdt_note_list(struct list_head *sdt_notes);
int sdt_notes__get_count(struct list_head *start);
#define SDT_BASE_SCN ".stapsdt.base"
#define SDT_NOTE_SCN ".note.stapsdt"
#define SDT_NOTE_TYPE 3
#define SDT_NOTE_NAME "stapsdt"
#define NR_ADDR 3
#endif /* __PERF_SYMBOL */
......@@ -202,7 +202,7 @@ int thread__insert_map(struct thread *thread, struct map *map)
{
int ret;
ret = unwind__prepare_access(thread, map);
ret = unwind__prepare_access(thread, map, NULL);
if (ret)
return ret;
......@@ -212,6 +212,39 @@ int thread__insert_map(struct thread *thread, struct map *map)
return 0;
}
static int __thread__prepare_access(struct thread *thread)
{
bool initialized = false;
int i, err = 0;
for (i = 0; i < MAP__NR_TYPES; ++i) {
struct maps *maps = &thread->mg->maps[i];
struct map *map;
pthread_rwlock_rdlock(&maps->lock);
for (map = maps__first(maps); map; map = map__next(map)) {
err = unwind__prepare_access(thread, map, &initialized);
if (err || initialized)
break;
}
pthread_rwlock_unlock(&maps->lock);
}
return err;
}
static int thread__prepare_access(struct thread *thread)
{
int err = 0;
if (symbol_conf.use_callchain)
err = __thread__prepare_access(thread);
return err;
}
static int thread__clone_map_groups(struct thread *thread,
struct thread *parent)
{
......@@ -219,7 +252,7 @@ static int thread__clone_map_groups(struct thread *thread,
/* This is new thread, we share map groups for process. */
if (thread->pid_ == parent->pid_)
return 0;
return thread__prepare_access(thread);
if (thread->mg == parent->mg) {
pr_debug("broken map groups on thread %d/%d parent %d/%d\n",
......@@ -229,7 +262,7 @@ static int thread__clone_map_groups(struct thread *thread,
/* But this one is new process, copy maps. */
for (i = 0; i < MAP__NR_TYPES; ++i)
if (map_groups__clone(thread->mg, parent->mg, i) < 0)
if (map_groups__clone(thread, parent->mg, i) < 0)
return -ENOMEM;
return 0;
......
......@@ -14,15 +14,19 @@ static void unwind__register_ops(struct thread *thread,
thread->unwind_libunwind_ops = ops;
}
int unwind__prepare_access(struct thread *thread, struct map *map)
int unwind__prepare_access(struct thread *thread, struct map *map,
bool *initialized)
{
const char *arch;
enum dso_type dso_type;
struct unwind_libunwind_ops *ops = local_unwind_libunwind_ops;
int err;
if (thread->addr_space) {
pr_debug("unwind: thread map already set, dso=%s\n",
map->dso->name);
if (initialized)
*initialized = true;
return 0;
}
......@@ -51,7 +55,10 @@ int unwind__prepare_access(struct thread *thread, struct map *map)
out_register:
unwind__register_ops(thread, ops);
return thread->unwind_libunwind_ops->prepare_access(thread);
err = thread->unwind_libunwind_ops->prepare_access(thread);
if (initialized)
*initialized = err ? false : true;
return err;
}
void unwind__flush_access(struct thread *thread)
......
......@@ -42,12 +42,14 @@ int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
#endif
int LIBUNWIND__ARCH_REG_ID(int regnum);
int unwind__prepare_access(struct thread *thread, struct map *map);
int unwind__prepare_access(struct thread *thread, struct map *map,
bool *initialized);
void unwind__flush_access(struct thread *thread);
void unwind__finish_access(struct thread *thread);
#else
static inline int unwind__prepare_access(struct thread *thread __maybe_unused,
struct map *map __maybe_unused)
struct map *map __maybe_unused,
bool *initialized __maybe_unused)
{
return 0;
}
......@@ -67,7 +69,8 @@ unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
}
static inline int unwind__prepare_access(struct thread *thread __maybe_unused,
struct map *map __maybe_unused)
struct map *map __maybe_unused,
bool *initialized __maybe_unused)
{
return 0;
}
......
......@@ -19,12 +19,19 @@
#include "callchain.h"
#include "strlist.h"
struct callchain_param callchain_param = {
.mode = CHAIN_GRAPH_ABS,
.min_percent = 0.5,
.order = ORDER_CALLEE,
.key = CCKEY_FUNCTION,
.value = CCVAL_PERCENT,
#define CALLCHAIN_PARAM_DEFAULT \
.mode = CHAIN_GRAPH_ABS, \
.min_percent = 0.5, \
.order = ORDER_CALLEE, \
.key = CCKEY_FUNCTION, \
.value = CCVAL_PERCENT, \
struct callchain_param callchain_param = {
CALLCHAIN_PARAM_DEFAULT
};
struct callchain_param callchain_param_default = {
CALLCHAIN_PARAM_DEFAULT
};
/*
......
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