1 // Copyright 2015 The Chromium Authors
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 <stddef.h>
8 #include <sys/mman.h>
9 #include <sys/resource.h>
10 #include <sys/wait.h>
11 #include <unistd.h>
12
13 #include <atomic>
14 #include <cstdlib>
15 #include <memory>
16 #include <utility>
17 #include <vector>
18
19 #include "base/android/library_loader/anchor_functions.h"
20 #include "base/android/orderfile/orderfile_buildflags.h"
21 #include "base/bits.h"
22 #include "base/files/file.h"
23 #include "base/format_macros.h"
24 #include "base/logging.h"
25 #include "base/posix/eintr_wrapper.h"
26 #include "base/process/process_metrics.h"
27 #include "base/ranges/algorithm.h"
28 #include "base/strings/string_util.h"
29 #include "base/strings/stringprintf.h"
30 #include "build/build_config.h"
31
32 #if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
33 #include "base/android/orderfile/orderfile_instrumentation.h"
34 #endif
35
36 #if BUILDFLAG(SUPPORTS_CODE_ORDERING)
37
38 namespace base {
39 namespace android {
40
41 namespace {
42
43 // Valid for all Android architectures.
44 constexpr size_t kPageSize = 4096;
45
46 // Populates the per-page residency between |start| and |end| in |residency|. If
47 // successful, |residency| has the size of |end| - |start| in pages.
48 // Returns true for success.
Mincore(size_t start,size_t end,std::vector<unsigned char> * residency)49 bool Mincore(size_t start, size_t end, std::vector<unsigned char>* residency) {
50 if (start % kPageSize || end % kPageSize)
51 return false;
52 size_t size = end - start;
53 size_t size_in_pages = size / kPageSize;
54 if (residency->size() != size_in_pages)
55 residency->resize(size_in_pages);
56 int err = HANDLE_EINTR(
57 mincore(reinterpret_cast<void*>(start), size, &(*residency)[0]));
58 PLOG_IF(ERROR, err) << "mincore() failed";
59 return !err;
60 }
61
62 // Returns the start and end of .text, aligned to the lower and upper page
63 // boundaries, respectively.
GetTextRange()64 std::pair<size_t, size_t> GetTextRange() {
65 // |kStartOfText| may not be at the beginning of a page, since .plt can be
66 // before it, yet in the same mapping for instance.
67 size_t start_page = kStartOfText - kStartOfText % kPageSize;
68 // Set the end to the page on which the beginning of the last symbol is. The
69 // actual symbol may spill into the next page by a few bytes, but this is
70 // outside of the executable code range anyway.
71 size_t end_page = bits::AlignUp(kEndOfText, kPageSize);
72 return {start_page, end_page};
73 }
74
75 // Returns the start and end pages of the unordered section of .text, aligned to
76 // lower and upper page boundaries, respectively.
GetOrderedTextRange()77 std::pair<size_t, size_t> GetOrderedTextRange() {
78 size_t start_page = kStartOfOrderedText - kStartOfOrderedText % kPageSize;
79 // kEndOfUnorderedText is not considered ordered, but the byte immediately
80 // before is considered ordered and so can not be contained in the start page.
81 size_t end_page = bits::AlignUp(kEndOfOrderedText, kPageSize);
82 return {start_page, end_page};
83 }
84
85 // Calls madvise(advice) on the specified range. Does nothing if the range is
86 // empty.
MadviseOnRange(const std::pair<size_t,size_t> & range,int advice)87 void MadviseOnRange(const std::pair<size_t, size_t>& range, int advice) {
88 if (range.first >= range.second) {
89 return;
90 }
91 size_t size = range.second - range.first;
92 int err = madvise(reinterpret_cast<void*>(range.first), size, advice);
93 if (err) {
94 PLOG(ERROR) << "madvise() failed";
95 }
96 }
97
98 // Timestamp in ns since Unix Epoch, and residency, as returned by mincore().
99 struct TimestampAndResidency {
100 uint64_t timestamp_nanos;
101 std::vector<unsigned char> residency;
102
TimestampAndResidencybase::android::__anonb34960e30111::TimestampAndResidency103 TimestampAndResidency(uint64_t timestamp_nanos,
104 std::vector<unsigned char>&& residency)
105 : timestamp_nanos(timestamp_nanos), residency(residency) {}
106 };
107
108 // Returns true for success.
CollectResidency(size_t start,size_t end,std::vector<TimestampAndResidency> * data)109 bool CollectResidency(size_t start,
110 size_t end,
111 std::vector<TimestampAndResidency>* data) {
112 // Not using TimeTicks() to not call too many base:: symbol that would pollute
113 // the reached symbols dumps.
114 struct timespec ts;
115 if (HANDLE_EINTR(clock_gettime(CLOCK_MONOTONIC, &ts))) {
116 PLOG(ERROR) << "Cannot get the time.";
117 return false;
118 }
119 uint64_t now = static_cast<uint64_t>(ts.tv_sec) * 1000 * 1000 * 1000 +
120 static_cast<uint64_t>(ts.tv_nsec);
121 std::vector<unsigned char> residency;
122 if (!Mincore(start, end, &residency))
123 return false;
124
125 data->emplace_back(now, std::move(residency));
126 return true;
127 }
128
DumpResidency(size_t start,size_t end,std::unique_ptr<std::vector<TimestampAndResidency>> data)129 void DumpResidency(size_t start,
130 size_t end,
131 std::unique_ptr<std::vector<TimestampAndResidency>> data) {
132 LOG(WARNING) << "Dumping native library residency";
133 auto path = FilePath(
134 StringPrintf("/data/local/tmp/chrome/residency-%d.txt", getpid()));
135 auto file = File(path, File::FLAG_CREATE_ALWAYS | File::FLAG_WRITE);
136 if (!file.IsValid()) {
137 PLOG(ERROR) << "Cannot open file to dump the residency data "
138 << path.value();
139 return;
140 }
141
142 // First line: start-end of text range.
143 CHECK(AreAnchorsSane());
144 CHECK_LE(start, kStartOfText);
145 CHECK_LE(kEndOfText, end);
146 auto start_end = StringPrintf("%" PRIuS " %" PRIuS "\n", kStartOfText - start,
147 kEndOfText - start);
148 file.WriteAtCurrentPos(start_end.c_str(), static_cast<int>(start_end.size()));
149
150 for (const auto& data_point : *data) {
151 auto timestamp = StringPrintf("%" PRIu64 " ", data_point.timestamp_nanos);
152 file.WriteAtCurrentPos(timestamp.c_str(),
153 static_cast<int>(timestamp.size()));
154
155 std::vector<char> dump;
156 dump.reserve(data_point.residency.size() + 1);
157 for (auto c : data_point.residency)
158 dump.push_back(c ? '1' : '0');
159 dump[dump.size() - 1] = '\n';
160 file.WriteAtCurrentPos(&dump[0], checked_cast<int>(dump.size()));
161 }
162 }
163
164 #if !BUILDFLAG(ORDERFILE_INSTRUMENTATION)
165 // Reads a byte per page between |start| and |end| to force it into the page
166 // cache.
167 // Heap allocations, syscalls and library functions are not allowed in this
168 // function.
169 // Returns true for success.
170 #if defined(ADDRESS_SANITIZER)
171 // Disable AddressSanitizer instrumentation for this function. It is touching
172 // memory that hasn't been allocated by the app, though the addresses are
173 // valid. Furthermore, this takes place in a child process. See crbug.com/653372
174 // for the context.
175 __attribute__((no_sanitize_address))
176 #endif
Prefetch(size_t start,size_t end)177 void Prefetch(size_t start, size_t end) {
178 unsigned char* start_ptr = reinterpret_cast<unsigned char*>(start);
179 unsigned char* end_ptr = reinterpret_cast<unsigned char*>(end);
180 [[maybe_unused]] unsigned char dummy = 0;
181 for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kPageSize) {
182 // Volatile is required to prevent the compiler from eliminating this
183 // loop.
184 dummy ^= *static_cast<volatile unsigned char*>(ptr);
185 }
186 }
187
188 // These values were used in the past for recording
189 // "LibraryLoader.PrefetchDetailedStatus".
190 enum class PrefetchStatus {
191 kSuccess = 0,
192 kWrongOrdering = 1,
193 kForkFailed = 2,
194 kChildProcessCrashed = 3,
195 kChildProcessKilled = 4,
196 kMaxValue = kChildProcessKilled
197 };
198
ForkAndPrefetch(bool ordered_only)199 PrefetchStatus ForkAndPrefetch(bool ordered_only) {
200 if (!IsOrderingSane()) {
201 LOG(WARNING) << "Incorrect code ordering";
202 return PrefetchStatus::kWrongOrdering;
203 }
204
205 // Looking for ranges is done before the fork, to avoid syscalls and/or memory
206 // allocations in the forked process. The child process inherits the lock
207 // state of its parent thread. It cannot rely on being able to acquire any
208 // lock (unless special care is taken in a pre-fork handler), including being
209 // able to call malloc().
210 //
211 // Always prefetch the ordered section first, as it's reached early during
212 // startup, and not necessarily located at the beginning of .text.
213 std::vector<std::pair<size_t, size_t>> ranges = {GetOrderedTextRange()};
214 if (!ordered_only)
215 ranges.push_back(GetTextRange());
216
217 pid_t pid = fork();
218 if (pid == 0) {
219 // Android defines the background priority to this value since at least 2009
220 // (see Process.java).
221 constexpr int kBackgroundPriority = 10;
222 setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
223 // _exit() doesn't call the atexit() handlers.
224 for (const auto& range : ranges) {
225 Prefetch(range.first, range.second);
226 }
227 _exit(EXIT_SUCCESS);
228 } else {
229 if (pid < 0) {
230 return PrefetchStatus::kForkFailed;
231 }
232 int status;
233 const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0));
234 if (result == pid) {
235 if (WIFEXITED(status))
236 return PrefetchStatus::kSuccess;
237 if (WIFSIGNALED(status)) {
238 int signal = WTERMSIG(status);
239 switch (signal) {
240 case SIGSEGV:
241 case SIGBUS:
242 return PrefetchStatus::kChildProcessCrashed;
243 case SIGKILL:
244 case SIGTERM:
245 default:
246 return PrefetchStatus::kChildProcessKilled;
247 }
248 }
249 }
250 // Should not happen. Per man waitpid(2), errors are:
251 // - EINTR: handled.
252 // - ECHILD if the process doesn't have an unwaited-for child with this PID.
253 // - EINVAL.
254 return PrefetchStatus::kChildProcessKilled;
255 }
256 }
257 #endif // !BUILDFLAG(ORDERFILE_INSTRUMENTATION)
258
259 } // namespace
260
261 // static
ForkAndPrefetchNativeLibrary(bool ordered_only)262 void NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary(bool ordered_only) {
263 #if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
264 // Avoid forking with orderfile instrumentation because the child process
265 // would create a dump as well.
266 return;
267 #else
268 PrefetchStatus status = ForkAndPrefetch(ordered_only);
269 if (status != PrefetchStatus::kSuccess) {
270 LOG(WARNING) << "Cannot prefetch the library. status = "
271 << static_cast<int>(status);
272 }
273 #endif // BUILDFLAG(ORDERFILE_INSTRUMENTATION)
274 }
275
276 // static
PercentageOfResidentCode(size_t start,size_t end)277 int NativeLibraryPrefetcher::PercentageOfResidentCode(size_t start,
278 size_t end) {
279 size_t total_pages = 0;
280 size_t resident_pages = 0;
281
282 std::vector<unsigned char> residency;
283 bool ok = Mincore(start, end, &residency);
284 if (!ok)
285 return -1;
286 total_pages += residency.size();
287 resident_pages += static_cast<size_t>(
288 ranges::count_if(residency, [](unsigned char x) { return x & 1; }));
289 if (total_pages == 0)
290 return -1;
291 return static_cast<int>((100 * resident_pages) / total_pages);
292 }
293
294 // static
PercentageOfResidentNativeLibraryCode()295 int NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode() {
296 if (!AreAnchorsSane()) {
297 LOG(WARNING) << "Incorrect code ordering";
298 return -1;
299 }
300 const auto& range = GetTextRange();
301 return PercentageOfResidentCode(range.first, range.second);
302 }
303
304 // static
PeriodicallyCollectResidency()305 void NativeLibraryPrefetcher::PeriodicallyCollectResidency() {
306 CHECK_EQ(static_cast<long>(kPageSize), sysconf(_SC_PAGESIZE));
307
308 LOG(WARNING) << "Spawning thread to periodically collect residency";
309 const auto& range = GetTextRange();
310 auto data = std::make_unique<std::vector<TimestampAndResidency>>();
311 // Collect residency for about minute (the actual time spent collecting
312 // residency can vary, so this is only approximate).
313 for (int i = 0; i < 120; ++i) {
314 if (!CollectResidency(range.first, range.second, data.get()))
315 return;
316 usleep(5e5);
317 }
318 DumpResidency(range.first, range.second, std::move(data));
319 }
320
321 // static
MadviseForOrderfile()322 void NativeLibraryPrefetcher::MadviseForOrderfile() {
323 if (!IsOrderingSane()) {
324 LOG(WARNING) << "Code not ordered, madvise optimization skipped";
325 return;
326 }
327 // First MADV_RANDOM on all of text, then turn the ordered text range back to
328 // normal. The ordered range may be placed anywhere within .text.
329 MadviseOnRange(GetTextRange(), MADV_RANDOM);
330 MadviseOnRange(GetOrderedTextRange(), MADV_NORMAL);
331 }
332
333 // static
MadviseForResidencyCollection()334 void NativeLibraryPrefetcher::MadviseForResidencyCollection() {
335 if (!AreAnchorsSane()) {
336 LOG(WARNING) << "Code not ordered, cannot madvise";
337 return;
338 }
339 LOG(WARNING) << "Performing madvise for residency collection";
340 MadviseOnRange(GetTextRange(), MADV_RANDOM);
341 }
342
343 } // namespace android
344 } // namespace base
345 #endif // BUILDFLAG(SUPPORTS_CODE_ORDERING)
346