All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mrcd.mmat.HprofDumper Maven / Gradle / Ivy

The newest version!
package com.mrcd.mmat;

import com.mrcd.mmat.util.FileUtils;
import com.mrcd.mmat.util.ProcessUtil;

import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * dump hprof file
 * create by mrsimple at 2019-06-14.
 */
public class HprofDumper {

    private static final int BACK_PRESS_KEY = 4 ;
    public static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss") ;


    private HprofDumper() {
    }

    /**
     * 应用退到后台, 然后force gc, 最后dump hprof
     * @param config
     * @param disableMonkey
     * @return
     * @throws InterruptedException
     */
    static File dumpHprof(MMATConfig config, boolean disableMonkey) throws InterruptedException {
        // 只有在运行了monkey测试的情况下才需要跳转到主页面, 并且执行 force-gc
        if ( config.shouldRunMonkey() && !disableMonkey) {
            // dump meminfo来获取到进程的pid
            ProcessUtil.executeCommand("adb shell dumpsys meminfo " + config.packageName) ;
            // 跳转到应用的主页面, 并且清理掉其他页面 (确保除了主页面的其他页面都应该销毁, 如果没有销毁, 那么则是产生了内存泄漏)
            ProcessUtil.executeCommand(String.format("adb shell am start -n %s/%s", config.packageName, config.mainActivity)) ;
            // wait
            Thread.sleep(5000);

            // 发送两次后退事件, 使得应用退到后台
            ProcessUtil.executeCommand("adb shell input keyevent " + BACK_PRESS_KEY) ;
            //TODO test 这里去掉一次后退,防止进程被杀,dump 失败
//            ProcessUtil.executeCommand("adb shell input keyevent " + BACK_PRESS_KEY) ;
            if ( config.enableForceGc && ProcessUtil.getPid() != ProcessUtil.INVALID_PID ) {
                // force gc (需要root设备)
                ProcessUtil.executeCommand(String.format("adb shell su -c \"kill -10 %d\"", ProcessUtil.getPid())) ;
            } else {
                System.out.println("Disable force gc !");
            }
            // wait
            Thread.sleep(5000);
        }

        File hprofFile = tryDumpHprof(config, config.hprofStorageDir);
        // 使用 sdcard 路径dump失败时, 尝试使用 /data/local/tmp/ 路径保存 hprof
        if ( (!hprofFile.exists() || hprofFile.length() <= 0 )
                && MMATConfig.DEFAULT_HPROF_DIR.equalsIgnoreCase(config.hprofStorageDir) ) {
            System.out.println("try to dump hprof in " + MMATConfig.ANDROID_LOCAL_TMP_DIR);
            hprofFile = tryDumpHprof(config, MMATConfig.ANDROID_LOCAL_TMP_DIR) ;
        }
        return hprofFile ;
    }

    private static File tryDumpHprof(MMATConfig config, String hprofStorageDir) throws InterruptedException {
        String hprofName = String.format("%s-dump-%s.hprof", config.packageName, DATE_FORMAT.format(new Date())) ;
        // hprof file path
        final String hprofFilePath = hprofStorageDir + hprofName ;
        // dump hprof
        ProcessUtil.executeCommand(String.format("adb shell am dumpheap %s %s", config.packageName, hprofFilePath)) ;
        Thread.sleep(config.dumpHprofWaitTime * 1000);

        // pull hprof to report dir
        ProcessUtil.executeCommand("adb pull " + hprofFilePath + " " + FileUtils.getHeapDumpDir().getAbsolutePath()) ;
        Thread.sleep(config.pullHprofWaitTime * 1000);
        // delete hprof in sdcard
        ProcessUtil.executeCommand(String.format("adb shell rm %s", hprofFilePath)) ;
        // hprof file
        return new File(FileUtils.getHeapDumpDir().getAbsolutePath(), hprofName) ;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy