1 // Copyright (C) 2017 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 
16 #include "instruction_decoder.h"
17 
18 #include <android-base/logging.h>
19 #include <atomic>
20 #include <jni.h>
21 #include <jvmti.h>
22 #include <map>
23 #include <memory>
24 #include <mutex>
25 
26 // We could probably return a JNI_ERR here but lets crash instead if something fails.
27 #define CHECK_JVMTI_ERROR(jvmti, errnum) \
28     CHECK_EQ(JVMTI_ERROR_NONE, (errnum)) << GetJvmtiErrorString((jvmti), (errnum)) << (" ")
29 
30 namespace titrace {
31 
GetJvmtiErrorString(jvmtiEnv * jvmti,jvmtiError errnum)32 static const char* GetJvmtiErrorString(jvmtiEnv* jvmti, jvmtiError errnum) {
33   char* errnum_str = nullptr;
34   jvmti->GetErrorName(errnum, /*out*/ &errnum_str);
35   if (errnum_str == nullptr) {
36     return "Unknown";
37   }
38 
39   return errnum_str;
40 }
41 
42 // Type-safe wrapper for JVMTI-allocated memory.
43 // Deallocates with jvmtiEnv::Deallocate.
44 template <typename T>
45 struct TiMemory {
TiMemorytitrace::TiMemory46   explicit TiMemory(jvmtiEnv* env, T* mem, size_t size) : env_(env), mem_(mem), size_(size) {
47   }
48 
~TiMemorytitrace::TiMemory49   ~TiMemory() {
50     if (mem_ != nullptr) {
51       env_->Deallocate(static_cast<unsigned char*>(mem_));
52     }
53     mem_ = nullptr;
54   }
55 
56   TiMemory(const TiMemory& other) = delete;
TiMemorytitrace::TiMemory57   TiMemory(TiMemory&& other) noexcept {
58     env_ = other.env_;
59     mem_ = other.mem_;
60     size_ = other.size_;
61 
62     if (this != &other) {
63       other.env_ = nullptr;
64       other.mem_ = nullptr;
65       other.size_ = 0u;
66     }
67   }
68 
operator =titrace::TiMemory69   TiMemory& operator=(TiMemory&& other) noexcept {
70     if (mem_ != other.mem_) {
71       TiMemory::~TiMemory();
72     }
73     new (this) TiMemory(std::move(other));
74     return *this;
75   }
76 
GetMemorytitrace::TiMemory77   T* GetMemory() {
78     return mem_;
79   }
80 
Sizetitrace::TiMemory81   size_t Size() {
82     return size_ / sizeof(T);
83   }
84 
85  private:
86   jvmtiEnv* env_;
87   T* mem_;
88   size_t size_;
89 };
90 
91 struct MethodBytecode {
MethodBytecodetitrace::MethodBytecode92   explicit MethodBytecode(jvmtiEnv* env, unsigned char* memory, jint size)
93       : bytecode_(env, memory, static_cast<size_t>(size)) {
94   }
95 
96   TiMemory<uint8_t> bytecode_;
97 };
98 
99 struct TraceStatistics {
Initializetitrace::TraceStatistics100   static void Initialize(jvmtiEnv* jvmti) {
101     TraceStatistics& stats = GetSingleton();
102 
103     bool is_ri = true;
104     {
105       jvmtiError error;
106       char* value_ptr;
107       error = jvmti->GetSystemProperty("java.vm.name", /*out*/ &value_ptr);
108       CHECK_JVMTI_ERROR(jvmti, error) << "Failed to get property 'java.vm.name'";
109       CHECK(value_ptr != nullptr) << "Returned property was null for 'java.vm.name'";
110 
111       if (strcmp("Dalvik", value_ptr) == 0) {
112         is_ri = false;
113       }
114     }
115 
116     InstructionFileFormat format =
117         is_ri ? InstructionFileFormat::kClass : InstructionFileFormat::kDex;
118     stats.instruction_decoder_.reset(InstructionDecoder::NewInstance(format));
119 
120     CHECK_GE(arraysize(stats.instruction_counter_),
121              stats.instruction_decoder_->GetMaximumOpcode());
122   }
123 
GetSingletontitrace::TraceStatistics124   static TraceStatistics& GetSingleton() {
125     static TraceStatistics stats;
126     return stats;
127   }
128 
Logtitrace::TraceStatistics129   void Log() {
130     LOG(INFO) << "================================================";
131     LOG(INFO) << "              TI Trace // Summary               ";
132     LOG(INFO) << "++++++++++++++++++++++++++++++++++++++++++++++++";
133     LOG(INFO) << "  * Single step counter: " << single_step_counter_;
134     LOG(INFO) << "+++++++++++    Instructions Count   ++++++++++++";
135 
136     size_t total = single_step_counter_;
137     for (size_t i = 0; i < arraysize(instruction_counter_); ++i) {
138       size_t inst_count = instruction_counter_[i];
139       if (inst_count > 0) {
140         const char* opcode_name = instruction_decoder_->GetName(i);
141         LOG(INFO) << "  * " << opcode_name << "(op:" << i << "), count: " << inst_count
142                   << ", % of total: " << (100.0 * inst_count / total);
143       }
144     }
145 
146     LOG(INFO) << "------------------------------------------------";
147   }
148 
OnSingleSteptitrace::TraceStatistics149   void OnSingleStep(jvmtiEnv* jvmti_env, jmethodID method, jlocation location) {
150     // Counters do not need a happens-before.
151     // Use the weakest memory order simply to avoid tearing.
152     single_step_counter_.fetch_add(1u, std::memory_order_relaxed);
153 
154     MethodBytecode& bytecode = LookupBytecode(jvmti_env, method);
155 
156     // Decode jlocation value that depends on the bytecode format.
157     size_t actual_location = instruction_decoder_->LocationToOffset(static_cast<size_t>(location));
158 
159     // Decode the exact instruction and increment its counter.
160     CHECK_LE(actual_location, bytecode.bytecode_.Size());
161     RecordInstruction(bytecode.bytecode_.GetMemory() + actual_location);
162   }
163 
164  private:
RecordInstructiontitrace::TraceStatistics165   void RecordInstruction(const uint8_t* instruction) {
166     uint8_t opcode = instruction[0];
167     // Counters do not need a happens-before.
168     // Use the weakest memory order simply to avoid tearing.
169     instruction_counter_[opcode].fetch_add(1u, std::memory_order_relaxed);
170   }
171 
LookupBytecodetitrace::TraceStatistics172   MethodBytecode& LookupBytecode(jvmtiEnv* jvmti_env, jmethodID method) {
173     jvmtiError error;
174     std::lock_guard<std::mutex> lock(bytecode_cache_mutex_);
175 
176     auto it = bytecode_cache_.find(method);
177     if (it == bytecode_cache_.end()) {
178       jint bytecode_count_ptr = 0;
179       unsigned char* bytecodes_ptr = nullptr;
180 
181       error = jvmti_env->GetBytecodes(method, &bytecode_count_ptr, &bytecodes_ptr);
182       CHECK_JVMTI_ERROR(jvmti_env, error) << "Failed to get bytecodes for method " << method;
183       CHECK(bytecodes_ptr != nullptr) << "Bytecode ptr was null for method " << method;
184       CHECK_GE(bytecode_count_ptr, 0) << "Bytecode size too small for method " << method;
185 
186       // std::pair<iterator, bool inserted>
187       auto&& pair = bytecode_cache_.insert(
188           std::make_pair(method, MethodBytecode(jvmti_env, bytecodes_ptr, bytecode_count_ptr)));
189       it = pair.first;
190     }
191 
192     // Returning the address is safe. if map is resized, the contents will not move.
193     return it->second;
194   }
195 
196   std::unique_ptr<InstructionDecoder> instruction_decoder_;
197 
198   std::atomic<size_t> single_step_counter_{0u};
199   std::atomic<size_t> instruction_counter_[256]{};
200 
201   // Cache the bytecode to avoid calling into JVMTI repeatedly.
202   // TODO: invalidate if the bytecode was updated?
203   std::map<jmethodID, MethodBytecode> bytecode_cache_;
204   // bytecode cache is thread-safe.
205   std::mutex bytecode_cache_mutex_;
206 };
207 
208 struct EventCallbacks {
SingleSteptitrace::EventCallbacks209   static void SingleStep(jvmtiEnv* jvmti_env,
210                          JNIEnv* jni_env ATTRIBUTE_UNUSED,
211                          jthread thread ATTRIBUTE_UNUSED,
212                          jmethodID method,
213                          jlocation location) {
214     TraceStatistics& stats = TraceStatistics::GetSingleton();
215     stats.OnSingleStep(jvmti_env, method, location);
216   }
217 
218   // Use "kill -SIGQUIT" to generate a data dump request.
219   // Useful when running an android app since it doesn't go through
220   // a normal Agent_OnUnload.
DataDumpRequesttitrace::EventCallbacks221   static void DataDumpRequest(jvmtiEnv* jvmti_env ATTRIBUTE_UNUSED) {
222     TraceStatistics& stats = TraceStatistics::GetSingleton();
223     stats.Log();
224   }
225 };
226 
227 }  // namespace titrace
228 
229 // Late attachment (e.g. 'am attach-agent').
Agent_OnAttach(JavaVM * vm,char * options,void * reserved)230 JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char* options, void* reserved) {
231   return Agent_OnLoad(vm, options, reserved);
232 }
233 
234 // Early attachment (e.g. 'java -agent[lib|path]:filename.so').
Agent_OnLoad(JavaVM * jvm,char *,void *)235 JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm,
236                                     char* /* options */,
237                                     void* /* reserved */) {
238   using namespace titrace;  // NOLINT [build/namespaces] [5]
239 
240   android::base::InitLogging(/* argv= */nullptr);
241 
242   jvmtiEnv* jvmti = nullptr;
243   {
244     jint res = 0;
245     // Magic number that the agent can use to attach to non-debuggable apps on userdebug.
246     constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;
247     res = jvm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_1);
248 
249     if (res != JNI_OK || jvmti == nullptr) {
250       res = jvm->GetEnv(reinterpret_cast<void**>(&jvmti), kArtTiVersion);
251     }
252     if (res != JNI_OK || jvmti == nullptr) {
253       LOG(FATAL) << "Unable to access JVMTI, error code " << res;
254     }
255   }
256 
257   LOG(INFO) << "Agent_OnLoad: Hello World";
258 
259   {
260     // Initialize our instruction file-format decoder.
261     TraceStatistics::Initialize(jvmti);
262   }
263 
264   jvmtiError error{};
265 
266   // Set capabilities.
267   {
268     jvmtiCapabilities caps = {};
269     caps.can_generate_single_step_events = 1;
270     caps.can_get_bytecodes = 1;
271 
272     error = jvmti->AddCapabilities(&caps);
273     CHECK_JVMTI_ERROR(jvmti, error)
274       << "Unable to get necessary JVMTI capabilities";
275   }
276 
277   // Set callbacks.
278   {
279     jvmtiEventCallbacks callbacks = {};
280     callbacks.SingleStep = &EventCallbacks::SingleStep;
281     callbacks.DataDumpRequest = &EventCallbacks::DataDumpRequest;
282 
283     error = jvmti->SetEventCallbacks(&callbacks,
284                                      static_cast<jint>(sizeof(callbacks)));
285     CHECK_JVMTI_ERROR(jvmti, error) << "Unable to set event callbacks";
286   }
287 
288   // Enable events notification.
289   {
290     error = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
291                                             JVMTI_EVENT_SINGLE_STEP,
292                                             nullptr /* all threads */);
293     CHECK_JVMTI_ERROR(jvmti, error)
294       << "Failed to enable SINGLE_STEP notification";
295 
296     error = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
297                                             JVMTI_EVENT_DATA_DUMP_REQUEST,
298                                             nullptr /* all threads */);
299     CHECK_JVMTI_ERROR(jvmti, error)
300       << "Failed to enable DATA_DUMP_REQUEST notification";
301   }
302 
303   return JNI_OK;
304 }
305 
306 // Note: This is not called for normal Android apps,
307 // use "kill -SIGQUIT" instead to generate a data dump request.
Agent_OnUnload(JavaVM * vm ATTRIBUTE_UNUSED)308 JNIEXPORT void JNICALL Agent_OnUnload(JavaVM* vm ATTRIBUTE_UNUSED) {
309   using namespace titrace;  // NOLINT [build/namespaces] [5]
310   LOG(INFO) << "Agent_OnUnload: Goodbye";
311 
312   TraceStatistics::GetSingleton().Log();
313 }
314 
315