This is a simple patch for precise process time accounting using
the TSC. The current version works only on ia32 with TSC and it
needs some more tweaking to do SMP locks properly.

Bugs and comments to <mj@ucw.cz>.
					Enjoy
							Martin


--- fs/proc/base.c.mj	Thu Nov 22 00:18:15 2001
+++ fs/proc/base.c	Thu Nov 22 00:20:35 2001
@@ -39,6 +39,7 @@
 int proc_pid_status(struct task_struct*,char*);
 int proc_pid_statm(struct task_struct*,char*);
 int proc_pid_cpu(struct task_struct*,char*);
+int proc_pid_profile(struct task_struct*,char*);
 
 static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
 {
@@ -497,6 +498,7 @@
 	PROC_PID_STATM,
 	PROC_PID_MAPS,
 	PROC_PID_CPU,
+	PROC_PID_PROFILE,
 	PROC_PID_FD_DIR = 0x8000,	/* 0x8000-0xffff */
 };
 
@@ -516,6 +518,9 @@
   E(PROC_PID_CWD,	"cwd",		S_IFLNK|S_IRWXUGO),
   E(PROC_PID_ROOT,	"root",		S_IFLNK|S_IRWXUGO),
   E(PROC_PID_EXE,	"exe",		S_IFLNK|S_IRWXUGO),
+#ifdef CONFIG_UCW_PROFILER
+  E(PROC_PID_PROFILE,	"profile",	S_IFREG|S_IRUGO),
+#endif
   {0,0,NULL,0}
 };
 #undef E
@@ -875,6 +880,12 @@
 			inode->i_op = &proc_mem_inode_operations;
 			inode->i_fop = &proc_mem_operations;
 			break;
+#ifdef CONFIG_UCW_PROFILER
+		case PROC_PID_PROFILE:
+			inode->i_fop = &proc_info_file_operations;
+			inode->u.proc_i.op.proc_read = proc_pid_profile;
+			break;
+#endif
 		default:
 			printk("procfs: impossible type (%d)",p->type);
 			iput(inode);
--- fs/proc/array.c.mj	Thu Nov 22 00:21:40 2001
+++ fs/proc/array.c	Thu Nov 22 00:21:40 2001
@@ -694,3 +694,10 @@
 	return len;
 }
 #endif
+
+#ifdef CONFIG_UCW_PROFILER
+int proc_pid_profile(struct task_struct *task, char * buffer)
+{
+	return sprintf(buffer, "%Ld %Ld\n", task->prof_user, task->prof_sys);
+}
+#endif
--- include/linux/sched.h.mj	Wed Nov 21 23:28:31 2001
+++ include/linux/sched.h	Thu Nov 22 00:06:34 2001
@@ -313,6 +313,12 @@
 	struct list_head run_list;
 	unsigned long sleep_time;
 
+#ifdef CONFIG_UCW_PROFILER
+	u64 prof_last_tsc;
+	u64 prof_user;
+	u64 prof_sys;
+#endif
+
 	struct task_struct *next_task, *prev_task;
 	struct mm_struct *active_mm;
 	struct list_head local_pages;
--- arch/i386/kernel/process.c.mj	Thu Nov 22 00:05:13 2001
+++ arch/i386/kernel/process.c	Thu Nov 22 00:30:59 2001
@@ -591,6 +591,11 @@
 
 	p->thread.eip = (unsigned long) ret_from_fork;
 
+#ifdef CONFIG_UCW_PROFILER
+	p->prof_user = 0;
+	p->prof_sys = 0;
+#endif
+
 	savesegment(fs,p->thread.fs);
 	savesegment(gs,p->thread.gs);
 
@@ -680,6 +685,15 @@
 	struct tss_struct *tss = init_tss + smp_processor_id();
 
 	unlazy_fpu(prev_p);
