|  | // 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 |