• 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 #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