This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
[PATCH v3] Add new built-in tapset function abort()
- From: "Yichun Zhang (agentzh)" <agentzh at gmail dot com>
- To: systemtap at sourceware dot org
- Cc: "Yichun Zhang (agentzh)" <agentzh at gmail dot com>
- Date: Tue, 28 Aug 2018 12:54:01 -0700
- Subject: [PATCH v3] Add new built-in tapset function abort()
- References: <20180828024317.13076-1-agentzh@gmail.com>
The new built-in tapset function abort() is similar to exit(), but it
aborts the currently executing functions and probe handlers immediately.
It works in both the kernel and dyninst runtimes. The bpf runtime is not
yet supported.
Unlike error(), abort() cannot be caught by try {...} catch {...}.
Similar to exit(), abort() yeilds the zero process exit code.
fche thinks it is already too late to change the current behavior of
exit(), hence this new function.
fche suggests the function name abort().
fche suggests the global variable name _stp_abort_flag because it looks
alarming and is consistent with the existing global variable
_stp_exit_flag.
Also added corresponding tests for both abort() and exit(), including
tests for probe timer.profile + abort(), as suggested by fche. The tests
cover both the kernel and dyninst runtimes wherever possible.
This new function can be disabled by the '--compatible 3.3' option. Also
added tests for this.
Updated NEWS for this new feature.
---
NEWS | 8 +
runtime/dyninst/io.c | 9 +
runtime/linux/io.c | 13 ++
tapset/logging.stp | 25 +++
testsuite/systemtap.base/abort.exp | 335 +++++++++++++++++++++++++++++++++++
testsuite/systemtap.base/abort_1.stp | 11 ++
testsuite/systemtap.base/abort_2.stp | 17 ++
testsuite/systemtap.base/abort_3.stp | 5 +
testsuite/systemtap.base/abort_6.stp | 9 +
testsuite/systemtap.base/abort_7.stp | 4 +
testsuite/systemtap.base/abort_8.stp | 14 ++
testsuite/systemtap.base/exit.exp | 130 ++++++++++++++
testsuite/systemtap.base/exit_1.stp | 11 ++
testsuite/systemtap.base/exit_2.stp | 17 ++
testsuite/systemtap.base/exit_3.stp | 5 +
translate.cxx | 4 +-
16 files changed, 615 insertions(+), 2 deletions(-)
create mode 100644 testsuite/systemtap.base/abort.exp
create mode 100644 testsuite/systemtap.base/abort_1.stp
create mode 100644 testsuite/systemtap.base/abort_2.stp
create mode 100644 testsuite/systemtap.base/abort_3.stp
create mode 100644 testsuite/systemtap.base/abort_6.stp
create mode 100644 testsuite/systemtap.base/abort_7.stp
create mode 100644 testsuite/systemtap.base/abort_8.stp
create mode 100644 testsuite/systemtap.base/exit.exp
create mode 100644 testsuite/systemtap.base/exit_1.stp
create mode 100644 testsuite/systemtap.base/exit_2.stp
create mode 100644 testsuite/systemtap.base/exit_3.stp
diff --git a/NEWS b/NEWS
index ab20986bf..60e4c108e 100644
--- a/NEWS
+++ b/NEWS
@@ -69,6 +69,14 @@
(The syscall tapsets are broken on kernel 4.17-rc, and will be fixed
in a next release coming soon; PR23160.)
+- Add new built-in tapset function abort() which is similar to exit(),
+ but it aborts the currently executing functions and probe handlers
+ immediately instead of waiting. Unlike error(), abort() cannot be
+ caught by try {...} catch {...}. Similar to exit(), abort() yeilds
+ the zero process exit code. It works in both the kernel and dyninst
+ runtimes. This function can be disabled by the '--compatible 3.3'
+ option.
+
* What's new in version 3.2, 2017-10-18
- SystemTap now includes an extended Berkeley Packet Filter (eBPF)
diff --git a/runtime/dyninst/io.c b/runtime/dyninst/io.c
index b8e9568a5..39094cec2 100644
--- a/runtime/dyninst/io.c
+++ b/runtime/dyninst/io.c
@@ -19,6 +19,9 @@
#define WARN_STRING "WARNING: "
#define ERR_STRING "ERROR: "
+// a global flag to signal immediate probe/function execution abort from abort().
+static int _stp_abort_flag = 0;
+
enum code { INFO=0, WARN, ERROR, DBUG };
static void _stp_vlog (enum code type, const char *func, int line,
@@ -165,4 +168,10 @@ static void _stp_exit (void)
_stp_dyninst_transport_request_exit();
}
+static void _stp_abort (void)
+{
+ _stp_abort_flag = 1;
+ _stp_exit ();
+}
+
#endif /* _STAPDYN_IO_C_ */
diff --git a/runtime/linux/io.c b/runtime/linux/io.c
index 74a032c52..86a17792e 100644
--- a/runtime/linux/io.c
+++ b/runtime/linux/io.c
@@ -24,6 +24,9 @@
#error "STP_LOG_BUF_LEN is too short"
#endif
+// a global flag to signal immediate probe/function execution abort from abort().
+static int _stp_abort_flag = 0;
+
enum code { INFO=0, WARN, ERROR, DBUG };
static void _stp_vlog (enum code type, const char *func, int line, const char *fmt, va_list args)
@@ -105,6 +108,16 @@ static void _stp_exit (void)
_stp_exit_flag = 1;
}
+/** Similar to _stp_exit(), but also sets the flag
+ * _stp_abort_flag.
+ */
+static void _stp_abort (void)
+{
+ _stp_abort_flag = 1;
+ _stp_exit ();
+}
+
+
/** Prints error message and exits.
* This function sends an error message immediately to staprun. It
* will also be sent over the bulk transport (relayfs) if it is
diff --git a/tapset/logging.stp b/tapset/logging.stp
index d09b8eb80..17edac6da 100644
--- a/tapset/logging.stp
+++ b/tapset/logging.stp
@@ -74,6 +74,31 @@ function exit ()
%)
+%(systemtap_v >= "4.0" %?
+/**
+ * sfunction abort - Immediately shutting down probing script.
+ *
+ * Description: This is similar to exit() but immediately stops
+ * the currently executing functions and probes. Unlike error(),
+ * abort() cannot be caught by 'try ... catch'. Similar to exit(),
+ * it yields the zero process exit code.
+ */
+function abort ()
+%( runtime != "bpf" %?
+ %{ /* unprivileged */
+ atomic_set (session_state(), STAP_SESSION_STOPPING);
+ _stp_abort ();
+ %}
+%:
+ { /* unprivileged */ /* bpf */
+ _set_exit_status()
+ printf("ERROR: abort() not supported yet\n")
+ exit() /* TODO: need to abort the execution flow immediately */
+ }
+%)
+%)
+
+
/**
* sfunction error - Send an error message
*
diff --git a/testsuite/systemtap.base/abort.exp b/testsuite/systemtap.base/abort.exp
new file mode 100644
index 000000000..906a77962
--- /dev/null
+++ b/testsuite/systemtap.base/abort.exp
@@ -0,0 +1,335 @@
+set test "abort"
+set testpath "$srcdir/$subdir"
+
+if {! [installtest_p]} { untested "$test"; return }
+
+# --- TEST 1 ---
+
+set subtest1 "TEST 1: abort() in the middle of a func body"
+foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest1 ($runtime)"
+
+ set cmd "stap --runtime=$runtime '$srcdir/$subdir/${test}_1.stp'"
+ send_log "executing: $cmd\n"
+ set pipe [open "| sh -c {$cmd}" r]
+ set out [read $pipe]
+ set failed 0
+ if {[catch {close $pipe} stderr] != 0} {
+ if {$stderr ne "" && [string index $stderr end] ne "\n"} {
+ append stderr "\n"
+ }
+ global errorCode
+ if {"CHILDSTATUS" == [lindex $errorCode 0]} {
+ set failed [lindex $errorCode 2]
+ }
+ }
+
+ set exp_out "enter f\n"
+ regsub -all -- {\n} $exp_out {\n} escaped_exp_out
+ if {$out eq $exp_out} {
+ pass "${test_name}: stdout matches \"$escaped_exp_out\""
+ } else {
+ fail "${test_name}: stdout fails to match \"$escaped_exp_out\": got \"$out\""
+ }
+
+ if {$failed} {
+ fail "${test_name}: exit code should be zero but is $failed"
+ } else {
+ pass "${test_name}: exit code is zero"
+ }
+ if {$stderr ne ""} {
+ send_log "stderr:\n$stderr"
+ }
+}
+
+# --- TEST 2 ---
+
+set subtest2 "TEST 2: abort() in the middle of a func body in a deeper func call chain"
+foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest2 ($runtime)"
+
+ set cmd "stap --runtime=$runtime '$srcdir/$subdir/${test}_2.stp'"
+ send_log "executing: $cmd\n"
+ set pipe [open "| sh -c {$cmd}" r]
+ set out [read $pipe]
+ set failed 0
+ if {[catch {close $pipe} stderr] != 0} {
+ if {$stderr ne "" && [string index $stderr end] ne "\n"} {
+ append stderr "\n"
+ }
+ global errorCode
+ if {"CHILDSTATUS" == [lindex $errorCode 0]} {
+ set failed [lindex $errorCode 2]
+ }
+ }
+
+ set exp_out "enter g\nenter f\n"
+ regsub -all -- {\n} $exp_out {\n} escaped_exp_out
+ if {$out eq $exp_out} {
+ pass "${test_name}: stdout matches \"$escaped_exp_out\""
+ } else {
+ fail "${test_name}: stdout fails to match \"$escaped_exp_out\": got \"$out\""
+ }
+
+ if {$failed} {
+ fail "${test_name}: exit code should be zero but is $failed"
+ } else {
+ pass "${test_name}: exit code is zero"
+ }
+ if {$stderr ne ""} {
+ send_log "stderr:\n$stderr"
+ }
+}
+
+# --- TEST 3 ---
+
+set subtest3 "TEST 3: abort() in the middle of a probe handler body"
+foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest3 ($runtime)"
+
+ set cmd "stap --runtime=$runtime '$srcdir/$subdir/${test}_3.stp'"
+ send_log "executing: $cmd\n"
+ set pipe [open "| sh -c {$cmd}" r]
+ set out [read $pipe]
+ set failed 0
+ if {[catch {close $pipe} stderr] != 0} {
+ if {$stderr ne "" && [string index $stderr end] ne "\n"} {
+ append stderr "\n"
+ }
+ global errorCode
+ if {"CHILDSTATUS" == [lindex $errorCode 0]} {
+ set failed [lindex $errorCode 2]
+ }
+ }
+
+ set exp_out "enter probe\n"
+ regsub -all -- {\n} $exp_out {\n} escaped_exp_out
+ if {$out eq $exp_out} {
+ pass "${test_name}: stdout matches \"$escaped_exp_out\""
+ } else {
+ fail "${test_name}: stdout fails to match \"$escaped_exp_out\": got \"$out\""
+ }
+
+ if {$failed} {
+ fail "${test_name}: exit code should be zero but is $failed"
+ } else {
+ pass "${test_name}: exit code is zero"
+ }
+ if {$stderr ne ""} {
+ send_log "stderr:\n$stderr"
+ }
+}
+
+# --- TEST 4 ---
+
+set subtest4 "TEST 4: abort() in the middle of a probe handler body (--compatible 4.0)"
+foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest4 ($runtime)"
+
+ set cmd "stap --compatible 4.0 --runtime=$runtime '$srcdir/$subdir/${test}_3.stp'"
+ send_log "executing: $cmd\n"
+ set pipe [open "| sh -c {$cmd}" r]
+ set out [read $pipe]
+ set failed 0
+ if {[catch {close $pipe} stderr] != 0} {
+ if {$stderr ne "" && [string index $stderr end] ne "\n"} {
+ append stderr "\n"
+ }
+ global errorCode
+ if {"CHILDSTATUS" == [lindex $errorCode 0]} {
+ set failed [lindex $errorCode 2]
+ }
+ }
+
+ set exp_out "enter probe\n"
+ regsub -all -- {\n} $exp_out {\n} escaped_exp_out
+ if {$out eq $exp_out} {
+ pass "${test_name}: stdout matches \"$escaped_exp_out\""
+ } else {
+ fail "${test_name}: stdout fails to match \"$escaped_exp_out\": got \"$out\""
+ }
+
+ if {$failed} {
+ fail "${test_name}: exit code should be zero but is $failed"
+ } else {
+ pass "${test_name}: exit code is zero"
+ }
+ if {$stderr ne ""} {
+ send_log "stderr:\n$stderr"
+ }
+}
+
+# --- TEST 5 ---
+
+set subtest5 "TEST 5: abort() in the middle of a probe handler body (--compatible 3.3)"
+set test_name "$test: $subtest5"
+
+set cmd "stap --compatible 3.3 '$srcdir/$subdir/${test}_3.stp'"
+send_log "executing: $cmd\n"
+set pipe [open "| sh -c {$cmd}" r]
+set out [read $pipe]
+set failed 0
+if {[catch {close $pipe} stderr] != 0} {
+ if {$stderr ne "" && [string index $stderr end] ne "\n"} {
+ append stderr "\n"
+ }
+ global errorCode
+ if {"CHILDSTATUS" == [lindex $errorCode 0]} {
+ set failed [lindex $errorCode 2]
+ }
+}
+
+set exp_out ""
+regsub -all -- {\n} $exp_out {\n} escaped_exp_out
+if {$out eq $exp_out} {
+ pass "${test_name}: stdout matches \"$escaped_exp_out\""
+} else {
+ fail "${test_name}: stdout fails to match \"$escaped_exp_out\": got \"$out\""
+}
+
+if {$failed} {
+ pass "${test_name}: exit code should be non-zero"
+} else {
+ fail "${test_name}: exit code should be non-zero but is zero"
+}
+
+set stderr_pat "^semantic error: unresolved function \\(similar: \[^\\n\]*?\\): identifier 'abort' at \[^\\n\]*?\\.stp:3:5\\n"
+regsub -all -- {\n} $stderr_pat {\n} escaped_stderr_pat
+if {[regexp -linestop -lineanchor -- $stderr_pat $stderr]} {
+ pass "${test_name}: stderr matches \"$escaped_stderr_pat\""
+} else {
+ fail "${test_name}: stderr fails to match \"$escaped_stderr_pat\": got \"$stderr\""
+}
+
+# --- TEST 6 ---
+
+set subtest6 "TEST 6: abort() in timer.profile (using globals)"
+set test_name "$test: $subtest6"
+
+set cmd "stap '$srcdir/$subdir/${test}_6.stp'"
+send_log "executing: $cmd\n"
+set pipe [open "| sh -c {$cmd}" r]
+set out [read $pipe]
+set failed 0
+if {[catch {close $pipe} stderr] != 0} {
+ if {$stderr ne "" && [string index $stderr end] ne "\n"} {
+ append stderr "\n"
+ }
+ global errorCode
+ if {"CHILDSTATUS" == [lindex $errorCode 0]} {
+ set failed [lindex $errorCode 2]
+ }
+}
+
+set exp_out "fire 3!\nfire 2!\nfire 1!\n"
+regsub -all -- {\n} $exp_out {\n} escaped_exp_out
+if {$out eq $exp_out} {
+ pass "${test_name}: stdout matches \"$escaped_exp_out\""
+} else {
+ fail "${test_name}: stdout fails to match \"$escaped_exp_out\": got \"$out\""
+}
+
+if {$failed} {
+ fail "${test_name}: exit code should be zero but is $failed"
+} else {
+ pass "${test_name}: exit code is zero"
+}
+if {$stderr ne ""} {
+ send_log "stderr:\n$stderr"
+}
+
+# --- TEST 7 ---
+
+set subtest7 "TEST 7: abort() in timer.profile (more concurrency and no globals)"
+set test_name "$test: $subtest7"
+
+set cmd "stap '$srcdir/$subdir/${test}_7.stp'"
+send_log "executing: $cmd\n"
+set pipe [open "| sh -c {$cmd}" r]
+set out [read $pipe]
+set failed 0
+if {[catch {close $pipe} stderr] != 0} {
+ if {$stderr ne "" && [string index $stderr end] ne "\n"} {
+ append stderr "\n"
+ }
+ global errorCode
+ if {"CHILDSTATUS" == [lindex $errorCode 0]} {
+ set failed [lindex $errorCode 2]
+ }
+}
+
+set exp_out ""
+regsub -all -- {\n} $exp_out {\n} escaped_exp_out
+if {$out eq $exp_out} {
+ pass "${test_name}: stdout matches \"$escaped_exp_out\""
+} else {
+ fail "${test_name}: stdout fails to match \"$escaped_exp_out\": got \"$out\""
+}
+
+set exp_stderr ""
+regsub -all -- {\n} $exp_stderr {\n} escaped_exp_stderr
+if {$stderr eq $exp_stderr} {
+ pass "${test_name}: stderr matches \"$escaped_exp_stderr\""
+} else {
+ fail "${test_name}: stderr fails to match \"$escaped_exp_stderr\": got \"$stderr\""
+}
+
+if {$failed} {
+ fail "${test_name}: exit code should be zero but is $failed"
+} else {
+ pass "${test_name}: exit code is zero"
+}
+
+# --- TEST 8 ---
+
+set subtest8 "TEST 8: abort() in the middle of a func body - abort() cannot be caught"
+foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest8 ($runtime)"
+
+ set cmd "stap --runtime=$runtime '$srcdir/$subdir/${test}_8.stp'"
+ send_log "executing: $cmd\n"
+ set pipe [open "| sh -c {$cmd}" r]
+ set out [read $pipe]
+ set failed 0
+ if {[catch {close $pipe} stderr] != 0} {
+ if {$stderr ne "" && [string index $stderr end] ne "\n"} {
+ append stderr "\n"
+ }
+ global errorCode
+ if {"CHILDSTATUS" == [lindex $errorCode 0]} {
+ set failed [lindex $errorCode 2]
+ }
+ }
+
+ set exp_out "enter f\n"
+ regsub -all -- {\n} $exp_out {\n} escaped_exp_out
+ if {$out eq $exp_out} {
+ pass "${test_name}: stdout matches \"$escaped_exp_out\""
+ } else {
+ fail "${test_name}: stdout fails to match \"$escaped_exp_out\": got \"$out\""
+ }
+
+ if {$failed} {
+ fail "${test_name}: exit code should be zero but is $failed"
+ } else {
+ pass "${test_name}: exit code is zero"
+ }
+ if {$stderr ne ""} {
+ send_log "stderr:\n$stderr"
+ }
+}
diff --git a/testsuite/systemtap.base/abort_1.stp b/testsuite/systemtap.base/abort_1.stp
new file mode 100644
index 000000000..633a49616
--- /dev/null
+++ b/testsuite/systemtap.base/abort_1.stp
@@ -0,0 +1,11 @@
+function f() {
+ println("enter f");
+ abort();
+ println("leave f");
+}
+
+probe oneshot {
+ f();
+ printf("abort\n");
+ abort();
+}
diff --git a/testsuite/systemtap.base/abort_2.stp b/testsuite/systemtap.base/abort_2.stp
new file mode 100644
index 000000000..b8f864d06
--- /dev/null
+++ b/testsuite/systemtap.base/abort_2.stp
@@ -0,0 +1,17 @@
+function f() {
+ println("enter f");
+ abort();
+ println("leave f");
+}
+
+function g() {
+ println("enter g");
+ f();
+ println("leave g");
+}
+
+probe oneshot {
+ g();
+ printf("abort\n");
+ abort();
+}
diff --git a/testsuite/systemtap.base/abort_3.stp b/testsuite/systemtap.base/abort_3.stp
new file mode 100644
index 000000000..48182f1a3
--- /dev/null
+++ b/testsuite/systemtap.base/abort_3.stp
@@ -0,0 +1,5 @@
+probe oneshot {
+ println("enter probe");
+ abort();
+ println("leave probe");
+}
diff --git a/testsuite/systemtap.base/abort_6.stp b/testsuite/systemtap.base/abort_6.stp
new file mode 100644
index 000000000..2b42ec3f7
--- /dev/null
+++ b/testsuite/systemtap.base/abort_6.stp
@@ -0,0 +1,9 @@
+global rest = 3
+
+probe timer.profile {
+ id = rest--;
+ if (id <= 0) {
+ abort();
+ }
+ printf("fire %d!\n", id);
+}
diff --git a/testsuite/systemtap.base/abort_7.stp b/testsuite/systemtap.base/abort_7.stp
new file mode 100644
index 000000000..972ba365d
--- /dev/null
+++ b/testsuite/systemtap.base/abort_7.stp
@@ -0,0 +1,4 @@
+probe timer.profile {
+ abort();
+ error("fire after abort!\n")
+}
diff --git a/testsuite/systemtap.base/abort_8.stp b/testsuite/systemtap.base/abort_8.stp
new file mode 100644
index 000000000..304e788e6
--- /dev/null
+++ b/testsuite/systemtap.base/abort_8.stp
@@ -0,0 +1,14 @@
+function f() {
+ println("enter f");
+ try {
+ abort();
+ } catch {
+ println("caught abort!")
+ }
+ println("leave f");
+}
+
+probe oneshot {
+ f();
+ printf("abort\n");
+}
diff --git a/testsuite/systemtap.base/exit.exp b/testsuite/systemtap.base/exit.exp
new file mode 100644
index 000000000..9296590e2
--- /dev/null
+++ b/testsuite/systemtap.base/exit.exp
@@ -0,0 +1,130 @@
+set test "exit"
+set testpath "$srcdir/$subdir"
+
+if {! [installtest_p]} { untested "$test"; return }
+
+# --- TEST 1 ---
+
+set subtest1 "TEST 1: exit() in the middle of a func body"
+foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest1 ($runtime)"
+
+ set cmd "stap --runtime=$runtime '$srcdir/$subdir/${test}_1.stp'"
+ send_log "executing: $cmd\n"
+ set pipe [open "| sh -c {$cmd}" r]
+ set out [read $pipe]
+ set failed 0
+ if {[catch {close $pipe} stderr] != 0} {
+ if {$stderr ne "" && [string index $stderr end] ne "\n"} {
+ append stderr "\n"
+ }
+ global errorCode
+ if {"CHILDSTATUS" == [lindex $errorCode 0]} {
+ set failed [lindex $errorCode 2]
+ }
+ }
+
+ set exp_out "enter f\nleave f\nexit\n"
+ regsub -all -- {\n} $exp_out {\n} escaped_exp_out
+ if {$out eq $exp_out} {
+ pass "${test_name}: stdout matches \"$escaped_exp_out\""
+ } else {
+ fail "${test_name}: stdout fails to match \"$escaped_exp_out\": got \"$out\""
+ }
+
+ if {$failed} {
+ fail "${test_name}: exit code should be zero but is $failed"
+ } else {
+ pass "${test_name}: exit code is zero"
+ }
+ if {$stderr ne ""} {
+ send_log "stderr:\n$stderr"
+ }
+}
+
+# --- TEST 2 ---
+
+set subtest2 "TEST 2: exit() in the middle of a func body in a deeper func call chain"
+foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest2 ($runtime)"
+
+ set cmd "stap --runtime=$runtime '$srcdir/$subdir/${test}_2.stp'"
+ send_log "executing: $cmd\n"
+ set pipe [open "| sh -c {$cmd}" r]
+ set out [read $pipe]
+ set failed 0
+ if {[catch {close $pipe} stderr] != 0} {
+ if {$stderr ne "" && [string index $stderr end] ne "\n"} {
+ append stderr "\n"
+ }
+ global errorCode
+ if {"CHILDSTATUS" == [lindex $errorCode 0]} {
+ set failed [lindex $errorCode 2]
+ }
+ }
+
+ set exp_out "enter g\nenter f\nleave f\nleave g\nexit\n"
+ regsub -all -- {\n} $exp_out {\n} escaped_exp_out
+ if {$out eq $exp_out} {
+ pass "${test_name}: stdout matches \"$escaped_exp_out\""
+ } else {
+ fail "${test_name}: stdout fails to match \"$escaped_exp_out\": got \"$out\""
+ }
+
+ if {$failed} {
+ fail "${test_name}: exit code should be zero but is $failed"
+ } else {
+ pass "${test_name}: exit code is zero"
+ }
+ if {$stderr ne ""} {
+ send_log "stderr:\n$stderr"
+ }
+}
+
+# --- TEST 3 ---
+
+set subtest3 "TEST 3: exit() in the middle of a probe handler body"
+foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest3 ($runtime)"
+
+ set cmd "stap --runtime=$runtime '$srcdir/$subdir/${test}_3.stp'"
+ send_log "executing: $cmd\n"
+ set pipe [open "| sh -c {$cmd}" r]
+ set out [read $pipe]
+ set failed 0
+ if {[catch {close $pipe} stderr] != 0} {
+ if {$stderr ne "" && [string index $stderr end] ne "\n"} {
+ append stderr "\n"
+ }
+ global errorCode
+ if {"CHILDSTATUS" == [lindex $errorCode 0]} {
+ set failed [lindex $errorCode 2]
+ }
+ }
+
+ set exp_out "enter probe\nleave probe\n"
+ regsub -all -- {\n} $exp_out {\n} escaped_exp_out
+ if {$out eq $exp_out} {
+ pass "${test_name}: stdout matches \"$escaped_exp_out\""
+ } else {
+ fail "${test_name}: stdout fails to match \"$escaped_exp_out\": got \"$out\""
+ }
+
+ if {$failed} {
+ fail "${test_name}: exit code should be zero but is $failed"
+ } else {
+ pass "${test_name}: exit code is zero"
+ }
+ if {$stderr ne ""} {
+ send_log "stderr:\n$stderr"
+ }
+}
diff --git a/testsuite/systemtap.base/exit_1.stp b/testsuite/systemtap.base/exit_1.stp
new file mode 100644
index 000000000..c2dd0141a
--- /dev/null
+++ b/testsuite/systemtap.base/exit_1.stp
@@ -0,0 +1,11 @@
+function f() {
+ println("enter f");
+ exit();
+ println("leave f");
+}
+
+probe begin {
+ f();
+ printf("exit\n");
+ exit();
+}
diff --git a/testsuite/systemtap.base/exit_2.stp b/testsuite/systemtap.base/exit_2.stp
new file mode 100644
index 000000000..b747ddc0c
--- /dev/null
+++ b/testsuite/systemtap.base/exit_2.stp
@@ -0,0 +1,17 @@
+function f() {
+ println("enter f");
+ exit();
+ println("leave f");
+}
+
+function g() {
+ println("enter g");
+ f();
+ println("leave g");
+}
+
+probe begin {
+ g();
+ printf("exit\n");
+ exit();
+}
diff --git a/testsuite/systemtap.base/exit_3.stp b/testsuite/systemtap.base/exit_3.stp
new file mode 100644
index 000000000..3f3b801d1
--- /dev/null
+++ b/testsuite/systemtap.base/exit_3.stp
@@ -0,0 +1,5 @@
+probe begin {
+ println("enter probe");
+ exit();
+ println("leave probe");
+}
diff --git a/translate.cxx b/translate.cxx
index c7e7aa53f..743d6cd0e 100644
--- a/translate.cxx
+++ b/translate.cxx
@@ -5732,7 +5732,7 @@ c_unparser::visit_functioncall (functioncall* e)
// call function
o->newline() << c_funcname (r->name) << " (c);";
- o->newline() << "if (unlikely(c->last_error)) goto out;";
+ o->newline() << "if (unlikely(c->last_error || _stp_abort_flag)) goto out;";
if (!already_checked_action_count && !session->suppress_time_limits
&& !session->unoptimized)
@@ -6069,7 +6069,7 @@ c_unparser::visit_print_format (print_format* e)
}
o->line() << ");";
o->newline(-1) << "#endif // STP_LEGACY_PRINT";
- o->newline() << "if (unlikely(c->last_error)) goto out;";
+ o->newline() << "if (unlikely(c->last_error || _stp_abort_flag)) goto out;";
o->newline() << res.value() << ";";
}
}
--
2.11.0.295.gd7dffce