From dd4900d94f2f7bf3ccfdd4479a3d159e93cca2de Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 12 Jun 2024 19:19:35 -0400 Subject: [PATCH 001/347] ring-buffer: Allow mapped field to be set without mapping In preparation for having the ring buffer mapped to a dedicated location, which will have the same restrictions as user space memory mapped buffers, allow it to use the "mapped" field of the ring_buffer_per_cpu structure without having the user space meta page mapping. When this starts using the mapped field, it will need to handle adding a user space mapping (and removing it) from a ring buffer that is using a dedicated memory range. Link: https://lkml.kernel.org/r/20240612232025.190908567@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Daniel Bristot de Oliveira Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Youssef Esmat Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Signed-off-by: Steven Rostedt (Google) --- kernel/trace/ring_buffer.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 28853966aa9a..a240bdc0f2d8 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -491,6 +491,7 @@ struct ring_buffer_per_cpu { unsigned long pages_removed; unsigned int mapped; + unsigned int user_mapped; /* user space mapping */ struct mutex mapping_lock; unsigned long *subbuf_ids; /* ID to subbuf VA */ struct trace_buffer_meta *meta_page; @@ -5224,6 +5225,9 @@ static void rb_update_meta_page(struct ring_buffer_per_cpu *cpu_buffer) { struct trace_buffer_meta *meta = cpu_buffer->meta_page; + if (!meta) + return; + meta->reader.read = cpu_buffer->reader_page->read; meta->reader.id = cpu_buffer->reader_page->id; meta->reader.lost_events = cpu_buffer->lost_events; @@ -5280,7 +5284,7 @@ rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer) cpu_buffer->lost_events = 0; cpu_buffer->last_overrun = 0; - if (cpu_buffer->mapped) + if (cpu_buffer->user_mapped) rb_update_meta_page(cpu_buffer); rb_head_page_activate(cpu_buffer); @@ -6167,7 +6171,7 @@ rb_get_mapped_buffer(struct trace_buffer *buffer, int cpu) mutex_lock(&cpu_buffer->mapping_lock); - if (!cpu_buffer->mapped) { + if (!cpu_buffer->user_mapped) { mutex_unlock(&cpu_buffer->mapping_lock); return ERR_PTR(-ENODEV); } @@ -6191,19 +6195,26 @@ static int __rb_inc_dec_mapped(struct ring_buffer_per_cpu *cpu_buffer, lockdep_assert_held(&cpu_buffer->mapping_lock); + /* mapped is always greater or equal to user_mapped */ + if (WARN_ON(cpu_buffer->mapped < cpu_buffer->user_mapped)) + return -EINVAL; + if (inc && cpu_buffer->mapped == UINT_MAX) return -EBUSY; - if (WARN_ON(!inc && cpu_buffer->mapped == 0)) + if (WARN_ON(!inc && cpu_buffer->user_mapped == 0)) return -EINVAL; mutex_lock(&cpu_buffer->buffer->mutex); raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags); - if (inc) + if (inc) { + cpu_buffer->user_mapped++; cpu_buffer->mapped++; - else + } else { + cpu_buffer->user_mapped--; cpu_buffer->mapped--; + } raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags); mutex_unlock(&cpu_buffer->buffer->mutex); @@ -6328,7 +6339,7 @@ int ring_buffer_map(struct trace_buffer *buffer, int cpu, mutex_lock(&cpu_buffer->mapping_lock); - if (cpu_buffer->mapped) { + if (cpu_buffer->user_mapped) { err = __rb_map_vma(cpu_buffer, vma); if (!err) err = __rb_inc_dec_mapped(cpu_buffer, true); @@ -6359,12 +6370,15 @@ int ring_buffer_map(struct trace_buffer *buffer, int cpu, */ raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags); rb_setup_ids_meta_page(cpu_buffer, subbuf_ids); + raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags); err = __rb_map_vma(cpu_buffer, vma); if (!err) { raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags); - cpu_buffer->mapped = 1; + /* This is the first time it is mapped by user */ + cpu_buffer->mapped++; + cpu_buffer->user_mapped = 1; raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags); } else { kfree(cpu_buffer->subbuf_ids); @@ -6392,10 +6406,10 @@ int ring_buffer_unmap(struct trace_buffer *buffer, int cpu) mutex_lock(&cpu_buffer->mapping_lock); - if (!cpu_buffer->mapped) { + if (!cpu_buffer->user_mapped) { err = -ENODEV; goto out; - } else if (cpu_buffer->mapped > 1) { + } else if (cpu_buffer->user_mapped > 1) { __rb_inc_dec_mapped(cpu_buffer, false); goto out; } @@ -6403,7 +6417,10 @@ int ring_buffer_unmap(struct trace_buffer *buffer, int cpu) mutex_lock(&buffer->mutex); raw_spin_lock_irqsave(&cpu_buffer->reader_lock, flags); - cpu_buffer->mapped = 0; + /* This is the last user space mapping */ + if (!WARN_ON_ONCE(cpu_buffer->mapped < cpu_buffer->user_mapped)) + cpu_buffer->mapped--; + cpu_buffer->user_mapped = 0; raw_spin_unlock_irqrestore(&cpu_buffer->reader_lock, flags); From be68d63a139bd4a0eae44d06234eaff8c07d207c Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 12 Jun 2024 19:19:36 -0400 Subject: [PATCH 002/347] ring-buffer: Add ring_buffer_alloc_range() In preparation to allowing the trace ring buffer to be allocated in a range of memory that is persistent across reboots, add ring_buffer_alloc_range(). It takes a contiguous range of memory and will split it up evenly for the per CPU ring buffers. If there's not enough memory to handle all CPUs with the minimum size, it will fail to allocate the ring buffer. Link: https://lkml.kernel.org/r/20240612232025.363998725@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Daniel Bristot de Oliveira Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Youssef Esmat Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Signed-off-by: Steven Rostedt (Google) --- include/linux/ring_buffer.h | 17 +++ kernel/trace/ring_buffer.c | 237 ++++++++++++++++++++++++++++++------ 2 files changed, 219 insertions(+), 35 deletions(-) diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h index 96d2140b471e..a50b0223b1d3 100644 --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h @@ -89,6 +89,11 @@ 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); + /* * 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 +105,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); diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index a240bdc0f2d8..6edd70e45807 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -42,6 +42,9 @@ static void update_pages_handler(struct work_struct *work); +struct ring_buffer_meta { +}; + /* * The ring buffer header is special. We must manually up keep it. */ @@ -342,7 +345,8 @@ struct buffer_page { local_t entries; /* entries on this page */ unsigned long real_end; /* real end of data */ unsigned order; /* order of the page */ - u32 id; /* ID for external mapping */ + u32 id:30; /* ID for external mapping */ + u32 range:1; /* Mapped via a range */ struct buffer_data_page *page; /* Actual data page */ }; @@ -373,7 +377,9 @@ static __always_inline unsigned int rb_page_commit(struct buffer_page *bpage) static void free_buffer_page(struct buffer_page *bpage) { - free_pages((unsigned long)bpage->page, bpage->order); + /* Range pages are not to be freed */ + if (!bpage->range) + free_pages((unsigned long)bpage->page, bpage->order); kfree(bpage); } @@ -524,6 +530,9 @@ struct trace_buffer { struct rb_irq_work irq_work; bool time_stamp_abs; + unsigned long range_addr_start; + unsigned long range_addr_end; + unsigned int subbuf_size; unsigned int subbuf_order; unsigned int max_data_size; @@ -1491,9 +1500,70 @@ static void rb_check_pages(struct ring_buffer_per_cpu *cpu_buffer) } } +/* + * Take an address, add the meta data size as well as the array of + * array subbuffer indexes, then align it to a subbuffer size. + * + * This is used to help find the next per cpu subbuffer within a mapped range. + */ +static unsigned long +rb_range_align_subbuf(unsigned long addr, int subbuf_size, int nr_subbufs) +{ + addr += sizeof(struct ring_buffer_meta) + + sizeof(int) * nr_subbufs; + return ALIGN(addr, subbuf_size); +} + +/* + * Return a specific sub-buffer for a given @cpu defined by @idx. + */ +static void *rb_range_buffer(struct trace_buffer *buffer, int cpu, int nr_pages, int idx) +{ + unsigned long ptr; + int subbuf_size = buffer->subbuf_size + BUF_PAGE_HDR_SIZE; + int nr_subbufs; + + /* Include the reader page */ + nr_subbufs = nr_pages + 1; + + /* + * The first chunk may not be subbuffer aligned, where as + * the rest of the chunks are. + */ + ptr = buffer->range_addr_start; + ptr = rb_range_align_subbuf(ptr, subbuf_size, nr_subbufs); + if (cpu) { + unsigned long p; + + ptr += subbuf_size * nr_subbufs; + + /* Save the beginning of this CPU chunk */ + p = ptr; + + ptr = rb_range_align_subbuf(ptr, subbuf_size, nr_subbufs); + + /* We can use multiplication to find chunks greater than 1 */ + if (cpu > 1) { + unsigned long size; + + ptr += subbuf_size * nr_subbufs; + + /* Now all chunks after this are the same size */ + size = ptr - p; + ptr += size * (cpu - 2); + + ptr = rb_range_align_subbuf(ptr, subbuf_size, nr_subbufs); + } + } + if (ptr + subbuf_size * nr_subbufs > buffer->range_addr_end) + return NULL; + return (void *)ptr; +} + static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, long nr_pages, struct list_head *pages) { + struct trace_buffer *buffer = cpu_buffer->buffer; struct buffer_page *bpage, *tmp; bool user_thread = current->mm != NULL; gfp_t mflags; @@ -1530,6 +1600,7 @@ static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, set_current_oom_origin(); for (i = 0; i < nr_pages; i++) { struct page *page; + int cpu = cpu_buffer->cpu; bpage = kzalloc_node(ALIGN(sizeof(*bpage), cache_line_size()), mflags, cpu_to_node(cpu_buffer->cpu)); @@ -1538,14 +1609,26 @@ static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, rb_check_bpage(cpu_buffer, bpage); - list_add(&bpage->list, pages); + /* + * Append the pages as for mapped buffers we want to keep + * the order + */ + list_add_tail(&bpage->list, pages); - page = alloc_pages_node(cpu_to_node(cpu_buffer->cpu), - mflags | __GFP_COMP | __GFP_ZERO, - cpu_buffer->buffer->subbuf_order); - if (!page) - goto free_pages; - bpage->page = page_address(page); + if (buffer->range_addr_start) { + /* A range was given. Use that for the buffer page */ + bpage->page = rb_range_buffer(buffer, cpu, nr_pages, i + 1); + if (!bpage->page) + goto free_pages; + bpage->range = 1; + } else { + page = alloc_pages_node(cpu_to_node(cpu_buffer->cpu), + mflags | __GFP_COMP | __GFP_ZERO, + cpu_buffer->buffer->subbuf_order); + if (!page) + goto free_pages; + bpage->page = page_address(page); + } bpage->order = cpu_buffer->buffer->subbuf_order; rb_init_page(bpage->page); @@ -1627,11 +1710,19 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) cpu_buffer->reader_page = bpage; - page = alloc_pages_node(cpu_to_node(cpu), GFP_KERNEL | __GFP_COMP | __GFP_ZERO, - cpu_buffer->buffer->subbuf_order); - if (!page) - goto fail_free_reader; - bpage->page = page_address(page); + if (buffer->range_addr_start) { + bpage->page = rb_range_buffer(buffer, cpu, nr_pages, 0); + if (!bpage->page) + goto fail_free_reader; + bpage->range = 1; + } else { + page = alloc_pages_node(cpu_to_node(cpu), + GFP_KERNEL | __GFP_COMP | __GFP_ZERO, + cpu_buffer->buffer->subbuf_order); + if (!page) + goto fail_free_reader; + bpage->page = page_address(page); + } rb_init_page(bpage->page); INIT_LIST_HEAD(&cpu_buffer->reader_page->list); @@ -1682,22 +1773,14 @@ static void rb_free_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer) kfree(cpu_buffer); } -/** - * __ring_buffer_alloc - allocate a new ring_buffer - * @size: the size in bytes per cpu that is needed. - * @flags: attributes to set for the ring buffer. - * @key: ring buffer reader_lock_key. - * - * Currently the only flag that is available is the RB_FL_OVERWRITE - * flag. This flag means that the buffer will overwrite old data - * when the buffer wraps. If this flag is not set, the buffer will - * drop data when the tail hits the head. - */ -struct trace_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags, - struct lock_class_key *key) +static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, + int order, unsigned long start, + unsigned long end, + struct lock_class_key *key) { struct trace_buffer *buffer; long nr_pages; + int subbuf_size; int bsize; int cpu; int ret; @@ -1711,14 +1794,13 @@ struct trace_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags, if (!zalloc_cpumask_var(&buffer->cpumask, GFP_KERNEL)) goto fail_free_buffer; - /* Default buffer page size - one system page */ - buffer->subbuf_order = 0; - buffer->subbuf_size = PAGE_SIZE - BUF_PAGE_HDR_SIZE; + buffer->subbuf_order = order; + subbuf_size = (PAGE_SIZE << order); + buffer->subbuf_size = subbuf_size - BUF_PAGE_HDR_SIZE; /* Max payload is buffer page size - header (8bytes) */ buffer->max_data_size = buffer->subbuf_size - (sizeof(u32) * 2); - nr_pages = DIV_ROUND_UP(size, buffer->subbuf_size); buffer->flags = flags; buffer->clock = trace_clock_local; buffer->reader_lock_key = key; @@ -1726,10 +1808,6 @@ struct trace_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags, init_irq_work(&buffer->irq_work.work, rb_wake_up_waiters); init_waitqueue_head(&buffer->irq_work.waiters); - /* need at least two pages */ - if (nr_pages < 2) - nr_pages = 2; - buffer->cpus = nr_cpu_ids; bsize = sizeof(void *) * nr_cpu_ids; @@ -1738,6 +1816,54 @@ struct trace_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags, if (!buffer->buffers) goto fail_free_cpumask; + /* If start/end are specified, then that overrides size */ + if (start && end) { + unsigned long ptr; + int n; + + size = end - start; + size = size / nr_cpu_ids; + + /* + * The number of sub-buffers (nr_pages) is determined by the + * total size allocated minus the meta data size. + * Then that is divided by the number of per CPU buffers + * needed, plus account for the integer array index that + * will be appended to the meta data. + */ + nr_pages = (size - sizeof(struct ring_buffer_meta)) / + (subbuf_size + sizeof(int)); + /* Need at least two pages plus the reader page */ + if (nr_pages < 3) + goto fail_free_buffers; + + again: + /* Make sure that the size fits aligned */ + for (n = 0, ptr = start; n < nr_cpu_ids; n++) { + ptr += sizeof(struct ring_buffer_meta) + + sizeof(int) * nr_pages; + ptr = ALIGN(ptr, subbuf_size); + ptr += subbuf_size * nr_pages; + } + if (ptr > end) { + if (nr_pages <= 3) + goto fail_free_buffers; + nr_pages--; + goto again; + } + + /* nr_pages should not count the reader page */ + nr_pages--; + buffer->range_addr_start = start; + buffer->range_addr_end = end; + } else { + + /* need at least two pages */ + nr_pages = DIV_ROUND_UP(size, buffer->subbuf_size); + if (nr_pages < 2) + nr_pages = 2; + } + cpu = raw_smp_processor_id(); cpumask_set_cpu(cpu, buffer->cpumask); buffer->buffers[cpu] = rb_allocate_cpu_buffer(buffer, nr_pages, cpu); @@ -1766,8 +1892,49 @@ struct trace_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags, kfree(buffer); return NULL; } + +/** + * __ring_buffer_alloc - allocate a new ring_buffer + * @size: the size in bytes per cpu that is needed. + * @flags: attributes to set for the ring buffer. + * @key: ring buffer reader_lock_key. + * + * Currently the only flag that is available is the RB_FL_OVERWRITE + * flag. This flag means that the buffer will overwrite old data + * when the buffer wraps. If this flag is not set, the buffer will + * drop data when the tail hits the head. + */ +struct trace_buffer *__ring_buffer_alloc(unsigned long size, unsigned flags, + struct lock_class_key *key) +{ + /* Default buffer page size - one system page */ + return alloc_buffer(size, flags, 0, 0, 0,key); + +} EXPORT_SYMBOL_GPL(__ring_buffer_alloc); +/** + * __ring_buffer_alloc_range - allocate a new ring_buffer from existing memory + * @size: the size in bytes per cpu that is needed. + * @flags: attributes to set for the ring buffer. + * @start: start of allocated range + * @range_size: size of allocated range + * @order: sub-buffer order + * @key: ring buffer reader_lock_key. + * + * Currently the only flag that is available is the RB_FL_OVERWRITE + * flag. This flag means that the buffer will overwrite old data + * when the buffer wraps. If this flag is not set, the buffer will + * drop data when the tail hits the head. + */ +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) +{ + return alloc_buffer(size, flags, order, start, start + range_size, key); +} + /** * ring_buffer_free - free a ring buffer. * @buffer: the buffer to free. From b14d032973d4e6c36e025e96ea3437de53d072d9 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 12 Jun 2024 19:19:37 -0400 Subject: [PATCH 003/347] ring-buffer: Add ring_buffer_meta data Populate the ring_buffer_meta array. It holds the pointer to the head_buffer (next to read), the commit_buffer (next to write) the size of the sub-buffers, number of sub-buffers and an array that keeps track of the order of the sub-buffers. This information will be stored in the persistent memory to help on reboot to reconstruct the ring buffer. Link: https://lkml.kernel.org/r/20240612232025.530733577@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Daniel Bristot de Oliveira Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Youssef Esmat Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Signed-off-by: Steven Rostedt (Google) --- kernel/trace/ring_buffer.c | 211 ++++++++++++++++++++++++++++++++----- 1 file changed, 185 insertions(+), 26 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 6edd70e45807..0e3ed7f1cc5d 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -43,6 +43,11 @@ static void update_pages_handler(struct work_struct *work); struct ring_buffer_meta { + unsigned long head_buffer; + unsigned long commit_buffer; + __u32 subbuf_size; + __u32 nr_subbufs; + int buffers[]; }; /* @@ -501,6 +506,7 @@ struct ring_buffer_per_cpu { struct mutex mapping_lock; unsigned long *subbuf_ids; /* ID to subbuf VA */ struct trace_buffer_meta *meta_page; + struct ring_buffer_meta *ring_meta; /* ring buffer pages to update, > 0 to add, < 0 to remove */ long nr_pages_to_update; @@ -1261,6 +1267,11 @@ static void rb_head_page_activate(struct ring_buffer_per_cpu *cpu_buffer) * Set the previous list pointer to have the HEAD flag. */ rb_set_list_to_head(head->list.prev); + + if (cpu_buffer->ring_meta) { + struct ring_buffer_meta *meta = cpu_buffer->ring_meta; + meta->head_buffer = (unsigned long)head->page; + } } static void rb_list_head_clear(struct list_head *list) @@ -1515,51 +1526,127 @@ rb_range_align_subbuf(unsigned long addr, int subbuf_size, int nr_subbufs) } /* - * Return a specific sub-buffer for a given @cpu defined by @idx. + * Return the ring_buffer_meta for a given @cpu. */ -static void *rb_range_buffer(struct trace_buffer *buffer, int cpu, int nr_pages, int idx) +static void *rb_range_meta(struct trace_buffer *buffer, int nr_pages, int cpu) { - unsigned long ptr; int subbuf_size = buffer->subbuf_size + BUF_PAGE_HDR_SIZE; + unsigned long ptr = buffer->range_addr_start; + struct ring_buffer_meta *meta; int nr_subbufs; - /* Include the reader page */ - nr_subbufs = nr_pages + 1; + if (!ptr) + return NULL; + + /* When nr_pages passed in is zero, the first meta has already been initialized */ + if (!nr_pages) { + meta = (struct ring_buffer_meta *)ptr; + nr_subbufs = meta->nr_subbufs; + } else { + meta = NULL; + /* Include the reader page */ + nr_subbufs = nr_pages + 1; + } /* * The first chunk may not be subbuffer aligned, where as * the rest of the chunks are. */ - ptr = buffer->range_addr_start; - ptr = rb_range_align_subbuf(ptr, subbuf_size, nr_subbufs); if (cpu) { - unsigned long p; - - ptr += subbuf_size * nr_subbufs; - - /* Save the beginning of this CPU chunk */ - p = ptr; - ptr = rb_range_align_subbuf(ptr, subbuf_size, nr_subbufs); + ptr += subbuf_size * nr_subbufs; /* We can use multiplication to find chunks greater than 1 */ if (cpu > 1) { unsigned long size; + unsigned long p; + /* Save the beginning of this CPU chunk */ + p = ptr; + ptr = rb_range_align_subbuf(ptr, subbuf_size, nr_subbufs); ptr += subbuf_size * nr_subbufs; /* Now all chunks after this are the same size */ size = ptr - p; ptr += size * (cpu - 2); - - ptr = rb_range_align_subbuf(ptr, subbuf_size, nr_subbufs); } } - if (ptr + subbuf_size * nr_subbufs > buffer->range_addr_end) - return NULL; return (void *)ptr; } +/* Return the start of subbufs given the meta pointer */ +static void *rb_subbufs_from_meta(struct ring_buffer_meta *meta) +{ + int subbuf_size = meta->subbuf_size; + unsigned long ptr; + + ptr = (unsigned long)meta; + ptr = rb_range_align_subbuf(ptr, subbuf_size, meta->nr_subbufs); + + return (void *)ptr; +} + +/* + * Return a specific sub-buffer for a given @cpu defined by @idx. + */ +static void *rb_range_buffer(struct ring_buffer_per_cpu *cpu_buffer, int idx) +{ + struct ring_buffer_meta *meta; + unsigned long ptr; + int subbuf_size; + + meta = rb_range_meta(cpu_buffer->buffer, 0, cpu_buffer->cpu); + if (!meta) + return NULL; + + if (WARN_ON_ONCE(idx >= meta->nr_subbufs)) + return NULL; + + subbuf_size = meta->subbuf_size; + + /* Map this buffer to the order that's in meta->buffers[] */ + idx = meta->buffers[idx]; + + ptr = (unsigned long)rb_subbufs_from_meta(meta); + + ptr += subbuf_size * idx; + if (ptr + subbuf_size > cpu_buffer->buffer->range_addr_end) + return NULL; + + return (void *)ptr; +} + +static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages) +{ + struct ring_buffer_meta *meta; + void *subbuf; + int cpu; + int i; + + for (cpu = 0; cpu < nr_cpu_ids; cpu++) { + meta = rb_range_meta(buffer, nr_pages, cpu); + + meta->nr_subbufs = nr_pages + 1; + meta->subbuf_size = PAGE_SIZE; + + subbuf = rb_subbufs_from_meta(meta); + + /* + * The buffers[] array holds the order of the sub-buffers + * that are after the meta data. The sub-buffers may + * be swapped out when read and inserted into a different + * location of the ring buffer. Although their addresses + * remain the same, the buffers[] array contains the + * index into the sub-buffers holding their actual order. + */ + for (i = 0; i < meta->nr_subbufs; i++) { + meta->buffers[i] = i; + rb_init_page(subbuf); + subbuf += meta->subbuf_size; + } + } +} + static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, long nr_pages, struct list_head *pages) { @@ -1600,7 +1687,6 @@ static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, set_current_oom_origin(); for (i = 0; i < nr_pages; i++) { struct page *page; - int cpu = cpu_buffer->cpu; bpage = kzalloc_node(ALIGN(sizeof(*bpage), cache_line_size()), mflags, cpu_to_node(cpu_buffer->cpu)); @@ -1617,10 +1703,11 @@ static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, if (buffer->range_addr_start) { /* A range was given. Use that for the buffer page */ - bpage->page = rb_range_buffer(buffer, cpu, nr_pages, i + 1); + bpage->page = rb_range_buffer(cpu_buffer, i + 1); if (!bpage->page) goto free_pages; bpage->range = 1; + bpage->id = i + 1; } else { page = alloc_pages_node(cpu_to_node(cpu_buffer->cpu), mflags | __GFP_COMP | __GFP_ZERO, @@ -1628,9 +1715,9 @@ static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, if (!page) goto free_pages; bpage->page = page_address(page); + rb_init_page(bpage->page); } bpage->order = cpu_buffer->buffer->subbuf_order; - rb_init_page(bpage->page); if (user_thread && fatal_signal_pending(current)) goto free_pages; @@ -1711,7 +1798,13 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) cpu_buffer->reader_page = bpage; if (buffer->range_addr_start) { - bpage->page = rb_range_buffer(buffer, cpu, nr_pages, 0); + /* + * Range mapped buffers have the same restrictions as memory + * mapped ones do. + */ + cpu_buffer->mapped = 1; + cpu_buffer->ring_meta = rb_range_meta(buffer, nr_pages, cpu); + bpage->page = rb_range_buffer(cpu_buffer, 0); if (!bpage->page) goto fail_free_reader; bpage->range = 1; @@ -1722,8 +1815,8 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) if (!page) goto fail_free_reader; bpage->page = page_address(page); + rb_init_page(bpage->page); } - rb_init_page(bpage->page); INIT_LIST_HEAD(&cpu_buffer->reader_page->list); INIT_LIST_HEAD(&cpu_buffer->new_pages); @@ -1737,6 +1830,10 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) cpu_buffer->tail_page = cpu_buffer->commit_page = cpu_buffer->head_page; rb_head_page_activate(cpu_buffer); + if (cpu_buffer->ring_meta) { + struct ring_buffer_meta *meta = cpu_buffer->ring_meta; + meta->commit_buffer = meta->head_buffer; + } return cpu_buffer; @@ -1856,6 +1953,8 @@ static struct trace_buffer *alloc_buffer(unsigned long size, unsigned flags, nr_pages--; buffer->range_addr_start = start; buffer->range_addr_end = end; + + rb_range_meta_init(buffer, nr_pages); } else { /* need at least two pages */ @@ -2544,6 +2643,52 @@ static void rb_inc_iter(struct ring_buffer_iter *iter) iter->next_event = 0; } +/* Return the index into the sub-buffers for a given sub-buffer */ +static int rb_meta_subbuf_idx(struct ring_buffer_meta *meta, void *subbuf) +{ + void *subbuf_array; + + subbuf_array = (void *)meta + sizeof(int) * meta->nr_subbufs; + subbuf_array = (void *)ALIGN((unsigned long)subbuf_array, meta->subbuf_size); + return (subbuf - subbuf_array) / meta->subbuf_size; +} + +static void rb_update_meta_head(struct ring_buffer_per_cpu *cpu_buffer, + struct buffer_page *next_page) +{ + struct ring_buffer_meta *meta = cpu_buffer->ring_meta; + unsigned long old_head = (unsigned long)next_page->page; + unsigned long new_head; + + rb_inc_page(&next_page); + new_head = (unsigned long)next_page->page; + + /* + * Only move it forward once, if something else came in and + * moved it forward, then we don't want to touch it. + */ + (void)cmpxchg(&meta->head_buffer, old_head, new_head); +} + +static void rb_update_meta_reader(struct ring_buffer_per_cpu *cpu_buffer, + struct buffer_page *reader) +{ + struct ring_buffer_meta *meta = cpu_buffer->ring_meta; + void *old_reader = cpu_buffer->reader_page->page; + void *new_reader = reader->page; + int id; + + id = reader->id; + cpu_buffer->reader_page->id = id; + reader->id = 0; + + meta->buffers[0] = rb_meta_subbuf_idx(meta, new_reader); + meta->buffers[id] = rb_meta_subbuf_idx(meta, old_reader); + + /* The head pointer is the one after the reader */ + rb_update_meta_head(cpu_buffer, reader); +} + /* * rb_handle_head_page - writer hit the head page * @@ -2593,6 +2738,8 @@ rb_handle_head_page(struct ring_buffer_per_cpu *cpu_buffer, local_sub(rb_page_commit(next_page), &cpu_buffer->entries_bytes); local_inc(&cpu_buffer->pages_lost); + if (cpu_buffer->ring_meta) + rb_update_meta_head(cpu_buffer, next_page); /* * The entries will be zeroed out when we move the * tail page. @@ -3154,6 +3301,10 @@ rb_set_commit_to_write(struct ring_buffer_per_cpu *cpu_buffer) local_set(&cpu_buffer->commit_page->page->commit, rb_page_write(cpu_buffer->commit_page)); rb_inc_page(&cpu_buffer->commit_page); + if (cpu_buffer->ring_meta) { + struct ring_buffer_meta *meta = cpu_buffer->ring_meta; + meta->commit_buffer = (unsigned long)cpu_buffer->commit_page->page; + } /* add barrier to keep gcc from optimizing too much */ barrier(); } @@ -4771,6 +4922,9 @@ rb_get_reader_page(struct ring_buffer_per_cpu *cpu_buffer) if (!ret) goto spin; + if (cpu_buffer->ring_meta) + rb_update_meta_reader(cpu_buffer, reader); + /* * Yay! We succeeded in replacing the page. * @@ -5451,11 +5605,16 @@ rb_reset_cpu(struct ring_buffer_per_cpu *cpu_buffer) cpu_buffer->lost_events = 0; cpu_buffer->last_overrun = 0; - if (cpu_buffer->user_mapped) - rb_update_meta_page(cpu_buffer); - rb_head_page_activate(cpu_buffer); cpu_buffer->pages_removed = 0; + + if (cpu_buffer->mapped) { + rb_update_meta_page(cpu_buffer); + if (cpu_buffer->ring_meta) { + struct ring_buffer_meta *meta = cpu_buffer->ring_meta; + meta->commit_buffer = meta->head_buffer; + } + } } /* Must have disabled the cpu buffer then done a synchronize_rcu */ From 2124de79adaa0a0bd1795f1996774e2e69157766 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 12 Jun 2024 19:19:38 -0400 Subject: [PATCH 004/347] tracing: Implement creating an instance based on a given memory region Allow for creating a new instance by passing in an address and size to map the ring buffer for the instance to. This will allow features like a pstore memory mapped region to be used for an tracing instance ring buffer that can be retrieved from one boot to the next. Link: https://lkml.kernel.org/r/20240612232025.692086240@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Daniel Bristot de Oliveira Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Youssef Esmat Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace.c | 50 +++++++++++++++++++++++++++++++++++--------- kernel/trace/trace.h | 6 +++++- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 578a49ff5c32..ff2b504fbe00 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4921,6 +4921,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; } @@ -8664,11 +8669,13 @@ tracing_init_tracefs_percpu(struct trace_array *tr, long cpu) tr, cpu, &tracing_entries_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 } @@ -9205,7 +9212,18 @@ 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); + /* + * 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; @@ -9242,6 +9260,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")) { @@ -9342,7 +9364,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; @@ -9368,6 +9392,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); @@ -9425,7 +9453,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) @@ -9479,7 +9507,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; @@ -9672,8 +9700,10 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer) MEM_FAIL(1, "Could not allocate function filter files"); #ifdef CONFIG_TRACER_SNAPSHOT - trace_create_file("snapshot", TRACE_MODE_WRITE, d_tracer, - tr, &snapshot_fops); + if (!tr->range_addr_start) { + trace_create_file("snapshot", TRACE_MODE_WRITE, d_tracer, + tr, &snapshot_fops); + } #endif trace_create_file("error_log", TRACE_MODE_WRITE, d_tracer, diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 749a182dab48..5dd48932509c 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -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,11 @@ 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; + struct trace_pid_list __rcu *filtered_pids; struct trace_pid_list __rcu *filtered_no_pids; /* From 950032ffcee7a43c960951940059afc5a2bdbcbc Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 12 Jun 2024 19:19:39 -0400 Subject: [PATCH 005/347] ring-buffer: Add output of ring buffer meta page Add a buffer_meta per-cpu file for the trace instance that is mapped to boot memory. This shows the current meta-data and can be used by user space tools to record off the current mappings to help reconstruct the ring buffer after a reboot. It does not expose any virtual addresses, just indexes into the sub-buffer pages. Link: https://lkml.kernel.org/r/20240612232025.854471446@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Daniel Bristot de Oliveira Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Youssef Esmat Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Signed-off-by: Steven Rostedt (Google) --- kernel/trace/ring_buffer.c | 77 ++++++++++++++++++++++++++++++++++++++ kernel/trace/trace.c | 30 ++++++++++++++- kernel/trace/trace.h | 2 + 3 files changed, 107 insertions(+), 2 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 0e3ed7f1cc5d..25b0e61e8c76 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -32,6 +32,8 @@ #include #include +#include "trace.h" + /* * The "absolute" timestamp in the buffer is only 59 bits. * If a clock has the 5 MSBs set, it needs to be saved and @@ -1647,6 +1649,81 @@ static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages) } } +static void *rbm_start(struct seq_file *m, loff_t *pos) +{ + struct ring_buffer_per_cpu *cpu_buffer = m->private; + struct ring_buffer_meta *meta = cpu_buffer->ring_meta; + unsigned long val; + + if (!meta) + return NULL; + + if (*pos > meta->nr_subbufs) + return NULL; + + val = *pos; + val++; + + return (void *)val; +} + +static void *rbm_next(struct seq_file *m, void *v, loff_t *pos) +{ + (*pos)++; + + return rbm_start(m, pos); +} + +static int rb_meta_subbuf_idx(struct ring_buffer_meta *meta, void *subbuf); + +static int rbm_show(struct seq_file *m, void *v) +{ + struct ring_buffer_per_cpu *cpu_buffer = m->private; + struct ring_buffer_meta *meta = cpu_buffer->ring_meta; + unsigned long val = (unsigned long)v; + + if (val == 1) { + seq_printf(m, "head_buffer: %d\n", + rb_meta_subbuf_idx(meta, (void *)meta->head_buffer)); + seq_printf(m, "commit_buffer: %d\n", + rb_meta_subbuf_idx(meta, (void *)meta->commit_buffer)); + seq_printf(m, "subbuf_size: %d\n", meta->subbuf_size); + seq_printf(m, "nr_subbufs: %d\n", meta->nr_subbufs); + return 0; + } + + val -= 2; + seq_printf(m, "buffer[%ld]: %d\n", val, meta->buffers[val]); + + return 0; +} + +static void rbm_stop(struct seq_file *m, void *p) +{ +} + +static const struct seq_operations rb_meta_seq_ops = { + .start = rbm_start, + .next = rbm_next, + .show = rbm_show, + .stop = rbm_stop, +}; + +int ring_buffer_meta_seq_init(struct file *file, struct trace_buffer *buffer, int cpu) +{ + struct seq_file *m; + int ret; + + ret = seq_open(file, &rb_meta_seq_ops); + if (ret) + return ret; + + m = file->private_data; + m->private = buffer->buffers[cpu]; + + return 0; +} + static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, long nr_pages, struct list_head *pages) { diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index ff2b504fbe00..622fe670949d 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -5018,7 +5018,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; @@ -5059,7 +5059,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 @@ -6860,6 +6860,22 @@ tracing_total_entries_read(struct file *filp, char __user *ubuf, return simple_read_from_buffer(ubuf, cnt, ppos, buf, r); } +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) @@ -7436,6 +7452,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, @@ -8668,6 +8691,9 @@ 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 if (!tr->range_addr_start) { trace_create_cpu_file("snapshot", TRACE_MODE_WRITE, d_cpu, diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 5dd48932509c..3e56d3b22212 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -645,6 +645,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); From c76883f18e59b762247ee91d3e4224231711854e Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 12 Jun 2024 19:19:40 -0400 Subject: [PATCH 006/347] ring-buffer: Add test if range of boot buffer is valid Add a test against the ring buffer memory range to see if it has valid data. The ring_buffer_meta structure is given a new field called "first_buffer" which holds the address of the first sub-buffer. This is used to both determine if the other fields are valid as well as finding the offset between the old addresses of the sub-buffer from the previous boot to the new addresses of the current boot. Since the values for nr_subbufs and subbuf_size is to be the same, check if the values in the meta page match the values calculated. Take the range of the first_buffer and the total size of all the buffers and make sure the saved head_buffer and commit_buffer fall in the range. Iterate through all the sub-buffers to make sure that the values in the sub-buffer "commit" field (the field that holds the amount of data on the sub-buffer) is within the end of the sub-buffer. Also check the index array to make sure that all the indexes are within nr_subbufs. Link: https://lkml.kernel.org/r/20240612232026.013843655@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Daniel Bristot de Oliveira Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Youssef Esmat Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Signed-off-by: Steven Rostedt (Google) --- kernel/trace/ring_buffer.c | 143 ++++++++++++++++++++++++++++++++++--- 1 file changed, 135 insertions(+), 8 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 25b0e61e8c76..588bc057bad7 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -45,6 +45,7 @@ static void update_pages_handler(struct work_struct *work); struct ring_buffer_meta { + unsigned long first_buffer; unsigned long head_buffer; unsigned long commit_buffer; __u32 subbuf_size; @@ -1618,21 +1619,103 @@ static void *rb_range_buffer(struct ring_buffer_per_cpu *cpu_buffer, int idx) return (void *)ptr; } +/* + * See if the existing memory contains valid ring buffer data. + * As the previous kernel must be the same as this kernel, all + * the calculations (size of buffers and number of buffers) + * must be the same. + */ +static bool rb_meta_valid(struct ring_buffer_meta *meta, int cpu, + struct trace_buffer *buffer, int nr_pages) +{ + int subbuf_size = PAGE_SIZE; + struct buffer_data_page *subbuf; + unsigned long buffers_start; + unsigned long buffers_end; + int i; + + /* The subbuffer's size and number of subbuffers must match */ + if (meta->subbuf_size != subbuf_size || + meta->nr_subbufs != nr_pages + 1) { + pr_info("Ring buffer boot meta [%d] mismatch of subbuf_size/nr_pages\n", cpu); + return false; + } + + buffers_start = meta->first_buffer; + buffers_end = meta->first_buffer + (subbuf_size * meta->nr_subbufs); + + /* Is the head and commit buffers within the range of buffers? */ + if (meta->head_buffer < buffers_start || + meta->head_buffer >= buffers_end) { + pr_info("Ring buffer boot meta [%d] head buffer out of range\n", cpu); + return false; + } + + if (meta->commit_buffer < buffers_start || + meta->commit_buffer >= buffers_end) { + pr_info("Ring buffer boot meta [%d] commit buffer out of range\n", cpu); + return false; + } + + subbuf = rb_subbufs_from_meta(meta); + + /* Is the meta buffers and the subbufs themselves have correct data? */ + for (i = 0; i < meta->nr_subbufs; i++) { + if (meta->buffers[i] < 0 || + meta->buffers[i] >= meta->nr_subbufs) { + pr_info("Ring buffer boot meta [%d] array out of range\n", cpu); + return false; + } + + if ((unsigned)local_read(&subbuf->commit) > subbuf_size) { + pr_info("Ring buffer boot meta [%d] buffer invalid commit\n", cpu); + return false; + } + + subbuf = (void *)subbuf + subbuf_size; + } + + pr_info("Ring buffer meta is from previous boot!\n"); + return true; +} + static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages) { struct ring_buffer_meta *meta; + unsigned long delta; void *subbuf; int cpu; int i; for (cpu = 0; cpu < nr_cpu_ids; cpu++) { + void *next_meta; + meta = rb_range_meta(buffer, nr_pages, cpu); + if (rb_meta_valid(meta, cpu, buffer, nr_pages)) { + /* Make the mappings match the current address */ + subbuf = rb_subbufs_from_meta(meta); + delta = (unsigned long)subbuf - meta->first_buffer; + meta->first_buffer += delta; + meta->head_buffer += delta; + meta->commit_buffer += delta; + continue; + } + + if (cpu < nr_cpu_ids - 1) + next_meta = rb_range_meta(buffer, nr_pages, cpu + 1); + else + next_meta = (void *)buffer->range_addr_end; + + memset(meta, 0, next_meta - (void *)meta); + meta->nr_subbufs = nr_pages + 1; meta->subbuf_size = PAGE_SIZE; subbuf = rb_subbufs_from_meta(meta); + meta->first_buffer = (unsigned long)subbuf; + /* * The buffers[] array holds the order of the sub-buffers * that are after the meta data. The sub-buffers may @@ -1724,10 +1807,26 @@ int ring_buffer_meta_seq_init(struct file *file, struct trace_buffer *buffer, in return 0; } +/* Map the buffer_pages to the previous head and commit pages */ +static void rb_meta_buffer_update(struct ring_buffer_per_cpu *cpu_buffer, + struct buffer_page *bpage) +{ + struct ring_buffer_meta *meta = cpu_buffer->ring_meta; + + if (meta->head_buffer == (unsigned long)bpage->page) + cpu_buffer->head_page = bpage; + + if (meta->commit_buffer == (unsigned long)bpage->page) { + cpu_buffer->commit_page = bpage; + cpu_buffer->tail_page = bpage; + } +} + static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, long nr_pages, struct list_head *pages) { struct trace_buffer *buffer = cpu_buffer->buffer; + struct ring_buffer_meta *meta = NULL; struct buffer_page *bpage, *tmp; bool user_thread = current->mm != NULL; gfp_t mflags; @@ -1762,6 +1861,10 @@ static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, */ if (user_thread) set_current_oom_origin(); + + if (buffer->range_addr_start) + meta = rb_range_meta(buffer, nr_pages, cpu_buffer->cpu); + for (i = 0; i < nr_pages; i++) { struct page *page; @@ -1778,11 +1881,14 @@ static int __rb_allocate_pages(struct ring_buffer_per_cpu *cpu_buffer, */ list_add_tail(&bpage->list, pages); - if (buffer->range_addr_start) { + if (meta) { /* A range was given. Use that for the buffer page */ bpage->page = rb_range_buffer(cpu_buffer, i + 1); if (!bpage->page) goto free_pages; + /* If this is valid from a previous boot */ + if (meta->head_buffer) + rb_meta_buffer_update(cpu_buffer, bpage); bpage->range = 1; bpage->id = i + 1; } else { @@ -1844,6 +1950,7 @@ static struct ring_buffer_per_cpu * rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) { struct ring_buffer_per_cpu *cpu_buffer; + struct ring_buffer_meta *meta; struct buffer_page *bpage; struct page *page; int ret; @@ -1884,6 +1991,8 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) bpage->page = rb_range_buffer(cpu_buffer, 0); if (!bpage->page) goto fail_free_reader; + if (cpu_buffer->ring_meta->head_buffer) + rb_meta_buffer_update(cpu_buffer, bpage); bpage->range = 1; } else { page = alloc_pages_node(cpu_to_node(cpu), @@ -1902,14 +2011,32 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) if (ret < 0) goto fail_free_reader; - cpu_buffer->head_page - = list_entry(cpu_buffer->pages, struct buffer_page, list); - cpu_buffer->tail_page = cpu_buffer->commit_page = cpu_buffer->head_page; + /* If the boot meta was valid then this has already been updated */ + meta = cpu_buffer->ring_meta; + if (!meta || !meta->head_buffer || + !cpu_buffer->head_page || !cpu_buffer->commit_page || !cpu_buffer->tail_page) { + if (meta && meta->head_buffer && + (cpu_buffer->head_page || cpu_buffer->commit_page || cpu_buffer->tail_page)) { + pr_warn("Ring buffer meta buffers not all mapped\n"); + if (!cpu_buffer->head_page) + pr_warn(" Missing head_page\n"); + if (!cpu_buffer->commit_page) + pr_warn(" Missing commit_page\n"); + if (!cpu_buffer->tail_page) + pr_warn(" Missing tail_page\n"); + } - rb_head_page_activate(cpu_buffer); - if (cpu_buffer->ring_meta) { - struct ring_buffer_meta *meta = cpu_buffer->ring_meta; - meta->commit_buffer = meta->head_buffer; + cpu_buffer->head_page + = list_entry(cpu_buffer->pages, struct buffer_page, list); + cpu_buffer->tail_page = cpu_buffer->commit_page = cpu_buffer->head_page; + + rb_head_page_activate(cpu_buffer); + + if (cpu_buffer->ring_meta) + meta->commit_buffer = meta->head_buffer; + } else { + /* The valid meta buffer still needs to activate the head page */ + rb_head_page_activate(cpu_buffer); } return cpu_buffer; From 5f3b6e839f3ceb8d6ef02231ba9b5aca71b8bf55 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 12 Jun 2024 19:19:41 -0400 Subject: [PATCH 007/347] ring-buffer: Validate boot range memory events Make sure all the events in each of the sub-buffers that were mapped in a memory region are valid. This moves the code that walks the buffers for time-stamp validation out of the CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS ifdef block and is used to validate the content. Only the ring buffer event meta data and time stamps are checked and not the data load. This also has a second purpose. The buffer_page structure that points to the data sub-buffers has accounting that keeps track of the number of events that are on the sub-buffer. This updates that counter as well. That counter is used in reading the buffer and knowing if the ring buffer is empty or not. Link: https://lkml.kernel.org/r/20240612232026.172503570@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Daniel Bristot de Oliveira Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Youssef Esmat Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Signed-off-by: Steven Rostedt (Google) --- kernel/trace/ring_buffer.c | 190 +++++++++++++++++++++++++++++-------- 1 file changed, 152 insertions(+), 38 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 588bc057bad7..804dfbdeef84 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -1675,10 +1675,152 @@ static bool rb_meta_valid(struct ring_buffer_meta *meta, int cpu, subbuf = (void *)subbuf + subbuf_size; } - pr_info("Ring buffer meta is from previous boot!\n"); return true; } +static int rb_meta_subbuf_idx(struct ring_buffer_meta *meta, void *subbuf); + +static int rb_read_data_buffer(struct buffer_data_page *dpage, int tail, int cpu, + unsigned long long *timestamp, u64 *delta_ptr) +{ + struct ring_buffer_event *event; + u64 ts, delta; + int events = 0; + int e; + + *delta_ptr = 0; + *timestamp = 0; + + ts = dpage->time_stamp; + + for (e = 0; e < tail; e += rb_event_length(event)) { + + event = (struct ring_buffer_event *)(dpage->data + e); + + switch (event->type_len) { + + case RINGBUF_TYPE_TIME_EXTEND: + delta = rb_event_time_stamp(event); + ts += delta; + break; + + case RINGBUF_TYPE_TIME_STAMP: + delta = rb_event_time_stamp(event); + delta = rb_fix_abs_ts(delta, ts); + if (delta < ts) { + *delta_ptr = delta; + *timestamp = ts; + return -1; + } + ts = delta; + break; + + case RINGBUF_TYPE_PADDING: + if (event->time_delta == 1) + break; + fallthrough; + case RINGBUF_TYPE_DATA: + events++; + ts += event->time_delta; + break; + + default: + return -1; + } + } + *timestamp = ts; + return events; +} + +static int rb_validate_buffer(struct buffer_data_page *dpage, int cpu) +{ + unsigned long long ts; + u64 delta; + int tail; + + tail = local_read(&dpage->commit); + return rb_read_data_buffer(dpage, tail, cpu, &ts, &delta); +} + +/* If the meta data has been validated, now validate the events */ +static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) +{ + struct ring_buffer_meta *meta = cpu_buffer->ring_meta; + struct buffer_page *head_page; + unsigned long entry_bytes = 0; + unsigned long entries = 0; + int ret; + int i; + + if (!meta || !meta->head_buffer) + return; + + /* Do the reader page first */ + ret = rb_validate_buffer(cpu_buffer->reader_page->page, cpu_buffer->cpu); + if (ret < 0) { + pr_info("Ring buffer reader page is invalid\n"); + goto invalid; + } + entries += ret; + entry_bytes += local_read(&cpu_buffer->reader_page->page->commit); + local_set(&cpu_buffer->reader_page->entries, ret); + + head_page = cpu_buffer->head_page; + + /* If both the head and commit are on the reader_page then we are done. */ + if (head_page == cpu_buffer->reader_page && + head_page == cpu_buffer->commit_page) + goto done; + + /* Iterate until finding the commit page */ + for (i = 0; i < meta->nr_subbufs + 1; i++, rb_inc_page(&head_page)) { + + /* Reader page has already been done */ + if (head_page == cpu_buffer->reader_page) + continue; + + ret = rb_validate_buffer(head_page->page, cpu_buffer->cpu); + if (ret < 0) { + pr_info("Ring buffer meta [%d] invalid buffer page\n", + cpu_buffer->cpu); + goto invalid; + } + entries += ret; + entry_bytes += local_read(&head_page->page->commit); + local_set(&cpu_buffer->head_page->entries, ret); + + if (head_page == cpu_buffer->commit_page) + break; + } + + if (head_page != cpu_buffer->commit_page) { + pr_info("Ring buffer meta [%d] commit page not found\n", + cpu_buffer->cpu); + goto invalid; + } + done: + local_set(&cpu_buffer->entries, entries); + local_set(&cpu_buffer->entries_bytes, entry_bytes); + + pr_info("Ring buffer meta [%d] is from previous boot!\n", cpu_buffer->cpu); + return; + + invalid: + /* The content of the buffers are invalid, reset the meta data */ + meta->head_buffer = 0; + meta->commit_buffer = 0; + + /* Reset the reader page */ + local_set(&cpu_buffer->reader_page->entries, 0); + local_set(&cpu_buffer->reader_page->page->commit, 0); + + /* Reset all the subbuffers */ + for (i = 0; i < meta->nr_subbufs - 1; i++, rb_inc_page(&head_page)) { + local_set(&head_page->entries, 0); + local_set(&head_page->page->commit, 0); + } +} + static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages) { struct ring_buffer_meta *meta; @@ -1757,8 +1899,6 @@ static void *rbm_next(struct seq_file *m, void *v, loff_t *pos) return rbm_start(m, pos); } -static int rb_meta_subbuf_idx(struct ring_buffer_meta *meta, void *subbuf); - static int rbm_show(struct seq_file *m, void *v) { struct ring_buffer_per_cpu *cpu_buffer = m->private; @@ -2011,6 +2151,8 @@ rb_allocate_cpu_buffer(struct trace_buffer *buffer, long nr_pages, int cpu) if (ret < 0) goto fail_free_reader; + rb_meta_validate_events(cpu_buffer); + /* If the boot meta was valid then this has already been updated */ meta = cpu_buffer->ring_meta; if (!meta || !meta->head_buffer || @@ -3955,11 +4097,10 @@ static void check_buffer(struct ring_buffer_per_cpu *cpu_buffer, struct rb_event_info *info, unsigned long tail) { - struct ring_buffer_event *event; struct buffer_data_page *bpage; u64 ts, delta; bool full = false; - int e; + int ret; bpage = info->tail_page->page; @@ -3985,39 +4126,12 @@ static void check_buffer(struct ring_buffer_per_cpu *cpu_buffer, if (atomic_inc_return(this_cpu_ptr(&checking)) != 1) goto out; - ts = bpage->time_stamp; - - for (e = 0; e < tail; e += rb_event_length(event)) { - - event = (struct ring_buffer_event *)(bpage->data + e); - - switch (event->type_len) { - - case RINGBUF_TYPE_TIME_EXTEND: - delta = rb_event_time_stamp(event); - ts += delta; - break; - - case RINGBUF_TYPE_TIME_STAMP: - delta = rb_event_time_stamp(event); - delta = rb_fix_abs_ts(delta, ts); - if (delta < ts) { - buffer_warn_return("[CPU: %d]ABSOLUTE TIME WENT BACKWARDS: last ts: %lld absolute ts: %lld\n", - cpu_buffer->cpu, ts, delta); - } - ts = delta; - break; - - case RINGBUF_TYPE_PADDING: - if (event->time_delta == 1) - break; - fallthrough; - case RINGBUF_TYPE_DATA: - ts += event->time_delta; - break; - - default: - RB_WARN_ON(cpu_buffer, 1); + ret = rb_read_data_buffer(bpage, tail, cpu_buffer->cpu, &ts, &delta); + if (ret < 0) { + if (delta < ts) { + buffer_warn_return("[CPU: %d]ABSOLUTE TIME WENT BACKWARDS: last ts: %lld absolute ts: %lld\n", + cpu_buffer->cpu, ts, delta); + goto out; } } if ((full && ts > info->ts) || From e645535a954ad5eb53ed2c5752c534a3371547c3 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 12 Jun 2024 19:19:42 -0400 Subject: [PATCH 008/347] tracing: Add option to use memmapped memory for trace boot instance Add an option to the trace_instance kernel command line parameter that allows it to use the reserved memory from memmap boot parameter. memmap=12M$0x284500000 trace_instance=boot_mapped@0x284500000:12M The above will reserves 12 megs at the physical address 0x284500000. The second parameter will create a "boot_mapped" instance and use the memory reserved as the memory for the ring buffer. That will create an instance called "boot_mapped": /sys/kernel/tracing/instances/boot_mapped Note, because the ring buffer is using a defined memory ranged, it will act just like a memory mapped ring buffer. It will not have a snapshot buffer, as it can't swap out the buffer. The snapshot files as well as any tracers that uses a snapshot will not be present in the boot_mapped instance. Link: https://lkml.kernel.org/r/20240612232026.329660169@goodmis.org Cc: linux-mm@kvack.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Daniel Bristot de Oliveira Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Youssef Esmat Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Signed-off-by: Steven Rostedt (Google) --- .../admin-guide/kernel-parameters.txt | 9 +++ kernel/trace/trace.c | 75 +++++++++++++++++-- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index b600df82669d..ff26b6094e79 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -6754,6 +6754,15 @@ the same thing would happen if it was left off). The irq_handler_entry event, and all events under the "initcall" system. + 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. + trace_options=[option-list] [FTRACE] Enable or disable tracer options at boot. The option-list is a comma delimited list of options diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 622fe670949d..dfde26aa3211 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -9504,6 +9504,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. @@ -10350,6 +10375,7 @@ __init static void enable_instances(void) { struct trace_array *tr; char *curr_str; + char *name; char *str; char *tok; @@ -10358,19 +10384,56 @@ __init static void enable_instances(void) str = boot_instance_info; while ((curr_str = strsep(&str, "\t"))) { + unsigned long start = 0; + unsigned long size = 0; + unsigned long addr = 0; tok = strsep(&curr_str, ","); + name = strsep(&tok, "@"); + if (tok) { + start = memparse(tok, &tok); + if (!start) { + pr_warn("Tracing: Invalid boot instance address for %s\n", + name); + continue; + } + } - if (IS_ENABLED(CONFIG_TRACER_MAX_TRACE)) - do_allocate_snapshot(tok); + if (start) { + 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; + } + addr = map_pages(start, size); + if (addr) { + pr_info("Tracing: mapped boot instance %s at physical memory 0x%lx of size 0x%lx\n", + name, start, 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_get_by_name(tok, NULL); + tr = trace_array_create_systems(name, NULL, addr, size); if (!tr) { - pr_warn("Failed to create instance buffer %s\n", curr_str); + pr_warn("Tracing: Failed to create instance buffer %s\n", curr_str); continue; } - /* Allow user space to delete it */ - trace_array_put(tr); + + /* Only allow non mapped buffers to be deleted */ + if (!start) + trace_array_put(tr); while ((tok = strsep(&curr_str, ","))) { early_enable_events(tr, tok, true); From 8f3e6659656e63bd710fcab4f0cdfb8608bc1b96 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 12 Jun 2024 19:19:43 -0400 Subject: [PATCH 009/347] ring-buffer: Save text and data locations in mapped meta data When a ring buffer is mapped to a specific address, save the address of a text function and some data. This will be used to determine the delta between the last boot and the current boot for pointers to functions as well as to data. Link: https://lkml.kernel.org/r/20240612232026.496176678@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Daniel Bristot de Oliveira Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Youssef Esmat Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Signed-off-by: Steven Rostedt (Google) --- kernel/trace/ring_buffer.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 804dfbdeef84..8c287430411d 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -45,6 +45,8 @@ static void update_pages_handler(struct work_struct *work); struct ring_buffer_meta { + unsigned long text_addr; + unsigned long data_addr; unsigned long first_buffer; unsigned long head_buffer; unsigned long commit_buffer; @@ -542,6 +544,9 @@ struct trace_buffer { unsigned long range_addr_start; unsigned long range_addr_end; + long last_text_delta; + long last_data_delta; + unsigned int subbuf_size; unsigned int subbuf_order; unsigned int max_data_size; @@ -1821,10 +1826,15 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) } } +/* Used to calculate data delta */ +static char rb_data_ptr[] = ""; + static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages) { struct ring_buffer_meta *meta; unsigned long delta; + unsigned long this_text = (unsigned long)rb_range_meta_init; + unsigned long this_data = (unsigned long)rb_data_ptr; void *subbuf; int cpu; int i; @@ -1841,6 +1851,10 @@ static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages) meta->first_buffer += delta; meta->head_buffer += delta; meta->commit_buffer += delta; + buffer->last_text_delta = this_text - meta->text_addr; + buffer->last_data_delta = this_data - meta->data_addr; + meta->text_addr = this_text; + meta->data_addr = this_data; continue; } @@ -1857,6 +1871,8 @@ static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages) subbuf = rb_subbufs_from_meta(meta); meta->first_buffer = (unsigned long)subbuf; + meta->text_addr = this_text; + meta->data_addr = this_data; /* * The buffers[] array holds the order of the sub-buffers From 7a1d1e4b9639ff08b2f42605c2950ae1ba4a43bf Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 12 Jun 2024 19:19:44 -0400 Subject: [PATCH 010/347] tracing/ring-buffer: Add last_boot_info file to boot instance If an instance is mapped to memory on boot up, create a new file called "last_boot_info" that will hold information that can be used to properly parse the raw data in the ring buffer. It will export the delta of the addresses for text and data from what it was from the last boot. It does not expose actually addresses (unless you knew what the actual address was from the last boot). The output will look like: # cat last_boot_info text delta: -268435456 data delta: -268435456 The text and data are kept separate in case they are ever made different. Link: https://lkml.kernel.org/r/20240612232026.658680738@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Daniel Bristot de Oliveira Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Youssef Esmat Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Signed-off-by: Steven Rostedt (Google) --- include/linux/ring_buffer.h | 3 +++ kernel/trace/ring_buffer.c | 23 ++++++++++++++++++ kernel/trace/trace.c | 47 ++++++++++++++++++++++++++++++++++++- kernel/trace/trace.h | 2 ++ 4 files changed, 74 insertions(+), 1 deletion(-) diff --git a/include/linux/ring_buffer.h b/include/linux/ring_buffer.h index a50b0223b1d3..55de3798a9b9 100644 --- a/include/linux/ring_buffer.h +++ b/include/linux/ring_buffer.h @@ -94,6 +94,9 @@ struct trace_buffer *__ring_buffer_alloc_range(unsigned long size, unsigned flag 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 diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 8c287430411d..f3d772461a60 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -2396,6 +2396,29 @@ struct trace_buffer *__ring_buffer_alloc_range(unsigned long size, unsigned flag return alloc_buffer(size, flags, order, start, start + range_size, key); } +/** + * ring_buffer_last_boot_delta - return the delta offset from last boot + * @buffer: The buffer to return the delta from + * @text: Return text delta + * @data: Return data delta + * + * Returns: The true if the delta is non zero + */ +bool ring_buffer_last_boot_delta(struct trace_buffer *buffer, long *text, + long *data) +{ + if (!buffer) + return false; + + if (!buffer->last_text_delta) + return false; + + *text = buffer->last_text_delta; + *data = buffer->last_data_delta; + + return true; +} + /** * ring_buffer_free - free a ring buffer. * @buffer: the buffer to free. diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index dfde26aa3211..dc4eee33d920 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -6041,6 +6041,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 @@ -6058,6 +6070,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); @@ -6113,6 +6128,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); @@ -6860,6 +6877,21 @@ 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; @@ -7499,6 +7531,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, @@ -9242,6 +9281,9 @@ allocate_trace_buffer(struct trace_array *tr, struct array_buffer *buf, int 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. @@ -9751,7 +9793,10 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer) MEM_FAIL(1, "Could not allocate function filter files"); #ifdef CONFIG_TRACER_SNAPSHOT - if (!tr->range_addr_start) { + if (tr->range_addr_start) { + trace_create_file("last_boot_info", TRACE_MODE_READ, d_tracer, + tr, &last_boot_fops); + } else { trace_create_file("snapshot", TRACE_MODE_WRITE, d_tracer, tr, &snapshot_fops); } diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 3e56d3b22212..3dc5c8f14ce9 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -347,6 +347,8 @@ struct trace_array { 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; From 07714b4bb3f9800261c8b4b2f47e9010ed60979d Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 12 Jun 2024 19:19:45 -0400 Subject: [PATCH 011/347] tracing: Handle old buffer mappings for event strings and functions Use the saved text_delta and data_delta of a persistent memory mapped ring buffer that was saved from a previous boot, and use the delta in the trace event print output so that strings and functions show up normally. That is, for an event like trace_kmalloc() that prints the callsite via "%pS", if it used the address saved in the ring buffer it will not match the function that was saved in the previous boot if the kernel remaps itself between boots. For RCU events that point to saved static strings where only the address of the string is saved in the ring buffer, it too will be adjusted to point to where the string is on the current boot. Link: https://lkml.kernel.org/r/20240612232026.821020753@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Daniel Bristot de Oliveira Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Youssef Esmat Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace.c | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index dc4eee33d920..71cca10581d6 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -3671,8 +3671,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)) @@ -3691,7 +3694,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) { /* @@ -3720,6 +3726,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; @@ -3733,6 +3744,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 @@ -3751,6 +3780,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 @@ -3760,8 +3797,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; From 7cfeb9033dd1fbdaacd74b2e6613d7366f515e16 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 12 Jun 2024 19:19:46 -0400 Subject: [PATCH 012/347] tracing: Update function tracing output for previous boot buffer For a persistent ring buffer that is saved across boots, if function tracing was performed in the previous boot, it only saves the address of the functions and uses "%pS" to print their names. But the current boot, those functions may be in different locations. The persistent meta-data saves the text delta between the two boots and can be used to find the address of the saved function of where it is located in the current boot. Link: https://lkml.kernel.org/r/20240612232026.988226055@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Daniel Bristot de Oliveira Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Youssef Esmat Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace_output.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index d8b302d01083..b9d2c64c0648 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -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); @@ -1674,7 +1677,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)); From a62b4f6fbdffa8e90959da485b68f844241d300f Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 12 Jun 2024 19:19:47 -0400 Subject: [PATCH 013/347] tracing: Add last boot delta offset for stack traces The addresses of a stack trace event are relative to the kallsyms. As that can change between boots, when printing the stack trace from a buffer that was from the last boot, it needs all the addresses to be added to the "text_delta" that gives the delta between the addresses of the functions for the current boot compared to the address of the last boot. Then it can be passed to kallsyms to find the function name, otherwise it just shows a useless list of addresses. Link: https://lkml.kernel.org/r/20240612232027.145807384@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Daniel Bristot de Oliveira Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Youssef Esmat Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace_output.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index b9d2c64c0648..48de93598897 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -1233,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); @@ -1245,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'); } From 94dfa500e7deddceff22768bb994f0fa67bd2fd0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 20 Jun 2024 11:49:57 +0300 Subject: [PATCH 014/347] tracing: Fix NULL vs IS_ERR() check in enable_instances() The trace_array_create_systems() function returns error pointers, not NULL. Fix the check to match. Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Fixes: e645535a954a ("tracing: Add option to use memmapped memory for trace boot instance") Link: https://lore.kernel.org/9b23ea03-d709-435f-a309-461c3d747457@moroto.mountain Signed-off-by: Dan Carpenter Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 71cca10581d6..5462fb10ff64 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -10507,7 +10507,7 @@ __init static void enable_instances(void) } tr = trace_array_create_systems(name, NULL, addr, size); - if (!tr) { + if (IS_ERR(tr)) { pr_warn("Tracing: Failed to create instance buffer %s\n", curr_str); continue; } From b96c312551b241bc17226c5347c6d6b38a1efd3e Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 9 Jul 2024 23:56:58 +0200 Subject: [PATCH 015/347] ring-buffer: Use vma_pages() helper function Use the vma_pages() helper function and fix the following Coccinelle/coccicheck warning reported by vma_pages.cocci: WARNING: Consider using vma_pages helper on vma Rename the local variable vma_pages accordingly. Signed-off-by: Thorsten Blum Link: https://lore.kernel.org/20240709215657.322071-2-thorsten.blum@toblux.com Signed-off-by: Steven Rostedt (Google) --- kernel/trace/ring_buffer.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index f3d772461a60..d59cf0023e38 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -6920,7 +6920,7 @@ static int __rb_inc_dec_mapped(struct ring_buffer_per_cpu *cpu_buffer, static int __rb_map_vma(struct ring_buffer_per_cpu *cpu_buffer, struct vm_area_struct *vma) { - unsigned long nr_subbufs, nr_pages, vma_pages, pgoff = vma->vm_pgoff; + unsigned long nr_subbufs, nr_pages, nr_vma_pages, pgoff = vma->vm_pgoff; unsigned int subbuf_pages, subbuf_order; struct page **pages; int p = 0, s = 0; @@ -6946,11 +6946,11 @@ static int __rb_map_vma(struct ring_buffer_per_cpu *cpu_buffer, nr_subbufs = cpu_buffer->nr_pages + 1; /* + reader-subbuf */ nr_pages = ((nr_subbufs) << subbuf_order) - pgoff + 1; /* + meta-page */ - vma_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; - if (!vma_pages || vma_pages > nr_pages) + nr_vma_pages = vma_pages(vma); + if (!nr_vma_pages || nr_vma_pages > nr_pages) return -EINVAL; - nr_pages = vma_pages; + nr_pages = nr_vma_pages; pages = kcalloc(nr_pages, sizeof(*pages), GFP_KERNEL); if (!pages) From 45a0c928e7aa42caf2380b134bcd326b40a9df84 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Tue, 25 Jun 2024 02:13:39 +0800 Subject: [PATCH 016/347] perf trace: BTF-based enum pretty printing for syscall args MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this patch, BTF is used to turn enum value to the corresponding name. There is only one system call that uses enum value as its argument, that is `landlock_add_rule()`. The vmlinux btf is loaded lazily, when user decided to trace the `landlock_add_rule` syscall. But if one decide to run `perf trace` without any arguments, the behaviour is to trace `landlock_add_rule`, so vmlinux btf will be loaded by default. The laziest behaviour is to load vmlinux btf when a `landlock_add_rule` syscall hits. But I think you could lose some samples when loading vmlinux btf at run time, for it can delay the handling of other samples. I might need your precious opinions on this... before: ``` perf $ ./perf trace -e landlock_add_rule 0.000 ( 0.008 ms): ldlck-test/438194 landlock_add_rule(rule_type: 2) = -1 EBADFD (File descriptor in bad state) 0.010 ( 0.001 ms): ldlck-test/438194 landlock_add_rule(rule_type: 1) = -1 EBADFD (File descriptor in bad state) ``` after: ``` perf $ ./perf trace -e landlock_add_rule 0.000 ( 0.029 ms): ldlck-test/438194 landlock_add_rule(rule_type: LANDLOCK_RULE_NET_PORT) = -1 EBADFD (File descriptor in bad state) 0.036 ( 0.004 ms): ldlck-test/438194 landlock_add_rule(rule_type: LANDLOCK_RULE_PATH_BENEATH) = -1 EBADFD (File descriptor in bad state) ``` Committer notes: Made it build with NO_LIBBPF=1, simplified btf_enum_fprintf(), see [1] for the discussion. Signed-off-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Günther Noack Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Mickaël Salaün Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/lkml/20240613022757.3589783-1-howardchu95@gmail.com Link: https://lore.kernel.org/lkml/ZnXAhFflUl_LV1QY@x1 # [1] Link: https://lore.kernel.org/r/20240624181345.124764-3-howardchu95@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 110 +++++++++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 8449f2beb54d..1391564911d9 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -19,6 +19,7 @@ #ifdef HAVE_LIBBPF_SUPPORT #include #include +#include #ifdef HAVE_BPF_SKEL #include "bpf_skel/augmented_raw_syscalls.skel.h" #endif @@ -110,6 +111,10 @@ struct syscall_arg_fmt { const char *name; u16 nr_entries; // for arrays bool show_zero; + bool is_enum; +#ifdef HAVE_LIBBPF_SUPPORT + const struct btf_type *type; +#endif }; struct syscall_fmt { @@ -139,6 +144,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; @@ -204,6 +212,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 { @@ -887,6 +909,64 @@ 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 int syscall_arg_fmt__cache_btf_enum(struct syscall_arg_fmt *arg_fmt, struct btf *btf, char *type) +{ + int id; + + // Already cached? + if (arg_fmt->type != NULL) + return 0; + + type = strstr(type, "enum "); + if (type == NULL) + return -1; + + type += 5; // skip "enum " to get the enumeration name + + id = btf__find_by_name(btf, type); + if (id < 0) + return -1; + + arg_fmt->type = btf__type_by_id(btf, id); + return arg_fmt->type == NULL ? -1 : 0; +} + +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; +} + +static size_t trace__btf_enum_scnprintf(struct trace *trace, struct syscall_arg_fmt *arg_fmt, char *bf, + size_t size, int val, char *type) +{ + if (trace->btf == NULL) + return 0; + + if (syscall_arg_fmt__cache_btf_enum(arg_fmt, trace->btf, type) < 0) + return 0; + + return btf_enum_scnprintf(arg_fmt->type, trace->btf, bf, size, val); +} +#else // HAVE_LIBBPF_SUPPORT +static size_t trace__btf_enum_scnprintf(struct trace *trace __maybe_unused, struct syscall_arg_fmt *arg_fmt __maybe_unused, + char *bf __maybe_unused, size_t size __maybe_unused, int val __maybe_unused, + char *type __maybe_unused) +{ + return 0; +} +#endif // HAVE_LIBBPF_SUPPORT + #define STRARRAY(name, array) \ { .scnprintf = SCA_STRARRAY, \ .strtoul = STUL_STRARRAY, \ @@ -1238,6 +1318,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; @@ -1744,7 +1825,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; @@ -1756,6 +1838,7 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field continue; len = strlen(field->name); + arg->is_enum = false; if (strcmp(field->type, "const char *") == 0 && ((len >= 4 && strcmp(field->name + len - 4, "name") == 0) || @@ -1782,6 +1865,8 @@ 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 = arg->is_enum = true; } else { const struct syscall_arg_fmt *fmt = syscall_arg_fmt__find_by_name(field->name); @@ -1798,7 +1883,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 +1897,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,7 +1970,13 @@ 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) @@ -1891,7 +1984,7 @@ static int evsel__init_tp_arg_scnprintf(struct evsel *evsel) 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, NULL); return 0; } @@ -2103,6 +2196,15 @@ 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); + if (sc->arg_fmt[arg.idx].is_enum) { + size_t p = trace__btf_enum_scnprintf(trace, &sc->arg_fmt[arg.idx], bf + printed, + size - printed, val, field->type); + if (p) { + printed += p; + continue; + } + } + printed += syscall_arg_fmt__scnprintf_val(&sc->arg_fmt[arg.idx], bf + printed, size - printed, &arg, val); } From 607bbdb49ccb646be707a0f2ac1d78f5a7c3de7c Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Tue, 25 Jun 2024 02:13:40 +0800 Subject: [PATCH 017/347] perf trace: Augment non-syscall tracepoints with enum arguments with BTF Before: perf $ ./perf trace -e timer:hrtimer_start --max-events=1 0.000 :0/0 timer:hrtimer_start(hrtimer: 0xffff974466c25f18, function: 0xffffffff89da5be0, expires: 377432432256753, softexpires: 377432432256753, mode: 10) After: perf $ ./perf trace -e timer:hrtimer_start --max-events=1 0.000 :0/0 timer:hrtimer_start(hrtimer: 0xffff9498a6ca5f18, function: 0xffffffffa77a5be0, expires: 4382442895089, softexpires: 4382442895089, mode: HRTIMER_MODE_ABS_PINNED_HARD) in which HRTIMER_MODE_ABS_PINNED_HARD is: perf $ pahole hrtimer_mode enum hrtimer_mode { HRTIMER_MODE_ABS = 0, HRTIMER_MODE_REL = 1, HRTIMER_MODE_PINNED = 2, HRTIMER_MODE_SOFT = 4, HRTIMER_MODE_HARD = 8, HRTIMER_MODE_ABS_PINNED = 2, HRTIMER_MODE_REL_PINNED = 3, HRTIMER_MODE_ABS_SOFT = 4, HRTIMER_MODE_REL_SOFT = 5, HRTIMER_MODE_ABS_PINNED_SOFT = 6, HRTIMER_MODE_REL_PINNED_SOFT = 7, HRTIMER_MODE_ABS_HARD = 8, HRTIMER_MODE_REL_HARD = 9, HRTIMER_MODE_ABS_PINNED_HARD = 10, HRTIMER_MODE_REL_PINNED_HARD = 11, }; Can also be tested by ./perf trace -e pagemap:mm_lru_insertion,timer:hrtimer_start,timer:hrtimer_init,skb:kfree_skb --max-events=10 (Chose these 4 events because they happen quite frequently.) However some enum arguments may not be contained in vmlinux BTF. To see what enum arguments are supported, use: vmlinux_dir $ bpftool btf dump file /sys/kernel/btf/vmlinux > vmlinux vmlinux_dir $ while read l; do grep "ENUM '$l'" vmlinux; done < <(grep field:enum /sys/kernel/tracing/events/*/*/format | awk '{print $3}' | sort | uniq) | awk '{print $3}' | sed "s/'\(.*\)'/\1/g" dev_pm_qos_req_type error_detector hrtimer_mode i2c_slave_event ieee80211_bss_type lru_list migrate_mode nl80211_auth_type nl80211_band nl80211_iftype numa_vmaskip_reason pm_qos_req_action pwm_polarity skb_drop_reason thermal_trip_type xen_lazy_mode xen_mc_extend_args xen_mc_flush_reason zone_type And what tracepoints have these enum types as their arguments: vmlinux_dir $ while read l; do grep "ENUM '$l'" vmlinux; done < <(grep field:enum /sys/kernel/tracing/events/*/*/format | awk '{print $3}' | sort | uniq) | awk '{print $3}' | sed "s/'\(.*\)'/\1/g" > good_enums vmlinux_dir $ cat good_enums dev_pm_qos_req_type error_detector hrtimer_mode i2c_slave_event ieee80211_bss_type lru_list migrate_mode nl80211_auth_type nl80211_band nl80211_iftype numa_vmaskip_reason pm_qos_req_action pwm_polarity skb_drop_reason thermal_trip_type xen_lazy_mode xen_mc_extend_args xen_mc_flush_reason zone_type vmlinux_dir $ grep -f good_enums -l /sys/kernel/tracing/events/*/*/format /sys/kernel/tracing/events/cfg80211/cfg80211_chandef_dfs_required/format /sys/kernel/tracing/events/cfg80211/cfg80211_ch_switch_notify/format /sys/kernel/tracing/events/cfg80211/cfg80211_ch_switch_started_notify/format /sys/kernel/tracing/events/cfg80211/cfg80211_get_bss/format /sys/kernel/tracing/events/cfg80211/cfg80211_ibss_joined/format /sys/kernel/tracing/events/cfg80211/cfg80211_inform_bss_frame/format /sys/kernel/tracing/events/cfg80211/cfg80211_radar_event/format /sys/kernel/tracing/events/cfg80211/cfg80211_ready_on_channel_expired/format /sys/kernel/tracing/events/cfg80211/cfg80211_ready_on_channel/format /sys/kernel/tracing/events/cfg80211/cfg80211_reg_can_beacon/format /sys/kernel/tracing/events/cfg80211/cfg80211_return_bss/format /sys/kernel/tracing/events/cfg80211/cfg80211_tx_mgmt_expired/format /sys/kernel/tracing/events/cfg80211/rdev_add_virtual_intf/format /sys/kernel/tracing/events/cfg80211/rdev_auth/format /sys/kernel/tracing/events/cfg80211/rdev_change_virtual_intf/format /sys/kernel/tracing/events/cfg80211/rdev_channel_switch/format /sys/kernel/tracing/events/cfg80211/rdev_connect/format /sys/kernel/tracing/events/cfg80211/rdev_inform_bss/format /sys/kernel/tracing/events/cfg80211/rdev_libertas_set_mesh_channel/format /sys/kernel/tracing/events/cfg80211/rdev_mgmt_tx/format /sys/kernel/tracing/events/cfg80211/rdev_remain_on_channel/format /sys/kernel/tracing/events/cfg80211/rdev_return_chandef/format /sys/kernel/tracing/events/cfg80211/rdev_return_int_survey_info/format /sys/kernel/tracing/events/cfg80211/rdev_set_ap_chanwidth/format /sys/kernel/tracing/events/cfg80211/rdev_set_monitor_channel/format /sys/kernel/tracing/events/cfg80211/rdev_set_radar_background/format /sys/kernel/tracing/events/cfg80211/rdev_start_ap/format /sys/kernel/tracing/events/cfg80211/rdev_start_radar_detection/format /sys/kernel/tracing/events/cfg80211/rdev_tdls_channel_switch/format /sys/kernel/tracing/events/compaction/mm_compaction_defer_compaction/format /sys/kernel/tracing/events/compaction/mm_compaction_deferred/format /sys/kernel/tracing/events/compaction/mm_compaction_defer_reset/format /sys/kernel/tracing/events/compaction/mm_compaction_finished/format /sys/kernel/tracing/events/compaction/mm_compaction_kcompactd_wake/format /sys/kernel/tracing/events/compaction/mm_compaction_suitable/format /sys/kernel/tracing/events/compaction/mm_compaction_wakeup_kcompactd/format /sys/kernel/tracing/events/error_report/error_report_end/format /sys/kernel/tracing/events/i2c_slave/i2c_slave/format /sys/kernel/tracing/events/migrate/mm_migrate_pages/format /sys/kernel/tracing/events/migrate/mm_migrate_pages_start/format /sys/kernel/tracing/events/pagemap/mm_lru_insertion/format /sys/kernel/tracing/events/power/dev_pm_qos_add_request/format /sys/kernel/tracing/events/power/dev_pm_qos_remove_request/format /sys/kernel/tracing/events/power/dev_pm_qos_update_request/format /sys/kernel/tracing/events/power/pm_qos_update_flags/format /sys/kernel/tracing/events/power/pm_qos_update_target/format /sys/kernel/tracing/events/pwm/pwm_apply/format /sys/kernel/tracing/events/pwm/pwm_get/format /sys/kernel/tracing/events/sched/sched_skip_vma_numa/format /sys/kernel/tracing/events/skb/kfree_skb/format /sys/kernel/tracing/events/thermal/thermal_zone_trip/format /sys/kernel/tracing/events/timer/hrtimer_init/format /sys/kernel/tracing/events/timer/hrtimer_start/format /sys/kernel/tracing/events/xen/xen_mc_batch/format /sys/kernel/tracing/events/xen/xen_mc_extend_args/format /sys/kernel/tracing/events/xen/xen_mc_flush_reason/format /sys/kernel/tracing/events/xen/xen_mc_issue/format Committer testing: root@x1:~# perf trace -e timer:hrtimer_start --max-events=2 0.000 :0/0 timer:hrtimer_start(hrtimer: 0xffff8d4eff225050, function: 0xffffffff9e22ddd0, expires: 241152380000000, softexpires: 241152380000000, mode: HRTIMER_MODE_ABS) 0.028 :0/0 timer:hrtimer_start(hrtimer: 0xffff8d4eff225050, function: 0xffffffff9e22ddd0, expires: 241153654000000, softexpires: 241153654000000, mode: HRTIMER_MODE_ABS_PINNED_HARD) root@x1:~# Suggested-by: Arnaldo Carvalho de Melo Reviewed-by: Arnaldo Carvalho de Melo Signed-off-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/lkml/20240615032743.112750-1-howardchu95@gmail.com Link: https://lore.kernel.org/r/20240624181345.124764-4-howardchu95@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 1391564911d9..5618feb7d01a 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1979,12 +1979,12 @@ static int trace__read_syscall_info(struct trace *trace, int id) 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, NULL); + syscall_arg_fmt__init_array(fmt, evsel->tp_format->format.fields, use_btf); return 0; } @@ -2188,7 +2188,8 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, * property isn't set. */ 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].is_enum)) continue; printed += scnprintf(bf + printed, size - printed, "%s", printed ? ", " : ""); @@ -2893,7 +2894,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->is_enum) continue; printed += scnprintf(bf + printed, size - printed, "%s", printed ? ", " : ""); @@ -2901,6 +2902,15 @@ 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); + if (arg->is_enum) { + size_t p = trace__btf_enum_scnprintf(trace, arg, bf + printed, + size - printed, val, field->type); + if (p) { + printed += p; + continue; + } + } + printed += syscall_arg_fmt__scnprintf_val(arg, bf + printed, size - printed, &syscall_arg, val); } @@ -4553,7 +4563,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; @@ -4562,7 +4572,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; } @@ -5040,11 +5050,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) { From 95586588868a04ea2bbfa144cf473bea9bb86110 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Tue, 25 Jun 2024 02:13:41 +0800 Subject: [PATCH 018/347] perf trace: Filter enum arguments with enum names Before: perf $ ./perf trace -e timer:hrtimer_start --filter='mode!=HRTIMER_MODE_ABS_PINNED_HARD' --max-events=1 No resolver (strtoul) for "mode" in "timer:hrtimer_start", can't set filter "(mode!=HRTIMER_MODE_ABS_PINNED_HARD) && (common_pid != 281988)" After: perf $ ./perf trace -e timer:hrtimer_start --filter='mode!=HRTIMER_MODE_ABS_PINNED_HARD' --max-events=1 0.000 :0/0 timer:hrtimer_start(hrtimer: 0xffff9498a6ca5f18, function: 0xffffffffa77a5be0, expires: 12351248764875, softexpires: 12351248764875, mode: HRTIMER_MODE_ABS) && and ||: perf $ ./perf trace -e timer:hrtimer_start --filter='mode != HRTIMER_MODE_ABS_PINNED_HARD && mode != HRTIMER_MODE_ABS' --max-events=1 0.000 Hyprland/534 timer:hrtimer_start(hrtimer: 0xffff9497801a84d0, function: 0xffffffffc04cdbe0, expires: 12639434638458, softexpires: 12639433638458, mode: HRTIMER_MODE_REL) perf $ ./perf trace -e timer:hrtimer_start --filter='mode == HRTIMER_MODE_REL || mode == HRTIMER_MODE_PINNED' --max-events=1 0.000 ldlck-test/60639 timer:hrtimer_start(hrtimer: 0xffffb16404ee7bf8, function: 0xffffffffa7790420, expires: 12772614418016, softexpires: 12772614368016, mode: HRTIMER_MODE_REL) Switching it up, using both enum name and integer value(--filter='mode == HRTIMER_MODE_ABS_PINNED_HARD || mode == 0'): perf $ ./perf trace -e timer:hrtimer_start --filter='mode == HRTIMER_MODE_ABS_PINNED_HARD || mode == 0' --max-events=3 0.000 :0/0 timer:hrtimer_start(hrtimer: 0xffff9498a6ca5f18, function: 0xffffffffa77a5be0, expires: 12601748739825, softexpires: 12601748739825, mode: HRTIMER_MODE_ABS_PINNED_HARD) 0.036 :0/0 timer:hrtimer_start(hrtimer: 0xffff9498a6ca5f18, function: 0xffffffffa77a5be0, expires: 12518758748124, softexpires: 12518758748124, mode: HRTIMER_MODE_ABS_PINNED_HARD) 0.172 tmux: server/41881 timer:hrtimer_start(hrtimer: 0xffffb164081e7838, function: 0xffffffffa7790420, expires: 12518768255836, softexpires: 12518768205836, mode: HRTIMER_MODE_ABS) P.S. perf $ pahole hrtimer_mode enum hrtimer_mode { HRTIMER_MODE_ABS = 0, HRTIMER_MODE_REL = 1, HRTIMER_MODE_PINNED = 2, HRTIMER_MODE_SOFT = 4, HRTIMER_MODE_HARD = 8, HRTIMER_MODE_ABS_PINNED = 2, HRTIMER_MODE_REL_PINNED = 3, HRTIMER_MODE_ABS_SOFT = 4, HRTIMER_MODE_REL_SOFT = 5, HRTIMER_MODE_ABS_PINNED_SOFT = 6, HRTIMER_MODE_REL_PINNED_SOFT = 7, HRTIMER_MODE_ABS_HARD = 8, HRTIMER_MODE_REL_HARD = 9, HRTIMER_MODE_ABS_PINNED_HARD = 10, HRTIMER_MODE_REL_PINNED_HARD = 11, }; Committer testing: root@x1:~# perf trace -e timer:hrtimer_start --filter='mode != HRTIMER_MODE_ABS' --max-events=2 0.000 :0/0 timer:hrtimer_start(hrtimer: 0xffff8d4eff2a5050, function: 0xffffffff9e22ddd0, expires: 241502326000000, softexpires: 241502326000000, mode: HRTIMER_MODE_ABS_PINNED_HARD) 18446744073709.488 :0/0 timer:hrtimer_start(hrtimer: 0xffff8d4eff425050, function: 0xffffffff9e22ddd0, expires: 241501814000000, softexpires: 241501814000000, mode: HRTIMER_MODE_ABS_PINNED_HARD) root@x1:~# perf trace -e timer:hrtimer_start --filter='mode != HRTIMER_MODE_ABS && mode != HRTIMER_MODE_ABS_PINNED_HARD' --max-events=2 0.000 podman/510644 timer:hrtimer_start(hrtimer: 0xffffa2024f5f7dd0, function: 0xffffffff9e2170c0, expires: 241530497418194, softexpires: 241530497368194, mode: HRTIMER_MODE_REL) 40.251 gnome-shell/2484 timer:hrtimer_start(hrtimer: 0xffff8d48bda17650, function: 0xffffffffc0661550, expires: 241550528619247, softexpires: 241550527619247, mode: HRTIMER_MODE_REL) root@x1:~# perf trace -v -e timer:hrtimer_start --filter='mode != HRTIMER_MODE_ABS && mode != HRTIMER_MODE_ABS_PINNED_HARD && mode != HRTIMER_MODE_REL' --max-events=2 Using CPUID GenuineIntel-6-BA-3 vmlinux BTF loaded 0 0xa 0x1 New filter for timer:hrtimer_start: (mode != 0 && mode != 0xa && mode != 0x1) && (common_pid != 524049 && common_pid != 4041) mmap size 528384B ^Croot@x1:~# Suggested-by: Arnaldo Carvalho de Melo Signed-off-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/lkml/ZnCcliuecJABD5FN@x1 Link: https://lore.kernel.org/r/20240624181345.124764-5-howardchu95@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 62 ++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 5618feb7d01a..e664001d5ed7 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -932,6 +932,37 @@ static int syscall_arg_fmt__cache_btf_enum(struct syscall_arg_fmt *arg_fmt, stru return arg_fmt->type == NULL ? -1 : 0; } +static bool syscall_arg__strtoul_btf_enum(char *bf, size_t size, struct syscall_arg *arg, u64 *val) +{ + const struct btf_type *bt; + char *type = arg->parm; + struct btf_enum *be; + struct btf *btf; + + trace__load_vmlinux_btf(arg->trace); + + btf = arg->trace->btf; + if (btf == NULL) + return false; + + if (syscall_arg_fmt__cache_btf_enum(arg->fmt, btf, type) < 0) + return false; + + bt = arg->fmt->type; + 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 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); @@ -965,8 +996,16 @@ static size_t trace__btf_enum_scnprintf(struct trace *trace __maybe_unused, stru { return 0; } + +static bool syscall_arg__strtoul_btf_enum(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_ENUM syscall_arg__strtoul_btf_enum + #define STRARRAY(name, array) \ { .scnprintf = SCA_STRARRAY, \ .strtoul = STUL_STRARRAY, \ @@ -1867,6 +1906,7 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field arg->scnprintf = SCA_FD; } else if (strstr(field->type, "enum") && use_btf != NULL) { *use_btf = arg->is_enum = true; + arg->strtoul = STUL_BTF_ENUM; } else { const struct syscall_arg_fmt *fmt = syscall_arg_fmt__find_by_name(field->name); @@ -3792,7 +3832,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); @@ -3801,13 +3842,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; @@ -3840,14 +3883,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); @@ -3860,9 +3903,16 @@ static int trace__expand_filter(struct trace *trace __maybe_unused, struct evsel if (fmt->strtoul) { u64 val; struct syscall_arg syscall_arg = { - .parm = fmt->parm, + .trace = trace, + .fmt = fmt, }; + if (fmt->is_enum) { + syscall_arg.parm = type; + } else { + syscall_arg.parm = fmt->parm; + } + if (fmt->strtoul(right, right_size, &syscall_arg, &val)) { char *n, expansion[19]; int expansion_lenght = scnprintf(expansion, sizeof(expansion), "%#" PRIx64, val); From 3656e566cf03ab0f959b2bd6f8274ee9799641e6 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Tue, 25 Jun 2024 02:13:42 +0800 Subject: [PATCH 019/347] perf test: Add landlock workload We'll use it to add a regression test for the BTF augmentation of enum arguments for tracepoints in 'perf trace': root@x1:~# perf trace -e landlock_add_rule perf test -w landlock 0.000 ( 0.009 ms): perf/747160 landlock_add_rule(ruleset_fd: 11, rule_type: LANDLOCK_RULE_PATH_BENEATH, rule_attr: 0x7ffd8e258594, flags: 45) = -1 EINVAL (Invalid argument) 0.011 ( 0.002 ms): perf/747160 landlock_add_rule(ruleset_fd: 11, rule_type: LANDLOCK_RULE_NET_PORT, rule_attr: 0x7ffd8e2585a0, flags: 45) = -1 EINVAL (Invalid argument) root@x1:~# Committer notes: It was agreed on the discussion (see Link below) to shorten then name of the workload from 'landlock_add_rule' to 'landlock', and I moved it to a separate patch. Also, to address a build failure from Namhyung, I stopped loading linux/landlock.h and instead added the used defines, enums and types to make this build in older systems. All we want is to emit the syscall and intercept it. Suggested-by: Arnaldo Carvalho de Melo Signed-off-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/CAH0uvohaypdTV6Z7O5QSK+va_qnhZ6BP6oSJ89s1c1E0CjgxDA@mail.gmail.com Link: https://lore.kernel.org/r/20240624181345.124764-1-howardchu95@gmail.com Link: https://lore.kernel.org/r/20240624181345.124764-6-howardchu95@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/builtin-test.c | 1 + tools/perf/tests/tests.h | 1 + tools/perf/tests/workloads/Build | 1 + tools/perf/tests/workloads/landlock.c | 66 +++++++++++++++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 tools/perf/tests/workloads/landlock.c diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c index c3d84b67ca8e..470a9709427d 100644 --- a/tools/perf/tests/builtin-test.c +++ b/tools/perf/tests/builtin-test.c @@ -152,6 +152,7 @@ static struct test_workload *workloads[] = { &workload__sqrtloop, &workload__brstack, &workload__datasym, + &workload__landlock, }; static int num_subtests(const struct test_suite *t) diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h index 3aa7701ee0e9..6ea2be86b7bf 100644 --- a/tools/perf/tests/tests.h +++ b/tools/perf/tests/tests.h @@ -205,6 +205,7 @@ DECLARE_WORKLOAD(leafloop); DECLARE_WORKLOAD(sqrtloop); DECLARE_WORKLOAD(brstack); DECLARE_WORKLOAD(datasym); +DECLARE_WORKLOAD(landlock); extern const char *dso_to_test; extern const char *test_objdump_path; diff --git a/tools/perf/tests/workloads/Build b/tools/perf/tests/workloads/Build index 48bf0d3b0f3d..5af17206f04d 100644 --- a/tools/perf/tests/workloads/Build +++ b/tools/perf/tests/workloads/Build @@ -6,6 +6,7 @@ perf-test-y += leafloop.o perf-test-y += sqrtloop.o perf-test-y += brstack.o perf-test-y += datasym.o +perf-test-y += landlock.o CFLAGS_sqrtloop.o = -g -O0 -fno-inline -U_FORTIFY_SOURCE CFLAGS_leafloop.o = -g -O0 -fno-inline -fno-omit-frame-pointer -U_FORTIFY_SOURCE diff --git a/tools/perf/tests/workloads/landlock.c b/tools/perf/tests/workloads/landlock.c new file mode 100644 index 000000000000..e2b5ef647c09 --- /dev/null +++ b/tools/perf/tests/workloads/landlock.c @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include +#include +#include +#include "../tests.h" + +/* This workload was initially added to test enum augmentation with BTF in perf + * trace because its the only syscall that has an enum argument. Since it is + * a recent addition to the Linux kernel (at the time of the introduction of this + * 'perf test' workload) we just add the required types and defines here instead + * of including linux/landlock, that isn't available in older systems. + * + * We are not interested in the the result of the syscall, just in intercepting + * its arguments. + */ + +#ifndef __NR_landlock_add_rule +#define __NR_landlock_add_rule 445 +#endif + +#ifndef LANDLOCK_ACCESS_FS_READ_FILE +#define LANDLOCK_ACCESS_FS_READ_FILE (1ULL << 2) + +#define LANDLOCK_RULE_PATH_BENEATH 1 + +struct landlock_path_beneath_attr { + __u64 allowed_access; + __s32 parent_fd; +}; +#endif + +#ifndef LANDLOCK_ACCESS_NET_CONNECT_TCP +#define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1) + +#define LANDLOCK_RULE_NET_PORT 2 + +struct landlock_net_port_attr { + __u64 allowed_access; + __u64 port; +}; +#endif + +static int landlock(int argc __maybe_unused, const char **argv __maybe_unused) +{ + int fd = 11, flags = 45; + + struct landlock_path_beneath_attr path_beneath_attr = { + .allowed_access = LANDLOCK_ACCESS_FS_READ_FILE, + .parent_fd = 14, + }; + + struct landlock_net_port_attr net_port_attr = { + .port = 19, + .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP, + }; + + syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_PATH_BENEATH, + &path_beneath_attr, flags); + + syscall(__NR_landlock_add_rule, fd, LANDLOCK_RULE_NET_PORT, + &net_port_attr, flags); + + return 0; +} + +DEFINE_WORKLOAD(landlock); From d66763fed30f0bd8cc8fb2c8c144c69fcb560bda Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Tue, 25 Jun 2024 02:13:43 +0800 Subject: [PATCH 020/347] perf test trace_btf_enum: Add regression test for the BTF augmentation of enums in 'perf trace' Trace landlock_add_rule syscall to see if the output is desirable. Trace the non-syscall tracepoint 'timer:hrtimer_init' and 'timer:hrtimer_start', see if the 'mode' argument is augmented, the 'mode' enum argument has the prefix of 'HRTIMER_MODE_' in its name. Committer testing: root@x1:~# perf test enum 124: perf trace enum augmentation tests : Ok root@x1:~# perf test -v enum 124: perf trace enum augmentation tests : Ok root@x1:~# perf trace -e landlock_add_rule perf test -v enum 0.000 ( 0.010 ms): perf/749827 landlock_add_rule(ruleset_fd: 11, rule_type: LANDLOCK_RULE_PATH_BENEATH, rule_attr: 0x7ffd324171d4, flags: 45) = -1 EINVAL (Invalid argument) 0.012 ( 0.002 ms): perf/749827 landlock_add_rule(ruleset_fd: 11, rule_type: LANDLOCK_RULE_NET_PORT, rule_attr: 0x7ffd324171e0, flags: 45) = -1 EINVAL (Invalid argument) 457.821 ( 0.007 ms): perf/749830 landlock_add_rule(ruleset_fd: 11, rule_type: LANDLOCK_RULE_PATH_BENEATH, rule_attr: 0x7ffd4acd31e4, flags: 45) = -1 EINVAL (Invalid argument) 457.832 ( 0.003 ms): perf/749830 landlock_add_rule(ruleset_fd: 11, rule_type: LANDLOCK_RULE_NET_PORT, rule_attr: 0x7ffd4acd31f0, flags: 45) = -1 EINVAL (Invalid argument) 124: perf trace enum augmentation tests : Ok root@x1:~# Suggested-by: Arnaldo Carvalho de Melo Signed-off-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/20240619082042.4173621-6-howardchu95@gmail.com Link: https://lore.kernel.org/r/20240624181345.124764-7-howardchu95@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/trace_btf_enum.sh | 61 ++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100755 tools/perf/tests/shell/trace_btf_enum.sh diff --git a/tools/perf/tests/shell/trace_btf_enum.sh b/tools/perf/tests/shell/trace_btf_enum.sh new file mode 100755 index 000000000000..7d407b52bea5 --- /dev/null +++ b/tools/perf/tests/shell/trace_btf_enum.sh @@ -0,0 +1,61 @@ +#!/bin/sh +# perf trace enum augmentation tests +# SPDX-License-Identifier: GPL-2.0 + +err=0 +set -e + +syscall="landlock_add_rule" +non_syscall="timer:hrtimer_init,timer:hrtimer_start" + +TESTPROG="perf test -w landlock" + +. "$(dirname $0)"/lib/probe.sh +skip_if_no_perf_trace || exit 2 + +check_vmlinux() { + echo "Checking if vmlinux exists" + if ! ls /sys/kernel/btf/vmlinux 1>/dev/null 2>&1 + then + echo "trace+enum test [Skipped missing vmlinux BTF support]" + err=2 + fi +} + +trace_landlock() { + echo "Tracing syscall ${syscall}" + + # test flight just to see if landlock_add_rule and libbpf are available + $TESTPROG + + if perf trace -e $syscall $TESTPROG 2>&1 | \ + grep -q -E ".*landlock_add_rule\(ruleset_fd: 11, rule_type: (LANDLOCK_RULE_PATH_BENEATH|LANDLOCK_RULE_NET_PORT), rule_attr: 0x[a-f0-9]+, flags: 45\) = -1.*" + then + err=0 + else + err=1 + fi +} + +trace_non_syscall() { + echo "Tracing non-syscall tracepoint ${non-syscall}" + if perf trace -e $non_syscall --max-events=1 2>&1 | \ + grep -q -E '.*timer:hrtimer_.*\(.*mode: HRTIMER_MODE_.*\)$' + then + err=0 + else + err=1 + fi +} + +check_vmlinux + +if [ $err = 0 ]; then + trace_landlock +fi + +if [ $err = 0 ]; then + trace_non_syscall +fi + +exit $err From 62284329b194606f73252935cc422cf6156e811a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 25 Jun 2024 02:13:44 +0800 Subject: [PATCH 021/347] perf trace: Introduce trace__btf_scnprintf() To have a central place that will look at the BTF type and call the right scnprintf routine or return zero, meaning BTF pretty printing isn't available or not implemented for a specific type. Signed-off-by: Howard Chu Tested-by: Howard Chu Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240624181345.124764-8-howardchu95@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 49 +++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index e664001d5ed7..d9104fc4f61f 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -981,18 +981,28 @@ static size_t btf_enum_scnprintf(const struct btf_type *type, struct btf *btf, c static size_t trace__btf_enum_scnprintf(struct trace *trace, struct syscall_arg_fmt *arg_fmt, char *bf, size_t size, int val, char *type) { - if (trace->btf == NULL) - return 0; - if (syscall_arg_fmt__cache_btf_enum(arg_fmt, trace->btf, type) < 0) return 0; return btf_enum_scnprintf(arg_fmt->type, trace->btf, bf, size, val); } + +static size_t trace__btf_scnprintf(struct trace *trace, struct syscall_arg_fmt *arg_fmt, char *bf, + size_t size, int val, char *type) +{ + if (trace->btf == NULL) + return 0; + + if (arg_fmt->is_enum) + return trace__btf_enum_scnprintf(trace, arg_fmt, bf, size, val, type); + + return 0; +} + #else // HAVE_LIBBPF_SUPPORT -static size_t trace__btf_enum_scnprintf(struct trace *trace __maybe_unused, struct syscall_arg_fmt *arg_fmt __maybe_unused, - char *bf __maybe_unused, size_t size __maybe_unused, int val __maybe_unused, - char *type __maybe_unused) +static size_t trace__btf_scnprintf(struct trace *trace __maybe_unused, struct syscall_arg_fmt *arg_fmt __maybe_unused, + char *bf __maybe_unused, size_t size __maybe_unused, int val __maybe_unused, + char *type __maybe_unused) { return 0; } @@ -2183,7 +2193,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 = { @@ -2237,13 +2247,11 @@ 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); - if (sc->arg_fmt[arg.idx].is_enum) { - size_t p = trace__btf_enum_scnprintf(trace, &sc->arg_fmt[arg.idx], bf + printed, - size - printed, val, field->type); - if (p) { - printed += p; - continue; - } + btf_printed = trace__btf_scnprintf(trace, &sc->arg_fmt[arg.idx], 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], @@ -2892,7 +2900,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 = { @@ -2942,13 +2950,10 @@ 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); - if (arg->is_enum) { - size_t p = trace__btf_enum_scnprintf(trace, arg, bf + printed, - size - printed, val, field->type); - if (p) { - printed += p; - continue; - } + 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(arg, bf + printed, size - printed, &syscall_arg, val); From c3d747134cec49aa95aad22d14deffa43b2be37d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 25 Jun 2024 02:13:45 +0800 Subject: [PATCH 022/347] perf trace: Remove arg_fmt->is_enum, we can get that from the BTF type This is to pave the way for other BTF types, i.e. we try to find BTF type then use things like btf_is_enum(btf_type) that we cached to find the right strtoul and scnprintf routines. For now only enum is supported, all the other types simple return zero for scnprintf which makes it have the same behaviour as when BTF isn't available, i.e. fallback to no pretty printing. Ditto for strtoul. root@x1:~# perf test -v enum 124: perf trace enum augmentation tests : Ok root@x1:~# perf test -v enum 124: perf trace enum augmentation tests : Ok root@x1:~# perf test -v enum 124: perf trace enum augmentation tests : Ok root@x1:~# perf test -v enum 124: perf trace enum augmentation tests : Ok root@x1:~# perf test -v enum 124: perf trace enum augmentation tests : Ok root@x1:~# Signed-off-by: Howard Chu Tested-by: Howard Chu Cc: Adrian Hunter Cc: Howard Chu Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240624181345.124764-9-howardchu95@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 105 +++++++++++++++++-------------- tools/perf/trace/beauty/beauty.h | 1 + 2 files changed, 58 insertions(+), 48 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index d9104fc4f61f..488c2cedc110 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -111,7 +111,6 @@ struct syscall_arg_fmt { const char *name; u16 nr_entries; // for arrays bool show_zero; - bool is_enum; #ifdef HAVE_LIBBPF_SUPPORT const struct btf_type *type; #endif @@ -910,46 +909,29 @@ 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 int syscall_arg_fmt__cache_btf_enum(struct syscall_arg_fmt *arg_fmt, struct btf *btf, char *type) +static void syscall_arg_fmt__cache_btf_enum(struct syscall_arg_fmt *arg_fmt, struct btf *btf, char *type) { int id; - // Already cached? - if (arg_fmt->type != NULL) - return 0; - type = strstr(type, "enum "); if (type == NULL) - return -1; + return; type += 5; // skip "enum " to get the enumeration name id = btf__find_by_name(btf, type); if (id < 0) - return -1; + return; arg_fmt->type = btf__type_by_id(btf, id); - return arg_fmt->type == NULL ? -1 : 0; } static bool syscall_arg__strtoul_btf_enum(char *bf, size_t size, struct syscall_arg *arg, u64 *val) { - const struct btf_type *bt; - char *type = arg->parm; - struct btf_enum *be; - struct btf *btf; + const struct btf_type *bt = arg->fmt->type; + struct btf *btf = arg->trace->btf; + struct btf_enum *be = btf_enum(bt); - trace__load_vmlinux_btf(arg->trace); - - btf = arg->trace->btf; - if (btf == NULL) - return false; - - if (syscall_arg_fmt__cache_btf_enum(arg->fmt, btf, type) < 0) - return false; - - bt = arg->fmt->type; - 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)); @@ -963,6 +945,35 @@ static bool syscall_arg__strtoul_btf_enum(char *bf, size_t size, struct syscall_ 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); @@ -978,23 +989,23 @@ static size_t btf_enum_scnprintf(const struct btf_type *type, struct btf *btf, c return 0; } -static size_t trace__btf_enum_scnprintf(struct trace *trace, struct syscall_arg_fmt *arg_fmt, char *bf, - size_t size, int val, char *type) -{ - if (syscall_arg_fmt__cache_btf_enum(arg_fmt, trace->btf, type) < 0) - return 0; - - return btf_enum_scnprintf(arg_fmt->type, trace->btf, bf, size, val); -} - static size_t trace__btf_scnprintf(struct trace *trace, struct syscall_arg_fmt *arg_fmt, char *bf, size_t size, int val, char *type) { if (trace->btf == NULL) return 0; - if (arg_fmt->is_enum) - return trace__btf_enum_scnprintf(trace, arg_fmt, bf, size, val, type); + 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); return 0; } @@ -1007,14 +1018,14 @@ static size_t trace__btf_scnprintf(struct trace *trace __maybe_unused, struct sy return 0; } -static bool syscall_arg__strtoul_btf_enum(char *bf __maybe_unused, size_t size __maybe_unused, +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_ENUM syscall_arg__strtoul_btf_enum +#define STUL_BTF_TYPE syscall_arg__strtoul_btf_type #define STRARRAY(name, array) \ { .scnprintf = SCA_STRARRAY, \ @@ -1887,7 +1898,6 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field continue; len = strlen(field->name); - arg->is_enum = false; if (strcmp(field->type, "const char *") == 0 && ((len >= 4 && strcmp(field->name + len - 4, "name") == 0) || @@ -1915,8 +1925,8 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field */ arg->scnprintf = SCA_FD; } else if (strstr(field->type, "enum") && use_btf != NULL) { - *use_btf = arg->is_enum = true; - arg->strtoul = STUL_BTF_ENUM; + *use_btf = true; + arg->strtoul = STUL_BTF_TYPE; } else { const struct syscall_arg_fmt *fmt = syscall_arg_fmt__find_by_name(field->name); @@ -2236,10 +2246,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].is_enum)) + !(sc->arg_fmt && sc->arg_fmt[arg.idx].strtoul == STUL_BTF_TYPE)) continue; printed += scnprintf(bf + printed, size - printed, "%s", printed ? ", " : ""); @@ -2942,7 +2955,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 && !arg->is_enum) + if (val == 0 && !trace->show_zeros && !arg->show_zero && arg->strtoul != STUL_BTF_TYPE) continue; printed += scnprintf(bf + printed, size - printed, "%s", printed ? ", " : ""); @@ -3910,14 +3923,10 @@ static int trace__expand_filter(struct trace *trace, struct evsel *evsel) struct syscall_arg syscall_arg = { .trace = trace, .fmt = fmt, + .type_name = type, + .parm = fmt->parm, }; - if (fmt->is_enum) { - syscall_arg.parm = type; - } else { - syscall_arg.parm = fmt->parm; - } - if (fmt->strtoul(right, right_size, &syscall_arg, &val)) { char *n, expansion[19]; int expansion_lenght = scnprintf(expansion, sizeof(expansion), "%#" PRIx64, val); diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index 78d10d92d351..3ed11e18ee2d 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h @@ -113,6 +113,7 @@ struct syscall_arg { struct thread *thread; struct trace *trace; void *parm; + char *type_name; u16 len; u8 idx; u8 mask; From e293f4b1e57fcc4d7d34b7a7a44ebec8ba8a1b7d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 17 Jul 2024 09:57:17 -0700 Subject: [PATCH 023/347] perf test: Avoid python leak sanitizer test failures Leak sanitizer will report memory leaks from python and the leak sanitizer output causes tests to fail. For example: ``` $ perf test 98 -v 98: perf script tests: --- start --- test child forked, pid 1272962 DB test [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.046 MB /tmp/perf-test-script.x0EktdCel8/perf.data (8 samples) ] call_path_table((1, 0, 0, 0) call_path_table((2, 1, 0, 140339508617447) call_path_table((3, 2, 2, 0) call_path_table((4, 3, 3, 0) call_path_table((5, 4, 4, 0) call_path_table((6, 5, 5, 0) call_path_table((7, 6, 6, 0) call_path_table((8, 7, 7, 0) call_path_table((9, 8, 8, 0) call_path_table((10, 9, 9, 0) call_path_table((11, 10, 10, 0) call_path_table((12, 11, 11, 0) call_path_table((13, 12, 1, 0) sample_table((1, 1, 1, 1, 1, 1, 1, 8, -2058824120, 588306954119000, -1, 0, 0, 0, 0, 1, 0, 0, 128933429281, 0, 0, 13, 0, 0, 0, -1, -1)) sample_table((2, 1, 1, 1, 1, 1, 1, 8, -2058824120, 588306954137053, -1, 0, 0, 0, 0, 1, 0, 0, 128933429281, 0, 0, 13, 0, 0, 0, -1, -1)) sample_table((3, 1, 1, 1, 1, 1, 1, 8, -2058824120, 588306954140089, -1, 0, 0, 0, 0, 9, 0, 0, 128933429281, 0, 0, 13, 0, 0, 0, -1, -1)) sample_table((4, 1, 1, 1, 1, 1, 1, 8, -2058824120, 588306954142376, -1, 0, 0, 0, 0, 155, 0, 0, 128933429281, 0, 0, 13, 0, 0, 0, -1, -1)) sample_table((5, 1, 1, 1, 1, 1, 1, 8, -2058824120, 588306954144045, -1, 0, 0, 0, 0, 2493, 0, 0, 128933429281, 0, 0, 13, 0, 0, 0, -1, -1)) sample_table((6, 1, 1, 1, 1, 1, 12, 77, -2046828595, 588306954145722, -1, 0, 0, 0, 0, 47555, 0, 0, 128933429281, 0, 0, 13, 0, 0, 0, -1, -1)) call_path_table((14, 9, 14, 0) call_path_table((15, 14, 15, 0) call_path_table((16, 15, 0, -1040969624) call_path_table((17, 16, 16, 0) call_path_table((18, 17, 17, 0) call_path_table((19, 18, 18, 0) call_path_table((20, 19, 19, 0) call_path_table((21, 20, 13, 0) sample_table((7, 1, 1, 1, 2, 1, 13, 46, -2053700898, 588306954157436, -1, 0, 0, 0, 0, 964078, 0, 0, 128933429281, 0, 0, 21, 0, 0, 0, -1, -1)) call_path_table((22, 1, 21, 0) call_path_table((23, 22, 22, 0) call_path_table((24, 23, 23, 0) call_path_table((25, 24, 24, 0) call_path_table((26, 25, 25, 0) call_path_table((27, 26, 26, 0) call_path_table((28, 27, 27, 0) call_path_table((29, 28, 28, 0) call_path_table((30, 29, 29, 0) call_path_table((31, 30, 30, 0) call_path_table((32, 31, 31, 0) call_path_table((33, 32, 32, 0) call_path_table((34, 33, 33, 0) call_path_table((35, 34, 20, 0) sample_table((8, 1, 1, 1, 2, 1, 20, 49, -2046878127, 588306954378624, -1, 0, 0, 0, 0, 2534317, 0, 0, 128933429281, 0, 0, 35, 0, 0, 0, -1, -1)) ================================================================= ==1272975==ERROR: LeakSanitizer: detected memory leaks Direct leak of 13628 byte(s) in 6 object(s) allocated from: #0 0x56354f60c092 in malloc (/tmp/perf/perf+0x29c092) #1 0x7ff25c7d02e7 in _PyObject_Malloc /build/python3.11/../Objects/obmalloc.c:2003:11 #2 0x7ff25c7d02e7 in _PyObject_Malloc /build/python3.11/../Objects/obmalloc.c:1996:1 SUMMARY: AddressSanitizer: 13628 byte(s) leaked in 6 allocation(s). --- Cleaning up --- ---- end(-1) ---- 98: perf script tests : FAILED! ``` Disable leak sanitizer when running specific perf+python tests to avoid this. This causes the tests to pass when run with leak sanitizer. Reviewed-by: Aditya Gupta Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Richter Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/script.sh | 3 +++ tools/perf/tests/shell/test_task_analyzer.sh | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tools/perf/tests/shell/script.sh b/tools/perf/tests/shell/script.sh index c1a603653662..d3e2958d2242 100755 --- a/tools/perf/tests/shell/script.sh +++ b/tools/perf/tests/shell/script.sh @@ -61,7 +61,10 @@ _end_of_file_ esac perf record $cmd_flags -o "${perfdatafile}" true + # Disable lsan to avoid warnings about python memory leaks. + export ASAN_OPTIONS=detect_leaks=0 perf script -i "${perfdatafile}" -s "${db_test}" + export ASAN_OPTIONS= echo "DB test [Success]" } diff --git a/tools/perf/tests/shell/test_task_analyzer.sh b/tools/perf/tests/shell/test_task_analyzer.sh index 92d15154ba79..cb02bf23e6a5 100755 --- a/tools/perf/tests/shell/test_task_analyzer.sh +++ b/tools/perf/tests/shell/test_task_analyzer.sh @@ -11,6 +11,9 @@ if [ -e "$perfdir/scripts/python/Perf-Trace-Util" ]; then export PERF_EXEC_PATH=$perfdir fi +# Disable lsan to avoid warnings about python memory leaks. +export ASAN_OPTIONS=detect_leaks=0 + cleanup() { rm -f perf.data rm -f perf.data.old From 1d303deedb1057c6ca36fc0d1c0d7bf58a5b7322 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:44 +0530 Subject: [PATCH 024/347] perf annotate: Move the data structures related to register type to header file Data type profiling uses instruction tracking by checking each instruction and updating the register type state in some data structures. This is useful to find the data type in cases when the register state gets transferred from one reg to another. Example, in x86, "mov" instruction and in powerpc, "mr" instruction. Currently these structures are defined in annotate-data.c and instruction tracking is implemented only for x86. Move these data structures to "annotate-data.h" header file so that other arch implementations can use it in arch specific files as well. Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-2-atrajeev@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 53 +------------------------------ tools/perf/util/annotate-data.h | 56 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 52 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 965da6c0b542..a4c7f98a75e3 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -31,15 +31,6 @@ static void delete_var_types(struct die_var_type *var_types); -enum type_state_kind { - TSR_KIND_INVALID = 0, - TSR_KIND_TYPE, - TSR_KIND_PERCPU_BASE, - TSR_KIND_CONST, - TSR_KIND_POINTER, - TSR_KIND_CANARY, -}; - #define pr_debug_dtp(fmt, ...) \ do { \ if (debug_type_profile) \ @@ -140,49 +131,7 @@ static void pr_debug_location(Dwarf_Die *die, u64 pc, int reg) } } -/* - * Type information in a register, valid when @ok is true. - * The @caller_saved registers are invalidated after a function call. - */ -struct type_state_reg { - Dwarf_Die type; - u32 imm_value; - bool ok; - bool caller_saved; - u8 kind; -}; - -/* Type information in a stack location, dynamically allocated */ -struct type_state_stack { - struct list_head list; - Dwarf_Die type; - int offset; - int size; - bool compound; - u8 kind; -}; - -/* FIXME: This should be arch-dependent */ -#define TYPE_STATE_MAX_REGS 16 - -/* - * State table to maintain type info in each register and stack location. - * It'll be updated when new variable is allocated or type info is moved - * to a new location (register or stack). As it'd be used with the - * shortest path of basic blocks, it only maintains a single table. - */ -struct type_state { - /* state of general purpose registers */ - struct type_state_reg regs[TYPE_STATE_MAX_REGS]; - /* state of stack location */ - struct list_head stack_vars; - /* return value register */ - int ret_reg; - /* stack pointer register */ - int stack_reg; -}; - -static bool has_reg_type(struct type_state *state, int reg) +bool has_reg_type(struct type_state *state, int reg) { return (unsigned)reg < ARRAY_SIZE(state->regs); } diff --git a/tools/perf/util/annotate-data.h b/tools/perf/util/annotate-data.h index 0a57d9f5ee78..cdb5cd8960bb 100644 --- a/tools/perf/util/annotate-data.h +++ b/tools/perf/util/annotate-data.h @@ -6,6 +6,11 @@ #include #include #include +#include "annotate.h" + +#ifdef HAVE_DWARF_SUPPORT +#include "debuginfo.h" +#endif struct annotated_op_loc; struct debuginfo; @@ -15,6 +20,15 @@ struct hist_entry; struct map_symbol; struct thread; +enum type_state_kind { + TSR_KIND_INVALID = 0, + TSR_KIND_TYPE, + TSR_KIND_PERCPU_BASE, + TSR_KIND_CONST, + TSR_KIND_POINTER, + TSR_KIND_CANARY, +}; + /** * struct annotated_member - Type of member field * @node: List entry in the parent list @@ -143,6 +157,47 @@ struct annotated_data_stat { extern struct annotated_data_stat ann_data_stat; #ifdef HAVE_DWARF_SUPPORT +/* + * Type information in a register, valid when @ok is true. + * The @caller_saved registers are invalidated after a function call. + */ +struct type_state_reg { + Dwarf_Die type; + u32 imm_value; + bool ok; + bool caller_saved; + u8 kind; +}; + +/* Type information in a stack location, dynamically allocated */ +struct type_state_stack { + struct list_head list; + Dwarf_Die type; + int offset; + int size; + bool compound; + u8 kind; +}; + +/* FIXME: This should be arch-dependent */ +#define TYPE_STATE_MAX_REGS 16 + +/* + * State table to maintain type info in each register and stack location. + * It'll be updated when new variable is allocated or type info is moved + * to a new location (register or stack). As it'd be used with the + * shortest path of basic blocks, it only maintains a single table. + */ +struct type_state { + /* state of general purpose registers */ + struct type_state_reg regs[TYPE_STATE_MAX_REGS]; + /* state of stack location */ + struct list_head stack_vars; + /* return value register */ + int ret_reg; + /* stack pointer register */ + int stack_reg; +}; /* Returns data type at the location (ip, reg, offset) */ struct annotated_data_type *find_data_type(struct data_loc_info *dloc); @@ -160,6 +215,7 @@ void global_var_type__tree_delete(struct rb_root *root); int hist_entry__annotate_data_tty(struct hist_entry *he, struct evsel *evsel); +bool has_reg_type(struct type_state *state, int reg); #else /* HAVE_DWARF_SUPPORT */ static inline struct annotated_data_type * From 782959ac248ac3cbac80f7476d4e0410662ff400 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:45 +0530 Subject: [PATCH 025/347] perf annotate: Add "update_insn_state" callback function to handle arch specific instruction tracking Add "update_insn_state" callback to "struct arch" to handle instruction tracking. Currently updating instruction state is handled by static function "update_insn_state_x86" which is defined in "annotate-data.c". Make this as a callback for specific arch and move to archs specific file "arch/x86/annotate/instructions.c" . This will help to add helper function for other platforms in file: "arch//annotate/instructions.c" and make changes/updates easier. Define callback "update_insn_state" as part of "struct arch", also make some of the debug functions non-static so that it can be referenced from other places. Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-3-atrajeev@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/annotate/instructions.c | 377 +++++++++++++++++++ tools/perf/util/annotate-data.c | 391 +------------------- tools/perf/util/annotate-data.h | 23 ++ tools/perf/util/disasm.c | 4 + tools/perf/util/disasm.h | 12 + 5 files changed, 424 insertions(+), 383 deletions(-) diff --git a/tools/perf/arch/x86/annotate/instructions.c b/tools/perf/arch/x86/annotate/instructions.c index 5cdf457f5cbe..7b7d462c6c6b 100644 --- a/tools/perf/arch/x86/annotate/instructions.c +++ b/tools/perf/arch/x86/annotate/instructions.c @@ -206,3 +206,380 @@ 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]; + + 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; + 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]; + 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->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); + } + /* 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]; + +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; + + pr_debug_dtp("mov [%x] percpu base reg%d\n", + insn_offset, dst->reg1); + } + } + + 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 diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index a4c7f98a75e3..7a48c3d72b89 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -39,7 +39,7 @@ do { \ pr_debug3(fmt, ##__VA_ARGS__); \ } while (0) -static void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind) +void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind) { struct strbuf sb; char *str; @@ -390,7 +390,7 @@ static int check_variable(struct data_loc_info *dloc, Dwarf_Die *var_die, return 0; } -static struct type_state_stack *find_stack_state(struct type_state *state, +struct type_state_stack *find_stack_state(struct type_state *state, int offset) { struct type_state_stack *stack; @@ -406,7 +406,7 @@ static struct type_state_stack *find_stack_state(struct type_state *state, return NULL; } -static void set_stack_state(struct type_state_stack *stack, int offset, u8 kind, +void set_stack_state(struct type_state_stack *stack, int offset, u8 kind, Dwarf_Die *type_die) { int tag; @@ -433,7 +433,7 @@ static void set_stack_state(struct type_state_stack *stack, int offset, u8 kind, } } -static struct type_state_stack *findnew_stack_state(struct type_state *state, +struct type_state_stack *findnew_stack_state(struct type_state *state, int offset, u8 kind, Dwarf_Die *type_die) { @@ -537,7 +537,7 @@ void global_var_type__tree_delete(struct rb_root *root) } } -static bool get_global_var_info(struct data_loc_info *dloc, u64 addr, +bool get_global_var_info(struct data_loc_info *dloc, u64 addr, const char **var_name, int *var_offset) { struct addr_location al; @@ -611,7 +611,7 @@ static void global_var__collect(struct data_loc_info *dloc) } } -static bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc, +bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc, u64 ip, u64 var_addr, int *var_offset, Dwarf_Die *type_die) { @@ -722,381 +722,6 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo } } -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]; - - 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; - 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]; - 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->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); - } - /* 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]; - -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; - - pr_debug_dtp("mov [%x] percpu base reg%d\n", - insn_offset, dst->reg1); - } - } - - 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) */ -} - /** * update_insn_state - Update type state for an instruction * @state: type state table @@ -1115,8 +740,8 @@ retry: static void update_insn_state(struct type_state *state, struct data_loc_info *dloc, Dwarf_Die *cu_die, struct disasm_line *dl) { - if (arch__is(dloc->arch, "x86")) - update_insn_state_x86(state, dloc, cu_die, dl); + if (dloc->arch->update_insn_state) + dloc->arch->update_insn_state(state, dloc, cu_die, dl); } /* diff --git a/tools/perf/util/annotate-data.h b/tools/perf/util/annotate-data.h index cdb5cd8960bb..6fe8ee8b8410 100644 --- a/tools/perf/util/annotate-data.h +++ b/tools/perf/util/annotate-data.h @@ -6,6 +6,7 @@ #include #include #include +#include "dwarf-regs.h" #include "annotate.h" #ifdef HAVE_DWARF_SUPPORT @@ -20,6 +21,14 @@ struct hist_entry; struct map_symbol; struct thread; +#define pr_debug_dtp(fmt, ...) \ +do { \ + if (debug_type_profile) \ + pr_info(fmt, ##__VA_ARGS__); \ + else \ + pr_debug3(fmt, ##__VA_ARGS__); \ +} while (0) + enum type_state_kind { TSR_KIND_INVALID = 0, TSR_KIND_TYPE, @@ -216,6 +225,20 @@ void global_var_type__tree_delete(struct rb_root *root); int hist_entry__annotate_data_tty(struct hist_entry *he, struct evsel *evsel); bool has_reg_type(struct type_state *state, int reg); +struct type_state_stack *findnew_stack_state(struct type_state *state, + int offset, u8 kind, + Dwarf_Die *type_die); +void set_stack_state(struct type_state_stack *stack, int offset, u8 kind, + Dwarf_Die *type_die); +struct type_state_stack *find_stack_state(struct type_state *state, + int offset); +bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc, + u64 ip, u64 var_addr, int *var_offset, + Dwarf_Die *type_die); +bool get_global_var_info(struct data_loc_info *dloc, u64 addr, + const char **var_name, int *var_offset); +void pr_debug_type_name(Dwarf_Die *die, enum type_state_kind kind); + #else /* HAVE_DWARF_SUPPORT */ static inline struct annotated_data_type * diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index e10558b79504..931cd92dcc40 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -12,6 +12,7 @@ #include #include "annotate.h" +#include "annotate-data.h" #include "build-id.h" #include "debug.h" #include "disasm.h" @@ -145,6 +146,9 @@ static struct arch architectures[] = { .memory_ref_char = '(', .imm_char = '$', }, +#ifdef HAVE_DWARF_SUPPORT + .update_insn_state = update_insn_state_x86, +#endif }, { .name = "powerpc", diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h index 3d381a043520..c835759c8e2b 100644 --- a/tools/perf/util/disasm.h +++ b/tools/perf/util/disasm.h @@ -4,11 +4,18 @@ #include "map_symbol.h" +#ifdef HAVE_DWARF_SUPPORT +#include "dwarf-aux.h" +#endif + struct annotation_options; struct disasm_line; struct ins; struct evsel; struct symbol; +struct data_loc_info; +struct type_state; +struct disasm_line; struct arch { const char *name; @@ -32,6 +39,11 @@ struct arch { char memory_ref_char; char imm_char; } objdump; +#ifdef HAVE_DWARF_SUPPORT + void (*update_insn_state)(struct type_state *state, + struct data_loc_info *dloc, Dwarf_Die *cu_die, + struct disasm_line *dl); +#endif }; struct ins { From b1d8d968a7983e9756de34b8bcaa24cc339828ed Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:46 +0530 Subject: [PATCH 026/347] perf annotate: Update TYPE_STATE_MAX_REGS to include max of regs in powerpc TYPE_STATE_MAX_REGS is arch-dependent. Currently this is defined to be 16. While checking if reg is valid using has_reg_type, max value is checked using TYPE_STATE_MAX_REGS value. Define this conditionally for powerpc. Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-4-atrajeev@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/perf/util/annotate-data.h b/tools/perf/util/annotate-data.h index 6fe8ee8b8410..992b7ce4bd11 100644 --- a/tools/perf/util/annotate-data.h +++ b/tools/perf/util/annotate-data.h @@ -189,7 +189,11 @@ struct type_state_stack { }; /* FIXME: This should be arch-dependent */ +#ifdef __powerpc__ +#define TYPE_STATE_MAX_REGS 32 +#else #define TYPE_STATE_MAX_REGS 16 +#endif /* * State table to maintain type info in each register and stack location. From 06dd4c5a561c48c66745352bae0b2c04bbe455be Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:47 +0530 Subject: [PATCH 027/347] perf annotate: Add disasm_line__parse() to parse raw instruction for powerpc Currently, the perf tool infrastructure uses the disasm_line__parse function to parse disassembled line. Example snippet from objdump: objdump --start-address=
--stop-address=
-d --no-show-raw-insn -C c0000000010224b4: lwz r10,0(r9) This line "lwz r10,0(r9)" is parsed to extract instruction name, registers names and offset. In powerpc, the approach for data type profiling uses raw instruction instead of result from objdump to identify the instruction category and extract the source/target registers. Example: 38 01 81 e8 ld r4,312(r1) Here "38 01 81 e8" is the raw instruction representation. Add function "disasm_line__parse_powerpc" to handle parsing of raw instruction. Also update "struct disasm_line" to save the binary code/ With the change, function captures: line -> "38 01 81 e8 ld r4,312(r1)" raw instruction "38 01 81 e8" Raw instruction is used later to extract the reg/offset fields. Macros are added to extract opcode and register fields. "struct disasm_line" is updated to carry union of "bytes" and "raw_insn" of 32 bit to carry raw code (raw). Function "disasm_line__parse_powerpc fills the raw instruction hex value and can use macros to get opcode. There is no changes in existing code paths, which parses the disassembled code. The size of raw instruction depends on architecture. In case of powerpc, the parsing the disasm line needs to handle cases for reading binary code directly from DSO as well as parsing the objdump result. Hence adding the logic into separate function instead of updating "disasm_line__parse". The architecture using the instruction name and present approach is not altered. Since this approach targets powerpc, the macro implementation is added for powerpc as of now. Since the disasm_line__parse is used in other cases (perf annotate) and not only data tye profiling, the powerpc callback includes changes to work with binary code as well as mnemonic representation. Also in case if the DSO read fails and libcapstone is not supported, the approach fallback to use objdump as option. Hence as option, patch has changes to ensure objdump option also works well. Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-5-atrajeev@linux.vnet.ibm.com [ Add check for strndup() result ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/linux/string.h | 2 + tools/lib/string.c | 13 +++++ .../perf/arch/powerpc/annotate/instructions.c | 1 + tools/perf/arch/powerpc/util/dwarf-regs.c | 9 ++++ tools/perf/util/annotate.h | 5 +- tools/perf/util/disasm.c | 51 ++++++++++++++++++- 6 files changed, 79 insertions(+), 2 deletions(-) diff --git a/tools/include/linux/string.h b/tools/include/linux/string.h index db5c99318c79..0acb1fc14e19 100644 --- a/tools/include/linux/string.h +++ b/tools/include/linux/string.h @@ -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_ */ diff --git a/tools/lib/string.c b/tools/lib/string.c index 8b6892f959ab..3126d2cff716 100644 --- a/tools/lib/string.c +++ b/tools/lib/string.c @@ -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. diff --git a/tools/perf/arch/powerpc/annotate/instructions.c b/tools/perf/arch/powerpc/annotate/instructions.c index a3f423c27cae..d57fd023ef9c 100644 --- a/tools/perf/arch/powerpc/annotate/instructions.c +++ b/tools/perf/arch/powerpc/annotate/instructions.c @@ -55,6 +55,7 @@ static int powerpc__annotate_init(struct arch *arch, char *cpuid __maybe_unused) arch->initialized = true; arch->associate_instruction_ops = powerpc__associate_instruction_ops; arch->objdump.comment_char = '#'; + annotate_opts.show_asm_raw = true; } return 0; diff --git a/tools/perf/arch/powerpc/util/dwarf-regs.c b/tools/perf/arch/powerpc/util/dwarf-regs.c index 0c4f4caf53ac..430623ca5612 100644 --- a/tools/perf/arch/powerpc/util/dwarf-regs.c +++ b/tools/perf/arch/powerpc/util/dwarf-regs.c @@ -98,3 +98,12 @@ 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 diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index d5c821c22f79..9ba772f46270 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -113,7 +113,10 @@ struct annotation_line { struct disasm_line { struct ins ins; struct ins_operands ops; - + union { + u8 bytes[4]; + u32 raw_insn; + } raw; /* This needs to be at the end. */ struct annotation_line al; }; diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 931cd92dcc40..6d4055b9b966 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -44,6 +44,7 @@ static int call__scnprintf(struct ins *ins, char *bf, size_t size, static void ins__sort(struct arch *arch); static int disasm_line__parse(char *line, const char **namep, char **rawp); +static int disasm_line__parse_powerpc(struct disasm_line *dl); static __attribute__((constructor)) void symbol__init_regexpr(void) { @@ -845,6 +846,51 @@ out: return -1; } +/* + * Parses the result captured from symbol__disassemble_* + * Example, line read from DSO file in powerpc: + * line: 38 01 81 e8 + * opcode: fetched from arch specific get_opcode_insn + * rawp_insn: e8810138 + * + * rawp_insn is used later to extract the reg/offset fields + */ +#define PPC_OP(op) (((op) >> 26) & 0x3F) +#define RAW_BYTES 11 + +static int disasm_line__parse_powerpc(struct disasm_line *dl) +{ + char *line = dl->al.line; + const char **namep = &dl->ins.name; + char **rawp = &dl->ops.raw; + char *tmp_raw_insn, *name_raw_insn = skip_spaces(line); + char *name = skip_spaces(name_raw_insn + RAW_BYTES); + int objdump = 0; + + if (strlen(line) > RAW_BYTES) + objdump = 1; + + if (name_raw_insn[0] == '\0') + return -1; + + if (objdump) { + disasm_line__parse(name, namep, rawp); + } else + *namep = ""; + + tmp_raw_insn = strndup(name_raw_insn, 11); + if (tmp_raw_insn == NULL) + return -1; + + remove_spaces(tmp_raw_insn); + + sscanf(tmp_raw_insn, "%x", &dl->raw.raw_insn); + if (objdump) + dl->raw.raw_insn = be32_to_cpu(dl->raw.raw_insn); + + return 0; +} + static void annotation_line__init(struct annotation_line *al, struct annotate_args *args, int nr) @@ -898,7 +944,10 @@ struct disasm_line *disasm_line__new(struct annotate_args *args) goto out_delete; if (args->offset != -1) { - if (disasm_line__parse(dl->al.line, &dl->ins.name, &dl->ops.raw) < 0) + if (arch__is(args->arch, "powerpc")) { + if (disasm_line__parse_powerpc(dl) < 0) + goto out_free_line; + } else if (disasm_line__parse(dl->al.line, &dl->ins.name, &dl->ops.raw) < 0) goto out_free_line; disasm_line__init_ins(dl, args->arch, &args->ms); From 0b971e6bf1c305843a034ca762718b9428a5dd7f Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:48 +0530 Subject: [PATCH 028/347] perf annotate: Add support to capture and parse raw instruction in powerpc using dso__data_read_offset utility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support to capture and parse raw instruction in powerpc. Currently, the perf tool infrastructure uses two ways to disassemble and understand the instruction. One is objdump and other option is via libcapstone. Currently, the perf tool infrastructure uses "--no-show-raw-insn" option with "objdump" while disassemble. Example from powerpc with this option for an instruction address is: Snippet from: objdump --start-address=
--stop-address=
-d --no-show-raw-insn -C c0000000010224b4: lwz r10,0(r9) This line "lwz r10,0(r9)" is parsed to extract instruction name, registers names and offset. Also to find whether there is a memory reference in the operands, "memory_ref_char" field of objdump is used. For x86, "(" is used as memory_ref_char to tackle instructions of the form "mov (%rax), %rcx". In case of powerpc, not all instructions using "(" are the only memory instructions. Example, above instruction can also be of extended form (X form) "lwzx r10,0,r19". Inorder to easy identify the instruction category and extract the source/target registers, patch adds support to use raw instruction for powerpc. Approach used is to read the raw instruction directly from the DSO file using "dso__data_read_offset" utility which is already implemented in perf infrastructure in "util/dso.c". Example: 38 01 81 e8 ld r4,312(r1) Here "38 01 81 e8" is the raw instruction representation. In powerpc, this translates to instruction form: "ld RT,DS(RA)" and binary code as: | 58 | RT | RA | DS | | ------------------------------------- 0 6 11 16 30 31 Function "symbol__disassemble_dso" is updated to read raw instruction directly from DSO using dso__data_read_offset utility. In case of above example, this captures: line: 38 01 81 e8 The above works well when 'perf report' is invoked with only sort keys for data type ie type and typeoff. Because there is no instruction level annotation needed if only data type information is requested for. For annotating sample, along with type and typeoff sort key, "sym" sort key is also needed. And by default invoking just "perf report" uses sort key "sym" that displays the symbol information. With approach changes in powerpc which first reads DSO for raw instruction, "perf annotate" and "perf report" + a key breaks since it doesn't do the instruction level disassembly. Snippet of result from 'perf report': Samples: 1K of event 'mem-loads', 4000 Hz, Event count (approx.): 937238 do_work /usr/bin/pmlogger [Percent: local period] Percent│ ea230010 │ 3a550010 │ 3a600000 │ 38f60001 │ 39490008 │ 42400438 51.44 │ 81290008 │ 7d485378 Here, raw instruction is displayed in the output instead of human readable annotated form. One way to get the appropriate data is to specify "--objdump path", by which code annotation will be done. But the default behaviour will be changed. To fix this breakage, check if "sym" sort key is set. If so fallback and use the libcapstone/objdump way of disassmbling the sample. With the changes and "perf report" Samples: 1K of event 'mem-loads', 4000 Hz, Event count (approx.): 937238 do_work /usr/bin/pmlogger [Percent: local period] Percent│ ld r17,16(r3) │ addi r18,r21,16 │ li r19,0 │ 8b0: rldicl r10,r10,63,33 │ addi r10,r10,1 │ mtctr r10 │ ↓ b 8e4 │ 8c0: addi r7,r22,1 │ addi r10,r9,8 │ ↓ bdz d00 51.44 │ lwz r9,8(r9) │ mr r8,r10 │ cmpw r20,r9 Committer notes: Just add the extern for 'sort_order' in disasm.c so that we don't end up breaking the build due to this type colision with capstone and libbpf: In file included from /usr/include/capstone/capstone.h:325, from /git/perf-6.10.0/tools/perf/util/print_insn.h:23, from builtin-script.c:38: /usr/include/capstone/bpf.h:94:14: error: 'bpf_insn' defined as wrong kind of tag 94 | typedef enum bpf_insn { I reported this to the bpf mailing list, see one of the links below. Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-6-atrajeev@linux.vnet.ibm.com Link: https://lore.kernel.org/bpf/ZqOltPk9VQGgJZAA@x1/T/#u Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/disasm.c | 102 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 6d4055b9b966..bab15cce612f 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -1637,6 +1637,91 @@ err: } #endif +static int symbol__disassemble_raw(char *filename, struct symbol *sym, + struct annotate_args *args) +{ + struct annotation *notes = symbol__annotation(sym); + struct map *map = args->ms.map; + struct dso *dso = map__dso(map); + u64 start = map__rip_2objdump(map, sym->start); + u64 end = map__rip_2objdump(map, sym->end); + u64 len = end - start; + u64 offset; + int i, count; + u8 *buf = NULL; + char disasm_buf[512]; + struct disasm_line *dl; + u32 *line; + + /* Return if objdump is specified explicitly */ + if (args->options->objdump_path) + return -1; + + pr_debug("Reading raw instruction from : %s using dso__data_read_offset\n", filename); + + buf = malloc(len); + if (buf == NULL) + goto err; + + count = dso__data_read_offset(dso, NULL, sym->start, buf, len); + + line = (u32 *)buf; + + if ((u64)count != len) + goto err; + + /* add the function address and name */ + scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:", + start, sym->name); + + args->offset = -1; + args->line = disasm_buf; + args->line_nr = 0; + args->fileloc = NULL; + args->ms.sym = sym; + + dl = disasm_line__new(args); + if (dl == NULL) + goto err; + + annotation_line__add(&dl->al, ¬es->src->source); + + /* Each raw instruction is 4 byte */ + count = len/4; + + for (i = 0, offset = 0; i < count; i++) { + args->offset = offset; + sprintf(args->line, "%x", line[i]); + dl = disasm_line__new(args); + if (dl == NULL) + goto err; + + annotation_line__add(&dl->al, ¬es->src->source); + offset += 4; + } + + /* It failed in the middle */ + if (offset != len) { + struct list_head *list = ¬es->src->source; + + /* Discard all lines and fallback to objdump */ + while (!list_empty(list)) { + dl = list_first_entry(list, struct disasm_line, al.node); + + list_del_init(&dl->al.node); + disasm_line__free(dl); + } + count = -1; + } + +out: + free(buf); + return count < 0 ? count : 0; + +err: + count = -1; + goto out; +} /* * Possibly create a new version of line with tabs expanded. Returns the * existing or new line, storage is updated if a new line is allocated. If @@ -1761,6 +1846,23 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args) strcpy(symfs_filename, tmp); } + /* + * For powerpc data type profiling, use the dso__data_read_offset + * to read raw instruction directly and interpret the binary code + * to understand instructions and register fields. For sort keys as + * type and typeoff, disassemble to mnemonic notation is + * not required in case of powerpc. + */ + if (arch__is(args->arch, "powerpc")) { + extern const char *sort_order; + + if (sort_order && !strstr(sort_order, "sym")) { + err = symbol__disassemble_raw(symfs_filename, sym, args); + if (err == 0) + goto out_remove_tmp; + } + } + #ifdef HAVE_LIBCAPSTONE_SUPPORT err = symbol__disassemble_capstone(symfs_filename, sym, args); if (err == 0) From 1b4406d2a88cf65d615b0bcb42525d7a4723eb70 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:49 +0530 Subject: [PATCH 029/347] perf annotate: Update parameters for reg extract functions to use raw instruction on powerpc Use the raw instruction code and macros to identify memory instructions, extract register fields and also offset. The implementation addresses the D-form, X-form, DS-form instructions. Adds "mem_ref" field to check whether source/target has memory reference. Add function "get_powerpc_regs" which will set these fields: reg1, reg2, offset depending of where it is source or target ops. Update "parse" callback for "struct ins_ops" to also pass "struct disasm_line" as argument. This is needed in parse functions where opcode is used to determine whether to set multi_regs and other fields Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-7-atrajeev@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm64/annotate/instructions.c | 3 +- .../arch/loongarch/annotate/instructions.c | 6 ++- tools/perf/arch/powerpc/util/dwarf-regs.c | 44 +++++++++++++++++++ tools/perf/arch/s390/annotate/instructions.c | 5 ++- tools/perf/util/annotate.c | 19 ++++++-- tools/perf/util/disasm.c | 19 +++++--- tools/perf/util/disasm.h | 5 ++- tools/perf/util/include/dwarf-regs.h | 11 +++++ 8 files changed, 96 insertions(+), 16 deletions(-) diff --git a/tools/perf/arch/arm64/annotate/instructions.c b/tools/perf/arch/arm64/annotate/instructions.c index 4af0c3a0f86e..f86d9f4798bd 100644 --- a/tools/perf/arch/arm64/annotate/instructions.c +++ b/tools/perf/arch/arm64/annotate/instructions.c @@ -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; diff --git a/tools/perf/arch/loongarch/annotate/instructions.c b/tools/perf/arch/loongarch/annotate/instructions.c index 21cc7e4149f7..ab43b1ab51e3 100644 --- a/tools/perf/arch/loongarch/annotate/instructions.c +++ b/tools/perf/arch/loongarch/annotate/instructions.c @@ -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; diff --git a/tools/perf/arch/powerpc/util/dwarf-regs.c b/tools/perf/arch/powerpc/util/dwarf-regs.c index 430623ca5612..104c7ae5c433 100644 --- a/tools/perf/arch/powerpc/util/dwarf-regs.c +++ b/tools/perf/arch/powerpc/util/dwarf-regs.c @@ -107,3 +107,47 @@ int regs_query_register_offset(const char *name) #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); +} diff --git a/tools/perf/arch/s390/annotate/instructions.c b/tools/perf/arch/s390/annotate/instructions.c index da5aa3e1f04c..eeac25cca699 100644 --- a/tools/perf/arch/s390/annotate/instructions.c +++ b/tools/perf/arch/s390/annotate/instructions.c @@ -2,7 +2,7 @@ #include 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; diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 1451caf25e77..ce99db291c5e 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -2123,20 +2123,33 @@ int annotate_get_insn_location(struct arch *arch, struct disasm_line *dl, for_each_insn_op_loc(loc, i, op_loc) { const char *insn_str = ops->source.raw; bool multi_regs = ops->source.multi_regs; + bool mem_ref = ops->source.mem_ref; if (i == INSN_OP_TARGET) { insn_str = ops->target.raw; multi_regs = ops->target.multi_regs; + mem_ref = ops->target.mem_ref; } /* Invalidate the register by default */ op_loc->reg1 = -1; op_loc->reg2 = -1; - if (insn_str == NULL) - continue; + if (insn_str == NULL) { + if (!arch__is(arch, "powerpc")) + continue; + } - if (strchr(insn_str, arch->objdump.memory_ref_char)) { + /* + * For powerpc, call get_powerpc_regs function which extracts the + * required fields for op_loc, ie reg1, reg2, offset from the + * raw instruction. + */ + if (arch__is(arch, "powerpc")) { + op_loc->mem_ref = mem_ref; + op_loc->multi_regs = multi_regs; + get_powerpc_regs(dl->raw.raw_insn, !i, op_loc); + } else if (strchr(insn_str, arch->objdump.memory_ref_char)) { op_loc->mem_ref = true; op_loc->multi_regs = multi_regs; extract_reg_offset(arch, insn_str, op_loc); diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index bab15cce612f..3178dc9bedca 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -255,7 +255,8 @@ bool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2) return arch->ins_is_fused(arch, ins1, ins2); } -static int call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms) +static int call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms, + struct disasm_line *dl __maybe_unused) { char *endptr, *tok, *name; struct map *map = ms->map; @@ -350,7 +351,8 @@ static inline const char *validate_comma(const char *c, struct ins_operands *ops return c; } -static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms) +static int 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; @@ -509,7 +511,8 @@ static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep) return 0; } -static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms) +static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms, + struct disasm_line *dl __maybe_unused) { ops->locked.ops = zalloc(sizeof(*ops->locked.ops)); if (ops->locked.ops == NULL) @@ -524,7 +527,7 @@ static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map_s goto out_free_ops; if (ops->locked.ins.ops->parse && - ops->locked.ins.ops->parse(arch, ops->locked.ops, ms) < 0) + ops->locked.ins.ops->parse(arch, ops->locked.ops, ms, NULL) < 0) goto out_free_ops; return 0; @@ -595,7 +598,8 @@ static bool check_multi_regs(struct arch *arch, const char *op) return count > 1; } -static int mov__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms __maybe_unused) +static int mov__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms __maybe_unused, + struct disasm_line *dl __maybe_unused) { char *s = strchr(ops->raw, ','), *target, *comment, prev; @@ -673,7 +677,8 @@ static struct ins_ops mov_ops = { .scnprintf = mov__scnprintf, }; -static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map_symbol *ms __maybe_unused) +static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map_symbol *ms __maybe_unused, + struct disasm_line *dl __maybe_unused) { char *target, *comment, *s, prev; @@ -814,7 +819,7 @@ static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, str if (!dl->ins.ops) return; - if (dl->ins.ops->parse && dl->ins.ops->parse(arch, &dl->ops, ms) < 0) + if (dl->ins.ops->parse && dl->ins.ops->parse(arch, &dl->ops, ms, dl) < 0) dl->ins.ops = NULL; } diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h index c835759c8e2b..30be0a94ea04 100644 --- a/tools/perf/util/disasm.h +++ b/tools/perf/util/disasm.h @@ -62,6 +62,7 @@ struct ins_operands { bool offset_avail; bool outside; bool multi_regs; + bool mem_ref; } target; union { struct { @@ -69,6 +70,7 @@ struct ins_operands { char *name; u64 addr; bool multi_regs; + bool mem_ref; } source; struct { struct ins ins; @@ -83,7 +85,8 @@ struct ins_operands { struct ins_ops { void (*free)(struct ins_operands *ops); - int (*parse)(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms); + int (*parse)(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms, + struct disasm_line *dl); int (*scnprintf)(struct ins *ins, char *bf, size_t size, struct ins_operands *ops, int max_ins_name); }; diff --git a/tools/perf/util/include/dwarf-regs.h b/tools/perf/util/include/dwarf-regs.h index 01fb25a1150a..75b28dcc8317 100644 --- a/tools/perf/util/include/dwarf-regs.h +++ b/tools/perf/util/include/dwarf-regs.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #ifndef _PERF_DWARF_REGS_H_ #define _PERF_DWARF_REGS_H_ +#include "annotate.h" #define DWARF_REG_PC 0xd3af9c /* random number */ #define DWARF_REG_FB 0xd3affb /* random number */ @@ -31,6 +32,16 @@ static inline int get_dwarf_regnum(const char *name __maybe_unused, } #endif +#if !defined(__powerpc__) || !defined(HAVE_DWARF_SUPPORT) +static inline void get_powerpc_regs(u32 raw_insn __maybe_unused, int is_source __maybe_unused, + struct annotated_op_loc *op_loc __maybe_unused) +{ + return; +} +#else +void get_powerpc_regs(u32 raw_insn, int is_source, struct annotated_op_loc *op_loc); +#endif + #ifdef HAVE_ARCH_REGS_QUERY_REGISTER_OFFSET /* * Arch should support fetching the offset of a register in pt_regs From 1acdad68183ab870fca1e63b64f32601be612611 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:50 +0530 Subject: [PATCH 030/347] perf annotate: Add parse function for memory instructions in powerpc Use the raw instruction code and macros to identify memory instructions, extract register fields and also offset. The implementation addresses the D-form, X-form, DS-form instructions. Two main functions are added. New parse function "load_store__parse" as instruction ops parser for memory instructions. Unlike other parsers (like mov__parse), this one fills in the "multi_regs" field for source/target and new added "mem_ref" field. No other fields are set because, here there is no need to parse the disassembled code and arch specific macros will take care of extracting offset and regs which is easier and will be precise. In powerpc, all instructions with a primary opcode from 32 to 63 are memory instructions. Update "ins__find" function to have "raw_insn" also as a parameter. Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-8-atrajeev@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/arch/powerpc/annotate/instructions.c | 16 ++++++ tools/perf/util/disasm.c | 54 +++++++++++++++++-- tools/perf/util/disasm.h | 2 +- 3 files changed, 66 insertions(+), 6 deletions(-) diff --git a/tools/perf/arch/powerpc/annotate/instructions.c b/tools/perf/arch/powerpc/annotate/instructions.c index d57fd023ef9c..b084423d8477 100644 --- a/tools/perf/arch/powerpc/annotate/instructions.c +++ b/tools/perf/arch/powerpc/annotate/instructions.c @@ -49,6 +49,22 @@ static struct ins_ops *powerpc__associate_instruction_ops(struct arch *arch, con return ops; } +#define PPC_OP(op) (((op) >> 26) & 0x3F) + +static struct ins_ops *check_ppc_insn(u32 raw_insn) +{ + int opcode = PPC_OP(raw_insn); + + /* + * Instructions with opcode 32 to 63 are memory + * instructions in powerpc + */ + if ((opcode & 0x20)) + return &load_store_ops; + + return NULL; +} + static int powerpc__annotate_init(struct arch *arch, char *cpuid __maybe_unused) { if (!arch->initialized) { diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 3178dc9bedca..0afa1847f1c5 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -36,6 +36,7 @@ static struct ins_ops mov_ops; static struct ins_ops nop_ops; static struct ins_ops lock_ops; static struct ins_ops ret_ops; +static struct ins_ops load_store_ops; static int jump__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops, int max_ins_name); @@ -521,7 +522,7 @@ static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map_s if (disasm_line__parse(ops->raw, &ops->locked.ins.name, &ops->locked.ops->raw) < 0) goto out_free_ops; - ops->locked.ins.ops = ins__find(arch, ops->locked.ins.name); + ops->locked.ins.ops = ins__find(arch, ops->locked.ins.name, 0); if (ops->locked.ins.ops == NULL) goto out_free_ops; @@ -677,6 +678,37 @@ static struct ins_ops mov_ops = { .scnprintf = mov__scnprintf, }; +static int load_store__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops, int max_ins_name) +{ + return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, + ops->raw); +} + +/* + * Sets the fields: multi_regs and "mem_ref". + * "mem_ref" is set for ops->source which is later used to + * fill the objdump->memory_ref-char field. This ops is currently + * used by powerpc and since binary instruction code is used to + * extract opcode, regs and offset, no other parsing is needed here + */ +static int load_store__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, + struct map_symbol *ms __maybe_unused, struct disasm_line *dl __maybe_unused) +{ + ops->source.mem_ref = true; + ops->source.multi_regs = false; + + ops->target.mem_ref = false; + ops->target.multi_regs = false; + + return 0; +} + +static struct ins_ops load_store_ops = { + .parse = load_store__parse, + .scnprintf = load_store__scnprintf, +}; + static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, struct map_symbol *ms __maybe_unused, struct disasm_line *dl __maybe_unused) { @@ -768,11 +800,23 @@ static void ins__sort(struct arch *arch) qsort(arch->instructions, nmemb, sizeof(struct ins), ins__cmp); } -static struct ins_ops *__ins__find(struct arch *arch, const char *name) +static struct ins_ops *__ins__find(struct arch *arch, const char *name, u32 raw_insn) { struct ins *ins; const int nmemb = arch->nr_instructions; + if (arch__is(arch, "powerpc")) { + /* + * For powerpc, identify the instruction ops + * from the opcode using raw_insn. + */ + struct ins_ops *ops; + + ops = check_ppc_insn(raw_insn); + if (ops) + return ops; + } + if (!arch->sorted_instructions) { ins__sort(arch); arch->sorted_instructions = true; @@ -802,9 +846,9 @@ static struct ins_ops *__ins__find(struct arch *arch, const char *name) return ins ? ins->ops : NULL; } -struct ins_ops *ins__find(struct arch *arch, const char *name) +struct ins_ops *ins__find(struct arch *arch, const char *name, u32 raw_insn) { - struct ins_ops *ops = __ins__find(arch, name); + struct ins_ops *ops = __ins__find(arch, name, raw_insn); if (!ops && arch->associate_instruction_ops) ops = arch->associate_instruction_ops(arch, name); @@ -814,7 +858,7 @@ struct ins_ops *ins__find(struct arch *arch, const char *name) static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map_symbol *ms) { - dl->ins.ops = ins__find(arch, dl->ins.name); + dl->ins.ops = ins__find(arch, dl->ins.name, dl->raw.raw_insn); if (!dl->ins.ops) return; diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h index 30be0a94ea04..c1bb1e484bfb 100644 --- a/tools/perf/util/disasm.h +++ b/tools/perf/util/disasm.h @@ -105,7 +105,7 @@ struct annotate_args { struct arch *arch__find(const char *name); bool arch__is(struct arch *arch, const char *name); -struct ins_ops *ins__find(struct arch *arch, const char *name); +struct ins_ops *ins__find(struct arch *arch, const char *name, u32 raw_insn); int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops, int max_ins_name); From ace7d681d82d8ac6cc1c9646b19ab4cca7be0d90 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:51 +0530 Subject: [PATCH 031/347] perf annotate: Add support to identify memory instructions of opcode 31 in powerpc There are memory instructions in powerpc with opcode as 31. Example: "ldx RT,RA,RB" , Its X form is as below: ______________________________________ | 31 | RT | RA | RB | 21 |/| -------------------------------------- 0 6 11 16 21 30 31 The opcode for "ldx" is 31. There are other instructions also with opcode 31 which are memory insn like ldux, stbx, lwzx, lhaux But all instructions with opcode 31 are not memory. Example is add instruction: "add RT,RA,RB" The value in bit 21-30 [ 21 for ldx ] is different for these instructions. Patch uses this value to assign instruction ops for these cases. The naming convention and value to identify these are picked from defines in "arch/powerpc/include/asm/ppc-opcode.h" Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-9-atrajeev@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/arch/powerpc/annotate/instructions.c | 107 +++++++++++++++++- tools/perf/util/disasm.c | 3 + 2 files changed, 108 insertions(+), 2 deletions(-) diff --git a/tools/perf/arch/powerpc/annotate/instructions.c b/tools/perf/arch/powerpc/annotate/instructions.c index b084423d8477..1ffb64c6bd0d 100644 --- a/tools/perf/arch/powerpc/annotate/instructions.c +++ b/tools/perf/arch/powerpc/annotate/instructions.c @@ -49,18 +49,121 @@ static struct ins_ops *powerpc__associate_instruction_ops(struct arch *arch, con return ops; } -#define PPC_OP(op) (((op) >> 26) & 0x3F) +#define PPC_OP(op) (((op) >> 26) & 0x3F) +#define PPC_21_30(R) (((R) >> 1) & 0x3ff) + +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, }, +}; + +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(u32 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 + }; /* * Instructions with opcode 32 to 63 are memory * instructions in powerpc */ - if ((opcode & 0x20)) + if ((opcode & 0x20)) { 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 != NULL) + return &load_store_ops; + } return NULL; } diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 0afa1847f1c5..e36be7b7c625 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -697,6 +697,9 @@ static int load_store__parse(struct arch *arch __maybe_unused, struct ins_operan { ops->source.mem_ref = true; ops->source.multi_regs = false; + /* opcode 31 is of X form */ + if (PPC_OP(dl->raw.raw_insn) == 31) + ops->source.multi_regs = true; ops->target.mem_ref = false; ops->target.multi_regs = false; From cd0b6f67c4ab1aecdfedb277c42880fcffe75ace Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:52 +0530 Subject: [PATCH 032/347] perf annotate: Add some of the arithmetic instructions to support instruction tracking in powerpc Data-type profiling has the concept of instruction tracking. Example sequence in powerpc: ld r10,264(r3) mr r31,r3 < ld r9,312(r31) or differently lwz r10,264(r3) add r31, r3, RB lwz r9, 0(r31) If a sample is hit at "lwz r9, 0(r31)", data type of r31 depends on previous instruction sequence here. So to track the previous instructions, patch adds changes to identify some of the arithmetic instructions which are having opcode as 31. Since memory instructions also has cases with opcode 31, use the bits 22:30 to filter the arithmetic instructions here. Also there are instructions with just two operands like "addme", "addze". This patch adds new instructions ops "arithmetic_ops" to handle this Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-10-atrajeev@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/arch/powerpc/annotate/instructions.c | 49 ++++++++++++++++++ tools/perf/util/disasm.c | 51 +++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/tools/perf/arch/powerpc/annotate/instructions.c b/tools/perf/arch/powerpc/annotate/instructions.c index 1ffb64c6bd0d..aa5ee09fa28f 100644 --- a/tools/perf/arch/powerpc/annotate/instructions.c +++ b/tools/perf/arch/powerpc/annotate/instructions.c @@ -51,6 +51,7 @@ static struct ins_ops *powerpc__associate_instruction_ops(struct arch *arch, con #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; @@ -134,6 +135,44 @@ static struct insn_offset ins_array[] = { { .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 int cmp_offset(const void *a, const void *b) { const struct insn_offset *val1 = a; @@ -163,6 +202,16 @@ static struct ins_ops *check_ppc_insn(u32 raw_insn) ret = bsearch(&mem_insns_31_opcode, ins_array, ARRAY_SIZE(ins_array), sizeof(ins_array[0]), cmp_offset); if (ret != NULL) 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; + } } return NULL; diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index e36be7b7c625..65915e1f575a 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -37,6 +37,7 @@ static struct ins_ops nop_ops; static struct ins_ops lock_ops; static struct ins_ops ret_ops; static struct ins_ops load_store_ops; +static struct ins_ops arithmetic_ops; static int jump__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops, int max_ins_name); @@ -678,6 +679,56 @@ static struct ins_ops mov_ops = { .scnprintf = mov__scnprintf, }; +#define PPC_22_30(R) (((R) >> 1) & 0x1ff) +#define MINUS_EXT_XO_FORM 234 +#define SUB_EXT_XO_FORM 232 +#define ADD_ZERO_EXT_XO_FORM 202 +#define SUB_ZERO_EXT_XO_FORM 200 + +static int arithmetic__scnprintf(struct ins *ins, char *bf, size_t size, + struct ins_operands *ops, int max_ins_name) +{ + return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, + ops->raw); +} + +/* + * Sets the fields: multi_regs and "mem_ref". + * "mem_ref" is set for ops->source which is later used to + * fill the objdump->memory_ref-char field. This ops is currently + * used by powerpc and since binary instruction code is used to + * extract opcode, regs and offset, no other parsing is needed here. + * + * Dont set multi regs for 4 cases since it has only one operand + * for source: + * - Add to Minus One Extended XO-form ( Ex: addme, addmeo ) + * - Subtract From Minus One Extended XO-form ( Ex: subfme ) + * - Add to Zero Extended XO-form ( Ex: addze, addzeo ) + * - Subtract From Zero Extended XO-form ( Ex: subfze ) + */ +static int arithmetic__parse(struct arch *arch __maybe_unused, struct ins_operands *ops, + struct map_symbol *ms __maybe_unused, struct disasm_line *dl) +{ + int opcode = PPC_OP(dl->raw.raw_insn); + + ops->source.mem_ref = false; + if (opcode == 31) { + if ((opcode != MINUS_EXT_XO_FORM) && (opcode != SUB_EXT_XO_FORM) \ + && (opcode != ADD_ZERO_EXT_XO_FORM) && (opcode != SUB_ZERO_EXT_XO_FORM)) + ops->source.multi_regs = true; + } + + ops->target.mem_ref = false; + ops->target.multi_regs = false; + + return 0; +} + +static struct ins_ops arithmetic_ops = { + .parse = arithmetic__parse, + .scnprintf = arithmetic__scnprintf, +}; + static int load_store__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops, int max_ins_name) { From 539bfea3e09c8e7a773b0fc4f6a4b26d921d63ef Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:53 +0530 Subject: [PATCH 033/347] perf annotate: Add more instructions for instruction tracking Add few more instructions and use opcode as search key to find if it is supported by the architecture. The added ones are: addi, addic, addic., addis, subfic and mulli Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-11-atrajeev@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/powerpc/annotate/instructions.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tools/perf/arch/powerpc/annotate/instructions.c b/tools/perf/arch/powerpc/annotate/instructions.c index aa5ee09fa28f..aa25a336d8d0 100644 --- a/tools/perf/arch/powerpc/annotate/instructions.c +++ b/tools/perf/arch/powerpc/annotate/instructions.c @@ -172,6 +172,14 @@ static struct insn_offset arithmetic_ins_op_31[] = { { .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) { @@ -212,6 +220,12 @@ static struct ins_ops *check_ppc_insn(u32 raw_insn) 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; From 88444952bdfe27fcc91b1c499a0f77675db592a9 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:54 +0530 Subject: [PATCH 034/347] perf annotate: Update instruction tracking for powerpc Add instruction tracking function "update_insn_state_powerpc" for powerpc. Example sequence in powerpc: ld r10,264(r3) mr r31,r3 < ld r9,312(r31) Consider ithe sample is pointing to: "ld r9,312(r31)". Here the memory reference is hit at "312(r31)" where 312 is the offset and r31 is the source register. Previous instruction sequence shows that register state of r3 is moved to r31. So to identify the data type for r31 access, the previous instruction ("mr") needs to be tracked and the state type entry has to be updated. Current instruction tracking support in perf tools infrastructure is specific to x86. Patch adds this support for powerpc as well. Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-12-atrajeev@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/arch/powerpc/annotate/instructions.c | 59 +++++++++++++++++++ tools/perf/util/annotate-data.c | 9 ++- tools/perf/util/disasm.c | 3 + 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/tools/perf/arch/powerpc/annotate/instructions.c b/tools/perf/arch/powerpc/annotate/instructions.c index aa25a336d8d0..af1032572bf3 100644 --- a/tools/perf/arch/powerpc/annotate/instructions.c +++ b/tools/perf/arch/powerpc/annotate/instructions.c @@ -231,6 +231,65 @@ static struct ins_ops *check_ppc_insn(u32 raw_insn) return NULL; } +/* + * Instruction tracking function to track register state moves. + * Example sequence: + * ld r10,264(r3) + * mr r31,r3 + * < + * 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) { diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 7a48c3d72b89..734acdd8c4b7 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -1080,6 +1080,13 @@ out: return ret; } +static int arch_supports_insn_tracking(struct data_loc_info *dloc) +{ + if ((arch__is(dloc->arch, "x86")) || (arch__is(dloc->arch, "powerpc"))) + return 1; + return 0; +} + /* * Construct a list of basic blocks for each scope with variables and try to find * the data type by updating a type state table through instructions. @@ -1094,7 +1101,7 @@ static int find_data_type_block(struct data_loc_info *dloc, int ret = -1; /* TODO: other architecture support */ - if (!arch__is(dloc->arch, "x86")) + if (!arch_supports_insn_tracking(dloc)) return -1; prev_dst_ip = dst_ip = dloc->ip; diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 65915e1f575a..3d25443e1162 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -156,6 +156,9 @@ static struct arch architectures[] = { { .name = "powerpc", .init = powerpc__annotate_init, +#ifdef HAVE_DWARF_SUPPORT + .update_insn_state = update_insn_state_powerpc, +#endif }, { .name = "riscv64", From 1fe86bc245abd1aded01403675d6610455794b5f Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:55 +0530 Subject: [PATCH 035/347] perf annotate: Make capstone_init non-static so that it can be used during symbol disassemble symbol__disassemble_capstone in util/disasm.c calls function open_capstone_handle to open/init the capstone. We already have a capstone_init function in "util/print_insn.c". But capstone_init is defined as a static function in util/print_insn.c. Change this and also add the function in print_insn.h The open_capstone_handle checks the disassembler_style option from annotation_options to decide whether to set CS_OPT_SYNTAX_ATT. Add that logic in capstone_init also and by default set it to true. Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-13-atrajeev@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/print_insn.c | 12 +++++++++--- tools/perf/util/print_insn.h | 5 +++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/print_insn.c b/tools/perf/util/print_insn.c index a950e9157d2d..a76aae81d7a0 100644 --- a/tools/perf/util/print_insn.c +++ b/tools/perf/util/print_insn.c @@ -32,7 +32,7 @@ size_t sample__fprintf_insn_raw(struct perf_sample *sample, FILE *fp) #ifdef HAVE_LIBCAPSTONE_SUPPORT #include -static int capstone_init(struct machine *machine, csh *cs_handle, bool is64) +int capstone_init(struct machine *machine, csh *cs_handle, bool is64, bool disassembler_style) { cs_arch arch; cs_mode mode; @@ -62,7 +62,13 @@ static int capstone_init(struct machine *machine, csh *cs_handle, bool is64) } if (machine__normalized_is(machine, "x86")) { - cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT); + /* + * In case of using capstone_init while symbol__disassemble + * setting CS_OPT_SYNTAX_ATT depends if disassembler_style opts + * is set via annotation args + */ + if (disassembler_style) + cs_option(*cs_handle, CS_OPT_SYNTAX, CS_OPT_SYNTAX_ATT); /* * Resolving address operands to symbols is implemented * on x86 by investigating instruction details. @@ -122,7 +128,7 @@ ssize_t fprintf_insn_asm(struct machine *machine, struct thread *thread, u8 cpum int ret; /* TODO: Try to initiate capstone only once but need a proper place. */ - ret = capstone_init(machine, &cs_handle, is64bit); + ret = capstone_init(machine, &cs_handle, is64bit, true); if (ret < 0) return ret; diff --git a/tools/perf/util/print_insn.h b/tools/perf/util/print_insn.h index 07d11af3fc1c..2c8ee41c4a5d 100644 --- a/tools/perf/util/print_insn.h +++ b/tools/perf/util/print_insn.h @@ -19,4 +19,9 @@ ssize_t fprintf_insn_asm(struct machine *machine, struct thread *thread, u8 cpum bool is64bit, const uint8_t *code, size_t code_size, uint64_t ip, int *lenp, int print_opts, FILE *fp); +#ifdef HAVE_LIBCAPSTONE_SUPPORT +#include +int capstone_init(struct machine *machine, csh *cs_handle, bool is64, bool disassembler_style); +#endif + #endif /* PERF_PRINT_INSN_H */ From f1e9347c855de73d8df1b0fa763184a46548d62f Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:56 +0530 Subject: [PATCH 036/347] perf annotate: Use capstone_init and remove open_capstone_handle from disasm.c capstone_init is made availbale for all archs to use and updated to enable support for CS_ARCH_PPC as well. Patch removes open_capstone_handle and uses capstone_init in all the places. Committer notes: Avoid including capstone/capstone.h from print_insn.h to not break the build in builtin-script.c due to the namespace clash with libbpf: /usr/include/capstone/bpf.h:94:14: error: 'bpf_insn' defined as wrong kind of tag Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-14-atrajeev@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/disasm.c | 2 ++ tools/perf/util/print_insn.c | 2 ++ tools/perf/util/print_insn.h | 5 ----- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 3d25443e1162..7a11edefe1bd 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -1512,6 +1512,8 @@ symbol__disassemble_bpf_image(struct symbol *sym, #ifdef HAVE_LIBCAPSTONE_SUPPORT #include +int capstone_init(struct machine *machine, csh *cs_handle, bool is64, bool disassembler_style); + static int open_capstone_handle(struct annotate_args *args, bool is_64bit, csh *handle) { diff --git a/tools/perf/util/print_insn.c b/tools/perf/util/print_insn.c index a76aae81d7a0..a33a7726422d 100644 --- a/tools/perf/util/print_insn.c +++ b/tools/perf/util/print_insn.c @@ -32,6 +32,8 @@ size_t sample__fprintf_insn_raw(struct perf_sample *sample, FILE *fp) #ifdef HAVE_LIBCAPSTONE_SUPPORT #include +int capstone_init(struct machine *machine, csh *cs_handle, bool is64, bool disassembler_style); + int capstone_init(struct machine *machine, csh *cs_handle, bool is64, bool disassembler_style) { cs_arch arch; diff --git a/tools/perf/util/print_insn.h b/tools/perf/util/print_insn.h index 2c8ee41c4a5d..07d11af3fc1c 100644 --- a/tools/perf/util/print_insn.h +++ b/tools/perf/util/print_insn.h @@ -19,9 +19,4 @@ ssize_t fprintf_insn_asm(struct machine *machine, struct thread *thread, u8 cpum bool is64bit, const uint8_t *code, size_t code_size, uint64_t ip, int *lenp, int print_opts, FILE *fp); -#ifdef HAVE_LIBCAPSTONE_SUPPORT -#include -int capstone_init(struct machine *machine, csh *cs_handle, bool is64, bool disassembler_style); -#endif - #endif /* PERF_PRINT_INSN_H */ From c5d60de1813a9e09ea6d94f82da287d0fa1e1179 Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:57 +0530 Subject: [PATCH 037/347] perf annotate: Add support to use libcapstone in powerpc Now perf uses the capstone library to disassemble the instructions in x86. capstone is used (if available) for perf annotate to speed up. Currently it only supports x86 architecture. This patch includes changes to enable this in powerpc. For now, only for data type sort keys, this method is used and only binary code (raw instruction) is read. This is because powerpc approach to understand instructions and reg fields uses raw instruction. The "cs_disasm" is currently not enabled. While attempting to do cs_disasm, observation is that some of the instructions were not identified (ex: extswsli, maddld) and it had to fallback to use objdump. Hence enabling "cs_disasm" is added in comment section as a TODO for powerpc. Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-15-atrajeev@linux.vnet.ibm.com [ Use dso__nsinfo(dso) as required to match EXTRA_CFLAGS=-DREFCNT_CHECKING=1 build expectations ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/disasm.c | 143 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 7a11edefe1bd..6e7a3db9f330 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -1614,6 +1614,144 @@ static void print_capstone_detail(cs_insn *insn, char *buf, size_t len, } } +static int symbol__disassemble_capstone_powerpc(char *filename, struct symbol *sym, + struct annotate_args *args) +{ + struct annotation *notes = symbol__annotation(sym); + struct map *map = args->ms.map; + struct dso *dso = map__dso(map); + struct nscookie nsc; + u64 start = map__rip_2objdump(map, sym->start); + u64 end = map__rip_2objdump(map, sym->end); + u64 len = end - start; + u64 offset; + int i, fd, count; + bool is_64bit = false; + bool needs_cs_close = false; + u8 *buf = NULL; + struct find_file_offset_data data = { + .ip = start, + }; + csh handle; + char disasm_buf[512]; + struct disasm_line *dl; + u32 *line; + bool disassembler_style = false; + + if (args->options->objdump_path) + return -1; + + nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); + fd = open(filename, O_RDONLY); + nsinfo__mountns_exit(&nsc); + if (fd < 0) + return -1; + + if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data, + &is_64bit) == 0) + goto err; + + if (!args->options->disassembler_style || + !strcmp(args->options->disassembler_style, "att")) + disassembler_style = true; + + if (capstone_init(maps__machine(args->ms.maps), &handle, is_64bit, disassembler_style) < 0) + goto err; + + needs_cs_close = true; + + buf = malloc(len); + if (buf == NULL) + goto err; + + count = pread(fd, buf, len, data.offset); + close(fd); + fd = -1; + + if ((u64)count != len) + goto err; + + line = (u32 *)buf; + + /* add the function address and name */ + scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:", + start, sym->name); + + args->offset = -1; + args->line = disasm_buf; + args->line_nr = 0; + args->fileloc = NULL; + args->ms.sym = sym; + + dl = disasm_line__new(args); + if (dl == NULL) + goto err; + + annotation_line__add(&dl->al, ¬es->src->source); + + /* + * TODO: enable disassm for powerpc + * count = cs_disasm(handle, buf, len, start, len, &insn); + * + * For now, only binary code is saved in disassembled line + * to be used in "type" and "typeoff" sort keys. Each raw code + * is 32 bit instruction. So use "len/4" to get the number of + * entries. + */ + count = len/4; + + for (i = 0, offset = 0; i < count; i++) { + args->offset = offset; + sprintf(args->line, "%x", line[i]); + + dl = disasm_line__new(args); + if (dl == NULL) + goto err; + + annotation_line__add(&dl->al, ¬es->src->source); + + offset += 4; + } + + /* It failed in the middle */ + if (offset != len) { + struct list_head *list = ¬es->src->source; + + /* Discard all lines and fallback to objdump */ + while (!list_empty(list)) { + dl = list_first_entry(list, struct disasm_line, al.node); + + list_del_init(&dl->al.node); + disasm_line__free(dl); + } + count = -1; + } + +out: + if (needs_cs_close) + cs_close(&handle); + free(buf); + return count < 0 ? count : 0; + +err: + if (fd >= 0) + close(fd); + if (needs_cs_close) { + struct disasm_line *tmp; + + /* + * It probably failed in the middle of the above loop. + * Release any resources it might add. + */ + list_for_each_entry_safe(dl, tmp, ¬es->src->source, al.node) { + list_del(&dl->al.node); + free(dl); + } + } + count = -1; + goto out; +} + static int symbol__disassemble_capstone(char *filename, struct symbol *sym, struct annotate_args *args) { @@ -1968,6 +2106,11 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args) err = symbol__disassemble_raw(symfs_filename, sym, args); if (err == 0) goto out_remove_tmp; +#ifdef HAVE_LIBCAPSTONE_SUPPORT + err = symbol__disassemble_capstone_powerpc(symfs_filename, sym, args); + if (err == 0) + goto out_remove_tmp; +#endif } } From 2c9db7475e5de5125b5043fe8f560284b248b0ae Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 18 Jul 2024 14:13:58 +0530 Subject: [PATCH 038/347] perf annotate: Set instruction name to be used with insn-stat when using raw instruction Since the "ins.name" is not set while using raw instruction, 'perf annotate' with insn-stat gives wrong data: Result from "./perf annotate --data-type --insn-stat": Annotate Instruction stats total 615, ok 419 (68.1%), bad 196 (31.9%) Name : Good Bad ----------------------------------------------------------- : 419 196 This patch sets "dl->ins.name" in arch specific function "check_ppc_insn" while initialising "struct disasm_line". Also update "ins_find" function to pass "struct disasm_line" as a parameter so as to set its name field in arch specific call. With the patch changes: Annotate Instruction stats total 609, ok 446 (73.2%), bad 163 (26.8%) Name/opcode : Good Bad ----------------------------------------------------------- 58 : 323 80 32 : 49 43 34 : 33 11 OP_31_XOP_LDX : 8 20 40 : 23 0 OP_31_XOP_LWARX : 5 1 OP_31_XOP_LWZX : 2 3 OP_31_XOP_LDARX : 3 0 33 : 0 2 OP_31_XOP_LBZX : 0 1 OP_31_XOP_LWAX : 0 1 OP_31_XOP_LHZX : 0 1 Reviewed-by: Kajol Jain Reviewed-by: Namhyung Kim Signed-off-by: Athira Rajeev Tested-by: Kajol Jain Cc: Adrian Hunter Cc: Akanksha J N Cc: Christophe Leroy Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Jiri Olsa Cc: Madhavan Srinivasan Cc: Segher Boessenkool Link: https://lore.kernel.org/lkml/20240718084358.72242-16-atrajeev@linux.vnet.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/arch/powerpc/annotate/instructions.c | 18 +++++++++++++++--- tools/perf/builtin-annotate.c | 4 ++-- tools/perf/util/annotate.c | 2 +- tools/perf/util/disasm.c | 10 +++++----- tools/perf/util/disasm.h | 2 +- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/tools/perf/arch/powerpc/annotate/instructions.c b/tools/perf/arch/powerpc/annotate/instructions.c index af1032572bf3..ede9eeade0ab 100644 --- a/tools/perf/arch/powerpc/annotate/instructions.c +++ b/tools/perf/arch/powerpc/annotate/instructions.c @@ -189,8 +189,9 @@ static int cmp_offset(const void *a, const void *b) return (val1->value - val2->value); } -static struct ins_ops *check_ppc_insn(u32 raw_insn) +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; @@ -198,19 +199,30 @@ static struct ins_ops *check_ppc_insn(u32 raw_insn) "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 != NULL) + if (ret) { + if (!strlen(dl->ins.name)) + dl->ins.name = strdup(ret->name); return &load_store_ops; - else { + } 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); diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index b10b7f005658..cf60392b1c19 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -396,10 +396,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"); } diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index ce99db291c5e..a2ee4074f768 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -2229,7 +2229,7 @@ static struct annotated_item_stat *annotate_data_stat(struct list_head *head, return NULL; istat->name = strdup(name); - if (istat->name == NULL) { + if ((istat->name == NULL) || (!strlen(istat->name))) { free(istat); return NULL; } diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 6e7a3db9f330..410e52cd9cfd 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -857,7 +857,7 @@ static void ins__sort(struct arch *arch) qsort(arch->instructions, nmemb, sizeof(struct ins), ins__cmp); } -static struct ins_ops *__ins__find(struct arch *arch, const char *name, u32 raw_insn) +static struct ins_ops *__ins__find(struct arch *arch, const char *name, struct disasm_line *dl) { struct ins *ins; const int nmemb = arch->nr_instructions; @@ -869,7 +869,7 @@ static struct ins_ops *__ins__find(struct arch *arch, const char *name, u32 raw_ */ struct ins_ops *ops; - ops = check_ppc_insn(raw_insn); + ops = check_ppc_insn(dl); if (ops) return ops; } @@ -903,9 +903,9 @@ static struct ins_ops *__ins__find(struct arch *arch, const char *name, u32 raw_ return ins ? ins->ops : NULL; } -struct ins_ops *ins__find(struct arch *arch, const char *name, u32 raw_insn) +struct ins_ops *ins__find(struct arch *arch, const char *name, struct disasm_line *dl) { - struct ins_ops *ops = __ins__find(arch, name, raw_insn); + struct ins_ops *ops = __ins__find(arch, name, dl); if (!ops && arch->associate_instruction_ops) ops = arch->associate_instruction_ops(arch, name); @@ -915,7 +915,7 @@ struct ins_ops *ins__find(struct arch *arch, const char *name, u32 raw_insn) static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map_symbol *ms) { - dl->ins.ops = ins__find(arch, dl->ins.name, dl->raw.raw_insn); + dl->ins.ops = ins__find(arch, dl->ins.name, dl); if (!dl->ins.ops) return; diff --git a/tools/perf/util/disasm.h b/tools/perf/util/disasm.h index c1bb1e484bfb..f56beedeb9da 100644 --- a/tools/perf/util/disasm.h +++ b/tools/perf/util/disasm.h @@ -105,7 +105,7 @@ struct annotate_args { struct arch *arch__find(const char *name); bool arch__is(struct arch *arch, const char *name); -struct ins_ops *ins__find(struct arch *arch, const char *name, u32 raw_insn); +struct ins_ops *ins__find(struct arch *arch, const char *name, struct disasm_line *dl); int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops, int max_ins_name); From 42d37fc0c819b81f6f6afd108b55d04ba9d32d0f Mon Sep 17 00:00:00 2001 From: Kajol Jain Date: Tue, 23 Jul 2024 10:51:54 +0530 Subject: [PATCH 039/347] perf vendor events power10: Update JSON/events Update JSON/events for power10 platform with additional events. Also move PM_VECTOR_LD_CMPL event from others.json to frontend.json file. Reviewed-by: Ian Rogers Signed-off-by: Kajol Jain Tested-by: Disha Goel Cc: Akanksha J N Cc: Athira Rajeev Cc: Disha Goel Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: hbathini@linux.ibm.com Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20240723052154.96202-1-kjain@linux.ibm.com [ Remove alternative to ' char that made the build break in some distros with a unicode parsing python error ] Signed-off-by: Arnaldo Carvalho de Melo --- .../arch/powerpc/power10/frontend.json | 5 + .../arch/powerpc/power10/others.json | 100 +++++++++++++++++- 2 files changed, 100 insertions(+), 5 deletions(-) diff --git a/tools/perf/pmu-events/arch/powerpc/power10/frontend.json b/tools/perf/pmu-events/arch/powerpc/power10/frontend.json index 5977f5e64212..53660c279286 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/frontend.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/frontend.json @@ -74,6 +74,11 @@ "EventName": "PM_ISSUE_KILL", "BriefDescription": "Cycles in which an instruction or group of instructions were cancelled after being issued. This event increments once per occurrence, regardless of how many instructions are included in the issue group." }, + { + "EventCode": "0x44054", + "EventName": "PM_VECTOR_LD_CMPL", + "BriefDescription": "Vector load instruction completed." + }, { "EventCode": "0x44056", "EventName": "PM_VECTOR_ST_CMPL", diff --git a/tools/perf/pmu-events/arch/powerpc/power10/others.json b/tools/perf/pmu-events/arch/powerpc/power10/others.json index fcf8a8ebe7bd..3789304cb363 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/others.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/others.json @@ -94,11 +94,6 @@ "EventName": "PM_L1_ICACHE_RELOADED_ALL", "BriefDescription": "Counts all instruction cache reloads includes demand, prefetch, prefetch turned into demand and demand turned into prefetch." }, - { - "EventCode": "0x44054", - "EventName": "PM_VECTOR_LD_CMPL", - "BriefDescription": "Vector load instruction completed." - }, { "EventCode": "0x4D05E", "EventName": "PM_BR_CMPL", @@ -108,5 +103,100 @@ "EventCode": "0x400F0", "EventName": "PM_LD_DEMAND_MISS_L1_FIN", "BriefDescription": "Load missed L1, counted at finish time." + }, + { + "EventCode": "0x00000038BC", + "EventName": "PM_ISYNC_CMPL", + "BriefDescription": "Isync completion count per thread." + }, + { + "EventCode": "0x000000C088", + "EventName": "PM_LD0_32B_FIN", + "BriefDescription": "256-bit load finished in the LD0 load execution unit." + }, + { + "EventCode": "0x000000C888", + "EventName": "PM_LD1_32B_FIN", + "BriefDescription": "256-bit load finished in the LD1 load execution unit." + }, + { + "EventCode": "0x000000C090", + "EventName": "PM_LD0_UNALIGNED_FIN", + "BriefDescription": "Load instructions in LD0 port that are either unaligned, or treated as unaligned and require an additional recycle through the pipeline using the load gather buffer. This typically adds about 10 cycles to the latency of the instruction. This includes loads that cross the 128 byte boundary, octword loads that are not aligned, and a special forward progress case of a load that does not hit in the L1 and crosses the 32 byte boundary and is launched NTC. Counted at finish time." + }, + { + "EventCode": "0x000000C890", + "EventName": "PM_LD1_UNALIGNED_FIN", + "BriefDescription": "Load instructions in LD1 port that are either unaligned, or treated as unaligned and require an additional recycle through the pipeline using the load gather buffer. This typically adds about 10 cycles to the latency of the instruction. This includes loads that cross the 128 byte boundary, octword loads that are not aligned, and a special forward progress case of a load that does not hit in the L1 and crosses the 32 byte boundary and is launched NTC. Counted at finish time." + }, + { + "EventCode": "0x000000C0A4", + "EventName": "PM_ST0_UNALIGNED_FIN", + "BriefDescription": "Store instructions in ST0 port that are either unaligned, or treated as unaligned and require an additional recycle through the pipeline. This typically adds about 10 cycles to the latency of the instruction. This only includes stores that cross the 128 byte boundary. Counted at finish time." + }, + { + "EventCode": "0x000000C8A4", + "EventName": "PM_ST1_UNALIGNED_FIN", + "BriefDescription": "Store instructions in ST1 port that are either unaligned, or treated as unaligned and require an additional recycle through the pipeline. This typically adds about 10 cycles to the latency of the instruction. This only includes stores that cross the 128 byte boundary. Counted at finish time." + }, + { + "EventCode": "0x000000C8B8", + "EventName": "PM_STCX_SUCCESS_CMPL", + "BriefDescription": "STCX instructions that completed successfully. Specifically, counts only when a pass status is returned from the nest." + }, + { + "EventCode": "0x000000D0B4", + "EventName": "PM_DC_PREF_STRIDED_CONF", + "BriefDescription": "A demand load referenced a line in an active strided prefetch stream. The stream could have been allocated through the hardware prefetch mechanism or through software." + }, + { + "EventCode": "0x000000F880", + "EventName": "PM_SNOOP_TLBIE_CYC", + "BriefDescription": "Cycles in which TLBIE snoops are executed in the LSU." + }, + { + "EventCode": "0x000000F084", + "EventName": "PM_SNOOP_TLBIE_CACHE_WALK_CYC", + "BriefDescription": "TLBIE snoop cycles in which the data cache is being walked." + }, + { + "EventCode": "0x000000F884", + "EventName": "PM_SNOOP_TLBIE_WAIT_ST_CYC", + "BriefDescription": "TLBIE snoop cycles in which older stores are still draining." + }, + { + "EventCode": "0x000000F088", + "EventName": "PM_SNOOP_TLBIE_WAIT_LD_CYC", + "BriefDescription": "TLBIE snoop cycles in which older loads are still draining." + }, + { + "EventCode": "0x000000F08C", + "EventName": "PM_SNOOP_TLBIE_WAIT_MMU_CYC", + "BriefDescription": "TLBIE snoop cycles in which the Load-Store unit is waiting for the MMU to finish invalidation." + }, + { + "EventCode": "0x0000004884", + "EventName": "PM_NO_FETCH_IBUF_FULL_CYC", + "BriefDescription": "Cycles in which no instructions are fetched because there is no room in the instruction buffers." + }, + { + "EventCode": "0x00000048B4", + "EventName": "PM_BR_TKN_UNCOND_FIN", + "BriefDescription": "An unconditional branch finished. All unconditional branches are taken." + }, + { + "EventCode": "0x0B0000016080", + "EventName": "PM_L2_TLBIE_SLBIE_START", + "BriefDescription": "NCU Master received a TLBIE/SLBIEG/SLBIAG operation from the core. Event count should be multiplied by 2 since the data is coming from a 2:1 clock domain and the data is time sliced across all 4 threads." + }, + { + "EventCode": "0x0B0000016880", + "EventName": "PM_L2_TLBIE_SLBIE_DELAY", + "BriefDescription": "Cycles when a TLBIE/SLBIEG/SLBIAG command was held in a hottemp condition by the NCU Master. Multiply this count by 1000 to obtain the total number of cycles. This can be divided by PM_L2_TLBIE_SLBIE_SENT to obtain the average time a TLBIE/SLBIEG/SLBIAG command was held. Event count should be multiplied by 2 since the data is coming from a 2:1 clock domain and the data is time sliced across all 4 threads." + }, + { + "EventCode": "0x0B0000026880", + "EventName": "PM_L2_SNP_TLBIE_SLBIE_DELAY", + "BriefDescription": "Cycles when a TLBIE/SLBIEG/SLBIAG that targets this thread's LPAR was in flight while in a hottemp condition. Multiply this count by 1000 to obtain the total number of cycles. This can be divided by PM_L2_SNP_TLBIE_SLBIE_START to obtain the overall efficiency. Note: 'inflight' means SnpTLB has been sent to core(ie doesn't include when SnpTLB is in NCU waiting to be launched serially behind different SnpTLB). The NCU Snooper gets in a 'hottemp' delay window when it detects it is above its TLBIE/SLBIE threshold for process SnpTLBIE/SLBIE with this core. Event count should be multiplied by 2 since the data is coming from a 2:1 clock domain and the data is time sliced across all 4 threads." } ] From 050f2a03aaadac13fff13f9ab7c5c1f0937ab729 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 16 Jul 2024 15:34:05 +0800 Subject: [PATCH 040/347] perf annotate: Convert comma to semicolon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace a comma between expression statements by a semicolon. Reviewed-by: Ian Rogers Signed-off-by: Chen Ni Cc: Adrian Hunter Cc: Ahelenia Ziemiańska Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Pekka Enberg Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240716073405.968801-1-nichen@iscas.ac.cn Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/annotate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index ea986430241e..fe991a81256b 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -985,7 +985,7 @@ int symbol__tui_annotate(struct map_symbol *ms, struct evsel *evsel, browser.b.width = notes->src->widths.max_line_len; browser.b.nr_entries = notes->src->nr_entries; - browser.b.entries = ¬es->src->source, + browser.b.entries = ¬es->src->source; browser.b.width += 18; /* Percentage */ if (annotate_opts.hide_src_code) From e60fc19eab439736009018406a19fad28e60e664 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 16 Jul 2024 15:43:40 +0800 Subject: [PATCH 041/347] perf daemon: Convert comma to semicolon Replace a comma between expression statements by a semicolon. Reviewed-by: Ian Rogers Signed-off-by: Chen Ni Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240716074340.968909-1-nichen@iscas.ac.cn Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-daemon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-daemon.c b/tools/perf/builtin-daemon.c index de76bbc50bfb..18f7f417ba11 100644 --- a/tools/perf/builtin-daemon.c +++ b/tools/perf/builtin-daemon.c @@ -1433,7 +1433,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); From 496cae1b3306e2ea7bbb8ca7ced082a2046afed9 Mon Sep 17 00:00:00 2001 From: Chen Ni Date: Tue, 16 Jul 2024 15:53:47 +0800 Subject: [PATCH 042/347] perf inject: Convert comma to semicolon Replace a comma between expression statements by a semicolon. Reviewed-by: Ian Rogers Signed-off-by: Chen Ni Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240716075347.969041-1-nichen@iscas.ac.cn Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index a212678d47be..7b4a5d56d279 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -2069,7 +2069,7 @@ static int __cmd_inject(struct perf_inject *inject) */ inject->tool.finished_init = host__finished_init; /* Obey finished round ordering */ - inject->tool.finished_round = host__finished_round, + inject->tool.finished_round = host__finished_round; /* Keep track of which CPU a VCPU is runnng on */ inject->tool.context_switch = host__context_switch; /* From 4194744602c8cd971ad7fd95710416ca0568d999 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 22 Jul 2024 16:27:56 +0100 Subject: [PATCH 043/347] perf cs-etm: Output 0 instead of 0xdeadbeef when exception packets are flushed Normally exception packets don't directly output a branch sample, but if they're the last record in a buffer then they will. Because they don't have addresses set we'll see the placeholder value CS_ETM_INVAL_ADDR (0xdeadbeef) in the output. Since commit 6035b6804bdf ("perf cs-etm: Support dummy address value for CS_ETM_TRACE_ON packet") we've used 0 as an externally visible "not set" address value. For consistency reasons and to not make exceptions look like an error, change them to use 0 too. This is particularly visible when doing userspace only tracing because trace is disabled when jumping to the kernel, causing the flush and then forcing the last exception packet to be emitted as a branch. With kernel trace included, there is no flush so exception packets don't generate samples until the next range packet and they'll pick up the correct address. Before: $ perf record -e cs_etm//u -- stress -i 1 -t 1 $ perf script -F comm,ip,addr,flags stress syscall ffffb7eedbc0 => deadbeefdeadbeef stress syscall ffffb7f14a14 => deadbeefdeadbeef stress syscall ffffb7eedbc0 => deadbeefdeadbeef After: stress syscall ffffb7eedbc0 => 0 stress syscall ffffb7f14a14 => 0 stress syscall ffffb7eedbc0 => 0 Reviewed-by: Mike Leach Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Leo Yan Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Suzuki Poulouse Cc: Will Deacon Cc: coresight@lists.linaro.org Cc: gankulkarni@os.amperecomputing.com Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240722152756.59453-2-james.clark@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cs-etm.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 5e9fbcfad7d4..d3e9c64d17d4 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -1267,8 +1267,12 @@ static inline int cs_etm__t32_instr_size(struct cs_etm_queue *etmq, static inline u64 cs_etm__first_executed_instr(struct cs_etm_packet *packet) { - /* Returns 0 for the CS_ETM_DISCONTINUITY packet */ - if (packet->sample_type == CS_ETM_DISCONTINUITY) + /* + * Return 0 for packets that have no addresses so that CS_ETM_INVAL_ADDR doesn't + * appear in samples. + */ + if (packet->sample_type == CS_ETM_DISCONTINUITY || + packet->sample_type == CS_ETM_EXCEPTION) return 0; return packet->start_addr; From ae8e4f4048b839c1cb333d9e3d20e634b430139e Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 23 Jul 2024 14:28:58 +0100 Subject: [PATCH 044/347] perf scripts python cs-etm: Restore first sample log in verbose mode The linked commit moved the early return on the first sample to before the verbose log, so move the log earlier too. Now the first sample is also logged and not skipped. Fixes: 2d98dbb4c9c5b09c ("perf scripts python arm-cs-trace-disasm.py: Do not ignore disam first sample") Reviewed-by: Leo Yan Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Benjamin Gray Cc: coresight@lists.linaro.org Cc: gankulkarni@os.amperecomputing.com Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ruidong Tian Cc: Suzuki Poulouse Link: https://lore.kernel.org/r/20240723132858.12747-1-james.clark@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/scripts/python/arm-cs-trace-disasm.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tools/perf/scripts/python/arm-cs-trace-disasm.py b/tools/perf/scripts/python/arm-cs-trace-disasm.py index d973c2baed1c..7aff02d84ffb 100755 --- a/tools/perf/scripts/python/arm-cs-trace-disasm.py +++ b/tools/perf/scripts/python/arm-cs-trace-disasm.py @@ -192,17 +192,16 @@ def process_event(param_dict): ip = sample["ip"] addr = sample["addr"] + if (options.verbose == True): + print("Event type: %s" % name) + print_sample(sample) + # Initialize CPU data if it's empty, and directly return back # if this is the first tracing event for this CPU. if (cpu_data.get(str(cpu) + 'addr') == None): cpu_data[str(cpu) + 'addr'] = addr return - - if (options.verbose == True): - print("Event type: %s" % name) - print_sample(sample) - # If cannot find dso so cannot dump assembler, bail out if (dso == '[unknown]'): return From c91928a8d5243f6b7355aab6325fe7545ee65404 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 15 Jul 2024 19:07:04 +0300 Subject: [PATCH 045/347] perf tools: Enable evsel__is_aux_event() to work for ARM/ARM64 Set pmu->auxtrace on ARM/ARM64 AUX area PMUs. evsel__is_aux_event() needs the setting to identify AUX area tracing selected events. Currently, the features that use evsel__is_aux_event() are used only by Intel PT, but that may change in the future. Reviewed-by: Andi Kleen Reviewed-by: Leo Yan Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Alexander Shishkin Cc: Heiko Carstens Cc: Hendrik Brueckner Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Jonathan Cameron Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Will Deacon Cc: Yicong Yang Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240715160712.127117-6-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm/util/pmu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/arch/arm/util/pmu.c b/tools/perf/arch/arm/util/pmu.c index 1c9541d01722..57dc94a6e38c 100644 --- a/tools/perf/arch/arm/util/pmu.c +++ b/tools/perf/arch/arm/util/pmu.c @@ -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 } From feab89bf991c940e2d44e45ededbde8640ac8830 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Mon, 15 Jul 2024 19:07:05 +0300 Subject: [PATCH 046/347] perf tools: Enable evsel__is_aux_event() to work for S390_CPUMSF evsel__is_aux_event() identifies AUX area tracing selected events. S390_CPUMSF uses a raw event type (PERF_TYPE_RAW - refer s390_cpumsf_evsel_is_auxtrace()) not a PMU type value that could be checked in evsel__is_aux_event(). However it sets needs_auxtrace_mmap (refer auxtrace_record__init()), so check that first. Currently, the features that use evsel__is_aux_event() are used only by Intel PT, but that may change in the future. Reviewed-by: Andi Kleen Reviewed-by: Leo Yan Signed-off-by: Adrian Hunter Acked-by: Ian Rogers Cc: Alexander Shishkin Cc: Heiko Carstens Cc: Hendrik Brueckner Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Jonathan Cameron Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Suzuki Poulouse Cc: Thomas Richter Cc: Will Deacon Cc: Yicong Yang Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240715160712.127117-7-adrian.hunter@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/pmu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 986166bc7c78..0b38c51bd6eb 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -1199,8 +1199,12 @@ void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu) bool evsel__is_aux_event(const struct evsel *evsel) { - struct perf_pmu *pmu = evsel__find_pmu(evsel); + struct perf_pmu *pmu; + if (evsel->needs_auxtrace_mmap) + return true; + + pmu = evsel__find_pmu(evsel); return pmu && pmu->auxtrace; } From 156e8dcfeceebce0694fb00542ac68e61a785fe1 Mon Sep 17 00:00:00 2001 From: "Dr. David Alan Gilbert" Date: Sat, 27 Jul 2024 18:59:19 +0100 Subject: [PATCH 047/347] perf test pmu: Remove unused test_pmus Commit aa1551f299ba ("perf test pmu: Refactor format test and exposed test APIs") added the 'test_pmus' list, but didn't use it. (It seems to put them on the other_pmus list?) Remove it. Fixes: aa1551f299ba414c ("perf test pmu: Refactor format test and exposed test APIs") Reviewed-by: Ian Rogers Signed-off-by: Dr. David Alan Gilbert Cc: Ian Rogers Cc: Kan Liang Link: https://lore.kernel.org/lkml/20240727175919.1041468-1-linux@treblig.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/pmu.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c index 40132655ccd1..0b2f04a55d7b 100644 --- a/tools/perf/tests/pmu.c +++ b/tools/perf/tests/pmu.c @@ -18,9 +18,6 @@ #include #include -/* Fake PMUs created in temp directory. */ -static LIST_HEAD(test_pmus); - /* Cleanup test PMU directory. */ static int test_pmu_put(const char *dir, struct perf_pmu *pmu) { From c77800894b5a7d7d37b83862fd732230c5c3e518 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sun, 28 Jul 2024 17:41:24 -0700 Subject: [PATCH 048/347] perf ftrace: Add 'tail' option to --graph-opts The 'graph-tail' option is to print function name as a comment at the end. This is useful when a large function is mixed with other functions (possibly from different CPUs). For example, $ sudo perf ftrace -- perf stat true ... 1) | get_unused_fd_flags() { 1) | alloc_fd() { 1) 0.178 us | _raw_spin_lock(); 1) 0.187 us | expand_files(); 1) 0.169 us | _raw_spin_unlock(); 1) 1.211 us | } 1) 1.503 us | } $ sudo perf ftrace --graph-opts tail -- perf stat true ... 1) | get_unused_fd_flags() { 1) | alloc_fd() { 1) 0.099 us | _raw_spin_lock(); 1) 0.083 us | expand_files(); 1) 0.081 us | _raw_spin_unlock(); 1) 0.601 us | } /* alloc_fd */ 1) 0.751 us | } /* get_unused_fd_flags */ Reviewed-by: Ian Rogers Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Changbin Du Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Steven Rostedt (VMware) Link: https://lore.kernel.org/lkml/20240729004127.238611-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-ftrace.txt | 1 + tools/perf/builtin-ftrace.c | 18 ++++++++++++++++++ tools/perf/util/ftrace.h | 1 + 3 files changed, 20 insertions(+) diff --git a/tools/perf/Documentation/perf-ftrace.txt b/tools/perf/Documentation/perf-ftrace.txt index d780b93fcf87..7ea1645a13cf 100644 --- a/tools/perf/Documentation/perf-ftrace.txt +++ b/tools/perf/Documentation/perf-ftrace.txt @@ -125,6 +125,7 @@ OPTIONS for 'perf ftrace trace' - verbose - Show process names, PIDs, timestamps, etc. - thresh= - Setup trace duration threshold in microseconds. - depth= - Set max depth for function graph tracer to follow. + - tail - Print function name at the end. OPTIONS for 'perf ftrace latency' diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c index eb30c8eca488..33c52ebda2fd 100644 --- a/tools/perf/builtin-ftrace.c +++ b/tools/perf/builtin-ftrace.c @@ -228,6 +228,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 +465,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 +552,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; } @@ -1099,6 +1116,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, } }; diff --git a/tools/perf/util/ftrace.h b/tools/perf/util/ftrace.h index 558efcb98d25..2515e841c64c 100644 --- a/tools/perf/util/ftrace.h +++ b/tools/perf/util/ftrace.h @@ -25,6 +25,7 @@ struct perf_ftrace { int graph_noirqs; int graph_verbose; int graph_thresh; + int graph_tail; }; struct filter_entry { From 608585f43f9e4798db7585d8999c0404103d4b66 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sun, 28 Jul 2024 17:41:25 -0700 Subject: [PATCH 049/347] perf ftrace: Factor out check_ftrace_capable() The check is a common part of the ftrace commands, let's move it out. Reviewed-by: Ian Rogers Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Changbin Du Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Steven Rostedt (VMware) Link: https://lore.kernel.org/lkml/20240729004127.238611-3-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-ftrace.c | 44 +++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c index 33c52ebda2fd..978608ecd89c 100644 --- a/tools/perf/builtin-ftrace.c +++ b/tools/perf/builtin-ftrace.c @@ -59,6 +59,22 @@ static void ftrace__workload_exec_failed_signal(int signo __maybe_unused, done = true; } +static int check_ftrace_capable(void) +{ + 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; + } + return 0; +} + static int __write_tracing_file(const char *name, const char *val, bool append) { char *file; @@ -586,18 +602,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) { @@ -902,18 +906,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; @@ -1220,6 +1212,10 @@ int cmd_ftrace(int argc, const char **argv) signal(SIGCHLD, sig_handler); signal(SIGPIPE, sig_handler); + ret = check_ftrace_capable(); + if (ret < 0) + return -1; + ret = perf_config(perf_ftrace_config, &ftrace); if (ret < 0) return -1; From 0f223813edd051a516ec4b1fc23b1fdc00dd3b6d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sun, 28 Jul 2024 17:41:26 -0700 Subject: [PATCH 050/347] perf ftrace: Add 'profile' command The 'perf ftrace profile' command is to get function execution profiles using function-graph tracer so that users can see the total, average, max execution time as well as the number of invocations easily. The following is a profile for the perf_event_open syscall. $ sudo perf ftrace profile -G __x64_sys_perf_event_open -- \ perf stat -e cycles -C1 true 2> /dev/null | head # Total (us) Avg (us) Max (us) Count Function 65.611 65.611 65.611 1 __x64_sys_perf_event_open 30.527 30.527 30.527 1 anon_inode_getfile 30.260 30.260 30.260 1 __anon_inode_getfile 29.700 29.700 29.700 1 alloc_file_pseudo 17.578 17.578 17.578 1 d_alloc_pseudo 17.382 17.382 17.382 1 __d_alloc 16.738 16.738 16.738 1 kmem_cache_alloc_lru 15.686 15.686 15.686 1 perf_event_alloc 14.012 7.006 11.264 2 obj_cgroup_charge # Reviewed-by: Ian Rogers Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Changbin Du Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Steven Rostedt (VMware) Link: https://lore.kernel.org/lkml/20240729004127.238611-4-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-ftrace.txt | 42 ++- tools/perf/builtin-ftrace.c | 318 ++++++++++++++++++++++- tools/perf/util/ftrace.h | 2 + 3 files changed, 359 insertions(+), 3 deletions(-) diff --git a/tools/perf/Documentation/perf-ftrace.txt b/tools/perf/Documentation/perf-ftrace.txt index 7ea1645a13cf..33f32467f287 100644 --- a/tools/perf/Documentation/perf-ftrace.txt +++ b/tools/perf/Documentation/perf-ftrace.txt @@ -9,7 +9,7 @@ perf-ftrace - simple wrapper for kernel's ftrace functionality SYNOPSIS -------- [verse] -'perf ftrace' {trace|latency} +'perf ftrace' {trace|latency|profile} 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 @@ -146,6 +149,43 @@ 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, is expected to + be a number with appended unit character - B/K/M/G. + + SEE ALSO -------- linkperf:perf-record[1], linkperf:perf-trace[1] diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c index 978608ecd89c..ae9389435d1b 100644 --- a/tools/perf/builtin-ftrace.c +++ b/tools/perf/builtin-ftrace.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -22,15 +23,18 @@ #include "debug.h" #include #include +#include #include #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" @@ -959,6 +963,294 @@ 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); +} + +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; + + /* compare by total time */ + if ((p1->st.n * p1->st.mean) > (p2->st.n * p2->st.mean)) + 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; @@ -1126,6 +1418,7 @@ enum perf_ftrace_subcommand { PERF_FTRACE_NONE, PERF_FTRACE_TRACE, PERF_FTRACE_LATENCY, + PERF_FTRACE_PROFILE, }; int cmd_ftrace(int argc, const char **argv) @@ -1191,13 +1484,28 @@ 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_PARENT(common_options), + }; const struct option *options = ftrace_options; const char * const ftrace_usage[] = { "perf ftrace [] []", "perf ftrace [] -- [] []", - "perf ftrace {trace|latency} [] []", - "perf ftrace {trace|latency} [] -- [] []", + "perf ftrace {trace|latency|profile} [] []", + "perf ftrace {trace|latency|profile} [] -- [] []", NULL }; enum perf_ftrace_subcommand subcmd = PERF_FTRACE_NONE; @@ -1226,6 +1534,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) { @@ -1261,6 +1572,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"); diff --git a/tools/perf/util/ftrace.h b/tools/perf/util/ftrace.h index 2515e841c64c..bae649ef50e8 100644 --- a/tools/perf/util/ftrace.h +++ b/tools/perf/util/ftrace.h @@ -6,6 +6,7 @@ #include "target.h" struct evlist; +struct hashamp; struct perf_ftrace { struct evlist *evlist; @@ -15,6 +16,7 @@ struct perf_ftrace { struct list_head notrace; struct list_head graph_funcs; struct list_head nograph_funcs; + struct hashmap *profile_hash; unsigned long percpu_buffer_size; bool inherit; bool use_nsec; From 74ae366c37b71b46be7f2fa45fa4b2c9c6708fbe Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sun, 28 Jul 2024 17:41:27 -0700 Subject: [PATCH 051/347] perf ftrace profile: Add -s/--sort option The -s/--sort option is to sort the output by given column. $ sudo perf ftrace profile -s max sync | head # Total (us) Avg (us) Max (us) Count Function 6301.811 6301.811 6301.811 1 __do_sys_sync 6301.328 6301.328 6301.328 1 ksys_sync 5320.300 1773.433 2858.819 3 iterate_supers 2755.875 17.012 2610.633 162 sync_fs_one_sb 2728.351 682.088 2610.413 4 ext4_sync_fs [ext4] 2603.654 2603.654 2603.654 1 jbd2_log_wait_commit [jbd2] 4750.615 593.827 2597.427 8 schedule 2164.986 26.728 2115.673 81 sync_inodes_one_sb 2143.842 26.467 2115.438 81 sync_inodes_sb Reviewed-by: Ian Rogers Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Changbin Du Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Steven Rostedt (VMware) Link: https://lore.kernel.org/lkml/20240729004127.238611-5-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-ftrace.txt | 5 ++ tools/perf/builtin-ftrace.c | 63 +++++++++++++++++++++++- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/tools/perf/Documentation/perf-ftrace.txt b/tools/perf/Documentation/perf-ftrace.txt index 33f32467f287..eaec8253be68 100644 --- a/tools/perf/Documentation/perf-ftrace.txt +++ b/tools/perf/Documentation/perf-ftrace.txt @@ -185,6 +185,11 @@ OPTIONS for 'perf ftrace profile' Set the size of per-cpu tracing buffer, 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 -------- diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c index ae9389435d1b..a615c405d98f 100644 --- a/tools/perf/builtin-ftrace.c +++ b/tools/perf/builtin-ftrace.c @@ -1090,15 +1090,47 @@ static int parse_func_duration(struct perf_ftrace *ftrace, char *line, size_t le 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; - /* compare by total time */ - if ((p1->st.n * p1->st.mean) > (p2->st.n * p2->st.mean)) + 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; @@ -1414,6 +1446,30 @@ 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, @@ -1497,6 +1553,9 @@ int cmd_ftrace(int argc, const char **argv) "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; From 4ed0f392e7dbd2e90a903bdd77d1f6e61b7d3073 Mon Sep 17 00:00:00 2001 From: Weilin Wang Date: Wed, 22 May 2024 13:42:54 -0700 Subject: [PATCH 052/347] perf test: make metric validation test return early when there is no metric supported on the test system Add a check to return the metric validation test early when perf list metric does not output any metric. This would happen when NO_JEVENTS=1 is set or in a system that there is no metric supported. Signed-off-by: Weilin Wang Tested-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Caleb Biggers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Cc: Perry Taylor Cc: Peter Zijlstra Cc: Samantha Alt Link: https://lore.kernel.org/lkml/20240522204254.1841420-1-weilin.wang@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/lib/perf_metric_validation.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tools/perf/tests/shell/lib/perf_metric_validation.py b/tools/perf/tests/shell/lib/perf_metric_validation.py index a2d235252183..0b94216c9c46 100644 --- a/tools/perf/tests/shell/lib/perf_metric_validation.py +++ b/tools/perf/tests/shell/lib/perf_metric_validation.py @@ -95,7 +95,7 @@ class Validator: indent=4) def get_results(self, idx: int = 0): - return self.results[idx] + return self.results.get(idx) def get_bounds(self, lb, ub, error, alias={}, ridx: int = 0) -> list: """ @@ -173,7 +173,10 @@ class Validator: pcnt = 0 tcnt = 0 rerun = list() - for name, val in self.get_results().items(): + results = self.get_results() + if not results: + return + for name, val in results.items(): if val < 0: negmetric[name] = val rerun.append(name) @@ -532,6 +535,9 @@ class Validator: ''' if not self.collectlist: self.parse_perf_metrics() + if not self.metrics: + print("No metric found for testing") + return 0 self.create_rules() for i in range(0, len(self.workloads)): self.wlidx = i From ccd6fcda257573081b308f3f8f002a15810eba7c Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Thu, 25 Jul 2024 07:46:19 +0100 Subject: [PATCH 053/347] perf arm-spe: Extract evsel setting up The evsel for Arm SPE PMU needs to be set up. Extract the setting up into a function arm_spe_setup_evsel(). Signed-off-by: Leo Yan Cc: Mark Rutland Cc: Suzuki K Poulose Cc: Ian Rogers Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Cc: James Clark Cc: Mike Leach Cc: Cc: John Garry Cc: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm64/util/arm-spe.c | 74 +++++++++++++++------------- 1 file changed, 40 insertions(+), 34 deletions(-) diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/util/arm-spe.c index 0b52e67edb3b..fe7942824113 100644 --- a/tools/perf/arch/arm64/util/arm-spe.c +++ b/tools/perf/arch/arm64/util/arm-spe.c @@ -132,6 +132,45 @@ 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) @@ -144,7 +183,6 @@ static int arm_spe_recording_options(struct auxtrace_record *itr, bool privileged = perf_event_paranoid_check(-1); struct evsel *tracking_evsel; int err; - u64 bit; sper->evlist = evlist; @@ -154,9 +192,6 @@ static int arm_spe_recording_options(struct auxtrace_record *itr, pr_err("There may be only one " ARM_SPE_PMU_NAME "x event\n"); 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,36 +257,7 @@ 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); - } - - /* - * 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); + arm_spe_setup_evsel(arm_spe_evsel, cpus); /* Add dummy event to keep tracking */ err = parse_event(evlist, "dummy:u"); From 1635bdca4b02bbe6529e769dd7d56a98960344fc Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Thu, 25 Jul 2024 07:46:20 +0100 Subject: [PATCH 054/347] perf arm-spe: Support multiple Arm SPE events As the flag 'auxtrace' has been set for Arm SPE events, now it is ready to use evsel__is_aux_event() to check if an event is AUX trace event or not. Use this function to replace the old checking for only the first Arm SPE event. Signed-off-by: Leo Yan Cc: Mark Rutland Cc: Suzuki K Poulose Cc: Ian Rogers Cc: Adrian Hunter Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Cc: James Clark Cc: Mike Leach Cc: Cc: John Garry Cc: Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm64/util/arm-spe.c | 37 ++++++++++++++++++---------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/util/arm-spe.c index fe7942824113..d59f6ca499f2 100644 --- a/tools/perf/arch/arm64/util/arm-spe.c +++ b/tools/perf/arch/arm64/util/arm-spe.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -177,8 +178,7 @@ static int arm_spe_recording_options(struct auxtrace_record *itr, { 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; @@ -187,12 +187,12 @@ static int arm_spe_recording_options(struct auxtrace_record *itr, 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; } - arm_spe_evsel = evsel; opts->full_auxtrace = true; } } @@ -257,7 +257,10 @@ 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); - arm_spe_setup_evsel(arm_spe_evsel, cpus); + evlist__for_each_entry_safe(evlist, tmp, evsel) { + if (evsel__is_aux_event(evsel)) + arm_spe_setup_evsel(evsel, cpus); + } /* Add dummy event to keep tracking */ err = parse_event(evlist, "dummy:u"); @@ -307,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) @@ -320,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) From d261f9ebcf424535fe04e720a1cfa023be409f52 Mon Sep 17 00:00:00 2001 From: Charlie Jenkins Date: Mon, 29 Jul 2024 16:11:02 -0700 Subject: [PATCH 055/347] libperf: Add gitignore Ignore files that are generated by libperf and libperf tests. Signed-off-by: Charlie Jenkins Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/lkml/20240729-libperf_gitignore-v1-1-1c70dd98edf9@rivosinc.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/perf/.gitignore | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tools/lib/perf/.gitignore diff --git a/tools/lib/perf/.gitignore b/tools/lib/perf/.gitignore new file mode 100644 index 000000000000..0f5b4af63f62 --- /dev/null +++ b/tools/lib/perf/.gitignore @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only +libperf.pc +libperf.so.* +tests-shared +tests-static From 839b1832e68a5b7d8c81c9281296b03e5ba7c31a Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 30 Jul 2024 14:23:00 +0800 Subject: [PATCH 056/347] perf tools: Fix wrong message when running "make JOBS=1" There is only one job when running "make JOBS=1", it should print "sequential build" rather than "parallel build". Before: $ cd tools/perf && make JOBS=1 BUILD: Doing 'make -j1' parallel build After: $ cd tools/perf && make JOBS=1 BUILD: Doing 'make -j1' sequential build Signed-off-by: Tiezhu Yang Tested-by: Arnaldo Carvalho de Melo Cc: Ingo Molnar Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/lkml/20240730062301.23244-2-yangtiezhu@loongson.cn Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/Makefile b/tools/perf/Makefile index 75f3f6e0a231..816d5d84816b 100644 --- a/tools/perf/Makefile +++ b/tools/perf/Makefile @@ -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 From b48543c451c30387b53ee6e202dda8d5303f6268 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 30 Jul 2024 14:23:01 +0800 Subject: [PATCH 057/347] perf list: Give clues if failed to open tracing events directory When executing the command "perf list", I met "Error: failed to open tracing events directory" twice, the first reason is that there is no "/sys/kernel/tracing/events" directory due to it does not enable the kernel tracing infrastructure with CONFIG_FTRACE, the second reason is that there is no root privileges. Add the error string to tell the users what happened and what should to do, and also call put_tracing_file() to free events_path a little later to avoid messy code in the error message. At the same time, just remove the redundant "/" of the file path in the function get_tracing_file(), otherwise it shows something like "/sys/kernel/tracing//events". Before: $ ./perf list Error: failed to open tracing events directory After: (1) Without CONFIG_FTRACE $ ./perf list Error: failed to open tracing events directory /sys/kernel/tracing/events: No such file or directory (2) With CONFIG_FTRACE but no root privileges $ ./perf list Error: failed to open tracing events directory /sys/kernel/tracing/events: Permission denied Committer testing: Redirect stdout to null to quickly test the patch: Before: $ perf list > /dev/null Error: failed to open tracing events directory $ After: $ perf list > /dev/null Error: failed to open tracing events directory /sys/kernel/tracing/events: Permission denied $ Signed-off-by: Tiezhu Yang Tested-by: Arnaldo Carvalho de Melo Cc: Ingo Molnar Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/lkml/20240730062301.23244-3-yangtiezhu@loongson.cn Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/api/fs/tracing_path.c | 2 +- tools/perf/util/print-events.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/lib/api/fs/tracing_path.c b/tools/lib/api/fs/tracing_path.c index 30745f35d0d2..834fd64c7130 100644 --- a/tools/lib/api/fs/tracing_path.c +++ b/tools/lib/api/fs/tracing_path.c @@ -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; diff --git a/tools/perf/util/print-events.c b/tools/perf/util/print-events.c index 3f38c27f0157..81e0135cddf0 100644 --- a/tools/perf/util/print-events.c +++ b/tools/perf/util/print-events.c @@ -68,11 +68,12 @@ void print_tracepoint_events(const struct print_callbacks *print_cb __maybe_unus struct dirent **sys_namelist = NULL; int sys_items; - put_tracing_file(events_path); if (events_fd < 0) { pr_err("Error: failed to open tracing events directory\n"); + pr_err("%s: %s\n", events_path, strerror(errno)); return; } + put_tracing_file(events_path); sys_items = tracing_events__scandir_alphasort(&sys_namelist); From 0f2c0400b560a3752fac9e583c1576a53a51cde9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 30 Jul 2024 12:17:43 -0700 Subject: [PATCH 058/347] perf jevents: Use name for special find value (PMU_EVENTS__NOT_FOUND) -1000 was used as a special value added in Commit 3d5045492ab2 ("perf pmu-events: Add pmu_events_table__find_event()") to show that 1 table lacked a PMU/event but that didn't terminate the search in other tables. Add a new constant PMU_EVENTS__NOT_FOUND for this value and use it. Reviewed-by: John Garry Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jing Zhang Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Oliver Sang Cc: Peter Zijlstra Cc: Philip Li Cc: Sandipan Das Cc: Weilin Wang Cc: Xu Yang Link: https://lore.kernel.org/r/20240730191744.3097329-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/jevents.py | 6 +++--- tools/perf/pmu-events/pmu-events.h | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py index ac9b7ca41856..731776e29f47 100755 --- a/tools/perf/pmu-events/jevents.py +++ b/tools/perf/pmu-events/jevents.py @@ -906,7 +906,7 @@ static int pmu_events_table__find_event_pmu(const struct pmu_events_table *table do_call: return fn ? fn(&pe, table, data) : 0; } - return -1000; + return PMU_EVENTS__NOT_FOUND; } int pmu_events_table__for_each_event(const struct pmu_events_table *table, @@ -944,10 +944,10 @@ int pmu_events_table__find_event(const struct pmu_events_table *table, continue; ret = pmu_events_table__find_event_pmu(table, table_pmu, name, fn, data); - if (ret != -1000) + if (ret != PMU_EVENTS__NOT_FOUND) return ret; } - return -1000; + return PMU_EVENTS__NOT_FOUND; } size_t pmu_events_table__num_events(const struct pmu_events_table *table, diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h index f5aa96f1685c..5435ad92180c 100644 --- a/tools/perf/pmu-events/pmu-events.h +++ b/tools/perf/pmu-events/pmu-events.h @@ -70,6 +70,8 @@ struct pmu_metric { struct pmu_events_table; struct pmu_metrics_table; +#define PMU_EVENTS__NOT_FOUND -1000 + typedef int (*pmu_event_iter_fn)(const struct pmu_event *pe, const struct pmu_events_table *table, void *data); @@ -82,6 +84,13 @@ int pmu_events_table__for_each_event(const struct pmu_events_table *table, struct perf_pmu *pmu, pmu_event_iter_fn fn, void *data); +/* + * Search for table and entry matching with pmu__name_match. Each matching event + * has fn called on it. 0 implies to success/continue the search while non-zero + * means to terminate. The special value PMU_EVENTS__NOT_FOUND is used to + * indicate no event was found in one of the tables which doesn't terminate the + * search of all tables. + */ int pmu_events_table__find_event(const struct pmu_events_table *table, struct perf_pmu *pmu, const char *name, From edb08cdd10b57d30631492e534f5bc13a78e739b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 3 Jul 2024 15:30:28 -0700 Subject: [PATCH 059/347] perf bpf-filter: Make filters map a single entry hashmap And the value is now an array. This is to support multiple filter entries in the map later. No functional changes intended. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: KP Singh Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Cc: Stephane Eranian Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20240703223035.2024586-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-filter.c | 81 ++++++++++++++------ tools/perf/util/bpf_skel/sample-filter.h | 1 + tools/perf/util/bpf_skel/sample_filter.bpf.c | 39 +++++----- 3 files changed, 78 insertions(+), 43 deletions(-) diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c index 04f98b6bb291..2510832d83f9 100644 --- a/tools/perf/util/bpf-filter.c +++ b/tools/perf/util/bpf-filter.c @@ -93,71 +93,102 @@ static int check_sample_flags(struct evsel *evsel, struct perf_bpf_filter_expr * int perf_bpf_filter__prepare(struct evsel *evsel) { - int i, x, y, fd; + int i, x, y, fd, ret; struct sample_filter_bpf *skel; struct bpf_program *prog; struct bpf_link *link; struct perf_bpf_filter_expr *expr; + struct perf_bpf_filter_entry *entry; + + entry = calloc(MAX_FILTERS, sizeof(*entry)); + if (entry == NULL) + return -1; skel = sample_filter_bpf__open_and_load(); if (!skel) { pr_err("Failed to load perf sample-filter BPF skeleton\n"); - return -1; + ret = -EPERM; + goto err; } i = 0; fd = bpf_map__fd(skel->maps.filters); list_for_each_entry(expr, &evsel->bpf_filters, list) { - struct perf_bpf_filter_entry entry = { - .op = expr->op, - .part = expr->part, - .term = expr->term, - .value = expr->val, - }; + if (check_sample_flags(evsel, expr) < 0) { + ret = -EINVAL; + goto err; + } - if (check_sample_flags(evsel, expr) < 0) - return -1; + if (i == MAX_FILTERS) { + ret = -E2BIG; + goto err; + } - bpf_map_update_elem(fd, &i, &entry, BPF_ANY); + entry[i].op = expr->op; + entry[i].part = expr->part; + entry[i].term = expr->term; + entry[i].value = expr->val; i++; if (expr->op == PBF_OP_GROUP_BEGIN) { struct perf_bpf_filter_expr *group; list_for_each_entry(group, &expr->groups, list) { - struct perf_bpf_filter_entry group_entry = { - .op = group->op, - .part = group->part, - .term = group->term, - .value = group->val, - }; - bpf_map_update_elem(fd, &i, &group_entry, BPF_ANY); + if (i == MAX_FILTERS) { + ret = -E2BIG; + goto err; + } + + entry[i].op = group->op; + entry[i].part = group->part; + entry[i].term = group->term; + entry[i].value = group->val; i++; } - memset(&entry, 0, sizeof(entry)); - entry.op = PBF_OP_GROUP_END; - bpf_map_update_elem(fd, &i, &entry, BPF_ANY); + if (i == MAX_FILTERS) { + ret = -E2BIG; + goto err; + } + + entry[i].op = PBF_OP_GROUP_END; i++; } } - if (i > MAX_FILTERS) { - pr_err("Too many filters: %d (max = %d)\n", i, MAX_FILTERS); - return -1; + if (i < MAX_FILTERS) { + /* to terminate the loop early */ + entry[i].op = PBF_OP_DONE; + i++; } + + /* The filters map has only one entry for now */ + i = 0; + if (bpf_map_update_elem(fd, &i, entry, BPF_ANY) < 0) { + ret = -errno; + pr_err("Failed to update the filter map\n"); + goto err; + } + prog = skel->progs.perf_sample_filter; for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) { for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) { link = bpf_program__attach_perf_event(prog, FD(evsel, x, y)); if (IS_ERR(link)) { pr_err("Failed to attach perf sample-filter program\n"); - return PTR_ERR(link); + ret = PTR_ERR(link); + goto err; } } } + free(entry); evsel->bpf_skel = skel; return 0; + +err: + free(entry); + sample_filter_bpf__destroy(skel); + return ret; } int perf_bpf_filter__destroy(struct evsel *evsel) diff --git a/tools/perf/util/bpf_skel/sample-filter.h b/tools/perf/util/bpf_skel/sample-filter.h index 350efa121026..bb6a1b91f1df 100644 --- a/tools/perf/util/bpf_skel/sample-filter.h +++ b/tools/perf/util/bpf_skel/sample-filter.h @@ -14,6 +14,7 @@ enum perf_bpf_filter_op { PBF_OP_AND, PBF_OP_GROUP_BEGIN, PBF_OP_GROUP_END, + PBF_OP_DONE, }; enum perf_bpf_filter_term { diff --git a/tools/perf/util/bpf_skel/sample_filter.bpf.c b/tools/perf/util/bpf_skel/sample_filter.bpf.c index f59985101973..0d56e52b922c 100644 --- a/tools/perf/util/bpf_skel/sample_filter.bpf.c +++ b/tools/perf/util/bpf_skel/sample_filter.bpf.c @@ -9,10 +9,10 @@ /* BPF map that will be filled by user space */ struct filters { - __uint(type, BPF_MAP_TYPE_ARRAY); + __uint(type, BPF_MAP_TYPE_HASH); __type(key, int); - __type(value, struct perf_bpf_filter_entry); - __uint(max_entries, MAX_FILTERS); + __type(value, struct perf_bpf_filter_entry[MAX_FILTERS]); + __uint(max_entries, 1); } filters SEC(".maps"); int dropped; @@ -179,39 +179,39 @@ int perf_sample_filter(void *ctx) __u64 sample_data; int in_group = 0; int group_result = 0; - int i; + int i, k; kctx = bpf_cast_to_kern_ctx(ctx); + k = 0; + entry = bpf_map_lookup_elem(&filters, &k); + if (entry == NULL) + goto drop; + for (i = 0; i < MAX_FILTERS; i++) { - int key = i; /* needed for verifier :( */ + sample_data = perf_get_sample(kctx, &entry[i]); - entry = bpf_map_lookup_elem(&filters, &key); - if (entry == NULL) - break; - sample_data = perf_get_sample(kctx, entry); - - switch (entry->op) { + switch (entry[i].op) { case PBF_OP_EQ: - CHECK_RESULT(sample_data, ==, entry->value) + CHECK_RESULT(sample_data, ==, entry[i].value) break; case PBF_OP_NEQ: - CHECK_RESULT(sample_data, !=, entry->value) + CHECK_RESULT(sample_data, !=, entry[i].value) break; case PBF_OP_GT: - CHECK_RESULT(sample_data, >, entry->value) + CHECK_RESULT(sample_data, >, entry[i].value) break; case PBF_OP_GE: - CHECK_RESULT(sample_data, >=, entry->value) + CHECK_RESULT(sample_data, >=, entry[i].value) break; case PBF_OP_LT: - CHECK_RESULT(sample_data, <, entry->value) + CHECK_RESULT(sample_data, <, entry[i].value) break; case PBF_OP_LE: - CHECK_RESULT(sample_data, <=, entry->value) + CHECK_RESULT(sample_data, <=, entry[i].value) break; case PBF_OP_AND: - CHECK_RESULT(sample_data, &, entry->value) + CHECK_RESULT(sample_data, &, entry[i].value) break; case PBF_OP_GROUP_BEGIN: in_group = 1; @@ -222,6 +222,9 @@ int perf_sample_filter(void *ctx) goto drop; in_group = 0; break; + case PBF_OP_DONE: + /* no failures so far, accept it */ + return 1; } } /* generate sample data */ From 966854e72f6e8a259609ea3c7fd78215e6606c7b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 3 Jul 2024 15:30:29 -0700 Subject: [PATCH 060/347] perf bpf-filter: Pass 'target' to perf_bpf_filter__prepare() This is needed to prepare target-specific actions in the later patch. We want to reuse the pinned BPF program and map for regular users to profile their own processes. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: KP Singh Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Cc: Stephane Eranian Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20240703223035.2024586-3-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 2 +- tools/perf/builtin-stat.c | 2 +- tools/perf/builtin-top.c | 2 +- tools/perf/builtin-trace.c | 2 +- tools/perf/util/bpf-filter.c | 2 +- tools/perf/util/bpf-filter.h | 6 ++++-- tools/perf/util/evlist.c | 5 +++-- tools/perf/util/evlist.h | 4 +++- 8 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index a94516e8c522..c599e620ee89 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1389,7 +1389,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))); diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 661832756a24..1f92445f7480 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -833,7 +833,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))); diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index e8cbbf10d361..d1a06a88d693 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -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))); diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 488c2cedc110..ef951ce1a0dd 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -4135,7 +4135,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; diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c index 2510832d83f9..0b2eca56aa10 100644 --- a/tools/perf/util/bpf-filter.c +++ b/tools/perf/util/bpf-filter.c @@ -91,7 +91,7 @@ static int check_sample_flags(struct evsel *evsel, struct perf_bpf_filter_expr * return -1; } -int perf_bpf_filter__prepare(struct evsel *evsel) +int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target __maybe_unused) { int i, x, y, fd, ret; struct sample_filter_bpf *skel; diff --git a/tools/perf/util/bpf-filter.h b/tools/perf/util/bpf-filter.h index cd6764442c16..605a3d0226e0 100644 --- a/tools/perf/util/bpf-filter.h +++ b/tools/perf/util/bpf-filter.h @@ -16,6 +16,7 @@ struct perf_bpf_filter_expr { }; struct evsel; +struct target; #ifdef HAVE_BPF_SKEL struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(enum perf_bpf_filter_term term, @@ -23,7 +24,7 @@ struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(enum perf_bpf_filter_term enum perf_bpf_filter_op op, unsigned long val); int perf_bpf_filter__parse(struct list_head *expr_head, const char *str); -int perf_bpf_filter__prepare(struct evsel *evsel); +int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target); int perf_bpf_filter__destroy(struct evsel *evsel); u64 perf_bpf_filter__lost_count(struct evsel *evsel); @@ -34,7 +35,8 @@ static inline int perf_bpf_filter__parse(struct list_head *expr_head __maybe_unu { return -EOPNOTSUPP; } -static inline int perf_bpf_filter__prepare(struct evsel *evsel __maybe_unused) +static inline int perf_bpf_filter__prepare(struct evsel *evsel __maybe_unused, + struct target *target __maybe_unused) { return -EOPNOTSUPP; } diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 3a719edafc7a..1417f9a23083 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1086,7 +1086,8 @@ out_delete_threads: return -1; } -int evlist__apply_filters(struct evlist *evlist, struct evsel **err_evsel) +int evlist__apply_filters(struct evlist *evlist, struct evsel **err_evsel, + struct target *target) { struct evsel *evsel; int err = 0; @@ -1108,7 +1109,7 @@ int evlist__apply_filters(struct evlist *evlist, struct evsel **err_evsel) * non-tracepoint events can have BPF filters. */ if (!list_empty(&evsel->bpf_filters)) { - err = perf_bpf_filter__prepare(evsel); + err = perf_bpf_filter__prepare(evsel, target); if (err) { *err_evsel = evsel; break; diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index cb91dc9117a2..cccc34da5a02 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -20,6 +20,7 @@ struct pollfd; struct thread_map; struct perf_cpu_map; struct record_opts; +struct target; /* * State machine of bkw_mmap_state: @@ -212,7 +213,8 @@ void evlist__enable_non_dummy(struct evlist *evlist); void evlist__set_selected(struct evlist *evlist, struct evsel *evsel); int evlist__create_maps(struct evlist *evlist, struct target *target); -int evlist__apply_filters(struct evlist *evlist, struct evsel **err_evsel); +int evlist__apply_filters(struct evlist *evlist, struct evsel **err_evsel, + struct target *target); u64 __evlist__combined_sample_type(struct evlist *evlist); u64 evlist__combined_sample_type(struct evlist *evlist); From eb1693b1150d4b99af5241242d6bcd6290cf68a0 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 3 Jul 2024 15:30:30 -0700 Subject: [PATCH 061/347] perf bpf-filter: Split per-task filter use case If the target is a list of tasks, it can use a shared hash map for filter expressions. The key of the filter map is an integer index like in an array. A separate pid_hash map is added to get the index for the filter map using the tgid. For system-wide mode including per-cpu or per-user targets are handled by the single entry map like before. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: KP Singh Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Cc: Stephane Eranian Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20240703223035.2024586-4-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-filter.c | 186 +++++++++++++++---- tools/perf/util/bpf_skel/sample-filter.h | 1 + tools/perf/util/bpf_skel/sample_filter.bpf.c | 21 +++ 3 files changed, 168 insertions(+), 40 deletions(-) diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c index 0b2eca56aa10..5ec0e0955ec4 100644 --- a/tools/perf/util/bpf-filter.c +++ b/tools/perf/util/bpf-filter.c @@ -3,10 +3,13 @@ #include #include +#include #include +#include #include "util/debug.h" #include "util/evsel.h" +#include "util/target.h" #include "util/bpf-filter.h" #include @@ -91,38 +94,17 @@ static int check_sample_flags(struct evsel *evsel, struct perf_bpf_filter_expr * return -1; } -int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target __maybe_unused) +static int get_filter_entries(struct evsel *evsel, struct perf_bpf_filter_entry *entry) { - int i, x, y, fd, ret; - struct sample_filter_bpf *skel; - struct bpf_program *prog; - struct bpf_link *link; + int i = 0; struct perf_bpf_filter_expr *expr; - struct perf_bpf_filter_entry *entry; - entry = calloc(MAX_FILTERS, sizeof(*entry)); - if (entry == NULL) - return -1; - - skel = sample_filter_bpf__open_and_load(); - if (!skel) { - pr_err("Failed to load perf sample-filter BPF skeleton\n"); - ret = -EPERM; - goto err; - } - - i = 0; - fd = bpf_map__fd(skel->maps.filters); list_for_each_entry(expr, &evsel->bpf_filters, list) { - if (check_sample_flags(evsel, expr) < 0) { - ret = -EINVAL; - goto err; - } + if (check_sample_flags(evsel, expr) < 0) + return -EINVAL; - if (i == MAX_FILTERS) { - ret = -E2BIG; - goto err; - } + if (i == MAX_FILTERS) + return -E2BIG; entry[i].op = expr->op; entry[i].part = expr->part; @@ -134,10 +116,8 @@ int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target __maybe_ struct perf_bpf_filter_expr *group; list_for_each_entry(group, &expr->groups, list) { - if (i == MAX_FILTERS) { - ret = -E2BIG; - goto err; - } + if (i == MAX_FILTERS) + return -E2BIG; entry[i].op = group->op; entry[i].part = group->part; @@ -146,10 +126,8 @@ int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target __maybe_ i++; } - if (i == MAX_FILTERS) { - ret = -E2BIG; - goto err; - } + if (i == MAX_FILTERS) + return -E2BIG; entry[i].op = PBF_OP_GROUP_END; i++; @@ -161,15 +139,143 @@ int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target __maybe_ entry[i].op = PBF_OP_DONE; i++; } + return 0; +} - /* The filters map has only one entry for now */ - i = 0; - if (bpf_map_update_elem(fd, &i, entry, BPF_ANY) < 0) { - ret = -errno; - pr_err("Failed to update the filter map\n"); +static int convert_to_tgid(int tid) +{ + char path[128]; + char *buf, *p, *q; + int tgid; + size_t len; + + scnprintf(path, sizeof(path), "%d/status", tid); + if (procfs__read_str(path, &buf, &len) < 0) + return -1; + + p = strstr(buf, "Tgid:"); + if (p == NULL) { + free(buf); + return -1; + } + + tgid = strtol(p + 6, &q, 0); + free(buf); + if (*q != '\n') + return -1; + + return tgid; +} + +static int update_pid_hash(struct sample_filter_bpf *skel, struct evsel *evsel, + struct perf_bpf_filter_entry *entry) +{ + int filter_idx; + int nr, last; + int fd = bpf_map__fd(skel->maps.filters); + struct perf_thread_map *threads; + + /* Find the first available entry in the filters map */ + for (filter_idx = 0; filter_idx < MAX_FILTERS; filter_idx++) { + if (bpf_map_update_elem(fd, &filter_idx, entry, BPF_NOEXIST) == 0) + break; + } + + if (filter_idx == MAX_FILTERS) { + pr_err("Too many users for the filter map\n"); + return -EBUSY; + } + + threads = perf_evsel__threads(&evsel->core); + if (threads == NULL) { + pr_err("Cannot get the thread list of the event\n"); + return -EINVAL; + } + + /* save the index to a hash map */ + fd = bpf_map__fd(skel->maps.pid_hash); + + last = -1; + nr = perf_thread_map__nr(threads); + for (int i = 0; i < nr; i++) { + int pid = perf_thread_map__pid(threads, i); + int tgid; + + /* it actually needs tgid, let's get tgid from /proc. */ + tgid = convert_to_tgid(pid); + if (tgid < 0) { + /* the thread may be dead, ignore. */ + continue; + } + + if (tgid == last) + continue; + last = tgid; + + if (bpf_map_update_elem(fd, &tgid, &filter_idx, BPF_ANY) < 0) { + pr_err("Failed to update the pid hash\n"); + return -errno; + } + pr_debug("pid hash: %d -> %d\n", tgid, filter_idx); + } + return 0; +} + +int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target) +{ + int i, x, y, fd, ret; + struct sample_filter_bpf *skel = NULL; + struct bpf_program *prog; + struct bpf_link *link; + struct perf_bpf_filter_entry *entry; + bool needs_pid_hash = !target__has_cpu(target) && !target->uid_str; + + entry = calloc(MAX_FILTERS, sizeof(*entry)); + if (entry == NULL) + return -1; + + ret = get_filter_entries(evsel, entry); + if (ret < 0) { + pr_err("Failed to process filter entries\n"); goto err; } + skel = sample_filter_bpf__open(); + if (!skel) { + pr_err("Failed to open perf sample-filter BPF skeleton\n"); + ret = -EPERM; + goto err; + } + + if (needs_pid_hash) { + bpf_map__set_max_entries(skel->maps.filters, MAX_FILTERS); + bpf_map__set_max_entries(skel->maps.pid_hash, MAX_PIDS); + skel->rodata->use_pid_hash = 1; + } + + if (sample_filter_bpf__load(skel) < 0) { + pr_err("Failed to load perf sample-filter BPF skeleton\n"); + ret = -EPERM; + goto err; + } + + if (needs_pid_hash) { + /* The filters map is shared among other processes */ + ret = update_pid_hash(skel, evsel, entry); + if (ret < 0) + goto err; + } else { + i = 0; + fd = bpf_map__fd(skel->maps.filters); + + /* The filters map has only one entry in this case */ + if (bpf_map_update_elem(fd, &i, entry, BPF_ANY) < 0) { + ret = -errno; + pr_err("Failed to update the filter map\n"); + goto err; + } + } + prog = skel->progs.perf_sample_filter; for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) { for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) { diff --git a/tools/perf/util/bpf_skel/sample-filter.h b/tools/perf/util/bpf_skel/sample-filter.h index bb6a1b91f1df..e666bfd5fbdd 100644 --- a/tools/perf/util/bpf_skel/sample-filter.h +++ b/tools/perf/util/bpf_skel/sample-filter.h @@ -2,6 +2,7 @@ #define PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H #define MAX_FILTERS 64 +#define MAX_PIDS (16 * 1024) /* supported filter operations */ enum perf_bpf_filter_op { diff --git a/tools/perf/util/bpf_skel/sample_filter.bpf.c b/tools/perf/util/bpf_skel/sample_filter.bpf.c index 0d56e52b922c..c5273f06fa45 100644 --- a/tools/perf/util/bpf_skel/sample_filter.bpf.c +++ b/tools/perf/util/bpf_skel/sample_filter.bpf.c @@ -15,7 +15,16 @@ struct filters { __uint(max_entries, 1); } filters SEC(".maps"); +/* tgid to filter index */ +struct pid_hash { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, int); + __type(value, int); + __uint(max_entries, 1); +} pid_hash SEC(".maps"); + int dropped; +volatile const int use_pid_hash; void *bpf_cast_to_kern_ctx(void *) __ksym; @@ -184,6 +193,18 @@ int perf_sample_filter(void *ctx) kctx = bpf_cast_to_kern_ctx(ctx); k = 0; + + if (use_pid_hash) { + int tgid = bpf_get_current_pid_tgid() >> 32; + int *idx; + + idx = bpf_map_lookup_elem(&pid_hash, &tgid); + if (idx) + k = *idx; + else + goto drop; + } + entry = bpf_map_lookup_elem(&filters, &k); if (entry == NULL) goto drop; From 0715f65e94377016e8e12dae6bb6a6d24644c54d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 3 Jul 2024 15:30:31 -0700 Subject: [PATCH 062/347] perf bpf-filter: Support pin/unpin BPF object And use the pinned objects for unprivileged users to profile their own tasks. The BPF objects need to be pinned in the BPF-fs by root first and it'll be handled in the later patch. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: KP Singh Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Cc: Stephane Eranian Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20240703223035.2024586-5-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-filter.c | 234 +++++++++++++++++++++++++++++------ tools/perf/util/bpf-filter.h | 13 ++ 2 files changed, 211 insertions(+), 36 deletions(-) diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c index 5ec0e0955ec4..37ed6c48debf 100644 --- a/tools/perf/util/bpf-filter.c +++ b/tools/perf/util/bpf-filter.c @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0 */ #include +#include +#include +#include #include #include @@ -23,6 +26,9 @@ #define __PERF_SAMPLE_TYPE(tt, st, opt) { tt, #st, opt } #define PERF_SAMPLE_TYPE(_st, opt) __PERF_SAMPLE_TYPE(PBF_TERM_##_st, PERF_SAMPLE_##_st, opt) +/* Index in the pinned 'filters' map. Should be released after use. */ +static int pinned_filter_idx = -1; + static const struct perf_sample_info { enum perf_bpf_filter_term type; const char *name; @@ -47,6 +53,8 @@ static const struct perf_sample_info { PERF_SAMPLE_TYPE(DATA_PAGE_SIZE, "--data-page-size"), }; +static int get_pinned_fd(const char *name); + static const struct perf_sample_info *get_sample_info(enum perf_bpf_filter_term type) { size_t i; @@ -167,19 +175,26 @@ static int convert_to_tgid(int tid) return tgid; } -static int update_pid_hash(struct sample_filter_bpf *skel, struct evsel *evsel, - struct perf_bpf_filter_entry *entry) +static int update_pid_hash(struct evsel *evsel, struct perf_bpf_filter_entry *entry) { int filter_idx; - int nr, last; - int fd = bpf_map__fd(skel->maps.filters); + int fd, nr, last; struct perf_thread_map *threads; + fd = get_pinned_fd("filters"); + if (fd < 0) { + pr_debug("cannot get fd for 'filters' map\n"); + return fd; + } + /* Find the first available entry in the filters map */ for (filter_idx = 0; filter_idx < MAX_FILTERS; filter_idx++) { - if (bpf_map_update_elem(fd, &filter_idx, entry, BPF_NOEXIST) == 0) + if (bpf_map_update_elem(fd, &filter_idx, entry, BPF_NOEXIST) == 0) { + pinned_filter_idx = filter_idx; break; + } } + close(fd); if (filter_idx == MAX_FILTERS) { pr_err("Too many users for the filter map\n"); @@ -193,7 +208,9 @@ static int update_pid_hash(struct sample_filter_bpf *skel, struct evsel *evsel, } /* save the index to a hash map */ - fd = bpf_map__fd(skel->maps.pid_hash); + fd = get_pinned_fd("pid_hash"); + if (fd < 0) + return fd; last = -1; nr = perf_thread_map__nr(threads); @@ -214,10 +231,12 @@ static int update_pid_hash(struct sample_filter_bpf *skel, struct evsel *evsel, if (bpf_map_update_elem(fd, &tgid, &filter_idx, BPF_ANY) < 0) { pr_err("Failed to update the pid hash\n"); - return -errno; + close(fd); + return -1; } pr_debug("pid hash: %d -> %d\n", tgid, filter_idx); } + close(fd); return 0; } @@ -240,40 +259,48 @@ int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target) goto err; } - skel = sample_filter_bpf__open(); - if (!skel) { - pr_err("Failed to open perf sample-filter BPF skeleton\n"); - ret = -EPERM; - goto err; - } - - if (needs_pid_hash) { - bpf_map__set_max_entries(skel->maps.filters, MAX_FILTERS); - bpf_map__set_max_entries(skel->maps.pid_hash, MAX_PIDS); - skel->rodata->use_pid_hash = 1; - } - - if (sample_filter_bpf__load(skel) < 0) { - pr_err("Failed to load perf sample-filter BPF skeleton\n"); - ret = -EPERM; - goto err; - } - - if (needs_pid_hash) { - /* The filters map is shared among other processes */ - ret = update_pid_hash(skel, evsel, entry); + if (needs_pid_hash && geteuid() != 0) { + /* The filters map is shared among other processes */ + ret = update_pid_hash(evsel, entry); if (ret < 0) goto err; - } else { - i = 0; - fd = bpf_map__fd(skel->maps.filters); - /* The filters map has only one entry in this case */ - if (bpf_map_update_elem(fd, &i, entry, BPF_ANY) < 0) { - ret = -errno; - pr_err("Failed to update the filter map\n"); + fd = get_pinned_fd("perf_sample_filter"); + if (fd < 0) { + ret = fd; goto err; } + + for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) { + for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) { + ret = ioctl(FD(evsel, x, y), PERF_EVENT_IOC_SET_BPF, fd); + if (ret < 0) { + pr_err("Failed to attach perf sample-filter\n"); + goto err; + } + } + } + + close(fd); + free(entry); + return 0; + } + + skel = sample_filter_bpf__open_and_load(); + if (!skel) { + ret = -errno; + pr_err("Failed to load perf sample-filter BPF skeleton\n"); + goto err; + } + + i = 0; + fd = bpf_map__fd(skel->maps.filters); + + /* The filters map has only one entry in this case */ + if (bpf_map_update_elem(fd, &i, entry, BPF_ANY) < 0) { + ret = -errno; + pr_err("Failed to update the filter map\n"); + goto err; } prog = skel->progs.perf_sample_filter; @@ -306,6 +333,15 @@ int perf_bpf_filter__destroy(struct evsel *evsel) free(expr); } sample_filter_bpf__destroy(evsel->bpf_skel); + + if (pinned_filter_idx >= 0) { + int fd = get_pinned_fd("filters"); + + bpf_map_delete_elem(fd, &pinned_filter_idx); + pinned_filter_idx = -1; + close(fd); + } + return 0; } @@ -349,3 +385,129 @@ int perf_bpf_filter__parse(struct list_head *expr_head, const char *str) return ret; } + +int perf_bpf_filter__pin(void) +{ + struct sample_filter_bpf *skel; + char *path = NULL; + int dir_fd, ret = -1; + + skel = sample_filter_bpf__open(); + if (!skel) { + ret = -errno; + pr_err("Failed to open perf sample-filter BPF skeleton\n"); + goto err; + } + + /* pinned program will use pid-hash */ + bpf_map__set_max_entries(skel->maps.filters, MAX_FILTERS); + bpf_map__set_max_entries(skel->maps.pid_hash, MAX_PIDS); + skel->rodata->use_pid_hash = 1; + + if (sample_filter_bpf__load(skel) < 0) { + ret = -errno; + pr_err("Failed to load perf sample-filter BPF skeleton\n"); + goto err; + } + + if (asprintf(&path, "%s/fs/bpf/%s", sysfs__mountpoint(), + PERF_BPF_FILTER_PIN_PATH) < 0) { + ret = -errno; + pr_err("Failed to allocate pathname in the BPF-fs\n"); + goto err; + } + + ret = bpf_object__pin(skel->obj, path); + if (ret < 0) { + pr_err("Failed to pin BPF filter objects\n"); + goto err; + } + + /* setup access permissions for the pinned objects */ + dir_fd = open(path, O_PATH); + if (dir_fd < 0) { + bpf_object__unpin(skel->obj, path); + ret = dir_fd; + goto err; + } + + /* BPF-fs root has the sticky bit */ + if (fchmodat(dir_fd, "..", 01755, 0) < 0) { + pr_debug("chmod for BPF-fs failed\n"); + ret = -errno; + goto err_close; + } + + /* perf_filter directory */ + if (fchmodat(dir_fd, ".", 0755, 0) < 0) { + pr_debug("chmod for perf_filter directory failed?\n"); + ret = -errno; + goto err_close; + } + + /* programs need write permission for some reason */ + if (fchmodat(dir_fd, "perf_sample_filter", 0777, 0) < 0) { + pr_debug("chmod for perf_sample_filter failed\n"); + ret = -errno; + } + /* maps */ + if (fchmodat(dir_fd, "filters", 0666, 0) < 0) { + pr_debug("chmod for filters failed\n"); + ret = -errno; + } + if (fchmodat(dir_fd, "pid_hash", 0666, 0) < 0) { + pr_debug("chmod for pid_hash failed\n"); + ret = -errno; + } + +err_close: + close(dir_fd); + +err: + free(path); + sample_filter_bpf__destroy(skel); + return ret; +} + +int perf_bpf_filter__unpin(void) +{ + struct sample_filter_bpf *skel; + char *path = NULL; + int ret = -1; + + skel = sample_filter_bpf__open_and_load(); + if (!skel) { + ret = -errno; + pr_err("Failed to open perf sample-filter BPF skeleton\n"); + goto err; + } + + if (asprintf(&path, "%s/fs/bpf/%s", sysfs__mountpoint(), + PERF_BPF_FILTER_PIN_PATH) < 0) { + ret = -errno; + pr_err("Failed to allocate pathname in the BPF-fs\n"); + goto err; + } + + ret = bpf_object__unpin(skel->obj, path); + +err: + free(path); + sample_filter_bpf__destroy(skel); + return ret; +} + +static int get_pinned_fd(const char *name) +{ + char *path = NULL; + int fd; + + if (asprintf(&path, "%s/fs/bpf/%s/%s", sysfs__mountpoint(), + PERF_BPF_FILTER_PIN_PATH, name) < 0) + return -1; + + fd = bpf_obj_get(path); + + free(path); + return fd; +} diff --git a/tools/perf/util/bpf-filter.h b/tools/perf/util/bpf-filter.h index 605a3d0226e0..916ed7770b73 100644 --- a/tools/perf/util/bpf-filter.h +++ b/tools/perf/util/bpf-filter.h @@ -18,6 +18,9 @@ struct perf_bpf_filter_expr { struct evsel; struct target; +/* path in BPF-fs for the pinned program and maps */ +#define PERF_BPF_FILTER_PIN_PATH "perf_filter" + #ifdef HAVE_BPF_SKEL struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(enum perf_bpf_filter_term term, int part, @@ -27,6 +30,8 @@ int perf_bpf_filter__parse(struct list_head *expr_head, const char *str); int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target); int perf_bpf_filter__destroy(struct evsel *evsel); u64 perf_bpf_filter__lost_count(struct evsel *evsel); +int perf_bpf_filter__pin(void); +int perf_bpf_filter__unpin(void); #else /* !HAVE_BPF_SKEL */ @@ -48,5 +53,13 @@ static inline u64 perf_bpf_filter__lost_count(struct evsel *evsel __maybe_unused { return 0; } +static inline int perf_bpf_filter__pin(void) +{ + return -EOPNOTSUPP; +} +static inline int perf_bpf_filter__unpin(void) +{ + return -EOPNOTSUPP; +} #endif /* HAVE_BPF_SKEL*/ #endif /* PERF_UTIL_BPF_FILTER_H */ From 1ec6fd34e0572588b9628e397ff134fd3cf79b5d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 3 Jul 2024 15:30:32 -0700 Subject: [PATCH 063/347] perf bpf-filter: Support separate lost counts for each filter As the BPF filter is shared between other processes, it should have its own counter for each invocation. Add a new array map (lost_count) to save the count using the same index as the filter. It should clear the count before running the filter. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: KP Singh Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Cc: Stephane Eranian Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20240703223035.2024586-6-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-filter.c | 37 ++++++++++++++++++-- tools/perf/util/bpf_skel/sample_filter.bpf.c | 15 ++++++-- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c index 37ed6c48debf..c5eb0b7eec19 100644 --- a/tools/perf/util/bpf-filter.c +++ b/tools/perf/util/bpf-filter.c @@ -260,11 +260,23 @@ int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target) } if (needs_pid_hash && geteuid() != 0) { + int zero = 0; + /* The filters map is shared among other processes */ ret = update_pid_hash(evsel, entry); if (ret < 0) goto err; + fd = get_pinned_fd("dropped"); + if (fd < 0) { + ret = fd; + goto err; + } + + /* Reset the lost count */ + bpf_map_update_elem(fd, &pinned_filter_idx, &zero, BPF_ANY); + close(fd); + fd = get_pinned_fd("perf_sample_filter"); if (fd < 0) { ret = fd; @@ -347,9 +359,25 @@ int perf_bpf_filter__destroy(struct evsel *evsel) u64 perf_bpf_filter__lost_count(struct evsel *evsel) { - struct sample_filter_bpf *skel = evsel->bpf_skel; + int count = 0; - return skel ? skel->bss->dropped : 0; + if (list_empty(&evsel->bpf_filters)) + return 0; + + if (pinned_filter_idx >= 0) { + int fd = get_pinned_fd("dropped"); + + bpf_map_lookup_elem(fd, &pinned_filter_idx, &count); + close(fd); + } else if (evsel->bpf_skel) { + struct sample_filter_bpf *skel = evsel->bpf_skel; + int fd = bpf_map__fd(skel->maps.dropped); + int idx = 0; + + bpf_map_lookup_elem(fd, &idx, &count); + } + + return count; } struct perf_bpf_filter_expr *perf_bpf_filter_expr__new(enum perf_bpf_filter_term term, @@ -402,6 +430,7 @@ int perf_bpf_filter__pin(void) /* pinned program will use pid-hash */ bpf_map__set_max_entries(skel->maps.filters, MAX_FILTERS); bpf_map__set_max_entries(skel->maps.pid_hash, MAX_PIDS); + bpf_map__set_max_entries(skel->maps.dropped, MAX_FILTERS); skel->rodata->use_pid_hash = 1; if (sample_filter_bpf__load(skel) < 0) { @@ -459,6 +488,10 @@ int perf_bpf_filter__pin(void) pr_debug("chmod for pid_hash failed\n"); ret = -errno; } + if (fchmodat(dir_fd, "dropped", 0666, 0) < 0) { + pr_debug("chmod for dropped failed\n"); + ret = -errno; + } err_close: close(dir_fd); diff --git a/tools/perf/util/bpf_skel/sample_filter.bpf.c b/tools/perf/util/bpf_skel/sample_filter.bpf.c index c5273f06fa45..4c75354b84fd 100644 --- a/tools/perf/util/bpf_skel/sample_filter.bpf.c +++ b/tools/perf/util/bpf_skel/sample_filter.bpf.c @@ -23,7 +23,14 @@ struct pid_hash { __uint(max_entries, 1); } pid_hash SEC(".maps"); -int dropped; +/* tgid to filter index */ +struct lost_count { + __uint(type, BPF_MAP_TYPE_ARRAY); + __type(key, int); + __type(value, int); + __uint(max_entries, 1); +} dropped SEC(".maps"); + volatile const int use_pid_hash; void *bpf_cast_to_kern_ctx(void *) __ksym; @@ -189,6 +196,7 @@ int perf_sample_filter(void *ctx) int in_group = 0; int group_result = 0; int i, k; + int *losts; kctx = bpf_cast_to_kern_ctx(ctx); @@ -252,7 +260,10 @@ int perf_sample_filter(void *ctx) return 1; drop: - __sync_fetch_and_add(&dropped, 1); + losts = bpf_map_lookup_elem(&dropped, &k); + if (losts != NULL) + __sync_fetch_and_add(losts, 1); + return 0; } From 73bf63a4750ea18d7fbb8f80695dcfd0656d13f4 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 3 Jul 2024 15:30:33 -0700 Subject: [PATCH 064/347] perf record: Fix a potential error handling issue The evlist is allocated at the beginning of cmd_record(). Also free-ing thread masks should be paired with record__init_thread_masks() which is called right before __cmd_record(). Let's change the order of these functions to release the resources correctly in case of errors. This is maybe fine as the process exits, but it might be a problem if it manages some system-wide resources that live longer than the process. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: KP Singh Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Cc: Stephane Eranian Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20240703223035.2024586-7-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index c599e620ee89..01fb95487a63 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -4242,13 +4242,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; } From 3dee4b83a6b4c167db0827c2b1e288460af72ac6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 3 Jul 2024 15:30:34 -0700 Subject: [PATCH 065/347] perf record: Add --setup-filter option To allow BPF filters for unprivileged users it needs to pin the BPF objects to BPF-fs first. Let's add a new option to pin and unpin the objects easily. I'm not sure 'perf record' is a right place to do this but I don't have a better idea right now. $ sudo perf record --setup-filter pin The above command would pin BPF program and maps for the filter when the system has BPF-fs (usually at /sys/fs/bpf/). To unpin the objects, users can run the following command (as root). $ sudo perf record --setup-filter unpin Committer testing: root@number:~# perf record --setup-filter pin root@number:~# ls -la /sys/fs/bpf/perf_filter/ total 0 drwxr-xr-x. 2 root root 0 Jul 31 10:43 . drwxr-xr-t. 3 root root 0 Jul 31 10:43 .. -rw-rw-rw-. 1 root root 0 Jul 31 10:43 dropped -rw-rw-rw-. 1 root root 0 Jul 31 10:43 filters -rwxrwxrwx. 1 root root 0 Jul 31 10:43 perf_sample_filter -rw-rw-rw-. 1 root root 0 Jul 31 10:43 pid_hash -rw-------. 1 root root 0 Jul 31 10:43 sample_f_rodata root@number:~# ls -la /sys/fs/bpf/perf_filter/perf_sample_filter -rwxrwxrwx. 1 root root 0 Jul 31 10:43 /sys/fs/bpf/perf_filter/perf_sample_filter root@number:~# Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: KP Singh Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Cc: Stephane Eranian Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20240703223035.2024586-8-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-record.txt | 5 +++++ tools/perf/builtin-record.c | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index d6532ed97c02..41e36b4dc765 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -828,6 +828,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=:: + 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 diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 01fb95487a63..72345d1e54b0 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -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 */ @@ -3557,6 +3558,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 +4089,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(). From 9cb3549b73c1cd1f8ed0ff32bafab7649e5bf4ea Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 3 Jul 2024 15:30:35 -0700 Subject: [PATCH 066/347] perf test: Update sample filtering test Now it can run the BPF filtering test with normal user if the BPF objects are pinned by 'sudo perf record --setup-filter pin'. Let's update the test case to verify the behavior. It'll skip the test if the filter check is failed from a normal user, but it shows a message how to set up the filters. First, run the test as a normal user and it fails. $ perf test -vv filtering 95: perf record sample filtering (by BPF) tests: --- start --- test child forked, pid 425677 Checking BPF-filter privilege try 'sudo perf record --setup-filter pin' first. <<<--- here bpf-filter test [Skipped permission] ---- end(-2) ---- 95: perf record sample filtering (by BPF) tests : Skip According to the message, run the perf record command to pin the BPF objects. $ sudo perf record --setup-filter pin And re-run the test as a normal user. $ perf test -vv filtering 95: perf record sample filtering (by BPF) tests: --- start --- test child forked, pid 424486 Checking BPF-filter privilege Basic bpf-filter test Basic bpf-filter test [Success] Failing bpf-filter test Error: task-clock event does not have PERF_SAMPLE_CPU Failing bpf-filter test [Success] Group bpf-filter test Error: task-clock event does not have PERF_SAMPLE_CPU Error: task-clock event does not have PERF_SAMPLE_CODE_PAGE_SIZE Group bpf-filter test [Success] ---- end(0) ---- 95: perf record sample filtering (by BPF) tests : Ok Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: KP Singh Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Cc: Stephane Eranian Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20240703223035.2024586-9-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/record_bpf_filter.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/perf/tests/shell/record_bpf_filter.sh b/tools/perf/tests/shell/record_bpf_filter.sh index 31c593966e8c..c5882d620db7 100755 --- a/tools/perf/tests/shell/record_bpf_filter.sh +++ b/tools/perf/tests/shell/record_bpf_filter.sh @@ -22,15 +22,16 @@ trap trap_cleanup EXIT TERM INT test_bpf_filter_priv() { echo "Checking BPF-filter privilege" - if [ "$(id -u)" != 0 ] - then - echo "bpf-filter test [Skipped permission]" - err=2 - return - fi if ! perf record -e task-clock --filter 'period > 1' \ -o /dev/null --quiet true 2>&1 then + if [ "$(id -u)" != 0 ] + then + echo "try 'sudo perf record --setup-filter pin' first." + echo "bpf-filter test [Skipped permission]" + err=2 + return + fi echo "bpf-filter test [Skipped missing BPF support]" err=2 return From ea59b70a8418a313d6f2ab48a957de015fc33018 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 31 Jul 2024 11:58:56 -0300 Subject: [PATCH 067/347] perf bpf: Move BPF disassembly routines to separate file to avoid clash with capstone bpf headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a clash of the libbpf and capstone libraries, that ends up with: In file included from /usr/include/capstone/capstone.h:325, from util/disasm.c:1513: /usr/include/capstone/bpf.h:94:14: error: ‘bpf_insn’ defined as wrong kind of tag 94 | typedef enum bpf_insn { So far we're just trying to avoid this by not having both headers included in the same .c or .h file, do it one more time by moving the BPF diassembly routines from util/disasm.c to util/disasm_bpf.c. This is only being hit when building with BUILD_NONDISTRO=1, i.e. building with binutils-devel, that isn't the in the default build due to a licencing clash. We need to reimplement what is now isolated in util/disasm_bpf.c using some other library to have BPF annotation feature that now only is available with BUILD_NONDISTRO=1. Fixes: 6d17edc113de1e21 ("perf annotate: Use libcapstone to disassemble") Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/ZqpUSKPxMwaQKORr@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/Build | 1 + tools/perf/util/disasm.c | 187 +-------------------------------- tools/perf/util/disasm_bpf.c | 195 +++++++++++++++++++++++++++++++++++ tools/perf/util/disasm_bpf.h | 12 +++ 4 files changed, 209 insertions(+), 186 deletions(-) create mode 100644 tools/perf/util/disasm_bpf.c create mode 100644 tools/perf/util/disasm_bpf.h diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 0f18fe81ef0b..b24360c04aae 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -13,6 +13,7 @@ perf-util-y += copyfile.o perf-util-y += ctype.o perf-util-y += db-export.o perf-util-y += disasm.o +perf-util-y += disasm_bpf.o perf-util-y += env.o perf-util-y += event.o perf-util-y += evlist.o diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 410e52cd9cfd..85fb0cfedf94 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -16,6 +16,7 @@ #include "build-id.h" #include "debug.h" #include "disasm.h" +#include "disasm_bpf.h" #include "dso.h" #include "env.h" #include "evsel.h" @@ -1323,192 +1324,6 @@ fallback: return 0; } -#if defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) -#define PACKAGE "perf" -#include -#include -#include -#include -#include -#include -#include - -#include "bpf-event.h" -#include "bpf-utils.h" - -static int symbol__disassemble_bpf(struct symbol *sym, - struct annotate_args *args) -{ - struct annotation *notes = symbol__annotation(sym); - struct bpf_prog_linfo *prog_linfo = NULL; - struct bpf_prog_info_node *info_node; - int len = sym->end - sym->start; - disassembler_ftype disassemble; - struct map *map = args->ms.map; - struct perf_bpil *info_linear; - struct disassemble_info info; - struct dso *dso = map__dso(map); - int pc = 0, count, sub_id; - struct btf *btf = NULL; - char tpath[PATH_MAX]; - size_t buf_size; - int nr_skip = 0; - char *buf; - bfd *bfdf; - int ret; - FILE *s; - - if (dso__binary_type(dso) != DSO_BINARY_TYPE__BPF_PROG_INFO) - return SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE; - - pr_debug("%s: handling sym %s addr %" PRIx64 " len %" PRIx64 "\n", __func__, - sym->name, sym->start, sym->end - sym->start); - - memset(tpath, 0, sizeof(tpath)); - perf_exe(tpath, sizeof(tpath)); - - bfdf = bfd_openr(tpath, NULL); - if (bfdf == NULL) - abort(); - - if (!bfd_check_format(bfdf, bfd_object)) - abort(); - - s = open_memstream(&buf, &buf_size); - if (!s) { - ret = errno; - goto out; - } - init_disassemble_info_compat(&info, s, - (fprintf_ftype) fprintf, - fprintf_styled); - info.arch = bfd_get_arch(bfdf); - info.mach = bfd_get_mach(bfdf); - - info_node = perf_env__find_bpf_prog_info(dso__bpf_prog(dso)->env, - dso__bpf_prog(dso)->id); - if (!info_node) { - ret = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF; - goto out; - } - info_linear = info_node->info_linear; - sub_id = dso__bpf_prog(dso)->sub_id; - - info.buffer = (void *)(uintptr_t)(info_linear->info.jited_prog_insns); - info.buffer_length = info_linear->info.jited_prog_len; - - if (info_linear->info.nr_line_info) - prog_linfo = bpf_prog_linfo__new(&info_linear->info); - - if (info_linear->info.btf_id) { - struct btf_node *node; - - node = perf_env__find_btf(dso__bpf_prog(dso)->env, - info_linear->info.btf_id); - if (node) - btf = btf__new((__u8 *)(node->data), - node->data_size); - } - - disassemble_init_for_target(&info); - -#ifdef DISASM_FOUR_ARGS_SIGNATURE - disassemble = disassembler(info.arch, - bfd_big_endian(bfdf), - info.mach, - bfdf); -#else - disassemble = disassembler(bfdf); -#endif - if (disassemble == NULL) - abort(); - - fflush(s); - do { - const struct bpf_line_info *linfo = NULL; - struct disasm_line *dl; - size_t prev_buf_size; - const char *srcline; - u64 addr; - - addr = pc + ((u64 *)(uintptr_t)(info_linear->info.jited_ksyms))[sub_id]; - count = disassemble(pc, &info); - - if (prog_linfo) - linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo, - addr, sub_id, - nr_skip); - - if (linfo && btf) { - srcline = btf__name_by_offset(btf, linfo->line_off); - nr_skip++; - } else - srcline = NULL; - - fprintf(s, "\n"); - prev_buf_size = buf_size; - fflush(s); - - if (!annotate_opts.hide_src_code && srcline) { - args->offset = -1; - args->line = strdup(srcline); - args->line_nr = 0; - args->fileloc = NULL; - args->ms.sym = sym; - dl = disasm_line__new(args); - if (dl) { - annotation_line__add(&dl->al, - ¬es->src->source); - } - } - - args->offset = pc; - args->line = buf + prev_buf_size; - args->line_nr = 0; - args->fileloc = NULL; - args->ms.sym = sym; - dl = disasm_line__new(args); - if (dl) - annotation_line__add(&dl->al, ¬es->src->source); - - pc += count; - } while (count > 0 && pc < len); - - ret = 0; -out: - free(prog_linfo); - btf__free(btf); - fclose(s); - bfd_close(bfdf); - return ret; -} -#else // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) -static int symbol__disassemble_bpf(struct symbol *sym __maybe_unused, - struct annotate_args *args __maybe_unused) -{ - return SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF; -} -#endif // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) - -static int -symbol__disassemble_bpf_image(struct symbol *sym, - struct annotate_args *args) -{ - struct annotation *notes = symbol__annotation(sym); - struct disasm_line *dl; - - args->offset = -1; - args->line = strdup("to be implemented"); - args->line_nr = 0; - args->fileloc = NULL; - dl = disasm_line__new(args); - if (dl) - annotation_line__add(&dl->al, ¬es->src->source); - - zfree(&args->line); - return 0; -} - #ifdef HAVE_LIBCAPSTONE_SUPPORT #include diff --git a/tools/perf/util/disasm_bpf.c b/tools/perf/util/disasm_bpf.c new file mode 100644 index 000000000000..1fee71c79b62 --- /dev/null +++ b/tools/perf/util/disasm_bpf.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include "util/annotate.h" +#include "util/disasm_bpf.h" +#include "util/symbol.h" +#include +#include + +#if defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) +#define PACKAGE "perf" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/bpf-event.h" +#include "util/bpf-utils.h" +#include "util/debug.h" +#include "util/dso.h" +#include "util/map.h" +#include "util/env.h" +#include "util/util.h" + +int symbol__disassemble_bpf(struct symbol *sym, struct annotate_args *args) +{ + struct annotation *notes = symbol__annotation(sym); + struct bpf_prog_linfo *prog_linfo = NULL; + struct bpf_prog_info_node *info_node; + int len = sym->end - sym->start; + disassembler_ftype disassemble; + struct map *map = args->ms.map; + struct perf_bpil *info_linear; + struct disassemble_info info; + struct dso *dso = map__dso(map); + int pc = 0, count, sub_id; + struct btf *btf = NULL; + char tpath[PATH_MAX]; + size_t buf_size; + int nr_skip = 0; + char *buf; + bfd *bfdf; + int ret; + FILE *s; + + if (dso__binary_type(dso) != DSO_BINARY_TYPE__BPF_PROG_INFO) + return SYMBOL_ANNOTATE_ERRNO__BPF_INVALID_FILE; + + pr_debug("%s: handling sym %s addr %" PRIx64 " len %" PRIx64 "\n", __func__, + sym->name, sym->start, sym->end - sym->start); + + memset(tpath, 0, sizeof(tpath)); + perf_exe(tpath, sizeof(tpath)); + + bfdf = bfd_openr(tpath, NULL); + if (bfdf == NULL) + abort(); + + if (!bfd_check_format(bfdf, bfd_object)) + abort(); + + s = open_memstream(&buf, &buf_size); + if (!s) { + ret = errno; + goto out; + } + init_disassemble_info_compat(&info, s, + (fprintf_ftype) fprintf, + fprintf_styled); + info.arch = bfd_get_arch(bfdf); + info.mach = bfd_get_mach(bfdf); + + info_node = perf_env__find_bpf_prog_info(dso__bpf_prog(dso)->env, + dso__bpf_prog(dso)->id); + if (!info_node) { + ret = SYMBOL_ANNOTATE_ERRNO__BPF_MISSING_BTF; + goto out; + } + info_linear = info_node->info_linear; + sub_id = dso__bpf_prog(dso)->sub_id; + + info.buffer = (void *)(uintptr_t)(info_linear->info.jited_prog_insns); + info.buffer_length = info_linear->info.jited_prog_len; + + if (info_linear->info.nr_line_info) + prog_linfo = bpf_prog_linfo__new(&info_linear->info); + + if (info_linear->info.btf_id) { + struct btf_node *node; + + node = perf_env__find_btf(dso__bpf_prog(dso)->env, + info_linear->info.btf_id); + if (node) + btf = btf__new((__u8 *)(node->data), + node->data_size); + } + + disassemble_init_for_target(&info); + +#ifdef DISASM_FOUR_ARGS_SIGNATURE + disassemble = disassembler(info.arch, + bfd_big_endian(bfdf), + info.mach, + bfdf); +#else + disassemble = disassembler(bfdf); +#endif + if (disassemble == NULL) + abort(); + + fflush(s); + do { + const struct bpf_line_info *linfo = NULL; + struct disasm_line *dl; + size_t prev_buf_size; + const char *srcline; + u64 addr; + + addr = pc + ((u64 *)(uintptr_t)(info_linear->info.jited_ksyms))[sub_id]; + count = disassemble(pc, &info); + + if (prog_linfo) + linfo = bpf_prog_linfo__lfind_addr_func(prog_linfo, + addr, sub_id, + nr_skip); + + if (linfo && btf) { + srcline = btf__name_by_offset(btf, linfo->line_off); + nr_skip++; + } else + srcline = NULL; + + fprintf(s, "\n"); + prev_buf_size = buf_size; + fflush(s); + + if (!annotate_opts.hide_src_code && srcline) { + args->offset = -1; + args->line = strdup(srcline); + args->line_nr = 0; + args->fileloc = NULL; + args->ms.sym = sym; + dl = disasm_line__new(args); + if (dl) { + annotation_line__add(&dl->al, + ¬es->src->source); + } + } + + args->offset = pc; + args->line = buf + prev_buf_size; + args->line_nr = 0; + args->fileloc = NULL; + args->ms.sym = sym; + dl = disasm_line__new(args); + if (dl) + annotation_line__add(&dl->al, ¬es->src->source); + + pc += count; + } while (count > 0 && pc < len); + + ret = 0; +out: + free(prog_linfo); + btf__free(btf); + fclose(s); + bfd_close(bfdf); + return ret; +} +#else // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) +int symbol__disassemble_bpf(struct symbol *sym __maybe_unused, struct annotate_args *args __maybe_unused) +{ + return SYMBOL_ANNOTATE_ERRNO__NO_LIBOPCODES_FOR_BPF; +} +#endif // defined(HAVE_LIBBFD_SUPPORT) && defined(HAVE_LIBBPF_SUPPORT) + +int symbol__disassemble_bpf_image(struct symbol *sym, struct annotate_args *args) +{ + struct annotation *notes = symbol__annotation(sym); + struct disasm_line *dl; + + args->offset = -1; + args->line = strdup("to be implemented"); + args->line_nr = 0; + args->fileloc = NULL; + dl = disasm_line__new(args); + if (dl) + annotation_line__add(&dl->al, ¬es->src->source); + + zfree(&args->line); + return 0; +} diff --git a/tools/perf/util/disasm_bpf.h b/tools/perf/util/disasm_bpf.h new file mode 100644 index 000000000000..2ecb19545388 --- /dev/null +++ b/tools/perf/util/disasm_bpf.h @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#ifndef __PERF_DISASM_BPF_H +#define __PERF_DISASM_BPF_H + +struct symbol; +struct annotate_args; + +int symbol__disassemble_bpf(struct symbol *sym, struct annotate_args *args); +int symbol__disassemble_bpf_image(struct symbol *sym, struct annotate_args *args); + +#endif /* __PERF_DISASM_BPF_H */ From 0fe881f10ceb7ae2be455d8bdf70fab18c0a5c5f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 30 Jul 2024 12:17:44 -0700 Subject: [PATCH 068/347] perf jevents: Autogenerate empty-pmu-events.c empty-pmu-events.c exists so that builds may occur without python being installed on a system. Manually updating empty-pmu-events.c to be in sync with jevents.py is a pain, let's use jevents.py to generate empty-pmu-events.c. 1) change jevents.py so that an arch and model of none cause generation of a pmu-events.c without any json. Add a SPDX and autogenerated warning to the start of the file. 2) change Build so that if a generated pmu-events.c for arch none and model none doesn't match empty-pmu-events.c the build fails with a cat of the differences. Update Makefile.perf to clean up the files used for this. 3) update empty-pmu-events.c to match the output of jevents.py with arch and mode of none. Committer notes: The firtst paragraph is confusing, so I asked and Ian further clarified: --- The requirement for python hasn't changed. Case 1: no python or NO_JEVENTS=1 Build happens using empty-pmu-events.c that is checked in, no python is required. Case 2: python pmu-events.c is created by jevents.py (requiring python) and then built. This change adds a step where the empty-pmu-events.c is created using jevents.py and that file is diffed against the checked in version. This stops the checked in empty-pmu-events.c diverging if changes are made to jevents.py. If the diff causes the build to fail then you just copy the diff empty-pmu-events.c over the checked in one. --- Reviewed-by: John Garry Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jing Zhang Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Oliver Sang Cc: Peter Zijlstra Cc: Philip Li Cc: Sandipan Das Cc: Weilin Wang Cc: Xu Yang Link: https://lore.kernel.org/r/20240730191744.3097329-3-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 2 + tools/perf/pmu-events/Build | 12 +- tools/perf/pmu-events/empty-pmu-events.c | 886 ++++++++++++++--------- tools/perf/pmu-events/jevents.py | 6 +- 4 files changed, 558 insertions(+), 348 deletions(-) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index f8148db5fc38..72534bb72d43 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -1277,6 +1277,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) \ diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build index 1d18bb89402e..c3fa43c49706 100644 --- a/tools/perf/pmu-events/Build +++ b/tools/perf/pmu-events/Build @@ -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 diff --git a/tools/perf/pmu-events/empty-pmu-events.c b/tools/perf/pmu-events/empty-pmu-events.c index 13727421d424..c592079982fb 100644 --- a/tools/perf/pmu-events/empty-pmu-events.c +++ b/tools/perf/pmu-events/empty-pmu-events.c @@ -1,196 +1,193 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * An empty pmu-events.c file used when there is no architecture json files in - * arch or when the jevents.py script cannot be run. - * - * The test cpu/soc is provided for testing. - */ -#include "pmu-events/pmu-events.h" + +/* SPDX-License-Identifier: GPL-2.0 */ +/* THIS FILE WAS AUTOGENERATED BY jevents.py arch=none model=none ! */ + +#include #include "util/header.h" #include "util/pmu.h" #include #include -static const struct pmu_event pmu_events__test_soc_cpu[] = { - { - .name = "l3_cache_rd", - .event = "event=0x40", - .desc = "L3 cache access, read", - .topic = "cache", - .long_desc = "Attributable Level 3 cache access, read", - }, - { - .name = "segment_reg_loads.any", - .event = "event=0x6,period=200000,umask=0x80", - .desc = "Number of segment register loads", - .topic = "other", - }, - { - .name = "dispatch_blocked.any", - .event = "event=0x9,period=200000,umask=0x20", - .desc = "Memory cluster signals to block micro-op dispatch for any reason", - .topic = "other", - }, - { - .name = "eist_trans", - .event = "event=0x3a,period=200000,umask=0x0", - .desc = "Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions", - .topic = "other", - }, - { - .name = "uncore_hisi_ddrc.flux_wcmd", - .event = "event=0x2", - .desc = "DDRC write commands. Unit: hisi_sccl,ddrc ", - .topic = "uncore", - .long_desc = "DDRC write commands", - .pmu = "hisi_sccl,ddrc", - }, - { - .name = "unc_cbo_xsnp_response.miss_eviction", - .event = "event=0x22,umask=0x81", - .desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core. Unit: uncore_cbox ", - .topic = "uncore", - .long_desc = "A cross-core snoop resulted from L3 Eviction which misses in some processor core", - .pmu = "uncore_cbox", - }, - { - .name = "event-hyphen", - .event = "event=0xe0,umask=0x00", - .desc = "UNC_CBO_HYPHEN. Unit: uncore_cbox ", - .topic = "uncore", - .long_desc = "UNC_CBO_HYPHEN", - .pmu = "uncore_cbox", - }, - { - .name = "event-two-hyph", - .event = "event=0xc0,umask=0x00", - .desc = "UNC_CBO_TWO_HYPH. Unit: uncore_cbox ", - .topic = "uncore", - .long_desc = "UNC_CBO_TWO_HYPH", - .pmu = "uncore_cbox", - }, - { - .name = "uncore_hisi_l3c.rd_hit_cpipe", - .event = "event=0x7", - .desc = "Total read hits. Unit: hisi_sccl,l3c ", - .topic = "uncore", - .long_desc = "Total read hits", - .pmu = "hisi_sccl,l3c", - }, - { - .name = "uncore_imc_free_running.cache_miss", - .event = "event=0x12", - .desc = "Total cache misses. Unit: uncore_imc_free_running ", - .topic = "uncore", - .long_desc = "Total cache misses", - .pmu = "uncore_imc_free_running", - }, - { - .name = "uncore_imc.cache_hits", - .event = "event=0x34", - .desc = "Total cache hits. Unit: uncore_imc ", - .topic = "uncore", - .long_desc = "Total cache hits", - .pmu = "uncore_imc", - }, - { - .name = "bp_l1_btb_correct", - .event = "event=0x8a", - .desc = "L1 BTB Correction", - .topic = "branch", - }, - { - .name = "bp_l2_btb_correct", - .event = "event=0x8b", - .desc = "L2 BTB Correction", - .topic = "branch", - }, - { - .name = 0, - .event = 0, - .desc = 0, - }, +struct compact_pmu_event { + int offset; }; -static const struct pmu_metric pmu_metrics__test_soc_cpu[] = { - { - .metric_expr = "1 / IPC", - .metric_name = "CPI", - }, - { - .metric_expr = "inst_retired.any / cpu_clk_unhalted.thread", - .metric_name = "IPC", - .metric_group = "group1", - }, - { - .metric_expr = "idq_uops_not_delivered.core / (4 * (( ( cpu_clk_unhalted.thread / 2 ) * " - "( 1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk ) )))", - .metric_name = "Frontend_Bound_SMT", - }, - { - .metric_expr = "l1d\\-loads\\-misses / inst_retired.any", - .metric_name = "dcache_miss_cpi", - }, - { - .metric_expr = "l1i\\-loads\\-misses / inst_retired.any", - .metric_name = "icache_miss_cycles", - }, - { - .metric_expr = "(dcache_miss_cpi + icache_miss_cycles)", - .metric_name = "cache_miss_cycles", - .metric_group = "group1", - }, - { - .metric_expr = "l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit", - .metric_name = "DCache_L2_All_Hits", - }, - { - .metric_expr = "max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + " - "l2_rqsts.pf_miss + l2_rqsts.rfo_miss", - .metric_name = "DCache_L2_All_Miss", - }, - { - .metric_expr = "DCache_L2_All_Hits + DCache_L2_All_Miss", - .metric_name = "DCache_L2_All", - }, - { - .metric_expr = "d_ratio(DCache_L2_All_Hits, DCache_L2_All)", - .metric_name = "DCache_L2_Hits", - }, - { - .metric_expr = "d_ratio(DCache_L2_All_Miss, DCache_L2_All)", - .metric_name = "DCache_L2_Misses", - }, - { - .metric_expr = "ipc + M2", - .metric_name = "M1", - }, - { - .metric_expr = "ipc + M1", - .metric_name = "M2", - }, - { - .metric_expr = "1/M3", - .metric_name = "M3", - }, - { - .metric_expr = "64 * l1d.replacement / 1000000000 / duration_time", - .metric_name = "L1D_Cache_Fill_BW", - }, - { - .metric_expr = 0, - .metric_name = 0, - }, +struct pmu_table_entry { + const struct compact_pmu_event *entries; + uint32_t num_entries; + struct compact_pmu_event pmu_name; }; +static const char *const big_c_string = +/* offset=0 */ "default_core\000" +/* offset=13 */ "bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000" +/* offset=72 */ "bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000" +/* offset=131 */ "l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000Attributable Level 3 cache access, read\000" +/* offset=226 */ "segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000" +/* offset=325 */ "dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000" +/* offset=455 */ "eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000" +/* offset=570 */ "hisi_sccl,ddrc\000" +/* offset=585 */ "uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000DDRC write commands\000" +/* offset=671 */ "uncore_cbox\000" +/* offset=683 */ "unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000" +/* offset=914 */ "event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000UNC_CBO_HYPHEN\000" +/* offset=979 */ "event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000UNC_CBO_TWO_HYPH\000" +/* offset=1050 */ "hisi_sccl,l3c\000" +/* offset=1064 */ "uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000Total read hits\000" +/* offset=1144 */ "uncore_imc_free_running\000" +/* offset=1168 */ "uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000Total cache misses\000" +/* offset=1263 */ "uncore_imc\000" +/* offset=1274 */ "uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000Total cache hits\000" +/* offset=1352 */ "uncore_sys_ddr_pmu\000" +/* offset=1371 */ "sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000" +/* offset=1444 */ "uncore_sys_ccn_pmu\000" +/* offset=1463 */ "sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000" +/* offset=1537 */ "uncore_sys_cmn_pmu\000" +/* offset=1556 */ "sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000" +/* offset=1696 */ "CPI\000\0001 / IPC\000\000\000\000\000\000\000\00000" +/* offset=1718 */ "IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\00000" +/* offset=1781 */ "Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\00000" +/* offset=1947 */ "dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000" +/* offset=2011 */ "icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000" +/* offset=2078 */ "cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\00000" +/* offset=2149 */ "DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\00000" +/* offset=2243 */ "DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\00000" +/* offset=2377 */ "DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\00000" +/* offset=2441 */ "DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\00000" +/* offset=2509 */ "DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\00000" +/* offset=2579 */ "M1\000\000ipc + M2\000\000\000\000\000\000\000\00000" +/* offset=2601 */ "M2\000\000ipc + M1\000\000\000\000\000\000\000\00000" +/* offset=2623 */ "M3\000\0001 / M3\000\000\000\000\000\000\000\00000" +/* offset=2643 */ "L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\00000" +; + +static const struct compact_pmu_event pmu_events__test_soc_cpu_default_core[] = { +{ 13 }, /* bp_l1_btb_correct\000branch\000L1 BTB Correction\000event=0x8a\000\00000\000\000 */ +{ 72 }, /* bp_l2_btb_correct\000branch\000L2 BTB Correction\000event=0x8b\000\00000\000\000 */ +{ 325 }, /* dispatch_blocked.any\000other\000Memory cluster signals to block micro-op dispatch for any reason\000event=9,period=200000,umask=0x20\000\00000\000\000 */ +{ 455 }, /* eist_trans\000other\000Number of Enhanced Intel SpeedStep(R) Technology (EIST) transitions\000event=0x3a,period=200000\000\00000\000\000 */ +{ 131 }, /* l3_cache_rd\000cache\000L3 cache access, read\000event=0x40\000\00000\000Attributable Level 3 cache access, read\000 */ +{ 226 }, /* segment_reg_loads.any\000other\000Number of segment register loads\000event=6,period=200000,umask=0x80\000\00000\000\000 */ +}; +static const struct compact_pmu_event pmu_events__test_soc_cpu_hisi_sccl_ddrc[] = { +{ 585 }, /* uncore_hisi_ddrc.flux_wcmd\000uncore\000DDRC write commands\000event=2\000\00000\000DDRC write commands\000 */ +}; +static const struct compact_pmu_event pmu_events__test_soc_cpu_hisi_sccl_l3c[] = { +{ 1064 }, /* uncore_hisi_l3c.rd_hit_cpipe\000uncore\000Total read hits\000event=7\000\00000\000Total read hits\000 */ +}; +static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_cbox[] = { +{ 914 }, /* event-hyphen\000uncore\000UNC_CBO_HYPHEN\000event=0xe0\000\00000\000UNC_CBO_HYPHEN\000 */ +{ 979 }, /* event-two-hyph\000uncore\000UNC_CBO_TWO_HYPH\000event=0xc0\000\00000\000UNC_CBO_TWO_HYPH\000 */ +{ 683 }, /* unc_cbo_xsnp_response.miss_eviction\000uncore\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000event=0x22,umask=0x81\000\00000\000A cross-core snoop resulted from L3 Eviction which misses in some processor core\000 */ +}; +static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_imc[] = { +{ 1274 }, /* uncore_imc.cache_hits\000uncore\000Total cache hits\000event=0x34\000\00000\000Total cache hits\000 */ +}; +static const struct compact_pmu_event pmu_events__test_soc_cpu_uncore_imc_free_running[] = { +{ 1168 }, /* uncore_imc_free_running.cache_miss\000uncore\000Total cache misses\000event=0x12\000\00000\000Total cache misses\000 */ + +}; + +const struct pmu_table_entry pmu_events__test_soc_cpu[] = { +{ + .entries = pmu_events__test_soc_cpu_default_core, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_default_core), + .pmu_name = { 0 /* default_core\000 */ }, +}, +{ + .entries = pmu_events__test_soc_cpu_hisi_sccl_ddrc, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_hisi_sccl_ddrc), + .pmu_name = { 570 /* hisi_sccl,ddrc\000 */ }, +}, +{ + .entries = pmu_events__test_soc_cpu_hisi_sccl_l3c, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_hisi_sccl_l3c), + .pmu_name = { 1050 /* hisi_sccl,l3c\000 */ }, +}, +{ + .entries = pmu_events__test_soc_cpu_uncore_cbox, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_cbox), + .pmu_name = { 671 /* uncore_cbox\000 */ }, +}, +{ + .entries = pmu_events__test_soc_cpu_uncore_imc, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_imc), + .pmu_name = { 1263 /* uncore_imc\000 */ }, +}, +{ + .entries = pmu_events__test_soc_cpu_uncore_imc_free_running, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_cpu_uncore_imc_free_running), + .pmu_name = { 1144 /* uncore_imc_free_running\000 */ }, +}, +}; + +static const struct compact_pmu_event pmu_metrics__test_soc_cpu_default_core[] = { +{ 1696 }, /* CPI\000\0001 / IPC\000\000\000\000\000\000\000\00000 */ +{ 2377 }, /* DCache_L2_All\000\000DCache_L2_All_Hits + DCache_L2_All_Miss\000\000\000\000\000\000\000\00000 */ +{ 2149 }, /* DCache_L2_All_Hits\000\000l2_rqsts.demand_data_rd_hit + l2_rqsts.pf_hit + l2_rqsts.rfo_hit\000\000\000\000\000\000\000\00000 */ +{ 2243 }, /* DCache_L2_All_Miss\000\000max(l2_rqsts.all_demand_data_rd - l2_rqsts.demand_data_rd_hit, 0) + l2_rqsts.pf_miss + l2_rqsts.rfo_miss\000\000\000\000\000\000\000\00000 */ +{ 2441 }, /* DCache_L2_Hits\000\000d_ratio(DCache_L2_All_Hits, DCache_L2_All)\000\000\000\000\000\000\000\00000 */ +{ 2509 }, /* DCache_L2_Misses\000\000d_ratio(DCache_L2_All_Miss, DCache_L2_All)\000\000\000\000\000\000\000\00000 */ +{ 1781 }, /* Frontend_Bound_SMT\000\000idq_uops_not_delivered.core / (4 * (cpu_clk_unhalted.thread / 2 * (1 + cpu_clk_unhalted.one_thread_active / cpu_clk_unhalted.ref_xclk)))\000\000\000\000\000\000\000\00000 */ +{ 1718 }, /* IPC\000group1\000inst_retired.any / cpu_clk_unhalted.thread\000\000\000\000\000\000\000\00000 */ +{ 2643 }, /* L1D_Cache_Fill_BW\000\00064 * l1d.replacement / 1e9 / duration_time\000\000\000\000\000\000\000\00000 */ +{ 2579 }, /* M1\000\000ipc + M2\000\000\000\000\000\000\000\00000 */ +{ 2601 }, /* M2\000\000ipc + M1\000\000\000\000\000\000\000\00000 */ +{ 2623 }, /* M3\000\0001 / M3\000\000\000\000\000\000\000\00000 */ +{ 2078 }, /* cache_miss_cycles\000group1\000dcache_miss_cpi + icache_miss_cycles\000\000\000\000\000\000\000\00000 */ +{ 1947 }, /* dcache_miss_cpi\000\000l1d\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000 */ +{ 2011 }, /* icache_miss_cycles\000\000l1i\\-loads\\-misses / inst_retired.any\000\000\000\000\000\000\000\00000 */ + +}; + +const struct pmu_table_entry pmu_metrics__test_soc_cpu[] = { +{ + .entries = pmu_metrics__test_soc_cpu_default_core, + .num_entries = ARRAY_SIZE(pmu_metrics__test_soc_cpu_default_core), + .pmu_name = { 0 /* default_core\000 */ }, +}, +}; + +static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_ccn_pmu[] = { +{ 1463 }, /* sys_ccn_pmu.read_cycles\000uncore\000ccn read-cycles event\000config=0x2c\0000x01\00000\000\000 */ +}; +static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_cmn_pmu[] = { +{ 1556 }, /* sys_cmn_pmu.hnf_cache_miss\000uncore\000Counts total cache misses in first lookup result (high priority)\000eventid=1,type=5\000(434|436|43c|43a).*\00000\000\000 */ +}; +static const struct compact_pmu_event pmu_events__test_soc_sys_uncore_sys_ddr_pmu[] = { +{ 1371 }, /* sys_ddr_pmu.write_cycles\000uncore\000ddr write-cycles event\000event=0x2b\000v8\00000\000\000 */ + +}; + +const struct pmu_table_entry pmu_events__test_soc_sys[] = { +{ + .entries = pmu_events__test_soc_sys_uncore_sys_ccn_pmu, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_ccn_pmu), + .pmu_name = { 1444 /* uncore_sys_ccn_pmu\000 */ }, +}, +{ + .entries = pmu_events__test_soc_sys_uncore_sys_cmn_pmu, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_cmn_pmu), + .pmu_name = { 1537 /* uncore_sys_cmn_pmu\000 */ }, +}, +{ + .entries = pmu_events__test_soc_sys_uncore_sys_ddr_pmu, + .num_entries = ARRAY_SIZE(pmu_events__test_soc_sys_uncore_sys_ddr_pmu), + .pmu_name = { 1352 /* uncore_sys_ddr_pmu\000 */ }, +}, +}; + + /* Struct used to make the PMU event table implementation opaque to callers. */ struct pmu_events_table { - const struct pmu_event *entries; + const struct pmu_table_entry *pmus; + uint32_t num_pmus; }; /* Struct used to make the PMU metric table implementation opaque to callers. */ struct pmu_metrics_table { - const struct pmu_metric *entries; + const struct pmu_table_entry *pmus; + uint32_t num_pmus; }; /* @@ -202,92 +199,191 @@ struct pmu_metrics_table { * The cpuid can contain any character other than the comma. */ struct pmu_events_map { - const char *arch; - const char *cpuid; - const struct pmu_events_table event_table; - const struct pmu_metrics_table metric_table; + const char *arch; + const char *cpuid; + struct pmu_events_table event_table; + struct pmu_metrics_table metric_table; }; /* * Global table mapping each known CPU for the architecture to its * table of PMU events. */ -static const struct pmu_events_map pmu_events_map[] = { - { - .arch = "testarch", - .cpuid = "testcpu", - .event_table = { pmu_events__test_soc_cpu }, - .metric_table = { pmu_metrics__test_soc_cpu }, - }, - { - .arch = 0, - .cpuid = 0, - .event_table = { 0 }, - .metric_table = { 0 }, - }, -}; - -static const struct pmu_event pmu_events__test_soc_sys[] = { - { - .name = "sys_ddr_pmu.write_cycles", - .event = "event=0x2b", - .desc = "ddr write-cycles event. Unit: uncore_sys_ddr_pmu ", - .compat = "v8", - .topic = "uncore", - .pmu = "uncore_sys_ddr_pmu", - }, - { - .name = "sys_ccn_pmu.read_cycles", - .event = "config=0x2c", - .desc = "ccn read-cycles event. Unit: uncore_sys_ccn_pmu ", - .compat = "0x01", - .topic = "uncore", - .pmu = "uncore_sys_ccn_pmu", - }, - { - .name = "sys_cmn_pmu.hnf_cache_miss", - .event = "eventid=0x1,type=0x5", - .desc = "Counts total cache misses in first lookup result (high priority). Unit: uncore_sys_cmn_pmu ", - .compat = "(434|436|43c|43a).*", - .topic = "uncore", - .pmu = "uncore_sys_cmn_pmu", - }, - { - .name = 0, - .event = 0, - .desc = 0, +const struct pmu_events_map pmu_events_map[] = { +{ + .arch = "testarch", + .cpuid = "testcpu", + .event_table = { + .pmus = pmu_events__test_soc_cpu, + .num_pmus = ARRAY_SIZE(pmu_events__test_soc_cpu), }, + .metric_table = { + .pmus = pmu_metrics__test_soc_cpu, + .num_pmus = ARRAY_SIZE(pmu_metrics__test_soc_cpu), + } +}, +{ + .arch = 0, + .cpuid = 0, + .event_table = { 0, 0 }, + .metric_table = { 0, 0 }, +} }; struct pmu_sys_events { const char *name; - const struct pmu_events_table table; + struct pmu_events_table event_table; + struct pmu_metrics_table metric_table; }; static const struct pmu_sys_events pmu_sys_event_tables[] = { { - .table = { pmu_events__test_soc_sys }, + .event_table = { + .pmus = pmu_events__test_soc_sys, + .num_pmus = ARRAY_SIZE(pmu_events__test_soc_sys) + }, .name = "pmu_events__test_soc_sys", }, { - .table = { 0 } + .event_table = { 0, 0 }, + .metric_table = { 0, 0 }, }, }; -int pmu_events_table__for_each_event(const struct pmu_events_table *table, struct perf_pmu *pmu, - pmu_event_iter_fn fn, void *data) +static void decompress_event(int offset, struct pmu_event *pe) { - for (const struct pmu_event *pe = &table->entries[0]; pe->name; pe++) { - int ret; + const char *p = &big_c_string[offset]; - if (pmu && !pmu__name_match(pmu, pe->pmu)) + pe->name = (*p == '\0' ? NULL : p); + while (*p++); + pe->topic = (*p == '\0' ? NULL : p); + while (*p++); + pe->desc = (*p == '\0' ? NULL : p); + while (*p++); + pe->event = (*p == '\0' ? NULL : p); + while (*p++); + pe->compat = (*p == '\0' ? NULL : p); + while (*p++); + pe->deprecated = *p - '0'; + p++; + pe->perpkg = *p - '0'; + p++; + pe->unit = (*p == '\0' ? NULL : p); + while (*p++); + pe->long_desc = (*p == '\0' ? NULL : p); +} + +static void decompress_metric(int offset, struct pmu_metric *pm) +{ + const char *p = &big_c_string[offset]; + + pm->metric_name = (*p == '\0' ? NULL : p); + while (*p++); + pm->metric_group = (*p == '\0' ? NULL : p); + while (*p++); + pm->metric_expr = (*p == '\0' ? NULL : p); + while (*p++); + pm->metric_threshold = (*p == '\0' ? NULL : p); + while (*p++); + pm->desc = (*p == '\0' ? NULL : p); + while (*p++); + pm->long_desc = (*p == '\0' ? NULL : p); + while (*p++); + pm->unit = (*p == '\0' ? NULL : p); + while (*p++); + pm->compat = (*p == '\0' ? NULL : p); + while (*p++); + pm->metricgroup_no_group = (*p == '\0' ? NULL : p); + while (*p++); + pm->default_metricgroup_name = (*p == '\0' ? NULL : p); + while (*p++); + pm->aggr_mode = *p - '0'; + p++; + pm->event_grouping = *p - '0'; +} + +static int pmu_events_table__for_each_event_pmu(const struct pmu_events_table *table, + const struct pmu_table_entry *pmu, + pmu_event_iter_fn fn, + void *data) +{ + int ret; + struct pmu_event pe = { + .pmu = &big_c_string[pmu->pmu_name.offset], + }; + + for (uint32_t i = 0; i < pmu->num_entries; i++) { + decompress_event(pmu->entries[i].offset, &pe); + if (!pe.name) + continue; + ret = fn(&pe, table, data); + if (ret) + return ret; + } + return 0; + } + +static int pmu_events_table__find_event_pmu(const struct pmu_events_table *table, + const struct pmu_table_entry *pmu, + const char *name, + pmu_event_iter_fn fn, + void *data) +{ + struct pmu_event pe = { + .pmu = &big_c_string[pmu->pmu_name.offset], + }; + int low = 0, high = pmu->num_entries - 1; + + while (low <= high) { + int cmp, mid = (low + high) / 2; + + decompress_event(pmu->entries[mid].offset, &pe); + + if (!pe.name && !name) + goto do_call; + + if (!pe.name && name) { + low = mid + 1; + continue; + } + if (pe.name && !name) { + high = mid - 1; + continue; + } + + cmp = strcasecmp(pe.name, name); + if (cmp < 0) { + low = mid + 1; + continue; + } + if (cmp > 0) { + high = mid - 1; + continue; + } + do_call: + return fn ? fn(&pe, table, data) : 0; + } + return PMU_EVENTS__NOT_FOUND; +} + +int pmu_events_table__for_each_event(const struct pmu_events_table *table, + struct perf_pmu *pmu, + pmu_event_iter_fn fn, + void *data) +{ + for (size_t i = 0; i < table->num_pmus; i++) { + const struct pmu_table_entry *table_pmu = &table->pmus[i]; + const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; + int ret; + + if (pmu && !pmu__name_match(pmu, pmu_name)) continue; - ret = fn(pe, table, data); - if (ret) - return ret; - } - return 0; + ret = pmu_events_table__for_each_event_pmu(table, table_pmu, fn, data); + if (pmu || ret) + return ret; + } + return 0; } int pmu_events_table__find_event(const struct pmu_events_table *table, @@ -296,14 +392,19 @@ int pmu_events_table__find_event(const struct pmu_events_table *table, pmu_event_iter_fn fn, void *data) { - for (const struct pmu_event *pe = &table->entries[0]; pe->name; pe++) { - if (pmu && !pmu__name_match(pmu, pe->pmu)) + for (size_t i = 0; i < table->num_pmus; i++) { + const struct pmu_table_entry *table_pmu = &table->pmus[i]; + const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; + int ret; + + if (!pmu__name_match(pmu, pmu_name)) continue; - if (!strcasecmp(pe->name, name)) - return fn(pe, table, data); - } - return -1000; + ret = pmu_events_table__find_event_pmu(table, table_pmu, name, fn, data); + if (ret != PMU_EVENTS__NOT_FOUND) + return ret; + } + return PMU_EVENTS__NOT_FOUND; } size_t pmu_events_table__num_events(const struct pmu_events_table *table, @@ -311,160 +412,253 @@ size_t pmu_events_table__num_events(const struct pmu_events_table *table, { size_t count = 0; - for (const struct pmu_event *pe = &table->entries[0]; pe->name; pe++) { - if (pmu && !pmu__name_match(pmu, pe->pmu)) - continue; + for (size_t i = 0; i < table->num_pmus; i++) { + const struct pmu_table_entry *table_pmu = &table->pmus[i]; + const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - count++; - } + if (pmu__name_match(pmu, pmu_name)) + count += table_pmu->num_entries; + } return count; } -int pmu_metrics_table__for_each_metric(const struct pmu_metrics_table *table, pmu_metric_iter_fn fn, - void *data) +static int pmu_metrics_table__for_each_metric_pmu(const struct pmu_metrics_table *table, + const struct pmu_table_entry *pmu, + pmu_metric_iter_fn fn, + void *data) { - for (const struct pmu_metric *pm = &table->entries[0]; pm->metric_expr; pm++) { - int ret = fn(pm, table, data); + int ret; + struct pmu_metric pm = { + .pmu = &big_c_string[pmu->pmu_name.offset], + }; - if (ret) - return ret; - } - return 0; + for (uint32_t i = 0; i < pmu->num_entries; i++) { + decompress_metric(pmu->entries[i].offset, &pm); + if (!pm.metric_expr) + continue; + ret = fn(&pm, table, data); + if (ret) + return ret; + } + return 0; +} + +int pmu_metrics_table__for_each_metric(const struct pmu_metrics_table *table, + pmu_metric_iter_fn fn, + void *data) +{ + for (size_t i = 0; i < table->num_pmus; i++) { + int ret = pmu_metrics_table__for_each_metric_pmu(table, &table->pmus[i], + fn, data); + + if (ret) + return ret; + } + return 0; +} + +static const struct pmu_events_map *map_for_pmu(struct perf_pmu *pmu) +{ + static struct { + const struct pmu_events_map *map; + struct perf_pmu *pmu; + } last_result; + static struct { + const struct pmu_events_map *map; + char *cpuid; + } last_map_search; + static bool has_last_result, has_last_map_search; + const struct pmu_events_map *map = NULL; + char *cpuid = NULL; + size_t i; + + if (has_last_result && last_result.pmu == pmu) + return last_result.map; + + cpuid = perf_pmu__getcpuid(pmu); + + /* + * On some platforms which uses cpus map, cpuid can be NULL for + * PMUs other than CORE PMUs. + */ + if (!cpuid) + goto out_update_last_result; + + if (has_last_map_search && !strcmp(last_map_search.cpuid, cpuid)) { + map = last_map_search.map; + free(cpuid); + } else { + i = 0; + for (;;) { + map = &pmu_events_map[i++]; + + if (!map->arch) { + map = NULL; + break; + } + + if (!strcmp_cpuid_str(map->cpuid, cpuid)) + break; + } + free(last_map_search.cpuid); + last_map_search.cpuid = cpuid; + last_map_search.map = map; + has_last_map_search = true; + } +out_update_last_result: + last_result.pmu = pmu; + last_result.map = map; + has_last_result = true; + return map; } const struct pmu_events_table *perf_pmu__find_events_table(struct perf_pmu *pmu) { - const struct pmu_events_table *table = NULL; - char *cpuid = perf_pmu__getcpuid(pmu); - int i; + const struct pmu_events_map *map = map_for_pmu(pmu); - /* on some platforms which uses cpus map, cpuid can be NULL for - * PMUs other than CORE PMUs. - */ - if (!cpuid) - return NULL; + if (!map) + return NULL; - i = 0; - for (;;) { - const struct pmu_events_map *map = &pmu_events_map[i++]; + if (!pmu) + return &map->event_table; - if (!map->cpuid) - break; + for (size_t i = 0; i < map->event_table.num_pmus; i++) { + const struct pmu_table_entry *table_pmu = &map->event_table.pmus[i]; + const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - if (!strcmp_cpuid_str(map->cpuid, cpuid)) { - table = &map->event_table; - break; - } - } - free(cpuid); - return table; + if (pmu__name_match(pmu, pmu_name)) + return &map->event_table; + } + return NULL; } const struct pmu_metrics_table *perf_pmu__find_metrics_table(struct perf_pmu *pmu) { - const struct pmu_metrics_table *table = NULL; - char *cpuid = perf_pmu__getcpuid(pmu); - int i; + const struct pmu_events_map *map = map_for_pmu(pmu); - /* on some platforms which uses cpus map, cpuid can be NULL for - * PMUs other than CORE PMUs. - */ - if (!cpuid) - return NULL; + if (!map) + return NULL; - i = 0; - for (;;) { - const struct pmu_events_map *map = &pmu_events_map[i++]; + if (!pmu) + return &map->metric_table; - if (!map->cpuid) - break; + for (size_t i = 0; i < map->metric_table.num_pmus; i++) { + const struct pmu_table_entry *table_pmu = &map->metric_table.pmus[i]; + const char *pmu_name = &big_c_string[table_pmu->pmu_name.offset]; - if (!strcmp_cpuid_str(map->cpuid, cpuid)) { - table = &map->metric_table; - break; - } - } - free(cpuid); - return table; + if (pmu__name_match(pmu, pmu_name)) + return &map->metric_table; + } + return NULL; } const struct pmu_events_table *find_core_events_table(const char *arch, const char *cpuid) { - for (const struct pmu_events_map *tables = &pmu_events_map[0]; - tables->arch; - tables++) { - if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) - return &tables->event_table; - } - return NULL; + for (const struct pmu_events_map *tables = &pmu_events_map[0]; + tables->arch; + tables++) { + if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) + return &tables->event_table; + } + return NULL; } const struct pmu_metrics_table *find_core_metrics_table(const char *arch, const char *cpuid) { - for (const struct pmu_events_map *tables = &pmu_events_map[0]; - tables->arch; - tables++) { - if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) - return &tables->metric_table; - } - return NULL; + for (const struct pmu_events_map *tables = &pmu_events_map[0]; + tables->arch; + tables++) { + if (!strcmp(tables->arch, arch) && !strcmp_cpuid_str(tables->cpuid, cpuid)) + return &tables->metric_table; + } + return NULL; } int pmu_for_each_core_event(pmu_event_iter_fn fn, void *data) { - for (const struct pmu_events_map *tables = &pmu_events_map[0]; tables->arch; tables++) { - int ret = pmu_events_table__for_each_event(&tables->event_table, - /*pmu=*/ NULL, fn, data); + for (const struct pmu_events_map *tables = &pmu_events_map[0]; + tables->arch; + tables++) { + int ret = pmu_events_table__for_each_event(&tables->event_table, + /*pmu=*/ NULL, fn, data); - if (ret) - return ret; - } - return 0; + if (ret) + return ret; + } + return 0; } int pmu_for_each_core_metric(pmu_metric_iter_fn fn, void *data) { - for (const struct pmu_events_map *tables = &pmu_events_map[0]; - tables->arch; - tables++) { - int ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); + for (const struct pmu_events_map *tables = &pmu_events_map[0]; + tables->arch; + tables++) { + int ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); - if (ret) - return ret; - } - return 0; + if (ret) + return ret; + } + return 0; } const struct pmu_events_table *find_sys_events_table(const char *name) { - for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; - tables->name; - tables++) { - if (!strcmp(tables->name, name)) - return &tables->table; - } - return NULL; + for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; + tables->name; + tables++) { + if (!strcmp(tables->name, name)) + return &tables->event_table; + } + return NULL; } int pmu_for_each_sys_event(pmu_event_iter_fn fn, void *data) { - for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; - tables->name; - tables++) { - int ret = pmu_events_table__for_each_event(&tables->table, /*pmu=*/ NULL, fn, data); + for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; + tables->name; + tables++) { + int ret = pmu_events_table__for_each_event(&tables->event_table, + /*pmu=*/ NULL, fn, data); - if (ret) - return ret; - } - return 0; + if (ret) + return ret; + } + return 0; } -int pmu_for_each_sys_metric(pmu_metric_iter_fn fn __maybe_unused, void *data __maybe_unused) +int pmu_for_each_sys_metric(pmu_metric_iter_fn fn, void *data) { - return 0; + for (const struct pmu_sys_events *tables = &pmu_sys_event_tables[0]; + tables->name; + tables++) { + int ret = pmu_metrics_table__for_each_metric(&tables->metric_table, fn, data); + + if (ret) + return ret; + } + return 0; } -const char *describe_metricgroup(const char *group __maybe_unused) +static const int metricgroups[][2] = { + +}; + +const char *describe_metricgroup(const char *group) { - return NULL; + int low = 0, high = (int)ARRAY_SIZE(metricgroups) - 1; + + while (low <= high) { + int mid = (low + high) / 2; + const char *mgroup = &big_c_string[metricgroups[mid][0]]; + int cmp = strcmp(mgroup, group); + + if (cmp == 0) { + return &big_c_string[metricgroups[mid][1]]; + } else if (cmp < 0) { + low = mid + 1; + } else { + high = mid - 1; + } + } + return NULL; } diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py index 731776e29f47..fcf0158438b5 100755 --- a/tools/perf/pmu-events/jevents.py +++ b/tools/perf/pmu-events/jevents.py @@ -1256,6 +1256,10 @@ such as "arm/cortex-a34".''', 'output_file', type=argparse.FileType('w', encoding='utf-8'), nargs='?', default=sys.stdout) _args = ap.parse_args() + _args.output_file.write(f""" +/* SPDX-License-Identifier: GPL-2.0 */ +/* THIS FILE WAS AUTOGENERATED BY jevents.py arch={_args.arch} model={_args.model} ! */ +""") _args.output_file.write(""" #include #include "util/header.h" @@ -1281,7 +1285,7 @@ struct pmu_table_entry { if item.name == _args.arch or _args.arch == 'all' or item.name == 'test': archs.append(item.name) - if len(archs) < 2: + if len(archs) < 2 and _args.arch != 'none': raise IOError(f'Missing architecture directory \'{_args.arch}\'') archs.sort() From 7c5dd51bbb6767095df8a5e9e2b4528b8af18538 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 31 Jul 2024 16:00:05 -0700 Subject: [PATCH 069/347] perf python: Remove PYTHON_PERF ifdefs When perf code was compiled one way for the binary and another for the python module, the PYTHON_PERF ifdef was used to remove some code from the python module. Since switching to building the perf code as a series of libraries, with the same libraries being used for the python module, the ifdefs became unused as PYTHON_PERF is never defined. As such remove the ifdefs. Fixes: 9dabf4003423c8d3 ("perf python: Switch module to linking libraries from building source") Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240731230005.12295-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel_fprintf.c | 2 -- tools/perf/util/mmap.c | 4 ---- 2 files changed, 6 deletions(-) diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c index 8719b3cb5646..c2c0500d5da9 100644 --- a/tools/perf/util/evsel_fprintf.c +++ b/tools/perf/util/evsel_fprintf.c @@ -107,7 +107,6 @@ out: return ++printed; } -#ifndef PYTHON_PERF int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, unsigned int print_opts, struct callchain_cursor *cursor, struct strlist *bt_stop_list, FILE *fp) @@ -248,4 +247,3 @@ int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al, return printed; } -#endif /* PYTHON_PERF */ diff --git a/tools/perf/util/mmap.c b/tools/perf/util/mmap.c index 122ee198a86e..43b02293f1d2 100644 --- a/tools/perf/util/mmap.c +++ b/tools/perf/util/mmap.c @@ -230,9 +230,7 @@ void mmap__munmap(struct mmap *map) { bitmap_free(map->affinity_mask.bits); -#ifndef PYTHON_PERF zstd_fini(&map->zstd_data); -#endif perf_mmap__aio_munmap(map); if (map->data != NULL) { @@ -295,12 +293,10 @@ int mmap__mmap(struct mmap *map, struct mmap_params *mp, int fd, struct perf_cpu map->core.flush = mp->flush; -#ifndef PYTHON_PERF if (zstd_init(&map->zstd_data, mp->comp_level)) { pr_debug2("failed to init mmap compressor, error %d\n", errno); return -1; } -#endif if (mp->comp_level && !perf_mmap__aio_enabled(map)) { map->data = mmap(NULL, mmap__mmap_len(map), PROT_READ|PROT_WRITE, From 96465e0179fa8a7059bd48a81a81889dcad7d543 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 31 Jul 2024 16:55:00 -0700 Subject: [PATCH 070/347] perf hist: Correct hist_entry->mem_info refcounts The 'struct mem_info' is created by iter_prepare_mem_entry() at the beginning and destroyed by iter_finish_mem_entry() at the end. So if it's used in a new hist_entry, it should be cloned. Simplify (hopefully) the logic by adding some helper functions and by not holding the refcount in the temporary entry. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240731235505.710436-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 14 +++++++------- tools/perf/util/map_symbol.c | 18 ++++++++++++++++++ tools/perf/util/map_symbol.h | 3 +++ tools/perf/util/mem-info.c | 13 +++++++++++++ tools/perf/util/mem-info.h | 1 + 5 files changed, 42 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f028f113c4fd..f8ee1cd6929d 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -476,6 +476,12 @@ static int hist_entry__init(struct hist_entry *he, he->branch_info->to.ms.map = map__get(he->branch_info->to.ms.map); } + if (he->mem_info) { + he->mem_info = mem_info__clone(template->mem_info); + if (he->mem_info == NULL) + goto err_infos; + } + if (hist_entry__has_callchains(he) && symbol_conf.use_callchain) callchain_init(he->callchain); @@ -620,12 +626,6 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists, if (symbol_conf.cumulate_callchain) he_stat__add_period(he->stat_acc, period); - /* - * This mem info was allocated from sample__resolve_mem - * and will not be used anymore. - */ - mem_info__zput(entry->mem_info); - block_info__delete(entry->block_info); kvm_info__zput(entry->kvm_info); @@ -739,7 +739,7 @@ __hists__add_entry(struct hists *hists, .filtered = symbol__parent_filter(sym_parent) | al->filtered, .hists = hists, .branch_info = bi, - .mem_info = mem_info__get(mi), + .mem_info = mi, .kvm_info = ki, .block_info = block_info, .transaction = sample->transaction, diff --git a/tools/perf/util/map_symbol.c b/tools/perf/util/map_symbol.c index bef5079f2403..6ad2960bc289 100644 --- a/tools/perf/util/map_symbol.c +++ b/tools/perf/util/map_symbol.c @@ -13,3 +13,21 @@ void addr_map_symbol__exit(struct addr_map_symbol *ams) { map_symbol__exit(&ams->ms); } + +void map_symbol__copy(struct map_symbol *dst, struct map_symbol *src) +{ + dst->maps = maps__get(src->maps); + dst->map = map__get(src->map); + dst->sym = src->sym; +} + +void addr_map_symbol__copy(struct addr_map_symbol *dst, struct addr_map_symbol *src) +{ + map_symbol__copy(&dst->ms, &src->ms); + + dst->addr = src->addr; + dst->al_addr = src->al_addr; + dst->al_level = src->al_level; + dst->phys_addr = src->phys_addr; + dst->data_page_size = src->data_page_size; +} diff --git a/tools/perf/util/map_symbol.h b/tools/perf/util/map_symbol.h index 72d5ed938ed6..e370bb32ed47 100644 --- a/tools/perf/util/map_symbol.h +++ b/tools/perf/util/map_symbol.h @@ -26,4 +26,7 @@ struct addr_map_symbol { void map_symbol__exit(struct map_symbol *ms); void addr_map_symbol__exit(struct addr_map_symbol *ams); +void map_symbol__copy(struct map_symbol *dst, struct map_symbol *src); +void addr_map_symbol__copy(struct addr_map_symbol *dst, struct addr_map_symbol *src); + #endif // __PERF_MAP_SYMBOL diff --git a/tools/perf/util/mem-info.c b/tools/perf/util/mem-info.c index 27d67721a695..d3efa9c139f2 100644 --- a/tools/perf/util/mem-info.c +++ b/tools/perf/util/mem-info.c @@ -33,3 +33,16 @@ struct mem_info *mem_info__new(void) return result; } + +struct mem_info *mem_info__clone(struct mem_info *mi) +{ + struct mem_info *result = mem_info__new(); + + if (result) { + addr_map_symbol__copy(mem_info__iaddr(result), mem_info__iaddr(mi)); + addr_map_symbol__copy(mem_info__daddr(result), mem_info__daddr(mi)); + mem_info__data_src(result)->val = mem_info__data_src(mi)->val; + } + + return result; +} diff --git a/tools/perf/util/mem-info.h b/tools/perf/util/mem-info.h index 0f68e29f311b..df75e94ed3d0 100644 --- a/tools/perf/util/mem-info.h +++ b/tools/perf/util/mem-info.h @@ -15,6 +15,7 @@ DECLARE_RC_STRUCT(mem_info) { }; struct mem_info *mem_info__new(void); +struct mem_info *mem_info__clone(struct mem_info *mi); struct mem_info *mem_info__get(struct mem_info *mi); void mem_info__put(struct mem_info *mi); From 3da209bb1177462b6fe8e3021a5527a5a49a9336 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 31 Jul 2024 16:55:01 -0700 Subject: [PATCH 071/347] perf mem: Free the allocated sort string, fixing a leak The get_sort_order() returns either a new string (from strdup) or NULL but it never gets freed. Signed-off-by: Namhyung Kim Fixes: 2e7f545096f954a9 ("perf mem: Factor out a function to generate sort order") Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240731235505.710436-3-namhyung@kernel.org [ Added Fixes tag ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-mem.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 863fcd735dae..93413cfcd585 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -372,6 +372,7 @@ static int report_events(int argc, const char **argv, struct perf_mem *mem) rep_argv[i] = argv[j]; ret = cmd_report(i, rep_argv); + free(new_sort_order); free(rep_argv); return ret; } From 35b38a71c92fa033aa6c8d681ee827e215254964 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 31 Jul 2024 16:55:02 -0700 Subject: [PATCH 072/347] perf mem: Rework command option handling Split the common option and ones for record or report. Otherwise -U in the record option cannot be used because it clashes with in the common (or report) option. Also rename report_events() to __cmd_report() to follow the convention and to be sync with the record part. Also set the flag PARSE_OPT_STOP_AT_NON_OPTION for the common option so that it can show the help message in the subcommand like below: $ perf mem record -h Usage: perf mem record [] [] or: perf mem record [] -- [] -C, --cpu list of cpus to profile -e, --event event selector. use 'perf mem record -e list' to list available events -f, --force don't complain, do it -K, --all-kernel collect only kernel level data -p, --phys-data Record/Report sample physical addresses -t, --type memory operations(load,store) Default load,store -U, --all-user collect only user level data -v, --verbose be more verbose (show counter open errors, etc) --data-page-size Record/Report sample data address page size --ldlat mem-loads latency Cc: Aditya Gupta Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240731235505.710436-4-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-mem.c | 79 +++++++++++++++++++++++----------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 93413cfcd585..819edaf6b1df 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -34,6 +34,8 @@ struct perf_mem { bool force; bool phys_addr; bool data_page_size; + bool all_kernel; + bool all_user; int operation; const char *cpu_list; DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS); @@ -62,33 +64,19 @@ static int parse_record_events(const struct option *opt, return 0; } -static const char * const __usage[] = { - "perf mem record [] []", - "perf mem record [] -- []", - 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 [] []", + "perf mem record [] -- []", + NULL }; pmu = perf_mem_events_find_pmu(); @@ -102,7 +90,7 @@ static int __cmd_record(int argc, const char **argv, struct perf_mem *mem) 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. */ @@ -158,10 +146,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) { @@ -319,6 +307,7 @@ 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; @@ -346,11 +335,19 @@ static char *get_sort_order(struct perf_mem *mem) 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 []", + NULL + }; + + argc = parse_options(argc, argv, options, report_usage, + PARSE_OPT_KEEP_UNKNOWN); if (mem->dump_raw) return report_raw_events(mem); @@ -368,7 +365,7 @@ 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); @@ -475,22 +472,36 @@ int cmd_mem(int argc, const char **argv) 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_PARENT(mem_options) }; const char *const mem_subcommands[] = { "record", "report", NULL }; const char *mem_usage[] = { @@ -499,7 +510,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); @@ -512,9 +523,9 @@ 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); From 871893d748cce346097d907b9937604af3dafcf1 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 31 Jul 2024 16:55:03 -0700 Subject: [PATCH 073/347] perf tools: Add mode argument to sort_help() Some sort keys are meaningful only in a specific mode - like branch stack and memory (data-src). Add the mode to skip unnecessary ones. This will be used for 'perf mem report' later. While at it, change the prefix for the -F/--fields option to remove the duplicate part. Before: $ perf report -F Error: switch `F' requires a value Usage: perf report [] -F, --fields output field(s): overhead period sample overhead overhead_sys overhead_us overhead_guest_sys overhead_guest_us overhead_children sample period weight1 weight2 weight3 ins_lat retire_lat ... After: $ perf report -F Error: switch `F' requires a value Usage: perf report [] -F, --fields output field(s): overhead overhead_sys overhead_us overhead_guest_sys overhead_guest_us overhead_children sample period weight1 weight2 weight3 ins_lat retire_lat ... Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240731235505.710436-5-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 4 ++-- tools/perf/util/sort.c | 12 +++++++----- tools/perf/util/sort.h | 2 +- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 6edc0d4ce6fb..930052961c1a 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -1301,8 +1301,8 @@ int cmd_report(int argc, const char **argv) .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", diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index ab7c7ff35f9b..c4046d5d1749 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -3960,7 +3960,7 @@ static void add_hpp_sort_string(struct strbuf *sb, struct hpp_dimension *s, int add_key(sb, s[i].name, llen); } -char *sort_help(const char *prefix) +char *sort_help(const char *prefix, enum sort_mode mode) { struct strbuf sb; char *s; @@ -3972,10 +3972,12 @@ char *sort_help(const char *prefix) ARRAY_SIZE(hpp_sort_dimensions), &len); add_sort_string(&sb, common_sort_dimensions, ARRAY_SIZE(common_sort_dimensions), &len); - add_sort_string(&sb, bstack_sort_dimensions, - ARRAY_SIZE(bstack_sort_dimensions), &len); - add_sort_string(&sb, memory_sort_dimensions, - ARRAY_SIZE(memory_sort_dimensions), &len); + if (mode == SORT_MODE__NORMAL || mode == SORT_MODE__BRANCH) + add_sort_string(&sb, bstack_sort_dimensions, + ARRAY_SIZE(bstack_sort_dimensions), &len); + if (mode == SORT_MODE__NORMAL || mode == SORT_MODE__MEMORY) + add_sort_string(&sb, memory_sort_dimensions, + ARRAY_SIZE(memory_sort_dimensions), &len); s = strbuf_detach(&sb, NULL); strbuf_release(&sb); return s; diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 0bd0ee3ae76b..6357bc32c5ca 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -130,7 +130,7 @@ void reset_output_field(void); void sort__setup_elide(FILE *fp); void perf_hpp__set_elide(int idx, bool elide); -char *sort_help(const char *prefix); +char *sort_help(const char *prefix, enum sort_mode mode); int report_parse_ignore_callees_opt(const struct option *opt, const char *arg, int unset); From 2d99a991337f70a7d11135d6a539bed8c060cb0f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 31 Jul 2024 16:55:04 -0700 Subject: [PATCH 074/347] perf mem: Add -s/--sort option So that users can set the sort key manually as they want. $ perf mem report -s Error: switch `s' requires a value Usage: perf mem report [] -s, --sort sort by key(s): overhead overhead_sys overhead_us overhead_guest_sys overhead_guest_us overhead_children sample period weight1 weight2 weight3 ins_lat retire_lat p_stage_cyc pid comm dso symbol parent cpu socket srcline srcfile local_weight weight transaction trace symbol_size dso_size cgroup cgroup_id ipc_null time code_page_size local_ins_lat ins_lat local_p_stage_cyc p_stage_cyc addr local_retire_lat retire_lat simd type typeoff symoff symbol_daddr dso_daddr locked tlb mem snoop dcacheline symbol_iaddr phys_daddr data_page_size blocked Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240731235505.710436-6-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-mem.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 819edaf6b1df..24a4f0084f49 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -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 @@ -28,7 +29,8 @@ 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; @@ -313,11 +315,13 @@ 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); /* * 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) { @@ -468,6 +472,7 @@ int cmd_mem(int argc, const char **argv) */ .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", @@ -501,6 +506,8 @@ int cmd_mem(int argc, const char **argv) "separator", "separator for columns, no spaces will be added" " between columns '.' is reserved."), + OPT_STRING('s', "sort", &mem.sort_key, "key[,key2...]", + sort_order_help), OPT_PARENT(mem_options) }; const char *const mem_subcommands[] = { "record", "report", NULL }; From 7320ad972510415515d00838451c51f2db3974a7 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 31 Jul 2024 16:55:05 -0700 Subject: [PATCH 075/347] perf mem: Add -T/--data-type option to report subcommand This is just a shortcut to have 'type' in the sort key and use more compact output format like below. $ perf mem report -T ... # # Overhead Samples Memory access Snoop TLB access Data Type # ........ ............ ....................................... ............ ...................... ......... # 14.84% 22 L1 hit None L1 or L2 hit (unknown) 7.68% 8 LFB/MAB hit None L1 or L2 hit (unknown) 7.17% 3 RAM hit Hit L2 miss (unknown) 6.29% 12 L1 hit None L1 or L2 hit (stack operation) 4.85% 5 RAM hit Hit L1 or L2 hit (unknown) 3.97% 5 LFB/MAB hit None L1 or L2 hit struct psi_group_cpu 3.18% 3 LFB/MAB hit None L1 or L2 hit (stack operation) 2.58% 3 L1 hit None L1 or L2 hit unsigned int 2.36% 2 L1 hit None L1 or L2 hit struct 2.31% 2 L1 hit None L1 or L2 hit struct psi_group_cpu ... Users also can use their own sort keys and -T option makes sure it has the 'type' sort key at the end. $ perf mem report -T -s mem Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240731235505.710436-7-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-mem.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 24a4f0084f49..efa700d14c98 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -38,6 +38,7 @@ struct perf_mem { 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); @@ -317,6 +318,8 @@ static char *get_sort_order(struct perf_mem *mem) 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 @@ -336,6 +339,10 @@ 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); } @@ -508,6 +515,8 @@ int cmd_mem(int argc, const char **argv) " between columns '.' is reserved."), 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 }; From 13159a139d85fa12bbbaefcde5a647e2b9721b19 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 2 Aug 2024 11:09:13 -0700 Subject: [PATCH 076/347] perf mem: Update documentation for new options Add a common options section and move some items to the section. Also add description of new options to report options. Suggested-by: Ian Rogers Reviewed-by: Ian Rogers Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/lkml/20240802180913.1023886-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-mem.txt | 94 ++++++++++++++++++--------- 1 file changed, 63 insertions(+), 31 deletions(-) diff --git a/tools/perf/Documentation/perf-mem.txt b/tools/perf/Documentation/perf-mem.txt index 47456b212e99..8a1bd9ff0f86 100644 --- a/tools/perf/Documentation/perf-mem.txt +++ b/tools/perf/Documentation/perf-mem.txt @@ -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 -------- -...:: - Any command you can specify in a shell. - --i:: ---input=:: - Input file name. - +COMMON OPTIONS +-------------- -f:: --force:: Don't do ownership validation @@ -45,24 +38,9 @@ OPTIONS --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=:: - Specify the field separator used when dump raw samples (-D option). By default, - The separator is the space character. - --C:: ---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 -------------- +...:: + Any command you can specify in a shell. + -e:: --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 :: Specify desired latency for loads event. Supported on Intel and Arm64 processors only. Ignored on other archs. +REPORT OPTIONS +-------------- +-i:: +--input=:: + Input file name. + +-C:: +--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=:: + 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=:: + 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. From 96f30c8f0aa9923aa39b30bcaefeacf88b490231 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 15 Jul 2024 13:32:42 -0700 Subject: [PATCH 077/347] tools build: Correct libsubcmd fixdep dependencies All built targets need fixdep to be built first, before handling object dependencies [1]. We're missing one such dependency before the libsubcmd target. This resolves .cmd file generation issues such that the following sequence produces many fewer results: $ git clean -xfd tools/ $ make tools/objtool $ grep "cannot find fixdep" $(find tools/objtool -name '*.cmd') In particular, only a buggy tools/objtool/libsubcmd/.fixdep.o.cmd remains, due to circular dependencies of fixdep on itself. Such incomplete .cmd files don't usually cause a direct problem, since they're designed to fail "open", but they can cause some subtle problems that would otherwise be handled by proper fixdep'd dependency files. [2] [1] This problem is better described in commit abb26210a395 ("perf tools: Force fixdep compilation at the start of the build"). I don't apply its solution here, because additional recursive make can be a bit of overkill. [2] Example failure case: cp -arl linux-src linux-src2 cd linux-src2 make O=/path/to/out cd ../linux-src rm -rf ../linux-src2 make O=/path/to/out Previously, we'd see errors like: make[6]: *** No rule to make target '/path/to/linux-src2/tools/include/linux/compiler.h', needed by '/path/to/out/tools/bpf/resolve_btfids/libsubcmd/exec-cmd.o'. Stop. Now, the properly-fixdep'd .cmd files will ignore a missing /path/to/linux-src2/... Signed-off-by: Brian Norris Acked-by: Jiri Olsa Cc: Ian Rogers Cc: Josh Poimboeuf Cc: Masahiro Yamada Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Richter Link: https://lore.kernel.org/all/ZGVi9HbI43R5trN8@bhelgaas/ Link: https://lore.kernel.org/all/Zk-C5Eg84yt6_nml@google.com/ Link: https://lore.kernel.org/r/20240715203325.3832977-2-briannorris@chromium.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/subcmd/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/lib/subcmd/Makefile b/tools/lib/subcmd/Makefile index b87213263a5e..59b09f280e49 100644 --- a/tools/lib/subcmd/Makefile +++ b/tools/lib/subcmd/Makefile @@ -76,7 +76,7 @@ include $(srctree)/tools/build/Makefile.include all: fixdep $(LIBFILE) -$(SUBCMD_IN): FORCE +$(SUBCMD_IN): fixdep FORCE @$(MAKE) $(build)=libsubcmd $(LIBFILE): $(SUBCMD_IN) From ea974028a049f2cea4bb6be963ee3e3844a03f6d Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 15 Jul 2024 13:32:43 -0700 Subject: [PATCH 078/347] tools build: Avoid circular .fixdep-in.o.cmd issues The 'fixdep' tool is used to post-process dependency files for various reasons, and it runs after every object file generation command. This even includes 'fixdep' itself. In Kbuild, this isn't actually a problem, because it uses a single command to generate fixdep (a compile-and-link command on fixdep.c), and afterward runs the fixdep command on the accompanying .fixdep.cmd file. In tools/ builds (which notably is maintained separately from Kbuild), fixdep is generated in several phases: 1. fixdep.c -> fixdep-in.o 2. fixdep-in.o -> fixdep Thus, fixdep is not available in the post-processing for step 1, and instead, we generate .cmd files that look like: ## from tools/objtool/libsubcmd/.fixdep.o.cmd # cannot find fixdep (/path/to/linux/tools/objtool/libsubcmd//fixdep) [...] These invalid .cmd files are benign in some respects, but cause problems in others (such as the linked reports). Because the tools/ build system is rather complicated in its own right (and pointedly different than Kbuild), I choose to simply open-code the rule for building fixdep, and avoid the recursive-make indirection that produces the problem in the first place. Signed-off-by: Brian Norris Acked-by: Jiri Olsa Cc: Ian Rogers Cc: Josh Poimboeuf Cc: Masahiro Yamada Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Richter Link: https://lore.kernel.org/all/Zk-C5Eg84yt6_nml@google.com/ Link: https://lore.kernel.org/r/20240715203325.3832977-3-briannorris@chromium.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Build | 3 --- tools/build/Makefile | 11 ++--------- 2 files changed, 2 insertions(+), 12 deletions(-) delete mode 100644 tools/build/Build diff --git a/tools/build/Build b/tools/build/Build deleted file mode 100644 index 76d1a4960973..000000000000 --- a/tools/build/Build +++ /dev/null @@ -1,3 +0,0 @@ -hostprogs := fixdep - -fixdep-y := fixdep.o diff --git a/tools/build/Makefile b/tools/build/Makefile index 17cdf01e29a0..fea3cf647f5b 100644 --- a/tools/build/Makefile +++ b/tools/build/Makefile @@ -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_HOSTLDFLAGS) -o $@ $< From dbb2a7a986971ef43d5a60d235c05491647e16f4 Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Mon, 15 Jul 2024 13:32:44 -0700 Subject: [PATCH 079/347] tools build: Correct bpf fixdep dependencies The dependencies in tools/lib/bpf/Makefile are incorrect. Before we recurse to build $(BPF_IN_STATIC), we need to build its 'fixdep' executable. I can't use the usual shortcut from Makefile.include: : fixdep because its 'fixdep' target relies on $(OUTPUT), and $(OUTPUT) differs in the parent 'make' versus the child 'make' -- so I imitate it via open-coding. I tweak a few $(MAKE) invocations while I'm at it, because 1. I'm adding a new recursive make; and 2. these recursive 'make's print spurious lines about files that are "up to date" (which isn't normally a feature in Kbuild subtargets) or "jobserver not available" (see [1]) I also need to tweak the assignment of the OUTPUT variable, so that relative path builds work. For example, for 'make tools/lib/bpf', OUTPUT is unset, and is usually treated as "cwd" -- but recursive make will change cwd and so OUTPUT has a new meaning. For consistency, I ensure OUTPUT is always an absolute path. And $(Q) gets a backup definition in tools/build/Makefile.include, because Makefile.include is sometimes included without tools/build/Makefile, so the "quiet command" stuff doesn't actually work consistently without it. After this change, top-level builds result in an empty grep result from: $ grep 'cannot find fixdep' $(find tools/ -name '*.cmd') [1] https://www.gnu.org/software/make/manual/html_node/MAKE-Variable.html If we're not using $(MAKE) directly, then we need to use more '+'. Signed-off-by: Brian Norris Acked-by: Andrii Nakryiko Acked-by: Jiri Olsa Cc: Ian Rogers Cc: Josh Poimboeuf Cc: Masahiro Yamada Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Richter Link: https://lore.kernel.org/r/20240715203325.3832977-4-briannorris@chromium.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.include | 12 +++++++++++- tools/lib/bpf/.gitignore | 1 + tools/lib/bpf/Makefile | 13 ++++++++++--- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/tools/build/Makefile.include b/tools/build/Makefile.include index 8dadaa0fbb43..0e4de83400ac 100644 --- a/tools/build/Makefile.include +++ b/tools/build/Makefile.include @@ -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 diff --git a/tools/lib/bpf/.gitignore b/tools/lib/bpf/.gitignore index 0da84cb9e66d..f02725b123b3 100644 --- a/tools/lib/bpf/.gitignore +++ b/tools/lib/bpf/.gitignore @@ -5,3 +5,4 @@ TAGS tags cscope.* /bpf_helper_defs.h +fixdep diff --git a/tools/lib/bpf/Makefile b/tools/lib/bpf/Makefile index 2cf892774346..1b22f0f37288 100644 --- a/tools/lib/bpf/Makefile +++ b/tools/lib/bpf/Makefile @@ -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) \ From 2dc02c26419b984692e20949252ca6cba515abaf Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sat, 3 Aug 2024 14:13:28 -0700 Subject: [PATCH 080/347] perf annotate: Use al->data_nr if possible The data_nr keeps the number of entries in al->data[] so it should use it when it iterates the array. The notes->src->nr_events should have the same number but it'd be natural to use al->data_nr. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240803211332.1107222-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index a2ee4074f768..91ad948c89d5 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -1594,13 +1594,12 @@ bool ui__has_annotation(void) static double annotation_line__max_percent(struct annotation_line *al, - struct annotation *notes, unsigned int percent_type) { double percent_max = 0.0; int i; - for (i = 0; i < notes->src->nr_events; i++) { + for (i = 0; i < al->data_nr; i++) { double percent; percent = annotation_data__percent(&al->data[i], @@ -1672,7 +1671,7 @@ static void __annotation_line__write(struct annotation_line *al, struct annotati void (*obj__write_graph)(void *obj, int graph)) { - double percent_max = annotation_line__max_percent(al, notes, percent_type); + double percent_max = annotation_line__max_percent(al, percent_type); int pcnt_width = annotation__pcnt_width(notes), cycles_width = annotation__cycles_width(notes); bool show_title = false; @@ -1690,7 +1689,7 @@ static void __annotation_line__write(struct annotation_line *al, struct annotati if (al->offset != -1 && percent_max != 0.0) { int i; - for (i = 0; i < notes->src->nr_events; i++) { + for (i = 0; i < al->data_nr; i++) { double percent; percent = annotation_data__percent(&al->data[i], percent_type); From cb1e8bfc7914747692c06b215539ef80105fddbc Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sat, 3 Aug 2024 14:13:29 -0700 Subject: [PATCH 081/347] perf annotate: Set notes->src->nr_events early We want to use it in different places so make sure it sets properly in symbol__annotate() before creating the disasm lines. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240803211332.1107222-3-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 91ad948c89d5..09e6fdf344db 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -925,6 +925,11 @@ int symbol__annotate(struct map_symbol *ms, struct evsel *evsel, return -1; } + if (evsel__is_group_event(evsel)) + notes->src->nr_events = evsel->core.nr_members; + else + notes->src->nr_events = 1; + if (annotate_opts.full_addr) notes->src->start = map__objdump_2mem(ms->map, ms->sym->start); else @@ -1842,10 +1847,7 @@ int symbol__annotate2(struct map_symbol *ms, struct evsel *evsel, struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); size_t size = symbol__size(sym); - int nr_pcnt = 1, err; - - if (evsel__is_group_event(evsel)) - nr_pcnt = evsel->core.nr_members; + int err; err = symbol__annotate(ms, evsel, parch); if (err) @@ -1861,8 +1863,6 @@ int symbol__annotate2(struct map_symbol *ms, struct evsel *evsel, return err; annotation__init_column_widths(notes, sym); - notes->src->nr_events = nr_pcnt; - annotation__update_column_widths(notes); sym->annotate2 = 1; From b00e4d0d93d30a693ee88b2c25f40181d327e53b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sat, 3 Aug 2024 14:13:30 -0700 Subject: [PATCH 082/347] perf annotate: Use annotation__pcnt_width() consistently The annotation__pcnt_width() calculates the screen width for the overhead (percent) area considering event groups properly. Use this function consistently so that we can make sure it has similar output in different modes. But there's a difference in stdio and tui output: stdio uses 8 and tui uses 7 for a percent. Let's use 8 and adjust the print width in __annotation_line__write() properly. Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240803211332.1107222-4-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 14 +++++--------- tools/perf/util/annotate.h | 2 +- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 09e6fdf344db..917897fe44a2 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -699,13 +699,13 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start int percent_type) { struct disasm_line *dl = container_of(al, struct disasm_line, al); + struct annotation *notes = symbol__annotation(sym); static const char *prev_line; if (al->offset != -1) { double max_percent = 0.0; int i, nr_percent = 1; const char *color; - struct annotation *notes = symbol__annotation(sym); for (i = 0; i < al->data_nr; i++) { double percent; @@ -775,14 +775,11 @@ annotation_line__print(struct annotation_line *al, struct symbol *sym, u64 start } else if (max_lines && printed >= max_lines) return 1; else { - int width = symbol_conf.show_total_period ? 12 : 8; + int width = annotation__pcnt_width(notes); if (queue) return -1; - if (evsel__is_group_event(evsel)) - width *= evsel->core.nr_members; - if (!*al->line) printf(" %*s:\n", width, " "); else @@ -1111,7 +1108,7 @@ int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel) int more = 0; bool context = opts->context; u64 len; - int width = symbol_conf.show_total_period ? 12 : 8; + int width = annotation__pcnt_width(notes); int graph_dotted_len; char buf[512]; @@ -1127,7 +1124,6 @@ int symbol__annotate_printf(struct map_symbol *ms, struct evsel *evsel) len = symbol__size(sym); if (evsel__is_group_event(evsel)) { - width *= evsel->core.nr_members; evsel__group_desc(evsel, buf, sizeof(buf)); evsel_name = buf; } @@ -1703,10 +1699,10 @@ static void __annotation_line__write(struct annotation_line *al, struct annotati if (symbol_conf.show_total_period) { obj__printf(obj, "%11" PRIu64 " ", al->data[i].he.period); } else if (symbol_conf.show_nr_samples) { - obj__printf(obj, "%6" PRIu64 " ", + obj__printf(obj, "%7" PRIu64 " ", al->data[i].he.nr_samples); } else { - obj__printf(obj, "%6.2f ", percent); + obj__printf(obj, "%7.2f ", percent); } } } else { diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 9ba772f46270..64e70d716ff1 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -339,7 +339,7 @@ static inline int annotation__cycles_width(struct annotation *notes) static inline int annotation__pcnt_width(struct annotation *notes) { - return (symbol_conf.show_total_period ? 12 : 7) * notes->src->nr_events; + return (symbol_conf.show_total_period ? 12 : 8) * notes->src->nr_events; } static inline bool annotation_line__filter(struct annotation_line *al) From bb588e38290fb7236e7f2c0bcbd720150277ca83 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sat, 3 Aug 2024 14:13:31 -0700 Subject: [PATCH 083/347] perf annotate: Set al->data_nr using the notes->src->nr_events This is a preparation to support skipping empty events. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240803211332.1107222-5-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/disasm.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 85fb0cfedf94..22289003e16d 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -1037,10 +1037,8 @@ static size_t disasm_line_size(int nr) struct disasm_line *disasm_line__new(struct annotate_args *args) { struct disasm_line *dl = NULL; - int nr = 1; - - if (evsel__is_group_event(args->evsel)) - nr = args->evsel->core.nr_members; + struct annotation *notes = symbol__annotation(args->ms.sym); + int nr = notes->src->nr_events; dl = zalloc(disasm_line_size(nr)); if (!dl) From ce533c9bc6deb12535348693fd48fab92a748c2a Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sat, 3 Aug 2024 14:13:32 -0700 Subject: [PATCH 084/347] perf annotate: Add --skip-empty option Like in 'perf report', we want to hide empty events in the 'perf annotate' output. This is consistent when the option is set in perf report. For example, the following command would use 3 events including dummy. $ perf mem record -a -- perf test -w noploop $ perf evlist cpu/mem-loads,ldlat=30/P cpu/mem-stores/P dummy:u Just using perf annotate with --group will show the all 3 events. $ perf annotate --group --stdio | head Percent | Source code & Disassembly of ... -------------------------------------------------------------- : 0 0xe060 <_dl_relocate_object>: 0.00 0.00 0.00 : e060: pushq %rbp 0.00 0.00 0.00 : e061: movq %rsp, %rbp 0.00 0.00 0.00 : e064: pushq %r15 0.00 0.00 0.00 : e066: movq %rdi, %r15 0.00 0.00 0.00 : e069: pushq %r14 0.00 0.00 0.00 : e06b: pushq %r13 0.00 0.00 0.00 : e06d: movl %edx, %r13d Now with --skip-empty, it'll hide the last dummy event. $ perf annotate --group --stdio --skip-empty | head Percent | Source code & Disassembly of ... ------------------------------------------------------ : 0 0xe060 <_dl_relocate_object>: 0.00 0.00 : e060: pushq %rbp 0.00 0.00 : e061: movq %rsp, %rbp 0.00 0.00 : e064: pushq %r15 0.00 0.00 : e066: movq %rdi, %r15 0.00 0.00 : e069: pushq %r14 0.00 0.00 : e06b: pushq %r13 0.00 0.00 : e06d: movl %edx, %r13d Committer testing: root@x1:~# perf evlist cpu_atom/mem-loads,ldlat=30/P cpu_atom/mem-stores/P dummy:u root@x1:~# Before: root@x1:~# perf annotate --group --stdio2 do_lookup_x | head -25 Samples: 20 of events 'cpu_atom/mem-loads,ldlat=30/P, cpu_atom/mem-stores/P, dummy:u', 4000 Hz, Event count (approx.): 769079, [percent: local period] do_lookup_x() /usr/lib64/ld-linux-x86-64.so.2 Percent 0x9900 : pushq %rbp movq %rsp,%rbp pushq %r15 pushq %r14 pushq %r13 pushq %r12 pushq %rbx subq $0x88,%rsp movq %rdi,-0x50(%rbp) movl 8(%r9),%edi movq 0x10(%rbp),%r12 movq 0x28(%rbp),%r10 movq %rdx,-0x70(%rbp) movq %rcx,-0x58(%rbp) movq %rdi,%r11 0.00 5.73 0.00 movq %r8,-0x68(%rbp) movq (%r9),%r8 movl %esi,%eax 8.30 0.00 0.00 movl 0x30(%rbp),%r9d movl %esi,%r15d shrl $6, %eax movq %r8,%r13 root@x1:~# After: root@x1:~# perf annotate --group --skip-empty --stdio2 do_lookup_x | head -25 Samples: 20 of events 'cpu_atom/mem-loads,ldlat=30/P, cpu_atom/mem-stores/P', 4000 Hz, Event count (approx.): 769079, [percent: local period] do_lookup_x() /usr/lib64/ld-linux-x86-64.so.2 Percent 0x9900 : pushq %rbp movq %rsp,%rbp pushq %r15 pushq %r14 pushq %r13 pushq %r12 pushq %rbx subq $0x88,%rsp movq %rdi,-0x50(%rbp) movl 8(%r9),%edi movq 0x10(%rbp),%r12 movq 0x28(%rbp),%r10 movq %rdx,-0x70(%rbp) movq %rcx,-0x58(%rbp) movq %rdi,%r11 0.00 5.73 movq %r8,-0x68(%rbp) movq (%r9),%r8 movl %esi,%eax 8.30 0.00 movl 0x30(%rbp),%r9d movl %esi,%r15d shrl $6, %eax movq %r8,%r13 root@x1:~# Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240803211332.1107222-6-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-annotate.txt | 3 +++ tools/perf/builtin-annotate.c | 2 ++ tools/perf/util/annotate.c | 22 +++++++++++++++++----- 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt index b95524bea021..156c5f37b051 100644 --- a/tools/perf/Documentation/perf-annotate.txt +++ b/tools/perf/Documentation/perf-annotate.txt @@ -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 -------- diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index cf60392b1c19..efcadb7620b8 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -795,6 +795,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; diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 917897fe44a2..eafe8d65052e 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -848,6 +848,10 @@ static void annotation__calc_percent(struct annotation *notes, BUG_ON(i >= al->data_nr); + if (symbol_conf.skip_empty && + evsel__hists(evsel)->stats.nr_samples == 0) + continue; + data = &al->data[i++]; calc_percent(notes, evsel, data, al->offset, end); @@ -901,7 +905,7 @@ int symbol__annotate(struct map_symbol *ms, struct evsel *evsel, .options = &annotate_opts, }; struct arch *arch = NULL; - int err; + int err, nr; err = evsel__get_arch(evsel, &arch); if (err < 0) @@ -922,10 +926,18 @@ int symbol__annotate(struct map_symbol *ms, struct evsel *evsel, return -1; } - if (evsel__is_group_event(evsel)) - notes->src->nr_events = evsel->core.nr_members; - else - notes->src->nr_events = 1; + nr = 0; + if (evsel__is_group_event(evsel)) { + struct evsel *pos; + + for_each_group_evsel(pos, evsel) { + if (symbol_conf.skip_empty && + evsel__hists(pos)->stats.nr_samples == 0) + continue; + nr++; + } + } + notes->src->nr_events = nr ? nr : 1; if (annotate_opts.full_addr) notes->src->start = map__objdump_2mem(ms->map, ms->sym->start); From 2576b20abdb18d80c22cea783fda80e2235f36dd Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 5 Aug 2024 12:44:20 -0700 Subject: [PATCH 085/347] perf test: Add build test for JEVENTS_ARCH=all Building with JEVENTS_ARCH=all builds all CPU types and allows things like assertions to check the validity of the input JSON. Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Atish Patra Cc: Changbin Du Cc: Charles Ci-Jyun Wu Cc: Eric Lin Cc: Greentime Hu Cc: Guilherme Amadio Cc: Ingo Molnar Cc: Inochi Amaoto Cc: James Clark Cc: Ji Sheng Teoh Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: Kan Liang Cc: Leo Yan Cc: Locus Wei-Han Chen Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Samuel Holland Cc: Sandipan Das Cc: Vincent Chen Cc: Will Deacon Cc: Xu Yang Link: https://lore.kernel.org/r/20240805194424.597244-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/make | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/tests/make b/tools/perf/tests/make index a1f8adf85367..8a9edf758f10 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -70,6 +70,7 @@ make_python_perf_so := $(python_perf_so) make_debug := DEBUG=1 make_nondistro := BUILD_NONDISTRO=1 make_extra_tests := EXTRA_TESTS=1 +make_jevents_all := JEVENTS_ARCH=all make_no_bpf_skel := BUILD_BPF_SKEL=0 make_gen_vmlinux_h := GEN_VMLINUX_H=1 make_no_libperl := NO_LIBPERL=1 @@ -140,6 +141,7 @@ run += make_python_perf_so run += make_debug run += make_nondistro run += make_extra_tests +run += make_jevents_all run += make_no_bpf_skel run += make_gen_vmlinux_h run += make_no_libperl From b79f9a437a5758c6dee29c0dbcce8db293f088c0 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 5 Aug 2024 12:44:21 -0700 Subject: [PATCH 086/347] perf pmu-events: Change dependencies for empty-pmu-events.c test Switch from $? (all the prerequisites that are newer than the target) to $^ (all the prerequisites) as touching jevents.py will mean that empty-pmu-events.c won't be passed to the diff command breaking the build. Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Atish Patra Cc: Changbin Du Cc: Charles Ci-Jyun Wu Cc: Eric Lin Cc: Greentime Hu Cc: Guilherme Amadio Cc: Ingo Molnar Cc: Inochi Amaoto Cc: James Clark Cc: Ji Sheng Teoh Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: Kan Liang Cc: Leo Yan Cc: Locus Wei-Han Chen Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Samuel Holland Cc: Sandipan Das Cc: Vincent Chen Cc: Will Deacon Cc: Xu Yang Link: https://lore.kernel.org/r/20240805194424.597244-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/Build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build index c3fa43c49706..d941bc9d16e9 100644 --- a/tools/perf/pmu-events/Build +++ b/tools/perf/pmu-events/Build @@ -39,7 +39,7 @@ $(TEST_EMPTY_PMU_EVENTS_C): $(JSON) $(JSON_TEST) $(JEVENTS_PY) $(METRIC_PY) $(ME $(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) + $(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) From c4f74bb61ae01b30067ae3878ca8ba7d677283ae Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 5 Aug 2024 12:44:22 -0700 Subject: [PATCH 087/347] perf pmu-events: Remove duplicated ampereone event OP_SPEC is repeated twice in the file which will break invariants in 'perf list' as discussed in this thread: https://lore.kernel.org/linux-perf-users/20240719081651.24853-1-eric.lin@sifive.com/ Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Atish Patra Cc: Changbin Du Cc: Charles Ci-Jyun Wu Cc: Eric Lin Cc: Greentime Hu Cc: Guilherme Amadio Cc: Ingo Molnar Cc: Inochi Amaoto Cc: James Clark Cc: Ji Sheng Teoh Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: Kan Liang Cc: Leo Yan Cc: Locus Wei-Han Chen Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Samuel Holland Cc: Sandipan Das Cc: Vincent Chen Cc: Will Deacon Cc: Xu Yang Link: https://lore.kernel.org/r/20240805194424.597244-3-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- .../pmu-events/arch/arm64/ampere/ampereone/instruction.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/perf/pmu-events/arch/arm64/ampere/ampereone/instruction.json b/tools/perf/pmu-events/arch/arm64/ampere/ampereone/instruction.json index 18d1f2f76a23..9fe697d12fe0 100644 --- a/tools/perf/pmu-events/arch/arm64/ampere/ampereone/instruction.json +++ b/tools/perf/pmu-events/arch/arm64/ampere/ampereone/instruction.json @@ -77,9 +77,6 @@ { "ArchStdEvent": "OP_RETIRED" }, - { - "ArchStdEvent": "OP_SPEC" - }, { "PublicDescription": "Operation speculatively executed, NOP", "EventCode": "0x100", From 4bd380390fcce5c77610bc9067a9a97cfd999402 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 5 Aug 2024 12:44:24 -0700 Subject: [PATCH 088/347] perf jevents.py: Ensure event names aren't duplicated Duplicate event names break invariants in 'perf list'. Assert that an event name isn't duplicated so that broken JSON won't build. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Albert Ou Cc: Alexander Shishkin Cc: Atish Patra Cc: Changbin Du Cc: Charles Ci-Jyun Wu Cc: Eric Lin Cc: Greentime Hu Cc: Guilherme Amadio Cc: Ingo Molnar Cc: Inochi Amaoto Cc: James Clark Cc: Ji Sheng Teoh Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: Kan Liang Cc: Leo Yan Cc: Locus Wei-Han Chen Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Palmer Dabbelt Cc: Paul Walmsley Cc: Peter Zijlstra Cc: Samuel Holland Cc: Sandipan Das Cc: Vincent Chen Cc: Will Deacon Cc: Xu Yang Link: https://lore.kernel.org/r/20240805194424.597244-5-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/jevents.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py index fcf0158438b5..1d96b2204e52 100755 --- a/tools/perf/pmu-events/jevents.py +++ b/tools/perf/pmu-events/jevents.py @@ -503,8 +503,11 @@ def print_pending_events() -> None: first = True last_pmu = None + last_name = None pmus = set() for event in sorted(_pending_events, key=event_cmp_key): + if last_pmu and last_pmu == event.pmu: + assert event.name != last_name, f"Duplicate event: {last_pmu}/{last_name}/ in {_pending_events_tblname}" if event.pmu != last_pmu: if not first: _args.output_file.write('};\n') @@ -516,6 +519,7 @@ def print_pending_events() -> None: pmus.add((event.pmu, pmu_name)) _args.output_file.write(event.to_c_string(metric=False)) + last_name = event.name _pending_events = [] _args.output_file.write(f""" From b2f70c99edc7e4dcd201abbdcc4b75030973edce Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 6 Aug 2024 23:51:36 -0700 Subject: [PATCH 089/347] perf hist: Fix reference counting of branch_info iter_finish_branch_entry() doesn't put the branch_info from/to map elements creating memory leaks. This can be seen with: ``` $ perf record -e cycles -b perf test -w noploop $ perf report -D ... Direct leak of 984344 byte(s) in 123043 object(s) allocated from: #0 0x7fb2654f3bd7 in malloc libsanitizer/asan/asan_malloc_linux.cpp:69 #1 0x564d3400d10b in map__get util/map.h:186 #2 0x564d3400d10b in ip__resolve_ams util/machine.c:1981 #3 0x564d34014d81 in sample__resolve_bstack util/machine.c:2151 #4 0x564d34094790 in iter_prepare_branch_entry util/hist.c:898 #5 0x564d34098fa4 in hist_entry_iter__add util/hist.c:1238 #6 0x564d33d1f0c7 in process_sample_event tools/perf/builtin-report.c:334 #7 0x564d34031eb7 in perf_session__deliver_event util/session.c:1655 #8 0x564d3403ba52 in do_flush util/ordered-events.c:245 #9 0x564d3403ba52 in __ordered_events__flush util/ordered-events.c:324 #10 0x564d3402d32e in perf_session__process_user_event util/session.c:1708 #11 0x564d34032480 in perf_session__process_event util/session.c:1877 #12 0x564d340336ad in reader__read_event util/session.c:2399 #13 0x564d34033fdc in reader__process_events util/session.c:2448 #14 0x564d34033fdc in __perf_session__process_events util/session.c:2495 #15 0x564d34033fdc in perf_session__process_events util/session.c:2661 #16 0x564d33d27113 in __cmd_report tools/perf/builtin-report.c:1065 #17 0x564d33d27113 in cmd_report tools/perf/builtin-report.c:1805 #18 0x564d33e0ccb7 in run_builtin tools/perf/perf.c:350 #19 0x564d33e0d45e in handle_internal_command tools/perf/perf.c:403 #20 0x564d33cdd827 in run_argv tools/perf/perf.c:447 #21 0x564d33cdd827 in main tools/perf/perf.c:561 ... ``` Clearing up the map_symbols properly creates maps reference count issues so resolve those. Resolving this issue doesn't improve peak heap consumption for the test above. Committer testing: $ sudo dnf install libasan $ make -k CORESIGHT=1 EXTRA_CFLAGS="-fsanitize=address" CC=clang O=/tmp/build/$(basename $PWD)/ -C tools/perf install-bin Reviewed-by: Kan Liang Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Sun Haiyong Cc: Yanteng Si Link: https://lore.kernel.org/r/20240807065136.1039977-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index f8ee1cd6929d..c8c1b511f8a7 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -472,7 +472,9 @@ static int hist_entry__init(struct hist_entry *he, memcpy(he->branch_info, template->branch_info, sizeof(*he->branch_info)); + he->branch_info->from.ms.maps = maps__get(he->branch_info->from.ms.maps); he->branch_info->from.ms.map = map__get(he->branch_info->from.ms.map); + he->branch_info->to.ms.maps = maps__get(he->branch_info->to.ms.maps); he->branch_info->to.ms.map = map__get(he->branch_info->to.ms.map); } @@ -970,10 +972,21 @@ out: return err; } +static void branch_info__exit(struct branch_info *bi) +{ + map_symbol__exit(&bi->from.ms); + map_symbol__exit(&bi->to.ms); + zfree_srcline(&bi->srcline_from); + zfree_srcline(&bi->srcline_to); +} + static int iter_finish_branch_entry(struct hist_entry_iter *iter, struct addr_location *al __maybe_unused) { + for (int i = 0; i < iter->total; i++) + branch_info__exit(&iter->bi[i]); + zfree(&iter->bi); iter->he = NULL; @@ -1319,10 +1332,7 @@ void hist_entry__delete(struct hist_entry *he) map_symbol__exit(&he->ms); if (he->branch_info) { - map_symbol__exit(&he->branch_info->from.ms); - map_symbol__exit(&he->branch_info->to.ms); - zfree_srcline(&he->branch_info->srcline_from); - zfree_srcline(&he->branch_info->srcline_to); + branch_info__exit(he->branch_info); zfree(&he->branch_info); } From 037f1b67e81c2500c94625b089cfd1ecc76a18b5 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 5 Aug 2024 16:46:48 -0700 Subject: [PATCH 090/347] perf annotate: Cache debuginfo for data type profiling In find_data_type(), it creates and deletes a debug info whenver it tries to find data type for a sample. This is inefficient and it most likely accesses the same binary again and again. Let's add a single entry cache the debug info structure for the last DSO. Depending on sample data, it usually gives me 2~3x (and sometimes more) speed ups. Note that this will introduce a little difference in the output due to the order of checking stack operations. It used to check the stack ops before checking the availability of debug info but I moved it after the symbol check. So it'll report stack operations in DSOs without debug info as unknown. But I think it's ok and better to have the checking near the caching logic. Committer testing: root@x1:~# perf mem record -a sleep 5s root@x1:~# perf evlist cpu_atom/mem-loads,ldlat=30/P cpu_atom/mem-stores/P dummy:u root@x1:~# diff -u before after --- before 2024-08-08 09:33:53.880780784 -0300 +++ after 2024-08-08 09:35:13.917325041 -0300 @@ -81,8 +81,8 @@ # Overhead Data Type # ........ ......... # - 55.43% (unknown) - 11.61% (stack operation) + 55.56% (unknown) + 11.48% (stack operation) 4.93% struct pcpu_hot 3.26% unsigned int 2.48% struct Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240805234648.1453689-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 15 ++----------- tools/perf/util/annotate-data.h | 2 +- tools/perf/util/annotate.c | 37 +++++++++++++++++++++++++++++++++ tools/perf/util/annotate.h | 2 ++ tools/perf/util/session.c | 2 ++ 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 734acdd8c4b7..f125ac5f0bda 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -1345,16 +1345,9 @@ out: */ struct annotated_data_type *find_data_type(struct data_loc_info *dloc) { - struct annotated_data_type *result = NULL; struct dso *dso = map__dso(dloc->ms->map); Dwarf_Die type_die; - dloc->di = debuginfo__new(dso__long_name(dso)); - if (dloc->di == NULL) { - pr_debug_dtp("cannot get the debug info\n"); - return NULL; - } - /* * The type offset is the same as instruction offset by default. * But when finding a global variable, the offset won't be valid. @@ -1364,13 +1357,9 @@ struct annotated_data_type *find_data_type(struct data_loc_info *dloc) dloc->fbreg = -1; if (find_data_type_die(dloc, &type_die) < 0) - goto out; + return NULL; - result = dso__findnew_data_type(dso, &type_die); - -out: - debuginfo__delete(dloc->di); - return result; + return dso__findnew_data_type(dso, &type_die); } static int alloc_data_type_histograms(struct annotated_data_type *adt, int nr_entries) diff --git a/tools/perf/util/annotate-data.h b/tools/perf/util/annotate-data.h index 992b7ce4bd11..37a1a3b68e0b 100644 --- a/tools/perf/util/annotate-data.h +++ b/tools/perf/util/annotate-data.h @@ -123,9 +123,9 @@ struct data_loc_info { u64 var_addr; u8 cpumode; struct annotated_op_loc *op; + struct debuginfo *di; /* These are used internally */ - struct debuginfo *di; int fbreg; bool fb_cfa; diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index eafe8d65052e..a87d2e97e10d 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -25,6 +25,7 @@ #include "srcline.h" #include "units.h" #include "debug.h" +#include "debuginfo.h" #include "annotate.h" #include "annotate-data.h" #include "evsel.h" @@ -2333,6 +2334,20 @@ u64 annotate_calc_pcrel(struct map_symbol *ms, u64 ip, int offset, return map__rip_2objdump(ms->map, addr); } +static struct debuginfo_cache { + struct dso *dso; + struct debuginfo *dbg; +} di_cache; + +void debuginfo_cache__delete(void) +{ + dso__put(di_cache.dso); + di_cache.dso = NULL; + + debuginfo__delete(di_cache.dbg); + di_cache.dbg = NULL; +} + /** * hist_entry__get_data_type - find data type for given hist entry * @he: hist entry @@ -2367,6 +2382,27 @@ struct annotated_data_type *hist_entry__get_data_type(struct hist_entry *he) return NULL; } + /* + * di_cache holds a pair of values, but code below assumes + * di_cache.dso can be compared/updated and di_cache.dbg can be + * read/updated independently from each other. That assumption only + * holds in single threaded code. + */ + assert(perf_singlethreaded); + + if (map__dso(ms->map) != di_cache.dso) { + dso__put(di_cache.dso); + di_cache.dso = dso__get(map__dso(ms->map)); + + debuginfo__delete(di_cache.dbg); + di_cache.dbg = debuginfo__new(dso__long_name(di_cache.dso)); + } + + if (di_cache.dbg == NULL) { + ann_data_stat.no_dbginfo++; + return NULL; + } + /* Make sure it has the disasm of the function */ if (symbol__annotate(ms, evsel, &arch) < 0) { ann_data_stat.no_insn++; @@ -2411,6 +2447,7 @@ retry: .ip = ms->sym->start + dl->al.offset, .cpumode = he->cpumode, .op = op_loc, + .di = di_cache.dbg, }; if (!op_loc->mem_ref && op_loc->segment == INSN_SEG_NONE) diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 64e70d716ff1..0e52558baa01 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -543,4 +543,6 @@ struct annotated_basic_block { int annotate_get_basic_blocks(struct symbol *sym, s64 src, s64 dst, struct list_head *head); +void debuginfo_cache__delete(void); + #endif /* __PERF_ANNOTATE_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 5596bed1b8c8..f9072e003367 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -36,6 +36,7 @@ #include "util.h" #include "arch/common.h" #include "units.h" +#include "annotate.h" #include #ifdef HAVE_ZSTD_SUPPORT @@ -304,6 +305,7 @@ void perf_session__delete(struct perf_session *session) return; auxtrace__free(session); auxtrace_index__free(&session->auxtrace_index); + debuginfo_cache__delete(); perf_session__destroy_kernel_maps(session); perf_decomp__release_events(session->decomp_data.decomp); perf_env__exit(&session->header.env); From 90d78e7b8e57b064f700545081fc9288c1564e92 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 7 Aug 2024 15:31:29 -0700 Subject: [PATCH 091/347] perf annotate-data: Show typedef names properly The die_get_typename() would resolve typedef and get to the original type. But sometimes the original type is a struct without name and it makes the output confusing and hard to read. This is a diff of perf report -s type before and after the change. New types such as atomic{,64}_t and sigset_t appeared and the portion of unnamed struct was reduced. Also u32, u64 and size_t were splitted from the base types. --- b 2024-08-01 17:02:34.307809952 -0700 +++ a 2024-08-07 14:17:05.245853999 -0700 - 2.40% long unsigned int + 2.26% long unsigned int - 1.56% unsigned int + 1.27% unsigned int - 0.98% struct - 0.79% long long unsigned int + 0.58% long long unsigned int + 0.36% struct + 0.27% atomic64_t + 0.22% u32 + 0.21% u64 + 0.19% atomic_t + 0.13% size_t - 0.08% struct seqcount_spinlock + 0.08% seqcount_spinlock_t + 0.08% sigset_t + 0.08% __poll_t Let's use the typedef name directly and the resolved to get the size of the type. Committer testing: root@x1:~# diff -u before after | head -30 --- before 2024-08-08 09:35:13.917325041 -0300 +++ after 2024-08-08 09:37:35.312257905 -0300 @@ -10,25 +10,27 @@ # ........ ......... # 79.40% (unknown) - 2.28% union 1.96% (stack operation) - 1.24% struct + 1.87% pthread_mutex_t 0.99% u32[] - 0.92% unsigned int 0.77% struct task_struct + 0.75% U32 0.75% struct pcpu_hot 0.63% struct qspinlock + 0.61% atomic_t 0.59% struct list_head - 0.58% int 0.53% struct cfs_rq 0.51% BYTE* - 0.48% unsigned char + 0.48% BYTE 0.48% long unsigned int 0.46% struct rq 0.41% struct worker 0.41% struct memcg_vmstats_percpu + 0.41% pthread_cond_t 0.37% _Bool + 0.36% int root@x1:~# Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Masami Hiramatsu Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240807223129.1738004-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 39 +++++++++++++++++++++++++-------- tools/perf/util/dwarf-aux.c | 2 +- tools/perf/util/dwarf-aux.h | 2 ++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index f125ac5f0bda..c18272092a6b 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -217,8 +217,13 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) strbuf_init(&sb, 32); die_get_typename(die, &sb); - die_get_real_type(die, &member_type); - if (dwarf_aggregate_size(&member_type, &size) < 0) + __die_get_real_type(die, &member_type); + if (dwarf_tag(&member_type) == DW_TAG_typedef) + die_get_real_type(&member_type, &die_mem); + else + die_mem = member_type; + + if (dwarf_aggregate_size(&die_mem, &size) < 0) size = 0; if (!dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) @@ -235,11 +240,11 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) INIT_LIST_HEAD(&member->children); list_add_tail(&member->node, &parent->children); - tag = dwarf_tag(&member_type); + tag = dwarf_tag(&die_mem); switch (tag) { case DW_TAG_structure_type: case DW_TAG_union_type: - die_find_child(&member_type, __add_member_cb, member, &die_mem); + die_find_child(&die_mem, __add_member_cb, member, &die_mem); break; default: break; @@ -281,6 +286,10 @@ static struct annotated_data_type *dso__findnew_data_type(struct dso *dso, if (die_get_typename_from_type(type_die, &sb) < 0) strbuf_add(&sb, "(unknown type)", 14); type_name = strbuf_detach(&sb, NULL); + + if (dwarf_tag(type_die) == DW_TAG_typedef) + die_get_real_type(type_die, type_die); + dwarf_aggregate_size(type_die, &size); /* Check existing nodes in dso->data_types tree */ @@ -342,6 +351,7 @@ static int check_variable(struct data_loc_info *dloc, Dwarf_Die *var_die, { Dwarf_Word size; bool is_pointer = true; + Dwarf_Die sized_type; if (reg == DWARF_REG_PC) is_pointer = false; @@ -351,7 +361,7 @@ static int check_variable(struct data_loc_info *dloc, Dwarf_Die *var_die, is_pointer = false; /* Get the type of the variable */ - if (die_get_real_type(var_die, type_die) == NULL) { + if (__die_get_real_type(var_die, type_die) == NULL) { pr_debug_dtp("variable has no type\n"); ann_data_stat.no_typeinfo++; return -1; @@ -365,15 +375,20 @@ static int check_variable(struct data_loc_info *dloc, Dwarf_Die *var_die, if (is_pointer) { if ((dwarf_tag(type_die) != DW_TAG_pointer_type && dwarf_tag(type_die) != DW_TAG_array_type) || - die_get_real_type(type_die, type_die) == NULL) { + __die_get_real_type(type_die, type_die) == NULL) { pr_debug_dtp("no pointer or no type\n"); ann_data_stat.no_typeinfo++; return -1; } } + if (dwarf_tag(type_die) == DW_TAG_typedef) + die_get_real_type(type_die, &sized_type); + else + sized_type = *type_die; + /* Get the size of the actual type */ - if (dwarf_aggregate_size(type_die, &size) < 0) { + if (dwarf_aggregate_size(&sized_type, &size) < 0) { pr_debug_dtp("type size is unknown\n"); ann_data_stat.invalid_size++; return -1; @@ -846,6 +861,7 @@ static int check_matching_type(struct type_state *state, if (state->regs[reg].ok && state->regs[reg].kind == TSR_KIND_TYPE) { int tag = dwarf_tag(&state->regs[reg].type); + Dwarf_Die sized_type; /* * Normal registers should hold a pointer (or array) to @@ -862,13 +878,18 @@ static int check_matching_type(struct type_state *state, pr_debug_dtp("\n"); /* Remove the pointer and get the target type */ - if (die_get_real_type(&state->regs[reg].type, type_die) == NULL) + if (__die_get_real_type(&state->regs[reg].type, type_die) == NULL) return -1; dloc->type_offset = dloc->op->offset; + if (dwarf_tag(type_die) == DW_TAG_typedef) + die_get_real_type(type_die, &sized_type); + else + sized_type = *type_die; + /* Get the size of the actual type */ - if (dwarf_aggregate_size(type_die, &size) < 0 || + if (dwarf_aggregate_size(&sized_type, &size) < 0 || (unsigned)dloc->type_offset >= size) return -1; diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 44ef968a7ad3..5e080d7e22c2 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -267,7 +267,7 @@ Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) } /* Get a type die, but skip qualifiers */ -static Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) +Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem) { int tag; diff --git a/tools/perf/util/dwarf-aux.h b/tools/perf/util/dwarf-aux.h index 24446412b869..336a3a183a78 100644 --- a/tools/perf/util/dwarf-aux.h +++ b/tools/perf/util/dwarf-aux.h @@ -56,6 +56,8 @@ const char *die_get_decl_file(Dwarf_Die *dw_die); /* Get type die */ Dwarf_Die *die_get_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); +/* Get a type die, but skip qualifiers */ +Dwarf_Die *__die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); /* Get a type die, but skip qualifiers and typedef */ Dwarf_Die *die_get_real_type(Dwarf_Die *vr_die, Dwarf_Die *die_mem); From ed5bb548cc648de6dbb5894cf95955998e466589 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 7 Aug 2024 21:49:54 -0700 Subject: [PATCH 092/347] perf test: Add a new shell test for perf ftrace $ sudo ./perf test ftrace -vv 86: perf ftrace tests: --- start --- test child forked, pid 1772223 perf ftrace list test syscalls for sleep: __x64_sys_nanosleep __ia32_sys_nanosleep __x64_sys_clock_nanosleep __ia32_sys_clock_nanosleep perf ftrace list test [Success] perf ftrace trace test # tracer: function_graph # # CPU DURATION FUNCTION CALLS # | | | | | | | 0) | __x64_sys_clock_nanosleep() { 0) | common_nsleep() { 0) | hrtimer_nanosleep() { 0) | do_nanosleep() { perf ftrace trace test [Success] perf ftrace latency test target function: __x64_sys_clock_nanosleep # DURATION | COUNT | GRAPH | 32 - 64 ms | 1 | ############################################## | perf ftrace latency test [Success] perf ftrace profile test # Total (us) Avg (us) Max (us) Count Function 100136.400 100136.400 100136.400 1 __x64_sys_clock_nanosleep 100135.200 100135.200 100135.200 1 common_nsleep 100134.700 100134.700 100134.700 1 hrtimer_nanosleep 100133.700 100133.700 100133.700 1 do_nanosleep 100130.600 100130.600 100130.600 1 schedule 166.868 55.623 80.299 3 scheduler_tick 5.926 5.926 5.926 1 native_smp_send_reschedule 301.941 301.941 301.941 1 __x64_sys_execve 295.786 295.786 295.786 1 do_execveat_common.isra.0 71.397 35.699 46.403 2 bprm_execve 2.519 1.260 1.547 2 sched_mm_cid_before_execve 1.098 0.549 0.686 2 sched_mm_cid_after_execve perf ftrace profile test [Success] ---- end(0) ---- 86: perf ftrace tests : Ok Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Steven Rostedt (VMware) Link: https://lore.kernel.org/r/20240808044954.1775333-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/ftrace.sh | 84 ++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100755 tools/perf/tests/shell/ftrace.sh diff --git a/tools/perf/tests/shell/ftrace.sh b/tools/perf/tests/shell/ftrace.sh new file mode 100755 index 000000000000..b1c36d30559a --- /dev/null +++ b/tools/perf/tests/shell/ftrace.sh @@ -0,0 +1,84 @@ +#!/bin/sh +# perf ftrace tests +# SPDX-License-Identifier: GPL-2.0 + +set -e + +# perf ftrace commands only works for root +if [ "$(id -u)" != 0 ]; then + echo "perf ftrace test [Skipped: no permission]" + exit 2 +fi + +output=$(mktemp /tmp/__perf_test.ftrace.XXXXXX) + +cleanup() { + rm -f "${output}" + + trap - EXIT TERM INT +} + +trap_cleanup() { + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + +# this will be set in test_ftrace_trace() +target_function= + +test_ftrace_list() { + echo "perf ftrace list test" + perf ftrace -F > "${output}" + # this will be used in test_ftrace_trace() + sleep_functions=$(grep 'sys_.*sleep$' "${output}") + echo "syscalls for sleep:" + echo "${sleep_functions}" + echo "perf ftrace list test [Success]" +} + +test_ftrace_trace() { + echo "perf ftrace trace test" + perf ftrace trace --graph-opts depth=5 sleep 0.1 > "${output}" + # it should have some function name contains 'sleep' + grep "^#" "${output}" + grep -F 'sleep()' "${output}" + # find actual syscall function name + for FN in ${sleep_functions}; do + if grep -q "${FN}" "${output}"; then + target_function="${FN}" + echo "perf ftrace trace test [Success]" + return + fi + done + + echo "perf ftrace trace test [Failure: sleep syscall not found]" + exit 1 +} + +test_ftrace_latency() { + echo "perf ftrace latency test" + echo "target function: ${target_function}" + perf ftrace latency -T "${target_function}" sleep 0.1 > "${output}" + grep "^#" "${output}" + grep "###" "${output}" + echo "perf ftrace latency test [Success]" +} + +test_ftrace_profile() { + echo "perf ftrace profile test" + perf ftrace profile sleep 0.1 > "${output}" + grep ^# "${output}" + grep sleep "${output}" + grep schedule "${output}" + grep execve "${output}" + echo "perf ftrace profile test [Success]" +} + +test_ftrace_list +test_ftrace_trace +test_ftrace_latency +test_ftrace_profile + +cleanup +exit 0 From 2df5484bbf2f1fa3efa2b6385efc1350d89b7783 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 8 Aug 2024 09:59:40 -0300 Subject: [PATCH 093/347] perf tests ftrace: Add pattern check for time, count In 'perf ftrace profile sleep 0.1' we know that we'll have an specific kernel function that will take a bit more than 0.1 seconds and will take place just one time, so we can add a check for that so that we validate more than just the presence of some functions in the profile. Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Steven Rostedt (VMware) Link: https://lore.kernel.org/lkml/ZrTBo7KACZeuCyLj@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/ftrace.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/perf/tests/shell/ftrace.sh b/tools/perf/tests/shell/ftrace.sh index b1c36d30559a..a6ee740f0d7e 100755 --- a/tools/perf/tests/shell/ftrace.sh +++ b/tools/perf/tests/shell/ftrace.sh @@ -72,6 +72,11 @@ test_ftrace_profile() { grep sleep "${output}" grep schedule "${output}" grep execve "${output}" + time_re="[[:space:]]+10[[:digit:]]{4}\.[[:digit:]]{3}" + # 100283.000 100283.000 100283.000 1 __x64_sys_clock_nanosleep + # Check for one *clock_nanosleep line with a Count of just 1 that takes a bit more than 0.1 seconds + # Strip the _x64_sys part to work with other architectures + grep -E "^${time_re}${time_re}${time_re}[[:space:]]+1[[:space:]]+.*clock_nanosleep" "${output}" echo "perf ftrace profile test [Success]" } From 37e2a19c98bf99747ca997be876dfc13f9165e0a Mon Sep 17 00:00:00 2001 From: Veronika Molnarova Date: Thu, 8 Aug 2024 12:37:49 +0200 Subject: [PATCH 094/347] perf test pmu: Set uninitialized PMU alias to null Commit 3e0bf9fde2984469 ("perf pmu: Restore full PMU name wildcard support") adds a test case "PMU cmdline match" that covers PMU name wildcard support provided by function perf_pmu__match(). The test works with a wide range of supported combinations of PMU name matching but omits the case that if the perf_pmu__match() cannot match the PMU name to the wildcard, it tries to match its alias. However, this variable is not set up, causing the test case to fail when run with subprocesses or to segfault if run as a single process. ./perf test -vv 9 9: Sysfs PMU tests : 9.1: Parsing with PMU format directory : Ok 9.2: Parsing with PMU event : Ok 9.3: PMU event names : Ok 9.4: PMU name combining : Ok 9.5: PMU name comparison : Ok 9.6: PMU cmdline match : FAILED! ./perf test -F 9 9.1: Parsing with PMU format directory : Ok 9.2: Parsing with PMU event : Ok 9.3: PMU event names : Ok 9.4: PMU name combining : Ok 9.5: PMU name comparison : Ok Segmentation fault (core dumped) Initialize the PMU alias to null for all tests of perf_pmu__match() as this functionality is not being tested and the alias matching works exactly the same as the matching of the PMU name. ./perf test -F 9 9.1: Parsing with PMU format directory : Ok 9.2: Parsing with PMU event : Ok 9.3: PMU event names : Ok 9.4: PMU name combining : Ok 9.5: PMU name comparison : Ok 9.6: PMU cmdline match : Ok Fixes: 3e0bf9fde2984469 ("perf pmu: Restore full PMU name wildcard support") Signed-off-by: Veronika Molnarova Cc: James Clark Cc: Michael Petlan Cc: Namhyung Kim Cc: Radostin Stoyanov Link: https://lore.kernel.org/r/20240808103749.9356-1-vmolnaro@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/pmu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c index 0b2f04a55d7b..a4730b5dc0d9 100644 --- a/tools/perf/tests/pmu.c +++ b/tools/perf/tests/pmu.c @@ -453,11 +453,13 @@ static int test__name_cmp(struct test_suite *test __maybe_unused, int subtest __ /** * Test perf_pmu__match() that's used to search for a PMU given a name passed * on the command line. The name that's passed may also be a filename type glob - * match. + * match. If the name does not match, perf_pmu__match() attempts to match the + * alias of the PMU, if provided. */ static int test__pmu_match(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { struct perf_pmu test_pmu; + test_pmu.alias_name = NULL; test_pmu.name = "pmuname"; TEST_ASSERT_EQUAL("Exact match", perf_pmu__match(&test_pmu, "pmuname"), true); From 599c19397b17d197fc1184bbc950f163a292efc9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 7 Aug 2024 22:46:43 -0700 Subject: [PATCH 095/347] perf callchain: Fix stitch LBR memory leaks The 'struct callchain_cursor_node' has a 'struct map_symbol' whose maps and map members are reference counted. Ensure these values use a _get routine to increment the reference counts and use map_symbol__exit() to release the reference counts. Do similar for 'struct thread's prev_lbr_cursor, but save the size of the prev_lbr_cursor array so that it may be iterated. Ensure that when stitch_nodes are placed on the free list the map_symbols are exited. Fix resolve_lbr_callchain_sample() by replacing list_replace_init() to list_splice_init(), so the whole list is moved and nodes aren't leaked. A reproduction of the memory leaks is possible with a leak sanitizer build in the perf report command of: ``` $ perf record -e cycles --call-graph lbr perf test -w thloop $ perf report --stitch-lbr ``` Reviewed-by: Kan Liang Fixes: ff165628d72644e3 ("perf callchain: Stitch LBR call stack") Signed-off-by: Ian Rogers [ Basic tests after applying the patch, repeating the example above ] Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andi Kleen Cc: Anne Macedo Cc: Changbin Du Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240808054644.1286065-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/machine.c | 17 +++++++++++++++-- tools/perf/util/thread.c | 4 ++++ tools/perf/util/thread.h | 1 + 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 8477edefc299..706be5e4a076 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -2270,8 +2270,12 @@ static void save_lbr_cursor_node(struct thread *thread, cursor->curr = cursor->first; else cursor->curr = cursor->curr->next; + + map_symbol__exit(&lbr_stitch->prev_lbr_cursor[idx].ms); memcpy(&lbr_stitch->prev_lbr_cursor[idx], cursor->curr, sizeof(struct callchain_cursor_node)); + lbr_stitch->prev_lbr_cursor[idx].ms.maps = maps__get(cursor->curr->ms.maps); + lbr_stitch->prev_lbr_cursor[idx].ms.map = map__get(cursor->curr->ms.map); lbr_stitch->prev_lbr_cursor[idx].valid = true; cursor->pos++; @@ -2482,6 +2486,9 @@ static bool has_stitched_lbr(struct thread *thread, memcpy(&stitch_node->cursor, &lbr_stitch->prev_lbr_cursor[i], sizeof(struct callchain_cursor_node)); + stitch_node->cursor.ms.maps = maps__get(lbr_stitch->prev_lbr_cursor[i].ms.maps); + stitch_node->cursor.ms.map = map__get(lbr_stitch->prev_lbr_cursor[i].ms.map); + if (callee) list_add(&stitch_node->node, &lbr_stitch->lists); else @@ -2505,6 +2512,8 @@ static bool alloc_lbr_stitch(struct thread *thread, unsigned int max_lbr) if (!thread__lbr_stitch(thread)->prev_lbr_cursor) goto free_lbr_stitch; + thread__lbr_stitch(thread)->prev_lbr_cursor_size = max_lbr + 1; + INIT_LIST_HEAD(&thread__lbr_stitch(thread)->lists); INIT_LIST_HEAD(&thread__lbr_stitch(thread)->free_lists); @@ -2560,8 +2569,12 @@ static int resolve_lbr_callchain_sample(struct thread *thread, max_lbr, callee); if (!stitched_lbr && !list_empty(&lbr_stitch->lists)) { - list_replace_init(&lbr_stitch->lists, - &lbr_stitch->free_lists); + struct stitch_list *stitch_node; + + list_for_each_entry(stitch_node, &lbr_stitch->lists, node) + map_symbol__exit(&stitch_node->cursor.ms); + + list_splice_init(&lbr_stitch->lists, &lbr_stitch->free_lists); } memcpy(&lbr_stitch->prev_sample, sample, sizeof(*sample)); } diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 87c59aa9fe38..0ffdd52d86d7 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -476,6 +476,7 @@ void thread__free_stitch_list(struct thread *thread) return; list_for_each_entry_safe(pos, tmp, &lbr_stitch->lists, node) { + map_symbol__exit(&pos->cursor.ms); list_del_init(&pos->node); free(pos); } @@ -485,6 +486,9 @@ void thread__free_stitch_list(struct thread *thread) free(pos); } + for (unsigned int i = 0 ; i < lbr_stitch->prev_lbr_cursor_size; i++) + map_symbol__exit(&lbr_stitch->prev_lbr_cursor[i].ms); + zfree(&lbr_stitch->prev_lbr_cursor); free(thread__lbr_stitch(thread)); thread__set_lbr_stitch(thread, NULL); diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 8b4a3c69bad1..6cbf6eb2812e 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -26,6 +26,7 @@ struct lbr_stitch { struct list_head free_lists; struct perf_sample prev_sample; struct callchain_cursor_node *prev_lbr_cursor; + unsigned int prev_lbr_cursor_size; }; DECLARE_RC_STRUCT(thread) { From 32559b99e0f590700dcc2522d97bc637979bcf61 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 7 Aug 2024 22:46:44 -0700 Subject: [PATCH 096/347] perf test: Add set of perf record LBR tests Adds coverage for LBR operations and LBR callgraph. Reviewed-by: Kan Liang Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andi Kleen Cc: Anne Macedo Cc: Changbin Du Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240808054644.1286065-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/record_lbr.sh | 161 +++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100755 tools/perf/tests/shell/record_lbr.sh diff --git a/tools/perf/tests/shell/record_lbr.sh b/tools/perf/tests/shell/record_lbr.sh new file mode 100755 index 000000000000..baf168d0ddbb --- /dev/null +++ b/tools/perf/tests/shell/record_lbr.sh @@ -0,0 +1,161 @@ +#!/bin/bash +# perf record LBR tests +# SPDX-License-Identifier: GPL-2.0 + +set -e + +if [ ! -f /sys/devices/cpu/caps/branches ] +then + echo "Skip: only x86 CPUs support LBR" + exit 2 +fi + +err=0 +perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) + +cleanup() { + rm -rf "${perfdata}" + rm -rf "${perfdata}".old + rm -rf "${perfdata}".txt + + trap - EXIT TERM INT +} + +trap_cleanup() { + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT + + +lbr_callgraph_test() { + test="LBR callgraph" + + echo "$test" + if ! perf record -e cycles --call-graph lbr -o "${perfdata}" perf test -w thloop + then + echo "$test [Failed support missing]" + if [ $err -eq 0 ] + then + err=2 + fi + return + fi + + if ! perf report --stitch-lbr -i "${perfdata}" > "${perfdata}".txt + then + cat "${perfdata}".txt + echo "$test [Failed in perf report]" + err=1 + return + fi + + echo "$test [Success]" +} + +lbr_test() { + local branch_flags=$1 + local test="LBR $2 test" + local threshold=$3 + local out + local sam_nr + local bs_nr + local zero_nr + local r + + echo "$test" + if ! perf record -e cycles $branch_flags -o "${perfdata}" perf test -w thloop + then + echo "$test [Failed support missing]" + perf record -e cycles $branch_flags -o "${perfdata}" perf test -w thloop || true + if [ $err -eq 0 ] + then + err=2 + fi + return + fi + + out=$(perf report -D -i "${perfdata}" 2> /dev/null | grep -A1 'PERF_RECORD_SAMPLE') + sam_nr=$(echo "$out" | grep -c 'PERF_RECORD_SAMPLE' || true) + if [ $sam_nr -eq 0 ] + then + echo "$test [Failed no samples captured]" + err=1 + return + fi + echo "$test: $sam_nr samples" + + bs_nr=$(echo "$out" | grep -c 'branch stack: nr:' || true) + if [ $sam_nr -ne $bs_nr ] + then + echo "$test [Failed samples missing branch stacks]" + err=1 + return + fi + + zero_nr=$(echo "$out" | grep -c 'branch stack: nr:0' || true) + r=$(($zero_nr * 100 / $bs_nr)) + if [ $r -gt $threshold ]; then + echo "$test [Failed empty br stack ratio exceed $threshold%: $r%]" + err=1 + return + fi + + echo "$test [Success]" +} + +parallel_lbr_test() { + err=0 + perfdata=$(mktemp /tmp/__perf_test.perf.data.XXXXX) + lbr_test "$1" "$2" "$3" + cleanup + exit $err +} + +lbr_callgraph_test + +# Sequential +lbr_test "-b" "any branch" 2 +lbr_test "-j any_call" "any call" 2 +lbr_test "-j any_ret" "any ret" 2 +lbr_test "-j ind_call" "any indirect call" 2 +lbr_test "-j ind_jmp" "any indirect jump" 100 +lbr_test "-j call" "direct calls" 2 +lbr_test "-j ind_call,u" "any indirect user call" 100 +lbr_test "-a -b" "system wide any branch" 2 +lbr_test "-a -j any_call" "system wide any call" 2 + +# Parallel +parallel_lbr_test "-b" "parallel any branch" 100 & +pid1=$! +parallel_lbr_test "-j any_call" "parallel any call" 100 & +pid2=$! +parallel_lbr_test "-j any_ret" "parallel any ret" 100 & +pid3=$! +parallel_lbr_test "-j ind_call" "parallel any indirect call" 100 & +pid4=$! +parallel_lbr_test "-j ind_jmp" "parallel any indirect jump" 100 & +pid5=$! +parallel_lbr_test "-j call" "parallel direct calls" 100 & +pid6=$! +parallel_lbr_test "-j ind_call,u" "parallel any indirect user call" 100 & +pid7=$! +parallel_lbr_test "-a -b" "parallel system wide any branch" 100 & +pid8=$! +parallel_lbr_test "-a -j any_call" "parallel system wide any call" 100 & +pid9=$! + +for pid in $pid1 $pid2 $pid3 $pid4 $pid5 $pid6 $pid7 $pid8 $pid9 +do + set +e + wait $pid + child_err=$? + set -e + if ([ $err -eq 2 ] && [ $child_err -eq 1 ]) || [ $err -eq 0 ] + then + err=$child_err + fi +done + +cleanup +exit $err From 9e9d0a79d34716f8e2590ccab0eee08f4fdfc7f4 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 8 Aug 2024 11:26:13 -0300 Subject: [PATCH 097/347] perf test shell lbr: Support hybrid x86 systems too Running on a: root@x1:~# grep 'model name' -m1 /proc/cpuinfo model name : 13th Gen Intel(R) Core(TM) i7-1365U root@x1:~# It skips all the tests with: root@x1:~# perf test -vvvv LBR 97: perf record LBR tests: --- start --- test child forked, pid 2033388 Skip: only x86 CPUs support LBR ---- end(-2) ---- 97: perf record LBR tests : Skip root@x1:~# Because the test checks for the /sys/devices/cpu/caps/branches file, that isn't present as we have instead: root@x1:~# ls -la /sys/devices/cpu*/caps/branches -r--r--r--. 1 root root 4096 Aug 8 11:22 /sys/devices/cpu_atom/caps/branches -r--r--r--. 1 root root 4096 Aug 8 11:21 /sys/devices/cpu_core/caps/branches root@x1:~# If we check as well for one of those, /sys/devices/cpu_core/caps/branches, then we don't skip the tests and all are run on these x86 Intel Hybrid systems as well, passing all of them: root@x1:~# perf test -vvvv LBR 97: perf record LBR tests: --- start --- test child forked, pid 2034956 LBR callgraph [ perf record: Woken up 5 times to write data ] [ perf record: Captured and wrote 1.812 MB /tmp/__perf_test.perf.data.B2HvQ (8114 samples) ] LBR callgraph [Success] LBR any branch test [ perf record: Woken up 25 times to write data ] [ perf record: Captured and wrote 6.382 MB /tmp/__perf_test.perf.data.B2HvQ (8071 samples) ] LBR any branch test: 8071 samples LBR any branch test [Success] LBR any call test [ perf record: Woken up 23 times to write data ] [ perf record: Captured and wrote 6.208 MB /tmp/__perf_test.perf.data.B2HvQ (8092 samples) ] LBR any call test: 8092 samples LBR any call test [Success] LBR any ret test [ perf record: Woken up 24 times to write data ] [ perf record: Captured and wrote 6.396 MB /tmp/__perf_test.perf.data.B2HvQ (8093 samples) ] LBR any ret test: 8093 samples LBR any ret test [Success] LBR any indirect call test [ perf record: Woken up 25 times to write data ] [ perf record: Captured and wrote 6.344 MB /tmp/__perf_test.perf.data.B2HvQ (8067 samples) ] LBR any indirect call test: 8067 samples LBR any indirect call test [Success] LBR any indirect jump test [ perf record: Woken up 12 times to write data ] [ perf record: Captured and wrote 3.073 MB /tmp/__perf_test.perf.data.B2HvQ (8061 samples) ] LBR any indirect jump test: 8061 samples LBR any indirect jump test [Success] LBR direct calls test [ perf record: Woken up 25 times to write data ] [ perf record: Captured and wrote 6.380 MB /tmp/__perf_test.perf.data.B2HvQ (8076 samples) ] LBR direct calls test: 8076 samples LBR direct calls test [Success] LBR any indirect user call test [ perf record: Woken up 5 times to write data ] [ perf record: Captured and wrote 1.597 MB /tmp/__perf_test.perf.data.B2HvQ (8079 samples) ] LBR any indirect user call test: 8079 samples LBR any indirect user call test [Success] LBR system wide any branch test [ perf record: Woken up 26 times to write data ] [ perf record: Captured and wrote 9.088 MB /tmp/__perf_test.perf.data.B2HvQ (9209 samples) ] LBR system wide any branch test: 9209 samples LBR system wide any branch test [Success] LBR system wide any call test [ perf record: Woken up 25 times to write data ] [ perf record: Captured and wrote 8.945 MB /tmp/__perf_test.perf.data.B2HvQ (9333 samples) ] LBR system wide any call test: 9333 samples LBR system wide any call test [Success] LBR parallel any branch test LBR parallel any call test LBR parallel any ret test LBR parallel any indirect call test LBR parallel any indirect jump test LBR parallel direct calls test LBR parallel system wide any branch test LBR parallel any indirect user call test LBR parallel system wide any call test [ perf record: Woken up 9 times to write data ] [ perf record: Woken up 51 times to write data ] [ perf record: Woken up 1 times to write data ] [ perf record: Woken up 5 times to write data ] [ perf record: Woken up 559 times to write data ] [ perf record: Woken up 14 times to write data ] [ perf record: Woken up 17 times to write data ] [ perf record: Woken up 1 times to write data ] [ perf record: Woken up 11 times to write data ] [ perf record: Captured and wrote 0.150 MB /tmp/__perf_test.perf.data.lANpR (1909 samples) ] [ perf record: Captured and wrote 2.371 MB /tmp/__perf_test.perf.data.Olum8 (3033 samples) ] [ perf record: Captured and wrote 1.230 MB /tmp/__perf_test.perf.data.njfJ8 (1742 samples) ] [ perf record: Captured and wrote 5.554 MB /tmp/__perf_test.perf.data.4ZTrj (29662 samples) ] [ perf record: Captured and wrote 19.906 MB /tmp/__perf_test.perf.data.dlGQt (29576 samples) ] [ perf record: Captured and wrote 0.289 MB /tmp/__perf_test.perf.data.CAT7y (4311 samples) ] [ perf record: Captured and wrote 3.129 MB /tmp/__perf_test.perf.data.diuKG (3971 samples) ] LBR parallel any indirect user call test: 1909 samples [ perf record: Captured and wrote 4.858 MB /tmp/__perf_test.perf.data.sVjtN (6130 samples) ] LBR parallel any indirect user call test [Success] [ perf record: Captured and wrote 3.669 MB /tmp/__perf_test.perf.data.AJtNI (4827 samples) ] LBR parallel any indirect jump test: 4311 samples LBR parallel any indirect jump test [Success] LBR parallel direct calls test: 3033 samples LBR parallel direct calls test [Success] LBR parallel any indirect call test: 1742 samples LBR parallel any indirect call test [Success] LBR parallel any call test: 4827 samples LBR parallel any call test [Success] LBR parallel any branch test: 6130 samples LBR parallel any branch test [Success] LBR parallel system wide any branch test: 29662 samples LBR parallel any ret test: 3971 samples LBR parallel any ret test [Success] LBR parallel system wide any branch test [Success] LBR parallel system wide any call test: 29576 samples LBR parallel system wide any call test [Success] ---- end(0) ---- 97: perf record LBR tests : Ok root@x1:~# Reviewed-by: Kan Liang Acked-by: Ian Rogers Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/ZrTXftup0H46R8WK@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/record_lbr.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/tests/shell/record_lbr.sh b/tools/perf/tests/shell/record_lbr.sh index baf168d0ddbb..32314641217e 100755 --- a/tools/perf/tests/shell/record_lbr.sh +++ b/tools/perf/tests/shell/record_lbr.sh @@ -4,7 +4,7 @@ set -e -if [ ! -f /sys/devices/cpu/caps/branches ] +if [ ! -f /sys/devices/cpu/caps/branches ] && [ ! -f /sys/devices/cpu_core/caps/branches ] then echo "Skip: only x86 CPUs support LBR" exit 2 From 05673c42f73965c6381ab986b4dd2145700e6a0c Mon Sep 17 00:00:00 2001 From: Zixian Cai Date: Fri, 9 Aug 2024 08:01:36 +0000 Subject: [PATCH 098/347] perf script python: Add the 'ins_lat' field to event handler For example, when using the Alder Lake PMU memory load event, the instruction latency is stored in 'ins_lat', while the cache latency is stored in 'weight'. This patch reports the 'ins_lat' field for Python scripting. Committer testing: On a Rocket Lake Refresh Intel machine (14th gen): root@number:~# grep -m1 'model name' /proc/cpuinfo model name : Intel(R) Core(TM) i7-14700K root@number:~# perf mem record -a sleep 5 Memory events are enabled on a subset of CPUs: 16-27 [ perf record: Woken up 85 times to write data ] [ perf record: Captured and wrote 41.236 MB perf.data (191390 samples) ] root@number:~# perf evlist -v cpu_atom/mem-loads,ldlat=30/P: type: 10 (cpu_atom), size: 136, config: 0x5d0 (mem-loads), { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ADDR|CPU|PERIOD|IDENTIFIER|DATA_SRC|WEIGHT_STRUCT, read_format: ID|LOST, disabled: 1, inherit: 1, freq: 1, precise_ip: 3, sample_id_all: 1, { bp_addr, config1 }: 0x1f cpu_atom/mem-stores/P: type: 10 (cpu_atom), size: 136, config: 0x6d0 (mem-stores), { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ADDR|CPU|PERIOD|IDENTIFIER|DATA_SRC|WEIGHT_STRUCT, read_format: ID|LOST, disabled: 1, inherit: 1, freq: 1, precise_ip: 3, sample_id_all: 1 dummy:u: type: 1 (software), size: 136, config: 0x9 (PERF_COUNT_SW_DUMMY), { sample_period, sample_freq }: 1, sample_type: IP|TID|TIME|ADDR|CPU|IDENTIFIER|DATA_SRC|WEIGHT_STRUCT, read_format: ID|LOST, inherit: 1, exclude_kernel: 1, exclude_hv: 1, mmap: 1, comm: 1, task: 1, mmap_data: 1, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 root@number:~# Now generate a python script to then dump the dictionary that now needs to have that 'ins_lat' field: root@number:~# perf script --gen python generated Python script: perf-script.py root@number:~# vim perf-script.py root@number:~# perf script -s perf-script.py | head -40 in trace_begin in trace_end root@number:~# vim perf-script.py Reviewed-by: Adrian Hunter Signed-off-by: Zixian Cai Cc: Alexander Shishkin Cc: Ben Gainey Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Paran Lee Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240809080137.3590148-1-fzczx123@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/scripting-engines/trace-event-python.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index fb00f3ad6815..6971dd6c231f 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -888,6 +888,8 @@ static PyObject *get_perf_sample_dict(struct perf_sample *sample, set_sample_read_in_dict(dict_sample, sample, evsel); pydict_set_item_string_decref(dict_sample, "weight", PyLong_FromUnsignedLongLong(sample->weight)); + pydict_set_item_string_decref(dict_sample, "ins_lat", + PyLong_FromUnsignedLong(sample->ins_lat)); pydict_set_item_string_decref(dict_sample, "transaction", PyLong_FromUnsignedLongLong(sample->transaction)); set_sample_datasrc_in_dict(dict_sample, sample); @@ -1317,7 +1319,7 @@ static void python_export_sample_table(struct db_export *dbe, struct tables *tables = container_of(dbe, struct tables, dbe); PyObject *t; - t = tuple_new(27); + t = tuple_new(28); tuple_set_d64(t, 0, es->db_id); tuple_set_d64(t, 1, es->evsel->db_id); @@ -1346,6 +1348,7 @@ static void python_export_sample_table(struct db_export *dbe, tuple_set_s32(t, 24, es->sample->flags); tuple_set_d64(t, 25, es->sample->id); tuple_set_d64(t, 26, es->sample->stream_id); + tuple_set_u32(t, 27, es->sample->ins_lat); call_object(tables->sample_handler, t, "sample_table"); From 13d675aea6cac5bb4619ef9e3fdd90129fe6d139 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 9 Aug 2024 11:32:45 -0300 Subject: [PATCH 099/347] perf debuginfo: Fix the build with !HAVE_DWARF_SUPPORT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In that case we have a set of placeholder functions, one of them uses a 'Dwarf_Addr' type that is not present as it is defined in the missing DWARF libraries, so provide a placeholder typedef for that as well. The build error before this patch: In file included from util/annotate.c:28: util/debuginfo.h:44:46: error: unknown type name ‘Dwarf_Addr’ 44 | Dwarf_Addr *offs __maybe_unused, | ^~~~~~~~~~ make[6]: *** [/home/acme/git/perf-tools-next/tools/build/Makefile.build:106: util/annotate.o] Error 1 make[6]: *** Waiting for unfinished jobs.... Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Link: https://lore.kernel.org/lkml/CAM9d7ciushSwEfj7yW4rtDEJBTcCB991V4cswwFEL+cv6QF2pg@mail.gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/debuginfo.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/util/debuginfo.h b/tools/perf/util/debuginfo.h index 4d65b8c605fc..ad6422c3f8ca 100644 --- a/tools/perf/util/debuginfo.h +++ b/tools/perf/util/debuginfo.h @@ -40,6 +40,8 @@ static inline void debuginfo__delete(struct debuginfo *dbg __maybe_unused) { } +typedef void Dwarf_Addr; + static inline int debuginfo__get_text_offset(struct debuginfo *dbg __maybe_unused, Dwarf_Addr *offs __maybe_unused, bool adjust_offset __maybe_unused) From 890a1961c812db801e0f9dfaa4af233aa3c6ab63 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Wed, 7 Aug 2024 16:18:20 -0700 Subject: [PATCH 100/347] perf tools: Create source symlink in perf object dir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a source symlink to the original source in the objdir. This is similar to what the main kernel build script does. Committer testing: ⬢[acme@toolbox perf-tools-next]$ make O=/tmp/build/$(basename $PWD)/ -C tools/perf install-bin ⬢[acme@toolbox perf-tools-next]$ ls -la /tmp/build/perf-tools-next/source lrwxrwxrwx. 1 acme acme 41 Aug 9 16:26 /tmp/build/perf-tools-next/source -> /home/acme/git/perf-tools-next/tools/perf ⬢[acme@toolbox perf-tools-next]$ Signed-off-by: Andi Kleen Tested-by: Arnaldo Carvalho de Melo Acked-by: Ian Rogers Acked-by: Namhyung Kim Link: https://lore.kernel.org/r/20240807231823.898979-1-ak@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 72534bb72d43..9731f5e84131 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -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) From 336989d00f2140c7524b76126aa62c8a47a5a1d3 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 6 Aug 2024 23:15:55 -0700 Subject: [PATCH 101/347] perf annotate: Fix --group behavior when leader has no samples MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When --group option is used, it should display all events together. But the current logic only checks if the first (leader) event has samples or not. Let's check the member events as well. Also it missed to put the linked samples from member evsels to the output RB-tree so that it can be displayed in the output. For example, take a look at this example. $ ./perf evlist cpu/mem-loads,ldlat=30/P cpu/mem-stores/P dummy:u It has three events but 'path_put' function has samples only for mem-stores (second) event. $ sudo ./perf annotate --stdio -f path_put Percent | Source code & Disassembly of kcore for cpu/mem-stores/P (2 samples, percent: local period) ---------------------------------------------------------------------------------------------------------- : 0 0xffffffffae600020 : 0.00 : ffffffffae600020: endbr64 0.00 : ffffffffae600024: nopl (%rax, %rax) 91.22 : ffffffffae600029: pushq %rbx 0.00 : ffffffffae60002a: movq %rdi, %rbx 0.00 : ffffffffae60002d: movq 8(%rdi), %rdi 8.78 : ffffffffae600031: callq 0xffffffffae614aa0 0.00 : ffffffffae600036: movq (%rbx), %rdi 0.00 : ffffffffae600039: popq %rbx 0.00 : ffffffffae60003a: jmp 0xffffffffae620670 0.00 : ffffffffae60003f: nop Therefore, it didn't show up when --group option is used since the leader ("mem-loads") event has no samples. But now it checks both events. Before: $ sudo ./perf annotate --stdio -f --group path_put (no output) After: $ sudo ./perf annotate --stdio -f --group path_put Percent | Source code & Disassembly of kcore for cpu/mem-loads,ldlat=30/P, cpu/mem-stores/P, dummy:u (0 samples, percent: local period) ------------------------------------------------------------------------------------------------------------------------------------------------------------- : 0 0xffffffffae600020 : 0.00 0.00 0.00 : ffffffffae600020: endbr64 0.00 0.00 0.00 : ffffffffae600024: nopl (%rax, %rax) 0.00 91.22 0.00 : ffffffffae600029: pushq %rbx 0.00 0.00 0.00 : ffffffffae60002a: movq %rdi, %rbx 0.00 0.00 0.00 : ffffffffae60002d: movq 8(%rdi), %rdi 0.00 8.78 0.00 : ffffffffae600031: callq 0xffffffffae614aa0 0.00 0.00 0.00 : ffffffffae600036: movq (%rbx), %rdi 0.00 0.00 0.00 : ffffffffae600039: popq %rbx 0.00 0.00 0.00 : ffffffffae60003a: jmp 0xffffffffae620670 0.00 0.00 0.00 : ffffffffae60003f: nop Committer testing: Before: root@number:~# perf annotate --group --stdio2 clear_page_erms root@number:~# After: root@number:~# perf annotate --group --stdio2 clear_page_erms Samples: 125 of events 'cpu_atom/mem-loads,ldlat=30/P, cpu_atom/mem-stores/P, dummy:u', 4000 Hz, Event count (approx.): 13198416, [percent: local period] clear_page_erms() /proc/kcore Percent 0xffffffff990c6cc0 : endbr64 movl $0x1000,%ecx xorl %eax,%eax 0.00 100.00 0.00 rep stosb %al, (%rdi) ← retq int3 int3 int3 int3 nop nop root@number:~# Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: http://lore.kernel.org/lkml/20240807061555.1642669-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index efcadb7620b8..1bfe41783a7c 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -632,12 +632,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); } From cb1898f58e0f175d168a5a8c5a269a4d24cbbef5 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 6 Aug 2024 23:17:13 -0700 Subject: [PATCH 102/347] perf annotate-data: Support --skip-empty option The --skip-empty option is to hide dummy events in a group. Like other output mode in 'perf report' and 'perf annotate', the data-type profiling output should support the option. Committer testing: With dummy: root@number:~# perf annotate --stdio --group --data-type --skip-empty | head -24 Annotate type: 'pthread_mutex_t' in /usr/lib64/libc.so.6 (50 samples): event[0] = cpu_atom/mem-loads,ldlat=30/P event[1] = cpu_atom/mem-stores/P event[2] = dummy:u ============================================================================ Percent offset size field 100.00 100.00 0.00 0 40 pthread_mutex_t { 100.00 100.00 0.00 0 40 struct __pthread_mutex_s __data { 45.21 84.54 0.00 0 4 int __lock; 0.00 0.00 0.00 4 4 unsigned int __count; 0.00 1.83 0.00 8 4 int __owner; 5.19 10.65 0.00 12 4 unsigned int __nusers; 49.61 2.97 0.00 16 4 int __kind; 0.00 0.00 0.00 20 2 short int __spins; 0.00 0.00 0.00 22 2 short int __elision; 0.00 0.00 0.00 24 16 __pthread_list_t __list { 0.00 0.00 0.00 24 8 struct __pthread_internal_list* __prev; 0.00 0.00 0.00 32 8 struct __pthread_internal_list* __next; }; }; 0.00 0.00 0.00 0 0 char[] __size; 45.21 84.54 0.00 0 8 long int __align; }; Skipping it: root@number:~# perf annotate --stdio --group --data-type --skip-empty | head -24 Annotate type: 'pthread_mutex_t' in /usr/lib64/libc.so.6 (50 samples): event[0] = cpu_atom/mem-loads,ldlat=30/P event[1] = cpu_atom/mem-stores/P ============================================================================ Percent offset size field 100.00 100.00 0 40 pthread_mutex_t { 100.00 100.00 0 40 struct __pthread_mutex_s __data { 45.21 84.54 0 4 int __lock; 0.00 0.00 4 4 unsigned int __count; 0.00 1.83 8 4 int __owner; 5.19 10.65 12 4 unsigned int __nusers; 49.61 2.97 16 4 int __kind; 0.00 0.00 20 2 short int __spins; 0.00 0.00 22 2 short int __elision; 0.00 0.00 24 16 __pthread_list_t __list { 0.00 0.00 24 8 struct __pthread_internal_list* __prev; 0.00 0.00 32 8 struct __pthread_internal_list* __next; }; }; 0.00 0.00 0 0 char[] __size; 45.21 84.54 0 8 long int __align; }; Annotate type: 'pthread_mutexattr_t' in /usr/lib64/libc.so.6 (1 samples): root@number:~# Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240807061713.1642924-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/annotate-data.c | 30 ++++++++++++++---- tools/perf/util/annotate-data.c | 44 +++++++++++++------------- 2 files changed, 45 insertions(+), 29 deletions(-) diff --git a/tools/perf/ui/browsers/annotate-data.c b/tools/perf/ui/browsers/annotate-data.c index 8d6bf08d371d..c3db80a7589a 100644 --- a/tools/perf/ui/browsers/annotate-data.c +++ b/tools/perf/ui/browsers/annotate-data.c @@ -46,15 +46,18 @@ static int get_member_overhead(struct annotated_data_type *adt, struct annotated_member *member = entry->data; int i, k; - for (i = 0; i < member->size; i++) { + for (i = 0, k = 0; i < member->size; i++) { struct type_hist *h; struct evsel *evsel; int offset = member->offset + i; for_each_group_evsel(evsel, leader) { + if (symbol_conf.skip_empty && + evsel__hists(evsel)->stats.nr_samples == 0) + continue; + h = adt->histograms[evsel->core.idx]; - k = evsel__group_idx(evsel); - update_hist_entry(&entry->hists[k], &h->addr[offset]); + update_hist_entry(&entry->hists[k++], &h->addr[offset]); } } return 0; @@ -203,6 +206,7 @@ static void browser__write(struct ui_browser *uib, void *entry, int row) struct annotated_data_type *adt = he->mem_type; struct evsel *leader = hists_to_evsel(he->hists); struct evsel *evsel; + int idx = 0; if (member == NULL) { bool current = ui_browser__is_current_entry(uib, row); @@ -219,9 +223,12 @@ static void browser__write(struct ui_browser *uib, void *entry, int row) /* print the number */ for_each_group_evsel(evsel, leader) { struct type_hist *h = adt->histograms[evsel->core.idx]; - int idx = evsel__group_idx(evsel); - browser__write_overhead(uib, h, &be->hists[idx], row); + if (symbol_conf.skip_empty && + evsel__hists(evsel)->stats.nr_samples == 0) + continue; + + browser__write_overhead(uib, h, &be->hists[idx++], row); } /* print type info */ @@ -300,8 +307,17 @@ int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel, ui_helpline__push("Press ESC to exit"); - if (evsel__is_group_event(evsel)) - browser.nr_events = evsel->core.nr_members; + if (evsel__is_group_event(evsel)) { + struct evsel *pos; + int nr = 0; + + for_each_group_evsel(pos, evsel) { + if (!symbol_conf.skip_empty || + evsel__hists(pos)->stats.nr_samples) + nr++; + } + browser.nr_events = nr; + } ret = annotated_data_browser__collect_entries(&browser); if (ret == 0) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index c18272092a6b..ff85d190e3ac 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -1502,10 +1502,15 @@ static void print_annotated_data_header(struct hist_entry *he, struct evsel *evs struct evsel *pos; int i = 0; - for_each_group_evsel(pos, evsel) - printf(" event[%d] = %s\n", i++, pos->name); + nr_members = 0; + for_each_group_evsel(pos, evsel) { + if (symbol_conf.skip_empty && + evsel__hists(pos)->stats.nr_samples == 0) + continue; - nr_members = evsel->core.nr_members; + printf(" event[%d] = %s\n", i++, pos->name); + nr_members++; + } } if (symbol_conf.show_total_period) { @@ -1540,31 +1545,26 @@ static void print_annotated_data_type(struct annotated_data_type *mem_type, { struct annotated_member *child; struct type_hist *h = mem_type->histograms[evsel->core.idx]; - int i, nr_events = 1, samples = 0; + int i, nr_events = 0, samples = 0; u64 period = 0; int width = symbol_conf.show_total_period ? 11 : 7; + struct evsel *pos; - for (i = 0; i < member->size; i++) { - samples += h->addr[member->offset + i].nr_samples; - period += h->addr[member->offset + i].period; - } - print_annotated_data_value(h, period, samples); + for_each_group_evsel(pos, evsel) { + h = mem_type->histograms[pos->core.idx]; - if (evsel__is_group_event(evsel)) { - struct evsel *pos; + if (symbol_conf.skip_empty && + evsel__hists(pos)->stats.nr_samples == 0) + continue; - for_each_group_member(pos, evsel) { - h = mem_type->histograms[pos->core.idx]; - - samples = 0; - period = 0; - for (i = 0; i < member->size; i++) { - samples += h->addr[member->offset + i].nr_samples; - period += h->addr[member->offset + i].period; - } - print_annotated_data_value(h, period, samples); + samples = 0; + period = 0; + for (i = 0; i < member->size; i++) { + samples += h->addr[member->offset + i].nr_samples; + period += h->addr[member->offset + i].period; } - nr_events = evsel->core.nr_members; + print_annotated_data_value(h, period, samples); + nr_events++; } printf(" %10d %10d %*s%s\t%s", From 4f21bfed691c96da1fc85b76c16e8f6d8fe98a3d Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 12 Aug 2024 09:57:20 -0300 Subject: [PATCH 103/347] perf tests pmu: Initialize all fields of test_pmu variable Instead of explicitely initializing just the .name and .alias_name, use struct member named initialization of just the non-null -name field, the compiler will initialize all the other non-explicitely initialized fields to NULL. This makes the code more robust, avoiding the error recently fixed when the .alias_name was used and contained a random value. Reviewed-by: Veronika Molnarova Cc: Adrian Hunter Cc: Ian Rogers Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Michael Petlan Cc: Namhyung Kim Cc: Radostin Stoyanov Link: https://lore.kernel.org/lkml/e26941f9-f86c-4f2e-b812-20c49fb2c0d3@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/pmu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/pmu.c b/tools/perf/tests/pmu.c index a4730b5dc0d9..be18506f6a24 100644 --- a/tools/perf/tests/pmu.c +++ b/tools/perf/tests/pmu.c @@ -458,10 +458,10 @@ static int test__name_cmp(struct test_suite *test __maybe_unused, int subtest __ */ static int test__pmu_match(struct test_suite *test __maybe_unused, int subtest __maybe_unused) { - struct perf_pmu test_pmu; - test_pmu.alias_name = NULL; + struct perf_pmu test_pmu = { + .name = "pmuname", + }; - test_pmu.name = "pmuname"; TEST_ASSERT_EQUAL("Exact match", perf_pmu__match(&test_pmu, "pmuname"), true); TEST_ASSERT_EQUAL("Longer token", perf_pmu__match(&test_pmu, "longertoken"), false); TEST_ASSERT_EQUAL("Shorter token", perf_pmu__match(&test_pmu, "pmu"), false); From e6b56ae7c2d82976398fdbf00858f31193cf5971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Li=C5=A1ka?= Date: Fri, 19 Jul 2024 12:57:08 +0200 Subject: [PATCH 104/347] perf script: add --addr2line option Similarly to other subcommands (like report, top), it would be handy to provide a path for addr2line command. Signed-off-by: Martin Liska Cc: Ian Rogers Link: https://lore.kernel.org/r/eadc3e36-029d-4848-9d69-272fe5a83a26@foxlink.cz Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-script.txt | 3 +++ tools/perf/builtin-script.c | 2 ++ tools/perf/util/symbol_conf.h | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index ff086ef05a0c..5abb960c4960 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -369,6 +369,9 @@ OPTIONS --demangle-kernel:: Demangle kernel symbol names to human readable form (for C++ kernels). +--addr2line=:: + Path to addr2line binary. + --header Show perf.data header. diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index c16224b1fef3..932167b2362b 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -4052,6 +4052,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, diff --git a/tools/perf/util/symbol_conf.h b/tools/perf/util/symbol_conf.h index 657cfa5af43c..a9c51acc722f 100644 --- a/tools/perf/util/symbol_conf.h +++ b/tools/perf/util/symbol_conf.h @@ -64,7 +64,7 @@ struct symbol_conf { *sym_list_str, *col_width_list_str, *bt_stop_list_str; - char *addr2line_path; + const char *addr2line_path; unsigned long time_quantum; struct strlist *dso_list, *comm_list, From 043da846c2b2dfd5b187bf3a9993b8fb0a6ed94a Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Mon, 12 Aug 2024 10:34:59 +0100 Subject: [PATCH 105/347] perf docs: Refine the description for the buffer size Current description for the AUX trace buffer size is misleading. When a user specifies the option '-m,512M', it represents a size value in bytes (512MiB) but not 512M pages (512M x 4KiB regard to a page of 4KiB). Make the document clear that the normal buffer and the AUX tracing buffer share the same semantics. Syncs the documents for consistent text. Reviewed-by: James Clark Signed-off-by: Leo Yan Cc: Adrian Hunter Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240812093459.2575278-1-leo.yan@arm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-kvm.txt | 6 +++--- tools/perf/Documentation/perf-record.txt | 9 +++++---- tools/perf/Documentation/perf-top.txt | 4 ++-- tools/perf/Documentation/perf-trace.txt | 4 ++-- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt index b66be66fe836..c26524d38f47 100644 --- a/tools/perf/Documentation/perf-kvm.txt +++ b/tools/perf/Documentation/perf-kvm.txt @@ -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:: diff --git a/tools/perf/Documentation/perf-record.txt b/tools/perf/Documentation/perf-record.txt index 41e36b4dc765..242223240a08 100644 --- a/tools/perf/Documentation/perf-record.txt +++ b/tools/perf/Documentation/perf-record.txt @@ -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 diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt index 667e5102075e..af3e4230c72f 100644 --- a/tools/perf/Documentation/perf-top.txt +++ b/tools/perf/Documentation/perf-top.txt @@ -83,8 +83,8 @@ Default is to monitor all CPUS. -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. -p :: --pid=:: diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt index f0da8cf63e9a..6e0cc50bbc13 100644 --- a/tools/perf/Documentation/perf-trace.txt +++ b/tools/perf/Documentation/perf-trace.txt @@ -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:: From 00b04242683ebae91164244883b46e6ab2669d63 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sat, 10 Aug 2024 12:15:02 -0700 Subject: [PATCH 106/347] perf annotate-data: Fix a buffer overflow in TUI browser In get_member_overhead(), k is updated when it has a entry in the histogram. But the entry->hists array is allocated with the number of evsel in the group. So the k should be reset when it iterates the event using for_each_group_evsel(), otherwise it'd crash due to a buffer overflow. Fixes: cb1898f58e0f175d ("perf annotate-data: Support --skip-empty option") Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240810191502.1947959-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/annotate-data.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/ui/browsers/annotate-data.c b/tools/perf/ui/browsers/annotate-data.c index c3db80a7589a..a937b55da736 100644 --- a/tools/perf/ui/browsers/annotate-data.c +++ b/tools/perf/ui/browsers/annotate-data.c @@ -46,11 +46,12 @@ static int get_member_overhead(struct annotated_data_type *adt, struct annotated_member *member = entry->data; int i, k; - for (i = 0, k = 0; i < member->size; i++) { + for (i = 0; i < member->size; i++) { struct type_hist *h; struct evsel *evsel; int offset = member->offset + i; + k = 0; for_each_group_evsel(evsel, leader) { if (symbol_conf.skip_empty && evsel__hists(evsel)->stats.nr_samples == 0) From 040c0f887fdcfe747a3f63c94e9cd29e9ed0b872 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 12 Aug 2024 10:25:33 -0700 Subject: [PATCH 107/347] perf lock contention: Change stack_id type to s32 The bpf_get_stackid() helper returns a signed type to check whether it failed to get a stacktrace or not. But it saved the result in u32 and checked if the value is negative. 376 if (needs_callstack) { 377 pelem->stack_id = bpf_get_stackid(ctx, &stacks, 378 BPF_F_FAST_STACK_CMP | stack_skip); --> 379 if (pelem->stack_id < 0) ./tools/perf/util/bpf_skel/lock_contention.bpf.c:379 contention_begin() warn: unsigned 'pelem->stack_id' is never less than zero. Let's change the type to s32 instead. Fixes: 6d499a6b3d90277d ("perf lock: Print the number of lost entries for BPF") Reported-by: Dan Carpenter Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240812172533.2015291-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf_skel/lock_data.h | 4 ++-- tools/perf/util/bpf_skel/vmlinux/vmlinux.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/bpf_skel/lock_data.h b/tools/perf/util/bpf_skel/lock_data.h index 36af11faad03..de12892f992f 100644 --- a/tools/perf/util/bpf_skel/lock_data.h +++ b/tools/perf/util/bpf_skel/lock_data.h @@ -7,11 +7,11 @@ struct tstamp_data { u64 timestamp; u64 lock; u32 flags; - u32 stack_id; + s32 stack_id; }; struct contention_key { - u32 stack_id; + s32 stack_id; u32 pid; u64 lock_addr_or_cgroup; }; diff --git a/tools/perf/util/bpf_skel/vmlinux/vmlinux.h b/tools/perf/util/bpf_skel/vmlinux/vmlinux.h index e9028235d771..d818e30c5457 100644 --- a/tools/perf/util/bpf_skel/vmlinux/vmlinux.h +++ b/tools/perf/util/bpf_skel/vmlinux/vmlinux.h @@ -15,6 +15,7 @@ typedef __u8 u8; typedef __u32 u32; +typedef __s32 s32; typedef __u64 u64; typedef __s64 s64; From 7a75c6c23a2ea8dd22d90805b3a42bd65c53830e Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 10 Aug 2024 21:20:04 -0700 Subject: [PATCH 108/347] perf vendor events: SKX, CLX, SNR uncore cache event fixes Cache home agent (CHA) events were setting the low rather than high config1 bits. SNR was using CLX CHA events, however its CHA is similar to ICX so remove the events. Incorporate the updates in: https://github.com/intel/perfmon/pull/215 https://github.com/intel/perfmon/pull/216 Fixes: 4cc49942444e958b ("perf vendor events: Update cascadelakex events/metrics") Closes: https://lore.kernel.org/linux-perf-users/CAPhsuW4nem9XZP+b=sJJ7kqXG-cafz0djZf51HsgjCiwkGBA+A@mail.gmail.com/ Reported-by: Song Liu Reviewed-by: Kan Liang Co-authored-by: Weilin Wang Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240811042004.421869-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- .../arch/x86/cascadelakex/uncore-cache.json | 60 +++++++++---------- .../arch/x86/skylakex/uncore-cache.json | 60 +++++++++---------- .../arch/x86/snowridgex/uncore-cache.json | 57 ------------------ 3 files changed, 60 insertions(+), 117 deletions(-) diff --git a/tools/perf/pmu-events/arch/x86/cascadelakex/uncore-cache.json b/tools/perf/pmu-events/arch/x86/cascadelakex/uncore-cache.json index c9596e18ec09..6347eba48810 100644 --- a/tools/perf/pmu-events/arch/x86/cascadelakex/uncore-cache.json +++ b/tools/perf/pmu-events/arch/x86/cascadelakex/uncore-cache.json @@ -4577,7 +4577,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_CRD", - "Filter": "config1=0x40233", + "Filter": "config1=0x4023300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : CRds issued by iA Cores that Hit the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -4588,7 +4588,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_DRD", - "Filter": "config1=0x40433", + "Filter": "config1=0x4043300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : DRds issued by iA Cores that Hit the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -4599,7 +4599,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_LlcPrefCRD", - "Filter": "config1=0x4b233", + "Filter": "config1=0x4b23300000000", "PerPkg": "1", "UMask": "0x11", "Unit": "CHA" @@ -4609,7 +4609,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_LlcPrefDRD", - "Filter": "config1=0x4b433", + "Filter": "config1=0x4b43300000000", "PerPkg": "1", "UMask": "0x11", "Unit": "CHA" @@ -4619,7 +4619,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_LlcPrefRFO", - "Filter": "config1=0x4b033", + "Filter": "config1=0x4b03300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : LLCPrefRFO issued by iA Cores that hit the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -4630,7 +4630,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_RFO", - "Filter": "config1=0x40033", + "Filter": "config1=0x4003300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : RFOs issued by iA Cores that Hit the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -4651,7 +4651,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_CRD", - "Filter": "config1=0x40233", + "Filter": "config1=0x4023300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : CRds issued by iA Cores that Missed the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -4662,7 +4662,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_DRD", - "Filter": "config1=0x40433", + "Filter": "config1=0x4043300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : DRds issued by iA Cores that Missed the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -4673,7 +4673,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LlcPrefCRD", - "Filter": "config1=0x4b233", + "Filter": "config1=0x4b23300000000", "PerPkg": "1", "UMask": "0x21", "Unit": "CHA" @@ -4683,7 +4683,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LlcPrefDRD", - "Filter": "config1=0x4b433", + "Filter": "config1=0x4b43300000000", "PerPkg": "1", "UMask": "0x21", "Unit": "CHA" @@ -4693,7 +4693,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LlcPrefRFO", - "Filter": "config1=0x4b033", + "Filter": "config1=0x4b03300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : LLCPrefRFO issued by iA Cores that missed the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -4704,7 +4704,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_RFO", - "Filter": "config1=0x40033", + "Filter": "config1=0x4003300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : RFOs issued by iA Cores that Missed the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -4747,7 +4747,7 @@ "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IO_MISS_ITOM", "Experimental": "1", - "Filter": "config1=0x49033", + "Filter": "config1=0x4903300000000", "PerPkg": "1", "PublicDescription": "Counts the number of entries successfully inserted into the TOR that are generated from local IO ItoM requests that miss the LLC. An ItoM request is used by IIO to request a data write without first reading the data for ownership.", "UMask": "0x24", @@ -4759,7 +4759,7 @@ "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IO_MISS_RDCUR", "Experimental": "1", - "Filter": "config1=0x43C33", + "Filter": "config1=0x43c3300000000", "PerPkg": "1", "PublicDescription": "Counts the number of entries successfully inserted into the TOR that are generated from local IO RdCur requests and miss the LLC. A RdCur request is used by IIO to read data without changing state.", "UMask": "0x24", @@ -4771,7 +4771,7 @@ "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IO_MISS_RFO", "Experimental": "1", - "Filter": "config1=0x40033", + "Filter": "config1=0x4003300000000", "PerPkg": "1", "PublicDescription": "Counts the number of entries successfully inserted into the TOR that are generated from local IO RFO requests that miss the LLC. A read for ownership (RFO) requests a cache line to be cached in E state with the intent to modify.", "UMask": "0x24", @@ -4999,7 +4999,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_CRD", - "Filter": "config1=0x40233", + "Filter": "config1=0x4023300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : CRds issued by iA Cores that Hit the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -5010,7 +5010,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_DRD", - "Filter": "config1=0x40433", + "Filter": "config1=0x4043300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : DRds issued by iA Cores that Hit the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -5021,7 +5021,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_LlcPrefCRD", - "Filter": "config1=0x4b233", + "Filter": "config1=0x4b23300000000", "PerPkg": "1", "UMask": "0x11", "Unit": "CHA" @@ -5031,7 +5031,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_LlcPrefDRD", - "Filter": "config1=0x4b433", + "Filter": "config1=0x4b43300000000", "PerPkg": "1", "UMask": "0x11", "Unit": "CHA" @@ -5041,7 +5041,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_LlcPrefRFO", - "Filter": "config1=0x4b033", + "Filter": "config1=0x4b03300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : LLCPrefRFO issued by iA Cores that hit the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -5052,7 +5052,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_RFO", - "Filter": "config1=0x40033", + "Filter": "config1=0x4003300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : RFOs issued by iA Cores that Hit the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -5073,7 +5073,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_CRD", - "Filter": "config1=0x40233", + "Filter": "config1=0x4023300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : CRds issued by iA Cores that Missed the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -5084,7 +5084,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD", - "Filter": "config1=0x40433", + "Filter": "config1=0x4043300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : DRds issued by iA Cores that Missed the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -5095,7 +5095,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LlcPrefCRD", - "Filter": "config1=0x4b233", + "Filter": "config1=0x4b23300000000", "PerPkg": "1", "UMask": "0x21", "Unit": "CHA" @@ -5105,7 +5105,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LlcPrefDRD", - "Filter": "config1=0x4b433", + "Filter": "config1=0x4b43300000000", "PerPkg": "1", "UMask": "0x21", "Unit": "CHA" @@ -5115,7 +5115,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LlcPrefRFO", - "Filter": "config1=0x4b033", + "Filter": "config1=0x4b03300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : LLCPrefRFO issued by iA Cores that missed the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -5126,7 +5126,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_RFO", - "Filter": "config1=0x40033", + "Filter": "config1=0x4003300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : RFOs issued by iA Cores that Missed the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -5171,7 +5171,7 @@ "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_ITOM", "Experimental": "1", - "Filter": "config1=0x49033", + "Filter": "config1=0x4903300000000", "PerPkg": "1", "PublicDescription": "For each cycle, this event accumulates the number of valid entries in the TOR that are generated from local IO ItoM requests that miss the LLC. An ItoM is used by IIO to request a data write without first reading the data for ownership.", "UMask": "0x24", @@ -5183,7 +5183,7 @@ "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_RDCUR", "Experimental": "1", - "Filter": "config1=0x43C33", + "Filter": "config1=0x43c3300000000", "PerPkg": "1", "PublicDescription": "For each cycle, this event accumulates the number of valid entries in the TOR that are generated from local IO RdCur requests that miss the LLC. A RdCur request is used by IIO to read data without changing state.", "UMask": "0x24", @@ -5195,7 +5195,7 @@ "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_RFO", "Experimental": "1", - "Filter": "config1=0x40033", + "Filter": "config1=0x4003300000000", "PerPkg": "1", "PublicDescription": "For each cycle, this event accumulates the number of valid entries in the TOR that are generated from local IO RFO requests that miss the LLC. A read for ownership (RFO) requests data to be cached in E state with the intent to modify.", "UMask": "0x24", diff --git a/tools/perf/pmu-events/arch/x86/skylakex/uncore-cache.json b/tools/perf/pmu-events/arch/x86/skylakex/uncore-cache.json index da46a3aeb58c..4fc818626491 100644 --- a/tools/perf/pmu-events/arch/x86/skylakex/uncore-cache.json +++ b/tools/perf/pmu-events/arch/x86/skylakex/uncore-cache.json @@ -4454,7 +4454,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_CRD", - "Filter": "config1=0x40233", + "Filter": "config1=0x4023300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : CRds issued by iA Cores that Hit the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -4465,7 +4465,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_DRD", - "Filter": "config1=0x40433", + "Filter": "config1=0x4043300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : DRds issued by iA Cores that Hit the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -4476,7 +4476,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_LlcPrefCRD", - "Filter": "config1=0x4b233", + "Filter": "config1=0x4b23300000000", "PerPkg": "1", "UMask": "0x11", "Unit": "CHA" @@ -4486,7 +4486,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_LlcPrefDRD", - "Filter": "config1=0x4b433", + "Filter": "config1=0x4b43300000000", "PerPkg": "1", "UMask": "0x11", "Unit": "CHA" @@ -4496,7 +4496,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_LlcPrefRFO", - "Filter": "config1=0x4b033", + "Filter": "config1=0x4b03300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : LLCPrefRFO issued by iA Cores that hit the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -4507,7 +4507,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_HIT_RFO", - "Filter": "config1=0x40033", + "Filter": "config1=0x4003300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : RFOs issued by iA Cores that Hit the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -4528,7 +4528,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_CRD", - "Filter": "config1=0x40233", + "Filter": "config1=0x4023300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : CRds issued by iA Cores that Missed the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -4539,7 +4539,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_DRD", - "Filter": "config1=0x40433", + "Filter": "config1=0x4043300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : DRds issued by iA Cores that Missed the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -4550,7 +4550,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LlcPrefCRD", - "Filter": "config1=0x4b233", + "Filter": "config1=0x4b23300000000", "PerPkg": "1", "UMask": "0x21", "Unit": "CHA" @@ -4560,7 +4560,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LlcPrefDRD", - "Filter": "config1=0x4b433", + "Filter": "config1=0x4b43300000000", "PerPkg": "1", "UMask": "0x21", "Unit": "CHA" @@ -4570,7 +4570,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_LlcPrefRFO", - "Filter": "config1=0x4b033", + "Filter": "config1=0x4b03300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : LLCPrefRFO issued by iA Cores that missed the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -4581,7 +4581,7 @@ "Counter": "0,1,2,3", "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IA_MISS_RFO", - "Filter": "config1=0x40033", + "Filter": "config1=0x4003300000000", "PerPkg": "1", "PublicDescription": "TOR Inserts : RFOs issued by iA Cores that Missed the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -4624,7 +4624,7 @@ "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IO_MISS_ITOM", "Experimental": "1", - "Filter": "config1=0x49033", + "Filter": "config1=0x4903300000000", "PerPkg": "1", "PublicDescription": "Counts the number of entries successfully inserted into the TOR that are generated from local IO ItoM requests that miss the LLC. An ItoM request is used by IIO to request a data write without first reading the data for ownership.", "UMask": "0x24", @@ -4636,7 +4636,7 @@ "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IO_MISS_RDCUR", "Experimental": "1", - "Filter": "config1=0x43C33", + "Filter": "config1=0x43c3300000000", "PerPkg": "1", "PublicDescription": "Counts the number of entries successfully inserted into the TOR that are generated from local IO RdCur requests and miss the LLC. A RdCur request is used by IIO to read data without changing state.", "UMask": "0x24", @@ -4648,7 +4648,7 @@ "EventCode": "0x35", "EventName": "UNC_CHA_TOR_INSERTS.IO_MISS_RFO", "Experimental": "1", - "Filter": "config1=0x40033", + "Filter": "config1=0x4003300000000", "PerPkg": "1", "PublicDescription": "Counts the number of entries successfully inserted into the TOR that are generated from local IO RFO requests that miss the LLC. A read for ownership (RFO) requests a cache line to be cached in E state with the intent to modify.", "UMask": "0x24", @@ -4865,7 +4865,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_CRD", - "Filter": "config1=0x40233", + "Filter": "config1=0x4023300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : CRds issued by iA Cores that Hit the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -4876,7 +4876,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_DRD", - "Filter": "config1=0x40433", + "Filter": "config1=0x4043300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : DRds issued by iA Cores that Hit the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -4887,7 +4887,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_LlcPrefCRD", - "Filter": "config1=0x4b233", + "Filter": "config1=0x4b23300000000", "PerPkg": "1", "UMask": "0x11", "Unit": "CHA" @@ -4897,7 +4897,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_LlcPrefDRD", - "Filter": "config1=0x4b433", + "Filter": "config1=0x4b43300000000", "PerPkg": "1", "UMask": "0x11", "Unit": "CHA" @@ -4907,7 +4907,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_LlcPrefRFO", - "Filter": "config1=0x4b033", + "Filter": "config1=0x4b03300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : LLCPrefRFO issued by iA Cores that hit the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -4918,7 +4918,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_HIT_RFO", - "Filter": "config1=0x40033", + "Filter": "config1=0x4003300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : RFOs issued by iA Cores that Hit the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x11", @@ -4939,7 +4939,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_CRD", - "Filter": "config1=0x40233", + "Filter": "config1=0x4023300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : CRds issued by iA Cores that Missed the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -4950,7 +4950,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_DRD", - "Filter": "config1=0x40433", + "Filter": "config1=0x4043300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : DRds issued by iA Cores that Missed the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -4961,7 +4961,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LlcPrefCRD", - "Filter": "config1=0x4b233", + "Filter": "config1=0x4b23300000000", "PerPkg": "1", "UMask": "0x21", "Unit": "CHA" @@ -4971,7 +4971,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LlcPrefDRD", - "Filter": "config1=0x4b433", + "Filter": "config1=0x4b43300000000", "PerPkg": "1", "UMask": "0x21", "Unit": "CHA" @@ -4981,7 +4981,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_LlcPrefRFO", - "Filter": "config1=0x4b033", + "Filter": "config1=0x4b03300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : LLCPrefRFO issued by iA Cores that missed the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -4992,7 +4992,7 @@ "Counter": "0", "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IA_MISS_RFO", - "Filter": "config1=0x40033", + "Filter": "config1=0x4003300000000", "PerPkg": "1", "PublicDescription": "TOR Occupancy : RFOs issued by iA Cores that Missed the LLC : For each cycle, this event accumulates the number of valid entries in the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", "UMask": "0x21", @@ -5037,7 +5037,7 @@ "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_ITOM", "Experimental": "1", - "Filter": "config1=0x49033", + "Filter": "config1=0x4903300000000", "PerPkg": "1", "PublicDescription": "For each cycle, this event accumulates the number of valid entries in the TOR that are generated from local IO ItoM requests that miss the LLC. An ItoM is used by IIO to request a data write without first reading the data for ownership.", "UMask": "0x24", @@ -5049,7 +5049,7 @@ "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_RDCUR", "Experimental": "1", - "Filter": "config1=0x43C33", + "Filter": "config1=0x43c3300000000", "PerPkg": "1", "PublicDescription": "For each cycle, this event accumulates the number of valid entries in the TOR that are generated from local IO RdCur requests that miss the LLC. A RdCur request is used by IIO to read data without changing state.", "UMask": "0x24", @@ -5061,7 +5061,7 @@ "EventCode": "0x36", "EventName": "UNC_CHA_TOR_OCCUPANCY.IO_MISS_RFO", "Experimental": "1", - "Filter": "config1=0x40033", + "Filter": "config1=0x4003300000000", "PerPkg": "1", "PublicDescription": "For each cycle, this event accumulates the number of valid entries in the TOR that are generated from local IO RFO requests that miss the LLC. A read for ownership (RFO) requests data to be cached in E state with the intent to modify.", "UMask": "0x24", diff --git a/tools/perf/pmu-events/arch/x86/snowridgex/uncore-cache.json b/tools/perf/pmu-events/arch/x86/snowridgex/uncore-cache.json index 7551fb91a9d7..a81776deb2e6 100644 --- a/tools/perf/pmu-events/arch/x86/snowridgex/uncore-cache.json +++ b/tools/perf/pmu-events/arch/x86/snowridgex/uncore-cache.json @@ -1,61 +1,4 @@ [ - { - "BriefDescription": "MMIO reads. Derived from unc_cha_tor_inserts.ia_miss", - "Counter": "0,1,2,3", - "EventCode": "0x35", - "EventName": "LLC_MISSES.MMIO_READ", - "Filter": "config1=0x40040e33", - "PerPkg": "1", - "PublicDescription": "TOR Inserts : All requests from iA Cores that Missed the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", - "UMask": "0xc001fe01", - "Unit": "CHA" - }, - { - "BriefDescription": "MMIO writes. Derived from unc_cha_tor_inserts.ia_miss", - "Counter": "0,1,2,3", - "EventCode": "0x35", - "EventName": "LLC_MISSES.MMIO_WRITE", - "Filter": "config1=0x40041e33", - "PerPkg": "1", - "PublicDescription": "TOR Inserts : All requests from iA Cores that Missed the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", - "UMask": "0xc001fe01", - "Unit": "CHA" - }, - { - "BriefDescription": "LLC misses - Uncacheable reads (from cpu) . Derived from unc_cha_tor_inserts.ia_miss", - "Counter": "0,1,2,3", - "EventCode": "0x35", - "EventName": "LLC_MISSES.UNCACHEABLE", - "Filter": "config1=0x40e33", - "PerPkg": "1", - "PublicDescription": "TOR Inserts : All requests from iA Cores that Missed the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", - "UMask": "0xc001fe01", - "Unit": "CHA" - }, - { - "BriefDescription": "Streaming stores (full cache line). Derived from unc_cha_tor_inserts.ia_miss", - "Counter": "0,1,2,3", - "EventCode": "0x35", - "EventName": "LLC_REFERENCES.STREAMING_FULL", - "Filter": "config1=0x41833", - "PerPkg": "1", - "PublicDescription": "TOR Inserts : All requests from iA Cores that Missed the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", - "ScaleUnit": "64Bytes", - "UMask": "0xc001fe01", - "Unit": "CHA" - }, - { - "BriefDescription": "Streaming stores (partial cache line). Derived from unc_cha_tor_inserts.ia_miss", - "Counter": "0,1,2,3", - "EventCode": "0x35", - "EventName": "LLC_REFERENCES.STREAMING_PARTIAL", - "Filter": "config1=0x41a33", - "PerPkg": "1", - "PublicDescription": "TOR Inserts : All requests from iA Cores that Missed the LLC : Counts the number of entries successfully inserted into the TOR that match qualifications specified by the subevent. Does not include addressless requests such as locks and interrupts.", - "ScaleUnit": "64Bytes", - "UMask": "0xc001fe01", - "Unit": "CHA" - }, { "BriefDescription": "CMS Agent0 AD Credits Acquired : For Transgress 0", "Counter": "0,1,2,3", From 05fc5b7de395c82a17871e441a551566a49c1ea8 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 12 Aug 2024 12:44:45 -0700 Subject: [PATCH 109/347] perf annotate-data: Support folding in TUI browser MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Like in the hists browser, it should support folding current entry so that it can hide unwanted details in some data structures. The folded entries will be displayed with the '+' sign, while unfolded entries will have the '-' sign. Entries that have no children will not show any signs. Annotate type: 'struct socket' (1 samples) Percent Offset Size Field - 100.00 0 128 struct socket { ◆ 0.00 0 4 socket_state state; ▒ 0.00 4 2 short int type; ▒ 0.00 8 8 long unsigned int flags; ▒ 0.00 16 8 struct file* file; ▒ 100.00 24 8 struct sock* sk; ▒ 0.00 32 8 struct proto_ops* ops; ▒ - 0.00 64 64 struct socket_wq wq { ▒ - 0.00 64 24 wait_queue_head_t wait { ▒ + 0.00 64 4 spinlock_t lock; ▒ - 0.00 72 16 struct list_head head { ▒ 0.00 72 8 struct list_head* next; ▒ 0.00 80 8 struct list_head* prev; ▒ }; ▒ }; ▒ 0.00 88 8 struct fasync_struct* fasync_list; ▒ 0.00 96 8 long unsigned int flags; ▒ + 0.00 104 16 struct callback_head rcu; ▒ }; ▒ }; ▒ This just adds the display logic for folding, actually folding action will be implemented in the next patch. Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240812194447.2049187-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/annotate-data.c | 235 ++++++++++++++++++++++--- 1 file changed, 212 insertions(+), 23 deletions(-) diff --git a/tools/perf/ui/browsers/annotate-data.c b/tools/perf/ui/browsers/annotate-data.c index a937b55da736..04c73b67cd6c 100644 --- a/tools/perf/ui/browsers/annotate-data.c +++ b/tools/perf/ui/browsers/annotate-data.c @@ -14,6 +14,10 @@ #include "util/evlist.h" #include "util/sort.h" +#define FOLDED_SIGN '+' +#define UNFOLD_SIGN '-' +#define NOCHLD_SIGN ' ' + struct annotated_data_browser { struct ui_browser b; struct list_head entries; @@ -24,7 +28,11 @@ struct browser_entry { struct list_head node; struct annotated_member *data; struct type_hist_entry *hists; - int indent; + struct browser_entry *parent; + struct list_head children; + int indent; /*indentation level, starts from 0 */ + int nr_entries; /* # of visible entries: self + descendents */ + bool folded; /* only can be false when it has children */ }; static struct annotated_data_browser *get_browser(struct ui_browser *uib) @@ -65,13 +73,14 @@ static int get_member_overhead(struct annotated_data_type *adt, } static int add_child_entries(struct annotated_data_browser *browser, + struct browser_entry *parent, struct annotated_data_type *adt, struct annotated_member *member, struct evsel *evsel, int indent) { struct annotated_member *pos; struct browser_entry *entry; - int nr_entries = 0; + struct list_head *parent_list; entry = zalloc(sizeof(*entry)); if (entry == NULL) @@ -84,36 +93,60 @@ static int add_child_entries(struct annotated_data_browser *browser, } entry->data = member; + entry->parent = parent; entry->indent = indent; if (get_member_overhead(adt, entry, evsel) < 0) { free(entry); return -1; } - list_add_tail(&entry->node, &browser->entries); - nr_entries++; + INIT_LIST_HEAD(&entry->children); + if (parent) + parent_list = &parent->children; + else + parent_list = &browser->entries; + + list_add_tail(&entry->node, parent_list); list_for_each_entry(pos, &member->children, node) { - int nr = add_child_entries(browser, adt, pos, evsel, indent + 1); - + int nr = add_child_entries(browser, entry, adt, pos, evsel, + indent + 1); if (nr < 0) return nr; - - nr_entries += nr; } /* add an entry for the closing bracket ("}") */ if (!list_empty(&member->children)) { - entry = zalloc(sizeof(*entry)); - if (entry == NULL) + struct browser_entry *bracket; + + bracket = zalloc(sizeof(*bracket)); + if (bracket == NULL) return -1; - entry->indent = indent; - list_add_tail(&entry->node, &browser->entries); - nr_entries++; + bracket->indent = indent; + bracket->parent = entry; + bracket->folded = true; + bracket->nr_entries = 1; + + INIT_LIST_HEAD(&bracket->children); + list_add_tail(&bracket->node, &entry->children); } - return nr_entries; + /* fold child entries by default */ + entry->folded = true; + entry->nr_entries = 1; + return 0; +} + +static u32 count_visible_entries(struct annotated_data_browser *browser) +{ + int nr = 0; + struct browser_entry *entry; + + list_for_each_entry(entry, &browser->entries, node) + nr += entry->nr_entries; + + return nr; } static int annotated_data_browser__collect_entries(struct annotated_data_browser *browser) @@ -123,9 +156,12 @@ static int annotated_data_browser__collect_entries(struct annotated_data_browser struct evsel *evsel = hists_to_evsel(he->hists); INIT_LIST_HEAD(&browser->entries); + + add_child_entries(browser, /*parent=*/NULL, adt, &adt->self, evsel, + /*indent=*/0); + browser->b.entries = &browser->entries; - browser->b.nr_entries = add_child_entries(browser, adt, &adt->self, - evsel, /*indent=*/0); + browser->b.nr_entries = count_visible_entries(browser); return 0; } @@ -140,9 +176,155 @@ static void annotated_data_browser__delete_entries(struct annotated_data_browser } } +static struct browser_entry *get_first_child(struct browser_entry *entry) +{ + if (list_empty(&entry->children)) + return NULL; + + return list_first_entry(&entry->children, struct browser_entry, node); +} + +static struct browser_entry *get_last_child(struct browser_entry *entry) +{ + if (list_empty(&entry->children)) + return NULL; + + return list_last_entry(&entry->children, struct browser_entry, node); +} + +static bool is_first_child(struct browser_entry *entry) +{ + /* This will be checked in a different way */ + if (entry->parent == NULL) + return false; + + return get_first_child(entry->parent) == entry; +} + +static bool is_last_child(struct browser_entry *entry) +{ + /* This will be checked in a different way */ + if (entry->parent == NULL) + return false; + + return get_last_child(entry->parent) == entry; +} + +static struct browser_entry *browser__prev_entry(struct ui_browser *uib, + struct browser_entry *entry) +{ + struct annotated_data_browser *browser = get_browser(uib); + struct browser_entry *first; + + first = list_first_entry(&browser->entries, struct browser_entry, node); + + while (entry != first) { + if (is_first_child(entry)) + entry = entry->parent; + else { + entry = list_prev_entry(entry, node); + while (!entry->folded) + entry = get_last_child(entry); + } + + if (!uib->filter || !uib->filter(uib, &entry->node)) + return entry; + } + return first; +} + +static struct browser_entry *browser__next_entry(struct ui_browser *uib, + struct browser_entry *entry) +{ + struct annotated_data_browser *browser = get_browser(uib); + struct browser_entry *last; + + last = list_last_entry(&browser->entries, struct browser_entry, node); + while (!last->folded) + last = get_last_child(last); + + while (entry != last) { + if (!entry->folded) + entry = get_first_child(entry); + else { + while (is_last_child(entry)) + entry = entry->parent; + + entry = list_next_entry(entry, node); + } + + if (!uib->filter || !uib->filter(uib, &entry->node)) + return entry; + } + return last; +} + +static void browser__seek(struct ui_browser *uib, off_t offset, int whence) +{ + struct annotated_data_browser *browser = get_browser(uib); + struct browser_entry *entry; + + if (uib->nr_entries == 0) + return; + + switch (whence) { + case SEEK_SET: + entry = list_first_entry(&browser->entries, typeof(*entry), node); + if (uib->filter && uib->filter(uib, &entry->node)) + entry = browser__next_entry(uib, entry); + break; + case SEEK_CUR: + entry = list_entry(uib->top, typeof(*entry), node); + break; + case SEEK_END: + entry = list_last_entry(&browser->entries, typeof(*entry), node); + while (!entry->folded) + entry = get_last_child(entry); + if (uib->filter && uib->filter(uib, &entry->node)) + entry = browser__prev_entry(uib, entry); + break; + default: + return; + } + + assert(entry != NULL); + + if (offset > 0) { + while (offset-- != 0) + entry = browser__next_entry(uib, entry); + } else { + while (offset++ != 0) + entry = browser__prev_entry(uib, entry); + } + + uib->top = &entry->node; +} + static unsigned int browser__refresh(struct ui_browser *uib) { - return ui_browser__list_head_refresh(uib); + struct browser_entry *entry, *next; + int row = 0; + + if (uib->top == NULL || uib->top == uib->entries) + browser__seek(uib, SEEK_SET, 0); + + entry = list_entry(uib->top, typeof(*entry), node); + + while (true) { + if (!uib->filter || !uib->filter(uib, &entry->node)) { + ui_browser__gotorc(uib, row, 0); + uib->write(uib, entry, row); + if (++row == uib->rows) + break; + } + next = browser__next_entry(uib, entry); + if (next == entry) + break; + + entry = next; + } + + return row; } static int browser__show(struct ui_browser *uib) @@ -171,7 +353,7 @@ static int browser__show(struct ui_browser *uib) strcpy(title, "Percent"); ui_browser__printf(uib, "%*s %10s %10s %10s %s", - 11 * (browser->nr_events - 1), "", + 2 + 11 * (browser->nr_events - 1), "", title, "Offset", "Size", "Field"); ui_browser__write_nstring(uib, "", uib->width); return 0; @@ -208,12 +390,12 @@ static void browser__write(struct ui_browser *uib, void *entry, int row) struct evsel *leader = hists_to_evsel(he->hists); struct evsel *evsel; int idx = 0; + bool current = ui_browser__is_current_entry(uib, row); if (member == NULL) { - bool current = ui_browser__is_current_entry(uib, row); - /* print the closing bracket */ ui_browser__set_percent_color(uib, 0, current); + ui_browser__printf(uib, "%c ", NOCHLD_SIGN); ui_browser__write_nstring(uib, "", 11 * browser->nr_events); ui_browser__printf(uib, " %10s %10s %*s};", "", "", be->indent * 4, ""); @@ -221,6 +403,13 @@ static void browser__write(struct ui_browser *uib, void *entry, int row) return; } + ui_browser__set_percent_color(uib, 0, current); + + if (!list_empty(&be->children)) + ui_browser__printf(uib, "%c ", be->folded ? FOLDED_SIGN : UNFOLD_SIGN); + else + ui_browser__printf(uib, "%c ", NOCHLD_SIGN); + /* print the number */ for_each_group_evsel(evsel, leader) { struct type_hist *h = adt->histograms[evsel->core.idx]; @@ -237,13 +426,13 @@ static void browser__write(struct ui_browser *uib, void *entry, int row) ui_browser__printf(uib, " %10d %10d %s%s", member->offset, member->size, member->type_name, - list_empty(&member->children) ? ";" : " {"); + list_empty(&member->children) || be->folded? ";" : " {"); } else { ui_browser__printf(uib, " %10d %10d %*s%s\t%s%s", member->offset, member->size, be->indent * 4, "", member->type_name, member->var_name ?: "", - list_empty(&member->children) ? ";" : " {"); + list_empty(&member->children) || be->folded ? ";" : " {"); } /* fill the rest */ ui_browser__write_nstring(uib, "", uib->width); @@ -297,7 +486,7 @@ int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel, struct annotated_data_browser browser = { .b = { .refresh = browser__refresh, - .seek = ui_browser__list_head_seek, + .seek = browser__seek, .write = browser__write, .priv = he, .extra_title_lines = 1, From af73856e9ac818bfe8e2f95b32734acbcabe6689 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 12 Aug 2024 12:44:46 -0700 Subject: [PATCH 110/347] perf annotate-data: Implement folding in TUI browser Like 'perf report', use 'e' or 'E' key to toggle folding the current entry so that it can control displaying child entries. Note I didn't add the 'c' and 'C' key to collapse the entry because it's also handled with the 'e'/'E' since it toggles the state. Committer testing: Do some 'perf mem record' for some workload of the whole system, using the target options, as usual (--pid/-p, -C/--cpu, -a for the system wide profiling, etc) and then: # perf annotate --skip-empty --data-type=pthread_mutex_t That, by default, will start as --tui, then press 'E' to see the whole struct unfolded, etc. Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240812194447.2049187-3-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/annotate-data.c | 98 ++++++++++++++++++++++++-- 1 file changed, 92 insertions(+), 6 deletions(-) diff --git a/tools/perf/ui/browsers/annotate-data.c b/tools/perf/ui/browsers/annotate-data.c index 04c73b67cd6c..a5c5ad63425e 100644 --- a/tools/perf/ui/browsers/annotate-data.c +++ b/tools/perf/ui/browsers/annotate-data.c @@ -18,12 +18,6 @@ #define UNFOLD_SIGN '-' #define NOCHLD_SIGN ' ' -struct annotated_data_browser { - struct ui_browser b; - struct list_head entries; - int nr_events; -}; - struct browser_entry { struct list_head node; struct annotated_member *data; @@ -35,6 +29,13 @@ struct browser_entry { bool folded; /* only can be false when it has children */ }; +struct annotated_data_browser { + struct ui_browser b; + struct list_head entries; + struct browser_entry *curr; + int nr_events; +}; + static struct annotated_data_browser *get_browser(struct ui_browser *uib) { return container_of(uib, struct annotated_data_browser, b); @@ -302,6 +303,7 @@ static void browser__seek(struct ui_browser *uib, off_t offset, int whence) static unsigned int browser__refresh(struct ui_browser *uib) { + struct annotated_data_browser *browser = get_browser(uib); struct browser_entry *entry, *next; int row = 0; @@ -314,6 +316,8 @@ static unsigned int browser__refresh(struct ui_browser *uib) if (!uib->filter || !uib->filter(uib, &entry->node)) { ui_browser__gotorc(uib, row, 0); uib->write(uib, entry, row); + if (uib->top_idx + row == uib->index) + browser->curr = entry; if (++row == uib->rows) break; } @@ -438,6 +442,78 @@ static void browser__write(struct ui_browser *uib, void *entry, int row) ui_browser__write_nstring(uib, "", uib->width); } +static void annotated_data_browser__fold(struct annotated_data_browser *browser, + struct browser_entry *entry, + bool recursive) +{ + struct browser_entry *child; + + if (list_empty(&entry->children)) + return; + if (entry->folded && !recursive) + return; + + if (recursive) { + list_for_each_entry(child, &entry->children, node) + annotated_data_browser__fold(browser, child, true); + } + + entry->nr_entries = 1; + entry->folded = true; +} + +static void annotated_data_browser__unfold(struct annotated_data_browser *browser, + struct browser_entry *entry, + bool recursive) +{ + struct browser_entry *child; + int nr_entries; + + if (list_empty(&entry->children)) + return; + if (!entry->folded && !recursive) + return; + + nr_entries = 1; /* for self */ + list_for_each_entry(child, &entry->children, node) { + if (recursive) + annotated_data_browser__unfold(browser, child, true); + + nr_entries += child->nr_entries; + } + + entry->nr_entries = nr_entries; + entry->folded = false; +} + +static void annotated_data_browser__toggle_fold(struct annotated_data_browser *browser, + bool recursive) +{ + struct browser_entry *curr = browser->curr; + struct browser_entry *parent; + + parent = curr->parent; + while (parent) { + parent->nr_entries -= curr->nr_entries; + parent = parent->parent; + } + browser->b.nr_entries -= curr->nr_entries; + + if (curr->folded) + annotated_data_browser__unfold(browser, curr, recursive); + else + annotated_data_browser__fold(browser, curr, recursive); + + parent = curr->parent; + while (parent) { + parent->nr_entries += curr->nr_entries; + parent = parent->parent; + } + browser->b.nr_entries += curr->nr_entries; + + assert(browser->b.nr_entries == count_visible_entries(browser)); +} + static int annotated_data_browser__run(struct annotated_data_browser *browser, struct evsel *evsel __maybe_unused, struct hist_browser_timer *hbt) @@ -462,8 +538,18 @@ static int annotated_data_browser__run(struct annotated_data_browser *browser, "UP/DOWN/PGUP\n" "PGDN/SPACE Navigate\n" " Move to prev/next symbol\n" + "e Expand/Collapse current entry\n" + "E Expand/Collapse all children of the current\n" "q/ESC/CTRL+C Exit\n\n"); continue; + case 'e': + annotated_data_browser__toggle_fold(browser, + /*recursive=*/false); + break; + case 'E': + annotated_data_browser__toggle_fold(browser, + /*recursive=*/true); + break; case K_LEFT: case '<': case '>': From 7f3c8f13ad93044ab1d18949b8b840fed7c59c30 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 12 Aug 2024 12:44:47 -0700 Subject: [PATCH 111/347] perf annotate-data: Show first-level children by default in TUI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now default is to fold everything but it only shows the name of the top-level data type which is not very useful. Instead just expand the top level entry so that it can show the layout at a higher level. Annotate type: 'struct task_struct' (4 samples) Percent Offset Size Field - 100.00 0 9792 struct task_struct { ◆ + 0.50 0 24 struct thread_info thread_info; ▒ 0.00 24 4 unsigned int __state; ▒ 0.00 32 8 void* stack; ▒ + 0.00 40 4 refcount_t usage; ▒ 0.00 44 4 unsigned int flags; ▒ 0.00 48 4 unsigned int ptrace; ▒ 0.00 52 4 int on_cpu; ▒ + 0.00 56 16 struct __call_single_node wake_entry; ▒ 0.00 72 4 unsigned int wakee_flips; ▒ 0.00 80 8 long unsigned int wakee_flip_decay_ts;▒ 0.00 88 8 struct task_struct* last_wakee; ▒ 0.00 96 4 int recent_used_cpu; ▒ 0.00 100 4 int wake_cpu; ▒ 0.00 104 4 int on_rq; ▒ 0.00 108 4 int prio; ▒ 0.00 112 4 int static_prio; ▒ 0.00 116 4 int normal_prio; ▒ 0.00 120 4 unsigned int rt_priority; ▒ + 0.00 128 256 struct sched_entity se; ▒ + 0.00 384 48 struct sched_rt_entity rt; ▒ + 0.00 432 224 struct sched_dl_entity dl; ▒ 0.00 656 8 struct sched_class* sched_class; ▒ ... Committer testing: # perf mem record -a sleep 5s # perf annotate --group --data-type=pthread_mutex_t Annotate type: 'pthread_mutex_t' (13 samples) Percent Offset Size Field - 100.00 0 40 pthread_mutex_t { ▒ - 100.00 0 40 struct __pthread_mutex_s __data { ▒ 39.45 0 4 int __lock; ▒ 0.00 4 4 unsigned int __count; ▒ 7.80 8 4 int __owner; ▒ 6.88 12 4 unsigned int __nusers; ▒ 45.87 16 4 int __kind; ▒ 0.00 20 2 short int __spins; ▒ 0.00 22 2 short int __elision; ▒ + 0.00 24 16 __pthread_list_t __list; ▒ }; ▒ 0.00 0 0 char[] __size; ▒ 39.45 0 8 long int __align; Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240812194447.2049187-4-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/annotate-data.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tools/perf/ui/browsers/annotate-data.c b/tools/perf/ui/browsers/annotate-data.c index a5c5ad63425e..f563a3bb072c 100644 --- a/tools/perf/ui/browsers/annotate-data.c +++ b/tools/perf/ui/browsers/annotate-data.c @@ -596,9 +596,17 @@ int hist_entry__annotate_data_tui(struct hist_entry *he, struct evsel *evsel, } ret = annotated_data_browser__collect_entries(&browser); - if (ret == 0) - ret = annotated_data_browser__run(&browser, evsel, hbt); + if (ret < 0) + goto out; + /* To get the top and current entry */ + browser__refresh(&browser.b); + /* Show the first-level child entries by default */ + annotated_data_browser__toggle_fold(&browser, /*recursive=*/false); + + ret = annotated_data_browser__run(&browser, evsel, hbt); + +out: annotated_data_browser__delete_entries(&browser); return ret; From 79bcd34e0f3da39fda841406ccc957405e724852 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 29 Jul 2024 15:06:20 -0700 Subject: [PATCH 112/347] perf inject: Fix leader sampling inserting additional samples The processing of leader samples would turn an individual sample with a group of read values into multiple samples. 'perf inject' would pass through the additional samples increasing the output data file size: $ perf record -g -e "{instructions,cycles}:S" -o perf.orig.data true $ perf script -D -i perf.orig.data | sed -e 's/perf.orig.data/perf.data/g' > orig.txt $ perf inject -i perf.orig.data -o perf.new.data $ perf script -D -i perf.new.data | sed -e 's/perf.new.data/perf.data/g' > new.txt $ diff -u orig.txt new.txt --- orig.txt 2024-07-29 14:29:40.606576769 -0700 +++ new.txt 2024-07-29 14:30:04.142737434 -0700 ... -0xc550@perf.data [0x30]: event: 3 +0xc550@perf.data [0xd0]: event: 9 +. +. ... raw event: size 208 bytes +. 0000: 09 00 00 00 01 00 d0 00 fc 72 01 86 ff ff ff ff .........r...... +. 0010: 74 7d 2c 00 74 7d 2c 00 fb c3 79 f9 ba d5 05 00 t},.t},...y..... +. 0020: e6 cb 1a 00 00 00 00 00 01 00 00 00 00 00 00 00 ................ +. 0030: 02 00 00 00 00 00 00 00 76 01 00 00 00 00 00 00 ........v....... +. 0040: e6 cb 1a 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ +. 0050: 62 18 00 00 00 00 00 00 f6 cb 1a 00 00 00 00 00 b............... +. 0060: 00 00 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 ................ +. 0070: 80 ff ff ff ff ff ff ff fc 72 01 86 ff ff ff ff .........r...... +. 0080: f3 0e 6e 85 ff ff ff ff 0c cb 7f 85 ff ff ff ff ..n............. +. 0090: bc f2 87 85 ff ff ff ff 44 af 7f 85 ff ff ff ff ........D....... +. 00a0: bd be 7f 85 ff ff ff ff 26 d0 7f 85 ff ff ff ff ........&....... +. 00b0: 6d a4 ff 85 ff ff ff ff ea 00 20 86 ff ff ff ff m......... ..... +. 00c0: 00 fe ff ff ff ff ff ff 57 14 4f 43 fc 7e 00 00 ........W.OC.~.. + +1642373909693435 0xc550 [0xd0]: PERF_RECORD_SAMPLE(IP, 0x1): 2915700/2915700: 0xffffffff860172fc period: 1 addr: 0 +... FP chain: nr:12 +..... 0: ffffffffffffff80 +..... 1: ffffffff860172fc +..... 2: ffffffff856e0ef3 +..... 3: ffffffff857fcb0c +..... 4: ffffffff8587f2bc +..... 5: ffffffff857faf44 +..... 6: ffffffff857fbebd +..... 7: ffffffff857fd026 +..... 8: ffffffff85ffa46d +..... 9: ffffffff862000ea +..... 10: fffffffffffffe00 +..... 11: 00007efc434f1457 +... sample_read: +.... group nr 2 +..... id 00000000001acbe6, value 0000000000000176, lost 0 +..... id 00000000001acbf6, value 0000000000001862, lost 0 + +0xc620@perf.data [0x30]: event: 3 ... This behavior is incorrect as in the case above 'perf inject' should have done nothing. Fix this behavior by disabling separating samples for a tool that requests it. Only request this for `perf inject` so as to not affect other perf tools. With the patch and the test above there are no differences between the orig.txt and new.txt. Fixes: e4caec0d1af3d608 ("perf evsel: Add PERF_SAMPLE_READ sample related processing") Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andi Kleen Cc: Ingo Molnar Cc: Jiri Olsa Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240729220620.2957754-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 1 + tools/perf/util/session.c | 3 +++ tools/perf/util/tool.h | 1 + 3 files changed, 5 insertions(+) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 7b4a5d56d279..8f3dd8980da0 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -2204,6 +2204,7 @@ int cmd_inject(int argc, const char **argv) .finished_init = perf_event__repipe_op2_synth, .compressed = perf_event__repipe_op4_synth, .auxtrace = perf_event__repipe_auxtrace, + .dont_split_sample_group = true, }, .input_name = "-", .samples = LIST_HEAD_INIT(inject.samples), diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index f9072e003367..453ca0297c80 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1513,6 +1513,9 @@ static int deliver_sample_group(struct evlist *evlist, int ret = -EINVAL; struct sample_read_value *v = sample->read.group.values; + if (tool->dont_split_sample_group) + return deliver_sample_value(evlist, tool, event, sample, v, machine); + sample_read_group__for_each(v, sample->read.group.nr, read_format) { ret = deliver_sample_value(evlist, tool, event, sample, v, machine); diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index c957fb849ac6..62bbc9cec151 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -85,6 +85,7 @@ struct perf_tool { bool namespace_events; bool cgroup_events; bool no_warn; + bool dont_split_sample_group; enum show_feature_header show_feat_hdr; }; From 4e322c785514e85c5ede49c54d72795a5573fa79 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:46:53 -0700 Subject: [PATCH 113/347] perf auxtrace: Remove dummy tools Add perf_session__deliver_synth_attr_event that synthesizes a perf_record_header_attr event with one id. Remove use of perf_event__synthesize_attr that necessitates the use of the dummy tool in order to pass the session. Reviewed-by: Adrian Hunter Signed-off-by: Ian Rogers Tested-by: Adrian Hunter Tested-by: Leo Yan Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/arm-spe.c | 49 ++++++++----------------------------- tools/perf/util/cs-etm.c | 33 ++----------------------- tools/perf/util/intel-bts.c | 31 +---------------------- tools/perf/util/intel-pt.c | 24 +----------------- tools/perf/util/session.c | 24 ++++++++++++++++++ tools/perf/util/session.h | 3 +++ 6 files changed, 41 insertions(+), 123 deletions(-) diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c index afbd5869f6bf..fa40f3cb6266 100644 --- a/tools/perf/util/arm-spe.c +++ b/tools/perf/util/arm-spe.c @@ -1073,35 +1073,6 @@ static void arm_spe_print_info(__u64 *arr) fprintf(stdout, arm_spe_info_fmts[ARM_SPE_PMU_TYPE], arr[ARM_SPE_PMU_TYPE]); } -struct arm_spe_synth { - struct perf_tool dummy_tool; - struct perf_session *session; -}; - -static int arm_spe_event_synth(struct perf_tool *tool, - union perf_event *event, - struct perf_sample *sample __maybe_unused, - struct machine *machine __maybe_unused) -{ - struct arm_spe_synth *arm_spe_synth = - container_of(tool, struct arm_spe_synth, dummy_tool); - - return perf_session__deliver_synth_event(arm_spe_synth->session, - event, NULL); -} - -static int arm_spe_synth_event(struct perf_session *session, - struct perf_event_attr *attr, u64 id) -{ - struct arm_spe_synth arm_spe_synth; - - memset(&arm_spe_synth, 0, sizeof(struct arm_spe_synth)); - arm_spe_synth.session = session; - - return perf_event__synthesize_attr(&arm_spe_synth.dummy_tool, attr, 1, - &id, arm_spe_event_synth); -} - static void arm_spe_set_event_name(struct evlist *evlist, u64 id, const char *name) { @@ -1172,7 +1143,7 @@ arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session) spe->sample_flc = true; /* Level 1 data cache miss */ - err = arm_spe_synth_event(session, &attr, id); + err = perf_session__deliver_synth_attr_event(session, &attr, id); if (err) return err; spe->l1d_miss_id = id; @@ -1180,7 +1151,7 @@ arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session) id += 1; /* Level 1 data cache access */ - err = arm_spe_synth_event(session, &attr, id); + err = perf_session__deliver_synth_attr_event(session, &attr, id); if (err) return err; spe->l1d_access_id = id; @@ -1192,7 +1163,7 @@ arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session) spe->sample_llc = true; /* Last level cache miss */ - err = arm_spe_synth_event(session, &attr, id); + err = perf_session__deliver_synth_attr_event(session, &attr, id); if (err) return err; spe->llc_miss_id = id; @@ -1200,7 +1171,7 @@ arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session) id += 1; /* Last level cache access */ - err = arm_spe_synth_event(session, &attr, id); + err = perf_session__deliver_synth_attr_event(session, &attr, id); if (err) return err; spe->llc_access_id = id; @@ -1212,7 +1183,7 @@ arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session) spe->sample_tlb = true; /* TLB miss */ - err = arm_spe_synth_event(session, &attr, id); + err = perf_session__deliver_synth_attr_event(session, &attr, id); if (err) return err; spe->tlb_miss_id = id; @@ -1220,7 +1191,7 @@ arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session) id += 1; /* TLB access */ - err = arm_spe_synth_event(session, &attr, id); + err = perf_session__deliver_synth_attr_event(session, &attr, id); if (err) return err; spe->tlb_access_id = id; @@ -1232,7 +1203,7 @@ arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session) spe->sample_branch = true; /* Branch miss */ - err = arm_spe_synth_event(session, &attr, id); + err = perf_session__deliver_synth_attr_event(session, &attr, id); if (err) return err; spe->branch_miss_id = id; @@ -1244,7 +1215,7 @@ arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session) spe->sample_remote_access = true; /* Remote access */ - err = arm_spe_synth_event(session, &attr, id); + err = perf_session__deliver_synth_attr_event(session, &attr, id); if (err) return err; spe->remote_access_id = id; @@ -1255,7 +1226,7 @@ arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session) if (spe->synth_opts.mem) { spe->sample_memory = true; - err = arm_spe_synth_event(session, &attr, id); + err = perf_session__deliver_synth_attr_event(session, &attr, id); if (err) return err; spe->memory_id = id; @@ -1276,7 +1247,7 @@ arm_spe_synth_events(struct arm_spe *spe, struct perf_session *session) attr.config = PERF_COUNT_HW_INSTRUCTIONS; attr.sample_period = spe->synth_opts.period; spe->instructions_sample_period = attr.sample_period; - err = arm_spe_synth_event(session, &attr, id); + err = perf_session__deliver_synth_attr_event(session, &attr, id); if (err) return err; spe->instructions_id = id; diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index d3e9c64d17d4..566498ebc694 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -1599,35 +1599,6 @@ static int cs_etm__synth_branch_sample(struct cs_etm_queue *etmq, return ret; } -struct cs_etm_synth { - struct perf_tool dummy_tool; - struct perf_session *session; -}; - -static int cs_etm__event_synth(struct perf_tool *tool, - union perf_event *event, - struct perf_sample *sample __maybe_unused, - struct machine *machine __maybe_unused) -{ - struct cs_etm_synth *cs_etm_synth = - container_of(tool, struct cs_etm_synth, dummy_tool); - - return perf_session__deliver_synth_event(cs_etm_synth->session, - event, NULL); -} - -static int cs_etm__synth_event(struct perf_session *session, - struct perf_event_attr *attr, u64 id) -{ - struct cs_etm_synth cs_etm_synth; - - memset(&cs_etm_synth, 0, sizeof(struct cs_etm_synth)); - cs_etm_synth.session = session; - - return perf_event__synthesize_attr(&cs_etm_synth.dummy_tool, attr, 1, - &id, cs_etm__event_synth); -} - static int cs_etm__synth_events(struct cs_etm_auxtrace *etm, struct perf_session *session) { @@ -1679,7 +1650,7 @@ static int cs_etm__synth_events(struct cs_etm_auxtrace *etm, attr.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS; attr.sample_period = 1; attr.sample_type |= PERF_SAMPLE_ADDR; - err = cs_etm__synth_event(session, &attr, id); + err = perf_session__deliver_synth_attr_event(session, &attr, id); if (err) return err; etm->branches_sample_type = attr.sample_type; @@ -1702,7 +1673,7 @@ static int cs_etm__synth_events(struct cs_etm_auxtrace *etm, attr.config = PERF_COUNT_HW_INSTRUCTIONS; attr.sample_period = etm->synth_opts.period; etm->instructions_sample_period = attr.sample_period; - err = cs_etm__synth_event(session, &attr, id); + err = perf_session__deliver_synth_attr_event(session, &attr, id); if (err) return err; etm->instructions_sample_type = attr.sample_type; diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index ec1b3bd9f530..160d346cb450 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c @@ -737,35 +737,6 @@ static bool intel_bts_evsel_is_auxtrace(struct perf_session *session, return evsel->core.attr.type == bts->pmu_type; } -struct intel_bts_synth { - struct perf_tool dummy_tool; - struct perf_session *session; -}; - -static int intel_bts_event_synth(struct perf_tool *tool, - union perf_event *event, - struct perf_sample *sample __maybe_unused, - struct machine *machine __maybe_unused) -{ - struct intel_bts_synth *intel_bts_synth = - container_of(tool, struct intel_bts_synth, dummy_tool); - - return perf_session__deliver_synth_event(intel_bts_synth->session, - event, NULL); -} - -static int intel_bts_synth_event(struct perf_session *session, - struct perf_event_attr *attr, u64 id) -{ - struct intel_bts_synth intel_bts_synth; - - memset(&intel_bts_synth, 0, sizeof(struct intel_bts_synth)); - intel_bts_synth.session = session; - - return perf_event__synthesize_attr(&intel_bts_synth.dummy_tool, attr, 1, - &id, intel_bts_event_synth); -} - static int intel_bts_synth_events(struct intel_bts *bts, struct perf_session *session) { @@ -814,7 +785,7 @@ static int intel_bts_synth_events(struct intel_bts *bts, attr.sample_type |= PERF_SAMPLE_ADDR; pr_debug("Synthesizing 'branches' event with id %" PRIu64 " sample type %#" PRIx64 "\n", id, (u64)attr.sample_type); - err = intel_bts_synth_event(session, &attr, id); + err = perf_session__deliver_synth_attr_event(session, &attr, id); if (err) { pr_err("%s: failed to synthesize 'branches' event type\n", __func__); diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index d6d7b7512505..1fc03bfd14c3 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -3659,37 +3659,15 @@ static int intel_pt_queue_data(struct perf_session *session, data_offset, timestamp); } -struct intel_pt_synth { - struct perf_tool dummy_tool; - struct perf_session *session; -}; - -static int intel_pt_event_synth(struct perf_tool *tool, - union perf_event *event, - struct perf_sample *sample __maybe_unused, - struct machine *machine __maybe_unused) -{ - struct intel_pt_synth *intel_pt_synth = - container_of(tool, struct intel_pt_synth, dummy_tool); - - return perf_session__deliver_synth_event(intel_pt_synth->session, event, - NULL); -} - static int intel_pt_synth_event(struct perf_session *session, const char *name, struct perf_event_attr *attr, u64 id) { - struct intel_pt_synth intel_pt_synth; int err; pr_debug("Synthesizing '%s' event with id %" PRIu64 " sample type %#" PRIx64 "\n", name, id, (u64)attr->sample_type); - memset(&intel_pt_synth, 0, sizeof(struct intel_pt_synth)); - intel_pt_synth.session = session; - - err = perf_event__synthesize_attr(&intel_pt_synth.dummy_tool, attr, 1, - &id, intel_pt_event_synth); + err = perf_session__deliver_synth_attr_event(session, attr, id); if (err) pr_err("%s: failed to synthesize '%s' event type\n", __func__, name); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 453ca0297c80..aa02c1d7b1f9 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1769,6 +1769,30 @@ int perf_session__deliver_synth_event(struct perf_session *session, return machines__deliver_event(&session->machines, evlist, event, sample, tool, 0, NULL); } +int perf_session__deliver_synth_attr_event(struct perf_session *session, + const struct perf_event_attr *attr, + u64 id) +{ + union { + struct { + struct perf_record_header_attr attr; + u64 ids[1]; + } attr_id; + union perf_event ev; + } ev = { + .attr_id.attr.header.type = PERF_RECORD_HEADER_ATTR, + .attr_id.attr.header.size = sizeof(ev.attr_id), + .attr_id.ids[0] = id, + }; + + if (attr->size != sizeof(ev.attr_id.attr.attr)) { + pr_debug("Unexpected perf_event_attr size\n"); + return -EINVAL; + } + ev.attr_id.attr.attr = *attr; + return perf_session__deliver_synth_event(session, &ev.ev, NULL); +} + static void event_swap(union perf_event *event, bool sample_id_all) { perf_event__swap_op swap; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 4c29dc86956f..cc28976bb00e 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -154,6 +154,9 @@ extern volatile int session_done; int perf_session__deliver_synth_event(struct perf_session *session, union perf_event *event, struct perf_sample *sample); +int perf_session__deliver_synth_attr_event(struct perf_session *session, + const struct perf_event_attr *attr, + u64 id); int perf_session__dsos_hit_all(struct perf_session *session); From 1816dc4bc5be050cc543e6e4fcedf3805a2aea20 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:46:54 -0700 Subject: [PATCH 114/347] perf s390-cpumsf: Remove unused struct struct s390_cpumsf_synth was likely cargo culted from other auxtrace examples. It has no users, so remove. Reviewed-by: Adrian Hunter Signed-off-by: Ian Rogers Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-3-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/s390-cpumsf.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tools/perf/util/s390-cpumsf.c b/tools/perf/util/s390-cpumsf.c index 6fe478b0b61b..4ec583e511af 100644 --- a/tools/perf/util/s390-cpumsf.c +++ b/tools/perf/util/s390-cpumsf.c @@ -952,11 +952,6 @@ s390_cpumsf_process_event(struct perf_session *session, return err; } -struct s390_cpumsf_synth { - struct perf_tool cpumsf_tool; - struct perf_session *session; -}; - static int s390_cpumsf_process_auxtrace_event(struct perf_session *session, union perf_event *event __maybe_unused, From 30f29bae9142f34e978a4861ed07aa512af21416 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:46:55 -0700 Subject: [PATCH 115/347] perf tool: Constify tool pointers The tool pointer (to a struct largely of function pointers) is passed around but is unchanged except at initialization. Change parameter and variable types to be const to lower the possibilities of what could happen with a tool. Reviewed-by: Adrian Hunter Signed-off-by: Ian Rogers Tested-by: Adrian Hunter Tested-by: Leo Yan Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-4-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/util/event.c | 4 +- tools/perf/bench/synthesize.c | 2 +- tools/perf/builtin-annotate.c | 2 +- tools/perf/builtin-c2c.c | 2 +- tools/perf/builtin-diff.c | 2 +- tools/perf/builtin-inject.c | 70 ++++++++++++------------- tools/perf/builtin-kmem.c | 3 +- tools/perf/builtin-kvm.c | 4 +- tools/perf/builtin-kwork.c | 22 ++++---- tools/perf/builtin-lock.c | 4 +- tools/perf/builtin-mem.c | 4 +- tools/perf/builtin-record.c | 14 ++--- tools/perf/builtin-report.c | 12 ++--- tools/perf/builtin-sched.c | 34 ++++++------ tools/perf/builtin-script.c | 41 ++++++++------- tools/perf/builtin-stat.c | 8 +-- tools/perf/builtin-timechart.c | 8 +-- tools/perf/builtin-top.c | 2 +- tools/perf/builtin-trace.c | 4 +- tools/perf/tests/cpumap.c | 6 +-- tools/perf/tests/dlfilter-test.c | 2 +- tools/perf/tests/dwarf-unwind.c | 2 +- tools/perf/tests/event_update.c | 8 +-- tools/perf/tests/stat.c | 6 +-- tools/perf/tests/thread-map.c | 2 +- tools/perf/util/arm-spe.c | 6 +-- tools/perf/util/auxtrace.c | 12 ++--- tools/perf/util/auxtrace.h | 20 ++++---- tools/perf/util/bpf-event.c | 4 +- tools/perf/util/build-id.c | 4 +- tools/perf/util/build-id.h | 4 +- tools/perf/util/cs-etm.c | 6 +-- tools/perf/util/data-convert-bt.c | 4 +- tools/perf/util/data-convert-json.c | 4 +- tools/perf/util/event.c | 34 ++++++------ tools/perf/util/event.h | 34 ++++++------ tools/perf/util/header.c | 6 +-- tools/perf/util/header.h | 4 +- tools/perf/util/hisi-ptt.c | 6 +-- tools/perf/util/intel-bts.c | 6 +-- tools/perf/util/intel-pt.c | 6 +-- tools/perf/util/jitdump.c | 4 +- tools/perf/util/s390-cpumsf.c | 6 +-- tools/perf/util/session.c | 30 +++++------ tools/perf/util/session.h | 2 +- tools/perf/util/synthetic-events.c | 80 ++++++++++++++--------------- tools/perf/util/synthetic-events.h | 68 ++++++++++++------------ tools/perf/util/tool.h | 8 +-- tools/perf/util/tsc.c | 2 +- 49 files changed, 313 insertions(+), 315 deletions(-) diff --git a/tools/perf/arch/x86/util/event.c b/tools/perf/arch/x86/util/event.c index e65b7dbe27fb..a0400707180c 100644 --- a/tools/perf/arch/x86/util/event.c +++ b/tools/perf/arch/x86/util/event.c @@ -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) { diff --git a/tools/perf/bench/synthesize.c b/tools/perf/bench/synthesize.c index 7401ebbac100..9b333276cbdb 100644 --- a/tools/perf/bench/synthesize.c +++ b/tools/perf/bench/synthesize.c @@ -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) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 1bfe41783a7c..cc65e6f8f4da 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -279,7 +279,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, diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index c157bd31f2e5..88c131d05186 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -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, diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 57d300d8e570..4c0567882a7a 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -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, diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 8f3dd8980da0..283429ccd034 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -134,7 +134,7 @@ struct event_entry { union perf_event event[]; }; -static int dso__inject_build_id(struct dso *dso, struct perf_tool *tool, +static int dso__inject_build_id(struct dso *dso, const struct perf_tool *tool, struct machine *machine, u8 cpumode, u32 flags); static int output_bytes(struct perf_inject *inject, void *buf, size_t sz) @@ -149,7 +149,7 @@ static int output_bytes(struct perf_inject *inject, void *buf, size_t sz) return 0; } -static int perf_event__repipe_synth(struct perf_tool *tool, +static int perf_event__repipe_synth(const struct perf_tool *tool, union perf_event *event) { struct perf_inject *inject = container_of(tool, struct perf_inject, @@ -158,7 +158,7 @@ static int perf_event__repipe_synth(struct perf_tool *tool, return output_bytes(inject, event, event->header.size); } -static int perf_event__repipe_oe_synth(struct perf_tool *tool, +static int perf_event__repipe_oe_synth(const struct perf_tool *tool, union perf_event *event, struct ordered_events *oe __maybe_unused) { @@ -166,7 +166,7 @@ static int perf_event__repipe_oe_synth(struct perf_tool *tool, } #ifdef HAVE_JITDUMP -static int perf_event__drop_oe(struct perf_tool *tool __maybe_unused, +static int perf_event__drop_oe(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct ordered_events *oe __maybe_unused) { @@ -188,7 +188,7 @@ static int perf_event__repipe_op4_synth(struct perf_session *session, return perf_event__repipe_synth(session->tool, event); } -static int perf_event__repipe_attr(struct perf_tool *tool, +static int perf_event__repipe_attr(const struct perf_tool *tool, union perf_event *event, struct evlist **pevlist) { @@ -206,7 +206,7 @@ static int perf_event__repipe_attr(struct perf_tool *tool, return perf_event__repipe_synth(tool, event); } -static int perf_event__repipe_event_update(struct perf_tool *tool, +static int perf_event__repipe_event_update(const struct perf_tool *tool, union perf_event *event, struct evlist **pevlist __maybe_unused) { @@ -237,7 +237,7 @@ static int copy_bytes(struct perf_inject *inject, struct perf_data *data, off_t static s64 perf_event__repipe_auxtrace(struct perf_session *session, union perf_event *event) { - struct perf_tool *tool = session->tool; + const struct perf_tool *tool = session->tool; struct perf_inject *inject = container_of(tool, struct perf_inject, tool); int ret; @@ -284,7 +284,7 @@ perf_event__repipe_auxtrace(struct perf_session *session __maybe_unused, #endif -static int perf_event__repipe(struct perf_tool *tool, +static int perf_event__repipe(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -292,7 +292,7 @@ static int perf_event__repipe(struct perf_tool *tool, return perf_event__repipe_synth(tool, event); } -static int perf_event__drop(struct perf_tool *tool __maybe_unused, +static int perf_event__drop(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -300,7 +300,7 @@ static int perf_event__drop(struct perf_tool *tool __maybe_unused, return 0; } -static int perf_event__drop_aux(struct perf_tool *tool, +static int perf_event__drop_aux(const struct perf_tool *tool, union perf_event *event __maybe_unused, struct perf_sample *sample, struct machine *machine __maybe_unused) @@ -341,13 +341,13 @@ perf_inject__cut_auxtrace_sample(struct perf_inject *inject, return ev; } -typedef int (*inject_handler)(struct perf_tool *tool, +typedef int (*inject_handler)(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct evsel *evsel, struct machine *machine); -static int perf_event__repipe_sample(struct perf_tool *tool, +static int perf_event__repipe_sample(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct evsel *evsel, @@ -372,7 +372,7 @@ static int perf_event__repipe_sample(struct perf_tool *tool, return perf_event__repipe_synth(tool, event); } -static int perf_event__repipe_mmap(struct perf_tool *tool, +static int perf_event__repipe_mmap(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -386,7 +386,7 @@ static int perf_event__repipe_mmap(struct perf_tool *tool, } #ifdef HAVE_JITDUMP -static int perf_event__jit_repipe_mmap(struct perf_tool *tool, +static int perf_event__jit_repipe_mmap(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -455,7 +455,7 @@ static struct dso *findnew_dso(int pid, int tid, const char *filename, return dso; } -static int perf_event__repipe_buildid_mmap(struct perf_tool *tool, +static int perf_event__repipe_buildid_mmap(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -474,7 +474,7 @@ static int perf_event__repipe_buildid_mmap(struct perf_tool *tool, return perf_event__repipe(tool, event, sample, machine); } -static int perf_event__repipe_mmap2(struct perf_tool *tool, +static int perf_event__repipe_mmap2(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -500,7 +500,7 @@ static int perf_event__repipe_mmap2(struct perf_tool *tool, } #ifdef HAVE_JITDUMP -static int perf_event__jit_repipe_mmap2(struct perf_tool *tool, +static int perf_event__jit_repipe_mmap2(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -524,7 +524,7 @@ static int perf_event__jit_repipe_mmap2(struct perf_tool *tool, } #endif -static int perf_event__repipe_buildid_mmap2(struct perf_tool *tool, +static int perf_event__repipe_buildid_mmap2(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -565,7 +565,7 @@ static int perf_event__repipe_buildid_mmap2(struct perf_tool *tool, return 0; } -static int perf_event__repipe_fork(struct perf_tool *tool, +static int perf_event__repipe_fork(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -578,7 +578,7 @@ static int perf_event__repipe_fork(struct perf_tool *tool, return err; } -static int perf_event__repipe_comm(struct perf_tool *tool, +static int perf_event__repipe_comm(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -591,7 +591,7 @@ static int perf_event__repipe_comm(struct perf_tool *tool, return err; } -static int perf_event__repipe_namespaces(struct perf_tool *tool, +static int perf_event__repipe_namespaces(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -603,7 +603,7 @@ static int perf_event__repipe_namespaces(struct perf_tool *tool, return err; } -static int perf_event__repipe_exit(struct perf_tool *tool, +static int perf_event__repipe_exit(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -712,7 +712,7 @@ static bool perf_inject__lookup_known_build_id(struct perf_inject *inject, return false; } -static int dso__inject_build_id(struct dso *dso, struct perf_tool *tool, +static int dso__inject_build_id(struct dso *dso, const struct perf_tool *tool, struct machine *machine, u8 cpumode, u32 flags) { struct perf_inject *inject = container_of(tool, struct perf_inject, @@ -743,7 +743,7 @@ static int dso__inject_build_id(struct dso *dso, struct perf_tool *tool, return 0; } -int perf_event__inject_buildid(struct perf_tool *tool, union perf_event *event, +int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct evsel *evsel __maybe_unused, struct machine *machine) @@ -776,7 +776,7 @@ repipe: return 0; } -static int perf_inject__sched_process_exit(struct perf_tool *tool, +static int perf_inject__sched_process_exit(const struct perf_tool *tool, union perf_event *event __maybe_unused, struct perf_sample *sample, struct evsel *evsel __maybe_unused, @@ -796,7 +796,7 @@ static int perf_inject__sched_process_exit(struct perf_tool *tool, return 0; } -static int perf_inject__sched_switch(struct perf_tool *tool, +static int perf_inject__sched_switch(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct evsel *evsel, @@ -821,7 +821,7 @@ static int perf_inject__sched_switch(struct perf_tool *tool, } #ifdef HAVE_LIBTRACEEVENT -static int perf_inject__sched_stat(struct perf_tool *tool, +static int perf_inject__sched_stat(const struct perf_tool *tool, union perf_event *event __maybe_unused, struct perf_sample *sample, struct evsel *evsel, @@ -866,7 +866,7 @@ static int guest_session__output_bytes(struct guest_session *gs, void *buf, size return ret < 0 ? ret : 0; } -static int guest_session__repipe(struct perf_tool *tool, +static int guest_session__repipe(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -1032,7 +1032,7 @@ static struct guest_id *guest_session__lookup_id(struct guest_session *gs, u64 i return NULL; } -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 perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { @@ -1160,7 +1160,7 @@ static u64 evlist__first_id(struct evlist *evlist) return 0; } -static int process_build_id(struct perf_tool *tool, +static int process_build_id(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -1210,7 +1210,7 @@ static int guest_session__add_build_ids(struct guest_session *gs) gs); } -static int guest_session__ksymbol_event(struct perf_tool *tool, +static int guest_session__ksymbol_event(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -1574,7 +1574,7 @@ static int guest_session__flush_events(struct guest_session *gs) return guest_session__inject_events(gs, -1); } -static int host__repipe(struct perf_tool *tool, +static int host__repipe(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -1647,7 +1647,7 @@ static int host__finished_init(struct perf_session *session, union perf_event *e * guest events up to the same time. Finally write out the FINISHED_ROUND event * itself. */ -static int host__finished_round(struct perf_tool *tool, +static int host__finished_round(const struct perf_tool *tool, union perf_event *event, struct ordered_events *oe) { @@ -1665,7 +1665,7 @@ static int host__finished_round(struct perf_tool *tool, return perf_event__repipe_oe_synth(tool, event, oe); } -static int host__context_switch(struct perf_tool *tool, +static int host__context_switch(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -1719,7 +1719,7 @@ static int evsel__check_stype(struct evsel *evsel, u64 sample_type, const char * return 0; } -static int drop_sample(struct perf_tool *tool __maybe_unused, +static int drop_sample(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_sample *sample __maybe_unused, struct evsel *evsel __maybe_unused, diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 6fd95be5032b..859ff018eace 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -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, @@ -2061,4 +2061,3 @@ out_delete: return ret; } - diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 71165036e4ca..a3b903cf4311 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -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, diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index 56e3f3a5e03a..8ffaa80a2d1d 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -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, diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 0253184b3b58..6efa9d646637 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -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, diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index efa700d14c98..0fb4d75ab959 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -179,7 +179,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) @@ -253,7 +253,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, diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 72345d1e54b0..46410eb3a76b 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -609,7 +609,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) @@ -620,7 +620,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) @@ -705,7 +705,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) @@ -1417,7 +1417,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, @@ -3244,7 +3244,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) { /* @@ -3256,7 +3256,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) { /* @@ -3269,7 +3269,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) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 930052961c1a..5f609a7791ea 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -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, @@ -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, @@ -765,7 +765,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 +777,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) @@ -793,7 +793,7 @@ static int count_lost_samples_event(struct perf_tool *tool, 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); @@ -1233,7 +1233,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) { diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 8750b5f2d49b..2c60bd3a8149 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -1489,7 +1489,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 +1502,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 +1770,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 +1796,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 +1809,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 +1826,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 +1839,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 +1860,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) @@ -2506,7 +2506,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 +2515,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 +2599,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 +2627,7 @@ static int timehist_migrate_task_event(struct perf_tool *tool, return 0; } -static int timehist_sched_change_event(struct perf_tool *tool, +static int timehist_sched_change_event(const struct perf_tool *tool, union perf_event *event, struct evsel *evsel, struct perf_sample *sample, @@ -2758,7 +2758,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 +2767,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 +3010,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, diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 932167b2362b..31a6f800d55f 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -2399,7 +2399,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 +2486,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 +2552,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 +2588,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 +2607,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 +2619,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 +2631,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 +2643,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 +2656,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 +2668,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 +2680,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 +2712,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 +2722,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 +2733,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 +2743,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 +2755,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 +3757,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 +3779,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 +3809,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); diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 1f92445f7480..d44e708f8a44 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -248,7 +248,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) @@ -2180,7 +2180,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 +2229,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 +2248,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; diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 19d4542ea18a..5bf818baa662 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -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, diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index d1a06a88d693..1cdac4e190e9 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -740,7 +740,7 @@ static int hist_iter__top_callback(struct hist_entry_iter *iter, 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, diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index ef951ce1a0dd..d6ca541fdc78 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1778,7 +1778,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) @@ -3179,7 +3179,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, diff --git a/tools/perf/tests/cpumap.c b/tools/perf/tests/cpumap.c index bd8e396f3e57..2f0168b2a5a9 100644 --- a/tools/perf/tests/cpumap.c +++ b/tools/perf/tests/cpumap.c @@ -11,7 +11,7 @@ struct machine; -static int process_event_mask(struct perf_tool *tool __maybe_unused, +static int process_event_mask(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -47,7 +47,7 @@ static int process_event_mask(struct perf_tool *tool __maybe_unused, return 0; } -static int process_event_cpus(struct perf_tool *tool __maybe_unused, +static int process_event_cpus(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -73,7 +73,7 @@ static int process_event_cpus(struct perf_tool *tool __maybe_unused, return 0; } -static int process_event_range_cpus(struct perf_tool *tool __maybe_unused, +static int process_event_range_cpus(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) diff --git a/tools/perf/tests/dlfilter-test.c b/tools/perf/tests/dlfilter-test.c index da3a9b50b1b1..54f59d1246bc 100644 --- a/tools/perf/tests/dlfilter-test.c +++ b/tools/perf/tests/dlfilter-test.c @@ -62,7 +62,7 @@ static int test_result(const char *msg, int ret) return ret; } -static int process(struct perf_tool *tool, union perf_event *event, +static int process(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) { diff --git a/tools/perf/tests/dwarf-unwind.c b/tools/perf/tests/dwarf-unwind.c index d01aa931fe81..f85d391ced98 100644 --- a/tools/perf/tests/dwarf-unwind.c +++ b/tools/perf/tests/dwarf-unwind.c @@ -37,7 +37,7 @@ #define NO_TAIL_CALL_BARRIER __asm__ __volatile__("" : : : "memory"); #endif -static int mmap_handler(struct perf_tool *tool __maybe_unused, +static int mmap_handler(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c index d093a9b878d1..fdecad920f59 100644 --- a/tools/perf/tests/event_update.c +++ b/tools/perf/tests/event_update.c @@ -12,7 +12,7 @@ #include "tests.h" #include "debug.h" -static int process_event_unit(struct perf_tool *tool __maybe_unused, +static int process_event_unit(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -25,7 +25,7 @@ static int process_event_unit(struct perf_tool *tool __maybe_unused, return 0; } -static int process_event_scale(struct perf_tool *tool __maybe_unused, +static int process_event_scale(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -43,7 +43,7 @@ struct event_name { const char *name; }; -static int process_event_name(struct perf_tool *tool, +static int process_event_name(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -57,7 +57,7 @@ static int process_event_name(struct perf_tool *tool, return 0; } -static int process_event_cpus(struct perf_tool *tool __maybe_unused, +static int process_event_cpus(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) diff --git a/tools/perf/tests/stat.c b/tools/perf/tests/stat.c index 706780fb5695..6468cc0d0204 100644 --- a/tools/perf/tests/stat.c +++ b/tools/perf/tests/stat.c @@ -21,7 +21,7 @@ static bool has_term(struct perf_record_stat_config *config, return false; } -static int process_stat_config_event(struct perf_tool *tool __maybe_unused, +static int process_stat_config_event(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -62,7 +62,7 @@ static int test__synthesize_stat_config(struct test_suite *test __maybe_unused, return 0; } -static int process_stat_event(struct perf_tool *tool __maybe_unused, +static int process_stat_event(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -93,7 +93,7 @@ static int test__synthesize_stat(struct test_suite *test __maybe_unused, int sub return 0; } -static int process_stat_round_event(struct perf_tool *tool __maybe_unused, +static int process_stat_round_event(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) diff --git a/tools/perf/tests/thread-map.c b/tools/perf/tests/thread-map.c index 74308c1368fe..1fe521466bf4 100644 --- a/tools/perf/tests/thread-map.c +++ b/tools/perf/tests/thread-map.c @@ -60,7 +60,7 @@ static int test__thread_map(struct test_suite *test __maybe_unused, int subtest return 0; } -static int process_event(struct perf_tool *tool __maybe_unused, +static int process_event(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) diff --git a/tools/perf/util/arm-spe.c b/tools/perf/util/arm-spe.c index fa40f3cb6266..138ffc71b32d 100644 --- a/tools/perf/util/arm-spe.c +++ b/tools/perf/util/arm-spe.c @@ -899,7 +899,7 @@ static int arm_spe_context_switch(struct arm_spe *spe, union perf_event *event, static int arm_spe_process_event(struct perf_session *session, union perf_event *event, struct perf_sample *sample, - struct perf_tool *tool) + const struct perf_tool *tool) { int err = 0; u64 timestamp; @@ -947,7 +947,7 @@ static int arm_spe_process_event(struct perf_session *session, static int arm_spe_process_auxtrace_event(struct perf_session *session, union perf_event *event, - struct perf_tool *tool __maybe_unused) + const struct perf_tool *tool __maybe_unused) { struct arm_spe *spe = container_of(session->auxtrace, struct arm_spe, auxtrace); @@ -985,7 +985,7 @@ static int arm_spe_process_auxtrace_event(struct perf_session *session, } static int arm_spe_flush(struct perf_session *session __maybe_unused, - struct perf_tool *tool __maybe_unused) + const struct perf_tool *tool __maybe_unused) { struct arm_spe *spe = container_of(session->auxtrace, struct arm_spe, auxtrace); diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index e2f317063eec..cbb773ed6f1a 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -1240,7 +1240,7 @@ void auxtrace_synth_error(struct perf_record_auxtrace_error *auxtrace_error, int } int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, - struct perf_tool *tool, + const struct perf_tool *tool, struct perf_session *session, perf_event__handler_t process) { @@ -1831,7 +1831,7 @@ int __weak compat_auxtrace_mmap__write_tail(struct auxtrace_mmap *mm, u64 tail) static int __auxtrace_mmap__read(struct mmap *map, struct auxtrace_record *itr, - struct perf_tool *tool, process_auxtrace_t fn, + const struct perf_tool *tool, process_auxtrace_t fn, bool snapshot, size_t snapshot_size) { struct auxtrace_mmap *mm = &map->auxtrace_mmap; @@ -1942,14 +1942,14 @@ static int __auxtrace_mmap__read(struct mmap *map, } int auxtrace_mmap__read(struct mmap *map, struct auxtrace_record *itr, - struct perf_tool *tool, process_auxtrace_t fn) + const struct perf_tool *tool, process_auxtrace_t fn) { return __auxtrace_mmap__read(map, itr, tool, fn, false, 0); } int auxtrace_mmap__read_snapshot(struct mmap *map, struct auxtrace_record *itr, - struct perf_tool *tool, process_auxtrace_t fn, + const struct perf_tool *tool, process_auxtrace_t fn, size_t snapshot_size) { return __auxtrace_mmap__read(map, itr, tool, fn, true, snapshot_size); @@ -2829,7 +2829,7 @@ int auxtrace_parse_filters(struct evlist *evlist) } int auxtrace__process_event(struct perf_session *session, union perf_event *event, - struct perf_sample *sample, struct perf_tool *tool) + struct perf_sample *sample, const struct perf_tool *tool) { if (!session->auxtrace) return 0; @@ -2847,7 +2847,7 @@ void auxtrace__dump_auxtrace_sample(struct perf_session *session, session->auxtrace->dump_auxtrace_sample(session, sample); } -int auxtrace__flush_events(struct perf_session *session, struct perf_tool *tool) +int auxtrace__flush_events(struct perf_session *session, const struct perf_tool *tool) { if (!session->auxtrace) return 0; diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index 8a6ec9565835..d405efcd8708 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h @@ -208,17 +208,17 @@ struct auxtrace { int (*process_event)(struct perf_session *session, union perf_event *event, struct perf_sample *sample, - struct perf_tool *tool); + const struct perf_tool *tool); int (*process_auxtrace_event)(struct perf_session *session, union perf_event *event, - struct perf_tool *tool); + const struct perf_tool *tool); int (*queue_data)(struct perf_session *session, struct perf_sample *sample, union perf_event *event, u64 data_offset); void (*dump_auxtrace_sample)(struct perf_session *session, struct perf_sample *sample); int (*flush_events)(struct perf_session *session, - struct perf_tool *tool); + const struct perf_tool *tool); void (*free_events)(struct perf_session *session); void (*free)(struct perf_session *session); bool (*evsel_is_auxtrace)(struct perf_session *session, @@ -508,17 +508,17 @@ void auxtrace_mmap_params__set_idx(struct auxtrace_mmap_params *mp, struct evlist *evlist, struct evsel *evsel, int idx); -typedef int (*process_auxtrace_t)(struct perf_tool *tool, +typedef int (*process_auxtrace_t)(const struct perf_tool *tool, struct mmap *map, union perf_event *event, void *data1, size_t len1, void *data2, size_t len2); int auxtrace_mmap__read(struct mmap *map, struct auxtrace_record *itr, - struct perf_tool *tool, process_auxtrace_t fn); + const struct perf_tool *tool, process_auxtrace_t fn); int auxtrace_mmap__read_snapshot(struct mmap *map, struct auxtrace_record *itr, - struct perf_tool *tool, process_auxtrace_t fn, + const struct perf_tool *tool, process_auxtrace_t fn, size_t snapshot_size); int auxtrace_queues__init_nr(struct auxtrace_queues *queues, int nr_queues); @@ -639,10 +639,10 @@ int addr_filters__parse_bare_filter(struct addr_filters *filts, int auxtrace_parse_filters(struct evlist *evlist); int auxtrace__process_event(struct perf_session *session, union perf_event *event, - struct perf_sample *sample, struct perf_tool *tool); + struct perf_sample *sample, const struct perf_tool *tool); void auxtrace__dump_auxtrace_sample(struct perf_session *session, struct perf_sample *sample); -int auxtrace__flush_events(struct perf_session *session, struct perf_tool *tool); +int auxtrace__flush_events(struct perf_session *session, const struct perf_tool *tool); void auxtrace__free_events(struct perf_session *session); void auxtrace__free(struct perf_session *session); bool auxtrace__evsel_is_auxtrace(struct perf_session *session, @@ -809,7 +809,7 @@ static inline int auxtrace__process_event(struct perf_session *session __maybe_unused, union perf_event *event __maybe_unused, struct perf_sample *sample __maybe_unused, - struct perf_tool *tool __maybe_unused) + const struct perf_tool *tool __maybe_unused) { return 0; } @@ -822,7 +822,7 @@ void auxtrace__dump_auxtrace_sample(struct perf_session *session __maybe_unused, static inline int auxtrace__flush_events(struct perf_session *session __maybe_unused, - struct perf_tool *tool __maybe_unused) + const struct perf_tool *tool __maybe_unused) { return 0; } diff --git a/tools/perf/util/bpf-event.c b/tools/perf/util/bpf-event.c index 827695cd0408..13608237c50e 100644 --- a/tools/perf/util/bpf-event.c +++ b/tools/perf/util/bpf-event.c @@ -170,7 +170,7 @@ static int perf_event__synthesize_one_bpf_prog(struct perf_session *session, { struct perf_record_ksymbol *ksymbol_event = &event->ksymbol; struct perf_record_bpf_event *bpf_event = &event->bpf; - struct perf_tool *tool = session->tool; + const struct perf_tool *tool = session->tool; struct bpf_prog_info_node *info_node; struct perf_bpil *info_linear; struct bpf_prog_info *info; @@ -310,7 +310,7 @@ struct kallsyms_parse { union perf_event *event; perf_event__handler_t process; struct machine *machine; - struct perf_tool *tool; + const struct perf_tool *tool; }; static int diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 83a1581e8cf1..098fcc625d91 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -42,7 +42,7 @@ static bool no_buildid_cache; -int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, +int build_id__mark_dso_hit(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct evsel *evsel __maybe_unused, @@ -67,7 +67,7 @@ int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, return 0; } -static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused, +static int perf_event__exit_del_thread(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index 3fa8bffb07ca..ae87c4c58d5b 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -35,11 +35,11 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size, char *__dso__build_id_filename(const struct dso *dso, char *bf, size_t size, bool is_debug, bool is_kallsyms); -int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, +int build_id__mark_dso_hit(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct evsel *evsel, struct machine *machine); -int perf_event__inject_buildid(struct perf_tool *tool, union perf_event *event, +int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct evsel *evsel, struct machine *machine); diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 566498ebc694..34552940ec1d 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -788,7 +788,7 @@ static void cs_etm__dump_event(struct cs_etm_queue *etmq, } static int cs_etm__flush_events(struct perf_session *session, - struct perf_tool *tool) + const struct perf_tool *tool) { struct cs_etm_auxtrace *etm = container_of(session->auxtrace, struct cs_etm_auxtrace, @@ -2715,7 +2715,7 @@ static int cs_etm__process_switch_cpu_wide(struct cs_etm_auxtrace *etm, static int cs_etm__process_event(struct perf_session *session, union perf_event *event, struct perf_sample *sample, - struct perf_tool *tool) + const struct perf_tool *tool) { struct cs_etm_auxtrace *etm = container_of(session->auxtrace, struct cs_etm_auxtrace, @@ -2785,7 +2785,7 @@ static void dump_queued_data(struct cs_etm_auxtrace *etm, static int cs_etm__process_auxtrace_event(struct perf_session *session, union perf_event *event, - struct perf_tool *tool __maybe_unused) + const struct perf_tool *tool __maybe_unused) { struct cs_etm_auxtrace *etm = container_of(session->auxtrace, struct cs_etm_auxtrace, diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index 2b732bccabad..9e2170604b66 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -792,7 +792,7 @@ static bool is_flush_needed(struct ctf_stream *cs) return cs->count >= STREAM_FLUSH_COUNT; } -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, @@ -871,7 +871,7 @@ do { \ } while(0) #define __FUNC_PROCESS_NON_SAMPLE(_name, body) \ -static int process_##_name##_event(struct perf_tool *tool, \ +static int process_##_name##_event(const struct perf_tool *tool, \ union perf_event *_event, \ struct perf_sample *sample, \ struct machine *machine) \ diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c index 3cf64f5b23ee..905ea9823f9d 100644 --- a/tools/perf/util/data-convert-json.c +++ b/tools/perf/util/data-convert-json.c @@ -118,7 +118,7 @@ static void output_json_key_format(FILE *out, bool comma, int depth, va_end(args); } -static void output_sample_callchain_entry(struct perf_tool *tool, +static void output_sample_callchain_entry(const struct perf_tool *tool, u64 ip, struct addr_location *al) { struct convert_json *c = container_of(tool, struct convert_json, tool); @@ -146,7 +146,7 @@ static void output_sample_callchain_entry(struct perf_tool *tool, output_json_format(out, false, 4, "}"); } -static int process_sample_event(struct perf_tool *tool, +static int process_sample_event(const struct perf_tool *tool, union perf_event *event __maybe_unused, struct perf_sample *sample, struct evsel *evsel __maybe_unused, diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index f32f9abf6344..c2f0e7f40ad5 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -216,7 +216,7 @@ size_t perf_event__fprintf_cgroup(union perf_event *event, FILE *fp) event->cgroup.id, event->cgroup.path); } -int perf_event__process_comm(struct perf_tool *tool __maybe_unused, +int perf_event__process_comm(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -224,7 +224,7 @@ int perf_event__process_comm(struct perf_tool *tool __maybe_unused, return machine__process_comm_event(machine, event, sample); } -int perf_event__process_namespaces(struct perf_tool *tool __maybe_unused, +int perf_event__process_namespaces(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -232,7 +232,7 @@ int perf_event__process_namespaces(struct perf_tool *tool __maybe_unused, return machine__process_namespaces_event(machine, event, sample); } -int perf_event__process_cgroup(struct perf_tool *tool __maybe_unused, +int perf_event__process_cgroup(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -240,7 +240,7 @@ int perf_event__process_cgroup(struct perf_tool *tool __maybe_unused, return machine__process_cgroup_event(machine, event, sample); } -int perf_event__process_lost(struct perf_tool *tool __maybe_unused, +int perf_event__process_lost(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -248,7 +248,7 @@ int perf_event__process_lost(struct perf_tool *tool __maybe_unused, return machine__process_lost_event(machine, event, sample); } -int perf_event__process_aux(struct perf_tool *tool __maybe_unused, +int perf_event__process_aux(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine) @@ -256,7 +256,7 @@ int perf_event__process_aux(struct perf_tool *tool __maybe_unused, return machine__process_aux_event(machine, event); } -int perf_event__process_itrace_start(struct perf_tool *tool __maybe_unused, +int perf_event__process_itrace_start(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine) @@ -264,7 +264,7 @@ int perf_event__process_itrace_start(struct perf_tool *tool __maybe_unused, return machine__process_itrace_start_event(machine, event); } -int perf_event__process_aux_output_hw_id(struct perf_tool *tool __maybe_unused, +int perf_event__process_aux_output_hw_id(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine) @@ -272,7 +272,7 @@ int perf_event__process_aux_output_hw_id(struct perf_tool *tool __maybe_unused, return machine__process_aux_output_hw_id_event(machine, event); } -int perf_event__process_lost_samples(struct perf_tool *tool __maybe_unused, +int perf_event__process_lost_samples(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -280,7 +280,7 @@ int perf_event__process_lost_samples(struct perf_tool *tool __maybe_unused, return machine__process_lost_samples_event(machine, event, sample); } -int perf_event__process_switch(struct perf_tool *tool __maybe_unused, +int perf_event__process_switch(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine) @@ -288,7 +288,7 @@ int perf_event__process_switch(struct perf_tool *tool __maybe_unused, return machine__process_switch_event(machine, event); } -int perf_event__process_ksymbol(struct perf_tool *tool __maybe_unused, +int perf_event__process_ksymbol(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample __maybe_unused, struct machine *machine) @@ -296,7 +296,7 @@ int perf_event__process_ksymbol(struct perf_tool *tool __maybe_unused, return machine__process_ksymbol(machine, event, sample); } -int perf_event__process_bpf(struct perf_tool *tool __maybe_unused, +int perf_event__process_bpf(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -304,7 +304,7 @@ int perf_event__process_bpf(struct perf_tool *tool __maybe_unused, return machine__process_bpf(machine, event, sample); } -int perf_event__process_text_poke(struct perf_tool *tool __maybe_unused, +int perf_event__process_text_poke(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -387,7 +387,7 @@ size_t perf_event__fprintf_cpu_map(union perf_event *event, FILE *fp) return ret; } -int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, +int perf_event__process_mmap(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -395,7 +395,7 @@ int perf_event__process_mmap(struct perf_tool *tool __maybe_unused, return machine__process_mmap_event(machine, event, sample); } -int perf_event__process_mmap2(struct perf_tool *tool __maybe_unused, +int perf_event__process_mmap2(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -410,7 +410,7 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp) event->fork.ppid, event->fork.ptid); } -int perf_event__process_fork(struct perf_tool *tool __maybe_unused, +int perf_event__process_fork(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -418,7 +418,7 @@ int perf_event__process_fork(struct perf_tool *tool __maybe_unused, return machine__process_fork_event(machine, event, sample); } -int perf_event__process_exit(struct perf_tool *tool __maybe_unused, +int perf_event__process_exit(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) @@ -587,7 +587,7 @@ size_t perf_event__fprintf(union perf_event *event, struct machine *machine, FIL return ret; } -int perf_event__process(struct perf_tool *tool __maybe_unused, +int perf_event__process(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample, struct machine *machine) diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index d8bcee2e9b93..4b24f1c580fd 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -267,71 +267,71 @@ struct perf_tool; void perf_event__read_stat_config(struct perf_stat_config *config, struct perf_record_stat_config *event); -int perf_event__process_comm(struct perf_tool *tool, +int perf_event__process_comm(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_lost(struct perf_tool *tool, +int perf_event__process_lost(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_lost_samples(struct perf_tool *tool, +int perf_event__process_lost_samples(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_aux(struct perf_tool *tool, +int perf_event__process_aux(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_itrace_start(struct perf_tool *tool, +int perf_event__process_itrace_start(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_aux_output_hw_id(struct perf_tool *tool, +int perf_event__process_aux_output_hw_id(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_switch(struct perf_tool *tool, +int perf_event__process_switch(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_namespaces(struct perf_tool *tool, +int perf_event__process_namespaces(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_cgroup(struct perf_tool *tool, +int perf_event__process_cgroup(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_mmap(struct perf_tool *tool, +int perf_event__process_mmap(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_mmap2(struct perf_tool *tool, +int perf_event__process_mmap2(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_fork(struct perf_tool *tool, +int perf_event__process_fork(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_exit(struct perf_tool *tool, +int perf_event__process_exit(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_ksymbol(struct perf_tool *tool, +int perf_event__process_ksymbol(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_bpf(struct perf_tool *tool, +int perf_event__process_bpf(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process_text_poke(struct perf_tool *tool, +int perf_event__process_text_poke(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__process(struct perf_tool *tool, +int perf_event__process(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 55e9553861d0..3309fe7f1d12 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -4326,7 +4326,7 @@ out_delete_evlist: int perf_event__process_feature(struct perf_session *session, union perf_event *event) { - struct perf_tool *tool = session->tool; + const struct perf_tool *tool = session->tool; struct feat_fd ff = { .fd = 0 }; struct perf_record_header_feature *fe = (struct perf_record_header_feature *)event; int type = fe->header.type; @@ -4405,7 +4405,7 @@ size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp) return ret; } -int perf_event__process_attr(struct perf_tool *tool __maybe_unused, +int perf_event__process_attr(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct evlist **pevlist) { @@ -4444,7 +4444,7 @@ int perf_event__process_attr(struct perf_tool *tool __maybe_unused, return 0; } -int perf_event__process_event_update(struct perf_tool *tool __maybe_unused, +int perf_event__process_event_update(const struct perf_tool *tool __maybe_unused, union perf_event *event, struct evlist **pevlist) { diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 7c16a250e738..07ff647197ff 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -156,9 +156,9 @@ int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full); int perf_event__process_feature(struct perf_session *session, union perf_event *event); -int perf_event__process_attr(struct perf_tool *tool, union perf_event *event, +int perf_event__process_attr(const struct perf_tool *tool, union perf_event *event, struct evlist **pevlist); -int perf_event__process_event_update(struct perf_tool *tool, +int perf_event__process_event_update(const struct perf_tool *tool, union perf_event *event, struct evlist **pevlist); size_t perf_event__fprintf_event_update(union perf_event *event, FILE *fp); diff --git a/tools/perf/util/hisi-ptt.c b/tools/perf/util/hisi-ptt.c index 37ea987017f6..e4cc4785f744 100644 --- a/tools/perf/util/hisi-ptt.c +++ b/tools/perf/util/hisi-ptt.c @@ -79,14 +79,14 @@ static void hisi_ptt_dump_event(struct hisi_ptt *ptt, unsigned char *buf, static int hisi_ptt_process_event(struct perf_session *session __maybe_unused, union perf_event *event __maybe_unused, struct perf_sample *sample __maybe_unused, - struct perf_tool *tool __maybe_unused) + const struct perf_tool *tool __maybe_unused) { return 0; } static int hisi_ptt_process_auxtrace_event(struct perf_session *session, union perf_event *event, - struct perf_tool *tool __maybe_unused) + const struct perf_tool *tool __maybe_unused) { struct hisi_ptt *ptt = container_of(session->auxtrace, struct hisi_ptt, auxtrace); @@ -123,7 +123,7 @@ static int hisi_ptt_process_auxtrace_event(struct perf_session *session, } static int hisi_ptt_flush(struct perf_session *session __maybe_unused, - struct perf_tool *tool __maybe_unused) + const struct perf_tool *tool __maybe_unused) { return 0; } diff --git a/tools/perf/util/intel-bts.c b/tools/perf/util/intel-bts.c index 160d346cb450..27d9b5c9fec8 100644 --- a/tools/perf/util/intel-bts.c +++ b/tools/perf/util/intel-bts.c @@ -591,7 +591,7 @@ static int intel_bts_process_queues(struct intel_bts *bts, u64 timestamp) static int intel_bts_process_event(struct perf_session *session, union perf_event *event, struct perf_sample *sample, - struct perf_tool *tool) + const struct perf_tool *tool) { struct intel_bts *bts = container_of(session->auxtrace, struct intel_bts, auxtrace); @@ -634,7 +634,7 @@ static int intel_bts_process_event(struct perf_session *session, static int intel_bts_process_auxtrace_event(struct perf_session *session, union perf_event *event, - struct perf_tool *tool __maybe_unused) + const struct perf_tool *tool __maybe_unused) { struct intel_bts *bts = container_of(session->auxtrace, struct intel_bts, auxtrace); @@ -675,7 +675,7 @@ static int intel_bts_process_auxtrace_event(struct perf_session *session, } static int intel_bts_flush(struct perf_session *session, - struct perf_tool *tool __maybe_unused) + const struct perf_tool *tool __maybe_unused) { struct intel_bts *bts = container_of(session->auxtrace, struct intel_bts, auxtrace); diff --git a/tools/perf/util/intel-pt.c b/tools/perf/util/intel-pt.c index 1fc03bfd14c3..fd2597613f3d 100644 --- a/tools/perf/util/intel-pt.c +++ b/tools/perf/util/intel-pt.c @@ -3449,7 +3449,7 @@ out: static int intel_pt_process_event(struct perf_session *session, union perf_event *event, struct perf_sample *sample, - struct perf_tool *tool) + const struct perf_tool *tool) { struct intel_pt *pt = container_of(session->auxtrace, struct intel_pt, auxtrace); @@ -3533,7 +3533,7 @@ static int intel_pt_process_event(struct perf_session *session, return err; } -static int intel_pt_flush(struct perf_session *session, struct perf_tool *tool) +static int intel_pt_flush(struct perf_session *session, const struct perf_tool *tool) { struct intel_pt *pt = container_of(session->auxtrace, struct intel_pt, auxtrace); @@ -3600,7 +3600,7 @@ static bool intel_pt_evsel_is_auxtrace(struct perf_session *session, static int intel_pt_process_auxtrace_event(struct perf_session *session, union perf_event *event, - struct perf_tool *tool __maybe_unused) + const struct perf_tool *tool __maybe_unused) { struct intel_pt *pt = container_of(session->auxtrace, struct intel_pt, auxtrace); diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index 1f657ef8975f..5ce13653512b 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c @@ -424,7 +424,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr) { struct perf_sample sample; union perf_event *event; - struct perf_tool *tool = jd->session->tool; + const struct perf_tool *tool = jd->session->tool; uint64_t code, addr; uintptr_t uaddr; char *filename; @@ -543,7 +543,7 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr) { struct perf_sample sample; union perf_event *event; - struct perf_tool *tool = jd->session->tool; + const struct perf_tool *tool = jd->session->tool; char *filename; size_t size; struct stat st; diff --git a/tools/perf/util/s390-cpumsf.c b/tools/perf/util/s390-cpumsf.c index 4ec583e511af..73846b73d0cf 100644 --- a/tools/perf/util/s390-cpumsf.c +++ b/tools/perf/util/s390-cpumsf.c @@ -912,7 +912,7 @@ static int s390_cpumsf_process_event(struct perf_session *session, union perf_event *event, struct perf_sample *sample, - struct perf_tool *tool) + const struct perf_tool *tool) { struct s390_cpumsf *sf = container_of(session->auxtrace, struct s390_cpumsf, @@ -955,7 +955,7 @@ s390_cpumsf_process_event(struct perf_session *session, static int s390_cpumsf_process_auxtrace_event(struct perf_session *session, union perf_event *event __maybe_unused, - struct perf_tool *tool __maybe_unused) + const struct perf_tool *tool __maybe_unused) { struct s390_cpumsf *sf = container_of(session->auxtrace, struct s390_cpumsf, @@ -998,7 +998,7 @@ static void s390_cpumsf_free_events(struct perf_session *session __maybe_unused) } static int s390_cpumsf_flush(struct perf_session *session __maybe_unused, - struct perf_tool *tool __maybe_unused) + const struct perf_tool *tool __maybe_unused) { return 0; } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index aa02c1d7b1f9..41beebd0913c 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -103,7 +103,7 @@ static int perf_session__process_compressed_event(struct perf_session *session, static int perf_session__deliver_event(struct perf_session *session, union perf_event *event, - struct perf_tool *tool, + const struct perf_tool *tool, u64 file_offset, const char *file_path); @@ -330,7 +330,7 @@ static int process_event_synth_tracing_data_stub(struct perf_session *session return 0; } -static int process_event_synth_attr_stub(struct perf_tool *tool __maybe_unused, +static int process_event_synth_attr_stub(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct evlist **pevlist __maybe_unused) @@ -339,7 +339,7 @@ static int process_event_synth_attr_stub(struct perf_tool *tool __maybe_unused, return 0; } -static int process_event_synth_event_update_stub(struct perf_tool *tool __maybe_unused, +static int process_event_synth_event_update_stub(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct evlist **pevlist __maybe_unused) @@ -351,7 +351,7 @@ static int process_event_synth_event_update_stub(struct perf_tool *tool __maybe_ return 0; } -static int process_event_sample_stub(struct perf_tool *tool __maybe_unused, +static int process_event_sample_stub(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_sample *sample __maybe_unused, struct evsel *evsel __maybe_unused, @@ -361,7 +361,7 @@ static int process_event_sample_stub(struct perf_tool *tool __maybe_unused, return 0; } -static int process_event_stub(struct perf_tool *tool __maybe_unused, +static int process_event_stub(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct perf_sample *sample __maybe_unused, struct machine *machine __maybe_unused) @@ -370,7 +370,7 @@ static int process_event_stub(struct perf_tool *tool __maybe_unused, return 0; } -static int process_finished_round_stub(struct perf_tool *tool __maybe_unused, +static int process_finished_round_stub(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct ordered_events *oe __maybe_unused) { @@ -1078,7 +1078,7 @@ static perf_event__swap_op perf_event__swap_ops[] = { * Flush every events below timestamp 7 * etc... */ -int perf_event__process_finished_round(struct perf_tool *tool __maybe_unused, +int perf_event__process_finished_round(const struct perf_tool *tool __maybe_unused, union perf_event *event __maybe_unused, struct ordered_events *oe) { @@ -1472,7 +1472,7 @@ static struct machine *machines__find_for_cpumode(struct machines *machines, } static int deliver_sample_value(struct evlist *evlist, - struct perf_tool *tool, + const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct sample_read_value *v, @@ -1504,7 +1504,7 @@ static int deliver_sample_value(struct evlist *evlist, } static int deliver_sample_group(struct evlist *evlist, - struct perf_tool *tool, + const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine, @@ -1526,7 +1526,7 @@ static int deliver_sample_group(struct evlist *evlist, return ret; } -static int evlist__deliver_sample(struct evlist *evlist, struct perf_tool *tool, +static int evlist__deliver_sample(struct evlist *evlist, const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct evsel *evsel, struct machine *machine) { @@ -1551,7 +1551,7 @@ static int machines__deliver_event(struct machines *machines, struct evlist *evlist, union perf_event *event, struct perf_sample *sample, - struct perf_tool *tool, u64 file_offset, + const struct perf_tool *tool, u64 file_offset, const char *file_path) { struct evsel *evsel; @@ -1639,7 +1639,7 @@ static int machines__deliver_event(struct machines *machines, static int perf_session__deliver_event(struct perf_session *session, union perf_event *event, - struct perf_tool *tool, + const struct perf_tool *tool, u64 file_offset, const char *file_path) { @@ -1672,7 +1672,7 @@ static s64 perf_session__process_user_event(struct perf_session *session, const char *file_path) { struct ordered_events *oe = &session->ordered_events; - struct perf_tool *tool = session->tool; + const struct perf_tool *tool = session->tool; struct perf_sample sample = { .time = 0, }; int fd = perf_data__fd(session->data); int err; @@ -1759,7 +1759,7 @@ int perf_session__deliver_synth_event(struct perf_session *session, struct perf_sample *sample) { struct evlist *evlist = session->evlist; - struct perf_tool *tool = session->tool; + const struct perf_tool *tool = session->tool; events_stats__inc(&evlist->stats, event->header.type); @@ -1891,7 +1891,7 @@ static s64 perf_session__process_event(struct perf_session *session, const char *file_path) { struct evlist *evlist = session->evlist; - struct perf_tool *tool = session->tool; + const struct perf_tool *tool = session->tool; int ret; if (session->header.needs_swap) diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index cc28976bb00e..6886cc85600f 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -163,7 +163,7 @@ int perf_session__dsos_hit_all(struct perf_session *session); int perf_event__process_id_index(struct perf_session *session, union perf_event *event); -int perf_event__process_finished_round(struct perf_tool *tool, +int perf_event__process_finished_round(const struct perf_tool *tool, union perf_event *event, struct ordered_events *oe); diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index 5498048f56ea..7f884d70de81 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only +// SPDX-License-Identifier: GPL-2.0-only #include "util/cgroup.h" #include "util/data.h" @@ -47,7 +47,7 @@ unsigned int proc_map_timeout = DEFAULT_PROC_MAP_PARSE_TIMEOUT; -int perf_tool__process_synth_event(struct perf_tool *tool, +int perf_tool__process_synth_event(const struct perf_tool *tool, union perf_event *event, struct machine *machine, perf_event__handler_t process) @@ -187,7 +187,7 @@ static int perf_event__prepare_comm(union perf_event *event, pid_t pid, pid_t ti return 0; } -pid_t perf_event__synthesize_comm(struct perf_tool *tool, +pid_t perf_event__synthesize_comm(const struct perf_tool *tool, union perf_event *event, pid_t pid, perf_event__handler_t process, struct machine *machine) @@ -218,7 +218,7 @@ static void perf_event__get_ns_link_info(pid_t pid, const char *ns, } } -int perf_event__synthesize_namespaces(struct perf_tool *tool, +int perf_event__synthesize_namespaces(const struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, perf_event__handler_t process, @@ -257,7 +257,7 @@ int perf_event__synthesize_namespaces(struct perf_tool *tool, return 0; } -static int perf_event__synthesize_fork(struct perf_tool *tool, +static int perf_event__synthesize_fork(const struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, pid_t ppid, perf_event__handler_t process, @@ -418,7 +418,7 @@ out: dso__put(dso); } -int perf_event__synthesize_mmap_events(struct perf_tool *tool, +int perf_event__synthesize_mmap_events(const struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, perf_event__handler_t process, @@ -542,7 +542,7 @@ out: } #ifdef HAVE_FILE_HANDLE -static int perf_event__synthesize_cgroup(struct perf_tool *tool, +static int perf_event__synthesize_cgroup(const struct perf_tool *tool, union perf_event *event, char *path, size_t mount_len, perf_event__handler_t process, @@ -582,7 +582,7 @@ static int perf_event__synthesize_cgroup(struct perf_tool *tool, return 0; } -static int perf_event__walk_cgroup_tree(struct perf_tool *tool, +static int perf_event__walk_cgroup_tree(const struct perf_tool *tool, union perf_event *event, char *path, size_t mount_len, perf_event__handler_t process, @@ -630,7 +630,7 @@ static int perf_event__walk_cgroup_tree(struct perf_tool *tool, return ret; } -int perf_event__synthesize_cgroups(struct perf_tool *tool, +int perf_event__synthesize_cgroups(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine) { @@ -657,7 +657,7 @@ int perf_event__synthesize_cgroups(struct perf_tool *tool, return 0; } #else -int perf_event__synthesize_cgroups(struct perf_tool *tool __maybe_unused, +int perf_event__synthesize_cgroups(const struct perf_tool *tool __maybe_unused, perf_event__handler_t process __maybe_unused, struct machine *machine __maybe_unused) { @@ -666,7 +666,7 @@ int perf_event__synthesize_cgroups(struct perf_tool *tool __maybe_unused, #endif struct perf_event__synthesize_modules_maps_cb_args { - struct perf_tool *tool; + const struct perf_tool *tool; perf_event__handler_t process; struct machine *machine; union perf_event *event; @@ -717,7 +717,7 @@ static int perf_event__synthesize_modules_maps_cb(struct map *map, void *data) return 0; } -int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t process, +int perf_event__synthesize_modules(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine) { int rc; @@ -763,7 +763,7 @@ static int __event__synthesize_thread(union perf_event *comm_event, union perf_event *fork_event, union perf_event *namespaces_event, pid_t pid, int full, perf_event__handler_t process, - struct perf_tool *tool, struct machine *machine, + const struct perf_tool *tool, struct machine *machine, bool needs_mmap, bool mmap_data) { char filename[PATH_MAX]; @@ -852,7 +852,7 @@ static int __event__synthesize_thread(union perf_event *comm_event, return rc; } -int perf_event__synthesize_thread_map(struct perf_tool *tool, +int perf_event__synthesize_thread_map(const struct perf_tool *tool, struct perf_thread_map *threads, perf_event__handler_t process, struct machine *machine, @@ -929,7 +929,7 @@ out: return err; } -static int __perf_event__synthesize_threads(struct perf_tool *tool, +static int __perf_event__synthesize_threads(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine, bool needs_mmap, @@ -993,7 +993,7 @@ out: } struct synthesize_threads_arg { - struct perf_tool *tool; + const struct perf_tool *tool; perf_event__handler_t process; struct machine *machine; bool needs_mmap; @@ -1015,7 +1015,7 @@ static void *synthesize_threads_worker(void *arg) return NULL; } -int perf_event__synthesize_threads(struct perf_tool *tool, +int perf_event__synthesize_threads(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine, bool needs_mmap, bool mmap_data, @@ -1104,14 +1104,14 @@ free_dirent: return err; } -int __weak perf_event__synthesize_extra_kmaps(struct perf_tool *tool __maybe_unused, +int __weak perf_event__synthesize_extra_kmaps(const struct perf_tool *tool __maybe_unused, perf_event__handler_t process __maybe_unused, struct machine *machine __maybe_unused) { return 0; } -static int __perf_event__synthesize_kernel_mmap(struct perf_tool *tool, +static int __perf_event__synthesize_kernel_mmap(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine) { @@ -1183,7 +1183,7 @@ static int __perf_event__synthesize_kernel_mmap(struct perf_tool *tool, return err; } -int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, +int perf_event__synthesize_kernel_mmap(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine) { @@ -1196,7 +1196,7 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, return perf_event__synthesize_extra_kmaps(tool, process, machine); } -int perf_event__synthesize_thread_map2(struct perf_tool *tool, +int perf_event__synthesize_thread_map2(const struct perf_tool *tool, struct perf_thread_map *threads, perf_event__handler_t process, struct machine *machine) @@ -1346,7 +1346,7 @@ static struct perf_record_cpu_map *cpu_map_event__new(const struct perf_cpu_map } -int perf_event__synthesize_cpu_map(struct perf_tool *tool, +int perf_event__synthesize_cpu_map(const struct perf_tool *tool, const struct perf_cpu_map *map, perf_event__handler_t process, struct machine *machine) @@ -1364,7 +1364,7 @@ int perf_event__synthesize_cpu_map(struct perf_tool *tool, return err; } -int perf_event__synthesize_stat_config(struct perf_tool *tool, +int perf_event__synthesize_stat_config(const struct perf_tool *tool, struct perf_stat_config *config, perf_event__handler_t process, struct machine *machine) @@ -1403,7 +1403,7 @@ int perf_event__synthesize_stat_config(struct perf_tool *tool, return err; } -int perf_event__synthesize_stat(struct perf_tool *tool, +int perf_event__synthesize_stat(const struct perf_tool *tool, struct perf_cpu cpu, u32 thread, u64 id, struct perf_counts_values *count, perf_event__handler_t process, @@ -1425,7 +1425,7 @@ int perf_event__synthesize_stat(struct perf_tool *tool, return process(tool, (union perf_event *) &event, NULL, machine); } -int perf_event__synthesize_stat_round(struct perf_tool *tool, +int perf_event__synthesize_stat_round(const struct perf_tool *tool, u64 evtime, u64 type, perf_event__handler_t process, struct machine *machine) @@ -1826,7 +1826,7 @@ int perf_event__synthesize_id_sample(__u64 *array, u64 type, const struct perf_s return (void *)array - (void *)start; } -int __perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_t process, +int __perf_event__synthesize_id_index(const struct perf_tool *tool, perf_event__handler_t process, struct evlist *evlist, struct machine *machine, size_t from) { union perf_event *ev; @@ -1918,13 +1918,13 @@ out_err: return err; } -int perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_t process, +int perf_event__synthesize_id_index(const struct perf_tool *tool, perf_event__handler_t process, struct evlist *evlist, struct machine *machine) { return __perf_event__synthesize_id_index(tool, process, evlist, machine, 0); } -int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, +int __machine__synthesize_threads(struct machine *machine, const struct perf_tool *tool, struct target *target, struct perf_thread_map *threads, perf_event__handler_t process, bool needs_mmap, bool data_mmap, unsigned int nr_threads_synthesize) @@ -1985,7 +1985,7 @@ static struct perf_record_event_update *event_update_event__new(size_t size, u64 return ev; } -int perf_event__synthesize_event_update_unit(struct perf_tool *tool, struct evsel *evsel, +int perf_event__synthesize_event_update_unit(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process) { size_t size = strlen(evsel->unit); @@ -2002,7 +2002,7 @@ int perf_event__synthesize_event_update_unit(struct perf_tool *tool, struct evse return err; } -int perf_event__synthesize_event_update_scale(struct perf_tool *tool, struct evsel *evsel, +int perf_event__synthesize_event_update_scale(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process) { struct perf_record_event_update *ev; @@ -2019,7 +2019,7 @@ int perf_event__synthesize_event_update_scale(struct perf_tool *tool, struct evs return err; } -int perf_event__synthesize_event_update_name(struct perf_tool *tool, struct evsel *evsel, +int perf_event__synthesize_event_update_name(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process) { struct perf_record_event_update *ev; @@ -2036,7 +2036,7 @@ int perf_event__synthesize_event_update_name(struct perf_tool *tool, struct evse return err; } -int perf_event__synthesize_event_update_cpus(struct perf_tool *tool, struct evsel *evsel, +int perf_event__synthesize_event_update_cpus(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process) { struct synthesize_cpu_map_data syn_data = { .map = evsel->core.own_cpus }; @@ -2059,7 +2059,7 @@ int perf_event__synthesize_event_update_cpus(struct perf_tool *tool, struct evse return err; } -int perf_event__synthesize_attrs(struct perf_tool *tool, struct evlist *evlist, +int perf_event__synthesize_attrs(const struct perf_tool *tool, struct evlist *evlist, perf_event__handler_t process) { struct evsel *evsel; @@ -2087,7 +2087,7 @@ static bool has_scale(struct evsel *evsel) return evsel->scale != 1; } -int perf_event__synthesize_extra_attr(struct perf_tool *tool, struct evlist *evsel_list, +int perf_event__synthesize_extra_attr(const struct perf_tool *tool, struct evlist *evsel_list, perf_event__handler_t process, bool is_pipe) { struct evsel *evsel; @@ -2143,7 +2143,7 @@ int perf_event__synthesize_extra_attr(struct perf_tool *tool, struct evlist *evs return 0; } -int perf_event__synthesize_attr(struct perf_tool *tool, struct perf_event_attr *attr, +int perf_event__synthesize_attr(const struct perf_tool *tool, struct perf_event_attr *attr, u32 ids, u64 *id, perf_event__handler_t process) { union perf_event *ev; @@ -2177,7 +2177,7 @@ int perf_event__synthesize_attr(struct perf_tool *tool, struct perf_event_attr * } #ifdef HAVE_LIBTRACEEVENT -int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, struct evlist *evlist, +int perf_event__synthesize_tracing_data(const struct perf_tool *tool, int fd, struct evlist *evlist, perf_event__handler_t process) { union perf_event ev; @@ -2225,7 +2225,7 @@ int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, struct e } #endif -int perf_event__synthesize_build_id(struct perf_tool *tool, struct dso *pos, u16 misc, +int perf_event__synthesize_build_id(const struct perf_tool *tool, struct dso *pos, u16 misc, perf_event__handler_t process, struct machine *machine) { union perf_event ev; @@ -2249,7 +2249,7 @@ int perf_event__synthesize_build_id(struct perf_tool *tool, struct dso *pos, u16 return process(tool, &ev, NULL, machine); } -int perf_event__synthesize_stat_events(struct perf_stat_config *config, struct perf_tool *tool, +int perf_event__synthesize_stat_events(struct perf_stat_config *config, const struct perf_tool *tool, struct evlist *evlist, perf_event__handler_t process, bool attrs) { int err; @@ -2286,7 +2286,7 @@ int perf_event__synthesize_stat_events(struct perf_stat_config *config, struct p extern const struct perf_header_feature_ops feat_ops[HEADER_LAST_FEATURE]; -int perf_event__synthesize_features(struct perf_tool *tool, struct perf_session *session, +int perf_event__synthesize_features(const struct perf_tool *tool, struct perf_session *session, struct evlist *evlist, perf_event__handler_t process) { struct perf_header *header = &session->header; @@ -2349,7 +2349,7 @@ int perf_event__synthesize_features(struct perf_tool *tool, struct perf_session return ret; } -int perf_event__synthesize_for_pipe(struct perf_tool *tool, +int perf_event__synthesize_for_pipe(const struct perf_tool *tool, struct perf_session *session, struct perf_data *data, perf_event__handler_t process) diff --git a/tools/perf/util/synthetic-events.h b/tools/perf/util/synthetic-events.h index 53737d1619a4..31df7653677f 100644 --- a/tools/perf/util/synthetic-events.h +++ b/tools/perf/util/synthetic-events.h @@ -40,45 +40,45 @@ enum perf_record_synth { int parse_synth_opt(char *str); -typedef int (*perf_event__handler_t)(struct perf_tool *tool, union perf_event *event, +typedef int (*perf_event__handler_t)(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -int perf_event__synthesize_attrs(struct perf_tool *tool, struct evlist *evlist, perf_event__handler_t process); -int perf_event__synthesize_attr(struct perf_tool *tool, struct perf_event_attr *attr, u32 ids, u64 *id, perf_event__handler_t process); -int perf_event__synthesize_build_id(struct perf_tool *tool, struct dso *pos, u16 misc, perf_event__handler_t process, struct machine *machine); -int perf_event__synthesize_cpu_map(struct perf_tool *tool, const struct perf_cpu_map *cpus, perf_event__handler_t process, struct machine *machine); -int perf_event__synthesize_event_update_cpus(struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process); -int perf_event__synthesize_event_update_name(struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process); -int perf_event__synthesize_event_update_scale(struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process); -int perf_event__synthesize_event_update_unit(struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process); -int perf_event__synthesize_extra_attr(struct perf_tool *tool, struct evlist *evsel_list, perf_event__handler_t process, bool is_pipe); -int perf_event__synthesize_extra_kmaps(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); -int perf_event__synthesize_features(struct perf_tool *tool, struct perf_session *session, struct evlist *evlist, perf_event__handler_t process); -int perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_t process, struct evlist *evlist, struct machine *machine); -int __perf_event__synthesize_id_index(struct perf_tool *tool, perf_event__handler_t process, struct evlist *evlist, struct machine *machine, size_t from); +int perf_event__synthesize_attrs(const struct perf_tool *tool, struct evlist *evlist, perf_event__handler_t process); +int perf_event__synthesize_attr(const struct perf_tool *tool, struct perf_event_attr *attr, u32 ids, u64 *id, perf_event__handler_t process); +int perf_event__synthesize_build_id(const struct perf_tool *tool, struct dso *pos, u16 misc, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_cpu_map(const struct perf_tool *tool, const struct perf_cpu_map *cpus, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_event_update_cpus(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process); +int perf_event__synthesize_event_update_name(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process); +int perf_event__synthesize_event_update_scale(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process); +int perf_event__synthesize_event_update_unit(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process); +int perf_event__synthesize_extra_attr(const struct perf_tool *tool, struct evlist *evsel_list, perf_event__handler_t process, bool is_pipe); +int perf_event__synthesize_extra_kmaps(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_features(const struct perf_tool *tool, struct perf_session *session, struct evlist *evlist, perf_event__handler_t process); +int perf_event__synthesize_id_index(const struct perf_tool *tool, perf_event__handler_t process, struct evlist *evlist, struct machine *machine); +int __perf_event__synthesize_id_index(const struct perf_tool *tool, perf_event__handler_t process, struct evlist *evlist, struct machine *machine, size_t from); int perf_event__synthesize_id_sample(__u64 *array, u64 type, const struct perf_sample *sample); -int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); -int perf_event__synthesize_mmap_events(struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, perf_event__handler_t process, struct machine *machine, bool mmap_data); -int perf_event__synthesize_modules(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); -int perf_event__synthesize_namespaces(struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, perf_event__handler_t process, struct machine *machine); -int perf_event__synthesize_cgroups(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_kernel_mmap(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_mmap_events(const struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, perf_event__handler_t process, struct machine *machine, bool mmap_data); +int perf_event__synthesize_modules(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_namespaces(const struct perf_tool *tool, union perf_event *event, pid_t pid, pid_t tgid, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_cgroups(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); int perf_event__synthesize_sample(union perf_event *event, u64 type, u64 read_format, const struct perf_sample *sample); -int perf_event__synthesize_stat_config(struct perf_tool *tool, struct perf_stat_config *config, perf_event__handler_t process, struct machine *machine); -int perf_event__synthesize_stat_events(struct perf_stat_config *config, struct perf_tool *tool, struct evlist *evlist, perf_event__handler_t process, bool attrs); -int perf_event__synthesize_stat_round(struct perf_tool *tool, u64 time, u64 type, perf_event__handler_t process, struct machine *machine); -int perf_event__synthesize_stat(struct perf_tool *tool, struct perf_cpu cpu, u32 thread, u64 id, struct perf_counts_values *count, perf_event__handler_t process, struct machine *machine); -int perf_event__synthesize_thread_map2(struct perf_tool *tool, struct perf_thread_map *threads, perf_event__handler_t process, struct machine *machine); -int perf_event__synthesize_thread_map(struct perf_tool *tool, struct perf_thread_map *threads, perf_event__handler_t process, struct machine *machine, bool needs_mmap, bool mmap_data); -int perf_event__synthesize_threads(struct perf_tool *tool, perf_event__handler_t process, struct machine *machine, bool needs_mmap, bool mmap_data, unsigned int nr_threads_synthesize); -int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd, struct evlist *evlist, perf_event__handler_t process); -int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc, struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); -pid_t perf_event__synthesize_comm(struct perf_tool *tool, union perf_event *event, pid_t pid, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_stat_config(const struct perf_tool *tool, struct perf_stat_config *config, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_stat_events(struct perf_stat_config *config, const struct perf_tool *tool, struct evlist *evlist, perf_event__handler_t process, bool attrs); +int perf_event__synthesize_stat_round(const struct perf_tool *tool, u64 time, u64 type, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_stat(const struct perf_tool *tool, struct perf_cpu cpu, u32 thread, u64 id, struct perf_counts_values *count, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_thread_map2(const struct perf_tool *tool, struct perf_thread_map *threads, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_thread_map(const struct perf_tool *tool, struct perf_thread_map *threads, perf_event__handler_t process, struct machine *machine, bool needs_mmap, bool mmap_data); +int perf_event__synthesize_threads(const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine, bool needs_mmap, bool mmap_data, unsigned int nr_threads_synthesize); +int perf_event__synthesize_tracing_data(const struct perf_tool *tool, int fd, struct evlist *evlist, perf_event__handler_t process); +int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc, const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine); +pid_t perf_event__synthesize_comm(const struct perf_tool *tool, union perf_event *event, pid_t pid, perf_event__handler_t process, struct machine *machine); -int perf_tool__process_synth_event(struct perf_tool *tool, union perf_event *event, struct machine *machine, perf_event__handler_t process); +int perf_tool__process_synth_event(const struct perf_tool *tool, union perf_event *event, struct machine *machine, perf_event__handler_t process); size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type, u64 read_format); -int __machine__synthesize_threads(struct machine *machine, struct perf_tool *tool, +int __machine__synthesize_threads(struct machine *machine, const struct perf_tool *tool, struct target *target, struct perf_thread_map *threads, perf_event__handler_t process, bool needs_mmap, bool data_mmap, unsigned int nr_threads_synthesize); @@ -87,7 +87,7 @@ int machine__synthesize_threads(struct machine *machine, struct target *target, unsigned int nr_threads_synthesize); #ifdef HAVE_AUXTRACE_SUPPORT -int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, struct perf_tool *tool, +int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, const struct perf_tool *tool, struct perf_session *session, perf_event__handler_t process); #else // HAVE_AUXTRACE_SUPPORT @@ -96,7 +96,7 @@ int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr, struct per static inline int perf_event__synthesize_auxtrace_info(struct auxtrace_record *itr __maybe_unused, - struct perf_tool *tool __maybe_unused, + const struct perf_tool *tool __maybe_unused, struct perf_session *session __maybe_unused, perf_event__handler_t process __maybe_unused) { @@ -117,7 +117,7 @@ static inline int perf_event__synthesize_bpf_events(struct perf_session *session } #endif // HAVE_LIBBPF_SUPPORT -int perf_event__synthesize_for_pipe(struct perf_tool *tool, +int perf_event__synthesize_for_pipe(const struct perf_tool *tool, struct perf_session *session, struct perf_data *data, perf_event__handler_t process); diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 62bbc9cec151..2b12c674768f 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -15,14 +15,14 @@ struct perf_tool; struct machine; struct ordered_events; -typedef int (*event_sample)(struct perf_tool *tool, union perf_event *event, +typedef int (*event_sample)(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct evsel *evsel, struct machine *machine); -typedef int (*event_op)(struct perf_tool *tool, union perf_event *event, +typedef int (*event_op)(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); -typedef int (*event_attr_op)(struct perf_tool *tool, +typedef int (*event_attr_op)(const struct perf_tool *tool, union perf_event *event, struct evlist **pevlist); @@ -31,7 +31,7 @@ typedef s64 (*event_op3)(struct perf_session *session, union perf_event *event); typedef int (*event_op4)(struct perf_session *session, union perf_event *event, u64 data, const char *str); -typedef int (*event_oe)(struct perf_tool *tool, union perf_event *event, +typedef int (*event_oe)(const struct perf_tool *tool, union perf_event *event, struct ordered_events *oe); enum show_feature_header { diff --git a/tools/perf/util/tsc.c b/tools/perf/util/tsc.c index f19791d46e99..2e33a20e1e1b 100644 --- a/tools/perf/util/tsc.c +++ b/tools/perf/util/tsc.c @@ -72,7 +72,7 @@ int perf_read_tsc_conversion(const struct perf_event_mmap_page *pc, } int perf_event__synth_time_conv(const struct perf_event_mmap_page *pc, - struct perf_tool *tool, + const struct perf_tool *tool, perf_event__handler_t process, struct machine *machine) { From 564e5cbcfdf5c55491461edc61f6b7e2a4418063 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:46:56 -0700 Subject: [PATCH 116/347] perf tool: Move fill defaults into tool.c The aim here is to eventually make perf_tool__fill_defaults() an init function so that the tools struct is more const. Create a tool.c to go along with tool.h. Move perf_tool__fill_defaults() out of session.c into tool.c along with the default stub values. Add perf_tool__compressed_is_stub() for a test in perf_session__process_user_event(). perf_session__process_compressed_event() is only used from being default initialized so migrate into tool.c. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-5-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/Build | 1 + tools/perf/util/session.c | 310 +----------------------------------- tools/perf/util/session.h | 2 - tools/perf/util/tool.c | 325 ++++++++++++++++++++++++++++++++++++++ tools/perf/util/tool.h | 4 + 5 files changed, 331 insertions(+), 311 deletions(-) create mode 100644 tools/perf/util/tool.c diff --git a/tools/perf/util/Build b/tools/perf/util/Build index b24360c04aae..f6f18b4228bb 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -66,6 +66,7 @@ perf-util-y += map.o perf-util-y += maps.o perf-util-y += pstack.o perf-util-y += session.o +perf-util-y += tool.o perf-util-y += sample-raw.o perf-util-y += s390-sample-raw.o perf-util-y += amd-sample-raw.o diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 41beebd0913c..5c0e0341d39e 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -39,68 +39,6 @@ #include "annotate.h" #include -#ifdef HAVE_ZSTD_SUPPORT -static int perf_session__process_compressed_event(struct perf_session *session, - union perf_event *event, u64 file_offset, - const char *file_path) -{ - void *src; - size_t decomp_size, src_size; - u64 decomp_last_rem = 0; - size_t mmap_len, decomp_len = session->header.env.comp_mmap_len; - struct decomp *decomp, *decomp_last = session->active_decomp->decomp_last; - - if (decomp_last) { - decomp_last_rem = decomp_last->size - decomp_last->head; - decomp_len += decomp_last_rem; - } - - mmap_len = sizeof(struct decomp) + decomp_len; - decomp = mmap(NULL, mmap_len, PROT_READ|PROT_WRITE, - MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); - if (decomp == MAP_FAILED) { - pr_err("Couldn't allocate memory for decompression\n"); - return -1; - } - - decomp->file_pos = file_offset; - decomp->file_path = file_path; - decomp->mmap_len = mmap_len; - decomp->head = 0; - - if (decomp_last_rem) { - memcpy(decomp->data, &(decomp_last->data[decomp_last->head]), decomp_last_rem); - decomp->size = decomp_last_rem; - } - - src = (void *)event + sizeof(struct perf_record_compressed); - src_size = event->pack.header.size - sizeof(struct perf_record_compressed); - - decomp_size = zstd_decompress_stream(session->active_decomp->zstd_decomp, src, src_size, - &(decomp->data[decomp_last_rem]), decomp_len - decomp_last_rem); - if (!decomp_size) { - munmap(decomp, mmap_len); - pr_err("Couldn't decompress data\n"); - return -1; - } - - decomp->size += decomp_size; - - if (session->active_decomp->decomp == NULL) - session->active_decomp->decomp = decomp; - else - session->active_decomp->decomp_last->next = decomp; - - session->active_decomp->decomp_last = decomp; - - pr_debug("decomp (B): %zd to %zd\n", src_size, decomp_size); - - return 0; -} -#else /* !HAVE_ZSTD_SUPPORT */ -#define perf_session__process_compressed_event perf_session__process_compressed_event_stub -#endif - static int perf_session__deliver_event(struct perf_session *session, union perf_event *event, const struct perf_tool *tool, @@ -321,251 +259,6 @@ void perf_session__delete(struct perf_session *session) free(session); } -static int process_event_synth_tracing_data_stub(struct perf_session *session - __maybe_unused, - union perf_event *event - __maybe_unused) -{ - dump_printf(": unhandled!\n"); - return 0; -} - -static int process_event_synth_attr_stub(const struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct evlist **pevlist - __maybe_unused) -{ - dump_printf(": unhandled!\n"); - return 0; -} - -static int process_event_synth_event_update_stub(const struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct evlist **pevlist - __maybe_unused) -{ - if (dump_trace) - perf_event__fprintf_event_update(event, stdout); - - dump_printf(": unhandled!\n"); - return 0; -} - -static int process_event_sample_stub(const struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct perf_sample *sample __maybe_unused, - struct evsel *evsel __maybe_unused, - struct machine *machine __maybe_unused) -{ - dump_printf(": unhandled!\n"); - return 0; -} - -static int process_event_stub(const struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct perf_sample *sample __maybe_unused, - struct machine *machine __maybe_unused) -{ - dump_printf(": unhandled!\n"); - return 0; -} - -static int process_finished_round_stub(const struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct ordered_events *oe __maybe_unused) -{ - dump_printf(": unhandled!\n"); - return 0; -} - -static int skipn(int fd, off_t n) -{ - char buf[4096]; - ssize_t ret; - - while (n > 0) { - ret = read(fd, buf, min(n, (off_t)sizeof(buf))); - if (ret <= 0) - return ret; - n -= ret; - } - - return 0; -} - -static s64 process_event_auxtrace_stub(struct perf_session *session __maybe_unused, - union perf_event *event) -{ - dump_printf(": unhandled!\n"); - if (perf_data__is_pipe(session->data)) - skipn(perf_data__fd(session->data), event->auxtrace.size); - return event->auxtrace.size; -} - -static int process_event_op2_stub(struct perf_session *session __maybe_unused, - union perf_event *event __maybe_unused) -{ - dump_printf(": unhandled!\n"); - return 0; -} - - -static -int process_event_thread_map_stub(struct perf_session *session __maybe_unused, - union perf_event *event __maybe_unused) -{ - if (dump_trace) - perf_event__fprintf_thread_map(event, stdout); - - dump_printf(": unhandled!\n"); - return 0; -} - -static -int process_event_cpu_map_stub(struct perf_session *session __maybe_unused, - union perf_event *event __maybe_unused) -{ - if (dump_trace) - perf_event__fprintf_cpu_map(event, stdout); - - dump_printf(": unhandled!\n"); - return 0; -} - -static -int process_event_stat_config_stub(struct perf_session *session __maybe_unused, - union perf_event *event __maybe_unused) -{ - if (dump_trace) - perf_event__fprintf_stat_config(event, stdout); - - dump_printf(": unhandled!\n"); - return 0; -} - -static int process_stat_stub(struct perf_session *perf_session __maybe_unused, - union perf_event *event) -{ - if (dump_trace) - perf_event__fprintf_stat(event, stdout); - - dump_printf(": unhandled!\n"); - return 0; -} - -static int process_stat_round_stub(struct perf_session *perf_session __maybe_unused, - union perf_event *event) -{ - if (dump_trace) - perf_event__fprintf_stat_round(event, stdout); - - dump_printf(": unhandled!\n"); - return 0; -} - -static int process_event_time_conv_stub(struct perf_session *perf_session __maybe_unused, - union perf_event *event) -{ - if (dump_trace) - perf_event__fprintf_time_conv(event, stdout); - - dump_printf(": unhandled!\n"); - return 0; -} - -static int perf_session__process_compressed_event_stub(struct perf_session *session __maybe_unused, - union perf_event *event __maybe_unused, - u64 file_offset __maybe_unused, - const char *file_path __maybe_unused) -{ - dump_printf(": unhandled!\n"); - return 0; -} - -void perf_tool__fill_defaults(struct perf_tool *tool) -{ - if (tool->sample == NULL) - tool->sample = process_event_sample_stub; - if (tool->mmap == NULL) - tool->mmap = process_event_stub; - if (tool->mmap2 == NULL) - tool->mmap2 = process_event_stub; - if (tool->comm == NULL) - tool->comm = process_event_stub; - if (tool->namespaces == NULL) - tool->namespaces = process_event_stub; - if (tool->cgroup == NULL) - tool->cgroup = process_event_stub; - if (tool->fork == NULL) - tool->fork = process_event_stub; - if (tool->exit == NULL) - tool->exit = process_event_stub; - if (tool->lost == NULL) - tool->lost = perf_event__process_lost; - if (tool->lost_samples == NULL) - tool->lost_samples = perf_event__process_lost_samples; - if (tool->aux == NULL) - tool->aux = perf_event__process_aux; - if (tool->itrace_start == NULL) - tool->itrace_start = perf_event__process_itrace_start; - if (tool->context_switch == NULL) - tool->context_switch = perf_event__process_switch; - if (tool->ksymbol == NULL) - tool->ksymbol = perf_event__process_ksymbol; - if (tool->bpf == NULL) - tool->bpf = perf_event__process_bpf; - if (tool->text_poke == NULL) - tool->text_poke = perf_event__process_text_poke; - if (tool->aux_output_hw_id == NULL) - tool->aux_output_hw_id = perf_event__process_aux_output_hw_id; - if (tool->read == NULL) - tool->read = process_event_sample_stub; - if (tool->throttle == NULL) - tool->throttle = process_event_stub; - if (tool->unthrottle == NULL) - tool->unthrottle = process_event_stub; - if (tool->attr == NULL) - tool->attr = process_event_synth_attr_stub; - if (tool->event_update == NULL) - tool->event_update = process_event_synth_event_update_stub; - if (tool->tracing_data == NULL) - tool->tracing_data = process_event_synth_tracing_data_stub; - if (tool->build_id == NULL) - tool->build_id = process_event_op2_stub; - if (tool->finished_round == NULL) { - if (tool->ordered_events) - tool->finished_round = perf_event__process_finished_round; - else - tool->finished_round = process_finished_round_stub; - } - if (tool->id_index == NULL) - tool->id_index = process_event_op2_stub; - if (tool->auxtrace_info == NULL) - tool->auxtrace_info = process_event_op2_stub; - if (tool->auxtrace == NULL) - tool->auxtrace = process_event_auxtrace_stub; - if (tool->auxtrace_error == NULL) - tool->auxtrace_error = process_event_op2_stub; - if (tool->thread_map == NULL) - tool->thread_map = process_event_thread_map_stub; - if (tool->cpu_map == NULL) - tool->cpu_map = process_event_cpu_map_stub; - if (tool->stat_config == NULL) - tool->stat_config = process_event_stat_config_stub; - if (tool->stat == NULL) - tool->stat = process_stat_stub; - if (tool->stat_round == NULL) - tool->stat_round = process_stat_round_stub; - if (tool->time_conv == NULL) - tool->time_conv = process_event_time_conv_stub; - if (tool->feature == NULL) - tool->feature = process_event_op2_stub; - if (tool->compressed == NULL) - tool->compressed = perf_session__process_compressed_event; - if (tool->finished_init == NULL) - tool->finished_init = process_event_op2_stub; -} - static void swap_sample_id_all(union perf_event *event, void *data) { void *end = (void *) event + event->header.size; @@ -1677,8 +1370,7 @@ static s64 perf_session__process_user_event(struct perf_session *session, int fd = perf_data__fd(session->data); int err; - if (event->header.type != PERF_RECORD_COMPRESSED || - tool->compressed == perf_session__process_compressed_event_stub) + if (event->header.type != PERF_RECORD_COMPRESSED || perf_tool__compressed_is_stub(tool)) dump_event(session->evlist, event, file_offset, &sample, file_path); /* These events are processed right away */ diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 6886cc85600f..7f69baeae7fb 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -92,8 +92,6 @@ int perf_session__process_events(struct perf_session *session); int perf_session__queue_event(struct perf_session *s, union perf_event *event, u64 timestamp, u64 file_offset, const char *file_path); -void perf_tool__fill_defaults(struct perf_tool *tool); - int perf_session__resolve_callchain(struct perf_session *session, struct evsel *evsel, struct thread *thread, diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c new file mode 100644 index 000000000000..17219ecb8fa6 --- /dev/null +++ b/tools/perf/util/tool.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "data.h" +#include "debug.h" +#include "header.h" +#include "session.h" +#include "stat.h" +#include "tool.h" +#include "tsc.h" +#include +#include + +#ifdef HAVE_ZSTD_SUPPORT +static int perf_session__process_compressed_event(struct perf_session *session, + union perf_event *event, u64 file_offset, + const char *file_path) +{ + void *src; + size_t decomp_size, src_size; + u64 decomp_last_rem = 0; + size_t mmap_len, decomp_len = session->header.env.comp_mmap_len; + struct decomp *decomp, *decomp_last = session->active_decomp->decomp_last; + + if (decomp_last) { + decomp_last_rem = decomp_last->size - decomp_last->head; + decomp_len += decomp_last_rem; + } + + mmap_len = sizeof(struct decomp) + decomp_len; + decomp = mmap(NULL, mmap_len, PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + if (decomp == MAP_FAILED) { + pr_err("Couldn't allocate memory for decompression\n"); + return -1; + } + + decomp->file_pos = file_offset; + decomp->file_path = file_path; + decomp->mmap_len = mmap_len; + decomp->head = 0; + + if (decomp_last_rem) { + memcpy(decomp->data, &(decomp_last->data[decomp_last->head]), decomp_last_rem); + decomp->size = decomp_last_rem; + } + + src = (void *)event + sizeof(struct perf_record_compressed); + src_size = event->pack.header.size - sizeof(struct perf_record_compressed); + + decomp_size = zstd_decompress_stream(session->active_decomp->zstd_decomp, src, src_size, + &(decomp->data[decomp_last_rem]), decomp_len - decomp_last_rem); + if (!decomp_size) { + munmap(decomp, mmap_len); + pr_err("Couldn't decompress data\n"); + return -1; + } + + decomp->size += decomp_size; + + if (session->active_decomp->decomp == NULL) + session->active_decomp->decomp = decomp; + else + session->active_decomp->decomp_last->next = decomp; + + session->active_decomp->decomp_last = decomp; + + pr_debug("decomp (B): %zd to %zd\n", src_size, decomp_size); + + return 0; +} +#endif + +static int process_event_synth_tracing_data_stub(struct perf_session *session + __maybe_unused, + union perf_event *event + __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + +static int process_event_synth_attr_stub(const struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct evlist **pevlist + __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + +static int process_event_synth_event_update_stub(const struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct evlist **pevlist + __maybe_unused) +{ + if (dump_trace) + perf_event__fprintf_event_update(event, stdout); + + dump_printf(": unhandled!\n"); + return 0; +} + +static int process_event_sample_stub(const struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_sample *sample __maybe_unused, + struct evsel *evsel __maybe_unused, + struct machine *machine __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + +static int process_event_stub(const struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_sample *sample __maybe_unused, + struct machine *machine __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + +static int process_finished_round_stub(const struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct ordered_events *oe __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + +static int skipn(int fd, off_t n) +{ + char buf[4096]; + ssize_t ret; + + while (n > 0) { + ret = read(fd, buf, min(n, (off_t)sizeof(buf))); + if (ret <= 0) + return ret; + n -= ret; + } + + return 0; +} + +static s64 process_event_auxtrace_stub(struct perf_session *session __maybe_unused, + union perf_event *event) +{ + dump_printf(": unhandled!\n"); + if (perf_data__is_pipe(session->data)) + skipn(perf_data__fd(session->data), event->auxtrace.size); + return event->auxtrace.size; +} + +static int process_event_op2_stub(struct perf_session *session __maybe_unused, + union perf_event *event __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + + +static +int process_event_thread_map_stub(struct perf_session *session __maybe_unused, + union perf_event *event __maybe_unused) +{ + if (dump_trace) + perf_event__fprintf_thread_map(event, stdout); + + dump_printf(": unhandled!\n"); + return 0; +} + +static +int process_event_cpu_map_stub(struct perf_session *session __maybe_unused, + union perf_event *event __maybe_unused) +{ + if (dump_trace) + perf_event__fprintf_cpu_map(event, stdout); + + dump_printf(": unhandled!\n"); + return 0; +} + +static +int process_event_stat_config_stub(struct perf_session *session __maybe_unused, + union perf_event *event __maybe_unused) +{ + if (dump_trace) + perf_event__fprintf_stat_config(event, stdout); + + dump_printf(": unhandled!\n"); + return 0; +} + +static int process_stat_stub(struct perf_session *perf_session __maybe_unused, + union perf_event *event) +{ + if (dump_trace) + perf_event__fprintf_stat(event, stdout); + + dump_printf(": unhandled!\n"); + return 0; +} + +static int process_stat_round_stub(struct perf_session *perf_session __maybe_unused, + union perf_event *event) +{ + if (dump_trace) + perf_event__fprintf_stat_round(event, stdout); + + dump_printf(": unhandled!\n"); + return 0; +} + +static int process_event_time_conv_stub(struct perf_session *perf_session __maybe_unused, + union perf_event *event) +{ + if (dump_trace) + perf_event__fprintf_time_conv(event, stdout); + + dump_printf(": unhandled!\n"); + return 0; +} + +static int perf_session__process_compressed_event_stub(struct perf_session *session __maybe_unused, + union perf_event *event __maybe_unused, + u64 file_offset __maybe_unused, + const char *file_path __maybe_unused) +{ + dump_printf(": unhandled!\n"); + return 0; +} + +void perf_tool__fill_defaults(struct perf_tool *tool) +{ + if (tool->sample == NULL) + tool->sample = process_event_sample_stub; + if (tool->mmap == NULL) + tool->mmap = process_event_stub; + if (tool->mmap2 == NULL) + tool->mmap2 = process_event_stub; + if (tool->comm == NULL) + tool->comm = process_event_stub; + if (tool->namespaces == NULL) + tool->namespaces = process_event_stub; + if (tool->cgroup == NULL) + tool->cgroup = process_event_stub; + if (tool->fork == NULL) + tool->fork = process_event_stub; + if (tool->exit == NULL) + tool->exit = process_event_stub; + if (tool->lost == NULL) + tool->lost = perf_event__process_lost; + if (tool->lost_samples == NULL) + tool->lost_samples = perf_event__process_lost_samples; + if (tool->aux == NULL) + tool->aux = perf_event__process_aux; + if (tool->itrace_start == NULL) + tool->itrace_start = perf_event__process_itrace_start; + if (tool->context_switch == NULL) + tool->context_switch = perf_event__process_switch; + if (tool->ksymbol == NULL) + tool->ksymbol = perf_event__process_ksymbol; + if (tool->bpf == NULL) + tool->bpf = perf_event__process_bpf; + if (tool->text_poke == NULL) + tool->text_poke = perf_event__process_text_poke; + if (tool->aux_output_hw_id == NULL) + tool->aux_output_hw_id = perf_event__process_aux_output_hw_id; + if (tool->read == NULL) + tool->read = process_event_sample_stub; + if (tool->throttle == NULL) + tool->throttle = process_event_stub; + if (tool->unthrottle == NULL) + tool->unthrottle = process_event_stub; + if (tool->attr == NULL) + tool->attr = process_event_synth_attr_stub; + if (tool->event_update == NULL) + tool->event_update = process_event_synth_event_update_stub; + if (tool->tracing_data == NULL) + tool->tracing_data = process_event_synth_tracing_data_stub; + if (tool->build_id == NULL) + tool->build_id = process_event_op2_stub; + if (tool->finished_round == NULL) { + if (tool->ordered_events) + tool->finished_round = perf_event__process_finished_round; + else + tool->finished_round = process_finished_round_stub; + } + if (tool->id_index == NULL) + tool->id_index = process_event_op2_stub; + if (tool->auxtrace_info == NULL) + tool->auxtrace_info = process_event_op2_stub; + if (tool->auxtrace == NULL) + tool->auxtrace = process_event_auxtrace_stub; + if (tool->auxtrace_error == NULL) + tool->auxtrace_error = process_event_op2_stub; + if (tool->thread_map == NULL) + tool->thread_map = process_event_thread_map_stub; + if (tool->cpu_map == NULL) + tool->cpu_map = process_event_cpu_map_stub; + if (tool->stat_config == NULL) + tool->stat_config = process_event_stat_config_stub; + if (tool->stat == NULL) + tool->stat = process_stat_stub; + if (tool->stat_round == NULL) + tool->stat_round = process_stat_round_stub; + if (tool->time_conv == NULL) + tool->time_conv = process_event_time_conv_stub; + if (tool->feature == NULL) + tool->feature = process_event_op2_stub; + if (tool->compressed == NULL) { +#ifdef HAVE_ZSTD_SUPPORT + tool->compressed = perf_session__process_compressed_event; +#else + tool->compressed = perf_session__process_compressed_event_stub; +#endif + } + if (tool->finished_init == NULL) + tool->finished_init = process_event_op2_stub; +} + +bool perf_tool__compressed_is_stub(const struct perf_tool *tool) +{ + return tool->compressed == perf_session__process_compressed_event_stub; +} diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 2b12c674768f..b917b1fa6204 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -89,4 +89,8 @@ struct perf_tool { enum show_feature_header show_feat_hdr; }; +void perf_tool__fill_defaults(struct perf_tool *tool); + +bool perf_tool__compressed_is_stub(const struct perf_tool *tool); + #endif /* __PERF_TOOL_H */ From ae737b61029ced77391e2b850433cd316716ddf9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:46:57 -0700 Subject: [PATCH 117/347] perf tool: Add perf_tool__init() Add init function that behaves like perf_tool__fill_defaults() but assumes all values haven't been initialized. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-6-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/tool.c | 58 ++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/tool.h | 1 + 2 files changed, 59 insertions(+) diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c index 17219ecb8fa6..cbd9b888bd73 100644 --- a/tools/perf/util/tool.c +++ b/tools/perf/util/tool.c @@ -230,6 +230,64 @@ static int perf_session__process_compressed_event_stub(struct perf_session *sess return 0; } +void perf_tool__init(struct perf_tool *tool, bool ordered_events) +{ + tool->ordered_events = ordered_events; + tool->ordering_requires_timestamps = false; + tool->namespace_events = false; + tool->cgroup_events = false; + tool->no_warn = false; + tool->show_feat_hdr = SHOW_FEAT_NO_HEADER; + + tool->sample = process_event_sample_stub; + tool->mmap = process_event_stub; + tool->mmap2 = process_event_stub; + tool->comm = process_event_stub; + tool->namespaces = process_event_stub; + tool->cgroup = process_event_stub; + tool->fork = process_event_stub; + tool->exit = process_event_stub; + tool->lost = perf_event__process_lost; + tool->lost_samples = perf_event__process_lost_samples; + tool->aux = perf_event__process_aux; + tool->itrace_start = perf_event__process_itrace_start; + tool->context_switch = perf_event__process_switch; + tool->ksymbol = perf_event__process_ksymbol; + tool->bpf = perf_event__process_bpf; + tool->text_poke = perf_event__process_text_poke; + tool->aux_output_hw_id = perf_event__process_aux_output_hw_id; + tool->read = process_event_sample_stub; + tool->throttle = process_event_stub; + tool->unthrottle = process_event_stub; + tool->attr = process_event_synth_attr_stub; + tool->event_update = process_event_synth_event_update_stub; + tool->tracing_data = process_event_synth_tracing_data_stub; + tool->build_id = process_event_op2_stub; + + if (ordered_events) + tool->finished_round = perf_event__process_finished_round; + else + tool->finished_round = process_finished_round_stub; + + tool->id_index = process_event_op2_stub; + tool->auxtrace_info = process_event_op2_stub; + tool->auxtrace = process_event_auxtrace_stub; + tool->auxtrace_error = process_event_op2_stub; + tool->thread_map = process_event_thread_map_stub; + tool->cpu_map = process_event_cpu_map_stub; + tool->stat_config = process_event_stat_config_stub; + tool->stat = process_stat_stub; + tool->stat_round = process_stat_round_stub; + tool->time_conv = process_event_time_conv_stub; + tool->feature = process_event_op2_stub; +#ifdef HAVE_ZSTD_SUPPORT + tool->compressed = perf_session__process_compressed_event; +#else + tool->compressed = perf_session__process_compressed_event_stub; +#endif + tool->finished_init = process_event_op2_stub; +} + void perf_tool__fill_defaults(struct perf_tool *tool) { if (tool->sample == NULL) diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index b917b1fa6204..7c7ce395e573 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -89,6 +89,7 @@ struct perf_tool { enum show_feature_header show_feat_hdr; }; +void perf_tool__init(struct perf_tool *tool, bool ordered_events); void perf_tool__fill_defaults(struct perf_tool *tool); bool perf_tool__compressed_is_stub(const struct perf_tool *tool); From f32b37cc783ac28a58ab75542de7cec2c7c01e74 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:46:58 -0700 Subject: [PATCH 118/347] perf kmem: Use perf_tool__init Reduce the scope of the tool from global/static to just that of the cmd_kmem function where the session is scoped. Use the perf_tool__init() to initialize default values. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-7-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kmem.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index 859ff018eace..b3cbac40b8c7 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -986,15 +986,6 @@ static int process_sample_event(const 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); From 584a268f50758a35b7176ecad798bbe77d672d21 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:46:59 -0700 Subject: [PATCH 119/347] perf buildid-list: Use perf_tool__init Reduce scope of build_id__mark_dso_hit_ops() to the scope of function perf_session__list_build_ids, its only use, and use perf_tool__init() for the default values. Move perf_event__exit_del_thread() to event.[ch] so it can be used in builtin-buildid-list.c. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-8-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-buildid-list.c | 10 ++++++++++ tools/perf/util/build-id.c | 32 ------------------------------- tools/perf/util/build-id.h | 4 +--- tools/perf/util/event.c | 20 +++++++++++++++++++ tools/perf/util/event.h | 4 ++++ 5 files changed, 35 insertions(+), 35 deletions(-) diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c index 383d5de36ce4..52dfacaff8e3 100644 --- a/tools/perf/builtin-buildid-list.c +++ b/tools/perf/builtin-buildid-list.c @@ -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); diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 098fcc625d91..451d145fa4ed 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -67,38 +67,6 @@ int build_id__mark_dso_hit(const struct perf_tool *tool __maybe_unused, return 0; } -static int perf_event__exit_del_thread(const struct perf_tool *tool __maybe_unused, - union perf_event *event, - struct perf_sample *sample - __maybe_unused, - struct machine *machine) -{ - struct thread *thread = machine__findnew_thread(machine, - event->fork.pid, - event->fork.tid); - - dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, - event->fork.ppid, event->fork.ptid); - - if (thread) { - machine__remove_thread(machine, thread); - thread__put(thread); - } - - return 0; -} - -struct perf_tool build_id__mark_dso_hit_ops = { - .sample = build_id__mark_dso_hit, - .mmap = perf_event__process_mmap, - .mmap2 = perf_event__process_mmap2, - .fork = perf_event__process_fork, - .exit = perf_event__exit_del_thread, - .attr = perf_event__process_attr, - .build_id = perf_event__process_build_id, - .ordered_events = true, -}; - int build_id__sprintf(const struct build_id *build_id, char *bf) { char *bid = bf; diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index ae87c4c58d5b..a212497bfdb0 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -16,11 +16,9 @@ struct build_id { size_t size; }; -struct nsinfo; - -extern struct perf_tool build_id__mark_dso_hit_ops; struct dso; struct feat_fd; +struct nsinfo; void build_id__init(struct build_id *bid, const u8 *data, size_t size); int build_id__sprintf(const struct build_id *build_id, char *bf); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index c2f0e7f40ad5..aac96d5d1917 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -426,6 +426,26 @@ int perf_event__process_exit(const struct perf_tool *tool __maybe_unused, return machine__process_exit_event(machine, event, sample); } +int perf_event__exit_del_thread(const struct perf_tool *tool __maybe_unused, + union perf_event *event, + struct perf_sample *sample __maybe_unused, + struct machine *machine) +{ + struct thread *thread = machine__findnew_thread(machine, + event->fork.pid, + event->fork.tid); + + dump_printf("(%d:%d):(%d:%d)\n", event->fork.pid, event->fork.tid, + event->fork.ppid, event->fork.ptid); + + if (thread) { + machine__remove_thread(machine, thread); + thread__put(thread); + } + + return 0; +} + size_t perf_event__fprintf_aux(union perf_event *event, FILE *fp) { return fprintf(fp, " offset: %#"PRI_lx64" size: %#"PRI_lx64" flags: %#"PRI_lx64" [%s%s%s]\n", diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 4b24f1c580fd..f8742e6230a5 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -319,6 +319,10 @@ int perf_event__process_exit(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine); +int perf_event__exit_del_thread(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine); int perf_event__process_ksymbol(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, From a01a5ef98870bd7a72f7eee0cb6168233c41bf53 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:00 -0700 Subject: [PATCH 120/347] perf kvm: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-9-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kvm.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index a3b903cf4311..692267b1b7e8 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -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; From b4fd4d00f9d4f51eab54e5b336afe27a846f1578 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:01 -0700 Subject: [PATCH 121/347] perf lock: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-10-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-lock.c | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 6efa9d646637..2c216427e929 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -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"); From 419cbc44f5b94c1bc6a68e4c694ee46fe3907fc0 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:02 -0700 Subject: [PATCH 122/347] perf evlist: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-11-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-evlist.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index 7117656939e7..818ab21c3f73 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c @@ -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, - }; + struct perf_tool tool; bool has_tracepoint = 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); From cecb1cf154b301c6f8da434adccd7bcb855d3191 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:03 -0700 Subject: [PATCH 123/347] perf record: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-12-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 33 ++++++++++++++++++++------------- tools/perf/util/tool.c | 10 +++++----- tools/perf/util/tool.h | 6 ++++++ 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 46410eb3a76b..39367709fd99 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -194,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) { @@ -1459,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); } @@ -2387,6 +2396,16 @@ 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; session = perf_session__new(data, tool); if (IS_ERR(session)) { pr_err("Perf session creation failed.\n"); @@ -3327,18 +3346,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 diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c index cbd9b888bd73..a4ca16e5eefe 100644 --- a/tools/perf/util/tool.c +++ b/tools/perf/util/tool.c @@ -99,11 +99,11 @@ static int process_event_synth_event_update_stub(const struct perf_tool *tool __ return 0; } -static int process_event_sample_stub(const struct perf_tool *tool __maybe_unused, - union perf_event *event __maybe_unused, - struct perf_sample *sample __maybe_unused, - struct evsel *evsel __maybe_unused, - struct machine *machine __maybe_unused) +int process_event_sample_stub(const struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_sample *sample __maybe_unused, + struct evsel *evsel __maybe_unused, + struct machine *machine __maybe_unused) { dump_printf(": unhandled!\n"); return 0; diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index 7c7ce395e573..c7fea58729a5 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -94,4 +94,10 @@ void perf_tool__fill_defaults(struct perf_tool *tool); bool perf_tool__compressed_is_stub(const struct perf_tool *tool); +int process_event_sample_stub(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct evsel *evsel, + struct machine *machine); + #endif /* __PERF_TOOL_H */ From 6bfb6df8663f3baf901f6540bd3f95ccb86c2b9b Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:04 -0700 Subject: [PATCH 124/347] perf c2c: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-13-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-c2c.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index 88c131d05186..cd2bd573bfc3 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -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); From 2fa28ccb17c4bfc31f7752b59e4b90856d7b33b1 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:05 -0700 Subject: [PATCH 125/347] perf script: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-14-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 65 +++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 31a6f800d55f..54598d1e815a 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -3899,38 +3899,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, }; @@ -4104,10 +4073,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; @@ -4298,6 +4265,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); From a37c0436f3799d02f13781d5e1c836b956b8c05d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:06 -0700 Subject: [PATCH 126/347] perf inject: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-15-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 91 ++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 48 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 283429ccd034..ef9cba173dd2 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -2165,47 +2165,6 @@ static int __cmd_inject(struct perf_inject *inject) int cmd_inject(int argc, const char **argv) { struct perf_inject inject = { - .tool = { - .sample = perf_event__repipe_sample, - .read = perf_event__repipe_sample, - .mmap = perf_event__repipe, - .mmap2 = perf_event__repipe, - .comm = perf_event__repipe, - .namespaces = perf_event__repipe, - .cgroup = perf_event__repipe, - .fork = perf_event__repipe, - .exit = perf_event__repipe, - .lost = perf_event__repipe, - .lost_samples = perf_event__repipe, - .aux = perf_event__repipe, - .itrace_start = perf_event__repipe, - .aux_output_hw_id = perf_event__repipe, - .context_switch = perf_event__repipe, - .throttle = perf_event__repipe, - .unthrottle = perf_event__repipe, - .ksymbol = perf_event__repipe, - .bpf = perf_event__repipe, - .text_poke = perf_event__repipe, - .attr = perf_event__repipe_attr, - .event_update = perf_event__repipe_event_update, - .tracing_data = perf_event__repipe_op2_synth, - .finished_round = perf_event__repipe_oe_synth, - .build_id = perf_event__repipe_op2_synth, - .id_index = perf_event__repipe_op2_synth, - .auxtrace_info = perf_event__repipe_op2_synth, - .auxtrace_error = perf_event__repipe_op2_synth, - .time_conv = perf_event__repipe_op2_synth, - .thread_map = perf_event__repipe_op2_synth, - .cpu_map = perf_event__repipe_op2_synth, - .stat_config = perf_event__repipe_op2_synth, - .stat = perf_event__repipe_op2_synth, - .stat_round = perf_event__repipe_op2_synth, - .feature = perf_event__repipe_op2_synth, - .finished_init = perf_event__repipe_op2_synth, - .compressed = perf_event__repipe_op4_synth, - .auxtrace = perf_event__repipe_auxtrace, - .dont_split_sample_group = true, - }, .input_name = "-", .samples = LIST_HEAD_INIT(inject.samples), .output = { @@ -2270,6 +2229,7 @@ int cmd_inject(int argc, const char **argv) "perf inject []", NULL }; + bool ordered_events; if (!inject.itrace_synth_opts.set) { /* Disable eager loading of kernel symbols that adds overhead to perf inject. */ @@ -2334,7 +2294,48 @@ int cmd_inject(int argc, const char **argv) if (strcmp(inject.input_name, "-")) repipe = false; } - + ordered_events = inject.jit_mode || inject.sched_stat || + (inject.build_ids && !inject.build_id_all); + perf_tool__init(&inject.tool, ordered_events); + inject.tool.sample = perf_event__repipe_sample; + inject.tool.read = perf_event__repipe_sample; + inject.tool.mmap = perf_event__repipe; + inject.tool.mmap2 = perf_event__repipe; + inject.tool.comm = perf_event__repipe; + inject.tool.namespaces = perf_event__repipe; + inject.tool.cgroup = perf_event__repipe; + inject.tool.fork = perf_event__repipe; + inject.tool.exit = perf_event__repipe; + inject.tool.lost = perf_event__repipe; + inject.tool.lost_samples = perf_event__repipe; + inject.tool.aux = perf_event__repipe; + inject.tool.itrace_start = perf_event__repipe; + inject.tool.aux_output_hw_id = perf_event__repipe; + inject.tool.context_switch = perf_event__repipe; + inject.tool.throttle = perf_event__repipe; + inject.tool.unthrottle = perf_event__repipe; + inject.tool.ksymbol = perf_event__repipe; + inject.tool.bpf = perf_event__repipe; + inject.tool.text_poke = perf_event__repipe; + inject.tool.attr = perf_event__repipe_attr; + inject.tool.event_update = perf_event__repipe_event_update; + inject.tool.tracing_data = perf_event__repipe_op2_synth; + inject.tool.finished_round = perf_event__repipe_oe_synth; + inject.tool.build_id = perf_event__repipe_op2_synth; + inject.tool.id_index = perf_event__repipe_op2_synth; + inject.tool.auxtrace_info = perf_event__repipe_op2_synth; + inject.tool.auxtrace_error = perf_event__repipe_op2_synth; + inject.tool.time_conv = perf_event__repipe_op2_synth; + inject.tool.thread_map = perf_event__repipe_op2_synth; + inject.tool.cpu_map = perf_event__repipe_op2_synth; + inject.tool.stat_config = perf_event__repipe_op2_synth; + inject.tool.stat = perf_event__repipe_op2_synth; + inject.tool.stat_round = perf_event__repipe_op2_synth; + inject.tool.feature = perf_event__repipe_op2_synth; + inject.tool.finished_init = perf_event__repipe_op2_synth; + inject.tool.compressed = perf_event__repipe_op4_synth; + inject.tool.auxtrace = perf_event__repipe_auxtrace; + inject.tool.dont_split_sample_group = true; inject.session = __perf_session__new(&data, repipe, output_fd(&inject), &inject.tool); @@ -2373,7 +2374,6 @@ int cmd_inject(int argc, const char **argv) * mmaps. We cannot generate the buildid hit list and * inject the jit mmaps at the same time for now. */ - inject.tool.ordered_events = true; inject.tool.ordering_requires_timestamps = true; if (known_build_ids != NULL) { inject.known_build_ids = @@ -2386,15 +2386,10 @@ int cmd_inject(int argc, const char **argv) } } - if (inject.sched_stat) { - inject.tool.ordered_events = true; - } - #ifdef HAVE_JITDUMP if (inject.jit_mode) { inject.tool.mmap2 = perf_event__jit_repipe_mmap2; inject.tool.mmap = perf_event__jit_repipe_mmap; - inject.tool.ordered_events = true; inject.tool.ordering_requires_timestamps = true; /* * JIT MMAP injection injects all MMAP events in one go, so it From 113f614c6dd0c2fb13f4c6d7f3c4a82212de0c0f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:07 -0700 Subject: [PATCH 127/347] perf report: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-16-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 55 ++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 5f609a7791ea..dfb47fa85e5c 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -799,7 +799,7 @@ static int process_attr(const struct perf_tool *tool __maybe_unused, 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; @@ -817,8 +817,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; @@ -1272,30 +1271,6 @@ 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, @@ -1477,6 +1452,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 +1507,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 +1538,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); From 071b117e755bd5000ea13f488e8d19ed9e42121c Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:08 -0700 Subject: [PATCH 128/347] perf stat: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-17-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index d44e708f8a44..487eff9a8582 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -2271,15 +2271,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 +2313,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); From d48940cabcf70c186901f2c75fe89fed12d4ec18 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:09 -0700 Subject: [PATCH 129/347] perf annotate: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-18-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 42 +++++++++++++++++------------------ 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index cc65e6f8f4da..d6f6ba5a569d 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -696,28 +696,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, }; @@ -876,6 +855,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); From 41860d49473c0c09dc0a2a4d148047f97aaa2539 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:10 -0700 Subject: [PATCH 130/347] perf sched: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-19-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 2c60bd3a8149..34fe8e540c43 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3100,7 +3100,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; @@ -3605,14 +3604,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, @@ -3733,6 +3724,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) From 4a20562bc4118478b4592bab8f0d90975df77095 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:11 -0700 Subject: [PATCH 131/347] perf mem: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-20-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-mem.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 0fb4d75ab959..18e5f9a0ddc2 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -276,7 +276,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); @@ -458,21 +474,6 @@ 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 From 60b5fd3f62d0622b70450d4707dc30be47a724c7 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:12 -0700 Subject: [PATCH 132/347] perf timechart: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-21-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-timechart.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 5bf818baa662..218c8b44d7be 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -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, From 1e1ec8f2e5fa914da0155170c63099d7189f3cfc Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:13 -0700 Subject: [PATCH 133/347] perf diff: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-22-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-diff.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 4c0567882a7a..28c5208fcdc9 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -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) @@ -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); From b9d276d1a282dfca186d0dbc2a001581b7c6f5b7 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:14 -0700 Subject: [PATCH 134/347] perf data convert json: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-23-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/data-convert-json.c | 43 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/tools/perf/util/data-convert-json.c b/tools/perf/util/data-convert-json.c index 905ea9823f9d..20bfb0884e9e 100644 --- a/tools/perf/util/data-convert-json.c +++ b/tools/perf/util/data-convert-json.c @@ -316,39 +316,36 @@ int bt_convert__perf2json(const char *input_name, const char *output_name, struct perf_session *session; int fd; int ret = -1; - struct convert_json c = { - .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, -#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, - .ordered_events = true, - .ordering_requires_timestamps = true, - }, .first = true, .events_count = 0, }; - struct perf_data data = { .mode = PERF_DATA_MODE_READ, .path = input_name, .force = opts->force, }; + perf_tool__init(&c.tool, /*ordered_events=*/true); + c.tool.sample = process_sample_event; + c.tool.mmap = perf_event__process_mmap; + c.tool.mmap2 = perf_event__process_mmap2; + c.tool.comm = perf_event__process_comm; + c.tool.namespaces = perf_event__process_namespaces; + c.tool.cgroup = perf_event__process_cgroup; + c.tool.exit = perf_event__process_exit; + c.tool.fork = perf_event__process_fork; + c.tool.lost = perf_event__process_lost; +#ifdef HAVE_LIBTRACEEVENT + c.tool.tracing_data = perf_event__process_tracing_data; +#endif + c.tool.build_id = perf_event__process_build_id; + c.tool.id_index = perf_event__process_id_index; + c.tool.auxtrace_info = perf_event__process_auxtrace_info; + c.tool.auxtrace = perf_event__process_auxtrace; + c.tool.event_update = perf_event__process_event_update; + c.tool.ordering_requires_timestamps = true; + if (opts->all) { pr_err("--all is currently unsupported for JSON output.\n"); goto err; From 2721c6cc04d79057b855411a16964965d5a3da0a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:15 -0700 Subject: [PATCH 135/347] perf data convert ctf: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-24-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/data-convert-bt.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/tools/perf/util/data-convert-bt.c b/tools/perf/util/data-convert-bt.c index 9e2170604b66..021e9b1d5cc5 100644 --- a/tools/perf/util/data-convert-bt.c +++ b/tools/perf/util/data-convert-bt.c @@ -1607,25 +1607,23 @@ int bt_convert__perf2ctf(const char *input, const char *path, .mode = PERF_DATA_MODE_READ, .force = opts->force, }; - struct convert c = { - .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, - .tracing_data = perf_event__process_tracing_data, - .build_id = perf_event__process_build_id, - .namespaces = perf_event__process_namespaces, - .ordered_events = true, - .ordering_requires_timestamps = true, - }, - }; + struct convert c = {}; struct ctf_writer *cw = &c.writer; int err; + perf_tool__init(&c.tool, /*ordered_events=*/true); + c.tool.sample = process_sample_event; + c.tool.mmap = perf_event__process_mmap; + c.tool.mmap2 = perf_event__process_mmap2; + c.tool.comm = perf_event__process_comm; + c.tool.exit = perf_event__process_exit; + c.tool.fork = perf_event__process_fork; + c.tool.lost = perf_event__process_lost; + c.tool.tracing_data = perf_event__process_tracing_data; + c.tool.build_id = perf_event__process_build_id; + c.tool.namespaces = perf_event__process_namespaces; + c.tool.ordering_requires_timestamps = true; + if (opts->all) { c.tool.comm = process_comm_event; c.tool.exit = process_exit_event; From 332b897f34b95fea32c998ceca0569ac2c049d3d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:16 -0700 Subject: [PATCH 136/347] perf test event_update: Ensure tools is initialized Ensure tool is initialized to avoid lazy initialization pattern so that more uses of struct perf_tool can be made const. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-25-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/event_update.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/tests/event_update.c b/tools/perf/tests/event_update.c index fdecad920f59..d6b4ce3ef4ee 100644 --- a/tools/perf/tests/event_update.c +++ b/tools/perf/tests/event_update.c @@ -103,6 +103,7 @@ static int test__event_update(struct test_suite *test __maybe_unused, int subtes TEST_ASSERT_VAL("failed to synthesize attr update scale", !perf_event__synthesize_event_update_scale(NULL, evsel, process_event_scale)); + perf_tool__init(&tmp.tool, /*ordered_events=*/false); tmp.name = evsel__name(evsel); TEST_ASSERT_VAL("failed to synthesize attr update name", From fcd00f3e3b375ee87caf5be587840441ef1b4243 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:17 -0700 Subject: [PATCH 137/347] perf kwork: Use perf_tool__init() Use perf_tool__init() so that more uses of 'struct perf_tool' can be const and not relying on perf_tool__fill_defaults(). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-26-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-kwork.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index 8ffaa80a2d1d..6a4281b8fd10 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -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); From 15d4a6f41d7275799d318b198f479c9783d56f78 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:18 -0700 Subject: [PATCH 138/347] perf tool: Remove perf_tool__fill_defaults() Now all tools are fully initialized prior to use it has no use so remove. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-27-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 6 --- tools/perf/util/tool.c | 89 --------------------------------------- tools/perf/util/tool.h | 1 - 3 files changed, 96 deletions(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 5c0e0341d39e..a8404cc99cfc 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1781,8 +1781,6 @@ static int __perf_session__process_pipe_events(struct perf_session *session) void *p; bool update_prog = false; - perf_tool__fill_defaults(tool); - /* * If it's from a file saving pipe data (by redirection), it would have * a file name other than "-". Then we can get the total size and show @@ -2206,8 +2204,6 @@ static int __perf_session__process_events(struct perf_session *session) struct ui_progress prog; int err; - perf_tool__fill_defaults(tool); - if (rd.data_size == 0) return -1; @@ -2260,8 +2256,6 @@ static int __perf_session__process_dir_events(struct perf_session *session) u64 total_size = perf_data__size(session->data); struct reader *rd; - perf_tool__fill_defaults(tool); - ui_progress__init_size(&prog, total_size, "Processing events..."); nr_readers = 1; diff --git a/tools/perf/util/tool.c b/tools/perf/util/tool.c index a4ca16e5eefe..3b7f390f26eb 100644 --- a/tools/perf/util/tool.c +++ b/tools/perf/util/tool.c @@ -288,95 +288,6 @@ void perf_tool__init(struct perf_tool *tool, bool ordered_events) tool->finished_init = process_event_op2_stub; } -void perf_tool__fill_defaults(struct perf_tool *tool) -{ - if (tool->sample == NULL) - tool->sample = process_event_sample_stub; - if (tool->mmap == NULL) - tool->mmap = process_event_stub; - if (tool->mmap2 == NULL) - tool->mmap2 = process_event_stub; - if (tool->comm == NULL) - tool->comm = process_event_stub; - if (tool->namespaces == NULL) - tool->namespaces = process_event_stub; - if (tool->cgroup == NULL) - tool->cgroup = process_event_stub; - if (tool->fork == NULL) - tool->fork = process_event_stub; - if (tool->exit == NULL) - tool->exit = process_event_stub; - if (tool->lost == NULL) - tool->lost = perf_event__process_lost; - if (tool->lost_samples == NULL) - tool->lost_samples = perf_event__process_lost_samples; - if (tool->aux == NULL) - tool->aux = perf_event__process_aux; - if (tool->itrace_start == NULL) - tool->itrace_start = perf_event__process_itrace_start; - if (tool->context_switch == NULL) - tool->context_switch = perf_event__process_switch; - if (tool->ksymbol == NULL) - tool->ksymbol = perf_event__process_ksymbol; - if (tool->bpf == NULL) - tool->bpf = perf_event__process_bpf; - if (tool->text_poke == NULL) - tool->text_poke = perf_event__process_text_poke; - if (tool->aux_output_hw_id == NULL) - tool->aux_output_hw_id = perf_event__process_aux_output_hw_id; - if (tool->read == NULL) - tool->read = process_event_sample_stub; - if (tool->throttle == NULL) - tool->throttle = process_event_stub; - if (tool->unthrottle == NULL) - tool->unthrottle = process_event_stub; - if (tool->attr == NULL) - tool->attr = process_event_synth_attr_stub; - if (tool->event_update == NULL) - tool->event_update = process_event_synth_event_update_stub; - if (tool->tracing_data == NULL) - tool->tracing_data = process_event_synth_tracing_data_stub; - if (tool->build_id == NULL) - tool->build_id = process_event_op2_stub; - if (tool->finished_round == NULL) { - if (tool->ordered_events) - tool->finished_round = perf_event__process_finished_round; - else - tool->finished_round = process_finished_round_stub; - } - if (tool->id_index == NULL) - tool->id_index = process_event_op2_stub; - if (tool->auxtrace_info == NULL) - tool->auxtrace_info = process_event_op2_stub; - if (tool->auxtrace == NULL) - tool->auxtrace = process_event_auxtrace_stub; - if (tool->auxtrace_error == NULL) - tool->auxtrace_error = process_event_op2_stub; - if (tool->thread_map == NULL) - tool->thread_map = process_event_thread_map_stub; - if (tool->cpu_map == NULL) - tool->cpu_map = process_event_cpu_map_stub; - if (tool->stat_config == NULL) - tool->stat_config = process_event_stat_config_stub; - if (tool->stat == NULL) - tool->stat = process_stat_stub; - if (tool->stat_round == NULL) - tool->stat_round = process_stat_round_stub; - if (tool->time_conv == NULL) - tool->time_conv = process_event_time_conv_stub; - if (tool->feature == NULL) - tool->feature = process_event_op2_stub; - if (tool->compressed == NULL) { -#ifdef HAVE_ZSTD_SUPPORT - tool->compressed = perf_session__process_compressed_event; -#else - tool->compressed = perf_session__process_compressed_event_stub; -#endif - } - if (tool->finished_init == NULL) - tool->finished_init = process_event_op2_stub; -} - bool perf_tool__compressed_is_stub(const struct perf_tool *tool) { return tool->compressed == perf_session__process_compressed_event_stub; diff --git a/tools/perf/util/tool.h b/tools/perf/util/tool.h index c7fea58729a5..db1c7642b0d1 100644 --- a/tools/perf/util/tool.h +++ b/tools/perf/util/tool.h @@ -90,7 +90,6 @@ struct perf_tool { }; void perf_tool__init(struct perf_tool *tool, bool ordered_events); -void perf_tool__fill_defaults(struct perf_tool *tool); bool perf_tool__compressed_is_stub(const struct perf_tool *tool); From 8f29be326da202963e3e6d33550d375313009f68 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 13:47:19 -0700 Subject: [PATCH 139/347] perf session: Constify tool Make tool const now that all uses are const and perf_tool__fill_defaults() won't be used. The aim is to better capture that sessions don't mutate tools. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anshuman Khandual Cc: Athira Rajeev Cc: Huacai Chen Cc: Ilkka Koskinen Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Jonathan Cameron Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Nick Desaulniers Cc: Nick Terrell Cc: Oliver Upton Cc: Peter Zijlstra Cc: Song Liu Cc: Sun Haiyong Cc: Suzuki Poulouse Cc: Will Deacon Cc: Yanteng Si Cc: Yicong Yang Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240812204720.631678-28-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.c | 6 +++--- tools/perf/util/session.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index a8404cc99cfc..d2bd563119bc 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1770,7 +1770,7 @@ static int __perf_session__process_decomp_events(struct perf_session *session); static int __perf_session__process_pipe_events(struct perf_session *session) { struct ordered_events *oe = &session->ordered_events; - struct perf_tool *tool = session->tool; + const struct perf_tool *tool = session->tool; struct ui_progress prog; union perf_event *event; uint32_t size, cur_size = 0; @@ -2200,7 +2200,7 @@ static int __perf_session__process_events(struct perf_session *session) .in_place_update = session->data->in_place_update, }; struct ordered_events *oe = &session->ordered_events; - struct perf_tool *tool = session->tool; + const struct perf_tool *tool = session->tool; struct ui_progress prog; int err; @@ -2250,7 +2250,7 @@ out_err: static int __perf_session__process_dir_events(struct perf_session *session) { struct perf_data *data = session->data; - struct perf_tool *tool = session->tool; + const struct perf_tool *tool = session->tool; int i, ret, readers, nr_readers; struct ui_progress prog; u64 total_size = perf_data__size(session->data); diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 7f69baeae7fb..7c8dd6956330 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -43,7 +43,7 @@ struct perf_session { u64 one_mmap_offset; struct ordered_events ordered_events; struct perf_data *data; - struct perf_tool *tool; + const struct perf_tool *tool; u64 bytes_transferred; u64 bytes_compressed; struct zstd_data zstd_data; From 807746b9bdc2acc9b18130d0a7ff0be1626ccc76 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 20 Jul 2024 02:20:54 -0400 Subject: [PATCH 140/347] perf parse-events: Add a retirement latency modifier Retirement latency is a separate sampled count used on newer Intel CPUs. Reviewed-by: Namhyung Kim Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Caleb Biggers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Perry Taylor Cc: Peter Zijlstra Cc: Samantha Alt Link: https://lore.kernel.org/r/20240720062102.444578-2-weilin.wang@intel.com Signed-off-by: Weilin Wang Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.h | 1 + tools/perf/util/parse-events.c | 2 ++ tools/perf/util/parse-events.h | 1 + tools/perf/util/parse-events.l | 3 ++- 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 80b5f6dd868e..14f777b9e03e 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -98,6 +98,7 @@ struct evsel { bool bpf_counter; bool use_config_name; bool skippable; + bool retire_lat; int bpf_fd; struct bpf_object *bpf_obj; struct list_head config_terms; diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 321586fb5556..fab01ba54e34 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1811,6 +1811,8 @@ static int parse_events__modifier_list(struct parse_events_state *parse_state, evsel->weak_group = true; if (mod.bpf) evsel->bpf_counter = true; + if (mod.retire_lat) + evsel->retire_lat = true; } return 0; } diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index e13de2c8b706..b735cd9e0acf 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -203,6 +203,7 @@ struct parse_events_modifier { bool hypervisor : 1; /* 'h' */ bool guest : 1; /* 'G' */ bool host : 1; /* 'H' */ + bool retire_lat : 1; /* 'R' */ }; int parse_events__modifier_event(struct parse_events_state *parse_state, void *loc, diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l index 16045c383ada..5a0bcd7f166a 100644 --- a/tools/perf/util/parse-events.l +++ b/tools/perf/util/parse-events.l @@ -209,6 +209,7 @@ static int modifiers(struct parse_events_state *parse_state, yyscan_t scanner) CASE('W', weak); CASE('e', exclusive); CASE('b', bpf); + CASE('R', retire_lat); default: return PE_ERROR; } @@ -250,7 +251,7 @@ drv_cfg_term [a-zA-Z0-9_\.]+(=[a-zA-Z0-9_*?\.:]+)? * If you add a modifier you need to update check_modifier(). * Also, the letters in modifier_event must not be in modifier_bp. */ -modifier_event [ukhpPGHSDIWeb]{1,15} +modifier_event [ukhpPGHSDIWebR]{1,16} modifier_bp [rwx]{1,3} lc_type (L1-dcache|l1-d|l1d|L1-data|L1-icache|l1-i|l1i|L1-instruction|LLC|L2|dTLB|d-tlb|Data-TLB|iTLB|i-tlb|Instruction-TLB|branch|branches|bpu|btb|bpc|node) lc_op_result (load|loads|read|store|stores|write|prefetch|prefetches|speculative-read|speculative-load|refs|Reference|ops|access|misses|miss) From a9a4ca5767c1756b1bc5bd3397b515af2cbc2c82 Mon Sep 17 00:00:00 2001 From: Weilin Wang Date: Sat, 20 Jul 2024 02:20:55 -0400 Subject: [PATCH 141/347] perf data: Allow to use given fd in data->file.fd When in PIPE mode, allow to use fd dynamically opened and asigned to data->file.fd instead of STDIN_FILENO or STDOUT_FILENO. Reviewed-by: Namhyung Kim Signed-off-by: Weilin Wang Acked-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Caleb Biggers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Perry Taylor Cc: Peter Zijlstra Cc: Samantha Alt Link: https://lore.kernel.org/r/20240720062102.444578-3-weilin.wang@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/data.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index 08c4bfbd817f..98661ede2a73 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c @@ -204,7 +204,12 @@ static bool check_pipe(struct perf_data *data) data->file.fd = fd; data->use_stdio = false; } - } else { + + /* + * When is_pipe and data->file.fd is given, use given fd + * instead of STDIN_FILENO or STDOUT_FILENO + */ + } else if (data->file.fd <= 0) { data->file.fd = fd; } } From 8db5cabcf1b6f9d62bd024aa6293de715d75518f Mon Sep 17 00:00:00 2001 From: Weilin Wang Date: Sat, 20 Jul 2024 02:20:56 -0400 Subject: [PATCH 142/347] perf stat: Fork and launch 'perf record' when 'perf stat' needs to get retire latency value for a metric. When retire_latency value is used in a metric formula, evsel would fork a 'perf record' process with "-e" and "-W" options. 'perf record' will collect required retire_latency values in parallel while 'perf stat' is collecting counting values. At the point of time that 'perf stat' stops counting, evsel would stop 'perf record' by sending sigterm signal to 'perf record' process. Sampled data will be processed to get retire latency value. Another thread is required to synchronize between 'perf stat' and 'perf record' when we pass data through pipe. Retire_latency evsel is not opened for 'perf stat' so that there is no counter wasted on it. This commit includes code suggested by Namhyung to adjust reading size for groups that include retire_latency evsels. In current :R parsing implementation, the parser would recognize events with retire_latency modifier and insert them into the evlist like a normal event. Ideally, we need to avoid counting these events. In this commit, at the time when a retire_latency evsel is read, set the retire latency value processed from the sampled data to count value. This sampled retire latency value will be used for metric calculation and final event count print out. No special metric calculation and event print out code required for retire_latency events. Reviewed-by: Namhyung Kim Signed-off-by: Weilin Wang Acked-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Caleb Biggers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Perry Taylor Cc: Peter Zijlstra Cc: Samantha Alt Link: https://lore.kernel.org/r/20240720062102.444578-4-weilin.wang@intel.com [ Squashed the 3rd and 4th commit in the series to keep it building patch by patch ] [ Constified the 'struct perf_tool' pointer in process_sample_event() ] [ Use perf_tool__init(&tool, false) to address a segfault I reported and Ian/Weilin diagnosed ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/util/evlist.c | 6 + tools/perf/builtin-stat.c | 4 + tools/perf/util/Build | 1 + tools/perf/util/evlist.c | 2 + tools/perf/util/evsel.c | 81 +++++- tools/perf/util/evsel.h | 5 + tools/perf/util/intel-tpebs.c | 432 ++++++++++++++++++++++++++++++ tools/perf/util/intel-tpebs.h | 35 +++ 8 files changed, 564 insertions(+), 2 deletions(-) create mode 100644 tools/perf/util/intel-tpebs.c create mode 100644 tools/perf/util/intel-tpebs.h diff --git a/tools/perf/arch/x86/util/evlist.c b/tools/perf/arch/x86/util/evlist.c index b1ce0c52d88d..cebdd483149e 100644 --- a/tools/perf/arch/x86/util/evlist.c +++ b/tools/perf/arch/x86/util/evlist.c @@ -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; } diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 487eff9a8582..6162b98fc941 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -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 @@ -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; } diff --git a/tools/perf/util/Build b/tools/perf/util/Build index f6f18b4228bb..7ea261416c14 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -156,6 +156,7 @@ perf-util-y += clockid.o perf-util-y += list_sort.o perf-util-y += mutex.o perf-util-y += sharded_mutex.o +perf-util-$(CONFIG_X86_64) += intel-tpebs.o perf-util-$(CONFIG_LIBBPF) += bpf_map.o perf-util-$(CONFIG_PERF_BPF_SKEL) += bpf_counter.o diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 1417f9a23083..4b2538e4f679 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -33,6 +33,7 @@ #include "util/bpf-filter.h" #include "util/stat.h" #include "util/util.h" +#include "util/intel-tpebs.h" #include #include #include @@ -179,6 +180,7 @@ void evlist__delete(struct evlist *evlist) if (evlist == NULL) return; + tpebs_delete(); evlist__free_stats(evlist); evlist__munmap(evlist); evlist__close(evlist); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index bc603193c477..d607056b73c9 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -59,6 +59,7 @@ #include #include #include +#include "util/intel-tpebs.h" #include @@ -1539,6 +1540,11 @@ static int evsel__read_one(struct evsel *evsel, int cpu_map_idx, int thread) return perf_evsel__read(&evsel->core, cpu_map_idx, thread, count); } +static int evsel__read_retire_lat(struct evsel *evsel, int cpu_map_idx, int thread) +{ + return tpebs_set_evsel(evsel, cpu_map_idx, thread); +} + static void evsel__set_count(struct evsel *counter, int cpu_map_idx, int thread, u64 val, u64 ena, u64 run, u64 lost) { @@ -1546,6 +1552,12 @@ static void evsel__set_count(struct evsel *counter, int cpu_map_idx, int thread, count = perf_counts(counter->counts, cpu_map_idx, thread); + if (counter->retire_lat) { + evsel__read_retire_lat(counter, cpu_map_idx, thread); + perf_counts__set_loaded(counter->counts, cpu_map_idx, thread, true); + return; + } + count->val = val; count->ena = ena; count->run = run; @@ -1554,6 +1566,60 @@ static void evsel__set_count(struct evsel *counter, int cpu_map_idx, int thread, perf_counts__set_loaded(counter->counts, cpu_map_idx, thread, true); } +static bool evsel__group_has_tpebs(struct evsel *leader) +{ + struct evsel *evsel; + + for_each_group_evsel(evsel, leader) { + if (evsel__is_retire_lat(evsel)) + return true; + } + return false; +} + +static u64 evsel__group_read_nr_members(struct evsel *leader) +{ + u64 nr = leader->core.nr_members; + struct evsel *evsel; + + for_each_group_evsel(evsel, leader) { + if (evsel__is_retire_lat(evsel)) + nr--; + } + return nr; +} + +static u64 evsel__group_read_size(struct evsel *leader) +{ + u64 read_format = leader->core.attr.read_format; + int entry = sizeof(u64); /* value */ + int size = 0; + int nr = 1; + + if (!evsel__group_has_tpebs(leader)) + return perf_evsel__read_size(&leader->core); + + if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) + size += sizeof(u64); + + if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING) + size += sizeof(u64); + + if (read_format & PERF_FORMAT_ID) + entry += sizeof(u64); + + if (read_format & PERF_FORMAT_LOST) + entry += sizeof(u64); + + if (read_format & PERF_FORMAT_GROUP) { + nr = evsel__group_read_nr_members(leader); + size += sizeof(u64); + } + + size += entry * nr; + return size; +} + static int evsel__process_group_data(struct evsel *leader, int cpu_map_idx, int thread, u64 *data) { u64 read_format = leader->core.attr.read_format; @@ -1562,7 +1628,7 @@ static int evsel__process_group_data(struct evsel *leader, int cpu_map_idx, int nr = *data++; - if (nr != (u64) leader->core.nr_members) + if (nr != evsel__group_read_nr_members(leader)) return -EINVAL; if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED) @@ -1592,7 +1658,7 @@ static int evsel__read_group(struct evsel *leader, int cpu_map_idx, int thread) { struct perf_stat_evsel *ps = leader->stats; u64 read_format = leader->core.attr.read_format; - int size = perf_evsel__read_size(&leader->core); + int size = evsel__group_read_size(leader); u64 *data = ps->group_data; if (!(read_format & PERF_FORMAT_ID)) @@ -1784,6 +1850,9 @@ int evsel__read_counter(struct evsel *evsel, int cpu_map_idx, int thread) if (evsel__is_tool(evsel)) return evsel__read_tool(evsel, cpu_map_idx, thread); + if (evsel__is_retire_lat(evsel)) + return evsel__read_retire_lat(evsel, cpu_map_idx, thread); + if (evsel->core.attr.read_format & PERF_FORMAT_GROUP) return evsel__read_group(evsel, cpu_map_idx, thread); @@ -2200,6 +2269,9 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, return 0; } + if (evsel__is_retire_lat(evsel)) + return tpebs_start(evsel->evlist); + err = __evsel__prepare_open(evsel, cpus, threads); if (err) return err; @@ -2392,6 +2464,8 @@ int evsel__open(struct evsel *evsel, struct perf_cpu_map *cpus, void evsel__close(struct evsel *evsel) { + if (evsel__is_retire_lat(evsel)) + tpebs_delete(); perf_evsel__close(&evsel->core); perf_evsel__free_id(&evsel->core); } @@ -3357,6 +3431,9 @@ static int store_evsel_ids(struct evsel *evsel, struct evlist *evlist) { int cpu_map_idx, thread; + if (evsel__is_retire_lat(evsel)) + return 0; + for (cpu_map_idx = 0; cpu_map_idx < xyarray__max_x(evsel->core.fd); cpu_map_idx++) { for (thread = 0; thread < xyarray__max_y(evsel->core.fd); thread++) { diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 14f777b9e03e..a5da4b03bb1c 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -311,6 +311,11 @@ static inline bool evsel__is_tool(const struct evsel *evsel) return evsel->tool_event != PERF_TOOL_NONE; } +static inline bool evsel__is_retire_lat(const struct evsel *evsel) +{ + return evsel->retire_lat; +} + const char *evsel__group_name(struct evsel *evsel); int evsel__group_desc(struct evsel *evsel, char *buf, size_t size); diff --git a/tools/perf/util/intel-tpebs.c b/tools/perf/util/intel-tpebs.c new file mode 100644 index 000000000000..50a3c3e07160 --- /dev/null +++ b/tools/perf/util/intel-tpebs.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * intel_tpebs.c: Intel TPEBS support + */ + + +#include +#include +#include +#include "intel-tpebs.h" +#include +#include +#include +#include "sample.h" +#include "debug.h" +#include "evlist.h" +#include "evsel.h" +#include "session.h" +#include "tool.h" +#include "cpumap.h" +#include "metricgroup.h" +#include +#include +#include +#include + +#define PERF_DATA "-" + +bool tpebs_recording; +static pid_t tpebs_pid = -1; +static size_t tpebs_event_size; +static LIST_HEAD(tpebs_results); +static pthread_t tpebs_reader_thread; +static struct child_process *tpebs_cmd; + +struct tpebs_retire_lat { + struct list_head nd; + /* Event name */ + const char *name; + /* Event name with the TPEBS modifier R */ + const char *tpebs_name; + /* Count of retire_latency values found in sample data */ + size_t count; + /* Sum of all the retire_latency values in sample data */ + int sum; + /* Average of retire_latency, val = sum / count */ + double val; +}; + +static int get_perf_record_args(const char **record_argv, char buf[], + const char *cpumap_buf) +{ + struct tpebs_retire_lat *e; + int i = 0; + + pr_debug("tpebs: Prepare perf record for retire_latency\n"); + + record_argv[i++] = "perf"; + record_argv[i++] = "record"; + record_argv[i++] = "-W"; + record_argv[i++] = "--synth=no"; + record_argv[i++] = buf; + + if (!cpumap_buf) { + pr_err("tpebs: Require cpumap list to run sampling\n"); + return -ECANCELED; + } + /* Use -C when cpumap_buf is not "-1" */ + if (strcmp(cpumap_buf, "-1")) { + record_argv[i++] = "-C"; + record_argv[i++] = cpumap_buf; + } + + list_for_each_entry(e, &tpebs_results, nd) { + record_argv[i++] = "-e"; + record_argv[i++] = e->name; + } + + record_argv[i++] = "-o"; + record_argv[i++] = PERF_DATA; + + return 0; +} + +static int prepare_run_command(const char **argv) +{ + tpebs_cmd = zalloc(sizeof(struct child_process)); + if (!tpebs_cmd) + return -ENOMEM; + tpebs_cmd->argv = argv; + tpebs_cmd->out = -1; + return 0; +} + +static int start_perf_record(int control_fd[], int ack_fd[], + const char *cpumap_buf) +{ + const char **record_argv; + int ret; + char buf[32]; + + scnprintf(buf, sizeof(buf), "--control=fd:%d,%d", control_fd[0], ack_fd[1]); + + record_argv = calloc(12 + 2 * tpebs_event_size, sizeof(char *)); + if (!record_argv) + return -ENOMEM; + + ret = get_perf_record_args(record_argv, buf, cpumap_buf); + if (ret) + goto out; + + ret = prepare_run_command(record_argv); + if (ret) + goto out; + ret = start_command(tpebs_cmd); +out: + free(record_argv); + return ret; +} + +static int process_sample_event(const struct perf_tool *tool __maybe_unused, + union perf_event *event __maybe_unused, + struct perf_sample *sample, + struct evsel *evsel, + struct machine *machine __maybe_unused) +{ + int ret = 0; + const char *evname; + struct tpebs_retire_lat *t; + + evname = evsel__name(evsel); + + /* + * Need to handle per core results? We are assuming average retire + * latency value will be used. Save the number of samples and the sum of + * retire latency value for each event. + */ + list_for_each_entry(t, &tpebs_results, nd) { + if (!strcmp(evname, t->name)) { + t->count += 1; + t->sum += sample->retire_lat; + t->val = (double) t->sum / t->count; + break; + } + } + + return ret; +} + +static int process_feature_event(struct perf_session *session, + union perf_event *event) +{ + if (event->feat.feat_id < HEADER_LAST_FEATURE) + return perf_event__process_feature(session, event); + return 0; +} + +static void *__sample_reader(void *arg) +{ + struct child_process *child = arg; + struct perf_session *session; + struct perf_data data = { + .mode = PERF_DATA_MODE_READ, + .path = PERF_DATA, + .file.fd = child->out, + }; + struct perf_tool tool; + + perf_tool__init(&tool, /*ordered_events=*/false); + tool.sample = process_sample_event; + tool.feature = process_feature_event; + tool.attr = perf_event__process_attr; + + session = perf_session__new(&data, &tool); + if (IS_ERR(session)) + return NULL; + perf_session__process_events(session); + perf_session__delete(session); + + return NULL; +} + +/* + * tpebs_stop - stop the sample data read thread and the perf record process. + */ +static int tpebs_stop(void) +{ + int ret = 0; + + /* Like tpebs_start, we should only run tpebs_end once. */ + if (tpebs_pid != -1) { + kill(tpebs_cmd->pid, SIGTERM); + tpebs_pid = -1; + pthread_join(tpebs_reader_thread, NULL); + close(tpebs_cmd->out); + ret = finish_command(tpebs_cmd); + if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL) + ret = 0; + } + return ret; +} + +/* + * tpebs_start - start tpebs execution. + * @evsel_list: retire_latency evsels in this list will be selected and sampled + * to get the average retire_latency value. + * + * This function will be called from evlist level later when evlist__open() is + * called consistently. + */ +int tpebs_start(struct evlist *evsel_list) +{ + int ret = 0; + struct evsel *evsel; + char cpumap_buf[50]; + + /* + * We should only run tpebs_start when tpebs_recording is enabled. + * And we should only run it once with all the required events. + */ + if (tpebs_pid != -1 || !tpebs_recording) + return 0; + + cpu_map__snprint(evsel_list->core.user_requested_cpus, cpumap_buf, sizeof(cpumap_buf)); + /* + * Prepare perf record for sampling event retire_latency before fork and + * prepare workload + */ + evlist__for_each_entry(evsel_list, evsel) { + int i; + char *name; + struct tpebs_retire_lat *new; + + if (!evsel->retire_lat) + continue; + + pr_debug("tpebs: Retire_latency of event %s is required\n", evsel->name); + for (i = strlen(evsel->name) - 1; i > 0; i--) { + if (evsel->name[i] == 'R') + break; + } + if (i <= 0 || evsel->name[i] != 'R') { + ret = -1; + goto err; + } + + name = strdup(evsel->name); + if (!name) { + ret = -ENOMEM; + goto err; + } + name[i] = 'p'; + + new = zalloc(sizeof(*new)); + if (!new) { + ret = -1; + zfree(name); + goto err; + } + new->name = name; + new->tpebs_name = evsel->name; + list_add_tail(&new->nd, &tpebs_results); + tpebs_event_size += 1; + } + + if (tpebs_event_size > 0) { + struct pollfd pollfd = { .events = POLLIN, }; + int control_fd[2], ack_fd[2], len; + char ack_buf[8]; + + /*Create control and ack fd for --control*/ + if (pipe(control_fd) < 0) { + pr_err("tpebs: Failed to create control fifo"); + ret = -1; + goto out; + } + if (pipe(ack_fd) < 0) { + pr_err("tpebs: Failed to create control fifo"); + ret = -1; + goto out; + } + + ret = start_perf_record(control_fd, ack_fd, cpumap_buf); + if (ret) + goto out; + tpebs_pid = tpebs_cmd->pid; + if (pthread_create(&tpebs_reader_thread, NULL, __sample_reader, tpebs_cmd)) { + kill(tpebs_cmd->pid, SIGTERM); + close(tpebs_cmd->out); + pr_err("Could not create thread to process sample data.\n"); + ret = -1; + goto out; + } + /* Wait for perf record initialization.*/ + len = strlen(EVLIST_CTL_CMD_ENABLE_TAG); + ret = write(control_fd[1], EVLIST_CTL_CMD_ENABLE_TAG, len); + if (ret != len) { + pr_err("perf record control write control message failed\n"); + goto out; + } + + /* wait for an ack */ + pollfd.fd = ack_fd[0]; + + /* + * We need this poll to ensure the ack_fd PIPE will not hang + * when perf record failed for any reason. The timeout value + * 3000ms is an empirical selection. + */ + if (!poll(&pollfd, 1, 3000)) { + pr_err("tpebs failed: perf record ack timeout\n"); + ret = -1; + goto out; + } + + if (!(pollfd.revents & POLLIN)) { + pr_err("tpebs failed: did not received an ack\n"); + ret = -1; + goto out; + } + + ret = read(ack_fd[0], ack_buf, sizeof(ack_buf)); + if (ret > 0) + ret = strcmp(ack_buf, EVLIST_CTL_CMD_ACK_TAG); + else { + pr_err("tpebs: perf record control ack failed\n"); + goto out; + } +out: + close(control_fd[0]); + close(control_fd[1]); + close(ack_fd[0]); + close(ack_fd[1]); + } +err: + if (ret) + tpebs_delete(); + return ret; +} + + +int tpebs_set_evsel(struct evsel *evsel, int cpu_map_idx, int thread) +{ + __u64 val; + bool found = false; + struct tpebs_retire_lat *t; + struct perf_counts_values *count; + + /* Non reitre_latency evsel should never enter this function. */ + if (!evsel__is_retire_lat(evsel)) + return -1; + + /* + * Need to stop the forked record to ensure get sampled data from the + * PIPE to process and get non-zero retire_lat value for hybrid. + */ + tpebs_stop(); + count = perf_counts(evsel->counts, cpu_map_idx, thread); + + list_for_each_entry(t, &tpebs_results, nd) { + if (t->tpebs_name == evsel->name || + (evsel->metric_id && !strcmp(t->tpebs_name, evsel->metric_id))) { + found = true; + break; + } + } + + /* Set ena and run to non-zero */ + count->ena = count->run = 1; + count->lost = 0; + + if (!found) { + /* + * Set default value or 0 when retire_latency for this event is + * not found from sampling data (record_tpebs not set or 0 + * sample recorded). + */ + count->val = 0; + return 0; + } + + /* + * Only set retire_latency value to the first CPU and thread. + */ + if (cpu_map_idx == 0 && thread == 0) + val = rint(t->val); + else + val = 0; + + count->val = val; + return 0; +} + +static void tpebs_retire_lat__delete(struct tpebs_retire_lat *r) +{ + zfree(&r->name); + free(r); +} + + +/* + * tpebs_delete - delete tpebs related data and stop the created thread and + * process by calling tpebs_stop(). + * + * This function is called from evlist_delete() and also from builtin-stat + * stat_handle_error(). If tpebs_start() is called from places other then perf + * stat, need to ensure tpebs_delete() is also called to safely free mem and + * close the data read thread and the forked perf record process. + * + * This function is also called in evsel__close() to be symmetric with + * tpebs_start() being called in evsel__open(). We will update this call site + * when move tpebs_start() to evlist level. + */ +void tpebs_delete(void) +{ + struct tpebs_retire_lat *r, *rtmp; + + if (tpebs_pid == -1) + return; + + tpebs_stop(); + + list_for_each_entry_safe(r, rtmp, &tpebs_results, nd) { + list_del_init(&r->nd); + tpebs_retire_lat__delete(r); + } + + if (tpebs_cmd) { + free(tpebs_cmd); + tpebs_cmd = NULL; + } +} diff --git a/tools/perf/util/intel-tpebs.h b/tools/perf/util/intel-tpebs.h new file mode 100644 index 000000000000..766b3fbd79f1 --- /dev/null +++ b/tools/perf/util/intel-tpebs.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * intel_tpebs.h: Intel TEPBS support + */ +#ifndef INCLUDE__PERF_INTEL_TPEBS_H__ +#define INCLUDE__PERF_INTEL_TPEBS_H__ + +#include "stat.h" +#include "evsel.h" + +#ifdef HAVE_ARCH_X86_64_SUPPORT + +extern bool tpebs_recording; +int tpebs_start(struct evlist *evsel_list); +void tpebs_delete(void); +int tpebs_set_evsel(struct evsel *evsel, int cpu_map_idx, int thread); + +#else + +static inline int tpebs_start(struct evlist *evsel_list __maybe_unused) +{ + return 0; +} + +static inline void tpebs_delete(void) {}; + +static inline int tpebs_set_evsel(struct evsel *evsel __maybe_unused, + int cpu_map_idx __maybe_unused, + int thread __maybe_unused) +{ + return 0; +} + +#endif +#endif From 0a7381601b8a55d44c97ea132e23876c0dd9f90e Mon Sep 17 00:00:00 2001 From: Weilin Wang Date: Sat, 20 Jul 2024 02:20:58 -0400 Subject: [PATCH 143/347] perf vendor events intel: Add MTL metric JSON files Add MTL metric JSON file for TMA4.8. Some of the metrics' formulas use TPEBS retire_latency in MTL. This also includes lated E-Core TMA3.6 changes. Reviewed-by: Namhyung Kim Signed-off-by: Weilin Wang Acked-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Caleb Biggers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Perry Taylor Cc: Peter Zijlstra Cc: Samantha Alt Link: https://lore.kernel.org/r/20240720062102.444578-6-weilin.wang@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- .../arch/x86/meteorlake/metricgroups.json | 142 + .../arch/x86/meteorlake/mtl-metrics.json | 2535 +++++++++++++++++ 2 files changed, 2677 insertions(+) create mode 100644 tools/perf/pmu-events/arch/x86/meteorlake/metricgroups.json create mode 100644 tools/perf/pmu-events/arch/x86/meteorlake/mtl-metrics.json diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/metricgroups.json b/tools/perf/pmu-events/arch/x86/meteorlake/metricgroups.json new file mode 100644 index 000000000000..b54a5fc0861f --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/meteorlake/metricgroups.json @@ -0,0 +1,142 @@ +{ + "Backend": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Bad": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BadSpec": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BigFootprint": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BrMispredicts": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Branches": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BvBC": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BvBO": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BvCB": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BvFB": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BvIO": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BvMB": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BvML": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BvMP": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BvMS": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BvMT": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BvOB": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "BvUW": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "C0Wait": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "CacheHits": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "CacheMisses": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "CodeGen": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Compute": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Cor": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "DSB": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "DSBmiss": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "DataSharing": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Fed": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "FetchBW": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "FetchLat": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Flops": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "FpScalar": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "FpVector": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Frontend": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "HPC": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "IcMiss": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Ifetch": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "InsType": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "IntVector": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "L2Evicts": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "LSD": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Load_Store_Miss": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "MachineClears": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Machine_Clears": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Mem": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "MemOffcore": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Mem_Exec": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "MemoryBW": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "MemoryBound": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "MemoryLat": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "MemoryTLB": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Memory_BW": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Memory_Lat": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "MicroSeq": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "OS": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Offcore": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "PGO": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Pipeline": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "PortsUtil": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Power": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Prefetches": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Ret": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Retire": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "SMT": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Server": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Snoop": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "SoC": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "Summary": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "TmaL1": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "TmaL2": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "TmaL3mem": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "TopdownL1": "Metrics for top-down breakdown at level 1", + "TopdownL2": "Metrics for top-down breakdown at level 2", + "TopdownL3": "Metrics for top-down breakdown at level 3", + "TopdownL4": "Metrics for top-down breakdown at level 4", + "TopdownL5": "Metrics for top-down breakdown at level 5", + "TopdownL6": "Metrics for top-down breakdown at level 6", + "load_store_bound": "Grouping from Top-down Microarchitecture Analysis Metrics spreadsheet", + "tma_L1_group": "Metrics for top-down breakdown at level 1", + "tma_L2_group": "Metrics for top-down breakdown at level 2", + "tma_L3_group": "Metrics for top-down breakdown at level 3", + "tma_L4_group": "Metrics for top-down breakdown at level 4", + "tma_L5_group": "Metrics for top-down breakdown at level 5", + "tma_L6_group": "Metrics for top-down breakdown at level 6", + "tma_alu_op_utilization_group": "Metrics contributing to tma_alu_op_utilization category", + "tma_assists_group": "Metrics contributing to tma_assists category", + "tma_backend_bound_group": "Metrics contributing to tma_backend_bound category", + "tma_bad_speculation_group": "Metrics contributing to tma_bad_speculation category", + "tma_branch_mispredicts_group": "Metrics contributing to tma_branch_mispredicts category", + "tma_branch_resteers_group": "Metrics contributing to tma_branch_resteers category", + "tma_core_bound_group": "Metrics contributing to tma_core_bound category", + "tma_dram_bound_group": "Metrics contributing to tma_dram_bound category", + "tma_dtlb_load_group": "Metrics contributing to tma_dtlb_load category", + "tma_dtlb_store_group": "Metrics contributing to tma_dtlb_store category", + "tma_fetch_bandwidth_group": "Metrics contributing to tma_fetch_bandwidth category", + "tma_fetch_latency_group": "Metrics contributing to tma_fetch_latency category", + "tma_fp_arith_group": "Metrics contributing to tma_fp_arith category", + "tma_fp_vector_group": "Metrics contributing to tma_fp_vector category", + "tma_frontend_bound_group": "Metrics contributing to tma_frontend_bound category", + "tma_heavy_operations_group": "Metrics contributing to tma_heavy_operations category", + "tma_ifetch_bandwidth_group": "Metrics contributing to tma_ifetch_bandwidth category", + "tma_ifetch_latency_group": "Metrics contributing to tma_ifetch_latency category", + "tma_int_operations_group": "Metrics contributing to tma_int_operations category", + "tma_issue2P": "Metrics related by the issue $issue2P", + "tma_issueBM": "Metrics related by the issue $issueBM", + "tma_issueBW": "Metrics related by the issue $issueBW", + "tma_issueComp": "Metrics related by the issue $issueComp", + "tma_issueD0": "Metrics related by the issue $issueD0", + "tma_issueFB": "Metrics related by the issue $issueFB", + "tma_issueFL": "Metrics related by the issue $issueFL", + "tma_issueL1": "Metrics related by the issue $issueL1", + "tma_issueLat": "Metrics related by the issue $issueLat", + "tma_issueMC": "Metrics related by the issue $issueMC", + "tma_issueMS": "Metrics related by the issue $issueMS", + "tma_issueMV": "Metrics related by the issue $issueMV", + "tma_issueRFO": "Metrics related by the issue $issueRFO", + "tma_issueSL": "Metrics related by the issue $issueSL", + "tma_issueSO": "Metrics related by the issue $issueSO", + "tma_issueSmSt": "Metrics related by the issue $issueSmSt", + "tma_issueSpSt": "Metrics related by the issue $issueSpSt", + "tma_issueSyncxn": "Metrics related by the issue $issueSyncxn", + "tma_issueTLB": "Metrics related by the issue $issueTLB", + "tma_l1_bound_group": "Metrics contributing to tma_l1_bound category", + "tma_l3_bound_group": "Metrics contributing to tma_l3_bound category", + "tma_light_operations_group": "Metrics contributing to tma_light_operations category", + "tma_load_op_utilization_group": "Metrics contributing to tma_load_op_utilization category", + "tma_machine_clears_group": "Metrics contributing to tma_machine_clears category", + "tma_mem_latency_group": "Metrics contributing to tma_mem_latency category", + "tma_memory_bound_group": "Metrics contributing to tma_memory_bound category", + "tma_microcode_sequencer_group": "Metrics contributing to tma_microcode_sequencer category", + "tma_mite_group": "Metrics contributing to tma_mite category", + "tma_other_light_ops_group": "Metrics contributing to tma_other_light_ops category", + "tma_ports_utilization_group": "Metrics contributing to tma_ports_utilization category", + "tma_ports_utilized_0_group": "Metrics contributing to tma_ports_utilized_0 category", + "tma_ports_utilized_3m_group": "Metrics contributing to tma_ports_utilized_3m category", + "tma_resource_bound_group": "Metrics contributing to tma_resource_bound category", + "tma_retiring_group": "Metrics contributing to tma_retiring category", + "tma_serializing_operation_group": "Metrics contributing to tma_serializing_operation category", + "tma_store_bound_group": "Metrics contributing to tma_store_bound category", + "tma_store_op_utilization_group": "Metrics contributing to tma_store_op_utilization category" +} diff --git a/tools/perf/pmu-events/arch/x86/meteorlake/mtl-metrics.json b/tools/perf/pmu-events/arch/x86/meteorlake/mtl-metrics.json new file mode 100644 index 000000000000..1a7392f0da86 --- /dev/null +++ b/tools/perf/pmu-events/arch/x86/meteorlake/mtl-metrics.json @@ -0,0 +1,2535 @@ +[ + { + "BriefDescription": "C10 residency percent per package", + "MetricExpr": "cstate_pkg@c10\\-residency@ / TSC", + "MetricGroup": "Power", + "MetricName": "C10_Pkg_Residency", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "C1 residency percent per core", + "MetricExpr": "cstate_core@c1\\-residency@ / TSC", + "MetricGroup": "Power", + "MetricName": "C1_Core_Residency", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "C2 residency percent per package", + "MetricExpr": "cstate_pkg@c2\\-residency@ / TSC", + "MetricGroup": "Power", + "MetricName": "C2_Pkg_Residency", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "C3 residency percent per package", + "MetricExpr": "cstate_pkg@c3\\-residency@ / TSC", + "MetricGroup": "Power", + "MetricName": "C3_Pkg_Residency", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "C6 residency percent per core", + "MetricExpr": "cstate_core@c6\\-residency@ / TSC", + "MetricGroup": "Power", + "MetricName": "C6_Core_Residency", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "C6 residency percent per package", + "MetricExpr": "cstate_pkg@c6\\-residency@ / TSC", + "MetricGroup": "Power", + "MetricName": "C6_Pkg_Residency", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "C7 residency percent per core", + "MetricExpr": "cstate_core@c7\\-residency@ / TSC", + "MetricGroup": "Power", + "MetricName": "C7_Core_Residency", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "C7 residency percent per package", + "MetricExpr": "cstate_pkg@c7\\-residency@ / TSC", + "MetricGroup": "Power", + "MetricName": "C7_Pkg_Residency", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "C8 residency percent per package", + "MetricExpr": "cstate_pkg@c8\\-residency@ / TSC", + "MetricGroup": "Power", + "MetricName": "C8_Pkg_Residency", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "C9 residency percent per package", + "MetricExpr": "cstate_pkg@c9\\-residency@ / TSC", + "MetricGroup": "Power", + "MetricName": "C9_Pkg_Residency", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "Percentage of cycles spent in System Management Interrupts.", + "MetricExpr": "((msr@aperf@ - cycles) / msr@aperf@ if msr@smi@ > 0 else 0)", + "MetricGroup": "smi", + "MetricName": "smi_cycles", + "MetricThreshold": "smi_cycles > 0.1", + "ScaleUnit": "100%" + }, + { + "BriefDescription": "Number of SMI interrupts.", + "MetricExpr": "msr@smi@", + "MetricGroup": "smi", + "MetricName": "smi_num", + "ScaleUnit": "1SMI#" + }, + { + "BriefDescription": "Counts the number of issue slots that were not consumed by the backend due to certain allocation restrictions", + "MetricExpr": "tma_core_bound", + "MetricGroup": "TopdownL3;tma_L3_group;tma_core_bound_group", + "MetricName": "tma_allocation_restriction", + "MetricThreshold": "tma_allocation_restriction > 0.1 & (tma_core_bound > 0.1 & tma_backend_bound > 0.1)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the total number of issue slots that were not consumed by the backend due to backend stalls", + "MetricExpr": "cpu_atom@TOPDOWN_BE_BOUND.ALL_P@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL1;tma_L1_group", + "MetricName": "tma_backend_bound", + "MetricThreshold": "tma_backend_bound > 0.1", + "MetricgroupNoGroup": "TopdownL1", + "PublicDescription": "Counts the total number of issue slots that were not consumed by the backend due to backend stalls. Note that uops must be available for consumption in order for this event to count. If a uop is not available (IQ is empty), this event will not count", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the total number of issue slots that were not consumed by the backend because allocation is stalled due to a mispredicted jump or a machine clear", + "MetricExpr": "cpu_atom@TOPDOWN_BAD_SPECULATION.ALL_P@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL1;tma_L1_group", + "MetricName": "tma_bad_speculation", + "MetricThreshold": "tma_bad_speculation > 0.15", + "MetricgroupNoGroup": "TopdownL1", + "PublicDescription": "Counts the total number of issue slots that were not consumed by the backend because allocation is stalled due to a mispredicted jump or a machine clear. Only issue slots wasted due to fast nukes such as memory ordering nukes are counted. Other nukes are not accounted for. Counts all issue slots blocked during this recovery window including relevant microcode flows and while uops are not yet available in the instruction queue (IQ). Also includes the issue slots that were consumed by the backend but were thrown away because they were younger than the mispredict or machine clear.", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not delivered by the frontend due to BACLEARS, which occurs when the Branch Target Buffer (BTB) prediction or lack thereof, was corrected by a later branch predictor in the frontend", + "MetricExpr": "cpu_atom@TOPDOWN_FE_BOUND.BRANCH_DETECT@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_ifetch_latency_group", + "MetricName": "tma_branch_detect", + "MetricThreshold": "tma_branch_detect > 0.05 & (tma_ifetch_latency > 0.15 & tma_frontend_bound > 0.2)", + "PublicDescription": "Counts the number of issue slots that were not delivered by the frontend due to BACLEARS, which occurs when the Branch Target Buffer (BTB) prediction or lack thereof, was corrected by a later branch predictor in the frontend. Includes BACLEARS due to all branch types including conditional and unconditional jumps, returns, and indirect branches.", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not consumed by the backend due to branch mispredicts", + "MetricExpr": "cpu_atom@TOPDOWN_BAD_SPECULATION.MISPREDICT@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL2;tma_L2_group;tma_bad_speculation_group", + "MetricName": "tma_branch_mispredicts", + "MetricThreshold": "tma_branch_mispredicts > 0.05 & tma_bad_speculation > 0.15", + "MetricgroupNoGroup": "TopdownL2", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not delivered by the frontend due to BTCLEARS, which occurs when the Branch Target Buffer (BTB) predicts a taken branch.", + "MetricExpr": "cpu_atom@TOPDOWN_FE_BOUND.BRANCH_RESTEER@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_ifetch_latency_group", + "MetricName": "tma_branch_resteer", + "MetricThreshold": "tma_branch_resteer > 0.05 & (tma_ifetch_latency > 0.15 & tma_frontend_bound > 0.2)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not delivered by the frontend due to the microcode sequencer (MS).", + "MetricExpr": "cpu_atom@TOPDOWN_FE_BOUND.CISC@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_ifetch_bandwidth_group", + "MetricName": "tma_cisc", + "MetricThreshold": "tma_cisc > 0.05 & (tma_ifetch_bandwidth > 0.1 & tma_frontend_bound > 0.2)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of cycles due to backend bound stalls that are bounded by core restrictions and not attributed to an outstanding load or stores, or resource limitation", + "MetricExpr": "cpu_atom@TOPDOWN_BE_BOUND.ALLOC_RESTRICTIONS@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL2;tma_L2_group;tma_backend_bound_group", + "MetricName": "tma_core_bound", + "MetricThreshold": "tma_core_bound > 0.1 & tma_backend_bound > 0.1", + "MetricgroupNoGroup": "TopdownL2", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not delivered by the frontend due to decode stalls.", + "MetricExpr": "cpu_atom@TOPDOWN_FE_BOUND.DECODE@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_ifetch_bandwidth_group", + "MetricName": "tma_decode", + "MetricThreshold": "tma_decode > 0.05 & (tma_ifetch_bandwidth > 0.1 & tma_frontend_bound > 0.2)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not consumed by the backend due to a machine clear that does not require the use of microcode, classified as a fast nuke, due to memory ordering, memory disambiguation and memory renaming", + "MetricExpr": "cpu_atom@TOPDOWN_BAD_SPECULATION.FASTNUKE@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_machine_clears_group", + "MetricName": "tma_fast_nuke", + "MetricThreshold": "tma_fast_nuke > 0.05 & (tma_machine_clears > 0.05 & tma_bad_speculation > 0.15)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not consumed by the backend due to frontend stalls.", + "MetricExpr": "cpu_atom@TOPDOWN_FE_BOUND.ALL_P@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL1;tma_L1_group", + "MetricName": "tma_frontend_bound", + "MetricThreshold": "tma_frontend_bound > 0.2", + "MetricgroupNoGroup": "TopdownL1", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not delivered by the frontend due to instruction cache misses.", + "MetricExpr": "cpu_atom@TOPDOWN_FE_BOUND.ICACHE@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_ifetch_latency_group", + "MetricName": "tma_icache_misses", + "MetricThreshold": "tma_icache_misses > 0.05 & (tma_ifetch_latency > 0.15 & tma_frontend_bound > 0.2)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not delivered by the frontend due to frontend bandwidth restrictions due to decode, predecode, cisc, and other limitations.", + "MetricExpr": "cpu_atom@TOPDOWN_FE_BOUND.FRONTEND_BANDWIDTH@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL2;tma_L2_group;tma_frontend_bound_group", + "MetricName": "tma_ifetch_bandwidth", + "MetricThreshold": "tma_ifetch_bandwidth > 0.1 & tma_frontend_bound > 0.2", + "MetricgroupNoGroup": "TopdownL2", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not delivered by the frontend due to frontend latency restrictions due to icache misses, itlb misses, branch detection, and resteer limitations.", + "MetricExpr": "cpu_atom@TOPDOWN_FE_BOUND.FRONTEND_LATENCY@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL2;tma_L2_group;tma_frontend_bound_group", + "MetricName": "tma_ifetch_latency", + "MetricThreshold": "tma_ifetch_latency > 0.15 & tma_frontend_bound > 0.2", + "MetricgroupNoGroup": "TopdownL2", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instructions per Floating Point (FP) Operation", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / cpu_atom@FP_FLOPS_RETIRED.ALL@", + "MetricGroup": "Flops", + "MetricName": "tma_info_arith_inst_mix_ipflop", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instructions per FP Arithmetic AVX/SSE 128-bit instruction", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / (cpu_atom@FP_INST_RETIRED.128B_DP@ + cpu_atom@FP_INST_RETIRED.128B_SP@)", + "MetricGroup": "Flops", + "MetricName": "tma_info_arith_inst_mix_ipfparith_avx128", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instructions per FP Arithmetic Scalar Double-Precision instruction", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / cpu_atom@FP_INST_RETIRED.64B_DP@", + "MetricGroup": "Flops", + "MetricName": "tma_info_arith_inst_mix_ipfparith_scalar_dp", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instructions per FP Arithmetic Scalar Single-Precision instruction", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / cpu_atom@FP_INST_RETIRED.32B_SP@", + "MetricGroup": "Flops", + "MetricName": "tma_info_arith_inst_mix_ipfparith_scalar_sp", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of time that retirement is stalled due to a first level data TLB miss", + "MetricExpr": "100 * (cpu_atom@LD_HEAD.DTLB_MISS_AT_RET@ + cpu_atom@LD_HEAD.PGWALK_AT_RET@) / cpu_atom@CPU_CLK_UNHALTED.CORE@", + "MetricName": "tma_info_bottleneck_%_dtlb_miss_bound_cycles", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of time that allocation and retirement is stalled by the Frontend Cluster due to an Ifetch Miss, either Icache or ITLB Miss", + "MetricExpr": "100 * cpu_atom@MEM_BOUND_STALLS_IFETCH.ALL@ / cpu_atom@CPU_CLK_UNHALTED.CORE@", + "MetricGroup": "Ifetch", + "MetricName": "tma_info_bottleneck_%_ifetch_miss_bound_cycles", + "PublicDescription": "Percentage of time that allocation and retirement is stalled by the Frontend Cluster due to an Ifetch Miss, either Icache or ITLB Miss. See Info.Ifetch_Bound", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of time that retirement is stalled due to an L1 miss", + "MetricExpr": "100 * cpu_atom@MEM_BOUND_STALLS_LOAD.ALL@ / cpu_atom@CPU_CLK_UNHALTED.CORE@", + "MetricGroup": "Load_Store_Miss", + "MetricName": "tma_info_bottleneck_%_load_miss_bound_cycles", + "PublicDescription": "Percentage of time that retirement is stalled due to an L1 miss. See Info.Load_Miss_Bound", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of time that retirement is stalled by the Memory Cluster due to a pipeline stall", + "MetricExpr": "100 * cpu_atom@LD_HEAD.ANY_AT_RET@ / cpu_atom@CPU_CLK_UNHALTED.CORE@", + "MetricGroup": "Mem_Exec", + "MetricName": "tma_info_bottleneck_%_mem_exec_bound_cycles", + "PublicDescription": "Percentage of time that retirement is stalled by the Memory Cluster due to a pipeline stall. See Info.Mem_Exec_Bound", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instructions per Branch (lower number means higher occurrence rate)", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / cpu_atom@BR_INST_RETIRED.ALL_BRANCHES@", + "MetricName": "tma_info_br_inst_mix_ipbranch", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instruction per (near) call (lower number means higher occurrence rate)", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / cpu_atom@BR_INST_RETIRED.NEAR_CALL@", + "MetricName": "tma_info_br_inst_mix_ipcall", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instructions per Far Branch ( Far Branches apply upon transition from application to operating system, handling interrupts, exceptions) [lower number means higher occurrence rate]", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / cpu_atom@BR_INST_RETIRED.FAR_BRANCH@u", + "MetricName": "tma_info_br_inst_mix_ipfarbranch", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instructions per retired conditional Branch Misprediction where the branch was not taken", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / (cpu_atom@BR_MISP_RETIRED.COND@ - cpu_atom@BR_MISP_RETIRED.COND_TAKEN@)", + "MetricName": "tma_info_br_inst_mix_ipmisp_cond_ntaken", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instructions per retired conditional Branch Misprediction where the branch was taken", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / cpu_atom@BR_MISP_RETIRED.COND_TAKEN@", + "MetricName": "tma_info_br_inst_mix_ipmisp_cond_taken", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instructions per retired indirect call or jump Branch Misprediction", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / cpu_atom@BR_MISP_RETIRED.INDIRECT@", + "MetricName": "tma_info_br_inst_mix_ipmisp_indirect", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instructions per retired return Branch Misprediction", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / cpu_atom@BR_MISP_RETIRED.RETURN@", + "MetricName": "tma_info_br_inst_mix_ipmisp_ret", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instructions per retired Branch Misprediction", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / cpu_atom@BR_MISP_RETIRED.ALL_BRANCHES@", + "MetricName": "tma_info_br_inst_mix_ipmispredict", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Ratio of all branches which mispredict", + "MetricExpr": "cpu_atom@BR_MISP_RETIRED.ALL_BRANCHES@ / cpu_atom@BR_INST_RETIRED.ALL_BRANCHES@", + "MetricName": "tma_info_br_mispredict_bound_branch_mispredict_ratio", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Ratio between Mispredicted branches and unknown branches", + "MetricExpr": "cpu_atom@BR_MISP_RETIRED.ALL_BRANCHES@ / cpu_atom@BACLEARS.ANY@", + "MetricName": "tma_info_br_mispredict_bound_branch_mispredict_to_unknown_branch_ratio", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of time that allocation is stalled due to load buffer full", + "MetricExpr": "100 * cpu_atom@MEM_SCHEDULER_BLOCK.LD_BUF@ / cpu_atom@CPU_CLK_UNHALTED.CORE@", + "MetricName": "tma_info_buffer_stalls_%_load_buffer_stall_cycles", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of time that allocation is stalled due to memory reservation stations full", + "MetricExpr": "100 * cpu_atom@MEM_SCHEDULER_BLOCK.RSV@ / cpu_atom@CPU_CLK_UNHALTED.CORE@", + "MetricName": "tma_info_buffer_stalls_%_mem_rsv_stall_cycles", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of time that allocation is stalled due to store buffer full", + "MetricExpr": "100 * cpu_atom@MEM_SCHEDULER_BLOCK.ST_BUF@ / cpu_atom@CPU_CLK_UNHALTED.CORE@", + "MetricName": "tma_info_buffer_stalls_%_store_buffer_stall_cycles", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Cycles Per Instruction", + "MetricExpr": "cpu_atom@CPU_CLK_UNHALTED.CORE@ / cpu_atom@INST_RETIRED.ANY@", + "MetricName": "tma_info_core_cpi", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Floating Point Operations Per Cycle", + "MetricExpr": "cpu_atom@FP_FLOPS_RETIRED.ALL@ / cpu_atom@CPU_CLK_UNHALTED.CORE@", + "MetricGroup": "Flops", + "MetricName": "tma_info_core_flopc", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instructions Per Cycle", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / cpu_atom@CPU_CLK_UNHALTED.CORE@", + "MetricName": "tma_info_core_ipc", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Uops Per Instruction", + "MetricExpr": "cpu_atom@TOPDOWN_RETIRING.ALL_P@ / cpu_atom@INST_RETIRED.ANY@", + "MetricName": "tma_info_core_upi", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of ifetch miss bound stalls, where the ifetch miss hits in the L2", + "MetricExpr": "100 * cpu_atom@MEM_BOUND_STALLS_IFETCH.L2_HIT@ / cpu_atom@MEM_BOUND_STALLS_IFETCH.ALL@", + "MetricName": "tma_info_ifetch_miss_bound_%_ifetchmissbound_with_l2hit", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of ifetch miss bound stalls, where the ifetch miss hits in the L3", + "MetricExpr": "100 * cpu_atom@MEM_BOUND_STALLS_IFETCH.LLC_HIT@ / cpu_atom@MEM_BOUND_STALLS_IFETCH.ALL@", + "MetricName": "tma_info_ifetch_miss_bound_%_ifetchmissbound_with_l3hit", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of ifetch miss bound stalls, where the ifetch miss subsequently misses in the L3", + "MetricExpr": "100 * cpu_atom@MEM_BOUND_STALLS_IFETCH.LLC_MISS@ / cpu_atom@MEM_BOUND_STALLS_IFETCH.ALL@", + "MetricName": "tma_info_ifetch_miss_bound_%_ifetchmissbound_with_l3miss", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of memory bound stalls where retirement is stalled due to an L1 miss that hit the L2", + "MetricExpr": "100 * cpu_atom@MEM_BOUND_STALLS_LOAD.L2_HIT@ / cpu_atom@MEM_BOUND_STALLS_LOAD.ALL@", + "MetricGroup": "load_store_bound", + "MetricName": "tma_info_load_miss_bound_%_loadmissbound_with_l2hit", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of memory bound stalls where retirement is stalled due to an L1 miss that hit the L3", + "MetricExpr": "100 * cpu_atom@MEM_BOUND_STALLS_LOAD.LLC_HIT@ / cpu_atom@MEM_BOUND_STALLS_LOAD.ALL@", + "MetricGroup": "load_store_bound", + "MetricName": "tma_info_load_miss_bound_%_loadmissbound_with_l3hit", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of memory bound stalls where retirement is stalled due to an L1 miss that subsequently misses the L3", + "MetricExpr": "100 * cpu_atom@MEM_BOUND_STALLS_LOAD.LLC_MISS@ / cpu_atom@MEM_BOUND_STALLS_LOAD.ALL@", + "MetricGroup": "load_store_bound", + "MetricName": "tma_info_load_miss_bound_%_loadmissbound_with_l3miss", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of cycles that the oldest load of the load buffer is stalled at retirement due to a pipeline block", + "MetricExpr": "100 * cpu_atom@LD_HEAD.L1_BOUND_AT_RET@ / cpu_atom@CPU_CLK_UNHALTED.CORE@", + "MetricGroup": "load_store_bound", + "MetricName": "tma_info_load_store_bound_l1_bound", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of cycles that the oldest load of the load buffer is stalled at retirement", + "MetricExpr": "100 * (cpu_atom@LD_HEAD.L1_BOUND_AT_RET@ + cpu_atom@MEM_BOUND_STALLS_LOAD.ALL@) / cpu_atom@CPU_CLK_UNHALTED.CORE@", + "MetricGroup": "load_store_bound", + "MetricName": "tma_info_load_store_bound_load_bound", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of cycles the core is stalled due to store buffer full", + "MetricExpr": "100 * (cpu_atom@MEM_SCHEDULER_BLOCK.ST_BUF@ / cpu_atom@MEM_SCHEDULER_BLOCK.ALL@) * tma_mem_scheduler", + "MetricGroup": "load_store_bound", + "MetricName": "tma_info_load_store_bound_store_bound", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of machine clears relative to thousands of instructions retired, due to floating point assists", + "MetricExpr": "1e3 * cpu_atom@MACHINE_CLEARS.FP_ASSIST@ / cpu_atom@INST_RETIRED.ANY@", + "MetricName": "tma_info_machine_clear_bound_machine_clears_fp_assist_pki", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of machine clears relative to thousands of instructions retired, due to page faults", + "MetricExpr": "1e3 * cpu_atom@MACHINE_CLEARS.PAGE_FAULT@ / cpu_atom@INST_RETIRED.ANY@", + "MetricName": "tma_info_machine_clear_bound_machine_clears_page_fault_pki", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of machine clears relative to thousands of instructions retired, due to self-modifying code", + "MetricExpr": "1e3 * cpu_atom@MACHINE_CLEARS.SMC@ / cpu_atom@INST_RETIRED.ANY@", + "MetricName": "tma_info_machine_clear_bound_machine_clears_smc_pki", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of total non-speculative loads with an address aliasing block", + "MetricExpr": "100 * cpu_atom@LD_BLOCKS.ADDRESS_ALIAS@ / cpu_atom@MEM_UOPS_RETIRED.ALL_LOADS@", + "MetricName": "tma_info_mem_exec_blocks_%_loads_with_adressaliasing", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of total non-speculative loads with a store forward or unknown store address block", + "MetricExpr": "100 * cpu_atom@LD_BLOCKS.DATA_UNKNOWN@ / cpu_atom@MEM_UOPS_RETIRED.ALL_LOADS@", + "MetricName": "tma_info_mem_exec_blocks_%_loads_with_storefwdblk", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of Memory Execution Bound due to a first level data cache miss", + "MetricExpr": "100 * cpu_atom@LD_HEAD.L1_MISS_AT_RET@ / cpu_atom@LD_HEAD.ANY_AT_RET@", + "MetricName": "tma_info_mem_exec_bound_%_loadhead_with_l1miss", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of Memory Execution Bound due to other block cases, such as pipeline conflicts, fences, etc", + "MetricExpr": "100 * cpu_atom@LD_HEAD.OTHER_AT_RET@ / cpu_atom@LD_HEAD.ANY_AT_RET@", + "MetricName": "tma_info_mem_exec_bound_%_loadhead_with_otherpipelineblks", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of Memory Execution Bound due to a pagewalk", + "MetricExpr": "100 * cpu_atom@LD_HEAD.PGWALK_AT_RET@ / cpu_atom@LD_HEAD.ANY_AT_RET@", + "MetricName": "tma_info_mem_exec_bound_%_loadhead_with_pagewalk", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of Memory Execution Bound due to a second level TLB miss", + "MetricExpr": "100 * cpu_atom@LD_HEAD.DTLB_MISS_AT_RET@ / cpu_atom@LD_HEAD.ANY_AT_RET@", + "MetricName": "tma_info_mem_exec_bound_%_loadhead_with_stlbhit", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of Memory Execution Bound due to a store forward address match", + "MetricExpr": "100 * cpu_atom@LD_HEAD.ST_ADDR_AT_RET@ / cpu_atom@LD_HEAD.ANY_AT_RET@", + "MetricName": "tma_info_mem_exec_bound_%_loadhead_with_storefwding", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instructions per Load", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / cpu_atom@MEM_UOPS_RETIRED.ALL_LOADS@", + "MetricName": "tma_info_mem_mix_ipload", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Instructions per Store", + "MetricExpr": "cpu_atom@INST_RETIRED.ANY@ / cpu_atom@MEM_UOPS_RETIRED.ALL_STORES@", + "MetricName": "tma_info_mem_mix_ipstore", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of total non-speculative loads that perform one or more locks", + "MetricExpr": "100 * cpu_atom@MEM_UOPS_RETIRED.LOCK_LOADS@ / cpu_atom@MEM_UOPS_RETIRED.ALL_LOADS@", + "MetricName": "tma_info_mem_mix_load_locks_ratio", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of total non-speculative loads that are splits", + "MetricExpr": "100 * cpu_atom@MEM_UOPS_RETIRED.SPLIT_LOADS@ / cpu_atom@MEM_UOPS_RETIRED.ALL_LOADS@", + "MetricName": "tma_info_mem_mix_load_splits_ratio", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Ratio of mem load uops to all uops", + "MetricExpr": "1e3 * cpu_atom@MEM_UOPS_RETIRED.ALL_LOADS@ / cpu_atom@TOPDOWN_RETIRING.ALL_P@", + "MetricName": "tma_info_mem_mix_memload_ratio", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of time that the core is stalled due to a TPAUSE or UMWAIT instruction", + "MetricExpr": "100 * cpu_atom@SERIALIZATION.C01_MS_SCB@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricName": "tma_info_serialization _%_tpause_cycles", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Average CPU Utilization", + "MetricExpr": "cpu_atom@CPU_CLK_UNHALTED.REF_TSC@ / TSC", + "MetricName": "tma_info_system_cpu_utilization", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Giga Floating Point Operations Per Second", + "MetricExpr": "cpu_atom@FP_FLOPS_RETIRED.ALL@ / (duration_time * 1e9)", + "MetricGroup": "Flops", + "MetricName": "tma_info_system_gflops", + "PublicDescription": "Giga Floating Point Operations Per Second. Aggregate across all supported options of: FP precisions, scalar and vector instructions, vector-width", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Fraction of cycles spent in Kernel mode", + "MetricExpr": "cpu_atom@CPU_CLK_UNHALTED.CORE_P@k / cpu_atom@CPU_CLK_UNHALTED.CORE@", + "MetricGroup": "Summary", + "MetricName": "tma_info_system_kernel_utilization", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Average Frequency Utilization relative nominal frequency", + "MetricExpr": "cpu_atom@CPU_CLK_UNHALTED.CORE@ / cpu_atom@CPU_CLK_UNHALTED.REF_TSC@", + "MetricGroup": "Power", + "MetricName": "tma_info_system_turbo_utilization", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of all uops which are FPDiv uops", + "MetricExpr": "100 * cpu_atom@UOPS_RETIRED.FPDIV@ / cpu_atom@TOPDOWN_RETIRING.ALL_P@", + "MetricName": "tma_info_uop_mix_fpdiv_uop_ratio", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of all uops which are IDiv uops", + "MetricExpr": "100 * cpu_atom@UOPS_RETIRED.IDIV@ / cpu_atom@TOPDOWN_RETIRING.ALL_P@", + "MetricName": "tma_info_uop_mix_idiv_uop_ratio", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of all uops which are microcode ops", + "MetricExpr": "100 * cpu_atom@UOPS_RETIRED.MS@ / cpu_atom@TOPDOWN_RETIRING.ALL_P@", + "MetricName": "tma_info_uop_mix_microcode_uop_ratio", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Percentage of all uops which are x87 uops", + "MetricExpr": "100 * cpu_atom@UOPS_RETIRED.X87@ / cpu_atom@TOPDOWN_RETIRING.ALL_P@", + "MetricName": "tma_info_uop_mix_x87_uop_ratio", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not delivered by the frontend due to Instruction Table Lookaside Buffer (ITLB) misses.", + "MetricExpr": "cpu_atom@TOPDOWN_FE_BOUND.ITLB_MISS@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_ifetch_latency_group", + "MetricName": "tma_itlb_misses", + "MetricThreshold": "tma_itlb_misses > 0.05 & (tma_ifetch_latency > 0.15 & tma_frontend_bound > 0.2)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the total number of issue slots that were not consumed by the backend because allocation is stalled due to a machine clear (nuke) of any kind including memory ordering and memory disambiguation", + "MetricExpr": "cpu_atom@TOPDOWN_BAD_SPECULATION.MACHINE_CLEARS@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL2;tma_L2_group;tma_bad_speculation_group", + "MetricName": "tma_machine_clears", + "MetricThreshold": "tma_machine_clears > 0.05 & tma_bad_speculation > 0.15", + "MetricgroupNoGroup": "TopdownL2", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not consumed by the backend due to memory reservation stalls in which a scheduler is not able to accept uops", + "MetricExpr": "cpu_atom@TOPDOWN_BE_BOUND.MEM_SCHEDULER@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_resource_bound_group", + "MetricName": "tma_mem_scheduler", + "MetricThreshold": "tma_mem_scheduler > 0.1 & (tma_resource_bound > 0.2 & tma_backend_bound > 0.1)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not consumed by the backend due to IEC or FPC RAT stalls, which can be due to FIQ or IEC reservation stalls in which the integer, floating point or SIMD scheduler is not able to accept uops", + "MetricExpr": "cpu_atom@TOPDOWN_BE_BOUND.NON_MEM_SCHEDULER@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_resource_bound_group", + "MetricName": "tma_non_mem_scheduler", + "MetricThreshold": "tma_non_mem_scheduler > 0.1 & (tma_resource_bound > 0.2 & tma_backend_bound > 0.1)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not consumed by the backend due to a machine clear that requires the use of microcode (slow nuke)", + "MetricExpr": "cpu_atom@TOPDOWN_BAD_SPECULATION.NUKE@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_machine_clears_group", + "MetricName": "tma_nuke", + "MetricThreshold": "tma_nuke > 0.05 & (tma_machine_clears > 0.05 & tma_bad_speculation > 0.15)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not delivered by the frontend due to other common frontend stalls not categorized.", + "MetricExpr": "cpu_atom@TOPDOWN_FE_BOUND.OTHER@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_ifetch_bandwidth_group", + "MetricName": "tma_other_fb", + "MetricThreshold": "tma_other_fb > 0.05 & (tma_ifetch_bandwidth > 0.1 & tma_frontend_bound > 0.2)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not delivered by the frontend due to wrong predecodes.", + "MetricExpr": "cpu_atom@TOPDOWN_FE_BOUND.PREDECODE@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_ifetch_bandwidth_group", + "MetricName": "tma_predecode", + "MetricThreshold": "tma_predecode > 0.05 & (tma_ifetch_bandwidth > 0.1 & tma_frontend_bound > 0.2)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not consumed by the backend due to the physical register file unable to accept an entry (marble stalls)", + "MetricExpr": "cpu_atom@TOPDOWN_BE_BOUND.REGISTER@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_resource_bound_group", + "MetricName": "tma_register", + "MetricThreshold": "tma_register > 0.1 & (tma_resource_bound > 0.2 & tma_backend_bound > 0.1)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not consumed by the backend due to the reorder buffer being full (ROB stalls)", + "MetricExpr": "cpu_atom@TOPDOWN_BE_BOUND.REORDER_BUFFER@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_resource_bound_group", + "MetricName": "tma_reorder_buffer", + "MetricThreshold": "tma_reorder_buffer > 0.1 & (tma_resource_bound > 0.2 & tma_backend_bound > 0.1)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of cycles the core is stalled due to a resource limitation", + "MetricExpr": "tma_backend_bound - tma_core_bound", + "MetricGroup": "TopdownL2;tma_L2_group;tma_backend_bound_group", + "MetricName": "tma_resource_bound", + "MetricThreshold": "tma_resource_bound > 0.2 & tma_backend_bound > 0.1", + "MetricgroupNoGroup": "TopdownL2", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that result in retirement slots", + "MetricExpr": "cpu_atom@TOPDOWN_RETIRING.ALL_P@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL1;tma_L1_group", + "MetricName": "tma_retiring", + "MetricThreshold": "tma_retiring > 0.75", + "MetricgroupNoGroup": "TopdownL1", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Counts the number of issue slots that were not consumed by the backend due to scoreboards from the instruction queue (IQ), jump execution unit (JEU), or microcode sequencer (MS)", + "MetricExpr": "cpu_atom@TOPDOWN_BE_BOUND.SERIALIZATION@ / (6 * cpu_atom@CPU_CLK_UNHALTED.CORE@)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_resource_bound_group", + "MetricName": "tma_serialization", + "MetricThreshold": "tma_serialization > 0.1 & (tma_resource_bound > 0.2 & tma_backend_bound > 0.1)", + "ScaleUnit": "100%", + "Unit": "cpu_atom" + }, + { + "BriefDescription": "Uncore frequency per die [GHZ]", + "MetricExpr": "tma_info_system_socket_clks / #num_dies / duration_time / 1e9", + "MetricGroup": "SoC", + "MetricName": "UNCORE_FREQ", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents Core fraction of cycles CPU dispatched uops on execution ports for ALU operations.", + "MetricExpr": "(cpu_core@UOPS_DISPATCHED.PORT_0@ + cpu_core@UOPS_DISPATCHED.PORT_1@ + cpu_core@UOPS_DISPATCHED.PORT_5_11@ + cpu_core@UOPS_DISPATCHED.PORT_6@) / (5 * tma_info_core_core_clks)", + "MetricGroup": "TopdownL5;tma_L5_group;tma_ports_utilized_3m_group", + "MetricName": "tma_alu_op_utilization", + "MetricThreshold": "tma_alu_op_utilization > 0.4", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates fraction of slots the CPU retired uops delivered by the Microcode_Sequencer as a result of Assists", + "MetricExpr": "78 * cpu_core@ASSISTS.ANY@ / tma_info_thread_slots", + "MetricGroup": "BvIO;TopdownL4;tma_L4_group;tma_microcode_sequencer_group", + "MetricName": "tma_assists", + "MetricThreshold": "tma_assists > 0.1 & (tma_microcode_sequencer > 0.05 & tma_heavy_operations > 0.1)", + "PublicDescription": "This metric estimates fraction of slots the CPU retired uops delivered by the Microcode_Sequencer as a result of Assists. Assists are long sequences of uops that are required in certain corner-cases for operations that cannot be handled natively by the execution pipeline. For example; when working with very small floating point values (so-called Denormals); the FP units are not set up to perform these operations natively. Instead; a sequence of instructions to perform the computation on the Denormals is injected into the pipeline. Since these microcode sequences might be dozens of uops long; Assists can be extremely deleterious to performance and they can be avoided in many cases. Sample with: ASSISTS.ANY", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates fraction of slots the CPU retired uops as a result of handing SSE to AVX* or AVX* to SSE transition Assists.", + "MetricExpr": "63 * cpu_core@ASSISTS.SSE_AVX_MIX@ / tma_info_thread_slots", + "MetricGroup": "HPC;TopdownL5;tma_L5_group;tma_assists_group", + "MetricName": "tma_avx_assists", + "MetricThreshold": "tma_avx_assists > 0.1", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This category represents fraction of slots where no uops are being delivered due to a lack of required resources for accepting new uops in the Backend", + "MetricExpr": "cpu_core@topdown\\-be\\-bound@ / (cpu_core@topdown\\-fe\\-bound@ + cpu_core@topdown\\-bad\\-spec@ + cpu_core@topdown\\-retiring@ + cpu_core@topdown\\-be\\-bound@) + 0 * tma_info_thread_slots", + "MetricGroup": "BvOB;TmaL1;TopdownL1;tma_L1_group", + "MetricName": "tma_backend_bound", + "MetricThreshold": "tma_backend_bound > 0.2", + "MetricgroupNoGroup": "TopdownL1", + "PublicDescription": "This category represents fraction of slots where no uops are being delivered due to a lack of required resources for accepting new uops in the Backend. Backend is the portion of the processor core where the out-of-order scheduler dispatches ready uops into their respective execution units; and once completed these uops get retired according to program order. For example; stalls due to data-cache misses or stalls due to the divider unit being overloaded are both categorized under Backend Bound. Backend Bound is further divided into two main categories: Memory Bound and Core Bound. Sample with: TOPDOWN.BACKEND_BOUND_SLOTS", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This category represents fraction of slots wasted due to incorrect speculations", + "MetricExpr": "max(1 - (tma_frontend_bound + tma_backend_bound + tma_retiring), 0)", + "MetricGroup": "TmaL1;TopdownL1;tma_L1_group", + "MetricName": "tma_bad_speculation", + "MetricThreshold": "tma_bad_speculation > 0.15", + "MetricgroupNoGroup": "TopdownL1", + "PublicDescription": "This category represents fraction of slots wasted due to incorrect speculations. This include slots used to issue uops that do not eventually get retired and slots for which the issue-pipeline was blocked due to recovery from earlier incorrect speculation. For example; wasted work due to miss-predicted branches are categorized under Bad Speculation category. Incorrect data speculation followed by Memory Ordering Nukes is another example.", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots the CPU has wasted due to Branch Misprediction", + "MetricExpr": "cpu_core@topdown\\-br\\-mispredict@ / (cpu_core@topdown\\-fe\\-bound@ + cpu_core@topdown\\-bad\\-spec@ + cpu_core@topdown\\-retiring@ + cpu_core@topdown\\-be\\-bound@) + 0 * tma_info_thread_slots", + "MetricGroup": "BadSpec;BrMispredicts;BvMP;TmaL2;TopdownL2;tma_L2_group;tma_bad_speculation_group;tma_issueBM", + "MetricName": "tma_branch_mispredicts", + "MetricThreshold": "tma_branch_mispredicts > 0.1 & tma_bad_speculation > 0.15", + "MetricgroupNoGroup": "TopdownL2", + "PublicDescription": "This metric represents fraction of slots the CPU has wasted due to Branch Misprediction. These slots are either wasted by uops fetched from an incorrectly speculated program path; or stalls when the out-of-order part of the machine needs to recover its state from a speculative path. Sample with: TOPDOWN.BR_MISPREDICT_SLOTS. Related metrics: tma_info_bad_spec_branch_misprediction_cost, tma_info_bottleneck_mispredictions, tma_mispredicts_resteers", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles the CPU was stalled due to Branch Resteers", + "MetricExpr": "cpu_core@INT_MISC.CLEAR_RESTEER_CYCLES@ / tma_info_thread_clks + tma_unknown_branches", + "MetricGroup": "FetchLat;TopdownL3;tma_L3_group;tma_fetch_latency_group", + "MetricName": "tma_branch_resteers", + "MetricThreshold": "tma_branch_resteers > 0.05 & (tma_fetch_latency > 0.1 & tma_frontend_bound > 0.15)", + "PublicDescription": "This metric represents fraction of cycles the CPU was stalled due to Branch Resteers. Branch Resteers estimates the Frontend delay in fetching operations from corrected path; following all sorts of miss-predicted branches. For example; branchy code with lots of miss-predictions might get categorized under Branch Resteers. Note the value of this node may overlap with its siblings. Sample with: BR_MISP_RETIRED.ALL_BRANCHES", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles the CPU was stalled due staying in C0.1 power-performance optimized state (Faster wakeup time; Smaller power savings).", + "MetricExpr": "cpu_core@CPU_CLK_UNHALTED.C01@ / tma_info_thread_clks", + "MetricGroup": "C0Wait;TopdownL4;tma_L4_group;tma_serializing_operation_group", + "MetricName": "tma_c01_wait", + "MetricThreshold": "tma_c01_wait > 0.05 & (tma_serializing_operation > 0.1 & (tma_core_bound > 0.1 & tma_backend_bound > 0.2))", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles the CPU was stalled due staying in C0.2 power-performance optimized state (Slower wakeup time; Larger power savings).", + "MetricExpr": "cpu_core@CPU_CLK_UNHALTED.C02@ / tma_info_thread_clks", + "MetricGroup": "C0Wait;TopdownL4;tma_L4_group;tma_serializing_operation_group", + "MetricName": "tma_c02_wait", + "MetricThreshold": "tma_c02_wait > 0.05 & (tma_serializing_operation > 0.1 & (tma_core_bound > 0.1 & tma_backend_bound > 0.2))", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates fraction of cycles the CPU retired uops originated from CISC (complex instruction set computer) instruction", + "MetricExpr": "max(0, tma_microcode_sequencer - tma_assists)", + "MetricGroup": "TopdownL4;tma_L4_group;tma_microcode_sequencer_group", + "MetricName": "tma_cisc", + "MetricThreshold": "tma_cisc > 0.1 & (tma_microcode_sequencer > 0.05 & tma_heavy_operations > 0.1)", + "PublicDescription": "This metric estimates fraction of cycles the CPU retired uops originated from CISC (complex instruction set computer) instruction. A CISC instruction has multiple uops that are required to perform the instruction's functionality as in the case of read-modify-write as an example. Since these instructions require multiple uops they may or may not imply sub-optimal use of machine resources. Sample with: FRONTEND_RETIRED.MS_FLOWS", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles the CPU was stalled due to Branch Resteers as a result of Machine Clears", + "MetricExpr": "(1 - tma_branch_mispredicts / tma_bad_speculation) * cpu_core@INT_MISC.CLEAR_RESTEER_CYCLES@ / tma_info_thread_clks", + "MetricGroup": "BadSpec;MachineClears;TopdownL4;tma_L4_group;tma_branch_resteers_group;tma_issueMC", + "MetricName": "tma_clears_resteers", + "MetricThreshold": "tma_clears_resteers > 0.05 & (tma_branch_resteers > 0.05 & (tma_fetch_latency > 0.1 & tma_frontend_bound > 0.15))", + "PublicDescription": "This metric represents fraction of cycles the CPU was stalled due to Branch Resteers as a result of Machine Clears. Sample with: INT_MISC.CLEAR_RESTEER_CYCLES. Related metrics: tma_l1_bound, tma_machine_clears, tma_microcode_sequencer, tma_ms_switches", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates fraction of cycles while the memory subsystem was handling synchronizations due to contested accesses", + "MetricExpr": "(cpu_core@MEM_LOAD_L3_HIT_RETIRED.XSNP_MISS@ * min(cpu_core@MEM_LOAD_L3_HIT_RETIRED.XSNP_MISS@R, 24 * tma_info_system_core_frequency) + cpu_core@MEM_LOAD_L3_HIT_RETIRED.XSNP_FWD@ * min(cpu_core@MEM_LOAD_L3_HIT_RETIRED.XSNP_FWD@R, 25 * tma_info_system_core_frequency) * (cpu_core@OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM@ / (cpu_core@OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM@ + cpu_core@OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HIT_WITH_FWD@))) * (1 + cpu_core@MEM_LOAD_RETIRED.FB_HIT@ / cpu_core@MEM_LOAD_RETIRED.L1_MISS@ / 2) / tma_info_thread_clks", + "MetricGroup": "BvMS;DataSharing;Offcore;Snoop;TopdownL4;tma_L4_group;tma_issueSyncxn;tma_l3_bound_group", + "MetricName": "tma_contested_accesses", + "MetricThreshold": "tma_contested_accesses > 0.05 & (tma_l3_bound > 0.05 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric estimates fraction of cycles while the memory subsystem was handling synchronizations due to contested accesses. Contested accesses occur when data written by one Logical Processor are read by another Logical Processor on a different Physical Core. Examples of contested accesses include synchronizations such as locks; true data sharing such as modified locked variables; and false sharing. Sample with: MEM_LOAD_L3_HIT_RETIRED.XSNP_FWD;MEM_LOAD_L3_HIT_RETIRED.XSNP_MISS. Related metrics: tma_data_sharing, tma_false_sharing, tma_machine_clears, tma_remote_cache", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots where Core non-memory issues were of a bottleneck", + "MetricExpr": "max(0, tma_backend_bound - tma_memory_bound)", + "MetricGroup": "Backend;Compute;TmaL2;TopdownL2;tma_L2_group;tma_backend_bound_group", + "MetricName": "tma_core_bound", + "MetricThreshold": "tma_core_bound > 0.1 & tma_backend_bound > 0.2", + "MetricgroupNoGroup": "TopdownL2", + "PublicDescription": "This metric represents fraction of slots where Core non-memory issues were of a bottleneck. Shortage in hardware compute resources; or dependencies in software's instructions are both categorized under Core Bound. Hence it may indicate the machine ran out of an out-of-order resource; certain execution units are overloaded or dependencies in program's data- or instruction-flow are limiting the performance (e.g. FP-chained long-latency arithmetic operations).", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates fraction of cycles while the memory subsystem was handling synchronizations due to data-sharing accesses", + "MetricExpr": "(cpu_core@MEM_LOAD_L3_HIT_RETIRED.XSNP_NO_FWD@ * min(cpu_core@MEM_LOAD_L3_HIT_RETIRED.XSNP_NO_FWD@R, 24 * tma_info_system_core_frequency) + cpu_core@MEM_LOAD_L3_HIT_RETIRED.XSNP_FWD@ * min(cpu_core@MEM_LOAD_L3_HIT_RETIRED.XSNP_FWD@R, 24 * tma_info_system_core_frequency) * (1 - cpu_core@OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM@ / (cpu_core@OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM@ + cpu_core@OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HIT_WITH_FWD@))) * (1 + cpu_core@MEM_LOAD_RETIRED.FB_HIT@ / cpu_core@MEM_LOAD_RETIRED.L1_MISS@ / 2) / tma_info_thread_clks", + "MetricGroup": "BvMS;Offcore;Snoop;TopdownL4;tma_L4_group;tma_issueSyncxn;tma_l3_bound_group", + "MetricName": "tma_data_sharing", + "MetricThreshold": "tma_data_sharing > 0.05 & (tma_l3_bound > 0.05 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric estimates fraction of cycles while the memory subsystem was handling synchronizations due to data-sharing accesses. Data shared by multiple Logical Processors (even just read shared) may cause increased access latency due to cache coherency. Excessive data sharing can drastically harm multithreaded performance. Sample with: MEM_LOAD_L3_HIT_RETIRED.XSNP_NO_FWD. Related metrics: tma_contested_accesses, tma_false_sharing, tma_machine_clears, tma_remote_cache", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles where decoder-0 was the only active decoder", + "MetricExpr": "(cpu_core@INST_DECODED.DECODERS\\,cmask\\=1@ - cpu_core@INST_DECODED.DECODERS\\,cmask\\=2@) / tma_info_core_core_clks / 2", + "MetricGroup": "DSBmiss;FetchBW;TopdownL4;tma_L4_group;tma_issueD0;tma_mite_group", + "MetricName": "tma_decoder0_alone", + "MetricThreshold": "tma_decoder0_alone > 0.1 & (tma_mite > 0.1 & tma_fetch_bandwidth > 0.2)", + "PublicDescription": "This metric represents fraction of cycles where decoder-0 was the only active decoder. Related metrics: tma_few_uops_instructions", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles where the Divider unit was active", + "MetricExpr": "cpu_core@ARITH.DIV_ACTIVE@ / tma_info_thread_clks", + "MetricGroup": "BvCB;TopdownL3;tma_L3_group;tma_core_bound_group", + "MetricName": "tma_divider", + "MetricThreshold": "tma_divider > 0.2 & (tma_core_bound > 0.1 & tma_backend_bound > 0.2)", + "PublicDescription": "This metric represents fraction of cycles where the Divider unit was active. Divide and square root instructions are performed by the Divider unit and can take considerably longer latency than integer or Floating Point addition; subtraction; or multiplication. Sample with: ARITH.DIVIDER_UOPS", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates how often the CPU was stalled on accesses to external memory (DRAM) by loads", + "MetricExpr": "cpu_core@MEMORY_ACTIVITY.STALLS_L3_MISS@ / tma_info_thread_clks", + "MetricGroup": "MemoryBound;TmaL3mem;TopdownL3;tma_L3_group;tma_memory_bound_group", + "MetricName": "tma_dram_bound", + "MetricThreshold": "tma_dram_bound > 0.1 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2)", + "PublicDescription": "This metric estimates how often the CPU was stalled on accesses to external memory (DRAM) by loads. Better caching can improve the latency and increase performance. Sample with: MEM_LOAD_RETIRED.L3_MISS_PS", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents Core fraction of cycles in which CPU was likely limited due to DSB (decoded uop cache) fetch pipeline", + "MetricExpr": "(cpu_core@IDQ.DSB_CYCLES_ANY@ - cpu_core@IDQ.DSB_CYCLES_OK@) / tma_info_core_core_clks / 2", + "MetricGroup": "DSB;FetchBW;TopdownL3;tma_L3_group;tma_fetch_bandwidth_group", + "MetricName": "tma_dsb", + "MetricThreshold": "tma_dsb > 0.15 & tma_fetch_bandwidth > 0.2", + "PublicDescription": "This metric represents Core fraction of cycles in which CPU was likely limited due to DSB (decoded uop cache) fetch pipeline. For example; inefficient utilization of the DSB cache structure or bank conflict when reading from it; are categorized here.", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles the CPU was stalled due to switches from DSB to MITE pipelines", + "MetricExpr": "cpu_core@DSB2MITE_SWITCHES.PENALTY_CYCLES@ / tma_info_thread_clks", + "MetricGroup": "DSBmiss;FetchLat;TopdownL3;tma_L3_group;tma_fetch_latency_group;tma_issueFB", + "MetricName": "tma_dsb_switches", + "MetricThreshold": "tma_dsb_switches > 0.05 & (tma_fetch_latency > 0.1 & tma_frontend_bound > 0.15)", + "PublicDescription": "This metric represents fraction of cycles the CPU was stalled due to switches from DSB to MITE pipelines. The DSB (decoded i-cache) is a Uop Cache where the front-end directly delivers Uops (micro operations) avoiding heavy x86 decoding. The DSB pipeline has shorter latency and delivered higher bandwidth than the MITE (legacy instruction decode pipeline). Switching between the two pipelines can cause penalties hence this metric measures the exposed penalty. Sample with: FRONTEND_RETIRED.DSB_MISS_PS. Related metrics: tma_fetch_bandwidth, tma_info_botlnk_l2_dsb_bandwidth, tma_info_botlnk_l2_dsb_misses, tma_info_frontend_dsb_coverage, tma_info_inst_mix_iptb, tma_lcp", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric roughly estimates the fraction of cycles where the Data TLB (DTLB) was missed by load accesses", + "MetricExpr": "cpu_core@MEM_INST_RETIRED.STLB_HIT_LOADS@ * min(cpu_core@MEM_INST_RETIRED.STLB_HIT_LOADS@R, 7) / tma_info_thread_clks + tma_load_stlb_miss", + "MetricGroup": "BvMT;MemoryTLB;TopdownL4;tma_L4_group;tma_issueTLB;tma_l1_bound_group", + "MetricName": "tma_dtlb_load", + "MetricThreshold": "tma_dtlb_load > 0.1 & (tma_l1_bound > 0.1 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric roughly estimates the fraction of cycles where the Data TLB (DTLB) was missed by load accesses. TLBs (Translation Look-aside Buffers) are processor caches for recently used entries out of the Page Tables that are used to map virtual- to physical-addresses by the operating system. This metric approximates the potential delay of demand loads missing the first-level data TLB (assuming worst case scenario with back to back misses to different pages). This includes hitting in the second-level TLB (STLB) as well as performing a hardware page walk on an STLB miss. Sample with: MEM_INST_RETIRED.STLB_MISS_LOADS_PS. Related metrics: tma_dtlb_store, tma_info_bottleneck_memory_data_tlbs, tma_info_bottleneck_memory_synchronization", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric roughly estimates the fraction of cycles spent handling first-level data TLB store misses", + "MetricExpr": "cpu_core@MEM_INST_RETIRED.STLB_HIT_STORES@ * min(cpu_core@MEM_INST_RETIRED.STLB_HIT_STORES@R, 7) / tma_info_thread_clks + tma_store_stlb_miss", + "MetricGroup": "BvMT;MemoryTLB;TopdownL4;tma_L4_group;tma_issueTLB;tma_store_bound_group", + "MetricName": "tma_dtlb_store", + "MetricThreshold": "tma_dtlb_store > 0.05 & (tma_store_bound > 0.2 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric roughly estimates the fraction of cycles spent handling first-level data TLB store misses. As with ordinary data caching; focus on improving data locality and reducing working-set size to reduce DTLB overhead. Additionally; consider using profile-guided optimization (PGO) to collocate frequently-used data on the same page. Try using larger page sizes for large amounts of frequently-used data. Sample with: MEM_INST_RETIRED.STLB_MISS_STORES_PS. Related metrics: tma_dtlb_load, tma_info_bottleneck_memory_data_tlbs, tma_info_bottleneck_memory_synchronization", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric roughly estimates how often CPU was handling synchronizations due to False Sharing", + "MetricExpr": "28 * tma_info_system_core_frequency * cpu_core@OCR.DEMAND_RFO.L3_HIT.SNOOP_HITM@ / tma_info_thread_clks", + "MetricGroup": "BvMS;DataSharing;Offcore;Snoop;TopdownL4;tma_L4_group;tma_issueSyncxn;tma_store_bound_group", + "MetricName": "tma_false_sharing", + "MetricThreshold": "tma_false_sharing > 0.05 & (tma_store_bound > 0.2 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric roughly estimates how often CPU was handling synchronizations due to False Sharing. False Sharing is a multithreading hiccup; where multiple Logical Processors contend on different data-elements mapped into the same cache line. Sample with: OCR.DEMAND_RFO.L3_HIT.SNOOP_HITM. Related metrics: tma_contested_accesses, tma_data_sharing, tma_machine_clears, tma_remote_cache", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric does a *rough estimation* of how often L1D Fill Buffer unavailability limited additional L1D miss memory access requests to proceed", + "MetricExpr": "cpu_core@L1D_PEND_MISS.FB_FULL@ / tma_info_thread_clks", + "MetricGroup": "BvMS;MemoryBW;TopdownL4;tma_L4_group;tma_issueBW;tma_issueSL;tma_issueSmSt;tma_l1_bound_group", + "MetricName": "tma_fb_full", + "MetricThreshold": "tma_fb_full > 0.3", + "PublicDescription": "This metric does a *rough estimation* of how often L1D Fill Buffer unavailability limited additional L1D miss memory access requests to proceed. The higher the metric value; the deeper the memory hierarchy level the misses are satisfied from (metric values >1 are valid). Often it hints on approaching bandwidth limits (to L2 cache; L3 cache or external memory). Related metrics: tma_info_bottleneck_cache_memory_bandwidth, tma_info_system_dram_bw_use, tma_mem_bandwidth, tma_sq_full, tma_store_latency, tma_streaming_stores", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots the CPU was stalled due to Frontend bandwidth issues", + "MetricExpr": "max(0, tma_frontend_bound - tma_fetch_latency)", + "MetricGroup": "FetchBW;Frontend;TmaL2;TopdownL2;tma_L2_group;tma_frontend_bound_group;tma_issueFB", + "MetricName": "tma_fetch_bandwidth", + "MetricThreshold": "tma_fetch_bandwidth > 0.2", + "MetricgroupNoGroup": "TopdownL2", + "PublicDescription": "This metric represents fraction of slots the CPU was stalled due to Frontend bandwidth issues. For example; inefficiencies at the instruction decoders; or restrictions for caching in the DSB (decoded uops cache) are categorized under Fetch Bandwidth. In such cases; the Frontend typically delivers suboptimal amount of uops to the Backend. Sample with: FRONTEND_RETIRED.LATENCY_GE_2_BUBBLES_GE_1_PS;FRONTEND_RETIRED.LATENCY_GE_1_PS;FRONTEND_RETIRED.LATENCY_GE_2_PS. Related metrics: tma_dsb_switches, tma_info_botlnk_l2_dsb_bandwidth, tma_info_botlnk_l2_dsb_misses, tma_info_frontend_dsb_coverage, tma_info_inst_mix_iptb, tma_lcp", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots the CPU was stalled due to Frontend latency issues", + "MetricExpr": "cpu_core@topdown\\-fetch\\-lat@ / (cpu_core@topdown\\-fe\\-bound@ + cpu_core@topdown\\-bad\\-spec@ + cpu_core@topdown\\-retiring@ + cpu_core@topdown\\-be\\-bound@) - cpu_core@INT_MISC.UOP_DROPPING@ / tma_info_thread_slots", + "MetricGroup": "Frontend;TmaL2;TopdownL2;tma_L2_group;tma_frontend_bound_group", + "MetricName": "tma_fetch_latency", + "MetricThreshold": "tma_fetch_latency > 0.1 & tma_frontend_bound > 0.15", + "MetricgroupNoGroup": "TopdownL2", + "PublicDescription": "This metric represents fraction of slots the CPU was stalled due to Frontend latency issues. For example; instruction-cache misses; iTLB misses or fetch stalls after a branch misprediction are categorized under Frontend Latency. In such cases; the Frontend eventually delivers no uops for some period. Sample with: FRONTEND_RETIRED.LATENCY_GE_16_PS;FRONTEND_RETIRED.LATENCY_GE_8_PS", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots where the CPU was retiring instructions that that are decoder into two or up to ([SNB+] four; [ADL+] five) uops", + "MetricExpr": "max(0, tma_heavy_operations - tma_microcode_sequencer)", + "MetricGroup": "TopdownL3;tma_L3_group;tma_heavy_operations_group;tma_issueD0", + "MetricName": "tma_few_uops_instructions", + "MetricThreshold": "tma_few_uops_instructions > 0.05 & tma_heavy_operations > 0.1", + "PublicDescription": "This metric represents fraction of slots where the CPU was retiring instructions that that are decoder into two or up to ([SNB+] four; [ADL+] five) uops. This highly-correlates with the number of uops in such instructions. Related metrics: tma_decoder0_alone", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents overall arithmetic floating-point (FP) operations fraction the CPU has executed (retired)", + "MetricExpr": "tma_x87_use + tma_fp_scalar + tma_fp_vector", + "MetricGroup": "HPC;TopdownL3;tma_L3_group;tma_light_operations_group", + "MetricName": "tma_fp_arith", + "MetricThreshold": "tma_fp_arith > 0.2 & tma_light_operations > 0.6", + "PublicDescription": "This metric represents overall arithmetic floating-point (FP) operations fraction the CPU has executed (retired). Note this metric's value may exceed its parent due to use of \"Uops\" CountDomain and FMA double-counting.", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric roughly estimates fraction of slots the CPU retired uops as a result of handing Floating Point (FP) Assists", + "MetricExpr": "30 * cpu_core@ASSISTS.FP@ / tma_info_thread_slots", + "MetricGroup": "HPC;TopdownL5;tma_L5_group;tma_assists_group", + "MetricName": "tma_fp_assists", + "MetricThreshold": "tma_fp_assists > 0.1", + "PublicDescription": "This metric roughly estimates fraction of slots the CPU retired uops as a result of handing Floating Point (FP) Assists. FP Assist may apply when working with very small floating point values (so-called Denormals).", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric approximates arithmetic floating-point (FP) scalar uops fraction the CPU has retired", + "MetricExpr": "cpu_core@FP_ARITH_INST_RETIRED.SCALAR@ / (tma_retiring * tma_info_thread_slots)", + "MetricGroup": "Compute;Flops;TopdownL4;tma_L4_group;tma_fp_arith_group;tma_issue2P", + "MetricName": "tma_fp_scalar", + "MetricThreshold": "tma_fp_scalar > 0.1 & (tma_fp_arith > 0.2 & tma_light_operations > 0.6)", + "PublicDescription": "This metric approximates arithmetic floating-point (FP) scalar uops fraction the CPU has retired. May overcount due to FMA double counting. Related metrics: tma_fp_vector, tma_fp_vector_128b, tma_fp_vector_256b, tma_fp_vector_512b, tma_int_vector_128b, tma_int_vector_256b, tma_port_0, tma_port_1, tma_port_5, tma_port_6, tma_ports_utilized_2", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric approximates arithmetic floating-point (FP) vector uops fraction the CPU has retired aggregated across all vector widths", + "MetricExpr": "cpu_core@FP_ARITH_INST_RETIRED.VECTOR@ / (tma_retiring * tma_info_thread_slots)", + "MetricGroup": "Compute;Flops;TopdownL4;tma_L4_group;tma_fp_arith_group;tma_issue2P", + "MetricName": "tma_fp_vector", + "MetricThreshold": "tma_fp_vector > 0.1 & (tma_fp_arith > 0.2 & tma_light_operations > 0.6)", + "PublicDescription": "This metric approximates arithmetic floating-point (FP) vector uops fraction the CPU has retired aggregated across all vector widths. May overcount due to FMA double counting. Related metrics: tma_fp_scalar, tma_fp_vector_128b, tma_fp_vector_256b, tma_fp_vector_512b, tma_int_vector_128b, tma_int_vector_256b, tma_port_0, tma_port_1, tma_port_5, tma_port_6, tma_ports_utilized_2", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric approximates arithmetic FP vector uops fraction the CPU has retired for 128-bit wide vectors", + "MetricExpr": "(cpu_core@FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE@ + cpu_core@FP_ARITH_INST_RETIRED.128B_PACKED_SINGLE@) / (tma_retiring * tma_info_thread_slots)", + "MetricGroup": "Compute;Flops;TopdownL5;tma_L5_group;tma_fp_vector_group;tma_issue2P", + "MetricName": "tma_fp_vector_128b", + "MetricThreshold": "tma_fp_vector_128b > 0.1 & (tma_fp_vector > 0.1 & (tma_fp_arith > 0.2 & tma_light_operations > 0.6))", + "PublicDescription": "This metric approximates arithmetic FP vector uops fraction the CPU has retired for 128-bit wide vectors. May overcount due to FMA double counting. Related metrics: tma_fp_scalar, tma_fp_vector, tma_fp_vector_256b, tma_fp_vector_512b, tma_int_vector_128b, tma_int_vector_256b, tma_port_0, tma_port_1, tma_port_5, tma_port_6, tma_ports_utilized_2", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric approximates arithmetic FP vector uops fraction the CPU has retired for 256-bit wide vectors", + "MetricExpr": "(cpu_core@FP_ARITH_INST_RETIRED.256B_PACKED_DOUBLE@ + cpu_core@FP_ARITH_INST_RETIRED.256B_PACKED_SINGLE@) / (tma_retiring * tma_info_thread_slots)", + "MetricGroup": "Compute;Flops;TopdownL5;tma_L5_group;tma_fp_vector_group;tma_issue2P", + "MetricName": "tma_fp_vector_256b", + "MetricThreshold": "tma_fp_vector_256b > 0.1 & (tma_fp_vector > 0.1 & (tma_fp_arith > 0.2 & tma_light_operations > 0.6))", + "PublicDescription": "This metric approximates arithmetic FP vector uops fraction the CPU has retired for 256-bit wide vectors. May overcount due to FMA double counting. Related metrics: tma_fp_scalar, tma_fp_vector, tma_fp_vector_128b, tma_fp_vector_512b, tma_int_vector_128b, tma_int_vector_256b, tma_port_0, tma_port_1, tma_port_5, tma_port_6, tma_ports_utilized_2", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This category represents fraction of slots where the processor's Frontend undersupplies its Backend", + "MetricExpr": "cpu_core@topdown\\-fe\\-bound@ / (cpu_core@topdown\\-fe\\-bound@ + cpu_core@topdown\\-bad\\-spec@ + cpu_core@topdown\\-retiring@ + cpu_core@topdown\\-be\\-bound@) - cpu_core@INT_MISC.UOP_DROPPING@ / tma_info_thread_slots", + "MetricGroup": "BvFB;BvIO;PGO;TmaL1;TopdownL1;tma_L1_group", + "MetricName": "tma_frontend_bound", + "MetricThreshold": "tma_frontend_bound > 0.15", + "MetricgroupNoGroup": "TopdownL1", + "PublicDescription": "This category represents fraction of slots where the processor's Frontend undersupplies its Backend. Frontend denotes the first part of the processor core responsible to fetch operations that are executed later on by the Backend part. Within the Frontend; a branch predictor predicts the next address to fetch; cache-lines are fetched from the memory subsystem; parsed into instructions; and lastly decoded into micro-operations (uops). Ideally the Frontend can issue Pipeline_Width uops every cycle to the Backend. Frontend Bound denotes unutilized issue-slots when there is no Backend stall; i.e. bubbles where Frontend delivered no uops while Backend could have accepted them. For example; stalls due to instruction-cache misses would be categorized under Frontend Bound. Sample with: FRONTEND_RETIRED.LATENCY_GE_4_PS", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots where the CPU was retiring fused instructions -- where one uop can represent multiple contiguous instructions", + "MetricExpr": "tma_light_operations * cpu_core@INST_RETIRED.MACRO_FUSED@ / (tma_retiring * tma_info_thread_slots)", + "MetricGroup": "Branches;BvBO;Pipeline;TopdownL3;tma_L3_group;tma_light_operations_group", + "MetricName": "tma_fused_instructions", + "MetricThreshold": "tma_fused_instructions > 0.1 & tma_light_operations > 0.6", + "PublicDescription": "This metric represents fraction of slots where the CPU was retiring fused instructions -- where one uop can represent multiple contiguous instructions. CMP+JCC or DEC+JCC are common examples of legacy fusions. {([MTL] Note new MOV+OP and Load+OP fusions appear under Other_Light_Ops in MTL!)}", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots where the CPU was retiring heavy-weight operations -- instructions that require two or more uops or micro-coded sequences", + "MetricExpr": "cpu_core@topdown\\-heavy\\-ops@ / (cpu_core@topdown\\-fe\\-bound@ + cpu_core@topdown\\-bad\\-spec@ + cpu_core@topdown\\-retiring@ + cpu_core@topdown\\-be\\-bound@) + 0 * tma_info_thread_slots", + "MetricGroup": "Retire;TmaL2;TopdownL2;tma_L2_group;tma_retiring_group", + "MetricName": "tma_heavy_operations", + "MetricThreshold": "tma_heavy_operations > 0.1", + "MetricgroupNoGroup": "TopdownL2", + "PublicDescription": "This metric represents fraction of slots where the CPU was retiring heavy-weight operations -- instructions that require two or more uops or micro-coded sequences. This highly-correlates with the uop length of these instructions/sequences. ([ICL+] Note this may overcount due to approximation using indirect events; [ADL+] .). Sample with: UOPS_RETIRED.HEAVY", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles the CPU was stalled due to instruction cache misses", + "MetricExpr": "cpu_core@ICACHE_DATA.STALLS@ / tma_info_thread_clks", + "MetricGroup": "BigFootprint;BvBC;FetchLat;IcMiss;TopdownL3;tma_L3_group;tma_fetch_latency_group", + "MetricName": "tma_icache_misses", + "MetricThreshold": "tma_icache_misses > 0.05 & (tma_fetch_latency > 0.1 & tma_frontend_bound > 0.15)", + "PublicDescription": "This metric represents fraction of cycles the CPU was stalled due to instruction cache misses. Sample with: FRONTEND_RETIRED.L2_MISS_PS;FRONTEND_RETIRED.L1I_MISS_PS", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Branch Misprediction Cost: Fraction of TMA slots wasted per non-speculative branch misprediction (retired JEClear)", + "MetricExpr": "tma_info_bottleneck_mispredictions * tma_info_thread_slots / cpu_core@BR_MISP_RETIRED.ALL_BRANCHES@ / 100", + "MetricGroup": "Bad;BrMispredicts;tma_issueBM", + "MetricName": "tma_info_bad_spec_branch_misprediction_cost", + "PublicDescription": "Branch Misprediction Cost: Fraction of TMA slots wasted per non-speculative branch misprediction (retired JEClear). Related metrics: tma_branch_mispredicts, tma_info_bottleneck_mispredictions, tma_mispredicts_resteers", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per retired mispredicts for conditional non-taken branches (lower number means higher occurrence rate).", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@BR_MISP_RETIRED.COND_NTAKEN@", + "MetricGroup": "Bad;BrMispredicts", + "MetricName": "tma_info_bad_spec_ipmisp_cond_ntaken", + "MetricThreshold": "tma_info_bad_spec_ipmisp_cond_ntaken < 200", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per retired mispredicts for conditional taken branches (lower number means higher occurrence rate).", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@BR_MISP_RETIRED.COND_TAKEN@", + "MetricGroup": "Bad;BrMispredicts", + "MetricName": "tma_info_bad_spec_ipmisp_cond_taken", + "MetricThreshold": "tma_info_bad_spec_ipmisp_cond_taken < 200", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per retired mispredicts for indirect CALL or JMP branches (lower number means higher occurrence rate).", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@BR_MISP_RETIRED.INDIRECT@", + "MetricGroup": "Bad;BrMispredicts", + "MetricName": "tma_info_bad_spec_ipmisp_indirect", + "MetricThreshold": "tma_info_bad_spec_ipmisp_indirect < 1e3", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per retired mispredicts for return branches (lower number means higher occurrence rate).", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@BR_MISP_RETIRED.RET@", + "MetricGroup": "Bad;BrMispredicts", + "MetricName": "tma_info_bad_spec_ipmisp_ret", + "MetricThreshold": "tma_info_bad_spec_ipmisp_ret < 500", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Number of Instructions per non-speculative Branch Misprediction (JEClear) (lower number means higher occurrence rate)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@BR_MISP_RETIRED.ALL_BRANCHES@", + "MetricGroup": "Bad;BadSpec;BrMispredicts", + "MetricName": "tma_info_bad_spec_ipmispredict", + "MetricThreshold": "tma_info_bad_spec_ipmispredict < 200", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Speculative to Retired ratio of all clears (covering mispredicts and nukes)", + "MetricExpr": "cpu_core@INT_MISC.CLEARS_COUNT@ / (cpu_core@BR_MISP_RETIRED.ALL_BRANCHES@ + cpu_core@MACHINE_CLEARS.COUNT@)", + "MetricGroup": "BrMispredicts", + "MetricName": "tma_info_bad_spec_spec_clears_ratio", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Probability of Core Bound bottleneck hidden by SMT-profiling artifacts", + "MetricExpr": "(100 * (1 - tma_core_bound / tma_ports_utilization if tma_core_bound < tma_ports_utilization else 1) if tma_info_system_smt_2t_utilization > 0.5 else 0)", + "MetricGroup": "Cor;SMT", + "MetricName": "tma_info_botlnk_l0_core_bound_likely", + "MetricThreshold": "tma_info_botlnk_l0_core_bound_likely > 0.5", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost of DSB (uop cache) hits - subset of the Instruction_Fetch_BW Bottleneck", + "MetricExpr": "100 * (tma_frontend_bound * (tma_fetch_bandwidth / (tma_fetch_bandwidth + tma_fetch_latency)) * (tma_dsb / (tma_dsb + tma_lsd + tma_mite)))", + "MetricGroup": "DSB;FetchBW;tma_issueFB", + "MetricName": "tma_info_botlnk_l2_dsb_bandwidth", + "MetricThreshold": "tma_info_botlnk_l2_dsb_bandwidth > 10", + "PublicDescription": "Total pipeline cost of DSB (uop cache) hits - subset of the Instruction_Fetch_BW Bottleneck. Related metrics: tma_dsb_switches, tma_fetch_bandwidth, tma_info_botlnk_l2_dsb_misses, tma_info_frontend_dsb_coverage, tma_info_inst_mix_iptb, tma_lcp", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost of DSB (uop cache) misses - subset of the Instruction_Fetch_BW Bottleneck", + "MetricExpr": "100 * (tma_fetch_latency * tma_dsb_switches / (tma_branch_resteers + tma_dsb_switches + tma_icache_misses + tma_itlb_misses + tma_lcp + tma_ms_switches) + tma_fetch_bandwidth * tma_mite / (tma_dsb + tma_lsd + tma_mite))", + "MetricGroup": "DSBmiss;Fed;tma_issueFB", + "MetricName": "tma_info_botlnk_l2_dsb_misses", + "MetricThreshold": "tma_info_botlnk_l2_dsb_misses > 10", + "PublicDescription": "Total pipeline cost of DSB (uop cache) misses - subset of the Instruction_Fetch_BW Bottleneck. Related metrics: tma_dsb_switches, tma_fetch_bandwidth, tma_info_botlnk_l2_dsb_bandwidth, tma_info_frontend_dsb_coverage, tma_info_inst_mix_iptb, tma_lcp", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost of Instruction Cache misses - subset of the Big_Code Bottleneck", + "MetricExpr": "100 * (tma_fetch_latency * tma_icache_misses / (tma_branch_resteers + tma_dsb_switches + tma_icache_misses + tma_itlb_misses + tma_lcp + tma_ms_switches))", + "MetricGroup": "Fed;FetchLat;IcMiss;tma_issueFL", + "MetricName": "tma_info_botlnk_l2_ic_misses", + "MetricThreshold": "tma_info_botlnk_l2_ic_misses > 5", + "PublicDescription": "Total pipeline cost of Instruction Cache misses - subset of the Big_Code Bottleneck. Related metrics: ", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost of instruction fetch related bottlenecks by large code footprint programs (i-side cache; TLB and BTB misses)", + "MetricExpr": "100 * tma_fetch_latency * (tma_itlb_misses + tma_icache_misses + tma_unknown_branches) / (tma_branch_resteers + tma_dsb_switches + tma_icache_misses + tma_itlb_misses + tma_lcp + tma_ms_switches)", + "MetricGroup": "BigFootprint;BvBC;Fed;Frontend;IcMiss;MemoryTLB", + "MetricName": "tma_info_bottleneck_big_code", + "MetricThreshold": "tma_info_bottleneck_big_code > 20", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost of instructions used for program control-flow - a subset of the Retiring category in TMA", + "MetricExpr": "100 * ((cpu_core@BR_INST_RETIRED.ALL_BRANCHES@ + 2 * cpu_core@BR_INST_RETIRED.NEAR_CALL@ + cpu_core@INST_RETIRED.NOP@) / tma_info_thread_slots)", + "MetricGroup": "BvBO;Ret", + "MetricName": "tma_info_bottleneck_branching_overhead", + "MetricThreshold": "tma_info_bottleneck_branching_overhead > 5", + "PublicDescription": "Total pipeline cost of instructions used for program control-flow - a subset of the Retiring category in TMA. Examples include function calls; loops and alignments. (A lower bound)", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost of external Memory- or Cache-Bandwidth related bottlenecks", + "MetricExpr": "100 * (tma_memory_bound * (tma_dram_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound)) * (tma_mem_bandwidth / (tma_mem_bandwidth + tma_mem_latency)) + tma_memory_bound * (tma_l3_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound)) * (tma_sq_full / (tma_contested_accesses + tma_data_sharing + tma_l3_hit_latency + tma_sq_full)) + tma_memory_bound * (tma_l1_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound)) * (tma_fb_full / (tma_dtlb_load + tma_fb_full + tma_l1_hit_latency + tma_lock_latency + tma_split_loads + tma_store_fwd_blk)))", + "MetricGroup": "BvMB;Mem;MemoryBW;Offcore;tma_issueBW", + "MetricName": "tma_info_bottleneck_cache_memory_bandwidth", + "MetricThreshold": "tma_info_bottleneck_cache_memory_bandwidth > 20", + "PublicDescription": "Total pipeline cost of external Memory- or Cache-Bandwidth related bottlenecks. Related metrics: tma_fb_full, tma_info_system_dram_bw_use, tma_mem_bandwidth, tma_sq_full", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost of external Memory- or Cache-Latency related bottlenecks", + "MetricExpr": "100 * (tma_memory_bound * (tma_dram_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound)) * (tma_mem_latency / (tma_mem_bandwidth + tma_mem_latency)) + tma_memory_bound * (tma_l3_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound)) * (tma_l3_hit_latency / (tma_contested_accesses + tma_data_sharing + tma_l3_hit_latency + tma_sq_full)) + tma_memory_bound * tma_l2_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) + tma_memory_bound * (tma_store_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound)) * (tma_store_latency / (tma_dtlb_store + tma_false_sharing + tma_split_stores + tma_store_latency + tma_streaming_stores)) + tma_memory_bound * (tma_l1_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound)) * (tma_l1_hit_latency / (tma_dtlb_load + tma_fb_full + tma_l1_hit_latency + tma_lock_latency + tma_split_loads + tma_store_fwd_blk)))", + "MetricGroup": "BvML;Mem;MemoryLat;Offcore;tma_issueLat", + "MetricName": "tma_info_bottleneck_cache_memory_latency", + "MetricThreshold": "tma_info_bottleneck_cache_memory_latency > 20", + "PublicDescription": "Total pipeline cost of external Memory- or Cache-Latency related bottlenecks. Related metrics: tma_l3_hit_latency, tma_mem_latency", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost when the execution is compute-bound - an estimation", + "MetricExpr": "100 * (tma_core_bound * tma_divider / (tma_divider + tma_ports_utilization + tma_serializing_operation) + tma_core_bound * (tma_ports_utilization / (tma_divider + tma_ports_utilization + tma_serializing_operation)) * (tma_ports_utilized_3m / (tma_ports_utilized_0 + tma_ports_utilized_1 + tma_ports_utilized_2 + tma_ports_utilized_3m)))", + "MetricGroup": "BvCB;Cor;tma_issueComp", + "MetricName": "tma_info_bottleneck_compute_bound_est", + "MetricThreshold": "tma_info_bottleneck_compute_bound_est > 20", + "PublicDescription": "Total pipeline cost when the execution is compute-bound - an estimation. Covers Core Bound when High ILP as well as when long-latency execution units are busy. Related metrics: ", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost of instruction fetch bandwidth related bottlenecks (when the front-end could not sustain operations delivery to the back-end)", + "MetricExpr": "100 * (tma_frontend_bound - (1 - 10 * tma_microcode_sequencer * tma_other_mispredicts / tma_branch_mispredicts) * tma_fetch_latency * tma_mispredicts_resteers / (tma_branch_resteers + tma_dsb_switches + tma_icache_misses + tma_itlb_misses + tma_lcp + tma_ms_switches) - (1 - cpu_core@INST_RETIRED.REP_ITERATION@ / cpu_core@UOPS_RETIRED.MS\\,cmask\\=1@) * (tma_fetch_latency * (tma_ms_switches + tma_branch_resteers * (tma_clears_resteers + tma_mispredicts_resteers * tma_other_mispredicts / tma_branch_mispredicts) / (tma_clears_resteers + tma_mispredicts_resteers + tma_unknown_branches)) / (tma_branch_resteers + tma_dsb_switches + tma_icache_misses + tma_itlb_misses + tma_lcp + tma_ms_switches))) - tma_info_bottleneck_big_code", + "MetricGroup": "BvFB;Fed;FetchBW;Frontend", + "MetricName": "tma_info_bottleneck_instruction_fetch_bw", + "MetricThreshold": "tma_info_bottleneck_instruction_fetch_bw > 20", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost of irregular execution (e.g", + "MetricExpr": "100 * ((1 - cpu_core@INST_RETIRED.REP_ITERATION@ / cpu_core@UOPS_RETIRED.MS\\,cmask\\=1@) * (tma_fetch_latency * (tma_ms_switches + tma_branch_resteers * (tma_clears_resteers + tma_mispredicts_resteers * tma_other_mispredicts / tma_branch_mispredicts) / (tma_clears_resteers + tma_mispredicts_resteers + tma_unknown_branches)) / (tma_branch_resteers + tma_dsb_switches + tma_icache_misses + tma_itlb_misses + tma_lcp + tma_ms_switches)) + 10 * tma_microcode_sequencer * tma_other_mispredicts / tma_branch_mispredicts * tma_branch_mispredicts + tma_machine_clears * tma_other_nukes / tma_other_nukes + tma_core_bound * (tma_serializing_operation + cpu_core@RS.EMPTY\\,umask\\=1@ / tma_info_thread_clks * tma_ports_utilized_0) / (tma_divider + tma_ports_utilization + tma_serializing_operation) + tma_microcode_sequencer / (tma_few_uops_instructions + tma_microcode_sequencer) * (tma_assists / tma_microcode_sequencer) * tma_heavy_operations)", + "MetricGroup": "Bad;BvIO;Cor;Ret;tma_issueMS", + "MetricName": "tma_info_bottleneck_irregular_overhead", + "MetricThreshold": "tma_info_bottleneck_irregular_overhead > 10", + "PublicDescription": "Total pipeline cost of irregular execution (e.g. FP-assists in HPC, Wait time with work imbalance multithreaded workloads, overhead in system services or virtualized environments). Related metrics: tma_microcode_sequencer, tma_ms_switches", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost of Memory Address Translation related bottlenecks (data-side TLBs)", + "MetricExpr": "100 * (tma_memory_bound * (tma_l1_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound)) * (tma_dtlb_load / (tma_dtlb_load + tma_fb_full + tma_l1_hit_latency + tma_lock_latency + tma_split_loads + tma_store_fwd_blk)) + tma_memory_bound * (tma_store_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound)) * (tma_dtlb_store / (tma_dtlb_store + tma_false_sharing + tma_split_stores + tma_store_latency + tma_streaming_stores)))", + "MetricGroup": "BvMT;Mem;MemoryTLB;Offcore;tma_issueTLB", + "MetricName": "tma_info_bottleneck_memory_data_tlbs", + "MetricThreshold": "tma_info_bottleneck_memory_data_tlbs > 20", + "PublicDescription": "Total pipeline cost of Memory Address Translation related bottlenecks (data-side TLBs). Related metrics: tma_dtlb_load, tma_dtlb_store, tma_info_bottleneck_memory_synchronization", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost of Memory Synchronization related bottlenecks (data transfers and coherency updates across processors)", + "MetricExpr": "100 * (tma_memory_bound * (tma_l3_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) * (tma_contested_accesses + tma_data_sharing) / (tma_contested_accesses + tma_data_sharing + tma_l3_hit_latency + tma_sq_full) + tma_store_bound / (tma_dram_bound + tma_l1_bound + tma_l2_bound + tma_l3_bound + tma_store_bound) * tma_false_sharing / (tma_dtlb_store + tma_false_sharing + tma_split_stores + tma_store_latency + tma_streaming_stores - tma_store_latency)) + tma_machine_clears * (1 - tma_other_nukes / tma_other_nukes))", + "MetricGroup": "BvMS;Mem;Offcore;tma_issueTLB", + "MetricName": "tma_info_bottleneck_memory_synchronization", + "MetricThreshold": "tma_info_bottleneck_memory_synchronization > 10", + "PublicDescription": "Total pipeline cost of Memory Synchronization related bottlenecks (data transfers and coherency updates across processors). Related metrics: tma_dtlb_load, tma_dtlb_store, tma_info_bottleneck_memory_data_tlbs", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost of Branch Misprediction related bottlenecks", + "MetricExpr": "100 * (1 - 10 * tma_microcode_sequencer * tma_other_mispredicts / tma_branch_mispredicts) * (tma_branch_mispredicts + tma_fetch_latency * tma_mispredicts_resteers / (tma_branch_resteers + tma_dsb_switches + tma_icache_misses + tma_itlb_misses + tma_lcp + tma_ms_switches))", + "MetricGroup": "Bad;BadSpec;BrMispredicts;BvMP;tma_issueBM", + "MetricName": "tma_info_bottleneck_mispredictions", + "MetricThreshold": "tma_info_bottleneck_mispredictions > 20", + "PublicDescription": "Total pipeline cost of Branch Misprediction related bottlenecks. Related metrics: tma_branch_mispredicts, tma_info_bad_spec_branch_misprediction_cost, tma_mispredicts_resteers", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost of remaining bottlenecks in the back-end", + "MetricExpr": "100 - (tma_info_bottleneck_big_code + tma_info_bottleneck_instruction_fetch_bw + tma_info_bottleneck_mispredictions + tma_info_bottleneck_cache_memory_bandwidth + tma_info_bottleneck_cache_memory_latency + tma_info_bottleneck_memory_data_tlbs + tma_info_bottleneck_memory_synchronization + tma_info_bottleneck_compute_bound_est + tma_info_bottleneck_irregular_overhead + tma_info_bottleneck_branching_overhead + tma_info_bottleneck_useful_work)", + "MetricGroup": "BvOB;Cor;Offcore", + "MetricName": "tma_info_bottleneck_other_bottlenecks", + "MetricThreshold": "tma_info_bottleneck_other_bottlenecks > 20", + "PublicDescription": "Total pipeline cost of remaining bottlenecks in the back-end. Examples include data-dependencies (Core Bound when Low ILP) and other unlisted memory-related stalls.", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total pipeline cost of \"useful operations\" - the portion of Retiring category not covered by Branching_Overhead nor Irregular_Overhead.", + "MetricExpr": "100 * (tma_retiring - (cpu_core@BR_INST_RETIRED.ALL_BRANCHES@ + 2 * cpu_core@BR_INST_RETIRED.NEAR_CALL@ + cpu_core@INST_RETIRED.NOP@) / tma_info_thread_slots - tma_microcode_sequencer / (tma_few_uops_instructions + tma_microcode_sequencer) * (tma_assists / tma_microcode_sequencer) * tma_heavy_operations)", + "MetricGroup": "BvUW;Ret", + "MetricName": "tma_info_bottleneck_useful_work", + "MetricThreshold": "tma_info_bottleneck_useful_work > 20", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fraction of branches that are CALL or RET", + "MetricExpr": "(cpu_core@BR_INST_RETIRED.NEAR_CALL@ + cpu_core@BR_INST_RETIRED.NEAR_RETURN@) / cpu_core@BR_INST_RETIRED.ALL_BRANCHES@", + "MetricGroup": "Bad;Branches", + "MetricName": "tma_info_branches_callret", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fraction of branches that are non-taken conditionals", + "MetricExpr": "cpu_core@BR_INST_RETIRED.COND_NTAKEN@ / cpu_core@BR_INST_RETIRED.ALL_BRANCHES@", + "MetricGroup": "Bad;Branches;CodeGen;PGO", + "MetricName": "tma_info_branches_cond_nt", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fraction of branches that are taken conditionals", + "MetricExpr": "cpu_core@BR_INST_RETIRED.COND_TAKEN@ / cpu_core@BR_INST_RETIRED.ALL_BRANCHES@", + "MetricGroup": "Bad;Branches;CodeGen;PGO", + "MetricName": "tma_info_branches_cond_tk", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fraction of branches that are unconditional (direct or indirect) jumps", + "MetricExpr": "(cpu_core@BR_INST_RETIRED.NEAR_TAKEN@ - cpu_core@BR_INST_RETIRED.COND_TAKEN@ - 2 * cpu_core@BR_INST_RETIRED.NEAR_CALL@) / cpu_core@BR_INST_RETIRED.ALL_BRANCHES@", + "MetricGroup": "Bad;Branches", + "MetricName": "tma_info_branches_jump", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fraction of branches of other types (not individually covered by other metrics in Info.Branches group)", + "MetricExpr": "1 - (tma_info_branches_cond_nt + tma_info_branches_cond_tk + tma_info_branches_callret + tma_info_branches_jump)", + "MetricGroup": "Bad;Branches", + "MetricName": "tma_info_branches_other_branches", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Core actual clocks when any Logical Processor is active on the Physical Core", + "MetricExpr": "(cpu_core@CPU_CLK_UNHALTED.DISTRIBUTED@ if #SMT_on else tma_info_thread_clks)", + "MetricGroup": "SMT", + "MetricName": "tma_info_core_core_clks", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions Per Cycle across hyper-threads (per physical core)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / tma_info_core_core_clks", + "MetricGroup": "Ret;SMT;TmaL1;tma_L1_group", + "MetricName": "tma_info_core_coreipc", + "Unit": "cpu_core" + }, + { + "BriefDescription": "uops Executed per Cycle", + "MetricExpr": "cpu_core@UOPS_EXECUTED.THREAD@ / tma_info_thread_clks", + "MetricGroup": "Power", + "MetricName": "tma_info_core_epc", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Floating Point Operations Per Cycle", + "MetricExpr": "(cpu_core@FP_ARITH_INST_RETIRED.SCALAR@ + 2 * cpu_core@FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE@ + 4 * cpu_core@FP_ARITH_INST_RETIRED.4_FLOPS@ + 8 * cpu_core@FP_ARITH_INST_RETIRED.256B_PACKED_SINGLE@) / tma_info_core_core_clks", + "MetricGroup": "Flops;Ret", + "MetricName": "tma_info_core_flopc", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Actual per-core usage of the Floating Point non-X87 execution units (regardless of precision or vector-width)", + "MetricExpr": "(cpu_core@FP_ARITH_DISPATCHED.PORT_0@ + cpu_core@FP_ARITH_DISPATCHED.PORT_1@ + cpu_core@FP_ARITH_DISPATCHED.PORT_5@) / (2 * tma_info_core_core_clks)", + "MetricGroup": "Cor;Flops;HPC", + "MetricName": "tma_info_core_fp_arith_utilization", + "PublicDescription": "Actual per-core usage of the Floating Point non-X87 execution units (regardless of precision or vector-width). Values > 1 are possible due to ([BDW+] Fused-Multiply Add (FMA) counting - common; [ADL+] use all of ADD/MUL/FMA in Scalar or 128/256-bit vectors - less common).", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instruction-Level-Parallelism (average number of uops executed when there is execution) per thread (logical-processor)", + "MetricExpr": "cpu_core@UOPS_EXECUTED.THREAD@ / cpu_core@UOPS_EXECUTED.THREAD\\,cmask\\=1@", + "MetricGroup": "Backend;Cor;Pipeline;PortsUtil", + "MetricName": "tma_info_core_ilp", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fraction of Uops delivered by the DSB (aka Decoded ICache; or Uop Cache)", + "MetricExpr": "cpu_core@IDQ.DSB_UOPS@ / cpu_core@UOPS_ISSUED.ANY@", + "MetricGroup": "DSB;Fed;FetchBW;tma_issueFB", + "MetricName": "tma_info_frontend_dsb_coverage", + "MetricThreshold": "tma_info_frontend_dsb_coverage < 0.7 & tma_info_thread_ipc / 6 > 0.35", + "PublicDescription": "Fraction of Uops delivered by the DSB (aka Decoded ICache; or Uop Cache). Related metrics: tma_dsb_switches, tma_fetch_bandwidth, tma_info_botlnk_l2_dsb_bandwidth, tma_info_botlnk_l2_dsb_misses, tma_info_inst_mix_iptb, tma_lcp", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average number of cycles of a switch from the DSB fetch-unit to MITE fetch unit - see DSB_Switches tree node for details.", + "MetricExpr": "cpu_core@DSB2MITE_SWITCHES.PENALTY_CYCLES@ / cpu_core@DSB2MITE_SWITCHES.PENALTY_CYCLES\\,cmask\\=1\\,edge@", + "MetricGroup": "DSBmiss", + "MetricName": "tma_info_frontend_dsb_switch_cost", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average number of Uops issued by front-end when it issued something", + "MetricExpr": "cpu_core@UOPS_ISSUED.ANY@ / cpu_core@UOPS_ISSUED.ANY\\,cmask\\=1@", + "MetricGroup": "Fed;FetchBW", + "MetricName": "tma_info_frontend_fetch_upc", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average Latency for L1 instruction cache misses", + "MetricExpr": "cpu_core@ICACHE_DATA.STALLS@ / cpu_core@ICACHE_DATA.STALLS\\,cmask\\=1\\,edge@", + "MetricGroup": "Fed;FetchLat;IcMiss", + "MetricName": "tma_info_frontend_icache_miss_latency", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per non-speculative DSB miss (lower number means higher occurrence rate)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@FRONTEND_RETIRED.ANY_DSB_MISS@", + "MetricGroup": "DSBmiss;Fed", + "MetricName": "tma_info_frontend_ipdsb_miss_ret", + "MetricThreshold": "tma_info_frontend_ipdsb_miss_ret < 50", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per speculative Unknown Branch Misprediction (BAClear) (lower number means higher occurrence rate)", + "MetricExpr": "tma_info_inst_mix_instructions / cpu_core@BACLEARS.ANY@", + "MetricGroup": "Fed", + "MetricName": "tma_info_frontend_ipunknown_branch", + "Unit": "cpu_core" + }, + { + "BriefDescription": "L2 cache true code cacheline misses per kilo instruction", + "MetricExpr": "1e3 * cpu_core@FRONTEND_RETIRED.L2_MISS@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "IcMiss", + "MetricName": "tma_info_frontend_l2mpki_code", + "Unit": "cpu_core" + }, + { + "BriefDescription": "L2 cache speculative code cacheline misses per kilo instruction", + "MetricExpr": "1e3 * cpu_core@L2_RQSTS.CODE_RD_MISS@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "IcMiss", + "MetricName": "tma_info_frontend_l2mpki_code_all", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fraction of Uops delivered by the LSD (Loop Stream Detector; aka Loop Cache)", + "MetricExpr": "cpu_core@LSD.UOPS@ / cpu_core@UOPS_ISSUED.ANY@", + "MetricGroup": "Fed;LSD", + "MetricName": "tma_info_frontend_lsd_coverage", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average number of cycles the front-end was delayed due to an Unknown Branch detection", + "MetricExpr": "cpu_core@INT_MISC.UNKNOWN_BRANCH_CYCLES@ / cpu_core@INT_MISC.UNKNOWN_BRANCH_CYCLES\\,cmask\\=1\\,edge@", + "MetricGroup": "Fed", + "MetricName": "tma_info_frontend_unknown_branch_cost", + "PublicDescription": "Average number of cycles the front-end was delayed due to an Unknown Branch detection. See Unknown_Branches node.", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Branch instructions per taken branch.", + "MetricExpr": "cpu_core@BR_INST_RETIRED.ALL_BRANCHES@ / cpu_core@BR_INST_RETIRED.NEAR_TAKEN@", + "MetricGroup": "Branches;Fed;PGO", + "MetricName": "tma_info_inst_mix_bptkbranch", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total number of retired Instructions", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "Summary;TmaL1;tma_L1_group", + "MetricName": "tma_info_inst_mix_instructions", + "PublicDescription": "Total number of retired Instructions. Sample with: INST_RETIRED.PREC_DIST", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per FP Arithmetic instruction (lower number means higher occurrence rate)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / (cpu_core@FP_ARITH_INST_RETIRED.SCALAR@ + cpu_core@FP_ARITH_INST_RETIRED.VECTOR@)", + "MetricGroup": "Flops;InsType", + "MetricName": "tma_info_inst_mix_iparith", + "MetricThreshold": "tma_info_inst_mix_iparith < 10", + "PublicDescription": "Instructions per FP Arithmetic instruction (lower number means higher occurrence rate). Values < 1 are possible due to intentional FMA double counting. Approximated prior to BDW.", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per FP Arithmetic AVX/SSE 128-bit instruction (lower number means higher occurrence rate)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / (cpu_core@FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE@ + cpu_core@FP_ARITH_INST_RETIRED.128B_PACKED_SINGLE@)", + "MetricGroup": "Flops;FpVector;InsType", + "MetricName": "tma_info_inst_mix_iparith_avx128", + "MetricThreshold": "tma_info_inst_mix_iparith_avx128 < 10", + "PublicDescription": "Instructions per FP Arithmetic AVX/SSE 128-bit instruction (lower number means higher occurrence rate). Values < 1 are possible due to intentional FMA double counting.", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per FP Arithmetic AVX* 256-bit instruction (lower number means higher occurrence rate)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / (cpu_core@FP_ARITH_INST_RETIRED.256B_PACKED_DOUBLE@ + cpu_core@FP_ARITH_INST_RETIRED.256B_PACKED_SINGLE@)", + "MetricGroup": "Flops;FpVector;InsType", + "MetricName": "tma_info_inst_mix_iparith_avx256", + "MetricThreshold": "tma_info_inst_mix_iparith_avx256 < 10", + "PublicDescription": "Instructions per FP Arithmetic AVX* 256-bit instruction (lower number means higher occurrence rate). Values < 1 are possible due to intentional FMA double counting.", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per FP Arithmetic Scalar Double-Precision instruction (lower number means higher occurrence rate)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@FP_ARITH_INST_RETIRED.SCALAR_DOUBLE@", + "MetricGroup": "Flops;FpScalar;InsType", + "MetricName": "tma_info_inst_mix_iparith_scalar_dp", + "MetricThreshold": "tma_info_inst_mix_iparith_scalar_dp < 10", + "PublicDescription": "Instructions per FP Arithmetic Scalar Double-Precision instruction (lower number means higher occurrence rate). Values < 1 are possible due to intentional FMA double counting.", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per FP Arithmetic Scalar Single-Precision instruction (lower number means higher occurrence rate)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@FP_ARITH_INST_RETIRED.SCALAR_SINGLE@", + "MetricGroup": "Flops;FpScalar;InsType", + "MetricName": "tma_info_inst_mix_iparith_scalar_sp", + "MetricThreshold": "tma_info_inst_mix_iparith_scalar_sp < 10", + "PublicDescription": "Instructions per FP Arithmetic Scalar Single-Precision instruction (lower number means higher occurrence rate). Values < 1 are possible due to intentional FMA double counting.", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per Branch (lower number means higher occurrence rate)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@BR_INST_RETIRED.ALL_BRANCHES@", + "MetricGroup": "Branches;Fed;InsType", + "MetricName": "tma_info_inst_mix_ipbranch", + "MetricThreshold": "tma_info_inst_mix_ipbranch < 8", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per (near) call (lower number means higher occurrence rate)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@BR_INST_RETIRED.NEAR_CALL@", + "MetricGroup": "Branches;Fed;PGO", + "MetricName": "tma_info_inst_mix_ipcall", + "MetricThreshold": "tma_info_inst_mix_ipcall < 200", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per Floating Point (FP) Operation (lower number means higher occurrence rate)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / (cpu_core@FP_ARITH_INST_RETIRED.SCALAR@ + 2 * cpu_core@FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE@ + 4 * cpu_core@FP_ARITH_INST_RETIRED.4_FLOPS@ + 8 * cpu_core@FP_ARITH_INST_RETIRED.256B_PACKED_SINGLE@)", + "MetricGroup": "Flops;InsType", + "MetricName": "tma_info_inst_mix_ipflop", + "MetricThreshold": "tma_info_inst_mix_ipflop < 10", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per Load (lower number means higher occurrence rate)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@MEM_INST_RETIRED.ALL_LOADS@", + "MetricGroup": "InsType", + "MetricName": "tma_info_inst_mix_ipload", + "MetricThreshold": "tma_info_inst_mix_ipload < 3", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per PAUSE (lower number means higher occurrence rate)", + "MetricExpr": "tma_info_inst_mix_instructions / cpu_core@CPU_CLK_UNHALTED.PAUSE_INST@", + "MetricGroup": "Flops;FpVector;InsType", + "MetricName": "tma_info_inst_mix_ippause", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per Store (lower number means higher occurrence rate)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@MEM_INST_RETIRED.ALL_STORES@", + "MetricGroup": "InsType", + "MetricName": "tma_info_inst_mix_ipstore", + "MetricThreshold": "tma_info_inst_mix_ipstore < 8", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per Software prefetch instruction (of any type: NTA/T0/T1/T2/Prefetch) (lower number means higher occurrence rate)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@SW_PREFETCH_ACCESS.T0\\,umask\\=0xF@", + "MetricGroup": "Prefetches", + "MetricName": "tma_info_inst_mix_ipswpf", + "MetricThreshold": "tma_info_inst_mix_ipswpf < 100", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per taken branch", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@BR_INST_RETIRED.NEAR_TAKEN@", + "MetricGroup": "Branches;Fed;FetchBW;Frontend;PGO;tma_issueFB", + "MetricName": "tma_info_inst_mix_iptb", + "MetricThreshold": "tma_info_inst_mix_iptb < 13", + "PublicDescription": "Instructions per taken branch. Related metrics: tma_dsb_switches, tma_fetch_bandwidth, tma_info_botlnk_l2_dsb_bandwidth, tma_info_botlnk_l2_dsb_misses, tma_info_frontend_dsb_coverage, tma_lcp", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average per-core data fill bandwidth to the L1 data cache [GB / sec]", + "MetricExpr": "tma_info_memory_l1d_cache_fill_bw", + "MetricGroup": "Mem;MemoryBW", + "MetricName": "tma_info_memory_core_l1d_cache_fill_bw_2t", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average per-core data fill bandwidth to the L2 cache [GB / sec]", + "MetricExpr": "tma_info_memory_l2_cache_fill_bw", + "MetricGroup": "Mem;MemoryBW", + "MetricName": "tma_info_memory_core_l2_cache_fill_bw_2t", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average per-core data access bandwidth to the L3 cache [GB / sec]", + "MetricExpr": "tma_info_memory_l3_cache_access_bw", + "MetricGroup": "Mem;MemoryBW;Offcore", + "MetricName": "tma_info_memory_core_l3_cache_access_bw_2t", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average per-core data fill bandwidth to the L3 cache [GB / sec]", + "MetricExpr": "tma_info_memory_l3_cache_fill_bw", + "MetricGroup": "Mem;MemoryBW", + "MetricName": "tma_info_memory_core_l3_cache_fill_bw_2t", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fill Buffer (FB) hits per kilo instructions for retired demand loads (L1D misses that merge into ongoing miss-handling entries)", + "MetricExpr": "1e3 * cpu_core@MEM_LOAD_RETIRED.FB_HIT@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "CacheHits;Mem", + "MetricName": "tma_info_memory_fb_hpki", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average per-thread data fill bandwidth to the L1 data cache [GB / sec]", + "MetricExpr": "64 * cpu_core@L1D.REPLACEMENT@ / 1e9 / duration_time", + "MetricGroup": "Mem;MemoryBW", + "MetricName": "tma_info_memory_l1d_cache_fill_bw", + "Unit": "cpu_core" + }, + { + "BriefDescription": "L1 cache true misses per kilo instruction for retired demand loads", + "MetricExpr": "1e3 * cpu_core@MEM_LOAD_RETIRED.L1_MISS@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "CacheHits;Mem", + "MetricName": "tma_info_memory_l1mpki", + "Unit": "cpu_core" + }, + { + "BriefDescription": "L1 cache true misses per kilo instruction for all demand loads (including speculative)", + "MetricExpr": "1e3 * cpu_core@L2_RQSTS.ALL_DEMAND_DATA_RD@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "CacheHits;Mem", + "MetricName": "tma_info_memory_l1mpki_load", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average per-thread data fill bandwidth to the L2 cache [GB / sec]", + "MetricExpr": "64 * cpu_core@L2_LINES_IN.ALL@ / 1e9 / duration_time", + "MetricGroup": "Mem;MemoryBW", + "MetricName": "tma_info_memory_l2_cache_fill_bw", + "Unit": "cpu_core" + }, + { + "BriefDescription": "L2 cache hits per kilo instruction for all request types (including speculative)", + "MetricExpr": "1e3 * (cpu_core@L2_RQSTS.REFERENCES@ - cpu_core@L2_RQSTS.MISS@) / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "CacheHits;Mem", + "MetricName": "tma_info_memory_l2hpki_all", + "Unit": "cpu_core" + }, + { + "BriefDescription": "L2 cache hits per kilo instruction for all demand loads (including speculative)", + "MetricExpr": "1e3 * cpu_core@L2_RQSTS.DEMAND_DATA_RD_HIT@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "CacheHits;Mem", + "MetricName": "tma_info_memory_l2hpki_load", + "Unit": "cpu_core" + }, + { + "BriefDescription": "L2 cache true misses per kilo instruction for retired demand loads", + "MetricExpr": "1e3 * cpu_core@MEM_LOAD_RETIRED.L2_MISS@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "Backend;CacheHits;Mem", + "MetricName": "tma_info_memory_l2mpki", + "Unit": "cpu_core" + }, + { + "BriefDescription": "L2 cache ([RKL+] true) misses per kilo instruction for all request types (including speculative)", + "MetricExpr": "1e3 * cpu_core@L2_RQSTS.MISS@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "CacheHits;Mem;Offcore", + "MetricName": "tma_info_memory_l2mpki_all", + "Unit": "cpu_core" + }, + { + "BriefDescription": "L2 cache ([RKL+] true) misses per kilo instruction for all demand loads (including speculative)", + "MetricExpr": "1e3 * cpu_core@L2_RQSTS.DEMAND_DATA_RD_MISS@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "CacheHits;Mem", + "MetricName": "tma_info_memory_l2mpki_load", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Offcore requests (L2 cache miss) per kilo instruction for demand RFOs", + "MetricExpr": "1e3 * cpu_core@L2_RQSTS.RFO_MISS@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "CacheMisses;Offcore", + "MetricName": "tma_info_memory_l2mpki_rfo", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average per-thread data access bandwidth to the L3 cache [GB / sec]", + "MetricExpr": "64 * cpu_core@OFFCORE_REQUESTS.ALL_REQUESTS@ / 1e9 / duration_time", + "MetricGroup": "Mem;MemoryBW;Offcore", + "MetricName": "tma_info_memory_l3_cache_access_bw", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average per-thread data fill bandwidth to the L3 cache [GB / sec]", + "MetricExpr": "64 * cpu_core@LONGEST_LAT_CACHE.MISS@ / 1e9 / duration_time", + "MetricGroup": "Mem;MemoryBW", + "MetricName": "tma_info_memory_l3_cache_fill_bw", + "Unit": "cpu_core" + }, + { + "BriefDescription": "L3 cache true misses per kilo instruction for retired demand loads", + "MetricExpr": "1e3 * cpu_core@MEM_LOAD_RETIRED.L3_MISS@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "Mem", + "MetricName": "tma_info_memory_l3mpki", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average Parallel L2 cache miss data reads", + "MetricExpr": "cpu_core@OFFCORE_REQUESTS_OUTSTANDING.DATA_RD@ / cpu_core@OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DATA_RD@", + "MetricGroup": "Memory_BW;Offcore", + "MetricName": "tma_info_memory_latency_data_l2_mlp", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average Latency for L2 cache miss demand Loads", + "MetricExpr": "cpu_core@OFFCORE_REQUESTS_OUTSTANDING.DEMAND_DATA_RD@ / cpu_core@OFFCORE_REQUESTS.DEMAND_DATA_RD@", + "MetricGroup": "Memory_Lat;Offcore", + "MetricName": "tma_info_memory_latency_load_l2_miss_latency", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average Parallel L2 cache miss demand Loads", + "MetricExpr": "cpu_core@OFFCORE_REQUESTS_OUTSTANDING.DEMAND_DATA_RD@ / cpu_core@OFFCORE_REQUESTS_OUTSTANDING.DEMAND_DATA_RD\\,cmask\\=1@", + "MetricGroup": "Memory_BW;Offcore", + "MetricName": "tma_info_memory_latency_load_l2_mlp", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average Latency for L3 cache miss demand Loads", + "MetricExpr": "cpu_core@OFFCORE_REQUESTS_OUTSTANDING.L3_MISS_DEMAND_DATA_RD@ / cpu_core@OFFCORE_REQUESTS.L3_MISS_DEMAND_DATA_RD@", + "MetricGroup": "Memory_Lat;Offcore", + "MetricName": "tma_info_memory_latency_load_l3_miss_latency", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Actual Average Latency for L1 data-cache miss demand load operations (in core cycles)", + "MetricExpr": "cpu_core@L1D_PEND_MISS.PENDING@ / cpu_core@MEM_LOAD_COMPLETED.L1_MISS_ANY@", + "MetricGroup": "Mem;MemoryBound;MemoryLat", + "MetricName": "tma_info_memory_load_miss_real_latency", + "Unit": "cpu_core" + }, + { + "BriefDescription": "\"Bus lock\" per kilo instruction", + "MetricExpr": "1e3 * cpu_core@SQ_MISC.BUS_LOCK@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "Mem", + "MetricName": "tma_info_memory_mix_bus_lock_pki", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Un-cacheable retired load per kilo instruction", + "MetricExpr": "1e3 * cpu_core@MEM_LOAD_MISC_RETIRED.UC@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "Mem", + "MetricName": "tma_info_memory_mix_uc_load_pki", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Memory-Level-Parallelism (average number of L1 miss demand load when there is at least one such miss", + "MetricExpr": "cpu_core@L1D_PEND_MISS.PENDING@ / cpu_core@L1D_PEND_MISS.PENDING_CYCLES@", + "MetricGroup": "Mem;MemoryBW;MemoryBound", + "MetricName": "tma_info_memory_mlp", + "PublicDescription": "Memory-Level-Parallelism (average number of L1 miss demand load when there is at least one such miss. Per-Logical Processor)", + "Unit": "cpu_core" + }, + { + "BriefDescription": "STLB (2nd level TLB) code speculative misses per kilo instruction (misses of any page-size that complete the page walk)", + "MetricExpr": "1e3 * cpu_core@ITLB_MISSES.WALK_COMPLETED@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "Fed;MemoryTLB", + "MetricName": "tma_info_memory_tlb_code_stlb_mpki", + "Unit": "cpu_core" + }, + { + "BriefDescription": "STLB (2nd level TLB) data load speculative misses per kilo instruction (misses of any page-size that complete the page walk)", + "MetricExpr": "1e3 * cpu_core@DTLB_LOAD_MISSES.WALK_COMPLETED@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "Mem;MemoryTLB", + "MetricName": "tma_info_memory_tlb_load_stlb_mpki", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Utilization of the core's Page Walker(s) serving STLB misses triggered by instruction/Load/Store accesses", + "MetricExpr": "(cpu_core@ITLB_MISSES.WALK_PENDING@ + cpu_core@DTLB_LOAD_MISSES.WALK_PENDING@ + cpu_core@DTLB_STORE_MISSES.WALK_PENDING@) / (4 * tma_info_core_core_clks)", + "MetricGroup": "Mem;MemoryTLB", + "MetricName": "tma_info_memory_tlb_page_walks_utilization", + "MetricThreshold": "tma_info_memory_tlb_page_walks_utilization > 0.5", + "Unit": "cpu_core" + }, + { + "BriefDescription": "STLB (2nd level TLB) data store speculative misses per kilo instruction (misses of any page-size that complete the page walk)", + "MetricExpr": "1e3 * cpu_core@DTLB_STORE_MISSES.WALK_COMPLETED@ / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "Mem;MemoryTLB", + "MetricName": "tma_info_memory_tlb_store_stlb_mpki", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instruction-Level-Parallelism (average number of uops executed when there is execution) per core", + "MetricExpr": "cpu_core@UOPS_EXECUTED.THREAD@ / (cpu_core@UOPS_EXECUTED.CORE_CYCLES_GE_1@ / 2 if #SMT_on else cpu_core@UOPS_EXECUTED.THREAD\\,cmask\\=1@)", + "MetricGroup": "Cor;Pipeline;PortsUtil;SMT", + "MetricName": "tma_info_pipeline_execute", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average number of uops fetched from DSB per cycle", + "MetricExpr": "cpu_core@IDQ.DSB_UOPS@ / cpu_core@IDQ.DSB_CYCLES_ANY@", + "MetricGroup": "Fed;FetchBW", + "MetricName": "tma_info_pipeline_fetch_dsb", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average number of uops fetched from LSD per cycle", + "MetricExpr": "cpu_core@LSD.UOPS@ / cpu_core@LSD.CYCLES_ACTIVE@", + "MetricGroup": "Fed;FetchBW", + "MetricName": "tma_info_pipeline_fetch_lsd", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average number of uops fetched from MITE per cycle", + "MetricExpr": "cpu_core@IDQ.MITE_UOPS@ / cpu_core@IDQ.MITE_CYCLES_ANY@", + "MetricGroup": "Fed;FetchBW", + "MetricName": "tma_info_pipeline_fetch_mite", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per a microcode Assist invocation", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@ASSISTS.ANY@", + "MetricGroup": "MicroSeq;Pipeline;Ret;Retire", + "MetricName": "tma_info_pipeline_ipassist", + "MetricThreshold": "tma_info_pipeline_ipassist < 100e3", + "PublicDescription": "Instructions per a microcode Assist invocation. See Assists tree node for details (lower number means higher occurrence rate)", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average number of Uops retired in cycles where at least one uop has retired.", + "MetricExpr": "tma_retiring * tma_info_thread_slots / cpu_core@UOPS_RETIRED.SLOTS\\,cmask\\=1@", + "MetricGroup": "Pipeline;Ret", + "MetricName": "tma_info_pipeline_retire", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Estimated fraction of retirement-cycles dealing with repeat instructions", + "MetricExpr": "cpu_core@INST_RETIRED.REP_ITERATION@ / cpu_core@UOPS_RETIRED.SLOTS\\,cmask\\=1@", + "MetricGroup": "MicroSeq;Pipeline;Ret", + "MetricName": "tma_info_pipeline_strings_cycles", + "MetricThreshold": "tma_info_pipeline_strings_cycles > 0.1", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fraction of cycles the processor is waiting yet unhalted; covering legacy PAUSE instruction, as well as C0.1 / C0.2 power-performance optimized states", + "MetricExpr": "cpu_core@CPU_CLK_UNHALTED.C0_WAIT@ / tma_info_thread_clks", + "MetricGroup": "C0Wait", + "MetricName": "tma_info_system_c0_wait", + "MetricThreshold": "tma_info_system_c0_wait > 0.05", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Measured Average Core Frequency for unhalted processors [GHz]", + "MetricExpr": "tma_info_system_turbo_utilization * TSC / 1e9 / duration_time", + "MetricGroup": "Power;Summary", + "MetricName": "tma_info_system_core_frequency", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average CPU Utilization (percentage)", + "MetricExpr": "tma_info_system_cpus_utilized / #num_cpus_online", + "MetricGroup": "HPC;Summary", + "MetricName": "tma_info_system_cpu_utilization", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average number of utilized CPUs", + "MetricExpr": "cpu_core@CPU_CLK_UNHALTED.REF_TSC@ / TSC", + "MetricGroup": "Summary", + "MetricName": "tma_info_system_cpus_utilized", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average external Memory Bandwidth Use for reads and writes [GB / sec]", + "MetricExpr": "64 * (UNC_HAC_ARB_TRK_REQUESTS.ALL + UNC_HAC_ARB_COH_TRK_REQUESTS.ALL) / 1e9 / duration_time", + "MetricGroup": "HPC;MemOffcore;MemoryBW;SoC;tma_issueBW", + "MetricName": "tma_info_system_dram_bw_use", + "PublicDescription": "Average external Memory Bandwidth Use for reads and writes [GB / sec]. Related metrics: tma_fb_full, tma_info_bottleneck_cache_memory_bandwidth, tma_mem_bandwidth, tma_sq_full", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Giga Floating Point Operations Per Second", + "MetricExpr": "(cpu_core@FP_ARITH_INST_RETIRED.SCALAR@ + 2 * cpu_core@FP_ARITH_INST_RETIRED.128B_PACKED_DOUBLE@ + 4 * cpu_core@FP_ARITH_INST_RETIRED.4_FLOPS@ + 8 * cpu_core@FP_ARITH_INST_RETIRED.256B_PACKED_SINGLE@) / 1e9 / duration_time", + "MetricGroup": "Cor;Flops;HPC", + "MetricName": "tma_info_system_gflops", + "PublicDescription": "Giga Floating Point Operations Per Second. Aggregate across all supported options of: FP precisions, scalar and vector instructions, vector-width", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions per Far Branch ( Far Branches apply upon transition from application to operating system, handling interrupts, exceptions) [lower number means higher occurrence rate]", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / cpu_core@BR_INST_RETIRED.FAR_BRANCH@u", + "MetricGroup": "Branches;OS", + "MetricName": "tma_info_system_ipfarbranch", + "MetricThreshold": "tma_info_system_ipfarbranch < 1e6", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Cycles Per Instruction for the Operating System (OS) Kernel mode", + "MetricExpr": "cpu_core@CPU_CLK_UNHALTED.THREAD_P@k / cpu_core@INST_RETIRED.ANY_P@k", + "MetricGroup": "OS", + "MetricName": "tma_info_system_kernel_cpi", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fraction of cycles spent in the Operating System (OS) Kernel mode", + "MetricExpr": "cpu_core@CPU_CLK_UNHALTED.THREAD_P@k / cpu_core@CPU_CLK_UNHALTED.THREAD@", + "MetricGroup": "OS", + "MetricName": "tma_info_system_kernel_utilization", + "MetricThreshold": "tma_info_system_kernel_utilization > 0.05", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average number of parallel data read requests to external memory", + "MetricExpr": "UNC_ARB_DAT_OCCUPANCY.RD / UNC_ARB_DAT_OCCUPANCY.RD@cmask\\=1@", + "MetricGroup": "Mem;MemoryBW;SoC", + "MetricName": "tma_info_system_mem_parallel_reads", + "PublicDescription": "Average number of parallel data read requests to external memory. Accounts for demand loads and L1/L2 prefetches", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fraction of cycles where both hardware Logical Processors were active", + "MetricExpr": "(1 - cpu_core@CPU_CLK_UNHALTED.ONE_THREAD_ACTIVE@ / cpu_core@CPU_CLK_UNHALTED.REF_DISTRIBUTED@ if #SMT_on else 0)", + "MetricGroup": "SMT", + "MetricName": "tma_info_system_smt_2t_utilization", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Socket actual clocks when any core is active on that socket", + "MetricExpr": "UNC_CLOCK.SOCKET", + "MetricGroup": "SoC", + "MetricName": "tma_info_system_socket_clks", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Average Frequency Utilization relative nominal frequency", + "MetricExpr": "tma_info_thread_clks / cpu_core@CPU_CLK_UNHALTED.REF_TSC@", + "MetricGroup": "Power", + "MetricName": "tma_info_system_turbo_utilization", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Per-Logical Processor actual clocks when the Logical Processor is active.", + "MetricExpr": "cpu_core@CPU_CLK_UNHALTED.THREAD@", + "MetricGroup": "Pipeline", + "MetricName": "tma_info_thread_clks", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Cycles Per Instruction (per Logical Processor)", + "MetricExpr": "1 / tma_info_thread_ipc", + "MetricGroup": "Mem;Pipeline", + "MetricName": "tma_info_thread_cpi", + "Unit": "cpu_core" + }, + { + "BriefDescription": "The ratio of Executed- by Issued-Uops", + "MetricExpr": "cpu_core@UOPS_EXECUTED.THREAD@ / cpu_core@UOPS_ISSUED.ANY@", + "MetricGroup": "Cor;Pipeline", + "MetricName": "tma_info_thread_execute_per_issue", + "PublicDescription": "The ratio of Executed- by Issued-Uops. Ratio > 1 suggests high rate of uop micro-fusions. Ratio < 1 suggest high rate of \"execute\" at rename stage.", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Instructions Per Cycle (per Logical Processor)", + "MetricExpr": "cpu_core@INST_RETIRED.ANY@ / tma_info_thread_clks", + "MetricGroup": "Ret;Summary", + "MetricName": "tma_info_thread_ipc", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Total issue-pipeline slots (per-Physical Core till ICL; per-Logical Processor ICL onward)", + "MetricExpr": "cpu_core@TOPDOWN.SLOTS@", + "MetricGroup": "TmaL1;tma_L1_group", + "MetricName": "tma_info_thread_slots", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Fraction of Physical Core issue-slots utilized by this Logical Processor", + "MetricExpr": "(tma_info_thread_slots / (cpu_core@TOPDOWN.SLOTS@ / 2) if #SMT_on else 1)", + "MetricGroup": "SMT;TmaL1;tma_L1_group", + "MetricName": "tma_info_thread_slots_utilization", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Uops Per Instruction", + "MetricExpr": "tma_retiring * tma_info_thread_slots / cpu_core@INST_RETIRED.ANY@", + "MetricGroup": "Pipeline;Ret;Retire", + "MetricName": "tma_info_thread_uoppi", + "MetricThreshold": "tma_info_thread_uoppi > 1.05", + "Unit": "cpu_core" + }, + { + "BriefDescription": "Uops per taken branch", + "MetricExpr": "tma_retiring * tma_info_thread_slots / cpu_core@BR_INST_RETIRED.NEAR_TAKEN@", + "MetricGroup": "Branches;Fed;FetchBW", + "MetricName": "tma_info_thread_uptb", + "MetricThreshold": "tma_info_thread_uptb < 9", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents overall Integer (Int) select operations fraction the CPU has executed (retired)", + "MetricExpr": "tma_int_vector_128b + tma_int_vector_256b", + "MetricGroup": "Pipeline;TopdownL3;tma_L3_group;tma_light_operations_group", + "MetricName": "tma_int_operations", + "MetricThreshold": "tma_int_operations > 0.1 & tma_light_operations > 0.6", + "PublicDescription": "This metric represents overall Integer (Int) select operations fraction the CPU has executed (retired). Vector/Matrix Int operations and shuffles are counted. Note this metric's value may exceed its parent due to use of \"Uops\" CountDomain.", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents 128-bit vector Integer ADD/SUB/SAD or VNNI (Vector Neural Network Instructions) uops fraction the CPU has retired", + "MetricExpr": "(cpu_core@INT_VEC_RETIRED.ADD_128@ + cpu_core@INT_VEC_RETIRED.VNNI_128@) / (tma_retiring * tma_info_thread_slots)", + "MetricGroup": "Compute;IntVector;Pipeline;TopdownL4;tma_L4_group;tma_int_operations_group;tma_issue2P", + "MetricName": "tma_int_vector_128b", + "MetricThreshold": "tma_int_vector_128b > 0.1 & (tma_int_operations > 0.1 & tma_light_operations > 0.6)", + "PublicDescription": "This metric represents 128-bit vector Integer ADD/SUB/SAD or VNNI (Vector Neural Network Instructions) uops fraction the CPU has retired. Related metrics: tma_fp_scalar, tma_fp_vector, tma_fp_vector_128b, tma_fp_vector_256b, tma_fp_vector_512b, tma_int_vector_256b, tma_port_0, tma_port_1, tma_port_5, tma_port_6, tma_ports_utilized_2", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents 256-bit vector Integer ADD/SUB/SAD/MUL or VNNI (Vector Neural Network Instructions) uops fraction the CPU has retired", + "MetricExpr": "(cpu_core@INT_VEC_RETIRED.ADD_256@ + cpu_core@INT_VEC_RETIRED.MUL_256@ + cpu_core@INT_VEC_RETIRED.VNNI_256@) / (tma_retiring * tma_info_thread_slots)", + "MetricGroup": "Compute;IntVector;Pipeline;TopdownL4;tma_L4_group;tma_int_operations_group;tma_issue2P", + "MetricName": "tma_int_vector_256b", + "MetricThreshold": "tma_int_vector_256b > 0.1 & (tma_int_operations > 0.1 & tma_light_operations > 0.6)", + "PublicDescription": "This metric represents 256-bit vector Integer ADD/SUB/SAD/MUL or VNNI (Vector Neural Network Instructions) uops fraction the CPU has retired. Related metrics: tma_fp_scalar, tma_fp_vector, tma_fp_vector_128b, tma_fp_vector_256b, tma_fp_vector_512b, tma_int_vector_128b, tma_port_0, tma_port_1, tma_port_5, tma_port_6, tma_ports_utilized_2", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles the CPU was stalled due to Instruction TLB (ITLB) misses", + "MetricExpr": "cpu_core@ICACHE_TAG.STALLS@ / tma_info_thread_clks", + "MetricGroup": "BigFootprint;BvBC;FetchLat;MemoryTLB;TopdownL3;tma_L3_group;tma_fetch_latency_group", + "MetricName": "tma_itlb_misses", + "MetricThreshold": "tma_itlb_misses > 0.05 & (tma_fetch_latency > 0.1 & tma_frontend_bound > 0.15)", + "PublicDescription": "This metric represents fraction of cycles the CPU was stalled due to Instruction TLB (ITLB) misses. Sample with: FRONTEND_RETIRED.STLB_MISS_PS;FRONTEND_RETIRED.ITLB_MISS_PS", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates how often the CPU was stalled without loads missing the L1 data cache", + "MetricExpr": "max((cpu_core@EXE_ACTIVITY.BOUND_ON_LOADS@ - cpu_core@MEMORY_ACTIVITY.STALLS_L1D_MISS@) / tma_info_thread_clks, 0)", + "MetricGroup": "CacheHits;MemoryBound;TmaL3mem;TopdownL3;tma_L3_group;tma_issueL1;tma_issueMC;tma_memory_bound_group", + "MetricName": "tma_l1_bound", + "MetricThreshold": "tma_l1_bound > 0.1 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2)", + "PublicDescription": "This metric estimates how often the CPU was stalled without loads missing the L1 data cache. The L1 data cache typically has the shortest latency. However; in certain cases like loads blocked on older stores; a load might suffer due to high latency even though it is being satisfied by the L1. Another example is loads who miss in the TLB. These cases are characterized by execution unit stalls; while some non-completed demand load lives in the machine without having that demand load missing the L1 cache. Sample with: MEM_LOAD_RETIRED.L1_HIT_PS;MEM_LOAD_RETIRED.FB_HIT_PS. Related metrics: tma_clears_resteers, tma_machine_clears, tma_microcode_sequencer, tma_ms_switches, tma_ports_utilized_1", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric roughly estimates fraction of cycles with demand load accesses that hit the L1 cache", + "MetricExpr": "min(2 * (cpu_core@MEM_INST_RETIRED.ALL_LOADS@ - cpu_core@MEM_LOAD_RETIRED.FB_HIT@ - cpu_core@MEM_LOAD_RETIRED.L1_MISS@) * 20 / 100, max(cpu_core@CYCLE_ACTIVITY.CYCLES_MEM_ANY@ - cpu_core@MEMORY_ACTIVITY.CYCLES_L1D_MISS@, 0)) / tma_info_thread_clks", + "MetricGroup": "BvML;MemoryLat;TopdownL4;tma_L4_group;tma_l1_bound_group", + "MetricName": "tma_l1_hit_latency", + "MetricThreshold": "tma_l1_hit_latency > 0.1 & (tma_l1_bound > 0.1 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric roughly estimates fraction of cycles with demand load accesses that hit the L1 cache. The short latency of the L1 data cache may be exposed in pointer-chasing memory access patterns as an example. Sample with: MEM_LOAD_RETIRED.L1_HIT", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates how often the CPU was stalled due to L2 cache accesses by loads", + "MetricExpr": "(cpu_core@MEMORY_ACTIVITY.STALLS_L1D_MISS@ - cpu_core@MEMORY_ACTIVITY.STALLS_L2_MISS@) / tma_info_thread_clks", + "MetricGroup": "BvML;CacheHits;MemoryBound;TmaL3mem;TopdownL3;tma_L3_group;tma_memory_bound_group", + "MetricName": "tma_l2_bound", + "MetricThreshold": "tma_l2_bound > 0.05 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2)", + "PublicDescription": "This metric estimates how often the CPU was stalled due to L2 cache accesses by loads. Avoiding cache misses (i.e. L1 misses/L2 hits) can improve the latency and increase performance. Sample with: MEM_LOAD_RETIRED.L2_HIT_PS", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates how often the CPU was stalled due to loads accesses to L3 cache or contended with a sibling Core", + "MetricExpr": "(cpu_core@MEMORY_ACTIVITY.STALLS_L2_MISS@ - cpu_core@MEMORY_ACTIVITY.STALLS_L3_MISS@) / tma_info_thread_clks", + "MetricGroup": "CacheHits;MemoryBound;TmaL3mem;TopdownL3;tma_L3_group;tma_memory_bound_group", + "MetricName": "tma_l3_bound", + "MetricThreshold": "tma_l3_bound > 0.05 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2)", + "PublicDescription": "This metric estimates how often the CPU was stalled due to loads accesses to L3 cache or contended with a sibling Core. Avoiding cache misses (i.e. L2 misses/L3 hits) can improve the latency and increase performance. Sample with: MEM_LOAD_RETIRED.L3_HIT_PS", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates fraction of cycles with demand load accesses that hit the L3 cache under unloaded scenarios (possibly L3 latency limited)", + "MetricExpr": "cpu_core@MEM_LOAD_RETIRED.L3_HIT@ * min(cpu_core@MEM_LOAD_RETIRED.L3_HIT@R, 9 * tma_info_system_core_frequency) * (1 + cpu_core@MEM_LOAD_RETIRED.FB_HIT@ / cpu_core@MEM_LOAD_RETIRED.L1_MISS@ / 2) / tma_info_thread_clks", + "MetricGroup": "BvML;MemoryLat;TopdownL4;tma_L4_group;tma_issueLat;tma_l3_bound_group", + "MetricName": "tma_l3_hit_latency", + "MetricThreshold": "tma_l3_hit_latency > 0.1 & (tma_l3_bound > 0.05 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric estimates fraction of cycles with demand load accesses that hit the L3 cache under unloaded scenarios (possibly L3 latency limited). Avoiding private cache misses (i.e. L2 misses/L3 hits) will improve the latency; reduce contention with sibling physical cores and increase performance. Note the value of this node may overlap with its siblings. Sample with: MEM_LOAD_RETIRED.L3_HIT_PS. Related metrics: tma_info_bottleneck_cache_memory_latency, tma_mem_latency", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles CPU was stalled due to Length Changing Prefixes (LCPs)", + "MetricExpr": "cpu_core@DECODE.LCP@ / tma_info_thread_clks", + "MetricGroup": "FetchLat;TopdownL3;tma_L3_group;tma_fetch_latency_group;tma_issueFB", + "MetricName": "tma_lcp", + "MetricThreshold": "tma_lcp > 0.05 & (tma_fetch_latency > 0.1 & tma_frontend_bound > 0.15)", + "PublicDescription": "This metric represents fraction of cycles CPU was stalled due to Length Changing Prefixes (LCPs). Using proper compiler flags or Intel Compiler by default will certainly avoid this. #Link: Optimization Guide about LCP BKMs. Related metrics: tma_dsb_switches, tma_fetch_bandwidth, tma_info_botlnk_l2_dsb_bandwidth, tma_info_botlnk_l2_dsb_misses, tma_info_frontend_dsb_coverage, tma_info_inst_mix_iptb", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots where the CPU was retiring light-weight operations -- instructions that require no more than one uop (micro-operation)", + "MetricExpr": "max(0, tma_retiring - tma_heavy_operations)", + "MetricGroup": "Retire;TmaL2;TopdownL2;tma_L2_group;tma_retiring_group", + "MetricName": "tma_light_operations", + "MetricThreshold": "tma_light_operations > 0.6", + "MetricgroupNoGroup": "TopdownL2", + "PublicDescription": "This metric represents fraction of slots where the CPU was retiring light-weight operations -- instructions that require no more than one uop (micro-operation). This correlates with total number of instructions used by the program. A uops-per-instruction (see UopPI metric) ratio of 1 or less should be expected for decently optimized code running on Intel Core/Xeon products. While this often indicates efficient X86 instructions were executed; high value does not necessarily mean better performance cannot be achieved. ([ICL+] Note this may undercount due to approximation using indirect events; [ADL+] .). Sample with: INST_RETIRED.PREC_DIST", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents Core fraction of cycles CPU dispatched uops on execution port for Load operations", + "MetricExpr": "cpu_core@UOPS_DISPATCHED.PORT_2_3_10@ / (3 * tma_info_core_core_clks)", + "MetricGroup": "TopdownL5;tma_L5_group;tma_ports_utilized_3m_group", + "MetricName": "tma_load_op_utilization", + "MetricThreshold": "tma_load_op_utilization > 0.6", + "PublicDescription": "This metric represents Core fraction of cycles CPU dispatched uops on execution port for Load operations. Sample with: UOPS_DISPATCHED.PORT_2_3_10", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric roughly estimates the fraction of cycles where the (first level) DTLB was missed by load accesses, that later on hit in second-level TLB (STLB)", + "MetricExpr": "max(0, tma_dtlb_load - tma_load_stlb_miss)", + "MetricGroup": "MemoryTLB;TopdownL5;tma_L5_group;tma_dtlb_load_group", + "MetricName": "tma_load_stlb_hit", + "MetricThreshold": "tma_load_stlb_hit > 0.05 & (tma_dtlb_load > 0.1 & (tma_l1_bound > 0.1 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2)))", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates the fraction of cycles where the Second-level TLB (STLB) was missed by load accesses, performing a hardware page walk", + "MetricExpr": "cpu_core@DTLB_LOAD_MISSES.WALK_ACTIVE@ / tma_info_thread_clks", + "MetricGroup": "MemoryTLB;TopdownL5;tma_L5_group;tma_dtlb_load_group", + "MetricName": "tma_load_stlb_miss", + "MetricThreshold": "tma_load_stlb_miss > 0.05 & (tma_dtlb_load > 0.1 & (tma_l1_bound > 0.1 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2)))", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles the CPU spent handling cache misses due to lock operations", + "MetricExpr": "cpu_core@MEM_INST_RETIRED.LOCK_LOADS@ * cpu_core@MEM_INST_RETIRED.LOCK_LOADS@R / tma_info_thread_clks", + "MetricGroup": "Offcore;TopdownL4;tma_L4_group;tma_issueRFO;tma_l1_bound_group", + "MetricName": "tma_lock_latency", + "MetricThreshold": "tma_lock_latency > 0.2 & (tma_l1_bound > 0.1 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric represents fraction of cycles the CPU spent handling cache misses due to lock operations. Due to the microarchitecture handling of locks; they are classified as L1_Bound regardless of what memory source satisfied them. Sample with: MEM_INST_RETIRED.LOCK_LOADS. Related metrics: tma_store_latency", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents Core fraction of cycles in which CPU was likely limited due to LSD (Loop Stream Detector) unit", + "MetricExpr": "(cpu_core@LSD.CYCLES_ACTIVE@ - cpu_core@LSD.CYCLES_OK@) / tma_info_core_core_clks / 2", + "MetricGroup": "FetchBW;LSD;TopdownL3;tma_L3_group;tma_fetch_bandwidth_group", + "MetricName": "tma_lsd", + "MetricThreshold": "tma_lsd > 0.15 & tma_fetch_bandwidth > 0.2", + "PublicDescription": "This metric represents Core fraction of cycles in which CPU was likely limited due to LSD (Loop Stream Detector) unit. LSD typically does well sustaining Uop supply. However; in some rare cases; optimal uop-delivery could not be reached for small loops whose size (in terms of number of uops) does not suit well the LSD structure.", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots the CPU has wasted due to Machine Clears", + "MetricExpr": "max(0, tma_bad_speculation - tma_branch_mispredicts)", + "MetricGroup": "BadSpec;BvMS;MachineClears;TmaL2;TopdownL2;tma_L2_group;tma_bad_speculation_group;tma_issueMC;tma_issueSyncxn", + "MetricName": "tma_machine_clears", + "MetricThreshold": "tma_machine_clears > 0.1 & tma_bad_speculation > 0.15", + "MetricgroupNoGroup": "TopdownL2", + "PublicDescription": "This metric represents fraction of slots the CPU has wasted due to Machine Clears. These slots are either wasted by uops fetched prior to the clear; or stalls the out-of-order portion of the machine needs to recover its state after the clear. For example; this can happen due to memory ordering Nukes (e.g. Memory Disambiguation) or Self-Modifying-Code (SMC) nukes. Sample with: MACHINE_CLEARS.COUNT. Related metrics: tma_clears_resteers, tma_contested_accesses, tma_data_sharing, tma_false_sharing, tma_l1_bound, tma_microcode_sequencer, tma_ms_switches, tma_remote_cache", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates fraction of cycles where the core's performance was likely hurt due to approaching bandwidth limits of external memory - DRAM ([SPR-HBM] and/or HBM)", + "MetricExpr": "min(cpu_core@CPU_CLK_UNHALTED.THREAD@, cpu_core@OFFCORE_REQUESTS_OUTSTANDING.DATA_RD\\,cmask\\=4@) / tma_info_thread_clks", + "MetricGroup": "BvMS;MemoryBW;Offcore;TopdownL4;tma_L4_group;tma_dram_bound_group;tma_issueBW", + "MetricName": "tma_mem_bandwidth", + "MetricThreshold": "tma_mem_bandwidth > 0.2 & (tma_dram_bound > 0.1 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric estimates fraction of cycles where the core's performance was likely hurt due to approaching bandwidth limits of external memory - DRAM ([SPR-HBM] and/or HBM). The underlying heuristic assumes that a similar off-core traffic is generated by all IA cores. This metric does not aggregate non-data-read requests by this logical processor; requests from other IA Logical Processors/Physical Cores/sockets; or other non-IA devices like GPU; hence the maximum external memory bandwidth limits may or may not be approached when this metric is flagged (see Uncore counters for that). Related metrics: tma_fb_full, tma_info_bottleneck_cache_memory_bandwidth, tma_info_system_dram_bw_use, tma_sq_full", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates fraction of cycles where the performance was likely hurt due to latency from external memory - DRAM ([SPR-HBM] and/or HBM)", + "MetricExpr": "min(cpu_core@CPU_CLK_UNHALTED.THREAD@, cpu_core@OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DATA_RD@) / tma_info_thread_clks - tma_mem_bandwidth", + "MetricGroup": "BvML;MemoryLat;Offcore;TopdownL4;tma_L4_group;tma_dram_bound_group;tma_issueLat", + "MetricName": "tma_mem_latency", + "MetricThreshold": "tma_mem_latency > 0.1 & (tma_dram_bound > 0.1 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric estimates fraction of cycles where the performance was likely hurt due to latency from external memory - DRAM ([SPR-HBM] and/or HBM). This metric does not aggregate requests from other Logical Processors/Physical Cores/sockets (see Uncore counters for that). Related metrics: tma_info_bottleneck_cache_memory_latency, tma_l3_hit_latency", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots the Memory subsystem within the Backend was a bottleneck", + "MetricExpr": "cpu_core@topdown\\-mem\\-bound@ / (cpu_core@topdown\\-fe\\-bound@ + cpu_core@topdown\\-bad\\-spec@ + cpu_core@topdown\\-retiring@ + cpu_core@topdown\\-be\\-bound@) + 0 * tma_info_thread_slots", + "MetricGroup": "Backend;TmaL2;TopdownL2;tma_L2_group;tma_backend_bound_group", + "MetricName": "tma_memory_bound", + "MetricThreshold": "tma_memory_bound > 0.2 & tma_backend_bound > 0.2", + "MetricgroupNoGroup": "TopdownL2", + "PublicDescription": "This metric represents fraction of slots the Memory subsystem within the Backend was a bottleneck. Memory Bound estimates fraction of slots where pipeline is likely stalled due to demand load or store instructions. This accounts mainly for (1) non-completed in-flight memory demand loads which coincides with execution units starvation; in addition to (2) cases where stores could impose backpressure on the pipeline when many of them get buffered at the same time (less common out of the two).", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles the CPU was stalled due to LFENCE Instructions.", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", + "MetricExpr": "13 * cpu_core@MISC2_RETIRED.LFENCE@ / tma_info_thread_clks", + "MetricGroup": "TopdownL4;tma_L4_group;tma_serializing_operation_group", + "MetricName": "tma_memory_fence", + "MetricThreshold": "tma_memory_fence > 0.05 & (tma_serializing_operation > 0.1 & (tma_core_bound > 0.1 & tma_backend_bound > 0.2))", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots where the CPU was retiring memory operations -- uops for memory load or store accesses.", + "MetricExpr": "tma_light_operations * cpu_core@MEM_UOP_RETIRED.ANY@ / (tma_retiring * tma_info_thread_slots)", + "MetricGroup": "Pipeline;TopdownL3;tma_L3_group;tma_light_operations_group", + "MetricName": "tma_memory_operations", + "MetricThreshold": "tma_memory_operations > 0.1 & tma_light_operations > 0.6", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots the CPU was retiring uops fetched by the Microcode Sequencer (MS) unit", + "MetricExpr": "cpu_core@UOPS_RETIRED.MS@ / tma_info_thread_slots", + "MetricGroup": "MicroSeq;TopdownL3;tma_L3_group;tma_heavy_operations_group;tma_issueMC;tma_issueMS", + "MetricName": "tma_microcode_sequencer", + "MetricThreshold": "tma_microcode_sequencer > 0.05 & tma_heavy_operations > 0.1", + "PublicDescription": "This metric represents fraction of slots the CPU was retiring uops fetched by the Microcode Sequencer (MS) unit. The MS is used for CISC instructions not supported by the default decoders (like repeat move strings; or CPUID); or by microcode assists used to address some operation modes (like in Floating Point assists). These cases can often be avoided. Sample with: UOPS_RETIRED.MS. Related metrics: tma_clears_resteers, tma_info_bottleneck_irregular_overhead, tma_l1_bound, tma_machine_clears, tma_ms_switches", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles the CPU was stalled due to Branch Resteers as a result of Branch Misprediction at execution stage", + "MetricExpr": "tma_branch_mispredicts / tma_bad_speculation * cpu_core@INT_MISC.CLEAR_RESTEER_CYCLES@ / tma_info_thread_clks", + "MetricGroup": "BadSpec;BrMispredicts;BvMP;TopdownL4;tma_L4_group;tma_branch_resteers_group;tma_issueBM", + "MetricName": "tma_mispredicts_resteers", + "MetricThreshold": "tma_mispredicts_resteers > 0.05 & (tma_branch_resteers > 0.05 & (tma_fetch_latency > 0.1 & tma_frontend_bound > 0.15))", + "PublicDescription": "This metric represents fraction of cycles the CPU was stalled due to Branch Resteers as a result of Branch Misprediction at execution stage. Sample with: INT_MISC.CLEAR_RESTEER_CYCLES. Related metrics: tma_branch_mispredicts, tma_info_bad_spec_branch_misprediction_cost, tma_info_bottleneck_mispredictions", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents Core fraction of cycles in which CPU was likely limited due to the MITE pipeline (the legacy decode pipeline)", + "MetricExpr": "(cpu_core@IDQ.MITE_CYCLES_ANY@ - cpu_core@IDQ.MITE_CYCLES_OK@) / tma_info_core_core_clks / 2", + "MetricGroup": "DSBmiss;FetchBW;TopdownL3;tma_L3_group;tma_fetch_bandwidth_group", + "MetricName": "tma_mite", + "MetricThreshold": "tma_mite > 0.1 & tma_fetch_bandwidth > 0.2", + "PublicDescription": "This metric represents Core fraction of cycles in which CPU was likely limited due to the MITE pipeline (the legacy decode pipeline). This pipeline is used for code that was not pre-cached in the DSB or LSD. For example; inefficiencies due to asymmetric decoders; use of long immediate or LCP can manifest as MITE fetch bandwidth bottleneck. Sample with: FRONTEND_RETIRED.ANY_DSB_MISS", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates penalty in terms of percentage of([SKL+] injected blend uops out of all Uops Issued -- the Count Domain; [ADL+] cycles)", + "MetricExpr": "160 * cpu_core@ASSISTS.SSE_AVX_MIX@ / tma_info_thread_clks", + "MetricGroup": "TopdownL5;tma_L5_group;tma_issueMV;tma_ports_utilized_0_group", + "MetricName": "tma_mixing_vectors", + "MetricThreshold": "tma_mixing_vectors > 0.05", + "PublicDescription": "This metric estimates penalty in terms of percentage of([SKL+] injected blend uops out of all Uops Issued -- the Count Domain; [ADL+] cycles). Usually a Mixing_Vectors over 5% is worth investigating. Read more in Appendix B1 of the Optimizations Guide for this topic. Related metrics: tma_ms_switches", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates the fraction of cycles when the CPU was stalled due to switches of uop delivery to the Microcode Sequencer (MS)", + "MetricExpr": "3 * cpu_core@UOPS_RETIRED.MS\\,cmask\\=1\\,edge@ / (cpu_core@UOPS_RETIRED.SLOTS@ / cpu_core@UOPS_ISSUED.ANY@) / tma_info_thread_clks", + "MetricGroup": "FetchLat;MicroSeq;TopdownL3;tma_L3_group;tma_fetch_latency_group;tma_issueMC;tma_issueMS;tma_issueMV;tma_issueSO", + "MetricName": "tma_ms_switches", + "MetricThreshold": "tma_ms_switches > 0.05 & (tma_fetch_latency > 0.1 & tma_frontend_bound > 0.15)", + "PublicDescription": "This metric estimates the fraction of cycles when the CPU was stalled due to switches of uop delivery to the Microcode Sequencer (MS). Commonly used instructions are optimized for delivery by the DSB (decoded i-cache) or MITE (legacy instruction decode) pipelines. Certain operations cannot be handled natively by the execution pipeline; and must be performed by microcode (small programs injected into the execution stream). Switching to the MS too often can negatively impact performance. The MS is designated to deliver long uop flows required by CISC instructions like CPUID; or uncommon conditions like Floating Point Assists when dealing with Denormals. Sample with: FRONTEND_RETIRED.MS_FLOWS. Related metrics: tma_clears_resteers, tma_info_bottleneck_irregular_overhead, tma_l1_bound, tma_machine_clears, tma_microcode_sequencer, tma_mixing_vectors, tma_serializing_operation", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots where the CPU was retiring branch instructions that were not fused", + "MetricExpr": "tma_light_operations * (cpu_core@BR_INST_RETIRED.ALL_BRANCHES@ - cpu_core@INST_RETIRED.MACRO_FUSED@) / (tma_retiring * tma_info_thread_slots)", + "MetricGroup": "Branches;BvBO;Pipeline;TopdownL3;tma_L3_group;tma_light_operations_group", + "MetricName": "tma_non_fused_branches", + "MetricThreshold": "tma_non_fused_branches > 0.1 & tma_light_operations > 0.6", + "PublicDescription": "This metric represents fraction of slots where the CPU was retiring branch instructions that were not fused. Non-conditional branches like direct JMP or CALL would count here. Can be used to examine fusible conditional jumps that were not fused.", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots where the CPU was retiring NOP (no op) instructions", + "MetricExpr": "tma_light_operations * cpu_core@INST_RETIRED.NOP@ / (tma_retiring * tma_info_thread_slots)", + "MetricGroup": "BvBO;Pipeline;TopdownL4;tma_L4_group;tma_other_light_ops_group", + "MetricName": "tma_nop_instructions", + "MetricThreshold": "tma_nop_instructions > 0.1 & (tma_other_light_ops > 0.3 & tma_light_operations > 0.6)", + "PublicDescription": "This metric represents fraction of slots where the CPU was retiring NOP (no op) instructions. Compilers often use NOPs for certain address alignments - e.g. start address of a function or loop body. Sample with: INST_RETIRED.NOP", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents the remaining light uops fraction the CPU has executed - remaining means not covered by other sibling nodes", + "MetricExpr": "max(0, tma_light_operations - (tma_fp_arith + tma_int_operations + tma_memory_operations + tma_fused_instructions + tma_non_fused_branches))", + "MetricGroup": "Pipeline;TopdownL3;tma_L3_group;tma_light_operations_group", + "MetricName": "tma_other_light_ops", + "MetricThreshold": "tma_other_light_ops > 0.3 & tma_light_operations > 0.6", + "PublicDescription": "This metric represents the remaining light uops fraction the CPU has executed - remaining means not covered by other sibling nodes. May undercount due to FMA double counting", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates fraction of slots the CPU was stalled due to other cases of misprediction (non-retired x86 branches or other types).", + "MetricExpr": "max(tma_branch_mispredicts * (1 - cpu_core@BR_MISP_RETIRED.ALL_BRANCHES@ / (cpu_core@INT_MISC.CLEARS_COUNT@ - cpu_core@MACHINE_CLEARS.COUNT@)), 0.0001)", + "MetricGroup": "BrMispredicts;BvIO;TopdownL3;tma_L3_group;tma_branch_mispredicts_group", + "MetricName": "tma_other_mispredicts", + "MetricThreshold": "tma_other_mispredicts > 0.05 & (tma_branch_mispredicts > 0.1 & tma_bad_speculation > 0.15)", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots the CPU has wasted due to Nukes (Machine Clears) not related to memory ordering.", + "MetricExpr": "max(tma_machine_clears * (1 - cpu_core@MACHINE_CLEARS.MEMORY_ORDERING@ / cpu_core@MACHINE_CLEARS.COUNT@), 0.0001)", + "MetricGroup": "BvIO;Machine_Clears;TopdownL3;tma_L3_group;tma_machine_clears_group", + "MetricName": "tma_other_nukes", + "MetricThreshold": "tma_other_nukes > 0.05 & (tma_machine_clears > 0.1 & tma_bad_speculation > 0.15)", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric roughly estimates fraction of slots the CPU retired uops as a result of handing Page Faults", + "MetricExpr": "99 * cpu_core@ASSISTS.PAGE_FAULT@ / tma_info_thread_slots", + "MetricGroup": "TopdownL5;tma_L5_group;tma_assists_group", + "MetricName": "tma_page_faults", + "MetricThreshold": "tma_page_faults > 0.05", + "PublicDescription": "This metric roughly estimates fraction of slots the CPU retired uops as a result of handing Page Faults. A Page Fault may apply on first application access to a memory page. Note operating system handling of page faults accounts for the majority of its cost.", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents Core fraction of cycles CPU dispatched uops on execution port 0 ([SNB+] ALU; [HSW+] ALU and 2nd branch)", + "MetricExpr": "cpu_core@UOPS_DISPATCHED.PORT_0@ / tma_info_core_core_clks", + "MetricGroup": "Compute;TopdownL6;tma_L6_group;tma_alu_op_utilization_group;tma_issue2P", + "MetricName": "tma_port_0", + "MetricThreshold": "tma_port_0 > 0.6", + "PublicDescription": "This metric represents Core fraction of cycles CPU dispatched uops on execution port 0 ([SNB+] ALU; [HSW+] ALU and 2nd branch). Sample with: UOPS_DISPATCHED.PORT_0. Related metrics: tma_fp_scalar, tma_fp_vector, tma_fp_vector_128b, tma_fp_vector_256b, tma_fp_vector_512b, tma_int_vector_128b, tma_int_vector_256b, tma_port_1, tma_port_5, tma_port_6, tma_ports_utilized_2", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents Core fraction of cycles CPU dispatched uops on execution port 1 (ALU)", + "MetricExpr": "cpu_core@UOPS_DISPATCHED.PORT_1@ / tma_info_core_core_clks", + "MetricGroup": "TopdownL6;tma_L6_group;tma_alu_op_utilization_group;tma_issue2P", + "MetricName": "tma_port_1", + "MetricThreshold": "tma_port_1 > 0.6", + "PublicDescription": "This metric represents Core fraction of cycles CPU dispatched uops on execution port 1 (ALU). Sample with: UOPS_DISPATCHED.PORT_1. Related metrics: tma_fp_scalar, tma_fp_vector, tma_fp_vector_128b, tma_fp_vector_256b, tma_fp_vector_512b, tma_int_vector_128b, tma_int_vector_256b, tma_port_0, tma_port_5, tma_port_6, tma_ports_utilized_2", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents Core fraction of cycles CPU dispatched uops on execution port 6 ([HSW+] Primary Branch and simple ALU)", + "MetricExpr": "cpu_core@UOPS_DISPATCHED.PORT_6@ / tma_info_core_core_clks", + "MetricGroup": "TopdownL6;tma_L6_group;tma_alu_op_utilization_group;tma_issue2P", + "MetricName": "tma_port_6", + "MetricThreshold": "tma_port_6 > 0.6", + "PublicDescription": "This metric represents Core fraction of cycles CPU dispatched uops on execution port 6 ([HSW+] Primary Branch and simple ALU). Sample with: UOPS_DISPATCHED.PORT_6. Related metrics: tma_fp_scalar, tma_fp_vector, tma_fp_vector_128b, tma_fp_vector_256b, tma_fp_vector_512b, tma_int_vector_128b, tma_int_vector_256b, tma_port_0, tma_port_1, tma_port_5, tma_ports_utilized_2", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates fraction of cycles the CPU performance was potentially limited due to Core computation issues (non divider-related)", + "MetricExpr": "((tma_ports_utilized_0 * tma_info_thread_clks + (cpu_core@EXE_ACTIVITY.1_PORTS_UTIL@ + tma_retiring * cpu_core@EXE_ACTIVITY.2_PORTS_UTIL\\,umask\\=0xc@)) / tma_info_thread_clks if cpu_core@ARITH.DIV_ACTIVE@ < cpu_core@CYCLE_ACTIVITY.STALLS_TOTAL@ - cpu_core@EXE_ACTIVITY.BOUND_ON_LOADS@ else (cpu_core@EXE_ACTIVITY.1_PORTS_UTIL@ + tma_retiring * cpu_core@EXE_ACTIVITY.2_PORTS_UTIL\\,umask\\=0xc@) / tma_info_thread_clks)", + "MetricGroup": "PortsUtil;TopdownL3;tma_L3_group;tma_core_bound_group", + "MetricName": "tma_ports_utilization", + "MetricThreshold": "tma_ports_utilization > 0.15 & (tma_core_bound > 0.1 & tma_backend_bound > 0.2)", + "PublicDescription": "This metric estimates fraction of cycles the CPU performance was potentially limited due to Core computation issues (non divider-related). Two distinct categories can be attributed into this metric: (1) heavy data-dependency among contiguous instructions would manifest in this metric - such cases are often referred to as low Instruction Level Parallelism (ILP). (2) Contention on some hardware execution unit other than Divider. For example; when there are too many multiply operations.", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles CPU executed no uops on any execution port (Logical Processor cycles since ICL, Physical Core cycles otherwise)", + "MetricExpr": "max((cpu_core@EXE_ACTIVITY.EXE_BOUND_0_PORTS@ + max(cpu_core@RS.EMPTY_RESOURCE@ - cpu_core@RESOURCE_STALLS.SCOREBOARD@, 0)) / tma_info_thread_clks, 1) * (cpu_core@CYCLE_ACTIVITY.STALLS_TOTAL@ - cpu_core@EXE_ACTIVITY.BOUND_ON_LOADS@) / tma_info_thread_clks", + "MetricGroup": "PortsUtil;TopdownL4;tma_L4_group;tma_ports_utilization_group", + "MetricName": "tma_ports_utilized_0", + "MetricThreshold": "tma_ports_utilized_0 > 0.2 & (tma_ports_utilization > 0.15 & (tma_core_bound > 0.1 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric represents fraction of cycles CPU executed no uops on any execution port (Logical Processor cycles since ICL, Physical Core cycles otherwise). Long-latency instructions like divides may contribute to this metric.", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles where the CPU executed total of 1 uop per cycle on all execution ports (Logical Processor cycles since ICL, Physical Core cycles otherwise)", + "MetricExpr": "cpu_core@EXE_ACTIVITY.1_PORTS_UTIL@ / tma_info_thread_clks", + "MetricGroup": "PortsUtil;TopdownL4;tma_L4_group;tma_issueL1;tma_ports_utilization_group", + "MetricName": "tma_ports_utilized_1", + "MetricThreshold": "tma_ports_utilized_1 > 0.2 & (tma_ports_utilization > 0.15 & (tma_core_bound > 0.1 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric represents fraction of cycles where the CPU executed total of 1 uop per cycle on all execution ports (Logical Processor cycles since ICL, Physical Core cycles otherwise). This can be due to heavy data-dependency among software instructions; or over oversubscribing a particular hardware resource. In some other cases with high 1_Port_Utilized and L1_Bound; this metric can point to L1 data-cache latency bottleneck that may not necessarily manifest with complete execution starvation (due to the short L1 latency e.g. walking a linked list) - looking at the assembly can be helpful. Sample with: EXE_ACTIVITY.1_PORTS_UTIL. Related metrics: tma_l1_bound", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles CPU executed total of 2 uops per cycle on all execution ports (Logical Processor cycles since ICL, Physical Core cycles otherwise)", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", + "MetricExpr": "cpu_core@EXE_ACTIVITY.2_PORTS_UTIL@ / tma_info_thread_clks", + "MetricGroup": "PortsUtil;TopdownL4;tma_L4_group;tma_issue2P;tma_ports_utilization_group", + "MetricName": "tma_ports_utilized_2", + "MetricThreshold": "tma_ports_utilized_2 > 0.15 & (tma_ports_utilization > 0.15 & (tma_core_bound > 0.1 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric represents fraction of cycles CPU executed total of 2 uops per cycle on all execution ports (Logical Processor cycles since ICL, Physical Core cycles otherwise). Loop Vectorization -most compilers feature auto-Vectorization options today- reduces pressure on the execution ports as multiple elements are calculated with same uop. Sample with: EXE_ACTIVITY.2_PORTS_UTIL. Related metrics: tma_fp_scalar, tma_fp_vector, tma_fp_vector_128b, tma_fp_vector_256b, tma_fp_vector_512b, tma_int_vector_128b, tma_int_vector_256b, tma_port_0, tma_port_1, tma_port_5, tma_port_6", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles CPU executed total of 3 or more uops per cycle on all execution ports (Logical Processor cycles since ICL, Physical Core cycles otherwise)", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", + "MetricExpr": "cpu_core@UOPS_EXECUTED.CYCLES_GE_3@ / tma_info_thread_clks", + "MetricGroup": "BvCB;PortsUtil;TopdownL4;tma_L4_group;tma_ports_utilization_group", + "MetricName": "tma_ports_utilized_3m", + "MetricThreshold": "tma_ports_utilized_3m > 0.4 & (tma_ports_utilization > 0.15 & (tma_core_bound > 0.1 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric represents fraction of cycles CPU executed total of 3 or more uops per cycle on all execution ports (Logical Processor cycles since ICL, Physical Core cycles otherwise). Sample with: UOPS_EXECUTED.CYCLES_GE_3", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This category represents fraction of slots utilized by useful work i.e. issued uops that eventually get retired", + "MetricExpr": "cpu_core@topdown\\-retiring@ / (cpu_core@topdown\\-fe\\-bound@ + cpu_core@topdown\\-bad\\-spec@ + cpu_core@topdown\\-retiring@ + cpu_core@topdown\\-be\\-bound@) + 0 * tma_info_thread_slots", + "MetricGroup": "BvUW;TmaL1;TopdownL1;tma_L1_group", + "MetricName": "tma_retiring", + "MetricThreshold": "tma_retiring > 0.7 | tma_heavy_operations > 0.1", + "MetricgroupNoGroup": "TopdownL1", + "PublicDescription": "This category represents fraction of slots utilized by useful work i.e. issued uops that eventually get retired. Ideally; all pipeline slots would be attributed to the Retiring category. Retiring of 100% would indicate the maximum Pipeline_Width throughput was achieved. Maximizing Retiring typically increases the Instructions-per-cycle (see IPC metric). Note that a high Retiring value does not necessary mean there is no room for more performance. For example; Heavy-operations or Microcode Assists are categorized under Retiring. They often indicate suboptimal performance and can often be optimized or avoided. Sample with: UOPS_RETIRED.SLOTS", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles the CPU issue-pipeline was stalled due to serializing operations", + "MetricExpr": "cpu_core@RESOURCE_STALLS.SCOREBOARD@ / tma_info_thread_clks + tma_c02_wait", + "MetricGroup": "BvIO;PortsUtil;TopdownL3;tma_L3_group;tma_core_bound_group;tma_issueSO", + "MetricName": "tma_serializing_operation", + "MetricThreshold": "tma_serializing_operation > 0.1 & (tma_core_bound > 0.1 & tma_backend_bound > 0.2)", + "PublicDescription": "This metric represents fraction of cycles the CPU issue-pipeline was stalled due to serializing operations. Instructions like CPUID; WRMSR or LFENCE serialize the out-of-order execution which may limit performance. Sample with: RESOURCE_STALLS.SCOREBOARD. Related metrics: tma_ms_switches", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of slots where the CPU was retiring Shuffle operations of 256-bit vector size (FP or Integer)", + "MetricExpr": "tma_light_operations * cpu_core@INT_VEC_RETIRED.SHUFFLES@ / (tma_retiring * tma_info_thread_slots)", + "MetricGroup": "HPC;Pipeline;TopdownL4;tma_L4_group;tma_other_light_ops_group", + "MetricName": "tma_shuffles_256b", + "MetricThreshold": "tma_shuffles_256b > 0.1 & (tma_other_light_ops > 0.3 & tma_light_operations > 0.6)", + "PublicDescription": "This metric represents fraction of slots where the CPU was retiring Shuffle operations of 256-bit vector size (FP or Integer). Shuffles may incur slow cross \"vector lane\" data transfers.", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles the CPU was stalled due to PAUSE Instructions", + "MetricConstraint": "NO_GROUP_EVENTS_NMI", + "MetricExpr": "cpu_core@CPU_CLK_UNHALTED.PAUSE@ / tma_info_thread_clks", + "MetricGroup": "TopdownL4;tma_L4_group;tma_serializing_operation_group", + "MetricName": "tma_slow_pause", + "MetricThreshold": "tma_slow_pause > 0.05 & (tma_serializing_operation > 0.1 & (tma_core_bound > 0.1 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric represents fraction of cycles the CPU was stalled due to PAUSE Instructions. Sample with: CPU_CLK_UNHALTED.PAUSE_INST", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates fraction of cycles handling memory load split accesses - load that cross 64-byte cache line boundary", + "MetricExpr": "cpu_core@MEM_INST_RETIRED.SPLIT_LOADS@ * min(cpu_core@MEM_INST_RETIRED.SPLIT_LOADS@R, tma_info_memory_load_miss_real_latency) / tma_info_thread_clks", + "MetricGroup": "TopdownL4;tma_L4_group;tma_l1_bound_group", + "MetricName": "tma_split_loads", + "MetricThreshold": "tma_split_loads > 0.2 & (tma_l1_bound > 0.1 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric estimates fraction of cycles handling memory load split accesses - load that cross 64-byte cache line boundary. Sample with: MEM_INST_RETIRED.SPLIT_LOADS_PS", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents rate of split store accesses", + "MetricExpr": "cpu_core@MEM_INST_RETIRED.SPLIT_STORES@ * min(cpu_core@MEM_INST_RETIRED.SPLIT_STORES@R, 1) / tma_info_thread_clks", + "MetricGroup": "TopdownL4;tma_L4_group;tma_issueSpSt;tma_store_bound_group", + "MetricName": "tma_split_stores", + "MetricThreshold": "tma_split_stores > 0.2 & (tma_store_bound > 0.2 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric represents rate of split store accesses. Consider aligning your data to the 64-byte cache line granularity. Sample with: MEM_INST_RETIRED.SPLIT_STORES_PS. Related metrics: tma_port_4", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric measures fraction of cycles where the Super Queue (SQ) was full taking into account all request-types and both hardware SMT threads (Logical Processors)", + "MetricExpr": "(cpu_core@XQ.FULL_CYCLES@ + cpu_core@L1D_PEND_MISS.L2_STALLS@) / tma_info_thread_clks", + "MetricGroup": "BvMS;MemoryBW;Offcore;TopdownL4;tma_L4_group;tma_issueBW;tma_l3_bound_group", + "MetricName": "tma_sq_full", + "MetricThreshold": "tma_sq_full > 0.3 & (tma_l3_bound > 0.05 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric measures fraction of cycles where the Super Queue (SQ) was full taking into account all request-types and both hardware SMT threads (Logical Processors). Related metrics: tma_fb_full, tma_info_bottleneck_cache_memory_bandwidth, tma_info_system_dram_bw_use, tma_mem_bandwidth", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates how often CPU was stalled due to RFO store memory accesses; RFO store issue a read-for-ownership request before the write", + "MetricExpr": "cpu_core@EXE_ACTIVITY.BOUND_ON_STORES@ / tma_info_thread_clks", + "MetricGroup": "MemoryBound;TmaL3mem;TopdownL3;tma_L3_group;tma_memory_bound_group", + "MetricName": "tma_store_bound", + "MetricThreshold": "tma_store_bound > 0.2 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2)", + "PublicDescription": "This metric estimates how often CPU was stalled due to RFO store memory accesses; RFO store issue a read-for-ownership request before the write. Even though store accesses do not typically stall out-of-order CPUs; there are few cases where stores can lead to actual stalls. This metric will be flagged should RFO stores be a bottleneck. Sample with: MEM_INST_RETIRED.ALL_STORES_PS", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric roughly estimates fraction of cycles when the memory subsystem had loads blocked since they could not forward data from earlier (in program order) overlapping stores", + "MetricExpr": "13 * cpu_core@LD_BLOCKS.STORE_FORWARD@ / tma_info_thread_clks", + "MetricGroup": "TopdownL4;tma_L4_group;tma_l1_bound_group", + "MetricName": "tma_store_fwd_blk", + "MetricThreshold": "tma_store_fwd_blk > 0.1 & (tma_l1_bound > 0.1 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric roughly estimates fraction of cycles when the memory subsystem had loads blocked since they could not forward data from earlier (in program order) overlapping stores. To streamline memory operations in the pipeline; a load can avoid waiting for memory if a prior in-flight store is writing the data that the load wants to read (store forwarding process). However; in some cases the load may be blocked for a significant time pending the store forward. For example; when the prior store is writing a smaller region than the load is reading.", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates fraction of cycles the CPU spent handling L1D store misses", + "MetricExpr": "(cpu_core@MEM_STORE_RETIRED.L2_HIT@ * 10 * (1 - cpu_core@MEM_INST_RETIRED.LOCK_LOADS@ / cpu_core@MEM_INST_RETIRED.ALL_STORES@) + (1 - cpu_core@MEM_INST_RETIRED.LOCK_LOADS@ / cpu_core@MEM_INST_RETIRED.ALL_STORES@) * min(cpu_core@CPU_CLK_UNHALTED.THREAD@, cpu_core@OFFCORE_REQUESTS_OUTSTANDING.CYCLES_WITH_DEMAND_RFO@)) / tma_info_thread_clks", + "MetricGroup": "BvML;MemoryLat;Offcore;TopdownL4;tma_L4_group;tma_issueRFO;tma_issueSL;tma_store_bound_group", + "MetricName": "tma_store_latency", + "MetricThreshold": "tma_store_latency > 0.1 & (tma_store_bound > 0.2 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric estimates fraction of cycles the CPU spent handling L1D store misses. Store accesses usually less impact out-of-order core performance; however; holding resources for longer time can lead into undesired implications (e.g. contention on L1D fill-buffer entries - see FB_Full). Related metrics: tma_fb_full, tma_lock_latency", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents Core fraction of cycles CPU dispatched uops on execution port for Store operations", + "MetricExpr": "(cpu_core@UOPS_DISPATCHED.PORT_4_9@ + cpu_core@UOPS_DISPATCHED.PORT_7_8@) / (4 * tma_info_core_core_clks)", + "MetricGroup": "TopdownL5;tma_L5_group;tma_ports_utilized_3m_group", + "MetricName": "tma_store_op_utilization", + "MetricThreshold": "tma_store_op_utilization > 0.6", + "PublicDescription": "This metric represents Core fraction of cycles CPU dispatched uops on execution port for Store operations. Sample with: UOPS_DISPATCHED.PORT_7_8", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric roughly estimates the fraction of cycles where the TLB was missed by store accesses, hitting in the second-level TLB (STLB)", + "MetricExpr": "max(0, tma_dtlb_store - tma_store_stlb_miss)", + "MetricGroup": "MemoryTLB;TopdownL5;tma_L5_group;tma_dtlb_store_group", + "MetricName": "tma_store_stlb_hit", + "MetricThreshold": "tma_store_stlb_hit > 0.05 & (tma_dtlb_store > 0.05 & (tma_store_bound > 0.2 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2)))", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates the fraction of cycles where the STLB was missed by store accesses, performing a hardware page walk", + "MetricExpr": "cpu_core@DTLB_STORE_MISSES.WALK_ACTIVE@ / tma_info_core_core_clks", + "MetricGroup": "MemoryTLB;TopdownL5;tma_L5_group;tma_dtlb_store_group", + "MetricName": "tma_store_stlb_miss", + "MetricThreshold": "tma_store_stlb_miss > 0.05 & (tma_dtlb_store > 0.05 & (tma_store_bound > 0.2 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2)))", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric estimates how often CPU was stalled due to Streaming store memory accesses; Streaming store optimize out a read request required by RFO stores", + "MetricExpr": "9 * cpu_core@OCR.STREAMING_WR.ANY_RESPONSE@ / tma_info_thread_clks", + "MetricGroup": "MemoryBW;Offcore;TopdownL4;tma_L4_group;tma_issueSmSt;tma_store_bound_group", + "MetricName": "tma_streaming_stores", + "MetricThreshold": "tma_streaming_stores > 0.2 & (tma_store_bound > 0.2 & (tma_memory_bound > 0.2 & tma_backend_bound > 0.2))", + "PublicDescription": "This metric estimates how often CPU was stalled due to Streaming store memory accesses; Streaming store optimize out a read request required by RFO stores. Even though store accesses do not typically stall out-of-order CPUs; there are few cases where stores can lead to actual stalls. This metric will be flagged should Streaming stores be a bottleneck. Sample with: OCR.STREAMING_WR.ANY_RESPONSE. Related metrics: tma_fb_full", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric represents fraction of cycles the CPU was stalled due to new branch address clears", + "MetricExpr": "cpu_core@INT_MISC.UNKNOWN_BRANCH_CYCLES@ / tma_info_thread_clks", + "MetricGroup": "BigFootprint;BvBC;FetchLat;TopdownL4;tma_L4_group;tma_branch_resteers_group", + "MetricName": "tma_unknown_branches", + "MetricThreshold": "tma_unknown_branches > 0.05 & (tma_branch_resteers > 0.05 & (tma_fetch_latency > 0.1 & tma_frontend_bound > 0.15))", + "PublicDescription": "This metric represents fraction of cycles the CPU was stalled due to new branch address clears. These are fetched branches the Branch Prediction Unit was unable to recognize (e.g. first time the branch is fetched or hitting BTB capacity limit) hence called Unknown Branches. Sample with: FRONTEND_RETIRED.UNKNOWN_BRANCH", + "ScaleUnit": "100%", + "Unit": "cpu_core" + }, + { + "BriefDescription": "This metric serves as an approximation of legacy x87 usage", + "MetricExpr": "tma_retiring * cpu_core@UOPS_EXECUTED.X87@ / cpu_core@UOPS_EXECUTED.THREAD@", + "MetricGroup": "Compute;TopdownL4;tma_L4_group;tma_fp_arith_group", + "MetricName": "tma_x87_use", + "MetricThreshold": "tma_x87_use > 0.1 & (tma_fp_arith > 0.2 & tma_light_operations > 0.6)", + "PublicDescription": "This metric serves as an approximation of legacy x87 usage. It accounts for instructions beyond X87 FP arithmetic operations; hence may be used as a thermometer to avoid X87 high usage and preferably upgrade to modern ISA. See Tip under Tuning Hint.", + "ScaleUnit": "100%", + "Unit": "cpu_core" + } +] From d546e3acf3526eaf1d74902236c7219a424ac2e9 Mon Sep 17 00:00:00 2001 From: Weilin Wang Date: Sat, 20 Jul 2024 02:20:59 -0400 Subject: [PATCH 144/347] perf stat: Add command line option for enabling TPEBS recording With this command line option, TPEBS recording is turned off in 'perf stat' on default. It will only be turned on when this option is given in 'perf stat' command. Example with --record-tpebs: perf stat -M tma_split_loads -C1-4 --record-tpebs sleep 1 [ perf record: Woken up 2 times to write data ] [ perf record: Captured and wrote 0.044 MB - ] Performance counter stats for 'CPU(s) 1-4': 53,259,156,071 cpu_core/TOPDOWN.SLOTS/ # 1.6 % tma_split_loads (50.00%) 15,867,565,250 cpu_core/topdown-retiring/ (50.00%) 15,655,580,731 cpu_core/topdown-mem-bound/ (50.00%) 11,738,022,218 cpu_core/topdown-bad-spec/ (50.00%) 6,151,265,424 cpu_core/topdown-fe-bound/ (50.00%) 20,445,917,581 cpu_core/topdown-be-bound/ (50.00%) 6,925,098,013 cpu_core/L1D_PEND_MISS.PENDING/ (50.00%) 3,838,653,421 cpu_core/MEMORY_ACTIVITY.STALLS_L1D_MISS/ (50.00%) 4,797,059,783 cpu_core/EXE_ACTIVITY.BOUND_ON_LOADS/ (50.00%) 11,931,916,714 cpu_core/CPU_CLK_UNHALTED.THREAD/ (50.00%) 102,576,164 cpu_core/MEM_LOAD_COMPLETED.L1_MISS_ANY/ (50.00%) 64,071,854 cpu_core/MEM_INST_RETIRED.SPLIT_LOADS/ (50.00%) 3 cpu_core/MEM_INST_RETIRED.SPLIT_LOADS/R 1.003049679 seconds time elapsed Example without --record-tpebs: perf stat -M tma_contested_accesses -C1 sleep 1 Performance counter stats for 'CPU(s) 1': 50,203,891 cpu_core/TOPDOWN.SLOTS/ # 0.0 % tma_contested_accesses (63.60%) 10,040,777 cpu_core/topdown-retiring/ (63.60%) 6,890,729 cpu_core/topdown-mem-bound/ (63.60%) 2,756,463 cpu_core/topdown-bad-spec/ (63.60%) 10,828,288 cpu_core/topdown-fe-bound/ (63.60%) 28,350,432 cpu_core/topdown-be-bound/ (63.60%) 98 cpu_core/OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HITM/ (63.70%) 577,520 cpu_core/MEMORY_ACTIVITY.STALLS_L2_MISS/ (54.62%) 313,339 cpu_core/MEMORY_ACTIVITY.STALLS_L3_MISS/ (54.62%) 14,155 cpu_core/MEM_LOAD_RETIRED.L1_MISS/ (45.54%) 0 cpu_core/OCR.DEMAND_DATA_RD.L3_HIT.SNOOP_HIT_WITH_FWD/ (36.30%) 8,468,077 cpu_core/CPU_CLK_UNHALTED.THREAD/ (45.38%) 198 cpu_core/MEM_LOAD_L3_HIT_RETIRED.XSNP_MISS/ (45.38%) 8,324 cpu_core/MEM_LOAD_RETIRED.FB_HIT/ (45.38%) 3,388,031,520 TSC 23,226,785 cpu_core/CPU_CLK_UNHALTED.REF_TSC/ (54.46%) 80 cpu_core/MEM_LOAD_L3_HIT_RETIRED.XSNP_FWD/ (54.46%) 0 cpu_core/MEM_LOAD_L3_HIT_RETIRED.XSNP_FWD/R 0 cpu_core/MEM_LOAD_L3_HIT_RETIRED.XSNP_MISS/R 1,006,816,667 ns duration_time 1.002537737 seconds time elapsed Reviewed-by: Namhyung Kim Signed-off-by: Weilin Wang Acked-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Caleb Biggers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Perry Taylor Cc: Peter Zijlstra Cc: Samantha Alt Link: https://lore.kernel.org/r/20240720062102.444578-7-weilin.wang@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-stat.txt | 8 ++++++++ tools/perf/builtin-stat.c | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/tools/perf/Documentation/perf-stat.txt b/tools/perf/Documentation/perf-stat.txt index 29756a87ab6f..2bc063672486 100644 --- a/tools/perf/Documentation/perf-stat.txt +++ b/tools/perf/Documentation/perf-stat.txt @@ -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 diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 6162b98fc941..cf985cdb9a6e 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -2475,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, From 169f18fd980cea90804bd8bc5a5614335fa0c8ae Mon Sep 17 00:00:00 2001 From: Weilin Wang Date: Sat, 20 Jul 2024 02:21:00 -0400 Subject: [PATCH 145/347] perf Document: Add TPEBS (Timed PEBS(Precise Event-Based Sampling)) to Documents TPEBS (Timed PEBS(Precise Event-Based Sampling)) is a new feature Intel PMU from Granite Rapids microarchitecture. It will be used in new TMA (Top-Down Microarchitecture Analysis) releases. Add related introduction to documents while adding new code to support it in 'perf stat'. Reviewed-by: Namhyung Kim Signed-off-by: Weilin Wang Acked-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Caleb Biggers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Perry Taylor Cc: Peter Zijlstra Cc: Samantha Alt Link: https://lore.kernel.org/r/20240720062102.444578-8-weilin.wang@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-list.txt | 1 + tools/perf/Documentation/topdown.txt | 30 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index 6bf2468f59d3..dea005410ec0 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt @@ -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: diff --git a/tools/perf/Documentation/topdown.txt b/tools/perf/Documentation/topdown.txt index ae0aee86844f..5c17fff694ee 100644 --- a/tools/perf/Documentation/topdown.txt +++ b/tools/perf/Documentation/topdown.txt @@ -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 From b2738fda24543777a623a7d1cc2a9985ab81b448 Mon Sep 17 00:00:00 2001 From: Weilin Wang Date: Sat, 20 Jul 2024 02:21:01 -0400 Subject: [PATCH 146/347] perf test: Add test for Intel TPEBS counting mode Intel TPEBS sampling mode is supported through perf record. The counting mode code uses perf record to capture retire_latency value and use it in metric calculation. This test checks the counting mode code on Intel platforms. Committer testing: root@x1:~# perf test tpebs 123: test Intel TPEBS counting mode : Ok root@x1:~# set -o vi root@x1:~# perf test tpebs 123: test Intel TPEBS counting mode : Ok root@x1:~# perf test -v tpebs 123: test Intel TPEBS counting mode : Ok root@x1:~# perf test -vvv tpebs 123: test Intel TPEBS counting mode: --- start --- test child forked, pid 16603 Testing without --record-tpebs Testing with --record-tpebs ---- end(0) ---- 123: test Intel TPEBS counting mode : Ok root@x1:~# Reviewed-by: Namhyung Kim Signed-off-by: Weilin Wang Acked-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Caleb Biggers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Perry Taylor Cc: Peter Zijlstra Cc: Samantha Alt Link: https://lore.kernel.org/r/20240720062102.444578-9-weilin.wang@intel.com Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/tests/shell/test_stat_intel_tpebs.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100755 tools/perf/tests/shell/test_stat_intel_tpebs.sh diff --git a/tools/perf/tests/shell/test_stat_intel_tpebs.sh b/tools/perf/tests/shell/test_stat_intel_tpebs.sh new file mode 100755 index 000000000000..c60b29add980 --- /dev/null +++ b/tools/perf/tests/shell/test_stat_intel_tpebs.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# test Intel TPEBS counting mode +# SPDX-License-Identifier: GPL-2.0 + +set -e +grep -q GenuineIntel /proc/cpuinfo || { echo Skipping non-Intel; exit 2; } + +# Use this event for testing because it should exist in all platforms +event=cache-misses:R + +# Without this cmd option, default value or zero is returned +echo "Testing without --record-tpebs" +result=$(perf stat -e "$event" true 2>&1) +[[ "$result" =~ $event ]] || exit 1 + +# In platforms that do not support TPEBS, it should execute without error. +echo "Testing with --record-tpebs" +result=$(perf stat -e "$event" --record-tpebs -a sleep 0.01 2>&1) +[[ "$result" =~ "perf record" && "$result" =~ $event ]] || exit 1 From 1a9d080d19c3303569614ed4c3b43a39aacd90d7 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 15:41:19 -0700 Subject: [PATCH 147/347] perf callchain: Add a for_each callback style API Add a for_each callback style API to callchain with sample__for_each_callchain_node(). Possibly in the future such an API can avoid the overhead of constructing the call chain list. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Casey Chen Cc: Colin Ian King Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Peter Zijlstra Cc: Tom Zanussi Link: https://lore.kernel.org/r/20240812224119.744968-1-irogers@google.com [ Split from a larger patch that introduced the API and use it ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/callchain.c | 35 +++++++++++++++++++++++++++++++++++ tools/perf/util/callchain.h | 6 ++++++ 2 files changed, 41 insertions(+) diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 6d075648d2cc..0d608e875fe9 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -1797,3 +1797,38 @@ s64 callchain_avg_cycles(struct callchain_node *cnode) return cycles; } + +int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel, + struct perf_sample *sample, int max_stack, + callchain_iter_fn cb, void *data) +{ + struct callchain_cursor *cursor = get_tls_callchain_cursor(); + int ret; + + if (!cursor) + return -ENOMEM; + + /* Fill in the callchain. */ + ret = thread__resolve_callchain(thread, cursor, evsel, sample, + /*parent=*/NULL, /*root_al=*/NULL, + max_stack); + if (ret) + return ret; + + /* Switch from writing the callchain to reading it. */ + callchain_cursor_commit(cursor); + + while (1) { + struct callchain_cursor_node *node = callchain_cursor_current(cursor); + + if (!node) + break; + + ret = cb(node, data); + if (ret) + return ret; + + callchain_cursor_advance(cursor); + } + return 0; +} diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index d5c66345ae31..76891f8e2373 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -311,4 +311,10 @@ u64 callchain_total_hits(struct hists *hists); s64 callchain_avg_cycles(struct callchain_node *cnode); +typedef int (*callchain_iter_fn)(struct callchain_cursor_node *node, void *data); + +int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel, + struct perf_sample *sample, int max_stack, + callchain_iter_fn cb, void *data); + #endif /* __PERF_CALLCHAIN_H */ From 3d557dd3f54e329556e880129d0181988893b00f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 15:41:19 -0700 Subject: [PATCH 148/347] perf inject: Inject build ids for entire call chain The DSO build id is injected when the dso is first encountered but the checking for first encountered only looks at the sample->ip not the entire callchain. Use the callchain logic to ensure all build ids are inserted. Fixes: 454c407ec17a0c63 ("perf: add perf-inject builtin") Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Casey Chen Cc: Colin Ian King Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Peter Zijlstra Cc: Tom Zanussi Link: https://lore.kernel.org/r/20240812224119.744968-1-irogers@google.com [ Split from a larger patch that introduced the API and use it ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index ef9cba173dd2..a35bde3f3c09 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -743,6 +743,29 @@ static int dso__inject_build_id(struct dso *dso, const struct perf_tool *tool, return 0; } +struct mark_dso_hit_args { + const struct perf_tool *tool; + struct machine *machine; + u8 cpumode; +}; + +static int mark_dso_hit_callback(struct callchain_cursor_node *node, void *data) +{ + struct mark_dso_hit_args *args = data; + struct map *map = node->ms.map; + + if (map) { + struct dso *dso = map__dso(map); + + if (dso && !dso__hit(dso)) { + dso__set_hit(dso); + dso__inject_build_id(dso, args->tool, args->machine, + args->cpumode, map__flags(map)); + } + } + return 0; +} + int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct evsel *evsel __maybe_unused, @@ -750,6 +773,11 @@ int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *e { struct addr_location al; struct thread *thread; + struct mark_dso_hit_args args = { + .tool = tool, + .machine = machine, + .cpumode = sample->cpumode, + }; addr_location__init(&al); thread = machine__findnew_thread(machine, sample->pid, sample->tid); @@ -769,6 +797,9 @@ int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *e } } + sample__for_each_callchain_node(thread, evsel, sample, PERF_MAX_STACK_DEPTH, + mark_dso_hit_callback, &args); + thread__put(thread); repipe: perf_event__repipe(tool, event, sample, machine); From a05031713d460de5ff8500680458ed8f3fd946e6 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 21:06:12 -0700 Subject: [PATCH 149/347] perf disasm: Fix memory leak for locked operations lock__parse() calls disasm_line__parse() passing &ops->locked.ins.name that will use strdup() to populate it. Ensure ops->locked.ins.name is freed in lock__delete(). Found with address/leak sanitizer. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Athira Rajeev Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kajol Jain Cc: Kan Liang Cc: Mark Rutland Cc: Peter Zijlstra Cc: Thomas Richter Link: https://lore.kernel.org/r/20240813040613.882075-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/disasm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 22289003e16d..226d2181f694 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -566,6 +566,7 @@ static void lock__delete(struct ins_operands *ops) ins_ops__delete(ops->locked.ops); zfree(&ops->locked.ops); + zfree(&ops->locked.ins.name); zfree(&ops->target.raw); zfree(&ops->target.name); } From 653ac51f53032df8a17fd5f9d6c7540a5e70f85c Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 12 Aug 2024 21:06:13 -0700 Subject: [PATCH 150/347] perf test annotate: Dump trapping test in trap handler Help to better identify the location of test failures but dumping the failing test in the trap handler. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Athira Rajeev Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kajol Jain Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Thomas Richter Link: https://lore.kernel.org/r/20240813040613.882075-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/annotate.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/tests/shell/annotate.sh b/tools/perf/tests/shell/annotate.sh index b072d9b97387..2ccf4f1d46b6 100755 --- a/tools/perf/tests/shell/annotate.sh +++ b/tools/perf/tests/shell/annotate.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf annotate basic tests # SPDX-License-Identifier: GPL-2.0 @@ -28,6 +28,7 @@ cleanup() { } trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" cleanup exit 1 } From 3ef44458071a19e5b5832cdfe6f75273aa521b6e Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Tue, 13 Aug 2024 09:02:00 -0700 Subject: [PATCH 151/347] perf report: Fix --total-cycles --stdio output error The --total-cycles may output wrong information with the --stdio. For example: # perf record -e "{cycles,instructions}",cache-misses -b sleep 1 # perf report --total-cycles --stdio The total cycles output of {cycles,instructions} and cache-misses are almost the same. # Samples: 938 of events 'anon group { cycles, instructions }' # Event count (approx.): 938 # # Sampled Cycles% Sampled Cycles Avg Cycles% Avg Cycles [Program Block Range] # ............... .............. ........... .......... ..................................................> # 11.19% 2.6K 0.10% 21 [perf_iterate_ctx+48 -> > 5.79% 1.4K 0.45% 97 [__intel_pmu_enable_all.constprop.0+80 -> __intel_> 5.11% 1.2K 0.33% 71 [native_write_msr+0 ->> # Samples: 293 of event 'cache-misses' # Event count (approx.): 293 # # Sampled Cycles% Sampled Cycles Avg Cycles% Avg Cycles [Program Block Range] # ............... .............. ........... .......... ..................................................> # 11.19% 2.6K 0.13% 21 [perf_iterate_ctx+48 -> > 5.79% 1.4K 0.59% 97 [__intel_pmu_enable_all.constprop.0+80 -> __intel_> 5.11% 1.2K 0.43% 71 [native_write_msr+0 ->> With the symbol_conf.event_group, the 'perf report' should only report the block information of the leader event in a group. However, the current implementation retrieves the next event's block information, rather than the next group leader's block information. Make sure the index is updated even if the event is skipped. With the patch, # Samples: 293 of event 'cache-misses' # Event count (approx.): 293 # # Sampled Cycles% Sampled Cycles Avg Cycles% Avg Cycles [Program Block Range] # ............... .............. ........... .......... ..................................................> # 37.98% 9.0K 4.05% 299 [perf_event_addr_filters_exec+0 -> perf_event_a> 11.19% 2.6K 0.28% 21 [perf_iterate_ctx+48 -> > 5.79% 1.4K 1.32% 97 [__intel_pmu_enable_all.constprop.0+80 -> __intel_> Fixes: 6f7164fa231a5f36 ("perf report: Sort by sampled cycles percent per block for stdio") Reviewed-by: Andi Kleen Signed-off-by: Kan Liang Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jin Yao Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240813160208.2493643-2-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index dfb47fa85e5c..04b9a5c1bc7e 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -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,7 @@ 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, + report__browse_block_hists(&rep->block_reports[i - 1].hist, rep->min_percent, pos, NULL); continue; } From 183212a45e5fe66e7f6cbd360d186174b013d023 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Tue, 13 Aug 2024 09:02:01 -0700 Subject: [PATCH 152/347] perf report: Remove the first overflow check for branch counters A false overflow warning is triggered if a sample doesn't have any LBRs recorded and the branch counters feature is enabled. The current code does OVERFLOW_CHECK_u64() at the very beginning when reading the information of branch counters. It assumes that there is at least one LBR in the PEBS record. But it is a valid case that 0 LBR is recorded especially in a high context switch. Remove the OVERFLOW_CHECK_u64(). The later OVERFLOW_CHECK() should be good enough to check the overflow when reading the information of the branch counters. Fixes: 9fbb4b02302b0ae6 ("perf tools: Add branch counter knob") Reviewed-by: Andi Kleen Signed-off-by: Kan Liang Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Kan Liang Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240813160208.2493643-3-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index d607056b73c9..f22f402d54cc 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -2884,8 +2884,6 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, array = (void *)array + sz; if (evsel__has_branch_counters(evsel)) { - OVERFLOW_CHECK_u64(array); - data->branch_stack_cntr = (u64 *)array; sz = data->branch_stack->nr * sizeof(u64); From 3a867a6dadb2d67b85cbf9bc67eca2428075a109 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Tue, 13 Aug 2024 09:02:02 -0700 Subject: [PATCH 153/347] perf evlist: Save branch counters information The branch counters logging (A.K.A LBR event logging) introduces a per-counter indication of precise event occurrences in LBRs. The kernel only dumps the number of occurrences into a record. The perf tool has to map the number to the corresponding event. Add evlist__update_br_cntr() to go through the evlist to pick the events that are configured to be logged. Assign a logical idx to track them, and add the total number of the events in the leader event. The total number will be used to allocate the space to save the branch counters for a block. The logical idx will be used to locate the corresponding event quickly in the following patches. It only needs to iterate the evlist once. The evsel__has_branch_counters() is also optimized. Reviewed-by: Andi Kleen Signed-off-by: Kan Liang Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240813160208.2493643-4-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 15 +++++++++++++++ tools/perf/util/evlist.h | 2 ++ tools/perf/util/evsel.c | 13 +++++++------ tools/perf/util/evsel.h | 8 ++++++++ 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 4b2538e4f679..68bbd3ea771b 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -79,6 +79,7 @@ void evlist__init(struct evlist *evlist, struct perf_cpu_map *cpus, evlist->ctl_fd.fd = -1; evlist->ctl_fd.ack = -1; evlist->ctl_fd.pos = -1; + evlist->nr_br_cntr = -1; } struct evlist *evlist__new(void) @@ -1264,6 +1265,20 @@ u64 evlist__combined_branch_type(struct evlist *evlist) return branch_type; } +void evlist__update_br_cntr(struct evlist *evlist) +{ + struct evsel *evsel; + int i = 0; + + evlist__for_each_entry(evlist, evsel) { + if (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS) { + evsel->br_cntr_idx = i++; + evsel__leader(evsel)->br_cntr_nr++; + } + } + evlist->nr_br_cntr = i; +} + bool evlist__valid_read_format(struct evlist *evlist) { struct evsel *first = evlist__first(evlist), *pos = first; diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index cccc34da5a02..b46f1a320783 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -57,6 +57,7 @@ struct evlist { bool enabled; int id_pos; int is_pos; + int nr_br_cntr; u64 combined_sample_type; enum bkw_mmap_state bkw_mmap_state; struct { @@ -219,6 +220,7 @@ int evlist__apply_filters(struct evlist *evlist, struct evsel **err_evsel, u64 __evlist__combined_sample_type(struct evlist *evlist); u64 evlist__combined_sample_type(struct evlist *evlist); u64 evlist__combined_branch_type(struct evlist *evlist); +void evlist__update_br_cntr(struct evlist *evlist); bool evlist__sample_id_all(struct evlist *evlist); u16 evlist__id_hdr_size(struct evlist *evlist); diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index f22f402d54cc..38a74d6dde49 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -2636,17 +2636,18 @@ u64 evsel__bitfield_swap_branch_flags(u64 value) static inline bool evsel__has_branch_counters(const struct evsel *evsel) { - struct evsel *cur, *leader = evsel__leader(evsel); + struct evsel *leader = evsel__leader(evsel); /* The branch counters feature only supports group */ if (!leader || !evsel->evlist) return false; - evlist__for_each_entry(evsel->evlist, cur) { - if ((leader == evsel__leader(cur)) && - (cur->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS)) - return true; - } + if (evsel->evlist->nr_br_cntr < 0) + evlist__update_br_cntr(evsel->evlist); + + if (leader->br_cntr_nr > 0) + return true; + return false; } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index a5da4b03bb1c..a393dae1dc96 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -148,6 +148,14 @@ struct evsel { */ __u64 synth_sample_type; + /* + * Store the branch counter related information. + * br_cntr_idx: The idx of the branch counter event in the evlist + * br_cntr_nr: The number of the branch counter event in the group + * (Only available for the leader event) + */ + int br_cntr_idx; + int br_cntr_nr; /* * bpf_counter_ops serves two use cases: * 1. perf-stat -b counting events used byBPF programs From 1f2b7fbb04f53540090ac7d320194654f142443f Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Tue, 13 Aug 2024 09:02:03 -0700 Subject: [PATCH 154/347] perf annotate: Save branch counters for each block When annotating a basic block, it's useful to display the occurrences of other events in the block. The branch counter feature is only available for newer Intel platforms. So a dedicated option to display the branch counters is not introduced. Reuse the existing --total-cycles option, which triggers the annotation of a basic block and displays the cycle-related annotation. When the branch counters information is available, the branch counters are automatically appended after all the cycle-related annotation. Accounting the branch counters as well when accounting the cycles in hist__account_cycles(). In 'struct annotated_branch', introduce a br_cntr array to save the accumulation of each branch counter. In a sample, all the branch counters for a branch are saved in a u64 space. Because the saturation of a branch counter is small, e.g., for Intel Sierra Forest, the saturation is only 3. Add ANNOTATION__BR_CNTR_SATURATED_FLAG to indicate if a branch counter once saturated. That can be used to indicate a potential event lost because of the saturation. Reviewed-by: Andi Kleen Signed-off-by: Kan Liang Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240813160208.2493643-5-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 3 +- tools/perf/builtin-diff.c | 4 +-- tools/perf/builtin-report.c | 2 +- tools/perf/builtin-top.c | 4 +-- tools/perf/util/annotate.c | 68 ++++++++++++++++++++++++++++------- tools/perf/util/annotate.h | 10 +++++- tools/perf/util/branch.h | 1 + tools/perf/util/hist.c | 5 +-- tools/perf/util/hist.h | 2 +- tools/perf/util/machine.c | 3 ++ 10 files changed, 80 insertions(+), 22 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index d6f6ba5a569d..9b4a8a379b5b 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -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: diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 28c5208fcdc9..6983b5373372 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -431,8 +431,8 @@ static int diff__process_sample_event(const 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: diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 04b9a5c1bc7e..ab450644e5b3 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -328,7 +328,7 @@ static int process_sample_event(const 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); diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 1cdac4e190e9..881b861c35ee 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -735,8 +735,8 @@ 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; } diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index a87d2e97e10d..c269758d47c8 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -266,22 +266,30 @@ struct annotated_branch *annotation__get_branch(struct annotation *notes) return notes->branch; } -static struct cyc_hist *symbol__cycles_hist(struct symbol *sym) +static struct annotated_branch *symbol__find_branch_hist(struct symbol *sym, + unsigned int br_cntr_nr) { struct annotation *notes = symbol__annotation(sym); struct annotated_branch *branch; + const size_t size = symbol__size(sym); branch = annotation__get_branch(notes); if (branch == NULL) return NULL; if (branch->cycles_hist == NULL) { - const size_t size = symbol__size(sym); - branch->cycles_hist = calloc(size, sizeof(struct cyc_hist)); + if (!branch->cycles_hist) + return NULL; } - return branch->cycles_hist; + if (br_cntr_nr && branch->br_cntr == NULL) { + branch->br_cntr = calloc(br_cntr_nr * size, sizeof(u64)); + if (!branch->br_cntr) + return NULL; + } + + return branch; } struct annotated_source *symbol__hists(struct symbol *sym, int nr_hists) @@ -316,16 +324,44 @@ static int symbol__inc_addr_samples(struct map_symbol *ms, return src ? __symbol__inc_addr_samples(ms, src, evsel->core.idx, addr, sample) : 0; } -static int symbol__account_cycles(u64 addr, u64 start, - struct symbol *sym, unsigned cycles) +static int symbol__account_br_cntr(struct annotated_branch *branch, + struct evsel *evsel, + unsigned offset, + u64 br_cntr) { - struct cyc_hist *cycles_hist; + unsigned int br_cntr_nr = evsel__leader(evsel)->br_cntr_nr; + unsigned int base = evsel__leader(evsel)->br_cntr_idx; + unsigned int width = evsel__env(evsel)->br_cntr_width; + unsigned int off = offset * evsel->evlist->nr_br_cntr; + unsigned int i, mask = (1L << width) - 1; + u64 *branch_br_cntr = branch->br_cntr; + + if (!br_cntr || !branch_br_cntr) + return 0; + + for (i = 0; i < br_cntr_nr; i++) { + u64 cntr = (br_cntr >> i * width) & mask; + + branch_br_cntr[off + i + base] += cntr; + if (cntr == mask) + branch_br_cntr[off + i + base] |= ANNOTATION__BR_CNTR_SATURATED_FLAG; + } + + return 0; +} + +static int symbol__account_cycles(u64 addr, u64 start, struct symbol *sym, + unsigned cycles, struct evsel *evsel, + u64 br_cntr) +{ + struct annotated_branch *branch; unsigned offset; + int ret; if (sym == NULL) return 0; - cycles_hist = symbol__cycles_hist(sym); - if (cycles_hist == NULL) + branch = symbol__find_branch_hist(sym, evsel->evlist->nr_br_cntr); + if (!branch) return -ENOMEM; if (addr < sym->start || addr >= sym->end) return -ERANGE; @@ -337,15 +373,22 @@ static int symbol__account_cycles(u64 addr, u64 start, start = 0; } offset = addr - sym->start; - return __symbol__account_cycles(cycles_hist, + ret = __symbol__account_cycles(branch->cycles_hist, start ? start - sym->start : 0, offset, cycles, !!start); + + if (ret) + return ret; + + return symbol__account_br_cntr(branch, evsel, offset, br_cntr); } int addr_map_symbol__account_cycles(struct addr_map_symbol *ams, struct addr_map_symbol *start, - unsigned cycles) + unsigned cycles, + struct evsel *evsel, + u64 br_cntr) { u64 saddr = 0; int err; @@ -371,7 +414,7 @@ int addr_map_symbol__account_cycles(struct addr_map_symbol *ams, start ? start->addr : 0, ams->ms.sym ? ams->ms.sym->start + map__start(ams->ms.map) : 0, saddr); - err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles); + err = symbol__account_cycles(ams->al_addr, saddr, ams->ms.sym, cycles, evsel, br_cntr); if (err) pr_debug2("account_cycles failed %d\n", err); return err; @@ -412,6 +455,7 @@ static void annotated_branch__delete(struct annotated_branch *branch) { if (branch) { zfree(&branch->cycles_hist); + free(branch->br_cntr); free(branch); } } diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 0e52558baa01..6b3023f3b18f 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -14,6 +14,7 @@ #include "spark.h" #include "hashmap.h" #include "disasm.h" +#include "branch.h" struct hist_browser_timer; struct hist_entry; @@ -288,6 +289,9 @@ struct annotated_source { struct annotation_line *annotated_source__get_line(struct annotated_source *src, s64 offset); +/* A branch counter once saturated */ +#define ANNOTATION__BR_CNTR_SATURATED_FLAG (1ULL << 63) + /** * struct annotated_branch - basic block and IPC information for a symbol. * @@ -297,6 +301,7 @@ struct annotation_line *annotated_source__get_line(struct annotated_source *src, * @cover_insn: Number of distinct, actually executed instructions. * @cycles_hist: Array of cyc_hist for each instruction. * @max_coverage: Maximum number of covered basic block (used for block-range). + * @br_cntr: Array of the occurrences of events (branch counters) during a block. * * This struct is used by two different codes when the sample has branch stack * and cycles information. annotation__compute_ipc() calculates average IPC @@ -313,6 +318,7 @@ struct annotated_branch { unsigned int cover_insn; struct cyc_hist *cycles_hist; u64 max_coverage; + u64 *br_cntr; }; struct LOCKABLE annotation { @@ -383,7 +389,9 @@ struct annotated_branch *annotation__get_branch(struct annotation *notes); int addr_map_symbol__account_cycles(struct addr_map_symbol *ams, struct addr_map_symbol *start, - unsigned cycles); + unsigned cycles, + struct evsel *evsel, + u64 br_cntr); int hist_entry__inc_addr_samples(struct hist_entry *he, struct perf_sample *sample, struct evsel *evsel, u64 addr); diff --git a/tools/perf/util/branch.h b/tools/perf/util/branch.h index 87704d713ff6..b80c12c74bbb 100644 --- a/tools/perf/util/branch.h +++ b/tools/perf/util/branch.h @@ -34,6 +34,7 @@ struct branch_info { struct addr_map_symbol from; struct addr_map_symbol to; struct branch_flags flags; + u64 branch_stack_cntr; char *srcline_from; char *srcline_to; }; diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index c8c1b511f8a7..0f554febf9a1 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -2677,7 +2677,7 @@ int hists__unlink(struct hists *hists) void hist__account_cycles(struct branch_stack *bs, struct addr_location *al, struct perf_sample *sample, bool nonany_branch_mode, - u64 *total_cycles) + u64 *total_cycles, struct evsel *evsel) { struct branch_info *bi; struct branch_entry *entries = perf_sample__branch_entries(sample); @@ -2701,7 +2701,8 @@ void hist__account_cycles(struct branch_stack *bs, struct addr_location *al, for (int i = bs->nr - 1; i >= 0; i--) { addr_map_symbol__account_cycles(&bi[i].from, nonany_branch_mode ? NULL : prev, - bi[i].flags.cycles); + bi[i].flags.cycles, evsel, + bi[i].branch_stack_cntr); prev = &bi[i].to; if (total_cycles) diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 5273f5c37050..30c13fc8cbe4 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -742,7 +742,7 @@ unsigned int hists__overhead_width(struct hists *hists); void hist__account_cycles(struct branch_stack *bs, struct addr_location *al, struct perf_sample *sample, bool nonany_branch_mode, - u64 *total_cycles); + u64 *total_cycles, struct evsel *evsel); struct option; int parse_filter_percentage(const struct option *opt, const char *arg, int unset); diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 706be5e4a076..c08774d06d14 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -2141,6 +2141,7 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample, unsigned int i; const struct branch_stack *bs = sample->branch_stack; struct branch_entry *entries = perf_sample__branch_entries(sample); + u64 *branch_stack_cntr = sample->branch_stack_cntr; struct branch_info *bi = calloc(bs->nr, sizeof(struct branch_info)); if (!bi) @@ -2150,6 +2151,8 @@ struct branch_info *sample__resolve_bstack(struct perf_sample *sample, ip__resolve_ams(al->thread, &bi[i].to, entries[i].to); ip__resolve_ams(al->thread, &bi[i].from, entries[i].from); bi[i].flags = entries[i].flags; + if (branch_stack_cntr) + bi[i].branch_stack_cntr = branch_stack_cntr[i]; } return bi; } From 7398bf181d59a8c8d8d13cb555bf3f44b70f6221 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Tue, 13 Aug 2024 09:02:04 -0700 Subject: [PATCH 155/347] perf evsel: Assign abbr name for the branch counter events There could be several branch counter events. If perf tool output the result via the format "event name + a number", the line could be very long and hard to read. An abbreviation is introduced to replace the full event name in the display. The abbreviation starts from 'A' to 'Z9', which can support up to 286 events. The same abbreviation will be assigned if the same events are found in the evlist. The next patch will utilize the abbreviation name to show the branch counter events in the output. Reviewed-by: Andi Kleen Signed-off-by: Kan Liang Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240813160208.2493643-6-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 55 +++++++++++++++++++++++++++++++++++++++- tools/perf/util/evsel.h | 6 +++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 68bbd3ea771b..c0379fa1ccfe 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -33,6 +33,7 @@ #include "util/bpf-filter.h" #include "util/stat.h" #include "util/util.h" +#include "util/env.h" #include "util/intel-tpebs.h" #include #include @@ -1265,15 +1266,67 @@ u64 evlist__combined_branch_type(struct evlist *evlist) return branch_type; } +static struct evsel * +evlist__find_dup_event_from_prev(struct evlist *evlist, struct evsel *event) +{ + struct evsel *pos; + + evlist__for_each_entry(evlist, pos) { + if (event == pos) + break; + if ((pos->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS) && + !strcmp(pos->name, event->name)) + return pos; + } + return NULL; +} + +#define MAX_NR_ABBR_NAME (26 * 11) + +/* + * The abbr name is from A to Z9. If the number of event + * which requires the branch counter > MAX_NR_ABBR_NAME, + * return NA. + */ +static void evlist__new_abbr_name(char *name) +{ + static int idx; + int i = idx / 26; + + if (idx >= MAX_NR_ABBR_NAME) { + name[0] = 'N'; + name[1] = 'A'; + name[2] = '\0'; + return; + } + + name[0] = 'A' + (idx % 26); + + if (!i) + name[1] = '\0'; + else { + name[1] = '0' + i - 1; + name[2] = '\0'; + } + + idx++; +} + void evlist__update_br_cntr(struct evlist *evlist) { - struct evsel *evsel; + struct evsel *evsel, *dup; int i = 0; evlist__for_each_entry(evlist, evsel) { if (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS) { evsel->br_cntr_idx = i++; evsel__leader(evsel)->br_cntr_nr++; + + dup = evlist__find_dup_event_from_prev(evlist, evsel); + if (dup) + memcpy(evsel->abbr_name, dup->abbr_name, 3 * sizeof(char)); + else + evlist__new_abbr_name(evsel->abbr_name); } } evlist->nr_br_cntr = i; diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index a393dae1dc96..4316992f6a69 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -153,9 +153,15 @@ struct evsel { * br_cntr_idx: The idx of the branch counter event in the evlist * br_cntr_nr: The number of the branch counter event in the group * (Only available for the leader event) + * abbr_name: The abbreviation name assigned to an event which is + * logged by the branch counter. + * The abbr name is from A to Z9. NA is applied if out + * of the range. */ int br_cntr_idx; int br_cntr_nr; + char abbr_name[3]; + /* * bpf_counter_ops serves two use cases: * 1. perf-stat -b counting events used byBPF programs From 20d6f555283915f24d52e29a982b547cf6517f06 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Tue, 13 Aug 2024 09:02:05 -0700 Subject: [PATCH 156/347] perf report: Display the branch counter histogram Reusing the existing --total-cycles option to display the branch counters. Add a new PERF_HPP_REPORT__BLOCK_BRANCH_COUNTER to display the logged branch counter events. They are shown right after all the cycle-related annotations. Extend the 'struct block_info' to store and pass the branch counter related information. The annotation_br_cntr_entry() is to print the histogram of each branch counter event. If the number of logged events is less than 4, the exact number of the abbr name is printed. Otherwise, using '+' to stands for more than 3 events. Assume the number of logged events is less than 4. The annotation_br_cntr_abbr_list() prints the branch counter's abbreviation list. Press 'B' to display the list in the TUI mode. $ perf record -e "{branch-instructions:ppp,branch-misses}:S" -j any,counter $ perf report --total-cycles --stdio # To display the perf.data header info, please use --header/--header-only options. # # # Total Lost Samples: 0 # # Samples: 1M of events 'anon group { branch-instructions:ppp, branch-misses }' # Event count (approx.): 1610046 # # Branch counter abbr list: # branch-instructions:ppp = A # branch-misses = B # '-' No event occurs # '+' Event occurrences may be lost due to branch counter saturated # # Sampled Cycles% Sampled Cycles Avg Cycles% Avg Cycles Branch Counter [Program Block Range] # ............... .............. ........... .......... .............. .................. # 57.55% 2.5M 0.00% 3 |A |- | ... 25.27% 1.1M 0.00% 2 |AA |- | ... 15.61% 667.2K 0.00% 1 |A |- | ... 0.16% 6.9K 0.81% 575 |A |- | ... 0.16% 6.8K 1.38% 977 |AA |- | ... 0.16% 6.8K 0.04% 28 |AA |B | ... 0.15% 6.6K 1.33% 946 |A |- | ... 0.11% 4.5K 0.06% 46 |AAA+|- | ... 0.10% 4.4K 0.88% 624 |A |- | ... 0.09% 3.7K 0.74% 524 |AAA+|B | ... With -v applied, # Sampled Cycles% Sampled Cycles Avg Cycles% Avg Cycles Branch Counter [Program Block Range] # ............... .............. ........... .......... .............. .................. # 57.55% 2.5M 0.00% 3 A=1 ,B=- ... 25.27% 1.1M 0.00% 2 A=2 ,B=- ... 15.61% 667.2K 0.00% 1 A=1 ,B=- ... 0.16% 6.9K 0.81% 575 A=1 ,B=- ... 0.16% 6.8K 1.38% 977 A=2 ,B=- ... 0.16% 6.8K 0.04% 28 A=2 ,B=1 ... 0.15% 6.6K 1.33% 946 A=1 ,B=- ... 0.11% 4.5K 0.06% 46 A=3+,B=- ... 0.10% 4.4K 0.88% 624 A=1 ,B=- ... 0.09% 3.7K 0.74% 524 A=3+,B=1 ... Reviewed-by: Andi Kleen Signed-off-by: Kan Liang Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240813160208.2493643-7-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-report.txt | 1 + tools/perf/builtin-diff.c | 4 +- tools/perf/builtin-report.c | 20 +++- tools/perf/ui/browsers/hists.c | 17 ++- tools/perf/util/annotate.c | 145 +++++++++++++++++++++++ tools/perf/util/annotate.h | 3 + tools/perf/util/block-info.c | 66 +++++++++-- tools/perf/util/block-info.h | 8 +- 8 files changed, 246 insertions(+), 18 deletions(-) diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt index d2b1593ef700..7c66d81ab978 100644 --- a/tools/perf/Documentation/perf-report.txt +++ b/tools/perf/Documentation/perf-report.txt @@ -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. diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c index 6983b5373372..23326dd20333 100644 --- a/tools/perf/builtin-diff.c +++ b/tools/perf/builtin-diff.c @@ -691,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) { @@ -714,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); diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index ab450644e5b3..1643113616f4 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -575,6 +575,13 @@ 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) { + 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; @@ -1119,18 +1126,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; diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index b7219df51236..970f7f349298 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -3684,8 +3684,10 @@ int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, struct hist_browser *browser; int key = -1; struct popup_action action; + char *br_cntr_text = NULL; static const char help[] = - " q Quit \n"; + " q Quit \n" + " B Branch counter abbr list (Optional)\n"; browser = hist_browser__new(hists); if (!browser) @@ -3703,6 +3705,8 @@ int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, memset(&action, 0, sizeof(action)); + annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false); + while (1) { key = hist_browser__run(browser, "? - help", true, 0); @@ -3723,6 +3727,16 @@ int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, action.ms.sym = browser->selection->sym; do_annotate(browser, &action); continue; + case 'B': + if (br_cntr_text) { + ui__question_window("Branch counter abbr list", + br_cntr_text, "Press any key...", 0); + } else { + ui__question_window("Branch counter abbr list", + "\n The branch counter is not available.\n", + "Press any key...", 0); + } + continue; default: break; } @@ -3730,5 +3744,6 @@ int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, out: hist_browser__delete(browser); + free(br_cntr_text); return 0; } diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index c269758d47c8..5200cafe20e7 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -41,6 +41,7 @@ #include "namespaces.h" #include "thread.h" #include "hashmap.h" +#include "strbuf.h" #include #include #include @@ -48,6 +49,7 @@ #include #include #include +#include /* FIXME: For the HE_COLORSET */ #include "ui/browser.h" @@ -1719,6 +1721,149 @@ static void ipc_coverage_string(char *bf, int size, struct annotation *notes) ipc, coverage); } +int annotation_br_cntr_abbr_list(char **str, struct evsel *evsel, bool header) +{ + struct evsel *pos; + struct strbuf sb; + + if (evsel->evlist->nr_br_cntr <= 0) + return -ENOTSUP; + + strbuf_init(&sb, /*hint=*/ 0); + + if (header && strbuf_addf(&sb, "# Branch counter abbr list:\n")) + goto err; + + evlist__for_each_entry(evsel->evlist, pos) { + if (!(pos->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS)) + continue; + if (header && strbuf_addf(&sb, "#")) + goto err; + + if (strbuf_addf(&sb, " %s = %s\n", pos->name, pos->abbr_name)) + goto err; + } + + if (header && strbuf_addf(&sb, "#")) + goto err; + if (strbuf_addf(&sb, " '-' No event occurs\n")) + goto err; + + if (header && strbuf_addf(&sb, "#")) + goto err; + if (strbuf_addf(&sb, " '+' Event occurrences may be lost due to branch counter saturated\n")) + goto err; + + *str = strbuf_detach(&sb, NULL); + + return 0; +err: + strbuf_release(&sb); + return -ENOMEM; +} + +/* Assume the branch counter saturated at 3 */ +#define ANNOTATION_BR_CNTR_SATURATION 3 + +int annotation_br_cntr_entry(char **str, int br_cntr_nr, + u64 *br_cntr, int num_aggr, + struct evsel *evsel) +{ + struct evsel *pos = evsel ? evlist__first(evsel->evlist) : NULL; + bool saturated = false; + int i, j, avg, used; + struct strbuf sb; + + strbuf_init(&sb, /*hint=*/ 0); + for (i = 0; i < br_cntr_nr; i++) { + used = 0; + avg = ceil((double)(br_cntr[i] & ~ANNOTATION__BR_CNTR_SATURATED_FLAG) / + (double)num_aggr); + + /* + * A histogram with the abbr name is displayed by default. + * With -v, the exact number of branch counter is displayed. + */ + if (verbose) { + evlist__for_each_entry_from(evsel->evlist, pos) { + if ((pos->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS) && + (pos->br_cntr_idx == i)) + break; + } + if (strbuf_addstr(&sb, pos->abbr_name)) + goto err; + + if (!br_cntr[i]) { + if (strbuf_addstr(&sb, "=-")) + goto err; + } else { + if (strbuf_addf(&sb, "=%d", avg)) + goto err; + } + if (br_cntr[i] & ANNOTATION__BR_CNTR_SATURATED_FLAG) { + if (strbuf_addch(&sb, '+')) + goto err; + } else { + if (strbuf_addch(&sb, ' ')) + goto err; + } + + if ((i < br_cntr_nr - 1) && strbuf_addch(&sb, ',')) + goto err; + continue; + } + + if (strbuf_addch(&sb, '|')) + goto err; + + if (!br_cntr[i]) { + if (strbuf_addch(&sb, '-')) + goto err; + used++; + } else { + evlist__for_each_entry_from(evsel->evlist, pos) { + if ((pos->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS) && + (pos->br_cntr_idx == i)) + break; + } + if (br_cntr[i] & ANNOTATION__BR_CNTR_SATURATED_FLAG) + saturated = true; + + for (j = 0; j < avg; j++, used++) { + /* Print + if the number of logged events > 3 */ + if (j >= ANNOTATION_BR_CNTR_SATURATION) { + saturated = true; + break; + } + if (strbuf_addstr(&sb, pos->abbr_name)) + goto err; + } + + if (saturated) { + if (strbuf_addch(&sb, '+')) + goto err; + used++; + } + pos = list_next_entry(pos, core.node); + } + + for (j = used; j < ANNOTATION_BR_CNTR_SATURATION + 1; j++) { + if (strbuf_addch(&sb, ' ')) + goto err; + } + } + + if (!verbose && strbuf_addch(&sb, br_cntr_nr ? '|' : ' ')) + goto err; + + *str = strbuf_detach(&sb, NULL); + + return 0; +err: + strbuf_release(&sb); + return -ENOMEM; +} + static void __annotation_line__write(struct annotation_line *al, struct annotation *notes, bool first_line, bool current_entry, bool change_color, int width, void *obj, unsigned int percent_type, diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 6b3023f3b18f..4fdaa3d5e8d2 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -553,4 +553,7 @@ int annotate_get_basic_blocks(struct symbol *sym, s64 src, s64 dst, void debuginfo_cache__delete(void); +int annotation_br_cntr_entry(char **str, int br_cntr_nr, u64 *br_cntr, + int num_aggr, struct evsel *evsel); +int annotation_br_cntr_abbr_list(char **str, struct evsel *evsel, bool header); #endif /* __PERF_ANNOTATE_H */ diff --git a/tools/perf/util/block-info.c b/tools/perf/util/block-info.c index 04068d48683f..649392bee7ed 100644 --- a/tools/perf/util/block-info.c +++ b/tools/perf/util/block-info.c @@ -40,16 +40,32 @@ static struct block_header_column { [PERF_HPP_REPORT__BLOCK_DSO] = { .name = "Shared Object", .width = 20, + }, + [PERF_HPP_REPORT__BLOCK_BRANCH_COUNTER] = { + .name = "Branch Counter", + .width = 30, } }; -struct block_info *block_info__new(void) +static struct block_info *block_info__new(unsigned int br_cntr_nr) { - return zalloc(sizeof(struct block_info)); + struct block_info *bi = zalloc(sizeof(struct block_info)); + + if (bi && br_cntr_nr) { + bi->br_cntr = calloc(br_cntr_nr, sizeof(u64)); + if (!bi->br_cntr) { + free(bi); + return NULL; + } + } + + return bi; } void block_info__delete(struct block_info *bi) { + if (bi) + free(bi->br_cntr); free(bi); } @@ -86,7 +102,8 @@ int64_t block_info__cmp(struct perf_hpp_fmt *fmt __maybe_unused, static void init_block_info(struct block_info *bi, struct symbol *sym, struct cyc_hist *ch, int offset, - u64 total_cycles) + u64 total_cycles, unsigned int br_cntr_nr, + u64 *br_cntr, struct evsel *evsel) { bi->sym = sym; bi->start = ch->start; @@ -99,10 +116,18 @@ static void init_block_info(struct block_info *bi, struct symbol *sym, memcpy(bi->cycles_spark, ch->cycles_spark, NUM_SPARKS * sizeof(u64)); + + if (br_cntr && br_cntr_nr) { + bi->br_cntr_nr = br_cntr_nr; + memcpy(bi->br_cntr, &br_cntr[offset * br_cntr_nr], + br_cntr_nr * sizeof(u64)); + } + bi->evsel = evsel; } int block_info__process_sym(struct hist_entry *he, struct block_hist *bh, - u64 *block_cycles_aggr, u64 total_cycles) + u64 *block_cycles_aggr, u64 total_cycles, + unsigned int br_cntr_nr) { struct annotation *notes; struct cyc_hist *ch; @@ -125,12 +150,14 @@ int block_info__process_sym(struct hist_entry *he, struct block_hist *bh, struct block_info *bi; struct hist_entry *he_block; - bi = block_info__new(); + bi = block_info__new(br_cntr_nr); if (!bi) return -1; init_block_info(bi, he->ms.sym, &ch[i], i, - total_cycles); + total_cycles, br_cntr_nr, + notes->branch->br_cntr, + hists_to_evsel(he->hists)); cycles += bi->cycles_aggr / bi->num_aggr; he_block = hists__add_entry_block(&bh->block_hists, @@ -327,6 +354,24 @@ static void init_block_header(struct block_fmt *block_fmt) fmt->width = block_column_width; } +static int block_branch_counter_entry(struct perf_hpp_fmt *fmt, + struct perf_hpp *hpp, + struct hist_entry *he) +{ + struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt); + struct block_info *bi = he->block_info; + char *buf; + int ret; + + if (annotation_br_cntr_entry(&buf, bi->br_cntr_nr, bi->br_cntr, + bi->num_aggr, bi->evsel)) + return 0; + + ret = scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, buf); + free(buf); + return ret; +} + static void hpp_register(struct block_fmt *block_fmt, int idx, struct perf_hpp_list *hpp_list) { @@ -357,6 +402,9 @@ static void hpp_register(struct block_fmt *block_fmt, int idx, case PERF_HPP_REPORT__BLOCK_DSO: fmt->entry = block_dso_entry; break; + case PERF_HPP_REPORT__BLOCK_BRANCH_COUNTER: + fmt->entry = block_branch_counter_entry; + break; default: return; } @@ -390,7 +438,7 @@ static void init_block_hist(struct block_hist *bh, struct block_fmt *block_fmts, static int process_block_report(struct hists *hists, struct block_report *block_report, u64 total_cycles, int *block_hpps, - int nr_hpps) + int nr_hpps, unsigned int br_cntr_nr) { struct rb_node *next = rb_first_cached(&hists->entries); struct block_hist *bh = &block_report->hist; @@ -405,7 +453,7 @@ static int process_block_report(struct hists *hists, while (next) { he = rb_entry(next, struct hist_entry, rb_node); block_info__process_sym(he, bh, &block_report->cycles, - total_cycles); + total_cycles, br_cntr_nr); next = rb_next(&he->rb_node); } @@ -435,7 +483,7 @@ struct block_report *block_info__create_report(struct evlist *evlist, struct hists *hists = evsel__hists(pos); process_block_report(hists, &block_reports[i], total_cycles, - block_hpps, nr_hpps); + block_hpps, nr_hpps, evlist->nr_br_cntr); i++; } diff --git a/tools/perf/util/block-info.h b/tools/perf/util/block-info.h index 0b9e1aad4c55..b9329dc3ab59 100644 --- a/tools/perf/util/block-info.h +++ b/tools/perf/util/block-info.h @@ -18,6 +18,9 @@ struct block_info { u64 total_cycles; int num; int num_aggr; + int br_cntr_nr; + u64 *br_cntr; + struct evsel *evsel; }; struct block_fmt { @@ -36,6 +39,7 @@ enum { PERF_HPP_REPORT__BLOCK_AVG_CYCLES, PERF_HPP_REPORT__BLOCK_RANGE, PERF_HPP_REPORT__BLOCK_DSO, + PERF_HPP_REPORT__BLOCK_BRANCH_COUNTER, PERF_HPP_REPORT__BLOCK_MAX_INDEX }; @@ -46,7 +50,6 @@ struct block_report { int nr_fmts; }; -struct block_info *block_info__new(void); void block_info__delete(struct block_info *bi); int64_t __block_info__cmp(struct hist_entry *left, struct hist_entry *right); @@ -55,7 +58,8 @@ int64_t block_info__cmp(struct perf_hpp_fmt *fmt __maybe_unused, struct hist_entry *left, struct hist_entry *right); int block_info__process_sym(struct hist_entry *he, struct block_hist *bh, - u64 *block_cycles_aggr, u64 total_cycles); + u64 *block_cycles_aggr, u64 total_cycles, + unsigned int br_cntr_nr); struct block_report *block_info__create_report(struct evlist *evlist, u64 total_cycles, From e6952dcec8302643a8b59751df250d03762429c3 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Tue, 13 Aug 2024 09:02:06 -0700 Subject: [PATCH 157/347] perf annotate: Display the branch counter histogram MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Display the branch counter histogram in the annotation view. Press 'B' to display the branch counter's abbreviation list as well. Samples: 1M of events 'anon group { branch-instructions:ppp, branch-misses }', 4000 Hz, Event count (approx.): f3 /home/sdp/test/tchain_edit [Percent: local period] Percent │ IPC Cycle Branch Counter (Average IPC: 1.39, IPC Coverage: 29.4%) │ 0000000000401755 : 0.00 0.00 │ endbr64 │ push %rbp │ mov %rsp,%rbp │ movl $0x0,-0x4(%rbp) 0.00 0.00 │1.33 3 |A |- | ↓ jmp 25 11.03 11.03 │ 11: mov -0x4(%rbp),%eax │ and $0x1,%eax │ test %eax,%eax 17.13 17.13 │2.41 1 |A |- | ↓ je 21 │ addl $0x1,-0x4(%rbp) 21.84 21.84 │2.22 2 |AA |- | ↓ jmp 25 17.13 17.13 │ 21: addl $0x1,-0x4(%rbp) 21.84 21.84 │ 25: cmpl $0x270f,-0x4(%rbp) 11.03 11.03 │0.61 3 |A |- | ↑ jle 11 │ nop │ pop %rbp 0.00 0.00 │0.24 20 |AA |B | ← ret Originally-by: Tinghao Zhang Reviewed-by: Andi Kleen Signed-off-by: Kan Liang Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240813160208.2493643-8-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-annotate.c | 10 +++++--- tools/perf/ui/browsers/annotate.c | 18 ++++++++++++-- tools/perf/ui/browsers/hists.c | 3 ++- tools/perf/util/annotate.c | 40 ++++++++++++++++++++++++++++--- tools/perf/util/annotate.h | 11 +++++++++ tools/perf/util/disasm.c | 1 + 6 files changed, 74 insertions(+), 9 deletions(-) diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c index 9b4a8a379b5b..3dc6197ef3fa 100644 --- a/tools/perf/builtin-annotate.c +++ b/tools/perf/builtin-annotate.c @@ -927,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); diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index fe991a81256b..d7e727345dab 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c @@ -156,6 +156,7 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) struct symbol *sym = ms->sym; struct annotation *notes = symbol__annotation(sym); u8 pcnt_width = annotation__pcnt_width(notes); + u8 cntr_width = annotation__br_cntr_width(); int width; int diff = 0; @@ -205,13 +206,13 @@ static void annotate_browser__draw_current_jump(struct ui_browser *browser) ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS); __ui_browser__line_arrow(browser, - pcnt_width + 2 + notes->src->widths.addr + width, + pcnt_width + 2 + notes->src->widths.addr + width + cntr_width, from, to); diff = is_fused(ab, cursor); if (diff > 0) { ui_browser__mark_fused(browser, - pcnt_width + 3 + notes->src->widths.addr + width, + pcnt_width + 3 + notes->src->widths.addr + width + cntr_width, from - diff, diff, to > from); } } @@ -714,6 +715,7 @@ static int annotate_browser__run(struct annotate_browser *browser, struct annotation *notes = symbol__annotation(ms->sym); const char *help = "Press 'h' for help on key bindings"; int delay_secs = hbt ? hbt->refresh : 0; + char *br_cntr_text = NULL; char title[256]; int key; @@ -730,6 +732,8 @@ static int annotate_browser__run(struct annotate_browser *browser, nd = browser->curr_hot; + annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false); + while (1) { key = ui_browser__run(&browser->b, delay_secs); @@ -796,6 +800,7 @@ static int annotate_browser__run(struct annotate_browser *browser, "r Run available scripts\n" "p Toggle percent type [local/global]\n" "b Toggle percent base [period/hits]\n" + "B Branch counter abbr list (Optional)\n" "? Search string backwards\n" "f Toggle showing offsets to full address\n"); continue; @@ -904,6 +909,14 @@ show_sup_ins: hists__scnprintf_title(hists, title, sizeof(title)); annotate_browser__show(&browser->b, title, help); continue; + case 'B': + if (br_cntr_text) + ui_browser__help_window(&browser->b, br_cntr_text); + else { + ui_browser__help_window(&browser->b, + "\n The branch counter is not available.\n"); + } + continue; case 'f': annotation__toggle_full_addr(notes, ms); continue; @@ -923,6 +936,7 @@ show_sup_ins: } out: ui_browser__hide(&browser->b); + free(br_cntr_text); return key; } diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c index 970f7f349298..49ba82bf3391 100644 --- a/tools/perf/ui/browsers/hists.c +++ b/tools/perf/ui/browsers/hists.c @@ -3705,7 +3705,8 @@ int block_hists_tui_browse(struct block_hist *bh, struct evsel *evsel, memset(&action, 0, sizeof(action)); - annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false); + if (!annotation_br_cntr_abbr_list(&br_cntr_text, evsel, false)) + annotate_opts.show_br_cntr = true; while (1) { key = hist_browser__run(browser, "? - help", true, 0); diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 5200cafe20e7..4990c70b1794 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -501,8 +501,10 @@ static void annotation__count_and_fill(struct annotation *notes, u64 start, u64 } } -static int annotation__compute_ipc(struct annotation *notes, size_t size) +static int annotation__compute_ipc(struct annotation *notes, size_t size, + struct evsel *evsel) { + unsigned int br_cntr_nr = evsel->evlist->nr_br_cntr; int err = 0; s64 offset; @@ -537,6 +539,20 @@ static int annotation__compute_ipc(struct annotation *notes, size_t size) al->cycles->max = ch->cycles_max; al->cycles->min = ch->cycles_min; } + if (al && notes->branch->br_cntr) { + if (!al->br_cntr) { + al->br_cntr = calloc(br_cntr_nr, sizeof(u64)); + if (!al->br_cntr) { + err = ENOMEM; + break; + } + } + al->num_aggr = ch->num_aggr; + al->br_cntr_nr = br_cntr_nr; + al->evsel = evsel; + memcpy(al->br_cntr, ¬es->branch->br_cntr[offset * br_cntr_nr], + br_cntr_nr * sizeof(u64)); + } } } @@ -548,8 +564,10 @@ static int annotation__compute_ipc(struct annotation *notes, size_t size) struct annotation_line *al; al = annotated_source__get_line(notes->src, offset); - if (al) + if (al) { zfree(&al->cycles); + zfree(&al->br_cntr); + } } } } @@ -1960,6 +1978,22 @@ static void __annotation_line__write(struct annotation_line *al, struct annotati "Cycle(min/max)"); } + if (annotate_opts.show_br_cntr) { + if (show_title) { + obj__printf(obj, "%*s ", + ANNOTATION__BR_CNTR_WIDTH, + "Branch Counter"); + } else { + char *buf; + + if (!annotation_br_cntr_entry(&buf, al->br_cntr_nr, al->br_cntr, + al->num_aggr, al->evsel)) { + obj__printf(obj, "%*s ", ANNOTATION__BR_CNTR_WIDTH, buf); + free(buf); + } + } + } + if (show_title && !*al->line) { ipc_coverage_string(bf, sizeof(bf), notes); obj__printf(obj, "%*s", ANNOTATION__AVG_IPC_WIDTH, bf); @@ -2056,7 +2090,7 @@ int symbol__annotate2(struct map_symbol *ms, struct evsel *evsel, annotation__set_index(notes); annotation__mark_jump_targets(notes, sym); - err = annotation__compute_ipc(notes, size); + err = annotation__compute_ipc(notes, size, evsel); if (err) return err; diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 4fdaa3d5e8d2..8b9e05a1932f 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -31,6 +31,7 @@ struct annotated_data_type; #define ANNOTATION__CYCLES_WIDTH 6 #define ANNOTATION__MINMAX_CYCLES_WIDTH 19 #define ANNOTATION__AVG_IPC_WIDTH 36 +#define ANNOTATION__BR_CNTR_WIDTH 30 #define ANNOTATION_DUMMY_LEN 256 struct annotation_options { @@ -44,6 +45,7 @@ struct annotation_options { show_nr_jumps, show_minmax_cycle, show_asm_raw, + show_br_cntr, annotate_src, full_addr; u8 offset_level; @@ -104,6 +106,10 @@ struct annotation_line { char *fileloc; char *path; struct cycles_info *cycles; + int num_aggr; + int br_cntr_nr; + u64 *br_cntr; + struct evsel *evsel; int jump_sources; u32 idx; int idx_asm; @@ -353,6 +359,11 @@ static inline bool annotation_line__filter(struct annotation_line *al) return annotate_opts.hide_src_code && al->offset == -1; } +static inline u8 annotation__br_cntr_width(void) +{ + return annotate_opts.show_br_cntr ? ANNOTATION__BR_CNTR_WIDTH : 0; +} + void annotation__update_column_widths(struct annotation *notes); void annotation__toggle_full_addr(struct annotation *notes, struct map_symbol *ms); diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 226d2181f694..d11e75133b23 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -1015,6 +1015,7 @@ static void annotation_line__exit(struct annotation_line *al) zfree_srcline(&al->path); zfree(&al->line); zfree(&al->cycles); + zfree(&al->br_cntr); } static size_t disasm_line_size(int nr) From 6f9d8d1de2c61288edd7dfc2a7e7986c8b2ae927 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Tue, 13 Aug 2024 09:02:07 -0700 Subject: [PATCH 158/347] perf script: Add branch counters It's useful to print the branch counter information for each jump in the brstackinsn when it's available. Add a new field 'brcntr' to display the branch counter information. By default, the abbreviation will be used to indicate the branch counter. In the verbose mode, the real event name is shown. $ perf script -F +brstackinsn,+brcntr # Branch counter abbr list: # branch-instructions:ppp = A # branch-misses = B # '-' No event occurs # '+' Event occurrences may be lost due to branch counter saturated tchain_edit 332203 3366329.405674: 53030 branch-instructions:ppp: 401781 f3+0x2c (home/sdp/test/tchain_edit) f3+31: 0000000000401774 insn: eb 04 br_cntr: AA # PRED 5 cycles [5] 000000000040177a insn: 81 7d fc 0f 27 00 00 0000000000401781 insn: 7e e3 br_cntr: A # PRED 1 cycles [6] 2.00 IPC 0000000000401766 insn: 8b 45 fc 0000000000401769 insn: 83 e0 01 000000000040176c insn: 85 c0 000000000040176e insn: 74 06 br_cntr: A # PRED 1 cycles [7] 4.00 IPC 0000000000401776 insn: 83 45 fc 01 000000000040177a insn: 81 7d fc 0f 27 00 00 0000000000401781 insn: 7e e3 br_cntr: A # PRED 7 cycles [14] 0.43 IPC $ perf script -F +brstackinsn,+brcntr -v tchain_edit 332203 3366329.405674: 53030 branch-instructions:ppp: 401781 f3+0x2c (/home/sdp/os.linux.perf.test-suite/kernels/lbr_kernel/tchain_edit) f3+31: 0000000000401774 insn: eb 04 br_cntr: branch-instructions:ppp 2 branch-misses 0 # PRED 5 cycles [5] 000000000040177a insn: 81 7d fc 0f 27 00 00 0000000000401781 insn: 7e e3 br_cntr: branch-instructions:ppp 1 branch-misses 0 # PRED 1 cycles [6] 2.00 IPC 0000000000401766 insn: 8b 45 fc 0000000000401769 insn: 83 e0 01 000000000040176c insn: 85 c0 000000000040176e insn: 74 06 br_cntr: branch-instructions:ppp 1 branch-misses 0 # PRED 1 cycles [7] 4.00 IPC 0000000000401776 insn: 83 45 fc 01 000000000040177a insn: 81 7d fc 0f 27 00 00 0000000000401781 insn: 7e e3 br_cntr: branch-instructions:ppp 1 branch-misses 0 # PRED 7 cycles [14] 0.43 IPC Originally-by: Tinghao Zhang Reviewed-by: Andi Kleen Signed-off-by: Kan Liang Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240813160208.2493643-9-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-script.txt | 2 +- tools/perf/builtin-script.c | 69 +++++++++++++++++++++--- 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/tools/perf/Documentation/perf-script.txt b/tools/perf/Documentation/perf-script.txt index 5abb960c4960..b72866ef270b 100644 --- a/tools/perf/Documentation/perf-script.txt +++ b/tools/perf/Documentation/perf-script.txt @@ -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. diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 54598d1e815a..206b08426555 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -62,6 +62,7 @@ #include "util/record.h" #include "util/util.h" #include "util/cgroup.h" +#include "util/annotate.h" #include "perf.h" #include @@ -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,28 @@ static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, addr_location__exit(&al); } + if (PRINT_FIELD(BRCNTR)) { + unsigned int width = evsel__env(evsel)->br_cntr_width; + unsigned int i = 0, j, num, mask = (1L << width) - 1; + struct evsel *pos = evsel__leader(evsel); + + 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 +1318,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 +1332,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 +1344,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 +1358,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 +1388,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; @@ -1547,6 +1600,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 +1621,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 +1693,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 +2351,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); @@ -3947,7 +4001,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"), From dab5b6cb0d405425502e60450ecf1c8e5ea6b845 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Tue, 13 Aug 2024 09:02:08 -0700 Subject: [PATCH 159/347] perf test: Add new test cases for the branch counter feature Enhance the test case for the branch counter feature. Now, the test verifies: - The new filter can be successfully applied on the supported platforms. - The counter value can be outputted via the perf report -D - The counter value and the abbr name can be outputted via the perf script (New) Reviewed-by: Andi Kleen Signed-off-by: Kan Liang Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240813160208.2493643-10-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/record.sh | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/tools/perf/tests/shell/record.sh b/tools/perf/tests/shell/record.sh index 3d1a7759a7b2..7964ebd9007d 100755 --- a/tools/perf/tests/shell/record.sh +++ b/tools/perf/tests/shell/record.sh @@ -21,6 +21,7 @@ testprog="perf test -w thloop" cpu_pmu_dir="/sys/bus/event_source/devices/cpu*" br_cntr_file="/caps/branch_counter_nr" br_cntr_output="branch stack counters" +br_cntr_script_output="br_cntr: A" cleanup() { rm -rf "${perfdata}" @@ -165,7 +166,7 @@ test_workload() { } test_branch_counter() { - echo "Basic branch counter test" + echo "Branch counter test" # Check if the branch counter feature is supported for dir in $cpu_pmu_dir do @@ -175,19 +176,25 @@ test_branch_counter() { return fi done - if ! perf record -o "${perfdata}" -j any,counter ${testprog} 2> /dev/null + if ! perf record -o "${perfdata}" -e "{branches:p,instructions}" -j any,counter ${testprog} 2> /dev/null then - echo "Basic branch counter test [Failed record]" + echo "Branch counter record test [Failed record]" err=1 return fi if ! perf report -i "${perfdata}" -D -q | grep -q "$br_cntr_output" then - echo "Basic branch record test [Failed missing output]" + echo "Branch counter report test [Failed missing output]" err=1 return fi - echo "Basic branch counter test [Success]" + if ! perf script -i "${perfdata}" -F +brstackinsn,+brcntr | grep -q "$br_cntr_script_output" + then + echo " Branch counter script test [Failed missing output]" + err=1 + return + fi + echo "Branch counter test [Success]" } test_per_thread From 4c55560f23d19051adc7e76818687a88448bef83 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 14 Aug 2024 10:36:24 -0300 Subject: [PATCH 160/347] perf build: Fix up broken capstone feature detection fast path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The capstone devel headers define 'struct bpf_insn' in a way that clashes with what is in the libbpf devel headers, so we so far need to avoid including both. This is happening on the tools/build/feature/test-all.c file, where we try building all the expected set of libraries to be normally available on a system: ⬢[acme@toolbox perf-tools-next]$ cat /tmp/build/perf-tools-next/feature/test-all.make.output In file included from test-bpf.c:3, from test-all.c:150: /home/acme/git/perf-tools-next/tools/include/uapi/linux/bpf.h:77:8: error: ‘bpf_insn’ defined as wrong kind of tag 77 | struct bpf_insn { | ^~~~~~~~ ⬢[acme@toolbox perf-tools-next]$ cat /tmp/build/perf-tools-next/feature/test-all.make.output When doing so there is a trick where we define main to be main_test_libcapstone, then include the individual tools/build/feture/test-libcapstone.c capability query test, and then we undef 'main' because we'll do it all over again with the next expected library to be tested (at this time 'lzma'). To complete this mechanism we need to, in test-all.c 'main' routine, to call main_test_libcapstone(), which isn't being done, so the effect of adding references to capstone in test-all.c are not achieved. The only thing that is happening is that test-all.c is failing to build and thus all the tests will have to be done individually, which nullifies the test-all.c single build speedup. So lets remove references to capstone from test-all.c to see if this makes it build again so that we get faster builds or go on fixing up whatever is preventing us to get that benefit. Nothing: after this fix we get a clean test-all.c build and get the build speedup back: ⬢[acme@toolbox perf-tools-next]$ cat /tmp/build/perf-tools-next/feature/test-all.make.output ⬢[acme@toolbox perf-tools-next]$ cat /tmp/build/perf-tools-next/feature/test-all. test-all.bin test-all.d test-all.make.output ⬢[acme@toolbox perf-tools-next]$ cat /tmp/build/perf-tools-next/feature/test-all.make.output ⬢[acme@toolbox perf-tools-next]$ ldd /tmp/build/perf-tools-next/feature/test-all.bin linux-vdso.so.1 (0x00007f13277a1000) libpython3.12.so.1.0 => /lib64/libpython3.12.so.1.0 (0x00007f1326e00000) libm.so.6 => /lib64/libm.so.6 (0x00007f13274be000) libtraceevent.so.1 => /lib64/libtraceevent.so.1 (0x00007f1327496000) libtracefs.so.1 => /lib64/libtracefs.so.1 (0x00007f132746f000) libcrypto.so.3 => /lib64/libcrypto.so.3 (0x00007f1326800000) libunwind-x86_64.so.8 => /lib64/libunwind-x86_64.so.8 (0x00007f1327452000) libunwind.so.8 => /lib64/libunwind.so.8 (0x00007f1327436000) liblzma.so.5 => /lib64/liblzma.so.5 (0x00007f1327403000) libdw.so.1 => /lib64/libdw.so.1 (0x00007f1326d6f000) libz.so.1 => /lib64/libz.so.1 (0x00007f13273e2000) libelf.so.1 => /lib64/libelf.so.1 (0x00007f1326d53000) libnuma.so.1 => /lib64/libnuma.so.1 (0x00007f13273d4000) libslang.so.2 => /lib64/libslang.so.2 (0x00007f1326400000) libperl.so.5.38 => /lib64/libperl.so.5.38 (0x00007f1326000000) libc.so.6 => /lib64/libc.so.6 (0x00007f1325e0f000) libzstd.so.1 => /lib64/libzstd.so.1 (0x00007f1326741000) /lib64/ld-linux-x86-64.so.2 (0x00007f13277a3000) libbz2.so.1 => /lib64/libbz2.so.1 (0x00007f1326d3f000) libcrypt.so.2 => /lib64/libcrypt.so.2 (0x00007f1326d07000) ⬢[acme@toolbox perf-tools-next]$ And when having capstone-devel installed we get it detected and linked with perf, allowing us to benefit from the features that it enables: ⬢[acme@toolbox perf-tools-next]$ rpm -q capstone-devel capstone-devel-5.0.1-3.fc40.x86_64 ⬢[acme@toolbox perf-tools-next]$ ldd /tmp/build/perf-tools-next/perf | grep capstone libcapstone.so.5 => /lib64/libcapstone.so.5 (0x00007fe6a5c00000) ⬢[acme@toolbox perf-tools-next]$ /tmp/build/perf-tools-next/perf -vv | grep cap libcapstone: [ on ] # HAVE_LIBCAPSTONE_SUPPORT ⬢[acme@toolbox perf-tools-next]$ Fixes: 8b767db3309595a2 ("perf: build: introduce the libcapstone") Cc: Adrian Hunter Cc: Andi Kleen Cc: Changbin Du Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/Zry0sepD5Ppa5YKP@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/feature/test-all.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tools/build/feature/test-all.c b/tools/build/feature/test-all.c index dd0a18c2ef8f..6f4bf386a3b5 100644 --- a/tools/build/feature/test-all.c +++ b/tools/build/feature/test-all.c @@ -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 From 27ac597c0e2f596f53963db4a2cf53cd84657cda Mon Sep 17 00:00:00 2001 From: Veronika Molnarova Date: Mon, 29 Apr 2024 10:53:07 +0200 Subject: [PATCH 161/347] perf test record.sh: Raise limit of open file descriptors Subtest for system-wide record with '--threads=cpu' option fails due to a limit of open file descriptors on systems with 128 or more CPUs as the default limit is set to 1024. The number of open file descriptors should be slightly above nmb_events*nmb_cpus + nmb_cpus(for perf.data.n) + 4*nmb_cpus(for pipes), which equals 8*nmb_cpus. Therefore, temporarily raise the limit to 16*nmb_cpus for the test. Committer notes: Instead of disabling ShellCheck warnings all the uses of 'uname -n', i.e. those: In tests/shell/record.sh line 35: default_fd_limit=$(ulimit -Sn) ^-^ SC3045 (warning): In POSIX sh, ulimit -S is undefined. We can just switch from using '/bin/sh' to '/bin/bash' for this test, as bash _has_ 'ulimit -n', so ShellCheck will not emit that warning. There are dozens of 'perf test' shell tests that do just that, '/bin/bash' is a reasonable expectation for those tests. Signed-off-by: Veronika Molnarova Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Jiri Olsa Cc: Kajol Jain Cc: Michael Petlan Cc: Radostin Stoyanov Link: https://lore.kernel.org/linux-perf-users/20240429085721.10122-1-vmolnaro@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/record.sh | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tools/perf/tests/shell/record.sh b/tools/perf/tests/shell/record.sh index 7964ebd9007d..36883b03169f 100755 --- a/tools/perf/tests/shell/record.sh +++ b/tools/perf/tests/shell/record.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf record tests # SPDX-License-Identifier: GPL-2.0 @@ -23,6 +23,15 @@ br_cntr_file="/caps/branch_counter_nr" br_cntr_output="branch stack counters" br_cntr_script_output="br_cntr: A" +default_fd_limit=$(ulimit -Sn) +# With option --threads=cpu the number of open file descriptors should be +# equal to sum of: nmb_cpus * nmb_events (2+dummy), +# nmb_threads for perf.data.n (equal to nmb_cpus) and +# 2*nmb_cpus of pipes = 4*nmb_cpus (each pipe has 2 ends) +# All together it needs 8*nmb_cpus file descriptors plus some are also used +# outside of testing, thus raising the limit to 16*nmb_cpus +min_fd_limit=$(($(getconf _NPROCESSORS_ONLN) * 16)) + cleanup() { rm -rf "${perfdata}" rm -rf "${perfdata}".old @@ -197,11 +206,19 @@ test_branch_counter() { echo "Branch counter test [Success]" } +# raise the limit of file descriptors to minimum +if [[ $default_fd_limit -lt $min_fd_limit ]]; then + ulimit -Sn $min_fd_limit +fi + test_per_thread test_register_capture test_system_wide test_workload test_branch_counter +# restore the default value +ulimit -Sn $default_fd_limit + cleanup exit $err From 6d02eefecc5e3887eea08f3df5e0f2af6eb35447 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 19 Jul 2024 10:13:12 -0400 Subject: [PATCH 162/347] tracing: Fix ifdef of snapshots to not prevent last_boot_info file The mapping of the ring buffer to memory allocated at boot up will also expose a "last_boot_info" to help tooling to read the raw data from the last boot. As instances that have their ring buffer mapped to fixed memory cannot perform snapshots, they can either have the "snapshot" file or the "last_boot_info" file, but not both. The code that added the "last_boot_info" file failed to notice that the "snapshot" creation was inside a "#ifdef CONFIG_TRACER_SNAPSHOT" and incorrectly placed the creation of the "last_boot_info" file within the ifdef block. Not only does it cause a warning when CONFIG_TRACER_SNAPSHOT is not enabled, it also incorrectly prevents the file from appearing. Link: https://lore.kernel.org/all/20240719102640.718554-1-arnd@kernel.org/ Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Reported-by: Arnd Bergmann Link: https://lore.kernel.org/20240719101312.3d4ac707@rorschach.local.home Fixes: 7a1d1e4b9639 ("tracing/ring-buffer: Add last_boot_info file to boot instance") Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index d0af984a5337..8e5a4ca9fd70 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -9828,15 +9828,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"); -#ifdef CONFIG_TRACER_SNAPSHOT if (tr->range_addr_start) { trace_create_file("last_boot_info", TRACE_MODE_READ, d_tracer, tr, &last_boot_fops); +#ifdef CONFIG_TRACER_SNAPSHOT } 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); From 29a02ec66556ac942d263416c32b97f5b9206f69 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Thu, 15 Aug 2024 08:28:11 -0400 Subject: [PATCH 163/347] tracing: Allow boot instances to use reserve_mem boot memory Allow boot instances to use memory reserved by the reserve_mem boot option. reserve_mem=12M:4096:trace trace_instance=boot_mapped@trace The above will allocate 12 megs with 4096 alignment and label it "trace". The second parameter will create a "boot_mapped" instance and use the memory reserved and labeled as "trace" as the memory for the ring buffer. That will create an instance called "boot_mapped": /sys/kernel/tracing/instances/boot_mapped Note, because the ring buffer is using a defined memory ranged, it will act just like a memory mapped ring buffer. It will not have a snapshot buffer, as it can't swap out the buffer. The snapshot files as well as any tracers that uses a snapshot will not be present in the boot_mapped instance. Also note that reserve_mem is not reliable in acquiring the same physical memory at each soft reboot. It is possible that KALSR could map the kernel at the previous boot memory location forcing the reserve_mem to return a different memory location. In this case, the previous ring buffer will be lost. Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Linus Torvalds Cc: Ross Zwisler Cc: Vincent Donnefort Link: https://lore.kernel.org/20240815082811.669f7d8c@gandalf.local.home Signed-off-by: Steven Rostedt (Google) --- .../admin-guide/kernel-parameters.txt | 13 +++++++++++ kernel/trace/trace.c | 23 ++++++++++++------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 35b592823338..388653448e72 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -6752,6 +6752,19 @@ 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. + trace_options=[option-list] [FTRACE] Enable or disable tracer options at boot. The option-list is a comma delimited list of options diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 8e5a4ca9fd70..9bcef199ae90 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -10465,22 +10465,20 @@ __init static void enable_instances(void) str = boot_instance_info; while ((curr_str = strsep(&str, "\t"))) { - unsigned long start = 0; - unsigned long size = 0; + phys_addr_t start = 0; + phys_addr_t size = 0; unsigned long addr = 0; tok = strsep(&curr_str, ","); name = strsep(&tok, "@"); - if (tok) { + + if (tok && isdigit(*tok)) { start = memparse(tok, &tok); if (!start) { pr_warn("Tracing: Invalid boot instance address for %s\n", name); continue; } - } - - if (start) { if (*tok != ':') { pr_warn("Tracing: No size specified for instance %s\n", name); continue; @@ -10492,10 +10490,19 @@ __init static void enable_instances(void) 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 0x%lx of size 0x%lx\n", - name, start, size); + 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; From 4c57d0be528b1255abe61d8277f3213d4d22e85f Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Tue, 13 Aug 2024 13:11:06 -0400 Subject: [PATCH 164/347] tracing/fgraph: Have fgraph handle previous boot function addresses Update the function graph code to modify the function addresses for a previous boot buffer so that it matches the current kallsyms (note this does not handle module addresses, yet). After a reboot, instead of seeing: # trace-cmd show -B boot_mapped | tail -n30 swapper/0-1 [000] d..2. 56.286470: 0) 0.481 us | 0xffffffff925da5c4(); swapper/0-1 [000] d.... 56.286471: 0) 4.065 us | } swapper/0-1 [000] d.... 56.286471: 0) 4.920 us | } swapper/0-1 [000] d..1. 56.286472: 0) | 0xffffffff92536254() { swapper/0-1 [000] d..1. 56.286472: 0) + 28.974 us | 0xffffffff92534e30(); swapper/0-1 [000] d.... 56.286516: 0) + 43.881 us | } swapper/0-1 [000] d..1. 56.286517: 0) | 0xffffffff925136c4() { swapper/0-1 [000] d..1. 56.286518: 0) | 0xffffffff92514a14() { swapper/0-1 [000] d..1. 56.286518: 0) 6.003 us | 0xffffffff92514200(); swapper/0-1 [000] d.... 56.286529: 0) + 11.510 us | } swapper/0-1 [000] d.... 56.286529: 0) + 12.895 us | } swapper/0-1 [000] d.... 56.286530: 0) ! 382.884 us | } swapper/0-1 [000] d..1. 56.286530: 0) | 0xffffffff92536444() { swapper/0-1 [000] d..1. 56.286531: 0) | 0xffffffff92536254() { swapper/0-1 [000] d..1. 56.286531: 0) + 26.335 us | 0xffffffff92534e30(); swapper/0-1 [000] d.... 56.286560: 0) + 29.511 us | } swapper/0-1 [000] d.... 56.286561: 0) + 30.452 us | } swapper/0-1 [000] d..1. 56.286562: 0) | 0xffffffff9253c014() { swapper/0-1 [000] d..1. 56.286562: 0) | 0xffffffff9253bed4() { swapper/0-1 [000] d..1. 56.286563: 0) + 13.465 us | 0xffffffff92536684(); swapper/0-1 [000] d.... 56.286577: 0) + 14.651 us | } swapper/0-1 [000] d.... 56.286577: 0) + 15.821 us | } swapper/0-1 [000] d..1. 56.286578: 0) 0.667 us | 0xffffffff92547074(); swapper/0-1 [000] d..1. 56.286579: 0) 0.453 us | 0xffffffff924f35c4(); swapper/0-1 [000] d.... 56.286580: 0) # 3906.348 us | } swapper/0-1 [000] d..1. 56.286581: 0) | 0xffffffff92531a14() { swapper/0-1 [000] d..1. 56.286581: 0) 0.518 us | 0xffffffff92505cb4(); swapper/0-1 [000] d..1. 56.286595: 0) | 0xffffffff92db83c4() { swapper/0-1 [000] d..1. 56.286596: 0) | 0xffffffff92dec2e4() { swapper/0-1 [000] d..1. 56.286597: 0) | 0xffffffff92db5304() { It now shows: # trace-cmd show -B boot_mapped | tail -n30 swapper/0-1 [000] d..2. 363.079099: 0) 0.483 us | preempt_count_sub(); swapper/0-1 [000] d.... 363.079100: 0) 4.112 us | } swapper/0-1 [000] d.... 363.079101: 0) 4.979 us | } swapper/0-1 [000] d..1. 363.079101: 0) | disable_local_APIC() { swapper/0-1 [000] d..1. 363.079102: 0) + 29.153 us | clear_local_APIC.part.0(); swapper/0-1 [000] d.... 363.079148: 0) + 46.517 us | } swapper/0-1 [000] d..1. 363.079149: 0) | mcheck_cpu_clear() { swapper/0-1 [000] d..1. 363.079149: 0) | mce_intel_feature_clear() { swapper/0-1 [000] d..1. 363.079150: 0) 5.871 us | lmce_supported(); swapper/0-1 [000] d.... 363.079161: 0) + 11.340 us | } swapper/0-1 [000] d.... 363.079161: 0) + 12.638 us | } swapper/0-1 [000] d.... 363.079162: 0) ! 383.518 us | } swapper/0-1 [000] d..1. 363.079162: 0) | lapic_shutdown() { swapper/0-1 [000] d..1. 363.079163: 0) | disable_local_APIC() { swapper/0-1 [000] d..1. 363.079163: 0) + 26.144 us | clear_local_APIC.part.0(); swapper/0-1 [000] d.... 363.079192: 0) + 29.424 us | } swapper/0-1 [000] d.... 363.079192: 0) + 30.376 us | } swapper/0-1 [000] d..1. 363.079193: 0) | restore_boot_irq_mode() { swapper/0-1 [000] d..1. 363.079194: 0) | native_restore_boot_irq_mode() { swapper/0-1 [000] d..1. 363.079194: 0) + 13.863 us | disconnect_bsp_APIC(); swapper/0-1 [000] d.... 363.079209: 0) + 14.933 us | } swapper/0-1 [000] d.... 363.079209: 0) + 16.009 us | } swapper/0-1 [000] d..1. 363.079210: 0) 0.694 us | hpet_disable(); swapper/0-1 [000] d..1. 363.079211: 0) 0.511 us | iommu_shutdown_noop(); swapper/0-1 [000] d.... 363.079212: 0) # 3980.260 us | } swapper/0-1 [000] d..1. 363.079212: 0) | native_machine_emergency_restart() { swapper/0-1 [000] d..1. 363.079213: 0) 0.495 us | tboot_shutdown(); swapper/0-1 [000] d..1. 363.079230: 0) | acpi_reboot() { swapper/0-1 [000] d..1. 363.079231: 0) | acpi_reset() { swapper/0-1 [000] d..1. 363.079232: 0) | acpi_os_write_port() { Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Linus Torvalds Cc: Ross Zwisler Cc: Vincent Donnefort Link: https://lore.kernel.org/20240813171257.478901820@goodmis.org Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace_functions_graph.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c index 13d0387ac6a6..a569daaac4c4 100644 --- a/kernel/trace/trace_functions_graph.c +++ b/kernel/trace/trace_functions_graph.c @@ -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 */ From ac01c8c4246546fd8340a232f3ada1921dc0ee48 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 15 Aug 2024 15:22:12 +0100 Subject: [PATCH 165/347] perf hist: Update hist symbol when updating maps AddressSanitizer found a use-after-free bug in the symbol code which manifested as 'perf top' segfaulting. ==1238389==ERROR: AddressSanitizer: heap-use-after-free on address 0x60b00c48844b at pc 0x5650d8035961 bp 0x7f751aaecc90 sp 0x7f751aaecc80 READ of size 1 at 0x60b00c48844b thread T193 #0 0x5650d8035960 in _sort__sym_cmp util/sort.c:310 #1 0x5650d8043744 in hist_entry__cmp util/hist.c:1286 #2 0x5650d8043951 in hists__findnew_entry util/hist.c:614 #3 0x5650d804568f in __hists__add_entry util/hist.c:754 #4 0x5650d8045bf9 in hists__add_entry util/hist.c:772 #5 0x5650d8045df1 in iter_add_single_normal_entry util/hist.c:997 #6 0x5650d8043326 in hist_entry_iter__add util/hist.c:1242 #7 0x5650d7ceeefe in perf_event__process_sample /home/matt/src/linux/tools/perf/builtin-top.c:845 #8 0x5650d7ceeefe in deliver_event /home/matt/src/linux/tools/perf/builtin-top.c:1208 #9 0x5650d7fdb51b in do_flush util/ordered-events.c:245 #10 0x5650d7fdb51b in __ordered_events__flush util/ordered-events.c:324 #11 0x5650d7ced743 in process_thread /home/matt/src/linux/tools/perf/builtin-top.c:1120 #12 0x7f757ef1f133 in start_thread nptl/pthread_create.c:442 #13 0x7f757ef9f7db in clone3 ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81 When updating hist maps it's also necessary to update the hist symbol reference because the old one gets freed in map__put(). While this bug was probably introduced with 5c24b67aae72f54c ("perf tools: Replace map->referenced & maps->removed_maps with map->refcnt"), the symbol objects were leaked until c087e9480cf33672 ("perf machine: Fix refcount usage when processing PERF_RECORD_KSYMBOL") was merged so the bug was masked. Fixes: c087e9480cf33672 ("perf machine: Fix refcount usage when processing PERF_RECORD_KSYMBOL") Reported-by: Yunzhao Li Signed-off-by: Matt Fleming (Cloudflare) Cc: Ian Rogers Cc: kernel-team@cloudflare.com Cc: Namhyung Kim Cc: Riccardo Mancini Cc: stable@vger.kernel.org # v5.13+ Link: https://lore.kernel.org/r/20240815142212.3834625-1-matt@readmodwrite.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 0f554febf9a1..0f9ce2ee2c31 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -639,6 +639,11 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists, * the history counter to increment. */ if (he->ms.map != entry->ms.map) { + if (he->ms.sym) { + u64 addr = he->ms.sym->start; + he->ms.sym = map__find_symbol(entry->ms.map, addr); + } + map__put(he->ms.map); he->ms.map = map__get(entry->ms.map); } From b53f20b323ee3c5daa5494ad69e3ae0e77cfd82a Mon Sep 17 00:00:00 2001 From: Alexander Gordeev Date: Thu, 15 Aug 2024 09:20:46 +0200 Subject: [PATCH 166/347] tools build: Provide consistent build options for fixdep The fixdep binary is being compiled and linked in one step. While the host linker flags are passed to the compiler the host compiler flags are missed. That leads to build errors at least on x86_64, arm64 and s390 as result of the compiler vs linker flags inconsistency. For example, during RPM package build redhat-hardened-ld script is provided to gcc, while redhat-hardened-cc1 script is missed. Provide both KBUILD_HOSTCFLAGS and KBUILD_HOSTLDFLAGS to avoid that. Fixes: ea974028a049f2ce ("tools build: Avoid circular .fixdep-in.o.cmd issues") Closes: https://lore.kernel.org/lkml/99ae0d34-ed76-4ca0-a9fd-c337da33c9f9@leemhuis.info/ Reported-by: Thorsten Leemhuis Reviewed-by: Brian Norris Signed-off-by: Alexander Gordeev Tested-by: Thorsten Leemhuis Acked-by: Jiri Olsa Link: https://lore.kernel.org/r/20240815072046.1002837-1-agordeev@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/build/Makefile b/tools/build/Makefile index fea3cf647f5b..18ad131f6ea7 100644 --- a/tools/build/Makefile +++ b/tools/build/Makefile @@ -44,4 +44,4 @@ ifneq ($(wildcard $(TMP_O)),) endif $(OUTPUT)fixdep: $(srctree)/tools/build/fixdep.c - $(QUIET_CC)$(HOSTCC) $(KBUILD_HOSTLDFLAGS) -o $@ $< + $(QUIET_CC)$(HOSTCC) $(KBUILD_HOSTCFLAGS) $(KBUILD_HOSTLDFLAGS) -o $@ $< From 6bdf5168b6fb19541b0c1862bdaa596d116c7bfb Mon Sep 17 00:00:00 2001 From: Yang Jihong Date: Tue, 6 Aug 2024 10:35:33 +0800 Subject: [PATCH 167/347] perf sched timehist: Fix missing free of session in perf_sched__timehist() When perf_time__parse_str() fails in perf_sched__timehist(), need to free session that was previously created, fix it. Fixes: 853b74071110bed3 ("perf sched timehist: Add option to specify time window of interest") Signed-off-by: Yang Jihong Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: David Ahern Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240806023533.1316348-1-yangjihong@bytedance.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 34fe8e540c43..9c1276dc8ef6 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3120,7 +3120,8 @@ 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) From 2615639352420e6e3115952c5b8f46846e1c6d0e Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Fri, 2 Aug 2024 14:58:00 +0800 Subject: [PATCH 168/347] perf stat: Display iostat headers correctly Currently we'll only print metric headers for metric leader in aggregration mode. This will make `perf iostat` header not shown since it'll aggregrated globally but don't have metric events: root@ubuntu204:/home/yang/linux/tools/perf# ./perf stat --iostat --timeout 1000 Performance counter stats for 'system wide': port 0000:00 0 0 0 0 0000:80 0 0 0 0 [...] Fix this by excluding the iostat in the check of printing metric headers. Then we can see the headers: root@ubuntu204:/home/yang/linux/tools/perf# ./perf stat --iostat --timeout 1000 Performance counter stats for 'system wide': port Inbound Read(MB) Inbound Write(MB) Outbound Read(MB) Outbound Write(MB) 0000:00 0 0 0 0 0000:80 0 0 0 0 [...] Fixes: 193a9e30207f5477 ("perf stat: Don't display metric header for non-leader uncore events") Signed-off-by: Yicong Yang Acked-by: Namhyung Kim Cc: Ian Rogers Cc: Ingo Molnar Cc: Jonathan Cameron Cc: Junhao He Cc: linuxarm@huawei.com Cc: Mark Rutland Cc: Peter Zijlstra Cc: Shameerali Kolothum Thodi Cc: Zeng Tao Link: https://lore.kernel.org/r/20240802065800.48774-1-yangyicong@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/stat-display.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/stat-display.c b/tools/perf/util/stat-display.c index c38bcb6f4c78..ea96e4ebad8c 100644 --- a/tools/perf/util/stat-display.c +++ b/tools/perf/util/stat-display.c @@ -1237,7 +1237,8 @@ static void print_metric_headers(struct perf_stat_config *config, /* Print metrics headers only */ evlist__for_each_entry(evlist, counter) { - if (config->aggr_mode != AGGR_NONE && counter->metric_leader != counter) + if (!config->iostat_run && + config->aggr_mode != AGGR_NONE && counter->metric_leader != counter) continue; os.evsel = counter; From e8bb03ed6850c6ed4ce2f1600ea73401fc2ebd95 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 16 Aug 2024 16:58:31 -0700 Subject: [PATCH 169/347] perf dwarf-aux: Check allowed location expressions when collecting variables It missed to call check_allowed_ops() in __die_collect_vars_cb() so it can take variables with complex location expression incorrectly. For example, I found some variable has this expression. 015d8df8 ffffffff81aacfb3 (base address) 015d8e01 v000000000000004 v000000000000000 views at 015d8df2 for: ffffffff81aacfb3 ffffffff81aacfd2 (DW_OP_fbreg: -176; DW_OP_deref; DW_OP_plus_uconst: 332; DW_OP_deref_size: 4; DW_OP_lit1; DW_OP_shra; DW_OP_const1u: 64; DW_OP_minus; DW_OP_stack_value) 015d8e14 v000000000000000 v000000000000000 views at 015d8df4 for: ffffffff81aacfd2 ffffffff81aacfd7 (DW_OP_reg3 (rbx)) 015d8e19 v000000000000000 v000000000000000 views at 015d8df6 for: ffffffff81aacfd7 ffffffff81aad020 (DW_OP_fbreg: -176; DW_OP_deref; DW_OP_plus_uconst: 332; DW_OP_deref_size: 4; DW_OP_lit1; DW_OP_shra; DW_OP_const1u: 64; DW_OP_minus; DW_OP_stack_value) 015d8e2c It looks like '((int *)(-176(%rbp) + 332) >> 1) - 64' but the current code thought it's just -176(%rbp) and processed the variable incorrectly. It should reject such a complex expression if check_allowed_ops() doesn't like it. :) Fixes: 932dcc2c39aedf54 ("perf dwarf-aux: Add die_collect_vars()") Signed-off-by: Namhyung Kim Acked-by: Masami Hiramatsu Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240816235840.2754937-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dwarf-aux.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 5e080d7e22c2..beb632153a74 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -1598,6 +1598,9 @@ static int __die_collect_vars_cb(Dwarf_Die *die_mem, void *arg) if (dwarf_getlocations(&attr, 0, &base, &start, &end, &ops, &nops) <= 0) return DIE_FIND_CB_SIBLING; + if (!check_allowed_ops(ops, nops)) + return DIE_FIND_CB_SIBLING; + if (die_get_real_type(die_mem, &type_die) == NULL) return DIE_FIND_CB_SIBLING; From 3ab0b8b238b5130ae3fa37ddaa329fc0e93b6b9a Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 16 Aug 2024 16:58:32 -0700 Subject: [PATCH 170/347] perf annotate-data: Fix off-by-one in location range check The location list will have entries with half-open addressing like [start, end) which means it doesn't include the end address. So it should skip entries at the end address and match to the next entry. An example location list looks like this (from readelf -wo): 00237876 ffffffff8110d32b (base address) 0023787f v000000000000000 v000000000000002 views at 00237868 for: ffffffff8110d32b ffffffff8110d4eb (DW_OP_reg3 (rbx)) <<<--- 1 00237885 v000000000000002 v000000000000000 views at 0023786a for: ffffffff8110d4eb ffffffff8110d50b (DW_OP_reg14 (r14)) <<<--- 2 0023788c v000000000000000 v000000000000001 views at 0023786c for: ffffffff8110d50b ffffffff8110d7c4 (DW_OP_reg3 (rbx)) 00237893 v000000000000000 v000000000000000 views at 0023786e for: ffffffff8110d806 ffffffff8110d854 (DW_OP_reg3 (rbx)) 0023789a v000000000000000 v000000000000000 views at 00237870 for: ffffffff8110d876 ffffffff8110d88e (DW_OP_reg3 (rbx)) The first entry at 0023787f has [8110d32b, 8110d4eb) (omitting the ffffffff at the beginning), and the second one has [8110d4eb, 8110d50b). Fixes: 2bc3cf575a162a2c ("perf annotate-data: Improve debug message with location info") Reviewed-by: Masami Hiramatsu Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240816235840.2754937-3-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 2 +- tools/perf/util/dwarf-aux.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index ff85d190e3ac..fd8d3cdead5a 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -95,7 +95,7 @@ static void pr_debug_location(Dwarf_Die *die, u64 pc, int reg) return; while ((off = dwarf_getlocations(&attr, off, &base, &start, &end, &ops, &nops)) > 0) { - if (reg != DWARF_REG_PC && end < pc) + if (reg != DWARF_REG_PC && end <= pc) continue; if (reg != DWARF_REG_PC && start > pc) break; diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index beb632153a74..0151a8d14350 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -1444,7 +1444,7 @@ static int __die_find_var_reg_cb(Dwarf_Die *die_mem, void *arg) while ((off = dwarf_getlocations(&attr, off, &base, &start, &end, &ops, &nops)) > 0) { /* Assuming the location list is sorted by address */ - if (end < data->pc) + if (end <= data->pc) continue; if (start > data->pc) break; From 976862f8abefafc42a27fff4c0e5d56cfc8d8ef4 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 16 Aug 2024 16:58:33 -0700 Subject: [PATCH 171/347] perf annotate-data: Add 'enum type_match_result' And let check_variable() return the enum value so that callers can know what was the problem. This will be used by the later patch to update the statistics correctly and print the error message in a right place. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240816235840.2754937-4-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 36 +++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index fd8d3cdead5a..8e3b422eca22 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -345,9 +345,20 @@ static bool find_cu_die(struct debuginfo *di, u64 pc, Dwarf_Die *cu_die) return false; } +enum type_match_result { + PERF_TMR_UNKNOWN = 0, + PERF_TMR_OK, + PERF_TMR_NO_TYPE, + PERF_TMR_NO_POINTER, + PERF_TMR_NO_SIZE, + PERF_TMR_BAD_OFFSET, +}; + /* The type info will be saved in @type_die */ -static int check_variable(struct data_loc_info *dloc, Dwarf_Die *var_die, - Dwarf_Die *type_die, int reg, int offset, bool is_fbreg) +static enum type_match_result check_variable(struct data_loc_info *dloc, + Dwarf_Die *var_die, + Dwarf_Die *type_die, int reg, + int offset, bool is_fbreg) { Dwarf_Word size; bool is_pointer = true; @@ -364,7 +375,7 @@ static int check_variable(struct data_loc_info *dloc, Dwarf_Die *var_die, if (__die_get_real_type(var_die, type_die) == NULL) { pr_debug_dtp("variable has no type\n"); ann_data_stat.no_typeinfo++; - return -1; + return PERF_TMR_NO_TYPE; } /* @@ -378,7 +389,7 @@ static int check_variable(struct data_loc_info *dloc, Dwarf_Die *var_die, __die_get_real_type(type_die, type_die) == NULL) { pr_debug_dtp("no pointer or no type\n"); ann_data_stat.no_typeinfo++; - return -1; + return PERF_TMR_NO_POINTER; } } @@ -391,7 +402,7 @@ static int check_variable(struct data_loc_info *dloc, Dwarf_Die *var_die, if (dwarf_aggregate_size(&sized_type, &size) < 0) { pr_debug_dtp("type size is unknown\n"); ann_data_stat.invalid_size++; - return -1; + return PERF_TMR_NO_SIZE; } /* Minimal sanity check */ @@ -399,10 +410,10 @@ static int check_variable(struct data_loc_info *dloc, Dwarf_Die *var_die, pr_debug_dtp("offset: %d is bigger than size: %"PRIu64"\n", offset, size); ann_data_stat.bad_offset++; - return -1; + return PERF_TMR_BAD_OFFSET; } - return 0; + return PERF_TMR_OK; } struct type_state_stack *find_stack_state(struct type_state *state, @@ -652,7 +663,7 @@ bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc, /* Try to get the variable by address first */ if (die_find_variable_by_addr(cu_die, var_addr, &var_die, &offset) && check_variable(dloc, &var_die, type_die, DWARF_REG_PC, offset, - /*is_fbreg=*/false) == 0) { + /*is_fbreg=*/false) == PERF_TMR_OK) { var_name = dwarf_diename(&var_die); *var_offset = offset; goto ok; @@ -666,7 +677,7 @@ bool get_global_var_type(Dwarf_Die *cu_die, struct data_loc_info *dloc, /* Try to get the name of global variable */ if (die_find_variable_at(cu_die, var_name, pc, &var_die) && check_variable(dloc, &var_die, type_die, DWARF_REG_PC, *var_offset, - /*is_fbreg=*/false) == 0) + /*is_fbreg=*/false) == PERF_TMR_OK) goto ok; return false; @@ -1205,6 +1216,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) bool is_fbreg = false; u64 pc; char buf[64]; + enum type_match_result result; if (dloc->op->multi_regs) snprintf(buf, sizeof(buf), "reg%d, reg%d", dloc->op->reg1, dloc->op->reg2); @@ -1299,8 +1311,8 @@ retry: } /* Found a variable, see if it's correct */ - ret = check_variable(dloc, &var_die, type_die, reg, offset, is_fbreg); - if (ret == 0) { + result = check_variable(dloc, &var_die, type_die, reg, offset, is_fbreg); + if (result == PERF_TMR_OK) { pr_debug_dtp("found \"%s\" in scope=%d/%d (die: %#lx) ", dwarf_diename(&var_die), i+1, nr_scopes, (long)dwarf_dieoffset(&scopes[i])); @@ -1315,12 +1327,14 @@ retry: } pr_debug_location(&var_die, pc, reg); pr_debug_type_name(type_die, TSR_KIND_TYPE); + ret = 0; } else { pr_debug_dtp("check variable \"%s\" failed (die: %#lx)\n", dwarf_diename(&var_die), (long)dwarf_dieoffset(&var_die)); pr_debug_location(&var_die, pc, reg); pr_debug_type_name(type_die, TSR_KIND_TYPE); + ret = -1; } dloc->type_offset = offset; goto out; From 653185d808ea6f5e3e6247087b793fcd8510bb26 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 16 Aug 2024 16:58:34 -0700 Subject: [PATCH 172/347] perf annotate-data: Add variable_state_str() So that it can show a proper debug message in the right place. The check_variable() is used in other places which don't want to print the message. $ perf --debug type-profile annotate --data-type Before: ----------------------------------------------------------- find data type for 0x140(reg14) at update_blocked_averages+0x2db CU for kernel/sched/fair.c (die:0x12dd892) frame base: cfa=1 fbreg=7 no pointer or no type <<<--- removed check variable "__mptr" failed (die: 0x13022f1) variable location: base=reg14, offset=0x140 type='void*' size=0x8 (die:0x12dd8f9) After: ----------------------------------------------------------- find data type for 0x140(reg14) at update_blocked_averages+0x2db CU for kernel/sched/fair.c (die:0x12dd892) frame base: cfa=1 fbreg=7 found "__mptr" (die: 0x13022f1) in scope=4/4 (die: 0x13022e8) failed: no/void pointer <<<--- here variable location: base=reg14, offset=0x140 type='void*' size=0x8 (die:0x12dd8f9) Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240816235840.2754937-5-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 41 +++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 8e3b422eca22..332254da49be 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -354,6 +354,25 @@ enum type_match_result { PERF_TMR_BAD_OFFSET, }; +static const char *match_result_str(enum type_match_result tmr) +{ + switch (tmr) { + case PERF_TMR_OK: + return "Good!"; + case PERF_TMR_NO_TYPE: + return "no type information"; + case PERF_TMR_NO_POINTER: + return "no/void pointer"; + case PERF_TMR_NO_SIZE: + return "type size is unknown"; + case PERF_TMR_BAD_OFFSET: + return "offset bigger than size"; + case PERF_TMR_UNKNOWN: + default: + return "invalid state"; + } +} + /* The type info will be saved in @type_die */ static enum type_match_result check_variable(struct data_loc_info *dloc, Dwarf_Die *var_die, @@ -373,7 +392,6 @@ static enum type_match_result check_variable(struct data_loc_info *dloc, /* Get the type of the variable */ if (__die_get_real_type(var_die, type_die) == NULL) { - pr_debug_dtp("variable has no type\n"); ann_data_stat.no_typeinfo++; return PERF_TMR_NO_TYPE; } @@ -387,7 +405,6 @@ static enum type_match_result check_variable(struct data_loc_info *dloc, if ((dwarf_tag(type_die) != DW_TAG_pointer_type && dwarf_tag(type_die) != DW_TAG_array_type) || __die_get_real_type(type_die, type_die) == NULL) { - pr_debug_dtp("no pointer or no type\n"); ann_data_stat.no_typeinfo++; return PERF_TMR_NO_POINTER; } @@ -400,15 +417,12 @@ static enum type_match_result check_variable(struct data_loc_info *dloc, /* Get the size of the actual type */ if (dwarf_aggregate_size(&sized_type, &size) < 0) { - pr_debug_dtp("type size is unknown\n"); ann_data_stat.invalid_size++; return PERF_TMR_NO_SIZE; } /* Minimal sanity check */ if ((unsigned)offset >= size) { - pr_debug_dtp("offset: %d is bigger than size: %"PRIu64"\n", - offset, size); ann_data_stat.bad_offset++; return PERF_TMR_BAD_OFFSET; } @@ -1310,12 +1324,13 @@ retry: continue; } + pr_debug_dtp("found \"%s\" (die: %#lx) in scope=%d/%d (die: %#lx) ", + dwarf_diename(&var_die), (long)dwarf_dieoffset(&var_die), + i+1, nr_scopes, (long)dwarf_dieoffset(&scopes[i])); + /* Found a variable, see if it's correct */ result = check_variable(dloc, &var_die, type_die, reg, offset, is_fbreg); if (result == PERF_TMR_OK) { - pr_debug_dtp("found \"%s\" in scope=%d/%d (die: %#lx) ", - dwarf_diename(&var_die), i+1, nr_scopes, - (long)dwarf_dieoffset(&scopes[i])); if (reg == DWARF_REG_PC) { pr_debug_dtp("addr=%#"PRIx64" type_offset=%#x\n", dloc->var_addr, offset); @@ -1325,17 +1340,13 @@ retry: } else { pr_debug_dtp("type_offset=%#x\n", offset); } - pr_debug_location(&var_die, pc, reg); - pr_debug_type_name(type_die, TSR_KIND_TYPE); ret = 0; } else { - pr_debug_dtp("check variable \"%s\" failed (die: %#lx)\n", - dwarf_diename(&var_die), - (long)dwarf_dieoffset(&var_die)); - pr_debug_location(&var_die, pc, reg); - pr_debug_type_name(type_die, TSR_KIND_TYPE); + pr_debug_dtp("failed: %s\n", match_result_str(result)); ret = -1; } + pr_debug_location(&var_die, pc, reg); + pr_debug_type_name(type_die, TSR_KIND_TYPE); dloc->type_offset = offset; goto out; } From 69e2c78425c92361f0ab01cbbb8f3a44fc94341f Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 16 Aug 2024 16:58:35 -0700 Subject: [PATCH 173/347] perf annotate-data: Change return type of find_data_type_block() So that it can return enum variable_match_type to be propagated to the find_data_type_die(). Also update the debug message to show the result of the check_matching_type(). chk [dd] reg0 offset=0 ok=1 kind=1 : Good! or chk [177] reg4 offset=0x138 ok=0 kind=0 cfa : no type information Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240816235840.2754937-6-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 117 ++++++++++++++++---------------- 1 file changed, 58 insertions(+), 59 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 332254da49be..5548cd8e84ba 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -352,6 +352,7 @@ enum type_match_result { PERF_TMR_NO_POINTER, PERF_TMR_NO_SIZE, PERF_TMR_BAD_OFFSET, + PERF_TMR_BAIL_OUT, }; static const char *match_result_str(enum type_match_result tmr) @@ -368,6 +369,7 @@ static const char *match_result_str(enum type_match_result tmr) case PERF_TMR_BAD_OFFSET: return "offset bigger than size"; case PERF_TMR_UNKNOWN: + case PERF_TMR_BAIL_OUT: default: return "invalid state"; } @@ -868,19 +870,19 @@ static void setup_stack_canary(struct data_loc_info *dloc) /* * It's at the target address, check if it has a matching type. - * It returns 1 if found, 0 if not or -1 if not found but no need to - * repeat the search. The last case is for per-cpu variables which + * It returns PERF_TMR_BAIL_OUT when it looks up per-cpu variables which * are similar to global variables and no additional info is needed. */ -static int check_matching_type(struct type_state *state, - struct data_loc_info *dloc, - Dwarf_Die *cu_die, Dwarf_Die *type_die) +static enum type_match_result check_matching_type(struct type_state *state, + struct data_loc_info *dloc, + Dwarf_Die *cu_die, + Dwarf_Die *type_die) { Dwarf_Word size; u32 insn_offset = dloc->ip - dloc->ms->sym->start; int reg = dloc->op->reg1; - pr_debug_dtp("chk [%x] reg%d offset=%#x ok=%d kind=%d", + pr_debug_dtp("chk [%x] reg%d offset=%#x ok=%d kind=%d ", insn_offset, reg, dloc->op->offset, state->regs[reg].ok, state->regs[reg].kind); @@ -896,15 +898,12 @@ static int check_matching_type(struct type_state *state, if (dloc->op->offset < 0 && reg != state->stack_reg) goto check_kernel; - pr_debug_dtp("\n"); - return -1; + return PERF_TMR_NO_POINTER; } - pr_debug_dtp("\n"); - /* Remove the pointer and get the target type */ if (__die_get_real_type(&state->regs[reg].type, type_die) == NULL) - return -1; + return PERF_TMR_NO_POINTER; dloc->type_offset = dloc->op->offset; @@ -916,33 +915,33 @@ static int check_matching_type(struct type_state *state, /* Get the size of the actual type */ if (dwarf_aggregate_size(&sized_type, &size) < 0 || (unsigned)dloc->type_offset >= size) - return -1; + return PERF_TMR_BAD_OFFSET; - return 1; + return PERF_TMR_OK; } if (reg == dloc->fbreg) { struct type_state_stack *stack; - pr_debug_dtp(" fbreg\n"); + pr_debug_dtp("fbreg"); stack = find_stack_state(state, dloc->type_offset); if (stack == NULL) - return 0; + return PERF_TMR_NO_TYPE; if (stack->kind == TSR_KIND_CANARY) { setup_stack_canary(dloc); - return -1; + return PERF_TMR_BAIL_OUT; } if (stack->kind != TSR_KIND_TYPE) - return 0; + return PERF_TMR_NO_TYPE; *type_die = stack->type; /* Update the type offset from the start of slot */ dloc->type_offset -= stack->offset; - return 1; + return PERF_TMR_OK; } if (dloc->fb_cfa) { @@ -950,38 +949,38 @@ static int check_matching_type(struct type_state *state, u64 pc = map__rip_2objdump(dloc->ms->map, dloc->ip); int fbreg, fboff; - pr_debug_dtp(" cfa\n"); + pr_debug_dtp("cfa"); if (die_get_cfa(dloc->di->dbg, pc, &fbreg, &fboff) < 0) fbreg = -1; if (reg != fbreg) - return 0; + return PERF_TMR_NO_TYPE; stack = find_stack_state(state, dloc->type_offset - fboff); if (stack == NULL) - return 0; + return PERF_TMR_NO_TYPE; if (stack->kind == TSR_KIND_CANARY) { setup_stack_canary(dloc); - return -1; + return PERF_TMR_BAIL_OUT; } if (stack->kind != TSR_KIND_TYPE) - return 0; + return PERF_TMR_NO_TYPE; *type_die = stack->type; /* Update the type offset from the start of slot */ dloc->type_offset -= fboff + stack->offset; - return 1; + return PERF_TMR_OK; } if (state->regs[reg].kind == TSR_KIND_PERCPU_BASE) { u64 var_addr = dloc->op->offset; int var_offset; - pr_debug_dtp(" percpu var\n"); + pr_debug_dtp("percpu var"); if (dloc->op->multi_regs) { int reg2 = dloc->op->reg2; @@ -997,14 +996,14 @@ static int check_matching_type(struct type_state *state, if (get_global_var_type(cu_die, dloc, dloc->ip, var_addr, &var_offset, type_die)) { dloc->type_offset = var_offset; - return 1; + return PERF_TMR_OK; } /* No need to retry per-cpu (global) variables */ - return -1; + return PERF_TMR_BAIL_OUT; } if (state->regs[reg].ok && state->regs[reg].kind == TSR_KIND_POINTER) { - pr_debug_dtp(" percpu ptr\n"); + pr_debug_dtp("percpu ptr"); /* * It's actaully pointer but the address was calculated using @@ -1017,13 +1016,13 @@ static int check_matching_type(struct type_state *state, /* Get the size of the actual type */ if (dwarf_aggregate_size(type_die, &size) < 0 || (unsigned)dloc->type_offset >= size) - return -1; + return PERF_TMR_BAIL_OUT; - return 1; + return PERF_TMR_OK; } if (state->regs[reg].ok && state->regs[reg].kind == TSR_KIND_CANARY) { - pr_debug_dtp(" stack canary\n"); + pr_debug_dtp("stack canary"); /* * This is a saved value of the stack canary which will be handled @@ -1032,7 +1031,7 @@ static int check_matching_type(struct type_state *state, */ setup_stack_canary(dloc); - return -1; + return PERF_TMR_BAIL_OUT; } check_kernel: @@ -1043,16 +1042,16 @@ check_kernel: /* Direct this-cpu access like "%gs:0x34740" */ if (dloc->op->segment == INSN_SEG_X86_GS && dloc->op->imm && arch__is(dloc->arch, "x86")) { - pr_debug_dtp(" this-cpu var\n"); + pr_debug_dtp("this-cpu var"); addr = dloc->op->offset; if (get_global_var_type(cu_die, dloc, dloc->ip, addr, &offset, type_die)) { dloc->type_offset = offset; - return 1; + return PERF_TMR_OK; } - return -1; + return PERF_TMR_BAIL_OUT; } /* Access to global variable like "-0x7dcf0500(,%rdx,8)" */ @@ -1061,31 +1060,30 @@ check_kernel: if (get_global_var_type(cu_die, dloc, dloc->ip, addr, &offset, type_die)) { - pr_debug_dtp(" global var\n"); + pr_debug_dtp("global var"); dloc->type_offset = offset; - return 1; + return PERF_TMR_OK; } - pr_debug_dtp(" negative offset\n"); - return -1; + return PERF_TMR_BAIL_OUT; } } - pr_debug_dtp("\n"); - return 0; + return PERF_TMR_UNKNOWN; } /* Iterate instructions in basic blocks and update type table */ -static int find_data_type_insn(struct data_loc_info *dloc, - struct list_head *basic_blocks, - struct die_var_type *var_types, - Dwarf_Die *cu_die, Dwarf_Die *type_die) +static enum type_match_result find_data_type_insn(struct data_loc_info *dloc, + struct list_head *basic_blocks, + struct die_var_type *var_types, + Dwarf_Die *cu_die, + Dwarf_Die *type_die) { struct type_state state; struct symbol *sym = dloc->ms->sym; struct annotation *notes = symbol__annotation(sym); struct annotated_basic_block *bb; - int ret = 0; + enum type_match_result ret = PERF_TMR_UNKNOWN; init_type_state(&state, dloc->arch); @@ -1111,6 +1109,7 @@ static int find_data_type_insn(struct data_loc_info *dloc, if (this_ip == dloc->ip) { ret = check_matching_type(&state, dloc, cu_die, type_die); + pr_debug_dtp(" : %s\n", match_result_str(ret)); goto out; } @@ -1137,24 +1136,25 @@ static int arch_supports_insn_tracking(struct data_loc_info *dloc) * Construct a list of basic blocks for each scope with variables and try to find * the data type by updating a type state table through instructions. */ -static int find_data_type_block(struct data_loc_info *dloc, - Dwarf_Die *cu_die, Dwarf_Die *scopes, - int nr_scopes, Dwarf_Die *type_die) +static enum type_match_result find_data_type_block(struct data_loc_info *dloc, + Dwarf_Die *cu_die, + Dwarf_Die *scopes, + int nr_scopes, + Dwarf_Die *type_die) { LIST_HEAD(basic_blocks); struct die_var_type *var_types = NULL; u64 src_ip, dst_ip, prev_dst_ip; - int ret = -1; + enum type_match_result ret = PERF_TMR_UNKNOWN; /* TODO: other architecture support */ if (!arch_supports_insn_tracking(dloc)) - return -1; + return PERF_TMR_BAIL_OUT; prev_dst_ip = dst_ip = dloc->ip; for (int i = nr_scopes - 1; i >= 0; i--) { Dwarf_Addr base, start, end; LIST_HEAD(this_blocks); - int found; if (dwarf_ranges(&scopes[i], 0, &base, &start, &end) < 0) break; @@ -1185,9 +1185,9 @@ again: fixup_var_address(var_types, start); /* Find from start of this scope to the target instruction */ - found = find_data_type_insn(dloc, &basic_blocks, var_types, + ret = find_data_type_insn(dloc, &basic_blocks, var_types, cu_die, type_die); - if (found > 0) { + if (ret == PERF_TMR_OK) { char buf[64]; if (dloc->op->multi_regs) @@ -1199,11 +1199,10 @@ again: pr_debug_dtp("found by insn track: %#x(%s) type-offset=%#x\n", dloc->op->offset, buf, dloc->type_offset); pr_debug_type_name(type_die, TSR_KIND_TYPE); - ret = 0; break; } - if (found < 0) + if (ret == PERF_TMR_BAIL_OUT) break; /* Go up to the next scope and find blocks to the start */ @@ -1357,11 +1356,11 @@ retry: } if (reg != DWARF_REG_PC) { - ret = find_data_type_block(dloc, &cu_die, scopes, + result = find_data_type_block(dloc, &cu_die, scopes, nr_scopes, type_die); - if (ret == 0) { + if (result == PERF_TMR_OK) { ann_data_stat.insn_track++; - goto out; + ret = 0; } } From 98d1f1dc72fd6e0e85db4acc0b2dd591f4488216 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 16 Aug 2024 16:58:36 -0700 Subject: [PATCH 174/347] perf annotate-data: Add is_pointer_type() helper It treats pointers and arrays in the same way. Let's add the helper and use it when it checks if it needs a pointer. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240816235840.2754937-7-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 5548cd8e84ba..b2414747cdeb 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -375,6 +375,13 @@ static const char *match_result_str(enum type_match_result tmr) } } +static bool is_pointer_type(Dwarf_Die *type_die) +{ + int tag = dwarf_tag(type_die); + + return tag == DW_TAG_pointer_type || tag == DW_TAG_array_type; +} + /* The type info will be saved in @type_die */ static enum type_match_result check_variable(struct data_loc_info *dloc, Dwarf_Die *var_die, @@ -382,15 +389,15 @@ static enum type_match_result check_variable(struct data_loc_info *dloc, int offset, bool is_fbreg) { Dwarf_Word size; - bool is_pointer = true; + bool needs_pointer = true; Dwarf_Die sized_type; if (reg == DWARF_REG_PC) - is_pointer = false; + needs_pointer = false; else if (reg == dloc->fbreg || is_fbreg) - is_pointer = false; + needs_pointer = false; else if (arch__is(dloc->arch, "x86") && reg == X86_REG_SP) - is_pointer = false; + needs_pointer = false; /* Get the type of the variable */ if (__die_get_real_type(var_die, type_die) == NULL) { @@ -403,9 +410,8 @@ static enum type_match_result check_variable(struct data_loc_info *dloc, * Convert to a real type it points to. But global variables * and local variables are accessed directly without a pointer. */ - if (is_pointer) { - if ((dwarf_tag(type_die) != DW_TAG_pointer_type && - dwarf_tag(type_die) != DW_TAG_array_type) || + if (needs_pointer) { + if (!is_pointer_type(type_die) || __die_get_real_type(type_die, type_die) == NULL) { ann_data_stat.no_typeinfo++; return PERF_TMR_NO_POINTER; @@ -887,14 +893,13 @@ static enum type_match_result check_matching_type(struct type_state *state, state->regs[reg].ok, state->regs[reg].kind); if (state->regs[reg].ok && state->regs[reg].kind == TSR_KIND_TYPE) { - int tag = dwarf_tag(&state->regs[reg].type); Dwarf_Die sized_type; /* * Normal registers should hold a pointer (or array) to * dereference a memory location. */ - if (tag != DW_TAG_pointer_type && tag != DW_TAG_array_type) { + if (!is_pointer_type(&state->regs[reg].type)) { if (dloc->op->offset < 0 && reg != state->stack_reg) goto check_kernel; From c663451f9239c89746b55f1a17ad62b014fa7905 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 16 Aug 2024 16:58:37 -0700 Subject: [PATCH 175/347] perf annotate-data: Add is_better_type() helper Sometimes more than one variables are located in the same register or a stack slot. Or it can overwrite existing information with others. I found this is not helpful in some cases so it needs to update the type information from the variable only if it's better. But it's hard to know which one is better, so we needs heuristics. :) As it deals with memory accesses, the location should have a pointer or something similar (like array or reference). So if it had an integer type and a variable is a pointer, we can take the variable's type to resolve the target of the access. If it has a pointer type and a variable with the same location has a different pointer type, it'll take one with bigger target type. This can be useful when the target type embeds a smaller type (like list header or RB-tree node) at the beginning so their location is same. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240816235840.2754937-8-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 61 +++++++++++++++++++++++++++------ 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index b2414747cdeb..916d26bfb9eb 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -382,6 +382,38 @@ static bool is_pointer_type(Dwarf_Die *type_die) return tag == DW_TAG_pointer_type || tag == DW_TAG_array_type; } +/* returns if Type B has better information than Type A */ +static bool is_better_type(Dwarf_Die *type_a, Dwarf_Die *type_b) +{ + Dwarf_Word size_a, size_b; + Dwarf_Die die_a, die_b; + + /* pointer type is preferred */ + if (is_pointer_type(type_a) != is_pointer_type(type_b)) + return is_pointer_type(type_b); + + if (is_pointer_type(type_b)) { + /* + * We want to compare the target type, but 'void *' can fail to + * get the target type. + */ + if (die_get_real_type(type_a, &die_a) == NULL) + return true; + if (die_get_real_type(type_b, &die_b) == NULL) + return false; + + type_a = &die_a; + type_b = &die_b; + } + + /* bigger type is preferred */ + if (dwarf_aggregate_size(type_a, &size_a) < 0 || + dwarf_aggregate_size(type_b, &size_b) < 0) + return false; + + return size_a < size_b; +} + /* The type info will be saved in @type_die */ static enum type_match_result check_variable(struct data_loc_info *dloc, Dwarf_Die *var_die, @@ -741,24 +773,33 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo if (!dwarf_offdie(dloc->di->dbg, var->die_off, &mem_die)) continue; - if (var->reg == DWARF_REG_FB) { - findnew_stack_state(state, var->offset, TSR_KIND_TYPE, + if (var->reg == DWARF_REG_FB || var->reg == fbreg) { + int offset = var->offset; + struct type_state_stack *stack; + + if (var->reg != DWARF_REG_FB) + offset -= fb_offset; + + stack = find_stack_state(state, offset); + if (stack && stack->kind == TSR_KIND_TYPE && + !is_better_type(&stack->type, &mem_die)) + continue; + + findnew_stack_state(state, offset, TSR_KIND_TYPE, &mem_die); pr_debug_dtp("var [%"PRIx64"] -%#x(stack)", - insn_offset, -var->offset); - pr_debug_type_name(&mem_die, TSR_KIND_TYPE); - } else if (var->reg == fbreg) { - findnew_stack_state(state, var->offset - fb_offset, - TSR_KIND_TYPE, &mem_die); - - pr_debug_dtp("var [%"PRIx64"] -%#x(stack)", - insn_offset, -var->offset + fb_offset); + insn_offset, -offset); pr_debug_type_name(&mem_die, TSR_KIND_TYPE); } else if (has_reg_type(state, var->reg) && var->offset == 0) { struct type_state_reg *reg; reg = &state->regs[var->reg]; + + if (reg->ok && reg->kind == TSR_KIND_TYPE && + !is_better_type(®->type, &mem_die)) + continue; + reg->type = mem_die; reg->kind = TSR_KIND_TYPE; reg->ok = true; From ba8833703b49451453d97bf6414a522293f7e31d Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 16 Aug 2024 16:58:38 -0700 Subject: [PATCH 176/347] perf annotate-data: Check variables in every scope Sometimes it matches a variable in the inner scope but it fails because the actual access can be on a different type. Let's try variables in every scope and choose the best one using is_better_type(). I have an example with update_blocked_averages(), at first it found a variable (__mptr) but it's a void pointer. So it moved on to the upper scope and found another variable (cfs_rq). $ perf --debug type-profile annotate --data-type --stdio ... ----------------------------------------------------------- find data type for 0x140(reg14) at update_blocked_averages+0x2db CU for kernel/sched/fair.c (die:0x12dd892) frame base: cfa=1 fbreg=7 found "__mptr" (die: 0x13022f1) in scope=4/4 (die: 0x13022e8) failed: no/void pointer variable location: base=reg14, offset=0x140 type='void*' size=0x8 (die:0x12dd8f9) found "cfs_rq" (die: 0x1301721) in scope=3/4 (die: 0x130171c) type_offset=0x140 variable location: reg14 type='struct cfs_rq' size=0x1c0 (die:0x12e37e5) final type: type='struct cfs_rq' size=0x1c0 (die:0x12e37e5) IIUC the scope is like below: 1: update_blocked_averages 2: __update_blocked_fair 3: for_each_leaf_cfs_rq_safe 4: list_entry -> (container_of) The container_of is implemented like: #define container_of(ptr, type, member) ({ \ void *__mptr = (void *)(ptr); \ static_assert(__same_type(*(ptr), ((type *)0)->member) || \ __same_type(*(ptr), void), \ "pointer type mismatch in container_of()"); \ ((type *)(__mptr - offsetof(type, member))); }) That's why we see the __mptr variable first but it failed since it has no type information. Then for_each_leaf_cfs_rq_safe() is defined as #define for_each_leaf_cfs_rq_safe(rq, cfs_rq, pos) \ list_for_each_entry_safe(cfs_rq, pos, &rq->leaf_cfs_rq_list, \ leaf_cfs_rq_list) Note that the access was 0x140(r14). And the cfs_rq has leaf_cfs_rq_list at the 0x140. So it converts the list_head pointer to a pointer to struct cfs_rq here. $ pahole --hex -C cfs_rq vmlinux | grep 140 struct cfs_rq struct list_head leaf_cfs_rq_list; /* 0x140 0x10 */ Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240816235840.2754937-9-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 44 ++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 916d26bfb9eb..e86f40fed323 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -1244,7 +1244,6 @@ again: pr_debug_dtp("found by insn track: %#x(%s) type-offset=%#x\n", dloc->op->offset, buf, dloc->type_offset); - pr_debug_type_name(type_die, TSR_KIND_TYPE); break; } @@ -1273,6 +1272,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) int fbreg = -1; int fb_offset = 0; bool is_fbreg = false; + bool found = false; u64 pc; char buf[64]; enum type_match_result result; @@ -1358,14 +1358,17 @@ retry: /* Search from the inner-most scope to the outer */ for (i = nr_scopes - 1; i >= 0; i--) { + Dwarf_Die mem_die; + int type_offset = offset; + if (reg == DWARF_REG_PC) { if (!die_find_variable_by_addr(&scopes[i], dloc->var_addr, - &var_die, &offset)) + &var_die, &type_offset)) continue; } else { /* Look up variables/parameters in this scope */ if (!die_find_variable_by_reg(&scopes[i], pc, reg, - &offset, is_fbreg, &var_die)) + &type_offset, is_fbreg, &var_die)) continue; } @@ -1374,43 +1377,50 @@ retry: i+1, nr_scopes, (long)dwarf_dieoffset(&scopes[i])); /* Found a variable, see if it's correct */ - result = check_variable(dloc, &var_die, type_die, reg, offset, is_fbreg); + result = check_variable(dloc, &var_die, &mem_die, reg, type_offset, is_fbreg); if (result == PERF_TMR_OK) { if (reg == DWARF_REG_PC) { pr_debug_dtp("addr=%#"PRIx64" type_offset=%#x\n", - dloc->var_addr, offset); + dloc->var_addr, type_offset); } else if (reg == DWARF_REG_FB || is_fbreg) { pr_debug_dtp("stack_offset=%#x type_offset=%#x\n", - fb_offset, offset); + fb_offset, type_offset); } else { - pr_debug_dtp("type_offset=%#x\n", offset); + pr_debug_dtp("type_offset=%#x\n", type_offset); + } + + if (!found || is_better_type(type_die, &mem_die)) { + *type_die = mem_die; + dloc->type_offset = type_offset; + found = true; } - ret = 0; } else { pr_debug_dtp("failed: %s\n", match_result_str(result)); - ret = -1; } + pr_debug_location(&var_die, pc, reg); - pr_debug_type_name(type_die, TSR_KIND_TYPE); - dloc->type_offset = offset; - goto out; + pr_debug_type_name(&mem_die, TSR_KIND_TYPE); } - if (loc->multi_regs && reg == loc->reg1 && loc->reg1 != loc->reg2) { + if (!found && loc->multi_regs && reg == loc->reg1 && loc->reg1 != loc->reg2) { reg = loc->reg2; goto retry; } - if (reg != DWARF_REG_PC) { + if (!found && reg != DWARF_REG_PC) { result = find_data_type_block(dloc, &cu_die, scopes, - nr_scopes, type_die); + nr_scopes, type_die); if (result == PERF_TMR_OK) { ann_data_stat.insn_track++; - ret = 0; + found = true; } } - if (ret < 0) { + if (found) { + pr_debug_dtp("final type:"); + pr_debug_type_name(type_die, TSR_KIND_TYPE); + ret = 0; + } else { pr_debug_dtp("no variable found\n"); ann_data_stat.no_var++; } From 023aceecc74ae7dd9e91c8e35d641da74a371078 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Fri, 16 Aug 2024 16:58:39 -0700 Subject: [PATCH 177/347] perf annotate-data: Update type stat at the end of find_data_type_die() After trying all possibilities with DWARF and instruction tracking. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240816235840.2754937-10-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 47 +++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index e86f40fed323..aa330c7d8edd 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -432,10 +432,8 @@ static enum type_match_result check_variable(struct data_loc_info *dloc, needs_pointer = false; /* Get the type of the variable */ - if (__die_get_real_type(var_die, type_die) == NULL) { - ann_data_stat.no_typeinfo++; + if (__die_get_real_type(var_die, type_die) == NULL) return PERF_TMR_NO_TYPE; - } /* * Usually it expects a pointer type for a memory access. @@ -444,10 +442,8 @@ static enum type_match_result check_variable(struct data_loc_info *dloc, */ if (needs_pointer) { if (!is_pointer_type(type_die) || - __die_get_real_type(type_die, type_die) == NULL) { - ann_data_stat.no_typeinfo++; + __die_get_real_type(type_die, type_die) == NULL) return PERF_TMR_NO_POINTER; - } } if (dwarf_tag(type_die) == DW_TAG_typedef) @@ -456,16 +452,12 @@ static enum type_match_result check_variable(struct data_loc_info *dloc, sized_type = *type_die; /* Get the size of the actual type */ - if (dwarf_aggregate_size(&sized_type, &size) < 0) { - ann_data_stat.invalid_size++; + if (dwarf_aggregate_size(&sized_type, &size) < 0) return PERF_TMR_NO_SIZE; - } /* Minimal sanity check */ - if ((unsigned)offset >= size) { - ann_data_stat.bad_offset++; + if ((unsigned)offset >= size) return PERF_TMR_BAD_OFFSET; - } return PERF_TMR_OK; } @@ -1275,7 +1267,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) bool found = false; u64 pc; char buf[64]; - enum type_match_result result; + enum type_match_result result = PERF_TMR_UNKNOWN; if (dloc->op->multi_regs) snprintf(buf, sizeof(buf), "reg%d, reg%d", dloc->op->reg1, dloc->op->reg2); @@ -1317,7 +1309,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) pr_debug_dtp("found by addr=%#"PRIx64" type_offset=%#x\n", dloc->var_addr, offset); pr_debug_type_name(type_die, TSR_KIND_TYPE); - ret = 0; + found = true; goto out; } } @@ -1416,16 +1408,37 @@ retry: } } +out: if (found) { pr_debug_dtp("final type:"); pr_debug_type_name(type_die, TSR_KIND_TYPE); ret = 0; } else { - pr_debug_dtp("no variable found\n"); - ann_data_stat.no_var++; + switch (result) { + case PERF_TMR_NO_TYPE: + case PERF_TMR_NO_POINTER: + pr_debug_dtp("%s\n", match_result_str(result)); + ann_data_stat.no_typeinfo++; + break; + case PERF_TMR_NO_SIZE: + pr_debug_dtp("%s\n", match_result_str(result)); + ann_data_stat.invalid_size++; + break; + case PERF_TMR_BAD_OFFSET: + pr_debug_dtp("%s\n", match_result_str(result)); + ann_data_stat.bad_offset++; + break; + case PERF_TMR_UNKNOWN: + case PERF_TMR_BAIL_OUT: + case PERF_TMR_OK: /* should not reach here */ + default: + pr_debug_dtp("no variable found\n"); + ann_data_stat.no_var++; + break; + } + ret = -1; } -out: free(scopes); return ret; } From 2518e13275ab9ea6b2540f828cf78b0280991f85 Mon Sep 17 00:00:00 2001 From: Xu Yang Date: Mon, 19 Aug 2024 10:34:03 +0800 Subject: [PATCH 178/347] perf python: Fix the build on 32-bit arm by including missing "util/sample.h" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 32-bit arm build system will complain: tools/perf/util/python.c:75:28: error: field ‘sample’ has incomplete type 75 | struct perf_sample sample; However, arm64 build system doesn't complain this. The root cause is arm64 define "HAVE_KVM_STAT_SUPPORT := 1" in tools/perf/arch/arm64/Makefile, but arm arch doesn't define this. This will lead to kvm-stat.h include other header files on arm64 build system, especially "util/sample.h" for util/python.c. This will try to directly include "util/sample.h" for "util/python.c" to avoid such build issue on arm platform. Signed-off-by: Xu Yang Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: imx@lists.linux.dev Link: https://lore.kernel.org/r/20240819023403.201324-1-xu.yang_2@nxp.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/python.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 3be882b2e845..31a223eaf8e6 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -20,6 +20,7 @@ #include "util/env.h" #include "util/kvm-stat.h" #include "util/kwork.h" +#include "util/sample.h" #include "util/lock-contention.h" #include #include "../builtin.h" From 2aebebb834e208344eeabcc4cacfc680026c8f6f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 16 Aug 2024 23:44:30 -0700 Subject: [PATCH 179/347] perf synthetic-events: Avoid unnecessary memset Make sure the memset of a synthesized event only zeros the necessary tracing data part of the event, as a full event can be over 4kb in size. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anne Macedo Cc: Arnd Bergmann Cc: Athira Rajeev Cc: Casey Chen Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jann Horn Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Masahiro Yamada Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Sun Haiyong Cc: Weilin Wang Cc: Yang Jihong Cc: Yunseong Kim Cc: Ze Gao Link: https://lore.kernel.org/r/20240817064442.2152089-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/synthetic-events.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index 7f884d70de81..0a7f93ae76fb 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -2200,7 +2200,7 @@ int perf_event__synthesize_tracing_data(const struct perf_tool *tool, int fd, st if (!tdata) return -1; - memset(&ev, 0, sizeof(ev)); + memset(&ev, 0, sizeof(ev.tracing_data)); ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA; size = tdata->size; From a031073626d135b332c1004dd6ade1b1e8d7c9f8 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 16 Aug 2024 23:44:31 -0700 Subject: [PATCH 180/347] perf map: API clean up map__init() is only used internally so make it static. Assume memory is zero initialized, which will better support adding fields to struct map in the future and was already the case for map__new2. To reduce complexity, change set_priv and set_erange_warned to not take a value to assign as they always assign true. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anne Macedo Cc: Arnd Bergmann Cc: Athira Rajeev Cc: Casey Chen Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jann Horn Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Masahiro Yamada Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Sun Haiyong Cc: Weilin Wang Cc: Yang Jihong Cc: Yunseong Kim Cc: Ze Gao Link: https://lore.kernel.org/r/20240817064442.2152089-3-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-top.c | 2 +- tools/perf/tests/vmlinux-kallsyms.c | 4 ++-- tools/perf/util/map.c | 24 ++++++++++++------------ tools/perf/util/map.h | 11 ++++------- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index 881b861c35ee..724a79386321 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -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, diff --git a/tools/perf/tests/vmlinux-kallsyms.c b/tools/perf/tests/vmlinux-kallsyms.c index cd3b480d20bd..74cdbd2ce9d0 100644 --- a/tools/perf/tests/vmlinux-kallsyms.c +++ b/tools/perf/tests/vmlinux-kallsyms.c @@ -131,7 +131,7 @@ static int test__vmlinux_matches_kallsyms_cb1(struct map *map, void *data) (dso__kernel(dso) ? dso__short_name(dso) : dso__name(dso))); if (pair) { - map__set_priv(pair, 1); + map__set_priv(pair); map__put(pair); } else { if (!args->header_printed) { @@ -166,7 +166,7 @@ static int test__vmlinux_matches_kallsyms_cb2(struct map *map, void *data) pr_info(":\nWARN: *%" PRIx64 "-%" PRIx64 " %" PRIx64, map__start(pair), map__end(pair), map__pgoff(pair)); pr_info(" %s\n", dso__name(dso)); - map__set_priv(pair, 1); + map__set_priv(pair); } map__put(pair); return 0; diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index e1d14936a60d..e781c8d56a9a 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -102,16 +102,20 @@ static inline bool replace_android_lib(const char *filename, char *newfilename) return false; } -void map__init(struct map *map, u64 start, u64 end, u64 pgoff, struct dso *dso) +static void map__init(struct map *map, u64 start, u64 end, u64 pgoff, + struct dso *dso, u32 prot, u32 flags) { map__set_start(map, start); map__set_end(map, end); map__set_pgoff(map, pgoff); - map__set_reloc(map, 0); + assert(map__reloc(map) == 0); map__set_dso(map, dso__get(dso)); - map__set_mapping_type(map, MAPPING_TYPE__DSO); - map__set_erange_warned(map, false); refcount_set(map__refcnt(map), 1); + RC_CHK_ACCESS(map)->prot = prot; + RC_CHK_ACCESS(map)->flags = flags; + map__set_mapping_type(map, MAPPING_TYPE__DSO); + assert(map__erange_warned(map) == false); + assert(map__priv(map) == false); } struct map *map__new(struct machine *machine, u64 start, u64 len, @@ -124,7 +128,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len, struct nsinfo *nsi = NULL; struct nsinfo *nnsi; - map = malloc(sizeof(*map)); + map = zalloc(sizeof(*map)); if (ADD_RC_CHK(result, map)) { char newfilename[PATH_MAX]; struct dso *dso, *header_bid_dso; @@ -134,8 +138,6 @@ struct map *map__new(struct machine *machine, u64 start, u64 len, anon = is_anon_memory(filename) || flags & MAP_HUGETLB; vdso = is_vdso_map(filename); no_dso = is_no_dso_memory(filename); - map->prot = prot; - map->flags = flags; nsi = nsinfo__get(thread__nsinfo(thread)); if ((anon || no_dso) && nsi && (prot & PROT_EXEC)) { @@ -169,7 +171,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len, goto out_delete; assert(!dso__kernel(dso)); - map__init(result, start, start + len, pgoff, dso); + map__init(result, start, start + len, pgoff, dso, prot, flags); if (anon || no_dso) { map->mapping_type = MAPPING_TYPE__IDENTITY; @@ -223,10 +225,8 @@ struct map *map__new2(u64 start, struct dso *dso) map = calloc(1, sizeof(*map) + (dso__kernel(dso) ? sizeof(struct kmap) : 0)); if (ADD_RC_CHK(result, map)) { - /* - * ->end will be filled after we load all the symbols - */ - map__init(result, start, 0, 0, dso); + /* ->end will be filled after we load all the symbols. */ + map__init(result, start, /*end=*/0, /*pgoff=*/0, dso, /*prot=*/0, /*flags=*/0); } return result; diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 65e2609fa1b1..6c43f31a9fe0 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -166,9 +166,6 @@ struct thread; #define map__for_each_symbol_by_name(map, sym_name, pos, idx) \ __map__for_each_symbol_by_name(map, sym_name, (pos), idx) -void map__init(struct map *map, - u64 start, u64 end, u64 pgoff, struct dso *dso); - struct dso_id; struct build_id; @@ -285,14 +282,14 @@ static inline void map__set_reloc(struct map *map, u64 reloc) RC_CHK_ACCESS(map)->reloc = reloc; } -static inline void map__set_priv(struct map *map, int priv) +static inline void map__set_priv(struct map *map) { - RC_CHK_ACCESS(map)->priv = priv; + RC_CHK_ACCESS(map)->priv = true; } -static inline void map__set_erange_warned(struct map *map, bool erange_warned) +static inline void map__set_erange_warned(struct map *map) { - RC_CHK_ACCESS(map)->erange_warned = erange_warned; + RC_CHK_ACCESS(map)->erange_warned = true; } static inline void map__set_dso(struct map *map, struct dso *dso) From 0847c193c3d777b79af9d019d6509b5857591f33 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 16 Aug 2024 23:44:32 -0700 Subject: [PATCH 181/347] perf jit: Constify filename argument Make it clearer the argument is just being used as a string. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anne Macedo Cc: Arnd Bergmann Cc: Athira Rajeev Cc: Casey Chen Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jann Horn Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Masahiro Yamada Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Sun Haiyong Cc: Weilin Wang Cc: Yang Jihong Cc: Yunseong Kim Cc: Ze Gao Link: https://lore.kernel.org/r/20240817064442.2152089-4-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/jit.h | 3 ++- tools/perf/util/jitdump.c | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/jit.h b/tools/perf/util/jit.h index fb810e1b2de7..f4037203e9ec 100644 --- a/tools/perf/util/jit.h +++ b/tools/perf/util/jit.h @@ -5,7 +5,8 @@ #include int jit_process(struct perf_session *session, struct perf_data *output, - struct machine *machine, char *filename, pid_t pid, pid_t tid, u64 *nbytes); + struct machine *machine, const char *filename, + pid_t pid, pid_t tid, u64 *nbytes); int jit_inject_record(const char *filename); diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c index 5ce13653512b..346513e5e9b7 100644 --- a/tools/perf/util/jitdump.c +++ b/tools/perf/util/jitdump.c @@ -710,7 +710,7 @@ jit_process_dump(struct jit_buf_desc *jd) } static int -jit_inject(struct jit_buf_desc *jd, char *path) +jit_inject(struct jit_buf_desc *jd, const char *path) { int ret; @@ -737,7 +737,7 @@ jit_inject(struct jit_buf_desc *jd, char *path) * as captured in the RECORD_MMAP record */ static int -jit_detect(char *mmap_name, pid_t pid, struct nsinfo *nsi) +jit_detect(const char *mmap_name, pid_t pid, struct nsinfo *nsi) { char *p; char *end = NULL; @@ -821,7 +821,7 @@ int jit_process(struct perf_session *session, struct perf_data *output, struct machine *machine, - char *filename, + const char *filename, pid_t pid, pid_t tid, u64 *nbytes) From e4bb4caa54b24b839c21612d816b40673a75f6d4 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 16 Aug 2024 23:44:33 -0700 Subject: [PATCH 182/347] perf dso: Constify dso_id The passed dso_id is copied and so is never an out argument. Remove its mutability. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anne Macedo Cc: Arnd Bergmann Cc: Athira Rajeev Cc: Casey Chen Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jann Horn Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Masahiro Yamada Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Sun Haiyong Cc: Weilin Wang Cc: Yang Jihong Cc: Yunseong Kim Cc: Ze Gao Link: https://lore.kernel.org/r/20240817064442.2152089-5-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 2 +- tools/perf/util/dso.c | 4 ++-- tools/perf/util/dso.h | 4 ++-- tools/perf/util/dsos.c | 12 ++++++------ tools/perf/util/dsos.h | 2 +- tools/perf/util/machine.c | 3 ++- tools/perf/util/machine.h | 3 ++- 7 files changed, 16 insertions(+), 14 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index a35bde3f3c09..8440ddfbf4fe 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -411,7 +411,7 @@ static int perf_event__jit_repipe_mmap(const struct perf_tool *tool, #endif static struct dso *findnew_dso(int pid, int tid, const char *filename, - struct dso_id *id, struct machine *machine) + const struct dso_id *id, struct machine *machine) { struct thread *thread; struct nsinfo *nsi = NULL; diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index 67414944f245..5c6e85fdae0d 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -1327,7 +1327,7 @@ bool dso_id__empty(const struct dso_id *id) return !id->maj && !id->min && !id->ino && !id->ino_generation; } -void __dso__inject_id(struct dso *dso, struct dso_id *id) +void __dso__inject_id(struct dso *dso, const struct dso_id *id) { struct dsos *dsos = dso__dsos(dso); struct dso_id *dso_id = dso__id(dso); @@ -1417,7 +1417,7 @@ void dso__set_sorted_by_name(struct dso *dso) RC_CHK_ACCESS(dso)->sorted_by_name = true; } -struct dso *dso__new_id(const char *name, struct dso_id *id) +struct dso *dso__new_id(const char *name, const struct dso_id *id) { RC_STRUCT(dso) *dso = zalloc(sizeof(*dso) + strlen(name) + 1); struct dso *res; diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index ed0068251c65..bb8e8f444054 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -640,14 +640,14 @@ static inline void dso__set_text_offset(struct dso *dso, u64 val) int dso_id__cmp(const struct dso_id *a, const struct dso_id *b); bool dso_id__empty(const struct dso_id *id); -struct dso *dso__new_id(const char *name, struct dso_id *id); +struct dso *dso__new_id(const char *name, const struct dso_id *id); struct dso *dso__new(const char *name); void dso__delete(struct dso *dso); int dso__cmp_id(struct dso *a, struct dso *b); void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated); void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated); -void __dso__inject_id(struct dso *dso, struct dso_id *id); +void __dso__inject_id(struct dso *dso, const struct dso_id *id); int dso__name_len(const struct dso *dso); diff --git a/tools/perf/util/dsos.c b/tools/perf/util/dsos.c index d4acdb37f046..e0998e2a7c4e 100644 --- a/tools/perf/util/dsos.c +++ b/tools/perf/util/dsos.c @@ -155,7 +155,7 @@ static int dsos__cmp_key_long_name_id(const void *vkey, const void *vdso) */ static struct dso *__dsos__find_by_longname_id(struct dsos *dsos, const char *name, - struct dso_id *id, + const struct dso_id *id, bool write_locked) { struct dsos__key key = { @@ -244,7 +244,7 @@ int dsos__add(struct dsos *dsos, struct dso *dso) struct dsos__find_id_cb_args { const char *name; - struct dso_id *id; + const struct dso_id *id; struct dso *res; }; @@ -260,7 +260,7 @@ static int dsos__find_id_cb(struct dso *dso, void *data) } -static struct dso *__dsos__find_id(struct dsos *dsos, const char *name, struct dso_id *id, +static struct dso *__dsos__find_id(struct dsos *dsos, const char *name, const struct dso_id *id, bool cmp_short, bool write_locked) { struct dso *res; @@ -321,7 +321,7 @@ static void dso__set_basename(struct dso *dso) dso__set_short_name(dso, base, true); } -static struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, struct dso_id *id) +static struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, const struct dso_id *id) { struct dso *dso = dso__new_id(name, id); @@ -337,7 +337,7 @@ static struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, struct return dso; } -static struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id) +static struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, const struct dso_id *id) { struct dso *dso = __dsos__find_id(dsos, name, id, false, /*write_locked=*/true); @@ -347,7 +347,7 @@ static struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, struc return dso ? dso : __dsos__addnew_id(dsos, name, id); } -struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id) +struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, const struct dso_id *id) { struct dso *dso; down_write(&dsos->lock); diff --git a/tools/perf/util/dsos.h b/tools/perf/util/dsos.h index 6c13b65648bc..a26774950866 100644 --- a/tools/perf/util/dsos.h +++ b/tools/perf/util/dsos.h @@ -32,7 +32,7 @@ int __dsos__add(struct dsos *dsos, struct dso *dso); int dsos__add(struct dsos *dsos, struct dso *dso); struct dso *dsos__find(struct dsos *dsos, const char *name, bool cmp_short); -struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id); +struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, const struct dso_id *id); bool dsos__read_build_ids(struct dsos *dsos, bool with_hits); diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index c08774d06d14..cd79a830abae 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -3128,7 +3128,8 @@ out: return addr_cpumode; } -struct dso *machine__findnew_dso_id(struct machine *machine, const char *filename, struct dso_id *id) +struct dso *machine__findnew_dso_id(struct machine *machine, const char *filename, + const struct dso_id *id) { return dsos__findnew_id(&machine->dsos, filename, id); } diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index 82a47bac8023..a687876e3453 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -207,7 +207,8 @@ int machine__nr_cpus_avail(struct machine *machine); struct thread *machine__findnew_thread(struct machine *machine, pid_t pid, pid_t tid); -struct dso *machine__findnew_dso_id(struct machine *machine, const char *filename, struct dso_id *id); +struct dso *machine__findnew_dso_id(struct machine *machine, const char *filename, + const struct dso_id *id); struct dso *machine__findnew_dso(struct machine *machine, const char *filename); size_t machine__fprintf(struct machine *machine, FILE *fp); From 63c89dc5e129a73cd3f8528640d2496543b79706 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 16 Aug 2024 23:44:34 -0700 Subject: [PATCH 183/347] perf evsel: Constify evsel__id_hdr_size() argument Allows evsel__id_hdr_size() to be used when the evsel is const. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anne Macedo Cc: Arnd Bergmann Cc: Athira Rajeev Cc: Casey Chen Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jann Horn Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Masahiro Yamada Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Sun Haiyong Cc: Weilin Wang Cc: Yang Jihong Cc: Yunseong Kim Cc: Ze Gao Link: https://lore.kernel.org/r/20240817064442.2152089-6-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evsel.c | 2 +- tools/perf/util/evsel.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 38a74d6dde49..49cc71511c0c 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -3048,7 +3048,7 @@ int evsel__parse_sample_timestamp(struct evsel *evsel, union perf_event *event, return 0; } -u16 evsel__id_hdr_size(struct evsel *evsel) +u16 evsel__id_hdr_size(const struct evsel *evsel) { u64 sample_type = evsel->core.attr.sample_type; u16 size = 0; diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 4316992f6a69..15acf293e12a 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -442,7 +442,7 @@ int evsel__parse_sample(struct evsel *evsel, union perf_event *event, int evsel__parse_sample_timestamp(struct evsel *evsel, union perf_event *event, u64 *timestamp); -u16 evsel__id_hdr_size(struct evsel *evsel); +u16 evsel__id_hdr_size(const struct evsel *evsel); static inline struct evsel *evsel__next(struct evsel *evsel) { From a8656614ebe017a0487ba9f85213910c09e89c59 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 16 Aug 2024 23:44:35 -0700 Subject: [PATCH 184/347] perf test: Expand pipe/inject test Test recording of call-graphs and injecting --build-all. Add/expand trap handler. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anne Macedo Cc: Arnd Bergmann Cc: Athira Rajeev Cc: Casey Chen Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jann Horn Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Masahiro Yamada Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Sun Haiyong Cc: Weilin Wang Cc: Yang Jihong Cc: Yunseong Kim Cc: Ze Gao Link: https://lore.kernel.org/r/20240817064442.2152089-7-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/pipe_test.sh | 101 ++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 22 deletions(-) diff --git a/tools/perf/tests/shell/pipe_test.sh b/tools/perf/tests/shell/pipe_test.sh index a78d35d2cff0..ad10012fdc29 100755 --- a/tools/perf/tests/shell/pipe_test.sh +++ b/tools/perf/tests/shell/pipe_test.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # perf pipe recording and injection test # SPDX-License-Identifier: GPL-2.0 @@ -12,30 +12,87 @@ skip_test_missing_symbol ${sym} data=$(mktemp /tmp/perf.data.XXXXXX) prog="perf test -w noploop" -task="perf" +err=0 -if ! perf record -e task-clock:u -o - ${prog} | perf report -i - --task | grep ${task}; then - echo "cannot find the test file in the perf report" - exit 1 -fi +set -e -if ! perf record -e task-clock:u -o - ${prog} | perf inject -b | perf report -i - | grep ${sym}; then - echo "cannot find noploop function in pipe #1" - exit 1 -fi +cleanup() { + rm -rf "${data}" + rm -rf "${data}".old -perf record -e task-clock:u -o - ${prog} | perf inject -b -o ${data} -if ! perf report -i ${data} | grep ${sym}; then - echo "cannot find noploop function in pipe #2" - exit 1 -fi + trap - EXIT TERM INT +} -perf record -e task-clock:u -o ${data} ${prog} -if ! perf inject -b -i ${data} | perf report -i - | grep ${sym}; then - echo "cannot find noploop function in pipe #3" - exit 1 -fi +trap_cleanup() { + echo "Unexpected signal in ${FUNCNAME[1]}" + cleanup + exit 1 +} +trap trap_cleanup EXIT TERM INT +test_record_report() { + echo + echo "Record+report pipe test" + + task="perf" + if ! perf record -e task-clock:u -o - ${prog} | perf report -i - --task | grep -q ${task} + then + echo "Record+report pipe test [Failed - cannot find the test file in the perf report #1]" + err=1 + return + fi + + if ! perf record -g -e task-clock:u -o - ${prog} | perf report -i - --task | grep -q ${task} + then + echo "Record+report pipe test [Failed - cannot find the test file in the perf report #2]" + err=1 + return + fi + + echo "Record+report pipe test [Success]" +} + +test_inject_bids() { + inject_opt=$1 + + echo + echo "Inject ${inject_opt} build-ids test" + + if ! perf record -e task-clock:u -o - ${prog} | perf inject ${inject_opt}| perf report -i - | grep -q ${sym} + then + echo "Inject build-ids test [Failed - cannot find noploop function in pipe #1]" + err=1 + return + fi + + if ! perf record -g -e task-clock:u -o - ${prog} | perf inject ${inject_opt} | perf report -i - | grep -q ${sym} + then + echo "Inject ${inject_opt} build-ids test [Failed - cannot find noploop function in pipe #2]" + err=1 + return + fi + + perf record -e task-clock:u -o - ${prog} | perf inject ${inject_opt} -o ${data} + if ! perf report -i ${data} | grep -q ${sym}; then + echo "Inject ${inject_opt} build-ids test [Failed - cannot find noploop function in pipe #3]" + err=1 + return + fi + + perf record -e task-clock:u -o ${data} ${prog} + if ! perf inject ${inject_opt} -i ${data} | perf report -i - | grep -q ${sym}; then + echo "Inject ${inject_opt} build-ids test [Failed - cannot find noploop function in pipe #4]" + err=1 + return + fi + + echo "Inject ${inject_opt} build-ids test [Success]" +} + +test_record_report +test_inject_bids -b +test_inject_bids --buildid-all + +cleanup +exit $err -rm -f ${data} ${data}.old -exit 0 From 0ed4c8c31139e3360c5db9f536e8ff37c1824f8a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 16 Aug 2024 23:44:36 -0700 Subject: [PATCH 185/347] perf inject: Combine build_ids and build_id_all into enum It is clearer to have a single enum that determines how build ids are injected, it also allows for future extension. Set the header build ID feature whether lazy or all are generated, previously only the lazy case would set it. Allow parsing of known build IDs for either the lazy or all cases. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anne Macedo Cc: Arnd Bergmann Cc: Athira Rajeev Cc: Casey Chen Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jann Horn Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Masahiro Yamada Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Sun Haiyong Cc: Weilin Wang Cc: Yang Jihong Cc: Yunseong Kim Cc: Ze Gao Link: https://lore.kernel.org/r/20240817064442.2152089-8-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 51 ++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 8440ddfbf4fe..865d16ceead2 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -103,11 +103,16 @@ struct guest_session { struct guest_event ev; }; +enum build_id_rewrite_style { + BID_RWS__NONE = 0, + BID_RWS__INJECT_HEADER_LAZY, + BID_RWS__INJECT_HEADER_ALL, +}; + struct perf_inject { struct perf_tool tool; struct perf_session *session; - bool build_ids; - bool build_id_all; + enum build_id_rewrite_style build_id_style; bool sched_stat; bool have_auxtrace; bool strip; @@ -2015,8 +2020,8 @@ static int __cmd_inject(struct perf_inject *inject) signal(SIGINT, sig_handler); - if (inject->build_ids || inject->sched_stat || - inject->itrace_synth_opts.set || inject->build_id_all) { + if (inject->build_id_style != BID_RWS__NONE || inject->sched_stat || + inject->itrace_synth_opts.set) { inject->tool.mmap = perf_event__repipe_mmap; inject->tool.mmap2 = perf_event__repipe_mmap2; inject->tool.fork = perf_event__repipe_fork; @@ -2027,10 +2032,10 @@ static int __cmd_inject(struct perf_inject *inject) output_data_offset = perf_session__data_offset(session->evlist); - if (inject->build_id_all) { + if (inject->build_id_style == BID_RWS__INJECT_HEADER_ALL) { inject->tool.mmap = perf_event__repipe_buildid_mmap; inject->tool.mmap2 = perf_event__repipe_buildid_mmap2; - } else if (inject->build_ids) { + } else if (inject->build_id_style == BID_RWS__INJECT_HEADER_LAZY) { inject->tool.sample = perf_event__inject_buildid; } else if (inject->sched_stat) { struct evsel *evsel; @@ -2148,9 +2153,9 @@ static int __cmd_inject(struct perf_inject *inject) .inject = inject, }; - if (inject->build_ids) - perf_header__set_feat(&session->header, - HEADER_BUILD_ID); + if (inject->build_id_style == BID_RWS__INJECT_HEADER_LAZY || + inject->build_id_style == BID_RWS__INJECT_HEADER_ALL) + perf_header__set_feat(&session->header, HEADER_BUILD_ID); /* * Keep all buildids when there is unprocessed AUX data because * it is not known which ones the AUX trace hits. @@ -2211,11 +2216,13 @@ int cmd_inject(int argc, const char **argv) int ret; bool repipe = true; const char *known_build_ids = NULL; + bool build_ids; + bool build_id_all; struct option options[] = { - OPT_BOOLEAN('b', "build-ids", &inject.build_ids, + OPT_BOOLEAN('b', "build-ids", &build_ids, "Inject build-ids into the output stream"), - OPT_BOOLEAN(0, "buildid-all", &inject.build_id_all, + OPT_BOOLEAN(0, "buildid-all", &build_id_all, "Inject build-ids of all DSOs into the output stream"), OPT_STRING(0, "known-build-ids", &known_build_ids, "buildid path [,buildid path...]", @@ -2313,6 +2320,10 @@ int cmd_inject(int argc, const char **argv) return -1; } } + if (build_ids) + inject.build_id_style = BID_RWS__INJECT_HEADER_LAZY; + if (build_id_all) + inject.build_id_style = BID_RWS__INJECT_HEADER_ALL; data.path = inject.input_name; if (!strcmp(inject.input_name, "-") || inject.output.is_pipe) { @@ -2326,7 +2337,7 @@ int cmd_inject(int argc, const char **argv) repipe = false; } ordered_events = inject.jit_mode || inject.sched_stat || - (inject.build_ids && !inject.build_id_all); + (inject.build_id_style == BID_RWS__INJECT_HEADER_LAZY); perf_tool__init(&inject.tool, ordered_events); inject.tool.sample = perf_event__repipe_sample; inject.tool.read = perf_event__repipe_sample; @@ -2398,7 +2409,7 @@ int cmd_inject(int argc, const char **argv) goto out_delete; } - if (inject.build_ids && !inject.build_id_all) { + if (inject.build_id_style == BID_RWS__INJECT_HEADER_LAZY) { /* * to make sure the mmap records are ordered correctly * and so that the correct especially due to jitted code @@ -2406,14 +2417,14 @@ int cmd_inject(int argc, const char **argv) * inject the jit mmaps at the same time for now. */ inject.tool.ordering_requires_timestamps = true; - if (known_build_ids != NULL) { - inject.known_build_ids = - perf_inject__parse_known_build_ids(known_build_ids); + } + if (inject.build_id_style != BID_RWS__NONE && known_build_ids != NULL) { + inject.known_build_ids = + perf_inject__parse_known_build_ids(known_build_ids); - if (inject.known_build_ids == NULL) { - pr_err("Couldn't parse known build ids.\n"); - goto out_delete; - } + if (inject.known_build_ids == NULL) { + pr_err("Couldn't parse known build ids.\n"); + goto out_delete; } } From 048a7a9363a276ffc15e137ae8544d2cd7c28b67 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 16 Aug 2024 23:44:37 -0700 Subject: [PATCH 186/347] perf inject: Combine different mmap and mmap2 functions There are repipe, build ID and JIT dump variants of the mmap and mmap2 repipe functions. The organization doesn't allow JIT dump to work with build ID injection and the structure is less than clear. Combine the function and enable the different behaviors based on ifs. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anne Macedo Cc: Arnd Bergmann Cc: Athira Rajeev Cc: Casey Chen Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jann Horn Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Masahiro Yamada Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Sun Haiyong Cc: Weilin Wang Cc: Yang Jihong Cc: Yunseong Kim Cc: Ze Gao Link: https://lore.kernel.org/r/20240817064442.2152089-9-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 197 ++++++++++++++---------------------- 1 file changed, 75 insertions(+), 122 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 865d16ceead2..d99868953ff2 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -377,44 +377,6 @@ static int perf_event__repipe_sample(const struct perf_tool *tool, return perf_event__repipe_synth(tool, event); } -static int perf_event__repipe_mmap(const struct perf_tool *tool, - union perf_event *event, - struct perf_sample *sample, - struct machine *machine) -{ - int err; - - err = perf_event__process_mmap(tool, event, sample, machine); - perf_event__repipe(tool, event, sample, machine); - - return err; -} - -#ifdef HAVE_JITDUMP -static int perf_event__jit_repipe_mmap(const struct perf_tool *tool, - union perf_event *event, - struct perf_sample *sample, - struct machine *machine) -{ - struct perf_inject *inject = container_of(tool, struct perf_inject, tool); - u64 n = 0; - int ret; - - /* - * if jit marker, then inject jit mmaps and generate ELF images - */ - ret = jit_process(inject->session, &inject->output, machine, - event->mmap.filename, event->mmap.pid, event->mmap.tid, &n); - if (ret < 0) - return ret; - if (ret) { - inject->bytes_written += n; - return 0; - } - return perf_event__repipe_mmap(tool, event, sample, machine); -} -#endif - static struct dso *findnew_dso(int pid, int tid, const char *filename, const struct dso_id *id, struct machine *machine) { @@ -460,22 +422,45 @@ static struct dso *findnew_dso(int pid, int tid, const char *filename, return dso; } -static int perf_event__repipe_buildid_mmap(const struct perf_tool *tool, - union perf_event *event, - struct perf_sample *sample, - struct machine *machine) +static int perf_event__repipe_mmap(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) { - struct dso *dso; + struct perf_inject *inject = container_of(tool, struct perf_inject, tool); - dso = findnew_dso(event->mmap.pid, event->mmap.tid, - event->mmap.filename, NULL, machine); +#ifdef HAVE_JITDUMP + if (inject->jit_mode) { + u64 n = 0; + int ret; - if (dso && !dso__hit(dso)) { - dso__set_hit(dso); - dso__inject_build_id(dso, tool, machine, sample->cpumode, 0); + /* If jit marker, then inject jit mmaps and generate ELF images. */ + ret = jit_process(inject->session, &inject->output, machine, + event->mmap.filename, event->mmap.pid, event->mmap.tid, &n); + if (ret < 0) + return ret; + if (ret) { + inject->bytes_written += n; + return 0; + } } - dso__put(dso); +#endif + if (inject->build_id_style == BID_RWS__INJECT_HEADER_ALL) { + struct dso *dso = findnew_dso(event->mmap.pid, event->mmap.tid, + event->mmap.filename, NULL, machine); + if (dso && !dso__hit(dso)) { + dso__set_hit(dso); + dso__inject_build_id(dso, tool, machine, sample->cpumode, 0); + } + dso__put(dso); + } else { + /* Create the thread, map, etc. Not done for the unordered inject all case. */ + int err = perf_event__process_mmap(tool, event, sample, machine); + + if (err) + return err; + } return perf_event__repipe(tool, event, sample, machine); } @@ -484,90 +469,61 @@ static int perf_event__repipe_mmap2(const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine) { - int err; - - err = perf_event__process_mmap2(tool, event, sample, machine); - perf_event__repipe(tool, event, sample, machine); - - if (event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID) { - struct dso *dso; - - dso = findnew_dso(event->mmap2.pid, event->mmap2.tid, - event->mmap2.filename, NULL, machine); - if (dso) { - /* mark it not to inject build-id */ - dso__set_hit(dso); - } - dso__put(dso); - } - - return err; -} + struct perf_inject *inject = container_of(tool, struct perf_inject, tool); + struct dso *dso = NULL; #ifdef HAVE_JITDUMP -static int perf_event__jit_repipe_mmap2(const struct perf_tool *tool, - union perf_event *event, - struct perf_sample *sample, - struct machine *machine) -{ - struct perf_inject *inject = container_of(tool, struct perf_inject, tool); - u64 n = 0; - int ret; + if (inject->jit_mode) { + u64 n = 0; + int ret; - /* - * if jit marker, then inject jit mmaps and generate ELF images - */ - ret = jit_process(inject->session, &inject->output, machine, - event->mmap2.filename, event->mmap2.pid, event->mmap2.tid, &n); - if (ret < 0) - return ret; - if (ret) { - inject->bytes_written += n; - return 0; + /* If jit marker, then inject jit mmaps and generate ELF images. */ + ret = jit_process(inject->session, &inject->output, machine, + event->mmap2.filename, event->mmap2.pid, event->mmap2.tid, &n); + if (ret < 0) + return ret; + if (ret) { + inject->bytes_written += n; + return 0; + } } - return perf_event__repipe_mmap2(tool, event, sample, machine); -} #endif - -static int perf_event__repipe_buildid_mmap2(const struct perf_tool *tool, - union perf_event *event, - struct perf_sample *sample, - struct machine *machine) -{ - struct dso_id dso_id = { - .maj = event->mmap2.maj, - .min = event->mmap2.min, - .ino = event->mmap2.ino, - .ino_generation = event->mmap2.ino_generation, - }; - struct dso *dso; - if (event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID) { - /* cannot use dso_id since it'd have invalid info */ dso = findnew_dso(event->mmap2.pid, event->mmap2.tid, event->mmap2.filename, NULL, machine); if (dso) { /* mark it not to inject build-id */ dso__set_hit(dso); } - dso__put(dso); - perf_event__repipe(tool, event, sample, machine); - return 0; } + if (inject->build_id_style == BID_RWS__INJECT_HEADER_ALL) { + if (!(event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)) { + struct dso_id dso_id = { + .maj = event->mmap2.maj, + .min = event->mmap2.min, + .ino = event->mmap2.ino, + .ino_generation = event->mmap2.ino_generation, + }; - dso = findnew_dso(event->mmap2.pid, event->mmap2.tid, - event->mmap2.filename, &dso_id, machine); + dso = findnew_dso(event->mmap2.pid, event->mmap2.tid, + event->mmap2.filename, &dso_id, machine); + } + if (dso && !dso__hit(dso)) { + dso__set_hit(dso); + dso__inject_build_id(dso, tool, machine, sample->cpumode, + event->mmap2.flags); + } + } else { + /* Create the thread, map, etc. Not done for the unordered inject all case. */ + int err = perf_event__process_mmap(tool, event, sample, machine); - if (dso && !dso__hit(dso)) { - dso__set_hit(dso); - dso__inject_build_id(dso, tool, machine, sample->cpumode, - event->mmap2.flags); + if (err) { + dso__put(dso); + return err; + } } dso__put(dso); - - perf_event__repipe(tool, event, sample, machine); - - return 0; + return perf_event__repipe(tool, event, sample, machine); } static int perf_event__repipe_fork(const struct perf_tool *tool, @@ -2032,10 +1988,7 @@ static int __cmd_inject(struct perf_inject *inject) output_data_offset = perf_session__data_offset(session->evlist); - if (inject->build_id_style == BID_RWS__INJECT_HEADER_ALL) { - inject->tool.mmap = perf_event__repipe_buildid_mmap; - inject->tool.mmap2 = perf_event__repipe_buildid_mmap2; - } else if (inject->build_id_style == BID_RWS__INJECT_HEADER_LAZY) { + if (inject->build_id_style == BID_RWS__INJECT_HEADER_LAZY) { inject->tool.sample = perf_event__inject_buildid; } else if (inject->sched_stat) { struct evsel *evsel; @@ -2430,8 +2383,8 @@ int cmd_inject(int argc, const char **argv) #ifdef HAVE_JITDUMP if (inject.jit_mode) { - inject.tool.mmap2 = perf_event__jit_repipe_mmap2; - inject.tool.mmap = perf_event__jit_repipe_mmap; + inject.tool.mmap2 = perf_event__repipe_mmap2; + inject.tool.mmap = perf_event__repipe_mmap; inject.tool.ordering_requires_timestamps = true; /* * JIT MMAP injection injects all MMAP events in one go, so it From 05c4cfeba097b986532c7a5609dcc5435c0a4dee Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 16 Aug 2024 23:44:38 -0700 Subject: [PATCH 187/347] perf inject: Combine mmap and mmap2 handling The handling of mmap and mmap2 events is near identical. Add a common helper function and call that by the two event handling functions. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anne Macedo Cc: Arnd Bergmann Cc: Athira Rajeev Cc: Casey Chen Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jann Horn Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Masahiro Yamada Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Sun Haiyong Cc: Weilin Wang Cc: Yang Jihong Cc: Yunseong Kim Cc: Ze Gao Link: https://lore.kernel.org/r/20240817064442.2152089-10-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 118 +++++++++++++++++------------------- 1 file changed, 56 insertions(+), 62 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index d99868953ff2..a7c859db2e15 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -422,55 +422,21 @@ static struct dso *findnew_dso(int pid, int tid, const char *filename, return dso; } -static int perf_event__repipe_mmap(const struct perf_tool *tool, - union perf_event *event, - struct perf_sample *sample, - struct machine *machine) -{ - struct perf_inject *inject = container_of(tool, struct perf_inject, tool); - -#ifdef HAVE_JITDUMP - if (inject->jit_mode) { - u64 n = 0; - int ret; - - /* If jit marker, then inject jit mmaps and generate ELF images. */ - ret = jit_process(inject->session, &inject->output, machine, - event->mmap.filename, event->mmap.pid, event->mmap.tid, &n); - if (ret < 0) - return ret; - if (ret) { - inject->bytes_written += n; - return 0; - } - } -#endif - if (inject->build_id_style == BID_RWS__INJECT_HEADER_ALL) { - struct dso *dso = findnew_dso(event->mmap.pid, event->mmap.tid, - event->mmap.filename, NULL, machine); - - if (dso && !dso__hit(dso)) { - dso__set_hit(dso); - dso__inject_build_id(dso, tool, machine, sample->cpumode, 0); - } - dso__put(dso); - } else { - /* Create the thread, map, etc. Not done for the unordered inject all case. */ - int err = perf_event__process_mmap(tool, event, sample, machine); - - if (err) - return err; - } - return perf_event__repipe(tool, event, sample, machine); -} - -static int perf_event__repipe_mmap2(const struct perf_tool *tool, - union perf_event *event, - struct perf_sample *sample, - struct machine *machine) +static int perf_event__repipe_common_mmap(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine, + __u32 pid, __u32 tid, __u32 flags, + const char *filename, + const struct dso_id *dso_id, + int (*perf_event_process)(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine)) { struct perf_inject *inject = container_of(tool, struct perf_inject, tool); struct dso *dso = NULL; + bool dso_sought = false; #ifdef HAVE_JITDUMP if (inject->jit_mode) { @@ -479,7 +445,7 @@ static int perf_event__repipe_mmap2(const struct perf_tool *tool, /* If jit marker, then inject jit mmaps and generate ELF images. */ ret = jit_process(inject->session, &inject->output, machine, - event->mmap2.filename, event->mmap2.pid, event->mmap2.tid, &n); + filename, pid, tid, &n); if (ret < 0) return ret; if (ret) { @@ -489,33 +455,26 @@ static int perf_event__repipe_mmap2(const struct perf_tool *tool, } #endif if (event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID) { - dso = findnew_dso(event->mmap2.pid, event->mmap2.tid, - event->mmap2.filename, NULL, machine); + dso = findnew_dso(pid, tid, filename, dso_id, machine); + dso_sought = true; if (dso) { /* mark it not to inject build-id */ dso__set_hit(dso); } } if (inject->build_id_style == BID_RWS__INJECT_HEADER_ALL) { - if (!(event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)) { - struct dso_id dso_id = { - .maj = event->mmap2.maj, - .min = event->mmap2.min, - .ino = event->mmap2.ino, - .ino_generation = event->mmap2.ino_generation, - }; - - dso = findnew_dso(event->mmap2.pid, event->mmap2.tid, - event->mmap2.filename, &dso_id, machine); + if (!dso_sought) { + dso = findnew_dso(pid, tid, filename, dso_id, machine); + dso_sought = true; } + if (dso && !dso__hit(dso)) { dso__set_hit(dso); - dso__inject_build_id(dso, tool, machine, sample->cpumode, - event->mmap2.flags); + dso__inject_build_id(dso, tool, machine, sample->cpumode, flags); } } else { /* Create the thread, map, etc. Not done for the unordered inject all case. */ - int err = perf_event__process_mmap(tool, event, sample, machine); + int err = perf_event_process(tool, event, sample, machine); if (err) { dso__put(dso); @@ -526,6 +485,41 @@ static int perf_event__repipe_mmap2(const struct perf_tool *tool, return perf_event__repipe(tool, event, sample, machine); } +static int perf_event__repipe_mmap(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + return perf_event__repipe_common_mmap( + tool, event, sample, machine, + event->mmap.pid, event->mmap.tid, /*flags=*/0, + event->mmap.filename, /*dso_id=*/NULL, + perf_event__process_mmap); +} + +static int perf_event__repipe_mmap2(const struct perf_tool *tool, + union perf_event *event, + struct perf_sample *sample, + struct machine *machine) +{ + struct dso_id id; + struct dso_id *dso_id = NULL; + + if (!(event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)) { + id.maj = event->mmap2.maj; + id.min = event->mmap2.min; + id.ino = event->mmap2.ino; + id.ino_generation = event->mmap2.ino_generation; + dso_id = &id; + } + + return perf_event__repipe_common_mmap( + tool, event, sample, machine, + event->mmap2.pid, event->mmap2.tid, event->mmap2.flags, + event->mmap2.filename, dso_id, + perf_event__process_mmap2); +} + static int perf_event__repipe_fork(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, From 2865baf54077aa98fcdb478cefe6a42c417b9374 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 8 Apr 2024 20:04:58 -0700 Subject: [PATCH 188/347] x86: support user address masking instead of non-speculative conditional The Spectre-v1 mitigations made "access_ok()" much more expensive, since it has to serialize execution with the test for a valid user address. All the normal user copy routines avoid this by just masking the user address with a data-dependent mask instead, but the fast "unsafe_user_read()" kind of patterms that were supposed to be a fast case got slowed down. This introduces a notion of using src = masked_user_access_begin(src); to do the user address sanity using a data-dependent mask instead of the more traditional conditional if (user_read_access_begin(src, len)) { model. This model only works for dense accesses that start at 'src' and on architectures that have a guard region that is guaranteed to fault in between the user space and the kernel space area. With this, the user access doesn't need to be manually checked, because a bad address is guaranteed to fault (by some architecture masking trick: on x86-64 this involves just turning an invalid user address into all ones, since we don't map the top of address space). This only converts a couple of examples for now. Example x86-64 code generation for loading two words from user space: stac mov %rax,%rcx sar $0x3f,%rcx or %rax,%rcx mov (%rcx),%r13 mov 0x8(%rcx),%r14 clac where all the error handling and -EFAULT is now purely handled out of line by the exception path. Of course, if the micro-architecture does badly at 'clac' and 'stac', the above is still pitifully slow. But at least we did as well as we could. Signed-off-by: Linus Torvalds --- arch/x86/include/asm/uaccess_64.h | 8 ++++++++ fs/select.c | 4 +++- include/linux/uaccess.h | 7 +++++++ lib/strncpy_from_user.c | 9 +++++++++ lib/strnlen_user.c | 9 +++++++++ 5 files changed, 36 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h index 04789f45ab2b..a10149a96d9e 100644 --- a/arch/x86/include/asm/uaccess_64.h +++ b/arch/x86/include/asm/uaccess_64.h @@ -53,6 +53,14 @@ 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) ({ __uaccess_begin(); mask_user_address(x); }) + /* * User pointers can have tag bits on x86-64. This scheme tolerates * arbitrary values in those bits rather then masking them off. diff --git a/fs/select.c b/fs/select.c index 9515c3fa1a03..bc185d111436 100644 --- a/fs/select.c +++ b/fs/select.c @@ -780,7 +780,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); diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 3064314f4832..f18371f6cf36 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -32,6 +32,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 diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c index 6432b8c3e431..989a12a67872 100644 --- a/lib/strncpy_from_user.c +++ b/lib/strncpy_from_user.c @@ -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)) { diff --git a/lib/strnlen_user.c b/lib/strnlen_user.c index feeb935a2299..6e489f9e90f1 100644 --- a/lib/strnlen_user.c +++ b/lib/strnlen_user.c @@ -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)) { From 05f4216272c4b588c87551d3ba9bfb88b1bffaba Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 25 Jul 2024 13:45:10 -0700 Subject: [PATCH 189/347] x86: do the user address masking outside the user access area In any normal situation this really shouldn't matter, but in case the address passed in to masked_user_access_begin() were to be some complex expression, we should evaluate it fully before doing the 'stac' instruction. And even without that issue (which objdump would pick up on for any really bad case), just in general we should strive to minimize the amount of code we run with user accesses enabled. For example, even for the trivial pselect6() case, the code generation (obviously with a non-debug build) just diff with this ends up being - stac mov %rax,%rcx sar $0x3f,%rcx or %rax,%rcx + stac mov (%rcx),%r13 mov 0x8(%rcx),%r14 clac so the area delimeted by the 'stac / clac' pair is now literally just the two user access instructions, and the address generation has been moved out to before that code. This will be much more noticeable if we end up deciding that we can go back to just inlining "get_user()" using the new masked user access model. The get_user() pointers can often be more complex expressions involving kernel memory accesses or even function calls. Signed-off-by: Linus Torvalds --- arch/x86/include/asm/uaccess_64.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h index a10149a96d9e..92859c1ef59c 100644 --- a/arch/x86/include/asm/uaccess_64.h +++ b/arch/x86/include/asm/uaccess_64.h @@ -59,7 +59,9 @@ static inline unsigned long __untagged_addr_remote(struct mm_struct *mm, * 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) ({ __uaccess_begin(); mask_user_address(x); }) +#define masked_user_access_begin(x) ({ \ + __auto_type __masked_ptr = mask_user_address(x); \ + __uaccess_begin(); __masked_ptr; }) /* * User pointers can have tag bits on x86-64. This scheme tolerates From 3432bae89e044819f0b30f4c09260d2740896797 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sun, 18 Aug 2024 14:29:47 -0700 Subject: [PATCH 190/347] perf record: Fix sample cgroup & namespace tracking The recent change in 'struct perf_tool' constification broke the cgroup and/or namespace tracking by resetting tool fields. It should set the values after perf_tool__init(). Fixes: cecb1cf154b301c6 ("perf record: Use perf_tool__init()") Reviewed-by: Ian Rogers Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240818212948.2873156-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-record.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index 39367709fd99..adbaf80b398c 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -2374,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 @@ -2406,6 +2401,8 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) 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"); From 5cc698bad72667cf80097dece6b91efbb4a1a0a7 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Sun, 18 Aug 2024 14:29:48 -0700 Subject: [PATCH 191/347] perf test: Add cgroup sampling test Add it to the record.sh shell test to verify if it tracks cgroup information correctly. It records with --all-cgroups option can check if it has PERF_RECORD_CGROUP and the names are not "unknown". $ sudo ./perf test -vv 95 95: perf record tests: --- start --- test child forked, pid 2871922 169c90-169cd0 g test_loop perf does have symbol 'test_loop' Basic --per-thread mode test Basic --per-thread mode test [Success] Register capture test Register capture test [Success] Basic --system-wide mode test Basic --system-wide mode test [Success] Basic target workload test Basic target workload test [Success] Branch counter test branch counter feature not supported on all core PMUs (/sys/bus/event_source/devices/cpu) [Skipped] Cgroup sampling test Cgroup sampling test [Success] ---- end(0) ---- 95: perf record tests : Ok Reviewed-by: Ian Rogers Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240818212948.2873156-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/record.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tools/perf/tests/shell/record.sh b/tools/perf/tests/shell/record.sh index 36883b03169f..048078ee2eca 100755 --- a/tools/perf/tests/shell/record.sh +++ b/tools/perf/tests/shell/record.sh @@ -206,6 +206,28 @@ test_branch_counter() { echo "Branch counter test [Success]" } +test_cgroup() { + echo "Cgroup sampling test" + if ! perf record -aB --synth=cgroup --all-cgroups -o "${perfdata}" ${testprog} 2> /dev/null + then + echo "Cgroup sampling [Skipped not supported]" + return + fi + if ! perf report -i "${perfdata}" -D | grep -q "CGROUP" + then + echo "Cgroup sampling [Failed missing output]" + err=1 + return + fi + if ! perf script -i "${perfdata}" -F cgroup | grep -q -v "unknown" + then + echo "Cgroup sampling [Failed cannot resolve cgroup names]" + err=1 + return + fi + echo "Cgroup sampling test [Success]" +} + # raise the limit of file descriptors to minimum if [[ $default_fd_limit -lt $min_fd_limit ]]; then ulimit -Sn $min_fd_limit @@ -216,6 +238,7 @@ test_register_capture test_system_wide test_workload test_branch_counter +test_cgroup # restore the default value ulimit -Sn $default_fd_limit From 6236ebe07131a7746d870f1d8eb3637a8df13e70 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 19 Aug 2024 16:46:29 -0300 Subject: [PATCH 192/347] perf daemon: Fix the build on more 32-bit architectures The previous attempt fixed the build on debian:experimental-x-mipsel, but when building on a larger set of containers I noticed it broke the build on some other 32-bit architectures such as: 42 7.87 ubuntu:18.04-x-arm : FAIL gcc version 7.5.0 (Ubuntu/Linaro 7.5.0-3ubuntu1~18.04) builtin-daemon.c: In function 'cmd_session_list': builtin-daemon.c:692:16: error: format '%llu' expects argument of type 'long long unsigned int', but argument 4 has type 'long int' [-Werror=format=] fprintf(out, "%c%" PRIu64, ^~~~~ builtin-daemon.c:694:13: csv_sep, (curr - daemon->start) / 60); ~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from builtin-daemon.c:3:0: /usr/arm-linux-gnueabihf/include/inttypes.h:105:34: note: format string is defined here # define PRIu64 __PRI64_PREFIX "u" So lets cast that time_t (32-bit/64-bit) to uint64_t to make sure it builds everywhere. Fixes: 4bbe6002931954bb ("perf daemon: Fix the build on 32-bit architectures") Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/ZsPmldtJ0D9Cua9_@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-daemon.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-daemon.c b/tools/perf/builtin-daemon.c index 7dca39c4314b..f0568431fbd5 100644 --- a/tools/perf/builtin-daemon.c +++ b/tools/perf/builtin-daemon.c @@ -691,7 +691,7 @@ static int cmd_session_list(struct daemon *daemon, union cmd *cmd, FILE *out) fprintf(out, "%c%" PRIu64, /* session up time */ - csv_sep, (curr - daemon->start) / 60); + csv_sep, (uint64_t)((curr - daemon->start) / 60)); fprintf(out, "\n"); } else { @@ -702,7 +702,7 @@ static int cmd_session_list(struct daemon *daemon, union cmd *cmd, FILE *out) fprintf(out, " lock: %s/lock\n", daemon->base); fprintf(out, " up: %" PRIu64 " minutes\n", - (curr - daemon->start) / 60); + (uint64_t)((curr - daemon->start) / 60)); } } @@ -730,7 +730,7 @@ static int cmd_session_list(struct daemon *daemon, union cmd *cmd, FILE *out) fprintf(out, "%c%" PRIu64, /* session up time */ - csv_sep, (curr - session->start) / 60); + csv_sep, (uint64_t)((curr - session->start) / 60)); fprintf(out, "\n"); } else { @@ -747,7 +747,7 @@ static int cmd_session_list(struct daemon *daemon, union cmd *cmd, FILE *out) fprintf(out, " ack: %s/%s\n", session->base, SESSION_ACK); fprintf(out, " up: %" PRIu64 " minutes\n", - (curr - session->start) / 60); + (uint64_t)((curr - session->start) / 60)); } } From 8b1042c425f6a5a9fb57c5e1d5ecf5247cf899a6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 15 Aug 2024 15:38:23 -0700 Subject: [PATCH 193/347] perf annotate-data: Set bitfield member offset and size properly The bitfield members might not have DW_AT_data_member_location. Let's use DW_AT_data_bit_offset to set the member offset correct. Also use DW_AT_bit_size for the name like in a C program. Before: Annotate type: 'struct sk_buff' (1 samples) Percent Offset Size Field - 100.00 0 232 struct sk_buff { + 0.00 0 24 union ; + 0.00 24 8 union ; + 0.00 32 8 union ; 0.00 40 48 char[] cb; + 0.00 88 16 union ; 0.00 104 8 long unsigned int _nfct; 100.00 112 4 unsigned int len; 0.00 116 4 unsigned int data_len; 0.00 120 2 __u16 mac_len; 0.00 122 2 __u16 hdr_len; 0.00 124 2 __u16 queue_mapping; 0.00 126 0 __u8[] __cloned_offset; 0.00 0 1 __u8 cloned; 0.00 0 1 __u8 nohdr; 0.00 0 1 __u8 fclone; 0.00 0 1 __u8 peeked; 0.00 0 1 __u8 head_frag; 0.00 0 1 __u8 pfmemalloc; 0.00 0 1 __u8 pp_recycle; 0.00 127 1 __u8 active_extensions; + 0.00 128 60 union ; 0.00 188 4 sk_buff_data_t tail; 0.00 192 4 sk_buff_data_t end; 0.00 200 8 unsigned char* head; After: Annotate type: 'struct sk_buff' (1 samples) Percent Offset Size Field - 100.00 0 232 struct sk_buff { + 0.00 0 24 union ; + 0.00 24 8 union ; + 0.00 32 8 union ; 0.00 40 48 char[] cb + 0.00 88 16 union ; 0.00 104 8 long unsigned int _nfct; 100.00 112 4 unsigned int len; 0.00 116 4 unsigned int data_len; 0.00 120 2 __u16 mac_len; 0.00 122 2 __u16 hdr_len; 0.00 124 2 __u16 queue_mapping; 0.00 126 0 __u8[] __cloned_offset; 0.00 126 1 __u8 cloned:1; 0.00 126 1 __u8 nohdr:1; 0.00 126 1 __u8 fclone:2; 0.00 126 1 __u8 peeked:1; 0.00 126 1 __u8 head_frag:1; 0.00 126 1 __u8 pfmemalloc:1; 0.00 126 1 __u8 pp_recycle:1; 0.00 127 1 __u8 active_extensions; + 0.00 128 60 union ; 0.00 188 4 sk_buff_data_t tail; 0.00 192 4 sk_buff_data_t end; 0.00 200 8 unsigned char* head; Commiter notes: Collect some data: root@number:~# perf mem record -a --ldlat 5 -- ping -s 8193 -f 192.168.86.1 Memory events are enabled on a subset of CPUs: 16-27 PING 192.168.86.1 (192.168.86.1) 8193(8221) bytes of data. .^C --- 192.168.86.1 ping statistics --- 13881 packets transmitted, 13880 received, 0.00720409% packet loss, time 8664ms rtt min/avg/max/mdev = 0.510/0.599/7.768/0.115 ms, ipg/ewma 0.624/0.593 ms [ perf record: Woken up 8 times to write data ] [ perf record: Captured and wrote 14.877 MB perf.data (46785 samples) ] root@number:~# root@number:~# perf evlist cpu_atom/mem-loads,ldlat=5/P cpu_atom/mem-stores/P dummy:u root@number:~# perf evlist -v cpu_atom/mem-loads,ldlat=5/P: type: 10 (cpu_atom), size: 136, config: 0x5d0 (mem-loads), { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ADDR|CPU|PERIOD|IDENTIFIER|DATA_SRC|WEIGHT_STRUCT, read_format: ID|LOST, disabled: 1, inherit: 1, freq: 1, precise_ip: 3, sample_id_all: 1, { bp_addr, config1 }: 0x7 cpu_atom/mem-stores/P: type: 10 (cpu_atom), size: 136, config: 0x6d0 (mem-stores), { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|ADDR|CPU|PERIOD|IDENTIFIER|DATA_SRC|WEIGHT_STRUCT, read_format: ID|LOST, disabled: 1, inherit: 1, freq: 1, precise_ip: 3, sample_id_all: 1 dummy:u: type: 1 (software), size: 136, config: 0x9 (PERF_COUNT_SW_DUMMY), { sample_period, sample_freq }: 1, sample_type: IP|TID|TIME|ADDR|CPU|IDENTIFIER|DATA_SRC|WEIGHT_STRUCT, read_format: ID|LOST, inherit: 1, exclude_kernel: 1, exclude_hv: 1, mmap: 1, comm: 1, task: 1, mmap_data: 1, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 root@number:~# Ok, now lets see what changes from before this patch to after it: root@number:~# perf annotate --data-type > /tmp/before Apply the patch, build: root@number:~# perf annotate --data-type > /tmp/after The first hunk of the diff, for a glib data structure, in userspace, look at those bitfields: root@number:~# diff -u10 /tmp/before /tmp/after | head -20 --- /tmp/before 2024-08-20 17:29:58.306765780 -0300 +++ /tmp/after 2024-08-20 17:33:13.210582596 -0300 @@ -163,22 +163,22 @@ Annotate type: 'GHashTable' in /usr/lib64/libglib-2.0.so.0.8000.3 (1 samples): ============================================================================ Percent offset size field 100.00 0 96 GHashTable { 0.00 0 8 gsize size; 0.00 8 4 gint mod; 100.00 12 4 guint mask; 0.00 16 4 guint nnodes; 0.00 20 4 guint noccupied; - 0.00 0 4 guint have_big_keys; - 0.00 0 4 guint have_big_values; + 0.00 24 1 guint have_big_keys:1; + 0.00 24 1 guint have_big_values:1; 0.00 32 8 gpointer keys; 0.00 40 8 guint* hashes; 0.00 48 8 gpointer values; root@number:~# As advertised :-) Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240815223823.2402285-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 34 +++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index aa330c7d8edd..e5589268cb42 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -202,7 +202,7 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) struct annotated_member *parent = arg; struct annotated_member *member; Dwarf_Die member_type, die_mem; - Dwarf_Word size, loc; + Dwarf_Word size, loc, bit_size = 0; Dwarf_Attribute attr; struct strbuf sb; int tag; @@ -226,15 +226,37 @@ static int __add_member_cb(Dwarf_Die *die, void *arg) if (dwarf_aggregate_size(&die_mem, &size) < 0) size = 0; - if (!dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) - loc = 0; - else + if (dwarf_attr_integrate(die, DW_AT_data_member_location, &attr)) dwarf_formudata(&attr, &loc); + else { + /* bitfield member */ + if (dwarf_attr_integrate(die, DW_AT_data_bit_offset, &attr) && + dwarf_formudata(&attr, &loc) == 0) + loc /= 8; + else + loc = 0; + + if (dwarf_attr_integrate(die, DW_AT_bit_size, &attr) && + dwarf_formudata(&attr, &bit_size) == 0) + size = (bit_size + 7) / 8; + } member->type_name = strbuf_detach(&sb, NULL); /* member->var_name can be NULL */ - if (dwarf_diename(die)) - member->var_name = strdup(dwarf_diename(die)); + if (dwarf_diename(die)) { + if (bit_size) { + if (asprintf(&member->var_name, "%s:%ld", + dwarf_diename(die), (long)bit_size) < 0) + member->var_name = NULL; + } else { + member->var_name = strdup(dwarf_diename(die)); + } + + if (member->var_name == NULL) { + free(member); + return DIE_FIND_CB_END; + } + } member->size = size; member->offset = loc + parent->offset; INIT_LIST_HEAD(&member->children); From e25ebda78e230283bf707ae3e9655270ff40a7f9 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 6 Aug 2024 15:06:14 -0700 Subject: [PATCH 194/347] perf cap: Tidy up and improve capability testing Remove dependence on libcap. libcap is only used to query whether a capability is supported, which is just 1 capget system call. If the capget system call fails, fall back on root permission checking. Previously if libcap fails then the permission is assumed not present which may be pessimistic/wrong. Add a used_root out argument to perf_cap__capable to say whether the fall back root check was used. This allows the correct error message, "root" vs "users with the CAP_PERFMON or CAP_SYS_ADMIN capability", to be selected. Tidy uses of perf_cap__capable so that tests aren't repeated if capget isn't supported. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Athira Rajeev Cc: Changbin Du Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Namhyung Kim Cc: Oliver Upton Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240806220614.831914-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 11 ------- tools/perf/builtin-ftrace.c | 28 +++++++++--------- tools/perf/util/Build | 2 +- tools/perf/util/cap.c | 59 ++++++++++++++++++++++++++----------- tools/perf/util/cap.h | 23 ++------------- tools/perf/util/symbol.c | 8 ++--- tools/perf/util/util.c | 12 ++++++-- 7 files changed, 73 insertions(+), 70 deletions(-) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index fa679db61f62..4eb1fc897baf 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -1031,17 +1031,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 diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c index a615c405d98f..88a87bf387d2 100644 --- a/tools/perf/builtin-ftrace.c +++ b/tools/perf/builtin-ftrace.c @@ -63,20 +63,21 @@ static void ftrace__workload_exec_failed_signal(int signo __maybe_unused, done = true; } -static int check_ftrace_capable(void) +static bool check_ftrace_capable(void) { - 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 + 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 -1; - } - return 0; + return false; } static int __write_tracing_file(const char *name, const char *val, bool append) @@ -1579,8 +1580,7 @@ int cmd_ftrace(int argc, const char **argv) signal(SIGCHLD, sig_handler); signal(SIGPIPE, sig_handler); - ret = check_ftrace_capable(); - if (ret < 0) + if (!check_ftrace_capable()) return -1; ret = perf_config(perf_ftrace_config, &ftrace); diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 7ea261416c14..b87f918bdfe7 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -223,7 +223,7 @@ perf-util-$(CONFIG_ZLIB) += zlib.o perf-util-$(CONFIG_LZMA) += lzma.o perf-util-$(CONFIG_ZSTD) += zstd.o -perf-util-$(CONFIG_LIBCAP) += cap.o +perf-util-y += cap.o perf-util-$(CONFIG_CXX_DEMANGLE) += demangle-cxx.o perf-util-y += demangle-ocaml.o diff --git a/tools/perf/util/cap.c b/tools/perf/util/cap.c index c3ba841bbf37..7574a67651bc 100644 --- a/tools/perf/util/cap.c +++ b/tools/perf/util/cap.c @@ -3,27 +3,52 @@ * Capability utilities */ -#ifdef HAVE_LIBCAP_SUPPORT - #include "cap.h" -#include -#include +#include "debug.h" +#include +#include +#include +#include +#include -bool perf_cap__capable(cap_value_t cap) +#ifndef SYS_capget +#define SYS_capget 90 +#endif + +#define MAX_LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_3 + +bool perf_cap__capable(int cap, bool *used_root) { - cap_flag_value_t val; - cap_t caps = cap_get_proc(); + struct __user_cap_header_struct header = { + .version = _LINUX_CAPABILITY_VERSION_3, + .pid = getpid(), + }; + struct __user_cap_data_struct data[MAX_LINUX_CAPABILITY_U32S]; + __u32 cap_val; - if (!caps) - return false; + *used_root = false; + while (syscall(SYS_capget, &header, &data[0]) == -1) { + /* Retry, first attempt has set the header.version correctly. */ + if (errno == EINVAL && header.version != _LINUX_CAPABILITY_VERSION_3 && + header.version == _LINUX_CAPABILITY_VERSION_1) + continue; - if (cap_get_flag(caps, cap, CAP_EFFECTIVE, &val) != 0) - val = CAP_CLEAR; + pr_debug2("capget syscall failed (%s - %d) fall back on root check\n", + strerror(errno), errno); + *used_root = true; + return geteuid() == 0; + } - if (cap_free(caps) != 0) - return false; - - return val == CAP_SET; + /* Extract the relevant capability bit. */ + if (cap >= 32) { + if (header.version == _LINUX_CAPABILITY_VERSION_3) { + cap_val = data[1].effective; + } else { + /* Capability beyond 32 is requested but only 32 are supported. */ + return false; + } + } else { + cap_val = data[0].effective; + } + return (cap_val & (1 << (cap & 0x1f))) != 0; } - -#endif /* HAVE_LIBCAP_SUPPORT */ diff --git a/tools/perf/util/cap.h b/tools/perf/util/cap.h index ae52878c0b2e..0c6a1ff55f07 100644 --- a/tools/perf/util/cap.h +++ b/tools/perf/util/cap.h @@ -3,26 +3,6 @@ #define __PERF_CAP_H #include -#include -#include - -#ifdef HAVE_LIBCAP_SUPPORT - -#include - -bool perf_cap__capable(cap_value_t cap); - -#else - -#include -#include - -static inline bool perf_cap__capable(int cap __maybe_unused) -{ - return geteuid() == 0; -} - -#endif /* HAVE_LIBCAP_SUPPORT */ /* For older systems */ #ifndef CAP_SYSLOG @@ -33,4 +13,7 @@ static inline bool perf_cap__capable(int cap __maybe_unused) #define CAP_PERFMON 38 #endif +/* Query if a capability is supported, used_root is set if the fallback root check was used. */ +bool perf_cap__capable(int cap, bool *used_root); + #endif /* __PERF_CAP_H */ diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index 19eb623e0826..a18927d792af 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -2425,14 +2425,14 @@ static bool symbol__read_kptr_restrict(void) { bool value = false; FILE *fp = fopen("/proc/sys/kernel/kptr_restrict", "r"); + bool used_root; + bool cap_syslog = perf_cap__capable(CAP_SYSLOG, &used_root); if (fp != NULL) { char line[8]; if (fgets(line, sizeof(line), fp) != NULL) - value = perf_cap__capable(CAP_SYSLOG) ? - (atoi(line) >= 2) : - (atoi(line) != 0); + value = cap_syslog ? (atoi(line) >= 2) : (atoi(line) != 0); fclose(fp); } @@ -2440,7 +2440,7 @@ static bool symbol__read_kptr_restrict(void) /* Per kernel/kallsyms.c: * we also restrict when perf_event_paranoid > 1 w/o CAP_SYSLOG */ - if (perf_event_paranoid() > 1 && !perf_cap__capable(CAP_SYSLOG)) + if (perf_event_paranoid() > 1 && !cap_syslog) value = true; return value; diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 4f561e5e4162..9d55a13787ce 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -325,9 +325,15 @@ int perf_event_paranoid(void) bool perf_event_paranoid_check(int max_level) { - return perf_cap__capable(CAP_SYS_ADMIN) || - perf_cap__capable(CAP_PERFMON) || - perf_event_paranoid() <= max_level; + bool used_root; + + if (perf_cap__capable(CAP_SYS_ADMIN, &used_root)) + return true; + + if (!used_root && perf_cap__capable(CAP_PERFMON, &used_root)) + return true; + + return perf_event_paranoid() <= max_level; } static int From 922ec313f061cf75157d8e93fe16bc6397f6ea25 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 20 Aug 2024 23:54:06 -0700 Subject: [PATCH 195/347] perf annotate-data: Fix missing constant copy I found it missed to copy the immediate constant when it moves the register value. This could result in a wrong type inference since the address for the per-cpu variable would be 0 always. Fixes: eb9190afaed6afd5 ("perf annotate-data: Handle ADD instructions") Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240821065408.285548-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/annotate/instructions.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/arch/x86/annotate/instructions.c b/tools/perf/arch/x86/annotate/instructions.c index 7b7d462c6c6b..88b5bcf2116f 100644 --- a/tools/perf/arch/x86/annotate/instructions.c +++ b/tools/perf/arch/x86/annotate/instructions.c @@ -382,6 +382,7 @@ static void update_insn_state_x86(struct type_state *state, 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; pr_debug_dtp("mov [%x] reg%d -> reg%d", From 4a32a97268d304d5708f1212c4a4d0e1dd3246dd Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 20 Aug 2024 23:54:07 -0700 Subject: [PATCH 196/347] perf annotate-data: Prefer struct/union over base type Sometimes a compound type can have a single field and the size is the same as the base type. But it's still preferred as struct or union could carry more information than the base type. Also put a slight priority on the typedef for the same reason. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240821065408.285548-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index e5589268cb42..bc65264084d8 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -404,6 +404,13 @@ static bool is_pointer_type(Dwarf_Die *type_die) return tag == DW_TAG_pointer_type || tag == DW_TAG_array_type; } +static bool is_compound_type(Dwarf_Die *type_die) +{ + int tag = dwarf_tag(type_die); + + return tag == DW_TAG_structure_type || tag == DW_TAG_union_type; +} + /* returns if Type B has better information than Type A */ static bool is_better_type(Dwarf_Die *type_a, Dwarf_Die *type_b) { @@ -433,7 +440,18 @@ static bool is_better_type(Dwarf_Die *type_a, Dwarf_Die *type_b) dwarf_aggregate_size(type_b, &size_b) < 0) return false; - return size_a < size_b; + if (size_a != size_b) + return size_a < size_b; + + /* struct or union is preferred */ + if (is_compound_type(type_a) != is_compound_type(type_b)) + return is_compound_type(type_b); + + /* typedef is preferred */ + if (dwarf_tag(type_b) == DW_TAG_typedef) + return true; + + return false; } /* The type info will be saved in @type_die */ From 4d6d6e0f61e2267103e9b013d2a82d04ff278127 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 20 Aug 2024 23:54:08 -0700 Subject: [PATCH 197/347] perf annotate-data: Fix percpu pointer check In check_matching_type(), it checks the type state of the register in a wrong order. When it's the percpu pointer, it should check the type for the pointer, but it checks the CFA bit first and thought it has no type in the stack slot. This resulted in no type info. ----------------------------------------------------------- find data type for 0x28(reg1) at hrtimer_reprogram+0x88 CU for kernel/time/hrtimer.c (die:0x18f219f) frame base: cfa=1 fbreg=7 ... add [72] percpu 0x24500 -> reg1 pointer type='struct hrtimer_cpu_base' size=0x240 (die:0x18f6d46) bb: [7a - 7e] bb: [80 - 86] (here) bb: [88 - 88] vvv chk [88] reg1 offset=0x28 ok=1 kind=4 cfa : no type information no type information Here, instruction at 0x72 found reg1 has a (percpu) pointer and got the correct type. But when it checks the final result, it wrongly thought it was stack variable because it checks the cfa bit first. After changing the order of state check: ----------------------------------------------------------- find data type for 0x28(reg1) at hrtimer_reprogram+0x88 CU for kernel/time/hrtimer.c (die:0x18f219f) frame base: cfa=1 fbreg=7 ... (here) vvvvvvvvvv chk [88] reg1 offset=0x28 ok=1 kind=4 percpu ptr : Good! found by insn track: 0x28(reg1) type-offset=0x28 final type: type='struct hrtimer_cpu_base' size=0x240 (die:0x18f6d46) Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240821065408.285548-3-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/annotate/instructions.c | 3 + tools/perf/util/annotate-data.c | 122 ++++++++++---------- 2 files changed, 66 insertions(+), 59 deletions(-) diff --git a/tools/perf/arch/x86/annotate/instructions.c b/tools/perf/arch/x86/annotate/instructions.c index 88b5bcf2116f..15dfc2988e24 100644 --- a/tools/perf/arch/x86/annotate/instructions.c +++ b/tools/perf/arch/x86/annotate/instructions.c @@ -282,6 +282,7 @@ static void update_insn_state_x86(struct type_state *state, !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; } } @@ -533,9 +534,11 @@ retry: &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; } } diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index bc65264084d8..f5eefcb71c4f 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -965,7 +965,10 @@ static enum type_match_result check_matching_type(struct type_state *state, insn_offset, reg, dloc->op->offset, state->regs[reg].ok, state->regs[reg].kind); - if (state->regs[reg].ok && state->regs[reg].kind == TSR_KIND_TYPE) { + if (!state->regs[reg].ok) + goto check_non_register; + + if (state->regs[reg].kind == TSR_KIND_TYPE) { Dwarf_Die sized_type; /* @@ -998,6 +1001,65 @@ static enum type_match_result check_matching_type(struct type_state *state, return PERF_TMR_OK; } + if (state->regs[reg].kind == TSR_KIND_POINTER) { + pr_debug_dtp("percpu ptr"); + + /* + * It's actaully pointer but the address was calculated using + * some arithmetic. So it points to the actual type already. + */ + *type_die = state->regs[reg].type; + + dloc->type_offset = dloc->op->offset; + + /* Get the size of the actual type */ + if (dwarf_aggregate_size(type_die, &size) < 0 || + (unsigned)dloc->type_offset >= size) + return PERF_TMR_BAIL_OUT; + + return PERF_TMR_OK; + } + + if (state->regs[reg].kind == TSR_KIND_CANARY) { + pr_debug_dtp("stack canary"); + + /* + * This is a saved value of the stack canary which will be handled + * in the outer logic when it returns failure here. Pretend it's + * from the stack canary directly. + */ + setup_stack_canary(dloc); + + return PERF_TMR_BAIL_OUT; + } + + if (state->regs[reg].kind == TSR_KIND_PERCPU_BASE) { + u64 var_addr = dloc->op->offset; + int var_offset; + + pr_debug_dtp("percpu var"); + + if (dloc->op->multi_regs) { + int reg2 = dloc->op->reg2; + + if (dloc->op->reg2 == reg) + reg2 = dloc->op->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; + } + + if (get_global_var_type(cu_die, dloc, dloc->ip, var_addr, + &var_offset, type_die)) { + dloc->type_offset = var_offset; + return PERF_TMR_OK; + } + /* No need to retry per-cpu (global) variables */ + return PERF_TMR_BAIL_OUT; + } + +check_non_register: if (reg == dloc->fbreg) { struct type_state_stack *stack; @@ -1054,64 +1116,6 @@ static enum type_match_result check_matching_type(struct type_state *state, return PERF_TMR_OK; } - if (state->regs[reg].kind == TSR_KIND_PERCPU_BASE) { - u64 var_addr = dloc->op->offset; - int var_offset; - - pr_debug_dtp("percpu var"); - - if (dloc->op->multi_regs) { - int reg2 = dloc->op->reg2; - - if (dloc->op->reg2 == reg) - reg2 = dloc->op->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; - } - - if (get_global_var_type(cu_die, dloc, dloc->ip, var_addr, - &var_offset, type_die)) { - dloc->type_offset = var_offset; - return PERF_TMR_OK; - } - /* No need to retry per-cpu (global) variables */ - return PERF_TMR_BAIL_OUT; - } - - if (state->regs[reg].ok && state->regs[reg].kind == TSR_KIND_POINTER) { - pr_debug_dtp("percpu ptr"); - - /* - * It's actaully pointer but the address was calculated using - * some arithmetic. So it points to the actual type already. - */ - *type_die = state->regs[reg].type; - - dloc->type_offset = dloc->op->offset; - - /* Get the size of the actual type */ - if (dwarf_aggregate_size(type_die, &size) < 0 || - (unsigned)dloc->type_offset >= size) - return PERF_TMR_BAIL_OUT; - - return PERF_TMR_OK; - } - - if (state->regs[reg].ok && state->regs[reg].kind == TSR_KIND_CANARY) { - pr_debug_dtp("stack canary"); - - /* - * This is a saved value of the stack canary which will be handled - * in the outer logic when it returns failure here. Pretend it's - * from the stack canary directly. - */ - setup_stack_canary(dloc); - - return PERF_TMR_BAIL_OUT; - } - check_kernel: if (dso__kernel(map__dso(dloc->ms->map))) { u64 addr; From ce66d7c703d32851ceb50511e634712049f0c1c1 Mon Sep 17 00:00:00 2001 From: Yang Ruibin <11162571@vivo.com> Date: Wed, 21 Aug 2024 06:14:56 -0400 Subject: [PATCH 198/347] perf bpf: Remove redundant check that map is NULL The check that map is NULL is already done in the bpf_map__fd(map) and returns an errno, which does not run further checks. In addition, even if the check for map is run, the return is a pointer, which is not consistent with the err_number returned by bpf_map__fd(map). Signed-off-by: Yang Ruibin <11162571@vivo.com> Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Stephen Rothwell Cc: opensource.kernel@vivo.com Link: https://lore.kernel.org/r/20240821101500.4568-1-11162571@vivo.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf_map.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/perf/util/bpf_map.c b/tools/perf/util/bpf_map.c index c863ae0c5cb5..578f27d2d6b4 100644 --- a/tools/perf/util/bpf_map.c +++ b/tools/perf/util/bpf_map.c @@ -35,9 +35,6 @@ int bpf_map__fprintf(struct bpf_map *map, FILE *fp) if (fd < 0) return fd; - if (!map) - return PTR_ERR(map); - err = -ENOMEM; key = malloc(bpf_map__key_size(map)); if (key == NULL) From 7a5c2170244b4f55588353e68ebbafc249597ee6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 19 Aug 2024 16:36:02 -0700 Subject: [PATCH 199/347] perf annotate-data: Show offset and size in hex It'd be better to have them in hex to check cacheline alignment. Percent offset size field 100.00 0 0x1c0 struct cfs_rq { 0.00 0 0x10 struct load_weight load { 0.00 0 0x8 long unsigned int weight; 0.00 0x8 0x4 u32 inv_weight; }; 0.00 0x10 0x4 unsigned int nr_running; 14.56 0x14 0x4 unsigned int h_nr_running; 0.00 0x18 0x4 unsigned int idle_nr_running; 0.00 0x1c 0x4 unsigned int idle_h_nr_running; ... Committer notes: Justification from Namhyung when asked about why it would be "better": Cache line sizes are power of 2 so it'd be natural to use hex and check whether an offset is in the same boundary. Also 'perf annotate' shows instruction offsets in hex. > > Maybe this should be selectable? I can add an option and/or a config if you want. Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240819233603.54941-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/browsers/annotate-data.c | 4 ++-- tools/perf/util/annotate-data.c | 2 +- tools/perf/util/sort.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/ui/browsers/annotate-data.c b/tools/perf/ui/browsers/annotate-data.c index f563a3bb072c..cd562a8822b7 100644 --- a/tools/perf/ui/browsers/annotate-data.c +++ b/tools/perf/ui/browsers/annotate-data.c @@ -427,12 +427,12 @@ static void browser__write(struct ui_browser *uib, void *entry, int row) /* print type info */ if (be->indent == 0 && !member->var_name) { - ui_browser__printf(uib, " %10d %10d %s%s", + ui_browser__printf(uib, " %#10x %#10x %s%s", member->offset, member->size, member->type_name, list_empty(&member->children) || be->folded? ";" : " {"); } else { - ui_browser__printf(uib, " %10d %10d %*s%s\t%s%s", + ui_browser__printf(uib, " %#10x %#10x %*s%s\t%s%s", member->offset, member->size, be->indent * 4, "", member->type_name, member->var_name ?: "", diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index f5eefcb71c4f..1e8328dde720 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -1704,7 +1704,7 @@ static void print_annotated_data_type(struct annotated_data_type *mem_type, nr_events++; } - printf(" %10d %10d %*s%s\t%s", + printf(" %#10x %#10x %*s%s\t%s", member->offset, member->size, indent, "", member->type_name, member->var_name ?: ""); diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index c4046d5d1749..d315308f9170 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -2312,7 +2312,7 @@ static int hist_entry__typeoff_snprintf(struct hist_entry *he, char *bf, he->mem_type_off, true); buf[4095] = '\0'; - return repsep_snprintf(bf, size, "%s %+d (%s)", he_type->self.type_name, + return repsep_snprintf(bf, size, "%s +%#x (%s)", he_type->self.type_name, he->mem_type_off, buf); } From fd45d52eae5c42fdb68802127fa98d3718c80043 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 19 Aug 2024 16:36:03 -0700 Subject: [PATCH 200/347] perf annotate-data: Add 'typecln' sort key Sometimes it's useful to organize member fields in cache-line boundary. The 'typecln' sort key is short for type-cacheline and to show samples in each cacheline. The cacheline size is fixed to 64 for now, but it can read the actual size once it saves the value from sysfs. For example, you maybe want to which cacheline in a target is hot or cold. The following shows members in the cfs_rq's first cache line. $ perf report -s type,typecln,typeoff -H ... - 2.67% struct cfs_rq + 1.23% struct cfs_rq: cache-line 2 + 0.57% struct cfs_rq: cache-line 4 + 0.46% struct cfs_rq: cache-line 6 - 0.41% struct cfs_rq: cache-line 0 0.39% struct cfs_rq +0x14 (h_nr_running) 0.02% struct cfs_rq +0x38 (tasks_timeline.rb_leftmost) ... Committer testing: # root@number:~# perf report -s type,typecln,typeoff -H --stdio # Total Lost Samples: 0 # # Samples: 5K of event 'cpu_atom/mem-loads,ldlat=5/P' # Event count (approx.): 312251 # # Overhead Data Type / Data Type Cacheline / Data Type Offset # .............. .................................................. # 0.07% struct sigaction 0.05% struct sigaction: cache-line 1 0.02% struct sigaction +0x58 (sa_mask) 0.02% struct sigaction +0x78 (sa_mask) 0.03% struct sigaction: cache-line 0 0.02% struct sigaction +0x38 (sa_mask) 0.01% struct sigaction +0x8 (sa_mask) Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240819233603.54941-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.h | 1 + tools/perf/util/sort.c | 52 ++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/sort.h | 1 + 3 files changed, 54 insertions(+) diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index 30c13fc8cbe4..deb1087c5948 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -86,6 +86,7 @@ enum hist_column { HISTC_TYPE, HISTC_TYPE_OFFSET, HISTC_SYMBOL_OFFSET, + HISTC_TYPE_CACHELINE, HISTC_NR_COLS, /* Last entry */ }; diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index d315308f9170..013020f33ece 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -2326,6 +2326,57 @@ struct sort_entry sort_type_offset = { .se_width_idx = HISTC_TYPE_OFFSET, }; +/* --sort typecln */ + +/* TODO: use actual value in the system */ +#define TYPE_CACHELINE_SIZE 64 + +static int64_t +sort__typecln_sort(struct hist_entry *left, struct hist_entry *right) +{ + struct annotated_data_type *left_type = left->mem_type; + struct annotated_data_type *right_type = right->mem_type; + int64_t left_cln, right_cln; + int64_t ret; + + if (!left_type) { + sort__type_init(left); + left_type = left->mem_type; + } + + if (!right_type) { + sort__type_init(right); + right_type = right->mem_type; + } + + ret = strcmp(left_type->self.type_name, right_type->self.type_name); + if (ret) + return ret; + + left_cln = left->mem_type_off / TYPE_CACHELINE_SIZE; + right_cln = right->mem_type_off / TYPE_CACHELINE_SIZE; + return left_cln - right_cln; +} + +static int hist_entry__typecln_snprintf(struct hist_entry *he, char *bf, + size_t size, unsigned int width __maybe_unused) +{ + struct annotated_data_type *he_type = he->mem_type; + + return repsep_snprintf(bf, size, "%s: cache-line %d", he_type->self.type_name, + he->mem_type_off / TYPE_CACHELINE_SIZE); +} + +struct sort_entry sort_type_cacheline = { + .se_header = "Data Type Cacheline", + .se_cmp = sort__type_cmp, + .se_collapse = sort__typecln_sort, + .se_sort = sort__typecln_sort, + .se_init = sort__type_init, + .se_snprintf = hist_entry__typecln_snprintf, + .se_width_idx = HISTC_TYPE_CACHELINE, +}; + struct sort_dimension { const char *name; @@ -2384,6 +2435,7 @@ static struct sort_dimension common_sort_dimensions[] = { DIM(SORT_ANNOTATE_DATA_TYPE, "type", sort_type), DIM(SORT_ANNOTATE_DATA_TYPE_OFFSET, "typeoff", sort_type_offset), DIM(SORT_SYM_OFFSET, "symoff", sort_sym_offset), + DIM(SORT_ANNOTATE_DATA_TYPE_CACHELINE, "typecln", sort_type_cacheline), }; #undef DIM diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h index 6357bc32c5ca..9ff68c6786e7 100644 --- a/tools/perf/util/sort.h +++ b/tools/perf/util/sort.h @@ -71,6 +71,7 @@ enum sort_type { SORT_ANNOTATE_DATA_TYPE, SORT_ANNOTATE_DATA_TYPE_OFFSET, SORT_SYM_OFFSET, + SORT_ANNOTATE_DATA_TYPE_CACHELINE, /* branch stack specific sort keys */ __SORT_BRANCH_STACK, From a11b4222bb579dcf9646f3c4ecd2212ae762a2c8 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 21 Aug 2024 16:26:25 -0700 Subject: [PATCH 201/347] perf dwarf-aux: Handle bitfield members from pointer access The __die_find_member_offset_cb() missed to handle bitfield members which don't have DW_AT_data_member_location. Like in adding member types in __add_member_cb() it should fallback to check the bit offset when it resolves the member type for an offset. Fixes: 437683a9941891c1 ("perf dwarf-aux: Handle type transfer for memory access") Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240821232628.353177-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/dwarf-aux.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c index 0151a8d14350..92eb9c8dc3e5 100644 --- a/tools/perf/util/dwarf-aux.c +++ b/tools/perf/util/dwarf-aux.c @@ -1977,8 +1977,15 @@ static int __die_find_member_offset_cb(Dwarf_Die *die_mem, void *arg) return DIE_FIND_CB_SIBLING; /* Unions might not have location */ - if (die_get_data_member_location(die_mem, &loc) < 0) - loc = 0; + if (die_get_data_member_location(die_mem, &loc) < 0) { + Dwarf_Attribute attr; + + if (dwarf_attr_integrate(die_mem, DW_AT_data_bit_offset, &attr) && + dwarf_formudata(&attr, &loc) == 0) + loc /= 8; + else + loc = 0; + } if (offset == loc) return DIE_FIND_CB_END; From a0d57c606188ebf106f26951558c3801627dd0a1 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 21 Aug 2024 16:26:26 -0700 Subject: [PATCH 202/347] perf annotate-data: Update debug messages In check_matching_type(), it'd be easier to display the typename in question if it's available. For example, check out the line starts with 'chk'. ----------------------------------------------------------- find data type for 0x10(reg0) at cpuacct_charge+0x13 CU for kernel/sched/build_utility.c (die:0x137ee0b) frame base: cfa=1 fbreg=7 scope: [3/3] (die:13d9632) bb: [c - 13] var [c] reg5 type='struct task_struct*' size=0x8 (die:0x1381230) mov [c] 0xdf8(reg5) -> reg0 type='struct css_set*' size=0x8 (die:0x1385c56) chk [13] reg0 offset=0x10 ok=1 kind=1 (struct css_set*) : Good! <<<--- here found by insn track: 0x10(reg0) type-offset=0x10 final result: type='struct css_set' size=0x250 (die:0x1385b0e) Another example: ----------------------------------------------------------- find data type for 0x8(reg0) at menu_select+0x279 CU for drivers/cpuidle/governors/menu.c (die:0x7b0fe79) frame base: cfa=1 fbreg=7 scope: [2/2] (die:7b11010) bb: [273 - 277] bb: [279 - 279] chk [279] reg0 offset=0x8 ok=0 kind=0 cfa : no type information scope: [1/2] (die:7b10cbc) bb: [0 - 64] ... mov [26a] imm=0xffffffff -> reg15 bb: [273 - 277] bb: [279 - 279] chk [279] reg0 offset=0x8 ok=1 kind=1 (long long unsigned int) : no/void pointer <<<--- here final result: no/void pointer Also change some places to print negative offsets properly. Before: ----------------------------------------------------------- find data type for 0xffffff40(reg6) at __tcp_transmit_skb+0x58 After: ----------------------------------------------------------- find data type for -0xc0(reg6) at __tcp_transmit_skb+0x58 Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240821232628.353177-3-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 45 +++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 1e8328dde720..7d4b5aaa3b6a 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -960,9 +960,16 @@ static enum type_match_result check_matching_type(struct type_state *state, Dwarf_Word size; u32 insn_offset = dloc->ip - dloc->ms->sym->start; int reg = dloc->op->reg1; + int offset = dloc->op->offset; + const char *offset_sign = ""; - pr_debug_dtp("chk [%x] reg%d offset=%#x ok=%d kind=%d ", - insn_offset, reg, dloc->op->offset, + if (offset < 0) { + offset = -offset; + offset_sign = "-"; + } + + pr_debug_dtp("chk [%x] reg%d offset=%s%#x ok=%d kind=%d ", + insn_offset, reg, offset_sign, offset, state->regs[reg].ok, state->regs[reg].kind); if (!state->regs[reg].ok) @@ -970,6 +977,12 @@ static enum type_match_result check_matching_type(struct type_state *state, if (state->regs[reg].kind == TSR_KIND_TYPE) { Dwarf_Die sized_type; + struct strbuf sb; + + strbuf_init(&sb, 32); + die_get_typename_from_type(&state->regs[reg].type, &sb); + pr_debug_dtp("(%s)", sb.buf); + strbuf_release(&sb); /* * Normal registers should hold a pointer (or array) to @@ -1119,7 +1132,6 @@ check_non_register: check_kernel: if (dso__kernel(map__dso(dloc->ms->map))) { u64 addr; - int offset; /* Direct this-cpu access like "%gs:0x34740" */ if (dloc->op->segment == INSN_SEG_X86_GS && dloc->op->imm && @@ -1271,6 +1283,13 @@ again: cu_die, type_die); if (ret == PERF_TMR_OK) { char buf[64]; + int offset = dloc->op->offset; + const char *offset_sign = ""; + + if (offset < 0) { + offset = -offset; + offset_sign = "-"; + } if (dloc->op->multi_regs) snprintf(buf, sizeof(buf), "reg%d, reg%d", @@ -1278,8 +1297,8 @@ again: else snprintf(buf, sizeof(buf), "reg%d", dloc->op->reg1); - pr_debug_dtp("found by insn track: %#x(%s) type-offset=%#x\n", - dloc->op->offset, buf, dloc->type_offset); + pr_debug_dtp("found by insn track: %s%#x(%s) type-offset=%#x\n", + offset_sign, offset, buf, dloc->type_offset); break; } @@ -1302,7 +1321,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) struct annotated_op_loc *loc = dloc->op; Dwarf_Die cu_die, var_die; Dwarf_Die *scopes = NULL; - int reg, offset; + int reg, offset = loc->offset; int ret = -1; int i, nr_scopes; int fbreg = -1; @@ -1312,6 +1331,7 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) u64 pc; char buf[64]; enum type_match_result result = PERF_TMR_UNKNOWN; + const char *offset_sign = ""; if (dloc->op->multi_regs) snprintf(buf, sizeof(buf), "reg%d, reg%d", dloc->op->reg1, dloc->op->reg2); @@ -1320,10 +1340,15 @@ static int find_data_type_die(struct data_loc_info *dloc, Dwarf_Die *type_die) else snprintf(buf, sizeof(buf), "reg%d", dloc->op->reg1); + if (offset < 0) { + offset = -offset; + offset_sign = "-"; + } + pr_debug_dtp("-----------------------------------------------------------\n"); - pr_debug_dtp("find data type for %#x(%s) at %s+%#"PRIx64"\n", - dloc->op->offset, buf, dloc->ms->sym->name, - dloc->ip - dloc->ms->sym->start); + pr_debug_dtp("find data type for %s%#x(%s) at %s+%#"PRIx64"\n", + offset_sign, offset, buf, + dloc->ms->sym->name, dloc->ip - dloc->ms->sym->start); /* * IP is a relative instruction address from the start of the map, as @@ -1453,8 +1478,8 @@ retry: } out: + pr_debug_dtp("final result: "); if (found) { - pr_debug_dtp("final type:"); pr_debug_type_name(type_die, TSR_KIND_TYPE); ret = 0; } else { From 895891dad7353d6058a8e5c499cf814fe7de9388 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 21 Aug 2024 16:26:27 -0700 Subject: [PATCH 203/347] perf annotate-data: Update stack slot for the store When checking the match variable at the target instruction, it might not have any information if it's a first write to a stack slot. In this case it could spill a register value into the stack so the type info is in the source operand. But currently it's hard to get the operand from the checking function. Let's process the instruction and retry to get the type info from the stack if there's no information already. This is an example of __tcp_transmit_skb(). The instructions are <__tcp_transmit_skb>: 0: nopl 0x0(%rax, %rax, 1) 5: push %rbp 6: mov %rsp, %rbp 9: push %r15 b: push %r14 d: push %r13 f: push %r12 11: push %rbx 12: sub $0x98, %rsp 19: mov %r8d, -0xa8(%rbp) ... It cannot find any variable at -0xa8(%rbp) at this point. ----------------------------------------------------------- find data type for -0xa8(reg6) at __tcp_transmit_skb+0x19 CU for net/ipv4/tcp_output.c (die:0x817f543) frame base: cfa=0 fbreg=6 scope: [1/1] (die:81aac3e) bb: [0 - 19] var [0] -0x98(stack) type='struct tcp_out_options' size=0x28 (die:0x81af3df) var [5] reg8 type='unsigned int' size=0x4 (die:0x8180ed6) var [5] reg2 type='unsigned int' size=0x4 (die:0x8180ed6) var [5] reg1 type='int' size=0x4 (die:0x818059e) var [5] reg4 type='struct sk_buff*' size=0x8 (die:0x8181360) var [5] reg5 type='struct sock*' size=0x8 (die:0x8181a0c) chk [19] reg6 offset=-0xa8 ok=0 kind=0 fbreg : no type information no type information And it was able to find the type after processing the 'mov' instruction. ----------------------------------------------------------- find data type for -0xa8(reg6) at __tcp_transmit_skb+0x19 CU for net/ipv4/tcp_output.c (die:0x817f543) frame base: cfa=0 fbreg=6 scope: [1/1] (die:81aac3e) bb: [0 - 19] var [0] -0x98(stack) type='struct tcp_out_options' size=0x28 (die:0x81af3df) var [5] reg8 type='unsigned int' size=0x4 (die:0x8180ed6) var [5] reg2 type='unsigned int' size=0x4 (die:0x8180ed6) var [5] reg1 type='int' size=0x4 (die:0x818059e) var [5] reg4 type='struct sk_buff*' size=0x8 (die:0x8181360) var [5] reg5 type='struct sock*' size=0x8 (die:0x8181a0c) chk [19] reg6 offset=-0xa8 ok=0 kind=0 fbreg : retry <<<--- here mov [19] reg8 -> -0xa8(stack) type='unsigned int' size=0x4 (die:0x8180ed6) chk [19] reg6 offset=-0xa8 ok=0 kind=0 fbreg : Good! found by insn track: -0xa8(reg6) type-offset=0 final result: type='unsigned int' size=0x4 (die:0x8180ed6) Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240821232628.353177-4-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 7d4b5aaa3b6a..5d23c30b3a7b 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -955,19 +955,22 @@ static void setup_stack_canary(struct data_loc_info *dloc) static enum type_match_result check_matching_type(struct type_state *state, struct data_loc_info *dloc, Dwarf_Die *cu_die, + struct disasm_line *dl, Dwarf_Die *type_die) { Dwarf_Word size; - u32 insn_offset = dloc->ip - dloc->ms->sym->start; + u32 insn_offset = dl->al.offset; int reg = dloc->op->reg1; int offset = dloc->op->offset; const char *offset_sign = ""; + bool retry = true; if (offset < 0) { offset = -offset; offset_sign = "-"; } +again: pr_debug_dtp("chk [%x] reg%d offset=%s%#x ok=%d kind=%d ", insn_offset, reg, offset_sign, offset, state->regs[reg].ok, state->regs[reg].kind); @@ -1079,8 +1082,17 @@ check_non_register: pr_debug_dtp("fbreg"); stack = find_stack_state(state, dloc->type_offset); - if (stack == NULL) + if (stack == NULL) { + if (retry) { + pr_debug_dtp(" : retry\n"); + retry = false; + + /* update type info it's the first store to the stack */ + update_insn_state(state, dloc, cu_die, dl); + goto again; + } return PERF_TMR_NO_TYPE; + } if (stack->kind == TSR_KIND_CANARY) { setup_stack_canary(dloc); @@ -1111,8 +1123,17 @@ check_non_register: return PERF_TMR_NO_TYPE; stack = find_stack_state(state, dloc->type_offset - fboff); - if (stack == NULL) + if (stack == NULL) { + if (retry) { + pr_debug_dtp(" : retry\n"); + retry = false; + + /* update type info it's the first store to the stack */ + update_insn_state(state, dloc, cu_die, dl); + goto again; + } return PERF_TMR_NO_TYPE; + } if (stack->kind == TSR_KIND_CANARY) { setup_stack_canary(dloc); @@ -1202,7 +1223,7 @@ static enum type_match_result find_data_type_insn(struct data_loc_info *dloc, if (this_ip == dloc->ip) { ret = check_matching_type(&state, dloc, - cu_die, type_die); + cu_die, dl, type_die); pr_debug_dtp(" : %s\n", match_result_str(ret)); goto out; } From 1cfd01eb602d73b92df2ffc24196cd0a3dc3efb2 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Wed, 21 Aug 2024 16:26:28 -0700 Subject: [PATCH 204/347] perf annotate-data: Copy back variable types after move In some cases, compilers don't set the location expression in DWARF precisely. For instance, it may assign a variable to a register after copying it from a different register. Then it should use the register for the new type but still uses the old register. This makes hard to track the type information properly. This is an example I found in __tcp_transmit_skb(). The first argument (sk) of this function is a pointer to sock and there's a variable (tp) for tcp_sock. static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, gfp_t gfp_mask, u32 rcv_nxt) { ... struct tcp_sock *tp; BUG_ON(!skb || !tcp_skb_pcount(skb)); tp = tcp_sk(sk); prior_wstamp = tp->tcp_wstamp_ns; tp->tcp_wstamp_ns = max(tp->tcp_wstamp_ns, tp->tcp_clock_cache); ... So it basically calls tcp_sk(sk) to get the tcp_sock pointer from sk. But it turned out to be the same value because tcp_sock embeds sock as the first member. The sk is located in reg5 (RDI) and tp is in reg3 (RBX). The offset of tcp_wstamp_ns is 0x748 and tcp_clock_cache is 0x750. So you need to use RBX (reg3) to access the fields in the tcp_sock. But the code used RDI (reg5) as it has the same value. $ pahole --hex -C tcp_sock vmlinux | grep -e 748 -e 750 u64 tcp_wstamp_ns; /* 0x748 0x8 */ u64 tcp_clock_cache; /* 0x750 0x8 */ And this is the disassembly of the part of the function. <__tcp_transmit_skb>: ... 44: mov %rdi, %rbx 47: mov 0x748(%rdi), %rsi 4e: mov 0x750(%rdi), %rax 55: cmp %rax, %rsi Because compiler put the debug info to RBX, it only knows RDI is a pointer to sock and accessing those two fields resulted in error due to offset being beyond the type size. ----------------------------------------------------------- find data type for 0x748(reg5) at __tcp_transmit_skb+0x63 CU for net/ipv4/tcp_output.c (die:0x817f543) frame base: cfa=0 fbreg=6 scope: [1/1] (die:81aac3e) bb: [0 - 30] var [0] -0x98(stack) type='struct tcp_out_options' size=0x28 (die:0x81af3df) var [5] reg8 type='unsigned int' size=0x4 (die:0x8180ed6) var [5] reg2 type='unsigned int' size=0x4 (die:0x8180ed6) var [5] reg1 type='int' size=0x4 (die:0x818059e) var [5] reg4 type='struct sk_buff*' size=0x8 (die:0x8181360) var [5] reg5 type='struct sock*' size=0x8 (die:0x8181a0c) <<<--- the first argument ('sk' at %RDI) mov [19] reg8 -> -0xa8(stack) type='unsigned int' size=0x4 (die:0x8180ed6) mov [20] stack canary -> reg0 mov [29] reg0 -> -0x30(stack) stack canary bb: [36 - 3e] mov [36] reg4 -> reg15 type='struct sk_buff*' size=0x8 (die:0x8181360) bb: [44 - 63] mov [44] reg5 -> reg3 type='struct sock*' size=0x8 (die:0x8181a0c) <<<--- calling tcp_sk() var [47] reg3 type='struct tcp_sock*' size=0x8 (die:0x819eead) <<<--- new variable ('tp' at %RBX) var [4e] reg4 type='unsigned long long' size=0x8 (die:0x8180edd) mov [58] reg4 -> -0xc0(stack) type='unsigned long long' size=0x8 (die:0x8180edd) chk [63] reg5 offset=0x748 ok=1 kind=1 (struct sock*) : offset bigger than size <<<--- access with old variable final result: offset bigger than size While it's a fault in the compiler, we could work around this issue by using the type of new variable when it's copied directly. So I've added copied_from field in the register state to track those direct register to register copies. After that new register gets a new type and the old register still has the same type, it'll update (copy it back) the type of the old register. For example, if we can update type of reg5 at __tcp_transmit_skb+0x47, we can find the target type of the instruction at 0x63 like below: ----------------------------------------------------------- find data type for 0x748(reg5) at __tcp_transmit_skb+0x63 ... bb: [44 - 63] mov [44] reg5 -> reg3 type='struct sock*' size=0x8 (die:0x8181a0c) var [47] reg3 type='struct tcp_sock*' size=0x8 (die:0x819eead) var [47] copyback reg5 type='struct tcp_sock*' size=0x8 (die:0x819eead) <<<--- here mov [47] 0x748(reg5) -> reg4 type='unsigned long long' size=0x8 (die:0x8180edd) mov [4e] 0x750(reg5) -> reg0 type='unsigned long long' size=0x8 (die:0x8180edd) mov [58] reg4 -> -0xc0(stack) type='unsigned long long' size=0x8 (die:0x8180edd) chk [63] reg5 offset=0x748 ok=1 kind=1 (struct tcp_sock*) : Good! <<<--- new type found by insn track: 0x748(reg5) type-offset=0x748 final result: type='struct tcp_sock' size=0xa98 (die:0x819eeb2) Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Athira Rajeev Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240821232628.353177-5-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/x86/annotate/instructions.c | 8 ++++++ tools/perf/util/annotate-data.c | 31 +++++++++++++++++++++ tools/perf/util/annotate-data.h | 1 + 3 files changed, 40 insertions(+) diff --git a/tools/perf/arch/x86/annotate/instructions.c b/tools/perf/arch/x86/annotate/instructions.c index 15dfc2988e24..5caf5a17f03d 100644 --- a/tools/perf/arch/x86/annotate/instructions.c +++ b/tools/perf/arch/x86/annotate/instructions.c @@ -267,6 +267,7 @@ static void update_insn_state_x86(struct type_state *state, return; tsr = &state->regs[dst->reg1]; + tsr->copied_from = -1; if (src->imm) imm_value = src->offset; @@ -326,6 +327,8 @@ static void update_insn_state_x86(struct type_state *state, 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; @@ -386,6 +389,10 @@ static void update_insn_state_x86(struct type_state *state, 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); @@ -398,6 +405,7 @@ static void update_insn_state_x86(struct type_state *state, return; tsr = &state->regs[dst->reg1]; + tsr->copied_from = -1; retry: /* Check stack variables with offset */ diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index 5d23c30b3a7b..a0ea4e07e570 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -774,6 +774,11 @@ ok: return true; } +static bool die_is_same(Dwarf_Die *die_a, Dwarf_Die *die_b) +{ + return (die_a->cu == die_b->cu) && (die_a->addr == die_b->addr); +} + /** * update_var_state - Update type state using given variables * @state: type state table @@ -825,6 +830,7 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo pr_debug_type_name(&mem_die, TSR_KIND_TYPE); } else if (has_reg_type(state, var->reg) && var->offset == 0) { struct type_state_reg *reg; + Dwarf_Die orig_type; reg = &state->regs[var->reg]; @@ -832,6 +838,8 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo !is_better_type(®->type, &mem_die)) continue; + orig_type = reg->type; + reg->type = mem_die; reg->kind = TSR_KIND_TYPE; reg->ok = true; @@ -839,6 +847,29 @@ static void update_var_state(struct type_state *state, struct data_loc_info *dlo pr_debug_dtp("var [%"PRIx64"] reg%d", insn_offset, var->reg); pr_debug_type_name(&mem_die, TSR_KIND_TYPE); + + /* + * If this register is directly copied from another and it gets a + * better type, also update the type of the source register. This + * is usually the case of container_of() macro with offset of 0. + */ + if (has_reg_type(state, reg->copied_from)) { + struct type_state_reg *copy_reg; + + copy_reg = &state->regs[reg->copied_from]; + + /* TODO: check if type is compatible or embedded */ + if (!copy_reg->ok || (copy_reg->kind != TSR_KIND_TYPE) || + !die_is_same(©_reg->type, &orig_type) || + !is_better_type(©_reg->type, &mem_die)) + continue; + + copy_reg->type = mem_die; + + pr_debug_dtp("var [%"PRIx64"] copyback reg%d", + insn_offset, reg->copied_from); + pr_debug_type_name(&mem_die, TSR_KIND_TYPE); + } } } } diff --git a/tools/perf/util/annotate-data.h b/tools/perf/util/annotate-data.h index 37a1a3b68e0b..8ac0fd94a0ba 100644 --- a/tools/perf/util/annotate-data.h +++ b/tools/perf/util/annotate-data.h @@ -176,6 +176,7 @@ struct type_state_reg { bool ok; bool caller_saved; u8 kind; + u8 copied_from; }; /* Type information in a stack location, dynamically allocated */ From b81162302001f41157f6e93654aaccc30e817e2a Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 22 Aug 2024 14:13:49 -0300 Subject: [PATCH 205/347] perf python: Allow checking for the existence of warning options in clang We'll need to check if an warning option introduced in clang 19 is available on the clang version being used, so cover the error message emitted when testing for a -W option. Tested-by: Sedat Dilek Cc: Ian Rogers Cc: Ingo Molnar Cc: Namhyung Kim Cc: Nathan Chancellor Cc: Peter Zijlstra Link: https://lore.kernel.org/lkml/CA+icZUVtHn8X1Tb_Y__c-WswsO0K8U9uy3r2MzKXwTA5THtL7w@mail.gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 142e9d447ce7..26c0f2614fe9 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -17,7 +17,7 @@ src_feature_tests = getenv('srctree') + '/tools/build/feature' def clang_has_option(option): cc_output = Popen([cc, cc_options + option, path.join(src_feature_tests, "test-hello.c") ], stderr=PIPE).stderr.readlines() - return [o for o in cc_output if ((b"unknown argument" in o) or (b"is not supported" in o))] == [ ] + return [o for o in cc_output if ((b"unknown argument" in o) or (b"is not supported" in o) or (b"unknown warning option" in o))] == [ ] if cc_is_clang: from sysconfig import get_config_vars From 00dc514612fe98cfa117193b9df28f15e7c9db9c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 22 Aug 2024 14:13:49 -0300 Subject: [PATCH 206/347] perf python: Disable -Wno-cast-function-type-mismatch if present on clang The -Wcast-function-type-mismatch option was introduced in clang 19 and its enabled by default, since we use -Werror, and python bindings do casts that are valid but trips this warning, disable it if present. Closes: https://lore.kernel.org/all/CA+icZUXoJ6BS3GMhJHV3aZWyb5Cz2haFneX0C5pUMUUhG-UVKQ@mail.gmail.com Reported-by: Sedat Dilek Tested-by: Sedat Dilek Cc: Ian Rogers Cc: Ingo Molnar Cc: Namhyung Kim Cc: Nathan Chancellor Cc: Peter Zijlstra Cc: stable@vger.kernel.org # To allow building with the upcoming clang 19 Link: https://lore.kernel.org/lkml/CA+icZUVtHn8X1Tb_Y__c-WswsO0K8U9uy3r2MzKXwTA5THtL7w@mail.gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/setup.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 26c0f2614fe9..649550e9b7aa 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -63,6 +63,8 @@ cflags = getenv('CFLAGS', '').split() cflags += ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter', '-Wno-redundant-decls' ] if cc_is_clang: cflags += ["-Wno-unused-command-line-argument" ] + if clang_has_option("-Wno-cast-function-type-mismatch"): + cflags += ["-Wno-cast-function-type-mismatch" ] else: cflags += ['-Wno-cast-function-type' ] From f133c76409c81653cb580903da49aecefca67013 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Tue, 13 Aug 2024 14:36:49 -0700 Subject: [PATCH 207/347] perf test: Support external tests for separate objdir Extend the searching for the test files so that it works when running perf from a separate objdir, and also when the perf executable is symlinked. Signed-off-by: Andi Kleen Acked-by: Namhyung Kim Cc: Andi Kleen Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240813213651.1057362-2-ak@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/tests-scripts.c | 35 +++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c index e2042b368269..8dc1e398288c 100644 --- a/tools/perf/tests/tests-scripts.c +++ b/tools/perf/tests/tests-scripts.c @@ -29,16 +29,45 @@ static int shell_tests__dir_fd(void) { - char path[PATH_MAX], *exec_path; - static const char * const devel_dirs[] = { "./tools/perf/tests/shell", "./tests/shell", }; + struct stat st; + char path[PATH_MAX], path2[PATH_MAX], *exec_path; + static const char * const devel_dirs[] = { + "./tools/perf/tests/shell", + "./tests/shell", + "./source/tests/shell" + }; + int fd; + char *p; for (size_t i = 0; i < ARRAY_SIZE(devel_dirs); ++i) { - int fd = open(devel_dirs[i], O_PATH); + fd = open(devel_dirs[i], O_PATH); if (fd >= 0) return fd; } + /* Use directory of executable */ + if (readlink("/proc/self/exe", path2, sizeof path2) < 0) + return -1; + /* Follow another level of symlink if there */ + if (lstat(path2, &st) == 0 && (st.st_mode & S_IFMT) == S_IFLNK) { + scnprintf(path, sizeof(path), path2); + if (readlink(path, path2, sizeof path2) < 0) + return -1; + } + /* Get directory */ + p = strrchr(path2, '/'); + if (p) + *p = 0; + scnprintf(path, sizeof(path), "%s/tests/shell", path2); + fd = open(path, O_PATH); + if (fd >= 0) + return fd; + scnprintf(path, sizeof(path), "%s/source/tests/shell", path2); + fd = open(path, O_PATH); + if (fd >= 0) + return fd; + /* Then installed path. */ exec_path = get_argv_exec_path(); scnprintf(path, sizeof(path), "%s/tests/shell", exec_path); From bca704f62db2e2bb644fa845e98b08cf1e22f814 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 15 Aug 2024 11:36:29 -0400 Subject: [PATCH 208/347] ring-buffer: Don't reset persistent ring-buffer meta saved addresses The text and data address is saved in the meta data so that it can be used to know the delta of the text and data addresses of the last boot compared to the text and data addresses of the current boot. The delta is used to convert function pointer entries in the ring buffer to something that can be used by kallsyms (note this only works for built-in functions). But the saved addresses get reset on boot up. If the buffer is not used and there's another reboot, then the saved text and data addresses will be of the last boot and not that of the boot that created the content in the ring buffer. To get an idea of the issue: # trace-cmd start -B boot_mapped -p function # reboot # trace-cmd show -B boot_mapped | tail <...>-1 [000] d..1. 461.983243: native_apic_msr_write <-native_kick_ap <...>-1 [000] d..1. 461.983244: __pfx_native_apic_msr_eoi <-native_kick_ap <...>-1 [000] d..1. 461.983244: reserve_irq_vector_locked <-native_kick_ap <...>-1 [000] d..1. 461.983262: branch_emulate_op <-native_kick_ap <...>-1 [000] d..1. 461.983262: __ia32_sys_ia32_pread64 <-native_kick_ap <...>-1 [000] d..1. 461.983263: native_kick_ap <-__smpboot_create_thread <...>-1 [000] d..1. 461.983263: store_cache_disable <-native_kick_ap <...>-1 [000] d..1. 461.983279: acpi_power_off_prepare <-native_kick_ap <...>-1 [000] d..1. 461.983280: __pfx_acpi_ns_delete_node <-acpi_suspend_enter <...>-1 [000] d..1. 461.983280: __pfx_acpi_os_release_lock <-acpi_suspend_enter # reboot # trace-cmd show -B boot_mapped |tail <...>-1 [000] d..1. 461.983243: 0xffffffffa9669220 <-0xffffffffa965f3db <...>-1 [000] d..1. 461.983244: 0xffffffffa96690f0 <-0xffffffffa965f3db <...>-1 [000] d..1. 461.983244: 0xffffffffa9663fa0 <-0xffffffffa965f3db <...>-1 [000] d..1. 461.983262: 0xffffffffa9672e80 <-0xffffffffa965f3e0 <...>-1 [000] d..1. 461.983262: 0xffffffffa962b940 <-0xffffffffa965f3ec <...>-1 [000] d..1. 461.983263: 0xffffffffa965f540 <-0xffffffffa96e1362 <...>-1 [000] d..1. 461.983263: 0xffffffffa963c940 <-0xffffffffa965f55b <...>-1 [000] d..1. 461.983279: 0xffffffffa9ee30c0 <-0xffffffffa965f59b <...>-1 [000] d..1. 461.983280: 0xffffffffa9f16c10 <-0xffffffffa9ee3157 <...>-1 [000] d..1. 461.983280: 0xffffffffa9ee02e0 <-0xffffffffa9ee3157 By not updating the saved text and data addresses in the meta data at every boot up and only updating them when the buffer is reset, it allows multiple boots to see the same data. Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Cc: Vincent Donnefort Link: https://lore.kernel.org/20240815113629.0dc90af8@rorschach.local.home Signed-off-by: Steven Rostedt (Google) --- kernel/trace/ring_buffer.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 8e3a7123937a..b16f301b8a93 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -1817,12 +1817,19 @@ static void rb_meta_validate_events(struct ring_buffer_per_cpu *cpu_buffer) /* Used to calculate data delta */ static char rb_data_ptr[] = ""; +#define THIS_TEXT_PTR ((unsigned long)rb_meta_init_text_addr) +#define THIS_DATA_PTR ((unsigned long)rb_data_ptr) + +static void rb_meta_init_text_addr(struct ring_buffer_meta *meta) +{ + meta->text_addr = THIS_TEXT_PTR; + meta->data_addr = THIS_DATA_PTR; +} + static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages) { struct ring_buffer_meta *meta; unsigned long delta; - unsigned long this_text = (unsigned long)rb_range_meta_init; - unsigned long this_data = (unsigned long)rb_data_ptr; void *subbuf; int cpu; int i; @@ -1839,10 +1846,8 @@ static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages) meta->first_buffer += delta; meta->head_buffer += delta; meta->commit_buffer += delta; - buffer->last_text_delta = this_text - meta->text_addr; - buffer->last_data_delta = this_data - meta->data_addr; - meta->text_addr = this_text; - meta->data_addr = this_data; + buffer->last_text_delta = THIS_TEXT_PTR - meta->text_addr; + buffer->last_data_delta = THIS_DATA_PTR - meta->data_addr; continue; } @@ -1859,8 +1864,7 @@ static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages) subbuf = rb_subbufs_from_meta(meta); meta->first_buffer = (unsigned long)subbuf; - meta->text_addr = this_text; - meta->data_addr = this_data; + rb_meta_init_text_addr(meta); /* * The buffers[] array holds the order of the sub-buffers @@ -5990,6 +5994,7 @@ static void reset_disabled_cpu_buffer(struct ring_buffer_per_cpu *cpu_buffer) void ring_buffer_reset_cpu(struct trace_buffer *buffer, int cpu) { struct ring_buffer_per_cpu *cpu_buffer = buffer->buffers[cpu]; + struct ring_buffer_meta *meta; if (!cpumask_test_cpu(cpu, buffer->cpumask)) return; @@ -6008,6 +6013,11 @@ void ring_buffer_reset_cpu(struct trace_buffer *buffer, int cpu) atomic_dec(&cpu_buffer->record_disabled); atomic_dec(&cpu_buffer->resize_disabled); + /* Make sure persistent meta now uses this buffer's addresses */ + meta = rb_range_meta(buffer, 0, cpu_buffer->cpu); + if (meta) + rb_meta_init_text_addr(meta); + mutex_unlock(&buffer->mutex); } EXPORT_SYMBOL_GPL(ring_buffer_reset_cpu); @@ -6022,6 +6032,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_reset_cpu); void ring_buffer_reset_online_cpus(struct trace_buffer *buffer) { struct ring_buffer_per_cpu *cpu_buffer; + struct ring_buffer_meta *meta; int cpu; /* prevent another thread from changing buffer sizes */ @@ -6049,6 +6060,11 @@ void ring_buffer_reset_online_cpus(struct trace_buffer *buffer) reset_disabled_cpu_buffer(cpu_buffer); + /* Make sure persistent meta now uses this buffer's addresses */ + meta = rb_range_meta(buffer, 0, cpu_buffer->cpu); + if (meta) + rb_meta_init_text_addr(meta); + atomic_dec(&cpu_buffer->record_disabled); atomic_sub(RESET_BIT, &cpu_buffer->resize_disabled); } From d0f2d6e9512ecf4306c4432761f04bd35cf9e3a6 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 15 Aug 2024 11:50:32 -0400 Subject: [PATCH 209/347] ring-buffer: Add magic and struct size to boot up meta data Add a magic number as well as save the struct size of the ring_buffer_meta structure in the meta data to also use as validation. Updating the magic number could be used to force a invalidation between kernel versions, and saving the structure size is also a good method to make sure the content is what is expected. Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Cc: Vincent Donnefort Link: https://lore.kernel.org/20240815115032.0c197b32@rorschach.local.home Signed-off-by: Steven Rostedt (Google) --- kernel/trace/ring_buffer.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index b16f301b8a93..c3a5e6cbb940 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -44,7 +44,11 @@ static void update_pages_handler(struct work_struct *work); +#define RING_BUFFER_META_MAGIC 0xBADFEED + struct ring_buffer_meta { + int magic; + int struct_size; unsigned long text_addr; unsigned long data_addr; unsigned long first_buffer; @@ -1627,6 +1631,13 @@ static bool rb_meta_valid(struct ring_buffer_meta *meta, int cpu, unsigned long buffers_end; int i; + /* Check the meta magic and meta struct size */ + if (meta->magic != RING_BUFFER_META_MAGIC || + meta->struct_size != sizeof(*meta)) { + pr_info("Ring buffer boot meta[%d] mismatch of magic or struct size\n", cpu); + return false; + } + /* The subbuffer's size and number of subbuffers must match */ if (meta->subbuf_size != subbuf_size || meta->nr_subbufs != nr_pages + 1) { @@ -1858,6 +1869,9 @@ static void rb_range_meta_init(struct trace_buffer *buffer, int nr_pages) memset(meta, 0, next_meta - (void *)meta); + meta->magic = RING_BUFFER_META_MAGIC; + meta->struct_size = sizeof(*meta); + meta->nr_subbufs = nr_pages + 1; meta->subbuf_size = PAGE_SIZE; From eb2dcde9f970ed8d3669444d47c8524b4bdf7d32 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Fri, 28 Jun 2024 11:46:11 +0100 Subject: [PATCH 210/347] ring-buffer: Align meta-page to sub-buffers for improved TLB usage Previously, the mapped ring-buffer layout caused misalignment between the meta-page and sub-buffers when the sub-buffer size was not a multiple of PAGE_SIZE. This prevented hardware with larger TLB entries from utilizing them effectively. Add a padding with the zero-page between the meta-page and sub-buffers. Also update the ring-buffer map_test to verify that padding. Link: https://lore.kernel.org/20240628104611.1443542-1-vdonnefort@google.com Signed-off-by: Vincent Donnefort Signed-off-by: Steven Rostedt (Google) --- kernel/trace/ring_buffer.c | 33 +++++++++++-------- .../testing/selftests/ring-buffer/map_test.c | 14 ++++++++ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index c3a5e6cbb940..77dc0b25140e 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -6852,10 +6852,10 @@ static void rb_setup_ids_meta_page(struct ring_buffer_per_cpu *cpu_buffer, /* install subbuf ID to kern VA translation */ cpu_buffer->subbuf_ids = subbuf_ids; - meta->meta_page_size = PAGE_SIZE; meta->meta_struct_len = sizeof(*meta); meta->nr_subbufs = nr_subbufs; meta->subbuf_size = cpu_buffer->buffer->subbuf_size + BUF_PAGE_HDR_SIZE; + meta->meta_page_size = meta->subbuf_size; rb_update_meta_page(cpu_buffer); } @@ -6949,6 +6949,12 @@ static int __rb_map_vma(struct ring_buffer_per_cpu *cpu_buffer, !(vma->vm_flags & VM_MAYSHARE)) return -EPERM; + subbuf_order = cpu_buffer->buffer->subbuf_order; + subbuf_pages = 1 << subbuf_order; + + if (subbuf_order && pgoff % subbuf_pages) + return -EINVAL; + /* * Make sure the mapping cannot become writable later. Also tell the VM * to not touch these pages (VM_DONTCOPY | VM_DONTEXPAND). @@ -6958,11 +6964,8 @@ static int __rb_map_vma(struct ring_buffer_per_cpu *cpu_buffer, lockdep_assert_held(&cpu_buffer->mapping_lock); - subbuf_order = cpu_buffer->buffer->subbuf_order; - subbuf_pages = 1 << subbuf_order; - nr_subbufs = cpu_buffer->nr_pages + 1; /* + reader-subbuf */ - nr_pages = ((nr_subbufs) << subbuf_order) - pgoff + 1; /* + meta-page */ + nr_pages = ((nr_subbufs + 1) << subbuf_order) - pgoff; /* + meta-page */ nr_vma_pages = vma_pages(vma); if (!nr_vma_pages || nr_vma_pages > nr_pages) @@ -6975,20 +6978,24 @@ static int __rb_map_vma(struct ring_buffer_per_cpu *cpu_buffer, return -ENOMEM; if (!pgoff) { + unsigned long meta_page_padding; + pages[p++] = virt_to_page(cpu_buffer->meta_page); /* - * TODO: Align sub-buffers on their size, once - * vm_insert_pages() supports the zero-page. + * Pad with the zero-page to align the meta-page with the + * sub-buffers. */ + meta_page_padding = subbuf_pages - 1; + while (meta_page_padding-- && p < nr_pages) { + unsigned long __maybe_unused zero_addr = + vma->vm_start + (PAGE_SIZE * p); + + pages[p++] = ZERO_PAGE(zero_addr); + } } else { /* Skip the meta-page */ - pgoff--; - - if (pgoff % subbuf_pages) { - err = -EINVAL; - goto out; - } + pgoff -= subbuf_pages; s += pgoff / subbuf_pages; } diff --git a/tools/testing/selftests/ring-buffer/map_test.c b/tools/testing/selftests/ring-buffer/map_test.c index a9006fa7097e..4bb0192e43f3 100644 --- a/tools/testing/selftests/ring-buffer/map_test.c +++ b/tools/testing/selftests/ring-buffer/map_test.c @@ -228,6 +228,20 @@ TEST_F(map, data_mmap) data = mmap(NULL, data_len, PROT_READ, MAP_SHARED, desc->cpu_fd, meta_len); ASSERT_EQ(data, MAP_FAILED); + + /* Verify meta-page padding */ + if (desc->meta->meta_page_size > getpagesize()) { + void *addr; + + data_len = desc->meta->meta_page_size; + data = mmap(NULL, data_len, + PROT_READ, MAP_SHARED, desc->cpu_fd, 0); + ASSERT_NE(data, MAP_FAILED); + + addr = (void *)((unsigned long)data + getpagesize()); + ASSERT_EQ(*((int *)addr), 0); + munmap(data, data_len); + } } FIXTURE(snapshot) { From b6fc31b68731af71e408b4762ac0fbce4e40223e Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 22 Aug 2024 21:39:03 -0400 Subject: [PATCH 211/347] tracing: Add "traceoff" flag to boot time tracing instances Add a "flags" delimiter (^) to the "trace_instance" kernel command line parameter, and add the "traceoff" flag. The format is: trace_instance=[^[^]][@][,] The code allows for more than one flag to be added, but currently only "traceoff" is done so. The motivation for this change came from debugging with the persistent ring buffer and having trace_printk() writing to it. The trace_printk calls are always enabled, and the boot after the crash was having the unwanted trace_printks from the current boot inject into the ring buffer with the trace_printks of the crash kernel, making the output very confusing. Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Cc: Alexander Aring Cc: "Luis Claudio R. Goncalves" Cc: Tomas Glozar Cc: John Kacur Cc: Clark Williams Cc: Linus Torvalds Cc: "Jonathan Corbet" Link: https://lore.kernel.org/20240823014019.053229958@goodmis.org Signed-off-by: Steven Rostedt (Google) --- .../admin-guide/kernel-parameters.txt | 17 ++++++++++ kernel/trace/trace.c | 31 ++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 388653448e72..3803f2b7f065 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -6743,6 +6743,15 @@ 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 '^'. Currently there's only one flag + defined, and that's "traceoff", to have the tracing instance tracing + disabled after it is created. + + trace_instance=foo^traceoff,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: @@ -6765,6 +6774,14 @@ 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@trace,sched,irq + + trace_options=[option-list] [FTRACE] Enable or disable tracer options at boot. The option-list is a comma delimited list of options diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 9bcef199ae90..a79eefe84d6b 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -10468,10 +10468,36 @@ __init static void enable_instances(void) phys_addr_t start = 0; phys_addr_t size = 0; unsigned long addr = 0; + bool traceoff = false; + char *flag_delim; + char *addr_delim; tok = strsep(&curr_str, ","); - name = strsep(&tok, "@"); + flag_delim = strchr(tok, '^'); + addr_delim = strchr(tok, '@'); + + 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 + 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) { @@ -10519,6 +10545,9 @@ __init static void enable_instances(void) continue; } + if (traceoff) + tracer_tracing_off(tr); + /* Only allow non mapped buffers to be deleted */ if (!start) trace_array_put(tr); From ddb8ea9e5ae482c469bcfd61cc83399bef67beb8 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 22 Aug 2024 21:39:04 -0400 Subject: [PATCH 212/347] tracing: Allow trace_printk() to go to other instance buffers Currently, trace_printk() just goes to the top level ring buffer. But there may be times that it should go to one of the instances created by the kernel command line. Add a new trace_instance flag: traceprintk (also can use "printk" or "trace_printk" as people tend to forget the actual flag name). trace_instance=foo^traceprintk Will assign the trace_printk to this buffer at boot up. Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Cc: Alexander Aring Cc: "Luis Claudio R. Goncalves" Cc: Tomas Glozar Cc: John Kacur Cc: Clark Williams Cc: Linus Torvalds Cc: "Jonathan Corbet" Link: https://lore.kernel.org/20240823014019.226694946@goodmis.org Signed-off-by: Steven Rostedt (Google) --- .../admin-guide/kernel-parameters.txt | 14 ++++-- kernel/trace/trace.c | 46 ++++++++++++++----- 2 files changed, 45 insertions(+), 15 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 3803f2b7f065..a8803c0c0a89 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -6744,11 +6744,17 @@ 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 '^'. Currently there's only one flag - defined, and that's "traceoff", to have the tracing instance tracing - disabled after it is created. + created. The flags are separated by '^'. - trace_instance=foo^traceoff,sched,irq + 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) + Currently, traceprintk flag cannot be used for memory + mapped ring buffers as described below. + + trace_instance=foo^traceoff^traceprintk,sched,irq The flags must come before the defined events. diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index a79eefe84d6b..8e28f19f5316 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -500,6 +500,8 @@ static struct trace_array global_trace = { .trace_flags = TRACE_DEFAULT_FLAGS, }; +static struct trace_array *printk_trace = &global_trace; + void trace_set_ring_buffer_expanded(struct trace_array *tr) { if (!tr) @@ -1117,7 +1119,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); @@ -1128,6 +1130,7 @@ EXPORT_SYMBOL_GPL(__trace_puts); */ int __trace_bputs(unsigned long ip, const char *str) { + struct trace_array *tr = printk_trace; struct ring_buffer_event *event; struct trace_buffer *buffer; struct bputs_entry *entry; @@ -1135,14 +1138,14 @@ 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 (!(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, @@ -1155,7 +1158,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: @@ -3025,7 +3028,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); @@ -3244,7 +3247,7 @@ 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 = printk_trace; struct bprint_entry *entry; unsigned int trace_ctx; char *tbuffer; @@ -3342,7 +3345,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: @@ -3438,7 +3441,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); @@ -3450,7 +3453,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); @@ -9666,6 +9669,9 @@ static int __remove_instance(struct trace_array *tr) set_tracer_flag(tr, 1 << i, 0); } + if (printk_trace == tr) + printk_trace = &global_trace; + tracing_set_nop(tr); clear_ftrace_function_probes(tr); event_trace_del_tracer(tr); @@ -10468,6 +10474,7 @@ __init static void enable_instances(void) 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; @@ -10489,11 +10496,16 @@ __init static void enable_instances(void) char *flag; while ((flag = strsep(&flag_delim, "^"))) { - if (strcmp(flag, "traceoff") == 0) + if (strcmp(flag, "traceoff") == 0) { traceoff = true; - else + } 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); + } } } @@ -10548,6 +10560,18 @@ __init static void enable_instances(void) if (traceoff) tracer_tracing_off(tr); + if (traceprintk) { + /* + * The binary format of traceprintk can cause a crash if used + * by a buffer from another boot. Do not allow it for the + * memory mapped ring buffers. + */ + if (start) + pr_warn("Tracing: WARNING: memory mapped ring buffers cannot be used for trace_printk\n"); + else + printk_trace = tr; + } + /* Only allow non mapped buffers to be deleted */ if (!start) trace_array_put(tr); From 9b7bdf6f6ece6ea888cc7d2f02c00b403b66a119 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 22 Aug 2024 21:39:05 -0400 Subject: [PATCH 213/347] tracing: Have trace_printk not use binary prints if boot buffer If the persistent boot mapped ring buffer is used for trace_printk(), force it to not use the binary versions. trace_printk() by default uses bin_printf() that only saves the pointer to the format and not the format itself inside the ring buffer. But for a persistent buffer that is read after reboot, the pointers to the format strings may not be the same, or worse, not even exist! Instead, just force the more robust, but slower, version that does the formatting before saving into the ring buffer. The boot mapped buffer can now be used for trace_printk and friends! Using the trace_printk() and the persistent buffer was used to debug the issue with the osnoise tracer: Link: https://lore.kernel.org/all/20240822103443.6a6ae051@gandalf.local.home/ Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Cc: Alexander Aring Cc: "Luis Claudio R. Goncalves" Cc: Tomas Glozar Cc: John Kacur Cc: Clark Williams Cc: Linus Torvalds Cc: "Jonathan Corbet" Link: https://lore.kernel.org/20240823014019.386925800@goodmis.org Signed-off-by: Steven Rostedt (Google) --- .../admin-guide/kernel-parameters.txt | 4 +- kernel/trace/trace.c | 44 ++++++++++++------- kernel/trace/trace.h | 3 +- kernel/trace/trace_output.c | 5 ++- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index a8803c0c0a89..9e507e6cb4c8 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -6751,8 +6751,6 @@ 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) - Currently, traceprintk flag cannot be used for memory - mapped ring buffers as described below. trace_instance=foo^traceoff^traceprintk,sched,irq @@ -6785,7 +6783,7 @@ 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@trace,sched,irq + reserve_mem=12M:4096:trace trace_instance=boot_map^traceoff^traceprintk@trace,sched,irq trace_options=[option-list] diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 8e28f19f5316..35b37c9aa26c 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -502,6 +502,17 @@ static struct trace_array global_trace = { 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); +} + void trace_set_ring_buffer_expanded(struct trace_array *tr) { if (!tr) @@ -1130,7 +1141,7 @@ EXPORT_SYMBOL_GPL(__trace_puts); */ int __trace_bputs(unsigned long ip, const char *str) { - struct trace_array *tr = printk_trace; + struct trace_array *tr = READ_ONCE(printk_trace); struct ring_buffer_event *event; struct trace_buffer *buffer; struct bputs_entry *entry; @@ -1138,6 +1149,9 @@ int __trace_bputs(unsigned long ip, const char *str) int size = sizeof(struct bputs_entry); int ret = 0; + if (!printk_binsafe(tr)) + return __trace_puts(ip, str, strlen(str)); + if (!(tr->trace_flags & TRACE_ITER_PRINTK)) return 0; @@ -3247,12 +3261,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 = printk_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; @@ -10560,20 +10577,17 @@ __init static void enable_instances(void) if (traceoff) tracer_tracing_off(tr); - if (traceprintk) { - /* - * The binary format of traceprintk can cause a crash if used - * by a buffer from another boot. Do not allow it for the - * memory mapped ring buffers. - */ - if (start) - pr_warn("Tracing: WARNING: memory mapped ring buffers cannot be used for trace_printk\n"); - else - printk_trace = tr; - } + if (traceprintk) + printk_trace = tr; - /* Only allow non mapped buffers to be deleted */ - if (!start) + /* + * 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, ","))) { diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 4f448ab2d1e7..07b2d2af9b33 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -429,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; diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 48de93598897..868f2f912f28 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -1591,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); From ef2bd81d0c95616fab718738be48d7cc9b23e33d Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 22 Aug 2024 21:39:06 -0400 Subject: [PATCH 214/347] tracing: Add option to set an instance to be the trace_printk destination Add a option "trace_printk_dest" that will make the tracing instance the location that trace_printk() will go to. This is useful if the trace_printk or one of the top level tracers is too noisy and there's a need to separate the two. Then an instance can be created, the trace_printk can be set to go there instead, where it will not be lost in the noise of the top level tracer. Note, only one instance can be the destination of trace_printk at a time. If an instance sets this flag, the instance that had it set will have it cleared. There is always one instance that has this set. By default, that is the top instance. This flag cannot be cleared from the top instance. Doing so will result in an -EINVAL. The only way this flag can be cleared from the top instance is by another instance setting it. Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Cc: Alexander Aring Cc: "Luis Claudio R. Goncalves" Cc: Tomas Glozar Cc: John Kacur Cc: Clark Williams Cc: Linus Torvalds Cc: "Jonathan Corbet" Link: https://lore.kernel.org/20240823014019.545459018@goodmis.org Signed-off-by: Steven Rostedt (Google) --- Documentation/trace/ftrace.rst | 12 ++++++++++ kernel/trace/trace.c | 40 +++++++++++++++++++++++++++++----- kernel/trace/trace.h | 1 + 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/Documentation/trace/ftrace.rst b/Documentation/trace/ftrace.rst index 5aba74872ba7..4073ca48af4a 100644 --- a/Documentation/trace/ftrace.rst +++ b/Documentation/trace/ftrace.rst @@ -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 diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 35b37c9aa26c..658b40b483a3 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -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 @@ -513,6 +513,16 @@ static __always_inline bool printk_binsafe(struct trace_array *tr) 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) @@ -5300,7 +5310,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 */ @@ -5312,6 +5323,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 @@ -9687,7 +9717,7 @@ static int __remove_instance(struct trace_array *tr) } if (printk_trace == tr) - printk_trace = &global_trace; + update_printk_trace(&global_trace); tracing_set_nop(tr); clear_ftrace_function_probes(tr); @@ -10578,7 +10608,7 @@ __init static void enable_instances(void) tracer_tracing_off(tr); if (traceprintk) - printk_trace = tr; + update_printk_trace(tr); /* * If start is set, then this is a mapped buffer, and diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 07b2d2af9b33..c866991b9c78 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -1321,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 \ From 2fcd5aff92aab479a9a89cfce2dbc9c6a9455b4f Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Thu, 22 Aug 2024 21:39:07 -0400 Subject: [PATCH 215/347] tracing/Documentation: Start a document on how to debug with tracing Add a new document Documentation/trace/debugging.rst that will hold various ways to debug tracing. This initial version mentions trace_printk and how to create persistent buffers that can last across bootups. Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Vincent Donnefort Cc: Joel Fernandes Cc: Ingo Molnar Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: Vineeth Pillai Cc: Beau Belgrave Cc: Alexander Graf Cc: Baoquan He Cc: Borislav Petkov Cc: "Paul E. McKenney" Cc: David Howells Cc: Mike Rapoport Cc: Dave Hansen Cc: Tony Luck Cc: Guenter Roeck Cc: Ross Zwisler Cc: Kees Cook Cc: Alexander Aring Cc: "Luis Claudio R. Goncalves" Cc: Tomas Glozar Cc: John Kacur Cc: Clark Williams Cc: Linus Torvalds Cc: "Jonathan Corbet" Link: https://lore.kernel.org/20240823014019.702433486@goodmis.org Signed-off-by: Steven Rostedt (Google) --- .../admin-guide/kernel-parameters.txt | 2 + Documentation/trace/debugging.rst | 159 ++++++++++++++++++ 2 files changed, 161 insertions(+) create mode 100644 Documentation/trace/debugging.rst diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 9e507e6cb4c8..9bb50dc78338 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -6785,6 +6785,8 @@ 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. diff --git a/Documentation/trace/debugging.rst b/Documentation/trace/debugging.rst new file mode 100644 index 000000000000..54fb16239d70 --- /dev/null +++ b/Documentation/trace/debugging.rst @@ -0,0 +1,159 @@ +============================== +Using the tracer for debugging +============================== + +Copyright 2024 Google LLC. + +:Author: Steven Rostedt +: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. From 4f3affe0abf5d5910dc469a1f63257629605d3c3 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Tue, 20 Aug 2024 11:32:02 -0700 Subject: [PATCH 216/347] perf hist: Don't set hpp_fmt_value for members in --no-group Perf crashes as below when applying --no-group # perf record -e "{cache-misses,branches"} -b sleep 1 # perf report --stdio --no-group free(): invalid next size (fast) Aborted (core dumped) # In the __hpp__fmt(), only 1 hpp_fmt_value is allocated for the current event when --no-group is applied. However, the current implementation tries to assign the hists from all members to the hpp_fmt_value, which exceeds the allocated memory. Fixes: 8f6071a3dce40e69 ("perf hist: Simplify __hpp_fmt() using hpp_fmt_data") Signed-off-by: Kan Liang Acked-by: Namhyung Kim Cc: Ian Rogers Cc: Ingo Molnar Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240820183202.3174323-1-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/ui/hist.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c index 5d1f04f66a5a..e5491995adf0 100644 --- a/tools/perf/ui/hist.c +++ b/tools/perf/ui/hist.c @@ -62,7 +62,7 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, struct evsel *pos; char *buf = hpp->buf; size_t size = hpp->size; - int i, nr_members = 1; + int i = 0, nr_members = 1; struct hpp_fmt_value *values; if (evsel__is_group_event(evsel)) @@ -72,16 +72,16 @@ static int __hpp__fmt(struct perf_hpp *hpp, struct hist_entry *he, if (values == NULL) return 0; - i = 0; - for_each_group_evsel(pos, evsel) - values[i++].hists = evsel__hists(pos); - + values[0].hists = evsel__hists(evsel); values[0].val = get_field(he); values[0].samples = he->stat.nr_events; if (evsel__is_group_event(evsel)) { struct hist_entry *pair; + for_each_group_member(pos, evsel) + values[++i].hists = evsel__hists(pos); + list_for_each_entry(pair, &he->pairs.head, pairs.node) { for (i = 0; i < nr_members; i++) { if (values[i].hists != pair->hists) From 0fe2b18ddc40bf8fbcd96dab651822dbadc1cd85 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 20 Aug 2024 08:45:02 -0700 Subject: [PATCH 217/347] perf bpf-filter: Support multiple events properly So far it used tgid as a key to get the filter expressions in the pinned filters map for regular users but it won't work well if the has more than one filters at the same time. Let's add the event id to the key of the filter hash map so that it can identify the right filter expression in the BPF program. As the event can be inherited to child tasks, it should use the primary id which belongs to the parent (original) event. Since evsel opens the event for multiple CPUs and tasks, it needs to maintain a separate hash map for the event id. In the user space, it keeps a list for the multiple evsel and release the entries in the both hash map when it closes the event. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: KP Singh Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Link: https://lore.kernel.org/r/20240820154504.128923-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-filter.c | 288 ++++++++++++++++--- tools/perf/util/bpf_skel/sample-filter.h | 11 +- tools/perf/util/bpf_skel/sample_filter.bpf.c | 42 ++- tools/perf/util/bpf_skel/vmlinux/vmlinux.h | 5 + 4 files changed, 304 insertions(+), 42 deletions(-) diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c index c5eb0b7eec19..0a1832564dd2 100644 --- a/tools/perf/util/bpf-filter.c +++ b/tools/perf/util/bpf-filter.c @@ -1,4 +1,45 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/** + * Generic event filter for sampling events in BPF. + * + * The BPF program is fixed and just to read filter expressions in the 'filters' + * map and compare the sample data in order to reject samples that don't match. + * Each filter expression contains a sample flag (term) to compare, an operation + * (==, >=, and so on) and a value. + * + * Note that each entry has an array of filter expressions and it only succeeds + * when all of the expressions are satisfied. But it supports the logical OR + * using a GROUP operation which is satisfied when any of its member expression + * is evaluated to true. But it doesn't allow nested GROUP operations for now. + * + * To support non-root users, the filters map can be loaded and pinned in the BPF + * filesystem by root (perf record --setup-filter pin). Then each user will get + * a new entry in the shared filters map to fill the filter expressions. And the + * BPF program will find the filter using (task-id, event-id) as a key. + * + * The pinned BPF object (shared for regular users) has: + * + * event_hash | + * | | | + * event->id ---> | id | ---+ idx_hash | filters + * | | | | | | | | + * | .... | +-> | idx | --+--> | exprs | ---> perf_bpf_filter_entry[] + * | | | | | | .op + * task id (tgid) --------------+ | .... | | | ... | .term (+ part) + * | .value + * | + * ======= (root would skip this part) ======== (compares it in a loop) + * + * This is used for per-task use cases while system-wide profiling (normally from + * root user) uses a separate copy of the program and the maps for its own so that + * it can proceed even if a lot of non-root users are using the filters at the + * same time. In this case the filters map has a single entry and no need to use + * the hash maps to get the index (key) of the filters map (IOW it's always 0). + * + * The BPF program returns 1 to accept the sample or 0 to drop it. + * The 'dropped' map is to keep how many samples it dropped by the filter and + * it will be reported as lost samples. + */ #include #include #include @@ -6,6 +47,7 @@ #include #include +#include #include #include #include @@ -27,7 +69,14 @@ #define PERF_SAMPLE_TYPE(_st, opt) __PERF_SAMPLE_TYPE(PBF_TERM_##_st, PERF_SAMPLE_##_st, opt) /* Index in the pinned 'filters' map. Should be released after use. */ -static int pinned_filter_idx = -1; +struct pinned_filter_idx { + struct list_head list; + struct evsel *evsel; + u64 event_id; + int hash_idx; +}; + +static LIST_HEAD(pinned_filters); static const struct perf_sample_info { enum perf_bpf_filter_term type; @@ -175,24 +224,145 @@ static int convert_to_tgid(int tid) return tgid; } -static int update_pid_hash(struct evsel *evsel, struct perf_bpf_filter_entry *entry) +/* + * The event might be closed already so we cannot get the list of ids using FD + * like in create_event_hash() below, let's iterate the event_hash map and + * delete all entries that have the event id as a key. + */ +static void destroy_event_hash(u64 event_id) +{ + int fd; + u64 key, *prev_key = NULL; + int num = 0, alloced = 32; + u64 *ids = calloc(alloced, sizeof(*ids)); + + if (ids == NULL) + return; + + fd = get_pinned_fd("event_hash"); + if (fd < 0) { + pr_debug("cannot get fd for 'event_hash' map\n"); + free(ids); + return; + } + + /* Iterate the whole map to collect keys for the event id. */ + while (!bpf_map_get_next_key(fd, prev_key, &key)) { + u64 id; + + if (bpf_map_lookup_elem(fd, &key, &id) == 0 && id == event_id) { + if (num == alloced) { + void *tmp; + + alloced *= 2; + tmp = realloc(ids, alloced * sizeof(*ids)); + if (tmp == NULL) + break; + + ids = tmp; + } + ids[num++] = key; + } + + prev_key = &key; + } + + for (int i = 0; i < num; i++) + bpf_map_delete_elem(fd, &ids[i]); + + free(ids); + close(fd); +} + +/* + * Return a representative id if ok, or 0 for failures. + * + * The perf_event->id is good for this, but an evsel would have multiple + * instances for CPUs and tasks. So pick up the first id and setup a hash + * from id of each instance to the representative id (the first one). + */ +static u64 create_event_hash(struct evsel *evsel) +{ + int x, y, fd; + u64 the_id = 0, id; + + fd = get_pinned_fd("event_hash"); + if (fd < 0) { + pr_err("cannot get fd for 'event_hash' map\n"); + return 0; + } + + for (x = 0; x < xyarray__max_x(evsel->core.fd); x++) { + for (y = 0; y < xyarray__max_y(evsel->core.fd); y++) { + int ret = ioctl(FD(evsel, x, y), PERF_EVENT_IOC_ID, &id); + + if (ret < 0) { + pr_err("Failed to get the event id\n"); + if (the_id) + destroy_event_hash(the_id); + return 0; + } + + if (the_id == 0) + the_id = id; + + bpf_map_update_elem(fd, &id, &the_id, BPF_ANY); + } + } + + close(fd); + return the_id; +} + +static void destroy_idx_hash(struct pinned_filter_idx *pfi) +{ + int fd, nr; + struct perf_thread_map *threads; + + fd = get_pinned_fd("filters"); + bpf_map_delete_elem(fd, &pfi->hash_idx); + close(fd); + + if (pfi->event_id) + destroy_event_hash(pfi->event_id); + + threads = perf_evsel__threads(&pfi->evsel->core); + if (threads == NULL) + return; + + fd = get_pinned_fd("idx_hash"); + nr = perf_thread_map__nr(threads); + for (int i = 0; i < nr; i++) { + /* The target task might be dead already, just try the pid */ + struct idx_hash_key key = { + .evt_id = pfi->event_id, + .tgid = perf_thread_map__pid(threads, i), + }; + + bpf_map_delete_elem(fd, &key); + } + close(fd); +} + +/* Maintain a hashmap from (tgid, event-id) to filter index */ +static int create_idx_hash(struct evsel *evsel, struct perf_bpf_filter_entry *entry) { int filter_idx; int fd, nr, last; + u64 event_id = 0; + struct pinned_filter_idx *pfi = NULL; struct perf_thread_map *threads; fd = get_pinned_fd("filters"); if (fd < 0) { - pr_debug("cannot get fd for 'filters' map\n"); + pr_err("cannot get fd for 'filters' map\n"); return fd; } /* Find the first available entry in the filters map */ for (filter_idx = 0; filter_idx < MAX_FILTERS; filter_idx++) { - if (bpf_map_update_elem(fd, &filter_idx, entry, BPF_NOEXIST) == 0) { - pinned_filter_idx = filter_idx; + if (bpf_map_update_elem(fd, &filter_idx, entry, BPF_NOEXIST) == 0) break; - } } close(fd); @@ -201,22 +371,44 @@ static int update_pid_hash(struct evsel *evsel, struct perf_bpf_filter_entry *en return -EBUSY; } + pfi = zalloc(sizeof(*pfi)); + if (pfi == NULL) { + pr_err("Cannot save pinned filter index\n"); + goto err; + } + + pfi->evsel = evsel; + pfi->hash_idx = filter_idx; + + event_id = create_event_hash(evsel); + if (event_id == 0) { + pr_err("Cannot update the event hash\n"); + goto err; + } + + pfi->event_id = event_id; + threads = perf_evsel__threads(&evsel->core); if (threads == NULL) { pr_err("Cannot get the thread list of the event\n"); - return -EINVAL; + goto err; } /* save the index to a hash map */ - fd = get_pinned_fd("pid_hash"); - if (fd < 0) - return fd; + fd = get_pinned_fd("idx_hash"); + if (fd < 0) { + pr_err("cannot get fd for 'idx_hash' map\n"); + goto err; + } last = -1; nr = perf_thread_map__nr(threads); for (int i = 0; i < nr; i++) { int pid = perf_thread_map__pid(threads, i); int tgid; + struct idx_hash_key key = { + .evt_id = event_id, + }; /* it actually needs tgid, let's get tgid from /proc. */ tgid = convert_to_tgid(pid); @@ -228,16 +420,25 @@ static int update_pid_hash(struct evsel *evsel, struct perf_bpf_filter_entry *en if (tgid == last) continue; last = tgid; + key.tgid = tgid; - if (bpf_map_update_elem(fd, &tgid, &filter_idx, BPF_ANY) < 0) { - pr_err("Failed to update the pid hash\n"); + if (bpf_map_update_elem(fd, &key, &filter_idx, BPF_ANY) < 0) { + pr_err("Failed to update the idx_hash\n"); close(fd); - return -1; + goto err; } - pr_debug("pid hash: %d -> %d\n", tgid, filter_idx); + pr_debug("bpf-filter: idx_hash (task=%d,%s) -> %d\n", + tgid, evsel__name(evsel), filter_idx); } + + list_add(&pfi->list, &pinned_filters); close(fd); - return 0; + return filter_idx; + +err: + destroy_idx_hash(pfi); + free(pfi); + return -1; } int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target) @@ -247,7 +448,7 @@ int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target) struct bpf_program *prog; struct bpf_link *link; struct perf_bpf_filter_entry *entry; - bool needs_pid_hash = !target__has_cpu(target) && !target->uid_str; + bool needs_idx_hash = !target__has_cpu(target) && !target->uid_str; entry = calloc(MAX_FILTERS, sizeof(*entry)); if (entry == NULL) @@ -259,11 +460,11 @@ int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target) goto err; } - if (needs_pid_hash && geteuid() != 0) { + if (needs_idx_hash && geteuid() != 0) { int zero = 0; /* The filters map is shared among other processes */ - ret = update_pid_hash(evsel, entry); + ret = create_idx_hash(evsel, entry); if (ret < 0) goto err; @@ -274,7 +475,7 @@ int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target) } /* Reset the lost count */ - bpf_map_update_elem(fd, &pinned_filter_idx, &zero, BPF_ANY); + bpf_map_update_elem(fd, &ret, &zero, BPF_ANY); close(fd); fd = get_pinned_fd("perf_sample_filter"); @@ -288,6 +489,7 @@ int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target) ret = ioctl(FD(evsel, x, y), PERF_EVENT_IOC_SET_BPF, fd); if (ret < 0) { pr_err("Failed to attach perf sample-filter\n"); + close(fd); goto err; } } @@ -332,6 +534,15 @@ int perf_bpf_filter__prepare(struct evsel *evsel, struct target *target) err: free(entry); + if (!list_empty(&pinned_filters)) { + struct pinned_filter_idx *pfi, *tmp; + + list_for_each_entry_safe(pfi, tmp, &pinned_filters, list) { + destroy_idx_hash(pfi); + list_del(&pfi->list); + free(pfi); + } + } sample_filter_bpf__destroy(skel); return ret; } @@ -339,6 +550,7 @@ err: int perf_bpf_filter__destroy(struct evsel *evsel) { struct perf_bpf_filter_expr *expr, *tmp; + struct pinned_filter_idx *pfi, *pos; list_for_each_entry_safe(expr, tmp, &evsel->bpf_filters, list) { list_del(&expr->list); @@ -346,14 +558,11 @@ int perf_bpf_filter__destroy(struct evsel *evsel) } sample_filter_bpf__destroy(evsel->bpf_skel); - if (pinned_filter_idx >= 0) { - int fd = get_pinned_fd("filters"); - - bpf_map_delete_elem(fd, &pinned_filter_idx); - pinned_filter_idx = -1; - close(fd); + list_for_each_entry_safe(pfi, pos, &pinned_filters, list) { + destroy_idx_hash(pfi); + list_del(&pfi->list); + free(pfi); } - return 0; } @@ -364,10 +573,20 @@ u64 perf_bpf_filter__lost_count(struct evsel *evsel) if (list_empty(&evsel->bpf_filters)) return 0; - if (pinned_filter_idx >= 0) { + if (!list_empty(&pinned_filters)) { int fd = get_pinned_fd("dropped"); + struct pinned_filter_idx *pfi; - bpf_map_lookup_elem(fd, &pinned_filter_idx, &count); + if (fd < 0) + return 0; + + list_for_each_entry(pfi, &pinned_filters, list) { + if (pfi->evsel != evsel) + continue; + + bpf_map_lookup_elem(fd, &pfi->hash_idx, &count); + break; + } close(fd); } else if (evsel->bpf_skel) { struct sample_filter_bpf *skel = evsel->bpf_skel; @@ -429,9 +648,10 @@ int perf_bpf_filter__pin(void) /* pinned program will use pid-hash */ bpf_map__set_max_entries(skel->maps.filters, MAX_FILTERS); - bpf_map__set_max_entries(skel->maps.pid_hash, MAX_PIDS); + bpf_map__set_max_entries(skel->maps.event_hash, MAX_EVT_HASH); + bpf_map__set_max_entries(skel->maps.idx_hash, MAX_IDX_HASH); bpf_map__set_max_entries(skel->maps.dropped, MAX_FILTERS); - skel->rodata->use_pid_hash = 1; + skel->rodata->use_idx_hash = 1; if (sample_filter_bpf__load(skel) < 0) { ret = -errno; @@ -484,8 +704,12 @@ int perf_bpf_filter__pin(void) pr_debug("chmod for filters failed\n"); ret = -errno; } - if (fchmodat(dir_fd, "pid_hash", 0666, 0) < 0) { - pr_debug("chmod for pid_hash failed\n"); + if (fchmodat(dir_fd, "event_hash", 0666, 0) < 0) { + pr_debug("chmod for event_hash failed\n"); + ret = -errno; + } + if (fchmodat(dir_fd, "idx_hash", 0666, 0) < 0) { + pr_debug("chmod for idx_hash failed\n"); ret = -errno; } if (fchmodat(dir_fd, "dropped", 0666, 0) < 0) { diff --git a/tools/perf/util/bpf_skel/sample-filter.h b/tools/perf/util/bpf_skel/sample-filter.h index e666bfd5fbdd..5f0c8e4e83d3 100644 --- a/tools/perf/util/bpf_skel/sample-filter.h +++ b/tools/perf/util/bpf_skel/sample-filter.h @@ -1,8 +1,9 @@ #ifndef PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H #define PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H -#define MAX_FILTERS 64 -#define MAX_PIDS (16 * 1024) +#define MAX_FILTERS 64 +#define MAX_IDX_HASH (16 * 1024) +#define MAX_EVT_HASH (1024 * 1024) /* supported filter operations */ enum perf_bpf_filter_op { @@ -62,4 +63,10 @@ struct perf_bpf_filter_entry { __u64 value; }; +struct idx_hash_key { + __u64 evt_id; + __u32 tgid; + __u32 reserved; +}; + #endif /* PERF_UTIL_BPF_SKEL_SAMPLE_FILTER_H */ diff --git a/tools/perf/util/bpf_skel/sample_filter.bpf.c b/tools/perf/util/bpf_skel/sample_filter.bpf.c index 4c75354b84fd..4872a16eedfd 100644 --- a/tools/perf/util/bpf_skel/sample_filter.bpf.c +++ b/tools/perf/util/bpf_skel/sample_filter.bpf.c @@ -15,13 +15,25 @@ struct filters { __uint(max_entries, 1); } filters SEC(".maps"); -/* tgid to filter index */ -struct pid_hash { +/* + * An evsel has multiple instances for each CPU or task but we need a single + * id to be used as a key for the idx_hash. This hashmap would translate the + * instance's ID to a representative ID. + */ +struct event_hash { __uint(type, BPF_MAP_TYPE_HASH); - __type(key, int); + __type(key, __u64); + __type(value, __u64); + __uint(max_entries, 1); +} event_hash SEC(".maps"); + +/* tgid/evtid to filter index */ +struct idx_hash { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, struct idx_hash_key); __type(value, int); __uint(max_entries, 1); -} pid_hash SEC(".maps"); +} idx_hash SEC(".maps"); /* tgid to filter index */ struct lost_count { @@ -31,7 +43,7 @@ struct lost_count { __uint(max_entries, 1); } dropped SEC(".maps"); -volatile const int use_pid_hash; +volatile const int use_idx_hash; void *bpf_cast_to_kern_ctx(void *) __ksym; @@ -202,11 +214,25 @@ int perf_sample_filter(void *ctx) k = 0; - if (use_pid_hash) { - int tgid = bpf_get_current_pid_tgid() >> 32; + if (use_idx_hash) { + struct idx_hash_key key = { + .tgid = bpf_get_current_pid_tgid() >> 32, + }; + __u64 eid = kctx->event->id; + __u64 *key_id; int *idx; - idx = bpf_map_lookup_elem(&pid_hash, &tgid); + /* get primary_event_id */ + if (kctx->event->parent) + eid = kctx->event->parent->id; + + key_id = bpf_map_lookup_elem(&event_hash, &eid); + if (key_id == NULL) + goto drop; + + key.evt_id = *key_id; + + idx = bpf_map_lookup_elem(&idx_hash, &key); if (idx) k = *idx; else diff --git a/tools/perf/util/bpf_skel/vmlinux/vmlinux.h b/tools/perf/util/bpf_skel/vmlinux/vmlinux.h index d818e30c5457..4fa21468487e 100644 --- a/tools/perf/util/bpf_skel/vmlinux/vmlinux.h +++ b/tools/perf/util/bpf_skel/vmlinux/vmlinux.h @@ -175,6 +175,11 @@ struct perf_sample_data { u64 code_page_size; } __attribute__((__aligned__(64))) __attribute__((preserve_access_index)); +struct perf_event { + struct perf_event *parent; + u64 id; +} __attribute__((preserve_access_index)); + struct bpf_perf_event_data_kern { struct perf_sample_data *data; struct perf_event *event; From 1a5474a779798f6ba9d798469326ebfa6b4a8fae Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 20 Aug 2024 08:45:03 -0700 Subject: [PATCH 218/347] perf tools: Print lost samples due to BPF filter Print the actual dropped sample count in the event stat. $ sudo perf record -o- -e cycles --filter 'period < 10000' \ -e instructions --filter 'ip > 0x8000000000000000' perf test -w noploop | \ perf report --stat -i- [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.058 MB - ] Aggregated stats: TOTAL events: 469 MMAP events: 268 (57.1%) COMM events: 2 ( 0.4%) EXIT events: 1 ( 0.2%) SAMPLE events: 16 ( 3.4%) MMAP2 events: 22 ( 4.7%) LOST_SAMPLES events: 2 ( 0.4%) KSYMBOL events: 89 (19.0%) BPF_EVENT events: 39 ( 8.3%) ATTR events: 2 ( 0.4%) FINISHED_ROUND events: 1 ( 0.2%) ID_INDEX events: 1 ( 0.2%) THREAD_MAP events: 1 ( 0.2%) CPU_MAP events: 1 ( 0.2%) EVENT_UPDATE events: 2 ( 0.4%) TIME_CONV events: 1 ( 0.2%) FEATURE events: 20 ( 4.3%) FINISHED_INIT events: 1 ( 0.2%) cycles stats: SAMPLE events: 2 LOST_SAMPLES (BPF) events: 4010 instructions stats: SAMPLE events: 14 LOST_SAMPLES (BPF) events: 3990 Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: KP Singh Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Link: https://lore.kernel.org/r/20240820154504.128923-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 9 +++++++-- tools/perf/ui/stdio/hist.c | 4 ++-- tools/perf/util/events_stats.h | 15 ++++++++++++++- tools/perf/util/hist.c | 19 +++++++++++++++---- tools/perf/util/hist.h | 1 + tools/perf/util/machine.c | 5 +++-- tools/perf/util/session.c | 5 +++-- 7 files changed, 45 insertions(+), 13 deletions(-) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index 1643113616f4..c304d9b06190 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -795,8 +795,13 @@ static int count_lost_samples_event(const 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; } diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c index 9372e8904d22..74b2c619c56c 100644 --- a/tools/perf/ui/stdio/hist.c +++ b/tools/perf/ui/stdio/hist.c @@ -913,11 +913,11 @@ size_t events_stats__fprintf(struct events_stats *stats, FILE *fp) continue; if (i && total) { - ret += fprintf(fp, "%16s events: %10d (%4.1f%%)\n", + ret += fprintf(fp, "%20s events: %10d (%4.1f%%)\n", name, stats->nr_events[i], 100.0 * stats->nr_events[i] / total); } else { - ret += fprintf(fp, "%16s events: %10d\n", + ret += fprintf(fp, "%20s events: %10d\n", name, stats->nr_events[i]); } } diff --git a/tools/perf/util/events_stats.h b/tools/perf/util/events_stats.h index f43e5b1a366a..eabd7913c309 100644 --- a/tools/perf/util/events_stats.h +++ b/tools/perf/util/events_stats.h @@ -18,7 +18,18 @@ * PERF_RECORD_LOST_SAMPLES event. The number of lost-samples events is stored * in .nr_events[PERF_RECORD_LOST_SAMPLES] while total_lost_samples tells * exactly how many samples the kernel in fact dropped, i.e. it is the sum of - * all struct perf_record_lost_samples.lost fields reported. + * all struct perf_record_lost_samples.lost fields reported without setting the + * misc field in the header. + * + * The BPF program can discard samples according to the filter expressions given + * by the user. This number is kept in a BPF map and dumped at the end of perf + * record in a PERF_RECORD_LOST_SAMPLES event. To differentiate it from other + * lost samples, perf tools sets PERF_RECORD_MISC_LOST_SAMPLES_BPF flag in the + * header.misc field. The number of dropped-samples events is stored in + * .nr_events[PERF_RECORD_LOST_SAMPLES] while total_dropped_samples tells + * exactly how many samples the BPF program in fact dropped, i.e. it is the sum + * of all struct perf_record_lost_samples.lost fields reported with the misc + * field set in the header. * * The total_period is needed because by default auto-freq is used, so * multiplying nr_events[PERF_EVENT_SAMPLE] by a frequency isn't possible to get @@ -28,6 +39,7 @@ struct events_stats { u64 total_lost; u64 total_lost_samples; + u64 total_dropped_samples; u64 total_aux_lost; u64 total_aux_partial; u64 total_aux_collision; @@ -48,6 +60,7 @@ struct hists_stats { u32 nr_samples; u32 nr_non_filtered_samples; u32 nr_lost_samples; + u32 nr_dropped_samples; }; void events_stats__inc(struct events_stats *stats, u32 type); diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 0f9ce2ee2c31..dadce2889e52 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -2385,6 +2385,11 @@ void hists__inc_nr_lost_samples(struct hists *hists, u32 lost) hists->stats.nr_lost_samples += lost; } +void hists__inc_nr_dropped_samples(struct hists *hists, u32 lost) +{ + hists->stats.nr_dropped_samples += lost; +} + static struct hist_entry *hists__add_dummy_entry(struct hists *hists, struct hist_entry *pair) { @@ -2729,18 +2734,24 @@ size_t evlist__fprintf_nr_events(struct evlist *evlist, FILE *fp) evlist__for_each_entry(evlist, pos) { struct hists *hists = evsel__hists(pos); + u64 total_samples = hists->stats.nr_samples; - if (symbol_conf.skip_empty && !hists->stats.nr_samples && - !hists->stats.nr_lost_samples) + total_samples += hists->stats.nr_lost_samples; + total_samples += hists->stats.nr_dropped_samples; + + if (symbol_conf.skip_empty && total_samples == 0) continue; ret += fprintf(fp, "%s stats:\n", evsel__name(pos)); if (hists->stats.nr_samples) - ret += fprintf(fp, "%16s events: %10d\n", + ret += fprintf(fp, "%20s events: %10d\n", "SAMPLE", hists->stats.nr_samples); if (hists->stats.nr_lost_samples) - ret += fprintf(fp, "%16s events: %10d\n", + ret += fprintf(fp, "%20s events: %10d\n", "LOST_SAMPLES", hists->stats.nr_lost_samples); + if (hists->stats.nr_dropped_samples) + ret += fprintf(fp, "%20s events: %10d\n", + "LOST_SAMPLES (BPF)", hists->stats.nr_dropped_samples); } return ret; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index deb1087c5948..7d7ae94b4b31 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -372,6 +372,7 @@ void hists__inc_stats(struct hists *hists, struct hist_entry *h); void hists__inc_nr_events(struct hists *hists); void hists__inc_nr_samples(struct hists *hists, bool filtered); void hists__inc_nr_lost_samples(struct hists *hists, u32 lost); +void hists__inc_nr_dropped_samples(struct hists *hists, u32 lost); size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, int max_cols, float min_pcnt, FILE *fp, diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index cd79a830abae..5783b96fb988 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -642,8 +642,9 @@ int machine__process_lost_event(struct machine *machine __maybe_unused, int machine__process_lost_samples_event(struct machine *machine __maybe_unused, union perf_event *event, struct perf_sample *sample) { - dump_printf(": id:%" PRIu64 ": lost samples :%" PRI_lu64 "\n", - sample->id, event->lost_samples.lost); + dump_printf(": id:%" PRIu64 ": lost samples :%" PRI_lu64 "%s\n", + sample->id, event->lost_samples.lost, + event->header.misc & PERF_RECORD_MISC_LOST_SAMPLES_BPF ? " (BPF)" : ""); return 0; } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index d2bd563119bc..774eb3382000 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -1290,8 +1290,9 @@ static int machines__deliver_event(struct machines *machines, evlist->stats.total_lost += event->lost.lost; return tool->lost(tool, event, sample, machine); case PERF_RECORD_LOST_SAMPLES: - if (tool->lost_samples == perf_event__process_lost_samples && - !(event->header.misc & PERF_RECORD_MISC_LOST_SAMPLES_BPF)) + if (event->header.misc & PERF_RECORD_MISC_LOST_SAMPLES_BPF) + evlist->stats.total_dropped_samples += event->lost_samples.lost; + else if (tool->lost_samples == perf_event__process_lost_samples) evlist->stats.total_lost_samples += event->lost_samples.lost; return tool->lost_samples(tool, event, sample, machine); case PERF_RECORD_READ: From 150ca9ccc4e94eaa07b9d3e07f45fec842b68a78 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 20 Aug 2024 08:45:04 -0700 Subject: [PATCH 219/347] perf test: Update sample filtering tests with multiple events Add Multiple bpf-filter test for two or more events with filters. It uses task-clock and page-faults events with different filter expressions and check the perf script output $ sudo ./perf test filtering -vv 96: perf record sample filtering (by BPF) tests: --- start --- test child forked, pid 2804025 Checking BPF-filter privilege Basic bpf-filter test Basic bpf-filter test [Success] Failing bpf-filter test Error: task-clock event does not have PERF_SAMPLE_CPU Failing bpf-filter test [Success] Group bpf-filter test Error: task-clock event does not have PERF_SAMPLE_CPU Error: task-clock event does not have PERF_SAMPLE_CODE_PAGE_SIZE Group bpf-filter test [Success] Multiple bpf-filter test Multiple bpf-filter test [Success] ---- end(0) ---- 96: perf record sample filtering (by BPF) tests : Ok Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: KP Singh Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Link: https://lore.kernel.org/r/20240820154504.128923-3-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/record_bpf_filter.sh | 34 +++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tools/perf/tests/shell/record_bpf_filter.sh b/tools/perf/tests/shell/record_bpf_filter.sh index c5882d620db7..4df33c15bfa7 100755 --- a/tools/perf/tests/shell/record_bpf_filter.sh +++ b/tools/perf/tests/shell/record_bpf_filter.sh @@ -116,6 +116,36 @@ test_bpf_filter_group() { echo "Group bpf-filter test [Success]" } +test_bpf_filter_multi() { + echo "Multiple bpf-filter test" + + if ! perf record -e task-clock --filter 'period > 100000' \ + -e page-faults --filter 'ip < 0xffffffff00000000' \ + -o "${perfdata}" true 2> /dev/null + then + echo "Multiple bpf-filter test [Failed record]" + err=1 + return + fi + + if ! perf script -i "${perfdata}" -F period,event | grep task-clock | \ + awk '{ if (int($1) <= 100000) { print $0; exit(1); } }' + then + echo "Multiple bpf-filter test [Failed task-clock period]" + err=1 + return + fi + + if perf script -i "${perfdata}" -F event,ip | grep page-fault | \ + grep 'ffffffff[0-9a-f]*' + then + echo "Multiple bpf-filter test [Failed page-faults ip]" + err=1 + return + fi + + echo "Multiple bpf-filter test [Success]" +} test_bpf_filter_priv @@ -131,5 +161,9 @@ if [ $err = 0 ]; then test_bpf_filter_group fi +if [ $err = 0 ]; then + test_bpf_filter_multi +fi + cleanup exit $err From a68080e1a21b3183ee804ae05726f6103e322348 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 27 Aug 2024 15:57:33 -0300 Subject: [PATCH 220/347] perf test vfs_getname: Look for alternative line where to collect the pathname The getname_flags() routine changed recently and thus the place where we were getting the pathname is not probeable anymore, albeit still present, so use the next line for that, before: root@number:/home/acme/git/perf-tools-next# perf test vfs_getname 91: Add vfs_getname probe to get syscall args filenames : FAILED! 93: Use vfs_getname probe to get syscall args filenames : FAILED! 126: Check open filename arg using perf trace + vfs_getname : FAILED! root@number:/home/acme/git/perf-tools-next# Now tests 91 and 126 are passing, some more investigation is needed for test 93, that continues to fail. Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/lib/probe_vfs_getname.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/perf/tests/shell/lib/probe_vfs_getname.sh b/tools/perf/tests/shell/lib/probe_vfs_getname.sh index bf4c1fb71c4b..b3802d9494b4 100644 --- a/tools/perf/tests/shell/lib/probe_vfs_getname.sh +++ b/tools/perf/tests/shell/lib/probe_vfs_getname.sh @@ -13,7 +13,12 @@ cleanup_probe_vfs_getname() { add_probe_vfs_getname() { add_probe_verbose=$1 if [ $had_vfs_getname -eq 1 ] ; then - line=$(perf probe -L getname_flags 2>&1 | grep -E 'result.*=.*filename;' | sed -r 's/[[:space:]]+([[:digit:]]+)[[:space:]]+result->uptr.*/\1/') + result_filename_re="[[:space:]]+([[:digit:]]+)[[:space:]]+result->uptr.*" + line=$(perf probe -L getname_flags 2>&1 | grep -E "$result_filename_re=" | sed -r "s/$result_filename_re=/\1/") + if [ -z "$line" ] ; then + result_aname_re="[[:space:]]+([[:digit:]]+)[[:space:]]+result->aname = NULL;" + line=$(perf probe -L getname_flags 2>&1 | grep -E "$result_aname_re=" | sed -r "s/$result_aname_re=/\1/") + fi perf probe -q "vfs_getname=getname_flags:${line} pathname=result->name:string" || \ perf probe $add_probe_verbose "vfs_getname=getname_flags:${line} pathname=filename:ustring" fi From 5a02447c8145b87373ada2040bbab5bc2a8f87be Mon Sep 17 00:00:00 2001 From: Michael Petlan Date: Tue, 2 Jul 2024 13:08:40 +0200 Subject: [PATCH 221/347] perf tests shell: Skip base_* dirs in test script search The test scripts in base_* directories currently have their own drivers that run them. Before this patch, the shell test-suite generator causes them to run twice. Fix that by skipping them in the generator. A cleaner solution (for future) will be to use the directory structure idea (introduced by Carsten Haitzler in 7391db645938 ("perf test: Refactor shell tests allowing subdirs")) to generate test entries with subtests, like: $ perf test list [...] 97: perf probe shell tests 97:1: perf probe basic functionality 97:2: perf probe tests with arguments 97:3: perf probe invalid options handling [...] There is already a lot of shell test scripts and many are about to come, so there is a need for some hierarchy. Signed-off-by: Michael Petlan Cc: Athira Rajeev Cc: Ian Rogers Cc: Masami Hiramatsu Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240702110849.31904-3-vmolnaro@redhat.com Signed-off-by: Veronika Molnarova Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/tests-scripts.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/tests/tests-scripts.c b/tools/perf/tests/tests-scripts.c index 8dc1e398288c..ed114b044293 100644 --- a/tools/perf/tests/tests-scripts.c +++ b/tools/perf/tests/tests-scripts.c @@ -251,6 +251,8 @@ static void append_scripts_in_dir(int dir_fd, if (!S_ISDIR(st.st_mode)) continue; } + if (strncmp(ent->d_name, "base_", 5) == 0) + continue; /* Skip scripts that have a separate driver. */ fd = openat(dir_fd, ent->d_name, O_PATH); append_scripts_in_dir(fd, result, result_sz); } From a3a02a52bcfcbcc4a637d4b68bf1bc391c9fad02 Mon Sep 17 00:00:00 2001 From: Veronika Molnarova Date: Tue, 2 Jul 2024 13:08:41 +0200 Subject: [PATCH 222/347] perf testsuite: Merge settings files for shell tests Merge perf testsuite setting files into common settings to reduce duplicates and prevent errors. Signed-off-by: Michael Petlan Signed-off-by: Veronika Molnarova Cc: Athira Rajeev Cc: Ian Rogers Cc: Masami Hiramatsu Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240702110849.31904-4-vmolnaro@redhat.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/base_probe/settings.sh | 48 ------------------- .../shell/base_probe/test_adding_kernel.sh | 3 -- tools/perf/tests/shell/common/init.sh | 23 +++++++++ tools/perf/tests/shell/common/settings.sh | 23 +++++++++ 4 files changed, 46 insertions(+), 51 deletions(-) delete mode 100644 tools/perf/tests/shell/base_probe/settings.sh diff --git a/tools/perf/tests/shell/base_probe/settings.sh b/tools/perf/tests/shell/base_probe/settings.sh deleted file mode 100644 index 123621c7f95e..000000000000 --- a/tools/perf/tests/shell/base_probe/settings.sh +++ /dev/null @@ -1,48 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# settings.sh of perf_probe test -# Author: Michael Petlan -# Author: Masami Hiramatsu -# - -export TEST_NAME="perf_probe" - -export MY_ARCH=`arch` - -if [ -n "$PERFSUITE_RUN_DIR" ]; then - # when $PERFSUITE_RUN_DIR is set to something, all the logs and temp files will be placed there - # --> the $PERFSUITE_RUN_DIR/perf_something/examples and $PERFSUITE_RUN_DIR/perf_something/logs - # dirs will be used for that - export PERFSUITE_RUN_DIR=`readlink -f $PERFSUITE_RUN_DIR` - export CURRENT_TEST_DIR="$PERFSUITE_RUN_DIR/$TEST_NAME" - export MAKE_TARGET_DIR="$CURRENT_TEST_DIR/examples" - test -d "$MAKE_TARGET_DIR" || mkdir -p "$MAKE_TARGET_DIR" - export LOGS_DIR="$PERFSUITE_RUN_DIR/$TEST_NAME/logs" - test -d "$LOGS_DIR" || mkdir -p "$LOGS_DIR" -else - # when $PERFSUITE_RUN_DIR is not set, logs will be placed here - export CURRENT_TEST_DIR="." - export LOGS_DIR="." -fi - -check_kprobes_available() -{ - test -e /sys/kernel/debug/tracing/kprobe_events -} - -check_uprobes_available() -{ - test -e /sys/kernel/debug/tracing/uprobe_events -} - -clear_all_probes() -{ - echo 0 > /sys/kernel/debug/tracing/events/enable - check_kprobes_available && echo > /sys/kernel/debug/tracing/kprobe_events - check_uprobes_available && echo > /sys/kernel/debug/tracing/uprobe_events -} - -check_sdt_support() -{ - $CMD_PERF list sdt | grep sdt > /dev/null 2> /dev/null -} diff --git a/tools/perf/tests/shell/base_probe/test_adding_kernel.sh b/tools/perf/tests/shell/base_probe/test_adding_kernel.sh index 187dc8d4b163..d541ffd44a93 100755 --- a/tools/perf/tests/shell/base_probe/test_adding_kernel.sh +++ b/tools/perf/tests/shell/base_probe/test_adding_kernel.sh @@ -15,10 +15,7 @@ # include working environment . ../common/init.sh -. ./settings.sh -# shellcheck disable=SC2034 # the variable is later used after the working environment is included -THIS_TEST_NAME=`basename $0 .sh` TEST_RESULT=0 # shellcheck source=lib/probe_vfs_getname.sh diff --git a/tools/perf/tests/shell/common/init.sh b/tools/perf/tests/shell/common/init.sh index aadeaf782e03..b99fba50ba94 100644 --- a/tools/perf/tests/shell/common/init.sh +++ b/tools/perf/tests/shell/common/init.sh @@ -115,3 +115,26 @@ detect_amd() # 1 = is not AMD or unknown grep "vendor_id" < /proc/cpuinfo | grep -q "AMD" } + +# base probe utility +check_kprobes_available() +{ + test -e /sys/kernel/debug/tracing/kprobe_events +} + +check_uprobes_available() +{ + test -e /sys/kernel/debug/tracing/uprobe_events +} + +clear_all_probes() +{ + echo 0 > /sys/kernel/debug/tracing/events/enable + check_kprobes_available && echo > /sys/kernel/debug/tracing/kprobe_events + check_uprobes_available && echo > /sys/kernel/debug/tracing/uprobe_events +} + +check_sdt_support() +{ + $CMD_PERF list sdt | grep sdt > /dev/null 2> /dev/null +} diff --git a/tools/perf/tests/shell/common/settings.sh b/tools/perf/tests/shell/common/settings.sh index 361641dbaaad..dcdb7d49ac00 100644 --- a/tools/perf/tests/shell/common/settings.sh +++ b/tools/perf/tests/shell/common/settings.sh @@ -65,6 +65,29 @@ else export MEND="" fi +### general info +DIR_PATH=`dirname "$(readlink -e "$0")"` + +export TEST_NAME=`basename $DIR_PATH | sed 's/base/perf/'` +export MY_ARCH=`arch` + +# storing logs and temporary files variables +if [ -n "$PERFSUITE_RUN_DIR" ]; then + # when $PERFSUITE_RUN_DIR is set to something, all the logs and temp files will be placed there + # --> the $PERFSUITE_RUN_DIR/perf_something/examples and $PERFSUITE_RUN_DIR/perf_something/logs + # dirs will be used for that + export PERFSUITE_RUN_DIR=`readlink -f $PERFSUITE_RUN_DIR` + export CURRENT_TEST_DIR="$PERFSUITE_RUN_DIR/$TEST_NAME" + export MAKE_TARGET_DIR="$CURRENT_TEST_DIR/examples" + export LOGS_DIR="$CURRENT_TEST_DIR/logs" + test -d "$CURRENT_TEST_DIR" || mkdir -p "$CURRENT_TEST_DIR" + test -d "$LOGS_DIR" || mkdir -p "$LOGS_DIR" +else + # when $PERFSUITE_RUN_DIR is not set, logs will be placed here + export CURRENT_TEST_DIR="." + export LOGS_DIR="." +fi + #### test parametrization if [ ! -d ./common ]; then From 32ddd082dcac111908c5f11e0617cd0f8ec10350 Mon Sep 17 00:00:00 2001 From: Veronika Molnarova Date: Tue, 2 Jul 2024 13:08:42 +0200 Subject: [PATCH 223/347] perf testsuite: Fix shellcheck warnings Shellcheck is becoming a standard when building perf to prevent any unnecessary mistakes. Fix shellcheck warnings in perf testsuite. Signed-off-by: Veronika Molnarova Cc: Athira Rajeev Cc: Ian Rogers Cc: Masami Hiramatsu Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240702110849.31904-5-vmolnaro@redhat.com Signed-off-by: Michael Petlan Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/common/init.sh | 8 ++++---- tools/perf/tests/shell/common/settings.sh | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/tools/perf/tests/shell/common/init.sh b/tools/perf/tests/shell/common/init.sh index b99fba50ba94..075f17623c8e 100644 --- a/tools/perf/tests/shell/common/init.sh +++ b/tools/perf/tests/shell/common/init.sh @@ -26,8 +26,8 @@ print_results() PERF_RETVAL="$1"; shift CHECK_RETVAL="$1"; shift FAILURE_REASON="" - TASK_COMMENT="$@" - if [ $PERF_RETVAL -eq 0 -a $CHECK_RETVAL -eq 0 ]; then + TASK_COMMENT="$*" + if [ $PERF_RETVAL -eq 0 ] && [ $CHECK_RETVAL -eq 0 ]; then _echo "$MPASS-- [ PASS ] --$MEND $TEST_NAME :: $THIS_TEST_NAME :: $TASK_COMMENT" return 0 else @@ -56,7 +56,7 @@ print_overall_results() print_testcase_skipped() { - TASK_COMMENT="$@" + TASK_COMMENT="$*" _echo "$MSKIP-- [ SKIP ] --$MEND $TEST_NAME :: $THIS_TEST_NAME :: $TASK_COMMENT :: testcase skipped" return 0 } @@ -69,7 +69,7 @@ print_overall_skipped() print_warning() { - WARN_COMMENT="$@" + WARN_COMMENT="$*" _echo "$MWARN-- [ WARN ] --$MEND $TEST_NAME :: $THIS_TEST_NAME :: $WARN_COMMENT" return 0 } diff --git a/tools/perf/tests/shell/common/settings.sh b/tools/perf/tests/shell/common/settings.sh index dcdb7d49ac00..2e50dd0bff9b 100644 --- a/tools/perf/tests/shell/common/settings.sh +++ b/tools/perf/tests/shell/common/settings.sh @@ -45,7 +45,7 @@ export TEST_IGNORE_MISSING_PMU=${TEST_IGNORE_MISSING_PMU:-n} export LC_ALL=C #### colors -if [ -t 1 -o "$TESTLOG_FORCE_COLOR" = "yes" ]; then +if [ -t 1 ] || [ "$TESTLOG_FORCE_COLOR" = "yes" ]; then export MPASS="\e[32m" export MALLPASS="\e[1;32m" export MFAIL="\e[31m" @@ -68,15 +68,15 @@ fi ### general info DIR_PATH=`dirname "$(readlink -e "$0")"` -export TEST_NAME=`basename $DIR_PATH | sed 's/base/perf/'` -export MY_ARCH=`arch` +TEST_NAME=`basename $DIR_PATH | sed 's/base/perf/'`; export TEST_NAME +MY_ARCH=`arch`; export MY_ARCH # storing logs and temporary files variables if [ -n "$PERFSUITE_RUN_DIR" ]; then # when $PERFSUITE_RUN_DIR is set to something, all the logs and temp files will be placed there # --> the $PERFSUITE_RUN_DIR/perf_something/examples and $PERFSUITE_RUN_DIR/perf_something/logs # dirs will be used for that - export PERFSUITE_RUN_DIR=`readlink -f $PERFSUITE_RUN_DIR` + PERFSUITE_RUN_DIR=`readlink -f $PERFSUITE_RUN_DIR`; export PERFSUITE_RUN_DIR export CURRENT_TEST_DIR="$PERFSUITE_RUN_DIR/$TEST_NAME" export MAKE_TARGET_DIR="$CURRENT_TEST_DIR/examples" export LOGS_DIR="$CURRENT_TEST_DIR/logs" @@ -93,6 +93,7 @@ fi if [ ! -d ./common ]; then # set parameters based on runmode if [ -f ../common/parametrization.$PERFTOOL_TESTSUITE_RUNMODE.sh ]; then + # shellcheck source=/dev/null . ../common/parametrization.$PERFTOOL_TESTSUITE_RUNMODE.sh fi # if some parameters haven't been set until now, set them to default From def5480d63c1e84779d36bc2fe78382e3c5e1b12 Mon Sep 17 00:00:00 2001 From: Veronika Molnarova Date: Tue, 2 Jul 2024 13:08:43 +0200 Subject: [PATCH 224/347] perf testsuite probe: Add test for blacklisted kprobes handling Test perf probe interface. Blacklisted functions should be rejected when there is an attempt to set a kprobe to them. Signed-off-by: Veronika Molnarova Cc: Athira Rajeev Cc: Ian Rogers Cc: Masami Hiramatsu Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240702110849.31904-6-vmolnaro@redhat.com Signed-off-by: Michael Petlan Signed-off-by: Arnaldo Carvalho de Melo --- .../base_probe/test_adding_blacklisted.sh | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100755 tools/perf/tests/shell/base_probe/test_adding_blacklisted.sh diff --git a/tools/perf/tests/shell/base_probe/test_adding_blacklisted.sh b/tools/perf/tests/shell/base_probe/test_adding_blacklisted.sh new file mode 100755 index 000000000000..b5dc10b2a738 --- /dev/null +++ b/tools/perf/tests/shell/base_probe/test_adding_blacklisted.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# SPDX-License-Identifier: GPL-2.0 + +# +# test_adding_blacklisted of perf_probe test +# Author: Masami Hiramatsu +# Author: Michael Petlan +# +# Description: +# +# Blacklisted functions should not be added successfully as probes, +# they must be skipped. +# + +# include working environment +. ../common/init.sh + +TEST_RESULT=0 + +# skip if not supported +BLACKFUNC=`head -n 1 /sys/kernel/debug/kprobes/blacklist 2> /dev/null | cut -f2` +if [ -z "$BLACKFUNC" ]; then + print_overall_skipped + exit 0 +fi + +# remove all previously added probes +clear_all_probes + + +### adding blacklisted function + +# functions from blacklist should be skipped by perf probe +! $CMD_PERF probe $BLACKFUNC > $LOGS_DIR/adding_blacklisted.log 2> $LOGS_DIR/adding_blacklisted.err +PERF_EXIT_CODE=$? + +REGEX_SCOPE_FAIL="Failed to find scope of probe point" +REGEX_SKIP_MESSAGE=" is blacklisted function, skip it\." +REGEX_NOT_FOUND_MESSAGE="Probe point \'$BLACKFUNC\' not found." +REGEX_ERROR_MESSAGE="Error: Failed to add events." +REGEX_INVALID_ARGUMENT="Failed to write event: Invalid argument" +REGEX_SYMBOL_FAIL="Failed to find symbol at $RE_ADDRESS" +REGEX_OUT_SECTION="$BLACKFUNC is out of \.\w+, skip it" +../common/check_all_lines_matched.pl "$REGEX_SKIP_MESSAGE" "$REGEX_NOT_FOUND_MESSAGE" "$REGEX_ERROR_MESSAGE" "$REGEX_SCOPE_FAIL" "$REGEX_INVALID_ARGUMENT" "$REGEX_SYMBOL_FAIL" "$REGEX_OUT_SECTION" < $LOGS_DIR/adding_blacklisted.err +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "adding blacklisted function $BLACKFUNC" +(( TEST_RESULT += $? )) + + +### listing not-added probe + +# blacklisted probes should NOT appear in perf-list output +$CMD_PERF list probe:\* > $LOGS_DIR/adding_blacklisted_list.log +PERF_EXIT_CODE=$? + +../common/check_all_lines_matched.pl "$RE_LINE_EMPTY" "List of pre-defined events" "Metric Groups:" < $LOGS_DIR/adding_blacklisted_list.log +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "listing blacklisted probe (should NOT be listed)" +(( TEST_RESULT += $? )) + + +# print overall results +print_overall_results "$TEST_RESULT" +exit $? From adc1dd00dbc192a8811fab482d0e26b2914ba3e3 Mon Sep 17 00:00:00 2001 From: Veronika Molnarova Date: Tue, 2 Jul 2024 13:08:44 +0200 Subject: [PATCH 225/347] perf testsuite probe: Add test for basic perf-probe options Test basic behavior of perf-probe subcommand. It is run as a part of perftool-testsuite_probe test case. Signed-off-by: Veronika Molnarova Cc: Athira Rajeev Cc: Ian Rogers Cc: Masami Hiramatsu Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240702110849.31904-7-vmolnaro@redhat.com Signed-off-by: Michael Petlan Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/tests/shell/base_probe/test_basic.sh | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100755 tools/perf/tests/shell/base_probe/test_basic.sh diff --git a/tools/perf/tests/shell/base_probe/test_basic.sh b/tools/perf/tests/shell/base_probe/test_basic.sh new file mode 100755 index 000000000000..09669ec479f2 --- /dev/null +++ b/tools/perf/tests/shell/base_probe/test_basic.sh @@ -0,0 +1,80 @@ +#!/bin/bash + +# SPDX-License-Identifier: GPL-2.0 + +# +# test_basic of perf_probe test +# Author: Michael Petlan +# Author: Masami Hiramatsu +# +# Description: +# +# This test tests basic functionality of perf probe command. +# + +# include working environment +. ../common/init.sh + +TEST_RESULT=0 + +if ! check_kprobes_available; then + print_overall_skipped + exit 0 +fi + + +### help message + +if [ "$PARAM_GENERAL_HELP_TEXT_CHECK" = "y" ]; then + # test that a help message is shown and looks reasonable + $CMD_PERF probe --help > $LOGS_DIR/basic_helpmsg.log 2> $LOGS_DIR/basic_helpmsg.err + PERF_EXIT_CODE=$? + + ../common/check_all_patterns_found.pl "PERF-PROBE" "NAME" "SYNOPSIS" "DESCRIPTION" "OPTIONS" "PROBE\s+SYNTAX" "PROBE\s+ARGUMENT" "LINE\s+SYNTAX" < $LOGS_DIR/basic_helpmsg.log + CHECK_EXIT_CODE=$? + ../common/check_all_patterns_found.pl "LAZY\s+MATCHING" "FILTER\s+PATTERN" "EXAMPLES" "SEE\s+ALSO" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_all_patterns_found.pl "vmlinux" "module=" "source=" "verbose" "quiet" "add=" "del=" "list.*EVENT" "line=" "vars=" "externs" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_all_patterns_found.pl "no-inlines" "funcs.*FILTER" "filter=FILTER" "force" "dry-run" "max-probes" "exec=" "demangle-kernel" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_no_patterns_found.pl "No manual entry for" < $LOGS_DIR/basic_helpmsg.err + (( CHECK_EXIT_CODE += $? )) + + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "help message" + (( TEST_RESULT += $? )) +else + print_testcase_skipped "help message" +fi + + +### usage message + +# without any args perf-probe should print usage +$CMD_PERF probe 2> $LOGS_DIR/basic_usage.log > /dev/null + +../common/check_all_patterns_found.pl "[Uu]sage" "perf probe" "verbose" "quiet" "add" "del" "force" "line" "vars" "externs" "range" < $LOGS_DIR/basic_usage.log +CHECK_EXIT_CODE=$? + +print_results 0 $CHECK_EXIT_CODE "usage message" +(( TEST_RESULT += $? )) + + +### quiet switch + +# '--quiet' should mute all output +$CMD_PERF probe --quiet --add vfs_read > $LOGS_DIR/basic_quiet01.log 2> $LOGS_DIR/basic_quiet01.err +PERF_EXIT_CODE=$? +$CMD_PERF probe --quiet --del vfs_read > $LOGS_DIR/basic_quiet03.log 2> $LOGS_DIR/basic_quiet02.err +(( PERF_EXIT_CODE += $? )) + +test "`cat $LOGS_DIR/basic_quiet*log $LOGS_DIR/basic_quiet*err | wc -l`" -eq 0 +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "quiet switch" +(( TEST_RESULT += $? )) + + +# print overall results +print_overall_results "$TEST_RESULT" +exit $? From 83b6815dbb57aca3cd9b20bdcae769d2a7c38d5f Mon Sep 17 00:00:00 2001 From: Veronika Molnarova Date: Tue, 2 Jul 2024 13:08:45 +0200 Subject: [PATCH 226/347] perf testsuite probe: Add test for invalid options Test if various incompatible options are correctly handled-rejected. It is run as a part of perftool-testsuite_probe test case. Signed-off-by: Veronika Molnarova Cc: Athira Rajeev Cc: Ian Rogers Cc: Masami Hiramatsu Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240702110849.31904-8-vmolnaro@redhat.com Signed-off-by: Michael Petlan Signed-off-by: Arnaldo Carvalho de Melo --- .../shell/base_probe/test_invalid_options.sh | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100755 tools/perf/tests/shell/base_probe/test_invalid_options.sh diff --git a/tools/perf/tests/shell/base_probe/test_invalid_options.sh b/tools/perf/tests/shell/base_probe/test_invalid_options.sh new file mode 100755 index 000000000000..1fedfd8b0d0d --- /dev/null +++ b/tools/perf/tests/shell/base_probe/test_invalid_options.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +# SPDX-License-Identifier: GPL-2.0 + +# +# test_invalid_options of perf_probe test +# Author: Masami Hiramatsu +# Author: Michael Petlan +# +# Description: +# +# This test checks whether the invalid and incompatible options are reported +# + +# include working environment +. ../common/init.sh + +TEST_RESULT=0 + +if ! check_kprobes_available; then + print_overall_skipped + exit 0 +fi + + +### missing argument + +# some options require an argument +for opt in '-a' '-d' '-L' '-V'; do + ! $CMD_PERF probe $opt 2> $LOGS_DIR/invalid_options_missing_argument$opt.err + PERF_EXIT_CODE=$? + + ../common/check_all_patterns_found.pl "Error: switch .* requires a value" < $LOGS_DIR/invalid_options_missing_argument$opt.err + CHECK_EXIT_CODE=$? + + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "missing argument for $opt" + (( TEST_RESULT += $? )) +done + + +### unnecessary argument + +# some options may omit the argument +for opt in '-F' '-l'; do + $CMD_PERF probe -F > /dev/null 2> $LOGS_DIR/invalid_options_unnecessary_argument$opt.err + PERF_EXIT_CODE=$? + + test ! -s $LOGS_DIR/invalid_options_unnecessary_argument$opt.err + CHECK_EXIT_CODE=$? + + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "unnecessary argument for $opt" + (( TEST_RESULT += $? )) +done + + +### mutually exclusive options + +# some options are mutually exclusive +test -e $LOGS_DIR/invalid_options_mutually_exclusive.log && rm -f $LOGS_DIR/invalid_options_mutually_exclusive.log +for opt in '-a xxx -d xxx' '-a xxx -L foo' '-a xxx -V foo' '-a xxx -l' '-a xxx -F' \ + '-d xxx -L foo' '-d xxx -V foo' '-d xxx -l' '-d xxx -F' \ + '-L foo -V bar' '-L foo -l' '-L foo -F' '-V foo -l' '-V foo -F' '-l -F'; do + ! $CMD_PERF probe $opt > /dev/null 2> $LOGS_DIR/aux.log + PERF_EXIT_CODE=$? + + ../common/check_all_patterns_found.pl "Error: switch .+ cannot be used with switch .+" < $LOGS_DIR/aux.log + CHECK_EXIT_CODE=$? + + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "mutually exclusive options :: $opt" + (( TEST_RESULT += $? )) + + # gather the logs + cat $LOGS_DIR/aux.log | grep "Error" >> $LOGS_DIR/invalid_options_mutually_exclusive.log +done + + +# print overall results +print_overall_results "$TEST_RESULT" +exit $? From c0964af8162e7ce9a7b8bb896ece92534d5e7e64 Mon Sep 17 00:00:00 2001 From: Veronika Molnarova Date: Tue, 2 Jul 2024 13:08:46 +0200 Subject: [PATCH 227/347] perf testsuite probe: Add test for line semantics The perf-probe command uses a specific semantics to describe probes. Test some patterns that are known to be both valid and invalid if they are handled appropriately. This test is run as a part of perftool-testsuite_probe test case. Signed-off-by: Veronika Molnarova Cc: Athira Rajeev Cc: Ian Rogers Cc: Masami Hiramatsu Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240702110849.31904-9-vmolnaro@redhat.com Signed-off-by: Michael Petlan Signed-off-by: Arnaldo Carvalho de Melo --- .../shell/base_probe/test_line_semantics.sh | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100755 tools/perf/tests/shell/base_probe/test_line_semantics.sh diff --git a/tools/perf/tests/shell/base_probe/test_line_semantics.sh b/tools/perf/tests/shell/base_probe/test_line_semantics.sh new file mode 100755 index 000000000000..d8f4bde0f585 --- /dev/null +++ b/tools/perf/tests/shell/base_probe/test_line_semantics.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# SPDX-License-Identifier: GPL-2.0 + +# +# test_line_semantics of perf_probe test +# Author: Masami Hiramatsu +# Author: Michael Petlan +# +# Description: +# +# This test checks whether the semantic errors of line option's +# arguments are properly reported. +# + +# include working environment +. ../common/init.sh + +TEST_RESULT=0 + +if ! check_kprobes_available; then + print_overall_skipped + exit 0 +fi + + +### acceptable --line descriptions + +# testing acceptance of valid patterns for the '--line' option +VALID_PATTERNS="func func:10 func:0-10 func:2+10 func@source.c func@source.c:1 source.c:1 source.c:1+1 source.c:1-10" +for desc in $VALID_PATTERNS; do + ! ( $CMD_PERF probe --line $desc 2>&1 | grep -q "Semantic error" ) + CHECK_EXIT_CODE=$? + + print_results 0 $CHECK_EXIT_CODE "acceptable descriptions :: $desc" + (( TEST_RESULT += $? )) +done + + +### unacceptable --line descriptions + +# testing handling of invalid patterns for the '--line' option +INVALID_PATTERNS="func:foo func:1-foo func:1+foo func;lazy\*pattern" +for desc in $INVALID_PATTERNS; do + $CMD_PERF probe --line $desc 2>&1 | grep -q "Semantic error" + CHECK_EXIT_CODE=$? + + print_results 0 $CHECK_EXIT_CODE "unacceptable descriptions :: $desc" + (( TEST_RESULT += $? )) +done + + +# print overall results +print_overall_results "$TEST_RESULT" +exit $? From 13d58a6672d11200bd3c35604caa9e166468d61b Mon Sep 17 00:00:00 2001 From: Veronika Molnarova Date: Tue, 2 Jul 2024 13:08:47 +0200 Subject: [PATCH 228/347] perf testsuite: Add common output checking helper As a form of validation, it is a common practice to check the outputs of commands whether they contain expected patterns or match a certain regular expression. This output checking helper is designed to allow checking stderr output of perf commands for unexpected messages, while ignoring messages that are known to be harmless, e.g.: "Lowering default frequency rate to \d+\." "\d+ out of order events recorded." etc. Signed-off-by: Veronika Molnarova Cc: Athira Rajeev Cc: Ian Rogers Cc: Masami Hiramatsu Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240702110849.31904-10-vmolnaro@redhat.com Signed-off-by: Michael Petlan Signed-off-by: Arnaldo Carvalho de Melo --- .../shell/common/check_errors_whitelisted.pl | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100755 tools/perf/tests/shell/common/check_errors_whitelisted.pl diff --git a/tools/perf/tests/shell/common/check_errors_whitelisted.pl b/tools/perf/tests/shell/common/check_errors_whitelisted.pl new file mode 100755 index 000000000000..c57d355dd76e --- /dev/null +++ b/tools/perf/tests/shell/common/check_errors_whitelisted.pl @@ -0,0 +1,51 @@ +#!/usr/bin/perl +# SPDX-License-Identifier: GPL-2.0 + +$whitelist_file = shift; + +if (defined $whitelist_file) +{ + open (INFILE, $whitelist_file) or die "Checker error: Unable to open the whitelist file: $whitelist_file\n"; + @regexps = ; + close INFILE or die "Checker error: Unable to close the whitelist file: $whitelist_file\n"; +} +else +{ + @regexps = (); +} + +$max_printed_lines = 20; +$max_printed_lines = $ENV{TESTLOG_ERR_MSG_MAX_LINES} if (defined $ENV{TESTLOG_ERR_MSG_MAX_LINES}); + +$quiet = 1; +$quiet = 0 if (defined $ENV{TESTLOG_VERBOSITY} && $ENV{TESTLOG_VERBOSITY} ge 2); + +$passed = 1; +$lines_printed = 0; + +while () +{ + s/\n//; + + $line_matched = 0; + for $r (@regexps) + { + chomp $r; + if (/$r/) + { + $line_matched = 1; + last; + } + } + + unless ($line_matched) + { + if ($lines_printed++ < $max_printed_lines) + { + print "Line did not match any pattern: \"$_\"\n" unless $quiet; + } + $passed = 0; + } +} + +exit ($passed == 0); From 61f87151839b9767a06be93b99f1fc324147a0d3 Mon Sep 17 00:00:00 2001 From: Veronika Molnarova Date: Tue, 2 Jul 2024 13:08:48 +0200 Subject: [PATCH 229/347] perf testsuite report: Add test for perf-report basic functionality Test basic execution and some options of perf-report subcommand, like show-nr-samples, header, showcpuutilization, pid and symbol filtering. Signed-off-by: Veronika Molnarova Cc: Athira Rajeev Cc: Ian Rogers Cc: Masami Hiramatsu Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240702110849.31904-11-vmolnaro@redhat.com Signed-off-by: Michael Petlan Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/base_report/setup.sh | 32 +++ .../shell/base_report/stderr-whitelist.txt | 5 + .../tests/shell/base_report/test_basic.sh | 190 ++++++++++++++++++ tools/perf/tests/shell/common/settings.sh | 2 + 4 files changed, 229 insertions(+) create mode 100755 tools/perf/tests/shell/base_report/setup.sh create mode 100644 tools/perf/tests/shell/base_report/stderr-whitelist.txt create mode 100755 tools/perf/tests/shell/base_report/test_basic.sh diff --git a/tools/perf/tests/shell/base_report/setup.sh b/tools/perf/tests/shell/base_report/setup.sh new file mode 100755 index 000000000000..4caa496660c6 --- /dev/null +++ b/tools/perf/tests/shell/base_report/setup.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# SPDX-License-Identifier: GPL-2.0 + +# +# setup.sh of perf report test +# Author: Michael Petlan +# +# Description: +# +# We need some sample data for perf-report testing +# +# + +# include working environment +. ../common/init.sh + +test -d "$HEADER_TAR_DIR" || mkdir -p "$HEADER_TAR_DIR" + +SW_EVENT="cpu-clock" + +$CMD_PERF record -asdg -e $SW_EVENT -o $CURRENT_TEST_DIR/perf.data -- $CMD_LONGER_SLEEP 2> $LOGS_DIR/setup.log +PERF_EXIT_CODE=$? + +../common/check_all_patterns_found.pl "$RE_LINE_RECORD1" "$RE_LINE_RECORD2" < $LOGS_DIR/setup.log +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "prepare the perf.data file" +TEST_RESULT=$? + +print_overall_results $TEST_RESULT +exit $? diff --git a/tools/perf/tests/shell/base_report/stderr-whitelist.txt b/tools/perf/tests/shell/base_report/stderr-whitelist.txt new file mode 100644 index 000000000000..e3341401b47c --- /dev/null +++ b/tools/perf/tests/shell/base_report/stderr-whitelist.txt @@ -0,0 +1,5 @@ +no symbols found in .*, maybe install a debug package +was updated .*is prelink enabled.+ Restart the long running apps that use it +Warning: +\d+ out of order events recorded. +detected invalid bpf_prog_info diff --git a/tools/perf/tests/shell/base_report/test_basic.sh b/tools/perf/tests/shell/base_report/test_basic.sh new file mode 100755 index 000000000000..47677cbd4df3 --- /dev/null +++ b/tools/perf/tests/shell/base_report/test_basic.sh @@ -0,0 +1,190 @@ +#!/bin/bash + +# SPDX-License-Identifier: GPL-2.0 + +# +# test_basic of perf_report test +# Author: Michael Petlan +# +# Description: +# +# This test tests basic functionality of perf report command. +# +# + +# include working environment +. ../common/init.sh + +TEST_RESULT=0 + + +### help message + +if [ "$PARAM_GENERAL_HELP_TEXT_CHECK" = "y" ]; then + # test that a help message is shown and looks reasonable + $CMD_PERF report --help > $LOGS_DIR/basic_helpmsg.log 2> $LOGS_DIR/basic_helpmsg.err + PERF_EXIT_CODE=$? + + ../common/check_all_patterns_found.pl "PERF-REPORT" "NAME" "SYNOPSIS" "DESCRIPTION" "OPTIONS" "OVERHEAD\s+CALCULATION" "SEE ALSO" < $LOGS_DIR/basic_helpmsg.log + CHECK_EXIT_CODE=$? + ../common/check_all_patterns_found.pl "input" "verbose" "show-nr-samples" "show-cpu-utilization" "threads" "comms" "pid" "tid" "dsos" "symbols" "symbol-filter" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_all_patterns_found.pl "hide-unresolved" "sort" "fields" "parent" "exclude-other" "column-widths" "field-separator" "dump-raw-trace" "children" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_all_patterns_found.pl "call-graph" "max-stack" "inverted" "ignore-callees" "pretty" "stdio" "tui" "gtk" "vmlinux" "kallsyms" "modules" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_all_patterns_found.pl "force" "symfs" "cpu" "disassembler-style" "source" "asm-raw" "show-total-period" "show-info" "branch-stack" "group" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_all_patterns_found.pl "branch-history" "objdump" "demangle" "percent-limit" "percentage" "header" "itrace" "full-source-path" "show-ref-call-graph" < $LOGS_DIR/basic_helpmsg.log + (( CHECK_EXIT_CODE += $? )) + ../common/check_no_patterns_found.pl "No manual entry for" < $LOGS_DIR/basic_helpmsg.err + (( CHECK_EXIT_CODE += $? )) + + print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "help message" + (( TEST_RESULT += $? )) +else + print_testcase_skipped "help message" +fi + + +### basic execution + +# test that perf report is even working +$CMD_PERF report -i $CURRENT_TEST_DIR/perf.data --stdio > $LOGS_DIR/basic_basic.log 2> $LOGS_DIR/basic_basic.err +PERF_EXIT_CODE=$? + +REGEX_LOST_SAMPLES_INFO="#\s*Total Lost Samples:\s+$RE_NUMBER" +REGEX_SAMPLES_INFO="#\s*Samples:\s+(?:$RE_NUMBER)\w?\s+of\s+event\s+'$RE_EVENT_ANY'" +REGEX_LINES_HEADER="#\s*Children\s+Self\s+Command\s+Shared Object\s+Symbol" +REGEX_LINES="\s*$RE_NUMBER%\s+$RE_NUMBER%\s+\S+\s+\[kernel\.(?:vmlinux)|(?:kallsyms)\]\s+\[[k\.]\]\s+\w+" +../common/check_all_patterns_found.pl "$REGEX_LOST_SAMPLES_INFO" "$REGEX_SAMPLES_INFO" "$REGEX_LINES_HEADER" "$REGEX_LINES" < $LOGS_DIR/basic_basic.log +CHECK_EXIT_CODE=$? +../common/check_errors_whitelisted.pl "stderr-whitelist.txt" < $LOGS_DIR/basic_basic.err +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "basic execution" +(( TEST_RESULT += $? )) + + +### number of samples + +# '--show-nr-samples' should show number of samples for each symbol +$CMD_PERF report --stdio -i $CURRENT_TEST_DIR/perf.data --show-nr-samples > $LOGS_DIR/basic_nrsamples.log 2> $LOGS_DIR/basic_nrsamples.err +PERF_EXIT_CODE=$? + +REGEX_LINES_HEADER="#\s*Children\s+Self\s+Samples\s+Command\s+Shared Object\s+Symbol" +REGEX_LINES="\s*$RE_NUMBER%\s+$RE_NUMBER%\s+$RE_NUMBER\s+\S+\s+\[kernel\.(?:vmlinux)|(?:kallsyms)\]\s+\[[k\.]\]\s+\w+" +../common/check_all_patterns_found.pl "$REGEX_LINES_HEADER" "$REGEX_LINES" < $LOGS_DIR/basic_nrsamples.log +CHECK_EXIT_CODE=$? +../common/check_errors_whitelisted.pl "stderr-whitelist.txt" < $LOGS_DIR/basic_nrsamples.err +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "number of samples" +(( TEST_RESULT += $? )) + + +### header + +# '--header' and '--header-only' should show perf report header +$CMD_PERF report -i $CURRENT_TEST_DIR/perf.data --stdio --header-only > $LOGS_DIR/basic_header.log +PERF_EXIT_CODE=$? + +REGEX_LINE_TIMESTAMP="#\s+captured on\s*:\s*$RE_DATE_TIME" +REGEX_LINE_HOSTNAME="#\s+hostname\s*:\s*$MY_HOSTNAME" +REGEX_LINE_KERNEL="#\s+os release\s*:\s*${MY_KERNEL_VERSION//+/\\+}" +REGEX_LINE_PERF="#\s+perf version\s*:\s*" +REGEX_LINE_ARCH="#\s+arch\s*:\s*$MY_ARCH" +REGEX_LINE_CPUS_ONLINE="#\s+nrcpus online\s*:\s*$MY_CPUS_ONLINE" +REGEX_LINE_CPUS_AVAIL="#\s+nrcpus avail\s*:\s*$MY_CPUS_AVAILABLE" +# disable precise check for "nrcpus avail" in BASIC runmode +test $PERFTOOL_TESTSUITE_RUNMODE -lt $RUNMODE_STANDARD && REGEX_LINE_CPUS_AVAIL="#\s+nrcpus avail\s*:\s*$RE_NUMBER" +../common/check_all_patterns_found.pl "$REGEX_LINE_TIMESTAMP" "$REGEX_LINE_HOSTNAME" "$REGEX_LINE_KERNEL" "$REGEX_LINE_PERF" "$REGEX_LINE_ARCH" "$REGEX_LINE_CPUS_ONLINE" "$REGEX_LINE_CPUS_AVAIL" < $LOGS_DIR/basic_header.log +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "header" +(( TEST_RESULT += $? )) + +# '--header' and '--header-only' should use creation time +OLD_TIMESTAMP=`$CMD_PERF report --stdio --header-only -i $CURRENT_TEST_DIR/perf.data | grep "captured on"` +PERF_EXIT_CODE=$? + +( tar -C $CURRENT_TEST_DIR -c perf.data | xz > $CURRENT_TEST_DIR/perf.data.tar.xz ; xzcat $CURRENT_TEST_DIR/perf.data.tar.xz | tar x -C $HEADER_TAR_DIR ) +(( PERF_EXIT_CODE += $? )) + +NEW_TIMESTAMP=`$CMD_PERF report --stdio --header-only -i $HEADER_TAR_DIR/perf.data | grep "captured on"` +(( PERF_EXIT_CODE += $? )) + +test "$OLD_TIMESTAMP" = "$NEW_TIMESTAMP" +CHECK_EXIT_CODE=$? + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "header timestamp" +(( TEST_RESULT += $? )) + + +### show CPU utilization + +# '--showcpuutilization' should show percentage for both system and userspace mode +$CMD_PERF report -i $CURRENT_TEST_DIR/perf.data --stdio --showcpuutilization > $LOGS_DIR/basic_cpuut.log 2> $LOGS_DIR/basic_cpuut.err +PERF_EXIT_CODE=$? + +REGEX_LINES_HEADER="#\s*Children\s+Self\s+sys\s+usr\s+Command\s+Shared Object\s+Symbol" +REGEX_LINES="\s*$RE_NUMBER%\s+$RE_NUMBER%\s+$RE_NUMBER%\s+$RE_NUMBER%\s+\S+\s+\[kernel\.(?:vmlinux)|(?:kallsyms)\]\s+\[[k\.]\]\s+\w+" +../common/check_all_patterns_found.pl "$REGEX_LINES_HEADER" "$REGEX_LINES" < $LOGS_DIR/basic_cpuut.log +CHECK_EXIT_CODE=$? +../common/check_errors_whitelisted.pl "stderr-whitelist.txt" < $LOGS_DIR/basic_cpuut.err +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "show CPU utilization" +(( TEST_RESULT += $? )) + + +### pid + +# '--pid=' should limit the output for a process with the given pid only +$CMD_PERF report --stdio -i $CURRENT_TEST_DIR/perf.data --pid=1 > $LOGS_DIR/basic_pid.log 2> $LOGS_DIR/basic_pid.err +PERF_EXIT_CODE=$? + +grep -P -v '^#' $LOGS_DIR/basic_pid.log | grep -P '\s+[\d\.]+%' | ../common/check_all_lines_matched.pl "systemd|init" +CHECK_EXIT_CODE=$? +../common/check_errors_whitelisted.pl "stderr-whitelist.txt" < $LOGS_DIR/basic_pid.err +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "pid" +(( TEST_RESULT += $? )) + + +### non-existing symbol + +# '--symbols' should show only the given symbols +$CMD_PERF report --stdio -i $CURRENT_TEST_DIR/perf.data --symbols=dummynonexistingsymbol > $LOGS_DIR/basic_symbols.log 2> $LOGS_DIR/basic_symbols.err +PERF_EXIT_CODE=$? + +../common/check_all_lines_matched.pl "$RE_LINE_EMPTY" "$RE_LINE_COMMENT" < $LOGS_DIR/basic_symbols.log +CHECK_EXIT_CODE=$? +../common/check_errors_whitelisted.pl "stderr-whitelist.txt" < $LOGS_DIR/basic_symbols.err +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "non-existing symbol" +(( TEST_RESULT += $? )) + + +### symbol filter + +# '--symbol-filter' should filter symbols based on substrings +$CMD_PERF report --stdio -i $CURRENT_TEST_DIR/perf.data --symbol-filter=map > $LOGS_DIR/basic_symbolfilter.log 2> $LOGS_DIR/basic_symbolfilter.err +PERF_EXIT_CODE=$? + +grep -P -v '^#' $LOGS_DIR/basic_symbolfilter.log | grep -P '\s+[\d\.]+%' | ../common/check_all_lines_matched.pl "\[[k\.]\]\s+.*map" +CHECK_EXIT_CODE=$? +../common/check_errors_whitelisted.pl "stderr-whitelist.txt" < $LOGS_DIR/basic_symbolfilter.err +(( CHECK_EXIT_CODE += $? )) + +print_results $PERF_EXIT_CODE $CHECK_EXIT_CODE "symbol filter" +(( TEST_RESULT += $? )) + + +# TODO: $CMD_PERF report -n --showcpuutilization -TUxDg 2> 01.log + +# print overall results +print_overall_results "$TEST_RESULT" +exit $? diff --git a/tools/perf/tests/shell/common/settings.sh b/tools/perf/tests/shell/common/settings.sh index 2e50dd0bff9b..cba1b338f96f 100644 --- a/tools/perf/tests/shell/common/settings.sh +++ b/tools/perf/tests/shell/common/settings.sh @@ -80,12 +80,14 @@ if [ -n "$PERFSUITE_RUN_DIR" ]; then export CURRENT_TEST_DIR="$PERFSUITE_RUN_DIR/$TEST_NAME" export MAKE_TARGET_DIR="$CURRENT_TEST_DIR/examples" export LOGS_DIR="$CURRENT_TEST_DIR/logs" + export HEADER_TAR_DIR="$CURRENT_TEST_DIR/header_tar" test -d "$CURRENT_TEST_DIR" || mkdir -p "$CURRENT_TEST_DIR" test -d "$LOGS_DIR" || mkdir -p "$LOGS_DIR" else # when $PERFSUITE_RUN_DIR is not set, logs will be placed here export CURRENT_TEST_DIR="." export LOGS_DIR="." + export HEADER_TAR_DIR="./header_tar" fi From e37cb2a6beed817df510832931bfdaa6bb8a0447 Mon Sep 17 00:00:00 2001 From: Veronika Molnarova Date: Tue, 2 Jul 2024 13:08:49 +0200 Subject: [PATCH 230/347] perf testsuite report: Add test case for perf report Add a new 'perf report' test case that acts as an entry element in 'perf test list'. Runs multiple subtests from directory "base_report", which can be expanded without further editing. Signed-off-by: Veronika Molnarova Cc: Athira Rajeev Cc: Ian Rogers Cc: Masami Hiramatsu Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240702110849.31904-12-vmolnaro@redhat.com Signed-off-by: Michael Petlan Signed-off-by: Arnaldo Carvalho de Melo --- .../tests/shell/perftool-testsuite_report.sh | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100755 tools/perf/tests/shell/perftool-testsuite_report.sh diff --git a/tools/perf/tests/shell/perftool-testsuite_report.sh b/tools/perf/tests/shell/perftool-testsuite_report.sh new file mode 100755 index 000000000000..973012ce92a7 --- /dev/null +++ b/tools/perf/tests/shell/perftool-testsuite_report.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# perftool-testsuite_report +# SPDX-License-Identifier: GPL-2.0 + +test -d "$(dirname "$0")/base_report" || exit 2 +cd "$(dirname "$0")/base_report" || exit 2 +status=0 + +PERFSUITE_RUN_DIR=$(mktemp -d /tmp/"$(basename "$0" .sh)".XXX) +export PERFSUITE_RUN_DIR + +for testcase in setup.sh test_*; do # skip setup.sh if not present or not executable + test -x "$testcase" || continue + ./"$testcase" + (( status += $? )) +done + +if ! [ "$PERFTEST_KEEP_LOGS" = "y" ]; then + rm -rf "$PERFSUITE_RUN_DIR" +fi + +test $status -ne 0 && exit 1 +exit 0 From 097fe67df1aa9cc79092b15c68c9135569a679d6 Mon Sep 17 00:00:00 2001 From: Michael Petlan Date: Tue, 2 Jul 2024 13:08:50 +0200 Subject: [PATCH 231/347] perf testsuite: Install perf-report tests in the 'make install-tests -C tools/perf' target Signed-off-by: Michael Petlan Cc: Athira Rajeev Cc: Ian Rogers Cc: Masami Hiramatsu Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240702110849.31904-13-vmolnaro@redhat.com Signed-off-by: Veronika Molnarova Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.perf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index 9731f5e84131..9dd2e8d3f3c9 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf @@ -1142,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 From 8b48f8ba16b06024920c8ff349fed769118777cc Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 27 Aug 2024 14:27:57 -0700 Subject: [PATCH 232/347] perf report: Name events in stats for pipe mode In stats mode PERF_RECORD_EVENT_UPDATE isn't being handled meaning the evsels aren't named when handling pipe mode output. Before: $ perf record -e inst_retired.any -a -o - sleep 0.1|perf report --stats -i - ... Aggregated stats: TOTAL events: 23358 COMM events: 2608 (11.2%) EXIT events: 1 ( 0.0%) FORK events: 2607 (11.2%) SAMPLE events: 174 ( 0.7%) MMAP2 events: 17936 (76.8%) ATTR events: 2 ( 0.0%) FINISHED_ROUND events: 2 ( 0.0%) ID_INDEX events: 1 ( 0.0%) THREAD_MAP events: 1 ( 0.0%) CPU_MAP events: 1 ( 0.0%) EVENT_UPDATE events: 3 ( 0.0%) TIME_CONV events: 1 ( 0.0%) FEATURE events: 20 ( 0.1%) FINISHED_INIT events: 1 ( 0.0%) raw 0xc0 stats: SAMPLE events: 174 After: $ perf record -e inst_retired.any -a -o - sleep 0.1|perf report --stats -i - ... Aggregated stats: TOTAL events: 23742 COMM events: 2620 (11.0%) EXIT events: 2 ( 0.0%) FORK events: 2619 (11.0%) SAMPLE events: 165 ( 0.7%) MMAP2 events: 18304 (77.1%) ATTR events: 2 ( 0.0%) FINISHED_ROUND events: 2 ( 0.0%) ID_INDEX events: 1 ( 0.0%) THREAD_MAP events: 1 ( 0.0%) CPU_MAP events: 1 ( 0.0%) EVENT_UPDATE events: 3 ( 0.0%) TIME_CONV events: 1 ( 0.0%) FEATURE events: 20 ( 0.1%) FINISHED_INIT events: 1 ( 0.0%) inst_retired.any stats: SAMPLE events: 165 This makes the pipe output match the regular output. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240827212757.1469340-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-report.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c index c304d9b06190..5dc17ffee27a 100644 --- a/tools/perf/builtin-report.c +++ b/tools/perf/builtin-report.c @@ -816,6 +816,7 @@ static void stats_setup(struct report *rep) 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; } From 4451dae46992131ef5c42147444c183c364b1149 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Thu, 15 Aug 2024 09:36:17 +0800 Subject: [PATCH 233/347] perf evlist: Introduce method to find if there is a bpf-output event We'll use it in the next patch, to deciding how to set up the ring buffer. Signed-off-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240815013626.935097-2-howardchu95@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 12 ++++++++++++ tools/perf/util/evlist.h | 1 + 2 files changed, 13 insertions(+) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index c0379fa1ccfe..ef58a7764318 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -2627,3 +2627,15 @@ void evlist__uniquify_name(struct evlist *evlist) } } } + +bool evlist__has_bpf_output(struct evlist *evlist) +{ + struct evsel *evsel; + + evlist__for_each_entry(evlist, evsel) { + if (evsel__is_bpf_output(evsel)) + return true; + } + + return false; +} diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index b46f1a320783..bcc1c6984bb5 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -447,5 +447,6 @@ int evlist__scnprintf_evsels(struct evlist *evlist, size_t size, char *bf); void evlist__check_mem_load_aux(struct evlist *evlist); void evlist__warn_user_requested_cpus(struct evlist *evlist, const char *cpu_list); void evlist__uniquify_name(struct evlist *evlist); +bool evlist__has_bpf_output(struct evlist *evlist); #endif /* __PERF_EVLIST_H */ From 8df1d8c6cbd6825b3784068e7c2b37fa8a8a43f0 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Thu, 15 Aug 2024 09:36:17 +0800 Subject: [PATCH 234/347] perf trace: Fix perf trace -p 'perf trace -p ' work on a syscall that is unaugmented, but doesn't work on a syscall that's augmented (when it calls perf_event_output() in BPF). Let's take open() as an example. open() is augmented in perf trace. Before: $ perf trace -e open -p 3792392 ? ( ): ... [continued]: open()) = -1 ENOENT (No such file or directory) ? ( ): ... [continued]: open()) = -1 ENOENT (No such file or directory) We can see there's no output. After: $ perf trace -e open -p 3792392 0.000 ( 0.123 ms): a.out/3792392 open(filename: "DINGZHEN", flags: WRONLY) = -1 ENOENT (No such file or directory) 1000.398 ( 0.116 ms): a.out/3792392 open(filename: "DINGZHEN", flags: WRONLY) = -1 ENOENT (No such file or directory) Reason: bpf_perf_event_output() will fail when you specify a pid in 'perf trace' (EOPNOTSUPP). When using 'perf trace -p 114', before perf_event_open(), we'll have PID = 114, and CPU = -1. This is bad for bpf-output event, because the ring buffer won't accept output from BPF's perf_event_output(), making it fail. I'm still trying to find out why. If we open bpf-output for every cpu, instead of setting it to -1, like this: PID = , CPU = 0 PID = , CPU = 1 PID = , CPU = 2 PID = , CPU = 3 Everything works. You can test it with this script (open.c): #include #include int main() { int i1 = 1, i2 = 2, i3 = 3, i4 = 4; char s1[] = "DINGZHEN", s2[] = "XUEBAO"; while (1) { syscall(SYS_open, s1, i1, i2); sleep(1); } return 0; } save, compile: make open perf trace: perf trace -e open Signed-off-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240815013626.935097-2-howardchu95@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/evlist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index ef58a7764318..f14b7e6ff1dc 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -1067,7 +1067,7 @@ int evlist__create_maps(struct evlist *evlist, struct target *target) if (!threads) return -1; - if (target__uses_dummy_map(target)) + if (target__uses_dummy_map(target) && !evlist__has_bpf_output(evlist)) cpus = perf_cpu_map__new_any_cpu(); else cpus = perf_cpu_map__new(target->cpu_list); From 7bedcbaefdf5d4f71302cb5428eb72ada4f380e9 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 22 Aug 2024 15:10:27 -0300 Subject: [PATCH 235/347] perf trace: Pass the richer 'struct syscall_arg' pointer to trace__btf_scnprintf() Since we'll need it later in the current patch series and we can get the syscall_arg_fmt from syscall_arg->fmt. Based-on-a-patch-by: Howard Chu Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/Zsd8vqCrTh5h69rp@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index d6ca541fdc78..53690bbb143e 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -989,9 +989,11 @@ static size_t btf_enum_scnprintf(const struct btf_type *type, struct btf *btf, c return 0; } -static size_t trace__btf_scnprintf(struct trace *trace, struct syscall_arg_fmt *arg_fmt, char *bf, +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; @@ -1011,7 +1013,7 @@ static size_t trace__btf_scnprintf(struct trace *trace, struct syscall_arg_fmt * } #else // HAVE_LIBBPF_SUPPORT -static size_t trace__btf_scnprintf(struct trace *trace __maybe_unused, struct syscall_arg_fmt *arg_fmt __maybe_unused, +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) { @@ -2260,7 +2262,7 @@ 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); - btf_printed = trace__btf_scnprintf(trace, &sc->arg_fmt[arg.idx], bf + printed, + btf_printed = trace__btf_scnprintf(trace, &arg, bf + printed, size - printed, val, field->type); if (btf_printed) { printed += btf_printed; @@ -2963,7 +2965,7 @@ 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, arg, bf + printed, size - printed, val, field->type); + btf_printed = trace__btf_scnprintf(trace, &syscall_arg, bf + printed, size - printed, val, field->type); if (btf_printed) { printed += btf_printed; continue; From c5d50457a8fc2695ca5a921f3a4a402343cf5313 Mon Sep 17 00:00:00 2001 From: Kajol Jain Date: Tue, 27 Aug 2024 11:02:04 +0530 Subject: [PATCH 236/347] perf vendor events power10: Update JSON/events Update JSON/events for power10 platform with additional events. Reviewed-by: Ian Rogers Signed-off-by: Kajol Jain Cc: Athira Rajeev Cc: Disha Goel Cc: Hari Bathini Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20240827053206.538814-1-kjain@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- .../arch/powerpc/power10/datasource.json | 25 +++++++++++++++++++ .../arch/powerpc/power10/frontend.json | 10 ++++++++ .../pmu-events/arch/powerpc/power10/pmc.json | 5 ++++ 3 files changed, 40 insertions(+) diff --git a/tools/perf/pmu-events/arch/powerpc/power10/datasource.json b/tools/perf/pmu-events/arch/powerpc/power10/datasource.json index 0eeaaf1a95b8..283284745d9c 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/datasource.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/datasource.json @@ -14,6 +14,31 @@ "EventName": "PM_DATA_FROM_MEMORY", "BriefDescription": "The processor's data cache was reloaded from local, remote, or distant memory due to a demand miss." }, + { + "EventCode": "0x0000004080", + "EventName": "PM_INST_FROM_L1", + "BriefDescription": "An instruction fetch hit in the L1. Each fetch group contains 8 instructions. The same line can hit 4 times if 32 sequential instructions are fetched." + }, + { + "EventCode": "0x000000026080", + "EventName": "PM_L2_LD_MISS", + "BriefDescription": "All successful D-Side Load dispatches for this thread that missed in the L2. Since the event happens in a 2:1 clock domain and is time-sliced across all 4 threads, the event count should be multiplied by 2." + }, + { + "EventCode": "0x000000026880", + "EventName": "PM_L2_ST_MISS", + "BriefDescription": "All successful D-Side Store dispatches for this thread that missed in the L2. Since the event happens in a 2:1 clock domain and is time-sliced across all 4 threads, the event count should be multiplied by 2." + }, + { + "EventCode": "0x010000046880", + "EventName": "PM_L2_ST_HIT", + "BriefDescription": "All successful D-side store dispatches for this thread that were L2 hits. Since the event happens in a 2:1 clock domain and is time-sliced across all 4 threads, the event count should be multiplied by 2." + }, + { + "EventCode": "0x000000036880", + "EventName": "PM_L2_INST_MISS", + "BriefDescription": "All successful instruction (demand and prefetch) dispatches for this thread that missed in the L2. Since the event happens in a 2:1 clock domain and is time-sliced across all 4 threads, the event count should be multiplied by 2." + }, { "EventCode": "0x000300000000C040", "EventName": "PM_INST_FROM_L2", diff --git a/tools/perf/pmu-events/arch/powerpc/power10/frontend.json b/tools/perf/pmu-events/arch/powerpc/power10/frontend.json index 53660c279286..456971f60814 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/frontend.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/frontend.json @@ -93,5 +93,15 @@ "EventCode": "0x400FC", "EventName": "PM_ITLB_MISS", "BriefDescription": "Instruction TLB reload (after a miss), all page sizes. Includes only demand misses." + }, + { + "EventCode": "0x00000040B8", + "EventName": "PM_PRED_BR_TKN_COND_DIR", + "BriefDescription": "A conditional branch finished with correctly predicted direction. Resolved taken." + }, + { + "EventCode": "0x00000048B8", + "EventName": "PM_PRED_BR_NTKN_COND_DIR", + "BriefDescription": "A conditional branch finished with correctly predicted direction. Resolved not taken." } ] diff --git a/tools/perf/pmu-events/arch/powerpc/power10/pmc.json b/tools/perf/pmu-events/arch/powerpc/power10/pmc.json index 0e0253d0e757..04732698d9b2 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/pmc.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/pmc.json @@ -104,6 +104,11 @@ "EventName": "PM_RUN_CYC", "BriefDescription": "Processor cycles gated by the run latch." }, + { + "EventCode": "0x200F8", + "EventName": "PM_EXT_INT", + "BriefDescription": "Cycles an external interrupt was active." + }, { "EventCode": "0x30010", "EventName": "PM_PMC2_OVERFLOW", From 0edee819712ed00ce199f94108a8d588970eea80 Mon Sep 17 00:00:00 2001 From: Kajol Jain Date: Tue, 27 Aug 2024 11:02:05 +0530 Subject: [PATCH 237/347] perf vendor events power10: Move the JSON/events Move some of the JSON/events from others.json to more appropriate JSON files for power10 platform. Reviewed-by: Ian Rogers Signed-off-by: Kajol Jain Cc: Athira Rajeev Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20240827053206.538814-2-kjain@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- .../arch/powerpc/power10/cache.json | 15 ++ .../arch/powerpc/power10/datasource.json | 15 ++ .../arch/powerpc/power10/frontend.json | 10 ++ .../arch/powerpc/power10/locks.json | 10 ++ .../arch/powerpc/power10/memory.json | 30 ++++ .../arch/powerpc/power10/others.json | 130 ------------------ .../arch/powerpc/power10/pipeline.json | 45 ++++++ .../pmu-events/arch/powerpc/power10/pmc.json | 5 + 8 files changed, 130 insertions(+), 130 deletions(-) diff --git a/tools/perf/pmu-events/arch/powerpc/power10/cache.json b/tools/perf/pmu-events/arch/powerpc/power10/cache.json index 839ae26945fb..9814a59fce31 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/cache.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/cache.json @@ -1,4 +1,19 @@ [ + { + "EventCode": "0x1002C", + "EventName": "PM_LD_PREFETCH_CACHE_LINE_MISS", + "BriefDescription": "The L1 cache was reloaded with a line that fulfills a prefetch request." + }, + { + "EventCode": "0x200FD", + "EventName": "PM_L1_ICACHE_MISS", + "BriefDescription": "Demand instruction cache miss." + }, + { + "EventCode": "0x30068", + "EventName": "PM_L1_ICACHE_RELOADED_PREF", + "BriefDescription": "Counts all instruction cache prefetch reloads (includes demand turned into prefetch)." + }, { "EventCode": "0x300F4", "EventName": "PM_RUN_INST_CMPL_CONC", diff --git a/tools/perf/pmu-events/arch/powerpc/power10/datasource.json b/tools/perf/pmu-events/arch/powerpc/power10/datasource.json index 283284745d9c..a5d5be35b5e6 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/datasource.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/datasource.json @@ -1,4 +1,14 @@ [ + { + "EventCode": "0x1505E", + "EventName": "PM_LD_HIT_L1", + "BriefDescription": "Load finished without experiencing an L1 miss." + }, + { + "EventCode": "0x100FC", + "EventName": "PM_LD_REF_L1", + "BriefDescription": "All L1 D cache load references counted at finish, gated by reject. In P9 and earlier this event counted only cacheable loads but in P10 both cacheable and non-cacheable loads are included." + }, { "EventCode": "0x200FE", "EventName": "PM_DATA_FROM_L2MISS", @@ -9,6 +19,11 @@ "EventName": "PM_DATA_FROM_L3MISS", "BriefDescription": "The processor's L1 data cache was reloaded from beyond the local core's L3 due to a demand miss." }, + { + "EventCode": "0x400F0", + "EventName": "PM_LD_DEMAND_MISS_L1_FIN", + "BriefDescription": "Load missed L1, counted at finish time." + }, { "EventCode": "0x400FE", "EventName": "PM_DATA_FROM_MEMORY", diff --git a/tools/perf/pmu-events/arch/powerpc/power10/frontend.json b/tools/perf/pmu-events/arch/powerpc/power10/frontend.json index 456971f60814..684374fe5699 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/frontend.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/frontend.json @@ -84,6 +84,11 @@ "EventName": "PM_VECTOR_ST_CMPL", "BriefDescription": "Vector store instruction completed." }, + { + "EventCode": "0x4D05E", + "EventName": "PM_BR_CMPL", + "BriefDescription": "A branch completed. All branches are included." + }, { "EventCode": "0x4E054", "EventName": "PM_DTLB_HIT_1G", @@ -94,6 +99,11 @@ "EventName": "PM_ITLB_MISS", "BriefDescription": "Instruction TLB reload (after a miss), all page sizes. Includes only demand misses." }, + { + "EventCode": "0x00000048B4", + "EventName": "PM_BR_TKN_UNCOND_FIN", + "BriefDescription": "An unconditional branch finished. All unconditional branches are taken." + }, { "EventCode": "0x00000040B8", "EventName": "PM_PRED_BR_TKN_COND_DIR", diff --git a/tools/perf/pmu-events/arch/powerpc/power10/locks.json b/tools/perf/pmu-events/arch/powerpc/power10/locks.json index b5a0d6521963..a8ea4d0def1a 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/locks.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/locks.json @@ -4,9 +4,19 @@ "EventName": "PM_STCX_FAIL_FIN", "BriefDescription": "Conditional store instruction (STCX) failed. LARX and STCX are instructions used to acquire a lock." }, + { + "EventCode": "0x2E014", + "EventName": "PM_STCX_FIN", + "BriefDescription": "Conditional store instruction (STCX) finished. LARX and STCX are instructions used to acquire a lock." + }, { "EventCode": "0x4E050", "EventName": "PM_STCX_PASS_FIN", "BriefDescription": "Conditional store instruction (STCX) passed. LARX and STCX are instructions used to acquire a lock." + }, + { + "EventCode": "0x000000C8B8", + "EventName": "PM_STCX_SUCCESS_CMPL", + "BriefDescription": "STCX instructions that completed successfully. Specifically, counts only when a pass status is returned from the nest." } ] diff --git a/tools/perf/pmu-events/arch/powerpc/power10/memory.json b/tools/perf/pmu-events/arch/powerpc/power10/memory.json index 885262957beb..0d7191b3f2c6 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/memory.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/memory.json @@ -69,6 +69,11 @@ "EventName": "PM_XFER_FROM_SRC_PMC3", "BriefDescription": "The processor's L1 data cache was reloaded from the source specified in MMCR3[30:42]. If MMCR1[16|17] is 0 (default), this count includes only lines that were reloaded to satisfy a demand miss. If MMCR1[16|17] is 1, this count includes both demand misses and prefetch reloads." }, + { + "EventCode": "0x3F04A", + "EventName": "PM_LSU_ST5_FIN", + "BriefDescription": "LSU Finished an internal operation in ST2 port." + }, { "EventCode": "0x3C054", "EventName": "PM_DERAT_MISS_16M", @@ -108,5 +113,30 @@ "EventCode": "0x4C05A", "EventName": "PM_DTLB_MISS_1G", "BriefDescription": "Data TLB reload (after a miss) page size 1G. Implies radix translation was used. When MMCR1[16]=0 this event counts only for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." + }, + { + "EventCode": "0x000000F880", + "EventName": "PM_SNOOP_TLBIE_CYC", + "BriefDescription": "Cycles in which TLBIE snoops are executed in the LSU." + }, + { + "EventCode": "0x000000F084", + "EventName": "PM_SNOOP_TLBIE_CACHE_WALK_CYC", + "BriefDescription": "TLBIE snoop cycles in which the data cache is being walked." + }, + { + "EventCode": "0x000000F884", + "EventName": "PM_SNOOP_TLBIE_WAIT_ST_CYC", + "BriefDescription": "TLBIE snoop cycles in which older stores are still draining." + }, + { + "EventCode": "0x000000F088", + "EventName": "PM_SNOOP_TLBIE_WAIT_LD_CYC", + "BriefDescription": "TLBIE snoop cycles in which older loads are still draining." + }, + { + "EventCode": "0x000000F08C", + "EventName": "PM_SNOOP_TLBIE_WAIT_MMU_CYC", + "BriefDescription": "TLBIE snoop cycles in which the Load-Store unit is waiting for the MMU to finish invalidation." } ] diff --git a/tools/perf/pmu-events/arch/powerpc/power10/others.json b/tools/perf/pmu-events/arch/powerpc/power10/others.json index 3789304cb363..1bf802076ee0 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/others.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/others.json @@ -1,89 +1,19 @@ [ - { - "EventCode": "0x1002C", - "EventName": "PM_LD_PREFETCH_CACHE_LINE_MISS", - "BriefDescription": "The L1 cache was reloaded with a line that fulfills a prefetch request." - }, - { - "EventCode": "0x1505E", - "EventName": "PM_LD_HIT_L1", - "BriefDescription": "Load finished without experiencing an L1 miss." - }, - { - "EventCode": "0x1F056", - "EventName": "PM_DISP_SS0_2_INSTR_CYC", - "BriefDescription": "Cycles in which Superslice 0 dispatches either 1 or 2 instructions." - }, - { - "EventCode": "0x1F05A", - "EventName": "PM_DISP_HELD_SYNC_CYC", - "BriefDescription": "Cycles dispatch is held because of a synchronizing instruction that requires the ICT to be empty before dispatch." - }, { "EventCode": "0x10066", "EventName": "PM_ADJUNCT_CYC", "BriefDescription": "Cycles in which the thread is in Adjunct state. MSR[S HV PR] bits = 011." }, - { - "EventCode": "0x100FC", - "EventName": "PM_LD_REF_L1", - "BriefDescription": "All L1 D cache load references counted at finish, gated by reject. In P9 and earlier this event counted only cacheable loads but in P10 both cacheable and non-cacheable loads are included." - }, { "EventCode": "0x2E010", "EventName": "PM_ADJUNCT_INST_CMPL", "BriefDescription": "PowerPC instruction completed while the thread was in Adjunct state." }, - { - "EventCode": "0x2E014", - "EventName": "PM_STCX_FIN", - "BriefDescription": "Conditional store instruction (STCX) finished. LARX and STCX are instructions used to acquire a lock." - }, - { - "EventCode": "0x2F054", - "EventName": "PM_DISP_SS1_2_INSTR_CYC", - "BriefDescription": "Cycles in which Superslice 1 dispatches either 1 or 2 instructions." - }, - { - "EventCode": "0x2F056", - "EventName": "PM_DISP_SS1_4_INSTR_CYC", - "BriefDescription": "Cycles in which Superslice 1 dispatches either 3 or 4 instructions." - }, { "EventCode": "0x200F2", "EventName": "PM_INST_DISP", "BriefDescription": "PowerPC instruction dispatched." }, - { - "EventCode": "0x200FD", - "EventName": "PM_L1_ICACHE_MISS", - "BriefDescription": "Demand instruction cache miss." - }, - { - "EventCode": "0x3F04A", - "EventName": "PM_LSU_ST5_FIN", - "BriefDescription": "LSU Finished an internal operation in ST2 port." - }, - { - "EventCode": "0x3405A", - "EventName": "PM_PRIVILEGED_INST_CMPL", - "BriefDescription": "PowerPC instruction completed while the thread was in Privileged state." - }, - { - "EventCode": "0x3F054", - "EventName": "PM_DISP_SS0_4_INSTR_CYC", - "BriefDescription": "Cycles in which Superslice 0 dispatches either 3 or 4 instructions." - }, - { - "EventCode": "0x3F056", - "EventName": "PM_DISP_SS0_8_INSTR_CYC", - "BriefDescription": "Cycles in which Superslice 0 dispatches either 5, 6, 7 or 8 instructions." - }, - { - "EventCode": "0x30068", - "EventName": "PM_L1_ICACHE_RELOADED_PREF", - "BriefDescription": "Counts all instruction cache prefetch reloads (includes demand turned into prefetch)." - }, { "EventCode": "0x300F6", "EventName": "PM_LD_DEMAND_MISS_L1", @@ -94,16 +24,6 @@ "EventName": "PM_L1_ICACHE_RELOADED_ALL", "BriefDescription": "Counts all instruction cache reloads includes demand, prefetch, prefetch turned into demand and demand turned into prefetch." }, - { - "EventCode": "0x4D05E", - "EventName": "PM_BR_CMPL", - "BriefDescription": "A branch completed. All branches are included." - }, - { - "EventCode": "0x400F0", - "EventName": "PM_LD_DEMAND_MISS_L1_FIN", - "BriefDescription": "Load missed L1, counted at finish time." - }, { "EventCode": "0x00000038BC", "EventName": "PM_ISYNC_CMPL", @@ -139,64 +59,14 @@ "EventName": "PM_ST1_UNALIGNED_FIN", "BriefDescription": "Store instructions in ST1 port that are either unaligned, or treated as unaligned and require an additional recycle through the pipeline. This typically adds about 10 cycles to the latency of the instruction. This only includes stores that cross the 128 byte boundary. Counted at finish time." }, - { - "EventCode": "0x000000C8B8", - "EventName": "PM_STCX_SUCCESS_CMPL", - "BriefDescription": "STCX instructions that completed successfully. Specifically, counts only when a pass status is returned from the nest." - }, { "EventCode": "0x000000D0B4", "EventName": "PM_DC_PREF_STRIDED_CONF", "BriefDescription": "A demand load referenced a line in an active strided prefetch stream. The stream could have been allocated through the hardware prefetch mechanism or through software." }, - { - "EventCode": "0x000000F880", - "EventName": "PM_SNOOP_TLBIE_CYC", - "BriefDescription": "Cycles in which TLBIE snoops are executed in the LSU." - }, - { - "EventCode": "0x000000F084", - "EventName": "PM_SNOOP_TLBIE_CACHE_WALK_CYC", - "BriefDescription": "TLBIE snoop cycles in which the data cache is being walked." - }, - { - "EventCode": "0x000000F884", - "EventName": "PM_SNOOP_TLBIE_WAIT_ST_CYC", - "BriefDescription": "TLBIE snoop cycles in which older stores are still draining." - }, - { - "EventCode": "0x000000F088", - "EventName": "PM_SNOOP_TLBIE_WAIT_LD_CYC", - "BriefDescription": "TLBIE snoop cycles in which older loads are still draining." - }, - { - "EventCode": "0x000000F08C", - "EventName": "PM_SNOOP_TLBIE_WAIT_MMU_CYC", - "BriefDescription": "TLBIE snoop cycles in which the Load-Store unit is waiting for the MMU to finish invalidation." - }, { "EventCode": "0x0000004884", "EventName": "PM_NO_FETCH_IBUF_FULL_CYC", "BriefDescription": "Cycles in which no instructions are fetched because there is no room in the instruction buffers." - }, - { - "EventCode": "0x00000048B4", - "EventName": "PM_BR_TKN_UNCOND_FIN", - "BriefDescription": "An unconditional branch finished. All unconditional branches are taken." - }, - { - "EventCode": "0x0B0000016080", - "EventName": "PM_L2_TLBIE_SLBIE_START", - "BriefDescription": "NCU Master received a TLBIE/SLBIEG/SLBIAG operation from the core. Event count should be multiplied by 2 since the data is coming from a 2:1 clock domain and the data is time sliced across all 4 threads." - }, - { - "EventCode": "0x0B0000016880", - "EventName": "PM_L2_TLBIE_SLBIE_DELAY", - "BriefDescription": "Cycles when a TLBIE/SLBIEG/SLBIAG command was held in a hottemp condition by the NCU Master. Multiply this count by 1000 to obtain the total number of cycles. This can be divided by PM_L2_TLBIE_SLBIE_SENT to obtain the average time a TLBIE/SLBIEG/SLBIAG command was held. Event count should be multiplied by 2 since the data is coming from a 2:1 clock domain and the data is time sliced across all 4 threads." - }, - { - "EventCode": "0x0B0000026880", - "EventName": "PM_L2_SNP_TLBIE_SLBIE_DELAY", - "BriefDescription": "Cycles when a TLBIE/SLBIEG/SLBIAG that targets this thread's LPAR was in flight while in a hottemp condition. Multiply this count by 1000 to obtain the total number of cycles. This can be divided by PM_L2_SNP_TLBIE_SLBIE_START to obtain the overall efficiency. Note: 'inflight' means SnpTLB has been sent to core(ie doesn't include when SnpTLB is in NCU waiting to be launched serially behind different SnpTLB). The NCU Snooper gets in a 'hottemp' delay window when it detects it is above its TLBIE/SLBIE threshold for process SnpTLBIE/SLBIE with this core. Event count should be multiplied by 2 since the data is coming from a 2:1 clock domain and the data is time sliced across all 4 threads." } ] diff --git a/tools/perf/pmu-events/arch/powerpc/power10/pipeline.json b/tools/perf/pmu-events/arch/powerpc/power10/pipeline.json index 21b23bb55d0d..940375d251cb 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/pipeline.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/pipeline.json @@ -94,11 +94,21 @@ "EventName": "PM_CMPL_STALL_LWSYNC", "BriefDescription": "Cycles in which the oldest instruction in the pipeline was a lwsync waiting to complete." }, + { + "EventCode": "0x1F056", + "EventName": "PM_DISP_SS0_2_INSTR_CYC", + "BriefDescription": "Cycles in which Superslice 0 dispatches either 1 or 2 instructions." + }, { "EventCode": "0x1F058", "EventName": "PM_DISP_HELD_CYC", "BriefDescription": "Cycles dispatch is held." }, + { + "EventCode": "0x1F05A", + "EventName": "PM_DISP_HELD_SYNC_CYC", + "BriefDescription": "Cycles dispatch is held because of a synchronizing instruction that requires the ICT to be empty before dispatch." + }, { "EventCode": "0x10064", "EventName": "PM_DISP_STALL_IC_L2", @@ -229,6 +239,16 @@ "EventName": "PM_NTC_FIN", "BriefDescription": "Cycles in which the oldest instruction in the pipeline (NTC) finishes. Note that instructions can finish out of order, therefore not all the instructions that finish have a Next-to-complete status." }, + { + "EventCode": "0x2F054", + "EventName": "PM_DISP_SS1_2_INSTR_CYC", + "BriefDescription": "Cycles in which Superslice 1 dispatches either 1 or 2 instructions." + }, + { + "EventCode": "0x2F056", + "EventName": "PM_DISP_SS1_4_INSTR_CYC", + "BriefDescription": "Cycles in which Superslice 1 dispatches either 3 or 4 instructions." + }, { "EventCode": "0x20066", "EventName": "PM_DISP_HELD_OTHER_CYC", @@ -329,6 +349,16 @@ "EventName": "PM_DISP_STALL_IC_L3", "BriefDescription": "Cycles when dispatch was stalled while the instruction was fetched from the local L3." }, + { + "EventCode": "0x3F054", + "EventName": "PM_DISP_SS0_4_INSTR_CYC", + "BriefDescription": "Cycles in which Superslice 0 dispatches either 3 or 4 instructions." + }, + { + "EventCode": "0x3F056", + "EventName": "PM_DISP_SS0_8_INSTR_CYC", + "BriefDescription": "Cycles in which Superslice 0 dispatches either 5, 6, 7 or 8 instructions." + }, { "EventCode": "0x30060", "EventName": "PM_DISP_HELD_XVFC_MAPPER_CYC", @@ -458,5 +488,20 @@ "EventCode": "0x400F8", "EventName": "PM_FLUSH", "BriefDescription": "Flush (any type)." + }, + { + "EventCode": "0x0B0000016080", + "EventName": "PM_L2_TLBIE_SLBIE_START", + "BriefDescription": "NCU Master received a TLBIE/SLBIEG/SLBIAG operation from the core. Event count should be multiplied by 2 since the data is coming from a 2:1 clock domain and the data is time sliced across all 4 threads." + }, + { + "EventCode": "0x0B0000016880", + "EventName": "PM_L2_TLBIE_SLBIE_DELAY", + "BriefDescription": "Cycles when a TLBIE/SLBIEG/SLBIAG command was held in a hottemp condition by the NCU Master. Multiply this count by 1000 to obtain the total number of cycles. This can be divided by PM_L2_TLBIE_SLBIE_SENT to obtain the average time a TLBIE/SLBIEG/SLBIAG command was held. Event count should be multiplied by 2 since the data is coming from a 2:1 clock domain and the data is time sliced across all 4 threads." + }, + { + "EventCode": "0x0B0000026880", + "EventName": "PM_L2_SNP_TLBIE_SLBIE_DELAY", + "BriefDescription": "Cycles when a TLBIE/SLBIEG/SLBIAG that targets this thread's LPAR was in flight while in a hottemp condition. Multiply this count by 1000 to obtain the total number of cycles. This can be divided by PM_L2_SNP_TLBIE_SLBIE_START to obtain the overall efficiency. Note: 'inflight' means SnpTLB has been sent to core(ie doesn't include when SnpTLB is in NCU waiting to be launched serially behind different SnpTLB). The NCU Snooper gets in a 'hottemp' delay window when it detects it is above its TLBIE/SLBIE threshold for process SnpTLBIE/SLBIE with this core. Event count should be multiplied by 2 since the data is coming from a 2:1 clock domain and the data is time sliced across all 4 threads." } ] diff --git a/tools/perf/pmu-events/arch/powerpc/power10/pmc.json b/tools/perf/pmu-events/arch/powerpc/power10/pmc.json index 04732698d9b2..6f5b0e8fde12 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/pmc.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/pmc.json @@ -129,6 +129,11 @@ "EventName": "PM_PMC6_OVERFLOW", "BriefDescription": "The event selected for PMC6 caused the event counter to overflow." }, + { + "EventCode": "0x3405A", + "EventName": "PM_PRIVILEGED_INST_CMPL", + "BriefDescription": "PowerPC instruction completed while the thread was in Privileged state." + }, { "EventCode": "0x3006C", "EventName": "PM_RUN_CYC_SMT2_MODE", From adf50a6e66ae7527f9db4f5c9c11e6cc2a870d4f Mon Sep 17 00:00:00 2001 From: Kajol Jain Date: Tue, 27 Aug 2024 11:02:06 +0530 Subject: [PATCH 238/347] perf vendor events: Move PM_BR_MPRED_CMPL event for power10 platform Move PM_BR_MPRED_CMPL event from cache.json to frontend.json file for power10 platform Reviewed-by: Ian Rogers Signed-off-by: Kajol Jain Cc: Athira Rajeev Cc: Disha Goel Cc: Hari Bathini Cc: Ian Rogers Cc: Madhavan Srinivasan Cc: Namhyung Kim Cc: linuxppc-dev@lists.ozlabs.org Link: https://lore.kernel.org/r/20240827053206.538814-3-kjain@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/arch/powerpc/power10/cache.json | 5 ----- tools/perf/pmu-events/arch/powerpc/power10/frontend.json | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/perf/pmu-events/arch/powerpc/power10/cache.json b/tools/perf/pmu-events/arch/powerpc/power10/cache.json index 9814a59fce31..b7e0be09ff57 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/cache.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/cache.json @@ -18,10 +18,5 @@ "EventCode": "0x300F4", "EventName": "PM_RUN_INST_CMPL_CONC", "BriefDescription": "PowerPC instruction completed by this thread when all threads in the core had the run-latch set." - }, - { - "EventCode": "0x400F6", - "EventName": "PM_BR_MPRED_CMPL", - "BriefDescription": "A mispredicted branch completed. Includes direction and target." } ] diff --git a/tools/perf/pmu-events/arch/powerpc/power10/frontend.json b/tools/perf/pmu-events/arch/powerpc/power10/frontend.json index 684374fe5699..b6998987ab75 100644 --- a/tools/perf/pmu-events/arch/powerpc/power10/frontend.json +++ b/tools/perf/pmu-events/arch/powerpc/power10/frontend.json @@ -94,6 +94,11 @@ "EventName": "PM_DTLB_HIT_1G", "BriefDescription": "Data TLB hit (DERAT reload) page size 1G. Implies radix translation. When MMCR1[16]=0 this event counts only for demand misses. When MMCR1[16]=1 this event includes demand misses and prefetches." }, + { + "EventCode": "0x400F6", + "EventName": "PM_BR_MPRED_CMPL", + "BriefDescription": "A mispredicted branch completed. Includes direction and target." + }, { "EventCode": "0x400FC", "EventName": "PM_ITLB_MISS", From aea4d463459c95a1b19627d1aa9c21b691da843a Mon Sep 17 00:00:00 2001 From: Lucas Stach Date: Mon, 1 Jul 2024 19:57:35 +0200 Subject: [PATCH 239/347] perf vendor events arm64: Move Yitian 710 DDR PMU into T-Head directory The Yitian 710 is not a Freescale/NXP design and thus should be located in a separate T-Head vendor directory. Reviewed-by: Jing Zhang Signed-off-by: Lucas Stach Cc: Ingo Molnar Cc: James Clark Cc: John Garry Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Shuai Xue Cc: Will Deacon Cc: kernel@pengutronix.de Cc: linux-arm-kernel@lists.infradead.org Cc: patchwork-lst@pengutronix.de Link: https://lore.kernel.org/r/20240701175735.485655-1-l.stach@pengutronix.de Signed-off-by: Arnaldo Carvalho de Melo --- .../arch/arm64/{freescale => thead}/yitian710/sys/ali_drw.json | 0 .../arch/arm64/{freescale => thead}/yitian710/sys/metrics.json | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename tools/perf/pmu-events/arch/arm64/{freescale => thead}/yitian710/sys/ali_drw.json (100%) rename tools/perf/pmu-events/arch/arm64/{freescale => thead}/yitian710/sys/metrics.json (100%) diff --git a/tools/perf/pmu-events/arch/arm64/freescale/yitian710/sys/ali_drw.json b/tools/perf/pmu-events/arch/arm64/thead/yitian710/sys/ali_drw.json similarity index 100% rename from tools/perf/pmu-events/arch/arm64/freescale/yitian710/sys/ali_drw.json rename to tools/perf/pmu-events/arch/arm64/thead/yitian710/sys/ali_drw.json diff --git a/tools/perf/pmu-events/arch/arm64/freescale/yitian710/sys/metrics.json b/tools/perf/pmu-events/arch/arm64/thead/yitian710/sys/metrics.json similarity index 100% rename from tools/perf/pmu-events/arch/arm64/freescale/yitian710/sys/metrics.json rename to tools/perf/pmu-events/arch/arm64/thead/yitian710/sys/metrics.json From c87826ddcefaf0b1245d1d8c61040679ea0bd387 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Tue, 6 Aug 2024 21:41:22 +0100 Subject: [PATCH 240/347] perf auxtrace: Use evsel__is_aux_event() for checking AUX event Use evsel__is_aux_event() to decide if an event is a AUX event, this is a refactoring to replace comparing the PMU type. Reviewed-by: Adrian Hunter Signed-off-by: Leo Yan Cc: Ian Rogers Cc: James Clark Cc: Kan Liang Cc: Mike Leach Cc: Namhyung Kim Cc: Suzuki Poulouse Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240806204130.720977-2-leo.yan@arm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/auxtrace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/auxtrace.c b/tools/perf/util/auxtrace.c index cbb773ed6f1a..ca8682966fae 100644 --- a/tools/perf/util/auxtrace.c +++ b/tools/perf/util/auxtrace.c @@ -671,11 +671,11 @@ int auxtrace_record__read_finish(struct auxtrace_record *itr, int idx) { struct evsel *evsel; - if (!itr->evlist || !itr->pmu) + if (!itr->evlist) return -EINVAL; evlist__for_each_entry(itr->evlist, evsel) { - if (evsel->core.attr.type == itr->pmu->type) { + if (evsel__is_aux_event(evsel)) { if (evsel->disabled) return 0; return evlist__enable_event_idx(itr->evlist, evsel, idx); From d5726f1c8d421f000a9265601c28279e82d43db0 Mon Sep 17 00:00:00 2001 From: Leo Yan Date: Tue, 6 Aug 2024 21:41:23 +0100 Subject: [PATCH 241/347] perf auxtrace: Remove unused 'pmu' pointer from struct auxtrace_record The 'pmu' pointer in the auxtrace_record structure is not used after support multiple AUX events, remove it. Reviewed-by: Adrian Hunter Signed-off-by: Leo Yan Cc: Ian Rogers Cc: James Clark Cc: Kan Liang Cc: Mike Leach Cc: Namhyung Kim Cc: Suzuki Poulouse Cc: coresight@lists.linaro.org Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240806204130.720977-3-leo.yan@arm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm/util/cs-etm.c | 1 - tools/perf/arch/arm64/util/arm-spe.c | 1 - tools/perf/arch/arm64/util/hisi-ptt.c | 1 - tools/perf/arch/x86/util/intel-bts.c | 1 - tools/perf/arch/x86/util/intel-pt.c | 1 - tools/perf/util/auxtrace.h | 1 - 6 files changed, 6 deletions(-) diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c index da6231367993..96aeb7cdbee1 100644 --- a/tools/perf/arch/arm/util/cs-etm.c +++ b/tools/perf/arch/arm/util/cs-etm.c @@ -888,7 +888,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; diff --git a/tools/perf/arch/arm64/util/arm-spe.c b/tools/perf/arch/arm64/util/arm-spe.c index d59f6ca499f2..2be99fdf997d 100644 --- a/tools/perf/arch/arm64/util/arm-spe.c +++ b/tools/perf/arch/arm64/util/arm-spe.c @@ -514,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; diff --git a/tools/perf/arch/arm64/util/hisi-ptt.c b/tools/perf/arch/arm64/util/hisi-ptt.c index ba97c8a562a0..eac9739c87e6 100644 --- a/tools/perf/arch/arm64/util/hisi-ptt.c +++ b/tools/perf/arch/arm64/util/hisi-ptt.c @@ -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; diff --git a/tools/perf/arch/x86/util/intel-bts.c b/tools/perf/arch/x86/util/intel-bts.c index 34696f3d3d5d..85c8186300c8 100644 --- a/tools/perf/arch/x86/util/intel-bts.c +++ b/tools/perf/arch/x86/util/intel-bts.c @@ -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; diff --git a/tools/perf/arch/x86/util/intel-pt.c b/tools/perf/arch/x86/util/intel-pt.c index 4b710e875953..ea510a7486b1 100644 --- a/tools/perf/arch/x86/util/intel-pt.c +++ b/tools/perf/arch/x86/util/intel-pt.c @@ -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; diff --git a/tools/perf/util/auxtrace.h b/tools/perf/util/auxtrace.h index d405efcd8708..a1895a4f530b 100644 --- a/tools/perf/util/auxtrace.h +++ b/tools/perf/util/auxtrace.h @@ -411,7 +411,6 @@ struct auxtrace_record { int (*read_finish)(struct auxtrace_record *itr, int idx); unsigned int alignment; unsigned int default_aux_sample_size; - struct perf_pmu *pmu; struct evlist *evlist; }; From 6f87543c74ddc9649cfd9a6e6908821cdb4d397a Mon Sep 17 00:00:00 2001 From: James Clark Date: Fri, 9 Aug 2024 10:54:22 +0100 Subject: [PATCH 242/347] perf test trace_btf_enum: Fix shellcheck warning Shellcheck versions < v0.7.2 can't follow this path so add the helper to fix the following warning: In tests/shell/trace_btf_enum.sh line 13: . "$(dirname $0)"/lib/probe.sh ^--------------------------^ SC1090: Can't follow non-constant source. Use a directive to specify location. Fixes: d66763fed30f0bd8 ("perf test trace_btf_enum: Add regression test for the BTF augmentation of enums in 'perf trace'") Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Howard Chu Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240809095426.3065163-1-james.clark@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/trace_btf_enum.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/tests/shell/trace_btf_enum.sh b/tools/perf/tests/shell/trace_btf_enum.sh index 7d407b52bea5..5a3b8a5a9b5c 100755 --- a/tools/perf/tests/shell/trace_btf_enum.sh +++ b/tools/perf/tests/shell/trace_btf_enum.sh @@ -10,6 +10,7 @@ non_syscall="timer:hrtimer_init,timer:hrtimer_start" TESTPROG="perf test -w landlock" +# shellcheck source=lib/probe.sh . "$(dirname $0)"/lib/probe.sh skip_if_no_perf_trace || exit 2 From 9af2efee41b27a0f386fb5aa95d8d0b4b5d9fede Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 26 Aug 2024 15:10:42 -0700 Subject: [PATCH 243/347] perf report: Fix segfault when 'sym' sort key is not used The fields in the hist_entry are filled on-demand which means they only have meaningful values when relevant sort keys are used. So if neither of 'dso' nor 'sym' sort keys are used, the map/symbols in the hist entry can be garbage. So it shouldn't access it unconditionally. I got a segfault, when I wanted to see cgroup profiles. $ sudo perf record -a --all-cgroups --synth=cgroup true $ sudo perf report -s cgroup Program received signal SIGSEGV, Segmentation fault. 0x00005555557a8d90 in map__dso (map=0x0) at util/map.h:48 48 return RC_CHK_ACCESS(map)->dso; (gdb) bt #0 0x00005555557a8d90 in map__dso (map=0x0) at util/map.h:48 #1 0x00005555557aa39b in map__load (map=0x0) at util/map.c:344 #2 0x00005555557aa592 in map__find_symbol (map=0x0, addr=140736115941088) at util/map.c:385 #3 0x00005555557ef000 in hists__findnew_entry (hists=0x555556039d60, entry=0x7fffffffa4c0, al=0x7fffffffa8c0, sample_self=true) at util/hist.c:644 #4 0x00005555557ef61c in __hists__add_entry (hists=0x555556039d60, al=0x7fffffffa8c0, sym_parent=0x0, bi=0x0, mi=0x0, ki=0x0, block_info=0x0, sample=0x7fffffffaa90, sample_self=true, ops=0x0) at util/hist.c:761 #5 0x00005555557ef71f in hists__add_entry (hists=0x555556039d60, al=0x7fffffffa8c0, sym_parent=0x0, bi=0x0, mi=0x0, ki=0x0, sample=0x7fffffffaa90, sample_self=true) at util/hist.c:779 #6 0x00005555557f00fb in iter_add_single_normal_entry (iter=0x7fffffffa900, al=0x7fffffffa8c0) at util/hist.c:1015 #7 0x00005555557f09a7 in hist_entry_iter__add (iter=0x7fffffffa900, al=0x7fffffffa8c0, max_stack_depth=127, arg=0x7fffffffbce0) at util/hist.c:1260 #8 0x00005555555ba7ce in process_sample_event (tool=0x7fffffffbce0, event=0x7ffff7c14128, sample=0x7fffffffaa90, evsel=0x555556039ad0, machine=0x5555560388e8) at builtin-report.c:334 #9 0x00005555557b30c8 in evlist__deliver_sample (evlist=0x555556039010, tool=0x7fffffffbce0, event=0x7ffff7c14128, sample=0x7fffffffaa90, evsel=0x555556039ad0, machine=0x5555560388e8) at util/session.c:1232 #10 0x00005555557b32bc in machines__deliver_event (machines=0x5555560388e8, evlist=0x555556039010, event=0x7ffff7c14128, sample=0x7fffffffaa90, tool=0x7fffffffbce0, file_offset=110888, file_path=0x555556038ff0 "perf.data") at util/session.c:1271 #11 0x00005555557b3848 in perf_session__deliver_event (session=0x5555560386d0, event=0x7ffff7c14128, tool=0x7fffffffbce0, file_offset=110888, file_path=0x555556038ff0 "perf.data") at util/session.c:1354 #12 0x00005555557affaf in ordered_events__deliver_event (oe=0x555556038e60, event=0x555556135aa0) at util/session.c:132 #13 0x00005555557bb605 in do_flush (oe=0x555556038e60, show_progress=false) at util/ordered-events.c:245 #14 0x00005555557bb95c in __ordered_events__flush (oe=0x555556038e60, how=OE_FLUSH__ROUND, timestamp=0) at util/ordered-events.c:324 #15 0x00005555557bba46 in ordered_events__flush (oe=0x555556038e60, how=OE_FLUSH__ROUND) at util/ordered-events.c:342 #16 0x00005555557b1b3b in perf_event__process_finished_round (tool=0x7fffffffbce0, event=0x7ffff7c15bb8, oe=0x555556038e60) at util/session.c:780 #17 0x00005555557b3b27 in perf_session__process_user_event (session=0x5555560386d0, event=0x7ffff7c15bb8, file_offset=117688, file_path=0x555556038ff0 "perf.data") at util/session.c:1406 As you can see the entry->ms.map was NULL even if he->ms.map has a value. This is because 'sym' sort key is not given, so it cannot assume whether he->ms.sym and entry->ms.sym is the same. I only checked the 'sym' sort key here as it implies 'dso' behavior (so maps are the same). Fixes: ac01c8c4246546fd ("perf hist: Update hist symbol when updating maps") Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Matt Fleming Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240826221045.1202305-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/hist.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index dadce2889e52..f387e85a0087 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -638,7 +638,7 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists, * mis-adjust symbol addresses when computing * the history counter to increment. */ - if (he->ms.map != entry->ms.map) { + if (hists__has(hists, sym) && he->ms.map != entry->ms.map) { if (he->ms.sym) { u64 addr = he->ms.sym->start; he->ms.sym = map__find_symbol(entry->ms.map, addr); From 591156f25f6bc71b143d4a7c7cbe7cdb68dda94e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 26 Aug 2024 15:10:43 -0700 Subject: [PATCH 244/347] perf bpf-filter: Add build dependency to header files The flex and bison files need to be recompiled when one of these header filters are changed. * util/bpf-filter.h * util/bpf_skel/sample-filter.h Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240826221045.1202305-3-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/Build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/Build b/tools/perf/util/Build index b87f918bdfe7..260cec2f6c0b 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -278,12 +278,12 @@ $(OUTPUT)util/pmu-bison.c $(OUTPUT)util/pmu-bison.h: util/pmu.y $(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) \ -o $(OUTPUT)util/pmu-bison.c -p perf_pmu_ -$(OUTPUT)util/bpf-filter-flex.c $(OUTPUT)util/bpf-filter-flex.h: util/bpf-filter.l $(OUTPUT)util/bpf-filter-bison.c +$(OUTPUT)util/bpf-filter-flex.c $(OUTPUT)util/bpf-filter-flex.h: util/bpf-filter.l $(OUTPUT)util/bpf-filter-bison.c util/bpf-filter.h util/bpf_skel/sample-filter.h $(call rule_mkdir) $(Q)$(call echo-cmd,flex)$(FLEX) -o $(OUTPUT)util/bpf-filter-flex.c \ --header-file=$(OUTPUT)util/bpf-filter-flex.h $(PARSER_DEBUG_FLEX) $< -$(OUTPUT)util/bpf-filter-bison.c $(OUTPUT)util/bpf-filter-bison.h: util/bpf-filter.y +$(OUTPUT)util/bpf-filter-bison.c $(OUTPUT)util/bpf-filter-bison.h: util/bpf-filter.y util/bpf-filter.h util/bpf_skel/sample-filter.h $(call rule_mkdir) $(Q)$(call echo-cmd,bison)$(BISON) -v $< -d $(PARSER_DEBUG_BISON) $(BISON_FILE_PREFIX_MAP) \ -o $(OUTPUT)util/bpf-filter-bison.c -p perf_bpf_filter_ From 91e88437d5156b209b1d69b69b560f0a02b80712 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 26 Aug 2024 15:10:44 -0700 Subject: [PATCH 245/347] perf bpf-filter: Support filtering on cgroups The new cgroup filter can take either of '==' or '!=' operator and a pathname for the target cgroup. $ perf record -a --all-cgroups -e cycles --filter 'cgroup == /abc/def' -- sleep 1 Users should have --all-cgroups option in the command line to enable cgroup filtering. Technically it doesn't need to have the option as it can get the current task's cgroup info directly from BPF. But I want to follow the convention for the other sample info. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240826221045.1202305-4-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf-filter.c | 1 + tools/perf/util/bpf-filter.l | 28 ++++++++++++++++---- tools/perf/util/bpf-filter.y | 28 ++++++++++++++++++-- tools/perf/util/bpf_skel/sample-filter.h | 2 +- tools/perf/util/bpf_skel/sample_filter.bpf.c | 4 ++- tools/perf/util/bpf_skel/vmlinux/vmlinux.h | 1 + 6 files changed, 55 insertions(+), 9 deletions(-) diff --git a/tools/perf/util/bpf-filter.c b/tools/perf/util/bpf-filter.c index 0a1832564dd2..e87b6789eb9e 100644 --- a/tools/perf/util/bpf-filter.c +++ b/tools/perf/util/bpf-filter.c @@ -100,6 +100,7 @@ static const struct perf_sample_info { PERF_SAMPLE_TYPE(TRANSACTION, "--transaction"), PERF_SAMPLE_TYPE(CODE_PAGE_SIZE, "--code-page-size"), PERF_SAMPLE_TYPE(DATA_PAGE_SIZE, "--data-page-size"), + PERF_SAMPLE_TYPE(CGROUP, "--all-cgroups"), }; static int get_pinned_fd(const char *name); diff --git a/tools/perf/util/bpf-filter.l b/tools/perf/util/bpf-filter.l index 2a7c839f3fae..f313404f95a9 100644 --- a/tools/perf/util/bpf-filter.l +++ b/tools/perf/util/bpf-filter.l @@ -9,8 +9,11 @@ #include "bpf-filter.h" #include "bpf-filter-bison.h" +extern int perf_bpf_filter_needs_path; + static int sample(enum perf_bpf_filter_term term) { + perf_bpf_filter_needs_path = 0; perf_bpf_filter_lval.sample.term = term; perf_bpf_filter_lval.sample.part = 0; return BFT_SAMPLE; @@ -18,11 +21,20 @@ static int sample(enum perf_bpf_filter_term term) static int sample_part(enum perf_bpf_filter_term term, int part) { + perf_bpf_filter_needs_path = 0; perf_bpf_filter_lval.sample.term = term; perf_bpf_filter_lval.sample.part = part; return BFT_SAMPLE; } +static int sample_path(enum perf_bpf_filter_term term) +{ + perf_bpf_filter_needs_path = 1; + perf_bpf_filter_lval.sample.term = term; + perf_bpf_filter_lval.sample.part = 0; + return BFT_SAMPLE_PATH; +} + static int operator(enum perf_bpf_filter_op op) { perf_bpf_filter_lval.op = op; @@ -48,10 +60,15 @@ static int constant(int val) return BFT_NUM; } -static int error(const char *str) +static int path_or_error(void) { - printf("perf_bpf_filter: Unexpected filter %s: %s\n", str, perf_bpf_filter_text); - return BFT_ERROR; + if (!perf_bpf_filter_needs_path) { + printf("perf_bpf_filter: Error: Unexpected item: %s\n", + perf_bpf_filter_text); + return BFT_ERROR; + } + perf_bpf_filter_lval.path = perf_bpf_filter_text; + return BFT_PATH; } %} @@ -59,6 +76,7 @@ static int error(const char *str) num_dec [0-9]+ num_hex 0[Xx][0-9a-fA-F]+ space [ \t]+ +path [^ \t\n]+ ident [_a-zA-Z][_a-zA-Z0-9]+ %% @@ -97,6 +115,7 @@ mem_blk { return sample_part(PBF_TERM_DATA_SRC, 7); } mem_hops { return sample_part(PBF_TERM_DATA_SRC, 8); } uid { return sample(PBF_TERM_UID); } gid { return sample(PBF_TERM_GID); } +cgroup { return sample_path(PBF_TERM_CGROUP); } "==" { return operator(PBF_OP_EQ); } "!=" { return operator(PBF_OP_NEQ); } @@ -155,7 +174,6 @@ hops3 { return constant(PERF_MEM_HOPS_3); } "," { return ','; } "||" { return BFT_LOGICAL_OR; } -{ident} { return error("ident"); } -. { return error("input"); } +{path} { return path_or_error(); } %% diff --git a/tools/perf/util/bpf-filter.y b/tools/perf/util/bpf-filter.y index 0c56fccb8874..5a79a8e7a45b 100644 --- a/tools/perf/util/bpf-filter.y +++ b/tools/perf/util/bpf-filter.y @@ -12,9 +12,13 @@ #include #include #include "bpf-filter.h" +#include "cgroup.h" int perf_bpf_filter_lex(void); +/* To indicate if the current term needs a pathname or not */ +int perf_bpf_filter_needs_path; + static void perf_bpf_filter_error(struct list_head *expr __maybe_unused, char const *msg) { @@ -26,6 +30,7 @@ static void perf_bpf_filter_error(struct list_head *expr __maybe_unused, %union { unsigned long num; + char *path; struct { enum perf_bpf_filter_term term; int part; @@ -34,12 +39,13 @@ static void perf_bpf_filter_error(struct list_head *expr __maybe_unused, struct perf_bpf_filter_expr *expr; } -%token BFT_SAMPLE BFT_OP BFT_ERROR BFT_NUM BFT_LOGICAL_OR +%token BFT_SAMPLE BFT_SAMPLE_PATH BFT_OP BFT_ERROR BFT_NUM BFT_LOGICAL_OR BFT_PATH %type filter_term filter_expr %destructor { free ($$); } -%type BFT_SAMPLE +%type BFT_SAMPLE BFT_SAMPLE_PATH %type BFT_OP %type BFT_NUM +%type BFT_PATH %% @@ -81,5 +87,23 @@ BFT_SAMPLE BFT_OP BFT_NUM { $$ = perf_bpf_filter_expr__new($1.term, $1.part, $2, $3); } +| +BFT_SAMPLE_PATH BFT_OP BFT_PATH +{ + struct cgroup *cgrp; + unsigned long cgroup_id = 0; + + if ($2 != PBF_OP_EQ && $2 != PBF_OP_NEQ) { + printf("perf_bpf_filter: cgroup accepts '==' or '!=' only\n"); + YYERROR; + } + + cgrp = cgroup__new($3, /*do_open=*/false); + if (cgrp && read_cgroup_id(cgrp) == 0) + cgroup_id = cgrp->id; + + $$ = perf_bpf_filter_expr__new($1.term, $1.part, $2, cgroup_id); + cgroup__put(cgrp); +} %% diff --git a/tools/perf/util/bpf_skel/sample-filter.h b/tools/perf/util/bpf_skel/sample-filter.h index 5f0c8e4e83d3..683fec85e71e 100644 --- a/tools/perf/util/bpf_skel/sample-filter.h +++ b/tools/perf/util/bpf_skel/sample-filter.h @@ -45,7 +45,7 @@ enum perf_bpf_filter_term { __PBF_UNUSED_TERM18 = PBF_TERM_SAMPLE_START + 18, /* SAMPLE_REGS_INTR = 1U << 18 */ PBF_TERM_PHYS_ADDR = PBF_TERM_SAMPLE_START + 19, /* SAMPLE_PHYS_ADDR = 1U << 19 */ __PBF_UNUSED_TERM20 = PBF_TERM_SAMPLE_START + 20, /* SAMPLE_AUX = 1U << 20 */ - __PBF_UNUSED_TERM21 = PBF_TERM_SAMPLE_START + 21, /* SAMPLE_CGROUP = 1U << 21 */ + PBF_TERM_CGROUP = PBF_TERM_SAMPLE_START + 21, /* SAMPLE_CGROUP = 1U << 21 */ PBF_TERM_DATA_PAGE_SIZE = PBF_TERM_SAMPLE_START + 22, /* SAMPLE_DATA_PAGE_SIZE = 1U << 22 */ PBF_TERM_CODE_PAGE_SIZE = PBF_TERM_SAMPLE_START + 23, /* SAMPLE_CODE_PAGE_SIZE = 1U << 23 */ PBF_TERM_WEIGHT_STRUCT = PBF_TERM_SAMPLE_START + 24, /* SAMPLE_WEIGHT_STRUCT = 1U << 24 */ diff --git a/tools/perf/util/bpf_skel/sample_filter.bpf.c b/tools/perf/util/bpf_skel/sample_filter.bpf.c index 4872a16eedfd..b195e6efeb8b 100644 --- a/tools/perf/util/bpf_skel/sample_filter.bpf.c +++ b/tools/perf/util/bpf_skel/sample_filter.bpf.c @@ -93,6 +93,7 @@ static inline __u64 perf_get_sample(struct bpf_perf_event_data_kern *kctx, BUILD_CHECK_SAMPLE(DATA_SRC); BUILD_CHECK_SAMPLE(TRANSACTION); BUILD_CHECK_SAMPLE(PHYS_ADDR); + BUILD_CHECK_SAMPLE(CGROUP); BUILD_CHECK_SAMPLE(DATA_PAGE_SIZE); BUILD_CHECK_SAMPLE(CODE_PAGE_SIZE); BUILD_CHECK_SAMPLE(WEIGHT_STRUCT); @@ -135,6 +136,8 @@ static inline __u64 perf_get_sample(struct bpf_perf_event_data_kern *kctx, return kctx->data->weight.full; case PBF_TERM_PHYS_ADDR: return kctx->data->phys_addr; + case PBF_TERM_CGROUP: + return kctx->data->cgroup; case PBF_TERM_CODE_PAGE_SIZE: return kctx->data->code_page_size; case PBF_TERM_DATA_PAGE_SIZE: @@ -183,7 +186,6 @@ static inline __u64 perf_get_sample(struct bpf_perf_event_data_kern *kctx, case __PBF_UNUSED_TERM16: case __PBF_UNUSED_TERM18: case __PBF_UNUSED_TERM20: - case __PBF_UNUSED_TERM21: default: break; } diff --git a/tools/perf/util/bpf_skel/vmlinux/vmlinux.h b/tools/perf/util/bpf_skel/vmlinux/vmlinux.h index 4fa21468487e..4dcad7b682bd 100644 --- a/tools/perf/util/bpf_skel/vmlinux/vmlinux.h +++ b/tools/perf/util/bpf_skel/vmlinux/vmlinux.h @@ -171,6 +171,7 @@ struct perf_sample_data { u32 cpu; } cpu_entry; u64 phys_addr; + u64 cgroup; u64 data_page_size; u64 code_page_size; } __attribute__((__aligned__(64))) __attribute__((preserve_access_index)); From d56a4d56a25c6aa76d816f1ee1888d38cf654004 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 26 Aug 2024 15:10:45 -0700 Subject: [PATCH 246/347] perf test: Add 'perf record cgroup' filtering test $ sudo ./perf test filtering -vv 96: perf record sample filtering (by BPF) tests: --- start --- test child forked, pid 2966908 Checking BPF-filter privilege Basic bpf-filter test Basic bpf-filter test [Success] Failing bpf-filter test Failing bpf-filter test [Success] Group bpf-filter test Group bpf-filter test [Success] Multiple bpf-filter test Multiple bpf-filter test [Success] Cgroup bpf-filter test Cgroup bpf-filter test [Success] ---- end(0) ---- 96: perf record sample filtering (by BPF) tests : Ok Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Stephane Eranian Link: https://lore.kernel.org/r/20240826221045.1202305-5-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/record_bpf_filter.sh | 39 +++++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/tools/perf/tests/shell/record_bpf_filter.sh b/tools/perf/tests/shell/record_bpf_filter.sh index 4df33c15bfa7..1b58ccc1fd88 100755 --- a/tools/perf/tests/shell/record_bpf_filter.sh +++ b/tools/perf/tests/shell/record_bpf_filter.sh @@ -68,7 +68,7 @@ test_bpf_filter_fail() { # 'cpu' requires PERF_SAMPLE_CPU flag if ! perf record -e task-clock --filter 'cpu > 0' \ - -o /dev/null true 2>&1 | grep PERF_SAMPLE_CPU + -o /dev/null true 2>&1 | grep -q PERF_SAMPLE_CPU then echo "Failing bpf-filter test [Failed forbidden CPU]" err=1 @@ -98,7 +98,7 @@ test_bpf_filter_group() { fi if ! perf record -e task-clock --filter 'cpu > 0 || ip > 0' \ - -o /dev/null true 2>&1 | grep PERF_SAMPLE_CPU + -o /dev/null true 2>&1 | grep -q PERF_SAMPLE_CPU then echo "Group bpf-filter test [Failed forbidden CPU]" err=1 @@ -106,7 +106,7 @@ test_bpf_filter_group() { fi if ! perf record -e task-clock --filter 'period > 0 || code_pgsz > 4096' \ - -o /dev/null true 2>&1 | grep PERF_SAMPLE_CODE_PAGE_SIZE + -o /dev/null true 2>&1 | grep -q PERF_SAMPLE_CODE_PAGE_SIZE then echo "Group bpf-filter test [Failed forbidden CODE_PAGE_SIZE]" err=1 @@ -147,6 +147,35 @@ test_bpf_filter_multi() { echo "Multiple bpf-filter test [Success]" } +test_bpf_filter_cgroup() { + echo "Cgroup bpf-filter test" + + if ! perf record -e task-clock --filter 'cgroup == /' \ + -a --all-cgroups --synth=cgroup -o "${perfdata}" true 2> /dev/null + then + echo "Cgroup bpf-filter test [Skipped cgroup not supported]" + return + fi + + # 'cgroup' requires PERF_SAMPLE_CGROUP flag + if ! perf record -e task-clock --filter 'cgroup == /' \ + -o /dev/null true 2>&1 | grep -q PERF_SAMPLE_CGROUP + then + echo "Cgroup bpf-filter test [Failed CGROUP requires --all-cgroups]" + err=1 + return + fi + + if ! perf report -i "${perfdata}" -s cgroup -q | grep -q -F '100.00%' + then + echo "Cgroup bpf-filter test [Failed root cgroup does not have 100%]" + err=1 + return + fi + + echo "Cgroup bpf-filter test [Success]" +} + test_bpf_filter_priv if [ $err = 0 ]; then @@ -165,5 +194,9 @@ if [ $err = 0 ]; then test_bpf_filter_multi fi +if [ $err = 0 ]; then + test_bpf_filter_cgroup +fi + cleanup exit $err From 47b3b6435e4bfb61ae8ffc63a11bd3c310f69acf Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 28 Aug 2024 19:06:47 -0300 Subject: [PATCH 247/347] tools build: Remove leftover libcap tests that prevents fast path feature detection from working I noticed that the fast path feature detection was failing: $ cat /tmp/build/perf-tools-next/feature/test-all.make.output /usr/bin/ld: cannot find -lcap: No such file or directory collect2: error: ld returned 1 exit status $ The patch removing the dependency (Fixes tag below) didn't remove the detection of libcap, and as the fast path feature detection (test-all.c) had -lcap in its Makefile link list of libraries to link, it was failing when libcap-devel is not available, fix it by removing those leftover files. Fixes: e25ebda78e230283 ("perf cap: Tidy up and improve capability testing") Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/Zs-gjOGFWtAvIZit@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/feature/Makefile | 6 +----- tools/build/feature/test-libcap.c | 20 -------------------- 2 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 tools/build/feature/test-libcap.c diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index 12796808f07a..b873fc58804e 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -25,7 +25,6 @@ FILES= \ test-libbfd-liberty-z.bin \ test-cplus-demangle.bin \ test-cxa-demangle.bin \ - test-libcap.bin \ test-libelf.bin \ test-libelf-getphdrnum.bin \ test-libelf-gelf_getnote.bin \ @@ -112,7 +111,7 @@ all: $(FILES) __BUILD = $(CC) $(CFLAGS) -MD -Wall -Werror -o $@ $(patsubst %.bin,%.c,$(@F)) $(LDFLAGS) BUILD = $(__BUILD) > $(@:.bin=.make.output) 2>&1 BUILD_BFD = $(BUILD) -DPACKAGE='"perf"' -lbfd -ldl - BUILD_ALL = $(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -lslang $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz -llzma -lzstd -lcap + BUILD_ALL = $(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -lslang $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz -llzma -lzstd __BUILDXX = $(CXX) $(CXXFLAGS) -MD -Wall -Werror -o $@ $(patsubst %.bin,%.cpp,$(@F)) $(LDFLAGS) BUILDXX = $(__BUILDXX) > $(@:.bin=.make.output) 2>&1 @@ -140,9 +139,6 @@ $(OUTPUT)test-fortify-source.bin: $(OUTPUT)test-bionic.bin: $(BUILD) -$(OUTPUT)test-libcap.bin: - $(BUILD) -lcap - $(OUTPUT)test-libelf.bin: $(BUILD) -lelf diff --git a/tools/build/feature/test-libcap.c b/tools/build/feature/test-libcap.c deleted file mode 100644 index d2a2e152195f..000000000000 --- a/tools/build/feature/test-libcap.c +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -#include -#include - -int main(void) -{ - cap_flag_value_t val; - cap_t caps = cap_get_proc(); - - if (!caps) - return 1; - - if (cap_get_flag(caps, CAP_SYS_ADMIN, CAP_EFFECTIVE, &val) != 0) - return 1; - - if (cap_free(caps) != 0) - return 1; - - return 0; -} From 0fd77ae4a3c94eca3f07e01083680ad0056df628 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Thu, 29 Aug 2024 11:46:40 -0300 Subject: [PATCH 248/347] Revert "tools build: Remove leftover libcap tests that prevents fast path feature detection from working" Ian pointed out that the libcap feature test is also used by bpftool, so we can't remove it just because perf stopped using it, revert the removal of the feature test. Since both perf and libcap uses the fast path feature detection (tools/build/feature/test-all.c), probably the best thing is to keep libcap-devel when building perf even it not being used there. This reverts commit 47b3b6435e4bfb61ae8ffc63a11bd3c310f69acf. Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/feature/Makefile | 6 +++++- tools/build/feature/test-libcap.c | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tools/build/feature/test-libcap.c diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index b873fc58804e..12796808f07a 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -25,6 +25,7 @@ FILES= \ test-libbfd-liberty-z.bin \ test-cplus-demangle.bin \ test-cxa-demangle.bin \ + test-libcap.bin \ test-libelf.bin \ test-libelf-getphdrnum.bin \ test-libelf-gelf_getnote.bin \ @@ -111,7 +112,7 @@ all: $(FILES) __BUILD = $(CC) $(CFLAGS) -MD -Wall -Werror -o $@ $(patsubst %.bin,%.c,$(@F)) $(LDFLAGS) BUILD = $(__BUILD) > $(@:.bin=.make.output) 2>&1 BUILD_BFD = $(BUILD) -DPACKAGE='"perf"' -lbfd -ldl - BUILD_ALL = $(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -lslang $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz -llzma -lzstd + BUILD_ALL = $(BUILD) -fstack-protector-all -O2 -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -lslang $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz -llzma -lzstd -lcap __BUILDXX = $(CXX) $(CXXFLAGS) -MD -Wall -Werror -o $@ $(patsubst %.bin,%.cpp,$(@F)) $(LDFLAGS) BUILDXX = $(__BUILDXX) > $(@:.bin=.make.output) 2>&1 @@ -139,6 +140,9 @@ $(OUTPUT)test-fortify-source.bin: $(OUTPUT)test-bionic.bin: $(BUILD) +$(OUTPUT)test-libcap.bin: + $(BUILD) -lcap + $(OUTPUT)test-libelf.bin: $(BUILD) -lelf diff --git a/tools/build/feature/test-libcap.c b/tools/build/feature/test-libcap.c new file mode 100644 index 000000000000..d2a2e152195f --- /dev/null +++ b/tools/build/feature/test-libcap.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +int main(void) +{ + cap_flag_value_t val; + cap_t caps = cap_get_proc(); + + if (!caps) + return 1; + + if (cap_get_flag(caps, CAP_SYS_ADMIN, CAP_EFFECTIVE, &val) != 0) + return 1; + + if (cap_free(caps) != 0) + return 1; + + return 0; +} From b6aa0de9a53a231eb068ce1e62b5e7ec9e30e627 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 22 Jul 2024 11:11:43 +0100 Subject: [PATCH 249/347] perf cs-etm: Create decoders after both AUX and HW_ID search passes Both of these passes gather information about how to create the decoders. AUX records determine formatted/unformatted, and the HW_IDs determine the traceID/metadata mappings. Therefore it makes sense to cache the information and wait until both passes are over until creating the decoders, rather than creating them at the first HW_ID found. This will allow a simplification of the creation process where cs_etm_queue->traceid_list will exclusively used to create the decoders, rather than the current two methods depending on whether the trace is formatted or not. Previously the sample CPU from the AUX record was used to initialize the decoder CPU, but actually sample CPU == AUX queue index in per-CPU mode, so saving the sample CPU isn't required. Similarly formatted/unformatted was used upfront to create the decoders, but now it's cached until later. Reviewed-by: Anshuman Khandual Reviewed-by: Mike Leach Signed-off-by: James Clark Signed-off-by: James Clark Tested-by: Ganapatrao Kulkarni Tested-by: Leo Yan Acked-by: Suzuki Poulouse Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Alexandre Torgue Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Maxime Coquelin Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Will Deacon Link: https://lore.kernel.org/r/20240722101202.26915-2-james.clark@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cs-etm.c | 182 ++++++++++++++++++++++++--------------- 1 file changed, 113 insertions(+), 69 deletions(-) diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 34552940ec1d..0915a4356fb3 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -97,12 +97,19 @@ struct cs_etm_traceid_queue { struct cs_etm_packet_queue packet_queue; }; +enum cs_etm_format { + UNSET, + FORMATTED, + UNFORMATTED +}; + struct cs_etm_queue { struct cs_etm_auxtrace *etm; struct cs_etm_decoder *decoder; struct auxtrace_buffer *buffer; unsigned int queue_nr; u8 pending_timestamp_chan_id; + enum cs_etm_format format; u64 offset; const unsigned char *buf; size_t buf_len, buf_used; @@ -696,7 +703,7 @@ static void cs_etm__set_trace_param_ete(struct cs_etm_trace_params *t_params, static int cs_etm__init_trace_params(struct cs_etm_trace_params *t_params, struct cs_etm_auxtrace *etm, - bool formatted, + enum cs_etm_format format, int sample_cpu, int decoders) { @@ -705,7 +712,7 @@ static int cs_etm__init_trace_params(struct cs_etm_trace_params *t_params, u64 architecture; for (t_idx = 0; t_idx < decoders; t_idx++) { - if (formatted) + if (format == FORMATTED) m_idx = t_idx; else { m_idx = get_cpu_data_idx(etm, sample_cpu); @@ -738,8 +745,7 @@ static int cs_etm__init_trace_params(struct cs_etm_trace_params *t_params, static int cs_etm__init_decoder_params(struct cs_etm_decoder_params *d_params, struct cs_etm_queue *etmq, - enum cs_etm_decoder_operation mode, - bool formatted) + enum cs_etm_decoder_operation mode) { int ret = -EINVAL; @@ -749,7 +755,7 @@ static int cs_etm__init_decoder_params(struct cs_etm_decoder_params *d_params, d_params->packet_printer = cs_etm__packet_dump; d_params->operation = mode; d_params->data = etmq; - d_params->formatted = formatted; + d_params->formatted = etmq->format == FORMATTED; d_params->fsyncs = false; d_params->hsyncs = false; d_params->frame_aligned = true; @@ -1041,81 +1047,34 @@ out: return ret; } -static struct cs_etm_queue *cs_etm__alloc_queue(struct cs_etm_auxtrace *etm, - bool formatted, int sample_cpu) +static struct cs_etm_queue *cs_etm__alloc_queue(void) { - struct cs_etm_decoder_params d_params; - struct cs_etm_trace_params *t_params = NULL; - struct cs_etm_queue *etmq; - /* - * Each queue can only contain data from one CPU when unformatted, so only one decoder is - * needed. - */ - int decoders = formatted ? etm->num_cpu : 1; - - etmq = zalloc(sizeof(*etmq)); + struct cs_etm_queue *etmq = zalloc(sizeof(*etmq)); if (!etmq) return NULL; etmq->traceid_queues_list = intlist__new(NULL); if (!etmq->traceid_queues_list) - goto out_free; + free(etmq); - /* Use metadata to fill in trace parameters for trace decoder */ - t_params = zalloc(sizeof(*t_params) * decoders); - - if (!t_params) - goto out_free; - - if (cs_etm__init_trace_params(t_params, etm, formatted, sample_cpu, decoders)) - goto out_free; - - /* Set decoder parameters to decode trace packets */ - if (cs_etm__init_decoder_params(&d_params, etmq, - dump_trace ? CS_ETM_OPERATION_PRINT : - CS_ETM_OPERATION_DECODE, - formatted)) - goto out_free; - - etmq->decoder = cs_etm_decoder__new(decoders, &d_params, - t_params); - - if (!etmq->decoder) - goto out_free; - - /* - * Register a function to handle all memory accesses required by - * the trace decoder library. - */ - if (cs_etm_decoder__add_mem_access_cb(etmq->decoder, - 0x0L, ((u64) -1L), - cs_etm__mem_access)) - goto out_free_decoder; - - zfree(&t_params); return etmq; - -out_free_decoder: - cs_etm_decoder__free(etmq->decoder); -out_free: - intlist__delete(etmq->traceid_queues_list); - free(etmq); - - return NULL; } static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm, struct auxtrace_queue *queue, - unsigned int queue_nr, - bool formatted, - int sample_cpu) + unsigned int queue_nr, enum cs_etm_format format) { struct cs_etm_queue *etmq = queue->priv; + if (etmq && format != etmq->format) { + pr_err("CS_ETM: mixed formatted and unformatted trace not supported\n"); + return -EINVAL; + } + if (list_empty(&queue->head) || etmq) return 0; - etmq = cs_etm__alloc_queue(etm, formatted, sample_cpu); + etmq = cs_etm__alloc_queue(); if (!etmq) return -ENOMEM; @@ -1123,7 +1082,9 @@ static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm, queue->priv = etmq; etmq->etm = etm; etmq->queue_nr = queue_nr; + queue->cpu = queue_nr; /* Placeholder, may be reset to -1 in per-thread mode */ etmq->offset = 0; + etmq->format = format; return 0; } @@ -2818,7 +2779,7 @@ static int cs_etm__process_auxtrace_event(struct perf_session *session, * formatted in piped mode (true). */ err = cs_etm__setup_queue(etm, &etm->queues.queue_array[idx], - idx, true, -1); + idx, FORMATTED); if (err) return err; @@ -2939,7 +2900,7 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o union perf_event auxtrace_fragment; __u64 aux_offset, aux_size; __u32 idx; - bool formatted; + enum cs_etm_format format; struct cs_etm_auxtrace *etm = container_of(session->auxtrace, struct cs_etm_auxtrace, @@ -3022,9 +2983,10 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o return err; idx = auxtrace_event->idx; - formatted = !(aux_event->flags & PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW); - return cs_etm__setup_queue(etm, &etm->queues.queue_array[idx], - idx, formatted, sample->cpu); + format = (aux_event->flags & PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW) ? + UNFORMATTED : FORMATTED; + + return cs_etm__setup_queue(etm, &etm->queues.queue_array[idx], idx, format); } /* Wasn't inside this buffer, but there were no parse errors. 1 == 'not found' */ @@ -3208,6 +3170,84 @@ static int cs_etm__clear_unused_trace_ids_metadata(int num_cpu, u64 **metadata) return 0; } +/* + * Use the data gathered by the peeks for HW_ID (trace ID mappings) and AUX + * (formatted or not) packets to create the decoders. + */ +static int cs_etm__create_queue_decoders(struct cs_etm_queue *etmq) +{ + struct cs_etm_decoder_params d_params; + + /* + * Each queue can only contain data from one CPU when unformatted, so only one decoder is + * needed. + */ + int decoders = etmq->format == FORMATTED ? etmq->etm->num_cpu : 1; + + /* Use metadata to fill in trace parameters for trace decoder */ + struct cs_etm_trace_params *t_params = zalloc(sizeof(*t_params) * decoders); + + if (!t_params) + goto out_free; + + if (cs_etm__init_trace_params(t_params, etmq->etm, etmq->format, + etmq->queue_nr, decoders)) + goto out_free; + + /* Set decoder parameters to decode trace packets */ + if (cs_etm__init_decoder_params(&d_params, etmq, + dump_trace ? CS_ETM_OPERATION_PRINT : + CS_ETM_OPERATION_DECODE)) + goto out_free; + + etmq->decoder = cs_etm_decoder__new(decoders, &d_params, + t_params); + + if (!etmq->decoder) + goto out_free; + + /* + * Register a function to handle all memory accesses required by + * the trace decoder library. + */ + if (cs_etm_decoder__add_mem_access_cb(etmq->decoder, + 0x0L, ((u64) -1L), + cs_etm__mem_access)) + goto out_free_decoder; + + zfree(&t_params); + return 0; + +out_free_decoder: + cs_etm_decoder__free(etmq->decoder); +out_free: + zfree(&t_params); + return -EINVAL; +} + +static int cs_etm__create_decoders(struct cs_etm_auxtrace *etm) +{ + struct auxtrace_queues *queues = &etm->queues; + + for (unsigned int i = 0; i < queues->nr_queues; i++) { + bool empty = list_empty(&queues->queue_array[i].head); + struct cs_etm_queue *etmq = queues->queue_array[i].priv; + int ret; + + /* + * Don't create decoders for empty queues, mainly because + * etmq->format is unknown for empty queues. + */ + if (empty) + continue; + + ret = cs_etm__create_queue_decoders(etmq); + if (ret) + return ret; + } + return 0; +} + int cs_etm__process_auxtrace_info_full(union perf_event *event, struct perf_session *session) { @@ -3371,6 +3411,10 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, if (err) goto err_free_queues; + err = cs_etm__queue_aux_records(session); + if (err) + goto err_free_queues; + /* * Map Trace ID values to CPU metadata. * @@ -3393,7 +3437,7 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, * flags if present. */ - /* first scan for AUX_OUTPUT_HW_ID records to map trace ID values to CPU metadata */ + /* Scan for AUX_OUTPUT_HW_ID records to map trace ID values to CPU metadata */ aux_hw_id_found = 0; err = perf_session__peek_events(session, session->header.data_offset, session->header.data_size, @@ -3411,7 +3455,7 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, if (err) goto err_free_queues; - err = cs_etm__queue_aux_records(session); + err = cs_etm__create_decoders(etm); if (err) goto err_free_queues; From 57880a7966be510cdaa08ab76b9d63e3df786bf0 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 22 Jul 2024 11:11:44 +0100 Subject: [PATCH 250/347] perf: cs-etm: Allocate queues for all CPUs Make cs_etm__setup_queue() setup a queue even if it's empty, and pre-allocate queues based on the max CPU that was recorded. In per-CPU mode aux queues are indexed based on CPU ID even if all CPUs aren't recorded, sparse queue arrays aren't used. This will allow HW_IDs to be saved even if no aux data was received in that queue without having to call cs_etm__setup_queue() from two different places. Reviewed-by: Mike Leach Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Alexandre Torgue Cc: Anshuman Khandual Cc: Ganapatrao Kulkarni Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Maxime Coquelin Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Suzuki Poulouse Cc: Will Deacon Link: https://lore.kernel.org/r/20240722101202.26915-3-james.clark@linaro.org Signed-off-by: James Clark Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cs-etm.c | 53 +++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 0915a4356fb3..03df8894a900 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -1062,16 +1062,11 @@ static struct cs_etm_queue *cs_etm__alloc_queue(void) static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm, struct auxtrace_queue *queue, - unsigned int queue_nr, enum cs_etm_format format) + unsigned int queue_nr) { struct cs_etm_queue *etmq = queue->priv; - if (etmq && format != etmq->format) { - pr_err("CS_ETM: mixed formatted and unformatted trace not supported\n"); - return -EINVAL; - } - - if (list_empty(&queue->head) || etmq) + if (etmq) return 0; etmq = cs_etm__alloc_queue(); @@ -1084,7 +1079,6 @@ static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm, etmq->queue_nr = queue_nr; queue->cpu = queue_nr; /* Placeholder, may be reset to -1 in per-thread mode */ etmq->offset = 0; - etmq->format = format; return 0; } @@ -2772,17 +2766,6 @@ static int cs_etm__process_auxtrace_event(struct perf_session *session, if (err) return err; - /* - * Knowing if the trace is formatted or not requires a lookup of - * the aux record so only works in non-piped mode where data is - * queued in cs_etm__queue_aux_records(). Always assume - * formatted in piped mode (true). - */ - err = cs_etm__setup_queue(etm, &etm->queues.queue_array[idx], - idx, FORMATTED); - if (err) - return err; - if (dump_trace) if (auxtrace_buffer__get_data(buffer, fd)) { cs_etm__dump_event(etm->queues.queue_array[idx].priv, buffer); @@ -2899,7 +2882,6 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o struct perf_record_auxtrace *auxtrace_event; union perf_event auxtrace_fragment; __u64 aux_offset, aux_size; - __u32 idx; enum cs_etm_format format; struct cs_etm_auxtrace *etm = container_of(session->auxtrace, @@ -2966,6 +2948,8 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o if (aux_offset >= auxtrace_event->offset && aux_offset + aux_size <= auxtrace_event->offset + auxtrace_event->size) { + struct cs_etm_queue *etmq = etm->queues.queue_array[auxtrace_event->idx].priv; + /* * If this AUX event was inside this buffer somewhere, create a new auxtrace event * based on the sizes of the aux event, and queue that fragment. @@ -2982,11 +2966,14 @@ static int cs_etm__queue_aux_fragment(struct perf_session *session, off_t file_o if (err) return err; - idx = auxtrace_event->idx; format = (aux_event->flags & PERF_AUX_FLAG_CORESIGHT_FORMAT_RAW) ? UNFORMATTED : FORMATTED; - - return cs_etm__setup_queue(etm, &etm->queues.queue_array[idx], idx, format); + if (etmq->format != UNSET && format != etmq->format) { + pr_err("CS_ETM: mixed formatted and unformatted trace not supported\n"); + return -EINVAL; + } + etmq->format = format; + return 0; } /* Wasn't inside this buffer, but there were no parse errors. 1 == 'not found' */ @@ -3238,6 +3225,7 @@ static int cs_etm__create_decoders(struct cs_etm_auxtrace *etm) * Don't create decoders for empty queues, mainly because * etmq->format is unknown for empty queues. */ + assert(empty == (etmq->format == UNSET)); if (empty) continue; @@ -3257,10 +3245,10 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, int event_header_size = sizeof(struct perf_event_header); int total_size = auxtrace_info->header.size; int priv_size = 0; - int num_cpu; + int num_cpu, max_cpu = 0; int err = 0; int aux_hw_id_found; - int i, j; + int i; u64 *ptr = NULL; u64 **metadata = NULL; @@ -3291,7 +3279,7 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, * required by the trace decoder to properly decode the trace due * to its highly compressed nature. */ - for (j = 0; j < num_cpu; j++) { + for (int j = 0; j < num_cpu; j++) { if (ptr[i] == __perf_cs_etmv3_magic) { metadata[j] = cs_etm__create_meta_blk(ptr, &i, @@ -3315,6 +3303,9 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, err = -ENOMEM; goto err_free_metadata; } + + if ((int) metadata[j][CS_ETM_CPU] > max_cpu) + max_cpu = metadata[j][CS_ETM_CPU]; } /* @@ -3344,10 +3335,16 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, */ etm->pid_fmt = cs_etm__init_pid_fmt(metadata[0]); - err = auxtrace_queues__init(&etm->queues); + err = auxtrace_queues__init_nr(&etm->queues, max_cpu + 1); if (err) goto err_free_etm; + for (unsigned int j = 0; j < etm->queues.nr_queues; ++j) { + err = cs_etm__setup_queue(etm, &etm->queues.queue_array[j], j); + if (err) + goto err_free_queues; + } + if (session->itrace_synth_opts->set) { etm->synth_opts = *session->itrace_synth_opts; } else { @@ -3469,7 +3466,7 @@ err_free_etm: zfree(&etm); err_free_metadata: /* No need to check @metadata[j], free(NULL) is supported */ - for (j = 0; j < num_cpu; j++) + for (int j = 0; j < num_cpu; j++) zfree(&metadata[j]); zfree(&metadata); err_free_traceid_list: From 77c123f53e97ad4bde0271eb671b71774a99ebf6 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 22 Jul 2024 11:11:45 +0100 Subject: [PATCH 251/347] perf: cs-etm: Move traceid_list to each queue The global list won't work for per-sink trace ID allocations, so put a list in each queue where the IDs will be unique to that queue. To keep the same behavior as before, for version 0 of the HW_ID packets, copy all the HW_ID mappings into all queues. This change doesn't effect the decoders, only trace ID lookups on the Perf side. The decoders are still created with global mappings which will be fixed in a later commit. Reviewed-by: Mike Leach Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Alexandre Torgue Cc: Anshuman Khandual Cc: Ganapatrao Kulkarni Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Maxime Coquelin Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Suzuki Poulouse Cc: Will Deacon Link: https://lore.kernel.org/r/20240722101202.26915-4-james.clark@linaro.org Signed-off-by: James Clark Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/util/cs-etm-decoder/cs-etm-decoder.c | 28 ++- tools/perf/util/cs-etm.c | 217 +++++++++++------- tools/perf/util/cs-etm.h | 2 +- 3 files changed, 148 insertions(+), 99 deletions(-) diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c index e917985bbbe6..0c9c48cedbf1 100644 --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c @@ -388,7 +388,8 @@ cs_etm_decoder__reset_timestamp(struct cs_etm_packet_queue *packet_queue) } static ocsd_datapath_resp_t -cs_etm_decoder__buffer_packet(struct cs_etm_packet_queue *packet_queue, +cs_etm_decoder__buffer_packet(struct cs_etm_queue *etmq, + struct cs_etm_packet_queue *packet_queue, const u8 trace_chan_id, enum cs_etm_sample_type sample_type) { @@ -398,7 +399,7 @@ cs_etm_decoder__buffer_packet(struct cs_etm_packet_queue *packet_queue, if (packet_queue->packet_count >= CS_ETM_PACKET_MAX_BUFFER - 1) return OCSD_RESP_FATAL_SYS_ERR; - if (cs_etm__get_cpu(trace_chan_id, &cpu) < 0) + if (cs_etm__get_cpu(etmq, trace_chan_id, &cpu) < 0) return OCSD_RESP_FATAL_SYS_ERR; et = packet_queue->tail; @@ -436,7 +437,7 @@ cs_etm_decoder__buffer_range(struct cs_etm_queue *etmq, int ret = 0; struct cs_etm_packet *packet; - ret = cs_etm_decoder__buffer_packet(packet_queue, trace_chan_id, + ret = cs_etm_decoder__buffer_packet(etmq, packet_queue, trace_chan_id, CS_ETM_RANGE); if (ret != OCSD_RESP_CONT && ret != OCSD_RESP_WAIT) return ret; @@ -496,7 +497,8 @@ out: } static ocsd_datapath_resp_t -cs_etm_decoder__buffer_discontinuity(struct cs_etm_packet_queue *queue, +cs_etm_decoder__buffer_discontinuity(struct cs_etm_queue *etmq, + struct cs_etm_packet_queue *queue, const uint8_t trace_chan_id) { /* @@ -504,18 +506,19 @@ cs_etm_decoder__buffer_discontinuity(struct cs_etm_packet_queue *queue, * reset time statistics. */ cs_etm_decoder__reset_timestamp(queue); - return cs_etm_decoder__buffer_packet(queue, trace_chan_id, + return cs_etm_decoder__buffer_packet(etmq, queue, trace_chan_id, CS_ETM_DISCONTINUITY); } static ocsd_datapath_resp_t -cs_etm_decoder__buffer_exception(struct cs_etm_packet_queue *queue, +cs_etm_decoder__buffer_exception(struct cs_etm_queue *etmq, + struct cs_etm_packet_queue *queue, const ocsd_generic_trace_elem *elem, const uint8_t trace_chan_id) { int ret = 0; struct cs_etm_packet *packet; - ret = cs_etm_decoder__buffer_packet(queue, trace_chan_id, + ret = cs_etm_decoder__buffer_packet(etmq, queue, trace_chan_id, CS_ETM_EXCEPTION); if (ret != OCSD_RESP_CONT && ret != OCSD_RESP_WAIT) return ret; @@ -527,10 +530,11 @@ cs_etm_decoder__buffer_exception(struct cs_etm_packet_queue *queue, } static ocsd_datapath_resp_t -cs_etm_decoder__buffer_exception_ret(struct cs_etm_packet_queue *queue, +cs_etm_decoder__buffer_exception_ret(struct cs_etm_queue *etmq, + struct cs_etm_packet_queue *queue, const uint8_t trace_chan_id) { - return cs_etm_decoder__buffer_packet(queue, trace_chan_id, + return cs_etm_decoder__buffer_packet(etmq, queue, trace_chan_id, CS_ETM_EXCEPTION_RET); } @@ -599,7 +603,7 @@ static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer( case OCSD_GEN_TRC_ELEM_EO_TRACE: case OCSD_GEN_TRC_ELEM_NO_SYNC: case OCSD_GEN_TRC_ELEM_TRACE_ON: - resp = cs_etm_decoder__buffer_discontinuity(packet_queue, + resp = cs_etm_decoder__buffer_discontinuity(etmq, packet_queue, trace_chan_id); break; case OCSD_GEN_TRC_ELEM_INSTR_RANGE: @@ -607,11 +611,11 @@ static ocsd_datapath_resp_t cs_etm_decoder__gen_trace_elem_printer( trace_chan_id); break; case OCSD_GEN_TRC_ELEM_EXCEPTION: - resp = cs_etm_decoder__buffer_exception(packet_queue, elem, + resp = cs_etm_decoder__buffer_exception(etmq, packet_queue, elem, trace_chan_id); break; case OCSD_GEN_TRC_ELEM_EXCEPTION_RET: - resp = cs_etm_decoder__buffer_exception_ret(packet_queue, + resp = cs_etm_decoder__buffer_exception_ret(etmq, packet_queue, trace_chan_id); break; case OCSD_GEN_TRC_ELEM_TIMESTAMP: diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 03df8894a900..0f56618fc7a8 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -116,16 +116,18 @@ struct cs_etm_queue { /* Conversion between traceID and index in traceid_queues array */ struct intlist *traceid_queues_list; struct cs_etm_traceid_queue **traceid_queues; + /* Conversion between traceID and metadata pointers */ + struct intlist *traceid_list; }; -/* RB tree for quick conversion between traceID and metadata pointers */ -static struct intlist *traceid_list; - static int cs_etm__process_timestamped_queues(struct cs_etm_auxtrace *etm); static int cs_etm__process_timeless_queues(struct cs_etm_auxtrace *etm, pid_t tid); static int cs_etm__get_data_block(struct cs_etm_queue *etmq); static int cs_etm__decode_data_block(struct cs_etm_queue *etmq); +static int cs_etm__metadata_get_trace_id(u8 *trace_chan_id, u64 *cpu_metadata); +static u64 *get_cpu_data(struct cs_etm_auxtrace *etm, int cpu); +static int cs_etm__metadata_set_trace_id(u8 trace_chan_id, u64 *cpu_metadata); /* PTMs ETMIDR [11:8] set to b0011 */ #define ETMIDR_PTM_VERSION 0x00000300 @@ -151,12 +153,12 @@ static u32 cs_etm__get_v7_protocol_version(u32 etmidr) return CS_ETM_PROTO_ETMV3; } -static int cs_etm__get_magic(u8 trace_chan_id, u64 *magic) +static int cs_etm__get_magic(struct cs_etm_queue *etmq, u8 trace_chan_id, u64 *magic) { struct int_node *inode; u64 *metadata; - inode = intlist__find(traceid_list, trace_chan_id); + inode = intlist__find(etmq->traceid_list, trace_chan_id); if (!inode) return -EINVAL; @@ -165,12 +167,12 @@ static int cs_etm__get_magic(u8 trace_chan_id, u64 *magic) return 0; } -int cs_etm__get_cpu(u8 trace_chan_id, int *cpu) +int cs_etm__get_cpu(struct cs_etm_queue *etmq, u8 trace_chan_id, int *cpu) { struct int_node *inode; u64 *metadata; - inode = intlist__find(traceid_list, trace_chan_id); + inode = intlist__find(etmq->traceid_list, trace_chan_id); if (!inode) return -EINVAL; @@ -222,30 +224,108 @@ enum cs_etm_pid_fmt cs_etm__get_pid_fmt(struct cs_etm_queue *etmq) return etmq->etm->pid_fmt; } -static int cs_etm__map_trace_id(u8 trace_chan_id, u64 *cpu_metadata) +static int cs_etm__insert_trace_id_node(struct cs_etm_queue *etmq, + u8 trace_chan_id, u64 *cpu_metadata) { - struct int_node *inode; - /* Get an RB node for this CPU */ - inode = intlist__findnew(traceid_list, trace_chan_id); + struct int_node *inode = intlist__findnew(etmq->traceid_list, trace_chan_id); /* Something went wrong, no need to continue */ if (!inode) return -ENOMEM; - /* - * The node for that CPU should not be taken. - * Back out if that's the case. - */ - if (inode->priv) - return -EINVAL; + /* Disallow re-mapping a different traceID to metadata pair. */ + if (inode->priv) { + u64 *curr_cpu_data = inode->priv; + u8 curr_chan_id; + int err; - /* All good, associate the traceID with the metadata pointer */ + if (curr_cpu_data[CS_ETM_CPU] != cpu_metadata[CS_ETM_CPU]) { + pr_err("CS_ETM: map mismatch between HW_ID packet CPU and Trace ID\n"); + return -EINVAL; + } + + /* check that the mapped ID matches */ + err = cs_etm__metadata_get_trace_id(&curr_chan_id, curr_cpu_data); + if (err) + return err; + + if (curr_chan_id != trace_chan_id) { + pr_err("CS_ETM: mismatch between CPU trace ID and HW_ID packet ID\n"); + return -EINVAL; + } + + /* Skip re-adding the same mappings if everything matched */ + return 0; + } + + /* Not one we've seen before, associate the traceID with the metadata pointer */ inode->priv = cpu_metadata; return 0; } +static struct cs_etm_queue *cs_etm__get_queue(struct cs_etm_auxtrace *etm, int cpu) +{ + if (etm->per_thread_decoding) + return etm->queues.queue_array[0].priv; + else + return etm->queues.queue_array[cpu].priv; +} + +static int cs_etm__map_trace_id_v0(struct cs_etm_auxtrace *etm, u8 trace_chan_id, + u64 *cpu_metadata) +{ + struct cs_etm_queue *etmq; + + /* + * If the queue is unformatted then only save one mapping in the + * queue associated with that CPU so only one decoder is made. + */ + etmq = cs_etm__get_queue(etm, cpu_metadata[CS_ETM_CPU]); + if (etmq->format == UNFORMATTED) + return cs_etm__insert_trace_id_node(etmq, trace_chan_id, + cpu_metadata); + + /* + * Otherwise, version 0 trace IDs are global so save them into every + * queue. + */ + for (unsigned int i = 0; i < etm->queues.nr_queues; ++i) { + int ret; + + etmq = etm->queues.queue_array[i].priv; + ret = cs_etm__insert_trace_id_node(etmq, trace_chan_id, + cpu_metadata); + if (ret) + return ret; + } + + return 0; +} + +static int cs_etm__process_trace_id_v0(struct cs_etm_auxtrace *etm, int cpu, + u64 hw_id) +{ + int err; + u64 *cpu_data; + u8 trace_chan_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id); + + cpu_data = get_cpu_data(etm, cpu); + if (cpu_data == NULL) + return -EINVAL; + + err = cs_etm__map_trace_id_v0(etm, trace_chan_id, cpu_data); + if (err) + return err; + + /* + * if we are picking up the association from the packet, need to plug + * the correct trace ID into the metadata for setting up decoders later. + */ + return cs_etm__metadata_set_trace_id(trace_chan_id, cpu_data); +} + static int cs_etm__metadata_get_trace_id(u8 *trace_chan_id, u64 *cpu_metadata) { u64 cs_etm_magic = cpu_metadata[CS_ETM_MAGIC]; @@ -329,17 +409,13 @@ static int cs_etm__process_aux_output_hw_id(struct perf_session *session, { struct cs_etm_auxtrace *etm; struct perf_sample sample; - struct int_node *inode; struct evsel *evsel; - u64 *cpu_data; u64 hw_id; int cpu, version, err; - u8 trace_chan_id, curr_chan_id; /* extract and parse the HW ID */ hw_id = event->aux_output_hw_id.hw_id; version = FIELD_GET(CS_AUX_HW_ID_VERSION_MASK, hw_id); - trace_chan_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id); /* check that we can handle this version */ if (version > CS_AUX_HW_ID_CURR_VERSION) { @@ -367,43 +443,7 @@ static int cs_etm__process_aux_output_hw_id(struct perf_session *session, return -EINVAL; } - /* See if the ID is mapped to a CPU, and it matches the current CPU */ - inode = intlist__find(traceid_list, trace_chan_id); - if (inode) { - cpu_data = inode->priv; - if ((int)cpu_data[CS_ETM_CPU] != cpu) { - pr_err("CS_ETM: map mismatch between HW_ID packet CPU and Trace ID\n"); - return -EINVAL; - } - - /* check that the mapped ID matches */ - err = cs_etm__metadata_get_trace_id(&curr_chan_id, cpu_data); - if (err) - return err; - if (curr_chan_id != trace_chan_id) { - pr_err("CS_ETM: mismatch between CPU trace ID and HW_ID packet ID\n"); - return -EINVAL; - } - - /* mapped and matched - return OK */ - return 0; - } - - cpu_data = get_cpu_data(etm, cpu); - if (cpu_data == NULL) - return err; - - /* not one we've seen before - lets map it */ - err = cs_etm__map_trace_id(trace_chan_id, cpu_data); - if (err) - return err; - - /* - * if we are picking up the association from the packet, need to plug - * the correct trace ID into the metadata for setting up decoders later. - */ - err = cs_etm__metadata_set_trace_id(trace_chan_id, cpu_data); - return err; + return cs_etm__process_trace_id_v0(etm, cpu, hw_id); } void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq, @@ -856,6 +896,7 @@ static void cs_etm__free_traceid_queues(struct cs_etm_queue *etmq) static void cs_etm__free_queue(void *priv) { + struct int_node *inode, *tmp; struct cs_etm_queue *etmq = priv; if (!etmq) @@ -863,6 +904,14 @@ static void cs_etm__free_queue(void *priv) cs_etm_decoder__free(etmq->decoder); cs_etm__free_traceid_queues(etmq); + + /* First remove all traceID/metadata nodes for the RB tree */ + intlist__for_each_entry_safe(inode, tmp, etmq->traceid_list) + intlist__remove(etmq->traceid_list, inode); + + /* Then the RB tree itself */ + intlist__delete(etmq->traceid_list); + free(etmq); } @@ -885,19 +934,12 @@ static void cs_etm__free_events(struct perf_session *session) static void cs_etm__free(struct perf_session *session) { int i; - struct int_node *inode, *tmp; struct cs_etm_auxtrace *aux = container_of(session->auxtrace, struct cs_etm_auxtrace, auxtrace); cs_etm__free_events(session); session->auxtrace = NULL; - /* First remove all traceID/metadata nodes for the RB tree */ - intlist__for_each_entry_safe(inode, tmp, traceid_list) - intlist__remove(traceid_list, inode); - /* Then the RB tree itself */ - intlist__delete(traceid_list); - for (i = 0; i < aux->num_cpu; i++) zfree(&aux->metadata[i]); @@ -1055,9 +1097,24 @@ static struct cs_etm_queue *cs_etm__alloc_queue(void) etmq->traceid_queues_list = intlist__new(NULL); if (!etmq->traceid_queues_list) - free(etmq); + goto out_free; + + /* + * Create an RB tree for traceID-metadata tuple. Since the conversion + * has to be made for each packet that gets decoded, optimizing access + * in anything other than a sequential array is worth doing. + */ + etmq->traceid_list = intlist__new(NULL); + if (!etmq->traceid_list) + goto out_free; return etmq; + +out_free: + intlist__delete(etmq->traceid_queues_list); + free(etmq); + + return NULL; } static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm, @@ -2182,7 +2239,7 @@ static int cs_etm__set_sample_flags(struct cs_etm_queue *etmq, PERF_IP_FLAG_TRACE_END; break; case CS_ETM_EXCEPTION: - ret = cs_etm__get_magic(packet->trace_chan_id, &magic); + ret = cs_etm__get_magic(etmq, packet->trace_chan_id, &magic); if (ret) return ret; @@ -3099,7 +3156,8 @@ static bool cs_etm__has_virtual_ts(u64 **metadata, int num_cpu) } /* map trace ids to correct metadata block, from information in metadata */ -static int cs_etm__map_trace_ids_metadata(int num_cpu, u64 **metadata) +static int cs_etm__map_trace_ids_metadata(struct cs_etm_auxtrace *etm, int num_cpu, + u64 **metadata) { u64 cs_etm_magic; u8 trace_chan_id; @@ -3121,7 +3179,7 @@ static int cs_etm__map_trace_ids_metadata(int num_cpu, u64 **metadata) /* unknown magic number */ return -EINVAL; } - err = cs_etm__map_trace_id(trace_chan_id, metadata[i]); + err = cs_etm__map_trace_id_v0(etm, trace_chan_id, metadata[i]); if (err) return err; } @@ -3252,23 +3310,12 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, u64 *ptr = NULL; u64 **metadata = NULL; - /* - * Create an RB tree for traceID-metadata tuple. Since the conversion - * has to be made for each packet that gets decoded, optimizing access - * in anything other than a sequential array is worth doing. - */ - traceid_list = intlist__new(NULL); - if (!traceid_list) - return -ENOMEM; - /* First the global part */ ptr = (u64 *) auxtrace_info->priv; num_cpu = ptr[CS_PMU_TYPE_CPUS] & 0xffffffff; metadata = zalloc(sizeof(*metadata) * num_cpu); - if (!metadata) { - err = -ENOMEM; - goto err_free_traceid_list; - } + if (!metadata) + return -ENOMEM; /* Start parsing after the common part of the header */ i = CS_HEADER_VERSION_MAX; @@ -3447,7 +3494,7 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, err = cs_etm__clear_unused_trace_ids_metadata(num_cpu, metadata); /* otherwise, this is a file with metadata values only, map from metadata */ else - err = cs_etm__map_trace_ids_metadata(num_cpu, metadata); + err = cs_etm__map_trace_ids_metadata(etm, num_cpu, metadata); if (err) goto err_free_queues; @@ -3469,7 +3516,5 @@ err_free_metadata: for (int j = 0; j < num_cpu; j++) zfree(&metadata[j]); zfree(&metadata); -err_free_traceid_list: - intlist__delete(traceid_list); return err; } diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h index 4696267a32f0..f4f69f7cc0f3 100644 --- a/tools/perf/util/cs-etm.h +++ b/tools/perf/util/cs-etm.h @@ -252,7 +252,7 @@ enum cs_etm_pid_fmt { #ifdef HAVE_CSTRACE_SUPPORT #include -int cs_etm__get_cpu(u8 trace_chan_id, int *cpu); +int cs_etm__get_cpu(struct cs_etm_queue *etmq, u8 trace_chan_id, int *cpu); enum cs_etm_pid_fmt cs_etm__get_pid_fmt(struct cs_etm_queue *etmq); int cs_etm__etmq_set_tid_el(struct cs_etm_queue *etmq, pid_t tid, u8 trace_chan_id, ocsd_ex_level el); From 19c3e4db38c5bf30c7e7b53dad5a464d7031dec4 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 22 Jul 2024 11:11:46 +0100 Subject: [PATCH 252/347] perf: cs-etm: Create decoders based on the trace ID mappings Now that each queue has a unique set of trace ID mappings, use this list to create the decoders. In unformatted mode just add a single mapping so only one decoder is made. Previously each queue would have a decoder created for each traced CPU on the system but this won't work anymore because CPUs can have overlapping trace IDs. This also means that the CORESIGHT_TRACE_ID_UNUSED_FLAG isn't needed any more. If mappings aren't added then decoders aren't created, rather than needing a flag to suppress creation. Reviewed-by: Mike Leach Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Alexandre Torgue Cc: Anshuman Khandual Cc: Ganapatrao Kulkarni Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Maxime Coquelin Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Suzuki Poulouse Cc: Will Deacon Link: https://lore.kernel.org/r/20240722101202.26915-5-james.clark@linaro.org Signed-off-by: James Clark Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm/util/cs-etm.c | 8 +- .../perf/util/cs-etm-decoder/cs-etm-decoder.c | 4 - tools/perf/util/cs-etm.c | 153 ++++++------------ tools/perf/util/cs-etm.h | 10 -- 4 files changed, 54 insertions(+), 121 deletions(-) diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c index 96aeb7cdbee1..3bbc1e57d71d 100644 --- a/tools/perf/arch/arm/util/cs-etm.c +++ b/tools/perf/arch/arm/util/cs-etm.c @@ -654,8 +654,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 +686,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 +742,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]); diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c index 0c9c48cedbf1..d49c3e9c7c21 100644 --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c @@ -684,10 +684,6 @@ cs_etm_decoder__create_etm_decoder(struct cs_etm_decoder_params *d_params, return -1; } - /* if the CPU has no trace ID associated, no decoder needed */ - if (csid == CORESIGHT_TRACE_ID_UNUSED_VAL) - return 0; - if (d_params->operation == CS_ETM_OPERATION_DECODE) { if (ocsd_dt_create_decoder(decoder->dcd_tree, decoder->decoder_name, diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 0f56618fc7a8..01c60a660fb3 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -348,7 +348,6 @@ static int cs_etm__metadata_get_trace_id(u8 *trace_chan_id, u64 *cpu_metadata) /* * update metadata trace ID from the value found in the AUX_HW_INFO packet. - * This will also clear the CORESIGHT_TRACE_ID_UNUSED_FLAG flag if present. */ static int cs_etm__metadata_set_trace_id(u8 trace_chan_id, u64 *cpu_metadata) { @@ -700,80 +699,58 @@ static void cs_etm__packet_dump(const char *pkt_string) } static void cs_etm__set_trace_param_etmv3(struct cs_etm_trace_params *t_params, - struct cs_etm_auxtrace *etm, int t_idx, - int m_idx, u32 etmidr) + u64 *metadata, u32 etmidr) { - u64 **metadata = etm->metadata; - - t_params[t_idx].protocol = cs_etm__get_v7_protocol_version(etmidr); - t_params[t_idx].etmv3.reg_ctrl = metadata[m_idx][CS_ETM_ETMCR]; - t_params[t_idx].etmv3.reg_trc_id = metadata[m_idx][CS_ETM_ETMTRACEIDR]; + t_params->protocol = cs_etm__get_v7_protocol_version(etmidr); + t_params->etmv3.reg_ctrl = metadata[CS_ETM_ETMCR]; + t_params->etmv3.reg_trc_id = metadata[CS_ETM_ETMTRACEIDR]; } static void cs_etm__set_trace_param_etmv4(struct cs_etm_trace_params *t_params, - struct cs_etm_auxtrace *etm, int t_idx, - int m_idx) + u64 *metadata) { - u64 **metadata = etm->metadata; - - t_params[t_idx].protocol = CS_ETM_PROTO_ETMV4i; - t_params[t_idx].etmv4.reg_idr0 = metadata[m_idx][CS_ETMV4_TRCIDR0]; - t_params[t_idx].etmv4.reg_idr1 = metadata[m_idx][CS_ETMV4_TRCIDR1]; - t_params[t_idx].etmv4.reg_idr2 = metadata[m_idx][CS_ETMV4_TRCIDR2]; - t_params[t_idx].etmv4.reg_idr8 = metadata[m_idx][CS_ETMV4_TRCIDR8]; - t_params[t_idx].etmv4.reg_configr = metadata[m_idx][CS_ETMV4_TRCCONFIGR]; - t_params[t_idx].etmv4.reg_traceidr = metadata[m_idx][CS_ETMV4_TRCTRACEIDR]; + t_params->protocol = CS_ETM_PROTO_ETMV4i; + t_params->etmv4.reg_idr0 = metadata[CS_ETMV4_TRCIDR0]; + t_params->etmv4.reg_idr1 = metadata[CS_ETMV4_TRCIDR1]; + t_params->etmv4.reg_idr2 = metadata[CS_ETMV4_TRCIDR2]; + t_params->etmv4.reg_idr8 = metadata[CS_ETMV4_TRCIDR8]; + t_params->etmv4.reg_configr = metadata[CS_ETMV4_TRCCONFIGR]; + t_params->etmv4.reg_traceidr = metadata[CS_ETMV4_TRCTRACEIDR]; } static void cs_etm__set_trace_param_ete(struct cs_etm_trace_params *t_params, - struct cs_etm_auxtrace *etm, int t_idx, - int m_idx) + u64 *metadata) { - u64 **metadata = etm->metadata; - - t_params[t_idx].protocol = CS_ETM_PROTO_ETE; - t_params[t_idx].ete.reg_idr0 = metadata[m_idx][CS_ETE_TRCIDR0]; - t_params[t_idx].ete.reg_idr1 = metadata[m_idx][CS_ETE_TRCIDR1]; - t_params[t_idx].ete.reg_idr2 = metadata[m_idx][CS_ETE_TRCIDR2]; - t_params[t_idx].ete.reg_idr8 = metadata[m_idx][CS_ETE_TRCIDR8]; - t_params[t_idx].ete.reg_configr = metadata[m_idx][CS_ETE_TRCCONFIGR]; - t_params[t_idx].ete.reg_traceidr = metadata[m_idx][CS_ETE_TRCTRACEIDR]; - t_params[t_idx].ete.reg_devarch = metadata[m_idx][CS_ETE_TRCDEVARCH]; + t_params->protocol = CS_ETM_PROTO_ETE; + t_params->ete.reg_idr0 = metadata[CS_ETE_TRCIDR0]; + t_params->ete.reg_idr1 = metadata[CS_ETE_TRCIDR1]; + t_params->ete.reg_idr2 = metadata[CS_ETE_TRCIDR2]; + t_params->ete.reg_idr8 = metadata[CS_ETE_TRCIDR8]; + t_params->ete.reg_configr = metadata[CS_ETE_TRCCONFIGR]; + t_params->ete.reg_traceidr = metadata[CS_ETE_TRCTRACEIDR]; + t_params->ete.reg_devarch = metadata[CS_ETE_TRCDEVARCH]; } static int cs_etm__init_trace_params(struct cs_etm_trace_params *t_params, - struct cs_etm_auxtrace *etm, - enum cs_etm_format format, - int sample_cpu, - int decoders) + struct cs_etm_queue *etmq) { - int t_idx, m_idx; - u32 etmidr; - u64 architecture; + struct int_node *inode; - for (t_idx = 0; t_idx < decoders; t_idx++) { - if (format == FORMATTED) - m_idx = t_idx; - else { - m_idx = get_cpu_data_idx(etm, sample_cpu); - if (m_idx == -1) { - pr_warning("CS_ETM: unknown CPU, falling back to first metadata\n"); - m_idx = 0; - } - } - - architecture = etm->metadata[m_idx][CS_ETM_MAGIC]; + intlist__for_each_entry(inode, etmq->traceid_list) { + u64 *metadata = inode->priv; + u64 architecture = metadata[CS_ETM_MAGIC]; + u32 etmidr; switch (architecture) { case __perf_cs_etmv3_magic: - etmidr = etm->metadata[m_idx][CS_ETM_ETMIDR]; - cs_etm__set_trace_param_etmv3(t_params, etm, t_idx, m_idx, etmidr); + etmidr = metadata[CS_ETM_ETMIDR]; + cs_etm__set_trace_param_etmv3(t_params++, metadata, etmidr); break; case __perf_cs_etmv4_magic: - cs_etm__set_trace_param_etmv4(t_params, etm, t_idx, m_idx); + cs_etm__set_trace_param_etmv4(t_params++, metadata); break; case __perf_cs_ete_magic: - cs_etm__set_trace_param_ete(t_params, etm, t_idx, m_idx); + cs_etm__set_trace_param_ete(t_params++, metadata); break; default: return -EINVAL; @@ -3186,35 +3163,6 @@ static int cs_etm__map_trace_ids_metadata(struct cs_etm_auxtrace *etm, int num_c return 0; } -/* - * If we found AUX_HW_ID packets, then set any metadata marked as unused to the - * unused value to reduce the number of unneeded decoders created. - */ -static int cs_etm__clear_unused_trace_ids_metadata(int num_cpu, u64 **metadata) -{ - u64 cs_etm_magic; - int i; - - for (i = 0; i < num_cpu; i++) { - cs_etm_magic = metadata[i][CS_ETM_MAGIC]; - switch (cs_etm_magic) { - case __perf_cs_etmv3_magic: - if (metadata[i][CS_ETM_ETMTRACEIDR] & CORESIGHT_TRACE_ID_UNUSED_FLAG) - metadata[i][CS_ETM_ETMTRACEIDR] = CORESIGHT_TRACE_ID_UNUSED_VAL; - break; - case __perf_cs_etmv4_magic: - case __perf_cs_ete_magic: - if (metadata[i][CS_ETMV4_TRCTRACEIDR] & CORESIGHT_TRACE_ID_UNUSED_FLAG) - metadata[i][CS_ETMV4_TRCTRACEIDR] = CORESIGHT_TRACE_ID_UNUSED_VAL; - break; - default: - /* unknown magic number */ - return -EINVAL; - } - } - return 0; -} - /* * Use the data gathered by the peeks for HW_ID (trace ID mappings) and AUX * (formatted or not) packets to create the decoders. @@ -3222,21 +3170,26 @@ static int cs_etm__clear_unused_trace_ids_metadata(int num_cpu, u64 **metadata) static int cs_etm__create_queue_decoders(struct cs_etm_queue *etmq) { struct cs_etm_decoder_params d_params; + struct cs_etm_trace_params *t_params; + int decoders = intlist__nr_entries(etmq->traceid_list); + + if (decoders == 0) + return 0; /* * Each queue can only contain data from one CPU when unformatted, so only one decoder is * needed. */ - int decoders = etmq->format == FORMATTED ? etmq->etm->num_cpu : 1; + if (etmq->format == UNFORMATTED) + assert(decoders == 1); /* Use metadata to fill in trace parameters for trace decoder */ - struct cs_etm_trace_params *t_params = zalloc(sizeof(*t_params) * decoders); + t_params = zalloc(sizeof(*t_params) * decoders); if (!t_params) goto out_free; - if (cs_etm__init_trace_params(t_params, etmq->etm, etmq->format, - etmq->queue_nr, decoders)) + if (cs_etm__init_trace_params(t_params, etmq)) goto out_free; /* Set decoder parameters to decode trace packets */ @@ -3462,9 +3415,9 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, /* * Map Trace ID values to CPU metadata. * - * Trace metadata will always contain Trace ID values from the legacy algorithm. If the - * files has been recorded by a "new" perf updated to handle AUX_HW_ID then the metadata - * ID value will also have the CORESIGHT_TRACE_ID_UNUSED_FLAG set. + * Trace metadata will always contain Trace ID values from the legacy algorithm + * in case it's read by a version of Perf that doesn't know about HW_ID packets + * or the kernel doesn't emit them. * * The updated kernel drivers that use AUX_HW_ID to sent Trace IDs will attempt to use * the same IDs as the old algorithm as far as is possible, unless there are clashes @@ -3473,12 +3426,11 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, * * For a perf able to interpret AUX_HW_ID packets we first check for the presence of * those packets. If they are there then the values will be mapped and plugged into - * the metadata. We then set any remaining metadata values with the used flag to a - * value CORESIGHT_TRACE_ID_UNUSED_VAL - which indicates no decoder is required. + * the metadata and decoders are only created for each mapping received. * * If no AUX_HW_ID packets are present - which means a file recorded on an old kernel - * then we map Trace ID values to CPU directly from the metadata - clearing any unused - * flags if present. + * then we map Trace ID values to CPU directly from the metadata and create decoders + * for all mappings. */ /* Scan for AUX_OUTPUT_HW_ID records to map trace ID values to CPU metadata */ @@ -3489,15 +3441,12 @@ int cs_etm__process_auxtrace_info_full(union perf_event *event, if (err) goto err_free_queues; - /* if HW ID found then clear any unused metadata ID values */ - if (aux_hw_id_found) - err = cs_etm__clear_unused_trace_ids_metadata(num_cpu, metadata); - /* otherwise, this is a file with metadata values only, map from metadata */ - else + /* if no HW ID found this is a file with metadata values only, map from metadata */ + if (!aux_hw_id_found) { err = cs_etm__map_trace_ids_metadata(etm, num_cpu, metadata); - - if (err) - goto err_free_queues; + if (err) + goto err_free_queues; + } err = cs_etm__create_decoders(etm); if (err) diff --git a/tools/perf/util/cs-etm.h b/tools/perf/util/cs-etm.h index f4f69f7cc0f3..a8caeea720aa 100644 --- a/tools/perf/util/cs-etm.h +++ b/tools/perf/util/cs-etm.h @@ -230,16 +230,6 @@ struct cs_etm_packet_queue { /* CoreSight trace ID is currently the bottom 7 bits of the value */ #define CORESIGHT_TRACE_ID_VAL_MASK GENMASK(6, 0) -/* - * perf record will set the legacy meta data values as unused initially. - * This allows perf report to manage the decoders created when dynamic - * allocation in operation. - */ -#define CORESIGHT_TRACE_ID_UNUSED_FLAG BIT(31) - -/* Value to set for unused trace ID values */ -#define CORESIGHT_TRACE_ID_UNUSED_VAL 0x7F - int cs_etm__process_auxtrace_info(union perf_event *event, struct perf_session *session); void cs_etm_get_default_config(const struct perf_pmu *pmu, struct perf_event_attr *attr); From 940007cee539fd07c7c274030f7f7252f8c5a5d7 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 22 Jul 2024 11:11:47 +0100 Subject: [PATCH 253/347] perf: cs-etm: Only save valid trace IDs into files This isn't a bug because Perf always masks with CORESIGHT_TRACE_ID_VAL_MASK before using these values, but to avoid it looking like it could be, make an effort to not save bad values. Reviewed-by: Mike Leach Signed-off-by: James Clark Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Alexandre Torgue Cc: Anshuman Khandual Cc: Ganapatrao Kulkarni Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Maxime Coquelin Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Suzuki Poulouse Cc: Will Deacon Link: https://lore.kernel.org/r/20240722101202.26915-6-james.clark@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/arch/arm/util/cs-etm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/arch/arm/util/cs-etm.c b/tools/perf/arch/arm/util/cs-etm.c index 3bbc1e57d71d..ea891d12f8f4 100644 --- a/tools/perf/arch/arm/util/cs-etm.c +++ b/tools/perf/arch/arm/util/cs-etm.c @@ -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) From 1506af6db8c4abbe3d5dd573ec72b90f81abfcf7 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 22 Jul 2024 11:11:48 +0100 Subject: [PATCH 254/347] perf: cs-etm: Support version 0.1 of HW_ID packets v0.1 HW_ID packets have a new field that describes which sink each CPU writes to. Use the sink ID to link trace ID maps to each other so that mappings are shared wherever the sink is shared. Also update the error message to show that overlapping IDs aren't an error in per-thread mode, just not supported. In the future we can use the CPU ID from the AUX records, or watch for changing sink IDs on HW_ID packets to use the correct decoders. Reviewed-by: Mike Leach Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Alexandre Torgue Cc: Anshuman Khandual Cc: Ganapatrao Kulkarni Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Maxime Coquelin Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Suzuki Poulouse Cc: Will Deacon Link: https://lore.kernel.org/r/20240722101202.26915-7-james.clark@linaro.org Signed-off-by: James Clark Signed-off-by: Arnaldo Carvalho de Melo --- tools/include/linux/coresight-pmu.h | 17 +++-- tools/perf/util/cs-etm.c | 100 +++++++++++++++++++++++++--- 2 files changed, 103 insertions(+), 14 deletions(-) diff --git a/tools/include/linux/coresight-pmu.h b/tools/include/linux/coresight-pmu.h index 51ac441a37c3..89b0ac0014b0 100644 --- a/tools/include/linux/coresight-pmu.h +++ b/tools/include/linux/coresight-pmu.h @@ -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 diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 01c60a660fb3..775d4f35c270 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -118,6 +118,12 @@ struct cs_etm_queue { struct cs_etm_traceid_queue **traceid_queues; /* Conversion between traceID and metadata pointers */ struct intlist *traceid_list; + /* + * Same as traceid_list, but traceid_list may be a reference to another + * queue's which has a matching sink ID. + */ + struct intlist *own_traceid_list; + u32 sink_id; }; static int cs_etm__process_timestamped_queues(struct cs_etm_auxtrace *etm); @@ -142,6 +148,7 @@ static int cs_etm__metadata_set_trace_id(u8 trace_chan_id, u64 *cpu_metadata); (queue_nr << 16 | trace_chan_id) #define TO_QUEUE_NR(cs_queue_nr) (cs_queue_nr >> 16) #define TO_TRACE_CHAN_ID(cs_queue_nr) (cs_queue_nr & 0x0000ffff) +#define SINK_UNSET ((u32) -1) static u32 cs_etm__get_v7_protocol_version(u32 etmidr) { @@ -241,7 +248,16 @@ static int cs_etm__insert_trace_id_node(struct cs_etm_queue *etmq, int err; if (curr_cpu_data[CS_ETM_CPU] != cpu_metadata[CS_ETM_CPU]) { - pr_err("CS_ETM: map mismatch between HW_ID packet CPU and Trace ID\n"); + /* + * With > CORESIGHT_TRACE_IDS_MAX ETMs, overlapping IDs + * are expected (but not supported) in per-thread mode, + * rather than signifying an error. + */ + if (etmq->etm->per_thread_decoding) + pr_err("CS_ETM: overlapping Trace IDs aren't currently supported in per-thread mode\n"); + else + pr_err("CS_ETM: map mismatch between HW_ID packet CPU and Trace ID\n"); + return -EINVAL; } @@ -326,6 +342,64 @@ static int cs_etm__process_trace_id_v0(struct cs_etm_auxtrace *etm, int cpu, return cs_etm__metadata_set_trace_id(trace_chan_id, cpu_data); } +static int cs_etm__process_trace_id_v0_1(struct cs_etm_auxtrace *etm, int cpu, + u64 hw_id) +{ + struct cs_etm_queue *etmq = cs_etm__get_queue(etm, cpu); + int ret; + u64 *cpu_data; + u32 sink_id = FIELD_GET(CS_AUX_HW_ID_SINK_ID_MASK, hw_id); + u8 trace_id = FIELD_GET(CS_AUX_HW_ID_TRACE_ID_MASK, hw_id); + + /* + * Check sink id hasn't changed in per-cpu mode. In per-thread mode, + * let it pass for now until an actual overlapping trace ID is hit. In + * most cases IDs won't overlap even if the sink changes. + */ + if (!etmq->etm->per_thread_decoding && etmq->sink_id != SINK_UNSET && + etmq->sink_id != sink_id) { + pr_err("CS_ETM: mismatch between sink IDs\n"); + return -EINVAL; + } + + etmq->sink_id = sink_id; + + /* Find which other queues use this sink and link their ID maps */ + for (unsigned int i = 0; i < etm->queues.nr_queues; ++i) { + struct cs_etm_queue *other_etmq = etm->queues.queue_array[i].priv; + + /* Different sinks, skip */ + if (other_etmq->sink_id != etmq->sink_id) + continue; + + /* Already linked, skip */ + if (other_etmq->traceid_list == etmq->traceid_list) + continue; + + /* At the point of first linking, this one should be empty */ + if (!intlist__empty(etmq->traceid_list)) { + pr_err("CS_ETM: Can't link populated trace ID lists\n"); + return -EINVAL; + } + + etmq->own_traceid_list = NULL; + intlist__delete(etmq->traceid_list); + etmq->traceid_list = other_etmq->traceid_list; + break; + } + + cpu_data = get_cpu_data(etm, cpu); + ret = cs_etm__insert_trace_id_node(etmq, trace_id, cpu_data); + if (ret) + return ret; + + ret = cs_etm__metadata_set_trace_id(trace_id, cpu_data); + if (ret) + return ret; + + return 0; +} + static int cs_etm__metadata_get_trace_id(u8 *trace_chan_id, u64 *cpu_metadata) { u64 cs_etm_magic = cpu_metadata[CS_ETM_MAGIC]; @@ -414,10 +488,10 @@ static int cs_etm__process_aux_output_hw_id(struct perf_session *session, /* extract and parse the HW ID */ hw_id = event->aux_output_hw_id.hw_id; - version = FIELD_GET(CS_AUX_HW_ID_VERSION_MASK, hw_id); + version = FIELD_GET(CS_AUX_HW_ID_MAJOR_VERSION_MASK, hw_id); /* check that we can handle this version */ - if (version > CS_AUX_HW_ID_CURR_VERSION) { + if (version > CS_AUX_HW_ID_MAJOR_VERSION) { pr_err("CS ETM Trace: PERF_RECORD_AUX_OUTPUT_HW_ID version %d not supported. Please update Perf.\n", version); return -EINVAL; @@ -442,7 +516,10 @@ static int cs_etm__process_aux_output_hw_id(struct perf_session *session, return -EINVAL; } - return cs_etm__process_trace_id_v0(etm, cpu, hw_id); + if (FIELD_GET(CS_AUX_HW_ID_MINOR_VERSION_MASK, hw_id) == 0) + return cs_etm__process_trace_id_v0(etm, cpu, hw_id); + + return cs_etm__process_trace_id_v0_1(etm, cpu, hw_id); } void cs_etm__etmq_set_traceid_queue_timestamp(struct cs_etm_queue *etmq, @@ -882,12 +959,14 @@ static void cs_etm__free_queue(void *priv) cs_etm_decoder__free(etmq->decoder); cs_etm__free_traceid_queues(etmq); - /* First remove all traceID/metadata nodes for the RB tree */ - intlist__for_each_entry_safe(inode, tmp, etmq->traceid_list) - intlist__remove(etmq->traceid_list, inode); + if (etmq->own_traceid_list) { + /* First remove all traceID/metadata nodes for the RB tree */ + intlist__for_each_entry_safe(inode, tmp, etmq->own_traceid_list) + intlist__remove(etmq->own_traceid_list, inode); - /* Then the RB tree itself */ - intlist__delete(etmq->traceid_list); + /* Then the RB tree itself */ + intlist__delete(etmq->own_traceid_list); + } free(etmq); } @@ -1081,7 +1160,7 @@ static struct cs_etm_queue *cs_etm__alloc_queue(void) * has to be made for each packet that gets decoded, optimizing access * in anything other than a sequential array is worth doing. */ - etmq->traceid_list = intlist__new(NULL); + etmq->traceid_list = etmq->own_traceid_list = intlist__new(NULL); if (!etmq->traceid_list) goto out_free; @@ -1113,6 +1192,7 @@ static int cs_etm__setup_queue(struct cs_etm_auxtrace *etm, etmq->queue_nr = queue_nr; queue->cpu = queue_nr; /* Placeholder, may be reset to -1 in per-thread mode */ etmq->offset = 0; + etmq->sink_id = SINK_UNSET; return 0; } From 022aa67b5ab9077d8f5bf02df5a7f0f2d4dfb909 Mon Sep 17 00:00:00 2001 From: James Clark Date: Mon, 22 Jul 2024 11:11:49 +0100 Subject: [PATCH 255/347] perf: cs-etm: Print queue number in raw trace dump Now that we have overlapping trace IDs it's also useful to know what the queue number is to be able to distinguish the source of the trace so print it inline. Hide it behind the -v option because it might not be obvious to users what the queue number is. Reviewed-by: Mike Leach Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Alexandre Torgue Cc: Anshuman Khandual Cc: Ganapatrao Kulkarni Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: John Garry Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Maxime Coquelin Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Suzuki Poulouse Cc: Will Deacon Link: https://lore.kernel.org/r/20240722101202.26915-8-james.clark@linaro.org Signed-off-by: James Clark Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/cs-etm-decoder/cs-etm-decoder.c | 4 ++-- tools/perf/util/cs-etm-decoder/cs-etm-decoder.h | 2 +- tools/perf/util/cs-etm.c | 13 ++++++++++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c index d49c3e9c7c21..b78ef0262135 100644 --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.c @@ -41,7 +41,7 @@ const u32 INSTR_PER_NS = 10; struct cs_etm_decoder { void *data; - void (*packet_printer)(const char *msg); + void (*packet_printer)(const char *msg, void *data); bool suppress_printing; dcd_tree_handle_t dcd_tree; cs_etm_mem_cb_type mem_access; @@ -202,7 +202,7 @@ static void cs_etm_decoder__print_str_cb(const void *p_context, const struct cs_etm_decoder *decoder = p_context; if (p_context && str_len && !decoder->suppress_printing) - decoder->packet_printer(msg); + decoder->packet_printer(msg, decoder->data); } static int diff --git a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h index 272c2efe78ee..12c782fa6db2 100644 --- a/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h +++ b/tools/perf/util/cs-etm-decoder/cs-etm-decoder.h @@ -60,7 +60,7 @@ struct cs_etm_trace_params { struct cs_etm_decoder_params { int operation; - void (*packet_printer)(const char *msg); + void (*packet_printer)(const char *msg, void *data); cs_etm_mem_cb_type mem_acc_cb; bool formatted; bool fsyncs; diff --git a/tools/perf/util/cs-etm.c b/tools/perf/util/cs-etm.c index 775d4f35c270..90f32f327b9b 100644 --- a/tools/perf/util/cs-etm.c +++ b/tools/perf/util/cs-etm.c @@ -762,15 +762,22 @@ static void cs_etm__packet_swap(struct cs_etm_auxtrace *etm, } } -static void cs_etm__packet_dump(const char *pkt_string) +static void cs_etm__packet_dump(const char *pkt_string, void *data) { const char *color = PERF_COLOR_BLUE; int len = strlen(pkt_string); + struct cs_etm_queue *etmq = data; + char queue_nr[64]; + + if (verbose) + snprintf(queue_nr, sizeof(queue_nr), "Qnr:%d; ", etmq->queue_nr); + else + queue_nr[0] = '\0'; if (len && (pkt_string[len-1] == '\n')) - color_fprintf(stdout, color, " %s", pkt_string); + color_fprintf(stdout, color, " %s%s", queue_nr, pkt_string); else - color_fprintf(stdout, color, " %s\n", pkt_string); + color_fprintf(stdout, color, " %s%s\n", queue_nr, pkt_string); fflush(stdout); } From d9c993100ef1906bd1f25bab789390828ef10a41 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 29 Aug 2024 08:01:48 -0700 Subject: [PATCH 256/347] perf session: Document 'struct perf_session' and constify its 'auxtrace' member perf_session is a central data structure to the tool so let's comment it. The auxtrace callbacks are never modified in session so constify. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Nick Terrell Cc: Peter Zijlstra Cc: Yanteng Si Cc: Yicong Yang Link: https://lore.kernel.org/r/20240829150154.37929-3-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/session.h | 48 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 7c8dd6956330..e56518639711 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -26,26 +26,72 @@ struct decomp_data { struct zstd_data *zstd_decomp; }; +/** + * struct perf_session- A Perf session holds the main state when the program is + * working with live perf events or reading data from an input file. + * + * The rough organization of a perf_session is: + * ``` + * +--------------+ +-----------+ +------------+ + * | Session |1..* ----->| Machine |1..* ----->| Thread | + * +--------------+ +-----------+ +------------+ + * ``` + */ struct perf_session { + /** + * @header: The read version of a perf_file_header, or captures global + * information from a live session. + */ struct perf_header header; + /** @machines: Machines within the session a host and 0 or more guests. */ struct machines machines; + /** @evlist: List of evsels/events of the session. */ struct evlist *evlist; - struct auxtrace *auxtrace; + /** @auxtrace: callbacks to allow AUX area data decoding. */ + const struct auxtrace *auxtrace; + /** @itrace_synth_opts: AUX area tracing synthesis options. */ struct itrace_synth_opts *itrace_synth_opts; + /** @auxtrace_index: index of AUX area tracing events within a perf.data file. */ struct list_head auxtrace_index; #ifdef HAVE_LIBTRACEEVENT + /** @tevent: handles for libtraceevent and plugins. */ struct trace_event tevent; #endif + /** @time_conv: Holds contents of last PERF_RECORD_TIME_CONV event. */ struct perf_record_time_conv time_conv; + /** + * @repipe: When set causes certain reading (header and trace events) to + * also write events. The written file descriptor must be provided for + * the header but is implicitly stdout for trace events. + */ bool repipe; + /** + * @one_mmap: The reader will use a single mmap by default. There may be + * multiple data files in particular for aux events. If this is true + * then the single big mmap for the data file can be assumed. + */ bool one_mmap; + /** @one_mmap_addr: Address of initial perf data file reader mmap. */ void *one_mmap_addr; + /** @one_mmap_offset: File offset in perf.data file when mapped. */ u64 one_mmap_offset; + /** @ordered_events: Used to turn unordered events into ordered ones. */ struct ordered_events ordered_events; + /** @data: Optional perf data file being read from. */ struct perf_data *data; + /** @tool: callbacks for event handling. */ const struct perf_tool *tool; + /** + * @bytes_transferred: Used by perf record to count written bytes before + * compression. + */ u64 bytes_transferred; + /** + * @bytes_compressed: Used by perf record to count written bytes after + * compression. + */ u64 bytes_compressed; + /** @zstd_data: Owner of global compression state, buffers, etc. */ struct zstd_data zstd_data; struct decomp_data decomp_data; struct decomp_data *active_decomp; From d71bbe799c0cda4a0581f98ea3e4d646a18e799d Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 29 Aug 2024 08:01:49 -0700 Subject: [PATCH 257/347] perf header: Add kerneldoc to 'struct perf_file_header' Some of the values are a little strange so add documentation to resolve ambiguity. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Nick Terrell Cc: Peter Zijlstra Cc: Yanteng Si Cc: Yicong Yang Link: https://lore.kernel.org/r/20240829150154.37929-4-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 07ff647197ff..3285981948d7 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -61,14 +61,28 @@ struct perf_file_section { u64 size; }; +/** + * struct perf_file_header: Header representation on disk. + */ struct perf_file_header { + /** @magic: Holds "PERFILE2". */ u64 magic; + /** @size: Size of this header - sizeof(struct perf_file_header). */ u64 size; + /** + * @attr_size: Size of attrs entries - sizeof(struct perf_event_attr) + + * sizeof(struct perf_file_section). + */ u64 attr_size; + /** @attrs: Offset and size of file section holding attributes. */ struct perf_file_section attrs; + /** @data: Offset and size of file section holding regular event data. */ struct perf_file_section data; - /* event_types is ignored */ + /** @event_types: Ignored. */ struct perf_file_section event_types; + /** + * @adds_features: Bitmap of features. The features are immediately after the data section. + */ DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS); }; From 10df481fda13d29c2d9893b7460b8a7095d0ccf5 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 29 Aug 2024 08:01:50 -0700 Subject: [PATCH 258/347] perf header: Fail read if header sections overlap Buggy perf.data files can have the attributes and data overlapping. For example, when processing pipe data the attributes aren't known and so file offset header calculations can consider them not present. Later this can cause the attributes to overwrite the data. This can be seen in: $ perf record -o - true > a.data [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.059 MB - ] $ perf inject -i a.data -o b.data $ perf report --stats -i b.data 0x68 [0]: failed to process type: 510379 [Invalid argument] Error: failed to process sample $ This change makes reading the corrupt file fail: $ perf report --stats -i b.data Perf file header corrupt: Attributes and data overlap incompatible file format (rerun with -v to learn more) $ Which is more informative. Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Nick Terrell Cc: Peter Zijlstra Cc: Yanteng Si Cc: Yicong Yang Link: https://lore.kernel.org/r/20240829150154.37929-5-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 3309fe7f1d12..65c9086610cb 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -3986,6 +3986,24 @@ int perf_file_header__read(struct perf_file_header *header, adds_features)); } + if (header->size > header->attrs.offset) { + pr_err("Perf file header corrupt: header overlaps attrs\n"); + return -1; + } + + if (header->size > header->data.offset) { + pr_err("Perf file header corrupt: header overlaps data\n"); + return -1; + } + + if ((header->attrs.offset <= header->data.offset && + header->attrs.offset + header->attrs.size > header->data.offset) || + (header->attrs.offset > header->data.offset && + header->data.offset + header->data.size > header->attrs.offset)) { + pr_err("Perf file header corrupt: Attributes and data overlap\n"); + return -1; + } + if (header->size != sizeof(*header)) { /* Support the previous format */ if (header->size == offsetof(typeof(*header), adds_features)) From e9a7053da377e19ebc6f1afe55c7f75219aaef79 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 29 Aug 2024 08:01:51 -0700 Subject: [PATCH 259/347] perf header: Allow attributes to be written after data With a file, to write data an offset needs to be known. Typically data follows the event attributes in a file. However, if processing a pipe the number of event attributes may not be known. It is convenient in that case to write the attributes after the data. Expand perf_session__do_write_header() to allow this when the data offset and size are known. This approach may be useful for more than just taking a pipe file to write into a data file, `perf inject --itrace` will reserve and additional 8kb for attributes, which would be unnecessary if the attributes were written after the data. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Nick Terrell Cc: Peter Zijlstra Cc: Yanteng Si Cc: Yicong Yang Link: https://lore.kernel.org/r/20240829150154.37929-6-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 108 ++++++++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 40 deletions(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 65c9086610cb..4eb39463067e 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -3676,31 +3676,49 @@ int perf_header__write_pipe(int fd) static int perf_session__do_write_header(struct perf_session *session, struct evlist *evlist, int fd, bool at_exit, - struct feat_copier *fc) + struct feat_copier *fc, + bool write_attrs_after_data) { struct perf_file_header f_header; - struct perf_file_attr f_attr; struct perf_header *header = &session->header; struct evsel *evsel; struct feat_fd ff = { .fd = fd, }; - u64 attr_offset; + u64 attr_offset = sizeof(f_header), attr_size = 0; int err; - lseek(fd, sizeof(f_header), SEEK_SET); - - evlist__for_each_entry(session->evlist, evsel) { - evsel->id_offset = lseek(fd, 0, SEEK_CUR); - err = do_write(&ff, evsel->core.id, evsel->core.ids * sizeof(u64)); - if (err < 0) { - pr_debug("failed to write perf header\n"); - free(ff.buf); - return err; + if (write_attrs_after_data && at_exit) { + /* + * Write features at the end of the file first so that + * attributes may come after them. + */ + if (!header->data_offset && header->data_size) { + pr_err("File contains data but offset unknown\n"); + err = -1; + goto err_out; } + header->feat_offset = header->data_offset + header->data_size; + err = perf_header__adds_write(header, evlist, fd, fc); + if (err < 0) + goto err_out; + attr_offset = lseek(fd, 0, SEEK_CUR); + } else { + lseek(fd, attr_offset, SEEK_SET); } - attr_offset = lseek(ff.fd, 0, SEEK_CUR); + evlist__for_each_entry(session->evlist, evsel) { + evsel->id_offset = attr_offset; + /* Avoid writing at the end of the file until the session is exiting. */ + if (!write_attrs_after_data || at_exit) { + err = do_write(&ff, evsel->core.id, evsel->core.ids * sizeof(u64)); + if (err < 0) { + pr_debug("failed to write perf header\n"); + goto err_out; + } + } + attr_offset += evsel->core.ids * sizeof(u64); + } evlist__for_each_entry(evlist, evsel) { if (evsel->core.attr.size < sizeof(evsel->core.attr)) { @@ -3711,40 +3729,46 @@ static int perf_session__do_write_header(struct perf_session *session, */ evsel->core.attr.size = sizeof(evsel->core.attr); } - f_attr = (struct perf_file_attr){ - .attr = evsel->core.attr, - .ids = { - .offset = evsel->id_offset, - .size = evsel->core.ids * sizeof(u64), + /* Avoid writing at the end of the file until the session is exiting. */ + if (!write_attrs_after_data || at_exit) { + struct perf_file_attr f_attr = { + .attr = evsel->core.attr, + .ids = { + .offset = evsel->id_offset, + .size = evsel->core.ids * sizeof(u64), + } + }; + err = do_write(&ff, &f_attr, sizeof(f_attr)); + if (err < 0) { + pr_debug("failed to write perf header attribute\n"); + goto err_out; } - }; - err = do_write(&ff, &f_attr, sizeof(f_attr)); - if (err < 0) { - pr_debug("failed to write perf header attribute\n"); - free(ff.buf); - return err; } + attr_size += sizeof(struct perf_file_attr); } - if (!header->data_offset) - header->data_offset = lseek(fd, 0, SEEK_CUR); + if (!header->data_offset) { + if (write_attrs_after_data) + header->data_offset = sizeof(f_header); + else + header->data_offset = attr_offset + attr_size; + } header->feat_offset = header->data_offset + header->data_size; - if (at_exit) { + if (!write_attrs_after_data && at_exit) { + /* Write features now feat_offset is known. */ err = perf_header__adds_write(header, evlist, fd, fc); - if (err < 0) { - free(ff.buf); - return err; - } + if (err < 0) + goto err_out; } f_header = (struct perf_file_header){ .magic = PERF_MAGIC, .size = sizeof(f_header), - .attr_size = sizeof(f_attr), + .attr_size = sizeof(struct perf_file_attr), .attrs = { .offset = attr_offset, - .size = evlist->core.nr_entries * sizeof(f_attr), + .size = attr_size, }, .data = { .offset = header->data_offset, @@ -3757,21 +3781,24 @@ static int perf_session__do_write_header(struct perf_session *session, lseek(fd, 0, SEEK_SET); err = do_write(&ff, &f_header, sizeof(f_header)); - free(ff.buf); if (err < 0) { pr_debug("failed to write perf header\n"); - return err; + goto err_out; + } else { + lseek(fd, 0, SEEK_END); + err = 0; } - lseek(fd, header->data_offset + header->data_size, SEEK_SET); - - return 0; +err_out: + free(ff.buf); + return err; } int perf_session__write_header(struct perf_session *session, struct evlist *evlist, int fd, bool at_exit) { - return perf_session__do_write_header(session, evlist, fd, at_exit, NULL); + return perf_session__do_write_header(session, evlist, fd, at_exit, /*fc=*/NULL, + /*write_attrs_after_data=*/false); } size_t perf_session__data_offset(const struct evlist *evlist) @@ -3793,7 +3820,8 @@ int perf_session__inject_header(struct perf_session *session, int fd, struct feat_copier *fc) { - return perf_session__do_write_header(session, evlist, fd, true, fc); + return perf_session__do_write_header(session, evlist, fd, true, fc, + /*write_attrs_after_data=*/false); } static int perf_header__getbuffer64(struct perf_header *header, From 89d64e72732f3f970a048a46a7b60164b34dfc43 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 29 Aug 2024 08:01:52 -0700 Subject: [PATCH 260/347] perf inject: Overhaul handling of pipe files Previously inject->is_pipe was set if the input or output were a pipe. Determining the input was a pipe had to be done prior to starting the session and opening the file. This was done by comparing the input file name with '-' but it fails if the pipe file is written to disk. Opening a pipe file from disk will correctly set perf_data.is_pipe, but this is too late for 'perf inject' and results in a broken file. A workaround is 'cat pipe_perf|perf inject -i - ...'. This change removes inject->is_pipe and changes the dependent conditions to use the is_pipe flag on the input (inject->session->data) and output files (inject->output). This ensures the is_pipe condition reflects things like the header being read. The change removes the use of perf file header repiping, that is writing the file header out while reading it in. The case of input pipe and output file cannot repipe as the attributes for the file are unknown. To resolve this, write the file header when writing to disk and as the attributes may be unknown, write them after the data. Update sessions repipe variable to be trace_event_repipe as those are the only events now impacted by it. Update __perf_session__new as the repipe_fd no longer needs passing. Fully removing repipe from session header reading will be done in a later change. Committer testing: root@number:~# perf record -e syscalls:sys_enter_*sleep/max-stack=4/ -o - sleep 0.01 | perf report -i - # To display the perf.data header info, please use --header/--header-only options. # [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.050 MB - ] # # Total Lost Samples: 0 # # Samples: 1 of event 'syscalls:sys_enter_clock_nanosleep' # Event count (approx.): 1 # # Overhead Command Shared Object Symbol # ........ ....... ............. ............................... # 100.00% sleep libc.so.6 [.] clock_nanosleep@GLIBC_2.2.5 | ---__libc_start_main@@GLIBC_2.34 __libc_start_call_main 0x562fc2560a9f clock_nanosleep@GLIBC_2.2.5 # # (Tip: Create an archive with symtabs to analyse on other machine: perf archive) # root@number:~# perf record -e syscalls:sys_enter_*sleep/max-stack=4/ -o - sleep 0.01 > pipe.data [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.050 MB - ] root@number:~# perf report --stdio -i pipe.data # To display the perf.data header info, please use --header/--header-only options. # # # Total Lost Samples: 0 # # Samples: 1 of event 'syscalls:sys_enter_clock_nanosleep' # Event count (approx.): 1 # # Overhead Command Shared Object Symbol # ........ ....... ............. ............................... # 100.00% sleep libc.so.6 [.] clock_nanosleep@GLIBC_2.2.5 | ---__libc_start_main@@GLIBC_2.34 __libc_start_call_main 0x55f775975a9f clock_nanosleep@GLIBC_2.2.5 # # (Tip: To set sampling period of individual events use perf record -e cpu/cpu-cycles,period=100001/,cpu/branches,period=10001/ ...) # root@number:~# Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Nick Terrell Cc: Peter Zijlstra Cc: Yanteng Si Cc: Yicong Yang Link: https://lore.kernel.org/r/20240829150154.37929-7-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 60 +++++++++++++++++++------------------ tools/perf/util/header.c | 12 ++++---- tools/perf/util/header.h | 3 +- tools/perf/util/session.c | 8 ++--- tools/perf/util/session.h | 14 ++++----- 5 files changed, 48 insertions(+), 49 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index a7c859db2e15..0ccf80fe8399 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -119,7 +119,6 @@ struct perf_inject { bool jit_mode; bool in_place_update; bool in_place_update_dry_run; - bool is_pipe; bool copy_kcore_dir; const char *input_name; struct perf_data output; @@ -205,7 +204,8 @@ static int perf_event__repipe_attr(const struct perf_tool *tool, if (ret) return ret; - if (!inject->is_pipe) + /* If the output isn't a pipe then the attributes will be written as part of the header. */ + if (!inject->output.is_pipe) return 0; return perf_event__repipe_synth(tool, event); @@ -1966,7 +1966,13 @@ static int __cmd_inject(struct perf_inject *inject) struct guest_session *gs = &inject->guest_session; struct perf_session *session = inject->session; int fd = output_fd(inject); - u64 output_data_offset; + u64 output_data_offset = perf_session__data_offset(session->evlist); + /* + * Pipe input hasn't loaded the attributes and will handle them as + * events. So that the attributes don't overlap the data, write the + * attributes after the data. + */ + bool write_attrs_after_data = !inject->output.is_pipe && inject->session->data->is_pipe; signal(SIGINT, sig_handler); @@ -1980,8 +1986,6 @@ static int __cmd_inject(struct perf_inject *inject) #endif } - output_data_offset = perf_session__data_offset(session->evlist); - if (inject->build_id_style == BID_RWS__INJECT_HEADER_LAZY) { inject->tool.sample = perf_event__inject_buildid; } else if (inject->sched_stat) { @@ -2075,7 +2079,7 @@ static int __cmd_inject(struct perf_inject *inject) if (!inject->itrace_synth_opts.set) auxtrace_index__free(&session->auxtrace_index); - if (!inject->is_pipe && !inject->in_place_update) + if (!inject->output.is_pipe && !inject->in_place_update) lseek(fd, output_data_offset, SEEK_SET); ret = perf_session__process_events(session); @@ -2094,7 +2098,7 @@ static int __cmd_inject(struct perf_inject *inject) } } - if (!inject->is_pipe && !inject->in_place_update) { + if (!inject->output.is_pipe && !inject->in_place_update) { struct inject_fc inj_fc = { .fc.copy = feat_copy_cb, .inject = inject, @@ -2124,7 +2128,8 @@ static int __cmd_inject(struct perf_inject *inject) } session->header.data_offset = output_data_offset; session->header.data_size = inject->bytes_written; - perf_session__inject_header(session, session->evlist, fd, &inj_fc.fc); + perf_session__inject_header(session, session->evlist, fd, &inj_fc.fc, + write_attrs_after_data); if (inject->copy_kcore_dir) { ret = copy_kcore_dir(inject); @@ -2161,7 +2166,6 @@ int cmd_inject(int argc, const char **argv) .use_stdio = true, }; int ret; - bool repipe = true; const char *known_build_ids = NULL; bool build_ids; bool build_id_all; @@ -2273,16 +2277,7 @@ int cmd_inject(int argc, const char **argv) inject.build_id_style = BID_RWS__INJECT_HEADER_ALL; data.path = inject.input_name; - if (!strcmp(inject.input_name, "-") || inject.output.is_pipe) { - inject.is_pipe = true; - /* - * Do not repipe header when input is a regular file - * since either it can rewrite the header at the end - * or write a new pipe header. - */ - if (strcmp(inject.input_name, "-")) - repipe = false; - } + ordered_events = inject.jit_mode || inject.sched_stat || (inject.build_id_style == BID_RWS__INJECT_HEADER_LAZY); perf_tool__init(&inject.tool, ordered_events); @@ -2325,9 +2320,9 @@ int cmd_inject(int argc, const char **argv) inject.tool.compressed = perf_event__repipe_op4_synth; inject.tool.auxtrace = perf_event__repipe_auxtrace; inject.tool.dont_split_sample_group = true; - inject.session = __perf_session__new(&data, repipe, - output_fd(&inject), - &inject.tool); + inject.session = __perf_session__new(&data, &inject.tool, + /*trace_event_repipe=*/inject.output.is_pipe); + if (IS_ERR(inject.session)) { ret = PTR_ERR(inject.session); goto out_close_output; @@ -2341,19 +2336,26 @@ int cmd_inject(int argc, const char **argv) if (ret) goto out_delete; - if (!data.is_pipe && inject.output.is_pipe) { + if (inject.output.is_pipe) { ret = perf_header__write_pipe(perf_data__fd(&inject.output)); if (ret < 0) { pr_err("Couldn't write a new pipe header.\n"); goto out_delete; } - ret = perf_event__synthesize_for_pipe(&inject.tool, - inject.session, - &inject.output, - perf_event__repipe); - if (ret < 0) - goto out_delete; + /* + * If the input is already a pipe then the features and + * attributes don't need synthesizing, they will be present in + * the input. + */ + if (!data.is_pipe) { + ret = perf_event__synthesize_for_pipe(&inject.tool, + inject.session, + &inject.output, + perf_event__repipe); + if (ret < 0) + goto out_delete; + } } if (inject.build_id_style == BID_RWS__INJECT_HEADER_LAZY) { diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 4eb39463067e..59e2f37c1cb4 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -3818,10 +3818,11 @@ size_t perf_session__data_offset(const struct evlist *evlist) int perf_session__inject_header(struct perf_session *session, struct evlist *evlist, int fd, - struct feat_copier *fc) + struct feat_copier *fc, + bool write_attrs_after_data) { return perf_session__do_write_header(session, evlist, fd, true, fc, - /*write_attrs_after_data=*/false); + write_attrs_after_data); } static int perf_header__getbuffer64(struct perf_header *header, @@ -4145,7 +4146,7 @@ static int perf_header__read_pipe(struct perf_session *session, int repipe_fd) struct perf_pipe_file_header f_header; if (perf_file_header__read_pipe(&f_header, header, session->data, - session->repipe, repipe_fd) < 0) { + /*repipe=*/false, repipe_fd) < 0) { pr_debug("incompatible file format\n"); return -EINVAL; } @@ -4560,15 +4561,14 @@ int perf_event__process_tracing_data(struct perf_session *session, SEEK_SET); } - size_read = trace_report(fd, &session->tevent, - session->repipe); + size_read = trace_report(fd, &session->tevent, session->trace_event_repipe); padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; if (readn(fd, buf, padding) < 0) { pr_err("%s: reading input file", __func__); return -1; } - if (session->repipe) { + if (session->trace_event_repipe) { int retw = write(STDOUT_FILENO, buf, padding); if (retw <= 0 || retw != padding) { pr_err("%s: repiping tracing data padding", __func__); diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 3285981948d7..7137509cf6d8 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -150,7 +150,8 @@ struct feat_copier { int perf_session__inject_header(struct perf_session *session, struct evlist *evlist, int fd, - struct feat_copier *fc); + struct feat_copier *fc, + bool write_attrs_after_data); size_t perf_session__data_offset(const struct evlist *evlist); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 774eb3382000..5a51db036ee8 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -135,8 +135,8 @@ static int ordered_events__deliver_event(struct ordered_events *oe, } struct perf_session *__perf_session__new(struct perf_data *data, - bool repipe, int repipe_fd, - struct perf_tool *tool) + struct perf_tool *tool, + bool trace_event_repipe) { int ret = -ENOMEM; struct perf_session *session = zalloc(sizeof(*session)); @@ -144,7 +144,7 @@ struct perf_session *__perf_session__new(struct perf_data *data, if (!session) goto out; - session->repipe = repipe; + session->trace_event_repipe = trace_event_repipe; session->tool = tool; session->decomp_data.zstd_decomp = &session->zstd_data; session->active_decomp = &session->decomp_data; @@ -162,7 +162,7 @@ struct perf_session *__perf_session__new(struct perf_data *data, session->data = data; if (perf_data__is_read(data)) { - ret = perf_session__open(session, repipe_fd); + ret = perf_session__open(session, /*repipe_fd=*/-1); if (ret < 0) goto out_delete; diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index e56518639711..bcf1bcf06959 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -59,12 +59,8 @@ struct perf_session { #endif /** @time_conv: Holds contents of last PERF_RECORD_TIME_CONV event. */ struct perf_record_time_conv time_conv; - /** - * @repipe: When set causes certain reading (header and trace events) to - * also write events. The written file descriptor must be provided for - * the header but is implicitly stdout for trace events. - */ - bool repipe; + /** @trace_event_repipe: When set causes read trace events to be written to stdout. */ + bool trace_event_repipe; /** * @one_mmap: The reader will use a single mmap by default. There may be * multiple data files in particular for aux events. If this is true @@ -110,13 +106,13 @@ struct decomp { struct perf_tool; struct perf_session *__perf_session__new(struct perf_data *data, - bool repipe, int repipe_fd, - struct perf_tool *tool); + struct perf_tool *tool, + bool trace_event_repipe); static inline struct perf_session *perf_session__new(struct perf_data *data, struct perf_tool *tool) { - return __perf_session__new(data, false, -1, tool); + return __perf_session__new(data, tool, /*trace_event_repipe=*/false); } void perf_session__delete(struct perf_session *session); From 2d57c32b32fb84c6f6b2342bca85c9ba2b2d759b Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 29 Aug 2024 08:01:53 -0700 Subject: [PATCH 261/347] perf header: Remove repipe option No longer used by `perf inject` the repipe_fd is always -1 and repipe is always false. Remove the options and associated code knowing the constant values of the removed variables. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Nick Terrell Cc: Peter Zijlstra Cc: Yanteng Si Cc: Yicong Yang Link: https://lore.kernel.org/r/20240829150154.37929-8-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/header.c | 19 +++++-------------- tools/perf/util/header.h | 2 +- tools/perf/util/session.c | 6 +++--- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 59e2f37c1cb4..a6386d12afd7 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -4113,13 +4113,8 @@ static int perf_file_section__process(struct perf_file_section *section, static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, struct perf_header *ph, - struct perf_data* data, - bool repipe, int repipe_fd) + struct perf_data *data) { - struct feat_fd ff = { - .fd = repipe_fd, - .ph = ph, - }; ssize_t ret; ret = perf_data__read(data, header, sizeof(*header)); @@ -4134,19 +4129,15 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, if (ph->needs_swap) header->size = bswap_64(header->size); - if (repipe && do_write(&ff, header, sizeof(*header)) < 0) - return -1; - return 0; } -static int perf_header__read_pipe(struct perf_session *session, int repipe_fd) +static int perf_header__read_pipe(struct perf_session *session) { struct perf_header *header = &session->header; struct perf_pipe_file_header f_header; - if (perf_file_header__read_pipe(&f_header, header, session->data, - /*repipe=*/false, repipe_fd) < 0) { + if (perf_file_header__read_pipe(&f_header, header, session->data) < 0) { pr_debug("incompatible file format\n"); return -EINVAL; } @@ -4246,7 +4237,7 @@ static int evlist__prepare_tracepoint_events(struct evlist *evlist, struct tep_h } #endif -int perf_session__read_header(struct perf_session *session, int repipe_fd) +int perf_session__read_header(struct perf_session *session) { struct perf_data *data = session->data; struct perf_header *header = &session->header; @@ -4267,7 +4258,7 @@ int perf_session__read_header(struct perf_session *session, int repipe_fd) * We can read 'pipe' data event from regular file, * check for the pipe header regardless of source. */ - err = perf_header__read_pipe(session, repipe_fd); + err = perf_header__read_pipe(session); if (!err || perf_data__is_pipe(data)) { data->is_pipe = true; return err; diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 7137509cf6d8..a63a361f20f4 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -131,7 +131,7 @@ union perf_event; extern const char perf_version_string[]; -int perf_session__read_header(struct perf_session *session, int repipe_fd); +int perf_session__read_header(struct perf_session *session); int perf_session__write_header(struct perf_session *session, struct evlist *evlist, int fd, bool at_exit); diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index 5a51db036ee8..b492300ec959 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -45,11 +45,11 @@ static int perf_session__deliver_event(struct perf_session *session, u64 file_offset, const char *file_path); -static int perf_session__open(struct perf_session *session, int repipe_fd) +static int perf_session__open(struct perf_session *session) { struct perf_data *data = session->data; - if (perf_session__read_header(session, repipe_fd) < 0) { + if (perf_session__read_header(session) < 0) { pr_err("incompatible file format (rerun with -v to learn more)\n"); return -1; } @@ -162,7 +162,7 @@ struct perf_session *__perf_session__new(struct perf_data *data, session->data = data; if (perf_data__is_read(data)) { - ret = perf_session__open(session, /*repipe_fd=*/-1); + ret = perf_session__open(session); if (ret < 0) goto out_delete; From ccb9004656e55ea13e11e93862aa32aa575bf802 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 29 Aug 2024 08:01:54 -0700 Subject: [PATCH 262/347] perf test: Additional pipe tests with pipe output written to a file Additional pipe tests where piped files are written to disk. This means that spotting a file name of "-" isn't a sufficient "is pipe?" test. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Nick Terrell Cc: Peter Zijlstra Cc: Yanteng Si Cc: Yicong Yang Link: https://lore.kernel.org/r/20240829150154.37929-9-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/pipe_test.sh | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tools/perf/tests/shell/pipe_test.sh b/tools/perf/tests/shell/pipe_test.sh index ad10012fdc29..a3c94b4182c2 100755 --- a/tools/perf/tests/shell/pipe_test.sh +++ b/tools/perf/tests/shell/pipe_test.sh @@ -11,6 +11,7 @@ sym="noploop" skip_test_missing_symbol ${sym} data=$(mktemp /tmp/perf.data.XXXXXX) +data2=$(mktemp /tmp/perf.data2.XXXXXX) prog="perf test -w noploop" err=0 @@ -19,6 +20,8 @@ set -e cleanup() { rm -rf "${data}" rm -rf "${data}".old + rm -rf "${data2}" + rm -rf "${data2}".old trap - EXIT TERM INT } @@ -49,6 +52,14 @@ test_record_report() { return fi + perf record -g -e task-clock:u -o - ${prog} > ${data} + if ! perf report -i ${data} --task | grep -q ${task} + then + echo "Record+report pipe test [Failed - cannot find the test file in the perf report #3]" + err=1 + return + fi + echo "Record+report pipe test [Success]" } @@ -86,6 +97,21 @@ test_inject_bids() { return fi + perf record -e task-clock:u -o - ${prog} > ${data} + if ! perf inject ${inject_opt} -i ${data} | perf report -i - | grep -q ${sym}; then + echo "Inject ${inject_opt} build-ids test [Failed - cannot find noploop function in pipe #5]" + err=1 + return + fi + + perf record -e task-clock:u -o - ${prog} > ${data} + perf inject ${inject_opt} -i ${data} -o ${data2} + if ! perf report -i ${data2} | grep -q ${sym}; then + echo "Inject ${inject_opt} build-ids test [Failed - cannot find noploop function in pipe #6]" + err=1 + return + fi + echo "Inject ${inject_opt} build-ids test [Success]" } From 10d6c57c824e21b4cf8eda9491caea06edac9fd7 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 29 Aug 2024 23:51:48 -0700 Subject: [PATCH 263/347] perf lock contention: Handle error in a single place It has some duplicate codes to do the same job. Let's add a label and goto there to handle errors in a single place. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20240830065150.1758962-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf_skel/lock_contention.bpf.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/tools/perf/util/bpf_skel/lock_contention.bpf.c b/tools/perf/util/bpf_skel/lock_contention.bpf.c index d931a898c434..e8a6f6463019 100644 --- a/tools/perf/util/bpf_skel/lock_contention.bpf.c +++ b/tools/perf/util/bpf_skel/lock_contention.bpf.c @@ -439,11 +439,8 @@ int contention_end(u64 *ctx) duration = bpf_ktime_get_ns() - pelem->timestamp; if ((__s64)duration < 0) { - pelem->lock = 0; - if (need_delete) - bpf_map_delete_elem(&tstamp, &pid); __sync_fetch_and_add(&time_fail, 1); - return 0; + goto out; } switch (aggr_mode) { @@ -477,11 +474,8 @@ int contention_end(u64 *ctx) data = bpf_map_lookup_elem(&lock_stat, &key); if (!data) { if (data_map_full) { - pelem->lock = 0; - if (need_delete) - bpf_map_delete_elem(&tstamp, &pid); __sync_fetch_and_add(&data_fail, 1); - return 0; + goto out; } struct contention_data first = { @@ -502,10 +496,7 @@ int contention_end(u64 *ctx) data_map_full = 1; __sync_fetch_and_add(&data_fail, 1); } - pelem->lock = 0; - if (need_delete) - bpf_map_delete_elem(&tstamp, &pid); - return 0; + goto out; } __sync_fetch_and_add(&data->total_time, duration); @@ -517,6 +508,7 @@ int contention_end(u64 *ctx) if (data->min_time > duration) data->min_time = duration; +out: pelem->lock = 0; if (need_delete) bpf_map_delete_elem(&tstamp, &pid); From 05a5dd1dfd8fdeee6498875c2d78d4385d627bd6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 29 Aug 2024 23:51:49 -0700 Subject: [PATCH 264/347] perf lock contention: Simplify spinlock check The LCB_F_SPIN bit is used for spinlock, rwlock and optimistic spinning in mutex. In get_tstamp_elem() it needs to check spinlock and rwlock only. As mutex sets the LCB_F_MUTEX, it can check those two bits and reduce the number of operations. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20240830065150.1758962-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf_skel/lock_contention.bpf.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tools/perf/util/bpf_skel/lock_contention.bpf.c b/tools/perf/util/bpf_skel/lock_contention.bpf.c index e8a6f6463019..4b7237e178bd 100644 --- a/tools/perf/util/bpf_skel/lock_contention.bpf.c +++ b/tools/perf/util/bpf_skel/lock_contention.bpf.c @@ -323,8 +323,7 @@ static inline struct tstamp_data *get_tstamp_elem(__u32 flags) struct tstamp_data *pelem; /* Use per-cpu array map for spinlock and rwlock */ - if (flags == (LCB_F_SPIN | LCB_F_READ) || flags == LCB_F_SPIN || - flags == (LCB_F_SPIN | LCB_F_WRITE)) { + if ((flags & (LCB_F_SPIN | LCB_F_MUTEX)) == LCB_F_SPIN) { __u32 idx = 0; pelem = bpf_map_lookup_elem(&tstamp_cpu, &idx); From 36cddd105666d45a2cfb70a04c27b0c53bb383cd Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Thu, 29 Aug 2024 23:51:50 -0700 Subject: [PATCH 265/347] perf lock contention: Do not fail EEXIST for update When it updates the lock stat for the first time, it needs to create an element in the BPF hash map. But if there's a concurrent thread waiting for the same lock (like for rwsem or rwlock), it might race with the thread and possibly fail to update with -EEXIST. In that case, it can lookup the map again and put the data there instead of failing. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Cc: bpf@vger.kernel.org Link: https://lore.kernel.org/r/20240830065150.1758962-3-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf_skel/lock_contention.bpf.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/perf/util/bpf_skel/lock_contention.bpf.c b/tools/perf/util/bpf_skel/lock_contention.bpf.c index 4b7237e178bd..52a876b42699 100644 --- a/tools/perf/util/bpf_skel/lock_contention.bpf.c +++ b/tools/perf/util/bpf_skel/lock_contention.bpf.c @@ -491,6 +491,12 @@ int contention_end(u64 *ctx) err = bpf_map_update_elem(&lock_stat, &key, &first, BPF_NOEXIST); if (err < 0) { + if (err == -EEXIST) { + /* it lost the race, try to get it again */ + data = bpf_map_lookup_elem(&lock_stat, &key); + if (data != NULL) + goto found; + } if (err == -E2BIG) data_map_full = 1; __sync_fetch_and_add(&data_fail, 1); @@ -498,6 +504,7 @@ int contention_end(u64 *ctx) goto out; } +found: __sync_fetch_and_add(&data->total_time, duration); __sync_fetch_and_add(&data->count, 1); From 74fd69a35cae1d53d9c95b700aae312d77b0d1cb Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 27 Aug 2024 22:29:53 -0700 Subject: [PATCH 266/347] perf lock contention: Fix spinlock and rwlock accounting The spinlock and rwlock use a single-element per-cpu array to track current locks due to performance reason. But this means the key is always available and it cannot simply account lock stats in the array because some of them are invalid. In fact, the contention_end() program in the BPF invalidates the entry by setting the 'lock' value to 0 instead of deleting the entry for the hashmap. So it should skip entries with the lock value of 0 in the account_end_timestamp(). Otherwise, it'd have spurious high contention on an idle machine: $ sudo perf lock con -ab -Y spinlock sleep 3 contended total wait max wait avg wait type caller 8 4.72 s 1.84 s 590.46 ms spinlock rcu_core+0xc7 8 1.87 s 1.87 s 233.48 ms spinlock process_one_work+0x1b5 2 1.87 s 1.87 s 933.92 ms spinlock worker_thread+0x1a2 3 1.81 s 1.81 s 603.93 ms spinlock tmigr_update_events+0x13c 2 1.72 s 1.72 s 861.98 ms spinlock tick_do_update_jiffies64+0x25 6 42.48 us 13.02 us 7.08 us spinlock futex_q_lock+0x2a 1 13.03 us 13.03 us 13.03 us spinlock futex_wake+0xce 1 11.61 us 11.61 us 11.61 us spinlock rcu_core+0xc7 I don't believe it has contention on a spinlock longer than 1 second. After this change, it only reports some small contentions. $ sudo perf lock con -ab -Y spinlock sleep 3 contended total wait max wait avg wait type caller 4 133.51 us 43.29 us 33.38 us spinlock tick_do_update_jiffies64+0x25 4 69.06 us 31.82 us 17.27 us spinlock process_one_work+0x1b5 2 50.66 us 25.77 us 25.33 us spinlock rcu_core+0xc7 1 28.45 us 28.45 us 28.45 us spinlock rcu_core+0xc7 1 24.77 us 24.77 us 24.77 us spinlock tmigr_update_events+0x13c 1 23.34 us 23.34 us 23.34 us spinlock raw_spin_rq_lock_nested+0x15 Fixes: b5711042a1c8cc88 ("perf lock contention: Use per-cpu array map for spinlocks") Reported-by: Xi Wang Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: bpf@vger.kernel.org Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Song Liu Link: https://lore.kernel.org/r/20240828052953.1445862-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf_lock_contention.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c index b4cb3fe5cc25..bc4e92c0c08b 100644 --- a/tools/perf/util/bpf_lock_contention.c +++ b/tools/perf/util/bpf_lock_contention.c @@ -286,6 +286,9 @@ static void account_end_timestamp(struct lock_contention *con) goto next; for (int i = 0; i < total_cpus; i++) { + if (cpu_data[i].lock == 0) + continue; + update_lock_stat(stat_fd, -1, end_ts, aggr_mode, &cpu_data[i]); } From 39c243411bdb8fb35777adf49ee32549633c4e12 Mon Sep 17 00:00:00 2001 From: Yang Jihong Date: Mon, 19 Aug 2024 10:47:20 +0800 Subject: [PATCH 267/347] perf sched timehist: Fixed timestamp error when unable to confirm event sched_in time If sched_in event for current task is not recorded, sched_in timestamp will be set to end_time of time window interest, causing an error in timestamp show. In this case, we choose to ignore this event. Test scenario: perf[1229608] does not record the first sched_in event, run time and sch delay are both 0 # perf sched timehist Samples of sched_switch event do not have callchains. time cpu task name wait time sch delay run time [tid/pid] (msec) (msec) (msec) --------------- ------ ------------------------------ --------- --------- --------- 2090450.763231 [0000] perf[1229608] 0.000 0.000 0.000 2090450.763235 [0000] migration/0[15] 0.000 0.001 0.003 2090450.763263 [0001] perf[1229608] 0.000 0.000 0.000 2090450.763268 [0001] migration/1[21] 0.000 0.001 0.004 2090450.763302 [0002] perf[1229608] 0.000 0.000 0.000 2090450.763309 [0002] migration/2[27] 0.000 0.001 0.007 2090450.763338 [0003] perf[1229608] 0.000 0.000 0.000 2090450.763343 [0003] migration/3[33] 0.000 0.001 0.004 Before: arbitrarily specify a time window of interest, timestamp will be set to an incorrect value # perf sched timehist --time 100,200 Samples of sched_switch event do not have callchains. time cpu task name wait time sch delay run time [tid/pid] (msec) (msec) (msec) --------------- ------ ------------------------------ --------- --------- --------- 200.000000 [0000] perf[1229608] 0.000 0.000 0.000 200.000000 [0001] perf[1229608] 0.000 0.000 0.000 200.000000 [0002] perf[1229608] 0.000 0.000 0.000 200.000000 [0003] perf[1229608] 0.000 0.000 0.000 200.000000 [0004] perf[1229608] 0.000 0.000 0.000 200.000000 [0005] perf[1229608] 0.000 0.000 0.000 200.000000 [0006] perf[1229608] 0.000 0.000 0.000 200.000000 [0007] perf[1229608] 0.000 0.000 0.000 After: # perf sched timehist --time 100,200 Samples of sched_switch event do not have callchains. time cpu task name wait time sch delay run time [tid/pid] (msec) (msec) (msec) --------------- ------ ------------------------------ --------- --------- --------- Fixes: 853b74071110bed3 ("perf sched timehist: Add option to specify time window of interest") Signed-off-by: Yang Jihong Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: David Ahern Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240819024720.2405244-1-yangjihong@bytedance.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 9c1276dc8ef6..1c386ebe4b98 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2683,9 +2683,12 @@ static int timehist_sched_change_event(const 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) From 0e7eb23668948585f3f0ea8c6249338f33fde872 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 30 Aug 2024 19:53:47 -0300 Subject: [PATCH 268/347] perf tools: Build x86 32-bit syscall table from arch/x86/entry/syscalls/syscall_32.tbl To remove one more use of the audit libs and address a problem reported with a recent change where a function isn't available when using the audit libs method, that should really go away, this being one step in that direction. The script used to generate the 64-bit syscall table was already parametrized to generate for both 64-bit and 32-bit, so just use it and wire the generated table to the syscalltbl.c routines. Reported-by: Jiri Slaby Suggested-by: Ian Rogers Reviewed-by: Ian Rogers Tested-by: Jiri Slaby Cc: Adrian Hunter Cc: Howard Chu Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/6fe63fa3-6c63-4b75-ac09-884d26f6fb95@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 13 +- tools/perf/arch/x86/Makefile | 6 +- .../arch/x86/entry/syscalls/syscall_32.tbl | 470 ++++++++++++++++++ tools/perf/check-headers.sh | 1 + tools/perf/util/syscalltbl.c | 4 + 5 files changed, 484 insertions(+), 10 deletions(-) create mode 100644 tools/perf/arch/x86/entry/syscalls/syscall_32.tbl diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 4eb1fc897baf..9998cd7a8792 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -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) diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile index 8952e00f9b60..67b4969a6738 100644 --- a/tools/perf/arch/x86/Makefile +++ b/tools/perf/arch/x86/Makefile @@ -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) diff --git a/tools/perf/arch/x86/entry/syscalls/syscall_32.tbl b/tools/perf/arch/x86/entry/syscalls/syscall_32.tbl new file mode 100644 index 000000000000..534c74b14fab --- /dev/null +++ b/tools/perf/arch/x86/entry/syscalls/syscall_32.tbl @@ -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: +# [ [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 diff --git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh index 672421b858ac..714c78e5da07 100755 --- a/tools/perf/check-headers.sh +++ b/tools/perf/check-headers.sh @@ -172,6 +172,7 @@ check lib/ctype.c '-I "^EXPORT_SYMBOL" -I "^#include " -B check lib/list_sort.c '-I "^#include "' # 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 diff --git a/tools/perf/util/syscalltbl.c b/tools/perf/util/syscalltbl.c index 0dd26b991b3f..7c15dec6900d 100644 --- a/tools/perf/util/syscalltbl.c +++ b/tools/perf/util/syscalltbl.c @@ -18,6 +18,10 @@ #include const int syscalltbl_native_max_id = SYSCALLTBL_x86_64_MAX_ID; static const char *const *syscalltbl_native = syscalltbl_x86_64; +#elif defined(__i386__) +#include +const int syscalltbl_native_max_id = SYSCALLTBL_x86_MAX_ID; +static const char *const *syscalltbl_native = syscalltbl_x86; #elif defined(__s390x__) #include const int syscalltbl_native_max_id = SYSCALLTBL_S390_64_MAX_ID; From c3f8644c21df9b7db97eb70e08e2826368aaafa0 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sat, 3 Aug 2024 17:20:06 +0200 Subject: [PATCH 269/347] perf report: Support LLVM for addr2line() In addition to the existing support for libbfd and calling out to an external addr2line command, add support for using libllvm directly. This is both faster than libbfd, and can be enabled in distro builds (the LLVM license has an explicit provision for GPLv2 compatibility). Thus, it is set as the primary choice if available. As an example, running 'perf report' on a medium-size profile with DWARF-based backtraces took 58 seconds with LLVM, 78 seconds with libbfd, 153 seconds with external llvm-addr2line, and I got tired and aborted the test after waiting for 55 minutes with external bfd addr2line (which is the default for perf as compiled by distributions today). Evidently, for this case, the bfd addr2line process needs 18 seconds (on a 5.2 GHz Zen 3) to load the .debug ELF in question, hits the 1-second timeout and gets killed during initialization, getting restarted anew every time. Having an in-process addr2line makes this much more robust. As future extensions, libllvm can be used in many other places where we currently use libbfd or other libraries: - Symbol enumeration (in particular, for PE binaries). - Demangling (including non-Itanium demangling, e.g. Microsoft or Rust). - Disassembling (perf annotate). However, these are much less pressing; most people don't profile PE binaries, and perf has non-bfd paths for ELF. The same with demangling; the default _cxa_demangle path works fine for most users, and while bfd objdump can be slow on large binaries, it is possible to use --objdump=llvm-objdump to get the speed benefits. (It appears LLVM-based demangling is very simple, should we want that.) Tested with LLVM 14, 15, 16, 18 and 19. For some reason, LLVM 12 was not correctly detected using feature_check, and thus was not tested. Committer notes: Added the name and a __maybe_unused to address: 1 13.50 almalinux:8 : FAIL gcc version 8.5.0 20210514 (Red Hat 8.5.0-22) (GCC) util/srcline.c: In function 'dso__free_a2l': util/srcline.c:184:20: error: parameter name omitted void dso__free_a2l(struct dso *) ^~~~~~~~~~~~ make[3]: *** [/git/perf-6.11.0-rc3/tools/build/Makefile.build:158: util] Error 2 Signed-off-by: Steinar H. Gunderson Tested-by: Arnaldo Carvalho de Melo Cc: Ian Rogers Link: https://lore.kernel.org/r/20240803152008.2818485-1-sesse@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.feature | 1 + tools/perf/Makefile.config | 17 ++++ tools/perf/builtin-version.c | 1 + tools/perf/tests/make | 2 + tools/perf/util/Build | 1 + tools/perf/util/llvm-c-helpers.cpp | 134 +++++++++++++++++++++++++++++ tools/perf/util/llvm-c-helpers.h | 49 +++++++++++ tools/perf/util/srcline.c | 59 ++++++++++++- 8 files changed, 263 insertions(+), 1 deletion(-) create mode 100644 tools/perf/util/llvm-c-helpers.cpp create mode 100644 tools/perf/util/llvm-c-helpers.h diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index e1900abd44f6..0717e96d6a0e 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -136,6 +136,7 @@ FEATURE_DISPLAY ?= \ libunwind \ libdw-dwarf-unwind \ libcapstone \ + llvm \ zlib \ lzma \ get_cpuid \ diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 9998cd7a8792..7888c932b1b4 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -980,6 +980,23 @@ ifdef BUILD_NONDISTRO endif endif +ifndef NO_LIBLLVM + $(call feature_check,llvm) + ifeq ($(feature-llvm), 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 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) diff --git a/tools/perf/builtin-version.c b/tools/perf/builtin-version.c index 398aa53e9e2e..4b252196de12 100644 --- a/tools/perf/builtin-version.c +++ b/tools/perf/builtin-version.c @@ -65,6 +65,7 @@ static void library_status(void) STATUS(HAVE_LIBBFD_SUPPORT, libbfd); STATUS(HAVE_DEBUGINFOD_SUPPORT, debuginfod); STATUS(HAVE_LIBELF_SUPPORT, libelf); + STATUS(HAVE_LIBLLVM_SUPPORT, libllvm); STATUS(HAVE_LIBNUMA_SUPPORT, libnuma); STATUS(HAVE_LIBNUMA_SUPPORT, numa_num_possible_cpus); STATUS(HAVE_LIBPERL_SUPPORT, libperl); diff --git a/tools/perf/tests/make b/tools/perf/tests/make index 8a9edf758f10..a5040772043f 100644 --- a/tools/perf/tests/make +++ b/tools/perf/tests/make @@ -93,6 +93,7 @@ make_no_libbpf := NO_LIBBPF=1 make_libbpf_dynamic := LIBBPF_DYNAMIC=1 make_no_libbpf_DEBUG := NO_LIBBPF=1 DEBUG=1 make_no_libcrypto := NO_LIBCRYPTO=1 +make_no_libllvm := NO_LIBLLVM=1 make_with_babeltrace:= LIBBABELTRACE=1 make_with_coresight := CORESIGHT=1 make_no_sdt := NO_SDT=1 @@ -163,6 +164,7 @@ run += make_no_auxtrace run += make_no_libbpf run += make_no_libbpf_DEBUG run += make_no_libcrypto +run += make_no_libllvm run += make_no_sdt run += make_no_syscall_tbl run += make_with_babeltrace diff --git a/tools/perf/util/Build b/tools/perf/util/Build index 260cec2f6c0b..dc616292b2dd 100644 --- a/tools/perf/util/Build +++ b/tools/perf/util/Build @@ -229,6 +229,7 @@ perf-util-$(CONFIG_CXX_DEMANGLE) += demangle-cxx.o perf-util-y += demangle-ocaml.o perf-util-y += demangle-java.o perf-util-y += demangle-rust.o +perf-util-$(CONFIG_LIBLLVM) += llvm-c-helpers.o ifdef CONFIG_JITDUMP perf-util-$(CONFIG_LIBELF) += jitdump.o diff --git a/tools/perf/util/llvm-c-helpers.cpp b/tools/perf/util/llvm-c-helpers.cpp new file mode 100644 index 000000000000..3cc967ec6f28 --- /dev/null +++ b/tools/perf/util/llvm-c-helpers.cpp @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * Must come before the linux/compiler.h include, which defines several + * macros (e.g. noinline) that conflict with compiler builtins used + * by LLVM. + */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" /* Needed for LLVM <= 15 */ +#include +#pragma GCC diagnostic pop + +#include +#include +#include +extern "C" { +#include +} +#include "symbol_conf.h" +#include "llvm-c-helpers.h" + +using namespace llvm; +using llvm::symbolize::LLVMSymbolizer; + +/* + * Allocate a static LLVMSymbolizer, which will live to the end of the program. + * Unlike the bfd paths, LLVMSymbolizer has its own cache, so we do not need + * to store anything in the dso struct. + */ +static LLVMSymbolizer *get_symbolizer() +{ + static LLVMSymbolizer *instance = nullptr; + if (instance == nullptr) { + LLVMSymbolizer::Options opts; + /* + * LLVM sometimes demangles slightly different from the rest + * of the code, and this mismatch can cause new_inline_sym() + * to get confused and mark non-inline symbol as inlined + * (since the name does not properly match up with base_sym). + * Thus, disable the demangling and let the rest of the code + * handle it. + */ + opts.Demangle = false; + instance = new LLVMSymbolizer(opts); + } + return instance; +} + +/* Returns 0 on error, 1 on success. */ +static int extract_file_and_line(const DILineInfo &line_info, char **file, + unsigned int *line) +{ + if (file) { + if (line_info.FileName == "") { + /* Match the convention of libbfd. */ + *file = nullptr; + } else { + /* The caller expects to get something it can free(). */ + *file = strdup(line_info.FileName.c_str()); + if (*file == nullptr) + return 0; + } + } + if (line) + *line = line_info.Line; + return 1; +} + +extern "C" +int llvm_addr2line(const char *dso_name, u64 addr, + char **file, unsigned int *line, + bool unwind_inlines, + llvm_a2l_frame **inline_frames) +{ + LLVMSymbolizer *symbolizer = get_symbolizer(); + object::SectionedAddress sectioned_addr = { + addr, + object::SectionedAddress::UndefSection + }; + + if (unwind_inlines) { + Expected res_or_err = + symbolizer->symbolizeInlinedCode(dso_name, + sectioned_addr); + if (!res_or_err) + return 0; + unsigned num_frames = res_or_err->getNumberOfFrames(); + if (num_frames == 0) + return 0; + + if (extract_file_and_line(res_or_err->getFrame(0), + file, line) == 0) + return 0; + + *inline_frames = (llvm_a2l_frame *)calloc( + num_frames, sizeof(**inline_frames)); + if (*inline_frames == nullptr) + return 0; + + for (unsigned i = 0; i < num_frames; ++i) { + const DILineInfo &src = res_or_err->getFrame(i); + + llvm_a2l_frame &dst = (*inline_frames)[i]; + if (src.FileName == "") + /* Match the convention of libbfd. */ + dst.filename = nullptr; + else + dst.filename = strdup(src.FileName.c_str()); + dst.funcname = strdup(src.FunctionName.c_str()); + dst.line = src.Line; + + if (dst.filename == nullptr || + dst.funcname == nullptr) { + for (unsigned j = 0; j <= i; ++j) { + zfree(&(*inline_frames)[j].filename); + zfree(&(*inline_frames)[j].funcname); + } + zfree(inline_frames); + return 0; + } + } + + return num_frames; + } else { + if (inline_frames) + *inline_frames = nullptr; + + Expected res_or_err = + symbolizer->symbolizeCode(dso_name, sectioned_addr); + if (!res_or_err) + return 0; + return extract_file_and_line(*res_or_err, file, line); + } +} diff --git a/tools/perf/util/llvm-c-helpers.h b/tools/perf/util/llvm-c-helpers.h new file mode 100644 index 000000000000..19332dd98e14 --- /dev/null +++ b/tools/perf/util/llvm-c-helpers.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __PERF_LLVM_C_HELPERS +#define __PERF_LLVM_C_HELPERS 1 + +/* + * Helpers to call into LLVM C++ code from C, for the parts that do not have + * C APIs. + */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct llvm_a2l_frame { + char* filename; + char* funcname; + unsigned int line; +}; + +/* + * Implement addr2line() using libLLVM. LLVM is a C++ API, and + * many of the linux/ headers cannot be included in a C++ compile unit, + * so we need to make a little bridge code here. llvm_addr2line() will + * convert the inline frame information from LLVM's internal structures + * and put them into a flat array given in inline_frames. The caller + * is then responsible for taking that array and convert it into perf's + * regular inline frame structures (which depend on e.g. struct list_head). + * + * If the address could not be resolved, or an error occurred (e.g. OOM), + * returns 0. Otherwise, returns the number of inline frames (which means 1 + * if the address was not part of an inlined function). If unwind_inlines + * is set and the return code is nonzero, inline_frames will be set to + * a newly allocated array with that length. The caller is then responsible + * for freeing both the strings and the array itself. + */ +int llvm_addr2line(const char* dso_name, + u64 addr, + char** file, + unsigned int* line, + bool unwind_inlines, + struct llvm_a2l_frame** inline_frames); + +#ifdef __cplusplus +} +#endif + +#endif /* __PERF_LLVM_C_HELPERS */ diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index 760742fd4a7d..f32d0d4f4bc9 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -16,6 +17,9 @@ #include "util/debug.h" #include "util/callchain.h" #include "util/symbol_conf.h" +#ifdef HAVE_LIBLLVM_SUPPORT +#include "util/llvm-c-helpers.h" +#endif #include "srcline.h" #include "string2.h" #include "symbol.h" @@ -130,7 +134,60 @@ static struct symbol *new_inline_sym(struct dso *dso, #define MAX_INLINE_NEST 1024 -#ifdef HAVE_LIBBFD_SUPPORT +#ifdef HAVE_LIBLLVM_SUPPORT + +static void free_llvm_inline_frames(struct llvm_a2l_frame *inline_frames, + int num_frames) +{ + if (inline_frames != NULL) { + for (int i = 0; i < num_frames; ++i) { + zfree(&inline_frames[i].filename); + zfree(&inline_frames[i].funcname); + } + zfree(&inline_frames); + } +} + +static int addr2line(const char *dso_name, u64 addr, + char **file, unsigned int *line, struct dso *dso, + bool unwind_inlines, struct inline_node *node, + struct symbol *sym) +{ + struct llvm_a2l_frame *inline_frames = NULL; + int num_frames = llvm_addr2line(dso_name, addr, file, line, + node && unwind_inlines, &inline_frames); + + if (num_frames == 0 || !inline_frames) { + /* Error, or we didn't want inlines. */ + return num_frames; + } + + for (int i = 0; i < num_frames; ++i) { + struct symbol *inline_sym = + new_inline_sym(dso, sym, inline_frames[i].funcname); + char *srcline = NULL; + + if (inline_frames[i].filename) { + srcline = + srcline_from_fileline(inline_frames[i].filename, + inline_frames[i].line); + } + if (inline_list__append(inline_sym, srcline, node) != 0) { + free_llvm_inline_frames(inline_frames, num_frames); + return 0; + } + } + free_llvm_inline_frames(inline_frames, num_frames); + + return num_frames; +} + +void dso__free_a2l(struct dso *dso __maybe_unused) +{ + /* Nothing to free. */ +} + +#elif defined(HAVE_LIBBFD_SUPPORT) /* * Implement addr2line using libbfd. From 6eca7c5ac23effd552d6f03acc5ce0efc1ed1c1e Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sat, 3 Aug 2024 17:20:07 +0200 Subject: [PATCH 270/347] perf annotate: Split out read_symbol() The Capstone disassembler code has a useful code snippet to read the bytes for a given code symbol into memory. Split it out into its own function, so that the LLVM disassembler can use it in the next patch. Signed-off-by: Steinar H. Gunderson Cc: Ian Rogers Link: https://lore.kernel.org/r/20240803152008.2818485-2-sesse@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/disasm.c | 90 +++++++++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 34 deletions(-) diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index d11e75133b23..5e9be3487c6c 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -1372,6 +1372,53 @@ static int find_file_offset(u64 start, u64 len, u64 pgoff, void *arg) return 0; } +static u8 * +read_symbol(const char *filename, struct map *map, struct symbol *sym, + u64 *len, bool *is_64bit) +{ + struct dso *dso = map__dso(map); + struct nscookie nsc; + u64 start = map__rip_2objdump(map, sym->start); + u64 end = map__rip_2objdump(map, sym->end); + int fd, count; + u8 *buf = NULL; + struct find_file_offset_data data = { + .ip = start, + }; + + *is_64bit = false; + + nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); + fd = open(filename, O_RDONLY); + nsinfo__mountns_exit(&nsc); + if (fd < 0) + return NULL; + + if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data, + is_64bit) == 0) + goto err; + + *len = end - start; + buf = malloc(*len); + if (buf == NULL) + goto err; + + count = pread(fd, buf, *len, data.offset); + close(fd); + fd = -1; + + if ((u64)count != *len) + goto err; + + return buf; + +err: + if (fd >= 0) + close(fd); + free(buf); + return NULL; +} + static void print_capstone_detail(cs_insn *insn, char *buf, size_t len, struct annotate_args *args, u64 addr) { @@ -1572,19 +1619,13 @@ static int symbol__disassemble_capstone(char *filename, struct symbol *sym, { struct annotation *notes = symbol__annotation(sym); struct map *map = args->ms.map; - struct dso *dso = map__dso(map); - struct nscookie nsc; u64 start = map__rip_2objdump(map, sym->start); - u64 end = map__rip_2objdump(map, sym->end); - u64 len = end - start; + u64 len; u64 offset; - int i, fd, count; + int i, count; bool is_64bit = false; bool needs_cs_close = false; u8 *buf = NULL; - struct find_file_offset_data data = { - .ip = start, - }; csh handle; cs_insn *insn; char disasm_buf[512]; @@ -1593,31 +1634,9 @@ static int symbol__disassemble_capstone(char *filename, struct symbol *sym, if (args->options->objdump_path) return -1; - nsinfo__mountns_enter(dso__nsinfo(dso), &nsc); - fd = open(filename, O_RDONLY); - nsinfo__mountns_exit(&nsc); - if (fd < 0) - return -1; - - if (file__read_maps(fd, /*exe=*/true, find_file_offset, &data, - &is_64bit) == 0) - goto err; - - if (open_capstone_handle(args, is_64bit, &handle) < 0) - goto err; - - needs_cs_close = true; - - buf = malloc(len); + buf = read_symbol(filename, map, sym, &len, &is_64bit); if (buf == NULL) - goto err; - - count = pread(fd, buf, len, data.offset); - close(fd); - fd = -1; - - if ((u64)count != len) - goto err; + return -1; /* add the function address and name */ scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:", @@ -1635,6 +1654,11 @@ static int symbol__disassemble_capstone(char *filename, struct symbol *sym, annotation_line__add(&dl->al, ¬es->src->source); + if (open_capstone_handle(args, is_64bit, &handle) < 0) + goto err; + + needs_cs_close = true; + count = cs_disasm(handle, buf, len, start, len, &insn); for (i = 0, offset = 0; i < count; i++) { int printed; @@ -1679,8 +1703,6 @@ out: return count < 0 ? count : 0; err: - if (fd >= 0) - close(fd); if (needs_cs_close) { struct disasm_line *tmp; From 04885681788855bb266fd131f06c04952a717659 Mon Sep 17 00:00:00 2001 From: "Steinar H. Gunderson" Date: Sat, 3 Aug 2024 17:20:08 +0200 Subject: [PATCH 271/347] perf annotate: LLVM-based disassembler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support using LLVM as a disassembler method, allowing helperless annotation in non-distro builds. (It is also much faster than using libbfd or bfd objdump on binaries with a lot of debug information.) This is nearly identical to the output of llvm-objdump; there are some very rare whitespace differences, some minor changes to demangling (since we use perf's regular demangling and not LLVM's own) and the occasional case where llvm-objdump makes a different choice when multiple symbols share the same address. It should work across all of LLVM's supported architectures, although I've only tested 64-bit x86, and finding the right triple from perf's idea of machine architecture can sometimes be a bit tricky. Ideally, we should have some way of finding the triplet just from the file itself. Committer notes: Address this on 32-bit systems by using PRIu64 from inttypes.h 3 17.58 almalinux:9-i386 : FAIL gcc version 11.4.1 20231218 (Red Hat 11.4.1-3) (GCC) util/llvm-c-helpers.cpp: In function ‘char* make_symbol_relative_string(dso*, const char*, u64, u64)’: util/llvm-c-helpers.cpp:150:52: error: format ‘%lx’ expects argument of type ‘long unsigned int’, but argument 5 has type ‘u64’ {aka +‘long long unsigned int’} [-Werror=format=] 150 | snprintf(buf, sizeof(buf), "%s+0x%lx", | ~~^ | | | long unsigned int | %llx 151 | demangled ? demangled : sym_name, addr - base_addr); | ~~~~~~~~~~~~~~~~ | | | u64 {aka long long unsigned int} cc1plus: all warnings being treated as errors Signed-off-by: Steinar H. Gunderson Cc: Ian Rogers Link: https://lore.kernel.org/r/20240803152008.2818485-3-sesse@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/disasm.c | 193 +++++++++++++++++++++++++++++ tools/perf/util/llvm-c-helpers.cpp | 63 ++++++++++ tools/perf/util/llvm-c-helpers.h | 11 ++ 3 files changed, 267 insertions(+) diff --git a/tools/perf/util/disasm.c b/tools/perf/util/disasm.c index 5e9be3487c6c..f05ba7739c1e 100644 --- a/tools/perf/util/disasm.c +++ b/tools/perf/util/disasm.c @@ -48,6 +48,7 @@ static int call__scnprintf(struct ins *ins, char *bf, size_t size, static void ins__sort(struct arch *arch); static int disasm_line__parse(char *line, const char **namep, char **rawp); static int disasm_line__parse_powerpc(struct disasm_line *dl); +static char *expand_tabs(char *line, char **storage, size_t *storage_len); static __attribute__((constructor)) void symbol__init_regexpr(void) { @@ -1354,7 +1355,9 @@ static int open_capstone_handle(struct annotate_args *args, bool is_64bit, return 0; } +#endif +#if defined(HAVE_LIBCAPSTONE_SUPPORT) || defined(HAVE_LIBLLVM_SUPPORT) struct find_file_offset_data { u64 ip; u64 offset; @@ -1418,7 +1421,9 @@ err: free(buf); return NULL; } +#endif +#ifdef HAVE_LIBCAPSTONE_SUPPORT static void print_capstone_detail(cs_insn *insn, char *buf, size_t len, struct annotate_args *args, u64 addr) { @@ -1805,6 +1810,189 @@ err: count = -1; goto out; } + +#ifdef HAVE_LIBLLVM_SUPPORT +#include +#include +#include "util/llvm-c-helpers.h" + +struct symbol_lookup_storage { + u64 branch_addr; + u64 pcrel_load_addr; +}; + +/* + * Whenever LLVM wants to resolve an address into a symbol, it calls this + * callback. We don't ever actually _return_ anything (in particular, because + * it puts quotation marks around what we return), but we use this as a hint + * that there is a branch or PC-relative address in the expression that we + * should add some textual annotation for after the instruction. The caller + * will use this information to add the actual annotation. + */ +static const char * +symbol_lookup_callback(void *disinfo, uint64_t value, + uint64_t *ref_type, + uint64_t address __maybe_unused, + const char **ref __maybe_unused) +{ + struct symbol_lookup_storage *storage = disinfo; + + if (*ref_type == LLVMDisassembler_ReferenceType_In_Branch) + storage->branch_addr = value; + else if (*ref_type == LLVMDisassembler_ReferenceType_In_PCrel_Load) + storage->pcrel_load_addr = value; + *ref_type = LLVMDisassembler_ReferenceType_InOut_None; + return NULL; +} + +static int symbol__disassemble_llvm(char *filename, struct symbol *sym, + struct annotate_args *args) +{ + struct annotation *notes = symbol__annotation(sym); + struct map *map = args->ms.map; + struct dso *dso = map__dso(map); + u64 start = map__rip_2objdump(map, sym->start); + u8 *buf; + u64 len; + u64 pc; + bool is_64bit; + char triplet[64]; + char disasm_buf[2048]; + size_t disasm_len; + struct disasm_line *dl; + LLVMDisasmContextRef disasm = NULL; + struct symbol_lookup_storage storage; + char *line_storage = NULL; + size_t line_storage_len = 0; + int ret = -1; + + if (args->options->objdump_path) + return -1; + + LLVMInitializeAllTargetInfos(); + LLVMInitializeAllTargetMCs(); + LLVMInitializeAllDisassemblers(); + + buf = read_symbol(filename, map, sym, &len, &is_64bit); + if (buf == NULL) + return -1; + + if (arch__is(args->arch, "x86")) { + if (is_64bit) + scnprintf(triplet, sizeof(triplet), "x86_64-pc-linux"); + else + scnprintf(triplet, sizeof(triplet), "i686-pc-linux"); + } else { + scnprintf(triplet, sizeof(triplet), "%s-linux-gnu", + args->arch->name); + } + + disasm = LLVMCreateDisasm(triplet, &storage, 0, NULL, + symbol_lookup_callback); + if (disasm == NULL) + goto err; + + if (args->options->disassembler_style && + !strcmp(args->options->disassembler_style, "intel")) + LLVMSetDisasmOptions(disasm, + LLVMDisassembler_Option_AsmPrinterVariant); + + /* + * This needs to be set after AsmPrinterVariant, due to a bug in LLVM; + * setting AsmPrinterVariant makes a new instruction printer, making it + * forget about the PrintImmHex flag (which is applied before if both + * are given to the same call). + */ + LLVMSetDisasmOptions(disasm, LLVMDisassembler_Option_PrintImmHex); + + /* add the function address and name */ + scnprintf(disasm_buf, sizeof(disasm_buf), "%#"PRIx64" <%s>:", + start, sym->name); + + args->offset = -1; + args->line = disasm_buf; + args->line_nr = 0; + args->fileloc = NULL; + args->ms.sym = sym; + + dl = disasm_line__new(args); + if (dl == NULL) + goto err; + + annotation_line__add(&dl->al, ¬es->src->source); + + pc = start; + for (u64 offset = 0; offset < len; ) { + unsigned int ins_len; + + storage.branch_addr = 0; + storage.pcrel_load_addr = 0; + + ins_len = LLVMDisasmInstruction(disasm, buf + offset, + len - offset, pc, + disasm_buf, sizeof(disasm_buf)); + if (ins_len == 0) + goto err; + disasm_len = strlen(disasm_buf); + + if (storage.branch_addr != 0) { + char *name = llvm_name_for_code(dso, filename, + storage.branch_addr); + if (name != NULL) { + disasm_len += scnprintf(disasm_buf + disasm_len, + sizeof(disasm_buf) - + disasm_len, + " <%s>", name); + free(name); + } + } + if (storage.pcrel_load_addr != 0) { + char *name = llvm_name_for_data(dso, filename, + storage.pcrel_load_addr); + disasm_len += scnprintf(disasm_buf + disasm_len, + sizeof(disasm_buf) - disasm_len, + " # %#"PRIx64, + storage.pcrel_load_addr); + if (name) { + disasm_len += scnprintf(disasm_buf + disasm_len, + sizeof(disasm_buf) - + disasm_len, + " <%s>", name); + free(name); + } + } + + args->offset = offset; + args->line = expand_tabs(disasm_buf, &line_storage, + &line_storage_len); + args->line_nr = 0; + args->fileloc = NULL; + args->ms.sym = sym; + + llvm_addr2line(filename, pc, &args->fileloc, + (unsigned int *)&args->line_nr, false, NULL); + + dl = disasm_line__new(args); + if (dl == NULL) + goto err; + + annotation_line__add(&dl->al, ¬es->src->source); + + free(args->fileloc); + pc += ins_len; + offset += ins_len; + } + + ret = 0; + +err: + LLVMDisasmDispose(disasm); + free(buf); + free(line_storage); + return ret; +} +#endif + /* * Possibly create a new version of line with tabs expanded. Returns the * existing or new line, storage is updated if a new line is allocated. If @@ -1951,6 +2139,11 @@ int symbol__disassemble(struct symbol *sym, struct annotate_args *args) } } +#ifdef HAVE_LIBLLVM_SUPPORT + err = symbol__disassemble_llvm(symfs_filename, sym, args); + if (err == 0) + goto out_remove_tmp; +#endif #ifdef HAVE_LIBCAPSTONE_SUPPORT err = symbol__disassemble_capstone(symfs_filename, sym, args); if (err == 0) diff --git a/tools/perf/util/llvm-c-helpers.cpp b/tools/perf/util/llvm-c-helpers.cpp index 3cc967ec6f28..663bcaba2041 100644 --- a/tools/perf/util/llvm-c-helpers.cpp +++ b/tools/perf/util/llvm-c-helpers.cpp @@ -8,8 +8,10 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" /* Needed for LLVM <= 15 */ #include +#include #pragma GCC diagnostic pop +#include #include #include #include @@ -19,6 +21,9 @@ extern "C" { #include "symbol_conf.h" #include "llvm-c-helpers.h" +extern "C" +char *dso__demangle_sym(struct dso *dso, int kmodule, const char *elf_name); + using namespace llvm; using llvm::symbolize::LLVMSymbolizer; @@ -132,3 +137,61 @@ int llvm_addr2line(const char *dso_name, u64 addr, return extract_file_and_line(*res_or_err, file, line); } } + +static char * +make_symbol_relative_string(struct dso *dso, const char *sym_name, + u64 addr, u64 base_addr) +{ + if (!strcmp(sym_name, "")) + return NULL; + + char *demangled = dso__demangle_sym(dso, 0, sym_name); + if (base_addr && base_addr != addr) { + char buf[256]; + snprintf(buf, sizeof(buf), "%s+0x%" PRIx64, + demangled ? demangled : sym_name, addr - base_addr); + free(demangled); + return strdup(buf); + } else { + if (demangled) + return demangled; + else + return strdup(sym_name); + } +} + +extern "C" +char *llvm_name_for_code(struct dso *dso, const char *dso_name, u64 addr) +{ + LLVMSymbolizer *symbolizer = get_symbolizer(); + object::SectionedAddress sectioned_addr = { + addr, + object::SectionedAddress::UndefSection + }; + Expected res_or_err = + symbolizer->symbolizeCode(dso_name, sectioned_addr); + if (!res_or_err) { + return NULL; + } + return make_symbol_relative_string( + dso, res_or_err->FunctionName.c_str(), + addr, res_or_err->StartAddress ? *res_or_err->StartAddress : 0); +} + +extern "C" +char *llvm_name_for_data(struct dso *dso, const char *dso_name, u64 addr) +{ + LLVMSymbolizer *symbolizer = get_symbolizer(); + object::SectionedAddress sectioned_addr = { + addr, + object::SectionedAddress::UndefSection + }; + Expected res_or_err = + symbolizer->symbolizeData(dso_name, sectioned_addr); + if (!res_or_err) { + return NULL; + } + return make_symbol_relative_string( + dso, res_or_err->Name.c_str(), + addr, res_or_err->Start); +} diff --git a/tools/perf/util/llvm-c-helpers.h b/tools/perf/util/llvm-c-helpers.h index 19332dd98e14..d2b99637a28a 100644 --- a/tools/perf/util/llvm-c-helpers.h +++ b/tools/perf/util/llvm-c-helpers.h @@ -13,6 +13,8 @@ extern "C" { #endif +struct dso; + struct llvm_a2l_frame { char* filename; char* funcname; @@ -42,6 +44,15 @@ int llvm_addr2line(const char* dso_name, bool unwind_inlines, struct llvm_a2l_frame** inline_frames); +/* + * Simple symbolizers for addresses; will convert something like + * 0x12345 to "func+0x123". Will return NULL if no symbol was found. + * + * The returned value must be freed by the caller, with free(). + */ +char *llvm_name_for_code(struct dso *dso, const char *dso_name, u64 addr); +char *llvm_name_for_data(struct dso *dso, const char *dso_name, u64 addr); + #ifdef __cplusplus } #endif From 6c99903e084c68887f7bc52a6819117f26820667 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 31 Aug 2024 00:04:10 -0700 Subject: [PATCH 272/347] perf pmus: Fix name comparisons on 32-bit systems The hex PMU suffix maybe 64-bit but the comparisons were "unsigned long" or 32-bit on 32-bit systems. This was causing the "PMU name comparison" test to fail in a 32-bit build. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Athira Rajeev Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: David Ahern Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Junhao He Cc: Kan Liang Cc: Mark Rutland Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Yang Jihong Link: https://lore.kernel.org/r/20240831070415.506194-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/pmus.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index 3fcabfd8fca1..769b920d9250 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -69,7 +69,7 @@ size_t pmu_name_len_no_suffix(const char *str) int pmu_name_cmp(const char *lhs_pmu_name, const char *rhs_pmu_name) { - unsigned long lhs_num = 0, rhs_num = 0; + unsigned long long lhs_num = 0, rhs_num = 0; size_t lhs_pmu_name_len = pmu_name_len_no_suffix(lhs_pmu_name); size_t rhs_pmu_name_len = pmu_name_len_no_suffix(rhs_pmu_name); int ret = strncmp(lhs_pmu_name, rhs_pmu_name, @@ -79,9 +79,9 @@ int pmu_name_cmp(const char *lhs_pmu_name, const char *rhs_pmu_name) return ret; if (lhs_pmu_name_len + 1 < strlen(lhs_pmu_name)) - lhs_num = strtoul(&lhs_pmu_name[lhs_pmu_name_len + 1], NULL, 16); + lhs_num = strtoull(&lhs_pmu_name[lhs_pmu_name_len + 1], NULL, 16); if (rhs_pmu_name_len + 1 < strlen(rhs_pmu_name)) - rhs_num = strtoul(&rhs_pmu_name[rhs_pmu_name_len + 1], NULL, 16); + rhs_num = strtoull(&rhs_pmu_name[rhs_pmu_name_len + 1], NULL, 16); return lhs_num < rhs_num ? -1 : (lhs_num > rhs_num ? 1 : 0); } From 38e2648a81204c9fc5b4c87a8ffce93a6ed91b65 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 31 Aug 2024 00:04:11 -0700 Subject: [PATCH 273/347] perf time-utils: Fix 32-bit nsec parsing The "time utils" test fails in 32-bit builds: ... parse_nsec_time("18446744073.709551615") Failed. ptime 4294967295709551615 expected 18446744073709551615 ... Switch strtoul to strtoull as an unsigned long in 32-bit build isn't 64-bits. Fixes: c284d669a20d408b ("perf tools: Move parse_nsec_time to time-utils.c") Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Athira Rajeev Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: David Ahern Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Junhao He Cc: Kan Liang Cc: Mark Rutland Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Yang Jihong Link: https://lore.kernel.org/r/20240831070415.506194-3-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/time-utils.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/time-utils.c b/tools/perf/util/time-utils.c index 302443921681..1b91ccd4d523 100644 --- a/tools/perf/util/time-utils.c +++ b/tools/perf/util/time-utils.c @@ -20,7 +20,7 @@ int parse_nsec_time(const char *str, u64 *ptime) u64 time_sec, time_nsec; char *end; - time_sec = strtoul(str, &end, 10); + time_sec = strtoull(str, &end, 10); if (*end != '.' && *end != '\0') return -1; @@ -38,7 +38,7 @@ int parse_nsec_time(const char *str, u64 *ptime) for (i = strlen(nsec_buf); i < 9; i++) nsec_buf[i] = '0'; - time_nsec = strtoul(nsec_buf, &end, 10); + time_nsec = strtoull(nsec_buf, &end, 10); if (*end != '\0') return -1; } else From 91235380e5c78dc02f18d6afa97dff82ab3a6887 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 31 Aug 2024 00:04:12 -0700 Subject: [PATCH 274/347] perf test: Skip uprobe test if probe command isn't present The probe command is dependent on libelf. Skip the test if the required probe command isn't present. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Athira Rajeev Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: David Ahern Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Junhao He Cc: Kan Liang Cc: Mark Rutland Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Yang Jihong Link: https://lore.kernel.org/r/20240831070415.506194-4-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_uprobe_from_different_cu.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/perf/tests/shell/test_uprobe_from_different_cu.sh b/tools/perf/tests/shell/test_uprobe_from_different_cu.sh index 82bc774a078a..33387c329f92 100755 --- a/tools/perf/tests/shell/test_uprobe_from_different_cu.sh +++ b/tools/perf/tests/shell/test_uprobe_from_different_cu.sh @@ -4,6 +4,13 @@ set -e +# Skip if there's no probe command. +if ! perf | grep probe +then + echo "Skip: probe command isn't present" + exit 2 +fi + # skip if there's no gcc if ! [ -x "$(command -v gcc)" ]; then echo "failed: no gcc compiler" From 18f41f1ba5404cb4ca17590ba28258f8358f8695 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Sat, 31 Aug 2024 00:04:15 -0700 Subject: [PATCH 275/347] perf test: Make watchpoint data 32-bits on i386 i386 only supports watchpoints up to size 4, 8 bytes causes extra counts and test failures. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Athira Rajeev Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: David Ahern Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Junhao He Cc: Kan Liang Cc: Mark Rutland Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Yang Jihong Link: https://lore.kernel.org/r/20240831070415.506194-7-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/wp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/perf/tests/wp.c b/tools/perf/tests/wp.c index cc8719609b19..6c178985e37f 100644 --- a/tools/perf/tests/wp.c +++ b/tools/perf/tests/wp.c @@ -20,7 +20,12 @@ do { \ TEST_ASSERT_VAL(text, count == val); \ } while (0) +#ifdef __i386__ +/* Only breakpoint length less-than 8 has hardware support on i386. */ +volatile u32 data1; +#else volatile u64 data1; +#endif volatile u8 data2[3]; #ifndef __s390x__ From 76d3685400944e63c2c6e791a72d5329c2843770 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 2 Sep 2024 13:05:11 -0700 Subject: [PATCH 276/347] perf stat: Constify control data for BPF The control knobs set before loading BPF programs should be declared as 'const volatile' so that it can be optimized by the BPF core. Committer testing: root@x1:~# perf stat --bpf-counters -e cpu_core/cycles/,cpu_core/instructions/ sleep 1 Performance counter stats for 'sleep 1': 2,442,583 cpu_core/cycles/ 2,494,425 cpu_core/instructions/ 1.002687372 seconds time elapsed 0.001126000 seconds user 0.001166000 seconds sys root@x1:~# perf trace -e bpf --max-events 10 perf stat --bpf-counters -e cpu_core/cycles/,cpu_core/instructions/ sleep 1 0.000 ( 0.019 ms): perf/2944119 bpf(cmd: OBJ_GET, uattr: 0x7fffdf5cdd40, size: 20) = 5 0.021 ( 0.002 ms): perf/2944119 bpf(cmd: OBJ_GET_INFO_BY_FD, uattr: 0x7fffdf5cdcd0, size: 16) = 0 0.030 ( 0.005 ms): perf/2944119 bpf(cmd: MAP_LOOKUP_ELEM, uattr: 0x7fffdf5ceda0, size: 32) = 0 0.037 ( 0.004 ms): perf/2944119 bpf(cmd: LINK_GET_FD_BY_ID, uattr: 0x7fffdf5ced80, size: 12) = -1 ENOENT (No such file or directory) 0.189 ( 0.004 ms): perf/2944119 bpf(cmd: 36, uattr: 0x7fffdf5cec10, size: 8) = -1 EOPNOTSUPP (Operation not supported) 0.201 ( 0.095 ms): perf/2944119 bpf(cmd: PROG_LOAD, uattr: 0x7fffdf5ce940, size: 148) = 10 0.305 ( 0.026 ms): perf/2944119 bpf(cmd: PROG_LOAD, uattr: 0x7fffdf5cea00, size: 148) = 10 0.347 ( 0.012 ms): perf/2944119 bpf(cmd: BTF_LOAD, uattr: 0x7fffdf5ce8e0, size: 40) = 10 0.364 ( 0.004 ms): perf/2944119 bpf(cmd: BTF_LOAD, uattr: 0x7fffdf5ce950, size: 40) = 10 0.376 ( 0.006 ms): perf/2944119 bpf(cmd: BTF_LOAD, uattr: 0x7fffdf5ce730, size: 40) = 10 root@x1:~# Performance counter stats for 'sleep 1': 271,221 cpu_core/cycles/ 139,150 cpu_core/instructions/ 1.002881677 seconds time elapsed 0.001318000 seconds user 0.001314000 seconds sys root@x1:~# Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Link: https://lore.kernel.org/r/20240902200515.2103769-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf_counter_cgroup.c | 6 +++--- tools/perf/util/bpf_skel/bperf_cgroup.bpf.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/util/bpf_counter_cgroup.c b/tools/perf/util/bpf_counter_cgroup.c index ea29c372f339..6ff42619de12 100644 --- a/tools/perf/util/bpf_counter_cgroup.c +++ b/tools/perf/util/bpf_counter_cgroup.c @@ -61,6 +61,9 @@ static int bperf_load_program(struct evlist *evlist) skel->rodata->num_cpus = total_cpus; skel->rodata->num_events = evlist->core.nr_entries / nr_cgroups; + if (cgroup_is_v2("perf_event") > 0) + skel->rodata->use_cgroup_v2 = 1; + BUG_ON(evlist->core.nr_entries % nr_cgroups != 0); /* we need one copy of events per cpu for reading */ @@ -82,9 +85,6 @@ static int bperf_load_program(struct evlist *evlist) goto out; } - if (cgroup_is_v2("perf_event") > 0) - skel->bss->use_cgroup_v2 = 1; - err = -1; cgrp_switch = evsel__new(&cgrp_switch_attr); diff --git a/tools/perf/util/bpf_skel/bperf_cgroup.bpf.c b/tools/perf/util/bpf_skel/bperf_cgroup.bpf.c index 6a438e0102c5..57cab7647a9a 100644 --- a/tools/perf/util/bpf_skel/bperf_cgroup.bpf.c +++ b/tools/perf/util/bpf_skel/bperf_cgroup.bpf.c @@ -57,9 +57,9 @@ struct cgroup___old { const volatile __u32 num_events = 1; const volatile __u32 num_cpus = 1; +const volatile int use_cgroup_v2 = 0; int enabled = 0; -int use_cgroup_v2 = 0; int perf_subsys_id = -1; static inline __u64 get_cgroup_v1_ancestor_id(struct cgroup *cgrp, int level) From ac5a23b2f2868c54fdd2fc84c1990ebeb1e06188 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 2 Sep 2024 13:05:12 -0700 Subject: [PATCH 277/347] perf ftrace latency: Constify control data for BPF The control knobs set before loading BPF programs should be declared as 'const volatile' so that it can be optimized by the BPF core. Committer testing: root@x1:~# perf ftrace latency --use-bpf -T schedule ^C# DURATION | COUNT | GRAPH | 0 - 1 us | 0 | | 1 - 2 us | 0 | | 2 - 4 us | 0 | | 4 - 8 us | 0 | | 8 - 16 us | 1 | | 16 - 32 us | 5 | | 32 - 64 us | 2 | | 64 - 128 us | 6 | | 128 - 256 us | 7 | | 256 - 512 us | 5 | | 512 - 1024 us | 22 | # | 1 - 2 ms | 36 | ## | 2 - 4 ms | 68 | ##### | 4 - 8 ms | 22 | # | 8 - 16 ms | 91 | ####### | 16 - 32 ms | 11 | | 32 - 64 ms | 26 | ## | 64 - 128 ms | 213 | ################# | 128 - 256 ms | 19 | # | 256 - 512 ms | 14 | # | 512 - 1024 ms | 5 | | 1 - ... s | 8 | | root@x1:~# root@x1:~# perf trace -e bpf perf ftrace latency --use-bpf -T schedule 0.000 ( 0.015 ms): perf/2944525 bpf(cmd: 36, uattr: 0x7ffe80de7b40, size: 8) = -1 EOPNOTSUPP (Operation not supported) 0.025 ( 0.102 ms): perf/2944525 bpf(cmd: PROG_LOAD, uattr: 0x7ffe80de7870, size: 148) = 8 0.136 ( 0.026 ms): perf/2944525 bpf(cmd: PROG_LOAD, uattr: 0x7ffe80de7930, size: 148) = 8 0.174 ( 0.026 ms): perf/2944525 bpf(cmd: PROG_LOAD, uattr: 0x7ffe80de77e0, size: 148) = 8 0.205 ( 0.010 ms): perf/2944525 bpf(uattr: 0x7ffe80de7990, size: 80) = 8 0.227 ( 0.011 ms): perf/2944525 bpf(cmd: BTF_LOAD, uattr: 0x7ffe80de7810, size: 40) = 8 0.244 ( 0.004 ms): perf/2944525 bpf(cmd: BTF_LOAD, uattr: 0x7ffe80de7880, size: 40) = 8 0.257 ( 0.006 ms): perf/2944525 bpf(cmd: BTF_LOAD, uattr: 0x7ffe80de7660, size: 40) = 8 0.265 ( 0.058 ms): perf/2944525 bpf(cmd: PROG_LOAD, uattr: 0x7ffe80de7730, size: 148) = 9 0.330 ( 0.004 ms): perf/2944525 bpf(cmd: BTF_LOAD, uattr: 0x7ffe80de78e0, size: 40) = 8 0.337 ( 0.003 ms): perf/2944525 bpf(cmd: BTF_LOAD, uattr: 0x7ffe80de7890, size: 40) = 8 0.343 ( 0.004 ms): perf/2944525 bpf(cmd: BTF_LOAD, uattr: 0x7ffe80de7880, size: 40) = 8 0.349 ( 0.003 ms): perf/2944525 bpf(cmd: BTF_LOAD, uattr: 0x7ffe80de78b0, size: 40) = 8 0.355 ( 0.004 ms): perf/2944525 bpf(cmd: BTF_LOAD, uattr: 0x7ffe80de7890, size: 40) = 8 0.361 ( 0.003 ms): perf/2944525 bpf(cmd: BTF_LOAD, uattr: 0x7ffe80de78b0, size: 40) = 8 0.367 ( 0.003 ms): perf/2944525 bpf(cmd: BTF_LOAD, uattr: 0x7ffe80de7880, size: 40) = 8 0.373 ( 0.014 ms): perf/2944525 bpf(cmd: BTF_LOAD, uattr: 0x7ffe80de7a00, size: 40) = 8 0.390 ( 0.358 ms): perf/2944525 bpf(uattr: 0x7ffe80de7950, size: 80) = 9 0.763 ( 0.014 ms): perf/2944525 bpf(uattr: 0x7ffe80de7950, size: 80) = 9 0.783 ( 0.011 ms): perf/2944525 bpf(uattr: 0x7ffe80de7950, size: 80) = 9 0.798 ( 0.017 ms): perf/2944525 bpf(uattr: 0x7ffe80de7950, size: 80) = 9 0.819 ( 0.003 ms): perf/2944525 bpf(uattr: 0x7ffe80de7700, size: 80) = 9 0.824 ( 0.047 ms): perf/2944525 bpf(cmd: PROG_LOAD, uattr: 0x7ffe80de76c0, size: 148) = 10 0.878 ( 0.008 ms): perf/2944525 bpf(uattr: 0x7ffe80de7950, size: 80) = 9 0.891 ( 0.014 ms): perf/2944525 bpf(cmd: MAP_UPDATE_ELEM, uattr: 0x7ffe80de79e0, size: 32) = 0 0.910 ( 0.103 ms): perf/2944525 bpf(cmd: PROG_LOAD, uattr: 0x7ffe80de7880, size: 148) = 9 1.016 ( 0.143 ms): perf/2944525 bpf(cmd: PROG_LOAD, uattr: 0x7ffe80de7880, size: 148) = 10 3.777 ( 0.068 ms): perf/2944525 bpf(cmd: PROG_LOAD, uattr: 0x7ffe80de7570, size: 148) = 12 3.848 ( 0.003 ms): perf/2944525 bpf(cmd: LINK_CREATE, uattr: 0x7ffe80de7550, size: 64) = -1 EBADF (Bad file descriptor) 3.859 ( 0.006 ms): perf/2944525 bpf(cmd: LINK_CREATE, uattr: 0x7ffe80de77c0, size: 64) = 12 6.504 ( 0.010 ms): perf/2944525 bpf(cmd: LINK_CREATE, uattr: 0x7ffe80de77c0, size: 64) = 14 ^C# DURATION | COUNT | GRAPH | 0 - 1 us | 0 | | 1 - 2 us | 0 | | 2 - 4 us | 1 | | 4 - 8 us | 3 | | 8 - 16 us | 3 | | 16 - 32 us | 11 | | 32 - 64 us | 9 | | 64 - 128 us | 17 | | 128 - 256 us | 30 | # | 256 - 512 us | 20 | | 512 - 1024 us | 42 | # | 1 - 2 ms | 151 | ###### | 2 - 4 ms | 106 | #### | 4 - 8 ms | 18 | | 8 - 16 ms | 149 | ###### | 16 - 32 ms | 30 | # | 32 - 64 ms | 17 | | 64 - 128 ms | 360 | ############### | 128 - 256 ms | 52 | ## | 256 - 512 ms | 18 | | 512 - 1024 ms | 28 | # | 1 - ... s | 5 | | root@x1:~# Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Link: https://lore.kernel.org/r/20240902200515.2103769-3-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf_ftrace.c | 8 ++++---- tools/perf/util/bpf_skel/func_latency.bpf.c | 7 ++++--- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/tools/perf/util/bpf_ftrace.c b/tools/perf/util/bpf_ftrace.c index 7a4297d8fd2c..06d1c4018407 100644 --- a/tools/perf/util/bpf_ftrace.c +++ b/tools/perf/util/bpf_ftrace.c @@ -40,13 +40,17 @@ int perf_ftrace__latency_prepare_bpf(struct perf_ftrace *ftrace) if (ftrace->target.cpu_list) { ncpus = perf_cpu_map__nr(ftrace->evlist->core.user_requested_cpus); bpf_map__set_max_entries(skel->maps.cpu_filter, ncpus); + skel->rodata->has_cpu = 1; } if (target__has_task(&ftrace->target) || target__none(&ftrace->target)) { ntasks = perf_thread_map__nr(ftrace->evlist->core.threads); bpf_map__set_max_entries(skel->maps.task_filter, ntasks); + skel->rodata->has_task = 1; } + skel->rodata->use_nsec = ftrace->use_nsec; + set_max_rlimit(); err = func_latency_bpf__load(skel); @@ -59,7 +63,6 @@ int perf_ftrace__latency_prepare_bpf(struct perf_ftrace *ftrace) u32 cpu; u8 val = 1; - skel->bss->has_cpu = 1; fd = bpf_map__fd(skel->maps.cpu_filter); for (i = 0; i < ncpus; i++) { @@ -72,7 +75,6 @@ int perf_ftrace__latency_prepare_bpf(struct perf_ftrace *ftrace) u32 pid; u8 val = 1; - skel->bss->has_task = 1; fd = bpf_map__fd(skel->maps.task_filter); for (i = 0; i < ntasks; i++) { @@ -81,8 +83,6 @@ int perf_ftrace__latency_prepare_bpf(struct perf_ftrace *ftrace) } } - skel->bss->use_nsec = ftrace->use_nsec; - skel->links.func_begin = bpf_program__attach_kprobe(skel->progs.func_begin, false, func->name); if (IS_ERR(skel->links.func_begin)) { diff --git a/tools/perf/util/bpf_skel/func_latency.bpf.c b/tools/perf/util/bpf_skel/func_latency.bpf.c index 9d01e3af7479..f613dc9cb123 100644 --- a/tools/perf/util/bpf_skel/func_latency.bpf.c +++ b/tools/perf/util/bpf_skel/func_latency.bpf.c @@ -37,9 +37,10 @@ struct { int enabled = 0; -int has_cpu = 0; -int has_task = 0; -int use_nsec = 0; + +const volatile int has_cpu = 0; +const volatile int has_task = 0; +const volatile int use_nsec = 0; SEC("kprobe/func") int BPF_PROG(func_begin) From 066fd840873f2187deb4a646c5f531a8dba2fd36 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 2 Sep 2024 13:05:13 -0700 Subject: [PATCH 278/347] perf kwork: Constify control data for BPF The control knobs set before loading BPF programs should be declared as 'const volatile' so that it can be optimized by the BPF core. Committer testing: root@x1:~# perf kwork report --use-bpf Starting trace, Hit to stop and report ^C Kwork Name | Cpu | Total Runtime | Count | Max runtime | Max runtime start | Max runtime end | -------------------------------------------------------------------------------------------------------------------------------- (w)intel_atomic_commit_work [ | 0009 | 18.680 ms | 2 | 18.553 ms | 362410.681580 s | 362410.700133 s | (w)pm_runtime_work | 0007 | 13.300 ms | 1 | 13.300 ms | 362410.254996 s | 362410.268295 s | (w)intel_atomic_commit_work [ | 0009 | 9.846 ms | 2 | 9.717 ms | 362410.172352 s | 362410.182069 s | (w)acpi_ec_event_processor | 0002 | 8.106 ms | 1 | 8.106 ms | 362410.463187 s | 362410.471293 s | (s)SCHED:7 | 0000 | 1.351 ms | 106 | 0.063 ms | 362410.658017 s | 362410.658080 s | i915:157 | 0008 | 0.994 ms | 13 | 0.361 ms | 362411.222125 s | 362411.222486 s | (s)SCHED:7 | 0001 | 0.703 ms | 98 | 0.047 ms | 362410.245004 s | 362410.245051 s | (s)SCHED:7 | 0005 | 0.674 ms | 42 | 0.074 ms | 362411.483039 s | 362411.483113 s | (s)NET_RX:3 | 0001 | 0.556 ms | 10 | 0.079 ms | 362411.066388 s | 362411.066467 s | root@x1:~# perf trace -e bpf --max-events 5 perf kwork report --use-bpf 0.000 ( 0.016 ms): perf/2948007 bpf(cmd: 36, uattr: 0x7ffededa6660, size: 8) = -1 EOPNOTSUPP (Operation not supported) 0.026 ( 0.106 ms): perf/2948007 bpf(cmd: PROG_LOAD, uattr: 0x7ffededa6390, size: 148) = 12 0.152 ( 0.032 ms): perf/2948007 bpf(cmd: PROG_LOAD, uattr: 0x7ffededa6450, size: 148) = 12 26.247 ( 0.138 ms): perf/2948007 bpf(cmd: PROG_LOAD, uattr: 0x7ffededa6300, size: 148) = 12 26.396 ( 0.012 ms): perf/2948007 bpf(uattr: 0x7ffededa64b0, size: 80) = 12 Starting trace, Hit to stop and report root@x1:~# Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Cc: Yang Jihong Link: https://lore.kernel.org/r/20240902200515.2103769-4-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf_kwork.c | 9 +++++---- tools/perf/util/bpf_kwork_top.c | 7 ++++--- tools/perf/util/bpf_skel/kwork_top.bpf.c | 2 +- tools/perf/util/bpf_skel/kwork_trace.bpf.c | 5 +++-- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/tools/perf/util/bpf_kwork.c b/tools/perf/util/bpf_kwork.c index 44f0f708a15d..6c7126b7670d 100644 --- a/tools/perf/util/bpf_kwork.c +++ b/tools/perf/util/bpf_kwork.c @@ -176,8 +176,6 @@ static int setup_filters(struct perf_kwork *kwork) bpf_map_update_elem(fd, &cpu.cpu, &val, BPF_ANY); } perf_cpu_map__put(map); - - skel->bss->has_cpu_filter = 1; } if (kwork->profile_name != NULL) { @@ -197,8 +195,6 @@ static int setup_filters(struct perf_kwork *kwork) key = 0; bpf_map_update_elem(fd, &key, kwork->profile_name, BPF_ANY); - - skel->bss->has_name_filter = 1; } return 0; @@ -239,6 +235,11 @@ int perf_kwork__trace_prepare_bpf(struct perf_kwork *kwork) class_bpf->load_prepare(kwork); } + if (kwork->cpu_list != NULL) + skel->rodata->has_cpu_filter = 1; + if (kwork->profile_name != NULL) + skel->rodata->has_name_filter = 1; + if (kwork_trace_bpf__load(skel)) { pr_debug("Failed to load kwork trace skeleton\n"); goto out; diff --git a/tools/perf/util/bpf_kwork_top.c b/tools/perf/util/bpf_kwork_top.c index 22a3b00a1e23..7261cad43468 100644 --- a/tools/perf/util/bpf_kwork_top.c +++ b/tools/perf/util/bpf_kwork_top.c @@ -151,14 +151,12 @@ static int setup_filters(struct perf_kwork *kwork) bpf_map_update_elem(fd, &cpu.cpu, &val, BPF_ANY); } perf_cpu_map__put(map); - - skel->bss->has_cpu_filter = 1; } return 0; } -int perf_kwork__top_prepare_bpf(struct perf_kwork *kwork __maybe_unused) +int perf_kwork__top_prepare_bpf(struct perf_kwork *kwork) { struct bpf_program *prog; struct kwork_class *class; @@ -193,6 +191,9 @@ int perf_kwork__top_prepare_bpf(struct perf_kwork *kwork __maybe_unused) class_bpf->load_prepare(); } + if (kwork->cpu_list) + skel->rodata->has_cpu_filter = 1; + if (kwork_top_bpf__load(skel)) { pr_debug("Failed to load kwork top skeleton\n"); goto out; diff --git a/tools/perf/util/bpf_skel/kwork_top.bpf.c b/tools/perf/util/bpf_skel/kwork_top.bpf.c index 84c15ccbab44..594da91965a2 100644 --- a/tools/perf/util/bpf_skel/kwork_top.bpf.c +++ b/tools/perf/util/bpf_skel/kwork_top.bpf.c @@ -84,7 +84,7 @@ struct { int enabled = 0; -int has_cpu_filter = 0; +const volatile int has_cpu_filter = 0; __u64 from_timestamp = 0; __u64 to_timestamp = 0; diff --git a/tools/perf/util/bpf_skel/kwork_trace.bpf.c b/tools/perf/util/bpf_skel/kwork_trace.bpf.c index 063c124e0999..cbd79bc4b330 100644 --- a/tools/perf/util/bpf_skel/kwork_trace.bpf.c +++ b/tools/perf/util/bpf_skel/kwork_trace.bpf.c @@ -68,8 +68,9 @@ struct { } perf_kwork_name_filter SEC(".maps"); int enabled = 0; -int has_cpu_filter = 0; -int has_name_filter = 0; + +const volatile int has_cpu_filter = 0; +const volatile int has_name_filter = 0; static __always_inline int local_strncmp(const char *s1, unsigned int sz, const char *s2) From 4afdc00c378f34943fb3a3cc08db4babdacb5c5b Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 2 Sep 2024 13:05:14 -0700 Subject: [PATCH 279/347] perf lock contention: Constify control data for BPF The control knobs set before loading BPF programs should be declared as 'const volatile' so that it can be optimized by the BPF core. Committer testing: root@x1:~# perf lock contention --use-bpf contended total wait max wait avg wait type caller 5 31.57 us 14.93 us 6.31 us mutex btrfs_delayed_update_inode+0x43 1 16.91 us 16.91 us 16.91 us rwsem:R btrfs_tree_read_lock_nested+0x1b 1 15.13 us 15.13 us 15.13 us spinlock btrfs_getattr+0xd1 1 6.65 us 6.65 us 6.65 us rwsem:R btrfs_tree_read_lock_nested+0x1b 1 4.34 us 4.34 us 4.34 us spinlock process_one_work+0x1a9 root@x1:~# root@x1:~# perf trace -e bpf --max-events 10 perf lock contention --use-bpf 0.000 ( 0.013 ms): :2948281/2948281 bpf(cmd: 36, uattr: 0x7ffd5f12d730, size: 8) = -1 EOPNOTSUPP (Operation not supported) 0.024 ( 0.120 ms): :2948281/2948281 bpf(cmd: PROG_LOAD, uattr: 0x7ffd5f12d460, size: 148) = 16 0.158 ( 0.034 ms): :2948281/2948281 bpf(cmd: PROG_LOAD, uattr: 0x7ffd5f12d520, size: 148) = 16 26.653 ( 0.154 ms): perf/2948281 bpf(cmd: PROG_LOAD, uattr: 0x7ffd5f12d3d0, size: 148) = 16 26.825 ( 0.014 ms): perf/2948281 bpf(uattr: 0x7ffd5f12d580, size: 80) = 16 87.924 ( 0.038 ms): perf/2948281 bpf(cmd: BTF_LOAD, uattr: 0x7ffd5f12d400, size: 40) = 16 87.988 ( 0.006 ms): perf/2948281 bpf(cmd: BTF_LOAD, uattr: 0x7ffd5f12d470, size: 40) = 16 88.019 ( 0.006 ms): perf/2948281 bpf(cmd: BTF_LOAD, uattr: 0x7ffd5f12d250, size: 40) = 16 88.029 ( 0.172 ms): perf/2948281 bpf(cmd: PROG_LOAD, uattr: 0x7ffd5f12d320, size: 148) = 17 88.217 ( 0.005 ms): perf/2948281 bpf(cmd: BTF_LOAD, uattr: 0x7ffd5f12d4d0, size: 40) = 16 root@x1:~# Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Link: https://lore.kernel.org/r/20240902200515.2103769-5-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf_lock_contention.c | 45 ++++++++++--------- .../perf/util/bpf_skel/lock_contention.bpf.c | 27 +++++------ 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c index bc4e92c0c08b..41a1ad087895 100644 --- a/tools/perf/util/bpf_lock_contention.c +++ b/tools/perf/util/bpf_lock_contention.c @@ -46,14 +46,22 @@ int lock_contention_prepare(struct lock_contention *con) else bpf_map__set_max_entries(skel->maps.stacks, 1); - if (target__has_cpu(target)) + if (target__has_cpu(target)) { + skel->rodata->has_cpu = 1; ncpus = perf_cpu_map__nr(evlist->core.user_requested_cpus); - if (target__has_task(target)) + } + if (target__has_task(target)) { + skel->rodata->has_task = 1; ntasks = perf_thread_map__nr(evlist->core.threads); - if (con->filters->nr_types) + } + if (con->filters->nr_types) { + skel->rodata->has_type = 1; ntypes = con->filters->nr_types; - if (con->filters->nr_cgrps) + } + if (con->filters->nr_cgrps) { + skel->rodata->has_cgroup = 1; ncgrps = con->filters->nr_cgrps; + } /* resolve lock name filters to addr */ if (con->filters->nr_syms) { @@ -82,6 +90,7 @@ int lock_contention_prepare(struct lock_contention *con) con->filters->addrs = addrs; } naddrs = con->filters->nr_addrs; + skel->rodata->has_addr = 1; } bpf_map__set_max_entries(skel->maps.cpu_filter, ncpus); @@ -90,6 +99,16 @@ int lock_contention_prepare(struct lock_contention *con) bpf_map__set_max_entries(skel->maps.addr_filter, naddrs); bpf_map__set_max_entries(skel->maps.cgroup_filter, ncgrps); + skel->rodata->stack_skip = con->stack_skip; + skel->rodata->aggr_mode = con->aggr_mode; + skel->rodata->needs_callstack = con->save_callstack; + skel->rodata->lock_owner = con->owner; + + if (con->aggr_mode == LOCK_AGGR_CGROUP || con->filters->nr_cgrps) { + if (cgroup_is_v2("perf_event")) + skel->rodata->use_cgroup_v2 = 1; + } + if (lock_contention_bpf__load(skel) < 0) { pr_err("Failed to load lock-contention BPF skeleton\n"); return -1; @@ -99,7 +118,6 @@ int lock_contention_prepare(struct lock_contention *con) u32 cpu; u8 val = 1; - skel->bss->has_cpu = 1; fd = bpf_map__fd(skel->maps.cpu_filter); for (i = 0; i < ncpus; i++) { @@ -112,7 +130,6 @@ int lock_contention_prepare(struct lock_contention *con) u32 pid; u8 val = 1; - skel->bss->has_task = 1; fd = bpf_map__fd(skel->maps.task_filter); for (i = 0; i < ntasks; i++) { @@ -125,7 +142,6 @@ int lock_contention_prepare(struct lock_contention *con) u32 pid = evlist->workload.pid; u8 val = 1; - skel->bss->has_task = 1; fd = bpf_map__fd(skel->maps.task_filter); bpf_map_update_elem(fd, &pid, &val, BPF_ANY); } @@ -133,7 +149,6 @@ int lock_contention_prepare(struct lock_contention *con) if (con->filters->nr_types) { u8 val = 1; - skel->bss->has_type = 1; fd = bpf_map__fd(skel->maps.type_filter); for (i = 0; i < con->filters->nr_types; i++) @@ -143,7 +158,6 @@ int lock_contention_prepare(struct lock_contention *con) if (con->filters->nr_addrs) { u8 val = 1; - skel->bss->has_addr = 1; fd = bpf_map__fd(skel->maps.addr_filter); for (i = 0; i < con->filters->nr_addrs; i++) @@ -153,25 +167,14 @@ int lock_contention_prepare(struct lock_contention *con) if (con->filters->nr_cgrps) { u8 val = 1; - skel->bss->has_cgroup = 1; fd = bpf_map__fd(skel->maps.cgroup_filter); for (i = 0; i < con->filters->nr_cgrps; i++) bpf_map_update_elem(fd, &con->filters->cgrps[i], &val, BPF_ANY); } - /* these don't work well if in the rodata section */ - skel->bss->stack_skip = con->stack_skip; - skel->bss->aggr_mode = con->aggr_mode; - skel->bss->needs_callstack = con->save_callstack; - skel->bss->lock_owner = con->owner; - - if (con->aggr_mode == LOCK_AGGR_CGROUP) { - if (cgroup_is_v2("perf_event")) - skel->bss->use_cgroup_v2 = 1; - + if (con->aggr_mode == LOCK_AGGR_CGROUP) read_all_cgroups(&con->cgroups); - } bpf_program__set_autoload(skel->progs.collect_lock_syms, false); diff --git a/tools/perf/util/bpf_skel/lock_contention.bpf.c b/tools/perf/util/bpf_skel/lock_contention.bpf.c index 52a876b42699..1069bda5d733 100644 --- a/tools/perf/util/bpf_skel/lock_contention.bpf.c +++ b/tools/perf/util/bpf_skel/lock_contention.bpf.c @@ -117,21 +117,22 @@ struct mm_struct___new { } __attribute__((preserve_access_index)); /* control flags */ -int enabled; -int has_cpu; -int has_task; -int has_type; -int has_addr; -int has_cgroup; -int needs_callstack; -int stack_skip; -int lock_owner; - -int use_cgroup_v2; -int perf_subsys_id = -1; +const volatile int has_cpu; +const volatile int has_task; +const volatile int has_type; +const volatile int has_addr; +const volatile int has_cgroup; +const volatile int needs_callstack; +const volatile int stack_skip; +const volatile int lock_owner; +const volatile int use_cgroup_v2; /* determine the key of lock stat */ -int aggr_mode; +const volatile int aggr_mode; + +int enabled; + +int perf_subsys_id = -1; __u64 end_ts; From 8b3b1bb3ea1f930d44d79dfb0b8cb7d62db08ed6 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 2 Sep 2024 13:05:15 -0700 Subject: [PATCH 280/347] perf record offcpu: Constify control data for BPF The control knobs set before loading BPF programs should be declared as 'const volatile' so that it can be optimized by the BPF core. Committer testing: root@x1:~# perf record --off-cpu ^C[ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 1.807 MB perf.data (5645 samples) ] root@x1:~# perf evlist cpu_atom/cycles/P cpu_core/cycles/P offcpu-time dummy:u root@x1:~# perf evlist -v cpu_atom/cycles/P: type: 0 (PERF_TYPE_HARDWARE), size: 136, config: 0xa00000000, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|CPU|PERIOD|IDENTIFIER, read_format: ID|LOST, disabled: 1, inherit: 1, freq: 1, precise_ip: 3, sample_id_all: 1 cpu_core/cycles/P: type: 0 (PERF_TYPE_HARDWARE), size: 136, config: 0x400000000, { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|CPU|PERIOD|IDENTIFIER, read_format: ID|LOST, disabled: 1, inherit: 1, freq: 1, precise_ip: 3, sample_id_all: 1 offcpu-time: type: 1 (software), size: 136, config: 0xa (PERF_COUNT_SW_BPF_OUTPUT), { sample_period, sample_freq }: 1, sample_type: IP|TID|TIME|CALLCHAIN|CPU|PERIOD|IDENTIFIER, read_format: ID|LOST, disabled: 1, inherit: 1, freq: 1, sample_id_all: 1 dummy:u: type: 1 (software), size: 136, config: 0x9 (PERF_COUNT_SW_DUMMY), { sample_period, sample_freq }: 1, sample_type: IP|TID|TIME|CPU|IDENTIFIER, read_format: ID|LOST, inherit: 1, exclude_kernel: 1, exclude_hv: 1, mmap: 1, comm: 1, task: 1, sample_id_all: 1, exclude_guest: 1, mmap2: 1, comm_exec: 1, ksymbol: 1, bpf_event: 1 root@x1:~# perf trace -e bpf --max-events 5 perf record --off-cpu 0.000 ( 0.015 ms): :2949124/2949124 bpf(cmd: 36, uattr: 0x7ffefc6dbe30, size: 8) = -1 EOPNOTSUPP (Operation not supported) 0.031 ( 0.115 ms): :2949124/2949124 bpf(cmd: PROG_LOAD, uattr: 0x7ffefc6dbb60, size: 148) = 14 0.159 ( 0.037 ms): :2949124/2949124 bpf(cmd: PROG_LOAD, uattr: 0x7ffefc6dbc20, size: 148) = 14 23.868 ( 0.144 ms): perf/2949124 bpf(cmd: PROG_LOAD, uattr: 0x7ffefc6dbad0, size: 148) = 14 24.027 ( 0.014 ms): perf/2949124 bpf(uattr: 0x7ffefc6dbc80, size: 80) = 14 root@x1:~# Signed-off-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Cc: Song Liu Link: https://lore.kernel.org/r/20240902200515.2103769-6-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf_off_cpu.c | 16 ++++++++-------- tools/perf/util/bpf_skel/off_cpu.bpf.c | 9 +++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/tools/perf/util/bpf_off_cpu.c b/tools/perf/util/bpf_off_cpu.c index 6af36142dc5a..a590a8ac1f9d 100644 --- a/tools/perf/util/bpf_off_cpu.c +++ b/tools/perf/util/bpf_off_cpu.c @@ -73,14 +73,12 @@ static void off_cpu_start(void *arg) struct evlist *evlist = arg; /* update task filter for the given workload */ - if (!skel->bss->has_cpu && !skel->bss->has_task && + if (skel->rodata->has_task && skel->rodata->uses_tgid && perf_thread_map__pid(evlist->core.threads, 0) != -1) { int fd; u32 pid; u8 val = 1; - skel->bss->has_task = 1; - skel->bss->uses_tgid = 1; fd = bpf_map__fd(skel->maps.task_filter); pid = perf_thread_map__pid(evlist->core.threads, 0); bpf_map_update_elem(fd, &pid, &val, BPF_ANY); @@ -148,6 +146,7 @@ int off_cpu_prepare(struct evlist *evlist, struct target *target, if (target->cpu_list) { ncpus = perf_cpu_map__nr(evlist->core.user_requested_cpus); bpf_map__set_max_entries(skel->maps.cpu_filter, ncpus); + skel->rodata->has_cpu = 1; } if (target->pid) { @@ -173,11 +172,16 @@ int off_cpu_prepare(struct evlist *evlist, struct target *target, ntasks = MAX_PROC; bpf_map__set_max_entries(skel->maps.task_filter, ntasks); + skel->rodata->has_task = 1; + skel->rodata->uses_tgid = 1; } else if (target__has_task(target)) { ntasks = perf_thread_map__nr(evlist->core.threads); bpf_map__set_max_entries(skel->maps.task_filter, ntasks); + skel->rodata->has_task = 1; } else if (target__none(target)) { bpf_map__set_max_entries(skel->maps.task_filter, MAX_PROC); + skel->rodata->has_task = 1; + skel->rodata->uses_tgid = 1; } if (evlist__first(evlist)->cgrp) { @@ -186,6 +190,7 @@ int off_cpu_prepare(struct evlist *evlist, struct target *target, if (!cgroup_is_v2("perf_event")) skel->rodata->uses_cgroup_v1 = true; + skel->rodata->has_cgroup = 1; } if (opts->record_cgroup) { @@ -208,7 +213,6 @@ int off_cpu_prepare(struct evlist *evlist, struct target *target, u32 cpu; u8 val = 1; - skel->bss->has_cpu = 1; fd = bpf_map__fd(skel->maps.cpu_filter); for (i = 0; i < ncpus; i++) { @@ -220,8 +224,6 @@ int off_cpu_prepare(struct evlist *evlist, struct target *target, if (target->pid) { u8 val = 1; - skel->bss->has_task = 1; - skel->bss->uses_tgid = 1; fd = bpf_map__fd(skel->maps.task_filter); strlist__for_each_entry(pos, pid_slist) { @@ -240,7 +242,6 @@ int off_cpu_prepare(struct evlist *evlist, struct target *target, u32 pid; u8 val = 1; - skel->bss->has_task = 1; fd = bpf_map__fd(skel->maps.task_filter); for (i = 0; i < ntasks; i++) { @@ -253,7 +254,6 @@ int off_cpu_prepare(struct evlist *evlist, struct target *target, struct evsel *evsel; u8 val = 1; - skel->bss->has_cgroup = 1; fd = bpf_map__fd(skel->maps.cgroup_filter); evlist__for_each_entry(evlist, evsel) { diff --git a/tools/perf/util/bpf_skel/off_cpu.bpf.c b/tools/perf/util/bpf_skel/off_cpu.bpf.c index d877a0a9731f..c152116df72f 100644 --- a/tools/perf/util/bpf_skel/off_cpu.bpf.c +++ b/tools/perf/util/bpf_skel/off_cpu.bpf.c @@ -85,10 +85,11 @@ struct task_struct___old { } __attribute__((preserve_access_index)); int enabled = 0; -int has_cpu = 0; -int has_task = 0; -int has_cgroup = 0; -int uses_tgid = 0; + +const volatile int has_cpu = 0; +const volatile int has_task = 0; +const volatile int has_cgroup = 0; +const volatile int uses_tgid = 0; const volatile bool has_prev_state = false; const volatile bool needs_cgroup = false; From bf0db8c759ba137cebfeda9eecc1f728cb14dab7 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 29 Feb 2024 08:18:28 -0800 Subject: [PATCH 281/347] perf script: Minimize "not reaching sample" for '-F +brstackinsn' In some situations 'perf script -F +brstackinsn' sees a lot of "not reaching sample" messages. This happens when the last LBR block before the sample contains a branch that is not in the LBR, and the instruction dumping stops. $ perf record -b emacs -Q --batch '()' [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.396 MB perf.data (443 samples) ] $ perf script -F +brstackinsn ... 00007f0ab2d171a4 insn: 41 0f 94 c0 00007f0ab2d171a8 insn: 83 fa 01 00007f0ab2d171ab insn: 74 d3 # PRED 6 cycles [313] 1.00 IPC 00007f0ab2d17180 insn: 45 84 c0 00007f0ab2d17183 insn: 74 28 ... not reaching sample ... $ perf script -F +brstackinsn | grep -c reach 136 $ This is a problem for further analysis that wants to see the full code upto the sample. There are two common cases where the message is bogus: - The LBR only logs taken branches, but the branch might be a conditional branch that is not taken (that is the most common case actually) - The LBR sampling uses a filter ignoring some branches, but the perf script check checks for all branches. This patch fixes these two conditions, by only checking for conditional branches, as well as checking the perf_event_attr's branch filter attributes. For the test case above it fixes all the messages: $ ./perf script -F +brstackinsn | grep -c reach 0 Note that there are still conditions when the message is hit -- sometimes there can be a unconditional branch that misses the LBR update before the sample -- but they are much more rare now. Signed-off-by: Andi Kleen Reviewed-by: Adrian Hunter Link: https://lore.kernel.org/r/20240229161828.386397-1-ak@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 6 ++++-- tools/perf/util/dump-insn.c | 2 +- tools/perf/util/dump-insn.h | 2 +- tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c | 5 +++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index 206b08426555..dbe792b52c5c 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -1428,7 +1428,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; @@ -1463,7 +1463,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. */ diff --git a/tools/perf/util/dump-insn.c b/tools/perf/util/dump-insn.c index 2bd8585db93c..c1cc0ade48d0 100644 --- a/tools/perf/util/dump-insn.c +++ b/tools/perf/util/dump-insn.c @@ -15,7 +15,7 @@ const char *dump_insn(struct perf_insn *x __maybe_unused, } __weak -int arch_is_branch(const unsigned char *buf __maybe_unused, +int arch_is_uncond_branch(const unsigned char *buf __maybe_unused, size_t len __maybe_unused, int x86_64 __maybe_unused) { diff --git a/tools/perf/util/dump-insn.h b/tools/perf/util/dump-insn.h index 4a7797dd6d09..20d4d7bb5275 100644 --- a/tools/perf/util/dump-insn.h +++ b/tools/perf/util/dump-insn.h @@ -21,6 +21,6 @@ struct perf_insn { const char *dump_insn(struct perf_insn *x, u64 ip, u8 *inbuf, int inlen, int *lenp); -int arch_is_branch(const unsigned char *buf, size_t len, int x86_64); +int arch_is_uncond_branch(const unsigned char *buf, size_t len, int x86_64); #endif diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c index 4407130d91f8..47cf35799a4d 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-insn-decoder.c @@ -209,12 +209,13 @@ int intel_pt_get_insn(const unsigned char *buf, size_t len, int x86_64, return 0; } -int arch_is_branch(const unsigned char *buf, size_t len, int x86_64) +int arch_is_uncond_branch(const unsigned char *buf, size_t len, int x86_64) { struct intel_pt_insn in; if (intel_pt_get_insn(buf, len, x86_64, &in) < 0) return -1; - return in.branch != INTEL_PT_BR_NO_BRANCH; + return in.branch == INTEL_PT_BR_UNCONDITIONAL || + in.branch == INTEL_PT_BR_INDIRECT; } const char *dump_insn(struct perf_insn *x, uint64_t ip __maybe_unused, From 575eec218059f90bf07a4e26b531fd6d54fe1769 Mon Sep 17 00:00:00 2001 From: Yang Jihong Date: Mon, 12 Aug 2024 21:26:05 +0800 Subject: [PATCH 282/347] perf sched timehist: Skip print non-idle task samples when only show idle events when only show idle events, runtime stats of non-idle tasks is not updated, and the value is 0, there is no need to print non-idle samples. Before: # perf sched timehist -I Samples of sched_switch event do not have callchains. time cpu task name wait time sch delay run time [tid/pid] (msec) (msec) (msec) --------------- ------ ------------------------------ --------- --------- --------- 2090450.763235 [0000] migration/0[15] 0.000 0.000 0.000 2090450.763268 [0001] migration/1[21] 0.000 0.000 0.000 2090450.763309 [0002] migration/2[27] 0.000 0.000 0.000 2090450.763343 [0003] migration/3[33] 0.000 0.000 0.000 2090450.763469 [0004] migration/4[39] 0.000 0.000 0.000 2090450.763501 [0005] migration/5[45] 0.000 0.000 0.000 2090450.763622 [0006] migration/6[51] 0.000 0.000 0.000 2090450.763660 [0007] migration/7[57] 0.000 0.000 0.000 2090450.763741 [0009] migration/9[69] 0.000 0.000 0.000 2090450.763862 [0010] migration/10[75] 0.000 0.000 0.000 2090450.763894 [0011] migration/11[81] 0.000 0.000 0.000 2090450.764021 [0012] migration/12[87] 0.000 0.000 0.000 2090450.764056 [0013] migration/13[93] 0.000 0.000 0.000 2090450.764135 [0014] migration/14[99] 0.000 0.000 0.000 2090450.764163 [0015] migration/15[105] 0.000 0.000 0.000 2090450.764292 [0016] migration/16[111] 0.000 0.000 0.000 2090450.764371 [0017] migration/17[117] 0.000 0.000 0.000 2090450.764422 [0018] migration/18[123] 0.000 0.000 0.000 2090450.764490 [0000] 0.000 0.000 1.255 2090450.764505 [0000] s1-perf[8235/7168] 0.000 0.000 0.000 2090450.764571 [0016] 0.000 0.000 0.278 2090450.764588 [0010] 0.000 0.000 0.725 2090450.764590 [0016] s1-agent[7179/7162] 0.000 0.000 0.000 2090450.764635 [0000] 0.015 0.015 0.129 2090450.764637 [0017] 0.000 0.000 0.266 2090450.764639 [0000] s1-perf[8235/7168] 0.000 0.000 0.000 2090450.764668 [0017] s1-agent[7180/7162] 0.000 0.000 0.000 2090450.764669 [0000] 0.003 0.003 0.029 2090450.764672 [0000] s1-perf[8235/7168] 0.000 0.000 0.000 2090450.764683 [0000] 0.003 0.003 0.010 After: # perf sched timehist -I Samples of sched_switch event do not have callchains. time cpu task name wait time sch delay run time [tid/pid] (msec) (msec) (msec) --------------- ------ ------------------------------ --------- --------- --------- 2090450.764490 [0000] 0.000 0.000 1.255 2090450.764571 [0016] 0.000 0.000 0.278 2090450.764588 [0010] 0.000 0.000 0.725 2090450.764635 [0000] 0.015 0.015 0.129 2090450.764637 [0017] 0.000 0.000 0.266 2090450.764669 [0000] 0.003 0.003 0.029 2090450.764683 [0000] 0.003 0.003 0.010 2090450.764688 [0016] 0.019 0.019 0.097 2090450.764694 [0000] 0.001 0.001 0.009 2090450.764706 [0000] 0.001 0.001 0.010 2090450.764725 [0002] 0.000 0.000 1.415 2090450.764728 [0000] 0.002 0.002 0.019 2090450.764823 [0000] 0.003 0.003 0.091 2090450.764838 [0019] 0.000 0.000 0.154 2090450.764865 [0002] 0.109 0.109 0.029 2090450.764866 [0000] 0.012 0.012 0.030 2090450.764880 [0002] 0.013 0.013 0.001 2090450.764880 [0000] 0.002 0.002 0.011 2090450.764896 [0000] 0.001 0.001 0.013 2090450.764903 [0019] 0.063 0.063 0.002 2090450.764908 [0019] 0.003 0.003 0.001 Fixes: 07235f84ece6b66f ("perf sched timehist: Add -I/--idle-hist option") Signed-off-by: Yang Jihong Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240812132606.3126490-1-yangjihong@bytedance.com Reviewed-and-tested-by: Madadi Vineeth Reddy Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 1c386ebe4b98..72f0e7f97f56 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2730,10 +2730,10 @@ static int timehist_sched_change_event(const 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) From b93fb9cf45a99042f4472678fc67afd1de47c32e Mon Sep 17 00:00:00 2001 From: Yang Jihong Date: Mon, 12 Aug 2024 21:26:06 +0800 Subject: [PATCH 283/347] perf sched timehist: Remove redundant BUG_ON in timehist_sched_change_event() The BUG_ON(thread__tid(thread) != 0) in timehist_sched_change_event() is redundant, remove it. No functional change. Fixes: 07235f84ece6b66f ("perf sched timehist: Add -I/--idle-hist option") Reviewed-by: Madadi Vineeth Reddy Signed-off-by: Yang Jihong Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240812132606.3126490-2-yangjihong@bytedance.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-sched.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 72f0e7f97f56..dcae3f1f5f3c 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -2703,8 +2703,6 @@ static int timehist_sched_change_event(const 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; From 3fcd740990dee9404f4f6ebeb7a31f0066849e46 Mon Sep 17 00:00:00 2001 From: Yang Jihong Date: Mon, 19 Aug 2024 11:30:15 +0800 Subject: [PATCH 284/347] perf sched timehist: Add --show-prio option The --show-prio option is used to display the priority of task. It is disabled by default, which is consistent with original behavior. The display format is xxx (priority does not change during task running) or xxx->yyy (priority changes during task running) Testcase: # perf sched record nice -n 9 true [ perf record: Woken up 0 times to write data ] [ perf record: Captured and wrote 0.497 MB perf.data ] # perf sched timehist -h Usage: perf sched timehist [] -C, --cpu list of cpus to profile -D, --dump-raw-trace dump raw trace in ASCII -f, --force don't complain, do it -g, --call-graph Display call chains if present (default on) -I, --idle-hist Show idle events only -i, --input input file name -k, --vmlinux vmlinux pathname -M, --migrations Show migration events -n, --next Show next task -p, --pid analyze events only for given process id(s) -s, --summary Show only syscall summary with statistics -S, --with-summary Show all syscalls and summary with statistics -t, --tid analyze events only for given thread id(s) -V, --cpu-visual Add CPU visual -v, --verbose be more verbose (show symbol address, etc) -w, --wakeups Show wakeup events --kallsyms kallsyms pathname --max-stack Maximum number of functions to display backtrace. --show-prio Show task priority --state Show task state when sched-out --symfs Look for files with symbols relative to this directory --time Time span for analysis (start,stop) # perf sched timehist Samples of sched_switch event do not have callchains. time cpu task name wait time sch delay run time [tid/pid] (msec) (msec) (msec) --------------- ------ ------------------------------ --------- --------- --------- 23952.006537 [0000] perf[534] 0.000 0.000 0.000 23952.006593 [0000] migration/0[19] 0.000 0.014 0.056 23952.006899 [0001] perf[534] 0.000 0.000 0.000 23952.006947 [0001] migration/1[22] 0.000 0.015 0.047 23952.007138 [0002] perf[534] 0.000 0.000 0.000 # perf sched timehist --show-prio Samples of sched_switch event do not have callchains. time cpu task name prio wait time sch delay run time [tid/pid] (msec) (msec) (msec) --------------- ------ ------------------------------ -------- --------- --------- --------- 23952.006537 [0000] perf[534] 120 0.000 0.000 0.000 23952.006593 [0000] migration/0[19] 0 0.000 0.014 0.056 23952.006899 [0001] perf[534] 120 0.000 0.000 0.000 23952.034843 [0003] nice[535] 120->129 0.189 0.024 23.314 23952.053838 [0005] rcu_preempt[16] 120 3.993 0.000 0.023 23952.053990 [0005] 120 0.023 0.023 0.152 23952.054137 [0006] 120 1.427 1.427 17.855 23952.054278 [0007] 120 0.506 0.506 1.650 Signed-off-by: Yang Jihong Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240819033016.2427235-2-yangjihong@bytedance.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-sched.txt | 3 + tools/perf/builtin-sched.c | 91 +++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt index 84d49f9241b1..3efa5c58418d 100644 --- a/tools/perf/Documentation/perf-sched.txt +++ b/tools/perf/Documentation/perf-sched.txt @@ -212,6 +212,9 @@ OPTIONS for 'perf sched timehist' --state:: Show task state when it switched out. +--show-prio:: + Show task priority. + OPTIONS for 'perf sched replay' ------------------------------ diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index dcae3f1f5f3c..ba6563dc93d0 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -228,6 +228,7 @@ 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; @@ -258,6 +259,8 @@ struct thread_runtime { bool comm_changed; u64 migrations; + + int prio; }; /* per event run time data */ @@ -920,6 +923,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 +940,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; @@ -2036,6 +2045,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 +2080,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 +2102,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 +2124,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 +2179,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 +2349,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); @@ -2627,6 +2676,30 @@ static int timehist_migrate_task_event(const struct perf_tool *tool, return 0; } +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, @@ -2650,6 +2723,9 @@ static int timehist_sched_change_event(const struct perf_tool *tool, goto out; } + if (sched->show_prio) + timehist_update_task_prio(evsel, sample, machine); + thread = timehist_get_thread(sched, sample, machine, evsel); if (thread == NULL) { rc = -1; @@ -3684,6 +3760,7 @@ 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_PARENT(sched_options) }; From 9b3a48bbe20d9692e0d456488cd3a6244cac3e3b Mon Sep 17 00:00:00 2001 From: Yang Jihong Date: Mon, 19 Aug 2024 11:30:16 +0800 Subject: [PATCH 285/347] perf sched timehist: Add --prio option The --prio option is used to only show events for the given task priority(ies). The default is to show events for all priority tasks, which is consistent with the previous behavior. Testcase: # perf sched record nice -n 9 perf bench sched messaging -l 10000 # Running 'sched/messaging' benchmark: # 20 sender and receiver processes per group # 10 groups == 400 processes run Total time: 3.435 [sec] [ perf record: Woken up 270 times to write data ] [ perf record: Captured and wrote 618.688 MB perf.data (5729036 samples) ] # perf sched timehist -h Usage: perf sched timehist [] -C, --cpu list of cpus to profile -D, --dump-raw-trace dump raw trace in ASCII -f, --force don't complain, do it -g, --call-graph Display call chains if present (default on) -I, --idle-hist Show idle events only -i, --input input file name -k, --vmlinux vmlinux pathname -M, --migrations Show migration events -n, --next Show next task -p, --pid analyze events only for given process id(s) -s, --summary Show only syscall summary with statistics -S, --with-summary Show all syscalls and summary with statistics -t, --tid analyze events only for given thread id(s) -V, --cpu-visual Add CPU visual -v, --verbose be more verbose (show symbol address, etc) -w, --wakeups Show wakeup events --kallsyms kallsyms pathname --max-stack Maximum number of functions to display backtrace. --prio analyze events only for given task priority(ies) --show-prio Show task priority --state Show task state when sched-out --symfs Look for files with symbols relative to this directory --time Time span for analysis (start,stop) # perf sched timehist --prio 140 Samples of sched_switch event do not have callchains. Invalid prio string # perf sched timehist --show-prio --prio 129 Samples of sched_switch event do not have callchains. time cpu task name prio wait time sch delay run time [tid/pid] (msec) (msec) (msec) --------------- ------ ------------------------------ -------- --------- --------- --------- 2090450.765421 [0002] sched-messaging[1229618] 129 0.000 0.000 0.029 2090450.765445 [0007] sched-messaging[1229616] 129 0.000 0.062 0.043 2090450.765448 [0014] sched-messaging[1229619] 129 0.000 0.000 0.032 2090450.765478 [0013] sched-messaging[1229617] 129 0.000 0.065 0.048 2090450.765503 [0014] sched-messaging[1229622] 129 0.000 0.000 0.017 2090450.765550 [0002] sched-messaging[1229624] 129 0.000 0.000 0.021 2090450.765562 [0007] sched-messaging[1229621] 129 0.000 0.071 0.028 2090450.765570 [0005] sched-messaging[1229620] 129 0.000 0.064 0.066 2090450.765583 [0001] sched-messaging[1229625] 129 0.000 0.001 0.031 2090450.765595 [0013] sched-messaging[1229623] 129 0.000 0.060 0.028 2090450.765637 [0014] sched-messaging[1229628] 129 0.000 0.000 0.019 2090450.765665 [0007] sched-messaging[1229627] 129 0.000 0.038 0.030 # perf sched timehist --show-prio --prio 0,120-129 Samples of sched_switch event do not have callchains. time cpu task name prio wait time sch delay run time [tid/pid] (msec) (msec) (msec) --------------- ------ ------------------------------ -------- --------- --------- --------- 2090450.763231 [0000] perf[1229608] 120 0.000 0.000 0.000 2090450.763235 [0000] migration/0[15] 0 0.000 0.001 0.003 2090450.763263 [0001] perf[1229608] 120 0.000 0.000 0.000 2090450.763268 [0001] migration/1[21] 0 0.000 0.001 0.004 2090450.763302 [0002] perf[1229608] 120 0.000 0.000 0.000 2090450.763309 [0002] migration/2[27] 0 0.000 0.001 0.007 2090450.763338 [0003] perf[1229608] 120 0.000 0.000 0.000 2090450.763343 [0003] migration/3[33] 0 0.000 0.001 0.004 2090450.763459 [0004] perf[1229608] 120 0.000 0.000 0.000 2090450.763469 [0004] migration/4[39] 0 0.000 0.002 0.010 2090450.763496 [0005] perf[1229608] 120 0.000 0.000 0.000 2090450.763501 [0005] migration/5[45] 0 0.000 0.001 0.004 2090450.763613 [0006] perf[1229608] 120 0.000 0.000 0.000 2090450.763622 [0006] migration/6[51] 0 0.000 0.001 0.008 2090450.763652 [0007] perf[1229608] 120 0.000 0.000 0.000 2090450.763660 [0007] migration/7[57] 0 0.000 0.001 0.008 2090450.765665 [0001] 120 0.031 0.031 0.081 2090450.765665 [0007] sched-messaging[1229627] 129 0.000 0.038 0.030 2090450.765667 [0000] s1-perf[8235/7168] 120 0.008 0.000 0.004 2090450.765684 [0013] 120 0.028 0.028 0.088 2090450.765685 [0001] sched-messaging[1229630] 129 0.000 0.001 0.020 2090450.765688 [0000] 120 0.004 0.004 0.020 2090450.765689 [0002] 120 0.021 0.021 0.138 2090450.765691 [0005] sched-messaging[1229626] 129 0.000 0.085 0.029 Signed-off-by: Yang Jihong Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ian Rogers Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240819033016.2427235-3-yangjihong@bytedance.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-sched.txt | 6 ++ tools/perf/builtin-sched.c | 74 ++++++++++++++++++++++++- 2 files changed, 79 insertions(+), 1 deletion(-) diff --git a/tools/perf/Documentation/perf-sched.txt b/tools/perf/Documentation/perf-sched.txt index 3efa5c58418d..3db64954a267 100644 --- a/tools/perf/Documentation/perf-sched.txt +++ b/tools/perf/Documentation/perf-sched.txt @@ -215,6 +215,12 @@ OPTIONS for 'perf sched timehist' --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' ------------------------------ diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index ba6563dc93d0..0a7b2b2acd56 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -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); @@ -234,6 +235,8 @@ struct perf_sched { 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 */ @@ -2504,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; @@ -2723,7 +2747,7 @@ static int timehist_sched_change_event(const struct perf_tool *tool, goto out; } - if (sched->show_prio) + if (sched->show_prio || sched->prio_str) timehist_update_task_prio(evsel, sample, machine); thread = timehist_get_thread(sched, sample, machine, evsel); @@ -3143,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[] = { @@ -3204,6 +3269,11 @@ static int perf_sched__timehist(struct perf_sched *sched) 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 */ @@ -3761,6 +3831,8 @@ int cmd_sched(int argc, const char **argv) "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) }; From beef8fb2af95ae8aa77f05a2663710d260741ddd Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 17 Jul 2024 17:30:20 -0700 Subject: [PATCH 286/347] perf pmu: Merge boolean sysfs event option parsing Merge perf_pmu__parse_per_pkg() and perf_pmu__parse_snapshot() that do the same parsing except for the file suffix used. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ananth Narayan Cc: Bjorn Helgaas Cc: Dhananjay Ugwekar Cc: Dominique Martinet Cc: Gautham Shenoy Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Jonathan Corbet Cc: K Prateek Nayak Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sandipan Das Link: https://lore.kernel.org/r/20240718003025.1486232-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/pmu.c | 47 +++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 0b38c51bd6eb..ef8ab918eaeb 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -367,8 +367,8 @@ error: return -1; } -static int -perf_pmu__parse_per_pkg(struct perf_pmu *pmu, struct perf_pmu_alias *alias) +static bool perf_pmu__parse_event_source_bool(const char *pmu_name, const char *event_name, + const char *suffix) { char path[PATH_MAX]; size_t len; @@ -376,37 +376,36 @@ perf_pmu__parse_per_pkg(struct perf_pmu *pmu, struct perf_pmu_alias *alias) len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path)); if (!len) - return 0; - scnprintf(path + len, sizeof(path) - len, "%s/events/%s.per-pkg", pmu->name, alias->name); + return false; + + scnprintf(path + len, sizeof(path) - len, "%s/events/%s.%s", pmu_name, event_name, suffix); fd = open(path, O_RDONLY); if (fd == -1) - return -1; + return false; + +#ifndef NDEBUG + { + char buf[8]; + + len = read(fd, buf, sizeof(buf)); + assert(len == 1 || len == 2); + assert(buf[0] == '1'); + } +#endif close(fd); - - alias->per_pkg = true; - return 0; + return true; } -static int perf_pmu__parse_snapshot(struct perf_pmu *pmu, struct perf_pmu_alias *alias) +static void perf_pmu__parse_per_pkg(struct perf_pmu *pmu, struct perf_pmu_alias *alias) { - char path[PATH_MAX]; - size_t len; - int fd; + alias->per_pkg = perf_pmu__parse_event_source_bool(pmu->name, alias->name, "per-pkg"); +} - len = perf_pmu__event_source_devices_scnprintf(path, sizeof(path)); - if (!len) - return 0; - scnprintf(path + len, sizeof(path) - len, "%s/events/%s.snapshot", pmu->name, alias->name); - - fd = open(path, O_RDONLY); - if (fd == -1) - return -1; - - alias->snapshot = true; - close(fd); - return 0; +static void perf_pmu__parse_snapshot(struct perf_pmu *pmu, struct perf_pmu_alias *alias) +{ + alias->snapshot = perf_pmu__parse_event_source_bool(pmu->name, alias->name, "snapshot"); } /* Delete an alias entry. */ From f76e3525acf395f22712fc9120de555fb4b1a52f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 17 Jul 2024 17:30:21 -0700 Subject: [PATCH 287/347] perf parse-events: Pass cpu_list as a perf_cpu_map in __add_event() Previously the cpu_list is a string and typically no cpu_list is passed to __add_event(). Wanting to make events have their cpus distinct from the PMU means that in more occassions we want to pass a cpu_list. If we're reading this from sysfs it is easier to read a perf_cpu_map than allocate and pass around strings that will later be parsed. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ananth Narayan Cc: Bjorn Helgaas Cc: Dhananjay Ugwekar Cc: Dominique Martinet Cc: Gautham Shenoy Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: Jonathan Corbet Cc: K Prateek Nayak Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sandipan Das Link: https://lore.kernel.org/r/20240718003025.1486232-3-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-events.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index fab01ba54e34..9c0ce01684c3 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -227,12 +227,12 @@ __add_event(struct list_head *list, int *idx, bool init_attr, const char *name, const char *metric_id, struct perf_pmu *pmu, struct list_head *config_terms, bool auto_merge_stats, - const char *cpu_list) + struct perf_cpu_map *cpu_list) { struct evsel *evsel; - struct perf_cpu_map *cpus = pmu ? perf_cpu_map__get(pmu->cpus) : - cpu_list ? perf_cpu_map__new(cpu_list) : NULL; + struct perf_cpu_map *cpus = perf_cpu_map__is_empty(cpu_list) && pmu ? pmu->cpus : cpu_list; + cpus = perf_cpu_map__get(cpus); if (pmu) perf_pmu__warn_invalid_formats(pmu); @@ -305,16 +305,17 @@ static int add_event_tool(struct list_head *list, int *idx, .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_DUMMY, }; - const char *cpu_list = NULL; + struct perf_cpu_map *cpu_list = NULL; if (tool_event == PERF_TOOL_DURATION_TIME) { /* Duration time is gathered globally, pretend it is only on CPU0. */ - cpu_list = "0"; + cpu_list = perf_cpu_map__new("0"); } evsel = __add_event(list, idx, &attr, /*init_attr=*/true, /*name=*/NULL, /*metric_id=*/NULL, /*pmu=*/NULL, /*config_terms=*/NULL, /*auto_merge_stats=*/false, cpu_list); + perf_cpu_map__put(cpu_list); if (!evsel) return -ENOMEM; evsel->tool_event = tool_event; From 70b27c756f95156f51b1e28ec7d1b16a539d5487 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 3 Sep 2024 22:06:04 -0700 Subject: [PATCH 288/347] perf parse-events: Add default_breakpoint_len helper The default breakpoint length is "sizeof(long)" however this is incorrect on platforms like Aarch64 where sizeof(long) is 8 but the breakpoint length is 4. Add a helper function that can be used to determine the correct breakpoint length, in this change it just returns the existing default sizeof(long) value. Use the helper in the bp_account test so that, when modifying the event from a watchpoint to a breakpoint, the breakpoint length is appropriate for the architecture and not just sizeof(long). Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Athira Rajeev Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Junhao He Cc: Kan Liang Cc: Mark Rutland Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Yang Jihong Link: https://lore.kernel.org/r/20240904050606.752788-5-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/bp_account.c | 4 +++- tools/perf/tests/bp_signal.c | 3 ++- tools/perf/tests/bp_signal_overflow.c | 3 ++- tools/perf/tests/parse-events.c | 3 ++- tools/perf/util/parse-events.c | 7 ++++++- tools/perf/util/parse-events.h | 2 ++ 6 files changed, 17 insertions(+), 5 deletions(-) diff --git a/tools/perf/tests/bp_account.c b/tools/perf/tests/bp_account.c index 6f921db33cf9..4cb7d486b5c1 100644 --- a/tools/perf/tests/bp_account.c +++ b/tools/perf/tests/bp_account.c @@ -16,6 +16,7 @@ #include "tests.h" #include "debug.h" #include "event.h" +#include "parse-events.h" #include "../perf-sys.h" #include "cloexec.h" @@ -50,7 +51,7 @@ static int __event(bool is_x, void *addr, struct perf_event_attr *attr) attr->config = 0; attr->bp_type = is_x ? HW_BREAKPOINT_X : HW_BREAKPOINT_W; attr->bp_addr = (unsigned long) addr; - attr->bp_len = sizeof(long); + attr->bp_len = is_x ? default_breakpoint_len() : sizeof(long); attr->sample_period = 1; attr->sample_type = PERF_SAMPLE_IP; @@ -92,6 +93,7 @@ static int bp_accounting(int wp_cnt, int share) attr_mod = attr; attr_mod.bp_type = HW_BREAKPOINT_X; attr_mod.bp_addr = (unsigned long) test_function; + attr_mod.bp_len = default_breakpoint_len(); ret = ioctl(fd[0], PERF_EVENT_IOC_MODIFY_ATTRIBUTES, &attr_mod); TEST_ASSERT_VAL("failed to modify wp\n", ret == 0); diff --git a/tools/perf/tests/bp_signal.c b/tools/perf/tests/bp_signal.c index 1f2908f02389..3faeb5b6fe0b 100644 --- a/tools/perf/tests/bp_signal.c +++ b/tools/perf/tests/bp_signal.c @@ -26,6 +26,7 @@ #include "tests.h" #include "debug.h" #include "event.h" +#include "parse-events.h" #include "perf-sys.h" #include "cloexec.h" @@ -111,7 +112,7 @@ static int __event(bool is_x, void *addr, int sig) pe.config = 0; pe.bp_type = is_x ? HW_BREAKPOINT_X : HW_BREAKPOINT_W; pe.bp_addr = (unsigned long) addr; - pe.bp_len = sizeof(long); + pe.bp_len = is_x ? default_breakpoint_len() : sizeof(long); pe.sample_period = 1; pe.sample_type = PERF_SAMPLE_IP; diff --git a/tools/perf/tests/bp_signal_overflow.c b/tools/perf/tests/bp_signal_overflow.c index 4e897c2cf26b..ee560e156be6 100644 --- a/tools/perf/tests/bp_signal_overflow.c +++ b/tools/perf/tests/bp_signal_overflow.c @@ -25,6 +25,7 @@ #include "tests.h" #include "debug.h" #include "event.h" +#include "parse-events.h" #include "../perf-sys.h" #include "cloexec.h" @@ -88,7 +89,7 @@ static int test__bp_signal_overflow(struct test_suite *test __maybe_unused, int pe.config = 0; pe.bp_type = HW_BREAKPOINT_X; pe.bp_addr = (unsigned long) test_function; - pe.bp_len = sizeof(long); + pe.bp_len = default_breakpoint_len(); pe.sample_period = THRESHOLD; pe.sample_type = PERF_SAMPLE_IP; diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index edc2adcf1bae..639e65a9bf61 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -5,6 +5,7 @@ #include #include "tests.h" #include "debug.h" +#include "parse-events.h" #include "pmu.h" #include "pmus.h" #include @@ -262,7 +263,7 @@ static int test__checkevent_breakpoint_x(struct evlist *evlist) TEST_ASSERT_VAL("wrong config", test_config(evsel, 0)); TEST_ASSERT_VAL("wrong bp_type", HW_BREAKPOINT_X == evsel->core.attr.bp_type); - TEST_ASSERT_VAL("wrong bp_len", sizeof(long) == evsel->core.attr.bp_len); + TEST_ASSERT_VAL("wrong bp_len", default_breakpoint_len() == evsel->core.attr.bp_len); return TEST_OK; } diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 9c0ce01684c3..8c837c8a6c8c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -671,6 +671,11 @@ static int add_tracepoint_multi_sys(struct parse_events_state *parse_state, } #endif /* HAVE_LIBTRACEEVENT */ +size_t default_breakpoint_len(void) +{ + return sizeof(long); +} + static int parse_breakpoint_type(const char *type, struct perf_event_attr *attr) { @@ -729,7 +734,7 @@ int parse_events_add_breakpoint(struct parse_events_state *parse_state, /* Provide some defaults if len is not specified */ if (!len) { if (attr.bp_type == HW_BREAKPOINT_X) - len = sizeof(long); + len = default_breakpoint_len(); else len = HW_BREAKPOINT_LEN_4; } diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index b735cd9e0acf..68bfea9ffa70 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -286,4 +286,6 @@ static inline bool is_sdt_event(char *str __maybe_unused) } #endif /* HAVE_LIBELF_SUPPORT */ +size_t default_breakpoint_len(void); + #endif /* __PERF_PARSE_EVENTS_H */ From fa6cc3f932584a5fb5eedd2261fdf0058e1d8828 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 3 Sep 2024 22:06:05 -0700 Subject: [PATCH 289/347] perf parse-events: Vary default_breakpoint_len on i386 and arm64 On arm64 the breakpoint length should be 4-bytes but 8-bytes is tolerated as perf passes that as sizeof(long). Just pass the correct value. On i386 the sizeof(long) check in the kernel needs to match the kernel's long size. Check using an environment (uname checks) whether 4 or 8 bytes needs to be passed. Cache the value in a static. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Athira Rajeev Cc: Chaitanya S Prakash Cc: Colin Ian King Cc: Dominique Martinet Cc: Ingo Molnar Cc: James Clark Cc: Jiri Olsa Cc: John Garry Cc: Junhao He Cc: Kan Liang Cc: Mark Rutland Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Yang Jihong Link: https://lore.kernel.org/r/20240904050606.752788-6-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/parse-events.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 8c837c8a6c8c..f5eb1af8302c 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -8,6 +8,7 @@ #include #include #include "term.h" +#include "env.h" #include "evlist.h" #include "evsel.h" #include @@ -673,7 +674,22 @@ static int add_tracepoint_multi_sys(struct parse_events_state *parse_state, size_t default_breakpoint_len(void) { +#if defined(__i386__) + static int len; + + if (len == 0) { + struct perf_env env = {}; + + perf_env__init(&env); + len = perf_env__kernel_is_64_bit(&env) ? sizeof(u64) : sizeof(long); + perf_env__exit(&env); + } + return len; +#elif defined(__aarch64__) + return 4; +#else return sizeof(long); +#endif } static int From 1a5efc9e13f357abc396dbf445b25d08914c8060 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Wed, 4 Sep 2024 11:48:30 +0530 Subject: [PATCH 290/347] libsubcmd: Don't free the usage string Currently, commands which depend on 'parse_options_subcommand()' don't show the usage string, and instead show '(null)' $ ./perf sched Usage: (null) -D, --dump-raw-trace dump raw trace in ASCII -f, --force don't complain, do it -i, --input input file name -v, --verbose be more verbose (show symbol address, etc) 'parse_options_subcommand()' is generally expected to initialise the usage string, with information in the passed 'subcommands[]' array This behaviour was changed in: 230a7a71f92212e7 ("libsubcmd: Fix parse-options memory leak") Where the generated usage string is deallocated, and usage[0] string is reassigned as NULL. As discussed in [1], free the allocated usage string in the main function itself, and don't reset usage string to NULL in parse_options_subcommand With this change, the behaviour is restored. $ ./perf sched Usage: perf sched [] {record|latency|map|replay|script|timehist} -D, --dump-raw-trace dump raw trace in ASCII -f, --force don't complain, do it -i, --input input file name -v, --verbose be more verbose (show symbol address, etc) [1]: https://lore.kernel.org/linux-perf-users/htq5vhx6piet4nuq2mmhk7fs2bhfykv52dbppwxmo3s7du2odf@styd27tioc6e/ Fixes: 230a7a71f92212e7 ("libsubcmd: Fix parse-options memory leak") Suggested-by: Namhyung Kim Signed-off-by: Aditya Gupta Acked-by: Namhyung Kim Tested-by: Arnaldo Carvalho de Melo Cc: Athira Rajeev Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: Kajol Jain Cc: Madhavan Srinivasan Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240904061836.55873-2-adityag@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/subcmd/parse-options.c | 8 +++----- tools/perf/builtin-kmem.c | 2 ++ tools/perf/builtin-kvm.c | 3 +++ tools/perf/builtin-kwork.c | 3 +++ tools/perf/builtin-lock.c | 3 +++ tools/perf/builtin-mem.c | 3 +++ tools/perf/builtin-sched.c | 3 +++ 7 files changed, 20 insertions(+), 5 deletions(-) diff --git a/tools/lib/subcmd/parse-options.c b/tools/lib/subcmd/parse-options.c index 4b60ec03b0bb..eb896d30545b 100644 --- a/tools/lib/subcmd/parse-options.c +++ b/tools/lib/subcmd/parse-options.c @@ -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 [] {", 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); } diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c index b3cbac40b8c7..a756147e2eec 100644 --- a/tools/perf/builtin-kmem.c +++ b/tools/perf/builtin-kmem.c @@ -2057,6 +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; } diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c index 692267b1b7e8..55ea17c5ff02 100644 --- a/tools/perf/builtin-kvm.c +++ b/tools/perf/builtin-kvm.c @@ -2184,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; } diff --git a/tools/perf/builtin-kwork.c b/tools/perf/builtin-kwork.c index 6a4281b8fd10..c1daf82c9b92 100644 --- a/tools/perf/builtin-kwork.c +++ b/tools/perf/builtin-kwork.c @@ -2519,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; } diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c index 2c216427e929..062e2b56a2ab 100644 --- a/tools/perf/builtin-lock.c +++ b/tools/perf/builtin-lock.c @@ -2712,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; } diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 18e5f9a0ddc2..bb38bb5a1c26 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -546,5 +546,8 @@ int cmd_mem(int argc, const char **argv) else usage_with_options(mem_usage, mem_options); + /* free usage string allocated by parse_options_subcommand */ + free((void *)mem_usage[0]); + return 0; } diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 0a7b2b2acd56..5981cc51abc8 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c @@ -3954,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; } From 98ad0b77323ce8c216e5a505fda6b2ea53299231 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Wed, 4 Sep 2024 11:48:31 +0530 Subject: [PATCH 291/347] perf check: Introduce 'check' subcommand Currently the presence of a feature is checked with a combination of perf version --build-options and greps, such as: perf version --build-options | grep " on .* HAVE_FEATURE" Instead of this, introduce a subcommand "perf check feature", with which scripts can test for presence of a feature, such as: perf check feature HAVE_FEATURE 'perf check feature' command is expected to have exit status of 0 if feature is built-in, and 1 if it's not built-in or if feature is not known. Multiple features can also be passed as a comma-separated list, in which case the exit status will be 1 only if all of the passed features are built-in. For example, with below command, it will have exit status of 0 only if both libtraceevent and bpf are enabled, else 1 in all other cases perf check feature libtraceevent,bpf The arguments are case-insensitive. An array 'supported_features' has also been introduced that can be used by other commands like 'perf version --build-options', so that new features can be added in one place, with the array Committer testing: $ perf check feature libtraceevent,bpf libtraceevent: [ on ] # HAVE_LIBTRACEEVENT bpf: [ on ] # HAVE_LIBBPF_SUPPORT $ perf check feature libtraceevent libtraceevent: [ on ] # HAVE_LIBTRACEEVENT $ perf check feature bpf bpf: [ on ] # HAVE_LIBBPF_SUPPORT $ perf check -q feature bpf && echo "BPF support is present" BPF support is present $ perf check -q feature Bogus && echo "Bogus support is present" $ Reviewed-by: Athira Rajeev Signed-off-by: Aditya Gupta Tested-by: Arnaldo Carvalho de Melo Cc: Athira Rajeev Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: Kajol Jain Cc: Madhavan Srinivasan Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240904061836.55873-3-adityag@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Build | 1 + tools/perf/Documentation/perf-check.txt | 82 +++++++++++ tools/perf/builtin-check.c | 180 ++++++++++++++++++++++++ tools/perf/builtin.h | 17 +++ tools/perf/perf.c | 1 + 5 files changed, 281 insertions(+) create mode 100644 tools/perf/Documentation/perf-check.txt create mode 100644 tools/perf/builtin-check.c diff --git a/tools/perf/Build b/tools/perf/Build index 1d4957957d75..3e486f7df94b 100644 --- a/tools/perf/Build +++ b/tools/perf/Build @@ -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 diff --git a/tools/perf/Documentation/perf-check.txt b/tools/perf/Documentation/perf-check.txt new file mode 100644 index 000000000000..b081514d240a --- /dev/null +++ b/tools/perf/Documentation/perf-check.txt @@ -0,0 +1,82 @@ +perf-check(1) +=============== + +NAME +---- +perf-check - check if features are present in perf + +SYNOPSIS +-------- +[verse] +'perf check' [] +'perf check' {feature } [] + +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-support / HAVE_DWARF_UNWIND_SUPPORT + get_cpuid / 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' diff --git a/tools/perf/builtin-check.c b/tools/perf/builtin-check.c new file mode 100644 index 000000000000..16a04b5456d9 --- /dev/null +++ b/tools/perf/builtin-check.c @@ -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 +#include +#include +#include +#include + +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 ", + 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-support", HAVE_DWARF_UNWIND_SUPPORT), + FEATURE_STATUS("get_cpuid", 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 ' + * + * 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; +} diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h index f4375deabfa3..94f4b3769bf7 100644 --- a/tools/perf/builtin.h +++ b/tools/perf/builtin.h @@ -2,6 +2,22 @@ #ifndef BUILTIN_H #define BUILTIN_H +#include +#include +#include + +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); diff --git a/tools/perf/perf.c b/tools/perf/perf.c index bd3f80b5bb46..4def800f4089 100644 --- a/tools/perf/perf.c +++ b/tools/perf/perf.c @@ -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 }, From 9b2b9b66d532197d08efa1eba157fc484c3657bc Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Tue, 3 Sep 2024 21:43:51 -0700 Subject: [PATCH 292/347] perf jevents: Add cpuid to model lookup command When restricting jevents generated json lookup code with JEVENTS_MODEL a list of models must be provided. Some builds don't know model names but know cpuids. Add a command that can convert a cpuid to a model using mapfile.csv files. This can be used with JEVENTS_MODEL like: $ make JEVENTS_MODEL=`./pmu-events/models.py x86 'GenuineIntel-6-8D-1,AuthenticAMD-26-1' pmu-events/arch/` Committer testing: $ tools/perf/pmu-events/models.py x86 'GenuineIntel-6-8D-1,AuthenticAMD-26-1' tools/perf/pmu-events/arch/ tigerlake,amdzen5 $ perf stat -v sleep 1 |& head -1 Using CPUID GenuineIntel-6-B7-1 $ tools/perf/pmu-events/models.py x86 'GenuineIntel-6-B7-1' tools/perf/pmu-events/arch/ alderlake $ Signed-off-by: Ian Rogers Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240904044351.712080-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/models.py | 73 +++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100755 tools/perf/pmu-events/models.py diff --git a/tools/perf/pmu-events/models.py b/tools/perf/pmu-events/models.py new file mode 100755 index 000000000000..8f727d29c952 --- /dev/null +++ b/tools/perf/pmu-events/models.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) +"""List model names from mapfile.csv files.""" +import argparse +import csv +import os +import re +from typing import List + +def main() -> None: + def dir_path(path: str) -> str: + """Validate path is a directory for argparse.""" + if os.path.isdir(path): + return path + raise argparse.ArgumentTypeError(f'\'{path}\' is not a valid directory') + + def find_archs(start_dir: str, arch: str) -> List[str]: + archs = [] + for item in os.scandir(start_dir): + if not item.is_dir(): + continue + if arch in (item.name, 'all'): + archs.append(item.name) + + if len(archs) < 1: + raise IOError(f'Missing architecture directory \'{arch}\'') + + return archs + + def find_mapfiles(start_dir: str, archs: List[str]) -> List[str]: + result = [] + for arch in archs: + for item in os.scandir(f'{start_dir}/{arch}'): + if item.is_dir(): + continue + if item.name == 'mapfile.csv': + result.append(f'{start_dir}/{arch}/mapfile.csv') + return result + + def find_cpuids(mapfiles: List[str], cpuids: str) -> List[str]: + result = [] + for mapfile in mapfiles: + with open(mapfile, encoding='utf-8') as csvfile: + first = False + table = csv.reader(csvfile) + for row in table: + if not first or len(row) == 0 or row[0].startswith('#'): + first = True + continue + # Python regular expressions don't handle xdigit. + regex = row[0].replace('[[:xdigit:]]', '[0-9a-fA-F]') + for cpuid in cpuids.split(','): + if re.match(regex, cpuid): + result.append(row[2]) + return result + + ap = argparse.ArgumentParser() + ap.add_argument('arch', help='Architecture name like x86') + ap.add_argument('cpuid', default='all', help='List of cpuids to convert to model names') + ap.add_argument( + 'starting_dir', + type=dir_path, + help='Root of tree containing architecture directories containing json files' + ) + args = ap.parse_args() + + archs = find_archs(args.starting_dir, args.arch) + mapfiles = find_mapfiles(args.starting_dir, archs) + models = find_cpuids(mapfiles, args.cpuid) + print(','.join(models)) + +if __name__ == '__main__': + main() From d441734d0cfcebc5780bc1880d871f92debb588a Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 19 Aug 2024 17:20:28 -0400 Subject: [PATCH 293/347] ktest.pl: Always warn on build warnings If a warning happens at build, give a warning at the end: Build time: 1 minute 40 seconds Install time: 17 seconds Reboot time: 25 seconds *** WARNING found in build: 1 *** ******************************************* ******************************************* KTEST RESULT: TEST 1 SUCCESS!!!! ** ******************************************* ******************************************* This way, even if the test isn't made to fail on warnings during the build, a message is still displayed that warnings were found. Link: https://lore.kernel.org/<20240819172028.3a7fae09@gandalf.local.home> Acked-by: John 'Warthog9' Hawley (Tenstorrent) Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index eb31cd9c977b..c82b8d55dddb 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -222,6 +222,8 @@ my $install_time; my $reboot_time; my $test_time; +my $warning_found = 0; + my $pwd; my $dirname = $FindBin::Bin; @@ -729,11 +731,18 @@ sub print_times { show_time($test_time); doprint "\n"; } + if ($warning_found) { + doprint "\n*** WARNING"; + doprint "S" if ($warning_found > 1); + doprint " found in build: $warning_found ***\n\n"; + } + # reset for iterations like bisect $build_time = 0; $install_time = 0; $reboot_time = 0; $test_time = 0; + $warning_found = 0; } sub get_mandatory_configs { @@ -2460,8 +2469,6 @@ sub process_warning_line { # Returns 1 if OK # 0 otherwise sub check_buildlog { - return 1 if (!defined $warnings_file); - my %warnings_list; # Failed builds should not reboot the target @@ -2482,18 +2489,21 @@ sub check_buildlog { close(IN); } - # If warnings file didn't exist, and WARNINGS_FILE exist, - # then we fail on any warning! - open(IN, $buildlog) or dodie "Can't open $buildlog"; while () { if (/$check_build_re/) { my $warning = process_warning_line $_; if (!defined $warnings_list{$warning}) { - fail "New warning found (not in $warnings_file)\n$_\n"; - $no_reboot = $save_no_reboot; - return 0; + $warning_found++; + + # If warnings file didn't exist, and WARNINGS_FILE exist, + # then we fail on any warning! + if (defined $warnings_file) { + fail "New warning found (not in $warnings_file)\n$_\n"; + $no_reboot = $save_no_reboot; + return 0; + } } } } From 2351e8c65404aabc433300b6bf90c7a37e8bbc4d Mon Sep 17 00:00:00 2001 From: Daniel Jordan Date: Wed, 4 Sep 2024 13:55:30 -0400 Subject: [PATCH 294/347] ktest.pl: Avoid false positives with grub2 skip regex Some distros have grub2 config files with the lines if [ x"${feature_menuentry_id}" = xy ]; then menuentry_id_option="--id" else menuentry_id_option="" fi which match the skip regex defined for grub2 in get_grub_index(): $skip = '^\s*menuentry'; These false positives cause the grub number to be higher than it should be, and the wrong kernel can end up booting. Grub documents the menuentry command with whitespace between it and the title, so make the skip regex reflect this. Link: https://lore.kernel.org/20240904175530.84175-1-daniel.m.jordan@oracle.com Signed-off-by: Daniel Jordan Acked-by: John 'Warthog9' Hawley (Tenstorrent) Signed-off-by: Steven Rostedt --- tools/testing/ktest/ktest.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl index c82b8d55dddb..dacad94e2be4 100755 --- a/tools/testing/ktest/ktest.pl +++ b/tools/testing/ktest/ktest.pl @@ -2056,7 +2056,7 @@ sub get_grub_index { } elsif ($reboot_type eq "grub2") { $command = "cat $grub_file"; $target = '^\s*menuentry.*' . $grub_menu_qt; - $skip = '^\s*menuentry'; + $skip = '^\s*menuentry\s'; $submenu = '^\s*submenu\s'; } elsif ($reboot_type eq "grub2bls") { $command = $grub_bls_get; From 6cdd7750de408b08c9f77ac23723d48598fbeca7 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 5 Sep 2024 00:31:28 +0530 Subject: [PATCH 295/347] perf version: Update --build-options to use 'supported_features' array Now that the feature list has been duplicated in a global 'supported_features' array, use that array instead of manually checking status of built-in features. This helps in being consistent with commands such as 'perf check feature', so commands can use the same array, and any new feature can be added at one place, in the 'supported_features' array Reviewed-by: Athira Rajeev Signed-off-by: Aditya Gupta Acked-by: Namhyung Kim Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: Kajol Jain Cc: Madhavan Srinivasan Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240904190132.415212-4-adityag@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-version.c | 44 +++++++----------------------------- 1 file changed, 8 insertions(+), 36 deletions(-) diff --git a/tools/perf/builtin-version.c b/tools/perf/builtin-version.c index 4b252196de12..e149d96c6dc5 100644 --- a/tools/perf/builtin-version.c +++ b/tools/perf/builtin-version.c @@ -46,46 +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_LIBLLVM_SUPPORT, libllvm); - 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) From 8a028502b4124eb9e37b5216e440ebb9e187b0a1 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 5 Sep 2024 00:31:29 +0530 Subject: [PATCH 296/347] perf tools test_task_analyzer.sh: Update to use 'perf check feature' Currently we use output of 'perf version --build-options', to check whether perf was built with libtraceevent support. Instead, use 'perf check feature libtraceevent' to check for libtraceevent support. Reviewed-by: Athira Rajeev Signed-off-by: Aditya Gupta Acked-by: Namhyung Kim Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: Kajol Jain Cc: Madhavan Srinivasan Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240904190132.415212-5-adityag@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/test_task_analyzer.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/tests/shell/test_task_analyzer.sh b/tools/perf/tests/shell/test_task_analyzer.sh index cb02bf23e6a5..7d76fc63d995 100755 --- a/tools/perf/tests/shell/test_task_analyzer.sh +++ b/tools/perf/tests/shell/test_task_analyzer.sh @@ -55,8 +55,8 @@ find_str_or_fail() { # check if perf is compiled with libtraceevent support skip_no_probe_record_support() { - perf version --build-options | grep -q " OFF .* HAVE_LIBTRACEEVENT" && return 2 - return 0 + perf check feature -q libtraceevent && return 0 + return 2 } prepare_perf_data() { From 512fcf7d9d7fb3751b03db45977b7fabaadfaecd Mon Sep 17 00:00:00 2001 From: Athira Rajeev Date: Thu, 5 Sep 2024 00:31:30 +0530 Subject: [PATCH 297/347] perf tests probe_vfs_getname.sh: Update to use 'perf check feature' In probe_vfs_getname.sh, current we use "perf record --dry-run" to check for libtraceevent and skip the test if perf is not build with libtraceevent. Change the check to use "perf check feature" option Signed-off-by: Athira Rajeev Acked-by: Namhyung Kim Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: Kajol Jain Cc: Madhavan Srinivasan Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240904190132.415212-6-adityag@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/lib/probe_vfs_getname.sh | 4 ++-- tools/perf/tests/shell/record+probe_libc_inet_pton.sh | 5 ++++- tools/perf/tests/shell/record+script_probe_vfs_getname.sh | 5 ++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tools/perf/tests/shell/lib/probe_vfs_getname.sh b/tools/perf/tests/shell/lib/probe_vfs_getname.sh index b3802d9494b4..0606e693eb59 100644 --- a/tools/perf/tests/shell/lib/probe_vfs_getname.sh +++ b/tools/perf/tests/shell/lib/probe_vfs_getname.sh @@ -32,7 +32,7 @@ skip_if_no_debuginfo() { # check if perf is compiled with libtraceevent support skip_no_probe_record_support() { if [ $had_vfs_getname -eq 1 ] ; then - perf record --dry-run -e $1 2>&1 | grep "libtraceevent is necessary for tracepoint support" && return 2 - return 1 + perf check feature -q libtraceevent && return 1 + return 2 fi } diff --git a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh index 72c65570db37..f38c8ead0b03 100755 --- a/tools/perf/tests/shell/record+probe_libc_inet_pton.sh +++ b/tools/perf/tests/shell/record+probe_libc_inet_pton.sh @@ -63,7 +63,10 @@ trace_libc_inet_pton_backtrace() { # Check presence of libtraceevent support to run perf record skip_no_probe_record_support "$event_name/$eventattr/" - [ $? -eq 2 ] && return 2 + if [ $? -eq 2 ]; then + echo "WARN: Skipping test trace_libc_inet_pton_backtrace. No libtraceevent support." + return 2 + fi perf record -e $event_name/$eventattr/ -o $perf_data ping -6 -c 1 ::1 > /dev/null 2>&1 # check if perf data file got created in above step. diff --git a/tools/perf/tests/shell/record+script_probe_vfs_getname.sh b/tools/perf/tests/shell/record+script_probe_vfs_getname.sh index 5eedbe29bba1..9a61928e3c9a 100755 --- a/tools/perf/tests/shell/record+script_probe_vfs_getname.sh +++ b/tools/perf/tests/shell/record+script_probe_vfs_getname.sh @@ -21,7 +21,10 @@ record_open_file() { echo "Recording open file:" # Check presence of libtraceevent support to run perf record skip_no_probe_record_support "probe:vfs_getname*" - [ $? -eq 2 ] && return 2 + if [ $? -eq 2 ]; then + echo "WARN: Skipping test record_open_file. No libtraceevent support" + return 2 + fi perf record -o ${perfdata} -e probe:vfs_getname\* touch $file } From 35439fe4e29bca5fe8da8ed3f8524ef98f3e7aae Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 5 Sep 2024 00:31:31 +0530 Subject: [PATCH 298/347] perf check: Fix inconsistencies in feature names Fix two inconsistencies in feature names as discussed in [1]: 1. Rename "dwarf-unwind-support" to "dwarf-unwind" 2. 'get_cpuid' feature and 'HAVE_AUXTRACE_SUPPORT' names don't look related, change the feature name to 'auxtrace' to match the macro name, as 'get_cpuid' string is not used anywhere to check the feature presence [1]: https://lore.kernel.org/linux-perf-users/ZoRw5we4HLSTZND6@x1/ Suggested-by: Arnaldo Carvalho de Melo Signed-off-by: Aditya Gupta Cc: Athira Rajeev Cc: Disha Goel Cc: Ian Rogers Cc: Jiri Olsa Cc: Kajol Jain Cc: Madhavan Srinivasan Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240904190132.415212-7-adityag@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Documentation/perf-check.txt | 4 ++-- tools/perf/builtin-check.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/perf/Documentation/perf-check.txt b/tools/perf/Documentation/perf-check.txt index b081514d240a..10f69fb6850b 100644 --- a/tools/perf/Documentation/perf-check.txt +++ b/tools/perf/Documentation/perf-check.txt @@ -49,8 +49,8 @@ feature:: debuginfod / HAVE_DEBUGINFOD_SUPPORT dwarf / HAVE_DWARF_SUPPORT dwarf_getlocations / HAVE_DWARF_GETLOCATIONS_SUPPORT - dwarf-unwind-support / HAVE_DWARF_UNWIND_SUPPORT - get_cpuid / HAVE_AUXTRACE_SUPPORT + dwarf-unwind / HAVE_DWARF_UNWIND_SUPPORT + auxtrace / HAVE_AUXTRACE_SUPPORT libaudit / HAVE_LIBAUDIT_SUPPORT libbfd / HAVE_LIBBFD_SUPPORT libcapstone / HAVE_LIBCAPSTONE_SUPPORT diff --git a/tools/perf/builtin-check.c b/tools/perf/builtin-check.c index 16a04b5456d9..0b76b6e42b78 100644 --- a/tools/perf/builtin-check.c +++ b/tools/perf/builtin-check.c @@ -29,8 +29,8 @@ struct feature_status supported_features[] = { FEATURE_STATUS("debuginfod", HAVE_DEBUGINFOD_SUPPORT), FEATURE_STATUS("dwarf", HAVE_DWARF_SUPPORT), FEATURE_STATUS("dwarf_getlocations", HAVE_DWARF_GETLOCATIONS_SUPPORT), - FEATURE_STATUS("dwarf-unwind-support", HAVE_DWARF_UNWIND_SUPPORT), - FEATURE_STATUS("get_cpuid", HAVE_AUXTRACE_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), From f2dbc7790929b2e39a348a344311a8b70e6ed4b4 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Wed, 4 Sep 2024 14:17:05 -0700 Subject: [PATCH 299/347] perf jevents: Ignore sys when determining a model directory Existing sys directories aren't placed under a model directory like skylake. Placing a sys directory there causes the `is_leaf_dir` test to fail and consequently no events or metrics are generated for the model. Ignore sys directories in this case and update the comments to reflect why. This change has no affect, but when testing with a sys directory for a model people have reported running into the no event/metric issue. Reported-by: Stephane Eranian Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: Sandipan Das Cc: Thomas Richter Cc: Xu Yang Link: https://lore.kernel.org/r/20240904211705.915101-1-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/pmu-events/jevents.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/tools/perf/pmu-events/jevents.py b/tools/perf/pmu-events/jevents.py index 1d96b2204e52..bb0a5d92df4a 100755 --- a/tools/perf/pmu-events/jevents.py +++ b/tools/perf/pmu-events/jevents.py @@ -635,14 +635,17 @@ def preprocess_one_file(parents: Sequence[str], item: os.DirEntry) -> None: def process_one_file(parents: Sequence[str], item: os.DirEntry) -> None: """Process a JSON file during the main walk.""" - def is_leaf_dir(path: str) -> bool: + def is_leaf_dir_ignoring_sys(path: str) -> bool: for item in os.scandir(path): - if item.is_dir(): + if item.is_dir() and item.name != 'sys': return False return True - # model directory, reset topic - if item.is_dir() and is_leaf_dir(item.path): + # Model directories are leaves (ignoring possible sys + # directories). The FTW will walk into the directory next. Flush + # pending events and metrics and update the table names for the new + # model directory. + if item.is_dir() and is_leaf_dir_ignoring_sys(item.path): print_pending_events() print_pending_metrics() From 4bef6168c145852cebefe2f0bd78f9aaeba52843 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Thu, 5 Sep 2024 08:07:55 -0700 Subject: [PATCH 300/347] perf script python: Avoid buffer overflow in python PEBS register interface Running a script that processes PEBS records gives buffer overflows in valgrind. The problem is that the allocation of the register string doesn't include the terminating 0 byte. Fix this. I also replaced the very magic "28" with a more reasonable larger buffer that should fit all registers. There's no need to conserve memory here. ==2106591== Memcheck, a memory error detector ==2106591== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. ==2106591== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info ==2106591== Command: ../perf script -i tcall.data gcov.py tcall.gcov ==2106591== ==2106591== Invalid write of size 1 ==2106591== at 0x713354: regs_map (trace-event-python.c:748) ==2106591== by 0x7134EB: set_regs_in_dict (trace-event-python.c:784) ==2106591== by 0x713E58: get_perf_sample_dict (trace-event-python.c:940) ==2106591== by 0x716327: python_process_general_event (trace-event-python.c:1499) ==2106591== by 0x7164E1: python_process_event (trace-event-python.c:1531) ==2106591== by 0x44F9AF: process_sample_event (builtin-script.c:2549) ==2106591== by 0x6294DC: evlist__deliver_sample (session.c:1534) ==2106591== by 0x6296D0: machines__deliver_event (session.c:1573) ==2106591== by 0x629C39: perf_session__deliver_event (session.c:1655) ==2106591== by 0x625830: ordered_events__deliver_event (session.c:193) ==2106591== by 0x630B23: do_flush (ordered-events.c:245) ==2106591== by 0x630E7A: __ordered_events__flush (ordered-events.c:324) ==2106591== Address 0x7186fe0 is 0 bytes after a block of size 0 alloc'd ==2106591== at 0x484280F: malloc (vg_replace_malloc.c:442) ==2106591== by 0x7134AD: set_regs_in_dict (trace-event-python.c:780) ==2106591== by 0x713E58: get_perf_sample_dict (trace-event-python.c:940) ==2106591== by 0x716327: python_process_general_event (trace-event-python.c:1499) ==2106591== by 0x7164E1: python_process_event (trace-event-python.c:1531) ==2106591== by 0x44F9AF: process_sample_event (builtin-script.c:2549) ==2106591== by 0x6294DC: evlist__deliver_sample (session.c:1534) ==2106591== by 0x6296D0: machines__deliver_event (session.c:1573) ==2106591== by 0x629C39: perf_session__deliver_event (session.c:1655) ==2106591== by 0x625830: ordered_events__deliver_event (session.c:193) ==2106591== by 0x630B23: do_flush (ordered-events.c:245) ==2106591== by 0x630E7A: __ordered_events__flush (ordered-events.c:324) ==2106591== ==2106591== Invalid read of size 1 ==2106591== at 0x484B6C6: strlen (vg_replace_strmem.c:502) ==2106591== by 0x555D494: PyUnicode_FromString (unicodeobject.c:1899) ==2106591== by 0x7134F7: set_regs_in_dict (trace-event-python.c:786) ==2106591== by 0x713E58: get_perf_sample_dict (trace-event-python.c:940) ==2106591== by 0x716327: python_process_general_event (trace-event-python.c:1499) ==2106591== by 0x7164E1: python_process_event (trace-event-python.c:1531) ==2106591== by 0x44F9AF: process_sample_event (builtin-script.c:2549) ==2106591== by 0x6294DC: evlist__deliver_sample (session.c:1534) ==2106591== by 0x6296D0: machines__deliver_event (session.c:1573) ==2106591== by 0x629C39: perf_session__deliver_event (session.c:1655) ==2106591== by 0x625830: ordered_events__deliver_event (session.c:193) ==2106591== by 0x630B23: do_flush (ordered-events.c:245) ==2106591== Address 0x7186fe0 is 0 bytes after a block of size 0 alloc'd ==2106591== at 0x484280F: malloc (vg_replace_malloc.c:442) ==2106591== by 0x7134AD: set_regs_in_dict (trace-event-python.c:780) ==2106591== by 0x713E58: get_perf_sample_dict (trace-event-python.c:940) ==2106591== by 0x716327: python_process_general_event (trace-event-python.c:1499) ==2106591== by 0x7164E1: python_process_event (trace-event-python.c:1531) ==2106591== by 0x44F9AF: process_sample_event (builtin-script.c:2549) ==2106591== by 0x6294DC: evlist__deliver_sample (session.c:1534) ==2106591== by 0x6296D0: machines__deliver_event (session.c:1573) ==2106591== by 0x629C39: perf_session__deliver_event (session.c:1655) ==2106591== by 0x625830: ordered_events__deliver_event (session.c:193) ==2106591== by 0x630B23: do_flush (ordered-events.c:245) ==2106591== by 0x630E7A: __ordered_events__flush (ordered-events.c:324) ==2106591== ==2106591== Invalid write of size 1 ==2106591== at 0x713354: regs_map (trace-event-python.c:748) ==2106591== by 0x713539: set_regs_in_dict (trace-event-python.c:789) ==2106591== by 0x713E58: get_perf_sample_dict (trace-event-python.c:940) ==2106591== by 0x716327: python_process_general_event (trace-event-python.c:1499) ==2106591== by 0x7164E1: python_process_event (trace-event-python.c:1531) ==2106591== by 0x44F9AF: process_sample_event (builtin-script.c:2549) ==2106591== by 0x6294DC: evlist__deliver_sample (session.c:1534) ==2106591== by 0x6296D0: machines__deliver_event (session.c:1573) ==2106591== by 0x629C39: perf_session__deliver_event (session.c:1655) ==2106591== by 0x625830: ordered_events__deliver_event (session.c:193) ==2106591== by 0x630B23: do_flush (ordered-events.c:245) ==2106591== by 0x630E7A: __ordered_events__flush (ordered-events.c:324) ==2106591== Address 0x7186fe0 is 0 bytes after a block of size 0 alloc'd ==2106591== at 0x484280F: malloc (vg_replace_malloc.c:442) ==2106591== by 0x7134AD: set_regs_in_dict (trace-event-python.c:780) ==2106591== by 0x713E58: get_perf_sample_dict (trace-event-python.c:940) ==2106591== by 0x716327: python_process_general_event (trace-event-python.c:1499) ==2106591== by 0x7164E1: python_process_event (trace-event-python.c:1531) ==2106591== by 0x44F9AF: process_sample_event (builtin-script.c:2549) ==2106591== by 0x6294DC: evlist__deliver_sample (session.c:1534) ==2106591== by 0x6296D0: machines__deliver_event (session.c:1573) ==2106591== by 0x629C39: perf_session__deliver_event (session.c:1655) ==2106591== by 0x625830: ordered_events__deliver_event (session.c:193) ==2106591== by 0x630B23: do_flush (ordered-events.c:245) ==2106591== by 0x630E7A: __ordered_events__flush (ordered-events.c:324) ==2106591== ==2106591== Invalid read of size 1 ==2106591== at 0x484B6C6: strlen (vg_replace_strmem.c:502) ==2106591== by 0x555D494: PyUnicode_FromString (unicodeobject.c:1899) ==2106591== by 0x713545: set_regs_in_dict (trace-event-python.c:791) ==2106591== by 0x713E58: get_perf_sample_dict (trace-event-python.c:940) ==2106591== by 0x716327: python_process_general_event (trace-event-python.c:1499) ==2106591== by 0x7164E1: python_process_event (trace-event-python.c:1531) ==2106591== by 0x44F9AF: process_sample_event (builtin-script.c:2549) ==2106591== by 0x6294DC: evlist__deliver_sample (session.c:1534) ==2106591== by 0x6296D0: machines__deliver_event (session.c:1573) ==2106591== by 0x629C39: perf_session__deliver_event (session.c:1655) ==2106591== by 0x625830: ordered_events__deliver_event (session.c:193) ==2106591== by 0x630B23: do_flush (ordered-events.c:245) ==2106591== Address 0x7186fe0 is 0 bytes after a block of size 0 alloc'd ==2106591== at 0x484280F: malloc (vg_replace_malloc.c:442) ==2106591== by 0x7134AD: set_regs_in_dict (trace-event-python.c:780) ==2106591== by 0x713E58: get_perf_sample_dict (trace-event-python.c:940) ==2106591== by 0x716327: python_process_general_event (trace-event-python.c:1499) ==2106591== by 0x7164E1: python_process_event (trace-event-python.c:1531) ==2106591== by 0x44F9AF: process_sample_event (builtin-script.c:2549) ==2106591== by 0x6294DC: evlist__deliver_sample (session.c:1534) ==2106591== by 0x6296D0: machines__deliver_event (session.c:1573) ==2106591== by 0x629C39: perf_session__deliver_event (session.c:1655) ==2106591== by 0x625830: ordered_events__deliver_event (session.c:193) ==2106591== by 0x630B23: do_flush (ordered-events.c:245) ==2106591== by 0x630E7A: __ordered_events__flush (ordered-events.c:324) ==2106591== 73056 total, 29 ignored Signed-off-by: Andi Kleen Cc: Adrian Hunter Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240905151058.2127122-2-ak@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- .../perf/util/scripting-engines/trace-event-python.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 6971dd6c231f..d7183134b669 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -762,6 +762,8 @@ static void regs_map(struct regs_dump *regs, uint64_t mask, const char *arch, ch } } +#define MAX_REG_SIZE 128 + static int set_regs_in_dict(PyObject *dict, struct perf_sample *sample, struct evsel *evsel) @@ -769,14 +771,7 @@ static int set_regs_in_dict(PyObject *dict, struct perf_event_attr *attr = &evsel->core.attr; const char *arch = perf_env__arch(evsel__env(evsel)); - /* - * Here value 28 is a constant size which can be used to print - * one register value and its corresponds to: - * 16 chars is to specify 64 bit register in hexadecimal. - * 2 chars is for appending "0x" to the hexadecimal value and - * 10 chars is for register name. - */ - int size = __sw_hweight64(attr->sample_regs_intr) * 28; + int size = (__sw_hweight64(attr->sample_regs_intr) * MAX_REG_SIZE) + 1; char *bf = malloc(size); if (!bf) return -1; From 6e05d28ff232cf445cc6ae59336b7f2081ef9b96 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Thu, 5 Sep 2024 10:07:35 -0700 Subject: [PATCH 301/347] perf mem: Check mem_events for all eligible PMUs The current perf_pmu__mem_events_init() only checks the availability of the mem_events for the first eligible PMU. It works for non-hybrid machines and hybrid machines that have the same mem_events. However, it may bring issues if a hybrid machine has a different mem_events on different PMU, e.g., Alder Lake and Raptor Lake. A mem-loads-aux event is only required for the p-core. The mem_events on both e-core and p-core should be checked and marked. The issue was not found, because it's hidden by another bug, which only records the mem-events for the e-core. The wrong check for the p-core events didn't yell. Fixes: abbdd79b786e036e ("perf mem: Clean up perf_mem_events__name()") Signed-off-by: Kan Liang Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240905170737.4070743-1-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-c2c.c | 2 +- tools/perf/builtin-mem.c | 2 +- tools/perf/util/mem-events.c | 14 +++++++++++++- tools/perf/util/mem-events.h | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index cd2bd573bfc3..cef95b2781c0 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -3261,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; } diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index bb38bb5a1c26..a9f94785809e 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -88,7 +88,7 @@ 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; } diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c index be048bd02f36..17f80013e574 100644 --- a/tools/perf/util/mem-events.c +++ b/tools/perf/util/mem-events.c @@ -192,7 +192,7 @@ static bool perf_pmu__mem_events_supported(const char *mnt, struct perf_pmu *pmu return !stat(path, &st); } -int perf_pmu__mem_events_init(struct perf_pmu *pmu) +static int __perf_pmu__mem_events_init(struct perf_pmu *pmu) { const char *mnt = sysfs__mount(); bool found = false; @@ -219,6 +219,18 @@ int perf_pmu__mem_events_init(struct perf_pmu *pmu) return found ? 0 : -ENOENT; } +int perf_pmu__mem_events_init(void) +{ + struct perf_pmu *pmu = NULL; + + while ((pmu = perf_pmus__scan_mem(pmu)) != NULL) { + if (__perf_pmu__mem_events_init(pmu)) + return -ENOENT; + } + + return 0; +} + void perf_pmu__mem_events_list(struct perf_pmu *pmu) { int j; diff --git a/tools/perf/util/mem-events.h b/tools/perf/util/mem-events.h index ca31014d7934..a6fc2a593938 100644 --- a/tools/perf/util/mem-events.h +++ b/tools/perf/util/mem-events.h @@ -30,7 +30,7 @@ extern unsigned int perf_mem_events__loads_ldlat; extern struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX]; int perf_pmu__mem_events_parse(struct perf_pmu *pmu, const char *str); -int perf_pmu__mem_events_init(struct perf_pmu *pmu); +int perf_pmu__mem_events_init(void); struct perf_mem_event *perf_pmu__mem_events_ptr(struct perf_pmu *pmu, int i); struct perf_pmu *perf_mem_events_find_pmu(void); From 5ad7db2c3f941cde3045ce38a9c4c40b0c7d56b9 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Thu, 5 Sep 2024 10:07:36 -0700 Subject: [PATCH 302/347] perf mem: Fix missed p-core mem events on ADL and RPL The p-core mem events are missed when launching 'perf mem record' on ADL and RPL. root@number:~# perf mem record sleep 1 Memory events are enabled on a subset of CPUs: 16-27 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.032 MB perf.data ] root@number:~# perf evlist cpu_atom/mem-loads,ldlat=30/P cpu_atom/mem-stores/P dummy:u A variable 'record' in the 'struct perf_mem_event' is to indicate whether a mem event in a mem_events[] should be recorded. The current code only configure the variable for the first eligible PMU. It's good enough for a non-hybrid machine or a hybrid machine which has the same mem_events[]. However, if a different mem_events[] is used for different PMUs on a hybrid machine, e.g., ADL or RPL, the 'record' for the second PMU never get a chance to be set. The mem_events[] of the second PMU are always ignored. 'perf mem' doesn't support the per-PMU configuration now. A per-PMU mem_events[] 'record' variable doesn't make sense. Make it global. That could also avoid searching for the per-PMU mem_events[] via perf_pmu__mem_events_ptr every time. Committer testing: root@number:~# perf evlist -g cpu_atom/mem-loads,ldlat=30/P cpu_atom/mem-stores/P {cpu_core/mem-loads-aux/,cpu_core/mem-loads,ldlat=30/} cpu_core/mem-stores/P dummy:u root@number:~# The :S for '{cpu_core/mem-loads-aux/,cpu_core/mem-loads,ldlat=30/}' is not being added by 'perf evlist -g', to be checked. Fixes: abbdd79b786e036e ("perf mem: Clean up perf_mem_events__name()") Reported-by: Arnaldo Carvalho de Melo Signed-off-by: Kan Liang Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Closes: https://lore.kernel.org/lkml/Zthu81fA3kLC2CS2@x1/ Link: https://lore.kernel.org/r/20240905170737.4070743-2-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-c2c.c | 12 ++++-------- tools/perf/builtin-mem.c | 17 ++++++----------- tools/perf/util/mem-events.c | 6 ++++-- tools/perf/util/mem-events.h | 2 +- 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/tools/perf/builtin-c2c.c b/tools/perf/builtin-c2c.c index cef95b2781c0..15e1fce71c72 100644 --- a/tools/perf/builtin-c2c.c +++ b/tools/perf/builtin-c2c.c @@ -3285,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"; diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index a9f94785809e..615e905450b3 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -117,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"; diff --git a/tools/perf/util/mem-events.c b/tools/perf/util/mem-events.c index 17f80013e574..051feb93ed8d 100644 --- a/tools/perf/util/mem-events.c +++ b/tools/perf/util/mem-events.c @@ -29,6 +29,8 @@ struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX] = { }; #undef E +bool perf_mem_record[PERF_MEM_EVENTS__MAX] = { 0 }; + static char mem_loads_name[100]; static char mem_stores_name[100]; @@ -163,7 +165,7 @@ int perf_pmu__mem_events_parse(struct perf_pmu *pmu, const char *str) continue; if (strstr(e->tag, tok)) - e->record = found = true; + perf_mem_record[j] = found = true; } tok = strtok_r(NULL, ",", &saveptr); @@ -261,7 +263,7 @@ int perf_mem_events__record_args(const char **rec_argv, int *argv_nr) for (int j = 0; j < PERF_MEM_EVENTS__MAX; j++) { e = perf_pmu__mem_events_ptr(pmu, j); - if (!e->record) + if (!perf_mem_record[j]) continue; if (!e->supported) { diff --git a/tools/perf/util/mem-events.h b/tools/perf/util/mem-events.h index a6fc2a593938..8dc27db9fd52 100644 --- a/tools/perf/util/mem-events.h +++ b/tools/perf/util/mem-events.h @@ -6,7 +6,6 @@ #include struct perf_mem_event { - bool record; bool supported; bool ldlat; u32 aux_event; @@ -28,6 +27,7 @@ struct perf_pmu; extern unsigned int perf_mem_events__loads_ldlat; extern struct perf_mem_event perf_mem_events[PERF_MEM_EVENTS__MAX]; +extern bool perf_mem_record[PERF_MEM_EVENTS__MAX]; int perf_pmu__mem_events_parse(struct perf_pmu *pmu, const char *str); int perf_pmu__mem_events_init(void); From 003265bb6f028d7bcd7cbd92d6ba2b4e26382796 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Thu, 5 Sep 2024 10:07:37 -0700 Subject: [PATCH 303/347] perf mem: Fix the wrong reference in parse_record_events() A segmentation fault can be triggered when running 'perf mem record -e ldlat-loads' The commit 35b38a71c92fa033 ("perf mem: Rework command option handling") moves the OPT_CALLBACK of event from __cmd_record() to cmd_mem(). When invoking the __cmd_record(), the 'mem' has been referenced (&). So the &mem passed into the parse_record_events() is a double reference (&&) of the original struct perf_mem mem. But in the cmd_mem(), the &mem is the single reference (&) of the original struct perf_mem mem. Fixes: 35b38a71c92fa033 ("perf mem: Rework command option handling") Signed-off-by: Kan Liang Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240905170737.4070743-3-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-mem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-mem.c b/tools/perf/builtin-mem.c index 615e905450b3..651188c1d825 100644 --- a/tools/perf/builtin-mem.c +++ b/tools/perf/builtin-mem.c @@ -47,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(); From c1632cc5ed386cc8d85693ef578a106f4fb1932c Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 6 Sep 2024 15:37:43 -0300 Subject: [PATCH 304/347] perf trace augmented_syscalls.bpf: Move the renameat aumenter to renameat2, temporarily While trying to shape Howard Chu's generic BPF augmenter transition into the codebase I got stuck with the renameat2 syscall. Until I noticed that the attempt at reusing augmenters were making it use the 'openat' syscall augmenter, that collect just one string syscall arg, for the 'renameat2' syscall, that takes two strings. So, for the moment, just to help in this transition period, since 'renameat2' is what is used these days in the 'mv' utility, just make the BPF collector be associated with the more widely used syscall, hopefully the transition to Howard's generic BPF augmenter will cure this, so get this out of the way for now! So now we still have that odd "reuse", but for something we're not testing so won't get in the way anymore: root@number:~# rm -f 987654 ; touch 123456 ; perf trace -vv -e rename* mv 123456 987654 |& grep renameat Reusing "openat" BPF sys_enter augmenter for "renameat" 0.000 ( 0.079 ms): mv/1158612 renameat2(olddfd: CWD, oldname: "123456", newdfd: CWD, newname: "987654", flags: NOREPLACE) = 0 root@number:~# Reviewed-by: Ian Rogers Cc: Adrian Hunter Cc: Howard Chu Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/CAP-5=fXjGYs=tpBgETK-P9U-CuXssytk9pSnTXpfphrmmOydWA@mail.gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c index 0acbd74e8c76..0f9bd2690d4e 100644 --- a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c +++ b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c @@ -260,8 +260,8 @@ int sys_enter_rename(struct syscall_enter_args *args) return augmented__output(args, augmented_args, len); } -SEC("tp/syscalls/sys_enter_renameat") -int sys_enter_renameat(struct syscall_enter_args *args) +SEC("tp/syscalls/sys_enter_renameat2") +int sys_enter_renameat2(struct syscall_enter_args *args) { struct augmented_args_payload *augmented_args = augmented_args_payload(); const void *oldpath_arg = (const void *)args->args[1], From c90a88d33a23a8b3c58ee0e1d18d7392244b9b03 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Fri, 6 Sep 2024 16:26:45 -0300 Subject: [PATCH 305/347] perf trace: Use a common encoding for augmented arguments, with size + error + payload We were using a more compact format, without explicitely encoding the size and possible error in the payload for an argument. To do it generically, at least as Howard Chu did in his GSoC activities, it is more convenient to use the same model that was being used for string arguments, passing { size, error, payload }. So use that for the non string syscall args we have so far: struct timespec struct perf_event_attr struct sockaddr (this one has even a variable size) With this in place we have the userspace pretty printers: perf_event_attr___scnprintf() syscall_arg__scnprintf_augmented_sockaddr() syscall_arg__scnprintf_augmented_timespec() Ready to have the generic BPF collector in tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c sending its generic payload and thus we'll use them instead of a generic libbpf btf_dump interface that doesn't know about about the sockaddr mux, perf_event_attr non-trivial fields (sample_type, etc), leaving it as a (useful) fallback that prints just basic types until we put in place a more sophisticated pretty printer infrastructure that associates synthesized enums to struct fields using the header scrapers we have in tools/perf/trace/beauty/, some of them in this list: $ ls tools/perf/trace/beauty/*.sh tools/perf/trace/beauty/arch_errno_names.sh tools/perf/trace/beauty/kcmp_type.sh tools/perf/trace/beauty/perf_ioctl.sh tools/perf/trace/beauty/statx_mask.sh tools/perf/trace/beauty/clone.sh tools/perf/trace/beauty/kvm_ioctl.sh tools/perf/trace/beauty/pkey_alloc_access_rights.sh tools/perf/trace/beauty/sync_file_range.sh tools/perf/trace/beauty/drm_ioctl.sh tools/perf/trace/beauty/madvise_behavior.sh tools/perf/trace/beauty/prctl_option.sh tools/perf/trace/beauty/usbdevfs_ioctl.sh tools/perf/trace/beauty/fadvise.sh tools/perf/trace/beauty/mmap_flags.sh tools/perf/trace/beauty/rename_flags.sh tools/perf/trace/beauty/vhost_virtio_ioctl.sh tools/perf/trace/beauty/fs_at_flags.sh tools/perf/trace/beauty/mmap_prot.sh tools/perf/trace/beauty/sndrv_ctl_ioctl.sh tools/perf/trace/beauty/x86_arch_prctl.sh tools/perf/trace/beauty/fsconfig.sh tools/perf/trace/beauty/mount_flags.sh tools/perf/trace/beauty/sndrv_pcm_ioctl.sh tools/perf/trace/beauty/fsmount.sh tools/perf/trace/beauty/move_mount_flags.sh tools/perf/trace/beauty/sockaddr.sh tools/perf/trace/beauty/fspick.sh tools/perf/trace/beauty/mremap_flags.sh tools/perf/trace/beauty/socket.sh $ Testing it: root@number:~# rm -f 987654 ; touch 123456 ; perf trace -e rename* mv 123456 987654 0.000 ( 0.031 ms): mv/1193096 renameat2(olddfd: CWD, oldname: "123456", newdfd: CWD, newname: "987654", flags: NOREPLACE) = 0 root@number:~# perf trace -e *nanosleep sleep 1.2345678901 0.000 (1234.654 ms): sleep/1192697 clock_nanosleep(rqtp: { .tv_sec: 1, .tv_nsec: 234567891 }, rmtp: 0x7ffe1ea80460) = 0 root@number:~# perf trace -e perf_event_open* perf stat -e cpu-clock sleep 1 0.000 ( 0.011 ms): perf/1192701 perf_event_open(attr_uptr: { type: 1 (software), size: 136, config: 0 (PERF_COUNT_SW_CPU_CLOCK), sample_type: IDENTIFIER, read_format: TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING, disabled: 1, inherit: 1, enable_on_exec: 1, exclude_guest: 1 }, pid: 1192702 (perf), cpu: -1, group_fd: -1, flags: FD_CLOEXEC) = 3 Performance counter stats for 'sleep 1': 0.51 msec cpu-clock # 0.001 CPUs utilized 1.001242090 seconds time elapsed 0.000000000 seconds user 0.001010000 seconds sys root@number:~# perf trace -e connect* ping -c 1 bsky.app 0.000 ( 0.130 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: LOCAL, path: /run/systemd/resolve/io.systemd.Resolve }, addrlen: 42) = 0 23.907 ( 0.006 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: INET, port: 0, addr: 3.20.108.158 }, addrlen: 16) = 0 23.915 PING bsky.app (3.20.108.158) 56(84) bytes of data. ( 0.001 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: UNSPEC }, addrlen: 16) = 0 23.917 ( 0.002 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: INET, port: 0, addr: 3.12.170.30 }, addrlen: 16) = 0 23.921 ( 0.001 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: UNSPEC }, addrlen: 16) = 0 23.923 ( 0.001 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: INET, port: 0, addr: 18.217.70.179 }, addrlen: 16) = 0 23.925 ( 0.001 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: UNSPEC }, addrlen: 16) = 0 23.927 ( 0.001 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: INET, port: 0, addr: 3.132.20.46 }, addrlen: 16) = 0 23.930 ( 0.001 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: UNSPEC }, addrlen: 16) = 0 23.931 ( 0.001 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: INET, port: 0, addr: 3.142.89.165 }, addrlen: 16) = 0 23.934 ( 0.001 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: UNSPEC }, addrlen: 16) = 0 23.935 ( 0.002 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: INET, port: 0, addr: 18.119.147.159 }, addrlen: 16) = 0 23.938 ( 0.001 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: UNSPEC }, addrlen: 16) = 0 23.940 ( 0.001 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: INET, port: 0, addr: 3.22.38.164 }, addrlen: 16) = 0 23.942 ( 0.001 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: UNSPEC }, addrlen: 16) = 0 23.944 ( 0.001 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: INET, port: 0, addr: 3.13.14.133 }, addrlen: 16) = 0 23.956 ( 0.001 ms): ping/1192740 connect(fd: 5, uservaddr: { .family: INET, port: 1025, addr: 3.20.108.158 }, addrlen: 16) = 0 ^C --- bsky.app ping statistics --- 1 packets transmitted, 0 received, 100% packet loss, time 0ms root@number:~# Reviewed-by: Ian Rogers Cc: Adrian Hunter Cc: Howard Chu Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/CAP-5=fW4=2GoP6foAN6qbrCiUzy0a_TzHbd8rvDsakTPfdzvfg@mail.gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/trace/beauty/perf_event_open.c | 2 +- tools/perf/trace/beauty/sockaddr.c | 2 +- tools/perf/trace/beauty/timespec.c | 2 +- .../bpf_skel/augmented_raw_syscalls.bpf.c | 104 +++++++++++------- 4 files changed, 66 insertions(+), 44 deletions(-) diff --git a/tools/perf/trace/beauty/perf_event_open.c b/tools/perf/trace/beauty/perf_event_open.c index 01ee15fe9d0c..632237128640 100644 --- a/tools/perf/trace/beauty/perf_event_open.c +++ b/tools/perf/trace/beauty/perf_event_open.c @@ -76,7 +76,7 @@ static size_t perf_event_attr___scnprintf(struct perf_event_attr *attr, char *bf static size_t syscall_arg__scnprintf_augmented_perf_event_attr(struct syscall_arg *arg, char *bf, size_t size) { - return perf_event_attr___scnprintf((void *)arg->augmented.args, bf, size, arg->trace->show_zeros); + return perf_event_attr___scnprintf((void *)arg->augmented.args->value, bf, size, arg->trace->show_zeros); } static size_t syscall_arg__scnprintf_perf_event_attr(char *bf, size_t size, struct syscall_arg *arg) diff --git a/tools/perf/trace/beauty/sockaddr.c b/tools/perf/trace/beauty/sockaddr.c index 2e0e867c0c1b..a17a27ac2a6f 100644 --- a/tools/perf/trace/beauty/sockaddr.c +++ b/tools/perf/trace/beauty/sockaddr.c @@ -47,7 +47,7 @@ static size_t (*af_scnprintfs[])(struct sockaddr *sa, char *bf, size_t size) = { static size_t syscall_arg__scnprintf_augmented_sockaddr(struct syscall_arg *arg, char *bf, size_t size) { - struct sockaddr *sa = (struct sockaddr *)arg->augmented.args; + struct sockaddr *sa = (struct sockaddr *)&arg->augmented.args->value; char family[32]; size_t printed; diff --git a/tools/perf/trace/beauty/timespec.c b/tools/perf/trace/beauty/timespec.c index e1a61f092aad..b14ab72a2738 100644 --- a/tools/perf/trace/beauty/timespec.c +++ b/tools/perf/trace/beauty/timespec.c @@ -7,7 +7,7 @@ static size_t syscall_arg__scnprintf_augmented_timespec(struct syscall_arg *arg, char *bf, size_t size) { - struct timespec *ts = (struct timespec *)arg->augmented.args; + struct timespec *ts = (struct timespec *)arg->augmented.args->value; return scnprintf(bf, size, "{ .tv_sec: %" PRIu64 ", .tv_nsec: %" PRIu64 " }", ts->tv_sec, ts->tv_nsec); } diff --git a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c index 0f9bd2690d4e..9c7d2f855294 100644 --- a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c +++ b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c @@ -10,6 +10,9 @@ #include #include +#define PERF_ALIGN(x, a) __PERF_ALIGN_MASK(x, (typeof(x))(a)-1) +#define __PERF_ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) + /** * is_power_of_2() - check if a value is a power of two * @n: the value to check @@ -66,19 +69,6 @@ struct syscall_exit_args { long ret; }; -struct augmented_arg { - unsigned int size; - int err; - char value[PATH_MAX]; -}; - -struct pids_filtered { - __uint(type, BPF_MAP_TYPE_HASH); - __type(key, pid_t); - __type(value, bool); - __uint(max_entries, 64); -} pids_filtered SEC(".maps"); - /* * Desired design of maximum size and alignment (see RFC2553) */ @@ -105,17 +95,27 @@ struct sockaddr_storage { }; }; -struct augmented_args_payload { - struct syscall_enter_args args; - union { - struct { - struct augmented_arg arg, arg2; - }; +struct augmented_arg { + unsigned int size; + int err; + union { + char value[PATH_MAX]; struct sockaddr_storage saddr; - char __data[sizeof(struct augmented_arg)]; }; }; +struct pids_filtered { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, pid_t); + __type(value, bool); + __uint(max_entries, 64); +} pids_filtered SEC(".maps"); + +struct augmented_args_payload { + struct syscall_enter_args args; + struct augmented_arg arg, arg2; // We have to reserve space for two arguments (rename, etc) +}; + // We need more tmp space than the BPF stack can give us struct augmented_args_tmp { __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); @@ -182,15 +182,17 @@ int sys_enter_connect(struct syscall_enter_args *args) struct augmented_args_payload *augmented_args = augmented_args_payload(); const void *sockaddr_arg = (const void *)args->args[1]; unsigned int socklen = args->args[2]; - unsigned int len = sizeof(augmented_args->args); + unsigned int len = sizeof(u64) + sizeof(augmented_args->args); // the size + err in all 'augmented_arg' structs if (augmented_args == NULL) return 1; /* Failure: don't filter */ - _Static_assert(is_power_of_2(sizeof(augmented_args->saddr)), "sizeof(augmented_args->saddr) needs to be a power of two"); - socklen &= sizeof(augmented_args->saddr) - 1; + _Static_assert(is_power_of_2(sizeof(augmented_args->arg.saddr)), "sizeof(augmented_args->arg.saddr) needs to be a power of two"); + socklen &= sizeof(augmented_args->arg.saddr) - 1; - bpf_probe_read_user(&augmented_args->saddr, socklen, sockaddr_arg); + bpf_probe_read_user(&augmented_args->arg.saddr, socklen, sockaddr_arg); + augmented_args->arg.size = socklen; + augmented_args->arg.err = 0; return augmented__output(args, augmented_args, len + socklen); } @@ -201,14 +203,14 @@ int sys_enter_sendto(struct syscall_enter_args *args) struct augmented_args_payload *augmented_args = augmented_args_payload(); const void *sockaddr_arg = (const void *)args->args[4]; unsigned int socklen = args->args[5]; - unsigned int len = sizeof(augmented_args->args); + unsigned int len = sizeof(u64) + sizeof(augmented_args->args); // the size + err in all 'augmented_arg' structs if (augmented_args == NULL) return 1; /* Failure: don't filter */ - socklen &= sizeof(augmented_args->saddr) - 1; + socklen &= sizeof(augmented_args->arg.saddr) - 1; - bpf_probe_read_user(&augmented_args->saddr, socklen, sockaddr_arg); + bpf_probe_read_user(&augmented_args->arg.saddr, socklen, sockaddr_arg); return augmented__output(args, augmented_args, len + socklen); } @@ -249,13 +251,23 @@ int sys_enter_rename(struct syscall_enter_args *args) struct augmented_args_payload *augmented_args = augmented_args_payload(); const void *oldpath_arg = (const void *)args->args[0], *newpath_arg = (const void *)args->args[1]; - unsigned int len = sizeof(augmented_args->args), oldpath_len; + unsigned int len = sizeof(augmented_args->args), oldpath_len, newpath_len; if (augmented_args == NULL) return 1; /* Failure: don't filter */ + len += 2 * sizeof(u64); // The overhead of size and err, just before the payload... + oldpath_len = augmented_arg__read_str(&augmented_args->arg, oldpath_arg, sizeof(augmented_args->arg.value)); - len += oldpath_len + augmented_arg__read_str((void *)(&augmented_args->arg) + oldpath_len, newpath_arg, sizeof(augmented_args->arg.value)); + augmented_args->arg.size = PERF_ALIGN(oldpath_len + 1, sizeof(u64)); + len += augmented_args->arg.size; + + struct augmented_arg *arg2 = (void *)&augmented_args->arg.value + augmented_args->arg.size; + + newpath_len = augmented_arg__read_str(arg2, newpath_arg, sizeof(augmented_args->arg.value)); + arg2->size = newpath_len; + + len += newpath_len; return augmented__output(args, augmented_args, len); } @@ -266,13 +278,23 @@ int sys_enter_renameat2(struct syscall_enter_args *args) struct augmented_args_payload *augmented_args = augmented_args_payload(); const void *oldpath_arg = (const void *)args->args[1], *newpath_arg = (const void *)args->args[3]; - unsigned int len = sizeof(augmented_args->args), oldpath_len; + unsigned int len = sizeof(augmented_args->args), oldpath_len, newpath_len; if (augmented_args == NULL) return 1; /* Failure: don't filter */ + len += 2 * sizeof(u64); // The overhead of size and err, just before the payload... + oldpath_len = augmented_arg__read_str(&augmented_args->arg, oldpath_arg, sizeof(augmented_args->arg.value)); - len += oldpath_len + augmented_arg__read_str((void *)(&augmented_args->arg) + oldpath_len, newpath_arg, sizeof(augmented_args->arg.value)); + augmented_args->arg.size = PERF_ALIGN(oldpath_len + 1, sizeof(u64)); + len += augmented_args->arg.size; + + struct augmented_arg *arg2 = (void *)&augmented_args->arg.value + augmented_args->arg.size; + + newpath_len = augmented_arg__read_str(arg2, newpath_arg, sizeof(augmented_args->arg.value)); + arg2->size = newpath_len; + + len += newpath_len; return augmented__output(args, augmented_args, len); } @@ -293,26 +315,26 @@ int sys_enter_perf_event_open(struct syscall_enter_args *args) { struct augmented_args_payload *augmented_args = augmented_args_payload(); const struct perf_event_attr_size *attr = (const struct perf_event_attr_size *)args->args[0], *attr_read; - unsigned int len = sizeof(augmented_args->args); + unsigned int len = sizeof(u64) + sizeof(augmented_args->args); // the size + err in all 'augmented_arg' structs if (augmented_args == NULL) goto failure; - if (bpf_probe_read_user(&augmented_args->__data, sizeof(*attr), attr) < 0) + if (bpf_probe_read_user(&augmented_args->arg.value, sizeof(*attr), attr) < 0) goto failure; - attr_read = (const struct perf_event_attr_size *)augmented_args->__data; + attr_read = (const struct perf_event_attr_size *)augmented_args->arg.value; __u32 size = attr_read->size; if (!size) size = PERF_ATTR_SIZE_VER0; - if (size > sizeof(augmented_args->__data)) + if (size > sizeof(augmented_args->arg.value)) goto failure; // Now that we read attr->size and tested it against the size limits, read it completely - if (bpf_probe_read_user(&augmented_args->__data, size, attr) < 0) + if (bpf_probe_read_user(&augmented_args->arg.value, size, attr) < 0) goto failure; return augmented__output(args, augmented_args, len + size); @@ -325,16 +347,16 @@ int sys_enter_clock_nanosleep(struct syscall_enter_args *args) { struct augmented_args_payload *augmented_args = augmented_args_payload(); const void *rqtp_arg = (const void *)args->args[2]; - unsigned int len = sizeof(augmented_args->args); + unsigned int len = sizeof(u64) + sizeof(augmented_args->args); // the size + err in all 'augmented_arg' structs __u32 size = sizeof(struct timespec64); if (augmented_args == NULL) goto failure; - if (size > sizeof(augmented_args->__data)) + if (size > sizeof(augmented_args->arg.value)) goto failure; - bpf_probe_read_user(&augmented_args->__data, size, rqtp_arg); + bpf_probe_read_user(&augmented_args->arg.value, size, rqtp_arg); return augmented__output(args, augmented_args, len + size); failure: @@ -352,10 +374,10 @@ int sys_enter_nanosleep(struct syscall_enter_args *args) if (augmented_args == NULL) goto failure; - if (size > sizeof(augmented_args->__data)) + if (size > sizeof(augmented_args->arg.value)) goto failure; - bpf_probe_read_user(&augmented_args->__data, size, req_arg); + bpf_probe_read_user(&augmented_args->arg.value, size, req_arg); return augmented__output(args, augmented_args, len + size); failure: From 2f2e439ba56f45b9d8aff300b39fa0bfa49ec2d8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 9 Sep 2024 16:29:44 -0300 Subject: [PATCH 306/347] perf trace: Mark which syscall arguments go from user space to kernel space We need to know where to collect it in the BPF augmenters, if in the sys_enter hook or in the sys_exit hook. Start with the SCA_FILENAME one, that is just from user to kernel space. The alternative, better, but takes a bit more time than I have now, is to use the __user information that is already in the syscall args and encoded in BTF via a tag, do it later. Cc: Adrian Hunter Cc: Howard Chu Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 41 +++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 53690bbb143e..64f0aefeba3b 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -102,6 +102,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); @@ -110,6 +116,7 @@ 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; @@ -851,6 +858,11 @@ 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_pipe_flags(char *bf, size_t size, struct syscall_arg *arg) { @@ -1091,11 +1103,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", @@ -1117,7 +1129,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", @@ -1181,20 +1193,20 @@ 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", @@ -1203,7 +1215,7 @@ static const struct syscall_fmt syscall_fmts[] = { .arg = { [0] = { .scnprintf = SCA_TIMESPEC, /* 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 */ }, }, }, @@ -1299,9 +1311,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", @@ -1311,11 +1323,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 */ }, }, }, @@ -1903,9 +1915,10 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field 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")) + arg->from_user = true; + } 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; From 690eda6508c23023586ab81b11cae86476754d0b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 9 Sep 2024 16:57:22 -0300 Subject: [PATCH 307/347] perf trace: Introduce SCA_PERF_ATTR_FROM_USER() to set .from_user = true Paving the way for the generic BPF BTF based syscall arg augmenter. Cc: Adrian Hunter Cc: Howard Chu Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 2 +- tools/perf/trace/beauty/perf_event_open.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 64f0aefeba3b..5f3146651684 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1226,7 +1226,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 */ }, }, }, diff --git a/tools/perf/trace/beauty/perf_event_open.c b/tools/perf/trace/beauty/perf_event_open.c index 632237128640..9f1ed989c775 100644 --- a/tools/perf/trace/beauty/perf_event_open.c +++ b/tools/perf/trace/beauty/perf_event_open.c @@ -88,3 +88,7 @@ static size_t syscall_arg__scnprintf_perf_event_attr(char *bf, size_t size, stru } #define SCA_PERF_ATTR syscall_arg__scnprintf_perf_event_attr +// 'argname' is just documentational at this point, to remove the previous comment with that info +#define SCA_PERF_ATTR_FROM_USER(argname) \ + { .scnprintf = SCA_PERF_ATTR, \ + .from_user = true, } From be14a71984e16b95ff725dbda19899868c5ec8a6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 9 Sep 2024 16:57:22 -0300 Subject: [PATCH 308/347] perf trace: Introduce SCA_SOCKADDR_FROM_USER() to set .from_user = true Paving the way for the generic BPF BTF based syscall arg augmenter. Cc: Adrian Hunter Cc: Howard Chu Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 6 +++--- tools/perf/trace/beauty/beauty.h | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 5f3146651684..64c3f7f7a9aa 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1075,7 +1075,7 @@ 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), }, }, @@ -1095,7 +1095,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), }, }, @@ -1289,7 +1289,7 @@ 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_tid_address", .errpid = true, }, { .name = "setitimer", .arg = { [0] = STRARRAY(which, itimers), }, }, diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index 3ed11e18ee2d..9c6bfb7d0ef1 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h @@ -229,6 +229,11 @@ size_t syscall_arg__scnprintf_renameat2_flags(char *bf, size_t size, struct sysc size_t syscall_arg__scnprintf_sockaddr(char *bf, size_t size, struct syscall_arg *arg); #define SCA_SOCKADDR syscall_arg__scnprintf_sockaddr +// 'argname' is just documentational at this point, to remove the previous comment with that info +#define SCA_SOCKADDR_FROM_USER(argname) \ + { .scnprintf = SCA_SOCKADDR, \ + .from_user = true, } + size_t syscall_arg__scnprintf_socket_protocol(char *bf, size_t size, struct syscall_arg *arg); #define SCA_SK_PROTO syscall_arg__scnprintf_socket_protocol From c790f2bafb7a17d97c49c17607fa2ff919891f51 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Mon, 9 Sep 2024 16:57:22 -0300 Subject: [PATCH 309/347] perf trace: Introduce SCA_TIMESPEC_FROM_USER() to set .from_user = true Paving the way for the generic BPF BTF based syscall arg augmenter. Cc: Adrian Hunter Cc: Howard Chu Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 4 ++-- tools/perf/trace/beauty/beauty.h | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 64c3f7f7a9aa..2bdbb6813512 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1084,7 +1084,7 @@ static const struct syscall_fmt syscall_fmts[] = { { .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, }, @@ -1212,7 +1212,7 @@ static const struct syscall_fmt syscall_fmts[] = { { .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] = SCA_FILENAME_FROM_USER(pathname), diff --git a/tools/perf/trace/beauty/beauty.h b/tools/perf/trace/beauty/beauty.h index 9c6bfb7d0ef1..0a07ad158f87 100644 --- a/tools/perf/trace/beauty/beauty.h +++ b/tools/perf/trace/beauty/beauty.h @@ -255,6 +255,11 @@ size_t syscall_arg__scnprintf_sync_file_range_flags(char *bf, size_t size, struc size_t syscall_arg__scnprintf_timespec(char *bf, size_t size, struct syscall_arg *arg); #define SCA_TIMESPEC syscall_arg__scnprintf_timespec +// 'argname' is just documentational at this point, to remove the previous comment with that info +#define SCA_TIMESPEC_FROM_USER(argname) \ + { .scnprintf = SCA_TIMESPEC, \ + .from_user = true, } + size_t open__scnprintf_flags(unsigned long flags, char *bf, size_t size, bool show_prefix); void syscall_arg__set_ret_scnprintf(struct syscall_arg *arg, From d92f490cba69fbab495ac21eefff01130be3ec31 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Sep 2024 09:50:29 -0300 Subject: [PATCH 310/347] perf trace: Mark bpf's attr as from_user This one has no specific pretty printer right now, so will be handled by the generic BTF based one later in this patch series. Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 2bdbb6813512..72f61e928e2f 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1078,7 +1078,8 @@ static const struct syscall_fmt syscall_fmts[] = { [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", From 7f403067288f05335f7d8b8283ae1dac6cb1ab31 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sun, 25 Aug 2024 00:33:16 +0800 Subject: [PATCH 311/347] perf trace: Add trace__bpf_sys_enter_beauty_map() to prepare for fetching data in BPF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set up beauty_map, load it to BPF, in such format: if argument No.3 is a struct of size 32 bytes (of syscall number 114) beauty_map[114][2] = 32; if argument No.3 is a string (of syscall number 114) beauty_map[114][2] = 1; if argument No.3 is a buffer, its size is indicated by argument No.4 (of syscall number 114) beauty_map[114][2] = -4; /* -1 ~ -6, we'll read this buffer size in BPF */ Committer notes: Moved syscall_arg_fmt__cache_btf_struct() from a ifdef HAVE_LIBBPF_SUPPORT to closer to where it is used, that is ifdef'ed on HAVE_BPF_SKEL and thus breaks the build when building with BUILD_BPF_SKEL=0, as detected using 'make -C tools/perf build-test'. Also add 'struct beauty_map_enter' to tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c as we're using it in this patch, otherwise we get this while trying to build at this point in the original patch series: builtin-trace.c: In function ‘trace__init_syscalls_bpf_prog_array_maps’: builtin-trace.c:3725:58: error: ‘struct ’ has no member named ‘beauty_map_enter’ 3725 | int beauty_map_fd = bpf_map__fd(trace->skel->maps.beauty_map_enter); | We also have to take into account syscall_arg_fmt.from_user when telling the kernel what to copy in the sys_enter generic collector, we don't want to collect bogus data in buffers that will only be available to us at sys_exit time, i.e. after the kernel has filled it, so leave this for when we have such a sys_exit based collector. Committer testing: Not wired up yet, so all continues to work, using the existing BPF collector and userspace beautifiers that are augmentation aware: root@number:~# rm -f 987654 ; touch 123456 ; perf trace -e rename* mv 123456 987654 0.000 ( 0.031 ms): mv/20888 renameat2(olddfd: CWD, oldname: "123456", newdfd: CWD, newname: "987654", flags: NOREPLACE) = 0 root@number:~# perf trace -e connect,sendto ping -c 1 www.google.com 0.000 ( 0.014 ms): ping/20892 connect(fd: 5, uservaddr: { .family: LOCAL, path: /run/systemd/resolve/io.systemd.Resolve }, addrlen: 42) = 0 0.040 ( 0.003 ms): ping/20892 sendto(fd: 5, buff: 0x560b4ff17980, len: 97, flags: DONTWAIT|NOSIGNAL) = 97 0.480 ( 0.017 ms): ping/20892 sendto(fd: 5, buff: 0x7ffd82d07150, len: 20, addr: { .family: NETLINK }, addr_len: 0xc) = 20 0.526 ( 0.014 ms): ping/20892 connect(fd: 5, uservaddr: { .family: INET6, port: 0, addr: 2800:3f0:4004:810::2004 }, addrlen: 28) = 0 0.542 ( 0.002 ms): ping/20892 connect(fd: 5, uservaddr: { .family: UNSPEC }, addrlen: 16) = 0 0.544 ( 0.004 ms): ping/20892 connect(fd: 5, uservaddr: { .family: INET, port: 0, addr: 142.251.135.100 }, addrlen: 16) = 0 0.559 ( 0.002 ms): ping/20892 connect(fd: 5, uservaddr: { .family: INET, port: 1025, addr: 142.251.135.100 }, addrlen: 16PING www.google.com (142.251.135.100) 56(84) bytes of data. ) = 0 0.589 ( 0.058 ms): ping/20892 sendto(fd: 3, buff: 0x560b4ff11ac0, len: 64, addr: { .family: INET, port: 0, addr: 142.251.135.100 }, addr_len: 0x10) = 64 45.250 ( 0.029 ms): ping/20892 connect(fd: 5, uservaddr: { .family: LOCAL, path: /run/systemd/resolve/io.systemd.Resolve }, addrlen: 42) = 0 45.344 ( 0.012 ms): ping/20892 sendto(fd: 5, buff: 0x560b4ff19340, len: 111, flags: DONTWAIT|NOSIGNAL) = 111 64 bytes from rio09s08-in-f4.1e100.net (142.251.135.100): icmp_seq=1 ttl=49 time=44.4 ms --- www.google.com ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 44.361/44.361/44.361/0.000 ms root@number:~# Signed-off-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240815013626.935097-4-howardchu95@gmail.com Link: https://lore.kernel.org/r/20240824163322.60796-3-howardchu95@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 110 ++++++++++++++++++ .../bpf_skel/augmented_raw_syscalls.bpf.c | 7 ++ 2 files changed, 117 insertions(+) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 72f61e928e2f..c672702fc0cf 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -120,6 +120,7 @@ struct syscall_arg_fmt { bool show_zero; #ifdef HAVE_LIBBPF_SUPPORT const struct btf_type *type; + int type_id; /* used in btf_dump */ #endif }; @@ -3462,6 +3463,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; @@ -3537,6 +3555,87 @@ 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) { + struct_offset = strstr(field->type, "struct "); + + // XXX We're only collecting pointer payloads _from_ user space + if (!sc->arg_fmt[i].from_user) + continue; + + if (field->flags & TEP_FIELD_IS_POINTER && struct_offset) { /* struct */ + struct_offset += 7; + + /* 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; @@ -3641,7 +3740,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); @@ -3660,6 +3761,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; } /* diff --git a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c index 9c7d2f855294..4ebce6763743 100644 --- a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c +++ b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c @@ -124,6 +124,13 @@ struct augmented_args_tmp { __uint(max_entries, 1); } augmented_args_tmp SEC(".maps"); +struct beauty_map_enter { + __uint(type, BPF_MAP_TYPE_HASH); + __type(key, int); + __type(value, __u32[6]); + __uint(max_entries, 512); +} beauty_map_enter SEC(".maps"); + static inline struct augmented_args_payload *augmented_args_payload(void) { int key = 0; From cb32035214b9a09df29680a6a4da64d34579bb8f Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sun, 25 Aug 2024 00:33:18 +0800 Subject: [PATCH 312/347] perf trace: Pretty print struct data Change the arg->augmented.args to arg->augmented.args->value to skip the header for customized pretty printers, since we collect data in BPF using the general augment_sys_enter(), which always adds the header. Use btf_dump API to pretty print augmented struct pointer. Prefer existed pretty-printer than btf general pretty-printer. set compact = true and skip_names = true, so that no newline character and argument name are printed. Committer notes: Simplified the btf_dump_snprintf callback to avoid using multiple buffers, as discussed in the thread accessible via the Link tag below. Also made it do: dump_data_opts.skip_names = !arg->trace->show_arg_names; I.e. show the type and struct field names according to that tunable, we probably need another tunable just for this, but for now if the user wants to see syscall names in addition to its value, it makes sense to see the struct field names according to that tunable. Committer testing: The following have explicitely set beautifiers (SCA_FILENAME, SCA_SOCKADDR and SCA_PERF_ATTR), SCA_FILENAME is here just because we have been wiring up the "renameat2" ("renameat" until recently), so it doesn't use the introduced generic fallback (btf_struct_scnprintf(), see the definition of SCA_PERF_ATTR, SCA_SOCKADDR to see the more feature rich beautifiers, that are not using BTF): root@number:~# rm -f 987654 ; touch 123456 ; perf trace -e rename* mv 123456 987654 0.000 ( 0.039 ms): mv/258478 renameat2(olddfd: CWD, oldname: "123456", newdfd: CWD, newname: "987654", flags: NOREPLACE) = 0 root@number:~# perf trace -e connect,sendto ping -c 1 www.google.com 0.000 ( 0.014 ms): ping/258481 connect(fd: 5, uservaddr: { .family: LOCAL, path: /run/systemd/resolve/io.systemd.Resolve }, addrlen: 42) = 0 0.040 ( 0.003 ms): ping/258481 sendto(fd: 5, buff: 0x55bc317a6980, len: 97, flags: DONTWAIT|NOSIGNAL) = 97 18.742 ( 0.020 ms): ping/258481 sendto(fd: 5, buff: 0x7ffc04768df0, len: 20, addr: { .family: NETLINK }, addr_len: 0xc) = 20 PING www.google.com (142.251.129.68) 56(84) bytes of data. 18.783 ( 0.012 ms): ping/258481 connect(fd: 5, uservaddr: { .family: INET6, port: 0, addr: 2800:3f0:4004:810::2004 }, addrlen: 28) = 0 18.797 ( 0.001 ms): ping/258481 connect(fd: 5, uservaddr: { .family: UNSPEC }, addrlen: 16) = 0 18.800 ( 0.004 ms): ping/258481 connect(fd: 5, uservaddr: { .family: INET, port: 0, addr: 142.251.129.68 }, addrlen: 16) = 0 18.815 ( 0.002 ms): ping/258481 connect(fd: 5, uservaddr: { .family: INET, port: 1025, addr: 142.251.129.68 }, addrlen: 16) = 0 18.862 ( 0.023 ms): ping/258481 sendto(fd: 3, buff: 0x55bc317a0ac0, len: 64, addr: { .family: INET, port: 0, addr: 142.251.129.68 }, addr_len: 0x10) = 64 63.330 ( 0.038 ms): ping/258481 connect(fd: 5, uservaddr: { .family: LOCAL, path: /run/systemd/resolve/io.systemd.Resolve }, addrlen: 42) = 0 63.435 ( 0.010 ms): ping/258481 sendto(fd: 5, buff: 0x55bc317a8340, len: 110, flags: DONTWAIT|NOSIGNAL) = 110 64 bytes from rio07s07-in-f4.1e100.net (142.251.129.68): icmp_seq=1 ttl=49 time=44.2 ms --- www.google.com ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 44.158/44.158/44.158/0.000 ms root@number:~# perf trace -e perf_event_open perf stat -e instructions,cache-misses,syscalls:sys_enter*sleep* sleep 1.23456789 0.000 ( 0.010 ms): :258487/258487 perf_event_open(attr_uptr: { type: 0 (PERF_TYPE_HARDWARE), config: 0xa00000000, disabled: 1, { bp_len, config2 }: 0x900000000, branch_sample_type: USER|COUNTERS, sample_regs_user: 0x3f1b7ffffffff, sample_stack_user: 258487, clockid: -599052088, sample_regs_intr: 0x60a000003eb, sample_max_stack: 14, sig_data: 120259084288 }, cpu: -1, group_fd: -1, flags: FD_CLOEXEC) = 3 0.016 ( 0.002 ms): :258487/258487 perf_event_open(attr_uptr: { type: 0 (PERF_TYPE_HARDWARE), config: 0x400000000, disabled: 1, { bp_len, config2 }: 0x900000000, branch_sample_type: USER|COUNTERS, sample_regs_user: 0x3f1b7ffffffff, sample_stack_user: 258487, clockid: -599044082, sample_regs_intr: 0x60a000003eb, sample_max_stack: 14, sig_data: 120259084288 }, cpu: -1, group_fd: -1, flags: FD_CLOEXEC) = 4 1.838 ( 0.006 ms): perf/258487 perf_event_open(attr_uptr: { type: 0 (PERF_TYPE_HARDWARE), size: 136, config: 0xa00000001, sample_type: IDENTIFIER, read_format: TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING, disabled: 1, inherit: 1, enable_on_exec: 1, exclude_guest: 1 }, pid: 258488 (perf), cpu: -1, group_fd: -1, flags: FD_CLOEXEC) = 5 1.846 ( 0.002 ms): perf/258487 perf_event_open(attr_uptr: { type: 0 (PERF_TYPE_HARDWARE), size: 136, config: 0x400000001, sample_type: IDENTIFIER, read_format: TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING, disabled: 1, inherit: 1, enable_on_exec: 1, exclude_guest: 1 }, pid: 258488 (perf), cpu: -1, group_fd: -1, flags: FD_CLOEXEC) = 6 1.849 ( 0.002 ms): perf/258487 perf_event_open(attr_uptr: { type: 0 (PERF_TYPE_HARDWARE), size: 136, config: 0xa00000003, sample_type: IDENTIFIER, read_format: TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING, disabled: 1, inherit: 1, enable_on_exec: 1, exclude_guest: 1 }, pid: 258488 (perf), cpu: -1, group_fd: -1, flags: FD_CLOEXEC) = 7 1.851 ( 0.002 ms): perf/258487 perf_event_open(attr_uptr: { type: 0 (PERF_TYPE_HARDWARE), size: 136, config: 0x400000003, sample_type: IDENTIFIER, read_format: TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING, disabled: 1, inherit: 1, enable_on_exec: 1, exclude_guest: 1 }, pid: 258488 (perf), cpu: -1, group_fd: -1, flags: FD_CLOEXEC) = 9 1.853 ( 0.600 ms): perf/258487 perf_event_open(attr_uptr: { type: 2 (tracepoint), size: 136, config: 0x190 (syscalls:sys_enter_nanosleep), sample_type: IDENTIFIER, read_format: TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING, disabled: 1, inherit: 1, enable_on_exec: 1, exclude_guest: 1 }, pid: 258488 (perf), cpu: -1, group_fd: -1, flags: FD_CLOEXEC) = 10 2.456 ( 0.016 ms): perf/258487 perf_event_open(attr_uptr: { type: 2 (tracepoint), size: 136, config: 0x196 (syscalls:sys_enter_clock_nanosleep), sample_type: IDENTIFIER, read_format: TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING, disabled: 1, inherit: 1, enable_on_exec: 1, exclude_guest: 1 }, pid: 258488 (perf), cpu: -1, group_fd: -1, flags: FD_CLOEXEC) = 11 Performance counter stats for 'sleep 1.23456789': 1,402,839 cpu_atom/instructions/ cpu_core/instructions/ (0.00%) 11,066 cpu_atom/cache-misses/ cpu_core/cache-misses/ (0.00%) 0 syscalls:sys_enter_nanosleep 1 syscalls:sys_enter_clock_nanosleep 1.236246714 seconds time elapsed 0.000000000 seconds user 0.001308000 seconds sys root@number:~# Now if we use it even for the ones we have a specific beautifier in tools/perf/trace/beauty, i.e. use btf_struct_scnprintf() for all structs, by adding the following patch: @@ -2316,7 +2316,7 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, default_scnprintf = sc->arg_fmt[arg.idx].scnprintf; - if (default_scnprintf == NULL || default_scnprintf == SCA_PTR) { + if (1 || (default_scnprintf == NULL || default_scnprintf == SCA_PTR)) { btf_printed = trace__btf_scnprintf(trace, &arg, bf + printed, size - printed, val, field->type); if (btf_printed) { We get: root@number:~# perf trace -e connect,sendto ping -c 1 www.google.com PING www.google.com (142.251.129.68) 56(84) bytes of data. 0.000 ( 0.015 ms): ping/283259 connect(fd: 5, uservaddr: (struct sockaddr){.sa_family = (sa_family_t)1,(union){.sa_data_min = (char[14])['/','r','u','n','/','s','y','s','t','e','m','d','/','r',],},}, addrlen: 42) = 0 0.046 ( 0.004 ms): ping/283259 sendto(fd: 5, buff: 0x559b008ae980, len: 97, flags: DONTWAIT|NOSIGNAL) = 97 0.353 ( 0.012 ms): ping/283259 sendto(fd: 5, buff: 0x7ffc01294960, len: 20, addr: (struct sockaddr){.sa_family = (sa_family_t)16,}, addr_len: 0xc) = 20 0.377 ( 0.006 ms): ping/283259 connect(fd: 5, uservaddr: (struct sockaddr){.sa_family = (sa_family_t)2,}, addrlen: 16) = 0 0.388 ( 0.010 ms): ping/283259 connect(fd: 5, uservaddr: (struct sockaddr){.sa_family = (sa_family_t)10,}, addrlen: 28) = 0 0.402 ( 0.001 ms): ping/283259 connect(fd: 5, uservaddr: (struct sockaddr){.sa_family = (sa_family_t)2,(union){.sa_data_min = (char[14])[4,1,142,251,129,'D',],},}, addrlen: 16) = 0 0.425 ( 0.045 ms): ping/283259 sendto(fd: 3, buff: 0x559b008a8ac0, len: 64, addr: (struct sockaddr){.sa_family = (sa_family_t)2,}, addr_len: 0x10) = 64 64 bytes from rio07s07-in-f4.1e100.net (142.251.129.68): icmp_seq=1 ttl=49 time=44.1 ms --- www.google.com ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 44.113/44.113/44.113/0.000 ms 44.849 ( 0.038 ms): ping/283259 connect(fd: 5, uservaddr: (struct sockaddr){.sa_family = (sa_family_t)1,(union){.sa_data_min = (char[14])['/','r','u','n','/','s','y','s','t','e','m','d','/','r',],},}, addrlen: 42) = 0 44.927 ( 0.006 ms): ping/283259 sendto(fd: 5, buff: 0x559b008b03d0, len: 110, flags: DONTWAIT|NOSIGNAL) = 110 root@number:~# Which looks sane, i.e.: 18.800 ( 0.004 ms): ping/258481 connect(fd: 5, uservaddr: { .family: INET, port: 0, addr: 142.251.129.68 }, addrlen: 16) = 0 Becomes: 0.402 ( 0.001 ms): ping/283259 connect(fd: 5, uservaddr: (struct sockaddr){.sa_family = (sa_family_t)2,(union){.sa_data_min = (char[14])[4,1,142,251,129,'D',],},}, addrlen: 16) = 0 And. #define AF_UNIX 1 /* Unix domain sockets */ #define AF_LOCAL 1 /* POSIX name for AF_UNIX */ #define AF_INET 2 /* Internet IP Protocol */ #define AF_INET6 10 /* IP version 6 */ And 'D' == 68, so the preexisting sockaddr BPF collector is working with the new generic BTF pretty printer (btf_struct_scnprintf()), its just that it doesn't know about 'struct sockaddr' besides what is in BTF, i.e. its an array of bytes, not an IPv4 address that needs extra massaging. Ditto for the 'struct perf_event_attr' case: 1.851 ( 0.002 ms): perf/258487 perf_event_open(attr_uptr: { type: 0 (PERF_TYPE_HARDWARE), size: 136, config: 0x400000003, sample_type: IDENTIFIER, read_format: TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING, disabled: 1, inherit: 1, enable_on_exec: 1, exclude_guest: 1 }, pid: 258488 (perf), cpu: -1, group_fd: -1, flags: FD_CLOEXEC) = 9 Becomes: 2.081 ( 0.002 ms): :283304/283304 perf_event_open(attr_uptr: (struct perf_event_attr){.size = (__u32)136,.config = (__u64)17179869187,.sample_type = (__u64)65536,.read_format = (__u64)3,.disabled = (__u64)0x1,.inherit = (__u64)0x1,.enable_on_exec = (__u64)0x1,.exclude_guest = (__u64)0x1,}, pid: 283305 (sleep), cpu: -1, group_fd: -1, flags: FD_CLOEXEC) = 9 hex(17179869187) = 0x400000003, etc. read_format: TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING is enum perf_event_read_format { PERF_FORMAT_TOTAL_TIME_ENABLED = 1U << 0, PERF_FORMAT_TOTAL_TIME_RUNNING = 1U << 1, and so on. We need to work with the libbpf btf dump api to get one output that matches the 'perf trace'/strace expectations/format, but having this in this current form is already an improvement to 'perf trace', so lets improve from what we have. Signed-off-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240815013626.935097-7-howardchu95@gmail.com Link: https://lore.kernel.org/r/20240824163322.60796-5-howardchu95@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 65 +++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index c672702fc0cf..41d36a351bfd 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1002,6 +1002,54 @@ static size_t btf_enum_scnprintf(const struct btf_type *type, struct btf *btf, c 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) { @@ -1021,6 +1069,8 @@ static size_t trace__btf_scnprintf(struct trace *trace, struct syscall_arg *arg, 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)) + return btf_struct_scnprintf(arg_fmt->type, trace->btf, bf, size, arg); return 0; } @@ -2236,6 +2286,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 @@ -2277,11 +2328,15 @@ 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); - btf_printed = trace__btf_scnprintf(trace, &arg, bf + printed, - size - printed, val, field->type); - if (btf_printed) { - printed += btf_printed; - continue; + default_scnprintf = sc->arg_fmt[arg.idx].scnprintf; + + if (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], From b257fac12f38d7f503b932313d704cee21092350 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sun, 25 Aug 2024 00:33:19 +0800 Subject: [PATCH 313/347] perf trace: Pretty print buffer data Define TRACE_AUG_MAX_BUF in trace_augment.h data, which is the maximum buffer size we can augment. BPF will include this header too. Print buffer in a way that's different than just printing a string, we print all the control characters in \digits (such as \0 for null, and \10 for newline, LF). For character that has a bigger value than 127, we print the digits instead of the character itself as well. Committer notes: Simplified the buffer scnprintf to avoid using multiple buffers as discussed in the patch review thread. We can't really all 'buf' args to SCA_BUF as we're collecting so far just on the sys_enter path, so we would be printing the previous 'read' arg buffer contents, not what the kernel puts there. So instead of: static int syscall_fmt__cmp(const void *name, const void *fmtp) @@ -1987,8 +1989,6 @@ syscall_arg_fmt__init_array(struct syscall_arg_fmt *arg, struct tep_format_field - else if (strstr(field->type, "char *") && strstr(field->name, "buf")) - arg->scnprintf = SCA_BUF; Do: static const struct syscall_fmt syscall_fmts[] = { + { .name = "write", .errpid = true, + .arg = { [1] = { .scnprintf = SCA_BUF /* buf */, from_user = true, }, }, }, Signed-off-by: Howard Chu Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240815013626.935097-8-howardchu95@gmail.com Link: https://lore.kernel.org/r/20240824163322.60796-6-howardchu95@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 33 +++++++++++++++++++++++++++++++++ tools/perf/util/trace_augment.h | 6 ++++++ 2 files changed, 39 insertions(+) create mode 100644 tools/perf/util/trace_augment.h diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 41d36a351bfd..dab6d905015d 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -65,6 +65,7 @@ #include "syscalltbl.h" #include "rb_resort.h" #include "../perf.h" +#include "trace_augment.h" #include #include @@ -864,6 +865,10 @@ static size_t syscall_arg__scnprintf_filename(char *bf, size_t size, { .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) { @@ -1387,6 +1392,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) @@ -1758,6 +1765,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); diff --git a/tools/perf/util/trace_augment.h b/tools/perf/util/trace_augment.h new file mode 100644 index 000000000000..57a3e5045937 --- /dev/null +++ b/tools/perf/util/trace_augment.h @@ -0,0 +1,6 @@ +#ifndef TRACE_AUGMENT_H +#define TRACE_AUGMENT_H + +#define TRACE_AUG_MAX_BUF 32 /* for buffer augmentation in perf trace */ + +#endif From a68fd6a6cdd309bb51c72df4a39f49aa408728d0 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sun, 25 Aug 2024 00:33:20 +0800 Subject: [PATCH 314/347] perf trace: Collect augmented data using BPF Include trace_augment.h for TRACE_AUG_MAX_BUF, so that BPF reads TRACE_AUG_MAX_BUF bytes of buffer maximum. Determine what type of argument and how many bytes to read from user space, us ing the value in the beauty_map. This is the relation of parameter type and its corres ponding value in the beauty map, and how many bytes we read eventually: string: 1 -> size of string (till null) struct: size of struct -> size of struct buffer: -1 * (index of paired len) -> value of paired len (maximum: TRACE_AUG_ MAX_BUF) After reading from user space, we output the augmented data using bpf_perf_event_output(). If the struct augmenter, augment_sys_enter() failed, we fall back to using bpf_tail_call(). I have to make the payload 6 times the size of augmented_arg, to pass the BPF verifier. Signed-off-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240815013626.935097-10-howardchu95@gmail.com Link: https://lore.kernel.org/r/20240824163322.60796-7-howardchu95@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- .../bpf_skel/augmented_raw_syscalls.bpf.c | 107 +++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c index 4ebce6763743..b2f17cca014b 100644 --- a/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c +++ b/tools/perf/util/bpf_skel/augmented_raw_syscalls.bpf.c @@ -7,6 +7,8 @@ */ #include "vmlinux.h" +#include "../trace_augment.h" + #include #include @@ -131,6 +133,18 @@ struct beauty_map_enter { __uint(max_entries, 512); } beauty_map_enter SEC(".maps"); +struct beauty_payload_enter { + struct syscall_enter_args args; + struct augmented_arg aug_args[6]; +}; + +struct beauty_payload_enter_map { + __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); + __type(key, int); + __type(value, struct beauty_payload_enter); + __uint(max_entries, 1); +} beauty_payload_enter_map SEC(".maps"); + static inline struct augmented_args_payload *augmented_args_payload(void) { int key = 0; @@ -143,6 +157,11 @@ static inline int augmented__output(void *ctx, struct augmented_args_payload *ar return bpf_perf_event_output(ctx, &__augmented_syscalls__, BPF_F_CURRENT_CPU, args, len); } +static inline int augmented__beauty_output(void *ctx, void *data, int len) +{ + return bpf_perf_event_output(ctx, &__augmented_syscalls__, BPF_F_CURRENT_CPU, data, len); +} + static inline unsigned int augmented_arg__read_str(struct augmented_arg *augmented_arg, const void *arg, unsigned int arg_len) { @@ -401,6 +420,91 @@ static bool pid_filter__has(struct pids_filtered *pids, pid_t pid) return bpf_map_lookup_elem(pids, &pid) != NULL; } +static int augment_sys_enter(void *ctx, struct syscall_enter_args *args) +{ + bool augmented, do_output = false; + int zero = 0, size, aug_size, index, output = 0, + value_size = sizeof(struct augmented_arg) - offsetof(struct augmented_arg, value); + unsigned int nr, *beauty_map; + struct beauty_payload_enter *payload; + void *arg, *payload_offset; + + /* fall back to do predefined tail call */ + if (args == NULL) + return 1; + + /* use syscall number to get beauty_map entry */ + nr = (__u32)args->syscall_nr; + beauty_map = bpf_map_lookup_elem(&beauty_map_enter, &nr); + + /* set up payload for output */ + payload = bpf_map_lookup_elem(&beauty_payload_enter_map, &zero); + payload_offset = (void *)&payload->aug_args; + + if (beauty_map == NULL || payload == NULL) + return 1; + + /* copy the sys_enter header, which has the syscall_nr */ + __builtin_memcpy(&payload->args, args, sizeof(struct syscall_enter_args)); + + /* + * Determine what type of argument and how many bytes to read from user space, using the + * value in the beauty_map. This is the relation of parameter type and its corresponding + * value in the beauty map, and how many bytes we read eventually: + * + * string: 1 -> size of string + * struct: size of struct -> size of struct + * buffer: -1 * (index of paired len) -> value of paired len (maximum: TRACE_AUG_MAX_BUF) + */ + for (int i = 0; i < 6; i++) { + arg = (void *)args->args[i]; + augmented = false; + size = beauty_map[i]; + aug_size = size; /* size of the augmented data read from user space */ + + if (size == 0 || arg == NULL) + continue; + + if (size == 1) { /* string */ + aug_size = bpf_probe_read_user_str(((struct augmented_arg *)payload_offset)->value, value_size, arg); + /* minimum of 0 to pass the verifier */ + if (aug_size < 0) + aug_size = 0; + + augmented = true; + } else if (size > 0 && size <= value_size) { /* struct */ + if (!bpf_probe_read_user(((struct augmented_arg *)payload_offset)->value, size, arg)) + augmented = true; + } else if (size < 0 && size >= -6) { /* buffer */ + index = -(size + 1); + aug_size = args->args[index]; + + if (aug_size > TRACE_AUG_MAX_BUF) + aug_size = TRACE_AUG_MAX_BUF; + + if (aug_size > 0) { + if (!bpf_probe_read_user(((struct augmented_arg *)payload_offset)->value, aug_size, arg)) + augmented = true; + } + } + + /* write data to payload */ + if (augmented) { + int written = offsetof(struct augmented_arg, value) + aug_size; + + ((struct augmented_arg *)payload_offset)->size = aug_size; + output += written; + payload_offset += written; + do_output = true; + } + } + + if (!do_output) + return 1; + + return augmented__beauty_output(ctx, payload, sizeof(struct syscall_enter_args) + output); +} + SEC("tp/raw_syscalls/sys_enter") int sys_enter(struct syscall_enter_args *args) { @@ -429,7 +533,8 @@ int sys_enter(struct syscall_enter_args *args) * "!raw_syscalls:unaugmented" that will just return 1 to return the * unaugmented tracepoint payload. */ - bpf_tail_call(args, &syscalls_sys_enter, augmented_args->args.syscall_nr); + if (augment_sys_enter(args, &augmented_args->args)) + bpf_tail_call(args, &syscalls_sys_enter, augmented_args->args.syscall_nr); // If not found on the PROG_ARRAY syscalls map, then we're filtering it: return 0; From 3278024540e882b21e915afb507522e8e01ca160 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Sun, 25 Aug 2024 00:33:21 +0800 Subject: [PATCH 315/347] perf trace: Add --force-btf for debugging If --force-btf is enabled, prefer btf_dump general pretty printer to perf trace's customized pretty printers. Mostly for debug purposes. Committer testing: diff before/after shows we need several improvements to be able to compare the changes, first we need to cut off/disable mutable data such as pids and timestamps, then what is left are the buffer addresses passed from userspace, returned from kernel space, maybe we can ask 'perf trace' to go on making those reproducible. That would entail a Pointer Address Translation (PAT) like for networking, that would, for simple, reproducible if not for these details, workloads, that we would then use in our regression tests. Enough digression, this is one such diff: openat(dfd: CWD, filename: "/usr/share/locale/locale.alias", flags: RDONLY|CLOEXEC) = 3 -fstat(fd: 3, statbuf: 0x7fff01f212a0) = 0 -read(fd: 3, buf: 0x5596bab2d630, count: 4096) = 2998 -read(fd: 3, buf: 0x5596bab2d630, count: 4096) = 0 +fstat(fd: 3, statbuf: 0x7ffc163cf0e0) = 0 +read(fd: 3, buf: 0x55b4e0631630, count: 4096) = 2998 +read(fd: 3, buf: 0x55b4e0631630, count: 4096) = 0 close(fd: 3) = 0 openat(dfd: CWD, filename: "/usr/share/locale/en_US.UTF-8/LC_MESSAGES/coreutils.mo") = -1 ENOENT (No such file or directory) openat(dfd: CWD, filename: "/usr/share/locale/en_US.utf8/LC_MESSAGES/coreutils.mo") = -1 ENOENT (No such file or directory) @@ -45,7 +45,7 @@ openat(dfd: CWD, filename: "/usr/share/locale/en.UTF-8/LC_MESSAGES/coreutils.mo") = -1 ENOENT (No such file or directory) openat(dfd: CWD, filename: "/usr/share/locale/en.utf8/LC_MESSAGES/coreutils.mo") = -1 ENOENT (No such file or directory) openat(dfd: CWD, filename: "/usr/share/locale/en/LC_MESSAGES/coreutils.mo") = -1 ENOENT (No such file or directory) -{ .tv_sec: 1, .tv_nsec: 0 }, rmtp: 0x7fff01f21990) = 0 +(struct __kernel_timespec){.tv_sec = (__kernel_time64_t)1,}, rmtp: 0x7ffc163cf7d0) = The problem more close to our hands is to make the libbpf BTF pretty printer to have a mode that closely resembles what we're trying to resemble: strace output. Being able to run something with 'perf trace' and with 'strace' and get the exact same output should be of interest of anybody wanting to have strace and 'perf trace' regression tested against each other. That last part is 'perf trace' shot at being something so useful as strace... ;-) Suggested-by: Arnaldo Carvalho de Melo Signed-off-by: Howard Chu Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240824163322.60796-8-howardchu95@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index dab6d905015d..c47fde936c33 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -212,6 +212,7 @@ struct trace { bool show_string_prefix; bool force; bool vfs_getname; + bool force_btf; int trace_pgfaults; char *perfconfig_events; struct { @@ -2363,7 +2364,9 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, default_scnprintf = sc->arg_fmt[arg.idx].scnprintf; - if (default_scnprintf == NULL || default_scnprintf == SCA_PTR) { + if (trace->force_btf || + (default_scnprintf == NULL || + (default_scnprintf == SCA_PTR && strstr(field->type, "struct")))) { btf_printed = trace__btf_scnprintf(trace, &arg, bf + printed, size - printed, val, field->type); if (btf_printed) { @@ -5171,6 +5174,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() }; From f3f16112c65f5923f41d9d7222fe7e4f34bf73ad Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Sep 2024 10:17:21 -0300 Subject: [PATCH 316/347] perf trace: Support collecting 'union's with the BPF augmenter And reuse the BTF based struct pretty printer, with that we can offer initial support for the 'bpf' syscall's second argument, a 'union bpf_attr' pointer. But this is not that satisfactory as the libbpf btf dumper will pretty print _all_ the union, we need to have a way to say that the first arg selects the type for the union member to be pretty printed, something like what pahole does translating the PERF_RECORD_ selector into a name, and using that name to find a matching struct. In the case of 'union bpf_attr' it would map PROG_LOAD to one of the union members, but unfortunately there is no such mapping: root@number:~# pahole bpf_attr union bpf_attr { struct { __u32 map_type; /* 0 4 */ __u32 key_size; /* 4 4 */ __u32 value_size; /* 8 4 */ __u32 max_entries; /* 12 4 */ __u32 map_flags; /* 16 4 */ __u32 inner_map_fd; /* 20 4 */ __u32 numa_node; /* 24 4 */ char map_name[16]; /* 28 16 */ __u32 map_ifindex; /* 44 4 */ __u32 btf_fd; /* 48 4 */ __u32 btf_key_type_id; /* 52 4 */ __u32 btf_value_type_id; /* 56 4 */ __u32 btf_vmlinux_value_type_id; /* 60 4 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u64 map_extra; /* 64 8 */ __s32 value_type_btf_obj_fd; /* 72 4 */ __s32 map_token_fd; /* 76 4 */ }; /* 0 80 */ struct { __u32 map_fd; /* 0 4 */ /* XXX 4 bytes hole, try to pack */ __u64 key; /* 8 8 */ union { __u64 value; /* 16 8 */ __u64 next_key; /* 16 8 */ }; /* 16 8 */ __u64 flags; /* 24 8 */ }; /* 0 32 */ struct { __u64 in_batch; /* 0 8 */ __u64 out_batch; /* 8 8 */ __u64 keys; /* 16 8 */ __u64 values; /* 24 8 */ __u32 count; /* 32 4 */ __u32 map_fd; /* 36 4 */ __u64 elem_flags; /* 40 8 */ __u64 flags; /* 48 8 */ } batch; /* 0 56 */ struct { __u32 prog_type; /* 0 4 */ __u32 insn_cnt; /* 4 4 */ __u64 insns; /* 8 8 */ __u64 license; /* 16 8 */ __u32 log_level; /* 24 4 */ __u32 log_size; /* 28 4 */ __u64 log_buf; /* 32 8 */ __u32 kern_version; /* 40 4 */ __u32 prog_flags; /* 44 4 */ char prog_name[16]; /* 48 16 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 prog_ifindex; /* 64 4 */ __u32 expected_attach_type; /* 68 4 */ __u32 prog_btf_fd; /* 72 4 */ __u32 func_info_rec_size; /* 76 4 */ __u64 func_info; /* 80 8 */ __u32 func_info_cnt; /* 88 4 */ __u32 line_info_rec_size; /* 92 4 */ __u64 line_info; /* 96 8 */ __u32 line_info_cnt; /* 104 4 */ __u32 attach_btf_id; /* 108 4 */ union { __u32 attach_prog_fd; /* 112 4 */ __u32 attach_btf_obj_fd; /* 112 4 */ }; /* 112 4 */ __u32 core_relo_cnt; /* 116 4 */ __u64 fd_array; /* 120 8 */ /* --- cacheline 2 boundary (128 bytes) --- */ __u64 core_relos; /* 128 8 */ __u32 core_relo_rec_size; /* 136 4 */ __u32 log_true_size; /* 140 4 */ __s32 prog_token_fd; /* 144 4 */ }; /* 0 152 */ struct { __u64 pathname; /* 0 8 */ __u32 bpf_fd; /* 8 4 */ __u32 file_flags; /* 12 4 */ __s32 path_fd; /* 16 4 */ }; /* 0 24 */ struct { union { __u32 target_fd; /* 0 4 */ __u32 target_ifindex; /* 0 4 */ }; /* 0 4 */ __u32 attach_bpf_fd; /* 4 4 */ __u32 attach_type; /* 8 4 */ __u32 attach_flags; /* 12 4 */ __u32 replace_bpf_fd; /* 16 4 */ union { __u32 relative_fd; /* 20 4 */ __u32 relative_id; /* 20 4 */ }; /* 20 4 */ __u64 expected_revision; /* 24 8 */ }; /* 0 32 */ struct { __u32 prog_fd; /* 0 4 */ __u32 retval; /* 4 4 */ __u32 data_size_in; /* 8 4 */ __u32 data_size_out; /* 12 4 */ __u64 data_in; /* 16 8 */ __u64 data_out; /* 24 8 */ __u32 repeat; /* 32 4 */ __u32 duration; /* 36 4 */ __u32 ctx_size_in; /* 40 4 */ __u32 ctx_size_out; /* 44 4 */ __u64 ctx_in; /* 48 8 */ __u64 ctx_out; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ __u32 flags; /* 64 4 */ __u32 cpu; /* 68 4 */ __u32 batch_size; /* 72 4 */ } test; /* 0 80 */ struct { union { __u32 start_id; /* 0 4 */ __u32 prog_id; /* 0 4 */ __u32 map_id; /* 0 4 */ __u32 btf_id; /* 0 4 */ __u32 link_id; /* 0 4 */ }; /* 0 4 */ __u32 next_id; /* 4 4 */ __u32 open_flags; /* 8 4 */ }; /* 0 12 */ struct { __u32 bpf_fd; /* 0 4 */ __u32 info_len; /* 4 4 */ __u64 info; /* 8 8 */ } info; /* 0 16 */ struct { union { __u32 target_fd; /* 0 4 */ __u32 target_ifindex; /* 0 4 */ }; /* 0 4 */ __u32 attach_type; /* 4 4 */ __u32 query_flags; /* 8 4 */ __u32 attach_flags; /* 12 4 */ __u64 prog_ids; /* 16 8 */ union { __u32 prog_cnt; /* 24 4 */ __u32 count; /* 24 4 */ }; /* 24 4 */ /* XXX 4 bytes hole, try to pack */ __u64 prog_attach_flags; /* 32 8 */ __u64 link_ids; /* 40 8 */ __u64 link_attach_flags; /* 48 8 */ __u64 revision; /* 56 8 */ } query; /* 0 64 */ struct { __u64 name; /* 0 8 */ __u32 prog_fd; /* 8 4 */ /* XXX 4 bytes hole, try to pack */ __u64 cookie; /* 16 8 */ } raw_tracepoint; /* 0 24 */ struct { __u64 btf; /* 0 8 */ __u64 btf_log_buf; /* 8 8 */ __u32 btf_size; /* 16 4 */ __u32 btf_log_size; /* 20 4 */ __u32 btf_log_level; /* 24 4 */ __u32 btf_log_true_size; /* 28 4 */ __u32 btf_flags; /* 32 4 */ __s32 btf_token_fd; /* 36 4 */ }; /* 0 40 */ struct { __u32 pid; /* 0 4 */ __u32 fd; /* 4 4 */ __u32 flags; /* 8 4 */ __u32 buf_len; /* 12 4 */ __u64 buf; /* 16 8 */ __u32 prog_id; /* 24 4 */ __u32 fd_type; /* 28 4 */ __u64 probe_offset; /* 32 8 */ __u64 probe_addr; /* 40 8 */ } task_fd_query; /* 0 48 */ struct { union { __u32 prog_fd; /* 0 4 */ __u32 map_fd; /* 0 4 */ }; /* 0 4 */ union { __u32 target_fd; /* 4 4 */ __u32 target_ifindex; /* 4 4 */ }; /* 4 4 */ __u32 attach_type; /* 8 4 */ __u32 flags; /* 12 4 */ union { __u32 target_btf_id; /* 16 4 */ struct { __u64 iter_info; /* 16 8 */ __u32 iter_info_len; /* 24 4 */ }; /* 16 16 */ struct { __u64 bpf_cookie; /* 16 8 */ } perf_event; /* 16 8 */ struct { __u32 flags; /* 16 4 */ __u32 cnt; /* 20 4 */ __u64 syms; /* 24 8 */ __u64 addrs; /* 32 8 */ __u64 cookies; /* 40 8 */ } kprobe_multi; /* 16 32 */ struct { __u32 target_btf_id; /* 16 4 */ /* XXX 4 bytes hole, try to pack */ __u64 cookie; /* 24 8 */ } tracing; /* 16 16 */ struct { __u32 pf; /* 16 4 */ __u32 hooknum; /* 20 4 */ __s32 priority; /* 24 4 */ __u32 flags; /* 28 4 */ } netfilter; /* 16 16 */ struct { union { __u32 relative_fd; /* 16 4 */ __u32 relative_id; /* 16 4 */ }; /* 16 4 */ /* XXX 4 bytes hole, try to pack */ __u64 expected_revision; /* 24 8 */ } tcx; /* 16 16 */ struct { __u64 path; /* 16 8 */ __u64 offsets; /* 24 8 */ __u64 ref_ctr_offsets; /* 32 8 */ __u64 cookies; /* 40 8 */ __u32 cnt; /* 48 4 */ __u32 flags; /* 52 4 */ __u32 pid; /* 56 4 */ } uprobe_multi; /* 16 48 */ struct { union { __u32 relative_fd; /* 16 4 */ __u32 relative_id; /* 16 4 */ }; /* 16 4 */ /* XXX 4 bytes hole, try to pack */ __u64 expected_revision; /* 24 8 */ } netkit; /* 16 16 */ }; /* 16 48 */ } link_create; /* 0 64 */ struct { __u32 link_fd; /* 0 4 */ union { __u32 new_prog_fd; /* 4 4 */ __u32 new_map_fd; /* 4 4 */ }; /* 4 4 */ __u32 flags; /* 8 4 */ union { __u32 old_prog_fd; /* 12 4 */ __u32 old_map_fd; /* 12 4 */ }; /* 12 4 */ } link_update; /* 0 16 */ struct { __u32 link_fd; /* 0 4 */ } link_detach; /* 0 4 */ struct { __u32 type; /* 0 4 */ } enable_stats; /* 0 4 */ struct { __u32 link_fd; /* 0 4 */ __u32 flags; /* 4 4 */ } iter_create; /* 0 8 */ struct { __u32 prog_fd; /* 0 4 */ __u32 map_fd; /* 4 4 */ __u32 flags; /* 8 4 */ } prog_bind_map; /* 0 12 */ struct { __u32 flags; /* 0 4 */ __u32 bpffs_fd; /* 4 4 */ } token_create; /* 0 8 */ }; root@number:~# So this is one case where BTF gets us only that far, not getting all the way to automate the pretty printing of unions designed like 'union bpf_attr', we will need a custom pretty printer for this union, as using the libbpf union BTF dumper is way too verbose: root@number:~# perf trace --max-events 1 -e bpf bpftool map 0.000 ( 0.054 ms): bpftool/3409073 bpf(cmd: PROG_LOAD, uattr: (union bpf_attr){(struct){.map_type = (__u32)1,.key_size = (__u32)2,.value_size = (__u32)2755142048,.max_entries = (__u32)32764,.map_flags = (__u32)150263906,.inner_map_fd = (__u32)21920,},(struct){.map_fd = (__u32)1,.key = (__u64)140723063628192,(union){.value = (__u64)94145833392226,.next_key = (__u64)94145833392226,},},.batch = (struct){.in_batch = (__u64)8589934593,.out_batch = (__u64)140723063628192,.keys = (__u64)94145833392226,},(struct){.prog_type = (__u32)1,.insn_cnt = (__u32)2,.insns = (__u64)140723063628192,.license = (__u64)94145833392226,},(struct){.pathname = (__u64)8589934593,.bpf_fd = (__u32)2755142048,.file_flags = (__u32)32764,.path_fd = (__s32)150263906,},(struct){(union){.target_fd = (__u32)1,.target_ifindex = (__u32)1,},.attach_bpf_fd = (__u32)2,.attach_type = (__u32)2755142048,.attach_flags = (__u32)32764,.replace_bpf_fd = (__u32)150263906,(union){.relative_fd = (__u32)21920,.relative_id = (__u32)21920,},},.test = (struct){.prog_fd = (__u32)1,.retval = (__u32)2,.data_size_in = (__u32)2755142048,.data_size_out = (__u32)32764,.data_in = (__u64)94145833392226,},(struct){(union){.start_id = (__u32)1,.prog_id = (__u32)1,.map_id = (__u32)1,.btf_id = (__u32)1,.link_id = (__u32)1,},.next_id = (__u32)2,.open_flags = (__u32)2755142048,},.info = (struct){.bpf_fd = (__u32)1,.info_len = (__u32)2,.info = (__u64)140723063628192,},.query = (struct){(union){.target_fd = (__u32)1,.target_ifindex = (__u32)1,},.attach_type = (__u32)2,.query_flags = (__u32)2755142048,.attach_flags = (__u32)32764,.prog_ids = (__u64)94145833392226,},.raw_tracepoint = (struct){.name = (__u64)8589934593,.prog_fd = (__u32)2755142048,.cookie = (__u64)94145833392226,},(struct){.btf = (__u64)8589934593,.btf_log_buf = (__u64)140723063628192,.btf_size = (__u32)150263906,.btf_log_size = (__u32)21920,},.task_fd_query = (struct){.pid = (__u32)1,.fd = (__u32)2,.flags = (__u32)2755142048,.buf_len = (__u32)32764,.buf = (__u64)94145833392226,},.link_create = (struct){(union){.prog_fd = (__u32)1,.map_fd = (__u32)1,},(u) = 3 root@number:~# 2: prog_array name hid_jmp_table flags 0x0 key 4B value 4B max_entries 1024 memlock 8440B owner_prog_type tracing owner jited 13: hash_of_maps name cgroup_hash flags 0x0 key 8B value 4B max_entries 2048 memlock 167584B pids systemd(1) 960: array name libbpf_global flags 0x0 key 4B value 32B max_entries 1 memlock 280B 961: array name pid_iter.rodata flags 0x480 key 4B value 4B max_entries 1 memlock 8192B btf_id 1846 frozen pids bpftool(3409073) 962: array name libbpf_det_bind flags 0x0 key 4B value 32B max_entries 1 memlock 280B root@number:~# For simpler unions this may be better than not seeing any payload, so keep it there. Acked-by: Howard Chu Cc: Adrian Hunter Cc: Alan Maguire Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/ZuBLat8cbadILNLA@x1 [ Removed needless parenteses in the if block leading to the trace__btf_scnprintf() call, as per Howard's review comments ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index c47fde936c33..ed4c5e637e24 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1075,7 +1075,7 @@ static size_t trace__btf_scnprintf(struct trace *trace, struct syscall_arg *arg, 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)) + 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; @@ -2364,9 +2364,7 @@ static size_t syscall__scnprintf_args(struct syscall *sc, char *bf, size_t size, default_scnprintf = sc->arg_fmt[arg.idx].scnprintf; - if (trace->force_btf || - (default_scnprintf == NULL || - (default_scnprintf == SCA_PTR && strstr(field->type, "struct")))) { + 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) { @@ -3663,14 +3661,18 @@ static int trace__bpf_sys_enter_beauty_map(struct trace *trace, int key, unsigne return -1; for (i = 0, field = sc->args; field; ++i, field = field->next) { - struct_offset = strstr(field->type, "struct "); - // XXX We're only collecting pointer payloads _from_ user space if (!sc->arg_fmt[i].from_user) continue; - if (field->flags & TEP_FIELD_IS_POINTER && struct_offset) { /* struct */ - struct_offset += 7; + 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) { From 375f9262ac81f56f41dab41f089f0e43b2792ae6 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Sep 2024 10:52:27 -0300 Subject: [PATCH 317/347] perf trace: Mark the rlim arg in the prlimit64 and setrlimit syscalls as coming from user space With that it uses the generic BTF based pretty printer: root@number:~# perf trace -e prlimit64 0.000 ( 0.004 ms): :3417020/3417020 prlimit64(resource: NOFILE, old_rlim: 0x7fb8842fe3b0) = 0 0.126 ( 0.003 ms): Chroot Helper/3417022 prlimit64(resource: NOFILE, old_rlim: 0x7fb8842fdfd0) = 0 12.557 ( 0.005 ms): firefox/3417020 prlimit64(resource: STACK, old_rlim: 0x7ffe9ade1b80) = 0 26.640 ( 0.006 ms): MainThread/3417020 prlimit64(resource: STACK, old_rlim: 0x7ffe9ade1780) = 0 27.553 ( 0.002 ms): Web Content/3417020 prlimit64(resource: AS, old_rlim: 0x7ffe9ade1660) = 0 29.405 ( 0.003 ms): Web Content/3417020 prlimit64(resource: NOFILE, old_rlim: 0x7ffe9ade0c80) = 0 30.471 ( 0.002 ms): Web Content/3417020 prlimit64(resource: RTTIME, old_rlim: 0x7ffe9ade1370) = 0 30.485 ( 0.001 ms): Web Content/3417020 prlimit64(resource: RTTIME, new_rlim: (struct rlimit64){.rlim_cur = (__u64)50000,.rlim_max = (__u64)200000,}) = 0 31.779 ( 0.001 ms): Web Content/3417020 prlimit64(resource: STACK, old_rlim: 0x7ffe9ade1670) = 0 ^Croot@number:~# Better than before, still needs improvements in the configurability of the libbpf BTF dumper to get it to the strace output standard. Cc: Adrian Hunter Cc: Alan Maguire Cc: Howard Chu Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/ZuBQI-f8CGpuhIdH@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index ed4c5e637e24..838e179a2573 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1309,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 */ }, }, }, @@ -1352,7 +1353,8 @@ static const struct syscall_fmt syscall_fmts[] = { { .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", From 206dcfca1f5cfac344bd88e5ed4b6a095014b91f Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 10 Sep 2024 15:04:00 +0100 Subject: [PATCH 318/347] perf build: Autodetect minimum required llvm-dev version The new LLVM addr2line feature requires a minimum version of 13 to compile. Add a feature check for the version so that NO_LLVM=1 doesn't need to be explicitly added. Leave the existing llvm feature check intact because it's used by tools other than Perf. This fixes the following compilation error when the llvm-dev version doesn't match: util/llvm-c-helpers.cpp: In function 'char* llvm_name_for_code(dso*, const char*, u64)': util/llvm-c-helpers.cpp:178:21: error: 'std::remove_reference_t' {aka 'struct llvm::DILineInfo'} has no member named 'StartAddress' 178 | addr, res_or_err->StartAddress ? *res_or_err->StartAddress : 0); Fixes: c3f8644c21df9b7d ("perf report: Support LLVM for addr2line()") Signed-off-by: James Clark Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Bill Wendling Cc: Changbin Du Cc: Guilherme Amadio Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Justin Stitt Cc: Kan Liang Cc: Leo Yan Cc: Manu Bretelle Cc: Mark Rutland Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Nathan Chancellor Cc: Nick Desaulniers Cc: Peter Zijlstra Cc: Quentin Monnet Cc: Steinar H. Gunderson Link: https://lore.kernel.org/r/20240910140405.568791-1-james.clark@linaro.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.feature | 2 +- tools/build/feature/Makefile | 9 +++++++++ tools/build/feature/test-llvm-perf.cpp | 14 ++++++++++++++ tools/perf/Makefile.config | 6 +++--- 4 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 tools/build/feature/test-llvm-perf.cpp diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 0717e96d6a0e..427a9389e26c 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -136,7 +136,7 @@ FEATURE_DISPLAY ?= \ libunwind \ libdw-dwarf-unwind \ libcapstone \ - llvm \ + llvm-perf \ zlib \ lzma \ get_cpuid \ diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index 12796808f07a..d6a98b3854f8 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -73,6 +73,7 @@ FILES= \ test-libopencsd.bin \ test-clang.bin \ test-llvm.bin \ + test-llvm-perf.bin \ test-llvm-version.bin \ test-libaio.bin \ test-libzstd.bin \ @@ -388,6 +389,14 @@ $(OUTPUT)test-llvm.bin: $(shell $(LLVM_CONFIG) --system-libs) \ > $(@:.bin=.make.output) 2>&1 +$(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-llvm-version.bin: $(BUILDXX) -std=gnu++17 \ -I$(shell $(LLVM_CONFIG) --includedir) \ diff --git a/tools/build/feature/test-llvm-perf.cpp b/tools/build/feature/test-llvm-perf.cpp new file mode 100644 index 000000000000..a8cbb67e335e --- /dev/null +++ b/tools/build/feature/test-llvm-perf.cpp @@ -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; +} diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 7888c932b1b4..37e3eee2986e 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -981,8 +981,8 @@ ifdef BUILD_NONDISTRO endif ifndef NO_LIBLLVM - $(call feature_check,llvm) - ifeq ($(feature-llvm), 1) + $(call feature_check,llvm-perf) + ifeq ($(feature-llvm-perf), 1) CFLAGS += -DHAVE_LIBLLVM_SUPPORT CFLAGS += $(shell $(LLVM_CONFIG) --cflags) CXXFLAGS += -DHAVE_LIBLLVM_SUPPORT @@ -992,7 +992,7 @@ ifndef NO_LIBLLVM EXTLIBS += -lstdc++ $(call detected,CONFIG_LIBLLVM) else - $(warning No libllvm found, slower source file resolution, please install llvm-devel/llvm-dev) + $(warning No libllvm 13+ found, slower source file resolution, please install llvm-devel/llvm-dev) NO_LIBLLVM := 1 endif endif From 332f60ac052f5d7bfc646502f377c600986f10c2 Mon Sep 17 00:00:00 2001 From: James Clark Date: Tue, 10 Sep 2024 15:04:01 +0100 Subject: [PATCH 319/347] perf build: Remove unused feature test target llvm-version was removed in commit 56b11a2126bf ("perf bpf: Remove support for embedding clang for compiling BPF events (-e foo.c)") but some parts were left in the Makefile so finish removing them. Signed-off-by: James Clark Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Bill Wendling Cc: Changbin Du Cc: Daniel Wagner Cc: Guilherme Amadio Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Justin Stitt Cc: Kan Liang Cc: Leo Yan Cc: Manu Bretelle Cc: Mark Rutland Cc: Masami Hiramatsu Cc: Namhyung Kim Cc: Nathan Chancellor Cc: Nick Desaulniers Cc: Peter Zijlstra Cc: Quentin Monnet Cc: Steinar H. Gunderson Link: https://lore.kernel.org/r/20240910140405.568791-2-james.clark@linaro.org [ Removed one leftover, 'llvm-version' from FEATURE_TESTS_EXTRA ] Signed-off-by: Arnaldo Carvalho de Melo --- tools/build/Makefile.feature | 1 - tools/build/feature/Makefile | 6 ------ 2 files changed, 7 deletions(-) diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index 427a9389e26c..ffd117135094 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature @@ -100,7 +100,6 @@ FEATURE_TESTS_EXTRA := \ libunwind-debug-frame-aarch64 \ cxx \ llvm \ - llvm-version \ clang \ libbpf \ libbpf-btf__load_from_kernel_by_id \ diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index d6a98b3854f8..5938cf799dc6 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile @@ -74,7 +74,6 @@ FILES= \ test-clang.bin \ test-llvm.bin \ test-llvm-perf.bin \ - test-llvm-version.bin \ test-libaio.bin \ test-libzstd.bin \ test-clang-bpf-co-re.bin \ @@ -397,11 +396,6 @@ $(OUTPUT)test-llvm-perf.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) \ - > $(@:.bin=.make.output) 2>&1 - $(OUTPUT)test-clang.bin: $(BUILDXX) -std=gnu++17 \ -I$(shell $(LLVM_CONFIG) --includedir) \ From c8b9358778a82534251dc93ebc4601246d5ae04e Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Sep 2024 14:42:50 -0700 Subject: [PATCH 320/347] perf annotate: Treat 'call' instruction as stack operation I found some portion of mem-store events sampled on CALL instruction which has no memory access. But it actually saves a return address into stack. It should be considered as a stack operation like RET instruction. Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240909214251.3033827-1-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 4990c70b1794..1a347a711dcf 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -2474,6 +2474,7 @@ static bool is_stack_operation(struct arch *arch, struct disasm_line *dl) if (arch__is(arch, "x86")) { if (!strncmp(dl->ins.name, "push", 4) || !strncmp(dl->ins.name, "pop", 3) || + !strncmp(dl->ins.name, "call", 4) || !strncmp(dl->ins.name, "ret", 3)) return true; } From 02648783c2e1ed24cba23477282fa8cbc1525a77 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Mon, 9 Sep 2024 14:42:51 -0700 Subject: [PATCH 321/347] perf annotate-data: Add pr_debug_scope() The pr_debug_scope() is to print more information about the scope DIE during the instruction tracking so that it can help finding relevant debug info and the source code like inlined functions more easily. $ perf --debug type-profile annotate --data-type ... ----------------------------------------------------------- find data type for 0(reg0, reg12) at set_task_cpu+0xdd CU for kernel/sched/core.c (die:0x1268dae) frame base: cfa=1 fbreg=7 scope: [3/3] (die:12b6d28) [inlined] set_task_rq <<<--- (here) bb: [9f - dd] var [9f] reg3 type='struct task_struct*' size=0x8 (die:0x126aff0) var [9f] reg6 type='unsigned int' size=0x4 (die:0x1268e0d) Signed-off-by: Namhyung Kim Cc: Adrian Hunter Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240909214251.3033827-2-namhyung@kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/annotate-data.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c index a0ea4e07e570..976abedca09e 100644 --- a/tools/perf/util/annotate-data.c +++ b/tools/perf/util/annotate-data.c @@ -131,6 +131,26 @@ static void pr_debug_location(Dwarf_Die *die, u64 pc, int reg) } } +static void pr_debug_scope(Dwarf_Die *scope_die) +{ + int tag; + + if (!debug_type_profile && verbose < 3) + return; + + pr_info("(die:%lx) ", (long)dwarf_dieoffset(scope_die)); + + tag = dwarf_tag(scope_die); + if (tag == DW_TAG_subprogram) + pr_info("[function] %s\n", dwarf_diename(scope_die)); + else if (tag == DW_TAG_inlined_subroutine) + pr_info("[inlined] %s\n", dwarf_diename(scope_die)); + else if (tag == DW_TAG_lexical_block) + pr_info("[block]\n"); + else + pr_info("[unknown] tag=%x\n", tag); +} + bool has_reg_type(struct type_state *state, int reg) { return (unsigned)reg < ARRAY_SIZE(state->regs); @@ -1305,8 +1325,9 @@ static enum type_match_result find_data_type_block(struct data_loc_info *dloc, if (dwarf_ranges(&scopes[i], 0, &base, &start, &end) < 0) break; - pr_debug_dtp("scope: [%d/%d] (die:%lx)\n", - i + 1, nr_scopes, (long)dwarf_dieoffset(&scopes[i])); + pr_debug_dtp("scope: [%d/%d] ", i + 1, nr_scopes); + pr_debug_scope(&scopes[i]); + src_ip = map__objdump_2rip(dloc->ms->map, start); again: From ae39ba16554eb4a2d97f752847e93aa428438912 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 9 Sep 2024 13:37:37 -0700 Subject: [PATCH 322/347] perf inject: Fix build ID injection Build ID injection wasn't inserting a sample ID and aligning events to 64 bytes rather than 8. No sample ID means events are unordered and two different build_id events for the same path, as happens when a file is replaced, can't be differentiated. Add in sample ID insertion for the build_id events alongside some refactoring. The refactoring better aligns the function arguments for different use cases, such as synthesizing build_id events without needing to have a dso. The misc bits are explicitly passed as with callchains the maps/dsos may span user and kernel land, so using sample->cpumode isn't good enough. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anne Macedo Cc: Casey Chen Cc: Colin Ian King Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Peter Zijlstra Cc: Sun Haiyong Link: https://lore.kernel.org/r/20240909203740.143492-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 170 ++++++++++++++++++++++------- tools/perf/util/build-id.c | 6 +- tools/perf/util/synthetic-events.c | 44 ++++++-- tools/perf/util/synthetic-events.h | 10 +- 4 files changed, 175 insertions(+), 55 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 0ccf80fe8399..24470c57527d 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -130,6 +130,7 @@ struct perf_inject { struct perf_file_section secs[HEADER_FEAT_BITS]; struct guest_session guest_session; struct strlist *known_build_ids; + const struct evsel *mmap_evsel; }; struct event_entry { @@ -138,8 +139,13 @@ struct event_entry { union perf_event event[]; }; -static int dso__inject_build_id(struct dso *dso, const struct perf_tool *tool, - struct machine *machine, u8 cpumode, u32 flags); +static int tool__inject_build_id(const struct perf_tool *tool, + struct perf_sample *sample, + struct machine *machine, + const struct evsel *evsel, + __u16 misc, + const char *filename, + struct dso *dso, u32 flags); static int output_bytes(struct perf_inject *inject, void *buf, size_t sz) { @@ -422,6 +428,28 @@ static struct dso *findnew_dso(int pid, int tid, const char *filename, return dso; } +/* + * The evsel used for the sample ID for mmap events. Typically stashed when + * processing mmap events. If not stashed, search the evlist for the first mmap + * gathering event. + */ +static const struct evsel *inject__mmap_evsel(struct perf_inject *inject) +{ + struct evsel *pos; + + if (inject->mmap_evsel) + return inject->mmap_evsel; + + evlist__for_each_entry(inject->session->evlist, pos) { + if (pos->core.attr.mmap) { + inject->mmap_evsel = pos; + return pos; + } + } + pr_err("No mmap events found\n"); + return NULL; +} + static int perf_event__repipe_common_mmap(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, @@ -469,12 +497,28 @@ static int perf_event__repipe_common_mmap(const struct perf_tool *tool, } if (dso && !dso__hit(dso)) { - dso__set_hit(dso); - dso__inject_build_id(dso, tool, machine, sample->cpumode, flags); + struct evsel *evsel = evlist__event2evsel(inject->session->evlist, event); + + if (evsel) { + dso__set_hit(dso); + tool__inject_build_id(tool, sample, machine, evsel, + /*misc=*/sample->cpumode, + filename, dso, flags); + } } } else { + int err; + + /* + * Remember the evsel for lazy build id generation. It is used + * for the sample id header type. + */ + if (inject->build_id_style == BID_RWS__INJECT_HEADER_LAZY && + !inject->mmap_evsel) + inject->mmap_evsel = evlist__event2evsel(inject->session->evlist, event); + /* Create the thread, map, etc. Not done for the unordered inject all case. */ - int err = perf_event_process(tool, event, sample, machine); + err = perf_event_process(tool, event, sample, machine); if (err) { dso__put(dso); @@ -667,16 +711,20 @@ static bool perf_inject__lookup_known_build_id(struct perf_inject *inject, return false; } -static int dso__inject_build_id(struct dso *dso, const struct perf_tool *tool, - struct machine *machine, u8 cpumode, u32 flags) +static int tool__inject_build_id(const struct perf_tool *tool, + struct perf_sample *sample, + struct machine *machine, + const struct evsel *evsel, + __u16 misc, + const char *filename, + struct dso *dso, u32 flags) { - struct perf_inject *inject = container_of(tool, struct perf_inject, - tool); + struct perf_inject *inject = container_of(tool, struct perf_inject, tool); int err; - if (is_anon_memory(dso__long_name(dso)) || flags & MAP_HUGETLB) + if (is_anon_memory(filename) || flags & MAP_HUGETLB) return 0; - if (is_no_dso_memory(dso__long_name(dso))) + if (is_no_dso_memory(filename)) return 0; if (inject->known_build_ids != NULL && @@ -684,24 +732,65 @@ static int dso__inject_build_id(struct dso *dso, const struct perf_tool *tool, return 1; if (dso__read_build_id(dso) < 0) { - pr_debug("no build_id found for %s\n", dso__long_name(dso)); + pr_debug("no build_id found for %s\n", filename); return -1; } - err = perf_event__synthesize_build_id(tool, dso, cpumode, - perf_event__repipe, machine); + err = perf_event__synthesize_build_id(tool, sample, machine, + perf_event__repipe, + evsel, misc, dso__bid(dso), + filename); if (err) { - pr_err("Can't synthesize build_id event for %s\n", dso__long_name(dso)); + pr_err("Can't synthesize build_id event for %s\n", filename); return -1; } return 0; } +static int mark_dso_hit(const struct perf_tool *tool, + struct perf_sample *sample, + struct machine *machine, + const struct evsel *mmap_evsel, + struct map *map, bool sample_in_dso) +{ + struct dso *dso; + u16 misc = sample->cpumode; + + if (!map) + return 0; + + if (!sample_in_dso) { + u16 guest_mask = PERF_RECORD_MISC_GUEST_KERNEL | + PERF_RECORD_MISC_GUEST_USER; + + if ((misc & guest_mask) != 0) { + misc &= PERF_RECORD_MISC_HYPERVISOR; + misc |= __map__is_kernel(map) + ? PERF_RECORD_MISC_GUEST_KERNEL + : PERF_RECORD_MISC_GUEST_USER; + } else { + misc &= PERF_RECORD_MISC_HYPERVISOR; + misc |= __map__is_kernel(map) + ? PERF_RECORD_MISC_KERNEL + : PERF_RECORD_MISC_USER; + } + } + dso = map__dso(map); + if (dso && !dso__hit(dso)) { + dso__set_hit(dso); + tool__inject_build_id(tool, sample, machine, + mmap_evsel, misc, dso__long_name(dso), dso, + map__flags(map)); + } + return 0; +} + struct mark_dso_hit_args { const struct perf_tool *tool; + struct perf_sample *sample; struct machine *machine; - u8 cpumode; + const struct evsel *mmap_evsel; }; static int mark_dso_hit_callback(struct callchain_cursor_node *node, void *data) @@ -709,16 +798,8 @@ static int mark_dso_hit_callback(struct callchain_cursor_node *node, void *data) struct mark_dso_hit_args *args = data; struct map *map = node->ms.map; - if (map) { - struct dso *dso = map__dso(map); - - if (dso && !dso__hit(dso)) { - dso__set_hit(dso); - dso__inject_build_id(dso, args->tool, args->machine, - args->cpumode, map__flags(map)); - } - } - return 0; + return mark_dso_hit(args->tool, args->sample, args->machine, + args->mmap_evsel, map, /*sample_in_dso=*/false); } int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *event, @@ -728,10 +809,16 @@ int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *e { struct addr_location al; struct thread *thread; + struct perf_inject *inject = container_of(tool, struct perf_inject, tool); struct mark_dso_hit_args args = { .tool = tool, + /* + * Use the parsed sample data of the sample event, which will + * have a later timestamp than the mmap event. + */ + .sample = sample, .machine = machine, - .cpumode = sample->cpumode, + .mmap_evsel = inject__mmap_evsel(inject), }; addr_location__init(&al); @@ -743,13 +830,8 @@ int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *e } if (thread__find_map(thread, sample->cpumode, sample->ip, &al)) { - struct dso *dso = map__dso(al.map); - - if (!dso__hit(dso)) { - dso__set_hit(dso); - dso__inject_build_id(dso, tool, machine, - sample->cpumode, map__flags(al.map)); - } + mark_dso_hit(tool, sample, machine, args.mmap_evsel, al.map, + /*sample_in_dso=*/true); } sample__for_each_callchain_node(thread, evsel, sample, PERF_MAX_STACK_DEPTH, @@ -1159,17 +1241,27 @@ static int process_build_id(const struct perf_tool *tool, static int synthesize_build_id(struct perf_inject *inject, struct dso *dso, pid_t machine_pid) { struct machine *machine = perf_session__findnew_machine(inject->session, machine_pid); - u8 cpumode = dso__is_in_kernel_space(dso) ? - PERF_RECORD_MISC_GUEST_KERNEL : - PERF_RECORD_MISC_GUEST_USER; + struct perf_sample synth_sample = { + .pid = -1, + .tid = -1, + .time = -1, + .stream_id = -1, + .cpu = -1, + .period = 1, + .cpumode = dso__is_in_kernel_space(dso) + ? PERF_RECORD_MISC_GUEST_KERNEL + : PERF_RECORD_MISC_GUEST_USER, + }; if (!machine) return -ENOMEM; dso__set_hit(dso); - return perf_event__synthesize_build_id(&inject->tool, dso, cpumode, - process_build_id, machine); + return perf_event__synthesize_build_id(&inject->tool, &synth_sample, machine, + process_build_id, inject__mmap_evsel(inject), + /*misc=*/synth_sample.cpumode, + dso__bid(dso), dso__long_name(dso)); } static int guest_session__add_build_ids_cb(struct dso *dso, void *data) diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index 451d145fa4ed..8982f68e7230 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -277,8 +277,8 @@ static int write_buildid(const char *name, size_t name_len, struct build_id *bid struct perf_record_header_build_id b; size_t len; - len = name_len + 1; - len = PERF_ALIGN(len, NAME_ALIGN); + len = sizeof(b) + name_len + 1; + len = PERF_ALIGN(len, sizeof(u64)); memset(&b, 0, sizeof(b)); memcpy(&b.data, bid->data, bid->size); @@ -286,7 +286,7 @@ static int write_buildid(const char *name, size_t name_len, struct build_id *bid misc |= PERF_RECORD_MISC_BUILD_ID_SIZE; b.pid = pid; b.header.misc = misc; - b.header.size = sizeof(b) + len; + b.header.size = len; err = do_write(fd, &b, sizeof(b)); if (err < 0) diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index 0a7f93ae76fb..6bb62e4e2d5d 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -2225,28 +2225,48 @@ int perf_event__synthesize_tracing_data(const struct perf_tool *tool, int fd, st } #endif -int perf_event__synthesize_build_id(const struct perf_tool *tool, struct dso *pos, u16 misc, - perf_event__handler_t process, struct machine *machine) +int perf_event__synthesize_build_id(const struct perf_tool *tool, + struct perf_sample *sample, + struct machine *machine, + perf_event__handler_t process, + const struct evsel *evsel, + __u16 misc, + const struct build_id *bid, + const char *filename) { union perf_event ev; size_t len; - if (!dso__hit(pos)) - return 0; + len = sizeof(ev.build_id) + strlen(filename) + 1; + len = PERF_ALIGN(len, sizeof(u64)); - memset(&ev, 0, sizeof(ev)); + memset(&ev, 0, len); - len = dso__long_name_len(pos) + 1; - len = PERF_ALIGN(len, NAME_ALIGN); - ev.build_id.size = min(dso__bid(pos)->size, sizeof(dso__bid(pos)->data)); - memcpy(&ev.build_id.build_id, dso__bid(pos)->data, ev.build_id.size); + ev.build_id.size = min(bid->size, sizeof(ev.build_id.build_id)); + memcpy(ev.build_id.build_id, bid->data, ev.build_id.size); ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID; ev.build_id.header.misc = misc | PERF_RECORD_MISC_BUILD_ID_SIZE; ev.build_id.pid = machine->pid; - ev.build_id.header.size = sizeof(ev.build_id) + len; - memcpy(&ev.build_id.filename, dso__long_name(pos), dso__long_name_len(pos)); + ev.build_id.header.size = len; + strcpy(ev.build_id.filename, filename); - return process(tool, &ev, NULL, machine); + if (evsel) { + void *array = &ev; + int ret; + + array += ev.header.size; + ret = perf_event__synthesize_id_sample(array, evsel->core.attr.sample_type, sample); + if (ret < 0) + return ret; + + if (ret & 7) { + pr_err("Bad id sample size %d\n", ret); + return -EINVAL; + } + + ev.header.size += ret; + } + return process(tool, &ev, sample, machine); } int perf_event__synthesize_stat_events(struct perf_stat_config *config, const struct perf_tool *tool, diff --git a/tools/perf/util/synthetic-events.h b/tools/perf/util/synthetic-events.h index 31df7653677f..795bf3e18396 100644 --- a/tools/perf/util/synthetic-events.h +++ b/tools/perf/util/synthetic-events.h @@ -9,6 +9,7 @@ #include struct auxtrace_record; +struct build_id; struct dso; struct evlist; struct evsel; @@ -45,7 +46,14 @@ typedef int (*perf_event__handler_t)(const struct perf_tool *tool, union perf_ev int perf_event__synthesize_attrs(const struct perf_tool *tool, struct evlist *evlist, perf_event__handler_t process); int perf_event__synthesize_attr(const struct perf_tool *tool, struct perf_event_attr *attr, u32 ids, u64 *id, perf_event__handler_t process); -int perf_event__synthesize_build_id(const struct perf_tool *tool, struct dso *pos, u16 misc, perf_event__handler_t process, struct machine *machine); +int perf_event__synthesize_build_id(const struct perf_tool *tool, + struct perf_sample *sample, + struct machine *machine, + perf_event__handler_t process, + const struct evsel *evsel, + __u16 misc, + const struct build_id *bid, + const char *filename); int perf_event__synthesize_cpu_map(const struct perf_tool *tool, const struct perf_cpu_map *cpus, perf_event__handler_t process, struct machine *machine); int perf_event__synthesize_event_update_cpus(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process); int perf_event__synthesize_event_update_name(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process); From d762ba020d29ddb676805a90bf1450921817418f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 9 Sep 2024 13:37:38 -0700 Subject: [PATCH 323/347] perf inject: Add new mmap2-buildid-all option Add an option that allows all mmap or mmap2 events to be rewritten as mmap2 events with build IDs. This is similar to the existing -b/--build-ids and --buildid-all options except instead of adding a build_id event an existing mmap/mmap2 event is used as a template and a new mmap2 event synthesized from it. As mmap2 events are typical this avoids the insertion of build_id events. Add test coverage to the pipe test. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anne Macedo Cc: Casey Chen Cc: Colin Ian King Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Peter Zijlstra Cc: Sun Haiyong Link: https://lore.kernel.org/r/20240909203740.143492-3-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 88 ++++++++++++++++++++++++++++- tools/perf/tests/shell/pipe_test.sh | 1 + tools/perf/util/synthetic-events.c | 57 +++++++++++++++++++ tools/perf/util/synthetic-events.h | 11 ++++ 4 files changed, 154 insertions(+), 3 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 24470c57527d..5a27fa46e93d 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -107,6 +107,7 @@ enum build_id_rewrite_style { BID_RWS__NONE = 0, BID_RWS__INJECT_HEADER_LAZY, BID_RWS__INJECT_HEADER_ALL, + BID_RWS__MMAP2_BUILDID_ALL, }; struct perf_inject { @@ -146,6 +147,16 @@ static int tool__inject_build_id(const struct perf_tool *tool, __u16 misc, const char *filename, struct dso *dso, u32 flags); +static int tool__inject_mmap2_build_id(const struct perf_tool *tool, + struct perf_sample *sample, + struct machine *machine, + const struct evsel *evsel, + __u16 misc, + __u32 pid, __u32 tid, + __u64 start, __u64 len, __u64 pgoff, + struct dso *dso, + __u32 prot, __u32 flags, + const char *filename); static int output_bytes(struct perf_inject *inject, void *buf, size_t sz) { @@ -161,6 +172,7 @@ static int output_bytes(struct perf_inject *inject, void *buf, size_t sz) static int perf_event__repipe_synth(const struct perf_tool *tool, union perf_event *event) + { struct perf_inject *inject = container_of(tool, struct perf_inject, tool); @@ -454,7 +466,9 @@ static int perf_event__repipe_common_mmap(const struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct machine *machine, - __u32 pid, __u32 tid, __u32 flags, + __u32 pid, __u32 tid, + __u64 start, __u64 len, __u64 pgoff, + __u32 flags, __u32 prot, const char *filename, const struct dso_id *dso_id, int (*perf_event_process)(const struct perf_tool *tool, @@ -525,6 +539,26 @@ static int perf_event__repipe_common_mmap(const struct perf_tool *tool, return err; } } + if ((inject->build_id_style == BID_RWS__MMAP2_BUILDID_ALL) && + !(event->header.misc & PERF_RECORD_MISC_MMAP_BUILD_ID)) { + struct evsel *evsel = evlist__event2evsel(inject->session->evlist, event); + + if (evsel && !dso_sought) { + dso = findnew_dso(pid, tid, filename, dso_id, machine); + dso_sought = true; + } + if (evsel && dso && + !tool__inject_mmap2_build_id(tool, sample, machine, evsel, + sample->cpumode | PERF_RECORD_MISC_MMAP_BUILD_ID, + pid, tid, start, len, pgoff, + dso, + prot, flags, + filename)) { + /* Injected mmap2 so no need to repipe. */ + dso__put(dso); + return 0; + } + } dso__put(dso); return perf_event__repipe(tool, event, sample, machine); } @@ -536,7 +570,9 @@ static int perf_event__repipe_mmap(const struct perf_tool *tool, { return perf_event__repipe_common_mmap( tool, event, sample, machine, - event->mmap.pid, event->mmap.tid, /*flags=*/0, + event->mmap.pid, event->mmap.tid, + event->mmap.start, event->mmap.len, event->mmap.pgoff, + /*flags=*/0, PROT_EXEC, event->mmap.filename, /*dso_id=*/NULL, perf_event__process_mmap); } @@ -559,7 +595,9 @@ static int perf_event__repipe_mmap2(const struct perf_tool *tool, return perf_event__repipe_common_mmap( tool, event, sample, machine, - event->mmap2.pid, event->mmap2.tid, event->mmap2.flags, + event->mmap2.pid, event->mmap2.tid, + event->mmap2.start, event->mmap2.len, event->mmap2.pgoff, + event->mmap2.flags, event->mmap2.prot, event->mmap2.filename, dso_id, perf_event__process_mmap2); } @@ -748,6 +786,45 @@ static int tool__inject_build_id(const struct perf_tool *tool, return 0; } +static int tool__inject_mmap2_build_id(const struct perf_tool *tool, + struct perf_sample *sample, + struct machine *machine, + const struct evsel *evsel, + __u16 misc, + __u32 pid, __u32 tid, + __u64 start, __u64 len, __u64 pgoff, + struct dso *dso, + __u32 prot, __u32 flags, + const char *filename) +{ + int err; + + /* Return to repipe anonymous maps. */ + if (is_anon_memory(filename) || flags & MAP_HUGETLB) + return 1; + if (is_no_dso_memory(filename)) + return 1; + + if (dso__read_build_id(dso)) { + pr_debug("no build_id found for %s\n", filename); + return -1; + } + + err = perf_event__synthesize_mmap2_build_id(tool, sample, machine, + perf_event__repipe, + evsel, + misc, pid, tid, + start, len, pgoff, + dso__bid(dso), + prot, flags, + filename); + if (err) { + pr_err("Can't synthesize build_id event for %s\n", filename); + return -1; + } + return 0; +} + static int mark_dso_hit(const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine, @@ -2261,12 +2338,15 @@ int cmd_inject(int argc, const char **argv) const char *known_build_ids = NULL; bool build_ids; bool build_id_all; + bool mmap2_build_id_all; struct option options[] = { OPT_BOOLEAN('b', "build-ids", &build_ids, "Inject build-ids into the output stream"), OPT_BOOLEAN(0, "buildid-all", &build_id_all, "Inject build-ids of all DSOs into the output stream"), + OPT_BOOLEAN(0, "mmap2-buildid-all", &mmap2_build_id_all, + "Rewrite all mmap events as mmap2 events with build IDs"), OPT_STRING(0, "known-build-ids", &known_build_ids, "buildid path [,buildid path...]", "build-ids to use for given paths"), @@ -2363,6 +2443,8 @@ int cmd_inject(int argc, const char **argv) return -1; } } + if (mmap2_build_id_all) + inject.build_id_style = BID_RWS__MMAP2_BUILDID_ALL; if (build_ids) inject.build_id_style = BID_RWS__INJECT_HEADER_LAZY; if (build_id_all) diff --git a/tools/perf/tests/shell/pipe_test.sh b/tools/perf/tests/shell/pipe_test.sh index a3c94b4182c2..250574cd68b6 100755 --- a/tools/perf/tests/shell/pipe_test.sh +++ b/tools/perf/tests/shell/pipe_test.sh @@ -118,6 +118,7 @@ test_inject_bids() { test_record_report test_inject_bids -b test_inject_bids --buildid-all +test_inject_bids --mmap2-buildid-all cleanup exit $err diff --git a/tools/perf/util/synthetic-events.c b/tools/perf/util/synthetic-events.c index 6bb62e4e2d5d..a58444c4aed1 100644 --- a/tools/perf/util/synthetic-events.c +++ b/tools/perf/util/synthetic-events.c @@ -2266,6 +2266,63 @@ int perf_event__synthesize_build_id(const struct perf_tool *tool, ev.header.size += ret; } + + return process(tool, &ev, sample, machine); +} + +int perf_event__synthesize_mmap2_build_id(const struct perf_tool *tool, + struct perf_sample *sample, + struct machine *machine, + perf_event__handler_t process, + const struct evsel *evsel, + __u16 misc, + __u32 pid, __u32 tid, + __u64 start, __u64 len, __u64 pgoff, + const struct build_id *bid, + __u32 prot, __u32 flags, + const char *filename) +{ + union perf_event ev; + size_t ev_len; + void *array; + int ret; + + ev_len = sizeof(ev.mmap2) - sizeof(ev.mmap2.filename) + strlen(filename) + 1; + ev_len = PERF_ALIGN(ev_len, sizeof(u64)); + + memset(&ev, 0, ev_len); + + ev.mmap2.header.type = PERF_RECORD_MMAP2; + ev.mmap2.header.misc = misc | PERF_RECORD_MISC_MMAP_BUILD_ID; + ev.mmap2.header.size = ev_len; + + ev.mmap2.pid = pid; + ev.mmap2.tid = tid; + ev.mmap2.start = start; + ev.mmap2.len = len; + ev.mmap2.pgoff = pgoff; + + ev.mmap2.build_id_size = min(bid->size, sizeof(ev.mmap2.build_id)); + memcpy(ev.mmap2.build_id, bid->data, ev.mmap2.build_id_size); + + ev.mmap2.prot = prot; + ev.mmap2.flags = flags; + + memcpy(ev.mmap2.filename, filename, min(strlen(filename), sizeof(ev.mmap.filename))); + + array = &ev; + array += ev.header.size; + ret = perf_event__synthesize_id_sample(array, evsel->core.attr.sample_type, sample); + if (ret < 0) + return ret; + + if (ret & 7) { + pr_err("Bad id sample size %d\n", ret); + return -EINVAL; + } + + ev.header.size += ret; + return process(tool, &ev, sample, machine); } diff --git a/tools/perf/util/synthetic-events.h b/tools/perf/util/synthetic-events.h index 795bf3e18396..b9c936b5cfeb 100644 --- a/tools/perf/util/synthetic-events.h +++ b/tools/perf/util/synthetic-events.h @@ -54,6 +54,17 @@ int perf_event__synthesize_build_id(const struct perf_tool *tool, __u16 misc, const struct build_id *bid, const char *filename); +int perf_event__synthesize_mmap2_build_id(const struct perf_tool *tool, + struct perf_sample *sample, + struct machine *machine, + perf_event__handler_t process, + const struct evsel *evsel, + __u16 misc, + __u32 pid, __u32 tid, + __u64 start, __u64 len, __u64 pgoff, + const struct build_id *bid, + __u32 prot, __u32 flags, + const char *filename); int perf_event__synthesize_cpu_map(const struct perf_tool *tool, const struct perf_cpu_map *cpus, perf_event__handler_t process, struct machine *machine); int perf_event__synthesize_event_update_cpus(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process); int perf_event__synthesize_event_update_name(const struct perf_tool *tool, struct evsel *evsel, perf_event__handler_t process); From 64eed019f3fce1248cd29443c8a8635d317cb70f Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 9 Sep 2024 13:37:39 -0700 Subject: [PATCH 324/347] perf inject: Lazy build-id mmap2 event insertion Add -B option that lazily inserts mmap2 events thereby dropping all mmap events without samples. This is similar to the behavior of -b where only build_id events are inserted when a dso is accessed in a sample. File size savings can be significant in system-wide mode, consider: $ perf record -g -a -o perf.data sleep 1 $ perf inject -B -i perf.data -o perf.new.data $ ls -al perf.data perf.new.data 5147049 perf.data 2248493 perf.new.data Give test coverage of the new option in pipe test. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anne Macedo Cc: Casey Chen Cc: Colin Ian King Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Peter Zijlstra Cc: Sun Haiyong Link: https://lore.kernel.org/r/20240909203740.143492-4-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 62 +++++++++++++++++++++++------ tools/perf/tests/shell/pipe_test.sh | 1 + tools/perf/util/map.c | 1 + tools/perf/util/map.h | 11 +++++ 4 files changed, 63 insertions(+), 12 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 5a27fa46e93d..9eb72ff48d88 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -108,6 +108,7 @@ enum build_id_rewrite_style { BID_RWS__INJECT_HEADER_LAZY, BID_RWS__INJECT_HEADER_ALL, BID_RWS__MMAP2_BUILDID_ALL, + BID_RWS__MMAP2_BUILDID_LAZY, }; struct perf_inject { @@ -527,7 +528,8 @@ static int perf_event__repipe_common_mmap(const struct perf_tool *tool, * Remember the evsel for lazy build id generation. It is used * for the sample id header type. */ - if (inject->build_id_style == BID_RWS__INJECT_HEADER_LAZY && + if ((inject->build_id_style == BID_RWS__INJECT_HEADER_LAZY || + inject->build_id_style == BID_RWS__MMAP2_BUILDID_LAZY) && !inject->mmap_evsel) inject->mmap_evsel = evlist__event2evsel(inject->session->evlist, event); @@ -560,6 +562,9 @@ static int perf_event__repipe_common_mmap(const struct perf_tool *tool, } } dso__put(dso); + if (inject->build_id_style == BID_RWS__MMAP2_BUILDID_LAZY) + return 0; + return perf_event__repipe(tool, event, sample, machine); } @@ -825,7 +830,8 @@ static int tool__inject_mmap2_build_id(const struct perf_tool *tool, return 0; } -static int mark_dso_hit(const struct perf_tool *tool, +static int mark_dso_hit(const struct perf_inject *inject, + const struct perf_tool *tool, struct perf_sample *sample, struct machine *machine, const struct evsel *mmap_evsel, @@ -854,16 +860,39 @@ static int mark_dso_hit(const struct perf_tool *tool, } } dso = map__dso(map); - if (dso && !dso__hit(dso)) { - dso__set_hit(dso); - tool__inject_build_id(tool, sample, machine, - mmap_evsel, misc, dso__long_name(dso), dso, - map__flags(map)); + if (inject->build_id_style == BID_RWS__INJECT_HEADER_LAZY) { + if (dso && !dso__hit(dso)) { + dso__set_hit(dso); + tool__inject_build_id(tool, sample, machine, + mmap_evsel, misc, dso__long_name(dso), dso, + map__flags(map)); + } + } else if (inject->build_id_style == BID_RWS__MMAP2_BUILDID_LAZY) { + if (!map__hit(map)) { + const struct build_id null_bid = { .size = 0 }; + const struct build_id *bid = dso ? dso__bid(dso) : &null_bid; + const char *filename = dso ? dso__long_name(dso) : ""; + + map__set_hit(map); + perf_event__synthesize_mmap2_build_id(tool, sample, machine, + perf_event__repipe, + mmap_evsel, + misc, + sample->pid, sample->tid, + map__start(map), + map__end(map) - map__start(map), + map__pgoff(map), + bid, + map__prot(map), + map__flags(map), + filename); + } } return 0; } struct mark_dso_hit_args { + const struct perf_inject *inject; const struct perf_tool *tool; struct perf_sample *sample; struct machine *machine; @@ -875,7 +904,7 @@ static int mark_dso_hit_callback(struct callchain_cursor_node *node, void *data) struct mark_dso_hit_args *args = data; struct map *map = node->ms.map; - return mark_dso_hit(args->tool, args->sample, args->machine, + return mark_dso_hit(args->inject, args->tool, args->sample, args->machine, args->mmap_evsel, map, /*sample_in_dso=*/false); } @@ -888,6 +917,7 @@ int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *e struct thread *thread; struct perf_inject *inject = container_of(tool, struct perf_inject, tool); struct mark_dso_hit_args args = { + .inject = inject, .tool = tool, /* * Use the parsed sample data of the sample event, which will @@ -907,7 +937,7 @@ int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *e } if (thread__find_map(thread, sample->cpumode, sample->ip, &al)) { - mark_dso_hit(tool, sample, machine, args.mmap_evsel, al.map, + mark_dso_hit(inject, tool, sample, machine, args.mmap_evsel, al.map, /*sample_in_dso=*/true); } @@ -2155,7 +2185,8 @@ static int __cmd_inject(struct perf_inject *inject) #endif } - if (inject->build_id_style == BID_RWS__INJECT_HEADER_LAZY) { + if (inject->build_id_style == BID_RWS__INJECT_HEADER_LAZY || + inject->build_id_style == BID_RWS__MMAP2_BUILDID_LAZY) { inject->tool.sample = perf_event__inject_buildid; } else if (inject->sched_stat) { struct evsel *evsel; @@ -2338,6 +2369,7 @@ int cmd_inject(int argc, const char **argv) const char *known_build_ids = NULL; bool build_ids; bool build_id_all; + bool mmap2_build_ids; bool mmap2_build_id_all; struct option options[] = { @@ -2345,6 +2377,8 @@ int cmd_inject(int argc, const char **argv) "Inject build-ids into the output stream"), OPT_BOOLEAN(0, "buildid-all", &build_id_all, "Inject build-ids of all DSOs into the output stream"), + OPT_BOOLEAN('B', "mmap2-buildids", &mmap2_build_ids, + "Drop unused mmap events, make others mmap2 with build IDs"), OPT_BOOLEAN(0, "mmap2-buildid-all", &mmap2_build_id_all, "Rewrite all mmap events as mmap2 events with build IDs"), OPT_STRING(0, "known-build-ids", &known_build_ids, @@ -2443,6 +2477,8 @@ int cmd_inject(int argc, const char **argv) return -1; } } + if (mmap2_build_ids) + inject.build_id_style = BID_RWS__MMAP2_BUILDID_LAZY; if (mmap2_build_id_all) inject.build_id_style = BID_RWS__MMAP2_BUILDID_ALL; if (build_ids) @@ -2453,7 +2489,8 @@ int cmd_inject(int argc, const char **argv) data.path = inject.input_name; ordered_events = inject.jit_mode || inject.sched_stat || - (inject.build_id_style == BID_RWS__INJECT_HEADER_LAZY); + inject.build_id_style == BID_RWS__INJECT_HEADER_LAZY || + inject.build_id_style == BID_RWS__MMAP2_BUILDID_LAZY; perf_tool__init(&inject.tool, ordered_events); inject.tool.sample = perf_event__repipe_sample; inject.tool.read = perf_event__repipe_sample; @@ -2532,7 +2569,8 @@ int cmd_inject(int argc, const char **argv) } } - if (inject.build_id_style == BID_RWS__INJECT_HEADER_LAZY) { + if (inject.build_id_style == BID_RWS__INJECT_HEADER_LAZY || + inject.build_id_style == BID_RWS__MMAP2_BUILDID_LAZY) { /* * to make sure the mmap records are ordered correctly * and so that the correct especially due to jitted code diff --git a/tools/perf/tests/shell/pipe_test.sh b/tools/perf/tests/shell/pipe_test.sh index 250574cd68b6..d4c8005ce9b9 100755 --- a/tools/perf/tests/shell/pipe_test.sh +++ b/tools/perf/tests/shell/pipe_test.sh @@ -116,6 +116,7 @@ test_inject_bids() { } test_record_report +test_inject_bids -B test_inject_bids -b test_inject_bids --buildid-all test_inject_bids --mmap2-buildid-all diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index e781c8d56a9a..d729438b7d65 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -116,6 +116,7 @@ static void map__init(struct map *map, u64 start, u64 end, u64 pgoff, map__set_mapping_type(map, MAPPING_TYPE__DSO); assert(map__erange_warned(map) == false); assert(map__priv(map) == false); + assert(map__hit(map) == false); } struct map *map__new(struct machine *machine, u64 start, u64 len, diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 6c43f31a9fe0..4262f5a143be 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -35,6 +35,7 @@ DECLARE_RC_STRUCT(map) { enum mapping_type mapping_type:8; bool erange_warned; bool priv; + bool hit; }; struct kmap; @@ -83,6 +84,11 @@ static inline bool map__priv(const struct map *map) return RC_CHK_ACCESS(map)->priv; } +static inline bool map__hit(const struct map *map) +{ + return RC_CHK_ACCESS(map)->hit; +} + static inline refcount_t *map__refcnt(struct map *map) { return &RC_CHK_ACCESS(map)->refcnt; @@ -287,6 +293,11 @@ static inline void map__set_priv(struct map *map) RC_CHK_ACCESS(map)->priv = true; } +static inline void map__set_hit(struct map *map) +{ + RC_CHK_ACCESS(map)->hit = true; +} + static inline void map__set_erange_warned(struct map *map) { RC_CHK_ACCESS(map)->erange_warned = true; From 02b2705017d2e5ad32c23067d4b44477ec20b637 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Mon, 9 Sep 2024 13:37:40 -0700 Subject: [PATCH 325/347] perf callchain: Allow symbols to be optional when resolving a callchain In uses like 'perf inject' it is not necessary to gather the symbol for each call chain location, the map for the sample IP is wanted so that build IDs and the like can be injected. Make gathering the symbol in the callchain_cursor optional. For a 'perf inject -B' command this lowers the peak RSS from 54.1MB to 29.6MB by avoiding loading symbols. Signed-off-by: Ian Rogers Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Anne Macedo Cc: Casey Chen Cc: Colin Ian King Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Peter Zijlstra Cc: Sun Haiyong Link: https://lore.kernel.org/r/20240909203740.143492-5-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-inject.c | 2 +- tools/perf/util/callchain.c | 8 ++-- tools/perf/util/callchain.h | 2 +- tools/perf/util/machine.c | 92 +++++++++++++++++++++---------------- tools/perf/util/machine.h | 33 ++++++++++--- 5 files changed, 85 insertions(+), 52 deletions(-) diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c index 9eb72ff48d88..d6989195a061 100644 --- a/tools/perf/builtin-inject.c +++ b/tools/perf/builtin-inject.c @@ -942,7 +942,7 @@ int perf_event__inject_buildid(const struct perf_tool *tool, union perf_event *e } sample__for_each_callchain_node(thread, evsel, sample, PERF_MAX_STACK_DEPTH, - mark_dso_hit_callback, &args); + /*symbols=*/false, mark_dso_hit_callback, &args); thread__put(thread); repipe: diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index 0d608e875fe9..0c7564747a14 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -1800,7 +1800,7 @@ s64 callchain_avg_cycles(struct callchain_node *cnode) int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel, struct perf_sample *sample, int max_stack, - callchain_iter_fn cb, void *data) + bool symbols, callchain_iter_fn cb, void *data) { struct callchain_cursor *cursor = get_tls_callchain_cursor(); int ret; @@ -1809,9 +1809,9 @@ int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel, return -ENOMEM; /* Fill in the callchain. */ - ret = thread__resolve_callchain(thread, cursor, evsel, sample, - /*parent=*/NULL, /*root_al=*/NULL, - max_stack); + ret = __thread__resolve_callchain(thread, cursor, evsel, sample, + /*parent=*/NULL, /*root_al=*/NULL, + max_stack, symbols); if (ret) return ret; diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 76891f8e2373..86ed9e4d04f9 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -315,6 +315,6 @@ typedef int (*callchain_iter_fn)(struct callchain_cursor_node *node, void *data) int sample__for_each_callchain_node(struct thread *thread, struct evsel *evsel, struct perf_sample *sample, int max_stack, - callchain_iter_fn cb, void *data); + bool symbols, callchain_iter_fn cb, void *data); #endif /* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 5783b96fb988..fad227b625d1 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -2060,7 +2060,8 @@ static int add_callchain_ip(struct thread *thread, bool branch, struct branch_flags *flags, struct iterations *iter, - u64 branch_from) + u64 branch_from, + bool symbols) { struct map_symbol ms = {}; struct addr_location al; @@ -2099,7 +2100,8 @@ static int add_callchain_ip(struct thread *thread, } goto out; } - thread__find_symbol(thread, *cpumode, ip, &al); + if (symbols) + thread__find_symbol(thread, *cpumode, ip, &al); } if (al.sym != NULL) { @@ -2228,7 +2230,8 @@ static int lbr_callchain_add_kernel_ip(struct thread *thread, struct symbol **parent, struct addr_location *root_al, u64 branch_from, - bool callee, int end) + bool callee, int end, + bool symbols) { struct ip_callchain *chain = sample->callchain; u8 cpumode = PERF_RECORD_MISC_USER; @@ -2238,7 +2241,8 @@ static int lbr_callchain_add_kernel_ip(struct thread *thread, for (i = 0; i < end + 1; i++) { err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, chain->ips[i], - false, NULL, NULL, branch_from); + false, NULL, NULL, branch_from, + symbols); if (err) return err; } @@ -2248,7 +2252,8 @@ static int lbr_callchain_add_kernel_ip(struct thread *thread, for (i = end; i >= 0; i--) { err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, chain->ips[i], - false, NULL, NULL, branch_from); + false, NULL, NULL, branch_from, + symbols); if (err) return err; } @@ -2291,7 +2296,8 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread, struct symbol **parent, struct addr_location *root_al, u64 *branch_from, - bool callee) + bool callee, + bool symbols) { struct branch_stack *lbr_stack = sample->branch_stack; struct branch_entry *entries = perf_sample__branch_entries(sample); @@ -2324,7 +2330,7 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread, err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip, true, flags, NULL, - *branch_from); + *branch_from, symbols); if (err) return err; @@ -2349,7 +2355,7 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread, err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip, true, flags, NULL, - *branch_from); + *branch_from, symbols); if (err) return err; save_lbr_cursor_node(thread, cursor, i); @@ -2364,7 +2370,7 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread, err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip, true, flags, NULL, - *branch_from); + *branch_from, symbols); if (err) return err; save_lbr_cursor_node(thread, cursor, i); @@ -2378,7 +2384,7 @@ static int lbr_callchain_add_lbr_ip(struct thread *thread, err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip, true, flags, NULL, - *branch_from); + *branch_from, symbols); if (err) return err; } @@ -2545,7 +2551,8 @@ static int resolve_lbr_callchain_sample(struct thread *thread, struct symbol **parent, struct addr_location *root_al, int max_stack, - unsigned int max_lbr) + unsigned int max_lbr, + bool symbols) { bool callee = (callchain_param.order == ORDER_CALLEE); struct ip_callchain *chain = sample->callchain; @@ -2587,12 +2594,12 @@ static int resolve_lbr_callchain_sample(struct thread *thread, /* Add kernel ip */ err = lbr_callchain_add_kernel_ip(thread, cursor, sample, parent, root_al, branch_from, - true, i); + true, i, symbols); if (err) goto error; err = lbr_callchain_add_lbr_ip(thread, cursor, sample, parent, - root_al, &branch_from, true); + root_al, &branch_from, true, symbols); if (err) goto error; @@ -2609,14 +2616,14 @@ static int resolve_lbr_callchain_sample(struct thread *thread, goto error; } err = lbr_callchain_add_lbr_ip(thread, cursor, sample, parent, - root_al, &branch_from, false); + root_al, &branch_from, false, symbols); if (err) goto error; /* Add kernel ip */ err = lbr_callchain_add_kernel_ip(thread, cursor, sample, parent, root_al, branch_from, - false, i); + false, i, symbols); if (err) goto error; } @@ -2630,7 +2637,7 @@ static int find_prev_cpumode(struct ip_callchain *chain, struct thread *thread, struct callchain_cursor *cursor, struct symbol **parent, struct addr_location *root_al, - u8 *cpumode, int ent) + u8 *cpumode, int ent, bool symbols) { int err = 0; @@ -2640,7 +2647,7 @@ static int find_prev_cpumode(struct ip_callchain *chain, struct thread *thread, if (ip >= PERF_CONTEXT_MAX) { err = add_callchain_ip(thread, cursor, parent, root_al, cpumode, ip, - false, NULL, NULL, 0); + false, NULL, NULL, 0, symbols); break; } } @@ -2662,7 +2669,8 @@ static int thread__resolve_callchain_sample(struct thread *thread, struct perf_sample *sample, struct symbol **parent, struct addr_location *root_al, - int max_stack) + int max_stack, + bool symbols) { struct branch_stack *branch = sample->branch_stack; struct branch_entry *entries = perf_sample__branch_entries(sample); @@ -2682,7 +2690,8 @@ static int thread__resolve_callchain_sample(struct thread *thread, err = resolve_lbr_callchain_sample(thread, cursor, sample, parent, root_al, max_stack, - !env ? 0 : env->max_branches); + !env ? 0 : env->max_branches, + symbols); if (err) return (err < 0) ? err : 0; } @@ -2747,13 +2756,14 @@ static int thread__resolve_callchain_sample(struct thread *thread, root_al, NULL, be[i].to, true, &be[i].flags, - NULL, be[i].from); + NULL, be[i].from, symbols); - if (!err) + if (!err) { err = add_callchain_ip(thread, cursor, parent, root_al, NULL, be[i].from, true, &be[i].flags, - &iter[i], 0); + &iter[i], 0, symbols); + } if (err == -EINVAL) break; if (err) @@ -2769,7 +2779,7 @@ static int thread__resolve_callchain_sample(struct thread *thread, check_calls: if (chain && callchain_param.order != ORDER_CALLEE) { err = find_prev_cpumode(chain, thread, cursor, parent, root_al, - &cpumode, chain->nr - first_call); + &cpumode, chain->nr - first_call, symbols); if (err) return (err < 0) ? err : 0; } @@ -2791,7 +2801,7 @@ check_calls: ++nr_entries; else if (callchain_param.order != ORDER_CALLEE) { err = find_prev_cpumode(chain, thread, cursor, parent, - root_al, &cpumode, j); + root_al, &cpumode, j, symbols); if (err) return (err < 0) ? err : 0; continue; @@ -2818,8 +2828,8 @@ check_calls: if (leaf_frame_caller && leaf_frame_caller != ip) { err = add_callchain_ip(thread, cursor, parent, - root_al, &cpumode, leaf_frame_caller, - false, NULL, NULL, 0); + root_al, &cpumode, leaf_frame_caller, + false, NULL, NULL, 0, symbols); if (err) return (err < 0) ? err : 0; } @@ -2827,7 +2837,7 @@ check_calls: err = add_callchain_ip(thread, cursor, parent, root_al, &cpumode, ip, - false, NULL, NULL, 0); + false, NULL, NULL, 0, symbols); if (err) return (err < 0) ? err : 0; @@ -2907,7 +2917,7 @@ static int thread__resolve_callchain_unwind(struct thread *thread, struct callchain_cursor *cursor, struct evsel *evsel, struct perf_sample *sample, - int max_stack) + int max_stack, bool symbols) { /* Can we do dwarf post unwind? */ if (!((evsel->core.attr.sample_type & PERF_SAMPLE_REGS_USER) && @@ -2919,17 +2929,21 @@ static int thread__resolve_callchain_unwind(struct thread *thread, (!sample->user_stack.size)) return 0; + if (!symbols) + pr_debug("Not resolving symbols with an unwinder isn't currently supported\n"); + return unwind__get_entries(unwind_entry, cursor, thread, sample, max_stack, false); } -int thread__resolve_callchain(struct thread *thread, - struct callchain_cursor *cursor, - struct evsel *evsel, - struct perf_sample *sample, - struct symbol **parent, - struct addr_location *root_al, - int max_stack) +int __thread__resolve_callchain(struct thread *thread, + struct callchain_cursor *cursor, + struct evsel *evsel, + struct perf_sample *sample, + struct symbol **parent, + struct addr_location *root_al, + int max_stack, + bool symbols) { int ret = 0; @@ -2942,22 +2956,22 @@ int thread__resolve_callchain(struct thread *thread, ret = thread__resolve_callchain_sample(thread, cursor, evsel, sample, parent, root_al, - max_stack); + max_stack, symbols); if (ret) return ret; ret = thread__resolve_callchain_unwind(thread, cursor, evsel, sample, - max_stack); + max_stack, symbols); } else { ret = thread__resolve_callchain_unwind(thread, cursor, evsel, sample, - max_stack); + max_stack, symbols); if (ret) return ret; ret = thread__resolve_callchain_sample(thread, cursor, evsel, sample, parent, root_al, - max_stack); + max_stack, symbols); } return ret; diff --git a/tools/perf/util/machine.h b/tools/perf/util/machine.h index a687876e3453..2e5a4cb342d8 100644 --- a/tools/perf/util/machine.h +++ b/tools/perf/util/machine.h @@ -178,13 +178,32 @@ struct mem_info *sample__resolve_mem(struct perf_sample *sample, struct callchain_cursor; -int thread__resolve_callchain(struct thread *thread, - struct callchain_cursor *cursor, - struct evsel *evsel, - struct perf_sample *sample, - struct symbol **parent, - struct addr_location *root_al, - int max_stack); +int __thread__resolve_callchain(struct thread *thread, + struct callchain_cursor *cursor, + struct evsel *evsel, + struct perf_sample *sample, + struct symbol **parent, + struct addr_location *root_al, + int max_stack, + bool symbols); + +static inline int thread__resolve_callchain(struct thread *thread, + struct callchain_cursor *cursor, + struct evsel *evsel, + struct perf_sample *sample, + struct symbol **parent, + struct addr_location *root_al, + int max_stack) +{ + return __thread__resolve_callchain(thread, + cursor, + evsel, + sample, + parent, + root_al, + max_stack, + /*symbols=*/true); +} /* * Default guest kernel is defined by parameter --guestkallsyms From e37b315c17df21c3906e4798b3531c5b6d1da489 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Tue, 10 Sep 2024 08:55:22 +0800 Subject: [PATCH 326/347] perf parse-events: Remove duplicated include in parse-events.c The header files parse-events.h is included twice in parse-events.c, so one inclusion of each can be removed. Reported-by: Abaci Robot Signed-off-by: Yang Li Reviewed-by: James Clark Cc: Ingo Molnar Cc: Namhyung Kim Cc: Peter Zijlstra Closes: https://bugzilla.openanolis.cn/show_bug.cgi?id=10822 Link: https://lore.kernel.org/r/20240910005522.35994-1-yang.lee@linux.alibaba.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/parse-events.c | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index 639e65a9bf61..e228511c85a5 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -5,7 +5,6 @@ #include #include "tests.h" #include "debug.h" -#include "parse-events.h" #include "pmu.h" #include "pmus.h" #include From 4c1af9bf97eb56d069421c3233ce61608458d5c8 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Sep 2024 13:54:23 -0300 Subject: [PATCH 327/347] perf trace: If a syscall arg is marked as 'const', assume it is coming _from_ userspace We need to decide where to copy syscall arg contents, if at the syscalls:sys_entry hook, meaning is something that is coming from user to kernel space, or if it is a response, i.e. if it is something the _kernel_ is filling in and thus going to userspace. Since we have 'const' used in those syscalls, and unsure about this being consistent, doing: root@number:~# echo $(grep const /sys/kernel/tracing/events/syscalls/sys_enter_*/format | grep struct | cut -c47- | cut -d'/' -f1) clock_nanosleep clock_settime epoll_pwait2 futex io_pgetevents landlock_create_ruleset listmount mq_getsetattr mq_notify mq_timedreceive mq_timedsend preadv2 preadv prlimit64 process_madvise process_vm_readv process_vm_readv process_vm_writev process_vm_writev pwritev2 pwritev readv rt_sigaction rt_sigtimedwait semtimedop statmount timerfd_settime timer_settime vmsplice writev root@number:~# Seems to indicate that we can use that for the ones that have the 'const' to mark it as coming from user space, do it. Most notable/frequent syscall that now gets BTF pretty printed in a system wide 'perf trace' session is: root@number:~# perf trace 21.160 ( ): MediaSu~isor #/1028597 futex(uaddr: 0x7f49e1dfe964, op: WAIT_BITSET|PRIVATE_FLAG, utime: (struct __kernel_timespec){.tv_sec = (__kernel_time64_t)50290,.tv_nsec = (long long int)810362837,}, val3: MATCH_ANY) ... 21.166 ( 0.000 ms): RemVidChild/6995 futex(uaddr: 0x7f49fcc7fa00, op: WAKE|PRIVATE_FLAG, val: 1) = 0 21.169 ( 0.001 ms): RemVidChild/6995 sendmsg(fd: 25, msg: 0x7f49e9af9da0, flags: DONTWAIT) = 280 21.172 ( 0.289 ms): RemVidChild/6995 futex(uaddr: 0x7f49fcc7fa58, op: WAIT_BITSET|PRIVATE_FLAG|CLOCK_REALTIME, val3: MATCH_ANY) = 0 21.463 ( 0.000 ms): RemVidChild/6995 futex(uaddr: 0x7f49fcc7fa00, op: WAKE|PRIVATE_FLAG, val: 1) = 0 21.467 ( 0.001 ms): RemVidChild/6995 futex(uaddr: 0x7f49e28bb964, op: WAKE|PRIVATE_FLAG, val: 1) = 1 21.160 ( 0.314 ms): MediaSu~isor #/1028597 ... [continued]: futex()) = 0 21.469 ( ): RemVidChild/6995 futex(uaddr: 0x7f49fcc7fa5c, op: WAIT_BITSET|PRIVATE_FLAG|CLOCK_REALTIME, val3: MATCH_ANY) ... 21.475 ( 0.000 ms): MediaSu~isor #/1028597 futex(uaddr: 0x7f49d0223040, op: WAKE|PRIVATE_FLAG, val: 1) = 0 21.478 ( 0.001 ms): MediaSu~isor #/1028597 futex(uaddr: 0x7f49e26ac964, op: WAKE|PRIVATE_FLAG, val: 1) = 1 ^Croot@number:~# root@number:~# cat /sys/kernel/tracing/events/syscalls/sys_enter_futex/format name: sys_enter_futex ID: 454 format: field:unsigned short common_type; offset:0; size:2; signed:0; field:unsigned char common_flags; offset:2; size:1; signed:0; field:unsigned char common_preempt_count; offset:3; size:1; signed:0; field:int common_pid; offset:4; size:4; signed:1; field:int __syscall_nr; offset:8; size:4; signed:1; field:u32 * uaddr; offset:16; size:8; signed:0; field:int op; offset:24; size:8; signed:0; field:u32 val; offset:32; size:8; signed:0; field:const struct __kernel_timespec * utime; offset:40; size:8; signed:0; field:u32 * uaddr2; offset:48; size:8; signed:0; field:u32 val3; offset:56; size:8; signed:0; print fmt: "uaddr: 0x%08lx, op: 0x%08lx, val: 0x%08lx, utime: 0x%08lx, uaddr2: 0x%08lx, val3: 0x%08lx", ((unsigned long)(REC->uaddr)), ((unsigned long)(REC->op)), ((unsigned long)(REC->val)), ((unsigned long)(REC->utime)), ((unsigned long)(REC->uaddr2)), ((unsigned long)(REC->val3)) root@number:~# Suggested-by: Ian Rogers Reviewed-by: Ian Rogers Cc: Adrian Hunter Cc: Alan Maguire Cc: Howard Chu Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/CAP-5=fWnuQrrBoTn6Rrn6vM_xQ2fCoc9i-AitD7abTcNi-4o1Q@mail.gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 838e179a2573..3b88ba50bb80 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -2001,11 +2001,14 @@ 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)) { arg->scnprintf = SCA_FILENAME; - arg->from_user = true; } else if ((field->flags & TEP_FIELD_IS_POINTER) || strstr(field->name, "addr")) arg->scnprintf = SCA_PTR; else if (strcmp(field->type, "pid_t") == 0) From 9327f0ecad48517d4f6f9dfa7b194d0cf53092ea Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Sep 2024 16:44:21 -0300 Subject: [PATCH 328/347] perf build: Require at least clang 16.0.6 to build BPF skeletons Howard reported problems using perf features that use BPF: perf $ clang -v Debian clang version 15.0.6 Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /bin Found candidate GCC installation: /bin/../lib/gcc/x86_64-linux-gnu/12 Selected GCC installation: /bin/../lib/gcc/x86_64-linux-gnu/12 Candidate multilib: .;@m64 Selected multilib: .;@m64 perf $ ./perf trace -e write --max-events=1 libbpf: prog 'sys_enter_rename': BPF program load failed: Permission denied libbpf: prog 'sys_enter_rename': -- BEGIN PROG LOAD LOG -- 0: R1=ctx() R10=fp0 But it works with: perf $ clang -v Debian clang version 16.0.6 (15~deb12u1) Target: x86_64-pc-linux-gnu Thread model: posix InstalledDir: /bin Found candidate GCC installation: /bin/../lib/gcc/x86_64-linux-gnu/12 Selected GCC installation: /bin/../lib/gcc/x86_64-linux-gnu/12 Candidate multilib: .;@m64 Selected multilib: .;@m64 perf $ ./perf trace -e write --max-events=1 0.000 ( 0.009 ms): gmain/1448 write(fd: 4, buf: \1\0\0\0\0\0\0\0, count: 8) = 8 (kworker/0:0-eve) perf $ So lets make that the required version, if you happen to have a slightly older version where this work, please report so that we can adjust the minimum required version. Reported-by: Howard Chu Cc: Ian Rogers Cc: Adrian Hunter Cc: Alan Maguire Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/ZuGL9ROeTV2uXoSp@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/Makefile.config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 37e3eee2986e..4c1f75e326e2 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -705,8 +705,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 From 83420d5f586344f3781b35f5d78b67aea55bff2b Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Tue, 10 Sep 2024 17:18:26 -0300 Subject: [PATCH 329/347] perf test shell probe_vfs_getname: Remove extraneous '=' from probe line number regex Thomas reported the vfs_getname perf tests failing on s/390, it seems it was just to some extraneous '=' somehow getting into the regexp, remove it, now: root@x1:~# perf test getname 91: Add vfs_getname probe to get syscall args filenames : Ok 93: Use vfs_getname probe to get syscall args filenames : FAILED! 126: Check open filename arg using perf trace + vfs_getname : Ok root@x1:~# Second one remains a mistery, have to take some time to nail it down. Reported-by: Thomas Richter Tested-by: Thomas Richter Cc: Adrian Hunter Cc: Alexander Gordeev Cc: Heiko Carstens Cc: Ian Rogers Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Cc: Vasily Gorbik , Link: https://lore.kernel.org/lkml/1d7f3b7b-9edc-4d90-955c-9345428563f1@linux.ibm.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/shell/lib/probe_vfs_getname.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/perf/tests/shell/lib/probe_vfs_getname.sh b/tools/perf/tests/shell/lib/probe_vfs_getname.sh index 0606e693eb59..5c33ec7a5a63 100644 --- a/tools/perf/tests/shell/lib/probe_vfs_getname.sh +++ b/tools/perf/tests/shell/lib/probe_vfs_getname.sh @@ -14,10 +14,10 @@ add_probe_vfs_getname() { add_probe_verbose=$1 if [ $had_vfs_getname -eq 1 ] ; then result_filename_re="[[:space:]]+([[:digit:]]+)[[:space:]]+result->uptr.*" - line=$(perf probe -L getname_flags 2>&1 | grep -E "$result_filename_re=" | sed -r "s/$result_filename_re=/\1/") + line=$(perf probe -L getname_flags 2>&1 | grep -E "$result_filename_re" | sed -r "s/$result_filename_re/\1/") if [ -z "$line" ] ; then result_aname_re="[[:space:]]+([[:digit:]]+)[[:space:]]+result->aname = NULL;" - line=$(perf probe -L getname_flags 2>&1 | grep -E "$result_aname_re=" | sed -r "s/$result_aname_re=/\1/") + line=$(perf probe -L getname_flags 2>&1 | grep -E "$result_aname_re" | sed -r "s/$result_aname_re/\1/") fi perf probe -q "vfs_getname=getname_flags:${line} pathname=result->name:string" || \ perf probe $add_probe_verbose "vfs_getname=getname_flags:${line} pathname=filename:ustring" From 74298dd8acb875dc9b0486437945e50811b31794 Mon Sep 17 00:00:00 2001 From: Changbin Du Date: Wed, 11 Sep 2024 18:01:26 +0800 Subject: [PATCH 330/347] perf ftrace: Detect whether ftrace is enabled on system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To make error messages more accurate, this change detects whether ftrace is enabled on system by checking trace file "set_ftrace_pid". Before: # perf ftrace failed to reset ftrace # After: # perf ftrace ftrace is not supported on this system # Committer testing: Doing it in an unprivileged toolbox container on Fedora 40: Before: acme@number:~/git/perf-tools-next$ toolbox enter perf ⬢[acme@toolbox perf-tools-next]$ sudo su - ⬢[root@toolbox ~]# ~acme/bin/perf ftrace failed to reset ftrace ⬢[root@toolbox ~]# After this patch: ⬢[root@toolbox ~]# ~acme/bin/perf ftrace ftrace is not supported on this system ⬢[root@toolbox ~]# Maybe we could check if we are in such as situation, inside an unprivileged container, and provide a HINT line? Reviewed-by: James Clark Signed-off-by: Changbin Du Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Changbin Du Cc: Ian Rogers Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Link: https://lore.kernel.org/r/20240911100126.900779-1-changbin.du@huawei.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-ftrace.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c index 88a87bf387d2..abcdc49b7a98 100644 --- a/tools/perf/builtin-ftrace.c +++ b/tools/perf/builtin-ftrace.c @@ -80,6 +80,24 @@ static bool check_ftrace_capable(void) 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; @@ -1583,6 +1601,11 @@ int cmd_ftrace(int argc, const char **argv) 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; From 4ae354d73a8e16b925f1fd1542105d6889eff7c1 Mon Sep 17 00:00:00 2001 From: Andrew Kreimer Date: Sat, 7 Sep 2024 16:10:01 +0300 Subject: [PATCH 331/347] perf help: Fix a typo ("bellow") Fix a typo in comments. Reported-by: Matthew Wilcox Reviewed-by: Ian Rogers Signed-off-by: Andrew Kreimer Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Ingo Molnar Cc: Jiri Olsa Cc: Kan Liang Cc: Mark Rutland Cc: Namhyung Kim Cc: Peter Zijlstra Cc: kernel-janitors@vger.kernel.org Link: https://lore.kernel.org/r/20240907131006.18510-1-algonell@gmail.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-help.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c index b2a368ae295a..0854d3cd9f6a 100644 --- a/tools/perf/builtin-help.c +++ b/tools/perf/builtin-help.c @@ -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; From d3d5c1a00fcdbae92456a6e78a7d440880fff18a Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 6 Sep 2024 22:08:16 -0700 Subject: [PATCH 332/347] perf list: Avoid potential out of bounds memory read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a desc string is 0 length then -1 will be out of bounds, add a check. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Benjamin Gray Cc: Changbin Du Cc: Clément Le Goffic Cc: Colin Ian King Cc: Dominique Martinet Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Ingo Molnar Cc: James Clark Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: Junhao He Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Oliver Upton Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sandipan Das Cc: Sun Haiyong Cc: Tiezhu Yang Cc: Veronika Molnarova Cc: Weilin Wang Cc: Will Deacon Cc: Xu Yang Cc: Yang Jihong Cc: Yicong Yang Cc: Ze Gao Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240907050830.6752-2-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-list.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index 82cb4b1010aa..65b8cba324be 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c @@ -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); } From 925320737ae290ab4bcf9c277c2a7718113717ae Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 6 Sep 2024 22:08:17 -0700 Subject: [PATCH 333/347] perf pmus: Fake PMU clean up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than passing a fake PMU around, just pass that the fake PMU should be used - true when doing testing. Move the fake PMU into pmus.[ch] and try to abstract the PMU's properties in pmu.c, ie so there is less "if fake_pmu" in non-PMU code. Give the fake PMU a made up type number. Signed-off-by: Ian Rogers Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Andi Kleen Cc: Athira Rajeev Cc: Benjamin Gray Cc: Changbin Du Cc: Clément Le Goffic Cc: Colin Ian King Cc: Dominique Martinet Cc: Dr. David Alan Gilbert Cc: Howard Chu Cc: Ingo Molnar Cc: James Clark Cc: Jing Zhang Cc: Jiri Olsa Cc: John Garry Cc: Junhao He Cc: Kan Liang Cc: Leo Yan Cc: Mark Rutland Cc: Mike Leach Cc: Namhyung Kim Cc: Oliver Upton Cc: Peter Zijlstra Cc: Ravi Bangoria Cc: Sandipan Das Cc: Sun Haiyong Cc: Tiezhu Yang Cc: Veronika Molnarova Cc: Weilin Wang Cc: Will Deacon Cc: Xu Yang Cc: Yang Jihong Cc: Yicong Yang Cc: Ze Gao Cc: linux-arm-kernel@lists.infradead.org Link: https://lore.kernel.org/r/20240907050830.6752-3-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/tests/parse-events.c | 4 ++-- tools/perf/tests/pmu-events.c | 12 +++++------- tools/perf/util/metricgroup.c | 10 +++++----- tools/perf/util/parse-events.c | 33 +++++++++++++++++---------------- tools/perf/util/parse-events.h | 8 ++++---- tools/perf/util/pmu.c | 21 +++++++++++++++------ tools/perf/util/pmu.h | 7 ++++--- tools/perf/util/pmus.c | 11 +++++++++++ tools/perf/util/pmus.h | 1 + 9 files changed, 64 insertions(+), 43 deletions(-) diff --git a/tools/perf/tests/parse-events.c b/tools/perf/tests/parse-events.c index e228511c85a5..9e3086d02150 100644 --- a/tools/perf/tests/parse-events.c +++ b/tools/perf/tests/parse-events.c @@ -2500,7 +2500,7 @@ static int test_event(const struct evlist_test *e) return TEST_FAIL; } parse_events_error__init(&err); - ret = __parse_events(evlist, e->name, /*pmu_filter=*/NULL, &err, /*fake_pmu=*/NULL, + ret = __parse_events(evlist, e->name, /*pmu_filter=*/NULL, &err, /*fake_pmu=*/false, /*warn_if_reordered=*/true, /*fake_tp=*/true); if (ret) { pr_debug("failed to parse event '%s', err %d\n", e->name, ret); @@ -2529,7 +2529,7 @@ static int test_event_fake_pmu(const char *str) parse_events_error__init(&err); ret = __parse_events(evlist, str, /*pmu_filter=*/NULL, &err, - &perf_pmu__fake, /*warn_if_reordered=*/true, + /*fake_pmu=*/true, /*warn_if_reordered=*/true, /*fake_tp=*/true); if (ret) { pr_debug("failed to parse event '%s', err %d\n", diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c index ff3e7bc0a77f..db004d26fcb0 100644 --- a/tools/perf/tests/pmu-events.c +++ b/tools/perf/tests/pmu-events.c @@ -819,8 +819,7 @@ static bool is_number(const char *str) return errno == 0 && end_ptr != str; } -static int check_parse_id(const char *id, struct parse_events_error *error, - struct perf_pmu *fake_pmu) +static int check_parse_id(const char *id, struct parse_events_error *error) { struct evlist *evlist; int ret; @@ -841,7 +840,7 @@ static int check_parse_id(const char *id, struct parse_events_error *error, for (cur = strchr(dup, '@') ; cur; cur = strchr(++cur, '@')) *cur = '/'; - ret = __parse_events(evlist, dup, /*pmu_filter=*/NULL, error, fake_pmu, + ret = __parse_events(evlist, dup, /*pmu_filter=*/NULL, error, /*fake_pmu=*/true, /*warn_if_reordered=*/true, /*fake_tp=*/false); free(dup); @@ -855,7 +854,7 @@ static int check_parse_fake(const char *id) int ret; parse_events_error__init(&error); - ret = check_parse_id(id, &error, &perf_pmu__fake); + ret = check_parse_id(id, &error); parse_events_error__exit(&error); return ret; } @@ -1051,9 +1050,8 @@ static int test__parsing_fake_callback(const struct pmu_metric *pm, } /* - * Parse all the metrics for current architecture, - * or all defined cpus via the 'fake_pmu' - * in parse_events. + * Parse all the metrics for current architecture, or all defined cpus via the + * 'fake_pmu' in parse_events. */ static int test__parsing_fake(struct test_suite *test __maybe_unused, int subtest __maybe_unused) diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c index 69f6a46402c3..4dff3e925a47 100644 --- a/tools/perf/util/metricgroup.c +++ b/tools/perf/util/metricgroup.c @@ -1436,7 +1436,7 @@ err_out: * parse_ids - Build the event string for the ids and parse them creating an * evlist. The encoded metric_ids are decoded. * @metric_no_merge: is metric sharing explicitly disabled. - * @fake_pmu: used when testing metrics not supported by the current CPU. + * @fake_pmu: use a fake PMU when testing metrics not supported by the current CPU. * @ids: the event identifiers parsed from a metric. * @modifier: any modifiers added to the events. * @group_events: should events be placed in a weak group. @@ -1444,7 +1444,7 @@ err_out: * the overall list of metrics. * @out_evlist: the created list of events. */ -static int parse_ids(bool metric_no_merge, struct perf_pmu *fake_pmu, +static int parse_ids(bool metric_no_merge, bool fake_pmu, struct expr_parse_ctx *ids, const char *modifier, bool group_events, const bool tool_events[PERF_TOOL_MAX], struct evlist **out_evlist) @@ -1528,7 +1528,7 @@ static int parse_groups(struct evlist *perf_evlist, bool metric_no_threshold, const char *user_requested_cpu_list, bool system_wide, - struct perf_pmu *fake_pmu, + bool fake_pmu, struct rblist *metric_events_list, const struct pmu_metrics_table *table) { @@ -1703,7 +1703,7 @@ int metricgroup__parse_groups(struct evlist *perf_evlist, return parse_groups(perf_evlist, pmu, str, metric_no_group, metric_no_merge, metric_no_threshold, user_requested_cpu_list, system_wide, - /*fake_pmu=*/NULL, metric_events, table); + /*fake_pmu=*/false, metric_events, table); } int metricgroup__parse_groups_test(struct evlist *evlist, @@ -1717,7 +1717,7 @@ int metricgroup__parse_groups_test(struct evlist *evlist, /*metric_no_threshold=*/false, /*user_requested_cpu_list=*/NULL, /*system_wide=*/false, - &perf_pmu__fake, metric_events, table); + /*fake_pmu=*/true, metric_events, table); } struct metricgroup__has_metric_data { diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index f5eb1af8302c..9a8be1e46d67 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -1500,8 +1500,8 @@ static int parse_events_add_pmu(struct parse_events_state *parse_state, } /* Look for event names in the terms and rewrite into format based terms. */ - if (!parse_state->fake_pmu && perf_pmu__check_alias(pmu, &parsed_terms, - &info, &alias_rewrote_terms, err)) { + if (perf_pmu__check_alias(pmu, &parsed_terms, + &info, &alias_rewrote_terms, err)) { parse_events_terms__exit(&parsed_terms); return -EINVAL; } @@ -1537,8 +1537,7 @@ static int parse_events_add_pmu(struct parse_events_state *parse_state, return -ENOMEM; } - if (!parse_state->fake_pmu && - perf_pmu__config(pmu, &attr, &parsed_terms, parse_state->error)) { + if (perf_pmu__config(pmu, &attr, &parsed_terms, parse_state->error)) { free_config_terms(&config_terms); parse_events_terms__exit(&parsed_terms); return -EINVAL; @@ -1558,11 +1557,6 @@ static int parse_events_add_pmu(struct parse_events_state *parse_state, evsel->percore = config_term_percore(&evsel->config_terms); - if (parse_state->fake_pmu) { - parse_events_terms__exit(&parsed_terms); - return 0; - } - parse_events_terms__exit(&parsed_terms); free((char *)evsel->unit); evsel->unit = strdup(info.unit); @@ -1638,13 +1632,13 @@ int parse_events_multi_pmu_add(struct parse_events_state *parse_state, } if (parse_state->fake_pmu) { - if (!parse_events_add_pmu(parse_state, list, parse_state->fake_pmu, &parsed_terms, + if (!parse_events_add_pmu(parse_state, list, perf_pmus__fake_pmu(), &parsed_terms, /*auto_merge_stats=*/true)) { struct strbuf sb; strbuf_init(&sb, /*hint=*/ 0); parse_events_terms__to_strbuf(&parsed_terms, &sb); - pr_debug("%s -> %s/%s/\n", event_name, "fake_pmu", sb.buf); + pr_debug("%s -> fake/%s/\n", event_name, sb.buf); strbuf_release(&sb); ok++; } @@ -1678,11 +1672,18 @@ int parse_events_multi_pmu_add_or_add_pmu(struct parse_events_state *parse_state INIT_LIST_HEAD(*listp); /* Attempt to add to list assuming event_or_pmu is a PMU name. */ - pmu = parse_state->fake_pmu ?: perf_pmus__find(event_or_pmu); + pmu = perf_pmus__find(event_or_pmu); if (pmu && !parse_events_add_pmu(parse_state, *listp, pmu, const_parsed_terms, /*auto_merge_stats=*/false)) return 0; + if (parse_state->fake_pmu) { + if (!parse_events_add_pmu(parse_state, *listp, perf_pmus__fake_pmu(), + const_parsed_terms, + /*auto_merge_stats=*/false)) + return 0; + } + pmu = NULL; /* Failed to add, try wildcard expansion of event_or_pmu as a PMU name. */ while ((pmu = perf_pmus__scan(pmu)) != NULL) { @@ -1983,8 +1984,8 @@ static int evsel__compute_group_pmu_name(struct evsel *evsel, } } } - /* Assign the actual name taking care that the fake PMU lacks a name. */ - evsel->group_pmu_name = strdup(group_pmu_name ?: "fake"); + /* Record computed name. */ + evsel->group_pmu_name = strdup(group_pmu_name); return evsel->group_pmu_name ? 0 : -ENOMEM; } @@ -2146,7 +2147,7 @@ static int parse_events__sort_events_and_fix_groups(struct list_head *list) } int __parse_events(struct evlist *evlist, const char *str, const char *pmu_filter, - struct parse_events_error *err, struct perf_pmu *fake_pmu, + struct parse_events_error *err, bool fake_pmu, bool warn_if_reordered, bool fake_tp) { struct parse_events_state parse_state = { @@ -2365,7 +2366,7 @@ int parse_events_option(const struct option *opt, const char *str, parse_events_error__init(&err); ret = __parse_events(*args->evlistp, str, args->pmu_filter, &err, - /*fake_pmu=*/NULL, /*warn_if_reordered=*/true, + /*fake_pmu=*/false, /*warn_if_reordered=*/true, /*fake_tp=*/false); if (ret) { diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 68bfea9ffa70..10cc9c433116 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h @@ -31,14 +31,14 @@ int parse_events_option(const struct option *opt, const char *str, int unset); int parse_events_option_new_evlist(const struct option *opt, const char *str, int unset); __attribute__((nonnull(1, 2, 4))) int __parse_events(struct evlist *evlist, const char *str, const char *pmu_filter, - struct parse_events_error *error, struct perf_pmu *fake_pmu, + struct parse_events_error *error, bool fake_pmu, bool warn_if_reordered, bool fake_tp); __attribute__((nonnull(1, 2, 3))) static inline int parse_events(struct evlist *evlist, const char *str, struct parse_events_error *err) { - return __parse_events(evlist, str, /*pmu_filter=*/NULL, err, /*fake_pmu=*/NULL, + return __parse_events(evlist, str, /*pmu_filter=*/NULL, err, /*fake_pmu=*/false, /*warn_if_reordered=*/true, /*fake_tp=*/false); } @@ -150,8 +150,8 @@ struct parse_events_state { struct parse_events_terms *terms; /* Start token. */ int stoken; - /* Special fake PMU marker for testing. */ - struct perf_pmu *fake_pmu; + /* Use the fake PMU marker for testing. */ + bool fake_pmu; /* Skip actual tracepoint processing for testing. */ bool fake_tp; /* If non-null, when wildcard matching only match the given PMU. */ diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index ef8ab918eaeb..2d85c4482f5e 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -30,10 +30,6 @@ #include "util/evsel_config.h" #include -struct perf_pmu perf_pmu__fake = { - .name = "fake", -}; - #define UNIT_MAX_LEN 31 /* max length for event unit name */ enum event_source { @@ -1172,6 +1168,11 @@ struct perf_pmu *perf_pmu__create_placeholder_core_pmu(struct list_head *core_pm return pmu; } +static bool perf_pmu__is_fake(const struct perf_pmu *pmu) +{ + return pmu->type == PERF_PMU_TYPE_FAKE; +} + void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu) { struct perf_pmu_format *format; @@ -1182,7 +1183,7 @@ void perf_pmu__warn_invalid_formats(struct perf_pmu *pmu) pmu->formats_checked = true; /* fake pmu doesn't have format list */ - if (pmu == &perf_pmu__fake) + if (perf_pmu__is_fake(pmu)) return; list_for_each_entry(format, &pmu->format, list) { @@ -1510,6 +1511,10 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, { bool zero = !!pmu->perf_event_attr_init_default; + /* Fake PMU doesn't have proper terms so nothing to configure in attr. */ + if (perf_pmu__is_fake(pmu)) + return 0; + return perf_pmu__config_terms(pmu, attr, head_terms, zero, err); } @@ -1618,6 +1623,10 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct parse_events_terms *head_ info->scale = 0.0; info->snapshot = false; + /* Fake PMU doesn't rewrite terms. */ + if (perf_pmu__is_fake(pmu)) + goto out; + list_for_each_entry_safe(term, h, &head_terms->terms, list) { alias = pmu_find_alias(pmu, term); if (!alias) @@ -1640,7 +1649,7 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct parse_events_terms *head_ list_del_init(&term->list); parse_events_term__delete(term); } - +out: /* * if no unit or scale found in aliases, then * set defaults as for evsel diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index b2d3fd291f02..d6f702e55a05 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -36,6 +36,10 @@ struct perf_pmu_caps { struct list_head list; }; +enum { + PERF_PMU_TYPE_FAKE = 0xFFFFFFFF, +}; + /** * struct perf_pmu */ @@ -173,9 +177,6 @@ struct perf_pmu { struct perf_mem_event *mem_events; }; -/** @perf_pmu__fake: A special global PMU used for testing. */ -extern struct perf_pmu perf_pmu__fake; - struct perf_pmu_info { const char *unit; double scale; diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index 769b920d9250..7e201e422812 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -720,3 +720,14 @@ struct perf_pmu *perf_pmus__add_test_pmu(int test_sysfs_dirfd, const char *name) */ return perf_pmu__lookup(&other_pmus, test_sysfs_dirfd, name, /*eager_load=*/true); } + +struct perf_pmu *perf_pmus__fake_pmu(void) +{ + static struct perf_pmu fake = { + .name = "fake", + .type = PERF_PMU_TYPE_FAKE, + .format = LIST_HEAD_INIT(fake.format), + }; + + return &fake; +} diff --git a/tools/perf/util/pmus.h b/tools/perf/util/pmus.h index bdbff02324bb..e1742b56eec7 100644 --- a/tools/perf/util/pmus.h +++ b/tools/perf/util/pmus.h @@ -30,5 +30,6 @@ bool perf_pmus__supports_extended_type(void); char *perf_pmus__default_pmu_name(void); struct perf_pmu *perf_pmus__add_test_pmu(int test_sysfs_dirfd, const char *name); +struct perf_pmu *perf_pmus__fake_pmu(void); #endif /* __PMUS_H */ From f08cc258431df0ac498a4700d2d5b6f6aebb4889 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 6 Sep 2024 22:08:18 -0700 Subject: [PATCH 334/347] perf evsel: Add accessor for tool_event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently tool events use a dedicated variable within the evsel. Later changes will move this to the unused struct perf_event_attr config for these events. Add an accessor to allow the later change to be well typed and avoid changing all uses. Signed-off-by: Ian Rogers Link: https://lore.kernel.org/r/20240907050830.6752-4-irogers@google.com Cc: Ravi Bangoria Cc: Sandipan Das Cc: Mark Rutland Cc: Yang Jihong Cc: Dominique Martinet Cc: Clément Le Goffic Cc: Colin Ian King Cc: Howard Chu Cc: Ze Gao Cc: Yicong Yang Cc: Changbin Du Cc: Junhao He Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Weilin Wang Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Cc: Will Deacon Cc: James Clark Cc: Mike Leach Cc: Jing Zhang Cc: Leo Yan Cc: Oliver Upton Cc: Benjamin Gray Cc: Andi Kleen Cc: Alexander Shishkin Cc: Kan Liang Cc: Athira Jajeev Cc: linux-arm-kernel@lists.infradead.org Cc: Sun Haiyong Cc: Tiezhu Yang Cc: Xu Yang Cc: John Garry Cc: Ingo Molnar Cc: Veronika Molnarova Cc: Dr. David Alan Gilbert Cc: linux-kernel@vger.kernel.org Cc: linux-perf-users@vger.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-stat.c | 6 +++--- tools/perf/util/evsel.c | 24 ++++++++++++------------ tools/perf/util/evsel.h | 5 +++++ tools/perf/util/stat-shadow.c | 2 +- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index cf985cdb9a6e..689a3d43c258 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c @@ -294,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; diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 49cc71511c0c..dbf9c8cee3c5 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -773,7 +773,7 @@ const char *evsel__name(struct evsel *evsel) case PERF_TYPE_SOFTWARE: if (evsel__is_tool(evsel)) - evsel__tool_name(evsel->tool_event, bf, sizeof(bf)); + evsel__tool_name(evsel__tool_event(evsel), bf, sizeof(bf)); else evsel__sw_name(evsel, bf, sizeof(bf)); break; @@ -811,7 +811,7 @@ const char *evsel__metric_id(const struct evsel *evsel) return evsel->metric_id; if (evsel__is_tool(evsel)) - return perf_tool_event__to_str(evsel->tool_event); + return perf_tool_event__to_str(evsel__tool_event(evsel)); return "unknown"; } @@ -1503,8 +1503,8 @@ void evsel__exit(struct evsel *evsel) evsel->per_pkg_mask = NULL; zfree(&evsel->metric_events); perf_evsel__object.fini(evsel); - if (evsel->tool_event == PERF_TOOL_SYSTEM_TIME || - evsel->tool_event == PERF_TOOL_USER_TIME) + if (evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME || + evsel__tool_event(evsel) == PERF_TOOL_USER_TIME) xyarray__delete(evsel->start_times); } @@ -1785,7 +1785,7 @@ static int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread) count = perf_counts(evsel->counts, cpu_map_idx, thread); - switch (evsel->tool_event) { + switch (evsel__tool_event(evsel)) { case PERF_TOOL_DURATION_TIME: /* * Pretend duration_time is only on the first CPU and thread, or @@ -1800,7 +1800,7 @@ static int evsel__read_tool(struct evsel *evsel, int cpu_map_idx, int thread) break; case PERF_TOOL_USER_TIME: case PERF_TOOL_SYSTEM_TIME: { - bool system = evsel->tool_event == PERF_TOOL_SYSTEM_TIME; + bool system = evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME; start_time = xyarray__entry(evsel->start_times, cpu_map_idx, thread); fd = FD(evsel, cpu_map_idx, thread); @@ -2072,8 +2072,8 @@ static int __evsel__prepare_open(struct evsel *evsel, struct perf_cpu_map *cpus, perf_evsel__alloc_fd(&evsel->core, perf_cpu_map__nr(cpus), nthreads) < 0) return -ENOMEM; - if ((evsel->tool_event == PERF_TOOL_SYSTEM_TIME || - evsel->tool_event == PERF_TOOL_USER_TIME) && + if ((evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME || + evsel__tool_event(evsel) == PERF_TOOL_USER_TIME) && !evsel->start_times) { evsel->start_times = xyarray__new(perf_cpu_map__nr(cpus), nthreads, sizeof(__u64)); if (!evsel->start_times) @@ -2262,7 +2262,7 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus, int pid = -1, err, old_errno; enum rlimit_action set_rlimit = NO_CHANGE; - if (evsel->tool_event == PERF_TOOL_DURATION_TIME) { + if (evsel__tool_event(evsel) == PERF_TOOL_DURATION_TIME) { if (evsel->core.attr.sample_period) /* no sampling */ return -EINVAL; evsel->start_time = rdclock(); @@ -2304,9 +2304,9 @@ retry_open: if (!evsel->cgrp && !evsel->core.system_wide) pid = perf_thread_map__pid(threads, thread); - if (evsel->tool_event == PERF_TOOL_USER_TIME || - evsel->tool_event == PERF_TOOL_SYSTEM_TIME) { - bool system = evsel->tool_event == PERF_TOOL_SYSTEM_TIME; + if (evsel__tool_event(evsel) == PERF_TOOL_USER_TIME || + evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME) { + bool system = evsel__tool_event(evsel) == PERF_TOOL_SYSTEM_TIME; __u64 *start_time = NULL; if (evsel->core.attr.sample_period) { diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 15acf293e12a..15e745a9a798 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -330,6 +330,11 @@ static inline bool evsel__is_retire_lat(const struct evsel *evsel) return evsel->retire_lat; } +static inline enum perf_tool_event evsel__tool_event(const struct evsel *evsel) +{ + return evsel->tool_event; +} + const char *evsel__group_name(struct evsel *evsel); int evsel__group_desc(struct evsel *evsel, char *buf, size_t size); diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index 6bb975e46de3..99376c12dd8e 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c @@ -380,7 +380,7 @@ static int prepare_metric(const struct metric_expr *mexp, struct stats *stats; double scale; - switch (metric_events[i]->tool_event) { + switch (evsel__tool_event(metric_events[i])) { case PERF_TOOL_DURATION_TIME: stats = &walltime_nsecs_stats; scale = 1e-9; From 89c0a55e550ebb1fd19bba72fc08c8f6e2d3b1db Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 6 Sep 2024 22:08:19 -0700 Subject: [PATCH 335/347] perf pmu: To info add event_type_desc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All PMU events are assumed to be "Kernel PMU event", however, this isn't true for fake PMUs and won't be true with the addition of more software PMUs. Make the PMU's type description name configurable - largely for printing callbacks. Signed-off-by: Ian Rogers Link: https://lore.kernel.org/r/20240907050830.6752-5-irogers@google.com Cc: Ravi Bangoria Cc: Sandipan Das Cc: Mark Rutland Cc: Yang Jihong Cc: Dominique Martinet Cc: Clément Le Goffic Cc: Colin Ian King Cc: Howard Chu Cc: Ze Gao Cc: Yicong Yang Cc: Changbin Du Cc: Junhao He Cc: Peter Zijlstra Cc: Adrian Hunter Cc: Weilin Wang Cc: Arnaldo Carvalho de Melo Cc: Jiri Olsa Cc: Namhyung Kim Cc: Will Deacon Cc: James Clark Cc: Mike Leach Cc: Jing Zhang Cc: Leo Yan Cc: Oliver Upton Cc: Benjamin Gray Cc: Andi Kleen Cc: Alexander Shishkin Cc: Kan Liang Cc: Athira Jajeev Cc: linux-arm-kernel@lists.infradead.org Cc: Sun Haiyong Cc: Tiezhu Yang Cc: Xu Yang Cc: John Garry Cc: Ingo Molnar Cc: Veronika Molnarova Cc: Dr. David Alan Gilbert Cc: linux-kernel@vger.kernel.org Cc: linux-perf-users@vger.kernel.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/pmu.c | 1 + tools/perf/util/pmu.h | 1 + tools/perf/util/pmus.c | 5 ++++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 2d85c4482f5e..61bdda01a05a 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -1856,6 +1856,7 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus, struct perf_pmu_alias *event; struct pmu_event_info info = { .pmu = pmu, + .event_type_desc = "Kernel PMU event", }; int ret = 0; struct strbuf sb; diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index d6f702e55a05..4397c48ad569 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -194,6 +194,7 @@ struct pmu_event_info { const char *encoding_desc; const char *topic; const char *pmu_name; + const char *event_type_desc; const char *str; bool deprecated; }; diff --git a/tools/perf/util/pmus.c b/tools/perf/util/pmus.c index 7e201e422812..52109af5f2f1 100644 --- a/tools/perf/util/pmus.c +++ b/tools/perf/util/pmus.c @@ -371,6 +371,7 @@ struct sevent { const char *encoding_desc; const char *topic; const char *pmu_name; + const char *event_type_desc; bool deprecated; }; @@ -444,6 +445,7 @@ static int perf_pmus__print_pmu_events__callback(void *vstate, COPY_STR(encoding_desc); COPY_STR(topic); COPY_STR(pmu_name); + COPY_STR(event_type_desc); #undef COPY_STR s->deprecated = info->deprecated; state->index++; @@ -498,7 +500,7 @@ void perf_pmus__print_pmu_events(const struct print_callbacks *print_cb, void *p aliases[j].alias, aliases[j].scale_unit, aliases[j].deprecated, - "Kernel PMU event", + aliases[j].event_type_desc, aliases[j].desc, aliases[j].long_desc, aliases[j].encoding_desc); @@ -511,6 +513,7 @@ free: zfree(&aliases[j].encoding_desc); zfree(&aliases[j].topic); zfree(&aliases[j].pmu_name); + zfree(&aliases[j].event_type_desc); } if (printed && pager_in_use()) printf("\n"); From eb9b9a6f5ab35db7a431184456fe410b792be03f Mon Sep 17 00:00:00 2001 From: Sam James Date: Sun, 8 Sep 2024 19:46:41 +0100 Subject: [PATCH 336/347] tools: Drop nonsensical -O6 -O6 is very much not-a-thing. Really, this should've been dropped entirely in 49b3cd306e60b9d8 ("tools: Set the maximum optimization level according to the compiler being used") instead of just passing it for not-Clang. Just collapse it down to -O3, instead of "-O6 unless Clang, in which case -O3". GCC interprets > -O3 as -O3. It doesn't even interpret > -O3 as -Ofast, which is a good thing, given -Ofast has specific (non-)requirements for code built using it. So, this does nothing except look a bit daft. Remove the silliness and also save a few lines in the Makefiles accordingly. Reviewed-by: Ian Rogers Reviewed-by: Jesper Juhl Signed-off-by: Sam James Acked-by: Namhyung Kim Cc: Adrian Hunter Cc: Alexander Shishkin Cc: Bill Wendling Cc: Ingo Molnar Cc: Jiri Olsa Cc: Justin Stitt Cc: Kan Liang Cc: Mark Rutland Cc: Nathan Chancellor Cc: Nick Desaulniers Cc: Peter Zijlstra Cc: llvm@lists.linux.dev Link: https://lore.kernel.org/r/4f01524fa4ea91c7146a41e26ceaf9dae4c127e4.1725821201.git.sam@gentoo.org Signed-off-by: Arnaldo Carvalho de Melo --- tools/lib/api/Makefile | 4 ---- tools/lib/subcmd/Makefile | 4 +--- tools/lib/symbol/Makefile | 4 ---- tools/perf/Makefile.config | 6 +----- 4 files changed, 2 insertions(+), 16 deletions(-) diff --git a/tools/lib/api/Makefile b/tools/lib/api/Makefile index 044860ac1ed1..7f6396087b46 100644 --- a/tools/lib/api/Makefile +++ b/tools/lib/api/Makefile @@ -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) diff --git a/tools/lib/subcmd/Makefile b/tools/lib/subcmd/Makefile index 59b09f280e49..8703ab487b68 100644 --- a/tools/lib/subcmd/Makefile +++ b/tools/lib/subcmd/Makefile @@ -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 diff --git a/tools/lib/symbol/Makefile b/tools/lib/symbol/Makefile index 13d43c6f92b4..426b845edfac 100644 --- a/tools/lib/symbol/Makefile +++ b/tools/lib/symbol/Makefile @@ -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) diff --git a/tools/perf/Makefile.config b/tools/perf/Makefile.config index 4c1f75e326e2..4dcf7a0fd235 100644 --- a/tools/perf/Makefile.config +++ b/tools/perf/Makefile.config @@ -233,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 From 9953807c9e016759c86ec0d0ab6bfe223e19f0ba Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Sun, 8 Sep 2024 13:28:47 -0700 Subject: [PATCH 337/347] perf evlist: Print hint for group An event group is a critical relationship. There is a -g option that can display the relationship. But it's hard for a user to know when should this option be applied. If there is an event group in the perf record, print a hint to suggest the user apply the -g to display the group information. With the patch, $ perf record -e "{cycles,instructions},instructions" sleep 1 [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.024 MB perf.data (4 samples) ] $ $ perf evlist cycles instructions instructions # Tip: use 'perf evlist -g' to show group information $ perf evlist -g {cycles,instructions} instructions $ Committer testing: So for a perf.data file _with_ a group: root@number:~# perf evlist -g {cpu_core/branch-instructions/pp,cpu_core/branches/} dummy:u root@number:~# perf evlist cpu_core/branch-instructions/pp cpu_core/branches/ dummy:u # Tip: use 'perf evlist -g' to show group information root@number:~# Then for something _without_ a group, no hint: root@number:~# perf record ls [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.035 MB perf.data (7 samples) ] root@number:~# perf evlist cpu_atom/cycles/P cpu_core/cycles/P dummy:u root@number:~# No suggestion, good. Suggested-by: Arnaldo Carvalho de Melo Reviewed-by: Ian Rogers Signed-off-by: Kan Liang Tested-by: Arnaldo Carvalho de Melo Cc: Adrian Hunter Cc: Jiri Olsa Cc: Namhyung Kim Closes: https://lore.kernel.org/lkml/ZttgvduaKsVn1r4p@x1/ Link: https://lore.kernel.org/r/20240908202847.176280-1-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-evlist.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c index 818ab21c3f73..a9bd7bbef5a9 100644 --- a/tools/perf/builtin-evlist.c +++ b/tools/perf/builtin-evlist.c @@ -36,7 +36,7 @@ static int __cmd_evlist(const char *file_name, struct perf_attr_details *details .force = details->force, }; struct perf_tool tool; - bool has_tracepoint = false; + bool has_tracepoint = false, has_group = false; perf_tool__init(&tool, /*ordered_events=*/false); /* only needed for pipe mode */ @@ -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; } From edf3ce0ed38e2d04a817984e4ea7f05b18102926 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Mon, 9 Sep 2024 11:42:00 -0700 Subject: [PATCH 338/347] perf env: Find correct branch counter info on hybrid No event is printed in the "Branch Counter" column on hybrid machines. For example, $ perf record -e "{cpu_core/branch-instructions/pp,cpu_core/branches/}:S" -j any,counter $ perf report --total-cycles # Branch counter abbr list: # cpu_core/branch-instructions/pp = A # cpu_core/branches/ = B # '-' No event occurs # '+' Event occurrences may be lost due to branch counter saturated # # Sampled Cycles% Sampled Cycles Avg Cycles% Avg Cycles Branch Counter # ............... .............. ........... .......... .............. 44.54% 727.1K 0.00% 1 |+ |+ | 36.31% 592.7K 0.00% 2 |+ |+ | 17.83% 291.1K 0.00% 1 |+ |+ | The branch counter information (br_cntr_width and br_cntr_nr) in the perf_env is retrieved from the CPU_PMU_CAPS. However, the CPU_PMU_CAPS is not available on hybrid machines. Without the width information, the number of occurrences of an event cannot be calculated. For a hybrid machine, the caps information should be retrieved from the PMU_CAPS, and stored in the perf_env->pmu_caps. Add a perf_env__find_br_cntr_info() to return the correct branch counter information from the corresponding fields. Committer notes: While testing I couldn't s ee those "Branch counter" columns enabled by pressing 'B' on the TUI, after reporting it to the list Kan explained the situation: For a hybrid client, the "Branch Counter" feature is only supported starting from the just released Lunar Lake. Perf falls back to only "ANY" on your Raptor Lake. The "The branch counter is not available" message is expected. Here is the 'perf evlist' result from my Lunar Lake machine, # perf evlist -v cpu_core/branch-instructions/pp: type: 4 (cpu_core), size: 136, config: 0xc4 (branch-instructions), { sample_period, sample_freq }: 4000, sample_type: IP|TID|TIME|READ|PERIOD|BRANCH_STACK|IDENTIFIER, read_format: ID|GROUP|LOST, disabled: 1, freq: 1, enable_on_exec: 1, precise_ip: 2, sample_id_all: 1, exclude_guest: 1, branch_sample_type: ANY|COUNTERS # Fixes: 6f9d8d1de2c61288 ("perf script: Add branch counters") Reviewed-by: Ian Rogers Signed-off-by: Kan Liang Cc: Adrian Hunter Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim Link: https://lore.kernel.org/r/20240909184201.553519-1-kan.liang@linux.intel.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-script.c | 5 +++-- tools/perf/util/annotate.c | 5 +++-- tools/perf/util/env.c | 15 +++++++++++++++ tools/perf/util/env.h | 3 +++ tools/perf/util/session.c | 6 ++++-- 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c index dbe792b52c5c..a644787fa9e1 100644 --- a/tools/perf/builtin-script.c +++ b/tools/perf/builtin-script.c @@ -1241,10 +1241,11 @@ static int ip__fprintf_jump(uint64_t ip, struct branch_entry *en, } if (PRINT_FIELD(BRCNTR)) { - unsigned int width = evsel__env(evsel)->br_cntr_width; - unsigned int i = 0, j, num, mask = (1L << width) - 1; 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)) diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 1a347a711dcf..37ce43c4eb8f 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -333,14 +333,15 @@ static int symbol__account_br_cntr(struct annotated_branch *branch, { unsigned int br_cntr_nr = evsel__leader(evsel)->br_cntr_nr; unsigned int base = evsel__leader(evsel)->br_cntr_idx; - unsigned int width = evsel__env(evsel)->br_cntr_width; unsigned int off = offset * evsel->evlist->nr_br_cntr; - unsigned int i, mask = (1L << width) - 1; u64 *branch_br_cntr = branch->br_cntr; + unsigned int i, mask, width; if (!br_cntr || !branch_br_cntr) return 0; + perf_env__find_br_cntr_info(evsel__env(evsel), NULL, &width); + mask = (1L << width) - 1; for (i = 0; i < br_cntr_nr; i++) { u64 cntr = (br_cntr >> i * width) & mask; diff --git a/tools/perf/util/env.c b/tools/perf/util/env.c index a459374d0a1a..1edbccfc3281 100644 --- a/tools/perf/util/env.c +++ b/tools/perf/util/env.c @@ -624,3 +624,18 @@ out: free(cap_eq); return NULL; } + +void perf_env__find_br_cntr_info(struct perf_env *env, + unsigned int *nr, + unsigned int *width) +{ + if (nr) { + *nr = env->cpu_pmu_caps ? env->br_cntr_nr : + env->pmu_caps->br_cntr_nr; + } + + if (width) { + *width = env->cpu_pmu_caps ? env->br_cntr_width : + env->pmu_caps->br_cntr_width; + } +} diff --git a/tools/perf/util/env.h b/tools/perf/util/env.h index 2a2c37cc40b7..51b36c36019b 100644 --- a/tools/perf/util/env.h +++ b/tools/perf/util/env.h @@ -192,4 +192,7 @@ char *perf_env__find_pmu_cap(struct perf_env *env, const char *pmu_name, const char *cap); bool perf_env__has_pmu_mapping(struct perf_env *env, const char *pmu_name); +void perf_env__find_br_cntr_info(struct perf_env *env, + unsigned int *nr, + unsigned int *width); #endif /* __PERF_ENV_H */ diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index b492300ec959..dbaf07bf6c5f 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -856,7 +856,6 @@ static void branch_stack__printf(struct perf_sample *sample, struct branch_entry *entries = perf_sample__branch_entries(sample); bool callstack = evsel__has_branch_callstack(evsel); u64 *branch_stack_cntr = sample->branch_stack_cntr; - struct perf_env *env = evsel__env(evsel); uint64_t i; if (!callstack) { @@ -900,8 +899,11 @@ static void branch_stack__printf(struct perf_sample *sample, } if (branch_stack_cntr) { + unsigned int br_cntr_width, br_cntr_nr; + + perf_env__find_br_cntr_info(evsel__env(evsel), &br_cntr_nr, &br_cntr_width); printf("... branch stack counters: nr:%" PRIu64 " (counter width: %u max counter nr:%u)\n", - sample->branch_stack->nr, env->br_cntr_width, env->br_cntr_nr); + sample->branch_stack->nr, br_cntr_width, br_cntr_nr); for (i = 0; i < sample->branch_stack->nr; i++) printf("..... %2"PRIu64": %016" PRIx64 "\n", i, branch_stack_cntr[i]); } From 21ff365b5c88c0bf8447989aadb5d8fe401c9cfc Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Tue, 10 Sep 2024 17:23:34 +0100 Subject: [PATCH 339/347] selftests/ring-buffer: Verify the entire meta-page padding Improve the ring-buffer meta-page test coverage by checking for the entire padding region to be 0 instead of just looking at the first 4 bytes. Cc: linux-kselftest@vger.kernel.org Link: https://lore.kernel.org/20240910162335.2993310-2-vdonnefort@google.com Acked-by: Shuah Khan Signed-off-by: Vincent Donnefort Signed-off-by: Steven Rostedt (Google) --- tools/testing/selftests/ring-buffer/map_test.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/ring-buffer/map_test.c b/tools/testing/selftests/ring-buffer/map_test.c index 4bb0192e43f3..ba12fd31de87 100644 --- a/tools/testing/selftests/ring-buffer/map_test.c +++ b/tools/testing/selftests/ring-buffer/map_test.c @@ -231,15 +231,15 @@ TEST_F(map, data_mmap) /* Verify meta-page padding */ if (desc->meta->meta_page_size > getpagesize()) { - void *addr; - data_len = desc->meta->meta_page_size; data = mmap(NULL, data_len, PROT_READ, MAP_SHARED, desc->cpu_fd, 0); ASSERT_NE(data, MAP_FAILED); - addr = (void *)((unsigned long)data + getpagesize()); - ASSERT_EQ(*((int *)addr), 0); + for (int i = desc->meta->meta_struct_len; + i < desc->meta->meta_page_size; i += sizeof(int)) + ASSERT_EQ(*(int *)(data + i), 0); + munmap(data, data_len); } } From 75d7ff9aa0ae1a8d1b3f9c8c87dde3a4fbe9a2cf Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Tue, 10 Sep 2024 17:23:35 +0100 Subject: [PATCH 340/347] selftests/ring-buffer: Handle meta-page bigger than the system Handle the case where the meta-page content is bigger than the system page-size. This prepares the ground for extending features covered by the meta-page. Cc: Shuah Khan Cc: linux-kselftest@vger.kernel.org Link: https://lore.kernel.org/20240910162335.2993310-3-vdonnefort@google.com Acked-by: Shuah Khan Signed-off-by: Vincent Donnefort Signed-off-by: Steven Rostedt (Google) --- tools/testing/selftests/ring-buffer/map_test.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/testing/selftests/ring-buffer/map_test.c b/tools/testing/selftests/ring-buffer/map_test.c index ba12fd31de87..d10a847130fb 100644 --- a/tools/testing/selftests/ring-buffer/map_test.c +++ b/tools/testing/selftests/ring-buffer/map_test.c @@ -92,12 +92,22 @@ int tracefs_cpu_map(struct tracefs_cpu_map_desc *desc, int cpu) if (desc->cpu_fd < 0) return -ENODEV; +again: map = mmap(NULL, page_size, PROT_READ, MAP_SHARED, desc->cpu_fd, 0); if (map == MAP_FAILED) return -errno; desc->meta = (struct trace_buffer_meta *)map; + /* the meta-page is bigger than the original mapping */ + if (page_size < desc->meta->meta_struct_len) { + int meta_page_size = desc->meta->meta_page_size; + + munmap(desc->meta, page_size); + page_size = meta_page_size; + goto again; + } + return 0; } From 0c1019e3463b263a89e71d3b4543c28408ebe9a1 Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 11 Sep 2024 16:34:16 -0300 Subject: [PATCH 341/347] perf trace: Mark the 'rseq' arg in the rseq syscall as coming from user space With that it uses the generic BTF based pretty printer: root@number:~# grep -w rseq /sys/kernel/tracing/events/syscalls/sys_enter_rseq/format field:struct rseq * rseq; offset:16; size:8; signed:0; print fmt: "rseq: 0x%08lx, rseq_len: 0x%08lx, flags: 0x%08lx, sig: 0x%08lx", ((unsigned long)(REC->rseq)), ((unsigned long)(REC->rseq_len)), ((unsigned long)(REC->flags)), ((unsigned long)(REC->sig)) root@number:~# Before: root@number:~# perf trace -e rseq 0.000 ( 0.017 ms): Isolated Web C/1195452 rseq(rseq: 0x7ff0ecfe6fe0, rseq_len: 32, sig: 1392848979) = 0 74.018 ( 0.006 ms): :1195453/1195453 rseq(rseq: 0x7f2af20fffe0, rseq_len: 32, sig: 1392848979) = 0 1817.220 ( 0.009 ms): Isolated Web C/1195454 rseq(rseq: 0x7f5c9ec7dfe0, rseq_len: 32, sig: 1392848979) = 0 2515.526 ( 0.034 ms): :1195455/1195455 rseq(rseq: 0x7f61503fffe0, rseq_len: 32, sig: 1392848979) = 0 ^Croot@number:~# After: root@number:~# perf trace -e rseq 0.000 ( 0.019 ms): Isolated Web C/1197258 rseq(rseq: (struct rseq){.cpu_id_start = (__u32)4,.cpu_id = (__u32)4,.mm_cid = (__u32)5,}, rseq_len: 32, sig: 1392848979) = 0 1663.835 ( 0.019 ms): Isolated Web C/1197259 rseq(rseq: (struct rseq){.cpu_id_start = (__u32)24,.cpu_id = (__u32)24,.mm_cid = (__u32)2,}, rseq_len: 32, sig: 1392848979) = 0 4750.444 ( 0.018 ms): Isolated Web C/1197260 rseq(rseq: (struct rseq){.cpu_id_start = (__u32)8,.cpu_id = (__u32)8,.mm_cid = (__u32)4,}, rseq_len: 32, sig: 1392848979) = 0 4994.132 ( 0.018 ms): Isolated Web C/1197261 rseq(rseq: (struct rseq){.cpu_id_start = (__u32)10,.cpu_id = (__u32)10,.mm_cid = (__u32)1,}, rseq_len: 32, sig: 1392848979) = 0 4997.578 ( 0.011 ms): Isolated Web C/1197263 rseq(rseq: (struct rseq){.cpu_id_start = (__u32)16,.cpu_id = (__u32)16,.mm_cid = (__u32)4,}, rseq_len: 32, sig: 1392848979) = 0 4997.462 ( 0.014 ms): Isolated Web C/1197262 rseq(rseq: (struct rseq){.cpu_id_start = (__u32)17,.cpu_id = (__u32)17,.mm_cid = (__u32)3,}, rseq_len: 32, sig: 1392848979) = 0 ^Croot@number:~# We'll probably need to come up with some way for using the BTF info to synthesize a test that then gets used and captures the output of the 'perf trace' output to check if the arguments are the ones synthesized, randomically, for now, lets make do manually: root@number:~# cat ~acme/c/rseq.c #include /* Definition of SYS_* constants */ #include #include #include #include #include #include /* Provide own rseq stub because glibc doesn't */ __attribute__((weak)) int sys_rseq(struct rseq *rseq, __u32 rseq_len, int flags, __u32 sig) { return syscall(SYS_rseq, rseq, rseq_len, flags, sig); } int main(int argc, char *argv[]) { struct rseq rseq = { .cpu_id_start = 12, .cpu_id = 34, .rseq_cs = 56, .flags = 78, .node_id = 90, .mm_cid = 12, }; int err = sys_rseq(&rseq, sizeof(rseq), 98765, 0xdeadbeaf); printf("sys_rseq({ .cpu_id_start = 12, .cpu_id = 34, .rseq_cs = 56, .flags = 78, .node_id = 90, .mm_cid = 12, }, %d, 0) = %d (%s)\n", sizeof(rseq), err, strerror(errno)); return err; } root@number:~# perf trace -e rseq ~acme/c/rseq sys_rseq({ .cpu_id_start = 12, .cpu_id = 34, .rseq_cs = 56, .flags = 78, .node_id = 90, .mm_cid = 12, }, 32, 0) = -1 (Invalid argument) 0.000 ( 0.003 ms): rseq/1200640 rseq(rseq: (struct rseq){}, rseq_len: 32, sig: 1392848979) = 0.064 ( 0.001 ms): rseq/1200640 rseq(rseq: (struct rseq){.cpu_id_start = (__u32)12,.cpu_id = (__u32)34,.rseq_cs = (__u64)56,.flags = (__u32)78,.node_id = (__u32)90,.mm_cid = (__u32)12,}, rseq_len: 32, flags: 98765, sig: 3735928495) = -1 EINVAL (Invalid argument) root@number:~#root@number:~# cat ~acme/c/rseq.c #include /* Definition of SYS_* constants */ #include #include #include #include #include #include /* Provide own rseq stub because glibc doesn't */ __attribute__((weak)) int sys_rseq(struct rseq *rseq, __u32 rseq_len, int flags, __u32 sig) { return syscall(SYS_rseq, rseq, rseq_len, flags, sig); } int main(int argc, char *argv[]) { struct rseq rseq = { .cpu_id_start = 12, .cpu_id = 34, .rseq_cs = 56, .flags = 78, .node_id = 90, .mm_cid = 12, }; int err = sys_rseq(&rseq, sizeof(rseq), 98765, 0xdeadbeaf); printf("sys_rseq({ .cpu_id_start = 12, .cpu_id = 34, .rseq_cs = 56, .flags = 78, .node_id = 90, .mm_cid = 12, }, %d, 0) = %d (%s)\n", sizeof(rseq), err, strerror(errno)); return err; } root@number:~# perf trace -e rseq ~acme/c/rseq sys_rseq({ .cpu_id_start = 12, .cpu_id = 34, .rseq_cs = 56, .flags = 78, .node_id = 90, .mm_cid = 12, }, 32, 0) = -1 (Invalid argument) 0.000 ( 0.003 ms): rseq/1200640 rseq(rseq: (struct rseq){}, rseq_len: 32, sig: 1392848979) = 0.064 ( 0.001 ms): rseq/1200640 rseq(rseq: (struct rseq){.cpu_id_start = (__u32)12,.cpu_id = (__u32)34,.rseq_cs = (__u64)56,.flags = (__u32)78,.node_id = (__u32)90,.mm_cid = (__u32)12,}, rseq_len: 32, flags: 98765, sig: 3735928495) = -1 EINVAL (Invalid argument) root@number:~# Interesting, glibc seems to be using rseq here, as in addition to the totally fake one this test case uses, we have this one, around these other syscalls: 0.175 ( 0.001 ms): rseq/1201095 set_tid_address(tidptr: 0x7f6def759a10) = 1201095 (rseq) 0.177 ( 0.001 ms): rseq/1201095 set_robust_list(head: 0x7f6def759a20, len: 24) = 0 0.178 ( 0.001 ms): rseq/1201095 rseq(rseq: (struct rseq){}, rseq_len: 32, sig: 1392848979) = 0.231 ( 0.005 ms): rseq/1201095 mprotect(start: 0x7f6def93f000, len: 16384, prot: READ) = 0 0.238 ( 0.003 ms): rseq/1201095 mprotect(start: 0x403000, len: 4096, prot: READ) = 0 0.244 ( 0.004 ms): rseq/1201095 mprotect(start: 0x7f6def99c000, len: 8192, prot: READ) Matches strace (well, not really as the strace in fedora:40 doesn't know about rseq, printing just integer values in hex): set_robust_list(0x7fbc6acc7a20, 24) = 0 rseq(0x7fbc6acc8060, 0x20, 0, 0x53053053) = 0 mprotect(0x7fbc6aead000, 16384, PROT_READ) = 0 mprotect(0x403000, 4096, PROT_READ) = 0 mprotect(0x7fbc6af0a000, 8192, PROT_READ) = 0 prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0 munmap(0x7fbc6aebd000, 81563) = 0 rseq(0x7fff15bb9920, 0x20, 0x181cd, 0xdeadbeaf) = -1 EINVAL (Invalid argument) fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x9), ...}) = 0 getrandom("\xd0\x34\x97\x17\x61\xc2\x2b\x10", 8, GRND_NONBLOCK) = 8 brk(NULL) = 0x18ff4000 brk(0x19015000) = 0x19015000 write(1, "sys_rseq({ .cpu_id_start = 12, ."..., 136sys_rseq({ .cpu_id_start = 12, .cpu_id = 34, .rseq_cs = 56, .flags = 78, .node_id = 90, .mm_cid = 12, }, 32, 0) = -1 (Invalid argument) ) = 136 exit_group(-1) = ? +++ exited with 255 +++ root@number:~# And also the focus for the v6.13 should be to have a better, strace like BTF pretty printer as one of the outputs we can get from the libbpf BTF dumper. Cc: Adrian Hunter Cc: Alan Maguire Cc: Howard Chu Cc: Jiri Olsa Cc: Kan Liang Cc: Mathieu Desnoyers Cc: Namhyung Kim Link: https://lore.kernel.org/lkml/ZuH2K1LLt1pIDkbd@x1 Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/builtin-trace.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 3b88ba50bb80..27084ae38bc4 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1327,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", From 1de5b5dcb8353f36581c963df2d359a5f151a0be Mon Sep 17 00:00:00 2001 From: Arnaldo Carvalho de Melo Date: Wed, 11 Sep 2024 17:10:33 -0300 Subject: [PATCH 342/347] perf trace: Mark the 'head' arg in the set_robust_list syscall as coming from user space With that it uses the generic BTF based pretty printer: This one we need to think about, not being acquainted with this syscall, should we _traverse_ that list somehow? Would that be useful? root@number:~# perf trace -e set_robust_list sleep 1 0.000 ( 0.004 ms): sleep/1206493 set_robust_list(head: (struct robust_list_head){.list = (struct robust_list){.next = (struct robust_list *)0x7f48a9a02a20,},.futex_offset = (long int)-32,}, len: 24) = root@number:~# strace prints the default integer args: root@number:~# strace -e set_robust_list sleep 1 set_robust_list(0x7efd99559a20, 24) = 0 +++ exited with 0 +++ root@number:~# Cc: Adrian Hunter Cc: Alan Maguire Cc: Howard Chu Cc: Jiri Olsa Cc: Kan Liang Cc: Namhyung Kim --- tools/perf/builtin-trace.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c index 27084ae38bc4..f6e847529073 100644 --- a/tools/perf/builtin-trace.c +++ b/tools/perf/builtin-trace.c @@ -1351,6 +1351,8 @@ static const struct syscall_fmt syscall_fmts[] = { { .name = "sendto", .arg = { [3] = { .scnprintf = SCA_MSG_FLAGS, /* flags */ }, [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), }, }, From 673a5009cf2f020dac440cd79e70c4c8b8e20d08 Mon Sep 17 00:00:00 2001 From: Kan Liang Date: Thu, 12 Sep 2024 07:50:25 -0700 Subject: [PATCH 343/347] perf: Fix topology_sibling_cpumask check warning on ARM The below warning is triggered when building with arm multi_v7_defconfig. kernel/events/core.c: In function 'perf_event_setup_cpumask': kernel/events/core.c:14012:13: warning: the comparison will always evaluate as 'true' for the address of 'thread_sibling' will never be NULL [-Waddress] 14012 | if (!topology_sibling_cpumask(cpu)) { The perf_event_init_cpu() may be invoked at the early boot stage, while the topology_*_cpumask hasn't been initialized yet. The check is to specially handle the case, and initialize the perf_online__masks on the boot CPU. X86 uses a per-cpu cpumask pointer, which could be NULL at the early boot stage. However, ARM uses a global variable, which never be NULL. Use perf_online_mask as an indicator instead. Only initialize the perf_online__masks when perf_online_mask is empty. Fix a typo as well. Fixes: 4ba4f1afb6a9 ("perf: Generic hotplug support for a PMU with a scope") Reported-by: Stephen Rothwell Closes: https://lore.kernel.org/lkml/20240911153854.240bbc1f@canb.auug.org.au/ Reported-by: Steven Price Closes: https://lore.kernel.org/lkml/1835eb6d-3e05-47f3-9eae-507ce165c3bf@arm.com/ Signed-off-by: Kan Liang Tested-by: Steven Price Signed-off-by: Linus Torvalds --- kernel/events/core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 0016f5ff9ba2..5afde9f7b52a 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -14002,21 +14002,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__masks includes the first CPU of each domain. - * Always uncondifionally set the boot CPU for the perf_online__masks. + * Always unconditionally set the boot CPU for the perf_online__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++) { @@ -14031,6 +14029,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) From 533ab223aa1a036cfe5d6747fa3be92069f80988 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 22 Sep 2024 10:55:42 -0700 Subject: [PATCH 344/347] x86: make the masked_user_access_begin() macro use its argument only once This doesn't actually matter for any of the current users, but before merging it mainline, make sure we don't have any surprising semantics. We don't actually want to use an inline function here, because we want to allow - but not require - const pointer arguments, and return them as such. But we already had a local auto-type variable, so let's just use it to avoid any possible double evaluation. Signed-off-by: Linus Torvalds --- arch/x86/include/asm/uaccess_64.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h index 92859c1ef59c..afce8ee5d7b7 100644 --- a/arch/x86/include/asm/uaccess_64.h +++ b/arch/x86/include/asm/uaccess_64.h @@ -59,8 +59,9 @@ static inline unsigned long __untagged_addr_remote(struct mm_struct *mm, * 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 = mask_user_address(x); \ +#define masked_user_access_begin(x) ({ \ + __auto_type __masked_ptr = (x); \ + __masked_ptr = mask_user_address(__masked_ptr); \ __uaccess_begin(); __masked_ptr; }) /* From d0dd066a0fa26d55c19ace9e89dedd9504c5bcba Mon Sep 17 00:00:00 2001 From: "Christoph Lameter (Ampere)" Date: Wed, 12 Jun 2024 09:49:56 -0700 Subject: [PATCH 345/347] seqcount: replace smp_rmb() in read_seqcount() with load acquire Many architectures support load acquire which can replace a memory barrier and save some cycles. A typical sequence do { seq = read_seqcount_begin(&s); } while (read_seqcount_retry(&s, seq); requires 13 cycles on an N1 Neoverse arm64 core (Ampere Altra, to be specific) for an empty loop. Two read memory barriers are needed. One for each of the seqcount_* functions. We can replace the first read barrier with a load acquire of the seqcount which saves us one barrier. On the Altra doing so reduces the cycle count from 13 to 8. According to ARM, this is a general improvement for the ARM64 architecture and not specific to a certain processor. See https://developer.arm.com/documentation/102336/0100/Load-Acquire-and-Store-Release-instructions "Weaker ordering requirements that are imposed by Load-Acquire and Store-Release instructions allow for micro-architectural optimizations, which could reduce some of the performance impacts that are otherwise imposed by an explicit memory barrier. If the ordering requirement is satisfied using either a Load-Acquire or Store-Release, then it would be preferable to use these instructions instead of a DMB" [ NOTE! This is my original minimal patch that unconditionally switches over to using smp_load_acquire(), instead of the much more involved and subtle patch that Christoph Lameter wrote that made it conditional. But Christoph gets authorship credit because I had initially thought that we needed the more complex model, and Christoph ran with it it and did the work. Only after looking at code generation for all the relevant architectures, did I come to the conclusion that nobody actually really needs the old "smp_rmb()" model. Even architectures without load-acquire support generally do as well or better with smp_load_acquire(). So credit to Christoph, but if this then causes issues on other architectures, put the blame solidly on me. Also note as part of the ruthless simplification, this gets rid of the overly subtle optimization where some code uses a non-barrier version of the sequence count (see the __read_seqcount_begin() users in fs/namei.c). They then play games with their own barriers and/or with nested sequence counts. Those optimizations are literally meaningless on x86, and questionable elsewhere. If somebody can show that they matter, we need to re-do them more cleanly than "use an internal helper". - Linus ] Signed-off-by: Christoph Lameter (Ampere) Link: https://lore.kernel.org/all/20240912-seq_optimize-v3-1-8ee25e04dffa@gentwo.org/ Signed-off-by: Linus Torvalds --- include/linux/seqlock.h | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/include/linux/seqlock.h b/include/linux/seqlock.h index d90d8ee29d81..fffeb754880f 100644 --- a/include/linux/seqlock.h +++ b/include/linux/seqlock.h @@ -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; \ }) From 9631042b91c359f7f1eda8f30c0932ef90bc4089 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Mon, 23 Sep 2024 06:52:35 -0700 Subject: [PATCH 346/347] hexagon: vdso: Fix build failure Hexagon images fail to build with the following error. arch/hexagon/kernel/vdso.c:57:3: error: use of undeclared identifier 'name' name = "[vdso]", ^ Add the missing '.' to fix the problem. Fixes: 497258dfafcc ("mm: remove legacy install_special_mapping() code") Cc: Linus Torvalds Signed-off-by: Guenter Roeck Reviewed-by: Brian Cain Signed-off-by: Linus Torvalds --- arch/hexagon/kernel/vdso.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/hexagon/kernel/vdso.c b/arch/hexagon/kernel/vdso.c index 6fd27ff1df73..8119084dc519 100644 --- a/arch/hexagon/kernel/vdso.c +++ b/arch/hexagon/kernel/vdso.c @@ -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)) From f8eb5bd9a818cc5f2a1e50b22b0091830b28cc36 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 23 Sep 2024 08:58:31 -0700 Subject: [PATCH 347/347] mm: fix build on 32-bit targets without MAX_PHYSMEM_BITS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The merge resolution to deal with the conflict between commits ea72ce5da228 ("x86/kaslr: Expose and use the end of the physical memory address space") and 99185c10d5d9 ("resource, kunit: add test case for region_intersects()") ended up being broken in configurations didn't define a MAX_PHYSMEM_BITS and that had a 32-bit 'phys_addr_t'. The fallback to using all bits set (ie "(-1ULL)") ended up causing a build error: kernel/resource.c: In function ‘gfr_start’: include/linux/minmax.h:93:30: error: conversion from ‘long long unsigned int’ to ‘resource_size_t’ {aka ‘unsigned int’} changes value from ‘18446744073709551615’ to ‘4294967295’ [-Werror=overflow] this was reported by Geert for m68k, but he points out that it happens on other 32-bit architectures too, eg mips, xtensa, parisc, and powerpc. Limiting 'PHYSMEM_END' to a 'phys_addr_t' (which is the same as 'resource_size_t') fixes the build, but Geert points out that it will then cause a silent overflow in mm/sparse.c: unsigned long max_sparsemem_pfn = (PHYSMEM_END + 1) >> PAGE_SHIFT; so we actually do want PHYSMEM_END to be defined a 64-bit type - just not all ones, and not larger than 'phys_addr_t'. The proper fix is probably to not have some kind of default fallback at all, but just make sure every architecture has a valid MAX_PHYSMEM_BITS. But in the meantime, this just applies the rule that PHYSMEM_END is the largest value that fits in a 'phys_addr_t', but does not have the high bit set in 64 bits. Ugly, ugly. Reported-by: Geert Uytterhoeven Cc: Andrew Morton Cc: Huang Ying Cc: Thomas Gleixner Signed-off-by: Linus Torvalds --- include/linux/mm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/mm.h b/include/linux/mm.h index b62437447077..ecf63d2b0582 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -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