[DTrace-devel] [PATCH] Built-in variable "timestamp": semantics should agree with v1

eugene.loh at oracle.com eugene.loh at oracle.com
Sat Jul 18 08:12:42 PDT 2020


From: Eugene Loh <eugene.loh at oracle.com>

The semantics of the built-in variable "timestamp" are not 100% clear from
the documentation.  Specifically, should it record a fresh time stamp each
time it is called?  Or should it be measured once upon probe (or clause)
entry and then reused?

Regardless, "legacy" DTrace seems to record a fresh timestamp for each
clause, reusing the same value throughout the clause.  Consider:

    # dtrace -qn 'BEGIN {trace(timestamp);trace(timestamp);}
                  BEGIN {trace(timestamp);trace(timestamp);exit(0)};'
    3823604430297094
    3823604430297094
    3823604430297975
    3823604430297975

In contrast, DTv2 appears to use a fresh timestamp each instance,
even within the same clause:

    # dtrace -qn 'BEGIN {trace(timestamp);trace(timestamp); exit(0);}'
    1645711727876826
    1645711727876873

The timestamp value should be consistent throughout an entire clause
and its predicate.  E.g., with DTv1:

    # dtrace -qn 'BEGIN
                  /(t = timestamp) == timestamp/
                  { trace(t);
                    trace(timestamp);
                    trace(timestamp); exit(0);
                  }'

trace() reports the same value three times, while with DTv2 the
predicate never even evaluates to true at all and the test hangs.

Fix the DTv2 version to match the legacy behavior, including in
dt_cg_prologue() moving mstate initialization before predicate evaluation.
Also, free the register used for any predicate, fixing a register leak.

Orabug: 31632630, 31638431
Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
---
 bpf/get_bvar.c      |  4 +++-
 libdtrace/dt_cg.c   | 32 ++++++++++++++++++--------------
 libdtrace/dt_dctx.h |  4 +++-
 3 files changed, 24 insertions(+), 16 deletions(-)

diff --git a/bpf/get_bvar.c b/bpf/get_bvar.c
index 40045fb0..1d0f3e46 100644
--- a/bpf/get_bvar.c
+++ b/bpf/get_bvar.c
@@ -21,7 +21,9 @@ noinline uint64_t dt_get_bvar(dt_mstate_t *mst, uint32_t id)
 	case DIF_VAR_CURTHREAD:
 		return bpf_get_current_task();
 	case DIF_VAR_TIMESTAMP:
-		return bpf_ktime_get_ns();
+		if (mst->tstamp == 0)
+			mst->tstamp = bpf_ktime_get_ns();
+		return mst->tstamp;
 	case DIF_VAR_EPID:
 		return mst->epid;
 	case DIF_VAR_ARG0: case DIF_VAR_ARG1: case DIF_VAR_ARG2:
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 2435b84d..2746ccd7 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -181,20 +181,6 @@ dt_cg_prologue(dt_pcb_t *pcb, dt_node_t *pred)
 	instr = BPF_STORE(BPF_DW, BPF_REG_FP, DT_STK_DCTX, BPF_REG_1);
 	dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
 
-	/*
-	 * If there is a predicate:
-	 *
-	 *	rc = predicate;		//     (evaluate predicate into %rX)
-	 *	if (rc == 0)		// jeq %rX, 0, pcb->pcb_exitlbl
-	 *		goto exit;
-	 */
-	if (pred != NULL) {
-		dt_cg_node(pred, &pcb->pcb_ir, pcb->pcb_regs);
-		instr = BPF_BRANCH_IMM(BPF_JEQ, pred->dn_reg, 0,
-				       pcb->pcb_exitlbl);
-		dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
-	}
-
 	/*
 	 *	buf = dctx->buf;	// lddw %r0, [%fp + DT_STK_DCTX]
 	 *				// lddw %r9, [%r0 + DCTX_BUF]
@@ -207,6 +193,7 @@ dt_cg_prologue(dt_pcb_t *pcb, dt_node_t *pred)
 	/*
 	 *	dctx->mst->fault = 0;	// lddw %r0, [%r0 + DCTX_MST]
 	 *				// stdw [%r0 + DMST_FAULT], 0
+	 *	dctx->mst->tstamp = 0;	// stdw [%r0 + DMST_TSTAMP], 0
 	 *	dctx->mst->epid = EPID;	// stw [%r0 + DMST_EPID], EPID
 	 *	*((uint32_t *)&buf[0]) = EPID;
 	 *				// stw [%r9 + 0], EPID
@@ -215,6 +202,8 @@ dt_cg_prologue(dt_pcb_t *pcb, dt_node_t *pred)
 	dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
 	instr = BPF_STORE_IMM(BPF_DW, BPF_REG_0, DMST_FAULT, 0);
 	dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
+	instr = BPF_STORE_IMM(BPF_DW, BPF_REG_0, DMST_TSTAMP, 0);
+	dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
 	instr = BPF_STORE_IMM(BPF_W, BPF_REG_0, DMST_EPID, -1);
 	dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
 	dlp->dl_last->di_extern = epid;
@@ -232,6 +221,21 @@ dt_cg_prologue(dt_pcb_t *pcb, dt_node_t *pred)
 	instr = BPF_STORE_IMM(BPF_W, BPF_REG_9, 4, 0);
 	dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
 
+	/*
+	 * If there is a predicate:
+	 *
+	 *	rc = predicate;		//     (evaluate predicate into %rX)
+	 *	if (rc == 0)		// jeq %rX, 0, pcb->pcb_exitlbl
+	 *		goto exit;
+	 */
+	if (pred != NULL) {
+		dt_cg_node(pred, &pcb->pcb_ir, pcb->pcb_regs);
+		instr = BPF_BRANCH_IMM(BPF_JEQ, pred->dn_reg, 0,
+				       pcb->pcb_exitlbl);
+		dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
+		dt_regset_free(pcb->pcb_regs, pred->dn_reg);
+	}
+
 	/*
 	 * Account for 32-bit epid (at offset 0) and 32-bit tag (at offset 4).
 	 */
diff --git a/libdtrace/dt_dctx.h b/libdtrace/dt_dctx.h
index 647d181a..df4ff6fa 100644
--- a/libdtrace/dt_dctx.h
+++ b/libdtrace/dt_dctx.h
@@ -11,12 +11,13 @@
 #include <dt_pt_regs.h>
 
 /*
- * The DTrace machien state.
+ * The DTrace machine state.
  */
 typedef struct dt_mstate {
 	uint32_t	epid;		/* Enabled probe ID */
 	uint32_t	tag;		/* Tag (for future use) */
 	uint64_t	fault;		/* DTrace fault flags */
+	uint64_t	tstamp;		/* timestamp built-in variable */
 #if 0
 	dt_pt_regs	regs;		/* CPU registers */
 #endif
@@ -46,6 +47,7 @@ typedef struct dt_dctx {
 #define DMST_EPID	offsetof(dt_mstate_t, epid)
 #define DMST_TAG	offsetof(dt_mstate_t, tag)
 #define DMST_FAULT	offsetof(dt_mstate_t, fault)
+#define DMST_TSTAMP	offsetof(dt_mstate_t, tstamp)
 #define DMST_REGS	offsetof(dt_mstate_t, regs)
 #define DMST_ARG(n)	offsetof(dt_mstate_t, argv[n])
 
-- 
2.18.2




More information about the DTrace-devel mailing list