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. | |
rmcilroy
2015/04/07 12:17:27
This comment is not very clear, could you make it
Benoit L
2015/04/16 13:28:48
Done.
| |
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( | |
rmcilroy
2015/04/07 12:17:27
nit - /s/Libchrome/LibChrome
Benoit L
2015/04/16 13:28:47
Acknowledged.
| |
79 const std::vector<base::debug::MappedMemoryRegion>& regions, | |
80 std::vector<AddressRange>* ranges) { | |
81 const std::string& libchrome_suffix = kSuffixesToMatch[0]; | |
rmcilroy
2015/04/07 12:17:27
It would be better to have "libchrome.so" as a sep
Benoit L
2015/04/16 13:28:48
Done.
| |
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); | |
rmcilroy
2015/04/07 12:17:27
nit - FilterLibChromeRangesOnlyIfPossible
Benoit L
2015/04/16 13:28:48
Done.
| |
115 return true; | |
116 } | |
117 | |
118 // static | |
119 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() { | |
120 // Looking for ranges is done before the fork, to avoid syscalls and/or memory | |
121 // allocations in the forked process. The child process inherits the lock | |
122 // state of its parent thread. It cannot rely on being able to acquire any | |
123 // lock (unless special care is taken in a pre-fork handler), including being | |
124 // able to call malloc(). | |
rmcilroy
2015/04/07 12:17:27
As you state here, forking a process with threads
Benoit L
2015/04/16 13:28:47
Forking a new process was brought up as a nice sol
rmcilroy
2015/04/23 15:09:42
Makes sense. Thanks for the explination.
| |
125 std::vector<AddressRange> ranges; | |
126 if (!FindRanges(&ranges)) | |
127 return false; | |
128 pid_t pid = fork(); | |
129 if (pid == 0) { | |
130 setpriority(PRIO_PROCESS, 0, kBackgroundPriority); | |
131 // _exit() doesn't call the atexit() handlers. | |
132 _exit(Prefetch(ranges) ? 0 : 1); | |
133 } else { | |
134 if (pid < 0) { | |
135 return false; | |
136 } | |
137 int status; | |
138 const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0)); | |
139 if (result == pid) { | |
140 if (WIFEXITED(status)) { | |
141 return WEXITSTATUS(status) == 0; | |
142 } | |
143 } | |
144 return false; | |
145 } | |
146 } | |
147 | |
148 } // namespace android | |
149 } // namespace base | |
OLD | NEW |