Skip to content

Commit

Permalink
Add support for the ustack() action
Browse files Browse the repository at this point in the history
Stack unwind is very difficult.  Therefore, adapt testing to the safest of
conditions:  probe firing in a stack that we compiled with no optimization,
-fno-omit-frame-pointer, no tail call optimizations, and no leaf function
optimizations.  Further, adapt the test to take better advantage of test
suite infrastructure for triggers and results checking.

The "strsize" argument has no effect.  Its value is silently ignored, and
strsize==0 is assumed.

When a pid entry probe fires, the stack frame is not yet set up.  Therefore,
the immediate caller will be missing from the call stack.

Signed-off-by: Eugene Loh <eugene.loh@oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees@oracle.com>
  • Loading branch information
euloh authored and kvanhees committed Jun 18, 2021
1 parent c26d66d commit b86caa0
Show file tree
Hide file tree
Showing 23 changed files with 413 additions and 293 deletions.
69 changes: 53 additions & 16 deletions libdtrace/dt_cg.c
Original file line number Diff line number Diff line change
Expand Up @@ -1505,37 +1505,74 @@ dt_cg_act_trunc(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
static void
dt_cg_act_ustack(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
{
dt_node_t *arg0 = dnp->dn_args;
dt_node_t *arg1 = arg0 != NULL ? arg0->dn_list : NULL;
#ifdef FIXME
uint32_t nframes = 0;
uint32_t strsize = 0;
#endif
dt_irlist_t *dlp = &pcb->pcb_ir;
dt_regset_t *drp = pcb->pcb_regs;
dtrace_hdl_t *dtp = pcb->pcb_hdl;
int nframes = dtp->dt_options[DTRACEOPT_USTACKFRAMES];
int strsize = 0;
int stacksize;
dt_node_t *arg0 = dnp->dn_args;
dt_node_t *arg1 = arg0 != NULL ? arg0->dn_list : NULL;
int skip = 0;
uint_t off;
uint_t lbl_valid = dt_irlist_label(dlp);

if (nframes == DTRACEOPT_UNSET)
nframes = _dtrace_ustackframes;

if (arg0 != NULL) {
if (!dt_node_is_posconst(arg0)) {
if (!dt_node_is_posconst(arg0))
dnerror(arg0, D_USTACK_FRAMES, "ustack( ) argument #1 "
"must be a non-zero positive integer "
"constant\n");
}

#ifdef FIXME
nframes = (uint32_t)arg0->dn_value;
#endif
nframes = arg0->dn_value;
}

if (nframes > dtp->dt_options[DTRACEOPT_MAXFRAMES])
nframes = dtp->dt_options[DTRACEOPT_MAXFRAMES];

/* FIXME: for now, accept non-zero strsize, but it does nothing */
if (arg1 != NULL) {
if (arg1->dn_kind != DT_NODE_INT ||
((arg1->dn_flags & DT_NF_SIGNED) &&
(int64_t)arg1->dn_value < 0)) {
(int64_t)arg1->dn_value < 0))
dnerror(arg1, D_USTACK_STRSIZE, "ustack( ) argument #2 "
"must be a positive integer constant\n");
}

#ifdef FIXME
strsize = (uint32_t)arg1->dn_value;
#endif
strsize = arg1->dn_value;
}

/* Reserve space in the output buffer. */
stacksize = nframes * sizeof(uint64_t);
off = dt_rec_add(pcb->pcb_hdl, dt_cg_fill_gap, DTRACEACT_USTACK,
8 + stacksize, 8, NULL,
DTRACE_USTACK_ARG(nframes, strsize));

if (dt_regset_xalloc_args(drp) == -1)
longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
dt_regset_xalloc(drp, BPF_REG_0);

/* Write the tgid. */
emit(dlp, BPF_CALL_HELPER(BPF_FUNC_get_current_pid_tgid));
emit(dlp, BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffffffff));
emit(dlp, BPF_STORE(BPF_DW, BPF_REG_9, off, BPF_REG_0));

/* Now call bpf_get_stack(ctx, buf, size, flags). */
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_DCTX));
emit(dlp, BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_1, DCTX_CTX));
emit(dlp, BPF_MOV_REG(BPF_REG_2, BPF_REG_9));
emit(dlp, BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, off + 8));
emit(dlp, BPF_MOV_IMM(BPF_REG_3, stacksize));
emit(dlp, BPF_MOV_IMM(BPF_REG_4, (skip & BPF_F_SKIP_FIELD_MASK)
| BPF_F_USER_STACK));
emit(dlp, BPF_CALL_HELPER(BPF_FUNC_get_stack));
dt_regset_free_args(drp);
emit(dlp, BPF_BRANCH_IMM(BPF_JSGE, BPF_REG_0, 0, lbl_valid));
dt_cg_probe_error(pcb, -1, DTRACEFLT_BADSTACK, 0);
emitl(dlp, lbl_valid,
BPF_NOP());
dt_regset_free(drp, BPF_REG_0);
}

