#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "base/android/library_loader/library_prefetcher.h"
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <algorithm>
#include <array>
#include <cerrno>
#include <csignal>
#include <cstddef>
#include <string>
#include "base/android/library_loader/anchor_functions.h"
#include "base/android/orderfile/orderfile_buildflags.h"
#include "base/bits.h"
#include "base/containers/span.h"
#include "base/feature_list.h"
#include "base/features.h"
#include "base/logging.h"
#include "base/memory/page_size.h"
#include "base/metrics/histogram_functions.h"
#include "base/posix/eintr_wrapper.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.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 {
#if !BUILDFLAG(ORDERFILE_INSTRUMENTATION)
#if defined(ARCH_CPU_ARM64)
constexpr size_t kBinaryPageSize = 16384;
#else
constexpr size_t kBinaryPageSize = 4096;
#endif
#if defined(ADDRESS_SANITIZER)
__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);
[[maybe_unused]] unsigned char dummy = 0;
for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kBinaryPageSize) {
dummy ^= *static_cast<volatile unsigned char*>(ptr);
}
}
int MadviseRange(size_t start, size_t end, int advice, size_t target_length) {
if (target_length == 0) {
target_length = end - start;
}
target_length = bits::AlignUp(target_length, base::GetPageSize());
for (size_t current = start; current < end; current += target_length) {
size_t length = std::min(target_length, end - current);
if (madvise(reinterpret_cast<void*>(current), length, advice) != 0) {
return errno;
}
}
return 0;
}
struct Section {
static Section CreateAligned(std::string_view name,
size_t start_anchor,
size_t end_anchor) {
return Section{
.name = name,
.start = start_anchor - start_anchor % kBinaryPageSize,
.end = bits::AlignUp(end_anchor, kBinaryPageSize),
};
}
std::string_view name;
size_t start;
size_t end;
};
enum class PrefetchStatus {
kSuccess = 0,
kWrongOrdering = 1,
kForkFailed = 2,
kChildProcessCrashed = 3,
kChildProcessKilled = 4,
kMadviseFailed = 5,
kMaxValue = kMadviseFailed,
};
PrefetchStatus PrefetchWithMadvise(base::span<const Section> sections,
int madvise_advice,
size_t madvise_length) {
TRACE_EVENT("startup", "LibraryPrefetcher::PrefetchWithMadvise");
for (const auto& section : sections) {
int result = MadviseRange(section.start, section.end, madvise_advice,
madvise_length);
if (result != 0) {
PLOG(WARNING) << "madvise failed for " << section.name << " section";
return PrefetchStatus::kMadviseFailed;
}
}
return PrefetchStatus::kSuccess;
}
PrefetchStatus PrefetchWithFork(base::span<const Section> sections) {
TRACE_EVENT("startup", "LibraryPrefetcher::PrefetchWithFork");
base::TimeTicks fork_start_time = base::TimeTicks::Now();
pid_t pid = fork();
if (pid == 0) {
constexpr int kBackgroundPriority = 10;
setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
for (const auto& section : sections) {
Prefetch(section.start, section.end);
}
_exit(EXIT_SUCCESS);
} else {
base::UmaHistogramCustomMicrosecondsTimes(
"Android.LibraryLoader.Prefetch.ForkDuration",
base::TimeTicks::Now() - fork_start_time, base::Microseconds(1),
base::Seconds(1), 50);
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;
case SIGKILL:
case SIGTERM:
default:
return PrefetchStatus::kChildProcessKilled;
}
}
}
return PrefetchStatus::kChildProcessKilled;
}
}
PrefetchStatus PrefetchWithForkOrMadvise() {
if (!IsOrderingSane()) {
LOG(WARNING) << "Incorrect code ordering";
return PrefetchStatus::kWrongOrdering;
}
const std::array<const Section, 2> sections{
Section::CreateAligned("ordered", kStartOfOrderedText, kEndOfOrderedText),
Section::CreateAligned("text", kStartOfText, kEndOfText),
};
if (base::FeatureList::IsEnabled(features::kLibraryPrefetcherMadvise)) {
constexpr int madvise_advice = MADV_WILLNEED;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnonnull"
bool supported = madvise(nullptr, 0, madvise_advice) == 0;
#pragma clang diagnostic pop
if (!supported) {
PLOG(WARNING) << "madvise not supported for library prefetch";
}
base::UmaHistogramBoolean("Android.LibraryLoader.Prefetch.Madvise",
supported);
if (supported) {
base::ThreadType old_thread_type =
base::PlatformThread::GetCurrentThreadType();
base::PlatformThread::SetCurrentThreadType(base::ThreadType::kBackground);
PrefetchStatus status =
PrefetchWithMadvise(sections, madvise_advice,
features::kLibraryPrefetcherMadviseLength.Get());
base::PlatformThread::SetCurrentThreadType(old_thread_type);
return status;
}
if (!features::kLibraryPrefetcherMadviseFallback.Get()) {
return PrefetchStatus::kMadviseFailed;
}
LOG(WARNING) << "falling back to fork-based library prefetch";
}
return PrefetchWithFork(sections);
}
#endif
}
void NativeLibraryPrefetcher::PrefetchNativeLibrary() {
#if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
return;
#else
base::TimeTicks start_time = base::TimeTicks::Now();
PrefetchStatus status = PrefetchWithForkOrMadvise();
base::UmaHistogramMediumTimes("Android.LibraryLoader.Prefetch.Duration",
base::TimeTicks::Now() - start_time);
base::UmaHistogramEnumeration("Android.LibraryLoader.Prefetch.Status",
status);
if (status != PrefetchStatus::kSuccess) {
LOG(WARNING) << "Cannot prefetch the library. status = "
<< static_cast<int>(status);
}
#endif
}
}
}
#endif