statement ();
statement (const token* tok);
virtual ~statement ();
+ virtual bool might_pushdown_lock () = 0;
};
std::ostream& operator << (std::ostream& o, const statement& k);
bool tagged_p (const interned_string& tag) const;
void print (std::ostream& o) const;
void visit (visitor* u);
+ virtual bool might_pushdown_lock () { return false; };
};
block (statement* car, statement* cdr);
block (statement* car, statement* cdr1, statement* cdr2);
virtual ~block () {}
+ virtual bool might_pushdown_lock () { return true; };
};
symbol* catch_error_var; // may be 0
void print (std::ostream& o) const;
void visit (visitor* u);
+
+ // PR26296: for try/catch, don't try to push lock/unlock down
+ virtual bool might_pushdown_lock () { return false; };
};
statement* block;
void print (std::ostream& o) const;
void visit (visitor* u);
+
+ // PR26269 lockpushdown:
+ // for loops, forget optimizing, just emit locks at top & bottom
+ virtual bool might_pushdown_lock () { return false; };
};
statement* block;
void print (std::ostream& o) const;
void visit (visitor* u);
+
+ // PR26269 lockpushdown:
+ // for loops, forget optimizing, just emit locks at top & bottom
+ virtual bool might_pushdown_lock () { return false; };
};
void print (std::ostream& o) const;
void visit (visitor* u);
null_statement (const token* tok);
+ virtual bool might_pushdown_lock () { return false; };
};
expression* value; // executed for side-effects
void print (std::ostream& o) const;
void visit (visitor* u);
+ virtual bool might_pushdown_lock () { return false; };
};
statement* elseblock; // may be 0
void print (std::ostream& o) const;
void visit (visitor* u);
+ virtual bool might_pushdown_lock () { return true; };
};
{
void print (std::ostream& o) const;
void visit (visitor* u);
+
+ // PR26296: We should not encounter a RETURN statement in a
+ // lock-relevant section of code (a probe handler body) at all.
+ virtual bool might_pushdown_lock () { return false; };
};
{
void print (std::ostream& o) const;
void visit (visitor* u);
+ virtual bool might_pushdown_lock () { return false; };
};
{
void print (std::ostream& o) const;
void visit (visitor* u);
+ virtual bool might_pushdown_lock () { return false; };
};
{
void print (std::ostream& o) const;
void visit (visitor* u);
+ virtual bool might_pushdown_lock () { return false; };
};
{
void print (std::ostream& o) const;
void visit (visitor* u);
+ virtual bool might_pushdown_lock () { return false; };
};
--- /dev/null
+set test "lock-pushdown-bugs"
+set testpath "$srcdir/$subdir"
+
+if {! [installtest_p]} { untested $test; return }
+if {! [uretprobes_p]} { untested $test; return }
+
+# --- TEST 1 ---
+
+set subtest1 "TEST 1: PR31018: if stmt needing locks before a map op stmt (check at runtime)"
+
+set res [target_compile ${testpath}/${test}_1.c ./a.out executable \
+ "additional_flags=-O additional_flags=-g additional_flags=-Wno-unused-parameter"]
+if {$res ne ""} {
+ verbose "target_compile failed: $res" 2
+ fail "$test: $subtest1: unable to compile ${test}_1.c"
+} else {
+ foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest1 ($runtime)"
+ set cmd "stap -g --runtime=$runtime -c ./a.out '$srcdir/$subdir/${test}_1.stp'"
+ set exit_code [run_cmd_2way $cmd out stderr]
+ set exp_out "ok\n"
+ is "${test_name}: stdout" $out $exp_out
+ set exp_stderr ""
+ is "${test_name}: stderr" $stderr $exp_stderr
+ is "${test_name}: exit code" $exit_code 0
+ }
+}
+
+# --- TEST 2 ---
+
+set subtest2 "TEST 2: PR31018: if stmt needing locks before a map op stmt (check at runtime, if entered)"
+
+set res [target_compile ${testpath}/${test}_1.c ./a.out executable \
+ "additional_flags=-O additional_flags=-g additional_flags=-Wno-unused-parameter"]
+if {$res ne ""} {
+ verbose "target_compile failed: $res" 2
+ fail "$test: $subtest2: unable to compile ${test}_1.c"
+} else {
+ foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest2 ($runtime)"
+ set cmd "stap -g --runtime=$runtime -c ./a.out '$srcdir/$subdir/${test}_2.stp'"
+ set exit_code [run_cmd_2way $cmd out stderr]
+ set exp_out "ok\n"
+ is "${test_name}: stdout" $out $exp_out
+ set exp_stderr ""
+ is "${test_name}: stderr" $stderr $exp_stderr
+ is "${test_name}: exit code" $exit_code 0
+ }
+}
+
+# --- TEST 3 ---
+
+set subtest3 "TEST 3: PR31018: nested block stmt needing locks before a map op stmt (check at runtime)"
+
+set res [target_compile ${testpath}/${test}_1.c ./a.out executable \
+ "additional_flags=-O additional_flags=-g additional_flags=-Wno-unused-parameter"]
+if {$res ne ""} {
+ verbose "target_compile failed: $res" 2
+ fail "$test: $subtest3: unable to compile ${test}_1.c"
+} else {
+ foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest3 ($runtime)"
+ set cmd "stap -g --runtime=$runtime -c ./a.out '$srcdir/$subdir/${test}_3.stp'"
+ set exit_code [run_cmd_2way $cmd out stderr]
+ set exp_out "ok\n"
+ is "${test_name}: stdout" $out $exp_out
+ set exp_stderr ""
+ is "${test_name}: stderr" $stderr $exp_stderr
+ is "${test_name}: exit code" $exit_code 0
+ }
+}
+
+# --- TEST 4 ---
+
+set subtest4 "TEST 4: PR31018: nested block stmt needing locks before a map op stmt (check at runtime, if taken)"
+
+set res [target_compile ${testpath}/${test}_1.c ./a.out executable \
+ "additional_flags=-O additional_flags=-g additional_flags=-Wno-unused-parameter"]
+if {$res ne ""} {
+ verbose "target_compile failed: $res" 2
+ fail "$test: $subtest4: unable to compile ${test}_1.c"
+} else {
+ foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest4 ($runtime)"
+ set cmd "stap -g --runtime=$runtime -c ./a.out '$srcdir/$subdir/${test}_4.stp'"
+ set exit_code [run_cmd_2way $cmd out stderr]
+ set exp_out "ok\n"
+ is "${test_name}: stdout" $out $exp_out
+ set exp_stderr ""
+ is "${test_name}: stderr" $stderr $exp_stderr
+ is "${test_name}: exit code" $exit_code 0
+ }
+}
+
+# --- TEST 5 ---
+
+set subtest5 "TEST 5: PR31018: if stmt needing locks before a map op stmt (check at runtime)"
+
+set res [target_compile ${testpath}/${test}_1.c ./a.out executable \
+ "additional_flags=-O additional_flags=-g additional_flags=-Wno-unused-parameter"]
+if {$res ne ""} {
+ verbose "target_compile failed: $res" 2
+ fail "$test: $subtest5: unable to compile ${test}_1.c"
+} else {
+ foreach runtime [get_runtime_list] {
+ if {$runtime eq ""} {
+ set runtime "kernel"
+ }
+ set test_name "$test: $subtest5 ($runtime)"
+ set cmd "stap -g --runtime=$runtime -c ./a.out '$srcdir/$subdir/${test}_5.stp'"
+ set exit_code [run_cmd_2way $cmd out stderr]
+ set exp_out "ok\n"
+ is "${test_name}: stdout" $out $exp_out
+ set exp_stderr ""
+ is "${test_name}: stderr" $stderr $exp_stderr
+ is "${test_name}: exit code" $exit_code 0
+ }
+}
--- /dev/null
+int main(int argc, char *argv[]) {
+ return argc - 1;
+}
--- /dev/null
+@define assert_lock(v) %( if (%{ c->locked %} != @v) { error(sprintf("locked mismatch, expected %d, actual %d", @v, %{ c->locked %})) } %)
+
+global stats;
+
+probe process.function("main") {
+ my_pid = pid();
+ v = @var("argc");
+ @assert_lock(0);
+ if (v > 3) {
+ delete stats;
+ }
+ @assert_lock(0);
+ stats[my_pid] <<< v;
+ @assert_lock(1);
+ stats[my_pid] <<< v;
+ @assert_lock(2);
+ println("ok");
+}
--- /dev/null
+@define assert_lock(v) %( if (%{ c->locked %} != @v) { error(sprintf("locked mismatch, expected %d, actual %d", @v, %{ c->locked %})) } %)
+
+global stats;
+
+probe process.function("main") {
+ my_pid = pid();
+ v = @var("argc");
+ @assert_lock(0);
+ if (v == 1) {
+ @assert_lock(0);
+ delete stats;
+ }
+ @assert_lock(1);
+ stats[my_pid] <<< v;
+ @assert_lock(1);
+ stats[my_pid] <<< v;
+ @assert_lock(2);
+ println("ok");
+}
--- /dev/null
+@define assert_lock(v) %( if (%{ c->locked %} != @v) { error(sprintf("locked mismatch, expected %d, actual %d", @v, %{ c->locked %})) } %)
+
+global stats;
+
+probe process.function("main") {
+ my_pid = pid();
+ v = @var("argc");
+ @assert_lock(0);
+ {
+ @assert_lock(0);
+ if (v > 3) {
+ delete stats;
+ }
+ }
+ @assert_lock(0);
+ stats[my_pid] <<< v;
+ @assert_lock(1);
+ stats[my_pid] <<< v;
+ @assert_lock(2);
+ println("ok");
+ @assert_lock(2);
+}
--- /dev/null
+@define assert_lock(v) %( if (%{ c->locked %} != @v) { error(sprintf("locked mismatch, expected %d, actual %d", @v, %{ c->locked %})) } %)
+
+global stats;
+
+probe process.function("main") {
+ my_pid = pid();
+ v = @var("argc");
+ @assert_lock(0);
+ {
+ @assert_lock(0);
+ if (v == 1) {
+ @assert_lock(0);
+ delete stats;
+ }
+ }
+ @assert_lock(1);
+ stats[my_pid] <<< v;
+ @assert_lock(1);
+ stats[my_pid] <<< v;
+ @assert_lock(2);
+ println("ok");
+ @assert_lock(2);
+}
--- /dev/null
+@define assert_lock(v) %( if (%{ c->locked %} != @v) { error(sprintf("locked mismatch, expected %d, actual %d", @v, %{ c->locked %})) } %)
+
+global stats;
+
+probe process.function("main") {
+ my_pid = pid();
+ v = @var("argc");
+ @assert_lock(0);
+ if (v > 3) {
+ delete stats;
+ }
+ @assert_lock(0);
+ if (v > 2) {
+ delete stats;
+ }
+ @assert_lock(0);
+ stats[my_pid] <<< v;
+ @assert_lock(1);
+ stats[my_pid] <<< v;
+ @assert_lock(2);
+ println("ok");
+}
// if needed, find the lock insertion site; instruct it to lock
if (pushdown_lock_p(s))
- for (unsigned i=0; i<s->statements.size(); i++)
- if (locks_needed_p (s->statements[i]))
- { pushdown_lock.insert(s->statements[i]); pushed_lock_down = true; break; }
-
+ {
+ for (unsigned i=0; i<s->statements.size(); i++)
+ {
+ struct statement *stmt = s->statements[i];
+ if (locks_needed_p (stmt))
+ {
+ pushed_lock_down = true;
+ pushdown_lock.insert (stmt);
+
+ if (! stmt->might_pushdown_lock ())
+ {
+ // now we know the subsquement stmts must have locks
+ // held, so we don't bother going forward.
+ break;
+ }
+ }
+ }
+ }
+
// if needed, find the unlock insertion site; instruct it to unlock
if (pushdown_unlock_p(s))
for (ssize_t i=s->statements.size()-1; i>=0; i--) // NB: traverse backward!