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

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: Typo. 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/posix/eintr_wrapper.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_util.h"
16
17 namespace base {
18 namespace android {
19
20 NativeLibraryPrefetcher::FileLineIterator::FileLineIterator(
21 const base::FilePath& path) {
22 file_ = base::OpenFile(path, "r");
23 }
24
25 NativeLibraryPrefetcher::FileLineIterator::~FileLineIterator() {
26 if (file_) {
27 base::CloseFile(file_);
28 }
29 }
30
31 bool NativeLibraryPrefetcher::FileLineIterator::NextLine(std::string* line) {
32 if (!file_) {
33 return false;
34 }
35 char* line_str = fgets(line_buffer_, kMaxLineLength - 1, file_);
36 if (line_str) {
37 *line = std::string(line_str);
38 return true;
39 }
40 return false;
41 }
42
43 NativeLibraryPrefetcher::ProcMapsIterator::ProcMapsIterator(
44 FileLineIterator* line_iterator)
45 : line_iterator_(line_iterator) {
46 }
47
48 bool NativeLibraryPrefetcher::ProcMapsIterator::Next(
49 NativeLibraryPrefetcher::ProcMapsIterator::Mapping* mapping) {
50 std::string line;
51 bool ok = line_iterator_->NextLine(&line);
52 if (!ok) {
53 return false;
54 }
55 // Example:
56 // b6e62000-b6e64000 r-xp 00000000 b3:15 1204 /system/lib/libstdc++.so
57 // start-end flags offset device inode [filename]
58 std::vector<std::string> pieces;
59 base::SplitStringAlongWhitespace(line, &pieces);
60 size_t pieces_count = pieces.size();
61 // Filename is optional
62 if ((pieces_count != 5) && (pieces_count != 6)) {
pasko 2015/03/25 12:57:58 a filename might contain a space, right?
Benoit L 2015/03/26 15:45:03 Done.
63 return false;
64 }
65
66 std::vector<std::string> start_end;
67 base::SplitString(pieces[0], '-', &start_end);
68 if (start_end.size() != 2) {
69 return false;
70 }
71 ok = base::HexStringToUInt64(start_end[0], &mapping->start);
72 ok &= base::HexStringToUInt64(start_end[1], &mapping->end);
73 mapping->flags = pieces[1];
74 ok &= base::HexStringToUInt64(pieces[2], &mapping->offset);
75 mapping->device = pieces[3];
76 ok &= base::StringToInt64(pieces[4], &mapping->inode);
77 if (!ok) {
78 return false;
79 }
80 if (pieces_count == 5) {
81 mapping->filename = std::string();
82 } else {
83 mapping->filename = pieces[5];
84 }
85 return true;
86 }
87
88 namespace {
89
90 // Android defines the background priority to this value since at least 2009
91 // (see Process.java).
92 const int kBackgroundPriority = 10;
93 // Valid for all the Android architectures.
94 const size_t kPageSize = 4096;
95 // We may load directly from the APK.
96 const int kSuffixesToMatchCount = 2;
97 const std::string kSuffixesToMatch[] = {"libchrome.so", "base.apk"};
98
99 bool IsReadableAndPrivate(const std::string& flags) {
100 return flags.size() == 4 && flags[0] == 'r' && flags[3] == 'p';
101 }
102
103 bool FilenameMatchesSuffix(const std::string& filename) {
104 for (int i = 0; i < kSuffixesToMatchCount; i++) {
105 if (EndsWith(filename, kSuffixesToMatch[i], true)) {
106 return true;
107 }
108 }
109 return false;
110 }
111
112 // For each range, reads a byte per page to force it into the page cache.
113 void Prefetch(const std::vector<std::pair<uint64_t, uint64_t>>& ranges) {
114 for (const auto& range : ranges) {
115 const uint64_t page_mask = kPageSize - 1;
116 CHECK(!(range.first & page_mask) && !(range.second & page_mask));
117 unsigned char* start_address = (unsigned char*) range.first;
118 unsigned char* end_address = (unsigned char*) range.second;
119 unsigned char dummy;
120 for (unsigned char* addr = start_address; addr < end_address;
121 addr += kPageSize) {
122 // Volatile is required to prevent the compiler from eliminating this
123 // loop.
124 dummy ^= *((volatile unsigned char*)addr);
125 }
126 }
127 }
128
129 } // namespace
130
131 std::vector<std::pair<uint64_t, uint64_t>>
132 NativeLibraryPrefetcher::ProcMapsIterator::FindRanges(
133 FileLineIterator* file_line_iterator) {
134 std::vector<std::pair<uint64_t, uint64_t>> ranges;
135 ProcMapsIterator maps_iterator(file_line_iterator);
136 NativeLibraryPrefetcher::ProcMapsIterator::Mapping mapping;
137
138 while (maps_iterator.Next(&mapping)) {
139 if (FilenameMatchesSuffix(mapping.filename) &&
140 IsReadableAndPrivate(mapping.flags)) {
141 ranges.push_back(std::make_pair(mapping.start, mapping.end));
142 }
143 }
144 return ranges;
145 }
146
147 // Forks and waits for a process prefetching the native library. This is done in
148 // a forked process for the following reasons:
149 // - Isolating the main process from mistakes in the parsing. If the parsing
150 // returns an incorrect address, only the forked process will crash.
151 // - Not inflating the memory used by the main process uselessly, which could
152 // increase its likelihood to be killed.
153 // The forked process has background priority and, since it is not declared to
154 // the android runtime, can be killed at any time, which is not an issue here.
155 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() {
156 // Looking for ranges is done before the fork, to avoid syscalls and/or
157 // memory allocations in the forked process, that can be problematic.
158 FileLineIterator file_line_iterator(base::FilePath("/proc/self/maps"));
159 std::vector<std::pair<uint64_t, uint64_t>> ranges =
160 ProcMapsIterator::FindRanges(&file_line_iterator);
161 pid_t pid = fork();
162 if (pid == 0) {
163 setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
164 Prefetch(ranges);
165 exit(0);
166 } else {
167 if (pid < 0) {
168 return false;
169 }
170 int status;
171 const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0));
172 if (result == pid) {
173 if (WIFEXITED(status)) {
174 return WEXITSTATUS(status) == 0;
175 }
176 if (WIFSIGNALED(status)) {
pasko 2015/03/25 12:57:58 why checking this? we would have returned false an
Benoit L 2015/03/26 15:45:03 To log whether the process was killed or crashed.
177 return false;
178 }
179 }
180 return false;
181 }
182 }
183
184 } // namespace android
185 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698