Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 | |
| OLD | NEW |