[DTrace-devel] [PATCH v4] Implement the pcap action

Kris Van Hees kris.van.hees at oracle.com
Wed Nov 8 16:46:56 UTC 2023


The currently implementation can only capture the linear portion of
the packet data.  Fortunately, this covers the vast majority of all
uses of pcap() anyway.

Future work is needed to be able to access non-linear data for large
packets.

Signed-off-by: Kris Van Hees <kris.van.hees at oracle.com>
---
 libdtrace/dt_cg.c                             | 160 +++++++++++++++---
 libdtrace/dt_consume.c                        |  53 +++---
 libdtrace/dt_open.c                           |   5 +
 libdtrace/dt_pcap.h                           |   4 +-
 libdtrace/pcap.d                              |   4 +-
 .../err.D_PROTO_ARG.pcap_non_scalar_arg1.d    |  27 +++
 .../err.D_PROTO_ARG.pcap_non_scalar_arg1.r    |   4 +
 .../err.D_PROTO_ARG.pcap_non_scalar_arg2.d    |  23 +++
 .../err.D_PROTO_ARG.pcap_non_scalar_arg2.r    |   4 +
 .../pcap/err.D_PROTO_LEN.pcap_missing_arg.d   |  23 +++
 .../pcap/err.D_PROTO_LEN.pcap_missing_arg.r   |   2 +
 .../pcap/err.D_PROTO_LEN.pcap_too_few_args.d  |  23 +++
 .../pcap/err.D_PROTO_LEN.pcap_too_few_args.r  |   2 +
 .../pcap/err.D_PROTO_LEN.pcap_too_many_args.d |  23 +++
 .../pcap/err.D_PROTO_LEN.pcap_too_many_args.r |   2 +
 test/unittest/pcap/tst.pcap.file.sh           |   3 +-
 test/unittest/pcap/tst.pcap.regleak.d         |  38 +++++
 .../pcap/tst.pcap.stdout-fork-error.sh        |   2 -
 test/unittest/pcap/tst.pcap.stdout.sh         |   2 -
 test/unittest/pcap/tst.pcap.tshark.sh         |   3 +-
 20 files changed, 346 insertions(+), 61 deletions(-)
 create mode 100644 test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg1.d
 create mode 100644 test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg1.r
 create mode 100644 test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg2.d
 create mode 100644 test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg2.r
 create mode 100644 test/unittest/pcap/err.D_PROTO_LEN.pcap_missing_arg.d
 create mode 100644 test/unittest/pcap/err.D_PROTO_LEN.pcap_missing_arg.r
 create mode 100644 test/unittest/pcap/err.D_PROTO_LEN.pcap_too_few_args.d
 create mode 100644 test/unittest/pcap/err.D_PROTO_LEN.pcap_too_few_args.r
 create mode 100644 test/unittest/pcap/err.D_PROTO_LEN.pcap_too_many_args.d
 create mode 100644 test/unittest/pcap/err.D_PROTO_LEN.pcap_too_many_args.r
 create mode 100644 test/unittest/pcap/tst.pcap.regleak.d

diff --git a/libdtrace/dt_cg.c b/libdtrace/dt_cg.c
index 86ee6908..489da823 100644
--- a/libdtrace/dt_cg.c
+++ b/libdtrace/dt_cg.c
@@ -1632,6 +1632,29 @@ dt_cg_clsflags(dt_pcb_t *pcb, dtrace_actkind_t kind, const dt_node_t *dnp)
 	*cfp |= DT_CLSFLAG_DATAREC;
 }
 
