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:
Huaibin Yang 2014-10-24 12:32:17 -07:00 committed by David Keitel
parent 6faada7714
commit 4ae0ab18f6
2 changed files with 179 additions and 80 deletions

View file

@ -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

View file

@ -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;
}