1*795d594fSAndroid Build Coastguard Worker // Copyright (C) 2017 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 #include <atomic>
18*795d594fSAndroid Build Coastguard Worker #include <iostream>
19*795d594fSAndroid Build Coastguard Worker #include <iomanip>
20*795d594fSAndroid Build Coastguard Worker #include <jni.h>
21*795d594fSAndroid Build Coastguard Worker #include <jvmti.h>
22*795d594fSAndroid Build Coastguard Worker #include <memory>
23*795d594fSAndroid Build Coastguard Worker #include <string>
24*795d594fSAndroid Build Coastguard Worker #include <vector>
25*795d594fSAndroid Build Coastguard Worker
26*795d594fSAndroid Build Coastguard Worker namespace breakpoint_logger {
27*795d594fSAndroid Build Coastguard Worker
28*795d594fSAndroid Build Coastguard Worker struct SingleBreakpointTarget {
29*795d594fSAndroid Build Coastguard Worker std::string class_name;
30*795d594fSAndroid Build Coastguard Worker std::string method_name;
31*795d594fSAndroid Build Coastguard Worker std::string method_sig;
32*795d594fSAndroid Build Coastguard Worker jlocation location;
33*795d594fSAndroid Build Coastguard Worker };
34*795d594fSAndroid Build Coastguard Worker
35*795d594fSAndroid Build Coastguard Worker struct BreakpointTargets {
36*795d594fSAndroid Build Coastguard Worker std::vector<SingleBreakpointTarget> bps;
37*795d594fSAndroid Build Coastguard Worker };
38*795d594fSAndroid Build Coastguard Worker
VMInitCB(jvmtiEnv * jvmti,JNIEnv * env,jthread thr)39*795d594fSAndroid Build Coastguard Worker static void VMInitCB(jvmtiEnv* jvmti, JNIEnv* env, [[maybe_unused]] jthread thr) {
40*795d594fSAndroid Build Coastguard Worker BreakpointTargets* all_targets = nullptr;
41*795d594fSAndroid Build Coastguard Worker jvmtiError err = jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&all_targets));
42*795d594fSAndroid Build Coastguard Worker if (err != JVMTI_ERROR_NONE || all_targets == nullptr) {
43*795d594fSAndroid Build Coastguard Worker env->FatalError("unable to get breakpoint targets");
44*795d594fSAndroid Build Coastguard Worker }
45*795d594fSAndroid Build Coastguard Worker for (const SingleBreakpointTarget& target : all_targets->bps) {
46*795d594fSAndroid Build Coastguard Worker jclass k = env->FindClass(target.class_name.c_str());
47*795d594fSAndroid Build Coastguard Worker if (env->ExceptionCheck()) {
48*795d594fSAndroid Build Coastguard Worker env->ExceptionDescribe();
49*795d594fSAndroid Build Coastguard Worker env->FatalError("Could not find class!");
50*795d594fSAndroid Build Coastguard Worker return;
51*795d594fSAndroid Build Coastguard Worker }
52*795d594fSAndroid Build Coastguard Worker jmethodID m = env->GetMethodID(k, target.method_name.c_str(), target.method_sig.c_str());
53*795d594fSAndroid Build Coastguard Worker if (env->ExceptionCheck()) {
54*795d594fSAndroid Build Coastguard Worker env->ExceptionClear();
55*795d594fSAndroid Build Coastguard Worker m = env->GetStaticMethodID(k, target.method_name.c_str(), target.method_sig.c_str());
56*795d594fSAndroid Build Coastguard Worker if (env->ExceptionCheck()) {
57*795d594fSAndroid Build Coastguard Worker env->ExceptionDescribe();
58*795d594fSAndroid Build Coastguard Worker env->FatalError("Could not find method!");
59*795d594fSAndroid Build Coastguard Worker return;
60*795d594fSAndroid Build Coastguard Worker }
61*795d594fSAndroid Build Coastguard Worker }
62*795d594fSAndroid Build Coastguard Worker err = jvmti->SetBreakpoint(m, target.location);
63*795d594fSAndroid Build Coastguard Worker if (err != JVMTI_ERROR_NONE) {
64*795d594fSAndroid Build Coastguard Worker env->FatalError("unable to set breakpoint");
65*795d594fSAndroid Build Coastguard Worker return;
66*795d594fSAndroid Build Coastguard Worker }
67*795d594fSAndroid Build Coastguard Worker env->DeleteLocalRef(k);
68*795d594fSAndroid Build Coastguard Worker }
69*795d594fSAndroid Build Coastguard Worker }
70*795d594fSAndroid Build Coastguard Worker
71*795d594fSAndroid Build Coastguard Worker class ScopedThreadInfo {
72*795d594fSAndroid Build Coastguard Worker public:
ScopedThreadInfo(jvmtiEnv * jvmti_env,JNIEnv * env,jthread thread)73*795d594fSAndroid Build Coastguard Worker ScopedThreadInfo(jvmtiEnv* jvmti_env, JNIEnv* env, jthread thread)
74*795d594fSAndroid Build Coastguard Worker : jvmti_env_(jvmti_env), env_(env), free_name_(false) {
75*795d594fSAndroid Build Coastguard Worker memset(&info_, 0, sizeof(info_));
76*795d594fSAndroid Build Coastguard Worker if (thread == nullptr) {
77*795d594fSAndroid Build Coastguard Worker info_.name = const_cast<char*>("<NULLPTR>");
78*795d594fSAndroid Build Coastguard Worker } else if (jvmti_env->GetThreadInfo(thread, &info_) != JVMTI_ERROR_NONE) {
79*795d594fSAndroid Build Coastguard Worker info_.name = const_cast<char*>("<UNKNOWN THREAD>");
80*795d594fSAndroid Build Coastguard Worker } else {
81*795d594fSAndroid Build Coastguard Worker free_name_ = true;
82*795d594fSAndroid Build Coastguard Worker }
83*795d594fSAndroid Build Coastguard Worker }
84*795d594fSAndroid Build Coastguard Worker
~ScopedThreadInfo()85*795d594fSAndroid Build Coastguard Worker ~ScopedThreadInfo() {
86*795d594fSAndroid Build Coastguard Worker if (free_name_) {
87*795d594fSAndroid Build Coastguard Worker jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(info_.name));
88*795d594fSAndroid Build Coastguard Worker }
89*795d594fSAndroid Build Coastguard Worker env_->DeleteLocalRef(info_.thread_group);
90*795d594fSAndroid Build Coastguard Worker env_->DeleteLocalRef(info_.context_class_loader);
91*795d594fSAndroid Build Coastguard Worker }
92*795d594fSAndroid Build Coastguard Worker
GetName() const93*795d594fSAndroid Build Coastguard Worker const char* GetName() const {
94*795d594fSAndroid Build Coastguard Worker return info_.name;
95*795d594fSAndroid Build Coastguard Worker }
96*795d594fSAndroid Build Coastguard Worker
97*795d594fSAndroid Build Coastguard Worker private:
98*795d594fSAndroid Build Coastguard Worker jvmtiEnv* jvmti_env_;
99*795d594fSAndroid Build Coastguard Worker JNIEnv* env_;
100*795d594fSAndroid Build Coastguard Worker bool free_name_;
101*795d594fSAndroid Build Coastguard Worker jvmtiThreadInfo info_;
102*795d594fSAndroid Build Coastguard Worker };
103*795d594fSAndroid Build Coastguard Worker
104*795d594fSAndroid Build Coastguard Worker class ScopedClassInfo {
105*795d594fSAndroid Build Coastguard Worker public:
ScopedClassInfo(jvmtiEnv * jvmti_env,jclass c)106*795d594fSAndroid Build Coastguard Worker ScopedClassInfo(jvmtiEnv* jvmti_env, jclass c)
107*795d594fSAndroid Build Coastguard Worker : jvmti_env_(jvmti_env),
108*795d594fSAndroid Build Coastguard Worker class_(c),
109*795d594fSAndroid Build Coastguard Worker name_(nullptr),
110*795d594fSAndroid Build Coastguard Worker generic_(nullptr),
111*795d594fSAndroid Build Coastguard Worker file_(nullptr),
112*795d594fSAndroid Build Coastguard Worker debug_ext_(nullptr) {}
113*795d594fSAndroid Build Coastguard Worker
~ScopedClassInfo()114*795d594fSAndroid Build Coastguard Worker ~ScopedClassInfo() {
115*795d594fSAndroid Build Coastguard Worker if (class_ != nullptr) {
116*795d594fSAndroid Build Coastguard Worker jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(name_));
117*795d594fSAndroid Build Coastguard Worker jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(generic_));
118*795d594fSAndroid Build Coastguard Worker jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(file_));
119*795d594fSAndroid Build Coastguard Worker jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(debug_ext_));
120*795d594fSAndroid Build Coastguard Worker }
121*795d594fSAndroid Build Coastguard Worker }
122*795d594fSAndroid Build Coastguard Worker
Init()123*795d594fSAndroid Build Coastguard Worker bool Init() {
124*795d594fSAndroid Build Coastguard Worker if (class_ == nullptr) {
125*795d594fSAndroid Build Coastguard Worker name_ = const_cast<char*>("<NONE>");
126*795d594fSAndroid Build Coastguard Worker generic_ = const_cast<char*>("<NONE>");
127*795d594fSAndroid Build Coastguard Worker return true;
128*795d594fSAndroid Build Coastguard Worker } else {
129*795d594fSAndroid Build Coastguard Worker jvmtiError ret1 = jvmti_env_->GetSourceFileName(class_, &file_);
130*795d594fSAndroid Build Coastguard Worker jvmtiError ret2 = jvmti_env_->GetSourceDebugExtension(class_, &debug_ext_);
131*795d594fSAndroid Build Coastguard Worker return jvmti_env_->GetClassSignature(class_, &name_, &generic_) == JVMTI_ERROR_NONE &&
132*795d594fSAndroid Build Coastguard Worker ret1 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
133*795d594fSAndroid Build Coastguard Worker ret1 != JVMTI_ERROR_INVALID_CLASS &&
134*795d594fSAndroid Build Coastguard Worker ret2 != JVMTI_ERROR_MUST_POSSESS_CAPABILITY &&
135*795d594fSAndroid Build Coastguard Worker ret2 != JVMTI_ERROR_INVALID_CLASS;
136*795d594fSAndroid Build Coastguard Worker }
137*795d594fSAndroid Build Coastguard Worker }
138*795d594fSAndroid Build Coastguard Worker
GetClass() const139*795d594fSAndroid Build Coastguard Worker jclass GetClass() const {
140*795d594fSAndroid Build Coastguard Worker return class_;
141*795d594fSAndroid Build Coastguard Worker }
GetName() const142*795d594fSAndroid Build Coastguard Worker const char* GetName() const {
143*795d594fSAndroid Build Coastguard Worker return name_;
144*795d594fSAndroid Build Coastguard Worker }
145*795d594fSAndroid Build Coastguard Worker // Generic type parameters, whatever is in the <> for a class
GetGeneric() const146*795d594fSAndroid Build Coastguard Worker const char* GetGeneric() const {
147*795d594fSAndroid Build Coastguard Worker return generic_;
148*795d594fSAndroid Build Coastguard Worker }
GetSourceDebugExtension() const149*795d594fSAndroid Build Coastguard Worker const char* GetSourceDebugExtension() const {
150*795d594fSAndroid Build Coastguard Worker if (debug_ext_ == nullptr) {
151*795d594fSAndroid Build Coastguard Worker return "<UNKNOWN_SOURCE_DEBUG_EXTENSION>";
152*795d594fSAndroid Build Coastguard Worker } else {
153*795d594fSAndroid Build Coastguard Worker return debug_ext_;
154*795d594fSAndroid Build Coastguard Worker }
155*795d594fSAndroid Build Coastguard Worker }
GetSourceFileName() const156*795d594fSAndroid Build Coastguard Worker const char* GetSourceFileName() const {
157*795d594fSAndroid Build Coastguard Worker if (file_ == nullptr) {
158*795d594fSAndroid Build Coastguard Worker return "<UNKNOWN_FILE>";
159*795d594fSAndroid Build Coastguard Worker } else {
160*795d594fSAndroid Build Coastguard Worker return file_;
161*795d594fSAndroid Build Coastguard Worker }
162*795d594fSAndroid Build Coastguard Worker }
163*795d594fSAndroid Build Coastguard Worker
164*795d594fSAndroid Build Coastguard Worker private:
165*795d594fSAndroid Build Coastguard Worker jvmtiEnv* jvmti_env_;
166*795d594fSAndroid Build Coastguard Worker jclass class_;
167*795d594fSAndroid Build Coastguard Worker char* name_;
168*795d594fSAndroid Build Coastguard Worker char* generic_;
169*795d594fSAndroid Build Coastguard Worker char* file_;
170*795d594fSAndroid Build Coastguard Worker char* debug_ext_;
171*795d594fSAndroid Build Coastguard Worker };
172*795d594fSAndroid Build Coastguard Worker
173*795d594fSAndroid Build Coastguard Worker class ScopedMethodInfo {
174*795d594fSAndroid Build Coastguard Worker public:
ScopedMethodInfo(jvmtiEnv * jvmti_env,JNIEnv * env,jmethodID method)175*795d594fSAndroid Build Coastguard Worker ScopedMethodInfo(jvmtiEnv* jvmti_env, JNIEnv* env, jmethodID method)
176*795d594fSAndroid Build Coastguard Worker : jvmti_env_(jvmti_env),
177*795d594fSAndroid Build Coastguard Worker env_(env),
178*795d594fSAndroid Build Coastguard Worker method_(method),
179*795d594fSAndroid Build Coastguard Worker declaring_class_(nullptr),
180*795d594fSAndroid Build Coastguard Worker class_info_(nullptr),
181*795d594fSAndroid Build Coastguard Worker name_(nullptr),
182*795d594fSAndroid Build Coastguard Worker signature_(nullptr),
183*795d594fSAndroid Build Coastguard Worker generic_(nullptr),
184*795d594fSAndroid Build Coastguard Worker first_line_(-1) {}
185*795d594fSAndroid Build Coastguard Worker
~ScopedMethodInfo()186*795d594fSAndroid Build Coastguard Worker ~ScopedMethodInfo() {
187*795d594fSAndroid Build Coastguard Worker env_->DeleteLocalRef(declaring_class_);
188*795d594fSAndroid Build Coastguard Worker jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(name_));
189*795d594fSAndroid Build Coastguard Worker jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(signature_));
190*795d594fSAndroid Build Coastguard Worker jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(generic_));
191*795d594fSAndroid Build Coastguard Worker }
192*795d594fSAndroid Build Coastguard Worker
Init()193*795d594fSAndroid Build Coastguard Worker bool Init() {
194*795d594fSAndroid Build Coastguard Worker if (jvmti_env_->GetMethodDeclaringClass(method_, &declaring_class_) != JVMTI_ERROR_NONE) {
195*795d594fSAndroid Build Coastguard Worker return false;
196*795d594fSAndroid Build Coastguard Worker }
197*795d594fSAndroid Build Coastguard Worker class_info_.reset(new ScopedClassInfo(jvmti_env_, declaring_class_));
198*795d594fSAndroid Build Coastguard Worker jint nlines;
199*795d594fSAndroid Build Coastguard Worker jvmtiLineNumberEntry* lines;
200*795d594fSAndroid Build Coastguard Worker jvmtiError err = jvmti_env_->GetLineNumberTable(method_, &nlines, &lines);
201*795d594fSAndroid Build Coastguard Worker if (err == JVMTI_ERROR_NONE) {
202*795d594fSAndroid Build Coastguard Worker if (nlines > 0) {
203*795d594fSAndroid Build Coastguard Worker first_line_ = lines[0].line_number;
204*795d594fSAndroid Build Coastguard Worker }
205*795d594fSAndroid Build Coastguard Worker jvmti_env_->Deallocate(reinterpret_cast<unsigned char*>(lines));
206*795d594fSAndroid Build Coastguard Worker } else if (err != JVMTI_ERROR_ABSENT_INFORMATION &&
207*795d594fSAndroid Build Coastguard Worker err != JVMTI_ERROR_NATIVE_METHOD) {
208*795d594fSAndroid Build Coastguard Worker return false;
209*795d594fSAndroid Build Coastguard Worker }
210*795d594fSAndroid Build Coastguard Worker return class_info_->Init() &&
211*795d594fSAndroid Build Coastguard Worker (jvmti_env_->GetMethodName(method_, &name_, &signature_, &generic_) == JVMTI_ERROR_NONE);
212*795d594fSAndroid Build Coastguard Worker }
213*795d594fSAndroid Build Coastguard Worker
GetDeclaringClassInfo() const214*795d594fSAndroid Build Coastguard Worker const ScopedClassInfo& GetDeclaringClassInfo() const {
215*795d594fSAndroid Build Coastguard Worker return *class_info_;
216*795d594fSAndroid Build Coastguard Worker }
217*795d594fSAndroid Build Coastguard Worker
GetDeclaringClass() const218*795d594fSAndroid Build Coastguard Worker jclass GetDeclaringClass() const {
219*795d594fSAndroid Build Coastguard Worker return declaring_class_;
220*795d594fSAndroid Build Coastguard Worker }
221*795d594fSAndroid Build Coastguard Worker
GetName() const222*795d594fSAndroid Build Coastguard Worker const char* GetName() const {
223*795d594fSAndroid Build Coastguard Worker return name_;
224*795d594fSAndroid Build Coastguard Worker }
225*795d594fSAndroid Build Coastguard Worker
GetSignature() const226*795d594fSAndroid Build Coastguard Worker const char* GetSignature() const {
227*795d594fSAndroid Build Coastguard Worker return signature_;
228*795d594fSAndroid Build Coastguard Worker }
229*795d594fSAndroid Build Coastguard Worker
GetGeneric() const230*795d594fSAndroid Build Coastguard Worker const char* GetGeneric() const {
231*795d594fSAndroid Build Coastguard Worker return generic_;
232*795d594fSAndroid Build Coastguard Worker }
233*795d594fSAndroid Build Coastguard Worker
GetFirstLine() const234*795d594fSAndroid Build Coastguard Worker jint GetFirstLine() const {
235*795d594fSAndroid Build Coastguard Worker return first_line_;
236*795d594fSAndroid Build Coastguard Worker }
237*795d594fSAndroid Build Coastguard Worker
238*795d594fSAndroid Build Coastguard Worker private:
239*795d594fSAndroid Build Coastguard Worker jvmtiEnv* jvmti_env_;
240*795d594fSAndroid Build Coastguard Worker JNIEnv* env_;
241*795d594fSAndroid Build Coastguard Worker jmethodID method_;
242*795d594fSAndroid Build Coastguard Worker jclass declaring_class_;
243*795d594fSAndroid Build Coastguard Worker std::unique_ptr<ScopedClassInfo> class_info_;
244*795d594fSAndroid Build Coastguard Worker char* name_;
245*795d594fSAndroid Build Coastguard Worker char* signature_;
246*795d594fSAndroid Build Coastguard Worker char* generic_;
247*795d594fSAndroid Build Coastguard Worker jint first_line_;
248*795d594fSAndroid Build Coastguard Worker
249*795d594fSAndroid Build Coastguard Worker friend std::ostream& operator<<(std::ostream& os, ScopedMethodInfo const& method);
250*795d594fSAndroid Build Coastguard Worker };
251*795d594fSAndroid Build Coastguard Worker
operator <<(std::ostream & os,const ScopedMethodInfo * method)252*795d594fSAndroid Build Coastguard Worker std::ostream& operator<<(std::ostream& os, const ScopedMethodInfo* method) {
253*795d594fSAndroid Build Coastguard Worker return os << *method;
254*795d594fSAndroid Build Coastguard Worker }
255*795d594fSAndroid Build Coastguard Worker
operator <<(std::ostream & os,ScopedMethodInfo const & method)256*795d594fSAndroid Build Coastguard Worker std::ostream& operator<<(std::ostream& os, ScopedMethodInfo const& method) {
257*795d594fSAndroid Build Coastguard Worker return os << method.GetDeclaringClassInfo().GetName() << "->" << method.GetName()
258*795d594fSAndroid Build Coastguard Worker << method.GetSignature() << " (source: "
259*795d594fSAndroid Build Coastguard Worker << method.GetDeclaringClassInfo().GetSourceFileName() << ":" << method.GetFirstLine()
260*795d594fSAndroid Build Coastguard Worker << ")";
261*795d594fSAndroid Build Coastguard Worker }
262*795d594fSAndroid Build Coastguard Worker
BreakpointCB(jvmtiEnv * jvmti_env,JNIEnv * env,jthread thread,jmethodID method,jlocation location)263*795d594fSAndroid Build Coastguard Worker static void BreakpointCB(jvmtiEnv* jvmti_env,
264*795d594fSAndroid Build Coastguard Worker JNIEnv* env,
265*795d594fSAndroid Build Coastguard Worker jthread thread,
266*795d594fSAndroid Build Coastguard Worker jmethodID method,
267*795d594fSAndroid Build Coastguard Worker jlocation location) {
268*795d594fSAndroid Build Coastguard Worker ScopedThreadInfo info(jvmti_env, env, thread);
269*795d594fSAndroid Build Coastguard Worker ScopedMethodInfo method_info(jvmti_env, env, method);
270*795d594fSAndroid Build Coastguard Worker if (!method_info.Init()) {
271*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Unable to get method info!";
272*795d594fSAndroid Build Coastguard Worker return;
273*795d594fSAndroid Build Coastguard Worker }
274*795d594fSAndroid Build Coastguard Worker LOG(WARNING) << "Breakpoint at location: 0x" << std::setw(8) << std::setfill('0') << std::hex
275*795d594fSAndroid Build Coastguard Worker << location << " in method " << method_info << " thread: " << info.GetName();
276*795d594fSAndroid Build Coastguard Worker }
277*795d594fSAndroid Build Coastguard Worker
SubstrOf(const std::string & s,size_t start,size_t end)278*795d594fSAndroid Build Coastguard Worker static std::string SubstrOf(const std::string& s, size_t start, size_t end) {
279*795d594fSAndroid Build Coastguard Worker if (end == std::string::npos) {
280*795d594fSAndroid Build Coastguard Worker end = s.size();
281*795d594fSAndroid Build Coastguard Worker }
282*795d594fSAndroid Build Coastguard Worker if (end == start) {
283*795d594fSAndroid Build Coastguard Worker return "";
284*795d594fSAndroid Build Coastguard Worker }
285*795d594fSAndroid Build Coastguard Worker CHECK_GT(end, start) << "cannot get substr of " << s;
286*795d594fSAndroid Build Coastguard Worker return s.substr(start, end - start);
287*795d594fSAndroid Build Coastguard Worker }
288*795d594fSAndroid Build Coastguard Worker
ParseSingleBreakpoint(const std::string & bp,SingleBreakpointTarget * target)289*795d594fSAndroid Build Coastguard Worker static bool ParseSingleBreakpoint(const std::string& bp, /*out*/SingleBreakpointTarget* target) {
290*795d594fSAndroid Build Coastguard Worker std::string option = bp;
291*795d594fSAndroid Build Coastguard Worker if (option.empty() || option[0] != 'L' || option.find(';') == std::string::npos) {
292*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << option << " doesn't look like it has a class name";
293*795d594fSAndroid Build Coastguard Worker return false;
294*795d594fSAndroid Build Coastguard Worker }
295*795d594fSAndroid Build Coastguard Worker target->class_name = SubstrOf(option, 1, option.find(';'));
296*795d594fSAndroid Build Coastguard Worker
297*795d594fSAndroid Build Coastguard Worker option = SubstrOf(option, option.find(';') + 1, std::string::npos);
298*795d594fSAndroid Build Coastguard Worker if (option.size() < 2 || option[0] != '-' || option[1] != '>') {
299*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << bp << " doesn't seem to indicate a method, expected ->";
300*795d594fSAndroid Build Coastguard Worker return false;
301*795d594fSAndroid Build Coastguard Worker }
302*795d594fSAndroid Build Coastguard Worker option = SubstrOf(option, 2, std::string::npos);
303*795d594fSAndroid Build Coastguard Worker size_t sig_start = option.find('(');
304*795d594fSAndroid Build Coastguard Worker size_t loc_start = option.find('@');
305*795d594fSAndroid Build Coastguard Worker if (option.empty() || sig_start == std::string::npos) {
306*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << bp << " doesn't seem to have a method sig!";
307*795d594fSAndroid Build Coastguard Worker return false;
308*795d594fSAndroid Build Coastguard Worker } else if (loc_start == std::string::npos ||
309*795d594fSAndroid Build Coastguard Worker loc_start < sig_start ||
310*795d594fSAndroid Build Coastguard Worker loc_start + 1 >= option.size()) {
311*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << bp << " doesn't seem to have a valid location!";
312*795d594fSAndroid Build Coastguard Worker return false;
313*795d594fSAndroid Build Coastguard Worker }
314*795d594fSAndroid Build Coastguard Worker target->method_name = SubstrOf(option, 0, sig_start);
315*795d594fSAndroid Build Coastguard Worker target->method_sig = SubstrOf(option, sig_start, loc_start);
316*795d594fSAndroid Build Coastguard Worker target->location = std::stol(SubstrOf(option, loc_start + 1, std::string::npos));
317*795d594fSAndroid Build Coastguard Worker return true;
318*795d594fSAndroid Build Coastguard Worker }
319*795d594fSAndroid Build Coastguard Worker
RemoveLastOption(const std::string & op)320*795d594fSAndroid Build Coastguard Worker static std::string RemoveLastOption(const std::string& op) {
321*795d594fSAndroid Build Coastguard Worker if (op.find(',') == std::string::npos) {
322*795d594fSAndroid Build Coastguard Worker return "";
323*795d594fSAndroid Build Coastguard Worker } else {
324*795d594fSAndroid Build Coastguard Worker return SubstrOf(op, op.find(',') + 1, std::string::npos);
325*795d594fSAndroid Build Coastguard Worker }
326*795d594fSAndroid Build Coastguard Worker }
327*795d594fSAndroid Build Coastguard Worker
328*795d594fSAndroid Build Coastguard Worker // Fills targets with the breakpoints to add.
329*795d594fSAndroid Build Coastguard Worker // Lname/of/Klass;->methodName(Lsig/of/Method)Lreturn/Type;@location,<...>
ParseArgs(const std::string & start_options,BreakpointTargets * targets)330*795d594fSAndroid Build Coastguard Worker static bool ParseArgs(const std::string& start_options,
331*795d594fSAndroid Build Coastguard Worker /*out*/BreakpointTargets* targets) {
332*795d594fSAndroid Build Coastguard Worker for (std::string options = start_options;
333*795d594fSAndroid Build Coastguard Worker !options.empty();
334*795d594fSAndroid Build Coastguard Worker options = RemoveLastOption(options)) {
335*795d594fSAndroid Build Coastguard Worker SingleBreakpointTarget target;
336*795d594fSAndroid Build Coastguard Worker std::string next = SubstrOf(options, 0, options.find(','));
337*795d594fSAndroid Build Coastguard Worker if (!ParseSingleBreakpoint(next, /*out*/ &target)) {
338*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Unable to parse breakpoint from " << next;
339*795d594fSAndroid Build Coastguard Worker return false;
340*795d594fSAndroid Build Coastguard Worker }
341*795d594fSAndroid Build Coastguard Worker targets->bps.push_back(target);
342*795d594fSAndroid Build Coastguard Worker }
343*795d594fSAndroid Build Coastguard Worker return true;
344*795d594fSAndroid Build Coastguard Worker }
345*795d594fSAndroid Build Coastguard Worker
346*795d594fSAndroid Build Coastguard Worker enum class StartType {
347*795d594fSAndroid Build Coastguard Worker OnAttach, OnLoad,
348*795d594fSAndroid Build Coastguard Worker };
349*795d594fSAndroid Build Coastguard Worker
AgentStart(StartType start,JavaVM * vm,char * options,void * reserved)350*795d594fSAndroid Build Coastguard Worker static jint AgentStart(StartType start,
351*795d594fSAndroid Build Coastguard Worker JavaVM* vm,
352*795d594fSAndroid Build Coastguard Worker char* options,
353*795d594fSAndroid Build Coastguard Worker [[maybe_unused]] void* reserved) {
354*795d594fSAndroid Build Coastguard Worker jvmtiEnv* jvmti = nullptr;
355*795d594fSAndroid Build Coastguard Worker jvmtiError error = JVMTI_ERROR_NONE;
356*795d594fSAndroid Build Coastguard Worker {
357*795d594fSAndroid Build Coastguard Worker jint res = 0;
358*795d594fSAndroid Build Coastguard Worker res = vm->GetEnv(reinterpret_cast<void**>(&jvmti), JVMTI_VERSION_1_1);
359*795d594fSAndroid Build Coastguard Worker
360*795d594fSAndroid Build Coastguard Worker if (res != JNI_OK || jvmti == nullptr) {
361*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Unable to access JVMTI, error code " << res;
362*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
363*795d594fSAndroid Build Coastguard Worker }
364*795d594fSAndroid Build Coastguard Worker }
365*795d594fSAndroid Build Coastguard Worker
366*795d594fSAndroid Build Coastguard Worker void* bp_target_mem = nullptr;
367*795d594fSAndroid Build Coastguard Worker error = jvmti->Allocate(sizeof(BreakpointTargets),
368*795d594fSAndroid Build Coastguard Worker reinterpret_cast<unsigned char**>(&bp_target_mem));
369*795d594fSAndroid Build Coastguard Worker if (error != JVMTI_ERROR_NONE) {
370*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Unable to alloc memory for breakpoint target data";
371*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
372*795d594fSAndroid Build Coastguard Worker }
373*795d594fSAndroid Build Coastguard Worker
374*795d594fSAndroid Build Coastguard Worker BreakpointTargets* data = new(bp_target_mem) BreakpointTargets;
375*795d594fSAndroid Build Coastguard Worker error = jvmti->SetEnvironmentLocalStorage(data);
376*795d594fSAndroid Build Coastguard Worker if (error != JVMTI_ERROR_NONE) {
377*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Unable to set local storage";
378*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
379*795d594fSAndroid Build Coastguard Worker }
380*795d594fSAndroid Build Coastguard Worker
381*795d594fSAndroid Build Coastguard Worker if (!ParseArgs(options, /*out*/data)) {
382*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "failed to parse breakpoint list!";
383*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
384*795d594fSAndroid Build Coastguard Worker }
385*795d594fSAndroid Build Coastguard Worker
386*795d594fSAndroid Build Coastguard Worker jvmtiCapabilities caps{};
387*795d594fSAndroid Build Coastguard Worker caps.can_generate_breakpoint_events = JNI_TRUE;
388*795d594fSAndroid Build Coastguard Worker caps.can_get_line_numbers = JNI_TRUE;
389*795d594fSAndroid Build Coastguard Worker caps.can_get_source_file_name = JNI_TRUE;
390*795d594fSAndroid Build Coastguard Worker caps.can_get_source_debug_extension = JNI_TRUE;
391*795d594fSAndroid Build Coastguard Worker error = jvmti->AddCapabilities(&caps);
392*795d594fSAndroid Build Coastguard Worker if (error != JVMTI_ERROR_NONE) {
393*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Unable to set caps";
394*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
395*795d594fSAndroid Build Coastguard Worker }
396*795d594fSAndroid Build Coastguard Worker
397*795d594fSAndroid Build Coastguard Worker jvmtiEventCallbacks callbacks{};
398*795d594fSAndroid Build Coastguard Worker callbacks.Breakpoint = &BreakpointCB;
399*795d594fSAndroid Build Coastguard Worker callbacks.VMInit = &VMInitCB;
400*795d594fSAndroid Build Coastguard Worker
401*795d594fSAndroid Build Coastguard Worker error = jvmti->SetEventCallbacks(&callbacks, static_cast<jint>(sizeof(callbacks)));
402*795d594fSAndroid Build Coastguard Worker
403*795d594fSAndroid Build Coastguard Worker if (error != JVMTI_ERROR_NONE) {
404*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Unable to set event callbacks.";
405*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
406*795d594fSAndroid Build Coastguard Worker }
407*795d594fSAndroid Build Coastguard Worker
408*795d594fSAndroid Build Coastguard Worker error = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
409*795d594fSAndroid Build Coastguard Worker JVMTI_EVENT_BREAKPOINT,
410*795d594fSAndroid Build Coastguard Worker nullptr /* all threads */);
411*795d594fSAndroid Build Coastguard Worker if (error != JVMTI_ERROR_NONE) {
412*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Unable to enable breakpoint event";
413*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
414*795d594fSAndroid Build Coastguard Worker }
415*795d594fSAndroid Build Coastguard Worker if (start == StartType::OnAttach) {
416*795d594fSAndroid Build Coastguard Worker JNIEnv* env = nullptr;
417*795d594fSAndroid Build Coastguard Worker jint res = 0;
418*795d594fSAndroid Build Coastguard Worker res = vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_2);
419*795d594fSAndroid Build Coastguard Worker if (res != JNI_OK || env == nullptr) {
420*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Unable to get jnienv";
421*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
422*795d594fSAndroid Build Coastguard Worker }
423*795d594fSAndroid Build Coastguard Worker VMInitCB(jvmti, env, nullptr);
424*795d594fSAndroid Build Coastguard Worker } else {
425*795d594fSAndroid Build Coastguard Worker error = jvmti->SetEventNotificationMode(JVMTI_ENABLE,
426*795d594fSAndroid Build Coastguard Worker JVMTI_EVENT_VM_INIT,
427*795d594fSAndroid Build Coastguard Worker nullptr /* all threads */);
428*795d594fSAndroid Build Coastguard Worker if (error != JVMTI_ERROR_NONE) {
429*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Unable to set event vminit";
430*795d594fSAndroid Build Coastguard Worker return JNI_ERR;
431*795d594fSAndroid Build Coastguard Worker }
432*795d594fSAndroid Build Coastguard Worker }
433*795d594fSAndroid Build Coastguard Worker return JNI_OK;
434*795d594fSAndroid Build Coastguard Worker }
435*795d594fSAndroid Build Coastguard Worker
436*795d594fSAndroid Build Coastguard Worker // Late attachment (e.g. 'am attach-agent').
Agent_OnAttach(JavaVM * vm,char * options,void * reserved)437*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char* options, void* reserved) {
438*795d594fSAndroid Build Coastguard Worker return AgentStart(StartType::OnAttach, vm, options, reserved);
439*795d594fSAndroid Build Coastguard Worker }
440*795d594fSAndroid Build Coastguard Worker
441*795d594fSAndroid Build Coastguard Worker // Early attachment
Agent_OnLoad(JavaVM * jvm,char * options,void * reserved)442*795d594fSAndroid Build Coastguard Worker extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, char* options, void* reserved) {
443*795d594fSAndroid Build Coastguard Worker return AgentStart(StartType::OnLoad, jvm, options, reserved);
444*795d594fSAndroid Build Coastguard Worker }
445*795d594fSAndroid Build Coastguard Worker
446*795d594fSAndroid Build Coastguard Worker } // namespace breakpoint_logger
447*795d594fSAndroid Build Coastguard Worker
448