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..83ad376e974b7fe4fb14862fd4f80ec2eafa3e62 |
--- /dev/null |
+++ b/base/android/library_loader/library_prefetcher.cc |
@@ -0,0 +1,158 @@ |
+// 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_util.h" |
+ |
+namespace base { |
+namespace android { |
+ |
+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 char* kSuffixesToMatch[] = {"libchrome.so", "base.apk"}; |
+ |
+bool IsReadableAndPrivate(const base::debug::MappedMemoryRegion& region) { |
+ return region.permissions & base::debug::MappedMemoryRegion::READ && |
+ region.permissions & base::debug::MappedMemoryRegion::PRIVATE; |
+} |
+ |
+bool PathMatchesSuffix(const std::string& path) { |
+ for (int i = 0; i < kSuffixesToMatchCount; i++) { |
Robert Sesek
2015/04/02 15:58:02
You can remove this in favor of arraysize(kSuffixe
Benoit L
2015/04/02 16:43:48
Thank you for the suggestion!
Done.
|
+ if (EndsWith(path, kSuffixesToMatch[i], true)) { |
+ return true; |
+ } |
+ } |
+ return false; |
+} |
+ |
+// For each range, reads a byte per page to force it into the page cache. |
+// Heap allocations, syscalls and library functions are not allowed in this |
+// function. |
+// Returns true for success. |
+bool Prefetch(const std::vector<std::pair<uintptr_t, uintptr_t>>& ranges) { |
Robert Sesek
2015/04/02 15:58:02
Consider making this pair a named type (in the .h
Benoit L
2015/04/02 16:43:48
Thank you for the suggestion.
I've updated the oth
|
+ for (const auto& range : ranges) { |
+ const uintptr_t page_mask = kPageSize - 1; |
+ // If start or end is not page-aligned, parsing went wrong. It is better to |
+ // exit with an error. |
+ if ((range.first & page_mask) || (range.second & page_mask)) { |
+ return false; // CHECK() is not allowed here. |
+ } |
+ unsigned char* start_ptr = reinterpret_cast<unsigned char*>(range.first); |
+ unsigned char* end_ptr = reinterpret_cast<unsigned char*>(range.second); |
+ unsigned char dummy; |
+ for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kPageSize) { |
+ // Volatile is required to prevent the compiler from eliminating this |
+ // loop. |
+ dummy ^= *static_cast<volatile unsigned char*>(ptr); |
+ } |
+ } |
+ return true; |
+} |
+ |
+} // namespace |
+ |
+// static |
+bool NativeLibraryPrefetcher::IsGoodToPrefetch( |
+ const base::debug::MappedMemoryRegion& region) { |
+ return PathMatchesSuffix(region.path) && |
+ IsReadableAndPrivate(region); // .text and .data mappings are private. |
+} |
+ |
+// static |
+void NativeLibraryPrefetcher::TakeOnlyLibchromeRangesIfPossible( |
+ const std::vector<base::debug::MappedMemoryRegion>& regions, |
+ std::vector<std::pair<uintptr_t, uintptr_t>>* ranges) { |
+ const std::string& libchrome_suffix = kSuffixesToMatch[0]; |
+ bool has_libchrome_region = false; |
+ for (const base::debug::MappedMemoryRegion& region : regions) { |
+ if (EndsWith(region.path, libchrome_suffix, true)) { |
+ has_libchrome_region = true; |
+ break; |
+ } |
+ } |
+ for (const base::debug::MappedMemoryRegion& region : regions) { |
+ if (has_libchrome_region && |
+ !EndsWith(region.path, libchrome_suffix, true)) { |
+ continue; |
+ } |
+ ranges->push_back(std::make_pair(region.start, region.end)); |
+ } |
+} |
+ |
+// static |
+bool NativeLibraryPrefetcher::FindRanges( |
+ std::vector<std::pair<uintptr_t, uintptr_t>>* ranges) { |
+ std::string proc_maps; |
+ if (!base::debug::ReadProcMaps(&proc_maps)) |
+ return false; |
+ std::vector<base::debug::MappedMemoryRegion> regions; |
+ if (!ParseProcMaps(proc_maps, ®ions)) |
Robert Sesek
2015/04/02 15:58:02
base::debug:: ?
Benoit L
2015/04/02 16:43:48
Done.
|
+ return false; |
+ |
+ std::vector<base::debug::MappedMemoryRegion> regions_to_prefetch; |
+ for (const auto& region : regions) { |
+ if (IsGoodToPrefetch(region)) { |
+ regions_to_prefetch.push_back(region); |
+ } |
+ } |
+ |
+ TakeOnlyLibchromeRangesIfPossible(regions_to_prefetch, ranges); |
+ return true; |
+} |
+ |
+// 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. |
+// static |
+bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() { |
+ // Looking for ranges is done before the fork, to avoid syscalls and/or memory |
+ // allocations in the forked process. The child process inherits the lock |
+ // state of its parent thread. It cannot rely on being able to acquire any |
+ // lock (unless special care is taken in a pre-fork handler), including being |
+ // able to call malloc(). |
+ std::vector<std::pair<uintptr_t, uintptr_t>> ranges; |
+ if (!FindRanges(&ranges)) |
+ return false; |
+ pid_t pid = fork(); |
+ if (pid == 0) { |
+ setpriority(PRIO_PROCESS, 0, kBackgroundPriority); |
+ // _exit() doesn't call the atexit() handlers. |
+ _exit(Prefetch(ranges) ? 0 : 1); |
+ } 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; |
+ } |
+ } |
+ return false; |
+ } |
+} |
+ |
+} // namespace android |
+} // namespace base |