Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Android/art/art/openjdkjvmti/   (Android Betriebssystem Version 17©)  Datei vom 26.5.2026 mit Größe 55 kB image not shown  

Quelle  ti_stack.cc

  Sprache: C
 

/* Copyright (C) 2016 The Android Open Source Project
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This file implements interfaces from the file jvmti.h. This implementation
 * is licensed under the same terms as the file jvmti.h.  The
 * copyright and license information for the file jvmti.h follows.
 *
 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */


#include "ti_stack.h"

#include <algorithm>
#include <initializer_list>
#include <list>
#include <unordered_map>
#include <vector>

#include "android-base/macros.h"
#include "android-base/thread_annotations.h"
#include "arch/context.h"
#include "art_field-inl.h"
#include "art_jvmti.h"
#include "art_method-inl.h"
#include "barrier.h"
#include "base/bit_utils.h"
#include "base/locks.h"
#include "base/macros.h"
#include "base/mutex.h"
#include "base/pointer_size.h"
#include "deopt_manager.h"
#include "dex/code_item_accessors-inl.h"
#include "dex/dex_file.h"
#include "dex/dex_file_annotations.h"
#include "dex/dex_file_types.h"
#include "dex/dex_instruction-inl.h"
#include "dex/primitive.h"
#include "events.h"
#include "gc_root.h"
#include "handle_scope-inl.h"
#include "instrumentation.h"
#include "interpreter/shadow_frame-inl.h"
#include "interpreter/shadow_frame.h"
#include "jni/jni_env_ext.h"
#include "jni/jni_internal.h"
#include "jvalue-inl.h"
#include "jvalue.h"
#include "jvmti.h"
#include "mirror/class.h"
#include "mirror/dex_cache.h"
#include "nativehelper/scoped_local_ref.h"
#include "scoped_thread_state_change-inl.h"
#include "scoped_thread_state_change.h"
#include "stack.h"
#include "thread-current-inl.h"
#include "thread.h"
#include "thread_list.h"
#include "thread_pool.h"
#include "thread_state.h"
#include "ti_logging.h"
#include "ti_thread.h"
#include "well_known_classes-inl.h"

namespace openjdkjvmti {

template <typename FrameFn>
struct GetStackTraceVisitor : public art::StackVisitor {
  GetStackTraceVisitor(art::Thread* thread_in,
                       size_t start_,
                       size_t stop_,
                       FrameFn fn_)
      : StackVisitor(thread_in, nullptr, StackVisitor::StackWalkKind::kIncludeInlinedFrames),
        fn(fn_),
        start(start_),
        stop(stop_) {}
  GetStackTraceVisitor(const GetStackTraceVisitor&) = default;
  GetStackTraceVisitor(GetStackTraceVisitor&&) noexcept = default;

  bool VisitFrame() override REQUIRES_SHARED(art::Locks::mutator_lock_) {
    art::ArtMethod* m = GetMethod();
    if (m->IsRuntimeMethod()) {
      return true;
    }

    if (start == 0) {
      m = m->GetInterfaceMethodIfProxy(art::kRuntimePointerSize);
      jmethodID id = art::jni::EncodeArtMethod(m);

      uint32_t dex_pc = GetDexPc(false);
      jlong dex_location = (dex_pc == art::dex::kDexNoIndex) ? -1 : static_cast<jlong>(dex_pc);

      jvmtiFrameInfo info = { id, dex_location };
      fn(info);

      if (stop == 1) {
        return false;  // We're done.
      } else if (stop > 0) {
        stop--;
      }
    } else {
      start--;
    }

    return true;
  }

  FrameFn fn;
  size_t start;
  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,
                                                    size_t stop,
                                                    FrameFn fn) {
  return GetStackTraceVisitor<FrameFn>(thread_in, start, stop, fn);
}

struct GetStackTraceVectorClosure : public art::Closure {
 public:
  GetStackTraceVectorClosure(size_t start, size_t stop)
      : start_input(start),
        stop_input(stop),
        start_result(0),
        stop_result(0) {}

  void Run(art::Thread* self) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
    auto frames_fn = [&](jvmtiFrameInfo info) {
      frames.push_back(info);
    };
    auto visitor = MakeStackTraceVisitor(self, start_input, stop_input, frames_fn);
    visitor.WalkStack(/* include_transitions= */ false);

    start_result = visitor.start;
    stop_result = visitor.stop;
  }

  const size_t start_input;
  const size_t stop_input;

  std::vector<jvmtiFrameInfo> frames;
  size_t start_result;
  size_t stop_result;
};

static jvmtiError TranslateFrameVector(const std::vector<jvmtiFrameInfo>& frames,
                                       jint start_depth,
                                       size_t start_result,
                                       jint max_frame_count,
                                       jvmtiFrameInfo* frame_buffer,
                                       jint* count_ptr) {
  size_t collected_frames = frames.size();

  // Assume we're here having collected something.
  DCHECK_GT(max_frame_count, 0);

  // Frames from the top.
  if (start_depth >= 0) {
    if (start_result != 0) {
      // Not enough frames.
      return ERR(ILLEGAL_ARGUMENT);
    }
    DCHECK_LE(collected_frames, static_cast<size_t>(max_frame_count));
    if (frames.size() > 0) {
      memcpy(frame_buffer, frames.data(), collected_frames * sizeof(jvmtiFrameInfo));
    }
    *count_ptr = static_cast<jint>(frames.size());
    return ERR(NONE);
  }

  // Frames from the bottom.
  if (collected_frames < static_cast<size_t>(-start_depth)) {
    return ERR(ILLEGAL_ARGUMENT);
  }

  size_t count = std::min(static_cast<size_t>(-start_depth), static_cast<size_t>(max_frame_count));
  memcpy(frame_buffer,
         &frames.data()[collected_frames + start_depth],
         count * sizeof(jvmtiFrameInfo));
  *count_ptr = static_cast<jint>(count);
  return ERR(NONE);
}

struct GetStackTraceDirectClosure : public art::Closure {
 public:
  GetStackTraceDirectClosure(jvmtiFrameInfo* frame_buffer_, size_t start, size_t stop)
      : frame_buffer(frame_buffer_),
        start_input(start),
        stop_input(stop),
        index(0) {
    DCHECK_GE(start_input, 0u);
  }

  void Run(art::Thread* self) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
    auto frames_fn = [&](jvmtiFrameInfo info) {
      frame_buffer[index] = info;
      ++index;
    };
    auto visitor = MakeStackTraceVisitor(self, start_input, stop_input, frames_fn);
    visitor.WalkStack(/* include_transitions= */ false);
  }

  jvmtiFrameInfo* frame_buffer;

  const size_t start_input;
  const size_t stop_input;

  size_t index = 0;
};

