[DTrace-devel] [PATCH 1/5] Split out gmap creation into their own functions
Kris Van Hees
kris.van.hees at oracle.com
Fri Aug 19 17:25:58 UTC 2022
A lot of BPF maps are created for DTrace. Rather than creating them all
in one big function, implement them as individual functions.
Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
libdtrace/dt_bpf.c | 482 ++++++++++++++++++++++++++++-----------------
1 file changed, 299 insertions(+), 183 deletions(-)
diff --git a/libdtrace/dt_bpf.c b/libdtrace/dt_bpf.c
index f3f8c79d..720fe02d 100644
--- a/libdtrace/dt_bpf.c
+++ b/libdtrace/dt_bpf.c
@@ -286,163 +286,174 @@ create_gmap(dtrace_hdl_t *dtp, const char *name, enum bpf_map_type type,
return fd;
}
-static void
-populate_probes_map(dtrace_hdl_t *dtp, int fd)
+/*
+ * Create the 'state' BPF map.
+ *
+ * DTrace session state, used to communicate state between BPF programs and
+ * userspace. The content of the map is defined in dt_state.h.
+ */
+static int
+gmap_create_state(dtrace_hdl_t *dtp)
{
- dt_probe_t *prp;
-
- for (prp = dt_list_next(&dtp->dt_enablings); prp != NULL;
- prp = dt_list_next(prp)) {
- dt_bpf_probe_t pinfo;
-
- pinfo.prv = dt_strtab_index(dtp->dt_ccstab, prp->desc->prv);
- pinfo.mod = dt_strtab_index(dtp->dt_ccstab, prp->desc->mod);
- pinfo.fun = dt_strtab_index(dtp->dt_ccstab, prp->desc->fun);
- pinfo.prb = dt_strtab_index(dtp->dt_ccstab, prp->desc->prb);
+ dtp->dt_stmap_fd = create_gmap(dtp, "state", BPF_MAP_TYPE_ARRAY,
+ sizeof(DT_STATE_KEY_TYPE),
+ sizeof(DT_STATE_VAL_TYPE),
+ DT_STATE_NUM_ELEMS);
- dt_bpf_map_update(fd, &prp->desc->id, &pinfo);
- }
+ return dtp->dt_stmap_fd;
}
/*
- * Create the global BPF maps that are shared between all BPF programs in a
- * single tracing session:
+ * Create the 'aggs' BPF map.
*
- * - state: DTrace session state, used to communicate state between BPF
- * programs and userspace. The content of the map is defined in
- * dt_state.h.
- * - aggs: Aggregation data buffer map, associated with each CPU. The
- * map is implemented as a global per-CPU map with a singleton
- * element (key 0).
- * - specs: Map associating speculation IDs with a dt_bpf_specs_t struct
- * giving the number of buffers speculated into for this
- * speculation, and the number drained by userspace.
- * - 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: Scratch memory. This 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 can use it
- * without interference from other CPUs. The scratch memory is
- * used to store the DTrace machine state, the temporary output
- * buffer, and temporary storage for stack traces, string
- * manipulation, etc.
- * - scratchmem: Storage for alloca() and other per-clause scratch space,
- * implemented just as for mem.
- * - strtab: String table map. This is a global map with a singleton
- * element (key 0) that contains the entire string table as a
- * concatenation of all unique strings (each terminated with a
- * NUL byte). The string table size is taken from the DTrace
- * consumer handle (dt_strlen). Extra memory is allocated as a
- * memory block of zeros for initializing memory regions. Its
- * size is at least the maximum string size to ensure the BPF
- * verifier can validate all access requests for dynamic
- * references to string constants.
- * - probes: Probe information map. This is a global map indexed by probe
- * ID. The value is a struct that contains static probe info.
- * The map only contains entries for probes that are actually in
- * use.
- * - gvars: Global variables map. This is a global map with a singleton
- * element (key 0) addressed by variable offset.
- * - dvars: Dynamic variables map. This is a global hash map indexed with
- * a unique numeric identifier for each dynamic variable (thread
- * local variable or associative array element). The value size
- * is the largest dynamic variable size across all programs in the
- * tracing session.
- * - lvars: Local variables map. This is a per-CPU map with a singleton
- * element (key 0) addressed by variable offset.
- * - tuples: Tuple-to-id map. This is a global hash map indexed with a
- * tuple. The value associated with the tuple key is an id that
- * is used to index the dvars map. The key size is determined as
- * the largest tuple used across all programs in the tracing
- * session.
+ * Aggregation data buffer map, associated with each CPU. The map is
+ * implemented as a global per-CPU map with a singleton element (key 0).
*/
-int
-dt_bpf_gmap_create(dtrace_hdl_t *dtp)
+static int
+gmap_create_aggs(dtrace_hdl_t *dtp)
{
- size_t sz;
- int dvarc = 0;
- int ci_mapfd, st_mapfd, pr_mapfd;
- uint64_t key = 0;
- size_t strsize = dtp->dt_options[DTRACEOPT_STRSIZE];
- size_t scratchsize = dtp->dt_options[DTRACEOPT_SCRATCHSIZE];
- uint8_t *buf, *end;
- char *strtab;
+ size_t sz = dt_idhash_datasize(dtp->dt_aggs);
- /* If we already created the global maps, return success. */
- if (dt_gmap_done)
+ /* Only create the map if it is used. */
+ if (sz == 0)
return 0;
- /* Mark global maps creation as completed. */
- dt_gmap_done = 1;
+ dtp->dt_aggmap_fd = create_gmap(dtp, "aggs", BPF_MAP_TYPE_PERCPU_ARRAY,
+ sizeof(uint32_t), sz, 1);
- /* Create BPF maps as long as there are no errors. */
+ return dtp->dt_aggmap_fd;
- /* state map */
- dtp->dt_stmap_fd = create_gmap(dtp, "state", BPF_MAP_TYPE_ARRAY,
- sizeof(DT_STATE_KEY_TYPE),
- sizeof(DT_STATE_VAL_TYPE),
- DT_STATE_NUM_ELEMS);
- if (dtp->dt_stmap_fd == -1)
- return -1; /* dt_errno is set for us */
+}
- /*
- * Check if there is aggregation data to be collected.
- */
- sz = dt_idhash_datasize(dtp->dt_aggs);
- if (sz > 0) {
- dtp->dt_aggmap_fd = create_gmap(dtp, "aggs",
- BPF_MAP_TYPE_PERCPU_ARRAY,
- sizeof(uint32_t), sz, 1);
- if (dtp->dt_aggmap_fd == -1)
- return -1; /* dt_errno is set for us */
- }
+/*
+ * Create the 'specs' BPF map.
+ *
+ * DTrace session state, used to communicate state between BPF programs and
+ * userspace. The content of the map is defined in dt_state.h.
+ * Map associating speculation IDs with a dt_bpf_specs_t struct giving the
+ * number of buffers speculated into for this speculation, and the number
+ * drained by userspace.
+ */
+static int
+gmap_create_specs(dtrace_hdl_t *dtp)
+{
+ return create_gmap(dtp, "specs", BPF_MAP_TYPE_HASH, sizeof(uint32_t),
+ sizeof(dt_bpf_specs_t),
+ dtp->dt_options[DTRACEOPT_NSPEC]);
+}
+
+/*
+ * Create the 'buffers' BPF map.
+ *
+ * Perf event output buffer map, associating a perf event output buffer with
+ * each CPU. The map is indexed by CPU id.
+ */
+static int
+gmap_create_buffers(dtrace_hdl_t *dtp)
+{
+ return create_gmap(dtp, "buffers", BPF_MAP_TYPE_PERF_EVENT_ARRAY,
+ sizeof(uint32_t), sizeof(uint32_t),
+ dtp->dt_conf.num_online_cpus);
+}
- /* speculations */
- if (create_gmap(dtp, "specs", BPF_MAP_TYPE_HASH,
- sizeof(uint32_t), sizeof(dt_bpf_specs_t),
- dtp->dt_options[DTRACEOPT_NSPEC]) == -1)
- return -1; /* dt_errno is set for us */
+/*
+ * Create the 'cpuinfo' BPF map.
+ *
+ * CPU information map, associating a cpuinfo_t structure with each online CPU
+ * on the system.
+ */
+static int
+gmap_create_cpuinfo(dtrace_hdl_t *dtp)
+{
+ int fd;
+ uint32_t key = 0;
- /* output buffers */
- if (create_gmap(dtp, "buffers", BPF_MAP_TYPE_PERF_EVENT_ARRAY,
- sizeof(uint32_t), sizeof(uint32_t),
- dtp->dt_conf.num_online_cpus) == -1)
- return -1; /* dt_errno is set for us */
+ fd = create_gmap(dtp, "cpuinfo", BPF_MAP_TYPE_PERCPU_ARRAY,
+ sizeof(uint32_t), sizeof(cpuinfo_t), 1);
+ if (fd == -1)
+ return -1;
- /* cpuinfo */
- 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 (dt_bpf_map_update(fd, &key, dtp->dt_conf.cpus) == -1)
+ return dt_bpf_error(dtp,
+ "cannot update BPF map '%cpuinfo': %s\n",
+ strerror(errno));
- /*
- * The size of the map value (a byte array) is the sum of:
- * - size of the DTrace machine state, rounded up to the nearest
- * multiple of 8
- * - 8 bytes padding for trace buffer alignment purposes
- * - maximum trace buffer record size, rounded up to the nearest
- * multiple of 8
- * - size of dctx->mem (see dt_dctx.h)
- */
- sz = roundup(sizeof(dt_mstate_t), 8) +
+ return 0;
+}
+
+/*
+ * Create the 'mem' BPF map.
+ *
+ * CPU local storage. This 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 can use it without interference from other
+ * CPUs. The local storage is used to store the DTrace machine state, the
+ * temporary output buffer, and temporary storage for stack traces, string
+ * manipulation, etc.
+ *
+ * The size of the memory region is the sum of:
+ * - size of the DTrace machine state, rounded up to the nearest
+ * multiple of 8
+ * - 8 bytes padding for trace buffer alignment purposes
+ * - maximum trace buffer record size, rounded up to the nearest
+ * multiple of 8
+ * - size of dctx->mem (see dt_dctx.h)
+ */
+static int
+gmap_create_mem(dtrace_hdl_t *dtp)
+{
+ size_t sz = roundup(sizeof(dt_mstate_t), 8) +
8 +
roundup(dtp->dt_maxreclen, 8) +
DMEM_SIZE(dtp);
- if (create_gmap(dtp, "mem", BPF_MAP_TYPE_PERCPU_ARRAY,
- sizeof(uint32_t), sz, 1) == -1)
- return -1; /* dt_errno is set for us */
- /*
- * The size for this is twice what it needs to be, to allow us to bcopy
- * things the size of the scratch space to the start of the scratch
- * space without tripping verifier failures: see dt_cg_check_bounds.
- */
- if (scratchsize > 0 &&
- create_gmap(dtp, "scratchmem", BPF_MAP_TYPE_PERCPU_ARRAY,
- sizeof(uint32_t), scratchsize * 2, 1) == -1)
- return -1; /* dt_errno is set for us */
+ return create_gmap(dtp, "mem", BPF_MAP_TYPE_PERCPU_ARRAY,
+ sizeof(uint32_t), sz, 1);
+}
+
+/*
+ * Create the 'scratchmem' BPF map.
+ *
+ * Storage for alloca() and other per-clause scratch space, implemented just as
+ * for mem.
+ *
+ * The size for this is twice what it needs to be, to allow us to bcopy things
+ * the size of the scratch space to the start of the scratch space without
+ * tripping verifier failures: see dt_cg_check_bounds.
+ */
+static int
+gmap_create_scratchmem(dtrace_hdl_t *dtp)
+{
+ size_t sz = dtp->dt_options[DTRACEOPT_SCRATCHSIZE];
+
+ /* Only create the map if it is used. */
+ if (sz == 0)
+ return 0;
+
+ return create_gmap(dtp, "scratchmem", BPF_MAP_TYPE_PERCPU_ARRAY,
+ sizeof(uint32_t), sz * 2, 1);
+}
+
+/*
+ * Create the 'strtab' BPF map.
+ *
+ * String table map. This is a global map with a singleton element (key 0)
+ * that contains the entire string table as a concatenation of all unique
+ * strings (each terminated with a NUL byte). The string table size is taken
+ * from the DTrace consumer handle (dt_strlen). Extra memory is allocated as a
+ * memory block of zeros for initializing memory regions. Its size is at least
+ * the maximum string size to ensure the BPF verifier can validate all access
+ * requests for dynamic references to string constants.
+ */
+static int
+gmap_create_strtab(dtrace_hdl_t *dtp)
+{
+ size_t sz;
+ uint8_t *buf, *end;
+ char *strtab;
+ size_t strsize = dtp->dt_options[DTRACEOPT_STRSIZE];
+ uint32_t key = 0;
+ int fd;
/*
* We need to create the global (consolidated) string table. We store
@@ -480,58 +491,163 @@ dt_bpf_gmap_create(dtrace_hdl_t *dtp)
buf += len + 1;
}
- st_mapfd = create_gmap(dtp, "strtab", BPF_MAP_TYPE_ARRAY,
- sizeof(uint32_t), sz, 1);
- if (st_mapfd == -1)
- return -1; /* dt_errno is set for us */
-
- /* probe hash table */
- pr_mapfd = create_gmap(dtp, "probes", BPF_MAP_TYPE_HASH,
- sizeof(uint32_t), sizeof(dt_bpf_probe_t),
- dt_list_length(&dtp->dt_enablings));
- if (pr_mapfd == -1)
- return -1; /* dt_errno is set for us */
-
- /* global variables */
- sz = dt_idhash_datasize(dtp->dt_globals);
- if (sz > 0 &&
- create_gmap(dtp, "gvars", BPF_MAP_TYPE_ARRAY,
- sizeof(uint32_t), sz, 1) == -1)
- return -1; /* dt_errno is set for us */
-
- /* local variables */
- sz = P2ROUNDUP(dtp->dt_maxlvaralloc, 8);
- if (sz > 0 && create_gmap(dtp, "lvars", BPF_MAP_TYPE_PERCPU_ARRAY,
- sizeof(uint32_t), sz, 1) == -1)
- return -1; /* dt_errno is set for us */
-
- /* TLS and dynamic variables */
- if (dtp->dt_maxdvarsize)
- dvarc = dtp->dt_options[DTRACEOPT_DYNVARSIZE] /
- dtp->dt_maxdvarsize;
-
- if (dvarc > 0) {
- if (create_gmap(dtp, "dvars", BPF_MAP_TYPE_HASH,
- sizeof(uint64_t), dtp->dt_maxdvarsize,
- dvarc) == -1)
- return -1; /* dt_errno is set for us */
-
- assert(dtp->dt_maxtuplesize > 0);
-
- if (create_gmap(dtp, "tuples", BPF_MAP_TYPE_HASH,
- dtp->dt_maxtuplesize, sizeof(uint64_t),
- dvarc) == -1)
- return -1; /* dt_errno is set for us */
+ fd = create_gmap(dtp, "strtab", BPF_MAP_TYPE_ARRAY, sizeof(uint32_t),
+ sz, 1);
+ if (fd == -1)
+ return -1;
+
+ if (dt_bpf_map_update(fd, &key, strtab) == -1)
+ return dt_bpf_error(dtp, "cannot update BPF map 'strtab': %s\n",
+ strerror(errno));
+
+ return 0;
+}
+
+/*
+ * Create the 'probes' BPF map.
+ *
+ * Probe information map. This is a global map indexed by probe ID. The value
+ * is a struct that contains static probe info. The map only contains entries
+ 8 for probes that are actually in use.
+ */
+static int
+gmap_create_probes(dtrace_hdl_t *dtp)
+{
+ int fd;
+ dt_probe_t *prp;
+
+ fd = create_gmap(dtp, "probes", BPF_MAP_TYPE_HASH, sizeof(uint32_t),
+ sizeof(dt_bpf_probe_t),
+ dt_list_length(&dtp->dt_enablings));
+ if (fd == -1)
+ return -1;
+
+ for (prp = dt_list_next(&dtp->dt_enablings); prp != NULL;
+ prp = dt_list_next(prp)) {
+ dt_bpf_probe_t pinfo;
+
+ pinfo.prv = dt_strtab_index(dtp->dt_ccstab, prp->desc->prv);
+ pinfo.mod = dt_strtab_index(dtp->dt_ccstab, prp->desc->mod);
+ pinfo.fun = dt_strtab_index(dtp->dt_ccstab, prp->desc->fun);
+ pinfo.prb = dt_strtab_index(dtp->dt_ccstab, prp->desc->prb);
+
+ if (dt_bpf_map_update(fd, &prp->desc->id, &pinfo) < 0)
+ return dt_bpf_error(
+ dtp, "cannot update BPF map 'probes': %s\n",
+ strerror(errno));
}
- /* Populate the 'cpuinfo' map. */
- dt_bpf_map_update(ci_mapfd, &key, dtp->dt_conf.cpus);
+ return 0;
+}
+
+/*
+ * Create the 'gvars' BPF map.
+ *
+ * Global variables map. This is a global map with a singleton element (key 0)
+ * addressed by variable offset.
+ */
+static int
+gmap_create_gvars(dtrace_hdl_t *dtp)
+{
+ size_t sz = dt_idhash_datasize(dtp->dt_globals);
+
+ /* Only create the map if it is used. */
+ if (sz == 0)
+ return 0;
+
+ return create_gmap(dtp, "gvars", BPF_MAP_TYPE_ARRAY, sizeof(uint32_t),
+ sz, 1);
+}
+
+/*
+ * Create the 'lvars' BPF map.
+ *
+ * Local variables map. This is a per-CPU map with a singleton element (key 0)
+ * addressed by variable offset.
+ */
+static int
+gmap_create_lvars(dtrace_hdl_t *dtp)
+{
+ size_t sz = P2ROUNDUP(dtp->dt_maxlvaralloc, 8);
+
+ /* Only create the map if it is used. */
+ if (sz == 0)
+ return 0;
+
+ return create_gmap(dtp, "lvars", BPF_MAP_TYPE_PERCPU_ARRAY,
+ sizeof(uint32_t), sz, 1);
+}
+
+/*
+ * Create the 'dvars' BPF map (and its companion 'tuples' BPF map).
+ *
+ * - dvars: Dynamic variables map. This is a global hash map indexed with
+ * a unique numeric identifier for each dynamic variable (thread
+ * local variable or associative array element). The value size
+ * is the largest dynamic variable size across all programs in the
+ * tracing session.
+ * - tuples: Tuple-to-id map. This is a global hash map indexed with a
+ * tuple. The value associated with the tuple key is an id that
+ * is used to index the dvars map. The key size is determined as
+ * the largest tuple used across all programs in the tracing
+ * session.
+ */
+static int
+gmap_create_dvars(dtrace_hdl_t *dtp)
+{
+ size_t nelems = 0;
+
+ /* Only create the map if it is used. */
+ if (dtp->dt_maxdvarsize == 0)
+ return 0;
+
+ nelems = dtp->dt_options[DTRACEOPT_DYNVARSIZE] / dtp->dt_maxdvarsize;
+ if (nelems == 0)
+ return 0;
+
+ if (create_gmap(dtp, "dvars", BPF_MAP_TYPE_HASH, sizeof(uint64_t),
+ dtp->dt_maxdvarsize, nelems) == -1)
+ return -1;
+
+ /* Only create the map if it is used. */
+ if (dtp->dt_maxtuplesize == 0)
+ return 0;
+
+ return create_gmap(dtp, "tuples", BPF_MAP_TYPE_HASH,
+ dtp->dt_maxtuplesize, sizeof(uint64_t), nelems);
+}
+
+/*
+ * Create the global BPF maps that are shared between all BPF programs in a
+ * single tracing session.
+ */
+int
+dt_bpf_gmap_create(dtrace_hdl_t *dtp)
+{
+ /* If we already created the global maps, return success. */
+ if (dt_gmap_done)
+ return 0;
+
+ /* Mark global maps creation as completed. */
+ dt_gmap_done = 1;
- /* Populate the 'strtab' map. */
- dt_bpf_map_update(st_mapfd, &key, strtab);
+#define CREATE_MAP(name) \
+ if (gmap_create_##name(dtp) == -1) \
+ return -1;
- /* Populate the 'probes' map. */
- populate_probes_map(dtp, pr_mapfd);
+ CREATE_MAP(state)
+ CREATE_MAP(aggs)
+ CREATE_MAP(specs)
+ CREATE_MAP(buffers)
+ CREATE_MAP(cpuinfo)
+ CREATE_MAP(mem)
+ CREATE_MAP(scratchmem)
+ CREATE_MAP(strtab)
+ CREATE_MAP(probes)
+ CREATE_MAP(gvars)
+ CREATE_MAP(lvars)
+ CREATE_MAP(dvars)
+#undef CREATE_MAP
return 0;
}
--
2.34.1
More information about the DTrace-devel
mailing list