typedef void dt_cg_action_f(dt_pcb_t *, dt_node_t *, dtrace_actkind_t);
Expand Down
9 changes: 7 additions & 2 deletions libdtrace/dt_consume.c
Original file line number Diff line number Diff line change
Expand Up @@ -1089,10 +1089,10 @@ dt_print_ustack(dtrace_hdl_t *dtp, FILE *fp, const char *format,
caddr_t addr, uint64_t arg)
{
/* LINTED - alignment */
uint64_t *pc = ((uint64_t *)addr) + 1;
uint64_t *pc = ((uint64_t *)addr);
uint32_t depth = DTRACE_USTACK_NFRAMES(arg);
uint32_t strsize = DTRACE_USTACK_STRSIZE(arg);
const char *strbase = addr + (depth + 2) * sizeof(uint64_t);
const char *strbase = addr + (depth + 1) * sizeof(uint64_t);
const char *str = strsize ? strbase : NULL;
int err = 0;

Expand Down Expand Up @@ -2092,6 +2092,11 @@ dt_consume_one(dtrace_hdl_t *dtp, FILE *fp, char *buf,
if (dt_print_mod(dtp, fp, NULL, recdata) < 0)
return -1;
continue;
case DTRACEACT_USTACK:
if (dt_print_ustack(dtp, fp, NULL,
recdata, rec->dtrd_arg) < 0)
return -1;
continue;
case DTRACEACT_PRINTF:
func = dtrace_fprintf;
break;
Expand Down
1 change: 1 addition & 0 deletions libdtrace/dt_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,7 @@ extern size_t _dtrace_bufsize; /* default dt_buf_create() size */
extern int _dtrace_argmax; /* default maximum probe arguments */
extern int _dtrace_debug_assert; /* turn on expensive assertions */
extern int _dtrace_stackframes; /* default number of stack frames */
extern int _dtrace_ustackframes; /* default number of user stack frames */

extern const char *_dtrace_moddir; /* default kernel module directory */

Expand Down
1 change: 1 addition & 0 deletions libdtrace/dt_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,7 @@ uint_t _dtrace_pidlrulim = 8; /* default number of pid handles to cache */
size_t _dtrace_bufsize = 512; /* default dt_buf_create() size */
int _dtrace_argmax = 32; /* default maximum number of probe arguments */
int _dtrace_stackframes = 20; /* default number of stack frames */
int _dtrace_ustackframes = 100; /* default number of user stack frames */

const char *const _dtrace_version = DT_VERS_STRING; /* API version string */
const char *const _libdtrace_vcs_version = DT_GIT_VERSION; /* Build version string */
Expand Down
9 changes: 5 additions & 4 deletions test/triggers/Build
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# Oracle Linux DTrace.
# Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
# Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.
# Licensed under the Universal Permissive License v 1.0 as shown at
# http://oss.oracle.com/licenses/upl.

EXTERNAL_64BIT_TRIGGERS = testprobe readwholedir mmap bogus-ioctl open delaydie pid-tst-args1 pid-tst-float pid-tst-fork pid-tst-gcc pid-tst-ret1 pid-tst-ret2 pid-tst-vfork pid-tst-weak1 pid-tst-weak2 proc-tst-sigwait proc-tst-omp proc-tst-pthread-exec profile-tst-ufuncsort raise-tst-raise1 raise-tst-raise2 raise-tst-raise3 syscall-tst-args ustack-tst-bigstack ustack-tst-spin ustack-tst-mtspin visible-constructor visible-constructor-static visible-constructor-static-unstripped
EXTERNAL_64BIT_TRIGGERS = testprobe readwholedir mmap bogus-ioctl open delaydie pid-tst-args1 pid-tst-float pid-tst-fork pid-tst-gcc pid-tst-ret1 pid-tst-ret2 pid-tst-vfork pid-tst-weak1 pid-tst-weak2 proc-tst-sigwait proc-tst-omp proc-tst-pthread-exec profile-tst-ufuncsort raise-tst-raise1 raise-tst-raise2 raise-tst-raise3 syscall-tst-args ustack-tst-basic ustack-tst-bigstack ustack-tst-spin ustack-tst-mtspin visible-constructor visible-constructor-static visible-constructor-static-unstripped

EXTERNAL_64BIT_SDT_TRIGGERS = usdt-tst-argmap usdt-tst-args usdt-tst-forker usdt-tst-special
EXTERNAL_64BIT_TRIGGERS += $(EXTERNAL_64BIT_SDT_TRIGGERS)
Expand Down Expand Up @@ -196,9 +196,10 @@ usdt-tst-forker_PROV := usdt-tst-forker-prov.d
usdt-tst-special_CFLAGS := -fno-inline -O2
usdt-tst-special_PROV := usdt-tst-special-prov.d

# ustack-tst-mtspin uses threads, and neither ustack-tst-spin nor
# ustack-tst-mtspin want optimization.
# ustack-tst-mtspin uses threads
# ustack wants no optimization

ustack-tst-basic_CFLAGS := -O0 -fno-inline -fno-omit-frame-pointer
ustack-tst-spin_CFLAGS := -O0
ustack-tst-mtspin_CFLAGS := -O0
ustack-tst-mtspin_LIBS := -lpthread
90 changes: 90 additions & 0 deletions test/triggers/ustack-tst-basic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Oracle Linux DTrace.
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/

int mycallee(int i) {
return i ^ 1;
}

int myfunc_z() {
int i = 1;

/* call another function to prevent leaf call optimizations */
i = mycallee(i);

/* endless loop, profile-n probe will fire here */
while (i < 3)
i = 2 * i - 1;

return i;
}

/* have a relatively deep stack; ^1 prevents tail calls */
int myfunc_y() { return myfunc_z() ^ 1; }
int myfunc_x() { return myfunc_y() ^ 1; }
int myfunc_w() { return myfunc_x() ^ 1; }
int myfunc_v() { return myfunc_w() ^ 1; }
int myfunc_u() { return myfunc_v() ^ 1; }
int myfunc_t() { return myfunc_u() ^ 1; }
int myfunc_s() { return myfunc_t() ^ 1; }
int myfunc_r() { return myfunc_s() ^ 1; }
int myfunc_q() { return myfunc_r() ^ 1; }
int myfunc_p() { return myfunc_q() ^ 1; }
int myfunc_o() { return myfunc_p() ^ 1; }
int myfunc_n() { return myfunc_o() ^ 1; }
int myfunc_m() { return myfunc_n() ^ 1; }
int myfunc_l() { return myfunc_m() ^ 1; }
int myfunc_k() { return myfunc_l() ^ 1; }
int myfunc_j() { return myfunc_k() ^ 1; }
int myfunc_i() { return myfunc_j() ^ 1; }
int myfunc_h() { return myfunc_i() ^ 1; }
int myfunc_g() { return myfunc_h() ^ 1; }
int myfunc_f() { return myfunc_g() ^ 1; }
int myfunc_e() { return myfunc_f() ^ 1; }
int myfunc_d() { return myfunc_e() ^ 1; }
int myfunc_c() { return myfunc_d() ^ 1; }
int myfunc_b() { return myfunc_c() ^ 1; }
int myfunc_a() { return myfunc_b() ^ 1; }
int myfunc_Z() { return myfunc_a() ^ 1; }
int myfunc_Y() { return myfunc_Z() ^ 1; }
int myfunc_X() { return myfunc_Y() ^ 1; }
int myfunc_W() { return myfunc_X() ^ 1; }
int myfunc_V() { return myfunc_W() ^ 1; }
int myfunc_U() { return myfunc_V() ^ 1; }
int myfunc_T() { return myfunc_U() ^ 1; }
int myfunc_S() { return myfunc_T() ^ 1; }
int myfunc_R() { return myfunc_S() ^ 1; }
int myfunc_Q() { return myfunc_R() ^ 1; }
int myfunc_P() { return myfunc_Q() ^ 1; }
int myfunc_O() { return myfunc_P() ^ 1; }
int myfunc_N() { return myfunc_O() ^ 1; }
int myfunc_M() { return myfunc_N() ^ 1; }
int myfunc_L() { return myfunc_M() ^ 1; }
int myfunc_K() { return myfunc_L() ^ 1; }
int myfunc_J() { return myfunc_K() ^ 1; }
int myfunc_I() { return myfunc_J() ^ 1; }
int myfunc_H() { return myfunc_I() ^ 1; }
int myfunc_G() { return myfunc_H() ^ 1; }
int myfunc_F() { return myfunc_G() ^ 1; }
int myfunc_E() { return myfunc_F() ^ 1; }
int myfunc_D() { return myfunc_E() ^ 1; }
int myfunc_C() { return myfunc_D() ^ 1; }
int myfunc_B() { return myfunc_C() ^ 1; }
int myfunc_A() { return myfunc_B() ^ 1; }
int myfunc_9() { return myfunc_A() ^ 1; }
int myfunc_8() { return myfunc_9() ^ 1; }
int myfunc_7() { return myfunc_8() ^ 1; }
int myfunc_6() { return myfunc_7() ^ 1; }
int myfunc_5() { return myfunc_6() ^ 1; }
int myfunc_4() { return myfunc_5() ^ 1; }
int myfunc_3() { return myfunc_4() ^ 1; }
int myfunc_2() { return myfunc_3() ^ 1; }
int myfunc_1() { return myfunc_2() ^ 1; }
int myfunc_0() { return myfunc_1() ^ 1; }

int main(int c, char **v) {
return myfunc_0() ^ 1;
}

0 comments on commit b86caa0

Please sign in to comment.