jvmtiError StackUtil::GetStackTrace(jvmtiEnv* jvmti_env,
                                    jthread java_thread,
                                    jint start_depth,
                                    jint max_frame_count,
                                    jvmtiFrameInfo* frame_buffer,
                                    jint* count_ptr) {
  // It is not great that we have to hold these locks for so long, but it is necessary to ensure
  // that the thread isn't dying on us.
  art::ScopedObjectAccess soa(art::Thread::Current());
  art::Locks::thread_list_lock_->ExclusiveLock(soa.Self());

  art::Thread* thread;
  jvmtiError thread_error = ERR(INTERNAL);
  if (!ThreadUtil::GetAliveNativeThread(java_thread, soa, &thread, &thread_error)) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(soa.Self());
    return thread_error;
  }
  DCHECK(thread != nullptr);

  art::ThreadState state = thread->GetState();
  if (state == art::ThreadState::kStarting || thread->IsStillStarting()) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(soa.Self());
    return ERR(THREAD_NOT_ALIVE);
  }

  if (max_frame_count < 0) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(soa.Self());
    return ERR(ILLEGAL_ARGUMENT);
  }
  if (frame_buffer == nullptr || count_ptr == nullptr) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(soa.Self());
    return ERR(NULL_POINTER);
  }

  if (max_frame_count == 0) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(soa.Self());
    *count_ptr = 0;
    return ERR(NONE);
  }

  if (start_depth >= 0) {
    // Fast path: Regular order of stack trace. Fill into the frame_buffer directly.
    GetStackTraceDirectClosure closure(frame_buffer,
                                       static_cast<size_t>(start_depth),
                                       static_cast<size_t>(max_frame_count));
    // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution.
    if (!thread->RequestSynchronousCheckpoint(&closure)) {
      return ERR(THREAD_NOT_ALIVE);
    }
    *count_ptr = static_cast<jint>(closure.index);
    if (closure.index == 0) {
      JVMTI_LOG(INFO, jvmti_env) << "The stack is not large enough for a start_depth of "
                                 << start_depth << ".";
      return ERR(ILLEGAL_ARGUMENT);
    }
    return ERR(NONE);
  } else {
    GetStackTraceVectorClosure closure(00);
    // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution.
    if (!thread->RequestSynchronousCheckpoint(&closure)) {
      return ERR(THREAD_NOT_ALIVE);
    }

    return TranslateFrameVector(closure.frames,
                                start_depth,
                                closure.start_result,
                                max_frame_count,
                                frame_buffer,
                                count_ptr);
  }
}

template <typename Data>
struct GetAllStackTracesVectorClosure : public art::Closure {
  GetAllStackTracesVectorClosure(size_t stop, Data* data_)
      : barrier(0), stop_input(stop), data(data_) {}

  void Run(art::Thread* thread) override
      REQUIRES_SHARED(art::Locks::mutator_lock_)
      REQUIRES(!data->mutex) {
    art::Thread* self = art::Thread::Current();
    Work(thread, self);
    barrier.Pass(self);
  }

  void Work(art::Thread* thread, art::Thread* self)
      REQUIRES_SHARED(art::Locks::mutator_lock_)
      REQUIRES(!data->mutex) {
    // Skip threads that are still starting.
    if (thread->IsStillStarting()) {
      return;
    }

    std::vector<jvmtiFrameInfo>* thread_frames = data->GetFrameStorageFor(self, thread);
    if (thread_frames == nullptr) {
      return;
    }

    // Now collect the data.
    auto frames_fn = [&](jvmtiFrameInfo info) {
      thread_frames->push_back(info);
    };
    auto visitor = MakeStackTraceVisitor(thread, 0u, stop_input, frames_fn);
    visitor.WalkStack(/* include_transitions= */ false);
  }

  art::Barrier barrier;
  const size_t stop_input;
  Data* data;
};

template <typename Data>
static void RunCheckpointAndWait(Data* data, size_t max_frame_count)
    REQUIRES_SHARED(art::Locks::mutator_lock_) {
  // Note: requires the mutator lock as the checkpoint requires the mutator lock.
  GetAllStackTracesVectorClosure<Data> closure(max_frame_count, data);
  // TODO(b/253671779): Replace this use of RunCheckpointUnchecked() with RunCheckpoint(). This is
  // currently not possible, since the following undesirable call chain (abbreviated here) is then
  // possible and exercised by current tests: (jvmti) GetAllStackTraces -> <this function> ->
  // RunCheckpoint -> GetStackTraceVisitor -> EncodeMethodId -> Class::EnsureMethodIds ->
  // Class::Alloc -> AllocObjectWithAllocator -> potentially suspends, or runs GC, etc. -> CHECK
  // failure.
  size_t barrier_count = art::Runtime::Current()->GetThreadList()->RunCheckpointUnchecked(&closure);
  if (barrier_count == 0) {
    return;
  }
  art::Thread* self = art::Thread::Current();
  art::ScopedThreadStateChange tsc(self, art::ThreadState::kWaitingForCheckPointsToRun);
  closure.barrier.Increment(self, barrier_count);
}

