Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / base / android / library_loader / library_prefetcher.cc
blob118e80cc0e7a0d146ad43653e1ef18941b28be59
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.
5 #include "base/android/library_loader/library_prefetcher.h"
7 #include <sys/resource.h>
8 #include <sys/wait.h>
9 #include <unistd.h>
10 #include <utility>
11 #include <vector>
13 #include "base/macros.h"
14 #include "base/posix/eintr_wrapper.h"
15 #include "base/strings/string_util.h"
17 namespace base {
18 namespace android {
20 namespace {
22 // Android defines the background priority to this value since at least 2009
23 // (see Process.java).
24 const int kBackgroundPriority = 10;
25 // Valid for all the Android architectures.
26 const size_t kPageSize = 4096;
27 const char* kLibchromeSuffix = "libchrome.so";
28 // "base.apk" is a suffix because the library may be loaded directly from the
29 // APK.
30 const char* kSuffixesToMatch[] = {kLibchromeSuffix, "base.apk"};
32 bool IsReadableAndPrivate(const base::debug::MappedMemoryRegion& region) {
33 return region.permissions & base::debug::MappedMemoryRegion::READ &&
34 region.permissions & base::debug::MappedMemoryRegion::PRIVATE;
37 bool PathMatchesSuffix(const std::string& path) {
38 for (size_t i = 0; i < arraysize(kSuffixesToMatch); i++) {
39 if (EndsWith(path, kSuffixesToMatch[i], CompareCase::SENSITIVE)) {
40 return true;
43 return false;
46 // For each range, reads a byte per page to force it into the page cache.
47 // Heap allocations, syscalls and library functions are not allowed in this
48 // function.
49 // Returns true for success.
50 bool Prefetch(const std::vector<std::pair<uintptr_t, uintptr_t>>& ranges) {
51 for (const auto& range : ranges) {
52 const uintptr_t page_mask = kPageSize - 1;
53 // If start or end is not page-aligned, parsing went wrong. It is better to
54 // exit with an error.
55 if ((range.first & page_mask) || (range.second & page_mask)) {
56 return false; // CHECK() is not allowed here.
58 unsigned char* start_ptr = reinterpret_cast<unsigned char*>(range.first);
59 unsigned char* end_ptr = reinterpret_cast<unsigned char*>(range.second);
60 unsigned char dummy = 0;
61 for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kPageSize) {
62 // Volatile is required to prevent the compiler from eliminating this
63 // loop.
64 dummy ^= *static_cast<volatile unsigned char*>(ptr);
67 return true;
70 } // namespace
72 // static
73 bool NativeLibraryPrefetcher::IsGoodToPrefetch(
74 const base::debug::MappedMemoryRegion& region) {
75 return PathMatchesSuffix(region.path) &&
76 IsReadableAndPrivate(region); // .text and .data mappings are private.
79 // static
80 void NativeLibraryPrefetcher::FilterLibchromeRangesOnlyIfPossible(
81 const std::vector<base::debug::MappedMemoryRegion>& regions,
82 std::vector<AddressRange>* ranges) {
83 bool has_libchrome_region = false;
84 for (const base::debug::MappedMemoryRegion& region : regions) {
85 if (EndsWith(region.path, kLibchromeSuffix, CompareCase::SENSITIVE)) {
86 has_libchrome_region = true;
87 break;
90 for (const base::debug::MappedMemoryRegion& region : regions) {
91 if (has_libchrome_region &&
92 !EndsWith(region.path, kLibchromeSuffix, CompareCase::SENSITIVE)) {
93 continue;
95 ranges->push_back(std::make_pair(region.start, region.end));
99 // static
100 bool NativeLibraryPrefetcher::FindRanges(std::vector<AddressRange>* ranges) {
101 std::string proc_maps;
102 if (!base::debug::ReadProcMaps(&proc_maps))
103 return false;
104 std::vector<base::debug::MappedMemoryRegion> regions;
105 if (!base::debug::ParseProcMaps(proc_maps, &regions))
106 return false;
108 std::vector<base::debug::MappedMemoryRegion> regions_to_prefetch;
109 for (const auto& region : regions) {
110 if (IsGoodToPrefetch(region)) {
111 regions_to_prefetch.push_back(region);
115 FilterLibchromeRangesOnlyIfPossible(regions_to_prefetch, ranges);
116 return true;
119 // static
120 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() {
121 // Avoid forking with cygprofile instrumentation because the latter performs
122 // memory allocations.
123 #if defined(CYGPROFILE_INSTRUMENTATION)
124 return false;
125 #endif
127 // Looking for ranges is done before the fork, to avoid syscalls and/or memory
128 // allocations in the forked process. The child process inherits the lock
129 // state of its parent thread. It cannot rely on being able to acquire any
130 // lock (unless special care is taken in a pre-fork handler), including being
131 // able to call malloc().
132 std::vector<AddressRange> ranges;
133 if (!FindRanges(&ranges))
134 return false;
136 pid_t pid = fork();
137 if (pid == 0) {
138 setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
139 // _exit() doesn't call the atexit() handlers.
140 _exit(Prefetch(ranges) ? 0 : 1);
141 } else {
142 if (pid < 0) {
143 return false;
145 int status;
146 const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0));
147 if (result == pid) {
148 if (WIFEXITED(status)) {
149 return WEXITSTATUS(status) == 0;
152 return false;
156 } // namespace android
157 } // namespace base