/*--------------------------------------------------------------------------------*\ Originalsource im amv_new -Treiber (avm_profile_stat.c) gcc profile_new.c -O3 -m32 -Wall -g -ggdb -o profile_new \*--------------------------------------------------------------------------------*/ #if defined(__KERNEL__) #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_KALLSYMS #include #endif/*--- #ifdef CONFIG_KALLSYMS ---*/ #include "avm_profile.h" #define DBG_ERR(args...) #define DBG_CALL(args...) #define DBG_ENTRIES(args...) #define DBG_TRC(args...) #else /*--- #if defined(__KERNEL__) ---*/ /*--------------------------------------------------------------------------------*\ * SOF: Offline-testtool Offline-testtool Offline-testtool Offline-testtool Offline-testtool \*--------------------------------------------------------------------------------*/ #define CONFIG_PROC_FS #define CONFIG_KALLSYMS #define NR_CPUS 4 #define PAGE_SHIFT 12 #define __GFP_NORETRY 0 #define unlikely(a) a #include #include #include #include #include "linux_avm_profile.h" #include "avm_profile.h" struct page { unsigned int page; }; static int pages_cnt; static inline struct page *alloc_pages(unsigned int dummy, unsigned int order) { pages_cnt++; return malloc( (1<< PAGE_SHIFT) * (1< (b) ? (b) : (a) /*--------------------------------------------------------------------------------*\ * Format: +0x/0x \*--------------------------------------------------------------------------------*/ static unsigned long codeaddr_extract(char *symbol, unsigned long *offset) { unsigned long codelength = 0; *offset = 0; if(symbol == NULL) { return codelength; } while(*symbol && (*symbol != '+')) symbol++; if(*symbol == '+') { sscanf(&symbol[1], "0x%lx", offset); } while(*symbol && (*symbol != '/')) symbol++; if(*symbol == '/') { sscanf(&symbol[1], "0x%lx", &codelength); } return codelength; } /*--------------------------------------------------------------------------------*\ * EOF: Offline-testtool Offline-testtool Offline-testtool Offline-testtool Offline-testtool \*--------------------------------------------------------------------------------*/ #endif /*--- #else ---*//*--- #if defined(__KERNEL__) ---*/ unsigned long gCycle_per_usec = 250; #if defined(CONFIG_KALLSYMS) && defined(CONFIG_PROC_FS) #define ARRAY_EL(a) (sizeof(a) / sizeof(a[0])) #if !defined(__KERNEL__) /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static char *avm_profile_data_short_names[] = { [avm_profile_data_type_code_address_info] "CODE", [avm_profile_data_type_trace_skb] "SKB", [avm_profile_data_type_hw_irq_begin] "BIRQ", [avm_profile_data_type_hw_irq_end] "EIRQ", [avm_profile_data_type_sw_irq_begin] "BSWI", [avm_profile_data_type_sw_irq_end] "ESWI", [avm_profile_data_type_timer_begin] "BTIM", [avm_profile_data_type_timer_end] "ETIM", [avm_profile_data_type_tasklet_begin] "BLET", [avm_profile_data_type_tasklet_end] "ELET", [avm_profile_data_type_hi_tasklet_begin] "BLHT", [avm_profile_data_type_hi_tasklet_end] "ELHT", [avm_profile_data_type_workitem_begin] "BWRK", [avm_profile_data_type_workitem_end] "EWRK", [avm_profile_data_type_func_begin] "BFUN", [avm_profile_data_type_func_end] "EFUN", [avm_profile_data_type_trigger_tasklet_begin] "BTLT", [avm_profile_data_type_trigger_tasklet_end] "ETLT", [avm_profile_data_type_trigger_user_begin] "BTLT", [avm_profile_data_type_trigger_user_end] "ETLT", [avm_profile_data_type_unknown] "ERROR" }; #endif/*--- #if !defined(__KERNEL__) ---*/ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline __u64 mylongdiv(__u64 divider, __u32 quotient) { do_div(divider, quotient); return divider; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline __u64 _CLK_TO_USEC(__u64 cycles) { do_div(cycles, gCycle_per_usec); return cycles; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline __u32 CLK_TO_USEC(__u32 cycles) { return cycles / gCycle_per_usec; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _profile_header { struct _profile_header *next; unsigned int used[NR_CPUS]; }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _profile_codeaddrcall_entry { void *addr; unsigned short id; unsigned short delete; __u32 total_calls; __u32 codelength; }; #define PROFILE_CALL_ENTRIES (((1 << PAGE_SHIFT) - sizeof(struct _profile_header)) / ((sizeof(struct _profile_codeaddrcall_entry)) + sizeof(struct _profile_header))) /*--------------------------------------------------------------------------------*\ * vermerke alle Symbole \*--------------------------------------------------------------------------------*/ struct _profile_codeaddrcallheader { struct _profile_header head; struct _profile_codeaddrcall_entry list[PROFILE_CALL_ENTRIES]; } __attribute__((packed)); static char *profile_find_symbol(void *addr, int pid, char *buf); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _profile_list_entry { void *addr; unsigned short id; unsigned short recursion; __u32 total_calls; __u32 max_latency; __u32 min_latency; __u32 max_consumption; __u64 sum_latency; __u64 part_consumption; __u64 sum_consumption; __u64 last_btime; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) __u32 total_access; __u32 total_activate; __u32 last_total_access; __u32 last_total_activate; #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ /*--- unsigned long long last_etime; ---*/ }; #define PROFILE_LIST_ENTRIES (((1 << PAGE_SHIFT) - sizeof(struct _profile_header))/ ((sizeof(struct _profile_list_entry) * NR_CPUS))) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _profile_listheader { struct _profile_header head; struct _profile_list_entry list[NR_CPUS][PROFILE_LIST_ENTRIES]; } __attribute__((packed)); /*--- #define PROFILE_LIST_ENTRIES 4 ---*/ static struct _profile_listheader *profileheader[12]; static struct _profile_list_data_type_mapper { const char *name; struct _profile_listheader **profileheader; #define END_ENTRY (0x1 << 0) #define BEGIN_ENTRY (0x1 << 1) #define CODE_ENTRY (BEGIN_ENTRY | END_ENTRY) #define TRIGGER_ENTRY (0x1 << 2) /*--- no new area only trigger-timestamp ---*/ #define BH_ENTRY (0x1 << 3) /*--- bh-area ---*/ #define IRQ_ENTRY (0x1 << 4) /*--- irq-area ---*/ #define USER_ENTRY (0x1 << 5) /*--- area, there should also be code (code not interrupted) ---*/ #define ID_COMPARE (0x1 << 6) #define ADDR_COMPARE (0x1 << 7) unsigned int operation; } data_type_mapper[avm_profile_data_type_unknown] = { [avm_profile_data_type_code_address_info] {name: "Code", profileheader: &profileheader[ 0], operation: CODE_ENTRY | ID_COMPARE }, /*--- [avm_profile_data_type_trace_skb] {name: "Skbs", profileheader: &profileheader[ 1], TRIGGER_ENTRY | ADDR_COMPARE }, ---*/ [avm_profile_data_type_hw_irq_begin] {name: "Hw-Irqs", profileheader: &profileheader[ 2], operation: IRQ_ENTRY | BEGIN_ENTRY | ID_COMPARE }, [avm_profile_data_type_hw_irq_end] {name: NULL, profileheader: &profileheader[ 2], operation: IRQ_ENTRY | END_ENTRY | ID_COMPARE }, [avm_profile_data_type_sw_irq_begin] {name: "Sw-Irqs", profileheader: &profileheader[ 3], operation: BH_ENTRY | BEGIN_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_sw_irq_end] {name: NULL, profileheader: &profileheader[ 3], operation: BH_ENTRY | END_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_timer_begin] {name: "Timers", profileheader: &profileheader[ 4], operation: BH_ENTRY | BEGIN_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_timer_end] {name: NULL, profileheader: &profileheader[ 4], operation: BH_ENTRY | END_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_tasklet_begin] {name: "Tasklets", profileheader: &profileheader[ 5], operation: BH_ENTRY | BEGIN_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_tasklet_end] {name: NULL, profileheader: &profileheader[ 5], operation: BH_ENTRY | END_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_hi_tasklet_begin] {name: "Hi-Tasklets", profileheader: &profileheader[ 6], operation: BH_ENTRY | BEGIN_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_hi_tasklet_end] {name: NULL, profileheader: &profileheader[ 6], operation: BH_ENTRY | END_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_workitem_begin] {name: "Workitems", profileheader: &profileheader[ 7], operation: BEGIN_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_workitem_end] {name: NULL, profileheader: &profileheader[ 7], operation: END_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_func_begin] {name: "func", profileheader: &profileheader[ 8], operation: USER_ENTRY | BEGIN_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_func_end] {name: NULL, profileheader: &profileheader[ 8], operation: USER_ENTRY | END_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_trigger_tasklet_begin] {name: "Tasklet-Trg", profileheader: &profileheader[ 9], operation: TRIGGER_ENTRY | BEGIN_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_trigger_tasklet_end] {name: NULL, profileheader: &profileheader[ 9], operation: TRIGGER_ENTRY | END_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_trigger_user_begin] {name: "User-Trg", profileheader: &profileheader[10], operation: TRIGGER_ENTRY | BEGIN_ENTRY | ADDR_COMPARE }, [avm_profile_data_type_trigger_user_end] {name: NULL, profileheader: &profileheader[10], operation: TRIGGER_ENTRY | END_ENTRY | ADDR_COMPARE }, /*--- [avm_profile_data_type_code_address_yield] {name: "code_yield", profileheader: &profileheader[11], operation: BEGIN_ENTRY }, ---*/ }; /*--------------------------------------------------------------------------------*\ * mit diesen Eintrag geht eine CPU in den IDLE-Zustand * return: 0 no change \*--------------------------------------------------------------------------------*/ static inline unsigned int profile_activ_cpu_mask(struct _avm_profile_data *pdata, unsigned int *cpu_mask) { unsigned int old_cpu_mask = *cpu_mask; if((pdata->type == avm_profile_data_type_code_address_info) && (pdata->id == AVM_PROFILE_IDLE_ID)) { *cpu_mask &= ~(1 << pdata->cpu_id); } else { *cpu_mask |= (1 << pdata->cpu_id); } return *cpu_mask != old_cpu_mask; } /*--------------------------------------------------------------------------------*\ * zaehle aktive CPU's \*--------------------------------------------------------------------------------*/ static inline unsigned int profile_count_cpus(unsigned int cpu_mask) { unsigned int cpus = 0; while(cpu_mask) { if(cpu_mask & 1) { cpus++; } cpu_mask >>= 1; } return cpus; } #if defined(__KERNEL__) /*--------------------------------------------------------------------------------*\ * buf mindestens KSYM_NAME_LEN gross \*--------------------------------------------------------------------------------*/ static char *profile_find_symbol(void *addr, pid_t pid, char *buf){ if(buf == NULL) { return buf; } if(pid != (pid_t) -1) { struct task_struct *tsk; struct pid *ppid; if(pid == AVM_PROFILE_IDLE_ID) { snprintf(buf, TASK_COMM_LEN, "IDLE"); } else if(pid && (ppid = find_get_pid(pid))) { tsk = get_pid_task(ppid, PIDTYPE_PID); if (tsk) { strncpy(buf, tsk->comm, TASK_COMM_LEN); put_task_struct(tsk); } put_pid(ppid); } else { snprintf(buf, TASK_COMM_LEN, "PID_%d", pid); } return buf; } kallsyms_lookup((unsigned long)addr, NULL, NULL, NULL, buf); return buf; } #endif/*--- #if defined(__KERNEL__) ---*/ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _profile_header *alloc_profile_header(void) { struct _profile_header *plh; struct page *ptr = alloc_pages(__GFP_NORETRY, 0); if(ptr == NULL) { return NULL; } plh = (struct _profile_header *)lowmem_page_address(ptr); memset(plh, 0, 1 << PAGE_SHIFT); return plh; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void free_profile_list(struct _profile_header **anchor) { struct _profile_header *lh_act = *anchor; while(lh_act) { unsigned long tmp = (unsigned long)lh_act; lh_act = lh_act->next; free_pages(tmp, 0); } *anchor = NULL; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _profile_codeaddrcall_entry *find_maxcodeaddrcallentry(struct _profile_codeaddrcallheader *totalcall_list, unsigned int fbit, unsigned int weight, unsigned int delete){ #if defined(DEBUG_PROFILE_STAT) char txtbuf[KSYM_NAME_LEN]; #endif/*--- #if defined(DEBUG_PROFILE_STAT) ---*/ unsigned int i; struct _profile_codeaddrcall_entry *max_pce = NULL; struct _profile_codeaddrcallheader *lh_act = totalcall_list; struct _profile_codeaddrcallheader *lh_last = NULL; unsigned int last_idx = 0; unsigned int max_calls = 0; unsigned int max_weight = 0; unsigned int act_weight = 0; while(lh_act) { for(i = 0; i < lh_act->head.used[0]; i++) { struct _profile_codeaddrcall_entry *pce = &lh_act->list[i]; if((pce->delete == 0) && (pce->total_calls)) { if(weight) { if(pce->codelength) { act_weight = (pce->total_calls << fbit) / pce->codelength; if(act_weight > max_weight) { max_weight = act_weight; max_pce = pce; last_idx = i; lh_last = lh_act; } } } else if(pce->total_calls > max_calls) { max_calls = pce->total_calls; max_pce = pce; last_idx = i; lh_last = lh_act; } } } lh_act = (struct _profile_codeaddrcallheader *)lh_act->head.next; } if(max_pce && delete) { max_pce->delete = 1; /*--- mark as deleted ---*/ if(lh_last->head.used[0] == last_idx + 1) { lh_last->head.used[0] = last_idx; } } /*--- DBG_ERR("%p[%d] match id=%u codelength=%u addr=%p(%s)\n", lh_last, last_idx, max_pce->id, max_pce->codelength, max_pce->addr, profile_find_symbol(max_pce->addr, -1, txtbuf)); ---*/ return max_pce; } /*--------------------------------------------------------------------------------*\ * Suche call-Eintrag entsprechend pdata \*--------------------------------------------------------------------------------*/ static struct _profile_codeaddrcall_entry *find_codeaddrcall_entry(struct _profile_codeaddrcallheader **anchor, struct _avm_profile_data *pdata, __u32 *sumcode_length) { char txtbuf[KSYM_NAME_LEN]; struct _profile_codeaddrcall_entry *pce; unsigned int i; void *addr; unsigned long codelength = 0, offset = 0; unsigned long _addr = (unsigned long)pdata->addr; unsigned int id = pdata->id; struct _profile_codeaddrcallheader *last = NULL, *lh_act = *anchor, *new; #if defined(__KERNEL__) kallsyms_lookup(_addr, &codelength, &offset, NULL, txtbuf); _addr -= offset; #endif/*--- #if defined(__KERNEL__) ---*/ addr = (void *)_addr; /*--- DBG_ERR("%s: addr=%p offset=%x ('%s')id=%d\n", __func__, addr, offset, txtbuf, id); ---*/ while(lh_act) { for(i = 0; i < PROFILE_CALL_ENTRIES; i++) { struct _profile_codeaddrcall_entry *pce = &lh_act->list[i]; if((pce->total_calls == 0)) { lh_act->head.used[0] = i + 1; pce->addr = addr; pce->id = id; #if !defined(__KERNEL__) codelength = codeaddr_extract(profile_find_symbol(addr, -1, txtbuf), &offset); #endif/*--- #if !defined(__KERNEL__) ---*/ if(codelength) { pce->codelength = codelength; } else { /*--- approximation ---*/ pce->codelength = (1 << PAGE_SHIFT) / 10; } if(sumcode_length) *sumcode_length += pce->codelength; return pce; } if(addr == NULL) { if(pce->id != id) { continue; } } else if(pce->addr != addr) { continue; } DBG_CALL("found %p[%d] match id=%u codelength=%u addr=%p(%s)\n", lh_act, i, pce->id, pce->codelength, pce->addr, profile_find_symbol(pce->addr, -1, txtbuf)); return pce; } last = lh_act; lh_act = (struct _profile_codeaddrcallheader *)lh_act->head.next; } new = (struct _profile_codeaddrcallheader *)alloc_profile_header(); if(new == NULL) { return NULL; } if(last) { last->head.next = (struct _profile_header *)new; } else { *anchor = new; } new->head.used[0] = 1; pce = &new->list[0]; pce->addr = addr; pce->id = id; if(codelength) { pce->codelength = codelength; } else { /*--- approximation ---*/ pce->codelength = (1 << PAGE_SHIFT) / 10; } if(sumcode_length) *sumcode_length += pce->codelength; DBG_CALL("alloc in new page: : %p %p- %s id=%d codelength=%u\n", new, pce->addr, profile_find_symbol(pce->addr, -1, txtbuf), pce->id, pce->codelength); return pce; } /*--------------------------------------------------------------------------------*\ * Suche Eintrag entsprechend pdata * flag: (verodert) * BEGIN_ENTRY : falls nicht vorhanden so allozieren * END_ENTRY * ID_COMPARE: die ID vergleichen * ADDR_COMPARE: die Adresse vergleichen \*--------------------------------------------------------------------------------*/ static struct _profile_list_entry *find_profile_entry(struct _profile_listheader **anchor, struct _avm_profile_data *pdata, unsigned int flag) { #if defined(DEBUG_PROFILE_STAT) char txtbuf[KSYM_NAME_LEN]; #endif/*--- #if defined(DEBUG_PROFILE_STAT) ---*/ struct _profile_list_entry *ple; unsigned int i; unsigned int cpu_id = pdata->cpu_id; void *addr = (void *)(unsigned long)pdata->addr; unsigned int id = pdata->id; struct _profile_listheader *last = NULL, *lh_act = *anchor, *new; if(cpu_id >= ARRAY_EL(lh_act->list)) { return NULL; } DBG_TRC("%s: addr=%p id=%d flag %x->", __func__, addr, id, flag); while(lh_act) { for(i = 0; i < PROFILE_LIST_ENTRIES; i++) { struct _profile_list_entry *ple = &lh_act->list[cpu_id][i]; if(ple->last_btime == 0) { if(flag & BEGIN_ENTRY) { lh_act->head.used[cpu_id] = i + 1; ple->addr = addr; ple->id = id; DBG_TRC("alloc field %pS[%u]:%p - %s id=%d\n", lh_act, i, ple->addr, profile_find_symbol(ple->addr, -1, txtbuf), ple->id); return ple; } return NULL; } if((flag & ID_COMPARE) && (ple->id != id)) { continue; } if((flag & ADDR_COMPARE) && (ple->addr != addr)) { continue; } DBG_TRC("found %p[%d] match id=%u addr=%p\n", lh_act, i, ple->id, ple->addr); return ple; } last = lh_act; lh_act = (struct _profile_listheader *)lh_act->head.next; } if(!(flag & BEGIN_ENTRY)) { return NULL; } new = (struct _profile_listheader *)alloc_profile_header(); if(new == NULL) { return NULL; } if(last) { last->head.next = (struct _profile_header *)new; } else { *anchor = new; } new->head.used[cpu_id] = 1; ple = &new->list[cpu_id][0]; ple->addr = addr; ple->id = id; DBG_TRC("alloc in new page: : %p %p- %s id=%d\n", new, ple->addr, profile_find_symbol(ple->addr, -1, txtbuf), ple->id); return ple; } /*--------------------------------------------------------------------------------*\ * ret: idle-Eintrag \*--------------------------------------------------------------------------------*/ static struct _profile_list_entry *profile_idle(struct _profile_listheader *anchor, unsigned int cpu_id) { struct _profile_listheader *lh_act = anchor; unsigned int i; while(lh_act) { for(i = 0; i < lh_act->head.used[cpu_id]; i++) { struct _profile_list_entry *ple = &lh_act->list[cpu_id][i]; if(ple->last_btime == 0) { continue; } if(ple->id == AVM_PROFILE_IDLE_ID) { return ple; } } lh_act = (struct _profile_listheader *)lh_act->head.next; } return NULL; } /*--------------------------------------------------------------------------------*\ * ret: Summe der verbrauchten Zeit dieses Typs \*--------------------------------------------------------------------------------*/ static __u64 profile_consumption(struct _profile_listheader *anchor, unsigned int cpu_id) { struct _profile_listheader *lh_act = anchor; unsigned int i; __u64 sum_consumption = 0; while(lh_act) { for(i = 0; i < lh_act->head.used[cpu_id]; i++) { struct _profile_list_entry *ple = &lh_act->list[cpu_id][i]; if(ple->last_btime == 0) { continue; } sum_consumption += ple->sum_consumption; } lh_act = (struct _profile_listheader *)lh_act->head.next; } return sum_consumption; } #if defined(DEBUG_PROFILE_STAT) /*--------------------------------------------------------------------------------*\ * ret: Struktur mit groessten Werten \*--------------------------------------------------------------------------------*/ static void print_valid_entries(const char *prefix, struct _profile_listheader *anchor, unsigned int cpu_id, unsigned int is_code) { char txtbuf[2][KSYM_NAME_LEN]; struct _profile_listheader *lh_act = anchor; unsigned int i; printk("--------------------\n"); printk("%s: %p %s cpu_id=%u used=%d\n", __func__, anchor, prefix, cpu_id, lh_act ? lh_act->head.used[cpu_id] : 0); while(lh_act) { for(i = 0; i < lh_act->head.used[cpu_id]; i++) { struct _profile_list_entry *ple = &lh_act->list[cpu_id][i]; if(ple->last_btime) { printk("%s: %p[%d][%d]: %s(%p) %s%s id=%u: Calls: %u Consum=%llu(0x%llx) max_latency=%u(0x%x) sum_latency=%llu\n", __func__, lh_act, cpu_id, i, profile_find_symbol(ple->addr, -1, txtbuf[0]), ple->addr, is_code ? "pid=" : "", is_code ? profile_find_symbol(0, ple->id, txtbuf[1]) : "", ple->id, ple->total_calls, ple->sum_consumption, ple->sum_consumption, ple->max_latency, ple->max_latency, ple->sum_latency); } } lh_act = (struct _profile_listheader *)lh_act->head.next; } } #endif/*--- #if defined(DEBUG_PROFILE_STAT) ---*/ /*--------------------------------------------------------------------------------*\ * ret: Struktur mit groessten Verbrauch * delete: traegt diesen auch aus, so dass beim naechsten Aufruf der naechstgroesste gefunden werden kann \*--------------------------------------------------------------------------------*/ static struct _profile_list_entry *profile_entry_with_max_sum_consumption(struct _profile_listheader *anchor, unsigned int cpu_id, int delete) { struct _profile_listheader *lh_act = anchor; struct _profile_listheader *lh_last = NULL; unsigned int i, last_idx = 0; __u32 max_sum_consumption = 0; struct _profile_list_entry *ple_maxconsumption = NULL; while(lh_act) { for(i = 0; i < lh_act->head.used[cpu_id]; i++) { struct _profile_list_entry *ple = &lh_act->list[cpu_id][i]; /*--- DBG_TRC("%s: %d: %p(%d): %llu %lu\n", __func__, i, ple->addr, ple->id, ple->last_btime, ple->max_consumption); ---*/ if(ple->last_btime) { if(ple->sum_consumption > max_sum_consumption) { max_sum_consumption = ple->sum_consumption; ple_maxconsumption = ple; last_idx = i; lh_last = lh_act; } } } lh_act = (struct _profile_listheader *)lh_act->head.next; } if(ple_maxconsumption && delete) { ple_maxconsumption->last_btime = 0; /*--- invalidiere Eintrag ---*/ if(lh_last->head.used[cpu_id] == last_idx + 1) { lh_last->head.used[cpu_id] = last_idx; } } return ple_maxconsumption; } #if !defined(__KERNEL__) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void print_timelineheader(unsigned int mode, unsigned cpu_offset, unsigned int cpus) { char csv = (mode & 0x4) ? ' ' : ';'; char *punit = (mode & 0x2) ? "(us) " : "(cyc)"; if(cpus == 1) { printk(KERN_INFO"Cpu: %x\n", cpu_offset); } else { printk(KERN_INFO "Cpu's: %x-%x\n", cpu_offset, cpu_offset + cpus - 1); } printk(KERN_INFO"consumption between BEGIN an END of same, or for CODE (exclude interrupts)\n" "latency for normal between BEGIN's of same (not for CODE)\n" "latency for trigger also between BEGIN and END of same instead of consumption\n" #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) "total_access,total_activate like consumption\n" #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ ); printk(KERN_INFO"cpu%ctimstamp%s%cdiff%s%c" #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) "total_access%ctotal_activate%c" #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ "consumption%s%clatency%s%ctype%csymbol%ccurrent%cid\n", csv, punit, csv, punit, csv, #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) csv, csv, #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ punit, csv, punit, csv, csv, csv, csv); } #define FORMAT_STRING_CSV_PART1 "%x;%llu;%u;" /*--- cpu, time, diff ---*/ #define FORMAT_STRING_CSV_PART2 "%u;%u;" /*--- total_access, total_activate ---*/ #define FORMAT_STRING_CSV_PART3_A "%u;%s;" /*--- consumption, ---*/ #define FORMAT_STRING_CSV_PART3_B "%s;%u;" /*--- ,latency ---*/ #define FORMAT_STRING_CSV_PART3_C "%s;;" #define FORMAT_STRING_CSV_PART4 "%s;%s;%s;%u" /*--- type, addr-symbol, current, id ---*/ #define FORMAT_STRING_HR_PART1 "%x %12llu %10u " /*--- cpu, time, diff ---*/ #define FORMAT_STRING_HR_PART2 "%10u %10u " /*--- total_access, total_activate ---*/ #define FORMAT_STRING_HR_PART3_A "%12u %-12s " /*--- consumption, ---*/ #define FORMAT_STRING_HR_PART3_B "%-12s %12u " /*--- ,latency ---*/ #define FORMAT_STRING_HR_PART3_C "%-12s %-12s " #define FORMAT_STRING_HR_PART4 "%s %s %s %u" /*--- type, addr-symbol, current, id ---*/ /*--------------------------------------------------------------------------------*\ * mode or'red: * mode: 0x2 usec instead cycles * mode: 0x4 human readable instead csv * mode << 8: ENTRY-Macros \*--------------------------------------------------------------------------------*/ static void print_timeline(unsigned int cpu_id, __u64 act_time, __u32 diff_time, __u32 _consumption, __u32 _latency, #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) __u32 total_access, __u32 total_activate, #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ enum _avm_profile_data_type type, unsigned long addr, pid_t pid, unsigned int id, unsigned int mode) { char txtbuf[512], txtpart[2][KSYM_NAME_LEN], *ptxt; unsigned int written; __u32 consumption, latency; int hr = (mode & 0x4) ? 1 : 0; int len = sizeof(txtbuf); ptxt = txtbuf; if(mode & 0x2) { act_time = _CLK_TO_USEC(act_time); diff_time = CLK_TO_USEC(diff_time); consumption = CLK_TO_USEC(_consumption); latency = CLK_TO_USEC(_latency); } else { consumption = _consumption; latency = _latency; } written = snprintf(ptxt, len, hr ? FORMAT_STRING_HR_PART1 : FORMAT_STRING_CSV_PART1, cpu_id, act_time, diff_time); ptxt += written, len -= written; mode >>= 0x8; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) written = snprintf(ptxt, len, hr ? FORMAT_STRING_HR_PART2 : FORMAT_STRING_CSV_PART2, total_access, total_activate); ptxt += written, len -= written; #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ if((mode & CODE_ENTRY) == CODE_ENTRY) { if(_consumption) { written = snprintf(ptxt, len, hr ? FORMAT_STRING_HR_PART3_A : FORMAT_STRING_CSV_PART3_A, consumption, ""); ptxt += written, len -= written; } else if(_latency) { written = snprintf(ptxt, len, hr ? FORMAT_STRING_HR_PART3_B : FORMAT_STRING_CSV_PART3_B, "", latency); ptxt += written, len -= written; } } else if((mode & BEGIN_ENTRY) || (mode & TRIGGER_ENTRY)) { written = snprintf(ptxt, len, hr ? FORMAT_STRING_HR_PART3_B : FORMAT_STRING_CSV_PART3_B, "", latency); ptxt += written, len -= written; } else if(mode & END_ENTRY) { written = snprintf(ptxt, len, hr ? FORMAT_STRING_HR_PART3_A : FORMAT_STRING_CSV_PART3_A, consumption, ""); ptxt += written, len -= written; } else { written = snprintf(ptxt, len, hr ? FORMAT_STRING_HR_PART3_C : FORMAT_STRING_CSV_PART3_C, "",""); ptxt += written, len -= written; } written = snprintf(ptxt, len, hr ? FORMAT_STRING_HR_PART4 : FORMAT_STRING_CSV_PART4, avm_profile_data_short_names[type], profile_find_symbol((void *)addr, (pid_t)-1, txtpart[0]), profile_find_symbol(0, pid, txtpart[1]), id); printk(KERN_INFO"%s\n", txtbuf); } #endif/*--- #if !defined(__KERNEL__) ---*/ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _last_per_cpu { struct _profile_list_entry *ple_code; unsigned int irq_flag; unsigned int curr_pid; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) /*--- __u32 total_access_code, total_activate_code; ---*//*--- remark for print_timeline - remark only for code ---*/ __u32 total_access, total_activate; /*--- remark for print_timeline - any remark ---*/ #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ }; /*--------------------------------------------------------------------------------*\ * geht alle Eintrage durch vermerkt die Haeufigkeit der entsprechenden Symbole * weight: Gewichtung: calls / (total-Length ^ weight) \*--------------------------------------------------------------------------------*/ static __u32 fill_profilecall_lists(struct _profile_codeaddrcallheader **call_list, unsigned int cpu_offset, unsigned int cpus, __u64 *sum_codelength, unsigned int weight) { struct _avm_profile_data *pdata; struct _profile_codeaddrcall_entry *pce; struct _profile_list_data_type_mapper *dtm; unsigned int len, i, total_calls = 0, line = 0, cpu_id; unsigned int sum = 0; *call_list = NULL; #if defined(__KERNEL__) avm_simple_profiling_enable(0, 0, &len, NULL, 1); #else len = avm_profile_data_entries; #endif/*--- #else ---*//*--- #if defined(__KERNEL__) ---*/ for(i = 0 ; i < len ; i++) { line++; pdata = avm_simple_profiling_by_idx(i); if(pdata == NULL) { break; } if((__u32)pdata->type >= avm_profile_data_type_unknown) { continue; } cpu_id = pdata->cpu_id; if((cpu_id < cpu_offset) || (cpu_id >= (cpus + cpu_offset))) { continue; } dtm = &data_type_mapper[pdata->type]; if(dtm->profileheader == NULL) { printk(KERN_INFO"no analyzing for %s\n", data_type_mapper[pdata->type].name); continue; } if(pdata->type == avm_profile_data_type_code_address_info) { if(pdata->id == AVM_PROFILE_IDLE_ID) { /*--- do not collect idle ---*/ continue; } if(pdata->addr == 0) { /*--- do not collect schedule ---*/ continue; } } pce = find_codeaddrcall_entry(call_list, pdata, &sum); if(pce) { total_calls++; pce->total_calls++; if(weight == 2) { pce->codelength *= pce->codelength; } else if(weight == 3) { pce->codelength = pce->codelength * pce->codelength * pce->codelength; } } } if(sum_codelength) { *sum_codelength = sum; if(weight == 2) { *sum_codelength *= *sum_codelength; } else if(weight == 3) { *sum_codelength = *sum_codelength * *sum_codelength * *sum_codelength; } } return total_calls; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static __u32 sum_weight_lists(struct _profile_codeaddrcallheader *call_list, unsigned fbit) { struct _profile_codeaddrcallheader *lh_act = call_list; unsigned int i, sum = 0; while(lh_act) { for(i = 0; i < lh_act->head.used[0]; i++) { struct _profile_codeaddrcall_entry *pce = &lh_act->list[i]; if(pce->codelength) { sum += (pce->total_calls << fbit) / pce->codelength; } } lh_act = (struct _profile_codeaddrcallheader *)lh_act->head.next; } return sum; } /*--------------------------------------------------------------------------------*\ * geht alle Eintrage durch und füllt die einzelnen Typen profileheader im data_type_mapper ret: konsumierte Gesamtzeit timeline orred: 0x1 on 0x2 usec instead cycles 0x4 human readable instead csv \*--------------------------------------------------------------------------------*/ static __u64 fill_profilestat_lists(unsigned int *entries, unsigned int cpu_offset, unsigned int cpus, unsigned int timeline __attribute__((unused))) { #if defined(DEBUG_PROFILE_STAT) char txtbuf[2][KSYM_NAME_LEN]; #endif/*--- #if defined(DEBUG_PROFILE_STAT) ---*/ struct _last_per_cpu last[cpu_offset + cpus]; unsigned int cpu, activ_cpus = cpus; unsigned int activ_cpu_mask = (1 << (cpu_offset +cpus)) -1; __u64 last_act_time = 1; struct _avm_profile_data *pdata; struct _profile_list_data_type_mapper *dtm; struct _profile_list_entry *ple; unsigned int len, i, _entries = 0; unsigned int line = 0, cpu_id; __u32 last_time = 0; __u32 diff_time = 0; __u64 act_time = 1; #if defined(__KERNEL__) avm_simple_profiling_enable(0, 0, &len, NULL, 1); #else len = avm_profile_data_entries; #endif/*--- #else ---*//*--- #if defined(__KERNEL__) ---*/ for(i = 0; i < cpu_offset + cpus; i++) { last[i].irq_flag = 0; last[i].curr_pid = 0; last[i].ple_code = NULL; } #if !defined(__KERNEL__) if(timeline) { print_timelineheader(timeline, cpu_offset, cpus); } #endif/*--- #if !defined(__KERNEL__) ---*/ for(i = 0 ; i < len ; i++) { line++; pdata = avm_simple_profiling_by_idx(i); if(pdata == NULL) { break; } if((__u32)pdata->type >= avm_profile_data_type_unknown) { continue; } cpu_id = pdata->cpu_id; if((cpu_id < cpu_offset) || (cpu_id >= (cpus + cpu_offset))) { continue; } dtm = &data_type_mapper[pdata->type]; if(dtm->profileheader == NULL) { printk(KERN_INFO"no analyzing for %s\n", data_type_mapper[pdata->type].name); continue; } _entries++; if(likely(last_time)) { diff_time = ((pdata->time | 0x1) - last_time); act_time += (__u64)diff_time; } last_time = pdata->time | 0x1; ple = find_profile_entry(dtm->profileheader, pdata, dtm->operation); if(ple == NULL) { /*--- printk(KERN_INFO"line %u: no entry alloced %s(%u)\n", line, avm_profile_data_short_names[pdata->type], pdata->type); ---*/ continue; } #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) DBG_TRC("line=%u:activ_cpus=%u cpu=%u irq_bh=%u curr_id=%u typ=%s act_time=%llu delta=%x: act_total=%u/%u last_total %u/%u %p (%s) id=%d: last_btime=%llx consumption %llx max_consumption %x total=%u/%u last=%u/%u\n", line, activ_cpus, cpu_id, last[cpu_id].irq_flag, last[cpu_id].curr_pid, avm_profile_data_short_names[pdata->type],act_time, diff_time, pdata->total_access, pdata->total_activate, last[cpu_id].total_access, last[cpu_id].total_access, ple->addr, profile_find_symbol(ple->addr, -1, txtbuf[0]), ple->id, ple->last_btime, ple->sum_consumption, ple->max_consumption, ple->total_activate, ple->total_access, ple->last_total_access, ple->last_total_activate ); #else DBG_TRC("line=%u:activ_cpus=%u cpu=%u irq_bh=%u curr_id=%u typ=%s act_time=%llu delta=%x: %p (%s) id=%d: last_btime=%llx consumption %llx max_consumption %x\n", line, activ_cpus, cpu_id, last[cpu_id].irq_flag, last[cpu_id].curr_pid, avm_profile_data_short_names[pdata->type],act_time, diff_time, ple->addr, profile_find_symbol(ple->addr, -1, txtbuf[0]), ple->id, ple->last_btime, ple->sum_consumption, ple->max_consumption, ); #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ if(!(dtm->operation & TRIGGER_ENTRY) && !(dtm->operation & USER_ENTRY)) { /*--- this entries do a change of code-status ---*/ for(cpu = cpu_offset; cpu < cpu_offset + cpus; cpu++) { struct _profile_list_entry *ple_code= last[cpu].ple_code; if((last[cpu].irq_flag == 0) && ple_code) { /*--- we are not in irq/bh-context and have an code-entry : ---*/ __u32 consumption = (__u32)(act_time - last_act_time); DBG_TRC("\t[cpu=%x]last was CODE: %p: calls=%u addr=%p (%s) pid=%d: act_time=%llx last_btime=%llx consumption %x/%u max_consumption %x\n", cpu, ple_code, ple_code->total_calls, ple_code->addr, profile_find_symbol(0, ple_code->id, txtbuf[0]), ple_code->id, act_time, ple_code->last_btime, consumption, activ_cpus, ple_code->max_consumption); if(activ_cpus > 1) { consumption /= activ_cpus; #if defined(DEBUG_PROFILE_STAT) } else { if(activ_cpus == 0) DBG_TRC("error activ_cpus == 0\n"); #endif/*--- #if defined(DEBUG_PROFILE_STAT) ---*/ } ple_code->part_consumption += consumption; DBG_ENTRIES("\t\tcpu_id=%d->activ_cpus=%u %s: part_consumption=%llu (sum_consumption=%llu)\n",cpu_id, activ_cpus, profile_find_symbol(0, ple_code->id, txtbuf[0]), ple_code->part_consumption, ple_code->sum_consumption); if(cpu_id == cpu) { #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) DBG_TRC("\t\t->total update(code %s): access: %u/%u -> %u/%u\n", profile_find_symbol(0, ple_code->id, txtbuf[0]), ple_code->total_access, ple_code->total_activate, ple_code->total_access + (pdata->total_access - last[cpu_id].total_access), ple_code->total_activate + (pdata->total_activate - last[cpu_id].total_activate) ); ple_code->total_access += pdata->total_access - last[cpu_id].total_access; ple_code->total_activate += pdata->total_activate - last[cpu_id].total_activate; #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ } } } #if !defined(__KERNEL__) if(timeline) { struct _profile_list_entry *ple_code= last[cpu_id].ple_code; if((last[cpu_id].irq_flag == 0) && ple_code) { /*--- print out the last collected!!! code-entry ---*/ #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) & 0 DBG_TRC("\t\t->%s: timeline: %u/%u last %u/%u\n",profile_find_symbol(0, ple_code->id, txtbuf[0]), ple_code->total_access, last[cpu_id].ple_code->total_activate, ple_code->last_total_access, last[cpu_id].ple_code->last_total_activate); #else DBG_TRC("\t\tcpu_id=%d->%s: part_consumption=%llu\n",cpu_id, profile_find_symbol(0, ple_code->id, txtbuf[0]), ple_code->part_consumption); #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ print_timeline(cpu_id, act_time, diff_time, ple_code->part_consumption, 0, #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) pdata->total_access - last[cpu_id].total_access, pdata->total_activate - last[cpu_id].total_activate, #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ avm_profile_data_type_code_address_info, (unsigned long)ple_code->addr, ple_code->id, ple_code->id, (timeline & 0xFF) | (CODE_ENTRY << 8)); diff_time = 0; } } #endif/*--- #if !defined(__KERNEL__) ---*/ } /*--------------------------------------------------------------------------------*\ * Handling for CODE \*--------------------------------------------------------------------------------*/ if((dtm->operation & CODE_ENTRY) == CODE_ENTRY) { /*--- update only if not the same entry: ---*/ if(ple != last[cpu_id].ple_code) { if(last[cpu_id].irq_flag != 0) { /*--- error on csv or nested irq-call: ---*/ DBG_ERR("Warning:line %u Code-Entry with setting irq_flag=%d (missing End of IRQ - perhaps nested irq)- reset irq_flag\n", line, last[cpu_id].irq_flag); /*--- last[cpu_id].irq_flag = 0; ---*/ } if(last[cpu_id].ple_code) { struct _profile_list_entry *ple_code= last[cpu_id].ple_code; /*--- close-up the old code entry ---*/ ple_code->sum_consumption += ple_code->part_consumption; DBG_ENTRIES("%u: CODE_ENTRY activ_cpus=%x bh_flag=%x %s: part_consumption %llu sum_consumption %llu\n", line, activ_cpus , last[cpu_id].irq_flag, profile_find_symbol(0, ple_code->id, txtbuf[0]), ple_code->part_consumption, ple_code->sum_consumption); if(ple_code->part_consumption > ple_code->max_consumption) { ple_code->max_consumption = ple_code->part_consumption; } ple_code->part_consumption = 0; } /*--- prepare the new entry ---*/ ple->total_calls++; if(ple->last_btime) { __u32 latency = (__u32)min(act_time - ple->last_btime, (0x1ULL << 32) - 1); if(latency > ple->max_latency) { /*--- DBG_TRC("%s: %d: latency %u -> max_latency %u act_time %llu last_btime %llu\n", __func__, i, latency, ple->max_latency, act_time, ple->last_btime); ---*/ ple->max_latency = latency; } /*--- DBG_TRC("%s: latency %u min_latency %u\n", __func__, latency, ple->min_latency); ---*/ if((latency < ple->min_latency) || (ple->min_latency == 0)) { ple->min_latency = latency; } ple->sum_latency += latency; #if !defined(__KERNEL__) && 0 if(timeline) { /*--- print out start of code-entry ---*/ print_timeline(cpu_id, act_time, diff_time, 0, latency, #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) 0, 0, #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ avm_profile_data_type_code_address_info, (unsigned int)ple->addr, ple->id, ple->id, (timeline & 0xFF) | (CODE_ENTRY << 8)); } #endif /*--- #if !defined(__KERNEL__) ---*/ } ple->last_btime = act_time; last[cpu_id].ple_code = ple; last[cpu_id].curr_pid = pdata->id; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ple->last_total_access = ple->total_access; ple->last_total_activate = ple->total_activate; #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ /*--- get active cpus (special idle-code-entry) ---*/ if(profile_activ_cpu_mask(pdata, &activ_cpu_mask)) { activ_cpus = profile_count_cpus(activ_cpu_mask); } DBG_TRC("\tMark as %s %p %s pid=%u last_btime=%llu\n", pdata->id == AVM_PROFILE_IDLE_ID ? "idle" : "code", ple->addr, profile_find_symbol(0, pdata->id, txtbuf[0]), pdata->id, ple->last_btime); } else { DBG_TRC("\tsame %s %p %s pid=%u last_btime=%llu\n", pdata->id == AVM_PROFILE_IDLE_ID ? "idle" : "code", ple->addr, profile_find_symbol(0, pdata->id, txtbuf[0]), pdata->id, ple->last_btime); } if(pdata->id == AVM_PROFILE_IDLE_ID) { if(last[cpu_id].irq_flag) { DBG_ERR("Warning:line %u IDLE-Entry with setting irq_flag (%d) -> reset\n", line, last[cpu_id].irq_flag = 0); last[cpu_id].irq_flag = 0; } } /*--------------------------------------------------------------------------------*\ * Handling for BEGIN_ENTRY \*--------------------------------------------------------------------------------*/ } else if(dtm->operation & BEGIN_ENTRY) { ple->total_calls++; if(ple->recursion == 0) { __u32 latency; if(ple->last_btime) { latency = (__u32)min(act_time - ple->last_btime, (0x1ULL << 32) - 1); if(!(dtm->operation & TRIGGER_ENTRY)) { if(latency > ple->max_latency) { /*--- printk("%s: %d: latency %u -> max_latency %x act_time %llx last_btime %llx\n", __func__, i, latency, ple->max_latency, act_time, ple->last_btime); ---*/ ple->max_latency = latency; } /*--- DBG_TRC("%s: latency %u min_latency %x\n", __func__, latency, ple->min_latency); ---*/ if((latency < ple->min_latency) || (ple->min_latency == 0)) { ple->min_latency = latency; } ple->sum_latency += latency; } } else { latency = 0; } #if !defined(__KERNEL__) if(timeline) { /*--- latency also between begin-triggers ---*/ print_timeline(cpu_id, act_time, diff_time, 0, latency, #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) 0,0, #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ pdata->type, pdata->addr, last[cpu_id].curr_pid, pdata->id, (timeline & 0xFF) | (dtm->operation << 8)); } #endif/*--- #if !defined(__KERNEL__) ---*/ ple->last_btime = act_time; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ple->last_total_access = pdata->total_access; ple->last_total_activate = pdata->total_activate; #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ DBG_TRC("\tMark as irq/trigger begin %p %s last_btime=%llx\n", ple->addr, profile_find_symbol(ple->addr, -1, txtbuf[0]), ple->last_btime); } if((dtm->operation & IRQ_ENTRY)) { /*--- code interrupted ---*/ if(pdata->id != AVM_PROFILE_PAGE_FAULT_ID) { last[cpu_id].irq_flag++; } ple->recursion++; } /*--------------------------------------------------------------------------------*\ * Handling for END_ENTRY \*--------------------------------------------------------------------------------*/ } else if(dtm->operation & END_ENTRY) { if((dtm->operation & IRQ_ENTRY)) { if(pdata->id != AVM_PROFILE_PAGE_FAULT_ID) { if(last[cpu_id].irq_flag) { last[cpu_id].irq_flag--; } else { DBG_ERR("Warning:line %u %s: End-Entry without setting irq_flag\n", line, avm_profile_data_short_names[pdata->type]); } } } if(ple->recursion) { ple->recursion--; } if(ple->last_btime && (ple->recursion == 0)) { if((dtm->operation & TRIGGER_ENTRY)) { /*--- der Abschnitt zwischen Begin und End interessiert als Latenz: ---*/ __u32 latency = (__u32)(act_time - ple->last_btime); if(latency > ple->max_latency) { /*--- printk("%s: %d: latency %x -> max_latency %x act_time %llu last_btime %llx\n", __func__, i, latency, ple->max_latency, act_time, ple->last_btime); ---*/ ple->max_latency = latency; } /*--- DBG_TRC("%s: latency %x min_latency %x\n", __func__, latency, ple->min_latency); ---*/ if((latency < ple->min_latency) || (ple->min_latency == 0)) { ple->min_latency = latency; } ple->sum_latency += latency; #if !defined(__KERNEL__) if(timeline) { print_timeline(cpu_id, act_time, diff_time, 0, latency, #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) 0, 0, #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ pdata->type, pdata->addr, last[cpu_id].curr_pid, pdata->id, (timeline & 0xFF) | (dtm->operation << 8)); } #endif/*--- #if !defined(__KERNEL__) ---*/ DBG_TRC("\t trigger end %p %s latency=%x\n", ple->addr, profile_find_symbol(ple->addr, (pid_t)-1, txtbuf[0]), latency); } else { __u32 consumption = (__u32)(act_time - ple->last_btime); DBG_TRC("\t irq end %p %s consumption=%x/%d\n", ple->addr, profile_find_symbol(ple->addr, (pid_t)-1, txtbuf[0]), consumption, activ_cpus); if(activ_cpus > 1) { consumption /= activ_cpus; #if defined(DEBUG_PROFILE_STAT) } else { if(activ_cpus == 0) DBG_TRC("error activ_cpus == 0\n"); #endif/*--- #if defined(DEBUG_PROFILE_STAT) ---*/ } if(consumption > ple->max_consumption) { /*--- DBG_TRC(" %s: %x %d: consumption %x max_consumption %x\n", __func__, ple->addr, i, consumption, ple->max_consumption); ---*/ ple->max_consumption = consumption; } ple->sum_consumption += consumption; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ple->total_access += pdata->total_access - ple->last_total_access; ple->total_activate += pdata->total_activate - ple->last_total_activate; /*--- DBG_TRC("\t\t->total update(END_ENTRY): %u %u\n",ple->total_access, ple->total_activate); ---*/ #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ #if !defined(__KERNEL__) if(timeline) { print_timeline(cpu_id, act_time, diff_time, consumption, 0, #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) pdata->total_access - ple->last_total_access, pdata->total_activate - ple->last_total_activate, #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ pdata->type, pdata->addr, last[cpu_id].curr_pid, pdata->id, (timeline & 0xFF) | (dtm->operation << 8)); } #endif/*--- #if !defined(__KERNEL__) ---*/ } } } if(!(dtm->operation & TRIGGER_ENTRY) && !(dtm->operation & USER_ENTRY)) { /*--- Remarks fuer die Ermittlung der Codeabschnitte---*/ last_act_time = act_time; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) last[cpu_id].total_access = pdata->total_access; last[cpu_id].total_activate = pdata->total_activate; #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ } } /*--- close-up all code entries ---*/ for(cpu = cpu_offset; cpu < cpu_offset + cpus; cpu++) { if(last[cpu].ple_code) { struct _profile_list_entry *ple_code= last[cpu].ple_code; last[cpu].ple_code->sum_consumption += ple_code->part_consumption; if(ple_code->part_consumption > ple_code->max_consumption) { ple_code->max_consumption = ple_code->part_consumption; } } } if(entries) *entries = _entries; return act_time; } #define rprint(buf, buf_len, args...) if(buf) {if(buf_len > 0) {unsigned int slen = snprintf(buf, buf_len, args); buf += slen; buf_len -= slen; } } else { printk(KERN_INFO args) ; } #define MAX_VAL (((0x1ULL << 32) / 100)) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static char *percent(__u64 val, __u64 norm, char *buf) { __u32 rem, div; while((val > MAX_VAL) || (norm > MAX_VAL)) { norm >>= 1; val >>= 1; } div = (__u32)val * 100U; rem = div % (__u32)norm; div = div / (__u32)norm; rem *= 100U; rem /= (__u32)norm; rem %= 100; sprintf(buf, "%3u.%02u %%", div, rem); return buf; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static char *human_time(__u64 cycles, char *buf) { __u32 usec = (__u32)_CLK_TO_USEC(cycles); if(usec >= (1000 * 1000)) { __u32 frac = usec / 100; sprintf(buf, "%3u.%02u s", usec / (1000 * 1000), frac % 100); } else if(usec >= 1000) { sprintf(buf, "%3u.%02u ms", usec / 1000, usec % 100); } else { sprintf(buf, " %3u us", usec); } return buf; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline __u64 scale_consumption(__u64 val, unsigned int active_cpus __attribute__((unused))) { if(active_cpus) { do_div(val, active_cpus); } return val; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void free_profilestat_lists(void) { unsigned int i; for(i = 0; i < avm_profile_data_type_unknown; i++) { struct _profile_list_data_type_mapper *pdtm = &data_type_mapper[i]; if((pdtm->profileheader == NULL)) { continue; } free_profile_list((struct _profile_header **)pdtm->profileheader); } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ char *print_addr_with_mask(unsigned int addr, unsigned int addrlevel, char *buf) { char *end = &buf[8+2]; sprintf(buf, "0x%08x", addr); while(addrlevel-- && (end != &buf[1])) { *(--end) = 'X'; } return buf; } /*--------------------------------------------------------------------------------*\ * export-function: list total call of function * cpus: count of vpes * weight: total call weighted with codelength ^ weight \*--------------------------------------------------------------------------------*/ int profilestat_totalcall(char *txtbuf, int txtbuf_len, unsigned int cpu_offset, unsigned int cpus, unsigned int weight) { char txt[3][KSYM_NAME_LEN], *sym; unsigned int weight_divider; struct _profile_codeaddrcallheader *totalcall_list; struct _profile_codeaddrcall_entry *pce = NULL; char *buf_start = txtbuf; __u32 total_calls, fbit = 0; __u64 sum_codelength; if(weight > 3) { weight = 3; } total_calls = fill_profilecall_lists(&totalcall_list, cpu_offset, cpus, &sum_codelength, weight); if(total_calls == 0) { return 0; } switch(weight) { case 0: rprint(txtbuf, txtbuf_len, "display top of function calls\n"); break; case 1: rprint(txtbuf, txtbuf_len, "display top of function calls weighted with codelength\n"); break; case 2: rprint(txtbuf, txtbuf_len, "display top of function calls weighted with square-codelength\n"); break; } if(cpus == 1) { rprint(txtbuf, txtbuf_len, "Cpu: %x ", cpu_offset); } else { rprint(txtbuf, txtbuf_len, "Cpu's: %x-%x ", cpu_offset, cpu_offset + cpus - 1); } rprint(txtbuf, txtbuf_len, "sum of calls: %u (user-function without codelength-info approximated to 10 %% of PAGE-SIZE)\n", total_calls); rprint(txtbuf, txtbuf_len, "%-64s (%-8s/%5s) %-16s %s %8s %6s\n", "symbol", "addr", "id", "pid", "percent", "calls", weight <= 1 ? "codelen" : "weight"); if(weight) { fbit= 32 - fls(total_calls); weight_divider = sum_weight_lists(totalcall_list, fbit); if(weight_divider == 0) { weight_divider= 1; /*--- fprintf(stderr, "error weight_divider=%d %lld\n", total_calls, sum_codelength); ---*/ } } else { weight_divider = total_calls; } for(;;) { __u64 weight_val; pce = find_maxcodeaddrcallentry(totalcall_list, fbit, weight, 1); if(pce == NULL) { break; } switch(weight) { default: case 0: weight_val = pce->total_calls; break; case 1 ... 3: weight_val = (pce->total_calls << fbit) / pce->codelength; break; } #if defined(__KERNEL__) sym = txt[0]; if(get_user_info(txt[0], sizeof(txt[0]), pce->id, (unsigned long)pce->addr)) #endif/*--- #if defined(__KERNEL__) ---*/ sym = profile_find_symbol(pce->addr, (pid_t)-1, txt[0]); while(*sym && (*sym != '+')) sym++; if(*sym == '+') *sym = 0; rprint(txtbuf, txtbuf_len, "%-64s (%08lx/%5d) %-16s %s %8u %6u\n", txt[0], (unsigned long)pce->addr, pce->id, profile_find_symbol(0, pce->id, txt[1]), percent(weight_val, weight_divider, txt[2]), pce->total_calls, pce->codelength ); } free_profile_list((struct _profile_header **)&totalcall_list); return txtbuf - buf_start; } /*--------------------------------------------------------------------------------*\ * export-function: list complete statistic * cpus: count of vpes * code works only correct with vpe's (consumption-calculation) * for real cpus call with cpu_offset and cpu-idx * * full: also access-statistic \*--------------------------------------------------------------------------------*/ int profilestat_category(char *txtbuf, int txtbuf_len, unsigned int cpu_offset, unsigned int cpus, unsigned int full) { char *buf_start = txtbuf; __u64 time; __u64 idle_sum = 0; unsigned int i, cpu, entries; char txt[2][32]; if(gCycle_per_usec == 0) { return 0; } time = fill_profilestat_lists(&entries, cpu_offset, cpus, 0); rprint(txtbuf, txtbuf_len, "Measure time: %s (cycles=%llu) with %u entries\n", human_time(time, txt[0]), time, entries); if(time == 0) { return 0; } for(cpu = cpu_offset; cpu < (cpus + cpu_offset); cpu++) { for(i = 0; i < avm_profile_data_type_unknown; i++) { struct _profile_list_data_type_mapper *pdtm = &data_type_mapper[i]; __u64 sum_consumption; if((pdtm->profileheader == NULL) || (pdtm->name == NULL)) { continue; } #if defined(DEBUG_PROFILE_STAT) print_valid_entries(pdtm->name, *pdtm->profileheader, cpu, ((pdtm->operation & CODE_ENTRY) == CODE_ENTRY) ? 1 : 0); #endif/*--- #if defined(DEBUG_PROFILE_STAT) ---*/ if(i == avm_profile_data_type_code_address_info) { /*--- delete idle ---*/ struct _profile_list_entry *pidle = profile_idle(*pdtm->profileheader, cpu); if(pidle) { idle_sum += scale_consumption(pidle->sum_consumption, cpus); pidle->last_btime = 0; } } sum_consumption = profile_consumption(*pdtm->profileheader, cpu); if(sum_consumption == 0) { continue; } rprint(txtbuf, txtbuf_len, "\n[CPU %x] ---------- %s needs %s (%s) of total time ----------\n", cpu, pdtm->name, percent(sum_consumption, time, txt[0]), human_time(sum_consumption, txt[1])); for(;;){ char buf[5][64]; char tmp[3][KSYM_NAME_LEN]; struct _profile_list_entry *pmax = profile_entry_with_max_sum_consumption(*pdtm->profileheader, cpu, 1); if(pmax == NULL) { break; } if(i == avm_profile_data_type_hw_irq_begin) { if(pmax->id == AVM_PROFILE_PAGE_FAULT_ID) { strcpy(buf[0], "PAGE-FAULT:"); } else { snprintf(buf[0], sizeof(buf[0]), "IRQ: %5u: %-30s", pmax->id, profile_find_symbol(pmax->addr, (pid_t)-1, tmp[0])); } } else if(i == avm_profile_data_type_code_address_info) { snprintf(buf[0], sizeof(buf[0]), "%-30s", profile_find_symbol(0, pmax->id, tmp[0])); } else { snprintf(buf[0], sizeof(buf[0]), "%-40s(%5u)", profile_find_symbol(pmax->addr, (pid_t)-1, tmp[0]), pmax->id); } percent(pmax->sum_consumption, sum_consumption, buf[1]); snprintf(buf[2], sizeof(buf[2]), "consume:max %s avg %s", human_time(pmax->max_consumption, tmp[0]), human_time((__u32)(mylongdiv(pmax->sum_consumption, pmax->total_calls)), tmp[1]) ); snprintf(buf[3], sizeof(buf[3]), "latency:max %s min %s avg %s", human_time(pmax->max_latency, tmp[0]), human_time(pmax->min_latency, tmp[1]), human_time((__u32)(mylongdiv(pmax->sum_latency, pmax->total_calls > 1 ? (pmax->total_calls -1) : 1)), tmp[2]) ); buf[4][0] = 0; if(full) { #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) snprintf(buf[4], sizeof(buf[4]), "access:%12u activate:%12u", pmax->total_access, pmax->total_activate); #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ } rprint(txtbuf, txtbuf_len, "%s%s Calls %6u %s %s %s\n", buf[0], buf[1], pmax->total_calls, buf[2], buf[3], buf[4] ); } } } rprint(txtbuf, txtbuf_len, "\nCPU's idle %s (%s) of total time\n", human_time(idle_sum, txt[0]), percent(idle_sum, time, txt[1])); free_profilestat_lists(); return txtbuf - buf_start; } #endif/*--- #if defined(CONFIG_KALLSYMS) && defined(CONFIG_PROC_FS) ---*/ #if !defined(__KERNEL__) /*--------------------------------------------------------------------------------*\ * SOF: Offline-testtool Offline-testtool Offline-testtool Offline-testtool Offline-testtool \*--------------------------------------------------------------------------------*/ static struct _data_type_helper { const char *match; } data_type_helper[avm_profile_data_type_unknown] = { [avm_profile_data_type_code_address_info] {match: "CODE;"}, [avm_profile_data_type_trace_skb] {match: "SKB;" }, [avm_profile_data_type_hw_irq_begin] {match: "BIRQ;"}, [avm_profile_data_type_hw_irq_end] {match: "EIRQ;"}, [avm_profile_data_type_sw_irq_begin] {match: "BSWI;"}, [avm_profile_data_type_sw_irq_end] {match: "ESWI;"}, [avm_profile_data_type_timer_begin] {match: "BTIM;"}, [avm_profile_data_type_timer_end] {match: "ETIM;"}, [avm_profile_data_type_tasklet_begin] {match: "BLET;"}, [avm_profile_data_type_tasklet_end] {match: "ELET;"}, [avm_profile_data_type_hi_tasklet_begin] {match: "BLHT;"}, [avm_profile_data_type_hi_tasklet_end] {match: "ELHT;"}, [avm_profile_data_type_workitem_begin] {match: "BWRK;"}, [avm_profile_data_type_workitem_end] {match: "EWRK;"}, [avm_profile_data_type_func_begin] {match: "BFUN;"}, [avm_profile_data_type_func_end] {match: "EFUN;"}, [avm_profile_data_type_trigger_tasklet_begin] {match: "BTLT;"}, [avm_profile_data_type_trigger_tasklet_end] {match: "ETLT;"}, [avm_profile_data_type_trigger_user_begin] {match: "BUST;"}, [avm_profile_data_type_trigger_user_end] {match: "EUST;"}, }; #define SKIP_CHAR(p, val) while(*p && *p != ';') p++; if(*p == ';') p++; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static char *profile_find_symbol(void *addr, pid_t pid, char *buf) { static char tmpbuf[KSYM_NAME_LEN]; struct _symbols_name *psym; if(buf == NULL) { buf = tmpbuf; } if(pid != (pid_t)-1) { struct _current_name *pcurr = current_name; while(pcurr->valid) { if(pid == pcurr->curr_pid) { strcpy(buf, pcurr->current); return buf; } pcurr++; } buf[0] = 0; return buf; } psym = symbolname; while(psym->valid) { unsigned int _addr = (unsigned long)(addr) & 0xFFFFFFFF; if(_addr == psym->addr) { strcpy(buf, psym->name ? psym->name : ""); return buf; } psym++; } sprintf(buf, "0x%p", addr); return buf; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _current_name *profile_add_curr(struct _current_name *pcurr, char *curr) { static unsigned int current_pid; while(pcurr && (pcurr->valid == 1)) { if(pcurr->current && strcmp(curr, pcurr->current) == 0) { return pcurr; } pcurr++; } /*--- printf("%s '%s' '%s' new entry %d\n", __func__, pcurr->current, curr, current_pid + 1); ---*/ strcpy(pcurr->current, curr); pcurr->curr_pid = ++current_pid; /*--- dummy-pid ---*/ pcurr->valid = 1; return pcurr; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int fill_type_from_csv(char *string, struct _avm_profile_data *pdata, struct _symbols_name *psym, struct _current_name *pcurr) { struct _current_name *pdup; unsigned int i; unsigned long offset; unsigned int cpu_id, time, total_access, total_activate, data_id; unsigned int data_addr; char *p = string; sscanf(p, "%x", &cpu_id); SKIP_CHAR(p,";"); sscanf(p, "0x%x", &time); SKIP_CHAR(p,";"); sscanf(p, "0x%x", &total_access); SKIP_CHAR(p,";"); sscanf(p, "0x%x", &total_activate); SKIP_CHAR(p,";"); for(i = 0; i < avm_profile_data_type_unknown; i++) { if(data_type_helper[i].match && (strstr(p, data_type_helper[i].match) == p)) { char curr[TASK_COMM_LEN]; char *ps, *pt; pdata->cpu_id = cpu_id; pdata->time = time; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) pdata->total_access = total_access; pdata->total_activate = total_activate; #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ pdata->type = i; SKIP_CHAR(p,";"); sscanf(p, "0x%x", &data_addr); SKIP_CHAR(p,";"); ps = p; SKIP_CHAR(ps,";"); snprintf(psym->name, sizeof(psym->name), "%.*s", (unsigned int)(ps-p > 0 ? ps -p - 1 : 0), p); pt = psym->name; codeaddr_extract(pt, &offset); data_addr -= offset; pdata->addr = data_addr; /*--- while(*pt && *pt != '+') pt++; if(*pt == '+')*pt = 0; ---*/ while(*pt && *pt != ';') pt++; if(*pt == ';')*pt = 0; psym->addr = data_addr; psym->valid = 1; /*--- printf("psym->addr=%x -> '%s'\n", psym->addr, psym->name); ---*/ p = ps; SKIP_CHAR(ps,";"); snprintf(curr, TASK_COMM_LEN, "%.*s", (unsigned int)(ps-p > 0 ? ps -p - 1 : 0), p); pdup =profile_add_curr(pcurr, curr); if(pdup) { if(i == avm_profile_data_type_code_address_info) { if(strcmp(curr, "IDLE") == 0) { pdata->id = AVM_PROFILE_IDLE_ID; pdup->curr_pid = AVM_PROFILE_IDLE_ID; } else { pdata->id = pdup->curr_pid; } } } if(i != avm_profile_data_type_code_address_info) { sscanf(ps, "%d", &data_id); pdata->id = (unsigned short)data_id; } return 0; } } return 1; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static unsigned int parse_profile_csv(FILE *fp, struct _avm_profile_data **ap_data, struct _symbols_name **symbols, struct _current_name **current, unsigned int *pmaxcpus) { struct _avm_profile_data *pdata; struct _symbols_name *psym; struct _current_name *pcurr; unsigned int lines = 0, actline = 0, entries = 0, messure_time = 0; __u64 cycsum = 0; __u32 last_cycle = 0; char txt[1024], *p; for(;;) { if(fgets(txt, sizeof(txt), fp) == NULL) { break; } if(messure_time == 0) { char *p; if((p = strstr(txt, "measure time "))) { p += sizeof("measure time ") -1; sscanf(p, "%u", &messure_time); } } lines++; } DBG_TRC("%d\n", lines); fseek(fp, 0, SEEK_SET); pdata = malloc((sizeof(struct _avm_profile_data)) * lines); memset(pdata, 0x55 ,sizeof(struct _avm_profile_data) * lines); psym = malloc((sizeof(struct _symbols_name)) * lines); memset(psym, 0, (sizeof(struct _symbols_name)) * lines); pcurr = malloc((sizeof(struct _current_name)) * lines); memset(pcurr, 0, (sizeof(struct _current_name)) * lines); *ap_data = pdata; *symbols = psym; *current = pcurr; pdata = data; psym = symbolname; *pmaxcpus = 0; for(;;) { p = fgets(txt, sizeof(txt), fp); if(p == NULL) { break; } actline++; /*--- DBG_TRC("%8d: '%s'", actline, p); ---*/ if(fill_type_from_csv(p, pdata, psym, pcurr) == 0) { #if 0 DBG_TRC(" parsed: %x;0x%08X;0x%08X;0x%08X;%s0x%08X;%s;%d\n\n", pdata->cpu_id, pdata->time, pdata->total_access, pdata->total_activate, data_type_helper[pdata->type].match, pdata->addr, psym->name, pdata->id); #endif if(pdata->cpu_id > NR_CPUS) { printf("warning: line %u cpu_id %x > reserved NR_CPUS -> ignore\n", actline, pdata->cpu_id); continue; } if(pdata->cpu_id > *pmaxcpus) { *pmaxcpus = pdata->cpu_id; } if(last_cycle) { cycsum += (__u32)(pdata->time - last_cycle); } last_cycle = pdata->time; pdata++, psym++, entries++; } } if(cycsum && messure_time) { gCycle_per_usec = mylongdiv(cycsum, messure_time) / 1000; printf("cycle per usec %lu\n", gCycle_per_usec); } *pmaxcpus = *pmaxcpus + 1; return entries; } char *gCsvInfile; char *gOutfile; int gHelp, gStatistic, gTimelineCycles, gTimelineuSec, gKernelstatistic, gCsv, gExcludePerf, gFreq; int gKernelstatisticVal=3, gKSWeight = 1; int gCpu = -1; int gExcludeVPE; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _cmdlinetab { char *prefix; enum { string, integer, flag } type; void *value; char *help; } cmdline[] = { { prefix: "-i", type: string, value: &gCsvInfile, help: "infile (csv)"}, { prefix: "-o", type: string, value: &gOutfile, help: "outfile"}, { prefix: "-s", type: flag, value: &gStatistic, help: "statistic"}, { prefix: "-k", type: flag, value: &gKernelstatistic, help: "kernel-statistic "}, { prefix: "--kweigth", type: integer, value: &gKSWeight, help: "weighted kernelstatistic: calls/code_length^weigth"}, { prefix: "-t", type: flag, value: &gTimelineuSec, help: "timeline in us"}, { prefix: "--timeusec", type: flag, value: &gTimelineuSec, help: ""}, { prefix: "-T", type: flag, value: &gTimelineCycles, help: "timeline in cycles"}, { prefix: "--timecycles", type: flag, value: &gTimelineCycles, help: ""}, { prefix: "--csv", type: flag, value: &gCsv, help: "timeline: csv output format"}, { prefix: "--noperf", type: flag, value: &gExcludePerf, help: "statistic: exclude performance-counter"}, { prefix: "--cpu", type: integer, value: &gCpu, help: "timeline/nonvpe: select cpu (default: all)"}, { prefix: "--novpe", type: flag, value: &gExcludeVPE, help: "multicore: no vpe-architecture"}, { prefix: "--freq", type: integer, value: &gFreq, help: "cpu-freq in MHz"}, { prefix: "-h", type: flag, value: &gHelp, help: "this helptext"}, { prefix: "--help", type: flag, value: &gHelp, help: ""}, { prefix: NULL, type: string, value: NULL}, }; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct _cmdlinetab *find_prefix(char **arg) { struct _cmdlinetab *pcmd = cmdline; while(pcmd->prefix) { if(strncmp(pcmd->prefix, *arg, strlen(pcmd->prefix))) { pcmd++; continue; } *arg += strlen(pcmd->prefix); return pcmd; } return NULL; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void print_help(char *name) { struct _cmdlinetab *pcmd = cmdline; fprintf(stderr, "%s -i infile -s ...\n", name); while(pcmd->prefix) { fprintf(stderr, "\t%-12s %7s %s\n", pcmd->prefix, (pcmd->type != flag) ? "" : "", pcmd->help); pcmd++; } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int parse_cmdline(int argc, char *argv[]){ struct _cmdlinetab *pcmd; int idx = 1; argc--; while(argc) { char *ptmp; char *p = argv[idx++]; argc--; /*--- printf("argv[%d]=%s\n", idx, argv[idx]); ---*/ ptmp = p; switch(*p) { case '-': pcmd = find_prefix(&p); if(pcmd == NULL) { fprintf(stderr, "%s: unknown option '%s'\n", argv[0], ptmp); return -1; } if(pcmd->type == flag) { if(*p) { fprintf(stderr, "%s: invalid rest on flag '%s'\n", argv[0], ptmp); return -1; } *((int *)pcmd->value) = 1; break; } if(*p == 0) { if(argc == 0) { fprintf(stderr, "%s: missing parameter '%s'\n", argv[0], ptmp); return -1; } p = argv[idx++]; argc--; } if(pcmd->type == string) { *((char **)pcmd->value) = p; } else { sscanf(p, "%d", (unsigned int *)pcmd->value); } break; default: fprintf(stderr, "%s: unknown option '%s'\n", argv[0], ptmp); return -1; } } return 0; } /*--------------------------------------------------------------------------------*\ gcc -DCONFIG_PROC_FS -DCONFIG_KALLSYMS avm_profile_stat.c -m32 -Wall -g -ggdb -DNR_CPUS=2 -DPAGE_SHIFT=12 -D__GFP_NORETRY=0 -o profile_stat lantiq: crossgcc -DCONFIG_PROC_FS -DCONFIG_KALLSYMS avm_profile_stat.c -Wall -g -ggdb -DNR_CPUS=2 -DPAGE_SHIFT=12 -D__GFP_NORETRY=0 -DCONFIG_MIPS -DCONFIG_MIPS_MT_SMTC -o profile_stat \*--------------------------------------------------------------------------------*/ int main(int argc, char *argv[0]){ FILE *fin = stdin; unsigned int startcpu = 0, maxcpus, cpu, formatflag = 0; fout = stdout; if(parse_cmdline(argc, argv)) { print_help(argv[0]); return -1; } if(gHelp) { print_help(argv[0]); return 0; } if(gTimelineCycles && gTimelineuSec) { fprintf(stderr, "%s: conflict of parameter \n", argv[0]); return -1; } if(gCsvInfile) { fin = fopen(gCsvInfile, "r"); } if(fin == NULL) { fprintf(stderr, "%s: unable to open input file %s\n", argv[0], gCsvInfile); return -errno; } if(gOutfile) { fout = fopen(gOutfile, "w"); } if(fout == NULL) { fprintf(stderr, "%s: unable to open outputfile %s\n", argv[0], gOutfile); return -errno; } if(gFreq) { gCycle_per_usec = gFreq / 2; } avm_profile_data_entries = parse_profile_csv(fin, &data, &symbolname, ¤t_name, &maxcpus); printf("entries=%u PROFILE_LIST_ENTRIES=%ld size=%lu maxcpus=%d\n", avm_profile_data_entries, PROFILE_LIST_ENTRIES, sizeof(struct _profile_list_entry), maxcpus); if(gCpu != -1) { startcpu = gCpu; maxcpus = 1; } if(gKSWeight > 3) { gKSWeight = 3; } if(gKernelstatistic) { if(gExcludeVPE) { for(cpu = startcpu; cpu < maxcpus; cpu++) { profilestat_totalcall(NULL, 0, startcpu, maxcpus, gKSWeight); } } else { profilestat_totalcall(NULL, 0, startcpu, maxcpus, gKSWeight); } } if(gStatistic) { if(gExcludeVPE) { for(cpu = startcpu; cpu < maxcpus; cpu++) { profilestat_category(NULL, 0, startcpu, maxcpus, !gExcludePerf); } } else { profilestat_category(NULL, 0, startcpu, maxcpus, !gExcludePerf); } } formatflag = gTimelineCycles ? 0x1 : gTimelineuSec ? 0x3 : 0; formatflag |= gCsv ? 0x0 : 0x4; if(formatflag & 0x1) { fill_profilestat_lists(NULL, startcpu, maxcpus, formatflag ); free_profilestat_lists(); } /*--- printf("pages_cnt=%d\n", pages_cnt); ---*/ return 0; } /*--------------------------------------------------------------------------------*\ * EOF: Offline-testtool Offline-testtool Offline-testtool Offline-testtool Offline-testtool \*--------------------------------------------------------------------------------*/ #endif/*--- #if !defined(__KERNEL__) ---*/