[DTrace-devel] [PATCH 2/2 v2] Add support for the dirname() action

eugene.loh at oracle.com eugene.loh at oracle.com
Fri Nov 19 03:23:24 UTC 2021


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

Signed-off-by: Eugene Loh <eugene.loh at oracle.com>
---
 bpf/Build                          |   1 +
 bpf/dirname.S                      | 140 +++++++++++++++++++++++++++++
 libdtrace/dt_cg.c                  |   8 +-
 libdtrace/dt_dlibs.c               |   1 +
 test/unittest/dif/dirname.d        |   1 -
 test/unittest/funcs/tst.dirname0.d |  39 ++++++++
 test/unittest/funcs/tst.dirname0.r |  21 +++++
 7 files changed, 209 insertions(+), 2 deletions(-)
 create mode 100644 bpf/dirname.S
 create mode 100644 test/unittest/funcs/tst.dirname0.d
 create mode 100644 test/unittest/funcs/tst.dirname0.r

diff --git a/bpf/Build b/bpf/Build
index e66e8dc9..258a505d 100644
--- a/bpf/Build
+++ b/bpf/Build
@@ -24,6 +24,7 @@ bpf_dlib_SRCDEPS = $(objdir)/include/.dir.stamp
 bpf_dlib_SOURCES = \
 	agg_lqbin.c agg_qbin.c \
 	basename.S \
+	dirname.S \
 	get_bvar.c \
 	get_tvar.c set_tvar.c \
 	index.S \
diff --git a/bpf/dirname.S b/bpf/dirname.S
new file mode 100644
index 00000000..1ab676ce
--- /dev/null
+++ b/bpf/dirname.S
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
+ */
+
+#define DT_STRLEN_BYTES		2
+
+#define BPF_FUNC_probe_read_str	45
+
+/*
+ * void dt_dirname(char *src, char *dst);
+ */
+	.text
+	.align	4
+	.global	dt_dirname
+	.type	dt_dirname, @function
+dt_dirname :
+#define SRC %r6
+#define DST %r7
+#define LEN %r8
+
+	/* store copies of input arguments */
+	mov	SRC, %r1
+	mov	DST, %r2
+
+	/* ignore the string-length prefix */
+	add	SRC, DT_STRLEN_BYTES
+
+	/* r0 = bpf_probe_read_str(dst, STRSZ + 1, src) */
+	mov	%r1, DST
+	lddw	%r2, STRSZ
+	add	%r2, 1
+	mov	%r3, SRC
+	call	BPF_FUNC_probe_read_str
+
+	/* if (r0 s<= 1) goto Ldot */
+	jsle	%r0, 1, .Ldot
+
+	/* len = r0 - 1 */
+	mov	LEN, %r0
+	sub	LEN, 1
+
+	/*
+	 * In general, the string will look like
+	 * "dirname////basename////".
+	 */
+
+	/*
+	 * Back up, looking for a non-'/' char.
+	 * That gives us the end of basename.
+	 */
+.L1:
+	/* len-- */
+	sub	LEN, 1
+	/* if (len s< 0) goto Lslash */
+	jslt	LEN, 0, .Lslash
+	/* if (src[len] == '/') goto L1 */
+	mov	%r0, SRC
+	add	%r0, LEN
+	ldxb	%r0, [%r0+0]
+	and	%r0, 0xff
+	jeq	%r0, '/', .L1
+
+	/*
+	 * Back up, looking for a '/' char.
+	 * That gives us right before the start of basename.
+	 */
+.L2:
+	/* len-- */
+	sub	LEN, 1
+	/* if (len s< 0) goto Ldot */
+	jslt	LEN, 0, .Ldot
+	/* if (src[len] != '/') goto L2 */
+	mov	%r0, SRC
+	add	%r0, LEN
+	ldxb	%r0, [%r0+0]
+	and	%r0, 0xff
+	jne	%r0, '/', .L2
+
+	/*
+	 * Back up, looking for a non-'/' char.
+	 * That gives us the end of dirname.
+	 */
+.L3:
+	/* len-- */
+	sub	LEN, 1
+	/* if (len s< 0) goto Lslash */
+	jslt	LEN, 0, .Lslash
+	/* if (src[len] == '/') goto L3 */
+	mov	%r0, SRC
+	add	%r0, LEN
+	ldxb	%r0, [%r0+0]
+	and	%r0, 0xff
+	jeq	%r0, '/', .L3
+
+	/* len++ */
+	add	LEN, 1
+
+	/*
+	 * The output string is up to the end of the dirname.
+	 */
+
+	/* bpf_probe_read_str(dst + DT_STRLEN_BYTES, len + 1, src) */
+	mov	%r1, DST
+	add	%r1, DT_STRLEN_BYTES
+	mov	%r2, LEN
+	add	%r2, 1
+	mov	%r3, SRC
+	call	BPF_FUNC_probe_read_str
+
+.Lcoda:
+	/* dt_strlen_store(len, dst) */
+	mov	%r1, LEN
+	mov	%r2, DST
+	call	dt_strlen_store
+
+	/* return */
+	exit
+
+.Ldot:
+	/*
+	 * The output string is simply ".".
+	 */
+	mov	LEN, 1
+	stb	[DST+DT_STRLEN_BYTES], '.'
+	stb	[DST+(DT_STRLEN_BYTES+1)], '\0'
+	ja	.Lcoda
+
+.Lslash:
+	/*
+	 * The output string is simply "/".
+	 */
+	mov	LEN, 1
+	stb	[DST+DT_STRLEN_BYTES], '/'
+	stb	[DST+(DT_STRLEN_BYTES+1)], '\0'
+	ja	.Lcoda
+#undef SRC
+#undef DST
+#undef LEN
+	.size	dt_dirname, .-dt_dirname
diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 9808802c..e85b0422 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -3348,6 +3348,12 @@ dt_cg_subr_basename(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	dt_cg_subr_str_of_str(dnp, dlp, drp, "dt_basename");
 }
 
