perf/core improvements and fixes:
Documentation: - Update android build documentation (Chris Phlipot) Infrastructure: - 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: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJXevj8AAoJENZQFvNTUqpA9wAP/R63AvMb3mRmvPqNG1wBQQlg yq2rstcSZ74Lrg2uo94+YPrcq/MH2HsnFE05km4AZR15R/5xmhlGrGEHfp7NDA+X hj9G3F/gQIv4sKh5CHpSQAQ1hGhLUG1sx/RtHRpNqY1DzhrA8mZLkUnqCS4acsJF mCyYa27MUJF+XpCH6knCCc2DyGECr5FawDc9mEzHDHCPug67mfsvNiBQ58soQiuz cOvrLePcMjIMZU+ZhD4fOdSohDWFUR0W3rO1xjlhQv85HB86Gv1lclN5OY6J/R09 frh7YnfM9P5+j9HFyxfzIUst8g+kRFuetciQLp5nHgRA6a6urDNec7S60Uv6uheC GavYANe3Ky2fr1kci38JMSCWApbpEK3R8QDSfPTGfj0xvPw6q9cG0dTaPRW9QmJj w89dPDjq+VhpGyRqYt7THQm2rOgClSkjjCcQ8RgF0NRhumqnQIRV8TKcoWzO+3rb rHbvvMoJw4KdLFZBvfj0MTF1ClLwbSgD2T2Rak+b6MWCOgjxrs66XKxCoRha15/a Dao5QvEiek5+wzYvj9G5aNZQ1k0/GY+WwLi2vWFOwdfgphsMFvAAo12dpSZDsb90 STO8GFoM+Cg6kOY4OTwUXlHhy+fkfwg1BmPg1zMQN4Ol4l5KW3fSGj8kTqFg3IiC nIu+1toqyz2Q3V4REXOz =UWaR -----END PGP SIGNATURE----- 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: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
c50f62454f
|
@ -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();
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#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 @@ out_cleanup:
|
|||
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 @@ retry:
|
|||
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;
|
||||
int i;
|
||||
struct numa_node *n;
|
||||
|
||||
/* 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;
|
||||
|
||||
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 @@ errout:
|
|||
|
||||
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 @@ found:
|
|||
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 @@ out_err:
|
|||
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
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue