Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add perf stats collection for ApkAssets, AssetManager, and Resources. #9010

Open
wants to merge 1 commit into
base: google
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -23,6 +23,7 @@
import org.robolectric.res.android.Registries;
import org.robolectric.res.android.ResXMLTree;
import org.robolectric.shadows.ShadowApkAssets.Picker;
import org.robolectric.util.PerfStatsCollector;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.Direct;
Expand Down Expand Up @@ -96,15 +97,23 @@ protected static ApkAssets loadFromPath(String path, boolean system) throws IOEx
path = RuntimeEnvironment.getAndroidFrameworkJarPath().toString();
}

return reflector(_ApkAssets_.class).loadFromPath(path, system);
String finalPath = path;
return PerfStatsCollector.getInstance()
.<ApkAssets, IOException>measure(
"ApkAssets-loadFromPath",
() -> reflector(_ApkAssets_.class).loadFromPath(finalPath, system));
}

@Implementation(minSdk = R)
protected static ApkAssets loadFromPath(String path, int flags) throws IOException {
if (FRAMEWORK_APK_PATH.equals(path)) {
path = RuntimeEnvironment.getAndroidFrameworkJarPath().toString();
}
return reflector(_ApkAssets_.class).loadFromPath(path, flags);
String finalPath = path;
return PerfStatsCollector.getInstance()
.<ApkAssets, IOException>measure(
"ApkAssets-loadFromPath",
() -> reflector(_ApkAssets_.class).loadFromPath(finalPath, flags));
}

// static jlong NativeLoad(JNIEnv* env, jclass /*clazz*/, jstring java_path, jboolean system,
Expand All @@ -113,39 +122,45 @@ protected static ApkAssets loadFromPath(String path, int flags) throws IOExcepti
@Implementation(maxSdk = Q)
protected static long nativeLoad(
String path, boolean system, boolean forceSharedLib, boolean overlay) throws IOException {
if (path == null) {
return 0;
}

long cachedApkAssetsPtr = ApkAssetsCache.get(path, system, RuntimeEnvironment.getApiLevel());
if (cachedApkAssetsPtr != -1) {
return cachedApkAssetsPtr;
}

ATRACE_NAME(String.format("LoadApkAssets(%s)", path));

CppApkAssets apk_assets;
try {
if (overlay) {
apk_assets = CppApkAssets.LoadOverlay(path, system);
} else if (forceSharedLib) {
apk_assets = CppApkAssets.LoadAsSharedLibrary(path, system);
} else {
apk_assets = CppApkAssets.Load(path, system);
}
} catch (OutOfMemoryError e) {
OutOfMemoryError outOfMemoryError = new OutOfMemoryError("Failed to load " + path);
outOfMemoryError.initCause(e);
throw outOfMemoryError;
}

if (apk_assets == null) {
String error_msg = String.format("Failed to load asset path %s", path);
throw new IOException(error_msg);
}
long ptr = Registries.NATIVE_APK_ASSETS_REGISTRY.register(apk_assets);
ApkAssetsCache.put(path, system, RuntimeEnvironment.getApiLevel(), ptr);
return ptr;
return PerfStatsCollector.getInstance()
.<Long, IOException>measure(
"ApkAssets-nativeLoad",
() -> {
if (path == null) {
return 0L;
}

long cachedApkAssetsPtr =
ApkAssetsCache.get(path, system, RuntimeEnvironment.getApiLevel());
if (cachedApkAssetsPtr != -1) {
return cachedApkAssetsPtr;
}

ATRACE_NAME(String.format("LoadApkAssets(%s)", path));

CppApkAssets apk_assets;
try {
if (overlay) {
apk_assets = CppApkAssets.LoadOverlay(path, system);
} else if (forceSharedLib) {
apk_assets = CppApkAssets.LoadAsSharedLibrary(path, system);
} else {
apk_assets = CppApkAssets.Load(path, system);
}
} catch (OutOfMemoryError e) {
OutOfMemoryError outOfMemoryError = new OutOfMemoryError("Failed to load " + path);
outOfMemoryError.initCause(e);
throw outOfMemoryError;
}

if (apk_assets == null) {
String error_msg = String.format("Failed to load asset path %s", path);
throw new IOException(error_msg);
}
long ptr = Registries.NATIVE_APK_ASSETS_REGISTRY.register(apk_assets);
ApkAssetsCache.put(path, system, RuntimeEnvironment.getApiLevel(), ptr);
return ptr;
});
}

