xref: /aosp_15_r20/external/cronet/build/android/docs/java_optimization.md (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1# Optimizing Java Code
2
3This doc describes how Java code is optimized in Chrome on Android and how to
4deal with issues caused by the optimizer. For tips on how to write optimized
5code, see [//docs/speed/binary_size/optimization_advice.md#optimizing-java-code](/docs/speed/binary_size/optimization_advice.md#optimizing-java-code).
6
7[TOC]
8
9## ProGuard vs R8
10
11ProGuard is the original open-source tool used by many Android applications to
12perform whole-program bytecode optimization. [R8](https://r8.googlesource.com/r8),
13is a re-implementation that is used by Chrome (and the default for Android Studio).
14The terms "ProGuard" and "R8" are used interchangeably within Chromium but
15generally they're meant to refer to the tool providing Java code optimizations.
16
17## What does ProGuard do?
18
191. Shrinking: ProGuard will remove unused code. This is especially useful
20   when depending on third party libraries where only a few functions are used.
21
222. Obfuscation: ProGuard will rename classes/fields/methods to use shorter
23   names. Obfuscation is used for minification purposes only (not security).
24
253. Optimization: ProGuard performs a series of optimizations to shrink code
26   further through various approaches (ex. inlining, outlining, class merging,
27   etc).
28
29## Build Process
30
31ProGuard is enabled only for release builds of Chrome because it is a slow build
32step and breaks Java debugging. It can also be enabled manually via the GN arg:
33```is_java_debug = false```
34
35### ProGuard configuration files
36
37Most GN Java targets can specify ProGuard configuration files by setting the
38`proguard_configs` variable. [//base/android/proguard](/base/android/proguard)
39contains common flags shared by most Chrome applications.
40
41### GN build rules
42
43When `is_java_debug = false` and a target has enabled ProGuard, the `proguard`
44step generates the `.dex` files for the application. The `proguard` step takes
45as input a list of `.jar` files, runs R8/ProGuard on those `.jar` files, and
46produces the final `.dex` file(s) that will be packaged into your `.apk`
47
48## Deobfuscation
49
50Obfuscation can be turned off for local builds while leaving ProGuard enabled
51by setting `enable_proguard_obfuscation = false` in GN args.
52
53There are two main methods for deobfuscating Java stack traces locally:
541. Using APK wrapper scripts (stacks are automatically deobfuscated)
55  * `$OUT/bin/chrome_public_apk logcat`  # Run adb logcat
56  * `$OUT/bin/chrome_public_apk run`  # Launch chrome and run adb logcat
57
582. Using `java_deobfuscate`
59  * build/android/stacktrace/java_deobfuscate.py $OUT/apks/ChromePublic.apk.mapping < logcat.txt`
60    * ProGuard mapping files are located beside APKs (ex.
61      `$OUT/apks/ChromePublic.apk` and `$OUT/apks/ChromePublic.apk.mapping`)
62
63Helpful links for deobfuscation:
64
65* [Internal bits about how mapping files are archived][proguard-site]
66* [More detailed deobfuscation instructions][proguard-doc]
67* [Script for deobfuscating official builds][deob-official]
68
69[proguard-site]: http://goto.google.com/chrome-android-proguard
70[proguard-doc]: http://goto.google.com/chromejavadeobfuscation
71[deob-official]: http://goto.google.com/chrome-android-official-deobfuscation
72
73## Debugging common failures
74
75ProGuard failures are often hard to debug. This section aims to outline some of
76the more common errors.
77
78### Classes expected to be discarded
79
80The `-checkdiscard` directive can be used to ensure that certain items are
81removed by ProGuard. A common use of `-checkdiscard` it to ensure that ProGuard
82optimizations do not regress in their ability to remove code, such as code
83intended only for debug builds, or generated JNI classes that are meant to be
84zero-overhead abstractions. Annotating a class with
85[@CheckDiscard][checkdiscard] will add a `-checkdiscard` rule automatically.
86
87[checkdiscard]: /build/android/java/src/org/chromium/build/annotations/CheckDiscard.java
88
89```
90Item void org.chromium.base.library_loader.LibraryPrefetcherJni.<init>() was not discarded.
91void org.chromium.base.library_loader.LibraryPrefetcherJni.<init>()
92|- is invoked from:
93|  void org.chromium.base.library_loader.LibraryPrefetcher.asyncPrefetchLibrariesToMemory()
94... more code path lines
95|- is referenced in keep rule:
96|  obj/chrome/android/chrome_public_apk/chrome_public_apk.resources.proguard.txt:104:1
97
98Error: Discard checks failed.
99```
100
101Things to check
102  * Did you add code that is referenced by code path in the error message?
103  * If so, check the original class for why the `CheckDiscard` was added
104    originally and verify that the reason is still valid with your change (may
105    need git blame to do this).
106  * Try the extra debugging steps listed in the JNI section below.
107
108### JNI wrapper classes not discarded
109
110Proxy native methods (`@NativeMethods`) use generated wrapper classes to provide
111access to native methods. We rely on ProGuard to fully optimize the generated
112code so that native methods aren't a source of binary size bloat. The above
113error message is an example when a JNI wrapper class wasn't discarded (notice
114the name of the offending class).
115  * The ProGuard rule pointed to in the error message isn't helpful (just tells
116    us a code path that reaches the not-inlined class).
117  * Common causes:
118    * Caching the result of `ClassNameJni.get()` in a member variable.
119    * Passing a native wrapper method reference instead of using a lambda (i.e.
120      `Jni.get()::methodName` vs. `() -> Jni.get.methodName()`).
121  * For more debugging info, add to `base/android/proguard/chromium_code.flags`:
122      ```
123      -whyareyounotinlining class org.chromium.base.library_loader.LibraryPrefetcherJni {
124          <init>();
125      }
126      ```
127
128### Duplicate classes
129
130```
131Type YourClassName is defined multiple times: obj/jar1.jar:YourClassName.class, obj/jar2.jar:YourClassName.class
132```
133
134Common causes:
135  * Multiple targets with overlapping `srcjar_deps`:
136    * Each `.srcjar` can only be depended on by a single Java target in any
137      given APK target. `srcjar_deps` are just a convenient way to depend on
138      generated files and should be treated like source files rather than
139      `deps`.
140    * Solution: Wrap the `srcjar` in an `android_library` target or have only a
141      single Java target depend on the `srcjar` and have other targets depend on
142      the containing Java target instead.
143  * Accidentally enabling APK level generated files for multiple targets that
144    share generated code (ex. Trichrome or App Bundles):
145    * Solution: Make sure the generated file is only added once.
146
147Debugging ProGuard failures isn't easy, so please message [email protected]
148or [file a bug](crbug.com/new) with `component=Build os=Android` for any
149issues related to Java code optimization.
150