jvmtiError StackUtil::GetAllStackTraces(jvmtiEnv* env,
                                        jint max_frame_count,
                                        jvmtiStackInfo** stack_info_ptr,
                                        jint* thread_count_ptr) {
  if (max_frame_count < 0) {
    return ERR(ILLEGAL_ARGUMENT);
  }
  if (stack_info_ptr == nullptr || thread_count_ptr == nullptr) {
    return ERR(NULL_POINTER);
  }

  struct AllStackTracesData {
    AllStackTracesData() : mutex("GetAllStackTraces", art::LockLevel::kAbortLock) {}
    ~AllStackTracesData() {
      JNIEnv* jni_env = art::Thread::Current()->GetJniEnv();
      for (jthread global_thread_ref : thread_peers) {
        jni_env->DeleteGlobalRef(global_thread_ref);
      }
    }

    std::vector<jvmtiFrameInfo>* GetFrameStorageFor(art::Thread* self, art::Thread* thread)
        REQUIRES_SHARED(art::Locks::mutator_lock_)
        REQUIRES(!mutex) {
      art::MutexLock mu(self, mutex);

      threads.push_back(thread);

      jthread peer = art::Runtime::Current()->GetJavaVM()->AddGlobalRef(
          self, thread->GetPeerFromOtherThread());
      thread_peers.push_back(peer);

      frames.emplace_back(new std::vector<jvmtiFrameInfo>());
      return frames.back().get();
    }

    art::Mutex mutex;

    // Storage. Only access directly after completion.

    std::vector<art::Thread*> threads;
    // "thread_peers" contains global references to their peers.
    std::vector<jthread> thread_peers;

    std::vector<std::unique_ptr<std::vector<jvmtiFrameInfo>>> frames;
  };

  AllStackTracesData data;
  art::Thread* current = art::Thread::Current();
  {
    art::ScopedObjectAccess soa(current);
    RunCheckpointAndWait(&data, static_cast<size_t>(max_frame_count));
  }

  // Convert the data into our output format.

  // Note: we use an array of jvmtiStackInfo for convenience. The spec says we need to
  //       allocate one big chunk for this and the actual frames, which means we need
  //       to either be conservative or rearrange things later (the latter is implemented).
  std::unique_ptr<jvmtiStackInfo[]> stack_info_array(new jvmtiStackInfo[data.frames.size()]);
  std::vector<std::unique_ptr<jvmtiFrameInfo[]>> frame_infos;
  frame_infos.reserve(data.frames.size());

  // Now run through and add data for each thread.
  size_t sum_frames = 0;
  for (size_t index = 0; index < data.frames.size(); ++index) {
    jvmtiStackInfo& stack_info = stack_info_array.get()[index];
    memset(&stack_info, 0sizeof(jvmtiStackInfo));

    const std::vector<jvmtiFrameInfo>& thread_frames = *data.frames[index].get();

    // For the time being, set the thread to null. We'll fix it up in the second stage.
    stack_info.thread = nullptr;
    stack_info.state = JVMTI_THREAD_STATE_SUSPENDED;

    size_t collected_frames = thread_frames.size();
    if (max_frame_count == 0 || collected_frames == 0) {
      stack_info.frame_count = 0;
      stack_info.frame_buffer = nullptr;
      continue;
    }
    DCHECK_LE(collected_frames, static_cast<size_t>(max_frame_count));

    jvmtiFrameInfo* frame_info = new jvmtiFrameInfo[collected_frames];
    frame_infos.emplace_back(frame_info);

    jint count;
    jvmtiError translate_result = TranslateFrameVector(thread_frames,
                                                       0,
                                                       0,
                                                       static_cast<jint>(collected_frames),
                                                       frame_info,
                                                       &count);
    DCHECK(translate_result == JVMTI_ERROR_NONE);
    stack_info.frame_count = static_cast<jint>(collected_frames);
    stack_info.frame_buffer = frame_info;
    sum_frames += static_cast<size_t>(count);
  }

  // No errors, yet. Now put it all into an output buffer.
  size_t rounded_stack_info_size = art::RoundUp(sizeof(jvmtiStackInfo) * data.frames.size(),
                                                alignof(jvmtiFrameInfo));
  size_t chunk_size = rounded_stack_info_size + sum_frames * sizeof(jvmtiFrameInfo);
  unsigned char* chunk_data;
  jvmtiError alloc_result = env->Allocate(chunk_size, &chunk_data);
  if (alloc_result != ERR(NONE)) {
    return alloc_result;
  }

  jvmtiStackInfo* stack_info = reinterpret_cast<jvmtiStackInfo*>(chunk_data);
  // First copy in all the basic data.
  memcpy(stack_info, stack_info_array.get(), sizeof(jvmtiStackInfo) * data.frames.size());

  // Now copy the frames and fix up the pointers.
  jvmtiFrameInfo* frame_info = reinterpret_cast<jvmtiFrameInfo*>(
      chunk_data + rounded_stack_info_size);
  for (size_t i = 0; i < data.frames.size(); ++i) {
    jvmtiStackInfo& old_stack_info = stack_info_array.get()[i];
    jvmtiStackInfo& new_stack_info = stack_info[i];

    // Translate the global ref into a local ref.
    new_stack_info.thread =
        static_cast<JNIEnv*>(current->GetJniEnv())->NewLocalRef(data.thread_peers[i]);

    if (old_stack_info.frame_count > 0) {
      // Only copy when there's data - leave the nullptr alone.
      size_t frames_size = static_cast<size_t>(old_stack_info.frame_count) * sizeof(jvmtiFrameInfo);
      memcpy(frame_info, old_stack_info.frame_buffer, frames_size);
      new_stack_info.frame_buffer = frame_info;
      frame_info += old_stack_info.frame_count;
    }
  }

  *stack_info_ptr = stack_info;
  *thread_count_ptr = static_cast<jint>(data.frames.size());

  return ERR(NONE);
}

