Skip to content

Commit

Permalink
retsnoop: make func call trace and call stack modes independent
Browse files Browse the repository at this point in the history
Make it possible to have only function trace mode without emitting call
stack traces, and vice versa. For function trace-only mode switch the
default to allow capturing success stacks, as that's the most typical
assumption. For call stack mode still default to emitting erroring
stacks only, by default. Allow to force it one way or the other with -Sn
or -Sy/-S arguments.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
  • Loading branch information
anakryiko committed Jul 26, 2024
1 parent 232dc40 commit 503e7cd
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 30 deletions.
30 changes: 27 additions & 3 deletions src/env.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ static const struct argp_option opts[] = {

/* Running mode configuration */
{ .flags = OPTION_DOC, "RUNMODE\n=========================" },
{ "call-stack", 'E', NULL, 0, "Capture and emit call stacks (default mode)" },
{ "trace", 'T', NULL, 0, "Capture and emit function call traces" },
{ "capture-args", 'A', NULL, 0, "Capture and emit function arguments" },
{ "lbr", 'B', "SPEC", OPTION_ARG_OPTIONAL,
Expand Down Expand Up @@ -113,7 +114,8 @@ static const struct argp_option opts[] = {
"Skip tracing processes with given name" },
{ "longer", 'L', "MS", 0,
"Only emit stacks that took at least a given amount of milliseconds" },
{ "success-stacks", 'S', NULL, 0, "Emit any stack, successful or not" },
{ "success-stacks", 'S', "VALUE", OPTION_ARG_OPTIONAL,
"Specify whether emitting non-erroring (successful) call stacks is allowed" },
{ "allow-errors", 'x', "ERROR", 0, "Record stacks only with specified errors" },
{ "deny-errors", 'X', "ERROR", 0, "Ignore stacks that have specified errors" },

Expand Down Expand Up @@ -393,7 +395,7 @@ static enum debug_feat parse_config_arg(const char *arg)

static error_t parse_arg(int key, char *arg, struct argp_state *state)
{
int i, j, err;
int i, j, err, val;

switch (key) {
case 'h':
Expand All @@ -417,6 +419,9 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
else if (!env.debug_extra)
env.debug_extra = true;
break;
case 'E':
env.emit_call_stack = true;
break;
case 'T':
env.emit_func_trace = true;
break;
Expand Down Expand Up @@ -537,6 +542,11 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
return err;
break;
case 'x':
if (env.emit_success_stacks > 0) {
elog("Can't specify -S/-Sy and -x arguments at the same time!\n");
return -EINVAL;
}
env.emit_success_stacks = -1; /* force failing stacks only */
err = str_to_err(arg);
if (err < 0)
return err;
Expand All @@ -562,7 +572,21 @@ static error_t parse_arg(int key, char *arg, struct argp_state *state)
err_mask_set(env.deny_error_mask, err);
break;
case 'S':
env.emit_success_stacks = true;
if (arg && strcasecmp(arg, "y") == 0) {
val = +1;
} else if (arg && strcasecmp(arg, "n") == 0) {
val = -1;
} else if (!arg) {
val = +1;
} else {
elog("Unrecognized -S%s argument, only -S, -Sy, or -Sn are supported!\n", arg);
return -EINVAL;
}
if (env.emit_success_stacks != 0 && env.emit_success_stacks != val) {
elog("Conflicting combination of -S/-Sn/-Sy and -x arguments specified!\n");
return -EINVAL;
}
env.emit_success_stacks = val;
break;
case 'M':
if (env.attach_mode != ATTACH_DEFAULT) {
Expand Down
20 changes: 16 additions & 4 deletions src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,20 +49,26 @@ enum debug_feat {
};

struct env {
/* two main modes of operation; if only emit_func_trace is specified,
* we default to capturing all (including non-erroring) sessions
*/
bool emit_call_stack;
bool emit_func_trace;

bool capture_args;
bool use_lbr;

bool show_version;
bool show_config_help;
bool verbose;
bool debug;
bool debug_extra;
bool dry_run;
bool emit_success_stacks;
bool emit_func_trace;
bool capture_args;
enum attach_mode attach_mode;
enum debug_feat debug_feats;
bool use_lbr;
long lbr_flags;
int lbr_max_cnt;

const char *vmlinux_path;
int pid;
int longer_than_ms;
Expand Down Expand Up @@ -105,6 +111,12 @@ struct env {
int allow_comm_cnt;
int deny_comm_cnt;

/* default 0 will depend on allow_call_stack and allow_func_trace settings;
* +1 forces success stacks capture;
* -1 forces unsuccessful stacks only capture;
*/
int emit_success_stacks;

int allow_error_cnt;
bool has_error_filter;
uint64_t allow_error_mask[(MAX_ERRNO + 1) / 64];
Expand Down
41 changes: 23 additions & 18 deletions src/logic.c
Original file line number Diff line number Diff line change
Expand Up @@ -1148,23 +1148,6 @@ static int handle_session_end(struct ctx *dctx, struct session *sess, const stru
s->depth, s->max_depth, s->saved_depth, s->saved_max_depth);
}

fstack_n = filter_fstack(dctx, fstack, s);
if (fstack_n < 0) {
fprintf(stderr, "FAILURE DURING FILTERING FUNCTION STACK!!! %d\n", fstack_n);
ret = -EINVAL;
goto out_purge;
}
kstack_n = filter_kstack(dctx, kstack, s);
if (kstack_n < 0) {
fprintf(stderr, "FAILURE DURING FILTERING KERNEL STACK!!! %d\n", kstack_n);
ret = -EINVAL;
goto out_purge;
}
if (env.debug) {
printf("FSTACK (%d items):\n", fstack_n);
printf("KSTACK (%d items out of original %ld):\n", kstack_n, s->kstack_sz / 8);
}

ts_to_str(ktime_to_ts(sess->start_ts), ts1, sizeof(ts1));
ts_to_str(ktime_to_ts(r->emit_ts), ts2, sizeof(ts2));
printf("%s -> %s TID/PID %d/%d (%s/%s):\n", ts1, ts2, sess->pid, sess->tgid,
Expand All @@ -1183,6 +1166,26 @@ static int handle_session_end(struct ctx *dctx, struct session *sess, const stru
print_ft_items(dctx, &stack_items1);
}

if (!env.emit_call_stack && !env.use_lbr)
goto skip_call_stack;

fstack_n = filter_fstack(dctx, fstack, s);
if (fstack_n < 0) {
fprintf(stderr, "FAILURE DURING FILTERING FUNCTION STACK!!! %d\n", fstack_n);
ret = -EINVAL;
goto out_purge;
}
kstack_n = filter_kstack(dctx, kstack, s);
if (kstack_n < 0) {
fprintf(stderr, "FAILURE DURING FILTERING KERNEL STACK!!! %d\n", kstack_n);
ret = -EINVAL;
goto out_purge;
}
if (env.debug) {
printf("FSTACK (%d items):\n", fstack_n);
printf("KSTACK (%d items out of original %ld):\n", kstack_n, s->kstack_sz / 8);
}

/* Determine address range of deepest nested function */
if (fstack_n > 0) {
const struct fstack_item *fitem = &fstack[fstack_n - 1];
Expand All @@ -1204,8 +1207,10 @@ static int handle_session_end(struct ctx *dctx, struct session *sess, const stru
}

/* Emit combined fstack/kstack + errors stack trace */
output_call_stack(dctx, sess, fstack, fstack_n, kstack, kstack_n);
if (env.emit_call_stack)
output_call_stack(dctx, sess, fstack, fstack_n, kstack, kstack_n);

skip_call_stack:
if (r->dropped_records) {
printf("WARNING! Sample data incomplete! %d record%s dropped. Consider increasing --ringbuf-map-size.\n",
r->dropped_records, r->dropped_records == 1 ? "" : "s");
Expand Down
6 changes: 3 additions & 3 deletions src/retsnoop.bpf.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ struct {

const volatile bool verbose = false;
const volatile bool extra_verbose = false;
const volatile bool use_lbr = true;
const volatile int targ_tgid = -1;
const volatile bool emit_success_stacks = false;
const volatile bool emit_call_stack = true;
const volatile bool emit_func_trace = true;
const volatile bool emit_success_stacks = false;
const volatile bool capture_args = true;
const volatile bool capture_raw_ptrs = true;
const volatile bool use_lbr = true;
const volatile bool use_kprobes = true;

const volatile int args_max_total_args_sz;
Expand Down
13 changes: 11 additions & 2 deletions src/retsnoop.c
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,15 @@ int main(int argc, char **argv, char **envp)
goto cleanup_silent;
}
#endif
if (!env.emit_func_trace)
env.emit_call_stack = true;
/* default setting for success stacks, resolve based on call stack vs func trace modes */
if (env.emit_success_stacks == 0) {
if (env.emit_call_stack)
env.emit_success_stacks = -1;
else
env.emit_success_stacks = +1;
}

/* Open BPF skeleton */
env.ctx.skel = skel = retsnoop_bpf__open();
Expand Down Expand Up @@ -384,8 +393,7 @@ int main(int argc, char **argv, char **envp)
/* turn on extra bpf_printk()'s on BPF side */
skel->rodata->verbose = env.debug_feats & DEBUG_BPF;
skel->rodata->extra_verbose = (env.debug_feats & DEBUG_BPF) && env.debug_extra;
skel->rodata->targ_tgid = env.pid;
skel->rodata->emit_success_stacks = env.emit_success_stacks;
skel->rodata->emit_success_stacks = env.emit_success_stacks > 0;
skel->rodata->duration_ns = env.longer_than_ms * 1000000ULL;
skel->rodata->use_kprobes = env.attach_mode != ATTACH_FENTRY;
memset(skel->rodata->spaces, ' ', sizeof(skel->rodata->spaces) - 1);
Expand Down Expand Up @@ -424,6 +432,7 @@ int main(int argc, char **argv, char **envp)
if (env.use_lbr && env.verbose)
printf("LBR capture enabled.\n");

skel->rodata->emit_call_stack = env.emit_call_stack;
skel->rodata->emit_func_trace = env.emit_func_trace;

att_opts.verbose = env.verbose;
Expand Down

0 comments on commit 503e7cd

Please sign in to comment.