Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf updates from Ingo Molnar: "Bigger kernel side changes: - Add backwards writing capability to the perf ring-buffer code, which is preparation for future advanced features like robust 'overwrite support' and snapshot mode. (Wang Nan) - Add pause and resume ioctls for the perf ringbuffer (Wang Nan) - x86 Intel cstate code cleanups and reorgnization (Thomas Gleixner) - x86 Intel uncore and CPU PMU driver updates (Kan Liang, Peter Zijlstra) - x86 AUX (Intel PT) related enhancements and updates (Alexander Shishkin) - x86 MSR PMU driver enhancements and updates (Huang Rui) - ... and lots of other changes spread out over 40+ commits. Biggest tooling side changes: - 'perf trace' features and enhancements. (Arnaldo Carvalho de Melo) - BPF tooling updates (Wang Nan) - 'perf sched' updates (Jiri Olsa) - 'perf probe' updates (Masami Hiramatsu) - ... plus 200+ other enhancements, fixes and cleanups to tools/ The merge commits, the shortlog and the changelogs contain a lot more details" * 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (249 commits) perf/core: Disable the event on a truncated AUX record perf/x86/intel/pt: Generate PMI in the STOP region as well perf buildid-cache: Use lsdir() for looking up buildid caches perf symbols: Use lsdir() for the search in kcore cache directory perf tools: Use SBUILD_ID_SIZE where applicable perf tools: Fix lsdir to set errno correctly perf trace: Move seccomp args beautifiers to tools/perf/trace/beauty/ perf trace: Move flock op beautifier to tools/perf/trace/beauty/ perf build: Add build-test for debug-frame on arm/arm64 perf build: Add build-test for libunwind cross-platforms support perf script: Fix export of callchains with recursion in db-export perf script: Fix callchain addresses in db-export perf script: Fix symbol insertion behavior in db-export perf symbols: Add dso__insert_symbol function perf scripting python: Use Py_FatalError instead of die() perf tools: Remove xrealloc and ALLOC_GROW perf help: Do not use ALLOC_GROW in add_cmd_list perf pmu: Make pmu_formats_string to check return value of strbuf perf header: Make topology checkers to check return value of strbuf perf tools: Make alias handler to check return value of strbuf ...
This commit is contained in:
@@ -672,6 +672,7 @@ The letters are:
|
||||
d create a debug log
|
||||
g synthesize a call chain (use with i or x)
|
||||
l synthesize last branch entries (use with i or x)
|
||||
s skip initial number of events
|
||||
|
||||
"Instructions" events look like they were recorded by "perf record -e
|
||||
instructions".
|
||||
@@ -730,6 +731,12 @@ from one sample to the next.
|
||||
|
||||
To disable trace decoding entirely, use the option --no-itrace.
|
||||
|
||||
It is also possible to skip events generated (instructions, branches, transactions)
|
||||
at the beginning. This is useful to ignore initialization code.
|
||||
|
||||
--itrace=i0nss1000000
|
||||
|
||||
skips the first million instructions.
|
||||
|
||||
dump option
|
||||
-----------
|
||||
|
@@ -7,6 +7,7 @@
|
||||
d create a debug log
|
||||
g synthesize a call chain (use with i or x)
|
||||
l synthesize last branch entries (use with i or x)
|
||||
s skip initial number of events
|
||||
|
||||
The default is all events i.e. the same as --itrace=ibxe
|
||||
|
||||
@@ -24,3 +25,10 @@
|
||||
|
||||
Also the number of last branch entries (default 64, max. 1024) for
|
||||
instructions or transactions events can be specified.
|
||||
|
||||
It is also possible to skip events generated (instructions, branches, transactions)
|
||||
at the beginning. This is useful to ignore initialization code.
|
||||
|
||||
--itrace=i0nss1000000
|
||||
|
||||
skips the first million instructions.
|
||||
|
@@ -33,7 +33,7 @@ OPTIONS
|
||||
|
||||
-f::
|
||||
--force::
|
||||
Don't complain, do it.
|
||||
Don't do ownership validation.
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
|
@@ -75,7 +75,7 @@ OPTIONS
|
||||
|
||||
-f::
|
||||
--force::
|
||||
Don't complain, do it.
|
||||
Don't do ownership validation.
|
||||
|
||||
--symfs=<directory>::
|
||||
Look for files with symbols relative to this directory.
|
||||
|
@@ -93,6 +93,67 @@ raw encoding of 0x1A8 can be used:
|
||||
You should refer to the processor specific documentation for getting these
|
||||
details. Some of them are referenced in the SEE ALSO section below.
|
||||
|
||||
ARBITRARY PMUS
|
||||
--------------
|
||||
|
||||
perf also supports an extended syntax for specifying raw parameters
|
||||
to PMUs. Using this typically requires looking up the specific event
|
||||
in the CPU vendor specific documentation.
|
||||
|
||||
The available PMUs and their raw parameters can be listed with
|
||||
|
||||
ls /sys/devices/*/format
|
||||
|
||||
For example the raw event "LSD.UOPS" core pmu event above could
|
||||
be specified as
|
||||
|
||||
perf stat -e cpu/event=0xa8,umask=0x1,name=LSD.UOPS_CYCLES,cmask=1/ ...
|
||||
|
||||
PER SOCKET PMUS
|
||||
---------------
|
||||
|
||||
Some PMUs are not associated with a core, but with a whole CPU socket.
|
||||
Events on these PMUs generally cannot be sampled, but only counted globally
|
||||
with perf stat -a. They can be bound to one logical CPU, but will measure
|
||||
all the CPUs in the same socket.
|
||||
|
||||
This example measures memory bandwidth every second
|
||||
on the first memory controller on socket 0 of a Intel Xeon system
|
||||
|
||||
perf stat -C 0 -a uncore_imc_0/cas_count_read/,uncore_imc_0/cas_count_write/ -I 1000 ...
|
||||
|
||||
Each memory controller has its own PMU. Measuring the complete system
|
||||
bandwidth would require specifying all imc PMUs (see perf list output),
|
||||
and adding the values together.
|
||||
|
||||
This example measures the combined core power every second
|
||||
|
||||
perf stat -I 1000 -e power/energy-cores/ -a
|
||||
|
||||
ACCESS RESTRICTIONS
|
||||
-------------------
|
||||
|
||||
For non root users generally only context switched PMU events are available.
|
||||
This is normally only the events in the cpu PMU, the predefined events
|
||||
like cycles and instructions and some software events.
|
||||
|
||||
Other PMUs and global measurements are normally root only.
|
||||
Some event qualifiers, such as "any", are also root only.
|
||||
|
||||
This can be overriden by setting the kernel.perf_event_paranoid
|
||||
sysctl to -1, which allows non root to use these events.
|
||||
|
||||
For accessing trace point events perf needs to have read access to
|
||||
/sys/kernel/debug/tracing, even when perf_event_paranoid is in a relaxed
|
||||
setting.
|
||||
|
||||
TRACING
|
||||
-------
|
||||
|
||||
Some PMUs control advanced hardware tracing capabilities, such as Intel PT,
|
||||
that allows low overhead execution tracing. These are described in a separate
|
||||
intel-pt.txt document.
|
||||
|
||||
PARAMETERIZED EVENTS
|
||||
--------------------
|
||||
|
||||
@@ -106,6 +167,50 @@ also be supplied. For example:
|
||||
|
||||
perf stat -C 0 -e 'hv_gpci/dtbp_ptitc,phys_processor_idx=0x2/' ...
|
||||
|
||||
EVENT GROUPS
|
||||
------------
|
||||
|
||||
Perf supports time based multiplexing of events, when the number of events
|
||||
active exceeds the number of hardware performance counters. Multiplexing
|
||||
can cause measurement errors when the workload changes its execution
|
||||
profile.
|
||||
|
||||
When metrics are computed using formulas from event counts, it is useful to
|
||||
ensure some events are always measured together as a group to minimize multiplexing
|
||||
errors. Event groups can be specified using { }.
|
||||
|
||||
perf stat -e '{instructions,cycles}' ...
|
||||
|
||||
The number of available performance counters depend on the CPU. A group
|
||||
cannot contain more events than available counters.
|
||||
For example Intel Core CPUs typically have four generic performance counters
|
||||
for the core, plus three fixed counters for instructions, cycles and
|
||||
ref-cycles. Some special events have restrictions on which counter they
|
||||
can schedule, and may not support multiple instances in a single group.
|
||||
When too many events are specified in the group none of them will not
|
||||
be measured.
|
||||
|
||||
Globally pinned events can limit the number of counters available for
|
||||
other groups. On x86 systems, the NMI watchdog pins a counter by default.
|
||||
The nmi watchdog can be disabled as root with
|
||||
|
||||
echo 0 > /proc/sys/kernel/nmi_watchdog
|
||||
|
||||
Events from multiple different PMUs cannot be mixed in a group, with
|
||||
some exceptions for software events.
|
||||
|
||||
LEADER SAMPLING
|
||||
---------------
|
||||
|
||||
perf also supports group leader sampling using the :S specifier.
|
||||
|
||||
perf record -e '{cycles,instructions}:S' ...
|
||||
perf report --group
|
||||
|
||||
Normally all events in a event group sample, but with :S only
|
||||
the first event (the leader) samples, and it only reads the values of the
|
||||
other events in the group.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
|
||||
@@ -143,5 +248,5 @@ SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-top[1],
|
||||
linkperf:perf-record[1],
|
||||
http://www.intel.com/Assets/PDF/manual/253669.pdf[Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide],
|
||||
http://www.intel.com/sdm/[Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide],
|
||||
http://support.amd.com/us/Processor_TechDocs/24593_APM_v2.pdf[AMD64 Architecture Programmer’s Manual Volume 2: System Programming]
|
||||
|
@@ -48,6 +48,14 @@ OPTIONS
|
||||
option can be passed in record mode. It will be interpreted the same way as perf
|
||||
record.
|
||||
|
||||
-K::
|
||||
--all-kernel::
|
||||
Configure all used events to run in kernel space.
|
||||
|
||||
-U::
|
||||
--all-user::
|
||||
Configure all used events to run in user space.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-report[1]
|
||||
|
@@ -347,6 +347,19 @@ Configure all used events to run in kernel space.
|
||||
--all-user::
|
||||
Configure all used events to run in user space.
|
||||
|
||||
--timestamp-filename
|
||||
Append timestamp to output file name.
|
||||
|
||||
--switch-output::
|
||||
Generate multiple perf.data files, timestamp prefixed, switching to a new one
|
||||
when receiving a SIGUSR2.
|
||||
|
||||
A possible use case is to, given an external event, slice the perf.data file
|
||||
that gets then processed, possibly via a perf script, to decide if that
|
||||
particular perf.data snapshot should be kept or not.
|
||||
|
||||
Implies --timestamp-filename, --no-buildid and --no-buildid-cache.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-stat[1], linkperf:perf-list[1]
|
||||
|
@@ -248,7 +248,7 @@ OPTIONS
|
||||
Note that when using the --itrace option the synthesized callchain size
|
||||
will override this value if the synthesized callchain size is bigger.
|
||||
|
||||
Default: 127
|
||||
Default: /proc/sys/kernel/perf_event_max_stack when present, 127 otherwise.
|
||||
|
||||
-G::
|
||||
--inverted::
|
||||
@@ -285,7 +285,7 @@ OPTIONS
|
||||
|
||||
-f::
|
||||
--force::
|
||||
Don't complain, do it.
|
||||
Don't do ownership validation.
|
||||
|
||||
--symfs=<directory>::
|
||||
Look for files with symbols relative to this directory.
|
||||
|
@@ -50,6 +50,22 @@ OPTIONS
|
||||
--dump-raw-trace=::
|
||||
Display verbose dump of the sched data.
|
||||
|
||||
OPTIONS for 'perf sched map'
|
||||
----------------------------
|
||||
|
||||
--compact::
|
||||
Show only CPUs with activity. Helps visualizing on high core
|
||||
count systems.
|
||||
|
||||
--cpus::
|
||||
Show just entries with activities for the given CPUs.
|
||||
|
||||
--color-cpus::
|
||||
Highlight the given cpus.
|
||||
|
||||
--color-pids::
|
||||
Highlight the given pids.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1]
|
||||
|
@@ -259,9 +259,23 @@ include::itrace.txt[]
|
||||
--full-source-path::
|
||||
Show the full path for source files for srcline output.
|
||||
|
||||
--max-stack::
|
||||
Set the stack depth limit when parsing the callchain, anything
|
||||
beyond the specified depth will be ignored. This is a trade-off
|
||||
between information loss and faster processing especially for
|
||||
workloads that can have a very long callchain stack.
|
||||
Note that when using the --itrace option the synthesized callchain size
|
||||
will override this value if the synthesized callchain size is bigger.
|
||||
|
||||
Default: /proc/sys/kernel/perf_event_max_stack when present, 127 otherwise.
|
||||
|
||||
--ns::
|
||||
Use 9 decimal places when displaying time (i.e. show the nanoseconds)
|
||||
|
||||
-f::
|
||||
--force::
|
||||
Don't do ownership validation.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-script-perl[1],
|
||||
|
@@ -177,7 +177,7 @@ Default is to monitor all CPUS.
|
||||
between information loss and faster processing especially for
|
||||
workloads that can have a very long callchain stack.
|
||||
|
||||
Default: 127
|
||||
Default: /proc/sys/kernel/perf_event_max_stack when present, 127 otherwise.
|
||||
|
||||
--ignore-callees=<regex>::
|
||||
Ignore callees of the function(s) matching the given regex.
|
||||
|
@@ -117,9 +117,41 @@ the thread executes on the designated CPUs. Default is to monitor all CPUs.
|
||||
--syscalls::
|
||||
Trace system calls. This options is enabled by default.
|
||||
|
||||
--call-graph [mode,type,min[,limit],order[,key][,branch]]::
|
||||
Setup and enable call-graph (stack chain/backtrace) recording.
|
||||
See `--call-graph` section in perf-record and perf-report
|
||||
man pages for details. The ones that are most useful in 'perf trace'
|
||||
are 'dwarf' and 'lbr', where available, try: 'perf trace --call-graph dwarf'.
|
||||
|
||||
Using this will, for the root user, bump the value of --mmap-pages to 4
|
||||
times the maximum for non-root users, based on the kernel.perf_event_mlock_kb
|
||||
sysctl. This is done only if the user doesn't specify a --mmap-pages value.
|
||||
|
||||
--kernel-syscall-graph::
|
||||
Show the kernel callchains on the syscall exit path.
|
||||
|
||||
--event::
|
||||
Trace other events, see 'perf list' for a complete list.
|
||||
|
||||
--max-stack::
|
||||
Set the stack depth limit when parsing the callchain, anything
|
||||
beyond the specified depth will be ignored. Note that at this point
|
||||
this is just about the presentation part, i.e. the kernel is still
|
||||
not limiting, the overhead of callchains needs to be set via the
|
||||
knobs in --call-graph dwarf.
|
||||
|
||||
Implies '--call-graph dwarf' when --call-graph not present on the
|
||||
command line, on systems where DWARF unwinding was built in.
|
||||
|
||||
Default: /proc/sys/kernel/perf_event_max_stack when present, 127 otherwise.
|
||||
|
||||
--min-stack::
|
||||
Set the stack depth limit when parsing the callchain, anything
|
||||
below the specified depth will be ignored. Disabled by default.
|
||||
|
||||
Implies '--call-graph dwarf' when --call-graph not present on the
|
||||
command line, on systems where DWARF unwinding was built in.
|
||||
|
||||
--proc-map-timeout::
|
||||
When processing pre-existing threads /proc/XXX/mmap, it may take a long time,
|
||||
because the file may be huge. A time out is needed in such cases.
|
||||
|
@@ -183,6 +183,11 @@ endif
|
||||
include config/Makefile
|
||||
endif
|
||||
|
||||
ifeq ($(config),0)
|
||||
include $(srctree)/tools/scripts/Makefile.arch
|
||||
-include arch/$(ARCH)/Makefile
|
||||
endif
|
||||
|
||||
# The FEATURE_DUMP_EXPORT holds location of the actual
|
||||
# FEATURE_DUMP file to be used to bypass feature detection
|
||||
# (for bpf or any other subproject)
|
||||
@@ -297,8 +302,6 @@ endif
|
||||
# because maintaining the nesting to match is a pain. If
|
||||
# we had "elif" things would have been much nicer...
|
||||
|
||||
-include arch/$(ARCH)/Makefile
|
||||
|
||||
ifneq ($(OUTPUT),)
|
||||
CFLAGS += -I$(OUTPUT)
|
||||
endif
|
||||
@@ -390,7 +393,7 @@ endif
|
||||
__build-dir = $(subst $(OUTPUT),,$(dir $@))
|
||||
build-dir = $(if $(__build-dir),$(__build-dir),.)
|
||||
|
||||
prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h fixdep
|
||||
prepare: $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)common-cmds.h fixdep archheaders
|
||||
|
||||
$(OUTPUT)%.o: %.c prepare FORCE
|
||||
$(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=$(build-dir) $@
|
||||
@@ -430,7 +433,7 @@ $(patsubst perf-%,%.o,$(PROGRAMS)): $(wildcard */*.h)
|
||||
|
||||
LIBPERF_IN := $(OUTPUT)libperf-in.o
|
||||
|
||||
$(LIBPERF_IN): fixdep FORCE
|
||||
$(LIBPERF_IN): prepare fixdep FORCE
|
||||
$(Q)$(MAKE) $(build)=libperf
|
||||
|
||||
$(LIB_FILE): $(LIBPERF_IN)
|
||||
@@ -625,7 +628,7 @@ config-clean:
|
||||
$(call QUIET_CLEAN, config)
|
||||
$(Q)$(MAKE) -C $(srctree)/tools/build/feature/ $(if $(OUTPUT),OUTPUT=$(OUTPUT)feature/,) clean >/dev/null
|
||||
|
||||
clean: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean config-clean
|
||||
clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean config-clean
|
||||
$(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS)
|
||||
$(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete
|
||||
$(Q)$(RM) $(OUTPUT).config-detected
|
||||
@@ -662,5 +665,5 @@ FORCE:
|
||||
.PHONY: all install clean config-clean strip install-gtk
|
||||
.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
|
||||
.PHONY: $(GIT-HEAD-PHONY) TAGS tags cscope FORCE prepare
|
||||
.PHONY: libtraceevent_plugins
|
||||
.PHONY: libtraceevent_plugins archheaders
|
||||
|
||||
|
@@ -3,4 +3,5 @@ PERF_HAVE_DWARF_REGS := 1
|
||||
endif
|
||||
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
|
@@ -10,19 +10,26 @@
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <dwarf-regs.h>
|
||||
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/kernel.h>
|
||||
#include "util.h"
|
||||
|
||||
struct pt_regs_dwarfnum {
|
||||
const char *name;
|
||||
unsigned int dwarfnum;
|
||||
unsigned int ptregs_offset;
|
||||
};
|
||||
|
||||
#define STR(s) #s
|
||||
#define REG_DWARFNUM_NAME(r, num) {.name = r, .dwarfnum = num}
|
||||
#define GPR_DWARFNUM_NAME(num) \
|
||||
{.name = STR(%gpr##num), .dwarfnum = num}
|
||||
#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0}
|
||||
#define REG_DWARFNUM_NAME(r, num) \
|
||||
{.name = STR(%)STR(r), .dwarfnum = num, \
|
||||
.ptregs_offset = offsetof(struct pt_regs, r)}
|
||||
#define GPR_DWARFNUM_NAME(num) \
|
||||
{.name = STR(%gpr##num), .dwarfnum = num, \
|
||||
.ptregs_offset = offsetof(struct pt_regs, gpr[num])}
|
||||
#define REG_DWARFNUM_END {.name = NULL, .dwarfnum = 0, .ptregs_offset = 0}
|
||||
|
||||
/*
|
||||
* Reference:
|
||||
@@ -61,12 +68,12 @@ static const struct pt_regs_dwarfnum regdwarfnum_table[] = {
|
||||
GPR_DWARFNUM_NAME(29),
|
||||
GPR_DWARFNUM_NAME(30),
|
||||
GPR_DWARFNUM_NAME(31),
|
||||
REG_DWARFNUM_NAME("%msr", 66),
|
||||
REG_DWARFNUM_NAME("%ctr", 109),
|
||||
REG_DWARFNUM_NAME("%link", 108),
|
||||
REG_DWARFNUM_NAME("%xer", 101),
|
||||
REG_DWARFNUM_NAME("%dar", 119),
|
||||
REG_DWARFNUM_NAME("%dsisr", 118),
|
||||
REG_DWARFNUM_NAME(msr, 66),
|
||||
REG_DWARFNUM_NAME(ctr, 109),
|
||||
REG_DWARFNUM_NAME(link, 108),
|
||||
REG_DWARFNUM_NAME(xer, 101),
|
||||
REG_DWARFNUM_NAME(dar, 119),
|
||||
REG_DWARFNUM_NAME(dsisr, 118),
|
||||
REG_DWARFNUM_END,
|
||||
};
|
||||
|
||||
@@ -86,3 +93,12 @@ const char *get_arch_regstr(unsigned int n)
|
||||
return roff->name;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int regs_query_register_offset(const char *name)
|
||||
{
|
||||
const struct pt_regs_dwarfnum *roff;
|
||||
for (roff = regdwarfnum_table; roff->name != NULL; roff++)
|
||||
if (!strcmp(roff->name, name))
|
||||
return roff->ptregs_offset;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@@ -19,12 +19,6 @@ bool elf__needs_adjust_symbols(GElf_Ehdr ehdr)
|
||||
ehdr.e_type == ET_DYN;
|
||||
}
|
||||
|
||||
#if defined(_CALL_ELF) && _CALL_ELF == 2
|
||||
void arch__elf_sym_adjust(GElf_Sym *sym)
|
||||
{
|
||||
sym->st_value += PPC64_LOCAL_ENTRY_OFFSET(sym->st_other);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(_CALL_ELF) || _CALL_ELF != 2
|
||||
@@ -65,18 +59,45 @@ bool arch__prefers_symtab(void)
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBELF_SUPPORT
|
||||
void arch__sym_update(struct symbol *s, GElf_Sym *sym)
|
||||
{
|
||||
s->arch_sym = sym->st_other;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define PPC64LE_LEP_OFFSET 8
|
||||
|
||||
void arch__fix_tev_from_maps(struct perf_probe_event *pev,
|
||||
struct probe_trace_event *tev, struct map *map)
|
||||
struct probe_trace_event *tev, struct map *map,
|
||||
struct symbol *sym)
|
||||
{
|
||||
int lep_offset;
|
||||
|
||||
/*
|
||||
* ppc64 ABIv2 local entry point is currently always 2 instructions
|
||||
* (8 bytes) after the global entry point.
|
||||
* When probing at a function entry point, we normally always want the
|
||||
* LEP since that catches calls to the function through both the GEP and
|
||||
* the LEP. Hence, we would like to probe at an offset of 8 bytes if
|
||||
* the user only specified the function entry.
|
||||
*
|
||||
* However, if the user specifies an offset, we fall back to using the
|
||||
* GEP since all userspace applications (objdump/readelf) show function
|
||||
* disassembly with offsets from the GEP.
|
||||
*
|
||||
* In addition, we shouldn't specify an offset for kretprobes.
|
||||
*/
|
||||
if (!pev->uprobes && map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS) {
|
||||
tev->point.address += PPC64LE_LEP_OFFSET;
|
||||
if (pev->point.offset || pev->point.retprobe || !map || !sym)
|
||||
return;
|
||||
|
||||
lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym);
|
||||
|
||||
if (map->dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS)
|
||||
tev->point.offset += PPC64LE_LEP_OFFSET;
|
||||
else if (lep_offset) {
|
||||
if (pev->uprobes)
|
||||
tev->point.address += lep_offset;
|
||||
else
|
||||
tev->point.offset += lep_offset;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@@ -4,3 +4,26 @@ endif
|
||||
HAVE_KVM_STAT_SUPPORT := 1
|
||||
PERF_HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET := 1
|
||||
PERF_HAVE_JITDUMP := 1
|
||||
|
||||
###
|
||||
# Syscall table generation
|
||||
#
|
||||
|
||||
out := $(OUTPUT)arch/x86/include/generated/asm
|
||||
header := $(out)/syscalls_64.c
|
||||
sys := $(srctree)/tools/perf/arch/x86/entry/syscalls
|
||||
systbl := $(sys)/syscalltbl.sh
|
||||
|
||||
# Create output directory if not already present
|
||||
_dummy := $(shell [ -d '$(out)' ] || mkdir -p '$(out)')
|
||||
|
||||
$(header): $(sys)/syscall_64.tbl $(systbl)
|
||||
@(test -d ../../kernel -a -d ../../tools -a -d ../perf && ( \
|
||||
(diff -B arch/x86/entry/syscalls/syscall_64.tbl ../../arch/x86/entry/syscalls/syscall_64.tbl >/dev/null) \
|
||||
|| echo "Warning: x86_64's syscall_64.tbl differs from kernel" >&2 )) || true
|
||||
$(Q)$(SHELL) '$(systbl)' $(sys)/syscall_64.tbl 'x86_64' > $@
|
||||
|
||||
clean::
|
||||
$(call QUIET_CLEAN, x86) $(RM) $(header)
|
||||
|
||||
archheaders: $(header)
|
||||
|
376
tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
Normal file
376
tools/perf/arch/x86/entry/syscalls/syscall_64.tbl
Normal file
@@ -0,0 +1,376 @@
|
||||
#
|
||||
# 64-bit system call numbers and entry vectors
|
||||
#
|
||||
# The format is:
|
||||
# <number> <abi> <name> <entry point>
|
||||
#
|
||||
# The abi is "common", "64" or "x32" for this file.
|
||||
#
|
||||
0 common read sys_read
|
||||
1 common write sys_write
|
||||
2 common open sys_open
|
||||
3 common close sys_close
|
||||
4 common stat sys_newstat
|
||||
5 common fstat sys_newfstat
|
||||
6 common lstat sys_newlstat
|
||||
7 common poll sys_poll
|
||||
8 common lseek sys_lseek
|
||||
9 common mmap sys_mmap
|
||||
10 common mprotect sys_mprotect
|
||||
11 common munmap sys_munmap
|
||||
12 common brk sys_brk
|
||||
13 64 rt_sigaction sys_rt_sigaction
|
||||
14 common rt_sigprocmask sys_rt_sigprocmask
|
||||
15 64 rt_sigreturn sys_rt_sigreturn/ptregs
|
||||
16 64 ioctl sys_ioctl
|
||||
17 common pread64 sys_pread64
|
||||
18 common pwrite64 sys_pwrite64
|
||||
19 64 readv sys_readv
|
||||
20 64 writev sys_writev
|
||||
21 common access sys_access
|
||||
22 common pipe sys_pipe
|
||||
23 common select sys_select
|
||||
24 common sched_yield sys_sched_yield
|
||||
25 common mremap sys_mremap
|
||||
26 common msync sys_msync
|
||||
27 common mincore sys_mincore
|
||||
28 common madvise sys_madvise
|
||||
29 common shmget sys_shmget
|
||||
30 common shmat sys_shmat
|
||||
31 common shmctl sys_shmctl
|
||||
32 common dup sys_dup
|
||||
33 common dup2 sys_dup2
|
||||
34 common pause sys_pause
|
||||
35 common nanosleep sys_nanosleep
|
||||
36 common getitimer sys_getitimer
|
||||
37 common alarm sys_alarm
|
||||
38 common setitimer sys_setitimer
|
||||
39 common getpid sys_getpid
|
||||
40 common sendfile sys_sendfile64
|
||||
41 common socket sys_socket
|
||||
42 common connect sys_connect
|
||||
43 common accept sys_accept
|
||||
44 common sendto sys_sendto
|
||||
45 64 recvfrom sys_recvfrom
|
||||
46 64 sendmsg sys_sendmsg
|
||||
47 64 recvmsg sys_recvmsg
|
||||
48 common shutdown sys_shutdown
|
||||
49 common bind sys_bind
|
||||
50 common listen sys_listen
|
||||
51 common getsockname sys_getsockname
|
||||
52 common getpeername sys_getpeername
|
||||
53 common socketpair sys_socketpair
|
||||
54 64 setsockopt sys_setsockopt
|
||||
55 64 getsockopt sys_getsockopt
|
||||
56 common clone sys_clone/ptregs
|
||||
57 common fork sys_fork/ptregs
|
||||
58 common vfork sys_vfork/ptregs
|
||||
59 64 execve sys_execve/ptregs
|
||||
60 common exit sys_exit
|
||||
61 common wait4 sys_wait4
|
||||
62 common kill sys_kill
|
||||
63 common uname sys_newuname
|
||||
64 common semget sys_semget
|
||||
65 common semop sys_semop
|
||||
66 common semctl sys_semctl
|
||||
67 common shmdt sys_shmdt
|
||||
68 common msgget sys_msgget
|
||||
69 common msgsnd sys_msgsnd
|
||||
70 common msgrcv sys_msgrcv
|
||||
71 common msgctl sys_msgctl
|
||||
72 common fcntl sys_fcntl
|
||||
73 common flock sys_flock
|
||||
74 common fsync sys_fsync
|
||||
75 common fdatasync sys_fdatasync
|
||||
76 common truncate sys_truncate
|
||||
77 common ftruncate sys_ftruncate
|
||||
78 common getdents sys_getdents
|
||||
79 common getcwd sys_getcwd
|
||||
80 common chdir sys_chdir
|
||||
81 common fchdir sys_fchdir
|
||||
82 common rename sys_rename
|
||||
83 common mkdir sys_mkdir
|
||||
84 common rmdir sys_rmdir
|
||||
85 common creat sys_creat
|
||||
86 common link sys_link
|
||||
87 common unlink sys_unlink
|
||||
88 common symlink sys_symlink
|
||||
89 common readlink sys_readlink
|
||||
90 common chmod sys_chmod
|
||||
91 common fchmod sys_fchmod
|
||||
92 common chown sys_chown
|
||||
93 common fchown sys_fchown
|
||||
94 common lchown sys_lchown
|
||||
95 common umask sys_umask
|
||||
96 common gettimeofday sys_gettimeofday
|
||||
97 common getrlimit sys_getrlimit
|
||||
98 common getrusage sys_getrusage
|
||||
99 common sysinfo sys_sysinfo
|
||||
100 common times sys_times
|
||||
101 64 ptrace sys_ptrace
|
||||
102 common getuid sys_getuid
|
||||
103 common syslog sys_syslog
|
||||
104 common getgid sys_getgid
|
||||
105 common setuid sys_setuid
|
||||
106 common setgid sys_setgid
|
||||
107 common geteuid sys_geteuid
|
||||
108 common getegid sys_getegid
|
||||
109 common setpgid sys_setpgid
|
||||
110 common getppid sys_getppid
|
||||
111 common getpgrp sys_getpgrp
|
||||
112 common setsid sys_setsid
|
||||
113 common setreuid sys_setreuid
|
||||
114 common setregid sys_setregid
|
||||
115 common getgroups sys_getgroups
|
||||
116 common setgroups sys_setgroups
|
||||
117 common setresuid sys_setresuid
|
||||
118 common getresuid sys_getresuid
|
||||
119 common setresgid sys_setresgid
|
||||
120 common getresgid sys_getresgid
|
||||
121 common getpgid sys_getpgid
|
||||
122 common setfsuid sys_setfsuid
|
||||
123 common setfsgid sys_setfsgid
|
||||
124 common getsid sys_getsid
|
||||
125 common capget sys_capget
|
||||
126 common capset sys_capset
|
||||
127 64 rt_sigpending sys_rt_sigpending
|
||||
128 64 rt_sigtimedwait sys_rt_sigtimedwait
|
||||
129 64 rt_sigqueueinfo sys_rt_sigqueueinfo
|
||||
130 common rt_sigsuspend sys_rt_sigsuspend
|
||||
131 64 sigaltstack sys_sigaltstack
|
||||
132 common utime sys_utime
|
||||
133 common mknod sys_mknod
|
||||
134 64 uselib
|
||||
135 common personality sys_personality
|
||||
136 common ustat sys_ustat
|
||||
137 common statfs sys_statfs
|
||||
138 common fstatfs sys_fstatfs
|
||||
139 common sysfs sys_sysfs
|
||||
140 common getpriority sys_getpriority
|
||||
141 common setpriority sys_setpriority
|
||||
142 common sched_setparam sys_sched_setparam
|
||||
143 common sched_getparam sys_sched_getparam
|
||||
144 common sched_setscheduler sys_sched_setscheduler
|
||||
145 common sched_getscheduler sys_sched_getscheduler
|
||||
146 common sched_get_priority_max sys_sched_get_priority_max
|
||||
147 common sched_get_priority_min sys_sched_get_priority_min
|
||||
148 common sched_rr_get_interval sys_sched_rr_get_interval
|
||||
149 common mlock sys_mlock
|
||||
150 common munlock sys_munlock
|
||||
151 common mlockall sys_mlockall
|
||||
152 common munlockall sys_munlockall
|
||||
153 common vhangup sys_vhangup
|
||||
154 common modify_ldt sys_modify_ldt
|
||||
155 common pivot_root sys_pivot_root
|
||||
156 64 _sysctl sys_sysctl
|
||||
157 common prctl sys_prctl
|
||||
158 common arch_prctl sys_arch_prctl
|
||||
159 common adjtimex sys_adjtimex
|
||||
160 common setrlimit sys_setrlimit
|
||||
161 common chroot sys_chroot
|
||||
162 common sync sys_sync
|
||||
163 common acct sys_acct
|
||||
164 common settimeofday sys_settimeofday
|
||||
165 common mount sys_mount
|
||||
166 common umount2 sys_umount
|
||||
167 common swapon sys_swapon
|
||||
168 common swapoff sys_swapoff
|
||||
169 common reboot sys_reboot
|
||||
170 common sethostname sys_sethostname
|
||||
171 common setdomainname sys_setdomainname
|
||||
172 common iopl sys_iopl/ptregs
|
||||
173 common ioperm sys_ioperm
|
||||
174 64 create_module
|
||||
175 common init_module sys_init_module
|
||||
176 common delete_module sys_delete_module
|
||||
177 64 get_kernel_syms
|
||||
178 64 query_module
|
||||
179 common quotactl sys_quotactl
|
||||
180 64 nfsservctl
|
||||
181 common getpmsg
|
||||
182 common putpmsg
|
||||
183 common afs_syscall
|
||||
184 common tuxcall
|
||||
185 common security
|
||||
186 common gettid sys_gettid
|
||||
187 common readahead sys_readahead
|
||||
188 common setxattr sys_setxattr
|
||||
189 common lsetxattr sys_lsetxattr
|
||||
190 common fsetxattr sys_fsetxattr
|
||||
191 common getxattr sys_getxattr
|
||||
192 common lgetxattr sys_lgetxattr
|
||||
193 common fgetxattr sys_fgetxattr
|
||||
194 common listxattr sys_listxattr
|
||||
195 common llistxattr sys_llistxattr
|
||||
196 common flistxattr sys_flistxattr
|
||||
197 common removexattr sys_removexattr
|
||||
198 common lremovexattr sys_lremovexattr
|
||||
199 common fremovexattr sys_fremovexattr
|
||||
200 common tkill sys_tkill
|
||||
201 common time sys_time
|
||||
202 common futex sys_futex
|
||||
203 common sched_setaffinity sys_sched_setaffinity
|
||||
204 common sched_getaffinity sys_sched_getaffinity
|
||||
205 64 set_thread_area
|
||||
206 64 io_setup sys_io_setup
|
||||
207 common io_destroy sys_io_destroy
|
||||
208 common io_getevents sys_io_getevents
|
||||
209 64 io_submit sys_io_submit
|
||||
210 common io_cancel sys_io_cancel
|
||||
211 64 get_thread_area
|
||||
212 common lookup_dcookie sys_lookup_dcookie
|
||||
213 common epoll_create sys_epoll_create
|
||||
214 64 epoll_ctl_old
|
||||
215 64 epoll_wait_old
|
||||
216 common remap_file_pages sys_remap_file_pages
|
||||
217 common getdents64 sys_getdents64
|
||||
218 common set_tid_address sys_set_tid_address
|
||||
219 common restart_syscall sys_restart_syscall
|
||||
220 common semtimedop sys_semtimedop
|
||||
221 common fadvise64 sys_fadvise64
|
||||
222 64 timer_create sys_timer_create
|
||||
223 common timer_settime sys_timer_settime
|
||||
224 common timer_gettime sys_timer_gettime
|
||||
225 common timer_getoverrun sys_timer_getoverrun
|
||||
226 common timer_delete sys_timer_delete
|
||||
227 common clock_settime sys_clock_settime
|
||||
228 common clock_gettime sys_clock_gettime
|
||||
229 common clock_getres sys_clock_getres
|
||||
230 common clock_nanosleep sys_clock_nanosleep
|
||||
231 common exit_group sys_exit_group
|
||||
232 common epoll_wait sys_epoll_wait
|
||||
233 common epoll_ctl sys_epoll_ctl
|
||||
234 common tgkill sys_tgkill
|
||||
235 common utimes sys_utimes
|
||||
236 64 vserver
|
||||
237 common mbind sys_mbind
|
||||
238 common set_mempolicy sys_set_mempolicy
|
||||
239 common get_mempolicy sys_get_mempolicy
|
||||
240 common mq_open sys_mq_open
|
||||
241 common mq_unlink sys_mq_unlink
|
||||
242 common mq_timedsend sys_mq_timedsend
|
||||
243 common mq_timedreceive sys_mq_timedreceive
|
||||
244 64 mq_notify sys_mq_notify
|
||||
245 common mq_getsetattr sys_mq_getsetattr
|
||||
246 64 kexec_load sys_kexec_load
|
||||
247 64 waitid sys_waitid
|
||||
248 common add_key sys_add_key
|
||||
249 common request_key sys_request_key
|
||||
250 common keyctl sys_keyctl
|
||||
251 common ioprio_set sys_ioprio_set
|
||||
252 common ioprio_get sys_ioprio_get
|
||||
253 common inotify_init sys_inotify_init
|
||||
254 common inotify_add_watch sys_inotify_add_watch
|
||||
255 common inotify_rm_watch sys_inotify_rm_watch
|
||||
256 common migrate_pages sys_migrate_pages
|
||||
257 common openat sys_openat
|
||||
258 common mkdirat sys_mkdirat
|
||||
259 common mknodat sys_mknodat
|
||||
260 common fchownat sys_fchownat
|
||||
261 common futimesat sys_futimesat
|
||||
262 common newfstatat sys_newfstatat
|
||||
263 common unlinkat sys_unlinkat
|
||||
264 common renameat sys_renameat
|
||||
265 common linkat sys_linkat
|
||||
266 common symlinkat sys_symlinkat
|
||||
267 common readlinkat sys_readlinkat
|
||||
268 common fchmodat sys_fchmodat
|
||||
269 common faccessat sys_faccessat
|
||||
270 common pselect6 sys_pselect6
|
||||
271 common ppoll sys_ppoll
|
||||
272 common unshare sys_unshare
|
||||
273 64 set_robust_list sys_set_robust_list
|
||||
274 64 get_robust_list sys_get_robust_list
|
||||
275 common splice sys_splice
|
||||
276 common tee sys_tee
|
||||
277 common sync_file_range sys_sync_file_range
|
||||
278 64 vmsplice sys_vmsplice
|
||||
279 64 move_pages sys_move_pages
|
||||
280 common utimensat sys_utimensat
|
||||
281 common epoll_pwait sys_epoll_pwait
|
||||
282 common signalfd sys_signalfd
|
||||
283 common timerfd_create sys_timerfd_create
|
||||
284 common eventfd sys_eventfd
|
||||
285 common fallocate sys_fallocate
|
||||
286 common timerfd_settime sys_timerfd_settime
|
||||
287 common timerfd_gettime sys_timerfd_gettime
|
||||
288 common accept4 sys_accept4
|
||||
289 common signalfd4 sys_signalfd4
|
||||
290 common eventfd2 sys_eventfd2
|
||||
291 common epoll_create1 sys_epoll_create1
|
||||
292 common dup3 sys_dup3
|
||||
293 common pipe2 sys_pipe2
|
||||
294 common inotify_init1 sys_inotify_init1
|
||||
295 64 preadv sys_preadv
|
||||
296 64 pwritev sys_pwritev
|
||||
297 64 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo
|
||||
298 common perf_event_open sys_perf_event_open
|
||||
299 64 recvmmsg sys_recvmmsg
|
||||
300 common fanotify_init sys_fanotify_init
|
||||
301 common fanotify_mark sys_fanotify_mark
|
||||
302 common prlimit64 sys_prlimit64
|
||||
303 common name_to_handle_at sys_name_to_handle_at
|
||||
304 common open_by_handle_at sys_open_by_handle_at
|
||||
305 common clock_adjtime sys_clock_adjtime
|
||||
306 common syncfs sys_syncfs
|
||||
307 64 sendmmsg sys_sendmmsg
|
||||
308 common setns sys_setns
|
||||
309 common getcpu sys_getcpu
|
||||
310 64 process_vm_readv sys_process_vm_readv
|
||||
311 64 process_vm_writev sys_process_vm_writev
|
||||
312 common kcmp sys_kcmp
|
||||
313 common finit_module sys_finit_module
|
||||
314 common sched_setattr sys_sched_setattr
|
||||
315 common sched_getattr sys_sched_getattr
|
||||
316 common renameat2 sys_renameat2
|
||||
317 common seccomp sys_seccomp
|
||||
318 common getrandom sys_getrandom
|
||||
319 common memfd_create sys_memfd_create
|
||||
320 common kexec_file_load sys_kexec_file_load
|
||||
321 common bpf sys_bpf
|
||||
322 64 execveat sys_execveat/ptregs
|
||||
323 common userfaultfd sys_userfaultfd
|
||||
324 common membarrier sys_membarrier
|
||||
325 common mlock2 sys_mlock2
|
||||
326 common copy_file_range sys_copy_file_range
|
||||
327 64 preadv2 sys_preadv2
|
||||
328 64 pwritev2 sys_pwritev2
|
||||
|
||||
#
|
||||
# x32-specific system call numbers start at 512 to avoid cache impact
|
||||
# for native 64-bit operation.
|
||||
#
|
||||
512 x32 rt_sigaction compat_sys_rt_sigaction
|
||||
513 x32 rt_sigreturn sys32_x32_rt_sigreturn
|
||||
514 x32 ioctl compat_sys_ioctl
|
||||
515 x32 readv compat_sys_readv
|
||||
516 x32 writev compat_sys_writev
|
||||
517 x32 recvfrom compat_sys_recvfrom
|
||||
518 x32 sendmsg compat_sys_sendmsg
|
||||
519 x32 recvmsg compat_sys_recvmsg
|
||||
520 x32 execve compat_sys_execve/ptregs
|
||||
521 x32 ptrace compat_sys_ptrace
|
||||
522 x32 rt_sigpending compat_sys_rt_sigpending
|
||||
523 x32 rt_sigtimedwait compat_sys_rt_sigtimedwait
|
||||
524 x32 rt_sigqueueinfo compat_sys_rt_sigqueueinfo
|
||||
525 x32 sigaltstack compat_sys_sigaltstack
|
||||
526 x32 timer_create compat_sys_timer_create
|
||||
527 x32 mq_notify compat_sys_mq_notify
|
||||
528 x32 kexec_load compat_sys_kexec_load
|
||||
529 x32 waitid compat_sys_waitid
|
||||
530 x32 set_robust_list compat_sys_set_robust_list
|
||||
531 x32 get_robust_list compat_sys_get_robust_list
|
||||
532 x32 vmsplice compat_sys_vmsplice
|
||||
533 x32 move_pages compat_sys_move_pages
|
||||
534 x32 preadv compat_sys_preadv64
|
||||
535 x32 pwritev compat_sys_pwritev64
|
||||
536 x32 rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo
|
||||
537 x32 recvmmsg compat_sys_recvmmsg
|
||||
538 x32 sendmmsg compat_sys_sendmmsg
|
||||
539 x32 process_vm_readv compat_sys_process_vm_readv
|
||||
540 x32 process_vm_writev compat_sys_process_vm_writev
|
||||
541 x32 setsockopt compat_sys_setsockopt
|
||||
542 x32 getsockopt compat_sys_getsockopt
|
||||
543 x32 io_setup compat_sys_io_setup
|
||||
544 x32 io_submit compat_sys_io_submit
|
||||
545 x32 execveat compat_sys_execveat/ptregs
|
39
tools/perf/arch/x86/entry/syscalls/syscalltbl.sh
Executable file
39
tools/perf/arch/x86/entry/syscalls/syscalltbl.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/bin/sh
|
||||
|
||||
in="$1"
|
||||
arch="$2"
|
||||
|
||||
syscall_macro() {
|
||||
nr="$1"
|
||||
name="$2"
|
||||
|
||||
echo " [$nr] = \"$name\","
|
||||
}
|
||||
|
||||
emit() {
|
||||
nr="$1"
|
||||
entry="$2"
|
||||
|
||||
syscall_macro "$nr" "$entry"
|
||||
}
|
||||
|
||||
echo "static const char *syscalltbl_${arch}[] = {"
|
||||
|
||||
sorted_table=$(mktemp /tmp/syscalltbl.XXXXXX)
|
||||
grep '^[0-9]' "$in" | sort -n > $sorted_table
|
||||
|
||||
max_nr=0
|
||||
while read nr abi name entry compat; do
|
||||
if [ $nr -ge 512 ] ; then # discard compat sycalls
|
||||
break
|
||||
fi
|
||||
|
||||
emit "$nr" "$name"
|
||||
max_nr=$nr
|
||||
done < $sorted_table
|
||||
|
||||
rm -f $sorted_table
|
||||
|
||||
echo "};"
|
||||
|
||||
echo "#define SYSCALLTBL_${arch}_MAX_ID ${max_nr}"
|
@@ -71,7 +71,7 @@ int test__perf_time_to_tsc(int subtest __maybe_unused)
|
||||
|
||||
CHECK__(parse_events(evlist, "cycles:u", NULL));
|
||||
|
||||
perf_evlist__config(evlist, &opts);
|
||||
perf_evlist__config(evlist, &opts, NULL);
|
||||
|
||||
evsel = perf_evlist__first(evlist);
|
||||
|
||||
|
@@ -438,6 +438,11 @@ struct auxtrace_record *intel_bts_recording_init(int *err)
|
||||
if (!intel_bts_pmu)
|
||||
return NULL;
|
||||
|
||||
if (setenv("JITDUMP_USE_ARCH_TIMESTAMP", "1", 1)) {
|
||||
*err = -errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
btsr = zalloc(sizeof(struct intel_bts_recording));
|
||||
if (!btsr) {
|
||||
*err = -ENOMEM;
|
||||
|
@@ -1027,6 +1027,11 @@ struct auxtrace_record *intel_pt_recording_init(int *err)
|
||||
if (!intel_pt_pmu)
|
||||
return NULL;
|
||||
|
||||
if (setenv("JITDUMP_USE_ARCH_TIMESTAMP", "1", 1)) {
|
||||
*err = -errno;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ptr = zalloc(sizeof(struct intel_pt_recording));
|
||||
if (!ptr) {
|
||||
*err = -ENOMEM;
|
||||
|
@@ -7,7 +7,6 @@
|
||||
#include <linux/types.h>
|
||||
#include "../../util/debug.h"
|
||||
#include "../../util/tsc.h"
|
||||
#include "tsc.h"
|
||||
|
||||
int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
|
||||
struct perf_tsc_conversion *tc)
|
||||
@@ -46,3 +45,34 @@ u64 rdtsc(void)
|
||||
|
||||
return low | ((u64)high) << 32;
|
||||
}
|
||||
|
||||
int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc,
|
||||
struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine)
|
||||
{
|
||||
union perf_event event = {
|
||||
.time_conv = {
|
||||
.header = {
|
||||
.type = PERF_RECORD_TIME_CONV,
|
||||
.size = sizeof(struct time_conv_event),
|
||||
},
|
||||
},
|
||||
};
|
||||
struct perf_tsc_conversion tc;
|
||||
int err;
|
||||
|
||||
err = perf_read_tsc_conversion(pc, &tc);
|
||||
if (err == -EOPNOTSUPP)
|
||||
return 0;
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
pr_debug2("Synthesizing TSC conversion information\n");
|
||||
|
||||
event.time_conv.time_mult = tc.time_mult;
|
||||
event.time_conv.time_shift = tc.time_shift;
|
||||
event.time_conv.time_zero = tc.time_zero;
|
||||
|
||||
return process(tool, &event, NULL, machine);
|
||||
}
|
||||
|
@@ -1,17 +0,0 @@
|
||||
#ifndef TOOLS_PERF_ARCH_X86_UTIL_TSC_H__
|
||||
#define TOOLS_PERF_ARCH_X86_UTIL_TSC_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct perf_tsc_conversion {
|
||||
u16 time_shift;
|
||||
u32 time_mult;
|
||||
u64 time_zero;
|
||||
};
|
||||
|
||||
struct perf_event_mmap_page;
|
||||
|
||||
int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc,
|
||||
struct perf_tsc_conversion *tc);
|
||||
|
||||
#endif /* TOOLS_PERF_ARCH_X86_UTIL_TSC_H__ */
|
@@ -83,7 +83,7 @@ static void *workerfn(void *arg)
|
||||
do {
|
||||
int ret;
|
||||
again:
|
||||
ret = futex_lock_pi(w->futex, NULL, 0, futex_flag);
|
||||
ret = futex_lock_pi(w->futex, NULL, futex_flag);
|
||||
|
||||
if (ret) { /* handle lock acquisition */
|
||||
if (!silent)
|
||||
|
@@ -57,13 +57,11 @@ futex_wake(u_int32_t *uaddr, int nr_wake, int opflags)
|
||||
|
||||
/**
|
||||
* futex_lock_pi() - block on uaddr as a PI mutex
|
||||
* @detect: whether (1) or not (0) to perform deadlock detection
|
||||
*/
|
||||
static inline int
|
||||
futex_lock_pi(u_int32_t *uaddr, struct timespec *timeout, int detect,
|
||||
int opflags)
|
||||
futex_lock_pi(u_int32_t *uaddr, struct timespec *timeout, int opflags)
|
||||
{
|
||||
return futex(uaddr, FUTEX_LOCK_PI, detect, timeout, NULL, 0, opflags);
|
||||
return futex(uaddr, FUTEX_LOCK_PI, 0, timeout, NULL, 0, opflags);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -6,6 +6,7 @@
|
||||
* Written by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
|
||||
*/
|
||||
|
||||
#include "debug.h"
|
||||
#include "../perf.h"
|
||||
#include "../util/util.h"
|
||||
#include <subcmd/parse-options.h>
|
||||
@@ -63,14 +64,16 @@ static struct perf_event_attr cycle_attr = {
|
||||
.config = PERF_COUNT_HW_CPU_CYCLES
|
||||
};
|
||||
|
||||
static void init_cycles(void)
|
||||
static int init_cycles(void)
|
||||
{
|
||||
cycles_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, perf_event_open_cloexec_flag());
|
||||
|
||||
if (cycles_fd < 0 && errno == ENOSYS)
|
||||
die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
|
||||
else
|
||||
BUG_ON(cycles_fd < 0);
|
||||
if (cycles_fd < 0 && errno == ENOSYS) {
|
||||
pr_debug("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return cycles_fd;
|
||||
}
|
||||
|
||||
static u64 get_cycles(void)
|
||||
@@ -155,8 +158,13 @@ static int bench_mem_common(int argc, const char **argv, struct bench_mem_info *
|
||||
|
||||
argc = parse_options(argc, argv, options, info->usage, 0);
|
||||
|
||||
if (use_cycles)
|
||||
init_cycles();
|
||||
if (use_cycles) {
|
||||
i = init_cycles();
|
||||
if (i < 0) {
|
||||
fprintf(stderr, "Failed to open cycles counter\n");
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
size = (size_t)perf_atoll((char *)size_str);
|
||||
size_total = (double)size * nr_loops;
|
||||
|
@@ -12,6 +12,7 @@
|
||||
#include <subcmd/parse-options.h>
|
||||
#include "util/util.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/config.h"
|
||||
|
||||
static bool use_system_config, use_user_config;
|
||||
|
||||
@@ -32,13 +33,28 @@ static struct option config_options[] = {
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
static int show_config(const char *key, const char *value,
|
||||
void *cb __maybe_unused)
|
||||
static int show_config(struct perf_config_set *set)
|
||||
{
|
||||
if (value)
|
||||
printf("%s=%s\n", key, value);
|
||||
else
|
||||
printf("%s\n", key);
|
||||
struct perf_config_section *section;
|
||||
struct perf_config_item *item;
|
||||
struct list_head *sections;
|
||||
|
||||
if (set == NULL)
|
||||
return -1;
|
||||
|
||||
sections = &set->sections;
|
||||
if (list_empty(sections))
|
||||
return -1;
|
||||
|
||||
list_for_each_entry(section, sections, node) {
|
||||
list_for_each_entry(item, §ion->items, node) {
|
||||
char *value = item->value;
|
||||
|
||||
if (value)
|
||||
printf("%s.%s=%s\n", section->name,
|
||||
item->name, value);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -46,6 +62,7 @@ static int show_config(const char *key, const char *value,
|
||||
int cmd_config(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
{
|
||||
int ret = 0;
|
||||
struct perf_config_set *set;
|
||||
char *user_config = mkpath("%s/.perfconfig", getenv("HOME"));
|
||||
|
||||
argc = parse_options(argc, argv, config_options, config_usage,
|
||||
@@ -63,13 +80,19 @@ int cmd_config(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
else if (use_user_config)
|
||||
config_exclusive_filename = user_config;
|
||||
|
||||
set = perf_config_set__new();
|
||||
if (!set) {
|
||||
ret = -1;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
switch (actions) {
|
||||
case ACTION_LIST:
|
||||
if (argc) {
|
||||
pr_err("Error: takes no arguments\n");
|
||||
parse_options_usage(config_usage, config_options, "l", 1);
|
||||
} else {
|
||||
ret = perf_config(show_config, NULL);
|
||||
ret = show_config(set);
|
||||
if (ret < 0) {
|
||||
const char * config_filename = config_exclusive_filename;
|
||||
if (!config_exclusive_filename)
|
||||
@@ -83,5 +106,7 @@ int cmd_config(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
usage_with_options(config_usage, config_options);
|
||||
}
|
||||
|
||||
perf_config_set__delete(set);
|
||||
out_err:
|
||||
return ret;
|
||||
}
|
||||
|
@@ -428,7 +428,7 @@ static void hists__baseline_only(struct hists *hists)
|
||||
struct rb_root *root;
|
||||
struct rb_node *next;
|
||||
|
||||
if (sort__need_collapse)
|
||||
if (hists__has(hists, need_collapse))
|
||||
root = &hists->entries_collapsed;
|
||||
else
|
||||
root = hists->entries_in;
|
||||
@@ -450,7 +450,7 @@ static void hists__precompute(struct hists *hists)
|
||||
struct rb_root *root;
|
||||
struct rb_node *next;
|
||||
|
||||
if (sort__need_collapse)
|
||||
if (hists__has(hists, need_collapse))
|
||||
root = &hists->entries_collapsed;
|
||||
else
|
||||
root = hists->entries_in;
|
||||
|
@@ -61,6 +61,7 @@ static int check_emacsclient_version(void)
|
||||
struct child_process ec_process;
|
||||
const char *argv_ec[] = { "emacsclient", "--version", NULL };
|
||||
int version;
|
||||
int ret = -1;
|
||||
|
||||
/* emacsclient prints its version number on stderr */
|
||||
memset(&ec_process, 0, sizeof(ec_process));
|
||||
@@ -71,7 +72,10 @@ static int check_emacsclient_version(void)
|
||||
fprintf(stderr, "Failed to start emacsclient.\n");
|
||||
return -1;
|
||||
}
|
||||
strbuf_read(&buffer, ec_process.err, 20);
|
||||
if (strbuf_read(&buffer, ec_process.err, 20) < 0) {
|
||||
fprintf(stderr, "Failed to read emacsclient version\n");
|
||||
goto out;
|
||||
}
|
||||
close(ec_process.err);
|
||||
|
||||
/*
|
||||
@@ -82,8 +86,7 @@ static int check_emacsclient_version(void)
|
||||
|
||||
if (prefixcmp(buffer.buf, "emacsclient")) {
|
||||
fprintf(stderr, "Failed to parse emacsclient version.\n");
|
||||
strbuf_release(&buffer);
|
||||
return -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
version = atoi(buffer.buf + strlen("emacsclient"));
|
||||
@@ -92,12 +95,11 @@ static int check_emacsclient_version(void)
|
||||
fprintf(stderr,
|
||||
"emacsclient version '%d' too old (< 22).\n",
|
||||
version);
|
||||
strbuf_release(&buffer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
} else
|
||||
ret = 0;
|
||||
out:
|
||||
strbuf_release(&buffer);
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void exec_woman_emacs(const char *path, const char *page)
|
||||
|
@@ -748,6 +748,7 @@ int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.auxtrace_info = perf_event__repipe_op2_synth,
|
||||
.auxtrace = perf_event__repipe_auxtrace,
|
||||
.auxtrace_error = perf_event__repipe_op2_synth,
|
||||
.time_conv = perf_event__repipe_op2_synth,
|
||||
.finished_round = perf_event__repipe_oe_synth,
|
||||
.build_id = perf_event__repipe_op2_synth,
|
||||
.id_index = perf_event__repipe_op2_synth,
|
||||
|
@@ -375,7 +375,7 @@ static u64 find_callsite(struct perf_evsel *evsel, struct perf_sample *sample)
|
||||
}
|
||||
|
||||
al.thread = machine__findnew_thread(machine, sample->pid, sample->tid);
|
||||
sample__resolve_callchain(sample, NULL, evsel, &al, 16);
|
||||
sample__resolve_callchain(sample, &callchain_cursor, NULL, evsel, &al, 16);
|
||||
|
||||
callchain_cursor_commit(&callchain_cursor);
|
||||
while (true) {
|
||||
|
@@ -982,7 +982,7 @@ static int kvm_live_open_events(struct perf_kvm_stat *kvm)
|
||||
struct perf_evlist *evlist = kvm->evlist;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
perf_evlist__config(evlist, &kvm->opts);
|
||||
perf_evlist__config(evlist, &kvm->opts, NULL);
|
||||
|
||||
/*
|
||||
* Note: exclude_{guest,host} do not apply here.
|
||||
|
@@ -62,19 +62,22 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
|
||||
int rec_argc, i = 0, j;
|
||||
const char **rec_argv;
|
||||
int ret;
|
||||
bool all_user = false, all_kernel = false;
|
||||
struct option options[] = {
|
||||
OPT_CALLBACK('e', "event", &mem, "event",
|
||||
"event selector. use 'perf mem record -e list' to list available events",
|
||||
parse_record_events),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show counter open errors, etc)"),
|
||||
OPT_BOOLEAN('U', "--all-user", &all_user, "collect only user level data"),
|
||||
OPT_BOOLEAN('K', "--all-kernel", &all_kernel, "collect only kernel level data"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, record_mem_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
rec_argc = argc + 7; /* max number of arguments */
|
||||
rec_argc = argc + 9; /* max number of arguments */
|
||||
rec_argv = calloc(rec_argc + 1, sizeof(char *));
|
||||
if (!rec_argv)
|
||||
return -1;
|
||||
@@ -103,6 +106,12 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
|
||||
rec_argv[i++] = perf_mem_events__name(j);
|
||||
};
|
||||
|
||||
if (all_user)
|
||||
rec_argv[i++] = "--all-user";
|
||||
|
||||
if (all_kernel)
|
||||
rec_argv[i++] = "--all-kernel";
|
||||
|
||||
for (j = 0; j < argc; j++, i++)
|
||||
rec_argv[i] = argv[j];
|
||||
|
||||
|
@@ -29,10 +29,12 @@
|
||||
#include "util/data.h"
|
||||
#include "util/perf_regs.h"
|
||||
#include "util/auxtrace.h"
|
||||
#include "util/tsc.h"
|
||||
#include "util/parse-branch-options.h"
|
||||
#include "util/parse-regs-options.h"
|
||||
#include "util/llvm-utils.h"
|
||||
#include "util/bpf-loader.h"
|
||||
#include "util/trigger.h"
|
||||
#include "asm/bug.h"
|
||||
|
||||
#include <unistd.h>
|
||||
@@ -55,6 +57,8 @@ struct record {
|
||||
bool no_buildid_cache;
|
||||
bool no_buildid_cache_set;
|
||||
bool buildid_all;
|
||||
bool timestamp_filename;
|
||||
bool switch_output;
|
||||
unsigned long long samples;
|
||||
};
|
||||
|
||||
@@ -124,9 +128,10 @@ out:
|
||||
static volatile int done;
|
||||
static volatile int signr = -1;
|
||||
static volatile int child_finished;
|
||||
static volatile int auxtrace_snapshot_enabled;
|
||||
static volatile int auxtrace_snapshot_err;
|
||||
|
||||
static volatile int auxtrace_record__snapshot_started;
|
||||
static DEFINE_TRIGGER(auxtrace_snapshot_trigger);
|
||||
static DEFINE_TRIGGER(switch_output_trigger);
|
||||
|
||||
static void sig_handler(int sig)
|
||||
{
|
||||
@@ -244,11 +249,12 @@ static void record__read_auxtrace_snapshot(struct record *rec)
|
||||
{
|
||||
pr_debug("Recording AUX area tracing snapshot\n");
|
||||
if (record__auxtrace_read_snapshot_all(rec) < 0) {
|
||||
auxtrace_snapshot_err = -1;
|
||||
trigger_error(&auxtrace_snapshot_trigger);
|
||||
} else {
|
||||
auxtrace_snapshot_err = auxtrace_record__snapshot_finish(rec->itr);
|
||||
if (!auxtrace_snapshot_err)
|
||||
auxtrace_snapshot_enabled = 1;
|
||||
if (auxtrace_record__snapshot_finish(rec->itr))
|
||||
trigger_error(&auxtrace_snapshot_trigger);
|
||||
else
|
||||
trigger_ready(&auxtrace_snapshot_trigger);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -283,7 +289,7 @@ static int record__open(struct record *rec)
|
||||
struct record_opts *opts = &rec->opts;
|
||||
int rc = 0;
|
||||
|
||||
perf_evlist__config(evlist, opts);
|
||||
perf_evlist__config(evlist, opts, &callchain_param);
|
||||
|
||||
evlist__for_each(evlist, pos) {
|
||||
try_again:
|
||||
@@ -494,6 +500,73 @@ record__finish_output(struct record *rec)
|
||||
return;
|
||||
}
|
||||
|
||||
static int record__synthesize_workload(struct record *rec)
|
||||
{
|
||||
struct {
|
||||
struct thread_map map;
|
||||
struct thread_map_data map_data;
|
||||
} thread_map;
|
||||
|
||||
thread_map.map.nr = 1;
|
||||
thread_map.map.map[0].pid = rec->evlist->workload.pid;
|
||||
thread_map.map.map[0].comm = NULL;
|
||||
return perf_event__synthesize_thread_map(&rec->tool, &thread_map.map,
|
||||
process_synthesized_event,
|
||||
&rec->session->machines.host,
|
||||
rec->opts.sample_address,
|
||||
rec->opts.proc_map_timeout);
|
||||
}
|
||||
|
||||
static int record__synthesize(struct record *rec);
|
||||
|
||||
static int
|
||||
record__switch_output(struct record *rec, bool at_exit)
|
||||
{
|
||||
struct perf_data_file *file = &rec->file;
|
||||
int fd, err;
|
||||
|
||||
/* Same Size: "2015122520103046"*/
|
||||
char timestamp[] = "InvalidTimestamp";
|
||||
|
||||
rec->samples = 0;
|
||||
record__finish_output(rec);
|
||||
err = fetch_current_timestamp(timestamp, sizeof(timestamp));
|
||||
if (err) {
|
||||
pr_err("Failed to get current timestamp\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fd = perf_data_file__switch(file, timestamp,
|
||||
rec->session->header.data_offset,
|
||||
at_exit);
|
||||
if (fd >= 0 && !at_exit) {
|
||||
rec->bytes_written = 0;
|
||||
rec->session->header.data_size = 0;
|
||||
}
|
||||
|
||||
if (!quiet)
|
||||
fprintf(stderr, "[ perf record: Dump %s.%s ]\n",
|
||||
file->path, timestamp);
|
||||
|
||||
/* Output tracking events */
|
||||
if (!at_exit) {
|
||||
record__synthesize(rec);
|
||||
|
||||
/*
|
||||
* In 'perf record --switch-output' without -a,
|
||||
* record__synthesize() in record__switch_output() won't
|
||||
* generate tracking events because there's no thread_map
|
||||
* in evlist. Which causes newly created perf.data doesn't
|
||||
* contain map and comm information.
|
||||
* Create a fake thread_map and directly call
|
||||
* perf_event__synthesize_thread_map() for those events.
|
||||
*/
|
||||
if (target__none(&rec->opts.target))
|
||||
record__synthesize_workload(rec);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
static volatile int workload_exec_errno;
|
||||
|
||||
/*
|
||||
@@ -512,6 +585,15 @@ static void workload_exec_failed_signal(int signo __maybe_unused,
|
||||
|
||||
static void snapshot_sig_handler(int sig);
|
||||
|
||||
int __weak
|
||||
perf_event__synth_time_conv(const struct perf_event_mmap_page *pc __maybe_unused,
|
||||
struct perf_tool *tool __maybe_unused,
|
||||
perf_event__handler_t process __maybe_unused,
|
||||
struct machine *machine __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int record__synthesize(struct record *rec)
|
||||
{
|
||||
struct perf_session *session = rec->session;
|
||||
@@ -549,6 +631,11 @@ static int record__synthesize(struct record *rec)
|
||||
}
|
||||
}
|
||||
|
||||
err = perf_event__synth_time_conv(rec->evlist->mmap[0].base, tool,
|
||||
process_synthesized_event, machine);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (rec->opts.full_auxtrace) {
|
||||
err = perf_event__synthesize_auxtrace_info(rec->itr, tool,
|
||||
session, process_synthesized_event);
|
||||
@@ -600,10 +687,16 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
||||
signal(SIGCHLD, sig_handler);
|
||||
signal(SIGINT, sig_handler);
|
||||
signal(SIGTERM, sig_handler);
|
||||
if (rec->opts.auxtrace_snapshot_mode)
|
||||
|
||||
if (rec->opts.auxtrace_snapshot_mode || rec->switch_output) {
|
||||
signal(SIGUSR2, snapshot_sig_handler);
|
||||
else
|
||||
if (rec->opts.auxtrace_snapshot_mode)
|
||||
trigger_on(&auxtrace_snapshot_trigger);
|
||||
if (rec->switch_output)
|
||||
trigger_on(&switch_output_trigger);
|
||||
} else {
|
||||
signal(SIGUSR2, SIG_IGN);
|
||||
}
|
||||
|
||||
session = perf_session__new(file, false, tool);
|
||||
if (session == NULL) {
|
||||
@@ -729,27 +822,45 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
||||
perf_evlist__enable(rec->evlist);
|
||||
}
|
||||
|
||||
auxtrace_snapshot_enabled = 1;
|
||||
trigger_ready(&auxtrace_snapshot_trigger);
|
||||
trigger_ready(&switch_output_trigger);
|
||||
for (;;) {
|
||||
unsigned long long hits = rec->samples;
|
||||
|
||||
if (record__mmap_read_all(rec) < 0) {
|
||||
auxtrace_snapshot_enabled = 0;
|
||||
trigger_error(&auxtrace_snapshot_trigger);
|
||||
trigger_error(&switch_output_trigger);
|
||||
err = -1;
|
||||
goto out_child;
|
||||
}
|
||||
|
||||
if (auxtrace_record__snapshot_started) {
|
||||
auxtrace_record__snapshot_started = 0;
|
||||
if (!auxtrace_snapshot_err)
|
||||
if (!trigger_is_error(&auxtrace_snapshot_trigger))
|
||||
record__read_auxtrace_snapshot(rec);
|
||||
if (auxtrace_snapshot_err) {
|
||||
if (trigger_is_error(&auxtrace_snapshot_trigger)) {
|
||||
pr_err("AUX area tracing snapshot failed\n");
|
||||
err = -1;
|
||||
goto out_child;
|
||||
}
|
||||
}
|
||||
|
||||
if (trigger_is_hit(&switch_output_trigger)) {
|
||||
trigger_ready(&switch_output_trigger);
|
||||
|
||||
if (!quiet)
|
||||
fprintf(stderr, "[ perf record: dump data: Woken up %ld times ]\n",
|
||||
waking);
|
||||
waking = 0;
|
||||
fd = record__switch_output(rec, false);
|
||||
if (fd < 0) {
|
||||
pr_err("Failed to switch to new file\n");
|
||||
trigger_error(&switch_output_trigger);
|
||||
err = fd;
|
||||
goto out_child;
|
||||
}
|
||||
}
|
||||
|
||||
if (hits == rec->samples) {
|
||||
if (done || draining)
|
||||
break;
|
||||
@@ -772,12 +883,13 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
||||
* disable events in this case.
|
||||
*/
|
||||
if (done && !disabled && !target__none(&opts->target)) {
|
||||
auxtrace_snapshot_enabled = 0;
|
||||
trigger_off(&auxtrace_snapshot_trigger);
|
||||
perf_evlist__disable(rec->evlist);
|
||||
disabled = true;
|
||||
}
|
||||
}
|
||||
auxtrace_snapshot_enabled = 0;
|
||||
trigger_off(&auxtrace_snapshot_trigger);
|
||||
trigger_off(&switch_output_trigger);
|
||||
|
||||
if (forks && workload_exec_errno) {
|
||||
char msg[STRERR_BUFSIZE];
|
||||
@@ -811,11 +923,22 @@ out_child:
|
||||
/* this will be recalculated during process_buildids() */
|
||||
rec->samples = 0;
|
||||
|
||||
if (!err)
|
||||
record__finish_output(rec);
|
||||
if (!err) {
|
||||
if (!rec->timestamp_filename) {
|
||||
record__finish_output(rec);
|
||||
} else {
|
||||
fd = record__switch_output(rec, true);
|
||||
if (fd < 0) {
|
||||
status = fd;
|
||||
goto out_delete_session;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!err && !quiet) {
|
||||
char samples[128];
|
||||
const char *postfix = rec->timestamp_filename ?
|
||||
".<timestamp>" : "";
|
||||
|
||||
if (rec->samples && !rec->opts.full_auxtrace)
|
||||
scnprintf(samples, sizeof(samples),
|
||||
@@ -823,9 +946,9 @@ out_child:
|
||||
else
|
||||
samples[0] = '\0';
|
||||
|
||||
fprintf(stderr, "[ perf record: Captured and wrote %.3f MB %s%s ]\n",
|
||||
fprintf(stderr, "[ perf record: Captured and wrote %.3f MB %s%s%s ]\n",
|
||||
perf_data_file__size(file) / 1024.0 / 1024.0,
|
||||
file->path, samples);
|
||||
file->path, postfix, samples);
|
||||
}
|
||||
|
||||
out_delete_session:
|
||||
@@ -833,58 +956,61 @@ out_delete_session:
|
||||
return status;
|
||||
}
|
||||
|
||||
static void callchain_debug(void)
|
||||
static void callchain_debug(struct callchain_param *callchain)
|
||||
{
|
||||
static const char *str[CALLCHAIN_MAX] = { "NONE", "FP", "DWARF", "LBR" };
|
||||
|
||||
pr_debug("callchain: type %s\n", str[callchain_param.record_mode]);
|
||||
pr_debug("callchain: type %s\n", str[callchain->record_mode]);
|
||||
|
||||
if (callchain_param.record_mode == CALLCHAIN_DWARF)
|
||||
if (callchain->record_mode == CALLCHAIN_DWARF)
|
||||
pr_debug("callchain: stack dump size %d\n",
|
||||
callchain_param.dump_size);
|
||||
callchain->dump_size);
|
||||
}
|
||||
|
||||
int record_opts__parse_callchain(struct record_opts *record,
|
||||
struct callchain_param *callchain,
|
||||
const char *arg, bool unset)
|
||||
{
|
||||
int ret;
|
||||
callchain->enabled = !unset;
|
||||
|
||||
/* --no-call-graph */
|
||||
if (unset) {
|
||||
callchain->record_mode = CALLCHAIN_NONE;
|
||||
pr_debug("callchain: disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = parse_callchain_record_opt(arg, callchain);
|
||||
if (!ret) {
|
||||
/* Enable data address sampling for DWARF unwind. */
|
||||
if (callchain->record_mode == CALLCHAIN_DWARF)
|
||||
record->sample_address = true;
|
||||
callchain_debug(callchain);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int record_parse_callchain_opt(const struct option *opt,
|
||||
const char *arg,
|
||||
int unset)
|
||||
{
|
||||
int ret;
|
||||
struct record_opts *record = (struct record_opts *)opt->value;
|
||||
|
||||
record->callgraph_set = true;
|
||||
callchain_param.enabled = !unset;
|
||||
|
||||
/* --no-call-graph */
|
||||
if (unset) {
|
||||
callchain_param.record_mode = CALLCHAIN_NONE;
|
||||
pr_debug("callchain: disabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = parse_callchain_record_opt(arg, &callchain_param);
|
||||
if (!ret) {
|
||||
/* Enable data address sampling for DWARF unwind. */
|
||||
if (callchain_param.record_mode == CALLCHAIN_DWARF)
|
||||
record->sample_address = true;
|
||||
callchain_debug();
|
||||
}
|
||||
|
||||
return ret;
|
||||
return record_opts__parse_callchain(opt->value, &callchain_param, arg, unset);
|
||||
}
|
||||
|
||||
int record_callchain_opt(const struct option *opt,
|
||||
const char *arg __maybe_unused,
|
||||
int unset __maybe_unused)
|
||||
{
|
||||
struct record_opts *record = (struct record_opts *)opt->value;
|
||||
struct callchain_param *callchain = opt->value;
|
||||
|
||||
record->callgraph_set = true;
|
||||
callchain_param.enabled = true;
|
||||
callchain->enabled = true;
|
||||
|
||||
if (callchain_param.record_mode == CALLCHAIN_NONE)
|
||||
callchain_param.record_mode = CALLCHAIN_FP;
|
||||
if (callchain->record_mode == CALLCHAIN_NONE)
|
||||
callchain->record_mode = CALLCHAIN_FP;
|
||||
|
||||
callchain_debug();
|
||||
callchain_debug(callchain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1122,7 +1248,7 @@ struct option __record_options[] = {
|
||||
record__parse_mmap_pages),
|
||||
OPT_BOOLEAN(0, "group", &record.opts.group,
|
||||
"put the counters into a counter group"),
|
||||
OPT_CALLBACK_NOOPT('g', NULL, &record.opts,
|
||||
OPT_CALLBACK_NOOPT('g', NULL, &callchain_param,
|
||||
NULL, "enables call-graph recording" ,
|
||||
&record_callchain_opt),
|
||||
OPT_CALLBACK(0, "call-graph", &record.opts,
|
||||
@@ -1195,6 +1321,10 @@ struct option __record_options[] = {
|
||||
"file", "vmlinux pathname"),
|
||||
OPT_BOOLEAN(0, "buildid-all", &record.buildid_all,
|
||||
"Record build-id of all DSOs regardless of hits"),
|
||||
OPT_BOOLEAN(0, "timestamp-filename", &record.timestamp_filename,
|
||||
"append timestamp to output filename"),
|
||||
OPT_BOOLEAN(0, "switch-output", &record.switch_output,
|
||||
"Switch output when receive SIGUSR2"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@@ -1250,6 +1380,9 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rec->switch_output)
|
||||
rec->timestamp_filename = true;
|
||||
|
||||
if (!rec->itr) {
|
||||
rec->itr = auxtrace_record__init(rec->evlist, &err);
|
||||
if (err)
|
||||
@@ -1261,6 +1394,14 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = bpf__setup_stdout(rec->evlist);
|
||||
if (err) {
|
||||
bpf__strerror_setup_stdout(rec->evlist, err, errbuf, sizeof(errbuf));
|
||||
pr_err("ERROR: Setup BPF stdout failed: %s\n",
|
||||
errbuf);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = -ENOMEM;
|
||||
|
||||
symbol__init(NULL);
|
||||
@@ -1275,8 +1416,36 @@ int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"If some relocation was applied (e.g. kexec) symbols may be misresolved\n"
|
||||
"even with a suitable vmlinux or kallsyms file.\n\n");
|
||||
|
||||
if (rec->no_buildid_cache || rec->no_buildid)
|
||||
if (rec->no_buildid_cache || rec->no_buildid) {
|
||||
disable_buildid_cache();
|
||||
} else if (rec->switch_output) {
|
||||
/*
|
||||
* In 'perf record --switch-output', disable buildid
|
||||
* generation by default to reduce data file switching
|
||||
* overhead. Still generate buildid if they are required
|
||||
* explicitly using
|
||||
*
|
||||
* perf record --signal-trigger --no-no-buildid \
|
||||
* --no-no-buildid-cache
|
||||
*
|
||||
* Following code equals to:
|
||||
*
|
||||
* if ((rec->no_buildid || !rec->no_buildid_set) &&
|
||||
* (rec->no_buildid_cache || !rec->no_buildid_cache_set))
|
||||
* disable_buildid_cache();
|
||||
*/
|
||||
bool disable = true;
|
||||
|
||||
if (rec->no_buildid_set && !rec->no_buildid)
|
||||
disable = false;
|
||||
if (rec->no_buildid_cache_set && !rec->no_buildid_cache)
|
||||
disable = false;
|
||||
if (disable) {
|
||||
rec->no_buildid = true;
|
||||
rec->no_buildid_cache = true;
|
||||
disable_buildid_cache();
|
||||
}
|
||||
}
|
||||
|
||||
if (rec->evlist->nr_entries == 0 &&
|
||||
perf_evlist__add_default(rec->evlist) < 0) {
|
||||
@@ -1335,9 +1504,13 @@ out_symbol_exit:
|
||||
|
||||
static void snapshot_sig_handler(int sig __maybe_unused)
|
||||
{
|
||||
if (!auxtrace_snapshot_enabled)
|
||||
return;
|
||||
auxtrace_snapshot_enabled = 0;
|
||||
auxtrace_snapshot_err = auxtrace_record__snapshot_start(record.itr);
|
||||
auxtrace_record__snapshot_started = 1;
|
||||
if (trigger_is_ready(&auxtrace_snapshot_trigger)) {
|
||||
trigger_hit(&auxtrace_snapshot_trigger);
|
||||
auxtrace_record__snapshot_started = 1;
|
||||
if (auxtrace_record__snapshot_start(record.itr))
|
||||
trigger_error(&auxtrace_snapshot_trigger);
|
||||
}
|
||||
|
||||
if (trigger_is_ready(&switch_output_trigger))
|
||||
trigger_hit(&switch_output_trigger);
|
||||
}
|
||||
|
@@ -47,7 +47,6 @@ struct report {
|
||||
struct perf_tool tool;
|
||||
struct perf_session *session;
|
||||
bool use_tui, use_gtk, use_stdio;
|
||||
bool dont_use_callchains;
|
||||
bool show_full_info;
|
||||
bool show_threads;
|
||||
bool inverted_callchain;
|
||||
@@ -235,7 +234,7 @@ static int report__setup_sample_type(struct report *rep)
|
||||
sample_type |= PERF_SAMPLE_BRANCH_STACK;
|
||||
|
||||
if (!is_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
|
||||
if (sort__has_parent) {
|
||||
if (perf_hpp_list.parent) {
|
||||
ui__error("Selected --sort parent, but no "
|
||||
"callchain data. Did you call "
|
||||
"'perf record' without -g?\n");
|
||||
@@ -247,7 +246,7 @@ static int report__setup_sample_type(struct report *rep)
|
||||
"you call 'perf record' without -g?\n");
|
||||
return -1;
|
||||
}
|
||||
} else if (!rep->dont_use_callchains &&
|
||||
} else if (!callchain_param.enabled &&
|
||||
callchain_param.mode != CHAIN_NONE &&
|
||||
!symbol_conf.use_callchain) {
|
||||
symbol_conf.use_callchain = true;
|
||||
@@ -599,13 +598,15 @@ static int __cmd_report(struct report *rep)
|
||||
static int
|
||||
report_parse_callchain_opt(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct report *rep = (struct report *)opt->value;
|
||||
struct callchain_param *callchain = opt->value;
|
||||
|
||||
callchain->enabled = !unset;
|
||||
/*
|
||||
* --no-call-graph
|
||||
*/
|
||||
if (unset) {
|
||||
rep->dont_use_callchains = true;
|
||||
symbol_conf.use_callchain = false;
|
||||
callchain->mode = CHAIN_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -690,7 +691,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.ordered_events = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
.max_stack = PERF_MAX_STACK_DEPTH,
|
||||
.max_stack = sysctl_perf_event_max_stack,
|
||||
.pretty_printing_style = "normal",
|
||||
.socket_filter = -1,
|
||||
};
|
||||
@@ -734,7 +735,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"regex filter to identify parent, see: '--sort parent'"),
|
||||
OPT_BOOLEAN('x', "exclude-other", &symbol_conf.exclude_other,
|
||||
"Only display entries with parent-match"),
|
||||
OPT_CALLBACK_DEFAULT('g', "call-graph", &report,
|
||||
OPT_CALLBACK_DEFAULT('g', "call-graph", &callchain_param,
|
||||
"print_type,threshold[,print_limit],order,sort_key[,branch],value",
|
||||
report_callchain_help, &report_parse_callchain_opt,
|
||||
callchain_default_opt),
|
||||
@@ -743,7 +744,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
OPT_INTEGER(0, "max-stack", &report.max_stack,
|
||||
"Set the maximum stack depth when parsing the callchain, "
|
||||
"anything beyond the specified depth will be ignored. "
|
||||
"Default: " __stringify(PERF_MAX_STACK_DEPTH)),
|
||||
"Default: kernel.perf_event_max_stack or " __stringify(PERF_MAX_STACK_DEPTH)),
|
||||
OPT_BOOLEAN('G', "inverted", &report.inverted_callchain,
|
||||
"alias for inverted call graph"),
|
||||
OPT_CALLBACK(0, "ignore-callees", NULL, "regex",
|
||||
@@ -935,7 +936,7 @@ repeat:
|
||||
goto error;
|
||||
}
|
||||
|
||||
sort__need_collapse = true;
|
||||
perf_hpp_list.need_collapse = true;
|
||||
}
|
||||
|
||||
/* Force tty output for header output and per-thread stat. */
|
||||
|
@@ -11,6 +11,8 @@
|
||||
#include "util/session.h"
|
||||
#include "util/tool.h"
|
||||
#include "util/cloexec.h"
|
||||
#include "util/thread_map.h"
|
||||
#include "util/color.h"
|
||||
|
||||
#include <subcmd/parse-options.h>
|
||||
#include "util/trace-event.h"
|
||||
@@ -122,6 +124,21 @@ struct trace_sched_handler {
|
||||
struct machine *machine);
|
||||
};
|
||||
|
||||
#define COLOR_PIDS PERF_COLOR_BLUE
|
||||
#define COLOR_CPUS PERF_COLOR_BG_RED
|
||||
|
||||
struct perf_sched_map {
|
||||
DECLARE_BITMAP(comp_cpus_mask, MAX_CPUS);
|
||||
int *comp_cpus;
|
||||
bool comp;
|
||||
struct thread_map *color_pids;
|
||||
const char *color_pids_str;
|
||||
struct cpu_map *color_cpus;
|
||||
const char *color_cpus_str;
|
||||
struct cpu_map *cpus;
|
||||
const char *cpus_str;
|
||||
};
|
||||
|
||||
struct perf_sched {
|
||||
struct perf_tool tool;
|
||||
const char *sort_order;
|
||||
@@ -173,6 +190,7 @@ struct perf_sched {
|
||||
struct list_head sort_list, cmp_pid;
|
||||
bool force;
|
||||
bool skip_merge;
|
||||
struct perf_sched_map map;
|
||||
};
|
||||
|
||||
static u64 get_nsecs(void)
|
||||
@@ -1339,6 +1357,38 @@ static int process_sched_wakeup_event(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
union map_priv {
|
||||
void *ptr;
|
||||
bool color;
|
||||
};
|
||||
|
||||
static bool thread__has_color(struct thread *thread)
|
||||
{
|
||||
union map_priv priv = {
|
||||
.ptr = thread__priv(thread),
|
||||
};
|
||||
|
||||
return priv.color;
|
||||
}
|
||||
|
||||
static struct thread*
|
||||
map__findnew_thread(struct perf_sched *sched, struct machine *machine, pid_t pid, pid_t tid)
|
||||
{
|
||||
struct thread *thread = machine__findnew_thread(machine, pid, tid);
|
||||
union map_priv priv = {
|
||||
.color = false,
|
||||
};
|
||||
|
||||
if (!sched->map.color_pids || !thread || thread__priv(thread))
|
||||
return thread;
|
||||
|
||||
if (thread_map__has(sched->map.color_pids, tid))
|
||||
priv.color = true;
|
||||
|
||||
thread__set_priv(thread, priv.ptr);
|
||||
return thread;
|
||||
}
|
||||
|
||||
static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
|
||||
struct perf_sample *sample, struct machine *machine)
|
||||
{
|
||||
@@ -1347,13 +1397,25 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
|
||||
int new_shortname;
|
||||
u64 timestamp0, timestamp = sample->time;
|
||||
s64 delta;
|
||||
int cpu, this_cpu = sample->cpu;
|
||||
int i, this_cpu = sample->cpu;
|
||||
int cpus_nr;
|
||||
bool new_cpu = false;
|
||||
const char *color = PERF_COLOR_NORMAL;
|
||||
|
||||
BUG_ON(this_cpu >= MAX_CPUS || this_cpu < 0);
|
||||
|
||||
if (this_cpu > sched->max_cpu)
|
||||
sched->max_cpu = this_cpu;
|
||||
|
||||
if (sched->map.comp) {
|
||||
cpus_nr = bitmap_weight(sched->map.comp_cpus_mask, MAX_CPUS);
|
||||
if (!test_and_set_bit(this_cpu, sched->map.comp_cpus_mask)) {
|
||||
sched->map.comp_cpus[cpus_nr++] = this_cpu;
|
||||
new_cpu = true;
|
||||
}
|
||||
} else
|
||||
cpus_nr = sched->max_cpu;
|
||||
|
||||
timestamp0 = sched->cpu_last_switched[this_cpu];
|
||||
sched->cpu_last_switched[this_cpu] = timestamp;
|
||||
if (timestamp0)
|
||||
@@ -1366,7 +1428,7 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
|
||||
return -1;
|
||||
}
|
||||
|
||||
sched_in = machine__findnew_thread(machine, -1, next_pid);
|
||||
sched_in = map__findnew_thread(sched, machine, -1, next_pid);
|
||||
if (sched_in == NULL)
|
||||
return -1;
|
||||
|
||||
@@ -1400,26 +1462,52 @@ static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
|
||||
new_shortname = 1;
|
||||
}
|
||||
|
||||
for (cpu = 0; cpu <= sched->max_cpu; cpu++) {
|
||||
for (i = 0; i < cpus_nr; i++) {
|
||||
int cpu = sched->map.comp ? sched->map.comp_cpus[i] : i;
|
||||
struct thread *curr_thread = sched->curr_thread[cpu];
|
||||
const char *pid_color = color;
|
||||
const char *cpu_color = color;
|
||||
|
||||
if (curr_thread && thread__has_color(curr_thread))
|
||||
pid_color = COLOR_PIDS;
|
||||
|
||||
if (sched->map.cpus && !cpu_map__has(sched->map.cpus, cpu))
|
||||
continue;
|
||||
|
||||
if (sched->map.color_cpus && cpu_map__has(sched->map.color_cpus, cpu))
|
||||
cpu_color = COLOR_CPUS;
|
||||
|
||||
if (cpu != this_cpu)
|
||||
printf(" ");
|
||||
color_fprintf(stdout, cpu_color, " ");
|
||||
else
|
||||
printf("*");
|
||||
color_fprintf(stdout, cpu_color, "*");
|
||||
|
||||
if (sched->curr_thread[cpu])
|
||||
printf("%2s ", sched->curr_thread[cpu]->shortname);
|
||||
color_fprintf(stdout, pid_color, "%2s ", sched->curr_thread[cpu]->shortname);
|
||||
else
|
||||
printf(" ");
|
||||
color_fprintf(stdout, color, " ");
|
||||
}
|
||||
|
||||
printf(" %12.6f secs ", (double)timestamp/1e9);
|
||||
if (sched->map.cpus && !cpu_map__has(sched->map.cpus, this_cpu))
|
||||
goto out;
|
||||
|
||||
color_fprintf(stdout, color, " %12.6f secs ", (double)timestamp/1e9);
|
||||
if (new_shortname) {
|
||||
printf("%s => %s:%d\n",
|
||||
const char *pid_color = color;
|
||||
|
||||
if (thread__has_color(sched_in))
|
||||
pid_color = COLOR_PIDS;
|
||||
|
||||
color_fprintf(stdout, pid_color, "%s => %s:%d",
|
||||
sched_in->shortname, thread__comm_str(sched_in), sched_in->tid);
|
||||
} else {
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (sched->map.comp && new_cpu)
|
||||
color_fprintf(stdout, color, " (CPU %d)", this_cpu);
|
||||
|
||||
out:
|
||||
color_fprintf(stdout, color, "\n");
|
||||
|
||||
thread__put(sched_in);
|
||||
|
||||
return 0;
|
||||
@@ -1675,9 +1763,75 @@ static int perf_sched__lat(struct perf_sched *sched)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_map_cpus(struct perf_sched *sched)
|
||||
{
|
||||
struct cpu_map *map;
|
||||
|
||||
sched->max_cpu = sysconf(_SC_NPROCESSORS_CONF);
|
||||
|
||||
if (sched->map.comp) {
|
||||
sched->map.comp_cpus = zalloc(sched->max_cpu * sizeof(int));
|
||||
if (!sched->map.comp_cpus)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!sched->map.cpus_str)
|
||||
return 0;
|
||||
|
||||
map = cpu_map__new(sched->map.cpus_str);
|
||||
if (!map) {
|
||||
pr_err("failed to get cpus map from %s\n", sched->map.cpus_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sched->map.cpus = map;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_color_pids(struct perf_sched *sched)
|
||||
{
|
||||
struct thread_map *map;
|
||||
|
||||
if (!sched->map.color_pids_str)
|
||||
return 0;
|
||||
|
||||
map = thread_map__new_by_tid_str(sched->map.color_pids_str);
|
||||
if (!map) {
|
||||
pr_err("failed to get thread map from %s\n", sched->map.color_pids_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sched->map.color_pids = map;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_color_cpus(struct perf_sched *sched)
|
||||
{
|
||||
struct cpu_map *map;
|
||||
|
||||
if (!sched->map.color_cpus_str)
|
||||
return 0;
|
||||
|
||||
map = cpu_map__new(sched->map.color_cpus_str);
|
||||
if (!map) {
|
||||
pr_err("failed to get thread map from %s\n", sched->map.color_cpus_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
sched->map.color_cpus = map;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_sched__map(struct perf_sched *sched)
|
||||
{
|
||||
sched->max_cpu = sysconf(_SC_NPROCESSORS_CONF);
|
||||
if (setup_map_cpus(sched))
|
||||
return -1;
|
||||
|
||||
if (setup_color_pids(sched))
|
||||
return -1;
|
||||
|
||||
if (setup_color_cpus(sched))
|
||||
return -1;
|
||||
|
||||
setup_pager();
|
||||
if (perf_sched__read_events(sched))
|
||||
@@ -1831,6 +1985,17 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"dump raw trace in ASCII"),
|
||||
OPT_END()
|
||||
};
|
||||
const struct option map_options[] = {
|
||||
OPT_BOOLEAN(0, "compact", &sched.map.comp,
|
||||
"map output in compact mode"),
|
||||
OPT_STRING(0, "color-pids", &sched.map.color_pids_str, "pids",
|
||||
"highlight given pids in map"),
|
||||
OPT_STRING(0, "color-cpus", &sched.map.color_cpus_str, "cpus",
|
||||
"highlight given CPUs in map"),
|
||||
OPT_STRING(0, "cpus", &sched.map.cpus_str, "cpus",
|
||||
"display given CPUs in map"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const latency_usage[] = {
|
||||
"perf sched latency [<options>]",
|
||||
NULL
|
||||
@@ -1839,6 +2004,10 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"perf sched replay [<options>]",
|
||||
NULL
|
||||
};
|
||||
const char * const map_usage[] = {
|
||||
"perf sched map [<options>]",
|
||||
NULL
|
||||
};
|
||||
const char *const sched_subcommands[] = { "record", "latency", "map",
|
||||
"replay", "script", NULL };
|
||||
const char *sched_usage[] = {
|
||||
@@ -1887,6 +2056,11 @@ int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
setup_sorting(&sched, latency_options, latency_usage);
|
||||
return perf_sched__lat(&sched);
|
||||
} else if (!strcmp(argv[0], "map")) {
|
||||
if (argc) {
|
||||
argc = parse_options(argc, argv, map_options, map_usage, 0);
|
||||
if (argc)
|
||||
usage_with_options(map_usage, map_options);
|
||||
}
|
||||
sched.tp_handler = &map_ops;
|
||||
setup_sorting(&sched, latency_options, latency_usage);
|
||||
return perf_sched__map(&sched);
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include "util/thread_map.h"
|
||||
#include "util/stat.h"
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/stringify.h>
|
||||
#include "asm/bug.h"
|
||||
#include "util/mem-events.h"
|
||||
|
||||
@@ -317,19 +318,19 @@ static void set_print_ip_opts(struct perf_event_attr *attr)
|
||||
|
||||
output[type].print_ip_opts = 0;
|
||||
if (PRINT_FIELD(IP))
|
||||
output[type].print_ip_opts |= PRINT_IP_OPT_IP;
|
||||
output[type].print_ip_opts |= EVSEL__PRINT_IP;
|
||||
|
||||
if (PRINT_FIELD(SYM))
|
||||
output[type].print_ip_opts |= PRINT_IP_OPT_SYM;
|
||||
output[type].print_ip_opts |= EVSEL__PRINT_SYM;
|
||||
|
||||
if (PRINT_FIELD(DSO))
|
||||
output[type].print_ip_opts |= PRINT_IP_OPT_DSO;
|
||||
output[type].print_ip_opts |= EVSEL__PRINT_DSO;
|
||||
|
||||
if (PRINT_FIELD(SYMOFFSET))
|
||||
output[type].print_ip_opts |= PRINT_IP_OPT_SYMOFFSET;
|
||||
output[type].print_ip_opts |= EVSEL__PRINT_SYMOFFSET;
|
||||
|
||||
if (PRINT_FIELD(SRCLINE))
|
||||
output[type].print_ip_opts |= PRINT_IP_OPT_SRCLINE;
|
||||
output[type].print_ip_opts |= EVSEL__PRINT_SRCLINE;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -569,18 +570,23 @@ static void print_sample_bts(struct perf_sample *sample,
|
||||
/* print branch_from information */
|
||||
if (PRINT_FIELD(IP)) {
|
||||
unsigned int print_opts = output[attr->type].print_ip_opts;
|
||||
struct callchain_cursor *cursor = NULL;
|
||||
|
||||
if (symbol_conf.use_callchain && sample->callchain) {
|
||||
printf("\n");
|
||||
} else {
|
||||
printf(" ");
|
||||
if (print_opts & PRINT_IP_OPT_SRCLINE) {
|
||||
if (symbol_conf.use_callchain && sample->callchain &&
|
||||
thread__resolve_callchain(al->thread, &callchain_cursor, evsel,
|
||||
sample, NULL, NULL, scripting_max_stack) == 0)
|
||||
cursor = &callchain_cursor;
|
||||
|
||||
if (cursor == NULL) {
|
||||
putchar(' ');
|
||||
if (print_opts & EVSEL__PRINT_SRCLINE) {
|
||||
print_srcline_last = true;
|
||||
print_opts &= ~PRINT_IP_OPT_SRCLINE;
|
||||
print_opts &= ~EVSEL__PRINT_SRCLINE;
|
||||
}
|
||||
}
|
||||
perf_evsel__print_ip(evsel, sample, al, print_opts,
|
||||
scripting_max_stack);
|
||||
} else
|
||||
putchar('\n');
|
||||
|
||||
sample__fprintf_sym(sample, al, 0, print_opts, cursor, stdout);
|
||||
}
|
||||
|
||||
/* print branch_to information */
|
||||
@@ -783,14 +789,15 @@ static void process_event(struct perf_script *script,
|
||||
printf("%16" PRIu64, sample->weight);
|
||||
|
||||
if (PRINT_FIELD(IP)) {
|
||||
if (!symbol_conf.use_callchain)
|
||||
printf(" ");
|
||||
else
|
||||
printf("\n");
|
||||
struct callchain_cursor *cursor = NULL;
|
||||
|
||||
perf_evsel__print_ip(evsel, sample, al,
|
||||
output[attr->type].print_ip_opts,
|
||||
scripting_max_stack);
|
||||
if (symbol_conf.use_callchain && sample->callchain &&
|
||||
thread__resolve_callchain(al->thread, &callchain_cursor, evsel,
|
||||
sample, NULL, NULL, scripting_max_stack) == 0)
|
||||
cursor = &callchain_cursor;
|
||||
|
||||
putchar(cursor ? '\n' : ' ');
|
||||
sample__fprintf_sym(sample, al, 0, output[attr->type].print_ip_opts, cursor, stdout);
|
||||
}
|
||||
|
||||
if (PRINT_FIELD(IREGS))
|
||||
@@ -1959,6 +1966,7 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.attr = process_attr,
|
||||
.event_update = perf_event__process_event_update,
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.id_index = perf_event__process_id_index,
|
||||
@@ -2020,6 +2028,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"only consider symbols in these pids"),
|
||||
OPT_STRING(0, "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
|
||||
"only consider symbols in these tids"),
|
||||
OPT_UINTEGER(0, "max-stack", &scripting_max_stack,
|
||||
"Set the maximum stack depth when parsing the callchain, "
|
||||
"anything beyond the specified depth will be ignored. "
|
||||
"Default: kernel.perf_event_max_stack or " __stringify(PERF_MAX_STACK_DEPTH)),
|
||||
OPT_BOOLEAN('I', "show-info", &show_full_info,
|
||||
"display extended information from perf.data file"),
|
||||
OPT_BOOLEAN('\0', "show-kernel-path", &symbol_conf.show_kernel_path,
|
||||
@@ -2055,6 +2067,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
NULL
|
||||
};
|
||||
|
||||
scripting_max_stack = sysctl_perf_event_max_stack;
|
||||
|
||||
setup_scripting();
|
||||
|
||||
argc = parse_options_subcommand(argc, argv, options, script_subcommands, script_usage,
|
||||
|
@@ -298,6 +298,14 @@ static int read_counter(struct perf_evsel *counter)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose > 1) {
|
||||
fprintf(stat_config.output,
|
||||
"%s: %d: %" PRIu64 " %" PRIu64 " %" PRIu64 "\n",
|
||||
perf_evsel__name(counter),
|
||||
cpu,
|
||||
count->val, count->ena, count->run);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -688,7 +688,7 @@ static int hist_iter__top_callback(struct hist_entry_iter *iter,
|
||||
struct hist_entry *he = iter->he;
|
||||
struct perf_evsel *evsel = iter->evsel;
|
||||
|
||||
if (sort__has_sym && single)
|
||||
if (perf_hpp_list.sym && single)
|
||||
perf_top__record_precise_ip(top, he, evsel->idx, al->addr);
|
||||
|
||||
hist__account_cycles(iter->sample->branch_stack, al, iter->sample,
|
||||
@@ -886,7 +886,7 @@ static int perf_top__start_counters(struct perf_top *top)
|
||||
struct perf_evlist *evlist = top->evlist;
|
||||
struct record_opts *opts = &top->record_opts;
|
||||
|
||||
perf_evlist__config(evlist, opts);
|
||||
perf_evlist__config(evlist, opts, &callchain_param);
|
||||
|
||||
evlist__for_each(evlist, counter) {
|
||||
try_again:
|
||||
@@ -917,15 +917,15 @@ out_err:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int perf_top__setup_sample_type(struct perf_top *top __maybe_unused)
|
||||
static int callchain_param__setup_sample_type(struct callchain_param *callchain)
|
||||
{
|
||||
if (!sort__has_sym) {
|
||||
if (symbol_conf.use_callchain) {
|
||||
if (!perf_hpp_list.sym) {
|
||||
if (callchain->enabled) {
|
||||
ui__error("Selected -g but \"sym\" not present in --sort/-s.");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (callchain_param.mode != CHAIN_NONE) {
|
||||
if (callchain_register_param(&callchain_param) < 0) {
|
||||
} else if (callchain->mode != CHAIN_NONE) {
|
||||
if (callchain_register_param(callchain) < 0) {
|
||||
ui__error("Can't register callchain params.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -952,7 +952,7 @@ static int __cmd_top(struct perf_top *top)
|
||||
goto out_delete;
|
||||
}
|
||||
|
||||
ret = perf_top__setup_sample_type(top);
|
||||
ret = callchain_param__setup_sample_type(&callchain_param);
|
||||
if (ret)
|
||||
goto out_delete;
|
||||
|
||||
@@ -962,7 +962,7 @@ static int __cmd_top(struct perf_top *top)
|
||||
machine__synthesize_threads(&top->session->machines.host, &opts->target,
|
||||
top->evlist->threads, false, opts->proc_map_timeout);
|
||||
|
||||
if (sort__has_socket) {
|
||||
if (perf_hpp_list.socket) {
|
||||
ret = perf_env__read_cpu_topology_map(&perf_env);
|
||||
if (ret < 0)
|
||||
goto out_err_cpu_topo;
|
||||
@@ -1045,18 +1045,17 @@ callchain_opt(const struct option *opt, const char *arg, int unset)
|
||||
static int
|
||||
parse_callchain_opt(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
struct record_opts *record = (struct record_opts *)opt->value;
|
||||
struct callchain_param *callchain = opt->value;
|
||||
|
||||
record->callgraph_set = true;
|
||||
callchain_param.enabled = !unset;
|
||||
callchain_param.record_mode = CALLCHAIN_FP;
|
||||
callchain->enabled = !unset;
|
||||
callchain->record_mode = CALLCHAIN_FP;
|
||||
|
||||
/*
|
||||
* --no-call-graph
|
||||
*/
|
||||
if (unset) {
|
||||
symbol_conf.use_callchain = false;
|
||||
callchain_param.record_mode = CALLCHAIN_NONE;
|
||||
callchain->record_mode = CALLCHAIN_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1104,7 +1103,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
},
|
||||
.proc_map_timeout = 500,
|
||||
},
|
||||
.max_stack = PERF_MAX_STACK_DEPTH,
|
||||
.max_stack = sysctl_perf_event_max_stack,
|
||||
.sym_pcnt_filter = 5,
|
||||
};
|
||||
struct record_opts *opts = &top.record_opts;
|
||||
@@ -1162,17 +1161,17 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
"output field(s): overhead, period, sample plus all of sort keys"),
|
||||
OPT_BOOLEAN('n', "show-nr-samples", &symbol_conf.show_nr_samples,
|
||||
"Show a column with the number of samples"),
|
||||
OPT_CALLBACK_NOOPT('g', NULL, &top.record_opts,
|
||||
OPT_CALLBACK_NOOPT('g', NULL, &callchain_param,
|
||||
NULL, "enables call-graph recording and display",
|
||||
&callchain_opt),
|
||||
OPT_CALLBACK(0, "call-graph", &top.record_opts,
|
||||
OPT_CALLBACK(0, "call-graph", &callchain_param,
|
||||
"record_mode[,record_size],print_type,threshold[,print_limit],order,sort_key[,branch]",
|
||||
top_callchain_help, &parse_callchain_opt),
|
||||
OPT_BOOLEAN(0, "children", &symbol_conf.cumulate_callchain,
|
||||
"Accumulate callchains of children and show total overhead as well"),
|
||||
OPT_INTEGER(0, "max-stack", &top.max_stack,
|
||||
"Set the maximum stack depth when parsing the callchain. "
|
||||
"Default: " __stringify(PERF_MAX_STACK_DEPTH)),
|
||||
"Default: kernel.perf_event_max_stack or " __stringify(PERF_MAX_STACK_DEPTH)),
|
||||
OPT_CALLBACK(0, "ignore-callees", NULL, "regex",
|
||||
"ignore callees of these functions in call graphs",
|
||||
report_parse_ignore_callees_opt),
|
||||
@@ -1256,7 +1255,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
|
||||
sort__mode = SORT_MODE__TOP;
|
||||
/* display thread wants entries to be collapsed in a different tree */
|
||||
sort__need_collapse = 1;
|
||||
perf_hpp_list.need_collapse = 1;
|
||||
|
||||
if (top.use_stdio)
|
||||
use_browser = 0;
|
||||
@@ -1312,7 +1311,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
|
||||
top.sym_evsel = perf_evlist__first(top.evlist);
|
||||
|
||||
if (!symbol_conf.use_callchain) {
|
||||
if (!callchain_param.enabled) {
|
||||
symbol_conf.cumulate_callchain = false;
|
||||
perf_hpp__cancel_cumulate();
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -27,7 +27,7 @@ NO_PERF_REGS := 1
|
||||
ifeq ($(ARCH),x86)
|
||||
$(call detected,CONFIG_X86)
|
||||
ifeq (${IS_64_BIT}, 1)
|
||||
CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT
|
||||
CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT -DHAVE_SYSCALL_TABLE -I$(OUTPUT)arch/x86/include/generated
|
||||
ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S
|
||||
LIBUNWIND_LIBS = -lunwind -lunwind-x86_64
|
||||
$(call detected,CONFIG_X86_64)
|
||||
@@ -295,9 +295,6 @@ ifndef NO_LIBELF
|
||||
CFLAGS += -DHAVE_ELF_GETPHDRNUM_SUPPORT
|
||||
endif
|
||||
|
||||
# include ARCH specific config
|
||||
-include $(src-perf)/arch/$(ARCH)/Makefile
|
||||
|
||||
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);
|
||||
|
@@ -92,6 +92,22 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int use_arch_timestamp;
|
||||
|
||||
static inline uint64_t
|
||||
get_arch_timestamp(void)
|
||||
{
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
unsigned int low, high;
|
||||
|
||||
asm volatile("rdtsc" : "=a" (low), "=d" (high));
|
||||
|
||||
return low | ((uint64_t)high) << 32;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#define NSEC_PER_SEC 1000000000
|
||||
static int perf_clk_id = CLOCK_MONOTONIC;
|
||||
|
||||
@@ -107,6 +123,9 @@ perf_get_timestamp(void)
|
||||
struct timespec ts;
|
||||
int ret;
|
||||
|
||||
if (use_arch_timestamp)
|
||||
return get_arch_timestamp();
|
||||
|
||||
ret = clock_gettime(perf_clk_id, &ts);
|
||||
if (ret)
|
||||
return 0;
|
||||
@@ -203,6 +222,17 @@ perf_close_marker_file(void)
|
||||
munmap(marker_addr, pgsz);
|
||||
}
|
||||
|
||||
static void
|
||||
init_arch_timestamp(void)
|
||||
{
|
||||
char *str = getenv("JITDUMP_USE_ARCH_TIMESTAMP");
|
||||
|
||||
if (!str || !*str || !strcmp(str, "0"))
|
||||
return;
|
||||
|
||||
use_arch_timestamp = 1;
|
||||
}
|
||||
|
||||
void *jvmti_open(void)
|
||||
{
|
||||
int pad_cnt;
|
||||
@@ -211,11 +241,17 @@ void *jvmti_open(void)
|
||||
int fd;
|
||||
FILE *fp;
|
||||
|
||||
init_arch_timestamp();
|
||||
|
||||
/*
|
||||
* check if clockid is supported
|
||||
*/
|
||||
if (!perf_get_timestamp())
|
||||
warnx("jvmti: kernel does not support %d clock id", perf_clk_id);
|
||||
if (!perf_get_timestamp()) {
|
||||
if (use_arch_timestamp)
|
||||
warnx("jvmti: arch timestamp not supported");
|
||||
else
|
||||
warnx("jvmti: kernel does not support %d clock id", perf_clk_id);
|
||||
}
|
||||
|
||||
memset(&header, 0, sizeof(header));
|
||||
|
||||
@@ -263,6 +299,9 @@ void *jvmti_open(void)
|
||||
|
||||
header.timestamp = perf_get_timestamp();
|
||||
|
||||
if (use_arch_timestamp)
|
||||
header.flags |= JITDUMP_FLAGS_ARCH_TIMESTAMP;
|
||||
|
||||
if (!fwrite(&header, sizeof(header), 1, fp)) {
|
||||
warn("jvmti: cannot write dumpfile header");
|
||||
goto error;
|
||||
|
@@ -17,6 +17,7 @@
|
||||
#include <subcmd/parse-options.h>
|
||||
#include "util/bpf-loader.h"
|
||||
#include "util/debug.h"
|
||||
#include <api/fs/fs.h>
|
||||
#include <api/fs/tracing_path.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
@@ -308,9 +309,11 @@ static int handle_alias(int *argcp, const char ***argv)
|
||||
if (*argcp > 1) {
|
||||
struct strbuf buf;
|
||||
|
||||
strbuf_init(&buf, PATH_MAX);
|
||||
strbuf_addstr(&buf, alias_string);
|
||||
sq_quote_argv(&buf, (*argv) + 1, PATH_MAX);
|
||||
if (strbuf_init(&buf, PATH_MAX) < 0 ||
|
||||
strbuf_addstr(&buf, alias_string) < 0 ||
|
||||
sq_quote_argv(&buf, (*argv) + 1,
|
||||
PATH_MAX) < 0)
|
||||
die("Failed to allocate memory.");
|
||||
free(alias_string);
|
||||
alias_string = buf.buf;
|
||||
}
|
||||
@@ -533,6 +536,7 @@ int main(int argc, const char **argv)
|
||||
{
|
||||
const char *cmd;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
int value;
|
||||
|
||||
/* libsubcmd init */
|
||||
exec_cmd_init("perf", PREFIX, PERF_EXEC_PATH, EXEC_PATH_ENVIRONMENT);
|
||||
@@ -542,6 +546,9 @@ int main(int argc, const char **argv)
|
||||
page_size = sysconf(_SC_PAGE_SIZE);
|
||||
cacheline_size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
|
||||
|
||||
if (sysctl__read_int("kernel/perf_event_max_stack", &value) == 0)
|
||||
sysctl_perf_event_max_stack = value;
|
||||
|
||||
cmd = extract_argv0_path(argv[0]);
|
||||
if (!cmd)
|
||||
cmd = "perf-help";
|
||||
@@ -549,6 +556,7 @@ int main(int argc, const char **argv)
|
||||
srandom(time(NULL));
|
||||
|
||||
perf_config(perf_default_config, NULL);
|
||||
set_buildid_dir(NULL);
|
||||
|
||||
/* get debugfs/tracefs mount point from /proc/mounts */
|
||||
tracing_path_mount();
|
||||
@@ -572,7 +580,6 @@ int main(int argc, const char **argv)
|
||||
}
|
||||
if (!prefixcmp(cmd, "trace")) {
|
||||
#ifdef HAVE_LIBAUDIT_SUPPORT
|
||||
set_buildid_dir(NULL);
|
||||
setup_path();
|
||||
argv[0] = "trace";
|
||||
return cmd_trace(argc, argv, NULL);
|
||||
@@ -587,7 +594,6 @@ int main(int argc, const char **argv)
|
||||
argc--;
|
||||
handle_options(&argv, &argc, NULL);
|
||||
commit_pager_choice();
|
||||
set_buildid_dir(NULL);
|
||||
|
||||
if (argc > 0) {
|
||||
if (!prefixcmp(argv[0], "--"))
|
||||
|
@@ -52,7 +52,6 @@ struct record_opts {
|
||||
bool sample_weight;
|
||||
bool sample_time;
|
||||
bool sample_time_set;
|
||||
bool callgraph_set;
|
||||
bool period;
|
||||
bool running_time;
|
||||
bool full_auxtrace;
|
||||
|
@@ -34,10 +34,9 @@ import datetime
|
||||
#
|
||||
# ubuntu:
|
||||
#
|
||||
# $ sudo apt-get install postgresql
|
||||
# $ sudo apt-get install postgresql python-pyside.qtsql libqt4-sql-psql
|
||||
# $ sudo su - postgres
|
||||
# $ createuser <your user id here>
|
||||
# Shall the new role be a superuser? (y/n) y
|
||||
# $ createuser -s <your user id here>
|
||||
#
|
||||
# An example of using this script with Intel PT:
|
||||
#
|
||||
@@ -224,11 +223,14 @@ sys.path.append(os.environ['PERF_EXEC_PATH'] + \
|
||||
|
||||
perf_db_export_mode = True
|
||||
perf_db_export_calls = False
|
||||
perf_db_export_callchains = False
|
||||
|
||||
|
||||
def usage():
|
||||
print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>]"
|
||||
print >> sys.stderr, "Usage is: export-to-postgresql.py <database name> [<columns>] [<calls>] [<callchains>]"
|
||||
print >> sys.stderr, "where: columns 'all' or 'branches'"
|
||||
print >> sys.stderr, " calls 'calls' => create calls table"
|
||||
print >> sys.stderr, " calls 'calls' => create calls and call_paths table"
|
||||
print >> sys.stderr, " callchains 'callchains' => create call_paths table"
|
||||
raise Exception("Too few arguments")
|
||||
|
||||
if (len(sys.argv) < 2):
|
||||
@@ -246,9 +248,11 @@ if columns not in ("all", "branches"):
|
||||
|
||||
branches = (columns == "branches")
|
||||
|
||||
if (len(sys.argv) >= 4):
|
||||
if (sys.argv[3] == "calls"):
|
||||
for i in range(3,len(sys.argv)):
|
||||
if (sys.argv[i] == "calls"):
|
||||
perf_db_export_calls = True
|
||||
elif (sys.argv[i] == "callchains"):
|
||||
perf_db_export_callchains = True
|
||||
else:
|
||||
usage()
|
||||
|
||||
@@ -359,14 +363,16 @@ else:
|
||||
'transaction bigint,'
|
||||
'data_src bigint,'
|
||||
'branch_type integer,'
|
||||
'in_tx boolean)')
|
||||
'in_tx boolean,'
|
||||
'call_path_id bigint)')
|
||||
|
||||
if perf_db_export_calls:
|
||||
if perf_db_export_calls or perf_db_export_callchains:
|
||||
do_query(query, 'CREATE TABLE call_paths ('
|
||||
'id bigint NOT NULL,'
|
||||
'parent_id bigint,'
|
||||
'symbol_id bigint,'
|
||||
'ip bigint)')
|
||||
if perf_db_export_calls:
|
||||
do_query(query, 'CREATE TABLE calls ('
|
||||
'id bigint NOT NULL,'
|
||||
'thread_id bigint,'
|
||||
@@ -428,7 +434,7 @@ do_query(query, 'CREATE VIEW comm_threads_view AS '
|
||||
'(SELECT tid FROM threads WHERE id = thread_id) AS tid'
|
||||
' FROM comm_threads')
|
||||
|
||||
if perf_db_export_calls:
|
||||
if perf_db_export_calls or perf_db_export_callchains:
|
||||
do_query(query, 'CREATE VIEW call_paths_view AS '
|
||||
'SELECT '
|
||||
'c.id,'
|
||||
@@ -444,6 +450,7 @@ if perf_db_export_calls:
|
||||
'(SELECT dso_id FROM symbols WHERE id = p.symbol_id) AS parent_dso_id,'
|
||||
'(SELECT dso FROM symbols_view WHERE id = p.symbol_id) AS parent_dso_short_name'
|
||||
' FROM call_paths c INNER JOIN call_paths p ON p.id = c.parent_id')
|
||||
if perf_db_export_calls:
|
||||
do_query(query, 'CREATE VIEW calls_view AS '
|
||||
'SELECT '
|
||||
'calls.id,'
|
||||
@@ -541,8 +548,9 @@ dso_file = open_output_file("dso_table.bin")
|
||||
symbol_file = open_output_file("symbol_table.bin")
|
||||
branch_type_file = open_output_file("branch_type_table.bin")
|
||||
sample_file = open_output_file("sample_table.bin")
|
||||
if perf_db_export_calls:
|
||||
if perf_db_export_calls or perf_db_export_callchains:
|
||||
call_path_file = open_output_file("call_path_table.bin")
|
||||
if perf_db_export_calls:
|
||||
call_file = open_output_file("call_table.bin")
|
||||
|
||||
def trace_begin():
|
||||
@@ -554,8 +562,8 @@ def trace_begin():
|
||||
comm_table(0, "unknown")
|
||||
dso_table(0, 0, "unknown", "unknown", "")
|
||||
symbol_table(0, 0, 0, 0, 0, "unknown")
|
||||
sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
if perf_db_export_calls:
|
||||
sample_table(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
||||
if perf_db_export_calls or perf_db_export_callchains:
|
||||
call_path_table(0, 0, 0, 0)
|
||||
|
||||
unhandled_count = 0
|
||||
@@ -571,8 +579,9 @@ def trace_end():
|
||||
copy_output_file(symbol_file, "symbols")
|
||||
copy_output_file(branch_type_file, "branch_types")
|
||||
copy_output_file(sample_file, "samples")
|
||||
if perf_db_export_calls:
|
||||
if perf_db_export_calls or perf_db_export_callchains:
|
||||
copy_output_file(call_path_file, "call_paths")
|
||||
if perf_db_export_calls:
|
||||
copy_output_file(call_file, "calls")
|
||||
|
||||
print datetime.datetime.today(), "Removing intermediate files..."
|
||||
@@ -585,8 +594,9 @@ def trace_end():
|
||||
remove_output_file(symbol_file)
|
||||
remove_output_file(branch_type_file)
|
||||
remove_output_file(sample_file)
|
||||
if perf_db_export_calls:
|
||||
if perf_db_export_calls or perf_db_export_callchains:
|
||||
remove_output_file(call_path_file)
|
||||
if perf_db_export_calls:
|
||||
remove_output_file(call_file)
|
||||
os.rmdir(output_dir_name)
|
||||
print datetime.datetime.today(), "Adding primary keys"
|
||||
@@ -599,8 +609,9 @@ def trace_end():
|
||||
do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)')
|
||||
do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)')
|
||||
do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)')
|
||||
if perf_db_export_calls:
|
||||
if perf_db_export_calls or perf_db_export_callchains:
|
||||
do_query(query, 'ALTER TABLE call_paths ADD PRIMARY KEY (id)')
|
||||
if perf_db_export_calls:
|
||||
do_query(query, 'ALTER TABLE calls ADD PRIMARY KEY (id)')
|
||||
|
||||
print datetime.datetime.today(), "Adding foreign keys"
|
||||
@@ -623,10 +634,11 @@ def trace_end():
|
||||
'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id),'
|
||||
'ADD CONSTRAINT todsofk FOREIGN KEY (to_dso_id) REFERENCES dsos (id),'
|
||||
'ADD CONSTRAINT tosymbolfk FOREIGN KEY (to_symbol_id) REFERENCES symbols (id)')
|
||||
if perf_db_export_calls:
|
||||
if perf_db_export_calls or perf_db_export_callchains:
|
||||
do_query(query, 'ALTER TABLE call_paths '
|
||||
'ADD CONSTRAINT parentfk FOREIGN KEY (parent_id) REFERENCES call_paths (id),'
|
||||
'ADD CONSTRAINT symbolfk FOREIGN KEY (symbol_id) REFERENCES symbols (id)')
|
||||
if perf_db_export_calls:
|
||||
do_query(query, 'ALTER TABLE calls '
|
||||
'ADD CONSTRAINT threadfk FOREIGN KEY (thread_id) REFERENCES threads (id),'
|
||||
'ADD CONSTRAINT commfk FOREIGN KEY (comm_id) REFERENCES comms (id),'
|
||||
@@ -694,11 +706,11 @@ def branch_type_table(branch_type, name, *x):
|
||||
value = struct.pack(fmt, 2, 4, branch_type, n, name)
|
||||
branch_type_file.write(value)
|
||||
|
||||
def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, *x):
|
||||
def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, call_path_id, *x):
|
||||
if branches:
|
||||
value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiB", 17, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx)
|
||||
value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiBiq", 18, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx, 8, call_path_id)
|
||||
else:
|
||||
value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiB", 21, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx)
|
||||
value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiBiq", 22, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx, 8, call_path_id)
|
||||
sample_file.write(value)
|
||||
|
||||
def call_path_table(cp_id, parent_id, symbol_id, ip, *x):
|
||||
|
@@ -37,6 +37,8 @@ perf-y += topology.o
|
||||
perf-y += cpumap.o
|
||||
perf-y += stat.o
|
||||
perf-y += event_update.o
|
||||
perf-y += event-times.o
|
||||
perf-y += backward-ring-buffer.o
|
||||
|
||||
$(OUTPUT)tests/llvm-src-base.c: tests/bpf-script-example.c tests/Build
|
||||
$(call rule_mkdir)
|
||||
|
151
tools/perf/tests/backward-ring-buffer.c
Normal file
151
tools/perf/tests/backward-ring-buffer.c
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Test backward bit in event attribute, read ring buffer from end to
|
||||
* beginning
|
||||
*/
|
||||
|
||||
#include <perf.h>
|
||||
#include <evlist.h>
|
||||
#include <sys/prctl.h>
|
||||
#include "tests.h"
|
||||
#include "debug.h"
|
||||
|
||||
#define NR_ITERS 111
|
||||
|
||||
static void testcase(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NR_ITERS; i++) {
|
||||
char proc_name[10];
|
||||
|
||||
snprintf(proc_name, sizeof(proc_name), "p:%d\n", i);
|
||||
prctl(PR_SET_NAME, proc_name);
|
||||
}
|
||||
}
|
||||
|
||||
static int count_samples(struct perf_evlist *evlist, int *sample_count,
|
||||
int *comm_count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < evlist->nr_mmaps; i++) {
|
||||
union perf_event *event;
|
||||
|
||||
perf_evlist__mmap_read_catchup(evlist, i);
|
||||
while ((event = perf_evlist__mmap_read_backward(evlist, i)) != NULL) {
|
||||
const u32 type = event->header.type;
|
||||
|
||||
switch (type) {
|
||||
case PERF_RECORD_SAMPLE:
|
||||
(*sample_count)++;
|
||||
break;
|
||||
case PERF_RECORD_COMM:
|
||||
(*comm_count)++;
|
||||
break;
|
||||
default:
|
||||
pr_err("Unexpected record of type %d\n", type);
|
||||
return TEST_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return TEST_OK;
|
||||
}
|
||||
|
||||
static int do_test(struct perf_evlist *evlist, int mmap_pages,
|
||||
int *sample_count, int *comm_count)
|
||||
{
|
||||
int err;
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
|
||||
err = perf_evlist__mmap(evlist, mmap_pages, true);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__mmap: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
return TEST_FAIL;
|
||||
}
|
||||
|
||||
perf_evlist__enable(evlist);
|
||||
testcase();
|
||||
perf_evlist__disable(evlist);
|
||||
|
||||
err = count_samples(evlist, sample_count, comm_count);
|
||||
perf_evlist__munmap(evlist);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int test__backward_ring_buffer(int subtest __maybe_unused)
|
||||
{
|
||||
int ret = TEST_SKIP, err, sample_count = 0, comm_count = 0;
|
||||
char pid[16], sbuf[STRERR_BUFSIZE];
|
||||
struct perf_evlist *evlist;
|
||||
struct perf_evsel *evsel __maybe_unused;
|
||||
struct parse_events_error parse_error;
|
||||
struct record_opts opts = {
|
||||
.target = {
|
||||
.uid = UINT_MAX,
|
||||
.uses_mmap = true,
|
||||
},
|
||||
.freq = 0,
|
||||
.mmap_pages = 256,
|
||||
.default_interval = 1,
|
||||
};
|
||||
|
||||
snprintf(pid, sizeof(pid), "%d", getpid());
|
||||
pid[sizeof(pid) - 1] = '\0';
|
||||
opts.target.tid = opts.target.pid = pid;
|
||||
|
||||
evlist = perf_evlist__new();
|
||||
if (!evlist) {
|
||||
pr_debug("No ehough memory to create evlist\n");
|
||||
return TEST_FAIL;
|
||||
}
|
||||
|
||||
err = perf_evlist__create_maps(evlist, &opts.target);
|
||||
if (err < 0) {
|
||||
pr_debug("Not enough memory to create thread/cpu maps\n");
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
bzero(&parse_error, sizeof(parse_error));
|
||||
err = parse_events(evlist, "syscalls:sys_enter_prctl", &parse_error);
|
||||
if (err) {
|
||||
pr_debug("Failed to parse tracepoint event, try use root\n");
|
||||
ret = TEST_SKIP;
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
perf_evlist__config(evlist, &opts, NULL);
|
||||
|
||||
/* Set backward bit, ring buffer should be writing from end */
|
||||
evlist__for_each(evlist, evsel)
|
||||
evsel->attr.write_backward = 1;
|
||||
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__open: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
ret = TEST_FAIL;
|
||||
err = do_test(evlist, opts.mmap_pages, &sample_count,
|
||||
&comm_count);
|
||||
if (err != TEST_OK)
|
||||
goto out_delete_evlist;
|
||||
|
||||
if ((sample_count != NR_ITERS) || (comm_count != NR_ITERS)) {
|
||||
pr_err("Unexpected counter: sample_count=%d, comm_count=%d\n",
|
||||
sample_count, comm_count);
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
err = do_test(evlist, 1, &sample_count, &comm_count);
|
||||
if (err != TEST_OK)
|
||||
goto out_delete_evlist;
|
||||
|
||||
ret = TEST_OK;
|
||||
out_delete_evlist:
|
||||
perf_evlist__delete(evlist);
|
||||
return ret;
|
||||
}
|
@@ -138,7 +138,7 @@ static int do_test(struct bpf_object *obj, int (*func)(void),
|
||||
perf_evlist__splice_list_tail(evlist, &parse_evlist.list);
|
||||
evlist->nr_groups = parse_evlist.nr_groups;
|
||||
|
||||
perf_evlist__config(evlist, &opts);
|
||||
perf_evlist__config(evlist, &opts, NULL);
|
||||
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
|
@@ -203,6 +203,14 @@ static struct test generic_tests[] = {
|
||||
.desc = "Test attr update synthesize",
|
||||
.func = test__event_update,
|
||||
},
|
||||
{
|
||||
.desc = "Test events times",
|
||||
.func = test__event_times,
|
||||
},
|
||||
{
|
||||
.desc = "Test backward reading from ring buffer",
|
||||
.func = test__backward_ring_buffer,
|
||||
},
|
||||
{
|
||||
.func = NULL,
|
||||
},
|
||||
|
@@ -532,7 +532,7 @@ static int do_test_code_reading(bool try_kcore)
|
||||
goto out_put;
|
||||
}
|
||||
|
||||
perf_evlist__config(evlist, &opts);
|
||||
perf_evlist__config(evlist, &opts, NULL);
|
||||
|
||||
evsel = perf_evlist__first(evlist);
|
||||
|
||||
|
@@ -202,7 +202,7 @@ static int dsos__create(int cnt, int size)
|
||||
{
|
||||
int i;
|
||||
|
||||
dsos = malloc(sizeof(dsos) * cnt);
|
||||
dsos = malloc(sizeof(*dsos) * cnt);
|
||||
TEST_ASSERT_VAL("failed to alloc dsos array", dsos);
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
|
236
tools/perf/tests/event-times.c
Normal file
236
tools/perf/tests/event-times.c
Normal file
@@ -0,0 +1,236 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <string.h>
|
||||
#include "tests.h"
|
||||
#include "evlist.h"
|
||||
#include "evsel.h"
|
||||
#include "util.h"
|
||||
#include "debug.h"
|
||||
#include "thread_map.h"
|
||||
#include "target.h"
|
||||
|
||||
static int attach__enable_on_exec(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evlist__last(evlist);
|
||||
struct target target = {
|
||||
.uid = UINT_MAX,
|
||||
};
|
||||
const char *argv[] = { "true", NULL, };
|
||||
char sbuf[STRERR_BUFSIZE];
|
||||
int err;
|
||||
|
||||
pr_debug("attaching to spawned child, enable on exec\n");
|
||||
|
||||
err = perf_evlist__create_maps(evlist, &target);
|
||||
if (err < 0) {
|
||||
pr_debug("Not enough memory to create thread/cpu maps\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = perf_evlist__prepare_workload(evlist, &target, argv, false, NULL);
|
||||
if (err < 0) {
|
||||
pr_debug("Couldn't run the workload!\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
evsel->attr.enable_on_exec = 1;
|
||||
|
||||
err = perf_evlist__open(evlist);
|
||||
if (err < 0) {
|
||||
pr_debug("perf_evlist__open: %s\n",
|
||||
strerror_r(errno, sbuf, sizeof(sbuf)));
|
||||
return err;
|
||||
}
|
||||
|
||||
return perf_evlist__start_workload(evlist) == 1 ? TEST_OK : TEST_FAIL;
|
||||
}
|
||||
|
||||
static int detach__enable_on_exec(struct perf_evlist *evlist)
|
||||
{
|
||||
waitpid(evlist->workload.pid, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int attach__current_disabled(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evlist__last(evlist);
|
||||
struct thread_map *threads;
|
||||
int err;
|
||||
|
||||
pr_debug("attaching to current thread as disabled\n");
|
||||
|
||||
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
if (threads == NULL) {
|
||||
pr_debug("thread_map__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
evsel->attr.disabled = 1;
|
||||
|
||||
err = perf_evsel__open_per_thread(evsel, threads);
|
||||
if (err) {
|
||||
pr_debug("Failed to open event cpu-clock:u\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
thread_map__put(threads);
|
||||
return perf_evsel__enable(evsel) == 0 ? TEST_OK : TEST_FAIL;
|
||||
}
|
||||
|
||||
static int attach__current_enabled(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evlist__last(evlist);
|
||||
struct thread_map *threads;
|
||||
int err;
|
||||
|
||||
pr_debug("attaching to current thread as enabled\n");
|
||||
|
||||
threads = thread_map__new(-1, getpid(), UINT_MAX);
|
||||
if (threads == NULL) {
|
||||
pr_debug("failed to call thread_map__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = perf_evsel__open_per_thread(evsel, threads);
|
||||
|
||||
thread_map__put(threads);
|
||||
return err == 0 ? TEST_OK : TEST_FAIL;
|
||||
}
|
||||
|
||||
static int detach__disable(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evlist__last(evlist);
|
||||
|
||||
return perf_evsel__enable(evsel);
|
||||
}
|
||||
|
||||
static int attach__cpu_disabled(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evlist__last(evlist);
|
||||
struct cpu_map *cpus;
|
||||
int err;
|
||||
|
||||
pr_debug("attaching to CPU 0 as enabled\n");
|
||||
|
||||
cpus = cpu_map__new("0");
|
||||
if (cpus == NULL) {
|
||||
pr_debug("failed to call cpu_map__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
evsel->attr.disabled = 1;
|
||||
|
||||
err = perf_evsel__open_per_cpu(evsel, cpus);
|
||||
if (err) {
|
||||
if (err == -EACCES)
|
||||
return TEST_SKIP;
|
||||
|
||||
pr_debug("Failed to open event cpu-clock:u\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
cpu_map__put(cpus);
|
||||
return perf_evsel__enable(evsel);
|
||||
}
|
||||
|
||||
static int attach__cpu_enabled(struct perf_evlist *evlist)
|
||||
{
|
||||
struct perf_evsel *evsel = perf_evlist__last(evlist);
|
||||
struct cpu_map *cpus;
|
||||
int err;
|
||||
|
||||
pr_debug("attaching to CPU 0 as enabled\n");
|
||||
|
||||
cpus = cpu_map__new("0");
|
||||
if (cpus == NULL) {
|
||||
pr_debug("failed to call cpu_map__new\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = perf_evsel__open_per_cpu(evsel, cpus);
|
||||
if (err == -EACCES)
|
||||
return TEST_SKIP;
|
||||
|
||||
cpu_map__put(cpus);
|
||||
return err ? TEST_FAIL : TEST_OK;
|
||||
}
|
||||
|
||||
static int test_times(int (attach)(struct perf_evlist *),
|
||||
int (detach)(struct perf_evlist *))
|
||||
{
|
||||
struct perf_counts_values count;
|
||||
struct perf_evlist *evlist = NULL;
|
||||
struct perf_evsel *evsel;
|
||||
int err = -1, i;
|
||||
|
||||
evlist = perf_evlist__new();
|
||||
if (!evlist) {
|
||||
pr_debug("failed to create event list\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
err = parse_events(evlist, "cpu-clock:u", NULL);
|
||||
if (err) {
|
||||
pr_debug("failed to parse event cpu-clock:u\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
evsel = perf_evlist__last(evlist);
|
||||
evsel->attr.read_format |=
|
||||
PERF_FORMAT_TOTAL_TIME_ENABLED |
|
||||
PERF_FORMAT_TOTAL_TIME_RUNNING;
|
||||
|
||||
err = attach(evlist);
|
||||
if (err == TEST_SKIP) {
|
||||
pr_debug(" SKIP : not enough rights\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
TEST_ASSERT_VAL("failed to attach", !err);
|
||||
|
||||
for (i = 0; i < 100000000; i++) { }
|
||||
|
||||
TEST_ASSERT_VAL("failed to detach", !detach(evlist));
|
||||
|
||||
perf_evsel__read(evsel, 0, 0, &count);
|
||||
|
||||
err = !(count.ena == count.run);
|
||||
|
||||
pr_debug(" %s: ena %" PRIu64", run %" PRIu64"\n",
|
||||
!err ? "OK " : "FAILED",
|
||||
count.ena, count.run);
|
||||
|
||||
out_err:
|
||||
if (evlist)
|
||||
perf_evlist__delete(evlist);
|
||||
return !err ? TEST_OK : TEST_FAIL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This test creates software event 'cpu-clock'
|
||||
* attaches it in several ways (explained below)
|
||||
* and checks that enabled and running times
|
||||
* match.
|
||||
*/
|
||||
int test__event_times(int subtest __maybe_unused)
|
||||
{
|
||||
int err, ret = 0;
|
||||
|
||||
#define _T(attach, detach) \
|
||||
err = test_times(attach, detach); \
|
||||
if (err && (ret == TEST_OK || ret == TEST_SKIP)) \
|
||||
ret = err;
|
||||
|
||||
/* attach on newly spawned process after exec */
|
||||
_T(attach__enable_on_exec, detach__enable_on_exec)
|
||||
/* attach on current process as enabled */
|
||||
_T(attach__current_enabled, detach__disable)
|
||||
/* attach on current process as disabled */
|
||||
_T(attach__current_disabled, detach__disable)
|
||||
/* attach on cpu as disabled */
|
||||
_T(attach__cpu_disabled, detach__disable)
|
||||
/* attach on cpu as enabled */
|
||||
_T(attach__cpu_enabled, detach__disable)
|
||||
|
||||
#undef _T
|
||||
return ret;
|
||||
}
|
@@ -30,7 +30,7 @@ static int process_event_scale(struct perf_tool *tool __maybe_unused,
|
||||
|
||||
TEST_ASSERT_VAL("wrong id", ev->id == 123);
|
||||
TEST_ASSERT_VAL("wrong id", ev->type == PERF_EVENT_UPDATE__SCALE);
|
||||
TEST_ASSERT_VAL("wrong scale", ev_data->scale = 0.123);
|
||||
TEST_ASSERT_VAL("wrong scale", ev_data->scale == 0.123);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -161,7 +161,7 @@ void print_hists_in(struct hists *hists)
|
||||
struct rb_root *root;
|
||||
struct rb_node *node;
|
||||
|
||||
if (sort__need_collapse)
|
||||
if (hists__has(hists, need_collapse))
|
||||
root = &hists->entries_collapsed;
|
||||
else
|
||||
root = hists->entries_in;
|
||||
|
@@ -101,7 +101,7 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
|
||||
if (machine__resolve(machine, &al, &sample) < 0)
|
||||
goto out;
|
||||
|
||||
if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH,
|
||||
if (hist_entry_iter__add(&iter, &al, sysctl_perf_event_max_stack,
|
||||
NULL) < 0) {
|
||||
addr_location__put(&al);
|
||||
goto out;
|
||||
@@ -126,7 +126,7 @@ static void del_hist_entries(struct hists *hists)
|
||||
struct rb_root *root_out;
|
||||
struct rb_node *node;
|
||||
|
||||
if (sort__need_collapse)
|
||||
if (hists__has(hists, need_collapse))
|
||||
root_in = &hists->entries_collapsed;
|
||||
else
|
||||
root_in = hists->entries_in;
|
||||
|
@@ -81,7 +81,7 @@ static int add_hist_entries(struct perf_evlist *evlist,
|
||||
|
||||
al.socket = fake_samples[i].socket;
|
||||
if (hist_entry_iter__add(&iter, &al,
|
||||
PERF_MAX_STACK_DEPTH, NULL) < 0) {
|
||||
sysctl_perf_event_max_stack, NULL) < 0) {
|
||||
addr_location__put(&al);
|
||||
goto out;
|
||||
}
|
||||
|
@@ -145,7 +145,7 @@ static int __validate_match(struct hists *hists)
|
||||
/*
|
||||
* Only entries from fake_common_samples should have a pair.
|
||||
*/
|
||||
if (sort__need_collapse)
|
||||
if (hists__has(hists, need_collapse))
|
||||
root = &hists->entries_collapsed;
|
||||
else
|
||||
root = hists->entries_in;
|
||||
@@ -197,7 +197,7 @@ static int __validate_link(struct hists *hists, int idx)
|
||||
* and some entries will have no pair. However every entry
|
||||
* in other hists should have (dummy) pair.
|
||||
*/
|
||||
if (sort__need_collapse)
|
||||
if (hists__has(hists, need_collapse))
|
||||
root = &hists->entries_collapsed;
|
||||
else
|
||||
root = hists->entries_in;
|
||||
|
@@ -67,7 +67,7 @@ static int add_hist_entries(struct hists *hists, struct machine *machine)
|
||||
if (machine__resolve(machine, &al, &sample) < 0)
|
||||
goto out;
|
||||
|
||||
if (hist_entry_iter__add(&iter, &al, PERF_MAX_STACK_DEPTH,
|
||||
if (hist_entry_iter__add(&iter, &al, sysctl_perf_event_max_stack,
|
||||
NULL) < 0) {
|
||||
addr_location__put(&al);
|
||||
goto out;
|
||||
@@ -92,7 +92,7 @@ static void del_hist_entries(struct hists *hists)
|
||||
struct rb_root *root_out;
|
||||
struct rb_node *node;
|
||||
|
||||
if (sort__need_collapse)
|
||||
if (hists__has(hists, need_collapse))
|
||||
root_in = &hists->entries_collapsed;
|
||||
else
|
||||
root_in = hists->entries_in;
|
||||
|
@@ -80,7 +80,7 @@ int test__keep_tracking(int subtest __maybe_unused)
|
||||
CHECK__(parse_events(evlist, "dummy:u", NULL));
|
||||
CHECK__(parse_events(evlist, "cycles:u", NULL));
|
||||
|
||||
perf_evlist__config(evlist, &opts);
|
||||
perf_evlist__config(evlist, &opts, NULL);
|
||||
|
||||
evsel = perf_evlist__first(evlist);
|
||||
|
||||
|
@@ -44,7 +44,7 @@ int test__syscall_openat_tp_fields(int subtest __maybe_unused)
|
||||
goto out_delete_evlist;
|
||||
}
|
||||
|
||||
perf_evsel__config(evsel, &opts);
|
||||
perf_evsel__config(evsel, &opts, NULL);
|
||||
|
||||
thread_map__set_pid(evlist->threads, 0, getpid());
|
||||
|
||||
|
@@ -99,7 +99,7 @@ int test__PERF_RECORD(int subtest __maybe_unused)
|
||||
perf_evsel__set_sample_bit(evsel, CPU);
|
||||
perf_evsel__set_sample_bit(evsel, TID);
|
||||
perf_evsel__set_sample_bit(evsel, TIME);
|
||||
perf_evlist__config(evlist, &opts);
|
||||
perf_evlist__config(evlist, &opts, NULL);
|
||||
|
||||
err = sched__get_first_possible_cpu(evlist->workload.pid, &cpu_mask);
|
||||
if (err < 0) {
|
||||
|
@@ -417,7 +417,7 @@ int test__switch_tracking(int subtest __maybe_unused)
|
||||
perf_evsel__set_sample_bit(tracking_evsel, TIME);
|
||||
|
||||
/* Config events */
|
||||
perf_evlist__config(evlist, &opts);
|
||||
perf_evlist__config(evlist, &opts, NULL);
|
||||
|
||||
/* Check moved event is still at the front */
|
||||
if (cycles_evsel != perf_evlist__first(evlist)) {
|
||||
|
@@ -85,6 +85,8 @@ int test__synthesize_stat_config(int subtest);
|
||||
int test__synthesize_stat(int subtest);
|
||||
int test__synthesize_stat_round(int subtest);
|
||||
int test__event_update(int subtest);
|
||||
int test__event_times(int subtest);
|
||||
int test__backward_ring_buffer(int subtest);
|
||||
|
||||
#if defined(__arm__) || defined(__aarch64__)
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
|
@@ -54,8 +54,14 @@ int test__vmlinux_matches_kallsyms(int subtest __maybe_unused)
|
||||
* Step 3:
|
||||
*
|
||||
* Load and split /proc/kallsyms into multiple maps, one per module.
|
||||
* Do not use kcore, as this test was designed before kcore support
|
||||
* and has parts that only make sense if using the non-kcore code.
|
||||
* XXX: extend it to stress the kcorre code as well, hint: the list
|
||||
* of modules extracted from /proc/kcore, in its current form, can't
|
||||
* be compacted against the list of modules found in the "vmlinux"
|
||||
* code and with the one got from /proc/modules from the "kallsyms" code.
|
||||
*/
|
||||
if (machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, NULL) <= 0) {
|
||||
if (__machine__load_kallsyms(&kallsyms, "/proc/kallsyms", type, true, NULL) <= 0) {
|
||||
pr_debug("dso__load_kallsyms ");
|
||||
goto out;
|
||||
}
|
||||
@@ -157,6 +163,9 @@ next_pair:
|
||||
|
||||
pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n",
|
||||
mem_start, sym->name, pair->name);
|
||||
} else {
|
||||
pr_debug("%#" PRIx64 ": diff name v: %s k: %s\n",
|
||||
mem_start, sym->name, first_pair->name);
|
||||
}
|
||||
}
|
||||
} else
|
||||
|
38
tools/perf/trace/beauty/eventfd.c
Normal file
38
tools/perf/trace/beauty/eventfd.c
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
#ifndef EFD_SEMAPHORE
|
||||
#define EFD_SEMAPHORE 1
|
||||
#endif
|
||||
|
||||
#ifndef EFD_NONBLOCK
|
||||
#define EFD_NONBLOCK 00004000
|
||||
#endif
|
||||
|
||||
#ifndef EFD_CLOEXEC
|
||||
#define EFD_CLOEXEC 02000000
|
||||
#endif
|
||||
|
||||
static size_t syscall_arg__scnprintf_eventfd_flags(char *bf, size_t size, struct syscall_arg *arg)
|
||||
{
|
||||
int printed = 0, flags = arg->val;
|
||||
|
||||
if (flags == 0)
|
||||
return scnprintf(bf, size, "NONE");
|
||||
#define P_FLAG(n) \
|
||||
if (flags & EFD_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
|
||||
flags &= ~EFD_##n; \
|
||||
}
|
||||
|
||||
P_FLAG(SEMAPHORE);
|
||||
P_FLAG(CLOEXEC);
|
||||
P_FLAG(NONBLOCK);
|
||||
#undef P_FLAG
|
||||
|
||||
if (flags)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_EFD_FLAGS syscall_arg__scnprintf_eventfd_flags
|
31
tools/perf/trace/beauty/flock.c
Normal file
31
tools/perf/trace/beauty/flock.c
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
static size_t syscall_arg__scnprintf_flock(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
int printed = 0, op = arg->val;
|
||||
|
||||
if (op == 0)
|
||||
return scnprintf(bf, size, "NONE");
|
||||
#define P_CMD(cmd) \
|
||||
if ((op & LOCK_##cmd) == LOCK_##cmd) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #cmd); \
|
||||
op &= ~LOCK_##cmd; \
|
||||
}
|
||||
|
||||
P_CMD(SH);
|
||||
P_CMD(EX);
|
||||
P_CMD(NB);
|
||||
P_CMD(UN);
|
||||
P_CMD(MAND);
|
||||
P_CMD(RW);
|
||||
P_CMD(READ);
|
||||
P_CMD(WRITE);
|
||||
#undef P_OP
|
||||
|
||||
if (op)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", op);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_FLOCK syscall_arg__scnprintf_flock
|
44
tools/perf/trace/beauty/futex_op.c
Normal file
44
tools/perf/trace/beauty/futex_op.c
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <linux/futex.h>
|
||||
|
||||
static size_t syscall_arg__scnprintf_futex_op(char *bf, size_t size, struct syscall_arg *arg)
|
||||
{
|
||||
enum syscall_futex_args {
|
||||
SCF_UADDR = (1 << 0),
|
||||
SCF_OP = (1 << 1),
|
||||
SCF_VAL = (1 << 2),
|
||||
SCF_TIMEOUT = (1 << 3),
|
||||
SCF_UADDR2 = (1 << 4),
|
||||
SCF_VAL3 = (1 << 5),
|
||||
};
|
||||
int op = arg->val;
|
||||
int cmd = op & FUTEX_CMD_MASK;
|
||||
size_t printed = 0;
|
||||
|
||||
switch (cmd) {
|
||||
#define P_FUTEX_OP(n) case FUTEX_##n: printed = scnprintf(bf, size, #n);
|
||||
P_FUTEX_OP(WAIT); arg->mask |= SCF_VAL3|SCF_UADDR2; break;
|
||||
P_FUTEX_OP(WAKE); arg->mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break;
|
||||
P_FUTEX_OP(FD); arg->mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break;
|
||||
P_FUTEX_OP(REQUEUE); arg->mask |= SCF_VAL3|SCF_TIMEOUT; break;
|
||||
P_FUTEX_OP(CMP_REQUEUE); arg->mask |= SCF_TIMEOUT; break;
|
||||
P_FUTEX_OP(CMP_REQUEUE_PI); arg->mask |= SCF_TIMEOUT; break;
|
||||
P_FUTEX_OP(WAKE_OP); break;
|
||||
P_FUTEX_OP(LOCK_PI); arg->mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break;
|
||||
P_FUTEX_OP(UNLOCK_PI); arg->mask |= SCF_VAL3|SCF_UADDR2|SCF_TIMEOUT; break;
|
||||
P_FUTEX_OP(TRYLOCK_PI); arg->mask |= SCF_VAL3|SCF_UADDR2; break;
|
||||
P_FUTEX_OP(WAIT_BITSET); arg->mask |= SCF_UADDR2; break;
|
||||
P_FUTEX_OP(WAKE_BITSET); arg->mask |= SCF_UADDR2; break;
|
||||
P_FUTEX_OP(WAIT_REQUEUE_PI); break;
|
||||
default: printed = scnprintf(bf, size, "%#x", cmd); break;
|
||||
}
|
||||
|
||||
if (op & FUTEX_PRIVATE_FLAG)
|
||||
printed += scnprintf(bf + printed, size - printed, "|PRIV");
|
||||
|
||||
if (op & FUTEX_CLOCK_REALTIME)
|
||||
printed += scnprintf(bf + printed, size - printed, "|CLKRT");
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_FUTEX_OP syscall_arg__scnprintf_futex_op
|
158
tools/perf/trace/beauty/mmap.c
Normal file
158
tools/perf/trace/beauty/mmap.c
Normal file
@@ -0,0 +1,158 @@
|
||||
#include <sys/mman.h>
|
||||
|
||||
static size_t syscall_arg__scnprintf_mmap_prot(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
int printed = 0, prot = arg->val;
|
||||
|
||||
if (prot == PROT_NONE)
|
||||
return scnprintf(bf, size, "NONE");
|
||||
#define P_MMAP_PROT(n) \
|
||||
if (prot & PROT_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
|
||||
prot &= ~PROT_##n; \
|
||||
}
|
||||
|
||||
P_MMAP_PROT(EXEC);
|
||||
P_MMAP_PROT(READ);
|
||||
P_MMAP_PROT(WRITE);
|
||||
#ifdef PROT_SEM
|
||||
P_MMAP_PROT(SEM);
|
||||
#endif
|
||||
P_MMAP_PROT(GROWSDOWN);
|
||||
P_MMAP_PROT(GROWSUP);
|
||||
#undef P_MMAP_PROT
|
||||
|
||||
if (prot)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", prot);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_MMAP_PROT syscall_arg__scnprintf_mmap_prot
|
||||
|
||||
#ifndef MAP_STACK
|
||||
# define MAP_STACK 0x20000
|
||||
#endif
|
||||
|
||||
static size_t syscall_arg__scnprintf_mmap_flags(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
int printed = 0, flags = arg->val;
|
||||
|
||||
#define P_MMAP_FLAG(n) \
|
||||
if (flags & MAP_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
|
||||
flags &= ~MAP_##n; \
|
||||
}
|
||||
|
||||
P_MMAP_FLAG(SHARED);
|
||||
P_MMAP_FLAG(PRIVATE);
|
||||
#ifdef MAP_32BIT
|
||||
P_MMAP_FLAG(32BIT);
|
||||
#endif
|
||||
P_MMAP_FLAG(ANONYMOUS);
|
||||
P_MMAP_FLAG(DENYWRITE);
|
||||
P_MMAP_FLAG(EXECUTABLE);
|
||||
P_MMAP_FLAG(FILE);
|
||||
P_MMAP_FLAG(FIXED);
|
||||
P_MMAP_FLAG(GROWSDOWN);
|
||||
#ifdef MAP_HUGETLB
|
||||
P_MMAP_FLAG(HUGETLB);
|
||||
#endif
|
||||
P_MMAP_FLAG(LOCKED);
|
||||
P_MMAP_FLAG(NONBLOCK);
|
||||
P_MMAP_FLAG(NORESERVE);
|
||||
P_MMAP_FLAG(POPULATE);
|
||||
P_MMAP_FLAG(STACK);
|
||||
#ifdef MAP_UNINITIALIZED
|
||||
P_MMAP_FLAG(UNINITIALIZED);
|
||||
#endif
|
||||
#undef P_MMAP_FLAG
|
||||
|
||||
if (flags)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_MMAP_FLAGS syscall_arg__scnprintf_mmap_flags
|
||||
|
||||
static size_t syscall_arg__scnprintf_mremap_flags(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
int printed = 0, flags = arg->val;
|
||||
|
||||
#define P_MREMAP_FLAG(n) \
|
||||
if (flags & MREMAP_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
|
||||
flags &= ~MREMAP_##n; \
|
||||
}
|
||||
|
||||
P_MREMAP_FLAG(MAYMOVE);
|
||||
#ifdef MREMAP_FIXED
|
||||
P_MREMAP_FLAG(FIXED);
|
||||
#endif
|
||||
#undef P_MREMAP_FLAG
|
||||
|
||||
if (flags)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_MREMAP_FLAGS syscall_arg__scnprintf_mremap_flags
|
||||
|
||||
#ifndef MADV_HWPOISON
|
||||
#define MADV_HWPOISON 100
|
||||
#endif
|
||||
|
||||
#ifndef MADV_MERGEABLE
|
||||
#define MADV_MERGEABLE 12
|
||||
#endif
|
||||
|
||||
#ifndef MADV_UNMERGEABLE
|
||||
#define MADV_UNMERGEABLE 13
|
||||
#endif
|
||||
|
||||
static size_t syscall_arg__scnprintf_madvise_behavior(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
int behavior = arg->val;
|
||||
|
||||
switch (behavior) {
|
||||
#define P_MADV_BHV(n) case MADV_##n: return scnprintf(bf, size, #n)
|
||||
P_MADV_BHV(NORMAL);
|
||||
P_MADV_BHV(RANDOM);
|
||||
P_MADV_BHV(SEQUENTIAL);
|
||||
P_MADV_BHV(WILLNEED);
|
||||
P_MADV_BHV(DONTNEED);
|
||||
P_MADV_BHV(REMOVE);
|
||||
P_MADV_BHV(DONTFORK);
|
||||
P_MADV_BHV(DOFORK);
|
||||
P_MADV_BHV(HWPOISON);
|
||||
#ifdef MADV_SOFT_OFFLINE
|
||||
P_MADV_BHV(SOFT_OFFLINE);
|
||||
#endif
|
||||
P_MADV_BHV(MERGEABLE);
|
||||
P_MADV_BHV(UNMERGEABLE);
|
||||
#ifdef MADV_HUGEPAGE
|
||||
P_MADV_BHV(HUGEPAGE);
|
||||
#endif
|
||||
#ifdef MADV_NOHUGEPAGE
|
||||
P_MADV_BHV(NOHUGEPAGE);
|
||||
#endif
|
||||
#ifdef MADV_DONTDUMP
|
||||
P_MADV_BHV(DONTDUMP);
|
||||
#endif
|
||||
#ifdef MADV_DODUMP
|
||||
P_MADV_BHV(DODUMP);
|
||||
#endif
|
||||
#undef P_MADV_PHV
|
||||
default: break;
|
||||
}
|
||||
|
||||
return scnprintf(bf, size, "%#x", behavior);
|
||||
}
|
||||
|
||||
#define SCA_MADV_BHV syscall_arg__scnprintf_madvise_behavior
|
68
tools/perf/trace/beauty/mode_t.c
Normal file
68
tools/perf/trace/beauty/mode_t.c
Normal file
@@ -0,0 +1,68 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* From include/linux/stat.h */
|
||||
#ifndef S_IRWXUGO
|
||||
#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO)
|
||||
#endif
|
||||
#ifndef S_IALLUGO
|
||||
#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
|
||||
#endif
|
||||
#ifndef S_IRUGO
|
||||
#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH)
|
||||
#endif
|
||||
#ifndef S_IWUGO
|
||||
#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH)
|
||||
#endif
|
||||
#ifndef S_IXUGO
|
||||
#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
|
||||
#endif
|
||||
|
||||
static size_t syscall_arg__scnprintf_mode_t(char *bf, size_t size, struct syscall_arg *arg)
|
||||
{
|
||||
int printed = 0, mode = arg->val;
|
||||
|
||||
#define P_MODE(n) \
|
||||
if ((mode & S_##n) == S_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
|
||||
mode &= ~S_##n; \
|
||||
}
|
||||
|
||||
P_MODE(IALLUGO);
|
||||
P_MODE(IRWXUGO);
|
||||
P_MODE(IRUGO);
|
||||
P_MODE(IWUGO);
|
||||
P_MODE(IXUGO);
|
||||
P_MODE(IFMT);
|
||||
P_MODE(IFSOCK);
|
||||
P_MODE(IFLNK);
|
||||
P_MODE(IFREG);
|
||||
P_MODE(IFBLK);
|
||||
P_MODE(IFDIR);
|
||||
P_MODE(IFCHR);
|
||||
P_MODE(IFIFO);
|
||||
P_MODE(ISUID);
|
||||
P_MODE(ISGID);
|
||||
P_MODE(ISVTX);
|
||||
P_MODE(IRWXU);
|
||||
P_MODE(IRUSR);
|
||||
P_MODE(IWUSR);
|
||||
P_MODE(IXUSR);
|
||||
P_MODE(IRWXG);
|
||||
P_MODE(IRGRP);
|
||||
P_MODE(IWGRP);
|
||||
P_MODE(IXGRP);
|
||||
P_MODE(IRWXO);
|
||||
P_MODE(IROTH);
|
||||
P_MODE(IWOTH);
|
||||
P_MODE(IXOTH);
|
||||
#undef P_MODE
|
||||
|
||||
if (mode)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", mode);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_MODE_T syscall_arg__scnprintf_mode_t
|
62
tools/perf/trace/beauty/msg_flags.c
Normal file
62
tools/perf/trace/beauty/msg_flags.c
Normal file
@@ -0,0 +1,62 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#ifndef MSG_PROBE
|
||||
#define MSG_PROBE 0x10
|
||||
#endif
|
||||
#ifndef MSG_WAITFORONE
|
||||
#define MSG_WAITFORONE 0x10000
|
||||
#endif
|
||||
#ifndef MSG_SENDPAGE_NOTLAST
|
||||
#define MSG_SENDPAGE_NOTLAST 0x20000
|
||||
#endif
|
||||
#ifndef MSG_FASTOPEN
|
||||
#define MSG_FASTOPEN 0x20000000
|
||||
#endif
|
||||
#ifndef MSG_CMSG_CLOEXEC
|
||||
# define MSG_CMSG_CLOEXEC 0x40000000
|
||||
#endif
|
||||
|
||||
static size_t syscall_arg__scnprintf_msg_flags(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
int printed = 0, flags = arg->val;
|
||||
|
||||
if (flags == 0)
|
||||
return scnprintf(bf, size, "NONE");
|
||||
#define P_MSG_FLAG(n) \
|
||||
if (flags & MSG_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
|
||||
flags &= ~MSG_##n; \
|
||||
}
|
||||
|
||||
P_MSG_FLAG(OOB);
|
||||
P_MSG_FLAG(PEEK);
|
||||
P_MSG_FLAG(DONTROUTE);
|
||||
P_MSG_FLAG(TRYHARD);
|
||||
P_MSG_FLAG(CTRUNC);
|
||||
P_MSG_FLAG(PROBE);
|
||||
P_MSG_FLAG(TRUNC);
|
||||
P_MSG_FLAG(DONTWAIT);
|
||||
P_MSG_FLAG(EOR);
|
||||
P_MSG_FLAG(WAITALL);
|
||||
P_MSG_FLAG(FIN);
|
||||
P_MSG_FLAG(SYN);
|
||||
P_MSG_FLAG(CONFIRM);
|
||||
P_MSG_FLAG(RST);
|
||||
P_MSG_FLAG(ERRQUEUE);
|
||||
P_MSG_FLAG(NOSIGNAL);
|
||||
P_MSG_FLAG(MORE);
|
||||
P_MSG_FLAG(WAITFORONE);
|
||||
P_MSG_FLAG(SENDPAGE_NOTLAST);
|
||||
P_MSG_FLAG(FASTOPEN);
|
||||
P_MSG_FLAG(CMSG_CLOEXEC);
|
||||
#undef P_MSG_FLAG
|
||||
|
||||
if (flags)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_MSG_FLAGS syscall_arg__scnprintf_msg_flags
|
56
tools/perf/trace/beauty/open_flags.c
Normal file
56
tools/perf/trace/beauty/open_flags.c
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
static size_t syscall_arg__scnprintf_open_flags(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
int printed = 0, flags = arg->val;
|
||||
|
||||
if (!(flags & O_CREAT))
|
||||
arg->mask |= 1 << (arg->idx + 1); /* Mask the mode parm */
|
||||
|
||||
if (flags == 0)
|
||||
return scnprintf(bf, size, "RDONLY");
|
||||
#define P_FLAG(n) \
|
||||
if (flags & O_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
|
||||
flags &= ~O_##n; \
|
||||
}
|
||||
|
||||
P_FLAG(APPEND);
|
||||
P_FLAG(ASYNC);
|
||||
P_FLAG(CLOEXEC);
|
||||
P_FLAG(CREAT);
|
||||
P_FLAG(DIRECT);
|
||||
P_FLAG(DIRECTORY);
|
||||
P_FLAG(EXCL);
|
||||
P_FLAG(LARGEFILE);
|
||||
P_FLAG(NOATIME);
|
||||
P_FLAG(NOCTTY);
|
||||
#ifdef O_NONBLOCK
|
||||
P_FLAG(NONBLOCK);
|
||||
#elif O_NDELAY
|
||||
P_FLAG(NDELAY);
|
||||
#endif
|
||||
#ifdef O_PATH
|
||||
P_FLAG(PATH);
|
||||
#endif
|
||||
P_FLAG(RDWR);
|
||||
#ifdef O_DSYNC
|
||||
if ((flags & O_SYNC) == O_SYNC)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", "SYNC");
|
||||
else {
|
||||
P_FLAG(DSYNC);
|
||||
}
|
||||
#else
|
||||
P_FLAG(SYNC);
|
||||
#endif
|
||||
P_FLAG(TRUNC);
|
||||
P_FLAG(WRONLY);
|
||||
#undef P_FLAG
|
||||
|
||||
if (flags)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_OPEN_FLAGS syscall_arg__scnprintf_open_flags
|
43
tools/perf/trace/beauty/perf_event_open.c
Normal file
43
tools/perf/trace/beauty/perf_event_open.c
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef PERF_FLAG_FD_NO_GROUP
|
||||
# define PERF_FLAG_FD_NO_GROUP (1UL << 0)
|
||||
#endif
|
||||
|
||||
#ifndef PERF_FLAG_FD_OUTPUT
|
||||
# define PERF_FLAG_FD_OUTPUT (1UL << 1)
|
||||
#endif
|
||||
|
||||
#ifndef PERF_FLAG_PID_CGROUP
|
||||
# define PERF_FLAG_PID_CGROUP (1UL << 2) /* pid=cgroup id, per-cpu mode only */
|
||||
#endif
|
||||
|
||||
#ifndef PERF_FLAG_FD_CLOEXEC
|
||||
# define PERF_FLAG_FD_CLOEXEC (1UL << 3) /* O_CLOEXEC */
|
||||
#endif
|
||||
|
||||
static size_t syscall_arg__scnprintf_perf_flags(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
int printed = 0, flags = arg->val;
|
||||
|
||||
if (flags == 0)
|
||||
return 0;
|
||||
|
||||
#define P_FLAG(n) \
|
||||
if (flags & PERF_FLAG_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
|
||||
flags &= ~PERF_FLAG_##n; \
|
||||
}
|
||||
|
||||
P_FLAG(FD_NO_GROUP);
|
||||
P_FLAG(FD_OUTPUT);
|
||||
P_FLAG(PID_CGROUP);
|
||||
P_FLAG(FD_CLOEXEC);
|
||||
#undef P_FLAG
|
||||
|
||||
if (flags)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_PERF_FLAGS syscall_arg__scnprintf_perf_flags
|
21
tools/perf/trace/beauty/pid.c
Normal file
21
tools/perf/trace/beauty/pid.c
Normal file
@@ -0,0 +1,21 @@
|
||||
static size_t syscall_arg__scnprintf_pid(char *bf, size_t size, struct syscall_arg *arg)
|
||||
{
|
||||
int pid = arg->val;
|
||||
struct trace *trace = arg->trace;
|
||||
size_t printed = scnprintf(bf, size, "%d", pid);
|
||||
struct thread *thread = machine__findnew_thread(trace->host, pid, pid);
|
||||
|
||||
if (thread != NULL) {
|
||||
if (!thread->comm_set)
|
||||
thread__set_comm_from_proc(thread);
|
||||
|
||||
if (thread->comm_set)
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
" (%s)", thread__comm_str(thread));
|
||||
thread__put(thread);
|
||||
}
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_PID syscall_arg__scnprintf_pid
|
44
tools/perf/trace/beauty/sched_policy.c
Normal file
44
tools/perf/trace/beauty/sched_policy.c
Normal file
@@ -0,0 +1,44 @@
|
||||
#include <sched.h>
|
||||
|
||||
/*
|
||||
* Not defined anywhere else, probably, just to make sure we
|
||||
* catch future flags
|
||||
*/
|
||||
#define SCHED_POLICY_MASK 0xff
|
||||
|
||||
#ifndef SCHED_DEADLINE
|
||||
#define SCHED_DEADLINE 6
|
||||
#endif
|
||||
|
||||
static size_t syscall_arg__scnprintf_sched_policy(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
const char *policies[] = {
|
||||
"NORMAL", "FIFO", "RR", "BATCH", "ISO", "IDLE", "DEADLINE",
|
||||
};
|
||||
size_t printed;
|
||||
int policy = arg->val,
|
||||
flags = policy & ~SCHED_POLICY_MASK;
|
||||
|
||||
policy &= SCHED_POLICY_MASK;
|
||||
if (policy <= SCHED_DEADLINE)
|
||||
printed = scnprintf(bf, size, "%s", policies[policy]);
|
||||
else
|
||||
printed = scnprintf(bf, size, "%#x", policy);
|
||||
|
||||
#define P_POLICY_FLAG(n) \
|
||||
if (flags & SCHED_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "|%s", #n); \
|
||||
flags &= ~SCHED_##n; \
|
||||
}
|
||||
|
||||
P_POLICY_FLAG(RESET_ON_FORK);
|
||||
#undef P_POLICY_FLAG
|
||||
|
||||
if (flags)
|
||||
printed += scnprintf(bf + printed, size - printed, "|%#x", flags);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_SCHED_POLICY syscall_arg__scnprintf_sched_policy
|
52
tools/perf/trace/beauty/seccomp.c
Normal file
52
tools/perf/trace/beauty/seccomp.c
Normal file
@@ -0,0 +1,52 @@
|
||||
#include <linux/seccomp.h>
|
||||
|
||||
#ifndef SECCOMP_SET_MODE_STRICT
|
||||
#define SECCOMP_SET_MODE_STRICT 0
|
||||
#endif
|
||||
#ifndef SECCOMP_SET_MODE_FILTER
|
||||
#define SECCOMP_SET_MODE_FILTER 1
|
||||
#endif
|
||||
|
||||
static size_t syscall_arg__scnprintf_seccomp_op(char *bf, size_t size, struct syscall_arg *arg)
|
||||
{
|
||||
int op = arg->val;
|
||||
size_t printed = 0;
|
||||
|
||||
switch (op) {
|
||||
#define P_SECCOMP_SET_MODE_OP(n) case SECCOMP_SET_MODE_##n: printed = scnprintf(bf, size, #n); break
|
||||
P_SECCOMP_SET_MODE_OP(STRICT);
|
||||
P_SECCOMP_SET_MODE_OP(FILTER);
|
||||
#undef P_SECCOMP_SET_MODE_OP
|
||||
default: printed = scnprintf(bf, size, "%#x", op); break;
|
||||
}
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_SECCOMP_OP syscall_arg__scnprintf_seccomp_op
|
||||
|
||||
#ifndef SECCOMP_FILTER_FLAG_TSYNC
|
||||
#define SECCOMP_FILTER_FLAG_TSYNC 1
|
||||
#endif
|
||||
|
||||
static size_t syscall_arg__scnprintf_seccomp_flags(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
int printed = 0, flags = arg->val;
|
||||
|
||||
#define P_FLAG(n) \
|
||||
if (flags & SECCOMP_FILTER_FLAG_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
|
||||
flags &= ~SECCOMP_FILTER_FLAG_##n; \
|
||||
}
|
||||
|
||||
P_FLAG(TSYNC);
|
||||
#undef P_FLAG
|
||||
|
||||
if (flags)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", flags);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_SECCOMP_FLAGS syscall_arg__scnprintf_seccomp_flags
|
53
tools/perf/trace/beauty/signum.c
Normal file
53
tools/perf/trace/beauty/signum.c
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
static size_t syscall_arg__scnprintf_signum(char *bf, size_t size, struct syscall_arg *arg)
|
||||
{
|
||||
int sig = arg->val;
|
||||
|
||||
switch (sig) {
|
||||
#define P_SIGNUM(n) case SIG##n: return scnprintf(bf, size, #n)
|
||||
P_SIGNUM(HUP);
|
||||
P_SIGNUM(INT);
|
||||
P_SIGNUM(QUIT);
|
||||
P_SIGNUM(ILL);
|
||||
P_SIGNUM(TRAP);
|
||||
P_SIGNUM(ABRT);
|
||||
P_SIGNUM(BUS);
|
||||
P_SIGNUM(FPE);
|
||||
P_SIGNUM(KILL);
|
||||
P_SIGNUM(USR1);
|
||||
P_SIGNUM(SEGV);
|
||||
P_SIGNUM(USR2);
|
||||
P_SIGNUM(PIPE);
|
||||
P_SIGNUM(ALRM);
|
||||
P_SIGNUM(TERM);
|
||||
P_SIGNUM(CHLD);
|
||||
P_SIGNUM(CONT);
|
||||
P_SIGNUM(STOP);
|
||||
P_SIGNUM(TSTP);
|
||||
P_SIGNUM(TTIN);
|
||||
P_SIGNUM(TTOU);
|
||||
P_SIGNUM(URG);
|
||||
P_SIGNUM(XCPU);
|
||||
P_SIGNUM(XFSZ);
|
||||
P_SIGNUM(VTALRM);
|
||||
P_SIGNUM(PROF);
|
||||
P_SIGNUM(WINCH);
|
||||
P_SIGNUM(IO);
|
||||
P_SIGNUM(PWR);
|
||||
P_SIGNUM(SYS);
|
||||
#ifdef SIGEMT
|
||||
P_SIGNUM(EMT);
|
||||
#endif
|
||||
#ifdef SIGSTKFLT
|
||||
P_SIGNUM(STKFLT);
|
||||
#endif
|
||||
#ifdef SIGSWI
|
||||
P_SIGNUM(SWI);
|
||||
#endif
|
||||
default: break;
|
||||
}
|
||||
|
||||
return scnprintf(bf, size, "%#x", sig);
|
||||
}
|
||||
|
||||
#define SCA_SIGNUM syscall_arg__scnprintf_signum
|
60
tools/perf/trace/beauty/socket_type.c
Normal file
60
tools/perf/trace/beauty/socket_type.c
Normal file
@@ -0,0 +1,60 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#ifndef SOCK_DCCP
|
||||
# define SOCK_DCCP 6
|
||||
#endif
|
||||
|
||||
#ifndef SOCK_CLOEXEC
|
||||
# define SOCK_CLOEXEC 02000000
|
||||
#endif
|
||||
|
||||
#ifndef SOCK_NONBLOCK
|
||||
# define SOCK_NONBLOCK 00004000
|
||||
#endif
|
||||
|
||||
#ifndef SOCK_TYPE_MASK
|
||||
#define SOCK_TYPE_MASK 0xf
|
||||
#endif
|
||||
|
||||
static size_t syscall_arg__scnprintf_socket_type(char *bf, size_t size, struct syscall_arg *arg)
|
||||
{
|
||||
size_t printed;
|
||||
int type = arg->val,
|
||||
flags = type & ~SOCK_TYPE_MASK;
|
||||
|
||||
type &= SOCK_TYPE_MASK;
|
||||
/*
|
||||
* Can't use a strarray, MIPS may override for ABI reasons.
|
||||
*/
|
||||
switch (type) {
|
||||
#define P_SK_TYPE(n) case SOCK_##n: printed = scnprintf(bf, size, #n); break;
|
||||
P_SK_TYPE(STREAM);
|
||||
P_SK_TYPE(DGRAM);
|
||||
P_SK_TYPE(RAW);
|
||||
P_SK_TYPE(RDM);
|
||||
P_SK_TYPE(SEQPACKET);
|
||||
P_SK_TYPE(DCCP);
|
||||
P_SK_TYPE(PACKET);
|
||||
#undef P_SK_TYPE
|
||||
default:
|
||||
printed = scnprintf(bf, size, "%#x", type);
|
||||
}
|
||||
|
||||
#define P_SK_FLAG(n) \
|
||||
if (flags & SOCK_##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "|%s", #n); \
|
||||
flags &= ~SOCK_##n; \
|
||||
}
|
||||
|
||||
P_SK_FLAG(CLOEXEC);
|
||||
P_SK_FLAG(NONBLOCK);
|
||||
#undef P_SK_FLAG
|
||||
|
||||
if (flags)
|
||||
printed += scnprintf(bf + printed, size - printed, "|%#x", flags);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_SK_TYPE syscall_arg__scnprintf_socket_type
|
26
tools/perf/trace/beauty/waitid_options.c
Normal file
26
tools/perf/trace/beauty/waitid_options.c
Normal file
@@ -0,0 +1,26 @@
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static size_t syscall_arg__scnprintf_waitid_options(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
int printed = 0, options = arg->val;
|
||||
|
||||
#define P_OPTION(n) \
|
||||
if (options & W##n) { \
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%s", printed ? "|" : "", #n); \
|
||||
options &= ~W##n; \
|
||||
}
|
||||
|
||||
P_OPTION(NOHANG);
|
||||
P_OPTION(UNTRACED);
|
||||
P_OPTION(CONTINUED);
|
||||
#undef P_OPTION
|
||||
|
||||
if (options)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s%#x", printed ? "|" : "", options);
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
#define SCA_WAITID_OPTIONS syscall_arg__scnprintf_waitid_options
|
@@ -1607,9 +1607,8 @@ static int hists_browser__scnprintf_hierarchy_headers(struct hist_browser *brows
|
||||
|
||||
ret = fmt->header(fmt, &dummy_hpp, hists_to_evsel(hists));
|
||||
dummy_hpp.buf[ret] = '\0';
|
||||
rtrim(dummy_hpp.buf);
|
||||
|
||||
start = ltrim(dummy_hpp.buf);
|
||||
start = trim(dummy_hpp.buf);
|
||||
ret = strlen(start);
|
||||
|
||||
if (start != dummy_hpp.buf)
|
||||
@@ -1897,11 +1896,10 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
|
||||
bool first = true;
|
||||
int ret;
|
||||
|
||||
if (symbol_conf.use_callchain)
|
||||
if (symbol_conf.use_callchain) {
|
||||
folded_sign = hist_entry__folded(he);
|
||||
|
||||
if (symbol_conf.use_callchain)
|
||||
printed += fprintf(fp, "%c ", folded_sign);
|
||||
}
|
||||
|
||||
hists__for_each_format(browser->hists, fmt) {
|
||||
if (perf_hpp__should_skip(fmt, he->hists))
|
||||
@@ -2137,7 +2135,7 @@ static int hists__browser_title(struct hists *hists,
|
||||
printed += snprintf(bf + printed, size - printed,
|
||||
", UID: %s", hists->uid_filter_str);
|
||||
if (thread) {
|
||||
if (sort__has_thread) {
|
||||
if (hists__has(hists, thread)) {
|
||||
printed += scnprintf(bf + printed, size - printed,
|
||||
", Thread: %s(%d)",
|
||||
(thread->comm_set ? thread__comm_str(thread) : ""),
|
||||
@@ -2322,7 +2320,8 @@ do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
|
||||
{
|
||||
struct thread *thread = act->thread;
|
||||
|
||||
if ((!sort__has_thread && !sort__has_comm) || thread == NULL)
|
||||
if ((!hists__has(browser->hists, thread) &&
|
||||
!hists__has(browser->hists, comm)) || thread == NULL)
|
||||
return 0;
|
||||
|
||||
if (browser->hists->thread_filter) {
|
||||
@@ -2331,7 +2330,7 @@ do_zoom_thread(struct hist_browser *browser, struct popup_action *act)
|
||||
thread__zput(browser->hists->thread_filter);
|
||||
ui_helpline__pop();
|
||||
} else {
|
||||
if (sort__has_thread) {
|
||||
if (hists__has(browser->hists, thread)) {
|
||||
ui_helpline__fpush("To zoom out press ESC or ENTER + \"Zoom out of %s(%d) thread\"",
|
||||
thread->comm_set ? thread__comm_str(thread) : "",
|
||||
thread->tid);
|
||||
@@ -2356,10 +2355,11 @@ add_thread_opt(struct hist_browser *browser, struct popup_action *act,
|
||||
{
|
||||
int ret;
|
||||
|
||||
if ((!sort__has_thread && !sort__has_comm) || thread == NULL)
|
||||
if ((!hists__has(browser->hists, thread) &&
|
||||
!hists__has(browser->hists, comm)) || thread == NULL)
|
||||
return 0;
|
||||
|
||||
if (sort__has_thread) {
|
||||
if (hists__has(browser->hists, thread)) {
|
||||
ret = asprintf(optstr, "Zoom %s %s(%d) thread",
|
||||
browser->hists->thread_filter ? "out of" : "into",
|
||||
thread->comm_set ? thread__comm_str(thread) : "",
|
||||
@@ -2382,7 +2382,7 @@ do_zoom_dso(struct hist_browser *browser, struct popup_action *act)
|
||||
{
|
||||
struct map *map = act->ms.map;
|
||||
|
||||
if (!sort__has_dso || map == NULL)
|
||||
if (!hists__has(browser->hists, dso) || map == NULL)
|
||||
return 0;
|
||||
|
||||
if (browser->hists->dso_filter) {
|
||||
@@ -2409,7 +2409,7 @@ static int
|
||||
add_dso_opt(struct hist_browser *browser, struct popup_action *act,
|
||||
char **optstr, struct map *map)
|
||||
{
|
||||
if (!sort__has_dso || map == NULL)
|
||||
if (!hists__has(browser->hists, dso) || map == NULL)
|
||||
return 0;
|
||||
|
||||
if (asprintf(optstr, "Zoom %s %s DSO",
|
||||
@@ -2431,10 +2431,10 @@ do_browse_map(struct hist_browser *browser __maybe_unused,
|
||||
}
|
||||
|
||||
static int
|
||||
add_map_opt(struct hist_browser *browser __maybe_unused,
|
||||
add_map_opt(struct hist_browser *browser,
|
||||
struct popup_action *act, char **optstr, struct map *map)
|
||||
{
|
||||
if (!sort__has_dso || map == NULL)
|
||||
if (!hists__has(browser->hists, dso) || map == NULL)
|
||||
return 0;
|
||||
|
||||
if (asprintf(optstr, "Browse map details") < 0)
|
||||
@@ -2536,7 +2536,7 @@ add_exit_opt(struct hist_browser *browser __maybe_unused,
|
||||
static int
|
||||
do_zoom_socket(struct hist_browser *browser, struct popup_action *act)
|
||||
{
|
||||
if (!sort__has_socket || act->socket < 0)
|
||||
if (!hists__has(browser->hists, socket) || act->socket < 0)
|
||||
return 0;
|
||||
|
||||
if (browser->hists->socket_filter > -1) {
|
||||
@@ -2558,7 +2558,7 @@ static int
|
||||
add_socket_opt(struct hist_browser *browser, struct popup_action *act,
|
||||
char **optstr, int socket_id)
|
||||
{
|
||||
if (!sort__has_socket || socket_id < 0)
|
||||
if (!hists__has(browser->hists, socket) || socket_id < 0)
|
||||
return 0;
|
||||
|
||||
if (asprintf(optstr, "Zoom %s Processor Socket %d",
|
||||
@@ -2749,7 +2749,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
*/
|
||||
goto out_free_stack;
|
||||
case 'a':
|
||||
if (!sort__has_sym) {
|
||||
if (!hists__has(hists, sym)) {
|
||||
ui_browser__warning(&browser->b, delay_secs * 2,
|
||||
"Annotation is only available for symbolic views, "
|
||||
"include \"sym*\" in --sort to use it.");
|
||||
@@ -2912,7 +2912,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sort__has_sym || browser->selection == NULL)
|
||||
if (!hists__has(hists, sym) || browser->selection == NULL)
|
||||
goto skip_annotation;
|
||||
|
||||
if (sort__mode == SORT_MODE__BRANCH) {
|
||||
@@ -2956,7 +2956,7 @@ skip_annotation:
|
||||
goto skip_scripting;
|
||||
|
||||
if (browser->he_selection) {
|
||||
if (sort__has_thread && thread) {
|
||||
if (hists__has(hists, thread) && thread) {
|
||||
nr_options += add_script_opt(browser,
|
||||
&actions[nr_options],
|
||||
&options[nr_options],
|
||||
@@ -2971,7 +2971,7 @@ skip_annotation:
|
||||
*
|
||||
* See hist_browser__show_entry.
|
||||
*/
|
||||
if (sort__has_sym && browser->selection->sym) {
|
||||
if (hists__has(hists, sym) && browser->selection->sym) {
|
||||
nr_options += add_script_opt(browser,
|
||||
&actions[nr_options],
|
||||
&options[nr_options],
|
||||
|
@@ -379,7 +379,7 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists,
|
||||
gtk_tree_store_set(store, &iter, col_idx++, s, -1);
|
||||
}
|
||||
|
||||
if (symbol_conf.use_callchain && sort__has_sym) {
|
||||
if (symbol_conf.use_callchain && hists__has(hists, sym)) {
|
||||
if (callchain_param.mode == CHAIN_GRAPH_REL)
|
||||
total = symbol_conf.cumulate_callchain ?
|
||||
h->stat_acc->period : h->stat.period;
|
||||
|
@@ -635,7 +635,7 @@ unsigned int hists__sort_list_width(struct hists *hists)
|
||||
ret += fmt->width(fmt, &dummy_hpp, hists_to_evsel(hists));
|
||||
}
|
||||
|
||||
if (verbose && sort__has_sym) /* Addr + origin */
|
||||
if (verbose && hists__has(hists, sym)) /* Addr + origin */
|
||||
ret += 3 + BITS_PER_LONG / 4;
|
||||
|
||||
return ret;
|
||||
|
@@ -569,9 +569,8 @@ static int print_hierarchy_header(struct hists *hists, struct perf_hpp *hpp,
|
||||
first_col = false;
|
||||
|
||||
fmt->header(fmt, hpp, hists_to_evsel(hists));
|
||||
rtrim(hpp->buf);
|
||||
|
||||
header_width += fprintf(fp, "%s", ltrim(hpp->buf));
|
||||
header_width += fprintf(fp, "%s", trim(hpp->buf));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,7 @@ libperf-y += env.o
|
||||
libperf-y += event.o
|
||||
libperf-y += evlist.o
|
||||
libperf-y += evsel.o
|
||||
libperf-y += evsel_fprintf.o
|
||||
libperf-y += find_bit.o
|
||||
libperf-y += kallsyms.o
|
||||
libperf-y += levenshtein.o
|
||||
@@ -26,9 +27,9 @@ libperf-y += strlist.o
|
||||
libperf-y += strfilter.o
|
||||
libperf-y += top.o
|
||||
libperf-y += usage.o
|
||||
libperf-y += wrapper.o
|
||||
libperf-y += dso.o
|
||||
libperf-y += symbol.o
|
||||
libperf-y += symbol_fprintf.o
|
||||
libperf-y += color.o
|
||||
libperf-y += header.o
|
||||
libperf-y += callchain.o
|
||||
@@ -38,6 +39,7 @@ libperf-y += machine.o
|
||||
libperf-y += map.o
|
||||
libperf-y += pstack.o
|
||||
libperf-y += session.o
|
||||
libperf-$(CONFIG_AUDIT) += syscalltbl.o
|
||||
libperf-y += ordered-events.o
|
||||
libperf-y += comm.o
|
||||
libperf-y += thread.o
|
||||
@@ -69,9 +71,9 @@ libperf-y += stat-shadow.o
|
||||
libperf-y += record.o
|
||||
libperf-y += srcline.o
|
||||
libperf-y += data.o
|
||||
libperf-$(CONFIG_X86) += tsc.o
|
||||
libperf-$(CONFIG_AUXTRACE) += tsc.o
|
||||
libperf-y += tsc.o
|
||||
libperf-y += cloexec.o
|
||||
libperf-y += call-path.o
|
||||
libperf-y += thread-stack.o
|
||||
libperf-$(CONFIG_AUXTRACE) += auxtrace.o
|
||||
libperf-$(CONFIG_AUXTRACE) += intel-pt-decoder/
|
||||
|
@@ -1138,7 +1138,7 @@ fallback:
|
||||
|
||||
if (dso->symtab_type == DSO_BINARY_TYPE__KALLSYMS &&
|
||||
!dso__is_kcore(dso)) {
|
||||
char bf[BUILD_ID_SIZE * 2 + 16] = " with build id ";
|
||||
char bf[SBUILD_ID_SIZE + 15] = " with build id ";
|
||||
char *build_id_msg = NULL;
|
||||
|
||||
if (dso->annotate_warned)
|
||||
@@ -1665,5 +1665,5 @@ int hist_entry__annotate(struct hist_entry *he, size_t privsize)
|
||||
|
||||
bool ui__has_annotation(void)
|
||||
{
|
||||
return use_browser == 1 && sort__has_sym;
|
||||
return use_browser == 1 && perf_hpp_list.sym;
|
||||
}
|
||||
|
@@ -940,6 +940,7 @@ void itrace_synth_opts__set_default(struct itrace_synth_opts *synth_opts)
|
||||
synth_opts->period = PERF_ITRACE_DEFAULT_PERIOD;
|
||||
synth_opts->callchain_sz = PERF_ITRACE_DEFAULT_CALLCHAIN_SZ;
|
||||
synth_opts->last_branch_sz = PERF_ITRACE_DEFAULT_LAST_BRANCH_SZ;
|
||||
synth_opts->initial_skip = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1064,6 +1065,12 @@ int itrace_parse_synth_opts(const struct option *opt, const char *str,
|
||||
synth_opts->last_branch_sz = val;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
synth_opts->initial_skip = strtoul(p, &endptr, 10);
|
||||
if (p == endptr)
|
||||
goto out_err;
|
||||
p = endptr;
|
||||
break;
|
||||
case ' ':
|
||||
case ',':
|
||||
break;
|
||||
|
@@ -68,6 +68,7 @@ enum itrace_period_type {
|
||||
* @last_branch_sz: branch context size
|
||||
* @period: 'instructions' events period
|
||||
* @period_type: 'instructions' events period type
|
||||
* @initial_skip: skip N events at the beginning.
|
||||
*/
|
||||
struct itrace_synth_opts {
|
||||
bool set;
|
||||
@@ -86,6 +87,7 @@ struct itrace_synth_opts {
|
||||
unsigned int last_branch_sz;
|
||||
unsigned long long period;
|
||||
enum itrace_period_type period_type;
|
||||
unsigned long initial_skip;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@@ -842,6 +842,58 @@ bpf_map_op__new(struct parse_events_term *term)
|
||||
return op;
|
||||
}
|
||||
|
||||
static struct bpf_map_op *
|
||||
bpf_map_op__clone(struct bpf_map_op *op)
|
||||
{
|
||||
struct bpf_map_op *newop;
|
||||
|
||||
newop = memdup(op, sizeof(*op));
|
||||
if (!newop) {
|
||||
pr_debug("Failed to alloc bpf_map_op\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&newop->list);
|
||||
if (op->key_type == BPF_MAP_KEY_RANGES) {
|
||||
size_t memsz = op->k.array.nr_ranges *
|
||||
sizeof(op->k.array.ranges[0]);
|
||||
|
||||
newop->k.array.ranges = memdup(op->k.array.ranges, memsz);
|
||||
if (!newop->k.array.ranges) {
|
||||
pr_debug("Failed to alloc indices for map\n");
|
||||
free(newop);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return newop;
|
||||
}
|
||||
|
||||
static struct bpf_map_priv *
|
||||
bpf_map_priv__clone(struct bpf_map_priv *priv)
|
||||
{
|
||||
struct bpf_map_priv *newpriv;
|
||||
struct bpf_map_op *pos, *newop;
|
||||
|
||||
newpriv = zalloc(sizeof(*newpriv));
|
||||
if (!newpriv) {
|
||||
pr_debug("No enough memory to alloc map private\n");
|
||||
return NULL;
|
||||
}
|
||||
INIT_LIST_HEAD(&newpriv->ops_list);
|
||||
|
||||
list_for_each_entry(pos, &priv->ops_list, list) {
|
||||
newop = bpf_map_op__clone(pos);
|
||||
if (!newop) {
|
||||
bpf_map_priv__purge(newpriv);
|
||||
return NULL;
|
||||
}
|
||||
list_add_tail(&newop->list, &newpriv->ops_list);
|
||||
}
|
||||
|
||||
return newpriv;
|
||||
}
|
||||
|
||||
static int
|
||||
bpf_map__add_op(struct bpf_map *map, struct bpf_map_op *op)
|
||||
{
|
||||
@@ -1417,6 +1469,89 @@ int bpf__apply_obj_config(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define bpf__for_each_map(pos, obj, objtmp) \
|
||||
bpf_object__for_each_safe(obj, objtmp) \
|
||||
bpf_map__for_each(pos, obj)
|
||||
|
||||
#define bpf__for_each_stdout_map(pos, obj, objtmp) \
|
||||
bpf__for_each_map(pos, obj, objtmp) \
|
||||
if (bpf_map__get_name(pos) && \
|
||||
(strcmp("__bpf_stdout__", \
|
||||
bpf_map__get_name(pos)) == 0))
|
||||
|
||||
int bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused)
|
||||
{
|
||||
struct bpf_map_priv *tmpl_priv = NULL;
|
||||
struct bpf_object *obj, *tmp;
|
||||
struct perf_evsel *evsel = NULL;
|
||||
struct bpf_map *map;
|
||||
int err;
|
||||
bool need_init = false;
|
||||
|
||||
bpf__for_each_stdout_map(map, obj, tmp) {
|
||||
struct bpf_map_priv *priv;
|
||||
|
||||
err = bpf_map__get_private(map, (void **)&priv);
|
||||
if (err)
|
||||
return -BPF_LOADER_ERRNO__INTERNAL;
|
||||
|
||||
/*
|
||||
* No need to check map type: type should have been
|
||||
* verified by kernel.
|
||||
*/
|
||||
if (!need_init && !priv)
|
||||
need_init = !priv;
|
||||
if (!tmpl_priv && priv)
|
||||
tmpl_priv = priv;
|
||||
}
|
||||
|
||||
if (!need_init)
|
||||
return 0;
|
||||
|
||||
if (!tmpl_priv) {
|
||||
err = parse_events(evlist, "bpf-output/no-inherit=1,name=__bpf_stdout__/",
|
||||
NULL);
|
||||
if (err) {
|
||||
pr_debug("ERROR: failed to create bpf-output event\n");
|
||||
return -err;
|
||||
}
|
||||
|
||||
evsel = perf_evlist__last(evlist);
|
||||
}
|
||||
|
||||
bpf__for_each_stdout_map(map, obj, tmp) {
|
||||
struct bpf_map_priv *priv;
|
||||
|
||||
err = bpf_map__get_private(map, (void **)&priv);
|
||||
if (err)
|
||||
return -BPF_LOADER_ERRNO__INTERNAL;
|
||||
if (priv)
|
||||
continue;
|
||||
|
||||
if (tmpl_priv) {
|
||||
priv = bpf_map_priv__clone(tmpl_priv);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
err = bpf_map__set_private(map, priv, bpf_map_priv__clear);
|
||||
if (err) {
|
||||
bpf_map_priv__clear(map, priv);
|
||||
return err;
|
||||
}
|
||||
} else if (evsel) {
|
||||
struct bpf_map_op *op;
|
||||
|
||||
op = bpf_map__add_newop(map, NULL);
|
||||
if (IS_ERR(op))
|
||||
return PTR_ERR(op);
|
||||
op->op_type = BPF_MAP_OP_SET_EVSEL;
|
||||
op->v.evsel = evsel;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ERRNO_OFFSET(e) ((e) - __BPF_LOADER_ERRNO__START)
|
||||
#define ERRCODE_OFFSET(c) ERRNO_OFFSET(BPF_LOADER_ERRNO__##c)
|
||||
#define NR_ERRNO (__BPF_LOADER_ERRNO__END - __BPF_LOADER_ERRNO__START)
|
||||
@@ -1590,3 +1725,11 @@ int bpf__strerror_apply_obj_config(int err, char *buf, size_t size)
|
||||
bpf__strerror_end(buf, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bpf__strerror_setup_stdout(struct perf_evlist *evlist __maybe_unused,
|
||||
int err, char *buf, size_t size)
|
||||
{
|
||||
bpf__strerror_head(err, buf, size);
|
||||
bpf__strerror_end(buf, size);
|
||||
return 0;
|
||||
}
|
||||
|
@@ -79,6 +79,11 @@ int bpf__strerror_config_obj(struct bpf_object *obj,
|
||||
size_t size);
|
||||
int bpf__apply_obj_config(void);
|
||||
int bpf__strerror_apply_obj_config(int err, char *buf, size_t size);
|
||||
|
||||
int bpf__setup_stdout(struct perf_evlist *evlist);
|
||||
int bpf__strerror_setup_stdout(struct perf_evlist *evlist, int err,
|
||||
char *buf, size_t size);
|
||||
|
||||
#else
|
||||
static inline struct bpf_object *
|
||||
bpf__prepare_load(const char *filename __maybe_unused,
|
||||
@@ -124,6 +129,12 @@ bpf__apply_obj_config(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
bpf__setup_stdout(struct perf_evlist *evlist __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
__bpf_strerror(char *buf, size_t size)
|
||||
{
|
||||
@@ -177,5 +188,13 @@ bpf__strerror_apply_obj_config(int err __maybe_unused,
|
||||
{
|
||||
return __bpf_strerror(buf, size);
|
||||
}
|
||||
|
||||
static inline int
|
||||
bpf__strerror_setup_stdout(struct perf_evlist *evlist __maybe_unused,
|
||||
int err __maybe_unused, char *buf,
|
||||
size_t size)
|
||||
{
|
||||
return __bpf_strerror(buf, size);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
@@ -261,14 +261,14 @@ static int machine__write_buildid_table(struct machine *machine, int fd)
|
||||
|
||||
if (dso__is_vdso(pos)) {
|
||||
name = pos->short_name;
|
||||
name_len = pos->short_name_len + 1;
|
||||
name_len = pos->short_name_len;
|
||||
} else if (dso__is_kcore(pos)) {
|
||||
machine__mmap_name(machine, nm, sizeof(nm));
|
||||
name = nm;
|
||||
name_len = strlen(nm) + 1;
|
||||
name_len = strlen(nm);
|
||||
} else {
|
||||
name = pos->long_name;
|
||||
name_len = pos->long_name_len + 1;
|
||||
name_len = pos->long_name_len;
|
||||
}
|
||||
|
||||
in_kernel = pos->kernel ||
|
||||
@@ -365,39 +365,17 @@ static char *build_id_cache__dirname_from_path(const char *name,
|
||||
int build_id_cache__list_build_ids(const char *pathname,
|
||||
struct strlist **result)
|
||||
{
|
||||
struct strlist *list;
|
||||
char *dir_name;
|
||||
DIR *dir;
|
||||
struct dirent *d;
|
||||
int ret = 0;
|
||||
|
||||
list = strlist__new(NULL, NULL);
|
||||
dir_name = build_id_cache__dirname_from_path(pathname, false, false);
|
||||
if (!list || !dir_name) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!dir_name)
|
||||
return -ENOMEM;
|
||||
|
||||
/* List up all dirents */
|
||||
dir = opendir(dir_name);
|
||||
if (!dir) {
|
||||
*result = lsdir(dir_name, lsdir_no_dot_filter);
|
||||
if (!*result)
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while ((d = readdir(dir)) != NULL) {
|
||||
if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
|
||||
continue;
|
||||
strlist__add(list, d->d_name);
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
out:
|
||||
free(dir_name);
|
||||
if (ret)
|
||||
strlist__delete(list);
|
||||
else
|
||||
*result = list;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@@ -40,25 +40,6 @@ int split_cmdline(char *cmdline, const char ***argv);
|
||||
|
||||
#define alloc_nr(x) (((x)+16)*3/2)
|
||||
|
||||
/*
|
||||
* Realloc the buffer pointed at by variable 'x' so that it can hold
|
||||
* at least 'nr' entries; the number of entries currently allocated
|
||||
* is 'alloc', using the standard growing factor alloc_nr() macro.
|
||||
*
|
||||
* DO NOT USE any expression with side-effect for 'x' or 'alloc'.
|
||||
*/
|
||||
#define ALLOC_GROW(x, nr, alloc) \
|
||||
do { \
|
||||
if ((nr) > alloc) { \
|
||||
if (alloc_nr(alloc) < (nr)) \
|
||||
alloc = (nr); \
|
||||
else \
|
||||
alloc = alloc_nr(alloc); \
|
||||
x = xrealloc((x), alloc * sizeof(*(x))); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
static inline int is_absolute_path(const char *path)
|
||||
{
|
||||
return path[0] == '/';
|
||||
|
122
tools/perf/util/call-path.c
Normal file
122
tools/perf/util/call-path.c
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* call-path.h: Manipulate a tree data structure containing function call paths
|
||||
* Copyright (c) 2014, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "call-path.h"
|
||||
|
||||
static void call_path__init(struct call_path *cp, struct call_path *parent,
|
||||
struct symbol *sym, u64 ip, bool in_kernel)
|
||||
{
|
||||
cp->parent = parent;
|
||||
cp->sym = sym;
|
||||
cp->ip = sym ? 0 : ip;
|
||||
cp->db_id = 0;
|
||||
cp->in_kernel = in_kernel;
|
||||
RB_CLEAR_NODE(&cp->rb_node);
|
||||
cp->children = RB_ROOT;
|
||||
}
|
||||
|
||||
struct call_path_root *call_path_root__new(void)
|
||||
{
|
||||
struct call_path_root *cpr;
|
||||
|
||||
cpr = zalloc(sizeof(struct call_path_root));
|
||||
if (!cpr)
|
||||
return NULL;
|
||||
call_path__init(&cpr->call_path, NULL, NULL, 0, false);
|
||||
INIT_LIST_HEAD(&cpr->blocks);
|
||||
return cpr;
|
||||
}
|
||||
|
||||
void call_path_root__free(struct call_path_root *cpr)
|
||||
{
|
||||
struct call_path_block *pos, *n;
|
||||
|
||||
list_for_each_entry_safe(pos, n, &cpr->blocks, node) {
|
||||
list_del(&pos->node);
|
||||
free(pos);
|
||||
}
|
||||
free(cpr);
|
||||
}
|
||||
|
||||
static struct call_path *call_path__new(struct call_path_root *cpr,
|
||||
struct call_path *parent,
|
||||
struct symbol *sym, u64 ip,
|
||||
bool in_kernel)
|
||||
{
|
||||
struct call_path_block *cpb;
|
||||
struct call_path *cp;
|
||||
size_t n;
|
||||
|
||||
if (cpr->next < cpr->sz) {
|
||||
cpb = list_last_entry(&cpr->blocks, struct call_path_block,
|
||||
node);
|
||||
} else {
|
||||
cpb = zalloc(sizeof(struct call_path_block));
|
||||
if (!cpb)
|
||||
return NULL;
|
||||
list_add_tail(&cpb->node, &cpr->blocks);
|
||||
cpr->sz += CALL_PATH_BLOCK_SIZE;
|
||||
}
|
||||
|
||||
n = cpr->next++ & CALL_PATH_BLOCK_MASK;
|
||||
cp = &cpb->cp[n];
|
||||
|
||||
call_path__init(cp, parent, sym, ip, in_kernel);
|
||||
|
||||
return cp;
|
||||
}
|
||||
|
||||
struct call_path *call_path__findnew(struct call_path_root *cpr,
|
||||
struct call_path *parent,
|
||||
struct symbol *sym, u64 ip, u64 ks)
|
||||
{
|
||||
struct rb_node **p;
|
||||
struct rb_node *node_parent = NULL;
|
||||
struct call_path *cp;
|
||||
bool in_kernel = ip >= ks;
|
||||
|
||||
if (sym)
|
||||
ip = 0;
|
||||
|
||||
if (!parent)
|
||||
return call_path__new(cpr, parent, sym, ip, in_kernel);
|
||||
|
||||
p = &parent->children.rb_node;
|
||||
while (*p != NULL) {
|
||||
node_parent = *p;
|
||||
cp = rb_entry(node_parent, struct call_path, rb_node);
|
||||
|
||||
if (cp->sym == sym && cp->ip == ip)
|
||||
return cp;
|
||||
|
||||
if (sym < cp->sym || (sym == cp->sym && ip < cp->ip))
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
cp = call_path__new(cpr, parent, sym, ip, in_kernel);
|
||||
if (!cp)
|
||||
return NULL;
|
||||
|
||||
rb_link_node(&cp->rb_node, node_parent, p);
|
||||
rb_insert_color(&cp->rb_node, &parent->children);
|
||||
|
||||
return cp;
|
||||
}
|
77
tools/perf/util/call-path.h
Normal file
77
tools/perf/util/call-path.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* call-path.h: Manipulate a tree data structure containing function call paths
|
||||
* Copyright (c) 2014, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __PERF_CALL_PATH_H
|
||||
#define __PERF_CALL_PATH_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/rbtree.h>
|
||||
|
||||
/**
|
||||
* struct call_path - node in list of calls leading to a function call.
|
||||
* @parent: call path to the parent function call
|
||||
* @sym: symbol of function called
|
||||
* @ip: only if sym is null, the ip of the function
|
||||
* @db_id: id used for db-export
|
||||
* @in_kernel: whether function is a in the kernel
|
||||
* @rb_node: node in parent's tree of called functions
|
||||
* @children: tree of call paths of functions called
|
||||
*
|
||||
* In combination with the call_return structure, the call_path structure
|
||||
* defines a context-sensitve call-graph.
|
||||
*/
|
||||
struct call_path {
|
||||
struct call_path *parent;
|
||||
struct symbol *sym;
|
||||
u64 ip;
|
||||
u64 db_id;
|
||||
bool in_kernel;
|
||||
struct rb_node rb_node;
|
||||
struct rb_root children;
|
||||
};
|
||||
|
||||
#define CALL_PATH_BLOCK_SHIFT 8
|
||||
#define CALL_PATH_BLOCK_SIZE (1 << CALL_PATH_BLOCK_SHIFT)
|
||||
#define CALL_PATH_BLOCK_MASK (CALL_PATH_BLOCK_SIZE - 1)
|
||||
|
||||
struct call_path_block {
|
||||
struct call_path cp[CALL_PATH_BLOCK_SIZE];
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct call_path_root - root of all call paths.
|
||||
* @call_path: root call path
|
||||
* @blocks: list of blocks to store call paths
|
||||
* @next: next free space
|
||||
* @sz: number of spaces
|
||||
*/
|
||||
struct call_path_root {
|
||||
struct call_path call_path;
|
||||
struct list_head blocks;
|
||||
size_t next;
|
||||
size_t sz;
|
||||
};
|
||||
|
||||
struct call_path_root *call_path_root__new(void);
|
||||
void call_path_root__free(struct call_path_root *cpr);
|
||||
|
||||
struct call_path *call_path__findnew(struct call_path_root *cpr,
|
||||
struct call_path *parent,
|
||||
struct symbol *sym, u64 ip, u64 ks);
|
||||
|
||||
#endif
|
@@ -109,6 +109,7 @@ __parse_callchain_report_opt(const char *arg, bool allow_record_opt)
|
||||
bool record_opt_set = false;
|
||||
bool try_stack_size = false;
|
||||
|
||||
callchain_param.enabled = true;
|
||||
symbol_conf.use_callchain = true;
|
||||
|
||||
if (!arg)
|
||||
@@ -117,6 +118,7 @@ __parse_callchain_report_opt(const char *arg, bool allow_record_opt)
|
||||
while ((tok = strtok((char *)arg, ",")) != NULL) {
|
||||
if (!strncmp(tok, "none", strlen(tok))) {
|
||||
callchain_param.mode = CHAIN_NONE;
|
||||
callchain_param.enabled = false;
|
||||
symbol_conf.use_callchain = false;
|
||||
return 0;
|
||||
}
|
||||
@@ -788,7 +790,8 @@ int callchain_cursor_append(struct callchain_cursor *cursor,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent,
|
||||
int sample__resolve_callchain(struct perf_sample *sample,
|
||||
struct callchain_cursor *cursor, struct symbol **parent,
|
||||
struct perf_evsel *evsel, struct addr_location *al,
|
||||
int max_stack)
|
||||
{
|
||||
@@ -796,8 +799,8 @@ int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent
|
||||
return 0;
|
||||
|
||||
if (symbol_conf.use_callchain || symbol_conf.cumulate_callchain ||
|
||||
sort__has_parent) {
|
||||
return thread__resolve_callchain(al->thread, evsel, sample,
|
||||
perf_hpp_list.parent) {
|
||||
return thread__resolve_callchain(al->thread, cursor, evsel, sample,
|
||||
parent, al, max_stack);
|
||||
}
|
||||
return 0;
|
||||
|
@@ -212,7 +212,14 @@ struct hist_entry;
|
||||
int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset);
|
||||
int record_callchain_opt(const struct option *opt, const char *arg, int unset);
|
||||
|
||||
int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent,
|
||||
struct record_opts;
|
||||
|
||||
int record_opts__parse_callchain(struct record_opts *record,
|
||||
struct callchain_param *callchain,
|
||||
const char *arg, bool unset);
|
||||
|
||||
int sample__resolve_callchain(struct perf_sample *sample,
|
||||
struct callchain_cursor *cursor, struct symbol **parent,
|
||||
struct perf_evsel *evsel, struct addr_location *al,
|
||||
int max_stack);
|
||||
int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample);
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include <subcmd/exec-cmd.h>
|
||||
#include "util/hist.h" /* perf_hist_config */
|
||||
#include "util/llvm-utils.h" /* perf_llvm_config */
|
||||
#include "config.h"
|
||||
|
||||
#define MAXNAME (256)
|
||||
|
||||
@@ -377,6 +378,21 @@ const char *perf_config_dirname(const char *name, const char *value)
|
||||
return value;
|
||||
}
|
||||
|
||||
static int perf_buildid_config(const char *var, const char *value)
|
||||
{
|
||||
/* same dir for all commands */
|
||||
if (!strcmp(var, "buildid.dir")) {
|
||||
const char *dir = perf_config_dirname(var, value);
|
||||
|
||||
if (!dir)
|
||||
return -1;
|
||||
strncpy(buildid_dir, dir, MAXPATHLEN-1);
|
||||
buildid_dir[MAXPATHLEN-1] = '\0';
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_default_core_config(const char *var __maybe_unused,
|
||||
const char *value __maybe_unused)
|
||||
{
|
||||
@@ -412,6 +428,9 @@ int perf_default_config(const char *var, const char *value,
|
||||
if (!prefixcmp(var, "llvm."))
|
||||
return perf_llvm_config(var, value);
|
||||
|
||||
if (!prefixcmp(var, "buildid."))
|
||||
return perf_buildid_config(var, value);
|
||||
|
||||
/* Add other config variables here. */
|
||||
return 0;
|
||||
}
|
||||
@@ -506,6 +525,178 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct perf_config_section *find_section(struct list_head *sections,
|
||||
const char *section_name)
|
||||
{
|
||||
struct perf_config_section *section;
|
||||
|
||||
list_for_each_entry(section, sections, node)
|
||||
if (!strcmp(section->name, section_name))
|
||||
return section;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct perf_config_item *find_config_item(const char *name,
|
||||
struct perf_config_section *section)
|
||||
{
|
||||
struct perf_config_item *item;
|
||||
|
||||
list_for_each_entry(item, §ion->items, node)
|
||||
if (!strcmp(item->name, name))
|
||||
return item;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct perf_config_section *add_section(struct list_head *sections,
|
||||
const char *section_name)
|
||||
{
|
||||
struct perf_config_section *section = zalloc(sizeof(*section));
|
||||
|
||||
if (!section)
|
||||
return NULL;
|
||||
|
||||
INIT_LIST_HEAD(§ion->items);
|
||||
section->name = strdup(section_name);
|
||||
if (!section->name) {
|
||||
pr_debug("%s: strdup failed\n", __func__);
|
||||
free(section);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_add_tail(§ion->node, sections);
|
||||
return section;
|
||||
}
|
||||
|
||||
static struct perf_config_item *add_config_item(struct perf_config_section *section,
|
||||
const char *name)
|
||||
{
|
||||
struct perf_config_item *item = zalloc(sizeof(*item));
|
||||
|
||||
if (!item)
|
||||
return NULL;
|
||||
|
||||
item->name = strdup(name);
|
||||
if (!item->name) {
|
||||
pr_debug("%s: strdup failed\n", __func__);
|
||||
free(item);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
list_add_tail(&item->node, §ion->items);
|
||||
return item;
|
||||
}
|
||||
|
||||
static int set_value(struct perf_config_item *item, const char *value)
|
||||
{
|
||||
char *val = strdup(value);
|
||||
|
||||
if (!val)
|
||||
return -1;
|
||||
|
||||
zfree(&item->value);
|
||||
item->value = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int collect_config(const char *var, const char *value,
|
||||
void *perf_config_set)
|
||||
{
|
||||
int ret = -1;
|
||||
char *ptr, *key;
|
||||
char *section_name, *name;
|
||||
struct perf_config_section *section = NULL;
|
||||
struct perf_config_item *item = NULL;
|
||||
struct perf_config_set *set = perf_config_set;
|
||||
struct list_head *sections = &set->sections;
|
||||
|
||||
key = ptr = strdup(var);
|
||||
if (!key) {
|
||||
pr_debug("%s: strdup failed\n", __func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
section_name = strsep(&ptr, ".");
|
||||
name = ptr;
|
||||
if (name == NULL || value == NULL)
|
||||
goto out_free;
|
||||
|
||||
section = find_section(sections, section_name);
|
||||
if (!section) {
|
||||
section = add_section(sections, section_name);
|
||||
if (!section)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
item = find_config_item(name, section);
|
||||
if (!item) {
|
||||
item = add_config_item(section, name);
|
||||
if (!item)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
ret = set_value(item, value);
|
||||
return ret;
|
||||
|
||||
out_free:
|
||||
free(key);
|
||||
perf_config_set__delete(set);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct perf_config_set *perf_config_set__new(void)
|
||||
{
|
||||
struct perf_config_set *set = zalloc(sizeof(*set));
|
||||
|
||||
if (set) {
|
||||
INIT_LIST_HEAD(&set->sections);
|
||||
perf_config(collect_config, set);
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
static void perf_config_item__delete(struct perf_config_item *item)
|
||||
{
|
||||
zfree(&item->name);
|
||||
zfree(&item->value);
|
||||
free(item);
|
||||
}
|
||||
|
||||
static void perf_config_section__purge(struct perf_config_section *section)
|
||||
{
|
||||
struct perf_config_item *item, *tmp;
|
||||
|
||||
list_for_each_entry_safe(item, tmp, §ion->items, node) {
|
||||
list_del_init(&item->node);
|
||||
perf_config_item__delete(item);
|
||||
}
|
||||
}
|
||||
|
||||
static void perf_config_section__delete(struct perf_config_section *section)
|
||||
{
|
||||
perf_config_section__purge(section);
|
||||
zfree(§ion->name);
|
||||
free(section);
|
||||
}
|
||||
|
||||
static void perf_config_set__purge(struct perf_config_set *set)
|
||||
{
|
||||
struct perf_config_section *section, *tmp;
|
||||
|
||||
list_for_each_entry_safe(section, tmp, &set->sections, node) {
|
||||
list_del_init(§ion->node);
|
||||
perf_config_section__delete(section);
|
||||
}
|
||||
}
|
||||
|
||||
void perf_config_set__delete(struct perf_config_set *set)
|
||||
{
|
||||
perf_config_set__purge(set);
|
||||
free(set);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call this to report error for your variable that should not
|
||||
* get a boolean value (i.e. "[my] var" means "true").
|
||||
@@ -515,49 +706,18 @@ int config_error_nonbool(const char *var)
|
||||
return error("Missing value for '%s'", var);
|
||||
}
|
||||
|
||||
struct buildid_dir_config {
|
||||
char *dir;
|
||||
};
|
||||
|
||||
static int buildid_dir_command_config(const char *var, const char *value,
|
||||
void *data)
|
||||
{
|
||||
struct buildid_dir_config *c = data;
|
||||
const char *v;
|
||||
|
||||
/* same dir for all commands */
|
||||
if (!strcmp(var, "buildid.dir")) {
|
||||
v = perf_config_dirname(var, value);
|
||||
if (!v)
|
||||
return -1;
|
||||
strncpy(c->dir, v, MAXPATHLEN-1);
|
||||
c->dir[MAXPATHLEN-1] = '\0';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check_buildid_dir_config(void)
|
||||
{
|
||||
struct buildid_dir_config c;
|
||||
c.dir = buildid_dir;
|
||||
perf_config(buildid_dir_command_config, &c);
|
||||
}
|
||||
|
||||
void set_buildid_dir(const char *dir)
|
||||
{
|
||||
if (dir)
|
||||
scnprintf(buildid_dir, MAXPATHLEN-1, "%s", dir);
|
||||
|
||||
/* try config file */
|
||||
if (buildid_dir[0] == '\0')
|
||||
check_buildid_dir_config();
|
||||
|
||||
/* default to $HOME/.debug */
|
||||
if (buildid_dir[0] == '\0') {
|
||||
char *v = getenv("HOME");
|
||||
if (v) {
|
||||
char *home = getenv("HOME");
|
||||
|
||||
if (home) {
|
||||
snprintf(buildid_dir, MAXPATHLEN-1, "%s/%s",
|
||||
v, DEBUG_CACHE_DIR);
|
||||
home, DEBUG_CACHE_DIR);
|
||||
} else {
|
||||
strncpy(buildid_dir, DEBUG_CACHE_DIR, MAXPATHLEN-1);
|
||||
}
|
||||
|
26
tools/perf/util/config.h
Normal file
26
tools/perf/util/config.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef __PERF_CONFIG_H
|
||||
#define __PERF_CONFIG_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
struct perf_config_item {
|
||||
char *name;
|
||||
char *value;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct perf_config_section {
|
||||
char *name;
|
||||
struct list_head items;
|
||||
struct list_head node;
|
||||
};
|
||||
|
||||
struct perf_config_set {
|
||||
struct list_head sections;
|
||||
};
|
||||
|
||||
struct perf_config_set *perf_config_set__new(void);
|
||||
void perf_config_set__delete(struct perf_config_set *set);
|
||||
|
||||
#endif /* __PERF_CONFIG_H */
|
@@ -587,3 +587,15 @@ int cpu__setup_cpunode_map(void)
|
||||
closedir(dir1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool cpu_map__has(struct cpu_map *cpus, int cpu)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cpus->nr; ++i) {
|
||||
if (cpus->map[i] == cpu)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@@ -66,4 +66,6 @@ int cpu__get_node(int cpu);
|
||||
int cpu_map__build_map(struct cpu_map *cpus, struct cpu_map **res,
|
||||
int (*f)(struct cpu_map *map, int cpu, void *data),
|
||||
void *data);
|
||||
|
||||
bool cpu_map__has(struct cpu_map *cpus, int cpu);
|
||||
#endif /* __PERF_CPUMAP_H */
|
||||
|
@@ -136,3 +136,44 @@ ssize_t perf_data_file__write(struct perf_data_file *file,
|
||||
{
|
||||
return writen(file->fd, buf, size);
|
||||
}
|
||||
|
||||
int perf_data_file__switch(struct perf_data_file *file,
|
||||
const char *postfix,
|
||||
size_t pos, bool at_exit)
|
||||
{
|
||||
char *new_filepath;
|
||||
int ret;
|
||||
|
||||
if (check_pipe(file))
|
||||
return -EINVAL;
|
||||
if (perf_data_file__is_read(file))
|
||||
return -EINVAL;
|
||||
|
||||
if (asprintf(&new_filepath, "%s.%s", file->path, postfix) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Only fire a warning, don't return error, continue fill
|
||||
* original file.
|
||||
*/
|
||||
if (rename(file->path, new_filepath))
|
||||
pr_warning("Failed to rename %s to %s\n", file->path, new_filepath);
|
||||
|
||||
if (!at_exit) {
|
||||
close(file->fd);
|
||||
ret = perf_data_file__open(file);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (lseek(file->fd, pos, SEEK_SET) == (off_t)-1) {
|
||||
ret = -errno;
|
||||
pr_debug("Failed to lseek to %zu: %s",
|
||||
pos, strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
ret = file->fd;
|
||||
out:
|
||||
free(new_filepath);
|
||||
return ret;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user