| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/android/library_loader/library_loader_hooks.h" |
| |
| #include "base/android/jni_string.h" |
| #include "base/android/library_loader/anchor_functions_buildflags.h" |
| #include "base/android/library_loader/library_load_from_apk_status_codes.h" |
| #include "base/android/library_loader/library_prefetcher.h" |
| #include "base/android/orderfile/orderfile_buildflags.h" |
| #include "base/at_exit.h" |
| #include "base/base_switches.h" |
| #include "base/command_line.h" |
| #include "base/metrics/histogram.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "jni/LibraryLoader_jni.h" |
| |
| #if BUILDFLAG(ORDERFILE_INSTRUMENTATION) |
| #include "base/android/orderfile/orderfile_instrumentation.h" |
| #endif |
| |
| namespace base { |
| namespace android { |
| |
| namespace { |
| |
| base::AtExitManager* g_at_exit_manager = NULL; |
| const char* g_library_version_number = ""; |
| LibraryLoadedHook* g_registration_callback = NULL; |
| NativeInitializationHook* g_native_initialization_hook = NULL; |
| |
| enum RendererHistogramCode { |
| // Renderer load at fixed address success, fail, or not attempted. |
| // Renderers do not attempt to load at at fixed address if on a |
| // low-memory device on which browser load at fixed address has already |
| // failed. |
| LFA_SUCCESS = 0, |
| LFA_BACKOFF_USED = 1, |
| LFA_NOT_ATTEMPTED = 2, |
| |
| // End sentinel, also used as nothing-pending indicator. |
| MAX_RENDERER_HISTOGRAM_CODE = 3, |
| NO_PENDING_HISTOGRAM_CODE = MAX_RENDERER_HISTOGRAM_CODE |
| }; |
| |
| enum BrowserHistogramCode { |
| // Non-low-memory random address browser loads. |
| NORMAL_LRA_SUCCESS = 0, |
| |
| // Low-memory browser loads at fixed address, success or fail. |
| LOW_MEMORY_LFA_SUCCESS = 1, |
| LOW_MEMORY_LFA_BACKOFF_USED = 2, |
| |
| MAX_BROWSER_HISTOGRAM_CODE = 3, |
| }; |
| |
| RendererHistogramCode g_renderer_histogram_code = NO_PENDING_HISTOGRAM_CODE; |
| |
| // Indicate whether g_library_preloader_renderer_histogram_code is valid |
| bool g_library_preloader_renderer_histogram_code_registered = false; |
| |
| // The return value of NativeLibraryPreloader.loadLibrary() in child processes, |
| // it is initialized to the invalid value which shouldn't showup in UMA report. |
| int g_library_preloader_renderer_histogram_code = -1; |
| |
| // The amount of time, in milliseconds, that it took to load the shared |
| // libraries in the renderer. Set in |
| // RegisterChromiumAndroidLinkerRendererHistogram. |
| long g_renderer_library_load_time_ms = 0; |
| |
| void RecordChromiumAndroidLinkerRendererHistogram() { |
| if (g_renderer_histogram_code == NO_PENDING_HISTOGRAM_CODE) |
| return; |
| // Record and release the pending histogram value. |
| UMA_HISTOGRAM_ENUMERATION("ChromiumAndroidLinker.RendererStates", |
| g_renderer_histogram_code, |
| MAX_RENDERER_HISTOGRAM_CODE); |
| g_renderer_histogram_code = NO_PENDING_HISTOGRAM_CODE; |
| |
| // Record how long it took to load the shared libraries. |
| UMA_HISTOGRAM_TIMES("ChromiumAndroidLinker.RendererLoadTime", |
| base::TimeDelta::FromMilliseconds(g_renderer_library_load_time_ms)); |
| } |
| |
| void RecordLibraryPreloaderRendereHistogram() { |
| if (g_library_preloader_renderer_histogram_code_registered) { |
| UmaHistogramSparse("Android.NativeLibraryPreloader.Result.Renderer", |
| g_library_preloader_renderer_histogram_code); |
| } |
| } |
| |
| #if BUILDFLAG(SUPPORTS_CODE_ORDERING) |
| bool ShouldDoOrderfileMemoryOptimization() { |
| return CommandLine::ForCurrentProcess()->HasSwitch( |
| switches::kOrderfileMemoryOptimization); |
| } |
| #endif |
| |
| } // namespace |
| |
| static void JNI_LibraryLoader_RegisterChromiumAndroidLinkerRendererHistogram( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& jcaller, |
| jboolean requested_shared_relro, |
| jboolean load_at_fixed_address_failed, |
| jlong library_load_time_ms) { |
| // Note a pending histogram value for later recording. |
| if (requested_shared_relro) { |
| g_renderer_histogram_code = load_at_fixed_address_failed |
| ? LFA_BACKOFF_USED : LFA_SUCCESS; |
| } else { |
| g_renderer_histogram_code = LFA_NOT_ATTEMPTED; |
| } |
| |
| g_renderer_library_load_time_ms = library_load_time_ms; |
| } |
| |
| static void 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) { |
| // For low-memory devices, record whether or not we successfully loaded the |
| // browser at a fixed address. Otherwise just record a normal invocation. |
| BrowserHistogramCode histogram_code; |
| if (is_using_browser_shared_relros) { |
| histogram_code = load_at_fixed_address_failed |
| ? LOW_MEMORY_LFA_BACKOFF_USED : LOW_MEMORY_LFA_SUCCESS; |
| } else { |
| histogram_code = NORMAL_LRA_SUCCESS; |
| } |
| UMA_HISTOGRAM_ENUMERATION("ChromiumAndroidLinker.BrowserStates", |
| histogram_code, |
| MAX_BROWSER_HISTOGRAM_CODE); |
| |
| // Record the device support for loading a library directly from the APK file. |
| UMA_HISTOGRAM_ENUMERATION( |
| "ChromiumAndroidLinker.LibraryLoadFromApkStatus", |
| static_cast<LibraryLoadFromApkStatusCodes>(library_load_from_apk_status), |
| LIBRARY_LOAD_FROM_APK_STATUS_CODES_MAX); |
| |
| // Record how long it took to load the shared libraries. |
| UMA_HISTOGRAM_TIMES("ChromiumAndroidLinker.BrowserLoadTime", |
| base::TimeDelta::FromMilliseconds(library_load_time_ms)); |
| } |
| |
| static void JNI_LibraryLoader_RecordLibraryPreloaderBrowserHistogram( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& jcaller, |
| jint status) { |
| UmaHistogramSparse("Android.NativeLibraryPreloader.Result.Browser", status); |
| } |
| |
| static void JNI_LibraryLoader_RegisterLibraryPreloaderRendererHistogram( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& jcaller, |
| jint status) { |
| g_library_preloader_renderer_histogram_code = status; |
| g_library_preloader_renderer_histogram_code_registered = true; |
| } |
| |
| void SetNativeInitializationHook( |
| NativeInitializationHook native_initialization_hook) { |
| g_native_initialization_hook = native_initialization_hook; |
| } |
| |
| void RecordLibraryLoaderRendererHistograms() { |
| RecordChromiumAndroidLinkerRendererHistogram(); |
| RecordLibraryPreloaderRendereHistogram(); |
| } |
| |
| void SetLibraryLoadedHook(LibraryLoadedHook* func) { |
| g_registration_callback = func; |
| } |
| |
| static jboolean JNI_LibraryLoader_LibraryLoaded( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& jcaller, |
| jint library_process_type) { |
| #if BUILDFLAG(ORDERFILE_INSTRUMENTATION) |
| orderfile::StartDelayedDump(); |
| #endif |
| |
| #if BUILDFLAG(SUPPORTS_CODE_ORDERING) |
| if (ShouldDoOrderfileMemoryOptimization()) { |
| NativeLibraryPrefetcher::MadviseForOrderfile(); |
| } |
| #endif |
| |
| if (g_native_initialization_hook && |
| !g_native_initialization_hook( |
| static_cast<LibraryProcessType>(library_process_type))) |
| return false; |
| if (g_registration_callback && !g_registration_callback(env, nullptr)) |
| return false; |
| return true; |
| } |
| |
| void LibraryLoaderExitHook() { |
| if (g_at_exit_manager) { |
| delete g_at_exit_manager; |
| g_at_exit_manager = NULL; |
| } |
| } |
| |
| static void JNI_LibraryLoader_ForkAndPrefetchNativeLibrary( |
| JNIEnv* env, |
| const JavaParamRef<jclass>& clazz) { |
| #if BUILDFLAG(SUPPORTS_CODE_ORDERING) |
| return NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary( |
| ShouldDoOrderfileMemoryOptimization()); |
| #endif |
| } |
| |
| static jint JNI_LibraryLoader_PercentageOfResidentNativeLibraryCode( |
| JNIEnv* env, |
| const JavaParamRef<jclass>& clazz) { |
| #if BUILDFLAG(SUPPORTS_CODE_ORDERING) |
| return NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode(); |
| #else |
| return -1; |
| #endif |
| } |
| |
| static void JNI_LibraryLoader_PeriodicallyCollectResidency( |
| JNIEnv* env, |
| const JavaParamRef<jclass>& clazz) { |
| #if BUILDFLAG(SUPPORTS_CODE_ORDERING) |
| NativeLibraryPrefetcher::PeriodicallyCollectResidency(); |
| #else |
| LOG(WARNING) << "Collecting residency is not supported."; |
| #endif |
| } |
| |
| void SetVersionNumber(const char* version_number) { |
| g_library_version_number = strdup(version_number); |
| } |
| |
| ScopedJavaLocalRef<jstring> JNI_LibraryLoader_GetVersionNumber( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& jcaller) { |
| return ConvertUTF8ToJavaString(env, g_library_version_number); |
| } |
| |
| void InitAtExitManager() { |
| g_at_exit_manager = new base::AtExitManager(); |
| } |
| |
| } // namespace android |
| } // namespace base |