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