+
+#ifdef CONFIG_UCW_PROFILER
+	{
+		u64 tsc;
+		rdtscll(tsc);
+		next_p->prof_last_tsc = tsc;
+		prev_p->prof_sys += tsc - prev_p->prof_last_tsc;
+	}
+#endif
 
 	/*
 	 * Reload esp0, LDT and the page table pointer:
--- arch/i386/kernel/entry.S.mj	Wed Nov 21 23:29:44 2001
+++ arch/i386/kernel/entry.S	Thu Nov 22 00:41:51 2001
@@ -78,6 +78,11 @@
 need_resched	= 20
 tsk_ptrace	= 24
 processor	= 52
+#ifdef CONFIG_UCW_PROFILER
+prof_last_tsc	= 72
+prof_user	= 80
+prof_sys	= 88
+#endif
 
 ENOSYS = 38
 
@@ -195,6 +200,16 @@
 	pushl %eax			# save orig_eax
 	SAVE_ALL
 	GET_CURRENT(%ebx)
+#ifdef CONFIG_UCW_PROFILER
+	rdtsc
+	subl prof_last_tsc(%ebx),%eax
+	sbbl prof_last_tsc+4(%ebx),%edx
+	addl %eax,prof_user(%ebx)
+	adcl %edx,prof_user+4(%ebx)
+	addl %eax,prof_last_tsc(%ebx)
+	adcl %edx,prof_last_tsc+4(%ebx)
+	movl ORIG_EAX(%esp),%eax
+#endif
 	testb $0x02,tsk_ptrace(%ebx)	# PT_TRACESYS
 	jne tracesys
 	cmpl $(NR_syscalls),%eax
@@ -202,6 +217,15 @@
 	call *SYMBOL_NAME(sys_call_table)(,%eax,4)
 	movl %eax,EAX(%esp)		# save the return value
 ENTRY(ret_from_sys_call)
+#ifdef CONFIG_UCW_PROFILER
+	rdtsc
+	subl prof_last_tsc(%ebx),%eax
+	sbbl prof_last_tsc+4(%ebx),%edx
+	addl %eax,prof_sys(%ebx)
+	adcl %edx,prof_sys+4(%ebx)
+	addl %eax,prof_last_tsc(%ebx)
+	adcl %edx,prof_last_tsc+4(%ebx)
+#endif
 	cli				# need_resched and signals atomic test
 	cmpl $0,need_resched(%ebx)
 	jne reschedule
@@ -287,6 +311,18 @@
 	movl %edx,%ds
 	movl %edx,%es
 	GET_CURRENT(%ebx)
+#ifdef CONFIG_UCW_PROFILER
+	testl $3,CS+8(%esp)		# called from supervisor => no counting needed
+	jne exc_prof_sys
+	rdtsc
+	subl prof_last_tsc(%ebx),%eax
+	sbbl prof_last_tsc+4(%ebx),%edx
+	addl %eax,prof_user(%ebx)
+	adcl %edx,prof_user+4(%ebx)
+	addl %eax,prof_last_tsc(%ebx)
+	adcl %edx,prof_last_tsc+4(%ebx)
+exc_prof_sys:
+#endif
 	call *%edi
 	addl $8,%esp
 	jmp ret_from_exception
--- arch/i386/kernel/irq.c.mj	Thu Nov 22 00:02:24 2001
+++ arch/i386/kernel/irq.c	Thu Nov 22 00:30:54 2001
@@ -578,6 +578,17 @@
 	struct irqaction * action;
 	unsigned int status;
 
+#ifdef CONFIG_UCW_PROFILER
+	if (regs.xcs & 3) {
+		/* Came from user mode, need to account */
+		u64 tsc;
+		rdtscll(tsc);
+		tsc -= current->prof_last_tsc;
+		current->prof_user += tsc;
+		current->prof_last_tsc += tsc;
+	}
+#endif
+
 	kstat.irqs[cpu][irq]++;
 	spin_lock(&desc->lock);
 	desc->handler->ack(irq);
--- arch/i386/config.in.mj	Wed Nov 21 23:23:48 2001
+++ arch/i386/config.in	Wed Nov 21 23:23:57 2001
@@ -406,5 +406,6 @@
    bool '  Spinlock debugging' CONFIG_DEBUG_SPINLOCK
    bool '  Verbose BUG() reporting (adds 70K)' CONFIG_DEBUG_BUGVERBOSE
 fi
+bool 'UCW profiler' CONFIG_UCW_PROFILER
 
 endmenu