jvmtiError StackUtil::GetThreadListStackTraces(jvmtiEnv* env,
                                               jint thread_count,
                                               const jthread* thread_list,
                                               jint max_frame_count,
                                               jvmtiStackInfo** stack_info_ptr) {
  if (max_frame_count < 0) {
    return ERR(ILLEGAL_ARGUMENT);
  }
  if (thread_count < 0) {
    return ERR(ILLEGAL_ARGUMENT);
  }
  if (thread_count == 0) {
    *stack_info_ptr = nullptr;
    return ERR(NONE);
  }
  if (thread_list == nullptr || stack_info_ptr == nullptr) {
    return ERR(NULL_POINTER);
  }

  art::Thread* current = art::Thread::Current();
  art::ScopedObjectAccess soa(current);      // Now we know we have the shared lock.

  struct SelectStackTracesData {
    SelectStackTracesData() : mutex("GetSelectStackTraces", art::LockLevel::kAbortLock) {}

    std::vector<jvmtiFrameInfo>* GetFrameStorageFor(art::Thread* self, art::Thread* thread)
              REQUIRES_SHARED(art::Locks::mutator_lock_)
              REQUIRES(!mutex) {
      art::ObjPtr<art::mirror::Object> peer = thread->GetPeerFromOtherThread();
      for (size_t index = 0; index != handles.size(); ++index) {
        if (peer == handles[index].Get()) {
          // Found the thread.
          art::MutexLock mu(self, mutex);

          thread_list_indices.push_back(index);

          frames.emplace_back(new std::vector<jvmtiFrameInfo>());
          return frames.back().get();
        }
      }
      return nullptr;
    }

    art::Mutex mutex;

    // Selection data.

    std::vector<art::Handle<art::mirror::Object>> handles;

    // Storage. Only access directly after completion.

    std::vector<size_t> thread_list_indices;

    std::vector<std::unique_ptr<std::vector<jvmtiFrameInfo>>> frames;
  };

  SelectStackTracesData data;

  // Decode all threads to raw pointers. Put them into a handle scope to avoid any moving GC bugs.
  art::VariableSizedHandleScope hs(current);
  for (jint i = 0; i != thread_count; ++i) {
    if (thread_list[i] == nullptr) {
      return ERR(INVALID_THREAD);
    }
    art::ObjPtr<art::mirror::Object> thread = soa.Decode<art::mirror::Object>(thread_list[i]);
    if (!thread->InstanceOf(art::WellKnownClasses::java_lang_Thread.Get())) {
      return ERR(INVALID_THREAD);
    }
    data.handles.push_back(hs.NewHandle(thread));
  }

  RunCheckpointAndWait(&data, static_cast<size_t>(max_frame_count));

  // Convert the data into our output format.

  // Note: we use an array of jvmtiStackInfo for convenience. The spec says we need to
  //       allocate one big chunk for this and the actual frames, which means we need
  //       to either be conservative or rearrange things later (the latter is implemented).
  std::unique_ptr<jvmtiStackInfo[]> stack_info_array(new jvmtiStackInfo[data.frames.size()]);
  std::vector<std::unique_ptr<jvmtiFrameInfo[]>> frame_infos;
  frame_infos.reserve(data.frames.size());

  // Now run through and add data for each thread.
  size_t sum_frames = 0;
  for (size_t index = 0; index < data.frames.size(); ++index) {
    jvmtiStackInfo& stack_info = stack_info_array.get()[index];
    memset(&stack_info, 0sizeof(jvmtiStackInfo));

    const std::vector<jvmtiFrameInfo>& thread_frames = *data.frames[index].get();

    // For the time being, set the thread to null. We don't have good ScopedLocalRef
    // infrastructure.
    stack_info.thread = nullptr;
    stack_info.state = JVMTI_THREAD_STATE_SUSPENDED;

    size_t collected_frames = thread_frames.size();
    if (max_frame_count == 0 || collected_frames == 0) {
      stack_info.frame_count = 0;
      stack_info.frame_buffer = nullptr;
      continue;
    }
    DCHECK_LE(collected_frames, static_cast<size_t>(max_frame_count));

    jvmtiFrameInfo* frame_info = new jvmtiFrameInfo[collected_frames];
    frame_infos.emplace_back(frame_info);

    jint count;
    jvmtiError translate_result = TranslateFrameVector(thread_frames,
                                                       0,
                                                       0,
                                                       static_cast<jint>(collected_frames),
                                                       frame_info,
                                                       &count);
    DCHECK(translate_result == JVMTI_ERROR_NONE);
    stack_info.frame_count = static_cast<jint>(collected_frames);
    stack_info.frame_buffer = frame_info;
    sum_frames += static_cast<size_t>(count);
  }

  // No errors, yet. Now put it all into an output buffer. Note that this is not frames.size(),
  // potentially.
  size_t rounded_stack_info_size = art::RoundUp(sizeof(jvmtiStackInfo) * thread_count,
                                                alignof(jvmtiFrameInfo));
  size_t chunk_size = rounded_stack_info_size + sum_frames * sizeof(jvmtiFrameInfo);
  unsigned char* chunk_data;
  jvmtiError alloc_result = env->Allocate(chunk_size, &chunk_data);
  if (alloc_result != ERR(NONE)) {
    return alloc_result;
  }

  jvmtiStackInfo* stack_info = reinterpret_cast<jvmtiStackInfo*>(chunk_data);
  jvmtiFrameInfo* frame_info = reinterpret_cast<jvmtiFrameInfo*>(
      chunk_data + rounded_stack_info_size);

  for (size_t i = 0; i < static_cast<size_t>(thread_count); ++i) {
    // Check whether we found a running thread for this.
    // Note: For simplicity, and with the expectation that the list is usually small, use a simple
    //       search. (The list is *not* sorted!)
    auto it = std::find(data.thread_list_indices.begin(), data.thread_list_indices.end(), i);
    if (it == data.thread_list_indices.end()) {
      // No native thread. Must be new or dead. We need to fill out the stack info now.
      // (Need to read the Java "started" field to know whether this is starting or terminated.)
      art::ObjPtr<art::mirror::Object> peer = soa.Decode<art::mirror::Object>(thread_list[i]);
      art::ObjPtr<art::mirror::Class> klass = peer->GetClass();
      art::ArtField* started_field = klass->FindDeclaredInstanceField("started""Z");
      CHECK(started_field != nullptr);
      bool started = started_field->GetBoolean(peer) != 0;
      constexpr jint kStartedState = JVMTI_JAVA_LANG_THREAD_STATE_NEW;
      constexpr jint kTerminatedState = JVMTI_THREAD_STATE_TERMINATED |
          JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED;
      stack_info[i].thread = reinterpret_cast<JNIEnv*>(soa.Env())->NewLocalRef(thread_list[i]);
      stack_info[i].state = started ? kTerminatedState : kStartedState;
      stack_info[i].frame_count = 0;
      stack_info[i].frame_buffer = nullptr;
    } else {
      // Had a native thread and frames.
      size_t f_index = it - data.thread_list_indices.begin();

      jvmtiStackInfo& old_stack_info = stack_info_array.get()[f_index];
      jvmtiStackInfo& new_stack_info = stack_info[i];

      memcpy(&new_stack_info, &old_stack_info, sizeof(jvmtiStackInfo));
      new_stack_info.thread = reinterpret_cast<JNIEnv*>(soa.Env())->NewLocalRef(thread_list[i]);
      if (old_stack_info.frame_count > 0) {
        // Only copy when there's data - leave the nullptr alone.
        size_t frames_size =
            static_cast<size_t>(old_stack_info.frame_count) * sizeof(jvmtiFrameInfo);
        memcpy(frame_info, old_stack_info.frame_buffer, frames_size);
        new_stack_info.frame_buffer = frame_info;
        frame_info += old_stack_info.frame_count;
      }
    }
  }

  *stack_info_ptr = stack_info;

  return ERR(NONE);
}

struct GetFrameCountClosure : public art::Closure {
 public:
  GetFrameCountClosure() : count(0) {}

  void Run(art::Thread* self) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
    // 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;
};

jvmtiError StackUtil::GetFrameCount([[maybe_unused]] jvmtiEnv* env,
                                    jthread java_thread,
                                    jint* count_ptr) {
  // It is not great that we have to hold these locks for so long, but it is necessary to ensure
  // that the thread isn't dying on us.
  art::ScopedObjectAccess soa(art::Thread::Current());
  art::Locks::thread_list_lock_->ExclusiveLock(soa.Self());

  art::Thread* thread;
  jvmtiError thread_error = ERR(INTERNAL);
  if (!ThreadUtil::GetAliveNativeThread(java_thread, soa, &thread, &thread_error)) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(soa.Self());
    return thread_error;
  }

  DCHECK(thread != nullptr);
  art::ThreadState state = thread->GetState();
  if (state == art::ThreadState::kStarting || thread->IsStillStarting()) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(soa.Self());
    return ERR(THREAD_NOT_ALIVE);
  }

  if (count_ptr == nullptr) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(soa.Self());
    return ERR(NULL_POINTER);
  }

  GetFrameCountClosure closure;
  // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution.
  if (!thread->RequestSynchronousCheckpoint(&closure)) {
    return ERR(THREAD_NOT_ALIVE);
  }

  *count_ptr = closure.count;
  return ERR(NONE);
}

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_) {
    // 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;
  art::ArtMethod* method;
  uint32_t dex_pc;
};