+/*
+ * Get offsetof(structname, membername) information from CTF.
+ * Optionally, also get member size.
+ */
+static int
+dt_cg_ctf_offsetof(const char *structname, const char *membername, size_t *sizep)
+{
+	ctf_file_t *cfp = yypcb->pcb_hdl->dt_shared_ctf;
+	ctf_id_t type;
+	ctf_membinfo_t ctm;
+
+	if (!cfp)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOCTF);
+	type = ctf_lookup_by_name(cfp, structname);
+	if (type == CTF_ERR)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOCTF);
+	if (ctf_member_info(cfp, type, membername, &ctm) == CTF_ERR)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOCTF);
+	if (sizep)
+		*sizep = ctf_type_size(cfp, ctm.ctm_type);
+	return (ctm.ctm_offset / NBBY);
+}
+
 static void
 dt_cg_act_breakpoint(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
 {
@@ -1882,8 +1905,118 @@ dt_cg_act_panic(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
 static void
 dt_cg_act_pcap(dt_pcb_t *pcb, dt_node_t *dnp, dtrace_actkind_t kind)
 {
-	dnerror(dnp, D_UNKNOWN, "pcap() is not implemented (yet)\n");
-	/* FIXME: Needs implementation */
+	dtrace_hdl_t	*dtp = pcb->pcb_hdl;
+	dt_regset_t	*drp = pcb->pcb_regs;
+	dt_irlist_t	*dlp = &pcb->pcb_ir;
+	dt_node_t	*addr = dnp->dn_args;
+	dt_node_t	*proto = addr->dn_list;
+	uint_t		time_off, size_off, data_off, off;
+	uint_t		lbl_lenok = dt_irlist_label(dlp);
+	uint64_t	pcapsz = dtp->dt_options[DTRACEOPT_PCAPSIZE];
+	int		lenreg;
+
+	TRACE_REGSET("pcap(): Begin ");
+
+	/*
+	 * Create records for timestamp, size, protocol id, and packet data.
+	 * The protocol id is stored immediately from the passed in argument.
+	 */
+	time_off = dt_rec_add(dtp, dt_cg_fill_gap, DTRACEACT_PCAP,
+			      sizeof(uint64_t), sizeof(uint64_t), NULL, 0);
+	size_off = dt_rec_add(dtp, dt_cg_fill_gap, DTRACEACT_PCAP,
+			      sizeof(uint32_t), sizeof(uint32_t), NULL, 0);
+	dt_cg_store_val(pcb, proto, DTRACEACT_PCAP, NULL, 0);
+	data_off = dt_rec_add(dtp, dt_cg_fill_gap, DTRACEACT_PCAP,
+			      pcapsz, 1, NULL, 0);
+
+	/* Store timestamp (ns since boot time). */
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emit(dlp,  BPF_CALL_HELPER(BPF_FUNC_ktime_get_ns));
+	dt_regset_free_args(drp);
+	emit(dlp,  BPF_STORE(BPF_DW, BPF_REG_9, time_off, BPF_REG_0));
+	dt_regset_free(drp, BPF_REG_0);
+
+	/*
+	 * Determine and store the packet data size.
+	 * The size of the data to be copied (and reported as size of the
+	 * packet capture) is the lesser of the linear data size
+	 * (skb->len - skb->data_len) and the capture size.
+	 */
+	dt_cg_node(addr, dlp, drp);
+
+	off = dt_cg_ctf_offsetof("struct sk_buff", "len", NULL);
+
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+
+	emit(dlp,  BPF_MOV_REG(BPF_REG_3, addr->dn_reg));
+	emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, off));
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_2, sizeof(uint32_t)));
+	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_SP));
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emit(dlp,  BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel]));
+	dt_regset_free_args(drp);
+	dt_regset_free(drp, BPF_REG_0);
+
+	if ((lenreg = dt_regset_alloc(drp)) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+
+	emit(dlp,  BPF_LOAD(BPF_DW, lenreg, BPF_REG_FP, DT_STK_SP));
+	emit(dlp,  BPF_LOAD(BPF_W, lenreg, lenreg, 0));
+
+	off = dt_cg_ctf_offsetof("struct sk_buff", "data_len", NULL);
+
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+
+	emit(dlp,  BPF_MOV_REG(BPF_REG_3, addr->dn_reg));
+	emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, off));
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_2, sizeof(uint32_t)));
+	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_SP));
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emit(dlp,  BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel]));
+	dt_regset_free_args(drp);
+	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_0, BPF_REG_FP, DT_STK_SP));
+	emit(dlp,  BPF_LOAD(BPF_W, BPF_REG_0, BPF_REG_0, 0));
+
+	emit(dlp,  BPF_ALU64_REG(BPF_SUB, lenreg, BPF_REG_0));
+	dt_regset_free(drp, BPF_REG_0);
+	emit(dlp,  BPF_BRANCH_IMM(BPF_JLE, lenreg, pcapsz, lbl_lenok));
+	emit(dlp,  BPF_MOV_IMM(lenreg, pcapsz));
+	emitl(dlp, lbl_lenok,
+		   BPF_STORE(BPF_W, BPF_REG_9, size_off, lenreg));
+
+	/* Copy the packet data to the output buffer. */
+	off = dt_cg_ctf_offsetof("struct sk_buff", "data", NULL);
+
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+	emit(dlp,  BPF_MOV_REG(BPF_REG_3, addr->dn_reg));
+	dt_regset_free(drp, addr->dn_reg);
+	emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, off));
+	emit(dlp,  BPF_MOV_IMM(BPF_REG_2, sizeof(uint64_t)));
+	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_1, BPF_REG_FP, DT_STK_SP));
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emit(dlp,  BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel]));
+	dt_regset_free_args(drp);
+	dt_regset_free(drp, BPF_REG_0);
+
+	if (dt_regset_xalloc_args(drp) == -1)
+		longjmp(yypcb->pcb_jmpbuf, EDT_NOREG);
+	emit(dlp,  BPF_MOV_REG(BPF_REG_2, lenreg));
+	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_FP, DT_STK_SP));
+	emit(dlp,  BPF_LOAD(BPF_DW, BPF_REG_3, BPF_REG_3, 0));
+	dt_regset_free(drp, lenreg);
+	emit(dlp,  BPF_MOV_REG(BPF_REG_1, BPF_REG_9));
+	emit(dlp,  BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, data_off));
+	dt_regset_xalloc(drp, BPF_REG_0);
+	emit(dlp,  BPF_CALL_HELPER(dtp->dt_bpfhelper[BPF_FUNC_probe_read_kernel]));
+	dt_regset_free_args(drp);
+	dt_regset_free(drp, BPF_REG_0);
+
+	TRACE_REGSET("pcap(): End   ");
 }
 
 static void
