Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(130)

Side by Side Diff: base/android/library_loader/library_prefetcher.cc

Issue 1001343002: Prefetch the native library from native code by parsing /proc/pid/maps. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Re-use existing /proc/maps parsing code. Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 <sys/resource.h>
8 #include <sys/wait.h>
9 #include <unistd.h>
10 #include <utility>
11 #include <vector>
12
13 #include "base/posix/eintr_wrapper.h"
14 #include "base/strings/string_util.h"
15
16 namespace base {
17 namespace android {
18
19 namespace {
20
21 // Android defines the background priority to this value since at least 2009
22 // (see Process.java).
23 const int kBackgroundPriority = 10;
24 // Valid for all the Android architectures.
25 const size_t kPageSize = 4096;
26 // We may load directly from the APK.
27 const int kSuffixesToMatchCount = 2;
28 const char* kSuffixesToMatch[] = {"libchrome.so", "base.apk"};
29
30 bool IsReadableAndPrivate(const base::debug::MappedMemoryRegion& region) {
31 return region.permissions & base::debug::MappedMemoryRegion::READ &&
32 region.permissions & base::debug::MappedMemoryRegion::PRIVATE;
33 }
34
35 bool PathMatchesSuffix(const std::string& path) {
36 for (int i = 0; i < kSuffixesToMatchCount; i++) {
Robert Sesek 2015/04/02 15:58:02 You can remove this in favor of arraysize(kSuffixe
Benoit L 2015/04/02 16:43:48 Thank you for the suggestion! Done.
37 if (EndsWith(path, kSuffixesToMatch[i], true)) {
38 return true;
39 }
40 }
41 return false;
42 }
43
44 // For each range, reads a byte per page to force it into the page cache.
45 // Heap allocations, syscalls and library functions are not allowed in this
46 // function.
47 // Returns true for success.
48 bool Prefetch(const std::vector<std::pair<uintptr_t, uintptr_t>>& ranges) {
Robert Sesek 2015/04/02 15:58:02 Consider making this pair a named type (in the .h
Benoit L 2015/04/02 16:43:48 Thank you for the suggestion. I've updated the oth
49 for (const auto& range : ranges) {
50 const uintptr_t page_mask = kPageSize - 1;
51 // If start or end is not page-aligned, parsing went wrong. It is better to
52 // exit with an error.
53 if ((range.first & page_mask) || (range.second & page_mask)) {
54 return false; // CHECK() is not allowed here.
55 }
56 unsigned char* start_ptr = reinterpret_cast<unsigned char*>(range.first);
57 unsigned char* end_ptr = reinterpret_cast<unsigned char*>(range.second);
58 unsigned char dummy;
59 for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kPageSize) {
60 // Volatile is required to prevent the compiler from eliminating this
61 // loop.
62 dummy ^= *static_cast<volatile unsigned char*>(ptr);
63 }
64 }
65 return true;
66 }
67
68 } // namespace
69
70 // static
71 bool NativeLibraryPrefetcher::IsGoodToPrefetch(
72 const base::debug::MappedMemoryRegion& region) {
73 return PathMatchesSuffix(region.path) &&
74 IsReadableAndPrivate(region); // .text and .data mappings are private.
75 }
76
77 // static
78 void NativeLibraryPrefetcher::TakeOnlyLibchromeRangesIfPossible(
79 const std::vector<base::debug::MappedMemoryRegion>& regions,
80 std::vector<std::pair<uintptr_t, uintptr_t>>* ranges) {
81 const std::string& libchrome_suffix = kSuffixesToMatch[0];
82 bool has_libchrome_region = false;
83 for (const base::debug::MappedMemoryRegion& region : regions) {
84 if (EndsWith(region.path, libchrome_suffix, true)) {
85 has_libchrome_region = true;
86 break;
87 }
88 }
89 for (const base::debug::MappedMemoryRegion& region : regions) {
90 if (has_libchrome_region &&
91 !EndsWith(region.path, libchrome_suffix, true)) {
92 continue;
93 }
94 ranges->push_back(std::make_pair(region.start, region.end));
95 }
96 }
97
98 // static
99 bool NativeLibraryPrefetcher::FindRanges(
100 std::vector<std::pair<uintptr_t, uintptr_t>>* 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 (!ParseProcMaps(proc_maps, &regions))
Robert Sesek 2015/04/02 15:58:02 base::debug:: ?
Benoit L 2015/04/02 16:43:48 Done.
106 return false;
107
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);
112 }
113 }
114
115 TakeOnlyLibchromeRangesIfPossible(regions_to_prefetch, ranges);
116 return true;
117 }
118
119 // Forks and waits for a process prefetching the native library. This is done in
120 // a forked process for the following reasons:
121 // - Isolating the main process from mistakes in the parsing. If the parsing
122 // returns an incorrect address, only the forked process will crash.
123 // - Not inflating the memory used by the main process uselessly, which could
124 // increase its likelihood to be killed.
125 // The forked process has background priority and, since it is not declared to
126 // the android runtime, can be killed at any time, which is not an issue here.
127 // static
128 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() {
129 // Looking for ranges is done before the fork, to avoid syscalls and/or memory
130 // allocations in the forked process. The child process inherits the lock
131 // state of its parent thread. It cannot rely on being able to acquire any
132 // lock (unless special care is taken in a pre-fork handler), including being
133 // able to call malloc().
134 std::vector<std::pair<uintptr_t, uintptr_t>> ranges;
135 if (!FindRanges(&ranges))
136 return false;
137 pid_t pid = fork();
138 if (pid == 0) {
139 setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
140 // _exit() doesn't call the atexit() handlers.
141 _exit(Prefetch(ranges) ? 0 : 1);
142 } else {
143 if (pid < 0) {
144 return false;
145 }
146 int status;
147 const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0));
148 if (result == pid) {
149 if (WIFEXITED(status)) {
150 return WEXITSTATUS(status) == 0;
151 }
152 }
153 return false;
154 }
155 }
156
157 } // namespace android
158 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698