summaryrefslogtreecommitdiff
path: root/test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc
diff options
context:
space:
mode:
author Alex Light <allight@google.com> 2020-01-14 14:45:40 -0800
committer Treehugger Robot <treehugger-gerrit@google.com> 2020-01-22 21:56:30 +0000
commit721e40283793649b4750c05da4fe972bd372f7f9 (patch)
tree9af2c2c583900e9a35c6adf7fef277d0221c0571 /test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc
parentdf7e5b836e78ab38101dde45399c8de51df4042e (diff)
Fix stack-walking race
During stack walking it was possible for a walking thread to race with the InstrumentationInstallStack. In this case the stack changes made by InstrumentationInstallStack could cause the other thread to incorrectly parse the stack data-structures, leading to failures/crashes. To fix this we increment an ID whenever instrumentation changes the instrumentation stack. Other stack-walkers then check this id and restart whenever it changes. Note that although the stack-walk restarts we keep track of how many java frames we've visited already and refrain from calling VisitFrame until we are at the same position again. This means that as far as the Visitor writers are concerned the stack-walk is only done once, just like always. Added a test (2011) that forces the race to occur. Also Disabled Dex2oatSwapUseTest#CheckSwapUsage. It seems that the increase in Thread* size has caused it to fail. Disable while we investigate. See b/29259363 Bug: 72608560 Bug: 29259363 Bug: 148166031 Test: ./test.py --host Test: ./test/run-test --host --dev --runtime-option -verbose:deopt,plugin --prebuild --compact-dex-level fast --jit --no-relocate --create-runner --runtime-option -Xcheck:jni 1965-get-set-local-primitive-no-tables parallel_run.py Change-Id: I77349dfc6fa860b7f007dee485e9fea1d8658090
Diffstat (limited to 'test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc')
-rw-r--r--test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc97
1 files changed, 97 insertions, 0 deletions
diff --git a/test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc b/test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc
new file mode 100644
index 0000000000..68e58f4143
--- /dev/null
+++ b/test/2011-stack-walk-concurrent-instrument/stack_walk_concurrent.cc
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <atomic>
+#include <string_view>
+
+#include "arch/context.h"
+#include "art_method-inl.h"
+#include "jni.h"
+#include "scoped_thread_state_change.h"
+#include "stack.h"
+#include "thread.h"
+
+namespace art {
+namespace StackWalkConcurrentInstrument {
+
+std::atomic<bool> instrument_waiting = false;
+std::atomic<bool> instrumented = false;
+
+// Spin lock.
+static void WaitForInstrument() REQUIRES_SHARED(Locks::mutator_lock_) {
+ ScopedThreadSuspension sts(Thread::Current(), ThreadState::kWaitingForDeoptimization);
+ instrument_waiting = true;
+ while (!instrumented) {
+ }
+}
+
+class SelfStackWalkVisitor : public StackVisitor {
+ public:
+ explicit SelfStackWalkVisitor(Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_)
+ : StackVisitor(thread, Context::Create(), StackWalkKind::kIncludeInlinedFrames) {}
+
+ bool VisitFrame() override REQUIRES_SHARED(Locks::mutator_lock_) {
+ if (GetMethod()->GetNameView() == "$noinline$f") {
+ CHECK(!found_f_);
+ found_f_ = true;
+ } else if (GetMethod()->GetNameView() == "$noinline$g") {
+ CHECK(!found_g_);
+ found_g_ = true;
+ WaitForInstrument();
+ } else if (GetMethod()->GetNameView() == "$noinline$h") {
+ CHECK(!found_h_);
+ found_h_ = true;
+ }
+ return true;
+ }
+
+ bool found_f_ = false;
+ bool found_g_ = false;
+ bool found_h_ = false;
+};
+
+extern "C" JNIEXPORT void JNICALL Java_Main_resetTest(JNIEnv*, jobject) {
+ instrument_waiting = false;
+ instrumented = false;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_doSelfStackWalk(JNIEnv*, jobject) {
+ ScopedObjectAccess soa(Thread::Current());
+ SelfStackWalkVisitor sswv(Thread::Current());
+ sswv.WalkStack();
+ CHECK(sswv.found_f_);
+ CHECK(sswv.found_g_);
+ CHECK(sswv.found_h_);
+}
+extern "C" JNIEXPORT void JNICALL Java_Main_waitAndDeopt(JNIEnv*, jobject, jobject target) {
+ while (!instrument_waiting) {
+ }
+ bool timed_out = false;
+ Thread* other = Runtime::Current()->GetThreadList()->SuspendThreadByPeer(
+ target, true, SuspendReason::kInternal, &timed_out);
+ CHECK(!timed_out);
+ CHECK(other != nullptr);
+ ScopedObjectAccess soa(Thread::Current());
+ Runtime::Current()->GetInstrumentation()->InstrumentThreadStack(other);
+ MutexLock mu(Thread::Current(), *Locks::thread_suspend_count_lock_);
+ bool updated = other->ModifySuspendCount(Thread::Current(), -1, nullptr, SuspendReason::kInternal);
+ CHECK(updated);
+ instrumented = true;
+ return;
+}
+
+} // namespace StackWalkConcurrentInstrument
+} // namespace art