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