+static void
+dt_cg_subr_dirname(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
+{
+	dt_cg_subr_str_of_str(dnp, dlp, drp, "dt_dirname");
+}
+
 /*
  * Get and return a new speculation ID.  These are unallocated entries in the
  * specs map, obtained by calling dt_speculation().  Return zero if none is
@@ -4041,7 +4047,7 @@ static dt_cg_subr_f *_dt_cg_subr[DIF_SUBR_MAX + 1] = {
 	[DIF_SUBR_STRJOIN]		= dt_cg_subr_strjoin,
 	[DIF_SUBR_LLTOSTR]		= &dt_cg_subr_lltostr,
 	[DIF_SUBR_BASENAME]		= &dt_cg_subr_basename,
-	[DIF_SUBR_DIRNAME]		= NULL,
+	[DIF_SUBR_DIRNAME]		= &dt_cg_subr_dirname,
 	[DIF_SUBR_CLEANPATH]		= NULL,
 	[DIF_SUBR_STRCHR]		= &dt_cg_subr_strchr,
 	[DIF_SUBR_STRRCHR]		= &dt_cg_subr_strrchr,
diff --git a/libdtrace/dt_dlibs.c b/libdtrace/dt_dlibs.c
index 771eba64..58fa9597 100644
--- a/libdtrace/dt_dlibs.c
+++ b/libdtrace/dt_dlibs.c
@@ -55,6 +55,7 @@ static const dt_ident_t		dt_bpf_symbols[] = {
 	DT_BPF_SYMBOL(dt_agg_lqbin, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_agg_qbin, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_basename, DT_IDENT_SYMBOL),
+	DT_BPF_SYMBOL(dt_dirname, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_error, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_get_bvar, DT_IDENT_SYMBOL),
 	DT_BPF_SYMBOL(dt_get_string, DT_IDENT_SYMBOL),
diff --git a/test/unittest/dif/dirname.d b/test/unittest/dif/dirname.d
index ed8053b0..7919180a 100644
--- a/test/unittest/dif/dirname.d
+++ b/test/unittest/dif/dirname.d
@@ -1,4 +1,3 @@
-/* @@xfail: dtv2 */
 BEGIN
 {
 	exit(dirname("/foo/bar") == "/foo" ? 0 : 1);
diff --git a/test/unittest/funcs/tst.dirname0.d b/test/unittest/funcs/tst.dirname0.d
new file mode 100644
index 00000000..a3708d56
--- /dev/null
+++ b/test/unittest/funcs/tst.dirname0.d
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#pragma D option quiet
+
+BEGIN
+{
+	printf("|%s|\n", dirname("/foo/bar/baz"));
+	printf("|%s|\n", dirname("/foo/bar///baz/"));
+	printf("|%s|\n", dirname("/foo/bar/baz/"));
+	printf("|%s|\n", dirname("/foo/bar/baz//"));
+	printf("|%s|\n", dirname("/foo/bar/baz/."));
+	printf("|%s|\n", dirname("/foo/bar/baz/./"));
+	printf("|%s|\n", dirname("/foo/bar/baz/.//"));
+	printf("|%s|\n", dirname("foo/bar/baz/"));
+	printf("|%s|\n", dirname("/"));
+	printf("|%s|\n", dirname("./"));
+	printf("|%s|\n", dirname("//"));
+	printf("|%s|\n", dirname("/."));
+	printf("|%s|\n", dirname("/./"));
+	printf("|%s|\n", dirname("/./."));
+	printf("|%s|\n", dirname("/.//"));
+	printf("|%s|\n", dirname("."));
+	printf("|%s|\n", dirname("f"));
+	printf("|%s|\n", dirname("f/"));
+	printf("|%s|\n", dirname("/////"));
+	printf("|%s|\n", dirname(""));
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
+}
+
diff --git a/test/unittest/funcs/tst.dirname0.r b/test/unittest/funcs/tst.dirname0.r
new file mode 100644
index 00000000..c1e5f257
--- /dev/null
+++ b/test/unittest/funcs/tst.dirname0.r
@@ -0,0 +1,21 @@
+|/foo/bar|
+|/foo/bar|
+|/foo/bar|
+|/foo/bar|
+|/foo/bar/baz|
+|/foo/bar/baz|
+|/foo/bar/baz|
+|foo/bar|
+|/|
+|.|
+|/|
+|/|
+|/|
+|/.|
+|/|
+|.|
+|.|
+|.|
+|/|
+|.|
+
-- 
2.18.4




More information about the DTrace-devel mailing list