diff options
Diffstat (limited to 'fs/proc/sprd_debug/proc_cpu_usage.c')
| -rw-r--r-- | fs/proc/sprd_debug/proc_cpu_usage.c | 783 |
1 files changed, 783 insertions, 0 deletions
diff --git a/fs/proc/sprd_debug/proc_cpu_usage.c b/fs/proc/sprd_debug/proc_cpu_usage.c new file mode 100644 index 00000000..ead5c3d2 --- /dev/null +++ b/fs/proc/sprd_debug/proc_cpu_usage.c @@ -0,0 +1,783 @@ +/* + * SPRD CPU USAGE TOOL: + * 1. cpu usage per real-cpu & total + * 2. cpu usage per thread + * 3. cpu system info per real-cpu & total: + * contextswitch, pagefault, pagemajfault per cpu & total + * */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/cpumask.h> +#include <linux/fs.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/hrtimer.h> +#include <linux/irqnr.h> +#include <linux/tick.h> +#include <linux/threads.h> +#include <linux/spinlock.h> +#include <linux/rtc.h> +#include <asm/cputime.h> +#include <asm/div64.h> +#include <asm/uaccess.h> +#include <linux/sysrq.h> +#ifdef CONFIG_VM_EVENT_COUNTERS +#include <../../../kernel/sched/sched.h> +#include <linux/mm.h> +#include <linux/vmstat.h> +#endif + +/* + * Macros Definitions: + * ---------------------------------------------- + * 1. DEBUG_PRINT : debug log control + * 2. buffer_size : define ringbuffer size + * 3. tick_2_ms : change tick to ms + * 4. ns_2_ms : change ns to ms + * 5. ns_2_us : change ns to us + * 6. bufferid : change index to buffer id + * */ +#define DEBUG_PRINT 0 +#define buffer_size 3 +#define tick_2_ms(time) (10 * (time)) +#define ns_2_ms(time) (do_div(time, 1000000)) +#define ns_2_us(time) (do_div(time, 1000)) +#define bufferid(index) ((index) % (buffer_size)) + +/* + * CpuInfo Structure Per Thread: + * ---------------------------------------------- + * 1. Located at each-thread stack area, beside threadinfo struct: + * HIGH ADDR ---> Thread Stack End. + * |-------------------------- + * | ... + * | stack + * | ... + * |-------------------------- + * | thread_cpuinfo + * |-------------------------- + * | thread_info + * |-------------------------- + * LOW ADDR ---> Thread Stack Start. + * + * 2. Cpu Threadinfo: + * index : for update; + * start : utime stime start record; + * persistance[] : ringbuffer --> size according to buffer_size; + * */ +typedef struct thread_cpuinfo { + unsigned long index; + struct u_s_time { + unsigned long utime; + unsigned long stime; + }start, persistance[buffer_size]; +}thread_cpuinfo; + +/* + * Cpu Info Structure: + * ---------------------------------------------- + * user : user usage, percentage; + * system : system usage, percentage; + * nice : nice usage, percentage; + * idle : idle usage, percentage; + * iowait : iowait usage, percentage; + * irq : irq usage, percentage; + * softirq : softirq usage, percentage; + * steal : steal usage, percentage; + * sum : sum usage, percentage; + * ------------------------------------ + * ctxt_switch : context switch count; + * pagefault : page fault count; + * pagemajfault : page majfault count; + * */ +typedef struct cpu_info { + unsigned long long user; + unsigned long long system; + unsigned long long nice; + unsigned long long idle; + unsigned long long iowait; + unsigned long long irq; + unsigned long long softirq; + unsigned long long steal; + unsigned long long sum; +#ifdef CONFIG_VM_EVENT_COUNTERS + unsigned long context_switch; + unsigned long pagefault; + unsigned long pagemajfault; +#endif +}cpu_info; + +/* + * Time Stamp Structure: + * ---------------------------------------------- + * 1. start : this record start cpu clock + * 2. end : this record end cpu clock + * 3. ts_start : + * 4. ts_end : + * 5. tm_start : + * 6. tm_end : + * */ +typedef struct time_stamp { + unsigned long long start; + unsigned long long end; + struct timespec ts_start; + struct timespec ts_end; + struct rtc_time tm_start; + struct rtc_time tm_end; +}time_stamp; + +/* + * Recorder Structure: + * ---------------------------------------------- + * 1. percpu : each cpu cpuinfo + * 2. total : total cpu_info + * 3. timestamp : timestamp for this record + * */ +typedef struct cpu_recorder { + struct cpu_info percpu[NR_CPUS]; + struct cpu_info total; + struct time_stamp timestamp; +}cpu_recorder; + +/* + * Global Data: + * ---------------------------------------------- + * 1. hrtimer : for update + * 2. global_index : for per thread + * 3. usage_lock : spinlock for update & print + * 4. recorder : for cpu usage + * */ +ktime_t kt; +long hrtimer_se = 3; +long long hrtimer_ns = 0; +static struct hrtimer timer; +static unsigned long global_index = 0; +static DEFINE_SPINLOCK(usage_lock); +static struct cpu_recorder recorder[buffer_size]; +static struct cpu_info prev_value[NR_CPUS]; +static struct timespec ts; +static unsigned long long each_time = 0; + +/* + * Extern Function: + * ---------------------------------------------- + * 1. simeple_strtol : change string to long + * 2. dump_stack : for debug + * */ +extern long simple_strtol(const char *cp, char **endp, unsigned int base); +extern void dump_stack(void); + + +/* + * Functions Start + * ---------------------------------------------- + * */ +static void div64_compute_ratio(const unsigned long long dividend, const unsigned long long divider, unsigned long *result) +{ + /*check divider*/ + if(0 == divider) { + result[0] = result[1] = 0; + return; + } + + /*64bit divition*/ + result[0] = 10000 * dividend; + result[1] = do_div(result[0], divider); + + /*save result as xx.xx%*/ + result[1] = result[0] % 100; + result[0] = result[0] / 100; + +#if 0 + /*to avoid cpu rate > 100%*/ + if(result[0] > 100) { + result[0] = 100; + result[1] = 0; + } +#endif +} + +#ifdef arch_idle_time +static cputime64_t get_idle_time(int cpu) +{ + cputime64_t idle; + + idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE]; + if (cpu_online(cpu) && !nr_iowait_cpu(cpu)) + idle += arch_idle_time(cpu); + return idle; +} + +static cputime64_t get_iowait_time(int cpu) +{ + cputime64_t iowait; + + iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT]; + if (cpu_online(cpu) && nr_iowait_cpu(cpu)) + iowait += arch_idle_time(cpu); + return iowait; +} +#else +static u64 get_idle_time(int cpu) +{ + u64 idle, idle_time = -1ULL; + + /* FIXME: the idle time from get_cpu_idle_time_us() is reset while CPU is hot-pluged. + * Using cpustat[CPUTIME_IDLE] to get idle. It isn't very accurate, but stable */ +#if 0 + if (cpu_online(cpu)) + idle_time = get_cpu_idle_time_us(cpu, NULL); +#endif + + if (idle_time == -1ULL) + idle = kcpustat_cpu(cpu).cpustat[CPUTIME_IDLE]; + else + idle = usecs_to_cputime64(idle_time); + + //FIXME: this idle function has bug on our shark platform: + // not monotone increasing + if(DEBUG_PRINT) printk("acedebug: get_idle_time: cpu=%d, idle=%llu\n", cpu, idle); + + return idle; +} + +static u64 get_iowait_time(int cpu) +{ + u64 iowait, iowait_time = -1ULL; + + /* FIXME: the iowait time from get_cpu_iowait_time_us() is reset while CPU is hot-pluged. + * Using cpustat[CPUTIME_IOWAIT] to get iowait. It isn't very accurate, but stable */ +#if 0 + if (cpu_online(cpu)) + iowait_time = get_cpu_iowait_time_us(cpu, NULL); +#endif + + if (iowait_time == -1ULL) + iowait = kcpustat_cpu(cpu).cpustat[CPUTIME_IOWAIT]; + else + iowait = usecs_to_cputime64(iowait_time); + + return iowait; +} +#endif + +static void get_each_cpu_data(struct cpu_info *new, int cpuid) +{ +#ifdef CONFIG_VM_EVENT_COUNTERS + struct vm_event_state *this = &per_cpu(vm_event_states, cpuid); + new->context_switch = cpu_rq(cpuid)->nr_switches; + new->pagefault = this->event[PGFAULT]; + new->pagemajfault = this->event[PGMAJFAULT]; +#endif + + new->sum = new->user = kcpustat_cpu(cpuid).cpustat[CPUTIME_USER]; + new->sum += new->system = kcpustat_cpu(cpuid).cpustat[CPUTIME_SYSTEM]; + new->sum += new->nice = kcpustat_cpu(cpuid).cpustat[CPUTIME_NICE]; + new->sum += new->idle = get_idle_time(cpuid); + new->sum += new->iowait = get_iowait_time(cpuid); + new->sum += new->irq = kcpustat_cpu(cpuid).cpustat[CPUTIME_IRQ]; + new->sum += new->softirq = kcpustat_cpu(cpuid).cpustat[CPUTIME_SOFTIRQ]; + new->sum += new->steal = kcpustat_cpu(cpuid).cpustat[CPUTIME_STEAL]; +} + +static void update_cpu_record(struct cpu_recorder *record, struct cpu_info *prev, struct cpu_info *next, int cpuid) +{ + record->total.user += record->percpu[cpuid].user = next->user - prev->user; + record->total.system += record->percpu[cpuid].system = next->system - prev->system; + record->total.nice += record->percpu[cpuid].nice = next->nice - prev->nice; + /* + * FIXME begin: idle from system maybe wrong!!! + * in our multi-core system idle may not monotone increasing!!! + * SO: here we walk-around, but need modify in future!!! + */ + if(next->idle < prev->idle) { + prev->idle = (next->idle > 200) ? (next->idle - 200) : 0; + prev->sum = prev->user + prev->system + prev->nice + prev->idle + prev->iowait + prev->irq + prev->softirq + prev->steal; + } + + record->total.idle += record->percpu[cpuid].idle = next->idle - prev->idle; + record->total.iowait += record->percpu[cpuid].iowait = next->iowait - prev->iowait; + record->total.irq += record->percpu[cpuid].irq = next->irq - prev->irq; + record->total.softirq += record->percpu[cpuid].softirq = next->softirq - prev->softirq; + record->total.steal += record->percpu[cpuid].steal = next->steal - prev->steal; + record->total.sum += record->percpu[cpuid].sum = next->sum - prev->sum; + +#ifdef CONFIG_VM_EVENT_COUNTERS + /* + * FIXME begin: pagefault & pagemajfault maybe wrong!!! + * in our multi-core system pagefault & pagemajfault may not monotone increasing!!! + * SO: here we walk-around, but need modify in future!!! + */ + if(next->context_switch < prev->context_switch) prev->context_switch = next->context_switch; + if(next->pagefault < prev->pagefault) prev->pagefault = next->pagefault; + if(next->pagemajfault < prev->pagemajfault) prev->pagemajfault = next->pagemajfault; + + record->total.context_switch += record->percpu[cpuid].context_switch = next->context_switch - prev->context_switch; + record->total.pagefault += record->percpu[cpuid].pagefault = next->pagefault - prev->pagefault; + record->total.pagemajfault += record->percpu[cpuid].pagemajfault = next->pagemajfault - prev->pagemajfault; +#endif +} + +static void cpu_threadinfo_clean(struct thread_cpuinfo *buffer) +{ + unsigned long i = 0; + unsigned long begin = 0; + unsigned long end = 0; + + begin = bufferid(buffer->index) + 1; + end = bufferid(global_index); + if((global_index - buffer->index) >= buffer_size) { + begin = 0; + end = buffer_size - 1; + } + + if(begin < end + 1) { + for(i = begin; i < end + 1; i++) { + buffer->persistance[i].utime = 0; + buffer->persistance[i].stime = 0; + } + } else { + for(i = begin; i < buffer_size; i++) { + buffer->persistance[i].utime = 0; + buffer->persistance[i].stime = 0; + } + for(i = 0; i < end + 1; i++) { + buffer->persistance[i].utime = 0; + buffer->persistance[i].stime = 0; + } + } +} + +static void update_prev(struct task_struct *prev) +{ + struct thread_cpuinfo *buffer = NULL; + + /*ignore pid=0 process*/ + //if(!strncmp(prev->comm, "swapper/", 8)) return; + + /*thread buffer*/ + buffer = (struct thread_cpuinfo *)(prev->stack + sizeof(struct thread_info)); + if(!buffer) return; + + /*update*/ + if(buffer->index == global_index) { + /*update utime stime*/ + buffer->persistance[bufferid(buffer->index)].utime += prev->utime - buffer->start.utime; + buffer->persistance[bufferid(buffer->index)].stime += prev->stime - buffer->start.stime; + } else { + /*clean buffer*/ + cpu_threadinfo_clean(buffer); + + /*update index utime stime*/ + buffer->index = global_index; + buffer->persistance[bufferid(buffer->index)].utime = prev->utime - buffer->start.utime; + buffer->persistance[bufferid(buffer->index)].stime = prev->stime - buffer->start.stime; + } +} + +static void update_next(struct task_struct *next) +{ + struct thread_cpuinfo *buffer = NULL; + + /*ignore pid=0 process*/ + //if(!strncmp(next->comm, "swapper/", 8)) return; + + /*thread buffer*/ + buffer = (struct thread_cpuinfo *)(next->stack + sizeof(struct thread_info)); + if(!buffer) return; + + /*update utime stime*/ + buffer->start.utime = next->utime; + buffer->start.stime = next->stime; +} + + +void update_cpu_usage(struct task_struct *prev, struct task_struct *next) +{ + unsigned long flags; + + /*lock*/ + spin_lock_irqsave(&usage_lock, flags); + + /*prev*/ + update_prev(prev); + + /*next*/ + update_next(next); + + /*unlock*/ + spin_unlock_irqrestore(&usage_lock, flags); + + return; +} + +static void record_cpu_usage(void) +{ + int i, pos; + struct cpu_info next; + + /*init*/ + pos = bufferid(global_index); + memset(&next, 0, sizeof(struct cpu_info)); + memset(&recorder[pos], 0, sizeof(struct cpu_recorder)); + + /*record start time*/ + recorder[pos].timestamp.start = each_time; + memcpy(&recorder[pos].timestamp.ts_start, &ts, sizeof(struct timespec)); + rtc_time_to_tm(recorder[pos].timestamp.ts_start.tv_sec, &recorder[pos].timestamp.tm_start); + + /*record end time*/ + recorder[pos].timestamp.end = each_time = cpu_clock(0); + getnstimeofday(&ts); + memcpy(&recorder[pos].timestamp.ts_end, &ts, sizeof(struct timespec)); + rtc_time_to_tm(recorder[pos].timestamp.ts_end.tv_sec, &recorder[pos].timestamp.tm_end); + + /*update recorder[pos] cpu data*/ + for_each_possible_cpu(i) { + /*get next*/ + get_each_cpu_data(&next, i); + + /*update recorder*/ + update_cpu_record(&recorder[pos], &prev_value[i], &next, i); + + /*update prev*/ + memcpy(&prev_value[i], &next, sizeof(struct cpu_info)); + } + + /*update global index*/ + global_index++; +} + +static void compute_print_cpu_rate(struct seq_file *p, void *v, struct cpu_recorder *record) +{ + unsigned long idle_ratio[2] = {0, 0}; + unsigned long user_ratio[2] = {0, 0}; + unsigned long system_ratio[2] = {0, 0}; + unsigned long nice_ratio[2] = {0, 0}; + unsigned long iowait_ratio[2] = {0, 0}; + unsigned long irq_ratio[2] = {0, 0}; + unsigned long softirq_ratio[2] = {0, 0}; + unsigned long steal_ratio[2] = {0, 0}; + unsigned long sum_ratio[2] = {0, 0}; + int i; + + for_each_possible_cpu(i) { + /*compute each cpu*/ + div64_compute_ratio(record->percpu[i].idle, record->percpu[i].sum, idle_ratio); + div64_compute_ratio(record->percpu[i].user, record->percpu[i].sum, user_ratio); + div64_compute_ratio(record->percpu[i].system, record->percpu[i].sum, system_ratio); + div64_compute_ratio(record->percpu[i].nice, record->percpu[i].sum, nice_ratio); + div64_compute_ratio(record->percpu[i].iowait, record->percpu[i].sum, iowait_ratio); + div64_compute_ratio(record->percpu[i].irq, record->percpu[i].sum, irq_ratio); + div64_compute_ratio(record->percpu[i].softirq, record->percpu[i].sum, softirq_ratio); + div64_compute_ratio(record->percpu[i].steal, record->percpu[i].sum, steal_ratio); + div64_compute_ratio(record->percpu[i].sum, record->percpu[i].sum, sum_ratio); + /*print each cpu*/ + seq_printf(p, " cpu%d(%d): %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% | %15lu %15lu %15lu\n", + i, cpu_online(i), idle_ratio[0],idle_ratio[1], user_ratio[0],user_ratio[1], system_ratio[0],system_ratio[1], \ + nice_ratio[0],nice_ratio[1], iowait_ratio[0],iowait_ratio[1], irq_ratio[0],irq_ratio[1], \ + softirq_ratio[0],softirq_ratio[1], steal_ratio[0],steal_ratio[1], sum_ratio[0],sum_ratio[1], \ + record->percpu[i].context_switch, record->percpu[i].pagefault, record->percpu[i].pagemajfault); + } + + if(NR_CPUS > 1) { + /*compute total*/ + div64_compute_ratio(record->total.idle, record->total.sum, idle_ratio); + div64_compute_ratio(record->total.user, record->total.sum, user_ratio); + div64_compute_ratio(record->total.system, record->total.sum, system_ratio); + div64_compute_ratio(record->total.nice, record->total.sum, nice_ratio); + div64_compute_ratio(record->total.iowait, record->total.sum, iowait_ratio); + div64_compute_ratio(record->total.irq, record->total.sum, irq_ratio); + div64_compute_ratio(record->total.softirq, record->total.sum, softirq_ratio); + div64_compute_ratio(record->total.steal, record->total.sum, steal_ratio); + div64_compute_ratio(record->total.sum, record->total.sum, sum_ratio); + /*print total*/ + seq_printf(p, " ------------------\n"); + seq_printf(p, " Total: %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% | %15lu %15lu %15lu\n", + idle_ratio[0],idle_ratio[1], user_ratio[0],user_ratio[1], system_ratio[0],system_ratio[1], \ + nice_ratio[0],nice_ratio[1], iowait_ratio[0],iowait_ratio[1], irq_ratio[0],irq_ratio[1], \ + softirq_ratio[0],softirq_ratio[1], steal_ratio[0],steal_ratio[1], sum_ratio[0],sum_ratio[1], \ + record->total.context_switch, record->total.pagefault, record->total.pagemajfault); + } +} + +static void compute_print_each_thread_rate(struct seq_file *p, void *v, int pid, char *name, + unsigned long u_time, unsigned long s_time, unsigned long index) +{ + unsigned long utime_ratio[2] = {0, 0}; + unsigned long stime_ratio[2] = {0, 0}; + unsigned long ttime_ratio[2] = {0, 0}; + unsigned long timer_circle_long = 0; + + /*devider*/ + timer_circle_long = recorder[index].timestamp.end - recorder[index].timestamp.start; + ns_2_ms(timer_circle_long); + + /*compute ratio*/ + div64_compute_ratio(u_time, timer_circle_long, utime_ratio); + div64_compute_ratio(s_time, timer_circle_long, stime_ratio); + div64_compute_ratio((u_time + s_time), timer_circle_long, ttime_ratio); + + if(NULL != name) { + seq_printf(p, " %-6d %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%% %-15s\n", + pid, utime_ratio[0],utime_ratio[1], stime_ratio[0],stime_ratio[1], ttime_ratio[0],ttime_ratio[1], name); + } else { + seq_printf(p, " %-6s %4lu.%02lu%% %4lu.%02lu%% %4lu.%02lu%%\n", + "Total:", utime_ratio[0],utime_ratio[1], stime_ratio[0],stime_ratio[1], ttime_ratio[0],ttime_ratio[1]); + } +} + +static void print_summary(struct seq_file *p, void *v, unsigned long bufid) +{ + unsigned long long timelongth; + unsigned long long start_time = 0; + unsigned long long end_time = 0; + + /*update recorder[bufid].timestamp temporarily*/ + if(bufid == bufferid(global_index)) { + recorder[bufid].timestamp.start = each_time; + recorder[bufid].timestamp.end = cpu_clock(0); + memcpy(&recorder[bufid].timestamp.ts_start, &ts, sizeof(struct timespec)); + getnstimeofday(&recorder[bufid].timestamp.ts_end); + rtc_time_to_tm(recorder[bufid].timestamp.ts_start.tv_sec, &recorder[bufid].timestamp.tm_start); + rtc_time_to_tm(recorder[bufid].timestamp.ts_end.tv_sec, &recorder[bufid].timestamp.tm_end); + } + + /*compute how long time*/ + timelongth = recorder[bufid].timestamp.end - recorder[bufid].timestamp.start; + + /*start_time & end_time*/ + start_time = recorder[bufid].timestamp.start; + end_time = recorder[bufid].timestamp.end; + + /*change ns to ms*/ + ns_2_ms(start_time); + ns_2_ms(end_time); + ns_2_ms(timelongth); + + /*print cpu core count*/ + seq_printf(p, "\n\nCpu Core Count: %-6d\n", NR_CPUS); + + /*from start_time to end_time*/ + seq_printf(p, "Timer Circle: %-llums.\n",timelongth); + seq_printf(p, " From time %llums(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC) to %llums(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC).\n\n", + start_time, recorder[bufid].timestamp.tm_start.tm_year + 1900, recorder[bufid].timestamp.tm_start.tm_mon + 1, + recorder[bufid].timestamp.tm_start.tm_mday, recorder[bufid].timestamp.tm_start.tm_hour, recorder[bufid].timestamp.tm_start.tm_min, + recorder[bufid].timestamp.tm_start.tm_sec, recorder[bufid].timestamp.ts_start.tv_nsec, + end_time, recorder[bufid].timestamp.tm_end.tm_year + 1900, recorder[bufid].timestamp.tm_end.tm_mon + 1, + recorder[bufid].timestamp.tm_end.tm_mday, recorder[bufid].timestamp.tm_end.tm_hour, recorder[bufid].timestamp.tm_end.tm_min, + recorder[bufid].timestamp.tm_end.tm_sec, recorder[bufid].timestamp.ts_end.tv_nsec); +} + +static void print_cpu_usage(struct seq_file *p, void *v, unsigned long bufid) +{ + int i; + struct cpu_info now; + + /*init*/ + memset(&now, 0, sizeof(cpu_info)); + + /*update current record temporarily*/ + if(bufid == bufferid(global_index)) { + memset(&recorder[bufid].percpu, 0, NR_CPUS * sizeof(struct cpu_info)); + memset(&recorder[bufid].total, 0, sizeof(struct cpu_info)); + + /*data*/ + for_each_possible_cpu(i) { + /*get now*/ + get_each_cpu_data(&now, i); + + /*update recorder*/ + update_cpu_record(&recorder[bufid], &prev_value[i], &now, i); + } + } + + /*print tile*/ + seq_printf(p, "%-87s %-s\n", " * CPU USAGE:", " | * OTHER COUNTS:"); + seq_printf(p, " -%lu- %8s %8s %8s %8s %8s %8s %8s %8s %8s | %15s %15s %15s\n", + bufid, "IDLE", "USER", "SYSTEM", "NICE", "IOWAIT", "IRQ", "SOFTIRQ", "STEAL", "TOTAL", "CTXT_SWITCH", "FG_FAULT", "FG_MAJ_FAULT"); + + /*compute & print*/ + compute_print_cpu_rate(p, v, &recorder[bufid]); +} + +static void print_each_thread_usage(struct seq_file *p, void *v, unsigned long bufid) +{ + unsigned long total_usr = 0; + unsigned long total_sys = 0; + struct task_struct *gp, *pp; + struct thread_cpuinfo *buffer = NULL; + + /*print tile*/ + seq_printf(p, "\n* USAGE PER THREAD:\n"); + seq_printf(p, " %-6s %8s %8s %8s %-15s\n", "PID", "USER", "SYSTEM", "TOTAL", "NAME"); + + /*for all threads*/ + do_each_thread(gp, pp) { + + /*get ringbuffer*/ + buffer = (struct thread_cpuinfo *)(pp->stack + sizeof(struct thread_info)); + if(!buffer) continue; + + /*clean ringbuffer*/ + if(global_index != buffer->index) { + + cpu_threadinfo_clean(buffer); + + /*update index*/ + buffer->index = global_index; + } + + /*print each none zero thread*/ + if(0 != (buffer->persistance[bufid].utime + buffer->persistance[bufid].stime)) { + + unsigned long u_time = tick_2_ms(buffer->persistance[bufid].utime); + unsigned long s_time = tick_2_ms(buffer->persistance[bufid].stime); + + /*compute total*/ + total_usr += buffer->persistance[bufid].utime; + total_sys += buffer->persistance[bufid].stime; + + compute_print_each_thread_rate(p, v, pp->pid, pp->comm, u_time, s_time, bufid); + } + } while_each_thread(gp, pp); + + /*print total*/ + seq_printf(p, " ------------------\n"); + compute_print_each_thread_rate(p, v, 0, NULL, (tick_2_ms(total_usr)), (tick_2_ms(total_sys)), bufid); +} + +static void print_usage(struct seq_file *p, void *v) +{ + unsigned long flags; + unsigned long i; + /*trigger sysrq when print usage for extra debug info*/ + __handle_sysrq('w' , false); + /*lock*/ + spin_lock_irqsave(&usage_lock, flags); + + /*each thread: global_index+1 ---> buffer_end*/ + for(i = bufferid(global_index) + 1; i < buffer_size; i++) { + + /*print time circle*/ + print_summary(p, v, i); + + /*print cpu info*/ + print_cpu_usage(p, v, i); + + /*print thread cpu_info*/ + print_each_thread_usage(p, v, i); + } + + /*each thread: buffer_start ---> global_index */ + for(i = 0; i < bufferid(global_index) + 1; i++) { + + /*print time circle*/ + print_summary(p, v, i); + + /*print cpu info*/ + print_cpu_usage(p, v, i); + + /*print thread cpu_info*/ + print_each_thread_usage(p, v, i); + } + + /*unlock*/ + spin_unlock_irqrestore(&usage_lock, flags); +} + +static enum hrtimer_restart hrtimer_handler(struct hrtimer *timer) +{ + unsigned long flags; + + /*lock*/ + spin_lock_irqsave(&usage_lock, flags); + + /*update cpu rate*/ + record_cpu_usage(); + + /*update hrtimer*/ + if((hrtimer_se > 0) || (hrtimer_ns >0)) { + kt = ktime_set(hrtimer_se, hrtimer_ns); + } + + hrtimer_forward_now(timer, kt); + + /*unlock*/ + spin_unlock_irqrestore(&usage_lock, flags); + + return HRTIMER_RESTART; +} + +static int show_cpu_usage(struct seq_file *p, void *v) +{ + print_usage(p, v); + + return 0; +} + +static int cpu_usage_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) +{ + char flag[len + 1]; + char **endp = NULL; + + memset(flag, 0 ,sizeof(flag)); + + if(copy_from_user(flag, buf, len)) return -EFAULT; + + hrtimer_se = simple_strtol(flag, endp, 10); + + return len; +} + +static int cpu_usage_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_cpu_usage, NULL); +} + +struct file_operations proc_cpu_usage_fops = { + .open = cpu_usage_open, + .read = seq_read, + .write = cpu_usage_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init proc_sprd_cpu_usage_init(void) +{ +#ifdef CONFIG_SPRD_DEBUG_CPU_RATE + /*int static*/ + memset(recorder, 0, sizeof(recorder)); + memset(prev_value, 0, sizeof(prev_value)); + memset(&ts, 0, sizeof(struct timespec)); + + /*init timer*/ + kt = ktime_set(hrtimer_se, hrtimer_ns); + hrtimer_init(&timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + timer.function = hrtimer_handler; + hrtimer_start(&timer, kt, HRTIMER_MODE_REL); + + /*create proc*/ + //proc_create("sprd_cpu_usage", 0, NULL, &proc_cpu_usage_fops); +#endif + + return 0; +} + +static void __exit proc_sprd_cpu_usage_exit(void) +{ +#ifdef CONFIG_SPRD_DEBUG_CPU_RATE + hrtimer_cancel(&timer); +#endif + return; +} + +module_init(proc_sprd_cpu_usage_init); +module_exit(proc_sprd_cpu_usage_exit); + |
