[DTrace-devel] [PATCH 10/15 v2] Implement the curcpu builtin D variable

Kris Van Hees kris.van.hees at oracle.com
Fri May 29 10:59:41 PDT 2020


The 'curcpu' builtin D variable is special in the sense that it is
a pointer to a cpuinfo_t struct for the CPU on which the D program is
executing, yet no such structure exists in the kernel (DTrace v1 used
to provide it through a kernel patch).  In order to provide the same
functionality, a new BPF map 'cpuinfo' is populated from userspace
with cpuinfo_t structs for each CPU.

The BPF map creation code had an entry for a 'probes' map that is no
longer needed, and it has therefore been removed (incl. the probec
argument to dt_bpf_gmap_create().)

To facilitate using map FDs at time of creation, create_gmap() now
returns the fd.

To satisfy the BPF verifier (and because it is good practice), the
code generator will now ensure that a pointer lvalue is not NULL prior
to accessing its members.

The builtin D variables currently supoorted in dt_get_bvar() have also
been reordered to reflevt the order of their IDs (for consistency).

Orabug: 31221984
Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
 bpf/Build              |  2 +-
 bpf/get_bvar.c         | 16 ++++++++++++----
 libdtrace/dt_bpf.c     | 30 +++++++++++++++++-------------
 libdtrace/dt_bpf.h     |  2 +-
 libdtrace/dt_cg.c      |  8 +++++++-
 libdtrace/dt_dlibs.c   |  6 +++---
 libdtrace/dt_open.c    |  2 +-
 libdtrace/dt_program.c |  2 +-
 libdtrace/procfs.d.in  |  3 ---
 libdtrace/sched.d      | 16 ++++++++++++++--
 10 files changed, 57 insertions(+), 30 deletions(-)

diff --git a/bpf/Build b/bpf/Build
index bc77dcf3..0111a518 100644
--- a/bpf/Build
+++ b/bpf/Build
@@ -17,7 +17,7 @@ bpf_dlib_TARGET = dlibs/bpf_dlib
 bpf_dlib_DIR := $(current-dir)
 bpf_dlib_SRCDEPS = $(objdir)/include/.dir.stamp
 bpf_dlib_SOURCES = \
-	get_bvar.c \
+	map_cpuinfo.c get_bvar.c \
 	map_gvar.c get_gvar.c set_gvar.c \
 	map_tvar.c get_tvar.c set_tvar.c \
 	memcpy.c strnlen.c
diff --git a/bpf/get_bvar.c b/bpf/get_bvar.c
index 4891b144..5a55a495 100644
--- a/bpf/get_bvar.c
+++ b/bpf/get_bvar.c
@@ -5,6 +5,7 @@
 #include <linux/bpf.h>
 #include <stdint.h>
 #include <bpf-helpers.h>
+#include <dtrace/conf.h>
 #include <dtrace/dif_defines.h>
 #include <dt_bpf_ctx.h>
 
@@ -12,9 +13,15 @@
 # define noinline	__attribute__((noinline))
 #endif
 
