Skip to content

Instantly share code, notes, and snippets.

@invisiblek
Created January 27, 2015 03:02
Show Gist options
  • Save invisiblek/8675f6f9ed9f8ef2e218 to your computer and use it in GitHub Desktop.
Save invisiblek/8675f6f9ed9f8ef2e218 to your computer and use it in GitHub Desktop.
/*
* Copyright (C) 2010 OPPO, Inc.
* Author: Andy <[email protected]>
*
* 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/fs.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/time.h>
#include <asm/ioctls.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/stacktrace.h>
#include <linux/hugetlb.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/mmzone.h>
#include <linux/swap.h>
#include <linux/vmstat.h>
#include <asm/atomic.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/workqueue.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include "oppo_trace.h"
#define IOCTL_OTRACER_TEST (1<<0)
#define IOCTL_OTRACER_STACK (1<<1)
#define IOCTL_OTRACER_MEMINFO (1<<2)
#define IOCTL_OTRACER_TASKINFO (1<<3)
#define IOCTL_OTRACER_ALLINFO (1<<4)
#define IOCTL_OTRACER_TOLCD (1<<5)
#define IOCTL_OTRACER_PANIC (1<<12)
struct vmalloc_info {
unsigned long used;
unsigned long largest_chunk;
};
#ifdef CONFIG_MMU
#define VMALLOC_TOTAL (VMALLOC_END - VMALLOC_START)
extern void get_vmalloc_info(struct vmalloc_info *vmi);
#endif
/* OPPO 2012-10-11 chendx Delete begin for debugtools Todo */
#if 0
extern unsigned reboot_reason;
extern void *restart_reason;
#endif
/* OPPO 2012-10-11 chendx Delete end */
void backtrace_test_saved(void)
{
struct stack_trace trace;
unsigned long entries[8];
printk("\nThe following trace is a kernel self test and not a bug!\n");
trace.nr_entries = 0;
trace.max_entries = ARRAY_SIZE(entries);
trace.entries = entries;
trace.skip = 0;
printk("Testing a dump_stack.\n");
dump_stack();
//printk("Testing a save_stack_trace.\n");
//save_stack_trace(&trace);
printk("Testing a print_stack_trace.\n");
print_stack_trace(&trace, 0);
}
void tasks_mem_get(struct mm_struct *mm, unsigned long *vsize, unsigned long *vrss)
{
unsigned long hiwater_vm, total_vm, hiwater_rss, total_rss;
hiwater_vm = total_vm = mm->total_vm;
if (hiwater_vm < mm->hiwater_vm)
hiwater_vm = mm->hiwater_vm;
hiwater_rss = total_rss = get_mm_rss(mm);
if (hiwater_rss < mm->hiwater_rss)
hiwater_rss = mm->hiwater_rss;
*vsize = (total_vm - mm->reserved_vm) << (PAGE_SHIFT-10);
*vrss = total_rss << (PAGE_SHIFT-10);
}
static const char *task_state_array[] = {
"R-0", /* 0 (running) */
"S-1", /* 1 (sleeping) */
"D-2", /* 2 (disk sleep) */
"T-4", /* 4 (stopped) */
"T-8", /* 8 (tracing stop) */
"Z-F", /* 16 (zombie) */
"X-" /* 32 (dead) */
};
static inline const char *get_task_state(struct task_struct *tsk)
{
unsigned int state = (tsk->state & TASK_REPORT) | tsk->exit_state;
const char **p = &task_state_array[0];
while (state) {
p++;
state >>= 1;
}
return *p;
}
void tasks_test_saved(void)
{
struct task_struct *p;
struct cred *cred =NULL;
struct mm_struct *mm;
unsigned long vsize = 0, vrss = 0;
printk("\nThe following trace is a kernel tasks test and not a bug!\n");
printk("USER\tPID\tVSIZE\tRSS\tSTATE\tNAME\n");
write_lock_irq(&tasklist_lock);
for_each_process(p) {
cred = (struct cred *)get_cred((struct cred *) __task_cred(p));
vsize = 0;
vrss = 0;
mm = get_task_mm(p);
if (mm) {
tasks_mem_get(mm, &vsize, &vrss);
}
printk("%d\t%d\t%ld\t%ld\t%s\t%s\n",
cred->uid,
task_pid_nr(p),
vsize,
vrss,
get_task_state(p),
p->comm);
}
write_unlock_irq(&tasklist_lock);
}
void meminfo_test_saved(void)
{
struct sysinfo i;
unsigned long committed;
unsigned long allowed;
struct vmalloc_info vmi;
long cached;
unsigned long pages[NR_LRU_LISTS];
int lru;
printk("\nThe following trace is a meminfo test and not a bug!\n");
/*
* display in kilobytes.
*/
#define K(x) ((x) << (PAGE_SHIFT - 10))
si_meminfo(&i);
si_swapinfo(&i);
/* OPPO 2010-11-18 Laijl Modify begin for msm platform */
#if 0
committed = atomic_long_read(&vm_committed_space);
#else
committed = percpu_counter_read_positive(&vm_committed_as);
#endif
/* OPPO 2010-11-18 Laijl Modify end */
allowed = ((totalram_pages - hugetlb_total_pages())
* sysctl_overcommit_ratio / 100) + total_swap_pages;
cached = global_page_state(NR_FILE_PAGES) -
total_swapcache_pages - i.bufferram;
if (cached < 0)
cached = 0;
get_vmalloc_info(&vmi);
for (lru = LRU_BASE; lru < NR_LRU_LISTS; lru++)
pages[lru] = global_page_state(NR_LRU_BASE + lru);
/*
* Tagged format, for easy grepping and expansion.
*/
printk(
"MemTotal: %8lu kB\n"
"MemFree: %8lu kB\n"
"Buffers: %8lu kB\n"
"Cached: %8lu kB\n"
"SwapCached: %8lu kB\n"
"Active: %8lu kB\n"
"Inactive: %8lu kB\n"
"Active(anon): %8lu kB\n"
"Inactive(anon): %8lu kB\n"
"Active(file): %8lu kB\n"
"Inactive(file): %8lu kB\n"
"Unevictable: %8lu kB\n"
"Mlocked: %8lu kB\n"
#ifdef CONFIG_HIGHMEM
"HighTotal: %8lu kB\n"
"HighFree: %8lu kB\n"
"LowTotal: %8lu kB\n"
"LowFree: %8lu kB\n"
#endif
#ifndef CONFIG_MMU
"MmapCopy: %8lu kB\n"
#endif
"SwapTotal: %8lu kB\n"
"SwapFree: %8lu kB\n"
"Dirty: %8lu kB\n"
"Writeback: %8lu kB\n"
"AnonPages: %8lu kB\n"
"Mapped: %8lu kB\n"
"Shmem: %8lu kB\n"
"Slab: %8lu kB\n"
"SReclaimable: %8lu kB\n"
"SUnreclaim: %8lu kB\n"
"KernelStack: %8lu kB\n"
"PageTables: %8lu kB\n"
#ifdef CONFIG_QUICKLIST
"Quicklists: %8lu kB\n"
#endif
"NFS_Unstable: %8lu kB\n"
"Bounce: %8lu kB\n"
"WritebackTmp: %8lu kB\n"
"CommitLimit: %8lu kB\n"
"Committed_AS: %8lu kB\n"
"VmallocTotal: %8lu kB\n"
"VmallocUsed: %8lu kB\n"
"VmallocChunk: %8lu kB\n"
#ifdef CONFIG_MEMORY_FAILURE
"HardwareCorrupted: %5lu kB\n"
#endif
,
K(i.totalram),
K(i.freeram),
K(i.bufferram),
K(cached),
K(total_swapcache_pages),
K(pages[LRU_ACTIVE_ANON] + pages[LRU_ACTIVE_FILE]),
K(pages[LRU_INACTIVE_ANON] + pages[LRU_INACTIVE_FILE]),
K(pages[LRU_ACTIVE_ANON]),
K(pages[LRU_INACTIVE_ANON]),
K(pages[LRU_ACTIVE_FILE]),
K(pages[LRU_INACTIVE_FILE]),
K(pages[LRU_UNEVICTABLE]),
K(global_page_state(NR_MLOCK)),
#ifdef CONFIG_HIGHMEM
K(i.totalhigh),
K(i.freehigh),
K(i.totalram-i.totalhigh),
K(i.freeram-i.freehigh),
#endif
#ifndef CONFIG_MMU
K((unsigned long) atomic_long_read(&mmap_pages_allocated)),
#endif
K(i.totalswap),
K(i.freeswap),
K(global_page_state(NR_FILE_DIRTY)),
K(global_page_state(NR_WRITEBACK)),
K(global_page_state(NR_ANON_PAGES)),
K(global_page_state(NR_FILE_MAPPED)),
K(global_page_state(NR_SHMEM)),
K(global_page_state(NR_SLAB_RECLAIMABLE) +
global_page_state(NR_SLAB_UNRECLAIMABLE)),
K(global_page_state(NR_SLAB_RECLAIMABLE)),
K(global_page_state(NR_SLAB_UNRECLAIMABLE)),
global_page_state(NR_KERNEL_STACK) * THREAD_SIZE / 1024,
K(global_page_state(NR_PAGETABLE)),
#ifdef CONFIG_QUICKLIST
K(quicklist_total_size()),
#endif
K(global_page_state(NR_UNSTABLE_NFS)),
K(global_page_state(NR_BOUNCE)),
K(global_page_state(NR_WRITEBACK_TEMP)),
K(allowed),
K(committed),
(unsigned long)VMALLOC_TOTAL >> 10,
vmi.used >> 10,
vmi.largest_chunk >> 10
#ifdef CONFIG_MEMORY_FAILURE
,atomic_long_read(&mce_bad_pages) << (PAGE_SHIFT - 10)
#endif
);
#undef K
}
#if 0
extern int oppo_con_write(const unsigned char *buf, int count);
extern void console_activate(void);
#else
int oppo_con_write(const unsigned char *buf, int count)
{
return 0;
}
void console_activate(void)
{
return;
}
#endif
static ssize_t otracer_read(struct file *filp, char __user *buf,
size_t size, loff_t *offp)
{
printk(KERN_INFO "otracer_read: initialized\n");
return 0;
}
/* OPPO 2010-11-25 Laijl Modify begin for reason buf overflaot*/
#if 0
static ssize_t otracer_write(struct file *filp, const char __user *buf,
size_t count, loff_t *offp)
{
printk(KERN_INFO "otracer_write: initialized\n");
if (!count)
return -EIO;
console_activate();
printk( "otracer_write console_activate pass\n");
oppo_con_write(buf, count);
printk( "otracer_write oppo_con_write pass\n");
return count;
}
#else
/* OPPO 2011-03-02 huanggd Add begin for oppo logo display */
extern int fbcon_takeover_global(int show_logo) ;
/* OPPO 2011-03-02 huanggd Add end */
/* OPPO 2013-01-30 zhenwx Add begin for modem fatal error enter ramdump */
#ifdef CONFIG_MODEM_ERR_ENTER_RAMDUMP
bool otrace_on = false;
#else
static bool otrace_on = false;
#endif
/* OPPO 2013-01-30 zhenwx Modify end */
bool is_otrace_on(void)
{
return otrace_on;
}
static ssize_t otracer_write(struct file *filp, const char __user *buf,
size_t count, loff_t *offp)
{
char *kbuf = NULL;
if(!is_otrace_on())
return count;
printk(KERN_INFO "otracer_write \n");
kbuf = kzalloc( PAGE_SIZE, GFP_KERNEL );
if(kbuf == NULL)
{
goto end;
}
if (!count)
{
goto free_buf;
}
if( copy_from_user( kbuf, buf, ((count > PAGE_SIZE) ? PAGE_SIZE : count )) )
{
goto free_buf;
}
oppo_con_write(kbuf, ((count > PAGE_SIZE) ? PAGE_SIZE : count ));
kfree(kbuf);
return count;
free_buf:
kfree(kbuf);
end:
printk( KERN_ERR "otracer_write fail! \n");
return ( -EFAULT);
}
#endif
/* OPPO 2010-11-25 Laijl Modify end */
static long otracer_ioctl(struct file * filp,
unsigned int cmd, unsigned long arg)
{
unsigned int cmdv = cmd;
int i;
cmdv = cmd;
printk(KERN_INFO "otracer_ioctl: initialized. cmd=0x%x\n", cmd);
/* mwalker give a chance to change reboot result to android for android framework. */
if (cmd == IOCTL_TRACE_UPDATE_REBOOTFLAG) {
printk (KERN_INFO "android update reboot flag\n");
/* OPPO 2012-10-11 chendx Delete begin for debug tools Todo */
//#if 0
reboot_reason = 0x77665502;
writel(0x77665502, restart_reason);
//#endif
/* OPPO 2012-10-11 chendx Delete end */
goto end;
}
for(i = 0; i < 16; i++)
{
if(cmdv == 0)
break;
if(cmdv & IOCTL_OTRACER_TOLCD)
{
//console_activate();
cmdv &= (~IOCTL_OTRACER_TOLCD);
}
if(cmdv & IOCTL_OTRACER_STACK)
{
backtrace_test_saved();
cmdv &= (~IOCTL_OTRACER_STACK);
}
if(cmdv & IOCTL_OTRACER_MEMINFO)
{
meminfo_test_saved();
cmdv &= (~IOCTL_OTRACER_MEMINFO);
}
if(cmdv & IOCTL_OTRACER_TASKINFO)
{
tasks_test_saved();
cmdv &= (~IOCTL_OTRACER_TASKINFO);
}
if(cmdv & IOCTL_OTRACER_ALLINFO)
{
backtrace_test_saved();
meminfo_test_saved();
tasks_test_saved();
cmdv &= (~IOCTL_OTRACER_ALLINFO);
}
if(cmdv & IOCTL_OTRACER_PANIC)
{
pr_info("ioctl panic reboot\n");
panic("android");
cmdv &= (~IOCTL_OTRACER_PANIC);
}
}
end:
return 0;
}
static int otracer_open(struct inode *inode, struct file *file)
{
pr_info("%s\n", __func__);
return nonseekable_open(inode, file);
}
static int otracer_close(struct inode *inode, struct file *file)
{
pr_info("%s\n", __func__);
return 0;
}
static struct file_operations otracer_fops = {
.owner = THIS_MODULE,
.open = otracer_open,
.release = otracer_close,
.read = otracer_read,
.write = otracer_write,
.unlocked_ioctl = otracer_ioctl,
};
static struct miscdevice otracer_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "otracer",
.fops = &otracer_fops,
};
static int otrace_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "\notrace_on:%d\n", is_otrace_on());
return 0;
}
static int otrace_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, otrace_proc_show, NULL);
}
static ssize_t otrace_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *pos)
{
unsigned char *lbuf;
size_t local_count;
unsigned long val;
if (count <= 0)
return 0;
#define LBUFSIZE 1200UL
lbuf = kmalloc(LBUFSIZE, GFP_KERNEL);
if (!lbuf)
return 0;
local_count = (LBUFSIZE - 1)>count?count:(LBUFSIZE - 1);
if (copy_from_user(lbuf, buffer, local_count) != 0) {
kfree(lbuf);
return -EFAULT;
}
lbuf[local_count] = '\0';
val = simple_strtoul(lbuf, NULL, 10);
if(val == 7978)
otrace_on = true;
else
otrace_on = false;
pr_info("val:%ld, otrace_on:%d\n", val, otrace_on);
kfree(lbuf);
return count;
}
static const struct file_operations otrace_proc_fops = {
.owner = THIS_MODULE,
.open = otrace_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = otrace_proc_write,
};
static struct proc_dir_entry *otrace_entry;
static int __init otracer_init(void)
{
int ret;
ret = misc_register(&otracer_misc);
if (unlikely(ret)) {
printk(KERN_ERR "otracer: failed to register misc device!\n");
return ret;
}
/* Set up the proc file system */
otrace_entry = proc_create("otrace_on", 0666, NULL, &otrace_proc_fops);
if (!otrace_entry) {
ret = -ENOMEM;
goto out_misc;
}
printk(KERN_INFO "otracer: initialized\n");
return 0;
out_misc:
misc_deregister(&otracer_misc);
return ret;
}
static void __exit otracer_exit(void)
{
int ret;
remove_proc_entry("otrace_on", NULL);
ret = misc_deregister(&otracer_misc);
if (unlikely(ret))
printk(KERN_ERR "otracer: failed to unregister misc device!\n");
printk(KERN_INFO "otracer: exit\n");
}
module_init(otracer_init);
module_exit(otracer_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("oppo tracer");
MODULE_AUTHOR("Andy <[email protected]>");
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment