mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
ftrace updates for v7.4:
- Have the stacktrace option work with func-args The func_stack_trace option has it's own ftrace handler and so does the func-args option. When they are both set, neither would run. The function tracer itself would error out. Allow the stacktrace and function args to work together. - Avoid BTF lookup in atomic context The ftrace_dump() can call function graph tracing output from an atomic context (like an NMI watchdog going off). The BTF lookup is not safe to call from atomic context. Have btf_find_func_proto() return if in atomic context. - Build trace_btf.c when CONFIG_DEBUG_INFO_BTF is enabled New code is depending on trace_btf.c but that only gets built when BPF_SYSCALL is also enabled. As the new code doesn't depend on that, have trace_btf.c get built with just DEBUG_INFO_BTF set. - Have function graph return values have BTF processing Have the return values printed by the function graph tracer use BTF to print the values in better format. For example, if it's a char, short, signed or unsigned, print according to the value defined by BTF. - Add a "max" field to the function profiler stats The function profiler shows all the functions run in the kernel, the number of times they are called, and the total time they took up. Also add a "max" field which holds the longest time a function ran for. - Fix memory leak in module with all skipped functions On module load, the ftrace entries are recorded to keep track of what functions can be traced or not. Some functions may be skipped for various reasons (some defined by the architecture). If all functions in the module are skipped, its data page is not freed and can leak. Catch this case and free the empty page. - Allow architectures to reject functions from being traced Allow architectures to skip a function address by returning zero from ftrace_call_adjust(). - Initialize modname and offset in print_rec() The variables modname and offset are initialized by kallsyms_lookup(). If that fails, the values are not set, but the code continues to print other information. The modname and offset are used later, but if the kallsyms_lookup() failed, they will not be set. If a module is being loaded and unloaded at the same time as reading the available_filter_functions file, it can cause a fault due to the lookup failing and modname and offset being used uninitialized. -----BEGIN PGP SIGNATURE----- iIoEABYKADIWIQRRSw7ePDh/lE+zeZMp5XQQmuv6qgUCatplBBQccm9zdGVkdEBn b29kbWlzLm9yZwAKCRAp5XQQmuv6qgAvAQD0xgDPtb6+4T9lB7ZerQwFWHZ2roO7 ghQY+yqcX8s4TwEAn7B85quPdz7XvGzQa3KeKBXMFfpogJx4vljJzpPQoAg= =SgDW -----END PGP SIGNATURE----- Merge tag 'trace-ftrace-v7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace Pull ftrace updates from Steven Rostedt: - Have the stacktrace option work with func-args The func_stack_trace option has it's own ftrace handler and so does the func-args option. When they are both set, neither would run. The function tracer itself would error out. Allow the stacktrace and function args to work together. - Avoid BTF lookup in atomic context The ftrace_dump() can call function graph tracing output from an atomic context (like an NMI watchdog going off). The BTF lookup is not safe to call from atomic context. Have btf_find_func_proto() return if in atomic context. - Build trace_btf.c when CONFIG_DEBUG_INFO_BTF is enabled New code is depending on trace_btf.c but that only gets built when BPF_SYSCALL is also enabled. As the new code doesn't depend on that, have trace_btf.c get built with just DEBUG_INFO_BTF set. - Have function graph return values have BTF processing Have the return values printed by the function graph tracer use BTF to print the values in better format. For example, if it's a char, short, signed or unsigned, print according to the value defined by BTF. - Add a "max" field to the function profiler stats The function profiler shows all the functions run in the kernel, the number of times they are called, and the total time they took up. Also add a "max" field which holds the longest time a function ran for. - Fix memory leak in module with all skipped functions On module load, the ftrace entries are recorded to keep track of what functions can be traced or not. Some functions may be skipped for various reasons (some defined by the architecture). If all functions in the module are skipped, its data page is not freed and can leak. Catch this case and free the empty page. - Allow architectures to reject functions from being traced Allow architectures to skip a function address by returning zero from ftrace_call_adjust(). - Initialize modname and offset in print_rec() The variables modname and offset are initialized by kallsyms_lookup(). If that fails, the values are not set, but the code continues to print other information. The modname and offset are used later, but if the kallsyms_lookup() failed, they will not be set. If a module is being loaded and unloaded at the same time as reading the available_filter_functions file, it can cause a fault due to the lookup failing and modname and offset being used uninitialized. * tag 'trace-ftrace-v7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: ftrace: Initialize modname and offset in print_rec() ftrace: Let ftrace_call_adjust() reject a patch site ftrace: Do not leak a module's empty page group ftrace: Add max field to function profiler stats tracing: Update funcgraph-retval documentation fgraph: Enhance funcgraph-retval with BTF-based type-aware output ftrace: Build trace_btf.c when CONFIG_DEBUG_INFO_BTF is enabled tracing: Avoid BTF lookup in atomic context ftrace: Have stacktrace option work with func-args
This commit is contained in:
commit
b8a7a86e48
|
|
@ -1483,6 +1483,10 @@ Options for function_graph tracer:
|
|||
printed in hexadecimal format. By default, this option
|
||||
is off.
|
||||
|
||||
funcgraph-retaddr
|
||||
When set, the return address will always be printed.
|
||||
By default, this option is off.
|
||||
|
||||
sleep-time
|
||||
When running function graph tracer, to include
|
||||
the time a task schedules out in its function.
|
||||
|
|
@ -2829,7 +2833,7 @@ It is default disabled.
|
|||
0) 2.861 us | } /* putname() */
|
||||
|
||||
The return value of each traced function can be displayed after
|
||||
an equal sign "=". When encountering system call failures, it
|
||||
an equal sign "ret=". When encountering system call failures, it
|
||||
can be very helpful to quickly locate the function that first
|
||||
returns an error code.
|
||||
|
||||
|
|
@ -2839,16 +2843,16 @@ returns an error code.
|
|||
Example with funcgraph-retval::
|
||||
|
||||
1) | cgroup_migrate() {
|
||||
1) 0.651 us | cgroup_migrate_add_task(); /* = 0xffff93fcfd346c00 */
|
||||
1) 0.651 us | cgroup_migrate_add_task(); /* ret=0xffff93fcfd346c00 */
|
||||
1) | cgroup_migrate_execute() {
|
||||
1) | cpu_cgroup_can_attach() {
|
||||
1) | cgroup_taskset_first() {
|
||||
1) 0.732 us | cgroup_taskset_next(); /* = 0xffff93fc8fb20000 */
|
||||
1) 1.232 us | } /* cgroup_taskset_first = 0xffff93fc8fb20000 */
|
||||
1) 0.380 us | sched_rt_can_attach(); /* = 0x0 */
|
||||
1) 2.335 us | } /* cpu_cgroup_can_attach = -22 */
|
||||
1) 4.369 us | } /* cgroup_migrate_execute = -22 */
|
||||
1) 7.143 us | } /* cgroup_migrate = -22 */
|
||||
1) 0.732 us | cgroup_taskset_next(); /* ret=0xffff93fc8fb20000 */
|
||||
1) 1.232 us | } /* cgroup_taskset_first ret=0xffff93fc8fb20000 */
|
||||
1) 0.380 us | sched_rt_can_attach(); /* ret=0x0 */
|
||||
1) 2.335 us | } /* cpu_cgroup_can_attach ret=-22 */
|
||||
1) 4.369 us | } /* cgroup_migrate_execute ret=-22 */
|
||||
1) 7.143 us | } /* cgroup_migrate ret=-22 */
|
||||
|
||||
The above example shows that the function cpu_cgroup_can_attach
|
||||
returned the error code -22 firstly, then we can read the code
|
||||
|
|
@ -2859,43 +2863,55 @@ be displayed in a smart way. Specifically, if it is an error code,
|
|||
it will be printed in signed decimal format, otherwise it will
|
||||
printed in hexadecimal format.
|
||||
|
||||
When CONFIG_DEBUG_INFO_BTF is enabled, the smart format also takes the
|
||||
declared return type into account. Signed integer and signed enum return
|
||||
values are printed in signed decimal format, unsigned enum return values
|
||||
are printed in unsigned decimal format, and unsigned integer, pointer, and
|
||||
aggregate return values are printed in hexadecimal format. Boolean return
|
||||
values are printed as ``true`` or ``false``. If the BTF type is unavailable,
|
||||
the legacy error-code detection is used.
|
||||
|
||||
- smart: echo nofuncgraph-retval-hex > trace_options
|
||||
- hexadecimal: echo funcgraph-retval-hex > trace_options
|
||||
|
||||
Example with funcgraph-retval-hex::
|
||||
|
||||
1) | cgroup_migrate() {
|
||||
1) 0.651 us | cgroup_migrate_add_task(); /* = 0xffff93fcfd346c00 */
|
||||
1) 0.651 us | cgroup_migrate_add_task(); /* ret=0xffff93fcfd346c00 */
|
||||
1) | cgroup_migrate_execute() {
|
||||
1) | cpu_cgroup_can_attach() {
|
||||
1) | cgroup_taskset_first() {
|
||||
1) 0.732 us | cgroup_taskset_next(); /* = 0xffff93fc8fb20000 */
|
||||
1) 1.232 us | } /* cgroup_taskset_first = 0xffff93fc8fb20000 */
|
||||
1) 0.380 us | sched_rt_can_attach(); /* = 0x0 */
|
||||
1) 2.335 us | } /* cpu_cgroup_can_attach = 0xffffffea */
|
||||
1) 4.369 us | } /* cgroup_migrate_execute = 0xffffffea */
|
||||
1) 7.143 us | } /* cgroup_migrate = 0xffffffea */
|
||||
1) 0.732 us | cgroup_taskset_next(); /* ret=0xffff93fc8fb20000 */
|
||||
1) 1.232 us | } /* cgroup_taskset_first ret=0xffff93fc8fb20000 */
|
||||
1) 0.380 us | sched_rt_can_attach(); /* ret=0x0 */
|
||||
1) 2.335 us | } /* cpu_cgroup_can_attach ret=0xffffffea */
|
||||
1) 4.369 us | } /* cgroup_migrate_execute ret=0xffffffea */
|
||||
1) 7.143 us | } /* cgroup_migrate ret=0xffffffea */
|
||||
|
||||
At present, there are some limitations when using the funcgraph-retval
|
||||
option, and these limitations will be eliminated in the future:
|
||||
Note that there are some limitations when using the funcgraph-retval
|
||||
option:
|
||||
|
||||
- Even if the function return type is void, a return value will still
|
||||
be printed, and you can just ignore it.
|
||||
- If CONFIG_DEBUG_INFO_BTF is disabled (n), a return value is printed even for
|
||||
functions with a void return type. When CONFIG_DEBUG_INFO_BTF is enabled (y),
|
||||
the return value is printed only for non-void functions.
|
||||
|
||||
- Even if return values are stored in multiple registers, only the
|
||||
value contained in the first register will be recorded and printed.
|
||||
To illustrate, in the x86 architecture, eax and edx are used to store
|
||||
a 64-bit return value, with the lower 32 bits saved in eax and the
|
||||
upper 32 bits saved in edx. However, only the value stored in eax
|
||||
will be recorded and printed.
|
||||
- If a return value occupies multiple registers, only the value in the first
|
||||
register is recorded and printed. For example, on the x86 architecture, a
|
||||
64-bit return value is stored across eax (lower 32 bits) and edx (upper 32 bits),
|
||||
but only the contents of eax are captured. If CONFIG_DEBUG_INFO_BTF is enabled,
|
||||
the suffix "(trunc)" is appended to the printed value to indicate that the
|
||||
output may be truncated because high-order register contents are omitted.
|
||||
|
||||
- In certain procedure call standards, such as arm64's AAPCS64, when a
|
||||
type is smaller than a GPR, it is the responsibility of the consumer
|
||||
to perform the narrowing, and the upper bits may contain UNKNOWN values.
|
||||
Therefore, it is advisable to check the code for such cases. For instance,
|
||||
when using a u8 in a 64-bit GPR, bits [63:8] may contain arbitrary values,
|
||||
especially when larger types are truncated, whether explicitly or implicitly.
|
||||
Here are some specific cases to illustrate this point:
|
||||
- Under certain procedure-call standards (e.g., arm64's AAPCS64), when the return
|
||||
type is smaller than a general-purpose register (GPR), the caller is responsible
|
||||
for narrowing the value; the upper bits of the register may contain undefined data.
|
||||
For instance, when a u8 is returned in 64-bit GPR, bits [63:8] can hold arbitrary
|
||||
values, especially when larger types are truncated (explicitly or implicitly). It
|
||||
is therefore advisable to inspect the code in such cases. If CONFIG_DEBUG_INFO_BTF
|
||||
is enabled (y), the return value is automatically trimmed to the width of the return
|
||||
type.
|
||||
|
||||
The following examples illustrate the behavior:
|
||||
|
||||
**Case One**:
|
||||
|
||||
|
|
@ -2914,7 +2930,9 @@ option, and these limitations will be eliminated in the future:
|
|||
RET
|
||||
|
||||
If you pass 0x123456789abcdef to this function and want to narrow it,
|
||||
it may be recorded as 0x123456789abcdef instead of 0xef.
|
||||
it may be recorded as 0x123456789abcdef instead of 0xef. When
|
||||
CONFIG_DEBUG_INFO_BTF is enabled, the value will be correctly truncated
|
||||
to 0xef based on the size constraints of the u8 type.
|
||||
|
||||
**Case Two**:
|
||||
|
||||
|
|
@ -2939,7 +2957,9 @@ option, and these limitations will be eliminated in the future:
|
|||
RET
|
||||
|
||||
When passing 0x2_0000_0000 to it, the return value may be recorded as
|
||||
0x2_0000_0000 instead of 0.
|
||||
0x2_0000_0000 instead of 0. When CONFIG_DEBUG_INFO_BTF is enabled, the
|
||||
value will be correctly truncated to 0 based on the size constraints of
|
||||
the int type.
|
||||
|
||||
You can put some comments on specific functions by using
|
||||
trace_printk() For example, if you want to put a comment inside
|
||||
|
|
|
|||
|
|
@ -770,7 +770,7 @@ config FPROBE_EVENTS
|
|||
config PROBE_EVENTS_BTF_ARGS
|
||||
depends on HAVE_FUNCTION_ARG_ACCESS_API
|
||||
depends on FPROBE_EVENTS || KPROBE_EVENTS
|
||||
depends on DEBUG_INFO_BTF && BPF_SYSCALL
|
||||
depends on DEBUG_INFO_BTF
|
||||
bool "Support BTF function arguments for probe events"
|
||||
default y
|
||||
help
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ obj-$(CONFIG_KGDB_KDB) += trace_kdb.o
|
|||
endif
|
||||
obj-$(CONFIG_DYNAMIC_EVENTS) += trace_dynevent.o
|
||||
obj-$(CONFIG_PROBE_EVENTS) += trace_probe.o
|
||||
obj-$(CONFIG_PROBE_EVENTS_BTF_ARGS) += trace_btf.o
|
||||
obj-$(CONFIG_DEBUG_INFO_BTF) += trace_btf.o
|
||||
obj-$(CONFIG_UPROBE_EVENTS) += trace_uprobe.o
|
||||
obj-$(CONFIG_BOOTTIME_TRACING) += trace_boot.o
|
||||
obj-$(CONFIG_FTRACE_RECORD_RECURSION) += trace_recursion_record.o
|
||||
|
|
|
|||
|
|
@ -428,6 +428,7 @@ struct ftrace_profile {
|
|||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
unsigned long long time;
|
||||
unsigned long long time_squared;
|
||||
unsigned long long time_max;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
@ -530,9 +531,9 @@ static int function_stat_headers(struct seq_file *m)
|
|||
{
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
|
||||
seq_puts(m, " Function "
|
||||
"Hit Time Avg s^2\n"
|
||||
"Hit Time Avg s^2 max\n"
|
||||
" -------- "
|
||||
"--- ---- --- ---\n");
|
||||
"--- ---- --- --- ---\n");
|
||||
#else
|
||||
seq_puts(m, " Function Hit\n"
|
||||
" -------- ---\n");
|
||||
|
|
@ -613,6 +614,8 @@ static int function_stat_show(struct seq_file *m, void *v)
|
|||
trace_print_graph_duration(avg, &s);
|
||||
trace_seq_puts(&s, " ");
|
||||
trace_print_graph_duration(stddev, &s);
|
||||
trace_seq_puts(&s, " ");
|
||||
trace_print_graph_duration(rec->time_max, &s);
|
||||
trace_print_seq(m, &s);
|
||||
#endif
|
||||
seq_putc(m, '\n');
|
||||
|
|
@ -920,6 +923,8 @@ static void profile_graph_return(struct ftrace_graph_ret *trace,
|
|||
if (rec) {
|
||||
rec->time += calltime;
|
||||
rec->time_squared += calltime * calltime;
|
||||
if (calltime > rec->time_max)
|
||||
rec->time_max = calltime;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4426,9 +4431,9 @@ subsys_initcall(ftrace_check_for_weak_functions);
|
|||
|
||||
static int print_rec(struct seq_file *m, unsigned long ip)
|
||||
{
|
||||
unsigned long offset;
|
||||
unsigned long offset = 0;
|
||||
char str[KSYM_SYMBOL_LEN];
|
||||
char *modname;
|
||||
char *modname = NULL;
|
||||
const char *ret;
|
||||
|
||||
ret = kallsyms_lookup(ip, NULL, &offset, &modname, str);
|
||||
|
|
@ -7702,6 +7707,11 @@ static int ftrace_process_locs(struct module *mod,
|
|||
}
|
||||
|
||||
addr = ftrace_call_adjust(addr);
|
||||
/* The architecture may return 0 to reject a patch site */
|
||||
if (!addr) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
|
||||
end_offset = (pg->index+1) * sizeof(pg->records[0]);
|
||||
if (end_offset > PAGE_SIZE << pg->order) {
|
||||
|
|
@ -7715,6 +7725,20 @@ static int ftrace_process_locs(struct module *mod,
|
|||
rec->ip = addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* ftrace_release_mod() finds a module's page groups by their first
|
||||
* record. If every entry was skipped there is none, so unlink the
|
||||
* new page groups and free them now.
|
||||
*/
|
||||
if (mod && !start_pg->index) {
|
||||
ftrace_pages->next = NULL;
|
||||
mutex_unlock(&ftrace_lock);
|
||||
/* Need to synchronize with ftrace_location_range() */
|
||||
synchronize_rcu();
|
||||
ftrace_free_pages(start_pg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pg->next) {
|
||||
pg_unuse = pg->next;
|
||||
pg->next = NULL;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include <linux/btf.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kallsyms.h>
|
||||
|
||||
#include "trace_btf.h"
|
||||
|
||||
|
|
@ -15,6 +16,9 @@ const struct btf_type *btf_find_func_proto(const char *func_name, struct btf **b
|
|||
const struct btf_type *t;
|
||||
s32 id;
|
||||
|
||||
if (unlikely(in_atomic() || irqs_disabled()))
|
||||
return NULL;
|
||||
|
||||
id = bpf_find_btf_id(func_name, BTF_KIND_FUNC, btf_p);
|
||||
if (id < 0)
|
||||
return NULL;
|
||||
|
|
@ -123,3 +127,85 @@ const struct btf_member *btf_find_struct_member(struct btf *btf,
|
|||
return member;
|
||||
}
|
||||
|
||||
void btf_trim_retval(unsigned long func, unsigned long *retval, bool *print_retval,
|
||||
int *fmt, bool hex)
|
||||
{
|
||||
const struct btf_type *t;
|
||||
char name[KSYM_NAME_LEN];
|
||||
struct btf *btf;
|
||||
u32 v, msb;
|
||||
bool signed_type;
|
||||
int kind;
|
||||
|
||||
if (lookup_symbol_name(func, name))
|
||||
return;
|
||||
|
||||
t = btf_find_func_proto(name, &btf);
|
||||
if (IS_ERR_OR_NULL(t))
|
||||
return;
|
||||
|
||||
t = btf_type_skip_modifiers(btf, t->type, NULL);
|
||||
kind = t ? BTF_INFO_KIND(t->info) : BTF_KIND_UNKN;
|
||||
switch (kind) {
|
||||
case BTF_KIND_UNKN:
|
||||
*print_retval = false;
|
||||
break;
|
||||
case BTF_KIND_STRUCT:
|
||||
case BTF_KIND_UNION:
|
||||
case BTF_KIND_ENUM:
|
||||
case BTF_KIND_ENUM64:
|
||||
if (kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION) {
|
||||
*fmt = RETVAL_FMT_HEX;
|
||||
signed_type = false;
|
||||
} else {
|
||||
*fmt = RETVAL_FMT_DEC;
|
||||
signed_type = btf_type_kflag(t);
|
||||
if (!signed_type)
|
||||
*fmt |= RETVAL_FMT_UNSIGNED;
|
||||
}
|
||||
|
||||
if (t->size > sizeof(unsigned long)) {
|
||||
*fmt |= RETVAL_FMT_TRUNC;
|
||||
msb = BITS_PER_LONG - 1;
|
||||
} else {
|
||||
msb = min_t(u32, BITS_PER_BYTE * t->size - 1,
|
||||
BITS_PER_LONG - 1);
|
||||
*retval &= GENMASK(msb, 0);
|
||||
}
|
||||
if (signed_type && t->size && !hex)
|
||||
*retval = sign_extend64(*retval, msb);
|
||||
break;
|
||||
case BTF_KIND_INT:
|
||||
v = *(u32 *)(t + 1);
|
||||
signed_type = false;
|
||||
if (BTF_INT_ENCODING(v) == BTF_INT_BOOL) {
|
||||
*fmt = RETVAL_FMT_BOOL;
|
||||
msb = 0;
|
||||
} else {
|
||||
signed_type = BTF_INT_ENCODING(v) & BTF_INT_SIGNED;
|
||||
if (signed_type)
|
||||
*fmt = RETVAL_FMT_DEC;
|
||||
else
|
||||
*fmt = RETVAL_FMT_HEX;
|
||||
|
||||
if (t->size > sizeof(unsigned long)) {
|
||||
*fmt |= RETVAL_FMT_TRUNC;
|
||||
msb = BITS_PER_LONG - 1;
|
||||
} else {
|
||||
msb = min_t(u32, BTF_INT_BITS(v) - 1,
|
||||
BITS_PER_LONG - 1);
|
||||
}
|
||||
}
|
||||
*retval &= GENMASK(msb, 0);
|
||||
if (signed_type && !hex)
|
||||
*retval = sign_extend64(*retval, msb);
|
||||
break;
|
||||
default:
|
||||
*fmt = RETVAL_FMT_HEX;
|
||||
break;
|
||||
}
|
||||
|
||||
if (*print_retval)
|
||||
*fmt |= RETVAL_FMT_BTF;
|
||||
btf_put(btf);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#include <linux/btf.h>
|
||||
|
||||
enum {
|
||||
RETVAL_FMT_HEX = BIT(0),
|
||||
RETVAL_FMT_DEC = BIT(1),
|
||||
RETVAL_FMT_BOOL = BIT(2),
|
||||
RETVAL_FMT_TRUNC = BIT(3),
|
||||
RETVAL_FMT_BTF = BIT(4),
|
||||
RETVAL_FMT_UNSIGNED = BIT(5),
|
||||
};
|
||||
|
||||
const struct btf_type *btf_find_func_proto(const char *func_name,
|
||||
struct btf **btf_p);
|
||||
const struct btf_param *btf_get_func_param(const struct btf_type *func_proto,
|
||||
|
|
@ -10,3 +19,12 @@ const struct btf_member *btf_find_struct_member(struct btf *btf,
|
|||
const char *member_name,
|
||||
u32 *anon_offset,
|
||||
const struct btf_type **member_type);
|
||||
#ifdef CONFIG_DEBUG_INFO_BTF
|
||||
void btf_trim_retval(unsigned long func, unsigned long *retval, bool *print_retval,
|
||||
int *fmt, bool hex);
|
||||
#else
|
||||
static inline void btf_trim_retval(unsigned long func, unsigned long *retval,
|
||||
bool *print_retval, int *fmt, bool hex)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ static ftrace_func_t select_trace_function(u32 flags_val)
|
|||
case TRACE_FUNC_OPT_ARGS:
|
||||
return function_args_trace_call;
|
||||
case TRACE_FUNC_OPT_STACK:
|
||||
case TRACE_FUNC_OPT_STACK | TRACE_FUNC_OPT_ARGS:
|
||||
return function_stack_trace_call;
|
||||
case TRACE_FUNC_OPT_NO_REPEATS:
|
||||
return function_no_repeats_trace_call;
|
||||
|
|
@ -295,7 +296,9 @@ function_stack_trace_call(unsigned long ip, unsigned long parent_ip,
|
|||
|
||||
if (likely(disabled == 1)) {
|
||||
trace_ctx = tracing_gen_ctx_flags(flags);
|
||||
trace_function(tr, ip, parent_ip, trace_ctx, NULL);
|
||||
if (!(tr->current_trace_flags->val & TRACE_FUNC_OPT_ARGS))
|
||||
fregs = NULL;
|
||||
trace_function(tr, ip, parent_ip, trace_ctx, fregs);
|
||||
#ifdef CONFIG_UNWINDER_FRAME_POINTER
|
||||
if (ftrace_pids_enabled(op))
|
||||
skip++;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "trace.h"
|
||||
#include "trace_output.h"
|
||||
#include "trace_btf.h"
|
||||
|
||||
/* When set, irq functions might be ignored */
|
||||
static int ftrace_graph_skip_irqs;
|
||||
|
|
@ -876,9 +877,10 @@ static void print_graph_retval(struct trace_seq *s, struct ftrace_graph_ent_entr
|
|||
{
|
||||
unsigned long err_code = 0;
|
||||
unsigned long retval = 0;
|
||||
bool hex_format;
|
||||
bool print_retaddr = false;
|
||||
bool print_retval = false;
|
||||
bool hex_format = !!(opt_flags & TRACE_GRAPH_PRINT_RETVAL_HEX);
|
||||
int retval_fmt = 0;
|
||||
|
||||
#ifdef CONFIG_FUNCTION_GRAPH_RETVAL
|
||||
retval = graph_ret->retval;
|
||||
|
|
@ -889,17 +891,38 @@ static void print_graph_retval(struct trace_seq *s, struct ftrace_graph_ent_entr
|
|||
print_retaddr = !!(opt_flags & TRACE_GRAPH_PRINT_RETADDR);
|
||||
#endif
|
||||
|
||||
if (print_retval && retval && !hex_format) {
|
||||
/* Check if the return value matches the negative format */
|
||||
if (IS_ENABLED(CONFIG_64BIT) && (retval & BIT(31)) &&
|
||||
(((u64)retval) >> 32) == 0) {
|
||||
err_code = sign_extend64(retval, 31);
|
||||
} else {
|
||||
err_code = retval;
|
||||
}
|
||||
if (print_retval) {
|
||||
int fmt = RETVAL_FMT_HEX;
|
||||
|
||||
if (!IS_ERR_VALUE(err_code))
|
||||
err_code = 0;
|
||||
hex_format = !!(opt_flags & TRACE_GRAPH_PRINT_RETVAL_HEX);
|
||||
btf_trim_retval((unsigned long)func, &retval, &print_retval, &fmt,
|
||||
hex_format);
|
||||
if (print_retval) {
|
||||
if (hex_format)
|
||||
retval_fmt = RETVAL_FMT_HEX;
|
||||
|
||||
if (retval && retval_fmt != RETVAL_FMT_HEX &&
|
||||
!(fmt & RETVAL_FMT_BTF)) {
|
||||
/* Check if the return value matches the negative format */
|
||||
if (IS_ENABLED(CONFIG_64BIT) && (retval & BIT(31)) &&
|
||||
(((u64)retval) >> 32) == 0) {
|
||||
err_code = sign_extend64(retval, 31);
|
||||
} else {
|
||||
err_code = retval;
|
||||
}
|
||||
|
||||
if (!IS_ERR_VALUE(err_code))
|
||||
err_code = 0;
|
||||
}
|
||||
|
||||
if (retval_fmt == RETVAL_FMT_HEX) {
|
||||
retval_fmt |= (fmt & RETVAL_FMT_TRUNC);
|
||||
} else {
|
||||
if (err_code && fmt & RETVAL_FMT_HEX)
|
||||
fmt = (fmt & ~RETVAL_FMT_HEX) | RETVAL_FMT_DEC;
|
||||
retval_fmt = fmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entry) {
|
||||
|
|
@ -926,10 +949,17 @@ static void print_graph_retval(struct trace_seq *s, struct ftrace_graph_ent_entr
|
|||
trace_flags, false);
|
||||
|
||||
if (print_retval) {
|
||||
if (hex_format || (err_code == 0))
|
||||
if (retval_fmt & RETVAL_FMT_HEX)
|
||||
trace_seq_printf(s, " ret=0x%lx", retval);
|
||||
else if (retval_fmt & RETVAL_FMT_BOOL)
|
||||
trace_seq_printf(s, " ret=%s", retval ? "true" : "false");
|
||||
else if (retval_fmt & RETVAL_FMT_UNSIGNED)
|
||||
trace_seq_printf(s, " ret=%lu", retval);
|
||||
else
|
||||
trace_seq_printf(s, " ret=%ld", err_code);
|
||||
trace_seq_printf(s, " ret=%ld", err_code ?: retval);
|
||||
|
||||
if (retval_fmt & RETVAL_FMT_TRUNC)
|
||||
trace_seq_printf(s, "(trunc)");
|
||||
}
|
||||
|
||||
if (!entry || print_retval || print_retaddr)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user