diff -urN -x '*.orig' linux-2.6.26.orig/arch/arm/Kconfig linux-2.6.26/arch/arm/Kconfig --- linux-2.6.26.orig/arch/arm/Kconfig 2009-09-10 10:22:11.000000000 -0400 +++ linux-2.6.26/arch/arm/Kconfig 2009-09-10 10:25:19.000000000 -0400 @@ -176,6 +176,13 @@ menu "System Type" +config ARM_PROFILER + bool "Kernel support for profiling on ARM targets" + default n + help + Sends low-level kernel events to RealView TRACE2 unit. Select yes + only if you have the trace hardware + choice prompt "ARM system type" default ARCH_VERSATILE diff -urN -x '*.orig' linux-2.6.26.orig/arch/arm/kernel/armprosle.c linux-2.6.26/arch/arm/kernel/armprosle.c --- linux-2.6.26.orig/arch/arm/kernel/armprosle.c 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.26/arch/arm/kernel/armprosle.c 2009-09-10 10:25:19.000000000 -0400 @@ -0,0 +1,388 @@ +/* + * Implementation of kernel hooks for ARM Profiler. + * + * Copyright (c) ARM Ltd 2009, All Rights Reserved Worldwide. + * + * Jeremy Webb + * Stephen Luce + * Richard Wilkes + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#define MAX_BUFFER_SIZE 1024 +#define HEADER_SIZE 10 + +#define CONTEXT_MASK 0xffffff00 + +void arm_trace_new_process(struct task_struct *p); +void arm_trace_update_process(struct task_struct *p); +void arm_trace_destroy_process(struct task_struct *p); +void arm_trace_new_thread(struct task_struct *p); +void arm_trace_update_thread(struct task_struct *p); +void arm_trace_destroy_thread(struct task_struct *p); +void arm_trace_context_switch(struct task_struct *p); +void arm_trace_context_notice(void); + +static spinlock_t mLock = SPIN_LOCK_UNLOCKED; +static unsigned char mBuffer[MAX_BUFFER_SIZE]; +static unsigned char * mBufferPosition; + +/* + * The Message Types that ARM Profiler understands. + * This implementation sends only ADD_PROCESS, REMOVE_PROCESS, ADD_THREAD, + * REMOVE_THREAD, ADD_CODE_MAPPING, REMOVE_CODE_MAPPING, and CONTEXT_SWITCH. + */ +enum MessageType { + ADD_PROCESS= 0, + UPDATE_PROCESS_NAME, + REMOVE_PROCESS, + ADD_THREAD, + UPDATE_THREAD_NAME, + REMOVE_THREAD, + ADD_CODE_MAPPING, + REMOVE_CODE_MAPPING, + CLEAR_CODE_MAPPING, + CONTEXT_SWITCH, + CONTEXT_SWITCH_NOTICE + }; + +/* + * Send a small amount of data out over the ContextID register + * Using only the top 3 bytes of the r4 register + */ +static void send_buffer(unsigned char buffer[], int length) { + asm volatile( + "send_buffer:\n\t" + "MRC p15, 0, r2, c13, c0, 1\n\t" /* r2 = ContextID (save original ContextId) */ + "ADD r1, %1, %0\n\t" /* r1 = end = buffer + length */ + "loop:\n\t" + "LDR r4, =0x1e\n\t" /* delay thirty cycles */ + "delayLoop:\n\t" + "SUBS r4, r4, #1\n\t" + "BNE delayLoop\n\t" + "AND r4, r2, #0xff\n\t" /* preserve the ASID in lower 8 bits or r4 */ + "LDRB r3, [%0], #1\n\t" /* put 3 bytes from buffer into top 24 bits of r4 */ + "ORR r4, r4, r3, LSL #8\n\t" + "LDRB r3, [%0], #1\n\t" + "ORR r4, r4, r3, LSL #16\n\t" + "LDRB r3, [%0], #1\n\t" + "ORR r4, r4, r3, LSL #24\n\t" + "MCR p15, 0, r4, c13, c0, 1\n\t" /* ContextID = r4 (push r4 out) */ + "SUB pc, #4\n\t" /* force a branch packet */ + "CMP %0, r1\n\t" + "BCC loop\n\t" + "LDR r4, =0x1e\n\t" + "delayLoop2:\n\t" + "SUBS r4, r4, #1\n\t" + "BNE delayLoop2\n\t" + "MCR p15, 0, r2, c13, c0, 1\n\t" /* ContextID = r2 (restore original ContextID) */ + "SUB pc, #4\n\t" /* force a branch packet */ + : + :"r"(buffer),"r"(length) + : "r1","r2", "r4", "cc", "memory" + ); +} + + +/* Write one byte */ +static void appendChar(const unsigned char aByte) { + *mBufferPosition++ = aByte; +} + +/* Write a 2 byte value */ +static void appendShort(const unsigned short aValue){ + appendChar((unsigned char)(aValue & 0xff)); + appendChar((unsigned char)(aValue >> 8 & 0xff)); +} + +/* write integer as 4 bytes */ +static void appendInt(const int aValue) { + appendChar((unsigned char)(aValue & 0xff)); + appendChar((unsigned char)(aValue >> 8 & 0xff)); + appendChar((unsigned char)(aValue >> 16 & 0xff)); + appendChar((unsigned char)(aValue >> 24 & 0xff)); +} + +/* Write n bytes */ +static void appendBytes(const unsigned char * aBytes, int aLength) { + while (aLength-- > 0) { + *mBufferPosition++ = *aBytes++; + } +} + +/* write null-terminated string */ +static void appendChars(const char * s) { + while (*s) { + appendChar((unsigned char)*s++); + } +} + +/* write length + null-terminated string */ +static void appendString(const unsigned char *s) { + int length = 0; + const unsigned char *ptr; + ptr = s; + + while (*ptr) { + ptr++; + length++; + } + appendInt(length); + appendBytes(s, length); +} + +static void setHeader(int messageType) { + unsigned char typeLow = messageType & 0xff; + unsigned char typeHigh = messageType >> 8 & 0xff; + unsigned char headerChecksum = 0 - ('A' + 'R' + 'M' + typeLow + typeHigh); + mBufferPosition = mBuffer; + // bytes 0 - 2 are "ARM" + appendChars("ARM"); + + // byte 3 is header checksum + appendChar(headerChecksum); + + // bytes 4 and 5 are message type + appendShort( (unsigned short) messageType); + + // bytes 6 and 7 are message checksum + appendShort(0); // Make room for the message checksum. + + // bytes 8 and 9 are message length + appendShort(0); // Make room for the message length. + + // total header size is 10 bytes +} + +static void sendMessage(void) { + int length = mBufferPosition - mBuffer; + unsigned short messageLength = length - HEADER_SIZE; + int padding = 2 - ((length + 2) % 3); + unsigned short checksum = 0; + int i; + + length += padding; + while(padding--){ + *mBufferPosition++ = 0; + } + + mBufferPosition = mBuffer + 8; + appendShort(messageLength); + + for (i = 0; i < length; i++) { + if ( (i & 1) == 0 ) { + // for all even bytes + checksum += mBuffer[i]; + } else { + // for all odd bytes + checksum += mBuffer[i] << 8; + } + } + checksum = 0 - checksum; + + mBufferPosition = mBuffer + 6; + appendShort(checksum); + + //local_irq_disable(); + send_buffer(mBuffer, length); + //local_irq_enable(); +} + + +/** + * called when code is loaded + */ +void arm_trace_code_load(struct task_struct *p, unsigned int addr, int len, char* file, unsigned long pgoff) { + if (file) { + unsigned long flags; + spin_lock_irqsave(&mLock, flags); + setHeader(ADD_CODE_MAPPING); + appendInt( (int) p & CONTEXT_MASK); + appendInt(addr); //Entry Point + appendInt(len); //Code Size + appendInt(pgoff * 0x1000); //file offset + appendString(file); + sendMessage(); + spin_unlock_irqrestore(&mLock, flags); + } +} + +/** + * called when code is unloaded + */ +void arm_trace_code_unload(struct task_struct *p, unsigned int addr, int len) { + unsigned long flags; + spin_lock_irqsave(&mLock, flags); + setHeader(REMOVE_CODE_MAPPING); + appendInt( (int) p & CONTEXT_MASK); + appendInt(addr); //Entry Point + appendInt(len); //Code Size + sendMessage(); + spin_unlock_irqrestore(&mLock, flags); +} + +/* + * Called by the thread notifier on thread events + */ +static int thread_notifier(struct notifier_block *self, unsigned long cmd, void *v) { + struct thread_info *t = v; + + switch (cmd) { + case THREAD_NOTIFY_SWITCH : { + arm_trace_context_switch(t->task); + break; + } + case THREAD_NOTIFY_FLUSH : { + if (t->task == t->task->group_leader) { + + arm_trace_context_notice(); + /* + * this represents a new process + * we send both new_process and new_thread to fulfill the assumption + * that all processes have one or more threads. + */ + arm_trace_new_process(t->task); + arm_trace_new_thread(t->task); + } else { + + /* this represents a new thread */ + arm_trace_new_thread(t->task); + } + break; + } + case THREAD_NOTIFY_RELEASE : { + if (t->task == t->task->group_leader) { + /* + * this represents a deleted process. + * we send both destroy_thread and destroy_process to tear down + * the process in the correct order as described above. + */ + arm_trace_destroy_thread(t->task); + arm_trace_destroy_process(t->task); + } else { + /* this represents a deleted thread */ + arm_trace_destroy_thread(t->task); + } + break; + } + } + return 0; +} + +static struct notifier_block thread_notifier_block = { + .notifier_call = thread_notifier, +}; + +/** + * called when a process is created + */ +void arm_trace_new_process(struct task_struct *p) { + unsigned long flags; + spin_lock_irqsave(&mLock, flags); + setHeader(ADD_PROCESS); + appendInt( (int) p & CONTEXT_MASK); + appendInt( (int) p->parent & CONTEXT_MASK); + task_lock(p); + appendString(p->comm); + task_unlock(p); + sendMessage(); + spin_unlock_irqrestore(&mLock, flags); +} + +/** + * Called when a process is destroyed + */ +void arm_trace_destroy_process(struct task_struct *p) { + unsigned long flags; + spin_lock_irqsave(&mLock, flags); + setHeader(REMOVE_PROCESS); + appendInt( (int) p & CONTEXT_MASK); + sendMessage(); + spin_unlock_irqrestore(&mLock, flags); +} + +/* + * Prepare receiver for context switches. + * This message should be sent BEFORE any CONTEXT_SWITCH + * or ADD_PROCESS. + */ +void arm_trace_context_notice(void) { + unsigned long flags; + spin_lock_irqsave(&mLock, flags); + setHeader(CONTEXT_SWITCH_NOTICE); + sendMessage(); + spin_unlock_irqrestore(&mLock, flags); +} + +/* + * Context Switch + */ +void arm_trace_context_switch(struct task_struct *p) { + unsigned long flags; + spin_lock_irqsave(&mLock, flags); + setHeader(CONTEXT_SWITCH); + appendInt( (int) p & CONTEXT_MASK); + sendMessage(); + spin_unlock_irqrestore(&mLock, flags); +} + +/** + * Called when a thread is created + */ +void arm_trace_new_thread(struct task_struct *p) { + struct task_struct *leader; + unsigned long flags; + + read_lock(&tasklist_lock); + leader = find_task_by_pid_ns(p->tgid, &init_pid_ns); + read_unlock(&tasklist_lock); + + spin_lock_irqsave(&mLock, flags); + setHeader(ADD_THREAD); + appendInt( (int) p & CONTEXT_MASK); + appendInt( (int) leader & CONTEXT_MASK); + task_lock(p); + appendString(p->comm);//this is parent process name + task_unlock(p); + sendMessage(); + spin_unlock_irqrestore(&mLock, flags); +} + +/** + * called when a thread is destroyed + */ +void arm_trace_destroy_thread(struct task_struct *p) { + unsigned long flags; + spin_lock_irqsave(&mLock, flags); + setHeader(REMOVE_THREAD); + appendInt( (int) p & CONTEXT_MASK); + sendMessage(); + spin_unlock_irqrestore(&mLock, flags); +} + +/* + * This must be called soon after startup + */ +int __init profiler_init(void) +{ + thread_register_notifier(&thread_notifier_block); + pr_info("ARM Profiler Enabled\n"); + return 0; +} + +/* + * this should be called during shutdown + */ +void __exit profiler_exit(void) +{ + thread_unregister_notifier(&thread_notifier_block); + pr_info("ARM Profiler Disabled\n"); +} diff -urN -x '*.orig' linux-2.6.26.orig/arch/arm/kernel/Makefile linux-2.6.26/arch/arm/kernel/Makefile --- linux-2.6.26.orig/arch/arm/kernel/Makefile 2009-09-10 10:22:10.000000000 -0400 +++ linux-2.6.26/arch/arm/kernel/Makefile 2009-09-10 10:25:19.000000000 -0400 @@ -27,6 +27,8 @@ obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312 +obj-$(CONFIG_ARM_PROFILER) += armprosle.o + obj-$(CONFIG_CPU_XSCALE) += xscale-cp0.o obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o obj-$(CONFIG_IWMMXT) += iwmmxt.o diff -urN -x '*.orig' linux-2.6.26.orig/arch/arm/kernel/process.c linux-2.6.26/arch/arm/kernel/process.c --- linux-2.6.26.orig/arch/arm/kernel/process.c 2009-09-10 10:22:10.000000000 -0400 +++ linux-2.6.26/arch/arm/kernel/process.c 2009-09-10 10:25:19.000000000 -0400 @@ -348,6 +348,16 @@ if (clone_flags & CLONE_SETTLS) thread->tp_value = regs->ARM_r3; +#ifdef CONFIG_ARM_PROFILER + /* + * flush_thread() is only called for new processes, must also + * notify on new threads - JDW + */ + if (clone_flags & CLONE_THREAD) { + thread_notify(THREAD_NOTIFY_FLUSH, thread); + } +#endif + return 0; } diff -urN -x '*.orig' linux-2.6.26.orig/fs/binfmt_elf.c linux-2.6.26/fs/binfmt_elf.c --- linux-2.6.26.orig/fs/binfmt_elf.c 2009-09-10 10:21:56.000000000 -0400 +++ linux-2.6.26/fs/binfmt_elf.c 2009-09-10 10:25:19.000000000 -0400 @@ -42,6 +42,10 @@ #include #include +#ifdef CONFIG_ARM_PROFILER +#include +#endif + static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs); static int load_elf_library(struct file *); static unsigned long elf_map(struct file *, unsigned long, struct elf_phdr *, @@ -972,7 +976,9 @@ */ ELF_PLAT_INIT(regs, reloc_func_desc); #endif - +#ifdef CONFIG_ARM_PROFILER + arm_trace_context_switch(current); +#endif start_thread(regs, elf_entry, bprm->p); if (unlikely(current->ptrace & PT_PTRACED)) { if (current->ptrace & PT_TRACE_EXEC) diff -urN -x '*.orig' linux-2.6.26.orig/include/asm-arm/armprosle.h linux-2.6.26/include/asm-arm/armprosle.h --- linux-2.6.26.orig/include/asm-arm/armprosle.h 1969-12-31 19:00:00.000000000 -0500 +++ linux-2.6.26/include/asm-arm/armprosle.h 2009-09-10 10:25:19.000000000 -0400 @@ -0,0 +1,44 @@ +/* + * External hooks into arch/arm/kernel/armprosle.c + * + * This interface supports the ARM Profiler. + * + * Copyright (c) ARM Ltd 2009, All Rights Reserved Worldwide. + * + * Jeremy Webb + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include + +/* + * Force a context switch event, used if the normal thread + * notifications are not being sent for some reason. + * see fs/binfmt_elf.c + */ +void arm_trace_context_switch(struct task_struct *p); + +/* + * Called when code load events occur + * see mm/mmap.c + */ +void arm_trace_code_load(struct task_struct *p, unsigned int addr, int len, char* file, unsigned long pgoff); + +/* + * Called when code un-load events occur + * see mm/mmap.c + */ +void arm_trace_code_unload(struct task_struct *p, unsigned int addr, int len); + +/* + * Called to initialize profiling support (during boot process). + * see init/main.c + */ +int profiler_init(void); + +/* + * Not called currently, but probably should be. + */ +void profiler_exit(void); diff -urN -x '*.orig' linux-2.6.26.orig/init/main.c linux-2.6.26/init/main.c --- linux-2.6.26.orig/init/main.c 2009-09-10 10:22:20.000000000 -0400 +++ linux-2.6.26/init/main.c 2009-09-10 10:25:19.000000000 -0400 @@ -71,6 +71,10 @@ #include #endif +#ifdef CONFIG_ARM_PROFILER +#include +#endif + /* * This is one of the first .c files built. Error out early if we have compiler * trouble. @@ -810,6 +814,11 @@ current->signal->flags |= SIGNAL_UNKILLABLE; +#ifdef CONFIG_ARM_PROFILER + //start profiling just before the init process is started. + profiler_init(); +#endif + if (ramdisk_execute_command) { run_init_process(ramdisk_execute_command); printk(KERN_WARNING "Failed to execute %s\n", diff -urN -x '*.orig' linux-2.6.26.orig/mm/mmap.c linux-2.6.26/mm/mmap.c --- linux-2.6.26.orig/mm/mmap.c 2009-09-10 10:21:56.000000000 -0400 +++ linux-2.6.26/mm/mmap.c 2009-09-10 10:25:19.000000000 -0400 @@ -32,6 +32,10 @@ #include #include +#ifdef CONFIG_ARM_PROFILER +#include +#endif + #ifndef arch_mmap_check #define arch_mmap_check(addr, len, flags) (0) #endif @@ -1218,6 +1222,11 @@ } if ((flags & MAP_POPULATE) && !(flags & MAP_NONBLOCK)) make_pages_present(addr, addr + len); + +#ifdef CONFIG_ARM_PROFILER + arm_trace_code_load(current, addr, len, inode ? (char *) file->f_path.dentry->d_name.name : NULL, pgoff); +#endif + return addr; unmap_and_free_vma: @@ -1765,6 +1774,10 @@ free_pgtables(&tlb, vma, prev? prev->vm_end: FIRST_USER_ADDRESS, next? next->vm_start: 0); tlb_finish_mmu(tlb, start, end); + +#ifdef CONFIG_ARM_PROFILER + arm_trace_code_unload(current, start, end - start); +#endif } /*