Skip to content

Commit 8a06290

Browse files
tzanussirostedt
authored andcommitted
tracing: Add tracing error log
Introduce a new ftrace file, tracing/error_log, for ftrace commands to log errors. This is useful for allowing more complex commands such as hist trigger and kprobe_event commands to point out specifically where something may have gone wrong without forcing them to resort to more ad hoc methods such as tacking error messages onto existing output files. To log a tracing error, call the event_log_err() function, passing it a location string describing where it came from e.g. kprobe_events or system:event, the command that caused the error, an array of static error strings describing errors and an index within that array which describes the specific error, along with the position to place the error caret. Reading the log displays the last (currently) 8 errors logged in the following format: [timestamp] <loc>: error: <static error text> Command: <command that caused the error> ^ Memory for the error log isn't allocated unless there has been a trace event error, and the error log can be cleared and have its memory freed by writing the empty string in truncation mode to it: # echo > tracing/error_log. Link: http://lkml.kernel.org/r/0c2c82571fd38c5f3a88ca823627edff250e9416.1554072478.git.tom.zanussi@linux.intel.com Acked-by: Masami Hiramatsu <[email protected]> Suggested-by: Masami Hiramatsu <[email protected]> Improvements-suggested-by: Steve Rostedt <[email protected]> Acked-by: Namhyung Kim <[email protected]> Signed-off-by: Tom Zanussi <[email protected]> Signed-off-by: Steven Rostedt (VMware) <[email protected]>
1 parent f45d122 commit 8a06290

File tree

2 files changed

+222
-0
lines changed

2 files changed

+222
-0
lines changed

