/*------------------------------------------------------------------------------------------*\ * * Copyright (C) 2006 AVM GmbH * * 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 program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \*------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include "avm_sammel.h" #include "avm_profile.h" /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #include #if defined(CONFIG_PROC_FS) #include #include #include #include struct _arch_profile_ctrl arch_profile_ctrl; static struct _cpucore_profile cpu_config_default = { cpu_nr_offset: 0, vpe_nr: 1, next_core: NULL }; static void __proc_read_profiler_perform(struct seq_file *seq, unsigned int format); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _pid_field { unsigned short pid; unsigned char comm[TASK_COMM_LEN]; }; extern char *avm_profile_data_short_names[avm_profile_data_type_unknown + 1]; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ int __get_userinfo(char *buf, unsigned int maxbuflen, struct mm_struct *mmm, unsigned long addr) { struct vm_area_struct *vm; unsigned int i = 0; if(mmm == NULL) { return 1; } vm = mmm->mmap; while(vm) { /*--- printk(KERN_INFO"%s[%x]:%p %x - %x vm_mm %p\n", __func__, addr, vm, vm->vm_start, vm->vm_end, vm->vm_mm); ---*/ if((addr >= vm->vm_start) && (addr < vm->vm_end)) { snprintf(buf, maxbuflen,"seg=%3u of=0x%08lx/0x%lx [%s]", i, addr - (unsigned long)vm->vm_start, (unsigned long)vm->vm_end - (unsigned long)vm->vm_start, (vm->vm_file && vm->vm_file->f_path.dentry) ? (char *)vm->vm_file->f_path.dentry->d_name.name : ""); /*--- printk(KERN_INFO"%s", buf); ---*/ return 0; } vm = vm->vm_next; i++; } return 1; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ int get_user_info(char *buf, unsigned int maxbuflen, pid_t pid, unsigned long addr) { struct pid *ppid; unsigned int ret = 1; buf[0] = 0; /*--- printk(KERN_INFO"%s: pid=%u: addr=%08x ppid=%p\n", __func__, pid, addr, find_vpid(pid)); ---*/ #if defined(CONFIG_MIPS) if(addr >= KSEG0) { return 1; } #endif/*--- #if defined(CONFIG_MIPS) ---*/ if(pid && (ppid = find_get_pid(pid))) { struct task_struct *tsk = get_pid_task(ppid, PIDTYPE_PID); if (tsk) { /*--- printk(KERN_INFO"%s: -> %s: addr=%08x active_mm=%p mm=%p\n", __func__, tsk->comm, addr, tsk->active_mm, tsk->mm); ---*/ if((ret = __get_userinfo(buf, maxbuflen, tsk->active_mm, addr))) { ret = __get_userinfo(buf, maxbuflen, tsk->mm, addr); } put_task_struct(tsk); } put_pid(ppid); } return ret; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void format_profile_header(struct seq_file *seq, unsigned long timediff) { unsigned int cores = 0; const struct _cpucore_profile *cpu_profile; cpu_profile = arch_profile_ctrl.cpu_profile; while(cpu_profile) { cores++; cpu_profile = cpu_profile->next_core; } seq_printf(seq, "# measure time %lu msec, real cpu-cores %d", timediff, cores ? cores : 1); cores = 0; cpu_profile = arch_profile_ctrl.cpu_profile; while(cpu_profile) { seq_printf(seq, " [%u] cpu_ofs %u vpes %u", cores, cpu_profile->cpu_nr_offset, cpu_profile->vpe_nr); cores++; cpu_profile = cpu_profile->next_core; } seq_printf(seq, "\n"); __proc_read_profiler_perform(seq, 1); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ unsigned int format_profile_line(struct seq_file *seq, struct _avm_profile_data *data, struct _pid_field act_pid_percpu[], unsigned int mode, unsigned int supress_kernel_output) { char *description = ""; unsigned char Symbols[256]; unsigned char LrSymbols[256]; unsigned int len = 0; struct task_struct *tsk = NULL; struct pid *pid; pid_t act_pid = 0; char *comm = NULL; if(data->cpu_id > NR_CPUS) { return len; } act_pid = act_pid_percpu[data->cpu_id].pid; comm = act_pid_percpu[data->cpu_id].comm; switch(data->type) { case avm_profile_data_type_unknown: default: printk(KERN_ERR"[simple-profiling] internal error data type %d unknown (time=%x)\n", data->type, data->time); break; case avm_profile_data_type_code_begin: case avm_profile_data_type_code_end: /*--- kein break; ---*/ case avm_profile_data_type_code_address_info: act_pid = data->id; if(act_pid != act_pid_percpu[data->cpu_id].pid) { if(act_pid == AVM_PROFILE_IDLE_ID) { strncpy(comm, "IDLE", sizeof(act_pid_percpu[data->cpu_id].comm)); } else if(act_pid && (pid = find_get_pid(act_pid))) { tsk = get_pid_task(pid, PIDTYPE_PID); if (tsk) { strncpy(comm, tsk->comm, sizeof(act_pid_percpu[data->cpu_id].comm)); put_task_struct(tsk); } put_pid(pid); } else { unsigned int destsize =sizeof(act_pid_percpu[data->cpu_id].comm); #if defined(AVM_PROFILE_CURRENT_COMM_INCLUDED) unsigned int comm_size = min(destsize, sizeof(data->comm)); memcpy(comm, data->comm, comm_size); comm[comm_size] = 0; if(act_pid) { comm_size = strlen(comm); strncat(comm, " (fin)", destsize - comm_size); } #else snprintf(comm, destsize, "PID_%u", act_pid); #endif/*--- #if defined(AVM_PROFILE_CURRENT_COMM_INCLUDED) ---*/ } } act_pid_percpu[data->cpu_id].pid = act_pid; if(get_user_info(Symbols, sizeof(Symbols), act_pid, data->addr #if defined(CONFIG_MIPS) || defined(CONFIG_ARM) & ~0x3 /*--- falls mips16/tumb-code: keine Unaligneds provozieren ---*/ #endif/*--- #if defined(CONFIG_MIPS) || defined(CONFIG_ARM) ---*/ )){ sprint_symbol(Symbols, data->addr); } if(get_user_info(LrSymbols, sizeof(LrSymbols), act_pid, data->lr #if defined(CONFIG_MIPS) || defined(CONFIG_ARM) & ~0x3 /*--- falls mips16/tumb-code: keine Unaligneds provozieren ---*/ #endif/*--- #if defined(CONFIG_MIPS) || defined(CONFIG_ARM) ---*/ )){ sprint_symbol(LrSymbols, data->lr); } switch(mode) { case 3: seq_printf(seq, "%x;C%x;T%x;S0x%08x;0x%08X;0x%08X;0x%08X;%s;0x%08x;%s;0x%08x;%s;%.*s;%u\n", data->cpu_id, data->core_id, data->tc_id, data->cpu_status, data->time, #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) data->total_access, data->total_activate, #else/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ 0,0, #endif/*--- #else ---*//*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ avm_profile_data_short_names[data->type], data->addr, Symbols, data->lr, LrSymbols, TASK_COMM_LEN, comm, data->id); if(supress_kernel_output == 0) printk("c"); break; case 4-5: seq_printf(seq, "%x;C%x;T%x;S0x%08x;0x%08X: %s 0x%08x %s lr=0x%08x %s %.*s pid=%u\n", data->cpu_id, data->core_id, data->tc_id, data->cpu_status, data->time, avm_profile_data_short_names[data->type], data->addr, Symbols, data->lr, LrSymbols, TASK_COMM_LEN, comm, data->id); if(supress_kernel_output == 0)printk("c"); break; } break; case avm_profile_data_type_trace_skb: if(supress_kernel_output == 0)printk("s"); goto print_work_trace; case avm_profile_data_type_hw_irq_begin: case avm_profile_data_type_hw_irq_end: description = "interrupted by irq"; goto print_work_trace; case avm_profile_data_type_sw_irq_begin: case avm_profile_data_type_sw_irq_end: case avm_profile_data_type_timer_begin: case avm_profile_data_type_timer_end: case avm_profile_data_type_tasklet_begin: case avm_profile_data_type_tasklet_end: case avm_profile_data_type_hi_tasklet_begin: case avm_profile_data_type_hi_tasklet_end: case avm_profile_data_type_trigger_tasklet_begin: case avm_profile_data_type_trigger_tasklet_end: case avm_profile_data_type_workitem_begin: case avm_profile_data_type_workitem_end: case avm_profile_data_type_func_begin: case avm_profile_data_type_func_end: case avm_profile_data_type_trigger_user_begin: case avm_profile_data_type_trigger_user_end: description = "id:"; /*--- kein break ---*/ print_work_trace: if(get_user_info(Symbols, sizeof(Symbols), act_pid, data->addr)){ sprint_symbol(Symbols, data->addr); } if(get_user_info(LrSymbols, sizeof(LrSymbols), act_pid, data->lr)){ sprint_symbol(LrSymbols, data->lr); } switch(mode) { case 3: seq_printf(seq, "%x;C%x;T%x;S0x%08x;0x%08X;0x%08X;0x%08X;%s;0x%08x;%s;0x%08x;%s;%.*s;%u;\n", data->cpu_id, data->core_id, data->tc_id, data->cpu_status, data->time, #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) data->total_access, data->total_activate, #else/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ 0,0, #endif/*--- #else ---*//*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ avm_profile_data_short_names[data->type], data->addr, Symbols, data->lr, LrSymbols, TASK_COMM_LEN, comm, data->id); if(supress_kernel_output == 0)printk("d"); break; case 4-5: seq_printf(seq, "%x;C%x;T%x;S0x%08x;0x%08X:%s 0x%08x %s lr=0x%08x %s (%.*s %s %u);\n", data->cpu_id, data->core_id, data->tc_id, data->cpu_status, data->time, avm_profile_data_short_names[data->type], data->addr, Symbols, data->lr, LrSymbols, TASK_COMM_LEN, comm, description, data->id); if(supress_kernel_output == 0)printk("d"); break; } } return len; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _profile_readcsv { unsigned int pos; unsigned int entries; unsigned long timediff; struct _pid_field act_pid[NR_CPUS]; struct _avm_profile_data *data; unsigned int cnt; unsigned int percent; unsigned int one_percent; }; /*--------------------------------------------------------------------------------*\ Returns false if pos at or past end of file. \*--------------------------------------------------------------------------------*/ static int update_iter(struct _profile_readcsv *iter, loff_t pos) { /* Module symbols can be accessed randomly. */ iter->data = avm_simple_profiling_by_idx(pos); iter->pos = pos; if(iter->data == NULL) { printk(KERN_CONT"[100%%]"); avm_simple_profiling_unset_busy(); return 0; } return 1; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void *s_next(struct seq_file *m, void *p, loff_t *pos) { (*pos)++; if (!update_iter(m->private, *pos)) { return NULL; } return p; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void *s_start(struct seq_file *m, loff_t *pos) { if (!update_iter(m->private, *pos)) { return NULL; } return m->private; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void s_stop(struct seq_file *m __attribute__((unused)), void *p __attribute__((unused))) { avm_simple_profiling_unset_busy(); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int s_show(struct seq_file *m, void *p __attribute__((unused))) { struct _profile_readcsv *iter = m->private; if(unlikely(iter->pos == 0)) { format_profile_header(m, iter->timediff); } if (iter->data == NULL) { avm_simple_profiling_unset_busy(); return 0; } if(unlikely(iter->cnt++ >= iter->one_percent)) { iter->cnt = 0; printk(KERN_CONT"[%u%%]", ++iter->percent); } format_profile_line(m, iter->data, iter->act_pid, 3, 1); return 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static const struct seq_operations readcsv_op = { .start = s_start, .next = s_next, .stop = s_stop, .show = s_show }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void reset_iter(struct _profile_readcsv *iter, loff_t new_pos) { unsigned int i; avm_simple_profiling_enable(sp_enable_off, 0, 0, &iter->entries, &iter->timediff, 1); iter->pos = new_pos; iter->cnt = 0; iter->percent = 0; iter->one_percent= iter->entries / 100; printk(KERN_INFO"\[%u%%]", iter->percent); for(i = 0; i < sizeof(iter->act_pid) / sizeof(iter->act_pid[0]); i++) { strcpy(iter->act_pid[i].comm, "PID_0"); iter->act_pid[i].pid = 0; } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int readcsv_open(struct inode *inode __attribute__((unused)), struct file *file) { /* * We keep iterator in m->private, since normal case is to * s_start from where we left off, so we avoid doing */ struct _profile_readcsv *iter; int ret; iter = kmalloc(sizeof(*iter), GFP_KERNEL); if (!iter) { return -ENOMEM; } reset_iter(iter, 0); ret = seq_open(file, &readcsv_op); ((struct seq_file *)file->private_data)->private = iter; return ret; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static const struct file_operations readcsv_operations = { .open = readcsv_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release_private, }; #if defined(CONFIG_KALLSYMS) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int profilestat_show(struct seq_file *seq, void *data __attribute__((unused))) { /*--- printk("%s %p %p\n", __func__, seq->private, data); ---*/ if(seq->private) { seq_printf(seq, "%s", (char *)seq->private); } return 0; } #define PROFILE_BUF_SIZE (128 << 10) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int profilestat_open(struct inode *inode __attribute__((unused)), struct file *file) { const struct _cpucore_profile *cpu_profile; unsigned int bufsize = PROFILE_BUF_SIZE; unsigned int full = 0; char *filename; char *buf; buf = kmalloc(bufsize, GFP_KERNEL); if(buf == NULL) { return -ENOMEM; } buf[0] = 0; filename = file->f_path.dentry ? (char *)file->f_path.dentry->d_name.name : ""; if(strcmp(filename, "statistic") == 0) { unsigned char *pbuf = buf; unsigned int cpu_nr = 0; cpu_profile = arch_profile_ctrl.cpu_profile ? arch_profile_ctrl.cpu_profile : &cpu_config_default; while(cpu_profile) { unsigned int txtlen = profilestat_category(pbuf, bufsize, cpu_nr, cpu_profile->cpu_nr_offset, cpu_profile->vpe_nr, full); cpu_nr++; pbuf += txtlen; bufsize -= txtlen; cpu_profile = cpu_profile->next_core; } } else if(strcmp(filename, "totalcall") == 0) { profilestat_totalcall(buf, bufsize, -1, 0, NR_CPUS, 0); } else if(strcmp(filename, "totalweight") == 0) { profilestat_totalcall(buf, bufsize, -1, 0, NR_CPUS, 1); } avm_simple_profiling_unset_busy(); /*--- printk("%s %p\n", __func__, buf); ---*/ return single_open(file, profilestat_show, buf); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int profilestat_close(struct inode *inode, struct file *file) { struct seq_file *seq = file->private_data; kfree(seq->private); seq->private = NULL; return single_release(inode, file); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static const struct file_operations profilestat_fops = { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) .owner = THIS_MODULE, #endif .open = profilestat_open, .read = seq_read, .llseek = seq_lseek, .release = profilestat_close, }; #endif /*--- #if defined(CONFIG_KALLSYMS) ---*/ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _profile_helper { char *action; char *help; unsigned int tracemask; enum _simple_profile_enable_mode mode; } profile_helper[] = { { action: "all", help: "trace all entries", tracemask: (1 << avm_profile_data_type_code_address_info) | (1 << avm_profile_data_type_hw_irq_begin) | (1 << avm_profile_data_type_hw_irq_end) | (1 << avm_profile_data_type_sw_irq_begin) | (1 << avm_profile_data_type_sw_irq_end) | (1 << avm_profile_data_type_timer_begin) | (1 << avm_profile_data_type_timer_end) | (1 << avm_profile_data_type_tasklet_begin) | (1 << avm_profile_data_type_tasklet_end) | (1 << avm_profile_data_type_hi_tasklet_begin) | (1 << avm_profile_data_type_hi_tasklet_end) | (1 << avm_profile_data_type_workitem_begin) | (1 << avm_profile_data_type_workitem_end) | (1 << avm_profile_data_type_tasklet_end) | (1 << avm_profile_data_type_hi_tasklet_end) | (1 << avm_profile_data_type_trigger_tasklet_begin) | (1 << avm_profile_data_type_trigger_tasklet_end) | (1 << avm_profile_data_type_trigger_user_begin) | (1 << avm_profile_data_type_trigger_user_end) | (1 << avm_profile_data_type_code_begin) | (1 << avm_profile_data_type_code_end) | (1 << avm_profile_data_type_func_begin) | (1 << avm_profile_data_type_func_end) | 0, mode: sp_enable_on }, { action: "stop", help: "stop tracing", tracemask: 0x0, mode: sp_enable_max }, { action: "wrap", help: "wrap if buffer full", tracemask: 0x0, mode: sp_enable_wrap }, { action: "codeuart", help: "code-trace over gpio (uart-emulation)", tracemask: (1 << avm_profile_data_type_code_address_info), mode: sp_enable_uart }, { action: "code", help: "only code-trace", tracemask: (1 << avm_profile_data_type_code_address_info), mode: sp_enable_on }, { action: "sched", help: "only schedule-trace", tracemask: (1 << avm_profile_data_type_code_begin) | (1 << avm_profile_data_type_code_end), mode: sp_enable_on }, { action: "hwirq", help: "hardware irqs", tracemask: 1 << avm_profile_data_type_hw_irq_begin | 1 << avm_profile_data_type_hw_irq_end, mode: sp_enable_on }, { action: "swirq", help: "software irqs", tracemask: 1 << avm_profile_data_type_sw_irq_begin | 1 << avm_profile_data_type_sw_irq_end, mode: sp_enable_on }, { action: "timer", help: "timer irqs", tracemask: 1 << avm_profile_data_type_timer_begin | 1 << avm_profile_data_type_timer_end, mode: sp_enable_on }, { action: "tasklet", help: "(hi-)tasklets", tracemask: 1 << avm_profile_data_type_tasklet_begin | 1 << avm_profile_data_type_tasklet_begin | 1 << avm_profile_data_type_hi_tasklet_begin | 1 << avm_profile_data_type_hi_tasklet_begin , mode: sp_enable_on }, { action: "workitem", help: "workqueues", tracemask: 1 << avm_profile_data_type_workitem_begin | 1 << avm_profile_data_type_workitem_end, mode: sp_enable_on }, { action: "trigger", help: "tasklet-trigger an user-defined trigger", tracemask: 1 << avm_profile_data_type_trigger_tasklet_begin | 1 << avm_profile_data_type_trigger_tasklet_end | 1 << avm_profile_data_type_trigger_user_begin | 1 << avm_profile_data_type_trigger_user_end, mode: sp_enable_on }, { action: "func", help: "user defined function", tracemask: 1 << avm_profile_data_type_func_begin | 1 << avm_profile_data_type_func_end, mode: sp_enable_on }, { action: "bh", help: "bottom half (sw-irq, tasklets, timer)", tracemask: 1 << avm_profile_data_type_timer_begin | 1 << avm_profile_data_type_timer_end | 1 << avm_profile_data_type_tasklet_begin | 1 << avm_profile_data_type_tasklet_end | 1 << avm_profile_data_type_sw_irq_begin | 1<< avm_profile_data_type_sw_irq_end, mode: sp_enable_on }, { action: NULL, mode: sp_enable_off } }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void lproc_profile_help(struct seq_file *seq, void *priv){ struct _profile_helper *ph = (struct _profile_helper *)priv; seq_printf(seq, "AVM Profiler Version 3.0\n" "csv - get raw-profile-list for offline evaluation\n" #if defined(CONFIG_KALLSYMS) "statistic - get profile statistic (consumption/latency)\n" "totalcall - get top of function calls\n" "totalweight - get top of function calls weighted with codelength\n" /*--- "totalweight2 - get top of function calls weighted with square-codelength\n" ---*/ #endif/*--- #if defined(CONFIG_KALLSYMS) ---*/ "action - all, stop, ... mbytes=x (see below)\n\n" ); seq_printf(seq, "parameter(s) for action:\n"); while(ph->action) { seq_printf(seq, "%-10s - %s\n", ph->action, ph->help); ph++; } seq_printf(seq, "mbytes=: size of profiler-buffer (actual: %u MiB)\n\n", (profile_BlockNeeded * (1 << PAGE_SHIFT)) / (1 << 20)); seq_printf(seq, "example: echo bh workitem wrap > /proc/avm/profile/action\n"); if(arch_profile_ctrl.performance_counter_help) { arch_profile_ctrl.performance_counter_help(seq); } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int proc_write_profiler_perform(char *buffer, void *priv __attribute__((unused))) { char parsbuf[512]; strncpy(parsbuf, buffer, sizeof(parsbuf) - 1); parsbuf[sizeof(parsbuf) -1] = '\0'; /*--- printk(KERN_INFO"%s parsbuf='%s'\n", __func__, parsbuf); ---*/ if((strstr(parsbuf, "help"))) { if(arch_profile_ctrl.performance_counter_help) { arch_profile_ctrl.performance_counter_help(NULL); } return 0; } if(arch_profile_ctrl.performance_counter_action) { arch_profile_ctrl.performance_counter_action(parsbuf); } return 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void __proc_read_profiler_perform(struct seq_file *seq, unsigned int format){ unsigned int core = 0; const struct _cpucore_profile *cpu_profile; if(arch_profile_ctrl.profiling_performance_statistic == NULL) { return; } cpu_profile = arch_profile_ctrl.cpu_profile ? arch_profile_ctrl.cpu_profile : &cpu_config_default; while(cpu_profile) { seq_printf(seq, "Performance-Counter - CORE%u\n", core); arch_profile_ctrl.profiling_performance_statistic(core, seq, format); core++; cpu_profile = cpu_profile->next_core; } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void proc_read_profiler_perform_human(struct seq_file *seq, void *priv __maybe_unused){ __proc_read_profiler_perform(seq, 2); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void proc_read_profiler_perform_list(struct seq_file *seq, void *priv __maybe_unused){ __proc_read_profiler_perform(seq, 1); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int proc_write_profiler_action(char *buffer, void *priv) { struct _profile_helper *ph = (struct _profile_helper *)priv; unsigned int tracemask = 0, mbytes, BlockNeeded; char parsbuf[256], *p; enum _simple_profile_enable_mode mode = sp_enable_off; strncpy(parsbuf, buffer, sizeof(parsbuf) - 1); parsbuf[sizeof(parsbuf) -1] = '\0'; /*--- printk(KERN_INFO"%s parsbuf='%s'\n", __func__, parsbuf); ---*/ if((p = strstr(parsbuf, "mbytes="))) { sscanf(p, "mbytes=%u", &mbytes); if(mbytes == 0) { mbytes = 2; } BlockNeeded = mbytes * (1 << (20 - PAGE_SHIFT)); avm_simple_profiling_memresize(BlockNeeded); } while(ph->action) { if(strstr(parsbuf, ph->action)) { if((ph->mode) > mode) { mode = ph->mode; } if(mode == sp_enable_uart) { tracemask = ph->tracemask; /*--- nur exklusiv code tracen ---*/ break; } tracemask |= ph->tracemask; } ph++; } if(mode >= sp_enable_max) { mode = sp_enable_off; } /*--- printk(KERN_INFO"%s %x %x\n", __func__, mode, tracemask); ---*/ avm_simple_profiling_enable(mode, 1, tracemask, NULL, NULL, 0); return 0; } static struct proc_dir_entry *profileprocdir; #define PROC_PROFILEDIR "avm/profile" /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int __init avm_profiler_init(void) { profileprocdir = proc_mkdir(PROC_PROFILEDIR, NULL); if(profileprocdir == NULL) { return 0; } proc_create("csv", 0444, profileprocdir, &readcsv_operations); #if defined(CONFIG_KALLSYMS) proc_create("statistic", 0444, profileprocdir, &profilestat_fops); proc_create("totalcall", 0444, profileprocdir, &profilestat_fops); proc_create("totalweight", 0444, profileprocdir, &profilestat_fops); /*--- proc_create("totalweight2", 0444, profileprocdir, &profilestat_fops); ---*/ #endif /*--- #if defined(CONFIG_KALLSYMS) ---*/ add_simple_proc_file( "avm/profile/help", NULL, lproc_profile_help, profile_helper); add_simple_proc_file( "avm/profile/action", proc_write_profiler_action, NULL, profile_helper); add_simple_proc_file( "avm/profile/perform", proc_write_profiler_perform, proc_read_profiler_perform_human, profile_helper); add_simple_proc_file( "avm/profile/performlist", NULL, proc_read_profiler_perform_list, NULL); printk("AVM Simple Profiling enabled Version %u.0\n", AVM_PROFILING_VERSION); return 0; } late_initcall(avm_profiler_init); #if defined(CONFIG_AVM_DEBUG_MODULE) && (CONFIG_AVM_DEBUG_MODULE == 1) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void avm_profiler_exit(void) { if(profileprocdir) { remove_proc_entry("csv", profileprocdir); #if defined(CONFIG_KALLSYMS) remove_proc_entry("statistic", profileprocdir); remove_proc_entry("totalcall", profileprocdir); remove_proc_entry("totalweight", profileprocdir); /*--- remove_proc_entry("totalweight2", profileprocdir); ---*/ #endif /*--- #if defined(CONFIG_KALLSYMS) ---*/ remove_simple_proc_file("avm/profile/action"); remove_simple_proc_file("avm/profile/perform"); remove_simple_proc_file("avm/profile/performlist"); remove_simple_proc_file("avm/profile/help"); remove_proc_entry(PROC_PROFILEDIR, NULL); profileprocdir= NULL; } } } module_exit(avm_profiler_exit); #endif/*--- #if defined(CONFIG_AVM_DEBUG_MODULE) && (CONFIG_AVM_DEBUG_MODULE == 1) ---*/ #endif/*--- #if defined(CONFIG_PROC_FS) ---*/