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