@Implementation(minSdk = R)
Expand Down Expand Up @@ -208,13 +223,18 @@ protected static Object nativeLoadFd(
Object propertyFlags,
Object assetsProvider)
throws IOException {
CppApkAssets apkAssets = CppApkAssets.loadArscFromFd((FileDescriptor) fileDescriptor);
if (apkAssets == null) {
String errorMessage =
String.format("Failed to load from the file descriptor %s", fileDescriptor);
throw new IOException(errorMessage);
}
return Registries.NATIVE_APK_ASSETS_REGISTRY.register(apkAssets);
return PerfStatsCollector.getInstance()
.<Object, IOException>measure(
"ApkAssets-nativeLoadFd",
() -> {
CppApkAssets apkAssets = CppApkAssets.loadArscFromFd((FileDescriptor) fileDescriptor);
if (apkAssets == null) {
String errorMessage =
String.format("Failed to load from the file descriptor %s", fileDescriptor);
throw new IOException(errorMessage);
}
return Registries.NATIVE_APK_ASSETS_REGISTRY.register(apkAssets);
});
}

// static jstring NativeGetAssetPath(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
Expand All @@ -241,28 +261,32 @@ protected static boolean nativeIsUpToDate(long ptr) {
// static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) {
@Implementation
protected static long nativeOpenXml(long ptr, String file_name) throws FileNotFoundException {
String path_utf8 = file_name;
if (path_utf8 == null) {
return 0;
}

CppApkAssets apk_assets =
Registries.NATIVE_APK_ASSETS_REGISTRY.getNativeObject(ptr);
Asset asset = apk_assets.Open(path_utf8, Asset.AccessMode.ACCESS_RANDOM);
if (asset == null) {
throw new FileNotFoundException(path_utf8);
}

// DynamicRefTable is only needed when looking up resource references. Opening an XML file
// directly from an ApkAssets has no notion of proper resource references.
ResXMLTree xml_tree = new ResXMLTree(null); // util.make_unique<ResXMLTree>(nullptr /*dynamicRefTable*/);
int err = xml_tree.setTo(asset.getBuffer(true), (int) asset.getLength(), true);
// asset.reset();

if (err != NO_ERROR) {
throw new FileNotFoundException("Corrupt XML binary file");
}
return Registries.NATIVE_RES_XML_TREES.register(xml_tree); // reinterpret_cast<jlong>(xml_tree.release());
return PerfStatsCollector.getInstance()
.<Long, FileNotFoundException>measure(
"ApkAssets-nativeOpenXml",
() -> {
String path_utf8 = file_name;
if (path_utf8 == null) {
return 0L;
}

CppApkAssets apk_assets = Registries.NATIVE_APK_ASSETS_REGISTRY.getNativeObject(ptr);
Asset asset = apk_assets.Open(path_utf8, Asset.AccessMode.ACCESS_RANDOM);
if (asset == null) {
throw new FileNotFoundException(path_utf8);
}

// DynamicRefTable is only needed when looking up resource references. Opening an XML
// file directly from an ApkAssets has no notion of proper resource references.
ResXMLTree xml_tree = new ResXMLTree(null); // util.make_unique<ResXMLTree>(nullptr /*dynamicRefTable*/);
int err = xml_tree.setTo(asset.getBuffer(true), (int) asset.getLength(), true);
// asset.reset();

if (err != NO_ERROR) {
throw new FileNotFoundException("Corrupt XML binary file");
}
return Registries.NATIVE_RES_XML_TREES.register(xml_tree); // reinterpret_cast<jlong>(xml_tree.release());
});
}

// // JNI registration.
Expand Down
Expand Up @@ -64,6 +64,7 @@
import org.robolectric.res.android.String8;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.ShadowAssetManager.Picker;
import org.robolectric.util.PerfStatsCollector;

// native method impls transliterated from
// https://android.googlesource.com/platform/frameworks/base/+/android-9.0.0_r12/core/jni/android_util_AssetManager.cpp
Expand Down Expand Up @@ -718,12 +719,23 @@ protected static void applyStyle(long themeToken, int defStyleAttr, int defStyle
@HiddenApi @Implementation(minSdk = LOLLIPOP, maxSdk = N_MR1)
protected static void applyStyle(long themeToken, int defStyleAttr, int defStyleRes,
long xmlParserToken, int[] attrs, int[] outValues, int[] outIndices) {
ResTableTheme theme = Registries.NATIVE_THEME_REGISTRY.getNativeObject(themeToken);
ResXMLParser xmlParser = xmlParserToken == 0
? null
: Registries.NATIVE_RES_XML_PARSERS.getNativeObject(xmlParserToken);
AttributeResolution.ApplyStyle(theme, xmlParser, defStyleAttr, defStyleRes,
attrs, attrs.length, outValues, outIndices);
PerfStatsCollector.getInstance()
.measure("AssetManager-applyStyle",
() -> {
ResTableTheme theme = Registries.NATIVE_THEME_REGISTRY.getNativeObject(themeToken);
ResXMLParser xmlParser = xmlParserToken == 0
? null
: Registries.NATIVE_RES_XML_PARSERS.getNativeObject(xmlParserToken);
AttributeResolution.ApplyStyle(
theme,
xmlParser,
defStyleAttr,
defStyleRes,
attrs,
attrs.length,
outValues,
outIndices);
});
}

@Implementation @HiddenApi
Expand Down