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/logging.h" | |
14 #include "base/posix/eintr_wrapper.h" | |
15 #include "base/strings/string_number_conversions.h" | |
16 #include "base/strings/string_util.h" | |
17 | |
18 namespace base { | |
19 namespace android { | |
20 | |
21 namespace { | |
22 | |
23 // Android defines the background priority to this value since at least 2009 | |
pasko
2015/03/27 13:29:28
If THREAD_PRIORITY_BACKGROUND can change, let's ju
Benoit L
2015/03/27 15:44:52
Well, this adds clutter, and as you said, is highl
| |
24 // (see Process.java). | |
25 const int kBackgroundPriority = 10; | |
26 // Valid for all the Android architectures. | |
27 const size_t kPageSize = 4096; | |
28 // We may load directly from the APK. | |
29 const int kSuffixesToMatchCount = 2; | |
30 const std::string kSuffixesToMatch[] = {"libchrome.so", "base.apk"}; | |
pasko
2015/03/27 13:29:28
<quote>
... we only allow static variables to cont
Benoit L
2015/03/27 15:44:52
Done.
| |
31 | |
32 bool IsReadableAndPrivate(const std::string& flags) { | |
33 return flags.size() == 4 && flags[0] == 'r' && flags[3] == 'p'; | |
34 } | |
35 | |
36 bool FilenameMatchesSuffix(const std::string& filename) { | |
37 for (int i = 0; i < kSuffixesToMatchCount; i++) { | |
38 if (EndsWith(filename, kSuffixesToMatch[i], true)) { | |
39 return true; | |
40 } | |
41 } | |
42 return false; | |
43 } | |
44 | |
45 // For each range, reads a byte per page to force it into the page cache. | |
pasko
2015/03/27 17:51:54
can you add a comment saying that heap allocations
| |
46 void Prefetch(const std::vector<std::pair<uint64_t, uint64_t>>& ranges) { | |
47 for (const auto& range : ranges) { | |
48 const uint64_t page_mask = kPageSize - 1; | |
49 CHECK(!(range.first & page_mask) && !(range.second & page_mask)); | |
50 unsigned char* start_address = (unsigned char*)range.first; | |
51 unsigned char* end_address = (unsigned char*)range.second; | |
52 unsigned char dummy; | |
53 for (unsigned char* addr = start_address; addr < end_address; | |
54 addr += kPageSize) { | |
55 // Volatile is required to prevent the compiler from eliminating this | |
56 // loop. | |
57 dummy ^= *((volatile unsigned char*)addr); | |
58 } | |
59 } | |
60 } | |
61 | |
62 } // namespace | |
63 | |
64 bool NativeLibraryPrefetcher::ParseMapping(const std::string& line, | |
pasko
2015/03/27 13:29:28
// static
(same for the 2 other functions)
Benoit L
2015/03/27 15:44:51
Done.
| |
65 Mapping* mapping) { | |
66 // Example: | |
67 // b6e62000-b6e64000 r-xp 00000000 b3:15 1204 /system/lib/libstdc++.so | |
68 // start-end flags offset device inode [filename] | |
69 std::vector<std::string> pieces; | |
70 base::SplitStringAlongWhitespace(line, &pieces); | |
71 size_t pieces_count = pieces.size(); | |
72 // Filename is optional, so at least 5 fields. | |
73 if (pieces_count < 5) { | |
74 return false; | |
75 } | |
76 | |
77 std::vector<std::string> start_end; | |
78 base::SplitString(pieces[0], '-', &start_end); | |
79 if (start_end.size() != 2) { | |
80 return false; | |
81 } | |
82 bool ok = base::HexStringToUInt64(start_end[0], &mapping->start); | |
83 ok &= base::HexStringToUInt64(start_end[1], &mapping->end); | |
84 mapping->flags = pieces[1]; | |
85 ok &= base::HexStringToUInt64(pieces[2], &mapping->offset); | |
86 mapping->device = pieces[3]; | |
87 ok &= base::StringToInt64(pieces[4], &mapping->inode); | |
88 if (!ok) { | |
89 return false; | |
90 } | |
91 if (pieces_count == 5) { | |
92 mapping->filename = std::string(); | |
93 } else { | |
94 // Truncates filenames containing spaces, but these are not the droids we're | |
95 // looking for. | |
96 mapping->filename = pieces[5]; | |
97 } | |
98 return true; | |
99 } | |
100 | |
101 bool NativeLibraryPrefetcher::MappingMatches(const Mapping& mapping) { | |
102 return FilenameMatchesSuffix(mapping.filename) && | |
103 IsReadableAndPrivate(mapping.flags); // Code mappings are private. | |
104 } | |
105 | |
106 bool NativeLibraryPrefetcher::FindRanges( | |
107 std::vector<std::pair<uint64_t, uint64_t>>* ranges) { | |
108 base::ScopedFILE file(base::OpenFile(base::FilePath("/proc/self/maps"), "r")); | |
pasko
2015/03/27 13:29:27
OpenFile is the implementation detail of base::Fil
Benoit L
2015/03/27 15:44:52
We need a FILE*, and not a File. And File::Platfor
pasko
2015/03/27 17:51:54
ah, you are right, fgets, and there is no line by
| |
109 if (!file.get()) { | |
110 return false; | |
111 } | |
112 std::vector<Mapping> mappings; | |
113 const size_t kMaxLineLength = 4096; | |
114 char line_buffer[kMaxLineLength]; | |
115 while (char* line_str = fgets(line_buffer, kMaxLineLength - 1, file.get())) { | |
116 std::string line(line_str); | |
117 Mapping mapping; | |
118 if (!ParseMapping(line, &mapping)) { | |
119 continue; | |
120 } | |
121 if (MappingMatches(mapping)) { | |
122 mappings.push_back(mapping); | |
123 } | |
124 } | |
125 const std::string& libchrome_suffix = kSuffixesToMatch[0]; | |
pasko
2015/03/27 13:29:27
nit: it's probably nice to separate this part with
Benoit L
2015/03/27 15:44:52
Done.
| |
126 bool has_libchrome_mapping = false; | |
127 for (const Mapping& mapping : mappings) { | |
128 if (EndsWith(mapping.filename, libchrome_suffix, true)) { | |
129 has_libchrome_mapping = true; | |
130 break; | |
131 } | |
132 } | |
133 for (const Mapping& mapping : mappings) { | |
134 if (has_libchrome_mapping | |
135 && !EndsWith(mapping.filename, libchrome_suffix, true)) { | |
136 continue; | |
137 } | |
138 ranges->push_back(std::make_pair(mapping.start, mapping.end)); | |
139 } | |
140 return true; | |
141 } | |
142 | |
143 // Forks and waits for a process prefetching the native library. This is done in | |
144 // a forked process for the following reasons: | |
145 // - Isolating the main process from mistakes in the parsing. If the parsing | |
pasko
2015/03/27 11:03:37
Forking does not fully 'isolate' because the proce
| |
146 // returns an incorrect address, only the forked process will crash. | |
147 // - Not inflating the memory used by the main process uselessly, which could | |
148 // increase its likelihood to be killed. | |
149 // The forked process has background priority and, since it is not declared to | |
150 // the android runtime, can be killed at any time, which is not an issue here. | |
151 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() { | |
152 // Looking for ranges is done before the fork, to avoid syscalls and/or | |
153 // memory allocations in the forked process, that can be problematic. | |
pasko
2015/03/27 17:51:54
s/ that can be problematic//
does not add any use
| |
154 std::vector<std::pair<uint64_t, uint64_t>> ranges; | |
155 if (!FindRanges(&ranges)) { | |
156 return false; | |
157 } | |
158 pid_t pid = fork(); | |
pasko
2015/03/27 11:03:37
what if the child process deadlocks? It might happ
Benoit L
2015/03/27 15:44:51
This process doesn't do any heap allocation, and d
| |
159 if (pid == 0) { | |
160 setpriority(PRIO_PROCESS, 0, kBackgroundPriority); | |
161 Prefetch(ranges); | |
162 exit(0); | |
pasko
2015/03/27 11:03:37
this would invoke destructors which we don't do no
Benoit L
2015/03/27 15:44:51
_exit(2) doesn't call the atexit() handlers.
Done.
| |
163 } else { | |
164 if (pid < 0) { | |
165 return false; | |
166 } | |
167 int status; | |
168 const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0)); | |
169 if (result == pid) { | |
170 if (WIFEXITED(status)) { | |
171 return WEXITSTATUS(status) == 0; | |
172 } | |
173 if (WIFSIGNALED(status)) { | |
174 switch (WTERMSIG(status)) { | |
175 case SIGABRT: | |
176 case SIGBUS: | |
177 case SIGFPE: | |
178 case SIGILL: | |
179 case SIGSEGV: | |
180 LOG(WARNING) << "The native library prefetching process crashed (" | |
181 << pid << ")"; | |
pasko
2015/03/27 11:03:37
break;
Benoit L
2015/03/27 15:44:52
Done.
pasko
2015/03/27 17:51:54
I like how this got done :)
| |
182 case SIGINT: | |
183 case SIGKILL: | |
184 case SIGTERM: | |
185 LOG(WARNING) << "The native library prefetching process was killed " | |
pasko
2015/03/27 11:03:37
is there a reason to distinguish these cases? If y
Benoit L
2015/03/27 15:44:52
Done.
| |
186 << "(" << pid << ")"; | |
187 default: | |
188 break; | |
189 } | |
190 } | |
191 } | |
192 return false; | |
193 } | |
194 } | |
195 | |
196 } // namespace android | |
197 } // namespace base | |
OLD | NEW |