Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  instrumentation.h

  Sprache: C
 

/*
 * Copyright (C) 2011 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_INSTRUMENTATION_H_
#define ART_RUNTIME_INSTRUMENTATION_H_

#include <stdint.h>

#include <functional>
#include <list>
#include <memory>
#include <optional>
#include <queue>
#include <unordered_set>

#include "arch/instruction_set.h"
#include "base/locks.h"
#include "base/macros.h"
#include "base/offsets.h"
#include "base/pointer_size.h"
#include "base/safe_map.h"
#include "gc_root.h"
#include "jvalue.h"

namespace art HIDDEN {
namespace mirror {
class Class;
class Object;
class Throwable;
}  // namespace mirror
class ArtField;
class ArtMethod;
class Context;
template <typename T> class Handle;
template <typename T> class MutableHandle;
struct NthCallerVisitor;
union JValue;
class OatQuickMethodHeader;
class SHARED_LOCKABLE ReaderWriterMutex;
class ShadowFrame;
class Thread;
enum class DeoptimizationMethodType;

namespace instrumentation {


// Do we want to deoptimize for method entry and exit listeners or just try to intercept
// invocations? Deoptimization forces all code to run in the interpreter and considerably hurts the
// application's performance.
static constexpr bool kDeoptimizeForAccurateMethodEntryExitListeners = true;

// an optional frame is either Some(const ShadowFrame& current_frame) or None depending on if the
// method being exited has a shadow-frame associed with the current stack frame. In cases where
// there is no shadow-frame associated with this stack frame this will be None.
using OptionalFrame = std::optional<std::reference_wrapper<const ShadowFrame>>;

// Instrumentation event listener API. Registered listeners will get the appropriate call back for
// the events they are listening for. The call backs supply the thread, method and dex_pc the event
// occurred upon. The thread may or may not be Thread::Current().
struct InstrumentationListener {
  InstrumentationListener() {}
  virtual ~InstrumentationListener() {}

  // Call-back for when a method is entered.
  virtual void MethodEntered(Thread* thread, ArtMethod* method)
      REQUIRES_SHARED(Locks::mutator_lock_) = 0;

  virtual void MethodExited(Thread* thread,
                            ArtMethod* method,
                            OptionalFrame frame,
                            MutableHandle<mirror::Object>& return_value)
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Call-back for when a method is exited. The implementor should either handler-ize the return
  // value (if appropriate) or use the alternate MethodExited callback instead if they need to
  // go through a suspend point.
  virtual void MethodExited(Thread* thread,
                            ArtMethod* method,
                            OptionalFrame frame,
                            JValue& return_value)
      REQUIRES_SHARED(Locks::mutator_lock_) = 0;

  // Call-back for when a method is popped due to an exception throw. A method will either cause a
  // MethodExited call-back or a MethodUnwind call-back when its activation is removed.
  virtual void MethodUnwind(Thread* thread,
                            ArtMethod* method,
                            uint32_t dex_pc)
      REQUIRES_SHARED(Locks::mutator_lock_) = 0;

  // Call-back for when the dex pc moves in a method.
  virtual void DexPcMoved(Thread* thread,
                          Handle<mirror::Object> this_object,
                          ArtMethod* method,
                          uint32_t new_dex_pc)
      REQUIRES_SHARED(Locks::mutator_lock_) = 0;

  // Call-back for when we read from a field.
  virtual void FieldRead(Thread* thread,
                         Handle<mirror::Object> this_object,
                         ArtMethod* method,
                         uint32_t dex_pc,
                         ArtField* field) = 0;

  virtual void FieldWritten(Thread* thread,
                            Handle<mirror::Object> this_object,
                            ArtMethod* method,
                            uint32_t dex_pc,
                            ArtField* field,
                            Handle<mirror::Object> field_value)
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Call-back for when we write into a field.
  virtual void FieldWritten(Thread* thread,
                            Handle<mirror::Object> this_object,
                            ArtMethod* method,
                            uint32_t dex_pc,
                            ArtField* field,
                            const JValue& field_value)
      REQUIRES_SHARED(Locks::mutator_lock_) = 0;

  // Call-back when an exception is thrown.
  virtual void ExceptionThrown(Thread* thread,
                               Handle<mirror::Throwable> exception_object)
      REQUIRES_SHARED(Locks::mutator_lock_) = 0;

  // Call-back when an exception is caught/handled by java code.
  virtual void ExceptionHandled(Thread* thread, Handle<mirror::Throwable> exception_object)
      REQUIRES_SHARED(Locks::mutator_lock_) = 0;

  // Call-back for when we execute a branch.
  virtual void Branch(Thread* thread,
                      ArtMethod* method,
                      uint32_t dex_pc,
                      int32_t dex_pc_offset)
      REQUIRES_SHARED(Locks::mutator_lock_) = 0;

  // Call-back when a shadow_frame with the needs_notify_pop_ boolean set is popped off the stack by
  // either return or exceptions. Normally instrumentation listeners should ensure that there are
  // shadow-frames by deoptimizing stacks.
  virtual void WatchedFramePop([[maybe_unused]] Thread* thread,
                               [[maybe_unused]] const ShadowFrame& frame)
      REQUIRES_SHARED(Locks::mutator_lock_) = 0;
};

class Instrumentation;
// A helper to send instrumentation events while popping the stack in a safe way.
class InstrumentationStackPopper {
 public:
  explicit InstrumentationStackPopper(Thread* self);
  ~InstrumentationStackPopper() REQUIRES_SHARED(Locks::mutator_lock_);

  // Increase the number of frames being popped up to `stack_pointer`. Return true if the
  // frames were popped without any exceptions, false otherwise. The exception that caused
  // the pop is 'exception'.
  bool PopFramesTo(uintptr_t stack_pointer, /*in-out*/MutableHandle<mirror::Throwable>&&nbsp;exception)
      REQUIRES_SHARED(Locks::mutator_lock_);

 private:
  Thread* self_;
  Instrumentation* instrumentation_;
  // The stack pointer limit for frames to pop.
  uintptr_t pop_until_;
};

