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

Unified 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: Re-use existing /proc/maps parsing code. 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 side-by-side diff with in-line comments
Download patch
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, &regions))
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

Powered by Google App Engine
This is Rietveld 408576698