From 4ae0ab18f6311c9e4ba9d70e327d6ee603b8c0fe Mon Sep 17 00:00:00 2001 From: Huaibin Yang Date: Fri, 24 Oct 2014 12:32:17 -0700 Subject: [PATCH] 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 Signed-off-by: Ingrid Gallardo [cip@codeaurora.org: Use debugfs_create_u32 for xlog panic] Signed-off-by: Clarence Ip --- drivers/video/fbdev/msm/mdss_debug.h | 13 +- drivers/video/fbdev/msm/mdss_debug_xlog.c | 246 +++++++++++++++------- 2 files changed, 179 insertions(+), 80 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_debug.h b/drivers/video/fbdev/msm/mdss_debug.h index 08fa655f73c9..fe5eaf4d65bf 100644 --- a/drivers/video/fbdev/msm/mdss_debug.h +++ b/drivers/video/fbdev/msm/mdss_debug.h @@ -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 diff --git a/drivers/video/fbdev/msm/mdss_debug_xlog.c b/drivers/video/fbdev/msm/mdss_debug_xlog.c index 3f9e74a7a88c..c6e48e805c74 100644 --- a/drivers/video/fbdev/msm/mdss_debug_xlog.c +++ b/drivers/video/fbdev/msm/mdss_debug_xlog.c @@ -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 #include #include +#include #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; +} +