jvmtiError StackUtil::GetFrameLocation([[maybe_unused]] jvmtiEnv* env,
                                       jthread java_thread,
                                       jint depth,
                                       jmethodID* method_ptr,
                                       jlocation* location_ptr) {
  // It is not great that we have to hold these locks for so long, but it is necessary to ensure
  // that the thread isn't dying on us.
  art::ScopedObjectAccess soa(art::Thread::Current());
  art::Locks::thread_list_lock_->ExclusiveLock(soa.Self());

  art::Thread* thread;
  jvmtiError thread_error = ERR(INTERNAL);
  if (!ThreadUtil::GetAliveNativeThread(java_thread, soa, &thread, &thread_error)) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(soa.Self());
    return thread_error;
  }
  DCHECK(thread != nullptr);

  art::ThreadState state = thread->GetState();
  if (state == art::ThreadState::kStarting || thread->IsStillStarting()) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(soa.Self());
    return ERR(THREAD_NOT_ALIVE);
  }

  if (depth < 0) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(soa.Self());
    return ERR(ILLEGAL_ARGUMENT);
  }
  if (method_ptr == nullptr || location_ptr == nullptr) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(soa.Self());
    return ERR(NULL_POINTER);
  }

  GetLocationClosure closure(static_cast<size_t>(depth));
  // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution.
  if (!thread->RequestSynchronousCheckpoint(&closure)) {
    return ERR(THREAD_NOT_ALIVE);
  }

  if (closure.method == nullptr) {
    return ERR(NO_MORE_FRAMES);
  }

  *method_ptr = art::jni::EncodeArtMethod(closure.method);
  if (closure.method->IsNative() || closure.method->IsProxyMethod()) {
    *location_ptr = -1;
  } else {
    if (closure.dex_pc == art::dex::kDexNoIndex) {
      return ERR(INTERNAL);
    }
    *location_ptr = static_cast<jlocation>(closure.dex_pc);
  }

  return ERR(NONE);
}

struct MonitorVisitor : public art::StackVisitor, public art::SingleRootVisitor {
  // We need a context because VisitLocks needs it retrieve the monitor objects.
  explicit MonitorVisitor(art::Thread* thread)
      REQUIRES_SHARED(art::Locks::mutator_lock_)
      : art::StackVisitor(thread,
                          art::Context::Create(),
                          art::StackVisitor::StackWalkKind::kIncludeInlinedFrames),
        hs(art::Thread::Current()),
        current_stack_depth(0) {}

  ~MonitorVisitor() {
    delete context_;
  }

  bool VisitFrame() override REQUIRES_SHARED(art::Locks::mutator_lock_) {
    art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
    if (!GetMethod()->IsRuntimeMethod()) {
      art::Monitor::VisitLocks(this, AppendOwnedMonitors, this);
      ++current_stack_depth;
    }
    return true;
  }

  static void AppendOwnedMonitors(art::ObjPtr<art::mirror::Object> owned_monitor, void* arg)
      REQUIRES_SHARED(art::Locks::mutator_lock_) {
    art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
    MonitorVisitor* visitor = reinterpret_cast<MonitorVisitor*>(arg);
    // Filter out duplicates.
    for (const art::Handle<art::mirror::Object>& monitor : visitor->monitors) {
      if (monitor.Get() == owned_monitor) {
        return;
      }
    }
    visitor->monitors.push_back(visitor->hs.NewHandle(owned_monitor));
    visitor->stack_depths.push_back(visitor->current_stack_depth);
  }

  void VisitRoot(art::mirror::Object* obj, [[maybe_unused]] const art::RootInfo& info) override
      REQUIRES_SHARED(art::Locks::mutator_lock_) {
    for (const art::Handle<art::mirror::Object>& m : monitors) {
      if (m.Get() == obj) {
        return;
      }
    }
    monitors.push_back(hs.NewHandle(obj));
    stack_depths.push_back(-1);
  }

  art::VariableSizedHandleScope hs;
  jint current_stack_depth;
  std::vector<art::Handle<art::mirror::Object>> monitors;
  std::vector<jint> stack_depths;
};

template<typename Fn>
struct MonitorInfoClosure : public art::Closure {
 public:
  explicit MonitorInfoClosure(Fn handle_results)
      : err_(OK), handle_results_(handle_results) {}

  void Run(art::Thread* target) override REQUIRES_SHARED(art::Locks::mutator_lock_) {
    art::Locks::mutator_lock_->AssertSharedHeld(art::Thread::Current());
    // Find the monitors on the stack.
    MonitorVisitor visitor(target);
    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);
    err_ = handle_results_(visitor);
  }

  jvmtiError GetError() {
    return err_;
  }

 private:
  jvmtiError err_;
  Fn handle_results_;
};


template <typename Fn>
static jvmtiError GetOwnedMonitorInfoCommon(const art::ScopedObjectAccessAlreadyRunnable& soa,
                                            jthread thread,
                                            Fn handle_results)
    REQUIRES_SHARED(art::Locks::mutator_lock_) {
  art::Thread* self = art::Thread::Current();
  MonitorInfoClosure<Fn> closure(handle_results);
  bool called_method = false;
  {
    art::Locks::thread_list_lock_->ExclusiveLock(self);
    art::Thread* target = nullptr;
    jvmtiError err = ERR(INTERNAL);
    if (!ThreadUtil::GetAliveNativeThread(thread, soa, &target, &err)) {
      art::Locks::thread_list_lock_->ExclusiveUnlock(self);
      return err;
    }
    if (target != self) {
      called_method = true;
      // RequestSynchronousCheckpoint releases the thread_list_lock_ as a part of its execution.
      // Since this deals with object references we need to avoid going to sleep.
      art::ScopedAssertNoThreadSuspension sants("Getting owned monitor usage");
      if (!target->RequestSynchronousCheckpoint(&closure, art::ThreadState::kRunnable)) {
        return ERR(THREAD_NOT_ALIVE);
      }
    } else {
      art::Locks::thread_list_lock_->ExclusiveUnlock(self);
    }
  }
  // Cannot call the closure on the current thread if we have thread_list_lock since we need to call
  // into the verifier which can cause the current thread to suspend for gc. Suspending would be a
  // bad thing to do if we hold the ThreadListLock. For other threads since we are running it on a
  // checkpoint we are fine but if the thread is the current one we need to drop the mutex first.
  if (!called_method) {
    closure.Run(self);
  }
  return closure.GetError();
}

jvmtiError StackUtil::GetOwnedMonitorStackDepthInfo(jvmtiEnv* env,
                                                    jthread thread,
                                                    jint* info_cnt,
                                                    jvmtiMonitorStackDepthInfo** info_ptr) {
  if (info_cnt == nullptr || info_ptr == nullptr) {
    return ERR(NULL_POINTER);
  }
  art::ScopedObjectAccess soa(art::Thread::Current());
  std::vector<art::GcRoot<art::mirror::Object>> mons;
  std::vector<uint32_t> depths;
  auto handle_fun = [&] (MonitorVisitor& visitor) REQUIRES_SHARED(art::Locks::mutator_lock_) {
    for (size_t i = 0; i < visitor.monitors.size(); i++) {
      mons.push_back(art::GcRoot<art::mirror::Object>(visitor.monitors[i].Get()));
      depths.push_back(visitor.stack_depths[i]);
    }
    return OK;
  };
  jvmtiError err = GetOwnedMonitorInfoCommon(soa, thread, handle_fun);
  if (err != OK) {
    return err;
  }
  auto nbytes = sizeof(jvmtiMonitorStackDepthInfo) * mons.size();
  err = env->Allocate(nbytes, reinterpret_cast<unsigned char**>(info_ptr));
  if (err != OK) {
    return err;
  }
  *info_cnt = mons.size();
  for (uint32_t i = 0; i < mons.size(); i++) {
    (*info_ptr)[i] = {
      soa.AddLocalReference<jobject>(mons[i].Read()),
      static_cast<jint>(depths[i])
    };
  }
  return err;
}

