Merge "Describe image loads in Traces" into tm-qpr-dev

This commit is contained in:
Jernej Virag
2022-06-27 09:29:16 +00:00
committed by Android (Google) Code Review

View File

@@ -40,6 +40,7 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable; import android.graphics.drawable.NinePatchDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Trace;
import android.system.ErrnoException; import android.system.ErrnoException;
import android.system.Os; import android.system.Os;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
@@ -223,13 +224,21 @@ public final class ImageDecoder implements AutoCloseable {
public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException { public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
return nCreate(mData, mOffset, mLength, preferAnimation, this); return nCreate(mData, mOffset, mLength, preferAnimation, this);
} }
@Override
public String toString() {
return "ByteArraySource{len=" + mLength + "}";
}
} }
private static class ByteBufferSource extends Source { private static class ByteBufferSource extends Source {
ByteBufferSource(@NonNull ByteBuffer buffer) { ByteBufferSource(@NonNull ByteBuffer buffer) {
mBuffer = buffer; mBuffer = buffer;
mLength = mBuffer.limit() - mBuffer.position();
} }
private final ByteBuffer mBuffer; private final ByteBuffer mBuffer;
private final int mLength;
@Override @Override
public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException { public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
@@ -241,6 +250,11 @@ public final class ImageDecoder implements AutoCloseable {
ByteBuffer buffer = mBuffer.slice(); ByteBuffer buffer = mBuffer.slice();
return nCreate(buffer, buffer.position(), buffer.limit(), preferAnimation, this); return nCreate(buffer, buffer.position(), buffer.limit(), preferAnimation, this);
} }
@Override
public String toString() {
return "ByteBufferSource{len=" + mLength + "}";
}
} }
private static class ContentResolverSource extends Source { private static class ContentResolverSource extends Source {
@@ -285,6 +299,16 @@ public final class ImageDecoder implements AutoCloseable {
return createFromAssetFileDescriptor(assetFd, preferAnimation, this); return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
} }
@Override
public String toString() {
String uri = mUri.toString();
if (uri.length() > 90) {
// We want to keep the Uri usable - usually the authority and the end is important.
uri = uri.substring(0, 80) + ".." + uri.substring(uri.length() - 10);
}
return "ContentResolverSource{uri=" + uri + "}";
}
} }
@NonNull @NonNull
@@ -399,6 +423,11 @@ public final class ImageDecoder implements AutoCloseable {
return createFromStream(is, false, preferAnimation, this); return createFromStream(is, false, preferAnimation, this);
} }
} }
@Override
public String toString() {
return "InputStream{s=" + mInputStream + "}";
}
} }
/** /**
@@ -444,6 +473,11 @@ public final class ImageDecoder implements AutoCloseable {
return createFromAsset(ais, preferAnimation, this); return createFromAsset(ais, preferAnimation, this);
} }
} }
@Override
public String toString() {
return "AssetInputStream{s=" + mAssetInputStream + "}";
}
} }
private static class ResourceSource extends Source { private static class ResourceSource extends Source {
@@ -485,6 +519,17 @@ public final class ImageDecoder implements AutoCloseable {
return createFromAsset((AssetInputStream) is, preferAnimation, this); return createFromAsset((AssetInputStream) is, preferAnimation, this);
} }
@Override
public String toString() {
// Try to return a human-readable name for debugging purposes.
try {
return "Resource{name=" + mResources.getResourceName(mResId) + "}";
} catch (Resources.NotFoundException e) {
// It's ok if we don't find it, fall back to ID.
}
return "Resource{id=" + mResId + "}";
}
} }
/** /**
@@ -521,6 +566,11 @@ public final class ImageDecoder implements AutoCloseable {
InputStream is = mAssets.open(mFileName); InputStream is = mAssets.open(mFileName);
return createFromAsset((AssetInputStream) is, preferAnimation, this); return createFromAsset((AssetInputStream) is, preferAnimation, this);
} }
@Override
public String toString() {
return "AssetSource{file=" + mFileName + "}";
}
} }
private static class FileSource extends Source { private static class FileSource extends Source {
@@ -534,6 +584,11 @@ public final class ImageDecoder implements AutoCloseable {
public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException { public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
return createFromFile(mFile, preferAnimation, this); return createFromFile(mFile, preferAnimation, this);
} }
@Override
public String toString() {
return "FileSource{file=" + mFile + "}";
}
} }
private static class CallableSource extends Source { private static class CallableSource extends Source {
@@ -557,6 +612,11 @@ public final class ImageDecoder implements AutoCloseable {
} }
return createFromAssetFileDescriptor(assetFd, preferAnimation, this); return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
} }
@Override
public String toString() {
return "CallableSource{obj=" + mCallable.toString() + "}";
}
} }
/** /**
@@ -1763,20 +1823,21 @@ public final class ImageDecoder implements AutoCloseable {
@NonNull @NonNull
private static Drawable decodeDrawableImpl(@NonNull Source src, private static Drawable decodeDrawableImpl(@NonNull Source src,
@Nullable OnHeaderDecodedListener listener) throws IOException { @Nullable OnHeaderDecodedListener listener) throws IOException {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ImageDecoder#decodeDrawable");
try (ImageDecoder decoder = src.createImageDecoder(true /*preferAnimation*/)) { try (ImageDecoder decoder = src.createImageDecoder(true /*preferAnimation*/)) {
decoder.mSource = src; decoder.mSource = src;
decoder.callHeaderDecoded(listener, src); decoder.callHeaderDecoded(listener, src);
try (ImageDecoderSourceTrace unused = new ImageDecoderSourceTrace(decoder)) {
if (decoder.mUnpremultipliedRequired) { if (decoder.mUnpremultipliedRequired) {
// Though this could be supported (ignored) for opaque images, // Though this could be supported (ignored) for opaque images,
// it seems better to always report this error. // it seems better to always report this error.
throw new IllegalStateException("Cannot decode a Drawable " + throw new IllegalStateException(
"with unpremultiplied pixels!"); "Cannot decode a Drawable with unpremultiplied pixels!");
} }
if (decoder.mMutable) { if (decoder.mMutable) {
throw new IllegalStateException("Cannot decode a mutable " + throw new IllegalStateException("Cannot decode a mutable Drawable!");
"Drawable!");
} }
// this call potentially manipulates the decoder so it must be performed prior to // this call potentially manipulates the decoder so it must be performed prior to
@@ -1785,8 +1846,7 @@ public final class ImageDecoder implements AutoCloseable {
if (decoder.mAnimated) { if (decoder.mAnimated) {
// AnimatedImageDrawable calls postProcessAndRelease only if // AnimatedImageDrawable calls postProcessAndRelease only if
// mPostProcessor exists. // mPostProcessor exists.
ImageDecoder postProcessPtr = decoder.mPostProcessor == null ? ImageDecoder postProcessPtr = decoder.mPostProcessor == null ? null : decoder;
null : decoder;
decoder.checkState(true); decoder.checkState(true);
Drawable d = new AnimatedImageDrawable(decoder.mNativePtr, Drawable d = new AnimatedImageDrawable(decoder.mNativePtr,
postProcessPtr, decoder.mDesiredWidth, postProcessPtr, decoder.mDesiredWidth,
@@ -1819,6 +1879,10 @@ public final class ImageDecoder implements AutoCloseable {
return new BitmapDrawable(res, bm); return new BitmapDrawable(res, bm);
} }
} finally {
// Close the ImageDecoder#decode trace.
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
} }
/** /**
@@ -1867,10 +1931,11 @@ public final class ImageDecoder implements AutoCloseable {
@NonNull @NonNull
private static Bitmap decodeBitmapImpl(@NonNull Source src, private static Bitmap decodeBitmapImpl(@NonNull Source src,
@Nullable OnHeaderDecodedListener listener) throws IOException { @Nullable OnHeaderDecodedListener listener) throws IOException {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ImageDecoder#decodeBitmap");
try (ImageDecoder decoder = src.createImageDecoder(false /*preferAnimation*/)) { try (ImageDecoder decoder = src.createImageDecoder(false /*preferAnimation*/)) {
decoder.mSource = src; decoder.mSource = src;
decoder.callHeaderDecoded(listener, src); decoder.callHeaderDecoded(listener, src);
try (ImageDecoderSourceTrace unused = new ImageDecoderSourceTrace(decoder)) {
// this call potentially manipulates the decoder so it must be performed prior to // this call potentially manipulates the decoder so it must be performed prior to
// decoding the bitmap // decoding the bitmap
final int srcDensity = decoder.computeDensity(src); final int srcDensity = decoder.computeDensity(src);
@@ -1884,9 +1949,33 @@ public final class ImageDecoder implements AutoCloseable {
nGetPadding(decoder.mNativePtr, padding); nGetPadding(decoder.mNativePtr, padding);
} }
} }
return bm; return bm;
} }
} finally {
// Close the ImageDecoder#decode trace.
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
/**
* This describes the decoder in traces to ease debugging. It has to be called after
* header has been decoded and width/height have been populated. It should be used
* inside a try-with-resources call to automatically complete the trace.
*/
private static AutoCloseable traceDecoderSource(ImageDecoder decoder) {
final boolean resourceTracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_RESOURCES);
if (resourceTracingEnabled) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, describeDecoderForTrace(decoder));
}
return new AutoCloseable() {
@Override
public void close() throws Exception {
if (resourceTracingEnabled) {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
};
} }
// This method may modify the decoder so it must be called prior to performing the decode // This method may modify the decoder so it must be called prior to performing the decode
@@ -1994,6 +2083,66 @@ public final class ImageDecoder implements AutoCloseable {
} }
} }
/**
* Returns a short string describing what passed ImageDecoder is loading -
* it reports image dimensions, desired dimensions (if any) and source resource.
*
* The string appears in perf traces to simplify search for slow or memory intensive
* image loads.
*
* Example: ID#w=300;h=250;dw=150;dh=150;src=Resource{name=@resource}
*
* @hide
*/
private static String describeDecoderForTrace(@NonNull ImageDecoder decoder) {
StringBuilder builder = new StringBuilder();
// Source dimensions
builder.append("ID#w=");
builder.append(decoder.mWidth);
builder.append(";h=");
builder.append(decoder.mHeight);
// Desired dimensions (if present)
if (decoder.mDesiredWidth != decoder.mWidth
|| decoder.mDesiredHeight != decoder.mHeight) {
builder.append(";dw=");
builder.append(decoder.mDesiredWidth);
builder.append(";dh=");
builder.append(decoder.mDesiredHeight);
}
// Source description
builder.append(";src=");
builder.append(decoder.mSource);
return builder.toString();
}
/**
* Records a trace with information about the source being decoded - dimensions,
* desired dimensions and source information.
*
* It significantly eases debugging of slow resource loads on main thread and
* possible large memory consumers.
*
* @hide
*/
private static final class ImageDecoderSourceTrace implements AutoCloseable {
private final boolean mResourceTracingEnabled;
ImageDecoderSourceTrace(ImageDecoder decoder) {
mResourceTracingEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_RESOURCES);
if (mResourceTracingEnabled) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, describeDecoderForTrace(decoder));
}
}
@Override
public void close() {
if (mResourceTracingEnabled) {
Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
}
}
}
private static native ImageDecoder nCreate(long asset, private static native ImageDecoder nCreate(long asset,
boolean preferAnimation, Source src) throws IOException; boolean preferAnimation, Source src) throws IOException;
private static native ImageDecoder nCreate(ByteBuffer buffer, int position, int limit, private static native ImageDecoder nCreate(ByteBuffer buffer, int position, int limit,