diff options
Diffstat (limited to 'fs/proc/sprd_debug')
| -rw-r--r-- | fs/proc/sprd_debug/Kconfig | 39 | ||||
| -rw-r--r-- | fs/proc/sprd_debug/Makefile | 12 | ||||
| -rw-r--r-- | fs/proc/sprd_debug/proc_cpu_usage.c | 783 | ||||
| -rw-r--r-- | fs/proc/sprd_debug/proc_phymem.c | 190 | ||||
| -rw-r--r-- | fs/proc/sprd_debug/proc_sprd_debug.c | 227 | ||||
| -rw-r--r-- | fs/proc/sprd_debug/proc_sprd_debug_frame.c | 135 | ||||
| -rw-r--r-- | fs/proc/sprd_debug/proc_usermeminfo.c | 174 | ||||
| -rw-r--r-- | fs/proc/sprd_debug/proc_userstack.c | 169 | ||||
| -rw-r--r-- | fs/proc/sprd_debug/sprd_uboot_log_dump.c | 121 | ||||
| -rw-r--r-- | fs/proc/sprd_debug/usermeminfo.h | 4 |
10 files changed, 1854 insertions, 0 deletions
diff --git a/fs/proc/sprd_debug/Kconfig b/fs/proc/sprd_debug/Kconfig new file mode 100644 index 00000000..958f0f2c --- /dev/null +++ b/fs/proc/sprd_debug/Kconfig @@ -0,0 +1,39 @@ +config SPRD_DEBUG_FRAME + bool "sprd debug proc interface support" + depends on DEBUG_FS && SPRD_DEBUG + default y + +config SPRD_DEBUG_PHYMEM_INFO + bool "physical memory show support" + depends on SPRD_DEBUG_FRAME + default n + +config SPRD_DEBUG_USRPROCMEM_INFO + bool "user proc's memory show support" + depends on SPRD_DEBUG_FRAME + default n + +config SPRD_DEBUG_USRPROCSTACK_INFO + bool "user proc's current stack show support" + depends on SPRD_DEBUG_FRAME + default n + +config SPRD_DEBUG_CPU_RATE + bool "open sprd cpu rate compute" + depends on SPRD_DEBUG_FRAME + default y + +config SPRD_DEBUG_MORE_SMAPS_INFO + default n + depends on PROC_FS && MMU + bool "Enable more smaps info" if EXPERT + help + Add more infos in /proc/pid/smaps + +config SPRD_UBOOT_LOG_DUMP + bool "Spreadtrum u-boot log dump" + depends on SPRD_SYSDUMP && PROC_FS + default y + help + Enable this option to support dumping u-boot log via sysfs from + userspace diff --git a/fs/proc/sprd_debug/Makefile b/fs/proc/sprd_debug/Makefile new file mode 100644 index 00000000..8425757f --- /dev/null +++ b/fs/proc/sprd_debug/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the Linux proc filesystem routines. +# + +obj-$(CONFIG_SPRD_DEBUG) += sprd_debug.o +obj-$(CONFIG_SPRD_UBOOT_LOG_DUMP) += sprd_uboot_log_dump.o +sprd_debug-$(CONFIG_SPRD_DEBUG_FRAME) += proc_sprd_debug_frame.o +sprd_debug-$(CONFIG_SPRD_DEBUG_PHYMEM_INFO) += proc_phymem.o +sprd_debug-$(CONFIG_SPRD_DEBUG_SCHED_LOG) += proc_sprd_debug.o +sprd_debug-$(CONFIG_SPRD_DEBUG_USRPROCMEM_INFO) += proc_usermeminfo.o +sprd_debug-$(CONFIG_SPRD_DEBUG_USRPROCSTACK_INFO) += proc_userstack.o +sprd_debug-$(CONFIG_SPRD_DEBUG_CPU_RATE) += proc_cpu_usage.o 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); + diff --git a/fs/proc/sprd_debug/proc_phymem.c b/fs/proc/sprd_debug/proc_phymem.c new file mode 100644 index 00000000..2984a38e --- /dev/null +++ b/fs/proc/sprd_debug/proc_phymem.c @@ -0,0 +1,190 @@ +#include <linux/module.h> +#include <linux/swap.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/seq_file.h> +#include <linux/proc_fs.h> +#include <linux/mm.h> +#include <linux/nmi.h> +#include <linux/quicklist.h> +#include <linux/vmalloc.h> +#include <asm/setup.h> +#include "../internal.h" + +#define K(x) ((x) << (PAGE_SHIFT - 10)) + +extern struct meminfo meminfo; + +static struct proc_dir_entry* phymem; + +static int _phymem_map_proc_show(struct seq_file *m, void *v) +{ + int node, i, totalsize = 0; + struct meminfo *mi = &meminfo; + + for_each_bank (i,mi) { + struct membank *bank = &mi->bank[i]; + unsigned int pfn1, pfn2; + totalsize += bank->size; + seq_printf(m, "0x%08lx - 0x%08lx %lukB\n", + bank->start, bank->start + bank->size, bank->size/1024); + } + seq_printf(m, "linux total memory: %dkB\n", totalsize/1024); + return 0; +} + +static int _phymem_pages_proc_show(struct seq_file *m, void *v) +{ + int free = 0, total = 0, reserved = 0; + int other = 0, shared = 0, cached = 0, slab = 0, node, i; + + struct meminfo * mi = &meminfo; + for_each_bank (i,mi) { + struct membank *bank = &mi->bank[i]; + unsigned int pfn1, pfn2; + struct page *page, *end; + pfn1 = bank_pfn_start(bank); + pfn2 = bank_pfn_end(bank); + page = pfn_to_page(pfn1); + end = pfn_to_page(pfn2 - 1) + 1; + do { + total++; + if (PageReserved(page)) + reserved++; + else if (PageSwapCache(page)) + cached++; + else if (PageSlab(page)) + slab++; + else if (page_count(page) > 1) + shared++; + else if (!page_count(page)) + free++; + else + other++; + page++; + }while (page < end); + } + + seq_printf(m, "pages of RAM %d\n", total); + seq_printf(m, "free pages %d\n", free); + seq_printf(m, "reserved pages %d\n", reserved); + seq_printf(m, "slab pages %d\n", slab); + seq_printf(m, "pages shared %d\n", shared); + seq_printf(m, "pages swap cached %d\n", cached); + seq_printf(m, "other pages %d\n", other); + return 0; +} + +#define MLK_ROUNDUP(b, t) b, t, DIV_ROUND_UP(((t) - (b)), SZ_1K) + +static int _phymem_dist_proc_show(struct seq_file *m, void *v) +{ + struct sysinfo sys_info; + int node, i, pfn = 0, linux_total_size = 0; + struct meminfo *mi = &meminfo; + long cached; + long kernelmem; + struct vmalloc_info vmi; + unsigned long pages[NR_LRU_LISTS]; + int lru; + + for_each_bank (i,mi) { + struct membank *bank = &mi->bank[i]; + unsigned int pfn1, pfn2; + linux_total_size += bank->size; + pfn = bank_pfn_end(bank); + } + + si_meminfo(&sys_info); + si_swapinfo(&sys_info); + + cached = global_page_state(NR_FILE_PAGES) - + total_swapcache_pages() - sys_info.bufferram; + if (cached < 0) + cached = 0; + + for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++) + pages[lru] = global_page_state(NR_LRU_BASE + lru); + + kernelmem = K(sys_info.totalram - sys_info.freeram - sys_info.bufferram - pages[LRU_ACTIVE_ANON] \ + - pages[LRU_INACTIVE_ANON] - cached - total_swapcache_pages()); + get_vmalloc_info(&vmi); + + seq_printf(m, "mem: %lukB\n", pfn*4); + seq_printf(m, " |--mem_other: %lukB\n", pfn*4 - linux_total_size/1024); + seq_printf(m, " |--mem_linux: %lukB\n", linux_total_size/1024); + seq_printf(m, " |--reserved: %lukB\n", linux_total_size/1024 - K(sys_info.totalram)); + seq_printf(m, " |--mem_total: %lukB\n", K(sys_info.totalram)); + seq_printf(m, " |--free: %lukB\n", K(sys_info.freeram)); + seq_printf(m, " |--buffer: %lukB\n", K(sys_info.bufferram)); + seq_printf(m, " |--cache: %lukB\n", K(cached)); + seq_printf(m, " |--swapcache: %lukB\n", K(total_swapcache_pages())); + seq_printf(m, " |--user: %lukB\n", K(pages[LRU_ACTIVE_ANON] + pages[LRU_INACTIVE_ANON])); + seq_printf(m, " | |--active anon: %lukB\n", K(pages[LRU_ACTIVE_ANON])); + seq_printf(m, " | |--inactive anon: %lukB\n", K(pages[LRU_INACTIVE_ANON])); + seq_printf(m, " |--kernel: %lukB\n", kernelmem); + seq_printf(m, " |--stack: %lukB\n", global_page_state(NR_KERNEL_STACK) * THREAD_SIZE / 1024); + seq_printf(m, " |--slab: %lukB\n", K(global_page_state(NR_SLAB_RECLAIMABLE) +global_page_state(NR_SLAB_UNRECLAIMABLE))); + seq_printf(m, " |--pagetable: %lukB\n", K(global_page_state(NR_PAGETABLE))); + seq_printf(m, " |--vmalloc: %lukB\n", + kernelmem - global_page_state(NR_KERNEL_STACK) * THREAD_SIZE / 1024 \ + - K(global_page_state(NR_SLAB_RECLAIMABLE) - global_page_state(NR_SLAB_UNRECLAIMABLE)) \ + -K(global_page_state(NR_PAGETABLE))); + return 0; +} + + +static int _phymem_map_proc_open(struct inode* inode, struct file* file) +{ + single_open(file, _phymem_map_proc_show, NULL); + return 0; +} + +static int _phymem_pages_proc_open(struct inode* inode, struct file* file) +{ + single_open(file, _phymem_pages_proc_show, NULL); + return 0; +} + +static int _phymem_dist_proc_open(struct inode* inode, struct file* file) +{ + single_open(file, _phymem_dist_proc_show, NULL); + return 0; +} + +static int _phymem_showmem_proc_show(struct file *filp, const char __user *buf, size_t len, loff_t *ppos) +{ + /* show_mem(); */ + return 0; +} + +struct file_operations phymem_map_proc_fops = { + .open = _phymem_map_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +struct file_operations phymem_pages_proc_fops = { + .open = _phymem_pages_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +struct file_operations phymem_dist_proc_fops = { + .open = _phymem_dist_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +struct file_operations phymem_showmem_proc_fops = { + .open = NULL, + .write = _phymem_showmem_proc_show, + .llseek = NULL, + .release = NULL, +}; + diff --git a/fs/proc/sprd_debug/proc_sprd_debug.c b/fs/proc/sprd_debug/proc_sprd_debug.c new file mode 100644 index 00000000..802fb252 --- /dev/null +++ b/fs/proc/sprd_debug/proc_sprd_debug.c @@ -0,0 +1,227 @@ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/seq_file.h> +#include <linux/proc_fs.h> +#include <soc/sprd/sprd_debug.h> +#include <asm/uaccess.h> +#include <linux/regs_debug.h> + +extern struct sched_log (*psprd_debug_log); +extern int get_task_log_idx(int cpu); +extern int get_irq_log_idx(int cpu); + + +/* sche debug proc */ +static int scheinfo_num = 50; + +static int get_task_log_start_idx(int cpu, int length) +{ + if (length >= SCHED_LOG_MAX) + return -1; + + return (SCHED_LOG_MAX + get_task_log_idx(cpu) - length + 1) % SCHED_LOG_MAX; +} + +static int _scheinfo_proc_show(struct seq_file *m, void *v) +{ + int i; + int j; + int idx; + int start_idx; + for (i = 0; i < NR_CPUS; i++) { + seq_printf(m, "cpu %d\n", i); + seq_printf(m, "%7s%20s %-20s\n", "pid", "time", "taskName"); + seq_printf(m, "-------------------------------------------\n"); + start_idx = get_task_log_start_idx(i, scheinfo_num); + for (j = 0 ; j < scheinfo_num; j++ ) { + idx = (start_idx + j) % SCHED_LOG_MAX; + seq_printf(m, "%7d%20llu %-20s\n", + psprd_debug_log->task[i][idx].pid, + psprd_debug_log->task[i][idx].time, + psprd_debug_log->task[i][idx].comm); + } + seq_printf(m, "\n"); + } + + return 0; +} + +static int _scheinfo_proc_open(struct inode* inode, struct file* file) +{ + single_open(file, _scheinfo_proc_show, NULL); + return 0; +} + +struct file_operations scheinfo_proc_fops = { + .open = _scheinfo_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + +static int _scheinfo_num_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%d\n", scheinfo_num); + return 0; +} + +static int _scheinfo_num_proc_open(struct inode* inode, struct file* file) +{ + single_open(file, _scheinfo_num_proc_show, NULL); + return 0; +} + +static int _scheinfo_num_proc_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) +{ + char sche_num_buf[10] = {0}; + int sche_num; + int err; + + if (len <= 0 || len >= 10) + return -EFAULT; + + if (copy_from_user(&sche_num_buf, buf, len)) + return -EFAULT; + + err = strict_strtol(strstrip(sche_num_buf), 0, &sche_num); + + if (err) + return -EINVAL; + + scheinfo_num = sche_num; + + return strlen(sche_num_buf); +} + +struct file_operations scheinfo_num_proc_fops = { + .open = _scheinfo_num_proc_open, + .read = seq_read, + .write = _scheinfo_num_proc_write, + .llseek = seq_lseek, + .release = single_release, +}; + + +/* irq debug proc */ +static int irqinfo_num = 50; + +static int get_irq_log_start_idx(int cpu, int length) +{ + if (length >= SCHED_LOG_MAX) + return -1; + + return (SCHED_LOG_MAX + get_irq_log_idx(cpu) - length + 1) % SCHED_LOG_MAX; +} + +static int _irqinfo_proc_show(struct seq_file *m, void *v) +{ + int i; + int j; + int idx; + int start_idx; + for (i = 0; i < NR_CPUS; i++) { + seq_printf(m, "cpu %d\n", i); + seq_printf(m, "%7s%20s%5s%18s\n", "irq", "time", "en", "fn"); + seq_printf(m, "-------------------------------------------\n"); + start_idx = get_irq_log_start_idx(i, irqinfo_num); + for (j = 0 ; j < irqinfo_num; j++ ) { + idx = (start_idx + j) % SCHED_LOG_MAX; + seq_printf(m, "%7d%20llu%5d%18p\n", + psprd_debug_log->irq[i][idx].irq, + psprd_debug_log->irq[i][idx].time, + psprd_debug_log->irq[i][idx].en, + psprd_debug_log->irq[i][idx].fn); + } + seq_printf(m, "\n"); + } + + return 0; +} + +static int _irqinfo_proc_open(struct inode* inode, struct file* file) +{ + single_open(file, _irqinfo_proc_show, NULL); + return 0; +} + +struct file_operations irqinfo_proc_fops = { + .open = _irqinfo_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int _irqinfo_num_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%d\n", irqinfo_num); + return 0; +} + +static int _irqinfo_num_proc_open(struct inode* inode, struct file* file) +{ + single_open(file, _irqinfo_num_proc_show, NULL); + return 0; +} + +static int _irqinfo_num_proc_write(struct file *file, const char __user *buf, size_t len, loff_t *ppos) +{ + char irq_num_buf[10] = {0}; + int irq_num; + int err; + + if (len <= 0 || len >= 10) + return -EFAULT; + + if (copy_from_user(&irq_num_buf, buf, len)) + return -EFAULT; + + err = strict_strtol(strstrip(irq_num_buf), 0, &irq_num); + + if (err) + return -EINVAL; + + irqinfo_num = irq_num; + + return strlen(irq_num_buf); +} + +struct file_operations irqinfo_num_proc_fops = { + .open = _irqinfo_num_proc_open, + .read = seq_read, + .write = _irqinfo_num_proc_write, + .llseek = seq_lseek, + .release = single_release, +}; + +/* last regs info */ +extern struct sprd_debug_regs_access *sprd_debug_last_regs_access; +static int _last_regs_info_proc_show(struct seq_file *m, void *v) +{ + int i; + for (i = 0; i < NR_CPUS; i++) { + seq_printf(m, "cpu %d\n", i); + seq_printf(m, "vaddr:%p value:%p pc:%p time:%p status:%p\n", + sprd_debug_last_regs_access[i].vaddr, + sprd_debug_last_regs_access[i].value, + sprd_debug_last_regs_access[i].pc, + sprd_debug_last_regs_access[i].time, + sprd_debug_last_regs_access[i].status); + } +} + +static int _last_regs_info_proc_open(struct inode* inode, struct file* file) +{ + single_open(file, _last_regs_info_proc_show, NULL); + return 0; +} + +struct file_operations last_regs_info_proc_fops = { + .open = _last_regs_info_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + diff --git a/fs/proc/sprd_debug/proc_sprd_debug_frame.c b/fs/proc/sprd_debug/proc_sprd_debug_frame.c new file mode 100644 index 00000000..8551a4ac --- /dev/null +++ b/fs/proc/sprd_debug/proc_sprd_debug_frame.c @@ -0,0 +1,135 @@ +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/seq_file.h> +#include <linux/debugfs.h> +#include <linux/mm.h> +#include <linux/stat.h> + +enum sprd_debug_type{ + SCHE, + MEM, + COMM, + TIMER, + IRQ, + IO, + CPU, + MISC, + TYPE_COUNT +}; + +struct sprd_debug_dir { + char *name; + struct dentry *debugfs_entry; +}; + +struct sprd_debug_dir sprd_debug_dir_table[TYPE_COUNT] = { + {.name = "sche", .debugfs_entry = NULL}, + {.name = "mem", .debugfs_entry = NULL}, + {.name = "comm", .debugfs_entry = NULL}, + {.name = "timer", .debugfs_entry = NULL}, + {.name = "irq", .debugfs_entry = NULL}, + {.name = "io", .debugfs_entry = NULL}, + {.name = "cpu", .debugfs_entry = NULL}, + {.name = "misc", .debugfs_entry = NULL}, +}; + +struct sprd_debug_entry { + enum sprd_debug_type type; + char *name; + mode_t mode; + struct file_operations *fop; +}; + +#define NOD(TYPE, NAME, MODE, FOP) { \ + .type = TYPE, \ + .name = NAME, \ + .mode = MODE, \ + .fop = FOP} + +#ifdef CONFIG_SPRD_DEBUG_PHYMEM_INFO +extern struct file_operations phymem_dist_proc_fops; +extern struct file_operations phymem_pages_proc_fops; +extern struct file_operations phymem_map_proc_fops; +#endif + +#ifdef CONFIG_SPRD_DEBUG_USRPROCMEM_INFO +extern struct file_operations user_process_meminfo_fops; +#endif + +#ifdef CONFIG_SPRD_DEBUG_SCHED_LOG +extern struct file_operations scheinfo_proc_fops; +extern struct file_operations scheinfo_num_proc_fops; +extern struct file_operations irqinfo_proc_fops; +extern struct file_operations irqinfo_num_proc_fops; +extern struct file_operations last_regs_info_proc_fops; +extern struct file_operations userstack_info_proc_fops; +#endif + +#ifdef CONFIG_SPRD_DEBUG_CPU_RATE +extern struct file_operations proc_cpu_usage_fops; +#endif + +static struct sprd_debug_entry sprd_debug_array[] = { +#ifdef CONFIG_SPRD_DEBUG_PHYMEM_INFO + NOD(MEM, "phymemdist", 0, &phymem_dist_proc_fops), + NOD(MEM, "phymempages", 0, &phymem_pages_proc_fops), + NOD(MEM, "phymemmap", 0, &phymem_map_proc_fops), +#endif + +#ifdef CONFIG_SPRD_DEBUG_USRPROCMEM_INFO + NOD(MEM, "userprocmem", 0, &user_process_meminfo_fops), + NOD(MEM, "userthreadstack", 0, &userstack_info_proc_fops), +#endif + +#ifdef CONFIG_SPRD_DEBUG_SCHED_LOG + NOD(SCHE, "schedlog", 0444 , &scheinfo_proc_fops), + NOD(SCHE, "schedlog_num", 0, &scheinfo_num_proc_fops), + NOD(IRQ, "irqlog", 0, &irqinfo_proc_fops), + NOD(IRQ, "irqlog_num", 0, &irqinfo_num_proc_fops), + NOD(IO, "last_regs", 0, &last_regs_info_proc_fops), +#endif + +#ifdef CONFIG_SPRD_DEBUG_CPU_RATE + NOD(CPU, "cpu_usage", 0444, &proc_cpu_usage_fops), +#endif +}; + +static struct dentry *debugfs_sprd_debug_root; + +static void _sprd_entry_create(struct sprd_debug_entry *psprd_debug) +{ + if ((psprd_debug->name != NULL) && (psprd_debug->fop != NULL)) + debugfs_create_file(psprd_debug->name, psprd_debug->mode, + sprd_debug_dir_table[psprd_debug->type].debugfs_entry, NULL, psprd_debug->fop); +} + +static void _sprd_debug_init(void) +{ + int i; + int num = sizeof(sprd_debug_array) / sizeof(struct sprd_debug_entry); + + for (i = 0; i < num; i++) + _sprd_entry_create(&sprd_debug_array[i]); + +} + +static void _sprd_dir_init(void) +{ + int i; + + debugfs_sprd_debug_root = debugfs_create_dir("sprd_debug", NULL); + for (i = 0; i < TYPE_COUNT; i++) + sprd_debug_dir_table[i].debugfs_entry = debugfs_create_dir(sprd_debug_dir_table[i].name, debugfs_sprd_debug_root); +} + +static int _sprd_debug_module_init(void) +{ + _sprd_dir_init(); + _sprd_debug_init(); + return 0; +} + +module_init(_sprd_debug_module_init); + diff --git a/fs/proc/sprd_debug/proc_usermeminfo.c b/fs/proc/sprd_debug/proc_usermeminfo.c new file mode 100644 index 00000000..434cf4c0 --- /dev/null +++ b/fs/proc/sprd_debug/proc_usermeminfo.c @@ -0,0 +1,174 @@ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/pid.h> +#include <asm/uaccess.h> +#include <asm/current.h> +#include <linux/mm.h> + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("userspace process memory info device"); + +struct user_process_mem_info { + unsigned long vss; + unsigned long rss; + unsigned long rss_mapped; + unsigned long pss; + unsigned long uss; + int task_pid; +}; + +static int memoryinfo_flag = 0; + +static pte_t check_pte(struct mm_struct *mm, unsigned long address) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + pgd = pgd_offset(mm, address); + if (!pgd_present(*pgd)) + goto check_fail; + + pud = pud_offset(pgd, address); + if (!pud_present(*pud)) + goto check_fail; + + pmd = pmd_offset(pud, address); + if (!pmd_present(*pmd)) + goto check_fail; + if (pmd_trans_huge(*pmd)) + goto check_fail; + + pte = pte_offset_map(pmd, address); + if (!pte_present(*pte)) + goto check_fail; + + return *pte; + +check_fail: + return 0; +} + + +static struct page *check_pfn(pte_t pte) +{ + if (!pte_pfn(pte)) + return NULL; + else + return pfn_to_page(pte_pfn(pte)); +} + +int get_userprocess_meminfo(struct task_struct* p, struct user_process_mem_info *upmeminfo) +{ + struct vm_area_struct *vma = NULL; + if (p->mm == NULL) + return -1; + + upmeminfo->task_pid = p->pid; + for (vma = p->mm->mmap; vma; vma = vma->vm_next) { + unsigned long pagesize = PAGE_SIZE; + unsigned long step = vma->vm_start; + + for(; step < vma->vm_end; step += pagesize) { + pte_t pte; + int mapcount; + struct page *page; + + /*!!! VSS in procrank equals rss, and actually wrong !!!*/ + upmeminfo->vss += pagesize; /*vss*/ + pte = check_pte(p->mm, step); + if(!pte) continue; + + page = check_pfn(pte); + if(!page) continue; + + /*!!! RSS in procrank equals rss_mapped, and actually wrong !!!*/ + upmeminfo->rss += pagesize; /*rss*/ + mapcount = page_mapcount(page); + if(2 <= mapcount) { + upmeminfo->rss_mapped += pagesize; /*rss_mapped*/ + upmeminfo->pss += pagesize / mapcount; /*pss*/ + } else if (1 == mapcount) { + upmeminfo->rss_mapped += pagesize; /*rss_mapped*/ + upmeminfo->pss += pagesize; /*pss*/ + upmeminfo->uss += pagesize; /*uss*/ + } else if (0 == mapcount) { + /*this is the difference between rss & rss_mapped.*/ + } + } + } + + return 0; +} + +int user_process_meminfo_show(void) +{ + struct task_struct *p = NULL; + struct user_process_mem_info upmeminfo; + + printk(KERN_INFO "%5s %11s %9s %11s %9s %9s %s\n", + "pid", "vss", "rss", "rss_mapped", "pss", "uss", "name"); + + printk(KERN_INFO "-----------------------------------------------------------------\n"); + for_each_process(p) { + memset(&upmeminfo, 0, sizeof(upmeminfo)); + if (get_userprocess_meminfo(p, &upmeminfo)) + continue; + + printk(KERN_INFO "%5d %10luK %8luK %10luK %8luK %8luK %s\n", + upmeminfo.task_pid, + upmeminfo.vss >> 10, + upmeminfo.rss >> 10, + upmeminfo.rss_mapped >> 10, + upmeminfo.pss >> 10, + upmeminfo.uss >> 10, + p->comm); + } + + return 0; +} + +static int proc_user_process_meminfo_show(struct seq_file *m, void *v) +{ + struct task_struct *p = NULL; + struct user_process_mem_info upmeminfo; + + seq_printf(m, "%5s %11s %9s %11s %9s %9s %s\n", + "pid", "vss", "rss", "rss_mapped", "pss", "uss", "name"); + + seq_printf(m, "-----------------------------------------------------------------\n"); + for_each_process(p) { + memset(&upmeminfo, 0, sizeof(upmeminfo)); + if (get_userprocess_meminfo(p, &upmeminfo)) + continue; + + seq_printf(m, "%5d %10luK %8luK %10luK %8luK %8luK %s\n", + upmeminfo.task_pid, + upmeminfo.vss >> 10, + upmeminfo.rss >> 10, + upmeminfo.rss_mapped >> 10, + upmeminfo.pss >> 10, + upmeminfo.uss >> 10, + p->comm); + } + + return 0; +} + +static int proc_user_process_meminfo_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_user_process_meminfo_show, NULL); +} + +struct file_operations user_process_meminfo_fops = { + .open = proc_user_process_meminfo_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + diff --git a/fs/proc/sprd_debug/proc_userstack.c b/fs/proc/sprd_debug/proc_userstack.c new file mode 100644 index 00000000..ef055bb4 --- /dev/null +++ b/fs/proc/sprd_debug/proc_userstack.c @@ -0,0 +1,169 @@ +//#ifdef CONFIG_USERSTACKTRACEG
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/seq_file.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/nmi.h>
+#include <linux/quicklist.h>
+#include <asm/setup.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/cacheflush.h>
+#include <asm/ptrace.h>
+#include <asm/uaccess.h>
+#include <asm/cacheflush.h>
+#include <linux/kallsyms.h>
+#include <linux/ext2_fs.h>
+#include <linux/highmem.h>
+#define K(x) ((x) << (PAGE_SHIFT - 10))
+
+extern struct meminfo meminfo;
+static int show_user_stack(struct seq_file *m, void *v);
+//static struct proc_dir_entry* userstack_info;
+
+static int get_pfn_from_mm(unsigned long address, struct mm_struct *mm)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+ int ret = 0;
+
+ pgd = pgd_offset(mm, address);
+ if (!pgd_present(*pgd))
+ return ret;
+
+ pud = pud_offset(pgd, address);
+ if (!pud_present(*pud))
+ return ret;
+
+ pmd = pmd_offset(pud, address);
+ if (!pmd_present(*pmd))
+ return ret;
+
+ pte = pte_offset_map(pmd, address);
+
+ if (!pte_present(*pte))
+ return 0;
+
+ return pte_pfn(*pte);
+}
+
+static struct page *user_addr_to_page(unsigned long uaddr, struct task_struct *task)
+{
+ int pfn;
+ pfn = get_pfn_from_mm(uaddr, task->mm);
+
+ if (!pfn)
+ return NULL;
+
+ return pfn_to_page(pfn);
+}
+
+static int show_userstack_of_task(struct task_struct *task, struct seq_file *m, void *v)
+{
+ unsigned long val;
+ unsigned long p;
+ struct vm_area_struct *vma;
+ unsigned long usersp, kusersp;
+ unsigned long length = 32*10;
+ unsigned long *kaddr = NULL;
+ struct page *page = NULL;
+ pgoff_t pgoff;
+
+ if (!task->mm) {
+ seq_printf(m, "pid:%d, it's a kthread, no userstack!\n",task->pid);
+ return 0;
+ }
+
+ usersp = task_pt_regs(task)->uregs[13];
+
+ seq_printf(m, "userstack pid:%d sp:0x%p\n\n", task->pid, (void *)usersp);
+
+ page = user_addr_to_page(usersp, task);
+ if (!page) {
+ seq_printf(m, "Sorry, can't find the stack's page!\n");
+ return 0;
+ }
+
+ pgoff = usersp & 0x00000fff;
+ kaddr = kmap(page);
+ kusersp = (unsigned long)kaddr + pgoff;
+
+ for (p = kusersp; p < kusersp + length; p += 4) {
+ vma = NULL;
+ val = *(unsigned long *)p;
+ vma = find_vma(task->mm, val);
+ if (vma) {
+ struct file *file = vma->vm_file;
+ struct mm_struct *mm = vma->vm_mm;
+
+ if (val < vma->vm_start) {
+ seq_printf(m, "%4lx: %08lx \n", p, val);
+ continue;
+ }
+
+ if (file) {
+ char buf[50];
+ char *pp = NULL;
+
+ memset(buf, 0, 50);
+ pp = d_path(&file->f_path, buf, 50);
+ if (!IS_ERR(pp))
+ mangle_path(buf, pp, "\n");
+
+ seq_printf(m, "%4lx: %08lx %s 0x%p\n", p, val, buf, (void *)vma->vm_start);
+ } else {
+ const char *name = arch_vma_name(vma);
+ if (!name) {
+ if (vma->vm_start <= mm->start_brk && vma->vm_end >= mm->brk)
+ seq_printf(m, "%4lx: %08lx [heap]\n", p, val);
+ else if (vma->vm_start <= mm->start_stack && vma->vm_end >= mm->start_stack)
+ seq_printf(m, "%4lx: %08lx [stack]\n", p, val);
+ else
+ seq_printf(m, "%4lx: %08lx [vdso]\n", p, val);
+ } else
+ seq_printf(m, "%4lx: %08lx %s\n", p, val, name);
+ }
+ } else
+ seq_printf(m, "%4lx: %08lx \n", p, val);
+ }
+ seq_printf(m, "====================================================\n");
+ kunmap(page);
+ return 0;
+}
+
+static int show_user_stack(struct seq_file *m, void *v)
+{
+ struct task_struct *g, *p;
+ seq_printf(m, "=============enter ====show_user_stack===========\n");
+
+ do_each_thread(g, p) {
+ task_lock(p);
+ if (p->state == TASK_UNINTERRUPTIBLE)
+ show_userstack_of_task(p, m, v);
+ task_unlock(p);
+ }while_each_thread(g, p);
+
+ return 0;
+}
+
+static int userstack_proc_open(struct inode* inode, struct file* file)
+{
+ single_open(file, show_user_stack, NULL);
+ return 0;
+}
+
+
+struct file_operations userstack_info_proc_fops =
+{
+ .open = userstack_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
diff --git a/fs/proc/sprd_debug/sprd_uboot_log_dump.c b/fs/proc/sprd_debug/sprd_uboot_log_dump.c new file mode 100644 index 00000000..b195bdf7 --- /dev/null +++ b/fs/proc/sprd_debug/sprd_uboot_log_dump.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2015 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/memblock.h> +#include <linux/io.h> +#include <linux/proc_fs.h> + +#define PROC_DUMP_PARENT "bootloader" +#define PROC_DUMP "log_buf" + +static unsigned int log_start = ~0x1; +static unsigned int log_size = ~0x1; + +static int __init set_ram_range(char *str) +{ + + if (get_option(&str, &log_start) == 2) + get_option(&str, &log_size); + + return 1; +} + +__setup("boot_ram_log=", set_ram_range); + +static int log_buf_open(struct inode *inode, struct file *filp) +{ + void *base; + + /* determine the ram area is valid or not. */ + if (!valid_phys_addr_range(log_start, log_size) + || !memblock_is_region_reserved(log_start, log_size)) { + pr_err("%s: check memory range failed!\n", __func__); + + return -EINVAL; + } + + base = ioremap(log_start, log_size); + filp->private_data = base; + if (!base) { + pr_err("%s: remap error!\n", __func__); + return -ENOMEM; + } + + return 0; +} + +static int log_buf_release(struct inode *inode, struct file *filp) +{ + void *base = filp->private_data; + + if (base) + iounmap(base); + + return 0; +} + + +static ssize_t log_buf_read(struct file *filp, char __user *buffer, + size_t len, loff_t *offset) +{ + void *base = filp->private_data; + + if (!base) + return -ENOMEM; + + return simple_read_from_buffer(buffer, len, offset, base, log_size); +} + + +static int log_buf_mmap(struct file *filp, struct vm_area_struct *vma) +{ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + return vm_iomap_memory(vma, log_start, log_size); +} + + +static const struct file_operations log_buf_fops = { + .owner = THIS_MODULE, + .read = log_buf_read, + .open = log_buf_open, + .mmap = log_buf_mmap, + .release = log_buf_release, +}; + + +static int __init uboot_log_dump(void) +{ + int err; + struct proc_dir_entry *entry_parent, *entry_dump; + + entry_parent = proc_mkdir("bootloader", NULL); + if (!entry_parent) { + pr_err("u-boot log dump: create proc file failed\n"); + return -ENOMEM; + } + + /* other user is NOT permitted to dump buffer */ + entry_dump = proc_create_data(PROC_DUMP, S_IRUSR, entry_parent, + &log_buf_fops, NULL); + + err = entry_dump ? 0 : -ENOMEM; + + return err; +} + +module_init(uboot_log_dump); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Spreadtrum U-boot log dump support"); diff --git a/fs/proc/sprd_debug/usermeminfo.h b/fs/proc/sprd_debug/usermeminfo.h new file mode 100644 index 00000000..b3987978 --- /dev/null +++ b/fs/proc/sprd_debug/usermeminfo.h @@ -0,0 +1,4 @@ +#ifndef _USERMEM_INFO_H +#define _USERMEM_INOF_H +extern int user_process_meminfo_show(void); +#endif |