jvmtiError StackUtil::GetOwnedMonitorInfo(jvmtiEnv* env,
                                          jthread thread,
                                          jint* owned_monitor_count_ptr,
                                          jobject** owned_monitors_ptr) {
  if (owned_monitor_count_ptr == nullptr || owned_monitors_ptr == nullptr) {
    return ERR(NULL_POINTER);
  }
  art::ScopedObjectAccess soa(art::Thread::Current());
  std::vector<art::GcRoot<art::mirror::Object>> mons;
  auto handle_fun = [&] (MonitorVisitor& visitor) REQUIRES_SHARED(art::Locks::mutator_lock_) {
    for (size_t i = 0; i < visitor.monitors.size(); i++) {
      mons.push_back(art::GcRoot<art::mirror::Object>(visitor.monitors[i].Get()));
    }
    return OK;
  };
  jvmtiError err = GetOwnedMonitorInfoCommon(soa, thread, handle_fun);
  if (err != OK) {
    return err;
  }
  auto nbytes = sizeof(jobject) * mons.size();
  err = env->Allocate(nbytes, reinterpret_cast<unsigned char**>(owned_monitors_ptr));
  if (err != OK) {
    return err;
  }
  *owned_monitor_count_ptr = mons.size();
  for (uint32_t i = 0; i < mons.size(); i++) {
    (*owned_monitors_ptr)[i] = soa.AddLocalReference<jobject>(mons[i].Read());
  }
  return err;
}

jvmtiError StackUtil::NotifyFramePop(jvmtiEnv* env, jthread thread, jint depth) {
  if (depth < 0) {
    return ERR(ILLEGAL_ARGUMENT);
  }
  ArtJvmTiEnv* tienv = ArtJvmTiEnv::AsArtJvmTiEnv(env);
  art::Thread* self = art::Thread::Current();
  art::Thread* target;

  ScopedNoUserCodeSuspension snucs(self);
  // 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::Locks::thread_list_lock_->ExclusiveLock(self);
  jvmtiError err = ERR(INTERNAL);
  if (!ThreadUtil::GetAliveNativeThread(thread, soa, &target, &err)) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(self);
    return err;
  }
  if (target != self) {
    // TODO This is part of the spec but we could easily avoid needing to do it.
    // We would just put all the logic into a sync-checkpoint.
    art::Locks::thread_suspend_count_lock_->ExclusiveLock(self);
    if (target->GetUserCodeSuspendCount() == 0) {
      art::Locks::thread_suspend_count_lock_->ExclusiveUnlock(self);
      art::Locks::thread_list_lock_->ExclusiveUnlock(self);
      return ERR(THREAD_NOT_SUSPENDED);
    }
    art::Locks::thread_suspend_count_lock_->ExclusiveUnlock(self);
  }
  // We hold the user_code_suspension_lock_ so the target thread is staying
  // suspended until we are done (unless it's 'self' in which case we don't care
  // since we aren't going to be returning).
  // TODO We could implement this using a synchronous checkpoint and not bother
  // with any of the suspension stuff. The spec does specifically say to return
  // THREAD_NOT_SUSPENDED though. Find the requested stack frame.
  std::unique_ptr<art::Context> context(art::Context::Create());
  FindFrameAtDepthVisitor visitor(target, context.get(), depth);
  visitor.WalkStack();
  if (!visitor.FoundFrame()) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(self);
    return ERR(NO_MORE_FRAMES);
  }
  art::ArtMethod* method = visitor.GetMethod();
  if (method->IsNative()) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(self);
    return ERR(OPAQUE_FRAME);
  }
  // From here we are sure to succeed.
  bool needs_instrument = false;
  // Get/create a shadow frame
  art::ShadowFrame* shadow_frame =
      visitor.GetOrCreateShadowFrame(&needs_instrument);
  {
    art::WriterMutexLock lk(self, tienv->event_info_mutex_);
    if (LIKELY(!shadow_frame->NeedsNotifyPop())) {
      // Ensure we won't miss exceptions being thrown if we get jit-compiled. We
      // only do this for the first NotifyPopFrame.
      target->IncrementForceInterpreterCount();

      // Mark shadow frame as needs_notify_pop_
      shadow_frame->SetNotifyPop(true);
    }
    tienv->notify_frames.insert(shadow_frame);
  }
  // Make sure can we will go to the interpreter and use the shadow frames.
  if (needs_instrument) {
    art::FunctionClosure fc([](art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_) {
      DeoptManager::Get()->DeoptimizeThread(self);
    });
    target->RequestSynchronousCheckpoint(&fc);
  } else {
    art::Locks::thread_list_lock_->ExclusiveUnlock(self);
  }
  return OK;
}

