Repository navigation
JSON and Logs
Use JSON for scripts and retain the runtime log to reproduce or investigate a report. The HTML report additionally embeds reconstructed code for interactive browsing.
Aggregate output contains coverageSummary. -pam and capture also include perMinuteSummaries. Only selected coverage categories appear.
This illustrative coverage-only report shows the field names and value types:
{
"coverageSummary": {
"methods": {"covered": 3, "total": 5, "percent": "60.0%"}
},
"perMinuteSummaries": {
"10-23 09:30": {
"methods": {"cumulative": 2, "overall": 5, "percent": "40.0%"}
},
"10-23 09:31": {
"methods": {"cumulative": 3, "overall": 5, "percent": "60.0%"}
}
}
}Aggregate metrics use covered and total. Timeline metrics use cumulative and overall. Percentages are strings with a percent sign; for calculations, use the integer counts. For a zero denominator, JSON preserves "0.0%", while the terminal displays n/a.
The category keys are statements, methods, classes, activities, services, broadcast-receivers, and content-providers. A graph-only run can have an empty coverage summary.
When call tracing is requested or matching call records are available, callGraph contains:
| Field | Meaning |
|---|---|
available |
Whether matching call observations were accepted |
nodes |
Method signatures and display metadata |
edges |
Aggregated caller-to-callee relationships |
stats |
Record, callsite, and edge counts |
launcherActivities |
Manifest-derived launcher target classes; may be absent in older reports or null when not collected |
note |
Text describing observation semantics and limits |
A node has id (full method signature), className, label, and inReport. inReport means a method body is available in the collected code listing. It is not a coverage flag. Code bodies are collected for HTML generation, so JSON generated without -html can have inReport: false even for instrumented methods.
An edge has caller, callee, kind, count, and callSites:
{
"caller": "<demo.Main: void run()>",
"callee": "<demo.Dog: void speak()>",
"kind": "observed",
"count": 1,
"callSites": [
{
"id": "7",
"declared": "<demo.Animal: void speak()>",
"kind": "virtual"
}
]
}This is an illustrative individual edge, not a complete report. Edge kind is observed or declared. Each callsite's kind describes its invocation, such as virtual, interface, static, special, constructor, or dynamic; legacy records use legacy. A callsite ID is the original statement position within its caller, not a source line or a globally unique ID.
count sums distinct accepted records for the relationship. It is not the number of times the method was called. The same caller/callee pair can have separate observed and declared edges when different callsites have different confirmation status.
| Statistic | Meaning |
|---|---|
observedEdges |
Aggregated edges with confirmed runtime targets |
declaredEdges |
Aggregated fallback edges retaining declared targets |
unresolvedSites |
Recorded callsites with no confirmed target |
callSites |
Number of recorded callsites represented in the graph |
records |
Number of distinct accepted call records |
Edge counts and callsite counts differ because several callsites can share an edge, and one callsite can reach several targets. See Dynamic Call Graph for interpretation.
This Python example requires only the standard library:
import json
with open("coverage.json", encoding="utf-8") as source:
report = json.load(source)
for category, metric in report["coverageSummary"].items():
print(category, metric["covered"], metric["total"], metric["percent"])
for edge in report.get("callGraph", {}).get("edges", []):
if edge["kind"] == "observed":
print(edge["caller"], "->", edge["callee"])Coverage messages begin with CLASS=, METHOD=, STATEMENT=, ACTIVITY=, SERVICE=, BROADCASTRECEIVER=, or CONTENTPROVIDER=. Statement identities combine the method signature, original Jimple text, and position within the method.
The current call-tracing message has nine pipe-separated fields:
MC2|trace UUID|sequence|status|site|caller|declared|actual|invocation kind
D records a reached callsite and its declared target; its actual field is empty. O records a confirmed actual target. Signatures are URL-encoded with UTF-8 form encoding, so decode + as a space. A process trace UUID and sequence identify each record. The runtime deduplicates declarations and actual-target pairs per callsite within a process.
The parser validates the tag, payload, callsite, and signatures against the original APK baseline. Duplicate call-record identities are ignored. Malformed, truncated, or incompatible records are rejected, but the current report does not provide a rejection-count breakdown. Legacy CALL= records retain only declared targets.
Use adb logcat -v threadtime for the expected MM-DD HH:mm:ss prefix. The aggregate parser accepts several tagged logcat formats, including year-prefixed timestamps. The current timeline extractor accepts only a leading MM-DD HH:mm minute. Untimestamped and year-prefixed observations can therefore contribute to aggregate totals without entering the timeline.
Distinct coverage probes are assigned to their first matching occurrence in file order. Keep logs chronological when building a timeline. The minute key has no year, so do not use it as an unambiguous multi-year time series.
For aggregate coverage, logs from the same APK, probes, scope, and tag can be concatenated and parsed with -pa. Coverage entities and repeated MC2 records are deduplicated. Different process UUIDs preserve distinct call observations across executions. This produces an aggregate union, not a run comparison or an invocation-frequency profile. Retain individual logs if you need to distinguish runs.
Home · Quick Start · CLI Reference · Report an issue
AndroLog by Jordan Samhi · LGPL v2.1 · Documentation checked against 68b3312, 23 October 2026.
Start here
Understand your results
Reference