xref: /aosp_15_r20/frameworks/base/tools/dump-coverage/dump_coverage.cc (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker // Copyright (C) 2018 The Android Open Source Project
2*d57664e9SAndroid Build Coastguard Worker //
3*d57664e9SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*d57664e9SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*d57664e9SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*d57664e9SAndroid Build Coastguard Worker //
7*d57664e9SAndroid Build Coastguard Worker //      http://www.apache.org/licenses/LICENSE-2.0
8*d57664e9SAndroid Build Coastguard Worker //
9*d57664e9SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*d57664e9SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*d57664e9SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*d57664e9SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*d57664e9SAndroid Build Coastguard Worker // limitations under the License.
14*d57664e9SAndroid Build Coastguard Worker //
15*d57664e9SAndroid Build Coastguard Worker 
16*d57664e9SAndroid Build Coastguard Worker #include <android-base/logging.h>
17*d57664e9SAndroid Build Coastguard Worker #include <jni.h>
18*d57664e9SAndroid Build Coastguard Worker #include <jvmti.h>
19*d57664e9SAndroid Build Coastguard Worker #include <string.h>
20*d57664e9SAndroid Build Coastguard Worker 
21*d57664e9SAndroid Build Coastguard Worker #include <fstream>
22*d57664e9SAndroid Build Coastguard Worker 
23*d57664e9SAndroid Build Coastguard Worker using std::get;
24*d57664e9SAndroid Build Coastguard Worker using std::tuple;
25*d57664e9SAndroid Build Coastguard Worker 
26*d57664e9SAndroid Build Coastguard Worker namespace dump_coverage {
27*d57664e9SAndroid Build Coastguard Worker 
28*d57664e9SAndroid Build Coastguard Worker #define CHECK_JVMTI(x) CHECK_EQ((x), JVMTI_ERROR_NONE)
29*d57664e9SAndroid Build Coastguard Worker #define CHECK_NOTNULL(x) CHECK((x) != nullptr)
30*d57664e9SAndroid Build Coastguard Worker #define CHECK_NO_EXCEPTION(env) CHECK(!(env)->ExceptionCheck());
31*d57664e9SAndroid Build Coastguard Worker 
32*d57664e9SAndroid Build Coastguard Worker static JavaVM* java_vm = nullptr;
33*d57664e9SAndroid Build Coastguard Worker 
34*d57664e9SAndroid Build Coastguard Worker // Get the current JNI environment.
GetJNIEnv()35*d57664e9SAndroid Build Coastguard Worker static JNIEnv* GetJNIEnv() {
36*d57664e9SAndroid Build Coastguard Worker   JNIEnv* env = nullptr;
37*d57664e9SAndroid Build Coastguard Worker   CHECK_EQ(java_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6),
38*d57664e9SAndroid Build Coastguard Worker            JNI_OK);
39*d57664e9SAndroid Build Coastguard Worker   return env;
40*d57664e9SAndroid Build Coastguard Worker }
41*d57664e9SAndroid Build Coastguard Worker 
42*d57664e9SAndroid Build Coastguard Worker // Get the JaCoCo Agent class and an instance of the class, given a JNI
43*d57664e9SAndroid Build Coastguard Worker // environment.
44*d57664e9SAndroid Build Coastguard Worker // Will crash if the Agent isn't found or if any Java Exception occurs.
GetJavaAgent(JNIEnv * env)45*d57664e9SAndroid Build Coastguard Worker static tuple<jclass, jobject> GetJavaAgent(JNIEnv* env) {
46*d57664e9SAndroid Build Coastguard Worker   jclass java_agent_class =
47*d57664e9SAndroid Build Coastguard Worker       env->FindClass("org/jacoco/agent/rt/internal/Agent");
48*d57664e9SAndroid Build Coastguard Worker   CHECK_NOTNULL(java_agent_class);
49*d57664e9SAndroid Build Coastguard Worker 
50*d57664e9SAndroid Build Coastguard Worker   jmethodID java_agent_get_instance =
51*d57664e9SAndroid Build Coastguard Worker       env->GetStaticMethodID(java_agent_class, "getInstance",
52*d57664e9SAndroid Build Coastguard Worker                              "()Lorg/jacoco/agent/rt/internal/Agent;");
53*d57664e9SAndroid Build Coastguard Worker   CHECK_NOTNULL(java_agent_get_instance);
54*d57664e9SAndroid Build Coastguard Worker 
55*d57664e9SAndroid Build Coastguard Worker   jobject java_agent_instance =
56*d57664e9SAndroid Build Coastguard Worker       env->CallStaticObjectMethod(java_agent_class, java_agent_get_instance);
57*d57664e9SAndroid Build Coastguard Worker   CHECK_NO_EXCEPTION(env);
58*d57664e9SAndroid Build Coastguard Worker   CHECK_NOTNULL(java_agent_instance);
59*d57664e9SAndroid Build Coastguard Worker 
60*d57664e9SAndroid Build Coastguard Worker   return tuple(java_agent_class, java_agent_instance);
61*d57664e9SAndroid Build Coastguard Worker }
62*d57664e9SAndroid Build Coastguard Worker 
63*d57664e9SAndroid Build Coastguard Worker // Runs equivalent of Agent.getInstance().getExecutionData(false) and returns
64*d57664e9SAndroid Build Coastguard Worker // the result.
65*d57664e9SAndroid Build Coastguard Worker // Will crash if the Agent isn't found or if any Java Exception occurs.
GetExecutionData(JNIEnv * env)66*d57664e9SAndroid Build Coastguard Worker static jbyteArray GetExecutionData(JNIEnv* env) {
67*d57664e9SAndroid Build Coastguard Worker   auto java_agent = GetJavaAgent(env);
68*d57664e9SAndroid Build Coastguard Worker   jmethodID java_agent_get_execution_data =
69*d57664e9SAndroid Build Coastguard Worker       env->GetMethodID(get<0>(java_agent), "getExecutionData", "(Z)[B");
70*d57664e9SAndroid Build Coastguard Worker   CHECK_NO_EXCEPTION(env);
71*d57664e9SAndroid Build Coastguard Worker   CHECK_NOTNULL(java_agent_get_execution_data);
72*d57664e9SAndroid Build Coastguard Worker 
73*d57664e9SAndroid Build Coastguard Worker   jbyteArray java_result_array = (jbyteArray)env->CallObjectMethod(
74*d57664e9SAndroid Build Coastguard Worker       get<1>(java_agent), java_agent_get_execution_data, false);
75*d57664e9SAndroid Build Coastguard Worker   CHECK_NO_EXCEPTION(env);
76*d57664e9SAndroid Build Coastguard Worker 
77*d57664e9SAndroid Build Coastguard Worker   return java_result_array;
78*d57664e9SAndroid Build Coastguard Worker }
79*d57664e9SAndroid Build Coastguard Worker 
80*d57664e9SAndroid Build Coastguard Worker // Writes the execution data to a file.
81*d57664e9SAndroid Build Coastguard Worker //  data, length: represent the data, as a sequence of bytes.
82*d57664e9SAndroid Build Coastguard Worker //  filename: file to write coverage data to.
83*d57664e9SAndroid Build Coastguard Worker //  returns JNI_ERR if there is an error in writing the file, otherwise JNI_OK.
WriteFile(const char * data,int length,const std::string & filename)84*d57664e9SAndroid Build Coastguard Worker static jint WriteFile(const char* data, int length, const std::string& filename) {
85*d57664e9SAndroid Build Coastguard Worker   LOG(INFO) << "Writing file of length " << length << " to '" << filename
86*d57664e9SAndroid Build Coastguard Worker             << "'";
87*d57664e9SAndroid Build Coastguard Worker   std::ofstream file(filename, std::ios::binary);
88*d57664e9SAndroid Build Coastguard Worker 
89*d57664e9SAndroid Build Coastguard Worker   if (!file.is_open()) {
90*d57664e9SAndroid Build Coastguard Worker     LOG(ERROR) << "Could not open file: '" << filename << "'";
91*d57664e9SAndroid Build Coastguard Worker     return JNI_ERR;
92*d57664e9SAndroid Build Coastguard Worker   }
93*d57664e9SAndroid Build Coastguard Worker   file.write(data, length);
94*d57664e9SAndroid Build Coastguard Worker   file.close();
95*d57664e9SAndroid Build Coastguard Worker 
96*d57664e9SAndroid Build Coastguard Worker   if (!file) {
97*d57664e9SAndroid Build Coastguard Worker     LOG(ERROR) << "I/O error in reading file";
98*d57664e9SAndroid Build Coastguard Worker     return JNI_ERR;
99*d57664e9SAndroid Build Coastguard Worker   }
100*d57664e9SAndroid Build Coastguard Worker 
101*d57664e9SAndroid Build Coastguard Worker   LOG(INFO) << "Done writing file";
102*d57664e9SAndroid Build Coastguard Worker   return JNI_OK;
103*d57664e9SAndroid Build Coastguard Worker }
104*d57664e9SAndroid Build Coastguard Worker 
105*d57664e9SAndroid Build Coastguard Worker // Grabs execution data and writes it to a file.
106*d57664e9SAndroid Build Coastguard Worker //  filename: file to write coverage data to.
107*d57664e9SAndroid Build Coastguard Worker //  returns JNI_ERR if there is an error writing the file.
108*d57664e9SAndroid Build Coastguard Worker // Will crash if the Agent isn't found or if any Java Exception occurs.
Dump(const std::string & filename)109*d57664e9SAndroid Build Coastguard Worker static jint Dump(const std::string& filename) {
110*d57664e9SAndroid Build Coastguard Worker   LOG(INFO) << "Dumping file";
111*d57664e9SAndroid Build Coastguard Worker 
112*d57664e9SAndroid Build Coastguard Worker   JNIEnv* env = GetJNIEnv();
113*d57664e9SAndroid Build Coastguard Worker   jbyteArray java_result_array = GetExecutionData(env);
114*d57664e9SAndroid Build Coastguard Worker   CHECK_NOTNULL(java_result_array);
115*d57664e9SAndroid Build Coastguard Worker 
116*d57664e9SAndroid Build Coastguard Worker   jbyte* result_ptr = env->GetByteArrayElements(java_result_array, 0);
117*d57664e9SAndroid Build Coastguard Worker   CHECK_NOTNULL(result_ptr);
118*d57664e9SAndroid Build Coastguard Worker 
119*d57664e9SAndroid Build Coastguard Worker   int result_len = env->GetArrayLength(java_result_array);
120*d57664e9SAndroid Build Coastguard Worker 
121*d57664e9SAndroid Build Coastguard Worker   return WriteFile((const char*) result_ptr, result_len, filename);
122*d57664e9SAndroid Build Coastguard Worker }
123*d57664e9SAndroid Build Coastguard Worker 
124*d57664e9SAndroid Build Coastguard Worker // Resets execution data, performing the equivalent of
125*d57664e9SAndroid Build Coastguard Worker //  Agent.getInstance().reset();
126*d57664e9SAndroid Build Coastguard Worker //  args: should be empty.
127*d57664e9SAndroid Build Coastguard Worker //  returns JNI_ERR if the arguments are invalid.
128*d57664e9SAndroid Build Coastguard Worker // Will crash if the Agent isn't found or if any Java Exception occurs.
Reset(const std::string & args)129*d57664e9SAndroid Build Coastguard Worker static jint Reset(const std::string& args) {
130*d57664e9SAndroid Build Coastguard Worker   if (args != "") {
131*d57664e9SAndroid Build Coastguard Worker     LOG(ERROR) << "reset takes no arguments, but received '" << args << "'";
132*d57664e9SAndroid Build Coastguard Worker     return JNI_ERR;
133*d57664e9SAndroid Build Coastguard Worker   }
134*d57664e9SAndroid Build Coastguard Worker 
135*d57664e9SAndroid Build Coastguard Worker   JNIEnv* env = GetJNIEnv();
136*d57664e9SAndroid Build Coastguard Worker   auto java_agent = GetJavaAgent(env);
137*d57664e9SAndroid Build Coastguard Worker 
138*d57664e9SAndroid Build Coastguard Worker   jmethodID java_agent_reset =
139*d57664e9SAndroid Build Coastguard Worker       env->GetMethodID(get<0>(java_agent), "reset", "()V");
140*d57664e9SAndroid Build Coastguard Worker   CHECK_NOTNULL(java_agent_reset);
141*d57664e9SAndroid Build Coastguard Worker 
142*d57664e9SAndroid Build Coastguard Worker   env->CallVoidMethod(get<1>(java_agent), java_agent_reset);
143*d57664e9SAndroid Build Coastguard Worker   CHECK_NO_EXCEPTION(env);
144*d57664e9SAndroid Build Coastguard Worker   return JNI_OK;
145*d57664e9SAndroid Build Coastguard Worker }
146*d57664e9SAndroid Build Coastguard Worker 
147*d57664e9SAndroid Build Coastguard Worker // Given a string of the form "<a>:<b>" returns (<a>, <b>).
148*d57664e9SAndroid Build Coastguard Worker // Given a string <a> that doesn't contain a colon, returns (<a>, "").
SplitOnColon(const std::string & options)149*d57664e9SAndroid Build Coastguard Worker static tuple<std::string, std::string> SplitOnColon(const std::string& options) {
150*d57664e9SAndroid Build Coastguard Worker   size_t loc_delim = options.find(':');
151*d57664e9SAndroid Build Coastguard Worker   std::string command, args;
152*d57664e9SAndroid Build Coastguard Worker 
153*d57664e9SAndroid Build Coastguard Worker   if (loc_delim == std::string::npos) {
154*d57664e9SAndroid Build Coastguard Worker     command = options;
155*d57664e9SAndroid Build Coastguard Worker   } else {
156*d57664e9SAndroid Build Coastguard Worker     command = options.substr(0, loc_delim);
157*d57664e9SAndroid Build Coastguard Worker     args = options.substr(loc_delim + 1, options.length());
158*d57664e9SAndroid Build Coastguard Worker   }
159*d57664e9SAndroid Build Coastguard Worker   return tuple(command, args);
160*d57664e9SAndroid Build Coastguard Worker }
161*d57664e9SAndroid Build Coastguard Worker 
162*d57664e9SAndroid Build Coastguard Worker // Parses and executes a command specified by options of the form
163*d57664e9SAndroid Build Coastguard Worker // "<command>:<args>" where <command> is either "dump" or "reset".
ParseOptionsAndExecuteCommand(const std::string & options)164*d57664e9SAndroid Build Coastguard Worker static jint ParseOptionsAndExecuteCommand(const std::string& options) {
165*d57664e9SAndroid Build Coastguard Worker   auto split = SplitOnColon(options);
166*d57664e9SAndroid Build Coastguard Worker   auto command = get<0>(split), args = get<1>(split);
167*d57664e9SAndroid Build Coastguard Worker 
168*d57664e9SAndroid Build Coastguard Worker   LOG(INFO) << "command: '" << command << "' args: '" << args << "'";
169*d57664e9SAndroid Build Coastguard Worker 
170*d57664e9SAndroid Build Coastguard Worker   if (command == "dump") {
171*d57664e9SAndroid Build Coastguard Worker     return Dump(args);
172*d57664e9SAndroid Build Coastguard Worker   }
173*d57664e9SAndroid Build Coastguard Worker 
174*d57664e9SAndroid Build Coastguard Worker   if (command == "reset") {
175*d57664e9SAndroid Build Coastguard Worker     return Reset(args);
176*d57664e9SAndroid Build Coastguard Worker   }
177*d57664e9SAndroid Build Coastguard Worker 
178*d57664e9SAndroid Build Coastguard Worker   LOG(ERROR) << "Invalid command: expected 'dump' or 'reset' but was '"
179*d57664e9SAndroid Build Coastguard Worker              << command << "'";
180*d57664e9SAndroid Build Coastguard Worker   return JNI_ERR;
181*d57664e9SAndroid Build Coastguard Worker }
182*d57664e9SAndroid Build Coastguard Worker 
AgentStart(JavaVM * vm,char * options)183*d57664e9SAndroid Build Coastguard Worker static jint AgentStart(JavaVM* vm, char* options) {
184*d57664e9SAndroid Build Coastguard Worker   android::base::InitLogging(/* argv= */ nullptr);
185*d57664e9SAndroid Build Coastguard Worker   java_vm = vm;
186*d57664e9SAndroid Build Coastguard Worker 
187*d57664e9SAndroid Build Coastguard Worker   return ParseOptionsAndExecuteCommand(options);
188*d57664e9SAndroid Build Coastguard Worker }
189*d57664e9SAndroid Build Coastguard Worker 
190*d57664e9SAndroid Build Coastguard Worker // Late attachment (e.g. 'am attach-agent').
191*d57664e9SAndroid Build Coastguard Worker extern "C" JNIEXPORT jint JNICALL
Agent_OnAttach(JavaVM * vm,char * options,void * reserved ATTRIBUTE_UNUSED)192*d57664e9SAndroid Build Coastguard Worker Agent_OnAttach(JavaVM* vm, char* options, void* reserved ATTRIBUTE_UNUSED) {
193*d57664e9SAndroid Build Coastguard Worker   return AgentStart(vm, options);
194*d57664e9SAndroid Build Coastguard Worker }
195*d57664e9SAndroid Build Coastguard Worker 
196*d57664e9SAndroid Build Coastguard Worker // Early attachment.
197*d57664e9SAndroid Build Coastguard Worker extern "C" JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM * jvm ATTRIBUTE_UNUSED,char * options ATTRIBUTE_UNUSED,void * reserved ATTRIBUTE_UNUSED)198*d57664e9SAndroid Build Coastguard Worker Agent_OnLoad(JavaVM* jvm ATTRIBUTE_UNUSED, char* options ATTRIBUTE_UNUSED, void* reserved ATTRIBUTE_UNUSED) {
199*d57664e9SAndroid Build Coastguard Worker   LOG(ERROR)
200*d57664e9SAndroid Build Coastguard Worker     << "The dumpcoverage agent will not work on load,"
201*d57664e9SAndroid Build Coastguard Worker     << " as it does not have access to the runtime.";
202*d57664e9SAndroid Build Coastguard Worker   return JNI_ERR;
203*d57664e9SAndroid Build Coastguard Worker }
204*d57664e9SAndroid Build Coastguard Worker 
205*d57664e9SAndroid Build Coastguard Worker }  // namespace dump_coverage
206