// 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/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" 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& 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& 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(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& jcaller, jint status) { UmaHistogramSparse("Android.NativeLibraryPreloader.Result.Browser", status); } static void JNI_LibraryLoader_RegisterLibraryPreloaderRendererHistogram( JNIEnv* env, const JavaParamRef& 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& jcaller, jint library_process_type) { #if BUILDFLAG(SUPPORTS_CODE_ORDERING) if (ShouldDoOrderfileMemoryOptimization()) { NativeLibraryPrefetcher::MadviseForOrderfile(); } #endif if (g_native_initialization_hook && !g_native_initialization_hook( static_cast(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& clazz) { #if BUILDFLAG(SUPPORTS_CODE_ORDERING) if (!ShouldDoOrderfileMemoryOptimization() || CommandLine::ForCurrentProcess()->HasSwitch( switches::kForceNativePrefetch)) { NativeLibraryPrefetcher::ForkAndPrefetchNativeLibrary( CommandLine::ForCurrentProcess()->HasSwitch( switches::kNativePrefetchOrderedOnly)); } #endif } static jint JNI_LibraryLoader_PercentageOfResidentNativeLibraryCode( JNIEnv* env, const JavaParamRef& clazz) { #if BUILDFLAG(SUPPORTS_CODE_ORDERING) return NativeLibraryPrefetcher::PercentageOfResidentNativeLibraryCode(); #else return -1; #endif } static void JNI_LibraryLoader_PeriodicallyCollectResidency( JNIEnv* env, const JavaParamRef& 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 JNI_LibraryLoader_GetVersionNumber( JNIEnv* env, const JavaParamRef& jcaller) { return ConvertUTF8ToJavaString(env, g_library_version_number); } void InitAtExitManager() { g_at_exit_manager = new base::AtExitManager(); } } // namespace android } // namespace base