kernel/trace/trace.c

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6878,6 +6878,221 @@ static const struct file_operations snapshot_raw_fops = {
68786878

68796879
#endif /* CONFIG_TRACER_SNAPSHOT */
68806880

6881+
#define TRACING_LOG_ERRS_MAX 8
6882+
#define TRACING_LOG_LOC_MAX 128
6883+
6884+
#define CMD_PREFIX " Command: "
6885+
6886+
struct err_info {
6887+
const char **errs; /* ptr to loc-specific array of err strings */
6888+
u8 type; /* index into errs -> specific err string */
6889+
u8 pos; /* MAX_FILTER_STR_VAL = 256 */
6890+
u64 ts;
6891+
};
6892+
6893+
struct tracing_log_err {
6894+
struct list_head list;
6895+
struct err_info info;
6896+
char loc[TRACING_LOG_LOC_MAX]; /* err location */
6897+
char cmd[MAX_FILTER_STR_VAL]; /* what caused err */
6898+
};
6899+
6900+
static LIST_HEAD(tracing_err_log);
6901+
static DEFINE_MUTEX(tracing_err_log_lock);
6902+
6903+
static unsigned int n_tracing_err_log_entries;
6904+
6905+
struct tracing_log_err *get_tracing_log_err(void)
6906+
{
6907+
struct tracing_log_err *err;
6908+
6909+
if (n_tracing_err_log_entries < TRACING_LOG_ERRS_MAX) {
6910+
err = kzalloc(sizeof(*err), GFP_KERNEL);
6911+
if (!err)
6912+
err = ERR_PTR(-ENOMEM);
6913+
n_tracing_err_log_entries++;
6914+
6915+
return err;
6916+
}
6917+
6918+
err = list_first_entry(&tracing_err_log, struct tracing_log_err, list);
6919+
list_del(&err->list);
6920+
6921+
return err;
6922+
}
6923+
6924+
/**
6925+
* err_pos - find the position of a string within a command for error careting
6926+
* @cmd: The tracing command that caused the error
6927+
* @str: The string to position the caret at within @cmd
6928+
*
6929+
* Finds the position of the first occurence of @str within @cmd. The
6930+
* return value can be passed to tracing_log_err() for caret placement
6931+
* within @cmd.
6932+
*
6933+
* Returns the index within @cmd of the first occurence of @str or 0
6934+
* if @str was not found.
6935+
*/
6936+
unsigned int err_pos(char *cmd, const char *str)
6937+
{
6938+
char *found;
6939+
6940+
if (WARN_ON(!strlen(cmd)))
6941+
return 0;
6942+
6943+
found = strstr(cmd, str);
6944+
if (found)
6945+
return found - cmd;
6946+
6947+
return 0;
6948+
}
6949+
6950+
/**
6951+
* tracing_log_err - write an error to the tracing error log
6952+
* @loc: A string describing where the error occurred
6953+
* @cmd: The tracing command that caused the error
6954+
* @errs: The array of loc-specific static error strings
6955+
* @type: The index into errs[], which produces the specific static err string
6956+
* @pos: The position the caret should be placed in the cmd
6957+
*
6958+
* Writes an error into tracing/error_log of the form:
6959+
*
6960+
* <loc>: error: <text>
6961+
* Command: <cmd>
6962+
* ^
6963+
*
6964+
* tracing/error_log is a small log file containing the last
6965+
* TRACING_LOG_ERRS_MAX errors (8). Memory for errors isn't allocated
6966+
* unless there has been a tracing error, and the error log can be
6967+
* cleared and have its memory freed by writing the empty string in
6968+
* truncation mode to it i.e. echo > tracing/error_log.
6969+
*
6970+
* NOTE: the @errs array along with the @type param are used to
6971+
* produce a static error string - this string is not copied and saved
6972+
* when the error is logged - only a pointer to it is saved. See
6973+
* existing callers for examples of how static strings are typically
6974+
* defined for use with tracing_log_err().
6975+
*/
6976+
void tracing_log_err(const char *loc, const char *cmd,
6977+
const char **errs, u8 type, u8 pos)
6978+
{
6979+
struct tracing_log_err *err;
6980+
6981+
mutex_lock(&tracing_err_log_lock);
6982+
err = get_tracing_log_err();
6983+
if (PTR_ERR(err) == -ENOMEM) {
6984+
mutex_unlock(&tracing_err_log_lock);
6985+
return;
6986+
}
6987+
6988+
snprintf(err->loc, TRACING_LOG_LOC_MAX, "%s: error: ", loc);
6989+
snprintf(err->cmd, MAX_FILTER_STR_VAL,"\n" CMD_PREFIX "%s\n", cmd);
6990+
6991+
err->info.errs = errs;
6992+
err->info.type = type;
6993+
err->info.pos = pos;
6994+
err->info.ts = local_clock();
6995+
6996+
list_add_tail(&err->list, &tracing_err_log);
6997+
mutex_unlock(&tracing_err_log_lock);
6998+
}
6999+
7000+
static void clear_tracing_err_log(void)
7001+
{
7002+
struct tracing_log_err *err, *next;
7003+
7004+
mutex_lock(&tracing_err_log_lock);
7005+
list_for_each_entry_safe(err, next, &tracing_err_log, list) {
7006+
list_del(&err->list);
7007+
kfree(err);
7008+
}
7009+
7010+
n_tracing_err_log_entries = 0;
7011+
mutex_unlock(&tracing_err_log_lock);
7012+
}
7013+
7014+
static void *tracing_err_log_seq_start(struct seq_file *m, loff_t *pos)
7015+
{
7016+
mutex_lock(&tracing_err_log_lock);
7017+
7018+
return seq_list_start(&tracing_err_log, *pos);
7019+
}
7020+
7021+
static void *tracing_err_log_seq_next(struct seq_file *m, void *v, loff_t *pos)
7022+
{
7023+
return seq_list_next(v, &tracing_err_log, pos);
7024+
}
7025+
7026+
static void tracing_err_log_seq_stop(struct seq_file *m, void *v)
7027+
{
7028+
mutex_unlock(&tracing_err_log_lock);
7029+
}
7030+
7031+
static void tracing_err_log_show_pos(struct seq_file *m, u8 pos)
7032+
{
7033+
u8 i;
7034+
7035+
for (i = 0; i < sizeof(CMD_PREFIX) - 1; i++)
7036+
seq_putc(m, ' ');
7037+
for (i = 0; i < pos; i++)
7038+
seq_putc(m, ' ');
7039+
seq_puts(m, "^\n");
7040+
}
7041+
7042+
static int tracing_err_log_seq_show(struct seq_file *m, void *v)
7043+
{
7044+
struct tracing_log_err *err = v;
7045+
7046+
if (err) {
7047+
const char *err_text = err->info.errs[err->info.type];
7048+
u64 sec = err->info.ts;
7049+
u32 nsec;
7050+
7051+
nsec = do_div(sec, NSEC_PER_SEC);
7052+
seq_printf(m, "[%5llu.%06u] %s%s", sec, nsec / 1000,
7053+
err->loc, err_text);
7054+
seq_printf(m, "%s", err->cmd);
7055+
tracing_err_log_show_pos(m, err->info.pos);
7056+
}
7057+
7058+
return 0;
7059+
}
7060+
7061+
static const struct seq_operations tracing_err_log_seq_ops = {
7062+
.start = tracing_err_log_seq_start,
7063+
.next = tracing_err_log_seq_next,
7064+
.stop = tracing_err_log_seq_stop,
7065+
.show = tracing_err_log_seq_show
7066+
};
7067+
7068+
static int tracing_err_log_open(struct inode *inode, struct file *file)
7069+
{
7070+
int ret = 0;
7071+
7072+
/* If this file was opened for write, then erase contents */
7073+
if ((file->f_mode & FMODE_WRITE) && (file->f_flags & O_TRUNC))
7074+
clear_tracing_err_log();
7075+
7076+
if (file->f_mode & FMODE_READ)
7077+
ret = seq_open(file, &tracing_err_log_seq_ops);
7078+
7079+
return ret;
7080+
}
7081+
7082+
static ssize_t tracing_err_log_write(struct file *file,
7083+
const char __user *buffer,
7084+
size_t count, loff_t *ppos)
7085+
{
7086+
return count;
7087+
}
7088+
7089+
static const struct file_operations tracing_err_log_fops = {
7090+
.open = tracing_err_log_open,
7091+
.write = tracing_err_log_write,
7092+
.read = seq_read,
7093+
.llseek = seq_lseek,
7094+
};
7095+
68817096
static int tracing_buffers_open(struct inode *inode, struct file *filp)
68827097
{
68837098
struct trace_array *tr = inode->i_private;
@@ -8284,6 +8499,9 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer)
82848499
tr, &snapshot_fops);
82858500
#endif
82868501

8502+
trace_create_file("error_log", 0644, d_tracer,
8503+
tr, &tracing_err_log_fops);
8504+
82878505
for_each_tracing_cpu(cpu)
82888506
tracing_init_tracefs_percpu(tr, cpu);
82898507

kernel/trace/trace.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1884,6 +1884,10 @@ extern ssize_t trace_parse_run_command(struct file *file,
18841884
const char __user *buffer, size_t count, loff_t *ppos,
18851885
int (*createfn)(int, char**));
18861886

1887+
extern unsigned int err_pos(char *cmd, const char *str);
1888+
extern void tracing_log_err(const char *loc, const char *cmd,
1889+
const char **errs, u8 type, u8 pos);
1890+
18871891
/*
18881892
* Normal trace_printk() and friends allocates special buffers
18891893
* to do the manipulation, as well as saves the print formats

0 commit comments

Comments
 (0)