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