@@ -4276,29 +4409,6 @@ dt_cg_asgn_op(dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 	}
 }
 
-/*
- * Get offsetof(structname, membername) information from CTF.
- * Optionally, also get member size.
- */
-static int
-dt_cg_ctf_offsetof(const char *structname, const char *membername, size_t *sizep)
-{
-	ctf_file_t *cfp = yypcb->pcb_hdl->dt_shared_ctf;
-	ctf_id_t type;
-	ctf_membinfo_t ctm;
-
-	if (!cfp)
-		longjmp(yypcb->pcb_jmpbuf, EDT_NOCTF);
-	type = ctf_lookup_by_name(cfp, structname);
-	if (type == CTF_ERR)
-		longjmp(yypcb->pcb_jmpbuf, EDT_NOCTF);
-	if (ctf_member_info(cfp, type, membername, &ctm) == CTF_ERR)
-		longjmp(yypcb->pcb_jmpbuf, EDT_NOCTF);
-	if (sizep)
-		*sizep = ctf_type_size(cfp, ctm.ctm_type);
-	return (ctm.ctm_offset / NBBY);
-}
-
 static void
 dt_cg_uregs(unsigned int idx, dt_node_t *dnp, dt_irlist_t *dlp, dt_regset_t *drp)
 {
diff --git a/libdtrace/dt_consume.c b/libdtrace/dt_consume.c
index f5dfc373..7e2b1005 100644
--- a/libdtrace/dt_consume.c
+++ b/libdtrace/dt_consume.c
@@ -1427,28 +1427,26 @@ dt_print_mod(dtrace_hdl_t *dtp, FILE *fp, const char *format, caddr_t addr)
 }
 
 int