namespace {

enum class NonStandardExitType {
  kPopFrame,
  kForceReturn,
};

template<NonStandardExitType kExitType>
class NonStandardExitFrames {
 public:
  NonStandardExitFrames(art::ScopedObjectAccess& soa, jvmtiEnv* env, jthread thread)
      REQUIRES(art::Locks::user_code_suspension_lock_)
      REQUIRES_SHARED(art::Locks::mutator_lock_)
      REQUIRES(!art::Locks::thread_suspend_count_lock_)
      ACQUIRE(art::Locks::thread_list_lock_) {
    art::Thread* self = soa.Self();
    art::Locks::user_code_suspension_lock_->AssertExclusiveHeld(self);

    art::Locks::thread_list_lock_->ExclusiveLock(self);

    if (!ThreadUtil::GetAliveNativeThread(thread, soa, &target_, &result_)) {
      return;
    }
    {
      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.
        result_ = ERR(THREAD_NOT_SUSPENDED);
        return;
      }
    }
    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.";
      result_ = ERR(OPAQUE_FRAME);
      return;
    }
    // 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!
      JVMTI_LOG(INFO, env) << "Can not pop final frame off of a stack";
      result_ = ERR(NO_MORE_FRAMES);
      return;
    }

    art::ArtMethod* called_method = final_frame.GetMethod();
    art::ArtMethod* calling_method = penultimate_frame.GetMethod();
    if (!CheckFunctions(env, calling_method, called_method)) {
      return;
    }
    DCHECK(!called_method->IsNative()) << called_method->PrettyMethod();

    // From here we are sure to succeed.
    result_ = OK;

    // Get/create a shadow frame
    final_frame_ = final_frame.GetOrCreateShadowFrame(&created_final_frame_);
    penultimate_frame_ =
        (calling_method->IsNative()
             ? nullptr
             : penultimate_frame.GetOrCreateShadowFrame(&created_penultimate_frame_));

    final_frame_id_ = final_frame.GetFrameId();
    penultimate_frame_id_ = penultimate_frame.GetFrameId();

    CHECK_NE(final_frame_, penultimate_frame_) << "Frames at different depths not different!";
  }

  bool CheckFunctions(jvmtiEnv* env, art::ArtMethod* calling, art::ArtMethod* called)
      REQUIRES(art::Locks::thread_list_lock_, art::Locks::user_code_suspension_lock_)
      REQUIRES_SHARED(art::Locks::mutator_lock_);

  art::ObjPtr<art::mirror::Class> ResolveFinalFrameReturnType(art::Thread* self)
      REQUIRES(art::Locks::user_code_suspension_lock_)
      REQUIRES_SHARED(art::Locks::mutator_lock_)
      REQUIRES(art::Locks::thread_list_lock_)
      REQUIRES(!art::Locks::thread_suspend_count_lock_) {
    // Temporarily release the thread list lock to allow thread suspension and
    // resolve the final frame's return type.
    // Note: If the target thread is not `self`, holding the user code suspension
    // lock prevents the suspended target thread from resuming and therefore
    // prevents it from being unregistered, see `ThreadList::Unregister()`.
    art::Locks::user_code_suspension_lock_->AssertExclusiveHeld(self);
    art::Locks::thread_list_lock_->ExclusiveUnlock(self);
    // FIXME: What if `ArtMethod::ResolveReturnType()` throws an exception?
    art::ObjPtr<art::mirror::Class> return_type = final_frame_->GetMethod()->ResolveReturnType();
    art::Locks::thread_list_lock_->ExclusiveLock(self);
    return return_type;
  }

  ~NonStandardExitFrames()
      REQUIRES(art::Locks::user_code_suspension_lock_)
      REQUIRES_SHARED(art::Locks::mutator_lock_)
      REQUIRES(!art::Locks::thread_suspend_count_lock_)
      REQUIRES(!art::Locks::thread_list_lock_) {}

  art::ShadowFrame* final_frame_ GUARDED_BY(art::Locks::user_code_suspension_lock_) = nullptr;
  art::ShadowFrame* penultimate_frame_ GUARDED_BY(art::Locks::user_code_suspension_lock_) = nullptr;
  bool created_final_frame_ GUARDED_BY(art::Locks::user_code_suspension_lock_) = false;
  bool created_penultimate_frame_ GUARDED_BY(art::Locks::user_code_suspension_lock_) false;
  uint32_t final_frame_id_ GUARDED_BY(art::Locks::user_code_suspension_lock_) = -1;
  uint32_t penultimate_frame_id_ GUARDED_BY(art::Locks::user_code_suspension_lock_) = -1;
  art::Thread* target_ GUARDED_BY(art::Locks::thread_list_lock_) = nullptr;
  art::ThreadState old_state_ = art::ThreadState::kTerminated;
  jvmtiError result_ = ERR(INTERNAL);
};

template <>
bool NonStandardExitFrames<NonStandardExitType::kForceReturn>::CheckFunctions(
    jvmtiEnv* env, [[maybe_unused]] art::ArtMethod* calling, art::ArtMethod* called) {
  if (UNLIKELY(called->IsNative())) {
    result_ = ERR(OPAQUE_FRAME);
    JVMTI_LOG(INFO, env) << "Cannot force early return from " << called->PrettyMethod()
                         << " because it is native.";
    return false;
  } else {
    return true;
  }
}

template <>
bool NonStandardExitFrames<NonStandardExitType::kPopFrame>::CheckFunctions(
    jvmtiEnv* env, art::ArtMethod* calling, art::ArtMethod* called) {
  if (UNLIKELY(calling->IsNative() || called->IsNative())) {
    result_ = ERR(OPAQUE_FRAME);
    JVMTI_LOG(INFO, env) << "Cannot force early return from " << called->PrettyMethod() << " to "
                         << calling->PrettyMethod() << " because at least one of them is native.";
    return false;
  } else {
    return true;
  }
}

class SetupMethodExitEvents {
 public:
  SetupMethodExitEvents(art::Thread* self,
                        EventHandler* event_handler,
                        jthread target) REQUIRES(!art::Locks::mutator_lock_,
                                                 !art::Locks::user_code_suspension_lock_,
                                                 !art::Locks::thread_list_lock_)
      : self_(self), event_handler_(event_handler), target_(target) {
    DCHECK(target != nullptr);
    art::Locks::mutator_lock_->AssertNotHeld(self_);
    art::Locks::user_code_suspension_lock_->AssertNotHeld(self_);
    art::Locks::thread_list_lock_->AssertNotHeld(self_);
    event_handler_->SetInternalEvent(
        target_, ArtJvmtiEvent::kForceEarlyReturnUpdateReturnValue, JVMTI_ENABLE);
  }

  ~SetupMethodExitEvents() REQUIRES(!art::Locks::mutator_lock_,
                                    !art::Locks::user_code_suspension_lock_,
                                    !art::Locks::thread_list_lock_) {
    art::Locks::mutator_lock_->AssertNotHeld(self_);
    art::Locks::user_code_suspension_lock_->AssertNotHeld(self_);
    art::Locks::thread_list_lock_->AssertNotHeld(self_);
    if (failed_) {
      event_handler_->SetInternalEvent(
          target_, ArtJvmtiEvent::kForceEarlyReturnUpdateReturnValue, JVMTI_DISABLE);
    }
  }

  void NotifyFailure() {
    failed_ = true;
  }

 private:
  art::Thread* self_;
  EventHandler* event_handler_;
  jthread target_;
  bool failed_ = false;
};

template <typename T>
void AddDelayedMethodExitEvent(EventHandler* handler, art::ShadowFrame* frame, T value)
    REQUIRES_SHARED(art::Locks::mutator_lock_)
    REQUIRES(art::Locks::user_code_suspension_lock_, art::Locks::thread_list_lock_);

template <typename T>
void AddDelayedMethodExitEvent(EventHandler* handler, art::ShadowFrame* frame, T value) {
  art::JValue val = art::JValue::FromPrimitive(value);
  jvalue jval{ .j = val.GetJ() };
  handler->AddDelayedNonStandardExitEvent(frame, false, jval);
}

template <>
void AddDelayedMethodExitEvent<std::nullptr_t>(EventHandler* handler,
                                               art::ShadowFrame* frame,
                                               [[maybe_unused]] std::nullptr_t null_val) {
  jvalue jval;
  memset(&jval, 0sizeof(jval));
  handler->AddDelayedNonStandardExitEvent(frame, false, jval);
}

