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

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: Address comments. Created 5 years, 9 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/logging.h"
14 #include "base/posix/eintr_wrapper.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17
18 namespace base {
19 namespace android {
20
21 namespace {
22
23 // Android defines the background priority to this value since at least 2009
pasko 2015/03/27 13:29:28 If THREAD_PRIORITY_BACKGROUND can change, let's ju
Benoit L 2015/03/27 15:44:52 Well, this adds clutter, and as you said, is highl
24 // (see Process.java).
25 const int kBackgroundPriority = 10;
26 // Valid for all the Android architectures.
27 const size_t kPageSize = 4096;
28 // We may load directly from the APK.
29 const int kSuffixesToMatchCount = 2;
30 const std::string kSuffixesToMatch[] = {"libchrome.so", "base.apk"};
pasko 2015/03/27 13:29:28 <quote> ... we only allow static variables to cont
Benoit L 2015/03/27 15:44:52 Done.
31
32 bool IsReadableAndPrivate(const std::string& flags) {
33 return flags.size() == 4 && flags[0] == 'r' && flags[3] == 'p';
34 }
35
36 bool FilenameMatchesSuffix(const std::string& filename) {
37 for (int i = 0; i < kSuffixesToMatchCount; i++) {
38 if (EndsWith(filename, kSuffixesToMatch[i], true)) {
39 return true;
40 }
41 }
42 return false;
43 }
44
45 // For each range, reads a byte per page to force it into the page cache.
pasko 2015/03/27 17:51:54 can you add a comment saying that heap allocations
46 void Prefetch(const std::vector<std::pair<uint64_t, uint64_t>>& ranges) {
47 for (const auto& range : ranges) {
48 const uint64_t page_mask = kPageSize - 1;
49 CHECK(!(range.first & page_mask) && !(range.second & page_mask));
50 unsigned char* start_address = (unsigned char*)range.first;
51 unsigned char* end_address = (unsigned char*)range.second;
52 unsigned char dummy;
53 for (unsigned char* addr = start_address; addr < end_address;
54 addr += kPageSize) {
55 // Volatile is required to prevent the compiler from eliminating this
56 // loop.
57 dummy ^= *((volatile unsigned char*)addr);
58 }
59 }
60 }
61
62 } // namespace
63
64 bool NativeLibraryPrefetcher::ParseMapping(const std::string& line,
pasko 2015/03/27 13:29:28 // static (same for the 2 other functions)
Benoit L 2015/03/27 15:44:51 Done.
65 Mapping* mapping) {
66 // Example:
67 // b6e62000-b6e64000 r-xp 00000000 b3:15 1204 /system/lib/libstdc++.so
68 // start-end flags offset device inode [filename]
69 std::vector<std::string> pieces;
70 base::SplitStringAlongWhitespace(line, &pieces);
71 size_t pieces_count = pieces.size();
72 // Filename is optional, so at least 5 fields.
73 if (pieces_count < 5) {
74 return false;
75 }
76
77 std::vector<std::string> start_end;
78 base::SplitString(pieces[0], '-', &start_end);
79 if (start_end.size() != 2) {
80 return false;
81 }
82 bool ok = base::HexStringToUInt64(start_end[0], &mapping->start);
83 ok &= base::HexStringToUInt64(start_end[1], &mapping->end);
84 mapping->flags = pieces[1];
85 ok &= base::HexStringToUInt64(pieces[2], &mapping->offset);
86 mapping->device = pieces[3];
87 ok &= base::StringToInt64(pieces[4], &mapping->inode);
88 if (!ok) {
89 return false;
90 }
91 if (pieces_count == 5) {
92 mapping->filename = std::string();
93 } else {
94 // Truncates filenames containing spaces, but these are not the droids we're
95 // looking for.
96 mapping->filename = pieces[5];
97 }
98 return true;
99 }
100
101 bool NativeLibraryPrefetcher::MappingMatches(const Mapping& mapping) {
102 return FilenameMatchesSuffix(mapping.filename) &&
103 IsReadableAndPrivate(mapping.flags); // Code mappings are private.
104 }
105
106 bool NativeLibraryPrefetcher::FindRanges(
107 std::vector<std::pair<uint64_t, uint64_t>>* ranges) {
108 base::ScopedFILE file(base::OpenFile(base::FilePath("/proc/self/maps"), "r"));
pasko 2015/03/27 13:29:27 OpenFile is the implementation detail of base::Fil
Benoit L 2015/03/27 15:44:52 We need a FILE*, and not a File. And File::Platfor
pasko 2015/03/27 17:51:54 ah, you are right, fgets, and there is no line by
109 if (!file.get()) {
110 return false;
111 }
112 std::vector<Mapping> mappings;
113 const size_t kMaxLineLength = 4096;
114 char line_buffer[kMaxLineLength];
115 while (char* line_str = fgets(line_buffer, kMaxLineLength - 1, file.get())) {
116 std::string line(line_str);
117 Mapping mapping;
118 if (!ParseMapping(line, &mapping)) {
119 continue;
120 }
121 if (MappingMatches(mapping)) {
122 mappings.push_back(mapping);
123 }
124 }
125 const std::string& libchrome_suffix = kSuffixesToMatch[0];
pasko 2015/03/27 13:29:27 nit: it's probably nice to separate this part with
Benoit L 2015/03/27 15:44:52 Done.
126 bool has_libchrome_mapping = false;
127 for (const Mapping& mapping : mappings) {
128 if (EndsWith(mapping.filename, libchrome_suffix, true)) {
129 has_libchrome_mapping = true;
130 break;
131 }
132 }
133 for (const Mapping& mapping : mappings) {
134 if (has_libchrome_mapping
135 && !EndsWith(mapping.filename, libchrome_suffix, true)) {
136 continue;
137 }
138 ranges->push_back(std::make_pair(mapping.start, mapping.end));
139 }
140 return true;
141 }
142
143 // Forks and waits for a process prefetching the native library. This is done in
144 // a forked process for the following reasons:
145 // - Isolating the main process from mistakes in the parsing. If the parsing
pasko 2015/03/27 11:03:37 Forking does not fully 'isolate' because the proce
146 // returns an incorrect address, only the forked process will crash.
147 // - Not inflating the memory used by the main process uselessly, which could
148 // increase its likelihood to be killed.
149 // The forked process has background priority and, since it is not declared to
150 // the android runtime, can be killed at any time, which is not an issue here.
151 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() {
152 // Looking for ranges is done before the fork, to avoid syscalls and/or
153 // memory allocations in the forked process, that can be problematic.
pasko 2015/03/27 17:51:54 s/ that can be problematic// does not add any use
154 std::vector<std::pair<uint64_t, uint64_t>> ranges;
155 if (!FindRanges(&ranges)) {
156 return false;
157 }
158 pid_t pid = fork();
pasko 2015/03/27 11:03:37 what if the child process deadlocks? It might happ
Benoit L 2015/03/27 15:44:51 This process doesn't do any heap allocation, and d
159 if (pid == 0) {
160 setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
161 Prefetch(ranges);
162 exit(0);
pasko 2015/03/27 11:03:37 this would invoke destructors which we don't do no
Benoit L 2015/03/27 15:44:51 _exit(2) doesn't call the atexit() handlers. Done.
163 } else {
164 if (pid < 0) {
165 return false;
166 }
167 int status;
168 const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0));
169 if (result == pid) {
170 if (WIFEXITED(status)) {
171 return WEXITSTATUS(status) == 0;
172 }
173 if (WIFSIGNALED(status)) {
174 switch (WTERMSIG(status)) {
175 case SIGABRT:
176 case SIGBUS:
177 case SIGFPE:
178 case SIGILL:
179 case SIGSEGV:
180 LOG(WARNING) << "The native library prefetching process crashed ("
181 << pid << ")";
pasko 2015/03/27 11:03:37 break;
Benoit L 2015/03/27 15:44:52 Done.
pasko 2015/03/27 17:51:54 I like how this got done :)
182 case SIGINT:
183 case SIGKILL:
184 case SIGTERM:
185 LOG(WARNING) << "The native library prefetching process was killed "
pasko 2015/03/27 11:03:37 is there a reason to distinguish these cases? If y
Benoit L 2015/03/27 15:44:52 Done.
186 << "(" << pid << ")";
187 default:
188 break;
189 }
190 }
191 }
192 return false;
193 }
194 }
195
196 } // namespace android
197 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698