Chromium Code Reviews| 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..c595437bded13ee41c09b6b7ff6741f2872afe0d |
| --- /dev/null |
| +++ b/base/android/library_loader/library_prefetcher.cc |
| @@ -0,0 +1,157 @@ |
| +// 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/macros.h" |
| +#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 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 (size_t i = 0; i < arraysize(kSuffixesToMatch); i++) { |
| + 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) { |
| + 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<AddressRange>* 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<AddressRange>* ranges) { |
| + std::string proc_maps; |
| + if (!base::debug::ReadProcMaps(&proc_maps)) |
| + return false; |
| + std::vector<base::debug::MappedMemoryRegion> regions; |
| + if (!base::debug::ParseProcMaps(proc_maps, ®ions)) |
| + 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 |
|
Robert Sesek
2015/04/02 19:46:48
I think this would be better as the class-level co
Benoit L
2015/04/03 09:47:16
Done.
|
| +// 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<AddressRange> 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 |