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