// Instrumentation is a catch-all for when extra information is required from the runtime. The
// typical use for instrumentation is for profiling and debugging. Instrumentation may add stubs
// to method entry and exit, it may also force execution to be switched to the interpreter and
// trigger deoptimization.
class Instrumentation {
 public:
  enum InstrumentationEvent {
    kMethodEntered = 0x1,
    kMethodExited = 0x2,
    kMethodUnwind = 0x4,
    kDexPcMoved = 0x8,
    kFieldRead = 0x10,
    kFieldWritten = 0x20,
    kExceptionThrown = 0x40,
    kBranch = 0x80,
    kWatchedFramePop = 0x200,
    kExceptionHandled = 0x400,
  };

  enum class InstrumentationLevel {
    kInstrumentNothing,             // execute without instrumentation
    kInstrumentWithEntryExitHooks,  // execute with entry/exit hooks
    kInstrumentWithInterpreter      // execute with interpreter
  };

  enum class ListenerType {
    // We differentiate between trace listeners and other jvmti listeners for handling method exit
    // callbacks. Trace listeners should only receive method exit callback once per each method
    // exit. The jvmti listeners expect to be notified multiple times to implement pop frame and
    // force return features.
    kRegularListener,
    kFastTraceListener,
    kSlowTraceListener
  };

  static constexpr uint8_t kFastTraceListeners = 0b01;
  static constexpr uint8_t kSlowMethodEntryExitListeners = 0b10;
  static constexpr uint8_t kSlowTraceListeners = 0b100;

  Instrumentation();

  static constexpr MemberOffset RunExitHooksOffset() {
    // Assert that run_entry_exit_hooks_ is 8bits wide. If the size changes
    // update the compare instructions in the code generator when generating checks for
    // MethodEntryExitHooks.
    static_assert(sizeof(run_exit_hooks_) == 1"run_exit_hooks_ isn't expected size");
    return MemberOffset(OFFSETOF_MEMBER(Instrumentation, run_exit_hooks_));
  }

  static constexpr MemberOffset HaveMethodEntryListenersOffset() {
    // Assert that have_method_entry_listeners_ is 8bits wide. If the size changes
    // update the compare instructions in the code generator when generating checks for
    // MethodEntryExitHooks.
    static_assert(sizeof(have_method_entry_listeners_) == 1,
                  "have_method_entry_listeners_ isn't expected size");
    return MemberOffset(OFFSETOF_MEMBER(Instrumentation, have_method_entry_listeners_));
  }

  static constexpr MemberOffset HaveMethodExitListenersOffset() {
    // Assert that have_method_exit_slow_listeners_ is 8bits wide. If the size changes
    // update the compare instructions in the code generator when generating checks for
    // MethodEntryExitHooks.
    static_assert(sizeof(have_method_exit_listeners_) == 1,
                  "have_method_exit_listeners_ isn't expected size");
    return MemberOffset(OFFSETOF_MEMBER(Instrumentation, have_method_exit_listeners_));
  }

