• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.base.library_loader;
6 
7 import org.jni_zero.JNINamespace;
8 import org.jni_zero.NativeMethods;
9 
10 import org.chromium.base.CommandLine;
11 import org.chromium.base.ContextUtils;
12 import org.chromium.base.SysUtils;
13 import org.chromium.base.TraceEvent;
14 import org.chromium.base.metrics.RecordHistogram;
15 import org.chromium.base.task.PostTask;
16 import org.chromium.base.task.TaskTraits;
17 
18 import java.util.concurrent.atomic.AtomicBoolean;
19 
20 /**
21  * Handles native library prefetch.
22  *
23  * See also base/android/library_loader/library_prefetcher_hooks.cc, which contains
24  * the native counterpart to this class.
25  */
26 @JNINamespace("base::android")
27 public class LibraryPrefetcher {
28 
29     private static final String TAG = "LibraryPrefetcher";
30     // One-way switch that becomes true once {@link asyncPrefetchLibrariesToMemory} has been called.
31     private static final AtomicBoolean sPrefetchLibraryHasBeenCalled = new AtomicBoolean();
32 
33     /**
34      * Prefetches the native libraries in a background thread.
35      *
36      * Launches a task that, through a short-lived forked process, reads a
37      * part of each page of the native library.  This is done to warm up the
38      * page cache, turning hard page faults into soft ones.
39      *
40      * This is done this way, as testing shows that fadvise(FADV_WILLNEED) is
41      * detrimental to the startup time.
42      */
asyncPrefetchLibrariesToMemory()43     public static void asyncPrefetchLibrariesToMemory() {
44         SysUtils.logPageFaultCountToTracing();
45 
46         final boolean coldStart = sPrefetchLibraryHasBeenCalled.compareAndSet(false, true);
47         // Collection should start close to the native library load, but doesn't have
48         // to be simultaneous with it. Also, don't prefetch in this case, as this would
49         // skew the results.
50         if (coldStart && CommandLine.getInstance().hasSwitch("log-native-library-residency")) {
51             // LibraryPrefetcherJni.get().periodicallyCollectResidency() sleeps, run it on another
52             // thread, and not on the thread pool.
53             new Thread(() -> LibraryPrefetcherJni.get().periodicallyCollectResidency()).start();
54             return;
55         }
56 
57         PostTask.postTask(
58                 TaskTraits.USER_BLOCKING,
59                 () -> {
60                     int percentage =
61                             LibraryPrefetcherJni.get().percentageOfResidentNativeLibraryCode();
62                     try (TraceEvent e =
63                             TraceEvent.scoped(
64                                     "LibraryPrefetcher.asyncPrefetchLibrariesToMemory",
65                                     Integer.toString(percentage))) {
66                         // Arbitrary percentage threshold. If most of the native library is already
67                         // resident (likely with monochrome), don't bother creating a prefetch
68                         // process.
69                         boolean prefetch = coldStart && percentage < 90;
70                         if (prefetch) LibraryPrefetcherJni.get().forkAndPrefetchNativeLibrary();
71                         if (percentage != -1) {
72                             String histogram =
73                                     "LibraryLoader.PercentageOfResidentCodeBeforePrefetch"
74                                             + (coldStart ? ".ColdStartup" : ".WarmStartup");
75                             RecordHistogram.recordPercentageHistogram(histogram, percentage);
76                         }
77                     }
78                     // Removes a dead flag, don't remove the removal code before M77 at least.
79                     ContextUtils.getAppSharedPreferences()
80                             .edit()
81                             .remove("dont_prefetch_libraries")
82                             .apply();
83                 });
84     }
85 
86     @NativeMethods
87     interface Natives {
88         // Finds the ranges corresponding to the native library pages, forks a new
89         // process to prefetch these pages and waits for it. The new process then
90         // terminates. This is blocking.
forkAndPrefetchNativeLibrary()91         void forkAndPrefetchNativeLibrary();
92 
93         // Returns the percentage of the native library code page that are currently reseident in
94         // memory.
percentageOfResidentNativeLibraryCode()95         int percentageOfResidentNativeLibraryCode();
96 
97         // Periodically logs native library residency from this thread.
periodicallyCollectResidency()98         void periodicallyCollectResidency();
99     }
100 }
101