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

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: . 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 <stdio.h>
8 #include <sys/resource.h>
9 #include <sys/wait.h>
10 #include <unistd.h>
11 #include <utility>
12 #include <vector>
13
14 #include "base/logging.h"
15 #include "base/posix/eintr_wrapper.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18
19 namespace base {
20 namespace android {
21
22 namespace {
23
24 // Android defines the background priority to this value since at least 2009
25 // (see Process.java).
26 const int kBackgroundPriority = 10;
27 // Valid for all the Android architectures.
28 const size_t kPageSize = 4096;
29 // We may load directly from the APK.
30 const int kSuffixesToMatchCount = 2;
31 const char* kSuffixesToMatch[] = {"libchrome.so", "base.apk"};
32
33 bool IsReadableAndPrivate(const std::string& flags) {
34 return flags.size() == 4 && flags[0] == 'r' && flags[3] == 'p';
35 }
36
37 bool FilenameMatchesSuffix(const std::string& filename) {
38 for (int i = 0; i < kSuffixesToMatchCount; i++) {
39 if (EndsWith(filename, kSuffixesToMatch[i], true)) {
40 return true;
41 }
42 }
43 return false;
44 }
45
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 void Prefetch(const std::vector<std::pair<uint64_t, uint64_t>>& ranges) {
50 for (const auto& range : ranges) {
51 const uint64_t page_mask = kPageSize - 1;
52 CHECK(!(range.first & page_mask) && !(range.second & page_mask));
53 unsigned char* start_address = (unsigned char*)range.first;
pasko 2015/03/31 12:01:43 C-style casts are discouraged, please use the leas
Benoit L 2015/03/31 14:00:01 Done.
54 unsigned char* end_address = (unsigned char*)range.second;
55 unsigned char dummy;
56 for (unsigned char* addr = start_address; addr < end_address;
57 addr += kPageSize) {
58 // Volatile is required to prevent the compiler from eliminating this
59 // loop.
60 dummy ^= *((volatile unsigned char*)addr);
61 }
62 }
63 }
64
65 } // namespace
66
67 // static
68 bool NativeLibraryPrefetcher::ParseMapping(const std::string& line,
69 Mapping* mapping) {
70 // Example:
71 // b6e62000-b6e64000 r-xp 00000000 b3:15 1204 /system/lib/libstdc++.so
72 // start-end flags offset device inode [filename]
73 std::vector<std::string> pieces;
74 base::SplitStringAlongWhitespace(line, &pieces);
75 size_t pieces_count = pieces.size();
76 // Filename is optional, so at least 5 fields.
77 if (pieces_count < 5) {
78 return false;
79 }
80
81 std::vector<std::string> start_end;
82 base::SplitString(pieces[0], '-', &start_end);
83 if (start_end.size() != 2) {
84 return false;
85 }
86 bool ok = base::HexStringToUInt64(start_end[0], &mapping->start);
87 ok &= base::HexStringToUInt64(start_end[1], &mapping->end);
88 mapping->flags = pieces[1];
89 // Discards fields 3, 4, and 5 starting from 1 (ie offset, device and inode).
pasko 2015/03/31 12:01:43 We use this form 'Discards' and 'Truncates' when w
Benoit L 2015/03/31 14:00:01 Done.
90 if (!ok) {
91 return false;
92 }
93 if (pieces_count == 5) {
94 mapping->filename = std::string();
95 } else {
96 // Truncates filenames containing spaces, but these are not the droids
97 // you're looking for.
98 mapping->filename = pieces[5];
99 }
100 return true;
101 }
102
103 // static
104 bool NativeLibraryPrefetcher::IsGoodToPrefetch(const Mapping& mapping) {
105 return FilenameMatchesSuffix(mapping.filename) &&
106 IsReadableAndPrivate(mapping.flags); // Code mappings are private.
pasko 2015/03/31 12:01:43 nit: s/Code/Code and data/ ?
Benoit L 2015/03/31 14:00:01 Done.
107 }
108
109 // static
110 bool NativeLibraryPrefetcher::FindRanges(
111 std::vector<std::pair<uint64_t, uint64_t>>* ranges) {
112 base::ScopedFILE file(base::OpenFile(base::FilePath("/proc/self/maps"), "r"));
113 if (!file.get()) {
114 return false;
115 }
116 std::vector<Mapping> mappings;
117 const size_t kMaxLineLength = 4096;
118 char line_buffer[kMaxLineLength];
119 while (char* line_str = fgets(line_buffer, kMaxLineLength - 1, file.get())) {
120 std::string line(line_str);
121 Mapping mapping;
122 if (!ParseMapping(line, &mapping)) {
123 continue;
124 }
125 if (IsGoodToPrefetch(mapping)) {
126 mappings.push_back(mapping);
127 }
128 }
129
130 // If libchrome.so is mapped, keep only libchrome's ranges.
pasko 2015/03/31 12:01:43 please move it to another function and test separa
Benoit L 2015/03/31 14:00:01 Done.
131 const std::string& libchrome_suffix = kSuffixesToMatch[0];
132 bool has_libchrome_mapping = false;
133 for (const Mapping& mapping : mappings) {
134 if (EndsWith(mapping.filename, libchrome_suffix, true)) {
135 has_libchrome_mapping = true;
136 break;
137 }
138 }
139 for (const Mapping& mapping : mappings) {
140 if (has_libchrome_mapping
141 && !EndsWith(mapping.filename, libchrome_suffix, true)) {
142 continue;
143 }
144 ranges->push_back(std::make_pair(mapping.start, mapping.end));
145 }
146 return true;
147 }
148
149 // Forks and waits for a process prefetching the native library. This is done in
150 // a forked process for the following reasons:
151 // - Isolating the main process from mistakes in the parsing. If the parsing
152 // returns an incorrect address, only the forked process will crash.
153 // - Not inflating the memory used by the main process uselessly, which could
154 // increase its likelihood to be killed.
155 // The forked process has background priority and, since it is not declared to
156 // the android runtime, can be killed at any time, which is not an issue here.
157 // static
158 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() {
159 // Looking for ranges is done before the fork, to avoid syscalls and/or memory
160 // allocations in the forked process. The child process inherits the lock
161 // state of all the parent thread locks. It cannot rely on being able to
162 // acquire any lock (unless special care is taken in a pre-fork handler),
163 // including being able to call malloc().
164 std::vector<std::pair<uint64_t, uint64_t>> ranges;
165 if (!FindRanges(&ranges)) {
166 return false;
167 }
168 pid_t pid = fork();
169 if (pid == 0) {
170 setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
171 Prefetch(ranges);
172 _exit(0); // Don't call the atexit() handlers.
173 } else {
174 if (pid < 0) {
175 return false;
176 }
177 int status;
178 const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0));
179 if (result == pid) {
180 if (WIFEXITED(status)) {
181 return WEXITSTATUS(status) == 0;
182 }
183 }
184 return false;
185 }
186 }
187
188 } // namespace android
189 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698