From patchwork Sun Nov 12 23:40:12 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Song Liu X-Patchwork-Id: 837307 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming@ozlabs.org Delivered-To: patchwork-incoming@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=fb.com header.i=@fb.com header.b="CHKeYuCQ"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3yZqz33z3wz9t20 for ; Mon, 13 Nov 2017 10:41:03 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751482AbdKLXlC (ORCPT ); Sun, 12 Nov 2017 18:41:02 -0500 Received: from mx0a-00082601.pphosted.com ([67.231.145.42]:35488 "EHLO mx0a-00082601.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751020AbdKLXkS (ORCPT ); Sun, 12 Nov 2017 18:40:18 -0500 Received: from pps.filterd (m0109334.ppops.net [127.0.0.1]) by mx0a-00082601.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id vACNcwnm029354 for ; Sun, 12 Nov 2017 15:40:18 -0800 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fb.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-type; s=facebook; bh=qPM2DxN3i4jd5BrFlhUFWD7WQbDmT830QaMAos0PKoc=; b=CHKeYuCQJK9ijntAKaovJgwxQq4M+n4q/PytlMsr+dDgewcTGfh3Rteza3J50GhTlM4q TuZD9TipofeHqkBQBT5rxkCQCBXlxHDkGaL/jhcEUw0czknywdtRacNoD8AkttWrQw/+ mMWYOGGO3g+ubfpAz2ZBVuosN12Zfggekmk= Received: from mail.thefacebook.com ([199.201.64.23]) by mx0a-00082601.pphosted.com with ESMTP id 2e6tgprhmj-4 (version=TLSv1 cipher=ECDHE-RSA-AES256-SHA bits=256 verify=NOT) for ; Sun, 12 Nov 2017 15:40:18 -0800 Received: from mx-out.facebook.com (192.168.52.123) by PRN-CHUB10.TheFacebook.com (192.168.16.20) with Microsoft SMTP Server id 14.3.361.1; Sun, 12 Nov 2017 15:40:17 -0800 Received: by devbig102.frc2.facebook.com (Postfix, from userid 4523) id 7E114428249D; Sun, 12 Nov 2017 15:40:16 -0800 (PST) Smtp-Origin-Hostprefix: devbig From: Song Liu Smtp-Origin-Hostname: devbig102.frc2.facebook.com To: , , , , , , CC: , Song Liu Smtp-Origin-Cluster: frc2c02 Subject: [RFC v2 4/6] perf: implement uprobe support to PERF_TYPE_PROBE Date: Sun, 12 Nov 2017 15:40:12 -0800 Message-ID: <20171112234014.2983360-7-songliubraving@fb.com> X-Mailer: git-send-email 2.9.5 In-Reply-To: <20171112234014.2983360-1-songliubraving@fb.com> References: <20171112234014.2983360-1-songliubraving@fb.com> X-FB-Internal: Safe MIME-Version: 1.0 X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-11-12_10:, , signatures=0 X-Proofpoint-Spam-Reason: safe X-FB-Internal: Safe Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This patch adds uprobe support to perf_probe with similar pattern as previous patch (for kprobe). Two functions, create_local_trace_uprobe() and destroy_local_trace_uprobe(), are created so a uprobe can be created and attached to the file descriptor created by perf_event_open(). Signed-off-by: Song Liu Reviewed-by: Yonghong Song Reviewed-by: Josef Bacik --- kernel/trace/trace_event_perf.c | 48 +++++++++++++++++++++- kernel/trace/trace_probe.h | 4 ++ kernel/trace/trace_uprobe.c | 90 ++++++++++++++++++++++++++++++++++++----- 3 files changed, 131 insertions(+), 11 deletions(-) diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c index bf9b99b..4e4de84 100644 --- a/kernel/trace/trace_event_perf.c +++ b/kernel/trace/trace_event_perf.c @@ -256,6 +256,39 @@ static int perf_probe_create_kprobe(struct perf_event *p_event, } #endif /* CONFIG_KPROBE_EVENTS */ +#ifdef CONFIG_UPROBE_EVENTS +static int perf_probe_create_uprobe(struct perf_event *p_event, + struct probe_desc *pd, char *name) +{ + struct trace_event_call *tp_event; + int ret; + + if (!name) + return -EINVAL; + tp_event = create_local_trace_uprobe( + name, pd->offset, p_event->attr.is_return); + if (IS_ERR(tp_event)) + return PTR_ERR(tp_event); + /* + * local trace_uprobe need to hold event_mutex to call + * uprobe_buffer_enable() and uprobe_buffer_disable(). + * event_mutex is not required for local trace_kprobes. + */ + mutex_lock(&event_mutex); + ret = perf_trace_event_init(tp_event, p_event); + if (ret) + destroy_local_trace_uprobe(tp_event); + mutex_unlock(&event_mutex); + return ret; +} +#else +static int perf_probe_create_uprobe(struct perf_event *p_event, + struct probe_desc *pd, char *name) +{ + return -EOPNOTSUPP; +} +#endif /* CONFIG_KPROBE_EVENTS */ + int perf_probe_init(struct perf_event *p_event) { struct probe_desc pd; @@ -292,7 +325,7 @@ int perf_probe_init(struct perf_event *p_event) if (!p_event->attr.is_uprobe) ret = perf_probe_create_kprobe(p_event, &pd, name); else - ret = -EOPNOTSUPP; + ret = perf_probe_create_uprobe(p_event, &pd, name); out: kfree(name); return ret; @@ -308,13 +341,26 @@ void perf_trace_destroy(struct perf_event *p_event) void perf_probe_destroy(struct perf_event *p_event) { + /* + * local trace_uprobe need to hold event_mutex to call + * uprobe_buffer_enable() and uprobe_buffer_disable(). + * event_mutex is not required for local trace_kprobes. + */ + if (p_event->attr.is_uprobe) + mutex_lock(&event_mutex); perf_trace_event_close(p_event); perf_trace_event_unreg(p_event); + if (p_event->attr.is_uprobe) + mutex_unlock(&event_mutex); if (!p_event->attr.is_uprobe) { #ifdef CONFIG_KPROBE_EVENTS destroy_local_trace_kprobe(p_event->tp_event); #endif + } else { +#ifdef CONFIG_UPROBE_EVENTS + destroy_local_trace_uprobe(p_event->tp_event); +#endif } } diff --git a/kernel/trace/trace_probe.h b/kernel/trace/trace_probe.h index 910ae1b..86b5925 100644 --- a/kernel/trace/trace_probe.h +++ b/kernel/trace/trace_probe.h @@ -417,4 +417,8 @@ extern struct trace_event_call * create_local_trace_kprobe(char *func, void *addr, unsigned long offs, bool is_return); extern void destroy_local_trace_kprobe(struct trace_event_call *event_call); + +extern struct trace_event_call * +create_local_trace_uprobe(char *name, unsigned long offs, bool is_return); +extern void destroy_local_trace_uprobe(struct trace_event_call *event_call); #endif diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c index 153c0e4..1aa82be 100644 --- a/kernel/trace/trace_uprobe.c +++ b/kernel/trace/trace_uprobe.c @@ -31,8 +31,8 @@ #define UPROBE_EVENT_SYSTEM "uprobes" struct uprobe_trace_entry_head { - struct trace_entry ent; - unsigned long vaddr[]; + struct trace_entry ent; + unsigned long vaddr[]; }; #define SIZEOF_TRACE_ENTRY(is_return) \ @@ -1292,16 +1292,25 @@ static struct trace_event_functions uprobe_funcs = { .trace = print_uprobe_event }; -static int register_uprobe_event(struct trace_uprobe *tu) +static inline void init_trace_event_call(struct trace_uprobe *tu, + struct trace_event_call *call) { - struct trace_event_call *call = &tu->tp.call; - int ret; - - /* Initialize trace_event_call */ INIT_LIST_HEAD(&call->class->fields); call->event.funcs = &uprobe_funcs; call->class->define_fields = uprobe_event_define_fields; + call->flags = TRACE_EVENT_FL_UPROBE; + call->class->reg = trace_uprobe_register; + call->data = tu; +} + +static int register_uprobe_event(struct trace_uprobe *tu) +{ + struct trace_event_call *call = &tu->tp.call; + int ret = 0; + + init_trace_event_call(tu, call); + if (set_print_fmt(&tu->tp, is_ret_probe(tu)) < 0) return -ENOMEM; @@ -1311,9 +1320,6 @@ static int register_uprobe_event(struct trace_uprobe *tu) return -ENODEV; } - call->flags = TRACE_EVENT_FL_UPROBE; - call->class->reg = trace_uprobe_register; - call->data = tu; ret = trace_add_event_call(call); if (ret) { @@ -1339,6 +1345,70 @@ static int unregister_uprobe_event(struct trace_uprobe *tu) return 0; } +#ifdef CONFIG_PERF_EVENTS +struct trace_event_call * +create_local_trace_uprobe(char *name, unsigned long offs, bool is_return) +{ + struct trace_uprobe *tu; + struct inode *inode; + struct path path; + int ret; + + ret = kern_path(name, LOOKUP_FOLLOW, &path); + if (ret) + return ERR_PTR(ret); + + inode = igrab(d_inode(path.dentry)); + path_put(&path); + + if (!inode || !S_ISREG(inode->i_mode)) { + iput(inode); + return ERR_PTR(-EINVAL); + } + + /* + * local trace_kprobes are not added to probe_list, so they are never + * searched in find_trace_kprobe(). Therefore, there is no concern of + * duplicated name "DUMMY_EVENT" here. + */ + tu = alloc_trace_uprobe(UPROBE_EVENT_SYSTEM, "DUMMY_EVENT", 0, + is_return); + + if (IS_ERR(tu)) { + pr_info("Failed to allocate trace_uprobe.(%d)\n", + (int)PTR_ERR(tu)); + return ERR_CAST(tu); + } + + tu->offset = offs; + tu->inode = inode; + tu->filename = kstrdup(name, GFP_KERNEL); + init_trace_event_call(tu, &tu->tp.call); + + if (set_print_fmt(&tu->tp, is_ret_probe(tu)) < 0) { + ret = -ENOMEM; + goto error; + } + + return &tu->tp.call; +error: + free_trace_uprobe(tu); + return ERR_PTR(ret); +} + +void destroy_local_trace_uprobe(struct trace_event_call *event_call) +{ + struct trace_uprobe *tu; + + tu = container_of(event_call, struct trace_uprobe, tp.call); + + kfree(tu->tp.call.print_fmt); + tu->tp.call.print_fmt = NULL; + + free_trace_uprobe(tu); +} +#endif /* CONFIG_PERF_EVENTS */ + /* Make a trace interface for controling probe points */ static __init int init_uprobe_trace(void) {