The major changes in this tracing update includes:

- Removing of non-DYNAMIC_FTRACE from 32bit x86
 
  - Removing of mcount support from x86
 
  - Emulating a call from int3 on x86_64, fixes live kernel patching
 
  - Consolidated Tracing Error logs file
 
 Minor updates:
 
  - Removal of klp_check_compiler_support()
 
  - kdb ftrace dumping output changes
 
  - Accessing and creating ftrace instances from inside the kernel
 
  - Clean up of #define if macro
 
  - Introduction of TRACE_EVENT_NOP() to disable trace events based on config
    options
 
 And other minor fixes and clean ups
 -----BEGIN PGP SIGNATURE-----
 
 iIoEABYIADIWIQRRSw7ePDh/lE+zeZMp5XQQmuv6qgUCXNxMZxQccm9zdGVkdEBn
 b29kbWlzLm9yZwAKCRAp5XQQmuv6qq4PAP44kP6VbwL8CHyI2A3xuJ6Hwxd+2Z2r
 ip66RtzyJ+2iCgEA2QCuWUlEt2bLpF9a8IQ4N9tWenSeW2i7gunPb+tioQw=
 =RVQo
 -----END PGP SIGNATURE-----

Merge tag 'trace-v5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace

Pull tracing updates from Steven Rostedt:
 "The major changes in this tracing update includes:

   - Removal of non-DYNAMIC_FTRACE from 32bit x86

   - Removal of mcount support from x86

   - Emulating a call from int3 on x86_64, fixes live kernel patching

   - Consolidated Tracing Error logs file

  Minor updates:

   - Removal of klp_check_compiler_support()

   - kdb ftrace dumping output changes

   - Accessing and creating ftrace instances from inside the kernel

   - Clean up of #define if macro

   - Introduction of TRACE_EVENT_NOP() to disable trace events based on
     config options

  And other minor fixes and clean ups"

* tag 'trace-v5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/rostedt/linux-trace: (44 commits)
  x86: Hide the int3_emulate_call/jmp functions from UML
  livepatch: Remove klp_check_compiler_support()
  ftrace/x86: Remove mcount support
  ftrace/x86_32: Remove support for non DYNAMIC_FTRACE
  tracing: Simplify "if" macro code
  tracing: Fix documentation about disabling options using trace_options
  tracing: Replace kzalloc with kcalloc
  tracing: Fix partial reading of trace event's id file
  tracing: Allow RCU to run between postponed startup tests
  tracing: Fix white space issues in parse_pred() function
  tracing: Eliminate const char[] auto variables
  ring-buffer: Fix mispelling of Calculate
  tracing: probeevent: Fix to make the type of $comm string
  tracing: probeevent: Do not accumulate on ret variable
  tracing: uprobes: Re-enable $comm support for uprobe events
  ftrace/x86_64: Emulate call function while updating in breakpoint handler
  x86_64: Allow breakpoints to emulate call instructions
  x86_64: Add gap to int3 to allow for call emulation
  tracing: kdb: Allow ftdump to skip all but the last few entries
  tracing: Add trace_total_entries() / trace_total_entries_cpu()
  ...
This commit is contained in:
Linus Torvalds 2019-05-15 16:05:47 -07:00
commit d2d8b14604
44 changed files with 1343 additions and 649 deletions

View File

@ -765,6 +765,37 @@ Here is the list of current tracers that may be configured.
tracers from tracing simply echo "nop" into tracers from tracing simply echo "nop" into
current_tracer. current_tracer.
Error conditions
----------------
For most ftrace commands, failure modes are obvious and communicated
using standard return codes.
For other more involved commands, extended error information may be
available via the tracing/error_log file. For the commands that
support it, reading the tracing/error_log file after an error will
display more detailed information about what went wrong, if
information is available. The tracing/error_log file is a circular
error log displaying a small number (currently, 8) of ftrace errors
for the last (8) failed commands.
The extended error information and usage takes the form shown in
this example::
# echo xxx > /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger
echo: write error: Invalid argument
# cat /sys/kernel/debug/tracing/error_log
[ 5348.887237] location: error: Couldn't yyy: zzz
Command: xxx
^
[ 7517.023364] location: error: Bad rrr: sss
Command: ppp qqq
^
To clear the error log, echo the empty string into it::
# echo > /sys/kernel/debug/tracing/error_log
Examples of using the tracer Examples of using the tracer
---------------------------- ----------------------------

View File

@ -199,20 +199,8 @@ Extended error information
For some error conditions encountered when invoking a hist trigger For some error conditions encountered when invoking a hist trigger
command, extended error information is available via the command, extended error information is available via the
corresponding event's 'hist' file. Reading the hist file after an tracing/error_log file. See Error Conditions in
error will display more detailed information about what went wrong, :file:`Documentation/trace/ftrace.rst` for details.
if information is available. This extended error information will
be available until the next hist trigger command for that event.
If available for a given error condition, the extended error
information and usage takes the following form::
# echo xxx > /sys/kernel/debug/tracing/events/sched/sched_wakeup/trigger
echo: write error: Invalid argument
# cat /sys/kernel/debug/tracing/events/sched/sched_wakeup/hist
ERROR: Couldn't yyy: zzz
Last command: xxx
6.2 'hist' trigger examples 6.2 'hist' trigger examples
--------------------------- ---------------------------

View File

