杰瑞科技汇

python adb dumpheap

Of course! Using Python to automate adb dumpheap is a powerful technique for Android performance analysis, especially for memory profiling and debugging memory leaks.

python adb dumpheap-图1
(图片来源网络,侵删)

Here's a comprehensive guide covering everything from the basic setup to a complete, production-ready Python script.

Prerequisites

Before you start, ensure you have the necessary tools:

  1. Python: Installed on your system.
  2. Android SDK Platform-Tools: This includes the adb (Android Debug Bridge) executable.
  3. Rooted Android Device (Optional but Recommended):
    • dumpheap requires the DUMP permission, which is typically only available to system apps or root users.
    • Non-Rooted: You can only dump the heap of your own app if it has the android.permission.DUMP permission declared in its manifest. This is rare for standard apps.
    • Rooted: You can use adb shell su -c "..." to run the command with root privileges, allowing you to dump the heap of any app process.

Understanding the adb dumpheap Command

The core command you'll be wrapping in Python is:

adb shell dumpheap <pid> <output_file.hprof>
  • <pid>: The Process ID of the target application. You can find this using adb shell ps.
  • <output_file.hprof>: The path on the device where the heap dump will be saved. A common location is /sdcard/.
  • Note: The .hprof file format is the standard for Android heap dumps. It can be analyzed by tools like Android Studio's Profiler, MAT (Memory Analyzer Tool), or hprof-conv.

The Python Script: A Step-by-Step Guide

Let's build a robust Python script that automates the entire process: finding the PID, dumping the heap, pulling the file to your computer, and cleaning up.

python adb dumpheap-图2
(图片来源网络,侵删)

Step 1: Install a Helper Library

The subprocess module is built-in, but shlex makes handling shell arguments with spaces much safer. We'll also use psutil for a more reliable way to find the PID on the host machine if the app is running.

pip install psutil

Step 2: The Complete Python Script

Here is a well-commented, reusable script. Save it as dump_heap.py.

import subprocess
import shlex
import time
import os
import sys
import argparse
import psutil # For finding the PID on the host machine
# --- Configuration ---
# A common place to store heap dumps on your computer
LOCAL_DUMP_DIR = "heap_dumps"
# A common place to store heap dumps on the Android device
REMOTE_DUMP_DIR = "/sdcard/"
def run_adb_command(command):
    """Executes an adb command and returns its output."""
    try:
        # shlex.split handles arguments with spaces correctly
        cmd = shlex.split(command)
        result = subprocess.run(cmd, capture_output=True, text=True, check=True)
        return result.stdout.strip()
    except subprocess.CalledProcessError as e:
        print(f"Error executing ADB command: {e}")
        print(f"STDERR: {e.stderr}")
        return None
def find_app_pid(package_name):
    """
    Finds the PID of an Android app using its package name.
    Tries to find it on the host machine first (more reliable), then falls back to adb.
    """
    # Try to find the process on the host machine (works if ADB is in port forwarding mode)
    for proc in psutil.process_iter(['pid', 'name']):
        try:
            # The command line for an ADB-connected process often contains the package name
            if package_name.lower() in proc.info['name'].lower():
                print(f"Found PID {proc.info['pid']} on host for package '{package_name}'.")
                return str(proc.info['pid'])
        except (psutil.NoSuchProcess, psutil.AccessDenied):
            continue
    # Fallback to the traditional 'adb shell ps' method
    print(f"Host search failed. Falling back to 'adb shell ps' for package '{package_name}'.")
    ps_output = run_adb_command("adb shell ps")
    if not ps_output:
        return None
    for line in ps_output.splitlines():
        # Example line: u0_a100   12345  1234  1080000 SyS_epoll_0 com.example.myapp
        parts = line.split()
        if package_name in parts:
            pid = parts[1]
            print(f"Found PID {pid} on device for package '{package_name}'.")
            return pid
    print(f"Could not find PID for package: {package_name}")
    return None
