mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
x86/calldepth: Add ret/call counting for debug
Add a debuigfs mechanism to validate the accounting, e.g. vs. call/ret balance and to gather statistics about the stuffing to call ratio. Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://lore.kernel.org/r/20220915111148.204285506@infradead.org
This commit is contained in:
parent
bbaceb189a
commit
f5c1bb2afe
3 changed files with 91 additions and 5 deletions
|
@ -57,6 +57,22 @@
|
||||||
#define RET_DEPTH_INIT_FROM_CALL 0xfc00000000000000ULL
|
#define RET_DEPTH_INIT_FROM_CALL 0xfc00000000000000ULL
|
||||||
#define RET_DEPTH_CREDIT 0xffffffffffffffffULL
|
#define RET_DEPTH_CREDIT 0xffffffffffffffffULL
|
||||||
|
|
||||||
|
#ifdef CONFIG_CALL_THUNKS_DEBUG
|
||||||
|
# define CALL_THUNKS_DEBUG_INC_CALLS \
|
||||||
|
incq %gs:__x86_call_count;
|
||||||
|
# define CALL_THUNKS_DEBUG_INC_RETS \
|
||||||
|
incq %gs:__x86_ret_count;
|
||||||
|
# define CALL_THUNKS_DEBUG_INC_STUFFS \
|
||||||
|
incq %gs:__x86_stuffs_count;
|
||||||
|
# define CALL_THUNKS_DEBUG_INC_CTXSW \
|
||||||
|
incq %gs:__x86_ctxsw_count;
|
||||||
|
#else
|
||||||
|
# define CALL_THUNKS_DEBUG_INC_CALLS
|
||||||
|
# define CALL_THUNKS_DEBUG_INC_RETS
|
||||||
|
# define CALL_THUNKS_DEBUG_INC_STUFFS
|
||||||
|
# define CALL_THUNKS_DEBUG_INC_CTXSW
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_CALL_DEPTH_TRACKING) && !defined(COMPILE_OFFSETS)
|
#if defined(CONFIG_CALL_DEPTH_TRACKING) && !defined(COMPILE_OFFSETS)
|
||||||
|
|
||||||
#include <asm/asm-offsets.h>
|
#include <asm/asm-offsets.h>
|
||||||
|
@ -75,18 +91,23 @@
|
||||||
#define RESET_CALL_DEPTH_FROM_CALL \
|
#define RESET_CALL_DEPTH_FROM_CALL \
|
||||||
mov $0xfc, %rax; \
|
mov $0xfc, %rax; \
|
||||||
shl $56, %rax; \
|
shl $56, %rax; \
|
||||||
movq %rax, PER_CPU_VAR(pcpu_hot + X86_call_depth);
|
movq %rax, PER_CPU_VAR(pcpu_hot + X86_call_depth); \
|
||||||
|
CALL_THUNKS_DEBUG_INC_CALLS
|
||||||
|
|
||||||
#define INCREMENT_CALL_DEPTH \
|
#define INCREMENT_CALL_DEPTH \
|
||||||
sarq $5, %gs:pcpu_hot + X86_call_depth;
|
sarq $5, %gs:pcpu_hot + X86_call_depth; \
|
||||||
|
CALL_THUNKS_DEBUG_INC_CALLS
|
||||||
|
|
||||||
#define ASM_INCREMENT_CALL_DEPTH \
|
#define ASM_INCREMENT_CALL_DEPTH \
|
||||||
sarq $5, PER_CPU_VAR(pcpu_hot + X86_call_depth);
|
sarq $5, PER_CPU_VAR(pcpu_hot + X86_call_depth); \
|
||||||
|
CALL_THUNKS_DEBUG_INC_CALLS
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#define CREDIT_CALL_DEPTH
|
#define CREDIT_CALL_DEPTH
|
||||||
|
#define ASM_CREDIT_CALL_DEPTH
|
||||||
#define RESET_CALL_DEPTH
|
#define RESET_CALL_DEPTH
|
||||||
#define INCREMENT_CALL_DEPTH
|
#define INCREMENT_CALL_DEPTH
|
||||||
|
#define ASM_INCREMENT_CALL_DEPTH
|
||||||
#define RESET_CALL_DEPTH_FROM_CALL
|
#define RESET_CALL_DEPTH_FROM_CALL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -137,7 +158,8 @@
|
||||||
jnz 771b; \
|
jnz 771b; \
|
||||||
/* barrier for jnz misprediction */ \
|
/* barrier for jnz misprediction */ \
|
||||||
lfence; \
|
lfence; \
|
||||||
ASM_CREDIT_CALL_DEPTH
|
ASM_CREDIT_CALL_DEPTH \
|
||||||
|
CALL_THUNKS_DEBUG_INC_CTXSW
|
||||||
#else
|
#else
|
||||||
/*
|
/*
|
||||||
* i386 doesn't unconditionally have LFENCE, as such it can't
|
* i386 doesn't unconditionally have LFENCE, as such it can't
|
||||||
|
@ -321,6 +343,12 @@ static inline void x86_set_skl_return_thunk(void)
|
||||||
{
|
{
|
||||||
x86_return_thunk = &__x86_return_skl;
|
x86_return_thunk = &__x86_return_skl;
|
||||||
}
|
}
|
||||||
|
#ifdef CONFIG_CALL_THUNKS_DEBUG
|
||||||
|
DECLARE_PER_CPU(u64, __x86_call_count);
|
||||||
|
DECLARE_PER_CPU(u64, __x86_ret_count);
|
||||||
|
DECLARE_PER_CPU(u64, __x86_stuffs_count);
|
||||||
|
DECLARE_PER_CPU(u64, __x86_ctxsw_count);
|
||||||
|
#endif
|
||||||
#else
|
#else
|
||||||
static inline void x86_set_skl_return_thunk(void) {}
|
static inline void x86_set_skl_return_thunk(void) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#define pr_fmt(fmt) "callthunks: " fmt
|
#define pr_fmt(fmt) "callthunks: " fmt
|
||||||
|
|
||||||
|
#include <linux/debugfs.h>
|
||||||
#include <linux/kallsyms.h>
|
#include <linux/kallsyms.h>
|
||||||
#include <linux/memory.h>
|
#include <linux/memory.h>
|
||||||
#include <linux/moduleloader.h>
|
#include <linux/moduleloader.h>
|
||||||
|
@ -35,6 +36,15 @@ static int __init debug_thunks(char *str)
|
||||||
}
|
}
|
||||||
__setup("debug-callthunks", debug_thunks);
|
__setup("debug-callthunks", debug_thunks);
|
||||||
|
|
||||||
|
#ifdef CONFIG_CALL_THUNKS_DEBUG
|
||||||
|
DEFINE_PER_CPU(u64, __x86_call_count);
|
||||||
|
DEFINE_PER_CPU(u64, __x86_ret_count);
|
||||||
|
DEFINE_PER_CPU(u64, __x86_stuffs_count);
|
||||||
|
DEFINE_PER_CPU(u64, __x86_ctxsw_count);
|
||||||
|
EXPORT_SYMBOL_GPL(__x86_ctxsw_count);
|
||||||
|
EXPORT_SYMBOL_GPL(__x86_call_count);
|
||||||
|
#endif
|
||||||
|
|
||||||
extern s32 __call_sites[], __call_sites_end[];
|
extern s32 __call_sites[], __call_sites_end[];
|
||||||
|
|
||||||
struct thunk_desc {
|
struct thunk_desc {
|
||||||
|
@ -283,3 +293,46 @@ void noinline callthunks_patch_module_calls(struct callthunk_sites *cs,
|
||||||
mutex_unlock(&text_mutex);
|
mutex_unlock(&text_mutex);
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_MODULES */
|
#endif /* CONFIG_MODULES */
|
||||||
|
|
||||||
|
#if defined(CONFIG_CALL_THUNKS_DEBUG) && defined(CONFIG_DEBUG_FS)
|
||||||
|
static int callthunks_debug_show(struct seq_file *m, void *p)
|
||||||
|
{
|
||||||
|
unsigned long cpu = (unsigned long)m->private;
|
||||||
|
|
||||||
|
seq_printf(m, "C: %16llu R: %16llu S: %16llu X: %16llu\n,",
|
||||||
|
per_cpu(__x86_call_count, cpu),
|
||||||
|
per_cpu(__x86_ret_count, cpu),
|
||||||
|
per_cpu(__x86_stuffs_count, cpu),
|
||||||
|
per_cpu(__x86_ctxsw_count, cpu));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int callthunks_debug_open(struct inode *inode, struct file *file)
|
||||||
|
{
|
||||||
|
return single_open(file, callthunks_debug_show, inode->i_private);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations dfs_ops = {
|
||||||
|
.open = callthunks_debug_open,
|
||||||
|
.read = seq_read,
|
||||||
|
.llseek = seq_lseek,
|
||||||
|
.release = single_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init callthunks_debugfs_init(void)
|
||||||
|
{
|
||||||
|
struct dentry *dir;
|
||||||
|
unsigned long cpu;
|
||||||
|
|
||||||
|
dir = debugfs_create_dir("callthunks", NULL);
|
||||||
|
for_each_possible_cpu(cpu) {
|
||||||
|
void *arg = (void *)cpu;
|
||||||
|
char name [10];
|
||||||
|
|
||||||
|
sprintf(name, "cpu%lu", cpu);
|
||||||
|
debugfs_create_file(name, 0644, dir, arg, &dfs_ops);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
__initcall(callthunks_debugfs_init);
|
||||||
|
#endif
|
||||||
|
|
|
@ -203,13 +203,18 @@ EXPORT_SYMBOL(__x86_return_thunk)
|
||||||
.align 64
|
.align 64
|
||||||
SYM_FUNC_START(__x86_return_skl)
|
SYM_FUNC_START(__x86_return_skl)
|
||||||
ANNOTATE_NOENDBR
|
ANNOTATE_NOENDBR
|
||||||
/* Keep the hotpath in a 16byte I-fetch */
|
/*
|
||||||
|
* Keep the hotpath in a 16byte I-fetch for the non-debug
|
||||||
|
* case.
|
||||||
|
*/
|
||||||
|
CALL_THUNKS_DEBUG_INC_RETS
|
||||||
shlq $5, PER_CPU_VAR(pcpu_hot + X86_call_depth)
|
shlq $5, PER_CPU_VAR(pcpu_hot + X86_call_depth)
|
||||||
jz 1f
|
jz 1f
|
||||||
ANNOTATE_UNRET_SAFE
|
ANNOTATE_UNRET_SAFE
|
||||||
ret
|
ret
|
||||||
int3
|
int3
|
||||||
1:
|
1:
|
||||||
|
CALL_THUNKS_DEBUG_INC_STUFFS
|
||||||
.rept 16
|
.rept 16
|
||||||
ANNOTATE_INTRA_FUNCTION_CALL
|
ANNOTATE_INTRA_FUNCTION_CALL
|
||||||
call 2f
|
call 2f
|
||||||
|
|
Loading…
Add table
Reference in a new issue