| /* arch/arm/mach-msm/qdsp5/evlog.h |
| * |
| * simple event log debugging facility |
| * |
| * Copyright (C) 2008 Google, Inc. |
| * |
| * 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/fs.h> |
| #include <linux/hrtimer.h> |
| #include <linux/debugfs.h> |
| |
| #define EV_LOG_ENTRY_NAME(n) n##_entry |
| |
| #define DECLARE_LOG(_name, _size, _str) \ |
| static struct ev_entry EV_LOG_ENTRY_NAME(_name)[_size]; \ |
| static struct ev_log _name = { \ |
| .name = #_name, \ |
| .strings = _str, \ |
| .num_strings = ARRAY_SIZE(_str), \ |
| .entry = EV_LOG_ENTRY_NAME(_name), \ |
| .max = ARRAY_SIZE(EV_LOG_ENTRY_NAME(_name)), \ |
| } |
| |
| struct ev_entry { |
| struct timespec when; |
| uint32_t id; |
| uint32_t arg; |
| }; |
| |
| struct ev_log { |
| struct ev_entry *entry; |
| unsigned max; |
| unsigned next; |
| unsigned fault; |
| const char **strings; |
| unsigned num_strings; |
| const char *name; |
| }; |
| |
| static char ev_buf[4096]; |
| |
| static ssize_t ev_log_read(struct file *file, char __user *buf, |
| size_t count, loff_t *ppos) |
| { |
| struct ev_log *log = file->private_data; |
| struct ev_entry *entry; |
| unsigned long flags; |
| int size = 0; |
| unsigned n, id, max; |
| struct timespec now, t; |
| |
| max = log->max; |
| getnstimeofday(&now); |
| local_irq_save(flags); |
| n = (log->next - 1) & (max - 1); |
| entry = log->entry; |
| while (n != log->next) { |
| t = timespec_sub(now, entry[n].when); |
| id = entry[n].id; |
| if (id) { |
| const char *str; |
| if (id < log->num_strings) |
| str = log->strings[id]; |
| else |
| str = "UNKNOWN"; |
| size += scnprintf(ev_buf + size, 4096 - size, |
| "%lu.%03lu %08x %s\n", |
| t.tv_sec, t.tv_nsec / 1000000, |
| entry[n].arg, str); |
| } |
| n = (n - 1) & (max - 1); |
| } |
| log->fault = 0; |
| local_irq_restore(flags); |
| return simple_read_from_buffer(buf, count, ppos, ev_buf, size); |
| } |
| |
| static void ev_log_write(struct ev_log *log, unsigned id, unsigned arg) |
| { |
| struct ev_entry *entry; |
| unsigned long flags; |
| local_irq_save(flags); |
| |
| if (log->fault) { |
| if (log->fault == 1) |
| goto done; |
| log->fault--; |
| } |
| |
| entry = log->entry + log->next; |
| getnstimeofday(&entry->when); |
| entry->id = id; |
| entry->arg = arg; |
| log->next = (log->next + 1) & (log->max - 1); |
| done: |
| local_irq_restore(flags); |
| } |
| |
| static int ev_log_open(struct inode *inode, struct file *file) |
| { |
| file->private_data = inode->i_private; |
| return 0; |
| } |
| |
| static const struct file_operations ev_log_ops = { |
| .read = ev_log_read, |
| .open = ev_log_open, |
| }; |
| |
| static int ev_log_init(struct ev_log *log) |
| { |
| debugfs_create_file(log->name, 0444, 0, log, &ev_log_ops); |
| return 0; |
| } |
| |