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_number_conversions.h" | |
15 #include "base/strings/string_util.h" | |
16 | |
17 namespace base { | |
18 namespace android { | |
19 | |
20 NativeLibraryPrefetcher::FileLineIterator::FileLineIterator( | |
21 const base::FilePath& path) { | |
22 file_ = base::OpenFile(path, "r"); | |
23 } | |
24 | |
25 NativeLibraryPrefetcher::FileLineIterator::~FileLineIterator() { | |
26 if (file_) { | |
27 base::CloseFile(file_); | |
28 } | |
29 } | |
30 | |
31 bool NativeLibraryPrefetcher::FileLineIterator::NextLine(std::string* line) { | |
32 if (!file_) { | |
33 return false; | |
34 } | |
35 char* line_str = fgets(line_buffer_, kMaxLineLength - 1, file_); | |
36 if (line_str) { | |
37 *line = std::string(line_str); | |
38 return true; | |
39 } | |
40 return false; | |
41 } | |
42 | |
43 NativeLibraryPrefetcher::ProcMapsIterator::ProcMapsIterator( | |
44 FileLineIterator* line_iterator) | |
45 : line_iterator_(line_iterator) { | |
46 } | |
47 | |
48 bool NativeLibraryPrefetcher::ProcMapsIterator::Next( | |
49 NativeLibraryPrefetcher::ProcMapsIterator::Mapping* mapping) { | |
50 std::string line; | |
51 bool ok = line_iterator_->NextLine(&line); | |
52 if (!ok) { | |
53 return false; | |
54 } | |
55 // Example: | |
56 // b6e62000-b6e64000 r-xp 00000000 b3:15 1204 /system/lib/libstdc++.so | |
57 // start-end flags offset device inode [filename] | |
58 std::vector<std::string> pieces; | |
59 base::SplitStringAlongWhitespace(line, &pieces); | |
60 size_t pieces_count = pieces.size(); | |
61 // Filename is optional | |
62 if ((pieces_count != 5) && (pieces_count != 6)) { | |
pasko
2015/03/25 12:57:58
a filename might contain a space, right?
Benoit L
2015/03/26 15:45:03
Done.
| |
63 return false; | |
64 } | |
65 | |
66 std::vector<std::string> start_end; | |
67 base::SplitString(pieces[0], '-', &start_end); | |
68 if (start_end.size() != 2) { | |
69 return false; | |
70 } | |
71 ok = base::HexStringToUInt64(start_end[0], &mapping->start); | |
72 ok &= base::HexStringToUInt64(start_end[1], &mapping->end); | |
73 mapping->flags = pieces[1]; | |
74 ok &= base::HexStringToUInt64(pieces[2], &mapping->offset); | |
75 mapping->device = pieces[3]; | |
76 ok &= base::StringToInt64(pieces[4], &mapping->inode); | |
77 if (!ok) { | |
78 return false; | |
79 } | |
80 if (pieces_count == 5) { | |
81 mapping->filename = std::string(); | |
82 } else { | |
83 mapping->filename = pieces[5]; | |
84 } | |
85 return true; | |
86 } | |
87 | |
88 namespace { | |
89 | |
90 // Android defines the background priority to this value since at least 2009 | |
91 // (see Process.java). | |
92 const int kBackgroundPriority = 10; | |
93 // Valid for all the Android architectures. | |
94 const size_t kPageSize = 4096; | |
95 // We may load directly from the APK. | |
96 const int kSuffixesToMatchCount = 2; | |
97 const std::string kSuffixesToMatch[] = {"libchrome.so", "base.apk"}; | |
98 | |
99 bool IsReadableAndPrivate(const std::string& flags) { | |
100 return flags.size() == 4 && flags[0] == 'r' && flags[3] == 'p'; | |
101 } | |
102 | |
103 bool FilenameMatchesSuffix(const std::string& filename) { | |
104 for (int i = 0; i < kSuffixesToMatchCount; i++) { | |
105 if (EndsWith(filename, kSuffixesToMatch[i], true)) { | |
106 return true; | |
107 } | |
108 } | |
109 return false; | |
110 } | |
111 | |
112 // For each range, reads a byte per page to force it into the page cache. | |
113 void Prefetch(const std::vector<std::pair<uint64_t, uint64_t>>& ranges) { | |
114 for (const auto& range : ranges) { | |
115 const uint64_t page_mask = kPageSize - 1; | |
116 CHECK(!(range.first & page_mask) && !(range.second & page_mask)); | |
117 unsigned char* start_address = (unsigned char*) range.first; | |
118 unsigned char* end_address = (unsigned char*) range.second; | |
119 unsigned char dummy; | |
120 for (unsigned char* addr = start_address; addr < end_address; | |
121 addr += kPageSize) { | |
122 // Volatile is required to prevent the compiler from eliminating this | |
123 // loop. | |
124 dummy ^= *((volatile unsigned char*)addr); | |
125 } | |
126 } | |
127 } | |
128 | |
129 } // namespace | |
130 | |
131 std::vector<std::pair<uint64_t, uint64_t>> | |
132 NativeLibraryPrefetcher::ProcMapsIterator::FindRanges( | |
133 FileLineIterator* file_line_iterator) { | |
134 std::vector<std::pair<uint64_t, uint64_t>> ranges; | |
135 ProcMapsIterator maps_iterator(file_line_iterator); | |
136 NativeLibraryPrefetcher::ProcMapsIterator::Mapping mapping; | |
137 | |
138 while (maps_iterator.Next(&mapping)) { | |
139 if (FilenameMatchesSuffix(mapping.filename) && | |
140 IsReadableAndPrivate(mapping.flags)) { | |
141 ranges.push_back(std::make_pair(mapping.start, mapping.end)); | |
142 } | |
143 } | |
144 return ranges; | |
145 } | |
146 | |
147 // Forks and waits for a process prefetching the native library. This is done in | |
148 // a forked process for the following reasons: | |
149 // - Isolating the main process from mistakes in the parsing. If the parsing | |
150 // returns an incorrect address, only the forked process will crash. | |
151 // - Not inflating the memory used by the main process uselessly, which could | |
152 // increase its likelihood to be killed. | |
153 // The forked process has background priority and, since it is not declared to | |
154 // the android runtime, can be killed at any time, which is not an issue here. | |
155 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() { | |
156 // Looking for ranges is done before the fork, to avoid syscalls and/or | |
157 // memory allocations in the forked process, that can be problematic. | |
158 FileLineIterator file_line_iterator(base::FilePath("/proc/self/maps")); | |
159 std::vector<std::pair<uint64_t, uint64_t>> ranges = | |
160 ProcMapsIterator::FindRanges(&file_line_iterator); | |
161 pid_t pid = fork(); | |
162 if (pid == 0) { | |
163 setpriority(PRIO_PROCESS, 0, kBackgroundPriority); | |
164 Prefetch(ranges); | |
165 exit(0); | |
166 } else { | |
167 if (pid < 0) { | |
168 return false; | |
169 } | |
170 int status; | |
171 const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0)); | |
172 if (result == pid) { | |
173 if (WIFEXITED(status)) { | |
174 return WEXITSTATUS(status) == 0; | |
175 } | |
176 if (WIFSIGNALED(status)) { | |
pasko
2015/03/25 12:57:58
why checking this? we would have returned false an
Benoit L
2015/03/26 15:45:03
To log whether the process was killed or crashed.
| |
177 return false; | |
178 } | |
179 } | |
180 return false; | |
181 } | |
182 } | |
183 | |
184 } // namespace android | |
185 } // namespace base | |
OLD | NEW |