Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
|
|
@ -0,0 +1,10 @@
|
|||
# //base/android/library_loader
|
||||
|
||||
Native code is split between this directory and:
|
||||
* [//third_party/android_crazy_linker](../../../third_party/android_crazy_linker/README.chromium)
|
||||
|
||||
Java code lives at:
|
||||
* [//base/android/java/src/org/chromium/base/library_loader/](../java/src/org/chromium/base/library_loader/)
|
||||
|
||||
A high-level guide to native code on Android exists at:
|
||||
* [//docs/android_native_libraries.md](../../../docs/android_native_libraries.md)
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright 2017 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/anchor_functions.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
|
||||
|
||||
// These functions are here to delimit the start and end of the ordered part of
|
||||
// .text. They require a suitably constructed orderfile, with these functions at
|
||||
// the beginning and end.
|
||||
//
|
||||
// These functions are weird: this is due to ICF (Identical Code Folding).
|
||||
// The linker merges functions that have the same code, which would be the case
|
||||
// if these functions were empty, or simple.
|
||||
// Gold's flag --icf=safe will *not* alias functions when their address is used
|
||||
// in code, but as of November 2017, we use the default setting that
|
||||
// deduplicates function in this case as well.
|
||||
//
|
||||
// Thus these functions are made to be unique, using inline .word in assembly,
|
||||
// or the equivalent directive depending on the architecture.
|
||||
//
|
||||
// Note that code |CheckOrderingSanity()| below will make sure that these
|
||||
// functions are not aliased, in case the toolchain becomes really clever.
|
||||
extern "C" {
|
||||
|
||||
// The assembler has a different syntax depending on the architecture.
|
||||
#if defined(ARCH_CPU_ARMEL) || defined(ARCH_CPU_ARM64)
|
||||
|
||||
// These functions have a well-defined ordering in this file, see the comment
|
||||
// in |IsOrderingSane()|.
|
||||
void dummy_function_end_of_ordered_text() {
|
||||
asm(".word 0x21bad44d");
|
||||
asm(".word 0xb815c5b0");
|
||||
}
|
||||
|
||||
void dummy_function_start_of_ordered_text() {
|
||||
asm(".word 0xe4a07375");
|
||||
asm(".word 0x66dda6dc");
|
||||
}
|
||||
|
||||
#elif defined(ARCH_CPU_X86_FAMILY)
|
||||
|
||||
void dummy_function_end_of_ordered_text() {
|
||||
asm(".4byte 0x21bad44d");
|
||||
asm(".4byte 0xb815c5b0");
|
||||
}
|
||||
|
||||
void dummy_function_start_of_ordered_text() {
|
||||
asm(".4byte 0xe4a07375");
|
||||
asm(".4byte 0x66dda6dc");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// These two symbols are defined by anchor_functions.lds and delimit the start
|
||||
// and end of .text.
|
||||
void linker_script_start_of_text();
|
||||
void linker_script_end_of_text();
|
||||
|
||||
} // extern "C"
|
||||
|
||||
namespace base {
|
||||
namespace android {
|
||||
|
||||
const size_t kStartOfText =
|
||||
reinterpret_cast<size_t>(linker_script_start_of_text);
|
||||
const size_t kEndOfText = reinterpret_cast<size_t>(linker_script_end_of_text);
|
||||
const size_t kStartOfOrderedText =
|
||||
reinterpret_cast<size_t>(dummy_function_start_of_ordered_text);
|
||||
const size_t kEndOfOrderedText =
|
||||
reinterpret_cast<size_t>(dummy_function_end_of_ordered_text);
|
||||
|
||||
bool AreAnchorsSane() {
|
||||
size_t here = reinterpret_cast<size_t>(&IsOrderingSane);
|
||||
return kStartOfText < here && here < kEndOfText;
|
||||
}
|
||||
|
||||
bool IsOrderingSane() {
|
||||
// The symbols linker_script_start_of_text and linker_script_end_of_text
|
||||
// should cover all of .text, and dummy_function_start_of_ordered_text and
|
||||
// dummy_function_end_of_ordered_text should cover the ordered part of it.
|
||||
// This check is intended to catch the lack of ordering.
|
||||
//
|
||||
// Ordered text can start at the start of text, but should not cover the
|
||||
// entire range. Most addresses are distinct nonetheless as the symbols are
|
||||
// different, but linker-defined symbols have zero size and therefore the
|
||||
// start address could be the same as the address of
|
||||
// dummy_function_start_of_ordered_text.
|
||||
return AreAnchorsSane() && kStartOfOrderedText < kEndOfOrderedText &&
|
||||
kStartOfText <= kStartOfOrderedText && kEndOfOrderedText < kEndOfText;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace base
|
||||
|
||||
#endif // BUILDFLAG(SUPPORTS_CODE_ORDERING)
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright 2017 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.
|
||||
|
||||
#ifndef BASE_ANDROID_LIBRARY_LOADER_ANCHOR_FUNCTIONS_H_
|
||||
#define BASE_ANDROID_LIBRARY_LOADER_ANCHOR_FUNCTIONS_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include "base/android/library_loader/anchor_functions_buildflags.h"
|
||||
|
||||
#include "base/base_export.h"
|
||||
|
||||
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
|
||||
|
||||
namespace base {
|
||||
namespace android {
|
||||
|
||||
// Start and end of .text, respectively.
|
||||
BASE_EXPORT extern const size_t kStartOfText;
|
||||
BASE_EXPORT extern const size_t kEndOfText;
|
||||
// Start and end of the ordered part of .text, respectively.
|
||||
BASE_EXPORT extern const size_t kStartOfOrderedText;
|
||||
BASE_EXPORT extern const size_t kEndOfOrderedText;
|
||||
|
||||
// Returns true if anchors are sane.
|
||||
BASE_EXPORT bool AreAnchorsSane();
|
||||
|
||||
// Returns true if the ordering looks sane.
|
||||
BASE_EXPORT bool IsOrderingSane();
|
||||
|
||||
} // namespace android
|
||||
} // namespace base
|
||||
#endif // BUILDFLAG(SUPPORTS_CODE_ORDERING)
|
||||
|
||||
#endif // BASE_ANDROID_LIBRARY_LOADER_ANCHOR_FUNCTIONS_H_
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
# Copyright 2018 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.
|
||||
|
||||
# Define symbols that point to the start and end of the .text section.
|
||||
PROVIDE_HIDDEN(linker_script_start_of_text = ADDR(.text));
|
||||
PROVIDE_HIDDEN(linker_script_end_of_text = ADDR(.text) + SIZEOF(.text));
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
// Copyright 2014 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_loader_hooks.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/android/jni_string.h"
|
||||
#include "base/android/library_loader/anchor_functions_buildflags.h"
|
||||
#include "base/android/library_loader/library_prefetcher.h"
|
||||
#include "base/android/orderfile/orderfile_buildflags.h"
|
||||
#include "base/android/sys_utils.h"
|
||||
#include "base/at_exit.h"
|
||||
#include "base/base_jni_headers/LibraryLoader_jni.h"
|
||||
#include "base/base_switches.h"
|
||||
#include "base/metrics/histogram.h"
|
||||
#include "base/metrics/histogram_functions.h"
|
||||
#include "base/metrics/histogram_macros.h"
|
||||
|
||||
#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
|
||||
#include "base/android/orderfile/orderfile_instrumentation.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace android {
|
||||
|
||||
namespace {
|
||||
|
||||
base::AtExitManager* g_at_exit_manager = nullptr;
|
||||
const char* g_library_version_number = "";
|
||||
LibraryLoadedHook* g_registration_callback = nullptr;
|
||||
NativeInitializationHook* g_native_initialization_hook = nullptr;
|
||||
NonMainDexJniRegistrationHook* g_jni_registration_hook = nullptr;
|
||||
|
||||
// The amount of time, in milliseconds, that it took to load the shared
|
||||
// libraries in the renderer. Set in
|
||||
// JNI_LibraryLoader_RecordRendererLibraryLoadTime().
|
||||
long g_renderer_library_load_time_ms = 0;
|
||||
|
||||
} // namespace
|
||||
|
||||
bool IsUsingOrderfileOptimization() {
|
||||
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
|
||||
return SysUtils::IsLowEndDeviceFromJni();
|
||||
#else // !SUPPORTS_CODE_ORDERING
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void JNI_LibraryLoader_RecordRendererLibraryLoadTime(
|
||||
JNIEnv* env,
|
||||
jlong library_load_time_ms) {
|
||||
g_renderer_library_load_time_ms = library_load_time_ms;
|
||||
}
|
||||
|
||||
void SetNativeInitializationHook(
|
||||
NativeInitializationHook native_initialization_hook) {
|
||||
g_native_initialization_hook = native_initialization_hook;
|
||||
}
|
||||
|
||||
void SetNonMainDexJniRegistrationHook(
|
||||
NonMainDexJniRegistrationHook jni_registration_hook) {
|
||||
DCHECK(!g_jni_registration_hook);
|
||||
g_jni_registration_hook = jni_registration_hook;
|
||||
}
|
||||
|
||||
void RecordLibraryLoaderRendererHistograms() {
|
||||
// Record how long it took to load the shared libraries.
|
||||
UMA_HISTOGRAM_TIMES(
|
||||
"ChromiumAndroidLinker.RendererLoadTime",
|
||||
base::TimeDelta::FromMilliseconds(g_renderer_library_load_time_ms));
|
||||
}
|
||||
|
||||
void SetLibraryLoadedHook(LibraryLoadedHook* func) {
|
||||
g_registration_callback = func;
|
||||
}
|
||||
|
||||
static jboolean JNI_LibraryLoader_LibraryLoaded(
|
||||
JNIEnv* env,
|
||||
jint library_process_type) {
|
||||
#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
|
||||
orderfile::StartDelayedDump();
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
|
||||
if (CommandLine::ForCurrentProcess()->HasSwitch(
|
||||
"log-native-library-residency")) {
|
||||
NativeLibraryPrefetcher::MadviseForResidencyCollection();
|
||||
} else if (IsUsingOrderfileOptimization()) {
|
||||
NativeLibraryPrefetcher::MadviseForOrderfile();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (g_native_initialization_hook &&
|
||||
!g_native_initialization_hook(
|
||||
static_cast<LibraryProcessType>(library_process_type)))
|
||||
return false;
|
||||
if (g_registration_callback &&
|
||||
!g_registration_callback(
|
||||
env, nullptr,
|
||||
static_cast<LibraryProcessType>(library_process_type))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void JNI_LibraryLoader_RegisterNonMainDexJni(JNIEnv* env) {
|
||||
if (g_jni_registration_hook) {
|
||||
g_jni_registration_hook();
|
||||
}
|
||||
}
|
||||
|
||||
void LibraryLoaderExitHook() {
|
||||
if (g_at_exit_manager) {
|
||||
delete g_at_exit_manager;
|
||||
g_at_exit_manager = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SetVersionNumber(const char* version_number) {
|
||||
g_library_version_number = strdup(version_number);
|
||||
}
|
||||
|
||||
ScopedJavaLocalRef<jstring> JNI_LibraryLoader_GetVersionNumber(JNIEnv* env) {
|
||||
return ConvertUTF8ToJavaString(env, g_library_version_number);
|
||||
}
|
||||
|
||||
void InitAtExitManager() {
|
||||
g_at_exit_manager = new base::AtExitManager();
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright 2014 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.
|
||||
|
||||
#ifndef BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_
|
||||
#define BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/metrics/field_trial.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace android {
|
||||
|
||||
// The process the shared library is loaded in.
|
||||
// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.base.library_loader
|
||||
enum LibraryProcessType {
|
||||
// The LibraryLoad has not been initialized.
|
||||
PROCESS_UNINITIALIZED = 0,
|
||||
// Shared library is running in browser process.
|
||||
PROCESS_BROWSER = 1,
|
||||
// Shared library is running in child process.
|
||||
PROCESS_CHILD = 2,
|
||||
// Shared library is running in the app that uses webview.
|
||||
PROCESS_WEBVIEW = 3,
|
||||
// Shared library is running in child process as part of webview.
|
||||
PROCESS_WEBVIEW_CHILD = 4,
|
||||
// Shared library is running in the app that uses weblayer.
|
||||
PROCESS_WEBLAYER = 5,
|
||||
// Shared library is running in child process as part of weblayer.
|
||||
PROCESS_WEBLAYER_CHILD = 6,
|
||||
};
|
||||
|
||||
// Whether fewer code should be prefetched, and no-readahead should be set.
|
||||
// Returns true on low-end devices, where this speeds up startup, and false
|
||||
// elsewhere, where it slows it down. See
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=758566#c71 for details.
|
||||
BASE_EXPORT bool IsUsingOrderfileOptimization();
|
||||
|
||||
typedef bool NativeInitializationHook(LibraryProcessType library_process_type);
|
||||
|
||||
BASE_EXPORT void SetNativeInitializationHook(
|
||||
NativeInitializationHook native_initialization_hook);
|
||||
|
||||
typedef void NonMainDexJniRegistrationHook();
|
||||
|
||||
BASE_EXPORT void SetNonMainDexJniRegistrationHook(
|
||||
NonMainDexJniRegistrationHook jni_registration_hook);
|
||||
|
||||
// Record any pending renderer histogram value as histograms. Pending values
|
||||
// are set by
|
||||
// JNI_LibraryLoader_RegisterChromiumAndroidLinkerRendererHistogram().
|
||||
BASE_EXPORT void RecordLibraryLoaderRendererHistograms();
|
||||
|
||||
// Typedef for hook function to be called (indirectly from Java) once the
|
||||
// libraries are loaded. The hook function should register the JNI bindings
|
||||
// required to start the application. It should return true for success and
|
||||
// false for failure.
|
||||
// Note: this can't use base::Callback because there is no way of initializing
|
||||
// the default callback without using static objects, which we forbid.
|
||||
typedef bool LibraryLoadedHook(JNIEnv* env,
|
||||
jclass clazz,
|
||||
LibraryProcessType library_process_type);
|
||||
|
||||
// Set the hook function to be called (from Java) once the libraries are loaded.
|
||||
// SetLibraryLoadedHook may only be called from JNI_OnLoad. The hook function
|
||||
// should register the JNI bindings required to start the application.
|
||||
|
||||
BASE_EXPORT void SetLibraryLoadedHook(LibraryLoadedHook* func);
|
||||
|
||||
// Pass the version name to the loader. This used to check that the library
|
||||
// version matches the version expected by Java before completing JNI
|
||||
// registration.
|
||||
// Note: argument must remain valid at least until library loading is complete.
|
||||
BASE_EXPORT void SetVersionNumber(const char* version_number);
|
||||
|
||||
// Call on exit to delete the AtExitManager which OnLibraryLoadedOnUIThread
|
||||
// created.
|
||||
BASE_EXPORT void LibraryLoaderExitHook();
|
||||
|
||||
// Initialize AtExitManager, this must be done at the begining of loading
|
||||
// shared library.
|
||||
void InitAtExitManager();
|
||||
|
||||
} // namespace android
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_ANDROID_LIBRARY_LOADER_LIBRARY_LOADER_HOOKS_H_
|
||||
|
|
@ -0,0 +1,347 @@
|
|||
// 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 <stddef.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/android/library_loader/anchor_functions.h"
|
||||
#include "base/android/orderfile/orderfile_buildflags.h"
|
||||
#include "base/bits.h"
|
||||
#include "base/files/file.h"
|
||||
#include "base/format_macros.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "base/process/process_metrics.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
|
||||
#include "base/android/orderfile/orderfile_instrumentation.h"
|
||||
#endif
|
||||
|
||||
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
|
||||
|
||||
namespace base {
|
||||
namespace android {
|
||||
|
||||
namespace {
|
||||
|
||||
// Valid for all Android architectures.
|
||||
constexpr size_t kPageSize = 4096;
|
||||
|
||||
// Populates the per-page residency between |start| and |end| in |residency|. If
|
||||
// successful, |residency| has the size of |end| - |start| in pages.
|
||||
// Returns true for success.
|
||||
bool Mincore(size_t start, size_t end, std::vector<unsigned char>* residency) {
|
||||
if (start % kPageSize || end % kPageSize)
|
||||
return false;
|
||||
size_t size = end - start;
|
||||
size_t size_in_pages = size / kPageSize;
|
||||
if (residency->size() != size_in_pages)
|
||||
residency->resize(size_in_pages);
|
||||
int err = HANDLE_EINTR(
|
||||
mincore(reinterpret_cast<void*>(start), size, &(*residency)[0]));
|
||||
PLOG_IF(ERROR, err) << "mincore() failed";
|
||||
return !err;
|
||||
}
|
||||
|
||||
// Returns the start and end of .text, aligned to the lower and upper page
|
||||
// boundaries, respectively.
|
||||
std::pair<size_t, size_t> GetTextRange() {
|
||||
// |kStartOfText| may not be at the beginning of a page, since .plt can be
|
||||
// before it, yet in the same mapping for instance.
|
||||
size_t start_page = kStartOfText - kStartOfText % kPageSize;
|
||||
// Set the end to the page on which the beginning of the last symbol is. The
|
||||
// actual symbol may spill into the next page by a few bytes, but this is
|
||||
// outside of the executable code range anyway.
|
||||
size_t end_page = base::bits::Align(kEndOfText, kPageSize);
|
||||
return {start_page, end_page};
|
||||
}
|
||||
|
||||
// Returns the start and end pages of the unordered section of .text, aligned to
|
||||
// lower and upper page boundaries, respectively.
|
||||
std::pair<size_t, size_t> GetOrderedTextRange() {
|
||||
size_t start_page = kStartOfOrderedText - kStartOfOrderedText % kPageSize;
|
||||
// kEndOfUnorderedText is not considered ordered, but the byte immediately
|
||||
// before is considered ordered and so can not be contained in the start page.
|
||||
size_t end_page = base::bits::Align(kEndOfOrderedText, kPageSize);
|
||||
return {start_page, end_page};
|
||||
}
|
||||
|
||||
// Calls madvise(advice) on the specified range. Does nothing if the range is
|
||||
// empty.
|
||||
void MadviseOnRange(const std::pair<size_t, size_t>& range, int advice) {
|
||||
if (range.first >= range.second) {
|
||||
return;
|
||||
}
|
||||
size_t size = range.second - range.first;
|
||||
int err = madvise(reinterpret_cast<void*>(range.first), size, advice);
|
||||
if (err) {
|
||||
PLOG(ERROR) << "madvise() failed";
|
||||
}
|
||||
}
|
||||
|
||||
// Timestamp in ns since Unix Epoch, and residency, as returned by mincore().
|
||||
struct TimestampAndResidency {
|
||||
uint64_t timestamp_nanos;
|
||||
std::vector<unsigned char> residency;
|
||||
|
||||
TimestampAndResidency(uint64_t timestamp_nanos,
|
||||
std::vector<unsigned char>&& residency)
|
||||
: timestamp_nanos(timestamp_nanos), residency(residency) {}
|
||||
};
|
||||
|
||||
// Returns true for success.
|
||||
bool CollectResidency(size_t start,
|
||||
size_t end,
|
||||
std::vector<TimestampAndResidency>* data) {
|
||||
// Not using base::TimeTicks() to not call too many base:: symbol that would
|
||||
// pollute the reached symbols dumps.
|
||||
struct timespec ts;
|
||||
if (HANDLE_EINTR(clock_gettime(CLOCK_MONOTONIC, &ts))) {
|
||||
PLOG(ERROR) << "Cannot get the time.";
|
||||
return false;
|
||||
}
|
||||
uint64_t now =
|
||||
static_cast<uint64_t>(ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec;
|
||||
std::vector<unsigned char> residency;
|
||||
if (!Mincore(start, end, &residency))
|
||||
return false;
|
||||
|
||||
data->emplace_back(now, std::move(residency));
|
||||
return true;
|
||||
}
|
||||
|
||||
void DumpResidency(size_t start,
|
||||
size_t end,
|
||||
std::unique_ptr<std::vector<TimestampAndResidency>> data) {
|
||||
LOG(WARNING) << "Dumping native library residency";
|
||||
auto path = base::FilePath(
|
||||
base::StringPrintf("/data/local/tmp/chrome/residency-%d.txt", getpid()));
|
||||
auto file =
|
||||
base::File(path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
|
||||
if (!file.IsValid()) {
|
||||
PLOG(ERROR) << "Cannot open file to dump the residency data "
|
||||
<< path.value();
|
||||
return;
|
||||
}
|
||||
|
||||
// First line: start-end of text range.
|
||||
CHECK(AreAnchorsSane());
|
||||
CHECK_LE(start, kStartOfText);
|
||||
CHECK_LE(kEndOfText, end);
|
||||
auto start_end = base::StringPrintf("%" PRIuS " %" PRIuS "\n",
|
||||
kStartOfText - start, kEndOfText - start);
|
||||
file.WriteAtCurrentPos(start_end.c_str(), start_end.size());
|
||||
|
||||
for (const auto& data_point : *data) {
|
||||
auto timestamp =
|
||||
base::StringPrintf("%" PRIu64 " ", data_point.timestamp_nanos);
|
||||
file.WriteAtCurrentPos(timestamp.c_str(), timestamp.size());
|
||||
|
||||
std::vector<char> dump;
|
||||
dump.reserve(data_point.residency.size() + 1);
|
||||
for (auto c : data_point.residency)
|
||||
dump.push_back(c ? '1' : '0');
|
||||
dump[dump.size() - 1] = '\n';
|
||||
file.WriteAtCurrentPos(&dump[0], dump.size());
|
||||
}
|
||||
}
|
||||
|
||||
#if !BUILDFLAG(ORDERFILE_INSTRUMENTATION)
|
||||
// Reads a byte per page between |start| and |end| to force it into the page
|
||||
// cache.
|
||||
// Heap allocations, syscalls and library functions are not allowed in this
|
||||
// function.
|
||||
// Returns true for success.
|
||||
#if defined(ADDRESS_SANITIZER)
|
||||
// Disable AddressSanitizer instrumentation for this function. It is touching
|
||||
// memory that hasn't been allocated by the app, though the addresses are
|
||||
// valid. Furthermore, this takes place in a child process. See crbug.com/653372
|
||||
// for the context.
|
||||
__attribute__((no_sanitize_address))
|
||||
#endif
|
||||
void Prefetch(size_t start, size_t end) {
|
||||
unsigned char* start_ptr = reinterpret_cast<unsigned char*>(start);
|
||||
unsigned char* end_ptr = reinterpret_cast<unsigned char*>(end);
|
||||
unsigned char dummy = 0;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// These values were used in the past for recording
|
||||
// "LibraryLoader.PrefetchDetailedStatus".
|
||||
enum class PrefetchStatus {
|
||||
kSuccess = 0,
|
||||
kWrongOrdering = 1,
|
||||
kForkFailed = 2,
|
||||
kChildProcessCrashed = 3,
|
||||
kChildProcessKilled = 4,
|
||||
kMaxValue = kChildProcessKilled
|
||||
};
|
||||
|
||||
PrefetchStatus ForkAndPrefetch(bool ordered_only) {
|
||||
if (!IsOrderingSane()) {
|
||||
LOG(WARNING) << "Incorrect code ordering";
|
||||
return PrefetchStatus::kWrongOrdering;
|
||||
}
|
||||
|
||||
// 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().
|
||||
//
|
||||
// Always prefetch the ordered section first, as it's reached early during
|
||||
// startup, and not necessarily located at the beginning of .text.
|
||||
std::vector<std::pair<size_t, size_t>> ranges = {GetOrderedTextRange()};
|
||||
if (!ordered_only)
|
||||
ranges.push_back(GetTextRange());
|
||||
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
// Android defines the background priority to this value since at least 2009
|
||||
// (see Process.java).
|
||||
constexpr int kBackgroundPriority = 10;
|
||||
setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
|
||||
// _exit() doesn't call the atexit() handlers.
|
||||
for (const auto& range : ranges) {
|
||||
Prefetch(range.first, range.second);
|
||||
}
|
||||
_exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
if (pid < 0) {
|
||||
return PrefetchStatus::kForkFailed;
|
||||
}
|
||||
int status;
|
||||
const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0));
|
||||
if (result == pid) {
|
||||
if (WIFEXITED(status))
|
||||
return PrefetchStatus::kSuccess;
|
||||
if (WIFSIGNALED(status)) {
|
||||
int signal = WTERMSIG(status);
|
||||
switch (signal) {
|
||||
case SIGSEGV:
|
||||
case SIGBUS:
|
||||
return PrefetchStatus::kChildProcessCrashed;
|
||||
break;
|
||||
case SIGKILL:
|
||||
case SIGTERM:
|
||||
default:
|
||||
return PrefetchStatus::kChildProcessKilled;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Should not happen. Per man waitpid(2), errors are:
|
||||
// - EINTR: handled.
|
||||
// - ECHILD if the process doesn't have an unwaited-for child with this PID.
|
||||
// - EINVAL.
|
||||
return PrefetchStatus::kChildProcessKilled;
|
||||
}
|
||||
}
|
||||
#endif // !BUILDFLAG(ORDERFILE_INSTRUMENTATION)
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary(bool ordered_only) {
|
||||
#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
|
||||
// Avoid forking with orderfile instrumentation because the child process
|
||||
// would create a dump as well.
|
||||
return;
|
||||
#else
|
||||
PrefetchStatus status = ForkAndPrefetch(ordered_only);
|
||||
if (status != PrefetchStatus::kSuccess) {
|
||||
LOG(WARNING) << "Cannot prefetch the library. status = "
|
||||
<< static_cast<int>(status);
|
||||
}
|
||||
#endif // BUILDFLAG(ORDERFILE_INSTRUMENTATION)
|
||||
}
|
||||
|
||||
// static
|
||||
int NativeLibraryPrefetcher::PercentageOfResidentCode(size_t start,
|
||||
size_t end) {
|
||||
size_t total_pages = 0;
|
||||
size_t resident_pages = 0;
|
||||
|
||||
std::vector<unsigned char> residency;
|
||||
bool ok = Mincore(start, end, &residency);
|
||||
if (!ok)
|
||||
return -1;
|
||||
total_pages += residency.size();
|
||||
resident_pages += std::count_if(residency.begin(), residency.end(),
|
||||
[](unsigned char x) { return x & 1; });
|
||||
if (total_pages == 0)
|
||||
return -1;
|
||||
return static_cast<int>((100 * resident_pages) / total_pages);
|
||||
}
|
||||
|
||||
// static
|
||||
int NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode() {
|
||||
if (!AreAnchorsSane()) {
|
||||
LOG(WARNING) << "Incorrect code ordering";
|
||||
return -1;
|
||||
}
|
||||
const auto& range = GetTextRange();
|
||||
return PercentageOfResidentCode(range.first, range.second);
|
||||
}
|
||||
|
||||
// static
|
||||
void NativeLibraryPrefetcher::PeriodicallyCollectResidency() {
|
||||
CHECK_EQ(static_cast<long>(kPageSize), sysconf(_SC_PAGESIZE));
|
||||
|
||||
LOG(WARNING) << "Spawning thread to periodically collect residency";
|
||||
const auto& range = GetTextRange();
|
||||
auto data = std::make_unique<std::vector<TimestampAndResidency>>();
|
||||
// Collect residency for about minute (the actual time spent collecting
|
||||
// residency can vary, so this is only approximate).
|
||||
for (int i = 0; i < 120; ++i) {
|
||||
if (!CollectResidency(range.first, range.second, data.get()))
|
||||
return;
|
||||
usleep(5e5);
|
||||
}
|
||||
DumpResidency(range.first, range.second, std::move(data));
|
||||
}
|
||||
|
||||
// static
|
||||
void NativeLibraryPrefetcher::MadviseForOrderfile() {
|
||||
if (!IsOrderingSane()) {
|
||||
LOG(WARNING) << "Code not ordered, madvise optimization skipped";
|
||||
return;
|
||||
}
|
||||
// First MADV_RANDOM on all of text, then turn the ordered text range back to
|
||||
// normal. The ordered range may be placed anywhere within .text.
|
||||
MadviseOnRange(GetTextRange(), MADV_RANDOM);
|
||||
MadviseOnRange(GetOrderedTextRange(), MADV_NORMAL);
|
||||
}
|
||||
|
||||
// static
|
||||
void NativeLibraryPrefetcher::MadviseForResidencyCollection() {
|
||||
if (!AreAnchorsSane()) {
|
||||
LOG(WARNING) << "Code not ordered, cannot madvise";
|
||||
return;
|
||||
}
|
||||
LOG(WARNING) << "Performing madvise for residency collection";
|
||||
MadviseOnRange(GetTextRange(), MADV_RANDOM);
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace base
|
||||
#endif // BUILDFLAG(SUPPORTS_CODE_ORDERING)
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// 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.
|
||||
|
||||
#ifndef BASE_ANDROID_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_
|
||||
#define BASE_ANDROID_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
#include "base/android/library_loader/anchor_functions_buildflags.h"
|
||||
#include "base/base_export.h"
|
||||
#include "base/gtest_prod_util.h"
|
||||
#include "base/macros.h"
|
||||
|
||||
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
|
||||
|
||||
namespace base {
|
||||
namespace android {
|
||||
|
||||
// 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 getting the address range, only
|
||||
// crashing the forked process in case of mistake.
|
||||
// - 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.
|
||||
class BASE_EXPORT NativeLibraryPrefetcher {
|
||||
public:
|
||||
// Finds the executable code range, forks a low priority process pre-fetching
|
||||
// it wait()s for the process to exit or die. If ordered_only is true, only
|
||||
// the ordered section is prefetched. See GetOrdrderedTextRange() in
|
||||
// library_prefetcher.cc.
|
||||
static void ForkAndPrefetchNativeLibrary(bool ordered_only);
|
||||
|
||||
// Returns the percentage of the native library code currently resident in
|
||||
// memory, or -1 in case of error.
|
||||
static int PercentageOfResidentNativeLibraryCode();
|
||||
|
||||
// Collects residency for the native library executable multiple times, then
|
||||
// dumps it to disk.
|
||||
static void PeriodicallyCollectResidency();
|
||||
|
||||
// Calls madvise() on the native library executable, using orderfile
|
||||
// information to decide how to advise each part of the library.
|
||||
static void MadviseForOrderfile();
|
||||
|
||||
// Calls madvise() on the native library executable so that residency
|
||||
// collection is accurate.
|
||||
static void MadviseForResidencyCollection();
|
||||
|
||||
private:
|
||||
// Returns the percentage of [start, end] currently resident in
|
||||
// memory, or -1 in case of error.
|
||||
static int PercentageOfResidentCode(size_t start, size_t end);
|
||||
|
||||
FRIEND_TEST_ALL_PREFIXES(NativeLibraryPrefetcherTest,
|
||||
TestPercentageOfResidentCode);
|
||||
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(NativeLibraryPrefetcher);
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
} // namespace base
|
||||
|
||||
#endif // BUILDFLAG(SUPPORTS_CODE_ORDERING)
|
||||
|
||||
#endif // BASE_ANDROID_LIBRARY_LOADER_LIBRARY_PREFETCHER_H_
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2019 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 <string>
|
||||
|
||||
#include "base/android/jni_android.h"
|
||||
#include "base/android/jni_string.h"
|
||||
#include "base/android/library_loader/anchor_functions_buildflags.h"
|
||||
#include "base/android/library_loader/library_loader_hooks.h"
|
||||
#include "base/android/library_loader/library_prefetcher.h"
|
||||
#include "base/android/scoped_java_ref.h"
|
||||
#include "base/base_jni_headers/LibraryPrefetcher_jni.h"
|
||||
#include "base/logging.h"
|
||||
|
||||
namespace base {
|
||||
namespace android {
|
||||
|
||||
static void JNI_LibraryPrefetcher_ForkAndPrefetchNativeLibrary(JNIEnv* env) {
|
||||
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
|
||||
return NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary(
|
||||
IsUsingOrderfileOptimization());
|
||||
#endif
|
||||
}
|
||||
|
||||
static jint JNI_LibraryPrefetcher_PercentageOfResidentNativeLibraryCode(
|
||||
JNIEnv* env) {
|
||||
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
|
||||
return NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode();
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void JNI_LibraryPrefetcher_PeriodicallyCollectResidency(JNIEnv* env) {
|
||||
#if BUILDFLAG(SUPPORTS_CODE_ORDERING)
|
||||
NativeLibraryPrefetcher::PeriodicallyCollectResidency();
|
||||
#else
|
||||
LOG(WARNING) << "Collecting residency is not supported.";
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace base
|
||||
Loading…
Add table
Add a link
Reference in a new issue