| /* |
| * Copyright (C) 2014 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. |
| */ |
| |
| #ifndef ART_RUNTIME_MONITOR_OBJECTS_STACK_VISITOR_H_ |
| #define ART_RUNTIME_MONITOR_OBJECTS_STACK_VISITOR_H_ |
| |
| #include <android-base/logging.h> |
| |
| #include "art_method.h" |
| #include "base/locks.h" |
| #include "monitor.h" |
| #include "stack.h" |
| #include "thread.h" |
| #include "thread_state.h" |
| |
| namespace art { |
| |
| namespace mirror { |
| class Object; |
| } |
| |
| class Context; |
| |
| class MonitorObjectsStackVisitor : public StackVisitor { |
| public: |
| MonitorObjectsStackVisitor(Thread* thread_in, |
| Context* context, |
| bool check_suspended = true, |
| bool dump_locks_in = true) |
| REQUIRES_SHARED(Locks::mutator_lock_) |
| : StackVisitor(thread_in, |
| context, |
| StackVisitor::StackWalkKind::kIncludeInlinedFrames, |
| check_suspended), |
| frame_count(0u), |
| dump_locks(dump_locks_in) {} |
| |
| enum class VisitMethodResult { |
| kContinueMethod, |
| kSkipMethod, |
| kEndStackWalk, |
| }; |
| |
| bool VisitFrame() final REQUIRES_SHARED(Locks::mutator_lock_) { |
| ArtMethod* m = GetMethod(); |
| if (m->IsRuntimeMethod()) { |
| return true; |
| } |
| |
| VisitMethodResult vmrEntry = StartMethod(m, frame_count); |
| switch (vmrEntry) { |
| case VisitMethodResult::kContinueMethod: |
| break; |
| case VisitMethodResult::kSkipMethod: |
| return true; |
| case VisitMethodResult::kEndStackWalk: |
| return false; |
| } |
| |
| if (frame_count == 0) { |
| // Top frame, check for blocked state. |
| |
| mirror::Object* monitor_object; |
| uint32_t lock_owner_tid; |
| ThreadState state = Monitor::FetchState(GetThread(), |
| &monitor_object, |
| &lock_owner_tid); |
| switch (state) { |
| case kWaiting: |
| case kTimedWaiting: |
| VisitWaitingObject(monitor_object, state); |
| break; |
| case kSleeping: |
| VisitSleepingObject(monitor_object); |
| break; |
| |
| case kBlocked: |
| case kWaitingForLockInflation: |
| VisitBlockedOnObject(monitor_object, state, lock_owner_tid); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| if (dump_locks) { |
| // Visit locks, but do not abort on errors. This could trigger a nested abort. |
| // Skip visiting locks if dump_locks is false as it would cause a bad_mutexes_held in |
| // RegTypeCache::RegTypeCache due to thread_list_lock. |
| Monitor::VisitLocks(this, VisitLockedObject, this, false); |
| } |
| |
| ++frame_count; |
| |
| VisitMethodResult vmrExit = EndMethod(m); |
| switch (vmrExit) { |
| case VisitMethodResult::kContinueMethod: |
| case VisitMethodResult::kSkipMethod: |
| return true; |
| |
| case VisitMethodResult::kEndStackWalk: |
| return false; |
| } |
| LOG(FATAL) << "Unreachable"; |
| UNREACHABLE(); |
| } |
| |
| protected: |
| virtual VisitMethodResult StartMethod(ArtMethod* m, size_t frame_nr) |
| REQUIRES_SHARED(Locks::mutator_lock_) = 0; |
| virtual VisitMethodResult EndMethod(ArtMethod* m) |
| REQUIRES_SHARED(Locks::mutator_lock_) = 0; |
| |
| virtual void VisitWaitingObject(mirror::Object* obj, ThreadState state) |
| REQUIRES_SHARED(Locks::mutator_lock_) = 0; |
| virtual void VisitSleepingObject(mirror::Object* obj) |
| REQUIRES_SHARED(Locks::mutator_lock_) = 0; |
| virtual void VisitBlockedOnObject(mirror::Object* obj, ThreadState state, uint32_t owner_tid) |
| REQUIRES_SHARED(Locks::mutator_lock_) = 0; |
| virtual void VisitLockedObject(mirror::Object* obj) |
| REQUIRES_SHARED(Locks::mutator_lock_) = 0; |
| |
| size_t frame_count; |
| |
| private: |
| static void VisitLockedObject(mirror::Object* o, void* context) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| MonitorObjectsStackVisitor* self = reinterpret_cast<MonitorObjectsStackVisitor*>(context); |
| if (o != nullptr) { |
| if (kUseReadBarrier && Thread::Current()->GetIsGcMarking()) { |
| // We may call Thread::Dump() in the middle of the CC thread flip and this thread's stack |
| // may have not been flipped yet and "o" may be a from-space (stale) ref, in which case the |
| // IdentityHashCode call below will crash. So explicitly mark/forward it here. |
| o = ReadBarrier::Mark(o); |
| } |
| } |
| self->VisitLockedObject(o); |
| } |
| |
| const bool dump_locks; |
| }; |
| |
| } // namespace art |
| |
| #endif // ART_RUNTIME_MONITOR_OBJECTS_STACK_VISITOR_H_ |