def dump_heap(package_name):
    """
    Main function to dump the heap of a given package.
    """
    print(f"--- Starting heap dump for package: {package_name} ---")
    # 1. Find the Process ID (PID)
    pid = find_app_pid(package_name)
    if not pid:
        return False
    # 2. Define the remote and local file paths
    remote_file_path = f"{REMOTE_DUMP_DIR}heapdump_{pid}_{int(time.time())}.hprof"
    local_file_path = f"{LOCAL_DUMP_DIR}/{os.path.basename(remote_file_path)}"
    # 3. Create local directory if it doesn't exist
    os.makedirs(LOCAL_DUMP_DIR, exist_ok=True)
    # 4. Execute the dumpheap command on the device
    # Using 'su -c' is required for non-system apps on non-rooted devices,
    # and is necessary for dumping other apps' heaps on rooted devices.
    # The 'am dumpheap' command is a modern alternative but requires the DUMP permission.
    dump_command = f"adb shell su -c \"dumpheap {pid} {remote_file_path}\""
    print(f"Executing: {dump_command}")
    # Note: This command can hang if the process is unresponsive.
    # A timeout could be added here.
    stdout = run_adb_command(dump_command)
    if stdout is None:
        print("Heap dump command failed.")
        return False
    print("Heap dump command sent successfully. Waiting for file to be created...")
    time.sleep(2) # Give the device a moment to write the file
    # 5. Pull the .hprof file from the device to your computer
    pull_command = f"adb pull {remote_file_path} {local_file_path}"
    print(f"Executing: {pull_command}")
    stdout = run_adb_command(pull_command)
    if stdout is None:
        print("Failed to pull heap dump file.")
        return False
    print(f"Successfully pulled heap dump to: {local_file_path}")
    # 6. Clean up: Remove the heap dump from the device
    # Note: Be careful with 'rm' commands. It's better practice to use 'rm -f'.
    rm_command = f"adb shell rm {remote_file_path}"
    print(f"Executing: {rm_command}")
    run_adb_command(rm_command) # We don't care if this fails, but we try.
    print("Cleaned up remote file.")
    print(f"--- Heap dump complete for {package_name} ---")
    return True
if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Automate ADB heap dump for an Android app.")
    parser.add_argument("package_name", help="The package name of the app (e.g., com.example.myapp)")
    args = parser.parse_args()
    if dump_heap(args.package_name):
        sys.exit(0)
    else:
        sys.exit(1)

Step 3: How to Use the Script

  1. Save the code: Save the script above as dump_heap.py.

  2. Connect your device: Make sure your Android device is connected via USB and USB debugging is enabled.

  3. Run from the terminal:

    python dump_heap.py com.example.myapp

    Replace com.example.myapp with the actual package name of the app you want to analyze.

Step 4: Analyze the Output

After the script runs, you will find a .hprof file in the heap_dumps directory (e.g., heap_dumps/heapdump_12345_1678886400.hprof).

You can now open this file with professional memory analysis tools:

  • Android Studio Profiler: The easiest way. Just open the Profiler tab and import the .hprof file.
  • Eclipse MAT (Memory Analyzer Tool): A powerful, standalone tool for analyzing heap dumps. It can find memory leaks, duplicate objects, and more.
  • LeakCanary: While primarily a runtime library, it can also be configured to analyze .hprof files.

Troubleshooting Common Issues

  • adb: command not found

    • Solution: The Android SDK Platform-Tools are not in your system's PATH. Add the directory containing adb.exe (Windows) or adb (macOS/Linux) to your PATH environment variable.
  • error: no devices/emulators found

    • Solution: Ensure your device is connected, USB debugging is enabled in the Developer options, and you've approved the computer's RSA key on your phone.
  • dumpheap: permission denied

    • Cause: You are trying to dump a process you don't have permission for.
    • Solution 1 (Rooted Device): The script already uses adb shell su -c "...". Ensure your device is properly rooted and that ADB has root access (you might need to use adb root first, or grant root access via SuperSU/Superuser app when prompted).
    • Solution 2 (Non-Rooted): You can only dump your own app if it has the DUMP permission. This is not feasible for most users. Rooting is the most reliable way for general-purpose heap dumping.
  • Can't find PID

    • Cause: The app is not running, or the package name is incorrect.
    • Solution: Double-check the package name. You can list all running apps with adb shell pm list packages -f and find the one corresponding to your app. Ensure the app is open and active on the device.
  • The operation timed out

    • Cause: The dumpheap process can take a long time on apps with large heaps, or it might be hanging.
    • Solution: Increase the sleep time in the script after the dump command. If it still hangs, you may need to manually intervene or investigate why the target process is unresponsive. You can add a timeout parameter to the subprocess.run call if you want the script to kill the command after a certain period.
分享:
扫描分享到社交APP
上一篇
下一篇