Performance Stats: Kernel patch

From: Maxim Uvarov
Date: Mon Apr 09 2007 - 10:22:08 EST


Hello collogues,

Patches make available to the user the following new per-process (thread) performance statistics:

* Involuntary Context Switches
* Voluntary Context Switches
* Number of system calls

This data is useful for detecting hyperactivity patterns between processes.

I have attached 3 patches perf_stat.patch, perf_stat_x86_64.patch and perf_stat_ppc.patch for linux-2.6.21-rc5
and corrected all notes which were before. Do you have any objections? I need your opinion again.

Best regards,
Maxim Uvarov.









Patch adds Process Performance Statistics.

arch/i386/kernel/asm-offsets.c | 3 +++
arch/i386/kernel/entry.S | 6 ++++++
fs/proc/array.c | 17 +++++++++++++++++
include/asm-i386/thread_info.h | 5 +++--
kernel/fork.c | 4 ++++
lib/Kconfig.debug | 15 +++++++++++++++
6 files changed, 48 insertions(+), 2 deletions(-)

Index: linux-2.6.21-rc5/fs/proc/array.c
===================================================================
--- linux-2.6.21-rc5.orig/fs/proc/array.c
+++ linux-2.6.21-rc5/fs/proc/array.c
@@ -291,6 +291,20 @@ static inline char *task_cap(struct task
cap_t(p->cap_effective));
}