-dt_print_pcap(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec,
+dt_print_pcap(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec, uint_t nrecs,
 	      const caddr_t buf)
 {
-	caddr_t	addr;
-	uint64_t time, proto, pktlen, maxlen;
-	const char *filename;
+	caddr_t			data;
+	uint64_t		time, proto, pktlen;
+	uint64_t		maxlen = dtp->dt_options[DTRACEOPT_PCAPSIZE];
+	const char		*filename;
 
-	addr = (caddr_t)buf + rec->dtrd_offset;
+	if (nrecs < 4)
+		return dt_set_errno(dtp, EDT_PCAP);
 
 	if (dt_read_scalar(buf, rec, &time) < 0 ||
-	    dt_read_scalar(buf + sizeof(uint64_t), rec, &pktlen) < 0)
+	    dt_read_scalar(buf, rec + 1, &pktlen) < 0)
 		return dt_set_errno(dtp, EDT_PCAP);
 
-	if (pktlen == 0) {
-		/*
-		 * skb must have been NULL, skip without capturing.
-		 */
+	/* If pktlen is 0, the skb must have been NULL, so don't capture it. */
+	if (pktlen == 0)
 		return 0;
-	}
-	maxlen = DT_PCAPSIZE(dtp->dt_options[DTRACEOPT_PCAPSIZE]);
 
-	if (dt_read_scalar(buf, rec + 1, &proto) < 0)
+	if (dt_read_scalar(buf, rec + 2, &proto) < 0)
 		return dt_set_errno(dtp, EDT_PCAP);
 
 	/*
@@ -1456,17 +1454,16 @@ dt_print_pcap(dtrace_hdl_t *dtp, FILE *fp, dtrace_recdesc_t *rec,
 	 * specified or if we have been able to connect a pipe to tshark,
 	 * otherwise print tracemem-like output.
 	 */
+	data = buf + (rec + 3)->dtrd_offset;
 	filename = dt_pcap_filename(dtp, fp);
-	if (filename != NULL) {
-		dt_pcap_dump(dtp, filename, proto, time,
-			     addr + (2 * sizeof(uint64_t)), (uint32_t)pktlen,
-			     (uint32_t)maxlen);
-	} else {
-		if (dt_print_rawbytes(dtp, fp, addr + (2 * sizeof(uint64_t)),
-		    pktlen > maxlen ? maxlen : pktlen) < 0)
-			return -1;
-	}
-	return 0;
+	if (filename != NULL)
+		dt_pcap_dump(dtp, filename, proto, time, data,
+			     (uint32_t)pktlen, (uint32_t)maxlen);
+	else if (dt_print_rawbytes(dtp, fp, data,
+				   pktlen > maxlen ? maxlen : pktlen) < 0)
+		return -1;
+
+	return 4;
 }
 
 /*
@@ -2508,6 +2505,16 @@ dt_consume_one_probe(dtrace_hdl_t *dtp, FILE *fp, char *data, uint32_t size,
 			i += n - 1;
 			continue;
 		}
+		case DTRACEACT_PCAP: {
+			int	nrecs;
+
+			nrecs = epd->dtdd_nrecs - i;
+			n = dt_print_pcap(dtp, fp, rec, nrecs, data);
+			if (n < 0)
+				return -1;
+			i += n - 1;
+			continue;
+		}
 		case DTRACEACT_PRINTF:
 			func = dtrace_fprintf;
 			break;
diff --git a/libdtrace/dt_open.c b/libdtrace/dt_open.c
index dd70df24..b521d887 100644
--- a/libdtrace/dt_open.c
+++ b/libdtrace/dt_open.c
@@ -799,6 +799,11 @@ dt_vopen(int version, int flags, int *errp,
 		return set_open_errno(dtp, errp, EDT_READMAXSTACK);
 	fclose(fd);
 
+	/*
+	 * Set the default pcap capture size.
+	 */
+	dtp->dt_options[DTRACEOPT_PCAPSIZE] = DT_PCAP_DEF_PKTSIZE;
+
 	/*
 	 * Set the default data rates.
 	 */
diff --git a/libdtrace/dt_pcap.h b/libdtrace/dt_pcap.h
index bf14d013..f4d08591 100644
--- a/libdtrace/dt_pcap.h
+++ b/libdtrace/dt_pcap.h
@@ -1,6 +1,6 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2023, 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.
  */