  static void ReportMethodEntryForOnStackMethods(InstrumentationListener* listener, Thread* thread)
      REQUIRES(Locks::mutator_lock_);

  // Add a listener to be notified of the masked together sent of instrumentation events. This
  // suspend the runtime to install stubs. You are expected to hold the mutator lock as a proxy
  // for saying you should have suspended all threads (installing stubs while threads are running
  // will break).
  EXPORT void AddListener(InstrumentationListener* listener,
                          uint32_t events,
                          ListenerType listener_type = ListenerType::kRegularListener)
      REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_);

  // Removes listeners for the specified events.
  EXPORT void RemoveListener(InstrumentationListener* listener,
                             uint32_t events,
                             ListenerType listener_type = ListenerType::kRegularListener)
      REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_);

  // Calls UndeoptimizeEverything which may visit class linker classes through ConfigureStubs.
  // try_switch_to_non_debuggable specifies if we can switch the runtime back to non-debuggable.
  // When a debugger is attached to a non-debuggable app, we switch the runtime to debuggable and
  // when we are detaching the debugger we move back to non-debuggable. If we are disabling
  // deoptimization for other reasons (ex: removing the last breakpoint) while the debugger is still
  // connected, we pass false to stay in debuggable. Switching runtimes is expensive so we only want
  // to switch when we know debug features aren't needed anymore.
  EXPORT void DisableDeoptimization(const char* key, bool try_switch_to_non_debuggable)
      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_);

  // Enables entry exit hooks support. This is called in preparation for debug requests that require
  // calling method entry / exit hooks.
  EXPORT void EnableEntryExitHooks(const char* key)
      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_);

  bool AreAllMethodsDeoptimized() const {
    return InterpreterStubsInstalled();
  }
  bool ShouldNotifyMethodEnterExitEvents() const REQUIRES_SHARED(Locks::mutator_lock_);

  // Executes everything with interpreter.
  EXPORT void DeoptimizeEverything(const char* key)
      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
          REQUIRES(!Locks::thread_list_lock_, !Locks::classlinker_classes_lock_);

  // Executes everything with compiled code (or interpreter if there is no code). May visit class
  // linker classes through ConfigureStubs.
  EXPORT void UndeoptimizeEverything(const char* key)
      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
          REQUIRES(!Locks::thread_list_lock_, !Locks::classlinker_classes_lock_);

  // Deoptimize a method by forcing its execution with the interpreter. Nevertheless, a static
  // method (except a class initializer) set to the resolution trampoline will be deoptimized only
  // once its declaring class is initialized.
  EXPORT void Deoptimize(ArtMethod* method)
      REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_);

  // Undeoptimze the method by restoring its entrypoints. Nevertheless, a static method
  // (except a class initializer) set to the resolution trampoline will be updated only once its
  // declaring class is initialized.
  EXPORT void Undeoptimize(ArtMethod* method)
      REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_);

  // Indicates whether the method has been deoptimized so it is executed with the interpreter.
  EXPORT bool IsDeoptimized(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);

  // Indicates if any method needs to be deoptimized. This is used to avoid walking the stack to
  // determine if a deoptimization is required.
  bool IsDeoptimizedMethodsEmpty() const REQUIRES_SHARED(Locks::mutator_lock_);

  // Enable method tracing by installing instrumentation entry/exit stubs or interpreter.
  EXPORT void EnableMethodTracing(
      const char* key,
      InstrumentationListener* listener,
      bool needs_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners)
      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
          REQUIRES(!Locks::thread_list_lock_, !Locks::classlinker_classes_lock_);

  // Disable method tracing by uninstalling instrumentation entry/exit stubs or interpreter.
  EXPORT void DisableMethodTracing(const char* key)
      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
          REQUIRES(!Locks::thread_list_lock_, !Locks::classlinker_classes_lock_);

  void InstrumentQuickAllocEntryPoints() REQUIRES(!Locks::instrument_entrypoints_lock_);
  void UninstrumentQuickAllocEntryPoints() REQUIRES(!Locks::instrument_entrypoints_lock_);
  void InstrumentQuickAllocEntryPointsLocked()
      REQUIRES(Locks::instrument_entrypoints_lock_, !Locks::thread_list_lock_,
               !Locks::runtime_shutdown_lock_);
  void UninstrumentQuickAllocEntryPointsLocked()
      REQUIRES(Locks::instrument_entrypoints_lock_, !Locks::thread_list_lock_,
               !Locks::runtime_shutdown_lock_);
  void ResetQuickAllocEntryPoints() REQUIRES(Locks::runtime_shutdown_lock_);

  // Returns a string representation of the given entry point.
  static std::string EntryPointString(const void* code);

  // Return the best initial entrypoint of a method, assuming that stubs are not in use.
  // This function can be called while the thread is suspended.
  const void* GetInitialEntrypoint(uint32_t method_access_flags, const void* aot_code);

  // Check if the best initial entrypoint needs to be overridden with stubs.
  bool InitialEntrypointNeedsInstrumentationStubs()
      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Roles::uninterruptible_);

  // Initialize the method's entrypoint with aot code or runtime stub.
  // The caller must check and apply `InitialEntrypointNeedsInstrumentationStubs()`
  // in the same `Roles::uninterruptible_` section of code.
  void InitializeMethodsCode(ArtMethod* method, const void* entrypoint, PointerSize pointer_size)
      REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(Roles::uninterruptible_);

  // Reinitialize the entrypoint of the method.
  EXPORT void ReinitializeMethodsCode(ArtMethod* method)
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Update the code of a method respecting any installed stubs.
  void UpdateMethodsCode(ArtMethod* method, const void* new_code)
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Update the code of a native method to a JITed stub.
  void UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const void* new_code)
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Return the code that we can execute for an invoke including from the JIT.
  EXPORT const void* GetCodeForInvoke(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);

  // Return the code that we can execute considering the current instrumentation level.
  // If interpreter stubs are installed return interpreter bridge. If the entry exit stubs
  // are installed return an instrumentation entry point. Otherwise, return the code that
  // can be executed including from the JIT.
  const void* GetMaybeInstrumentedCodeForInvoke(ArtMethod* method)
      REQUIRES_SHARED(Locks::mutator_lock_);

  void ForceInterpretOnly() {
    forced_interpret_only_ = true;
  }

  bool EntryExitStubsInstalled() const {
    return instrumentation_level_ == InstrumentationLevel::kInstrumentWithEntryExitHooks ||
           instrumentation_level_ == InstrumentationLevel::kInstrumentWithInterpreter;
  }

  bool InterpreterStubsInstalled() const {
    return instrumentation_level_ == InstrumentationLevel::kInstrumentWithInterpreter;
  }

  // Called by ArtMethod::Invoke to determine dispatch mechanism.
  bool InterpretOnly() const {
    return forced_interpret_only_ || InterpreterStubsInstalled();
  }
  bool InterpretOnly(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);

  bool IsForcedInterpretOnly() const {
    return forced_interpret_only_;
  }

  bool RunExitHooks() const {
    return run_exit_hooks_;
  }

  bool HasMethodEntryListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return have_method_entry_listeners_ != 0;
  }

  bool HasMethodExitListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return have_method_exit_listeners_ != 0;
  }

  bool HasFastMethodEntryListenersOnly() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return have_method_entry_listeners_ == kFastTraceListeners;
  }

  bool HasFastMethodExitListenersOnly() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return have_method_exit_listeners_ == kFastTraceListeners;
  }

  bool HasMethodUnwindListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return have_method_unwind_listeners_;
  }

  bool HasDexPcListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return have_dex_pc_listeners_;
  }

  bool HasFieldReadListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return have_field_read_listeners_;
  }

  bool HasFieldWriteListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return have_field_write_listeners_;
  }

  bool HasExceptionThrownListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return have_exception_thrown_listeners_;
  }

  bool HasBranchListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return have_branch_listeners_;
  }

  bool HasWatchedFramePopListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return have_watched_frame_pop_listeners_;
  }

  bool HasExceptionHandledListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return have_exception_handled_listeners_;
  }

  // Returns if dex pc events need to be reported for the specified method.
  // These events are reported when DexPCListeners are installed and at least one of the
  // following conditions hold:
  // 1. The method is deoptimized. This is done when there is a breakpoint on method.
  // 2. When the thread is deoptimized. This is used when single stepping a single thread.
  // 3. When interpreter stubs are installed. In this case no additional information is maintained
  //    about which methods need dex pc move events. This is usually used for features which need
  //    them for several methods across threads or need expensive processing. So it is OK to not
  //    further optimize this case.
  // DexPCListeners are installed when there is a breakpoint on any method / single stepping
  // on any of thread. These are removed when the last breakpoint was removed. See AddListener and
  // RemoveListener for more details.
  bool NeedsDexPcEvents(ArtMethod* method, Thread* thread) REQUIRES_SHARED(Locks::mutator_lock_);

  bool NeedsSlowInterpreterForListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return have_field_read_listeners_ ||
           have_field_write_listeners_ ||
           have_watched_frame_pop_listeners_ ||
           have_exception_handled_listeners_;
  }

  // Inform listeners that a method has been entered. A dex PC is provided as we may install
  // listeners into executing code and get method enter events for methods already on the stack.
  void MethodEnterEvent(Thread* thread, ArtMethod* method) const
      REQUIRES_SHARED(Locks::mutator_lock_) {
    if (UNLIKELY(HasMethodEntryListeners())) {
      MethodEnterEventImpl(thread, method);
    }
  }

  // Inform listeners that a method has been exited.
  template <typename T>
  void MethodExitEvent(Thread* thread,
                       ArtMethod* method,
                       OptionalFrame frame,
                       T& return_value,
                       bool skip_trace_listeners = falseconst
      REQUIRES_SHARED(Locks::mutator_lock_) {
    if (UNLIKELY(HasMethodExitListeners())) {
      MethodExitEventImpl(thread, method, frame, return_value, skip_trace_listeners);
    }
  }

  // Inform listeners that a method has been exited due to an exception.
  void MethodUnwindEvent(Thread* thread,
                         ArtMethod* method,
                         uint32_t dex_pc) const
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Inform listeners that the dex pc has moved (only supported by the interpreter).
  void DexPcMovedEvent(Thread* thread,
                       ObjPtr<mirror::Object> this_object,
                       ArtMethod* method,
                       uint32_t dex_pc) const
      REQUIRES_SHARED(Locks::mutator_lock_) {
    if (UNLIKELY(HasDexPcListeners())) {
      DexPcMovedEventImpl(thread, this_object, method, dex_pc);
    }
  }

  // Inform listeners that a branch has been taken (only supported by the interpreter).
  void Branch(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t offset) const
      REQUIRES_SHARED(Locks::mutator_lock_) {
    if (UNLIKELY(HasBranchListeners())) {
      BranchImpl(thread, method, dex_pc, offset);
    }
  }

  // Inform listeners that we read a field (only supported by the interpreter).
  void FieldReadEvent(Thread* thread,
                      ObjPtr<mirror::Object> this_object,
                      ArtMethod* method,
                      uint32_t dex_pc,
                      ArtField* field) const
      REQUIRES_SHARED(Locks::mutator_lock_) {
    if (UNLIKELY(HasFieldReadListeners())) {
      FieldReadEventImpl(thread, this_object, method, dex_pc, field);
    }
  }

  // Inform listeners that we write a field (only supported by the interpreter).
  void FieldWriteEvent(Thread* thread,
                       ObjPtr<mirror::Object> this_object,
                       ArtMethod* method,
                       uint32_t dex_pc,
                       ArtField* field,
                       const JValue& field_value) const
      REQUIRES_SHARED(Locks::mutator_lock_) {
    if (UNLIKELY(HasFieldWriteListeners())) {
      FieldWriteEventImpl(thread, this_object, method, dex_pc, field, field_value);
    }
  }

  // Inform listeners that a branch has been taken (only supported by the interpreter).
  void WatchedFramePopped(Thread* thread, const ShadowFrame& frame) const
      REQUIRES_SHARED(Locks::mutator_lock_) {
    if (UNLIKELY(HasWatchedFramePopListeners())) {
      WatchedFramePopImpl(thread, frame);
    }
  }

  // Inform listeners that an exception was thrown.
  void ExceptionThrownEvent(Thread* thread, ObjPtr<mirror::Throwable> exception_object) const
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Inform listeners that an exception has been handled. This is not sent for native code or for
  // exceptions which reach the end of the thread's stack.
  void ExceptionHandledEvent(Thread* thread, ObjPtr<mirror::Throwable> exception_object) const
      REQUIRES_SHARED(Locks::mutator_lock_);

  JValue GetReturnValue(ArtMethod* method, bool* is_ref, uint64_t* gpr_result, uint64_t* fpr_result)
      REQUIRES_SHARED(Locks::mutator_lock_);
  bool PushDeoptContextIfNeeded(Thread* self,
                                DeoptimizationMethodType deopt_type,
                                bool is_ref,
                                const JValue& result) REQUIRES_SHARED(Locks::mutator_lock_);

  // Deoptimize upon pending exception or if the caller requires it. Returns a long jump context if
  // a deoptimization is needed and taken.
  std::unique_ptr<Context> DeoptimizeIfNeeded(Thread* self,
                                              ArtMethod** sp,
                                              DeoptimizationMethodType type,
                                              JValue result,
                                              bool is_ref) REQUIRES_SHARED(Locks::mutator_lock_);
  // This returns if the caller of runtime method requires a deoptimization. This checks both if the
  // method requires a deopt or if this particular frame needs a deopt because of a class
  // redefinition.
  bool ShouldDeoptimizeCaller(Thread* self, ArtMethod** sp) REQUIRES_SHARED(Locks::mutator_lock_);
  bool ShouldDeoptimizeCaller(Thread* self, ArtMethod** sp, size_t frame_size)
      REQUIRES_SHARED(Locks::mutator_lock_);
  // This returns if the specified method requires a deoptimization. This doesn't account if a stack
  // frame involving this method requires a deoptimization.
  bool NeedsSlowInterpreterForMethod(Thread* self, ArtMethod* method)
      REQUIRES_SHARED(Locks::mutator_lock_);

  DeoptimizationMethodType GetDeoptimizationMethodType(ArtMethod* method)
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Call back for configure stubs.
  void InstallStubsForClass(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_);

  void InstallStubsForMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);

  EXPORT void UpdateEntrypointsForDebuggable() REQUIRES(art::Locks::mutator_lock_);

  // Install instrumentation exit stub on every method of the stack of the given thread.
  // This is used by:
  //  - the debugger to cause a deoptimization of the all frames in thread's stack (for
  //    example, after updating local variables)
  //  - to call method entry / exit hooks for tracing. For this we instrument
  //    the stack frame to run entry / exit hooks but we don't need to deoptimize.
  // force_deopt indicates whether the frames need to deoptimize or not.
  EXPORT void InstrumentThreadStack(Thread* thread, bool force_deopt)
      REQUIRES(Locks::mutator_lock_);
  void InstrumentAllThreadStacks(bool force_deopt) REQUIRES(Locks::mutator_lock_)
      REQUIRES(!Locks::thread_list_lock_);

  // Force all currently running frames to be deoptimized back to interpreter. This should only be
  // used in cases where basically all compiled code has been invalidated.
  EXPORT void DeoptimizeAllThreadFrames() REQUIRES(art::Locks::mutator_lock_);

  static size_t ComputeFrameId(Thread* self,
                               size_t frame_depth,
                               size_t inlined_frames_before_frame)
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Does not hold lock, used to check if someone changed from not instrumented to instrumented
  // during a GC suspend point.
  bool AllocEntrypointsInstrumented() const REQUIRES_SHARED(Locks::mutator_lock_) {
    return alloc_entrypoints_instrumented_;
  }

  bool ProcessMethodUnwindCallbacks(Thread* self,
                                    std::queue<ArtMethod*>& methods,
                                    MutableHandle<mirror::Throwable>& exception)
      REQUIRES_SHARED(Locks::mutator_lock_);

  EXPORT InstrumentationLevel GetCurrentInstrumentationLevel() const;

  bool MethodSupportsExitEvents(ArtMethod* method, const OatQuickMethodHeader* header)
      REQUIRES_SHARED(Locks::mutator_lock_);

 private:
  static bool CanUseAotCode(const void* quick_code);
  static const void* GetOptimizedCodeFor(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);

  // Update the current instrumentation_level_.
  void UpdateInstrumentationLevel(InstrumentationLevel level);

  // Does the job of installing or removing instrumentation code within methods.
  // In order to support multiple clients using instrumentation at the same time,
  // the caller must pass a unique key (a string) identifying it so we remind which
  // instrumentation level it needs. Therefore the current instrumentation level
  // becomes the highest instrumentation level required by a client.
  void ConfigureStubs(const char* key,
                      InstrumentationLevel desired_instrumentation_level,
                      bool try_switch_to_non_debuggable)
      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
      REQUIRES(!Locks::thread_list_lock_, !Locks::classlinker_classes_lock_);
  void UpdateStubs(bool try_switch_to_non_debuggable)
      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
      REQUIRES(!Locks::thread_list_lock_, !Locks::classlinker_classes_lock_);

  // If there are no pending deoptimizations restores the stack to the normal state by updating the
  // return pcs to actual return addresses from the instrumentation stack and clears the
  // instrumentation stack.
  void MaybeRestoreInstrumentationStack() REQUIRES(Locks::mutator_lock_);

  // Switches the runtime state to non-java debuggable if entry / exit hooks are no longer required
  // and the runtime did not start off as java debuggable.
  void MaybeSwitchRuntimeDebugState(Thread* self)
      REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_);

  // No thread safety analysis to get around SetQuickAllocEntryPointsInstrumented requiring
  // exclusive access to mutator lock which you can't get if the runtime isn't started.
  void SetEntrypointsInstrumented(bool instrumented) NO_THREAD_SAFETY_ANALYSIS;

  void MethodEnterEventImpl(Thread* thread, ArtMethod* method) const
      REQUIRES_SHARED(Locks::mutator_lock_);
  template <typename T>
  void MethodExitEventImpl(Thread* thread,
                           ArtMethod* method,
                           OptionalFrame frame,
                           T& return_value,
                           bool skip_trace_listener) const REQUIRES_SHARED(Locks::mutator_lock_);
  void DexPcMovedEventImpl(Thread* thread,
                           ObjPtr<mirror::Object> this_object,
                           ArtMethod* method,
                           uint32_t dex_pc) const
      REQUIRES_SHARED(Locks::mutator_lock_);
  void BranchImpl(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t offset) const
      REQUIRES_SHARED(Locks::mutator_lock_);
  void WatchedFramePopImpl(Thread* thread, const ShadowFrame& frame) const
      REQUIRES_SHARED(Locks::mutator_lock_);
  void FieldReadEventImpl(Thread* thread,
                          ObjPtr<mirror::Object> this_object,
                          ArtMethod* method,
                          uint32_t dex_pc,
                          ArtField* field) const
      REQUIRES_SHARED(Locks::mutator_lock_);
  void FieldWriteEventImpl(Thread* thread,
                           ObjPtr<mirror::Object> this_object,
                           ArtMethod* method,
                           uint32_t dex_pc,
                           ArtField* field,
                           const JValue& field_value) const
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Read barrier-aware utility functions for accessing deoptimized_methods_
  bool AddDeoptimizedMethod(ArtMethod* method) REQUIRES(Locks::mutator_lock_);
  bool IsDeoptimizedMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
  bool RemoveDeoptimizedMethod(ArtMethod* method) REQUIRES(Locks::mutator_lock_);
  void UpdateMethodsCodeImpl(ArtMethod* method, const void* new_code)
      REQUIRES_SHARED(Locks::mutator_lock_);

  // We need to run method exit hooks for two reasons:
  // 1. When method exit listeners are installed
  // 2. When we need to check if the caller of this method needs a deoptimization. This is needed
  // only for deoptimizing the currently active invocations on stack when we deoptimize a method or
  // invalidate the JITed code when redefining the classes. So future invocations don't need to do
  // this check.
  //
  // For JITed code of non-native methods we already have a stack slot reserved for deoptimizing
  // on demand and we use that stack slot to check if the caller needs a deoptimization. JITed code
  // checks if there are any method exit listeners or if the stack slot is set to determine if
  // method exit hooks need to be executed.
  //
  // For JITed JNI stubs there is no reserved stack slot for this and we just use this variable to
  // check if we need to run method entry / exit hooks. This variable would be set when either of
  // the above conditions are true. If we need method exit hooks only for case 2, we would call exit
  // hooks for any future invocations which aren't necessary.
  // QuickToInterpreterBridge and GenericJniStub also use this for same reasons.
  // If calling entry / exit hooks becomes expensive we could do the same optimization we did for
  // JITed code by having a reserved stack slot.
  bool run_exit_hooks_;

  // The required level of instrumentation. This could be one of the following values:
  // kInstrumentNothing: no instrumentation support is needed
  // kInstrumentWithEntryExitHooks: needs support to call method entry/exit stubs.
  // kInstrumentWithInterpreter: only execute with interpreter
  Instrumentation::InstrumentationLevel instrumentation_level_;

  // Did the runtime request we only run in the interpreter? ie -Xint mode.
  bool forced_interpret_only_;

  // For method entry / exit events, we maintain fast trace listeners in a separate list to make
  // implementation of fast trace listeners more efficient by JITing the code to handle fast trace
  // events. We use a uint8_t (and not bool) to encode if there are none / fast / slow listeners.
  // Do we have any listeners for method entry events.
  uint8_t have_method_entry_listeners_ GUARDED_BY(Locks::mutator_lock_);

  // Do we have any listeners for method exit events.
  uint8_t have_method_exit_listeners_ GUARDED_BY(Locks::mutator_lock_);

  // Do we have any listeners for method unwind events?
  bool have_method_unwind_listeners_ GUARDED_BY(Locks::mutator_lock_);

  // Do we have any listeners for dex move events?
  bool have_dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_);

  // Do we have any listeners for field read events?
  bool have_field_read_listeners_ GUARDED_BY(Locks::mutator_lock_);

  // Do we have any listeners for field write events?
  bool have_field_write_listeners_ GUARDED_BY(Locks::mutator_lock_);

  // Do we have any exception thrown listeners?
  bool have_exception_thrown_listeners_ GUARDED_BY(Locks::mutator_lock_);

  // Do we have any frame pop listeners?
  bool have_watched_frame_pop_listeners_ GUARDED_BY(Locks::mutator_lock_);

  // Do we have any branch listeners?
  bool have_branch_listeners_ GUARDED_BY(Locks::mutator_lock_);

  // Do we have any exception handled listeners?
  bool have_exception_handled_listeners_ GUARDED_BY(Locks::mutator_lock_);

  // Contains the instrumentation level required by each client of the instrumentation identified
  // by a string key.
  using InstrumentationLevelTable = SafeMap<const char*, InstrumentationLevel>;
  InstrumentationLevelTable requested_instrumentation_levels_ GUARDED_BY(Locks::mutator_lock_);

  // The event listeners, written to with the mutator_lock_ exclusively held.
  // Mutators must be able to iterate over these lists concurrently, that is, with listeners being
  // added or removed while iterating. The modifying thread holds exclusive lock,
  // so other threads cannot iterate (i.e. read the data of the list) at the same time but they
  // do keep iterators that need to remain valid. This is the reason these listeners are std::list
  // and not for example std::vector: the existing storage for a std::list does not move.
  // Note that mutators cannot make a copy of these lists before iterating, as the instrumentation
  // listeners can also be deleted concurrently.
  // As a result, these lists are never trimmed. That's acceptable given the low number of
  // listeners we have.
  std::list<InstrumentationListener*> method_entry_slow_listeners_ GUARDED_BY(Locks::mutator_lock_);
  std::list<InstrumentationListener*> method_entry_fast_trace_listeners_
      GUARDED_BY(Locks::mutator_lock_);
  std::list<InstrumentationListener*> method_exit_slow_listeners_ GUARDED_BY(Locks::mutator_lock_);
  std::list<InstrumentationListener*> method_exit_fast_trace_listeners_
      GUARDED_BY(Locks::mutator_lock_);
  std::list<InstrumentationListener*> method_exit_slow_trace_listeners_
      GUARDED_BY(Locks::mutator_lock_);
  std::list<InstrumentationListener*> method_unwind_listeners_ GUARDED_BY(Locks::mutator_lock_);
  std::list<InstrumentationListener*> branch_listeners_ GUARDED_BY(Locks::mutator_lock_);
  std::list<InstrumentationListener*> dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_);
  std::list<InstrumentationListener*> field_read_listeners_ GUARDED_BY(Locks::mutator_lock_);
  std::list<InstrumentationListener*> field_write_listeners_ GUARDED_BY(Locks::mutator_lock_);
  std::list<InstrumentationListener*> exception_thrown_listeners_ GUARDED_BY(Locks::mutator_lock_);
  std::list<InstrumentationListener*> watched_frame_pop_listeners_ GUARDED_BY(Locks::mutator_lock_);
  std::list<InstrumentationListener*> exception_handled_listeners_ GUARDED_BY(Locks::mutator_lock_);

  // The set of methods being deoptimized (by the debugger) which must be executed with interpreter
  // only.
  std::unordered_set<ArtMethod*> deoptimized_methods_ GUARDED_BY(Locks::mutator_lock_);

  // Current interpreter handler table. This is updated each time the thread state flags are
  // modified.

  // Greater than 0 if quick alloc entry points instrumented.
  size_t quick_alloc_entry_points_instrumentation_counter_;

  // alloc_entrypoints_instrumented_ is only updated with all the threads suspended, this is done
  // to prevent races with the GC where the GC relies on thread suspension only see
  // alloc_entrypoints_instrumented_ change during suspend points.
  bool alloc_entrypoints_instrumented_;

  friend class InstrumentationTest;  // For GetCurrentInstrumentationLevel and ConfigureStubs.
  friend class InstrumentationStackPopper;  // For popping instrumentation frames.
  friend void InstrumentationInstallStack(Thread*, bool);

  DISALLOW_COPY_AND_ASSIGN(Instrumentation);
};
std::ostream& operator<<(std::ostream& os, Instrumentation::InstrumentationEvent rhs);
std::ostream& operator<<(std::ostream& os, Instrumentation::InstrumentationLevel rhs);

}  // namespace instrumentation
}  // namespace art

#endif  // ART_RUNTIME_INSTRUMENTATION_H_

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

¤ Dauer der Verarbeitung: 0.14 Sekunden  (vorverarbeitet am  2026-06-29) ¤

*© Formatika GbR, Deutschland






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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik