How to capture and read an Android bug report with adb
Until Android 6.0, adb bugreport streamed plain text into your terminal. From Android 7.0 (API 24) it writes a single zip, and that zip is the fullest snapshot of a phone you can pull over adb: logcat, the kernel log, dumpsys output for the system services, battery statistics, and traces from recent ANRs and native crashes.
It fixes the vague report. A tester says “the app froze” or “battery was flat by morning”, and a logcat grabbed an hour later is an hour of unrelated noise. A bug report taken right after the failure keeps the surrounding state, which is why Google’s bug report documentation asks for one. This is for developers, QA and operators running Android phones they own or are authorised to test.
By the end you will have captured a report with one command, found a crash and an ANR in it, looked at battery data in Battery Historian, and know what to strip out before the file leaves your machine. I work from Windows, so I show PowerShell and bash where they differ.
what you need
- an Android device or emulator you own or are authorised to test, on Android 7.0 (API 24) or later for the zip format. older builds print plain text, so use
adb bugreport > bugreport.txt. - USB debugging switched on under Developer options, and a cable that carries data. some cables are charge only.
- current Android SDK platform-tools from Google.
adb versionprints what you have, and the adb reference lists every command. unzipandgrepon macOS or Linux, orExpand-ArchiveandSelect-Stringin PowerShell.- Docker, for step 7 only. Battery Historian is Google’s open source tool for Android 5.0 (API 21) and later, and it is free.
- a device to test on. your own phone, an emulator (emulator vs real device testing covers what emulators hide), or a rented one. if the bug only shows on a real Singapore mobile connection and you have no phone there, cloudf.one rents real Android phones in Singapore on dedicated hardware, each with a persistent Singapore mobile IP, controlled from the browser. there is no self-service adb. adb and Appium reach a rented phone only through a WireGuard tunnel that we set up on request (email [email protected]). Singapore only.
step by step
1. Confirm adb can see the device
adb version
adb devices
Expected: a version line, then your serial number with the word device next to it.
If it breaks: unauthorized means the RSA prompt on the phone is waiting for a tap (see adb authorization and RSA keys). offline or an empty list usually clears with a server restart, and the client, server and daemon explainer shows why:
adb kill-server
adb start-server
2. Reproduce the problem, then capture straight away
Logcat lives in RAM ring buffers. New lines push out old ones and a reboot wipes them, so capture soon after the failure. adb logcat -g prints each buffer’s size, which tells you how much history you have. Chasing one specific run? Clear the buffers first. Intermittent bug? Skip the clear.
adb logcat -g
adb logcat -c
adb shell am force-stop com.example.app
Use your own package name, then trigger the bug. The am and pm guide covers force-stop and friends.
Expected: -g prints one line per buffer with its size. The other two print nothing.
If it breaks: a reboot mid-test wipes the log history, so reproduce again. ANR traces and tombstones live on disk and may still be in the report.
3. Run adb bugreport
mkdir reports
adb bugreport reports
Expected: a message that the report is in progress and can take minutes, a rising percentage, then a line naming the saved zip, whose name carries the build id and a timestamp. Wording differs between adb versions, and the duration depends on the device.
With more than one device attached, adb refuses. Add -s <serial> before bugreport.
If it breaks: if the percentage freezes, run adb devices in a second terminal. A device that dropped to offline points at the cable or hub. Swap it, rerun, and delete the truncated zip.
The on-device route (Developer options, Take bug report, Interactive or Full) works too. I use adb for anything headed to a ticket because the file lands on the PC directly.
4. Unpack it and look inside
# bash
unzip reports/bugreport-<name>.zip -d reports/br
# PowerShell
Expand-Archive -Path .\reports\bugreport-<name>.zip -DestinationPath .\reports\br
Expected: a large bugreport-<device>-<build>-<date>.txt plus smaller items. Contents vary by Android version and vendor, but typically:
- the main text file: header, logcat, event log, kernel log, system properties, and a
DUMP OF SERVICEblock per system service dumpstate_log.txt: the log of the capture run, useful when a section is missingFS/: files copied off the phone, where ANR traces and tombstones often sit
Check the header before anything else. If the fingerprint is not your device and build, you grabbed the wrong report.
# bash
grep -m1 "Build fingerprint" reports/br/bugreport-*.txt
# PowerShell
Select-String -Path .\reports\br\bugreport-*.txt -Pattern "Build fingerprint" | Select-Object -First 1
If it breaks: Expand-Archive wants a .zip extension, and a near-empty zip means the capture was cut short, so rerun step 3. Open the big file in VS Code, not Notepad.
5. Find the crash
The main text file contains the logcat output, so crashes appear as ordinary log lines.
# bash
grep -n -e "FATAL EXCEPTION" -e "Fatal signal" -e "am_crash" reports/br/bugreport-*.txt
# PowerShell
Select-String -Path .\reports\br\bugreport-*.txt -Pattern "FATAL EXCEPTION","Fatal signal","am_crash"
Expected: a Java or Kotlin crash shows FATAL EXCEPTION: main, a Process: com.example.app, PID: ... line and a stack trace. Read down to the last Caused by:, which is usually the real cause. A native crash shows Fatal signal 11 (SIGSEGV) and a tombstone with a backtrace: block. am_crash is the event log’s one-line summary with the process and exception class. Read forty lines either side of it. The logcat guide covers the tag and priority filters if you want the same lines live.
If it breaks: no hits usually means the crash aged out of the log buffer. Check FS/ for tombstones, or reproduce and capture sooner.
6. Find the ANR
# bash
grep -n -e "ANR in" -e "am_anr" reports/br/bugreport-*.txt
ls reports/br/FS/data/anr
# PowerShell
Select-String -Path .\reports\br\bugreport-*.txt -Pattern "ANR in","am_anr"
Get-ChildItem .\reports\br\FS\data\anr
Expected: an ANR in com.example.app (com.example.app/.MainActivity) line from ActivityManager, then the PID, a Reason: line such as Input dispatching timed out, and load and CPU usage lines. Google’s ANR documentation lists the thresholds, and input events get 5 seconds. Next find the thread dump for that PID, in the text file or under FS/data/anr, and read the "main" thread. Blocked with waiting to lock ... held by thread N is a lock fight, so go read thread N. Native inside a Binder call means it was waiting on another process.
If it breaks: users say it froze but there is no ANR line? That is often jank. Search for Choreographer’s Skipped frames lines instead. If FS/data/anr is missing, your build may keep traces elsewhere.
7. Read battery data with Battery Historian
Battery Historian only helps if the data covers your test, so prepare before you reproduce. These two commands come from the project’s README:
adb shell dumpsys batterystats --enable full-wake-history
adb shell dumpsys batterystats --reset
Unplug the phone while you use it, because a USB cable charges it and flattens the curve you are reading. adb over Wi-Fi keeps adb connected without the cable. Capture with step 3, then start the tool:
docker run -p 9999:9999 gcr.io/android-battery-historian/stable:3.0 --port 9999
Open http://localhost:9999 and upload the zip. Expected: a timeline of battery level, screen state, wake locks, jobs and alarms, plus per-app stats. Look for your app holding a wake lock long after the screen went off.
If it breaks: the image tag comes from the Battery Historian README and I have not re-checked it as of September 2026. I also have not confirmed the project is still maintained, or run it on reports from the newest Android releases. If the pull or upload fails, read the README first, then fall back to searching the text file for batterystats, which holds the raw stats.
8. Scrub it before it leaves your machine
A bug report is not a log you can paste anywhere. It can include the account names on the device (usually email addresses), saved Wi-Fi network names, the installed app list, system properties that often carry the serial number, recent location data, and whatever any app wrote to logcat, which sometimes means URLs, tokens or user text. I can’t promise this list is complete, so treat the whole zip as sensitive.
# bash
grep -n -i -E "[a-z0-9._-]+@[a-z0-9.-]+\.[a-z]+" reports/br/bugreport-*.txt | head -20
# PowerShell
Select-String -Path .\reports\br\bugreport-*.txt -Pattern "[a-z0-9._-]+@[a-z0-9.-]+\.[a-z]+" | Select-Object -First 20
Expected: on a phone signed into Google, at least the account address. My rule is to share the excerpt (the crash block or the ANR trace), not the zip. If a vendor insists on the zip, send it privately. If the phone belongs to someone else, get their consent first, and note that data protection rules such as Singapore’s PDPA may apply. This is not legal advice.
If it breaks: too many hits to redact by hand means don’t hand over the file. Send the excerpt.
common pitfalls
- capturing late. someone reports a freeze from yesterday, and by then the log buffers have turned over. ask for the report at the moment of failure.
- two adb versions on PATH. a vendor SDK folder or an old distro package starts its own server on port 5037, and adb prints a “server version doesn’t match this client” line and kills the older server. run
adb versionand remove the stray copy. - reading times as your own. log timestamps use the device’s timezone, so a phone set to another zone puts the crash hours away from where you look.
- skipping the battery prep. without the reset, Historian shows history from before your test, and charging over USB fakes the discharge curve.
- posting the zip in a public issue or forum thread. post the excerpt.
scaling this
At 10 devices the manual version breaks first. A loop captures one report per attached phone into a folder named by serial:
mkdir -p reports
for serial in $(adb devices | tr -d '\r' | awk 'NR>1 && $2=="device" {print $1}'); do
mkdir -p "reports/$serial"
adb -s "$serial" bugreport "reports/$serial/" &
done
wait
At 100, cap the parallelism (xargs -P 4 is a sane start, then watch the host), swap hubs for wireless adb, and trigger capture on failure instead of by hand. Why Android UI tests are flaky is the argument for keeping evidence from every failed run. A crude triage counts ANR lines per zip:
for z in reports/*/bugreport-*.zip; do
printf '%s ' "$z"; unzip -p "$z" 'bugreport-*.txt' | grep -c "ANR in"
done
At 1000, stop collecting raw reports from everything. Crash and ANR rates for real users come from Android vitals in Google Play Console, and bug reports become the deep-dive tool for devices you control. Every zip holds personal data, so give the folder an owner and a retention period.
where to go next
- logcat guide: filter by app, tag and priority for slicing the log live instead of after the fact.
- why Android UI tests are flaky for deciding what to attach to a failed CI run.
- adb over Wi-Fi and wireless debugging for capturing without a cable.
- the blog index for the rest.
Written by Xavier Fok
disclosure: this article may contain affiliate links. if you buy through them we may earn a commission at no extra cost to you. verdicts are independent of payouts. last reviewed by Xavier Fok on 2026-09-20.