============================================= kfi-04.12.10.patch: This patch provides Kernel Function Instrumentation Signed-off-by: Geoff Levand for CELF --- Makefile | 4 arch/i386/Kconfig.debug | 18 arch/i386/boot/compressed/Makefile | 3 arch/ppc/Kconfig.debug | 18 arch/ppc/boot/Makefile | 4 arch/ppc/boot/common/Makefile | 3 arch/ppc/boot/lib/Makefile | 4 arch/ppc/boot/simple/Makefile | 4 arch/ppc/kernel/time.c | 5 drivers/char/Makefile | 10 drivers/char/kfi.c | 941 +++++++++++++++++++++++++++++++++++++ drivers/char/kfistatic.conf | 6 include/asm-ppc/delay.h | 1 include/asm-ppc/processor.h | 1 include/linux/init.h | 8 include/linux/kfi.h | 84 +++ init/main.c | 12 kernel/sys.c | 16 scripts/mkkfirun.pl | 241 +++++++++ 19 files changed, 1382 insertions(+), 1 deletion(-) diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/arch/i386/boot/compressed/Makefile branch_KFI/arch/i386/boot/compressed/Makefile --- tag_LINUX_2_6_9/arch/i386/boot/compressed/Makefile 2004-04-29 18:09:43.000000000 -0700 +++ branch_KFI/arch/i386/boot/compressed/Makefile 2004-07-28 14:50:18.000000000 -0700 @@ -6,6 +6,9 @@ targets := vmlinux vmlinux.bin vmlinux.bin.gz head.o misc.o piggy.o EXTRA_AFLAGS := -traditional +ifdef CONFIG_KFI +EXTRA_CFLAGS := -fno-instrument-functions +endif LDFLAGS_vmlinux := -Ttext $(IMAGE_OFFSET) -e startup_32 diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/arch/i386/Kconfig.debug branch_KFI/arch/i386/Kconfig.debug --- tag_LINUX_2_6_9/arch/i386/Kconfig.debug 2004-10-22 14:40:34.000000000 -0700 +++ branch_KFI/arch/i386/Kconfig.debug 2004-11-01 17:48:04.000000000 -0800 @@ -77,4 +77,22 @@ config X86_MPPARSE depends on X86_LOCAL_APIC && !X86_VISWS default y +config KFI + bool "Kernel Function Instrumentation" + help + Say Y here to turn on kernel function instrumentation. + FIXTHIS - need more help here + Say N here if you are unsure. + +config KFI_STATIC_RUN + bool "Static Instrumentation Configs" + depends on KFI + help + Say Y here to compile the KFI configuration statically + into the kernel. This is needed if you plan to use KFI + to get information about function timings on kernel bootup + (prior to the kernel starting user space). To do this, you + need to put a valid kfistatic.conf file in the directory + drivers/char. + endmenu diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/arch/ppc/boot/common/Makefile branch_KFI/arch/ppc/boot/common/Makefile --- tag_LINUX_2_6_9/arch/ppc/boot/common/Makefile 2004-04-29 18:09:44.000000000 -0700 +++ branch_KFI/arch/ppc/boot/common/Makefile 2004-08-24 11:12:02.000000000 -0700 @@ -7,6 +7,9 @@ # # Tom Rini January 2001 # +ifdef CONFIG_KFI +EXTRA_CFLAGS := -fno-instrument-functions +endif lib-y := string.o util.o misc-common.o \ serial_stub.o bootinfo.o diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/arch/ppc/boot/lib/Makefile branch_KFI/arch/ppc/boot/lib/Makefile --- tag_LINUX_2_6_9/arch/ppc/boot/lib/Makefile 2004-10-22 14:40:38.000000000 -0700 +++ branch_KFI/arch/ppc/boot/lib/Makefile 2004-11-01 17:49:05.000000000 -0800 @@ -4,6 +4,10 @@ CFLAGS_kbd.o += -Idrivers/char +ifdef CONFIG_KFI +EXTRA_CFLAGS := -fno-instrument-functions +endif + lib-y := $(addprefix ../../../../lib/zlib_inflate/, \ infblock.o infcodes.o inffast.o inflate.o inftrees.o infutil.o) lib-y += div64.o diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/arch/ppc/boot/Makefile branch_KFI/arch/ppc/boot/Makefile --- tag_LINUX_2_6_9/arch/ppc/boot/Makefile 2004-10-22 14:40:37.000000000 -0700 +++ branch_KFI/arch/ppc/boot/Makefile 2004-11-01 17:49:04.000000000 -0800 @@ -13,6 +13,10 @@ CFLAGS += -fno-builtin -D__BOOTER__ -Iarch/$(ARCH)/boot/include HOSTCFLAGS += -Iarch/$(ARCH)/boot/include +ifdef CONFIG_KFI +EXTRA_CFLAGS := -fno-instrument-functions +endif + BOOT_TARGETS = zImage zImage.initrd znetboot znetboot.initrd bootdir-y := simple diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/arch/ppc/boot/simple/Makefile branch_KFI/arch/ppc/boot/simple/Makefile --- tag_LINUX_2_6_9/arch/ppc/boot/simple/Makefile 2004-10-22 14:40:37.000000000 -0700 +++ branch_KFI/arch/ppc/boot/simple/Makefile 2004-11-01 17:49:06.000000000 -0800 @@ -21,6 +21,10 @@ # XXX_memory.o file for this to work, as well as editing the # misc-$(CONFIG_MACHINE) variable. +ifdef CONFIG_KFI +EXTRA_CFLAGS := -fno-instrument-functions +endif + boot := arch/ppc/boot common := $(boot)/common utils := $(boot)/utils diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/arch/ppc/Kconfig.debug branch_KFI/arch/ppc/Kconfig.debug --- tag_LINUX_2_6_9/arch/ppc/Kconfig.debug 2004-10-22 14:40:37.000000000 -0700 +++ branch_KFI/arch/ppc/Kconfig.debug 2004-11-01 17:48:50.000000000 -0800 @@ -81,4 +81,22 @@ config PPC_OCP depends on IBM_OCP || FSL_OCP default y +config KFI + bool "Kernel Function Instrumentation" + help + Say Y here to turn on kernel function instrumentation. + FIXTHIS - need more help here + Say N here if you are unsure. + +config KFI_STATIC_RUN + bool "Static Instrumentation Configs" + depends on KFI + help + Say Y here to compile the KFI configuration statically + into the kernel. This is needed if you plan to use KFI + to get information about function timings on kernel bootup + (prior to the kernel starting user space). To do this, you + need to put a valid kfistatic.conf file in the directory + drivers/char. + endmenu diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/arch/ppc/kernel/time.c branch_KFI/arch/ppc/kernel/time.c --- tag_LINUX_2_6_9/arch/ppc/kernel/time.c 2004-10-22 14:40:38.000000000 -0700 +++ branch_KFI/arch/ppc/kernel/time.c 2004-11-01 17:49:07.000000000 -0800 @@ -85,7 +85,12 @@ time_t last_rtc_update; unsigned tb_ticks_per_jiffy; unsigned tb_to_us; unsigned tb_last_stamp; +#if defined(CONFIG_KFI_STATIC_RUN) || defined(CONFIG_PRINTK_TIME) +/* preset value so sched_clock() works immediately at startup */ +unsigned long tb_to_ns_scale = 2559; /* value for ebony board */ +#else unsigned long tb_to_ns_scale; +#endif extern unsigned long wall_jiffies; diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/drivers/char/kfi.c branch_KFI/drivers/char/kfi.c --- tag_LINUX_2_6_9/drivers/char/kfi.c 1969-12-31 16:00:00.000000000 -0800 +++ branch_KFI/drivers/char/kfi.c 2004-09-02 12:59:45.000000000 -0700 @@ -0,0 +1,941 @@ +/* + * drivers/char/kfi.c + * + * Kernel Function Instrumentation + * + * Copyright (C) 2002 MontaVista Software + * + * Support for Function Instrumentation/Profiling feature of + * GCC (-finstrument-functions). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define KFI_MODULE_NAME "KFI" +#define PFX KFI_MODULE_NAME + +#define err(format, arg...) printk(KERN_ERR PFX ": " format , ## arg) +#define info(format, arg...) printk(KERN_INFO PFX ": " format , ## arg) +#define warn(format, arg...) printk(KERN_WARNING PFX ": " format , ## arg) +#define emerg(format, arg...) printk(KERN_EMERG PFX ": " format , ## arg) + +#define KFI_MINOR 51 + +#ifdef CONFIG_KFI_STATIC_RUN +extern struct kfi_run kfi_run0; +extern struct kfi_run* kfi_last_run; +extern const int kfi_num_runs; +static struct kfi_run* run_head = &kfi_run0; +static struct kfi_run* run_curr = &kfi_run0; +static struct kfi_run* run_tail = NULL; +#else +static struct kfi_run* run_head = NULL; +static struct kfi_run* run_curr = NULL; +static struct kfi_run* run_tail = NULL; +#endif // CONFIG_KFI_STATIC_RUN + +static int next_run_id = 0; +static int in_entry_exit = 0; + +//static spinlock_t kfi_lock = SPIN_LOCK_UNLOCKED; +DECLARE_WAIT_QUEUE_HEAD(kfi_wait); + +#define GENERIC_KFIREADCLOCK 1 + +#ifndef GENERIC_KFIREADCLOCK +/* + * Use arch-specific kfi_readclock() and kfi_clock_to_usecs() routines + */ + +/* + * !! Set the following for your machine!! + * + * CLOCK_FREQ is a hardcoded value for the frequency of + * whatever clock you are using for kfi_readclock() + * It would be nice to use a probed clock freq (cpu_hz) + * here, but it isn't set early enough for some boot + * measurements. + * Hint: for x86, boot once and look at /proc/cpuinfo + */ +// Tim's old laptop +//#define CLOCK_FREQ 645206000ULL +// Tim's HP desktop +//#define CLOCK_FREQ 2992332000ULL +// Ebony board +#define CLOCK_FREQ 400000000ULL + +/* + * CLOCK_SHIFT is used to bring the clock frequency into + * a manageable range. For my 3 GHz machine, I decided + * to divide the cpu cycle clock by 8. This throws + * away some clock precision, but makes some of the + * other math faster and helps us stay in 32 bits. + */ +#define CLOCK_SHIFT 3 + +/* + * This weird scaling makes it possible to use shifts instead + * of divisions, for the conversion to microseconds + */ +#define CLOCK_SCALE (((CLOCK_FREQ*1000000)/(1024*1024))>>CLOCK_SHIFT) + +#ifdef CONFIG_X86_TSC +#include /* for rdtscll macro */ +static inline unsigned long kfi_readclock(void) +{ + unsigned long long ticks; + + rdtscll(ticks); + return (unsigned long)((ticks>>CLOCK_SHIFT) & 0xffffffff); +} +#endif + +#if defined(CONFIG_PPC32) +#include /* for get_tbu macro */ +/* copied from sched_clock for ppc */ +static inline unsigned long kfi_readclock(void) +{ + unsigned long lo, hi, hi2; + unsigned long long ticks; + + do { + hi = get_tbu(); + lo = get_tbl(); + hi2 = get_tbu(); + } while (hi2 != hi); + ticks = ((unsigned long long) hi << 32) | lo; + return (unsigned long)((ticks>>CLOCK_SHIFT) & 0xffffffff); +} +#endif + +static inline unsigned long kfi_clock_to_usecs(unsigned long clock) +{ + /* math to stay in 32 bits. Try to avoid over and underflows */ + if (clock<4096) + return (clock<<20)/CLOCK_SCALE; + if (clock<(4096<<5)) + return (clock<<15)/(CLOCK_SCALE>>5); + if (clock<(4096<<10)) + return (clock<<10)/(CLOCK_SCALE>>10); + if (clock<(4096<<15)) + return (clock<<5)/(CLOCK_SCALE>>15); + else + return clock/(CLOCK_SCALE>>20); +} +#endif + +#ifdef GENERIC_KFIREADCLOCK +/* + * Define a genefic kfi_readclock routine. + * This should work well enough for platforms where sched_clock() + * gives good (sub-microsecond) precision. + * + * There are valid reasons to use other routines, including: + * - when using kfi for boot timings + * - on most platforms, sched_clock() does not work correctly until + * after time_init() + * - reduced overhead for obtaining a microsecond value + * (This may be incorrect, since at most this adds one + * 64-bit-by-32-bit divide, in addition to the shift that + * is inside sched_clock(). Kfi does enough other stuff + * that this one divide is not probably not a major factor + * in kfi overhead.) + */ +static inline unsigned long __noinstrument kfi_readclock(void) +{ + unsigned long long t; + + t = sched_clock(); + /* convert to microseconds */ + do_div(t,1000); + return (unsigned long)t; +} + +static inline unsigned long __noinstrument kfi_clock_to_usecs(unsigned long clock) +{ + return clock; +} +#endif + +static unsigned long usecs_since_boot = 0; +static unsigned long last_machine_cycles = 0; + +static inline unsigned long __noinstrument +update_usecs_since_boot(void) +{ + unsigned long machine_cycles, delta; + + machine_cycles = kfi_readclock(); + delta = machine_cycles - last_machine_cycles; + delta = kfi_clock_to_usecs(delta); + /* + * check for clock going backwards - this may happen + * because the clock is reset during startup + * initialization of the timer. + * In this case, we lose the correct value for this + * entry - but that's better than moving usecs_since_boot + * backwards and causing lots of negative durations in + * the log. + */ + if (delta > 0x8000000) { + delta = 0; + } + usecs_since_boot += delta; + + last_machine_cycles = machine_cycles; + return usecs_since_boot; +} + +static inline struct kfi_entry* __noinstrument +new_entry(struct kfi_run* run) +{ + struct kfi_entry* entry; + + if (run->next_entry >= run->num_entries) + return NULL; + + entry = &run->log[run->next_entry]; + run->next_entry++; + return entry; +} + + +static inline int __noinstrument +find_entry(struct kfi_run* run, void *this_fn, unsigned int pid) +{ + int i; + + for (i = run->next_entry-1; i >= 0; i--) { + struct kfi_entry* entry = &run->log[i]; + if (entry->va == this_fn && + entry->pid == pid && + entry->delta == 0) + return i; + } + + return -1; +} + + +static inline void __noinstrument +free_entry(struct kfi_run* run, int loc) +{ + int i; + run->next_entry--; + for (i = loc; i < run->next_entry; i++) + run->log[i] = run->log[i+1]; +} + + +static inline int __noinstrument +in_func_list(struct kfi_filters* filters, void* func) +{ + int i; + + for (i=0; i < filters->func_list_size; i++) { + if (filters->func_list[i] == func) + return 1; + } + + return 0; +} + + +static inline int __noinstrument +test_filters(struct kfi_filters* filters, void *this_fn) +{ + int in_intr; + + if (filters->func_list && !in_func_list(filters, this_fn)) { +#ifdef KFI_DEBUG + filters->cnt.func_list++; +#endif + return 1; + } + + in_intr = in_interrupt(); + + if (filters->no_ints && in_intr) { +#ifdef KFI_DEBUG + filters->cnt.no_ints++; +#endif + return 1; + } + + if (filters->only_ints && !in_intr) { +#ifdef KFI_DEBUG + filters->cnt.only_ints++; +#endif + return 1; + } + + return 0; +} + + +static inline void __noinstrument +do_func_entry(struct kfi_run* run, void *this_fn, void *call_site) +{ + struct kfi_entry* entry; + + if (!(entry = new_entry(run))) { + run->complete = 1; + run->stop_trigger.mark = update_usecs_since_boot(); + run->stop_trigger.type = TRIGGER_LOG_FULL; + return; + } + + entry->va = this_fn; + entry->call_site = call_site; + entry->pid = in_interrupt() ? INTR_CONTEXT : current->pid; + + entry->delta = 0; + entry->time = update_usecs_since_boot() - run->start_trigger.mark; +} + +static inline void __noinstrument +do_func_exit(struct kfi_run* run, void *this_fn, void *call_site) +{ + struct kfi_entry* entry; + unsigned long exittime; + unsigned long delta; + int entry_i; + + if ((entry_i = find_entry(run, this_fn, in_interrupt() ? + INTR_CONTEXT : current->pid)) < 0) { +#ifdef KFI_DEBUG + run->notfound++; +#endif + return; + } + + entry = &run->log[entry_i]; + + // calc delta + exittime = update_usecs_since_boot() - run->start_trigger.mark; + delta = exittime - entry->time; + + if ((run->filters.min_delta && delta < run->filters.min_delta) || + (run->filters.max_delta && delta > run->filters.max_delta)) { +#ifdef KFI_DEBUG + run->filters.cnt.delta++; +#endif + free_entry(run, entry_i); + } else { + entry->delta = delta; + } +} + + +static inline int __noinstrument test_trigger(struct kfi_run* run, + int start_trigger, + int func_entry, + void* func_addr) +{ + unsigned long time, base_time; + int ret = 0; + struct kfi_trigger* t; + + t = start_trigger ? &run->start_trigger : &run->stop_trigger; + + switch (t->type) { + case TRIGGER_TIME: + time = update_usecs_since_boot(); + if (start_trigger) { + /* trigger start time based from boot */ + base_time = 0; + } else { + /* trigger stop time based from start trigger time */ + base_time = run->start_trigger.mark; + } + + if (time >= base_time + t->time) { + t->mark = time; // mark trigger time + ret = 1; + } + break; + case TRIGGER_FUNC_ENTRY: + if (func_entry && func_addr == t->func_addr) { + time = update_usecs_since_boot(); + t->mark = time; // mark trigger time + ret = 1; + } + break; + case TRIGGER_FUNC_EXIT: + if (!func_entry && func_addr == t->func_addr) { + time = update_usecs_since_boot(); + t->mark = time; // mark trigger time + ret = 1; + } + break; + default: + break; + } + + return ret; +} + + +static inline void __noinstrument +func_entry_exit(void *this_fn, void *call_site, int func_entry) +{ + unsigned long flags; + struct kfi_run* run; + + local_irq_save(flags); + if (in_entry_exit) { + local_irq_restore(flags); + return; + } + in_entry_exit = 1; + + update_usecs_since_boot(); + + run = run_curr; + + if (!run || run->complete) { + goto entry_exit_byebye; + } + + if (!run->triggered) { + if (!(run->triggered = test_trigger(run, 1, + func_entry, + this_fn))) { + goto entry_exit_byebye; + } + } + + if (!test_filters(&run->filters, this_fn)) { + if (func_entry) + do_func_entry(run, this_fn, call_site); + else + do_func_exit(run, this_fn, call_site); + } + + if (!run->complete) { + run->complete = test_trigger(run, 0, + func_entry, + this_fn); + } + + if (run->complete) { + if (waitqueue_active(&kfi_wait)) + wake_up_interruptible(&kfi_wait); + if (run->next != NULL) + run_curr = run->next; + } + + entry_exit_byebye: + in_entry_exit = 0; + local_irq_restore(flags); +} + + +void __noinstrument __cyg_profile_func_enter (void *this_fn, void *call_site) +{ + func_entry_exit(this_fn, call_site, 1); +} + +void __noinstrument __cyg_profile_func_exit (void *this_fn, void *call_site) +{ + func_entry_exit(this_fn, call_site, 0); +} + + +#define dump_str(buf, len, fmt, arg...) \ + if (buf) len += sprintf(buf + len, fmt, ## arg); \ + else len += printk(KERN_EMERG fmt, ## arg) + + +static struct kfi_run* __noinstrument find_run(int id) +{ + struct kfi_run* run = run_head; + + while (run && run->id != id) + run = run->next; + + return run; +} + +static int __noinstrument +print_trigger(char* buf, int len, struct kfi_trigger* t, int start_trigger) +{ + char trigbuf[80]; + + switch (t->type) { + case TRIGGER_DEV: + sprintf(trigbuf, "system call\n"); + break; + case TRIGGER_TIME: + sprintf(trigbuf, "time at %lu usec from %s\n", + t->time, start_trigger ? "boot" : "start trigger"); + break; + case TRIGGER_FUNC_ENTRY: + sprintf(trigbuf, "entry to function 0x%08lx\n", + (unsigned long)t->func_addr); + break; + case TRIGGER_FUNC_EXIT: + sprintf(trigbuf, "exit from function 0x%08lx\n", + (unsigned long)t->func_addr); + break; + case TRIGGER_LOG_FULL: + sprintf(trigbuf, "log full\n"); + break; + default: + sprintf(trigbuf, "?\n"); + break; + } + + dump_str(buf, len, "Logging %s at %lu usec by %s", + (start_trigger ? "started" : "stopped"), + t->mark, trigbuf); + + return len; +} + +int __noinstrument kfi_dump_log(char* buf) +{ + int i, len = 0; + struct kfi_run* run = run_curr; + struct kfi_filters* filters = &run->filters; + + if (!run) { + dump_str(buf, len, "\nNo logging run registered\n"); + return len; + } + + if (!run->triggered) { + dump_str(buf, len, "\nLogging not yet triggered\n"); + return len; + } + + if (!run->complete) { + dump_str(buf, len, "\nLogging is running\n"); + return len; + } + + dump_str(buf, len, "\nKernel Instrumentation Run ID %d\n\n", + run->id); + + dump_str(buf, len, "Filters:\n"); + if (filters->func_list_size) { + dump_str(buf, len, "\t%d-entry function list\n", + filters->func_list_size); + } + if (filters->min_delta) { + dump_str(buf, len, "\t%ld usecs minimum execution time\n", + filters->min_delta); + } + if (filters->max_delta) { + dump_str(buf, len, "\t%ld usecs maximum execution time\n", + filters->max_delta); + } + if (filters->no_ints) { + dump_str(buf, len, "\tno functions in interrupt context\n"); + } + if (filters->only_ints) { + dump_str(buf, len, + "\tno functions NOT in interrupt context\n"); + } + if (filters->func_list) { + dump_str(buf, len, "\tfunction list\n"); + } + +#ifdef KFI_DEBUG + dump_str(buf, len, "\nFilter Counters:\n"); + + if (filters->min_delta || filters->max_delta) { + dump_str(buf, len, "\nExecution time filter count = %d\n", + filters->cnt.delta); + } + if (filters->no_ints) { + dump_str(buf, len, + "No Interrupt functions filter count = %d\n", + filters->cnt.no_ints); + } + if (filters->only_ints) { + dump_str(buf, len, + "Only Interrupt functions filter count = %d\n", + filters->cnt.only_ints); + } + if (filters->func_list_size) { + dump_str(buf, len, "Function List filter count = %d\n", + filters->cnt.func_list); + } + dump_str(buf, len, "Total entries filtered = %d\n", + filters->cnt.delta + + filters->cnt.no_ints + + filters->cnt.only_ints + + filters->cnt.func_list); + dump_str(buf, len, "Entries not found = %d\n", run->notfound); +#endif + dump_str(buf, len, "\nNumber of entries after filters = %d\n\n", + run->next_entry); + + len += print_trigger(buf, len, &run->start_trigger, 1); + len += print_trigger(buf, len, &run->stop_trigger, 0); + + /* print out header */ + dump_str(buf, len, "\n"); + dump_str(buf, len, + " Entry Delta PID Function Caller\n"); + dump_str(buf, len, + "-------- -------- -------- -------- --------\n"); + + for (i=0; i < run->next_entry; i++) { + dump_str(buf, len, "%8lu %8lu %7d%s %08x %08x\n", + run->log[i].time, + run->log[i].delta, + run->log[i].pid, + (run->log[i].pid == INTR_CONTEXT) ? "i" : " ", + (unsigned int)run->log[i].va, + (unsigned int)run->log[i].call_site); + } + + return len; +} + +int __noinstrument kfi_read_proc(char *buf, char **start, off_t fpos, + int length, int *eof, void *data) +{ + int len = kfi_dump_log(buf); + + if (fpos >= len) { + *start = buf; + *eof = 1; + return 0; + } + *start = buf + fpos; + if ((len -= fpos) > length) + return length; + *eof = 1; + return len; +} + + +static void __noinstrument kfi_reset(void) +{ + unsigned long flags; + struct kfi_run* run; + +#ifdef CONFIG_KFI_STATIC_RUN + run = kfi_last_run->next; +#else + run = run_head; +#endif + + local_irq_save(flags); + if (run_curr) + run_curr->complete = 1; + local_irq_restore(flags); + + while (run) { + struct kfi_run* tmp = run; + kfree(run->log); + if (run->filters.func_list) + kfree(run->filters.func_list); + run = run->next; + kfree(tmp); + } + + next_run_id = 0; + run_head = run_tail = run_curr = NULL; +} + + +static int __noinstrument kfi_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + unsigned long flags; + struct kfi_run* run; + struct kfi_run urun; + struct kfi_entry* ulog; + void** ufunc_list; + int ufunc_list_size; + + switch (cmd) { + case KFI_RESET: + kfi_reset(); + break; + case KFI_NEW_RUN: + if (verify_area(VERIFY_READ, (void *)arg, + sizeof(struct kfi_run)) || + verify_area(VERIFY_WRITE, (void *)arg, + sizeof(struct kfi_run))) + return -EFAULT; + if ((run = (struct kfi_run*)kmalloc(sizeof(struct kfi_run), + GFP_KERNEL)) == NULL) + return -ENOMEM; + copy_from_user(&urun, (struct kfi_run*)arg, + sizeof(struct kfi_run)); + if (urun.num_entries > MAX_RUN_LOG_ENTRIES) { + kfree(run); + return -EINVAL; + } + *run = urun; + run->id = next_run_id++; + run->triggered = run->complete = 0; + run->next_entry = 0; +#ifdef KFI_DEBUG + run->notfound = 0; + memset(&run->filters.cnt, 0, sizeof(run->filters.cnt)); +#endif + run->next = NULL; + urun = *run; + run->log = (struct kfi_entry*) + kmalloc(sizeof(struct kfi_entry) * run->num_entries, + GFP_KERNEL); + if (run->log == NULL) { + kfree(run); + return -ENOMEM; + } + memset(run->log, 0, + sizeof(struct kfi_entry) * run->num_entries); + + if (urun.filters.func_list) { + int size; + if (urun.filters.func_list_size > + MAX_FUNC_LIST_ENTRIES) { + kfree(run->log); + kfree(run); + return -EINVAL; + } + size = urun.filters.func_list_size * sizeof(void*); + if (verify_area(VERIFY_READ, + (void *)urun.filters.func_list, + size)) { + kfree(run->log); + kfree(run); + return -EFAULT; + } + run->filters.func_list = (void**)kmalloc(size, + GFP_KERNEL); + if (run->filters.func_list == NULL) { + kfree(run->log); + kfree(run); + return -ENOMEM; + } + copy_from_user(run->filters.func_list, + urun.filters.func_list, + size); + } + + /* new run is ready, return it to user */ + copy_to_user((struct kfi_run*)arg, &urun, + sizeof(struct kfi_run)); + /* tack it on */ + local_irq_save(flags); + if (!run_tail) { + run_head = run_tail = run_curr = run; + } else { + if (run_curr == run_tail && run_curr->complete) + run_curr = run; + run_tail->next = run; + run_tail = run; + } + local_irq_restore(flags); + break; + case KFI_START: + local_irq_save(flags); + run = run_curr; + if (!run || run->complete || run->triggered) { + local_irq_restore(flags); + return -EINVAL; + } + run->triggered = 1; + run->start_trigger.mark = update_usecs_since_boot(); + run->start_trigger.type = TRIGGER_DEV; + local_irq_restore(flags); + if (put_user(run->id, (int *)arg)) + return -EFAULT; + break; + case KFI_STOP: + local_irq_save(flags); + run = run_curr; + if (!run || run->complete) { + local_irq_restore(flags); + return -EINVAL; + } + run->complete = 1; + run->stop_trigger.mark = update_usecs_since_boot(); + run->stop_trigger.type = TRIGGER_DEV; + if (run->next != NULL) + run_curr = run->next; + local_irq_restore(flags); + if (waitqueue_active(&kfi_wait)) + wake_up_interruptible(&kfi_wait); + if (put_user(run->id, (int *)arg)) + return -EFAULT; + break; + case KFI_READ: + case KFI_READ_CURR: + if (verify_area(VERIFY_READ, (void *)arg, + sizeof(struct kfi_run)) || + verify_area(VERIFY_WRITE, (void *)arg, + sizeof(struct kfi_run))) + return -EFAULT; + copy_from_user(&urun, (struct kfi_run*)arg, + sizeof(struct kfi_run)); + + local_irq_save(flags); + + run = (cmd == KFI_READ_CURR) ? run_curr : find_run(urun.id); + if (!run) { + local_irq_restore(flags); + return -EINVAL; + } + + if (urun.log != NULL) { + if (urun.num_entries < run->num_entries) { + local_irq_restore(flags); + return -EINVAL; + } + + if (!run->complete) { + local_irq_restore(flags); + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + while (!run->complete) { + interruptible_sleep_on(&kfi_wait); + if (signal_pending(current)) + return -ERESTARTSYS; + } + local_irq_save(flags); + } + } + + ufunc_list_size = urun.filters.func_list_size; + + // save user pointers + ulog = urun.log; + ufunc_list = urun.filters.func_list; + + urun = *run; // copy run + + // restore user pointers + urun.log = ulog; + urun.filters.func_list = ufunc_list; + urun.next = NULL; + + local_irq_restore(flags); + + copy_to_user((void*)arg, &urun, sizeof(struct kfi_run)); + + if (urun.log != NULL) { + int size = run->next_entry * sizeof(struct kfi_entry); + if (verify_area(VERIFY_WRITE, (void*)urun.log, size)) + return -EFAULT; + copy_to_user((void*)urun.log, run->log, size); + } + + if (ufunc_list != NULL && run->filters.func_list != NULL) { + int size; + if (ufunc_list_size < run->filters.func_list_size) + return -EINVAL; + size = run->filters.func_list_size * sizeof(void*); + if (verify_area(VERIFY_WRITE, ufunc_list, size)) + return -EFAULT; + copy_to_user(ufunc_list, run->filters.func_list, size); + } + break; + case KFI_READ_TIMER: + if (put_user(update_usecs_since_boot(), + (unsigned long *)arg)) + return -EFAULT; + break; + default: + return -EINVAL; + } + + return 0; +} + +static unsigned int __noinstrument +kfi_poll(struct file * filp, poll_table * wait) +{ + poll_wait(filp, &kfi_wait, wait); + if (run_curr && run_curr->complete) + return POLLIN | POLLRDNORM; + return 0; +} + +int __noinstrument kfi_open(struct inode *minode, struct file *mfile) +{ + MOD_INC_USE_COUNT; + return 0; +} + +int __noinstrument kfi_release(struct inode *minode, struct file *mfile) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static loff_t __noinstrument kfi_llseek(struct file *mfile, + loff_t offset, int origint) +{ + return -ESPIPE; +} + +static struct file_operations kfi_fops = { + owner: THIS_MODULE, + llseek: kfi_llseek, + poll: kfi_poll, + ioctl: kfi_ioctl, + open: kfi_open, + release: kfi_release, +}; + +static struct miscdevice kfi_miscdev = { + KFI_MINOR, + "kfi", + &kfi_fops +}; + +static int __init __noinstrument kfi_init(void) +{ + int ret = misc_register(&kfi_miscdev); + if (ret) { + err("Register misc driver failed, errno is %d\n", ret); + return ret; + } + +#ifdef CONFIG_KFI_STATIC_RUN + run_tail = kfi_last_run; + next_run_id = kfi_num_runs; +#endif + + create_proc_read_entry("kfi", 0, NULL, + kfi_read_proc, NULL); + return 0; +} + +static void __exit __noinstrument kfi_exit(void) +{ + remove_proc_entry("kfi", NULL); + misc_deregister(&kfi_miscdev); +} + + +module_init(kfi_init); +module_exit(kfi_exit); + +EXPORT_SYMBOL(__cyg_profile_func_enter); +EXPORT_SYMBOL(__cyg_profile_func_exit); + diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/drivers/char/kfistatic.conf branch_KFI/drivers/char/kfistatic.conf --- tag_LINUX_2_6_9/drivers/char/kfistatic.conf 1969-12-31 16:00:00.000000000 -0800 +++ branch_KFI/drivers/char/kfistatic.conf 2004-07-28 15:09:19.000000000 -0700 @@ -0,0 +1,6 @@ +begin + trigger start entry start_kernel + trigger stop entry to_userspace + filter time 500,0 +# filter noint +end diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/drivers/char/Makefile branch_KFI/drivers/char/Makefile --- tag_LINUX_2_6_9/drivers/char/Makefile 2004-10-22 14:42:40.000000000 -0700 +++ branch_KFI/drivers/char/Makefile 2004-11-01 17:50:38.000000000 -0800 @@ -89,8 +89,11 @@ obj-$(CONFIG_IPMI_HANDLER) += ipmi/ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o +obj-$(CONFIG_KFI) += kfi.o +obj-$(CONFIG_KFI_STATIC_RUN) += kfistatic.o + # Files generated that shall be removed upon make clean -clean-files := consolemap_deftbl.c defkeymap.c qtronixmap.c +clean-files := consolemap_deftbl.c defkeymap.c qtronixmap.c kfistatic.c quiet_cmd_conmk = CONMK $@ cmd_conmk = scripts/conmakehash $< > $@ @@ -115,3 +118,8 @@ $(obj)/defkeymap.c $(obj)/qtronixmap.c: rm $@.tmp endif + +$(obj)/kfistatic.o: $(obj)/kfistatic.c + +$(obj)/kfistatic.c: $(src)/kfistatic.conf + $(srctree)/scripts/mkkfirun.pl $< > $@ diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/include/asm-ppc/delay.h branch_KFI/include/asm-ppc/delay.h --- tag_LINUX_2_6_9/include/asm-ppc/delay.h 2004-04-29 18:09:27.000000000 -0700 +++ branch_KFI/include/asm-ppc/delay.h 2004-08-24 11:21:14.000000000 -0700 @@ -2,6 +2,7 @@ #ifndef _PPC_DELAY_H #define _PPC_DELAY_H +#include /* for inline weirdness */ #include /* diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/include/asm-ppc/processor.h branch_KFI/include/asm-ppc/processor.h --- tag_LINUX_2_6_9/include/asm-ppc/processor.h 2004-08-04 13:30:33.000000000 -0700 +++ branch_KFI/include/asm-ppc/processor.h 2004-08-24 11:21:14.000000000 -0700 @@ -10,6 +10,7 @@ #include #include +#include /* for inline weirdness */ #include #include diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/include/linux/init.h branch_KFI/include/linux/init.h --- tag_LINUX_2_6_9/include/linux/init.h 2004-08-04 13:30:42.000000000 -0700 +++ branch_KFI/include/linux/init.h 2004-08-22 19:05:07.000000000 -0700 @@ -229,6 +229,14 @@ void __init parse_early_param(void); #define __devexitdata __exitdata #endif +#ifndef __ASSEMBLY__ +#define __instrument +#define __noinstrument __attribute__ ((no_instrument_function)) +#ifdef CONFIG_KFI +extern int kfi_dump_log(char* buf); +#endif +#endif + /* Functions marked as __devexit may be discarded at kernel link time, depending on config options. Newer versions of binutils detect references from retained sections to discarded sections and flag an error. Pointers to diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/include/linux/kfi.h branch_KFI/include/linux/kfi.h --- tag_LINUX_2_6_9/include/linux/kfi.h 1969-12-31 16:00:00.000000000 -0800 +++ branch_KFI/include/linux/kfi.h 2004-07-28 15:09:19.000000000 -0700 @@ -0,0 +1,84 @@ +#ifndef _LINUX_KFI_H +#define _LINUX_KFI_H + +#define KFI_DEBUG + +typedef struct kfi_entry { + void *va; /* VA of instrumented function */ + void *call_site; /* where this func was called */ + unsigned long time; /* function entry time since trigger start time, + in usec */ + unsigned long delta; /* delta time from entry to exit, in usec */ + int pid; +} kfi_entry_t; + +#define INTR_CONTEXT -1 + +typedef enum kfi_trigger_type { + TRIGGER_NONE = 0, + TRIGGER_TIME, + TRIGGER_FUNC_ENTRY, + TRIGGER_FUNC_EXIT, + TRIGGER_PROC, + TRIGGER_DEV, + TRIGGER_LOG_FULL +} kfi_trigger_type_t; + +typedef struct kfi_trigger { + enum kfi_trigger_type type; + union { + unsigned long time; // time since boot, in usec + void * func_addr; + }; + unsigned long mark; // time at which this trigger occured +} kfi_trigger_t; + +#define MAX_RUN_LOG_ENTRIES 20000 +#define MAX_FUNC_LIST_ENTRIES 512 + +typedef struct kfi_filters { + unsigned long min_delta; + unsigned long max_delta; + int no_ints; + int only_ints; + void** func_list; + int func_list_size; +#ifdef KFI_DEBUG + struct { + int delta; + int no_ints; + int only_ints; + int func_list; + } cnt; +#endif +} kfi_filters_t; + +typedef struct kfi_run { + int triggered; + int complete; + struct kfi_trigger start_trigger; + struct kfi_trigger stop_trigger; + struct kfi_filters filters; + struct kfi_entry* log; + int num_entries; + int next_entry; + int id; + struct kfi_run * next; + +#ifdef KFI_DEBUG + int notfound; +#endif +} kfi_run_t; + +/* Use 'i' as magic number */ +#define KFI_MAGIC 'i' + +#define KFI_RESET _IO (KFI_MAGIC, 0) +#define KFI_NEW_RUN _IOWR(KFI_MAGIC, 1, struct kfi_run) +#define KFI_START _IOR (KFI_MAGIC, 2, int) +#define KFI_STOP _IOR (KFI_MAGIC, 3, int) +#define KFI_READ _IOWR(KFI_MAGIC, 4, struct kfi_run) +#define KFI_READ_CURR _IOWR(KFI_MAGIC, 5, struct kfi_run) +#define KFI_READ_TIMER _IOR (KFI_MAGIC, 6, unsigned long) + +#endif // _LINUX_KFI_H diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/init/main.c branch_KFI/init/main.c --- tag_LINUX_2_6_9/init/main.c 2004-10-22 14:42:45.000000000 -0700 +++ branch_KFI/init/main.c 2004-11-09 15:24:16.000000000 -0800 @@ -572,6 +572,7 @@ asmlinkage void __init start_kernel(void rest_init(); } + static int __initdata initcall_debug; static int __init initcall_debug_setup(char *str) @@ -680,6 +681,12 @@ static inline void fixup_cpu_present_map #endif } +#ifdef CONFIG_KFI_STATIC_RUN +void to_userspace() +{ +} +#endif /* CONFIG_KFI_STATIC_RUN */ + static int init(void * unused) { lock_kernel(); @@ -734,6 +741,11 @@ static int init(void * unused) (void) sys_dup(0); (void) sys_dup(0); + +#ifdef CONFIG_KFI_STATIC_RUN + /* This is a stub function, for use as a stop trigger */ + to_userspace(); +#endif /* CONFIG_KFI_STATIC_RUN */ /* * We try each of these until one succeeds. diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/kernel/sys.c branch_KFI/kernel/sys.c --- tag_LINUX_2_6_9/kernel/sys.c 2004-10-22 14:39:55.000000000 -0700 +++ branch_KFI/kernel/sys.c 2004-11-01 17:55:24.000000000 -0800 @@ -28,6 +28,10 @@ #include #include +#ifdef CONFIG_KFI_DUMP +#include +#endif + #ifndef SET_UNALIGN_CTL # define SET_UNALIGN_CTL(a,b) (-EINVAL) #endif @@ -456,6 +460,9 @@ asmlinkage long sys_reboot(int magic1, i notifier_call_chain(&reboot_notifier_list, SYS_RESTART, NULL); system_state = SYSTEM_RESTART; device_shutdown(); +#ifdef CONFIG_KFI_DUMP + kfi_dump_log(NULL); +#endif printk(KERN_EMERG "Restarting system.\n"); machine_restart(NULL); break; @@ -472,6 +479,9 @@ asmlinkage long sys_reboot(int magic1, i notifier_call_chain(&reboot_notifier_list, SYS_HALT, NULL); system_state = SYSTEM_HALT; device_shutdown(); +#ifdef CONFIG_KFI_DUMP + kfi_dump_log(NULL); +#endif printk(KERN_EMERG "System halted.\n"); machine_halt(); unlock_kernel(); @@ -482,6 +492,9 @@ asmlinkage long sys_reboot(int magic1, i notifier_call_chain(&reboot_notifier_list, SYS_POWER_OFF, NULL); system_state = SYSTEM_POWER_OFF; device_shutdown(); +#ifdef CONFIG_KFI_DUMP + kfi_dump_log(NULL); +#endif printk(KERN_EMERG "Power down.\n"); machine_power_off(); unlock_kernel(); @@ -498,6 +511,9 @@ asmlinkage long sys_reboot(int magic1, i notifier_call_chain(&reboot_notifier_list, SYS_RESTART, buffer); system_state = SYSTEM_RESTART; device_shutdown(); +#ifdef CONFIG_KFI_DUMP + kfi_dump_log(NULL); +#endif printk(KERN_EMERG "Restarting system with command '%s'.\n", buffer); machine_restart(buffer); break; diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/Makefile branch_KFI/Makefile --- tag_LINUX_2_6_9/Makefile 2004-10-22 14:38:41.000000000 -0700 +++ branch_KFI/Makefile 2004-11-01 17:44:00.000000000 -0800 @@ -490,6 +490,10 @@ ifndef CONFIG_FRAME_POINTER CFLAGS += -fomit-frame-pointer endif +ifdef CONFIG_KFI +CFLAGS += -finstrument-functions +endif + ifdef CONFIG_DEBUG_INFO CFLAGS += -g endif diff -pruN -X ../cvs-admin/dontdiff tag_LINUX_2_6_9/scripts/mkkfirun.pl branch_KFI/scripts/mkkfirun.pl --- tag_LINUX_2_6_9/scripts/mkkfirun.pl 1969-12-31 16:00:00.000000000 -0800 +++ branch_KFI/scripts/mkkfirun.pl 2004-07-28 15:09:19.000000000 -0700 @@ -0,0 +1,241 @@ +#!/usr/bin/perl +# +# BRIEF MODULE DESCRIPTION +# Parses a Kernel Function Instrumentation config file. The output +# is C code representing the KFI logging run parameters listed in +# in the config file. +# +# Copyright 2002 MontaVista Software Inc. +# Author: MontaVista Software, Inc. +# stevel@mvista.com or source@mvista.com +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN +# NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 675 Mass Ave, Cambridge, MA 02139, USA. +# + +sub parse_args { + local($argstr) = $_[0]; + local(@arglist); + local($i) = 0; + + for (;;) { + while ($argstr =~ /^\s*$/ || $argstr =~ /^\s*\#/) { + $argstr = ; + } + + while ($argstr =~ s/^\s*(\w+)\s*(.*)/\2/) { + $arglist[$i++] = $1; + if (!($argstr =~ s/^\,(.*)/\1/)) { + return @arglist; + } + } + } +} + +sub parse_run { + local($thisrun, $nextrun) = @_; + local($start_type) = "TRIGGER_NONE"; + local($stop_type) = "TRIGGER_NONE"; + + local($filter_noint) = 0; + local($filter_onlyint) = 0; + local(@filter_func_list) = (0); + local($filter_func_list_size) = 0; + local(@filter_time_args) = (0,0); + local($logsize) = "MAX_RUN_LOG_ENTRIES"; + + while () { + + last if /^\s*end\b/; + + if ( /^\s*trigger\s+(\w+)\s+(\w+)\b\s*([\w\,\s]*)/ ) { + + $trigwhich = $1; + $trigtype = $2; + @trigargs = &parse_args($3); + + if ($trigwhich eq "start") { + if ($trigtype eq "entry") { + $start_type = "TRIGGER_FUNC_ENTRY"; + } elsif ($trigtype eq "exit") { + $start_type = "TRIGGER_FUNC_EXIT"; + } elsif ($trigtype eq "time") { + $start_type = "TRIGGER_TIME"; + } else { + die "#### PARSE ERROR: invalid trigger type ####\n"; + } + @start_args = @trigargs; + } elsif ($trigwhich eq "stop") { + if ($trigtype eq "entry") { + $stop_type = "TRIGGER_FUNC_ENTRY"; + } elsif ($trigtype eq "exit") { + $stop_type = "TRIGGER_FUNC_EXIT"; + } elsif ($trigtype eq "time") { + $stop_type = "TRIGGER_TIME"; + } else { + die "#### PARSE ERROR: invalid trigger type ####\n"; + } + @stop_args = @trigargs; + } else { + die "#### PARSE ERROR: invalid trigger ####\n"; + } + + } elsif ( /^\s*filter\s+(\w+)\b\s*([\w\,\s]*)/ ) { + + $filtertype = $1; + + if ($filtertype eq "time") { + @filter_time_args = &parse_args($2); + } elsif ($filtertype eq "noint") { + $filter_noint = 1; + } elsif ($filtertype eq "onlyint") { + $filter_onlyint = 1; + } elsif ($filtertype eq "funclist") { + @filter_func_list = &parse_args($2); + $filter_func_list_size = $#filter_func_list + 1; + } else { + die "#### PARSE ERROR: invalid filter ####\n"; + } + + } elsif ( /^\s*logsize\s+(\d+)/ ) { + @logargs = &parse_args($1); + $logsize = $logargs[0]; + } + } + + # done parsing this run, now spit out the C code + + # print forward reference to next run + if ($nextrun != 0) { + printf("kfi_run_t kfi_run%d;\n", $nextrun); + } + + if ($start_type eq "TRIGGER_FUNC_ENTRY" || + $start_type eq "TRIGGER_FUNC_EXIT") { + printf("extern void %s(void);\n\n", $start_args[0]); + } + + if ($stop_type eq "TRIGGER_FUNC_ENTRY" || + $stop_type eq "TRIGGER_FUNC_EXIT") { + printf("extern void %s(void);\n\n", $stop_args[0]); + } + + if ($filter_func_list_size) { + $funclist_name = sprintf("run%d_func_list", $thisrun); + + for ($i = 0; $i < $filter_func_list_size; $i++) { + print "extern void $filter_func_list[$i](void);\n" + if (!($filter_func_list[$i] =~ /^[0-9]/)); + } + + printf("\nstatic void* %s[] = {\n", $funclist_name); + + for ($i = 0; $i < $filter_func_list_size; $i++) { + printf("\t(void*)%s,\n", $filter_func_list[$i]); + } + printf("};\n\n"); + } else { + $funclist_name = "NULL"; + } + + printf("static kfi_entry_t run%d_log[%s];\n\n", $thisrun, $logsize); + + printf("kfi_run_t kfi_run%d = {\n", $thisrun); + + printf("\t0, 0,\n"); # triggered and complete flags + + # start trigger struct + if ($start_type eq "TRIGGER_FUNC_ENTRY" || + $start_type eq "TRIGGER_FUNC_EXIT") { + printf("\t{ %s, { func_addr: (void*)%s } },\n", + $start_type, $start_args[0]); + } elsif ($start_type eq "TRIGGER_TIME") { + printf("\t{ %s, { time: %d } },\n", $start_type, $start_args[0]); + } else { + printf("\t{ %s, {0} },\n", $start_type); + } + + # stop trigger struct + if ($stop_type eq "TRIGGER_FUNC_ENTRY" || + $stop_type eq "TRIGGER_FUNC_EXIT") { + printf("\t{ %s, { func_addr: (void*)%s } },\n", + $stop_type, $stop_args[0]); + } elsif ($stop_type eq "TRIGGER_TIME") { + printf("\t{ %s, { time: %d } },\n", $stop_type, $stop_args[0]); + } else { + printf("\t{ %s, {0} },\n", $stop_type); + } + + # filters struct + printf("\t{ %d, %d, %d, %d, %s, %d, {0} },\n", + $filter_time_args[0], $filter_time_args[1], + $filter_noint, $filter_onlyint, + $funclist_name, $filter_func_list_size); + + if ($nextrun != 0) { + printf("\trun%d_log, %s, 0, %d, &kfi_run%d,\n", + $thisrun, $logsize, $thisrun, $nextrun); + } else { + printf("\trun%d_log, %s, 0, %d, NULL,\n", + $thisrun, $logsize, $thisrun); + } + + printf("};\n\n"); +} + + +$numrun = 0; + +open(RUNFILE, $ARGV[0]) || die "Can't open KFI run config file"; + +# first pass get number of run configs listed +while () { + if ( /^\s*begin\b/ ) { + $numrun++; + } +} + +$numrun != 0 || die "No run listed???\n"; + +close(RUNFILE); +open(RUNFILE, "$ARGV[0]"); + +# print warning +print "/* DO NOT EDIT! It was automatically generated by mkkfirun.pl */\n\n"; + +# print needed headers +print "#include \n"; +print "#include \n\n"; + +$runindex = 0; +while () { + if ( /^\s*begin\b/ ) { + if ($runindex == $numrun-1) { + &parse_run($runindex, 0); + } else { + &parse_run($runindex, $runindex+1); + } + $runindex++; + } +} + +printf("const int kfi_num_runs = %d;\n", $numrun); +printf("kfi_run_t* kfi_first_run = &kfi_run0;\n"); +printf("kfi_run_t* kfi_last_run = &kfi_run%d;\n", $numrun-1);