Index: base/android/library_loader/library_prefetcher.cc |
diff --git a/base/android/library_loader/library_prefetcher.cc b/base/android/library_loader/library_prefetcher.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1a68f1b55ac7264efdbfce4c1d47d2243bbf6248 |
--- /dev/null |
+++ b/base/android/library_loader/library_prefetcher.cc |
@@ -0,0 +1,185 @@ |
+// Copyright 2015 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/android/library_loader/library_prefetcher.h" |
+ |
+#include <sys/resource.h> |
+#include <sys/wait.h> |
+#include <unistd.h> |
+#include <utility> |
+#include <vector> |
+ |
+#include "base/posix/eintr_wrapper.h" |
+#include "base/strings/string_number_conversions.h" |
+#include "base/strings/string_util.h" |
+ |
+namespace base { |
+namespace android { |
+ |
+NativeLibraryPrefetcher::FileLineIterator::FileLineIterator( |
+ const base::FilePath& path) { |
+ file_ = base::OpenFile(path, "r"); |
+} |
+ |
+NativeLibraryPrefetcher::FileLineIterator::~FileLineIterator() { |
+ if (file_) { |
+ base::CloseFile(file_); |
+ } |
+} |
+ |
+bool NativeLibraryPrefetcher::FileLineIterator::NextLine(std::string* line) { |
+ if (!file_) { |
+ return false; |
+ } |
+ char* line_str = fgets(line_buffer_, kMaxLineLength - 1, file_); |
+ if (line_str) { |
+ *line = std::string(line_str); |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+NativeLibraryPrefetcher::ProcMapsIterator::ProcMapsIterator( |
+ FileLineIterator* line_iterator) |
+ : line_iterator_(line_iterator) { |
+} |
+ |
+bool NativeLibraryPrefetcher::ProcMapsIterator::Next( |
+ NativeLibraryPrefetcher::ProcMapsIterator::Mapping* mapping) { |
+ std::string line; |
+ bool ok = line_iterator_->NextLine(&line); |
+ if (!ok) { |
+ return false; |
+ } |
+ // Example: |
+ // b6e62000-b6e64000 r-xp 00000000 b3:15 1204 /system/lib/libstdc++.so |
+ // start-end flags offset device inode [filename] |
+ std::vector<std::string> pieces; |
+ base::SplitStringAlongWhitespace(line, &pieces); |
+ size_t pieces_count = pieces.size(); |
+ // Filename is optional |
+ 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.
|
+ return false; |
+ } |
+ |
+ std::vector<std::string> start_end; |
+ base::SplitString(pieces[0], '-', &start_end); |
+ if (start_end.size() != 2) { |
+ return false; |
+ } |
+ ok = base::HexStringToUInt64(start_end[0], &mapping->start); |
+ ok &= base::HexStringToUInt64(start_end[1], &mapping->end); |
+ mapping->flags = pieces[1]; |
+ ok &= base::HexStringToUInt64(pieces[2], &mapping->offset); |
+ mapping->device = pieces[3]; |
+ ok &= base::StringToInt64(pieces[4], &mapping->inode); |
+ if (!ok) { |
+ return false; |
+ } |
+ if (pieces_count == 5) { |
+ mapping->filename = std::string(); |
+ } else { |
+ mapping->filename = pieces[5]; |
+ } |
+ return true; |
+} |
+ |
+namespace { |
+ |
+// Android defines the background priority to this value since at least 2009 |
+// (see Process.java). |
+const int kBackgroundPriority = 10; |
+// Valid for all the Android architectures. |
+const size_t kPageSize = 4096; |
+// We may load directly from the APK. |
+const int kSuffixesToMatchCount = 2; |
+const std::string kSuffixesToMatch[] = {"libchrome.so", "base.apk"}; |
+ |
+bool IsReadableAndPrivate(const std::string& flags) { |
+ return flags.size() == 4 && flags[0] == 'r' && flags[3] == 'p'; |
+} |
+ |
+bool FilenameMatchesSuffix(const std::string& filename) { |
+ for (int i = 0; i < kSuffixesToMatchCount; i++) { |
+ if (EndsWith(filename, kSuffixesToMatch[i], true)) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+// For each range, reads a byte per page to force it into the page cache. |
+void Prefetch(const std::vector<std::pair<uint64_t, uint64_t>>& ranges) { |
+ for (const auto& range : ranges) { |
+ const uint64_t page_mask = kPageSize - 1; |
+ CHECK(!(range.first & page_mask) && !(range.second & page_mask)); |
+ unsigned char* start_address = (unsigned char*) range.first; |
+ unsigned char* end_address = (unsigned char*) range.second; |
+ unsigned char dummy; |
+ for (unsigned char* addr = start_address; addr < end_address; |
+ addr += kPageSize) { |
+ // Volatile is required to prevent the compiler from eliminating this |
+ // loop. |
+ dummy ^= *((volatile unsigned char*)addr); |
+ } |
+ } |
+} |
+ |
+} // namespace |
+ |
+std::vector<std::pair<uint64_t, uint64_t>> |
+NativeLibraryPrefetcher::ProcMapsIterator::FindRanges( |
+ FileLineIterator* file_line_iterator) { |
+ std::vector<std::pair<uint64_t, uint64_t>> ranges; |
+ ProcMapsIterator maps_iterator(file_line_iterator); |
+ NativeLibraryPrefetcher::ProcMapsIterator::Mapping mapping; |
+ |
+ while (maps_iterator.Next(&mapping)) { |
+ if (FilenameMatchesSuffix(mapping.filename) && |
+ IsReadableAndPrivate(mapping.flags)) { |
+ ranges.push_back(std::make_pair(mapping.start, mapping.end)); |
+ } |
+ } |
+ return ranges; |
+} |
+ |
+// Forks and waits for a process prefetching the native library. This is done in |
+// a forked process for the following reasons: |
+// - Isolating the main process from mistakes in the parsing. If the parsing |
+// returns an incorrect address, only the forked process will crash. |
+// - Not inflating the memory used by the main process uselessly, which could |
+// increase its likelihood to be killed. |
+// The forked process has background priority and, since it is not declared to |
+// the android runtime, can be killed at any time, which is not an issue here. |
+bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() { |
+ // Looking for ranges is done before the fork, to avoid syscalls and/or |
+ // memory allocations in the forked process, that can be problematic. |
+ FileLineIterator file_line_iterator(base::FilePath("/proc/self/maps")); |
+ std::vector<std::pair<uint64_t, uint64_t>> ranges = |
+ ProcMapsIterator::FindRanges(&file_line_iterator); |
+ pid_t pid = fork(); |
+ if (pid == 0) { |
+ setpriority(PRIO_PROCESS, 0, kBackgroundPriority); |
+ Prefetch(ranges); |
+ exit(0); |
+ } else { |
+ if (pid < 0) { |
+ return false; |
+ } |
+ int status; |
+ const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0)); |
+ if (result == pid) { |
+ if (WIFEXITED(status)) { |
+ return WEXITSTATUS(status) == 0; |
+ } |
+ 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.
|
+ return false; |
+ } |
+ } |
+ return false; |
+ } |
+} |
+ |
+} // namespace android |
+} // namespace base |