• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <stddef.h>
8 #include <sys/mman.h>
9 #include <sys/resource.h>
10 #include <sys/wait.h>
11 #include <unistd.h>
12 #include <algorithm>
13 #include <utility>
14 #include <vector>
15 
16 #include "base/macros.h"
17 #include "base/posix/eintr_wrapper.h"
18 #include "base/strings/string_util.h"
19 #include "build/build_config.h"
20 
21 namespace base {
22 namespace android {
23 
24 namespace {
25 
26 // Android defines the background priority to this value since at least 2009
27 // (see Process.java).
28 const int kBackgroundPriority = 10;
29 // Valid for all the Android architectures.
30 const size_t kPageSize = 4096;
31 const char* kLibchromeSuffix = "libchrome.so";
32 // "base.apk" is a suffix because the library may be loaded directly from the
33 // APK.
34 const char* kSuffixesToMatch[] = {kLibchromeSuffix, "base.apk"};
35 
IsReadableAndPrivate(const base::debug::MappedMemoryRegion & region)36 bool IsReadableAndPrivate(const base::debug::MappedMemoryRegion& region) {
37   return region.permissions & base::debug::MappedMemoryRegion::READ &&
38          region.permissions & base::debug::MappedMemoryRegion::PRIVATE;
39 }
40 
PathMatchesSuffix(const std::string & path)41 bool PathMatchesSuffix(const std::string& path) {
42   for (size_t i = 0; i < arraysize(kSuffixesToMatch); i++) {
43     if (EndsWith(path, kSuffixesToMatch[i], CompareCase::SENSITIVE)) {
44       return true;
45     }
46   }
47   return false;
48 }
49 
50 // For each range, reads a byte per page to force it into the page cache.
51 // Heap allocations, syscalls and library functions are not allowed in this
52 // function.
53 // Returns true for success.
54 #if defined(ADDRESS_SANITIZER)
55 // Disable AddressSanitizer instrumentation for this function. It is touching
56 // memory that hasn't been allocated by the app, though the addresses are
57 // valid. Furthermore, this takes place in a child process. See crbug.com/653372
58 // for the context.
59 __attribute__((no_sanitize_address))
60 #endif
Prefetch(const std::vector<std::pair<uintptr_t,uintptr_t>> & ranges)61 bool Prefetch(const std::vector<std::pair<uintptr_t, uintptr_t>>& ranges) {
62   for (const auto& range : ranges) {
63     const uintptr_t page_mask = kPageSize - 1;
64     // If start or end is not page-aligned, parsing went wrong. It is better to
65     // exit with an error.
66     if ((range.first & page_mask) || (range.second & page_mask)) {
67       return false;  // CHECK() is not allowed here.
68     }
69     unsigned char* start_ptr = reinterpret_cast<unsigned char*>(range.first);
70     unsigned char* end_ptr = reinterpret_cast<unsigned char*>(range.second);
71     unsigned char dummy = 0;
72     for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kPageSize) {
73       // Volatile is required to prevent the compiler from eliminating this
74       // loop.
75       dummy ^= *static_cast<volatile unsigned char*>(ptr);
76     }
77   }
78   return true;
79 }
80 
81 }  // namespace
82 
83 // static
IsGoodToPrefetch(const base::debug::MappedMemoryRegion & region)84 bool NativeLibraryPrefetcher::IsGoodToPrefetch(
85     const base::debug::MappedMemoryRegion& region) {
86   return PathMatchesSuffix(region.path) &&
87          IsReadableAndPrivate(region);  // .text and .data mappings are private.
88 }
89 
90 // static
FilterLibchromeRangesOnlyIfPossible(const std::vector<base::debug::MappedMemoryRegion> & regions,std::vector<AddressRange> * ranges)91 void NativeLibraryPrefetcher::FilterLibchromeRangesOnlyIfPossible(
92     const std::vector<base::debug::MappedMemoryRegion>& regions,
93     std::vector<AddressRange>* ranges) {
94   bool has_libchrome_region = false;
95   for (const base::debug::MappedMemoryRegion& region : regions) {
96     if (EndsWith(region.path, kLibchromeSuffix, CompareCase::SENSITIVE)) {
97       has_libchrome_region = true;
98       break;
99     }
100   }
101   for (const base::debug::MappedMemoryRegion& region : regions) {
102     if (has_libchrome_region &&
103         !EndsWith(region.path, kLibchromeSuffix, CompareCase::SENSITIVE)) {
104       continue;
105     }
106     ranges->push_back(std::make_pair(region.start, region.end));
107   }
108 }
109 
110 // static
FindRanges(std::vector<AddressRange> * ranges)111 bool NativeLibraryPrefetcher::FindRanges(std::vector<AddressRange>* ranges) {
112   std::string proc_maps;
113   if (!base::debug::ReadProcMaps(&proc_maps))
114     return false;
115   std::vector<base::debug::MappedMemoryRegion> regions;
116   if (!base::debug::ParseProcMaps(proc_maps, &regions))
117     return false;
118 
119   std::vector<base::debug::MappedMemoryRegion> regions_to_prefetch;
120   for (const auto& region : regions) {
121     if (IsGoodToPrefetch(region)) {
122       regions_to_prefetch.push_back(region);
123     }
124   }
125 
126   FilterLibchromeRangesOnlyIfPossible(regions_to_prefetch, ranges);
127   return true;
128 }
129 
130 // static
ForkAndPrefetchNativeLibrary()131 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() {
132   // Avoid forking with cygprofile instrumentation because the latter performs
133   // memory allocations.
134 #if defined(CYGPROFILE_INSTRUMENTATION)
135   return false;
136 #endif
137 
138   // Looking for ranges is done before the fork, to avoid syscalls and/or memory
139   // allocations in the forked process. The child process inherits the lock
140   // state of its parent thread. It cannot rely on being able to acquire any
141   // lock (unless special care is taken in a pre-fork handler), including being
142   // able to call malloc().
143   std::vector<AddressRange> ranges;
144   if (!FindRanges(&ranges))
145     return false;
146 
147   pid_t pid = fork();
148   if (pid == 0) {
149     setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
150     // _exit() doesn't call the atexit() handlers.
151     _exit(Prefetch(ranges) ? 0 : 1);
152   } else {
153     if (pid < 0) {
154       return false;
155     }
156     int status;
157     const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0));
158     if (result == pid) {
159       if (WIFEXITED(status)) {
160         return WEXITSTATUS(status) == 0;
161       }
162     }
163     return false;
164   }
165 }
166 
167 // static
PercentageOfResidentCode(const std::vector<AddressRange> & ranges)168 int NativeLibraryPrefetcher::PercentageOfResidentCode(
169     const std::vector<AddressRange>& ranges) {
170   size_t total_pages = 0;
171   size_t resident_pages = 0;
172   const uintptr_t page_mask = kPageSize - 1;
173 
174   for (const auto& range : ranges) {
175     if (range.first & page_mask || range.second & page_mask)
176       return -1;
177     size_t length = range.second - range.first;
178     size_t pages = length / kPageSize;
179     total_pages += pages;
180     std::vector<unsigned char> is_page_resident(pages);
181     int err = mincore(reinterpret_cast<void*>(range.first), length,
182                       &is_page_resident[0]);
183     DPCHECK(!err);
184     if (err)
185       return -1;
186     resident_pages +=
187         std::count_if(is_page_resident.begin(), is_page_resident.end(),
188                       [](unsigned char x) { return x & 1; });
189   }
190   if (total_pages == 0)
191     return -1;
192   return static_cast<int>((100 * resident_pages) / total_pages);
193 }
194 
195 // static
PercentageOfResidentNativeLibraryCode()196 int NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode() {
197   std::vector<AddressRange> ranges;
198   if (!FindRanges(&ranges))
199     return -1;
200   return PercentageOfResidentCode(ranges);
201 }
202 
203 }  // namespace android
204 }  // namespace base
205