From patchwork Thu Oct 1 17:26:47 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Drew Richardson X-Patchwork-Id: 54399 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-lb0-f198.google.com (mail-lb0-f198.google.com [209.85.217.198]) by patches.linaro.org (Postfix) with ESMTPS id 588B023010 for ; Thu, 1 Oct 2015 18:55:58 +0000 (UTC) Received: by lbbmp1 with SMTP id mp1sf9460914lbb.2 for ; Thu, 01 Oct 2015 11:55:56 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:delivered-to:date:from:to:cc:subject:message-id :mime-version:content-type:content-disposition:user-agent:sender :precedence:list-id:x-original-sender :x-original-authentication-results:mailing-list:list-post:list-help :list-archive:list-unsubscribe; bh=mC50q8RubZg0MHF6x7ryilyZX0TZJdiI2qwwGTRIneg=; b=IRHMC6yFkhH+WVuK40QtKSfka+xTe3WOr+k+OZ1Nch2KCx7TR01at/b+LP8ZDRzEZQ +5R/ZeG8pelY3b2I7HzTNie+Za6OD0XZcLdkB63ZHbPEl7KS9IWwz40lkS8YRroG3aZj EwgCpb6DVY8SKc7xmbaR5ME7yfKNqb4iAyjk9UXYvzA5+sCL0pnlRYaAjTrkkf5QCTsK s+/QCAxJUJN7vuK0eVynLz9DfzxvDbyAlOg9fs2DVTs7ErmSbcFcY+XNC9IIDHvnDcQ3 sMg740PkFv8OZECdE8VWFbmptXOZELZ4Pde6mj8ncHOWlhiMvaexdK41VSz6yKx7hPBq fKwQ== X-Gm-Message-State: ALoCoQkSdJGDbgiIX6Dai+9mjTgpr0LXuU1hGKIK7yE8TOacTFiupPOlmRLIZ4HmfoUY8S5iW25/ X-Received: by 10.112.132.6 with SMTP id oq6mr1708178lbb.1.1443725756870; Thu, 01 Oct 2015 11:55:56 -0700 (PDT) X-BeenThere: patchwork-forward@linaro.org Received: by 10.25.164.77 with SMTP id n74ls132298lfe.94.gmail; Thu, 01 Oct 2015 11:55:56 -0700 (PDT) X-Received: by 10.112.184.196 with SMTP id ew4mr3543788lbc.17.1443725756699; Thu, 01 Oct 2015 11:55:56 -0700 (PDT) Received: from mail-lb0-f175.google.com (mail-lb0-f175.google.com. [209.85.217.175]) by mx.google.com with ESMTPS id ec5si3606877lbc.119.2015.10.01.11.55.56 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 01 Oct 2015 11:55:56 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.175 as permitted sender) client-ip=209.85.217.175; Received: by lbwr8 with SMTP id r8so14664208lbw.2 for ; Thu, 01 Oct 2015 11:55:56 -0700 (PDT) X-Received: by 10.112.163.131 with SMTP id yi3mr3686007lbb.36.1443725756502; Thu, 01 Oct 2015 11:55:56 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.59.35 with SMTP id w3csp777872lbq; Thu, 1 Oct 2015 11:55:55 -0700 (PDT) X-Received: by 10.68.254.7 with SMTP id ae7mr14249245pbd.131.1443725755080; Thu, 01 Oct 2015 11:55:55 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id vq2si10746736pbc.220.2015.10.01.11.55.54; Thu, 01 Oct 2015 11:55:54 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756457AbbJASzw (ORCPT + 30 others); Thu, 1 Oct 2015 14:55:52 -0400 Received: from foss.arm.com ([217.140.101.70]:37415 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753472AbbJASzv (ORCPT ); Thu, 1 Oct 2015 14:55:51 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id D4E9549; Thu, 1 Oct 2015 11:55:52 -0700 (PDT) Received: from localhost (usa-sjc-imap-foss1.foss.arm.com [10.72.51.249]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id C65B43F236; Thu, 1 Oct 2015 11:55:50 -0700 (PDT) Date: Thu, 1 Oct 2015 10:26:47 -0700 From: Drew Richardson To: will.deacon@arm.com, a.p.zijlstra@chello.nl, mingo@redhat.com, acme@kernel.org, linux@arm.linux.org.uk, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org Cc: wade.cherry@arm.com, pawel.moll@arm.com Subject: [PATCH] arm: perf: Fix userspace call stack walking Message-ID: <20151001172643.GA23147@dreric01-gentoo.localdomain> MIME-Version: 1.0 Content-Disposition: inline User-Agent: Mutt/1.5.23 (2014-03-12) Sender: linux-kernel-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: Drew.Richardson@arm.com X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.175 as permitted sender) smtp.mailfrom=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , I got some undeliverable responses the first time, sorry if you get this twice --- The layout of stack frames has changed over time. Testing using a arm-linux-gnueabi gcc-4.2 from 2007 the original code didn't work but this new code does. It also works with clang as well as newer versions of gcc. gcc has this layout for it's stackframes caller_fp caller_lr <- fp However clang has this layout caller_fp <- fp caller_lr Since the layouts are not compatible use a heuristic to determine for each stack frame which layout is used. Signed-off-by: Drew Richardson --- arch/arm/kernel/perf_callchain.c | 86 +++++++++++++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 10 deletions(-) diff --git a/arch/arm/kernel/perf_callchain.c b/arch/arm/kernel/perf_callchain.c index 4e02ae5950ff..99acfe9be9b1 100644 --- a/arch/arm/kernel/perf_callchain.c +++ b/arch/arm/kernel/perf_callchain.c @@ -13,16 +13,12 @@ /* * The registers we're interested in are at the end of the variable - * length saved register structure. The fp points at the end of this - * structure so the address of this struct is: - * (struct frame_tail *)(xxx->fp)-1 + * length saved register structure. * * This code has been adapted from the ARM OProfile support. */ struct frame_tail { - struct frame_tail __user *fp; - unsigned long sp; - unsigned long lr; + void __user *regs[3]; } __attribute__((packed)); /* @@ -35,6 +31,8 @@ user_backtrace(struct frame_tail __user *tail, { struct frame_tail buftail; unsigned long err; + void __user *fp; + void __user *lr; if (!access_ok(VERIFY_READ, tail, sizeof(buftail))) return NULL; @@ -46,16 +44,84 @@ user_backtrace(struct frame_tail __user *tail, if (err) return NULL; - perf_callchain_store(entry, buftail.lr); + /* + * gcc style stackframes + * + * parent_func <- caller_lr? + * ... + * caller_fp (regs[0]) <- tail + * caller_lr (regs[1]) <- fp + * other_data (regs[2]) + * ... + * caller_stackframe <- caller_fp + * ... + * parent_func <- caller_lr? + * + * clang style stackframes + * + * parent_func <- caller_lr? + * ... + * other_data (regs[0]) <- tail + * caller_fp (regs[1]) <- fp + * caller_lr (regs[2]) + * ... + * caller_stackframe <- caller_fp + * ... + * parent_func <- caller_lr? + * + * Is buftail.regs[1] the caller_lr or the caller_fp? Assume + * that the previous function does not exist before the + * previous stackframe (ie, caller_lr < tail || caller_lr > + * caller_fp) and that the stack grows downwards towards + * smaller addresses (ie, fp < caller_fp). In the case of + * ambiguity, assume gcc style stackframes. + * + * (caller_lr < tail < caller_fp) || (tail < caller_fp < caller_lr) + * + * gcc style stackframes + * + * (regs[1] < tail < regs[0]) || (tail < regs[0] < regs[1]) + * regs[2] is undefined + * + * regs[0] < tail = 0 + * regs[1] < tail = 0 || 1 + * regs[2] < tail = undefined + * regs[1] < regs[0] = 0 || 1 + * regs[2] < regs[0] = undefined + * regs[2] < regs[1] = undefined + * + * clang style stackframes + * + * (regs[2] < tail < regs[1]) || (tail < regs[1] < regs[2]) + * regs[0] is undefined + * + * regs[0] < tail = undefined + * regs[1] < tail = 0 + * regs[2] < tail = 0 || 1 + * regs[1] < regs[0] = undefined + * regs[2] < regs[0] = undefined + * regs[2] < regs[1] = 0 || 1 + */ + if (buftail.regs[0] > (void __user *)tail) { + /* gcc style stackframes */ + fp = buftail.regs[0]; + lr = buftail.regs[1]; + } else { + /* clang style stackframes */ + fp = buftail.regs[1]; + lr = buftail.regs[2]; + } + + perf_callchain_store(entry, (uintptr_t)lr); /* * Frame pointers should strictly progress back up the stack * (towards higher addresses). */ - if (tail + 1 >= buftail.fp) + if ((void __user *)tail + 4 >= fp) return NULL; - return buftail.fp - 1; + return fp - 4; } void @@ -73,7 +139,7 @@ perf_callchain_user(struct perf_callchain_entry *entry, struct pt_regs *regs) if (!current->mm) return; - tail = (struct frame_tail __user *)regs->ARM_fp - 1; + tail = (struct frame_tail __user *)(regs->ARM_fp - 4); while ((entry->nr < PERF_MAX_STACK_DEPTH) && tail && !((unsigned long)tail & 0x3))