@ -7,7 +7,6 @@
#ifndef CONFIG_DYNAMIC_FTRACE #ifndef CONFIG_DYNAMIC_FTRACE
extern void (*ftrace_trace_function)(unsigned long, unsigned long, extern void (*ftrace_trace_function)(unsigned long, unsigned long,
struct ftrace_ops*, struct pt_regs*); struct ftrace_ops*, struct pt_regs*);
extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace);
extern void ftrace_graph_caller(void); extern void ftrace_graph_caller(void);
noinline void __naked ftrace_stub(unsigned long ip, unsigned long parent_ip, noinline void __naked ftrace_stub(unsigned long ip, unsigned long parent_ip,

View File

@ -51,7 +51,6 @@ void notrace __hot ftrace_function_trampoline(unsigned long parent,
unsigned long org_sp_gr3) unsigned long org_sp_gr3)
{ {
extern ftrace_func_t ftrace_trace_function; /* depends on CONFIG_DYNAMIC_FTRACE */ extern ftrace_func_t ftrace_trace_function; /* depends on CONFIG_DYNAMIC_FTRACE */
extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace);
if (ftrace_trace_function != ftrace_stub) { if (ftrace_trace_function != ftrace_stub) {
/* struct ftrace_ops *op, struct pt_regs *regs); */ /* struct ftrace_ops *op, struct pt_regs *regs); */

View File

@ -24,11 +24,6 @@
#include <linux/sched/task_stack.h> #include <linux/sched/task_stack.h>
#ifdef CONFIG_LIVEPATCH #ifdef CONFIG_LIVEPATCH
static inline int klp_check_compiler_support(void)
{
return 0;
}
static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip) static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)
{ {
regs->nip = ip; regs->nip = ip;

View File

@ -13,11 +13,6 @@
#include <asm/ptrace.h> #include <asm/ptrace.h>
static inline int klp_check_compiler_support(void)
{
return 0;
}
static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip) static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)
{ {
regs->psw.addr = ip; regs->psw.addr = ip;

View File

@ -31,6 +31,17 @@ config X86_64
select SWIOTLB select SWIOTLB
select ARCH_HAS_SYSCALL_WRAPPER select ARCH_HAS_SYSCALL_WRAPPER
config FORCE_DYNAMIC_FTRACE
def_bool y
depends on X86_32
depends on FUNCTION_TRACER
select DYNAMIC_FTRACE
help
We keep the static function tracing (!DYNAMIC_FTRACE) around
in order to test the non static function tracing in the
generic code, as other architectures still use it. But we
only need to keep it around for x86_64. No need to keep it
for x86_32. For x86_32, force DYNAMIC_FTRACE.
# #
# Arch settings # Arch settings
# #

View File

@ -878,7 +878,7 @@ apicinterrupt IRQ_WORK_VECTOR irq_work_interrupt smp_irq_work_interrupt
* @paranoid == 2 is special: the stub will never switch stacks. This is for * @paranoid == 2 is special: the stub will never switch stacks. This is for
* #DF: if the thread stack is somehow unusable, we'll still get a useful OOPS. * #DF: if the thread stack is somehow unusable, we'll still get a useful OOPS.
*/ */
.macro idtentry sym do_sym has_error_code:req paranoid=0 shift_ist=-1 ist_offset=0 .macro idtentry sym do_sym has_error_code:req paranoid=0 shift_ist=-1 ist_offset=0 create_gap=0
ENTRY(\sym) ENTRY(\sym)
UNWIND_HINT_IRET_REGS offset=\has_error_code*8 UNWIND_HINT_IRET_REGS offset=\has_error_code*8
@ -898,6 +898,20 @@ ENTRY(\sym)
jnz .Lfrom_usermode_switch_stack_\@ jnz .Lfrom_usermode_switch_stack_\@
.endif .endif
.if \create_gap == 1
/*
* If coming from kernel space, create a 6-word gap to allow the
* int3 handler to emulate a call instruction.
*/
testb $3, CS-ORIG_RAX(%rsp)
jnz .Lfrom_usermode_no_gap_\@
.rept 6
pushq 5*8(%rsp)
.endr
UNWIND_HINT_IRET_REGS offset=8
.Lfrom_usermode_no_gap_\@:
.endif
.if \paranoid .if \paranoid
call paranoid_entry call paranoid_entry
.else .else
@ -1129,7 +1143,7 @@ apicinterrupt3 HYPERV_STIMER0_VECTOR \
#endif /* CONFIG_HYPERV */ #endif /* CONFIG_HYPERV */
idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=IST_INDEX_DB ist_offset=DB_STACK_OFFSET idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=IST_INDEX_DB ist_offset=DB_STACK_OFFSET
idtentry int3 do_int3 has_error_code=0 idtentry int3 do_int3 has_error_code=0 create_gap=1
idtentry stack_segment do_stack_segment has_error_code=1 idtentry stack_segment do_stack_segment has_error_code=1
#ifdef CONFIG_XEN_PV #ifdef CONFIG_XEN_PV

View File

@ -3,12 +3,10 @@
#define _ASM_X86_FTRACE_H #define _ASM_X86_FTRACE_H
#ifdef CONFIG_FUNCTION_TRACER #ifdef CONFIG_FUNCTION_TRACER
#ifdef CC_USING_FENTRY #ifndef CC_USING_FENTRY
# define MCOUNT_ADDR ((unsigned long)(__fentry__)) # error Compiler does not support fentry?
#else
# define MCOUNT_ADDR ((unsigned long)(mcount))
# define HAVE_FUNCTION_GRAPH_FP_TEST
#endif #endif
# define MCOUNT_ADDR ((unsigned long)(__fentry__))
#define MCOUNT_INSN_SIZE 5 /* sizeof mcount call */ #define MCOUNT_INSN_SIZE 5 /* sizeof mcount call */
#ifdef CONFIG_DYNAMIC_FTRACE #ifdef CONFIG_DYNAMIC_FTRACE

View File

@ -24,14 +24,6 @@
#include <asm/setup.h> #include <asm/setup.h>
#include <linux/ftrace.h> #include <linux/ftrace.h>
static inline int klp_check_compiler_support(void)
{
#ifndef CC_USING_FENTRY
return 1;
#endif
return 0;
}
static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip) static inline void klp_arch_set_pc(struct pt_regs *regs, unsigned long ip)
{ {
regs->ip = ip; regs->ip = ip;

View File

@ -42,4 +42,34 @@ extern int after_bootmem;
extern __ro_after_init struct mm_struct *poking_mm; extern __ro_after_init struct mm_struct *poking_mm;
extern __ro_after_init unsigned long poking_addr; extern __ro_after_init unsigned long poking_addr;
#ifndef CONFIG_UML_X86
static inline void int3_emulate_jmp(struct pt_regs *regs, unsigned long ip)
{
regs->ip = ip;
}
#define INT3_INSN_SIZE 1
#define CALL_INSN_SIZE 5
#ifdef CONFIG_X86_64
static inline void int3_emulate_push(struct pt_regs *regs, unsigned long val)
{
/*
* The int3 handler in entry_64.S adds a gap between the
* stack where the break point happened, and the saving of
* pt_regs. We can extend the original stack because of
* this gap. See the idtentry macro's create_gap option.
*/
regs->sp -= sizeof(unsigned long);
*(unsigned long *)regs->sp = val;
}
static inline void int3_emulate_call(struct pt_regs *regs, unsigned long func)
{
int3_emulate_push(regs, regs->ip - INT3_INSN_SIZE + CALL_INSN_SIZE);
int3_emulate_jmp(regs, func);
}
#endif /* CONFIG_X86_64 */
#endif /* !CONFIG_UML_X86 */
#endif /* _ASM_X86_TEXT_PATCHING_H */ #endif /* _ASM_X86_TEXT_PATCHING_H */

View File

@ -29,6 +29,7 @@
#include <asm/kprobes.h> #include <asm/kprobes.h>
#include <asm/ftrace.h> #include <asm/ftrace.h>
#include <asm/nops.h> #include <asm/nops.h>
#include <asm/text-patching.h>
#ifdef CONFIG_DYNAMIC_FTRACE #ifdef CONFIG_DYNAMIC_FTRACE
@ -231,6 +232,7 @@ int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
} }
static unsigned long ftrace_update_func; static unsigned long ftrace_update_func;
static unsigned long ftrace_update_func_call;
static int update_ftrace_func(unsigned long ip, void *new) static int update_ftrace_func(unsigned long ip, void *new)
{ {
@ -259,6 +261,8 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
unsigned char *new; unsigned char *new;
int ret; int ret;
ftrace_update_func_call = (unsigned long)func;
new = ftrace_call_replace(ip, (unsigned long)func); new = ftrace_call_replace(ip, (unsigned long)func);
ret = update_ftrace_func(ip, new); ret = update_ftrace_func(ip, new);
@ -294,13 +298,28 @@ int ftrace_int3_handler(struct pt_regs *regs)
if (WARN_ON_ONCE(!regs)) if (WARN_ON_ONCE(!regs))
return 0; return 0;
ip = regs->ip - 1; ip = regs->ip - INT3_INSN_SIZE;
if (!ftrace_location(ip) && !is_ftrace_caller(ip))
return 0;
regs->ip += MCOUNT_INSN_SIZE - 1; #ifdef CONFIG_X86_64
if (ftrace_location(ip)) {
int3_emulate_call(regs, (unsigned long)ftrace_regs_caller);
return 1;
} else if (is_ftrace_caller(ip)) {
if (!ftrace_update_func_call) {
int3_emulate_jmp(regs, ip + CALL_INSN_SIZE);
return 1;
}
int3_emulate_call(regs, ftrace_update_func_call);
return 1;
}
#else
if (ftrace_location(ip) || is_ftrace_caller(ip)) {
int3_emulate_jmp(regs, ip + CALL_INSN_SIZE);
return 1;
}
#endif
return 1; return 0;
} }
NOKPROBE_SYMBOL(ftrace_int3_handler); NOKPROBE_SYMBOL(ftrace_int3_handler);
@ -865,6 +884,8 @@ void arch_ftrace_update_trampoline(struct ftrace_ops *ops)
func = ftrace_ops_get_func(ops); func = ftrace_ops_get_func(ops);
ftrace_update_func_call = (unsigned long)func;
/* Do a safe modify in case the trampoline is executing */ /* Do a safe modify in case the trampoline is executing */
new = ftrace_call_replace(ip, (unsigned long)func); new = ftrace_call_replace(ip, (unsigned long)func);
ret = update_ftrace_func(ip, new); ret = update_ftrace_func(ip, new);
@ -966,6 +987,7 @@ static int ftrace_mod_jmp(unsigned long ip, void *func)
{ {
unsigned char *new; unsigned char *new;
ftrace_update_func_call = 0UL;
new = ftrace_jmp_replace(ip, (unsigned long)func); new = ftrace_jmp_replace(ip, (unsigned long)func);
return update_ftrace_func(ip, new); return update_ftrace_func(ip, new);

View File

@ -10,22 +10,10 @@
#include <asm/ftrace.h> #include <asm/ftrace.h>
#include <asm/nospec-branch.h> #include <asm/nospec-branch.h>
#ifdef CC_USING_FENTRY
# define function_hook __fentry__ # define function_hook __fentry__
EXPORT_SYMBOL(__fentry__) EXPORT_SYMBOL(__fentry__)
#else
# define function_hook mcount
EXPORT_SYMBOL(mcount)
#endif
#ifdef CONFIG_DYNAMIC_FTRACE #ifdef CONFIG_FRAME_POINTER
/* mcount uses a frame pointer even if CONFIG_FRAME_POINTER is not set */
#if !defined(CC_USING_FENTRY) || defined(CONFIG_FRAME_POINTER)
# define USING_FRAME_POINTER
#endif
#ifdef USING_FRAME_POINTER
# define MCOUNT_FRAME 1 /* using frame = true */ # define MCOUNT_FRAME 1 /* using frame = true */
#else #else
# define MCOUNT_FRAME 0 /* using frame = false */ # define MCOUNT_FRAME 0 /* using frame = false */
@ -37,8 +25,7 @@ END(function_hook)
ENTRY(ftrace_caller) ENTRY(ftrace_caller)
#ifdef USING_FRAME_POINTER #ifdef CONFIG_FRAME_POINTER
# ifdef CC_USING_FENTRY
/* /*
* Frame pointers are of ip followed by bp. * Frame pointers are of ip followed by bp.
* Since fentry is an immediate jump, we are left with * Since fentry is an immediate jump, we are left with
@ -49,7 +36,7 @@ ENTRY(ftrace_caller)
pushl %ebp pushl %ebp
movl %esp, %ebp movl %esp, %ebp
pushl 2*4(%esp) /* function ip */ pushl 2*4(%esp) /* function ip */
# endif
/* For mcount, the function ip is directly above */ /* For mcount, the function ip is directly above */
pushl %ebp pushl %ebp
movl %esp, %ebp movl %esp, %ebp
@ -59,7 +46,7 @@ ENTRY(ftrace_caller)
pushl %edx pushl %edx
pushl $0 /* Pass NULL as regs pointer */ pushl $0 /* Pass NULL as regs pointer */
#ifdef USING_FRAME_POINTER #ifdef CONFIG_FRAME_POINTER
/* Load parent ebp into edx */ /* Load parent ebp into edx */
movl 4*4(%esp), %edx movl 4*4(%esp), %edx
#else #else
@ -82,13 +69,11 @@ ftrace_call:
popl %edx popl %edx
popl %ecx popl %ecx
popl %eax popl %eax
#ifdef USING_FRAME_POINTER #ifdef CONFIG_FRAME_POINTER
popl %ebp popl %ebp
# ifdef CC_USING_FENTRY
addl $4,%esp /* skip function ip */ addl $4,%esp /* skip function ip */
popl %ebp /* this is the orig bp */ popl %ebp /* this is the orig bp */
addl $4, %esp /* skip parent ip */ addl $4, %esp /* skip parent ip */
# endif
#endif #endif
.Lftrace_ret: .Lftrace_ret:
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
@ -133,11 +118,7 @@ ENTRY(ftrace_regs_caller)
movl 12*4(%esp), %eax /* Load ip (1st parameter) */ movl 12*4(%esp), %eax /* Load ip (1st parameter) */
subl $MCOUNT_INSN_SIZE, %eax /* Adjust ip */ subl $MCOUNT_INSN_SIZE, %eax /* Adjust ip */
#ifdef CC_USING_FENTRY
movl 15*4(%esp), %edx /* Load parent ip (2nd parameter) */ movl 15*4(%esp), %edx /* Load parent ip (2nd parameter) */
#else
movl 0x4(%ebp), %edx /* Load parent ip (2nd parameter) */
#endif
movl function_trace_op, %ecx /* Save ftrace_pos in 3rd parameter */ movl function_trace_op, %ecx /* Save ftrace_pos in 3rd parameter */
pushl %esp /* Save pt_regs as 4th parameter */ pushl %esp /* Save pt_regs as 4th parameter */
@ -170,43 +151,6 @@ GLOBAL(ftrace_regs_call)
lea 3*4(%esp), %esp /* Skip orig_ax, ip and cs */ lea 3*4(%esp), %esp /* Skip orig_ax, ip and cs */
jmp .Lftrace_ret jmp .Lftrace_ret
#else /* ! CONFIG_DYNAMIC_FTRACE */
ENTRY(function_hook)
cmpl $__PAGE_OFFSET, %esp
jb ftrace_stub /* Paging not enabled yet? */
cmpl $ftrace_stub, ftrace_trace_function
jnz .Ltrace
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
cmpl $ftrace_stub, ftrace_graph_return
jnz ftrace_graph_caller
cmpl $ftrace_graph_entry_stub, ftrace_graph_entry
jnz ftrace_graph_caller
#endif
.globl ftrace_stub
ftrace_stub:
ret
/* taken from glibc */
.Ltrace:
pushl %eax
pushl %ecx
pushl %edx
movl 0xc(%esp), %eax
movl 0x4(%ebp), %edx
subl $MCOUNT_INSN_SIZE, %eax
movl ftrace_trace_function, %ecx
CALL_NOSPEC %ecx
popl %edx
popl %ecx
popl %eax
jmp ftrace_stub
END(function_hook)
#endif /* CONFIG_DYNAMIC_FTRACE */
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
ENTRY(ftrace_graph_caller) ENTRY(ftrace_graph_caller)
@ -215,13 +159,8 @@ ENTRY(ftrace_graph_caller)
pushl %edx pushl %edx
movl 3*4(%esp), %eax movl 3*4(%esp), %eax
/* Even with frame pointers, fentry doesn't have one here */ /* Even with frame pointers, fentry doesn't have one here */
#ifdef CC_USING_FENTRY
lea 4*4(%esp), %edx lea 4*4(%esp), %edx
movl $0, %ecx movl $0, %ecx
#else
lea 0x4(%ebp), %edx
movl (%ebp), %ecx
#endif
subl $MCOUNT_INSN_SIZE, %eax subl $MCOUNT_INSN_SIZE, %eax
call prepare_ftrace_return call prepare_ftrace_return
popl %edx popl %edx
@ -234,11 +173,7 @@ END(ftrace_graph_caller)
return_to_handler: return_to_handler:
pushl %eax pushl %eax
pushl %edx pushl %edx
#ifdef CC_USING_FENTRY
movl $0, %eax movl $0, %eax
#else
movl %ebp, %eax
#endif
call ftrace_return_to_handler call ftrace_return_to_handler
movl %eax, %ecx movl %eax, %ecx
popl %edx popl %edx

View File

@ -13,22 +13,12 @@
.code64 .code64
.section .entry.text, "ax" .section .entry.text, "ax"
#ifdef CC_USING_FENTRY
# define function_hook __fentry__ # define function_hook __fentry__
EXPORT_SYMBOL(__fentry__) EXPORT_SYMBOL(__fentry__)
#else
# define function_hook mcount
EXPORT_SYMBOL(mcount)
#endif
#ifdef CONFIG_FRAME_POINTER #ifdef CONFIG_FRAME_POINTER
# ifdef CC_USING_FENTRY
/* Save parent and function stack frames (rip and rbp) */ /* Save parent and function stack frames (rip and rbp) */
# define MCOUNT_FRAME_SIZE (8+16*2) # define MCOUNT_FRAME_SIZE (8+16*2)
# else
/* Save just function stack frame (rip and rbp) */
# define MCOUNT_FRAME_SIZE (8+16)
# endif
#else #else
/* No need to save a stack frame */ /* No need to save a stack frame */
# define MCOUNT_FRAME_SIZE 0 # define MCOUNT_FRAME_SIZE 0
@ -75,17 +65,13 @@ EXPORT_SYMBOL(mcount)
* fentry is called before the stack frame is set up, where as mcount * fentry is called before the stack frame is set up, where as mcount
* is called afterward. * is called afterward.
*/ */
#ifdef CC_USING_FENTRY
/* Save the parent pointer (skip orig rbp and our return address) */ /* Save the parent pointer (skip orig rbp and our return address) */
pushq \added+8*2(%rsp) pushq \added+8*2(%rsp)
pushq %rbp pushq %rbp
movq %rsp, %rbp movq %rsp, %rbp
/* Save the return address (now skip orig rbp, rbp and parent) */ /* Save the return address (now skip orig rbp, rbp and parent) */
pushq \added+8*3(%rsp) pushq \added+8*3(%rsp)
#else
/* Can't assume that rip is before this (unless added was zero) */
pushq \added+8(%rsp)
#endif
pushq %rbp pushq %rbp
movq %rsp, %rbp movq %rsp, %rbp
#endif /* CONFIG_FRAME_POINTER */ #endif /* CONFIG_FRAME_POINTER */
@ -113,12 +99,7 @@ EXPORT_SYMBOL(mcount)
movq %rdx, RBP(%rsp) movq %rdx, RBP(%rsp)
/* Copy the parent address into %rsi (second parameter) */ /* Copy the parent address into %rsi (second parameter) */
#ifdef CC_USING_FENTRY
movq MCOUNT_REG_SIZE+8+\added(%rsp), %rsi movq MCOUNT_REG_SIZE+8+\added(%rsp), %rsi
#else
/* %rdx contains original %rbp */
movq 8(%rdx), %rsi
#endif
/* Move RIP to its proper location */ /* Move RIP to its proper location */
movq MCOUNT_REG_SIZE+\added(%rsp), %rdi movq MCOUNT_REG_SIZE+\added(%rsp), %rdi
@ -303,15 +284,8 @@ ENTRY(ftrace_graph_caller)
/* Saves rbp into %rdx and fills first parameter */ /* Saves rbp into %rdx and fills first parameter */
save_mcount_regs save_mcount_regs
#ifdef CC_USING_FENTRY
leaq MCOUNT_REG_SIZE+8(%rsp), %rsi leaq MCOUNT_REG_SIZE+8(%rsp), %rsi
movq $0, %rdx /* No framepointers needed */ movq $0, %rdx /* No framepointers needed */
#else
/* Save address of the return address of traced function */
leaq 8(%rdx), %rsi
/* ftrace does sanity checks against frame pointers */
movq (%rdx), %rdx
#endif
call prepare_ftrace_return call prepare_ftrace_return
restore_mcount_regs restore_mcount_regs

View File

@ -53,23 +53,24 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val,
* "Define 'is'", Bill Clinton * "Define 'is'", Bill Clinton
* "Define 'if'", Steven Rostedt * "Define 'if'", Steven Rostedt
*/ */
#define if(cond, ...) __trace_if( (cond , ## __VA_ARGS__) ) #define if(cond, ...) if ( __trace_if_var( !!(cond , ## __VA_ARGS__) ) )
#define __trace_if(cond) \
if (__builtin_constant_p(!!(cond)) ? !!(cond) : \ #define __trace_if_var(cond) (__builtin_constant_p(cond) ? (cond) : __trace_if_value(cond))
({ \
int ______r; \ #define __trace_if_value(cond) ({ \
static struct ftrace_branch_data \ static struct ftrace_branch_data \
__aligned(4) \ __aligned(4) \
__section("_ftrace_branch") \ __section("_ftrace_branch") \
______f = { \ __if_trace = { \
.func = __func__, \ .func = __func__, \
.file = __FILE__, \ .file = __FILE__, \
.line = __LINE__, \ .line = __LINE__, \
}; \ }; \
______r = !!(cond); \ (cond) ? \
______r ? ______f.miss_hit[1]++ : ______f.miss_hit[0]++;\ (__if_trace.miss_hit[1]++,1) : \
______r; \ (__if_trace.miss_hit[0]++,0); \
})) })
#endif /* CONFIG_PROFILE_ALL_BRANCHES */ #endif /* CONFIG_PROFILE_ALL_BRANCHES */
#else #else

View File

@ -741,6 +741,8 @@ struct ftrace_graph_ret {
typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *); /* return */ typedef void (*trace_func_graph_ret_t)(struct ftrace_graph_ret *); /* return */
typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *); /* entry */ typedef int (*trace_func_graph_ent_t)(struct ftrace_graph_ent *); /* entry */
extern int ftrace_graph_entry_stub(struct ftrace_graph_ent *trace);
#ifdef CONFIG_FUNCTION_GRAPH_TRACER #ifdef CONFIG_FUNCTION_GRAPH_TRACER
struct fgraph_ops { struct fgraph_ops {

View File

@ -548,4 +548,19 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p)
#define TRACE_EVENT_PERF_PERM(event, expr...) #define TRACE_EVENT_PERF_PERM(event, expr...)
#define DECLARE_EVENT_NOP(name, proto, args) \
static inline void trace_##name(proto) \
{ } \
static inline bool trace_##name##_enabled(void) \
{ \
return false; \
}
#define TRACE_EVENT_NOP(name, proto, args, struct, assign, print) \
DECLARE_EVENT_NOP(name, PARAMS(proto), PARAMS(args))
#define DECLARE_EVENT_CLASS_NOP(name, proto, args, tstruct, assign, print)
#define DEFINE_EVENT_NOP(template, name, proto, args) \
DECLARE_EVENT_NOP(name, PARAMS(proto), PARAMS(args))
#endif /* ifdef TRACE_EVENT (see note above) */ #endif /* ifdef TRACE_EVENT (see note above) */

View File

@ -46,6 +46,12 @@
assign, print, reg, unreg) \ assign, print, reg, unreg) \
DEFINE_TRACE_FN(name, reg, unreg) DEFINE_TRACE_FN(name, reg, unreg)
#undef TRACE_EVENT_NOP
#define TRACE_EVENT_NOP(name, proto, args, struct, assign, print)
#undef DEFINE_EVENT_NOP
#define DEFINE_EVENT_NOP(template, name, proto, args)
#undef DEFINE_EVENT #undef DEFINE_EVENT
#define DEFINE_EVENT(template, name, proto, args) \ #define DEFINE_EVENT(template, name, proto, args) \
DEFINE_TRACE(name) DEFINE_TRACE(name)
@ -102,6 +108,8 @@
#undef TRACE_EVENT_FN #undef TRACE_EVENT_FN
#undef TRACE_EVENT_FN_COND #undef TRACE_EVENT_FN_COND
#undef TRACE_EVENT_CONDITION #undef TRACE_EVENT_CONDITION
#undef TRACE_EVENT_NOP
#undef DEFINE_EVENT_NOP
#undef DECLARE_EVENT_CLASS #undef DECLARE_EVENT_CLASS
#undef DEFINE_EVENT #undef DEFINE_EVENT
#undef DEFINE_EVENT_FN #undef DEFINE_EVENT_FN

View File

@ -7,6 +7,12 @@
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
#ifdef CONFIG_RCU_TRACE
#define TRACE_EVENT_RCU TRACE_EVENT
#else
#define TRACE_EVENT_RCU TRACE_EVENT_NOP
#endif
/* /*
* Tracepoint for start/end markers used for utilization calculations. * Tracepoint for start/end markers used for utilization calculations.
* By convention, the string is of the following forms: * By convention, the string is of the following forms:
@ -35,8 +41,6 @@ TRACE_EVENT(rcu_utilization,
TP_printk("%s", __entry->s) TP_printk("%s", __entry->s)
); );
#ifdef CONFIG_RCU_TRACE
#if defined(CONFIG_TREE_RCU) || defined(CONFIG_PREEMPT_RCU) #if defined(CONFIG_TREE_RCU) || defined(CONFIG_PREEMPT_RCU)
/* /*
@ -62,7 +66,7 @@ TRACE_EVENT(rcu_utilization,
* "end": End a grace period. * "end": End a grace period.
* "cpuend": CPU first notices a grace-period end. * "cpuend": CPU first notices a grace-period end.
*/ */
TRACE_EVENT(rcu_grace_period, TRACE_EVENT_RCU(rcu_grace_period,
TP_PROTO(const char *rcuname, unsigned long gp_seq, const char *gpevent), TP_PROTO(const char *rcuname, unsigned long gp_seq, const char *gpevent),
@ -101,7 +105,7 @@ TRACE_EVENT(rcu_grace_period,
* "Cleanup": Clean up rcu_node structure after previous GP. * "Cleanup": Clean up rcu_node structure after previous GP.
* "CleanupMore": Clean up, and another GP is needed. * "CleanupMore": Clean up, and another GP is needed.
*/ */
TRACE_EVENT(rcu_future_grace_period, TRACE_EVENT_RCU(rcu_future_grace_period,
TP_PROTO(const char *rcuname, unsigned long gp_seq, TP_PROTO(const char *rcuname, unsigned long gp_seq,
unsigned long gp_seq_req, u8 level, int grplo, int grphi, unsigned long gp_seq_req, u8 level, int grplo, int grphi,
@ -141,7 +145,7 @@ TRACE_EVENT(rcu_future_grace_period,
* rcu_node structure, and the mask of CPUs that will be waited for. * rcu_node structure, and the mask of CPUs that will be waited for.
* All but the type of RCU are extracted from the rcu_node structure. * All but the type of RCU are extracted from the rcu_node structure.
*/ */
TRACE_EVENT(rcu_grace_period_init, TRACE_EVENT_RCU(rcu_grace_period_init,
TP_PROTO(const char *rcuname, unsigned long gp_seq, u8 level, TP_PROTO(const char *rcuname, unsigned long gp_seq, u8 level,
int grplo, int grphi, unsigned long qsmask), int grplo, int grphi, unsigned long qsmask),
@ -186,7 +190,7 @@ TRACE_EVENT(rcu_grace_period_init,
* "endwake": Woke piggybackers up. * "endwake": Woke piggybackers up.
* "done": Someone else did the expedited grace period for us. * "done": Someone else did the expedited grace period for us.
*/ */
TRACE_EVENT(rcu_exp_grace_period, TRACE_EVENT_RCU(rcu_exp_grace_period,
TP_PROTO(const char *rcuname, unsigned long gpseq, const char *gpevent), TP_PROTO(const char *rcuname, unsigned long gpseq, const char *gpevent),
@ -218,7 +222,7 @@ TRACE_EVENT(rcu_exp_grace_period,
* "nxtlvl": Advance to next level of rcu_node funnel * "nxtlvl": Advance to next level of rcu_node funnel
* "wait": Wait for someone else to do expedited GP * "wait": Wait for someone else to do expedited GP
*/ */
TRACE_EVENT(rcu_exp_funnel_lock, TRACE_EVENT_RCU(rcu_exp_funnel_lock,
TP_PROTO(const char *rcuname, u8 level, int grplo, int grphi, TP_PROTO(const char *rcuname, u8 level, int grplo, int grphi,
const char *gpevent), const char *gpevent),
@ -269,7 +273,7 @@ TRACE_EVENT(rcu_exp_funnel_lock,
* "WaitQueue": Enqueue partially done, timed wait for it to complete. * "WaitQueue": Enqueue partially done, timed wait for it to complete.
* "WokeQueue": Partial enqueue now complete. * "WokeQueue": Partial enqueue now complete.
*/ */
TRACE_EVENT(rcu_nocb_wake, TRACE_EVENT_RCU(rcu_nocb_wake,
TP_PROTO(const char *rcuname, int cpu, const char *reason), TP_PROTO(const char *rcuname, int cpu, const char *reason),
@ -297,7 +301,7 @@ TRACE_EVENT(rcu_nocb_wake,
* include SRCU), the grace-period number that the task is blocking * include SRCU), the grace-period number that the task is blocking
* (the current or the next), and the task's PID. * (the current or the next), and the task's PID.
*/ */
TRACE_EVENT(rcu_preempt_task, TRACE_EVENT_RCU(rcu_preempt_task,
TP_PROTO(const char *rcuname, int pid, unsigned long gp_seq), TP_PROTO(const char *rcuname, int pid, unsigned long gp_seq),
@ -324,7 +328,7 @@ TRACE_EVENT(rcu_preempt_task,
* read-side critical section exiting that critical section. Track the * read-side critical section exiting that critical section. Track the
* type of RCU (which one day might include SRCU) and the task's PID. * type of RCU (which one day might include SRCU) and the task's PID.
*/ */
TRACE_EVENT(rcu_unlock_preempted_task, TRACE_EVENT_RCU(rcu_unlock_preempted_task,
TP_PROTO(const char *rcuname, unsigned long gp_seq, int pid), TP_PROTO(const char *rcuname, unsigned long gp_seq, int pid),
@ -353,7 +357,7 @@ TRACE_EVENT(rcu_unlock_preempted_task,
* whether there are any blocked tasks blocking the current grace period. * whether there are any blocked tasks blocking the current grace period.
* All but the type of RCU are extracted from the rcu_node structure. * All but the type of RCU are extracted from the rcu_node structure.
*/ */
TRACE_EVENT(rcu_quiescent_state_report, TRACE_EVENT_RCU(rcu_quiescent_state_report,
TP_PROTO(const char *rcuname, unsigned long gp_seq, TP_PROTO(const char *rcuname, unsigned long gp_seq,
unsigned long mask, unsigned long qsmask, unsigned long mask, unsigned long qsmask,
@ -396,7 +400,7 @@ TRACE_EVENT(rcu_quiescent_state_report,
* state, which can be "dti" for dyntick-idle mode or "kick" when kicking * state, which can be "dti" for dyntick-idle mode or "kick" when kicking
* a CPU that has been in dyntick-idle mode for too long. * a CPU that has been in dyntick-idle mode for too long.
*/ */
TRACE_EVENT(rcu_fqs, TRACE_EVENT_RCU(rcu_fqs,
TP_PROTO(const char *rcuname, unsigned long gp_seq, int cpu, const char *qsevent), TP_PROTO(const char *rcuname, unsigned long gp_seq, int cpu, const char *qsevent),
@ -436,7 +440,7 @@ TRACE_EVENT(rcu_fqs,
* events use two separate counters, and that the "++=" and "--=" events * events use two separate counters, and that the "++=" and "--=" events
* for irq/NMI will change the counter by two, otherwise by one. * for irq/NMI will change the counter by two, otherwise by one.
*/ */
TRACE_EVENT(rcu_dyntick, TRACE_EVENT_RCU(rcu_dyntick,
TP_PROTO(const char *polarity, long oldnesting, long newnesting, atomic_t dynticks), TP_PROTO(const char *polarity, long oldnesting, long newnesting, atomic_t dynticks),
@ -468,7 +472,7 @@ TRACE_EVENT(rcu_dyntick,
* number of lazy callbacks queued, and the fourth element is the * number of lazy callbacks queued, and the fourth element is the
* total number of callbacks queued. * total number of callbacks queued.
*/ */
TRACE_EVENT(rcu_callback, TRACE_EVENT_RCU(rcu_callback,
TP_PROTO(const char *rcuname, struct rcu_head *rhp, long qlen_lazy, TP_PROTO(const char *rcuname, struct rcu_head *rhp, long qlen_lazy,
long qlen), long qlen),
@ -504,7 +508,7 @@ TRACE_EVENT(rcu_callback,
* the fourth argument is the number of lazy callbacks queued, and the * the fourth argument is the number of lazy callbacks queued, and the
* fifth argument is the total number of callbacks queued. * fifth argument is the total number of callbacks queued.
*/ */
TRACE_EVENT(rcu_kfree_callback, TRACE_EVENT_RCU(rcu_kfree_callback,
TP_PROTO(const char *rcuname, struct rcu_head *rhp, unsigned long offset, TP_PROTO(const char *rcuname, struct rcu_head *rhp, unsigned long offset,
long qlen_lazy, long qlen), long qlen_lazy, long qlen),
@ -539,7 +543,7 @@ TRACE_EVENT(rcu_kfree_callback,
* the total number of callbacks queued, and the fourth argument is * the total number of callbacks queued, and the fourth argument is
* the current RCU-callback batch limit. * the current RCU-callback batch limit.
*/ */
TRACE_EVENT(rcu_batch_start, TRACE_EVENT_RCU(rcu_batch_start,
TP_PROTO(const char *rcuname, long qlen_lazy, long qlen, long blimit), TP_PROTO(const char *rcuname, long qlen_lazy, long qlen, long blimit),
@ -569,7 +573,7 @@ TRACE_EVENT(rcu_batch_start,
* The first argument is the type of RCU, and the second argument is * The first argument is the type of RCU, and the second argument is
* a pointer to the RCU callback itself. * a pointer to the RCU callback itself.
*/ */
TRACE_EVENT(rcu_invoke_callback, TRACE_EVENT_RCU(rcu_invoke_callback,
TP_PROTO(const char *rcuname, struct rcu_head *rhp), TP_PROTO(const char *rcuname, struct rcu_head *rhp),
@ -598,7 +602,7 @@ TRACE_EVENT(rcu_invoke_callback,
* is the offset of the callback within the enclosing RCU-protected * is the offset of the callback within the enclosing RCU-protected
* data structure. * data structure.
*/ */
TRACE_EVENT(rcu_invoke_kfree_callback, TRACE_EVENT_RCU(rcu_invoke_kfree_callback,
TP_PROTO(const char *rcuname, struct rcu_head *rhp, unsigned long offset), TP_PROTO(const char *rcuname, struct rcu_head *rhp, unsigned long offset),
@ -631,7 +635,7 @@ TRACE_EVENT(rcu_invoke_kfree_callback,
* and the sixth argument (risk) is the return value from * and the sixth argument (risk) is the return value from
* rcu_is_callbacks_kthread(). * rcu_is_callbacks_kthread().
*/ */
TRACE_EVENT(rcu_batch_end, TRACE_EVENT_RCU(rcu_batch_end,
TP_PROTO(const char *rcuname, int callbacks_invoked, TP_PROTO(const char *rcuname, int callbacks_invoked,
char cb, char nr, char iit, char risk), char cb, char nr, char iit, char risk),
@ -673,7 +677,7 @@ TRACE_EVENT(rcu_batch_end,
* callback address can be NULL. * callback address can be NULL.
*/ */
#define RCUTORTURENAME_LEN 8 #define RCUTORTURENAME_LEN 8
TRACE_EVENT(rcu_torture_read, TRACE_EVENT_RCU(rcu_torture_read,
TP_PROTO(const char *rcutorturename, struct rcu_head *rhp, TP_PROTO(const char *rcutorturename, struct rcu_head *rhp,
unsigned long secs, unsigned long c_old, unsigned long c), unsigned long secs, unsigned long c_old, unsigned long c),
@ -721,7 +725,7 @@ TRACE_EVENT(rcu_torture_read,
* The "cpu" argument is the CPU or -1 if meaningless, the "cnt" argument * The "cpu" argument is the CPU or -1 if meaningless, the "cnt" argument
* is the count of remaining callbacks, and "done" is the piggybacking count. * is the count of remaining callbacks, and "done" is the piggybacking count.
*/ */
TRACE_EVENT(rcu_barrier, TRACE_EVENT_RCU(rcu_barrier,
TP_PROTO(const char *rcuname, const char *s, int cpu, int cnt, unsigned long done), TP_PROTO(const char *rcuname, const char *s, int cpu, int cnt, unsigned long done),
@ -748,41 +752,6 @@ TRACE_EVENT(rcu_barrier,
__entry->done) __entry->done)
); );
#else /* #ifdef CONFIG_RCU_TRACE */
#define trace_rcu_grace_period(rcuname, gp_seq, gpevent) do { } while (0)
#define trace_rcu_future_grace_period(rcuname, gp_seq, gp_seq_req, \
level, grplo, grphi, event) \
do { } while (0)
#define trace_rcu_grace_period_init(rcuname, gp_seq, level, grplo, grphi, \
qsmask) do { } while (0)
#define trace_rcu_exp_grace_period(rcuname, gqseq, gpevent) \
do { } while (0)
#define trace_rcu_exp_funnel_lock(rcuname, level, grplo, grphi, gpevent) \
do { } while (0)
#define trace_rcu_nocb_wake(rcuname, cpu, reason) do { } while (0)
#define trace_rcu_preempt_task(rcuname, pid, gp_seq) do { } while (0)
#define trace_rcu_unlock_preempted_task(rcuname, gp_seq, pid) do { } while (0)
#define trace_rcu_quiescent_state_report(rcuname, gp_seq, mask, qsmask, level, \
grplo, grphi, gp_tasks) do { } \
while (0)
#define trace_rcu_fqs(rcuname, gp_seq, cpu, qsevent) do { } while (0)
#define trace_rcu_dyntick(polarity, oldnesting, newnesting, dyntick) do { } while (0)
#define trace_rcu_callback(rcuname, rhp, qlen_lazy, qlen) do { } while (0)
#define trace_rcu_kfree_callback(rcuname, rhp, offset, qlen_lazy, qlen) \
do { } while (0)
#define trace_rcu_batch_start(rcuname, qlen_lazy, qlen, blimit) \
do { } while (0)
#define trace_rcu_invoke_callback(rcuname, rhp) do { } while (0)
#define trace_rcu_invoke_kfree_callback(rcuname, rhp, offset) do { } while (0)
#define trace_rcu_batch_end(rcuname, callbacks_invoked, cb, nr, iit, risk) \
do { } while (0)
#define trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c) \
do { } while (0)
#define trace_rcu_barrier(name, s, cpu, cnt, done) do { } while (0)
#endif /* #else #ifdef CONFIG_RCU_TRACE */
#endif /* _TRACE_RCU_H */ #endif /* _TRACE_RCU_H */
/* This part must be outside protection */ /* This part must be outside protection */

View File

@ -241,7 +241,6 @@ DECLARE_EVENT_CLASS(sched_process_template,
DEFINE_EVENT(sched_process_template, sched_process_free, DEFINE_EVENT(sched_process_template, sched_process_free,
TP_PROTO(struct task_struct *p), TP_PROTO(struct task_struct *p),
TP_ARGS(p)); TP_ARGS(p));
/* /*
* Tracepoint for a task exiting: * Tracepoint for a task exiting:
@ -336,11 +335,20 @@ TRACE_EVENT(sched_process_exec,
__entry->pid, __entry->old_pid) __entry->pid, __entry->old_pid)
); );
#ifdef CONFIG_SCHEDSTATS
#define DEFINE_EVENT_SCHEDSTAT DEFINE_EVENT
#define DECLARE_EVENT_CLASS_SCHEDSTAT DECLARE_EVENT_CLASS
#else
#define DEFINE_EVENT_SCHEDSTAT DEFINE_EVENT_NOP
#define DECLARE_EVENT_CLASS_SCHEDSTAT DECLARE_EVENT_CLASS_NOP
#endif
/* /*
* XXX the below sched_stat tracepoints only apply to SCHED_OTHER/BATCH/IDLE * XXX the below sched_stat tracepoints only apply to SCHED_OTHER/BATCH/IDLE
* adding sched_stat support to SCHED_FIFO/RR would be welcome. * adding sched_stat support to SCHED_FIFO/RR would be welcome.
*/ */
DECLARE_EVENT_CLASS(sched_stat_template, DECLARE_EVENT_CLASS_SCHEDSTAT(sched_stat_template,
TP_PROTO(struct task_struct *tsk, u64 delay), TP_PROTO(struct task_struct *tsk, u64 delay),
@ -363,12 +371,11 @@ DECLARE_EVENT_CLASS(sched_stat_template,
(unsigned long long)__entry->delay) (unsigned long long)__entry->delay)
); );
/* /*
* Tracepoint for accounting wait time (time the task is runnable * Tracepoint for accounting wait time (time the task is runnable
* but not actually running due to scheduler contention). * but not actually running due to scheduler contention).
*/ */
DEFINE_EVENT(sched_stat_template, sched_stat_wait, DEFINE_EVENT_SCHEDSTAT(sched_stat_template, sched_stat_wait,
TP_PROTO(struct task_struct *tsk, u64 delay), TP_PROTO(struct task_struct *tsk, u64 delay),
TP_ARGS(tsk, delay)); TP_ARGS(tsk, delay));
@ -376,7 +383,7 @@ DEFINE_EVENT(sched_stat_template, sched_stat_wait,
* Tracepoint for accounting sleep time (time the task is not runnable, * Tracepoint for accounting sleep time (time the task is not runnable,
* including iowait, see below). * including iowait, see below).
*/ */
DEFINE_EVENT(sched_stat_template, sched_stat_sleep, DEFINE_EVENT_SCHEDSTAT(sched_stat_template, sched_stat_sleep,
TP_PROTO(struct task_struct *tsk, u64 delay), TP_PROTO(struct task_struct *tsk, u64 delay),
TP_ARGS(tsk, delay)); TP_ARGS(tsk, delay));
@ -384,14 +391,14 @@ DEFINE_EVENT(sched_stat_template, sched_stat_sleep,
* Tracepoint for accounting iowait time (time the task is not runnable * Tracepoint for accounting iowait time (time the task is not runnable
* due to waiting on IO to complete). * due to waiting on IO to complete).
*/ */
DEFINE_EVENT(sched_stat_template, sched_stat_iowait, DEFINE_EVENT_SCHEDSTAT(sched_stat_template, sched_stat_iowait,
TP_PROTO(struct task_struct *tsk, u64 delay), TP_PROTO(struct task_struct *tsk, u64 delay),
TP_ARGS(tsk, delay)); TP_ARGS(tsk, delay));
/* /*
* Tracepoint for accounting blocked time (time the task is in uninterruptible). * Tracepoint for accounting blocked time (time the task is in uninterruptible).
*/ */
DEFINE_EVENT(sched_stat_template, sched_stat_blocked, DEFINE_EVENT_SCHEDSTAT(sched_stat_template, sched_stat_blocked,
TP_PROTO(struct task_struct *tsk, u64 delay), TP_PROTO(struct task_struct *tsk, u64 delay),
TP_ARGS(tsk, delay)); TP_ARGS(tsk, delay));

View File

@ -1208,14 +1208,6 @@ void klp_module_going(struct module *mod)
static int __init klp_init(void) static int __init klp_init(void)
{ {
int ret;
ret = klp_check_compiler_support();
if (ret) {
pr_info("Your compiler is too old; turning off.\n");
return -EINVAL;
}
klp_root_kobj = kobject_create_and_add("livepatch", kernel_kobj); klp_root_kobj = kobject_create_and_add("livepatch", kernel_kobj);
if (!klp_root_kobj) if (!klp_root_kobj)
return -ENOMEM; return -ENOMEM;

View File

@ -11,11 +11,6 @@
#define __LINUX_RCU_H #define __LINUX_RCU_H
#include <trace/events/rcu.h> #include <trace/events/rcu.h>
#ifdef CONFIG_RCU_TRACE
#define RCU_TRACE(stmt) stmt
#else /* #ifdef CONFIG_RCU_TRACE */
#define RCU_TRACE(stmt)
#endif /* #else #ifdef CONFIG_RCU_TRACE */
/* Offset to allow distinguishing irq vs. task-based idle entry/exit. */ /* Offset to allow distinguishing irq vs. task-based idle entry/exit. */
#define DYNTICK_IRQ_NONIDLE ((LONG_MAX / 2) + 1) #define DYNTICK_IRQ_NONIDLE ((LONG_MAX / 2) + 1)
@ -216,12 +211,12 @@ static inline bool __rcu_reclaim(const char *rn, struct rcu_head *head)
rcu_lock_acquire(&rcu_callback_map); rcu_lock_acquire(&rcu_callback_map);
if (__is_kfree_rcu_offset(offset)) { if (__is_kfree_rcu_offset(offset)) {
RCU_TRACE(trace_rcu_invoke_kfree_callback(rn, head, offset);) trace_rcu_invoke_kfree_callback(rn, head, offset);
kfree((void *)head - offset); kfree((void *)head - offset);
rcu_lock_release(&rcu_callback_map); rcu_lock_release(&rcu_callback_map);
return true; return true;
} else { } else {
RCU_TRACE(trace_rcu_invoke_callback(rn, head);) trace_rcu_invoke_callback(rn, head);
f = head->func; f = head->func;
WRITE_ONCE(head->func, (rcu_callback_t)0L); WRITE_ONCE(head->func, (rcu_callback_t)0L);
f(head); f(head);

View File

@ -1969,14 +1969,14 @@ rcu_check_quiescent_state(struct rcu_data *rdp)
*/ */
int rcutree_dying_cpu(unsigned int cpu) int rcutree_dying_cpu(unsigned int cpu)
{ {
RCU_TRACE(bool blkd;) bool blkd;
RCU_TRACE(struct rcu_data *rdp = this_cpu_ptr(&rcu_data);) struct rcu_data *rdp = this_cpu_ptr(&rcu_data);
RCU_TRACE(struct rcu_node *rnp = rdp->mynode;) struct rcu_node *rnp = rdp->mynode;
if (!IS_ENABLED(CONFIG_HOTPLUG_CPU)) if (!IS_ENABLED(CONFIG_HOTPLUG_CPU))
return 0; return 0;
RCU_TRACE(blkd = !!(rnp->qsmask & rdp->grpmask);) blkd = !!(rnp->qsmask & rdp->grpmask);
trace_rcu_grace_period(rcu_state.name, rnp->gp_seq, trace_rcu_grace_period(rcu_state.name, rnp->gp_seq,
blkd ? TPS("cpuofl") : TPS("cpuofl-bgp")); blkd ? TPS("cpuofl") : TPS("cpuofl-bgp"));
return 0; return 0;

View File

@ -70,12 +70,8 @@
#define INIT_OPS_HASH(opsname) \ #define INIT_OPS_HASH(opsname) \
.func_hash = &opsname.local_hash, \ .func_hash = &opsname.local_hash, \
.local_hash.regex_lock = __MUTEX_INITIALIZER(opsname.local_hash.regex_lock), .local_hash.regex_lock = __MUTEX_INITIALIZER(opsname.local_hash.regex_lock),
#define ASSIGN_OPS_HASH(opsname, val) \
.func_hash = val, \
.local_hash.regex_lock = __MUTEX_INITIALIZER(opsname.local_hash.regex_lock),
#else #else
#define INIT_OPS_HASH(opsname) #define INIT_OPS_HASH(opsname)
#define ASSIGN_OPS_HASH(opsname, val)
#endif #endif
enum { enum {
@ -3880,7 +3876,7 @@ static int ftrace_hash_move_and_update_ops(struct ftrace_ops *ops,
static bool module_exists(const char *module) static bool module_exists(const char *module)
{ {
/* All modules have the symbol __this_module */ /* All modules have the symbol __this_module */
const char this_mod[] = "__this_module"; static const char this_mod[] = "__this_module";
char modname[MAX_PARAM_PREFIX_LEN + sizeof(this_mod) + 2]; char modname[MAX_PARAM_PREFIX_LEN + sizeof(this_mod) + 2];
unsigned long val; unsigned long val;
int n; int n;
@ -6265,6 +6261,9 @@ __ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
preempt_disable_notrace(); preempt_disable_notrace();
do_for_each_ftrace_op(op, ftrace_ops_list) { do_for_each_ftrace_op(op, ftrace_ops_list) {
/* Stub functions don't need to be called nor tested */
if (op->flags & FTRACE_OPS_FL_STUB)
continue;
/* /*
* Check the following for each ops before calling their func: * Check the following for each ops before calling their func:
* if RCU flag is set, then rcu_is_watching() must be true * if RCU flag is set, then rcu_is_watching() must be true

View File

@ -4979,7 +4979,7 @@ static __init int rb_write_something(struct rb_test_data *data, bool nested)
cnt = data->cnt + (nested ? 27 : 0); cnt = data->cnt + (nested ? 27 : 0);
/* Multiply cnt by ~e, to make some unique increment */ /* Multiply cnt by ~e, to make some unique increment */
size = (data->cnt * 68 / 25) % (sizeof(rb_string) - 1); size = (cnt * 68 / 25) % (sizeof(rb_string) - 1);
len = size + sizeof(struct rb_item); len = size + sizeof(struct rb_item);

View File

@ -362,7 +362,7 @@ static void ring_buffer_producer(void)
hit--; /* make it non zero */ hit--; /* make it non zero */
} }
/* Caculate the average time in nanosecs */ /* Calculate the average time in nanosecs */
avg = NSEC_PER_MSEC / (hit + missed); avg = NSEC_PER_MSEC / (hit + missed);
trace_printk("%ld ns per entry\n", avg); trace_printk("%ld ns per entry\n", avg);
} }

View File

@ -1727,6 +1727,10 @@ static __init int init_trace_selftests(void)
pr_info("Running postponed tracer tests:\n"); pr_info("Running postponed tracer tests:\n");
list_for_each_entry_safe(p, n, &postponed_selftests, list) { list_for_each_entry_safe(p, n, &postponed_selftests, list) {
/* This loop can take minutes when sanitizers are enabled, so
* lets make sure we allow RCU processing.
*/
cond_resched();
ret = run_tracer_selftest(p->type); ret = run_tracer_selftest(p->type);
/* If the test fails, then warn and remove from available_tracers */ /* If the test fails, then warn and remove from available_tracers */
if (ret < 0) { if (ret < 0) {
@ -3045,6 +3049,7 @@ void trace_printk_init_buffers(void)
if (global_trace.trace_buffer.buffer) if (global_trace.trace_buffer.buffer)
tracing_start_cmdline_record(); tracing_start_cmdline_record();
} }
EXPORT_SYMBOL_GPL(trace_printk_init_buffers);
void trace_printk_start_comm(void) void trace_printk_start_comm(void)
{ {
@ -3205,6 +3210,7 @@ int trace_array_printk(struct trace_array *tr,
va_end(ap); va_end(ap);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(trace_array_printk);
__printf(3, 4) __printf(3, 4)
int trace_array_printk_buf(struct ring_buffer *buffer, int trace_array_printk_buf(struct ring_buffer *buffer,
@ -3482,34 +3488,69 @@ static void s_stop(struct seq_file *m, void *p)
trace_event_read_unlock(); trace_event_read_unlock();
} }
static void
get_total_entries_cpu(struct trace_buffer *buf, unsigned long *total,
unsigned long *entries, int cpu)
{
unsigned long count;
count = ring_buffer_entries_cpu(buf->buffer, cpu);
/*
* If this buffer has skipped entries, then we hold all
* entries for the trace and we need to ignore the
* ones before the time stamp.
*/
if (per_cpu_ptr(buf->data, cpu)->skipped_entries) {
count -= per_cpu_ptr(buf->data, cpu)->skipped_entries;
/* total is the same as the entries */
*total = count;
} else
*total = count +
ring_buffer_overrun_cpu(buf->buffer, cpu);
*entries = count;
}
static void static void
get_total_entries(struct trace_buffer *buf, get_total_entries(struct trace_buffer *buf,
unsigned long *total, unsigned long *entries) unsigned long *total, unsigned long *entries)
{ {
unsigned long count; unsigned long t, e;
int cpu; int cpu;
*total = 0; *total = 0;
*entries = 0; *entries = 0;
for_each_tracing_cpu(cpu) { for_each_tracing_cpu(cpu) {
count = ring_buffer_entries_cpu(buf->buffer, cpu); get_total_entries_cpu(buf, &t, &e, cpu);
/* *total += t;
* If this buffer has skipped entries, then we hold all *entries += e;
* entries for the trace and we need to ignore the
* ones before the time stamp.
*/
if (per_cpu_ptr(buf->data, cpu)->skipped_entries) {
count -= per_cpu_ptr(buf->data, cpu)->skipped_entries;
/* total is the same as the entries */
*total += count;
} else
*total += count +
ring_buffer_overrun_cpu(buf->buffer, cpu);
*entries += count;
} }
} }
unsigned long trace_total_entries_cpu(struct trace_array *tr, int cpu)
{
unsigned long total, entries;
if (!tr)
tr = &global_trace;
get_total_entries_cpu(&tr->trace_buffer, &total, &entries, cpu);
return entries;
}
unsigned long trace_total_entries(struct trace_array *tr)
{
unsigned long total, entries;
if (!tr)
tr = &global_trace;
get_total_entries(&tr->trace_buffer, &total, &entries);
return entries;
}
static void print_lat_help_header(struct seq_file *m) static void print_lat_help_header(struct seq_file *m)
{ {
seq_puts(m, "# _------=> CPU# \n" seq_puts(m, "# _------=> CPU# \n"
@ -3548,25 +3589,18 @@ static void print_func_help_header_irq(struct trace_buffer *buf, struct seq_file
unsigned int flags) unsigned int flags)
{ {
bool tgid = flags & TRACE_ITER_RECORD_TGID; bool tgid = flags & TRACE_ITER_RECORD_TGID;
const char tgid_space[] = " "; const char *space = " ";
const char space[] = " "; int prec = tgid ? 10 : 2;
print_event_info(buf, m); print_event_info(buf, m);
seq_printf(m, "# %s _-----=> irqs-off\n", seq_printf(m, "# %.*s _-----=> irqs-off\n", prec, space);
tgid ? tgid_space : space); seq_printf(m, "# %.*s / _----=> need-resched\n", prec, space);
seq_printf(m, "# %s / _----=> need-resched\n", seq_printf(m, "# %.*s| / _---=> hardirq/softirq\n", prec, space);
tgid ? tgid_space : space); seq_printf(m, "# %.*s|| / _--=> preempt-depth\n", prec, space);
seq_printf(m, "# %s| / _---=> hardirq/softirq\n", seq_printf(m, "# %.*s||| / delay\n", prec, space);
tgid ? tgid_space : space); seq_printf(m, "# TASK-PID %.*sCPU# |||| TIMESTAMP FUNCTION\n", prec, " TGID ");
seq_printf(m, "# %s|| / _--=> preempt-depth\n", seq_printf(m, "# | | %.*s | |||| | |\n", prec, " | ");
tgid ? tgid_space : space);
seq_printf(m, "# %s||| / delay\n",
tgid ? tgid_space : space);
seq_printf(m, "# TASK-PID %sCPU# |||| TIMESTAMP FUNCTION\n",
tgid ? " TGID " : space);
seq_printf(m, "# | | %s | |||| | |\n",
tgid ? " | " : space);
} }
void void
@ -4692,6 +4726,7 @@ static const char readme_msg[] =
" trace_pipe\t\t- A consuming read to see the contents of the buffer\n" " trace_pipe\t\t- A consuming read to see the contents of the buffer\n"
" current_tracer\t- function and latency tracers\n" " current_tracer\t- function and latency tracers\n"
" available_tracers\t- list of configured tracers for current_tracer\n" " available_tracers\t- list of configured tracers for current_tracer\n"
" error_log\t- error log for failed commands (that support it)\n"
" buffer_size_kb\t- view and modify size of per cpu buffer\n" " buffer_size_kb\t- view and modify size of per cpu buffer\n"
" buffer_total_size_kb - view total size of all cpu buffers\n\n" " buffer_total_size_kb - view total size of all cpu buffers\n\n"
" trace_clock\t\t-change the clock used to order events\n" " trace_clock\t\t-change the clock used to order events\n"
@ -4712,7 +4747,7 @@ static const char readme_msg[] =
" instances\t\t- Make sub-buffers with: mkdir instances/foo\n" " instances\t\t- Make sub-buffers with: mkdir instances/foo\n"
"\t\t\t Remove sub-buffer with rmdir\n" "\t\t\t Remove sub-buffer with rmdir\n"
" trace_options\t\t- Set format or modify how tracing happens\n" " trace_options\t\t- Set format or modify how tracing happens\n"
"\t\t\t Disable an option by adding a suffix 'no' to the\n" "\t\t\t Disable an option by prefixing 'no' to the\n"
"\t\t\t option name\n" "\t\t\t option name\n"
" saved_cmdlines_size\t- echo command number in here to store comm-pid list\n" " saved_cmdlines_size\t- echo command number in here to store comm-pid list\n"
#ifdef CONFIG_DYNAMIC_FTRACE #ifdef CONFIG_DYNAMIC_FTRACE
@ -6296,13 +6331,13 @@ tracing_mark_write(struct file *filp, const char __user *ubuf,
struct ring_buffer *buffer; struct ring_buffer *buffer;
struct print_entry *entry; struct print_entry *entry;
unsigned long irq_flags; unsigned long irq_flags;
const char faulted[] = "<faulted>";
ssize_t written; ssize_t written;
int size; int size;
int len; int len;
/* Used in tracing_mark_raw_write() as well */ /* Used in tracing_mark_raw_write() as well */
#define FAULTED_SIZE (sizeof(faulted) - 1) /* '\0' is already accounted for */ #define FAULTED_STR "<faulted>"
#define FAULTED_SIZE (sizeof(FAULTED_STR) - 1) /* '\0' is already accounted for */
if (tracing_disabled) if (tracing_disabled)
return -EINVAL; return -EINVAL;
@ -6334,7 +6369,7 @@ tracing_mark_write(struct file *filp, const char __user *ubuf,
len = __copy_from_user_inatomic(&entry->buf, ubuf, cnt); len = __copy_from_user_inatomic(&entry->buf, ubuf, cnt);
if (len) { if (len) {
memcpy(&entry->buf, faulted, FAULTED_SIZE); memcpy(&entry->buf, FAULTED_STR, FAULTED_SIZE);
cnt = FAULTED_SIZE; cnt = FAULTED_SIZE;
written = -EFAULT; written = -EFAULT;
} else } else
@ -6375,7 +6410,6 @@ tracing_mark_raw_write(struct file *filp, const char __user *ubuf,
struct ring_buffer_event *event; struct ring_buffer_event *event;
struct ring_buffer *buffer; struct ring_buffer *buffer;
struct raw_data_entry *entry; struct raw_data_entry *entry;
const char faulted[] = "<faulted>";
unsigned long irq_flags; unsigned long irq_flags;
ssize_t written; ssize_t written;
int size; int size;
@ -6415,7 +6449,7 @@ tracing_mark_raw_write(struct file *filp, const char __user *ubuf,
len = __copy_from_user_inatomic(&entry->id, ubuf, cnt); len = __copy_from_user_inatomic(&entry->id, ubuf, cnt);
if (len) { if (len) {
entry->id = -1; entry->id = -1;
memcpy(&entry->buf, faulted, FAULTED_SIZE); memcpy(&entry->buf, FAULTED_STR, FAULTED_SIZE);
written = -EFAULT; written = -EFAULT;
} else } else
written = cnt; written = cnt;
@ -6868,6 +6902,238 @@ static const struct file_operations snapshot_raw_fops = {
#endif /* CONFIG_TRACER_SNAPSHOT */ #endif /* CONFIG_TRACER_SNAPSHOT */
#define TRACING_LOG_ERRS_MAX 8
#define TRACING_LOG_LOC_MAX 128
#define CMD_PREFIX " Command: "
struct err_info {
const char **errs; /* ptr to loc-specific array of err strings */
u8 type; /* index into errs -> specific err string */
u8 pos; /* MAX_FILTER_STR_VAL = 256 */
u64 ts;
};
struct tracing_log_err {
struct list_head list;
struct err_info info;
char loc[TRACING_LOG_LOC_MAX]; /* err location */
char cmd[MAX_FILTER_STR_VAL]; /* what caused err */
};
static DEFINE_MUTEX(tracing_err_log_lock);
struct tracing_log_err *get_tracing_log_err(struct trace_array *tr)
{
struct tracing_log_err *err;
if (tr->n_err_log_entries < TRACING_LOG_ERRS_MAX) {
err = kzalloc(sizeof(*err), GFP_KERNEL);
if (!err)
err = ERR_PTR(-ENOMEM);
tr->n_err_log_entries++;
return err;
}
err = list_first_entry(&tr->err_log, struct tracing_log_err, list);
list_del(&err->list);
return err;
}
/**
* err_pos - find the position of a string within a command for error careting
* @cmd: The tracing command that caused the error
* @str: The string to position the caret at within @cmd
*
* Finds the position of the first occurence of @str within @cmd. The
* return value can be passed to tracing_log_err() for caret placement
* within @cmd.
*
* Returns the index within @cmd of the first occurence of @str or 0
* if @str was not found.
*/
unsigned int err_pos(char *cmd, const char *str)
{
char *found;
if (WARN_ON(!strlen(cmd)))
return 0;
found = strstr(cmd, str);
if (found)
return found - cmd;
return 0;
}
/**
* tracing_log_err - write an error to the tracing error log
* @tr: The associated trace array for the error (NULL for top level array)
* @loc: A string describing where the error occurred
* @cmd: The tracing command that caused the error
* @errs: The array of loc-specific static error strings
* @type: The index into errs[], which produces the specific static err string
* @pos: The position the caret should be placed in the cmd
*
* Writes an error into tracing/error_log of the form:
*
* <loc>: error: <text>
* Command: <cmd>
* ^
*
* tracing/error_log is a small log file containing the last
* TRACING_LOG_ERRS_MAX errors (8). Memory for errors isn't allocated
* unless there has been a tracing error, and the error log can be
* cleared and have its memory freed by writing the empty string in
* truncation mode to it i.e. echo > tracing/error_log.
*
* NOTE: the @errs array along with the @type param are used to
* produce a static error string - this string is not copied and saved
* when the error is logged - only a pointer to it is saved. See
* existing callers for examples of how static strings are typically
* defined for use with tracing_log_err().
*/
void tracing_log_err(struct trace_array *tr,
const char *loc, const char *cmd,
const char **errs, u8 type, u8 pos)
{
struct tracing_log_err *err;
if (!tr)
tr = &global_trace;
mutex_lock(&tracing_err_log_lock);
err = get_tracing_log_err(tr);
if (PTR_ERR(err) == -ENOMEM) {
mutex_unlock(&tracing_err_log_lock);
return;
}
snprintf(err->loc, TRACING_LOG_LOC_MAX, "%s: error: ", loc);
snprintf(err->cmd, MAX_FILTER_STR_VAL,"\n" CMD_PREFIX "%s\n", cmd);
err->info.errs = errs;
err->info.type = type;
err->info.pos = pos;
err->info.ts = local_clock();
list_add_tail(&err->list, &tr->err_log);
mutex_unlock(&tracing_err_log_lock);
}
static void clear_tracing_err_log(struct trace_array *tr)
{
struct tracing_log_err *err, *next;
mutex_lock(&tracing_err_log_lock);
list_for_each_entry_safe(err, next, &tr->err_log, list) {
list_del(&err->list);
kfree(err);
}
tr->n_err_log_entries = 0;
mutex_unlock(&tracing_err_log_lock);
}
static void *tracing_err_log_seq_start(struct seq_file *m, loff_t *pos)
{
struct trace_array *tr = m->private;
mutex_lock(&tracing_err_log_lock);
return seq_list_start(&tr->err_log, *pos);
}
static void *tracing_err_log_seq_next(struct seq_file *m, void *v, loff_t *pos)
{
struct trace_array *tr = m->private;
return seq_list_next(v, &tr->err_log, pos);
}
static void tracing_err_log_seq_stop(struct seq_file *m, void *v)
{
mutex_unlock(&tracing_err_log_lock);
}
static void tracing_err_log_show_pos(struct seq_file *m, u8 pos)
{
u8 i;
for (i = 0; i < sizeof(CMD_PREFIX) - 1; i++)
seq_putc(m, ' ');
for (i = 0; i < pos; i++)
seq_putc(m, ' ');
seq_puts(m, "^\n");
}
static int tracing_err_log_seq_show(struct seq_file *m, void *v)
{
struct tracing_log_err *err = v;
if (err) {
const char *err_text = err->info.errs[err->info.type];
u64 sec = err->info.ts;
u32 nsec;
nsec = do_div(sec, NSEC_PER_SEC);
seq_printf(m, "[%5llu.%06u] %s%s", sec, nsec / 1000,
err->loc, err_text);
seq_printf(m, "%s", err->cmd);
tracing_err_log_show_pos(m, err->info.pos);
}
return 0;
}
static const struct seq_operations tracing_err_log_seq_ops = {
.start = tracing_err_log_seq_start,
.next = tracing_err_log_seq_next,
.stop = tracing_err_log_seq_stop,
.show = tracing_err_log_seq_show
};
static int tracing_err_log_open(struct inode *inode, struct file *file)
{
struct trace_array *tr = inode->i_private;
int ret = 0;
if (trace_array_get(tr) < 0)
return -ENODEV;
/* If this file was opened for write, then erase contents */
if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC))
clear_tracing_err_log(tr);
if (file->f_mode & FMODE_READ) {
ret = seq_open(file, &tracing_err_log_seq_ops);
if (!ret) {
struct seq_file *m = file->private_data;
m->private = tr;
} else {
trace_array_put(tr);
}
}
return ret;
}
static ssize_t tracing_err_log_write(struct file *file,
const char __user *buffer,
size_t count, loff_t *ppos)
{
return count;
}
static const struct file_operations tracing_err_log_fops = {
.open = tracing_err_log_open,
.write = tracing_err_log_write,
.read = seq_read,
.llseek = seq_lseek,
.release = tracing_release_generic_tr,
};
static int tracing_buffers_open(struct inode *inode, struct file *filp) static int tracing_buffers_open(struct inode *inode, struct file *filp)
{ {
struct trace_array *tr = inode->i_private; struct trace_array *tr = inode->i_private;
@ -8033,7 +8299,7 @@ static void update_tracer_options(struct trace_array *tr)
mutex_unlock(&trace_types_lock); mutex_unlock(&trace_types_lock);
} }
static int instance_mkdir(const char *name) struct trace_array *trace_array_create(const char *name)
{ {
struct trace_array *tr; struct trace_array *tr;
int ret; int ret;
@ -8072,6 +8338,7 @@ static int instance_mkdir(const char *name)
INIT_LIST_HEAD(&tr->systems); INIT_LIST_HEAD(&tr->systems);
INIT_LIST_HEAD(&tr->events); INIT_LIST_HEAD(&tr->events);
INIT_LIST_HEAD(&tr->hist_vars); INIT_LIST_HEAD(&tr->hist_vars);
INIT_LIST_HEAD(&tr->err_log);
if (allocate_trace_buffers(tr, trace_buf_size) < 0) if (allocate_trace_buffers(tr, trace_buf_size) < 0)
goto out_free_tr; goto out_free_tr;
@ -8097,7 +8364,7 @@ static int instance_mkdir(const char *name)
mutex_unlock(&trace_types_lock); mutex_unlock(&trace_types_lock);
mutex_unlock(&event_mutex); mutex_unlock(&event_mutex);
return 0; return tr;
out_free_tr: out_free_tr:
free_trace_buffers(tr); free_trace_buffers(tr);
@ -8109,33 +8376,21 @@ static int instance_mkdir(const char *name)
mutex_unlock(&trace_types_lock); mutex_unlock(&trace_types_lock);
mutex_unlock(&event_mutex); mutex_unlock(&event_mutex);
return ret; return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(trace_array_create);
static int instance_mkdir(const char *name)
{
return PTR_ERR_OR_ZERO(trace_array_create(name));
} }
static int instance_rmdir(const char *name) static int __remove_instance(struct trace_array *tr)
{ {
struct trace_array *tr;
int found = 0;
int ret;
int i; int i;
mutex_lock(&event_mutex);
mutex_lock(&trace_types_lock);
ret = -ENODEV;
list_for_each_entry(tr, &ftrace_trace_arrays, list) {
if (tr->name && strcmp(tr->name, name) == 0) {
found = 1;
break;
}
}
if (!found)
goto out_unlock;
ret = -EBUSY;
if (tr->ref || (tr->current_trace && tr->current_trace->ref)) if (tr->ref || (tr->current_trace && tr->current_trace->ref))
goto out_unlock; return -EBUSY;
list_del(&tr->list); list_del(&tr->list);
@ -8161,10 +8416,46 @@ static int instance_rmdir(const char *name)
free_cpumask_var(tr->tracing_cpumask); free_cpumask_var(tr->tracing_cpumask);
kfree(tr->name); kfree(tr->name);
kfree(tr); kfree(tr);
tr = NULL;
ret = 0; return 0;
}
int trace_array_destroy(struct trace_array *tr)
{
int ret;
if (!tr)
return -EINVAL;
mutex_lock(&event_mutex);
mutex_lock(&trace_types_lock);
ret = __remove_instance(tr);
mutex_unlock(&trace_types_lock);
mutex_unlock(&event_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(trace_array_destroy);
static int instance_rmdir(const char *name)
{
struct trace_array *tr;
int ret;
mutex_lock(&event_mutex);
mutex_lock(&trace_types_lock);
ret = -ENODEV;
list_for_each_entry(tr, &ftrace_trace_arrays, list) {
if (tr->name && strcmp(tr->name, name) == 0) {
ret = __remove_instance(tr);
break;
}
}
out_unlock:
mutex_unlock(&trace_types_lock); mutex_unlock(&trace_types_lock);
mutex_unlock(&event_mutex); mutex_unlock(&event_mutex);
@ -8254,6 +8545,9 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)
tr, &snapshot_fops); tr, &snapshot_fops);
#endif #endif
trace_create_file("error_log", 0644, d_tracer,
tr, &tracing_err_log_fops);
for_each_tracing_cpu(cpu) for_each_tracing_cpu(cpu)
tracing_init_tracefs_percpu(tr, cpu); tracing_init_tracefs_percpu(tr, cpu);
@ -8839,6 +9133,7 @@ __init static int tracer_alloc_buffers(void)
INIT_LIST_HEAD(&global_trace.systems); INIT_LIST_HEAD(&global_trace.systems);
INIT_LIST_HEAD(&global_trace.events); INIT_LIST_HEAD(&global_trace.events);
INIT_LIST_HEAD(&global_trace.hist_vars); INIT_LIST_HEAD(&global_trace.hist_vars);
INIT_LIST_HEAD(&global_trace.err_log);
list_add(&global_trace.list, &ftrace_trace_arrays); list_add(&global_trace.list, &ftrace_trace_arrays);
apply_trace_boot_options(); apply_trace_boot_options();

View File

@ -293,11 +293,13 @@ struct trace_array {
int nr_topts; int nr_topts;
bool clear_trace; bool clear_trace;
int buffer_percent; int buffer_percent;
unsigned int n_err_log_entries;
struct tracer *current_trace; struct tracer *current_trace;
unsigned int trace_flags; unsigned int trace_flags;
unsigned char trace_flags_index[TRACE_FLAGS_MAX_SIZE]; unsigned char trace_flags_index[TRACE_FLAGS_MAX_SIZE];
unsigned int flags; unsigned int flags;
raw_spinlock_t start_lock; raw_spinlock_t start_lock;
struct list_head err_log;
struct dentry *dir; struct dentry *dir;
struct dentry *options; struct dentry *options;
struct dentry *percpu_dir; struct dentry *percpu_dir;
@ -719,6 +721,9 @@ void trace_init_global_iter(struct trace_iterator *iter);
void tracing_iter_reset(struct trace_iterator *iter, int cpu); void tracing_iter_reset(struct trace_iterator *iter, int cpu);
unsigned long trace_total_entries_cpu(struct trace_array *tr, int cpu);
unsigned long trace_total_entries(struct trace_array *tr);
void trace_function(struct trace_array *tr, void trace_function(struct trace_array *tr,
unsigned long ip, unsigned long ip,
unsigned long parent_ip, unsigned long parent_ip,
@ -1545,7 +1550,8 @@ extern int apply_subsystem_event_filter(struct trace_subsystem_dir *dir,
extern void print_subsystem_event_filter(struct event_subsystem *system, extern void print_subsystem_event_filter(struct event_subsystem *system,
struct trace_seq *s); struct trace_seq *s);
extern int filter_assign_type(const char *type); extern int filter_assign_type(const char *type);
extern int create_event_filter(struct trace_event_call *call, extern int create_event_filter(struct trace_array *tr,
struct trace_event_call *call,
char *filter_str, bool set_str, char *filter_str, bool set_str,
struct event_filter **filterp); struct event_filter **filterp);
extern void free_event_filter(struct event_filter *filter); extern void free_event_filter(struct event_filter *filter);
@ -1876,6 +1882,11 @@ extern ssize_t trace_parse_run_command(struct file *file,
const char __user *buffer, size_t count, loff_t *ppos, const char __user *buffer, size_t count, loff_t *ppos,
int (*createfn)(int, char**)); int (*createfn)(int, char**));
extern unsigned int err_pos(char *cmd, const char *str);
extern void tracing_log_err(struct trace_array *tr,
const char *loc, const char *cmd,
const char **errs, u8 type, u8 pos);
/* /*
* Normal trace_printk() and friends allocates special buffers * Normal trace_printk() and friends allocates special buffers
* to do the manipulation, as well as saves the print formats * to do the manipulation, as well as saves the print formats

View File

@ -832,6 +832,7 @@ static int ftrace_set_clr_event(struct trace_array *tr, char *buf, int set)
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(ftrace_set_clr_event);
/** /**
* trace_set_clr_event - enable or disable an event * trace_set_clr_event - enable or disable an event
@ -1318,9 +1319,6 @@ event_id_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos)
char buf[32]; char buf[32];
int len; int len;
if (*ppos)
return 0;
if (unlikely(!id)) if (unlikely(!id))
return -ENODEV; return -ENODEV;

View File

@ -66,7 +66,8 @@ static const char * ops[] = { OPS };
C(INVALID_FILTER, "Meaningless filter expression"), \ C(INVALID_FILTER, "Meaningless filter expression"), \
C(IP_FIELD_ONLY, "Only 'ip' field is supported for function trace"), \ C(IP_FIELD_ONLY, "Only 'ip' field is supported for function trace"), \
C(INVALID_VALUE, "Invalid value (did you forget quotes)?"), \ C(INVALID_VALUE, "Invalid value (did you forget quotes)?"), \
C(NO_FILTER, "No filter found"), C(ERRNO, "Error"), \
C(NO_FILTER, "No filter found")
#undef C #undef C
#define C(a, b) FILT_ERR_##a #define C(a, b) FILT_ERR_##a
@ -76,7 +77,7 @@ enum { ERRORS };
#undef C #undef C
#define C(a, b) b #define C(a, b) b
static char *err_text[] = { ERRORS }; static const char *err_text[] = { ERRORS };
/* Called after a '!' character but "!=" and "!~" are not "not"s */ /* Called after a '!' character but "!=" and "!~" are not "not"s */
static bool is_not(const char *str) static bool is_not(const char *str)
@ -919,7 +920,8 @@ static void remove_filter_string(struct event_filter *filter)
filter->filter_string = NULL; filter->filter_string = NULL;
} }
static void append_filter_err(struct filter_parse_error *pe, static void append_filter_err(struct trace_array *tr,
struct filter_parse_error *pe,
struct event_filter *filter) struct event_filter *filter)
{ {
struct trace_seq *s; struct trace_seq *s;
@ -947,8 +949,14 @@ static void append_filter_err(struct filter_parse_error *pe,
if (pe->lasterr > 0) { if (pe->lasterr > 0) {
trace_seq_printf(s, "\n%*s", pos, "^"); trace_seq_printf(s, "\n%*s", pos, "^");
trace_seq_printf(s, "\nparse_error: %s\n", err_text[pe->lasterr]); trace_seq_printf(s, "\nparse_error: %s\n", err_text[pe->lasterr]);
tracing_log_err(tr, "event filter parse error",
filter->filter_string, err_text,
pe->lasterr, pe->lasterr_pos);
} else { } else {
trace_seq_printf(s, "\nError: (%d)\n", pe->lasterr); trace_seq_printf(s, "\nError: (%d)\n", pe->lasterr);
tracing_log_err(tr, "event filter parse error",
filter->filter_string, err_text,
FILT_ERR_ERRNO, 0);
} }
trace_seq_putc(s, 0); trace_seq_putc(s, 0);
buf = kmemdup_nul(s->buffer, s->seq.len, GFP_KERNEL); buf = kmemdup_nul(s->buffer, s->seq.len, GFP_KERNEL);
@ -1214,30 +1222,30 @@ static int parse_pred(const char *str, void *data,
* (perf doesn't use it) and grab everything. * (perf doesn't use it) and grab everything.
*/ */
if (strcmp(field->name, "ip") != 0) { if (strcmp(field->name, "ip") != 0) {
parse_error(pe, FILT_ERR_IP_FIELD_ONLY, pos + i); parse_error(pe, FILT_ERR_IP_FIELD_ONLY, pos + i);
goto err_free; goto err_free;
} }
pred->fn = filter_pred_none; pred->fn = filter_pred_none;
/* /*
* Quotes are not required, but if they exist then we need * Quotes are not required, but if they exist then we need
* to read them till we hit a matching one. * to read them till we hit a matching one.
*/ */
if (str[i] == '\'' || str[i] == '"') if (str[i] == '\'' || str[i] == '"')
q = str[i]; q = str[i];
else else
q = 0; q = 0;
for (i++; str[i]; i++) { for (i++; str[i]; i++) {
if (q && str[i] == q) if (q && str[i] == q)
break; break;
if (!q && (str[i] == ')' || str[i] == '&' || if (!q && (str[i] == ')' || str[i] == '&' ||
str[i] == '|')) str[i] == '|'))
break; break;
} }
/* Skip quotes */ /* Skip quotes */
if (q) if (q)
s++; s++;
len = i - s; len = i - s;
if (len >= MAX_FILTER_STR_VAL) { if (len >= MAX_FILTER_STR_VAL) {
parse_error(pe, FILT_ERR_OPERAND_TOO_LONG, pos + i); parse_error(pe, FILT_ERR_OPERAND_TOO_LONG, pos + i);
@ -1600,7 +1608,7 @@ static int process_system_preds(struct trace_subsystem_dir *dir,
if (err) { if (err) {
filter_disable(file); filter_disable(file);
parse_error(pe, FILT_ERR_BAD_SUBSYS_FILTER, 0); parse_error(pe, FILT_ERR_BAD_SUBSYS_FILTER, 0);
append_filter_err(pe, filter); append_filter_err(tr, pe, filter);
} else } else
event_set_filtered_flag(file); event_set_filtered_flag(file);
@ -1712,7 +1720,8 @@ static void create_filter_finish(struct filter_parse_error *pe)
* information if @set_str is %true and the caller is responsible for * information if @set_str is %true and the caller is responsible for
* freeing it. * freeing it.
*/ */
static int create_filter(struct trace_event_call *call, static int create_filter(struct trace_array *tr,
struct trace_event_call *call,
char *filter_string, bool set_str, char *filter_string, bool set_str,
struct event_filter **filterp) struct event_filter **filterp)
{ {
@ -1729,17 +1738,18 @@ static int create_filter(struct trace_event_call *call,
err = process_preds(call, filter_string, *filterp, pe); err = process_preds(call, filter_string, *filterp, pe);
if (err && set_str) if (err && set_str)
append_filter_err(pe, *filterp); append_filter_err(tr, pe, *filterp);
create_filter_finish(pe); create_filter_finish(pe);
return err; return err;
} }
int create_event_filter(struct trace_event_call *call, int create_event_filter(struct trace_array *tr,
struct trace_event_call *call,
char *filter_str, bool set_str, char *filter_str, bool set_str,
struct event_filter **filterp) struct event_filter **filterp)
{ {
return create_filter(call, filter_str, set_str, filterp); return create_filter(tr, call, filter_str, set_str, filterp);
} }
/** /**
@ -1766,7 +1776,7 @@ static int create_system_filter(struct trace_subsystem_dir *dir,
kfree((*filterp)->filter_string); kfree((*filterp)->filter_string);
(*filterp)->filter_string = NULL; (*filterp)->filter_string = NULL;
} else { } else {
append_filter_err(pe, *filterp); append_filter_err(tr, pe, *filterp);
} }
} }
create_filter_finish(pe); create_filter_finish(pe);
@ -1797,7 +1807,7 @@ int apply_event_filter(struct trace_event_file *file, char *filter_string)
return 0; return 0;
} }
err = create_filter(call, filter_string, true, &filter); err = create_filter(file->tr, call, filter_string, true, &filter);
/* /*
* Always swap the call filter with the new filter * Always swap the call filter with the new filter
@ -2053,7 +2063,7 @@ int ftrace_profile_set_filter(struct perf_event *event, int event_id,
if (event->filter) if (event->filter)
goto out_unlock; goto out_unlock;
err = create_filter(call, filter_str, false, &filter); err = create_filter(NULL, call, filter_str, false, &filter);
if (err) if (err)
goto free_filter; goto free_filter;
@ -2202,8 +2212,8 @@ static __init int ftrace_test_event_filter(void)
struct test_filter_data_t *d = &test_filter_data[i]; struct test_filter_data_t *d = &test_filter_data[i];
int err; int err;
err = create_filter(&event_ftrace_test_filter, d->filter, err = create_filter(NULL, &event_ftrace_test_filter,
false, &filter); d->filter, false, &filter);
if (err) { if (err) {
printk(KERN_INFO printk(KERN_INFO
"Failed to get filter for '%s', err %d\n", "Failed to get filter for '%s', err %d\n",

View File

@ -22,6 +22,57 @@
#define STR_VAR_LEN_MAX 32 /* must be multiple of sizeof(u64) */ #define STR_VAR_LEN_MAX 32 /* must be multiple of sizeof(u64) */
#define ERRORS \
C(NONE, "No error"), \
C(DUPLICATE_VAR, "Variable already defined"), \
C(VAR_NOT_UNIQUE, "Variable name not unique, need to use fully qualified name (subsys.event.var) for variable"), \
C(TOO_MANY_VARS, "Too many variables defined"), \
C(MALFORMED_ASSIGNMENT, "Malformed assignment"), \
C(NAMED_MISMATCH, "Named hist trigger doesn't match existing named trigger (includes variables)"), \
C(TRIGGER_EEXIST, "Hist trigger already exists"), \
C(TRIGGER_ENOENT_CLEAR, "Can't clear or continue a nonexistent hist trigger"), \
C(SET_CLOCK_FAIL, "Couldn't set trace_clock"), \
C(BAD_FIELD_MODIFIER, "Invalid field modifier"), \
C(TOO_MANY_SUBEXPR, "Too many subexpressions (3 max)"), \
C(TIMESTAMP_MISMATCH, "Timestamp units in expression don't match"), \
C(TOO_MANY_FIELD_VARS, "Too many field variables defined"), \
C(EVENT_FILE_NOT_FOUND, "Event file not found"), \
C(HIST_NOT_FOUND, "Matching event histogram not found"), \
C(HIST_CREATE_FAIL, "Couldn't create histogram for field"), \
C(SYNTH_VAR_NOT_FOUND, "Couldn't find synthetic variable"), \
C(SYNTH_EVENT_NOT_FOUND,"Couldn't find synthetic event"), \
C(SYNTH_TYPE_MISMATCH, "Param type doesn't match synthetic event field type"), \
C(SYNTH_COUNT_MISMATCH, "Param count doesn't match synthetic event field count"), \
C(FIELD_VAR_PARSE_FAIL, "Couldn't parse field variable"), \
C(VAR_CREATE_FIND_FAIL, "Couldn't create or find variable"), \
C(ONX_NOT_VAR, "For onmax(x) or onchange(x), x must be a variable"), \
C(ONX_VAR_NOT_FOUND, "Couldn't find onmax or onchange variable"), \
C(ONX_VAR_CREATE_FAIL, "Couldn't create onmax or onchange variable"), \
C(FIELD_VAR_CREATE_FAIL,"Couldn't create field variable"), \
C(TOO_MANY_PARAMS, "Too many action params"), \
C(PARAM_NOT_FOUND, "Couldn't find param"), \
C(INVALID_PARAM, "Invalid action param"), \
C(ACTION_NOT_FOUND, "No action found"), \
C(NO_SAVE_PARAMS, "No params found for save()"), \
C(TOO_MANY_SAVE_ACTIONS,"Can't have more than one save() action per hist"), \
C(ACTION_MISMATCH, "Handler doesn't support action"), \
C(NO_CLOSING_PAREN, "No closing paren found"), \
C(SUBSYS_NOT_FOUND, "Missing subsystem"), \
C(INVALID_SUBSYS_EVENT, "Invalid subsystem or event name"), \
C(INVALID_REF_KEY, "Using variable references as keys not supported"), \
C(VAR_NOT_FOUND, "Couldn't find variable"), \
C(FIELD_NOT_FOUND, "Couldn't find field"),
#undef C
#define C(a, b) HIST_ERR_##a
enum { ERRORS };
#undef C
#define C(a, b) b
static const char *err_text[] = { ERRORS };
struct hist_field; struct hist_field;
typedef u64 (*hist_field_fn_t) (struct hist_field *field, typedef u64 (*hist_field_fn_t) (struct hist_field *field,
@ -535,62 +586,49 @@ static struct track_data *track_data_alloc(unsigned int key_len,
return data; return data;
} }
static char last_hist_cmd[MAX_FILTER_STR_VAL]; static char last_cmd[MAX_FILTER_STR_VAL];
static char hist_err_str[MAX_FILTER_STR_VAL]; static char last_cmd_loc[MAX_FILTER_STR_VAL];
static void last_cmd_set(char *str) static int errpos(char *str)
{ {
if (!str) return err_pos(last_cmd, str);
return;
strncpy(last_hist_cmd, str, MAX_FILTER_STR_VAL - 1);
} }
static void hist_err(char *str, char *var) static void last_cmd_set(struct trace_event_file *file, char *str)
{ {
int maxlen = MAX_FILTER_STR_VAL - 1; const char *system = NULL, *name = NULL;
struct trace_event_call *call;
if (!str) if (!str)
return; return;
if (strlen(hist_err_str)) strncpy(last_cmd, str, MAX_FILTER_STR_VAL - 1);
return;
if (!var) if (file) {
var = ""; call = file->event_call;
if (strlen(hist_err_str) + strlen(str) + strlen(var) > maxlen) system = call->class->system;
return; if (system) {
name = trace_event_name(call);
if (!name)
system = NULL;
}
}
strcat(hist_err_str, str); if (system)
strcat(hist_err_str, var); snprintf(last_cmd_loc, MAX_FILTER_STR_VAL, "hist:%s:%s", system, name);
} }
static void hist_err_event(char *str, char *system, char *event, char *var) static void hist_err(struct trace_array *tr, u8 err_type, u8 err_pos)
{ {
char err[MAX_FILTER_STR_VAL]; tracing_log_err(tr, last_cmd_loc, last_cmd, err_text,
err_type, err_pos);
if (system && var)
snprintf(err, MAX_FILTER_STR_VAL, "%s.%s.%s", system, event, var);
else if (system)
snprintf(err, MAX_FILTER_STR_VAL, "%s.%s", system, event);
else
strscpy(err, var, MAX_FILTER_STR_VAL);
hist_err(str, err);
} }
static void hist_err_clear(void) static void hist_err_clear(void)
{ {
hist_err_str[0] = '\0'; last_cmd[0] = '\0';
} last_cmd_loc[0] = '\0';
static bool have_hist_err(void)
{
if (strlen(hist_err_str))
return true;
return false;
} }
struct synth_trace_event { struct synth_trace_event {
@ -1719,7 +1757,7 @@ static struct trace_event_file *find_var_file(struct trace_array *tr,
if (find_var_field(var_hist_data, var_name)) { if (find_var_field(var_hist_data, var_name)) {
if (found) { if (found) {
hist_err_event("Variable name not unique, need to use fully qualified name (subsys.event.var) for variable: ", system, event_name, var_name); hist_err(tr, HIST_ERR_VAR_NOT_UNIQUE, errpos(var_name));
return NULL; return NULL;
} }
@ -1770,7 +1808,8 @@ find_match_var(struct hist_trigger_data *hist_data, char *var_name)
hist_field = find_file_var(file, var_name); hist_field = find_file_var(file, var_name);
if (hist_field) { if (hist_field) {
if (found) { if (found) {
hist_err_event("Variable name not unique, need to use fully qualified name (subsys.event.var) for variable: ", system, event_name, var_name); hist_err(tr, HIST_ERR_VAR_NOT_UNIQUE,
errpos(var_name));
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
@ -2002,11 +2041,11 @@ static int parse_action(char *str, struct hist_trigger_attrs *attrs)
attrs->n_actions++; attrs->n_actions++;
ret = 0; ret = 0;
} }
return ret; return ret;
} }
static int parse_assignment(char *str, struct hist_trigger_attrs *attrs) static int parse_assignment(struct trace_array *tr,
char *str, struct hist_trigger_attrs *attrs)
{ {
int ret = 0; int ret = 0;
@ -2062,7 +2101,7 @@ static int parse_assignment(char *str, struct hist_trigger_attrs *attrs)
char *assignment; char *assignment;
if (attrs->n_assignments == TRACING_MAP_VARS_MAX) { if (attrs->n_assignments == TRACING_MAP_VARS_MAX) {
hist_err("Too many variables defined: ", str); hist_err(tr, HIST_ERR_TOO_MANY_VARS, errpos(str));
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
@ -2079,7 +2118,8 @@ static int parse_assignment(char *str, struct hist_trigger_attrs *attrs)
return ret; return ret;
} }
static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str) static struct hist_trigger_attrs *
parse_hist_trigger_attrs(struct trace_array *tr, char *trigger_str)
{ {
struct hist_trigger_attrs *attrs; struct hist_trigger_attrs *attrs;
int ret = 0; int ret = 0;
@ -2092,7 +2132,7 @@ static struct hist_trigger_attrs *parse_hist_trigger_attrs(char *trigger_str)
char *str = strsep(&trigger_str, ":"); char *str = strsep(&trigger_str, ":");
if (strchr(str, '=')) { if (strchr(str, '=')) {
ret = parse_assignment(str, attrs); ret = parse_assignment(tr, str, attrs);
if (ret) if (ret)
goto free; goto free;
} else if (strcmp(str, "pause") == 0) } else if (strcmp(str, "pause") == 0)
@ -2648,6 +2688,7 @@ static struct hist_field *parse_var_ref(struct hist_trigger_data *hist_data,
char *var_name) char *var_name)
{ {
struct hist_field *var_field = NULL, *ref_field = NULL; struct hist_field *var_field = NULL, *ref_field = NULL;
struct trace_array *tr = hist_data->event_file->tr;
if (!is_var_ref(var_name)) if (!is_var_ref(var_name))
return NULL; return NULL;
@ -2660,8 +2701,7 @@ static struct hist_field *parse_var_ref(struct hist_trigger_data *hist_data,
system, event_name); system, event_name);
if (!ref_field) if (!ref_field)
hist_err_event("Couldn't find variable: $", hist_err(tr, HIST_ERR_VAR_NOT_FOUND, errpos(var_name));
system, event_name, var_name);
return ref_field; return ref_field;
} }
@ -2672,6 +2712,7 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
{ {
struct ftrace_event_field *field = NULL; struct ftrace_event_field *field = NULL;
char *field_name, *modifier, *str; char *field_name, *modifier, *str;
struct trace_array *tr = file->tr;
modifier = str = kstrdup(field_str, GFP_KERNEL); modifier = str = kstrdup(field_str, GFP_KERNEL);
if (!modifier) if (!modifier)
@ -2695,7 +2736,7 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
else if (strcmp(modifier, "usecs") == 0) else if (strcmp(modifier, "usecs") == 0)
*flags |= HIST_FIELD_FL_TIMESTAMP_USECS; *flags |= HIST_FIELD_FL_TIMESTAMP_USECS;
else { else {
hist_err("Invalid field modifier: ", modifier); hist_err(tr, HIST_ERR_BAD_FIELD_MODIFIER, errpos(modifier));
field = ERR_PTR(-EINVAL); field = ERR_PTR(-EINVAL);
goto out; goto out;
} }
@ -2711,7 +2752,7 @@ parse_field(struct hist_trigger_data *hist_data, struct trace_event_file *file,
else { else {
field = trace_find_event_field(file->event_call, field_name); field = trace_find_event_field(file->event_call, field_name);
if (!field || !field->size) { if (!field || !field->size) {
hist_err("Couldn't find field: ", field_name); hist_err(tr, HIST_ERR_FIELD_NOT_FOUND, errpos(field_name));
field = ERR_PTR(-EINVAL); field = ERR_PTR(-EINVAL);
goto out; goto out;
} }
@ -2773,7 +2814,8 @@ static struct hist_field *parse_atom(struct hist_trigger_data *hist_data,
s = local_field_var_ref(hist_data, ref_system, ref_event, ref_var); s = local_field_var_ref(hist_data, ref_system, ref_event, ref_var);
if (!s) { if (!s) {
hist_field = parse_var_ref(hist_data, ref_system, ref_event, ref_var); hist_field = parse_var_ref(hist_data, ref_system,
ref_event, ref_var);
if (hist_field) { if (hist_field) {
if (var_name) { if (var_name) {
hist_field = create_alias(hist_data, hist_field, var_name); hist_field = create_alias(hist_data, hist_field, var_name);
@ -2822,7 +2864,7 @@ static struct hist_field *parse_unary(struct hist_trigger_data *hist_data,
/* we support only -(xxx) i.e. explicit parens required */ /* we support only -(xxx) i.e. explicit parens required */
if (level > 3) { if (level > 3) {
hist_err("Too many subexpressions (3 max): ", str); hist_err(file->tr, HIST_ERR_TOO_MANY_SUBEXPR, errpos(str));
ret = -EINVAL; ret = -EINVAL;
goto free; goto free;
} }
@ -2877,7 +2919,8 @@ static struct hist_field *parse_unary(struct hist_trigger_data *hist_data,
return ERR_PTR(ret); return ERR_PTR(ret);
} }
static int check_expr_operands(struct hist_field *operand1, static int check_expr_operands(struct trace_array *tr,
struct hist_field *operand1,
struct hist_field *operand2) struct hist_field *operand2)
{ {
unsigned long operand1_flags = operand1->flags; unsigned long operand1_flags = operand1->flags;
@ -2905,7 +2948,7 @@ static int check_expr_operands(struct hist_field *operand1,
if ((operand1_flags & HIST_FIELD_FL_TIMESTAMP_USECS) != if ((operand1_flags & HIST_FIELD_FL_TIMESTAMP_USECS) !=
(operand2_flags & HIST_FIELD_FL_TIMESTAMP_USECS)) { (operand2_flags & HIST_FIELD_FL_TIMESTAMP_USECS)) {
hist_err("Timestamp units in expression don't match", NULL); hist_err(tr, HIST_ERR_TIMESTAMP_MISMATCH, 0);
return -EINVAL; return -EINVAL;
} }
@ -2923,7 +2966,7 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
char *sep, *operand1_str; char *sep, *operand1_str;
if (level > 3) { if (level > 3) {
hist_err("Too many subexpressions (3 max): ", str); hist_err(file->tr, HIST_ERR_TOO_MANY_SUBEXPR, errpos(str));
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
@ -2968,7 +3011,7 @@ static struct hist_field *parse_expr(struct hist_trigger_data *hist_data,
goto free; goto free;
} }
ret = check_expr_operands(operand1, operand2); ret = check_expr_operands(file->tr, operand1, operand2);
if (ret) if (ret)
goto free; goto free;
@ -3161,16 +3204,14 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data,
int ret; int ret;
if (target_hist_data->n_field_var_hists >= SYNTH_FIELDS_MAX) { if (target_hist_data->n_field_var_hists >= SYNTH_FIELDS_MAX) {
hist_err_event("trace action: Too many field variables defined: ", hist_err(tr, HIST_ERR_TOO_MANY_FIELD_VARS, errpos(field_name));
subsys_name, event_name, field_name);
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
file = event_file(tr, subsys_name, event_name); file = event_file(tr, subsys_name, event_name);
if (IS_ERR(file)) { if (IS_ERR(file)) {
hist_err_event("trace action: Event file not found: ", hist_err(tr, HIST_ERR_EVENT_FILE_NOT_FOUND, errpos(field_name));
subsys_name, event_name, field_name);
ret = PTR_ERR(file); ret = PTR_ERR(file);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
@ -3183,8 +3224,7 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data,
*/ */
hist_data = find_compatible_hist(target_hist_data, file); hist_data = find_compatible_hist(target_hist_data, file);
if (!hist_data) { if (!hist_data) {
hist_err_event("trace action: Matching event histogram not found: ", hist_err(tr, HIST_ERR_HIST_NOT_FOUND, errpos(field_name));
subsys_name, event_name, field_name);
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
@ -3245,8 +3285,7 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data,
kfree(cmd); kfree(cmd);
kfree(var_hist->cmd); kfree(var_hist->cmd);
kfree(var_hist); kfree(var_hist);
hist_err_event("trace action: Couldn't create histogram for field: ", hist_err(tr, HIST_ERR_HIST_CREATE_FAIL, errpos(field_name));
subsys_name, event_name, field_name);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
@ -3258,8 +3297,7 @@ create_field_var_hist(struct hist_trigger_data *target_hist_data,
if (IS_ERR_OR_NULL(event_var)) { if (IS_ERR_OR_NULL(event_var)) {
kfree(var_hist->cmd); kfree(var_hist->cmd);
kfree(var_hist); kfree(var_hist);
hist_err_event("trace action: Couldn't find synthetic variable: ", hist_err(tr, HIST_ERR_SYNTH_VAR_NOT_FOUND, errpos(field_name));
subsys_name, event_name, field_name);
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
@ -3392,25 +3430,26 @@ static struct field_var *create_field_var(struct hist_trigger_data *hist_data,
{ {
struct hist_field *val = NULL, *var = NULL; struct hist_field *val = NULL, *var = NULL;
unsigned long flags = HIST_FIELD_FL_VAR; unsigned long flags = HIST_FIELD_FL_VAR;
struct trace_array *tr = file->tr;
struct field_var *field_var; struct field_var *field_var;
int ret = 0; int ret = 0;
if (hist_data->n_field_vars >= SYNTH_FIELDS_MAX) { if (hist_data->n_field_vars >= SYNTH_FIELDS_MAX) {
hist_err("Too many field variables defined: ", field_name); hist_err(tr, HIST_ERR_TOO_MANY_FIELD_VARS, errpos(field_name));
ret = -EINVAL; ret = -EINVAL;
goto err; goto err;
} }
val = parse_atom(hist_data, file, field_name, &flags, NULL); val = parse_atom(hist_data, file, field_name, &flags, NULL);
if (IS_ERR(val)) { if (IS_ERR(val)) {
hist_err("Couldn't parse field variable: ", field_name); hist_err(tr, HIST_ERR_FIELD_VAR_PARSE_FAIL, errpos(field_name));
ret = PTR_ERR(val); ret = PTR_ERR(val);
goto err; goto err;
} }
var = create_var(hist_data, file, field_name, val->size, val->type); var = create_var(hist_data, file, field_name, val->size, val->type);
if (IS_ERR(var)) { if (IS_ERR(var)) {
hist_err("Couldn't create or find variable: ", field_name); hist_err(tr, HIST_ERR_VAR_CREATE_FIND_FAIL, errpos(field_name));
kfree(val); kfree(val);
ret = PTR_ERR(var); ret = PTR_ERR(var);
goto err; goto err;
@ -3737,19 +3776,20 @@ static int track_data_create(struct hist_trigger_data *hist_data,
{ {
struct hist_field *var_field, *ref_field, *track_var = NULL; struct hist_field *var_field, *ref_field, *track_var = NULL;
struct trace_event_file *file = hist_data->event_file; struct trace_event_file *file = hist_data->event_file;
struct trace_array *tr = file->tr;
char *track_data_var_str; char *track_data_var_str;
int ret = 0; int ret = 0;
track_data_var_str = data->track_data.var_str; track_data_var_str = data->track_data.var_str;
if (track_data_var_str[0] != '$') { if (track_data_var_str[0] != '$') {
hist_err("For onmax(x) or onchange(x), x must be a variable: ", track_data_var_str); hist_err(tr, HIST_ERR_ONX_NOT_VAR, errpos(track_data_var_str));
return -EINVAL; return -EINVAL;
} }
track_data_var_str++; track_data_var_str++;
var_field = find_target_event_var(hist_data, NULL, NULL, track_data_var_str); var_field = find_target_event_var(hist_data, NULL, NULL, track_data_var_str);
if (!var_field) { if (!var_field) {
hist_err("Couldn't find onmax or onchange variable: ", track_data_var_str); hist_err(tr, HIST_ERR_ONX_VAR_NOT_FOUND, errpos(track_data_var_str));
return -EINVAL; return -EINVAL;
} }
@ -3762,7 +3802,7 @@ static int track_data_create(struct hist_trigger_data *hist_data,
if (data->handler == HANDLER_ONMAX) if (data->handler == HANDLER_ONMAX)
track_var = create_var(hist_data, file, "__max", sizeof(u64), "u64"); track_var = create_var(hist_data, file, "__max", sizeof(u64), "u64");
if (IS_ERR(track_var)) { if (IS_ERR(track_var)) {
hist_err("Couldn't create onmax variable: ", "__max"); hist_err(tr, HIST_ERR_ONX_VAR_CREATE_FAIL, 0);
ret = PTR_ERR(track_var); ret = PTR_ERR(track_var);
goto out; goto out;
} }
@ -3770,7 +3810,7 @@ static int track_data_create(struct hist_trigger_data *hist_data,
if (data->handler == HANDLER_ONCHANGE) if (data->handler == HANDLER_ONCHANGE)
track_var = create_var(hist_data, file, "__change", sizeof(u64), "u64"); track_var = create_var(hist_data, file, "__change", sizeof(u64), "u64");
if (IS_ERR(track_var)) { if (IS_ERR(track_var)) {
hist_err("Couldn't create onchange variable: ", "__change"); hist_err(tr, HIST_ERR_ONX_VAR_CREATE_FAIL, 0);
ret = PTR_ERR(track_var); ret = PTR_ERR(track_var);
goto out; goto out;
} }
@ -3781,7 +3821,8 @@ static int track_data_create(struct hist_trigger_data *hist_data,
return ret; return ret;
} }
static int parse_action_params(char *params, struct action_data *data) static int parse_action_params(struct trace_array *tr, char *params,
struct action_data *data)
{ {
char *param, *saved_param; char *param, *saved_param;
bool first_param = true; bool first_param = true;
@ -3789,20 +3830,20 @@ static int parse_action_params(char *params, struct action_data *data)
while (params) { while (params) {
if (data->n_params >= SYNTH_FIELDS_MAX) { if (data->n_params >= SYNTH_FIELDS_MAX) {
hist_err("Too many action params", ""); hist_err(tr, HIST_ERR_TOO_MANY_PARAMS, 0);
goto out; goto out;
} }
param = strsep(&params, ","); param = strsep(&params, ",");
if (!param) { if (!param) {
hist_err("No action param found", ""); hist_err(tr, HIST_ERR_PARAM_NOT_FOUND, 0);
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
param = strstrip(param); param = strstrip(param);
if (strlen(param) < 2) { if (strlen(param) < 2) {
hist_err("Invalid action param: ", param); hist_err(tr, HIST_ERR_INVALID_PARAM, errpos(param));
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
@ -3826,7 +3867,7 @@ static int parse_action_params(char *params, struct action_data *data)
return ret; return ret;
} }
static int action_parse(char *str, struct action_data *data, static int action_parse(struct trace_array *tr, char *str, struct action_data *data,
enum handler_id handler) enum handler_id handler)
{ {
char *action_name; char *action_name;
@ -3834,14 +3875,14 @@ static int action_parse(char *str, struct action_data *data,
strsep(&str, "."); strsep(&str, ".");
if (!str) { if (!str) {
hist_err("action parsing: No action found", ""); hist_err(tr, HIST_ERR_ACTION_NOT_FOUND, 0);
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
action_name = strsep(&str, "("); action_name = strsep(&str, "(");
if (!action_name || !str) { if (!action_name || !str) {
hist_err("action parsing: No action found", ""); hist_err(tr, HIST_ERR_ACTION_NOT_FOUND, 0);
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
@ -3850,12 +3891,12 @@ static int action_parse(char *str, struct action_data *data,
char *params = strsep(&str, ")"); char *params = strsep(&str, ")");
if (!params) { if (!params) {
hist_err("action parsing: No params found for %s", "save"); hist_err(tr, HIST_ERR_NO_SAVE_PARAMS, 0);
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
ret = parse_action_params(params, data); ret = parse_action_params(tr, params, data);
if (ret) if (ret)
goto out; goto out;
@ -3864,7 +3905,7 @@ static int action_parse(char *str, struct action_data *data,
else if (handler == HANDLER_ONCHANGE) else if (handler == HANDLER_ONCHANGE)
data->track_data.check_val = check_track_val_changed; data->track_data.check_val = check_track_val_changed;
else { else {
hist_err("action parsing: Handler doesn't support action: ", action_name); hist_err(tr, HIST_ERR_ACTION_MISMATCH, errpos(action_name));
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
@ -3876,7 +3917,7 @@ static int action_parse(char *str, struct action_data *data,
char *params = strsep(&str, ")"); char *params = strsep(&str, ")");
if (!str) { if (!str) {
hist_err("action parsing: No closing paren found: %s", params); hist_err(tr, HIST_ERR_NO_CLOSING_PAREN, errpos(params));
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
@ -3886,7 +3927,7 @@ static int action_parse(char *str, struct action_data *data,
else if (handler == HANDLER_ONCHANGE) else if (handler == HANDLER_ONCHANGE)
data->track_data.check_val = check_track_val_changed; data->track_data.check_val = check_track_val_changed;
else { else {
hist_err("action parsing: Handler doesn't support action: ", action_name); hist_err(tr, HIST_ERR_ACTION_MISMATCH, errpos(action_name));
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
@ -3901,7 +3942,7 @@ static int action_parse(char *str, struct action_data *data,
data->use_trace_keyword = true; data->use_trace_keyword = true;
if (params) { if (params) {
ret = parse_action_params(params, data); ret = parse_action_params(tr, params, data);
if (ret) if (ret)
goto out; goto out;
} }
@ -3954,7 +3995,7 @@ static struct action_data *track_data_parse(struct hist_trigger_data *hist_data,
goto free; goto free;
} }
ret = action_parse(str, data, handler); ret = action_parse(hist_data->event_file->tr, str, data, handler);
if (ret) if (ret)
goto free; goto free;
out: out:
@ -4024,6 +4065,7 @@ trace_action_find_var(struct hist_trigger_data *hist_data,
struct action_data *data, struct action_data *data,
char *system, char *event, char *var) char *system, char *event, char *var)
{ {
struct trace_array *tr = hist_data->event_file->tr;
struct hist_field *hist_field; struct hist_field *hist_field;
var++; /* skip '$' */ var++; /* skip '$' */
@ -4039,7 +4081,7 @@ trace_action_find_var(struct hist_trigger_data *hist_data,
} }
if (!hist_field) if (!hist_field)
hist_err_event("trace action: Couldn't find param: $", system, event, var); hist_err(tr, HIST_ERR_PARAM_NOT_FOUND, errpos(var));
return hist_field; return hist_field;
} }
@ -4097,6 +4139,7 @@ trace_action_create_field_var(struct hist_trigger_data *hist_data,
static int trace_action_create(struct hist_trigger_data *hist_data, static int trace_action_create(struct hist_trigger_data *hist_data,
struct action_data *data) struct action_data *data)
{ {
struct trace_array *tr = hist_data->event_file->tr;
char *event_name, *param, *system = NULL; char *event_name, *param, *system = NULL;
struct hist_field *hist_field, *var_ref; struct hist_field *hist_field, *var_ref;
unsigned int i, var_ref_idx; unsigned int i, var_ref_idx;
@ -4114,7 +4157,7 @@ static int trace_action_create(struct hist_trigger_data *hist_data,
event = find_synth_event(synth_event_name); event = find_synth_event(synth_event_name);
if (!event) { if (!event) {
hist_err("trace action: Couldn't find synthetic event: ", synth_event_name); hist_err(tr, HIST_ERR_SYNTH_EVENT_NOT_FOUND, errpos(synth_event_name));
return -EINVAL; return -EINVAL;
} }
@ -4175,15 +4218,14 @@ static int trace_action_create(struct hist_trigger_data *hist_data,
continue; continue;
} }
hist_err_event("trace action: Param type doesn't match synthetic event field type: ", hist_err(tr, HIST_ERR_SYNTH_TYPE_MISMATCH, errpos(param));
system, event_name, param);
kfree(p); kfree(p);
ret = -EINVAL; ret = -EINVAL;
goto err; goto err;
} }
if (field_pos != event->n_fields) { if (field_pos != event->n_fields) {
hist_err("trace action: Param count doesn't match synthetic event field count: ", event->name); hist_err(tr, HIST_ERR_SYNTH_COUNT_MISMATCH, errpos(event->name));
ret = -EINVAL; ret = -EINVAL;
goto err; goto err;
} }
@ -4202,6 +4244,7 @@ static int action_create(struct hist_trigger_data *hist_data,
struct action_data *data) struct action_data *data)
{ {
struct trace_event_file *file = hist_data->event_file; struct trace_event_file *file = hist_data->event_file;
struct trace_array *tr = file->tr;
struct track_data *track_data; struct track_data *track_data;
struct field_var *field_var; struct field_var *field_var;
unsigned int i; unsigned int i;
@ -4229,7 +4272,7 @@ static int action_create(struct hist_trigger_data *hist_data,
if (data->action == ACTION_SAVE) { if (data->action == ACTION_SAVE) {
if (hist_data->n_save_vars) { if (hist_data->n_save_vars) {
ret = -EEXIST; ret = -EEXIST;
hist_err("save action: Can't have more than one save() action per hist", ""); hist_err(tr, HIST_ERR_TOO_MANY_SAVE_ACTIONS, 0);
goto out; goto out;
} }
@ -4242,7 +4285,8 @@ static int action_create(struct hist_trigger_data *hist_data,
field_var = create_target_field_var(hist_data, NULL, NULL, param); field_var = create_target_field_var(hist_data, NULL, NULL, param);
if (IS_ERR(field_var)) { if (IS_ERR(field_var)) {
hist_err("save action: Couldn't create field variable: ", param); hist_err(tr, HIST_ERR_FIELD_VAR_CREATE_FAIL,
errpos(param));
ret = PTR_ERR(field_var); ret = PTR_ERR(field_var);
kfree(param); kfree(param);
goto out; goto out;
@ -4276,19 +4320,18 @@ static struct action_data *onmatch_parse(struct trace_array *tr, char *str)
match_event = strsep(&str, ")"); match_event = strsep(&str, ")");
if (!match_event || !str) { if (!match_event || !str) {
hist_err("onmatch: Missing closing paren: ", match_event); hist_err(tr, HIST_ERR_NO_CLOSING_PAREN, errpos(match_event));
goto free; goto free;
} }
match_event_system = strsep(&match_event, "."); match_event_system = strsep(&match_event, ".");
if (!match_event) { if (!match_event) {
hist_err("onmatch: Missing subsystem for match event: ", match_event_system); hist_err(tr, HIST_ERR_SUBSYS_NOT_FOUND, errpos(match_event_system));
goto free; goto free;
} }
if (IS_ERR(event_file(tr, match_event_system, match_event))) { if (IS_ERR(event_file(tr, match_event_system, match_event))) {
hist_err_event("onmatch: Invalid subsystem or event name: ", hist_err(tr, HIST_ERR_INVALID_SUBSYS_EVENT, errpos(match_event));
match_event_system, match_event, NULL);
goto free; goto free;
} }
@ -4304,7 +4347,7 @@ static struct action_data *onmatch_parse(struct trace_array *tr, char *str)
goto free; goto free;
} }
ret = action_parse(str, data, HANDLER_ONMATCH); ret = action_parse(tr, str, data, HANDLER_ONMATCH);
if (ret) if (ret)
goto free; goto free;
out: out:
@ -4373,13 +4416,14 @@ static int create_var_field(struct hist_trigger_data *hist_data,
struct trace_event_file *file, struct trace_event_file *file,
char *var_name, char *expr_str) char *var_name, char *expr_str)
{ {
struct trace_array *tr = hist_data->event_file->tr;
unsigned long flags = 0; unsigned long flags = 0;
if (WARN_ON(val_idx >= TRACING_MAP_VALS_MAX + TRACING_MAP_VARS_MAX)) if (WARN_ON(val_idx >= TRACING_MAP_VALS_MAX + TRACING_MAP_VARS_MAX))
return -EINVAL; return -EINVAL;
if (find_var(hist_data, file, var_name) && !hist_data->remove) { if (find_var(hist_data, file, var_name) && !hist_data->remove) {
hist_err("Variable already defined: ", var_name); hist_err(tr, HIST_ERR_DUPLICATE_VAR, errpos(var_name));
return -EINVAL; return -EINVAL;
} }
@ -4436,8 +4480,8 @@ static int create_key_field(struct hist_trigger_data *hist_data,
struct trace_event_file *file, struct trace_event_file *file,
char *field_str) char *field_str)
{ {
struct trace_array *tr = hist_data->event_file->tr;
struct hist_field *hist_field = NULL; struct hist_field *hist_field = NULL;
unsigned long flags = 0; unsigned long flags = 0;
unsigned int key_size; unsigned int key_size;
int ret = 0; int ret = 0;
@ -4460,7 +4504,7 @@ static int create_key_field(struct hist_trigger_data *hist_data,
} }
if (hist_field->flags & HIST_FIELD_FL_VAR_REF) { if (hist_field->flags & HIST_FIELD_FL_VAR_REF) {
hist_err("Using variable references as keys not supported: ", field_str); hist_err(tr, HIST_ERR_INVALID_REF_KEY, errpos(field_str));
destroy_hist_field(hist_field, 0); destroy_hist_field(hist_field, 0);
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
@ -4561,6 +4605,7 @@ static void free_var_defs(struct hist_trigger_data *hist_data)
static int parse_var_defs(struct hist_trigger_data *hist_data) static int parse_var_defs(struct hist_trigger_data *hist_data)
{ {
struct trace_array *tr = hist_data->event_file->tr;
char *s, *str, *var_name, *field_str; char *s, *str, *var_name, *field_str;
unsigned int i, j, n_vars = 0; unsigned int i, j, n_vars = 0;
int ret = 0; int ret = 0;
@ -4574,13 +4619,14 @@ static int parse_var_defs(struct hist_trigger_data *hist_data)
var_name = strsep(&field_str, "="); var_name = strsep(&field_str, "=");
if (!var_name || !field_str) { if (!var_name || !field_str) {
hist_err("Malformed assignment: ", var_name); hist_err(tr, HIST_ERR_MALFORMED_ASSIGNMENT,
errpos(var_name));
ret = -EINVAL; ret = -EINVAL;
goto free; goto free;
} }
if (n_vars == TRACING_MAP_VARS_MAX) { if (n_vars == TRACING_MAP_VARS_MAX) {
hist_err("Too many variables defined: ", var_name); hist_err(tr, HIST_ERR_TOO_MANY_VARS, errpos(var_name));
ret = -EINVAL; ret = -EINVAL;
goto free; goto free;
} }
@ -5431,11 +5477,6 @@ static int hist_show(struct seq_file *m, void *v)
hist_trigger_show(m, data, n++); hist_trigger_show(m, data, n++);
} }
if (have_hist_err()) {
seq_printf(m, "\nERROR: %s\n", hist_err_str);
seq_printf(m, " Last command: %s\n", last_hist_cmd);
}
out_unlock: out_unlock:
mutex_unlock(&event_mutex); mutex_unlock(&event_mutex);
@ -5800,6 +5841,7 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
{ {
struct hist_trigger_data *hist_data = data->private_data; struct hist_trigger_data *hist_data = data->private_data;
struct event_trigger_data *test, *named_data = NULL; struct event_trigger_data *test, *named_data = NULL;
struct trace_array *tr = file->tr;
int ret = 0; int ret = 0;
if (hist_data->attrs->name) { if (hist_data->attrs->name) {
@ -5807,7 +5849,7 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
if (named_data) { if (named_data) {
if (!hist_trigger_match(data, named_data, named_data, if (!hist_trigger_match(data, named_data, named_data,
true)) { true)) {
hist_err("Named hist trigger doesn't match existing named trigger (includes variables): ", hist_data->attrs->name); hist_err(tr, HIST_ERR_NAMED_MISMATCH, errpos(hist_data->attrs->name));
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
@ -5828,7 +5870,7 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
else if (hist_data->attrs->clear) else if (hist_data->attrs->clear)
hist_clear(test); hist_clear(test);
else { else {
hist_err("Hist trigger already exists", NULL); hist_err(tr, HIST_ERR_TRIGGER_EEXIST, 0);
ret = -EEXIST; ret = -EEXIST;
} }
goto out; goto out;
@ -5836,7 +5878,7 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
} }
new: new:
if (hist_data->attrs->cont || hist_data->attrs->clear) { if (hist_data->attrs->cont || hist_data->attrs->clear) {
hist_err("Can't clear or continue a nonexistent hist trigger", NULL); hist_err(tr, HIST_ERR_TRIGGER_ENOENT_CLEAR, 0);
ret = -ENOENT; ret = -ENOENT;
goto out; goto out;
} }
@ -5861,7 +5903,7 @@ static int hist_register_trigger(char *glob, struct event_trigger_ops *ops,
ret = tracing_set_clock(file->tr, hist_data->attrs->clock); ret = tracing_set_clock(file->tr, hist_data->attrs->clock);
if (ret) { if (ret) {
hist_err("Couldn't set trace_clock: ", clock); hist_err(tr, HIST_ERR_SET_CLOCK_FAIL, errpos(clock));
goto out; goto out;
} }
@ -6037,8 +6079,8 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
lockdep_assert_held(&event_mutex); lockdep_assert_held(&event_mutex);
if (glob && strlen(glob)) { if (glob && strlen(glob)) {
last_cmd_set(param);
hist_err_clear(); hist_err_clear();
last_cmd_set(file, param);
} }
if (!param) if (!param)
@ -6079,7 +6121,7 @@ static int event_hist_trigger_func(struct event_command *cmd_ops,
trigger = strstrip(trigger); trigger = strstrip(trigger);
} }
attrs = parse_hist_trigger_attrs(trigger); attrs = parse_hist_trigger_attrs(file->tr, trigger);
if (IS_ERR(attrs)) if (IS_ERR(attrs))
return PTR_ERR(attrs); return PTR_ERR(attrs);

View File

@ -731,7 +731,8 @@ int set_trigger_filter(char *filter_str,
goto out; goto out;
/* The filter is for the 'trigger' event, not the triggered event */ /* The filter is for the 'trigger' event, not the triggered event */
ret = create_event_filter(file->event_call, filter_str, false, &filter); ret = create_event_filter(file->tr, file->event_call,
filter_str, false, &filter);
/* /*
* If create_event_filter() fails, filter still needs to be freed. * If create_event_filter() fails, filter still needs to be freed.
* Which the calling code will do with data->filter. * Which the calling code will do with data->filter.

View File

@ -17,29 +17,25 @@
#include "trace.h" #include "trace.h"
#include "trace_output.h" #include "trace_output.h"
static void ftrace_dump_buf(int skip_lines, long cpu_file) static struct trace_iterator iter;
static struct ring_buffer_iter *buffer_iter[CONFIG_NR_CPUS];
static void ftrace_dump_buf(int skip_entries, long cpu_file)
{ {
/* use static because iter can be a bit big for the stack */
static struct trace_iterator iter;
static struct ring_buffer_iter *buffer_iter[CONFIG_NR_CPUS];
struct trace_array *tr; struct trace_array *tr;
unsigned int old_userobj; unsigned int old_userobj;
int cnt = 0, cpu; int cnt = 0, cpu;
trace_init_global_iter(&iter);
iter.buffer_iter = buffer_iter;
tr = iter.tr; tr = iter.tr;
for_each_tracing_cpu(cpu) {
atomic_inc(&per_cpu_ptr(iter.trace_buffer->data, cpu)->disabled);
}
old_userobj = tr->trace_flags; old_userobj = tr->trace_flags;
/* don't look at user memory in panic mode */ /* don't look at user memory in panic mode */
tr->trace_flags &= ~TRACE_ITER_SYM_USEROBJ; tr->trace_flags &= ~TRACE_ITER_SYM_USEROBJ;
kdb_printf("Dumping ftrace buffer:\n"); kdb_printf("Dumping ftrace buffer:\n");
if (skip_entries)
kdb_printf("(skipping %d entries)\n", skip_entries);
/* reset all but tr, trace, and overruns */ /* reset all but tr, trace, and overruns */
memset(&iter.seq, 0, memset(&iter.seq, 0,
@ -70,11 +66,11 @@ static void ftrace_dump_buf(int skip_lines, long cpu_file)
kdb_printf("---------------------------------\n"); kdb_printf("---------------------------------\n");
cnt++; cnt++;
if (!skip_lines) { if (!skip_entries) {
print_trace_line(&iter); print_trace_line(&iter);
trace_printk_seq(&iter.seq); trace_printk_seq(&iter.seq);
} else { } else {
skip_lines--; skip_entries--;
} }
if (KDB_FLAG(CMD_INTERRUPT)) if (KDB_FLAG(CMD_INTERRUPT))
@ -89,10 +85,6 @@ static void ftrace_dump_buf(int skip_lines, long cpu_file)
out: out:
tr->trace_flags = old_userobj; tr->trace_flags = old_userobj;
for_each_tracing_cpu(cpu) {
atomic_dec(&per_cpu_ptr(iter.trace_buffer->data, cpu)->disabled);
}
for_each_tracing_cpu(cpu) { for_each_tracing_cpu(cpu) {
if (iter.buffer_iter[cpu]) { if (iter.buffer_iter[cpu]) {
ring_buffer_read_finish(iter.buffer_iter[cpu]); ring_buffer_read_finish(iter.buffer_iter[cpu]);
@ -106,17 +98,19 @@ out:
*/ */
static int kdb_ftdump(int argc, const char **argv) static int kdb_ftdump(int argc, const char **argv)
{ {
int skip_lines = 0; int skip_entries = 0;
long cpu_file; long cpu_file;
char *cp; char *cp;
int cnt;
int cpu;
if (argc > 2) if (argc > 2)
return KDB_ARGCOUNT; return KDB_ARGCOUNT;
if (argc) { if (argc) {
skip_lines = simple_strtol(argv[1], &cp, 0); skip_entries = simple_strtol(argv[1], &cp, 0);
if (*cp) if (*cp)
skip_lines = 0; skip_entries = 0;
} }
if (argc == 2) { if (argc == 2) {
@ -129,7 +123,29 @@ static int kdb_ftdump(int argc, const char **argv)
} }
kdb_trap_printk++; kdb_trap_printk++;
ftrace_dump_buf(skip_lines, cpu_file);
trace_init_global_iter(&iter);
iter.buffer_iter = buffer_iter;
for_each_tracing_cpu(cpu) {
atomic_inc(&per_cpu_ptr(iter.trace_buffer->data, cpu)->disabled);
}
/* A negative skip_entries means skip all but the last entries */
if (skip_entries < 0) {
if (cpu_file == RING_BUFFER_ALL_CPUS)
cnt = trace_total_entries(NULL);
else
cnt = trace_total_entries_cpu(NULL, cpu_file);
skip_entries = max(cnt + skip_entries, 0);
}
ftrace_dump_buf(skip_entries, cpu_file);
for_each_tracing_cpu(cpu) {
atomic_dec(&per_cpu_ptr(iter.trace_buffer->data, cpu)->disabled);
}
kdb_trap_printk--; kdb_trap_printk--;
return 0; return 0;
@ -137,8 +153,9 @@ static int kdb_ftdump(int argc, const char **argv)
static __init int kdb_ftrace_register(void) static __init int kdb_ftrace_register(void)
{ {
kdb_register_flags("ftdump", kdb_ftdump, "[skip_#lines] [cpu]", kdb_register_flags("ftdump", kdb_ftdump, "[skip_#entries] [cpu]",
"Dump ftrace log", 0, KDB_ENABLE_ALWAYS_SAFE); "Dump ftrace log; -skip dumps last #entries", 0,
KDB_ENABLE_ALWAYS_SAFE);
return 0; return 0;
} }

View File

@ -441,13 +441,8 @@ static int __register_trace_kprobe(struct trace_kprobe *tk)
else else
ret = register_kprobe(&tk->rp.kp); ret = register_kprobe(&tk->rp.kp);
if (ret == 0) { if (ret == 0)
tk->tp.flags |= TP_FLAG_REGISTERED; tk->tp.flags |= TP_FLAG_REGISTERED;
} else if (ret == -EILSEQ) {
pr_warn("Probing address(0x%p) is not an instruction boundary.\n",
tk->rp.kp.addr);
ret = -EINVAL;
}
return ret; return ret;
} }
@ -591,7 +586,7 @@ static int trace_kprobe_create(int argc, const char *argv[])
* Type of args: * Type of args:
* FETCHARG:TYPE : use TYPE instead of unsigned long. * FETCHARG:TYPE : use TYPE instead of unsigned long.
*/ */
struct trace_kprobe *tk; struct trace_kprobe *tk = NULL;
int i, len, ret = 0; int i, len, ret = 0;
bool is_return = false; bool is_return = false;
char *symbol = NULL, *tmp = NULL; char *symbol = NULL, *tmp = NULL;
@ -615,44 +610,50 @@ static int trace_kprobe_create(int argc, const char *argv[])
if (argc < 2) if (argc < 2)
return -ECANCELED; return -ECANCELED;
trace_probe_log_init("trace_kprobe", argc, argv);
event = strchr(&argv[0][1], ':'); event = strchr(&argv[0][1], ':');
if (event) if (event)
event++; event++;
if (isdigit(argv[0][1])) { if (isdigit(argv[0][1])) {
if (!is_return) { if (!is_return) {
pr_info("Maxactive is not for kprobe"); trace_probe_log_err(1, MAXACT_NO_KPROBE);
return -EINVAL; goto parse_error;
} }
if (event) if (event)
len = event - &argv[0][1] - 1; len = event - &argv[0][1] - 1;
else else
len = strlen(&argv[0][1]); len = strlen(&argv[0][1]);
if (len > MAX_EVENT_NAME_LEN - 1) if (len > MAX_EVENT_NAME_LEN - 1) {
return -E2BIG; trace_probe_log_err(1, BAD_MAXACT);
goto parse_error;
}
memcpy(buf, &argv[0][1], len); memcpy(buf, &argv[0][1], len);
buf[len] = '\0'; buf[len] = '\0';
ret = kstrtouint(buf, 0, &maxactive); ret = kstrtouint(buf, 0, &maxactive);
if (ret || !maxactive) { if (ret || !maxactive) {
pr_info("Invalid maxactive number\n"); trace_probe_log_err(1, BAD_MAXACT);
return ret; goto parse_error;
} }
/* kretprobes instances are iterated over via a list. The /* kretprobes instances are iterated over via a list. The
* maximum should stay reasonable. * maximum should stay reasonable.
*/ */
if (maxactive > KRETPROBE_MAXACTIVE_MAX) { if (maxactive > KRETPROBE_MAXACTIVE_MAX) {
pr_info("Maxactive is too big (%d > %d).\n", trace_probe_log_err(1, MAXACT_TOO_BIG);
maxactive, KRETPROBE_MAXACTIVE_MAX); goto parse_error;
return -E2BIG;
} }
} }
/* try to parse an address. if that fails, try to read the /* try to parse an address. if that fails, try to read the
* input as a symbol. */ * input as a symbol. */
if (kstrtoul(argv[1], 0, (unsigned long *)&addr)) { if (kstrtoul(argv[1], 0, (unsigned long *)&addr)) {
trace_probe_log_set_index(1);
/* Check whether uprobe event specified */ /* Check whether uprobe event specified */
if (strchr(argv[1], '/') && strchr(argv[1], ':')) if (strchr(argv[1], '/') && strchr(argv[1], ':')) {
return -ECANCELED; ret = -ECANCELED;
goto error;
}
/* a symbol specified */ /* a symbol specified */
symbol = kstrdup(argv[1], GFP_KERNEL); symbol = kstrdup(argv[1], GFP_KERNEL);
if (!symbol) if (!symbol)
@ -660,23 +661,23 @@ static int trace_kprobe_create(int argc, const char *argv[])
/* TODO: support .init module functions */ /* TODO: support .init module functions */
ret = traceprobe_split_symbol_offset(symbol, &offset); ret = traceprobe_split_symbol_offset(symbol, &offset);
if (ret || offset < 0 || offset > UINT_MAX) { if (ret || offset < 0 || offset > UINT_MAX) {
pr_info("Failed to parse either an address or a symbol.\n"); trace_probe_log_err(0, BAD_PROBE_ADDR);
goto out; goto parse_error;
} }
if (kprobe_on_func_entry(NULL, symbol, offset)) if (kprobe_on_func_entry(NULL, symbol, offset))
flags |= TPARG_FL_FENTRY; flags |= TPARG_FL_FENTRY;
if (offset && is_return && !(flags & TPARG_FL_FENTRY)) { if (offset && is_return && !(flags & TPARG_FL_FENTRY)) {
pr_info("Given offset is not valid for return probe.\n"); trace_probe_log_err(0, BAD_RETPROBE);
ret = -EINVAL; goto parse_error;
goto out;
} }
} }
argc -= 2; argv += 2;
trace_probe_log_set_index(0);
if (event) { if (event) {
ret = traceprobe_parse_event_name(&event, &group, buf); ret = traceprobe_parse_event_name(&event, &group, buf,
event - argv[0]);
if (ret) if (ret)
goto out; goto parse_error;
} else { } else {
/* Make a new event name */ /* Make a new event name */
if (symbol) if (symbol)
@ -691,13 +692,14 @@ static int trace_kprobe_create(int argc, const char *argv[])
/* setup a probe */ /* setup a probe */
tk = alloc_trace_kprobe(group, event, addr, symbol, offset, maxactive, tk = alloc_trace_kprobe(group, event, addr, symbol, offset, maxactive,
argc, is_return); argc - 2, is_return);
if (IS_ERR(tk)) { if (IS_ERR(tk)) {
ret = PTR_ERR(tk); ret = PTR_ERR(tk);
/* This must return -ENOMEM otherwise there is a bug */ /* This must return -ENOMEM, else there is a bug */
WARN_ON_ONCE(ret != -ENOMEM); WARN_ON_ONCE(ret != -ENOMEM);
goto out; goto out; /* We know tk is not allocated */
} }
argc -= 2; argv += 2;
/* parse arguments */ /* parse arguments */
for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) { for (i = 0; i < argc && i < MAX_TRACE_ARGS; i++) {
@ -707,19 +709,32 @@ static int trace_kprobe_create(int argc, const char *argv[])
goto error; goto error;
} }
trace_probe_log_set_index(i + 2);
ret = traceprobe_parse_probe_arg(&tk->tp, i, tmp, flags); ret = traceprobe_parse_probe_arg(&tk->tp, i, tmp, flags);
kfree(tmp); kfree(tmp);
if (ret) if (ret)
goto error; goto error; /* This can be -ENOMEM */
} }
ret = register_trace_kprobe(tk); ret = register_trace_kprobe(tk);
if (ret) if (ret) {
trace_probe_log_set_index(1);
if (ret == -EILSEQ)
trace_probe_log_err(0, BAD_INSN_BNDRY);
else if (ret == -ENOENT)
trace_probe_log_err(0, BAD_PROBE_ADDR);
else if (ret != -ENOMEM)
trace_probe_log_err(0, FAIL_REG_PROBE);
goto error; goto error;
}
out: out:
trace_probe_log_clear();
kfree(symbol); kfree(symbol);
return ret; return ret;
parse_error:
ret = -EINVAL;
error: error:
free_trace_kprobe(tk); free_trace_kprobe(tk);
goto out; goto out;

View File

@ -13,6 +13,11 @@
#include "trace_probe.h" #include "trace_probe.h"
#undef C
#define C(a, b) b
static const char *trace_probe_err_text[] = { ERRORS };
static const char *reserved_field_names[] = { static const char *reserved_field_names[] = {
"common_type", "common_type",
"common_flags", "common_flags",
@ -133,6 +138,60 @@ fail:
return NULL; return NULL;
} }
static struct trace_probe_log trace_probe_log;
void trace_probe_log_init(const char *subsystem, int argc, const char **argv)
{
trace_probe_log.subsystem = subsystem;
trace_probe_log.argc = argc;
trace_probe_log.argv = argv;
trace_probe_log.index = 0;
}
void trace_probe_log_clear(void)
{
memset(&trace_probe_log, 0, sizeof(trace_probe_log));
}
void trace_probe_log_set_index(int index)
{
trace_probe_log.index = index;
}
void __trace_probe_log_err(int offset, int err_type)
{
char *command, *p;
int i, len = 0, pos = 0;
if (!trace_probe_log.argv)
return;
/* Recalcurate the length and allocate buffer */
for (i = 0; i < trace_probe_log.argc; i++) {
if (i == trace_probe_log.index)
pos = len;
len += strlen(trace_probe_log.argv[i]) + 1;
}
command = kzalloc(len, GFP_KERNEL);
if (!command)
return;
/* And make a command string from argv array */
p = command;
for (i = 0; i < trace_probe_log.argc; i++) {
len = strlen(trace_probe_log.argv[i]);
strcpy(p, trace_probe_log.argv[i]);
p[len] = ' ';
p += len + 1;
}
*(p - 1) = '\0';
tracing_log_err(NULL, trace_probe_log.subsystem, command,
trace_probe_err_text, err_type, pos + offset);
kfree(command);
}
/* Split symbol and offset. */ /* Split symbol and offset. */
int traceprobe_split_symbol_offset(char *symbol, long *offset) int traceprobe_split_symbol_offset(char *symbol, long *offset)
{ {
@ -156,7 +215,7 @@ int traceprobe_split_symbol_offset(char *symbol, long *offset)
/* @buf must has MAX_EVENT_NAME_LEN size */ /* @buf must has MAX_EVENT_NAME_LEN size */
int traceprobe_parse_event_name(const char **pevent, const char **pgroup, int traceprobe_parse_event_name(const char **pevent, const char **pgroup,
char *buf) char *buf, int offset)
{ {
const char *slash, *event = *pevent; const char *slash, *event = *pevent;
int len; int len;
@ -164,32 +223,33 @@ int traceprobe_parse_event_name(const char **pevent, const char **pgroup,
slash = strchr(event, '/'); slash = strchr(event, '/');
if (slash) { if (slash) {
if (slash == event) { if (slash == event) {
pr_info("Group name is not specified\n"); trace_probe_log_err(offset, NO_GROUP_NAME);
return -EINVAL; return -EINVAL;
} }
if (slash - event + 1 > MAX_EVENT_NAME_LEN) { if (slash - event + 1 > MAX_EVENT_NAME_LEN) {
pr_info("Group name is too long\n"); trace_probe_log_err(offset, GROUP_TOO_LONG);
return -E2BIG; return -EINVAL;
} }
strlcpy(buf, event, slash - event + 1); strlcpy(buf, event, slash - event + 1);
if (!is_good_name(buf)) { if (!is_good_name(buf)) {
pr_info("Group name must follow the same rules as C identifiers\n"); trace_probe_log_err(offset, BAD_GROUP_NAME);
return -EINVAL; return -EINVAL;
} }
*pgroup = buf; *pgroup = buf;
*pevent = slash + 1; *pevent = slash + 1;
offset += slash - event + 1;
event = *pevent; event = *pevent;
} }
len = strlen(event); len = strlen(event);
if (len == 0) { if (len == 0) {
pr_info("Event name is not specified\n"); trace_probe_log_err(offset, NO_EVENT_NAME);
return -EINVAL; return -EINVAL;
} else if (len > MAX_EVENT_NAME_LEN) { } else if (len > MAX_EVENT_NAME_LEN) {
pr_info("Event name is too long\n"); trace_probe_log_err(offset, EVENT_TOO_LONG);
return -E2BIG; return -EINVAL;
} }
if (!is_good_name(event)) { if (!is_good_name(event)) {
pr_info("Event name must follow the same rules as C identifiers\n"); trace_probe_log_err(offset, BAD_EVENT_NAME);
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
@ -198,56 +258,67 @@ int traceprobe_parse_event_name(const char **pevent, const char **pgroup,
#define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long)) #define PARAM_MAX_STACK (THREAD_SIZE / sizeof(unsigned long))
static int parse_probe_vars(char *arg, const struct fetch_type *t, static int parse_probe_vars(char *arg, const struct fetch_type *t,
struct fetch_insn *code, unsigned int flags) struct fetch_insn *code, unsigned int flags, int offs)
{ {
unsigned long param; unsigned long param;
int ret = 0; int ret = 0;
int len; int len;
if (strcmp(arg, "retval") == 0) { if (strcmp(arg, "retval") == 0) {
if (flags & TPARG_FL_RETURN) if (flags & TPARG_FL_RETURN) {
code->op = FETCH_OP_RETVAL; code->op = FETCH_OP_RETVAL;
else } else {
trace_probe_log_err(offs, RETVAL_ON_PROBE);
ret = -EINVAL; ret = -EINVAL;
}
} else if ((len = str_has_prefix(arg, "stack"))) { } else if ((len = str_has_prefix(arg, "stack"))) {
if (arg[len] == '\0') { if (arg[len] == '\0') {
code->op = FETCH_OP_STACKP; code->op = FETCH_OP_STACKP;
} else if (isdigit(arg[len])) { } else if (isdigit(arg[len])) {
ret = kstrtoul(arg + len, 10, &param); ret = kstrtoul(arg + len, 10, &param);
if (ret || ((flags & TPARG_FL_KERNEL) && if (ret) {
param > PARAM_MAX_STACK)) goto inval_var;
} else if ((flags & TPARG_FL_KERNEL) &&
param > PARAM_MAX_STACK) {
trace_probe_log_err(offs, BAD_STACK_NUM);
ret = -EINVAL; ret = -EINVAL;
else { } else {
code->op = FETCH_OP_STACK; code->op = FETCH_OP_STACK;
code->param = (unsigned int)param; code->param = (unsigned int)param;
} }
} else } else
ret = -EINVAL; goto inval_var;
} else if (strcmp(arg, "comm") == 0) { } else if (strcmp(arg, "comm") == 0) {
code->op = FETCH_OP_COMM; code->op = FETCH_OP_COMM;
#ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API #ifdef CONFIG_HAVE_FUNCTION_ARG_ACCESS_API
} else if (((flags & TPARG_FL_MASK) == } else if (((flags & TPARG_FL_MASK) ==
(TPARG_FL_KERNEL | TPARG_FL_FENTRY)) && (TPARG_FL_KERNEL | TPARG_FL_FENTRY)) &&
(len = str_has_prefix(arg, "arg"))) { (len = str_has_prefix(arg, "arg"))) {
if (!isdigit(arg[len]))
return -EINVAL;
ret = kstrtoul(arg + len, 10, &param); ret = kstrtoul(arg + len, 10, &param);
if (ret || !param || param > PARAM_MAX_STACK) if (ret) {
goto inval_var;
} else if (!param || param > PARAM_MAX_STACK) {
trace_probe_log_err(offs, BAD_ARG_NUM);
return -EINVAL; return -EINVAL;
}
code->op = FETCH_OP_ARG; code->op = FETCH_OP_ARG;
code->param = (unsigned int)param - 1; code->param = (unsigned int)param - 1;
#endif #endif
} else } else
ret = -EINVAL; goto inval_var;
return ret; return ret;
inval_var:
trace_probe_log_err(offs, BAD_VAR);
return -EINVAL;
} }
/* Recursive argument parser */ /* Recursive argument parser */
static int static int
parse_probe_arg(char *arg, const struct fetch_type *type, parse_probe_arg(char *arg, const struct fetch_type *type,
struct fetch_insn **pcode, struct fetch_insn *end, struct fetch_insn **pcode, struct fetch_insn *end,
unsigned int flags) unsigned int flags, int offs)
{ {
struct fetch_insn *code = *pcode; struct fetch_insn *code = *pcode;
unsigned long param; unsigned long param;
@ -257,7 +328,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
switch (arg[0]) { switch (arg[0]) {
case '$': case '$':
ret = parse_probe_vars(arg + 1, type, code, flags); ret = parse_probe_vars(arg + 1, type, code, flags, offs);
break; break;
case '%': /* named register */ case '%': /* named register */
@ -266,47 +337,57 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
code->op = FETCH_OP_REG; code->op = FETCH_OP_REG;
code->param = (unsigned int)ret; code->param = (unsigned int)ret;
ret = 0; ret = 0;
} } else
trace_probe_log_err(offs, BAD_REG_NAME);
break; break;
case '@': /* memory, file-offset or symbol */ case '@': /* memory, file-offset or symbol */
if (isdigit(arg[1])) { if (isdigit(arg[1])) {
ret = kstrtoul(arg + 1, 0, &param); ret = kstrtoul(arg + 1, 0, &param);
if (ret) if (ret) {
trace_probe_log_err(offs, BAD_MEM_ADDR);
break; break;
}
/* load address */ /* load address */
code->op = FETCH_OP_IMM; code->op = FETCH_OP_IMM;
code->immediate = param; code->immediate = param;
} else if (arg[1] == '+') { } else if (arg[1] == '+') {
/* kprobes don't support file offsets */ /* kprobes don't support file offsets */
if (flags & TPARG_FL_KERNEL) if (flags & TPARG_FL_KERNEL) {
trace_probe_log_err(offs, FILE_ON_KPROBE);
return -EINVAL; return -EINVAL;
}
ret = kstrtol(arg + 2, 0, &offset); ret = kstrtol(arg + 2, 0, &offset);
if (ret) if (ret) {
trace_probe_log_err(offs, BAD_FILE_OFFS);
break; break;
}
code->op = FETCH_OP_FOFFS; code->op = FETCH_OP_FOFFS;
code->immediate = (unsigned long)offset; // imm64? code->immediate = (unsigned long)offset; // imm64?
} else { } else {
/* uprobes don't support symbols */ /* uprobes don't support symbols */
if (!(flags & TPARG_FL_KERNEL)) if (!(flags & TPARG_FL_KERNEL)) {
trace_probe_log_err(offs, SYM_ON_UPROBE);
return -EINVAL; return -EINVAL;
}
/* Preserve symbol for updating */ /* Preserve symbol for updating */
code->op = FETCH_NOP_SYMBOL; code->op = FETCH_NOP_SYMBOL;
code->data = kstrdup(arg + 1, GFP_KERNEL); code->data = kstrdup(arg + 1, GFP_KERNEL);
if (!code->data) if (!code->data)
return -ENOMEM; return -ENOMEM;
if (++code == end) if (++code == end) {
return -E2BIG; trace_probe_log_err(offs, TOO_MANY_OPS);
return -EINVAL;
}
code->op = FETCH_OP_IMM; code->op = FETCH_OP_IMM;
code->immediate = 0; code->immediate = 0;
} }
/* These are fetching from memory */ /* These are fetching from memory */
if (++code == end) if (++code == end) {
return -E2BIG; trace_probe_log_err(offs, TOO_MANY_OPS);
return -EINVAL;
}
*pcode = code; *pcode = code;
code->op = FETCH_OP_DEREF; code->op = FETCH_OP_DEREF;
code->offset = offset; code->offset = offset;
@ -317,28 +398,38 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
/* fall through */ /* fall through */
case '-': case '-':
tmp = strchr(arg, '('); tmp = strchr(arg, '(');
if (!tmp) if (!tmp) {
trace_probe_log_err(offs, DEREF_NEED_BRACE);
return -EINVAL; return -EINVAL;
}
*tmp = '\0'; *tmp = '\0';
ret = kstrtol(arg, 0, &offset); ret = kstrtol(arg, 0, &offset);
if (ret) if (ret) {
trace_probe_log_err(offs, BAD_DEREF_OFFS);
break; break;
}
offs += (tmp + 1 - arg) + (arg[0] != '-' ? 1 : 0);
arg = tmp + 1; arg = tmp + 1;
tmp = strrchr(arg, ')'); tmp = strrchr(arg, ')');
if (!tmp) {
if (tmp) { trace_probe_log_err(offs + strlen(arg),
DEREF_OPEN_BRACE);
return -EINVAL;
} else {
const struct fetch_type *t2 = find_fetch_type(NULL); const struct fetch_type *t2 = find_fetch_type(NULL);
*tmp = '\0'; *tmp = '\0';
ret = parse_probe_arg(arg, t2, &code, end, flags); ret = parse_probe_arg(arg, t2, &code, end, flags, offs);
if (ret) if (ret)
break; break;
if (code->op == FETCH_OP_COMM) if (code->op == FETCH_OP_COMM) {
trace_probe_log_err(offs, COMM_CANT_DEREF);
return -EINVAL; return -EINVAL;
if (++code == end) }
return -E2BIG; if (++code == end) {
trace_probe_log_err(offs, TOO_MANY_OPS);
return -EINVAL;
}
*pcode = code; *pcode = code;
code->op = FETCH_OP_DEREF; code->op = FETCH_OP_DEREF;
@ -348,6 +439,7 @@ parse_probe_arg(char *arg, const struct fetch_type *type,
} }
if (!ret && code->op == FETCH_OP_NOP) { if (!ret && code->op == FETCH_OP_NOP) {
/* Parsed, but do not find fetch method */ /* Parsed, but do not find fetch method */
trace_probe_log_err(offs, BAD_FETCH_ARG);
ret = -EINVAL; ret = -EINVAL;
} }
return ret; return ret;
@ -379,7 +471,7 @@ static int __parse_bitfield_probe_arg(const char *bf,
return -EINVAL; return -EINVAL;
code++; code++;
if (code->op != FETCH_OP_NOP) if (code->op != FETCH_OP_NOP)
return -E2BIG; return -EINVAL;
*pcode = code; *pcode = code;
code->op = FETCH_OP_MOD_BF; code->op = FETCH_OP_MOD_BF;
@ -392,44 +484,66 @@ static int __parse_bitfield_probe_arg(const char *bf,
/* String length checking wrapper */ /* String length checking wrapper */
static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size, static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
struct probe_arg *parg, unsigned int flags) struct probe_arg *parg, unsigned int flags, int offset)
{ {
struct fetch_insn *code, *scode, *tmp = NULL; struct fetch_insn *code, *scode, *tmp = NULL;
char *t, *t2; char *t, *t2, *t3;
int ret, len; int ret, len;
if (strlen(arg) > MAX_ARGSTR_LEN) { len = strlen(arg);
pr_info("Argument is too long.: %s\n", arg); if (len > MAX_ARGSTR_LEN) {
return -ENOSPC; trace_probe_log_err(offset, ARG_TOO_LONG);
return -EINVAL;
} else if (len == 0) {
trace_probe_log_err(offset, NO_ARG_BODY);
return -EINVAL;
} }
parg->comm = kstrdup(arg, GFP_KERNEL); parg->comm = kstrdup(arg, GFP_KERNEL);
if (!parg->comm) { if (!parg->comm)
pr_info("Failed to allocate memory for command '%s'.\n", arg);
return -ENOMEM; return -ENOMEM;
}
t = strchr(arg, ':'); t = strchr(arg, ':');
if (t) { if (t) {
*t = '\0'; *t = '\0';
t2 = strchr(++t, '['); t2 = strchr(++t, '[');
if (t2) { if (t2) {
*t2 = '\0'; *t2++ = '\0';
parg->count = simple_strtoul(t2 + 1, &t2, 0); t3 = strchr(t2, ']');
if (strcmp(t2, "]") || parg->count == 0) if (!t3) {
offset += t2 + strlen(t2) - arg;
trace_probe_log_err(offset,
ARRAY_NO_CLOSE);
return -EINVAL; return -EINVAL;
if (parg->count > MAX_ARRAY_LEN) } else if (t3[1] != '\0') {
return -E2BIG; trace_probe_log_err(offset + t3 + 1 - arg,
BAD_ARRAY_SUFFIX);
return -EINVAL;
}
*t3 = '\0';
if (kstrtouint(t2, 0, &parg->count) || !parg->count) {
trace_probe_log_err(offset + t2 - arg,
BAD_ARRAY_NUM);
return -EINVAL;
}
if (parg->count > MAX_ARRAY_LEN) {
trace_probe_log_err(offset + t2 - arg,
ARRAY_TOO_BIG);
return -EINVAL;
}
} }
} }
/*
* The default type of $comm should be "string", and it can't be /* Since $comm can not be dereferred, we can find $comm by strcmp */
* dereferenced. if (strcmp(arg, "$comm") == 0) {
*/ /* The type of $comm must be "string", and not an array. */
if (!t && strcmp(arg, "$comm") == 0) if (parg->count || (t && strcmp(t, "string")))
return -EINVAL;
parg->type = find_fetch_type("string"); parg->type = find_fetch_type("string");
else } else
parg->type = find_fetch_type(t); parg->type = find_fetch_type(t);
if (!parg->type) { if (!parg->type) {
pr_info("Unsupported type: %s\n", t); trace_probe_log_err(offset + (t ? (t - arg) : 0), BAD_TYPE);
return -EINVAL; return -EINVAL;
} }
parg->offset = *size; parg->offset = *size;
@ -444,13 +558,13 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
parg->count); parg->count);
} }
code = tmp = kzalloc(sizeof(*code) * FETCH_INSN_MAX, GFP_KERNEL); code = tmp = kcalloc(FETCH_INSN_MAX, sizeof(*code), GFP_KERNEL);
if (!code) if (!code)
return -ENOMEM; return -ENOMEM;
code[FETCH_INSN_MAX - 1].op = FETCH_OP_END; code[FETCH_INSN_MAX - 1].op = FETCH_OP_END;
ret = parse_probe_arg(arg, parg->type, &code, &code[FETCH_INSN_MAX - 1], ret = parse_probe_arg(arg, parg->type, &code, &code[FETCH_INSN_MAX - 1],
flags); flags, offset);
if (ret) if (ret)
goto fail; goto fail;
@ -458,7 +572,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
if (!strcmp(parg->type->name, "string")) { if (!strcmp(parg->type->name, "string")) {
if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_IMM && if (code->op != FETCH_OP_DEREF && code->op != FETCH_OP_IMM &&
code->op != FETCH_OP_COMM) { code->op != FETCH_OP_COMM) {
pr_info("string only accepts memory or address.\n"); trace_probe_log_err(offset + (t ? (t - arg) : 0),
BAD_STRING);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
@ -470,7 +585,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
*/ */
code++; code++;
if (code->op != FETCH_OP_NOP) { if (code->op != FETCH_OP_NOP) {
ret = -E2BIG; trace_probe_log_err(offset, TOO_MANY_OPS);
ret = -EINVAL;
goto fail; goto fail;
} }
} }
@ -483,7 +599,8 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
} else { } else {
code++; code++;
if (code->op != FETCH_OP_NOP) { if (code->op != FETCH_OP_NOP) {
ret = -E2BIG; trace_probe_log_err(offset, TOO_MANY_OPS);
ret = -EINVAL;
goto fail; goto fail;
} }
code->op = FETCH_OP_ST_RAW; code->op = FETCH_OP_ST_RAW;
@ -493,20 +610,24 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
/* Modify operation */ /* Modify operation */
if (t != NULL) { if (t != NULL) {
ret = __parse_bitfield_probe_arg(t, parg->type, &code); ret = __parse_bitfield_probe_arg(t, parg->type, &code);
if (ret) if (ret) {
trace_probe_log_err(offset + t - arg, BAD_BITFIELD);
goto fail; goto fail;
}
} }
/* Loop(Array) operation */ /* Loop(Array) operation */
if (parg->count) { if (parg->count) {
if (scode->op != FETCH_OP_ST_MEM && if (scode->op != FETCH_OP_ST_MEM &&
scode->op != FETCH_OP_ST_STRING) { scode->op != FETCH_OP_ST_STRING) {
pr_info("array only accepts memory or address\n"); trace_probe_log_err(offset + (t ? (t - arg) : 0),
BAD_STRING);
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
code++; code++;
if (code->op != FETCH_OP_NOP) { if (code->op != FETCH_OP_NOP) {
ret = -E2BIG; trace_probe_log_err(offset, TOO_MANY_OPS);
ret = -EINVAL;
goto fail; goto fail;
} }
code->op = FETCH_OP_LP_ARRAY; code->op = FETCH_OP_LP_ARRAY;
@ -516,7 +637,7 @@ static int traceprobe_parse_probe_arg_body(char *arg, ssize_t *size,
code->op = FETCH_OP_END; code->op = FETCH_OP_END;
/* Shrink down the code buffer */ /* Shrink down the code buffer */
parg->code = kzalloc(sizeof(*code) * (code - tmp + 1), GFP_KERNEL); parg->code = kcalloc(code - tmp + 1, sizeof(*code), GFP_KERNEL);
if (!parg->code) if (!parg->code)
ret = -ENOMEM; ret = -ENOMEM;
else else
@ -555,15 +676,19 @@ int traceprobe_parse_probe_arg(struct trace_probe *tp, int i, char *arg,
{ {
struct probe_arg *parg = &tp->args[i]; struct probe_arg *parg = &tp->args[i];
char *body; char *body;
int ret;
/* Increment count for freeing args in error case */ /* Increment count for freeing args in error case */
tp->nr_args++; tp->nr_args++;
body = strchr(arg, '='); body = strchr(arg, '=');
if (body) { if (body) {
if (body - arg > MAX_ARG_NAME_LEN || body == arg) if (body - arg > MAX_ARG_NAME_LEN) {
trace_probe_log_err(0, ARG_NAME_TOO_LONG);
return -EINVAL; return -EINVAL;
} else if (body == arg) {
trace_probe_log_err(0, NO_ARG_NAME);
return -EINVAL;
}
parg->name = kmemdup_nul(arg, body - arg, GFP_KERNEL); parg->name = kmemdup_nul(arg, body - arg, GFP_KERNEL);
body++; body++;
} else { } else {
@ -575,22 +700,16 @@ int traceprobe_parse_probe_arg(struct trace_probe *tp, int i, char *arg,
return -ENOMEM; return -ENOMEM;
if (!is_good_name(parg->name)) { if (!is_good_name(parg->name)) {
pr_info("Invalid argument[%d] name: %s\n", trace_probe_log_err(0, BAD_ARG_NAME);
i, parg->name);
return -EINVAL; return -EINVAL;
} }
if (traceprobe_conflict_field_name(parg->name, tp->args, i)) { if (traceprobe_conflict_field_name(parg->name, tp->args, i)) {
pr_info("Argument[%d]: '%s' conflicts with another field.\n", trace_probe_log_err(0, USED_ARG_NAME);
i, parg->name);
return -EINVAL; return -EINVAL;
} }
/* Parse fetch argument */ /* Parse fetch argument */
ret = traceprobe_parse_probe_arg_body(body, &tp->size, parg, flags); return traceprobe_parse_probe_arg_body(body, &tp->size, parg, flags,
if (ret) body - arg);
pr_info("Parse error at argument[%d]. (%d)\n", i, ret);
return ret;
} }
void traceprobe_free_probe_arg(struct probe_arg *arg) void traceprobe_free_probe_arg(struct probe_arg *arg)

View File

@ -124,6 +124,7 @@ struct fetch_insn {
/* fetch + deref*N + store + mod + end <= 16, this allows N=12, enough */ /* fetch + deref*N + store + mod + end <= 16, this allows N=12, enough */
#define FETCH_INSN_MAX 16 #define FETCH_INSN_MAX 16
#define FETCH_TOKEN_COMM (-ECOMM)
/* Fetch type information table */ /* Fetch type information table */
struct fetch_type { struct fetch_type {
@ -280,8 +281,8 @@ extern int traceprobe_update_arg(struct probe_arg *arg);
extern void traceprobe_free_probe_arg(struct probe_arg *arg); extern void traceprobe_free_probe_arg(struct probe_arg *arg);
extern int traceprobe_split_symbol_offset(char *symbol, long *offset); extern int traceprobe_split_symbol_offset(char *symbol, long *offset);
extern int traceprobe_parse_event_name(const char **pevent, int traceprobe_parse_event_name(const char **pevent, const char **pgroup,
const char **pgroup, char *buf); char *buf, int offset);
extern int traceprobe_set_print_fmt(struct trace_probe *tp, bool is_return); extern int traceprobe_set_print_fmt(struct trace_probe *tp, bool is_return);
@ -298,3 +299,76 @@ extern void destroy_local_trace_uprobe(struct trace_event_call *event_call);
#endif #endif
extern int traceprobe_define_arg_fields(struct trace_event_call *event_call, extern int traceprobe_define_arg_fields(struct trace_event_call *event_call,
size_t offset, struct trace_probe *tp); size_t offset, struct trace_probe *tp);
#undef ERRORS
#define ERRORS \
C(FILE_NOT_FOUND, "Failed to find the given file"), \
C(NO_REGULAR_FILE, "Not a regular file"), \
C(BAD_REFCNT, "Invalid reference counter offset"), \
C(REFCNT_OPEN_BRACE, "Reference counter brace is not closed"), \
C(BAD_REFCNT_SUFFIX, "Reference counter has wrong suffix"), \
C(BAD_UPROBE_OFFS, "Invalid uprobe offset"), \
C(MAXACT_NO_KPROBE, "Maxactive is not for kprobe"), \
C(BAD_MAXACT, "Invalid maxactive number"), \
C(MAXACT_TOO_BIG, "Maxactive is too big"), \
C(BAD_PROBE_ADDR, "Invalid probed address or symbol"), \
C(BAD_RETPROBE, "Retprobe address must be an function entry"), \
C(NO_GROUP_NAME, "Group name is not specified"), \
C(GROUP_TOO_LONG, "Group name is too long"), \
C(BAD_GROUP_NAME, "Group name must follow the same rules as C identifiers"), \
C(NO_EVENT_NAME, "Event name is not specified"), \
C(EVENT_TOO_LONG, "Event name is too long"), \
C(BAD_EVENT_NAME, "Event name must follow the same rules as C identifiers"), \
C(RETVAL_ON_PROBE, "$retval is not available on probe"), \
C(BAD_STACK_NUM, "Invalid stack number"), \
C(BAD_ARG_NUM, "Invalid argument number"), \
C(BAD_VAR, "Invalid $-valiable specified"), \
C(BAD_REG_NAME, "Invalid register name"), \
C(BAD_MEM_ADDR, "Invalid memory address"), \
C(FILE_ON_KPROBE, "File offset is not available with kprobe"), \
C(BAD_FILE_OFFS, "Invalid file offset value"), \
C(SYM_ON_UPROBE, "Symbol is not available with uprobe"), \
C(TOO_MANY_OPS, "Dereference is too much nested"), \
C(DEREF_NEED_BRACE, "Dereference needs a brace"), \
C(BAD_DEREF_OFFS, "Invalid dereference offset"), \
C(DEREF_OPEN_BRACE, "Dereference brace is not closed"), \
C(COMM_CANT_DEREF, "$comm can not be dereferenced"), \
C(BAD_FETCH_ARG, "Invalid fetch argument"), \
C(ARRAY_NO_CLOSE, "Array is not closed"), \
C(BAD_ARRAY_SUFFIX, "Array has wrong suffix"), \
C(BAD_ARRAY_NUM, "Invalid array size"), \
C(ARRAY_TOO_BIG, "Array number is too big"), \
C(BAD_TYPE, "Unknown type is specified"), \
C(BAD_STRING, "String accepts only memory argument"), \
C(BAD_BITFIELD, "Invalid bitfield"), \
C(ARG_NAME_TOO_LONG, "Argument name is too long"), \
C(NO_ARG_NAME, "Argument name is not specified"), \
C(BAD_ARG_NAME, "Argument name must follow the same rules as C identifiers"), \
C(USED_ARG_NAME, "This argument name is already used"), \
C(ARG_TOO_LONG, "Argument expression is too long"), \
C(NO_ARG_BODY, "No argument expression"), \
C(BAD_INSN_BNDRY, "Probe point is not an instruction boundary"),\
C(FAIL_REG_PROBE, "Failed to register probe event"),
#undef C
#define C(a, b) TP_ERR_##a
/* Define TP_ERR_ */
enum { ERRORS };
/* Error text is defined in trace_probe.c */
struct trace_probe_log {
const char *subsystem;
const char **argv;
int argc;
int index;
};
void trace_probe_log_init(const char *subsystem, int argc, const char **argv);
void trace_probe_log_set_index(int index);
void trace_probe_log_clear(void);
void __trace_probe_log_err(int offset, int err);
#define trace_probe_log_err(offs, err) \
__trace_probe_log_err(offs, TP_ERR_##err)

View File

@ -88,7 +88,7 @@ stage3:
/* 3rd stage: store value to buffer */ /* 3rd stage: store value to buffer */
if (unlikely(!dest)) { if (unlikely(!dest)) {
if (code->op == FETCH_OP_ST_STRING) { if (code->op == FETCH_OP_ST_STRING) {
ret += fetch_store_strlen(val + code->offset); ret = fetch_store_strlen(val + code->offset);
code++; code++;
goto array; goto array;
} else } else

View File

@ -792,7 +792,10 @@ trace_selftest_startup_function_graph(struct tracer *trace,
/* check the trace buffer */ /* check the trace buffer */
ret = trace_test_buffer(&tr->trace_buffer, &count); ret = trace_test_buffer(&tr->trace_buffer, &count);
trace->reset(tr); /* Need to also simulate the tr->reset to remove this fgraph_ops */
tracing_stop_cmdline_record();
unregister_ftrace_graph(&fgraph_ops);
tracing_start(); tracing_start();
if (!ret && !count) { if (!ret && !count) {

View File

@ -156,7 +156,10 @@ fetch_store_string(unsigned long addr, void *dest, void *base)
if (unlikely(!maxlen)) if (unlikely(!maxlen))
return -ENOMEM; return -ENOMEM;
ret = strncpy_from_user(dst, src, maxlen); if (addr == FETCH_TOKEN_COMM)
ret = strlcpy(dst, current->comm, maxlen);
else
ret = strncpy_from_user(dst, src, maxlen);
if (ret >= 0) { if (ret >= 0) {
if (ret == maxlen) if (ret == maxlen)
dst[ret - 1] = '\0'; dst[ret - 1] = '\0';
@ -180,7 +183,10 @@ fetch_store_strlen(unsigned long addr)
int len; int len;
void __user *vaddr = (void __force __user *) addr; void __user *vaddr = (void __force __user *) addr;
len = strnlen_user(vaddr, MAX_STRING_SIZE); if (addr == FETCH_TOKEN_COMM)
len = strlen(current->comm) + 1;
else
len = strnlen_user(vaddr, MAX_STRING_SIZE);
return (len > MAX_STRING_SIZE) ? 0 : len; return (len > MAX_STRING_SIZE) ? 0 : len;
} }
@ -220,6 +226,9 @@ process_fetch_insn(struct fetch_insn *code, struct pt_regs *regs, void *dest,
case FETCH_OP_IMM: case FETCH_OP_IMM:
val = code->immediate; val = code->immediate;
break; break;
case FETCH_OP_COMM:
val = FETCH_TOKEN_COMM;
break;
case FETCH_OP_FOFFS: case FETCH_OP_FOFFS:
val = translate_user_vaddr(code->immediate); val = translate_user_vaddr(code->immediate);
break; break;
@ -457,13 +466,19 @@ static int trace_uprobe_create(int argc, const char **argv)
return -ECANCELED; return -ECANCELED;
} }
trace_probe_log_init("trace_uprobe", argc, argv);
trace_probe_log_set_index(1); /* filename is the 2nd argument */
*arg++ = '\0'; *arg++ = '\0';
ret = kern_path(filename, LOOKUP_FOLLOW, &path); ret = kern_path(filename, LOOKUP_FOLLOW, &path);
if (ret) { if (ret) {
trace_probe_log_err(0, FILE_NOT_FOUND);
kfree(filename); kfree(filename);
trace_probe_log_clear();
return ret; return ret;
} }
if (!d_is_reg(path.dentry)) { if (!d_is_reg(path.dentry)) {
trace_probe_log_err(0, NO_REGULAR_FILE);
ret = -EINVAL; ret = -EINVAL;
goto fail_address_parse; goto fail_address_parse;
} }
@ -472,9 +487,16 @@ static int trace_uprobe_create(int argc, const char **argv)
rctr = strchr(arg, '('); rctr = strchr(arg, '(');
if (rctr) { if (rctr) {
rctr_end = strchr(rctr, ')'); rctr_end = strchr(rctr, ')');
if (rctr > rctr_end || *(rctr_end + 1) != 0) { if (!rctr_end) {
ret = -EINVAL; ret = -EINVAL;
pr_info("Invalid reference counter offset.\n"); rctr_end = rctr + strlen(rctr);
trace_probe_log_err(rctr_end - filename,
REFCNT_OPEN_BRACE);
goto fail_address_parse;
} else if (rctr_end[1] != '\0') {
ret = -EINVAL;
trace_probe_log_err(rctr_end + 1 - filename,
BAD_REFCNT_SUFFIX);
goto fail_address_parse; goto fail_address_parse;
} }
@ -482,22 +504,23 @@ static int trace_uprobe_create(int argc, const char **argv)
*rctr_end = '\0'; *rctr_end = '\0';
ret = kstrtoul(rctr, 0, &ref_ctr_offset); ret = kstrtoul(rctr, 0, &ref_ctr_offset);
if (ret) { if (ret) {
pr_info("Invalid reference counter offset.\n"); trace_probe_log_err(rctr - filename, BAD_REFCNT);
goto fail_address_parse; goto fail_address_parse;
} }
} }
/* Parse uprobe offset. */ /* Parse uprobe offset. */
ret = kstrtoul(arg, 0, &offset); ret = kstrtoul(arg, 0, &offset);
if (ret) if (ret) {
trace_probe_log_err(arg - filename, BAD_UPROBE_OFFS);
goto fail_address_parse; goto fail_address_parse;
}
argc -= 2;
argv += 2;
/* setup a probe */ /* setup a probe */
trace_probe_log_set_index(0);
if (event) { if (event) {
ret = traceprobe_parse_event_name(&event, &group, buf); ret = traceprobe_parse_event_name(&event, &group, buf,
event - argv[0]);
if (ret) if (ret)
goto fail_address_parse; goto fail_address_parse;
} else { } else {
@ -519,6 +542,9 @@ static int trace_uprobe_create(int argc, const char **argv)
kfree(tail); kfree(tail);
} }
argc -= 2;
argv += 2;
tu = alloc_trace_uprobe(group, event, argc, is_return); tu = alloc_trace_uprobe(group, event, argc, is_return);
if (IS_ERR(tu)) { if (IS_ERR(tu)) {
ret = PTR_ERR(tu); ret = PTR_ERR(tu);
@ -539,6 +565,7 @@ static int trace_uprobe_create(int argc, const char **argv)
goto error; goto error;
} }
trace_probe_log_set_index(i + 2);
ret = traceprobe_parse_probe_arg(&tu->tp, i, tmp, ret = traceprobe_parse_probe_arg(&tu->tp, i, tmp,
is_return ? TPARG_FL_RETURN : 0); is_return ? TPARG_FL_RETURN : 0);
kfree(tmp); kfree(tmp);
@ -547,20 +574,20 @@ static int trace_uprobe_create(int argc, const char **argv)
} }
ret = register_trace_uprobe(tu); ret = register_trace_uprobe(tu);
if (ret) if (!ret)
goto error; goto out;
return 0;
error: error:
free_trace_uprobe(tu); free_trace_uprobe(tu);
out:
trace_probe_log_clear();
return ret; return ret;
fail_address_parse: fail_address_parse:
trace_probe_log_clear();
path_put(&path); path_put(&path);
kfree(filename); kfree(filename);
pr_info("Failed to parse address or file.\n");
return ret; return ret;
} }

View File

@ -0,0 +1,19 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# description: ftrace - test tracing error log support
fail() { #msg
echo $1
exit_fail
}
# event tracing is currently the only ftrace tracer that uses the
# tracing error_log, hence this check
if [ ! -f set_event ]; then
echo "event tracing is not supported"
exit_unsupported
fi
ftrace_errlog_check 'event filter parse error' '((sig >= 10 && sig < 15) || dsig ^== 17) && comm != bash' 'events/signal/signal_generate/filter'
exit 0

View File

@ -109,3 +109,15 @@ LOCALHOST=127.0.0.1
yield() { yield() {
ping $LOCALHOST -c 1 || sleep .001 || usleep 1 || sleep 1 ping $LOCALHOST -c 1 || sleep .001 || usleep 1 || sleep 1
} }
ftrace_errlog_check() { # err-prefix command-with-error-pos-by-^ command-file
pos=$(echo -n "${2%^*}" | wc -c) # error position
command=$(echo "$2" | tr -d ^)
echo "Test command: $command"
echo > error_log
(! echo "$command" > "$3" ) 2> /dev/null
grep "$1: error:" -A 3 error_log
N=$(tail -n 1 error_log | wc -c)
# " Command: " and "^\n" => 13
test $(expr 13 + $pos) -eq $N
}

View File

@ -0,0 +1,85 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# description: Kprobe event parser error log check
[ -f kprobe_events ] || exit_unsupported # this is configurable
[ -f error_log ] || exit_unsupported
check_error() { # command-with-error-pos-by-^
ftrace_errlog_check 'trace_kprobe' "$1" 'kprobe_events'
}
if grep -q 'r\[maxactive\]' README; then
check_error 'p^100 vfs_read' # MAXACT_NO_KPROBE
check_error 'r^1a111 vfs_read' # BAD_MAXACT
check_error 'r^100000 vfs_read' # MAXACT_TOO_BIG
fi
check_error 'p ^non_exist_func' # BAD_PROBE_ADDR (enoent)
check_error 'p ^hoge-fuga' # BAD_PROBE_ADDR (bad syntax)
check_error 'p ^hoge+1000-1000' # BAD_PROBE_ADDR (bad syntax)
check_error 'r ^vfs_read+10' # BAD_RETPROBE
check_error 'p:^/bar vfs_read' # NO_GROUP_NAME
check_error 'p:^12345678901234567890123456789012345678901234567890123456789012345/bar vfs_read' # GROUP_TOO_LONG
check_error 'p:^foo.1/bar vfs_read' # BAD_GROUP_NAME
check_error 'p:foo/^ vfs_read' # NO_EVENT_NAME
check_error 'p:foo/^12345678901234567890123456789012345678901234567890123456789012345 vfs_read' # EVENT_TOO_LONG
check_error 'p:foo/^bar.1 vfs_read' # BAD_EVENT_NAME
check_error 'p vfs_read ^$retval' # RETVAL_ON_PROBE
check_error 'p vfs_read ^$stack10000' # BAD_STACK_NUM
if grep -q '$arg<N>' README; then
check_error 'p vfs_read ^$arg10000' # BAD_ARG_NUM
fi
check_error 'p vfs_read ^$none_var' # BAD_VAR
check_error 'p vfs_read ^%none_reg' # BAD_REG_NAME
check_error 'p vfs_read ^@12345678abcde' # BAD_MEM_ADDR
check_error 'p vfs_read ^@+10' # FILE_ON_KPROBE
check_error 'p vfs_read ^+0@0)' # DEREF_NEED_BRACE
check_error 'p vfs_read ^+0ab1(@0)' # BAD_DEREF_OFFS
check_error 'p vfs_read +0(+0(@0^)' # DEREF_OPEN_BRACE
if grep -A1 "fetcharg:" README | grep -q '\$comm' ; then
check_error 'p vfs_read +0(^$comm)' # COMM_CANT_DEREF
fi
check_error 'p vfs_read ^&1' # BAD_FETCH_ARG
# We've introduced this limitation with array support
if grep -q ' <type>\\\[<array-size>\\\]' README; then
check_error 'p vfs_read +0(^+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(+0(@0))))))))))))))' # TOO_MANY_OPS?
check_error 'p vfs_read +0(@11):u8[10^' # ARRAY_NO_CLOSE
check_error 'p vfs_read +0(@11):u8[10]^a' # BAD_ARRAY_SUFFIX
check_error 'p vfs_read +0(@11):u8[^10a]' # BAD_ARRAY_NUM
check_error 'p vfs_read +0(@11):u8[^256]' # ARRAY_TOO_BIG
fi
check_error 'p vfs_read @11:^unknown_type' # BAD_TYPE
check_error 'p vfs_read $stack0:^string' # BAD_STRING
check_error 'p vfs_read @11:^b10@a/16' # BAD_BITFIELD
check_error 'p vfs_read ^arg123456789012345678901234567890=@11' # ARG_NAME_TOO_LOG
check_error 'p vfs_read ^=@11' # NO_ARG_NAME
check_error 'p vfs_read ^var.1=@11' # BAD_ARG_NAME
check_error 'p vfs_read var1=@11 ^var1=@12' # USED_ARG_NAME
check_error 'p vfs_read ^+1234567(+1234567(+1234567(+1234567(+1234567(+1234567(@1234))))))' # ARG_TOO_LONG
check_error 'p vfs_read arg1=^' # NO_ARG_BODY
# instruction boundary check is valid on x86 (at this moment)
case $(uname -m) in
x86_64|i[3456]86)
echo 'p vfs_read' > kprobe_events
if grep -q FTRACE ../kprobes/list ; then
check_error 'p ^vfs_read+3' # BAD_INSN_BNDRY (only if function-tracer is enabled)
fi
;;
esac
exit 0

View File

@ -0,0 +1,23 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# description: Uprobe event parser error log check
[ -f uprobe_events ] || exit_unsupported # this is configurable
[ -f error_log ] || exit_unsupported
check_error() { # command-with-error-pos-by-^
ftrace_errlog_check 'trace_uprobe' "$1" 'uprobe_events'
}
check_error 'p ^/non_exist_file:100' # FILE_NOT_FOUND
check_error 'p ^/sys:100' # NO_REGULAR_FILE
check_error 'p /bin/sh:^10a' # BAD_UPROBE_OFFS
check_error 'p /bin/sh:10(^1a)' # BAD_REFCNT
check_error 'p /bin/sh:10(10^' # REFCNT_OPEN_BRACE
check_error 'p /bin/sh:10(10)^a' # BAD_REFCNT_SUFFIX
check_error 'p /bin/sh:10 ^@+ab' # BAD_FILE_OFFS
check_error 'p /bin/sh:10 ^@symbol' # SYM_ON_UPROBE
exit 0

View File

@ -1,28 +0,0 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
# description: event trigger - test extended error support
fail() { #msg
echo $1
exit_fail
}
if [ ! -f set_event ]; then
echo "event tracing is not supported"
exit_unsupported
fi
if [ ! -f synthetic_events ]; then
echo "synthetic event is not supported"
exit_unsupported
fi
echo "Test extended error support"
echo 'hist:keys=pid:ts0=common_timestamp.usecs if comm=="ping"' > events/sched/sched_wakeup/trigger
! echo 'hist:keys=pid:ts0=common_timestamp.usecs if comm=="ping"' >> events/sched/sched_wakeup/trigger 2> /dev/null
if ! grep -q "ERROR:" events/sched/sched_wakeup/hist; then
fail "Failed to generate extended error in histogram"
fi
exit 0