@@ -20,8 +20,6 @@ extern "C" {
 struct dtrace_hdl;
 
 #define	DT_PCAP_DEF_PKTSIZE	1514
-#define	DT_PCAPSIZE(sz) \
-	((sz > 0 && sz < 65566) ? sz : DT_PCAP_DEF_PKTSIZE)
 
 typedef struct dt_global_pcap {
 	dt_list_t dt_pcaps;	/* pcap file info */
diff --git a/libdtrace/pcap.d b/libdtrace/pcap.d
index 5d0dc90d..21a8e74a 100644
--- a/libdtrace/pcap.d
+++ b/libdtrace/pcap.d
@@ -1,12 +1,12 @@
 /*
  * Oracle Linux DTrace.
- * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2023, 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 depends_on module vmlinux
-#pragma D depends_on provider tcp
+#pragma D depends_on provider ip
 
 /*
  * In general, PCAP_<type> names are chosen to match DL_<type> DLPI
diff --git a/test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg1.d b/test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg1.d
new file mode 100644
index 00000000..ecd6cf69
--- /dev/null
+++ b/test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg1.d
@@ -0,0 +1,27 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2023, 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.
+ */
+
+/*
+ * ASSERTION: The first argument to pcap() should be a pointer.
+ *
+ * SECTION: Actions and Subroutines/pcap()
+ */
+
+struct {
+	int	a;
+} arg;
+
+BEGIN
+{
+	pcap(arg, 0);
+	exit(0);
+}
+
+ERROR
+{
+	exit(0);
+}
diff --git a/test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg1.r b/test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg1.r
new file mode 100644
index 00000000..0754e27d
--- /dev/null
+++ b/test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg1.r
@@ -0,0 +1,4 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg1.d: [D_PROTO_ARG] line 20: pcap( ) argument #1 is incompatible with prototype:
+	prototype: void *
+	 argument: struct 
diff --git a/test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg2.d b/test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg2.d
new file mode 100644
index 00000000..fed3c4a9
--- /dev/null
+++ b/test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg2.d
@@ -0,0 +1,23 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2023, 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.
+ */
+
+/*
+ * ASSERTION: The second argument to pcap() should be a scalar.
+ *
+ * SECTION: Actions and Subroutines/pcap()
+ */
+
+BEGIN
+{
+	pcap(0, "a");
+	exit(0);
+}
+
+ERROR
+{
+	exit(0);
+}
diff --git a/test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg2.r b/test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg2.r
new file mode 100644
index 00000000..2c56503e
--- /dev/null
+++ b/test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg2.r
@@ -0,0 +1,4 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/pcap/err.D_PROTO_ARG.pcap_non_scalar_arg2.d: [D_PROTO_ARG] line 16: pcap( ) argument #2 is incompatible with prototype:
+	prototype: int
+	 argument: string
diff --git a/test/unittest/pcap/err.D_PROTO_LEN.pcap_missing_arg.d b/test/unittest/pcap/err.D_PROTO_LEN.pcap_missing_arg.d
new file mode 100644
index 00000000..3ab818a3
--- /dev/null
+++ b/test/unittest/pcap/err.D_PROTO_LEN.pcap_missing_arg.d
@@ -0,0 +1,23 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2023, 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.
+ */
+
+/*
+ * ASSERTION: pcap() arguments
+ *
+ * SECTION: Actions and Subroutines/pcap()
+ */
+
+BEGIN
+{
+	pcap();
+	exit(0);
+}
+
+ERROR
+{
+	exit(0);
+}
diff --git a/test/unittest/pcap/err.D_PROTO_LEN.pcap_missing_arg.r b/test/unittest/pcap/err.D_PROTO_LEN.pcap_missing_arg.r
new file mode 100644
index 00000000..a51daa61
--- /dev/null
+++ b/test/unittest/pcap/err.D_PROTO_LEN.pcap_missing_arg.r
@@ -0,0 +1,2 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/pcap/err.D_PROTO_LEN.pcap_missing_arg.d: [D_PROTO_LEN] line 16: pcap( ) prototype mismatch: 0 args passed, 2 expected
diff --git a/test/unittest/pcap/err.D_PROTO_LEN.pcap_too_few_args.d b/test/unittest/pcap/err.D_PROTO_LEN.pcap_too_few_args.d
new file mode 100644
index 00000000..4f4f2eaf
--- /dev/null
+++ b/test/unittest/pcap/err.D_PROTO_LEN.pcap_too_few_args.d
@@ -0,0 +1,23 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2023, 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.
+ */
+
+/*
+ * ASSERTION: The pcap() subroutine requires 2 arguments.
+ *
+ * SECTION: Actions and Subroutines/pcap()
+ */
+
+BEGIN
+{
+	pcap(0);
+	exit(0);
+}
+
+ERROR
+{
+	exit(0);
+}
diff --git a/test/unittest/pcap/err.D_PROTO_LEN.pcap_too_few_args.r b/test/unittest/pcap/err.D_PROTO_LEN.pcap_too_few_args.r
new file mode 100644
index 00000000..2951a6de
--- /dev/null
+++ b/test/unittest/pcap/err.D_PROTO_LEN.pcap_too_few_args.r
@@ -0,0 +1,2 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/pcap/err.D_PROTO_LEN.pcap_too_few_args.d: [D_PROTO_LEN] line 16: pcap( ) prototype mismatch: 1 arg passed, 2 expected
diff --git a/test/unittest/pcap/err.D_PROTO_LEN.pcap_too_many_args.d b/test/unittest/pcap/err.D_PROTO_LEN.pcap_too_many_args.d
new file mode 100644
index 00000000..e744df36
--- /dev/null
+++ b/test/unittest/pcap/err.D_PROTO_LEN.pcap_too_many_args.d
@@ -0,0 +1,23 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2023, 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.
+ */
+
+/*
+ * ASSERTION: The pcap() subroutine accepts no more than two arguments.
+ *
+ * SECTION: Actions and Subroutines/pcap()
+ */
+
+BEGIN
+{
+	pcap(1, 2, 3);
+	exit(0);
+}
+
+ERROR
+{
+	exit(0);
+}
diff --git a/test/unittest/pcap/err.D_PROTO_LEN.pcap_too_many_args.r b/test/unittest/pcap/err.D_PROTO_LEN.pcap_too_many_args.r
new file mode 100644
index 00000000..7960c808
--- /dev/null
+++ b/test/unittest/pcap/err.D_PROTO_LEN.pcap_too_many_args.r
@@ -0,0 +1,2 @@
+-- @@stderr --
+dtrace: failed to compile script test/unittest/pcap/err.D_PROTO_LEN.pcap_too_many_args.d: [D_PROTO_LEN] line 16: pcap( ) prototype mismatch: 3 args passed, 2 expected
diff --git a/test/unittest/pcap/tst.pcap.file.sh b/test/unittest/pcap/tst.pcap.file.sh
index f3be6541..2985514a 100755
--- a/test/unittest/pcap/tst.pcap.file.sh
+++ b/test/unittest/pcap/tst.pcap.file.sh
@@ -1,10 +1,9 @@
 #!/bin/bash
 #
 # Oracle Linux DTrace.
-# Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, 2023, 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.
-# @@xfail: dtv2
 
 #
 # Ensure pcap() action directed to file succeeds, and verify the content
diff --git a/test/unittest/pcap/tst.pcap.regleak.d b/test/unittest/pcap/tst.pcap.regleak.d
new file mode 100644
index 00000000..3eb3cbc7
--- /dev/null
+++ b/test/unittest/pcap/tst.pcap.regleak.d
@@ -0,0 +1,38 @@
+/*
+ * Oracle Linux DTrace.
+ * Copyright (c) 2023, 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.
+ */
+
+/*
+ * ASSERTION: pcap() does not leak registers
+ *
+ * SECTION: Actions and Subroutines/pcap()
+ */
+
+/* @@runtest-opts: -e */
+
+BEGIN
+{
+	/*
+	 * If pcap() leaks even a single register, ten invocations will most
+	 * certainly result in a compiler error (due to register starvation).
+	 */
+	pcap(0, PCAP_IP);
+	pcap(0, PCAP_IP);
+	pcap(0, PCAP_IP);
+	pcap(0, PCAP_IP);
+	pcap(0, PCAP_IP);
+	pcap(0, PCAP_IP);
+	pcap(0, PCAP_IP);
+	pcap(0, PCAP_IP);
+	pcap(0, PCAP_IP);
+	pcap(0, PCAP_IP);
+	exit(0);
+}
+
+ERROR
+{
+	exit(1);
+}
diff --git a/test/unittest/pcap/tst.pcap.stdout-fork-error.sh b/test/unittest/pcap/tst.pcap.stdout-fork-error.sh
index 5681936a..9e7b92de 100755
--- a/test/unittest/pcap/tst.pcap.stdout-fork-error.sh
+++ b/test/unittest/pcap/tst.pcap.stdout-fork-error.sh
@@ -12,8 +12,6 @@
 # skb->data.
 #
 
-# @@xfail: need ip provider
-
 if (( $# != 1 )); then
 	echo "expected one argument: <dtrace-path>" >&2
 	exit 2
diff --git a/test/unittest/pcap/tst.pcap.stdout.sh b/test/unittest/pcap/tst.pcap.stdout.sh
index bb0dc242..6ad322dc 100755
--- a/test/unittest/pcap/tst.pcap.stdout.sh
+++ b/test/unittest/pcap/tst.pcap.stdout.sh
@@ -11,8 +11,6 @@
 # skb->data.
 #
 
-# @@xfail: need ip provider
-
 if (( $# != 1 )); then
 	echo "expected one argument: <dtrace-path>" >&2
 	exit 2
diff --git a/test/unittest/pcap/tst.pcap.tshark.sh b/test/unittest/pcap/tst.pcap.tshark.sh
index 1d8ebe5b..f0baf071 100755
--- a/test/unittest/pcap/tst.pcap.tshark.sh
+++ b/test/unittest/pcap/tst.pcap.tshark.sh
@@ -1,10 +1,9 @@
 #!/bin/bash
 #
 # Oracle Linux DTrace.
-# Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2018, 2023, 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.
-# @@xfail: dtv2
 
 #
 # Ensure pcap() action in absence of freopen() results in tshark output
-- 
2.40.1




More information about the DTrace-devel mailing list