Loading core/java/android/content/res/ResourceTimer.java 0 → 100644 +289 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.content.res; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.SystemClock; import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.FastPrintWriter; import java.io.FileOutputStream; import java.io.PrintWriter; import java.util.Arrays; /** * Provides access to the resource timers without intruding on other classes. * @hide */ public final class ResourceTimer { private static final String TAG = "ResourceTimer"; // Enable metrics in this process. The flag may be set false in some processes. The flag is // never set true at runtime, so setting it false here disables the feature entirely. private static boolean sEnabled = true; // Set true for incremental metrics (the counters are reset after every fetch). Set false for // cumulative metrics (the counters are never reset and accumulate values for the lifetime of // the process). private static boolean sIncrementalMetrics = true; // Set true to enable some debug behavior. private static boolean ENABLE_DEBUG = false; // The global lock. private static final Object sLock = new Object(); // The singleton cache manager private static ResourceTimer sManager; // The handler for the timeouts. private static Handler mHandler; // The time at which the process started. private final static long sProcessStart = SystemClock.uptimeMillis(); // Metrics are published at offsets from the process start. Processes publish at five minutes // and one hour. Thereafter, metrics are published every 12 hours. The values in this array // are in units of minutes. private static final long[] sPublicationPoints = new long[]{ 5, 60, 60 * 12 }; // The index of the latest publication point. private static int sCurrentPoint; /** * The runtime timer configuration. */ private static class Config { // The number of timers in the runtime int maxTimer; // The number of histogram buckets per timer. int maxBuckets; // The number of "largest" values per timer. int maxLargest; // A string label for each timer. This array has maxTimer elements. String[] timers; } /** * The timer information that is pulled from the native runtime. All times have units of ns. */ private static class Timer { int count; long total; int mintime; int maxtime; int[] largest; int[] percentile; @Override public String toString() { return TextUtils.formatSimple("%d:%d:%d:%d", count, total, mintime, maxtime); } } /** * A singleton Config. This is initialized when the timers are started. */ @GuardedBy("sLock") private static Config sConfig; /** * A singleton Summary object that is refilled from the native side. The length of the array * is the number of timers that can be fetched. nativeGetTimers() will fill the array to the * smaller of the length of the array or the actual number of timers in the runtime. The * actual number of timers in the run time is returned by the function. */ @GuardedBy("sLock") private static Timer[] sTimers; /** * The time at which the local timer array was last updated. This has the same units as * sProcessStart; the difference is the process run time. */ @GuardedBy("sLock") private static long sLastUpdated = 0; // The constructor is private. Use the factory to get a hold of the manager. private ResourceTimer() { throw new RuntimeException("ResourceTimer constructor"); } /** * Start the manager. This runs a periodic job that collects and publishes timer values. * This is not part of the constructor only because the looper failicities might not be * available at the beginning of time. */ public static void start() { synchronized (sLock) { if (!sEnabled) { return; } if (mHandler != null) { // Nothing to be done. The job has already been started. return; } if (Looper.getMainLooper() == null) { throw new RuntimeException("ResourceTimer started too early"); } mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { ResourceTimer.handleMessage(msg); } }; // Initialize the container that holds information from the native runtime. The // container is built according to the dimensions returned by the native layer. sConfig = new Config(); nativeEnableTimers(sConfig); sTimers = new Timer[sConfig.maxTimer]; for (int i = 0; i < sTimers.length; i++) { sTimers[i] = new Timer(); sTimers[i].percentile = new int[sConfig.maxBuckets]; sTimers[i].largest = new int[sConfig.maxLargest]; } sCurrentPoint = 0; startTimer(); } } /** * Handle a refresh message. Publish the metrics and start a timer for the next publication. * The message parameter is unused. */ private static void handleMessage(Message msg) { synchronized (sLock) { publish(); startTimer(); } } /** * Start a timer to the next publication point. Publication points are referenced from * process start. */ @GuardedBy("sLock") private static void startTimer() { // The delay is in minutes. long delay; if (sCurrentPoint < sPublicationPoints.length) { delay = sPublicationPoints[sCurrentPoint]; } else { // Repeat with the final publication point. delay = sCurrentPoint * sPublicationPoints[sPublicationPoints.length - 1]; } // Convert minutes to milliseconds. delay *= 60 * 1000; // If debug is enabled, convert hours down to minutes. if (ENABLE_DEBUG) { delay /= 60; } mHandler.sendEmptyMessageAtTime(0, sProcessStart + delay); } /** * Update the local copy of the timers. The current time is saved as well. */ @GuardedBy("sLock") private static void update(boolean reset) { nativeGetTimers(sTimers, reset); sLastUpdated = SystemClock.uptimeMillis(); } /** * Retrieve the accumulated timer information, reset the native counters, and advance the * publication point. */ @GuardedBy("sLock") private static void publish() { update(true); // Log the number of records read. This happens a few times a day. for (int i = 0; i < sTimers.length; i++) { if (sTimers[i].count > 0) { Log.i(TAG, TextUtils.formatSimple("%s count=%d pvalues=%s", sConfig.timers[i], sTimers[i].count, packedString(sTimers[i].percentile))); } } sCurrentPoint++; } /** * Given an int[], return a string that is formatted as "a,b,c" with no spaces. */ private static String packedString(int[] a) { return Arrays.toString(a).replaceAll("[\\]\\[ ]", ""); } /** * Update the metrics information and dump it. * @hide */ public static void dumpTimers(@NonNull ParcelFileDescriptor pfd, @Nullable String[] args) { FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor()); PrintWriter pw = new FastPrintWriter(fout); synchronized (sLock) { if (!sEnabled || (sConfig == null)) { pw.println(" Timers are not enabled in this process"); pw.flush(); return; } } // Look for the --refresh switch. If the switch is present, then sTimers is updated. // Otherwise, the current value of sTimers is displayed. boolean refresh = Arrays.asList(args).contains("-refresh"); synchronized (sLock) { update(refresh); long runtime = sLastUpdated - sProcessStart; pw.format(" config runtime=%d proc=%s\n", runtime, Process.myProcessName()); for (int i = 0; i < sTimers.length; i++) { Timer t = sTimers[i]; if (t.count != 0) { String name = sConfig.timers[i]; pw.format(" stats timer=%s cnt=%d avg=%d min=%d max=%d pval=%s " + "largest=%s\n", name, t.count, t.total / t.count, t.mintime, t.maxtime, packedString(t.percentile), packedString(t.largest)); } } } pw.flush(); } // Enable (or disabled) the runtime timers. Note that timers are disabled by default. This // retrieves the runtime timer configuration that are taking effect private static native int nativeEnableTimers(@NonNull Config config); // Retrieve the timers from the native layer. If reset is true, the timers are reset after // being fetched. The method returns the number of timers that are defined in the runtime // layer. The stats array is filled out to the smaller of its actual size and the number of // runtime timers; it never overflows. private static native int nativeGetTimers(@NonNull Timer[] stats, boolean reset); } core/jni/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -209,6 +209,7 @@ cc_library_shared { "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_content_res_ResourceTimer.cpp", "android_security_Scrypt.cpp", "com_android_internal_content_om_OverlayConfig.cpp", "com_android_internal_net_NetworkUtilsInternal.cpp", Loading core/jni/AndroidRuntime.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -117,6 +117,7 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_content_res_ResourceTimer(JNIEnv* env); extern int register_android_graphics_BLASTBufferQueue(JNIEnv* env); extern int register_android_graphics_SurfaceTexture(JNIEnv* env); extern int register_android_view_DisplayEventReceiver(JNIEnv* env); Loading Loading @@ -1506,6 +1507,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_content_res_ResourceTimer), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_view_InputDevice), Loading core/jni/android_content_res_ResourceTimer.cpp 0 → 100644 +129 −0 Original line number Diff line number Diff line /* * Copyright 2022, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <jni.h> #include <core_jni_helpers.h> #include <utils/misc.h> #include <androidfw/ResourceTimer.h> namespace android { // ---------------------------------------------------------------------------- static struct { jfieldID maxTimer; jfieldID maxBuckets; jfieldID maxLargest; jfieldID timers; } gConfigOffsets; static struct { jfieldID count; jfieldID total; jfieldID mintime; jfieldID maxtime; jfieldID largest; jfieldID percentile; } gTimerOffsets; // ---------------------------------------------------------------------------- static int NativeGetTimers(JNIEnv* env, jobject /*clazz*/, jobjectArray timer, jboolean reset) { size_t size = ResourceTimer::counterSize; if (jsize st = env->GetArrayLength(timer); st < size) { // Shrink the size to the minimum of the available counters and the available space. size = st; } for (size_t i = 0; i < size; i++) { ResourceTimer::Timer src; ResourceTimer::copy(i, src, reset); jobject dst = env->GetObjectArrayElement(timer, i); env->SetIntField(dst, gTimerOffsets.count, src.count); if (src.count == 0) { continue; } src.compute(); env->SetIntField(dst, gTimerOffsets.count, src.count); env->SetLongField(dst, gTimerOffsets.total, src.total); env->SetIntField(dst, gTimerOffsets.mintime, src.mintime); env->SetIntField(dst, gTimerOffsets.maxtime, src.maxtime); jintArray percentile = reinterpret_cast<jintArray>(env->GetObjectField(dst, gTimerOffsets.percentile)); env->SetIntArrayRegion(percentile, 0, 1, &src.pvalues.p50.nominal); env->SetIntArrayRegion(percentile, 1, 1, &src.pvalues.p90.nominal); env->SetIntArrayRegion(percentile, 2, 1, &src.pvalues.p95.nominal); env->SetIntArrayRegion(percentile, 3, 1, &src.pvalues.p99.nominal); jintArray largest = reinterpret_cast<jintArray>(env->GetObjectField(dst, gTimerOffsets.largest)); env->SetIntArrayRegion(largest, 0, ResourceTimer::Timer::MaxLargest, src.largest); } return size; } static jstring counterName(JNIEnv *env, int counter) { char const *s = ResourceTimer::toString(static_cast<ResourceTimer::Counter>(counter)); return env->NewStringUTF(s); } static int NativeEnableTimers(JNIEnv* env, jobject /*clazz*/, jobject config) { ResourceTimer::enable(); env->SetIntField(config, gConfigOffsets.maxTimer, ResourceTimer::counterSize); env->SetIntField(config, gConfigOffsets.maxBuckets, 4); // Number of ints in PValues env->SetIntField(config, gConfigOffsets.maxLargest, ResourceTimer::Timer::MaxLargest); jclass str = env->FindClass("java/lang/String"); jstring empty = counterName(env, 0); jobjectArray timers = env->NewObjectArray(ResourceTimer::counterSize, str, empty); for (int i = 0; i < ResourceTimer::counterSize; i++) { env->SetObjectArrayElement(timers, i, counterName(env, i)); } env->SetObjectField(config, gConfigOffsets.timers, timers); return 0; } // ---------------------------------------------------------------------------- // JNI registration. static const JNINativeMethod gResourceTimerMethods[] = { {"nativeEnableTimers", "(Landroid/content/res/ResourceTimer$Config;)I", (void *) NativeEnableTimers}, {"nativeGetTimers", "([Landroid/content/res/ResourceTimer$Timer;Z)I", (void *) NativeGetTimers}, }; int register_android_content_res_ResourceTimer(JNIEnv* env) { jclass config = FindClassOrDie(env, "android/content/res/ResourceTimer$Config"); gConfigOffsets.maxTimer = GetFieldIDOrDie(env, config, "maxTimer", "I"); gConfigOffsets.maxBuckets = GetFieldIDOrDie(env, config, "maxBuckets", "I"); gConfigOffsets.maxLargest = GetFieldIDOrDie(env, config, "maxLargest", "I"); gConfigOffsets.timers = GetFieldIDOrDie(env, config, "timers", "[Ljava/lang/String;"); jclass timers = FindClassOrDie(env, "android/content/res/ResourceTimer$Timer"); gTimerOffsets.count = GetFieldIDOrDie(env, timers, "count", "I"); gTimerOffsets.total = GetFieldIDOrDie(env, timers, "total", "J"); gTimerOffsets.mintime = GetFieldIDOrDie(env, timers, "mintime", "I"); gTimerOffsets.maxtime = GetFieldIDOrDie(env, timers, "maxtime", "I"); gTimerOffsets.largest = GetFieldIDOrDie(env, timers, "largest", "[I"); gTimerOffsets.percentile = GetFieldIDOrDie(env, timers, "percentile", "[I"); return RegisterMethodsOrDie(env, "android/content/res/ResourceTimer", gResourceTimerMethods, NELEM(gResourceTimerMethods)); } }; // namespace android libs/androidfw/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ cc_library { "misc.cpp", "ObbFile.cpp", "PosixUtils.cpp", "ResourceTimer.cpp", "ResourceTypes.cpp", "ResourceUtils.cpp", "StreamingZipInflater.cpp", Loading Loading @@ -173,6 +174,7 @@ cc_test { "tests/Idmap_test.cpp", "tests/LoadedArsc_test.cpp", "tests/Locale_test.cpp", "tests/ResourceTimer_test.cpp", "tests/ResourceUtils_test.cpp", "tests/ResTable_test.cpp", "tests/Split_test.cpp", Loading Loading
core/java/android/content/res/ResourceTimer.java 0 → 100644 +289 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.content.res; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.SystemClock; import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.FastPrintWriter; import java.io.FileOutputStream; import java.io.PrintWriter; import java.util.Arrays; /** * Provides access to the resource timers without intruding on other classes. * @hide */ public final class ResourceTimer { private static final String TAG = "ResourceTimer"; // Enable metrics in this process. The flag may be set false in some processes. The flag is // never set true at runtime, so setting it false here disables the feature entirely. private static boolean sEnabled = true; // Set true for incremental metrics (the counters are reset after every fetch). Set false for // cumulative metrics (the counters are never reset and accumulate values for the lifetime of // the process). private static boolean sIncrementalMetrics = true; // Set true to enable some debug behavior. private static boolean ENABLE_DEBUG = false; // The global lock. private static final Object sLock = new Object(); // The singleton cache manager private static ResourceTimer sManager; // The handler for the timeouts. private static Handler mHandler; // The time at which the process started. private final static long sProcessStart = SystemClock.uptimeMillis(); // Metrics are published at offsets from the process start. Processes publish at five minutes // and one hour. Thereafter, metrics are published every 12 hours. The values in this array // are in units of minutes. private static final long[] sPublicationPoints = new long[]{ 5, 60, 60 * 12 }; // The index of the latest publication point. private static int sCurrentPoint; /** * The runtime timer configuration. */ private static class Config { // The number of timers in the runtime int maxTimer; // The number of histogram buckets per timer. int maxBuckets; // The number of "largest" values per timer. int maxLargest; // A string label for each timer. This array has maxTimer elements. String[] timers; } /** * The timer information that is pulled from the native runtime. All times have units of ns. */ private static class Timer { int count; long total; int mintime; int maxtime; int[] largest; int[] percentile; @Override public String toString() { return TextUtils.formatSimple("%d:%d:%d:%d", count, total, mintime, maxtime); } } /** * A singleton Config. This is initialized when the timers are started. */ @GuardedBy("sLock") private static Config sConfig; /** * A singleton Summary object that is refilled from the native side. The length of the array * is the number of timers that can be fetched. nativeGetTimers() will fill the array to the * smaller of the length of the array or the actual number of timers in the runtime. The * actual number of timers in the run time is returned by the function. */ @GuardedBy("sLock") private static Timer[] sTimers; /** * The time at which the local timer array was last updated. This has the same units as * sProcessStart; the difference is the process run time. */ @GuardedBy("sLock") private static long sLastUpdated = 0; // The constructor is private. Use the factory to get a hold of the manager. private ResourceTimer() { throw new RuntimeException("ResourceTimer constructor"); } /** * Start the manager. This runs a periodic job that collects and publishes timer values. * This is not part of the constructor only because the looper failicities might not be * available at the beginning of time. */ public static void start() { synchronized (sLock) { if (!sEnabled) { return; } if (mHandler != null) { // Nothing to be done. The job has already been started. return; } if (Looper.getMainLooper() == null) { throw new RuntimeException("ResourceTimer started too early"); } mHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { ResourceTimer.handleMessage(msg); } }; // Initialize the container that holds information from the native runtime. The // container is built according to the dimensions returned by the native layer. sConfig = new Config(); nativeEnableTimers(sConfig); sTimers = new Timer[sConfig.maxTimer]; for (int i = 0; i < sTimers.length; i++) { sTimers[i] = new Timer(); sTimers[i].percentile = new int[sConfig.maxBuckets]; sTimers[i].largest = new int[sConfig.maxLargest]; } sCurrentPoint = 0; startTimer(); } } /** * Handle a refresh message. Publish the metrics and start a timer for the next publication. * The message parameter is unused. */ private static void handleMessage(Message msg) { synchronized (sLock) { publish(); startTimer(); } } /** * Start a timer to the next publication point. Publication points are referenced from * process start. */ @GuardedBy("sLock") private static void startTimer() { // The delay is in minutes. long delay; if (sCurrentPoint < sPublicationPoints.length) { delay = sPublicationPoints[sCurrentPoint]; } else { // Repeat with the final publication point. delay = sCurrentPoint * sPublicationPoints[sPublicationPoints.length - 1]; } // Convert minutes to milliseconds. delay *= 60 * 1000; // If debug is enabled, convert hours down to minutes. if (ENABLE_DEBUG) { delay /= 60; } mHandler.sendEmptyMessageAtTime(0, sProcessStart + delay); } /** * Update the local copy of the timers. The current time is saved as well. */ @GuardedBy("sLock") private static void update(boolean reset) { nativeGetTimers(sTimers, reset); sLastUpdated = SystemClock.uptimeMillis(); } /** * Retrieve the accumulated timer information, reset the native counters, and advance the * publication point. */ @GuardedBy("sLock") private static void publish() { update(true); // Log the number of records read. This happens a few times a day. for (int i = 0; i < sTimers.length; i++) { if (sTimers[i].count > 0) { Log.i(TAG, TextUtils.formatSimple("%s count=%d pvalues=%s", sConfig.timers[i], sTimers[i].count, packedString(sTimers[i].percentile))); } } sCurrentPoint++; } /** * Given an int[], return a string that is formatted as "a,b,c" with no spaces. */ private static String packedString(int[] a) { return Arrays.toString(a).replaceAll("[\\]\\[ ]", ""); } /** * Update the metrics information and dump it. * @hide */ public static void dumpTimers(@NonNull ParcelFileDescriptor pfd, @Nullable String[] args) { FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor()); PrintWriter pw = new FastPrintWriter(fout); synchronized (sLock) { if (!sEnabled || (sConfig == null)) { pw.println(" Timers are not enabled in this process"); pw.flush(); return; } } // Look for the --refresh switch. If the switch is present, then sTimers is updated. // Otherwise, the current value of sTimers is displayed. boolean refresh = Arrays.asList(args).contains("-refresh"); synchronized (sLock) { update(refresh); long runtime = sLastUpdated - sProcessStart; pw.format(" config runtime=%d proc=%s\n", runtime, Process.myProcessName()); for (int i = 0; i < sTimers.length; i++) { Timer t = sTimers[i]; if (t.count != 0) { String name = sConfig.timers[i]; pw.format(" stats timer=%s cnt=%d avg=%d min=%d max=%d pval=%s " + "largest=%s\n", name, t.count, t.total / t.count, t.mintime, t.maxtime, packedString(t.percentile), packedString(t.largest)); } } } pw.flush(); } // Enable (or disabled) the runtime timers. Note that timers are disabled by default. This // retrieves the runtime timer configuration that are taking effect private static native int nativeEnableTimers(@NonNull Config config); // Retrieve the timers from the native layer. If reset is true, the timers are reset after // being fetched. The method returns the number of timers that are defined in the runtime // layer. The stats array is filled out to the smaller of its actual size and the number of // runtime timers; it never overflows. private static native int nativeGetTimers(@NonNull Timer[] stats, boolean reset); }
core/jni/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -209,6 +209,7 @@ cc_library_shared { "android_content_res_ApkAssets.cpp", "android_content_res_ObbScanner.cpp", "android_content_res_Configuration.cpp", "android_content_res_ResourceTimer.cpp", "android_security_Scrypt.cpp", "com_android_internal_content_om_OverlayConfig.cpp", "com_android_internal_net_NetworkUtilsInternal.cpp", Loading
core/jni/AndroidRuntime.cpp +2 −0 Original line number Diff line number Diff line Loading @@ -117,6 +117,7 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env); extern int register_android_content_StringBlock(JNIEnv* env); extern int register_android_content_XmlBlock(JNIEnv* env); extern int register_android_content_res_ApkAssets(JNIEnv* env); extern int register_android_content_res_ResourceTimer(JNIEnv* env); extern int register_android_graphics_BLASTBufferQueue(JNIEnv* env); extern int register_android_graphics_SurfaceTexture(JNIEnv* env); extern int register_android_view_DisplayEventReceiver(JNIEnv* env); Loading Loading @@ -1506,6 +1507,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_StringBlock), REG_JNI(register_android_content_XmlBlock), REG_JNI(register_android_content_res_ApkAssets), REG_JNI(register_android_content_res_ResourceTimer), REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_view_InputDevice), Loading
core/jni/android_content_res_ResourceTimer.cpp 0 → 100644 +129 −0 Original line number Diff line number Diff line /* * Copyright 2022, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <jni.h> #include <core_jni_helpers.h> #include <utils/misc.h> #include <androidfw/ResourceTimer.h> namespace android { // ---------------------------------------------------------------------------- static struct { jfieldID maxTimer; jfieldID maxBuckets; jfieldID maxLargest; jfieldID timers; } gConfigOffsets; static struct { jfieldID count; jfieldID total; jfieldID mintime; jfieldID maxtime; jfieldID largest; jfieldID percentile; } gTimerOffsets; // ---------------------------------------------------------------------------- static int NativeGetTimers(JNIEnv* env, jobject /*clazz*/, jobjectArray timer, jboolean reset) { size_t size = ResourceTimer::counterSize; if (jsize st = env->GetArrayLength(timer); st < size) { // Shrink the size to the minimum of the available counters and the available space. size = st; } for (size_t i = 0; i < size; i++) { ResourceTimer::Timer src; ResourceTimer::copy(i, src, reset); jobject dst = env->GetObjectArrayElement(timer, i); env->SetIntField(dst, gTimerOffsets.count, src.count); if (src.count == 0) { continue; } src.compute(); env->SetIntField(dst, gTimerOffsets.count, src.count); env->SetLongField(dst, gTimerOffsets.total, src.total); env->SetIntField(dst, gTimerOffsets.mintime, src.mintime); env->SetIntField(dst, gTimerOffsets.maxtime, src.maxtime); jintArray percentile = reinterpret_cast<jintArray>(env->GetObjectField(dst, gTimerOffsets.percentile)); env->SetIntArrayRegion(percentile, 0, 1, &src.pvalues.p50.nominal); env->SetIntArrayRegion(percentile, 1, 1, &src.pvalues.p90.nominal); env->SetIntArrayRegion(percentile, 2, 1, &src.pvalues.p95.nominal); env->SetIntArrayRegion(percentile, 3, 1, &src.pvalues.p99.nominal); jintArray largest = reinterpret_cast<jintArray>(env->GetObjectField(dst, gTimerOffsets.largest)); env->SetIntArrayRegion(largest, 0, ResourceTimer::Timer::MaxLargest, src.largest); } return size; } static jstring counterName(JNIEnv *env, int counter) { char const *s = ResourceTimer::toString(static_cast<ResourceTimer::Counter>(counter)); return env->NewStringUTF(s); } static int NativeEnableTimers(JNIEnv* env, jobject /*clazz*/, jobject config) { ResourceTimer::enable(); env->SetIntField(config, gConfigOffsets.maxTimer, ResourceTimer::counterSize); env->SetIntField(config, gConfigOffsets.maxBuckets, 4); // Number of ints in PValues env->SetIntField(config, gConfigOffsets.maxLargest, ResourceTimer::Timer::MaxLargest); jclass str = env->FindClass("java/lang/String"); jstring empty = counterName(env, 0); jobjectArray timers = env->NewObjectArray(ResourceTimer::counterSize, str, empty); for (int i = 0; i < ResourceTimer::counterSize; i++) { env->SetObjectArrayElement(timers, i, counterName(env, i)); } env->SetObjectField(config, gConfigOffsets.timers, timers); return 0; } // ---------------------------------------------------------------------------- // JNI registration. static const JNINativeMethod gResourceTimerMethods[] = { {"nativeEnableTimers", "(Landroid/content/res/ResourceTimer$Config;)I", (void *) NativeEnableTimers}, {"nativeGetTimers", "([Landroid/content/res/ResourceTimer$Timer;Z)I", (void *) NativeGetTimers}, }; int register_android_content_res_ResourceTimer(JNIEnv* env) { jclass config = FindClassOrDie(env, "android/content/res/ResourceTimer$Config"); gConfigOffsets.maxTimer = GetFieldIDOrDie(env, config, "maxTimer", "I"); gConfigOffsets.maxBuckets = GetFieldIDOrDie(env, config, "maxBuckets", "I"); gConfigOffsets.maxLargest = GetFieldIDOrDie(env, config, "maxLargest", "I"); gConfigOffsets.timers = GetFieldIDOrDie(env, config, "timers", "[Ljava/lang/String;"); jclass timers = FindClassOrDie(env, "android/content/res/ResourceTimer$Timer"); gTimerOffsets.count = GetFieldIDOrDie(env, timers, "count", "I"); gTimerOffsets.total = GetFieldIDOrDie(env, timers, "total", "J"); gTimerOffsets.mintime = GetFieldIDOrDie(env, timers, "mintime", "I"); gTimerOffsets.maxtime = GetFieldIDOrDie(env, timers, "maxtime", "I"); gTimerOffsets.largest = GetFieldIDOrDie(env, timers, "largest", "[I"); gTimerOffsets.percentile = GetFieldIDOrDie(env, timers, "percentile", "[I"); return RegisterMethodsOrDie(env, "android/content/res/ResourceTimer", gResourceTimerMethods, NELEM(gResourceTimerMethods)); } }; // namespace android
libs/androidfw/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ cc_library { "misc.cpp", "ObbFile.cpp", "PosixUtils.cpp", "ResourceTimer.cpp", "ResourceTypes.cpp", "ResourceUtils.cpp", "StreamingZipInflater.cpp", Loading Loading @@ -173,6 +174,7 @@ cc_test { "tests/Idmap_test.cpp", "tests/LoadedArsc_test.cpp", "tests/Locale_test.cpp", "tests/ResourceTimer_test.cpp", "tests/ResourceUtils_test.cpp", "tests/ResTable_test.cpp", "tests/Split_test.cpp", Loading