+#ifdef CONFIG_THREAD_PERF_STAT
+static inline char *task_perf(struct task_struct *p, char *buffer)
+{
+#ifdef CONFIG_THREAD_PERF_STAT_SYSC
+ buffer += sprintf(buffer, "Syscalls:\t%lu\n", p->thread_info->sysc_cnt);
+#endif /* CONFIG_THREAD_PERF_STAT_SYSC */
+
+ return buffer + sprintf(buffer, "Nvcsw:\t%lu\n"
+ "Nivcsw:\t%lu\n",
+ p->nvcsw,
+ p->nivcsw);
+}
+#endif /* CONFIG_THREAD_PERF_STAT */
+
int proc_pid_status(struct task_struct *task, char * buffer)
{
char * orig = buffer;
@@ -309,6 +323,9 @@ int proc_pid_status(struct task_struct *
#if defined(CONFIG_S390)
buffer = task_show_regs(task, buffer);
#endif
+#ifdef CONFIG_THREAD_PERF_STAT
+ buffer = task_perf(task, buffer);
+#endif /* CONFIG_THREAD_PERF_STAT */
return buffer - orig;
}

Index: linux-2.6.21-rc5/kernel/fork.c
===================================================================
--- linux-2.6.21-rc5.orig/kernel/fork.c
+++ linux-2.6.21-rc5/kernel/fork.c
@@ -1044,6 +1044,10 @@ static struct task_struct *copy_process(
p->syscr = 0; /* I/O counter: read syscalls */
p->syscw = 0; /* I/O counter: write syscalls */
#endif
+#ifdef CONFIG_THREAD_PERF_STAT_SYSC
+ p->thread_info->sysc_cnt = 0; /* Syscall counter: total numbers of syscalls */
+#endif /* CONFIG_THREAD_PERF_STAT_SYSC */
+
task_io_accounting_init(p);
acct_clear_integrals(p);

Index: linux-2.6.21-rc5/lib/Kconfig.debug
===================================================================
--- linux-2.6.21-rc5.orig/lib/Kconfig.debug
+++ linux-2.6.21-rc5/lib/Kconfig.debug
@@ -446,3 +446,18 @@ config FAULT_INJECTION_STACKTRACE_FILTER
select FRAME_POINTER
help
Provide stacktrace filter for fault-injection capabilities
+
+config THREAD_PERF_STAT
+ bool "Per-process (thread) performance statistics"
+ help
+ Make available to the user the following per-process (thread) performance statistics:
+ * Number of involuntary context switches
+ * Number of voluntary context switches
+ * Number of system calls (optional)
+ This information is available via /proc/PID/status.
+
+config THREAD_PERF_STAT_SYSC
+ bool "Enable syscall counter"
+ depends on THREAD_PERF_STAT && X86_32
+ help
+ This option adds a syscall counter to /proc/PID/status.
Index: linux-2.6.21-rc5/arch/i386/kernel/entry.S
===================================================================
--- linux-2.6.21-rc5.orig/arch/i386/kernel/entry.S
+++ linux-2.6.21-rc5/arch/i386/kernel/entry.S
@@ -342,6 +342,9 @@ sysenter_past_esp:
jae syscall_badsys
call *sys_call_table(,%eax,4)
movl %eax,PT_EAX(%esp)
+#ifdef CONFIG_THREAD_PERF_STAT_SYSC
+ incl TI_sysc_cnt(%ebp) # Increment syscalls counter
+#endif /* CONFIG_THREAD_PERF_STAT_SYSC */
DISABLE_INTERRUPTS(CLBR_ECX|CLBR_EDX)
TRACE_IRQS_OFF
movl TI_flags(%ebp), %ecx
@@ -385,6 +388,9 @@ syscall_call:
call *sys_call_table(,%eax,4)
movl %eax,PT_EAX(%esp) # store the return value
syscall_exit:
+#ifdef CONFIG_THREAD_PERF_STAT_SYSC
+ incl TI_sysc_cnt(%ebp) # Increment syscalls counter
+#endif /* CONFIG_THREAD_PERF_STAT_SYSC */
DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt
# setting need_resched or sigpending
# between sampling and the iret
Index: linux-2.6.21-rc5/arch/i386/kernel/asm-offsets.c
===================================================================
--- linux-2.6.21-rc5.orig/arch/i386/kernel/asm-offsets.c
+++ linux-2.6.21-rc5/arch/i386/kernel/asm-offsets.c
@@ -56,6 +56,9 @@ void foo(void)
OFFSET(TI_addr_limit, thread_info, addr_limit);
OFFSET(TI_restart_block, thread_info, restart_block);
OFFSET(TI_sysenter_return, thread_info, sysenter_return);
+#ifdef CONFIG_THREAD_PERF_STAT_SYSC
+ OFFSET(TI_sysc_cnt, thread_info, sysc_cnt);
+#endif /* CONFIG_THREAD_PERF_STAT_SYSC */
BLANK();

OFFSET(GDS_size, Xgt_desc_struct, size);
Index: linux-2.6.21-rc5/include/asm-i386/thread_info.h
===================================================================
--- linux-2.6.21-rc5.orig/include/asm-i386/thread_info.h
+++ linux-2.6.21-rc5/include/asm-i386/thread_info.h
@@ -31,8 +31,9 @@ struct thread_info {
unsigned long status; /* thread-synchronous flags */
__u32 cpu; /* current CPU */
int preempt_count; /* 0 => preemptable, <0 => BUG */
-
-
+#ifdef CONFIG_THREAD_PERF_STAT_SYSC
+ unsigned long sysc_cnt; /* Syscall counter */
+#endif /* CONFIG_THREAD_PERF_STAT_SYSC */
mm_segment_t addr_limit; /* thread address space:
0-0xBFFFFFFF for user-thead
0-0xFFFFFFFF for kernel-thread
Index: linux-2.6.21-rc5/lib/Kconfig.debug
===================================================================
--- linux-2.6.21-rc5.orig/lib/Kconfig.debug
+++ linux-2.6.21-rc5/lib/Kconfig.debug
@@ -458,6 +458,6 @@ config THREAD_PERF_STAT

config THREAD_PERF_STAT_SYSC
bool "Enable syscall counter"
- depends on THREAD_PERF_STAT && X86_32
+ depends on THREAD_PERF_STAT && X86
help
This option adds a syscall counter to /proc/PID/status.
Index: linux-2.6.21-rc5/include/asm-x86_64/thread_info.h
===================================================================
--- linux-2.6.21-rc5.orig/include/asm-x86_64/thread_info.h
+++ linux-2.6.21-rc5/include/asm-x86_64/thread_info.h
@@ -30,7 +30,9 @@ struct thread_info {
__u32 status; /* thread synchronous flags */
__u32 cpu; /* current CPU */
int preempt_count; /* 0 => preemptable, <0 => BUG */
-
+#ifdef CONFIG_THREAD_PERF_STAT_SYSC
+ unsigned long sysc_cnt; /* Syscall counter */
+#endif /* CONFIG_THREAD_PERF_STAT_SYSC */
mm_segment_t addr_limit;
struct restart_block restart_block;
};
Index: linux-2.6.21-rc5/arch/x86_64/kernel/entry.S
===================================================================
--- linux-2.6.21-rc5.orig/arch/x86_64/kernel/entry.S
+++ linux-2.6.21-rc5/arch/x86_64/kernel/entry.S
@@ -236,6 +236,11 @@ ENTRY(system_call)
movq %r10,%rcx
call *sys_call_table(,%rax,8) # XXX: rip relative
movq %rax,RAX-ARGOFFSET(%rsp)
+#ifdef CONFIG_THREAD_PERF_STAT_SYSC
+ GET_THREAD_INFO(%rcx)
+ addq $1, threadinfo_sysc_cnt(%rcx) # Increment syscalls counter
+#endif /* CONFIG_THREAD_PERF_STAT_SYSC */
+
/*
* Syscall return path ending with SYSRET (fast path)
* Has incomplete stack frame and undefined top of stack.
@@ -319,6 +324,10 @@ tracesys:
call *sys_call_table(,%rax,8)
1: movq %rax,RAX-ARGOFFSET(%rsp)
/* Use IRET because user could have changed frame */
+#ifdef CONFIG_THREAD_PERF_STAT_SYSC
+ GET_THREAD_INFO(%rcx)
+ addq $1, threadinfo_sysc_cnt(%rcx) # Increment syscalls counter
+#endif /* CONFIG_THREAD_PERF_STAT_SYSC */

/*
* Syscall return path ending with IRET.
Index: linux-2.6.21-rc5/arch/x86_64/kernel/asm-offsets.c
===================================================================
--- linux-2.6.21-rc5.orig/arch/x86_64/kernel/asm-offsets.c
+++ linux-2.6.21-rc5/arch/x86_64/kernel/asm-offsets.c
@@ -35,6 +35,9 @@ int main(void)
ENTRY(addr_limit);
ENTRY(preempt_count);
ENTRY(status);
+#ifdef CONFIG_THREAD_PERF_STAT_SYSC
+ ENTRY(sysc_cnt);
+#endif /* CONFIG_THREAD_PERF_STAT_SYSC */
BLANK();
#undef ENTRY
#define ENTRY(entry) DEFINE(pda_ ## entry, offsetof(struct x8664_pda, entry))
Index: linux-2.6.21-rc5/include/asm-powerpc/thread_info.h
===================================================================
--- linux-2.6.21-rc5.orig/include/asm-powerpc/thread_info.h
+++ linux-2.6.21-rc5/include/asm-powerpc/thread_info.h
@@ -35,6 +35,9 @@ struct thread_info {
int cpu; /* cpu we're on */
int preempt_count; /* 0 => preemptable,
<0 => BUG */
+#ifdef CONFIG_THREAD_PERF_STAT_SYSC
+ unsigned long sysc_cnt; /* Syscall counter */
+#endif /* CONFIG_THREAD_PERF_STAT_SYSC */
struct restart_block restart_block;
unsigned long local_flags; /* private flags for thread */

Index: linux-2.6.21-rc5/arch/powerpc/kernel/asm-offsets.c
===================================================================
--- linux-2.6.21-rc5.orig/arch/powerpc/kernel/asm-offsets.c
+++ linux-2.6.21-rc5/arch/powerpc/kernel/asm-offsets.c
@@ -94,6 +94,10 @@ int main(void)
DEFINE(TI_LOCAL_FLAGS, offsetof(struct thread_info, local_flags));
DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
DEFINE(TI_TASK, offsetof(struct thread_info, task));
+#ifdef CONFIG_THREAD_PERF_STAT_SYSC
+ DEFINE(TI_SYSC_CNT, offsetof(struct thread_info, sysc_cnt));
+#endif /* CONFIG_THREAD_PERF_STAT_SYSC */
+
#ifdef CONFIG_PPC32
DEFINE(TI_EXECDOMAIN, offsetof(struct thread_info, exec_domain));
DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
Index: linux-2.6.21-rc5/arch/powerpc/kernel/entry_32.S
===================================================================
--- linux-2.6.21-rc5.orig/arch/powerpc/kernel/entry_32.S
+++ linux-2.6.21-rc5/arch/powerpc/kernel/entry_32.S
@@ -223,6 +223,11 @@ ret_from_syscall:
#endif
mr r6,r3
rlwinm r12,r1,0,0,(31-THREAD_SHIFT) /* current_thread_info() */
+#ifdef CONFIG_THREAD_PERF_STAT_SYSC
+ lwz r9,TI_SYSC_CNT(r12)
+ addi r9,r9,1
+ stw r9,TI_SYSC_CNT(r12)
+#endif /* CONFIG_THREAD_PERF_STAT_SYSC */
/* disable interrupts so current_thread_info()->flags can't change */
LOAD_MSR_KERNEL(r10,MSR_KERNEL) /* doesn't include MSR_EE */
SYNC
Index: linux-2.6.21-rc5/lib/Kconfig.debug
===================================================================
--- linux-2.6.21-rc5.orig/lib/Kconfig.debug
+++ linux-2.6.21-rc5/lib/Kconfig.debug
@@ -458,6 +458,6 @@ config THREAD_PERF_STAT

config THREAD_PERF_STAT_SYSC
bool "Enable syscall counter"
- depends on THREAD_PERF_STAT && X86
+ depends on THREAD_PERF_STAT && (X86 || PPC32)
help
This option adds a syscall counter to /proc/PID/status.