instrumentation.h revision cbb2d20bea2861f244da2e2318d8c088300a3710
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#ifndef ART_RUNTIME_INSTRUMENTATION_H_ 18#define ART_RUNTIME_INSTRUMENTATION_H_ 19 20#include "atomic_integer.h" 21#include "base/macros.h" 22#include "locks.h" 23 24#include <stdint.h> 25#include <list> 26 27namespace art { 28namespace mirror { 29 class ArtMethod; 30 class Class; 31 class Object; 32 class Throwable; 33} // namespace mirror 34union JValue; 35class Thread; 36class ThrowLocation; 37 38namespace instrumentation { 39 40// Interpreter handler tables. 41enum InterpreterHandlerTable { 42 kMainHandlerTable = 0, // Main handler table: no suspend check, no instrumentation. 43 kAlternativeHandlerTable = 1, // Alternative handler table: suspend check and/or instrumentation 44 // enabled. 45 kNumHandlerTables 46}; 47 48// Instrumentation event listener API. Registered listeners will get the appropriate call back for 49// the events they are listening for. The call backs supply the thread, method and dex_pc the event 50// occurred upon. The thread may or may not be Thread::Current(). 51struct InstrumentationListener { 52 InstrumentationListener() {} 53 virtual ~InstrumentationListener() {} 54 55 // Call-back for when a method is entered. 56 virtual void MethodEntered(Thread* thread, mirror::Object* this_object, 57 const mirror::ArtMethod* method, 58 uint32_t dex_pc) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; 59 60 // Call-back for when a method is exited. 61 // TODO: its likely passing the return value would be useful, however, we may need to get and 62 // parse the shorty to determine what kind of register holds the result. 63 virtual void MethodExited(Thread* thread, mirror::Object* this_object, 64 const mirror::ArtMethod* method, uint32_t dex_pc, 65 const JValue& return_value) 66 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; 67 68 // Call-back for when a method is popped due to an exception throw. A method will either cause a 69 // MethodExited call-back or a MethodUnwind call-back when its activation is removed. 70 virtual void MethodUnwind(Thread* thread, mirror::Object* this_object, 71 const mirror::ArtMethod* method, uint32_t dex_pc) 72 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; 73 74 // Call-back for when the dex pc moves in a method. 75 virtual void DexPcMoved(Thread* thread, mirror::Object* this_object, 76 const mirror::ArtMethod* method, uint32_t new_dex_pc) 77 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; 78 79 // Call-back when an exception is caught. 80 virtual void ExceptionCaught(Thread* thread, const ThrowLocation& throw_location, 81 mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, 82 mirror::Throwable* exception_object) 83 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0; 84}; 85 86// Instrumentation is a catch-all for when extra information is required from the runtime. The 87// typical use for instrumentation is for profiling and debugging. Instrumentation may add stubs 88// to method entry and exit, it may also force execution to be switched to the interpreter and 89// trigger deoptimization. 90class Instrumentation { 91 public: 92 enum InstrumentationEvent { 93 kMethodEntered = 1, 94 kMethodExited = 2, 95 kMethodUnwind = 4, 96 kDexPcMoved = 8, 97 kExceptionCaught = 16 98 }; 99 100 Instrumentation() : 101 instrumentation_stubs_installed_(false), entry_exit_stubs_installed_(false), 102 interpreter_stubs_installed_(false), 103 interpret_only_(false), forced_interpret_only_(false), 104 have_method_entry_listeners_(false), have_method_exit_listeners_(false), 105 have_method_unwind_listeners_(false), have_dex_pc_listeners_(false), 106 have_exception_caught_listeners_(false), 107 interpreter_handler_table_(kMainHandlerTable), 108 quick_alloc_entry_points_instrumentation_counter_(0) {} 109 110 // Add a listener to be notified of the masked together sent of instrumentation events. This 111 // suspend the runtime to install stubs. You are expected to hold the mutator lock as a proxy 112 // for saying you should have suspended all threads (installing stubs while threads are running 113 // will break). 114 void AddListener(InstrumentationListener* listener, uint32_t events) 115 EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) 116 LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); 117 118 // Removes a listener possibly removing instrumentation stubs. 119 void RemoveListener(InstrumentationListener* listener, uint32_t events) 120 EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) 121 LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); 122 123 InterpreterHandlerTable GetInterpreterHandlerTable() const { 124 return interpreter_handler_table_; 125 } 126 127 void InstrumentQuickAllocEntryPoints() LOCKS_EXCLUDED(Locks::thread_list_lock_); 128 void UninstrumentQuickAllocEntryPoints() LOCKS_EXCLUDED(Locks::thread_list_lock_); 129 void ResetQuickAllocEntryPoints(); 130 131 // Update the code of a method respecting any installed stubs. 132 void UpdateMethodsCode(mirror::ArtMethod* method, const void* code) const; 133 134 // Get the quick code for the given method. More efficient than asking the class linker as it 135 // will short-cut to GetCode if instrumentation and static method resolution stubs aren't 136 // installed. 137 const void* GetQuickCodeFor(const mirror::ArtMethod* method) const 138 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); 139 140 void ForceInterpretOnly() { 141 interpret_only_ = true; 142 forced_interpret_only_ = true; 143 } 144 145 // Called by ArtMethod::Invoke to determine dispatch mechanism. 146 bool InterpretOnly() const { 147 return interpret_only_; 148 } 149 150 bool ShouldPortableCodeDeoptimize() const { 151 return instrumentation_stubs_installed_; 152 } 153 154 bool AreExitStubsInstalled() const { 155 return instrumentation_stubs_installed_; 156 } 157 158 bool HasMethodEntryListeners() const { 159 return have_method_entry_listeners_; 160 } 161 162 bool HasMethodExitListeners() const { 163 return have_method_exit_listeners_; 164 } 165 166 bool HasDexPcListeners() const { 167 return have_dex_pc_listeners_; 168 } 169 170 bool IsActive() const { 171 return have_dex_pc_listeners_ || have_method_entry_listeners_ || have_method_exit_listeners_ || 172 have_exception_caught_listeners_ || have_method_unwind_listeners_; 173 } 174 175 // Inform listeners that a method has been entered. A dex PC is provided as we may install 176 // listeners into executing code and get method enter events for methods already on the stack. 177 void MethodEnterEvent(Thread* thread, mirror::Object* this_object, 178 const mirror::ArtMethod* method, uint32_t dex_pc) const 179 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 180 if (UNLIKELY(HasMethodEntryListeners())) { 181 MethodEnterEventImpl(thread, this_object, method, dex_pc); 182 } 183 } 184 185 // Inform listeners that a method has been exited. 186 void MethodExitEvent(Thread* thread, mirror::Object* this_object, 187 const mirror::ArtMethod* method, uint32_t dex_pc, 188 const JValue& return_value) const 189 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 190 if (UNLIKELY(HasMethodExitListeners())) { 191 MethodExitEventImpl(thread, this_object, method, dex_pc, return_value); 192 } 193 } 194 195 // Inform listeners that a method has been exited due to an exception. 196 void MethodUnwindEvent(Thread* thread, mirror::Object* this_object, 197 const mirror::ArtMethod* method, uint32_t dex_pc) const 198 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); 199 200 // Inform listeners that the dex pc has moved (only supported by the interpreter). 201 void DexPcMovedEvent(Thread* thread, mirror::Object* this_object, 202 const mirror::ArtMethod* method, uint32_t dex_pc) const 203 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { 204 if (UNLIKELY(HasDexPcListeners())) { 205 DexPcMovedEventImpl(thread, this_object, method, dex_pc); 206 } 207 } 208 209 // Inform listeners that an exception was caught. 210 void ExceptionCaughtEvent(Thread* thread, const ThrowLocation& throw_location, 211 mirror::ArtMethod* catch_method, uint32_t catch_dex_pc, 212 mirror::Throwable* exception_object) const 213 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); 214 215 // Called when an instrumented method is entered. The intended link register (lr) is saved so 216 // that returning causes a branch to the method exit stub. Generates method enter events. 217 void PushInstrumentationStackFrame(Thread* self, mirror::Object* this_object, 218 mirror::ArtMethod* method, uintptr_t lr, 219 bool interpreter_entry) 220 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); 221 222 // Called when an instrumented method is exited. Removes the pushed instrumentation frame 223 // returning the intended link register. Generates method exit events. 224 uint64_t PopInstrumentationStackFrame(Thread* self, uintptr_t* return_pc, uint64_t gpr_result, 225 uint64_t fpr_result) 226 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); 227 228 // Pops an instrumentation frame from the current thread and generate an unwind event. 229 void PopMethodForUnwind(Thread* self, bool is_deoptimization) const 230 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); 231 232 // Call back for configure stubs. 233 bool InstallStubsForClass(mirror::Class* klass) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); 234 235 private: 236 // Does the job of installing or removing instrumentation code within methods. 237 void ConfigureStubs(bool require_entry_exit_stubs, bool require_interpreter) 238 EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_) 239 LOCKS_EXCLUDED(Locks::thread_list_lock_, Locks::classlinker_classes_lock_); 240 241 void UpdateInterpreterHandlerTable() { 242 interpreter_handler_table_ = IsActive() ? kAlternativeHandlerTable : kMainHandlerTable; 243 } 244 245 void MethodEnterEventImpl(Thread* thread, mirror::Object* this_object, 246 const mirror::ArtMethod* method, uint32_t dex_pc) const 247 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); 248 void MethodExitEventImpl(Thread* thread, mirror::Object* this_object, 249 const mirror::ArtMethod* method, 250 uint32_t dex_pc, const JValue& return_value) const 251 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); 252 void DexPcMovedEventImpl(Thread* thread, mirror::Object* this_object, 253 const mirror::ArtMethod* method, uint32_t dex_pc) const 254 SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); 255 256 // Have we hijacked ArtMethod::code_ so that it calls instrumentation/interpreter code? 257 bool instrumentation_stubs_installed_; 258 259 // Have we hijacked ArtMethod::code_ to reference the enter/exit stubs? 260 bool entry_exit_stubs_installed_; 261 262 // Have we hijacked ArtMethod::code_ to reference the enter interpreter stub? 263 bool interpreter_stubs_installed_; 264 265 // Do we need the fidelity of events that we only get from running within the interpreter? 266 bool interpret_only_; 267 268 // Did the runtime request we only run in the interpreter? ie -Xint mode. 269 bool forced_interpret_only_; 270 271 // Do we have any listeners for method entry events? Short-cut to avoid taking the 272 // instrumentation_lock_. 273 bool have_method_entry_listeners_; 274 275 // Do we have any listeners for method exit events? Short-cut to avoid taking the 276 // instrumentation_lock_. 277 bool have_method_exit_listeners_; 278 279 // Do we have any listeners for method unwind events? Short-cut to avoid taking the 280 // instrumentation_lock_. 281 bool have_method_unwind_listeners_; 282 283 // Do we have any listeners for dex move events? Short-cut to avoid taking the 284 // instrumentation_lock_. 285 bool have_dex_pc_listeners_; 286 287 // Do we have any exception caught listeners? Short-cut to avoid taking the instrumentation_lock_. 288 bool have_exception_caught_listeners_; 289 290 // The event listeners, written to with the mutator_lock_ exclusively held. 291 std::list<InstrumentationListener*> method_entry_listeners_ GUARDED_BY(Locks::mutator_lock_); 292 std::list<InstrumentationListener*> method_exit_listeners_ GUARDED_BY(Locks::mutator_lock_); 293 std::list<InstrumentationListener*> method_unwind_listeners_ GUARDED_BY(Locks::mutator_lock_); 294 std::list<InstrumentationListener*> dex_pc_listeners_ GUARDED_BY(Locks::mutator_lock_); 295 std::list<InstrumentationListener*> exception_caught_listeners_ GUARDED_BY(Locks::mutator_lock_); 296 297 // Current interpreter handler table. This is updated each time the thread state flags are 298 // modified. 299 InterpreterHandlerTable interpreter_handler_table_; 300 301 // Greater than 0 if quick alloc entry points instrumented. 302 // TODO: The access and changes to this is racy and should be guarded by a lock. 303 AtomicInteger quick_alloc_entry_points_instrumentation_counter_; 304 305 DISALLOW_COPY_AND_ASSIGN(Instrumentation); 306}; 307 308// An element in the instrumentation side stack maintained in art::Thread. 309struct InstrumentationStackFrame { 310 InstrumentationStackFrame(mirror::Object* this_object, mirror::ArtMethod* method, 311 uintptr_t return_pc, size_t frame_id, bool interpreter_entry) 312 : this_object_(this_object), method_(method), return_pc_(return_pc), frame_id_(frame_id), 313 interpreter_entry_(interpreter_entry) { 314 } 315 316 std::string Dump() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); 317 318 mirror::Object* this_object_; 319 mirror::ArtMethod* method_; 320 const uintptr_t return_pc_; 321 const size_t frame_id_; 322 const bool interpreter_entry_; 323}; 324 325} // namespace instrumentation 326} // namespace art 327 328#endif // ART_RUNTIME_INSTRUMENTATION_H_ 329