1 // Copyright 2015 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "base/android/library_loader/library_prefetcher.h" 6 7 #include <stddef.h> 8 #include <sys/mman.h> 9 #include <sys/resource.h> 10 #include <sys/wait.h> 11 #include <unistd.h> 12 #include <algorithm> 13 #include <utility> 14 #include <vector> 15 16 #include "base/macros.h" 17 #include "base/posix/eintr_wrapper.h" 18 #include "base/strings/string_util.h" 19 #include "build/build_config.h" 20 21 namespace base { 22 namespace android { 23 24 namespace { 25 26 // Android defines the background priority to this value since at least 2009 27 // (see Process.java). 28 const int kBackgroundPriority = 10; 29 // Valid for all the Android architectures. 30 const size_t kPageSize = 4096; 31 const char* kLibchromeSuffix = "libchrome.so"; 32 // "base.apk" is a suffix because the library may be loaded directly from the 33 // APK. 34 const char* kSuffixesToMatch[] = {kLibchromeSuffix, "base.apk"}; 35 36 bool IsReadableAndPrivate(const base::debug::MappedMemoryRegion& region) { 37 return region.permissions & base::debug::MappedMemoryRegion::READ && 38 region.permissions & base::debug::MappedMemoryRegion::PRIVATE; 39 } 40 41 bool PathMatchesSuffix(const std::string& path) { 42 for (size_t i = 0; i < arraysize(kSuffixesToMatch); i++) { 43 if (EndsWith(path, kSuffixesToMatch[i], CompareCase::SENSITIVE)) { 44 return true; 45 } 46 } 47 return false; 48 } 49 50 // For each range, reads a byte per page to force it into the page cache. 51 // Heap allocations, syscalls and library functions are not allowed in this 52 // function. 53 // Returns true for success. 54 #if defined(ADDRESS_SANITIZER) 55 // Disable AddressSanitizer instrumentation for this function. It is touching 56 // memory that hasn't been allocated by the app, though the addresses are 57 // valid. Furthermore, this takes place in a child process. See crbug.com/653372 58 // for the context. 59 __attribute__((no_sanitize_address)) 60 #endif 61 bool Prefetch(const std::vector<std::pair<uintptr_t, uintptr_t>>& ranges) { 62 for (const auto& range : ranges) { 63 const uintptr_t page_mask = kPageSize - 1; 64 // If start or end is not page-aligned, parsing went wrong. It is better to 65 // exit with an error. 66 if ((range.first & page_mask) || (range.second & page_mask)) { 67 return false; // CHECK() is not allowed here. 68 } 69 unsigned char* start_ptr = reinterpret_cast<unsigned char*>(range.first); 70 unsigned char* end_ptr = reinterpret_cast<unsigned char*>(range.second); 71 unsigned char dummy = 0; 72 for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kPageSize) { 73 // Volatile is required to prevent the compiler from eliminating this 74 // loop. 75 dummy ^= *static_cast<volatile unsigned char*>(ptr); 76 } 77 } 78 return true; 79 } 80 81 } // namespace 82 83 // static 84 bool NativeLibraryPrefetcher::IsGoodToPrefetch( 85 const base::debug::MappedMemoryRegion& region) { 86 return PathMatchesSuffix(region.path) && 87 IsReadableAndPrivate(region); // .text and .data mappings are private. 88 } 89 90 // static 91 void NativeLibraryPrefetcher::FilterLibchromeRangesOnlyIfPossible( 92 const std::vector<base::debug::MappedMemoryRegion>& regions, 93 std::vector<AddressRange>* ranges) { 94 bool has_libchrome_region = false; 95 for (const base::debug::MappedMemoryRegion& region : regions) { 96 if (EndsWith(region.path, kLibchromeSuffix, CompareCase::SENSITIVE)) { 97 has_libchrome_region = true; 98 break; 99 } 100 } 101 for (const base::debug::MappedMemoryRegion& region : regions) { 102 if (has_libchrome_region && 103 !EndsWith(region.path, kLibchromeSuffix, CompareCase::SENSITIVE)) { 104 continue; 105 } 106 ranges->push_back(std::make_pair(region.start, region.end)); 107 } 108 } 109 110 // static 111 bool NativeLibraryPrefetcher::FindRanges(std::vector<AddressRange>* ranges) { 112 std::string proc_maps; 113 if (!base::debug::ReadProcMaps(&proc_maps)) 114 return false; 115 std::vector<base::debug::MappedMemoryRegion> regions; 116 if (!base::debug::ParseProcMaps(proc_maps, ®ions)) 117 return false; 118 119 std::vector<base::debug::MappedMemoryRegion> regions_to_prefetch; 120 for (const auto& region : regions) { 121 if (IsGoodToPrefetch(region)) { 122 regions_to_prefetch.push_back(region); 123 } 124 } 125 126 FilterLibchromeRangesOnlyIfPossible(regions_to_prefetch, ranges); 127 return true; 128 } 129 130 // static 131 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() { 132 // Avoid forking with cygprofile instrumentation because the latter performs 133 // memory allocations. 134 #if defined(CYGPROFILE_INSTRUMENTATION) 135 return false; 136 #endif 137 138 // Looking for ranges is done before the fork, to avoid syscalls and/or memory 139 // allocations in the forked process. The child process inherits the lock 140 // state of its parent thread. It cannot rely on being able to acquire any 141 // lock (unless special care is taken in a pre-fork handler), including being 142 // able to call malloc(). 143 std::vector<AddressRange> ranges; 144 if (!FindRanges(&ranges)) 145 return false; 146 147 pid_t pid = fork(); 148 if (pid == 0) { 149 setpriority(PRIO_PROCESS, 0, kBackgroundPriority); 150 // _exit() doesn't call the atexit() handlers. 151 _exit(Prefetch(ranges) ? 0 : 1); 152 } else { 153 if (pid < 0) { 154 return false; 155 } 156 int status; 157 const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0)); 158 if (result == pid) { 159 if (WIFEXITED(status)) { 160 return WEXITSTATUS(status) == 0; 161 } 162 } 163 return false; 164 } 165 } 166 167 // static 168 int NativeLibraryPrefetcher::PercentageOfResidentCode( 169 const std::vector<AddressRange>& ranges) { 170 size_t total_pages = 0; 171 size_t resident_pages = 0; 172 const uintptr_t page_mask = kPageSize - 1; 173 174 for (const auto& range : ranges) { 175 if (range.first & page_mask || range.second & page_mask) 176 return -1; 177 size_t length = range.second - range.first; 178 size_t pages = length / kPageSize; 179 total_pages += pages; 180 std::vector<unsigned char> is_page_resident(pages); 181 int err = mincore(reinterpret_cast<void*>(range.first), length, 182 &is_page_resident[0]); 183 DPCHECK(!err); 184 if (err) 185 return -1; 186 resident_pages += 187 std::count_if(is_page_resident.begin(), is_page_resident.end(), 188 [](unsigned char x) { return x & 1; }); 189 } 190 if (total_pages == 0) 191 return -1; 192 return static_cast<int>((100 * resident_pages) / total_pages); 193 } 194 195 // static 196 int NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode() { 197 std::vector<AddressRange> ranges; 198 if (!FindRanges(&ranges)) 199 return -1; 200 return PercentageOfResidentCode(ranges); 201 } 202 203 } // namespace android 204 } // namespace base 205