[DTrace-devel] [PATCH 2/6] usdt: get arg types and xlations into DTrace from the DOF
Nick Alcock
nick.alcock at oracle.com
Fri Oct 18 19:58:04 UTC 2024
This change propagates native and xlated arg types and remapping info all
the way from the DOF, through the parser and dtprobed and the DOF stash,
into DTrace, where they end up in the pid_probespec_t for USDT probes.
We don't do anything with them once they get there in this commit: no
user-visible impacts expected.
We bump the DOF_PARSED_VERSION since we add three new types of record to the
dof_parser_t, all optional, covering native and xlated args and arg
remapping tables; to make life easier for consumers we emit them in a
defined order (documennted in dof_parser.h), and add a flags word to the
DIT_PROBE record that precedes them indicating which will be present. This
means we retain the property that you can always tell which records within a
provider are coming next purely from records you already have: there's no
need to make decisions once the records turn up. The DOF stash hardly
changes: all that happens is that the parsed data written to each per-probe
file gains some extra types of record (it can have DIT_NATIVE_ARGS,
DIT_XLAT_ARGS and DIT_REMAP_ARGS entries following the DIT_PROBE record
now).
As usual with DOF_PARSED_VERSION bumps, DTraces that cross this change will
refuse to read probes added by dtprobeds that are on the other side of it.
(Restarting dtprobed will reparse all the probes in the stash to match the
new layout, and newly-started DTraces will pick that up -- so this only
affects running DTraces picking up new probes, which DTrace cannot do yet:
so no visible effects are expected.)
The code is a bit repetitive, with nargs, xargs and remapping all handled
separately even though the code is very similar. In future maybe we could
unify them (but my attempts led to code that was much harder to read than
the original).
Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
---
dtprobed/dof_stash.c | 14 +++-
dtprobed/dtprobed.c | 12 ++-
include/dtrace/pid.h | 9 +++
libcommon/dof_parser.c | 167 ++++++++++++++++++++++++++++++++---------
libcommon/dof_parser.h | 80 ++++++++++++++++++--
libdtrace/dt_pid.c | 71 ++++++++++++++++++
6 files changed, 302 insertions(+), 51 deletions(-)
diff --git a/dtprobed/dof_stash.c b/dtprobed/dof_stash.c
index 0e639076f655..d9731844066c 100644
--- a/dtprobed/dof_stash.c
+++ b/dtprobed/dof_stash.c
@@ -59,8 +59,9 @@
* startup if the dof_parsed_t structure changed. The file consists of a
* uint64_t version number (DOF_PARSED_VERSION), then a stream of
* dof_parsed_t records, the first of type DIT_PROVIDER, the second
- * DIT_PROBE, then as many struct dof_parsed_t's of type DIT_TRACEPOINT as
- * the DIT_PROBE record specifies.
+ * DIT_PROBE, then optionally some DIT_*ARGS records if the probe's flags
+ * word calls for them, then as many struct dof_parsed_t's of type
+ * DIT_TRACEPOINT as the DIT_PROBE record specifies.
*
* /run/dtrace/probes/: Per-probe info, written by dtprobed, read by DTrace.
*
@@ -640,9 +641,14 @@ dof_stash_write_parsed(pid_t pid, dev_t dev, ino_t ino, dt_list_t *accum)
break;
}
- /* Tracepoint: add to already-open file. */
+ /* Args info or tracepoint: add to already-open file. */
+ case DIT_NATIVE_ARGS:
+ case DIT_XLAT_ARGS:
+ case DIT_REMAP_ARGS:
case DIT_TRACEPOINT:
- assert(state == DIT_PROBE || state == DIT_TRACEPOINT);
+ assert(state == DIT_PROBE || state == DIT_NATIVE_ARGS ||
+ state == DIT_XLAT_ARGS || state == DIT_REMAP_ARGS ||
+ state == DIT_TRACEPOINT);
state = accump->parsed->type;
if (write_chunk(parsed_fd, accump->parsed, accump->parsed->size) < 0)
diff --git a/dtprobed/dtprobed.c b/dtprobed/dtprobed.c
index fdcdee14f851..ecf27216ff80 100644
--- a/dtprobed/dtprobed.c
+++ b/dtprobed/dtprobed.c
@@ -795,16 +795,20 @@ process_dof(pid_t pid, int out, int in, dev_t dev, ino_t inum, dev_t exec_dev,
if (dof_stash_push_parsed(&accum, probe) < 0)
goto oom;
- for (j = 0; j < probe->probe.ntp; j++) {
+ j = 0;
+ do {
dof_parsed_t *tp = dof_read(pid, in);
- errmsg = "no tracepoints in a probe, or parse state corrupt";
- if (!tp || tp->type != DIT_TRACEPOINT)
+ errmsg = "no tracepoints in a probe, or parse state corrupt";
+ if (!tp || tp->type == DIT_PROVIDER || tp->type == DIT_PROBE)
goto err;
if (dof_stash_push_parsed(&accum, tp) < 0)
goto oom;
- }
+
+ if (tp->type == DIT_TRACEPOINT)
+ j++;
+ } while (j < probe->probe.ntp);
}
if (!reparsing)
diff --git a/include/dtrace/pid.h b/include/dtrace/pid.h
index 0129674adf0c..d9a5a40484a5 100644
--- a/include/dtrace/pid.h
+++ b/include/dtrace/pid.h
@@ -35,6 +35,15 @@ typedef struct pid_probespec {
ino_t pps_inum; /* object inode */
char *pps_fn; /* object full filename */
uint64_t pps_off; /* probe offset (in object) */
+ int pps_nargc; /* number of native args */
+ int pps_xargc; /* number of xlated args */
+ int pps_remapargc; /* number of remapped args */
+ char *pps_nargv; /* array of native args */
+ size_t pps_nargvlen; /* (high estimate of) length of array */
+ char *pps_xargv; /* array of xlated args */
+ size_t pps_xargvlen; /* (high estimate of) length of array */
+ int8_t *pps_remapargs; /* remapped arg indexes */
+ size_t pps_remapargslen; /* (high estimate of) length of array */
/*
* Fields below this point do not apply to underlying probes.
diff --git a/libcommon/dof_parser.c b/libcommon/dof_parser.c
index d6631a1afdcb..4a45ac41a64d 100644
--- a/libcommon/dof_parser.c
+++ b/libcommon/dof_parser.c
@@ -879,13 +879,31 @@ validate_probe(int out, dtrace_helper_probedesc_t *dhpb)
return 0;
}
+static size_t
+strings_len(const char *strtab, size_t count)
+{
+ size_t len = 0;
+
+ for (; count > 0; count--) {
+ size_t this_len = strlen(strtab) + 1;
+
+ len += this_len;
+ strtab += this_len;
+ }
+ return len;
+}
+
static void
emit_probe(int out, dtrace_helper_probedesc_t *dhpb)
{
int i;
- dof_parsed_t *probe_msg;
- size_t probe_msg_size;
+ dof_parsed_t *msg;
+ size_t msg_size;
+ size_t nargs_size;
+ size_t xargs_size;
+ size_t remap_size = 0;
char *ptr;
+ int remap_args = 0;
dt_dbg_dof(" Creating probe %s:%s:%s:%s\n", dhpb->dthpb_prov,
dhpb->dthpb_mod, dhpb->dthpb_func, dhpb->dthpb_name);
@@ -893,37 +911,124 @@ emit_probe(int out, dtrace_helper_probedesc_t *dhpb)
if (validate_probe(out, dhpb) != 0)
return;
- probe_msg_size = offsetof(dof_parsed_t, probe.name) +
- strlen(dhpb->dthpb_mod) + 1 + strlen(dhpb->dthpb_func) + 1 +
- strlen(dhpb->dthpb_name) + 1;
-
- probe_msg = malloc(probe_msg_size);
- if (!probe_msg) {
- dof_error(out, ENOMEM, "Out of memory allocating probe");
- return;
+ /*
+ * Figure out if the arguments are shuffled around (in which case we
+ * need to emit the argument remapping table).
+ */
+ for (i = 0; i < dhpb->dthpb_xargc; i++) {
+ if (dhpb->dthpb_args[i] != i) {
+ remap_args = 1;
+ break;
+ }
}
- memset(probe_msg, 0, probe_msg_size);
+ /*
+ * Compute the size of all the optional elements first, to fill out the
+ * flags.
+ */
- probe_msg->size = probe_msg_size;
- probe_msg->type = DIT_PROBE;
- probe_msg->probe.ntp = dhpb->dthpb_noffs + dhpb->dthpb_nenoffs;
- ptr = stpcpy(probe_msg->probe.name, dhpb->dthpb_mod);
+ msg_size = offsetof(dof_parsed_t, probe.name) +
+ strlen(dhpb->dthpb_mod) + 1 +
+ strlen(dhpb->dthpb_func) + 1 +
+ strlen(dhpb->dthpb_name) + 1;
+
+ nargs_size = strings_len(dhpb->dthpb_ntypes, dhpb->dthpb_nargc);
+ xargs_size = strings_len(dhpb->dthpb_xtypes, dhpb->dthpb_xargc);
+ if (remap_args)
+ remap_size = dhpb->dthpb_xargc * sizeof(uint8_t);
+
+ msg = malloc(msg_size);
+ if (!msg)
+ goto oom;
+
+ memset(msg, 0, msg_size);
+
+ msg->size = msg_size;
+ msg->type = DIT_PROBE;
+ msg->probe.ntp = dhpb->dthpb_noffs + dhpb->dthpb_nenoffs;
+
+ if (nargs_size)
+ msg->probe.flags |= DIT_HAS_NATIVE_ARGS;
+ if (xargs_size)
+ msg->probe.flags |= DIT_HAS_XLAT_ARGS;
+ if (remap_size)
+ msg->probe.flags |= DIT_HAS_REMAP_ARGS;
+
+ ptr = stpcpy(msg->probe.name, dhpb->dthpb_mod);
ptr++;
ptr = stpcpy(ptr, dhpb->dthpb_func);
ptr++;
strcpy(ptr, dhpb->dthpb_name);
- dof_parser_write_one(out, probe_msg, probe_msg_size);
+ dof_parser_write_one(out, msg, msg_size);
- free(probe_msg);
-
- /* XXX TODO translated args
- pp->ftp_nargs = dhpb->dthpb_xargc;
- pp->ftp_xtypes = dhpb->dthpb_xtypes;
- pp->ftp_ntypes = dhpb->dthpb_ntypes;
- */
+ free(msg);
/*
+ * Emit info on all native and translated args in turn.
+ *
+ * FIXME: this code is a bit repetitious.
+ *
+ * First native args (if any).
+ */
+
+ if (nargs_size > 0) {
+ msg_size = offsetof(dof_parsed_t, nargs.args) + nargs_size;
+
+ msg = malloc(msg_size);
+ if (!msg)
+ goto oom;
+
+ memset(msg, 0, msg_size);
+
+ msg->size = msg_size;
+ msg->type = DIT_NATIVE_ARGS;
+ msg->nargs.nargc = dhpb->dthpb_nargc;
+ memcpy(msg->nargs.args, dhpb->dthpb_ntypes, nargs_size);
+ dof_parser_write_one(out, msg, msg_size);
+
+ free(msg);
+ }
+
+ /* Then translated args. */
+
+ if (xargs_size > 0) {
+ msg_size = offsetof(dof_parsed_t, xargs.args) + xargs_size;
+
+ msg = malloc(msg_size);
+ if (!msg)
+ goto oom;
+
+ memset(msg, 0, msg_size);
+
+ msg->size = msg_size;
+ msg->type = DIT_XLAT_ARGS;
+ msg->xargs.nargc = dhpb->dthpb_xargc;
+ memcpy(msg->xargs.args, dhpb->dthpb_xtypes, xargs_size);
+ dof_parser_write_one(out, msg, msg_size);
+
+ free(msg);
+ }
+
+ /* Then the remapping table. */
+
+ if (remap_size > 0) {
+ msg_size = offsetof(dof_parsed_t, argremap.remapargs) + remap_size;
+
+ msg = malloc(msg_size);
+ if (!msg)
+ goto oom;
+
+ memset(msg, 0, msg_size);
+
+ msg->size = msg_size;
+ msg->type = DIT_REMAP_ARGS;
+ msg->argremap.nremapargs = dhpb->dthpb_xargc;
+ memcpy(msg->argremap.remapargs, dhpb->dthpb_args, remap_size);
+ dof_parser_write_one(out, msg, msg_size);
+ free(msg);
+ }
+
+ /*
* Emit info on each tracepoint in turn.
*/
for (i = 0; i < dhpb->dthpb_noffs; i++)
@@ -938,18 +1043,10 @@ emit_probe(int out, dtrace_helper_probedesc_t *dhpb)
for (i = 0; i < dhpb->dthpb_nenoffs; i++)
emit_tp(out, dhpb->dthpb_base, dhpb->dthpb_enoffs[i], 1);
- /*
- * XXX later:
- * If the arguments are shuffled around we set the argument remapping
- * table. Later, when the probe fires, we only remap the arguments
- * if the table is non-NULL.
- *
- for (i = 0; i < dhpb->dthpb_xargc; i++) {
- if (dhpb->dthpb_args[i] != i) {
- pp->ftp_argmap = dhpb->dthpb_args;
- break;
- }
- } */
+ return;
+ oom:
+ dof_error(out, ENOMEM, "Out of memory allocating %zi bytes for probe",
+ msg_size);
}
static void
diff --git a/libcommon/dof_parser.h b/libcommon/dof_parser.h
index d3a6836f15fd..bfcac2c52de4 100644
--- a/libcommon/dof_parser.h
+++ b/libcommon/dof_parser.h
@@ -15,10 +15,19 @@
#include <dtrace/helpers.h>
/*
- * Result of DOF probe parsing. We receive a provider info structure, followed
- * by N probe info structures each of which is followed by possibly many
- * tracepoint info structures, all tagged. Things not yet used for probe
- * creation (like args, translated types, etc) are not returned.
+ * Result of DOF probe parsing. The order of elements in the parsed stream
+ * is:
+ *
+ * DIT_PROVIDER (at least 1, which contains...)
+ * DIT_PROBE (at least 1, each of which has...)
+ * DIT_NATIVE_ARGS (1, optional)
+ * DIT_XLAT_ARGS (1, optional)
+ * DIT_REMAP_ARGS (1, optional)
+ * DIT_TRACEPOINT (any number >= 1)
+ *
+ * The dof_parsed.provider.flags word indicates the presence of the
+ * various optional args records in the following stream (you can rely on
+ * them if it simplifies things, but you don't have to).
*
* On error, a DIT_ERR structure is returned with an error message.
*/
@@ -27,9 +36,19 @@ typedef enum dof_parsed_info {
DIT_PROVIDER = 0,
DIT_PROBE = 1,
DIT_TRACEPOINT = 2,
- DIT_ERR = 3
+ DIT_ERR = 3,
+ DIT_NATIVE_ARGS = 4,
+ DIT_XLAT_ARGS = 5,
+ DIT_REMAP_ARGS = 6,
} dof_parsed_info_t;
+/*
+ * Record-presence flags for dof_parsed.provider.flags.
+ */
+#define DIT_HAS_NATIVE_ARGS 0x1
+#define DIT_HAS_XLAT_ARGS 0x2
+#define DIT_HAS_REMAP_ARGS 0x4
+
/*
* Bump this whenever dof_parsed changes.
*
@@ -37,7 +56,7 @@ typedef enum dof_parsed_info {
* start which is the version of the dof_parseds within it. The data flowing
* over the stream from the seccomped parser has no such prefix.
*/
-#define DOF_PARSED_VERSION 1
+#define DOF_PARSED_VERSION 2
typedef struct dof_parsed {
/*
@@ -59,17 +78,62 @@ typedef struct dof_parsed {
*/
char name[1];
} provider;
- struct dpi_probe_info {
+
+ struct dpi_probe_info {
/*
* Number of tracepoints that follow.
*/
size_t ntp;
+ /*
+ * Flags indicating the presence of DIT_*_ARGS.
+ */
+ int flags;
+
/*
* Probe module, function, and name (\0-separated).
*/
char name[1];
- } probe;
+ } probe;
+
+ /* V2+ only. */
+ struct dpi_probe_native_args {
+ /*
+ * Number of native args.
+ */
+ uint8_t nargc;
+ /*
+ * Array of native args.
+ */
+ char args[1];
+ } nargs;
+
+ /* V2+ only. */
+ struct dpi_probe_xlat_args {
+ /*
+ * Number of translated args.
+ */
+ uint8_t nargc;
+ /*
+ * Array of translated args.
+ */
+ char args[1];
+ } xargs;
+
+ /*
+ * V2+ only. If provided, the given args have been shuffled.
+ */
+ struct dpi_probe_remap_args {
+ /*
+ * Number of remapped arguments.
+ */
+ uint8_t nremapargs;
+
+ /*
+ * Mapping from native arg index to xlated arg index.
+ */
+ uint8_t remapargs[1];
+ } argremap;
struct dpi_tracepoint_info {
/*
diff --git a/libdtrace/dt_pid.c b/libdtrace/dt_pid.c
index 62060b5921b6..356b671c8385 100644
--- a/libdtrace/dt_pid.c
+++ b/libdtrace/dt_pid.c
@@ -866,6 +866,10 @@ dt_pid_create_usdt_probes_proc(dtrace_hdl_t *dtp, dt_proc_t *dpr,
uint64_t *dof_version;
char *prv, *mod, *fun, *prb;
dof_parsed_t *provider, *probe;
+ int nargc = 0, xargc = 0, remapargc = 0;
+ ssize_t nargvlen = 0, xargvlen = 0, remapargslen = 0;
+ char *nargv = NULL, *xargv = NULL;
+ int8_t *remapargs = NULL;
/*
* Regular files only: in particular, skip . and ..,
@@ -917,6 +921,55 @@ dt_pid_create_usdt_probes_proc(dtrace_hdl_t *dtp, dt_proc_t *dpr,
p += probe->size;
seen_size += probe->size;
+ /*
+ * Assume the order given in dof_parser.h, for simplicity.
+ */
+ if (probe->probe.flags & DIT_HAS_NATIVE_ARGS) {
+ dof_parsed_t *args = (dof_parsed_t *) p;
+
+ if (!validate_dof_record(path, args, DIT_NATIVE_ARGS,
+ dof_buf_size, seen_size))
+ goto parse_err;
+
+ nargc = args->nargs.nargc;
+ nargv = args->nargs.args;
+ nargvlen = args->size - offsetof(dof_parsed_t, nargs.args);
+ assert(nargvlen >= 0);
+
+ p += args->size;
+ seen_size += args->size;
+ }
+ if (probe->probe.flags & DIT_HAS_XLAT_ARGS) {
+ dof_parsed_t *args = (dof_parsed_t *) p;
+
+ if (!validate_dof_record(path, args, DIT_XLAT_ARGS,
+ dof_buf_size, seen_size))
+ goto parse_err;
+
+ xargc = args->xargs.nargc;
+ xargv = args->xargs.args;
+ xargvlen = args->size - offsetof(dof_parsed_t, xargs.args);
+ assert(xargvlen >= 0);
+
+ p += args->size;
+ seen_size += args->size;
+ }
+ if (probe->probe.flags & DIT_HAS_REMAP_ARGS) {
+ dof_parsed_t *args = (dof_parsed_t *) p;
+
+ if (!validate_dof_record(path, args, DIT_REMAP_ARGS,
+ dof_buf_size, seen_size))
+ goto parse_err;
+
+ remapargc = args->argremap.nremapargs;
+ remapargs = args->argremap.remapargs;
+ remapargslen = args->size - offsetof(dof_parsed_t, argremap.remapargs);
+ assert(remapargslen >= 0);
+
+ p += args->size;
+ seen_size += args->size;
+ }
+
/*
* Now the parsed DOF for this probe's tracepoints.
*/
@@ -967,6 +1020,24 @@ dt_pid_create_usdt_probes_proc(dtrace_hdl_t *dtp, dt_proc_t *dpr,
psp.pps_off = tp->tracepoint.addr - pmp->pr_file->first_segment->pr_vaddr;
psp.pps_nameoff = 0;
+ if (nargv) {
+ psp.pps_nargc = nargc;
+ psp.pps_nargvlen = nargvlen;
+ psp.pps_nargv = nargv;
+ }
+
+ if (xargv) {
+ psp.pps_xargc = xargc;
+ psp.pps_xargvlen = xargvlen;
+ psp.pps_xargv = xargv;
+ }
+
+ if (remapargs) {
+ psp.pps_remapargc = remapargc;
+ psp.pps_remapargslen = remapargslen;
+ psp.pps_remapargs = remapargs;
+ }
+
dt_dprintf("providing %s:%s:%s:%s for pid %d\n", psp.pps_prv,
psp.pps_mod, psp.pps_fun, psp.pps_prb, psp.pps_pid);
if (pvp->impl->provide_probe(dtp, &psp) < 0) {
--
2.46.0.278.g36e3a12567
More information about the DTrace-devel
mailing list