Home | History | Annotate | Download | only in library_loader
      1 // Copyright 2015 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/android/library_loader/library_prefetcher.h"
      6 
      7 #include <stddef.h>
      8 #include <sys/mman.h>
      9 #include <sys/resource.h>
     10 #include <sys/wait.h>
     11 #include <unistd.h>
     12 #include <algorithm>
     13 #include <utility>
     14 #include <vector>
     15 
     16 #include "base/macros.h"
     17 #include "base/posix/eintr_wrapper.h"
     18 #include "base/strings/string_util.h"
     19 #include "build/build_config.h"
     20 
     21 namespace base {
     22 namespace android {
     23 
     24 namespace {
     25 
     26 // Android defines the background priority to this value since at least 2009
     27 // (see Process.java).
     28 const int kBackgroundPriority = 10;
     29 // Valid for all the Android architectures.
     30 const size_t kPageSize = 4096;
     31 const char* kLibchromeSuffix = "libchrome.so";
     32 // "base.apk" is a suffix because the library may be loaded directly from the
     33 // APK.
     34 const char* kSuffixesToMatch[] = {kLibchromeSuffix, "base.apk"};
     35 
     36 bool IsReadableAndPrivate(const base::debug::MappedMemoryRegion& region) {
     37   return region.permissions & base::debug::MappedMemoryRegion::READ &&
     38          region.permissions & base::debug::MappedMemoryRegion::PRIVATE;
     39 }
     40 
     41 bool PathMatchesSuffix(const std::string& path) {
     42   for (size_t i = 0; i < arraysize(kSuffixesToMatch); i++) {
     43     if (EndsWith(path, kSuffixesToMatch[i], CompareCase::SENSITIVE)) {
     44       return true;
     45     }
     46   }
     47   return false;
     48 }
     49 
     50 // For each range, reads a byte per page to force it into the page cache.
     51 // Heap allocations, syscalls and library functions are not allowed in this
     52 // function.
     53 // Returns true for success.
     54 #if defined(ADDRESS_SANITIZER)
     55 // Disable AddressSanitizer instrumentation for this function. It is touching
     56 // memory that hasn't been allocated by the app, though the addresses are
     57 // valid. Furthermore, this takes place in a child process. See crbug.com/653372
     58 // for the context.
     59 __attribute__((no_sanitize_address))
     60 #endif
     61 bool Prefetch(const std::vector<std::pair<uintptr_t, uintptr_t>>& ranges) {
     62   for (const auto& range : ranges) {
     63     const uintptr_t page_mask = kPageSize - 1;
     64     // If start or end is not page-aligned, parsing went wrong. It is better to
     65     // exit with an error.
     66     if ((range.first & page_mask) || (range.second & page_mask)) {
     67       return false;  // CHECK() is not allowed here.
     68     }
     69     unsigned char* start_ptr = reinterpret_cast<unsigned char*>(range.first);
     70     unsigned char* end_ptr = reinterpret_cast<unsigned char*>(range.second);
     71     unsigned char dummy = 0;
     72     for (unsigned char* ptr = start_ptr; ptr < end_ptr; ptr += kPageSize) {
     73       // Volatile is required to prevent the compiler from eliminating this
     74       // loop.
     75       dummy ^= *static_cast<volatile unsigned char*>(ptr);
     76     }
     77   }
     78   return true;
     79 }
     80 
     81 }  // namespace
     82 
     83 // static
     84 bool NativeLibraryPrefetcher::IsGoodToPrefetch(
     85     const base::debug::MappedMemoryRegion& region) {
     86   return PathMatchesSuffix(region.path) &&
     87          IsReadableAndPrivate(region);  // .text and .data mappings are private.
     88 }
     89 
     90 // static
     91 void NativeLibraryPrefetcher::FilterLibchromeRangesOnlyIfPossible(
     92     const std::vector<base::debug::MappedMemoryRegion>& regions,
     93     std::vector<AddressRange>* ranges) {
     94   bool has_libchrome_region = false;
     95   for (const base::debug::MappedMemoryRegion& region : regions) {
     96     if (EndsWith(region.path, kLibchromeSuffix, CompareCase::SENSITIVE)) {
     97       has_libchrome_region = true;
     98       break;
     99     }
    100   }
    101   for (const base::debug::MappedMemoryRegion& region : regions) {
    102     if (has_libchrome_region &&
    103         !EndsWith(region.path, kLibchromeSuffix, CompareCase::SENSITIVE)) {
    104       continue;
    105     }
    106     ranges->push_back(std::make_pair(region.start, region.end));
    107   }
    108 }
    109 
    110 // static
    111 bool NativeLibraryPrefetcher::FindRanges(std::vector<AddressRange>* ranges) {
    112   std::string proc_maps;
    113   if (!base::debug::ReadProcMaps(&proc_maps))
    114     return false;
    115   std::vector<base::debug::MappedMemoryRegion> regions;
    116   if (!base::debug::ParseProcMaps(proc_maps, &regions))
    117     return false;
    118 
    119   std::vector<base::debug::MappedMemoryRegion> regions_to_prefetch;
    120   for (const auto& region : regions) {
    121     if (IsGoodToPrefetch(region)) {
    122       regions_to_prefetch.push_back(region);
    123     }
    124   }
    125 
    126   FilterLibchromeRangesOnlyIfPossible(regions_to_prefetch, ranges);
    127   return true;
    128 }
    129 
    130 // static
    131 bool NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary() {
    132   // Avoid forking with cygprofile instrumentation because the latter performs
    133   // memory allocations.
    134 #if defined(CYGPROFILE_INSTRUMENTATION)
    135   return false;
    136 #endif
    137 
    138   // Looking for ranges is done before the fork, to avoid syscalls and/or memory
    139   // allocations in the forked process. The child process inherits the lock
    140   // state of its parent thread. It cannot rely on being able to acquire any
    141   // lock (unless special care is taken in a pre-fork handler), including being
    142   // able to call malloc().
    143   std::vector<AddressRange> ranges;
    144   if (!FindRanges(&ranges))
    145     return false;
    146 
    147   pid_t pid = fork();
    148   if (pid == 0) {
    149     setpriority(PRIO_PROCESS, 0, kBackgroundPriority);
    150     // _exit() doesn't call the atexit() handlers.
    151     _exit(Prefetch(ranges) ? 0 : 1);
    152   } else {
    153     if (pid < 0) {
    154       return false;
    155     }
    156     int status;
    157     const pid_t result = HANDLE_EINTR(waitpid(pid, &status, 0));
    158     if (result == pid) {
    159       if (WIFEXITED(status)) {
    160         return WEXITSTATUS(status) == 0;
    161       }
    162     }
    163     return false;
    164   }
    165 }
    166 
    167 // static
    168 int NativeLibraryPrefetcher::PercentageOfResidentCode(
    169     const std::vector<AddressRange>& ranges) {
    170   size_t total_pages = 0;
    171   size_t resident_pages = 0;
    172   const uintptr_t page_mask = kPageSize - 1;
    173 
    174   for (const auto& range : ranges) {
    175     if (range.first & page_mask || range.second & page_mask)
    176       return -1;
    177     size_t length = range.second - range.first;
    178     size_t pages = length / kPageSize;
    179     total_pages += pages;
    180     std::vector<unsigned char> is_page_resident(pages);
    181     int err = mincore(reinterpret_cast<void*>(range.first), length,
    182                       &is_page_resident[0]);
    183     DPCHECK(!err);
    184     if (err)
    185       return -1;
    186     resident_pages +=
    187         std::count_if(is_page_resident.begin(), is_page_resident.end(),
    188                       [](unsigned char x) { return x & 1; });
    189   }
    190   if (total_pages == 0)
    191     return -1;
    192   return static_cast<int>((100 * resident_pages) / total_pages);
    193 }
    194 
    195 // static
    196 int NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode() {
    197   std::vector<AddressRange> ranges;
    198   if (!FindRanges(&ranges))
    199     return -1;
    200   return PercentageOfResidentCode(ranges);
    201 }
    202 
    203 }  // namespace android
    204 }  // namespace base
    205