| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <sstream> |
| |
| #include "debug_print.h" |
| |
| #include "class_linker.h" |
| #include "class_table.h" |
| #include "class_loader_utils.h" |
| #include "dex/utf.h" |
| #include "gc/heap.h" |
| #include "gc/space/space-inl.h" |
| #include "mirror/class.h" |
| #include "mirror/class_loader-inl.h" |
| #include "runtime.h" |
| #include "scoped_thread_state_change-inl.h" |
| #include "thread-current-inl.h" |
| #include "well_known_classes.h" |
| |
| namespace art { |
| |
| std::string DescribeSpace(ObjPtr<mirror::Class> klass) { |
| std::ostringstream oss; |
| gc::Heap* heap = Runtime::Current()->GetHeap(); |
| gc::space::ContinuousSpace* cs = |
| heap->FindContinuousSpaceFromObject(klass, /* fail_ok= */ true); |
| if (cs != nullptr) { |
| if (cs->IsImageSpace()) { |
| gc::space::ImageSpace* ispace = cs->AsImageSpace(); |
| oss << "image;" << ispace->GetName() << ";" |
| // If the file name is the same as the name, output "+" instead to shorten the output. |
| << (ispace->GetImageFilename() == cs->GetName() ? "+" : ispace->GetImageFilename()) |
| << ";" << static_cast<const void*>(ispace->Begin()); |
| } else { |
| oss << "continuous;" << cs->GetName(); |
| } |
| } else { |
| gc::space::DiscontinuousSpace* ds = |
| heap->FindDiscontinuousSpaceFromObject(klass, /* fail_ok= */ true); |
| if (ds != nullptr) { |
| oss << "discontinuous;" << ds->GetName(); |
| } else { |
| oss << "invalid"; |
| } |
| } |
| return oss.str(); |
| } |
| |
| std::string DescribeLoaders(ObjPtr<mirror::ClassLoader> loader, const char* class_descriptor) { |
| std::ostringstream oss; |
| uint32_t hash = ComputeModifiedUtf8Hash(class_descriptor); |
| ObjPtr<mirror::Class> path_class_loader = |
| WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_PathClassLoader); |
| ObjPtr<mirror::Class> dex_class_loader = |
| WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DexClassLoader); |
| ObjPtr<mirror::Class> delegate_last_class_loader = |
| WellKnownClasses::ToClass(WellKnownClasses::dalvik_system_DelegateLastClassLoader); |
| |
| // Print the class loader chain. |
| bool found_class = false; |
| const char* loader_separator = ""; |
| if (loader == nullptr) { |
| oss << "BootClassLoader"; // This would be unexpected. |
| } |
| for (; loader != nullptr; loader = loader->GetParent()) { |
| ClassTable* table = Runtime::Current()->GetClassLinker()->ClassTableForClassLoader(loader); |
| oss << loader_separator << loader->GetClass()->PrettyDescriptor() |
| << "/" << static_cast<const void*>(table); |
| loader_separator = ";"; |
| // If we didn't find the class yet, try to find it in the current class loader. |
| if (!found_class) { |
| ObjPtr<mirror::Class> klass = |
| (table != nullptr) ? table->Lookup(class_descriptor, hash) : nullptr; |
| if (klass != nullptr) { |
| found_class = true; |
| oss << "[hit:" << DescribeSpace(klass) << "]"; |
| } |
| } |
| |
| // For PathClassLoader, DexClassLoader or DelegateLastClassLoader |
| // also dump the dex file locations. |
| if (loader->GetClass() == path_class_loader || |
| loader->GetClass() == dex_class_loader || |
| loader->GetClass() == delegate_last_class_loader) { |
| oss << "("; |
| ScopedObjectAccessUnchecked soa(Thread::Current()); |
| StackHandleScope<1> hs(soa.Self()); |
| Handle<mirror::ClassLoader> handle(hs.NewHandle(loader)); |
| const char* path_separator = ""; |
| const DexFile* base_dex_file = nullptr; |
| VisitClassLoaderDexFiles( |
| soa, |
| handle, |
| [&](const DexFile* dex_file) { |
| oss << path_separator; |
| path_separator = ":"; |
| if (base_dex_file != nullptr && |
| dex_file->GetLocation().length() > base_dex_file->GetLocation().length() && |
| dex_file->GetLocation().compare(0u, |
| base_dex_file->GetLocation().length(), |
| base_dex_file->GetLocation()) == 0) { |
| // Replace the base location with "+" to shorten the output. |
| oss << "+" << dex_file->GetLocation().substr(base_dex_file->GetLocation().length()); |
| } else { |
| oss << dex_file->GetLocation(); |
| base_dex_file = dex_file; |
| } |
| oss << "/" << static_cast<const void*>(dex_file); |
| return true; // Continue with the next DexFile. |
| }); |
| oss << ")"; |
| } |
| } |
| |
| return oss.str(); |
| } |
| |
| void DumpB77342775DebugData(ObjPtr<mirror::Class> target_class, ObjPtr<mirror::Class> src_class) { |
| std::string target_descriptor_storage; |
| const char* target_descriptor = target_class->GetDescriptor(&target_descriptor_storage); |
| const char kCheckedPrefix[] = "Lorg/apache/http/"; |
| // Avoid spam for other packages. (That spam would break some ART run-tests for example.) |
| if (strncmp(target_descriptor, kCheckedPrefix, sizeof(kCheckedPrefix) - 1) != 0) { |
| return; |
| } |
| auto matcher = [target_descriptor, target_class](ObjPtr<mirror::Class> klass) |
| REQUIRES_SHARED(Locks::mutator_lock_) { |
| if (klass->DescriptorEquals(target_descriptor)) { |
| LOG(ERROR) << " descriptor match in " |
| << DescribeLoaders(klass->GetClassLoader(), target_descriptor) |
| << " match? " << std::boolalpha << (klass == target_class); |
| } |
| }; |
| |
| std::string source_descriptor_storage; |
| const char* source_descriptor = src_class->GetDescriptor(&source_descriptor_storage); |
| |
| LOG(ERROR) << "Maybe bug 77342775, looking for " << target_descriptor |
| << " " << target_class.Ptr() << "[" << DescribeSpace(target_class) << "]" |
| << " defined in " << target_class->GetDexFile().GetLocation() |
| << "/" << static_cast<const void*>(&target_class->GetDexFile()) |
| << "\n with loader: " << DescribeLoaders(target_class->GetClassLoader(), target_descriptor); |
| if (target_class->IsInterface()) { |
| ObjPtr<mirror::IfTable> iftable = src_class->GetIfTable(); |
| CHECK(iftable != nullptr); |
| size_t ifcount = iftable->Count(); |
| LOG(ERROR) << " in interface table for " << source_descriptor |
| << " " << src_class.Ptr() << "[" << DescribeSpace(src_class) << "]" |
| << " defined in " << src_class->GetDexFile().GetLocation() |
| << "/" << static_cast<const void*>(&src_class->GetDexFile()) |
| << " ifcount=" << ifcount |
| << "\n with loader " << DescribeLoaders(src_class->GetClassLoader(), source_descriptor); |
| for (size_t i = 0; i != ifcount; ++i) { |
| ObjPtr<mirror::Class> iface = iftable->GetInterface(i); |
| CHECK(iface != nullptr); |
| LOG(ERROR) << " iface #" << i << ": " << iface->PrettyDescriptor(); |
| matcher(iface); |
| } |
| } else { |
| LOG(ERROR) << " in superclass chain for " << source_descriptor |
| << " " << src_class.Ptr() << "[" << DescribeSpace(src_class) << "]" |
| << " defined in " << src_class->GetDexFile().GetLocation() |
| << "/" << static_cast<const void*>(&src_class->GetDexFile()) |
| << "\n with loader " << DescribeLoaders(src_class->GetClassLoader(), source_descriptor); |
| for (ObjPtr<mirror::Class> klass = src_class; |
| klass != nullptr; |
| klass = klass->GetSuperClass()) { |
| LOG(ERROR) << " - " << klass->PrettyDescriptor(); |
| matcher(klass); |
| } |
| } |
| } |
| |
| } // namespace art |