Messages in this thread Patch in this message | | | From | Hui Zhu <> | Subject | [RFC 1/4] BloodTest: kernel status | Date | Fri, 13 Oct 2017 16:56:02 +0800 |
| |
This patch include the base framework of BloodTest and get the kernel status function.
The interface is in "/sys/kernel/debug/bloodtest". Access "test" will call bt_insert that will call all start record function. And register a hrtimer to call bt_pullout to stop record.
bt_insert and bt_pullout will call analysing tools.
Signed-off-by: Hui Zhu <zhuhui@xiaomi.com> --- fs/proc/stat.c | 8 +-- include/linux/kernel_stat.h | 3 ++ init/Kconfig | 3 ++ kernel/Makefile | 2 + kernel/bloodtest/Makefile | 1 + kernel/bloodtest/core.c | 117 +++++++++++++++++++++++++++++++++++++++++ kernel/bloodtest/internal.h | 19 +++++++ kernel/bloodtest/kernel_stat.c | 62 ++++++++++++++++++++++ 8 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 kernel/bloodtest/Makefile create mode 100644 kernel/bloodtest/core.c create mode 100644 kernel/bloodtest/internal.h create mode 100644 kernel/bloodtest/kernel_stat.c
diff --git a/fs/proc/stat.c b/fs/proc/stat.c index bd4e55f..c6f4fd4 100644 --- a/fs/proc/stat.c +++ b/fs/proc/stat.c @@ -22,7 +22,7 @@ #ifdef arch_idle_time -static u64 get_idle_time(int cpu) +u64 get_idle_time(int cpu) { u64 idle; @@ -32,7 +32,7 @@ static u64 get_idle_time(int cpu) return idle; } -static u64 get_iowait_time(int cpu) +u64 get_iowait_time(int cpu) { u64 iowait; @@ -44,7 +44,7 @@ static u64 get_iowait_time(int cpu) #else -static u64 get_idle_time(int cpu) +u64 get_idle_time(int cpu) { u64 idle, idle_usecs = -1ULL; @@ -60,7 +60,7 @@ static u64 get_idle_time(int cpu) return idle; } -static u64 get_iowait_time(int cpu) +u64 get_iowait_time(int cpu) { u64 iowait, iowait_usecs = -1ULL; diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h index 66be8b6..bf8d3f0 100644 --- a/include/linux/kernel_stat.h +++ b/include/linux/kernel_stat.h @@ -96,4 +96,7 @@ static inline void account_process_tick(struct task_struct *tsk, int user) extern void account_idle_ticks(unsigned long ticks); +extern u64 get_idle_time(int cpu); +extern u64 get_iowait_time(int cpu); + #endif /* _LINUX_KERNEL_STAT_H */ diff --git a/init/Kconfig b/init/Kconfig index 78cb246..f63550c 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1909,3 +1909,6 @@ config ASN1 functions to call on what tags. source "kernel/Kconfig.locks" + +config BLOODTEST + bool "Blood test" diff --git a/kernel/Makefile b/kernel/Makefile index ed470aa..2a04e42 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -103,6 +103,8 @@ obj-$(CONFIG_BPF) += bpf/ obj-$(CONFIG_PERF_EVENTS) += events/ +obj-$(CONFIG_BLOODTEST) += bloodtest/ + obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o obj-$(CONFIG_PADATA) += padata.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o diff --git a/kernel/bloodtest/Makefile b/kernel/bloodtest/Makefile new file mode 100644 index 0000000..7f289af --- /dev/null +++ b/kernel/bloodtest/Makefile @@ -0,0 +1 @@ +obj-y = core.o kernel_stat.o diff --git a/kernel/bloodtest/core.c b/kernel/bloodtest/core.c new file mode 100644 index 0000000..7b39cbb --- /dev/null +++ b/kernel/bloodtest/core.c @@ -0,0 +1,117 @@ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/hrtimer.h> +#include <linux/debugfs.h> +#include <linux/fs.h> + +#include "internal.h" + +enum bt_stat_enum bt_stat; +DEFINE_SPINLOCK(bt_lock); + +static DECLARE_WAIT_QUEUE_HEAD(bt_wq); +static struct hrtimer bt_timer; +static ktime_t bt_ktime; + +static bool is_bt_stat(enum bt_stat_enum stat) +{ + unsigned long flags; + bool ret = false; + + spin_lock_irqsave(&bt_lock, flags); + if (bt_stat == stat) + ret = true; + spin_unlock_irqrestore(&bt_lock, flags); + + return ret; +} + +/* This function must be called under the protection of bt_lock. */ +static void bt_insert(void) +{ + bt_stat = bt_running; + + bt_insert_kernel_stat(); +} + +/* This function must be called under the protection of bt_lock. */ +static void bt_pullout(void) +{ + bt_pullout_kernel_stat(); + + bt_stat = bt_done; +} + +/* This function must be called under the protection of bt_lock. */ +static void bt_report(struct seq_file *p) +{ + bt_report_kernel_stat(p); +} + +static enum hrtimer_restart bt_timer_fn(struct hrtimer *data) +{ + spin_lock(&bt_lock); + bt_pullout(); + spin_unlock(&bt_lock); + + wake_up_interruptible_all(&bt_wq); + + return HRTIMER_NORESTART; +} + +static int test_show(struct seq_file *p, void *v) +{ + int ret = 0; + + spin_lock(&bt_lock); + if (bt_stat == bt_running) + goto wait; + + hrtimer_start(&bt_timer, bt_ktime, HRTIMER_MODE_REL); + bt_insert(); + +wait: + spin_unlock(&bt_lock); + ret = wait_event_interruptible(bt_wq, is_bt_stat(bt_done)); + if (ret) + goto out; + + spin_lock(&bt_lock); + bt_report(p); + spin_unlock(&bt_lock); + +out: + return ret; +} + +static int test_open(struct inode *inode, struct file *file) +{ + return single_open(file, test_show, NULL); +} + +static const struct file_operations test_fops = { + .open = test_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init bt_init(void) +{ + struct dentry *d, *t; + + d = debugfs_create_dir("bloodtest", NULL); + if (!d) + return -ENOMEM; + t = debugfs_create_file("test", S_IRUSR, d, NULL, &test_fops); + if (!t) + return -ENOMEM; + + hrtimer_init(&bt_timer, CLOCK_REALTIME, HRTIMER_MODE_REL); + bt_timer.function = bt_timer_fn; + bt_ktime = ktime_set(1, 0); + + return 0; +} + +core_initcall(bt_init); diff --git a/kernel/bloodtest/internal.h b/kernel/bloodtest/internal.h new file mode 100644 index 0000000..48faf4d --- /dev/null +++ b/kernel/bloodtest/internal.h @@ -0,0 +1,19 @@ +#ifndef _KERNEL_BLOODTEST_INTERNAL_H +#define _KERNEL_BLOODTEST_INTERNAL_H + +#include <linux/seq_file.h> + +enum bt_stat_enum { + bt_empty = 0, + bt_done, + bt_running, +}; + +extern enum bt_stat_enum bt_stat; +extern spinlock_t bt_lock; + +extern void bt_insert_kernel_stat(void); +extern void bt_pullout_kernel_stat(void); +extern void bt_report_kernel_stat(struct seq_file *p); + +#endif /* _KERNEL_BLOODTEST_INTERNAL_H */ diff --git a/kernel/bloodtest/kernel_stat.c b/kernel/bloodtest/kernel_stat.c new file mode 100644 index 0000000..7c37403 --- /dev/null +++ b/kernel/bloodtest/kernel_stat.c @@ -0,0 +1,62 @@ +#include <linux/kernel_stat.h> + +#include "internal.h" + +struct kernel_cpustat_rec { + u64 rec[2][NR_STATS]; +}; +static DEFINE_PER_CPU(struct kernel_cpustat_rec, cpustat_rec); + +#define kstat(cpu) (per_cpu(cpustat_rec, cpu).rec) + +static void record_kernel_stat(int rec) +{ + int cpu; + unsigned long flags; + + local_irq_save(flags); + for_each_possible_cpu(cpu) { + enum cpu_usage_stat j; + + for (j = CPUTIME_USER; j < NR_STATS; j++) { + if (j == CPUTIME_IDLE) + kstat(cpu)[rec][j] = get_idle_time(cpu); + else if (j == CPUTIME_IOWAIT) + kstat(cpu)[rec][j] = get_iowait_time(cpu); + else + kstat(cpu)[rec][j] + = kcpustat_cpu(cpu).cpustat[j]; + } + } + local_irq_restore(flags); +} + +static unsigned long long +get_kernel_stat(int cpu, enum cpu_usage_stat stat) +{ + return (unsigned long long)(kstat(cpu)[1][stat] - kstat(cpu)[0][stat]); +} + +void bt_insert_kernel_stat(void) +{ + record_kernel_stat(0); +} + +void bt_pullout_kernel_stat(void) +{ + record_kernel_stat(1); +} + +void bt_report_kernel_stat(struct seq_file *p) +{ + int cpu; + + for_each_possible_cpu(cpu) { + enum cpu_usage_stat j; + + seq_printf(p, "cpu%d", cpu); + for (j = CPUTIME_USER; j < NR_STATS; j++) + seq_put_decimal_ull(p, " ", get_kernel_stat(cpu, j)); + seq_putc(p, '\n'); + } +} -- 1.9.1
| |