summaryrefslogtreecommitdiff
path: root/openjdkjvmti/ti_stack.cc
diff options
context:
space:
mode:
Diffstat (limited to 'openjdkjvmti/ti_stack.cc')
-rw-r--r--openjdkjvmti/ti_stack.cc217
1 files changed, 143 insertions, 74 deletions
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index b6969afff1..385ac45488 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -36,6 +36,7 @@
#include <unordered_map>
#include <vector>
+#include "arch/context.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "art_jvmti.h"
@@ -57,6 +58,7 @@
#include "nativehelper/scoped_local_ref.h"
#include "scoped_thread_state_change-inl.h"
#include "stack.h"
+#include "ti_logging.h"
#include "ti_thread.h"
#include "thread-current-inl.h"
#include "thread_list.h"
@@ -77,9 +79,9 @@ struct GetStackTraceVisitor : public art::StackVisitor {
start(start_),
stop(stop_) {}
GetStackTraceVisitor(const GetStackTraceVisitor&) = default;
- GetStackTraceVisitor(GetStackTraceVisitor&&) = default;
+ GetStackTraceVisitor(GetStackTraceVisitor&&) noexcept = default;
- bool VisitFrame() REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ bool VisitFrame() override REQUIRES_SHARED(art::Locks::mutator_lock_) {
art::ArtMethod* m = GetMethod();
if (m->IsRuntimeMethod()) {
return true;
@@ -112,6 +114,23 @@ struct GetStackTraceVisitor : public art::StackVisitor {
size_t stop;
};
+art::ShadowFrame* FindFrameAtDepthVisitor::GetOrCreateShadowFrame(bool* created_frame) {
+ art::ShadowFrame* cur = GetCurrentShadowFrame();
+ if (cur == nullptr) {
+ *created_frame = true;
+ art::ArtMethod* method = GetMethod();
+ const uint16_t num_regs = method->DexInstructionData().RegistersSize();
+ cur = GetThread()->FindOrCreateDebuggerShadowFrame(GetFrameId(),
+ num_regs,
+ method,
+ GetDexPc());
+ DCHECK(cur != nullptr);
+ } else {
+ *created_frame = false;
+ }
+ return cur;
+}
+
template <typename FrameFn>
GetStackTraceVisitor<FrameFn> MakeStackTraceVisitor(art::Thread* thread_in,
size_t start,
@@ -133,7 +152,7 @@ struct GetStackTraceVectorClosure : public art::Closure {
frames.push_back(info);
};
auto visitor = MakeStackTraceVisitor(self, start_input, stop_input, frames_fn);
- visitor.WalkStack(/* include_transitions */ false);
+ visitor.WalkStack(/* include_transitions= */ false);
start_result = visitor.start;
stop_result = visitor.stop;
@@ -201,7 +220,7 @@ struct GetStackTraceDirectClosure : public art::Closure {
++index;
};
auto visitor = MakeStackTraceVisitor(self, start_input, stop_input, frames_fn);
- visitor.WalkStack(/* include_transitions */ false);
+ visitor.WalkStack(/* include_transitions= */ false);
}
jvmtiFrameInfo* frame_buffer;
@@ -313,7 +332,7 @@ struct GetAllStackTracesVectorClosure : public art::Closure {
thread_frames->push_back(info);
};
auto visitor = MakeStackTraceVisitor(thread, 0u, stop_input, frames_fn);
- visitor.WalkStack(/* include_transitions */ false);
+ visitor.WalkStack(/* include_transitions= */ false);
}
art::Barrier barrier;
@@ -655,34 +674,24 @@ jvmtiError StackUtil::GetThreadListStackTraces(jvmtiEnv* env,
return ERR(NONE);
}
-// Walks up the stack counting Java frames. This is not StackVisitor::ComputeNumFrames, as
-// runtime methods and transitions must not be counted.
-struct GetFrameCountVisitor : public art::StackVisitor {
- explicit GetFrameCountVisitor(art::Thread* thread)
- : art::StackVisitor(thread, nullptr, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
- count(0) {}
-
- bool VisitFrame() REQUIRES_SHARED(art::Locks::mutator_lock_) {
- art::ArtMethod* m = GetMethod();
- const bool do_count = !(m == nullptr || m->IsRuntimeMethod());
- if (do_count) {
- count++;
- }
- return true;
- }
-
- size_t count;
-};
-
struct GetFrameCountClosure : public art::Closure {
public:
GetFrameCountClosure() : count(0) {}
void Run(art::Thread* self) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
- GetFrameCountVisitor visitor(self);
- visitor.WalkStack(false);
-
- count = visitor.count;
+ // This is not StackVisitor::ComputeNumFrames, as runtime methods and transitions must not be
+ // counted.
+ art::StackVisitor::WalkStack(
+ [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::ArtMethod* m = stack_visitor->GetMethod();
+ if (m != nullptr && !m->IsRuntimeMethod()) {
+ count++;
+ }
+ return true;
+ },
+ self,
+ /* context= */ nullptr,
+ art::StackVisitor::StackWalkKind::kIncludeInlinedFrames);
}
size_t count;
@@ -725,46 +734,30 @@ jvmtiError StackUtil::GetFrameCount(jvmtiEnv* env ATTRIBUTE_UNUSED,
return ERR(NONE);
}
-// Walks up the stack 'n' callers, when used with Thread::WalkStack.
-struct GetLocationVisitor : public art::StackVisitor {
- GetLocationVisitor(art::Thread* thread, size_t n_in)
- : art::StackVisitor(thread, nullptr, art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
- n(n_in),
- count(0),
- caller(nullptr),
- caller_dex_pc(0) {}
-
- bool VisitFrame() REQUIRES_SHARED(art::Locks::mutator_lock_) {
- art::ArtMethod* m = GetMethod();
- const bool do_count = !(m == nullptr || m->IsRuntimeMethod());
- if (do_count) {
- DCHECK(caller == nullptr);
- if (count == n) {
- caller = m;
- caller_dex_pc = GetDexPc(false);
- return false;
- }
- count++;
- }
- return true;
- }
-
- const size_t n;
- size_t count;
- art::ArtMethod* caller;
- uint32_t caller_dex_pc;
-};
-
struct GetLocationClosure : public art::Closure {
public:
explicit GetLocationClosure(size_t n_in) : n(n_in), method(nullptr), dex_pc(0) {}
void Run(art::Thread* self) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
- GetLocationVisitor visitor(self, n);
- visitor.WalkStack(false);
-
- method = visitor.caller;
- dex_pc = visitor.caller_dex_pc;
+ // Walks up the stack 'n' callers.
+ size_t count = 0u;
+ art::StackVisitor::WalkStack(
+ [&](const art::StackVisitor* stack_visitor) REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ art::ArtMethod* m = stack_visitor->GetMethod();
+ if (m != nullptr && !m->IsRuntimeMethod()) {
+ DCHECK(method == nullptr);
+ if (count == n) {
+ method = m;
+ dex_pc = stack_visitor->GetDexPc(/*abort_on_failure=*/false);
+ return false;
+ }
+ count++;
+ }
+ return true;
+ },
+ self,
+ /* context= */ nullptr,
+ art::StackVisitor::StackWalkKind::kIncludeInlinedFrames);
}
const size_t n;
@@ -893,7 +886,7 @@ struct MonitorInfoClosure : public art::Closure {
art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
// Find the monitors on the stack.
MonitorVisitor visitor(target);
- visitor.WalkStack(/* include_transitions */ false);
+ visitor.WalkStack(/* include_transitions= */ false);
// Find any other monitors, including ones acquired in native code.
art::RootInfo root_info(art::kRootVMInternal);
target->GetJniEnv()->VisitMonitorRoots(&visitor, root_info);
@@ -1065,16 +1058,7 @@ jvmtiError StackUtil::NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth)
// From here we are sure to succeed.
bool needs_instrument = false;
// Get/create a shadow frame
- art::ShadowFrame* shadow_frame = visitor.GetCurrentShadowFrame();
- if (shadow_frame == nullptr) {
- needs_instrument = true;
- const size_t frame_id = visitor.GetFrameId();
- const uint16_t num_regs = method->DexInstructionData().RegistersSize();
- shadow_frame = target->FindOrCreateDebuggerShadowFrame(frame_id,
- num_regs,
- method,
- visitor.GetDexPc());
- }
+ art::ShadowFrame* shadow_frame = visitor.GetOrCreateShadowFrame(&needs_instrument);
{
art::WriterMutexLock lk(self, tienv->event_info_mutex_);
// Mark shadow frame as needs_notify_pop_
@@ -1089,4 +1073,89 @@ jvmtiError StackUtil::NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth)
} while (true);
}
+jvmtiError StackUtil::PopFrame(jvmtiEnv* env, jthread thread) {
+ art::Thread* self = art::Thread::Current();
+ art::Thread* target;
+ do {
+ ThreadUtil::SuspendCheck(self);
+ art::MutexLock ucsl_mu(self, *art::Locks::user_code_suspension_lock_);
+ // Make sure we won't be suspended in the middle of holding the thread_suspend_count_lock_ by a
+ // user-code suspension. We retry and do another SuspendCheck to clear this.
+ if (ThreadUtil::WouldSuspendForUserCodeLocked(self)) {
+ continue;
+ }
+ // From now on we know we cannot get suspended by user-code.
+ // NB This does a SuspendCheck (during thread state change) so we need to make sure we don't
+ // have the 'suspend_lock' locked here.
+ art::ScopedObjectAccess soa(self);
+ art::MutexLock tll_mu(self, *art::Locks::thread_list_lock_);
+ jvmtiError err = ERR(INTERNAL);
+ if (!ThreadUtil::GetAliveNativeThread(thread, soa, &target, &err)) {
+ return err;
+ }
+ {
+ art::MutexLock tscl_mu(self, *art::Locks::thread_suspend_count_lock_);
+ if (target == self || target->GetUserCodeSuspendCount() == 0) {
+ // We cannot be the current thread for this function.
+ return ERR(THREAD_NOT_SUSPENDED);
+ }
+ }
+ JvmtiGlobalTLSData* tls_data = ThreadUtil::GetGlobalTLSData(target);
+ constexpr art::StackVisitor::StackWalkKind kWalkKind =
+ art::StackVisitor::StackWalkKind::kIncludeInlinedFrames;
+ if (tls_data != nullptr &&
+ tls_data->disable_pop_frame_depth != JvmtiGlobalTLSData::kNoDisallowedPopFrame &&
+ tls_data->disable_pop_frame_depth == art::StackVisitor::ComputeNumFrames(target,
+ kWalkKind)) {
+ JVMTI_LOG(WARNING, env) << "Disallowing frame pop due to in-progress class-load/prepare. "
+ << "Frame at depth " << tls_data->disable_pop_frame_depth << " was "
+ << "marked as un-poppable by the jvmti plugin. See b/117615146 for "
+ << "more information.";
+ return ERR(OPAQUE_FRAME);
+ }
+ // We hold the user_code_suspension_lock_ so the target thread is staying suspended until we are
+ // done.
+ std::unique_ptr<art::Context> context(art::Context::Create());
+ FindFrameAtDepthVisitor final_frame(target, context.get(), 0);
+ FindFrameAtDepthVisitor penultimate_frame(target, context.get(), 1);
+ final_frame.WalkStack();
+ penultimate_frame.WalkStack();
+
+ if (!final_frame.FoundFrame() || !penultimate_frame.FoundFrame()) {
+ // Cannot do it if there is only one frame!
+ return ERR(NO_MORE_FRAMES);
+ }
+
+ art::ArtMethod* called_method = final_frame.GetMethod();
+ art::ArtMethod* calling_method = penultimate_frame.GetMethod();
+ if (calling_method->IsNative() || called_method->IsNative()) {
+ return ERR(OPAQUE_FRAME);
+ }
+ // From here we are sure to succeed.
+
+ // Get/create a shadow frame
+ bool created_final_frame = false;
+ bool created_penultimate_frame = false;
+ art::ShadowFrame* called_shadow_frame =
+ final_frame.GetOrCreateShadowFrame(&created_final_frame);
+ art::ShadowFrame* calling_shadow_frame =
+ penultimate_frame.GetOrCreateShadowFrame(&created_penultimate_frame);
+
+ CHECK_NE(called_shadow_frame, calling_shadow_frame)
+ << "Frames at different depths not different!";
+
+ // Tell the shadow-frame to return immediately and skip all exit events.
+ called_shadow_frame->SetForcePopFrame(true);
+ calling_shadow_frame->SetForceRetryInstruction(true);
+
+ // Make sure can we will go to the interpreter and use the shadow frames. The early return for
+ // the final frame will force everything to the interpreter so we only need to instrument if it
+ // was not present.
+ if (created_final_frame) {
+ DeoptManager::Get()->DeoptimizeThread(target);
+ }
+ return OK;
+ } while (true);
+}
+
} // namespace openjdkjvmti