1 // Copyright 2014 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_loader_hooks.h"
6 
7 #include "base/android/jni_string.h"
8 #include "base/android/library_loader/anchor_functions_buildflags.h"
9 #include "base/android/library_loader/library_load_from_apk_status_codes.h"
10 #include "base/android/library_loader/library_prefetcher.h"
11 #include "base/android/orderfile/orderfile_buildflags.h"
12 #include "base/at_exit.h"
13 #include "base/base_switches.h"
14 #include "base/command_line.h"
15 #include "base/metrics/histogram.h"
16 #include "base/metrics/histogram_functions.h"
17 #include "base/metrics/histogram_macros.h"
18 #include "jni/LibraryLoader_jni.h"
19 
20 #if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
21 #include "base/android/orderfile/orderfile_instrumentation.h"
22 #endif
23 
24 namespace base {
25 namespace android {
26 
27 namespace {
28 
29 base::AtExitManager* g_at_exit_manager = NULL;
30 const char* g_library_version_number = "";
31 LibraryLoadedHook* g_registration_callback = NULL;
32 NativeInitializationHook* g_native_initialization_hook = NULL;
33 
34 enum RendererHistogramCode {
35   // Renderer load at fixed address success, fail, or not attempted.
36   // Renderers do not attempt to load at at fixed address if on a
37   // low-memory device on which browser load at fixed address has already
38   // failed.
39   LFA_SUCCESS = 0,
40   LFA_BACKOFF_USED = 1,
41   LFA_NOT_ATTEMPTED = 2,
42 
43   // End sentinel, also used as nothing-pending indicator.
44   MAX_RENDERER_HISTOGRAM_CODE = 3,
45   NO_PENDING_HISTOGRAM_CODE = MAX_RENDERER_HISTOGRAM_CODE
46 };
47 
48 enum BrowserHistogramCode {
49   // Non-low-memory random address browser loads.
50   NORMAL_LRA_SUCCESS = 0,
51 
52   // Low-memory browser loads at fixed address, success or fail.
53   LOW_MEMORY_LFA_SUCCESS = 1,
54   LOW_MEMORY_LFA_BACKOFF_USED = 2,
55 
56   MAX_BROWSER_HISTOGRAM_CODE = 3,
57 };
58 
59 RendererHistogramCode g_renderer_histogram_code = NO_PENDING_HISTOGRAM_CODE;
60 
61 // Indicate whether g_library_preloader_renderer_histogram_code is valid
62 bool g_library_preloader_renderer_histogram_code_registered = false;
63 
64 // The return value of NativeLibraryPreloader.loadLibrary() in child processes,
65 // it is initialized to the invalid value which shouldn't showup in UMA report.
66 int g_library_preloader_renderer_histogram_code = -1;
67 
68 // The amount of time, in milliseconds, that it took to load the shared
69 // libraries in the renderer. Set in
70 // RegisterChromiumAndroidLinkerRendererHistogram.
71 long g_renderer_library_load_time_ms = 0;
72 
RecordChromiumAndroidLinkerRendererHistogram()73 void RecordChromiumAndroidLinkerRendererHistogram() {
74   if (g_renderer_histogram_code == NO_PENDING_HISTOGRAM_CODE)
75     return;
76   // Record and release the pending histogram value.
77   UMA_HISTOGRAM_ENUMERATION("ChromiumAndroidLinker.RendererStates",
78                             g_renderer_histogram_code,
79                             MAX_RENDERER_HISTOGRAM_CODE);
80   g_renderer_histogram_code = NO_PENDING_HISTOGRAM_CODE;
81 
82   // Record how long it took to load the shared libraries.
83   UMA_HISTOGRAM_TIMES("ChromiumAndroidLinker.RendererLoadTime",
84       base::TimeDelta::FromMilliseconds(g_renderer_library_load_time_ms));
85 }
86 
RecordLibraryPreloaderRendereHistogram()87 void RecordLibraryPreloaderRendereHistogram() {
88   if (g_library_preloader_renderer_histogram_code_registered) {
89     UmaHistogramSparse("Android.NativeLibraryPreloader.Result.Renderer",
90                        g_library_preloader_renderer_histogram_code);
91   }
92 }
93 
94 #if BUILDFLAG(SUPPORTS_CODE_ORDERING)
ShouldDoOrderfileMemoryOptimization()95 bool ShouldDoOrderfileMemoryOptimization() {
96   return CommandLine::ForCurrentProcess()->HasSwitch(
97       switches::kOrderfileMemoryOptimization);
98 }
99 #endif
100 
101 }  // namespace
102 
JNI_LibraryLoader_RegisterChromiumAndroidLinkerRendererHistogram(JNIEnv * env,const JavaParamRef<jobject> & jcaller,jboolean requested_shared_relro,jboolean load_at_fixed_address_failed,jlong library_load_time_ms)103 static void JNI_LibraryLoader_RegisterChromiumAndroidLinkerRendererHistogram(
104     JNIEnv* env,
105     const JavaParamRef<jobject>& jcaller,
106     jboolean requested_shared_relro,
107     jboolean load_at_fixed_address_failed,
108     jlong library_load_time_ms) {
109   // Note a pending histogram value for later recording.
110   if (requested_shared_relro) {
111     g_renderer_histogram_code = load_at_fixed_address_failed
112                                 ? LFA_BACKOFF_USED : LFA_SUCCESS;
113   } else {
114     g_renderer_histogram_code = LFA_NOT_ATTEMPTED;
115   }
116 
117   g_renderer_library_load_time_ms = library_load_time_ms;
118 }
119 
JNI_LibraryLoader_RecordChromiumAndroidLinkerBrowserHistogram(JNIEnv * env,const JavaParamRef<jobject> & jcaller,jboolean is_using_browser_shared_relros,jboolean load_at_fixed_address_failed,jint library_load_from_apk_status,jlong library_load_time_ms)120 static void JNI_LibraryLoader_RecordChromiumAndroidLinkerBrowserHistogram(
121     JNIEnv* env,
122     const JavaParamRef<jobject>& jcaller,
123     jboolean is_using_browser_shared_relros,
124     jboolean load_at_fixed_address_failed,
125     jint library_load_from_apk_status,
126     jlong library_load_time_ms) {
127   // For low-memory devices, record whether or not we successfully loaded the
128   // browser at a fixed address. Otherwise just record a normal invocation.
129   BrowserHistogramCode histogram_code;
130   if (is_using_browser_shared_relros) {
131     histogram_code = load_at_fixed_address_failed
132                      ? LOW_MEMORY_LFA_BACKOFF_USED : LOW_MEMORY_LFA_SUCCESS;
133   } else {
134     histogram_code = NORMAL_LRA_SUCCESS;
135   }
136   UMA_HISTOGRAM_ENUMERATION("ChromiumAndroidLinker.BrowserStates",
137                             histogram_code,
138                             MAX_BROWSER_HISTOGRAM_CODE);
139 
140   // Record the device support for loading a library directly from the APK file.
141   UMA_HISTOGRAM_ENUMERATION(
142       "ChromiumAndroidLinker.LibraryLoadFromApkStatus",
143       static_cast<LibraryLoadFromApkStatusCodes>(library_load_from_apk_status),
144       LIBRARY_LOAD_FROM_APK_STATUS_CODES_MAX);
145 
146   // Record how long it took to load the shared libraries.
147   UMA_HISTOGRAM_TIMES("ChromiumAndroidLinker.BrowserLoadTime",
148                       base::TimeDelta::FromMilliseconds(library_load_time_ms));
149 }
150 
JNI_LibraryLoader_RecordLibraryPreloaderBrowserHistogram(JNIEnv * env,const JavaParamRef<jobject> & jcaller,jint status)151 static void JNI_LibraryLoader_RecordLibraryPreloaderBrowserHistogram(
152     JNIEnv* env,
153     const JavaParamRef<jobject>& jcaller,
154     jint status) {
155   UmaHistogramSparse("Android.NativeLibraryPreloader.Result.Browser", status);
156 }
157 
JNI_LibraryLoader_RegisterLibraryPreloaderRendererHistogram(JNIEnv * env,const JavaParamRef<jobject> & jcaller,jint status)158 static void JNI_LibraryLoader_RegisterLibraryPreloaderRendererHistogram(
159     JNIEnv* env,
160     const JavaParamRef<jobject>& jcaller,
161     jint status) {
162   g_library_preloader_renderer_histogram_code = status;
163   g_library_preloader_renderer_histogram_code_registered = true;
164 }
165 
SetNativeInitializationHook(NativeInitializationHook native_initialization_hook)166 void SetNativeInitializationHook(
167     NativeInitializationHook native_initialization_hook) {
168   g_native_initialization_hook = native_initialization_hook;
169 }
170 
RecordLibraryLoaderRendererHistograms()171 void RecordLibraryLoaderRendererHistograms() {
172   RecordChromiumAndroidLinkerRendererHistogram();
173   RecordLibraryPreloaderRendereHistogram();
174 }
175 
SetLibraryLoadedHook(LibraryLoadedHook * func)176 void SetLibraryLoadedHook(LibraryLoadedHook* func) {
177   g_registration_callback = func;
178 }
179 
JNI_LibraryLoader_LibraryLoaded(JNIEnv * env,const JavaParamRef<jobject> & jcaller,jint library_process_type)180 static jboolean JNI_LibraryLoader_LibraryLoaded(
181     JNIEnv* env,
182     const JavaParamRef<jobject>& jcaller,
183     jint library_process_type) {
184 #if BUILDFLAG(ORDERFILE_INSTRUMENTATION)
185   orderfile::StartDelayedDump();
186 #endif
187 
188 #if BUILDFLAG(SUPPORTS_CODE_ORDERING)
189   if (ShouldDoOrderfileMemoryOptimization()) {
190     NativeLibraryPrefetcher::MadviseForOrderfile();
191   }
192 #endif
193 
194   if (g_native_initialization_hook &&
195       !g_native_initialization_hook(
196           static_cast<LibraryProcessType>(library_process_type)))
197     return false;
198   if (g_registration_callback && !g_registration_callback(env, nullptr))
199     return false;
200   return true;
201 }
202 
LibraryLoaderExitHook()203 void LibraryLoaderExitHook() {
204   if (g_at_exit_manager) {
205     delete g_at_exit_manager;
206     g_at_exit_manager = NULL;
207   }
208 }
209 
JNI_LibraryLoader_ForkAndPrefetchNativeLibrary(JNIEnv * env,const JavaParamRef<jclass> & clazz)210 static void JNI_LibraryLoader_ForkAndPrefetchNativeLibrary(
211     JNIEnv* env,
212     const JavaParamRef<jclass>& clazz) {
213 #if BUILDFLAG(SUPPORTS_CODE_ORDERING)
214   return NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary(
215       ShouldDoOrderfileMemoryOptimization());
216 #endif
217 }
218 
JNI_LibraryLoader_PercentageOfResidentNativeLibraryCode(JNIEnv * env,const JavaParamRef<jclass> & clazz)219 static jint JNI_LibraryLoader_PercentageOfResidentNativeLibraryCode(
220     JNIEnv* env,
221     const JavaParamRef<jclass>& clazz) {
222 #if BUILDFLAG(SUPPORTS_CODE_ORDERING)
223   return NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode();
224 #else
225   return -1;
226 #endif
227 }
228 
JNI_LibraryLoader_PeriodicallyCollectResidency(JNIEnv * env,const JavaParamRef<jclass> & clazz)229 static void JNI_LibraryLoader_PeriodicallyCollectResidency(
230     JNIEnv* env,
231     const JavaParamRef<jclass>& clazz) {
232 #if BUILDFLAG(SUPPORTS_CODE_ORDERING)
233   NativeLibraryPrefetcher::PeriodicallyCollectResidency();
234 #else
235   LOG(WARNING) << "Collecting residency is not supported.";
236 #endif
237 }
238 
SetVersionNumber(const char * version_number)239 void SetVersionNumber(const char* version_number) {
240   g_library_version_number = strdup(version_number);
241 }
242 
JNI_LibraryLoader_GetVersionNumber(JNIEnv * env,const JavaParamRef<jobject> & jcaller)243 ScopedJavaLocalRef<jstring> JNI_LibraryLoader_GetVersionNumber(
244     JNIEnv* env,
245     const JavaParamRef<jobject>& jcaller) {
246   return ConvertUTF8ToJavaString(env, g_library_version_number);
247 }
248 
InitAtExitManager()249 void InitAtExitManager() {
250   g_at_exit_manager = new base::AtExitManager();
251 }
252 
253 }  // namespace android
254 }  // namespace base
255