+extern struct bpf_map_def cpuinfo;
+
 noinline uint64_t dt_get_bvar(struct dt_bpf_context *dctx, uint32_t id)
 {
 	switch (id) {
+	case DIF_VAR_CURTHREAD:
+		return bpf_get_current_task();
+	case DIF_VAR_TIMESTAMP:
+		return bpf_ktime_get_ns();
 	case DIF_VAR_EPID:
 		return dctx->epid;
 	case DIF_VAR_ARG0: case DIF_VAR_ARG1: case DIF_VAR_ARG2:
@@ -22,10 +29,6 @@ noinline uint64_t dt_get_bvar(struct dt_bpf_context *dctx, uint32_t id)
 	case DIF_VAR_ARG6: case DIF_VAR_ARG7: case DIF_VAR_ARG8:
 	case DIF_VAR_ARG9:
 		return dctx->argv[id - DIF_VAR_ARG0];
-	case DIF_VAR_CURTHREAD:
-		return bpf_get_current_task();
-	case DIF_VAR_TIMESTAMP:
-		return bpf_ktime_get_ns();
 	case DIF_VAR_PID: {
 		uint64_t	val = bpf_get_current_pid_tgid();
 
@@ -46,6 +49,11 @@ noinline uint64_t dt_get_bvar(struct dt_bpf_context *dctx, uint32_t id)
 
 		return val >> 32;
 	}
+	case DIF_VAR_CURCPU: {
+		uint32_t	key = 0;
+
+		return bpf_map_lookup_elem(&cpuinfo, &key);
+	}
 	default:
 		/* Not implemented yet. */
 #if 1
diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
index a0dd0ef0..d5282b94 100644
--- a/libdtrace/dt_bpf.c
+++ b/libdtrace/dt_bpf.c
@@ -64,9 +64,9 @@ create_gmap(dtrace_hdl_t *dtp, const char *name, enum bpf_map_type type,
 	if (fd < 0)
 		return dt_bpf_error(dtp, "failed to create BPF map '%s': %s\n",
 				    name, strerror(errno));
-	else
-		dt_dprintf("BPF map '%s' is FD %d (ksz %u, vsz %u, sz %d)\n",
-			   name, fd, ksz, vsz, size);
+
+	dt_dprintf("BPF map '%s' is FD %d (ksz %u, vsz %u, sz %d)\n",
+		   name, fd, ksz, vsz, size);
 
 	/*
 	 * Assign the fd as id for the BPF map identifier.
@@ -79,7 +79,7 @@ create_gmap(dtrace_hdl_t *dtp, const char *name, enum bpf_map_type type,
 
 	dt_ident_set_id(idp, fd);
 
-	return 0;
+	return fd;
 }
 
 /*
@@ -88,6 +88,8 @@ create_gmap(dtrace_hdl_t *dtp, const char *name, enum bpf_map_type type,
  *
  * - buffers:	Perf event output buffer map, associating a perf event output
  *		buffer with each CPU.  The map is indexed by CPU id.
+ * - cpuinfo:	CPU information map, associating a cpuinfo_t structure with
+ *		each online CPU on the system.
  * - mem:	Output buffer scratch memory.  Thiss is implemented as a global
  *		per-CPU map with a singleton element (key 0).  This means that
  *		every CPU will see its own copy of this singleton element, and
@@ -124,13 +126,13 @@ create_gmap(dtrace_hdl_t *dtp, const char *name, enum bpf_map_type type,
  *		to allocate for these dynamic variables is calculated based on
  *		the number of uniquely named TLS variables (next-to-be-assigned
  *		id minus the base id).
- * - probes:	Probe information map, associating a probe info structure with
- *		each probe that is used in the current probing session.
  */
 int
-dt_bpf_gmap_create(dtrace_hdl_t *dtp, uint_t probec)
+dt_bpf_gmap_create(dtrace_hdl_t *dtp)
 {
-	int	gvarc, tvarc;
+	int		gvarc, tvarc;
+	int		ci_mapfd;
+	uint32_t	key = 0;
 
 	/* If we already created the global maps, return success. */
 	if (dt_gmap_done)
@@ -149,6 +151,11 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp, uint_t probec)
 			dtp->dt_conf.num_online_cpus) == -1)
 		return -1;	/* dt_errno is set for us */
 
+	ci_mapfd = create_gmap(dtp, "cpuinfo", BPF_MAP_TYPE_PERCPU_ARRAY,
+			       sizeof(uint32_t), sizeof(cpuinfo_t), 1);
+	if (ci_mapfd == -1)
+		return -1;	/* dt_errno is set for us */
+
 	if (create_gmap(dtp, "mem", BPF_MAP_TYPE_PERCPU_ARRAY,
 			sizeof(uint32_t), dtp->dt_maxreclen + 4, 1) == -1)
 		return -1;	/* dt_errno is set for us */
@@ -167,11 +174,8 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp, uint_t probec)
 			sizeof(uint32_t), sizeof(uint64_t), tvarc) == -1)
 		return -1;	/* dt_errno is set for us */
 
