From patchwork Wed Mar 7 21:58:21 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Stultz X-Patchwork-Id: 7145 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 9901323E64 for ; Wed, 7 Mar 2012 21:58:44 +0000 (UTC) Received: from mail-iy0-f180.google.com (mail-iy0-f180.google.com [209.85.210.180]) by fiordland.canonical.com (Postfix) with ESMTP id 6041FA181D7 for ; Wed, 7 Mar 2012 21:58:44 +0000 (UTC) Received: by mail-iy0-f180.google.com with SMTP id e36so12341121iag.11 for ; Wed, 07 Mar 2012 13:58:44 -0800 (PST) Received: by 10.50.183.137 with SMTP id em9mr5078499igc.58.1331157524148; Wed, 07 Mar 2012 13:58:44 -0800 (PST) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.231.53.18 with SMTP id k18csp22999ibg; Wed, 7 Mar 2012 13:58:43 -0800 (PST) Received: by 10.52.30.98 with SMTP id r2mr6068383vdh.8.1331157522143; Wed, 07 Mar 2012 13:58:42 -0800 (PST) Received: from e9.ny.us.ibm.com (e9.ny.us.ibm.com. [32.97.182.139]) by mx.google.com with ESMTPS id j18si8808126vcq.71.2012.03.07.13.58.41 (version=TLSv1/SSLv3 cipher=OTHER); Wed, 07 Mar 2012 13:58:42 -0800 (PST) Received-SPF: pass (google.com: domain of jstultz@us.ibm.com designates 32.97.182.139 as permitted sender) client-ip=32.97.182.139; Authentication-Results: mx.google.com; spf=pass (google.com: domain of jstultz@us.ibm.com designates 32.97.182.139 as permitted sender) smtp.mail=jstultz@us.ibm.com Received: from /spool/local by e9.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Wed, 7 Mar 2012 16:58:41 -0500 Received: from d01dlp01.pok.ibm.com (9.56.224.56) by e9.ny.us.ibm.com (192.168.1.109) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Wed, 7 Mar 2012 16:58:35 -0500 Received: from d01relay06.pok.ibm.com (d01relay06.pok.ibm.com [9.56.227.116]) by d01dlp01.pok.ibm.com (Postfix) with ESMTP id A23F938C8059; Wed, 7 Mar 2012 16:58:34 -0500 (EST) Received: from d01av02.pok.ibm.com (d01av02.pok.ibm.com [9.56.224.216]) by d01relay06.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id q27LwXFq3182810; Wed, 7 Mar 2012 16:58:33 -0500 Received: from d01av02.pok.ibm.com (loopback [127.0.0.1]) by d01av02.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id q27LwXTQ018721; Wed, 7 Mar 2012 18:58:33 -0300 Received: from kernel.beaverton.ibm.com (kernel.beaverton.ibm.com [9.47.67.96]) by d01av02.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id q27LwW2N018660; Wed, 7 Mar 2012 18:58:32 -0300 Received: by kernel.beaverton.ibm.com (Postfix, from userid 1056) id A72F2C0422; Wed, 7 Mar 2012 13:58:30 -0800 (PST) From: John Stultz To: lkml Cc: Colin Cross , Greg KH , Android Kernel Team , John Stultz Subject: [PATCH 11/13] android: persistent_trace: ftrace into persistent_ram Date: Wed, 7 Mar 2012 13:58:21 -0800 Message-Id: <1331157503-3413-12-git-send-email-john.stultz@linaro.org> X-Mailer: git-send-email 1.7.3.2.146.gca209 In-Reply-To: <1331157503-3413-1-git-send-email-john.stultz@linaro.org> References: <1331157503-3413-1-git-send-email-john.stultz@linaro.org> X-Content-Scanned: Fidelis XPS MAILER x-cbid: 12030721-7182-0000-0000-000000FDF955 X-Gm-Message-State: ALoCoQmx00CCWzH1/USbfS4nbGtEuoup4jJSun5kmzJFMqfmS77POhbW63U/26FO64s0R9Gl9fDb From: Colin Cross persistent_trace uses the ftrace infrastructure, but traces into a persistent_ram buffer instead of the regular ftrace ringbuffer. After a reset or panic, the trace can be decoded with cat /sys/kernel/debug/persistent_trace. CC: Greg KH CC: Android Kernel Team Change-Id: Ia6025ccc323599c7844e0783af0386d32ed7419e Signed-off-by: Colin Cross Signed-off-by: John Stultz --- drivers/staging/android/Kconfig | 14 ++ drivers/staging/android/Makefile | 3 + drivers/staging/android/trace_persistent.c | 242 ++++++++++++++++++++++++++++ 3 files changed, 259 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/android/trace_persistent.c diff --git a/drivers/staging/android/Kconfig b/drivers/staging/android/Kconfig index 08a3b11..bc33ab9 100644 --- a/drivers/staging/android/Kconfig +++ b/drivers/staging/android/Kconfig @@ -37,6 +37,20 @@ config ANDROID_RAM_CONSOLE select ANDROID_PERSISTENT_RAM default n +config PERSISTENT_TRACER + bool "Persistent function tracer" + depends on HAVE_FUNCTION_TRACER + select FUNCTION_TRACER + select ANDROID_PERSISTENT_RAM + help + persistent_trace traces function calls into a persistent ram + buffer that can be decoded and dumped after reboot through + /sys/kernel/debug/persistent_trace. It can be used to + determine what function was last called before a reset or + panic. + + If unsure, say N. + config ANDROID_TIMED_OUTPUT bool "Timed output class driver" default y diff --git a/drivers/staging/android/Makefile b/drivers/staging/android/Makefile index 9b6c9ed..35ffdfe 100644 --- a/drivers/staging/android/Makefile +++ b/drivers/staging/android/Makefile @@ -9,3 +9,6 @@ obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o obj-$(CONFIG_ANDROID_SWITCH) += switch/ obj-$(CONFIG_ANDROID_INTF_ALARM) += alarm.o obj-$(CONFIG_ANDROID_INTF_ALARM_DEV) += alarm-dev.o +obj-$(CONFIG_PERSISTENT_TRACER) += trace_persistent.o + +CFLAGS_REMOVE_trace_persistent.o = -pg diff --git a/drivers/staging/android/trace_persistent.c b/drivers/staging/android/trace_persistent.c new file mode 100644 index 0000000..176b4ff --- /dev/null +++ b/drivers/staging/android/trace_persistent.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2012 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 +#include +#include +#include +#include +#include +#include +#include + +#include "persistent_ram.h" +#include "../../../kernel/trace/trace.h" + +struct persistent_trace_record { + unsigned long ip; + unsigned long parent_ip; +}; + +#define REC_SIZE sizeof(struct persistent_trace_record) + +static struct persistent_ram_zone *persistent_trace; + +static int persistent_trace_enabled; + +static struct trace_array *persistent_trace_array; + +static struct ftrace_ops trace_ops; + +static int persistent_tracer_init(struct trace_array *tr) +{ + persistent_trace_array = tr; + tr->cpu = get_cpu(); + put_cpu(); + + tracing_start_cmdline_record(); + + persistent_trace_enabled = 0; + smp_wmb(); + + register_ftrace_function(&trace_ops); + + smp_wmb(); + persistent_trace_enabled = 1; + + return 0; +} + +static void persistent_trace_reset(struct trace_array *tr) +{ + persistent_trace_enabled = 0; + smp_wmb(); + + unregister_ftrace_function(&trace_ops); + + tracing_stop_cmdline_record(); +} + +static void persistent_trace_start(struct trace_array *tr) +{ + tracing_reset_online_cpus(tr); +} + +static void persistent_trace_call(unsigned long ip, unsigned long parent_ip) +{ + struct trace_array *tr = persistent_trace_array; + struct trace_array_cpu *data; + long disabled; + struct persistent_trace_record rec; + unsigned long flags; + int cpu; + + smp_rmb(); + if (unlikely(!persistent_trace_enabled)) + return; + + if (unlikely(oops_in_progress)) + return; + + /* + * Need to use raw, since this must be called before the + * recursive protection is performed. + */ + local_irq_save(flags); + cpu = raw_smp_processor_id(); + data = tr->data[cpu]; + disabled = atomic_inc_return(&data->disabled); + + if (likely(disabled == 1)) { + rec.ip = ip; + rec.parent_ip = parent_ip; + rec.ip |= cpu; + persistent_ram_write(persistent_trace, &rec, sizeof(rec)); + } + + atomic_dec(&data->disabled); + local_irq_restore(flags); +} + +static struct ftrace_ops trace_ops __read_mostly = { + .func = persistent_trace_call, + .flags = FTRACE_OPS_FL_GLOBAL, +}; + +static struct tracer persistent_tracer __read_mostly = { + .name = "persistent", + .init = persistent_tracer_init, + .reset = persistent_trace_reset, + .start = persistent_trace_start, + .wait_pipe = poll_wait_pipe, +}; + +struct persistent_trace_seq_data { + const void *ptr; + size_t off; + size_t size; +}; + +void *persistent_trace_seq_start(struct seq_file *s, loff_t *pos) +{ + struct persistent_trace_seq_data *data; + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (!data) + return NULL; + + data->ptr = persistent_ram_old(persistent_trace); + data->size = persistent_ram_old_size(persistent_trace); + data->off = data->size % REC_SIZE; + + data->off += *pos * REC_SIZE; + + if (data->off + REC_SIZE > data->size) { + kfree(data); + return NULL; + } + + return data; + +} +void persistent_trace_seq_stop(struct seq_file *s, void *v) +{ + kfree(v); +} + +void *persistent_trace_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct persistent_trace_seq_data *data = v; + + data->off += REC_SIZE; + + if (data->off + REC_SIZE > data->size) + return NULL; + + (*pos)++; + + return data; +} + +int persistent_trace_seq_show(struct seq_file *s, void *v) +{ + struct persistent_trace_seq_data *data = v; + struct persistent_trace_record *rec; + + rec = (struct persistent_trace_record *)(data->ptr + data->off); + + seq_printf(s, "%ld %08lx %08lx %pf <- %pF\n", + rec->ip & 3, rec->ip, rec->parent_ip, + (void *)rec->ip, (void *)rec->parent_ip); + + return 0; +} + +static const struct seq_operations persistent_trace_seq_ops = { + .start = persistent_trace_seq_start, + .next = persistent_trace_seq_next, + .stop = persistent_trace_seq_stop, + .show = persistent_trace_seq_show, +}; + +static int persistent_trace_old_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &persistent_trace_seq_ops); +} + +static const struct file_operations persistent_trace_old_fops = { + .open = persistent_trace_old_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int persistent_trace_probe(struct platform_device *pdev) +{ + struct dentry *d; + int ret; + + persistent_trace = persistent_ram_init_ringbuffer(&pdev->dev, false); + if (IS_ERR(persistent_trace)) { + pr_err("persistent_trace: failed to init ringbuffer: %ld\n", + PTR_ERR(persistent_trace)); + return PTR_ERR(persistent_trace); + } + + ret = register_tracer(&persistent_tracer); + if (ret) + pr_err("persistent_trace: failed to register tracer"); + + if (persistent_ram_old_size(persistent_trace) > 0) { + d = debugfs_create_file("persistent_trace", S_IRUGO, NULL, + NULL, &persistent_trace_old_fops); + if (IS_ERR_OR_NULL(d)) + pr_err("persistent_trace: failed to create old file\n"); + } + + return 0; +} + +static struct platform_driver persistent_trace_driver = { + .probe = persistent_trace_probe, + .driver = { + .name = "persistent_trace", + }, +}; + +static int __init persistent_trace_init(void) +{ + return platform_driver_register(&persistent_trace_driver); +} +core_initcall(persistent_trace_init);