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