1 // Copyright (C) 2018 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 <android-base/logging.h> 17 18 #include <atomic> 19 #include <iomanip> 20 #include <iostream> 21 #include <istream> 22 #include <jni.h> 23 #include <jvmti.h> 24 #include <memory> 25 #include <sstream> 26 #include <string.h> 27 #include <string> 28 #include <vector> 29 30 namespace fieldnull { 31 32 #define CHECK_JVMTI(x) CHECK_EQ((x), JVMTI_ERROR_NONE) 33 34 // Special art ti-version number. We will use this as a fallback if we cannot get a regular JVMTI 35 // env. 36 static constexpr jint kArtTiVersion = JVMTI_VERSION_1_2 | 0x40000000; 37 38 static JavaVM* java_vm = nullptr; 39 40 // Field is "Lclass/name/here;.field_name:Lfield/type/here;" 41 static std::pair<jclass, jfieldID> SplitField(JNIEnv* env, const std::string& field_id) { 42 CHECK_EQ(field_id[0], 'L'); 43 env->PushLocalFrame(1); 44 std::istringstream is(field_id); 45 std::string class_name; 46 std::string field_name; 47 std::string field_type; 48 49 std::getline(is, class_name, '.'); 50 std::getline(is, field_name, ':'); 51 std::getline(is, field_type, '\0'); 52 53 jclass klass = reinterpret_cast<jclass>( 54 env->NewGlobalRef(env->FindClass(class_name.substr(1, class_name.size() - 2).c_str()))); 55 jfieldID field = env->GetFieldID(klass, field_name.c_str(), field_type.c_str()); 56 CHECK(klass != nullptr); 57 CHECK(field != nullptr); 58 LOG(INFO) << "listing field " << field_id; 59 env->PopLocalFrame(nullptr); 60 return std::make_pair(klass, field); 61 } 62 63 static std::vector<std::pair<jclass, jfieldID>> GetRequestedFields(JNIEnv* env, 64 const std::string& args) { 65 std::vector<std::pair<jclass, jfieldID>> res; 66 std::stringstream args_stream(args); 67 std::string item; 68 while (std::getline(args_stream, item, ',')) { 69 if (item == "") { 70 continue; 71 } 72 res.push_back(SplitField(env, item)); 73 } 74 return res; 75 } 76 77 static jint SetupJvmtiEnv(JavaVM* vm, jvmtiEnv** jvmti) { 78 jint res = 0; 79 res = vm->GetEnv(reinterpret_cast<void**>(jvmti), JVMTI_VERSION_1_1); 80 81 if (res != JNI_OK || *jvmti == nullptr) { 82 LOG(ERROR) << "Unable to access JVMTI, error code " << res; 83 return vm->GetEnv(reinterpret_cast<void**>(jvmti), kArtTiVersion); 84 } 85 return res; 86 } 87 88 struct RequestList { 89 std::vector<std::pair<jclass, jfieldID>> fields_; 90 }; 91 92 static void DataDumpRequestCb(jvmtiEnv* jvmti) { 93 JNIEnv* env = nullptr; 94 CHECK_EQ(java_vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6), JNI_OK); 95 LOG(INFO) << "Dumping counts of null fields."; 96 LOG(INFO) << "\t" << "Field name" 97 << "\t" << "null count" 98 << "\t" << "total count"; 99 RequestList* list; 100 CHECK_JVMTI(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&list))); 101 for (std::pair<jclass, jfieldID>& p : list->fields_) { 102 jclass klass = p.first; 103 jfieldID field = p.second; 104 // Make sure all instances of the class are tagged with the klass ptr value. Since this is a 105 // global ref it's guaranteed to be unique. 106 CHECK_JVMTI(jvmti->IterateOverInstancesOfClass( 107 p.first, 108 // We need to do this to all objects every time since we might be looking for multiple 109 // fields in classes that are subtypes of each other. 110 JVMTI_HEAP_OBJECT_EITHER, 111 /* class_tag, size, tag_ptr, user_data*/ 112 [](jlong, jlong, jlong* tag_ptr, void* klass) -> jvmtiIterationControl { 113 *tag_ptr = static_cast<jlong>(reinterpret_cast<intptr_t>(klass)); 114 return JVMTI_ITERATION_CONTINUE; 115 }, 116 klass)); 117 jobject* obj_list; 118 jint obj_len; 119 jlong tag = static_cast<jlong>(reinterpret_cast<intptr_t>(klass)); 120 CHECK_JVMTI(jvmti->GetObjectsWithTags(1, &tag, &obj_len, &obj_list, nullptr)); 121 122 uint64_t null_cnt = 0; 123 for (jint i = 0; i < obj_len; i++) { 124 if (env->GetObjectField(obj_list[i], field) == nullptr) { 125 null_cnt++; 126 } 127 } 128 129 char* field_name; 130 char* field_sig; 131 char* class_name; 132 CHECK_JVMTI(jvmti->GetFieldName(klass, field, &field_name, &field_sig, nullptr)); 133 CHECK_JVMTI(jvmti->GetClassSignature(klass, &class_name, nullptr)); 134 LOG(INFO) << "\t" << class_name << "." << field_name << ":" << field_sig 135 << "\t" << null_cnt 136 << "\t" << obj_len; 137 CHECK_JVMTI(jvmti->Deallocate(reinterpret_cast<unsigned char*>(field_name))); 138 CHECK_JVMTI(jvmti->Deallocate(reinterpret_cast<unsigned char*>(field_sig))); 139 CHECK_JVMTI(jvmti->Deallocate(reinterpret_cast<unsigned char*>(class_name))); 140 } 141 } 142 143 static void VMDeathCb(jvmtiEnv* jvmti, JNIEnv* env ATTRIBUTE_UNUSED) { 144 DataDumpRequestCb(jvmti); 145 RequestList* list = nullptr; 146 CHECK_JVMTI(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&list))); 147 delete list; 148 } 149 150 static void CreateFieldList(jvmtiEnv* jvmti, JNIEnv* env, const std::string& args) { 151 RequestList* list = nullptr; 152 CHECK_JVMTI(jvmti->Allocate(sizeof(*list), reinterpret_cast<unsigned char**>(&list))); 153 new (list) RequestList { .fields_ = GetRequestedFields(env, args), }; 154 CHECK_JVMTI(jvmti->SetEnvironmentLocalStorage(list)); 155 } 156 157 static void VMInitCb(jvmtiEnv* jvmti, JNIEnv* env, jobject thr ATTRIBUTE_UNUSED) { 158 char* args = nullptr; 159 CHECK_JVMTI(jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&args))); 160 CHECK_JVMTI(jvmti->SetEnvironmentLocalStorage(nullptr)); 161 CreateFieldList(jvmti, env, args); 162 CHECK_JVMTI(jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, nullptr)); 163 CHECK_JVMTI(jvmti->SetEventNotificationMode(JVMTI_ENABLE, 164 JVMTI_EVENT_DATA_DUMP_REQUEST, 165 nullptr)); 166 CHECK_JVMTI(jvmti->Deallocate(reinterpret_cast<unsigned char*>(args))); 167 } 168 169 static jint AgentStart(JavaVM* vm, char* options, bool is_onload) { 170 android::base::InitLogging(/* argv= */nullptr); 171 java_vm = vm; 172 jvmtiEnv* jvmti = nullptr; 173 if (SetupJvmtiEnv(vm, &jvmti) != JNI_OK) { 174 LOG(ERROR) << "Could not get JVMTI env or ArtTiEnv!"; 175 return JNI_ERR; 176 } 177 jvmtiCapabilities caps { .can_tag_objects = 1, }; 178 CHECK_JVMTI(jvmti->AddCapabilities(&caps)); 179 jvmtiEventCallbacks cb { 180 .VMInit = VMInitCb, 181 .DataDumpRequest = DataDumpRequestCb, 182 .VMDeath = VMDeathCb, 183 }; 184 CHECK_JVMTI(jvmti->SetEventCallbacks(&cb, sizeof(cb))); 185 if (is_onload) { 186 unsigned char* ptr = nullptr; 187 CHECK_JVMTI(jvmti->Allocate(strlen(options) + 1, &ptr)); 188 strcpy(reinterpret_cast<char*>(ptr), options); 189 CHECK_JVMTI(jvmti->SetEnvironmentLocalStorage(ptr)); 190 CHECK_JVMTI(jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, nullptr)); 191 } else { 192 JNIEnv* env = nullptr; 193 CHECK_EQ(vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6), JNI_OK); 194 CreateFieldList(jvmti, env, options); 195 CHECK_JVMTI(jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, nullptr)); 196 CHECK_JVMTI(jvmti->SetEventNotificationMode(JVMTI_ENABLE, 197 JVMTI_EVENT_DATA_DUMP_REQUEST, 198 nullptr)); 199 } 200 return JNI_OK; 201 } 202 203 // Late attachment (e.g. 'am attach-agent'). 204 extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, 205 char* options, 206 void* reserved ATTRIBUTE_UNUSED) { 207 return AgentStart(vm, options, /*is_onload=*/false); 208 } 209 210 // Early attachment 211 extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* jvm, 212 char* options, 213 void* reserved ATTRIBUTE_UNUSED) { 214 return AgentStart(jvm, options, /*is_onload=*/true); 215 } 216 217 } // namespace fieldnull 218 219