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 // We may load directly from the APK. | |
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 (size_t i = 0; i < arraysize(kSuffixesToMatch); i++) { | |
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) { | |
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<AddressRange>* 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(std::vector<AddressRange>* ranges) { | |
100 std::string proc_maps; | |
101 if (!base::debug::ReadProcMaps(&proc_maps)) | |
102 return false; | |
103 std::vector<base::debug::MappedMemoryRegion> regions; | |
104 if (!base::debug::ParseProcMaps(proc_maps, ®ions)) | |
105 return false; | |
106 | |
107 std::vector<base::debug::MappedMemoryRegion> regions_to_prefetch; | |
108 for (const auto& region : regions) { | |
109 if (IsGoodToPrefetch(region)) { | |
110 regions_to_prefetch.push_back(region); | |
111 } | |
112 } | |
113 | |
114 TakeOnlyLibchromeRangesIfPossible(regions_to_prefetch, ranges); | |
115 return true; | |
116 } | |
117 | |
118 // Forks and waits for a process prefetching the native library. This is done in | |
Robert Sesek
2015/04/02 19:46:48
I think this would be better as the class-level co
Benoit L
2015/04/03 09:47:16
Done.
| |
119 // a forked process for the following reasons: | |
120 // - Isolating the main process from mistakes in the parsing. If the parsing | |
121 // returns an incorrect address, only the forked process will crash. | |
122 // - Not inflating the memory used by the main process uselessly, which could | |
123 // increase its likelihood to be killed. | |
124 // The forked process has background priority and, since it is not declared to | |
125 // the android runtime, can be killed at any time, which is not an issue here. | |
126 // static | |
127 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() { | |
128 // Looking for ranges is done before the fork, to avoid syscalls and/or memory | |
129 // allocations in the forked process. The child process inherits the lock | |
130 // state of its parent thread. It cannot rely on being able to acquire any | |
131 // lock (unless special care is taken in a pre-fork handler), including being | |
132 // able to call malloc(). | |
133 std::vector<AddressRange> ranges; | |
134 if (!FindRanges(&ranges)) | |
135 return false; | |
136 pid_t pid = fork(); | |
137 if (pid == 0) { | |
138 setpriority(PRIO_PROCESS, 0, kBackgroundPriority); | |
139 // _exit() doesn't call the atexit() handlers. | |
140 _exit(Prefetch(ranges) ? 0 : 1); | |
141 } else { | |
142 if (pid < 0) { | |
143 return false; | |
144 } | |
145 int status; | |
146 const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0)); | |
147 if (result == pid) { | |
148 if (WIFEXITED(status)) { | |
149 return WEXITSTATUS(status) == 0; | |
150 } | |
151 } | |
152 return false; | |
153 } | |
154 } | |
155 | |
156 } // namespace android | |
157 } // namespace base | |
OLD | NEW |