* testsuite/systemtap.clone/Makefile.clone: New file.
* testsuite/systemtap.clone/Makefile.fork_exec: Ditto.
* testsuite/systemtap.clone/Makefile.vfork_exec: Ditto.
* testsuite/systemtap.clone/dtrace_child.c: Ditto.
* testsuite/systemtap.clone/dtrace_child_probes.d: Ditto.
* testsuite/systemtap.clone/dtrace_clone.c: Ditto.
* testsuite/systemtap.clone/dtrace_clone.exp: Ditto.
* testsuite/systemtap.clone/dtrace_clone.stp: Ditto.
* testsuite/systemtap.clone/dtrace_clone_probes.d: Ditto.
* testsuite/systemtap.clone/dtrace_fork_exec.exp: Ditto.
* testsuite/systemtap.clone/dtrace_fork_exec.stp: Ditto.
* testsuite/systemtap.clone/dtrace_fork_parent.c: Ditto.
* testsuite/systemtap.clone/dtrace_fork_parent_probes.d: Ditto.
* testsuite/systemtap.clone/dtrace_vfork_exec.exp: Ditto.
* testsuite/systemtap.clone/dtrace_vfork_exec.stp: Ditto.
* testsuite/systemtap.clone/dtrace_vfork_parent.c: Ditto.
* testsuite/systemtap.clone/dtrace_vfork_parent_probes.d: Ditto.
* testsuite/systemtap.clone/test_progs.tcl: Ditto.
--- /dev/null
+CFLAGS += -g -O2
+
+DTRACE := dtrace
+
+OBJS := dtrace_clone.o dtrace_clone_probes.o
+
+TARGETS := dtrace_clone
+
+BUILT_SOURCES := dtrace_clone_probes.h
+
+all: $(TARGETS)
+
+dtrace_clone: $(OBJS)
+
+dtrace_clone.c: dtrace_clone_probes.h
+
+%.h: %.d
+ $(DTRACE) -C -h -s $< -o $@
+
+%.o: %.d
+ $(DTRACE) -C -G -s $< -o $@
+
+clean:
+ rm -f $(OBJS) $(TARGETS) $(BUILT_SOURCES)
--- /dev/null
+PWD := $(shell pwd)
+
+CFLAGS += -g -O2 -DTEST_DIR="$(PWD)"
+
+DTRACE := dtrace
+
+PARENT_OBJS := dtrace_fork_parent.o dtrace_fork_parent_probes.o
+
+CHILD_OBJS := dtrace_child.o dtrace_child_probes.o
+
+TARGETS := dtrace_fork_parent dtrace_child
+
+BUILT_SOURCES := dtrace_fork_parent_probes.h dtrace_child_probes.h
+
+all: $(TARGETS)
+
+dtrace_fork_parent: $(PARENT_OBJS)
+
+dtrace_fork_parent.c: dtrace_fork_parent_probes.h
+
+dtrace_child: $(CHILD_OBJS)
+
+dtrace_child.c: dtrace_child_probes.h
+
+%.h: %.d
+ $(DTRACE) -C -h -s $< -o $@
+
+%.o: %.d
+ $(DTRACE) -C -G -s $< -o $@
+
+clean:
+ rm -f $(PARENT_OBJS) $(CHILD_OBJS) $(TARGETS) $(BUILT_SOURCES)
--- /dev/null
+PWD := $(shell pwd)
+
+CFLAGS += -g -O2 -DTEST_DIR="$(PWD)"
+
+DTRACE := dtrace
+
+PARENT_OBJS := dtrace_vfork_parent.o dtrace_vfork_parent_probes.o
+
+CHILD_OBJS := dtrace_child.o dtrace_child_probes.o
+
+TARGETS := dtrace_vfork_parent dtrace_child
+
+BUILT_SOURCES := dtrace_vfork_parent_probes.h dtrace_child_probes.h
+
+all: $(TARGETS)
+
+dtrace_vfork_parent: $(PARENT_OBJS)
+
+dtrace_vfork_parent.c: dtrace_vfork_parent_probes.h
+
+dtrace_child: $(CHILD_OBJS)
+
+dtrace_child.c: dtrace_child_probes.h
+
+%.h: %.d
+ $(DTRACE) -C -h -s $< -o $@
+
+%.o: %.d
+ $(DTRACE) -C -G -s $< -o $@
+
+clean:
+ rm -f $(PARENT_OBJS) $(CHILD_OBJS) $(TARGETS) $(BUILT_SOURCES)
--- /dev/null
+/* Systemtap test case
+ * Copyright (C) 2010, Red Hat Inc.
+ *
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "dtrace_child_probes.h"
+
+int
+main(int argc, char **argv)
+{
+ if (CHILD_MAIN_ENABLED()) {
+ CHILD_MAIN(getpid());
+ }
+ return 0;
+}
--- /dev/null
+provider child {
+ probe main(pid_t pid);
+};
--- /dev/null
+/* Systemtap test case
+ * Copyright (C) 2010, Red Hat Inc.
+ *
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sched.h>
+#include <errno.h>
+#include "dtrace_clone_probes.h"
+
+#if !defined(CLONE_NEWPID)
+#define CLONE_NEWPID 0
+#endif
+
+#define CLONE_FLAGS (CLONE_NEWPID|CLONE_FILES|CLONE_FS|CLONE_VM|SIGCHLD)
+
+void *stack_base2 = NULL;
+void *stack_top2 = NULL;
+
+static int uprobes_ns_child2(void *argv __attribute__((__unused__)))
+{
+ if (TEST_CHILD2_ENABLED()) {
+ TEST_CHILD2(getpid());
+ }
+ _exit(0);
+}
+
+static int uprobes_ns_child1(void *argv __attribute__((__unused__)))
+{
+ int pid;
+ int rc = 0;
+
+ /* Create the second clone. */
+ if (TEST_CHILD1_ENABLED()) {
+ TEST_CHILD1(getpid());
+ }
+
+ pid = clone(uprobes_ns_child2, stack_top2, CLONE_FLAGS, NULL);
+ if (TEST_CHILD2_PID_ENABLED()) {
+ TEST_CHILD2_PID(pid);
+ }
+ if (pid < 0) {
+ /* Error. */
+ char *msg2 = "clone 2 failed\n";
+ write(2, msg2, strlen(msg2));
+ rc = errno;
+ }
+ else {
+ int status;
+
+ waitpid(pid, &status, 0);
+ rc = WEXITSTATUS(status);
+ }
+ _exit(rc);
+}
+
+int
+main(int argc, char **argv)
+{
+ long pagesize = sysconf(_SC_PAGESIZE);
+ void *stack_base1, *stack_top1;
+ int pid;
+ int rc = 0;
+
+ /* Allocate both stacks here. */
+ stack_base1 = calloc(pagesize * 4, 1);
+ if (stack_base1 == NULL) {
+ perror("calloc 1 failed:");
+ return -1;
+ }
+ stack_base2 = calloc(pagesize * 4, 1);
+ if (stack_base2 == NULL) {
+ perror("calloc 2 failed:");
+ return -1;
+ }
+
+ /*
+ * Get top of stacks. According to the clone() manpage:
+ * Stacks grow downwards on all processors that run Linux
+ * (except the HP PA processors), so child_stack usually points
+ * to the topmost address of the memory space set up for the
+ * child stack.
+ */
+ stack_top1 = stack_base1 + (pagesize * 4);
+ stack_top2 = stack_base2 + (pagesize * 4);
+
+ /* Create the first clone (which will create the 2nd). */
+ if (TEST_MAIN_ENABLED()) {
+ TEST_MAIN(getpid());
+ }
+ pid = clone(uprobes_ns_child1, stack_top1, CLONE_FLAGS, NULL);
+ if (TEST_CHILD1_PID_ENABLED()) {
+ TEST_CHILD1_PID(pid);
+ }
+ if (pid < 0) {
+ /* error */
+ perror("clone 1 failed:");
+ rc = -1;
+ }
+ else {
+ int status;
+
+ /* Wait on the first clone to finish. */
+ waitpid(pid, &status, 0);
+ rc = WEXITSTATUS(status);
+ }
+ /* Cleanup */
+ free(stack_base1);
+ free(stack_base2);
+ if (TEST_MAIN2_ENABLED()) {
+ TEST_MAIN2();
+ }
+
+ return rc;
+}
--- /dev/null
+set TEST_NAME "dtrace_clone"
+set build_dir ""
+set test_progs {"dtrace_clone"}
+
+if {![installtest_p]} { untested $TEST_NAME; return }
+if {![uprobes_p]} { untested $TEST_NAME; return }
+
+source $srcdir/$subdir/test_progs.tcl
+
+# Build everything
+set TEST_NAME "dtrace_clone1"
+if {[build_test_progs "Makefile.clone" $test_progs] == 0} {
+ fail "$TEST_NAME - build failure"
+ cleanup_test_progs
+ return
+} else {
+ pass "$TEST_NAME - build success"
+}
+
+# We have to be root to use CLONE_NEWPID.
+proc run_test_prog {} {
+ global build_dir
+ as_root $build_dir/dtrace_clone
+ return 0
+}
+
+# Run the test.
+#
+# Here's an explanation of the output. On kernels that support
+# CLONE_NEWPID (new pid namespace), we'll see the following output:
+# main - pid: XXX
+# main - child pid: YYY
+# child1 - pid: 1
+# child1 - child2 pid: 2
+# child2 - pid: 1
+# main - finished
+# On kernels with CLONE_NEWPID, the pids assigned to the children are
+# fixed since they exist in their own pid namespace. Because not all
+# kernels support CLONE_NEWPID, we have to accept all pids.
+set TEST_NAME "dtrace_clone2"
+set output_string "main - pid: \[0-9\]+\r\nmain - child pid: \[0-9\]+\r\nchild1 - pid: \[0-9\]+\r\nchild1 - child2 pid: \[0-9\]+\r\nchild2 - pid: \[0-9\]+\r\nmain - finished\r\n"
+stap_run $TEST_NAME run_test_prog $output_string \
+ $srcdir/$subdir/dtrace_clone.stp $build_dir/dtrace_clone
+
+# Cleanup
+cleanup_test_progs
--- /dev/null
+global output_string
+
+# Original (parent) probes
+probe process(@1).mark("main")
+{
+ output_string .= sprintf("main - pid: %d\n", $arg1)
+}
+probe process(@1).mark("child1_pid")
+{
+ output_string .= sprintf("main - child pid: %d\n", $arg1)
+}
+probe process(@1).mark("main2")
+{
+ output_string .= sprintf("main - finished\n")
+ exit()
+}
+
+# Child1 probes
+probe process(@1).mark("child1")
+{
+ output_string .= sprintf("child1 - pid: %d\n", $arg1)
+}
+probe process(@1).mark("child2_pid")
+{
+ output_string .= sprintf("child1 - child2 pid: %d\n", $arg1)
+}
+
+# Child2 probes
+probe process(@1).mark("child2")
+{
+ output_string .= sprintf("child2 - pid: %d\n", $arg1)
+}
+
+# Testsuite glue
+probe begin
+{
+ printf("systemtap starting probe\n");
+}
+
+probe end
+{
+ printf("systemtap ending probe\n");
+ printf("%s", output_string);
+}
--- /dev/null
+provider test {
+ probe main(pid_t pid);
+ probe main2();
+ probe child1(pid_t pid);
+ probe child1_pid(pid_t pid);
+ probe child2(pid_t pid);
+ probe child2_pid(pid_t pid);
+};
--- /dev/null
+set TEST_NAME "dtrace_fork_exec"
+set build_dir ""
+set test_progs {"dtrace_fork_parent" "dtrace_child"}
+
+if {![installtest_p]} { untested $TEST_NAME; return }
+if {![uprobes_p]} { untested $TEST_NAME; return }
+
+source $srcdir/$subdir/test_progs.tcl
+
+# Build everything
+if {[build_test_progs "Makefile.fork_exec" $test_progs] == 0} {
+ fail "$TEST_NAME - build failure"
+ cleanup_test_progs
+ return
+} else {
+ pass "$TEST_NAME - build success"
+}
+
+proc run_test_prog {} {
+ global build_dir
+ catch { exec $build_dir/dtrace_fork_parent $build_dir/dtrace_child }
+ return 0
+}
+
+# Run the test
+set TEST_NAME "dtrace_fork_exec2"
+set output_string "parent - pid: \[0-9\]+\r\nparent - child pid: \[0-9\]+\r\nparent - child pid before exec: \[0-9\]+\r\nchild - pid: \[0-9\]+\r\nparent - finished\r\n"
+stap_run $TEST_NAME run_test_prog $output_string \
+ $srcdir/$subdir/dtrace_fork_exec.stp $build_dir/dtrace_fork_parent \
+ $build_dir/dtrace_child
+
+# Cleanup
+cleanup_test_progs
--- /dev/null
+global output_string
+
+# Parent probes
+probe process(@1).mark("main")
+{
+ output_string .= sprintf("parent - pid: %d\n", $arg1)
+}
+probe process(@1).mark("child")
+{
+ output_string .= sprintf("parent - child pid before exec: %d\n", $arg1)
+}
+probe process(@1).mark("child_pid")
+{
+ output_string .= sprintf("parent - child pid: %d\n", $arg1)
+}
+probe process(@1).mark("finished")
+{
+ output_string .= sprintf("parent - finished\n")
+ exit()
+}
+
+# Child probes
+probe process(@2).mark("main")
+{
+ output_string .= sprintf("child - pid: %d\n", $arg1)
+}
+
+# Testsuite glue
+probe begin
+{
+ printf("systemtap starting probe\n");
+}
+
+probe end
+{
+ printf("systemtap ending probe\n");
+ printf("%s", output_string);
+}
--- /dev/null
+/* Systemtap test case
+ * Copyright (C) 2010, Red Hat Inc.
+ *
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sched.h>
+#include <errno.h>
+#include "dtrace_fork_parent_probes.h"
+
+int
+main(int argc, char **argv)
+{
+ int pid;
+ int rc = 0;
+ char *child_argv[] = { argv[1], NULL };
+
+ /* Create the child. */
+ if (PARENT_MAIN_ENABLED()) {
+ PARENT_MAIN(getpid());
+ }
+ pid = fork();
+ if (pid == 0) { /* child */
+ if (PARENT_CHILD_ENABLED()) {
+ PARENT_CHILD(getpid());
+ }
+ rc = execve(argv[1], child_argv, NULL);
+ _exit(rc);
+ }
+
+ if (PARENT_CHILD_PID_ENABLED()) {
+ PARENT_CHILD_PID(pid);
+ }
+ if (pid < 0) {
+ /* error */
+ perror("fork failed:");
+ rc = -1;
+ }
+ else {
+ int status;
+
+ waitpid(pid, &status, 0);
+ rc = WEXITSTATUS(status);
+ }
+
+ if (PARENT_FINISHED_ENABLED()) {
+ PARENT_FINISHED();
+ }
+ return rc;
+}
--- /dev/null
+provider parent {
+ probe main(pid_t pid);
+ probe child(pid_t pid);
+ probe child_pid(pid_t pid);
+ probe finished();
+};
--- /dev/null
+set TEST_NAME "dtrace_vfork_exec"
+set build_dir ""
+set test_progs {"dtrace_vfork_parent" "dtrace_child"}
+
+if {![installtest_p]} { untested $TEST_NAME; return }
+if {![uprobes_p]} { untested $TEST_NAME; return }
+
+source $srcdir/$subdir/test_progs.tcl
+
+# Build everything
+if {[build_test_progs "Makefile.vfork_exec" $test_progs] == 0} {
+ fail "$TEST_NAME - build failure"
+ cleanup_test_progs
+ return
+} else {
+ pass "$TEST_NAME - build success"
+}
+
+proc run_test_prog {} {
+ global build_dir
+ catch { exec $build_dir/dtrace_vfork_parent $build_dir/dtrace_child }
+ return 0
+}
+
+# Run the test
+set TEST_NAME "dtrace_vfork_exec2"
+set output_string "parent - pid: \[0-9\]+\r\nparent - child pid: \[0-9\]+\r\nchild - pid: \[0-9\]+\r\nparent - finished\r\n"
+stap_run $TEST_NAME run_test_prog $output_string \
+ $srcdir/$subdir/dtrace_vfork_exec.stp $build_dir/dtrace_vfork_parent \
+ $build_dir/dtrace_child
+
+# Cleanup
+cleanup_test_progs
--- /dev/null
+global output_string
+
+# Parent probes
+probe process(@1).mark("main")
+{
+ output_string .= sprintf("parent - pid: %d\n", $arg1)
+}
+probe process(@1).mark("child_pid")
+{
+ output_string .= sprintf("parent - child pid: %d\n", $arg1)
+}
+probe process(@1).mark("finished")
+{
+ output_string .= sprintf("parent - finished\n")
+ exit()
+}
+
+# Child probes
+probe process(@2).mark("main")
+{
+ output_string .= sprintf("child - pid: %d\n", $arg1)
+}
+
+# Testsuite glue
+probe begin
+{
+ printf("systemtap starting probe\n");
+}
+
+probe end
+{
+ printf("systemtap ending probe\n");
+ printf("%s", output_string);
+}
--- /dev/null
+/* Systemtap test case
+ * Copyright (C) 2010, Red Hat Inc.
+ *
+ * This file is part of systemtap, and is free software. You can
+ * redistribute it and/or modify it under the terms of the GNU General
+ * Public License (GPL); either version 2, or (at your option) any
+ * later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sched.h>
+#include <errno.h>
+#include "dtrace_vfork_parent_probes.h"
+
+int
+main(int argc, char **argv)
+{
+ int pid;
+ int rc = 0;
+ char *child_argv[] = { argv[1], NULL };
+
+ /* Create the child. */
+ if (PARENT_MAIN_ENABLED()) {
+ PARENT_MAIN(getpid());
+ }
+ pid = vfork();
+ if (pid == 0) { /* child */
+ rc = execve(argv[1], child_argv, NULL);
+ _exit(rc);
+ }
+
+ if (PARENT_CHILD_PID_ENABLED()) {
+ PARENT_CHILD_PID(pid);
+ }
+ if (pid < 0) {
+ /* error */
+ perror("fork failed:");
+ rc = -1;
+ }
+ else {
+ int status;
+
+ waitpid(pid, &status, 0);
+ rc = WEXITSTATUS(status);
+ }
+
+ if (PARENT_FINISHED_ENABLED()) {
+ PARENT_FINISHED();
+ }
+ return rc;
+}
--- /dev/null
+provider parent {
+ probe main(pid_t pid);
+ probe child_pid(pid_t pid);
+ probe finished();
+};
--- /dev/null
+proc build_test_progs {Makefile test_progs} {
+ global build_dir
+ global srcdir subdir
+ global env
+
+ # Create the build directory and populate it
+ if {[catch {exec mktemp -d staptestXXXXXX} build_dir]} {
+ verbose -log "Failed to create temporary directory: $build_dir"
+ return 0
+ }
+ foreach f [glob $srcdir/$subdir/*.c $srcdir/$subdir/*.d] {
+ exec cp $f $build_dir/
+ }
+ exec cp $srcdir/$subdir/$Makefile $build_dir/Makefile
+
+ # Run make.
+ set includes "-isystem$env(SYSTEMTAP_INCLUDES)"
+ verbose -log "exec make -C $build_dir CFLAGS=$includes"
+ set res [catch "exec make -C $build_dir CFLAGS=$includes" output]
+ verbose -log "$output"
+
+ # Check that the test progs were created
+ foreach f $test_progs {
+ if {![file exists $build_dir/$f]} {
+ verbose -log "Test program $f doesn't exist!"
+ return 0
+ }
+ }
+ return 1
+}
+
+proc cleanup_test_progs {} {
+ global build_dir
+ catch { exec kill -INT -[exp_pid] }
+ if {$build_dir != ""} {
+ catch { exec rm -rf $build_dir }
+ }
+}