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>
13 #include "base/macros.h"
14 #include "base/posix/eintr_wrapper.h"
15 #include "base/strings/string_util.h"
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
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
)) {
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
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
64 dummy
^= *static_cast<volatile unsigned char*>(ptr
);
73 bool NativeLibraryPrefetcher::IsGoodToPrefetch(
74 const base::debug::MappedMemoryRegion
& region
) {
75 return PathMatchesSuffix(region
.path
) &&
76 IsReadableAndPrivate(region
); // .text and .data mappings are private.
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;
90 for (const base::debug::MappedMemoryRegion
& region
: regions
) {
91 if (has_libchrome_region
&&
92 !EndsWith(region
.path
, kLibchromeSuffix
, CompareCase::SENSITIVE
)) {
95 ranges
->push_back(std::make_pair(region
.start
, region
.end
));
100 bool NativeLibraryPrefetcher::FindRanges(std::vector
<AddressRange
>* ranges
) {
101 std::string proc_maps
;
102 if (!base::debug::ReadProcMaps(&proc_maps
))
104 std::vector
<base::debug::MappedMemoryRegion
> regions
;
105 if (!base::debug::ParseProcMaps(proc_maps
, ®ions
))
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
);
120 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() {
121 // Looking for ranges is done before the fork, to avoid syscalls and/or memory
122 // allocations in the forked process. The child process inherits the lock
123 // state of its parent thread. It cannot rely on being able to acquire any
124 // lock (unless special care is taken in a pre-fork handler), including being
125 // able to call malloc().
126 std::vector
<AddressRange
> ranges
;
127 if (!FindRanges(&ranges
))
131 setpriority(PRIO_PROCESS
, 0, kBackgroundPriority
);
132 // _exit() doesn't call the atexit() handlers.
133 _exit(Prefetch(ranges
) ? 0 : 1);
139 const pid_t result
= HANDLE_EINTR(waitpid(pid
, &status
, 0));
141 if (WIFEXITED(status
)) {
142 return WEXITSTATUS(status
) == 0;
149 } // namespace android