Status Update
Comments
aa...@google.com <aa...@google.com>
je...@google.com <je...@google.com>
ja...@gmail.com <ja...@gmail.com> #2
This is still broken in 8.1.0-beta02.
ja...@gmail.com <ja...@gmail.com> #3
Here's where Lollipop is failing:
void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type,
bool no_guarantee_of_dex_cache_entry,
mirror::Class* referrer_class,
mirror::ArtMethod* method,
int* stats_flags,
MethodReference* target_method,
uintptr_t* direct_code,
uintptr_t* direct_method) {
// For direct and static methods compute possible direct_code and direct_method values, ie
// an address for the Method* being invoked and an address of the code for that Method*.
// For interface calls compute a value for direct_method that is the interface method being
// invoked, so this can be passed to the out-of-line runtime support code.
*direct_code = 0;
*direct_method = 0;
bool use_dex_cache = false;
const bool compiling_boot = Runtime::Current()->GetHeap()->IsCompilingBoot();
// TODO This is somewhat hacky. We should refactor all of this invoke codepath.
const bool force_relocations = (compiling_boot ||
GetCompilerOptions().GetIncludePatchInformation());
if (compiler_->IsPortable()) {
if (sharp_type != kStatic && sharp_type != kDirect) {
return;
}
use_dex_cache = true;
} else {
if (sharp_type != kStatic && sharp_type != kDirect) {
return;
}
// TODO: support patching on all architectures.
use_dex_cache = force_relocations && !support_boot_image_fixup_;
}
bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr);
if (!use_dex_cache) {
if (!method_code_in_boot) {
use_dex_cache = true;
} else {
bool has_clinit_trampoline =
method->IsStatic() && !method->GetDeclaringClass()->IsInitialized();
if (has_clinit_trampoline && (method->GetDeclaringClass() != referrer_class)) {
// Ensure we run the clinit trampoline unless we are invoking a static method in the same
// class.
use_dex_cache = true;
}
}
}
if (method_code_in_boot) {
*stats_flags |= kFlagDirectCallToBoot | kFlagDirectMethodToBoot;
}
if (!use_dex_cache && force_relocations) {
if (!IsImage() || !IsImageClass(method->GetDeclaringClassDescriptor())) {
// We can only branch directly to Methods that are resolved in the DexCache.
// Otherwise we won't invoke the resolution trampoline.
use_dex_cache = true;
}
}
// The method is defined not within this dex file. We need a dex cache slot within the current
// dex file or direct pointers.
bool must_use_direct_pointers = false;
if (target_method->dex_file == method->GetDeclaringClass()->GetDexCache()->GetDexFile()) {
target_method->dex_method_index = method->GetDexMethodIndex();
} else {
if (no_guarantee_of_dex_cache_entry) {
StackHandleScope<1> hs(Thread::Current());
MethodHelper mh(hs.NewHandle(method));
// See if the method is also declared in this dex cache.
uint32_t dex_method_idx = mh.FindDexMethodIndexInOtherDexFile(
*target_method->dex_file, target_method->dex_method_index);
if (dex_method_idx != DexFile::kDexNoIndex) {
target_method->dex_method_index = dex_method_idx;
} else {
if (force_relocations && !use_dex_cache) {
target_method->dex_method_index = method->GetDexMethodIndex();
target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
}
must_use_direct_pointers = true;
}
}
}
if (use_dex_cache) {
if (must_use_direct_pointers) {
// Fail. Test above showed the only safe dispatch was via the dex cache, however, the direct
// pointers are required as the dex cache lacks an appropriate entry.
VLOG(compiler) << "Dex cache devirtualization failed for: " << PrettyMethod(method);
} else {
*type = sharp_type;
}
} else {
bool method_in_image =
Runtime::Current()->GetHeap()->FindSpaceFromObject(method, false)->IsImageSpace();
if (method_in_image || compiling_boot) {
// We know we must be able to get to the method in the image, so use that pointer.
CHECK(!method->IsAbstract());
*type = sharp_type;
*direct_method = force_relocations ? -1 : reinterpret_cast<uintptr_t>(method);
*direct_code = force_relocations ? -1 : compiler_->GetEntryPointOf(method);
target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
target_method->dex_method_index = method->GetDexMethodIndex();
} else if (!must_use_direct_pointers) {
// Set the code and rely on the dex cache for the method.
*type = sharp_type;
if (force_relocations) {
*direct_code = -1;
target_method->dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
target_method->dex_method_index = method->GetDexMethodIndex();
} else {
*direct_code = compiler_->GetEntryPointOf(method);
}
} else {
// Direct pointers were required but none were available.
VLOG(compiler) << "Dex cache devirtualization failed for: " << PrettyMethod(method);
}
}
}
It's that CHECK(!method->IsAbstract());
line that's crashing.
ja...@gmail.com <ja...@gmail.com> #4
See the attached zip. Running this command on those files will reproduce the bug:
/system/bin/dex2oat --dex-file=/data/local/tmp/classes8.dex --dex-location=/data/local/tmp/classes8.dex --dex-file=/data/local/tmp/classes14.dex --dex-location=/data/local/tmp/classes14.dex --oat-file=/dev/null
ja...@gmail.com <ja...@gmail.com> #5
Examining all the invoke-directs from one classes dot dex to another, I came across:
CODE:000049A8 invoke-direct {this}, <void TileService.<init>() imp. @ def_TileService__init@V>
And indeed, removing the TileService from my app makes the issue go away.
Is this some kind of api compatibility tree shaking issue?
ja...@gmail.com <ja...@gmail.com> #6
Perhaps @RequiresApi(Build.VERSION_CODES.N)
is not working as it should in 8.1.0?
bi...@google.com <bi...@google.com> #7
Soren, do you have any ideas on this issue?
ja...@gmail.com <ja...@gmail.com> #8
Interestingly, the TileService constructor stub from classes14.dex isn't present at all when I compile this using 8.0.0. Specifically, the below is present in 8.1.0 but not 8.0.0.
CODE:00000438 # ============================================================================
CODE:00000438 # Method 8 (0x8)
CODE:00000438 word_438: .short 1 # DATA XREF: CODE:00000BB5↓i
CODE:00000438 # Number of registers : 0x1
CODE:0000043A .short 0 # Size of input args (in words) : 0x0
CODE:0000043C .short 1 # Size of output args (in words) : 0x1
CODE:0000043E .short 0 # Number of try_items : 0x0
CODE:00000440 .int 0 # Debug info
CODE:00000444 .int 6 # Size of bytecode (in 16-bit units): 0x6
CODE:00000448 synthetic static void android.service.quicksettings.TileService.<clinit>()
CODE:00000448 new-instance v0, <t: NoClassDefFoundError>
CODE:0000044C invoke-direct {v0}, <void NoClassDefFoundError.<init>() imp. @ _def_NoClassDefFoundError__init_@V>
CODE:00000452 throw v0
CODE:00000452 Method End
CODE:00000452 # ---------------------------------------------------------------------------
sg...@google.com <sg...@google.com> #9
Thank you very much for the report with reproduction and analysis!
I can confirm that I can reproduce this on an Android 5.0 (Lollipop, API level 21) emulator with the DEX files you provided. The issue does not reproduce on Android 5.1 (Lollipop MR1, API level 22).
The class which seems to cause the issue is the synthetic class android.service.quicksettings.TileService
.
.class public synthetic Landroid/service/quicksettings/TileService;
.super Landroid/app/Service;
.method static synthetic constructor <clinit>()V
.locals 1
new-instance v0, Ljava/lang/NoClassDefFoundError;
invoke-direct {v0}, Ljava/lang/NoClassDefFoundError;-><init>()V
throw v0
.end method
This class is an API stub generated to improve runtime performance (see dex2oat
goes away.
.class public synthetic Landroid/service/quicksettings/TileService;
.super Landroid/app/Service;
.method static synthetic constructor <clinit>()V
.locals 1
new-instance v0, Ljava/lang/NoClassDefFoundError;
invoke-direct {v0}, Ljava/lang/NoClassDefFoundError;-><init>()V
throw v0
.end method
# Adding this constructor fixes the issue.
.method public constructor <init>()V
.locals 0
invoke-direct {p0}, Ljava/lang/Object;-><init>()V
return-void
#.end method
Note, that the super constructor is called using invoke-direct {p0}, Ljava/lang/Object;-><init>()V
and not invoke-direct {p0}, Landroid/app/Service;-><init>()V
. This seems to work with dex2oat
.
A simple reproduction with just the class android.service.quicksettings.TileService
and a class A
with the following content is not enough to reproduce the issue.
.class public LA;
.super Landroid/service/quicksettings/TileService;
.method public constructor <init>()V
.locals 0
invoke-direct {p0}, Landroid/service/quicksettings/TileService;-><init>()V
return-void
.end method
ja...@gmail.com <ja...@gmail.com> #10
Is there anyway I can set D8's com.android.tools.r8.disableApiModeling
from AGP/gradle in the meanwhile, or otherwise call disableStubbingOfClasses()
?
bi...@google.com <bi...@google.com> #11
In AGP 8.2, there will be an optional boolean flag called android.enableApiModelingAndGlobalSynthetics
which you can use to disable api modeling. It should be available from 8.2 alpha03.
In AGP 8.1, the cherry-pick is not landed yet so currently there isn't a way to disable api modeling from AGP.
ja...@gmail.com <ja...@gmail.com> #12
Hm, okay. Just trying to find a way to work around this without waiting for the next 8.1.0 to be released in two weeks. I could repack the apk after round-tripping it through baksmali/smali as part of the build, but that seems excessive.
ze...@google.com <ze...@google.com> #13
As a temporary workaround, you should be able to set the system property -Dcom.android.tools.r8.disableApiModeling=1
as a command line option to the gradle build command. That should disable the modeling in the compiler.
ja...@gmail.com <ja...@gmail.com> #14
Thanks. I thought I'd tried that via the gradle file, but it didn't work. Turns out it was just a build cache issue.
So, adding System.setProperty("com.android.tools.r8.disableApiModeling", "1")
to my build.gradle.kts seems to do the trick too.
sg...@google.com <sg...@google.com> #15
After trying to reproduce the issue again I found that the analysis in
.class public LA;
.super Landroid/service/quicksettings/TileService;
.method public constructor <init>()V
.locals 0
invoke-direct {p0}, Landroid/service/quicksettings/TileService;-><init>()V
return-void
.end method
.method public x()V
.locals 3
const/4 v0, 0x0
invoke-super {p0, v0}, Landroid/service/quicksettings/TileService;->onBind(Landroid/content/Intent;)Landroid/os/IBinder;
return-void
.end method
.class public synthetic Landroid/service/quicksettings/TileService;
.super Landroid/app/Service;
.method static synthetic constructor <clinit>()V
.locals 1
new-instance v0, Ljava/lang/NoClassDefFoundError;
invoke-direct {v0}, Ljava/lang/NoClassDefFoundError;-><init>()V
throw v0
.end method
Addng a constructor to android/service/quicksettings/TileService
did not make a difference, but adding an onBind
method did fix/workaround the issue.
.method public onBind(Landroid/content/Intent;)Landroid/os/IBinder;
.locals 2
const/4 p1, 0x0
return-object p1
.end method
I have now been testing on a Nexus 4 with Android 5.0.2, and there I cannot reproduce the issue (yet). So for now this is only known to happen on x86 emulators on 5.0.
ap...@google.com <ap...@google.com> #16
Branch: main
commit 77e3c9ee8166f30ebd471784d37d1f47e8c529ee
Author: Morten Krogh-Jespersen <mkroghj@google.com>
Date: Mon May 15 12:37:19 2023
[ApiModel] Disable stubbing of super classes on L and below
Bug:
Change-Id: I1afd04dcb2134815162f073e186d25d2c61f6fca
M src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelMockRetraceTest.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
ap...@google.com <ap...@google.com> #17
Branch: 8.0
commit 0b06f25340736645e31672e5e98deabb366b9921
Author: Morten Krogh-Jespersen <mkroghj@google.com>
Date: Tue May 16 09:27:51 2023
Version 8.0.47
Bug:
Bug:
Bug:
Bug:
Change-Id: I8ea8c490d6d0e733e300728c77d8feddde9aa312
M src/main/java/com/android/tools/r8/Version.java
ap...@google.com <ap...@google.com> #18
Branch: 8.0
commit a4f37fe81ba59e950f31ceef599b146bb29aa772
Author: Morten Krogh-Jespersen <mkroghj@google.com>
Date: Tue May 16 09:27:33 2023
[ApiModel] Disable stubbing of super classes on L and below
Bug:
Change-Id: I1afd04dcb2134815162f073e186d25d2c61f6fca
M src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
ap...@google.com <ap...@google.com> #19
Branch: 8.1
commit d0af1518cea3186af14296f7eaccae005fa654d0
Author: Morten Krogh-Jespersen <mkroghj@google.com>
Date: Mon May 15 18:02:40 2023
Version 8.1.47
Bug:
Change-Id: If9242211ae882869e74c278816f25dc3d96d3b4f
M src/main/java/com/android/tools/r8/Version.java
ap...@google.com <ap...@google.com> #20
Branch: 8.1
commit 4d3d76666187e933b3ef943ff7301bb90a4f37fb
Author: Morten Krogh-Jespersen <mkroghj@google.com>
Date: Mon May 15 18:01:59 2023
[ApiModel] Disable stubbing of super classes on L and below
Bug:
Change-Id: I1afd04dcb2134815162f073e186d25d2c61f6fca
M src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelD8GradleSetupTest.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelMockRetraceTest.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
ap...@google.com <ap...@google.com> #21
Branch: 8.0
commit a4f37fe81ba59e950f31ceef599b146bb29aa772
Author: Morten Krogh-Jespersen <mkroghj@google.com>
Date: Tue May 16 09:27:33 2023
[ApiModel] Disable stubbing of super classes on L and below
Bug:
Change-Id: I1afd04dcb2134815162f073e186d25d2c61f6fca
M src/main/java/com/android/tools/r8/androidapi/ApiReferenceStubber.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelMockInheritedClassTest.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelMockSuperChainClassTest.java
M src/test/java/com/android/tools/r8/apimodel/ApiModelingTestHelper.java
Description
When I compile my app with agp 8.0.0, everything is fine. When I compile it with 8.1.0, and then install it onto the API 21 emulator, the installation fails. Logcat reveals this catastrophe: