
Renamed chromium_code.flags to shared_with_cronet.flags, this makes it clearer that the configs in this file are shared and any configs that do not need to be shared should go in chromium_apk.flags. Bug: 338060678 Change-Id: I108e40079859f5d6a553a5c4a811878ca0cb1f45 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5502569 Auto-Submit: Peter Wen <wnwen@chromium.org> Commit-Queue: Etienne Dechamps <edechamps@google.com> Reviewed-by: Etienne Dechamps <edechamps@google.com> Reviewed-by: Andrew Grieve <agrieve@chromium.org> Cr-Commit-Position: refs/heads/main@{#1294888}
150 lines
6.5 KiB
Markdown
150 lines
6.5 KiB
Markdown
# Optimizing Java Code
|
|
|
|
This doc describes how Java code is optimized in Chrome on Android and how to
|
|
deal with issues caused by the optimizer. For tips on how to write optimized
|
|
code, see [//docs/speed/binary_size/optimization_advice.md#optimizing-java-code](/docs/speed/binary_size/optimization_advice.md#optimizing-java-code).
|
|
|
|
[TOC]
|
|
|
|
## ProGuard vs R8
|
|
|
|
ProGuard is the original open-source tool used by many Android applications to
|
|
perform whole-program bytecode optimization. [R8](https://r8.googlesource.com/r8),
|
|
is a re-implementation that is used by Chrome (and the default for Android Studio).
|
|
The terms "ProGuard" and "R8" are used interchangeably within Chromium but
|
|
generally they're meant to refer to the tool providing Java code optimizations.
|
|
|
|
## What does ProGuard do?
|
|
|
|
1. Shrinking: ProGuard will remove unused code. This is especially useful
|
|
when depending on third party libraries where only a few functions are used.
|
|
|
|
2. Obfuscation: ProGuard will rename classes/fields/methods to use shorter
|
|
names. Obfuscation is used for minification purposes only (not security).
|
|
|
|
3. Optimization: ProGuard performs a series of optimizations to shrink code
|
|
further through various approaches (ex. inlining, outlining, class merging,
|
|
etc).
|
|
|
|
## Build Process
|
|
|
|
ProGuard is enabled only for release builds of Chrome because it is a slow build
|
|
step and breaks Java debugging. It can also be enabled manually via the GN arg:
|
|
```is_java_debug = false```
|
|
|
|
### ProGuard configuration files
|
|
|
|
Most GN Java targets can specify ProGuard configuration files by setting the
|
|
`proguard_configs` variable. [//base/android/proguard](/base/android/proguard)
|
|
contains common flags shared by most Chrome applications.
|
|
|
|
### GN build rules
|
|
|
|
When `is_java_debug = false` and a target has enabled ProGuard, the `proguard`
|
|
step generates the `.dex` files for the application. The `proguard` step takes
|
|
as input a list of `.jar` files, runs R8/ProGuard on those `.jar` files, and
|
|
produces the final `.dex` file(s) that will be packaged into your `.apk`
|
|
|
|
## Deobfuscation
|
|
|
|
Obfuscation can be turned off for local builds while leaving ProGuard enabled
|
|
by setting `enable_proguard_obfuscation = false` in GN args.
|
|
|
|
There are two main methods for deobfuscating Java stack traces locally:
|
|
1. Using APK wrapper scripts (stacks are automatically deobfuscated)
|
|
* `$OUT/bin/chrome_public_apk logcat` # Run adb logcat
|
|
* `$OUT/bin/chrome_public_apk run` # Launch chrome and run adb logcat
|
|
|
|
2. Using `java_deobfuscate`
|
|
* build/android/stacktrace/java_deobfuscate.py $OUT/apks/ChromePublic.apk.mapping < logcat.txt`
|
|
* ProGuard mapping files are located beside APKs (ex.
|
|
`$OUT/apks/ChromePublic.apk` and `$OUT/apks/ChromePublic.apk.mapping`)
|
|
|
|
Helpful links for deobfuscation:
|
|
|
|
* [Internal bits about how mapping files are archived][proguard-site]
|
|
* [More detailed deobfuscation instructions][proguard-doc]
|
|
* [Script for deobfuscating official builds][deob-official]
|
|
|
|
[proguard-site]: http://goto.google.com/chrome-android-proguard
|
|
[proguard-doc]: http://goto.google.com/chromejavadeobfuscation
|
|
[deob-official]: http://goto.google.com/chrome-android-official-deobfuscation
|
|
|
|
## Debugging common failures
|
|
|
|
ProGuard failures are often hard to debug. This section aims to outline some of
|
|
the more common errors.
|
|
|
|
### Classes expected to be discarded
|
|
|
|
The `-checkdiscard` directive can be used to ensure that certain items are
|
|
removed by ProGuard. A common use of `-checkdiscard` it to ensure that ProGuard
|
|
optimizations do not regress in their ability to remove code, such as code
|
|
intended only for debug builds, or generated JNI classes that are meant to be
|
|
zero-overhead abstractions. Annotating a class with
|
|
[@CheckDiscard][checkdiscard] will add a `-checkdiscard` rule automatically.
|
|
|
|
[checkdiscard]: /build/android/java/src/org/chromium/build/annotations/CheckDiscard.java
|
|
|
|
```
|
|
Item void org.chromium.base.library_loader.LibraryPrefetcherJni.<init>() was not discarded.
|
|
void org.chromium.base.library_loader.LibraryPrefetcherJni.<init>()
|
|
|- is invoked from:
|
|
| void org.chromium.base.library_loader.LibraryPrefetcher.asyncPrefetchLibrariesToMemory()
|
|
... more code path lines
|
|
|- is referenced in keep rule:
|
|
| obj/chrome/android/chrome_public_apk/chrome_public_apk.resources.proguard.txt:104:1
|
|
|
|
Error: Discard checks failed.
|
|
```
|
|
|
|
Things to check
|
|
* Did you add code that is referenced by code path in the error message?
|
|
* If so, check the original class for why the `CheckDiscard` was added
|
|
originally and verify that the reason is still valid with your change (may
|
|
need git blame to do this).
|
|
* Try the extra debugging steps listed in the JNI section below.
|
|
|
|
### JNI wrapper classes not discarded
|
|
|
|
Proxy native methods (`@NativeMethods`) use generated wrapper classes to provide
|
|
access to native methods. We rely on ProGuard to fully optimize the generated
|
|
code so that native methods aren't a source of binary size bloat. The above
|
|
error message is an example when a JNI wrapper class wasn't discarded (notice
|
|
the name of the offending class).
|
|
* The ProGuard rule pointed to in the error message isn't helpful (just tells
|
|
us a code path that reaches the not-inlined class).
|
|
* Common causes:
|
|
* Caching the result of `ClassNameJni.get()` in a member variable.
|
|
* Passing a native wrapper method reference instead of using a lambda (i.e.
|
|
`Jni.get()::methodName` vs. `() -> Jni.get.methodName()`).
|
|
* For more debugging info, add to `base/android/proguard/chromium_apk.flags`:
|
|
```
|
|
-whyareyounotinlining class org.chromium.base.library_loader.LibraryPrefetcherJni {
|
|
<init>();
|
|
}
|
|
```
|
|
|
|
### Duplicate classes
|
|
|
|
```
|
|
Type YourClassName is defined multiple times: obj/jar1.jar:YourClassName.class, obj/jar2.jar:YourClassName.class
|
|
```
|
|
|
|
Common causes:
|
|
* Multiple targets with overlapping `srcjar_deps`:
|
|
* Each `.srcjar` can only be depended on by a single Java target in any
|
|
given APK target. `srcjar_deps` are just a convenient way to depend on
|
|
generated files and should be treated like source files rather than
|
|
`deps`.
|
|
* Solution: Wrap the `srcjar` in an `android_library` target or have only a
|
|
single Java target depend on the `srcjar` and have other targets depend on
|
|
the containing Java target instead.
|
|
* Accidentally enabling APK level generated files for multiple targets that
|
|
share generated code (ex. Trichrome or App Bundles):
|
|
* Solution: Make sure the generated file is only added once.
|
|
|
|
Debugging ProGuard failures isn't easy, so please message java@chromium.org
|
|
or [file a bug](crbug.com/new) with `component=Build os=Android` for any
|
|
issues related to Java code optimization.
|