1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798
|
/*
* 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 <functional>
#include <stdint.h>
#include <list>
#include <memory>
#include <unordered_set>
#include <optional>
#include "arch/instruction_set.h"
#include "base/enums.h"
#include "base/locks.h"
#include "base/macros.h"
#include "base/safe_map.h"
#include "gc_root.h"
namespace art {
namespace mirror {
class Class;
class Object;
class Throwable;
} // namespace mirror
class ArtField;
class ArtMethod;
template <typename T> class Handle;
template <typename T> class MutableHandle;
union JValue;
class SHARED_LOCKABLE ReaderWriterMutex;
class ShadowFrame;
class Thread;
enum class DeoptimizationMethodType;
namespace instrumentation {
// Interpreter handler tables.
enum InterpreterHandlerTable {
kMainHandlerTable = 0, // Main handler table: no suspend check, no instrumentation.
kAlternativeHandlerTable = 1, // Alternative handler table: suspend check and/or instrumentation
// enabled.
kNumHandlerTables
};
// 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,
Handle<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) = 0;
virtual void MethodExited(Thread* thread,
Handle<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc,
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,
Handle<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc,
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,
Handle<mirror::Object> this_object,
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(Thread* thread ATTRIBUTE_UNUSED,
const ShadowFrame& frame ATTRIBUTE_UNUSED)
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>& 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
kInstrumentWithInstrumentationStubs, // execute with instrumentation entry/exit stubs
kInstrumentWithInterpreter // execute with interpreter
};
Instrumentation();
// 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).
void AddListener(InstrumentationListener* listener, uint32_t events)
REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_);
// Removes a listener possibly removing instrumentation stubs.
void RemoveListener(InstrumentationListener* listener, uint32_t events)
REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !Locks::classlinker_classes_lock_);
// Deoptimization.
void EnableDeoptimization()
REQUIRES(Locks::mutator_lock_)
REQUIRES(!GetDeoptimizedMethodsLock());
// Calls UndeoptimizeEverything which may visit class linker classes through ConfigureStubs.
void DisableDeoptimization(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!GetDeoptimizedMethodsLock());
bool AreAllMethodsDeoptimized() const {
return interpreter_stubs_installed_;
}
bool ShouldNotifyMethodEnterExitEvents() const REQUIRES_SHARED(Locks::mutator_lock_);
bool CanDeoptimize() {
return deoptimization_enabled_;
}
// Executes everything with interpreter.
void DeoptimizeEverything(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_,
!GetDeoptimizedMethodsLock());
// Executes everything with compiled code (or interpreter if there is no code). May visit class
// linker classes through ConfigureStubs.
void UndeoptimizeEverything(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_,
!GetDeoptimizedMethodsLock());
// 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.
void Deoptimize(ArtMethod* method)
REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !GetDeoptimizedMethodsLock());
// 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.
void Undeoptimize(ArtMethod* method)
REQUIRES(Locks::mutator_lock_, !Locks::thread_list_lock_, !GetDeoptimizedMethodsLock());
// Indicates whether the method has been deoptimized so it is executed with the interpreter.
bool IsDeoptimized(ArtMethod* method)
REQUIRES(!GetDeoptimizedMethodsLock()) REQUIRES_SHARED(Locks::mutator_lock_);
// Enable method tracing by installing instrumentation entry/exit stubs or interpreter.
void EnableMethodTracing(const char* key,
bool needs_interpreter = kDeoptimizeForAccurateMethodEntryExitListeners)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_,
!GetDeoptimizedMethodsLock());
// Disable method tracing by uninstalling instrumentation entry/exit stubs or interpreter.
void DisableMethodTracing(const char* key)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_,
!GetDeoptimizedMethodsLock());
InterpreterHandlerTable GetInterpreterHandlerTable() const
REQUIRES_SHARED(Locks::mutator_lock_) {
return interpreter_handler_table_;
}
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_);
// Update the code of a method respecting any installed stubs.
void UpdateMethodsCode(ArtMethod* method, const void* quick_code)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
// Update the code of a native method to a JITed stub.
void UpdateNativeMethodsCodeToJitCode(ArtMethod* method, const void* quick_code)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
// Update the code of a method to the interpreter respecting any installed stubs from debugger.
void UpdateMethodsCodeToInterpreterEntryPoint(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
// Update the code of a method respecting any installed stubs from debugger.
void UpdateMethodsCodeForJavaDebuggable(ArtMethod* method, const void* quick_code)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
// Return the code that we can execute for an invoke including from the JIT.
const void* GetCodeForInvoke(ArtMethod* method) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Get the quick code for the given method. More efficient than asking the class linker as it
// will short-cut to GetCode if instrumentation and static method resolution stubs aren't
// installed.
const void* GetQuickCodeFor(ArtMethod* method, PointerSize pointer_size) const
REQUIRES_SHARED(Locks::mutator_lock_);
void ForceInterpretOnly() {
interpret_only_ = true;
forced_interpret_only_ = true;
}
// Called by ArtMethod::Invoke to determine dispatch mechanism.
bool InterpretOnly() const {
return interpret_only_;
}
bool IsForcedInterpretOnly() const {
return forced_interpret_only_;
}
// Code is in boot image oat file which isn't compiled as debuggable.
// Need debug version (interpreter or jitted) if that's the case.
bool NeedDebugVersionFor(ArtMethod* method) const
REQUIRES_SHARED(Locks::mutator_lock_);
bool AreExitStubsInstalled() const {
return instrumentation_stubs_installed_;
}
bool HasMethodEntryListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
return have_method_entry_listeners_;
}
bool HasMethodExitListeners() const REQUIRES_SHARED(Locks::mutator_lock_) {
return have_method_exit_listeners_;
}
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_;
}
bool IsActive() const REQUIRES_SHARED(Locks::mutator_lock_) {
return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ ||
have_field_read_listeners_ || have_field_write_listeners_ ||
have_exception_thrown_listeners_ || have_method_unwind_listeners_ ||
have_branch_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,
ObjPtr<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc) const
REQUIRES_SHARED(Locks::mutator_lock_) {
if (UNLIKELY(HasMethodEntryListeners())) {
MethodEnterEventImpl(thread, this_object, method, dex_pc);
}
}
// Inform listeners that a method has been exited.
template<typename T>
void MethodExitEvent(Thread* thread,
ObjPtr<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc,
OptionalFrame frame,
T& return_value) const
REQUIRES_SHARED(Locks::mutator_lock_) {
if (UNLIKELY(HasMethodExitListeners())) {
MethodExitEventImpl(thread, this_object, method, dex_pc, frame, return_value);
}
}
// Inform listeners that a method has been exited due to an exception.
void MethodUnwindEvent(Thread* thread,
ObjPtr<mirror::Object> this_object,
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_);
// Called when an instrumented method is entered. The intended link register (lr) is saved so
// that returning causes a branch to the method exit stub. Generates method enter events.
void PushInstrumentationStackFrame(Thread* self,
ObjPtr<mirror::Object> this_object,
ArtMethod* method,
uintptr_t stack_pointer,
uintptr_t lr,
bool interpreter_entry)
REQUIRES_SHARED(Locks::mutator_lock_);
DeoptimizationMethodType GetDeoptimizationMethodType(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_);
// Called when an instrumented method is exited. Removes the pushed instrumentation frame
// returning the intended link register. Generates method exit events. The gpr_result and
// fpr_result pointers are pointers to the locations where the integer/pointer and floating point
// result values of the function are stored. Both pointers must always be valid but the values
// held there will only be meaningful if interpreted as the appropriate type given the function
// being returned from.
TwoWordReturn PopInstrumentationStackFrame(Thread* self,
uintptr_t* return_pc_addr,
uint64_t* gpr_result,
uint64_t* fpr_result)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
// Pops nframes instrumentation frames from the current thread. Returns the return pc for the last
// instrumentation frame that's popped.
uintptr_t PopFramesForDeoptimization(Thread* self, uintptr_t stack_pointer) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Call back for configure stubs.
void InstallStubsForClass(ObjPtr<mirror::Class> klass) REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!GetDeoptimizedMethodsLock());
void InstallStubsForMethod(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
// Sets up instrumentation to allow single thread deoptimization using ForceInterpreterCount.
void EnableSingleThreadDeopt()
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_,
!GetDeoptimizedMethodsLock());
// 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 thread's stack after updating
// local variable(s).
void InstrumentThreadStack(Thread* thread)
REQUIRES(Locks::mutator_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.
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_;
}
InstrumentationLevel GetCurrentInstrumentationLevel() const;
private:
// Returns true if moving to the given instrumentation level requires the installation of stubs.
// False otherwise.
bool RequiresInstrumentationInstallation(InstrumentationLevel new_level) const;
// 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)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!GetDeoptimizedMethodsLock(),
!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_);
void UpdateStubs() REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!GetDeoptimizedMethodsLock(),
!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_);
void UpdateInstrumentationLevels(InstrumentationLevel level)
REQUIRES(Locks::mutator_lock_, Roles::uninterruptible_)
REQUIRES(!GetDeoptimizedMethodsLock(),
!Locks::thread_list_lock_,
!Locks::classlinker_classes_lock_);
void UpdateInterpreterHandlerTable() REQUIRES(Locks::mutator_lock_) {
/*
* TUNING: Dalvik's mterp stashes the actual current handler table base in a
* tls field. For Arm, this enables all suspend, debug & tracing checks to be
* collapsed into a single conditionally-executed ldw instruction.
* Move to Dalvik-style handler-table management for both the goto interpreter and
* mterp.
*/
interpreter_handler_table_ = IsActive() ? kAlternativeHandlerTable : kMainHandlerTable;
}
// 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,
ObjPtr<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc) const
REQUIRES_SHARED(Locks::mutator_lock_);
template <typename T>
void MethodExitEventImpl(Thread* thread,
ObjPtr<mirror::Object> this_object,
ArtMethod* method,
uint32_t dex_pc,
OptionalFrame frame,
T& return_value) 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_SHARED(Locks::mutator_lock_) REQUIRES(GetDeoptimizedMethodsLock());
bool IsDeoptimizedMethod(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
bool RemoveDeoptimizedMethod(ArtMethod* method)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(GetDeoptimizedMethodsLock());
ArtMethod* BeginDeoptimizedMethod()
REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
bool IsDeoptimizedMethodsEmpty() const
REQUIRES_SHARED(Locks::mutator_lock_, GetDeoptimizedMethodsLock());
void UpdateMethodsCodeImpl(ArtMethod* method, const void* quick_code)
REQUIRES_SHARED(Locks::mutator_lock_) REQUIRES(!GetDeoptimizedMethodsLock());
ReaderWriterMutex* GetDeoptimizedMethodsLock() const {
return deoptimized_methods_lock_.get();
}
// A counter that's incremented every time a DeoptimizeAllFrames. We check each
// InstrumentationStackFrames creation id against this number and if they differ we deopt even if
// we could otherwise continue running.
uint64_t current_force_deopt_id_ GUARDED_BY(Locks::mutator_lock_);
// Have we hijacked ArtMethod::code_ so that it calls instrumentation/interpreter code?
bool instrumentation_stubs_installed_;
// Have we hijacked ArtMethod::code_ to reference the enter/exit stubs?
bool entry_exit_stubs_installed_;
// Have we hijacked ArtMethod::code_ to reference the enter interpreter stub?
bool interpreter_stubs_installed_;
// Do we need the fidelity of events that we only get from running within the interpreter?
bool interpret_only_;
// Did the runtime request we only run in the interpreter? ie -Xint mode.
bool forced_interpret_only_;
// Do we have any listeners for method entry events? Short-cut to avoid taking the
// instrumentation_lock_.
bool have_method_entry_listeners_ GUARDED_BY(Locks::mutator_lock_);
// Do we have any listeners for method exit events? Short-cut to avoid taking the
// instrumentation_lock_.
bool have_method_exit_listeners_ GUARDED_BY(Locks::mutator_lock_);
// Do we have any listeners for method unwind events? Short-cut to avoid taking the
// instrumentation_lock_.
bool have_method_unwind_listeners_ GUARDED_BY(Locks::mutator_lock_);
// Do we have any listeners for dex move events? Short-cut to avoid taking the
// instrumentation_lock_.
bool have_dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_);
// Do we have any listeners for field read events? Short-cut to avoid taking the
// instrumentation_lock_.
bool have_field_read_listeners_ GUARDED_BY(Locks::mutator_lock_);
// Do we have any listeners for field write events? Short-cut to avoid taking the
// instrumentation_lock_.
bool have_field_write_listeners_ GUARDED_BY(Locks::mutator_lock_);
// Do we have any exception thrown listeners? Short-cut to avoid taking the instrumentation_lock_.
bool have_exception_thrown_listeners_ GUARDED_BY(Locks::mutator_lock_);
// Do we have any frame pop listeners? Short-cut to avoid taking the instrumentation_lock_.
bool have_watched_frame_pop_listeners_ GUARDED_BY(Locks::mutator_lock_);
// Do we have any branch listeners? Short-cut to avoid taking the instrumentation_lock_.
bool have_branch_listeners_ GUARDED_BY(Locks::mutator_lock_);
// Do we have any exception handled listeners? Short-cut to avoid taking the
// instrumentation_lock_.
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.
typedef SafeMap<const char*, InstrumentationLevel> InstrumentationLevelTable;
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_listeners_ GUARDED_BY(Locks::mutator_lock_);
std::list<InstrumentationListener*> method_exit_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.
mutable std::unique_ptr<ReaderWriterMutex> deoptimized_methods_lock_ BOTTOM_MUTEX_ACQUIRED_AFTER;
std::unordered_set<ArtMethod*> deoptimized_methods_ GUARDED_BY(GetDeoptimizedMethodsLock());
bool deoptimization_enabled_;
// Current interpreter handler table. This is updated each time the thread state flags are
// modified.
InterpreterHandlerTable interpreter_handler_table_ GUARDED_BY(Locks::mutator_lock_);
// 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_;
// If we can use instrumentation trampolines. After the first time we instrument something with
// the interpreter we can no longer use trampolines because it can lead to stack corruption.
// TODO Figure out a way to remove the need for this.
bool can_use_instrumentation_trampolines_;
friend class InstrumentationTest; // For GetCurrentInstrumentationLevel and ConfigureStubs.
friend class InstrumentationStackPopper; // For popping instrumentation frames.
friend void InstrumentationInstallStack(Thread*, void*);
DISALLOW_COPY_AND_ASSIGN(Instrumentation);
};
std::ostream& operator<<(std::ostream& os, const Instrumentation::InstrumentationEvent& rhs);
std::ostream& operator<<(std::ostream& os, const Instrumentation::InstrumentationLevel& rhs);
// An element in the instrumentation side stack maintained in art::Thread.
struct InstrumentationStackFrame {
InstrumentationStackFrame(mirror::Object* this_object,
ArtMethod* method,
uintptr_t return_pc,
size_t frame_id,
bool interpreter_entry,
uint64_t force_deopt_id)
: this_object_(this_object),
method_(method),
return_pc_(return_pc),
frame_id_(frame_id),
interpreter_entry_(interpreter_entry),
force_deopt_id_(force_deopt_id) {
}
std::string Dump() const REQUIRES_SHARED(Locks::mutator_lock_);
mirror::Object* this_object_;
ArtMethod* method_;
uintptr_t return_pc_;
size_t frame_id_;
bool interpreter_entry_;
uint64_t force_deopt_id_;
};
} // namespace instrumentation
} // namespace art
#endif // ART_RUNTIME_INSTRUMENTATION_H_
|