OLD | NEW |
(Empty) | |
| 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 <sys/resource.h> |
| 8 #include <sys/wait.h> |
| 9 #include <unistd.h> |
| 10 #include <utility> |
| 11 #include <vector> |
| 12 |
| 13 #include "base/macros.h" |
| 14 #include "base/posix/eintr_wrapper.h" |
| 15 #include "base/strings/string_util.h" |
| 16 |
| 17 namespace base { |
| 18 namespace android { |
| 19 |
| 20 namespace { |
| 21 |
| 22 // Android defines the background priority to this value since at least 2009 |
| 23 // (see Process.java). |
| 24 const int kBackgroundPriority = 10; |
| 25 // Valid for all the Android architectures. |
| 26 const size_t kPageSize = 4096; |
| 27 const char* kLibchromeSuffix = "libchrome.so"; |
| 28 // "base.apk" is a suffix because the library may be loaded directly from the |
| 29 // APK. |
| 30 const char* kSuffixesToMatch[] = {kLibchromeSuffix, "base.apk"}; |
| 31 |
| 32 bool IsReadableAndPrivate(const base::debug::MappedMemoryRegion& region) { |
| 33 return region.permissions & base::debug::MappedMemoryRegion::READ && |
| 34 region.permissions & base::debug::MappedMemoryRegion::PRIVATE; |
| 35 } |
| 36 |
| 37 bool PathMatchesSuffix(const std::string& path) { |
| 38 for (size_t i = 0; i < arraysize(kSuffixesToMatch); i++) { |
| 39 if (EndsWith(path, kSuffixesToMatch[i], true)) { |
| 40 return true; |
| 41 } |
| 42 } |
| 43 return false; |
| 44 } |
| 45 |
| 46 // For each range, reads a byte per page to force it into the page cache. |
| 47 // Heap allocations, syscalls and library functions are not allowed in this |
| 48 // function. |
| 49 // Returns true for success. |
| 50 bool Prefetch(const std::vector<std::pair<uintptr_t, uintptr_t>>& ranges) { |
| 51 for (const auto& range : ranges) { |
| 52 const uintptr_t page_mask = kPageSize - 1; |
| 53 // If start or end is not page-aligned, parsing went wrong. It is better to |
| 54 // exit with an error. |
| 55 if ((range.first & page_mask) || (range.second & page_mask)) { |
| 56 return false; // CHECK() is not allowed here. |
| 57 } |
| 58 unsigned char* start_ptr = reinterpret_cast<unsigned char*>(range.first); |
| 59 unsigned char* end_ptr = reinterpret_cast<unsigned char*>(range.second); |
| 60 unsigned char dummy = 0; |
| 61 for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kPageSize) { |
| 62 // Volatile is required to prevent the compiler from eliminating this |
| 63 // loop. |
| 64 dummy ^= *static_cast<volatile unsigned char*>(ptr); |
| 65 } |
| 66 } |
| 67 return true; |
| 68 } |
| 69 |
| 70 } // namespace |
| 71 |
| 72 // static |
| 73 bool NativeLibraryPrefetcher::IsGoodToPrefetch( |
| 74 const base::debug::MappedMemoryRegion& region) { |
| 75 return PathMatchesSuffix(region.path) && |
| 76 IsReadableAndPrivate(region); // .text and .data mappings are private. |
| 77 } |
| 78 |
| 79 // static |
| 80 void NativeLibraryPrefetcher::FilterLibchromeRangesOnlyIfPossible( |
| 81 const std::vector<base::debug::MappedMemoryRegion>& regions, |
| 82 std::vector<AddressRange>* ranges) { |
| 83 bool has_libchrome_region = false; |
| 84 for (const base::debug::MappedMemoryRegion& region : regions) { |
| 85 if (EndsWith(region.path, kLibchromeSuffix, true)) { |
| 86 has_libchrome_region = true; |
| 87 break; |
| 88 } |
| 89 } |
| 90 for (const base::debug::MappedMemoryRegion& region : regions) { |
| 91 if (has_libchrome_region && |
| 92 !EndsWith(region.path, kLibchromeSuffix, true)) { |
| 93 continue; |
| 94 } |
| 95 ranges->push_back(std::make_pair(region.start, region.end)); |
| 96 } |
| 97 } |
| 98 |
| 99 // static |
| 100 bool NativeLibraryPrefetcher::FindRanges(std::vector<AddressRange>* ranges) { |
| 101 std::string proc_maps; |
| 102 if (!base::debug::ReadProcMaps(&proc_maps)) |
| 103 return false; |
| 104 std::vector<base::debug::MappedMemoryRegion> regions; |
| 105 if (!base::debug::ParseProcMaps(proc_maps, ®ions)) |
| 106 return false; |
| 107 |
| 108 std::vector<base::debug::MappedMemoryRegion> regions_to_prefetch; |
| 109 for (const auto& region : regions) { |
| 110 if (IsGoodToPrefetch(region)) { |
| 111 regions_to_prefetch.push_back(region); |
| 112 } |
| 113 } |
| 114 |
| 115 FilterLibchromeRangesOnlyIfPossible(regions_to_prefetch, ranges); |
| 116 return true; |
| 117 } |
| 118 |
| 119 // static |
| 120 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() { |
| 121 // Looking for ranges is done before the fork, to avoid syscalls and/or memory |
| 122 // allocations in the forked process. The child process inherits the lock |
| 123 // state of its parent thread. It cannot rely on being able to acquire any |
| 124 // lock (unless special care is taken in a pre-fork handler), including being |
| 125 // able to call malloc(). |
| 126 std::vector<AddressRange> ranges; |
| 127 if (!FindRanges(&ranges)) |
| 128 return false; |
| 129 pid_t pid = fork(); |
| 130 if (pid == 0) { |
| 131 setpriority(PRIO_PROCESS, 0, kBackgroundPriority); |
| 132 // _exit() doesn't call the atexit() handlers. |
| 133 _exit(Prefetch(ranges) ? 0 : 1); |
| 134 } else { |
| 135 if (pid < 0) { |
| 136 return false; |
| 137 } |
| 138 int status; |
| 139 const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0)); |
| 140 if (result == pid) { |
| 141 if (WIFEXITED(status)) { |
| 142 return WEXITSTATUS(status) == 0; |
| 143 } |
| 144 } |
| 145 return false; |
| 146 } |
| 147 } |
| 148 |
| 149 } // namespace android |
| 150 } // namespace base |
OLD | NEW |