-	if (probec > 0 &&
-	    create_gmap(dtp, "probes", BPF_MAP_TYPE_ARRAY,
-			sizeof(uint32_t), sizeof(void *), probec) == -1)
-		return -1;	/* dt_errno is set for us */
-	/* FIXME: Need to put in the actual struct ref for probe info. */
+	/* Populate the 'cpuinfo' map. */
+	dt_bpf_map_update(ci_mapfd, &key, dtp->dt_conf.cpus);
 
 	return 0;
 }
diff --git a/libdtrace/dt_bpf.h b/libdtrace/dt_bpf.h
index 41772229..5bee2e52 100644
--- a/libdtrace/dt_bpf.h
+++ b/libdtrace/dt_bpf.h
@@ -22,7 +22,7 @@ extern int perf_event_open(struct perf_event_attr *attr, pid_t pid, int cpu,
 			   int group_fd, unsigned long flags);
 extern int bpf(enum bpf_cmd cmd, union bpf_attr *attr);
 
-extern int dt_bpf_gmap_create(dtrace_hdl_t *, uint_t);
+extern int dt_bpf_gmap_create(dtrace_hdl_t *);
 extern int dt_bpf_map_update(int fd, const void *key, const void *val);
 extern int dt_bpf_prog(dtrace_hdl_t *, dtrace_prog_t *);
 
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 7a88654d..df36f51f 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -2470,6 +2470,13 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 		assert(dnp->dn_right->dn_kind == DT_NODE_IDENT);
 		dt_cg_node(dnp->dn_left, dlp, drp);
 
+		/*
+		 * Ensure that the lvalue is not the NULL pointer.
+		 */
+		instr = BPF_BRANCH_IMM(BPF_JEQ, dnp->dn_left->dn_reg, 0,
+				       yypcb->pcb_exitlbl);
+		dt_irlist_append(dlp, dt_cg_node_alloc(DT_LBL_NONE, instr));
+
 		/*
 		 * If the left-hand side of PTR or DOT is a dynamic variable,
 		 * we expect it to be the output of a D translator.   In this
@@ -2481,7 +2488,6 @@ dt_cg_node(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 		    dnp->dn_left, DT_IDENT_XLSOU)) != NULL ||
 		    (idp = dt_node_resolve(
 		    dnp->dn_left, DT_IDENT_XLPTR)) != NULL) {
-
 			dt_xlator_t *dxp;
 			dt_node_t *mnp;
 
diff --git a/libdtrace/dt_dlibs.c b/libdtrace/dt_dlibs.c
index 445654d7..899ac0f2 100644
--- a/libdtrace/dt_dlibs.c
+++ b/libdtrace/dt_dlibs.c
@@ -62,11 +62,11 @@ static const dt_ident_t		dt_bpf_symbols[] = {
 	DT_BPF_SYMBOL(dt_strnlen, DT_IDENT_SYMBOL),
 	/* BPF maps */
 	DT_BPF_SYMBOL(buffers, DT_IDENT_PTR),
-	DT_BPF_SYMBOL(strtab, DT_IDENT_PTR),
+	DT_BPF_SYMBOL(cpuinfo, DT_IDENT_PTR),
 	DT_BPF_SYMBOL(gvars, DT_IDENT_PTR),
-	DT_BPF_SYMBOL(tvars, DT_IDENT_PTR),
-	DT_BPF_SYMBOL(probes, DT_IDENT_PTR),
 	DT_BPF_SYMBOL(mem, DT_IDENT_PTR),
