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