1*795d594fSAndroid Build Coastguard Worker // Copyright (C) 2018 The Android Open Source Project
2*795d594fSAndroid Build Coastguard Worker //
3*795d594fSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*795d594fSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*795d594fSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*795d594fSAndroid Build Coastguard Worker //
7*795d594fSAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0
8*795d594fSAndroid Build Coastguard Worker //
9*795d594fSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*795d594fSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*795d594fSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*795d594fSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*795d594fSAndroid Build Coastguard Worker // limitations under the License.
14*795d594fSAndroid Build Coastguard Worker //
15*795d594fSAndroid Build Coastguard Worker
16*795d594fSAndroid Build Coastguard Worker #include <android-base/logging.h>
17*795d594fSAndroid Build Coastguard Worker
18*795d594fSAndroid Build Coastguard Worker #include <atomic>
19*795d594fSAndroid Build Coastguard Worker #include <fstream>
20*795d594fSAndroid Build Coastguard Worker #include <iostream>
21*795d594fSAndroid Build Coastguard Worker #include <istream>
22*795d594fSAndroid Build Coastguard Worker #include <iomanip>
23*795d594fSAndroid Build Coastguard Worker #include <jni.h>
24*795d594fSAndroid Build Coastguard Worker #include <jvmti.h>
25*795d594fSAndroid Build Coastguard Worker #include <limits>
26*795d594fSAndroid Build Coastguard Worker #include <map>
27*795d594fSAndroid Build Coastguard Worker #include <memory>
28*795d594fSAndroid Build Coastguard Worker #include <mutex>
29*795d594fSAndroid Build Coastguard Worker #include <string>
30*795d594fSAndroid Build Coastguard Worker #include <sstream>
31*795d594fSAndroid Build Coastguard Worker #include <vector>
32*795d594fSAndroid Build Coastguard Worker
33*795d594fSAndroid Build Coastguard Worker namespace tifast {
34*795d594fSAndroid Build Coastguard Worker
35*795d594fSAndroid Build Coastguard Worker namespace {
36*795d594fSAndroid Build Coastguard Worker
37*795d594fSAndroid Build Coastguard Worker // Special art ti-version number. We will use this as a fallback if we cannot get a regular JVMTI
38*795d594fSAndroid Build Coastguard Worker // env.
39*795d594fSAndroid Build Coastguard Worker static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000;
40*795d594fSAndroid Build Coastguard Worker
41*795d594fSAndroid Build Coastguard Worker // jthread is a typedef of jobject so we use this to allow the templates to distinguish them.
42*795d594fSAndroid Build Coastguard Worker struct jthreadContainer { jthread thread; };
43*795d594fSAndroid Build Coastguard Worker // jlocation is a typedef of jlong so use this to distinguish the less common jlong.
44*795d594fSAndroid Build Coastguard Worker struct jlongContainer { jlong val; };
45*795d594fSAndroid Build Coastguard Worker
DeleteLocalRef(JNIEnv * env,jobject obj)46*795d594fSAndroid Build Coastguard Worker static void DeleteLocalRef(JNIEnv* env, jobject obj) {
47*795d594fSAndroid Build Coastguard Worker if (obj != nullptr && env != nullptr) {
48*795d594fSAndroid Build Coastguard Worker env->DeleteLocalRef(obj);
49*795d594fSAndroid Build Coastguard Worker }
50*795d594fSAndroid Build Coastguard Worker }
51*795d594fSAndroid Build Coastguard Worker
52*795d594fSAndroid Build Coastguard Worker class ScopedThreadInfo {
53*795d594fSAndroid Build Coastguard Worker public:
ScopedThreadInfo(jvmtiEnv * jvmtienv,JNIEnv * env,jthread thread)54*795d594fSAndroid Build Coastguard Worker ScopedThreadInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jthread thread)
55*795d594fSAndroid Build Coastguard Worker : jvmtienv_(jvmtienv), env_(env), free_name_(false) {
56*795d594fSAndroid Build Coastguard Worker if (thread == nullptr) {
57*795d594fSAndroid Build Coastguard Worker info_.name = const_cast<char*>("<NULLPTR>");
58*795d594fSAndroid Build Coastguard Worker } else if (jvmtienv->GetThreadInfo(thread, &info_) != JVMTI_ERROR_NONE) {
59*795d594fSAndroid Build Coastguard Worker info_.name = const_cast<char*>("<UNKNOWN THREAD>");
60*795d594fSAndroid Build Coastguard Worker } else {
61*795d594fSAndroid Build Coastguard Worker free_name_ = true;
62*795d594fSAndroid Build Coastguard Worker }
63*795d594fSAndroid Build Coastguard Worker }
64*795d594fSAndroid Build Coastguard Worker
~ScopedThreadInfo()65*795d594fSAndroid Build Coastguard Worker ~ScopedThreadInfo() {
66*795d594fSAndroid Build Coastguard Worker if (free_name_) {
67*795d594fSAndroid Build Coastguard Worker jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(info_.name));
68*795d594fSAndroid Build Coastguard Worker }
69*795d594fSAndroid Build Coastguard Worker DeleteLocalRef(env_, info_.thread_group);
70*795d594fSAndroid Build Coastguard Worker DeleteLocalRef(env_, info_.context_class_loader);
71*795d594fSAndroid Build Coastguard Worker }
72*795d594fSAndroid Build Coastguard Worker
GetName() const73*795d594fSAndroid Build Coastguard Worker const char* GetName() const {
74*795d594fSAndroid Build Coastguard Worker return info_.name;
75*795d594fSAndroid Build Coastguard Worker }
76*795d594fSAndroid Build Coastguard Worker
77*795d594fSAndroid Build Coastguard Worker private:
78*795d594fSAndroid Build Coastguard Worker jvmtiEnv* jvmtienv_;
79*795d594fSAndroid Build Coastguard Worker JNIEnv* env_;
80*795d594fSAndroid Build Coastguard Worker bool free_name_;
81*795d594fSAndroid Build Coastguard Worker jvmtiThreadInfo info_{};
82*795d594fSAndroid Build Coastguard Worker };
83*795d594fSAndroid Build Coastguard Worker
84*795d594fSAndroid Build Coastguard Worker class ScopedClassInfo {
85*795d594fSAndroid Build Coastguard Worker public:
ScopedClassInfo(jvmtiEnv * jvmtienv,jclass c)86*795d594fSAndroid Build Coastguard Worker ScopedClassInfo(jvmtiEnv* jvmtienv, jclass c) : jvmtienv_(jvmtienv), class_(c) {}
87*795d594fSAndroid Build Coastguard Worker
~ScopedClassInfo()88*795d594fSAndroid Build Coastguard Worker ~ScopedClassInfo() {
89*795d594fSAndroid Build Coastguard Worker if (class_ != nullptr) {
90*795d594fSAndroid Build Coastguard Worker jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
91*795d594fSAndroid Build Coastguard Worker jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(generic_));
92*795d594fSAndroid Build Coastguard Worker jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(file_));
93*795d594fSAndroid Build Coastguard Worker jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(debug_ext_));
94*795d594fSAndroid Build Coastguard Worker }
95*795d594fSAndroid Build Coastguard Worker }
96*795d594fSAndroid Build Coastguard Worker
Init(bool get_generic=true)97*795d594fSAndroid Build Coastguard Worker bool Init(bool get_generic = true) {
98*795d594fSAndroid Build Coastguard Worker if (class_ == nullptr) {
99*795d594fSAndroid Build Coastguard Worker name_ = const_cast<char*>("<NONE>");
100*795d594fSAndroid Build Coastguard Worker generic_ = const_cast<char*>("<NONE>");
101*795d594fSAndroid Build Coastguard Worker return true;
102*795d594fSAndroid Build Coastguard Worker } else {
103*795d594fSAndroid Build Coastguard Worker jvmtiError ret1 = jvmtienv_->GetSourceFileName(class_, &file_);
104*795d594fSAndroid Build Coastguard Worker jvmtiError ret2 = jvmtienv_->GetSourceDebugExtension(class_, &debug_ext_);
105*795d594fSAndroid Build Coastguard Worker char** gen_ptr = &generic_;
106*795d594fSAndroid Build Coastguard Worker if (!get_generic) {
107*795d594fSAndroid Build Coastguard Worker generic_ = nullptr;
108*795d594fSAndroid Build Coastguard Worker gen_ptr = nullptr;
109*795d594fSAndroid Build Coastguard Worker }
110*795d594fSAndroid Build Coastguard Worker return jvmtienv_->GetClassSignature(class_, &name_, gen_ptr) == JVMTI_ERROR_NONE &&
111*795d594fSAndroid Build Coastguard Worker ret1 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
112*795d594fSAndroid Build Coastguard Worker ret1 != JVMTI_ERROR_INVALID_CLASS &&
113*795d594fSAndroid Build Coastguard Worker ret2 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
114*795d594fSAndroid Build Coastguard Worker ret2 != JVMTI_ERROR_INVALID_CLASS;
115*795d594fSAndroid Build Coastguard Worker }
116*795d594fSAndroid Build Coastguard Worker }
117*795d594fSAndroid Build Coastguard Worker
GetClass() const118*795d594fSAndroid Build Coastguard Worker jclass GetClass() const {
119*795d594fSAndroid Build Coastguard Worker return class_;
120*795d594fSAndroid Build Coastguard Worker }
121*795d594fSAndroid Build Coastguard Worker
GetName() const122*795d594fSAndroid Build Coastguard Worker const char* GetName() const {
123*795d594fSAndroid Build Coastguard Worker return name_;
124*795d594fSAndroid Build Coastguard Worker }
125*795d594fSAndroid Build Coastguard Worker
GetGeneric() const126*795d594fSAndroid Build Coastguard Worker const char* GetGeneric() const {
127*795d594fSAndroid Build Coastguard Worker return generic_;
128*795d594fSAndroid Build Coastguard Worker }
129*795d594fSAndroid Build Coastguard Worker
GetSourceDebugExtension() const130*795d594fSAndroid Build Coastguard Worker const char* GetSourceDebugExtension() const {
131*795d594fSAndroid Build Coastguard Worker if (debug_ext_ == nullptr) {
132*795d594fSAndroid Build Coastguard Worker return "<UNKNOWN_SOURCE_DEBUG_EXTENSION>";
133*795d594fSAndroid Build Coastguard Worker } else {
134*795d594fSAndroid Build Coastguard Worker return debug_ext_;
135*795d594fSAndroid Build Coastguard Worker }
136*795d594fSAndroid Build Coastguard Worker }
GetSourceFileName() const137*795d594fSAndroid Build Coastguard Worker const char* GetSourceFileName() const {
138*795d594fSAndroid Build Coastguard Worker if (file_ == nullptr) {
139*795d594fSAndroid Build Coastguard Worker return "<UNKNOWN_FILE>";
140*795d594fSAndroid Build Coastguard Worker } else {
141*795d594fSAndroid Build Coastguard Worker return file_;
142*795d594fSAndroid Build Coastguard Worker }
143*795d594fSAndroid Build Coastguard Worker }
144*795d594fSAndroid Build Coastguard Worker
145*795d594fSAndroid Build Coastguard Worker private:
146*795d594fSAndroid Build Coastguard Worker jvmtiEnv* jvmtienv_;
147*795d594fSAndroid Build Coastguard Worker jclass class_;
148*795d594fSAndroid Build Coastguard Worker char* name_ = nullptr;
149*795d594fSAndroid Build Coastguard Worker char* generic_ = nullptr;
150*795d594fSAndroid Build Coastguard Worker char* file_ = nullptr;
151*795d594fSAndroid Build Coastguard Worker char* debug_ext_ = nullptr;
152*795d594fSAndroid Build Coastguard Worker
153*795d594fSAndroid Build Coastguard Worker friend std::ostream& operator<<(std::ostream &os, ScopedClassInfo const& m);
154*795d594fSAndroid Build Coastguard Worker };
155*795d594fSAndroid Build Coastguard Worker
156*795d594fSAndroid Build Coastguard Worker class ScopedMethodInfo {
157*795d594fSAndroid Build Coastguard Worker public:
ScopedMethodInfo(jvmtiEnv * jvmtienv,JNIEnv * env,jmethodID m)158*795d594fSAndroid Build Coastguard Worker ScopedMethodInfo(jvmtiEnv* jvmtienv, JNIEnv* env, jmethodID m)
159*795d594fSAndroid Build Coastguard Worker : jvmtienv_(jvmtienv), env_(env), method_(m) {}
160*795d594fSAndroid Build Coastguard Worker
~ScopedMethodInfo()161*795d594fSAndroid Build Coastguard Worker ~ScopedMethodInfo() {
162*795d594fSAndroid Build Coastguard Worker DeleteLocalRef(env_, declaring_class_);
163*795d594fSAndroid Build Coastguard Worker jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(name_));
164*795d594fSAndroid Build Coastguard Worker jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(signature_));
165*795d594fSAndroid Build Coastguard Worker jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(generic_));
166*795d594fSAndroid Build Coastguard Worker }
167*795d594fSAndroid Build Coastguard Worker
Init(bool get_generic=true)168*795d594fSAndroid Build Coastguard Worker bool Init(bool get_generic = true) {
169*795d594fSAndroid Build Coastguard Worker if (jvmtienv_->GetMethodDeclaringClass(method_, &declaring_class_) != JVMTI_ERROR_NONE) {
170*795d594fSAndroid Build Coastguard Worker return false;
171*795d594fSAndroid Build Coastguard Worker }
172*795d594fSAndroid Build Coastguard Worker class_info_.reset(new ScopedClassInfo(jvmtienv_, declaring_class_));
173*795d594fSAndroid Build Coastguard Worker jint nlines;
174*795d594fSAndroid Build Coastguard Worker jvmtiLineNumberEntry* lines;
175*795d594fSAndroid Build Coastguard Worker jvmtiError err = jvmtienv_->GetLineNumberTable(method_, &nlines, &lines);
176*795d594fSAndroid Build Coastguard Worker if (err == JVMTI_ERROR_NONE) {
177*795d594fSAndroid Build Coastguard Worker if (nlines > 0) {
178*795d594fSAndroid Build Coastguard Worker first_line_ = lines[0].line_number;
179*795d594fSAndroid Build Coastguard Worker }
180*795d594fSAndroid Build Coastguard Worker jvmtienv_->Deallocate(reinterpret_cast<unsigned char*>(lines));
181*795d594fSAndroid Build Coastguard Worker } else if (err != JVMTI_ERROR_ABSENT_INFORMATION &&
182*795d594fSAndroid Build Coastguard Worker err != JVMTI_ERROR_NATIVE_METHOD) {
183*795d594fSAndroid Build Coastguard Worker return false;
184*795d594fSAndroid Build Coastguard Worker }
185*795d594fSAndroid Build Coastguard Worker return class_info_->Init(get_generic) &&
186*795d594fSAndroid Build Coastguard Worker (jvmtienv_->GetMethodName(method_, &name_, &signature_, &generic_) == JVMTI_ERROR_NONE);
187*795d594fSAndroid Build Coastguard Worker }
188*795d594fSAndroid Build Coastguard Worker
GetDeclaringClassInfo() const189*795d594fSAndroid Build Coastguard Worker const ScopedClassInfo& GetDeclaringClassInfo() const {
190*795d594fSAndroid Build Coastguard Worker return *class_info_;
191*795d594fSAndroid Build Coastguard Worker }
192*795d594fSAndroid Build Coastguard Worker
GetDeclaringClass() const193*795d594fSAndroid Build Coastguard Worker jclass GetDeclaringClass() const {
194*795d594fSAndroid Build Coastguard Worker return declaring_class_;
195*795d594fSAndroid Build Coastguard Worker }
196*795d594fSAndroid Build Coastguard Worker
GetName() const197*795d594fSAndroid Build Coastguard Worker const char* GetName() const {
198*795d594fSAndroid Build Coastguard Worker return name_;
199*795d594fSAndroid Build Coastguard Worker }
200*795d594fSAndroid Build Coastguard Worker
GetSignature() const201*795d594fSAndroid Build Coastguard Worker const char* GetSignature() const {
202*795d594fSAndroid Build Coastguard Worker return signature_;
203*795d594fSAndroid Build Coastguard Worker }
204*795d594fSAndroid Build Coastguard Worker
GetGeneric() const205*795d594fSAndroid Build Coastguard Worker const char* GetGeneric() const {
206*795d594fSAndroid Build Coastguard Worker return generic_;
207*795d594fSAndroid Build Coastguard Worker }
208*795d594fSAndroid Build Coastguard Worker
GetFirstLine() const209*795d594fSAndroid Build Coastguard Worker jint GetFirstLine() const {
210*795d594fSAndroid Build Coastguard Worker return first_line_;
211*795d594fSAndroid Build Coastguard Worker }
212*795d594fSAndroid Build Coastguard Worker
213*795d594fSAndroid Build Coastguard Worker private:
214*795d594fSAndroid Build Coastguard Worker jvmtiEnv* jvmtienv_;
215*795d594fSAndroid Build Coastguard Worker JNIEnv* env_;
216*795d594fSAndroid Build Coastguard Worker jmethodID method_;
217*795d594fSAndroid Build Coastguard Worker jclass declaring_class_ = nullptr;
218*795d594fSAndroid Build Coastguard Worker std::unique_ptr<ScopedClassInfo> class_info_;
219*795d594fSAndroid Build Coastguard Worker char* name_ = nullptr;
220*795d594fSAndroid Build Coastguard Worker char* signature_ = nullptr;
221*795d594fSAndroid Build Coastguard Worker char* generic_ = nullptr;
222*795d594fSAndroid Build Coastguard Worker jint first_line_ = -1;
223*795d594fSAndroid Build Coastguard Worker };
224*795d594fSAndroid Build Coastguard Worker
operator <<(std::ostream & os,ScopedClassInfo const & c)225*795d594fSAndroid Build Coastguard Worker std::ostream& operator<<(std::ostream &os, ScopedClassInfo const& c) {
226*795d594fSAndroid Build Coastguard Worker const char* generic = c.GetGeneric();
227*795d594fSAndroid Build Coastguard Worker if (generic != nullptr) {
228*795d594fSAndroid Build Coastguard Worker return os << c.GetName() << "<" << generic << ">" << " file: " << c.GetSourceFileName();
229*795d594fSAndroid Build Coastguard Worker } else {
230*795d594fSAndroid Build Coastguard Worker return os << c.GetName() << " file: " << c.GetSourceFileName();
231*795d594fSAndroid Build Coastguard Worker }
232*795d594fSAndroid Build Coastguard Worker }
233*795d594fSAndroid Build Coastguard Worker
234*795d594fSAndroid Build Coastguard Worker class LockedStream {
235*795d594fSAndroid Build Coastguard Worker public:
LockedStream(const std::string & filepath)236*795d594fSAndroid Build Coastguard Worker explicit LockedStream(const std::string& filepath) {
237*795d594fSAndroid Build Coastguard Worker stream_.open(filepath, std::ofstream::out);
238*795d594fSAndroid Build Coastguard Worker if (!stream_.is_open()) {
239*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "====== JVMTI FAILED TO OPEN LOG FILE";
240*795d594fSAndroid Build Coastguard Worker }
241*795d594fSAndroid Build Coastguard Worker }
~LockedStream()242*795d594fSAndroid Build Coastguard Worker ~LockedStream() {
243*795d594fSAndroid Build Coastguard Worker stream_.close();
244*795d594fSAndroid Build Coastguard Worker }
Write(const std::string & str)245*795d594fSAndroid Build Coastguard Worker void Write(const std::string& str) {
246*795d594fSAndroid Build Coastguard Worker stream_ << str;
247*795d594fSAndroid Build Coastguard Worker stream_.flush();
248*795d594fSAndroid Build Coastguard Worker }
249*795d594fSAndroid Build Coastguard Worker private:
250*795d594fSAndroid Build Coastguard Worker std::ofstream stream_;
251*795d594fSAndroid Build Coastguard Worker };
252*795d594fSAndroid Build Coastguard Worker
253*795d594fSAndroid Build Coastguard Worker static LockedStream* stream = nullptr;
254*795d594fSAndroid Build Coastguard Worker
255*795d594fSAndroid Build Coastguard Worker class UniqueStringTable {
256*795d594fSAndroid Build Coastguard Worker public:
257*795d594fSAndroid Build Coastguard Worker UniqueStringTable() = default;
258*795d594fSAndroid Build Coastguard Worker ~UniqueStringTable() = default;
Intern(const std::string & header,const std::string & key)259*795d594fSAndroid Build Coastguard Worker std::string Intern(const std::string& header, const std::string& key) {
260*795d594fSAndroid Build Coastguard Worker if (map_.find(key) == map_.end()) {
261*795d594fSAndroid Build Coastguard Worker map_[key] = next_index_;
262*795d594fSAndroid Build Coastguard Worker // Emit definition line. E.g., =123,string
263*795d594fSAndroid Build Coastguard Worker stream->Write(header + std::to_string(next_index_) + "," + key + "\n");
264*795d594fSAndroid Build Coastguard Worker ++next_index_;
265*795d594fSAndroid Build Coastguard Worker }
266*795d594fSAndroid Build Coastguard Worker return std::to_string(map_[key]);
267*795d594fSAndroid Build Coastguard Worker }
268*795d594fSAndroid Build Coastguard Worker private:
269*795d594fSAndroid Build Coastguard Worker int32_t next_index_;
270*795d594fSAndroid Build Coastguard Worker std::map<std::string, int32_t> map_;
271*795d594fSAndroid Build Coastguard Worker };
272*795d594fSAndroid Build Coastguard Worker
273*795d594fSAndroid Build Coastguard Worker static UniqueStringTable* string_table = nullptr;
274*795d594fSAndroid Build Coastguard Worker
275*795d594fSAndroid Build Coastguard Worker // Formatter for the thread, type, and size of an allocation.
formatAllocation(jvmtiEnv * jvmti,JNIEnv * jni,jthreadContainer thr,jclass klass,jlongContainer size)276*795d594fSAndroid Build Coastguard Worker static std::string formatAllocation(jvmtiEnv* jvmti,
277*795d594fSAndroid Build Coastguard Worker JNIEnv* jni,
278*795d594fSAndroid Build Coastguard Worker jthreadContainer thr,
279*795d594fSAndroid Build Coastguard Worker jclass klass,
280*795d594fSAndroid Build Coastguard Worker jlongContainer size) {
281*795d594fSAndroid Build Coastguard Worker ScopedThreadInfo sti(jvmti, jni, thr.thread);
282*795d594fSAndroid Build Coastguard Worker std::ostringstream allocation;
283*795d594fSAndroid Build Coastguard Worker allocation << "jthread[" << sti.GetName() << "]";
284*795d594fSAndroid Build Coastguard Worker ScopedClassInfo sci(jvmti, klass);
285*795d594fSAndroid Build Coastguard Worker if (sci.Init(/*get_generic=*/false)) {
286*795d594fSAndroid Build Coastguard Worker allocation << ", jclass[" << sci << "]";
287*795d594fSAndroid Build Coastguard Worker } else {
288*795d594fSAndroid Build Coastguard Worker allocation << ", jclass[TYPE UNKNOWN]";
289*795d594fSAndroid Build Coastguard Worker }
290*795d594fSAndroid Build Coastguard Worker allocation << ", size[" << size.val << ", hex: 0x" << std::hex << size.val << "]";
291*795d594fSAndroid Build Coastguard Worker return string_table->Intern("+", allocation.str());
292*795d594fSAndroid Build Coastguard Worker }
293*795d594fSAndroid Build Coastguard Worker
294*795d594fSAndroid Build Coastguard Worker // Formatter for a method entry on a call stack.
formatMethod(jvmtiEnv * jvmti,JNIEnv * jni,jmethodID method_id)295*795d594fSAndroid Build Coastguard Worker static std::string formatMethod(jvmtiEnv* jvmti, JNIEnv* jni, jmethodID method_id) {
296*795d594fSAndroid Build Coastguard Worker ScopedMethodInfo smi(jvmti, jni, method_id);
297*795d594fSAndroid Build Coastguard Worker std::string method;
298*795d594fSAndroid Build Coastguard Worker if (smi.Init(/*get_generic=*/false)) {
299*795d594fSAndroid Build Coastguard Worker method = std::string(smi.GetDeclaringClassInfo().GetName()) +
300*795d594fSAndroid Build Coastguard Worker "::" + smi.GetName() + smi.GetSignature();
301*795d594fSAndroid Build Coastguard Worker } else {
302*795d594fSAndroid Build Coastguard Worker method = "ERROR";
303*795d594fSAndroid Build Coastguard Worker }
304*795d594fSAndroid Build Coastguard Worker return string_table->Intern("+", method);
305*795d594fSAndroid Build Coastguard Worker }
306*795d594fSAndroid Build Coastguard Worker
307*795d594fSAndroid Build Coastguard Worker static int sampling_rate;
308*795d594fSAndroid Build Coastguard Worker static int stack_depth_limit;
309*795d594fSAndroid Build Coastguard Worker
logVMObjectAlloc(jvmtiEnv * jvmti,JNIEnv * jni,jthread thread,jobject obj,jclass klass,jlong size)310*795d594fSAndroid Build Coastguard Worker static void JNICALL logVMObjectAlloc(jvmtiEnv* jvmti,
311*795d594fSAndroid Build Coastguard Worker JNIEnv* jni,
312*795d594fSAndroid Build Coastguard Worker jthread thread,
313*795d594fSAndroid Build Coastguard Worker [[maybe_unused]] jobject obj,
314*795d594fSAndroid Build Coastguard Worker jclass klass,
315*795d594fSAndroid Build Coastguard Worker jlong size) {
316*795d594fSAndroid Build Coastguard Worker // Sample only once out of sampling_rate tries, and prevent recursive allocation tracking,
317*795d594fSAndroid Build Coastguard Worker static thread_local int sample_countdown = sampling_rate;
318*795d594fSAndroid Build Coastguard Worker --sample_countdown;
319*795d594fSAndroid Build Coastguard Worker if (sample_countdown != 0) {
320*795d594fSAndroid Build Coastguard Worker return;
321*795d594fSAndroid Build Coastguard Worker }
322*795d594fSAndroid Build Coastguard Worker
323*795d594fSAndroid Build Coastguard Worker // Guard accesses to string table and emission.
324*795d594fSAndroid Build Coastguard Worker static std::mutex mutex;
325*795d594fSAndroid Build Coastguard Worker std::lock_guard<std::mutex> lg(mutex);
326*795d594fSAndroid Build Coastguard Worker
327*795d594fSAndroid Build Coastguard Worker std::string record =
328*795d594fSAndroid Build Coastguard Worker formatAllocation(jvmti,
329*795d594fSAndroid Build Coastguard Worker jni,
330*795d594fSAndroid Build Coastguard Worker jthreadContainer{.thread = thread},
331*795d594fSAndroid Build Coastguard Worker klass,
332*795d594fSAndroid Build Coastguard Worker jlongContainer{.val = size});
333*795d594fSAndroid Build Coastguard Worker
334*795d594fSAndroid Build Coastguard Worker std::unique_ptr<jvmtiFrameInfo[]> stack_frames(new jvmtiFrameInfo[stack_depth_limit]);
335*795d594fSAndroid Build Coastguard Worker jint stack_depth;
336*795d594fSAndroid Build Coastguard Worker jvmtiError err = jvmti->GetStackTrace(thread,
337*795d594fSAndroid Build Coastguard Worker 0,
338*795d594fSAndroid Build Coastguard Worker stack_depth_limit,
339*795d594fSAndroid Build Coastguard Worker stack_frames.get(),
340*795d594fSAndroid Build Coastguard Worker &stack_depth);
341*795d594fSAndroid Build Coastguard Worker if (err == JVMTI_ERROR_NONE) {
342*795d594fSAndroid Build Coastguard Worker // Emit stack frames in order from deepest in the stack to most recent.
343*795d594fSAndroid Build Coastguard Worker // This simplifies post-collection processing.
344*795d594fSAndroid Build Coastguard Worker for (int i = stack_depth - 1; i >= 0; --i) {
345*795d594fSAndroid Build Coastguard Worker record += ";" + formatMethod(jvmti, jni, stack_frames[i].method);
346*795d594fSAndroid Build Coastguard Worker }
347*795d594fSAndroid Build Coastguard Worker }
348*795d594fSAndroid Build Coastguard Worker stream->Write(string_table->Intern("=", record) + "\n");
349*795d594fSAndroid Build Coastguard Worker
350*795d594fSAndroid Build Coastguard Worker sample_countdown = sampling_rate;
351*795d594fSAndroid Build Coastguard Worker }
352*795d594fSAndroid Build Coastguard Worker
353*795d594fSAndroid Build Coastguard Worker static jvmtiEventCallbacks kLogCallbacks {
354*795d594fSAndroid Build Coastguard Worker .VMObjectAlloc = logVMObjectAlloc,
355*795d594fSAndroid Build Coastguard Worker };
356*795d594fSAndroid Build Coastguard Worker
SetupJvmtiEnv(JavaVM * vm,jvmtiEnv ** jvmti)357*795d594fSAndroid Build Coastguard Worker static jint SetupJvmtiEnv(JavaVM* vm, jvmtiEnv** jvmti) {
358*795d594fSAndroid Build Coastguard Worker jint res = vm->GetEnv(reinterpret_cast<void**>(jvmti), JVMTI_VERSION_1_1);
359*795d594fSAndroid Build Coastguard Worker if (res != JNI_OK || *jvmti == nullptr) {
360*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Unable to access JVMTI, error code " << res;
361*795d594fSAndroid Build Coastguard Worker return vm->GetEnv(reinterpret_cast<void**>(jvmti), kArtTiVersion);
362*795d594fSAndroid Build Coastguard Worker }
363*795d594fSAndroid Build Coastguard Worker return res;
364*795d594fSAndroid Build Coastguard Worker }
365*795d594fSAndroid Build Coastguard Worker
366*795d594fSAndroid Build Coastguard Worker } // namespace
367*795d594fSAndroid Build Coastguard Worker
SetupCapabilities(jvmtiEnv * jvmti)368*795d594fSAndroid Build Coastguard Worker static jvmtiError SetupCapabilities(jvmtiEnv* jvmti) {
369*795d594fSAndroid Build Coastguard Worker jvmtiCapabilities caps{};
370*795d594fSAndroid Build Coastguard Worker caps.can_generate_vm_object_alloc_events = 1;
371*795d594fSAndroid Build Coastguard Worker caps.can_get_line_numbers = 1;
372*795d594fSAndroid Build Coastguard Worker caps.can_get_source_file_name = 1;
373*795d594fSAndroid Build Coastguard Worker caps.can_get_source_debug_extension = 1;
374*795d594fSAndroid Build Coastguard Worker return jvmti->AddCapabilities(&caps);
375*795d594fSAndroid Build Coastguard Worker }
376*795d594fSAndroid Build Coastguard Worker
ProcessOptions(std::string options)377*795d594fSAndroid Build Coastguard Worker static bool ProcessOptions(std::string options) {
378*795d594fSAndroid Build Coastguard Worker std::string output_file_path;
379*795d594fSAndroid Build Coastguard Worker if (options.empty()) {
380*795d594fSAndroid Build Coastguard Worker static constexpr int kDefaultSamplingRate = 10;
381*795d594fSAndroid Build Coastguard Worker static constexpr int kDefaultStackDepthLimit = 50;
382*795d594fSAndroid Build Coastguard Worker static constexpr const char* kDefaultOutputFilePath = "/data/local/tmp/logstream.txt";
383*795d594fSAndroid Build Coastguard Worker
384*795d594fSAndroid Build Coastguard Worker sampling_rate = kDefaultSamplingRate;
385*795d594fSAndroid Build Coastguard Worker stack_depth_limit = kDefaultStackDepthLimit;
386*795d594fSAndroid Build Coastguard Worker output_file_path = kDefaultOutputFilePath;
387*795d594fSAndroid Build Coastguard Worker } else {
388*795d594fSAndroid Build Coastguard Worker // options string should contain "sampling_rate,stack_depth_limit,output_file_path".
389*795d594fSAndroid Build Coastguard Worker size_t comma_pos = options.find(',');
390*795d594fSAndroid Build Coastguard Worker if (comma_pos == std::string::npos) {
391*795d594fSAndroid Build Coastguard Worker return false;
392*795d594fSAndroid Build Coastguard Worker }
393*795d594fSAndroid Build Coastguard Worker sampling_rate = std::stoi(options.substr(0, comma_pos));
394*795d594fSAndroid Build Coastguard Worker options = options.substr(comma_pos + 1);
395*795d594fSAndroid Build Coastguard Worker comma_pos = options.find(',');
396*795d594fSAndroid Build Coastguard Worker if (comma_pos == std::string::npos) {
397*795d594fSAndroid Build Coastguard Worker return false;
398*795d594fSAndroid Build Coastguard Worker }
399*795d594fSAndroid Build Coastguard Worker stack_depth_limit = std::stoi(options.substr(0, comma_pos));
400*795d594fSAndroid Build Coastguard Worker output_file_path = options.substr(comma_pos + 1);
401*795d594fSAndroid Build Coastguard Worker }
402*795d594fSAndroid Build Coastguard Worker LOG(INFO) << "Starting allocation tracing: sampling_rate=" << sampling_rate
403*795d594fSAndroid Build Coastguard Worker << ", stack_depth_limit=" << stack_depth_limit
404*795d594fSAndroid Build Coastguard Worker << ", output_file_path=" << output_file_path;
405*795d594fSAndroid Build Coastguard Worker stream = new LockedStream(output_file_path);
406*795d594fSAndroid Build Coastguard Worker
407*795d594fSAndroid Build Coastguard Worker return true;
408*795d594fSAndroid Build Coastguard Worker }
409*795d594fSAndroid Build Coastguard Worker
AgentStart(JavaVM * vm,char * options,void * reserved)410*795d594fSAndroid Build Coastguard Worker static jint AgentStart(JavaVM* vm, char* options, [[maybe_unused]] void* reserved) {
411*795d594fSAndroid Build Coastguard Worker // Handle the sampling rate, depth limit, and output path, if set.
412*795d594fSAndroid Build Coastguard Worker if (!ProcessOptions(options)) {
413*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
414*795d594fSAndroid Build Coastguard Worker }
415*795d594fSAndroid Build Coastguard Worker
416*795d594fSAndroid Build Coastguard Worker // Create the environment.
417*795d594fSAndroid Build Coastguard Worker jvmtiEnv* jvmti = nullptr;
418*795d594fSAndroid Build Coastguard Worker if (SetupJvmtiEnv(vm, &jvmti) != JNI_OK) {
419*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Could not get JVMTI env or ArtTiEnv!";
420*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
421*795d594fSAndroid Build Coastguard Worker }
422*795d594fSAndroid Build Coastguard Worker
423*795d594fSAndroid Build Coastguard Worker jvmtiError error = SetupCapabilities(jvmti);
424*795d594fSAndroid Build Coastguard Worker if (error != JVMTI_ERROR_NONE) {
425*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Unable to set caps";
426*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
427*795d594fSAndroid Build Coastguard Worker }
428*795d594fSAndroid Build Coastguard Worker
429*795d594fSAndroid Build Coastguard Worker // Add callbacks and notification.
430*795d594fSAndroid Build Coastguard Worker error = jvmti->SetEventCallbacks(&kLogCallbacks, static_cast<jint>(sizeof(kLogCallbacks)));
431*795d594fSAndroid Build Coastguard Worker if (error != JVMTI_ERROR_NONE) {
432*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Unable to set event callbacks.";
433*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
434*795d594fSAndroid Build Coastguard Worker }
435*795d594fSAndroid Build Coastguard Worker error = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
436*795d594fSAndroid Build Coastguard Worker JVMTI_EVENT_VM_OBJECT_ALLOC,
437*795d594fSAndroid Build Coastguard Worker nullptr /* all threads */);
438*795d594fSAndroid Build Coastguard Worker if (error != JVMTI_ERROR_NONE) {
439*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Unable to enable event " << JVMTI_EVENT_VM_OBJECT_ALLOC;
440*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
441*795d594fSAndroid Build Coastguard Worker }
442*795d594fSAndroid Build Coastguard Worker
443*795d594fSAndroid Build Coastguard Worker string_table = new UniqueStringTable();
444*795d594fSAndroid Build Coastguard Worker
445*795d594fSAndroid Build Coastguard Worker return JNI_OK;
446*795d594fSAndroid Build Coastguard Worker }
447*795d594fSAndroid Build Coastguard Worker
448*795d594fSAndroid Build Coastguard Worker // Late attachment (e.g. 'am attach-agent').
Agent_OnAttach(JavaVM * vm,char * options,void * reserved)449*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char* options, void* reserved) {
450*795d594fSAndroid Build Coastguard Worker return AgentStart(vm, options, reserved);
451*795d594fSAndroid Build Coastguard Worker }
452*795d594fSAndroid Build Coastguard Worker
453*795d594fSAndroid Build Coastguard Worker // Early attachment
Agent_OnLoad(JavaVM * jvm,char * options,void * reserved)454*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) {
455*795d594fSAndroid Build Coastguard Worker return AgentStart(jvm, options, reserved);
456*795d594fSAndroid Build Coastguard Worker }
457*795d594fSAndroid Build Coastguard Worker
458*795d594fSAndroid Build Coastguard Worker } // namespace tifast
459*795d594fSAndroid Build Coastguard Worker
460