Files
VibeCoding/.agents/skills/r8-analyzer/references/CONFIGURATION-ANALYZER.md

287 lines
17 KiB
Markdown

On each step, keep the user informed of the progress by displaying the output.
### 1. Requirements
- R8 Version: 9.3.7-dev or later
### 2. Generate proto
The report and files must be generated at `{project_root}/tmp/r8analysis`. If
the folder is not present, create it. For example:
mkdir -p "$PWD/tmp/r8analysis"
### 3. Remove existing files
To make sure that this invocation doesn't source data from previous runs, remove
the intermediate files `keepruleradius.json` and `analysis_result.txt` and
remove the proto files in the `{project_root}/tmp/r8analysis` folder. Example
bash commands:
# Remove the intermediate JSON and the directory containing protobuf files
rm tmp/r8analysis/keepruleradius.json
rm tmp/r8analysis/*.pb
# Copy the previous result to history before deleting the analysis
if [ -f tmp/r8analysis/analysis_result.txt ];
then cat tmp/r8analysis/analysis_result.txt > tmp/r8analysis/history.txt &&
rm tmp/r8analysis/analysis_result.txt; fi
### 4. Generate the Configuration Analyzer report
Run the R8 enabled build with the system property
"-Dcom.android.tools.r8.dumpkeepradiustodirectory=$PWD/tmp/r8analysis" to
generate Configuration Analyzer report
./gradlew assembleRelease \
-Dcom.android.tools.r8.dumpkeepradiustodirectory=$PWD/tmp/r8analysis
### 5. Convert to JSON
To convert the generated protobuf files in `{project_root}/tmp/r8analysis` into
json, run the following script. The json must be generated in
`{project_root}/tmp/r8analysis`. Ensure `keep_radius_pb2.py` (from Step 10) is
in the same directory.
import sys
import os
import glob
from google.protobuf import json_format
import keep_radius_pb2
def convert_pb_to_json(input_pb_path, output_json_path):
bundle = keep_radius_pb2.BlastRadiusContainer()
try:
with open(input_pb_path, "rb") as pb_file:
binary_data = pb_file.read()
except Exception as e:
print(f"Error reading file {input_pb_path}: {e}", file=sys.stderr)
return False
try:
bundle.ParseFromString(binary_data)
except Exception as e:
print(f"Error parsing protobuf: {e}", file=sys.stderr)
return False
try:
json_string = json_format.MessageToJson(
bundle,
always_print_fields_with_no_presence=True,
preserving_proto_field_name=True,
indent=4
)
with open(output_json_path, "w", encoding="utf-8") as json_file:
json_file.write(json_string)
return True
except Exception as e:
print(f"Error writing JSON: {e}", file=sys.stderr)
return False
if __name__ == "__main__":
input_pb = sys.argv[1] if len(sys.argv) > 1 else None
if not input_pb:
pb_files = glob.glob("tmp/r8analysis/*.pb")
if not pb_files:
print("Error: No .pb file found in tmp/r8analysis", file=sys.stderr)
sys.exit(1)
input_pb = sorted(pb_files)[-1] # Use the most recent one
output_json = sys.argv[2] if len(sys.argv) > 2 else "tmp/r8analysis/keepruleradius.json"
if not convert_pb_to_json(input_pb, output_json):
sys.exit(1)
### 6. Analyze
Run the following analysis script on the generated JSON to get the impact of the
keep rules and sort it.
import json, sys
def analyze(path):
try:
with open(path, 'r') as f:
d = json.load(f)
except Exception as e:
print(f"Error loading JSON: {e}")
return
# Build reference map
c_map = {c.get('id'): set(c.get('constraints', [])) for c in d.get('keep_constraints_table', [])}
r_map = {r.get('id'): c_map.get(r.get('constraints_id'), set()) for r in d.get('keep_rule_blast_radius_table', [])}
tot_opt = tot_obf = tot_shr = tot_items = 0
# Tally constraints across all kept items
for tbl in ('kept_class_info_table', 'kept_field_info_table', 'kept_method_info_table'):
for i in d.get(tbl, []):
tot_items += 1
kb = i.get('kept_by', [])
if any('DONT_OPTIMIZE' in r_map.get(r, set()) for r in kb): tot_opt += 1
if any('DONT_OBFUSCATE' in r_map.get(r, set()) for r in kb): tot_obf += 1
if any('DONT_SHRINK' in r_map.get(r, set()) for r in kb): tot_shr += 1
# Find denominator
bi = d.get('build_info', {})
live = sum(int(bi.get(k, 0)) for k in ('live_class_count', 'live_field_count', 'live_method_count'))
denom = live if live > 0 else tot_items
# Check for globals
globals_src = [g.get('source', '').lower() for g in d.get('global_keep_rule_blast_radius_table', [])]
def score(cnt, flag):
if any(flag in src for src in globals_src): return 0.0
return max(0.0, 100.0 - ((cnt / denom * 100) if denom > 0 else 0))
result = [
f"Optimization Score: {score(tot_opt, '-dontoptimize'):.2f}%",
f"Obfuscation Score: {score(tot_obf, '-dontobfuscate'):.2f}%",
f"Shrinking Score: {score(tot_shr, '-dontshrink'):.2f}%"
]
for line in result:
print(line)
with open("tmp/r8analysis/analysis_result.txt", "w") as f:
f.write("\n".join(result))
if __name__ == "__main__":
path = sys.argv[1] if len(sys.argv) > 1 else "tmp/r8analysis/keepruleradius.json"
analyze(path)
Outputs `analysis_result.txt` containing scores and rule impacts.
### 7. Report impactful rules
Identify the keep rules with the highest impact and the subsumed rules using the
following script.
import json, sys
def report(path):
try:
with open(path, 'r') as f:
data = json.load(f)
except Exception as e:
print(f"Error loading JSON: {e}")
return
# Calculate denominator for percentage
bi = data.get('build_info', {})
live = sum(int(bi.get(k, 0)) for k in ('live_class_count', 'live_field_count', 'live_method_count'))
denom = live if live > 0 else sum(len(data.get(tbl, [])) for tbl in ('kept_class_info_table', 'kept_field_info_table', 'kept_method_info_table'))
processed = []
for r in data.get('keep_rule_blast_radius_table', []):
br = r.get('blast_radius', {})
c, f, m = len(br.get('class_blast_radius', [])), len(br.get('field_blast_radius', [])), len(br.get('method_blast_radius', []))
impact = c + f + m
if impact == 0:
continue
impact_pct = (impact / denom * 100) if denom > 0 else 0.0
processed.append({
'id': r.get('id'),
'source': r.get('source'),
'impact': impact,
'impact_pct': f"{impact_pct:.2f}%",
'classes': c,
'fields': f,
'methods': m,
'subsumed_by': br.get('subsumed_by', [])
})
processed.sort(key=lambda x: x['impact'], reverse=True)
# Output JSON for the agent to fetch and process
print(json.dumps({
"top_5_impact_keep_rules": [r for r in processed if not r['subsumed_by']][:5],
"subsumed": [r for r in processed if r['subsumed_by']]
}, indent=2))
if __name__ == "__main__":
report("tmp/r8analysis/keepruleradius.json")
Add this data to the `analysis_result.txt` with the top impactful rules and
subsumed rules.
### 8. Compare with previous report
If `{project_root}/tmp/r8analysis/history.txt` exists, use the following script
to compare the previous run. Use this to compare with the current values
### 9. Remove generated files
After the final report and analysis results are generated, remove the
intermediate files `keepruleradius.json` and `analysis_result.txt` and remove
the proto files in "{project_root}/tmp/r8analysis" folder
rm tmp/r8analysis/keepruleradius.json
rm tmp/r8analysis/*.pb
### 10. Protobuf Python bindings
The following script `keep_radius_pb2.py` is required by the conversion script
in Step 5.
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import runtime_version as _runtime_version
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
_runtime_version.ValidateProtobufRuntimeVersion(
_runtime_version.Domain.PUBLIC,
6,
33,
4,
'',
'keep_radius.proto'
)
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11keep_radius.proto\x12&com.android.tools.r8.blastradius.proto\"\x9f\x02\n\x13KeepRuleBlastRadius\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\x16\n\x0e\x63onstraints_id\x18\x03 \x01(\x05\x12\x46\n\x06origin\x18\x04 \x01(\x0b\x32\x36.com.android.tools.r8.blastradius.proto.TextFileOrigin\x12I\n\x0c\x62last_radius\x18\x05 \x01(\x0b\x32\x33.com.android.tools.r8.blastradius.proto.BlastRadius\x12\x41\n\x04tags\x18\x06 \x03(\x0e\x32\x33.com.android.tools.r8.blastradius.proto.KeepRuleTag\"w\n\x0b\x42lastRadius\x12\x13\n\x0bsubsumed_by\x18\x01 \x03(\x05\x12\x1a\n\x12\x63lass_blast_radius\x18\x02 \x03(\x05\x12\x1a\n\x12\x66ield_blast_radius\x18\x03 \x03(\x05\x12\x1b\n\x13method_blast_radius\x18\x04 \x03(\x05\"\x7f\n\x19GlobalKeepRuleBlastRadius\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x0e\n\x06source\x18\x02 \x01(\t\x12\x46\n\x06origin\x18\x03 \x01(\x0b\x32\x36.com.android.tools.r8.blastradius.proto.TextFileOrigin\"j\n\x0fKeepConstraints\x12\n\n\x02id\x18\x01 \x01(\x05\x12K\n\x0b\x63onstraints\x18\x02 \x03(\x0e\x32\x36.com.android.tools.r8.blastradius.proto.KeepConstraint\"`\n\rKeptClassInfo\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x1a\n\x12\x63lass_reference_id\x18\x02 \x01(\x05\x12\x16\n\x0e\x66ile_origin_id\x18\x03 \x01(\x05\x12\x0f\n\x07kept_by\x18\x04 \x03(\x05\"`\n\rKeptFieldInfo\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x1a\n\x12\x66ield_reference_id\x18\x02 \x01(\x05\x12\x16\n\x0e\x66ile_origin_id\x18\x03 \x01(\x05\x12\x0f\n\x07kept_by\x18\x04 \x03(\x05\"b\n\x0eKeptMethodInfo\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x1b\n\x13method_reference_id\x18\x02 \x01(\x05\x12\x16\n\x0e\x66ile_origin_id\x18\x03 \x01(\x05\x12\x0f\n\x07kept_by\x18\x04 \x03(\x05\"a\n\x0e\x46ieldReference\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x1a\n\x12\x63lass_reference_id\x18\x02 \x01(\x05\x12\x19\n\x11type_reference_id\x18\x03 \x01(\x05\x12\x0c\n\x04name\x18\x04 \x01(\t\"c\n\x0fMethodReference\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x1a\n\x12\x63lass_reference_id\x18\x02 \x01(\x05\x12\x1a\n\x12proto_reference_id\x18\x03 \x01(\x05\x12\x0c\n\x04name\x18\x04 \x01(\t\"K\n\x0eProtoReference\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x15\n\rparameters_id\x18\x02 \x01(\x05\x12\x16\n\x0ereturn_type_id\x18\x03 \x01(\x05\"4\n\rTypeReference\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x17\n\x0fjava_descriptor\x18\x02 \x01(\t\";\n\x11TypeReferenceList\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x1a\n\x12type_reference_ids\x18\x02 \x03(\x05\"\x9f\x01\n\nFileOrigin\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x10\n\x08\x66ilename\x18\x02 \x01(\t\x12Q\n\x10maven_coordinate\x18\x03 \x01(\x0b\x32\x37.com.android.tools.r8.blastradius.proto.MavenCoordinate\x12 \n\x18provided_by_build_system\x18\x04 \x01(\x08\"I\n\x14\x43lassFileInJarOrigin\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x16\n\x0e\x66ile_origin_id\x18\x02 \x01(\x05\x12\r\n\x05\x65ntry\x18\x03 \x01(\t\"T\n\x0eTextFileOrigin\x12\x16\n\x0e\x66ile_origin_id\x18\x01 \x01(\x05\x12\x13\n\x0bline_number\x18\x02 \x01(\x05\x12\x15\n\rcolumn_number\x18\x03 \x01(\x05\"U\n\x0fMavenCoordinate\x12\n\n\x02id\x18\x01 \x01(\x05\x12\x10\n\x08group_id\x18\x02 \x01(\t\x12\x13\n\x0b\x61rtifact_id\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\"\x9a\x01\n\tBuildInfo\x12\x13\n\x0b\x63lass_count\x18\x01 \x01(\x05\x12\x13\n\x0b\x66ield_count\x18\x02 \x01(\x05\x12\x14\n\x0cmethod_count\x18\x03 \x01(\x05\x12\x18\n\x10live_class_count\x18\x04 \x01(\x05\x12\x18\n\x10live_field_count\x18\x05 \x01(\x05\x12\x19\n\x11live_method_count\x18\x06 \x01(\x05\"\xd5\n\n\x14\x42lastRadiusContainer\x12M\n\x11\x66ile_origin_table\x18\x01 \x03(\x0b\x32\x32.com.android.tools.r8.blastradius.proto.FileOrigin\x12\x64\n\x1e\x63lass_file_in_jar_origin_table\x18\x02 \x03(\x0b\x32<.com.android.tools.r8.blastradius.proto.ClassFileInJarOrigin\x12W\n\x16maven_coordinate_table\x18\x03 \x03(\x0b\x32\x37.com.android.tools.r8.blastradius.proto.MavenCoordinate\x12U\n\x15\x66ield_reference_table\x18\x04 \x03(\x0b\x32\x36.com.android.tools.r8.blastradius.proto.FieldReference\x12W\n\x16method_reference_table\x18\x05 \x03(\x0b\x32\x37.com.android.tools.r8.blastradius.proto.MethodReference\x12U\n\x15proto_reference_table\x18\x06 \x03(\x0b\x32\x36.com.android.tools.r8.blastradius.proto.ProtoReference\x12S\n\x14type_reference_table\x18\x07 \x03(\x0b\x32\x35.com.android.tools.r8.blastradius.proto.TypeReference\x12\\\n\x19type_reference_list_table\x18\x08 \x03(\x0b\x32\x39.com.android.tools.r8.blastradius.proto.TypeReferenceList\x12T\n\x15kept_class_info_table\x18\t \x03(\x0b\x32\x35.com.android.tools.r8.blastradius.proto.KeptClassInfo\x12T\n\x15kept_field_info_table\x18\n \x03(\x0b\x32\x35.com.android.tools.r8.blastradius.proto.KeptFieldInfo\x12V\n\x16kept_method_info_table\x18\x0b \x03(\x0b\x32\x36.com.android.tools.r8.blastradius.proto.KeptMethodInfo\x12W\n\x16keep_constraints_table\x18\x0c \x03(\x0b\x32\x37.com.android.tools.r8.blastradius.proto.KeepConstraints\x12\x61\n\x1ckeep_rule_blast_radius_table\x18\r \x03(\x0b\x32;.com.android.tools.r8.blastradius.proto.KeepRuleBlastRadius\x12n\n#global_keep_rule_blast_radius_table\x18\x0e \x03(\x0b\x32\x41.com.android.tools.r8.blastradius.proto.GlobalKeepRuleBlastRadius\x12\x45\n\nbuild_info\x18\x0f \x01(\x0b\x32\x31.com.android.tools.r8.blastradius.proto.BuildInfo*\x1f\n\x0bKeepRuleTag\x12\x10\n\x0cPACKAGE_WIDE\x10\x00*H\n\x0eKeepConstraint\x12\x12\n\x0e\x44ONT_OBFUSCATE\x10\x00\x12\x11\n\rDONT_OPTIMIZE\x10\x01\x12\x0f\n\x0b\x44ONT_SHRINK\x10\x02\x42\x45\n&com.android.tools.r8.blastradius.protoB\x19KeepRuleBlastRadiusProtosP\001\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'keep_radius_pb2', _globals)
if not _descriptor._USE_C_DESCRIPTORS:
_globals['DESCRIPTOR']._loaded_options = None
_globals['DESCRIPTOR']._serialized_options = b'\n&com.android.tools.r8.blastradius.protoB\031KeepRuleBlastRadiusProtosP\001'
_globals['_KEEPRULETAG']._serialized_start = 3332
_globals['_KEEPRULETAG']._serialized_end = 3363
_globals['_KEEPCONSTRAINT']._serialized_start = 3365
_globals['_KEEPCONSTRAINT']._serialized_end = 3437
_globals['_KEEPRULEBLASTRADIUS']._serialized_start = 62
_globals['_KEEPRULEBLASTRADIUS']._serialized_end = 349
_globals['_BLASTRADIUS']._serialized_start = 351
_globals['_BLASTRADIUS']._serialized_end = 470
_globals['_GLOBALKEEPRULEBLASTRADIUS']._serialized_start = 472
_globals['_GLOBALKEEPRULEBLASTRADIUS']._serialized_end = 599
_globals['_KEEPCONSTRAINTS']._serialized_start = 601
_globals['_KEEPCONSTRAINTS']._serialized_end = 707
_globals['_KEPTCLASSINFO']._serialized_start = 709
_globals['_KEPTCLASSINFO']._serialized_end = 805
_globals['_KEPTFIELDINFO']._serialized_start = 807
_globals['_KEPTFIELDINFO']._serialized_end = 903
_globals['_KEPTMETHODINFO']._serialized_start = 905
_globals['_KEPTMETHODINFO']._serialized_end = 1003
_globals['_FIELDREFERENCE']._serialized_start = 1005
_globals['_FIELDREFERENCE']._serialized_end = 1102
_globals['_METHODREFERENCE']._serialized_start = 1104
_globals['_METHODREFERENCE']._serialized_end = 1203
_globals['_PROTOREFERENCE']._serialized_start = 1205
_globals['_PROTOREFERENCE']._serialized_end = 1280
_globals['_TYPEREFERENCE']._serialized_start = 1282
_globals['_TYPEREFERENCE']._serialized_end = 1334
_globals['_TYPEREFERENCELIST']._serialized_start = 1336
_globals['_TYPEREFERENCELIST']._serialized_end = 1395
_globals['_FILEORIGIN']._serialized_start = 1398
_globals['_FILEORIGIN']._serialized_end = 1557
_globals['_CLASSFILEINJARORIGIN']._serialized_start = 1559
_globals['_CLASSFILEINJARORIGIN']._serialized_end = 1632
_globals['_TEXTFILEORIGIN']._serialized_start = 1634
_globals['_TEXTFILEORIGIN']._serialized_end = 1718
_globals['_MAVENCOORDINATE']._serialized_start = 1720
_globals['_MAVENCOORDINATE']._serialized_end = 1805
_globals['_BUILDINFO']._serialized_start = 1808
_globals['_BUILDINFO']._serialized_end = 1962
_globals['_BLASTRADIUSCONTAINER']._serialized_start = 1965
_globals['_BLASTRADIUSCONTAINER']._serialized_end = 3330
# @@protoc_insertion_point(module_scope)