+	DT_BPF_SYMBOL(strtab, DT_IDENT_PTR),
+	DT_BPF_SYMBOL(tvars, DT_IDENT_PTR),
 	/* BPF internal identifiers */
 	DT_BPF_SYMBOL_ID(EPID, DT_IDENT_SCALAR, DT_CONST_EPID),
 	DT_BPF_SYMBOL_ID(ARGC, DT_IDENT_SCALAR, DT_CONST_ARGC),
diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
index 7f680419..6a1efbfb 100644
--- a/libdtrace/dt_open.c
+++ b/libdtrace/dt_open.c
@@ -140,7 +140,7 @@ static const dt_ident_t _dtrace_globals[] = {
 { "count", DT_IDENT_AGGFUNC, 0, DTRACEAGG_COUNT, DT_ATTR_STABCMN, DT_VERS_1_0,
 	&dt_idops_func, "void()" },
 { "curcpu", DT_IDENT_SCALAR, 0, DIF_VAR_CURCPU, DT_ATTR_STABCMN, DT_VERS_1_0,
-	&dt_idops_type, "vmlinux`cpuinfo_t *" },
+	&dt_idops_type, "cpuinfo_t *" },
 { "curthread", DT_IDENT_SCALAR, 0, DIF_VAR_CURTHREAD,
 	{ DTRACE_STABILITY_STABLE, DTRACE_STABILITY_PRIVATE,
 	DTRACE_CLASS_COMMON }, DT_VERS_1_0,
diff --git a/libdtrace/dt_program.c b/libdtrace/dt_program.c
index 0b5eb4f0..5e1fd060 100644
--- a/libdtrace/dt_program.c
+++ b/libdtrace/dt_program.c
@@ -144,7 +144,7 @@ dtrace_program_exec(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
 	 * Create the global BPF maps.  This is done only once regardless of
 	 * how many programs there are.
 	 */
-	err = dt_bpf_gmap_create(dtp, 1);
+	err = dt_bpf_gmap_create(dtp);
 	if (err)
 		return err; /* dt_errno is set for us */
 
diff --git a/libdtrace/procfs.d.in b/libdtrace/procfs.d.in
index d4215041..cd857edf 100644
--- a/libdtrace/procfs.d.in
+++ b/libdtrace/procfs.d.in
@@ -17,9 +17,6 @@ typedef struct timestruc {
 	long tv_nsec;
 } timestruc_t;
 
-typedef id_t processorid_t;
-typedef id_t psetid_t;
-
 typedef struct lwpsinfo {
 	int pr_flag;			/* lwp flags (DEPRECATED) */
 	int pr_lwpid;			/* lwp id */
diff --git a/libdtrace/sched.d b/libdtrace/sched.d
index b11c7542..271d84e0 100644
--- a/libdtrace/sched.d
+++ b/libdtrace/sched.d
@@ -7,7 +7,20 @@
 
 #pragma D depends_on module vmlinux
 
-/*
+typedef id_t		processorid_t;
+typedef id_t		psetid_t;
+typedef id_t		chipid_t;
+typedef id_t		lgrp_id_t;
+
+typedef struct cpuinfo {
+	processorid_t	cpu_id;
+	psetid_t	cpu_pset;
+	chipid_t	cpu_chip;
+	lgrp_id_t	cpu_lgrp;
+} cpuinfo_t;
+
+typedef cpuinfo_t	*cpuinfo_t_p;
+
 inline processorid_t cpu = curcpu->cpu_id;
 #pragma D attributes Stable/Stable/Common cpu
 #pragma D binding "1.0" cpu
@@ -23,4 +36,3 @@ inline chipid_t chip = curcpu->cpu_chip;
 inline lgrp_id_t lgrp = curcpu->cpu_lgrp;
 #pragma D attributes Stable/Stable/Common lgrp
 #pragma D binding "1.0" lgrp
- */
-- 
2.26.0




More information about the DTrace-devel mailing list