This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
[PATCH v4] 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 16:22:27 -0700
- Subject: [PATCH v4] Add new built-in tapset function abort()
- References: <20180828195401.104512-1-agentzh@gmail.com>
The new built-in tapset function abort() is similar to exit(), but it
aborts the current probe handler (and any function calls in it) immediately.
It works with 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. And he suggests the function name abort().
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 | 9 +
runtime/common_probe_context.h | 6 +
tapset/logging.stp | 28 +++
tapsets.cxx | 1 +
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, 604 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..273b791fc 100644
--- a/NEWS
+++ b/NEWS
@@ -69,6 +69,15 @@
(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 current probe handler (and any function calls in
+ it) immediately instead of waiting for its completion. Probe handlers
+ already running on *other* CPU cores, however, will still continue
+ to their completion. Unlike error(), abort() cannot be caught by
+ try {...} catch {...}. Similar to exit(), abort() yeilds the zero
+ process exit code. It works with 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/common_probe_context.h b/runtime/common_probe_context.h
index 5ab917781..7af0b78ef 100644
--- a/runtime/common_probe_context.h
+++ b/runtime/common_probe_context.h
@@ -70,6 +70,12 @@ int nesting;
* if no more alternatives are to be executed. */
int next;
+/* A flat to signal aborting the currently running probe
+ handler.
+ While it's 0, execution continues
+ When it's 1, probe code unwnds. */
+int aborted;
+
/* A place to format error messages into if some error occurs, last_error
will then be pointed here. */
string_t error_buffer;
diff --git a/tapset/logging.stp b/tapset/logging.stp
index d09b8eb80..6b824c1b6 100644
--- a/tapset/logging.stp
+++ b/tapset/logging.stp
@@ -74,6 +74,34 @@ function exit ()
%)
+%(systemtap_v >= "4.0" %?
+/**
+ * sfunction abort - Immediately shutting down probing script.
+ *
+ * Description: This is similar to exit() but immediately aborts
+ * the current probe handler instead of waiting for its
+ * completion. Probe handlers already running on *other* CPU cores,
+ * however, will still continue to their completion. Unlike error(),
+ * this function call cannot be caught by 'try ... catch'.
+ */
+function abort ()
+%( runtime != "bpf" %?
+ %{ /* unprivileged */
+ atomic_set (session_state(), STAP_SESSION_STOPPING);
+ _stp_exit ();
+ CONTEXT->aborted = 1;
+ CONTEXT->last_stmt = NULL;
+ %}
+%:
+ { /* 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/tapsets.cxx b/tapsets.cxx
index ae6cfd83f..509e45748 100644
--- a/tapsets.cxx
+++ b/tapsets.cxx
@@ -181,6 +181,7 @@ common_probe_entryfn_prologue (systemtap_session& s,
s.op->newline(-1) << "}";
s.op->newline();
+ s.op->newline() << "c->aborted = 0;";
s.op->newline() << "c->last_stmt = 0;";
s.op->newline() << "c->last_error = 0;";
s.op->newline() << "c->nesting = -1;"; // NB: PR10516 packs locals[] tighter
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..a1b25d9b3 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 || c->aborted)) 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 || c->aborted)) goto out;";
o->newline() << res.value() << ";";
}
}
--
2.11.0.295.gd7dffce