Merge f8eb5bd9a8 ("mm: fix build on 32-bit targets without MAX_PHYSMEM_BITS") into android-mainline
Steps on the way to 6.12-rc1 Bug: 367265496 Change-Id: Ifd47581e267cffcb07297153a2a3cf3b0a93d575 Signed-off-by: Matthias Maennich <maennich@google.com>
This commit is contained in:
@@ -6827,6 +6827,51 @@
|
||||
the same thing would happen if it was left off). The irq_handler_entry
|
||||
event, and all events under the "initcall" system.
|
||||
|
||||
Flags can be added to the instance to modify its behavior when it is
|
||||
created. The flags are separated by '^'.
|
||||
|
||||
The available flags are:
|
||||
|
||||
traceoff - Have the tracing instance tracing disabled after it is created.
|
||||
traceprintk - Have trace_printk() write into this trace instance
|
||||
(note, "printk" and "trace_printk" can also be used)
|
||||
|
||||
trace_instance=foo^traceoff^traceprintk,sched,irq
|
||||
|
||||
The flags must come before the defined events.
|
||||
|
||||
If memory has been reserved (see memmap for x86), the instance
|
||||
can use that memory:
|
||||
|
||||
memmap=12M$0x284500000 trace_instance=boot_map@0x284500000:12M
|
||||
|
||||
The above will create a "boot_map" instance that uses the physical
|
||||
memory at 0x284500000 that is 12Megs. The per CPU buffers of that
|
||||
instance will be split up accordingly.
|
||||
|
||||
Alternatively, the memory can be reserved by the reserve_mem option:
|
||||
|
||||
reserve_mem=12M:4096:trace trace_instance=boot_map@trace
|
||||
|
||||
This will reserve 12 megabytes at boot up with a 4096 byte alignment
|
||||
and place the ring buffer in this memory. Note that due to KASLR, the
|
||||
memory may not be the same location each time, which will not preserve
|
||||
the buffer content.
|
||||
|
||||
Also note that the layout of the ring buffer data may change between
|
||||
kernel versions where the validator will fail and reset the ring buffer
|
||||
if the layout is not the same as the previous kernel.
|
||||
|
||||
If the ring buffer is used for persistent bootups and has events enabled,
|
||||
it is recommend to disable tracing so that events from a previous boot do not
|
||||
mix with events of the current boot (unless you are debugging a random crash
|
||||
at boot up).
|
||||
|
||||
reserve_mem=12M:4096:trace trace_instance=boot_map^traceoff^traceprintk@trace,sched,irq
|
||||
|
||||
See also Documentation/trace/debugging.rst
|
||||
|
||||
|
||||
trace_options=[option-list]
|
||||
[FTRACE] Enable or disable tracer options at boot.
|
||||
The option-list is a comma delimited list of options
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
==============================
|
||||
Using the tracer for debugging
|
||||
==============================
|
||||
|
||||
Copyright 2024 Google LLC.
|
||||
|
||||
:Author: Steven Rostedt <rostedt@goodmis.org>
|
||||
:License: The GNU Free Documentation License, Version 1.2
|
||||
(dual licensed under the GPL v2)
|
||||
|
||||
- Written for: 6.12
|
||||
|
||||
Introduction
|
||||
------------
|
||||
The tracing infrastructure can be very useful for debugging the Linux
|
||||
kernel. This document is a place to add various methods of using the tracer
|
||||
for debugging.
|
||||
|
||||
First, make sure that the tracefs file system is mounted::
|
||||
|
||||
$ sudo mount -t tracefs tracefs /sys/kernel/tracing
|
||||
|
||||
|
||||
Using trace_printk()
|
||||
--------------------
|
||||
|
||||
trace_printk() is a very lightweight utility that can be used in any context
|
||||
inside the kernel, with the exception of "noinstr" sections. It can be used
|
||||
in normal, softirq, interrupt and even NMI context. The trace data is
|
||||
written to the tracing ring buffer in a lockless way. To make it even
|
||||
lighter weight, when possible, it will only record the pointer to the format
|
||||
string, and save the raw arguments into the buffer. The format and the
|
||||
arguments will be post processed when the ring buffer is read. This way the
|
||||
trace_printk() format conversions are not done during the hot path, where
|
||||
the trace is being recorded.
|
||||
|
||||
trace_printk() is meant only for debugging, and should never be added into
|
||||
a subsystem of the kernel. If you need debugging traces, add trace events
|
||||
instead. If a trace_printk() is found in the kernel, the following will
|
||||
appear in the dmesg::
|
||||
|
||||
**********************************************************
|
||||
** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **
|
||||
** **
|
||||
** trace_printk() being used. Allocating extra memory. **
|
||||
** **
|
||||
** This means that this is a DEBUG kernel and it is **
|
||||
** unsafe for production use. **
|
||||
** **
|
||||
** If you see this message and you are not debugging **
|
||||
** the kernel, report this immediately to your vendor! **
|
||||
** **
|
||||
** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **
|
||||
**********************************************************
|
||||
|
||||
Debugging kernel crashes
|
||||
------------------------
|
||||
There is various methods of acquiring the state of the system when a kernel
|
||||
crash occurs. This could be from the oops message in printk, or one could
|
||||
use kexec/kdump. But these just show what happened at the time of the crash.
|
||||
It can be very useful in knowing what happened up to the point of the crash.
|
||||
The tracing ring buffer, by default, is a circular buffer than will
|
||||
overwrite older events with newer ones. When a crash happens, the content of
|
||||
the ring buffer will be all the events that lead up to the crash.
|
||||
|
||||
There are several kernel command line parameters that can be used to help in
|
||||
this. The first is "ftrace_dump_on_oops". This will dump the tracing ring
|
||||
buffer when a oops occurs to the console. This can be useful if the console
|
||||
is being logged somewhere. If a serial console is used, it may be prudent to
|
||||
make sure the ring buffer is relatively small, otherwise the dumping of the
|
||||
ring buffer may take several minutes to hours to finish. Here's an example
|
||||
of the kernel command line::
|
||||
|
||||
ftrace_dump_on_oops trace_buf_size=50K
|
||||
|
||||
Note, the tracing buffer is made up of per CPU buffers where each of these
|
||||
buffers is broken up into sub-buffers that are by default PAGE_SIZE. The
|
||||
above trace_buf_size option above sets each of the per CPU buffers to 50K,
|
||||
so, on a machine with 8 CPUs, that's actually 400K total.
|
||||
|
||||
Persistent buffers across boots
|
||||
-------------------------------
|
||||
If the system memory allows it, the tracing ring buffer can be specified at
|
||||
a specific location in memory. If the location is the same across boots and
|
||||
the memory is not modified, the tracing buffer can be retrieved from the
|
||||
following boot. There's two ways to reserve memory for the use of the ring
|
||||
buffer.
|
||||
|
||||
The more reliable way (on x86) is to reserve memory with the "memmap" kernel
|
||||
command line option and then use that memory for the trace_instance. This
|
||||
requires a bit of knowledge of the physical memory layout of the system. The
|
||||
advantage of using this method, is that the memory for the ring buffer will
|
||||
always be the same::
|
||||
|
||||
memmap==12M$0x284500000 trace_instance=boot_map@0x284500000:12M
|
||||
|
||||
The memmap above reserves 12 megabytes of memory at the physical memory
|
||||
location 0x284500000. Then the trace_instance option will create a trace
|
||||
instance "boot_map" at that same location with the same amount of memory
|
||||
reserved. As the ring buffer is broke up into per CPU buffers, the 12
|
||||
megabytes will be broken up evenly between those CPUs. If you have 8 CPUs,
|
||||
each per CPU ring buffer will be 1.5 megabytes in size. Note, that also
|
||||
includes meta data, so the amount of memory actually used by the ring buffer
|
||||
will be slightly smaller.
|
||||
|
||||
Another more generic but less robust way to allocate a ring buffer mapping
|
||||
at boot is with the "reserve_mem" option::
|
||||
|
||||
reserve_mem=12M:4096:trace trace_instance=boot_map@trace
|
||||
|
||||
The reserve_mem option above will find 12 megabytes that are available at
|
||||
boot up, and align it by 4096 bytes. It will label this memory as "trace"
|
||||
that can be used by later command line options.
|
||||
|
||||
The trace_instance option creates a "boot_map" instance and will use the
|
||||
memory reserved by reserve_mem that was labeled as "trace". This method is
|
||||
more generic but may not be as reliable. Due to KASLR, the memory reserved
|
||||
by reserve_mem may not be located at the same location. If this happens,
|
||||
then the ring buffer will not be from the previous boot and will be reset.
|
||||
|
||||
Sometimes, by using a larger alignment, it can keep KASLR from moving things
|
||||
around in such a way that it will move the location of the reserve_mem. By
|
||||
using a larger alignment, you may find better that the buffer is more
|
||||
consistent to where it is placed::
|
||||
|
||||
reserve_mem=12M:0x2000000:trace trace_instance=boot_map@trace
|
||||
|
||||
On boot up, the memory reserved for the ring buffer is validated. It will go
|
||||
through a series of tests to make sure that the ring buffer contains valid
|
||||
data. If it is, it will then set it up to be available to read from the
|
||||
instance. If it fails any of the tests, it will clear the entire ring buffer
|
||||
and initialize it as new.
|
||||
|
||||
The layout of this mapped memory may not be consistent from kernel to
|
||||
kernel, so only the same kernel is guaranteed to work if the mapping is
|
||||
preserved. Switching to a different kernel version may find a different
|
||||
layout and mark the buffer as invalid.
|
||||
|
||||
Using trace_printk() in the boot instance
|
||||
-----------------------------------------
|
||||
By default, the content of trace_printk() goes into the top level tracing
|
||||
instance. But this instance is never preserved across boots. To have the
|
||||
trace_printk() content, and some other internal tracing go to the preserved
|
||||
buffer (like dump stacks), either set the instance to be the trace_printk()
|
||||
destination from the kernel command line, or set it after boot up via the
|
||||
trace_printk_dest option.
|
||||
|
||||
After boot up::
|
||||
|
||||
echo 1 > /sys/kernel/tracing/instances/boot_map/options/trace_printk_dest
|
||||
|
||||
From the kernel command line::
|
||||
|
||||
reserve_mem=12M:4096:trace trace_instance=boot_map^traceprintk^traceoff@trace
|
||||
|
||||
If setting it from the kernel command line, it is recommended to also
|
||||
disable tracing with the "traceoff" flag, and enable tracing after boot up.
|
||||
Otherwise the trace from the most recent boot will be mixed with the trace
|
||||
from the previous boot, and may make it confusing to read.
|
||||
@@ -1186,6 +1186,18 @@ Here are the available options:
|
||||
trace_printk
|
||||
Can disable trace_printk() from writing into the buffer.
|
||||
|
||||
trace_printk_dest
|
||||
Set to have trace_printk() and similar internal tracing functions
|
||||
write into this instance. Note, only one trace instance can have
|
||||
this set. By setting this flag, it clears the trace_printk_dest flag
|
||||
of the instance that had it set previously. By default, the top
|
||||
level trace has this set, and will get it set again if another
|
||||
instance has it set then clears it.
|
||||
|
||||
This flag cannot be cleared by the top level instance, as it is the
|
||||
default instance. The only way the top level instance has this flag
|
||||
cleared, is by it being set in another instance.
|
||||
|
||||
annotate
|
||||
It is sometimes confusing when the CPU buffers are full
|
||||
and one CPU buffer had a lot of events recently, thus
|
||||
|
||||
@@ -54,7 +54,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
|
||||
struct vm_area_struct *vma;
|
||||
struct mm_struct *mm = current->mm;
|
||||
static struct vm_special_mapping vdso_mapping = {
|
||||
name = "[vdso]",
|
||||
.name = "[vdso]",
|
||||
};
|
||||
|
||||
if (mmap_write_lock_killable(mm))
|
||||
|
||||
@@ -53,6 +53,17 @@ static inline unsigned long __untagged_addr_remote(struct mm_struct *mm,
|
||||
*/
|
||||
#define valid_user_address(x) ((__force long)(x) >= 0)
|
||||
|
||||
/*
|
||||
* Masking the user address is an alternative to a conditional
|
||||
* user_access_begin that can avoid the fencing. This only works
|
||||
* for dense accesses starting at the address.
|
||||
*/
|
||||
#define mask_user_address(x) ((typeof(x))((long)(x)|((long)(x)>>63)))
|
||||
#define masked_user_access_begin(x) ({ \
|
||||
__auto_type __masked_ptr = (x); \
|
||||
__masked_ptr = mask_user_address(__masked_ptr); \
|
||||
__uaccess_begin(); __masked_ptr; })
|
||||
|
||||
/*
|
||||
* User pointers can have tag bits on x86-64. This scheme tolerates
|
||||
* arbitrary values in those bits rather then masking them off.
|
||||
|
||||
+3
-1
@@ -777,7 +777,9 @@ static inline int get_sigset_argpack(struct sigset_argpack *to,
|
||||
{
|
||||
// the path is hot enough for overhead of copy_from_user() to matter
|
||||
if (from) {
|
||||
if (!user_read_access_begin(from, sizeof(*from)))
|
||||
if (can_do_masked_user_access())
|
||||
from = masked_user_access_begin(from);
|
||||
else if (!user_read_access_begin(from, sizeof(*from)))
|
||||
return -EFAULT;
|
||||
unsafe_get_user(to->p, &from->p, Efault);
|
||||
unsafe_get_user(to->size, &from->size, Efault);
|
||||
|
||||
+1
-1
@@ -101,7 +101,7 @@ extern int mmap_rnd_compat_bits __read_mostly;
|
||||
# ifdef MAX_PHYSMEM_BITS
|
||||
# define PHYSMEM_END ((1ULL << MAX_PHYSMEM_BITS) - 1)
|
||||
# else
|
||||
# define PHYSMEM_END (-1ULL)
|
||||
# define PHYSMEM_END (((phys_addr_t)-1)&~(1ULL<<63))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -89,6 +89,14 @@ void ring_buffer_discard_commit(struct trace_buffer *buffer,
|
||||
struct trace_buffer *
|
||||
__ring_buffer_alloc(unsigned long size, unsigned flags, struct lock_class_key *key);
|
||||
|
||||
struct trace_buffer *__ring_buffer_alloc_range(unsigned long size, unsigned flags,
|
||||
int order, unsigned long start,
|
||||
unsigned long range_size,
|
||||
struct lock_class_key *key);
|
||||
|
||||
bool ring_buffer_last_boot_delta(struct trace_buffer *buffer, long *text,
|
||||
long *data);
|
||||
|
||||
/*
|
||||
* Because the ring buffer is generic, if other users of the ring buffer get
|
||||
* traced by ftrace, it can produce lockdep warnings. We need to keep each
|
||||
@@ -100,6 +108,18 @@ __ring_buffer_alloc(unsigned long size, unsigned flags, struct lock_class_key *k
|
||||
__ring_buffer_alloc((size), (flags), &__key); \
|
||||
})
|
||||
|
||||
/*
|
||||
* Because the ring buffer is generic, if other users of the ring buffer get
|
||||
* traced by ftrace, it can produce lockdep warnings. We need to keep each
|
||||
* ring buffer's lock class separate.
|
||||
*/
|
||||
#define ring_buffer_alloc_range(size, flags, order, start, range_size) \
|
||||
({ \
|
||||
static struct lock_class_key __key; \
|
||||
__ring_buffer_alloc_range((size), (flags), (order), (start), \
|
||||
(range_size), &__key); \
|
||||
})
|
||||
|
||||
typedef bool (*ring_buffer_cond_fn)(void *data);
|
||||
int ring_buffer_wait(struct trace_buffer *buffer, int cpu, int full,
|
||||
ring_buffer_cond_fn cond, void *data);
|
||||
|
||||
+5
-20
@@ -157,7 +157,7 @@ __seqprop_##lockname##_const_ptr(const seqcount_##lockname##_t *s) \
|
||||
static __always_inline unsigned \
|
||||
__seqprop_##lockname##_sequence(const seqcount_##lockname##_t *s) \
|
||||
{ \
|
||||
unsigned seq = READ_ONCE(s->seqcount.sequence); \
|
||||
unsigned seq = smp_load_acquire(&s->seqcount.sequence); \
|
||||
\
|
||||
if (!IS_ENABLED(CONFIG_PREEMPT_RT)) \
|
||||
return seq; \
|
||||
@@ -170,7 +170,7 @@ __seqprop_##lockname##_sequence(const seqcount_##lockname##_t *s) \
|
||||
* Re-read the sequence counter since the (possibly \
|
||||
* preempted) writer made progress. \
|
||||
*/ \
|
||||
seq = READ_ONCE(s->seqcount.sequence); \
|
||||
seq = smp_load_acquire(&s->seqcount.sequence); \
|
||||
} \
|
||||
\
|
||||
return seq; \
|
||||
@@ -208,7 +208,7 @@ static inline const seqcount_t *__seqprop_const_ptr(const seqcount_t *s)
|
||||
|
||||
static inline unsigned __seqprop_sequence(const seqcount_t *s)
|
||||
{
|
||||
return READ_ONCE(s->sequence);
|
||||
return smp_load_acquire(&s->sequence);
|
||||
}
|
||||
|
||||
static inline bool __seqprop_preemptible(const seqcount_t *s)
|
||||
@@ -263,17 +263,9 @@ SEQCOUNT_LOCKNAME(mutex, struct mutex, true, mutex)
|
||||
#define seqprop_assert(s) __seqprop(s, assert)(s)
|
||||
|
||||
/**
|
||||
* __read_seqcount_begin() - begin a seqcount_t read section w/o barrier
|
||||
* __read_seqcount_begin() - begin a seqcount_t read section
|
||||
* @s: Pointer to seqcount_t or any of the seqcount_LOCKNAME_t variants
|
||||
*
|
||||
* __read_seqcount_begin is like read_seqcount_begin, but has no smp_rmb()
|
||||
* barrier. Callers should ensure that smp_rmb() or equivalent ordering is
|
||||
* provided before actually loading any of the variables that are to be
|
||||
* protected in this critical section.
|
||||
*
|
||||
* Use carefully, only in critical code, and comment how the barrier is
|
||||
* provided.
|
||||
*
|
||||
* Return: count to be passed to read_seqcount_retry()
|
||||
*/
|
||||
#define __read_seqcount_begin(s) \
|
||||
@@ -293,13 +285,7 @@ SEQCOUNT_LOCKNAME(mutex, struct mutex, true, mutex)
|
||||
*
|
||||
* Return: count to be passed to read_seqcount_retry()
|
||||
*/
|
||||
#define raw_read_seqcount_begin(s) \
|
||||
({ \
|
||||
unsigned _seq = __read_seqcount_begin(s); \
|
||||
\
|
||||
smp_rmb(); \
|
||||
_seq; \
|
||||
})
|
||||
#define raw_read_seqcount_begin(s) __read_seqcount_begin(s)
|
||||
|
||||
/**
|
||||
* read_seqcount_begin() - begin a seqcount_t read critical section
|
||||
@@ -328,7 +314,6 @@ SEQCOUNT_LOCKNAME(mutex, struct mutex, true, mutex)
|
||||
({ \
|
||||
unsigned __seq = seqprop_sequence(s); \
|
||||
\
|
||||
smp_rmb(); \
|
||||
kcsan_atomic_next(KCSAN_SEQLOCK_REGION_MAX); \
|
||||
__seq; \
|
||||
})
|
||||
|
||||
@@ -33,6 +33,13 @@
|
||||
})
|
||||
#endif
|
||||
|
||||
#ifdef masked_user_access_begin
|
||||
#define can_do_masked_user_access() 1
|
||||
#else
|
||||
#define can_do_masked_user_access() 0
|
||||
#define masked_user_access_begin(src) NULL
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Architectures should provide two primitives (raw_copy_{to,from}_user())
|
||||
* and get rid of their private instances of copy_{to,from}_user() and
|
||||
|
||||
@@ -13988,21 +13988,19 @@ static void perf_event_setup_cpumask(unsigned int cpu)
|
||||
struct cpumask *pmu_cpumask;
|
||||
unsigned int scope;
|
||||
|
||||
cpumask_set_cpu(cpu, perf_online_mask);
|
||||
|
||||
/*
|
||||
* Early boot stage, the cpumask hasn't been set yet.
|
||||
* The perf_online_<domain>_masks includes the first CPU of each domain.
|
||||
* Always uncondifionally set the boot CPU for the perf_online_<domain>_masks.
|
||||
* Always unconditionally set the boot CPU for the perf_online_<domain>_masks.
|
||||
*/
|
||||
if (!topology_sibling_cpumask(cpu)) {
|
||||
if (cpumask_empty(perf_online_mask)) {
|
||||
for (scope = PERF_PMU_SCOPE_NONE + 1; scope < PERF_PMU_MAX_SCOPE; scope++) {
|
||||
pmu_cpumask = perf_scope_cpumask(scope);
|
||||
if (WARN_ON_ONCE(!pmu_cpumask))
|
||||
continue;
|
||||
cpumask_set_cpu(cpu, pmu_cpumask);
|
||||
}
|
||||
return;
|
||||
goto end;
|
||||
}
|
||||
|
||||
for (scope = PERF_PMU_SCOPE_NONE + 1; scope < PERF_PMU_MAX_SCOPE; scope++) {
|
||||
@@ -14017,6 +14015,8 @@ static void perf_event_setup_cpumask(unsigned int cpu)
|
||||
cpumask_any_and(pmu_cpumask, cpumask) >= nr_cpu_ids)
|
||||
cpumask_set_cpu(cpu, pmu_cpumask);
|
||||
}
|
||||
end:
|
||||
cpumask_set_cpu(cpu, perf_online_mask);
|
||||
}
|
||||
|
||||
int perf_event_init_cpu(unsigned int cpu)
|
||||
|
||||
+842
-105
File diff suppressed because it is too large
Load Diff
+338
-34
@@ -482,7 +482,7 @@ EXPORT_SYMBOL_GPL(unregister_ftrace_export);
|
||||
TRACE_ITER_ANNOTATE | TRACE_ITER_CONTEXT_INFO | \
|
||||
TRACE_ITER_RECORD_CMD | TRACE_ITER_OVERWRITE | \
|
||||
TRACE_ITER_IRQ_INFO | TRACE_ITER_MARKERS | \
|
||||
TRACE_ITER_HASH_PTR)
|
||||
TRACE_ITER_HASH_PTR | TRACE_ITER_TRACE_PRINTK)
|
||||
|
||||
/* trace_options that are only supported by global_trace */
|
||||
#define TOP_LEVEL_TRACE_FLAGS (TRACE_ITER_PRINTK | \
|
||||
@@ -490,7 +490,7 @@ EXPORT_SYMBOL_GPL(unregister_ftrace_export);
|
||||
|
||||
/* trace_flags that are default zero for instances */
|
||||
#define ZEROED_TRACE_FLAGS \
|
||||
(TRACE_ITER_EVENT_FORK | TRACE_ITER_FUNC_FORK)
|
||||
(TRACE_ITER_EVENT_FORK | TRACE_ITER_FUNC_FORK | TRACE_ITER_TRACE_PRINTK)
|
||||
|
||||
/*
|
||||
* The global_trace is the descriptor that holds the top-level tracing
|
||||
@@ -500,6 +500,29 @@ static struct trace_array global_trace = {
|
||||
.trace_flags = TRACE_DEFAULT_FLAGS,
|
||||
};
|
||||
|
||||
static struct trace_array *printk_trace = &global_trace;
|
||||
|
||||
static __always_inline bool printk_binsafe(struct trace_array *tr)
|
||||
{
|
||||
/*
|
||||
* The binary format of traceprintk can cause a crash if used
|
||||
* by a buffer from another boot. Force the use of the
|
||||
* non binary version of trace_printk if the trace_printk
|
||||
* buffer is a boot mapped ring buffer.
|
||||
*/
|
||||
return !(tr->flags & TRACE_ARRAY_FL_BOOT);
|
||||
}
|
||||
|
||||
static void update_printk_trace(struct trace_array *tr)
|
||||
{
|
||||
if (printk_trace == tr)
|
||||
return;
|
||||
|
||||
printk_trace->trace_flags &= ~TRACE_ITER_TRACE_PRINTK;
|
||||
printk_trace = tr;
|
||||
tr->trace_flags |= TRACE_ITER_TRACE_PRINTK;
|
||||
}
|
||||
|
||||
void trace_set_ring_buffer_expanded(struct trace_array *tr)
|
||||
{
|
||||
if (!tr)
|
||||
@@ -1116,7 +1139,7 @@ EXPORT_SYMBOL_GPL(__trace_array_puts);
|
||||
*/
|
||||
int __trace_puts(unsigned long ip, const char *str, int size)
|
||||
{
|
||||
return __trace_array_puts(&global_trace, ip, str, size);
|
||||
return __trace_array_puts(printk_trace, ip, str, size);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__trace_puts);
|
||||
|
||||
@@ -1127,6 +1150,7 @@ EXPORT_SYMBOL_GPL(__trace_puts);
|
||||
*/
|
||||
int __trace_bputs(unsigned long ip, const char *str)
|
||||
{
|
||||
struct trace_array *tr = READ_ONCE(printk_trace);
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_buffer *buffer;
|
||||
struct bputs_entry *entry;
|
||||
@@ -1134,14 +1158,17 @@ int __trace_bputs(unsigned long ip, const char *str)
|
||||
int size = sizeof(struct bputs_entry);
|
||||
int ret = 0;
|
||||
|
||||
if (!(global_trace.trace_flags & TRACE_ITER_PRINTK))
|
||||
if (!printk_binsafe(tr))
|
||||
return __trace_puts(ip, str, strlen(str));
|
||||
|
||||
if (!(tr->trace_flags & TRACE_ITER_PRINTK))
|
||||
return 0;
|
||||
|
||||
if (unlikely(tracing_selftest_running || tracing_disabled))
|
||||
return 0;
|
||||
|
||||
trace_ctx = tracing_gen_ctx();
|
||||
buffer = global_trace.array_buffer.buffer;
|
||||
buffer = tr->array_buffer.buffer;
|
||||
|
||||
ring_buffer_nest_start(buffer);
|
||||
event = __trace_buffer_lock_reserve(buffer, TRACE_BPUTS, size,
|
||||
@@ -1154,7 +1181,7 @@ int __trace_bputs(unsigned long ip, const char *str)
|
||||
entry->str = str;
|
||||
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
ftrace_trace_stack(&global_trace, buffer, trace_ctx, 4, NULL);
|
||||
ftrace_trace_stack(tr, buffer, trace_ctx, 4, NULL);
|
||||
|
||||
ret = 1;
|
||||
out:
|
||||
@@ -3019,7 +3046,7 @@ void trace_dump_stack(int skip)
|
||||
/* Skip 1 to skip this function. */
|
||||
skip++;
|
||||
#endif
|
||||
__ftrace_trace_stack(global_trace.array_buffer.buffer,
|
||||
__ftrace_trace_stack(printk_trace->array_buffer.buffer,
|
||||
tracing_gen_ctx(), skip, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_dump_stack);
|
||||
@@ -3238,12 +3265,15 @@ int trace_vbprintk(unsigned long ip, const char *fmt, va_list args)
|
||||
struct trace_event_call *call = &event_bprint;
|
||||
struct ring_buffer_event *event;
|
||||
struct trace_buffer *buffer;
|
||||
struct trace_array *tr = &global_trace;
|
||||
struct trace_array *tr = READ_ONCE(printk_trace);
|
||||
struct bprint_entry *entry;
|
||||
unsigned int trace_ctx;
|
||||
char *tbuffer;
|
||||
int len = 0, size;
|
||||
|
||||
if (!printk_binsafe(tr))
|
||||
return trace_vprintk(ip, fmt, args);
|
||||
|
||||
if (unlikely(tracing_selftest_running || tracing_disabled))
|
||||
return 0;
|
||||
|
||||
@@ -3336,7 +3366,7 @@ __trace_array_vprintk(struct trace_buffer *buffer,
|
||||
memcpy(&entry->buf, tbuffer, len + 1);
|
||||
if (!call_filter_check_discard(call, entry, buffer, event)) {
|
||||
__buffer_unlock_commit(buffer, event);
|
||||
ftrace_trace_stack(&global_trace, buffer, trace_ctx, 6, NULL);
|
||||
ftrace_trace_stack(printk_trace, buffer, trace_ctx, 6, NULL);
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -3432,7 +3462,7 @@ int trace_array_printk_buf(struct trace_buffer *buffer,
|
||||
int ret;
|
||||
va_list ap;
|
||||
|
||||
if (!(global_trace.trace_flags & TRACE_ITER_PRINTK))
|
||||
if (!(printk_trace->trace_flags & TRACE_ITER_PRINTK))
|
||||
return 0;
|
||||
|
||||
va_start(ap, fmt);
|
||||
@@ -3444,7 +3474,7 @@ int trace_array_printk_buf(struct trace_buffer *buffer,
|
||||
__printf(2, 0)
|
||||
int trace_vprintk(unsigned long ip, const char *fmt, va_list args)
|
||||
{
|
||||
return trace_array_vprintk(&global_trace, ip, fmt, args);
|
||||
return trace_array_vprintk(printk_trace, ip, fmt, args);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(trace_vprintk);
|
||||
|
||||
@@ -3665,8 +3695,11 @@ static void test_can_verify(void)
|
||||
void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
|
||||
va_list ap)
|
||||
{
|
||||
long text_delta = iter->tr->text_delta;
|
||||
long data_delta = iter->tr->data_delta;
|
||||
const char *p = fmt;
|
||||
const char *str;
|
||||
bool good;
|
||||
int i, j;
|
||||
|
||||
if (WARN_ON_ONCE(!fmt))
|
||||
@@ -3685,7 +3718,10 @@ void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
|
||||
|
||||
j = 0;
|
||||
|
||||
/* We only care about %s and variants */
|
||||
/*
|
||||
* We only care about %s and variants
|
||||
* as well as %p[sS] if delta is non-zero
|
||||
*/
|
||||
for (i = 0; p[i]; i++) {
|
||||
if (i + 1 >= iter->fmt_size) {
|
||||
/*
|
||||
@@ -3714,6 +3750,11 @@ void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
|
||||
}
|
||||
if (p[i+j] == 's')
|
||||
break;
|
||||
|
||||
if (text_delta && p[i+1] == 'p' &&
|
||||
((p[i+2] == 's' || p[i+2] == 'S')))
|
||||
break;
|
||||
|
||||
star = false;
|
||||
}
|
||||
j = 0;
|
||||
@@ -3727,6 +3768,24 @@ void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
|
||||
iter->fmt[i] = '\0';
|
||||
trace_seq_vprintf(&iter->seq, iter->fmt, ap);
|
||||
|
||||
/* Add delta to %pS pointers */
|
||||
if (p[i+1] == 'p') {
|
||||
unsigned long addr;
|
||||
char fmt[4];
|
||||
|
||||
fmt[0] = '%';
|
||||
fmt[1] = 'p';
|
||||
fmt[2] = p[i+2]; /* Either %ps or %pS */
|
||||
fmt[3] = '\0';
|
||||
|
||||
addr = va_arg(ap, unsigned long);
|
||||
addr += text_delta;
|
||||
trace_seq_printf(&iter->seq, fmt, (void *)addr);
|
||||
|
||||
p += i + 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If iter->seq is full, the above call no longer guarantees
|
||||
* that ap is in sync with fmt processing, and further calls
|
||||
@@ -3745,6 +3804,14 @@ void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
|
||||
/* The ap now points to the string data of the %s */
|
||||
str = va_arg(ap, const char *);
|
||||
|
||||
good = trace_safe_str(iter, str, star, len);
|
||||
|
||||
/* Could be from the last boot */
|
||||
if (data_delta && !good) {
|
||||
str += data_delta;
|
||||
good = trace_safe_str(iter, str, star, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* If you hit this warning, it is likely that the
|
||||
* trace event in question used %s on a string that
|
||||
@@ -3754,8 +3821,7 @@ void trace_check_vprintf(struct trace_iterator *iter, const char *fmt,
|
||||
* instead. See samples/trace_events/trace-events-sample.h
|
||||
* for reference.
|
||||
*/
|
||||
if (WARN_ONCE(!trace_safe_str(iter, str, star, len),
|
||||
"fmt: '%s' current_buffer: '%s'",
|
||||
if (WARN_ONCE(!good, "fmt: '%s' current_buffer: '%s'",
|
||||
fmt, seq_buf_str(&iter->seq.seq))) {
|
||||
int ret;
|
||||
|
||||
@@ -4917,6 +4983,11 @@ static int tracing_open(struct inode *inode, struct file *file)
|
||||
static bool
|
||||
trace_ok_for_array(struct tracer *t, struct trace_array *tr)
|
||||
{
|
||||
#ifdef CONFIG_TRACER_SNAPSHOT
|
||||
/* arrays with mapped buffer range do not have snapshots */
|
||||
if (tr->range_addr_start && t->use_max_tr)
|
||||
return false;
|
||||
#endif
|
||||
return (tr->flags & TRACE_ARRAY_FL_GLOBAL) || t->allow_instances;
|
||||
}
|
||||
|
||||
@@ -5009,7 +5080,7 @@ static int show_traces_open(struct inode *inode, struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_traces_release(struct inode *inode, struct file *file)
|
||||
static int tracing_seq_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct trace_array *tr = inode->i_private;
|
||||
|
||||
@@ -5050,7 +5121,7 @@ static const struct file_operations show_traces_fops = {
|
||||
.open = show_traces_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = show_traces_release,
|
||||
.release = tracing_seq_release,
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
@@ -5235,7 +5306,8 @@ int trace_keep_overwrite(struct tracer *tracer, u32 mask, int set)
|
||||
int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled)
|
||||
{
|
||||
if ((mask == TRACE_ITER_RECORD_TGID) ||
|
||||
(mask == TRACE_ITER_RECORD_CMD))
|
||||
(mask == TRACE_ITER_RECORD_CMD) ||
|
||||
(mask == TRACE_ITER_TRACE_PRINTK))
|
||||
lockdep_assert_held(&event_mutex);
|
||||
|
||||
/* do nothing if flag is already set */
|
||||
@@ -5247,6 +5319,25 @@ int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled)
|
||||
if (tr->current_trace->flag_changed(tr, mask, !!enabled))
|
||||
return -EINVAL;
|
||||
|
||||
if (mask == TRACE_ITER_TRACE_PRINTK) {
|
||||
if (enabled) {
|
||||
update_printk_trace(tr);
|
||||
} else {
|
||||
/*
|
||||
* The global_trace cannot clear this.
|
||||
* It's flag only gets cleared if another instance sets it.
|
||||
*/
|
||||
if (printk_trace == &global_trace)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* An instance must always have it set.
|
||||
* by default, that's the global_trace instane.
|
||||
*/
|
||||
if (printk_trace == tr)
|
||||
update_printk_trace(&global_trace);
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled)
|
||||
tr->trace_flags |= mask;
|
||||
else
|
||||
@@ -6032,6 +6123,18 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void update_last_data(struct trace_array *tr)
|
||||
{
|
||||
if (!tr->text_delta && !tr->data_delta)
|
||||
return;
|
||||
|
||||
/* Clear old data */
|
||||
tracing_reset_online_cpus(&tr->array_buffer);
|
||||
|
||||
/* Using current data now */
|
||||
tr->text_delta = 0;
|
||||
tr->data_delta = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tracing_update_buffers - used by tracing facility to expand ring buffers
|
||||
@@ -6049,6 +6152,9 @@ int tracing_update_buffers(struct trace_array *tr)
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
|
||||
update_last_data(tr);
|
||||
|
||||
if (!tr->ring_buffer_expanded)
|
||||
ret = __tracing_resize_ring_buffer(tr, trace_buf_size,
|
||||
RING_BUFFER_ALL_CPUS);
|
||||
@@ -6104,6 +6210,8 @@ int tracing_set_tracer(struct trace_array *tr, const char *buf)
|
||||
|
||||
mutex_lock(&trace_types_lock);
|
||||
|
||||
update_last_data(tr);
|
||||
|
||||
if (!tr->ring_buffer_expanded) {
|
||||
ret = __tracing_resize_ring_buffer(tr, trace_buf_size,
|
||||
RING_BUFFER_ALL_CPUS);
|
||||
@@ -6851,6 +6959,37 @@ tracing_total_entries_read(struct file *filp, char __user *ubuf,
|
||||
return simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
tracing_last_boot_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos)
|
||||
{
|
||||
struct trace_array *tr = filp->private_data;
|
||||
struct seq_buf seq;
|
||||
char buf[64];
|
||||
|
||||
seq_buf_init(&seq, buf, 64);
|
||||
|
||||
seq_buf_printf(&seq, "text delta:\t%ld\n", tr->text_delta);
|
||||
seq_buf_printf(&seq, "data delta:\t%ld\n", tr->data_delta);
|
||||
|
||||
return simple_read_from_buffer(ubuf, cnt, ppos, buf, seq_buf_used(&seq));
|
||||
}
|
||||
|
||||
static int tracing_buffer_meta_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct trace_array *tr = inode->i_private;
|
||||
int cpu = tracing_get_cpu(inode);
|
||||
int ret;
|
||||
|
||||
ret = tracing_check_open_get_tr(tr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ring_buffer_meta_seq_init(filp, tr->array_buffer.buffer, cpu);
|
||||
if (ret < 0)
|
||||
__trace_array_put(tr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
tracing_free_buffer_write(struct file *filp, const char __user *ubuf,
|
||||
size_t cnt, loff_t *ppos)
|
||||
@@ -7427,6 +7566,13 @@ static const struct file_operations tracing_entries_fops = {
|
||||
.release = tracing_release_generic_tr,
|
||||
};
|
||||
|
||||
static const struct file_operations tracing_buffer_meta_fops = {
|
||||
.open = tracing_buffer_meta_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = tracing_seq_release,
|
||||
};
|
||||
|
||||
static const struct file_operations tracing_total_entries_fops = {
|
||||
.open = tracing_open_generic_tr,
|
||||
.read = tracing_total_entries_read,
|
||||
@@ -7467,6 +7613,13 @@ static const struct file_operations trace_time_stamp_mode_fops = {
|
||||
.release = tracing_single_release_tr,
|
||||
};
|
||||
|
||||
static const struct file_operations last_boot_fops = {
|
||||
.open = tracing_open_generic_tr,
|
||||
.read = tracing_last_boot_read,
|
||||
.llseek = generic_file_llseek,
|
||||
.release = tracing_release_generic_tr,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_TRACER_SNAPSHOT
|
||||
static const struct file_operations snapshot_fops = {
|
||||
.open = tracing_snapshot_open,
|
||||
@@ -8659,12 +8812,17 @@ tracing_init_tracefs_percpu(struct trace_array *tr, long cpu)
|
||||
trace_create_cpu_file("buffer_size_kb", TRACE_MODE_READ, d_cpu,
|
||||
tr, cpu, &tracing_entries_fops);
|
||||
|
||||
if (tr->range_addr_start)
|
||||
trace_create_cpu_file("buffer_meta", TRACE_MODE_READ, d_cpu,
|
||||
tr, cpu, &tracing_buffer_meta_fops);
|
||||
#ifdef CONFIG_TRACER_SNAPSHOT
|
||||
trace_create_cpu_file("snapshot", TRACE_MODE_WRITE, d_cpu,
|
||||
tr, cpu, &snapshot_fops);
|
||||
if (!tr->range_addr_start) {
|
||||
trace_create_cpu_file("snapshot", TRACE_MODE_WRITE, d_cpu,
|
||||
tr, cpu, &snapshot_fops);
|
||||
|
||||
trace_create_cpu_file("snapshot_raw", TRACE_MODE_READ, d_cpu,
|
||||
tr, cpu, &snapshot_raw_fops);
|
||||
trace_create_cpu_file("snapshot_raw", TRACE_MODE_READ, d_cpu,
|
||||
tr, cpu, &snapshot_raw_fops);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -9201,7 +9359,21 @@ allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, int size
|
||||
|
||||
buf->tr = tr;
|
||||
|
||||
buf->buffer = ring_buffer_alloc(size, rb_flags);
|
||||
if (tr->range_addr_start && tr->range_addr_size) {
|
||||
buf->buffer = ring_buffer_alloc_range(size, rb_flags, 0,
|
||||
tr->range_addr_start,
|
||||
tr->range_addr_size);
|
||||
|
||||
ring_buffer_last_boot_delta(buf->buffer,
|
||||
&tr->text_delta, &tr->data_delta);
|
||||
/*
|
||||
* This is basically the same as a mapped buffer,
|
||||
* with the same restrictions.
|
||||
*/
|
||||
tr->mapped++;
|
||||
} else {
|
||||
buf->buffer = ring_buffer_alloc(size, rb_flags);
|
||||
}
|
||||
if (!buf->buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -9238,6 +9410,10 @@ static int allocate_trace_buffers(struct trace_array *tr, int size)
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_TRACER_MAX_TRACE
|
||||
/* Fix mapped buffer trace arrays do not have snapshot buffers */
|
||||
if (tr->range_addr_start)
|
||||
return 0;
|
||||
|
||||
ret = allocate_trace_buffer(tr, &tr->max_buffer,
|
||||
allocate_snapshot ? size : 1);
|
||||
if (MEM_FAIL(ret, "Failed to allocate trace buffer\n")) {
|
||||
@@ -9338,7 +9514,9 @@ static int trace_array_create_dir(struct trace_array *tr)
|
||||
}
|
||||
|
||||
static struct trace_array *
|
||||
trace_array_create_systems(const char *name, const char *systems)
|
||||
trace_array_create_systems(const char *name, const char *systems,
|
||||
unsigned long range_addr_start,
|
||||
unsigned long range_addr_size)
|
||||
{
|
||||
struct trace_array *tr;
|
||||
int ret;
|
||||
@@ -9364,6 +9542,10 @@ trace_array_create_systems(const char *name, const char *systems)
|
||||
goto out_free_tr;
|
||||
}
|
||||
|
||||
/* Only for boot up memory mapped ring buffers */
|
||||
tr->range_addr_start = range_addr_start;
|
||||
tr->range_addr_size = range_addr_size;
|
||||
|
||||
tr->trace_flags = global_trace.trace_flags & ~ZEROED_TRACE_FLAGS;
|
||||
|
||||
cpumask_copy(tr->tracing_cpumask, cpu_all_mask);
|
||||
@@ -9421,7 +9603,7 @@ trace_array_create_systems(const char *name, const char *systems)
|
||||
|
||||
static struct trace_array *trace_array_create(const char *name)
|
||||
{
|
||||
return trace_array_create_systems(name, NULL);
|
||||
return trace_array_create_systems(name, NULL, 0, 0);
|
||||
}
|
||||
|
||||
static int instance_mkdir(const char *name)
|
||||
@@ -9446,6 +9628,31 @@ out_unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u64 map_pages(u64 start, u64 size)
|
||||
{
|
||||
struct page **pages;
|
||||
phys_addr_t page_start;
|
||||
unsigned int page_count;
|
||||
unsigned int i;
|
||||
void *vaddr;
|
||||
|
||||
page_count = DIV_ROUND_UP(size, PAGE_SIZE);
|
||||
|
||||
page_start = start;
|
||||
pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL);
|
||||
if (!pages)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < page_count; i++) {
|
||||
phys_addr_t addr = page_start + i * PAGE_SIZE;
|
||||
pages[i] = pfn_to_page(addr >> PAGE_SHIFT);
|
||||
}
|
||||
vaddr = vmap(pages, page_count, VM_MAP, PAGE_KERNEL);
|
||||
kfree(pages);
|
||||
|
||||
return (u64)(unsigned long)vaddr;
|
||||
}
|
||||
|
||||
/**
|
||||
* trace_array_get_by_name - Create/Lookup a trace array, given its name.
|
||||
* @name: The name of the trace array to be looked up/created.
|
||||
@@ -9475,7 +9682,7 @@ struct trace_array *trace_array_get_by_name(const char *name, const char *system
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
tr = trace_array_create_systems(name, systems);
|
||||
tr = trace_array_create_systems(name, systems, 0, 0);
|
||||
|
||||
if (IS_ERR(tr))
|
||||
tr = NULL;
|
||||
@@ -9505,6 +9712,9 @@ static int __remove_instance(struct trace_array *tr)
|
||||
set_tracer_flag(tr, 1 << i, 0);
|
||||
}
|
||||
|
||||
if (printk_trace == tr)
|
||||
update_printk_trace(&global_trace);
|
||||
|
||||
tracing_set_nop(tr);
|
||||
clear_ftrace_function_probes(tr);
|
||||
event_trace_del_tracer(tr);
|
||||
@@ -9667,10 +9877,15 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)
|
||||
if (ftrace_create_function_files(tr, d_tracer))
|
||||
MEM_FAIL(1, "Could not allocate function filter files");
|
||||
|
||||
if (tr->range_addr_start) {
|
||||
trace_create_file("last_boot_info", TRACE_MODE_READ, d_tracer,
|
||||
tr, &last_boot_fops);
|
||||
#ifdef CONFIG_TRACER_SNAPSHOT
|
||||
trace_create_file("snapshot", TRACE_MODE_WRITE, d_tracer,
|
||||
tr, &snapshot_fops);
|
||||
} else {
|
||||
trace_create_file("snapshot", TRACE_MODE_WRITE, d_tracer,
|
||||
tr, &snapshot_fops);
|
||||
#endif
|
||||
}
|
||||
|
||||
trace_create_file("error_log", TRACE_MODE_WRITE, d_tracer,
|
||||
tr, &tracing_err_log_fops);
|
||||
@@ -10290,6 +10505,7 @@ __init static void enable_instances(void)
|
||||
{
|
||||
struct trace_array *tr;
|
||||
char *curr_str;
|
||||
char *name;
|
||||
char *str;
|
||||
char *tok;
|
||||
|
||||
@@ -10298,19 +10514,107 @@ __init static void enable_instances(void)
|
||||
str = boot_instance_info;
|
||||
|
||||
while ((curr_str = strsep(&str, "\t"))) {
|
||||
phys_addr_t start = 0;
|
||||
phys_addr_t size = 0;
|
||||
unsigned long addr = 0;
|
||||
bool traceprintk = false;
|
||||
bool traceoff = false;
|
||||
char *flag_delim;
|
||||
char *addr_delim;
|
||||
|
||||
tok = strsep(&curr_str, ",");
|
||||
|
||||
if (IS_ENABLED(CONFIG_TRACER_MAX_TRACE))
|
||||
do_allocate_snapshot(tok);
|
||||
flag_delim = strchr(tok, '^');
|
||||
addr_delim = strchr(tok, '@');
|
||||
|
||||
tr = trace_array_get_by_name(tok, NULL);
|
||||
if (!tr) {
|
||||
pr_warn("Failed to create instance buffer %s\n", curr_str);
|
||||
if (addr_delim)
|
||||
*addr_delim++ = '\0';
|
||||
|
||||
if (flag_delim)
|
||||
*flag_delim++ = '\0';
|
||||
|
||||
name = tok;
|
||||
|
||||
if (flag_delim) {
|
||||
char *flag;
|
||||
|
||||
while ((flag = strsep(&flag_delim, "^"))) {
|
||||
if (strcmp(flag, "traceoff") == 0) {
|
||||
traceoff = true;
|
||||
} else if ((strcmp(flag, "printk") == 0) ||
|
||||
(strcmp(flag, "traceprintk") == 0) ||
|
||||
(strcmp(flag, "trace_printk") == 0)) {
|
||||
traceprintk = true;
|
||||
} else {
|
||||
pr_info("Tracing: Invalid instance flag '%s' for %s\n",
|
||||
flag, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tok = addr_delim;
|
||||
if (tok && isdigit(*tok)) {
|
||||
start = memparse(tok, &tok);
|
||||
if (!start) {
|
||||
pr_warn("Tracing: Invalid boot instance address for %s\n",
|
||||
name);
|
||||
continue;
|
||||
}
|
||||
if (*tok != ':') {
|
||||
pr_warn("Tracing: No size specified for instance %s\n", name);
|
||||
continue;
|
||||
}
|
||||
tok++;
|
||||
size = memparse(tok, &tok);
|
||||
if (!size) {
|
||||
pr_warn("Tracing: Invalid boot instance size for %s\n",
|
||||
name);
|
||||
continue;
|
||||
}
|
||||
} else if (tok) {
|
||||
if (!reserve_mem_find_by_name(tok, &start, &size)) {
|
||||
start = 0;
|
||||
pr_warn("Failed to map boot instance %s to %s\n", name, tok);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (start) {
|
||||
addr = map_pages(start, size);
|
||||
if (addr) {
|
||||
pr_info("Tracing: mapped boot instance %s at physical memory %pa of size 0x%lx\n",
|
||||
name, &start, (unsigned long)size);
|
||||
} else {
|
||||
pr_warn("Tracing: Failed to map boot instance %s\n", name);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
/* Only non mapped buffers have snapshot buffers */
|
||||
if (IS_ENABLED(CONFIG_TRACER_MAX_TRACE))
|
||||
do_allocate_snapshot(name);
|
||||
}
|
||||
|
||||
tr = trace_array_create_systems(name, NULL, addr, size);
|
||||
if (IS_ERR(tr)) {
|
||||
pr_warn("Tracing: Failed to create instance buffer %s\n", curr_str);
|
||||
continue;
|
||||
}
|
||||
/* Allow user space to delete it */
|
||||
trace_array_put(tr);
|
||||
|
||||
if (traceoff)
|
||||
tracer_tracing_off(tr);
|
||||
|
||||
if (traceprintk)
|
||||
update_printk_trace(tr);
|
||||
|
||||
/*
|
||||
* If start is set, then this is a mapped buffer, and
|
||||
* cannot be deleted by user space, so keep the reference
|
||||
* to it.
|
||||
*/
|
||||
if (start)
|
||||
tr->flags |= TRACE_ARRAY_FL_BOOT;
|
||||
else
|
||||
trace_array_put(tr);
|
||||
|
||||
while ((tok = strsep(&curr_str, ","))) {
|
||||
early_enable_events(tr, tok, true);
|
||||
|
||||
+12
-2
@@ -336,7 +336,6 @@ struct trace_array {
|
||||
bool allocated_snapshot;
|
||||
spinlock_t snapshot_trigger_lock;
|
||||
unsigned int snapshot;
|
||||
unsigned int mapped;
|
||||
unsigned long max_latency;
|
||||
#ifdef CONFIG_FSNOTIFY
|
||||
struct dentry *d_max_latency;
|
||||
@@ -344,6 +343,13 @@ struct trace_array {
|
||||
struct irq_work fsnotify_irqwork;
|
||||
#endif
|
||||
#endif
|
||||
/* The below is for memory mapped ring buffer */
|
||||
unsigned int mapped;
|
||||
unsigned long range_addr_start;
|
||||
unsigned long range_addr_size;
|
||||
long text_delta;
|
||||
long data_delta;
|
||||
|
||||
struct trace_pid_list __rcu *filtered_pids;
|
||||
struct trace_pid_list __rcu *filtered_no_pids;
|
||||
/*
|
||||
@@ -423,7 +429,8 @@ struct trace_array {
|
||||
};
|
||||
|
||||
enum {
|
||||
TRACE_ARRAY_FL_GLOBAL = (1 << 0)
|
||||
TRACE_ARRAY_FL_GLOBAL = BIT(0),
|
||||
TRACE_ARRAY_FL_BOOT = BIT(1),
|
||||
};
|
||||
|
||||
extern struct list_head ftrace_trace_arrays;
|
||||
@@ -644,6 +651,8 @@ trace_buffer_lock_reserve(struct trace_buffer *buffer,
|
||||
unsigned long len,
|
||||
unsigned int trace_ctx);
|
||||
|
||||
int ring_buffer_meta_seq_init(struct file *file, struct trace_buffer *buffer, int cpu);
|
||||
|
||||
struct trace_entry *tracing_get_trace_entry(struct trace_array *tr,
|
||||
struct trace_array_cpu *data);
|
||||
|
||||
@@ -1312,6 +1321,7 @@ extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf,
|
||||
C(IRQ_INFO, "irq-info"), \
|
||||
C(MARKERS, "markers"), \
|
||||
C(EVENT_FORK, "event-fork"), \
|
||||
C(TRACE_PRINTK, "trace_printk_dest"), \
|
||||
C(PAUSE_ON_TRACE, "pause-on-trace"), \
|
||||
C(HASH_PTR, "hash-ptr"), /* Print hashed pointer */ \
|
||||
FUNCTION_FLAGS \
|
||||
|
||||
@@ -544,6 +544,8 @@ print_graph_irq(struct trace_iterator *iter, unsigned long addr,
|
||||
struct trace_seq *s = &iter->seq;
|
||||
struct trace_entry *ent = iter->ent;
|
||||
|
||||
addr += iter->tr->text_delta;
|
||||
|
||||
if (addr < (unsigned long)__irqentry_text_start ||
|
||||
addr >= (unsigned long)__irqentry_text_end)
|
||||
return;
|
||||
@@ -710,6 +712,7 @@ print_graph_entry_leaf(struct trace_iterator *iter,
|
||||
struct ftrace_graph_ret *graph_ret;
|
||||
struct ftrace_graph_ent *call;
|
||||
unsigned long long duration;
|
||||
unsigned long func;
|
||||
int cpu = iter->cpu;
|
||||
int i;
|
||||
|
||||
@@ -717,6 +720,8 @@ print_graph_entry_leaf(struct trace_iterator *iter,
|
||||
call = &entry->graph_ent;
|
||||
duration = graph_ret->rettime - graph_ret->calltime;
|
||||
|
||||
func = call->func + iter->tr->text_delta;
|
||||
|
||||
if (data) {
|
||||
struct fgraph_cpu_data *cpu_data;
|
||||
|
||||
@@ -747,10 +752,10 @@ print_graph_entry_leaf(struct trace_iterator *iter,
|
||||
* enabled.
|
||||
*/
|
||||
if (flags & __TRACE_GRAPH_PRINT_RETVAL)
|
||||
print_graph_retval(s, graph_ret->retval, true, (void *)call->func,
|
||||
print_graph_retval(s, graph_ret->retval, true, (void *)func,
|
||||
!!(flags & TRACE_GRAPH_PRINT_RETVAL_HEX));
|
||||
else
|
||||
trace_seq_printf(s, "%ps();\n", (void *)call->func);
|
||||
trace_seq_printf(s, "%ps();\n", (void *)func);
|
||||
|
||||
print_graph_irq(iter, graph_ret->func, TRACE_GRAPH_RET,
|
||||
cpu, iter->ent->pid, flags);
|
||||
@@ -766,6 +771,7 @@ print_graph_entry_nested(struct trace_iterator *iter,
|
||||
struct ftrace_graph_ent *call = &entry->graph_ent;
|
||||
struct fgraph_data *data = iter->private;
|
||||
struct trace_array *tr = iter->tr;
|
||||
unsigned long func;
|
||||
int i;
|
||||
|
||||
if (data) {
|
||||
@@ -788,7 +794,9 @@ print_graph_entry_nested(struct trace_iterator *iter,
|
||||
for (i = 0; i < call->depth * TRACE_GRAPH_INDENT; i++)
|
||||
trace_seq_putc(s, ' ');
|
||||
|
||||
trace_seq_printf(s, "%ps() {\n", (void *)call->func);
|
||||
func = call->func + iter->tr->text_delta;
|
||||
|
||||
trace_seq_printf(s, "%ps() {\n", (void *)func);
|
||||
|
||||
if (trace_seq_has_overflowed(s))
|
||||
return TRACE_TYPE_PARTIAL_LINE;
|
||||
@@ -863,6 +871,8 @@ check_irq_entry(struct trace_iterator *iter, u32 flags,
|
||||
int *depth_irq;
|
||||
struct fgraph_data *data = iter->private;
|
||||
|
||||
addr += iter->tr->text_delta;
|
||||
|
||||
/*
|
||||
* If we are either displaying irqs, or we got called as
|
||||
* a graph event and private data does not exist,
|
||||
@@ -990,11 +1000,14 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
|
||||
unsigned long long duration = trace->rettime - trace->calltime;
|
||||
struct fgraph_data *data = iter->private;
|
||||
struct trace_array *tr = iter->tr;
|
||||
unsigned long func;
|
||||
pid_t pid = ent->pid;
|
||||
int cpu = iter->cpu;
|
||||
int func_match = 1;
|
||||
int i;
|
||||
|
||||
func = trace->func + iter->tr->text_delta;
|
||||
|
||||
if (check_irq_return(iter, flags, trace->depth))
|
||||
return TRACE_TYPE_HANDLED;
|
||||
|
||||
@@ -1033,7 +1046,7 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
|
||||
* function-retval option is enabled.
|
||||
*/
|
||||
if (flags & __TRACE_GRAPH_PRINT_RETVAL) {
|
||||
print_graph_retval(s, trace->retval, false, (void *)trace->func,
|
||||
print_graph_retval(s, trace->retval, false, (void *)func,
|
||||
!!(flags & TRACE_GRAPH_PRINT_RETVAL_HEX));
|
||||
} else {
|
||||
/*
|
||||
@@ -1046,7 +1059,7 @@ print_graph_return(struct ftrace_graph_ret *trace, struct trace_seq *s,
|
||||
if (func_match && !(flags & TRACE_GRAPH_PRINT_TAIL))
|
||||
trace_seq_puts(s, "}\n");
|
||||
else
|
||||
trace_seq_printf(s, "} /* %ps */\n", (void *)trace->func);
|
||||
trace_seq_printf(s, "} /* %ps */\n", (void *)func);
|
||||
}
|
||||
|
||||
/* Overrun */
|
||||
|
||||
@@ -990,8 +990,11 @@ enum print_line_t trace_nop_print(struct trace_iterator *iter, int flags,
|
||||
}
|
||||
|
||||
static void print_fn_trace(struct trace_seq *s, unsigned long ip,
|
||||
unsigned long parent_ip, int flags)
|
||||
unsigned long parent_ip, long delta, int flags)
|
||||
{
|
||||
ip += delta;
|
||||
parent_ip += delta;
|
||||
|
||||
seq_print_ip_sym(s, ip, flags);
|
||||
|
||||
if ((flags & TRACE_ITER_PRINT_PARENT) && parent_ip) {
|
||||
@@ -1009,7 +1012,7 @@ static enum print_line_t trace_fn_trace(struct trace_iterator *iter, int flags,
|
||||
|
||||
trace_assign_type(field, iter->ent);
|
||||
|
||||
print_fn_trace(s, field->ip, field->parent_ip, flags);
|
||||
print_fn_trace(s, field->ip, field->parent_ip, iter->tr->text_delta, flags);
|
||||
trace_seq_putc(s, '\n');
|
||||
|
||||
return trace_handle_return(s);
|
||||
@@ -1230,6 +1233,7 @@ static enum print_line_t trace_stack_print(struct trace_iterator *iter,
|
||||
struct trace_seq *s = &iter->seq;
|
||||
unsigned long *p;
|
||||
unsigned long *end;
|
||||
long delta = iter->tr->text_delta;
|
||||
|
||||
trace_assign_type(field, iter->ent);
|
||||
end = (unsigned long *)((long)iter->ent + iter->ent_size);
|
||||
@@ -1242,7 +1246,7 @@ static enum print_line_t trace_stack_print(struct trace_iterator *iter,
|
||||
break;
|
||||
|
||||
trace_seq_puts(s, " => ");
|
||||
seq_print_ip_sym(s, *p, flags);
|
||||
seq_print_ip_sym(s, (*p) + delta, flags);
|
||||
trace_seq_putc(s, '\n');
|
||||
}
|
||||
|
||||
@@ -1587,10 +1591,13 @@ static enum print_line_t trace_print_print(struct trace_iterator *iter,
|
||||
{
|
||||
struct print_entry *field;
|
||||
struct trace_seq *s = &iter->seq;
|
||||
unsigned long ip;
|
||||
|
||||
trace_assign_type(field, iter->ent);
|
||||
|
||||
seq_print_ip_sym(s, field->ip, flags);
|
||||
ip = field->ip + iter->tr->text_delta;
|
||||
|
||||
seq_print_ip_sym(s, ip, flags);
|
||||
trace_seq_printf(s, ": %s", field->buf);
|
||||
|
||||
return trace_handle_return(s);
|
||||
@@ -1674,7 +1681,7 @@ trace_func_repeats_print(struct trace_iterator *iter, int flags,
|
||||
|
||||
trace_assign_type(field, iter->ent);
|
||||
|
||||
print_fn_trace(s, field->ip, field->parent_ip, flags);
|
||||
print_fn_trace(s, field->ip, field->parent_ip, iter->tr->text_delta, flags);
|
||||
trace_seq_printf(s, " (repeats: %u, last_ts:", field->count);
|
||||
trace_print_time(s, iter,
|
||||
iter->ts - FUNC_REPEATS_GET_DELTA_TS(field));
|
||||
|
||||
@@ -120,6 +120,15 @@ long strncpy_from_user(char *dst, const char __user *src, long count)
|
||||
if (unlikely(count <= 0))
|
||||
return 0;
|
||||
|
||||
if (can_do_masked_user_access()) {
|
||||
long retval;
|
||||
|
||||
src = masked_user_access_begin(src);
|
||||
retval = do_strncpy_from_user(dst, src, count, count);
|
||||
user_read_access_end();
|
||||
return retval;
|
||||
}
|
||||
|
||||
max_addr = TASK_SIZE_MAX;
|
||||
src_addr = (unsigned long)untagged_addr(src);
|
||||
if (likely(src_addr < max_addr)) {
|
||||
|
||||
@@ -96,6 +96,15 @@ long strnlen_user(const char __user *str, long count)
|
||||
if (unlikely(count <= 0))
|
||||
return 0;
|
||||
|
||||
if (can_do_masked_user_access()) {
|
||||
long retval;
|
||||
|
||||
str = masked_user_access_begin(str);
|
||||
retval = do_strnlen_user(str, count, count);
|
||||
user_read_access_end();
|
||||
return retval;
|
||||
}
|
||||
|
||||
max_addr = TASK_SIZE_MAX;
|
||||
src_addr = (unsigned long)untagged_addr(str);
|
||||
if (likely(src_addr < max_addr)) {
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
hostprogs := fixdep
|
||||
|
||||
fixdep-y := fixdep.o
|
||||
@@ -43,12 +43,5 @@ ifneq ($(wildcard $(TMP_O)),)
|
||||
$(Q)$(MAKE) -C feature OUTPUT=$(TMP_O) clean >/dev/null
|
||||
endif
|
||||
|
||||
$(OUTPUT)fixdep-in.o: FORCE
|
||||
$(Q)$(MAKE) $(build)=fixdep
|
||||
|
||||
$(OUTPUT)fixdep: $(OUTPUT)fixdep-in.o
|
||||
$(QUIET_LINK)$(HOSTCC) $(KBUILD_HOSTLDFLAGS) -o $@ $<
|
||||
|
||||
FORCE:
|
||||
|
||||
.PHONY: FORCE
|
||||
$(OUTPUT)fixdep: $(srctree)/tools/build/fixdep.c
|
||||
$(QUIET_CC)$(HOSTCC) $(KBUILD_HOSTCFLAGS) $(KBUILD_HOSTLDFLAGS) -o $@ $<
|
||||
|
||||
@@ -100,7 +100,6 @@ FEATURE_TESTS_EXTRA := \
|
||||
libunwind-debug-frame-aarch64 \
|
||||
cxx \
|
||||
llvm \
|
||||
llvm-version \
|
||||
clang \
|
||||
libbpf \
|
||||
libbpf-btf__load_from_kernel_by_id \
|
||||
@@ -136,6 +135,7 @@ FEATURE_DISPLAY ?= \
|
||||
libunwind \
|
||||
libdw-dwarf-unwind \
|
||||
libcapstone \
|
||||
llvm-perf \
|
||||
zlib \
|
||||
lzma \
|
||||
get_cpuid \
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
build := -f $(srctree)/tools/build/Makefile.build dir=. obj
|
||||
|
||||
# More than just $(Q), we sometimes want to suppress all command output from a
|
||||
# recursive make -- even the 'up to date' printout.
|
||||
ifeq ($(V),1)
|
||||
Q ?=
|
||||
SILENT_MAKE = +$(Q)$(MAKE)
|
||||
else
|
||||
Q ?= @
|
||||
SILENT_MAKE = +$(Q)$(MAKE) --silent
|
||||
endif
|
||||
|
||||
fixdep:
|
||||
$(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= $(OUTPUT)fixdep
|
||||
$(SILENT_MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= $(OUTPUT)fixdep
|
||||
|
||||
fixdep-clean:
|
||||
$(Q)$(MAKE) -C $(srctree)/tools/build clean
|
||||
|
||||
@@ -73,7 +73,7 @@ FILES= \
|
||||
test-libopencsd.bin \
|
||||
test-clang.bin \
|
||||
test-llvm.bin \
|
||||
test-llvm-version.bin \
|
||||
test-llvm-perf.bin \
|
||||
test-libaio.bin \
|
||||
test-libzstd.bin \
|
||||
test-clang-bpf-co-re.bin \
|
||||
@@ -388,9 +388,12 @@ $(OUTPUT)test-llvm.bin:
|
||||
$(shell $(LLVM_CONFIG) --system-libs) \
|
||||
> $(@:.bin=.make.output) 2>&1
|
||||
|
||||
$(OUTPUT)test-llvm-version.bin:
|
||||
$(BUILDXX) -std=gnu++17 \
|
||||
-I$(shell $(LLVM_CONFIG) --includedir) \
|
||||
$(OUTPUT)test-llvm-perf.bin:
|
||||
$(BUILDXX) -std=gnu++17 \
|
||||
-I$(shell $(LLVM_CONFIG) --includedir) \
|
||||
-L$(shell $(LLVM_CONFIG) --libdir) \
|
||||
$(shell $(LLVM_CONFIG) --libs Core BPF) \
|
||||
$(shell $(LLVM_CONFIG) --system-libs) \
|
||||
> $(@:.bin=.make.output) 2>&1
|
||||
|
||||
$(OUTPUT)test-clang.bin:
|
||||
|
||||
@@ -134,10 +134,6 @@
|
||||
#undef main
|
||||
#endif
|
||||
|
||||
#define main main_test_libcapstone
|
||||
# include "test-libcapstone.c"
|
||||
#undef main
|
||||
|
||||
#define main main_test_lzma
|
||||
# include "test-lzma.c"
|
||||
#undef main
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "llvm/Support/ManagedStatic.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
|
||||
#if LLVM_VERSION_MAJOR < 13
|
||||
# error "Perf requires llvm-devel/llvm-dev version 13 or greater"
|
||||
#endif
|
||||
|
||||
int main()
|
||||
{
|
||||
llvm::errs() << "Hello World!\n";
|
||||
llvm::llvm_shutdown();
|
||||
return 0;
|
||||
}
|
||||
@@ -49,12 +49,21 @@
|
||||
* Interpretation of the PERF_RECORD_AUX_OUTPUT_HW_ID payload.
|
||||
* Used to associate a CPU with the CoreSight Trace ID.
|
||||
* [07:00] - Trace ID - uses 8 bits to make value easy to read in file.
|
||||
* [59:08] - Unused (SBZ)
|
||||
* [63:60] - Version
|
||||
* [39:08] - Sink ID - as reported in /sys/bus/event_source/devices/cs_etm/sinks/
|
||||
* Added in minor version 1.
|
||||
* [55:40] - Unused (SBZ)
|
||||
* [59:56] - Minor Version - previously existing fields are compatible with
|
||||
* all minor versions.
|
||||
* [63:60] - Major Version - previously existing fields mean different things
|
||||
* in new major versions.
|
||||
*/
|
||||
#define CS_AUX_HW_ID_TRACE_ID_MASK GENMASK_ULL(7, 0)
|
||||
#define CS_AUX_HW_ID_VERSION_MASK GENMASK_ULL(63, 60)
|
||||
#define CS_AUX_HW_ID_SINK_ID_MASK GENMASK_ULL(39, 8)
|
||||
|
||||
#define CS_AUX_HW_ID_CURR_VERSION 0
|
||||
#define CS_AUX_HW_ID_MINOR_VERSION_MASK GENMASK_ULL(59, 56)
|
||||
#define CS_AUX_HW_ID_MAJOR_VERSION_MASK GENMASK_ULL(63, 60)
|
||||
|
||||
#define CS_AUX_HW_ID_MAJOR_VERSION 0
|
||||
#define CS_AUX_HW_ID_MINOR_VERSION 1
|
||||
|
||||
#endif
|
||||
|
||||
@@ -46,5 +46,7 @@ extern char * __must_check skip_spaces(const char *);
|
||||
|
||||
extern char *strim(char *);
|
||||
|
||||
extern void remove_spaces(char *s);
|
||||
|
||||
extern void *memchr_inv(const void *start, int c, size_t bytes);
|
||||
#endif /* _TOOLS_LINUX_STRING_H_ */
|
||||
|
||||
@@ -31,11 +31,7 @@ CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
|
||||
CFLAGS += -ggdb3 -Wall -Wextra -std=gnu99 -U_FORTIFY_SOURCE -fPIC
|
||||
|
||||
ifeq ($(DEBUG),0)
|
||||
ifeq ($(CC_NO_CLANG), 0)
|
||||
CFLAGS += -O3
|
||||
else
|
||||
CFLAGS += -O6
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(DEBUG),0)
|
||||
|
||||
@@ -69,7 +69,7 @@ char *get_tracing_file(const char *name)
|
||||
{
|
||||
char *file;
|
||||
|
||||
if (asprintf(&file, "%s/%s", tracing_path_mount(), name) < 0)
|
||||
if (asprintf(&file, "%s%s", tracing_path_mount(), name) < 0)
|
||||
return NULL;
|
||||
|
||||
return file;
|
||||
|
||||
@@ -5,3 +5,4 @@ TAGS
|
||||
tags
|
||||
cscope.*
|
||||
/bpf_helper_defs.h
|
||||
fixdep
|
||||
|
||||
+10
-3
@@ -108,6 +108,8 @@ MAKEOVERRIDES=
|
||||
|
||||
all:
|
||||
|
||||
OUTPUT ?= ./
|
||||
OUTPUT := $(abspath $(OUTPUT))/
|
||||
export srctree OUTPUT CC LD CFLAGS V
|
||||
include $(srctree)/tools/build/Makefile.include
|
||||
|
||||
@@ -141,7 +143,10 @@ all: fixdep
|
||||
|
||||
all_cmd: $(CMD_TARGETS) check
|
||||
|
||||
$(BPF_IN_SHARED): force $(BPF_GENERATED)
|
||||
$(SHARED_OBJDIR) $(STATIC_OBJDIR):
|
||||
$(Q)mkdir -p $@
|
||||
|
||||
$(BPF_IN_SHARED): force $(BPF_GENERATED) | $(SHARED_OBJDIR)
|
||||
@(test -f ../../include/uapi/linux/bpf.h -a -f ../../../include/uapi/linux/bpf.h && ( \
|
||||
(diff -B ../../include/uapi/linux/bpf.h ../../../include/uapi/linux/bpf.h >/dev/null) || \
|
||||
echo "Warning: Kernel ABI header at 'tools/include/uapi/linux/bpf.h' differs from latest version at 'include/uapi/linux/bpf.h'" >&2 )) || true
|
||||
@@ -151,9 +156,11 @@ $(BPF_IN_SHARED): force $(BPF_GENERATED)
|
||||
@(test -f ../../include/uapi/linux/if_xdp.h -a -f ../../../include/uapi/linux/if_xdp.h && ( \
|
||||
(diff -B ../../include/uapi/linux/if_xdp.h ../../../include/uapi/linux/if_xdp.h >/dev/null) || \
|
||||
echo "Warning: Kernel ABI header at 'tools/include/uapi/linux/if_xdp.h' differs from latest version at 'include/uapi/linux/if_xdp.h'" >&2 )) || true
|
||||
$(SILENT_MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= OUTPUT=$(SHARED_OBJDIR) $(SHARED_OBJDIR)fixdep
|
||||
$(Q)$(MAKE) $(build)=libbpf OUTPUT=$(SHARED_OBJDIR) CFLAGS="$(CFLAGS) $(SHLIB_FLAGS)"
|
||||
|
||||
$(BPF_IN_STATIC): force $(BPF_GENERATED)
|
||||
$(BPF_IN_STATIC): force $(BPF_GENERATED) | $(STATIC_OBJDIR)
|
||||
$(SILENT_MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= OUTPUT=$(STATIC_OBJDIR) $(STATIC_OBJDIR)fixdep
|
||||
$(Q)$(MAKE) $(build)=libbpf OUTPUT=$(STATIC_OBJDIR)
|
||||
|
||||
$(BPF_HELPER_DEFS): $(srctree)/tools/include/uapi/linux/bpf.h
|
||||
@@ -263,7 +270,7 @@ install_pkgconfig: $(PC_FILE)
|
||||
|
||||
install: install_lib install_pkgconfig install_headers
|
||||
|
||||
clean:
|
||||
clean: fixdep-clean
|
||||
$(call QUIET_CLEAN, libbpf) $(RM) -rf $(CMD_TARGETS) \
|
||||
*~ .*.d .*.cmd LIBBPF-CFLAGS $(BPF_GENERATED) \
|
||||
$(SHARED_OBJDIR) $(STATIC_OBJDIR) \
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
libperf.pc
|
||||
libperf.so.*
|
||||
tests-shared
|
||||
tests-static
|
||||
@@ -153,6 +153,19 @@ char *strim(char *s)
|
||||
return skip_spaces(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* remove_spaces - Removes whitespaces from @s
|
||||
*/
|
||||
void remove_spaces(char *s)
|
||||
{
|
||||
char *d = s;
|
||||
|
||||
do {
|
||||
while (*d == ' ')
|
||||
++d;
|
||||
} while ((*s++ = *d++));
|
||||
}
|
||||
|
||||
/**
|
||||
* strreplace - Replace all occurrences of character in string.
|
||||
* @s: The string to operate on.
|
||||
|
||||
@@ -38,10 +38,8 @@ endif
|
||||
|
||||
ifeq ($(DEBUG),1)
|
||||
CFLAGS += -O0
|
||||
else ifeq ($(CC_NO_CLANG), 0)
|
||||
CFLAGS += -O3
|
||||
else
|
||||
CFLAGS += -O6
|
||||
CFLAGS += -O3
|
||||
endif
|
||||
|
||||
# Treat warnings as errors unless directed not to
|
||||
@@ -76,7 +74,7 @@ include $(srctree)/tools/build/Makefile.include
|
||||
|
||||
all: fixdep $(LIBFILE)
|
||||
|
||||
$(SUBCMD_IN): FORCE
|
||||
$(SUBCMD_IN): fixdep FORCE
|
||||
@$(MAKE) $(build)=libsubcmd
|
||||
|
||||
$(LIBFILE): $(SUBCMD_IN)
|
||||
|
||||
@@ -633,10 +633,11 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
|
||||
const char *const subcommands[], const char *usagestr[], int flags)
|
||||
{
|
||||
struct parse_opt_ctx_t ctx;
|
||||
char *buf = NULL;
|
||||
|
||||
/* build usage string if it's not provided */
|
||||
if (subcommands && !usagestr[0]) {
|
||||
char *buf = NULL;
|
||||
|
||||
astrcatf(&buf, "%s %s [<options>] {", subcmd_config.exec_name, argv[0]);
|
||||
|
||||
for (int i = 0; subcommands[i]; i++) {
|
||||
@@ -678,10 +679,7 @@ int parse_options_subcommand(int argc, const char **argv, const struct option *o
|
||||
astrcatf(&error_buf, "unknown switch `%c'", *ctx.opt);
|
||||
usage_with_options(usagestr, options);
|
||||
}
|
||||
if (buf) {
|
||||
usagestr[0] = NULL;
|
||||
free(buf);
|
||||
}
|
||||
|
||||
return parse_options_end(&ctx);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,11 +31,7 @@ CFLAGS := $(EXTRA_WARNINGS) $(EXTRA_CFLAGS)
|
||||
CFLAGS += -ggdb3 -Wall -Wextra -std=gnu11 -U_FORTIFY_SOURCE -fPIC
|
||||
|
||||
ifeq ($(DEBUG),0)
|
||||
ifeq ($(CC_NO_CLANG), 0)
|
||||
CFLAGS += -O3
|
||||
else
|
||||
CFLAGS += -O6
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(DEBUG),0)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
perf-bench-y += builtin-bench.o
|
||||
perf-y += builtin-annotate.o
|
||||
perf-y += builtin-check.o
|
||||
perf-y += builtin-config.o
|
||||
perf-y += builtin-diff.o
|
||||
perf-y += builtin-evlist.o
|
||||
|
||||
@@ -165,6 +165,9 @@ include::itrace.txt[]
|
||||
--type-stat::
|
||||
Show stats for the data type annotation.
|
||||
|
||||
--skip-empty::
|
||||
Do not display empty (or dummy) events.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
perf-check(1)
|
||||
===============
|
||||
|
||||
NAME
|
||||
----
|
||||
perf-check - check if features are present in perf
|
||||
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf check' [<options>]
|
||||
'perf check' {feature <feature_list>} [<options>]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
With no subcommands given, 'perf check' command just prints the command
|
||||
usage on the standard output.
|
||||
|
||||
If the subcommand 'feature' is used, then status of feature is printed
|
||||
on the standard output (unless '-q' is also passed), ie. whether it is
|
||||
compiled-in/built-in or not.
|
||||
Also, 'perf check feature' returns with exit status 0 if the feature
|
||||
is built-in, otherwise returns with exit status 1.
|
||||
|
||||
SUBCOMMANDS
|
||||
-----------
|
||||
|
||||
feature::
|
||||
|
||||
Print whether feature(s) is compiled-in or not, and also returns with an
|
||||
exit status of 0, if passed feature(s) are compiled-in, else 1.
|
||||
|
||||
It expects a feature list as an argument. There can be a single feature
|
||||
name/macro, or multiple features can also be passed as a comma-separated
|
||||
list, in which case the exit status will be 0 only if all of the passed
|
||||
features are compiled-in.
|
||||
|
||||
The feature names/macros are case-insensitive.
|
||||
|
||||
Example Usage:
|
||||
perf check feature libtraceevent
|
||||
perf check feature HAVE_LIBTRACEEVENT
|
||||
perf check feature libtraceevent,bpf
|
||||
|
||||
Supported feature names/macro:
|
||||
aio / HAVE_AIO_SUPPORT
|
||||
bpf / HAVE_LIBBPF_SUPPORT
|
||||
bpf_skeletons / HAVE_BPF_SKEL
|
||||
debuginfod / HAVE_DEBUGINFOD_SUPPORT
|
||||
dwarf / HAVE_DWARF_SUPPORT
|
||||
dwarf_getlocations / HAVE_DWARF_GETLOCATIONS_SUPPORT
|
||||
dwarf-unwind / HAVE_DWARF_UNWIND_SUPPORT
|
||||
auxtrace / HAVE_AUXTRACE_SUPPORT
|
||||
libaudit / HAVE_LIBAUDIT_SUPPORT
|
||||
libbfd / HAVE_LIBBFD_SUPPORT
|
||||
libcapstone / HAVE_LIBCAPSTONE_SUPPORT
|
||||
libcrypto / HAVE_LIBCRYPTO_SUPPORT
|
||||
libdw-dwarf-unwind / HAVE_DWARF_SUPPORT
|
||||
libelf / HAVE_LIBELF_SUPPORT
|
||||
libnuma / HAVE_LIBNUMA_SUPPORT
|
||||
libopencsd / HAVE_CSTRACE_SUPPORT
|
||||
libperl / HAVE_LIBPERL_SUPPORT
|
||||
libpfm4 / HAVE_LIBPFM
|
||||
libpython / HAVE_LIBPYTHON_SUPPORT
|
||||
libslang / HAVE_SLANG_SUPPORT
|
||||
libtraceevent / HAVE_LIBTRACEEVENT
|
||||
libunwind / HAVE_LIBUNWIND_SUPPORT
|
||||
lzma / HAVE_LZMA_SUPPORT
|
||||
numa_num_possible_cpus / HAVE_LIBNUMA_SUPPORT
|
||||
syscall_table / HAVE_SYSCALL_TABLE_SUPPORT
|
||||
zlib / HAVE_ZLIB_SUPPORT
|
||||
zstd / HAVE_ZSTD_SUPPORT
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
-q::
|
||||
--quiet::
|
||||
Do not print any messages or warnings
|
||||
|
||||
This can be used along with subcommands such as 'perf check feature'
|
||||
to hide unnecessary output in test scripts, eg.
|
||||
'perf check feature --quiet libtraceevent'
|
||||
@@ -9,7 +9,7 @@ perf-ftrace - simple wrapper for kernel's ftrace functionality
|
||||
SYNOPSIS
|
||||
--------
|
||||
[verse]
|
||||
'perf ftrace' {trace|latency} <command>
|
||||
'perf ftrace' {trace|latency|profile} <command>
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -23,6 +23,9 @@ kernel's ftrace infrastructure.
|
||||
'perf ftrace latency' calculates execution latency of a given function
|
||||
(optionally with BPF) and display it as a histogram.
|
||||
|
||||
'perf ftrace profile' show a execution profile for each function including
|
||||
total, average, max time and the number of calls.
|
||||
|
||||
The following options apply to perf ftrace.
|
||||
|
||||
COMMON OPTIONS
|
||||
@@ -125,6 +128,7 @@ OPTIONS for 'perf ftrace trace'
|
||||
- verbose - Show process names, PIDs, timestamps, etc.
|
||||
- thresh=<n> - Setup trace duration threshold in microseconds.
|
||||
- depth=<n> - Set max depth for function graph tracer to follow.
|
||||
- tail - Print function name at the end.
|
||||
|
||||
|
||||
OPTIONS for 'perf ftrace latency'
|
||||
@@ -145,6 +149,48 @@ OPTIONS for 'perf ftrace latency'
|
||||
Use nano-second instead of micro-second as a base unit of the histogram.
|
||||
|
||||
|
||||
OPTIONS for 'perf ftrace profile'
|
||||
---------------------------------
|
||||
|
||||
-T::
|
||||
--trace-funcs=::
|
||||
Set function filter on the given function (or a glob pattern).
|
||||
Multiple functions can be given by using this option more than once.
|
||||
The function argument also can be a glob pattern. It will be passed
|
||||
to 'set_ftrace_filter' in tracefs.
|
||||
|
||||
-N::
|
||||
--notrace-funcs=::
|
||||
Do not trace functions given by the argument. Like -T option, this
|
||||
can be used more than once to specify multiple functions (or glob
|
||||
patterns). It will be passed to 'set_ftrace_notrace' in tracefs.
|
||||
|
||||
-G::
|
||||
--graph-funcs=::
|
||||
Set graph filter on the given function (or a glob pattern). This is
|
||||
useful to trace for functions executed from the given function. This
|
||||
can be used more than once to specify multiple functions. It will be
|
||||
passed to 'set_graph_function' in tracefs.
|
||||
|
||||
-g::
|
||||
--nograph-funcs=::
|
||||
Set graph notrace filter on the given function (or a glob pattern).
|
||||
Like -G option, this is useful for the function_graph tracer only and
|
||||
disables tracing for function executed from the given function. This
|
||||
can be used more than once to specify multiple functions. It will be
|
||||
passed to 'set_graph_notrace' in tracefs.
|
||||
|
||||
-m::
|
||||
--buffer-size::
|
||||
Set the size of per-cpu tracing buffer, <size> is expected to
|
||||
be a number with appended unit character - B/K/M/G.
|
||||
|
||||
-s::
|
||||
--sort=::
|
||||
Sort the result by the given field. Available values are:
|
||||
total, avg, max, count, name. Default is 'total'.
|
||||
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-trace[1]
|
||||
|
||||
@@ -115,9 +115,9 @@ STAT LIVE OPTIONS
|
||||
|
||||
-m::
|
||||
--mmap-pages=::
|
||||
Number of mmap data pages (must be a power of two) or size
|
||||
specification with appended unit character - B/K/M/G. The
|
||||
size is rounded up to have nearest pages power of two value.
|
||||
Number of mmap data pages (must be a power of two) or size
|
||||
specification in bytes with appended unit character - B/K/M/G.
|
||||
The size is rounded up to the nearest power-of-two page value.
|
||||
|
||||
-a::
|
||||
--all-cpus::
|
||||
|
||||
@@ -72,6 +72,7 @@ counted. The following modifiers exist:
|
||||
W - group is weak and will fallback to non-group if not schedulable,
|
||||
e - group or event are exclusive and do not share the PMU
|
||||
b - use BPF aggregration (see perf stat --bpf-counters)
|
||||
R - retire latency value of the event
|
||||
|
||||
The 'p' modifier can be used for specifying how precise the instruction
|
||||
address should be. The 'p' modifier can be specified multiple times:
|
||||
|
||||
@@ -28,15 +28,8 @@ and kernel support is required. See linkperf:perf-arm-spe[1] for a setup guide.
|
||||
Due to the statistical nature of SPE sampling, not every memory operation will
|
||||
be sampled.
|
||||
|
||||
OPTIONS
|
||||
-------
|
||||
<command>...::
|
||||
Any command you can specify in a shell.
|
||||
|
||||
-i::
|
||||
--input=<file>::
|
||||
Input file name.
|
||||
|
||||
COMMON OPTIONS
|
||||
--------------
|
||||
-f::
|
||||
--force::
|
||||
Don't do ownership validation
|
||||
@@ -45,24 +38,9 @@ OPTIONS
|
||||
--type=<type>::
|
||||
Select the memory operation type: load or store (default: load,store)
|
||||
|
||||
-D::
|
||||
--dump-raw-samples::
|
||||
Dump the raw decoded samples on the screen in a format that is easy to parse with
|
||||
one sample per line.
|
||||
|
||||
-x::
|
||||
--field-separator=<separator>::
|
||||
Specify the field separator used when dump raw samples (-D option). By default,
|
||||
The separator is the space character.
|
||||
|
||||
-C::
|
||||
--cpu=<cpu>::
|
||||
Monitor only on the list of CPUs provided. Multiple CPUs can be provided as a
|
||||
comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2. Default
|
||||
is to monitor all CPUS.
|
||||
-U::
|
||||
--hide-unresolved::
|
||||
Only display entries resolved to a symbol.
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose (show counter open errors, etc)
|
||||
|
||||
-p::
|
||||
--phys-data::
|
||||
@@ -73,6 +51,9 @@ OPTIONS
|
||||
|
||||
RECORD OPTIONS
|
||||
--------------
|
||||
<command>...::
|
||||
Any command you can specify in a shell.
|
||||
|
||||
-e::
|
||||
--event <event>::
|
||||
Event selector. Use 'perf mem record -e list' to list available events.
|
||||
@@ -85,14 +66,65 @@ RECORD OPTIONS
|
||||
--all-user::
|
||||
Configure all used events to run in user space.
|
||||
|
||||
-v::
|
||||
--verbose::
|
||||
Be more verbose (show counter open errors, etc)
|
||||
|
||||
--ldlat <n>::
|
||||
Specify desired latency for loads event. Supported on Intel and Arm64
|
||||
processors only. Ignored on other archs.
|
||||
|
||||
REPORT OPTIONS
|
||||
--------------
|
||||
-i::
|
||||
--input=<file>::
|
||||
Input file name.
|
||||
|
||||
-C::
|
||||
--cpu=<cpu>::
|
||||
Monitor only on the list of CPUs provided. Multiple CPUs can be provided as a
|
||||
comma-separated list with no space: 0,1. Ranges of CPUs are specified with -
|
||||
like 0-2. Default is to monitor all CPUS.
|
||||
|
||||
-D::
|
||||
--dump-raw-samples::
|
||||
Dump the raw decoded samples on the screen in a format that is easy to parse with
|
||||
one sample per line.
|
||||
|
||||
-s::
|
||||
--sort=<key>::
|
||||
Group result by given key(s) - multiple keys can be specified
|
||||
in CSV format. The keys are specific to memory samples are:
|
||||
symbol_daddr, symbol_iaddr, dso_daddr, locked, tlb, mem, snoop,
|
||||
dcacheline, phys_daddr, data_page_size, blocked.
|
||||
|
||||
- symbol_daddr: name of data symbol being executed on at the time of sample
|
||||
- symbol_iaddr: name of code symbol being executed on at the time of sample
|
||||
- dso_daddr: name of library or module containing the data being executed
|
||||
on at the time of the sample
|
||||
- locked: whether the bus was locked at the time of the sample
|
||||
- tlb: type of tlb access for the data at the time of the sample
|
||||
- mem: type of memory access for the data at the time of the sample
|
||||
- snoop: type of snoop (if any) for the data at the time of the sample
|
||||
- dcacheline: the cacheline the data address is on at the time of the sample
|
||||
- phys_daddr: physical address of data being executed on at the time of sample
|
||||
- data_page_size: the data page size of data being executed on at the time of sample
|
||||
- blocked: reason of blocked load access for the data at the time of the sample
|
||||
|
||||
And the default sort keys are changed to local_weight, mem, sym, dso,
|
||||
symbol_daddr, dso_daddr, snoop, tlb, locked, blocked, local_ins_lat.
|
||||
|
||||
-T::
|
||||
--type-profile::
|
||||
Show data-type profile result instead of code symbols. This requires
|
||||
the debug information and it will change the default sort keys to:
|
||||
mem, snoop, tlb, type.
|
||||
|
||||
-U::
|
||||
--hide-unresolved::
|
||||
Only display entries resolved to a symbol.
|
||||
|
||||
-x::
|
||||
--field-separator=<separator>::
|
||||
Specify the field separator used when dump raw samples (-D option). By default,
|
||||
The separator is the space character.
|
||||
|
||||
In addition, for report all perf report options are valid, and for record
|
||||
all perf record options.
|
||||
|
||||
|
||||
@@ -273,10 +273,11 @@ OPTIONS
|
||||
-m::
|
||||
--mmap-pages=::
|
||||
Number of mmap data pages (must be a power of two) or size
|
||||
specification with appended unit character - B/K/M/G. The
|
||||
size is rounded up to have nearest pages power of two value.
|
||||
Also, by adding a comma, the number of mmap pages for AUX
|
||||
area tracing can be specified.
|
||||
specification in bytes with appended unit character - B/K/M/G.
|
||||
The size is rounded up to the nearest power-of-two page value.
|
||||
By adding a comma, an additional parameter with the same
|
||||
semantics used for the normal mmap areas can be specified for
|
||||
AUX tracing area.
|
||||
|
||||
-g::
|
||||
Enables call-graph (stack chain/backtrace) recording for both
|
||||
@@ -828,6 +829,11 @@ filtered through the mask provided by -C option.
|
||||
only, as of now. So the applications built without the frame
|
||||
pointer might see bogus addresses.
|
||||
|
||||
--setup-filter=<action>::
|
||||
Prepare BPF filter to be used by regular users. The action should be
|
||||
either "pin" or "unpin". The filter can be used after it's pinned.
|
||||
|
||||
|
||||
include::intel-hybrid.txt[]
|
||||
|
||||
SEE ALSO
|
||||
|
||||
@@ -614,6 +614,7 @@ include::itrace.txt[]
|
||||
'Avg Cycles%' - block average sampled cycles / sum of total block average
|
||||
sampled cycles
|
||||
'Avg Cycles' - block average sampled cycles
|
||||
'Branch Counter' - block branch counter histogram (with -v showing the number)
|
||||
|
||||
--skip-empty::
|
||||
Do not print 0 results in the --stat output.
|
||||
|
||||
@@ -212,6 +212,15 @@ OPTIONS for 'perf sched timehist'
|
||||
--state::
|
||||
Show task state when it switched out.
|
||||
|
||||
--show-prio::
|
||||
Show task priority.
|
||||
|
||||
--prio::
|
||||
Only show events for given task priority(ies). Multiple priorities can be
|
||||
provided as a comma-separated list with no spaces: 0,120. Ranges of
|
||||
priorities are specified with -: 120-129. A combination of both can also be
|
||||
provided: 0,120-129.
|
||||
|
||||
OPTIONS for 'perf sched replay'
|
||||
------------------------------
|
||||
|
||||
|
||||
@@ -134,7 +134,7 @@ OPTIONS
|
||||
srcline, period, iregs, uregs, brstack, brstacksym, flags, bpf-output,
|
||||
brstackinsn, brstackinsnlen, brstackdisasm, brstackoff, callindent, insn, disasm,
|
||||
insnlen, synth, phys_addr, metric, misc, srccode, ipc, data_page_size,
|
||||
code_page_size, ins_lat, machine_pid, vcpu, cgroup, retire_lat,
|
||||
code_page_size, ins_lat, machine_pid, vcpu, cgroup, retire_lat, brcntr,
|
||||
|
||||
Field list can be prepended with the type, trace, sw or hw,
|
||||
to indicate to which event type the field list applies.
|
||||
@@ -369,6 +369,9 @@ OPTIONS
|
||||
--demangle-kernel::
|
||||
Demangle kernel symbol names to human readable form (for C++ kernels).
|
||||
|
||||
--addr2line=<path>::
|
||||
Path to addr2line binary.
|
||||
|
||||
--header
|
||||
Show perf.data header.
|
||||
|
||||
|
||||
@@ -498,6 +498,14 @@ To interpret the results it is usually needed to know on which
|
||||
CPUs the workload runs on. If needed the CPUs can be forced using
|
||||
taskset.
|
||||
|
||||
--record-tpebs::
|
||||
Enable automatic sampling on Intel TPEBS retire_latency events (event with :R
|
||||
modifier). Without this option, perf would not capture dynamic retire_latency
|
||||
at runtime. Currently, a zero value is assigned to the retire_latency event when
|
||||
this option is not set. The TPEBS hardware feature starts from Intel Granite
|
||||
Rapids microarchitecture. This option only exists in X86_64 and is meaningful on
|
||||
Intel platforms with TPEBS feature.
|
||||
|
||||
--td-level::
|
||||
Print the top-down statistics that equal the input level. It allows
|
||||
users to print the interested top-down metrics level instead of the
|
||||
|
||||
@@ -83,8 +83,8 @@ Default is to monitor all CPUS.
|
||||
-m <pages>::
|
||||
--mmap-pages=<pages>::
|
||||
Number of mmap data pages (must be a power of two) or size
|
||||
specification with appended unit character - B/K/M/G. The
|
||||
size is rounded up to have nearest pages power of two value.
|
||||
specification in bytes with appended unit character - B/K/M/G.
|
||||
The size is rounded up to the nearest power-of-two page value.
|
||||
|
||||
-p <pid>::
|
||||
--pid=<pid>::
|
||||
|
||||
@@ -106,8 +106,8 @@ filter out the startup phase of the program, which is often very different.
|
||||
-m::
|
||||
--mmap-pages=::
|
||||
Number of mmap data pages (must be a power of two) or size
|
||||
specification with appended unit character - B/K/M/G. The
|
||||
size is rounded up to have nearest pages power of two value.
|
||||
specification in bytes with appended unit character - B/K/M/G.
|
||||
The size is rounded up to the nearest power-of-two page value.
|
||||
|
||||
-C::
|
||||
--cpu::
|
||||
|
||||
@@ -325,6 +325,36 @@ other four level 2 metrics by subtracting corresponding metrics as below.
|
||||
Fetch_Bandwidth = Frontend_Bound - Fetch_Latency
|
||||
Core_Bound = Backend_Bound - Memory_Bound
|
||||
|
||||
TPEBS in TopDown
|
||||
================
|
||||
|
||||
TPEBS (Timed PEBS) is one of the new Intel PMU features provided since Granite
|
||||
Rapids microarchitecture. The TPEBS feature adds a 16 bit retire_latency field
|
||||
in the Basic Info group of the PEBS record. It records the Core cycles since the
|
||||
retirement of the previous instruction to the retirement of current instruction.
|
||||
Please refer to Section 8.4.1 of "Intel® Architecture Instruction Set Extensions
|
||||
Programming Reference" for more details about this feature. Because this feature
|
||||
extends PEBS record, sampling with weight option is required to get the
|
||||
retire_latency value.
|
||||
|
||||
perf record -e event_name -W ...
|
||||
|
||||
In the most recent release of TMA, the metrics begin to use event retire_latency
|
||||
values in some of the metrics’ formulas on processors that support TPEBS feature.
|
||||
For previous generations that do not support TPEBS, the values are static and
|
||||
predefined per processor family by the hardware architects. Due to the diversity
|
||||
of workloads in execution environments, retire_latency values measured at real
|
||||
time are more accurate. Therefore, new TMA metrics that use TPEBS will provide
|
||||
more accurate performance analysis results.
|
||||
|
||||
To support TPEBS in TMA metrics, a new modifier :R on event is added. Perf would
|
||||
capture retire_latency value of required events(event with :R in metric formula)
|
||||
with perf record. The retire_latency value would be used in metric calculation.
|
||||
Currently, this feature is supported through perf stat
|
||||
|
||||
perf stat -M metric_name --record-tpebs ...
|
||||
|
||||
|
||||
|
||||
[1] https://software.intel.com/en-us/top-down-microarchitecture-analysis-method-win
|
||||
[2] https://sites.google.com/site/analysismethods/yasin-pubs
|
||||
|
||||
+7
-1
@@ -51,8 +51,14 @@ else
|
||||
override DEBUG = 0
|
||||
endif
|
||||
|
||||
ifeq ($(JOBS),1)
|
||||
BUILD_TYPE := sequential
|
||||
else
|
||||
BUILD_TYPE := parallel
|
||||
endif
|
||||
|
||||
define print_msg
|
||||
@printf ' BUILD: Doing '\''make \033[33m-j'$(JOBS)'\033[m'\'' parallel build\n'
|
||||
@printf ' BUILD: Doing '\''make \033[33m-j'$(JOBS)'\033[m'\'' $(BUILD_TYPE) build\n'
|
||||
endef
|
||||
|
||||
define make
|
||||
|
||||
+24
-27
@@ -31,14 +31,8 @@ $(call detected_var,SRCARCH)
|
||||
ifneq ($(NO_SYSCALL_TABLE),1)
|
||||
NO_SYSCALL_TABLE := 1
|
||||
|
||||
ifeq ($(SRCARCH),x86)
|
||||
ifeq (${IS_64_BIT}, 1)
|
||||
NO_SYSCALL_TABLE := 0
|
||||
endif
|
||||
else
|
||||
ifeq ($(SRCARCH),$(filter $(SRCARCH),powerpc arm64 s390 mips loongarch))
|
||||
NO_SYSCALL_TABLE := 0
|
||||
endif
|
||||
ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 powerpc arm64 s390 mips loongarch))
|
||||
NO_SYSCALL_TABLE := 0
|
||||
endif
|
||||
|
||||
ifneq ($(NO_SYSCALL_TABLE),1)
|
||||
@@ -55,8 +49,9 @@ endif
|
||||
# Additional ARCH settings for x86
|
||||
ifeq ($(SRCARCH),x86)
|
||||
$(call detected,CONFIG_X86)
|
||||
CFLAGS += -I$(OUTPUT)arch/x86/include/generated
|
||||
ifeq (${IS_64_BIT}, 1)
|
||||
CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT -I$(OUTPUT)arch/x86/include/generated
|
||||
CFLAGS += -DHAVE_ARCH_X86_64_SUPPORT
|
||||
ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S
|
||||
LIBUNWIND_LIBS = -lunwind-x86_64 -lunwind -llzma
|
||||
$(call detected,CONFIG_X86_64)
|
||||
@@ -238,11 +233,7 @@ endif
|
||||
|
||||
ifeq ($(DEBUG),0)
|
||||
CORE_CFLAGS += -DNDEBUG=1
|
||||
ifeq ($(CC_NO_CLANG), 0)
|
||||
CORE_CFLAGS += -O3
|
||||
else
|
||||
CORE_CFLAGS += -O6
|
||||
endif
|
||||
CORE_CFLAGS += -O3
|
||||
else
|
||||
CORE_CFLAGS += -g
|
||||
CXXFLAGS += -g
|
||||
@@ -710,8 +701,8 @@ ifeq ($(BUILD_BPF_SKEL),1)
|
||||
BUILD_BPF_SKEL := 0
|
||||
else
|
||||
CLANG_VERSION := $(shell $(CLANG) --version | head -1 | sed 's/.*clang version \([[:digit:]]\+.[[:digit:]]\+.[[:digit:]]\+\).*/\1/g')
|
||||
ifeq ($(call version-lt3,$(CLANG_VERSION),12.0.1),1)
|
||||
$(warning Warning: Disabled BPF skeletons as reliable BTF generation needs at least $(CLANG) version 12.0.1)
|
||||
ifeq ($(call version-lt3,$(CLANG_VERSION),16.0.6),1)
|
||||
$(warning Warning: Disabled BPF skeletons as at least $(CLANG) version 16.0.6 is reported to be a working setup with the current of BPF based perf features)
|
||||
BUILD_BPF_SKEL := 0
|
||||
endif
|
||||
endif
|
||||
@@ -985,6 +976,23 @@ ifdef BUILD_NONDISTRO
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef NO_LIBLLVM
|
||||
$(call feature_check,llvm-perf)
|
||||
ifeq ($(feature-llvm-perf), 1)
|
||||
CFLAGS += -DHAVE_LIBLLVM_SUPPORT
|
||||
CFLAGS += $(shell $(LLVM_CONFIG) --cflags)
|
||||
CXXFLAGS += -DHAVE_LIBLLVM_SUPPORT
|
||||
CXXFLAGS += $(shell $(LLVM_CONFIG) --cxxflags)
|
||||
LIBLLVM = $(shell $(LLVM_CONFIG) --libs all) $(shell $(LLVM_CONFIG) --system-libs)
|
||||
EXTLIBS += -L$(shell $(LLVM_CONFIG) --libdir) $(LIBLLVM)
|
||||
EXTLIBS += -lstdc++
|
||||
$(call detected,CONFIG_LIBLLVM)
|
||||
else
|
||||
$(warning No libllvm 13+ found, slower source file resolution, please install llvm-devel/llvm-dev)
|
||||
NO_LIBLLVM := 1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef NO_DEMANGLE
|
||||
$(call feature_check,cxa-demangle)
|
||||
ifeq ($(feature-cxa-demangle), 1)
|
||||
@@ -1031,17 +1039,6 @@ ifndef NO_LIBZSTD
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef NO_LIBCAP
|
||||
ifeq ($(feature-libcap), 1)
|
||||
CFLAGS += -DHAVE_LIBCAP_SUPPORT
|
||||
EXTLIBS += -lcap
|
||||
$(call detected,CONFIG_LIBCAP)
|
||||
else
|
||||
$(warning No libcap found, disables capability support, please install libcap-devel/libcap-dev)
|
||||
NO_LIBCAP := 1
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef NO_BACKTRACE
|
||||
ifeq ($(feature-backtrace), 1)
|
||||
CFLAGS += -DHAVE_BACKTRACE_SUPPORT
|
||||
|
||||
@@ -163,6 +163,8 @@ ifneq ($(OUTPUT),)
|
||||
# for flex/bison parsers.
|
||||
VPATH += $(OUTPUT)
|
||||
export VPATH
|
||||
# create symlink to the original source
|
||||
SOURCE := $(shell ln -sf $(srctree)/tools/perf $(OUTPUT)/source)
|
||||
endif
|
||||
|
||||
ifeq ($(V),1)
|
||||
@@ -1140,6 +1142,8 @@ install-tests: all install-gtk
|
||||
$(INSTALL) tests/shell/common/*.pl '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/common'; \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/base_probe'; \
|
||||
$(INSTALL) tests/shell/base_probe/*.sh '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/base_probe'; \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/base_report'; \
|
||||
$(INSTALL) tests/shell/base_probe/*.sh '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/base_report'; \
|
||||
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/coresight' ; \
|
||||
$(INSTALL) tests/shell/coresight/*.sh '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/tests/shell/coresight'
|
||||
$(Q)$(MAKE) -C tests/shell/coresight install-tests
|
||||
@@ -1277,6 +1281,8 @@ clean:: $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clean $(LIBSYMBOL)-clean $(
|
||||
$(OUTPUT)util/intel-pt-decoder/inat-tables.c \
|
||||
$(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \
|
||||
$(OUTPUT)pmu-events/pmu-events.c \
|
||||
$(OUTPUT)pmu-events/test-empty-pmu-events.c \
|
||||
$(OUTPUT)pmu-events/empty-pmu-events.log \
|
||||
$(OUTPUT)pmu-events/metric_test.log \
|
||||
$(OUTPUT)$(fadvise_advice_array) \
|
||||
$(OUTPUT)$(fsconfig_arrays) \
|
||||
|
||||
@@ -643,7 +643,8 @@ static bool cs_etm_is_ete(struct perf_pmu *cs_etm_pmu, struct perf_cpu cpu)
|
||||
|
||||
static __u64 cs_etm_get_legacy_trace_id(struct perf_cpu cpu)
|
||||
{
|
||||
return CORESIGHT_LEGACY_CPU_TRACE_ID(cpu.cpu);
|
||||
/* Wrap at 48 so that invalid trace IDs aren't saved into files. */
|
||||
return CORESIGHT_LEGACY_CPU_TRACE_ID(cpu.cpu % 48);
|
||||
}
|
||||
|
||||
static void cs_etm_save_etmv4_header(__u64 data[], struct auxtrace_record *itr, struct perf_cpu cpu)
|
||||
@@ -654,8 +655,7 @@ static void cs_etm_save_etmv4_header(__u64 data[], struct auxtrace_record *itr,
|
||||
/* Get trace configuration register */
|
||||
data[CS_ETMV4_TRCCONFIGR] = cs_etmv4_get_config(itr);
|
||||
/* traceID set to legacy version, in case new perf running on older system */
|
||||
data[CS_ETMV4_TRCTRACEIDR] = cs_etm_get_legacy_trace_id(cpu) |
|
||||
CORESIGHT_TRACE_ID_UNUSED_FLAG;
|
||||
data[CS_ETMV4_TRCTRACEIDR] = cs_etm_get_legacy_trace_id(cpu);
|
||||
|
||||
/* Get read-only information from sysFS */
|
||||
cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv4_ro[CS_ETMV4_TRCIDR0],
|
||||
@@ -687,7 +687,7 @@ static void cs_etm_save_ete_header(__u64 data[], struct auxtrace_record *itr, st
|
||||
/* Get trace configuration register */
|
||||
data[CS_ETE_TRCCONFIGR] = cs_etmv4_get_config(itr);
|
||||
/* traceID set to legacy version, in case new perf running on older system */
|
||||
data[CS_ETE_TRCTRACEIDR] = cs_etm_get_legacy_trace_id(cpu) | CORESIGHT_TRACE_ID_UNUSED_FLAG;
|
||||
data[CS_ETE_TRCTRACEIDR] = cs_etm_get_legacy_trace_id(cpu);
|
||||
|
||||
/* Get read-only information from sysFS */
|
||||
cs_etm_get_ro(cs_etm_pmu, cpu, metadata_ete_ro[CS_ETE_TRCIDR0], &data[CS_ETE_TRCIDR0]);
|
||||
@@ -743,8 +743,7 @@ static void cs_etm_get_metadata(struct perf_cpu cpu, u32 *offset,
|
||||
/* Get configuration register */
|
||||
info->priv[*offset + CS_ETM_ETMCR] = cs_etm_get_config(itr);
|
||||
/* traceID set to legacy value in case new perf running on old system */
|
||||
info->priv[*offset + CS_ETM_ETMTRACEIDR] = cs_etm_get_legacy_trace_id(cpu) |
|
||||
CORESIGHT_TRACE_ID_UNUSED_FLAG;
|
||||
info->priv[*offset + CS_ETM_ETMTRACEIDR] = cs_etm_get_legacy_trace_id(cpu);
|
||||
/* Get read-only information from sysFS */
|
||||
cs_etm_get_ro(cs_etm_pmu, cpu, metadata_etmv3_ro[CS_ETM_ETMCCER],
|
||||
&info->priv[*offset + CS_ETM_ETMCCER]);
|
||||
@@ -888,7 +887,6 @@ struct auxtrace_record *cs_etm_record_init(int *err)
|
||||
}
|
||||
|
||||
ptr->cs_etm_pmu = cs_etm_pmu;
|
||||
ptr->itr.pmu = cs_etm_pmu;
|
||||
ptr->itr.parse_snapshot_options = cs_etm_parse_snapshot_options;
|
||||
ptr->itr.recording_options = cs_etm_recording_options;
|
||||
ptr->itr.info_priv_size = cs_etm_info_priv_size;
|
||||
|
||||
@@ -23,16 +23,19 @@ void perf_pmu__arch_init(struct perf_pmu *pmu)
|
||||
#ifdef HAVE_AUXTRACE_SUPPORT
|
||||
if (!strcmp(pmu->name, CORESIGHT_ETM_PMU_NAME)) {
|
||||
/* add ETM default config here */
|
||||
pmu->auxtrace = true;
|
||||
pmu->selectable = true;
|
||||
pmu->perf_event_attr_init_default = cs_etm_get_default_config;
|
||||
#if defined(__aarch64__)
|
||||
} else if (strstarts(pmu->name, ARM_SPE_PMU_NAME)) {
|
||||
pmu->auxtrace = true;
|
||||
pmu->selectable = true;
|
||||
pmu->is_uncore = false;
|
||||
pmu->perf_event_attr_init_default = arm_spe_pmu_default_config;
|
||||
if (strstarts(pmu->name, "arm_spe_"))
|
||||
pmu->mem_events = perf_mem_events_arm;
|
||||
} else if (strstarts(pmu->name, HISI_PTT_PMU_NAME)) {
|
||||
pmu->auxtrace = true;
|
||||
pmu->selectable = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ struct arm64_annotate {
|
||||
|
||||
static int arm64_mov__parse(struct arch *arch __maybe_unused,
|
||||
struct ins_operands *ops,
|
||||
struct map_symbol *ms __maybe_unused)
|
||||
struct map_symbol *ms __maybe_unused,
|
||||
struct disasm_line *dl __maybe_unused)
|
||||
{
|
||||
char *s = strchr(ops->raw, ','), *target, *endptr;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/zalloc.h>
|
||||
#include <time.h>
|
||||
|
||||
@@ -132,32 +133,66 @@ static __u64 arm_spe_pmu__sample_period(const struct perf_pmu *arm_spe_pmu)
|
||||
return sample_period;
|
||||
}
|
||||
|
||||
static void arm_spe_setup_evsel(struct evsel *evsel, struct perf_cpu_map *cpus)
|
||||
{
|
||||
u64 bit;
|
||||
|
||||
evsel->core.attr.freq = 0;
|
||||
evsel->core.attr.sample_period = arm_spe_pmu__sample_period(evsel->pmu);
|
||||
evsel->needs_auxtrace_mmap = true;
|
||||
|
||||
/*
|
||||
* To obtain the auxtrace buffer file descriptor, the auxtrace event
|
||||
* must come first.
|
||||
*/
|
||||
evlist__to_front(evsel->evlist, evsel);
|
||||
|
||||
/*
|
||||
* In the case of per-cpu mmaps, sample CPU for AUX event;
|
||||
* also enable the timestamp tracing for samples correlation.
|
||||
*/
|
||||
if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus)) {
|
||||
evsel__set_sample_bit(evsel, CPU);
|
||||
evsel__set_config_if_unset(evsel->pmu, evsel, "ts_enable", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set this only so that perf report knows that SPE generates memory info. It has no effect
|
||||
* on the opening of the event or the SPE data produced.
|
||||
*/
|
||||
evsel__set_sample_bit(evsel, DATA_SRC);
|
||||
|
||||
/*
|
||||
* The PHYS_ADDR flag does not affect the driver behaviour, it is used to
|
||||
* inform that the resulting output's SPE samples contain physical addresses
|
||||
* where applicable.
|
||||
*/
|
||||
bit = perf_pmu__format_bits(evsel->pmu, "pa_enable");
|
||||
if (evsel->core.attr.config & bit)
|
||||
evsel__set_sample_bit(evsel, PHYS_ADDR);
|
||||
}
|
||||
|
||||
static int arm_spe_recording_options(struct auxtrace_record *itr,
|
||||
struct evlist *evlist,
|
||||
struct record_opts *opts)
|
||||
{
|
||||
struct arm_spe_recording *sper =
|
||||
container_of(itr, struct arm_spe_recording, itr);
|
||||
struct perf_pmu *arm_spe_pmu = sper->arm_spe_pmu;
|
||||
struct evsel *evsel, *arm_spe_evsel = NULL;
|
||||
struct evsel *evsel, *tmp;
|
||||
struct perf_cpu_map *cpus = evlist->core.user_requested_cpus;
|
||||
bool privileged = perf_event_paranoid_check(-1);
|
||||
struct evsel *tracking_evsel;
|
||||
int err;
|
||||
u64 bit;
|
||||
|
||||
sper->evlist = evlist;
|
||||
|
||||
evlist__for_each_entry(evlist, evsel) {
|
||||
if (evsel->core.attr.type == arm_spe_pmu->type) {
|
||||
if (arm_spe_evsel) {
|
||||
pr_err("There may be only one " ARM_SPE_PMU_NAME "x event\n");
|
||||
if (evsel__is_aux_event(evsel)) {
|
||||
if (!strstarts(evsel->pmu_name, ARM_SPE_PMU_NAME)) {
|
||||
pr_err("Found unexpected auxtrace event: %s\n",
|
||||
evsel->pmu_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
evsel->core.attr.freq = 0;
|
||||
evsel->core.attr.sample_period = arm_spe_pmu__sample_period(arm_spe_pmu);
|
||||
evsel->needs_auxtrace_mmap = true;
|
||||
arm_spe_evsel = evsel;
|
||||
opts->full_auxtrace = true;
|
||||
}
|
||||
}
|
||||
@@ -222,37 +257,11 @@ static int arm_spe_recording_options(struct auxtrace_record *itr,
|
||||
pr_debug2("%sx snapshot size: %zu\n", ARM_SPE_PMU_NAME,
|
||||
opts->auxtrace_snapshot_size);
|
||||
|
||||
/*
|
||||
* To obtain the auxtrace buffer file descriptor, the auxtrace event
|
||||
* must come first.
|
||||
*/
|
||||
evlist__to_front(evlist, arm_spe_evsel);
|
||||
|
||||
/*
|
||||
* In the case of per-cpu mmaps, sample CPU for AUX event;
|
||||
* also enable the timestamp tracing for samples correlation.
|
||||
*/
|
||||
if (!perf_cpu_map__is_any_cpu_or_is_empty(cpus)) {
|
||||
evsel__set_sample_bit(arm_spe_evsel, CPU);
|
||||
evsel__set_config_if_unset(arm_spe_pmu, arm_spe_evsel,
|
||||
"ts_enable", 1);
|
||||
evlist__for_each_entry_safe(evlist, tmp, evsel) {
|
||||
if (evsel__is_aux_event(evsel))
|
||||
arm_spe_setup_evsel(evsel, cpus);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set this only so that perf report knows that SPE generates memory info. It has no effect
|
||||
* on the opening of the event or the SPE data produced.
|
||||
*/
|
||||
evsel__set_sample_bit(arm_spe_evsel, DATA_SRC);
|
||||
|
||||
/*
|
||||
* The PHYS_ADDR flag does not affect the driver behaviour, it is used to
|
||||
* inform that the resulting output's SPE samples contain physical addresses
|
||||
* where applicable.
|
||||
*/
|
||||
bit = perf_pmu__format_bits(arm_spe_pmu, "pa_enable");
|
||||
if (arm_spe_evsel->core.attr.config & bit)
|
||||
evsel__set_sample_bit(arm_spe_evsel, PHYS_ADDR);
|
||||
|
||||
/* Add dummy event to keep tracking */
|
||||
err = parse_event(evlist, "dummy:u");
|
||||
if (err)
|
||||
@@ -301,12 +310,16 @@ static int arm_spe_snapshot_start(struct auxtrace_record *itr)
|
||||
struct arm_spe_recording *ptr =
|
||||
container_of(itr, struct arm_spe_recording, itr);
|
||||
struct evsel *evsel;
|
||||
int ret = -EINVAL;
|
||||
|
||||
evlist__for_each_entry(ptr->evlist, evsel) {
|
||||
if (evsel->core.attr.type == ptr->arm_spe_pmu->type)
|
||||
return evsel__disable(evsel);
|
||||
if (evsel__is_aux_event(evsel)) {
|
||||
ret = evsel__disable(evsel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int arm_spe_snapshot_finish(struct auxtrace_record *itr)
|
||||
@@ -314,12 +327,16 @@ static int arm_spe_snapshot_finish(struct auxtrace_record *itr)
|
||||
struct arm_spe_recording *ptr =
|
||||
container_of(itr, struct arm_spe_recording, itr);
|
||||
struct evsel *evsel;
|
||||
int ret = -EINVAL;
|
||||
|
||||
evlist__for_each_entry(ptr->evlist, evsel) {
|
||||
if (evsel->core.attr.type == ptr->arm_spe_pmu->type)
|
||||
return evsel__enable(evsel);
|
||||
if (evsel__is_aux_event(evsel)) {
|
||||
ret = evsel__enable(evsel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int arm_spe_alloc_wrapped_array(struct arm_spe_recording *ptr, int idx)
|
||||
@@ -497,7 +514,6 @@ struct auxtrace_record *arm_spe_recording_init(int *err,
|
||||
}
|
||||
|
||||
sper->arm_spe_pmu = arm_spe_pmu;
|
||||
sper->itr.pmu = arm_spe_pmu;
|
||||
sper->itr.snapshot_start = arm_spe_snapshot_start;
|
||||
sper->itr.snapshot_finish = arm_spe_snapshot_finish;
|
||||
sper->itr.find_snapshot = arm_spe_find_snapshot;
|
||||
|
||||
@@ -174,7 +174,6 @@ struct auxtrace_record *hisi_ptt_recording_init(int *err,
|
||||
}
|
||||
|
||||
pttr->hisi_ptt_pmu = hisi_ptt_pmu;
|
||||
pttr->itr.pmu = hisi_ptt_pmu;
|
||||
pttr->itr.recording_options = hisi_ptt_recording_options;
|
||||
pttr->itr.info_priv_size = hisi_ptt_info_priv_size;
|
||||
pttr->itr.info_fill = hisi_ptt_info_fill;
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
* Copyright (C) 2020-2023 Loongson Technology Corporation Limited
|
||||
*/
|
||||
|
||||
static int loongarch_call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms)
|
||||
static int loongarch_call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms,
|
||||
struct disasm_line *dl __maybe_unused)
|
||||
{
|
||||
char *c, *endptr, *tok, *name;
|
||||
struct map *map = ms->map;
|
||||
@@ -51,7 +52,8 @@ static struct ins_ops loongarch_call_ops = {
|
||||
.scnprintf = call__scnprintf,
|
||||
};
|
||||
|
||||
static int loongarch_jump__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms)
|
||||
static int loongarch_jump__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms,
|
||||
struct disasm_line *dl __maybe_unused)
|
||||
{
|
||||
struct map *map = ms->map;
|
||||
struct symbol *sym = ms->sym;
|
||||
|
||||
@@ -49,12 +49,266 @@ static struct ins_ops *powerpc__associate_instruction_ops(struct arch *arch, con
|
||||
return ops;
|
||||
}
|
||||
|
||||
#define PPC_OP(op) (((op) >> 26) & 0x3F)
|
||||
#define PPC_21_30(R) (((R) >> 1) & 0x3ff)
|
||||
#define PPC_22_30(R) (((R) >> 1) & 0x1ff)
|
||||
|
||||
struct insn_offset {
|
||||
const char *name;
|
||||
int value;
|
||||
};
|
||||
|
||||
/*
|
||||
* There are memory instructions with opcode 31 which are
|
||||
* of X Form, Example:
|
||||
* ldx RT,RA,RB
|
||||
* ______________________________________
|
||||
* | 31 | RT | RA | RB | 21 |/|
|
||||
* --------------------------------------
|
||||
* 0 6 11 16 21 30 31
|
||||
*
|
||||
* But all instructions with opcode 31 are not memory.
|
||||
* Example: add RT,RA,RB
|
||||
*
|
||||
* Use bits 21 to 30 to check memory insns with 31 as opcode.
|
||||
* In ins_array below, for ldx instruction:
|
||||
* name => OP_31_XOP_LDX
|
||||
* value => 21
|
||||
*/
|
||||
|
||||
static struct insn_offset ins_array[] = {
|
||||
{ .name = "OP_31_XOP_LXSIWZX", .value = 12, },
|
||||
{ .name = "OP_31_XOP_LWARX", .value = 20, },
|
||||
{ .name = "OP_31_XOP_LDX", .value = 21, },
|
||||
{ .name = "OP_31_XOP_LWZX", .value = 23, },
|
||||
{ .name = "OP_31_XOP_LDUX", .value = 53, },
|
||||
{ .name = "OP_31_XOP_LWZUX", .value = 55, },
|
||||
{ .name = "OP_31_XOP_LXSIWAX", .value = 76, },
|
||||
{ .name = "OP_31_XOP_LDARX", .value = 84, },
|
||||
{ .name = "OP_31_XOP_LBZX", .value = 87, },
|
||||
{ .name = "OP_31_XOP_LVX", .value = 103, },
|
||||
{ .name = "OP_31_XOP_LBZUX", .value = 119, },
|
||||
{ .name = "OP_31_XOP_STXSIWX", .value = 140, },
|
||||
{ .name = "OP_31_XOP_STDX", .value = 149, },
|
||||
{ .name = "OP_31_XOP_STWX", .value = 151, },
|
||||
{ .name = "OP_31_XOP_STDUX", .value = 181, },
|
||||
{ .name = "OP_31_XOP_STWUX", .value = 183, },
|
||||
{ .name = "OP_31_XOP_STBX", .value = 215, },
|
||||
{ .name = "OP_31_XOP_STVX", .value = 231, },
|
||||
{ .name = "OP_31_XOP_STBUX", .value = 247, },
|
||||
{ .name = "OP_31_XOP_LHZX", .value = 279, },
|
||||
{ .name = "OP_31_XOP_LHZUX", .value = 311, },
|
||||
{ .name = "OP_31_XOP_LXVDSX", .value = 332, },
|
||||
{ .name = "OP_31_XOP_LWAX", .value = 341, },
|
||||
{ .name = "OP_31_XOP_LHAX", .value = 343, },
|
||||
{ .name = "OP_31_XOP_LWAUX", .value = 373, },
|
||||
{ .name = "OP_31_XOP_LHAUX", .value = 375, },
|
||||
{ .name = "OP_31_XOP_STHX", .value = 407, },
|
||||
{ .name = "OP_31_XOP_STHUX", .value = 439, },
|
||||
{ .name = "OP_31_XOP_LXSSPX", .value = 524, },
|
||||
{ .name = "OP_31_XOP_LDBRX", .value = 532, },
|
||||
{ .name = "OP_31_XOP_LSWX", .value = 533, },
|
||||
{ .name = "OP_31_XOP_LWBRX", .value = 534, },
|
||||
{ .name = "OP_31_XOP_LFSUX", .value = 567, },
|
||||
{ .name = "OP_31_XOP_LXSDX", .value = 588, },
|
||||
{ .name = "OP_31_XOP_LSWI", .value = 597, },
|
||||
{ .name = "OP_31_XOP_LFDX", .value = 599, },
|
||||
{ .name = "OP_31_XOP_LFDUX", .value = 631, },
|
||||
{ .name = "OP_31_XOP_STXSSPX", .value = 652, },
|
||||
{ .name = "OP_31_XOP_STDBRX", .value = 660, },
|
||||
{ .name = "OP_31_XOP_STXWX", .value = 661, },
|
||||
{ .name = "OP_31_XOP_STWBRX", .value = 662, },
|
||||
{ .name = "OP_31_XOP_STFSX", .value = 663, },
|
||||
{ .name = "OP_31_XOP_STFSUX", .value = 695, },
|
||||
{ .name = "OP_31_XOP_STXSDX", .value = 716, },
|
||||
{ .name = "OP_31_XOP_STSWI", .value = 725, },
|
||||
{ .name = "OP_31_XOP_STFDX", .value = 727, },
|
||||
{ .name = "OP_31_XOP_STFDUX", .value = 759, },
|
||||
{ .name = "OP_31_XOP_LXVW4X", .value = 780, },
|
||||
{ .name = "OP_31_XOP_LHBRX", .value = 790, },
|
||||
{ .name = "OP_31_XOP_LXVD2X", .value = 844, },
|
||||
{ .name = "OP_31_XOP_LFIWAX", .value = 855, },
|
||||
{ .name = "OP_31_XOP_LFIWZX", .value = 887, },
|
||||
{ .name = "OP_31_XOP_STXVW4X", .value = 908, },
|
||||
{ .name = "OP_31_XOP_STHBRX", .value = 918, },
|
||||
{ .name = "OP_31_XOP_STXVD2X", .value = 972, },
|
||||
{ .name = "OP_31_XOP_STFIWX", .value = 983, },
|
||||
};
|
||||
|
||||
/*
|
||||
* Arithmetic instructions which are having opcode as 31.
|
||||
* These instructions are tracked to save the register state
|
||||
* changes. Example:
|
||||
*
|
||||
* lwz r10,264(r3)
|
||||
* add r31, r3, r3
|
||||
* lwz r9, 0(r31)
|
||||
*
|
||||
* Here instruction tracking needs to identify the "add"
|
||||
* instruction and save data type of r3 to r31. If a sample
|
||||
* is hit at next "lwz r9, 0(r31)", by this instruction tracking,
|
||||
* data type of r31 can be resolved.
|
||||
*/
|
||||
static struct insn_offset arithmetic_ins_op_31[] = {
|
||||
{ .name = "SUB_CARRY_XO_FORM", .value = 8, },
|
||||
{ .name = "MUL_HDW_XO_FORM1", .value = 9, },
|
||||
{ .name = "ADD_CARRY_XO_FORM", .value = 10, },
|
||||
{ .name = "MUL_HW_XO_FORM1", .value = 11, },
|
||||
{ .name = "SUB_XO_FORM", .value = 40, },
|
||||
{ .name = "MUL_HDW_XO_FORM", .value = 73, },
|
||||
{ .name = "MUL_HW_XO_FORM", .value = 75, },
|
||||
{ .name = "SUB_EXT_XO_FORM", .value = 136, },
|
||||
{ .name = "ADD_EXT_XO_FORM", .value = 138, },
|
||||
{ .name = "SUB_ZERO_EXT_XO_FORM", .value = 200, },
|
||||
{ .name = "ADD_ZERO_EXT_XO_FORM", .value = 202, },
|
||||
{ .name = "SUB_EXT_XO_FORM2", .value = 232, },
|
||||
{ .name = "MUL_DW_XO_FORM", .value = 233, },
|
||||
{ .name = "ADD_EXT_XO_FORM2", .value = 234, },
|
||||
{ .name = "MUL_W_XO_FORM", .value = 235, },
|
||||
{ .name = "ADD_XO_FORM", .value = 266, },
|
||||
{ .name = "DIV_DW_XO_FORM1", .value = 457, },
|
||||
{ .name = "DIV_W_XO_FORM1", .value = 459, },
|
||||
{ .name = "DIV_DW_XO_FORM", .value = 489, },
|
||||
{ .name = "DIV_W_XO_FORM", .value = 491, },
|
||||
};
|
||||
|
||||
static struct insn_offset arithmetic_two_ops[] = {
|
||||
{ .name = "mulli", .value = 7, },
|
||||
{ .name = "subfic", .value = 8, },
|
||||
{ .name = "addic", .value = 12, },
|
||||
{ .name = "addic.", .value = 13, },
|
||||
{ .name = "addi", .value = 14, },
|
||||
{ .name = "addis", .value = 15, },
|
||||
};
|
||||
|
||||
static int cmp_offset(const void *a, const void *b)
|
||||
{
|
||||
const struct insn_offset *val1 = a;
|
||||
const struct insn_offset *val2 = b;
|
||||
|
||||
return (val1->value - val2->value);
|
||||
}
|
||||
|
||||
static struct ins_ops *check_ppc_insn(struct disasm_line *dl)
|
||||
{
|
||||
int raw_insn = dl->raw.raw_insn;
|
||||
int opcode = PPC_OP(raw_insn);
|
||||
int mem_insn_31 = PPC_21_30(raw_insn);
|
||||
struct insn_offset *ret;
|
||||
struct insn_offset mem_insns_31_opcode = {
|
||||
"OP_31_INSN",
|
||||
mem_insn_31
|
||||
};
|
||||
char name_insn[32];
|
||||
|
||||
/*
|
||||
* Instructions with opcode 32 to 63 are memory
|
||||
* instructions in powerpc
|
||||
*/
|
||||
if ((opcode & 0x20)) {
|
||||
/*
|
||||
* Set name in case of raw instruction to
|
||||
* opcode to be used in insn-stat
|
||||
*/
|
||||
if (!strlen(dl->ins.name)) {
|
||||
sprintf(name_insn, "%d", opcode);
|
||||
dl->ins.name = strdup(name_insn);
|
||||
}
|
||||
return &load_store_ops;
|
||||
} else if (opcode == 31) {
|
||||
/* Check for memory instructions with opcode 31 */
|
||||
ret = bsearch(&mem_insns_31_opcode, ins_array, ARRAY_SIZE(ins_array), sizeof(ins_array[0]), cmp_offset);
|
||||
if (ret) {
|
||||
if (!strlen(dl->ins.name))
|
||||
dl->ins.name = strdup(ret->name);
|
||||
return &load_store_ops;
|
||||
} else {
|
||||
mem_insns_31_opcode.value = PPC_22_30(raw_insn);
|
||||
ret = bsearch(&mem_insns_31_opcode, arithmetic_ins_op_31, ARRAY_SIZE(arithmetic_ins_op_31),
|
||||
sizeof(arithmetic_ins_op_31[0]), cmp_offset);
|
||||
if (ret != NULL)
|
||||
return &arithmetic_ops;
|
||||
/* Bits 21 to 30 has value 444 for "mr" insn ie, OR X form */
|
||||
if (PPC_21_30(raw_insn) == 444)
|
||||
return &arithmetic_ops;
|
||||
}
|
||||
} else {
|
||||
mem_insns_31_opcode.value = opcode;
|
||||
ret = bsearch(&mem_insns_31_opcode, arithmetic_two_ops, ARRAY_SIZE(arithmetic_two_ops),
|
||||
sizeof(arithmetic_two_ops[0]), cmp_offset);
|
||||
if (ret != NULL)
|
||||
return &arithmetic_ops;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Instruction tracking function to track register state moves.
|
||||
* Example sequence:
|
||||
* ld r10,264(r3)
|
||||
* mr r31,r3
|
||||
* <<after some sequence>
|
||||
* ld r9,312(r31)
|
||||
*
|
||||
* Previous instruction sequence shows that register state of r3
|
||||
* is moved to r31. update_insn_state_powerpc tracks these state
|
||||
* changes
|
||||
*/
|
||||
#ifdef HAVE_DWARF_SUPPORT
|
||||
static void update_insn_state_powerpc(struct type_state *state,
|
||||
struct data_loc_info *dloc, Dwarf_Die * cu_die __maybe_unused,
|
||||
struct disasm_line *dl)
|
||||
{
|
||||
struct annotated_insn_loc loc;
|
||||
struct annotated_op_loc *src = &loc.ops[INSN_OP_SOURCE];
|
||||
struct annotated_op_loc *dst = &loc.ops[INSN_OP_TARGET];
|
||||
struct type_state_reg *tsr;
|
||||
u32 insn_offset = dl->al.offset;
|
||||
|
||||
if (annotate_get_insn_location(dloc->arch, dl, &loc) < 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Value 444 for bits 21:30 is for "mr"
|
||||
* instruction. "mr" is extended OR. So set the
|
||||
* source and destination reg correctly
|
||||
*/
|
||||
if (PPC_21_30(dl->raw.raw_insn) == 444) {
|
||||
int src_reg = src->reg1;
|
||||
|
||||
src->reg1 = dst->reg1;
|
||||
dst->reg1 = src_reg;
|
||||
}
|
||||
|
||||
if (!has_reg_type(state, dst->reg1))
|
||||
return;
|
||||
|
||||
tsr = &state->regs[dst->reg1];
|
||||
|
||||
if (!has_reg_type(state, src->reg1) ||
|
||||
!state->regs[src->reg1].ok) {
|
||||
tsr->ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
tsr->type = state->regs[src->reg1].type;
|
||||
tsr->kind = state->regs[src->reg1].kind;
|
||||
tsr->ok = true;
|
||||
|
||||
pr_debug_dtp("mov [%x] reg%d -> reg%d",
|
||||
insn_offset, src->reg1, dst->reg1);
|
||||
pr_debug_type_name(&tsr->type, tsr->kind);
|
||||
}
|
||||
#endif /* HAVE_DWARF_SUPPORT */
|
||||
|
||||
static int powerpc__annotate_init(struct arch *arch, char *cpuid __maybe_unused)
|
||||
{
|
||||
if (!arch->initialized) {
|
||||
arch->initialized = true;
|
||||
arch->associate_instruction_ops = powerpc__associate_instruction_ops;
|
||||
arch->objdump.comment_char = '#';
|
||||
annotate_opts.show_asm_raw = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -98,3 +98,56 @@ int regs_query_register_offset(const char *name)
|
||||
return roff->ptregs_offset;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define PPC_OP(op) (((op) >> 26) & 0x3F)
|
||||
#define PPC_RA(a) (((a) >> 16) & 0x1f)
|
||||
#define PPC_RT(t) (((t) >> 21) & 0x1f)
|
||||
#define PPC_RB(b) (((b) >> 11) & 0x1f)
|
||||
#define PPC_D(D) ((D) & 0xfffe)
|
||||
#define PPC_DS(DS) ((DS) & 0xfffc)
|
||||
#define OP_LD 58
|
||||
#define OP_STD 62
|
||||
|
||||
static int get_source_reg(u32 raw_insn)
|
||||
{
|
||||
return PPC_RA(raw_insn);
|
||||
}
|
||||
|
||||
static int get_target_reg(u32 raw_insn)
|
||||
{
|
||||
return PPC_RT(raw_insn);
|
||||
}
|
||||
|
||||
static int get_offset_opcode(u32 raw_insn)
|
||||
{
|
||||
int opcode = PPC_OP(raw_insn);
|
||||
|
||||
/* DS- form */
|
||||
if ((opcode == OP_LD) || (opcode == OP_STD))
|
||||
return PPC_DS(raw_insn);
|
||||
else
|
||||
return PPC_D(raw_insn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fills the required fields for op_loc depending on if it
|
||||
* is a source or target.
|
||||
* D form: ins RT,D(RA) -> src_reg1 = RA, offset = D, dst_reg1 = RT
|
||||
* DS form: ins RT,DS(RA) -> src_reg1 = RA, offset = DS, dst_reg1 = RT
|
||||
* X form: ins RT,RA,RB -> src_reg1 = RA, src_reg2 = RB, dst_reg1 = RT
|
||||
*/
|
||||
void get_powerpc_regs(u32 raw_insn, int is_source,
|
||||
struct annotated_op_loc *op_loc)
|
||||
{
|
||||
if (is_source)
|
||||
op_loc->reg1 = get_source_reg(raw_insn);
|
||||
else
|
||||
op_loc->reg1 = get_target_reg(raw_insn);
|
||||
|
||||
if (op_loc->multi_regs)
|
||||
op_loc->reg2 = PPC_RB(raw_insn);
|
||||
|
||||
/* TODO: Implement offset handling for X Form */
|
||||
if ((op_loc->mem_ref) && (PPC_OP(raw_insn) != 31))
|
||||
op_loc->offset = get_offset_opcode(raw_insn);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <linux/compiler.h>
|
||||
|
||||
static int s390_call__parse(struct arch *arch, struct ins_operands *ops,
|
||||
struct map_symbol *ms)
|
||||
struct map_symbol *ms, struct disasm_line *dl __maybe_unused)
|
||||
{
|
||||
char *endptr, *tok, *name;
|
||||
struct map *map = ms->map;
|
||||
@@ -52,7 +52,8 @@ static struct ins_ops s390_call_ops = {
|
||||
|
||||
static int s390_mov__parse(struct arch *arch __maybe_unused,
|
||||
struct ins_operands *ops,
|
||||
struct map_symbol *ms __maybe_unused)
|
||||
struct map_symbol *ms __maybe_unused,
|
||||
struct disasm_line *dl __maybe_unused)
|
||||
{
|
||||
char *s = strchr(ops->raw, ','), *target, *endptr;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ PERF_HAVE_JITDUMP := 1
|
||||
generated := $(OUTPUT)arch/x86/include/generated
|
||||
out := $(generated)/asm
|
||||
header := $(out)/syscalls_64.c
|
||||
header_32 := $(out)/syscalls_32.c
|
||||
sys := $(srctree)/tools/perf/arch/x86/entry/syscalls
|
||||
systbl := $(sys)/syscalltbl.sh
|
||||
|
||||
@@ -22,7 +23,10 @@ $(shell [ -d '$(out)' ] || mkdir -p '$(out)')
|
||||
$(header): $(sys)/syscall_64.tbl $(systbl)
|
||||
$(Q)$(SHELL) '$(systbl)' $(sys)/syscall_64.tbl 'x86_64' > $@
|
||||
|
||||
$(header_32): $(sys)/syscall_32.tbl $(systbl)
|
||||
$(Q)$(SHELL) '$(systbl)' $(sys)/syscall_32.tbl 'x86' > $@
|
||||
|
||||
clean::
|
||||
$(call QUIET_CLEAN, x86) $(RM) -r $(header) $(generated)
|
||||
|
||||
archheaders: $(header)
|
||||
archheaders: $(header) $(header_32)
|
||||
|
||||
@@ -206,3 +206,392 @@ static int x86__annotate_init(struct arch *arch, char *cpuid)
|
||||
arch->initialized = true;
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef HAVE_DWARF_SUPPORT
|
||||
static void update_insn_state_x86(struct type_state *state,
|
||||
struct data_loc_info *dloc, Dwarf_Die *cu_die,
|
||||
struct disasm_line *dl)
|
||||
{
|
||||
struct annotated_insn_loc loc;
|
||||
struct annotated_op_loc *src = &loc.ops[INSN_OP_SOURCE];
|
||||
struct annotated_op_loc *dst = &loc.ops[INSN_OP_TARGET];
|
||||
struct type_state_reg *tsr;
|
||||
Dwarf_Die type_die;
|
||||
u32 insn_offset = dl->al.offset;
|
||||
int fbreg = dloc->fbreg;
|
||||
int fboff = 0;
|
||||
|
||||
if (annotate_get_insn_location(dloc->arch, dl, &loc) < 0)
|
||||
return;
|
||||
|
||||
if (ins__is_call(&dl->ins)) {
|
||||
struct symbol *func = dl->ops.target.sym;
|
||||
|
||||
if (func == NULL)
|
||||
return;
|
||||
|
||||
/* __fentry__ will preserve all registers */
|
||||
if (!strcmp(func->name, "__fentry__"))
|
||||
return;
|
||||
|
||||
pr_debug_dtp("call [%x] %s\n", insn_offset, func->name);
|
||||
|
||||
/* Otherwise invalidate caller-saved registers after call */
|
||||
for (unsigned i = 0; i < ARRAY_SIZE(state->regs); i++) {
|
||||
if (state->regs[i].caller_saved)
|
||||
state->regs[i].ok = false;
|
||||
}
|
||||
|
||||
/* Update register with the return type (if any) */
|
||||
if (die_find_func_rettype(cu_die, func->name, &type_die)) {
|
||||
tsr = &state->regs[state->ret_reg];
|
||||
tsr->type = type_die;
|
||||
tsr->kind = TSR_KIND_TYPE;
|
||||
tsr->ok = true;
|
||||
|
||||
pr_debug_dtp("call [%x] return -> reg%d",
|
||||
insn_offset, state->ret_reg);
|
||||
pr_debug_type_name(&type_die, tsr->kind);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp(dl->ins.name, "add", 3)) {
|
||||
u64 imm_value = -1ULL;
|
||||
int offset;
|
||||
const char *var_name = NULL;
|
||||
struct map_symbol *ms = dloc->ms;
|
||||
u64 ip = ms->sym->start + dl->al.offset;
|
||||
|
||||
if (!has_reg_type(state, dst->reg1))
|
||||
return;
|
||||
|
||||
tsr = &state->regs[dst->reg1];
|
||||
tsr->copied_from = -1;
|
||||
|
||||
if (src->imm)
|
||||
imm_value = src->offset;
|
||||
else if (has_reg_type(state, src->reg1) &&
|
||||
state->regs[src->reg1].kind == TSR_KIND_CONST)
|
||||
imm_value = state->regs[src->reg1].imm_value;
|
||||
else if (src->reg1 == DWARF_REG_PC) {
|
||||
u64 var_addr = annotate_calc_pcrel(dloc->ms, ip,
|
||||
src->offset, dl);
|
||||
|
||||
if (get_global_var_info(dloc, var_addr,
|
||||
&var_name, &offset) &&
|
||||
!strcmp(var_name, "this_cpu_off") &&
|
||||
tsr->kind == TSR_KIND_CONST) {
|
||||
tsr->kind = TSR_KIND_PERCPU_BASE;
|
||||
tsr->ok = true;
|
||||
imm_value = tsr->imm_value;
|
||||
}
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
if (tsr->kind != TSR_KIND_PERCPU_BASE)
|
||||
return;
|
||||
|
||||
if (get_global_var_type(cu_die, dloc, ip, imm_value, &offset,
|
||||
&type_die) && offset == 0) {
|
||||
/*
|
||||
* This is not a pointer type, but it should be treated
|
||||
* as a pointer.
|
||||
*/
|
||||
tsr->type = type_die;
|
||||
tsr->kind = TSR_KIND_POINTER;
|
||||
tsr->ok = true;
|
||||
|
||||
pr_debug_dtp("add [%x] percpu %#"PRIx64" -> reg%d",
|
||||
insn_offset, imm_value, dst->reg1);
|
||||
pr_debug_type_name(&tsr->type, tsr->kind);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (strncmp(dl->ins.name, "mov", 3))
|
||||
return;
|
||||
|
||||
if (dloc->fb_cfa) {
|
||||
u64 ip = dloc->ms->sym->start + dl->al.offset;
|
||||
u64 pc = map__rip_2objdump(dloc->ms->map, ip);
|
||||
|
||||
if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0)
|
||||
fbreg = -1;
|
||||
}
|
||||
|
||||
/* Case 1. register to register or segment:offset to register transfers */
|
||||
if (!src->mem_ref && !dst->mem_ref) {
|
||||
if (!has_reg_type(state, dst->reg1))
|
||||
return;
|
||||
|
||||
tsr = &state->regs[dst->reg1];
|
||||
tsr->copied_from = -1;
|
||||
|
||||
if (dso__kernel(map__dso(dloc->ms->map)) &&
|
||||
src->segment == INSN_SEG_X86_GS && src->imm) {
|
||||
u64 ip = dloc->ms->sym->start + dl->al.offset;
|
||||
u64 var_addr;
|
||||
int offset;
|
||||
|
||||
/*
|
||||
* In kernel, %gs points to a per-cpu region for the
|
||||
* current CPU. Access with a constant offset should
|
||||
* be treated as a global variable access.
|
||||
*/
|
||||
var_addr = src->offset;
|
||||
|
||||
if (var_addr == 40) {
|
||||
tsr->kind = TSR_KIND_CANARY;
|
||||
tsr->ok = true;
|
||||
|
||||
pr_debug_dtp("mov [%x] stack canary -> reg%d\n",
|
||||
insn_offset, dst->reg1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!get_global_var_type(cu_die, dloc, ip, var_addr,
|
||||
&offset, &type_die) ||
|
||||
!die_get_member_type(&type_die, offset, &type_die)) {
|
||||
tsr->ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
tsr->type = type_die;
|
||||
tsr->kind = TSR_KIND_TYPE;
|
||||
tsr->ok = true;
|
||||
|
||||
pr_debug_dtp("mov [%x] this-cpu addr=%#"PRIx64" -> reg%d",
|
||||
insn_offset, var_addr, dst->reg1);
|
||||
pr_debug_type_name(&tsr->type, tsr->kind);
|
||||
return;
|
||||
}
|
||||
|
||||
if (src->imm) {
|
||||
tsr->kind = TSR_KIND_CONST;
|
||||
tsr->imm_value = src->offset;
|
||||
tsr->ok = true;
|
||||
|
||||
pr_debug_dtp("mov [%x] imm=%#x -> reg%d\n",
|
||||
insn_offset, tsr->imm_value, dst->reg1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!has_reg_type(state, src->reg1) ||
|
||||
!state->regs[src->reg1].ok) {
|
||||
tsr->ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
tsr->type = state->regs[src->reg1].type;
|
||||
tsr->kind = state->regs[src->reg1].kind;
|
||||
tsr->imm_value = state->regs[src->reg1].imm_value;
|
||||
tsr->ok = true;
|
||||
|
||||
/* To copy back the variable type later (hopefully) */
|
||||
if (tsr->kind == TSR_KIND_TYPE)
|
||||
tsr->copied_from = src->reg1;
|
||||
|
||||
pr_debug_dtp("mov [%x] reg%d -> reg%d",
|
||||
insn_offset, src->reg1, dst->reg1);
|
||||
pr_debug_type_name(&tsr->type, tsr->kind);
|
||||
}
|
||||
/* Case 2. memory to register transers */
|
||||
if (src->mem_ref && !dst->mem_ref) {
|
||||
int sreg = src->reg1;
|
||||
|
||||
if (!has_reg_type(state, dst->reg1))
|
||||
return;
|
||||
|
||||
tsr = &state->regs[dst->reg1];
|
||||
tsr->copied_from = -1;
|
||||
|
||||
retry:
|
||||
/* Check stack variables with offset */
|
||||
if (sreg == fbreg) {
|
||||
struct type_state_stack *stack;
|
||||
int offset = src->offset - fboff;
|
||||
|
||||
stack = find_stack_state(state, offset);
|
||||
if (stack == NULL) {
|
||||
tsr->ok = false;
|
||||
return;
|
||||
} else if (!stack->compound) {
|
||||
tsr->type = stack->type;
|
||||
tsr->kind = stack->kind;
|
||||
tsr->ok = true;
|
||||
} else if (die_get_member_type(&stack->type,
|
||||
offset - stack->offset,
|
||||
&type_die)) {
|
||||
tsr->type = type_die;
|
||||
tsr->kind = TSR_KIND_TYPE;
|
||||
tsr->ok = true;
|
||||
} else {
|
||||
tsr->ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
pr_debug_dtp("mov [%x] -%#x(stack) -> reg%d",
|
||||
insn_offset, -offset, dst->reg1);
|
||||
pr_debug_type_name(&tsr->type, tsr->kind);
|
||||
}
|
||||
/* And then dereference the pointer if it has one */
|
||||
else if (has_reg_type(state, sreg) && state->regs[sreg].ok &&
|
||||
state->regs[sreg].kind == TSR_KIND_TYPE &&
|
||||
die_deref_ptr_type(&state->regs[sreg].type,
|
||||
src->offset, &type_die)) {
|
||||
tsr->type = type_die;
|
||||
tsr->kind = TSR_KIND_TYPE;
|
||||
tsr->ok = true;
|
||||
|
||||
pr_debug_dtp("mov [%x] %#x(reg%d) -> reg%d",
|
||||
insn_offset, src->offset, sreg, dst->reg1);
|
||||
pr_debug_type_name(&tsr->type, tsr->kind);
|
||||
}
|
||||
/* Or check if it's a global variable */
|
||||
else if (sreg == DWARF_REG_PC) {
|
||||
struct map_symbol *ms = dloc->ms;
|
||||
u64 ip = ms->sym->start + dl->al.offset;
|
||||
u64 addr;
|
||||
int offset;
|
||||
|
||||
addr = annotate_calc_pcrel(ms, ip, src->offset, dl);
|
||||
|
||||
if (!get_global_var_type(cu_die, dloc, ip, addr, &offset,
|
||||
&type_die) ||
|
||||
!die_get_member_type(&type_die, offset, &type_die)) {
|
||||
tsr->ok = false;
|
||||
return;
|
||||
}
|
||||
|
||||
tsr->type = type_die;
|
||||
tsr->kind = TSR_KIND_TYPE;
|
||||
tsr->ok = true;
|
||||
|
||||
pr_debug_dtp("mov [%x] global addr=%"PRIx64" -> reg%d",
|
||||
insn_offset, addr, dst->reg1);
|
||||
pr_debug_type_name(&type_die, tsr->kind);
|
||||
}
|
||||
/* And check percpu access with base register */
|
||||
else if (has_reg_type(state, sreg) &&
|
||||
state->regs[sreg].kind == TSR_KIND_PERCPU_BASE) {
|
||||
u64 ip = dloc->ms->sym->start + dl->al.offset;
|
||||
u64 var_addr = src->offset;
|
||||
int offset;
|
||||
|
||||
if (src->multi_regs) {
|
||||
int reg2 = (sreg == src->reg1) ? src->reg2 : src->reg1;
|
||||
|
||||
if (has_reg_type(state, reg2) && state->regs[reg2].ok &&
|
||||
state->regs[reg2].kind == TSR_KIND_CONST)
|
||||
var_addr += state->regs[reg2].imm_value;
|
||||
}
|
||||
|
||||
/*
|
||||
* In kernel, %gs points to a per-cpu region for the
|
||||
* current CPU. Access with a constant offset should
|
||||
* be treated as a global variable access.
|
||||
*/
|
||||
if (get_global_var_type(cu_die, dloc, ip, var_addr,
|
||||
&offset, &type_die) &&
|
||||
die_get_member_type(&type_die, offset, &type_die)) {
|
||||
tsr->type = type_die;
|
||||
tsr->kind = TSR_KIND_TYPE;
|
||||
tsr->ok = true;
|
||||
|
||||
if (src->multi_regs) {
|
||||
pr_debug_dtp("mov [%x] percpu %#x(reg%d,reg%d) -> reg%d",
|
||||
insn_offset, src->offset, src->reg1,
|
||||
src->reg2, dst->reg1);
|
||||
} else {
|
||||
pr_debug_dtp("mov [%x] percpu %#x(reg%d) -> reg%d",
|
||||
insn_offset, src->offset, sreg, dst->reg1);
|
||||
}
|
||||
pr_debug_type_name(&tsr->type, tsr->kind);
|
||||
} else {
|
||||
tsr->ok = false;
|
||||
}
|
||||
}
|
||||
/* And then dereference the calculated pointer if it has one */
|
||||
else if (has_reg_type(state, sreg) && state->regs[sreg].ok &&
|
||||
state->regs[sreg].kind == TSR_KIND_POINTER &&
|
||||
die_get_member_type(&state->regs[sreg].type,
|
||||
src->offset, &type_die)) {
|
||||
tsr->type = type_die;
|
||||
tsr->kind = TSR_KIND_TYPE;
|
||||
tsr->ok = true;
|
||||
|
||||
pr_debug_dtp("mov [%x] pointer %#x(reg%d) -> reg%d",
|
||||
insn_offset, src->offset, sreg, dst->reg1);
|
||||
pr_debug_type_name(&tsr->type, tsr->kind);
|
||||
}
|
||||
/* Or try another register if any */
|
||||
else if (src->multi_regs && sreg == src->reg1 &&
|
||||
src->reg1 != src->reg2) {
|
||||
sreg = src->reg2;
|
||||
goto retry;
|
||||
}
|
||||
else {
|
||||
int offset;
|
||||
const char *var_name = NULL;
|
||||
|
||||
/* it might be per-cpu variable (in kernel) access */
|
||||
if (src->offset < 0) {
|
||||
if (get_global_var_info(dloc, (s64)src->offset,
|
||||
&var_name, &offset) &&
|
||||
!strcmp(var_name, "__per_cpu_offset")) {
|
||||
tsr->kind = TSR_KIND_PERCPU_BASE;
|
||||
tsr->ok = true;
|
||||
|
||||
pr_debug_dtp("mov [%x] percpu base reg%d\n",
|
||||
insn_offset, dst->reg1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
tsr->ok = false;
|
||||
}
|
||||
}
|
||||
/* Case 3. register to memory transfers */
|
||||
if (!src->mem_ref && dst->mem_ref) {
|
||||
if (!has_reg_type(state, src->reg1) ||
|
||||
!state->regs[src->reg1].ok)
|
||||
return;
|
||||
|
||||
/* Check stack variables with offset */
|
||||
if (dst->reg1 == fbreg) {
|
||||
struct type_state_stack *stack;
|
||||
int offset = dst->offset - fboff;
|
||||
|
||||
tsr = &state->regs[src->reg1];
|
||||
|
||||
stack = find_stack_state(state, offset);
|
||||
if (stack) {
|
||||
/*
|
||||
* The source register is likely to hold a type
|
||||
* of member if it's a compound type. Do not
|
||||
* update the stack variable type since we can
|
||||
* get the member type later by using the
|
||||
* die_get_member_type().
|
||||
*/
|
||||
if (!stack->compound)
|
||||
set_stack_state(stack, offset, tsr->kind,
|
||||
&tsr->type);
|
||||
} else {
|
||||
findnew_stack_state(state, offset, tsr->kind,
|
||||
&tsr->type);
|
||||
}
|
||||
|
||||
pr_debug_dtp("mov [%x] reg%d -> -%#x(stack)",
|
||||
insn_offset, src->reg1, -offset);
|
||||
pr_debug_type_name(&tsr->type, tsr->kind);
|
||||
}
|
||||
/*
|
||||
* Ignore other transfers since it'd set a value in a struct
|
||||
* and won't change the type.
|
||||
*/
|
||||
}
|
||||
/* Case 4. memory to memory transfers (not handled for now) */
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,470 @@
|
||||
# SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
|
||||
#
|
||||
# 32-bit system call numbers and entry vectors
|
||||
#
|
||||
# The format is:
|
||||
# <number> <abi> <name> <entry point> [<compat entry point> [noreturn]]
|
||||
#
|
||||
# The __ia32_sys and __ia32_compat_sys stubs are created on-the-fly for
|
||||
# sys_*() system calls and compat_sys_*() compat system calls if
|
||||
# IA32_EMULATION is defined, and expect struct pt_regs *regs as their only
|
||||
# parameter.
|
||||
#
|
||||
# The abi is always "i386" for this file.
|
||||
#
|
||||
0 i386 restart_syscall sys_restart_syscall
|
||||
1 i386 exit sys_exit - noreturn
|
||||
2 i386 fork sys_fork
|
||||
3 i386 read sys_read
|
||||
4 i386 write sys_write
|
||||
5 i386 open sys_open compat_sys_open
|
||||
6 i386 close sys_close
|
||||
7 i386 waitpid sys_waitpid
|
||||
8 i386 creat sys_creat
|
||||
9 i386 link sys_link
|
||||
10 i386 unlink sys_unlink
|
||||
11 i386 execve sys_execve compat_sys_execve
|
||||
12 i386 chdir sys_chdir
|
||||
13 i386 time sys_time32
|
||||
14 i386 mknod sys_mknod
|
||||
15 i386 chmod sys_chmod
|
||||
16 i386 lchown sys_lchown16
|
||||
17 i386 break
|
||||
18 i386 oldstat sys_stat
|
||||
19 i386 lseek sys_lseek compat_sys_lseek
|
||||
20 i386 getpid sys_getpid
|
||||
21 i386 mount sys_mount
|
||||
22 i386 umount sys_oldumount
|
||||
23 i386 setuid sys_setuid16
|
||||
24 i386 getuid sys_getuid16
|
||||
25 i386 stime sys_stime32
|
||||
26 i386 ptrace sys_ptrace compat_sys_ptrace
|
||||
27 i386 alarm sys_alarm
|
||||
28 i386 oldfstat sys_fstat
|
||||
29 i386 pause sys_pause
|
||||
30 i386 utime sys_utime32
|
||||
31 i386 stty
|
||||
32 i386 gtty
|
||||
33 i386 access sys_access
|
||||
34 i386 nice sys_nice
|
||||
35 i386 ftime
|
||||
36 i386 sync sys_sync
|
||||
37 i386 kill sys_kill
|
||||
38 i386 rename sys_rename
|
||||
39 i386 mkdir sys_mkdir
|
||||
40 i386 rmdir sys_rmdir
|
||||
41 i386 dup sys_dup
|
||||
42 i386 pipe sys_pipe
|
||||
43 i386 times sys_times compat_sys_times
|
||||
44 i386 prof
|
||||
45 i386 brk sys_brk
|
||||
46 i386 setgid sys_setgid16
|
||||
47 i386 getgid sys_getgid16
|
||||
48 i386 signal sys_signal
|
||||
49 i386 geteuid sys_geteuid16
|
||||
50 i386 getegid sys_getegid16
|
||||
51 i386 acct sys_acct
|
||||
52 i386 umount2 sys_umount
|
||||
53 i386 lock
|
||||
54 i386 ioctl sys_ioctl compat_sys_ioctl
|
||||
55 i386 fcntl sys_fcntl compat_sys_fcntl64
|
||||
56 i386 mpx
|
||||
57 i386 setpgid sys_setpgid
|
||||
58 i386 ulimit
|
||||
59 i386 oldolduname sys_olduname
|
||||
60 i386 umask sys_umask
|
||||
61 i386 chroot sys_chroot
|
||||
62 i386 ustat sys_ustat compat_sys_ustat
|
||||
63 i386 dup2 sys_dup2
|
||||
64 i386 getppid sys_getppid
|
||||
65 i386 getpgrp sys_getpgrp
|
||||
66 i386 setsid sys_setsid
|
||||
67 i386 sigaction sys_sigaction compat_sys_sigaction
|
||||
68 i386 sgetmask sys_sgetmask
|
||||
69 i386 ssetmask sys_ssetmask
|
||||
70 i386 setreuid sys_setreuid16
|
||||
71 i386 setregid sys_setregid16
|
||||
72 i386 sigsuspend sys_sigsuspend
|
||||
73 i386 sigpending sys_sigpending compat_sys_sigpending
|
||||
74 i386 sethostname sys_sethostname
|
||||
75 i386 setrlimit sys_setrlimit compat_sys_setrlimit
|
||||
76 i386 getrlimit sys_old_getrlimit compat_sys_old_getrlimit
|
||||
77 i386 getrusage sys_getrusage compat_sys_getrusage
|
||||
78 i386 gettimeofday sys_gettimeofday compat_sys_gettimeofday
|
||||
79 i386 settimeofday sys_settimeofday compat_sys_settimeofday
|
||||
80 i386 getgroups sys_getgroups16
|
||||
81 i386 setgroups sys_setgroups16
|
||||
82 i386 select sys_old_select compat_sys_old_select
|
||||
83 i386 symlink sys_symlink
|
||||
84 i386 oldlstat sys_lstat
|
||||
85 i386 readlink sys_readlink
|
||||
86 i386 uselib sys_uselib
|
||||
87 i386 swapon sys_swapon
|
||||
88 i386 reboot sys_reboot
|
||||
89 i386 readdir sys_old_readdir compat_sys_old_readdir
|
||||
90 i386 mmap sys_old_mmap compat_sys_ia32_mmap
|
||||
91 i386 munmap sys_munmap
|
||||
92 i386 truncate sys_truncate compat_sys_truncate
|
||||
93 i386 ftruncate sys_ftruncate compat_sys_ftruncate
|
||||
94 i386 fchmod sys_fchmod
|
||||
95 i386 fchown sys_fchown16
|
||||
96 i386 getpriority sys_getpriority
|
||||
97 i386 setpriority sys_setpriority
|
||||
98 i386 profil
|
||||
99 i386 statfs sys_statfs compat_sys_statfs
|
||||
100 i386 fstatfs sys_fstatfs compat_sys_fstatfs
|
||||
101 i386 ioperm sys_ioperm
|
||||
102 i386 socketcall sys_socketcall compat_sys_socketcall
|
||||
103 i386 syslog sys_syslog
|
||||
104 i386 setitimer sys_setitimer compat_sys_setitimer
|
||||
105 i386 getitimer sys_getitimer compat_sys_getitimer
|
||||
106 i386 stat sys_newstat compat_sys_newstat
|
||||
107 i386 lstat sys_newlstat compat_sys_newlstat
|
||||
108 i386 fstat sys_newfstat compat_sys_newfstat
|
||||
109 i386 olduname sys_uname
|
||||
110 i386 iopl sys_iopl
|
||||
111 i386 vhangup sys_vhangup
|
||||
112 i386 idle
|
||||
113 i386 vm86old sys_vm86old sys_ni_syscall
|
||||
114 i386 wait4 sys_wait4 compat_sys_wait4
|
||||
115 i386 swapoff sys_swapoff
|
||||
116 i386 sysinfo sys_sysinfo compat_sys_sysinfo
|
||||
117 i386 ipc sys_ipc compat_sys_ipc
|
||||
118 i386 fsync sys_fsync
|
||||
119 i386 sigreturn sys_sigreturn compat_sys_sigreturn
|
||||
120 i386 clone sys_clone compat_sys_ia32_clone
|
||||
121 i386 setdomainname sys_setdomainname
|
||||
122 i386 uname sys_newuname
|
||||
123 i386 modify_ldt sys_modify_ldt
|
||||
124 i386 adjtimex sys_adjtimex_time32
|
||||
125 i386 mprotect sys_mprotect
|
||||
126 i386 sigprocmask sys_sigprocmask compat_sys_sigprocmask
|
||||
127 i386 create_module
|
||||
128 i386 init_module sys_init_module
|
||||
129 i386 delete_module sys_delete_module
|
||||
130 i386 get_kernel_syms
|
||||
131 i386 quotactl sys_quotactl
|
||||
132 i386 getpgid sys_getpgid
|
||||
133 i386 fchdir sys_fchdir
|
||||
134 i386 bdflush sys_ni_syscall
|
||||
135 i386 sysfs sys_sysfs
|
||||
136 i386 personality sys_personality
|
||||
137 i386 afs_syscall
|
||||
138 i386 setfsuid sys_setfsuid16
|
||||
139 i386 setfsgid sys_setfsgid16
|
||||
140 i386 _llseek sys_llseek
|
||||
141 i386 getdents sys_getdents compat_sys_getdents
|
||||
142 i386 _newselect sys_select compat_sys_select
|
||||
143 i386 flock sys_flock
|
||||
144 i386 msync sys_msync
|
||||
145 i386 readv sys_readv
|
||||
146 i386 writev sys_writev
|
||||
147 i386 getsid sys_getsid
|
||||
148 i386 fdatasync sys_fdatasync
|
||||
149 i386 _sysctl sys_ni_syscall
|
||||
150 i386 mlock sys_mlock
|
||||
151 i386 munlock sys_munlock
|
||||
152 i386 mlockall sys_mlockall
|
||||
153 i386 munlockall sys_munlockall
|
||||
154 i386 sched_setparam sys_sched_setparam
|
||||
155 i386 sched_getparam sys_sched_getparam
|
||||
156 i386 sched_setscheduler sys_sched_setscheduler
|
||||
157 i386 sched_getscheduler sys_sched_getscheduler
|
||||
158 i386 sched_yield sys_sched_yield
|
||||
159 i386 sched_get_priority_max sys_sched_get_priority_max
|
||||
160 i386 sched_get_priority_min sys_sched_get_priority_min
|
||||
161 i386 sched_rr_get_interval sys_sched_rr_get_interval_time32
|
||||
162 i386 nanosleep sys_nanosleep_time32
|
||||
163 i386 mremap sys_mremap
|
||||
164 i386 setresuid sys_setresuid16
|
||||
165 i386 getresuid sys_getresuid16
|
||||
166 i386 vm86 sys_vm86 sys_ni_syscall
|
||||
167 i386 query_module
|
||||
168 i386 poll sys_poll
|
||||
169 i386 nfsservctl
|
||||
170 i386 setresgid sys_setresgid16
|
||||
171 i386 getresgid sys_getresgid16
|
||||
172 i386 prctl sys_prctl
|
||||
173 i386 rt_sigreturn sys_rt_sigreturn compat_sys_rt_sigreturn
|
||||
174 i386 rt_sigaction sys_rt_sigaction compat_sys_rt_sigaction
|
||||
175 i386 rt_sigprocmask sys_rt_sigprocmask compat_sys_rt_sigprocmask
|
||||
176 i386 rt_sigpending sys_rt_sigpending compat_sys_rt_sigpending
|
||||
177 i386 rt_sigtimedwait sys_rt_sigtimedwait_time32 compat_sys_rt_sigtimedwait_time32
|
||||
178 i386 rt_sigqueueinfo sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo
|
||||
179 i386 rt_sigsuspend sys_rt_sigsuspend compat_sys_rt_sigsuspend
|
||||
180 i386 pread64 sys_ia32_pread64
|
||||
181 i386 pwrite64 sys_ia32_pwrite64
|
||||
182 i386 chown sys_chown16
|
||||
183 i386 getcwd sys_getcwd
|
||||
184 i386 capget sys_capget
|
||||
185 i386 capset sys_capset
|
||||
186 i386 sigaltstack sys_sigaltstack compat_sys_sigaltstack
|
||||
187 i386 sendfile sys_sendfile compat_sys_sendfile
|
||||
188 i386 getpmsg
|
||||
189 i386 putpmsg
|
||||
190 i386 vfork sys_vfork
|
||||
191 i386 ugetrlimit sys_getrlimit compat_sys_getrlimit
|
||||
192 i386 mmap2 sys_mmap_pgoff
|
||||
193 i386 truncate64 sys_ia32_truncate64
|
||||
194 i386 ftruncate64 sys_ia32_ftruncate64
|
||||
195 i386 stat64 sys_stat64 compat_sys_ia32_stat64
|
||||
196 i386 lstat64 sys_lstat64 compat_sys_ia32_lstat64
|
||||
197 i386 fstat64 sys_fstat64 compat_sys_ia32_fstat64
|
||||
198 i386 lchown32 sys_lchown
|
||||
199 i386 getuid32 sys_getuid
|
||||
200 i386 getgid32 sys_getgid
|
||||
201 i386 geteuid32 sys_geteuid
|
||||
202 i386 getegid32 sys_getegid
|
||||
203 i386 setreuid32 sys_setreuid
|
||||
204 i386 setregid32 sys_setregid
|
||||
205 i386 getgroups32 sys_getgroups
|
||||
206 i386 setgroups32 sys_setgroups
|
||||
207 i386 fchown32 sys_fchown
|
||||
208 i386 setresuid32 sys_setresuid
|
||||
209 i386 getresuid32 sys_getresuid
|
||||
210 i386 setresgid32 sys_setresgid
|
||||
211 i386 getresgid32 sys_getresgid
|
||||
212 i386 chown32 sys_chown
|
||||
213 i386 setuid32 sys_setuid
|
||||
214 i386 setgid32 sys_setgid
|
||||
215 i386 setfsuid32 sys_setfsuid
|
||||
216 i386 setfsgid32 sys_setfsgid
|
||||
217 i386 pivot_root sys_pivot_root
|
||||
218 i386 mincore sys_mincore
|
||||
219 i386 madvise sys_madvise
|
||||
220 i386 getdents64 sys_getdents64
|
||||
221 i386 fcntl64 sys_fcntl64 compat_sys_fcntl64
|
||||
# 222 is unused
|
||||
# 223 is unused
|
||||
224 i386 gettid sys_gettid
|
||||
225 i386 readahead sys_ia32_readahead
|
||||
226 i386 setxattr sys_setxattr
|
||||
227 i386 lsetxattr sys_lsetxattr
|
||||
228 i386 fsetxattr sys_fsetxattr
|
||||
229 i386 getxattr sys_getxattr
|
||||
230 i386 lgetxattr sys_lgetxattr
|
||||
231 i386 fgetxattr sys_fgetxattr
|
||||
232 i386 listxattr sys_listxattr
|
||||
233 i386 llistxattr sys_llistxattr
|
||||
234 i386 flistxattr sys_flistxattr
|
||||
235 i386 removexattr sys_removexattr
|
||||
236 i386 lremovexattr sys_lremovexattr
|
||||
237 i386 fremovexattr sys_fremovexattr
|
||||
238 i386 tkill sys_tkill
|
||||
239 i386 sendfile64 sys_sendfile64
|
||||
240 i386 futex sys_futex_time32
|
||||
241 i386 sched_setaffinity sys_sched_setaffinity compat_sys_sched_setaffinity
|
||||
242 i386 sched_getaffinity sys_sched_getaffinity compat_sys_sched_getaffinity
|
||||
243 i386 set_thread_area sys_set_thread_area
|
||||
244 i386 get_thread_area sys_get_thread_area
|
||||
245 i386 io_setup sys_io_setup compat_sys_io_setup
|
||||
246 i386 io_destroy sys_io_destroy
|
||||
247 i386 io_getevents sys_io_getevents_time32
|
||||
248 i386 io_submit sys_io_submit compat_sys_io_submit
|
||||
249 i386 io_cancel sys_io_cancel
|
||||
250 i386 fadvise64 sys_ia32_fadvise64
|
||||
# 251 is available for reuse (was briefly sys_set_zone_reclaim)
|
||||
252 i386 exit_group sys_exit_group - noreturn
|
||||
253 i386 lookup_dcookie
|
||||
254 i386 epoll_create sys_epoll_create
|
||||
255 i386 epoll_ctl sys_epoll_ctl
|
||||
256 i386 epoll_wait sys_epoll_wait
|
||||
257 i386 remap_file_pages sys_remap_file_pages
|
||||
258 i386 set_tid_address sys_set_tid_address
|
||||
259 i386 timer_create sys_timer_create compat_sys_timer_create
|
||||
260 i386 timer_settime sys_timer_settime32
|
||||
261 i386 timer_gettime sys_timer_gettime32
|
||||
262 i386 timer_getoverrun sys_timer_getoverrun
|
||||
263 i386 timer_delete sys_timer_delete
|
||||
264 i386 clock_settime sys_clock_settime32
|
||||
265 i386 clock_gettime sys_clock_gettime32
|
||||
266 i386 clock_getres sys_clock_getres_time32
|
||||
267 i386 clock_nanosleep sys_clock_nanosleep_time32
|
||||
268 i386 statfs64 sys_statfs64 compat_sys_statfs64
|
||||
269 i386 fstatfs64 sys_fstatfs64 compat_sys_fstatfs64
|
||||
270 i386 tgkill sys_tgkill
|
||||
271 i386 utimes sys_utimes_time32
|
||||
272 i386 fadvise64_64 sys_ia32_fadvise64_64
|
||||
273 i386 vserver
|
||||
274 i386 mbind sys_mbind
|
||||
275 i386 get_mempolicy sys_get_mempolicy
|
||||
276 i386 set_mempolicy sys_set_mempolicy
|
||||
277 i386 mq_open sys_mq_open compat_sys_mq_open
|
||||
278 i386 mq_unlink sys_mq_unlink
|
||||
279 i386 mq_timedsend sys_mq_timedsend_time32
|
||||
280 i386 mq_timedreceive sys_mq_timedreceive_time32
|
||||
281 i386 mq_notify sys_mq_notify compat_sys_mq_notify
|
||||
282 i386 mq_getsetattr sys_mq_getsetattr compat_sys_mq_getsetattr
|
||||
283 i386 kexec_load sys_kexec_load compat_sys_kexec_load
|
||||
284 i386 waitid sys_waitid compat_sys_waitid
|
||||
# 285 sys_setaltroot
|
||||
286 i386 add_key sys_add_key
|
||||
287 i386 request_key sys_request_key
|
||||
288 i386 keyctl sys_keyctl compat_sys_keyctl
|
||||
289 i386 ioprio_set sys_ioprio_set
|
||||
290 i386 ioprio_get sys_ioprio_get
|
||||
291 i386 inotify_init sys_inotify_init
|
||||
292 i386 inotify_add_watch sys_inotify_add_watch
|
||||
293 i386 inotify_rm_watch sys_inotify_rm_watch
|
||||
294 i386 migrate_pages sys_migrate_pages
|
||||
295 i386 openat sys_openat compat_sys_openat
|
||||
296 i386 mkdirat sys_mkdirat
|
||||
297 i386 mknodat sys_mknodat
|
||||
298 i386 fchownat sys_fchownat
|
||||
299 i386 futimesat sys_futimesat_time32
|
||||
300 i386 fstatat64 sys_fstatat64 compat_sys_ia32_fstatat64
|
||||
301 i386 unlinkat sys_unlinkat
|
||||
302 i386 renameat sys_renameat
|
||||
303 i386 linkat sys_linkat
|
||||
304 i386 symlinkat sys_symlinkat
|
||||
305 i386 readlinkat sys_readlinkat
|
||||
306 i386 fchmodat sys_fchmodat
|
||||
307 i386 faccessat sys_faccessat
|
||||
308 i386 pselect6 sys_pselect6_time32 compat_sys_pselect6_time32
|
||||
309 i386 ppoll sys_ppoll_time32 compat_sys_ppoll_time32
|
||||
310 i386 unshare sys_unshare
|
||||
311 i386 set_robust_list sys_set_robust_list compat_sys_set_robust_list
|
||||
312 i386 get_robust_list sys_get_robust_list compat_sys_get_robust_list
|
||||
313 i386 splice sys_splice
|
||||
314 i386 sync_file_range sys_ia32_sync_file_range
|
||||
315 i386 tee sys_tee
|
||||
316 i386 vmsplice sys_vmsplice
|
||||
317 i386 move_pages sys_move_pages
|
||||
318 i386 getcpu sys_getcpu
|
||||
319 i386 epoll_pwait sys_epoll_pwait
|
||||
320 i386 utimensat sys_utimensat_time32
|
||||
321 i386 signalfd sys_signalfd compat_sys_signalfd
|
||||
322 i386 timerfd_create sys_timerfd_create
|
||||
323 i386 eventfd sys_eventfd
|
||||
324 i386 fallocate sys_ia32_fallocate
|
||||
325 i386 timerfd_settime sys_timerfd_settime32
|
||||
326 i386 timerfd_gettime sys_timerfd_gettime32
|
||||
327 i386 signalfd4 sys_signalfd4 compat_sys_signalfd4
|
||||
328 i386 eventfd2 sys_eventfd2
|
||||
329 i386 epoll_create1 sys_epoll_create1
|
||||
330 i386 dup3 sys_dup3
|
||||
331 i386 pipe2 sys_pipe2
|
||||
332 i386 inotify_init1 sys_inotify_init1
|
||||
333 i386 preadv sys_preadv compat_sys_preadv
|
||||
334 i386 pwritev sys_pwritev compat_sys_pwritev
|
||||
335 i386 rt_tgsigqueueinfo sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo
|
||||
336 i386 perf_event_open sys_perf_event_open
|
||||
337 i386 recvmmsg sys_recvmmsg_time32 compat_sys_recvmmsg_time32
|
||||
338 i386 fanotify_init sys_fanotify_init
|
||||
339 i386 fanotify_mark sys_fanotify_mark compat_sys_fanotify_mark
|
||||
340 i386 prlimit64 sys_prlimit64
|
||||
341 i386 name_to_handle_at sys_name_to_handle_at
|
||||
342 i386 open_by_handle_at sys_open_by_handle_at compat_sys_open_by_handle_at
|
||||
343 i386 clock_adjtime sys_clock_adjtime32
|
||||
344 i386 syncfs sys_syncfs
|
||||
345 i386 sendmmsg sys_sendmmsg compat_sys_sendmmsg
|
||||
346 i386 setns sys_setns
|
||||
347 i386 process_vm_readv sys_process_vm_readv
|
||||
348 i386 process_vm_writev sys_process_vm_writev
|
||||
349 i386 kcmp sys_kcmp
|
||||
350 i386 finit_module sys_finit_module
|
||||
351 i386 sched_setattr sys_sched_setattr
|
||||
352 i386 sched_getattr sys_sched_getattr
|
||||
353 i386 renameat2 sys_renameat2
|
||||
354 i386 seccomp sys_seccomp
|
||||
355 i386 getrandom sys_getrandom
|
||||
356 i386 memfd_create sys_memfd_create
|
||||
357 i386 bpf sys_bpf
|
||||
358 i386 execveat sys_execveat compat_sys_execveat
|
||||
359 i386 socket sys_socket
|
||||
360 i386 socketpair sys_socketpair
|
||||
361 i386 bind sys_bind
|
||||
362 i386 connect sys_connect
|
||||
363 i386 listen sys_listen
|
||||
364 i386 accept4 sys_accept4
|
||||
365 i386 getsockopt sys_getsockopt sys_getsockopt
|
||||
366 i386 setsockopt sys_setsockopt sys_setsockopt
|
||||
367 i386 getsockname sys_getsockname
|
||||
368 i386 getpeername sys_getpeername
|
||||
369 i386 sendto sys_sendto
|
||||
370 i386 sendmsg sys_sendmsg compat_sys_sendmsg
|
||||
371 i386 recvfrom sys_recvfrom compat_sys_recvfrom
|
||||
372 i386 recvmsg sys_recvmsg compat_sys_recvmsg
|
||||
373 i386 shutdown sys_shutdown
|
||||
374 i386 userfaultfd sys_userfaultfd
|
||||
375 i386 membarrier sys_membarrier
|
||||
376 i386 mlock2 sys_mlock2
|
||||
377 i386 copy_file_range sys_copy_file_range
|
||||
378 i386 preadv2 sys_preadv2 compat_sys_preadv2
|
||||
379 i386 pwritev2 sys_pwritev2 compat_sys_pwritev2
|
||||
380 i386 pkey_mprotect sys_pkey_mprotect
|
||||
381 i386 pkey_alloc sys_pkey_alloc
|
||||
382 i386 pkey_free sys_pkey_free
|
||||
383 i386 statx sys_statx
|
||||
384 i386 arch_prctl sys_arch_prctl compat_sys_arch_prctl
|
||||
385 i386 io_pgetevents sys_io_pgetevents_time32 compat_sys_io_pgetevents
|
||||
386 i386 rseq sys_rseq
|
||||
393 i386 semget sys_semget
|
||||
394 i386 semctl sys_semctl compat_sys_semctl
|
||||
395 i386 shmget sys_shmget
|
||||
396 i386 shmctl sys_shmctl compat_sys_shmctl
|
||||
397 i386 shmat sys_shmat compat_sys_shmat
|
||||
398 i386 shmdt sys_shmdt
|
||||
399 i386 msgget sys_msgget
|
||||
400 i386 msgsnd sys_msgsnd compat_sys_msgsnd
|
||||
401 i386 msgrcv sys_msgrcv compat_sys_msgrcv
|
||||
402 i386 msgctl sys_msgctl compat_sys_msgctl
|
||||
403 i386 clock_gettime64 sys_clock_gettime
|
||||
404 i386 clock_settime64 sys_clock_settime
|
||||
405 i386 clock_adjtime64 sys_clock_adjtime
|
||||
406 i386 clock_getres_time64 sys_clock_getres
|
||||
407 i386 clock_nanosleep_time64 sys_clock_nanosleep
|
||||
408 i386 timer_gettime64 sys_timer_gettime
|
||||
409 i386 timer_settime64 sys_timer_settime
|
||||
410 i386 timerfd_gettime64 sys_timerfd_gettime
|
||||
411 i386 timerfd_settime64 sys_timerfd_settime
|
||||
412 i386 utimensat_time64 sys_utimensat
|
||||
413 i386 pselect6_time64 sys_pselect6 compat_sys_pselect6_time64
|
||||
414 i386 ppoll_time64 sys_ppoll compat_sys_ppoll_time64
|
||||
416 i386 io_pgetevents_time64 sys_io_pgetevents compat_sys_io_pgetevents_time64
|
||||
417 i386 recvmmsg_time64 sys_recvmmsg compat_sys_recvmmsg_time64
|
||||
418 i386 mq_timedsend_time64 sys_mq_timedsend
|
||||
419 i386 mq_timedreceive_time64 sys_mq_timedreceive
|
||||
420 i386 semtimedop_time64 sys_semtimedop
|
||||
421 i386 rt_sigtimedwait_time64 sys_rt_sigtimedwait compat_sys_rt_sigtimedwait_time64
|
||||
422 i386 futex_time64 sys_futex
|
||||
423 i386 sched_rr_get_interval_time64 sys_sched_rr_get_interval
|
||||
424 i386 pidfd_send_signal sys_pidfd_send_signal
|
||||
425 i386 io_uring_setup sys_io_uring_setup
|
||||
426 i386 io_uring_enter sys_io_uring_enter
|
||||
427 i386 io_uring_register sys_io_uring_register
|
||||
428 i386 open_tree sys_open_tree
|
||||
429 i386 move_mount sys_move_mount
|
||||
430 i386 fsopen sys_fsopen
|
||||
431 i386 fsconfig sys_fsconfig
|
||||
432 i386 fsmount sys_fsmount
|
||||
433 i386 fspick sys_fspick
|
||||
434 i386 pidfd_open sys_pidfd_open
|
||||
435 i386 clone3 sys_clone3
|
||||
436 i386 close_range sys_close_range
|
||||
437 i386 openat2 sys_openat2
|
||||
438 i386 pidfd_getfd sys_pidfd_getfd
|
||||
439 i386 faccessat2 sys_faccessat2
|
||||
440 i386 process_madvise sys_process_madvise
|
||||
441 i386 epoll_pwait2 sys_epoll_pwait2 compat_sys_epoll_pwait2
|
||||
442 i386 mount_setattr sys_mount_setattr
|
||||
443 i386 quotactl_fd sys_quotactl_fd
|
||||
444 i386 landlock_create_ruleset sys_landlock_create_ruleset
|
||||
445 i386 landlock_add_rule sys_landlock_add_rule
|
||||
446 i386 landlock_restrict_self sys_landlock_restrict_self
|
||||
447 i386 memfd_secret sys_memfd_secret
|
||||
448 i386 process_mrelease sys_process_mrelease
|
||||
449 i386 futex_waitv sys_futex_waitv
|
||||
450 i386 set_mempolicy_home_node sys_set_mempolicy_home_node
|
||||
451 i386 cachestat sys_cachestat
|
||||
452 i386 fchmodat2 sys_fchmodat2
|
||||
453 i386 map_shadow_stack sys_map_shadow_stack
|
||||
454 i386 futex_wake sys_futex_wake
|
||||
455 i386 futex_wait sys_futex_wait
|
||||
456 i386 futex_requeue sys_futex_requeue
|
||||
457 i386 statmount sys_statmount
|
||||
458 i386 listmount sys_listmount
|
||||
459 i386 lsm_get_self_attr sys_lsm_get_self_attr
|
||||
460 i386 lsm_set_self_attr sys_lsm_set_self_attr
|
||||
461 i386 lsm_list_modules sys_lsm_list_modules
|
||||
462 i386 mseal sys_mseal
|
||||
@@ -15,7 +15,7 @@
|
||||
#if defined(__x86_64__)
|
||||
|
||||
struct perf_event__synthesize_extra_kmaps_cb_args {
|
||||
struct perf_tool *tool;
|
||||
const struct perf_tool *tool;
|
||||
perf_event__handler_t process;
|
||||
struct machine *machine;
|
||||
union perf_event *event;
|
||||
@@ -65,7 +65,7 @@ static int perf_event__synthesize_extra_kmaps_cb(struct map *map, void *data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int perf_event__synthesize_extra_kmaps(struct perf_tool *tool,
|
||||
int perf_event__synthesize_extra_kmaps(const struct perf_tool *tool,
|
||||
perf_event__handler_t process,
|
||||
struct machine *machine)
|
||||
{
|
||||
|
||||
@@ -89,6 +89,12 @@ int arch_evlist__cmp(const struct evsel *lhs, const struct evsel *rhs)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Retire latency event should not be group leader*/
|
||||
if (lhs->retire_lat && !rhs->retire_lat)
|
||||
return 1;
|
||||
if (!lhs->retire_lat && rhs->retire_lat)
|
||||
return -1;
|
||||
|
||||
/* Default ordering by insertion index. */
|
||||
return lhs->core.idx - rhs->core.idx;
|
||||
}
|
||||
|
||||
@@ -434,7 +434,6 @@ struct auxtrace_record *intel_bts_recording_init(int *err)
|
||||
}
|
||||
|
||||
btsr->intel_bts_pmu = intel_bts_pmu;
|
||||
btsr->itr.pmu = intel_bts_pmu;
|
||||
btsr->itr.recording_options = intel_bts_recording_options;
|
||||
btsr->itr.info_priv_size = intel_bts_info_priv_size;
|
||||
btsr->itr.info_fill = intel_bts_info_fill;
|
||||
|
||||
@@ -1197,7 +1197,6 @@ struct auxtrace_record *intel_pt_recording_init(int *err)
|
||||
}
|
||||
|
||||
ptr->intel_pt_pmu = intel_pt_pmu;
|
||||
ptr->itr.pmu = intel_pt_pmu;
|
||||
ptr->itr.recording_options = intel_pt_recording_options;
|
||||
ptr->itr.info_priv_size = intel_pt_info_priv_size;
|
||||
ptr->itr.info_fill = intel_pt_info_fill;
|
||||
|
||||
@@ -49,7 +49,7 @@ static const char *const bench_usage[] = {
|
||||
|
||||
static atomic_t event_count;
|
||||
|
||||
static int process_synthesized_event(struct perf_tool *tool __maybe_unused,
|
||||
static int process_synthesized_event(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine __maybe_unused)
|
||||
|
||||
@@ -221,7 +221,8 @@ static int process_branch_callback(struct evsel *evsel,
|
||||
if (a.map != NULL)
|
||||
dso__set_hit(map__dso(a.map));
|
||||
|
||||
hist__account_cycles(sample->branch_stack, al, sample, false, NULL);
|
||||
hist__account_cycles(sample->branch_stack, al, sample, false,
|
||||
NULL, evsel);
|
||||
|
||||
ret = hist_entry_iter__add(&iter, &a, PERF_MAX_STACK_DEPTH, ann);
|
||||
out:
|
||||
@@ -279,7 +280,7 @@ static int evsel__add_sample(struct evsel *evsel, struct perf_sample *sample,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool,
|
||||
static int process_sample_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
@@ -396,10 +397,10 @@ static void print_annotate_item_stat(struct list_head *head, const char *title)
|
||||
printf("total %d, ok %d (%.1f%%), bad %d (%.1f%%)\n\n", total,
|
||||
total_good, 100.0 * total_good / (total ?: 1),
|
||||
total_bad, 100.0 * total_bad / (total ?: 1));
|
||||
printf(" %-10s: %5s %5s\n", "Name", "Good", "Bad");
|
||||
printf(" %-20s: %5s %5s\n", "Name/opcode", "Good", "Bad");
|
||||
printf("-----------------------------------------------------------\n");
|
||||
list_for_each_entry(istat, head, list)
|
||||
printf(" %-10s: %5d %5d\n", istat->name, istat->good, istat->bad);
|
||||
printf(" %-20s: %5d %5d\n", istat->name, istat->good, istat->bad);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
@@ -632,12 +633,22 @@ static int __cmd_annotate(struct perf_annotate *ann)
|
||||
evlist__for_each_entry(session->evlist, pos) {
|
||||
struct hists *hists = evsel__hists(pos);
|
||||
u32 nr_samples = hists->stats.nr_samples;
|
||||
struct ui_progress prog;
|
||||
struct evsel *evsel;
|
||||
|
||||
if (!symbol_conf.event_group || !evsel__is_group_leader(pos))
|
||||
continue;
|
||||
|
||||
for_each_group_member(evsel, pos)
|
||||
nr_samples += evsel__hists(evsel)->stats.nr_samples;
|
||||
|
||||
if (nr_samples == 0)
|
||||
continue;
|
||||
|
||||
if (!symbol_conf.event_group || !evsel__is_group_leader(pos))
|
||||
continue;
|
||||
ui_progress__init(&prog, nr_samples,
|
||||
"Sorting group events for output...");
|
||||
evsel__output_resort(pos, &prog);
|
||||
ui_progress__finish();
|
||||
|
||||
hists__find_annotations(hists, pos, ann);
|
||||
}
|
||||
@@ -686,28 +697,7 @@ static const char * const annotate_usage[] = {
|
||||
|
||||
int cmd_annotate(int argc, const char **argv)
|
||||
{
|
||||
struct perf_annotate annotate = {
|
||||
.tool = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.mmap2 = perf_event__process_mmap2,
|
||||
.comm = perf_event__process_comm,
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.namespaces = perf_event__process_namespaces,
|
||||
.attr = perf_event__process_attr,
|
||||
.build_id = perf_event__process_build_id,
|
||||
#ifdef HAVE_LIBTRACEEVENT
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
#endif
|
||||
.id_index = perf_event__process_id_index,
|
||||
.auxtrace_info = perf_event__process_auxtrace_info,
|
||||
.auxtrace = perf_event__process_auxtrace,
|
||||
.feature = process_feature_event,
|
||||
.ordered_events = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
};
|
||||
struct perf_annotate annotate = {};
|
||||
struct perf_data data = {
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
};
|
||||
@@ -795,6 +785,8 @@ int cmd_annotate(int argc, const char **argv)
|
||||
"Show stats for the data type annotation"),
|
||||
OPT_BOOLEAN(0, "insn-stat", &annotate.insn_stat,
|
||||
"Show instruction stats for the data type annotation"),
|
||||
OPT_BOOLEAN(0, "skip-empty", &symbol_conf.skip_empty,
|
||||
"Do not display empty (or dummy) events in the output"),
|
||||
OPT_END()
|
||||
};
|
||||
int ret;
|
||||
@@ -864,6 +856,25 @@ int cmd_annotate(int argc, const char **argv)
|
||||
|
||||
data.path = input_name;
|
||||
|
||||
perf_tool__init(&annotate.tool, /*ordered_events=*/true);
|
||||
annotate.tool.sample = process_sample_event;
|
||||
annotate.tool.mmap = perf_event__process_mmap;
|
||||
annotate.tool.mmap2 = perf_event__process_mmap2;
|
||||
annotate.tool.comm = perf_event__process_comm;
|
||||
annotate.tool.exit = perf_event__process_exit;
|
||||
annotate.tool.fork = perf_event__process_fork;
|
||||
annotate.tool.namespaces = perf_event__process_namespaces;
|
||||
annotate.tool.attr = perf_event__process_attr;
|
||||
annotate.tool.build_id = perf_event__process_build_id;
|
||||
#ifdef HAVE_LIBTRACEEVENT
|
||||
annotate.tool.tracing_data = perf_event__process_tracing_data;
|
||||
#endif
|
||||
annotate.tool.id_index = perf_event__process_id_index;
|
||||
annotate.tool.auxtrace_info = perf_event__process_auxtrace_info;
|
||||
annotate.tool.auxtrace = perf_event__process_auxtrace;
|
||||
annotate.tool.feature = process_feature_event;
|
||||
annotate.tool.ordering_requires_timestamps = true;
|
||||
|
||||
annotate.session = perf_session__new(&data, &annotate.tool);
|
||||
if (IS_ERR(annotate.session))
|
||||
return PTR_ERR(annotate.session);
|
||||
@@ -916,11 +927,15 @@ int cmd_annotate(int argc, const char **argv)
|
||||
sort_order = "dso,symbol";
|
||||
|
||||
/*
|
||||
* Set SORT_MODE__BRANCH so that annotate display IPC/Cycle
|
||||
* if branch info is in perf data in TUI mode.
|
||||
* Set SORT_MODE__BRANCH so that annotate displays IPC/Cycle and
|
||||
* branch counters, if the corresponding branch info is available
|
||||
* in the perf data in the TUI mode.
|
||||
*/
|
||||
if ((use_browser == 1 || annotate.use_stdio2) && annotate.has_br_stack)
|
||||
if ((use_browser == 1 || annotate.use_stdio2) && annotate.has_br_stack) {
|
||||
sort__mode = SORT_MODE__BRANCH;
|
||||
if (annotate.session->evlist->nr_br_cntr > 0)
|
||||
annotate_opts.show_br_cntr = true;
|
||||
}
|
||||
|
||||
if (setup_sorting(NULL) < 0)
|
||||
usage_with_options(annotate_usage, options);
|
||||
|
||||
@@ -89,6 +89,7 @@ static int perf_session__list_build_ids(bool force, bool with_hits)
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
.force = force,
|
||||
};
|
||||
struct perf_tool build_id__mark_dso_hit_ops;
|
||||
|
||||
symbol__elf_init();
|
||||
/*
|
||||
@@ -97,6 +98,15 @@ static int perf_session__list_build_ids(bool force, bool with_hits)
|
||||
if (filename__fprintf_build_id(input_name, stdout) > 0)
|
||||
goto out;
|
||||
|
||||
perf_tool__init(&build_id__mark_dso_hit_ops, /*ordered_events=*/true);
|
||||
build_id__mark_dso_hit_ops.sample = build_id__mark_dso_hit;
|
||||
build_id__mark_dso_hit_ops.mmap = perf_event__process_mmap;
|
||||
build_id__mark_dso_hit_ops.mmap2 = perf_event__process_mmap2;
|
||||
build_id__mark_dso_hit_ops.fork = perf_event__process_fork;
|
||||
build_id__mark_dso_hit_ops.exit = perf_event__exit_del_thread;
|
||||
build_id__mark_dso_hit_ops.attr = perf_event__process_attr;
|
||||
build_id__mark_dso_hit_ops.build_id = perf_event__process_build_id;
|
||||
|
||||
session = perf_session__new(&data, &build_id__mark_dso_hit_ops);
|
||||
if (IS_ERR(session))
|
||||
return PTR_ERR(session);
|
||||
|
||||
+19
-28
@@ -273,7 +273,7 @@ static void compute_stats(struct c2c_hist_entry *c2c_he,
|
||||
update_stats(&cstats->load, weight);
|
||||
}
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
static int process_sample_event(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
@@ -385,24 +385,6 @@ free_mi:
|
||||
goto out;
|
||||
}
|
||||
|
||||
static struct perf_c2c c2c = {
|
||||
.tool = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.mmap2 = perf_event__process_mmap2,
|
||||
.comm = perf_event__process_comm,
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.lost = perf_event__process_lost,
|
||||
.attr = perf_event__process_attr,
|
||||
.auxtrace_info = perf_event__process_auxtrace_info,
|
||||
.auxtrace = perf_event__process_auxtrace,
|
||||
.auxtrace_error = perf_event__process_auxtrace_error,
|
||||
.ordered_events = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
};
|
||||
|
||||
static const char * const c2c_usage[] = {
|
||||
"perf c2c {record|report}",
|
||||
NULL
|
||||
@@ -3070,6 +3052,19 @@ static int perf_c2c__report(int argc, const char **argv)
|
||||
data.path = input_name;
|
||||
data.force = symbol_conf.force;
|
||||
|
||||
perf_tool__init(&c2c.tool, /*ordered_events=*/true);
|
||||
c2c.tool.sample = process_sample_event;
|
||||
c2c.tool.mmap = perf_event__process_mmap;
|
||||
c2c.tool.mmap2 = perf_event__process_mmap2;
|
||||
c2c.tool.comm = perf_event__process_comm;
|
||||
c2c.tool.exit = perf_event__process_exit;
|
||||
c2c.tool.fork = perf_event__process_fork;
|
||||
c2c.tool.lost = perf_event__process_lost;
|
||||
c2c.tool.attr = perf_event__process_attr;
|
||||
c2c.tool.auxtrace_info = perf_event__process_auxtrace_info;
|
||||
c2c.tool.auxtrace = perf_event__process_auxtrace;
|
||||
c2c.tool.auxtrace_error = perf_event__process_auxtrace_error;
|
||||
c2c.tool.ordering_requires_timestamps = true;
|
||||
session = perf_session__new(&data, &c2c.tool);
|
||||
if (IS_ERR(session)) {
|
||||
err = PTR_ERR(session);
|
||||
@@ -3266,7 +3261,7 @@ static int perf_c2c__record(int argc, const char **argv)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (perf_pmu__mem_events_init(pmu)) {
|
||||
if (perf_pmu__mem_events_init()) {
|
||||
pr_err("failed: memory events not supported\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -3290,19 +3285,15 @@ static int perf_c2c__record(int argc, const char **argv)
|
||||
* PERF_MEM_EVENTS__LOAD_STORE if it is supported.
|
||||
*/
|
||||
if (e->tag) {
|
||||
e->record = true;
|
||||
perf_mem_record[PERF_MEM_EVENTS__LOAD_STORE] = true;
|
||||
rec_argv[i++] = "-W";
|
||||
} else {
|
||||
e = perf_pmu__mem_events_ptr(pmu, PERF_MEM_EVENTS__LOAD);
|
||||
e->record = true;
|
||||
|
||||
e = perf_pmu__mem_events_ptr(pmu, PERF_MEM_EVENTS__STORE);
|
||||
e->record = true;
|
||||
perf_mem_record[PERF_MEM_EVENTS__LOAD] = true;
|
||||
perf_mem_record[PERF_MEM_EVENTS__STORE] = true;
|
||||
}
|
||||
}
|
||||
|
||||
e = perf_pmu__mem_events_ptr(pmu, PERF_MEM_EVENTS__LOAD);
|
||||
if (e->record)
|
||||
if (perf_mem_record[PERF_MEM_EVENTS__LOAD])
|
||||
rec_argv[i++] = "-W";
|
||||
|
||||
rec_argv[i++] = "-d";
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "builtin.h"
|
||||
#include "color.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/header.h"
|
||||
#include <tools/config.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <subcmd/parse-options.h>
|
||||
|
||||
static const char * const check_subcommands[] = { "feature", NULL };
|
||||
static struct option check_options[] = {
|
||||
OPT_BOOLEAN('q', "quiet", &quiet, "do not show any warnings or messages"),
|
||||
OPT_END()
|
||||
};
|
||||
static struct option check_feature_options[] = { OPT_PARENT(check_options) };
|
||||
|
||||
static const char *check_usage[] = { NULL, NULL };
|
||||
static const char *check_feature_usage[] = {
|
||||
"perf check feature <feature_list>",
|
||||
NULL
|
||||
};
|
||||
|
||||
struct feature_status supported_features[] = {
|
||||
FEATURE_STATUS("aio", HAVE_AIO_SUPPORT),
|
||||
FEATURE_STATUS("bpf", HAVE_LIBBPF_SUPPORT),
|
||||
FEATURE_STATUS("bpf_skeletons", HAVE_BPF_SKEL),
|
||||
FEATURE_STATUS("debuginfod", HAVE_DEBUGINFOD_SUPPORT),
|
||||
FEATURE_STATUS("dwarf", HAVE_DWARF_SUPPORT),
|
||||
FEATURE_STATUS("dwarf_getlocations", HAVE_DWARF_GETLOCATIONS_SUPPORT),
|
||||
FEATURE_STATUS("dwarf-unwind", HAVE_DWARF_UNWIND_SUPPORT),
|
||||
FEATURE_STATUS("auxtrace", HAVE_AUXTRACE_SUPPORT),
|
||||
FEATURE_STATUS("libaudit", HAVE_LIBAUDIT_SUPPORT),
|
||||
FEATURE_STATUS("libbfd", HAVE_LIBBFD_SUPPORT),
|
||||
FEATURE_STATUS("libcapstone", HAVE_LIBCAPSTONE_SUPPORT),
|
||||
FEATURE_STATUS("libcrypto", HAVE_LIBCRYPTO_SUPPORT),
|
||||
FEATURE_STATUS("libdw-dwarf-unwind", HAVE_DWARF_SUPPORT),
|
||||
FEATURE_STATUS("libelf", HAVE_LIBELF_SUPPORT),
|
||||
FEATURE_STATUS("libnuma", HAVE_LIBNUMA_SUPPORT),
|
||||
FEATURE_STATUS("libopencsd", HAVE_CSTRACE_SUPPORT),
|
||||
FEATURE_STATUS("libperl", HAVE_LIBPERL_SUPPORT),
|
||||
FEATURE_STATUS("libpfm4", HAVE_LIBPFM),
|
||||
FEATURE_STATUS("libpython", HAVE_LIBPYTHON_SUPPORT),
|
||||
FEATURE_STATUS("libslang", HAVE_SLANG_SUPPORT),
|
||||
FEATURE_STATUS("libtraceevent", HAVE_LIBTRACEEVENT),
|
||||
FEATURE_STATUS("libunwind", HAVE_LIBUNWIND_SUPPORT),
|
||||
FEATURE_STATUS("lzma", HAVE_LZMA_SUPPORT),
|
||||
FEATURE_STATUS("numa_num_possible_cpus", HAVE_LIBNUMA_SUPPORT),
|
||||
FEATURE_STATUS("syscall_table", HAVE_SYSCALL_TABLE_SUPPORT),
|
||||
FEATURE_STATUS("zlib", HAVE_ZLIB_SUPPORT),
|
||||
FEATURE_STATUS("zstd", HAVE_ZSTD_SUPPORT),
|
||||
|
||||
/* this should remain at end, to know the array end */
|
||||
FEATURE_STATUS(NULL, _)
|
||||
};
|
||||
|
||||
static void on_off_print(const char *status)
|
||||
{
|
||||
printf("[ ");
|
||||
|
||||
if (!strcmp(status, "OFF"))
|
||||
color_fprintf(stdout, PERF_COLOR_RED, "%-3s", status);
|
||||
else
|
||||
color_fprintf(stdout, PERF_COLOR_GREEN, "%-3s", status);
|
||||
|
||||
printf(" ]");
|
||||
}
|
||||
|
||||
/* Helper function to print status of a feature along with name/macro */
|
||||
static void status_print(const char *name, const char *macro,
|
||||
const char *status)
|
||||
{
|
||||
printf("%22s: ", name);
|
||||
on_off_print(status);
|
||||
printf(" # %s\n", macro);
|
||||
}
|
||||
|
||||
#define STATUS(feature) \
|
||||
do { \
|
||||
if (feature.is_builtin) \
|
||||
status_print(feature.name, feature.macro, "on"); \
|
||||
else \
|
||||
status_print(feature.name, feature.macro, "OFF"); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* check whether "feature" is built-in with perf
|
||||
*
|
||||
* returns:
|
||||
* 0: NOT built-in or Feature not known
|
||||
* 1: Built-in
|
||||
*/
|
||||
static int has_support(const char *feature)
|
||||
{
|
||||
for (int i = 0; supported_features[i].name; ++i) {
|
||||
if ((strcasecmp(feature, supported_features[i].name) == 0) ||
|
||||
(strcasecmp(feature, supported_features[i].macro) == 0)) {
|
||||
if (!quiet)
|
||||
STATUS(supported_features[i]);
|
||||
return supported_features[i].is_builtin;
|
||||
}
|
||||
}
|
||||
|
||||
if (!quiet)
|
||||
pr_err("Unknown feature '%s', please use 'perf version --build-options' to see which ones are available.\n", feature);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Usage: 'perf check feature <feature_list>'
|
||||
*
|
||||
* <feature_list> can be a single feature name/macro, or a comma-separated list
|
||||
* of feature names/macros
|
||||
* eg. argument can be "libtraceevent" or "libtraceevent,bpf" etc
|
||||
*
|
||||
* In case of a comma-separated list, feature_enabled will be 1, only if
|
||||
* all features passed in the string are supported
|
||||
*
|
||||
* Note that argv will get modified
|
||||
*/
|
||||
static int subcommand_feature(int argc, const char **argv)
|
||||
{
|
||||
char *feature_list;
|
||||
char *feature_name;
|
||||
int feature_enabled;
|
||||
|
||||
argc = parse_options(argc, argv, check_feature_options,
|
||||
check_feature_usage, 0);
|
||||
|
||||
if (!argc)
|
||||
usage_with_options(check_feature_usage, check_feature_options);
|
||||
|
||||
if (argc > 1) {
|
||||
pr_err("Too many arguments passed to 'perf check feature'\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
feature_enabled = 1;
|
||||
/* feature_list is a non-const copy of 'argv[0]' */
|
||||
feature_list = strdup(argv[0]);
|
||||
if (!feature_list) {
|
||||
pr_err("ERROR: failed to allocate memory for feature list\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
feature_name = strtok(feature_list, ",");
|
||||
|
||||
while (feature_name) {
|
||||
feature_enabled &= has_support(feature_name);
|
||||
feature_name = strtok(NULL, ",");
|
||||
}
|
||||
|
||||
free(feature_list);
|
||||
|
||||
return !feature_enabled;
|
||||
}
|
||||
|
||||
int cmd_check(int argc, const char **argv)
|
||||
{
|
||||
argc = parse_options_subcommand(argc, argv, check_options,
|
||||
check_subcommands, check_usage, 0);
|
||||
|
||||
if (!argc)
|
||||
usage_with_options(check_usage, check_options);
|
||||
|
||||
if (strcmp(argv[0], "feature") == 0)
|
||||
return subcommand_feature(argc, argv);
|
||||
|
||||
/* If no subcommand matched above, print usage help */
|
||||
pr_err("Unknown subcommand: %s\n", argv[0]);
|
||||
usage_with_options(check_usage, check_options);
|
||||
|
||||
/* free usage string allocated by parse_options_subcommand */
|
||||
free((void *)check_usage[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1434,7 +1434,7 @@ static int __cmd_signal(struct daemon *daemon, struct option parent_options[],
|
||||
}
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
cmd.signal.cmd = CMD_SIGNAL,
|
||||
cmd.signal.cmd = CMD_SIGNAL;
|
||||
cmd.signal.sig = SIGUSR2;
|
||||
strncpy(cmd.signal.name, name, sizeof(cmd.signal.name) - 1);
|
||||
|
||||
|
||||
+18
-20
@@ -388,7 +388,7 @@ struct hist_entry_ops block_hist_ops = {
|
||||
.free = block_hist_free,
|
||||
};
|
||||
|
||||
static int diff__process_sample_event(struct perf_tool *tool,
|
||||
static int diff__process_sample_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
@@ -431,8 +431,8 @@ static int diff__process_sample_event(struct perf_tool *tool,
|
||||
goto out;
|
||||
}
|
||||
|
||||
hist__account_cycles(sample->branch_stack, &al, sample, false,
|
||||
NULL);
|
||||
hist__account_cycles(sample->branch_stack, &al, sample,
|
||||
false, NULL, evsel);
|
||||
break;
|
||||
|
||||
case COMPUTE_STREAM:
|
||||
@@ -467,21 +467,7 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct perf_diff pdiff = {
|
||||
.tool = {
|
||||
.sample = diff__process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.mmap2 = perf_event__process_mmap2,
|
||||
.comm = perf_event__process_comm,
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.lost = perf_event__process_lost,
|
||||
.namespaces = perf_event__process_namespaces,
|
||||
.cgroup = perf_event__process_cgroup,
|
||||
.ordered_events = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
};
|
||||
static struct perf_diff pdiff;
|
||||
|
||||
static struct evsel *evsel_match(struct evsel *evsel,
|
||||
struct evlist *evlist)
|
||||
@@ -705,7 +691,7 @@ static void hists__precompute(struct hists *hists)
|
||||
if (compute == COMPUTE_CYCLES) {
|
||||
bh = container_of(he, struct block_hist, he);
|
||||
init_block_hist(bh);
|
||||
block_info__process_sym(he, bh, NULL, 0);
|
||||
block_info__process_sym(he, bh, NULL, 0, 0);
|
||||
}
|
||||
|
||||
data__for_each_file_new(i, d) {
|
||||
@@ -728,7 +714,7 @@ static void hists__precompute(struct hists *hists)
|
||||
pair_bh = container_of(pair, struct block_hist,
|
||||
he);
|
||||
init_block_hist(pair_bh);
|
||||
block_info__process_sym(pair, pair_bh, NULL, 0);
|
||||
block_info__process_sym(pair, pair_bh, NULL, 0, 0);
|
||||
|
||||
bh = container_of(he, struct block_hist, he);
|
||||
|
||||
@@ -1959,6 +1945,18 @@ int cmd_diff(int argc, const char **argv)
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
perf_tool__init(&pdiff.tool, /*ordered_events=*/true);
|
||||
pdiff.tool.sample = diff__process_sample_event;
|
||||
pdiff.tool.mmap = perf_event__process_mmap;
|
||||
pdiff.tool.mmap2 = perf_event__process_mmap2;
|
||||
pdiff.tool.comm = perf_event__process_comm;
|
||||
pdiff.tool.exit = perf_event__process_exit;
|
||||
pdiff.tool.fork = perf_event__process_fork;
|
||||
pdiff.tool.lost = perf_event__process_lost;
|
||||
pdiff.tool.namespaces = perf_event__process_namespaces;
|
||||
pdiff.tool.cgroup = perf_event__process_cgroup;
|
||||
pdiff.tool.ordering_requires_timestamps = true;
|
||||
|
||||
perf_config(diff__config, NULL);
|
||||
|
||||
argc = parse_options(argc, argv, options, diff_usage, 0);
|
||||
|
||||
@@ -35,13 +35,13 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
.force = details->force,
|
||||
};
|
||||
struct perf_tool tool = {
|
||||
/* only needed for pipe mode */
|
||||
.attr = perf_event__process_attr,
|
||||
.feature = process_header_feature,
|
||||
};
|
||||
bool has_tracepoint = false;
|
||||
struct perf_tool tool;
|
||||
bool has_tracepoint = false, has_group = false;
|
||||
|
||||
perf_tool__init(&tool, /*ordered_events=*/false);
|
||||
/* only needed for pipe mode */
|
||||
tool.attr = perf_event__process_attr;
|
||||
tool.feature = process_header_feature;
|
||||
session = perf_session__new(&data, &tool);
|
||||
if (IS_ERR(session))
|
||||
return PTR_ERR(session);
|
||||
@@ -54,11 +54,17 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details
|
||||
|
||||
if (pos->core.attr.type == PERF_TYPE_TRACEPOINT)
|
||||
has_tracepoint = true;
|
||||
|
||||
if (!evsel__is_group_leader(pos))
|
||||
has_group = true;
|
||||
}
|
||||
|
||||
if (has_tracepoint && !details->trace_fields)
|
||||
printf("# Tip: use 'perf evlist --trace-fields' to show fields for tracepoint events\n");
|
||||
|
||||
if (has_group && !details->event_group)
|
||||
printf("# Tip: use 'perf evlist -g' to show group information\n");
|
||||
|
||||
perf_session__delete(session);
|
||||
return 0;
|
||||
}
|
||||
|
||||
+436
-26
@@ -13,6 +13,7 @@
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <math.h>
|
||||
#include <poll.h>
|
||||
#include <ctype.h>
|
||||
@@ -22,15 +23,18 @@
|
||||
#include "debug.h"
|
||||
#include <subcmd/pager.h>
|
||||
#include <subcmd/parse-options.h>
|
||||
#include <api/io.h>
|
||||
#include <api/fs/tracing_path.h>
|
||||
#include "evlist.h"
|
||||
#include "target.h"
|
||||
#include "cpumap.h"
|
||||
#include "hashmap.h"
|
||||
#include "thread_map.h"
|
||||
#include "strfilter.h"
|
||||
#include "util/cap.h"
|
||||
#include "util/config.h"
|
||||
#include "util/ftrace.h"
|
||||
#include "util/stat.h"
|
||||
#include "util/units.h"
|
||||
#include "util/parse-sublevel-options.h"
|
||||
|
||||
@@ -59,6 +63,41 @@ static void ftrace__workload_exec_failed_signal(int signo __maybe_unused,
|
||||
done = true;
|
||||
}
|
||||
|
||||
static bool check_ftrace_capable(void)
|
||||
{
|
||||
bool used_root;
|
||||
|
||||
if (perf_cap__capable(CAP_PERFMON, &used_root))
|
||||
return true;
|
||||
|
||||
if (!used_root && perf_cap__capable(CAP_SYS_ADMIN, &used_root))
|
||||
return true;
|
||||
|
||||
pr_err("ftrace only works for %s!\n",
|
||||
used_root ? "root"
|
||||
: "users with the CAP_PERFMON or CAP_SYS_ADMIN capability"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_ftrace_supported(void)
|
||||
{
|
||||
char *file;
|
||||
bool supported = false;
|
||||
|
||||
file = get_tracing_file("set_ftrace_pid");
|
||||
if (!file) {
|
||||
pr_debug("cannot get tracing file set_ftrace_pid\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!access(file, F_OK))
|
||||
supported = true;
|
||||
|
||||
put_tracing_file(file);
|
||||
return supported;
|
||||
}
|
||||
|
||||
static int __write_tracing_file(const char *name, const char *val, bool append)
|
||||
{
|
||||
char *file;
|
||||
@@ -228,6 +267,7 @@ static void reset_tracing_options(struct perf_ftrace *ftrace __maybe_unused)
|
||||
write_tracing_option_file("funcgraph-irqs", "1");
|
||||
write_tracing_option_file("funcgraph-proc", "0");
|
||||
write_tracing_option_file("funcgraph-abstime", "0");
|
||||
write_tracing_option_file("funcgraph-tail", "0");
|
||||
write_tracing_option_file("latency-format", "0");
|
||||
write_tracing_option_file("irq-info", "0");
|
||||
}
|
||||
@@ -464,6 +504,17 @@ static int set_tracing_funcgraph_verbose(struct perf_ftrace *ftrace)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_tracing_funcgraph_tail(struct perf_ftrace *ftrace)
|
||||
{
|
||||
if (!ftrace->graph_tail)
|
||||
return 0;
|
||||
|
||||
if (write_tracing_option_file("funcgraph-tail", "1") < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_tracing_thresh(struct perf_ftrace *ftrace)
|
||||
{
|
||||
int ret;
|
||||
@@ -540,6 +591,11 @@ static int set_tracing_options(struct perf_ftrace *ftrace)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (set_tracing_funcgraph_tail(ftrace) < 0) {
|
||||
pr_err("failed to set tracing option funcgraph-tail\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -569,18 +625,6 @@ static int __cmd_ftrace(struct perf_ftrace *ftrace)
|
||||
.events = POLLIN,
|
||||
};
|
||||
|
||||
if (!(perf_cap__capable(CAP_PERFMON) ||
|
||||
perf_cap__capable(CAP_SYS_ADMIN))) {
|
||||
pr_err("ftrace only works for %s!\n",
|
||||
#ifdef HAVE_LIBCAP_SUPPORT
|
||||
"users with the CAP_PERFMON or CAP_SYS_ADMIN capability"
|
||||
#else
|
||||
"root"
|
||||
#endif
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
|
||||
select_tracer(ftrace);
|
||||
|
||||
if (reset_tracing_files(ftrace) < 0) {
|
||||
@@ -885,18 +929,6 @@ static int __cmd_latency(struct perf_ftrace *ftrace)
|
||||
};
|
||||
int buckets[NUM_BUCKET] = { };
|
||||
|
||||
if (!(perf_cap__capable(CAP_PERFMON) ||
|
||||
perf_cap__capable(CAP_SYS_ADMIN))) {
|
||||
pr_err("ftrace only works for %s!\n",
|
||||
#ifdef HAVE_LIBCAP_SUPPORT
|
||||
"users with the CAP_PERFMON or CAP_SYS_ADMIN capability"
|
||||
#else
|
||||
"root"
|
||||
#endif
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
|
||||
trace_fd = prepare_func_latency(ftrace);
|
||||
if (trace_fd < 0)
|
||||
goto out;
|
||||
@@ -950,6 +982,326 @@ out:
|
||||
return (done && !workload_exec_errno) ? 0 : -1;
|
||||
}
|
||||
|
||||
static size_t profile_hash(long func, void *ctx __maybe_unused)
|
||||
{
|
||||
return str_hash((char *)func);
|
||||
}
|
||||
|
||||
static bool profile_equal(long func1, long func2, void *ctx __maybe_unused)
|
||||
{
|
||||
return !strcmp((char *)func1, (char *)func2);
|
||||
}
|
||||
|
||||
static int prepare_func_profile(struct perf_ftrace *ftrace)
|
||||
{
|
||||
ftrace->tracer = "function_graph";
|
||||
ftrace->graph_tail = 1;
|
||||
|
||||
ftrace->profile_hash = hashmap__new(profile_hash, profile_equal, NULL);
|
||||
if (ftrace->profile_hash == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This is saved in a hashmap keyed by the function name */
|
||||
struct ftrace_profile_data {
|
||||
struct stats st;
|
||||
};
|
||||
|
||||
static int add_func_duration(struct perf_ftrace *ftrace, char *func, double time_ns)
|
||||
{
|
||||
struct ftrace_profile_data *prof = NULL;
|
||||
|
||||
if (!hashmap__find(ftrace->profile_hash, func, &prof)) {
|
||||
char *key = strdup(func);
|
||||
|
||||
if (key == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
prof = zalloc(sizeof(*prof));
|
||||
if (prof == NULL) {
|
||||
free(key);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
init_stats(&prof->st);
|
||||
hashmap__add(ftrace->profile_hash, key, prof);
|
||||
}
|
||||
|
||||
update_stats(&prof->st, time_ns);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The ftrace function_graph text output normally looks like below:
|
||||
*
|
||||
* CPU DURATION FUNCTION
|
||||
*
|
||||
* 0) | syscall_trace_enter.isra.0() {
|
||||
* 0) | __audit_syscall_entry() {
|
||||
* 0) | auditd_test_task() {
|
||||
* 0) 0.271 us | __rcu_read_lock();
|
||||
* 0) 0.275 us | __rcu_read_unlock();
|
||||
* 0) 1.254 us | } /\* auditd_test_task *\/
|
||||
* 0) 0.279 us | ktime_get_coarse_real_ts64();
|
||||
* 0) 2.227 us | } /\* __audit_syscall_entry *\/
|
||||
* 0) 2.713 us | } /\* syscall_trace_enter.isra.0 *\/
|
||||
*
|
||||
* Parse the line and get the duration and function name.
|
||||
*/
|
||||
static int parse_func_duration(struct perf_ftrace *ftrace, char *line, size_t len)
|
||||
{
|
||||
char *p;
|
||||
char *func;
|
||||
double duration;
|
||||
|
||||
/* skip CPU */
|
||||
p = strchr(line, ')');
|
||||
if (p == NULL)
|
||||
return 0;
|
||||
|
||||
/* get duration */
|
||||
p = skip_spaces(p + 1);
|
||||
|
||||
/* no duration? */
|
||||
if (p == NULL || *p == '|')
|
||||
return 0;
|
||||
|
||||
/* skip markers like '*' or '!' for longer than ms */
|
||||
if (!isdigit(*p))
|
||||
p++;
|
||||
|
||||
duration = strtod(p, &p);
|
||||
|
||||
if (strncmp(p, " us", 3)) {
|
||||
pr_debug("non-usec time found.. ignoring\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* profile stat keeps the max and min values as integer,
|
||||
* convert to nsec time so that we can have accurate max.
|
||||
*/
|
||||
duration *= 1000;
|
||||
|
||||
/* skip to the pipe */
|
||||
while (p < line + len && *p != '|')
|
||||
p++;
|
||||
|
||||
if (*p++ != '|')
|
||||
return -EINVAL;
|
||||
|
||||
/* get function name */
|
||||
func = skip_spaces(p);
|
||||
|
||||
/* skip the closing bracket and the start of comment */
|
||||
if (*func == '}')
|
||||
func += 5;
|
||||
|
||||
/* remove semi-colon or end of comment at the end */
|
||||
p = line + len - 1;
|
||||
while (!isalnum(*p) && *p != ']') {
|
||||
*p = '\0';
|
||||
--p;
|
||||
}
|
||||
|
||||
return add_func_duration(ftrace, func, duration);
|
||||
}
|
||||
|
||||
enum perf_ftrace_profile_sort_key {
|
||||
PFP_SORT_TOTAL = 0,
|
||||
PFP_SORT_AVG,
|
||||
PFP_SORT_MAX,
|
||||
PFP_SORT_COUNT,
|
||||
PFP_SORT_NAME,
|
||||
};
|
||||
|
||||
static enum perf_ftrace_profile_sort_key profile_sort = PFP_SORT_TOTAL;
|
||||
|
||||
static int cmp_profile_data(const void *a, const void *b)
|
||||
{
|
||||
const struct hashmap_entry *e1 = *(const struct hashmap_entry **)a;
|
||||
const struct hashmap_entry *e2 = *(const struct hashmap_entry **)b;
|
||||
struct ftrace_profile_data *p1 = e1->pvalue;
|
||||
struct ftrace_profile_data *p2 = e2->pvalue;
|
||||
double v1, v2;
|
||||
|
||||
switch (profile_sort) {
|
||||
case PFP_SORT_NAME:
|
||||
return strcmp(e1->pkey, e2->pkey);
|
||||
case PFP_SORT_AVG:
|
||||
v1 = p1->st.mean;
|
||||
v2 = p2->st.mean;
|
||||
break;
|
||||
case PFP_SORT_MAX:
|
||||
v1 = p1->st.max;
|
||||
v2 = p2->st.max;
|
||||
break;
|
||||
case PFP_SORT_COUNT:
|
||||
v1 = p1->st.n;
|
||||
v2 = p2->st.n;
|
||||
break;
|
||||
case PFP_SORT_TOTAL:
|
||||
default:
|
||||
v1 = p1->st.n * p1->st.mean;
|
||||
v2 = p2->st.n * p2->st.mean;
|
||||
break;
|
||||
}
|
||||
|
||||
if (v1 > v2)
|
||||
return -1;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void print_profile_result(struct perf_ftrace *ftrace)
|
||||
{
|
||||
struct hashmap_entry *entry, **profile;
|
||||
size_t i, nr, bkt;
|
||||
|
||||
nr = hashmap__size(ftrace->profile_hash);
|
||||
if (nr == 0)
|
||||
return;
|
||||
|
||||
profile = calloc(nr, sizeof(*profile));
|
||||
if (profile == NULL) {
|
||||
pr_err("failed to allocate memory for the result\n");
|
||||
return;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
hashmap__for_each_entry(ftrace->profile_hash, entry, bkt)
|
||||
profile[i++] = entry;
|
||||
|
||||
assert(i == nr);
|
||||
|
||||
//cmp_profile_data(profile[0], profile[1]);
|
||||
qsort(profile, nr, sizeof(*profile), cmp_profile_data);
|
||||
|
||||
printf("# %10s %10s %10s %10s %s\n",
|
||||
"Total (us)", "Avg (us)", "Max (us)", "Count", "Function");
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
const char *name = profile[i]->pkey;
|
||||
struct ftrace_profile_data *p = profile[i]->pvalue;
|
||||
|
||||
printf("%12.3f %10.3f %6"PRIu64".%03"PRIu64" %10.0f %s\n",
|
||||
p->st.n * p->st.mean / 1000, p->st.mean / 1000,
|
||||
p->st.max / 1000, p->st.max % 1000, p->st.n, name);
|
||||
}
|
||||
|
||||
free(profile);
|
||||
|
||||
hashmap__for_each_entry(ftrace->profile_hash, entry, bkt) {
|
||||
free((char *)entry->pkey);
|
||||
free(entry->pvalue);
|
||||
}
|
||||
|
||||
hashmap__free(ftrace->profile_hash);
|
||||
ftrace->profile_hash = NULL;
|
||||
}
|
||||
|
||||
static int __cmd_profile(struct perf_ftrace *ftrace)
|
||||
{
|
||||
char *trace_file;
|
||||
int trace_fd;
|
||||
char buf[4096];
|
||||
struct io io;
|
||||
char *line = NULL;
|
||||
size_t line_len = 0;
|
||||
|
||||
if (prepare_func_profile(ftrace) < 0) {
|
||||
pr_err("failed to prepare func profiler\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (reset_tracing_files(ftrace) < 0) {
|
||||
pr_err("failed to reset ftrace\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* reset ftrace buffer */
|
||||
if (write_tracing_file("trace", "0") < 0)
|
||||
goto out;
|
||||
|
||||
if (set_tracing_options(ftrace) < 0)
|
||||
return -1;
|
||||
|
||||
if (write_tracing_file("current_tracer", ftrace->tracer) < 0) {
|
||||
pr_err("failed to set current_tracer to %s\n", ftrace->tracer);
|
||||
goto out_reset;
|
||||
}
|
||||
|
||||
setup_pager();
|
||||
|
||||
trace_file = get_tracing_file("trace_pipe");
|
||||
if (!trace_file) {
|
||||
pr_err("failed to open trace_pipe\n");
|
||||
goto out_reset;
|
||||
}
|
||||
|
||||
trace_fd = open(trace_file, O_RDONLY);
|
||||
|
||||
put_tracing_file(trace_file);
|
||||
|
||||
if (trace_fd < 0) {
|
||||
pr_err("failed to open trace_pipe\n");
|
||||
goto out_reset;
|
||||
}
|
||||
|
||||
fcntl(trace_fd, F_SETFL, O_NONBLOCK);
|
||||
|
||||
if (write_tracing_file("tracing_on", "1") < 0) {
|
||||
pr_err("can't enable tracing\n");
|
||||
goto out_close_fd;
|
||||
}
|
||||
|
||||
evlist__start_workload(ftrace->evlist);
|
||||
|
||||
io__init(&io, trace_fd, buf, sizeof(buf));
|
||||
io.timeout_ms = -1;
|
||||
|
||||
while (!done && !io.eof) {
|
||||
if (io__getline(&io, &line, &line_len) < 0)
|
||||
break;
|
||||
|
||||
if (parse_func_duration(ftrace, line, line_len) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
write_tracing_file("tracing_on", "0");
|
||||
|
||||
if (workload_exec_errno) {
|
||||
const char *emsg = str_error_r(workload_exec_errno, buf, sizeof(buf));
|
||||
/* flush stdout first so below error msg appears at the end. */
|
||||
fflush(stdout);
|
||||
pr_err("workload failed: %s\n", emsg);
|
||||
goto out_free_line;
|
||||
}
|
||||
|
||||
/* read remaining buffer contents */
|
||||
io.timeout_ms = 0;
|
||||
while (!io.eof) {
|
||||
if (io__getline(&io, &line, &line_len) < 0)
|
||||
break;
|
||||
|
||||
if (parse_func_duration(ftrace, line, line_len) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
print_profile_result(ftrace);
|
||||
|
||||
out_free_line:
|
||||
free(line);
|
||||
out_close_fd:
|
||||
close(trace_fd);
|
||||
out_reset:
|
||||
reset_tracing_files(ftrace);
|
||||
out:
|
||||
return (done && !workload_exec_errno) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int perf_ftrace_config(const char *var, const char *value, void *cb)
|
||||
{
|
||||
struct perf_ftrace *ftrace = cb;
|
||||
@@ -1099,6 +1451,7 @@ static int parse_graph_tracer_opts(const struct option *opt,
|
||||
{ .name = "verbose", .value_ptr = &ftrace->graph_verbose },
|
||||
{ .name = "thresh", .value_ptr = &ftrace->graph_thresh },
|
||||
{ .name = "depth", .value_ptr = &ftrace->graph_depth },
|
||||
{ .name = "tail", .value_ptr = &ftrace->graph_tail },
|
||||
{ .name = NULL, }
|
||||
};
|
||||
|
||||
@@ -1112,10 +1465,35 @@ static int parse_graph_tracer_opts(const struct option *opt,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_sort_key(const struct option *opt, const char *str, int unset)
|
||||
{
|
||||
enum perf_ftrace_profile_sort_key *key = (void *)opt->value;
|
||||
|
||||
if (unset)
|
||||
return 0;
|
||||
|
||||
if (!strcmp(str, "total"))
|
||||
*key = PFP_SORT_TOTAL;
|
||||
else if (!strcmp(str, "avg"))
|
||||
*key = PFP_SORT_AVG;
|
||||
else if (!strcmp(str, "max"))
|
||||
*key = PFP_SORT_MAX;
|
||||
else if (!strcmp(str, "count"))
|
||||
*key = PFP_SORT_COUNT;
|
||||
else if (!strcmp(str, "name"))
|
||||
*key = PFP_SORT_NAME;
|
||||
else {
|
||||
pr_err("Unknown sort key: %s\n", str);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum perf_ftrace_subcommand {
|
||||
PERF_FTRACE_NONE,
|
||||
PERF_FTRACE_TRACE,
|
||||
PERF_FTRACE_LATENCY,
|
||||
PERF_FTRACE_PROFILE,
|
||||
};
|
||||
|
||||
int cmd_ftrace(int argc, const char **argv)
|
||||
@@ -1181,13 +1559,31 @@ int cmd_ftrace(int argc, const char **argv)
|
||||
"Use nano-second histogram"),
|
||||
OPT_PARENT(common_options),
|
||||
};
|
||||
const struct option profile_options[] = {
|
||||
OPT_CALLBACK('T', "trace-funcs", &ftrace.filters, "func",
|
||||
"Trace given functions using function tracer",
|
||||
parse_filter_func),
|
||||
OPT_CALLBACK('N', "notrace-funcs", &ftrace.notrace, "func",
|
||||
"Do not trace given functions", parse_filter_func),
|
||||
OPT_CALLBACK('G', "graph-funcs", &ftrace.graph_funcs, "func",
|
||||
"Trace given functions using function_graph tracer",
|
||||
parse_filter_func),
|
||||
OPT_CALLBACK('g', "nograph-funcs", &ftrace.nograph_funcs, "func",
|
||||
"Set nograph filter on given functions", parse_filter_func),
|
||||
OPT_CALLBACK('m', "buffer-size", &ftrace.percpu_buffer_size, "size",
|
||||
"Size of per cpu buffer, needs to use a B, K, M or G suffix.", parse_buffer_size),
|
||||
OPT_CALLBACK('s', "sort", &profile_sort, "key",
|
||||
"Sort result by key: total (default), avg, max, count, name.",
|
||||
parse_sort_key),
|
||||
OPT_PARENT(common_options),
|
||||
};
|
||||
const struct option *options = ftrace_options;
|
||||
|
||||
const char * const ftrace_usage[] = {
|
||||
"perf ftrace [<options>] [<command>]",
|
||||
"perf ftrace [<options>] -- [<command>] [<options>]",
|
||||
"perf ftrace {trace|latency} [<options>] [<command>]",
|
||||
"perf ftrace {trace|latency} [<options>] -- [<command>] [<options>]",
|
||||
"perf ftrace {trace|latency|profile} [<options>] [<command>]",
|
||||
"perf ftrace {trace|latency|profile} [<options>] -- [<command>] [<options>]",
|
||||
NULL
|
||||
};
|
||||
enum perf_ftrace_subcommand subcmd = PERF_FTRACE_NONE;
|
||||
@@ -1202,6 +1598,14 @@ int cmd_ftrace(int argc, const char **argv)
|
||||
signal(SIGCHLD, sig_handler);
|
||||
signal(SIGPIPE, sig_handler);
|
||||
|
||||
if (!check_ftrace_capable())
|
||||
return -1;
|
||||
|
||||
if (!is_ftrace_supported()) {
|
||||
pr_err("ftrace is not supported on this system\n");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
ret = perf_config(perf_ftrace_config, &ftrace);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
@@ -1212,6 +1616,9 @@ int cmd_ftrace(int argc, const char **argv)
|
||||
} else if (!strcmp(argv[1], "latency")) {
|
||||
subcmd = PERF_FTRACE_LATENCY;
|
||||
options = latency_options;
|
||||
} else if (!strcmp(argv[1], "profile")) {
|
||||
subcmd = PERF_FTRACE_PROFILE;
|
||||
options = profile_options;
|
||||
}
|
||||
|
||||
if (subcmd != PERF_FTRACE_NONE) {
|
||||
@@ -1247,6 +1654,9 @@ int cmd_ftrace(int argc, const char **argv)
|
||||
}
|
||||
cmd_func = __cmd_latency;
|
||||
break;
|
||||
case PERF_FTRACE_PROFILE:
|
||||
cmd_func = __cmd_profile;
|
||||
break;
|
||||
case PERF_FTRACE_NONE:
|
||||
default:
|
||||
pr_err("Invalid subcommand\n");
|
||||
|
||||
@@ -417,7 +417,7 @@ static void open_html(const char *path)
|
||||
static int show_html_page(const char *perf_cmd)
|
||||
{
|
||||
const char *page = cmd_to_page(perf_cmd);
|
||||
char *page_path; /* it leaks but we exec bellow */
|
||||
char *page_path; /* it leaks but we exec below */
|
||||
|
||||
if (get_html_page_path(&page_path, page) < 0)
|
||||
return -1;
|
||||
|
||||
+480
-281
File diff suppressed because it is too large
Load Diff
+11
-11
@@ -955,7 +955,7 @@ static bool perf_kmem__skip_sample(struct perf_sample *sample)
|
||||
typedef int (*tracepoint_handler)(struct evsel *evsel,
|
||||
struct perf_sample *sample);
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
static int process_sample_event(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
@@ -986,15 +986,6 @@ static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct perf_tool perf_kmem = {
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.mmap2 = perf_event__process_mmap2,
|
||||
.namespaces = perf_event__process_namespaces,
|
||||
.ordered_events = true,
|
||||
};
|
||||
|
||||
static double fragmentation(unsigned long n_req, unsigned long n_alloc)
|
||||
{
|
||||
if (n_alloc == 0)
|
||||
@@ -1971,6 +1962,7 @@ int cmd_kmem(int argc, const char **argv)
|
||||
NULL
|
||||
};
|
||||
struct perf_session *session;
|
||||
struct perf_tool perf_kmem;
|
||||
static const char errmsg[] = "No %s allocation events found. Have you run 'perf kmem record --%s'?\n";
|
||||
int ret = perf_config(kmem_config, NULL);
|
||||
|
||||
@@ -1998,6 +1990,13 @@ int cmd_kmem(int argc, const char **argv)
|
||||
|
||||
data.path = input_name;
|
||||
|
||||
perf_tool__init(&perf_kmem, /*ordered_events=*/true);
|
||||
perf_kmem.sample = process_sample_event;
|
||||
perf_kmem.comm = perf_event__process_comm;
|
||||
perf_kmem.mmap = perf_event__process_mmap;
|
||||
perf_kmem.mmap2 = perf_event__process_mmap2;
|
||||
perf_kmem.namespaces = perf_event__process_namespaces;
|
||||
|
||||
kmem_session = session = perf_session__new(&data, &perf_kmem);
|
||||
if (IS_ERR(session))
|
||||
return PTR_ERR(session);
|
||||
@@ -2058,7 +2057,8 @@ int cmd_kmem(int argc, const char **argv)
|
||||
|
||||
out_delete:
|
||||
perf_session__delete(session);
|
||||
/* free usage string allocated by parse_options_subcommand */
|
||||
free((void *)kmem_usage[0]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+11
-11
@@ -1166,7 +1166,7 @@ static void print_result(struct perf_kvm_stat *kvm)
|
||||
}
|
||||
|
||||
#if defined(HAVE_TIMERFD_SUPPORT) && defined(HAVE_LIBTRACEEVENT)
|
||||
static int process_lost_event(struct perf_tool *tool,
|
||||
static int process_lost_event(const struct perf_tool *tool,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine __maybe_unused)
|
||||
@@ -1187,7 +1187,7 @@ static bool skip_sample(struct perf_kvm_stat *kvm,
|
||||
return false;
|
||||
}
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool,
|
||||
static int process_sample_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
@@ -1603,19 +1603,17 @@ static int read_events(struct perf_kvm_stat *kvm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
struct perf_tool eops = {
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.namespaces = perf_event__process_namespaces,
|
||||
.ordered_events = true,
|
||||
};
|
||||
struct perf_data file = {
|
||||
.path = kvm->file_name,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
.force = kvm->force,
|
||||
};
|
||||
|
||||
kvm->tool = eops;
|
||||
perf_tool__init(&kvm->tool, /*ordered_events=*/true);
|
||||
kvm->tool.sample = process_sample_event;
|
||||
kvm->tool.comm = perf_event__process_comm;
|
||||
kvm->tool.namespaces = perf_event__process_namespaces;
|
||||
|
||||
kvm->session = perf_session__new(&file, &kvm->tool);
|
||||
if (IS_ERR(kvm->session)) {
|
||||
pr_err("Initializing perf session failed\n");
|
||||
@@ -1919,14 +1917,13 @@ static int kvm_events_live(struct perf_kvm_stat *kvm,
|
||||
|
||||
|
||||
/* event handling */
|
||||
perf_tool__init(&kvm->tool, /*ordered_events=*/true);
|
||||
kvm->tool.sample = process_sample_event;
|
||||
kvm->tool.comm = perf_event__process_comm;
|
||||
kvm->tool.exit = perf_event__process_exit;
|
||||
kvm->tool.fork = perf_event__process_fork;
|
||||
kvm->tool.lost = process_lost_event;
|
||||
kvm->tool.namespaces = perf_event__process_namespaces;
|
||||
kvm->tool.ordered_events = true;
|
||||
perf_tool__fill_defaults(&kvm->tool);
|
||||
|
||||
/* set defaults */
|
||||
kvm->display_time = 1;
|
||||
@@ -2187,5 +2184,8 @@ int cmd_kvm(int argc, const char **argv)
|
||||
else
|
||||
usage_with_options(kvm_usage, kvm_options);
|
||||
|
||||
/* free usage string allocated by parse_options_subcommand */
|
||||
free((void *)kvm_usage[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+19
-17
@@ -958,7 +958,7 @@ static int top_sched_switch_event(struct perf_kwork *kwork,
|
||||
}
|
||||
|
||||
static struct kwork_class kwork_irq;
|
||||
static int process_irq_handler_entry_event(struct perf_tool *tool,
|
||||
static int process_irq_handler_entry_event(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -971,7 +971,7 @@ static int process_irq_handler_entry_event(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_irq_handler_exit_event(struct perf_tool *tool,
|
||||
static int process_irq_handler_exit_event(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -1037,7 +1037,7 @@ static struct kwork_class kwork_irq = {
|
||||
};
|
||||
|
||||
static struct kwork_class kwork_softirq;
|
||||
static int process_softirq_raise_event(struct perf_tool *tool,
|
||||
static int process_softirq_raise_event(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -1051,7 +1051,7 @@ static int process_softirq_raise_event(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_softirq_entry_event(struct perf_tool *tool,
|
||||
static int process_softirq_entry_event(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -1065,7 +1065,7 @@ static int process_softirq_entry_event(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_softirq_exit_event(struct perf_tool *tool,
|
||||
static int process_softirq_exit_event(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -1167,7 +1167,7 @@ static struct kwork_class kwork_softirq = {
|
||||
};
|
||||
|
||||
static struct kwork_class kwork_workqueue;
|
||||
static int process_workqueue_activate_work_event(struct perf_tool *tool,
|
||||
static int process_workqueue_activate_work_event(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -1181,7 +1181,7 @@ static int process_workqueue_activate_work_event(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_workqueue_execute_start_event(struct perf_tool *tool,
|
||||
static int process_workqueue_execute_start_event(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -1195,7 +1195,7 @@ static int process_workqueue_execute_start_event(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_workqueue_execute_end_event(struct perf_tool *tool,
|
||||
static int process_workqueue_execute_end_event(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -1266,7 +1266,7 @@ static struct kwork_class kwork_workqueue = {
|
||||
};
|
||||
|
||||
static struct kwork_class kwork_sched;
|
||||
static int process_sched_switch_event(struct perf_tool *tool,
|
||||
static int process_sched_switch_event(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -1945,12 +1945,12 @@ static int perf_kwork__report(struct perf_kwork *kwork)
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*tracepoint_handler)(struct perf_tool *tool,
|
||||
typedef int (*tracepoint_handler)(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
|
||||
static int perf_kwork__process_tracepoint_sample(struct perf_tool *tool,
|
||||
static int perf_kwork__process_tracepoint_sample(const struct perf_tool *tool,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
@@ -2322,12 +2322,6 @@ int cmd_kwork(int argc, const char **argv)
|
||||
{
|
||||
static struct perf_kwork kwork = {
|
||||
.class_list = LIST_HEAD_INIT(kwork.class_list),
|
||||
.tool = {
|
||||
.mmap = perf_event__process_mmap,
|
||||
.mmap2 = perf_event__process_mmap2,
|
||||
.sample = perf_kwork__process_tracepoint_sample,
|
||||
.ordered_events = true,
|
||||
},
|
||||
.atom_page_list = LIST_HEAD_INIT(kwork.atom_page_list),
|
||||
.sort_list = LIST_HEAD_INIT(kwork.sort_list),
|
||||
.cmp_id = LIST_HEAD_INIT(kwork.cmp_id),
|
||||
@@ -2462,6 +2456,11 @@ int cmd_kwork(int argc, const char **argv)
|
||||
"record", "report", "latency", "timehist", "top", NULL
|
||||
};
|
||||
|
||||
perf_tool__init(&kwork.tool, /*ordered_events=*/true);
|
||||
kwork.tool.mmap = perf_event__process_mmap;
|
||||
kwork.tool.mmap2 = perf_event__process_mmap2;
|
||||
kwork.tool.sample = perf_kwork__process_tracepoint_sample;
|
||||
|
||||
argc = parse_options_subcommand(argc, argv, kwork_options,
|
||||
kwork_subcommands, kwork_usage,
|
||||
PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
@@ -2520,5 +2519,8 @@ int cmd_kwork(int argc, const char **argv)
|
||||
} else
|
||||
usage_with_options(kwork_usage, kwork_options);
|
||||
|
||||
/* free usage string allocated by parse_options_subcommand */
|
||||
free((void *)kwork_usage[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ static void default_print_event(void *ps, const char *pmu_name, const char *topi
|
||||
if (pmu_name && strcmp(pmu_name, "default_core")) {
|
||||
desc_len = strlen(desc);
|
||||
desc_len = asprintf(&desc_with_unit,
|
||||
desc[desc_len - 1] != '.'
|
||||
desc_len > 0 && desc[desc_len - 1] != '.'
|
||||
? "%s. Unit: %s" : "%s Unit: %s",
|
||||
desc, pmu_name);
|
||||
}
|
||||
|
||||
+23
-21
@@ -1501,7 +1501,7 @@ static const struct evsel_str_handler contention_tracepoints[] = {
|
||||
{ "lock:contention_end", evsel__process_contention_end, },
|
||||
};
|
||||
|
||||
static int process_event_update(struct perf_tool *tool,
|
||||
static int process_event_update(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct evlist **pevlist)
|
||||
{
|
||||
@@ -1520,7 +1520,7 @@ static int process_event_update(struct perf_tool *tool,
|
||||
typedef int (*tracepoint_handler)(struct evsel *evsel,
|
||||
struct perf_sample *sample);
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
static int process_sample_event(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
@@ -1933,22 +1933,21 @@ static bool force;
|
||||
static int __cmd_report(bool display_info)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
struct perf_tool eops = {
|
||||
.attr = perf_event__process_attr,
|
||||
.event_update = process_event_update,
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.namespaces = perf_event__process_namespaces,
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
.ordered_events = true,
|
||||
};
|
||||
struct perf_tool eops;
|
||||
struct perf_data data = {
|
||||
.path = input_name,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
.force = force,
|
||||
};
|
||||
|
||||
perf_tool__init(&eops, /*ordered_events=*/true);
|
||||
eops.attr = perf_event__process_attr;
|
||||
eops.event_update = process_event_update;
|
||||
eops.sample = process_sample_event;
|
||||
eops.comm = perf_event__process_comm;
|
||||
eops.mmap = perf_event__process_mmap;
|
||||
eops.namespaces = perf_event__process_namespaces;
|
||||
eops.tracing_data = perf_event__process_tracing_data;
|
||||
session = perf_session__new(&data, &eops);
|
||||
if (IS_ERR(session)) {
|
||||
pr_err("Initializing perf session failed\n");
|
||||
@@ -2069,15 +2068,7 @@ static int check_lock_contention_options(const struct option *options,
|
||||
static int __cmd_contention(int argc, const char **argv)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
struct perf_tool eops = {
|
||||
.attr = perf_event__process_attr,
|
||||
.event_update = process_event_update,
|
||||
.sample = process_sample_event,
|
||||
.comm = perf_event__process_comm,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
.ordered_events = true,
|
||||
};
|
||||
struct perf_tool eops;
|
||||
struct perf_data data = {
|
||||
.path = input_name,
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
@@ -2100,6 +2091,14 @@ static int __cmd_contention(int argc, const char **argv)
|
||||
|
||||
con.result = &lockhash_table[0];
|
||||
|
||||
perf_tool__init(&eops, /*ordered_events=*/true);
|
||||
eops.attr = perf_event__process_attr;
|
||||
eops.event_update = process_event_update;
|
||||
eops.sample = process_sample_event;
|
||||
eops.comm = perf_event__process_comm;
|
||||
eops.mmap = perf_event__process_mmap;
|
||||
eops.tracing_data = perf_event__process_tracing_data;
|
||||
|
||||
session = perf_session__new(use_bpf ? NULL : &data, &eops);
|
||||
if (IS_ERR(session)) {
|
||||
pr_err("Initializing perf session failed\n");
|
||||
@@ -2713,6 +2712,9 @@ int cmd_lock(int argc, const char **argv)
|
||||
usage_with_options(lock_usage, lock_options);
|
||||
}
|
||||
|
||||
/* free usage string allocated by parse_options_subcommand */
|
||||
free((void *)lock_usage[0]);
|
||||
|
||||
zfree(&lockhash_table);
|
||||
return rc;
|
||||
}
|
||||
|
||||
+94
-67
@@ -19,6 +19,7 @@
|
||||
#include "util/symbol.h"
|
||||
#include "util/pmus.h"
|
||||
#include "util/sample.h"
|
||||
#include "util/sort.h"
|
||||
#include "util/string2.h"
|
||||
#include "util/util.h"
|
||||
#include <linux/err.h>
|
||||
@@ -28,12 +29,16 @@
|
||||
|
||||
struct perf_mem {
|
||||
struct perf_tool tool;
|
||||
char const *input_name;
|
||||
const char *input_name;
|
||||
const char *sort_key;
|
||||
bool hide_unresolved;
|
||||
bool dump_raw;
|
||||
bool force;
|
||||
bool phys_addr;
|
||||
bool data_page_size;
|
||||
bool all_kernel;
|
||||
bool all_user;
|
||||
bool data_type;
|
||||
int operation;
|
||||
const char *cpu_list;
|
||||
DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
@@ -42,7 +47,7 @@ struct perf_mem {
|
||||
static int parse_record_events(const struct option *opt,
|
||||
const char *str, int unset __maybe_unused)
|
||||
{
|
||||
struct perf_mem *mem = *(struct perf_mem **)opt->value;
|
||||
struct perf_mem *mem = (struct perf_mem *)opt->value;
|
||||
struct perf_pmu *pmu;
|
||||
|
||||
pmu = perf_mem_events_find_pmu();
|
||||
@@ -62,33 +67,19 @@ static int parse_record_events(const struct option *opt,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const __usage[] = {
|
||||
"perf mem record [<options>] [<command>]",
|
||||
"perf mem record [<options>] -- <command> [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char * const *record_mem_usage = __usage;
|
||||
|
||||
static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
|
||||
static int __cmd_record(int argc, const char **argv, struct perf_mem *mem,
|
||||
const struct option *options)
|
||||
{
|
||||
int rec_argc, i = 0, j;
|
||||
int start, end;
|
||||
const char **rec_argv;
|
||||
int ret;
|
||||
bool all_user = false, all_kernel = false;
|
||||
struct perf_mem_event *e;
|
||||
struct perf_pmu *pmu;
|
||||
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_UINTEGER(0, "ldlat", &perf_mem_events__loads_ldlat, "mem-loads latency"),
|
||||
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()
|
||||
const char * const record_usage[] = {
|
||||
"perf mem record [<options>] [<command>]",
|
||||
"perf mem record [<options>] -- <command> [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
pmu = perf_mem_events_find_pmu();
|
||||
@@ -97,12 +88,12 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (perf_pmu__mem_events_init(pmu)) {
|
||||
if (perf_pmu__mem_events_init()) {
|
||||
pr_err("failed: memory events not supported\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
argc = parse_options(argc, argv, options, record_mem_usage,
|
||||
argc = parse_options(argc, argv, options, record_usage,
|
||||
PARSE_OPT_KEEP_UNKNOWN);
|
||||
|
||||
/* Max number of arguments multiplied by number of PMUs that can support them. */
|
||||
@@ -126,22 +117,17 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
|
||||
if (e->tag &&
|
||||
(mem->operation & MEM_OPERATION_LOAD) &&
|
||||
(mem->operation & MEM_OPERATION_STORE)) {
|
||||
e->record = true;
|
||||
perf_mem_record[PERF_MEM_EVENTS__LOAD_STORE] = true;
|
||||
rec_argv[i++] = "-W";
|
||||
} else {
|
||||
if (mem->operation & MEM_OPERATION_LOAD) {
|
||||
e = perf_pmu__mem_events_ptr(pmu, PERF_MEM_EVENTS__LOAD);
|
||||
e->record = true;
|
||||
}
|
||||
if (mem->operation & MEM_OPERATION_LOAD)
|
||||
perf_mem_record[PERF_MEM_EVENTS__LOAD] = true;
|
||||
|
||||
if (mem->operation & MEM_OPERATION_STORE) {
|
||||
e = perf_pmu__mem_events_ptr(pmu, PERF_MEM_EVENTS__STORE);
|
||||
e->record = true;
|
||||
}
|
||||
if (mem->operation & MEM_OPERATION_STORE)
|
||||
perf_mem_record[PERF_MEM_EVENTS__STORE] = true;
|
||||
}
|
||||
|
||||
e = perf_pmu__mem_events_ptr(pmu, PERF_MEM_EVENTS__LOAD);
|
||||
if (e->record)
|
||||
if (perf_mem_record[PERF_MEM_EVENTS__LOAD])
|
||||
rec_argv[i++] = "-W";
|
||||
|
||||
rec_argv[i++] = "-d";
|
||||
@@ -158,10 +144,10 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem)
|
||||
goto out;
|
||||
end = i;
|
||||
|
||||
if (all_user)
|
||||
if (mem->all_user)
|
||||
rec_argv[i++] = "--all-user";
|
||||
|
||||
if (all_kernel)
|
||||
if (mem->all_kernel)
|
||||
rec_argv[i++] = "--all-kernel";
|
||||
|
||||
if (mem->cpu_list) {
|
||||
@@ -188,7 +174,7 @@ out:
|
||||
}
|
||||
|
||||
static int
|
||||
dump_raw_samples(struct perf_tool *tool,
|
||||
dump_raw_samples(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -262,7 +248,7 @@ out_put:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool,
|
||||
static int process_sample_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel __maybe_unused,
|
||||
@@ -285,7 +271,23 @@ static int report_raw_events(struct perf_mem *mem)
|
||||
.force = mem->force,
|
||||
};
|
||||
int ret;
|
||||
struct perf_session *session = perf_session__new(&data, &mem->tool);
|
||||
struct perf_session *session;
|
||||
|
||||
perf_tool__init(&mem->tool, /*ordered_events=*/true);
|
||||
mem->tool.sample = process_sample_event;
|
||||
mem->tool.mmap = perf_event__process_mmap;
|
||||
mem->tool.mmap2 = perf_event__process_mmap2;
|
||||
mem->tool.comm = perf_event__process_comm;
|
||||
mem->tool.lost = perf_event__process_lost;
|
||||
mem->tool.fork = perf_event__process_fork;
|
||||
mem->tool.attr = perf_event__process_attr;
|
||||
mem->tool.build_id = perf_event__process_build_id;
|
||||
mem->tool.namespaces = perf_event__process_namespaces;
|
||||
mem->tool.auxtrace_info = perf_event__process_auxtrace_info;
|
||||
mem->tool.auxtrace = perf_event__process_auxtrace;
|
||||
mem->tool.auxtrace_error = perf_event__process_auxtrace_error;
|
||||
|
||||
session = perf_session__new(&data, &mem->tool);
|
||||
|
||||
if (IS_ERR(session))
|
||||
return PTR_ERR(session);
|
||||
@@ -319,16 +321,21 @@ out_delete:
|
||||
perf_session__delete(session);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *get_sort_order(struct perf_mem *mem)
|
||||
{
|
||||
bool has_extra_options = (mem->phys_addr | mem->data_page_size) ? true : false;
|
||||
char sort[128];
|
||||
|
||||
if (mem->sort_key)
|
||||
scnprintf(sort, sizeof(sort), "--sort=%s", mem->sort_key);
|
||||
else if (mem->data_type)
|
||||
strcpy(sort, "--sort=mem,snoop,tlb,type");
|
||||
/*
|
||||
* there is no weight (cost) associated with stores, so don't print
|
||||
* the column
|
||||
*/
|
||||
if (!(mem->operation & MEM_OPERATION_LOAD)) {
|
||||
else if (!(mem->operation & MEM_OPERATION_LOAD)) {
|
||||
strcpy(sort, "--sort=mem,sym,dso,symbol_daddr,"
|
||||
"dso_daddr,tlb,locked");
|
||||
} else if (has_extra_options) {
|
||||
@@ -343,14 +350,26 @@ static char *get_sort_order(struct perf_mem *mem)
|
||||
if (mem->data_page_size)
|
||||
strcat(sort, ",data_page_size");
|
||||
|
||||
/* make sure it has 'type' sort key even -s option is used */
|
||||
if (mem->data_type && !strstr(sort, "type"))
|
||||
strcat(sort, ",type");
|
||||
|
||||
return strdup(sort);
|
||||
}
|
||||
|
||||
static int report_events(int argc, const char **argv, struct perf_mem *mem)
|
||||
static int __cmd_report(int argc, const char **argv, struct perf_mem *mem,
|
||||
const struct option *options)
|
||||
{
|
||||
const char **rep_argv;
|
||||
int ret, i = 0, j, rep_argc;
|
||||
char *new_sort_order;
|
||||
const char * const report_usage[] = {
|
||||
"perf mem report [<options>]",
|
||||
NULL
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, report_usage,
|
||||
PARSE_OPT_KEEP_UNKNOWN);
|
||||
|
||||
if (mem->dump_raw)
|
||||
return report_raw_events(mem);
|
||||
@@ -368,10 +387,11 @@ static int report_events(int argc, const char **argv, struct perf_mem *mem)
|
||||
if (new_sort_order)
|
||||
rep_argv[i++] = new_sort_order;
|
||||
|
||||
for (j = 1; j < argc; j++, i++)
|
||||
for (j = 0; j < argc; j++, i++)
|
||||
rep_argv[i] = argv[j];
|
||||
|
||||
ret = cmd_report(i, rep_argv);
|
||||
free(new_sort_order);
|
||||
free(rep_argv);
|
||||
return ret;
|
||||
}
|
||||
@@ -449,47 +469,51 @@ int cmd_mem(int argc, const char **argv)
|
||||
{
|
||||
struct stat st;
|
||||
struct perf_mem mem = {
|
||||
.tool = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.mmap2 = perf_event__process_mmap2,
|
||||
.comm = perf_event__process_comm,
|
||||
.lost = perf_event__process_lost,
|
||||
.fork = perf_event__process_fork,
|
||||
.attr = perf_event__process_attr,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.namespaces = perf_event__process_namespaces,
|
||||
.auxtrace_info = perf_event__process_auxtrace_info,
|
||||
.auxtrace = perf_event__process_auxtrace,
|
||||
.auxtrace_error = perf_event__process_auxtrace_error,
|
||||
.ordered_events = true,
|
||||
},
|
||||
.input_name = "perf.data",
|
||||
/*
|
||||
* default to both load an store sampling
|
||||
*/
|
||||
.operation = MEM_OPERATION_LOAD | MEM_OPERATION_STORE,
|
||||
};
|
||||
char *sort_order_help = sort_help("sort by key(s):", SORT_MODE__MEMORY);
|
||||
const struct option mem_options[] = {
|
||||
OPT_CALLBACK('t', "type", &mem.operation,
|
||||
"type", "memory operations(load,store) Default load,store",
|
||||
parse_mem_ops),
|
||||
OPT_STRING('C', "cpu", &mem.cpu_list, "cpu",
|
||||
"list of cpus to profile"),
|
||||
OPT_BOOLEAN('f', "force", &mem.force, "don't complain, do it"),
|
||||
OPT_INCR('v', "verbose", &verbose,
|
||||
"be more verbose (show counter open errors, etc)"),
|
||||
OPT_BOOLEAN('p', "phys-data", &mem.phys_addr, "Record/Report sample physical addresses"),
|
||||
OPT_BOOLEAN(0, "data-page-size", &mem.data_page_size, "Record/Report sample data address page size"),
|
||||
OPT_END()
|
||||
};
|
||||
const struct option record_options[] = {
|
||||
OPT_CALLBACK('e', "event", &mem, "event",
|
||||
"event selector. use 'perf mem record -e list' to list available events",
|
||||
parse_record_events),
|
||||
OPT_UINTEGER(0, "ldlat", &perf_mem_events__loads_ldlat, "mem-loads latency"),
|
||||
OPT_BOOLEAN('U', "all-user", &mem.all_user, "collect only user level data"),
|
||||
OPT_BOOLEAN('K', "all-kernel", &mem.all_kernel, "collect only kernel level data"),
|
||||
OPT_PARENT(mem_options)
|
||||
};
|
||||
const struct option report_options[] = {
|
||||
OPT_BOOLEAN('D', "dump-raw-samples", &mem.dump_raw,
|
||||
"dump raw samples in ASCII"),
|
||||
OPT_BOOLEAN('U', "hide-unresolved", &mem.hide_unresolved,
|
||||
"Only display entries resolved to a symbol"),
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
"input file name"),
|
||||
OPT_STRING('C', "cpu", &mem.cpu_list, "cpu",
|
||||
"list of cpus to profile"),
|
||||
OPT_STRING_NOEMPTY('x', "field-separator", &symbol_conf.field_sep,
|
||||
"separator",
|
||||
"separator for columns, no spaces will be added"
|
||||
" between columns '.' is reserved."),
|
||||
OPT_BOOLEAN('f', "force", &mem.force, "don't complain, do it"),
|
||||
OPT_BOOLEAN('p', "phys-data", &mem.phys_addr, "Record/Report sample physical addresses"),
|
||||
OPT_BOOLEAN(0, "data-page-size", &mem.data_page_size, "Record/Report sample data address page size"),
|
||||
OPT_END()
|
||||
OPT_STRING('s', "sort", &mem.sort_key, "key[,key2...]",
|
||||
sort_order_help),
|
||||
OPT_BOOLEAN('T', "type-profile", &mem.data_type,
|
||||
"Show data-type profile result"),
|
||||
OPT_PARENT(mem_options)
|
||||
};
|
||||
const char *const mem_subcommands[] = { "record", "report", NULL };
|
||||
const char *mem_usage[] = {
|
||||
@@ -498,7 +522,7 @@ int cmd_mem(int argc, const char **argv)
|
||||
};
|
||||
|
||||
argc = parse_options_subcommand(argc, argv, mem_options, mem_subcommands,
|
||||
mem_usage, PARSE_OPT_KEEP_UNKNOWN);
|
||||
mem_usage, PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
|
||||
if (!argc || !(strncmp(argv[0], "rec", 3) || mem.operation))
|
||||
usage_with_options(mem_usage, mem_options);
|
||||
@@ -511,11 +535,14 @@ int cmd_mem(int argc, const char **argv)
|
||||
}
|
||||
|
||||
if (strlen(argv[0]) > 2 && strstarts("record", argv[0]))
|
||||
return __cmd_record(argc, argv, &mem);
|
||||
return __cmd_record(argc, argv, &mem, record_options);
|
||||
else if (strlen(argv[0]) > 2 && strstarts("report", argv[0]))
|
||||
return report_events(argc, argv, &mem);
|
||||
return __cmd_report(argc, argv, &mem, report_options);
|
||||
else
|
||||
usage_with_options(mem_usage, mem_options);
|
||||
|
||||
/* free usage string allocated by parse_options_subcommand */
|
||||
free((void *)mem_usage[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+49
-30
@@ -171,6 +171,7 @@ struct record {
|
||||
bool timestamp_filename;
|
||||
bool timestamp_boundary;
|
||||
bool off_cpu;
|
||||
const char *filter_action;
|
||||
struct switch_output switch_output;
|
||||
unsigned long long samples;
|
||||
unsigned long output_max_size; /* = 0: unlimited */
|
||||
@@ -193,6 +194,15 @@ static const char *affinity_tags[PERF_AFFINITY_MAX] = {
|
||||
"SYS", "NODE", "CPU"
|
||||
};
|
||||
|
||||
static int build_id__process_mmap(const struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine);
|
||||
static int build_id__process_mmap2(const struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine);
|
||||
static int process_timestamp_boundary(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
|
||||
#ifndef HAVE_GETTID
|
||||
static inline pid_t gettid(void)
|
||||
{
|
||||
@@ -608,7 +618,7 @@ static int record__comp_enabled(struct record *rec)
|
||||
return rec->opts.comp_level > 0;
|
||||
}
|
||||
|
||||
static int process_synthesized_event(struct perf_tool *tool,
|
||||
static int process_synthesized_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine __maybe_unused)
|
||||
@@ -619,7 +629,7 @@ static int process_synthesized_event(struct perf_tool *tool,
|
||||
|
||||
static struct mutex synth_lock;
|
||||
|
||||
static int process_locked_synthesized_event(struct perf_tool *tool,
|
||||
static int process_locked_synthesized_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine __maybe_unused)
|
||||
@@ -704,7 +714,7 @@ static void record__sig_exit(void)
|
||||
|
||||
#ifdef HAVE_AUXTRACE_SUPPORT
|
||||
|
||||
static int record__process_auxtrace(struct perf_tool *tool,
|
||||
static int record__process_auxtrace(const struct perf_tool *tool,
|
||||
struct mmap *map,
|
||||
union perf_event *event, void *data1,
|
||||
size_t len1, void *data2, size_t len2)
|
||||
@@ -1389,7 +1399,7 @@ try_again:
|
||||
"even with a suitable vmlinux or kallsyms file.\n\n");
|
||||
}
|
||||
|
||||
if (evlist__apply_filters(evlist, &pos)) {
|
||||
if (evlist__apply_filters(evlist, &pos, &opts->target)) {
|
||||
pr_err("failed to set filter \"%s\" on event %s with %d (%s)\n",
|
||||
pos->filter ?: "BPF", evsel__name(pos), errno,
|
||||
str_error_r(errno, msg, sizeof(msg)));
|
||||
@@ -1416,7 +1426,7 @@ static void set_timestamp_boundary(struct record *rec, u64 sample_time)
|
||||
rec->evlist->last_sample_time = sample_time;
|
||||
}
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool,
|
||||
static int process_sample_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
@@ -1458,7 +1468,7 @@ static int process_buildids(struct record *rec)
|
||||
* first/last samples.
|
||||
*/
|
||||
if (rec->buildid_all && !rec->timestamp_boundary)
|
||||
rec->tool.sample = NULL;
|
||||
rec->tool.sample = process_event_sample_stub;
|
||||
|
||||
return perf_session__process_events(session);
|
||||
}
|
||||
@@ -2364,13 +2374,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
||||
signal(SIGTERM, sig_handler);
|
||||
signal(SIGSEGV, sigsegv_handler);
|
||||
|
||||
if (rec->opts.record_namespaces)
|
||||
tool->namespace_events = true;
|
||||
|
||||
if (rec->opts.record_cgroup) {
|
||||
#ifdef HAVE_FILE_HANDLE
|
||||
tool->cgroup_events = true;
|
||||
#else
|
||||
#ifndef HAVE_FILE_HANDLE
|
||||
pr_err("cgroup tracking is not supported\n");
|
||||
return -1;
|
||||
#endif
|
||||
@@ -2386,6 +2391,18 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)
|
||||
signal(SIGUSR2, SIG_IGN);
|
||||
}
|
||||
|
||||
perf_tool__init(tool, /*ordered_events=*/true);
|
||||
tool->sample = process_sample_event;
|
||||
tool->fork = perf_event__process_fork;
|
||||
tool->exit = perf_event__process_exit;
|
||||
tool->comm = perf_event__process_comm;
|
||||
tool->namespaces = perf_event__process_namespaces;
|
||||
tool->mmap = build_id__process_mmap;
|
||||
tool->mmap2 = build_id__process_mmap2;
|
||||
tool->itrace_start = process_timestamp_boundary;
|
||||
tool->aux = process_timestamp_boundary;
|
||||
tool->namespace_events = rec->opts.record_namespaces;
|
||||
tool->cgroup_events = rec->opts.record_cgroup;
|
||||
session = perf_session__new(data, tool);
|
||||
if (IS_ERR(session)) {
|
||||
pr_err("Perf session creation failed.\n");
|
||||
@@ -3243,7 +3260,7 @@ static const char * const __record_usage[] = {
|
||||
};
|
||||
const char * const *record_usage = __record_usage;
|
||||
|
||||
static int build_id__process_mmap(struct perf_tool *tool, union perf_event *event,
|
||||
static int build_id__process_mmap(const struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine)
|
||||
{
|
||||
/*
|
||||
@@ -3255,7 +3272,7 @@ static int build_id__process_mmap(struct perf_tool *tool, union perf_event *even
|
||||
return perf_event__process_mmap(tool, event, sample, machine);
|
||||
}
|
||||
|
||||
static int build_id__process_mmap2(struct perf_tool *tool, union perf_event *event,
|
||||
static int build_id__process_mmap2(const struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine)
|
||||
{
|
||||
/*
|
||||
@@ -3268,7 +3285,7 @@ static int build_id__process_mmap2(struct perf_tool *tool, union perf_event *eve
|
||||
return perf_event__process_mmap2(tool, event, sample, machine);
|
||||
}
|
||||
|
||||
static int process_timestamp_boundary(struct perf_tool *tool,
|
||||
static int process_timestamp_boundary(const struct perf_tool *tool,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine __maybe_unused)
|
||||
@@ -3326,18 +3343,6 @@ static struct record record = {
|
||||
.ctl_fd_ack = -1,
|
||||
.synth = PERF_SYNTH_ALL,
|
||||
},
|
||||
.tool = {
|
||||
.sample = process_sample_event,
|
||||
.fork = perf_event__process_fork,
|
||||
.exit = perf_event__process_exit,
|
||||
.comm = perf_event__process_comm,
|
||||
.namespaces = perf_event__process_namespaces,
|
||||
.mmap = build_id__process_mmap,
|
||||
.mmap2 = build_id__process_mmap2,
|
||||
.itrace_start = process_timestamp_boundary,
|
||||
.aux = process_timestamp_boundary,
|
||||
.ordered_events = true,
|
||||
},
|
||||
};
|
||||
|
||||
const char record_callchain_help[] = CALLCHAIN_RECORD_HELP
|
||||
@@ -3557,6 +3562,8 @@ static struct option __record_options[] = {
|
||||
"write collected trace data into several data files using parallel threads",
|
||||
record__parse_threads),
|
||||
OPT_BOOLEAN(0, "off-cpu", &record.off_cpu, "Enable off-cpu analysis"),
|
||||
OPT_STRING(0, "setup-filter", &record.filter_action, "pin|unpin",
|
||||
"BPF filter action"),
|
||||
OPT_END()
|
||||
};
|
||||
|
||||
@@ -4086,6 +4093,18 @@ int cmd_record(int argc, const char **argv)
|
||||
pr_warning("WARNING: --timestamp-filename option is not available in parallel streaming mode.\n");
|
||||
}
|
||||
|
||||
if (rec->filter_action) {
|
||||
if (!strcmp(rec->filter_action, "pin"))
|
||||
err = perf_bpf_filter__pin();
|
||||
else if (!strcmp(rec->filter_action, "unpin"))
|
||||
err = perf_bpf_filter__unpin();
|
||||
else {
|
||||
pr_warning("Unknown BPF filter action: %s\n", rec->filter_action);
|
||||
err = -EINVAL;
|
||||
}
|
||||
goto out_opts;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow aliases to facilitate the lookup of symbols for address
|
||||
* filters. Refer to auxtrace_parse_filters().
|
||||
@@ -4242,13 +4261,13 @@ int cmd_record(int argc, const char **argv)
|
||||
|
||||
err = __cmd_record(&record, argc, argv);
|
||||
out:
|
||||
evlist__delete(rec->evlist);
|
||||
record__free_thread_masks(rec, rec->nr_threads);
|
||||
rec->nr_threads = 0;
|
||||
symbol__exit();
|
||||
auxtrace_record__free(rec->itr);
|
||||
out_opts:
|
||||
record__free_thread_masks(rec, rec->nr_threads);
|
||||
rec->nr_threads = 0;
|
||||
evlist__close_control(rec->opts.ctl_fd, rec->opts.ctl_fd_ack, &rec->opts.ctl_fd_close);
|
||||
evlist__delete(rec->evlist);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
+62
-44
@@ -263,7 +263,7 @@ static int process_feature_event(struct perf_session *session,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool,
|
||||
static int process_sample_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
@@ -328,7 +328,7 @@ static int process_sample_event(struct perf_tool *tool,
|
||||
if (ui__has_annotation() || rep->symbol_ipc || rep->total_cycles_mode) {
|
||||
hist__account_cycles(sample->branch_stack, &al, sample,
|
||||
rep->nonany_branch_mode,
|
||||
&rep->total_cycles);
|
||||
&rep->total_cycles, evsel);
|
||||
}
|
||||
|
||||
ret = hist_entry_iter__add(&iter, &al, rep->max_stack, rep);
|
||||
@@ -339,7 +339,7 @@ out_put:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int process_read_event(struct perf_tool *tool,
|
||||
static int process_read_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct evsel *evsel,
|
||||
@@ -565,6 +565,7 @@ static int evlist__tty_browse_hists(struct evlist *evlist, struct report *rep, c
|
||||
struct hists *hists = evsel__hists(pos);
|
||||
const char *evname = evsel__name(pos);
|
||||
|
||||
i++;
|
||||
if (symbol_conf.event_group && !evsel__is_group_leader(pos))
|
||||
continue;
|
||||
|
||||
@@ -574,7 +575,14 @@ static int evlist__tty_browse_hists(struct evlist *evlist, struct report *rep, c
|
||||
hists__fprintf_nr_sample_events(hists, rep, evname, stdout);
|
||||
|
||||
if (rep->total_cycles_mode) {
|
||||
report__browse_block_hists(&rep->block_reports[i++].hist,
|
||||
char *buf;
|
||||
|
||||
if (!annotation_br_cntr_abbr_list(&buf, pos, true)) {
|
||||
fprintf(stdout, "%s", buf);
|
||||
fprintf(stdout, "#\n");
|
||||
free(buf);
|
||||
}
|
||||
report__browse_block_hists(&rep->block_reports[i - 1].hist,
|
||||
rep->min_percent, pos, NULL);
|
||||
continue;
|
||||
}
|
||||
@@ -765,7 +773,7 @@ static void report__output_resort(struct report *rep)
|
||||
ui_progress__finish();
|
||||
}
|
||||
|
||||
static int count_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
static int count_sample_event(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct evsel *evsel,
|
||||
@@ -777,7 +785,7 @@ static int count_sample_event(struct perf_tool *tool __maybe_unused,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int count_lost_samples_event(struct perf_tool *tool,
|
||||
static int count_lost_samples_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine __maybe_unused)
|
||||
@@ -787,22 +795,28 @@ static int count_lost_samples_event(struct perf_tool *tool,
|
||||
|
||||
evsel = evlist__id2evsel(rep->session->evlist, sample->id);
|
||||
if (evsel) {
|
||||
hists__inc_nr_lost_samples(evsel__hists(evsel),
|
||||
event->lost_samples.lost);
|
||||
struct hists *hists = evsel__hists(evsel);
|
||||
u32 count = event->lost_samples.lost;
|
||||
|
||||
if (event->header.misc & PERF_RECORD_MISC_LOST_SAMPLES_BPF)
|
||||
hists__inc_nr_dropped_samples(hists, count);
|
||||
else
|
||||
hists__inc_nr_lost_samples(hists, count);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_attr(struct perf_tool *tool __maybe_unused,
|
||||
static int process_attr(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct evlist **pevlist);
|
||||
|
||||
static void stats_setup(struct report *rep)
|
||||
{
|
||||
memset(&rep->tool, 0, sizeof(rep->tool));
|
||||
perf_tool__init(&rep->tool, /*ordered_events=*/false);
|
||||
rep->tool.attr = process_attr;
|
||||
rep->tool.sample = count_sample_event;
|
||||
rep->tool.lost_samples = count_lost_samples_event;
|
||||
rep->tool.event_update = perf_event__process_event_update;
|
||||
rep->tool.no_warn = true;
|
||||
}
|
||||
|
||||
@@ -817,8 +831,7 @@ static int stats_print(struct report *rep)
|
||||
|
||||
static void tasks_setup(struct report *rep)
|
||||
{
|
||||
memset(&rep->tool, 0, sizeof(rep->tool));
|
||||
rep->tool.ordered_events = true;
|
||||
perf_tool__init(&rep->tool, /*ordered_events=*/true);
|
||||
if (rep->mmaps_mode) {
|
||||
rep->tool.mmap = perf_event__process_mmap;
|
||||
rep->tool.mmap2 = perf_event__process_mmap2;
|
||||
@@ -1119,18 +1132,23 @@ static int __cmd_report(struct report *rep)
|
||||
report__output_resort(rep);
|
||||
|
||||
if (rep->total_cycles_mode) {
|
||||
int block_hpps[6] = {
|
||||
int nr_hpps = 4;
|
||||
int block_hpps[PERF_HPP_REPORT__BLOCK_MAX_INDEX] = {
|
||||
PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT,
|
||||
PERF_HPP_REPORT__BLOCK_LBR_CYCLES,
|
||||
PERF_HPP_REPORT__BLOCK_CYCLES_PCT,
|
||||
PERF_HPP_REPORT__BLOCK_AVG_CYCLES,
|
||||
PERF_HPP_REPORT__BLOCK_RANGE,
|
||||
PERF_HPP_REPORT__BLOCK_DSO,
|
||||
};
|
||||
|
||||
if (session->evlist->nr_br_cntr > 0)
|
||||
block_hpps[nr_hpps++] = PERF_HPP_REPORT__BLOCK_BRANCH_COUNTER;
|
||||
|
||||
block_hpps[nr_hpps++] = PERF_HPP_REPORT__BLOCK_RANGE;
|
||||
block_hpps[nr_hpps++] = PERF_HPP_REPORT__BLOCK_DSO;
|
||||
|
||||
rep->block_reports = block_info__create_report(session->evlist,
|
||||
rep->total_cycles,
|
||||
block_hpps, 6,
|
||||
block_hpps, nr_hpps,
|
||||
&rep->nr_block_reports);
|
||||
if (!rep->block_reports)
|
||||
return -1;
|
||||
@@ -1233,7 +1251,7 @@ parse_percent_limit(const struct option *opt, const char *str,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_attr(struct perf_tool *tool __maybe_unused,
|
||||
static int process_attr(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct evlist **pevlist)
|
||||
{
|
||||
@@ -1272,37 +1290,13 @@ int cmd_report(int argc, const char **argv)
|
||||
NULL
|
||||
};
|
||||
struct report report = {
|
||||
.tool = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.mmap2 = perf_event__process_mmap2,
|
||||
.comm = perf_event__process_comm,
|
||||
.namespaces = perf_event__process_namespaces,
|
||||
.cgroup = perf_event__process_cgroup,
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.lost = perf_event__process_lost,
|
||||
.read = process_read_event,
|
||||
.attr = process_attr,
|
||||
#ifdef HAVE_LIBTRACEEVENT
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
#endif
|
||||
.build_id = perf_event__process_build_id,
|
||||
.id_index = perf_event__process_id_index,
|
||||
.auxtrace_info = perf_event__process_auxtrace_info,
|
||||
.auxtrace = perf_event__process_auxtrace,
|
||||
.event_update = perf_event__process_event_update,
|
||||
.feature = process_feature_event,
|
||||
.ordered_events = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
.max_stack = PERF_MAX_STACK_DEPTH,
|
||||
.pretty_printing_style = "normal",
|
||||
.socket_filter = -1,
|
||||
.skip_empty = true,
|
||||
};
|
||||
char *sort_order_help = sort_help("sort by key(s):");
|
||||
char *field_order_help = sort_help("output field(s): overhead period sample ");
|
||||
char *sort_order_help = sort_help("sort by key(s):", SORT_MODE__NORMAL);
|
||||
char *field_order_help = sort_help("output field(s):", SORT_MODE__NORMAL);
|
||||
const char *disassembler_style = NULL, *objdump_path = NULL, *addr2line_path = NULL;
|
||||
const struct option options[] = {
|
||||
OPT_STRING('i', "input", &input_name, "file",
|
||||
@@ -1477,6 +1471,7 @@ int cmd_report(int argc, const char **argv)
|
||||
};
|
||||
int ret = hists__init();
|
||||
char sort_tmp[128];
|
||||
bool ordered_events = true;
|
||||
|
||||
if (ret < 0)
|
||||
goto exit;
|
||||
@@ -1531,7 +1526,7 @@ int cmd_report(int argc, const char **argv)
|
||||
report.tasks_mode = true;
|
||||
|
||||
if (dump_trace && report.disable_order)
|
||||
report.tool.ordered_events = false;
|
||||
ordered_events = false;
|
||||
|
||||
if (quiet)
|
||||
perf_quiet_option();
|
||||
@@ -1562,6 +1557,29 @@ int cmd_report(int argc, const char **argv)
|
||||
symbol_conf.skip_empty = report.skip_empty;
|
||||
|
||||
repeat:
|
||||
perf_tool__init(&report.tool, ordered_events);
|
||||
report.tool.sample = process_sample_event;
|
||||
report.tool.mmap = perf_event__process_mmap;
|
||||
report.tool.mmap2 = perf_event__process_mmap2;
|
||||
report.tool.comm = perf_event__process_comm;
|
||||
report.tool.namespaces = perf_event__process_namespaces;
|
||||
report.tool.cgroup = perf_event__process_cgroup;
|
||||
report.tool.exit = perf_event__process_exit;
|
||||
report.tool.fork = perf_event__process_fork;
|
||||
report.tool.lost = perf_event__process_lost;
|
||||
report.tool.read = process_read_event;
|
||||
report.tool.attr = process_attr;
|
||||
#ifdef HAVE_LIBTRACEEVENT
|
||||
report.tool.tracing_data = perf_event__process_tracing_data;
|
||||
#endif
|
||||
report.tool.build_id = perf_event__process_build_id;
|
||||
report.tool.id_index = perf_event__process_id_index;
|
||||
report.tool.auxtrace_info = perf_event__process_auxtrace_info;
|
||||
report.tool.auxtrace = perf_event__process_auxtrace;
|
||||
report.tool.event_update = perf_event__process_event_update;
|
||||
report.tool.feature = process_feature_event;
|
||||
report.tool.ordering_requires_timestamps = true;
|
||||
|
||||
session = perf_session__new(&data, &report.tool);
|
||||
if (IS_ERR(session)) {
|
||||
ret = PTR_ERR(session);
|
||||
|
||||
+192
-40
@@ -51,6 +51,7 @@
|
||||
#define COMM_LEN 20
|
||||
#define SYM_LEN 129
|
||||
#define MAX_PID 1024000
|
||||
#define MAX_PRIO 140
|
||||
|
||||
static const char *cpu_list;
|
||||
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
|
||||
@@ -228,11 +229,14 @@ struct perf_sched {
|
||||
bool show_next;
|
||||
bool show_migrations;
|
||||
bool show_state;
|
||||
bool show_prio;
|
||||
u64 skipped_samples;
|
||||
const char *time_str;
|
||||
struct perf_time_interval ptime;
|
||||
struct perf_time_interval hist_time;
|
||||
volatile bool thread_funcs_exit;
|
||||
const char *prio_str;
|
||||
DECLARE_BITMAP(prio_bitmap, MAX_PRIO);
|
||||
};
|
||||
|
||||
/* per thread run time data */
|
||||
@@ -258,6 +262,8 @@ struct thread_runtime {
|
||||
bool comm_changed;
|
||||
|
||||
u64 migrations;
|
||||
|
||||
int prio;
|
||||
};
|
||||
|
||||
/* per event run time data */
|
||||
@@ -920,6 +926,11 @@ struct sort_dimension {
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static inline void init_prio(struct thread_runtime *r)
|
||||
{
|
||||
r->prio = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* handle runtime stats saved per thread
|
||||
*/
|
||||
@@ -932,6 +943,7 @@ static struct thread_runtime *thread__init_runtime(struct thread *thread)
|
||||
return NULL;
|
||||
|
||||
init_stats(&r->run_stats);
|
||||
init_prio(r);
|
||||
thread__set_priv(thread, r);
|
||||
|
||||
return r;
|
||||
@@ -1489,7 +1501,7 @@ again:
|
||||
}
|
||||
}
|
||||
|
||||
static int process_sched_wakeup_event(struct perf_tool *tool,
|
||||
static int process_sched_wakeup_event(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -1502,7 +1514,7 @@ static int process_sched_wakeup_event(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_sched_wakeup_ignore(struct perf_tool *tool __maybe_unused,
|
||||
static int process_sched_wakeup_ignore(const struct perf_tool *tool __maybe_unused,
|
||||
struct evsel *evsel __maybe_unused,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine __maybe_unused)
|
||||
@@ -1770,7 +1782,7 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_sched_switch_event(struct perf_tool *tool,
|
||||
static int process_sched_switch_event(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -1796,7 +1808,7 @@ static int process_sched_switch_event(struct perf_tool *tool,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int process_sched_runtime_event(struct perf_tool *tool,
|
||||
static int process_sched_runtime_event(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -1809,7 +1821,7 @@ static int process_sched_runtime_event(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_sched__process_fork_event(struct perf_tool *tool,
|
||||
static int perf_sched__process_fork_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -1826,7 +1838,7 @@ static int perf_sched__process_fork_event(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_sched_migrate_task_event(struct perf_tool *tool,
|
||||
static int process_sched_migrate_task_event(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -1839,12 +1851,12 @@ static int process_sched_migrate_task_event(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*tracepoint_handler)(struct perf_tool *tool,
|
||||
typedef int (*tracepoint_handler)(const struct perf_tool *tool,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
|
||||
static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_unused,
|
||||
static int perf_sched__process_tracepoint_sample(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
@@ -1860,7 +1872,7 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_
|
||||
return err;
|
||||
}
|
||||
|
||||
static int perf_sched__process_comm(struct perf_tool *tool __maybe_unused,
|
||||
static int perf_sched__process_comm(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -2036,6 +2048,24 @@ static char *timehist_get_commstr(struct thread *thread)
|
||||
return str;
|
||||
}
|
||||
|
||||
/* prio field format: xxx or xxx->yyy */
|
||||
#define MAX_PRIO_STR_LEN 8
|
||||
static char *timehist_get_priostr(struct evsel *evsel,
|
||||
struct thread *thread,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
static char prio_str[16];
|
||||
int prev_prio = (int)evsel__intval(evsel, sample, "prev_prio");
|
||||
struct thread_runtime *tr = thread__priv(thread);
|
||||
|
||||
if (tr->prio != prev_prio && tr->prio != -1)
|
||||
scnprintf(prio_str, sizeof(prio_str), "%d->%d", tr->prio, prev_prio);
|
||||
else
|
||||
scnprintf(prio_str, sizeof(prio_str), "%d", prev_prio);
|
||||
|
||||
return prio_str;
|
||||
}
|
||||
|
||||
static void timehist_header(struct perf_sched *sched)
|
||||
{
|
||||
u32 ncpus = sched->max_cpu.cpu + 1;
|
||||
@@ -2053,8 +2083,14 @@ static void timehist_header(struct perf_sched *sched)
|
||||
printf(" ");
|
||||
}
|
||||
|
||||
printf(" %-*s %9s %9s %9s", comm_width,
|
||||
"task name", "wait time", "sch delay", "run time");
|
||||
if (sched->show_prio) {
|
||||
printf(" %-*s %-*s %9s %9s %9s",
|
||||
comm_width, "task name", MAX_PRIO_STR_LEN, "prio",
|
||||
"wait time", "sch delay", "run time");
|
||||
} else {
|
||||
printf(" %-*s %9s %9s %9s", comm_width,
|
||||
"task name", "wait time", "sch delay", "run time");
|
||||
}
|
||||
|
||||
if (sched->show_state)
|
||||
printf(" %s", "state");
|
||||
@@ -2069,8 +2105,14 @@ static void timehist_header(struct perf_sched *sched)
|
||||
if (sched->show_cpu_visual)
|
||||
printf(" %*s ", ncpus, "");
|
||||
|
||||
printf(" %-*s %9s %9s %9s", comm_width,
|
||||
"[tid/pid]", "(msec)", "(msec)", "(msec)");
|
||||
if (sched->show_prio) {
|
||||
printf(" %-*s %-*s %9s %9s %9s",
|
||||
comm_width, "[tid/pid]", MAX_PRIO_STR_LEN, "",
|
||||
"(msec)", "(msec)", "(msec)");
|
||||
} else {
|
||||
printf(" %-*s %9s %9s %9s", comm_width,
|
||||
"[tid/pid]", "(msec)", "(msec)", "(msec)");
|
||||
}
|
||||
|
||||
if (sched->show_state)
|
||||
printf(" %5s", "");
|
||||
@@ -2085,9 +2127,15 @@ static void timehist_header(struct perf_sched *sched)
|
||||
if (sched->show_cpu_visual)
|
||||
printf(" %.*s ", ncpus, graph_dotted_line);
|
||||
|
||||
printf(" %.*s %.9s %.9s %.9s", comm_width,
|
||||
graph_dotted_line, graph_dotted_line, graph_dotted_line,
|
||||
graph_dotted_line);
|
||||
if (sched->show_prio) {
|
||||
printf(" %.*s %.*s %.9s %.9s %.9s",
|
||||
comm_width, graph_dotted_line, MAX_PRIO_STR_LEN, graph_dotted_line,
|
||||
graph_dotted_line, graph_dotted_line, graph_dotted_line);
|
||||
} else {
|
||||
printf(" %.*s %.9s %.9s %.9s", comm_width,
|
||||
graph_dotted_line, graph_dotted_line, graph_dotted_line,
|
||||
graph_dotted_line);
|
||||
}
|
||||
|
||||
if (sched->show_state)
|
||||
printf(" %.5s", graph_dotted_line);
|
||||
@@ -2134,6 +2182,9 @@ static void timehist_print_sample(struct perf_sched *sched,
|
||||
|
||||
printf(" %-*s ", comm_width, timehist_get_commstr(thread));
|
||||
|
||||
if (sched->show_prio)
|
||||
printf(" %-*s ", MAX_PRIO_STR_LEN, timehist_get_priostr(evsel, thread, sample));
|
||||
|
||||
wait_time = tr->dt_sleep + tr->dt_iowait + tr->dt_preempt;
|
||||
print_sched_time(wait_time, 6);
|
||||
|
||||
@@ -2301,6 +2352,7 @@ static int init_idle_thread(struct thread *thread)
|
||||
if (itr == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
init_prio(&itr->tr);
|
||||
init_stats(&itr->tr.run_stats);
|
||||
callchain_init(&itr->callchain);
|
||||
callchain_cursor_reset(&itr->cursor);
|
||||
@@ -2455,12 +2507,33 @@ static bool timehist_skip_sample(struct perf_sched *sched,
|
||||
struct perf_sample *sample)
|
||||
{
|
||||
bool rc = false;
|
||||
int prio = -1;
|
||||
struct thread_runtime *tr = NULL;
|
||||
|
||||
if (thread__is_filtered(thread)) {
|
||||
rc = true;
|
||||
sched->skipped_samples++;
|
||||
}
|
||||
|
||||
if (sched->prio_str) {
|
||||
/*
|
||||
* Because priority may be changed during task execution,
|
||||
* first read priority from prev sched_in event for current task.
|
||||
* If prev sched_in event is not saved, then read priority from
|
||||
* current task sched_out event.
|
||||
*/
|
||||
tr = thread__get_runtime(thread);
|
||||
if (tr && tr->prio != -1)
|
||||
prio = tr->prio;
|
||||
else if (evsel__name_is(evsel, "sched:sched_switch"))
|
||||
prio = evsel__intval(evsel, sample, "prev_prio");
|
||||
|
||||
if (prio != -1 && !test_bit(prio, sched->prio_bitmap)) {
|
||||
rc = true;
|
||||
sched->skipped_samples++;
|
||||
}
|
||||
}
|
||||
|
||||
if (sched->idle_hist) {
|
||||
if (!evsel__name_is(evsel, "sched:sched_switch"))
|
||||
rc = true;
|
||||
@@ -2506,7 +2579,7 @@ static void timehist_print_wakeup_event(struct perf_sched *sched,
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int timehist_sched_wakeup_ignore(struct perf_tool *tool __maybe_unused,
|
||||
static int timehist_sched_wakeup_ignore(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct evsel *evsel __maybe_unused,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
@@ -2515,7 +2588,7 @@ static int timehist_sched_wakeup_ignore(struct perf_tool *tool __maybe_unused,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int timehist_sched_wakeup_event(struct perf_tool *tool,
|
||||
static int timehist_sched_wakeup_event(const struct perf_tool *tool,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
@@ -2599,7 +2672,7 @@ static void timehist_print_migration_event(struct perf_sched *sched,
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int timehist_migrate_task_event(struct perf_tool *tool,
|
||||
static int timehist_migrate_task_event(const struct perf_tool *tool,
|
||||
union perf_event *event __maybe_unused,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
@@ -2627,7 +2700,31 @@ static int timehist_migrate_task_event(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int timehist_sched_change_event(struct perf_tool *tool,
|
||||
static void timehist_update_task_prio(struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
{
|
||||
struct thread *thread;
|
||||
struct thread_runtime *tr = NULL;
|
||||
const u32 next_pid = evsel__intval(evsel, sample, "next_pid");
|
||||
const u32 next_prio = evsel__intval(evsel, sample, "next_prio");
|
||||
|
||||
if (next_pid == 0)
|
||||
thread = get_idle_thread(sample->cpu);
|
||||
else
|
||||
thread = machine__findnew_thread(machine, -1, next_pid);
|
||||
|
||||
if (thread == NULL)
|
||||
return;
|
||||
|
||||
tr = thread__get_runtime(thread);
|
||||
if (tr == NULL)
|
||||
return;
|
||||
|
||||
tr->prio = next_prio;
|
||||
}
|
||||
|
||||
static int timehist_sched_change_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
@@ -2650,6 +2747,9 @@ static int timehist_sched_change_event(struct perf_tool *tool,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (sched->show_prio || sched->prio_str)
|
||||
timehist_update_task_prio(evsel, sample, machine);
|
||||
|
||||
thread = timehist_get_thread(sched, sample, machine, evsel);
|
||||
if (thread == NULL) {
|
||||
rc = -1;
|
||||
@@ -2683,9 +2783,12 @@ static int timehist_sched_change_event(struct perf_tool *tool,
|
||||
* - previous sched event is out of window - we are done
|
||||
* - sample time is beyond window user cares about - reset it
|
||||
* to close out stats for time window interest
|
||||
* - If tprev is 0, that is, sched_in event for current task is
|
||||
* not recorded, cannot determine whether sched_in event is
|
||||
* within time window interest - ignore it
|
||||
*/
|
||||
if (ptime->end) {
|
||||
if (tprev > ptime->end)
|
||||
if (!tprev || tprev > ptime->end)
|
||||
goto out;
|
||||
|
||||
if (t > ptime->end)
|
||||
@@ -2700,8 +2803,6 @@ static int timehist_sched_change_event(struct perf_tool *tool,
|
||||
struct idle_thread_runtime *itr = (void *)tr;
|
||||
struct thread_runtime *last_tr;
|
||||
|
||||
BUG_ON(thread__tid(thread) != 0);
|
||||
|
||||
if (itr->last_thread == NULL)
|
||||
goto out;
|
||||
|
||||
@@ -2727,10 +2828,10 @@ static int timehist_sched_change_event(struct perf_tool *tool,
|
||||
|
||||
itr->last_thread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sched->summary_only)
|
||||
timehist_print_sample(sched, evsel, sample, &al, thread, t, state);
|
||||
if (!sched->summary_only)
|
||||
timehist_print_sample(sched, evsel, sample, &al, thread, t, state);
|
||||
}
|
||||
|
||||
out:
|
||||
if (sched->hist_time.start == 0 && t >= ptime->start)
|
||||
@@ -2758,7 +2859,7 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int timehist_sched_switch_event(struct perf_tool *tool,
|
||||
static int timehist_sched_switch_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
@@ -2767,7 +2868,7 @@ static int timehist_sched_switch_event(struct perf_tool *tool,
|
||||
return timehist_sched_change_event(tool, event, evsel, sample, machine);
|
||||
}
|
||||
|
||||
static int process_lost(struct perf_tool *tool __maybe_unused,
|
||||
static int process_lost(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine __maybe_unused)
|
||||
@@ -3010,13 +3111,13 @@ static void timehist_print_summary(struct perf_sched *sched,
|
||||
printf(" (x %d)\n", sched->max_cpu.cpu);
|
||||
}
|
||||
|
||||
typedef int (*sched_handler)(struct perf_tool *tool,
|
||||
typedef int (*sched_handler)(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine);
|
||||
|
||||
static int perf_timehist__process_sample(struct perf_tool *tool,
|
||||
static int perf_timehist__process_sample(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
@@ -3066,6 +3167,47 @@ static int timehist_check_attr(struct perf_sched *sched,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int timehist_parse_prio_str(struct perf_sched *sched)
|
||||
{
|
||||
char *p;
|
||||
unsigned long start_prio, end_prio;
|
||||
const char *str = sched->prio_str;
|
||||
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
while (isdigit(*str)) {
|
||||
p = NULL;
|
||||
start_prio = strtoul(str, &p, 0);
|
||||
if (start_prio >= MAX_PRIO || (*p != '\0' && *p != ',' && *p != '-'))
|
||||
return -1;
|
||||
|
||||
if (*p == '-') {
|
||||
str = ++p;
|
||||
p = NULL;
|
||||
end_prio = strtoul(str, &p, 0);
|
||||
|
||||
if (end_prio >= MAX_PRIO || (*p != '\0' && *p != ','))
|
||||
return -1;
|
||||
|
||||
if (end_prio < start_prio)
|
||||
return -1;
|
||||
} else {
|
||||
end_prio = start_prio;
|
||||
}
|
||||
|
||||
for (; start_prio <= end_prio; start_prio++)
|
||||
__set_bit(start_prio, sched->prio_bitmap);
|
||||
|
||||
if (*p)
|
||||
++p;
|
||||
|
||||
str = p;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int perf_sched__timehist(struct perf_sched *sched)
|
||||
{
|
||||
struct evsel_str_handler handlers[] = {
|
||||
@@ -3100,7 +3242,6 @@ static int perf_sched__timehist(struct perf_sched *sched)
|
||||
sched->tool.tracing_data = perf_event__process_tracing_data;
|
||||
sched->tool.build_id = perf_event__process_build_id;
|
||||
|
||||
sched->tool.ordered_events = true;
|
||||
sched->tool.ordering_requires_timestamps = true;
|
||||
|
||||
symbol_conf.use_callchain = sched->show_callchain;
|
||||
@@ -3121,12 +3262,18 @@ static int perf_sched__timehist(struct perf_sched *sched)
|
||||
|
||||
if (perf_time__parse_str(&sched->ptime, sched->time_str) != 0) {
|
||||
pr_err("Invalid time string\n");
|
||||
return -EINVAL;
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (timehist_check_attr(sched, evlist) != 0)
|
||||
goto out;
|
||||
|
||||
if (timehist_parse_prio_str(sched) != 0) {
|
||||
pr_err("Invalid prio string\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
setup_pager();
|
||||
|
||||
/* prefer sched_waking if it is captured */
|
||||
@@ -3605,14 +3752,6 @@ int cmd_sched(int argc, const char **argv)
|
||||
{
|
||||
static const char default_sort_order[] = "avg, max, switch, runtime";
|
||||
struct perf_sched sched = {
|
||||
.tool = {
|
||||
.sample = perf_sched__process_tracepoint_sample,
|
||||
.comm = perf_sched__process_comm,
|
||||
.namespaces = perf_event__process_namespaces,
|
||||
.lost = perf_event__process_lost,
|
||||
.fork = perf_sched__process_fork_event,
|
||||
.ordered_events = true,
|
||||
},
|
||||
.cmp_pid = LIST_HEAD_INIT(sched.cmp_pid),
|
||||
.sort_list = LIST_HEAD_INIT(sched.sort_list),
|
||||
.sort_order = default_sort_order,
|
||||
@@ -3691,6 +3830,9 @@ int cmd_sched(int argc, const char **argv)
|
||||
OPT_STRING('t', "tid", &symbol_conf.tid_list_str, "tid[,tid...]",
|
||||
"analyze events only for given thread id(s)"),
|
||||
OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
|
||||
OPT_BOOLEAN(0, "show-prio", &sched.show_prio, "Show task priority"),
|
||||
OPT_STRING(0, "prio", &sched.prio_str, "prio",
|
||||
"analyze events only for given task priority(ies)"),
|
||||
OPT_PARENT(sched_options)
|
||||
};
|
||||
|
||||
@@ -3733,6 +3875,13 @@ int cmd_sched(int argc, const char **argv)
|
||||
};
|
||||
int ret;
|
||||
|
||||
perf_tool__init(&sched.tool, /*ordered_events=*/true);
|
||||
sched.tool.sample = perf_sched__process_tracepoint_sample;
|
||||
sched.tool.comm = perf_sched__process_comm;
|
||||
sched.tool.namespaces = perf_event__process_namespaces;
|
||||
sched.tool.lost = perf_event__process_lost;
|
||||
sched.tool.fork = perf_sched__process_fork_event;
|
||||
|
||||
argc = parse_options_subcommand(argc, argv, sched_options, sched_subcommands,
|
||||
sched_usage, PARSE_OPT_STOP_AT_NON_OPTION);
|
||||
if (!argc)
|
||||
@@ -3805,5 +3954,8 @@ int cmd_sched(int argc, const char **argv)
|
||||
usage_with_options(sched_usage, sched_options);
|
||||
}
|
||||
|
||||
/* free usage string allocated by parse_options_subcommand */
|
||||
free((void *)sched_usage[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+119
-65
@@ -62,6 +62,7 @@
|
||||
#include "util/record.h"
|
||||
#include "util/util.h"
|
||||
#include "util/cgroup.h"
|
||||
#include "util/annotate.h"
|
||||
#include "perf.h"
|
||||
|
||||
#include <linux/ctype.h>
|
||||
@@ -138,6 +139,7 @@ enum perf_output_field {
|
||||
PERF_OUTPUT_DSOFF = 1ULL << 41,
|
||||
PERF_OUTPUT_DISASM = 1ULL << 42,
|
||||
PERF_OUTPUT_BRSTACKDISASM = 1ULL << 43,
|
||||
PERF_OUTPUT_BRCNTR = 1ULL << 44,
|
||||
};
|
||||
|
||||
struct perf_script {
|
||||
@@ -213,6 +215,7 @@ struct output_option {
|
||||
{.str = "cgroup", .field = PERF_OUTPUT_CGROUP},
|
||||
{.str = "retire_lat", .field = PERF_OUTPUT_RETIRE_LAT},
|
||||
{.str = "brstackdisasm", .field = PERF_OUTPUT_BRSTACKDISASM},
|
||||
{.str = "brcntr", .field = PERF_OUTPUT_BRCNTR},
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -520,6 +523,12 @@ static int evsel__check_attr(struct evsel *evsel, struct perf_session *session)
|
||||
"Hint: run 'perf record -b ...'\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (PRINT_FIELD(BRCNTR) &&
|
||||
!(evlist__combined_branch_type(session->evlist) & PERF_SAMPLE_BRANCH_COUNTERS)) {
|
||||
pr_err("Display of branch counter requested but it's not enabled\n"
|
||||
"Hint: run 'perf record -j any,counter ...'\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((PRINT_FIELD(PID) || PRINT_FIELD(TID)) &&
|
||||
evsel__check_stype(evsel, PERF_SAMPLE_TID, "TID", PERF_OUTPUT_TID|PERF_OUTPUT_PID))
|
||||
return -EINVAL;
|
||||
@@ -789,6 +798,19 @@ static int perf_sample__fprintf_start(struct perf_script *script,
|
||||
int printed = 0;
|
||||
char tstr[128];
|
||||
|
||||
/*
|
||||
* Print the branch counter's abbreviation list,
|
||||
* if the branch counter is available.
|
||||
*/
|
||||
if (PRINT_FIELD(BRCNTR) && !verbose) {
|
||||
char *buf;
|
||||
|
||||
if (!annotation_br_cntr_abbr_list(&buf, evsel, true)) {
|
||||
printed += fprintf(stdout, "%s", buf);
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (PRINT_FIELD(MACHINE_PID) && sample->machine_pid)
|
||||
printed += fprintf(fp, "VM:%5d ", sample->machine_pid);
|
||||
|
||||
@@ -1195,7 +1217,9 @@ static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en,
|
||||
struct perf_insn *x, u8 *inbuf, int len,
|
||||
int insn, FILE *fp, int *total_cycles,
|
||||
struct perf_event_attr *attr,
|
||||
struct thread *thread)
|
||||
struct thread *thread,
|
||||
struct evsel *evsel,
|
||||
u64 br_cntr)
|
||||
{
|
||||
int ilen = 0;
|
||||
int printed = fprintf(fp, "\t%016" PRIx64 "\t", ip);
|
||||
@@ -1216,6 +1240,29 @@ static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en,
|
||||
addr_location__exit(&al);
|
||||
}
|
||||
|
||||
if (PRINT_FIELD(BRCNTR)) {
|
||||
struct evsel *pos = evsel__leader(evsel);
|
||||
unsigned int i = 0, j, num, mask, width;
|
||||
|
||||
perf_env__find_br_cntr_info(evsel__env(evsel), NULL, &width);
|
||||
mask = (1L << width) - 1;
|
||||
printed += fprintf(fp, "br_cntr: ");
|
||||
evlist__for_each_entry_from(evsel->evlist, pos) {
|
||||
if (!(pos->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS))
|
||||
continue;
|
||||
if (evsel__leader(pos) != evsel__leader(evsel))
|
||||
break;
|
||||
|
||||
num = (br_cntr >> (i++ * width)) & mask;
|
||||
if (!verbose) {
|
||||
for (j = 0; j < num; j++)
|
||||
printed += fprintf(fp, "%s", pos->abbr_name);
|
||||
} else
|
||||
printed += fprintf(fp, "%s %d ", pos->name, num);
|
||||
}
|
||||
printed += fprintf(fp, "\t");
|
||||
}
|
||||
|
||||
printed += fprintf(fp, "#%s%s%s%s",
|
||||
en->flags.predicted ? " PRED" : "",
|
||||
en->flags.mispred ? " MISPRED" : "",
|
||||
@@ -1272,6 +1319,7 @@ out:
|
||||
}
|
||||
|
||||
static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
struct thread *thread,
|
||||
struct perf_event_attr *attr,
|
||||
struct machine *machine, FILE *fp)
|
||||
@@ -1285,6 +1333,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
|
||||
unsigned off;
|
||||
struct symbol *lastsym = NULL;
|
||||
int total_cycles = 0;
|
||||
u64 br_cntr = 0;
|
||||
|
||||
if (!(br && br->nr))
|
||||
return 0;
|
||||
@@ -1296,6 +1345,9 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
|
||||
x.machine = machine;
|
||||
x.cpu = sample->cpu;
|
||||
|
||||
if (PRINT_FIELD(BRCNTR) && sample->branch_stack_cntr)
|
||||
br_cntr = sample->branch_stack_cntr[nr - 1];
|
||||
|
||||
printed += fprintf(fp, "%c", '\n');
|
||||
|
||||
/* Handle first from jump, of which we don't know the entry. */
|
||||
@@ -1307,7 +1359,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
|
||||
x.cpumode, x.cpu, &lastsym, attr, fp);
|
||||
printed += ip__fprintf_jump(entries[nr - 1].from, &entries[nr - 1],
|
||||
&x, buffer, len, 0, fp, &total_cycles,
|
||||
attr, thread);
|
||||
attr, thread, evsel, br_cntr);
|
||||
if (PRINT_FIELD(SRCCODE))
|
||||
printed += print_srccode(thread, x.cpumode, entries[nr - 1].from);
|
||||
}
|
||||
@@ -1337,8 +1389,10 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
|
||||
|
||||
printed += ip__fprintf_sym(ip, thread, x.cpumode, x.cpu, &lastsym, attr, fp);
|
||||
if (ip == end) {
|
||||
if (PRINT_FIELD(BRCNTR) && sample->branch_stack_cntr)
|
||||
br_cntr = sample->branch_stack_cntr[i];
|
||||
printed += ip__fprintf_jump(ip, &entries[i], &x, buffer + off, len - off, ++insn, fp,
|
||||
&total_cycles, attr, thread);
|
||||
&total_cycles, attr, thread, evsel, br_cntr);
|
||||
if (PRINT_FIELD(SRCCODE))
|
||||
printed += print_srccode(thread, x.cpumode, ip);
|
||||
break;
|
||||
@@ -1375,7 +1429,7 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
|
||||
* Due to pipeline delays the LBRs might be missing a branch
|
||||
* or two, which can result in very large or negative blocks
|
||||
* between final branch and sample. When this happens just
|
||||
* continue walking after the last TO until we hit a branch.
|
||||
* continue walking after the last TO.
|
||||
*/
|
||||
start = entries[0].to;
|
||||
end = sample->ip;
|
||||
@@ -1410,7 +1464,9 @@ static int perf_sample__fprintf_brstackinsn(struct perf_sample *sample,
|
||||
printed += fprintf(fp, "\n");
|
||||
if (ilen == 0)
|
||||
break;
|
||||
if (arch_is_branch(buffer + off, len - off, x.is64bit) && start + off != sample->ip) {
|
||||
if ((attr->branch_sample_type == 0 || attr->branch_sample_type & PERF_SAMPLE_BRANCH_ANY)
|
||||
&& arch_is_uncond_branch(buffer + off, len - off, x.is64bit)
|
||||
&& start + off != sample->ip) {
|
||||
/*
|
||||
* Hit a missing branch. Just stop.
|
||||
*/
|
||||
@@ -1547,6 +1603,7 @@ void script_fetch_insn(struct perf_sample *sample, struct thread *thread,
|
||||
}
|
||||
|
||||
static int perf_sample__fprintf_insn(struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
struct perf_event_attr *attr,
|
||||
struct thread *thread,
|
||||
struct machine *machine, FILE *fp,
|
||||
@@ -1567,7 +1624,7 @@ static int perf_sample__fprintf_insn(struct perf_sample *sample,
|
||||
printed += sample__fprintf_insn_asm(sample, thread, machine, fp, al);
|
||||
}
|
||||
if (PRINT_FIELD(BRSTACKINSN) || PRINT_FIELD(BRSTACKINSNLEN) || PRINT_FIELD(BRSTACKDISASM))
|
||||
printed += perf_sample__fprintf_brstackinsn(sample, thread, attr, machine, fp);
|
||||
printed += perf_sample__fprintf_brstackinsn(sample, evsel, thread, attr, machine, fp);
|
||||
|
||||
return printed;
|
||||
}
|
||||
@@ -1639,7 +1696,7 @@ static int perf_sample__fprintf_bts(struct perf_sample *sample,
|
||||
if (print_srcline_last)
|
||||
printed += map__fprintf_srcline(al->map, al->addr, "\n ", fp);
|
||||
|
||||
printed += perf_sample__fprintf_insn(sample, attr, thread, machine, fp, al);
|
||||
printed += perf_sample__fprintf_insn(sample, evsel, attr, thread, machine, fp, al);
|
||||
printed += fprintf(fp, "\n");
|
||||
if (PRINT_FIELD(SRCCODE)) {
|
||||
int ret = map__fprintf_srccode(al->map, al->addr, stdout,
|
||||
@@ -2297,7 +2354,7 @@ static void process_event(struct perf_script *script,
|
||||
|
||||
if (evsel__is_bpf_output(evsel) && PRINT_FIELD(BPF_OUTPUT))
|
||||
perf_sample__fprintf_bpf_output(sample, fp);
|
||||
perf_sample__fprintf_insn(sample, attr, thread, machine, fp, al);
|
||||
perf_sample__fprintf_insn(sample, evsel, attr, thread, machine, fp, al);
|
||||
|
||||
if (PRINT_FIELD(PHYS_ADDR))
|
||||
fprintf(fp, "%16" PRIx64, sample->phys_addr);
|
||||
@@ -2399,7 +2456,7 @@ static bool filter_cpu(struct perf_sample *sample)
|
||||
return false;
|
||||
}
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool,
|
||||
static int process_sample_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
@@ -2486,7 +2543,7 @@ out_put:
|
||||
// Used when scr->per_event_dump is not set
|
||||
static struct evsel_script es_stdout;
|
||||
|
||||
static int process_attr(struct perf_tool *tool, union perf_event *event,
|
||||
static int process_attr(const struct perf_tool *tool, union perf_event *event,
|
||||
struct evlist **pevlist)
|
||||
{
|
||||
struct perf_script *scr = container_of(tool, struct perf_script, tool);
|
||||
@@ -2552,7 +2609,7 @@ static int process_attr(struct perf_tool *tool, union perf_event *event,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_event_with_time(struct perf_tool *tool,
|
||||
static int print_event_with_time(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine,
|
||||
@@ -2588,14 +2645,14 @@ static int print_event_with_time(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_event(struct perf_tool *tool, union perf_event *event,
|
||||
static int print_event(const struct perf_tool *tool, union perf_event *event,
|
||||
struct perf_sample *sample, struct machine *machine,
|
||||
pid_t pid, pid_t tid)
|
||||
{
|
||||
return print_event_with_time(tool, event, sample, machine, pid, tid, 0);
|
||||
}
|
||||
|
||||
static int process_comm_event(struct perf_tool *tool,
|
||||
static int process_comm_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -2607,7 +2664,7 @@ static int process_comm_event(struct perf_tool *tool,
|
||||
event->comm.tid);
|
||||
}
|
||||
|
||||
static int process_namespaces_event(struct perf_tool *tool,
|
||||
static int process_namespaces_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -2619,7 +2676,7 @@ static int process_namespaces_event(struct perf_tool *tool,
|
||||
event->namespaces.tid);
|
||||
}
|
||||
|
||||
static int process_cgroup_event(struct perf_tool *tool,
|
||||
static int process_cgroup_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -2631,7 +2688,7 @@ static int process_cgroup_event(struct perf_tool *tool,
|
||||
sample->tid);
|
||||
}
|
||||
|
||||
static int process_fork_event(struct perf_tool *tool,
|
||||
static int process_fork_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -2643,7 +2700,7 @@ static int process_fork_event(struct perf_tool *tool,
|
||||
event->fork.pid, event->fork.tid,
|
||||
event->fork.time);
|
||||
}
|
||||
static int process_exit_event(struct perf_tool *tool,
|
||||
static int process_exit_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -2656,7 +2713,7 @@ static int process_exit_event(struct perf_tool *tool,
|
||||
return perf_event__process_exit(tool, event, sample, machine);
|
||||
}
|
||||
|
||||
static int process_mmap_event(struct perf_tool *tool,
|
||||
static int process_mmap_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -2668,7 +2725,7 @@ static int process_mmap_event(struct perf_tool *tool,
|
||||
event->mmap.tid);
|
||||
}
|
||||
|
||||
static int process_mmap2_event(struct perf_tool *tool,
|
||||
static int process_mmap2_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -2680,7 +2737,7 @@ static int process_mmap2_event(struct perf_tool *tool,
|
||||
event->mmap2.tid);
|
||||
}
|
||||
|
||||
static int process_switch_event(struct perf_tool *tool,
|
||||
static int process_switch_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -2712,7 +2769,7 @@ static int process_auxtrace_error(struct perf_session *session,
|
||||
}
|
||||
|
||||
static int
|
||||
process_lost_event(struct perf_tool *tool,
|
||||
process_lost_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -2722,7 +2779,7 @@ process_lost_event(struct perf_tool *tool,
|
||||
}
|
||||
|
||||
static int
|
||||
process_throttle_event(struct perf_tool *tool __maybe_unused,
|
||||
process_throttle_event(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -2733,7 +2790,7 @@ process_throttle_event(struct perf_tool *tool __maybe_unused,
|
||||
}
|
||||
|
||||
static int
|
||||
process_finished_round_event(struct perf_tool *tool __maybe_unused,
|
||||
process_finished_round_event(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct ordered_events *oe __maybe_unused)
|
||||
|
||||
@@ -2743,7 +2800,7 @@ process_finished_round_event(struct perf_tool *tool __maybe_unused,
|
||||
}
|
||||
|
||||
static int
|
||||
process_bpf_events(struct perf_tool *tool __maybe_unused,
|
||||
process_bpf_events(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -2755,7 +2812,7 @@ process_bpf_events(struct perf_tool *tool __maybe_unused,
|
||||
sample->tid);
|
||||
}
|
||||
|
||||
static int process_text_poke_events(struct perf_tool *tool,
|
||||
static int process_text_poke_events(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -3757,7 +3814,7 @@ static
|
||||
int process_thread_map_event(struct perf_session *session,
|
||||
union perf_event *event)
|
||||
{
|
||||
struct perf_tool *tool = session->tool;
|
||||
const struct perf_tool *tool = session->tool;
|
||||
struct perf_script *script = container_of(tool, struct perf_script, tool);
|
||||
|
||||
if (dump_trace)
|
||||
@@ -3779,7 +3836,7 @@ static
|
||||
int process_cpu_map_event(struct perf_session *session,
|
||||
union perf_event *event)
|
||||
{
|
||||
struct perf_tool *tool = session->tool;
|
||||
const struct perf_tool *tool = session->tool;
|
||||
struct perf_script *script = container_of(tool, struct perf_script, tool);
|
||||
|
||||
if (dump_trace)
|
||||
@@ -3809,11 +3866,10 @@ static int process_feature_event(struct perf_session *session,
|
||||
static int perf_script__process_auxtrace_info(struct perf_session *session,
|
||||
union perf_event *event)
|
||||
{
|
||||
struct perf_tool *tool = session->tool;
|
||||
|
||||
int ret = perf_event__process_auxtrace_info(session, event);
|
||||
|
||||
if (ret == 0) {
|
||||
const struct perf_tool *tool = session->tool;
|
||||
struct perf_script *script = container_of(tool, struct perf_script, tool);
|
||||
|
||||
ret = perf_script__setup_per_event_dump(script);
|
||||
@@ -3900,38 +3956,7 @@ int cmd_script(int argc, const char **argv)
|
||||
const char *dlfilter_file = NULL;
|
||||
const char **__argv;
|
||||
int i, j, err = 0;
|
||||
struct perf_script script = {
|
||||
.tool = {
|
||||
.sample = process_sample_event,
|
||||
.mmap = perf_event__process_mmap,
|
||||
.mmap2 = perf_event__process_mmap2,
|
||||
.comm = perf_event__process_comm,
|
||||
.namespaces = perf_event__process_namespaces,
|
||||
.cgroup = perf_event__process_cgroup,
|
||||
.exit = perf_event__process_exit,
|
||||
.fork = perf_event__process_fork,
|
||||
.attr = process_attr,
|
||||
.event_update = perf_event__process_event_update,
|
||||
#ifdef HAVE_LIBTRACEEVENT
|
||||
.tracing_data = perf_event__process_tracing_data,
|
||||
#endif
|
||||
.feature = process_feature_event,
|
||||
.build_id = perf_event__process_build_id,
|
||||
.id_index = perf_event__process_id_index,
|
||||
.auxtrace_info = perf_script__process_auxtrace_info,
|
||||
.auxtrace = perf_event__process_auxtrace,
|
||||
.auxtrace_error = perf_event__process_auxtrace_error,
|
||||
.stat = perf_event__process_stat_event,
|
||||
.stat_round = process_stat_round_event,
|
||||
.stat_config = process_stat_config_event,
|
||||
.thread_map = process_thread_map_event,
|
||||
.cpu_map = process_cpu_map_event,
|
||||
.throttle = process_throttle_event,
|
||||
.unthrottle = process_throttle_event,
|
||||
.ordered_events = true,
|
||||
.ordering_requires_timestamps = true,
|
||||
},
|
||||
};
|
||||
struct perf_script script = {};
|
||||
struct perf_data data = {
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
};
|
||||
@@ -3979,7 +4004,8 @@ int cmd_script(int argc, const char **argv)
|
||||
"brstacksym,flags,data_src,weight,bpf-output,brstackinsn,"
|
||||
"brstackinsnlen,brstackdisasm,brstackoff,callindent,insn,disasm,insnlen,synth,"
|
||||
"phys_addr,metric,misc,srccode,ipc,tod,data_page_size,"
|
||||
"code_page_size,ins_lat,machine_pid,vcpu,cgroup,retire_lat",
|
||||
"code_page_size,ins_lat,machine_pid,vcpu,cgroup,retire_lat,"
|
||||
"brcntr",
|
||||
parse_output_fields),
|
||||
OPT_BOOLEAN('a', "all-cpus", &system_wide,
|
||||
"system-wide collection from all CPUs"),
|
||||
@@ -4052,6 +4078,8 @@ int cmd_script(int argc, const char **argv)
|
||||
"Enable symbol demangling"),
|
||||
OPT_BOOLEAN(0, "demangle-kernel", &symbol_conf.demangle_kernel,
|
||||
"Enable kernel symbol demangling"),
|
||||
OPT_STRING(0, "addr2line", &symbol_conf.addr2line_path, "path",
|
||||
"addr2line binary to use for line numbers"),
|
||||
OPT_STRING(0, "time", &script.time_str, "str",
|
||||
"Time span of interest (start,stop)"),
|
||||
OPT_BOOLEAN(0, "inline", &symbol_conf.inline_name,
|
||||
@@ -4103,10 +4131,8 @@ int cmd_script(int argc, const char **argv)
|
||||
data.path = input_name;
|
||||
data.force = symbol_conf.force;
|
||||
|
||||
if (unsorted_dump) {
|
||||
if (unsorted_dump)
|
||||
dump_trace = true;
|
||||
script.tool.ordered_events = false;
|
||||
}
|
||||
|
||||
if (symbol__validate_sym_arguments())
|
||||
return -1;
|
||||
@@ -4297,6 +4323,34 @@ script_found:
|
||||
use_browser = 0;
|
||||
}
|
||||
|
||||
perf_tool__init(&script.tool, !unsorted_dump);
|
||||
script.tool.sample = process_sample_event;
|
||||
script.tool.mmap = perf_event__process_mmap;
|
||||
script.tool.mmap2 = perf_event__process_mmap2;
|
||||
script.tool.comm = perf_event__process_comm;
|
||||
script.tool.namespaces = perf_event__process_namespaces;
|
||||
script.tool.cgroup = perf_event__process_cgroup;
|
||||
script.tool.exit = perf_event__process_exit;
|
||||
script.tool.fork = perf_event__process_fork;
|
||||
script.tool.attr = process_attr;
|
||||
script.tool.event_update = perf_event__process_event_update;
|
||||
#ifdef HAVE_LIBTRACEEVENT
|
||||
script.tool.tracing_data = perf_event__process_tracing_data;
|
||||
#endif
|
||||
script.tool.feature = process_feature_event;
|
||||
script.tool.build_id = perf_event__process_build_id;
|
||||
script.tool.id_index = perf_event__process_id_index;
|
||||
script.tool.auxtrace_info = perf_script__process_auxtrace_info;
|
||||
script.tool.auxtrace = perf_event__process_auxtrace;
|
||||
script.tool.auxtrace_error = perf_event__process_auxtrace_error;
|
||||
script.tool.stat = perf_event__process_stat_event;
|
||||
script.tool.stat_round = process_stat_round_event;
|
||||
script.tool.stat_config = process_stat_config_event;
|
||||
script.tool.thread_map = process_thread_map_event;
|
||||
script.tool.cpu_map = process_cpu_map_event;
|
||||
script.tool.throttle = process_throttle_event;
|
||||
script.tool.unthrottle = process_throttle_event;
|
||||
script.tool.ordering_requires_timestamps = true;
|
||||
session = perf_session__new(&data, &script.tool);
|
||||
if (IS_ERR(session))
|
||||
return PTR_ERR(session);
|
||||
|
||||
+25
-17
@@ -70,6 +70,7 @@
|
||||
#include "util/bpf_counter.h"
|
||||
#include "util/iostat.h"
|
||||
#include "util/util.h"
|
||||
#include "util/intel-tpebs.h"
|
||||
#include "asm/bug.h"
|
||||
|
||||
#include <linux/time64.h>
|
||||
@@ -248,7 +249,7 @@ static void perf_stat__reset_stats(void)
|
||||
perf_stat__reset_shadow_stats();
|
||||
}
|
||||
|
||||
static int process_synthesized_event(struct perf_tool *tool __maybe_unused,
|
||||
static int process_synthesized_event(const struct perf_tool *tool __maybe_unused,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine __maybe_unused)
|
||||
@@ -293,14 +294,14 @@ static int read_single_counter(struct evsel *counter, int cpu_map_idx, int threa
|
||||
* terminates. Use the wait4 values in that case.
|
||||
*/
|
||||
if (err && cpu_map_idx == 0 &&
|
||||
(counter->tool_event == PERF_TOOL_USER_TIME ||
|
||||
counter->tool_event == PERF_TOOL_SYSTEM_TIME)) {
|
||||
(evsel__tool_event(counter) == PERF_TOOL_USER_TIME ||
|
||||
evsel__tool_event(counter) == PERF_TOOL_SYSTEM_TIME)) {
|
||||
u64 val, *start_time;
|
||||
struct perf_counts_values *count =
|
||||
perf_counts(counter->counts, cpu_map_idx, thread);
|
||||
|
||||
start_time = xyarray__entry(counter->start_times, cpu_map_idx, thread);
|
||||
if (counter->tool_event == PERF_TOOL_USER_TIME)
|
||||
if (evsel__tool_event(counter) == PERF_TOOL_USER_TIME)
|
||||
val = ru_stats.ru_utime_usec_stat.mean;
|
||||
else
|
||||
val = ru_stats.ru_stime_usec_stat.mean;
|
||||
@@ -683,6 +684,9 @@ static enum counter_recovery stat_handle_error(struct evsel *counter)
|
||||
|
||||
if (child_pid != -1)
|
||||
kill(child_pid, SIGTERM);
|
||||
|
||||
tpebs_delete();
|
||||
|
||||
return COUNTER_FATAL;
|
||||
}
|
||||
|
||||
@@ -833,7 +837,7 @@ try_again_reset:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (evlist__apply_filters(evsel_list, &counter)) {
|
||||
if (evlist__apply_filters(evsel_list, &counter, &target)) {
|
||||
pr_err("failed to set filter \"%s\" on event %s with %d (%s)\n",
|
||||
counter->filter, evsel__name(counter), errno,
|
||||
str_error_r(errno, msg, sizeof(msg)));
|
||||
@@ -2180,7 +2184,7 @@ static
|
||||
int process_stat_config_event(struct perf_session *session,
|
||||
union perf_event *event)
|
||||
{
|
||||
struct perf_tool *tool = session->tool;
|
||||
const struct perf_tool *tool = session->tool;
|
||||
struct perf_stat *st = container_of(tool, struct perf_stat, tool);
|
||||
|
||||
perf_event__read_stat_config(&stat_config, &event->stat_config);
|
||||
@@ -2229,7 +2233,7 @@ static
|
||||
int process_thread_map_event(struct perf_session *session,
|
||||
union perf_event *event)
|
||||
{
|
||||
struct perf_tool *tool = session->tool;
|
||||
const struct perf_tool *tool = session->tool;
|
||||
struct perf_stat *st = container_of(tool, struct perf_stat, tool);
|
||||
|
||||
if (st->threads) {
|
||||
@@ -2248,7 +2252,7 @@ static
|
||||
int process_cpu_map_event(struct perf_session *session,
|
||||
union perf_event *event)
|
||||
{
|
||||
struct perf_tool *tool = session->tool;
|
||||
const struct perf_tool *tool = session->tool;
|
||||
struct perf_stat *st = container_of(tool, struct perf_stat, tool);
|
||||
struct perf_cpu_map *cpus;
|
||||
|
||||
@@ -2271,15 +2275,6 @@ static const char * const stat_report_usage[] = {
|
||||
};
|
||||
|
||||
static struct perf_stat perf_stat = {
|
||||
.tool = {
|
||||
.attr = perf_event__process_attr,
|
||||
.event_update = perf_event__process_event_update,
|
||||
.thread_map = process_thread_map_event,
|
||||
.cpu_map = process_cpu_map_event,
|
||||
.stat_config = process_stat_config_event,
|
||||
.stat = perf_event__process_stat_event,
|
||||
.stat_round = process_stat_round_event,
|
||||
},
|
||||
.aggr_mode = AGGR_UNSET,
|
||||
.aggr_level = 0,
|
||||
};
|
||||
@@ -2322,6 +2317,15 @@ static int __cmd_report(int argc, const char **argv)
|
||||
perf_stat.data.path = input_name;
|
||||
perf_stat.data.mode = PERF_DATA_MODE_READ;
|
||||
|
||||
perf_tool__init(&perf_stat.tool, /*ordered_events=*/false);
|
||||
perf_stat.tool.attr = perf_event__process_attr;
|
||||
perf_stat.tool.event_update = perf_event__process_event_update;
|
||||
perf_stat.tool.thread_map = process_thread_map_event;
|
||||
perf_stat.tool.cpu_map = process_cpu_map_event;
|
||||
perf_stat.tool.stat_config = process_stat_config_event;
|
||||
perf_stat.tool.stat = perf_event__process_stat_event;
|
||||
perf_stat.tool.stat_round = process_stat_round_event;
|
||||
|
||||
session = perf_session__new(&perf_stat.data, &perf_stat.tool);
|
||||
if (IS_ERR(session))
|
||||
return PTR_ERR(session);
|
||||
@@ -2471,6 +2475,10 @@ int cmd_stat(int argc, const char **argv)
|
||||
"disable adding events for the metric threshold calculation"),
|
||||
OPT_BOOLEAN(0, "topdown", &topdown_run,
|
||||
"measure top-down statistics"),
|
||||
#ifdef HAVE_ARCH_X86_64_SUPPORT
|
||||
OPT_BOOLEAN(0, "record-tpebs", &tpebs_recording,
|
||||
"enable recording for tpebs when retire_latency required"),
|
||||
#endif
|
||||
OPT_UINTEGER(0, "td-level", &stat_config.topdown_level,
|
||||
"Set the metrics level for the top-down statistics (0: max level)"),
|
||||
OPT_BOOLEAN(0, "smi-cost", &smi_cost,
|
||||
|
||||
@@ -320,7 +320,7 @@ static int *cpus_cstate_state;
|
||||
static u64 *cpus_pstate_start_times;
|
||||
static u64 *cpus_pstate_state;
|
||||
|
||||
static int process_comm_event(struct perf_tool *tool,
|
||||
static int process_comm_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine __maybe_unused)
|
||||
@@ -330,7 +330,7 @@ static int process_comm_event(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_fork_event(struct perf_tool *tool,
|
||||
static int process_fork_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine __maybe_unused)
|
||||
@@ -340,7 +340,7 @@ static int process_fork_event(struct perf_tool *tool,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_exit_event(struct perf_tool *tool,
|
||||
static int process_exit_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample __maybe_unused,
|
||||
struct machine *machine __maybe_unused)
|
||||
@@ -571,7 +571,7 @@ typedef int (*tracepoint_handler)(struct timechart *tchart,
|
||||
struct perf_sample *sample,
|
||||
const char *backtrace);
|
||||
|
||||
static int process_sample_event(struct perf_tool *tool,
|
||||
static int process_sample_event(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
@@ -1606,10 +1606,16 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name)
|
||||
.mode = PERF_DATA_MODE_READ,
|
||||
.force = tchart->force,
|
||||
};
|
||||
|
||||
struct perf_session *session = perf_session__new(&data, &tchart->tool);
|
||||
struct perf_session *session;
|
||||
int ret = -EINVAL;
|
||||
|
||||
perf_tool__init(&tchart->tool, /*ordered_events=*/true);
|
||||
tchart->tool.comm = process_comm_event;
|
||||
tchart->tool.fork = process_fork_event;
|
||||
tchart->tool.exit = process_exit_event;
|
||||
tchart->tool.sample = process_sample_event;
|
||||
|
||||
session = perf_session__new(&data, &tchart->tool);
|
||||
if (IS_ERR(session))
|
||||
return PTR_ERR(session);
|
||||
|
||||
@@ -1924,13 +1930,6 @@ parse_time(const struct option *opt, const char *arg, int __maybe_unused unset)
|
||||
int cmd_timechart(int argc, const char **argv)
|
||||
{
|
||||
struct timechart tchart = {
|
||||
.tool = {
|
||||
.comm = process_comm_event,
|
||||
.fork = process_fork_event,
|
||||
.exit = process_exit_event,
|
||||
.sample = process_sample_event,
|
||||
.ordered_events = true,
|
||||
},
|
||||
.proc_num = 15,
|
||||
.min_time = NSEC_PER_MSEC,
|
||||
.merge_dist = 1000,
|
||||
|
||||
@@ -191,7 +191,7 @@ static void ui__warn_map_erange(struct map *map, struct symbol *sym, u64 ip)
|
||||
if (use_browser <= 0)
|
||||
sleep(5);
|
||||
|
||||
map__set_erange_warned(map, true);
|
||||
map__set_erange_warned(map);
|
||||
}
|
||||
|
||||
static void perf_top__record_precise_ip(struct perf_top *top,
|
||||
@@ -735,12 +735,12 @@ static int hist_iter__top_callback(struct hist_entry_iter *iter,
|
||||
perf_top__record_precise_ip(top, iter->he, iter->sample, evsel, al->addr);
|
||||
|
||||
hist__account_cycles(iter->sample->branch_stack, al, iter->sample,
|
||||
!(top->record_opts.branch_stack & PERF_SAMPLE_BRANCH_ANY),
|
||||
NULL);
|
||||
!(top->record_opts.branch_stack & PERF_SAMPLE_BRANCH_ANY),
|
||||
NULL, evsel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void perf_event__process_sample(struct perf_tool *tool,
|
||||
static void perf_event__process_sample(const struct perf_tool *tool,
|
||||
const union perf_event *event,
|
||||
struct evsel *evsel,
|
||||
struct perf_sample *sample,
|
||||
@@ -1055,7 +1055,7 @@ try_again:
|
||||
}
|
||||
}
|
||||
|
||||
if (evlist__apply_filters(evlist, &counter)) {
|
||||
if (evlist__apply_filters(evlist, &counter, &opts->target)) {
|
||||
pr_err("failed to set filter \"%s\" on event %s with %d (%s)\n",
|
||||
counter->filter ?: "BPF", evsel__name(counter), errno,
|
||||
str_error_r(errno, msg, sizeof(msg)));
|
||||
|
||||
+454
-43
@@ -19,6 +19,7 @@
|
||||
#ifdef HAVE_LIBBPF_SUPPORT
|
||||
#include <bpf/bpf.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <bpf/btf.h>
|
||||
#ifdef HAVE_BPF_SKEL
|
||||
#include "bpf_skel/augmented_raw_syscalls.skel.h"
|
||||
#endif
|
||||
@@ -64,6 +65,7 @@
|
||||
#include "syscalltbl.h"
|
||||
#include "rb_resort.h"
|
||||
#include "../perf.h"
|
||||
#include "trace_augment.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
@@ -101,6 +103,12 @@
|
||||
|
||||
/*
|
||||
* strtoul: Go from a string to a value, i.e. for msr: MSR_FS_BASE to 0xc0000100
|
||||
*
|
||||
* We have to explicitely mark the direction of the flow of data, if from the
|
||||
* kernel to user space or the other way around, since the BPF collector we
|
||||
* have so far copies only from user to kernel space, mark the arguments that
|
||||
* go that direction, so that we don´t end up collecting the previous contents
|
||||
* for syscall args that goes from kernel to user space.
|
||||
*/
|
||||
struct syscall_arg_fmt {
|
||||
size_t (*scnprintf)(char *bf, size_t size, struct syscall_arg *arg);
|
||||
@@ -109,7 +117,12 @@ struct syscall_arg_fmt {
|
||||
void *parm;
|
||||
const char *name;
|
||||
u16 nr_entries; // for arrays
|
||||
bool from_user;
|
||||
bool show_zero;
|
||||
#ifdef HAVE_LIBBPF_SUPPORT
|
||||
const struct btf_type *type;
|
||||
int type_id; /* used in btf_dump */
|
||||
#endif
|
||||
};
|
||||
|
||||
struct syscall_fmt {
|
||||
@@ -139,6 +152,9 @@ struct trace {
|
||||
} syscalls;
|
||||
#ifdef HAVE_BPF_SKEL
|
||||
struct augmented_raw_syscalls_bpf *skel;
|
||||
#endif
|
||||
#ifdef HAVE_LIBBPF_SUPPORT
|
||||
struct btf *btf;
|
||||
#endif
|
||||
struct record_opts opts;
|
||||
struct evlist *evlist;
|
||||
@@ -196,6 +212,7 @@ struct trace {
|
||||
bool show_string_prefix;
|
||||
bool force;
|
||||
bool vfs_getname;
|
||||
bool force_btf;
|
||||
int trace_pgfaults;
|
||||
char *perfconfig_events;
|
||||
struct {
|
||||
@@ -204,6 +221,20 @@ struct trace {
|
||||
} oe;
|
||||
};
|
||||
|
||||
static void trace__load_vmlinux_btf(struct trace *trace __maybe_unused)
|
||||
{
|
||||
#ifdef HAVE_LIBBPF_SUPPORT
|
||||
if (trace->btf != NULL)
|
||||
return;
|
||||
|
||||
trace->btf = btf__load_vmlinux_btf();
|
||||
if (verbose > 0) {
|
||||
fprintf(trace->output, trace->btf ? "vmlinux BTF loaded\n" :
|
||||
"Failed to load vmlinux BTF\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
struct tp_field {
|
||||
int offset;
|
||||
union {
|
||||
@@ -830,6 +861,15 @@ static size_t syscall_arg__scnprintf_filename(char *bf, size_t size,
|
||||
|
||||
#define SCA_FILENAME syscall_arg__scnprintf_filename
|
||||
|
||||
// 'argname' is just documentational at this point, to remove the previous comment with that info
|
||||
#define SCA_FILENAME_FROM_USER(argname) \
|
||||
{ .scnprintf = SCA_FILENAME, \
|
||||
.from_user = true, }
|
||||
|
||||
static size_t syscall_arg__scnprintf_buf(char *bf, size_t size, struct syscall_arg *arg);
|
||||
|
||||
#define SCA_BUF syscall_arg__scnprintf_buf
|
||||
|
||||
static size_t syscall_arg__scnprintf_pipe_flags(char *bf, size_t size,
|
||||
struct syscall_arg *arg)
|
||||
{
|
||||
@@ -887,6 +927,177 @@ static size_t syscall_arg__scnprintf_getrandom_flags(char *bf, size_t size,
|
||||
|
||||
#define SCA_GETRANDOM_FLAGS syscall_arg__scnprintf_getrandom_flags
|
||||
|
||||
#ifdef HAVE_LIBBPF_SUPPORT
|
||||
static void syscall_arg_fmt__cache_btf_enum(struct syscall_arg_fmt *arg_fmt, struct btf *btf, char *type)
|
||||
{
|
||||
int id;
|
||||
|
||||
type = strstr(type, "enum ");
|
||||
if (type == NULL)
|
||||
return;
|
||||
|
||||
type += 5; // skip "enum " to get the enumeration name
|
||||
|
||||
id = btf__find_by_name(btf, type);
|
||||
if (id < 0)
|
||||
return;
|
||||
|
||||
arg_fmt->type = btf__type_by_id(btf, id);
|
||||
}
|
||||
|
||||
static bool syscall_arg__strtoul_btf_enum(char *bf, size_t size, struct syscall_arg *arg, u64 *val)
|
||||
{
|
||||
const struct btf_type *bt = arg->fmt->type;
|
||||
struct btf *btf = arg->trace->btf;
|
||||
struct btf_enum *be = btf_enum(bt);
|
||||
|
||||
for (int i = 0; i < btf_vlen(bt); ++i, ++be) {
|
||||
const char *name = btf__name_by_offset(btf, be->name_off);
|
||||
int max_len = max(size, strlen(name));
|
||||
|
||||
if (strncmp(name, bf, max_len) == 0) {
|
||||
*val = be->val;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool syscall_arg__strtoul_btf_type(char *bf, size_t size, struct syscall_arg *arg, u64 *val)
|
||||
{
|
||||
const struct btf_type *bt;
|
||||
char *type = arg->type_name;
|
||||
struct btf *btf;
|
||||
|
||||
trace__load_vmlinux_btf(arg->trace);
|
||||
|
||||
btf = arg->trace->btf;
|
||||
if (btf == NULL)
|
||||
return false;
|
||||
|
||||
if (arg->fmt->type == NULL) {
|
||||
// See if this is an enum
|
||||
syscall_arg_fmt__cache_btf_enum(arg->fmt, btf, type);
|
||||
}
|
||||
|
||||
// Now let's see if we have a BTF type resolved
|
||||
bt = arg->fmt->type;
|
||||
if (bt == NULL)
|
||||
return false;
|
||||
|
||||
// If it is an enum:
|
||||
if (btf_is_enum(arg->fmt->type))
|
||||
return syscall_arg__strtoul_btf_enum(bf, size, arg, val);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static size_t btf_enum_scnprintf(const struct btf_type *type, struct btf *btf, char *bf, size_t size, int val)
|
||||
{
|
||||
struct btf_enum *be = btf_enum(type);
|
||||
const int nr_entries = btf_vlen(type);
|
||||
|
||||
for (int i = 0; i < nr_entries; ++i, ++be) {
|
||||
if (be->val == val) {
|
||||
return scnprintf(bf, size, "%s",
|
||||
btf__name_by_offset(btf, be->name_off));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct trace_btf_dump_snprintf_ctx {
|
||||
char *bf;
|
||||
size_t printed, size;
|
||||
};
|
||||
|
||||
static void trace__btf_dump_snprintf(void *vctx, const char *fmt, va_list args)
|
||||
{
|
||||
struct trace_btf_dump_snprintf_ctx *ctx = vctx;
|
||||
|
||||
ctx->printed += vscnprintf(ctx->bf + ctx->printed, ctx->size - ctx->printed, fmt, args);
|
||||
}
|
||||
|
||||
static size_t btf_struct_scnprintf(const struct btf_type *type, struct btf *btf, char *bf, size_t size, struct syscall_arg *arg)
|
||||
{
|
||||
struct trace_btf_dump_snprintf_ctx ctx = {
|
||||
.bf = bf,
|
||||
.size = size,
|
||||
};
|
||||
struct augmented_arg *augmented_arg = arg->augmented.args;
|
||||
int type_id = arg->fmt->type_id, consumed;
|
||||
struct btf_dump *btf_dump;
|
||||
|
||||
LIBBPF_OPTS(btf_dump_opts, dump_opts);
|
||||
LIBBPF_OPTS(btf_dump_type_data_opts, dump_data_opts);
|
||||
|
||||
if (arg == NULL || arg->augmented.args == NULL)
|
||||
return 0;
|
||||
|
||||
dump_data_opts.compact = true;
|
||||
dump_data_opts.skip_names = !arg->trace->show_arg_names;
|
||||
|
||||
btf_dump = btf_dump__new(btf, trace__btf_dump_snprintf, &ctx, &dump_opts);
|
||||
if (btf_dump == NULL)
|
||||
return 0;
|
||||
|
||||
/* pretty print the struct data here */
|
||||
if (btf_dump__dump_type_data(btf_dump, type_id, arg->augmented.args->value, type->size, &dump_data_opts) == 0)
|
||||
return 0;
|
||||
|
||||
consumed = sizeof(*augmented_arg) + augmented_arg->size;
|
||||
arg->augmented.args = ((void *)arg->augmented.args) + consumed;
|
||||
arg->augmented.size -= consumed;
|
||||
|
||||
btf_dump__free(btf_dump);
|
||||
|
||||
return ctx.printed;
|
||||
}
|
||||
|
||||
static size_t trace__btf_scnprintf(struct trace *trace, struct syscall_arg *arg, char *bf,
|
||||
size_t size, int val, char *type)
|
||||
{
|
||||
struct syscall_arg_fmt *arg_fmt = arg->fmt;
|
||||
|
||||
if (trace->btf == NULL)
|
||||
return 0;
|
||||
|
||||
if (arg_fmt->type == NULL) {
|
||||
// Check if this is an enum and if we have the BTF type for it.
|
||||
syscall_arg_fmt__cache_btf_enum(arg_fmt, trace->btf, type);
|
||||
}
|
||||
|
||||
// Did we manage to find a BTF type for the syscall/tracepoint argument?
|
||||
if (arg_fmt->type == NULL)
|
||||
return 0;
|
||||
|
||||
if (btf_is_enum(arg_fmt->type))
|
||||
return btf_enum_scnprintf(arg_fmt->type, trace->btf, bf, size, val);
|
||||
else if (btf_is_struct(arg_fmt->type) || btf_is_union(arg_fmt->type))
|
||||
return btf_struct_scnprintf(arg_fmt->type, trace->btf, bf, size, arg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // HAVE_LIBBPF_SUPPORT
|
||||
static size_t trace__btf_scnprintf(struct trace *trace __maybe_unused, struct syscall_arg *arg __maybe_unused,
|
||||
char *bf __maybe_unused, size_t size __maybe_unused, int val __maybe_unused,
|
||||
char *type __maybe_unused)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool syscall_arg__strtoul_btf_type(char *bf __maybe_unused, size_t size __maybe_unused,
|
||||
struct syscall_arg *arg __maybe_unused, u64 *val __maybe_unused)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif // HAVE_LIBBPF_SUPPORT
|
||||
|
||||
#define STUL_BTF_TYPE syscall_arg__strtoul_btf_type
|
||||
|
||||
#define STRARRAY(name, array) \
|
||||
{ .scnprintf = SCA_STRARRAY, \
|
||||
.strtoul = STUL_STRARRAY, \
|
||||
@@ -921,16 +1132,17 @@ static const struct syscall_fmt syscall_fmts[] = {
|
||||
[1] = { .scnprintf = SCA_PTR, /* arg2 */ }, }, },
|
||||
{ .name = "bind",
|
||||
.arg = { [0] = { .scnprintf = SCA_INT, /* fd */ },
|
||||
[1] = { .scnprintf = SCA_SOCKADDR, /* umyaddr */ },
|
||||
[1] = SCA_SOCKADDR_FROM_USER(umyaddr),
|
||||
[2] = { .scnprintf = SCA_INT, /* addrlen */ }, }, },
|
||||
{ .name = "bpf",
|
||||
.arg = { [0] = STRARRAY(cmd, bpf_cmd), }, },
|
||||
.arg = { [0] = STRARRAY(cmd, bpf_cmd),
|
||||
[1] = { .from_user = true /* attr */, }, } },
|
||||
{ .name = "brk", .hexret = true,
|
||||
.arg = { [0] = { .scnprintf = SCA_PTR, /* brk */ }, }, },
|
||||
{ .name = "clock_gettime",
|
||||
.arg = { [0] = STRARRAY(clk_id, clockid), }, },
|
||||
{ .name = "clock_nanosleep",
|
||||
.arg = { [2] = { .scnprintf = SCA_TIMESPEC, /* rqtp */ }, }, },
|
||||
.arg = { [2] = SCA_TIMESPEC_FROM_USER(req), }, },
|
||||
{ .name = "clone", .errpid = true, .nr_args = 5,
|
||||
.arg = { [0] = { .name = "flags", .scnprintf = SCA_CLONE_FLAGS, },
|
||||
[1] = { .name = "child_stack", .scnprintf = SCA_HEX, },
|
||||
@@ -941,7 +1153,7 @@ static const struct syscall_fmt syscall_fmts[] = {
|
||||
.arg = { [0] = { .scnprintf = SCA_CLOSE_FD, /* fd */ }, }, },
|
||||
{ .name = "connect",
|
||||
.arg = { [0] = { .scnprintf = SCA_INT, /* fd */ },
|
||||
[1] = { .scnprintf = SCA_SOCKADDR, /* servaddr */ },
|
||||
[1] = SCA_SOCKADDR_FROM_USER(servaddr),
|
||||
[2] = { .scnprintf = SCA_INT, /* addrlen */ }, }, },
|
||||
{ .name = "epoll_ctl",
|
||||
.arg = { [1] = STRARRAY(op, epoll_ctl_ops), }, },
|
||||
@@ -949,11 +1161,11 @@ static const struct syscall_fmt syscall_fmts[] = {
|
||||
.arg = { [1] = { .scnprintf = SCA_EFD_FLAGS, /* flags */ }, }, },
|
||||
{ .name = "faccessat",
|
||||
.arg = { [0] = { .scnprintf = SCA_FDAT, /* dirfd */ },
|
||||
[1] = { .scnprintf = SCA_FILENAME, /* pathname */ },
|
||||
[1] = SCA_FILENAME_FROM_USER(pathname),
|
||||
[2] = { .scnprintf = SCA_ACCMODE, /* mode */ }, }, },
|
||||
{ .name = "faccessat2",
|
||||
.arg = { [0] = { .scnprintf = SCA_FDAT, /* dirfd */ },
|
||||
[1] = { .scnprintf = SCA_FILENAME, /* pathname */ },
|
||||
[1] = SCA_FILENAME_FROM_USER(pathname),
|
||||
[2] = { .scnprintf = SCA_ACCMODE, /* mode */ },
|
||||
[3] = { .scnprintf = SCA_FACCESSAT2_FLAGS, /* flags */ }, }, },
|
||||
{ .name = "fchmodat",
|
||||
@@ -975,7 +1187,7 @@ static const struct syscall_fmt syscall_fmts[] = {
|
||||
[2] = { .scnprintf = SCA_FSMOUNT_ATTR_FLAGS, /* attr_flags */ }, }, },
|
||||
{ .name = "fspick",
|
||||
.arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ },
|
||||
[1] = { .scnprintf = SCA_FILENAME, /* path */ },
|
||||
[1] = SCA_FILENAME_FROM_USER(path),
|
||||
[2] = { .scnprintf = SCA_FSPICK_FLAGS, /* flags */ }, }, },
|
||||
{ .name = "fstat", .alias = "newfstat", },
|
||||
{ .name = "futex",
|
||||
@@ -1039,29 +1251,29 @@ static const struct syscall_fmt syscall_fmts[] = {
|
||||
.parm = &strarray__mmap_flags, },
|
||||
[5] = { .scnprintf = SCA_HEX, /* offset */ }, }, },
|
||||
{ .name = "mount",
|
||||
.arg = { [0] = { .scnprintf = SCA_FILENAME, /* dev_name */ },
|
||||
.arg = { [0] = SCA_FILENAME_FROM_USER(devname),
|
||||
[3] = { .scnprintf = SCA_MOUNT_FLAGS, /* flags */
|
||||
.mask_val = SCAMV_MOUNT_FLAGS, /* flags */ }, }, },
|
||||
{ .name = "move_mount",
|
||||
.arg = { [0] = { .scnprintf = SCA_FDAT, /* from_dfd */ },
|
||||
[1] = { .scnprintf = SCA_FILENAME, /* from_pathname */ },
|
||||
[1] = SCA_FILENAME_FROM_USER(pathname),
|
||||
[2] = { .scnprintf = SCA_FDAT, /* to_dfd */ },
|
||||
[3] = { .scnprintf = SCA_FILENAME, /* to_pathname */ },
|
||||
[3] = SCA_FILENAME_FROM_USER(pathname),
|
||||
[4] = { .scnprintf = SCA_MOVE_MOUNT_FLAGS, /* flags */ }, }, },
|
||||
{ .name = "mprotect",
|
||||
.arg = { [0] = { .scnprintf = SCA_HEX, /* start */ },
|
||||
[2] = { .scnprintf = SCA_MMAP_PROT, .show_zero = true, /* prot */ }, }, },
|
||||
{ .name = "mq_unlink",
|
||||
.arg = { [0] = { .scnprintf = SCA_FILENAME, /* u_name */ }, }, },
|
||||
.arg = { [0] = SCA_FILENAME_FROM_USER(u_name), }, },
|
||||
{ .name = "mremap", .hexret = true,
|
||||
.arg = { [3] = { .scnprintf = SCA_MREMAP_FLAGS, /* flags */ }, }, },
|
||||
{ .name = "name_to_handle_at",
|
||||
.arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, }, },
|
||||
{ .name = "nanosleep",
|
||||
.arg = { [0] = { .scnprintf = SCA_TIMESPEC, /* req */ }, }, },
|
||||
.arg = { [0] = SCA_TIMESPEC_FROM_USER(req), }, },
|
||||
{ .name = "newfstatat", .alias = "fstatat",
|
||||
.arg = { [0] = { .scnprintf = SCA_FDAT, /* dirfd */ },
|
||||
[1] = { .scnprintf = SCA_FILENAME, /* pathname */ },
|
||||
[1] = SCA_FILENAME_FROM_USER(pathname),
|
||||
[3] = { .scnprintf = SCA_FS_AT_FLAGS, /* flags */ }, }, },
|
||||
{ .name = "open",
|
||||
.arg = { [1] = { .scnprintf = SCA_OPEN_FLAGS, /* flags */ }, }, },
|
||||
@@ -1072,7 +1284,7 @@ static const struct syscall_fmt syscall_fmts[] = {
|
||||
.arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ },
|
||||
[2] = { .scnprintf = SCA_OPEN_FLAGS, /* flags */ }, }, },
|
||||
{ .name = "perf_event_open",
|
||||
.arg = { [0] = { .scnprintf = SCA_PERF_ATTR, /* attr */ },
|
||||
.arg = { [0] = SCA_PERF_ATTR_FROM_USER(attr),
|
||||
[2] = { .scnprintf = SCA_INT, /* cpu */ },
|
||||
[3] = { .scnprintf = SCA_FD, /* group_fd */ },
|
||||
[4] = { .scnprintf = SCA_PERF_FLAGS, /* flags */ }, }, },
|
||||
@@ -1097,7 +1309,8 @@ static const struct syscall_fmt syscall_fmts[] = {
|
||||
{ .name = "pread", .alias = "pread64", },
|
||||
{ .name = "preadv", .alias = "pread", },
|
||||
{ .name = "prlimit64",
|
||||
.arg = { [1] = STRARRAY(resource, rlimit_resources), }, },
|
||||
.arg = { [1] = STRARRAY(resource, rlimit_resources),
|
||||
[2] = { .from_user = true /* new_rlim */, }, }, },
|
||||
{ .name = "pwrite", .alias = "pwrite64", },
|
||||
{ .name = "readlinkat",
|
||||
.arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, }, },
|
||||
@@ -1114,6 +1327,8 @@ static const struct syscall_fmt syscall_fmts[] = {
|
||||
.arg = { [0] = { .scnprintf = SCA_FDAT, /* olddirfd */ },
|
||||
[2] = { .scnprintf = SCA_FDAT, /* newdirfd */ },
|
||||
[4] = { .scnprintf = SCA_RENAMEAT2_FLAGS, /* flags */ }, }, },
|
||||
{ .name = "rseq", .errpid = true,
|
||||
.arg = { [0] = { .from_user = true /* rseq */, }, }, },
|
||||
{ .name = "rt_sigaction",
|
||||
.arg = { [0] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, },
|
||||
{ .name = "rt_sigprocmask",
|
||||
@@ -1135,12 +1350,15 @@ static const struct syscall_fmt syscall_fmts[] = {
|
||||
.arg = { [2] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, }, },
|
||||
{ .name = "sendto",
|
||||
.arg = { [3] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ },
|
||||
[4] = { .scnprintf = SCA_SOCKADDR, /* addr */ }, }, },
|
||||
[4] = SCA_SOCKADDR_FROM_USER(addr), }, },
|
||||
{ .name = "set_robust_list", .errpid = true,
|
||||
.arg = { [0] = { .from_user = true /* head */, }, }, },
|
||||
{ .name = "set_tid_address", .errpid = true, },
|
||||
{ .name = "setitimer",
|
||||
.arg = { [0] = STRARRAY(which, itimers), }, },
|
||||
{ .name = "setrlimit",
|
||||
.arg = { [0] = STRARRAY(resource, rlimit_resources), }, },
|
||||
.arg = { [0] = STRARRAY(resource, rlimit_resources),
|
||||
[1] = { .from_user = true /* rlim */, }, }, },
|
||||
{ .name = "setsockopt",
|
||||
.arg = { [1] = STRARRAY(level, socket_level), }, },
|
||||
{ .name = "socket",
|
||||
@@ -1157,9 +1375,9 @@ static const struct syscall_fmt syscall_fmts[] = {
|
||||
[2] = { .scnprintf = SCA_FS_AT_FLAGS, /* flags */ } ,
|
||||
[3] = { .scnprintf = SCA_STATX_MASK, /* mask */ }, }, },
|
||||
{ .name = "swapoff",
|
||||
.arg = { [0] = { .scnprintf = SCA_FILENAME, /* specialfile */ }, }, },
|
||||
.arg = { [0] = SCA_FILENAME_FROM_USER(specialfile), }, },
|
||||
{ .name = "swapon",
|
||||
.arg = { [0] = { .scnprintf = SCA_FILENAME, /* specialfile */ }, }, },
|
||||
.arg = { [0] = SCA_FILENAME_FROM_USER(specialfile), }, },
|
||||
{ .name = "symlinkat",
|
||||
.arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ }, }, },
|
||||
{ .name = "sync_file_range",
|
||||
@@ -1169,11 +1387,11 @@ static const struct syscall_fmt syscall_fmts[] = {
|
||||
{ .name = "tkill",
|
||||
.arg = { [1] = { .scnprintf = SCA_SIGNUM, /* sig */ }, }, },
|
||||
{ .name = "umount2", .alias = "umount",
|
||||
.arg = { [0] = { .scnprintf = SCA_FILENAME, /* name */ }, }, },
|
||||
.arg = { [0] = SCA_FILENAME_FROM_USER(name), }, },
|
||||
{ .name = "uname", .alias = "newuname", },
|
||||
{ .name = "unlinkat",
|
||||
.arg = { [0] = { .scnprintf = SCA_FDAT, /* dfd */ },
|
||||
[1] = { .scnprintf = SCA_FILENAME, /* pathname */ },
|
||||
[1] = SCA_FILENAME_FROM_USER(pathname),
|
||||
[2] = { .scnprintf = SCA_FS_AT_FLAGS, /* flags */ }, }, },
|
||||
{ .name = "utimensat",
|
||||
.arg = { [0] = { .scnprintf = SCA_FDAT, /* dirfd */ }, }, },
|
||||
@@ -1181,6 +1399,8 @@ static const struct syscall_fmt syscall_fmts[] = {
|
||||
.arg = { [2] = { .scnprintf = SCA_WAITID_OPTIONS, /* options */ }, }, },
|
||||
{ .name = "waitid", .errpid = true,
|
||||
.arg = { [3] = { .scnprintf = SCA_WAITID_OPTIONS, /* options */ }, }, },
|
||||
{ .name = "write", .errpid = true,
|
||||
.arg = { [1] = { .scnprintf = SCA_BUF /* buf */, .from_user = true, }, }, },
|
||||
};
|
||||
|
||||
static int syscall_fmt__cmp(const void *name, const void *fmtp)
|
||||
@@ -1238,6 +1458,7 @@ struct syscall {
|
||||
bool is_exit;
|
||||
bool is_open;
|
||||
bool nonexistent;
|
||||
bool use_btf;
|
||||
struct tep_format_field *args;
|
||||
const char *name;
|
||||
const struct syscall_fmt *fmt;
|
||||
@@ -1551,6 +1772,32 @@ static size_t syscall_arg__scnprintf_filename(char *bf, size_t size,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define MAX_CONTROL_CHAR 31
|
||||
#define MAX_ASCII 127
|
||||
|
||||
static size_t syscall_arg__scnprintf_buf(char *bf, size_t size, struct syscall_arg *arg)
|
||||
{
|
||||
struct augmented_arg *augmented_arg = arg->augmented.args;
|
||||
unsigned char *orig = (unsigned char *)augmented_arg->value;
|
||||
size_t printed = 0;
|
||||
int consumed;
|
||||
|
||||
if (augmented_arg == NULL)
|
||||
return 0;
|
||||
|
||||
for (int j = 0; j < augmented_arg->size; ++j) {
|
||||
bool control_char = orig[j] <= MAX_CONTROL_CHAR || orig[j] >= MAX_ASCII;
|
||||
/* print control characters (0~31 and 127), and non-ascii characters in \(digits) */
|
||||
printed += scnprintf(bf + printed, size - printed, control_char ? "\\%d" : "%c", (int)orig[j]);
|
||||
}
|
||||
|
||||
consumed = sizeof(*augmented_arg) + augmented_arg->size;
|
||||
arg->augmented.args = ((void *)arg->augmented.args) + consumed;
|
||||
arg->augmented.size -= consumed;
|
||||
|
||||
return printed;
|
||||
}
|
||||
|
||||
static bool trace__filter_duration(struct trace *trace, double t)
|
||||
{
|
||||
return t < (trace->duration_filter * NSEC_PER_MSEC);
|
||||
@@ -1637,7 +1884,7 @@ static int trace__process_event(struct trace *trace, struct machine *machine,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int trace__tool_process(struct perf_tool *tool,
|
||||
static int trace__tool_process(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct machine *machine)
|
||||
@@ -1744,7 +1991,8 @@ static const struct syscall_arg_fmt *syscall_arg_fmt__find_by_name(const char *n
|
||||
}
|
||||
|
||||
static struct tep_format_field *
|
||||
syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field *field)
|
||||
syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field *field,
|
||||
bool *use_btf)
|
||||
{
|
||||
struct tep_format_field *last_field = NULL;
|
||||
int len;
|
||||
@@ -1757,11 +2005,15 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field
|
||||
|
||||
len = strlen(field->name);
|
||||
|
||||
// As far as heuristics (or intention) goes this seems to hold true, and makes sense!
|
||||
if ((field->flags & TEP_FIELD_IS_POINTER) && strstarts(field->type, "const "))
|
||||
arg->from_user = true;
|
||||
|
||||
if (strcmp(field->type, "const char *") == 0 &&
|
||||
((len >= 4 && strcmp(field->name + len - 4, "name") == 0) ||
|
||||
strstr(field->name, "path") != NULL))
|
||||
strstr(field->name, "path") != NULL)) {
|
||||
arg->scnprintf = SCA_FILENAME;
|
||||
else if ((field->flags & TEP_FIELD_IS_POINTER) || strstr(field->name, "addr"))
|
||||
} else if ((field->flags & TEP_FIELD_IS_POINTER) || strstr(field->name, "addr"))
|
||||
arg->scnprintf = SCA_PTR;
|
||||
else if (strcmp(field->type, "pid_t") == 0)
|
||||
arg->scnprintf = SCA_PID;
|
||||
@@ -1782,6 +2034,9 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field
|
||||
* 7 unsigned long
|
||||
*/
|
||||
arg->scnprintf = SCA_FD;
|
||||
} else if (strstr(field->type, "enum") && use_btf != NULL) {
|
||||
*use_btf = true;
|
||||
arg->strtoul = STUL_BTF_TYPE;
|
||||
} else {
|
||||
const struct syscall_arg_fmt *fmt =
|
||||
syscall_arg_fmt__find_by_name(field->name);
|
||||
@@ -1798,7 +2053,8 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field
|
||||
|
||||
static int syscall__set_arg_fmts(struct syscall *sc)
|
||||
{
|
||||
struct tep_format_field *last_field = syscall_arg_fmt__init_array(sc->arg_fmt, sc->args);
|
||||
struct tep_format_field *last_field = syscall_arg_fmt__init_array(sc->arg_fmt, sc->args,
|
||||
&sc->use_btf);
|
||||
|
||||
if (last_field)
|
||||
sc->args_size = last_field->offset + last_field->size;
|
||||
@@ -1811,6 +2067,7 @@ static int trace__read_syscall_info(struct trace *trace, int id)
|
||||
char tp_name[128];
|
||||
struct syscall *sc;
|
||||
const char *name = syscalltbl__name(trace->sctbl, id);
|
||||
int err;
|
||||
|
||||
#ifdef HAVE_SYSCALL_TABLE_SUPPORT
|
||||
if (trace->syscalls.table == NULL) {
|
||||
@@ -1883,15 +2140,21 @@ static int trace__read_syscall_info(struct trace *trace, int id)
|
||||
sc->is_exit = !strcmp(name, "exit_group") || !strcmp(name, "exit");
|
||||
sc->is_open = !strcmp(name, "open") || !strcmp(name, "openat");
|
||||
|
||||
return syscall__set_arg_fmts(sc);
|
||||
err = syscall__set_arg_fmts(sc);
|
||||
|
||||
/* after calling syscall__set_arg_fmts() we'll know whether use_btf is true */
|
||||
if (sc->use_btf)
|
||||
trace__load_vmlinux_btf(trace);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int evsel__init_tp_arg_scnprintf(struct evsel *evsel)
|
||||
static int evsel__init_tp_arg_scnprintf(struct evsel *evsel, bool *use_btf)
|
||||
{
|
||||
struct syscall_arg_fmt *fmt = evsel__syscall_arg_fmt(evsel);
|
||||
|
||||
if (fmt != NULL) {
|
||||
syscall_arg_fmt__init_array(fmt, evsel->tp_format->format.fields);
|
||||
syscall_arg_fmt__init_array(fmt, evsel->tp_format->format.fields, use_btf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2050,7 +2313,7 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
|
||||
unsigned char *args, void *augmented_args, int augmented_args_size,
|
||||
struct trace *trace, struct thread *thread)
|
||||
{
|
||||
size_t printed = 0;
|
||||
size_t printed = 0, btf_printed;
|
||||
unsigned long val;
|
||||
u8 bit = 1;
|
||||
struct syscall_arg arg = {
|
||||
@@ -2066,6 +2329,7 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
|
||||
.show_string_prefix = trace->show_string_prefix,
|
||||
};
|
||||
struct thread_trace *ttrace = thread__priv(thread);
|
||||
void *default_scnprintf;
|
||||
|
||||
/*
|
||||
* Things like fcntl will set this in its 'cmd' formatter to pick the
|
||||
@@ -2093,9 +2357,13 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
|
||||
/*
|
||||
* Suppress this argument if its value is zero and show_zero
|
||||
* property isn't set.
|
||||
*
|
||||
* If it has a BTF type, then override the zero suppression knob
|
||||
* as the common case is for zero in an enum to have an associated entry.
|
||||
*/
|
||||
if (val == 0 && !trace->show_zeros &&
|
||||
!(sc->arg_fmt && sc->arg_fmt[arg.idx].show_zero))
|
||||
!(sc->arg_fmt && sc->arg_fmt[arg.idx].show_zero) &&
|
||||
!(sc->arg_fmt && sc->arg_fmt[arg.idx].strtoul == STUL_BTF_TYPE))
|
||||
continue;
|
||||
|
||||
printed += scnprintf(bf + printed, size - printed, "%s", printed ? ", " : "");
|
||||
@@ -2103,6 +2371,17 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size,
|
||||
if (trace->show_arg_names)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s: ", field->name);
|
||||
|
||||
default_scnprintf = sc->arg_fmt[arg.idx].scnprintf;
|
||||
|
||||
if (trace->force_btf || default_scnprintf == NULL || default_scnprintf == SCA_PTR) {
|
||||
btf_printed = trace__btf_scnprintf(trace, &arg, bf + printed,
|
||||
size - printed, val, field->type);
|
||||
if (btf_printed) {
|
||||
printed += btf_printed;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
printed += syscall_arg_fmt__scnprintf_val(&sc->arg_fmt[arg.idx],
|
||||
bf + printed, size - printed, &arg, val);
|
||||
}
|
||||
@@ -2749,7 +3028,7 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct evsel *evsel,
|
||||
size_t size = sizeof(bf);
|
||||
struct tep_format_field *field = evsel->tp_format->format.fields;
|
||||
struct syscall_arg_fmt *arg = __evsel__syscall_arg_fmt(evsel);
|
||||
size_t printed = 0;
|
||||
size_t printed = 0, btf_printed;
|
||||
unsigned long val;
|
||||
u8 bit = 1;
|
||||
struct syscall_arg syscall_arg = {
|
||||
@@ -2791,7 +3070,7 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct evsel *evsel,
|
||||
val = syscall_arg_fmt__mask_val(arg, &syscall_arg, val);
|
||||
|
||||
/* Suppress this argument if its value is zero and show_zero property isn't set. */
|
||||
if (val == 0 && !trace->show_zeros && !arg->show_zero)
|
||||
if (val == 0 && !trace->show_zeros && !arg->show_zero && arg->strtoul != STUL_BTF_TYPE)
|
||||
continue;
|
||||
|
||||
printed += scnprintf(bf + printed, size - printed, "%s", printed ? ", " : "");
|
||||
@@ -2799,6 +3078,12 @@ static size_t trace__fprintf_tp_fields(struct trace *trace, struct evsel *evsel,
|
||||
if (trace->show_arg_names)
|
||||
printed += scnprintf(bf + printed, size - printed, "%s: ", field->name);
|
||||
|
||||
btf_printed = trace__btf_scnprintf(trace, &syscall_arg, bf + printed, size - printed, val, field->type);
|
||||
if (btf_printed) {
|
||||
printed += btf_printed;
|
||||
continue;
|
||||
}
|
||||
|
||||
printed += syscall_arg_fmt__scnprintf_val(arg, bf + printed, size - printed, &syscall_arg, val);
|
||||
}
|
||||
|
||||
@@ -3009,7 +3294,7 @@ static void trace__set_base_time(struct trace *trace,
|
||||
trace->base_time = sample->time;
|
||||
}
|
||||
|
||||
static int trace__process_sample(struct perf_tool *tool,
|
||||
static int trace__process_sample(const struct perf_tool *tool,
|
||||
union perf_event *event,
|
||||
struct perf_sample *sample,
|
||||
struct evsel *evsel,
|
||||
@@ -3276,6 +3561,23 @@ out_enomem:
|
||||
}
|
||||
|
||||
#ifdef HAVE_BPF_SKEL
|
||||
static int syscall_arg_fmt__cache_btf_struct(struct syscall_arg_fmt *arg_fmt, struct btf *btf, char *type)
|
||||
{
|
||||
int id;
|
||||
|
||||
if (arg_fmt->type != NULL)
|
||||
return -1;
|
||||
|
||||
id = btf__find_by_name(btf, type);
|
||||
if (id < 0)
|
||||
return -1;
|
||||
|
||||
arg_fmt->type = btf__type_by_id(btf, id);
|
||||
arg_fmt->type_id = id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bpf_program *trace__find_bpf_program_by_title(struct trace *trace, const char *name)
|
||||
{
|
||||
struct bpf_program *pos, *prog = NULL;
|
||||
@@ -3351,6 +3653,91 @@ static int trace__bpf_prog_sys_exit_fd(struct trace *trace, int id)
|
||||
return sc ? bpf_program__fd(sc->bpf_prog.sys_exit) : bpf_program__fd(trace->skel->progs.syscall_unaugmented);
|
||||
}
|
||||
|
||||
static int trace__bpf_sys_enter_beauty_map(struct trace *trace, int key, unsigned int *beauty_array)
|
||||
{
|
||||
struct tep_format_field *field;
|
||||
struct syscall *sc = trace__syscall_info(trace, NULL, key);
|
||||
const struct btf_type *bt;
|
||||
char *struct_offset, *tmp, name[32];
|
||||
bool can_augment = false;
|
||||
int i, cnt;
|
||||
|
||||
if (sc == NULL)
|
||||
return -1;
|
||||
|
||||
trace__load_vmlinux_btf(trace);
|
||||
if (trace->btf == NULL)
|
||||
return -1;
|
||||
|
||||
for (i = 0, field = sc->args; field; ++i, field = field->next) {
|
||||
// XXX We're only collecting pointer payloads _from_ user space
|
||||
if (!sc->arg_fmt[i].from_user)
|
||||
continue;
|
||||
|
||||
struct_offset = strstr(field->type, "struct ");
|
||||
if (struct_offset == NULL)
|
||||
struct_offset = strstr(field->type, "union ");
|
||||
else
|
||||
struct_offset++; // "union" is shorter
|
||||
|
||||
if (field->flags & TEP_FIELD_IS_POINTER && struct_offset) { /* struct or union (think BPF's attr arg) */
|
||||
struct_offset += 6;
|
||||
|
||||
/* for 'struct foo *', we only want 'foo' */
|
||||
for (tmp = struct_offset, cnt = 0; *tmp != ' ' && *tmp != '\0'; ++tmp, ++cnt) {
|
||||
}
|
||||
|
||||
strncpy(name, struct_offset, cnt);
|
||||
name[cnt] = '\0';
|
||||
|
||||
/* cache struct's btf_type and type_id */
|
||||
if (syscall_arg_fmt__cache_btf_struct(&sc->arg_fmt[i], trace->btf, name))
|
||||
continue;
|
||||
|
||||
bt = sc->arg_fmt[i].type;
|
||||
beauty_array[i] = bt->size;
|
||||
can_augment = true;
|
||||
} else if (field->flags & TEP_FIELD_IS_POINTER && /* string */
|
||||
strcmp(field->type, "const char *") == 0 &&
|
||||
(strstr(field->name, "name") ||
|
||||
strstr(field->name, "path") ||
|
||||
strstr(field->name, "file") ||
|
||||
strstr(field->name, "root") ||
|
||||
strstr(field->name, "key") ||
|
||||
strstr(field->name, "special") ||
|
||||
strstr(field->name, "type") ||
|
||||
strstr(field->name, "description"))) {
|
||||
beauty_array[i] = 1;
|
||||
can_augment = true;
|
||||
} else if (field->flags & TEP_FIELD_IS_POINTER && /* buffer */
|
||||
strstr(field->type, "char *") &&
|
||||
(strstr(field->name, "buf") ||
|
||||
strstr(field->name, "val") ||
|
||||
strstr(field->name, "msg"))) {
|
||||
int j;
|
||||
struct tep_format_field *field_tmp;
|
||||
|
||||
/* find the size of the buffer that appears in pairs with buf */
|
||||
for (j = 0, field_tmp = sc->args; field_tmp; ++j, field_tmp = field_tmp->next) {
|
||||
if (!(field_tmp->flags & TEP_FIELD_IS_POINTER) && /* only integers */
|
||||
(strstr(field_tmp->name, "count") ||
|
||||
strstr(field_tmp->name, "siz") || /* size, bufsiz */
|
||||
(strstr(field_tmp->name, "len") && strcmp(field_tmp->name, "filename")))) {
|
||||
/* filename's got 'len' in it, we don't want that */
|
||||
beauty_array[i] = -(j + 1);
|
||||
can_augment = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (can_augment)
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static struct bpf_program *trace__find_usable_bpf_prog_entry(struct trace *trace, struct syscall *sc)
|
||||
{
|
||||
struct tep_format_field *field, *candidate_field;
|
||||
@@ -3455,7 +3842,9 @@ static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace)
|
||||
{
|
||||
int map_enter_fd = bpf_map__fd(trace->skel->maps.syscalls_sys_enter);
|
||||
int map_exit_fd = bpf_map__fd(trace->skel->maps.syscalls_sys_exit);
|
||||
int beauty_map_fd = bpf_map__fd(trace->skel->maps.beauty_map_enter);
|
||||
int err = 0;
|
||||
unsigned int beauty_array[6];
|
||||
|
||||
for (int i = 0; i < trace->sctbl->syscalls.nr_entries; ++i) {
|
||||
int prog_fd, key = syscalltbl__id_at_idx(trace->sctbl, i);
|
||||
@@ -3474,6 +3863,15 @@ static int trace__init_syscalls_bpf_prog_array_maps(struct trace *trace)
|
||||
err = bpf_map_update_elem(map_exit_fd, &key, &prog_fd, BPF_ANY);
|
||||
if (err)
|
||||
break;
|
||||
|
||||
/* use beauty_map to tell BPF how many bytes to collect, set beauty_map's value here */
|
||||
memset(beauty_array, 0, sizeof(beauty_array));
|
||||
err = trace__bpf_sys_enter_beauty_map(trace, key, (unsigned int *)beauty_array);
|
||||
if (err)
|
||||
continue;
|
||||
err = bpf_map_update_elem(beauty_map_fd, &key, beauty_array, BPF_ANY);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3680,7 +4078,8 @@ static int ordered_events__deliver_event(struct ordered_events *oe,
|
||||
return __trace__deliver_event(trace, event->event);
|
||||
}
|
||||
|
||||
static struct syscall_arg_fmt *evsel__find_syscall_arg_fmt_by_name(struct evsel *evsel, char *arg)
|
||||
static struct syscall_arg_fmt *evsel__find_syscall_arg_fmt_by_name(struct evsel *evsel, char *arg,
|
||||
char **type)
|
||||
{
|
||||
struct tep_format_field *field;
|
||||
struct syscall_arg_fmt *fmt = __evsel__syscall_arg_fmt(evsel);
|
||||
@@ -3689,13 +4088,15 @@ static struct syscall_arg_fmt *evsel__find_syscall_arg_fmt_by_name(struct evsel
|
||||
return NULL;
|
||||
|
||||
for (field = evsel->tp_format->format.fields; field; field = field->next, ++fmt)
|
||||
if (strcmp(field->name, arg) == 0)
|
||||
if (strcmp(field->name, arg) == 0) {
|
||||
*type = field->type;
|
||||
return fmt;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel *evsel)
|
||||
static int trace__expand_filter(struct trace *trace, struct evsel *evsel)
|
||||
{
|
||||
char *tok, *left = evsel->filter, *new_filter = evsel->filter;
|
||||
|
||||
@@ -3728,14 +4129,14 @@ static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel
|
||||
struct syscall_arg_fmt *fmt;
|
||||
int left_size = tok - left,
|
||||
right_size = right_end - right;
|
||||
char arg[128];
|
||||
char arg[128], *type;
|
||||
|
||||
while (isspace(left[left_size - 1]))
|
||||
--left_size;
|
||||
|
||||
scnprintf(arg, sizeof(arg), "%.*s", left_size, left);
|
||||
|
||||
fmt = evsel__find_syscall_arg_fmt_by_name(evsel, arg);
|
||||
fmt = evsel__find_syscall_arg_fmt_by_name(evsel, arg, &type);
|
||||
if (fmt == NULL) {
|
||||
pr_err("\"%s\" not found in \"%s\", can't set filter \"%s\"\n",
|
||||
arg, evsel->name, evsel->filter);
|
||||
@@ -3748,6 +4149,9 @@ static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel
|
||||
if (fmt->strtoul) {
|
||||
u64 val;
|
||||
struct syscall_arg syscall_arg = {
|
||||
.trace = trace,
|
||||
.fmt = fmt,
|
||||
.type_name = type,
|
||||
.parm = fmt->parm,
|
||||
};
|
||||
|
||||
@@ -3959,7 +4363,7 @@ static int trace__run(struct trace *trace, int argc, const char **argv)
|
||||
err = trace__expand_filters(trace, &evsel);
|
||||
if (err)
|
||||
goto out_delete_evlist;
|
||||
err = evlist__apply_filters(evlist, &evsel);
|
||||
err = evlist__apply_filters(evlist, &evsel, &trace->opts.target);
|
||||
if (err < 0)
|
||||
goto out_error_apply_filters;
|
||||
|
||||
@@ -4451,7 +4855,7 @@ static void evsel__set_syscall_arg_fmt(struct evsel *evsel, const char *name)
|
||||
}
|
||||
}
|
||||
|
||||
static int evlist__set_syscall_tp_fields(struct evlist *evlist)
|
||||
static int evlist__set_syscall_tp_fields(struct evlist *evlist, bool *use_btf)
|
||||
{
|
||||
struct evsel *evsel;
|
||||
|
||||
@@ -4460,7 +4864,7 @@ static int evlist__set_syscall_tp_fields(struct evlist *evlist)
|
||||
continue;
|
||||
|
||||
if (strcmp(evsel->tp_format->system, "syscalls")) {
|
||||
evsel__init_tp_arg_scnprintf(evsel);
|
||||
evsel__init_tp_arg_scnprintf(evsel, use_btf);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -4781,6 +5185,8 @@ int cmd_trace(int argc, const char **argv)
|
||||
OPT_INTEGER('D', "delay", &trace.opts.target.initial_delay,
|
||||
"ms to wait before starting measurement after program "
|
||||
"start"),
|
||||
OPT_BOOLEAN(0, "force-btf", &trace.force_btf, "Prefer btf_dump general pretty printer"
|
||||
"to customized ones"),
|
||||
OPTS_EVSWITCH(&trace.evswitch),
|
||||
OPT_END()
|
||||
};
|
||||
@@ -4938,11 +5344,16 @@ skip_augmentation:
|
||||
}
|
||||
|
||||
if (trace.evlist->core.nr_entries > 0) {
|
||||
bool use_btf = false;
|
||||
|
||||
evlist__set_default_evsel_handler(trace.evlist, trace__event_handler);
|
||||
if (evlist__set_syscall_tp_fields(trace.evlist)) {
|
||||
if (evlist__set_syscall_tp_fields(trace.evlist, &use_btf)) {
|
||||
perror("failed to set syscalls:* tracepoint fields");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (use_btf)
|
||||
trace__load_vmlinux_btf(&trace);
|
||||
}
|
||||
|
||||
if (trace.sort_events) {
|
||||
|
||||
@@ -46,45 +46,18 @@ static void status_print(const char *name, const char *macro,
|
||||
printf(" # %s\n", macro);
|
||||
}
|
||||
|
||||
#define STATUS(__d, __m) \
|
||||
do { \
|
||||
if (IS_BUILTIN(__d)) \
|
||||
status_print(#__m, #__d, "on"); \
|
||||
else \
|
||||
status_print(#__m, #__d, "OFF"); \
|
||||
#define STATUS(feature) \
|
||||
do { \
|
||||
if (feature.is_builtin) \
|
||||
status_print(feature.name, feature.macro, "on"); \
|
||||
else \
|
||||
status_print(feature.name, feature.macro, "OFF"); \
|
||||
} while (0)
|
||||
|
||||
static void library_status(void)
|
||||
{
|
||||
STATUS(HAVE_DWARF_SUPPORT, dwarf);
|
||||
STATUS(HAVE_DWARF_GETLOCATIONS_SUPPORT, dwarf_getlocations);
|
||||
#ifndef HAVE_SYSCALL_TABLE_SUPPORT
|
||||
STATUS(HAVE_LIBAUDIT_SUPPORT, libaudit);
|
||||
#endif
|
||||
STATUS(HAVE_SYSCALL_TABLE_SUPPORT, syscall_table);
|
||||
STATUS(HAVE_LIBBFD_SUPPORT, libbfd);
|
||||
STATUS(HAVE_DEBUGINFOD_SUPPORT, debuginfod);
|
||||
STATUS(HAVE_LIBELF_SUPPORT, libelf);
|
||||
STATUS(HAVE_LIBNUMA_SUPPORT, libnuma);
|
||||
STATUS(HAVE_LIBNUMA_SUPPORT, numa_num_possible_cpus);
|
||||
STATUS(HAVE_LIBPERL_SUPPORT, libperl);
|
||||
STATUS(HAVE_LIBPYTHON_SUPPORT, libpython);
|
||||
STATUS(HAVE_SLANG_SUPPORT, libslang);
|
||||
STATUS(HAVE_LIBCRYPTO_SUPPORT, libcrypto);
|
||||
STATUS(HAVE_LIBUNWIND_SUPPORT, libunwind);
|
||||
STATUS(HAVE_DWARF_SUPPORT, libdw-dwarf-unwind);
|
||||
STATUS(HAVE_LIBCAPSTONE_SUPPORT, libcapstone);
|
||||
STATUS(HAVE_ZLIB_SUPPORT, zlib);
|
||||
STATUS(HAVE_LZMA_SUPPORT, lzma);
|
||||
STATUS(HAVE_AUXTRACE_SUPPORT, get_cpuid);
|
||||
STATUS(HAVE_LIBBPF_SUPPORT, bpf);
|
||||
STATUS(HAVE_AIO_SUPPORT, aio);
|
||||
STATUS(HAVE_ZSTD_SUPPORT, zstd);
|
||||
STATUS(HAVE_LIBPFM, libpfm4);
|
||||
STATUS(HAVE_LIBTRACEEVENT, libtraceevent);
|
||||
STATUS(HAVE_BPF_SKEL, bpf_skeletons);
|
||||
STATUS(HAVE_DWARF_UNWIND_SUPPORT, dwarf-unwind-support);
|
||||
STATUS(HAVE_CSTRACE_SUPPORT, libopencsd);
|
||||
for (int i = 0; supported_features[i].name; ++i)
|
||||
STATUS(supported_features[i]);
|
||||
}
|
||||
|
||||
int cmd_version(int argc, const char **argv)
|
||||
|
||||
@@ -2,6 +2,22 @@
|
||||
#ifndef BUILTIN_H
|
||||
#define BUILTIN_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <tools/config.h>
|
||||
|
||||
struct feature_status {
|
||||
const char *name;
|
||||
const char *macro;
|
||||
int is_builtin;
|
||||
};
|
||||
|
||||
#define FEATURE_STATUS(name_, macro_) { \
|
||||
.name = name_, \
|
||||
.macro = #macro_, \
|
||||
.is_builtin = IS_BUILTIN(macro_) }
|
||||
|
||||
extern struct feature_status supported_features[];
|
||||
struct cmdnames;
|
||||
|
||||
void list_common_cmds_help(void);
|
||||
@@ -11,6 +27,7 @@ int cmd_annotate(int argc, const char **argv);
|
||||
int cmd_bench(int argc, const char **argv);
|
||||
int cmd_buildid_cache(int argc, const char **argv);
|
||||
int cmd_buildid_list(int argc, const char **argv);
|
||||
int cmd_check(int argc, const char **argv);
|
||||
int cmd_config(int argc, const char **argv);
|
||||
int cmd_c2c(int argc, const char **argv);
|
||||
int cmd_diff(int argc, const char **argv);
|
||||
|
||||
@@ -172,6 +172,7 @@ check lib/ctype.c '-I "^EXPORT_SYMBOL" -I "^#include <linux/export.h>" -B
|
||||
check lib/list_sort.c '-I "^#include <linux/bug.h>"'
|
||||
|
||||
# diff non-symmetric files
|
||||
check_2 tools/perf/arch/x86/entry/syscalls/syscall_32.tbl arch/x86/entry/syscalls/syscall_32.tbl
|
||||
check_2 tools/perf/arch/x86/entry/syscalls/syscall_64.tbl arch/x86/entry/syscalls/syscall_64.tbl
|
||||
check_2 tools/perf/arch/powerpc/entry/syscalls/syscall.tbl arch/powerpc/kernel/syscalls/syscall.tbl
|
||||
check_2 tools/perf/arch/s390/entry/syscalls/syscall.tbl arch/s390/kernel/syscalls/syscall.tbl
|
||||
|
||||
@@ -52,6 +52,7 @@ static struct cmd_struct commands[] = {
|
||||
{ "archive", NULL, 0 },
|
||||
{ "buildid-cache", cmd_buildid_cache, 0 },
|
||||
{ "buildid-list", cmd_buildid_list, 0 },
|
||||
{ "check", cmd_check, 0 },
|
||||
{ "config", cmd_config, 0 },
|
||||
{ "c2c", cmd_c2c, 0 },
|
||||
{ "diff", cmd_diff, 0 },
|
||||
|
||||
@@ -11,6 +11,8 @@ METRIC_TEST_PY = pmu-events/metric_test.py
|
||||
EMPTY_PMU_EVENTS_C = pmu-events/empty-pmu-events.c
|
||||
PMU_EVENTS_C = $(OUTPUT)pmu-events/pmu-events.c
|
||||
METRIC_TEST_LOG = $(OUTPUT)pmu-events/metric_test.log
|
||||
TEST_EMPTY_PMU_EVENTS_C = $(OUTPUT)pmu-events/test-empty-pmu-events.c
|
||||
EMPTY_PMU_EVENTS_TEST_LOG = $(OUTPUT)pmu-events/empty-pmu-events.log
|
||||
|
||||
ifeq ($(JEVENTS_ARCH),)
|
||||
JEVENTS_ARCH=$(SRCARCH)
|
||||
@@ -31,7 +33,15 @@ $(METRIC_TEST_LOG): $(METRIC_TEST_PY) $(METRIC_PY)
|
||||
$(call rule_mkdir)
|
||||
$(Q)$(call echo-cmd,test)$(PYTHON) $< 2> $@ || (cat $@ && false)
|
||||
|
||||
$(PMU_EVENTS_C): $(JSON) $(JSON_TEST) $(JEVENTS_PY) $(METRIC_PY) $(METRIC_TEST_LOG)
|
||||
$(TEST_EMPTY_PMU_EVENTS_C): $(JSON) $(JSON_TEST) $(JEVENTS_PY) $(METRIC_PY) $(METRIC_TEST_LOG)
|
||||
$(call rule_mkdir)
|
||||
$(Q)$(call echo-cmd,gen)$(PYTHON) $(JEVENTS_PY) none none pmu-events/arch $@
|
||||
|
||||
$(EMPTY_PMU_EVENTS_TEST_LOG): $(EMPTY_PMU_EVENTS_C) $(TEST_EMPTY_PMU_EVENTS_C)
|
||||
$(call rule_mkdir)
|
||||
$(Q)$(call echo-cmd,test)diff -u $^ 2> $@ || (cat $@ && false)
|
||||
|
||||
$(PMU_EVENTS_C): $(JSON) $(JSON_TEST) $(JEVENTS_PY) $(METRIC_PY) $(METRIC_TEST_LOG) $(EMPTY_PMU_EVENTS_TEST_LOG)
|
||||
$(call rule_mkdir)
|
||||
$(Q)$(call echo-cmd,gen)$(PYTHON) $(JEVENTS_PY) $(JEVENTS_ARCH) $(JEVENTS_MODEL) pmu-events/arch $@
|
||||
endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user