msm: mdss: debugfs: xlog: refactor xlog dump
Make overall dump facility more reliable and more useable for debugging: write xlog info to stdout; only un-logged and un-dumped entries will be dumped out each dump; add func line number, pid and time stamp difference for each entry; add index for each entry dump. Format of the log is as follows: name:line=>[entry:timestamp:delta][pid]:params Where: name: Name of the caller function line: Line in the source file entry: Index in the circular buffer timestamp: time when log was taken delta: time difference with previous log pid: process id params: parameters used to log information (these are specific to each log) Change-Id: I32853ef7e27cc8ac34775ff492d60c801df61dba Signed-off-by: Huaibin Yang <huaibiny@codeaurora.org> Signed-off-by: Ingrid Gallardo <ingridg@codeaurora.org> [cip@codeaurora.org: Use debugfs_create_u32 for xlog panic] Signed-off-by: Clarence Ip <cip@codeaurora.org>
This commit is contained in:
parent
6faada7714
commit
4ae0ab18f6
2 changed files with 179 additions and 80 deletions
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
@ -41,14 +41,14 @@ enum mdss_dbg_xlog_flag {
|
|||
MDSS_XLOG_ALL = BIT(7),
|
||||
};
|
||||
|
||||
#define MDSS_XLOG(...) mdss_xlog(__func__, MDSS_XLOG_DEFAULT, ##__VA_ARGS__, \
|
||||
DATA_LIMITER)
|
||||
#define MDSS_XLOG(...) mdss_xlog(__func__, __LINE__, MDSS_XLOG_DEFAULT, \
|
||||
##__VA_ARGS__, DATA_LIMITER)
|
||||
|
||||
#define MDSS_XLOG_TOUT_HANDLER(...) \
|
||||
mdss_xlog_tout_handler_default(__func__, ##__VA_ARGS__, \
|
||||
XLOG_TOUT_DATA_LIMITER)
|
||||
|
||||
#define MDSS_XLOG_ALL(...) mdss_xlog(__func__, MDSS_XLOG_ALL, \
|
||||
#define MDSS_XLOG_ALL(...) mdss_xlog(__func__, __LINE__, MDSS_XLOG_ALL, \
|
||||
##__VA_ARGS__, DATA_LIMITER)
|
||||
|
||||
|
||||
|
@ -103,7 +103,7 @@ int mdss_misr_get(struct mdss_data_type *mdata, struct mdp_misr *resp,
|
|||
void mdss_misr_crc_collect(struct mdss_data_type *mdata, int block_id);
|
||||
|
||||
int mdss_create_xlog_debug(struct mdss_debug_data *mdd);
|
||||
void mdss_xlog(const char *name, int flag, ...);
|
||||
void mdss_xlog(const char *name, int line, int flag, ...);
|
||||
void mdss_dump_reg(struct mdss_debug_base *dbg, u32 reg_dump_flag);
|
||||
void mdss_xlog_tout_handler_default(const char *name, ...);
|
||||
#else
|
||||
|
@ -126,11 +126,10 @@ static inline void mdss_misr_crc_collect(struct mdss_data_type *mdata,
|
|||
int block_id) { }
|
||||
|
||||
static inline int create_xlog_debug(struct mdss_data_type *mdata) { return 0; }
|
||||
static inline void mdss_xlog(const char *name, ...) { }
|
||||
static inline void mdss_xlog_dump(void) { }
|
||||
static inline void mdss_dump_reg(struct mdss_debug_base *dbg,
|
||||
u32 reg_dump_flag) { }
|
||||
static inline void mdss_xlog(const char *name, int flag...) { }
|
||||
static inline void mdss_xlog(const char *name, int line, int flag...) { }
|
||||
static inline void mdss_dsi_debug_check_te(struct mdss_panel_data *pdata) { }
|
||||
static inline void mdss_xlog_tout_handler_default(const char *name, ...) { }
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
@ -15,6 +15,7 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "mdss.h"
|
||||
#include "mdss_mdp.h"
|
||||
|
@ -23,18 +24,21 @@
|
|||
#define MDSS_XLOG_ENTRY 256
|
||||
#define MDSS_XLOG_MAX_DATA 6
|
||||
#define MDSS_XLOG_BUF_MAX 512
|
||||
#define MDSS_XLOG_BUF_ALIGN 32
|
||||
|
||||
struct tlog {
|
||||
u64 tick;
|
||||
u32 tick;
|
||||
const char *name;
|
||||
int line;
|
||||
u32 data[MDSS_XLOG_MAX_DATA];
|
||||
u32 data_cnt;
|
||||
int pid;
|
||||
};
|
||||
|
||||
struct mdss_dbg_xlog {
|
||||
struct tlog logs[MDSS_XLOG_ENTRY];
|
||||
int first;
|
||||
int last;
|
||||
u32 first;
|
||||
u32 last;
|
||||
spinlock_t xlock;
|
||||
struct dentry *xlog;
|
||||
u32 xlog_enable;
|
||||
|
@ -42,55 +46,13 @@ struct mdss_dbg_xlog {
|
|||
u32 enable_reg_dump;
|
||||
} mdss_dbg_xlog;
|
||||
|
||||
static int mdss_xlog_dump_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
/* non-seekable */
|
||||
file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
|
||||
file->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t mdss_xlog_dump_read(struct file *file, char __user *buff,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
MDSS_XLOG_TOUT_HANDLER("mdp", "dsi0", "dsi1", "edp", "hdmi", "panic");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations mdss_xlog_fops = {
|
||||
.open = mdss_xlog_dump_open,
|
||||
.read = mdss_xlog_dump_read,
|
||||
};
|
||||
|
||||
int mdss_create_xlog_debug(struct mdss_debug_data *mdd)
|
||||
{
|
||||
spin_lock_init(&mdss_dbg_xlog.xlock);
|
||||
|
||||
mdss_dbg_xlog.xlog = debugfs_create_dir("xlog", mdd->root);
|
||||
if (IS_ERR_OR_NULL(mdss_dbg_xlog.xlog)) {
|
||||
pr_err("debugfs_create_dir fail, error %ld\n",
|
||||
PTR_ERR(mdss_dbg_xlog.xlog));
|
||||
mdss_dbg_xlog.xlog = NULL;
|
||||
return -ENODEV;
|
||||
}
|
||||
debugfs_create_file("dump", 0644, mdss_dbg_xlog.xlog, NULL,
|
||||
&mdss_xlog_fops);
|
||||
debugfs_create_u32("enable", 0644, mdss_dbg_xlog.xlog,
|
||||
&mdss_dbg_xlog.xlog_enable);
|
||||
debugfs_create_bool("panic", 0644, mdss_dbg_xlog.xlog,
|
||||
&mdss_dbg_xlog.panic_on_err);
|
||||
debugfs_create_u32("reg_dump", 0644, mdss_dbg_xlog.xlog,
|
||||
&mdss_dbg_xlog.enable_reg_dump);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool mdss_xlog_is_enabled(u32 flag)
|
||||
{
|
||||
return (flag & mdss_dbg_xlog.xlog_enable) ||
|
||||
(flag == MDSS_XLOG_ALL && mdss_dbg_xlog.xlog_enable);
|
||||
}
|
||||
|
||||
void mdss_xlog(const char *name, int flag, ...)
|
||||
void mdss_xlog(const char *name, int line, int flag, ...)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i, val = 0;
|
||||
|
@ -105,10 +67,12 @@ void mdss_xlog(const char *name, int flag, ...)
|
|||
|
||||
time = ktime_get();
|
||||
|
||||
log = &mdss_dbg_xlog.logs[mdss_dbg_xlog.first];
|
||||
log = &mdss_dbg_xlog.logs[mdss_dbg_xlog.last % MDSS_XLOG_ENTRY];
|
||||
log->tick = local_clock();
|
||||
log->name = name;
|
||||
log->line = line;
|
||||
log->data_cnt = 0;
|
||||
log->pid = current->pid;
|
||||
|
||||
va_start(args, flag);
|
||||
for (i = 0; i < MDSS_XLOG_MAX_DATA; i++) {
|
||||
|
@ -120,43 +84,97 @@ void mdss_xlog(const char *name, int flag, ...)
|
|||
log->data[i] = val;
|
||||
}
|
||||
va_end(args);
|
||||
|
||||
log->data_cnt = i;
|
||||
|
||||
mdss_dbg_xlog.last = mdss_dbg_xlog.first;
|
||||
mdss_dbg_xlog.first++;
|
||||
mdss_dbg_xlog.first %= MDSS_XLOG_ENTRY;
|
||||
mdss_dbg_xlog.last++;
|
||||
|
||||
spin_unlock_irqrestore(&mdss_dbg_xlog.xlock, flags);
|
||||
}
|
||||
|
||||
static void mdss_xlog_dump(void)
|
||||
/* always dump the last entries which are not dumped yet */
|
||||
static bool __mdss_xlog_dump_calc_range(void)
|
||||
{
|
||||
int i, n, d_cnt, off;
|
||||
static u32 next;
|
||||
bool need_dump = true;
|
||||
unsigned long flags;
|
||||
unsigned long rem_nsec;
|
||||
struct tlog *log;
|
||||
char xlog_buf[MDSS_XLOG_BUF_MAX];
|
||||
struct mdss_dbg_xlog *xlog = &mdss_dbg_xlog;
|
||||
|
||||
spin_lock_irqsave(&mdss_dbg_xlog.xlock, flags);
|
||||
i = mdss_dbg_xlog.first;
|
||||
for (n = 0; n < MDSS_XLOG_ENTRY; n++) {
|
||||
log = &mdss_dbg_xlog.logs[i];
|
||||
rem_nsec = do_div(log->tick, 1000000000);
|
||||
off = snprintf(xlog_buf, MDSS_XLOG_BUF_MAX,
|
||||
"%-32s => [%5llu.%06lu]: ", log->name,
|
||||
log->tick, rem_nsec / 1000);
|
||||
for (d_cnt = 0; d_cnt < log->data_cnt;) {
|
||||
off += snprintf((xlog_buf + off),
|
||||
(MDSS_XLOG_BUF_MAX - off),
|
||||
"%x ", log->data[d_cnt]);
|
||||
d_cnt++;
|
||||
}
|
||||
pr_err("%s\n", xlog_buf);
|
||||
|
||||
i = (i + 1) % MDSS_XLOG_ENTRY;
|
||||
xlog->first = next;
|
||||
|
||||
if (xlog->last == xlog->first) {
|
||||
need_dump = false;
|
||||
goto dump_exit;
|
||||
}
|
||||
|
||||
if (xlog->last < xlog->first) {
|
||||
xlog->first %= MDSS_XLOG_ENTRY;
|
||||
if (xlog->last < xlog->first)
|
||||
xlog->last += MDSS_XLOG_ENTRY;
|
||||
}
|
||||
|
||||
if ((xlog->last - xlog->first) > MDSS_XLOG_ENTRY) {
|
||||
pr_warn("xlog buffer overflow before dump: %d\n",
|
||||
xlog->last - xlog->first);
|
||||
xlog->first = xlog->last - MDSS_XLOG_ENTRY;
|
||||
}
|
||||
need_dump = true;
|
||||
next = xlog->first + 1;
|
||||
|
||||
dump_exit:
|
||||
spin_unlock_irqrestore(&mdss_dbg_xlog.xlock, flags);
|
||||
|
||||
return need_dump;
|
||||
}
|
||||
|
||||
static ssize_t mdss_xlog_dump_entry(char *xlog_buf, ssize_t xlog_buf_size)
|
||||
{
|
||||
int i;
|
||||
ssize_t off = 0;
|
||||
struct tlog *log, *prev_log;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mdss_dbg_xlog.xlock, flags);
|
||||
|
||||
log = &mdss_dbg_xlog.logs[mdss_dbg_xlog.first %
|
||||
MDSS_XLOG_ENTRY];
|
||||
|
||||
prev_log = &mdss_dbg_xlog.logs[(mdss_dbg_xlog.first - 1) %
|
||||
MDSS_XLOG_ENTRY];
|
||||
|
||||
off = snprintf((xlog_buf + off), (xlog_buf_size - off), "%s:%-4d",
|
||||
log->name, log->line);
|
||||
|
||||
if (off < MDSS_XLOG_BUF_ALIGN) {
|
||||
memset((xlog_buf + off), 0x20, (MDSS_XLOG_BUF_ALIGN - off));
|
||||
off = MDSS_XLOG_BUF_ALIGN;
|
||||
}
|
||||
|
||||
off += snprintf((xlog_buf + off), (xlog_buf_size - off),
|
||||
"=>[%-8d:%-11u:%9u][%-4d]:", mdss_dbg_xlog.first,
|
||||
log->tick / 1000, (log->tick - prev_log->tick) / 1000,
|
||||
log->pid);
|
||||
|
||||
for (i = 0; i < log->data_cnt; i++)
|
||||
off += snprintf((xlog_buf + off), (xlog_buf_size - off),
|
||||
"%x ", log->data[i]);
|
||||
|
||||
off += snprintf((xlog_buf + off), (xlog_buf_size - off), "\n");
|
||||
|
||||
spin_unlock_irqrestore(&mdss_dbg_xlog.xlock, flags);
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
static void mdss_xlog_dump_all(void)
|
||||
{
|
||||
char xlog_buf[MDSS_XLOG_BUF_MAX];
|
||||
|
||||
while (__mdss_xlog_dump_calc_range()) {
|
||||
mdss_xlog_dump_entry(xlog_buf, MDSS_XLOG_BUF_MAX);
|
||||
pr_info("%s", xlog_buf);
|
||||
}
|
||||
}
|
||||
|
||||
static void mdss_dump_reg_by_blk(const char *blk_name)
|
||||
|
@ -176,6 +194,22 @@ static void mdss_dump_reg_by_blk(const char *blk_name)
|
|||
}
|
||||
}
|
||||
|
||||
static void mdss_dump_reg_all(void)
|
||||
{
|
||||
struct mdss_data_type *mdata = mdss_mdp_get_mdata();
|
||||
struct mdss_debug_data *mdd = mdata->debug_inf.debug_data;
|
||||
struct mdss_debug_base *blk_base, *tmp;
|
||||
|
||||
if (!mdd)
|
||||
return;
|
||||
|
||||
list_for_each_entry_safe(blk_base, tmp, &mdd->base_list, head) {
|
||||
if (blk_base->name)
|
||||
mdss_dump_reg(blk_base,
|
||||
mdss_dbg_xlog.enable_reg_dump);
|
||||
}
|
||||
}
|
||||
|
||||
void mdss_xlog_tout_handler_default(const char *name, ...)
|
||||
{
|
||||
int i, dead = 0;
|
||||
|
@ -198,8 +232,74 @@ void mdss_xlog_tout_handler_default(const char *name, ...)
|
|||
}
|
||||
va_end(args);
|
||||
|
||||
mdss_xlog_dump();
|
||||
mdss_xlog_dump_all();
|
||||
|
||||
if (dead && mdss_dbg_xlog.panic_on_err)
|
||||
panic(name);
|
||||
}
|
||||
static int mdss_xlog_dump_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
/* non-seekable */
|
||||
file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
|
||||
file->private_data = inode->i_private;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t mdss_xlog_dump_read(struct file *file, char __user *buff,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
ssize_t len = 0;
|
||||
char xlog_buf[MDSS_XLOG_BUF_MAX];
|
||||
|
||||
if (__mdss_xlog_dump_calc_range()) {
|
||||
len = mdss_xlog_dump_entry(xlog_buf, MDSS_XLOG_BUF_MAX);
|
||||
if (copy_to_user(buff, xlog_buf, len))
|
||||
return -EFAULT;
|
||||
*ppos += len;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t mdss_xlog_dump_write(struct file *file,
|
||||
const char __user *user_buf, size_t count, loff_t *ppos)
|
||||
{
|
||||
mdss_dump_reg_all();
|
||||
|
||||
mdss_xlog_dump_all();
|
||||
|
||||
if (mdss_dbg_xlog.panic_on_err)
|
||||
panic("mdss");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations mdss_xlog_fops = {
|
||||
.open = mdss_xlog_dump_open,
|
||||
.read = mdss_xlog_dump_read,
|
||||
.write = mdss_xlog_dump_write,
|
||||
};
|
||||
|
||||
int mdss_create_xlog_debug(struct mdss_debug_data *mdd)
|
||||
{
|
||||
spin_lock_init(&mdss_dbg_xlog.xlock);
|
||||
|
||||
mdss_dbg_xlog.xlog = debugfs_create_dir("xlog", mdd->root);
|
||||
if (IS_ERR_OR_NULL(mdss_dbg_xlog.xlog)) {
|
||||
pr_err("debugfs_create_dir fail, error %ld\n",
|
||||
PTR_ERR(mdss_dbg_xlog.xlog));
|
||||
mdss_dbg_xlog.xlog = NULL;
|
||||
return -ENODEV;
|
||||
}
|
||||
debugfs_create_file("dump", 0644, mdss_dbg_xlog.xlog, NULL,
|
||||
&mdss_xlog_fops);
|
||||
debugfs_create_u32("enable", 0644, mdss_dbg_xlog.xlog,
|
||||
&mdss_dbg_xlog.xlog_enable);
|
||||
debugfs_create_u32("panic", 0644, mdss_dbg_xlog.xlog,
|
||||
&mdss_dbg_xlog.panic_on_err);
|
||||
debugfs_create_u32("reg_dump", 0644, mdss_dbg_xlog.xlog,
|
||||
&mdss_dbg_xlog.enable_reg_dump);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue