[DTrace-devel] [PATCH 3/3] test/unittest: Add basic python bindings tests
Alan Maguire
alan.maguire at oracle.com
Fri May 15 13:44:15 UTC 2026
Add tests to read an aggregation from a python-invoked DTrace
session, grab a process and trace it, and create a process
and trace it.
Signed-off-by: Alan Maguire <alan.maguire at oracle.com>
---
test/unittest/python/tst.aggr.sh | 59 ++++++++++++++++++
test/unittest/python/tst.proc-create.sh | 56 +++++++++++++++++
test/unittest/python/tst.proc-grab.sh | 81 +++++++++++++++++++++++++
3 files changed, 196 insertions(+)
create mode 100755 test/unittest/python/tst.aggr.sh
create mode 100755 test/unittest/python/tst.proc-create.sh
create mode 100755 test/unittest/python/tst.proc-grab.sh
diff --git a/test/unittest/python/tst.aggr.sh b/test/unittest/python/tst.aggr.sh
new file mode 100755
index 00000000..9c2daa67
--- /dev/null
+++ b/test/unittest/python/tst.aggr.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2026, 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.
+#
+# Ensure aggregation can be retrieved from simple D script.
+
+if [ $# -ne 1 ]; then
+ echo "usage: $0 <dtrace>" >&2
+ exit 2
+fi
+
+dtrace=$1
+
+tmpdir=$(mktemp -d)
+cleanup() {
+ rm -rf "$tmpdir"
+}
+trap cleanup EXIT
+
+cat >"$tmpdir"/script.d <<EOF
+#!/usr/bin/env python3
+from pathlib import Path
+from subprocess import Popen
+
+from dtrace import DTraceSession
+
+prog = r"""
+syscall:::entry
+{
+ @counts[execname, probefunc] = count();
+}
+"""
+
+with DTraceSession() as dt:
+ p = dt.compile(prog)
+ dt.enable(p)
+ dt.go()
+ pid = Popen(["/bin/sleep", "1"]).wait()
+ dt.stop()
+ dt.agg_snap()
+ rows = dt.agg_walk()
+
+if not rows:
+ raise SystemExit("no rows returned")
+
+entry = rows[0]
+if "keys" not in entry or "value" not in entry:
+ raise SystemExit("missing keys or value")
+
+print("keys=", str(entry["keys"]), "value=", str(entry["value"]))
+EOF
+
+chmod +x "$tmpdir/script.d"
+PATH="$tmpdir:$PATH"
+
+$tmpdir/script.d
diff --git a/test/unittest/python/tst.proc-create.sh b/test/unittest/python/tst.proc-create.sh
new file mode 100755
index 00000000..e9d2d466
--- /dev/null
+++ b/test/unittest/python/tst.proc-create.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2026, 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.
+#
+# Validate tracing of a spawned command.
+
+if [ $# -ne 1 ]; then
+ echo "usage: $0 <dtrace>" >&2
+ exit 2
+fi
+
+dtrace=$1
+
+tmpdir=$(mktemp -d)
+cleanup() {
+ rm -rf "$tmpdir"
+}
+trap cleanup EXIT
+
+cat >"$tmpdir"/script.py <<'EOF'
+#!/usr/bin/env python3
+from subprocess import Popen
+from dtrace import DTraceSession
+
+prog = r"""
+syscall::read:entry
+{
+ @calls[execname] = count();
+}
+"""
+
+with DTraceSession() as dt:
+ p = dt.compile(prog)
+ dt.enable(p)
+ dt.go()
+ proc = dt.proc_create(["/bin/echo", "hello"])
+ dt.proc_continue(proc)
+ dt.proc_release(proc)
+ dt.stop()
+ dt.agg_snap()
+ rows = dt.agg_walk()
+
+if not rows:
+ raise SystemExit("no aggregation rows")
+
+names = {tuple(entry["keys"]) for entry in rows}
+if not any("echo" in key for key in names):
+ raise SystemExit("expected echo entry in aggregation")
+EOF
+
+chmod +x "$tmpdir/script.py"
+
+$tmpdir/script.py
diff --git a/test/unittest/python/tst.proc-grab.sh b/test/unittest/python/tst.proc-grab.sh
new file mode 100755
index 00000000..21faca65
--- /dev/null
+++ b/test/unittest/python/tst.proc-grab.sh
@@ -0,0 +1,81 @@
+#!/bin/bash
+#
+# Oracle Linux DTrace.
+# Copyright (c) 2026, 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.
+#
+# Ensure Python bindings can grab an existing process and trace its syscalls.
+
+if [ $# -ne 1 ]; then
+ echo "usage: $0 <dtrace>" >&2
+ exit 2
+fi
+
+dtrace=$1
+
+tmpdir=$(mktemp -d)
+cleanup() {
+ rm -rf "$tmpdir"
+}
+trap cleanup EXIT
+
+# Start a long-lived helper shell to grab.
+sleep_inf="$tmpdir/sleep.sh"
+cat >"$sleep_inf" <<'EOF'
+#!/bin/bash
+while :; do
+ sleep 1
+done
+EOF
+chmod +x "$sleep_inf"
+"$sleep_inf" &
+helper=$!
+
+cat >"$tmpdir"/script.py <<'EOF'
+#!/usr/bin/env python3
+import os
+import sys
+from subprocess import Popen
+from dtrace import DTraceSession
+
+prog = r"""
+syscall:::entry
+/ pid == $target /
+{
+ @counts[execname, probefunc] = count();
+}
+"""
+
+pid = int(os.environ["TARGET_PID"])
+
+with DTraceSession() as dt:
+ proc = dt.proc_grab_pid(pid)
+ if proc.getpid() != pid:
+ raise SystemExit("grabbed PID mismatch")
+ p = dt.compile(prog)
+ dt.enable(p)
+ dt.go()
+ dt.proc_continue(proc)
+ Popen(["/bin/sleep", "5"]).wait()
+ dt.stop()
+ dt.proc_release(proc)
+ dt.agg_snap()
+ rows = dt.agg_walk()
+
+if not rows:
+ raise SystemExit("no rows returned")
+
+entry = rows[0]
+if "keys" not in entry or "value" not in entry:
+ raise SystemExit("missing keys or value")
+EOF
+
+chmod +x "$tmpdir/script.py"
+export TARGET_PID=$helper
+$tmpdir/script.py
+ret=$?
+
+kill $helper
+wait $helper 2>/dev/null || true
+exit $ret
--
2.43.5
More information about the DTrace-devel
mailing list