Received: by 2002:a05:6a10:1a4d:0:0:0:0 with SMTP id nk13csp418382pxb; Tue, 1 Feb 2022 02:51:04 -0800 (PST) X-Google-Smtp-Source: ABdhPJz8fffdWX59LepKKZBV7q0q1TS4EB8hbzOnNNHN6pBpJ6wRfYbz54x0ISZOz8g/lqxnH8f/ X-Received: by 2002:a17:906:d551:: with SMTP id cr17mr19783866ejc.27.1643712663943; Tue, 01 Feb 2022 02:51:03 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1643712663; cv=none; d=google.com; s=arc-20160816; b=yufTVJCGuy8GFtjzKih/0zYTP0BMXhYJh7dGgPtpF4Azjp98oVt9mIp6ExtzcoE08l dH/VkXI4oLq0jx854htUbCLlQyOYNmLHM4STwSWYSFMOqpPTAtb27oAq6s7HFDl9ih+Y fL7i9c5vOWcNBSM2uFQ/Tfh/wP7uktCwMcMP7PjOCl3FSPdXao2VsRxHcgY530iExept cM7EgJgabbhliVjpqy2MLT0t+v+p8Nkf6hxN+NqMLEVerlJrWfNI97O9YLcKI07waeEY XSkufXXuO/bnFKNWuM6rcpU5BMZeJzTpD214ZDHRSqcYdPIBlg2RLuIrWSbZrIvEOqck u8cQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=qmBc2fV0r1moivjZbcJeWcHeiDtMm4VRovCbcEge9wo=; b=OYm/Ano/RI0qmQvOCpV0pIZgzxa4XU96u41kpCYMIb+N3YJjg2c7TUG185+n8OrHMm vtNquO9bZ40rhe8xeIljGDiQnbH/rLkI8Be6qU/KUGdHH38ONifbB8Z3vEGHpY1D9Hby ilEcigOlbKavS98gPc6vWZ4T3H1Tncbxv5z0Tsmm0IwZ8d3+LlkwRtgpgoFp9LIYC+NA wjBqvUKWA/1B6InkRzA3g972XH5Z/L7Mw4/MIx0k0v6zgBypQkQvNnq5LrLlbSViqAqg umamWezdGbgrImVWplgFTeYaSSWqM3n6JzA3+fxiscNla5ueInF4CwORfqvRh4tN0O32 pY6g== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=pFzOuihs; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id u1si8907531edx.12.2022.02.01.02.50.39; Tue, 01 Feb 2022 02:51:03 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20210112 header.b=pFzOuihs; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1355103AbiA3Oow (ORCPT + 99 others); Sun, 30 Jan 2022 09:44:52 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:58690 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1355094AbiA3Oou (ORCPT ); Sun, 30 Jan 2022 09:44:50 -0500 Received: from mail-pl1-x62a.google.com (mail-pl1-x62a.google.com [IPv6:2607:f8b0:4864:20::62a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E5C73C06173B for ; Sun, 30 Jan 2022 06:44:49 -0800 (PST) Received: by mail-pl1-x62a.google.com with SMTP id b15so10328696plg.3 for ; Sun, 30 Jan 2022 06:44:49 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=qmBc2fV0r1moivjZbcJeWcHeiDtMm4VRovCbcEge9wo=; b=pFzOuihs//feYMB8MtfCtzXJEDKXnf3g+zAJf496Mp+eNJCwOXX4sxXkeokno7XV74 NtKp4x82W+sob0FgXPHf9ZZK+Ujo5yN9j9tk1Y+sR8li1rjb96wzzwSJYXff/UQloc9K qbbJ/Vo4naHxURvsDG4x6QmMItOfgeSzVUyIyX9f0tfpeDlq6+GDMIFJvQrMiHlfLQAN wkIYJ5z7uoDz8FdUS3Wv8M9/r2Jp9F4Vghhs470utBsRx+JfjQzmmGXrjQ0f5c/sNNon S5kfARtbNfcyR39/rOnHy3H2VuNdLGY6gAAp2uNqIaRzq6BvlQOa0WnjIQiyw6U7Dgnw ZGbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=qmBc2fV0r1moivjZbcJeWcHeiDtMm4VRovCbcEge9wo=; b=5nmB9xGBAerE4p5HLf1YeQIyctNHkNYr/2BCVytXaiKoCmyh1q13OpHX5KtmxQHCna IzNvWrtAdSQ3RexN/nE/j0S+foGS/Np7zcRUICqjwOTSXmcvEMVdvrssCBeoMN0uGB6Z jO7KhNEmVf089tWJjm7fqrxzAGRV0ZsKO/JpmnJ/89WdfsQcrSr4s7cRGBVHNjIyJ70Q b3VWI/6dWGRgBhcBatt6sXWoNh68Zmd6uueS2kxz66KtzdV2RFG5iZBR85wyZA8m3gcz ySxJN7lPASCOj1OYhA9nr6s9GwWa3wtd/m03B8zup+kXSo2qMJrzZ2vsaavKSY5POBnu U9yQ== X-Gm-Message-State: AOAM530kwacW9dB5SAUtICW83H38afTKG1a4658nLgy6X/QPpEQDuKXL b3KpRL/iVWHERiFK96bnSj8= X-Received: by 2002:a17:903:234e:: with SMTP id c14mr16758286plh.50.1643553888755; Sun, 30 Jan 2022 06:44:48 -0800 (PST) Received: from localhost.localdomain (li567-56.members.linode.com. [192.155.81.56]) by smtp.gmail.com with ESMTPSA id w8sm10502126pfu.128.2022.01.30.06.44.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 30 Jan 2022 06:44:48 -0800 (PST) From: Jeff Xie To: rostedt@goodmis.org Cc: mhiramat@kernel.org, mingo@redhat.com, zanussi@kernel.org, linux-kernel@vger.kernel.org, Jeff Xie Subject: [PATCH v8 1/4] trace: Add trace any kernel object Date: Sun, 30 Jan 2022 22:44:27 +0800 Message-Id: <20220130144430.453221-2-xiehuan09@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20220130144430.453221-1-xiehuan09@gmail.com> References: <20220130144430.453221-1-xiehuan09@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Introduce objtrace trigger to get the call flow by tracing any kernel object in the function parameter. The objtrace trigger makes a list of the target object address from the given event parameter, and records all kernel function calls which has the object address in its parameter. Syntax: objtrace:add:obj[:count][if ] Usage: # echo 'p bio_add_page arg1=$arg1' > kprobe_events # cd events/kprobes/p_bio_add_page_0 # echo 'objtrace:add:arg1:1 if comm == "cat"' > ./trigger # cat /test.txt Signed-off-by: Jeff Xie --- include/linux/trace_events.h | 1 + kernel/trace/Kconfig | 10 + kernel/trace/Makefile | 1 + kernel/trace/trace.c | 3 + kernel/trace/trace.h | 8 + kernel/trace/trace_entries.h | 17 + kernel/trace/trace_events_trigger.c | 1 + kernel/trace/trace_object.c | 465 ++++++++++++++++++++++++++++ kernel/trace/trace_output.c | 40 +++ 9 files changed, 546 insertions(+) create mode 100644 kernel/trace/trace_object.c diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index 70c069aef02c..3ccdbc1d25dd 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -685,6 +685,7 @@ enum event_trigger_type { ETT_EVENT_HIST = (1 << 4), ETT_HIST_ENABLE = (1 << 5), ETT_EVENT_EPROBE = (1 << 6), + ETT_TRACE_OBJECT = (1 << 7), }; extern int filter_match_preds(struct event_filter *filter, void *rec); diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index a5eb5e7fd624..c51b7eb1508d 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -250,6 +250,16 @@ config FUNCTION_PROFILER If in doubt, say N. +config TRACE_OBJECT + bool "Trace kernel object in function parameter" + depends on FUNCTION_TRACER + depends on HAVE_FUNCTION_ARG_ACCESS_API + select TRACING + default y + help + You can trace the kernel object in the kernel function parameter. + The kernel object is dynamically specified via event trigger. + config STACK_TRACER bool "Trace max stack" depends on HAVE_FUNCTION_TRACER diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index bedc5caceec7..b924b8e55922 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += trace_functions_graph.o obj-$(CONFIG_TRACE_BRANCH_PROFILING) += trace_branch.o obj-$(CONFIG_BLK_DEV_IO_TRACE) += blktrace.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += fgraph.o +obj-$(CONFIG_TRACE_OBJECT) += trace_object.o ifeq ($(CONFIG_BLOCK),y) obj-$(CONFIG_EVENT_TRACING) += blktrace.o endif diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index a734d5ae34c8..b4513c2bbd52 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -5605,6 +5605,9 @@ static const char readme_msg[] = "\t enable_hist::\n" "\t disable_hist::\n" #endif +#ifdef CONFIG_TRACE_OBJECT + "\t objtrace:add:obj[:count][if ]\n" +#endif #ifdef CONFIG_STACKTRACE "\t\t stacktrace\n" #endif diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 0f5e22238cd2..8b66515a36d5 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -54,6 +54,7 @@ enum trace_type { TRACE_TIMERLAT, TRACE_RAW_DATA, TRACE_FUNC_REPEATS, + TRACE_OBJECT, __TRACE_LAST_TYPE, }; @@ -472,6 +473,7 @@ extern void __ftrace_bad_type(void); TRACE_GRAPH_RET); \ IF_ASSIGN(var, ent, struct func_repeats_entry, \ TRACE_FUNC_REPEATS); \ + IF_ASSIGN(var, ent, struct trace_object_entry, TRACE_OBJECT);\ __ftrace_bad_type(); \ } while (0) @@ -1537,6 +1539,12 @@ static inline int register_trigger_hist_cmd(void) { return 0; } static inline int register_trigger_hist_enable_disable_cmds(void) { return 0; } #endif +#ifdef CONFIG_TRACE_OBJECT +extern int register_trigger_object_cmd(void); +#else +static inline int register_trigger_object_cmd(void) { return 0; } +#endif + extern int register_trigger_cmds(void); extern void clear_event_triggers(struct trace_array *tr); diff --git a/kernel/trace/trace_entries.h b/kernel/trace/trace_entries.h index cd41e863b51c..bb120d9498a9 100644 --- a/kernel/trace/trace_entries.h +++ b/kernel/trace/trace_entries.h @@ -401,3 +401,20 @@ FTRACE_ENTRY(timerlat, timerlat_entry, __entry->context, __entry->timer_latency) ); + +/* + * trace object entry: + */ +FTRACE_ENTRY(object, trace_object_entry, + + TRACE_OBJECT, + + F_STRUCT( + __field( unsigned long, ip ) + __field( unsigned long, parent_ip ) + __field( unsigned long, object ) + ), + + F_printk(" %ps <-- %ps object:%lx\n", + (void *)__entry->ip, (void *)__entry->parent_ip, __entry->object) +); diff --git a/kernel/trace/trace_events_trigger.c b/kernel/trace/trace_events_trigger.c index d00fee705f9c..c3371a6902af 100644 --- a/kernel/trace/trace_events_trigger.c +++ b/kernel/trace/trace_events_trigger.c @@ -2025,6 +2025,7 @@ __init int register_trigger_cmds(void) register_trigger_enable_disable_cmds(); register_trigger_hist_enable_disable_cmds(); register_trigger_hist_cmd(); + register_trigger_object_cmd(); return 0; } diff --git a/kernel/trace/trace_object.c b/kernel/trace/trace_object.c new file mode 100644 index 000000000000..1eb485c513e9 --- /dev/null +++ b/kernel/trace/trace_object.c @@ -0,0 +1,465 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * trace the kernel object in the kernel function parameter + * Copyright (C) 2021 Jeff Xie + */ + +#define pr_fmt(fmt) "trace_object: " fmt + +#include "trace_output.h" + +#define MAX_TRACED_OBJECT 5 +static DEFINE_PER_CPU(unsigned int, trace_object_event_disable); +static DEFINE_RAW_SPINLOCK(trace_obj_lock); +static struct trace_event_file event_trace_file; +static const int max_args_num = 6; +static atomic_t trace_object_ref; +static atomic_t num_traced_obj; +static int exit_trace_object(void); +static int init_trace_object(void); + +static struct object_instance { + void *obj; +} traced_obj[MAX_TRACED_OBJECT]; + +static bool object_exist(void *obj) +{ + int i, max; + + max = atomic_read(&num_traced_obj); + smp_rmb(); + for (i = 0; i < max; i++) { + if (traced_obj[i].obj == obj) + return true; + } + return false; +} + +static bool object_empty(void) +{ + return !atomic_read(&num_traced_obj); +} + +static void set_trace_object(void *obj) +{ + unsigned long flags; + + if (in_nmi()) + return; + + if (!obj) + return; + + if (object_exist(obj)) + return; + + /* only this place has write operations */ + raw_spin_lock_irqsave(&trace_obj_lock, flags); + if (atomic_read(&num_traced_obj) == MAX_TRACED_OBJECT) { + trace_printk("object_pool is full, can't trace object:0x%px\n", obj); + goto out; + } + traced_obj[atomic_read(&num_traced_obj)].obj = obj; + /* make sure the num_traced_obj update always appears after traced_obj update */ + smp_wmb(); + atomic_inc(&num_traced_obj); +out: + raw_spin_unlock_irqrestore(&trace_obj_lock, flags); +} + +static void submit_trace_object(unsigned long ip, unsigned long parent_ip, + unsigned long object) +{ + + struct trace_buffer *buffer; + struct ring_buffer_event *event; + struct trace_object_entry *entry; + int pc; + + pc = preempt_count(); + event = trace_event_buffer_lock_reserve(&buffer, &event_trace_file, + TRACE_OBJECT, sizeof(*entry), pc); + if (!event) + return; + entry = ring_buffer_event_data(event); + entry->ip = ip; + entry->parent_ip = parent_ip; + entry->object = object; + + event_trigger_unlock_commit(&event_trace_file, buffer, event, + entry, pc); +} + +static void +trace_object_events_call(unsigned long ip, unsigned long parent_ip, + struct ftrace_ops *op, struct ftrace_regs *fregs) +{ + struct pt_regs *pt_regs = ftrace_get_regs(fregs); + unsigned long obj; + unsigned int disabled; + int n; + + preempt_disable_notrace(); + + disabled = this_cpu_inc_return(trace_object_event_disable); + if (disabled != 1) + goto out; + + if (object_empty()) + goto out; + + for (n = 0; n < max_args_num; n++) { + obj = regs_get_kernel_argument(pt_regs, n); + if (object_exist((void *)obj)) + submit_trace_object(ip, parent_ip, obj); + /* The parameters of a function may match multiple objects */ + } +out: + this_cpu_dec(trace_object_event_disable); + preempt_enable_notrace(); +} + +static struct ftrace_ops trace_ops = { + .func = trace_object_events_call, + .flags = FTRACE_OPS_FL_SAVE_REGS, +}; + +static void +trace_object_trigger(struct event_trigger_data *data, + struct trace_buffer *buffer, void *rec, + struct ring_buffer_event *event) +{ + + struct ftrace_event_field *field = data->private_data; + void *obj = NULL; + + memcpy(&obj, rec + field->offset, sizeof(obj)); + set_trace_object(obj); +} + +static void +trace_object_trigger_free(struct event_trigger_ops *ops, + struct event_trigger_data *data) +{ + if (WARN_ON_ONCE(data->ref <= 0)) + return; + + data->ref--; + if (!data->ref) + trigger_data_free(data); +} + +static void +trace_object_count_trigger(struct event_trigger_data *data, + struct trace_buffer *buffer, void *rec, + struct ring_buffer_event *event) +{ + if (!data->count) + return; + + if (data->count != -1) + (data->count)--; + + trace_object_trigger(data, buffer, rec, event); +} + +static int event_object_trigger_init(struct event_trigger_ops *ops, + struct event_trigger_data *data) +{ + data->ref++; + return 0; +} + +static int +event_trigger_print(const char *name, struct seq_file *m, + void *data, char *filter_str) +{ + long count = (long)data; + + seq_puts(m, name); + + if (count == -1) + seq_puts(m, ":unlimited"); + else + seq_printf(m, ":count=%ld", count); + + if (filter_str) + seq_printf(m, " if %s\n", filter_str); + else + seq_putc(m, '\n'); + + return 0; +} + +static int +trace_object_trigger_print(struct seq_file *m, struct event_trigger_ops *ops, + struct event_trigger_data *data) +{ + return event_trigger_print("objtrace", m, (void *)data->count, + data->filter_str); +} + +static struct event_trigger_ops objecttrace_trigger_ops = { + .trigger = trace_object_trigger, + .print = trace_object_trigger_print, + .init = event_object_trigger_init, + .free = trace_object_trigger_free, +}; + +static struct event_trigger_ops objecttrace_count_trigger_ops = { + .trigger = trace_object_count_trigger, + .print = trace_object_trigger_print, + .init = event_object_trigger_init, + .free = trace_object_trigger_free, +}; + +static struct event_trigger_ops * +objecttrace_get_trigger_ops(char *cmd, char *param) +{ + return param ? &objecttrace_count_trigger_ops : &objecttrace_trigger_ops; +} + +static int register_object_trigger(char *glob, + struct event_trigger_data *data, + struct trace_event_file *file) +{ + struct event_trigger_data *test; + int ret = 0; + + lockdep_assert_held(&event_mutex); + + list_for_each_entry(test, &file->triggers, list) { + if (test->cmd_ops->trigger_type == data->cmd_ops->trigger_type) { + ret = -EEXIST; + goto out; + } + } + + if (data->ops->init) { + ret = data->ops->init(data->ops, data); + if (ret < 0) + goto out; + } + + list_add_rcu(&data->list, &file->triggers); + ret++; + + update_cond_flag(file); + if (trace_event_trigger_enable_disable(file, 1) < 0) { + list_del_rcu(&data->list); + update_cond_flag(file); + ret--; + } + init_trace_object(); +out: + return ret; +} + +static void unregister_object_trigger(char *glob, + struct event_trigger_data *test, + struct trace_event_file *file) +{ + struct event_trigger_data *data; + bool unregistered = false; + + lockdep_assert_held(&event_mutex); + + list_for_each_entry(data, &file->triggers, list) { + if (data->cmd_ops->trigger_type == test->cmd_ops->trigger_type) { + unregistered = true; + list_del_rcu(&data->list); + trace_event_trigger_enable_disable(file, 0); + update_cond_flag(file); + break; + } + } + + if (unregistered) { + if (data->ops->free) + data->ops->free(data->ops, data); + exit_trace_object(); + } +} + +static int +event_object_trigger_parse(struct event_command *cmd_ops, + struct trace_event_file *file, + char *glob, char *cmd, char *param) +{ + struct event_trigger_data *trigger_data; + struct event_trigger_ops *trigger_ops; + struct trace_event_call *call; + struct ftrace_event_field *field; + char *objtrace_cmd; + char *trigger = NULL; + char *arg; + char *number; + int ret; + + ret = -EINVAL; + if (!param) + goto out; + + /* separate the trigger from the filter (c:a:n [if filter]) */ + trigger = strsep(¶m, " \t"); + if (!trigger) + goto out; + if (param) { + param = skip_spaces(param); + if (!*param) + param = NULL; + } + + objtrace_cmd = strsep(&trigger, ":"); + if (!objtrace_cmd || strcmp(objtrace_cmd, "add")) + goto out; + + arg = strsep(&trigger, ":"); + if (!arg) + goto out; + call = file->event_call; + field = trace_find_event_field(call, arg); + if (!field) + goto out; + + if (field->size != sizeof(void *)) + goto out; + + trigger_ops = cmd_ops->get_trigger_ops(cmd, trigger); + + ret = -ENOMEM; + trigger_data = kzalloc(sizeof(*trigger_data), GFP_KERNEL); + if (!trigger_data) + goto out; + + trigger_data->count = -1; + trigger_data->ops = trigger_ops; + trigger_data->cmd_ops = cmd_ops; + trigger_data->private_data = field; + INIT_LIST_HEAD(&trigger_data->list); + INIT_LIST_HEAD(&trigger_data->named_list); + + if (glob[0] == '!') { + cmd_ops->unreg(glob+1, trigger_data, file); + kfree(trigger_data); + ret = 0; + goto out; + } + + if (trigger) { + number = strsep(&trigger, ":"); + + ret = -EINVAL; + if (!strlen(number)) + goto out_free; + + /* + * We use the callback data field (which is a pointer) + * as our counter. + */ + ret = kstrtoul(number, 0, &trigger_data->count); + if (ret) + goto out_free; + } + + if (!param) /* if param is non-empty, it's supposed to be a filter */ + goto out_reg; + + if (!cmd_ops->set_filter) + goto out_reg; + + ret = cmd_ops->set_filter(param, trigger_data, file); + if (ret < 0) + goto out_free; + + out_reg: + /* Up the trigger_data count to make sure reg doesn't free it on failure */ + event_object_trigger_init(trigger_ops, trigger_data); + ret = cmd_ops->reg(glob, trigger_data, file); + /* + * The above returns on success the # of functions enabled, + * but if it didn't find any functions it returns zero. + * Consider no functions a failure too. + */ + if (!ret) { + cmd_ops->unreg(glob, trigger_data, file); + ret = -ENOENT; + } else if (ret > 0) + ret = 0; + + /* Down the counter of trigger_data or free it if not used anymore */ + trace_object_trigger_free(trigger_ops, trigger_data); + out: + return ret; + + out_free: + if (cmd_ops->set_filter) + cmd_ops->set_filter(NULL, trigger_data, NULL); + kfree(trigger_data); + goto out; +} + +static struct event_command trigger_object_cmd = { + .name = "objtrace", + .trigger_type = ETT_TRACE_OBJECT, + .flags = EVENT_CMD_FL_NEEDS_REC, + .parse = event_object_trigger_parse, + .reg = register_object_trigger, + .unreg = unregister_object_trigger, + .get_trigger_ops = objecttrace_get_trigger_ops, + .set_filter = set_trigger_filter, +}; + +__init int register_trigger_object_cmd(void) +{ + int ret; + + ret = register_event_command(&trigger_object_cmd); + WARN_ON(ret < 0); + + return ret; +} + +static int init_trace_object(void) +{ + int ret; + + if (atomic_inc_return(&trace_object_ref) != 1) { + ret = 0; + goto out; + } + + event_trace_file.tr = top_trace_array(); + if (WARN_ON(!event_trace_file.tr)) { + ret = -1; + atomic_dec(&trace_object_ref); + goto out; + } + ret = register_ftrace_function(&trace_ops); +out: + return ret; +} + +static int exit_trace_object(void) +{ + int ret; + + if (WARN_ON_ONCE(atomic_read(&trace_object_ref) <= 0)) { + ret = -1; + goto out; + } + + if (atomic_dec_return(&trace_object_ref) != 0) { + ret = 0; + goto out; + } + + ret = unregister_ftrace_function(&trace_ops); + if (ret) { + pr_err("can't unregister ftrace for trace object\n"); + goto out; + } + atomic_set(&num_traced_obj, 0); +out: + return ret; +} diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index 8aa493d25c73..265428154638 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -1547,6 +1547,45 @@ static struct trace_event trace_func_repeats_event = { .funcs = &trace_func_repeats_funcs, }; +/* TRACE_OBJECT */ +static enum print_line_t trace_object_print(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct trace_object_entry *field; + struct trace_seq *s = &iter->seq; + + trace_assign_type(field, iter->ent); + print_fn_trace(s, field->ip, field->parent_ip, flags); + trace_seq_printf(s, " object:0x%lx", field->object); + trace_seq_putc(s, '\n'); + + return trace_handle_return(s); +} + +static enum print_line_t trace_object_raw(struct trace_iterator *iter, int flags, + struct trace_event *event) +{ + struct trace_object_entry *field; + + trace_assign_type(field, iter->ent); + + trace_seq_printf(&iter->seq, "%lx %lx\n", + field->ip, + field->parent_ip); + + return trace_handle_return(&iter->seq); +} + +static struct trace_event_functions trace_object_funcs = { + .trace = trace_object_print, + .raw = trace_object_raw, +}; + +static struct trace_event trace_object_event = { + .type = TRACE_OBJECT, + .funcs = &trace_object_funcs, +}; + static struct trace_event *events[] __initdata = { &trace_fn_event, &trace_ctx_event, @@ -1561,6 +1600,7 @@ static struct trace_event *events[] __initdata = { &trace_timerlat_event, &trace_raw_data_event, &trace_func_repeats_event, + &trace_object_event, NULL }; -- 2.25.1