template <>
void AddDelayedMethodExitEvent<jobject>(EventHandler* handler,
                                        art::ShadowFrame* frame,
                                        jobject obj) {
  jvalue jval{ .l = art::Thread::Current()->GetJniEnv()->NewGlobalRef(obj) };
  handler->AddDelayedNonStandardExitEvent(frame, true, jval);
}

template <typename T>
bool ValidReturnType(art::Thread* self, art::ObjPtr<art::mirror::Class> return_type, T value)
    REQUIRES_SHARED(art::Locks::mutator_lock_)
        REQUIRES(art::Locks::user_code_suspension_lock_, art::Locks::thread_list_lock_);

#define SIMPLE_VALID_RETURN_TYPE(type, ...)                                                       \
  template <>                                                                                     \
  bool ValidReturnType<type>([[maybe_unused]] art::Thread * self,                                 \
                             art::ObjPtr<art::mirror::Class> return_type,                         \
                             [[maybe_unused]] type value) {                                       \
    static constexpr std::initializer_list<art::Primitive::Type> types{__VA_ARGS__};              \
    return std::find(types.begin(), types.end(), return_type->GetPrimitiveType()) != types.end(); \
  }

SIMPLE_VALID_RETURN_TYPE(jlong, art::Primitive::kPrimLong);
SIMPLE_VALID_RETURN_TYPE(jfloat, art::Primitive::kPrimFloat);
SIMPLE_VALID_RETURN_TYPE(jdouble, art::Primitive::kPrimDouble);
SIMPLE_VALID_RETURN_TYPE(nullptr_t, art::Primitive::kPrimVoid);
SIMPLE_VALID_RETURN_TYPE(jint,
                         art::Primitive::kPrimInt,
                         art::Primitive::kPrimChar,
                         art::Primitive::kPrimBoolean,
                         art::Primitive::kPrimShort,
                         art::Primitive::kPrimByte);
#undef SIMPLE_VALID_RETURN_TYPE

template <>
bool ValidReturnType<jobject>(art::Thread* self,
                              art::ObjPtr<art::mirror::Class> return_type,
                              jobject return_value) {
  if (return_type->IsPrimitive()) {
    return false;
  }
  if (return_value == nullptr) {
    // Null can be used for anything.
    return true;
  }
  return return_type->IsAssignableFrom(self->DecodeJObject(return_value)->GetClass());
}

}  // namespace

jvmtiError StackUtil::PopFrame(jvmtiEnv* env, jthread thread) {
  art::Thread* self = art::Thread::Current();
  ScopedNoUserCodeSuspension snucs(self);
  art::ScopedObjectAccess soa(self);
  NonStandardExitFrames<NonStandardExitType::kPopFrame> frames(soa, env, thread);
  if (frames.result_ != OK) {
    art::Locks::thread_list_lock_->ExclusiveUnlock(self);
    return frames.result_;
  }
  // Tell the shadow-frame to return immediately and skip all exit events.
  frames.penultimate_frame_->SetForceRetryInstruction(true);
  frames.final_frame_->SetForcePopFrame(true);
  frames.final_frame_->SetSkipMethodExitEvents(true);
  if (frames.created_final_frame_ || frames.created_penultimate_frame_) {
    art::FunctionClosure fc([](art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_){
      DeoptManager::Get()->DeoptimizeThread(self);
    });
    frames.target_->RequestSynchronousCheckpoint(&fc);
  } else {
    art::Locks::thread_list_lock_->ExclusiveUnlock(self);
  }
  return OK;
}

template <typename T>
jvmtiError
StackUtil::ForceEarlyReturn(jvmtiEnv* env, EventHandler* event_handler, jthread thread, T value) {
  art::Thread* self = art::Thread::Current();
  // We don't want to use the null == current-thread idiom since for events (that we use internally
  // to implement force-early-return) we instead have null == all threads. Instead just get the
  // current jthread if needed.
  ScopedLocalRef<jthread> cur_thread(self->GetJniEnv(), nullptr);
  if (UNLIKELY(thread == nullptr)) {
    art::ScopedObjectAccess soa(self);
    cur_thread.reset(soa.AddLocalReference<jthread>(self->GetPeer()));
    thread = cur_thread.get();
  }
  // This sets up the exit events we implement early return using before we have the locks and
  // thanks to destructor ordering will tear them down if something goes wrong.
  SetupMethodExitEvents smee(self, event_handler, thread);
  ScopedNoUserCodeSuspension snucs(self);
  art::ScopedObjectAccess soa(self);
  NonStandardExitFrames<NonStandardExitType::kForceReturn> frames(soa, env, thread);
  if (frames.result_ != OK) {
    smee.NotifyFailure();
    art::Locks::thread_list_lock_->ExclusiveUnlock(self);
    return frames.result_;
  } else if (!ValidReturnType<T>(self, frames.ResolveFinalFrameReturnType(self), value)) {
    smee.NotifyFailure();
    art::Locks::thread_list_lock_->ExclusiveUnlock(self);
    return ERR(TYPE_MISMATCH);
  } else if (frames.final_frame_->GetForcePopFrame()) {
    // TODO We should really support this.
    smee.NotifyFailure();
    std::string thread_name;
    frames.target_->GetThreadName(thread_name);
    JVMTI_LOG(WARNING, env) << "PopFrame or force-return already pending on thread " << thread_name;
    art::Locks::thread_list_lock_->ExclusiveUnlock(self);
    return ERR(OPAQUE_FRAME);
  }
  // Tell the shadow-frame to return immediately and skip all exit events.
  frames.final_frame_->SetForcePopFrame(true);
  AddDelayedMethodExitEvent<T>(event_handler, frames.final_frame_, value);
  if (frames.created_final_frame_ || frames.created_penultimate_frame_) {
    art::FunctionClosure fc([](art::Thread* self) REQUIRES_SHARED(art::Locks::mutator_lock_){
      DeoptManager::Get()->DeoptimizeThread(self);
    });
    frames.target_->RequestSynchronousCheckpoint(&fc);
  } else {
    art::Locks::thread_list_lock_->ExclusiveUnlock(self);
  }
  return OK;
}

// Instantiate the ForceEarlyReturn templates.
template jvmtiError StackUtil::ForceEarlyReturn(jvmtiEnv*, EventHandler*, jthread, jint);
template jvmtiError StackUtil::ForceEarlyReturn(jvmtiEnv*, EventHandler*, jthread, jlong);
template jvmtiError StackUtil::ForceEarlyReturn(jvmtiEnv*, EventHandler*, jthread, jfloat);
template jvmtiError StackUtil::ForceEarlyReturn(jvmtiEnv*, EventHandler*, jthread, jdouble);
template jvmtiError StackUtil::ForceEarlyReturn(jvmtiEnv*, EventHandler*, jthread, jobject);
template jvmtiError StackUtil::ForceEarlyReturn(jvmtiEnv*, EventHandler*, jthread, nullptr_t);

}  // namespace openjdkjvmti

Messung V0.5 in Prozent
C=95 H=91 G=92

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.40Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-29) ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.