Chromium Code Reviews| 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/posix/eintr_wrapper.h" | |
| 14 #include "base/strings/string_util.h" | |
| 15 | |
| 16 namespace base { | |
| 17 namespace android { | |
| 18 | |
| 19 namespace { | |
| 20 | |
| 21 // Android defines the background priority to this value since at least 2009 | |
| 22 // (see Process.java). | |
| 23 const int kBackgroundPriority = 10; | |
| 24 // Valid for all the Android architectures. | |
| 25 const size_t kPageSize = 4096; | |
| 26 // We may load directly from the APK. | |
| 27 const int kSuffixesToMatchCount = 2; | |
| 28 const char* kSuffixesToMatch[] = {"libchrome.so", "base.apk"}; | |
| 29 | |
| 30 bool IsReadableAndPrivate(const base::debug::MappedMemoryRegion& region) { | |
| 31 return region.permissions & base::debug::MappedMemoryRegion::READ && | |
| 32 region.permissions & base::debug::MappedMemoryRegion::PRIVATE; | |
| 33 } | |
| 34 | |
| 35 bool PathMatchesSuffix(const std::string& path) { | |
| 36 for (int i = 0; i < kSuffixesToMatchCount; i++) { | |
|
Robert Sesek
2015/04/02 15:58:02
You can remove this in favor of arraysize(kSuffixe
Benoit L
2015/04/02 16:43:48
Thank you for the suggestion!
Done.
| |
| 37 if (EndsWith(path, kSuffixesToMatch[i], true)) { | |
| 38 return true; | |
| 39 } | |
| 40 } | |
| 41 return false; | |
| 42 } | |
| 43 | |
| 44 // For each range, reads a byte per page to force it into the page cache. | |
| 45 // Heap allocations, syscalls and library functions are not allowed in this | |
| 46 // function. | |
| 47 // Returns true for success. | |
| 48 bool Prefetch(const std::vector<std::pair<uintptr_t, uintptr_t>>& ranges) { | |
|
Robert Sesek
2015/04/02 15:58:02
Consider making this pair a named type (in the .h
Benoit L
2015/04/02 16:43:48
Thank you for the suggestion.
I've updated the oth
| |
| 49 for (const auto& range : ranges) { | |
| 50 const uintptr_t page_mask = kPageSize - 1; | |
| 51 // If start or end is not page-aligned, parsing went wrong. It is better to | |
| 52 // exit with an error. | |
| 53 if ((range.first & page_mask) || (range.second & page_mask)) { | |
| 54 return false; // CHECK() is not allowed here. | |
| 55 } | |
| 56 unsigned char* start_ptr = reinterpret_cast<unsigned char*>(range.first); | |
| 57 unsigned char* end_ptr = reinterpret_cast<unsigned char*>(range.second); | |
| 58 unsigned char dummy; | |
| 59 for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kPageSize) { | |
| 60 // Volatile is required to prevent the compiler from eliminating this | |
| 61 // loop. | |
| 62 dummy ^= *static_cast<volatile unsigned char*>(ptr); | |
| 63 } | |
| 64 } | |
| 65 return true; | |
| 66 } | |
| 67 | |
| 68 } // namespace | |
| 69 | |
| 70 // static | |
| 71 bool NativeLibraryPrefetcher::IsGoodToPrefetch( | |
| 72 const base::debug::MappedMemoryRegion& region) { | |
| 73 return PathMatchesSuffix(region.path) && | |
| 74 IsReadableAndPrivate(region); // .text and .data mappings are private. | |
| 75 } | |
| 76 | |
| 77 // static | |
| 78 void NativeLibraryPrefetcher::TakeOnlyLibchromeRangesIfPossible( | |
| 79 const std::vector<base::debug::MappedMemoryRegion>& regions, | |
| 80 std::vector<std::pair<uintptr_t, uintptr_t>>* ranges) { | |
| 81 const std::string& libchrome_suffix = kSuffixesToMatch[0]; | |
| 82 bool has_libchrome_region = false; | |
| 83 for (const base::debug::MappedMemoryRegion& region : regions) { | |
| 84 if (EndsWith(region.path, libchrome_suffix, true)) { | |
| 85 has_libchrome_region = true; | |
| 86 break; | |
| 87 } | |
| 88 } | |
| 89 for (const base::debug::MappedMemoryRegion& region : regions) { | |
| 90 if (has_libchrome_region && | |
| 91 !EndsWith(region.path, libchrome_suffix, true)) { | |
| 92 continue; | |
| 93 } | |
| 94 ranges->push_back(std::make_pair(region.start, region.end)); | |
| 95 } | |
| 96 } | |
| 97 | |
| 98 // static | |
| 99 bool NativeLibraryPrefetcher::FindRanges( | |
| 100 std::vector<std::pair<uintptr_t, uintptr_t>>* 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 (!ParseProcMaps(proc_maps, ®ions)) | |
|
Robert Sesek
2015/04/02 15:58:02
base::debug:: ?
Benoit L
2015/04/02 16:43:48
Done.
| |
| 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 TakeOnlyLibchromeRangesIfPossible(regions_to_prefetch, ranges); | |
| 116 return true; | |
| 117 } | |
| 118 | |
| 119 // Forks and waits for a process prefetching the native library. This is done in | |
| 120 // a forked process for the following reasons: | |
| 121 // - Isolating the main process from mistakes in the parsing. If the parsing | |
| 122 // returns an incorrect address, only the forked process will crash. | |
| 123 // - Not inflating the memory used by the main process uselessly, which could | |
| 124 // increase its likelihood to be killed. | |
| 125 // The forked process has background priority and, since it is not declared to | |
| 126 // the android runtime, can be killed at any time, which is not an issue here. | |
| 127 // static | |
| 128 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() { | |
| 129 // Looking for ranges is done before the fork, to avoid syscalls and/or memory | |
| 130 // allocations in the forked process. The child process inherits the lock | |
| 131 // state of its parent thread. It cannot rely on being able to acquire any | |
| 132 // lock (unless special care is taken in a pre-fork handler), including being | |
| 133 // able to call malloc(). | |
| 134 std::vector<std::pair<uintptr_t, uintptr_t>> ranges; | |
| 135 if (!FindRanges(&ranges)) | |
| 136 return false; | |
| 137 pid_t pid = fork(); | |
| 138 if (pid == 0) { | |
| 139 setpriority(PRIO_PROCESS, 0, kBackgroundPriority); | |
| 140 // _exit() doesn't call the atexit() handlers. | |
| 141 _exit(Prefetch(ranges) ? 0 : 1); | |
| 142 } else { | |
| 143 if (pid < 0) { | |
| 144 return false; | |
| 145 } | |
| 146 int status; | |
| 147 const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0)); | |
| 148 if (result == pid) { | |
| 149 if (WIFEXITED(status)) { | |
| 150 return WEXITSTATUS(status) == 0; | |
| 151 } | |
| 152 } | |
| 153 return false; | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 } // namespace android | |
| 158 } // namespace base | |
| OLD | NEW |