Commit 9326657a authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull perf updates from Ingo Molnar:
 "Kernel side changes:

   - Add Intel RAPL energy counter support (Stephane Eranian)
   - Clean up uprobes (Oleg Nesterov)
   - Optimize ring-buffer writes (Peter Zijlstra)

  Tooling side changes, user visible:

   - 'perf diff':
     - Add column colouring improvements (Ramkumar Ramachandra)

  - 'perf kvm':
     - Add guest related improvements, including allowing to specify a
       directory with guest specific /proc information (Dongsheng Yang)
     - Add shell completion support (Ramkumar Ramachandra)
     - Add '-v' option (Dongsheng Yang)
     - Support --guestmount (Dongsheng Yang)

   - 'perf probe':
     - Support showing source code, asking for variables to be collected
       at probe time and other 'perf probe' operations that use DWARF
       information.

       This supports only binaries with debugging information at this
       time, detached debuginfo (aka debuginfo packages) support should
       come in later patches (Masami Hiramatsu)

   - 'perf record':
     - Rename --no-delay option to --no-buffering, better reflecting its
       purpose and freeing up '--delay' to take the place of
       '--initial-delay', so that 'record' and 'stat' are consistent
       (Arnaldo Carvalho de Melo)
     - Default the -t/--thread option to no inheritance (Adrian Hunter)
     - Make per-cpu mmaps the default (Adrian Hunter)

   - 'perf report':
     - Improve callchain processing performance (Frederic Weisbecker)
     - Retain bfd reference to lookup source line numbers, greatly
       optimizing, among other use cases, 'perf report -s srcline'
       (Adrian Hunter)
     - Improve callchain processing performance even more (Namhyung Kim)
     - Add a perf.data file header window in the 'perf report' TUI,
       associated with the 'i' hotkey, providing a counterpart to the
       --header option in the stdio UI (Namhyung Kim)

   - 'perf script':
     - Add an option in 'perf script' to print the source line number
       (Adrian Hunter)
     - Add --header/--header-only options to 'script' and 'report', the
       default is not tho show the header info, but as this has been the
       default for some time, leave a single line explaining how to
       obtain that information (Jiri Olsa)
     - Add options to show comm, fork, exit and mmap PERF_RECORD_ events
       (Namhyung Kim)
     - Print callchains and symbols if they exist (David Ahern)

   - 'perf timechart'
     - Add backtrace support to CPU info
     - Print pid along the name
     - Add support for CPU topology
     - Add new option --highlight'ing threads, be it by name or, if a
       numeric value is provided, that run more than given duration
       (Stanislav Fomichev)

   - 'perf top':
     - Make 'perf top -g' refer to callchains, for consistency with
       other tools (David Ahern)

   - 'perf trace':
     - Handle old kernels where the "raw_syscalls" tracepoints were
       called plain "syscalls" (David Ahern)
     - Remove thread summary coloring, by Pekka Enberg.
     - Honour -m option in 'trace', the tool was offering the option to
       set the mmap size, but wasn't using it when doing the actual mmap
       on the events file descriptors (Jiri Olsa)

   - generic:
     - Backport libtraceevent plugin support (trace-cmd repository, with
       plugins for jbd2, hrtimer, kmem, kvm, mac80211, sched_switch,
       function, xen, scsi, cfg80211 (Jiri Olsa)
     - Print session information only if --stdio is given (Namhyung Kim)

  Tooling side changes, developer visible (plumbing):

   - Improve 'perf probe' exit path, release resources (Masami
     Hiramatsu)
   - Improve libtraceevent plugins exit path, allowing the registering
     of an unregister handler to be called at exit time (Namhyung Kim)
   - Add an alias to the build test makefile (make -C tools/perf
     build-test) (Namhyung Kim)
   - Get rid of die() and friends (good riddance!) in libtraceevent
     (Namhyung Kim)
   - Fix cross build problems related to pkgconfig and CROSS_COMPILE not
     being propagated to the feature tests, leading to features being
     tested in the host and then being enabled on the target (Mark
     Rutland)
   - Improve forked workload error reporting by sending the errno in the
     signal data queueing integer field, using sigqueue and by doing the
     signal setup in the evlist methods, removing open coded equivalents
     in various tools (Arnaldo Carvalho de Melo)
   - Do more auto exit cleanup chores in the 'evlist' destructor, so
     that the tools don't have to all do that sequence (Arnaldo Carvalho
     de Melo)
   - Pack 'struct perf_session_env' and 'struct trace' (Arnaldo Carvalho
     de Melo)
   - Add test for building detached source tarballs (Arnaldo Carvalho de
     Melo)
   - Move some header files (tools/perf/ to tools/include/ to make them
     available to other tools/ dwelling codebases (Namhyung Kim)
   - Move logic to warn about kptr_restrict'ed kernels to separate
     function in 'report' (Arnaldo Carvalho de Melo)
   - Move hist browser selection code to separate function (Arnaldo
     Carvalho de Melo)
   - Move histogram entries collapsing to separate function (Arnaldo
     Carvalho de Melo)
   - Introduce evlist__for_each() & friends (Arnaldo Carvalho de Melo)
   - Automate setup of FEATURE_CHECK_(C|LD)FLAGS-all variables (Jiri
     Olsa)
   - Move arch setup into seprate Makefile (Jiri Olsa)
   - Make libtraceevent install target quieter (Jiri Olsa)
   - Make tests/make output more compact (Jiri Olsa)
   - Ignore generated files in feature-checks (Chunwei Chen)
   - Introduce pevent_filter_strerror() in libtraceevent, similar in
     purpose to libc's strerror() function (Namhyung Kim)
   - Use perf_data_file methods to write output file in 'record' and
     'inject' (Jiri Olsa)
   - Use pr_*() functions where applicable in 'report' (Namhyumg Kim)
   - Add 'machine' 'addr_location' struct to have full picture (machine,
     thread, map, symbol, addr) for a (partially) resolved address,
     reducing function signatures (Arnaldo Carvalho de Melo)
   - Reduce code duplication in the histogram entry creation/insertion
     (Arnaldo Carvalho de Melo)
   - Auto allocate annotation histogram data structures (Arnaldo
     Carvalho de Melo)
   - No need to test against NULL before calling free, also set freed
     memory in struct pointers to NULL, to help fixing use after free
     bugs (Arnaldo Carvalho de Melo)
   - Rename some struct DSO binary_type related members and methods, to
     clarify its purpose and need for differentiation (symtab_type, ie
     one is about the files .text, CFI, etc, i.e.  its binary contents,
     and the other is about where the symbol table came from (Arnaldo
     Carvalho de Melo)
   - Convert to new topic libraries, starting with an API one (sysfs,
     debugfs, etc), renaming liblk in the process (Borislav Petkov)
   - Get rid of some more panic() like error handling in libtraceevent.
     (Namhyung Kim)
   - Get rid of panic() like calls in libtraceevent (Namyung Kim)
   - Start carving out symbol parsing routines (perf, just moving
     routines to topic files in tools/lib/symbol/, tools that want to
     use it need to integrate it directly, ie no
     tools/lib/symbol/Makefile is provided (Arnaldo Carvalho de Melo)
   - Assorted refactoring patches, moving code around and adding utility
     evlist methods that will be used in the IPT patchset (Adrian
     Hunter)
   - Assorted mmap_pages handling fixes (Adrian Hunter)
   - Several man pages typo fixes (Dongsheng Yang)
   - Get rid of several die() calls in libtraceevent (Namhyung Kim)
   - Use basename() in a more robust way, to avoid problems related to
     different system library implementations for that function
     (Stephane Eranian)
   - Remove open coded management of short_name_allocated member (Adrian
     Hunter)
   - Several cleanups in the "dso" methods, constifying some parameters
     and renaming some fields to clarify its purpose (Arnaldo Carvalho
     de Melo)
   - Add per-feature check flags, fixing libunwind related build
     problems on some architectures (Jean Pihet)
   - Do not disable source line lookup just because of one failure.
     (Adrian Hunter)
   - Several 'perf kvm' man page corrections (Dongsheng Yang)
   - Correct the message in feature-libnuma checking, swowing the right
     devel package names for various distros (Dongsheng Yang)
   - Polish 'readn()' function and introduce its counterpart,
     'writen()' (Jiri Olsa)
   - Start moving timechart state from global variables to a 'perf_tool'
     derived 'timechart' struct (Arnaldo Carvalho de Melo)

  ... and lots of fixes and improvements I forgot to list"

* 'perf-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (282 commits)
  perf tools: Remove unnecessary callchain cursor state restore on unmatch
  perf callchain: Spare double comparison of callchain first entry
  perf tools: Do proper comm override error handling
  perf symbols: Export elf_section_by_name and reuse
  perf probe: Release all dynamically allocated parameters
  perf probe: Release allocated probe_trace_event if failed
  perf tools: Add 'build-test' make target
  tools lib traceevent: Unregister handler when xen plugin is unloaded
  tools lib traceevent: Unregister handler when scsi plugin is unloaded
  tools lib traceevent: Unregister handler when jbd2 plugin is is unloaded
  tools lib traceevent: Unregister handler when cfg80211 plugin is unloaded
  tools lib traceevent: Unregister handler when mac80211 plugin is unloaded
  tools lib traceevent: Unregister handler when sched_switch plugin is unloaded
  tools lib traceevent: Unregister handler when kvm plugin is unloaded
  tools lib traceevent: Unregister handler when kmem plugin is unloaded
  tools lib traceevent: Unregister handler when hrtimer plugin is unloaded
  tools lib traceevent: Unregister handler when function plugin is unloaded
  tools lib traceevent: Add pevent_unregister_print_function()
  tools lib traceevent: Add pevent_unregister_event_handler()
  tools lib traceevent: fix pointer-integer size mismatch
  ...
parents 2cc3f16c 45e6af06
...@@ -99,10 +99,6 @@ int armpmu_event_set_period(struct perf_event *event) ...@@ -99,10 +99,6 @@ int armpmu_event_set_period(struct perf_event *event)
s64 period = hwc->sample_period; s64 period = hwc->sample_period;
int ret = 0; int ret = 0;
/* The period may have been changed by PERF_EVENT_IOC_PERIOD */
if (unlikely(period != hwc->last_period))
left = period - (hwc->last_period - left);
if (unlikely(left <= -period)) { if (unlikely(left <= -period)) {
left = period; left = period;
local64_set(&hwc->period_left, left); local64_set(&hwc->period_left, left);
......
...@@ -36,9 +36,8 @@ typedef ppc_opcode_t uprobe_opcode_t; ...@@ -36,9 +36,8 @@ typedef ppc_opcode_t uprobe_opcode_t;
struct arch_uprobe { struct arch_uprobe {
union { union {
u8 insn[MAX_UINSN_BYTES]; u32 insn;
u8 ixol[MAX_UINSN_BYTES]; u32 ixol;
u32 ainsn;
}; };
}; };
......
...@@ -186,7 +186,7 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) ...@@ -186,7 +186,7 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
* emulate_step() returns 1 if the insn was successfully emulated. * emulate_step() returns 1 if the insn was successfully emulated.
* For all other cases, we need to single-step in hardware. * For all other cases, we need to single-step in hardware.
*/ */
ret = emulate_step(regs, auprobe->ainsn); ret = emulate_step(regs, auprobe->insn);
if (ret > 0) if (ret > 0)
return true; return true;
......
...@@ -36,7 +36,7 @@ obj-$(CONFIG_CPU_SUP_AMD) += perf_event_amd_iommu.o ...@@ -36,7 +36,7 @@ obj-$(CONFIG_CPU_SUP_AMD) += perf_event_amd_iommu.o
endif endif
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_p6.o perf_event_knc.o perf_event_p4.o obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_p6.o perf_event_knc.o perf_event_p4.o
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_lbr.o perf_event_intel_ds.o perf_event_intel.o obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_lbr.o perf_event_intel_ds.o perf_event_intel.o
obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_uncore.o obj-$(CONFIG_CPU_SUP_INTEL) += perf_event_intel_uncore.o perf_event_intel_rapl.o
endif endif
......
This diff is collapsed.
...@@ -320,6 +320,7 @@ struct perf_event { ...@@ -320,6 +320,7 @@ struct perf_event {
struct list_head migrate_entry; struct list_head migrate_entry;
struct hlist_node hlist_entry; struct hlist_node hlist_entry;
struct list_head active_entry;
int nr_siblings; int nr_siblings;
int group_flags; int group_flags;
struct perf_event *group_leader; struct perf_event *group_leader;
......
...@@ -26,16 +26,13 @@ ...@@ -26,16 +26,13 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <linux/types.h>
struct vm_area_struct; struct vm_area_struct;
struct mm_struct; struct mm_struct;
struct inode; struct inode;
struct notifier_block; struct notifier_block;
#ifdef CONFIG_ARCH_SUPPORTS_UPROBES
# include <asm/uprobes.h>
#endif
#define UPROBE_HANDLER_REMOVE 1 #define UPROBE_HANDLER_REMOVE 1
#define UPROBE_HANDLER_MASK 1 #define UPROBE_HANDLER_MASK 1
...@@ -60,6 +57,8 @@ struct uprobe_consumer { ...@@ -60,6 +57,8 @@ struct uprobe_consumer {
}; };
#ifdef CONFIG_UPROBES #ifdef CONFIG_UPROBES
#include <asm/uprobes.h>
enum uprobe_task_state { enum uprobe_task_state {
UTASK_RUNNING, UTASK_RUNNING,
UTASK_SSTEP, UTASK_SSTEP,
...@@ -72,35 +71,28 @@ enum uprobe_task_state { ...@@ -72,35 +71,28 @@ enum uprobe_task_state {
*/ */
struct uprobe_task { struct uprobe_task {
enum uprobe_task_state state; enum uprobe_task_state state;
union {
struct {
struct arch_uprobe_task autask; struct arch_uprobe_task autask;
unsigned long vaddr;
};
struct return_instance *return_instances; struct {
unsigned int depth; struct callback_head dup_xol_work;
struct uprobe *active_uprobe; unsigned long dup_xol_addr;
};
};
struct uprobe *active_uprobe;
unsigned long xol_vaddr; unsigned long xol_vaddr;
unsigned long vaddr;
};
/* struct return_instance *return_instances;
* On a breakpoint hit, thread contests for a slot. It frees the unsigned int depth;
* slot after singlestep. Currently a fixed number of slots are
* allocated.
*/
struct xol_area {
wait_queue_head_t wq; /* if all slots are busy */
atomic_t slot_count; /* number of in-use slots */
unsigned long *bitmap; /* 0 = free slot */
struct page *page;
/*
* We keep the vma's vm_start rather than a pointer to the vma
* itself. The probed process or a naughty kernel module could make
* the vma go away, and we must handle that reasonably gracefully.
*/
unsigned long vaddr; /* Page(s) of instruction slots */
}; };
struct xol_area;
struct uprobes_state { struct uprobes_state {
struct xol_area *xol_area; struct xol_area *xol_area;
}; };
...@@ -109,6 +101,7 @@ extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsign ...@@ -109,6 +101,7 @@ extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsign
extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr);
extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); extern bool __weak is_swbp_insn(uprobe_opcode_t *insn);
extern bool __weak is_trap_insn(uprobe_opcode_t *insn); extern bool __weak is_trap_insn(uprobe_opcode_t *insn);
extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs);
extern int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t); extern int uprobe_write_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t);
extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc);
extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool); extern int uprobe_apply(struct inode *inode, loff_t offset, struct uprobe_consumer *uc, bool);
...@@ -120,7 +113,6 @@ extern void uprobe_end_dup_mmap(void); ...@@ -120,7 +113,6 @@ extern void uprobe_end_dup_mmap(void);
extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm); extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm);
extern void uprobe_free_utask(struct task_struct *t); extern void uprobe_free_utask(struct task_struct *t);
extern void uprobe_copy_process(struct task_struct *t, unsigned long flags); extern void uprobe_copy_process(struct task_struct *t, unsigned long flags);
extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs);
extern int uprobe_post_sstep_notifier(struct pt_regs *regs); extern int uprobe_post_sstep_notifier(struct pt_regs *regs);
extern int uprobe_pre_sstep_notifier(struct pt_regs *regs); extern int uprobe_pre_sstep_notifier(struct pt_regs *regs);
extern void uprobe_notify_resume(struct pt_regs *regs); extern void uprobe_notify_resume(struct pt_regs *regs);
...@@ -176,10 +168,6 @@ static inline bool uprobe_deny_signal(void) ...@@ -176,10 +168,6 @@ static inline bool uprobe_deny_signal(void)
{ {
return false; return false;
} }
static inline unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
{
return 0;
}
static inline void uprobe_free_utask(struct task_struct *t) static inline void uprobe_free_utask(struct task_struct *t)
{ {
} }
......
...@@ -725,6 +725,7 @@ enum perf_callchain_context { ...@@ -725,6 +725,7 @@ enum perf_callchain_context {
#define PERF_FLAG_FD_NO_GROUP (1U << 0) #define PERF_FLAG_FD_NO_GROUP (1U << 0)
#define PERF_FLAG_FD_OUTPUT (1U << 1) #define PERF_FLAG_FD_OUTPUT (1U << 1)
#define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */ #define PERF_FLAG_PID_CGROUP (1U << 2) /* pid=cgroup id, per-cpu mode only */
#define PERF_FLAG_FD_CLOEXEC (1U << 3) /* O_CLOEXEC */
union perf_mem_data_src { union perf_mem_data_src {
__u64 val; __u64 val;
......
...@@ -119,7 +119,8 @@ static int cpu_function_call(int cpu, int (*func) (void *info), void *info) ...@@ -119,7 +119,8 @@ static int cpu_function_call(int cpu, int (*func) (void *info), void *info)
#define PERF_FLAG_ALL (PERF_FLAG_FD_NO_GROUP |\ #define PERF_FLAG_ALL (PERF_FLAG_FD_NO_GROUP |\
PERF_FLAG_FD_OUTPUT |\ PERF_FLAG_FD_OUTPUT |\
PERF_FLAG_PID_CGROUP) PERF_FLAG_PID_CGROUP |\
PERF_FLAG_FD_CLOEXEC)
/* /*
* branch priv levels that need permission checks * branch priv levels that need permission checks
...@@ -3542,7 +3543,7 @@ static void perf_event_for_each(struct perf_event *event, ...@@ -3542,7 +3543,7 @@ static void perf_event_for_each(struct perf_event *event,
static int perf_event_period(struct perf_event *event, u64 __user *arg) static int perf_event_period(struct perf_event *event, u64 __user *arg)
{ {
struct perf_event_context *ctx = event->ctx; struct perf_event_context *ctx = event->ctx;
int ret = 0; int ret = 0, active;
u64 value; u64 value;
if (!is_sampling_event(event)) if (!is_sampling_event(event))
...@@ -3566,6 +3567,20 @@ static int perf_event_period(struct perf_event *event, u64 __user *arg) ...@@ -3566,6 +3567,20 @@ static int perf_event_period(struct perf_event *event, u64 __user *arg)
event->attr.sample_period = value; event->attr.sample_period = value;
event->hw.sample_period = value; event->hw.sample_period = value;
} }
active = (event->state == PERF_EVENT_STATE_ACTIVE);
if (active) {
perf_pmu_disable(ctx->pmu);
event->pmu->stop(event, PERF_EF_UPDATE);
}
local64_set(&event->hw.period_left, 0);
if (active) {
event->pmu->start(event, PERF_EF_RELOAD);
perf_pmu_enable(ctx->pmu);
}
unlock: unlock:
raw_spin_unlock_irq(&ctx->lock); raw_spin_unlock_irq(&ctx->lock);
...@@ -6670,6 +6685,9 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu, ...@@ -6670,6 +6685,9 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
INIT_LIST_HEAD(&event->event_entry); INIT_LIST_HEAD(&event->event_entry);
INIT_LIST_HEAD(&event->sibling_list); INIT_LIST_HEAD(&event->sibling_list);
INIT_LIST_HEAD(&event->rb_entry); INIT_LIST_HEAD(&event->rb_entry);
INIT_LIST_HEAD(&event->active_entry);
INIT_HLIST_NODE(&event->hlist_entry);
init_waitqueue_head(&event->waitq); init_waitqueue_head(&event->waitq);
init_irq_work(&event->pending, perf_pending_event); init_irq_work(&event->pending, perf_pending_event);
...@@ -6980,6 +6998,7 @@ SYSCALL_DEFINE5(perf_event_open, ...@@ -6980,6 +6998,7 @@ SYSCALL_DEFINE5(perf_event_open,
int event_fd; int event_fd;
int move_group = 0; int move_group = 0;
int err; int err;
int f_flags = O_RDWR;
/* for future expandability... */ /* for future expandability... */
if (flags & ~PERF_FLAG_ALL) if (flags & ~PERF_FLAG_ALL)
...@@ -7008,7 +7027,10 @@ SYSCALL_DEFINE5(perf_event_open, ...@@ -7008,7 +7027,10 @@ SYSCALL_DEFINE5(perf_event_open,
if ((flags & PERF_FLAG_PID_CGROUP) && (pid == -1 || cpu == -1)) if ((flags & PERF_FLAG_PID_CGROUP) && (pid == -1 || cpu == -1))
return -EINVAL; return -EINVAL;
event_fd = get_unused_fd(); if (flags & PERF_FLAG_FD_CLOEXEC)
f_flags |= O_CLOEXEC;
event_fd = get_unused_fd_flags(f_flags);
if (event_fd < 0) if (event_fd < 0)
return event_fd; return event_fd;
...@@ -7130,7 +7152,8 @@ SYSCALL_DEFINE5(perf_event_open, ...@@ -7130,7 +7152,8 @@ SYSCALL_DEFINE5(perf_event_open,
goto err_context; goto err_context;
} }
event_file = anon_inode_getfile("[perf_event]", &perf_fops, event, O_RDWR); event_file = anon_inode_getfile("[perf_event]", &perf_fops, event,
f_flags);
if (IS_ERR(event_file)) { if (IS_ERR(event_file)) {
err = PTR_ERR(event_file); err = PTR_ERR(event_file);
goto err_context; goto err_context;
......
...@@ -61,19 +61,20 @@ static void perf_output_put_handle(struct perf_output_handle *handle) ...@@ -61,19 +61,20 @@ static void perf_output_put_handle(struct perf_output_handle *handle)
* *
* kernel user * kernel user
* *
* READ ->data_tail READ ->data_head * if (LOAD ->data_tail) { LOAD ->data_head
* smp_mb() (A) smp_rmb() (C) * (A) smp_rmb() (C)
* WRITE $data READ $data * STORE $data LOAD $data
* smp_wmb() (B) smp_mb() (D) * smp_wmb() (B) smp_mb() (D)
* STORE ->data_head WRITE ->data_tail * STORE ->data_head STORE ->data_tail
* }
* *
* Where A pairs with D, and B pairs with C. * Where A pairs with D, and B pairs with C.
* *
* I don't think A needs to be a full barrier because we won't in fact * In our case (A) is a control dependency that separates the load of
* write data until we see the store from userspace. So we simply don't * the ->data_tail and the stores of $data. In case ->data_tail
* issue the data WRITE until we observe it. Be conservative for now. * indicates there is no room in the buffer to store $data we do not.
* *
* OTOH, D needs to be a full barrier since it separates the data READ * D needs to be a full barrier since it separates the data READ
* from the tail WRITE. * from the tail WRITE.
* *
* For B a WMB is sufficient since it separates two WRITEs, and for C * For B a WMB is sufficient since it separates two WRITEs, and for C
...@@ -81,7 +82,7 @@ static void perf_output_put_handle(struct perf_output_handle *handle) ...@@ -81,7 +82,7 @@ static void perf_output_put_handle(struct perf_output_handle *handle)
* *
* See perf_output_begin(). * See perf_output_begin().
*/ */
smp_wmb(); smp_wmb(); /* B, matches C */
rb->user_page->data_head = head; rb->user_page->data_head = head;
/* /*
...@@ -144,17 +145,26 @@ int perf_output_begin(struct perf_output_handle *handle, ...@@ -144,17 +145,26 @@ int perf_output_begin(struct perf_output_handle *handle,
if (!rb->overwrite && if (!rb->overwrite &&
unlikely(CIRC_SPACE(head, tail, perf_data_size(rb)) < size)) unlikely(CIRC_SPACE(head, tail, perf_data_size(rb)) < size))
goto fail; goto fail;
head += size;
} while (local_cmpxchg(&rb->head, offset, head) != offset);
/* /*
* Separate the userpage->tail read from the data stores below. * The above forms a control dependency barrier separating the
* Matches the MB userspace SHOULD issue after reading the data * @tail load above from the data stores below. Since the @tail
* and before storing the new tail position. * load is required to compute the branch to fail below.
*
* A, matches D; the full memory barrier userspace SHOULD issue
* after reading the data and before storing the new tail
* position.
* *
* See perf_output_put_handle(). * See perf_output_put_handle().
*/ */
smp_mb();
head += size;
} while (local_cmpxchg(&rb->head, offset, head) != offset);
/*
* We rely on the implied barrier() by local_cmpxchg() to ensure
* none of the data stores below can be lifted up by the compiler.
*/
if (unlikely(head - local_read(&rb->wakeup) > rb->watermark)) if (unlikely(head - local_read(&rb->wakeup) > rb->watermark))
local_add(rb->watermark, &rb->wakeup); local_add(rb->watermark, &rb->wakeup);
......
...@@ -73,6 +73,17 @@ struct uprobe { ...@@ -73,6 +73,17 @@ struct uprobe {
struct inode *inode; /* Also hold a ref to inode */ struct inode *inode; /* Also hold a ref to inode */
loff_t offset; loff_t offset;
unsigned long flags; unsigned long flags;
/*
* The generic code assumes that it has two members of unknown type
* owned by the arch-specific code:
*
* insn - copy_insn() saves the original instruction here for
* arch_uprobe_analyze_insn().
*
* ixol - potentially modified instruction to execute out of
* line, copied to xol_area by xol_get_insn_slot().
*/
struct arch_uprobe arch; struct arch_uprobe arch;
}; };
...@@ -85,6 +96,29 @@ struct return_instance { ...@@ -85,6 +96,29 @@ struct return_instance {
struct return_instance *next; /* keep as stack */ struct return_instance *next; /* keep as stack */
}; };
/*
* Execute out of line area: anonymous executable mapping installed
* by the probed task to execute the copy of the original instruction
* mangled by set_swbp().
*
* On a breakpoint hit, thread contests for a slot. It frees the
* slot after singlestep. Currently a fixed number of slots are
* allocated.
*/
struct xol_area {
wait_queue_head_t wq; /* if all slots are busy */
atomic_t slot_count; /* number of in-use slots */
unsigned long *bitmap; /* 0 = free slot */
struct page *page;
/*
* We keep the vma's vm_start rather than a pointer to the vma
* itself. The probed process or a naughty kernel module could make
* the vma go away, and we must handle that reasonably gracefully.
*/
unsigned long vaddr; /* Page(s) of instruction slots */
};
/* /*
* valid_vma: Verify if the specified vma is an executable vma * valid_vma: Verify if the specified vma is an executable vma
* Relax restrictions while unregistering: vm_flags might have * Relax restrictions while unregistering: vm_flags might have
...@@ -330,7 +364,7 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned ...@@ -330,7 +364,7 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned
int __weak int __weak
set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr)
{ {
return uprobe_write_opcode(mm, vaddr, *(uprobe_opcode_t *)auprobe->insn); return uprobe_write_opcode(mm, vaddr, *(uprobe_opcode_t *)&auprobe->insn);
} }
static int match_uprobe(struct uprobe *l, struct uprobe *r) static int match_uprobe(struct uprobe *l, struct uprobe *r)
...@@ -529,8 +563,8 @@ static int copy_insn(struct uprobe *uprobe, struct file *filp) ...@@ -529,8 +563,8 @@ static int copy_insn(struct uprobe *uprobe, struct file *filp)
{ {
struct address_space *mapping = uprobe->inode->i_mapping; struct address_space *mapping = uprobe->inode->i_mapping;
loff_t offs = uprobe->offset; loff_t offs = uprobe->offset;
void *insn = uprobe->arch.insn; void *insn = &uprobe->arch.insn;
int size = MAX_UINSN_BYTES; int size = sizeof(uprobe->arch.insn);
int len, err = -EIO; int len, err = -EIO;
/* Copy only available bytes, -EIO if nothing was read */ /* Copy only available bytes, -EIO if nothing was read */
...@@ -569,7 +603,7 @@ static int prepare_uprobe(struct uprobe *uprobe, struct file *file, ...@@ -569,7 +603,7 @@ static int prepare_uprobe(struct uprobe *uprobe, struct file *file,
goto out; goto out;
ret = -ENOTSUPP; ret = -ENOTSUPP;
if (is_trap_insn((uprobe_opcode_t *)uprobe->arch.insn)) if (is_trap_insn((uprobe_opcode_t *)&uprobe->arch.insn))
goto out; goto out;
ret = arch_uprobe_analyze_insn(&uprobe->arch, mm, vaddr); ret = arch_uprobe_analyze_insn(&uprobe->arch, mm, vaddr);
...@@ -1264,7 +1298,7 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe) ...@@ -1264,7 +1298,7 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)
/* Initialize the slot */ /* Initialize the slot */
copy_to_page(area->page, xol_vaddr, copy_to_page(area->page, xol_vaddr,
uprobe->arch.ixol, sizeof(uprobe->arch.ixol)); &uprobe->arch.ixol, sizeof(uprobe->arch.ixol));
/* /*
* We probably need flush_icache_user_range() but it needs vma. * We probably need flush_icache_user_range() but it needs vma.
* This should work on supported architectures too. * This should work on supported architectures too.
...@@ -1403,12 +1437,10 @@ static void uprobe_warn(struct task_struct *t, const char *msg) ...@@ -1403,12 +1437,10 @@ static void uprobe_warn(struct task_struct *t, const char *msg)
static void dup_xol_work(struct callback_head *work) static void dup_xol_work(struct callback_head *work)
{ {
kfree(work);
if (current->flags & PF_EXITING) if (current->flags & PF_EXITING)
return; return;
if (!__create_xol_area(current->utask->vaddr)) if (!__create_xol_area(current->utask->dup_xol_addr))
uprobe_warn(current, "dup xol area"); uprobe_warn(current, "dup xol area");
} }
...@@ -1419,7 +1451,6 @@ void uprobe_copy_process(struct task_struct *t, unsigned long flags) ...@@ -1419,7 +1451,6 @@ void uprobe_copy_process(struct task_struct *t, unsigned long flags)
{ {
struct uprobe_task *utask = current->utask; struct uprobe_task *utask = current->utask;
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
struct callback_head *work;
struct xol_area *area; struct xol_area *area;
t->utask = NULL; t->utask = NULL;
...@@ -1441,14 +1472,9 @@ void uprobe_copy_process(struct task_struct *t, unsigned long flags) ...@@ -1441,14 +1472,9 @@ void uprobe_copy_process(struct task_struct *t, unsigned long flags)
if (mm == t->mm) if (mm == t->mm)
return; return;
/* TODO: move it into the union in uprobe_task */ t->utask->dup_xol_addr = area->vaddr;
work = kmalloc(sizeof(*work), GFP_KERNEL); init_task_work(&t->utask->dup_xol_work, dup_xol_work);
if (!work) task_work_add(t, &t->utask->dup_xol_work, true);
return uprobe_warn(t, "dup xol area");
t->utask->vaddr = area->vaddr;
init_task_work(work, dup_xol_work);
task_work_add(t, work, true);
} }
/* /*
......
...@@ -39,10 +39,10 @@ cpupower: FORCE ...@@ -39,10 +39,10 @@ cpupower: FORCE
cgroup firewire guest usb virtio vm net: FORCE cgroup firewire guest usb virtio vm net: FORCE
$(call descend,$@) $(call descend,$@)
liblk: FORCE libapikfs: FORCE
$(call descend,lib/lk) $(call descend,lib/api)
perf: liblk FORCE perf: libapikfs FORCE
$(call descend,$@) $(call descend,$@)
selftests: FORCE selftests: FORCE
...@@ -80,10 +80,10 @@ cpupower_clean: ...@@ -80,10 +80,10 @@ cpupower_clean:
cgroup_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean: cgroup_clean firewire_clean lguest_clean usb_clean virtio_clean vm_clean net_clean:
$(call descend,$(@:_clean=),clean) $(call descend,$(@:_clean=),clean)
liblk_clean: libapikfs_clean:
$(call descend,lib/lk,clean) $(call descend,lib/api,clean)
perf_clean: liblk_clean perf_clean: libapikfs_clean
$(call descend,$(@:_clean=),clean) $(call descend,$(@:_clean=),clean)
selftests_clean: selftests_clean:
......
#ifndef _PERF_ASM_GENERIC_BUG_H #ifndef _TOOLS_ASM_BUG_H
#define _PERF_ASM_GENERIC_BUG_H #define _TOOLS_ASM_BUG_H
#include <linux/compiler.h>
#define __WARN_printf(arg...) do { fprintf(stderr, arg); } while (0) #define __WARN_printf(arg...) do { fprintf(stderr, arg); } while (0)
...@@ -19,4 +21,5 @@ ...@@ -19,4 +21,5 @@
__warned = 1; \ __warned = 1; \
unlikely(__ret_warn_once); \ unlikely(__ret_warn_once); \
}) })
#endif
#endif /* _TOOLS_ASM_BUG_H */
#ifndef _PERF_LINUX_COMPILER_H_ #ifndef _TOOLS_LINUX_COMPILER_H_
#define _PERF_LINUX_COMPILER_H_ #define _TOOLS_LINUX_COMPILER_H_
#ifndef __always_inline #ifndef __always_inline
# define __always_inline inline __attribute__((always_inline)) # define __always_inline inline __attribute__((always_inline))
...@@ -27,4 +27,12 @@ ...@@ -27,4 +27,12 @@
# define __weak __attribute__((weak)) # define __weak __attribute__((weak))
#endif #endif
#ifndef likely
# define likely(x) __builtin_expect(!!(x), 1)
#endif #endif
#ifndef unlikely
# define unlikely(x) __builtin_expect(!!(x), 0)
#endif
#endif /* _TOOLS_LINUX_COMPILER_H */
include ../../scripts/Makefile.include include ../../scripts/Makefile.include
include ../../perf/config/utilities.mak # QUIET_CLEAN
CC = $(CROSS_COMPILE)gcc CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar AR = $(CROSS_COMPILE)ar
...@@ -7,11 +8,11 @@ AR = $(CROSS_COMPILE)ar ...@@ -7,11 +8,11 @@ AR = $(CROSS_COMPILE)ar
LIB_H= LIB_H=
LIB_OBJS= LIB_OBJS=
LIB_H += debugfs.h LIB_H += fs/debugfs.h
LIB_OBJS += $(OUTPUT)debugfs.o LIB_OBJS += $(OUTPUT)fs/debugfs.o
LIBFILE = liblk.a LIBFILE = libapikfs.a
CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) -fPIC CFLAGS = -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) -fPIC
EXTLIBS = -lelf -lpthread -lrt -lm EXTLIBS = -lelf -lpthread -lrt -lm
...@@ -25,14 +26,17 @@ $(LIBFILE): $(LIB_OBJS) ...@@ -25,14 +26,17 @@ $(LIBFILE): $(LIB_OBJS)
$(LIB_OBJS): $(LIB_H) $(LIB_OBJS): $(LIB_H)
$(OUTPUT)%.o: %.c libapi_dirs:
$(QUIET_MKDIR)mkdir -p $(OUTPUT)fs/
$(OUTPUT)%.o: %.c libapi_dirs
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
$(OUTPUT)%.s: %.c $(OUTPUT)%.s: %.c libapi_dirs
$(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $< $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
$(OUTPUT)%.o: %.S $(OUTPUT)%.o: %.S libapi_dirs
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $< $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
clean: clean:
$(RM) $(LIB_OBJS) $(LIBFILE) $(call QUIET_CLEAN, libapi) $(RM) $(LIB_OBJS) $(LIBFILE)
.PHONY: clean .PHONY: clean
#ifndef __LK_DEBUGFS_H__ #ifndef __API_DEBUGFS_H__
#define __LK_DEBUGFS_H__ #define __API_DEBUGFS_H__
#define _STR(x) #x #define _STR(x) #x
#define STR(x) _STR(x) #define STR(x) _STR(x)
...@@ -26,4 +26,4 @@ char *debugfs_mount(const char *mountpoint); ...@@ -26,4 +26,4 @@ char *debugfs_mount(const char *mountpoint);
extern char debugfs_mountpoint[]; extern char debugfs_mountpoint[];
#endif /* __LK_DEBUGFS_H__ */ #endif /* __API_DEBUGFS_H__ */
#include "symbol/kallsyms.h"
#include <stdio.h>
#include <stdlib.h>
int kallsyms__parse(const char *filename, void *arg,
int (*process_symbol)(void *arg, const char *name,
char type, u64 start))
{
char *line = NULL;
size_t n;
int err = -1;
FILE *file = fopen(filename, "r");
if (file == NULL)
goto out_failure;
err = 0;
while (!feof(file)) {
u64 start;
int line_len, len;
char symbol_type;
char *symbol_name;
line_len = getline(&line, &n, file);
if (line_len < 0 || !line)
break;
line[--line_len] = '\0'; /* \n */
len = hex2u64(line, &start);
len++;
if (len + 2 >= line_len)
continue;
symbol_type = line[len];
len += 2;
symbol_name = line + len;
len = line_len - len;
if (len >= KSYM_NAME_LEN) {
err = -1;
break;
}
err = process_symbol(arg, symbol_name, symbol_type, start);
if (err)
break;
}
free(line);
fclose(file);
return err;
out_failure:
return -1;
}
#ifndef __TOOLS_KALLSYMS_H_
#define __TOOLS_KALLSYMS_H_ 1
#include <elf.h>
#include <linux/ctype.h>
#include <linux/types.h>
#ifndef KSYM_NAME_LEN
#define KSYM_NAME_LEN 256
#endif
static inline u8 kallsyms2elf_type(char type)
{
if (type == 'W')
return STB_WEAK;
return isupper(type) ? STB_GLOBAL : STB_LOCAL;
}
int kallsyms__parse(const char *filename, void *arg,
int (*process_symbol)(void *arg, const char *name,
char type, u64 start));
#endif /* __TOOLS_KALLSYMS_H_ */
...@@ -43,6 +43,32 @@ man_dir_SQ = '$(subst ','\'',$(man_dir))' ...@@ -43,6 +43,32 @@ man_dir_SQ = '$(subst ','\'',$(man_dir))'
export man_dir man_dir_SQ INSTALL export man_dir man_dir_SQ INSTALL
export DESTDIR DESTDIR_SQ export DESTDIR DESTDIR_SQ
set_plugin_dir := 1
# Set plugin_dir to preffered global plugin location
# If we install under $HOME directory we go under
# $(HOME)/.traceevent/plugins
#
# We dont set PLUGIN_DIR in case we install under $HOME
# directory, because by default the code looks under:
# $(HOME)/.traceevent/plugins by default.
#
ifeq ($(plugin_dir),)
ifeq ($(prefix),$(HOME))
override plugin_dir = $(HOME)/.traceevent/plugins
set_plugin_dir := 0
else
override plugin_dir = $(prefix)/lib/traceevent/plugins
endif
endif
ifeq ($(set_plugin_dir),1)
PLUGIN_DIR = -DPLUGIN_DIR="$(DESTDIR)/$(plugin_dir)"
PLUGIN_DIR_SQ = '$(subst ','\'',$(PLUGIN_DIR))'
endif
include $(if $(BUILD_SRC),$(BUILD_SRC)/)../../scripts/Makefile.include
# copy a bit from Linux kbuild # copy a bit from Linux kbuild
ifeq ("$(origin V)", "command line") ifeq ("$(origin V)", "command line")
...@@ -57,18 +83,13 @@ ifeq ("$(origin O)", "command line") ...@@ -57,18 +83,13 @@ ifeq ("$(origin O)", "command line")
endif endif
ifeq ($(BUILD_SRC),) ifeq ($(BUILD_SRC),)
ifneq ($(BUILD_OUTPUT),) ifneq ($(OUTPUT),)
define build_output define build_output
$(if $(VERBOSE:1=),@)+$(MAKE) -C $(BUILD_OUTPUT) \ $(if $(VERBOSE:1=),@)+$(MAKE) -C $(OUTPUT) \
BUILD_SRC=$(CURDIR) -f $(CURDIR)/Makefile $1 BUILD_SRC=$(CURDIR)/ -f $(CURDIR)/Makefile $1
endef endef
saved-output := $(BUILD_OUTPUT)
BUILD_OUTPUT := $(shell cd $(BUILD_OUTPUT) && /bin/pwd)
$(if $(BUILD_OUTPUT),, \
$(error output directory "$(saved-output)" does not exist))
all: sub-make all: sub-make
$(MAKECMDGOALS): sub-make $(MAKECMDGOALS): sub-make
...@@ -80,7 +101,7 @@ sub-make: force ...@@ -80,7 +101,7 @@ sub-make: force
# Leave processing to above invocation of make # Leave processing to above invocation of make
skip-makefile := 1 skip-makefile := 1
endif # BUILD_OUTPUT endif # OUTPUT
endif # BUILD_SRC endif # BUILD_SRC
# We process the rest of the Makefile if this is the final invocation of make # We process the rest of the Makefile if this is the final invocation of make
...@@ -96,6 +117,7 @@ export prefix bindir src obj ...@@ -96,6 +117,7 @@ export prefix bindir src obj
# Shell quotes # Shell quotes
bindir_SQ = $(subst ','\'',$(bindir)) bindir_SQ = $(subst ','\'',$(bindir))
bindir_relative_SQ = $(subst ','\'',$(bindir_relative)) bindir_relative_SQ = $(subst ','\'',$(bindir_relative))
plugin_dir_SQ = $(subst ','\'',$(plugin_dir))
LIB_FILE = libtraceevent.a libtraceevent.so LIB_FILE = libtraceevent.a libtraceevent.so
...@@ -114,7 +136,7 @@ export Q VERBOSE ...@@ -114,7 +136,7 @@ export Q VERBOSE
EVENT_PARSE_VERSION = $(EP_VERSION).$(EP_PATCHLEVEL).$(EP_EXTRAVERSION) EVENT_PARSE_VERSION = $(EP_VERSION).$(EP_PATCHLEVEL).$(EP_EXTRAVERSION)
INCLUDES = -I. $(CONFIG_INCLUDES) INCLUDES = -I. -I $(srctree)/../../include $(CONFIG_INCLUDES)
# Set compile option CFLAGS if not set elsewhere # Set compile option CFLAGS if not set elsewhere
CFLAGS ?= -g -Wall CFLAGS ?= -g -Wall
...@@ -125,41 +147,14 @@ override CFLAGS += $(udis86-flags) -D_GNU_SOURCE ...@@ -125,41 +147,14 @@ override CFLAGS += $(udis86-flags) -D_GNU_SOURCE
ifeq ($(VERBOSE),1) ifeq ($(VERBOSE),1)
Q = Q =
print_compile =
print_app_build =
print_fpic_compile =
print_shared_lib_compile =
print_plugin_obj_compile =
print_plugin_build =
print_install =
else else
Q = @ Q = @
print_compile = echo ' CC '$(OBJ);
print_app_build = echo ' BUILD '$(OBJ);
print_fpic_compile = echo ' CC FPIC '$(OBJ);
print_shared_lib_compile = echo ' BUILD SHARED LIB '$(OBJ);
print_plugin_obj_compile = echo ' BUILD PLUGIN OBJ '$(OBJ);
print_plugin_build = echo ' BUILD PLUGIN '$(OBJ);
print_static_lib_build = echo ' BUILD STATIC LIB '$(OBJ);
print_install = echo ' INSTALL '$1' to $(DESTDIR_SQ)$2';
endif endif
do_fpic_compile = \
($(print_fpic_compile) \
$(CC) -c $(CFLAGS) $(EXT) -fPIC $< -o $@)
do_app_build = \
($(print_app_build) \
$(CC) $^ -rdynamic -o $@ $(CONFIG_LIBS) $(LIBS))
do_compile_shared_library = \ do_compile_shared_library = \
($(print_shared_lib_compile) \ ($(print_shared_lib_compile) \
$(CC) --shared $^ -o $@) $(CC) --shared $^ -o $@)
do_compile_plugin_obj = \
($(print_plugin_obj_compile) \
$(CC) -c $(CFLAGS) -fPIC -o $@ $<)
do_plugin_build = \ do_plugin_build = \
($(print_plugin_build) \ ($(print_plugin_build) \
$(CC) $(CFLAGS) -shared -nostartfiles -o $@ $<) $(CC) $(CFLAGS) -shared -nostartfiles -o $@ $<)
...@@ -169,23 +164,37 @@ do_build_static_lib = \ ...@@ -169,23 +164,37 @@ do_build_static_lib = \
$(RM) $@; $(AR) rcs $@ $^) $(RM) $@; $(AR) rcs $@ $^)
define do_compile do_compile = $(QUIET_CC)$(CC) -c $(CFLAGS) $(EXT) $< -o $(obj)/$@;
$(print_compile) \
$(CC) -c $(CFLAGS) $(EXT) $< -o $(obj)/$@;
endef
$(obj)/%.o: $(src)/%.c $(obj)/%.o: $(src)/%.c
$(Q)$(call do_compile) $(call do_compile)
%.o: $(src)/%.c %.o: $(src)/%.c
$(Q)$(call do_compile) $(call do_compile)
PEVENT_LIB_OBJS = event-parse.o trace-seq.o parse-filter.o parse-utils.o PEVENT_LIB_OBJS = event-parse.o
PEVENT_LIB_OBJS += event-plugin.o
PEVENT_LIB_OBJS += trace-seq.o
PEVENT_LIB_OBJS += parse-filter.o
PEVENT_LIB_OBJS += parse-utils.o
PEVENT_LIB_OBJS += kbuffer-parse.o PEVENT_LIB_OBJS += kbuffer-parse.o
ALL_OBJS = $(PEVENT_LIB_OBJS) PLUGIN_OBJS = plugin_jbd2.o
PLUGIN_OBJS += plugin_hrtimer.o
PLUGIN_OBJS += plugin_kmem.o
PLUGIN_OBJS += plugin_kvm.o
PLUGIN_OBJS += plugin_mac80211.o
PLUGIN_OBJS += plugin_sched_switch.o
PLUGIN_OBJS += plugin_function.o
PLUGIN_OBJS += plugin_xen.o
PLUGIN_OBJS += plugin_scsi.o
PLUGIN_OBJS += plugin_cfg80211.o
PLUGINS := $(PLUGIN_OBJS:.o=.so)
ALL_OBJS = $(PEVENT_LIB_OBJS) $(PLUGIN_OBJS)
CMD_TARGETS = $(LIB_FILE) CMD_TARGETS = $(LIB_FILE) $(PLUGINS)
TARGETS = $(CMD_TARGETS) TARGETS = $(CMD_TARGETS)
...@@ -195,13 +204,21 @@ all: all_cmd ...@@ -195,13 +204,21 @@ all: all_cmd
all_cmd: $(CMD_TARGETS) all_cmd: $(CMD_TARGETS)
libtraceevent.so: $(PEVENT_LIB_OBJS) libtraceevent.so: $(PEVENT_LIB_OBJS)
$(Q)$(do_compile_shared_library) $(QUIET_LINK)$(CC) --shared $^ -o $@
libtraceevent.a: $(PEVENT_LIB_OBJS) libtraceevent.a: $(PEVENT_LIB_OBJS)
$(Q)$(do_build_static_lib) $(QUIET_LINK)$(RM) $@; $(AR) rcs $@ $^
plugins: $(PLUGINS)
$(PEVENT_LIB_OBJS): %.o: $(src)/%.c TRACEEVENT-CFLAGS $(PEVENT_LIB_OBJS): %.o: $(src)/%.c TRACEEVENT-CFLAGS
$(Q)$(do_fpic_compile) $(QUIET_CC_FPIC)$(CC) -c $(CFLAGS) $(EXT) -fPIC $< -o $@
$(PLUGIN_OBJS): %.o : $(src)/%.c
$(QUIET_CC_FPIC)$(CC) -c $(CFLAGS) -fPIC -o $@ $<
$(PLUGINS): %.so: %.o
$(QUIET_LINK)$(CC) $(CFLAGS) -shared -nostartfiles -o $@ $<
define make_version.h define make_version.h
(echo '/* This file is automatically generated. Do not modify. */'; \ (echo '/* This file is automatically generated. Do not modify. */'; \
...@@ -283,27 +300,41 @@ TAGS: force ...@@ -283,27 +300,41 @@ TAGS: force
--regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/' --regex='/_PE(\([^,)]*\).*/PEVENT_ERRNO__\1/'
define do_install define do_install
$(print_install) \
if [ ! -d '$(DESTDIR_SQ)$2' ]; then \ if [ ! -d '$(DESTDIR_SQ)$2' ]; then \
$(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$2'; \
fi; \ fi; \
$(INSTALL) $1 '$(DESTDIR_SQ)$2' $(INSTALL) $1 '$(DESTDIR_SQ)$2'
endef endef
install_lib: all_cmd define do_install_plugins
$(Q)$(call do_install,$(LIB_FILE),$(bindir_SQ)) for plugin in $1; do \
$(call do_install,$$plugin,$(plugin_dir_SQ)); \
done
endef
install_lib: all_cmd install_plugins
$(call QUIET_INSTALL, $(LIB_FILE)) \
$(call do_install,$(LIB_FILE),$(bindir_SQ))
install_plugins: $(PLUGINS)
$(call QUIET_INSTALL, trace_plugins) \
$(call do_install_plugins, $(PLUGINS))
install: install_lib install: install_lib
clean: clean:
$(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d $(call QUIET_CLEAN, libtraceevent) \
$(RM) *.o *~ $(TARGETS) *.a *.so $(VERSION_FILES) .*.d \
$(RM) TRACEEVENT-CFLAGS tags TAGS $(RM) TRACEEVENT-CFLAGS tags TAGS
endif # skip-makefile endif # skip-makefile
PHONY += force PHONY += force plugins
force: force:
plugins:
@echo > /dev/null
# Declare the contents of the .PHONY variable as phony. We keep that # Declare the contents of the .PHONY variable as phony. We keep that
# information in a variable so we can use it in if_changed and friends. # information in a variable so we can use it in if_changed and friends.
.PHONY: $(PHONY) .PHONY: $(PHONY)
...@@ -2710,7 +2710,6 @@ process_func_handler(struct event_format *event, struct pevent_function_handler ...@@ -2710,7 +2710,6 @@ process_func_handler(struct event_format *event, struct pevent_function_handler
struct print_arg *farg; struct print_arg *farg;
enum event_type type; enum event_type type;
char *token; char *token;
const char *test;
int i; int i;
arg->type = PRINT_FUNC; arg->type = PRINT_FUNC;
...@@ -2727,15 +2726,19 @@ process_func_handler(struct event_format *event, struct pevent_function_handler ...@@ -2727,15 +2726,19 @@ process_func_handler(struct event_format *event, struct pevent_function_handler
} }
type = process_arg(event, farg, &token); type = process_arg(event, farg, &token);
if (i < (func->nr_args - 1)) if (i < (func->nr_args - 1)) {
test = ","; if (type != EVENT_DELIM || strcmp(token, ",") != 0) {
else warning("Error: function '%s()' expects %d arguments but event %s only uses %d",
test = ")"; func->name, func->nr_args,
event->name, i + 1);
if (test_type_token(type, token, EVENT_DELIM, test)) { goto err;
free_arg(farg); }
free_token(token); } else {
return EVENT_ERROR; if (type != EVENT_DELIM || strcmp(token, ")") != 0) {
warning("Error: function '%s()' only expects %d arguments but event %s has more",
func->name, func->nr_args, event->name);
goto err;
}
} }
*next_arg = farg; *next_arg = farg;
...@@ -2747,6 +2750,11 @@ process_func_handler(struct event_format *event, struct pevent_function_handler ...@@ -2747,6 +2750,11 @@ process_func_handler(struct event_format *event, struct pevent_function_handler
*tok = token; *tok = token;
return type; return type;
err:
free_arg(farg);
free_token(token);
return EVENT_ERROR;
} }
static enum event_type static enum event_type
...@@ -4099,6 +4107,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event ...@@ -4099,6 +4107,7 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
unsigned long long val; unsigned long long val;
struct func_map *func; struct func_map *func;
const char *saveptr; const char *saveptr;
struct trace_seq p;
char *bprint_fmt = NULL; char *bprint_fmt = NULL;
char format[32]; char format[32];
int show_func; int show_func;
...@@ -4306,8 +4315,12 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event ...@@ -4306,8 +4315,12 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
format[len] = 0; format[len] = 0;
if (!len_as_arg) if (!len_as_arg)
len_arg = -1; len_arg = -1;
print_str_arg(s, data, size, event, /* Use helper trace_seq */
trace_seq_init(&p);
print_str_arg(&p, data, size, event,
format, len_arg, arg); format, len_arg, arg);
trace_seq_terminate(&p);
trace_seq_puts(s, p.buffer);
arg = arg->next; arg = arg->next;
break; break;
default: default:
...@@ -5116,8 +5129,38 @@ enum pevent_errno __pevent_parse_format(struct event_format **eventp, ...@@ -5116,8 +5129,38 @@ enum pevent_errno __pevent_parse_format(struct event_format **eventp,
return ret; return ret;
} }
static enum pevent_errno
__pevent_parse_event(struct pevent *pevent,
struct event_format **eventp,
const char *buf, unsigned long size,
const char *sys)
{
int ret = __pevent_parse_format(eventp, pevent, buf, size, sys);
struct event_format *event = *eventp;
if (event == NULL)
return ret;
if (pevent && add_event(pevent, event)) {
ret = PEVENT_ERRNO__MEM_ALLOC_FAILED;
goto event_add_failed;
}
#define PRINT_ARGS 0
if (PRINT_ARGS && event->print_fmt.args)
print_args(event->print_fmt.args);
return 0;
event_add_failed:
pevent_free_format(event);
return ret;
}
/** /**
* pevent_parse_format - parse the event format * pevent_parse_format - parse the event format
* @pevent: the handle to the pevent
* @eventp: returned format
* @buf: the buffer storing the event format string * @buf: the buffer storing the event format string
* @size: the size of @buf * @size: the size of @buf
* @sys: the system the event belongs to * @sys: the system the event belongs to
...@@ -5129,10 +5172,12 @@ enum pevent_errno __pevent_parse_format(struct event_format **eventp, ...@@ -5129,10 +5172,12 @@ enum pevent_errno __pevent_parse_format(struct event_format **eventp,
* *
* /sys/kernel/debug/tracing/events/.../.../format * /sys/kernel/debug/tracing/events/.../.../format
*/ */
enum pevent_errno pevent_parse_format(struct event_format **eventp, const char *buf, enum pevent_errno pevent_parse_format(struct pevent *pevent,
struct event_format **eventp,
const char *buf,
unsigned long size, const char *sys) unsigned long size, const char *sys)
{ {
return __pevent_parse_format(eventp, NULL, buf, size, sys); return __pevent_parse_event(pevent, eventp, buf, size, sys);
} }
/** /**
...@@ -5153,25 +5198,7 @@ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, ...@@ -5153,25 +5198,7 @@ enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf,
unsigned long size, const char *sys) unsigned long size, const char *sys)
{ {
struct event_format *event = NULL; struct event_format *event = NULL;
int ret = __pevent_parse_format(&event, pevent, buf, size, sys); return __pevent_parse_event(pevent, &event, buf, size, sys);
if (event == NULL)
return ret;
if (add_event(pevent, event)) {
ret = PEVENT_ERRNO__MEM_ALLOC_FAILED;
goto event_add_failed;
}
#define PRINT_ARGS 0
if (PRINT_ARGS && event->print_fmt.args)
print_args(event->print_fmt.args);
return 0;
event_add_failed:
pevent_free_format(event);
return ret;
} }
#undef _PE #undef _PE
...@@ -5203,22 +5230,7 @@ int pevent_strerror(struct pevent *pevent __maybe_unused, ...@@ -5203,22 +5230,7 @@ int pevent_strerror(struct pevent *pevent __maybe_unused,
idx = errnum - __PEVENT_ERRNO__START - 1; idx = errnum - __PEVENT_ERRNO__START - 1;
msg = pevent_error_str[idx]; msg = pevent_error_str[idx];
switch (errnum) {
case PEVENT_ERRNO__MEM_ALLOC_FAILED:
case PEVENT_ERRNO__PARSE_EVENT_FAILED:
case PEVENT_ERRNO__READ_ID_FAILED:
case PEVENT_ERRNO__READ_FORMAT_FAILED:
case PEVENT_ERRNO__READ_PRINT_FAILED:
case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED:
case PEVENT_ERRNO__INVALID_ARG_TYPE:
snprintf(buf, buflen, "%s", msg); snprintf(buf, buflen, "%s", msg);
break;
default:
/* cannot reach here */
break;
}
return 0; return 0;
} }
...@@ -5548,6 +5560,52 @@ int pevent_register_print_function(struct pevent *pevent, ...@@ -5548,6 +5560,52 @@ int pevent_register_print_function(struct pevent *pevent,
return ret; return ret;
} }
/**
* pevent_unregister_print_function - unregister a helper function
* @pevent: the handle to the pevent
* @func: the function to process the helper function
* @name: the name of the helper function
*
* This function removes existing print handler for function @name.
*
* Returns 0 if the handler was removed successully, -1 otherwise.
*/
int pevent_unregister_print_function(struct pevent *pevent,
pevent_func_handler func, char *name)
{
struct pevent_function_handler *func_handle;
func_handle = find_func_handler(pevent, name);
if (func_handle && func_handle->func == func) {
remove_func_handler(pevent, name);
return 0;
}
return -1;
}
static struct event_format *pevent_search_event(struct pevent *pevent, int id,
const char *sys_name,
const char *event_name)
{
struct event_format *event;
if (id >= 0) {
/* search by id */
event = pevent_find_event(pevent, id);
if (!event)
return NULL;
if (event_name && (strcmp(event_name, event->name) != 0))
return NULL;
if (sys_name && (strcmp(sys_name, event->system) != 0))
return NULL;
} else {
event = pevent_find_event_by_name(pevent, sys_name, event_name);
if (!event)
return NULL;
}
return event;
}
/** /**
* pevent_register_event_handler - register a way to parse an event * pevent_register_event_handler - register a way to parse an event
* @pevent: the handle to the pevent * @pevent: the handle to the pevent
...@@ -5572,20 +5630,9 @@ int pevent_register_event_handler(struct pevent *pevent, int id, ...@@ -5572,20 +5630,9 @@ int pevent_register_event_handler(struct pevent *pevent, int id,
struct event_format *event; struct event_format *event;
struct event_handler *handle; struct event_handler *handle;
if (id >= 0) { event = pevent_search_event(pevent, id, sys_name, event_name);
/* search by id */ if (event == NULL)
event = pevent_find_event(pevent, id);
if (!event)
goto not_found;
if (event_name && (strcmp(event_name, event->name) != 0))
goto not_found;
if (sys_name && (strcmp(sys_name, event->system) != 0))
goto not_found;
} else {
event = pevent_find_event_by_name(pevent, sys_name, event_name);
if (!event)
goto not_found; goto not_found;
}
pr_stat("overriding event (%d) %s:%s with new print handler", pr_stat("overriding event (%d) %s:%s with new print handler",
event->id, event->system, event->name); event->id, event->system, event->name);
...@@ -5625,6 +5672,79 @@ int pevent_register_event_handler(struct pevent *pevent, int id, ...@@ -5625,6 +5672,79 @@ int pevent_register_event_handler(struct pevent *pevent, int id,
return -1; return -1;
} }
static int handle_matches(struct event_handler *handler, int id,
const char *sys_name, const char *event_name,
pevent_event_handler_func func, void *context)
{
if (id >= 0 && id != handler->id)
return 0;
if (event_name && (strcmp(event_name, handler->event_name) != 0))
return 0;
if (sys_name && (strcmp(sys_name, handler->sys_name) != 0))
return 0;
if (func != handler->func || context != handler->context)
return 0;
return 1;
}
/**
* pevent_unregister_event_handler - unregister an existing event handler
* @pevent: the handle to the pevent
* @id: the id of the event to unregister
* @sys_name: the system name the handler belongs to
* @event_name: the name of the event handler
* @func: the function to call to parse the event information
* @context: the data to be passed to @func
*
* This function removes existing event handler (parser).
*
* If @id is >= 0, then it is used to find the event.
* else @sys_name and @event_name are used.
*
* Returns 0 if handler was removed successfully, -1 if event was not found.
*/
int pevent_unregister_event_handler(struct pevent *pevent, int id,
const char *sys_name, const char *event_name,
pevent_event_handler_func func, void *context)
{
struct event_format *event;
struct event_handler *handle;
struct event_handler **next;
event = pevent_search_event(pevent, id, sys_name, event_name);
if (event == NULL)
goto not_found;
if (event->handler == func && event->context == context) {
pr_stat("removing override handler for event (%d) %s:%s. Going back to default handler.",
event->id, event->system, event->name);
event->handler = NULL;
event->context = NULL;
return 0;
}
not_found:
for (next = &pevent->handlers; *next; next = &(*next)->next) {
handle = *next;
if (handle_matches(handle, id, sys_name, event_name,
func, context))
break;
}
if (!(*next))
return -1;
*next = handle->next;
free_handler(handle);
return 0;
}
/** /**
* pevent_alloc - create a pevent handle * pevent_alloc - create a pevent handle
*/ */
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdarg.h> #include <stdarg.h>
#include <regex.h> #include <regex.h>
#include <string.h>
#ifndef __maybe_unused #ifndef __maybe_unused
#define __maybe_unused __attribute__((unused)) #define __maybe_unused __attribute__((unused))
...@@ -57,6 +58,12 @@ struct pevent_record { ...@@ -57,6 +58,12 @@ struct pevent_record {
#endif #endif
}; };
enum trace_seq_fail {
TRACE_SEQ__GOOD,
TRACE_SEQ__BUFFER_POISONED,
TRACE_SEQ__MEM_ALLOC_FAILED,
};
/* /*
* Trace sequences are used to allow a function to call several other functions * Trace sequences are used to allow a function to call several other functions
* to create a string of data to use (up to a max of PAGE_SIZE). * to create a string of data to use (up to a max of PAGE_SIZE).
...@@ -67,6 +74,7 @@ struct trace_seq { ...@@ -67,6 +74,7 @@ struct trace_seq {
unsigned int buffer_size; unsigned int buffer_size;
unsigned int len; unsigned int len;
unsigned int readpos; unsigned int readpos;
enum trace_seq_fail state;
}; };
void trace_seq_init(struct trace_seq *s); void trace_seq_init(struct trace_seq *s);
...@@ -97,7 +105,7 @@ typedef int (*pevent_event_handler_func)(struct trace_seq *s, ...@@ -97,7 +105,7 @@ typedef int (*pevent_event_handler_func)(struct trace_seq *s,
void *context); void *context);
typedef int (*pevent_plugin_load_func)(struct pevent *pevent); typedef int (*pevent_plugin_load_func)(struct pevent *pevent);
typedef int (*pevent_plugin_unload_func)(void); typedef int (*pevent_plugin_unload_func)(struct pevent *pevent);
struct plugin_option { struct plugin_option {
struct plugin_option *next; struct plugin_option *next;
...@@ -122,7 +130,7 @@ struct plugin_option { ...@@ -122,7 +130,7 @@ struct plugin_option {
* PEVENT_PLUGIN_UNLOADER: (optional) * PEVENT_PLUGIN_UNLOADER: (optional)
* The function called just before unloading * The function called just before unloading
* *
* int PEVENT_PLUGIN_UNLOADER(void) * int PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
* *
* PEVENT_PLUGIN_OPTIONS: (optional) * PEVENT_PLUGIN_OPTIONS: (optional)
* Plugin options that can be set before loading * Plugin options that can be set before loading
...@@ -355,12 +363,35 @@ enum pevent_flag { ...@@ -355,12 +363,35 @@ enum pevent_flag {
_PE(READ_FORMAT_FAILED, "failed to read event format"), \ _PE(READ_FORMAT_FAILED, "failed to read event format"), \
_PE(READ_PRINT_FAILED, "failed to read event print fmt"), \ _PE(READ_PRINT_FAILED, "failed to read event print fmt"), \
_PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace"),\ _PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace"),\
_PE(INVALID_ARG_TYPE, "invalid argument type") _PE(INVALID_ARG_TYPE, "invalid argument type"), \
_PE(INVALID_EXP_TYPE, "invalid expression type"), \
_PE(INVALID_OP_TYPE, "invalid operator type"), \
_PE(INVALID_EVENT_NAME, "invalid event name"), \
_PE(EVENT_NOT_FOUND, "no event found"), \
_PE(SYNTAX_ERROR, "syntax error"), \
_PE(ILLEGAL_RVALUE, "illegal rvalue"), \
_PE(ILLEGAL_LVALUE, "illegal lvalue for string comparison"), \
_PE(INVALID_REGEX, "regex did not compute"), \
_PE(ILLEGAL_STRING_CMP, "illegal comparison for string"), \
_PE(ILLEGAL_INTEGER_CMP,"illegal comparison for integer"), \
_PE(REPARENT_NOT_OP, "cannot reparent other than OP"), \
_PE(REPARENT_FAILED, "failed to reparent filter OP"), \
_PE(BAD_FILTER_ARG, "bad arg in filter tree"), \
_PE(UNEXPECTED_TYPE, "unexpected type (not a value)"), \
_PE(ILLEGAL_TOKEN, "illegal token"), \
_PE(INVALID_PAREN, "open parenthesis cannot come here"), \
_PE(UNBALANCED_PAREN, "unbalanced number of parenthesis"), \
_PE(UNKNOWN_TOKEN, "unknown token"), \
_PE(FILTER_NOT_FOUND, "no filter found"), \
_PE(NOT_A_NUMBER, "must have number field"), \
_PE(NO_FILTER, "no filters exists"), \
_PE(FILTER_MISS, "record does not match to filter")
#undef _PE #undef _PE
#define _PE(__code, __str) PEVENT_ERRNO__ ## __code #define _PE(__code, __str) PEVENT_ERRNO__ ## __code
enum pevent_errno { enum pevent_errno {
PEVENT_ERRNO__SUCCESS = 0, PEVENT_ERRNO__SUCCESS = 0,
PEVENT_ERRNO__FILTER_MATCH = PEVENT_ERRNO__SUCCESS,
/* /*
* Choose an arbitrary negative big number not to clash with standard * Choose an arbitrary negative big number not to clash with standard
...@@ -377,6 +408,12 @@ enum pevent_errno { ...@@ -377,6 +408,12 @@ enum pevent_errno {
}; };
#undef _PE #undef _PE
struct plugin_list;
struct plugin_list *traceevent_load_plugins(struct pevent *pevent);
void traceevent_unload_plugins(struct plugin_list *plugin_list,
struct pevent *pevent);
struct cmdline; struct cmdline;
struct cmdline_list; struct cmdline_list;
struct func_map; struct func_map;
...@@ -522,6 +559,15 @@ __data2host8(struct pevent *pevent, unsigned long long data) ...@@ -522,6 +559,15 @@ __data2host8(struct pevent *pevent, unsigned long long data)
__data2host8(pevent, __val); \ __data2host8(pevent, __val); \
}) })
static inline int traceevent_host_bigendian(void)
{
unsigned char str[] = { 0x1, 0x2, 0x3, 0x4 };
unsigned int val;
memcpy(&val, str, 4);
return val == 0x01020304;
}
/* taken from kernel/trace/trace.h */ /* taken from kernel/trace/trace.h */
enum trace_flag_type { enum trace_flag_type {
TRACE_FLAG_IRQS_OFF = 0x01, TRACE_FLAG_IRQS_OFF = 0x01,
...@@ -547,7 +593,9 @@ int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long siz ...@@ -547,7 +593,9 @@ int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long siz
enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf, enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf,
unsigned long size, const char *sys); unsigned long size, const char *sys);
enum pevent_errno pevent_parse_format(struct event_format **eventp, const char *buf, enum pevent_errno pevent_parse_format(struct pevent *pevent,
struct event_format **eventp,
const char *buf,
unsigned long size, const char *sys); unsigned long size, const char *sys);
void pevent_free_format(struct event_format *event); void pevent_free_format(struct event_format *event);
...@@ -576,10 +624,15 @@ int pevent_print_func_field(struct trace_seq *s, const char *fmt, ...@@ -576,10 +624,15 @@ int pevent_print_func_field(struct trace_seq *s, const char *fmt,
int pevent_register_event_handler(struct pevent *pevent, int id, int pevent_register_event_handler(struct pevent *pevent, int id,
const char *sys_name, const char *event_name, const char *sys_name, const char *event_name,
pevent_event_handler_func func, void *context); pevent_event_handler_func func, void *context);
int pevent_unregister_event_handler(struct pevent *pevent, int id,
const char *sys_name, const char *event_name,
pevent_event_handler_func func, void *context);
int pevent_register_print_function(struct pevent *pevent, int pevent_register_print_function(struct pevent *pevent,
pevent_func_handler func, pevent_func_handler func,
enum pevent_func_arg_type ret_type, enum pevent_func_arg_type ret_type,
char *name, ...); char *name, ...);
int pevent_unregister_print_function(struct pevent *pevent,
pevent_func_handler func, char *name);
struct format_field *pevent_find_common_field(struct event_format *event, const char *name); struct format_field *pevent_find_common_field(struct event_format *event, const char *name);
struct format_field *pevent_find_field(struct event_format *event, const char *name); struct format_field *pevent_find_field(struct event_format *event, const char *name);
...@@ -811,18 +864,22 @@ struct filter_type { ...@@ -811,18 +864,22 @@ struct filter_type {
struct filter_arg *filter; struct filter_arg *filter;
}; };
#define PEVENT_FILTER_ERROR_BUFSZ 1024
struct event_filter { struct event_filter {
struct pevent *pevent; struct pevent *pevent;
int filters; int filters;
struct filter_type *event_filters; struct filter_type *event_filters;
char error_buffer[PEVENT_FILTER_ERROR_BUFSZ];
}; };
struct event_filter *pevent_filter_alloc(struct pevent *pevent); struct event_filter *pevent_filter_alloc(struct pevent *pevent);
#define FILTER_NONE -2 /* for backward compatibility */
#define FILTER_NOEXIST -1 #define FILTER_NONE PEVENT_ERRNO__FILTER_NOT_FOUND
#define FILTER_MISS 0 #define FILTER_NOEXIST PEVENT_ERRNO__NO_FILTER
#define FILTER_MATCH 1 #define FILTER_MISS PEVENT_ERRNO__FILTER_MISS
#define FILTER_MATCH PEVENT_ERRNO__FILTER_MATCH
enum filter_trivial_type { enum filter_trivial_type {
FILTER_TRIVIAL_FALSE, FILTER_TRIVIAL_FALSE,
...@@ -830,20 +887,21 @@ enum filter_trivial_type { ...@@ -830,20 +887,21 @@ enum filter_trivial_type {
FILTER_TRIVIAL_BOTH, FILTER_TRIVIAL_BOTH,
}; };
int pevent_filter_add_filter_str(struct event_filter *filter, enum pevent_errno pevent_filter_add_filter_str(struct event_filter *filter,
const char *filter_str, const char *filter_str);
char **error_str);
enum pevent_errno pevent_filter_match(struct event_filter *filter,
int pevent_filter_match(struct event_filter *filter,
struct pevent_record *record); struct pevent_record *record);
int pevent_filter_strerror(struct event_filter *filter, enum pevent_errno err,
char *buf, size_t buflen);
int pevent_event_filtered(struct event_filter *filter, int pevent_event_filtered(struct event_filter *filter,
int event_id); int event_id);
void pevent_filter_reset(struct event_filter *filter); void pevent_filter_reset(struct event_filter *filter);
void pevent_filter_clear_trivial(struct event_filter *filter, int pevent_filter_clear_trivial(struct event_filter *filter,
enum filter_trivial_type type); enum filter_trivial_type type);
void pevent_filter_free(struct event_filter *filter); void pevent_filter_free(struct event_filter *filter);
......
/*
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <string.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include "event-parse.h"
#include "event-utils.h"
#define LOCAL_PLUGIN_DIR ".traceevent/plugins"
struct plugin_list {
struct plugin_list *next;
char *name;
void *handle;
};
static void
load_plugin(struct pevent *pevent, const char *path,
const char *file, void *data)
{
struct plugin_list **plugin_list = data;
pevent_plugin_load_func func;
struct plugin_list *list;
const char *alias;
char *plugin;
void *handle;
plugin = malloc(strlen(path) + strlen(file) + 2);
if (!plugin) {
warning("could not allocate plugin memory\n");
return;
}
strcpy(plugin, path);
strcat(plugin, "/");
strcat(plugin, file);
handle = dlopen(plugin, RTLD_NOW | RTLD_GLOBAL);
if (!handle) {
warning("could not load plugin '%s'\n%s\n",
plugin, dlerror());
goto out_free;
}
alias = dlsym(handle, PEVENT_PLUGIN_ALIAS_NAME);
if (!alias)
alias = file;
func = dlsym(handle, PEVENT_PLUGIN_LOADER_NAME);
if (!func) {
warning("could not find func '%s' in plugin '%s'\n%s\n",
PEVENT_PLUGIN_LOADER_NAME, plugin, dlerror());
goto out_free;
}
list = malloc(sizeof(*list));
if (!list) {
warning("could not allocate plugin memory\n");
goto out_free;
}
list->next = *plugin_list;
list->handle = handle;
list->name = plugin;
*plugin_list = list;
pr_stat("registering plugin: %s", plugin);
func(pevent);
return;
out_free:
free(plugin);
}
static void
load_plugins_dir(struct pevent *pevent, const char *suffix,
const char *path,
void (*load_plugin)(struct pevent *pevent,
const char *path,
const char *name,
void *data),
void *data)
{
struct dirent *dent;
struct stat st;
DIR *dir;
int ret;
ret = stat(path, &st);
if (ret < 0)
return;
if (!S_ISDIR(st.st_mode))
return;
dir = opendir(path);
if (!dir)
return;
while ((dent = readdir(dir))) {
const char *name = dent->d_name;
if (strcmp(name, ".") == 0 ||
strcmp(name, "..") == 0)
continue;
/* Only load plugins that end in suffix */
if (strcmp(name + (strlen(name) - strlen(suffix)), suffix) != 0)
continue;
load_plugin(pevent, path, name, data);
}
closedir(dir);
}
static void
load_plugins(struct pevent *pevent, const char *suffix,
void (*load_plugin)(struct pevent *pevent,
const char *path,
const char *name,
void *data),
void *data)
{
char *home;
char *path;
char *envdir;
/*
* If a system plugin directory was defined,
* check that first.
*/
#ifdef PLUGIN_DIR
load_plugins_dir(pevent, suffix, PLUGIN_DIR, load_plugin, data);
#endif
/*
* Next let the environment-set plugin directory
* override the system defaults.
*/
envdir = getenv("TRACEEVENT_PLUGIN_DIR");
if (envdir)
load_plugins_dir(pevent, suffix, envdir, load_plugin, data);
/*
* Now let the home directory override the environment
* or system defaults.
*/
home = getenv("HOME");
if (!home)
return;
path = malloc(strlen(home) + strlen(LOCAL_PLUGIN_DIR) + 2);
if (!path) {
warning("could not allocate plugin memory\n");
return;
}
strcpy(path, home);
strcat(path, "/");
strcat(path, LOCAL_PLUGIN_DIR);
load_plugins_dir(pevent, suffix, path, load_plugin, data);
free(path);
}
struct plugin_list*
traceevent_load_plugins(struct pevent *pevent)
{
struct plugin_list *list = NULL;
load_plugins(pevent, ".so", load_plugin, &list);
return list;
}
void
traceevent_unload_plugins(struct plugin_list *plugin_list, struct pevent *pevent)
{
pevent_plugin_unload_func func;
struct plugin_list *list;
while (plugin_list) {
list = plugin_list;
plugin_list = list->next;
func = dlsym(list->handle, PEVENT_PLUGIN_UNLOADER_NAME);
if (func)
func(pevent);
dlclose(list->handle);
free(list->name);
free(list);
}
}
...@@ -23,18 +23,14 @@ ...@@ -23,18 +23,14 @@
#include <ctype.h> #include <ctype.h>
/* Can be overridden */ /* Can be overridden */
void die(const char *fmt, ...);
void *malloc_or_die(unsigned int size);
void warning(const char *fmt, ...); void warning(const char *fmt, ...);
void pr_stat(const char *fmt, ...); void pr_stat(const char *fmt, ...);
void vpr_stat(const char *fmt, va_list ap); void vpr_stat(const char *fmt, va_list ap);
/* Always available */ /* Always available */
void __die(const char *fmt, ...);
void __warning(const char *fmt, ...); void __warning(const char *fmt, ...);
void __pr_stat(const char *fmt, ...); void __pr_stat(const char *fmt, ...);
void __vdie(const char *fmt, ...);
void __vwarning(const char *fmt, ...); void __vwarning(const char *fmt, ...);
void __vpr_stat(const char *fmt, ...); void __vpr_stat(const char *fmt, ...);
......
This diff is collapsed.
...@@ -25,40 +25,6 @@ ...@@ -25,40 +25,6 @@
#define __weak __attribute__((weak)) #define __weak __attribute__((weak))
void __vdie(const char *fmt, va_list ap)
{
int ret = errno;
if (errno)
perror("trace-cmd");
else
ret = -1;
fprintf(stderr, " ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
exit(ret);
}
void __die(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
__vdie(fmt, ap);
va_end(ap);
}
void __weak die(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
__vdie(fmt, ap);
va_end(ap);
}
void __vwarning(const char *fmt, va_list ap) void __vwarning(const char *fmt, va_list ap)
{ {
if (errno) if (errno)
...@@ -117,13 +83,3 @@ void __weak pr_stat(const char *fmt, ...) ...@@ -117,13 +83,3 @@ void __weak pr_stat(const char *fmt, ...)
__vpr_stat(fmt, ap); __vpr_stat(fmt, ap);
va_end(ap); va_end(ap);
} }
void __weak *malloc_or_die(unsigned int size)
{
void *data;
data = malloc(size);
if (!data)
die("malloc");
return data;
}
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <endian.h>
#include "event-parse.h"
static unsigned long long
process___le16_to_cpup(struct trace_seq *s,
unsigned long long *args)
{
uint16_t *val = (uint16_t *) (unsigned long) args[0];
return val ? (long long) le16toh(*val) : 0;
}
int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
{
pevent_register_print_function(pevent,
process___le16_to_cpup,
PEVENT_FUNC_ARG_INT,
"__le16_to_cpup",
PEVENT_FUNC_ARG_PTR,
PEVENT_FUNC_ARG_VOID);
return 0;
}
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
{
pevent_unregister_print_function(pevent, process___le16_to_cpup,
"__le16_to_cpup");
}
/*
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "event-parse.h"
#include "event-utils.h"
static struct func_stack {
int size;
char **stack;
} *fstack;
static int cpus = -1;
#define STK_BLK 10
static void add_child(struct func_stack *stack, const char *child, int pos)
{
int i;
if (!child)
return;
if (pos < stack->size)
free(stack->stack[pos]);
else {
char **ptr;
ptr = realloc(stack->stack, sizeof(char *) *
(stack->size + STK_BLK));
if (!ptr) {
warning("could not allocate plugin memory\n");
return;
}
stack->stack = ptr;
for (i = stack->size; i < stack->size + STK_BLK; i++)
stack->stack[i] = NULL;
stack->size += STK_BLK;
}
stack->stack[pos] = strdup(child);
}
static int add_and_get_index(const char *parent, const char *child, int cpu)
{
int i;
if (cpu < 0)
return 0;
if (cpu > cpus) {
struct func_stack *ptr;
ptr = realloc(fstack, sizeof(*fstack) * (cpu + 1));
if (!ptr) {
warning("could not allocate plugin memory\n");
return 0;
}
fstack = ptr;
/* Account for holes in the cpu count */
for (i = cpus + 1; i <= cpu; i++)
memset(&fstack[i], 0, sizeof(fstack[i]));
cpus = cpu;
}
for (i = 0; i < fstack[cpu].size && fstack[cpu].stack[i]; i++) {
if (strcmp(parent, fstack[cpu].stack[i]) == 0) {
add_child(&fstack[cpu], child, i+1);
return i;
}
}
/* Not found */
add_child(&fstack[cpu], parent, 0);
add_child(&fstack[cpu], child, 1);
return 0;
}
static int function_handler(struct trace_seq *s, struct pevent_record *record,
struct event_format *event, void *context)
{
struct pevent *pevent = event->pevent;
unsigned long long function;
unsigned long long pfunction;
const char *func;
const char *parent;
int index;
if (pevent_get_field_val(s, event, "ip", record, &function, 1))
return trace_seq_putc(s, '!');
func = pevent_find_function(pevent, function);
if (pevent_get_field_val(s, event, "parent_ip", record, &pfunction, 1))
return trace_seq_putc(s, '!');
parent = pevent_find_function(pevent, pfunction);
index = add_and_get_index(parent, func, record->cpu);
trace_seq_printf(s, "%*s", index*3, "");
if (func)
trace_seq_printf(s, "%s", func);
else
trace_seq_printf(s, "0x%llx", function);
trace_seq_printf(s, " <-- ");
if (parent)
trace_seq_printf(s, "%s", parent);
else
trace_seq_printf(s, "0x%llx", pfunction);
return 0;
}
int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
{
pevent_register_event_handler(pevent, -1, "ftrace", "function",
function_handler, NULL);
return 0;
}
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
{
int i, x;
pevent_unregister_event_handler(pevent, -1, "ftrace", "function",
function_handler, NULL);
for (i = 0; i <= cpus; i++) {
for (x = 0; x < fstack[i].size && fstack[i].stack[x]; x++)
free(fstack[i].stack[x]);
free(fstack[i].stack);
}
free(fstack);
fstack = NULL;
cpus = -1;
}
/*
* Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
* Copyright (C) 2009 Johannes Berg <johannes@sipsolutions.net>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "event-parse.h"
static int timer_expire_handler(struct trace_seq *s,
struct pevent_record *record,
struct event_format *event, void *context)
{
trace_seq_printf(s, "hrtimer=");
if (pevent_print_num_field(s, "0x%llx", event, "timer",
record, 0) == -1)
pevent_print_num_field(s, "0x%llx", event, "hrtimer",
record, 1);
trace_seq_printf(s, " now=");
pevent_print_num_field(s, "%llu", event, "now", record, 1);
pevent_print_func_field(s, " function=%s", event, "function",
record, 0);
return 0;
}
static int timer_start_handler(struct trace_seq *s,
struct pevent_record *record,
struct event_format *event, void *context)
{
trace_seq_printf(s, "hrtimer=");
if (pevent_print_num_field(s, "0x%llx", event, "timer",
record, 0) == -1)
pevent_print_num_field(s, "0x%llx", event, "hrtimer",
record, 1);
pevent_print_func_field(s, " function=%s", event, "function",
record, 0);
trace_seq_printf(s, " expires=");
pevent_print_num_field(s, "%llu", event, "expires", record, 1);
trace_seq_printf(s, " softexpires=");
pevent_print_num_field(s, "%llu", event, "softexpires", record, 1);
return 0;
}
int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
{
pevent_register_event_handler(pevent, -1,
"timer", "hrtimer_expire_entry",
timer_expire_handler, NULL);
pevent_register_event_handler(pevent, -1, "timer", "hrtimer_start",
timer_start_handler, NULL);
return 0;
}
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
{
pevent_unregister_event_handler(pevent, -1,
"timer", "hrtimer_expire_entry",
timer_expire_handler, NULL);
pevent_unregister_event_handler(pevent, -1, "timer", "hrtimer_start",
timer_start_handler, NULL);
}
/*
* Copyright (C) 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "event-parse.h"
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
#define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
static unsigned long long
process_jbd2_dev_to_name(struct trace_seq *s,
unsigned long long *args)
{
unsigned int dev = args[0];
trace_seq_printf(s, "%d:%d", MAJOR(dev), MINOR(dev));
return 0;
}
static unsigned long long
process_jiffies_to_msecs(struct trace_seq *s,
unsigned long long *args)
{
unsigned long long jiffies = args[0];
trace_seq_printf(s, "%lld", jiffies);
return jiffies;
}
int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
{
pevent_register_print_function(pevent,
process_jbd2_dev_to_name,
PEVENT_FUNC_ARG_STRING,
"jbd2_dev_to_name",
PEVENT_FUNC_ARG_INT,
PEVENT_FUNC_ARG_VOID);
pevent_register_print_function(pevent,
process_jiffies_to_msecs,
PEVENT_FUNC_ARG_LONG,
"jiffies_to_msecs",
PEVENT_FUNC_ARG_LONG,
PEVENT_FUNC_ARG_VOID);
return 0;
}
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
{
pevent_unregister_print_function(pevent, process_jbd2_dev_to_name,
"jbd2_dev_to_name");
pevent_unregister_print_function(pevent, process_jiffies_to_msecs,
"jiffies_to_msecs");
}
/*
* Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "event-parse.h"
static int call_site_handler(struct trace_seq *s, struct pevent_record *record,
struct event_format *event, void *context)
{
struct format_field *field;
unsigned long long val, addr;
void *data = record->data;
const char *func;
field = pevent_find_field(event, "call_site");
if (!field)
return 1;
if (pevent_read_number_field(field, data, &val))
return 1;
func = pevent_find_function(event->pevent, val);
if (!func)
return 1;
addr = pevent_find_function_address(event->pevent, val);
trace_seq_printf(s, "(%s+0x%x) ", func, (int)(val - addr));
return 1;
}
int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
{
pevent_register_event_handler(pevent, -1, "kmem", "kfree",
call_site_handler, NULL);
pevent_register_event_handler(pevent, -1, "kmem", "kmalloc",
call_site_handler, NULL);
pevent_register_event_handler(pevent, -1, "kmem", "kmalloc_node",
call_site_handler, NULL);
pevent_register_event_handler(pevent, -1, "kmem", "kmem_cache_alloc",
call_site_handler, NULL);
pevent_register_event_handler(pevent, -1, "kmem",
"kmem_cache_alloc_node",
call_site_handler, NULL);
pevent_register_event_handler(pevent, -1, "kmem", "kmem_cache_free",
call_site_handler, NULL);
return 0;
}
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
{
pevent_unregister_event_handler(pevent, -1, "kmem", "kfree",
call_site_handler, NULL);
pevent_unregister_event_handler(pevent, -1, "kmem", "kmalloc",
call_site_handler, NULL);
pevent_unregister_event_handler(pevent, -1, "kmem", "kmalloc_node",
call_site_handler, NULL);
pevent_unregister_event_handler(pevent, -1, "kmem", "kmem_cache_alloc",
call_site_handler, NULL);
pevent_unregister_event_handler(pevent, -1, "kmem",
"kmem_cache_alloc_node",
call_site_handler, NULL);
pevent_unregister_event_handler(pevent, -1, "kmem", "kmem_cache_free",
call_site_handler, NULL);
}
This diff is collapsed.
/*
* Copyright (C) 2009 Johannes Berg <johannes@sipsolutions.net>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "event-parse.h"
#define INDENT 65
static void print_string(struct trace_seq *s, struct event_format *event,
const char *name, const void *data)
{
struct format_field *f = pevent_find_field(event, name);
int offset;
int length;
if (!f) {
trace_seq_printf(s, "NOTFOUND:%s", name);
return;
}
offset = f->offset;
length = f->size;
if (!strncmp(f->type, "__data_loc", 10)) {
unsigned long long v;
if (pevent_read_number_field(f, data, &v)) {
trace_seq_printf(s, "invalid_data_loc");
return;
}
offset = v & 0xffff;
length = v >> 16;
}
trace_seq_printf(s, "%.*s", length, (char *)data + offset);
}
#define SF(fn) pevent_print_num_field(s, fn ":%d", event, fn, record, 0)
#define SFX(fn) pevent_print_num_field(s, fn ":%#x", event, fn, record, 0)
#define SP() trace_seq_putc(s, ' ')
static int drv_bss_info_changed(struct trace_seq *s,
struct pevent_record *record,
struct event_format *event, void *context)
{
void *data = record->data;
print_string(s, event, "wiphy_name", data);
trace_seq_printf(s, " vif:");
print_string(s, event, "vif_name", data);
pevent_print_num_field(s, "(%d)", event, "vif_type", record, 1);
trace_seq_printf(s, "\n%*s", INDENT, "");
SF("assoc"); SP();
SF("aid"); SP();
SF("cts"); SP();
SF("shortpre"); SP();
SF("shortslot"); SP();
SF("dtimper"); SP();
trace_seq_printf(s, "\n%*s", INDENT, "");
SF("bcnint"); SP();
SFX("assoc_cap"); SP();
SFX("basic_rates"); SP();
SF("enable_beacon");
trace_seq_printf(s, "\n%*s", INDENT, "");
SF("ht_operation_mode");
return 0;
}
int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
{
pevent_register_event_handler(pevent, -1, "mac80211",
"drv_bss_info_changed",
drv_bss_info_changed, NULL);
return 0;
}
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
{
pevent_unregister_event_handler(pevent, -1, "mac80211",
"drv_bss_info_changed",
drv_bss_info_changed, NULL);
}
/*
* Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License (not later!)
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, see <http://www.gnu.org/licenses>
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "event-parse.h"
static void write_state(struct trace_seq *s, int val)
{
const char states[] = "SDTtZXxW";
int found = 0;
int i;
for (i = 0; i < (sizeof(states) - 1); i++) {
if (!(val & (1 << i)))
continue;
if (found)
trace_seq_putc(s, '|');
found = 1;
trace_seq_putc(s, states[i]);
}
if (!found)
trace_seq_putc(s, 'R');
}
static void write_and_save_comm(struct format_field *field,
struct pevent_record *record,
struct trace_seq *s, int pid)
{
const char *comm;
int len;
comm = (char *)(record->data + field->offset);
len = s->len;
trace_seq_printf(s, "%.*s",
field->size, comm);
/* make sure the comm has a \0 at the end. */
trace_seq_terminate(s);
comm = &s->buffer[len];
/* Help out the comm to ids. This will handle dups */
pevent_register_comm(field->event->pevent, comm, pid);
}
static int sched_wakeup_handler(struct trace_seq *s,
struct pevent_record *record,
struct event_format *event, void *context)
{
struct format_field *field;
unsigned long long val;
if (pevent_get_field_val(s, event, "pid", record, &val, 1))
return trace_seq_putc(s, '!');
field = pevent_find_any_field(event, "comm");
if (field) {
write_and_save_comm(field, record, s, val);
trace_seq_putc(s, ':');
}
trace_seq_printf(s, "%lld", val);
if (pevent_get_field_val(s, event, "prio", record, &val, 0) == 0)
trace_seq_printf(s, " [%lld]", val);
if (pevent_get_field_val(s, event, "success", record, &val, 1) == 0)
trace_seq_printf(s, " success=%lld", val);
if (pevent_get_field_val(s, event, "target_cpu", record, &val, 0) == 0)
trace_seq_printf(s, " CPU:%03llu", val);
return 0;
}
static int sched_switch_handler(struct trace_seq *s,
struct pevent_record *record,
struct event_format *event, void *context)
{
struct format_field *field;
unsigned long long val;
if (pevent_get_field_val(s, event, "prev_pid", record, &val, 1))
return trace_seq_putc(s, '!');
field = pevent_find_any_field(event, "prev_comm");
if (field) {
write_and_save_comm(field, record, s, val);
trace_seq_putc(s, ':');
}
trace_seq_printf(s, "%lld ", val);
if (pevent_get_field_val(s, event, "prev_prio", record, &val, 0) == 0)
trace_seq_printf(s, "[%lld] ", val);
if (pevent_get_field_val(s, event, "prev_state", record, &val, 0) == 0)
write_state(s, val);
trace_seq_puts(s, " ==> ");
if (pevent_get_field_val(s, event, "next_pid", record, &val, 1))
return trace_seq_putc(s, '!');
field = pevent_find_any_field(event, "next_comm");
if (field) {
write_and_save_comm(field, record, s, val);
trace_seq_putc(s, ':');
}
trace_seq_printf(s, "%lld", val);
if (pevent_get_field_val(s, event, "next_prio", record, &val, 0) == 0)
trace_seq_printf(s, " [%lld]", val);
return 0;
}
int PEVENT_PLUGIN_LOADER(struct pevent *pevent)
{
pevent_register_event_handler(pevent, -1, "sched", "sched_switch",
sched_switch_handler, NULL);
pevent_register_event_handler(pevent, -1, "sched", "sched_wakeup",
sched_wakeup_handler, NULL);
pevent_register_event_handler(pevent, -1, "sched", "sched_wakeup_new",
sched_wakeup_handler, NULL);
return 0;
}
void PEVENT_PLUGIN_UNLOADER(struct pevent *pevent)
{
pevent_unregister_event_handler(pevent, -1, "sched", "sched_switch",
sched_switch_handler, NULL);
pevent_unregister_event_handler(pevent, -1, "sched", "sched_wakeup",
sched_wakeup_handler, NULL);
pevent_unregister_event_handler(pevent, -1, "sched", "sched_wakeup_new",
sched_wakeup_handler, NULL);
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -12,9 +12,9 @@ SYNOPSIS ...@@ -12,9 +12,9 @@ SYNOPSIS
DESCRIPTION DESCRIPTION
----------- -----------
This command runs runs perf-buildid-list --with-hits, and collects the files This command runs perf-buildid-list --with-hits, and collects the files with the
with the buildids found so that analysis of perf.data contents can be possible buildids found so that analysis of perf.data contents can be possible on another
on another machine. machine.
SEE ALSO SEE ALSO
......
This diff is collapsed.
This diff is collapsed.
...@@ -237,6 +237,15 @@ OPTIONS ...@@ -237,6 +237,15 @@ OPTIONS
Do not show entries which have an overhead under that percent. Do not show entries which have an overhead under that percent.
(Default: 0). (Default: 0).
--header::
Show header information in the perf.data file. This includes
various information like hostname, OS and perf version, cpu/mem
info, perf command line, event list and so on. Currently only
--stdio output supports this feature.
--header-only::
Show only perf.data header (forces --stdio).
SEE ALSO SEE ALSO
-------- --------
linkperf:perf-stat[1], linkperf:perf-annotate[1] linkperf:perf-stat[1], linkperf:perf-annotate[1]
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment