perf/core improvements and fixes
Arnaldo Carvalho de Melo
maintainerHi Ingo,
Please consider pulling,
- Arnaldo
The following changes since commit daa01794a4a36a1da1b09a529adec0c8c0b94ab2:
perf evsel: Do not call pevent_free_format when deleting tracepoint (2014-11-06 17:47:14 -0300)
are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.git tags/perf-core-for-mingo
for you to fetch changes up to daa01794a4a36a1da1b09a529adec0c8c0b94ab2:
perf evsel: Do not call pevent_free_format when deleting tracepoint (2014-11-06 17:47:14 -0300)
----------------------------------------------------------------
perf/core improvements and fixes:
Infrastructure:
o Add gzip decompression support for kernel modules (Namhyung Kim)
o More prep patches for Intel PT, including a a thread stack and more stuff made available via the database export mechanism (Adrian Hunter)
o Optimize checking that tracepoint events are defined in perf script perl/python (Jiri Olsa)
o Do not free pevent when deleting tracepoint evsel (Jiri Olsa)
o Fix build-id matching for vmlinux (Namhyung Kim)
----------------------------------------------------------------
Arnaldo Carvalho de Melo
maintainerPATCH 1/18 perf tools: Add a thread stack for synthesizing call chains
From: Adrian Hunter <[email protected]>
Add a thread stack for synthesizing call chains from call and return events.
--- tools/perf/Makefile.perf | 2 + tools/perf/util/event.h | 26 +++++++ tools/perf/util/thread-stack.c | 172 +++++++++++++++++++++++++++++++++++++++++ tools/perf/util/thread-stack.h | 32 ++++++++ tools/perf/util/thread.c | 3 + tools/perf/util/thread.h | 3 + 6 files changed, 238 insertions(+) create mode 100644 tools/perf/util/thread-stack.c create mode 100644 tools/perf/util/thread-stack.h
tools/perf/Makefile.perf +2 -0
@@ -317,6 +317,7 @@ LIB_H += ui/util.h LIB_H += ui/ui.h LIB_H += util/data.h LIB_H += util/kvm-stat.h +LIB_H += util/thread-stack.h LIB_OBJS += $(OUTPUT)util/abspath.o LIB_OBJS += $(OUTPUT)util/alias.o
@@ -394,6 +395,7 @@ LIB_OBJS += $(OUTPUT)util/srcline.o LIB_OBJS += $(OUTPUT)util/data.o LIB_OBJS += $(OUTPUT)util/tsc.o LIB_OBJS += $(OUTPUT)util/cloexec.o +LIB_OBJS += $(OUTPUT)util/thread-stack.o LIB_OBJS += $(OUTPUT)ui/setup.o LIB_OBJS += $(OUTPUT)ui/helpline.o
tools/perf/util/event.h +26 -0
@@ -143,6 +143,32 @@ struct branch_stack {struct branch_entry entries[0 ]; }; +enum { +PERF_IP_FLAG_BRANCH =1ULL <<0 , +PERF_IP_FLAG_CALL =1ULL <<1 , +PERF_IP_FLAG_RETURN =1ULL <<2 , +PERF_IP_FLAG_CONDITIONAL =1ULL <<3 , +PERF_IP_FLAG_SYSCALLRET =1ULL <<4 , +PERF_IP_FLAG_ASYNC =1ULL <<5 , +PERF_IP_FLAG_INTERRUPT =1ULL <<6 , +PERF_IP_FLAG_TX_ABORT =1ULL <<7 , +PERF_IP_FLAG_TRACE_BEGIN =1ULL <<8 , +PERF_IP_FLAG_TRACE_END =1ULL <<9 , +PERF_IP_FLAG_IN_TX =1ULL <<10 , +}; + +#define PERF_BRANCH_MASK (\ + PERF_IP_FLAG_BRANCH |\ + PERF_IP_FLAG_CALL |\ + PERF_IP_FLAG_RETURN |\ + PERF_IP_FLAG_CONDITIONAL |\ + PERF_IP_FLAG_SYSCALLRET |\ + PERF_IP_FLAG_ASYNC |\ + PERF_IP_FLAG_INTERRUPT |\ + PERF_IP_FLAG_TX_ABORT |\ + PERF_IP_FLAG_TRACE_BEGIN |\ + PERF_IP_FLAG_TRACE_END) +struct perf_sample {u64 ip;u32 pid, tid;
tools/perf/util/thread-stack.c new file +172 -0
172 lines not on this page · raw message
tools/perf/util/thread-stack.h new file +32 -0
@@ -0,0 +1,32 @@ +/* +* thread-stack.h: Synthesize a thread's stack using call / return events +* Copyright (c) 2014, Intel Corporation. +* +* This program is free software; you can redistribute it and/or modify it +* under the terms and conditions of the GNU General Public License, +* version 2, as published by the Free Software Foundation. +* +* This program is distributed in the hope it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +*/ + +#ifndef __PERF_THREAD_STACK_H +#define __PERF_THREAD_STACK_H + +#include <sys/types.h> + +#include <linux/types.h> + +struct thread ; +struct ip_callchain ; + +int thread_stack__event (struct thread *thread,u32 flags,u64 from_ip, +u64 to_ip,u16 insn_len,u64 trace_nr); +void thread_stack__sample (struct thread *thread,struct ip_callchain *chain, +size_t sz,u64 ip); +void thread_stack__free (struct thread *thread); + +#endif
tools/perf/util/thread.c +3 -0
@@ -4,6 +4,7 @@#include <string.h> #include "session.h" #include "thread.h" +#include "thread-stack.h" #include "util.h" #include "debug.h" #include "comm.h"
@@ -66,6 +67,8 @@ void thread__delete(struct thread *thread) {struct comm *comm, *tmp; +thread_stack__free (thread); +if (thread->mg) {map_groups__put (thread->mg); thread->mg =NULL ;
tools/perf/util/thread.h +3 -0
@@ -8,6 +8,8 @@#include "symbol.h" #include <strlist.h> +struct thread_stack ; + struct thread { union { struct rb_node rb_node;
@@ -26,6 +28,7 @@ struct thread {u64 db_id;void *priv; +struct thread_stack *ts; };struct machine ;
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 2/18 perf tools: Enhance the thread stack to output call/return data
In reply to Arnaldo Carvalho de MeloFrom: Adrian Hunter <[email protected]>
Enhance the thread stack to output detailed information about paired calls and returns.
The enhanced processing consumes sample information via thread_stack__process() and outputs information about paired calls / returns via a call-back.
While the call-back makes it possible for the facility to be used by arbitrary tools, a subsequent patch will provide the information to Python scripting via the db-export interface.
An important part of the call/return information is the call path which provides a structure that defines a context sensitive call graph.
Note that there are now two ways to use the thread stack.
For simply providing a call stack (like you would get from the perf record -g option) the interface consists of thread_stack__event() and thread_stack__sample().
Whereas the enhanced interface consists of call_return_processor__new() and thread_stack__process().
--- tools/perf/util/thread-stack.c | 585 ++++++++++++++++++++++++++++++++++++++++- tools/perf/util/thread-stack.h | 79 ++++++ 2 files changed, 659 insertions(+), 5 deletions(-)
tools/perf/util/thread-stack.c +580 -5
@@ -13,23 +13,96 @@* */ +#include <linux/rbtree.h> +#include <linux/list.h> #include "thread.h" #include "event.h" +#include "machine.h" #include "util.h" #include "debug.h" +#include "symbol.h" +#include "comm.h" #include "thread-stack.h" -#define STACK_GROWTH 4096 +#define CALL_PATH_BLOCK_SHIFT 8 +#define CALL_PATH_BLOCK_SIZE (1 << CALL_PATH_BLOCK_SHIFT) +#define CALL_PATH_BLOCK_MASK (CALL_PATH_BLOCK_SIZE - 1) +struct call_path_block { +struct call_path cp[CALL_PATH_BLOCK_SIZE ]; +struct list_head node; +}; + +/** +* struct call_path_root - root of all call paths. +* @call_path: root call path +* @blocks: list of blocks to store call paths +* @next: next free space +* @sz: number of spaces +*/ +struct call_path_root { +struct call_path call_path; +struct list_head blocks; +size_t next; +size_t sz; +}; + +/** +* struct call_return_processor - provides a call-back to consume call-return +* information. +* @cpr: call path root +* @process: call-back that accepts call/return information +* @data: anonymous data for call-back +*/ +struct call_return_processor { +struct call_path_root *cpr; +int (*process)(struct call_return *cr,void *data); +void *data; +}; + +#define STACK_GROWTH 2048 + +/** +* struct thread_stack_entry - thread stack entry. +* @ret_addr: return address +* @timestamp: timestamp (if known) +* @ref: external reference (e.g. db_id of sample) +* @branch_count: the branch count when the entry was created +* @cp: call path +* @no_call: a 'call' was not seen +*/ struct thread_stack_entry {u64 ret_addr; +u64 timestamp; +u64 ref; +u64 branch_count; +struct call_path *cp; +bool no_call; }; +/** +* struct thread_stack - thread stack constructed from 'call' and 'return' +* branch samples. +* @stack: array that holds the stack +* @cnt: number of entries in the stack +* @sz: current maximum stack size +* @trace_nr: current trace number +* @branch_count: running branch count +* @kernel_start: kernel start address +* @last_time: last timestamp +* @crp: call/return processor +* @comm: current comm +*/ struct thread_stack {struct thread_stack_entry *stack;size_t cnt;size_t sz;u64 trace_nr; +u64 branch_count; +u64 kernel_start; +u64 last_time; +struct call_return_processor *crp; +struct comm *comm; };static int thread_stack__grow (struct thread_stack *ts)
@@ -50,7 +123,8 @@ static int thread_stack__grow(struct thread_stack *ts)return 0 ; } -static struct thread_stack *thread_stack__new (void ) +static struct thread_stack *thread_stack__new (struct thread *thread, +struct call_return_processor *crp) {struct thread_stack *ts;
@@ -63,6 +137,12 @@ static struct thread_stack *thread_stack__new(void)return NULL ; } +if (thread->mg && thread->mg->machine) + ts->kernel_start =machine__kernel_start (thread->mg->machine); +else + ts->kernel_start =1ULL <<63 ; + ts->crp = crp; +return ts; }
@@ -104,6 +184,64 @@ static void thread_stack__pop(struct thread_stack *ts, u64 ret_addr) } } +static bool thread_stack__in_kernel (struct thread_stack *ts) +{ +if (!ts->cnt) +return false; + +return ts->stack[ts->cnt -1 ].cp->in_kernel; +} + +static int thread_stack__call_return (struct thread *thread, +struct thread_stack *ts,size_t idx, +u64 timestamp,u64 ref,bool no_return) +{ +struct call_return_processor *crp = ts->crp; +struct thread_stack_entry *tse; +struct call_return cr = { + .thread = thread, + .comm = ts->comm, + .db_id =0 , + }; + + tse = &ts->stack[idx]; + cr.cp = tse->cp; + cr.call_time = tse->timestamp; + cr.return_time = timestamp; + cr.branch_count = ts->branch_count - tse->branch_count; + cr.call_ref = tse->ref; + cr.return_ref = ref; +if (tse->no_call) + cr.flags |=CALL_RETURN_NO_CALL ; +if (no_return) + cr.flags |=CALL_RETURN_NO_RETURN ; + +return crp->process (&cr, crp->data); +} + +static int thread_stack__flush (struct thread *thread,struct thread_stack *ts) +{ +struct call_return_processor *crp = ts->crp; +int err; + +if (!crp) { + ts->cnt =0 ; +return 0 ; + } + +while (ts->cnt) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + ts->last_time,0 , true); +if (err) { +pr_err ("Error flushing thread stack!\n" ); + ts->cnt =0 ; +return err; + } + } + +return 0 ; +} +int thread_stack__event (struct thread *thread,u32 flags,u64 from_ip,u64 to_ip,u16 insn_len,u64 trace_nr) {
@@ -111,7 +249,7 @@ int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,return -EINVAL ;if (!thread->ts) { - thread->ts =thread_stack__new (); + thread->ts =thread_stack__new (thread,NULL );if (!thread->ts) {pr_warning ("Out of memory: no thread stack\n" );return -ENOMEM ;
@@ -122,13 +260,18 @@ int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,/* * When the trace is discontinuous, the trace_nr changes. In that case * the stack might be completely invalid. Better to report nothing than -* to report something misleading, so reset the stack count to zero. +* to report something misleading, so flush the stack. */ if (trace_nr != thread->ts->trace_nr) { +if (thread->ts->trace_nr) +thread_stack__flush (thread, thread->ts); thread->ts->trace_nr = trace_nr; - thread->ts->cnt =0 ; } +/* Stop here if thread_stack__process() is in use */ +if (thread->ts->crp) +return 0 ; +if (flags &PERF_IP_FLAG_CALL ) {u64 ret_addr;
@@ -147,9 +290,22 @@ int thread_stack__event(struct thread *thread, u32 flags, u64 from_ip,return 0 ; } +void thread_stack__set_trace_nr (struct thread *thread,u64 trace_nr) +{ +if (!thread || !thread->ts) +return ; + +if (trace_nr != thread->ts->trace_nr) { +if (thread->ts->trace_nr) +thread_stack__flush (thread, thread->ts); + thread->ts->trace_nr = trace_nr; + } +} +void thread_stack__free (struct thread *thread) {if (thread->ts) { +thread_stack__flush (thread, thread->ts);zfree (&thread->ts->stack);zfree (&thread->ts); }
@@ -170,3 +326,422 @@ void thread_stack__sample(struct thread *thread, struct ip_callchain *chain,for (i =1 ; i < chain->nr; i++) chain->ips[i] = thread->ts->stack[thread->ts->cnt - i].ret_addr; } + +static void call_path__init (struct call_path *cp,struct call_path *parent, +struct symbol *sym,u64 ip,bool in_kernel) +{ + cp->parent = parent; + cp->sym = sym; + cp->ip = sym ?0 : ip; + cp->db_id =0 ; + cp->in_kernel = in_kernel; +RB_CLEAR_NODE (&cp->rb_node); + cp->children =RB_ROOT ; +} + +static struct call_path_root *call_path_root__new (void ) +{ +struct call_path_root *cpr; + + cpr =zalloc (sizeof (struct call_path_root )); +if (!cpr) +return NULL ; +call_path__init (&cpr->call_path,NULL ,NULL ,0 , false); +INIT_LIST_HEAD (&cpr->blocks); +return cpr; +} + +static void call_path_root__free (struct call_path_root *cpr) +{ +struct call_path_block *pos, *n; + +list_for_each_entry_safe (pos, n, &cpr->blocks, node) { +list_del (&pos->node); +free (pos); + } +free (cpr); +} + +static struct call_path *call_path__new (struct call_path_root *cpr, +struct call_path *parent, +struct symbol *sym,u64 ip, +bool in_kernel) +{ +struct call_path_block *cpb; +struct call_path *cp; +size_t n; + +if (cpr->next < cpr->sz) { + cpb = list_last_entry(&cpr->blocks, struct call_path_block, + node); + }else { + cpb =zalloc (sizeof (struct call_path_block )); +if (!cpb) +return NULL ; +list_add_tail (&cpb->node, &cpr->blocks); + cpr->sz +=CALL_PATH_BLOCK_SIZE ; + } + + n = cpr->next++ &CALL_PATH_BLOCK_MASK ; + cp = &cpb->cp[n]; + +call_path__init (cp, parent, sym, ip, in_kernel); + +return cp; +} + +static struct call_path *call_path__findnew (struct call_path_root *cpr, +struct call_path *parent, +struct symbol *sym,u64 ip,u64 ks) +{ +struct rb_node **p; +struct rb_node *node_parent =NULL ; +struct call_path *cp; +bool in_kernel = ip >= ks; + +if (sym) + ip =0 ; + +if (!parent) +return call_path__new (cpr, parent, sym, ip, in_kernel); + + p = &parent->children.rb_node; +while (*p !=NULL ) { + node_parent = *p; + cp = rb_entry(node_parent, struct call_path, rb_node); + +if (cp->sym == sym && cp->ip == ip) +return cp; + +if (sym < cp->sym || (sym == cp->sym && ip < cp->ip)) + p = &(*p)->rb_left; +else + p = &(*p)->rb_right; + } + + cp =call_path__new (cpr, parent, sym, ip, in_kernel); +if (!cp) +return NULL ; + +rb_link_node (&cp->rb_node, node_parent, p); +rb_insert_color (&cp->rb_node, &parent->children); + +return cp; +} + +struct call_return_processor * +call_return_processor__new (int (*process)(struct call_return *cr,void *data), +void *data) +{ +struct call_return_processor *crp; + + crp =zalloc (sizeof (struct call_return_processor )); +if (!crp) +return NULL ; + crp->cpr =call_path_root__new (); +if (!crp->cpr) + goto out_free; + crp->process = process; + crp->data = data; +return crp; + +out_free: +free (crp); +return NULL ; +} + +void call_return_processor__free (struct call_return_processor *crp) +{ +if (crp) { +call_path_root__free (crp->cpr); +free (crp); + } +} + +static int thread_stack__push_cp (struct thread_stack *ts,u64 ret_addr, +u64 timestamp,u64 ref,struct call_path *cp, +bool no_call) +{ +struct thread_stack_entry *tse; +int err; + +if (ts->cnt == ts->sz) { + err =thread_stack__grow (ts); +if (err) +return err; + } + + tse = &ts->stack[ts->cnt++]; + tse->ret_addr = ret_addr; + tse->timestamp = timestamp; + tse->ref = ref; + tse->branch_count = ts->branch_count; + tse->cp = cp; + tse->no_call = no_call; + +return 0 ; +} + +static int thread_stack__pop_cp (struct thread *thread,struct thread_stack *ts, +u64 ret_addr,u64 timestamp,u64 ref, +struct symbol *sym) +{ +int err; + +if (!ts->cnt) +return 1 ; + +if (ts->cnt ==1 ) { +struct thread_stack_entry *tse = &ts->stack[0 ]; + +if (tse->cp->sym == sym) +return thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); + } + +if (ts->stack[ts->cnt -1 ].ret_addr == ret_addr) { +return thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); + }else { +size_t i = ts->cnt -1 ; + +while (i--) { +if (ts->stack[i].ret_addr != ret_addr) +continue ; + i +=1 ; +while (ts->cnt > i) { + err =thread_stack__call_return (thread, ts, + --ts->cnt, + timestamp, ref, + true); +if (err) +return err; + } +return thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); + } + } + +return 1 ; +} + +static int thread_stack__bottom (struct thread *thread,struct thread_stack *ts, +struct perf_sample *sample, +struct addr_location *from_al, +struct addr_location *to_al,u64 ref) +{ +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp; +struct symbol *sym; +u64 ip; + +if (sample->ip) { + ip = sample->ip; + sym = from_al->sym; + }else if (sample->addr) { + ip = sample->addr; + sym = to_al->sym; + }else { +return 0 ; + } + + cp =call_path__findnew (cpr, &cpr->call_path, sym, ip, + ts->kernel_start); +if (!cp) +return -ENOMEM ; + +return thread_stack__push_cp (thread->ts, ip, sample->time, ref, cp, + true); +} + +static int thread_stack__no_call_return (struct thread *thread, +struct thread_stack *ts, +struct perf_sample *sample, +struct addr_location *from_al, +struct addr_location *to_al,u64 ref) +{ +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp, *parent; +u64 ks = ts->kernel_start; +int err; + +if (sample->ip >= ks && sample->addr < ks) { +/* Return to userspace, so pop all kernel addresses */ +while (thread_stack__in_kernel (ts)) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + sample->time, ref, + true); +if (err) +return err; + } + +/* If the stack is empty, push the userspace address */ +if (!ts->cnt) { + cp =call_path__findnew (cpr, &cpr->call_path, + to_al->sym, sample->addr, + ts->kernel_start); +if (!cp) +return -ENOMEM ; +return thread_stack__push_cp (ts,0 , sample->time, ref, + cp, true); + } + }else if (thread_stack__in_kernel (ts) && sample->ip < ks) { +/* Return to userspace, so pop all kernel addresses */ +while (thread_stack__in_kernel (ts)) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + sample->time, ref, + true); +if (err) +return err; + } + } + +if (ts->cnt) + parent = ts->stack[ts->cnt -1 ].cp; +else + parent = &cpr->call_path; + +/* This 'return' had no 'call', so push and pop top of stack */ + cp =call_path__findnew (cpr, parent, from_al->sym, sample->ip, + ts->kernel_start); +if (!cp) +return -ENOMEM ; + + err =thread_stack__push_cp (ts, sample->addr, sample->time, ref, cp, + true); +if (err) +return err; + +return thread_stack__pop_cp (thread, ts, sample->addr, sample->time, ref, + to_al->sym); +} + +static int thread_stack__trace_begin (struct thread *thread, +struct thread_stack *ts,u64 timestamp, +u64 ref) +{ +struct thread_stack_entry *tse; +int err; + +if (!ts->cnt) +return 0 ; + +/* Pop trace end */ + tse = &ts->stack[ts->cnt -1 ]; +if (tse->cp->sym ==NULL && tse->cp->ip ==0 ) { + err =thread_stack__call_return (thread, ts, --ts->cnt, + timestamp, ref, false); +if (err) +return err; + } + +return 0 ; +} + +static int thread_stack__trace_end (struct thread_stack *ts, +struct perf_sample *sample,u64 ref) +{ +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp; +u64 ret_addr; + +/* No point having 'trace end' on the bottom of the stack */ +if (!ts->cnt || (ts->cnt ==1 && ts->stack[0 ].ref == ref)) +return 0 ; + + cp =call_path__findnew (cpr, ts->stack[ts->cnt -1 ].cp,NULL ,0 , + ts->kernel_start); +if (!cp) +return -ENOMEM ; + + ret_addr = sample->ip + sample->insn_len; + +return thread_stack__push_cp (ts, ret_addr, sample->time, ref, cp, + false); +} + +int thread_stack__process (struct thread *thread,struct comm *comm, +struct perf_sample *sample, +struct addr_location *from_al, +struct addr_location *to_al,u64 ref, +struct call_return_processor *crp) +{ +struct thread_stack *ts = thread->ts; +int err =0 ; + +if (ts) { +if (!ts->crp) { +/* Supersede thread_stack__event() */ +thread_stack__free (thread); + thread->ts =thread_stack__new (thread, crp); +if (!thread->ts) +return -ENOMEM ; + ts = thread->ts; + ts->comm = comm; + } + }else { + thread->ts =thread_stack__new (thread, crp); +if (!thread->ts) +return -ENOMEM ; + ts = thread->ts; + ts->comm = comm; + } + +/* Flush stack on exec */ +if (ts->comm != comm && thread->pid_ == thread->tid) { + err =thread_stack__flush (thread, ts); +if (err) +return err; + ts->comm = comm; + } + +/* If the stack is empty, put the current symbol on the stack */ +if (!ts->cnt) { + err =thread_stack__bottom (thread, ts, sample, from_al, to_al, + ref); +if (err) +return err; + } + + ts->branch_count +=1 ; + ts->last_time = sample->time; + +if (sample->flags &PERF_IP_FLAG_CALL ) { +struct call_path_root *cpr = ts->crp->cpr; +struct call_path *cp; +u64 ret_addr; + +if (!sample->ip || !sample->addr) +return 0 ; + + ret_addr = sample->ip + sample->insn_len; +if (ret_addr == sample->addr) +return 0 ;/* Zero-length calls are excluded */ + + cp =call_path__findnew (cpr, ts->stack[ts->cnt -1 ].cp, + to_al->sym, sample->addr, + ts->kernel_start); +if (!cp) +return -ENOMEM ; + err =thread_stack__push_cp (ts, ret_addr, sample->time, ref, + cp, false); + }else if (sample->flags &PERF_IP_FLAG_RETURN ) { +if (!sample->ip || !sample->addr) +return 0 ; + + err =thread_stack__pop_cp (thread, ts, sample->addr, + sample->time, ref, from_al->sym); +if (err) { +if (err <0 ) +return err; + err =thread_stack__no_call_return (thread, ts, sample, + from_al, to_al, ref); + } + }else if (sample->flags &PERF_IP_FLAG_TRACE_BEGIN ) { + err =thread_stack__trace_begin (thread, ts, sample->time, ref); + }else if (sample->flags &PERF_IP_FLAG_TRACE_END ) { + err =thread_stack__trace_end (ts, sample, ref); + } + +return err; +}
tools/perf/util/thread-stack.h +79 -0
93 lines not on this page · raw message
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 3/18 perf tools: Add branch type to db export
In reply to Arnaldo Carvalho de MeloFrom: Adrian Hunter <[email protected]>
Add the ability to export branch types through the database export facility.
--- tools/perf/util/db-export.c | 48 +++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/db-export.h | 6 ++++++ 2 files changed, 54 insertions(+)
tools/perf/util/db-export.c +48 -0
@@ -208,6 +208,15 @@ static int db_ids_from_al(struct db_export *dbe, struct addr_location *al,return 0 ; } +int db_export__branch_type (struct db_export *dbe,u32 branch_type, +const char *name) +{ +if (dbe->export_branch_type) +return dbe->export_branch_type (dbe, branch_type, name); + +return 0 ; +} +int db_export__sample (struct db_export *dbe,union perf_event *event,struct perf_sample *sample,struct perf_evsel *evsel,struct thread *thread,struct addr_location *al)
@@ -268,3 +277,42 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,return 0 ; } + +static struct { +u32 branch_type; +const char *name; +} branch_types[] = { + {0 ,"no branch" }, + {PERF_IP_FLAG_BRANCH |PERF_IP_FLAG_CALL ,"call" }, + {PERF_IP_FLAG_BRANCH |PERF_IP_FLAG_RETURN ,"return" }, + {PERF_IP_FLAG_BRANCH |PERF_IP_FLAG_CONDITIONAL ,"conditional jump" }, + {PERF_IP_FLAG_BRANCH ,"unconditional jump" }, + {PERF_IP_FLAG_BRANCH |PERF_IP_FLAG_CALL |PERF_IP_FLAG_INTERRUPT , +"software interrupt" }, + {PERF_IP_FLAG_BRANCH |PERF_IP_FLAG_RETURN |PERF_IP_FLAG_INTERRUPT , +"return from interrupt" }, + {PERF_IP_FLAG_BRANCH |PERF_IP_FLAG_CALL |PERF_IP_FLAG_SYSCALLRET , +"system call" }, + {PERF_IP_FLAG_BRANCH |PERF_IP_FLAG_RETURN |PERF_IP_FLAG_SYSCALLRET , +"return from system call" }, + {PERF_IP_FLAG_BRANCH |PERF_IP_FLAG_ASYNC ,"asynchronous branch" }, + {PERF_IP_FLAG_BRANCH |PERF_IP_FLAG_CALL |PERF_IP_FLAG_ASYNC | +PERF_IP_FLAG_INTERRUPT ,"hardware interrupt" }, + {PERF_IP_FLAG_BRANCH |PERF_IP_FLAG_TX_ABORT ,"transaction abort" }, + {PERF_IP_FLAG_BRANCH |PERF_IP_FLAG_TRACE_BEGIN ,"trace begin" }, + {PERF_IP_FLAG_BRANCH |PERF_IP_FLAG_TRACE_END ,"trace end" }, + {0 ,NULL } +}; + +int db_export__branch_types (struct db_export *dbe) +{ +int i, err =0 ; + +for (i =0 ; branch_types[i].name ; i++) { + err =db_export__branch_type (dbe, branch_types[i].branch_type, + branch_types[i].name); +if (err) +break ; + } +return err; +}
tools/perf/util/db-export.h +6 -0
@@ -54,6 +54,8 @@ struct db_export { struct machine *machine);int (*export_symbol)(struct db_export *dbe,struct symbol *sym,struct dso *dso); +int (*export_branch_type)(struct db_export *dbe,u32 branch_type, +const char *name);int (*export_sample)(struct db_export *dbe,struct export_sample *es);u64 evsel_last_db_id;u64 machine_last_db_id;
@@ -79,8 +81,12 @@ int db_export__dso(struct db_export *dbe, struct dso *dso, struct machine *machine);int db_export__symbol (struct db_export *dbe,struct symbol *sym,struct dso *dso); +int db_export__branch_type (struct db_export *dbe,u32 branch_type, +const char *name);int db_export__sample (struct db_export *dbe,union perf_event *event,struct perf_sample *sample,struct perf_evsel *evsel,struct thread *thread,struct addr_location *al); +int db_export__branch_types (struct db_export *dbe); +#endif
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 4/18 perf tools: Add branch_type and in_tx to Python export
In reply to Arnaldo Carvalho de MeloFrom: Adrian Hunter <[email protected]>
Add branch_type and in_tx to Python db export and the export-to-postgresql.py script.
--- tools/perf/scripts/python/export-to-postgresql.py | 32 ++++++++++++++++++---- .../util/scripting-engines/trace-event-python.c | 30 +++++++++++++++++++- 2 files changed, 55 insertions(+), 7 deletions(-)
tools/perf/scripts/python/export-to-postgresql.py +26 -6
@@ -123,6 +123,10 @@ do_query(query, 'CREATE TABLE symbols (' 'sym_end bigint,' 'binding integer,' 'name varchar(2048))') +do_query(query, 'CREATE TABLE branch_types (' + 'id integer NOT NULL,' + 'name varchar(80))') + if branches: do_query(query, 'CREATE TABLE samples (' 'id bigint NOT NULL,'
@@ -139,7 +143,9 @@ if branches: 'to_dso_id bigint,' 'to_symbol_id bigint,' 'to_sym_offset bigint,' - 'to_ip bigint)') + 'to_ip bigint,' + 'branch_type integer,' + 'in_tx boolean)') else: do_query(query, 'CREATE TABLE samples (' 'id bigint NOT NULL,'
@@ -160,7 +166,9 @@ else: 'period bigint,' 'weight bigint,' 'transaction bigint,' - 'data_src bigint)') + 'data_src bigint,' + 'branch_type integer,' + 'in_tx boolean)') do_query(query, 'CREATE VIEW samples_view AS ' 'SELECT '
@@ -178,7 +186,9 @@ do_query(query, 'CREATE VIEW samples_view AS ' 'to_hex(to_ip) AS to_ip_hex,' '(SELECT name FROM symbols WHERE id = to_symbol_id) AS to_symbol,' 'to_sym_offset,' - '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name' + '(SELECT short_name FROM dsos WHERE id = to_dso_id) AS to_dso_short_name,' + '(SELECT name FROM branch_types WHERE id = branch_type) AS branch_type_name,' + 'in_tx' ' FROM samples')
@@ -234,6 +244,7 @@ comm_file = open_output_file("comm_table.bin") comm_thread_file = open_output_file("comm_thread_table.bin") dso_file = open_output_file("dso_table.bin") symbol_file = open_output_file("symbol_table.bin") +branch_type_file = open_output_file("branch_type_table.bin") sample_file = open_output_file("sample_table.bin") def trace_begin():
@@ -257,6 +268,7 @@ def trace_end(): copy_output_file(comm_thread_file, "comm_threads") copy_output_file(dso_file, "dsos") copy_output_file(symbol_file, "symbols") + copy_output_file(branch_type_file, "branch_types") copy_output_file(sample_file, "samples") print datetime.datetime.today(), "Removing intermediate files..."
@@ -267,6 +279,7 @@ def trace_end(): remove_output_file(comm_thread_file) remove_output_file(dso_file) remove_output_file(symbol_file) + remove_output_file(branch_type_file) remove_output_file(sample_file) os.rmdir(output_dir_name) print datetime.datetime.today(), "Adding primary keys"
@@ -277,6 +290,7 @@ def trace_end(): do_query(query, 'ALTER TABLE comm_threads ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE dsos ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE symbols ADD PRIMARY KEY (id)') + do_query(query, 'ALTER TABLE branch_types ADD PRIMARY KEY (id)') do_query(query, 'ALTER TABLE samples ADD PRIMARY KEY (id)') print datetime.datetime.today(), "Adding foreign keys"
@@ -352,9 +366,15 @@ def symbol_table(symbol_id, dso_id, sym_start, sym_end, binding, symbol_name, *x value = struct.pack(fmt, 6, 8, symbol_id, 8, dso_id, 8, sym_start, 8, sym_end, 4, binding, n, symbol_name) symbol_file.write(value) -def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, *x): +def branch_type_table(branch_type, name, *x): + n = len(name) + fmt = "!hiii" + str(n) + "s" + value = struct.pack(fmt, 2, 4, branch_type, n, name) + branch_type_file.write(value) + +def sample_table(sample_id, evsel_id, machine_id, thread_id, comm_id, dso_id, symbol_id, sym_offset, ip, time, cpu, to_dso_id, to_symbol_id, to_sym_offset, to_ip, period, weight, transaction, data_src, branch_type, in_tx, *x): if branches: - value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiq", 15, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip) + value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiiiB", 17, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 4, branch_type, 1, in_tx) else: - value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiq", 19, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src) + value = struct.pack("!hiqiqiqiqiqiqiqiqiqiqiiiqiqiqiqiqiqiqiqiiiB", 21, 8, sample_id, 8, evsel_id, 8, machine_id, 8, thread_id, 8, comm_id, 8, dso_id, 8, symbol_id, 8, sym_offset, 8, ip, 8, time, 4, cpu, 8, to_dso_id, 8, to_symbol_id, 8, to_sym_offset, 8, to_ip, 8, period, 8, weight, 8, transaction, 8, data_src, 4, branch_type, 1, in_tx) sample_file.write(value)
tools/perf/util/scripting-engines/trace-event-python.c +29 -1
@@ -66,6 +66,7 @@ struct tables {PyObject *comm_thread_handler;PyObject *dso_handler;PyObject *symbol_handler; +PyObject *branch_type_handler;PyObject *sample_handler;bool db_export_mode; };
@@ -664,13 +665,31 @@ static int python_export_symbol(struct db_export *dbe, struct symbol *sym,return 0 ; } +static int python_export_branch_type (struct db_export *dbe,u32 branch_type, +const char *name) +{ + struct tables *tables = container_of(dbe, struct tables, dbe); +PyObject *t; + + t =tuple_new (2 ); + +tuple_set_s32 (t,0 , branch_type); +tuple_set_string (t,1 , name); + +call_object (tables->branch_type_handler, t,"branch_type_table" ); + +Py_DECREF (t); + +return 0 ; +} +static int python_export_sample (struct db_export *dbe,struct export_sample *es) { struct tables *tables = container_of(dbe, struct tables, dbe);PyObject *t; - t =tuple_new (19 ); + t =tuple_new (21 );tuple_set_u64 (t,0 , es->db_id);tuple_set_u64 (t,1 , es->evsel->db_id);
@@ -691,6 +710,8 @@ static int python_export_sample(struct db_export *dbe,tuple_set_u64 (t,16 , es->sample->weight);tuple_set_u64 (t,17 , es->sample->transaction);tuple_set_u64 (t,18 , es->sample->data_src); +tuple_set_s32 (t,19 , es->sample->flags &PERF_BRANCH_MASK ); +tuple_set_s32 (t,20 , !!(es->sample->flags &PERF_IP_FLAG_IN_TX ));call_object (tables->sample_handler, t,"sample_table" );
@@ -861,6 +882,7 @@ static void set_table_handlers(struct tables *tables)SET_TABLE_HANDLER (comm_thread);SET_TABLE_HANDLER (dso);SET_TABLE_HANDLER (symbol); +SET_TABLE_HANDLER (branch_type);SET_TABLE_HANDLER (sample); }
@@ -910,6 +932,12 @@ static int python_start_script(const char *script, int argc, const char **argv)set_table_handlers (tables); +if (tables->db_export_mode) { + err =db_export__branch_types (&tables->dbe); +if (err) + goto error; + } +return err; error:Py_Finalize ();
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 5/18 perf tools: Add call information to the database export API
In reply to Arnaldo Carvalho de MeloFrom: Adrian Hunter <[email protected]>
Make it possible for the database export API to use the enhanced thread stack and export detailed information about paired calls and returns.
--- tools/perf/util/db-export.c | 52 ++++++++++++++++++++++++++++++++++++++++++++- tools/perf/util/db-export.h | 12 +++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-)
tools/perf/util/db-export.c +51 -1
@@ -21,6 +21,7 @@#include "comm.h" #include "symbol.h" #include "event.h" +#include "thread-stack.h" #include "db-export.h" int db_export__init (struct db_export *dbe)
@@ -29,8 +30,10 @@ int db_export__init(struct db_export *dbe)return 0 ; } -void db_export__exit (struct db_export *dbe__maybe_unused ) +void db_export__exit (struct db_export *dbe) { +call_return_processor__free (dbe->crp); + dbe->crp =NULL ; }int db_export__evsel (struct db_export *dbe,struct perf_evsel *evsel)
@@ -270,6 +273,13 @@ int db_export__sample(struct db_export *dbe, union perf_event *event, &es.addr_sym_db_id, &es.addr_offset);if (err)return err; +if (dbe->crp) { + err =thread_stack__process (thread, comm, sample, al, + &addr_al, es.db_id, + dbe->crp); +if (err) +return err; + } }if (dbe->export_sample)
@@ -316,3 +326,43 @@ int db_export__branch_types(struct db_export *dbe) }return err; } + +int db_export__call_path (struct db_export *dbe,struct call_path *cp) +{ +int err; + +if (cp->db_id) +return 0 ; + +if (cp->parent) { + err =db_export__call_path (dbe, cp->parent); +if (err) +return err; + } + + cp->db_id = ++dbe->call_path_last_db_id; + +if (dbe->export_call_path) +return dbe->export_call_path (dbe, cp); + +return 0 ; +} + +int db_export__call_return (struct db_export *dbe,struct call_return *cr) +{ +int err; + +if (cr->db_id) +return 0 ; + + err =db_export__call_path (dbe, cr->cp); +if (err) +return err; + + cr->db_id = ++dbe->call_return_last_db_id; + +if (dbe->export_call_return) +return dbe->export_call_return (dbe, cr); + +return 0 ; +}
tools/perf/util/db-export.h +12 -0
@@ -25,6 +25,9 @@ struct comm;struct dso ;struct perf_sample ;struct addr_location ; +struct call_return_processor ; +struct call_path ; +struct call_return ;struct export_sample {union perf_event *event;
@@ -57,6 +60,10 @@ struct db_export {int (*export_branch_type)(struct db_export *dbe,u32 branch_type,const char *name);int (*export_sample)(struct db_export *dbe,struct export_sample *es); +int (*export_call_path)(struct db_export *dbe,struct call_path *cp); +int (*export_call_return)(struct db_export *dbe, +struct call_return *cr); +struct call_return_processor *crp;u64 evsel_last_db_id;u64 machine_last_db_id;u64 thread_last_db_id;
@@ -65,6 +72,8 @@ struct db_export {u64 dso_last_db_id;u64 symbol_last_db_id;u64 sample_last_db_id; +u64 call_path_last_db_id; +u64 call_return_last_db_id; };int db_export__init (struct db_export *dbe);
@@ -89,4 +98,7 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,int db_export__branch_types (struct db_export *dbe); +int db_export__call_path (struct db_export *dbe,struct call_path *cp); +int db_export__call_return (struct db_export *dbe,struct call_return *cr); +#endif
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 6/18 perf tools: Add call information to Python export
In reply to Arnaldo Carvalho de MeloFrom: Adrian Hunter <[email protected]>
Add the ability to export detailed information about paired calls and returns to Python db export and the export-to-postgresql.py script.
--- .../scripts/python/bin/export-to-postgresql-report | 15 ++-- tools/perf/scripts/python/export-to-postgresql.py | 66 ++++++++++++++++- .../util/scripting-engines/trace-event-python.c | 84 +++++++++++++++++++++- 3 files changed, 158 insertions(+), 7 deletions(-)
tools/perf/scripts/python/bin/export-to-postgresql-report +10 -5
@@ -1,6 +1,6 @@ #!/bin/bash # description: export perf data to a postgresql database -# args: [database name] [columns] +# args: [database name] [columns] [calls] n_args=0 for i in "$@" do
@@ -9,11 +9,16 @@ do fi n_args=$(( $n_args + 1 )) done -if [ "$n_args" -gt 2 ] ; then - echo "usage: export-to-postgresql-report [database name] [columns]" +if [ "$n_args" -gt 3 ] ; then + echo "usage: export-to-postgresql-report [database name] [columns] [calls]" exit fi -if [ "$n_args" -gt 1 ] ; then +if [ "$n_args" -gt 2 ] ; then + dbname=$1 + columns=$2 + calls=$3 + shift 3 +elif [ "$n_args" -gt 1 ] ; then dbname=$1 columns=$2 shift 2
@@ -21,4 +26,4 @@ elif [ "$n_args" -gt 0 ] ; then dbname=$1 shift fi -perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns +perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/export-to-postgresql.py $dbname $columns $calls
tools/perf/scripts/python/export-to-postgresql.py +65 -1
126 lines not on this page · raw message
tools/perf/util/scripting-engines/trace-event-python.c +83 -1
120 lines not on this page · raw message
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 7/18 perf tools: Defer export of comms that were not 'set'
In reply to Arnaldo Carvalho de MeloFrom: Adrian Hunter <[email protected]>
Tracing for a workload begins before the comm event is seen, which results in the initial comm having a string of the form ":<pid>" (e.g. ":12345").
In order to export the correct string, defer the export until the new script 'flush' callback.
--- tools/perf/util/db-export.c | 62 +++++++++++++++++++++- tools/perf/util/db-export.h | 3 ++ .../util/scripting-engines/trace-event-python.c | 4 +- 3 files changed, 67 insertions(+), 2 deletions(-)
tools/perf/util/db-export.c +61 -1
@@ -21,17 +21,74 @@#include "comm.h" #include "symbol.h" #include "event.h" +#include "util.h" #include "thread-stack.h" #include "db-export.h" +struct deferred_export { +struct list_head node; +struct comm *comm; +}; + +static int db_export__deferred (struct db_export *dbe) +{ +struct deferred_export *de; +int err; + +while (!list_empty (&dbe->deferred)) { + de = list_entry(dbe->deferred.next, struct deferred_export, + node); + err = dbe->export_comm (dbe, de->comm); +list_del (&de->node); +free (de); +if (err) +return err; + } + +return 0 ; +} + +static void db_export__free_deferred (struct db_export *dbe) +{ +struct deferred_export *de; + +while (!list_empty (&dbe->deferred)) { + de = list_entry(dbe->deferred.next, struct deferred_export, + node); +list_del (&de->node); +free (de); + } +} + +static int db_export__defer_comm (struct db_export *dbe,struct comm *comm) +{ +struct deferred_export *de; + + de =zalloc (sizeof (struct deferred_export )); +if (!de) +return -ENOMEM ; + + de->comm = comm; +list_add_tail (&de->node, &dbe->deferred); + +return 0 ; +} +int db_export__init (struct db_export *dbe) {memset (dbe,0 ,sizeof (struct db_export )); +INIT_LIST_HEAD (&dbe->deferred);return 0 ; } +int db_export__flush (struct db_export *dbe) +{ +return db_export__deferred (dbe); +} +void db_export__exit (struct db_export *dbe) { +db_export__free_deferred (dbe);call_return_processor__free (dbe->crp); dbe->crp =NULL ; }
@@ -115,7 +172,10 @@ int db_export__comm(struct db_export *dbe, struct comm *comm, comm->db_id = ++dbe->comm_last_db_id;if (dbe->export_comm) { - err = dbe->export_comm (dbe, comm); +if (main_thread->comm_set) + err = dbe->export_comm (dbe, comm); +else + err =db_export__defer_comm (dbe, comm);if (err)return err; }
tools/perf/util/db-export.h +3 -0
@@ -17,6 +17,7 @@#define __PERF_DB_EXPORT_H#include <linux/types.h> +#include <linux/list.h> struct perf_evsel ;struct machine ;
@@ -74,9 +75,11 @@ struct db_export {u64 sample_last_db_id;u64 call_path_last_db_id;u64 call_return_last_db_id; +struct list_head deferred; };int db_export__init (struct db_export *dbe); +int db_export__flush (struct db_export *dbe);void db_export__exit (struct db_export *dbe);int db_export__evsel (struct db_export *dbe,struct perf_evsel *evsel);int db_export__machine (struct db_export *dbe,struct machine *machine);
tools/perf/util/scripting-engines/trace-event-python.c +3 -1
@@ -1030,7 +1030,9 @@ error:static int python_flush_script (void ) { -return 0 ; +struct tables *tables = &tables_global; + +return db_export__flush (&tables->dbe); }/*
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 8/18 perf symbols: Preparation for compressed kernel module support
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
This patch adds basic support to handle compressed kernel module as some distro (such as Archlinux) carries on it now. The actual work using compression library will be added later.
--- tools/perf/util/dso.c | 75 ++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/dso.h | 7 +++++ tools/perf/util/machine.c | 19 ++++++++++- tools/perf/util/symbol-elf.c | 35 ++++++++++++++++++++- tools/perf/util/symbol.c | 8 ++++- 5 files changed, 141 insertions(+), 3 deletions(-)
tools/perf/util/dso.c +75 -0
@@ -21,8 +21,10 @@ char dso__symtab_origin(const struct dso *dso) [DSO_BINARY_TYPE__BUILDID_DEBUGINFO] = 'b', [DSO_BINARY_TYPE__SYSTEM_PATH_DSO] = 'd', [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K', + [DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP] = 'm', [DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g', [DSO_BINARY_TYPE__GUEST_KMODULE] = 'G', + [DSO_BINARY_TYPE__GUEST_KMODULE_COMP] = 'M', [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V', };
@@ -112,11 +114,13 @@ int dso__read_binary_type_filename(const struct dso *dso,break ;case DSO_BINARY_TYPE__GUEST_KMODULE : +case DSO_BINARY_TYPE__GUEST_KMODULE_COMP :path__join3 (filename, size, symbol_conf.symfs, root_dir, dso->long_name);break ;case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE : +case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP :__symbol__join_symfs (filename, size, dso->long_name);break ;
@@ -137,6 +141,77 @@ int dso__read_binary_type_filename(const struct dso *dso,return ret; } +static int decompress_dummy (const char *input__maybe_unused , +int output__maybe_unused ) +{ +return -1 ; +} + +static const struct { +const char *fmt; +int (*decompress)(const char *input,int output); +} compressions[] = { + {"gz" , decompress_dummy }, + {NULL , }, +}; + +bool is_supported_compression (const char *ext) +{ +unsigned i; + +for (i =0 ; compressions[i].fmt; i++) { +if (!strcmp (ext, compressions[i].fmt)) +return true; + } +return false; +} + +bool is_kmodule_extension (const char *ext) +{ +if (strncmp (ext,"ko" ,2 )) +return false; + +if (ext[2 ] =='\0' || (ext[2 ] =='.' &&is_supported_compression (ext+3 ))) +return true; + +return false; +} + +bool is_kernel_module (const char *pathname,bool *compressed) +{ +const char *ext =strrchr (pathname,'.' ); + +if (ext ==NULL ) +return false; + +if (is_supported_compression (ext +1 )) { +if (compressed) + *compressed = true; + ext -=3 ; + }else if (compressed) + *compressed = false; + +return is_kmodule_extension (ext +1 ); +} + +bool decompress_to_file (const char *ext,const char *filename,int output_fd) +{ +unsigned i; + +for (i =0 ; compressions[i].fmt; i++) { +if (!strcmp (ext, compressions[i].fmt)) +return !compressions[i].decompress (filename, + output_fd); + } +return false; +} + +bool dso__needs_decompress (struct dso *dso) +{ +return dso->symtab_type ==DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP || + dso->symtab_type ==DSO_BINARY_TYPE__GUEST_KMODULE_COMP ; +} +/* * Global list of open DSOs and the counter. */
tools/perf/util/dso.h +7 -0
@@ -22,7 +22,9 @@ enum dso_binary_type {DSO_BINARY_TYPE__BUILDID_DEBUGINFO ,DSO_BINARY_TYPE__SYSTEM_PATH_DSO ,DSO_BINARY_TYPE__GUEST_KMODULE , +DSO_BINARY_TYPE__GUEST_KMODULE_COMP ,DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE , +DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP ,DSO_BINARY_TYPE__KCORE ,DSO_BINARY_TYPE__GUEST_KCORE , DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO,
@@ -185,6 +187,11 @@ int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir);char dso__symtab_origin (const struct dso *dso);int dso__read_binary_type_filename (const struct dso *dso,enum dso_binary_type type,char *root_dir,char *filename,size_t size); +bool is_supported_compression (const char *ext); +bool is_kmodule_extension (const char *ext); +bool is_kernel_module (const char *pathname,bool *compressed); +bool decompress_to_file (const char *ext,const char *filename,int output_fd); +bool dso__needs_decompress (struct dso *dso);/* * The dso__data_* external interface provides following functions:
tools/perf/util/machine.c +18 -1
@@ -464,6 +464,7 @@ struct map *machine__new_module(struct machine *machine, u64 start, {struct map *map;struct dso *dso =__dsos__findnew (&machine->kernel_dsos, filename); +bool compressed;if (dso ==NULL )return NULL ;
@@ -476,6 +477,11 @@ struct map *machine__new_module(struct machine *machine, u64 start, dso->symtab_type =DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE ; else dso->symtab_type = DSO_BINARY_TYPE__GUEST_KMODULE; + +/* _KMODULE_COMP should be next to _KMODULE */ +if (is_kernel_module (filename, &compressed) && compressed) + dso->symtab_type++; +map_groups__insert (&machine->kmaps, map);return map; }
@@ -861,8 +867,14 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg,struct map *map;char *long_name; -if (dot ==NULL ||strcmp (dot,".ko" )) +if (dot ==NULL )continue ; + +/* On some system, modules are compressed like .ko.gz */ +if (is_supported_compression (dot +1 ) && +is_kmodule_extension (dot -2 )) + dot -=3 ; +snprintf (dso_name,sizeof (dso_name),"[%.*s]" , (int )(dot - dent->d_name), dent->d_name);
@@ -1044,6 +1056,11 @@ static int machine__process_kernel_mmap_event(struct machine *machine, dot =strrchr (name,'.' );if (dot ==NULL ) goto out_problem; +/* On some system, modules are compressed like .ko.gz */ +if (is_supported_compression (dot +1 )) + dot -=3 ; +if (!is_kmodule_extension (dot +1 )) + goto out_problem;snprintf (short_module_name,sizeof (short_module_name),"[%.*s]" , (int )(dot - name), name);strxfrchar (short_module_name,'-' ,'_' );
tools/perf/util/symbol-elf.c +34 -1
@@ -546,6 +546,35 @@ static int dso__swap_init(struct dso *dso, unsigned char eidata)return 0 ; } +static int decompress_kmodule (struct dso *dso,const char *name, +enum dso_binary_type type) +{ +int fd; +const char *ext =strrchr (name,'.' ); +char tmpbuf[] ="/tmp/perf-kmod-XXXXXX" ; + +if ((type !=DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP && + type !=DSO_BINARY_TYPE__GUEST_KMODULE_COMP ) || + type != dso->symtab_type) +return -1 ; + +if (!ext || !is_supported_compression (ext +1 )) +return -1 ; + + fd =mkstemp (tmpbuf); +if (fd <0 ) +return -1 ; + +if (!decompress_to_file (ext +1 , name, fd)) { +close (fd); + fd =-1 ; + } + +unlink (tmpbuf); + +return fd; +} +bool symsrc__possibly_runtime (struct symsrc *ss) {return ss->dynsym || ss->opdsec;
@@ -571,7 +600,11 @@ int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,Elf *elf;int fd; - fd =open (name,O_RDONLY ); +if (dso__needs_decompress (dso)) + fd =decompress_kmodule (dso, name, type); +else + fd =open (name,O_RDONLY ); +if (fd <0 )return -1 ;
tools/perf/util/symbol.c +7 -1
@@ -51,7 +51,9 @@ static enum dso_binary_type binary_type_symtab[] = {DSO_BINARY_TYPE__BUILDID_DEBUGINFO ,DSO_BINARY_TYPE__SYSTEM_PATH_DSO ,DSO_BINARY_TYPE__GUEST_KMODULE , +DSO_BINARY_TYPE__GUEST_KMODULE_COMP ,DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE , +DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP ,DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO , DSO_BINARY_TYPE__NOT_FOUND, };
@@ -1300,7 +1302,9 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,return dso->kernel ==DSO_TYPE_GUEST_KERNEL ;case DSO_BINARY_TYPE__GUEST_KMODULE : +case DSO_BINARY_TYPE__GUEST_KMODULE_COMP :case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE : +case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP :/* * kernel modules know their symtab type - it's set when * creating a module dso in machine__new_module().
@@ -1368,7 +1372,9 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)return -1 ; kmod = dso->symtab_type ==DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE || - dso->symtab_type ==DSO_BINARY_TYPE__GUEST_KMODULE ; + dso->symtab_type ==DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE_COMP || + dso->symtab_type ==DSO_BINARY_TYPE__GUEST_KMODULE || + dso->symtab_type ==DSO_BINARY_TYPE__GUEST_KMODULE_COMP ;/* * Iterate over candidate debug images.
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 9/18 perf tools: Add gzip decompression support for kernel module
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
Now my Archlinux box shows module symbols correctly.
Before:
$ perf report --stdio
Failed to open /tmp/perf-3477.map, continuing without symbols
no symbols found in /usr/bin/date, maybe install a debug package?
No kallsyms or vmlinux with build-id 7b4ea0a49ae2111925857099aaf05c3246ff33e0 was found
[drm] with build id 7b4ea0a49ae2111925857099aaf05c3246ff33e0 not found, continuing without symbols
No kallsyms or vmlinux with build-id edd931629094b660ca9dec09a1b635c8d87aa2ee was found
[jbd2] with build id edd931629094b660ca9dec09a1b635c8d87aa2ee not found, continuing without symbols
No kallsyms or vmlinux with build-id a7b1eada671c34933e5610bb920b2ca4945a82c3 was found
[ext4] with build id a7b1eada671c34933e5610bb920b2ca4945a82c3 not found, continuing without symbols
No kallsyms or vmlinux with build-id d69511fa3e5840e770336ef45b06c83fef8d74e3 was found
[scsi_mod] with build id d69511fa3e5840e770336ef45b06c83fef8d74e3 not found, continuing without symbols
No kallsyms or vmlinux with build-id af0430af13461af058770ee9b87afc07922c2e77 was found
[libata] with build id af0430af13461af058770ee9b87afc07922c2e77 not found, continuing without symbols
No kallsyms or vmlinux with build-id aaeedff8160ce631a5f0333591c6ff291201d29f was found
[libahci] with build id aaeedff8160ce631a5f0333591c6ff291201d29f not found, continuing without symbols
No kallsyms or vmlinux with build-id c57907712becaf662dc4981824bb372c0441d605 was found
[mac80211] with build id c57907712becaf662dc4981824bb372c0441d605 not found, continuing without symbols
No kallsyms or vmlinux with build-id e0589077cc0ec8c3e4c40eb9f2d9e69d236bee8f was found
[iwldvm] with build id e0589077cc0ec8c3e4c40eb9f2d9e69d236bee8f not found, continuing without symbols
No kallsyms or vmlinux with build-id 2d86086bf136bf374a2f029cf85a48194f9b950b was found
[cfg80211] with build id 2d86086bf136bf374a2f029cf85a48194f9b950b not found, continuing without symbols
No kallsyms or vmlinux with build-id 4493c48599bdb3d91d0f8db5150e0be33fdd9221 was found
[iwlwifi] with build id 4493c48599bdb3d91d0f8db5150e0be33fdd9221 not found, continuing without symbols
...
#
# Overhead Command Shared Object Symbol
# ........ ............... ....................... ........................................................
#
0.03% swapper [ext4] [k] 0x000000000000fe2e
0.03% swapper [kernel.kallsyms] [k] account_entity_enqueue
0.03% swapper [ext4] [k] 0x000000000000fc2b
0.03% irq/50-iwlwifi [iwlwifi] [k] 0x000000000000200b
0.03% swapper [kernel.kallsyms] [k] ktime_add_safe
0.03% swapper [kernel.kallsyms] [k] elv_completed_request
0.03% swapper [libata] [k] 0x0000000000003997
0.03% swapper [libahci] [k] 0x0000000000001f25
0.03% swapper [kernel.kallsyms] [k] rb_next
0.03% swapper [kernel.kallsyms] [k] blk_finish_request
0.03% swapper [ext4] [k] 0x0000000000010248
0.00% perf [kernel.kallsyms] [k] native_write_msr_safeAfter:
$ perf report --stdio Failed to open /tmp/perf-3477.map, continuing without symbols no symbols found in /usr/bin/tr, maybe install a debug package? ... # # Overhead Command Shared Object Symbol # ........ ............... ........................... ...................................................... #
0.04% kworker/u16:3 [ext4] [k] ext4_read_block_bitmap
0.03% kworker/u16:0 [mac80211] [k] ieee80211_sta_reset_beacon_monitor
0.02% irq/50-iwlwifi [mac80211] [k] ieee80211_get_bssid
0.02% firefox [e1000e] [k] __ew32_prepare
0.02% swapper [libahci] [k] ahci_handle_port_interrupt
0.02% emacs libglib-2.0.so.0.4000.0 [.] g_mutex_unlock
0.02% swapper [e1000e] [k] e1000_clean_tx_irq
0.02% dwm [kernel.kallsyms] [k] __schedule
0.02% gnome-terminal- [vdso] [.] __vdso_clock_gettime
0.02% swapper [e1000e] [k] e1000_alloc_rx_buffers
0.02% irq/50-iwlwifi [mac80211] [k] ieee80211_rx
0.01% firefox [vdso] [.] __vdso_gettimeofday
0.01% irq/50-iwlwifi [iwlwifi] [k] iwl_pcie_rxq_restock.part.13--- tools/perf/Makefile.perf | 7 +++ tools/perf/config/Makefile | 15 +++++- tools/perf/config/feature-checks/Makefile | 8 ++- tools/perf/config/feature-checks/test-all.c | 5 ++ tools/perf/config/feature-checks/test-zlib.c | 9 ++++ tools/perf/util/dso.c | 12 ++--- tools/perf/util/util.h | 5 ++ tools/perf/util/zlib.c | 78 ++++++++++++++++++++++++++++ 8 files changed, 127 insertions(+), 12 deletions(-) create mode 100644 tools/perf/config/feature-checks/test-zlib.c create mode 100644 tools/perf/util/zlib.c
tools/perf/Makefile.perf +7 -0
@@ -66,6 +66,9 @@ include config/utilities.mak # # Define NO_PERF_READ_VDSOX32 if you do not want to build perf-read-vdsox32 # for reading the x32 mode 32-bit compatibility VDSO in 64-bit mode +# +# Define NO_ZLIB if you do not want to support compressed kernel modules + ifeq ($(srctree),) srctree := $(patsubst %/,%,$(dir $(shell pwd)))
@@ -584,6 +587,10 @@ ifndef NO_LIBNUMA BUILTIN_OBJS += $(OUTPUT)bench/numa.o endif +ifndef NO_ZLIB + LIB_OBJS += $(OUTPUT)util/zlib.o +endif + ifdef ASCIIDOC8 export ASCIIDOC8 endif
tools/perf/config/Makefile +13 -2
@@ -200,7 +200,8 @@ CORE_FEATURE_TESTS = \ libunwind \ stackprotector-all \ timerfd \ - libdw-dwarf-unwind + libdw-dwarf-unwind \ + zlib LIB_FEATURE_TESTS = \ dwarf \
@@ -214,7 +215,8 @@ LIB_FEATURE_TESTS = \ libpython \ libslang \ libunwind \ - libdw-dwarf-unwind + libdw-dwarf-unwind \ + zlib VF_FEATURE_TESTS = \ backtrace \
@@ -604,6 +606,15 @@ ifneq ($(filter -lbfd,$(EXTLIBS)),) CFLAGS += -DHAVE_LIBBFD_SUPPORT endif +ifndef NO_ZLIB + ifeq ($(feature-zlib), 1) + CFLAGS += -DHAVE_ZLIB_SUPPORT + EXTLIBS += -lz + else + NO_ZLIB := 1 + endif +endif + ifndef NO_BACKTRACE ifeq ($(feature-backtrace), 1) CFLAGS += -DHAVE_BACKTRACE_SUPPORT
tools/perf/config/feature-checks/Makefile +6 -2
@@ -29,7 +29,8 @@ FILES= \ test-timerfd.bin \ test-libdw-dwarf-unwind.bin \ test-compile-32.bin \ - test-compile-x32.bin + test-compile-x32.bin \ + test-zlib.bin CC := $(CROSS_COMPILE)gcc -MD PKG_CONFIG := $(CROSS_COMPILE)pkg-config
@@ -41,7 +42,7 @@ BUILD = $(CC) $(CFLAGS) -o $(OUTPUT)$@ $(patsubst %.bin,%.c,$@) $(LDFLAGS) ############################### test-all.bin: - $(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl + $(BUILD) -Werror -fstack-protector-all -O2 -Werror -D_FORTIFY_SOURCE=2 -ldw -lelf -lnuma -lelf -laudit -I/usr/include/slang -lslang $(shell $(PKG_CONFIG) --libs --cflags gtk+-2.0 2>/dev/null) $(FLAGS_PERL_EMBED) $(FLAGS_PYTHON_EMBED) -DPACKAGE='"perf"' -lbfd -ldl -lz test-hello.bin: $(BUILD)
@@ -139,6 +140,9 @@ test-compile-32.bin: test-compile-x32.bin: $(CC) -mx32 -o $(OUTPUT)$@ test-compile.c +test-zlib.bin: + $(BUILD) -lz + -include *.d ###############################
tools/perf/config/feature-checks/test-all.c +5 -0
@@ -93,6 +93,10 @@# include "test-sync-compare-and-swap.c" #undef main +#define main main_test_zlib +# include "test-zlib.c" +#undef main +int main (int argc,char *argv[]) {main_test_libpython ();
@@ -116,6 +120,7 @@ int main(int argc, char *argv[])main_test_stackprotector_all ();main_test_libdw_dwarf_unwind ();main_test_sync_compare_and_swap (argc, argv); +main_test_zlib ();return 0 ; }
tools/perf/config/feature-checks/test-zlib.c new file +9 -0
@@ -0,0 +1,9 @@ +#include <zlib.h> + +int main (void ) +{ +z_stream zs; + +inflateInit (&zs); +return 0 ; +}
tools/perf/util/dso.c +4 -8
@@ -141,18 +141,14 @@ int dso__read_binary_type_filename(const struct dso *dso,return ret; } -static int decompress_dummy (const char *input__maybe_unused , -int output__maybe_unused ) -{ -return -1 ; -} -static const struct {const char *fmt;int (*decompress)(const char *input,int output); } compressions[] = { - {"gz" , decompress_dummy }, - {NULL , }, +#ifdef HAVE_ZLIB_SUPPORT + {"gz" , gzip_decompress_to_file }, +#endif + {NULL ,NULL }, };bool is_supported_compression (const char *ext)
tools/perf/util/util.h +5 -0
@@ -351,4 +351,9 @@ void mem_bswap_32(void *src, int byte_size);const char *get_filename_for_perf_kvm (void );bool find_process (const char *name); + +#ifdef HAVE_ZLIB_SUPPORT +int gzip_decompress_to_file (const char *input,int output_fd); +#endif +#endif /* GIT_COMPAT_UTIL_H */
tools/perf/util/zlib.c new file +78 -0
78 lines not on this page · raw message
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 10/18 perf build-id: Rename dsos__write_buildid_table()
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
The dsos__write_buildid_table() is not use struct dso and it mostly uses perf_session struct.
So rename it to perf_session__write_buildid_ table() so that it corresponds to other related functions such as perf_session__read_build_ids() and perf_session__cache_build_ids().
--- tools/perf/util/header.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
tools/perf/util/header.c +2 -4
@@ -297,10 +297,8 @@ static int machine__write_buildid_table(struct machine *machine, int fd)return err; } -static int dsos__write_buildid_table (struct perf_header *header,int fd) +static int perf_session__write_buildid_table (struct perf_session *session,int fd) { -struct perf_session *session =container_of (header, - struct perf_session, header);struct rb_node *nd;int err =machine__write_buildid_table (&session->machines.host, fd);
@@ -523,7 +521,7 @@ static int write_build_id(int fd, struct perf_header *h,if (!perf_session__read_build_ids (session, true))return -1 ; - err =dsos__write_buildid_table (h, fd); + err =perf_session__write_buildid_table (session, fd);if (err <0 ) {pr_debug ("failed to write buildid table\n" );return err;
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 11/18 perf build-id: Move build-id related functions to util/build-id.c
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
It'd be better managing those functions in a separate place as util/header.c file is already big.
It now exports following 3 functions to others:
bool perf_session__read_build_ids(struct perf_session *session, bool with_hits); int perf_session__write_buildid_table(struct perf_session *session, int fd); int perf_session__cache_build_ids(struct perf_session *session);
--- tools/perf/util/build-id.c | 334 ++++++++++++++++++++++++++++++++++++++++++++ tools/perf/util/build-id.h | 11 ++ tools/perf/util/header.c | 337 +-------------------------------------------- tools/perf/util/header.h | 8 +- 4 files changed, 349 insertions(+), 341 deletions(-)
tools/perf/util/build-id.c +334 -0
@@ -15,6 +15,8 @@#include "debug.h" #include "session.h" #include "tool.h" +#include "header.h" +#include "vdso.h" int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused, union perf_event *event,
@@ -105,3 +107,335 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size) build_id_hex, build_id_hex + 2);return bf; } + +#define dsos__for_each_with_build_id (pos, head) \ + list_for_each_entry(pos, head, node) \ + if (!pos->has_build_id) \ + continue; \ + else + +static int write_buildid (const char *name,size_t name_len,u8 *build_id, +pid_t pid,u16 misc,int fd) +{ +int err; +struct build_id_event b; +size_t len; + + len = name_len +1 ; + len =PERF_ALIGN (len,NAME_ALIGN ); + +memset (&b,0 ,sizeof (b)); +memcpy (&b.build_id, build_id,BUILD_ID_SIZE ); + b.pid = pid; + b.header.misc = misc; + b.header.size =sizeof (b) + len; + + err =writen (fd, &b,sizeof (b)); +if (err <0 ) +return err; + +return write_padded (fd, name, name_len +1 , len); +} + +static int __dsos__write_buildid_table (struct list_head *head, +struct machine *machine, +pid_t pid,u16 misc,int fd) +{ +char nm[PATH_MAX ]; +struct dso *pos; + +dsos__for_each_with_build_id (pos, head) { +int err; +const char *name; +size_t name_len; + +if (!pos->hit) +continue ; + +if (dso__is_vdso (pos)) { + name = pos->short_name; + name_len = pos->short_name_len +1 ; + }else if (dso__is_kcore (pos)) { +machine__mmap_name (machine, nm,sizeof (nm)); + name = nm; + name_len =strlen (nm) +1 ; + }else { + name = pos->long_name; + name_len = pos->long_name_len +1 ; + } + + err =write_buildid (name, name_len, pos->build_id, + pid, misc, fd); +if (err) +return err; + } + +return 0 ; +} + +static int machine__write_buildid_table (struct machine *machine,int fd) +{ +int err; +u16 kmisc =PERF_RECORD_MISC_KERNEL , + umisc =PERF_RECORD_MISC_USER ; + +if (!machine__is_host (machine)) { + kmisc =PERF_RECORD_MISC_GUEST_KERNEL ; + umisc =PERF_RECORD_MISC_GUEST_USER ; + } + + err =__dsos__write_buildid_table (&machine->kernel_dsos.head, machine, + machine->pid, kmisc, fd); +if (err ==0 ) + err =__dsos__write_buildid_table (&machine->user_dsos.head, + machine, machine->pid, umisc, + fd); +return err; +} + +int perf_session__write_buildid_table (struct perf_session *session,int fd) +{ +struct rb_node *nd; +int err =machine__write_buildid_table (&session->machines.host, fd); + +if (err) +return err; + +for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + err =machine__write_buildid_table (pos, fd); +if (err) +break ; + } +return err; +} + +static int __dsos__hit_all (struct list_head *head) +{ +struct dso *pos; + +list_for_each_entry (pos, head, node) + pos->hit = true; + +return 0 ; +} + +static int machine__hit_all_dsos (struct machine *machine) +{ +int err; + + err =__dsos__hit_all (&machine->kernel_dsos.head); +if (err) +return err; + +return __dsos__hit_all (&machine->user_dsos.head); +} + +int dsos__hit_all (struct perf_session *session) +{ +struct rb_node *nd; +int err; + + err =machine__hit_all_dsos (&session->machines.host); +if (err) +return err; + +for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + + err =machine__hit_all_dsos (pos); +if (err) +return err; + } + +return 0 ; +} + +int build_id_cache__add_s (const char *sbuild_id,const char *debugdir, +const char *name,bool is_kallsyms,bool is_vdso) +{ +const size_t size =PATH_MAX ; +char *realname, *filename =zalloc (size), + *linkname =zalloc (size), *targetname; +int len, err =-1 ; +bool slash = is_kallsyms || is_vdso; + +if (is_kallsyms) { +if (symbol_conf.kptr_restrict) { +pr_debug ("Not caching a kptr_restrict'ed /proc/kallsyms\n" ); + err =0 ; + goto out_free; + } + realname = (char *) name; + }else + realname =realpath (name,NULL ); + +if (realname ==NULL || filename ==NULL || linkname ==NULL ) + goto out_free; + + len =scnprintf (filename, size,"%s%s%s" , + debugdir, slash ?"/" :"" , + is_vdso ?DSO__NAME_VDSO : realname); +if (mkdir_p (filename,0755 )) + goto out_free; + +snprintf (filename + len, size - len,"/%s" , sbuild_id); + +if (access (filename,F_OK )) { +if (is_kallsyms) { +if (copyfile ("/proc/kallsyms" , filename)) + goto out_free; + }else if (link (realname, filename) &©file (name, filename)) + goto out_free; + } + + len =scnprintf (linkname, size,"%s/.build-id/%.2s" , + debugdir, sbuild_id); + +if (access (linkname,X_OK ) &&mkdir_p (linkname,0755 )) + goto out_free; + +snprintf (linkname + len, size - len,"/%s" , sbuild_id +2 ); + targetname = filename +strlen (debugdir) -5 ; +memcpy (targetname,"../.." ,5 ); + +if (symlink (targetname, linkname) ==0 ) + err =0 ; +out_free: +if (!is_kallsyms) +free (realname); +free (filename); +free (linkname); +return err; +} + +static int build_id_cache__add_b (const u8 *build_id,size_t build_id_size, +const char *name,const char *debugdir, +bool is_kallsyms,bool is_vdso) +{ +char sbuild_id[BUILD_ID_SIZE *2 +1 ]; + +build_id__sprintf (build_id, build_id_size, sbuild_id); + +return build_id_cache__add_s (sbuild_id, debugdir, name, + is_kallsyms, is_vdso); +} + +int build_id_cache__remove_s (const char *sbuild_id,const char *debugdir) +{ +const size_t size =PATH_MAX ; +char *filename =zalloc (size), + *linkname =zalloc (size); +int err =-1 ; + +if (filename ==NULL || linkname ==NULL ) + goto out_free; + +snprintf (linkname, size,"%s/.build-id/%.2s/%s" , + debugdir, sbuild_id, sbuild_id +2 ); + +if (access (linkname,F_OK )) + goto out_free; + +if (readlink (linkname, filename, size -1 ) <0 ) + goto out_free; + +if (unlink (linkname)) + goto out_free; + +/* +* Since the link is relative, we must make it absolute: +*/ +snprintf (linkname, size,"%s/.build-id/%.2s/%s" , + debugdir, sbuild_id, filename); + +if (unlink (linkname)) + goto out_free; + + err =0 ; +out_free: +free (filename); +free (linkname); +return err; +} + +static int dso__cache_build_id (struct dso *dso,struct machine *machine, +const char *debugdir) +{ +bool is_kallsyms = dso->kernel && dso->long_name[0 ] !='/' ; +bool is_vdso =dso__is_vdso (dso); +const char *name = dso->long_name; +char nm[PATH_MAX ]; + +if (dso__is_kcore (dso)) { + is_kallsyms = true; +machine__mmap_name (machine, nm,sizeof (nm)); + name = nm; + } +return build_id_cache__add_b (dso->build_id,sizeof (dso->build_id), name, + debugdir, is_kallsyms, is_vdso); +} + +static int __dsos__cache_build_ids (struct list_head *head, +struct machine *machine,const char *debugdir) +{ +struct dso *pos; +int err =0 ; + +dsos__for_each_with_build_id (pos, head) +if (dso__cache_build_id (pos, machine, debugdir)) + err =-1 ; + +return err; +} + +static int machine__cache_build_ids (struct machine *machine,const char *debugdir) +{ +int ret =__dsos__cache_build_ids (&machine->kernel_dsos.head, machine, + debugdir); + ret |=__dsos__cache_build_ids (&machine->user_dsos.head, machine, + debugdir); +return ret; +} + +int perf_session__cache_build_ids (struct perf_session *session) +{ +struct rb_node *nd; +int ret; +char debugdir[PATH_MAX ]; + +snprintf (debugdir,sizeof (debugdir),"%s" , buildid_dir); + +if (mkdir (debugdir,0755 ) !=0 && errno !=EEXIST ) +return -1 ; + + ret =machine__cache_build_ids (&session->machines.host, debugdir); + +for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + ret |=machine__cache_build_ids (pos, debugdir); + } +return ret ?-1 :0 ; +} + +static bool machine__read_build_ids (struct machine *machine,bool with_hits) +{ +bool ret; + + ret =__dsos__read_build_ids (&machine->kernel_dsos.head, with_hits); + ret |=__dsos__read_build_ids (&machine->user_dsos.head, with_hits); +return ret; +} + +bool perf_session__read_build_ids (struct perf_session *session,bool with_hits) +{ +struct rb_node *nd; +bool ret =machine__read_build_ids (&session->machines.host, with_hits); + +for (nd =rb_first (&session->machines.guests); nd; nd =rb_next (nd)) { + struct machine *pos = rb_entry(nd, struct machine, rb_node); + ret |=machine__read_build_ids (pos, with_hits); + } + +return ret; +}
tools/perf/util/build-id.h +11 -0
@@ -15,4 +15,15 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size);int build_id__mark_dso_hit (struct perf_tool *tool,union perf_event *event,struct perf_sample *sample,struct perf_evsel *evsel,struct machine *machine); + +int dsos__hit_all (struct perf_session *session); + +bool perf_session__read_build_ids (struct perf_session *session,bool with_hits); +int perf_session__write_buildid_table (struct perf_session *session,int fd); +int perf_session__cache_build_ids (struct perf_session *session); + +int build_id_cache__add_s (const char *sbuild_id,const char *debugdir, +const char *name,bool is_kallsyms,bool is_vdso); +int build_id_cache__remove_s (const char *sbuild_id,const char *debugdir); +#endif
tools/perf/util/header.c +1 -336
349 lines not on this page · raw message
tools/perf/util/header.h +3 -5
@@ -122,10 +122,6 @@ int perf_header__process_sections(struct perf_header *header, int fd,int perf_header__fprintf_info (struct perf_session *s,FILE *fp,bool full); -int build_id_cache__add_s (const char *sbuild_id,const char *debugdir, -const char *name,bool is_kallsyms,bool is_vdso); -int build_id_cache__remove_s (const char *sbuild_id,const char *debugdir); -int perf_event__synthesize_attr (struct perf_tool *tool,struct perf_event_attr *attr,u32 ids,u64 *id,perf_event__handler_t process);
@@ -151,7 +147,9 @@ int perf_event__process_build_id(struct perf_tool *tool, struct perf_session *session);bool is_perf_magic (u64 magic); -int dsos__hit_all (struct perf_session *session); +#define NAME_ALIGN 64 + +int write_padded (int fd,const void *bf,size_t count,size_t count_aligned);/* * arch specific callback
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 12/18 perf record: Do not save pathname in ./debug/.build-id directory for vmlinux
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
When perf record finishes a session, it pre-processes samples in order to write build-id info from DSOs that had samples.
During this process it'll call map__load() for the kernel map, and it ends up calling dso__load_vmlinux_path() which replaces dso->long_name.
But this function checks kernel's build-id before searching vmlinux path so it'll end up with a cryptic name, the pathname for the entry in the ~/.debug cache, which can be confusing to users.
This patch adds a flag to skip the build-id check during record, so that it'll have the original vmlinux path for the kernel dso->long_name, not the entry in the ~/.debug cache.
Before:
# perf record -va sleep 3 mmap size 528384B [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.196 MB perf.data (~8545 samples) ] Looking at the vmlinux_path (7 entries long) Using /home/namhyung/.debug/.build-id/f0/6e17aa50adf4d00b88925e03775de107611551 for symbols
After:
# perf record -va sleep 3 mmap size 528384B [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.193 MB perf.data (~8432 samples) ] Looking at the vmlinux_path (7 entries long) Using /lib/modules/3.16.4-1-ARCH/build/vmlinux for symbols
--- tools/perf/builtin-record.c | 11 +++++++++++ tools/perf/util/symbol.c | 11 ++++++----- tools/perf/util/symbol.h | 1 + 3 files changed, 18 insertions(+), 5 deletions(-)
tools/perf/builtin-record.c +11 -0
@@ -200,6 +200,17 @@ static int process_buildids(struct record *rec)if (size ==0 )return 0 ; +/* +* During this process, it'll load kernel map and replace the +* dso->long_name to a real pathname it found. In this case +* we prefer the vmlinux path like +* /lib/modules/3.16.4/build/vmlinux +* +* rather than build-id path (in debug directory). +* $HOME/.debug/.build-id/f0/6e17aa50adf4d00b88925e03775de107611551 +*/ + symbol_conf.ignore_vmlinux_buildid = true; +return __perf_session__process_events (session, start, size - start, size, &build_id__mark_dso_hit_ops);
tools/perf/util/symbol.c +6 -5
@@ -1511,12 +1511,10 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, symbol_filter_t filter) {int i, err =0 ; -char *filename; +char *filename =NULL ; -pr_debug ("Looking at the vmlinux_path (%d entries long)\n" , - vmlinux_path__nr_entries +1 ); - - filename =dso__build_id_filename (dso,NULL ,0 ); +if (!symbol_conf.ignore_vmlinux_buildid) + filename =dso__build_id_filename (dso,NULL ,0 );if (filename !=NULL ) { err =dso__load_vmlinux (dso, map, filename, true, filter);if (err >0 )
@@ -1524,6 +1522,9 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map,free (filename); } +pr_debug ("Looking at the vmlinux_path (%d entries long)\n" , + vmlinux_path__nr_entries +1 ); +for (i =0 ; i < vmlinux_path__nr_entries; ++i) { err =dso__load_vmlinux (dso, map, vmlinux_path[i], false, filter);if (err >0 )
tools/perf/util/symbol.h +1 -0
@@ -105,6 +105,7 @@ struct symbol_conf {unsigned short nr_events;bool try_vmlinux_path, ignore_vmlinux, + ignore_vmlinux_buildid, show_kernel_path, use_modules, sort_by_name,
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 13/18 perf tools: Fix build-id matching on vmlinux
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
There's a problem on finding correct kernel symbols when perf report runs on a different kernel. Although a part of the problem was solved by the prior commit 0a7e6d1b6844 ("perf tools: Check recorded kernel version when finding vmlinux"), there's a remaining problem still.
When perf records samples, it synthesizes the kernel map using machine__mmap_name() and ref_reloc_sym like "[kernel.kallsyms]_text". You can easily see it using 'perf report -D' command.
After finishing record, it goes through the recorded events to find maps/dsos actually used. And then record build-id info of them.
During this process, it needs to load symbols in a dso and it'd call dso__load_vmlinux_path() since the default value of the symbol_conf. try_vmlinux_path is true. However it changes dso->long_name to a real path of the vmlinux file (e.g. /lib/modules/3.16.4/build/vmlinux) if one is running on a custom kernel.
It resulted in that perf report reads the build-id of the vmlinux, but cannot use it since it only knows about the [kernel.kallsyms] map. It then falls back to possible vmlinux paths by using the recorded kernel version (in case of a recent version) or a running kernel silently.
Even with the recent tools, this still has a possibility of breaking the result. As the build directory is a symbolic link, if one built a new kernel in the same directory with different source/config, the old link to vmlinux will point the new file. So it's absolutely needed to use build-id when finding a kernel image.
In this patch, it's now changed to try to search a kernel dso in the existing dso list which was constructed during build-id table parsing so it'll always have a build-id. If not found, search "[kernel.kallsyms]".
Before:
$ perf report
# Children Self Command Shared Object Symbol
# ........ ........ ....... ................. ...............................
#
72.15% 0.00% swapper [kernel.kallsyms] [k] set_curr_task_rt
72.15% 0.00% swapper [kernel.kallsyms] [k] native_calibrate_tsc
72.15% 0.00% swapper [kernel.kallsyms] [k] tsc_refine_calibration_work
71.87% 71.87% swapper [kernel.kallsyms] [k] module_finalize
...After (for the same perf.data):
72.15% 0.00% swapper vmlinux [k] cpu_startup_entry
72.15% 0.00% swapper vmlinux [k] arch_cpu_idle
72.15% 0.00% swapper vmlinux [k] default_idle
71.87% 71.87% swapper vmlinux [k] native_safe_halt...
--- tools/perf/util/header.c | 2 +- tools/perf/util/machine.c | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-)
tools/perf/util/header.c +1 -1
@@ -1269,7 +1269,7 @@ static int __event_process_build_id(struct build_id_event *bev,dso__set_build_id (dso, &bev->build_id); -if (filename[0 ] =='[' ) +if (!is_kernel_module (filename,NULL )) dso->kernel = dso_type; build_id__sprintf(dso->build_id, sizeof(dso->build_id),
tools/perf/util/machine.c +14 -2
@@ -1085,8 +1085,20 @@ static int machine__process_kernel_mmap_event(struct machine *machine,* Should be there already, from the build-id table in * the header. */ -struct dso *kernel =__dsos__findnew (&machine->kernel_dsos, - kmmap_prefix); +struct dso *kernel =NULL ; +struct dso *dso; + +list_for_each_entry (dso, &machine->kernel_dsos.head, node) { +if (is_kernel_module (dso->long_name,NULL )) +continue ; + + kernel = dso; +break ; + } + +if (kernel ==NULL ) + kernel =__dsos__findnew (&machine->kernel_dsos, + kmmap_prefix);if (kernel ==NULL ) goto out_problem;
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 14/18 perf tools: Make vmlinux short name more like kallsyms short name
In reply to Arnaldo Carvalho de MeloFrom: Namhyung Kim <[email protected]>
The previous patch changed kernel dso name from '[kernel.kallsyms]' to vmlinux. However it might add confusion to old users accustomed to the old name. So change the short name to '[kernel.vmlinux]' to reduce such confusion.
Before:
# Overhead Command Shared Object Symbol
# ........ .............. ....................... ...............................
#
9.83% swapper vmlinux [k] intel_idle
4.10% awk libc-2.20.so [.] __strcmp_sse2
1.86% sed libc-2.20.so [.] __strcmp_sse2
1.78% netctl-auto libc-2.20.so [.] __strcmp_sse2
1.23% netctl-auto libc-2.20.so [.] __mbrtowc
1.21% firefox libxul.so [.] 0x00000000024b62bd
1.20% swapper vmlinux [k] cpuidle_enter_state
1.03% sleep vmlinux [k] copy_user_generic_unrolledAfter:
# Overhead Command Shared Object Symbol
# ........ .............. ....................... ...............................
#
9.83% swapper [kernel.vmlinux] [k] intel_idle
4.10% awk libc-2.20.so [.] __strcmp_sse2
1.86% sed libc-2.20.so [.] __strcmp_sse2
1.78% netctl-auto libc-2.20.so [.] __strcmp_sse2
1.23% netctl-auto libc-2.20.so [.] __mbrtowc
1.21% firefox libxul.so [.] 0x00000000024b62bd
1.20% swapper [kernel.vmlinux] [k] cpuidle_enter_state
1.03% sleep [kernel.vmlinux] [k] copy_user_generic_unrolled--- tools/perf/util/machine.c | 3 +++ 1 file changed, 3 insertions(+)
tools/perf/util/machine.c +3 -0
@@ -1106,6 +1106,9 @@ static int machine__process_kernel_mmap_event(struct machine *machine,if (__machine__create_kernel_maps (machine, kernel) <0 ) goto out_problem; +if (strstr (dso->long_name,"vmlinux" )) +dso__set_short_name (dso,"[kernel.vmlinux]" , false); +machine__set_kernel_mmap_len (machine, event);/*
Signature · 1 line
1.9.3
Re: [PATCH 14/18] perf tools: Make vmlinux short name more like kallsyms short name
Quoted 7 lines · Arnaldo Carvalho de Melo
From: Namhyung Kim <[email protected]>
The previous patch changed kernel dso name from '[kernel.kallsyms]' to vmlinux. However it might add confusion to old users accustomed to the old name. So change the short name to '[kernel.vmlinux]' to reduce such confusion.
SNIP
Quoted 10 lines
Signed-off-by Arnaldo Carvalho de Melo <[email protected]>--- tools/perf/util/machine.c | 3 +++ 1 file changed, 3 insertions(+)tools/perf/util/machine.c +0
-0
tools/perf/util/machine.c static int machine__process_kernel_mmap_event(struct machine *machine, · line 1111
Quoted 6 lines
tools/perf/util/machine.c +3
-0@@ -1106,6 +1106,9 @@ static int machine__process_kernel_mmap_event(struct machine *machine,if (__machine__create_kernel_maps (machine, kernel) <0 ) goto out_problem; +if (strstr (dso->long_name,"vmlinux" )) +dso__set_short_name (dso,"[kernel.vmlinux]" , false); +
heya,
I've got attached segfault in perf script because of this, if I revert it's ok..
jirka
[jolsa@krava perf]$ ./perf record -e instructions -c 100000000 yes > /dev/null
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.013 MB perf.data (~576 samples) ]
[jolsa@krava perf]$ gdb ./perf
GNU gdb (GDB) Fedora 7.7.1-21.fc20
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./perf...done.
(gdb) r script
Starting program: /home/jolsa/kernel.org/linux-perf/tools/perf/perf script
warning: section not found in /usr/lib/debug/lib/modules/3.16.6-203.fc20.x86_64/vdso/vdso64.so.debug
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Detaching after fork from child process 6849.
Program received signal SIGSEGV, Segmentation fault.__strstr_sse42 (s1=0x0, s2=0x5db702 "vmlinux") at ../sysdeps/x86_64/multiarch/strstr.c:175
175 if (__builtin_expect (p1[0] == '\0', 0))
Missing separate debuginfos, use: debuginfo-install audit-libs-2.4-2.fc20.x86_64 elfutils-libelf-0.160-1.fc20.x86_64 elfutils-libs-0.160-1.fc20.x86_64 libunwind-1.1-3.fc20.x86_64 nss-softokn-freebl-3.17.2-1.fc20.x86_64 numactl-libs-2.0.9-2.fc20.x86_64 perl-libs-5.18.4-290.fc20.x86_64 python-libs-2.7.5-14.fc20.x86_64 slang-2.2.4-11.fc20.x86_64 xz-libs-5.1.2-12alpha.fc20.x86_64 zlib-1.2.8-3.fc20.x86_64 (gdb) bt
#0 __strstr_sse42 (s1=0x0, s2=0x5db702 "vmlinux") at ../sysdeps/x86_64/multiarch/strstr.c:175
#1 0x00000000004a881d in machine__process_kernel_mmap_event (machine=0x8d3e80, event=0x7ffff7ff50d8)
at util/machine.c:1109
#2 0x00000000004a8b4f in machine__process_mmap_event (machine=0x8d3e80, event=0x7ffff7ff50d8,
sample=0x7fffffffd280) at util/machine.c:1203
#3 0x0000000000470e7e in perf_event__process_mmap (tool=0x7fffffffd650, event=0x7ffff7ff50d8,
sample=0x7fffffffd280, machine=0x8d3e80) at util/event.c:666
#4 0x00000000004af2fa in perf_session__deliver_event (session=0x8d3dc0, event=0x7ffff7ff50d8,
sample=0x7fffffffd280, tool=0x7fffffffd650, file_offset=216) at util/session.c:845
#5 0x00000000004afa96 in perf_session__process_event (session=0x8d3dc0, event=0x7ffff7ff50d8,
tool=0x7fffffffd650, file_offset=216) at util/session.c:1020
#6 0x00000000004b0451 in __perf_session__process_events (session=0x8d3dc0, data_offset=216, data_size=16240,
file_size=16456, tool=0x7fffffffd650) at util/session.c:1292
#7 0x00000000004b061e in perf_session__process_events (session=0x8d3dc0, tool=0x7fffffffd650)
at util/session.c:1337#8 0x00000000004453d4 in __cmd_script (script=0x7fffffffd650) at builtin-script.c:803
#9 0x0000000000448858 in cmd_script (argc=0, argv=0x7fffffffe2e0, prefix=0x0) at builtin-script.c:1835 #10 0x000000000041c88c in run_builtin (p=0x856f70 <commands+336>, argc=1, argv=0x7fffffffe2e0) at perf.c:331 #11 0x000000000041caeb in handle_internal_command (argc=1, argv=0x7fffffffe2e0) at perf.c:390 #12 0x000000000041cc37 in run_argv (argcp=0x7fffffffe13c, argv=0x7fffffffe130) at perf.c:434 #13 0x000000000041cf8e in main (argc=1, argv=0x7fffffffe2e0) at perf.c:549
(gdb) q
A debugging session is active.
Namhyung Kim
maintainerRe: [PATCH 14/18] perf tools: Make vmlinux short name more like kallsyms short name
Hi Jiri,
Quoted 24 lines · Jiri Olsa
Quoted 7 lines · Arnaldo Carvalho de Melo
From: Namhyung Kim <[email protected]>
The previous patch changed kernel dso name from '[kernel.kallsyms]' to vmlinux. However it might add confusion to old users accustomed to the old name. So change the short name to '[kernel.vmlinux]' to reduce such confusion.
SNIP
Quoted 13 lines
Signed-off-by Arnaldo Carvalho de Melo <[email protected]>--- tools/perf/util/machine.c | 3 +++ 1 file changed, 3 insertions(+)tools/perf/util/machine.c +0
-0@@ -1106,6 +1106,9 @@ static int machine__process_kernel_mmap_event(struct machine *machine,if (__machine__create_kernel_maps (machine, kernel) <0 ) goto out_problem;
Quoted 7 lines
tools/perf/util/machine.c static int machine__process_kernel_mmap_event(struct machine *machine, · line 1111
Quoted 3 lines
tools/perf/util/machine.c +3
-0+if (strstr (dso->long_name,"vmlinux" )) +dso__set_short_name (dso,"[kernel.vmlinux]" , false); +heya,
I've got attached segfault in perf script because of this, if I revert it's ok..
Oops, sorry. It seems somehow to fail to find a matching kernel dso from the build-id table.. anyway we need to access 'kernel' instead of 'dso' since it might be invalid at this time.
Could you please check below patch?
Quoted 1 line
From
e28ec815465721b81669c47eb00d8307f4b424cdMon Sep 17 00:00:00 2001
From: Namhyung Kim <[email protected]>
Date: Mon, 10 Nov 2014 15:05:26 +0900
Subject: [PATCH] perf tools: Fix segfault due to invalid kernel dso access
When processing kernel mmap event, it should access the 'kernel' variable as sometimes it cannot find a matching dso from build-id table so 'dso' might be invalid.
--- tools/perf/util/machine.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
tools/perf/util/machine.c +2 -2
@@ -1106,8 +1106,8 @@ static int machine__process_kernel_mmap_event(struct machine *machine,if (__machine__create_kernel_maps (machine, kernel) <0 ) goto out_problem; -if (strstr (dso->long_name,"vmlinux" )) -dso__set_short_name (dso,"[kernel.vmlinux]" , false); +if (strstr (kernel->long_name,"vmlinux" )) +dso__set_short_name (kernel,"[kernel.vmlinux]" , false);machine__set_kernel_mmap_len (machine, event);
Signature · 1 line
2.1.2
Jiri Olsa
Tested-byRe: [PATCH 14/18] perf tools: Make vmlinux short name more like kallsyms short name
On Mon, Nov 10, 2014 at 03:13:46PM +0900, Namhyung Kim wrote:
SNIP
Quoted 19 lines
Quoted 6 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ dso__set_short_name(dso, "[kernel.vmlinux]", false); +heya,
I've got attached segfault in perf script because of this, if I revert it's ok..Oops, sorry. It seems somehow to fail to find a matching kernel dso from the build-id table.. anyway we need to access 'kernel' instead of 'dso' since it might be invalid at this time.
Could you please check below patch?
From
e28ec815465721b81669c47eb00d8307f4b424cdMon Sep 17 00:00:00 2001
From: Namhyung Kim <[email protected]>
Date: Mon, 10 Nov 2014 15:05:26 +0900
Subject: [PATCH] perf tools: Fix segfault due to invalid kernel dso access
Quoted 6 lines
When processing kernel mmap event, it should access the 'kernel' variable as sometimes it cannot find a matching dso from build-id table so 'dso' might be invalid.
Reported-by Jiri Olsa <[email protected]>Signed-off-by Namhyung Kim <[email protected]>
yep, works nicely now
thanks,
jirka
Arnaldo Carvalho de Melo
maintainerPATCH 15/18 perf tools: Add test_and_set_bit function
In reply to Arnaldo Carvalho de MeloFrom: Jiri Olsa <[email protected]>
Set a bit and return its old value. Stolen from kernel sources, will be used in next patches.
--- tools/perf/util/include/linux/bitmap.h | 17 +++++++++++++++++ tools/perf/util/include/linux/bitops.h | 2 ++ 2 files changed, 19 insertions(+)
tools/perf/util/include/linux/bitmap.h +17 -0
@@ -46,4 +46,21 @@ static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, __bitmap_or(dst, src1, src2, nbits); } +/** +* test_and_set_bit - Set a bit and return its old value +* @nr: Bit to set +* @addr: Address to count from +*/ +static inline int test_and_set_bit (int nr,unsigned long *addr) +{ +unsigned long mask =BIT_MASK (nr); +unsigned long *p = ((unsigned long *)addr) +BIT_WORD (nr); +unsigned long old; + + old = *p; + *p = old | mask; + +return (old & mask) !=0 ; +} +#endif /* _PERF_BITOPS_H */
tools/perf/util/include/linux/bitops.h +2 -0
@@ -15,6 +15,8 @@#define BITS_TO_U64 (nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64))#define BITS_TO_U32 (nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32))#define BITS_TO_BYTES (nr) DIV_ROUND_UP(nr, BITS_PER_BYTE) +#define BIT_WORD (nr) ((nr) / BITS_PER_LONG) +#define BIT_MASK (nr) (1UL << ((nr) % BITS_PER_LONG))#define for_each_set_bit (bit, addr, size) \ for ((bit) = find_first_bit((addr), (size)); \
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 16/18 perf script perl: Removing event cache as it's no longer needed
In reply to Arnaldo Carvalho de MeloFrom: Jiri Olsa <[email protected]>
We don't need to maintain cache of 'struct event_format' objects. Currently the 'struct perf_evsel' holds this reference already.
Adding events_defined bitmap to keep track of defined events, which is much cheaper than array of pointers.
--- .../perf/util/scripting-engines/trace-event-perl.c | 29 +++++----------------- 1 file changed, 6 insertions(+), 23 deletions(-)
tools/perf/util/scripting-engines/trace-event-perl.c +6 -23
@@ -24,6 +24,7 @@#include <string.h> #include <ctype.h> #include <errno.h> +#include <linux/bitmap.h> #include "../util.h" #include <EXTERN.h>
@@ -57,7 +58,7 @@ INTERP my_perl;#define FTRACE_MAX_EVENT \ ((1 << (sizeof(unsigned short) * 8)) - 1) -struct event_format *events[FTRACE_MAX_EVENT ]; +static DECLARE_BITMAP(events_defined, FTRACE_MAX_EVENT);extern struct scripting_context *scripting_context;
@@ -238,35 +239,15 @@ static void define_event_symbols(struct event_format *event,define_event_symbols (event, ev_name, args->next); } -static inline struct event_format *find_cache_event (struct perf_evsel *evsel) -{ -static char ev_name[256 ]; -struct event_format *event; -int type = evsel->attr.config; - -if (events[type]) -return events[type]; - - events[type] = event = evsel->tp_format; -if (!event) -return NULL ; - -sprintf (ev_name,"%s::%s" , event->system, event->name); - -define_event_symbols (event, ev_name, event->print_fmt.args); - -return event; -} -static void perl_process_tracepoint (struct perf_sample *sample,struct perf_evsel *evsel,struct thread *thread) { +struct event_format *event = evsel->tp_format;struct format_field *field;static char handler[256 ];unsigned long long val;unsigned long s, ns; -struct event_format *event;int pid;int cpu = sample->cpu;void *data = sample->raw_data;
@@ -278,7 +259,6 @@ static void perl_process_tracepoint(struct perf_sample *sample,if (evsel->attr.type !=PERF_TYPE_TRACEPOINT )return ; - event =find_cache_event (evsel);if (!event)die ("ug! no event found for type %" PRIu64, (u64 )evsel->attr.config);
@@ -286,6 +266,9 @@ static void perl_process_tracepoint(struct perf_sample *sample,sprintf (handler,"%s::%s" , event->system, event->name); +if (!test_and_set_bit (event->id, events_defined)) +define_event_symbols (event, handler, event->print_fmt.args); + s = nsecs /NSECS_PER_SEC ; ns = nsecs - s *NSECS_PER_SEC ;
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 17/18 perf script python: Removing event cache as it's no longer needed
In reply to Arnaldo Carvalho de MeloFrom: Jiri Olsa <[email protected]>
We don't need to maintain cache of 'struct event_format' objects. Currently the 'struct perf_evsel' holds this reference already.
Adding events_defined bitmap to keep track of defined events, which is much cheaper than array of pointers.
--- .../util/scripting-engines/trace-event-python.c | 34 ++++------------------ 1 file changed, 6 insertions(+), 28 deletions(-)
tools/perf/util/scripting-engines/trace-event-python.c +6 -28
@@ -26,6 +26,7 @@#include <string.h> #include <stdbool.h> #include <errno.h> +#include <linux/bitmap.h> #include "../../perf.h" #include "../debug.h"
@@ -46,7 +47,7 @@ PyMODINIT_FUNC initperf_trace_context(void);#define FTRACE_MAX_EVENT \ ((1 << (sizeof(unsigned short) * 8)) - 1) -struct event_format *events[FTRACE_MAX_EVENT ]; +static DECLARE_BITMAP(events_defined, FTRACE_MAX_EVENT);#define MAX_FIELDS 64#define N_COMMON_FIELDS 7
@@ -255,31 +256,6 @@ static void define_event_symbols(struct event_format *event,define_event_symbols (event, ev_name, args->next); } -static inline struct event_format *find_cache_event (struct perf_evsel *evsel) -{ -static char ev_name[256 ]; -struct event_format *event; -int type = evsel->attr.config; - -/* -* XXX: Do we really need to cache this since now we have evsel->tp_format -* cached already? Need to re-read this "cache" routine that as well calls -* define_event_symbols() :-\ -*/ -if (events[type]) -return events[type]; - - events[type] = event = evsel->tp_format; -if (!event) -return NULL ; - -sprintf (ev_name,"%s__%s" , event->system, event->name); - -define_event_symbols (event, ev_name, event->print_fmt.args); - -return event; -} -static PyObject *get_field_numeric_entry (struct event_format *event,struct format_field *field,void *data) {
@@ -403,12 +379,12 @@ static void python_process_tracepoint(struct perf_sample *sample,struct thread *thread, struct addr_location *al) { + struct event_format *event = evsel->tp_format;PyObject *handler, *context, *t, *obj, *callchain;PyObject *dict =NULL ;static char handler_name[256 ];struct format_field *field;unsigned long s, ns; -struct event_format *event;unsigned n =0 ;int pid;int cpu = sample->cpu;
@@ -420,7 +396,6 @@ static void python_process_tracepoint(struct perf_sample *sample,if (!t)Py_FatalError ("couldn't create Python tuple" ); - event =find_cache_event (evsel);if (!event)die ("ug! no event found for type %d" , (int )evsel->attr.config);
@@ -428,6 +403,9 @@ static void python_process_tracepoint(struct perf_sample *sample,sprintf (handler_name,"%s__%s" , event->system, event->name); +if (!test_and_set_bit (event->id, events_defined)) +define_event_symbols (event, handler_name, event->print_fmt.args); + handler =get_handler (handler_name);if (!handler) { dict =PyDict_New ();
Signature · 1 line
1.9.3
Arnaldo Carvalho de Melo
maintainerPATCH 18/18 perf evsel: Do not call pevent_free_format when deleting tracepoint
In reply to Arnaldo Carvalho de MeloFrom: Jiri Olsa <[email protected]>
The libtraceevent library's main handle 'struct pevent' holds pointers of every event that was added to it via functions:
pevent_parse_format
pevent_parse_event
We can't release struct event_format (call pevent_free_format) separately, because that breaks that pointers array mentioned above and another add_event call could end up with segfault.
All added events are released within the handle cleanup in pevent_free.
--- tools/perf/util/evsel.c | 2 -- 1 file changed, 2 deletions(-)
tools/perf/util/evsel.c +0 -2
@@ -853,8 +853,6 @@ void perf_evsel__exit(struct perf_evsel *evsel)perf_evsel__free_id (evsel);close_cgroup (evsel->cgrp);zfree (&evsel->group_name); -if (evsel->tp_format) -pevent_free_format (evsel->tp_format);zfree (&evsel->name); perf_evsel__object.fini (evsel); }
Signature · 1 line
1.9.3
Quoted 30 lines · Arnaldo Carvalho de Melo
Hi Ingo,
Please consider pulling,
- Arnaldo
The following changes since commit
daa01794a4a36a1da1b09a529adec0c8c0b94ab2:perf evsel: Do not call
pevent_free_formatwhen deleting tracepoint (2014-11-06 17:47:14 -0300)are available in the git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux.gittags/perf-core-for-mingofor you to fetch changes up to
daa01794a4a36a1da1b09a529adec0c8c0b94ab2:perf evsel: Do not call
pevent_free_formatwhen deleting tracepoint (2014-11-06 17:47:14 -0300)----------------------------------------------------------------
perf/core improvements and fixes:Infrastructure:
o Add gzip decompression support for kernel modules (Namhyung Kim)
o More prep patches for Intel PT, including a a thread stack and more stuff made available via the database export mechanism (Adrian Hunter)
o Optimize checking that tracepoint events are defined in perf script perl/python (Jiri Olsa)
Quoted 7 lines
o Do not free pevent when deleting tracepoint evsel (Jiri Olsa)
o Fix build-id matching for vmlinux (Namhyung Kim)
Signed-off-by Arnaldo Carvalho de Melo <[email protected]>----------------------------------------------------------------
Pulled, thanks a lot Arnaldo!
Ingo