使用ddmlib实现android 性能监控

 

使用ddmlib实现android 性能监控

http://blog.csdn.net/kittyboy0001/article/details/47317855

原理:

cpu

adb shell dumpsys cpuinfo packageName

memory

adb shell dumpsys meminfo packageName

流量

cat /proc/uid_stat/uidxxx/tcp_rcv 
cat /proc/uid_stat/uidxxx/tcp_snd 
其中 uid的获得通过 
adb shell dumpsys package packageName 
取得 userId=(\d+)\s

FPS刷新频率

这个计算稍微复杂,请参见源码:

sdk/sources/android-18/com/android/uiautomator/platform/SurfaceFlingerHelper.java(以android-18中的路径为例,其他版本中也有该文件,可能路径不同)

我写了个GUI

请联系 lizejun_qd@126.com,试用~ 
这里写图片描述

核心代码实现

控制器

public class AdbController {

    protected static Logger logger = LoggerFactory.getLogger(AdbController.class);
    protected static AndroidDebugBridge adb = null;


    private static boolean isInit = false;

    public AdbController() {
        init();
    }

    public void init()
    {
        if(!isInit) {
            AndroidDebugBridge.init(true);
            DdmPreferences.setTimeOut(20000);   
        //  adb = AndroidDebugBridge.createBridge();    //使用该方式,在没有启动adb时,会提示找不到
            adb = AndroidDebugBridge.createBridge(ConfigurationSettings.ADB_PATH, false);
            isInit = true;
        }
        waitDeviceList(adb);
    }

    private void waitDeviceList(AndroidDebugBridge bridge) {
        int count = 0;
        while (bridge.hasInitialDeviceList() == false) {
            try {
                Thread.sleep(100);
                count++;
            } catch (InterruptedException e) {
        }
        if (count > 100) {
            System.out.println("Fail to Init Device list");
            break;
        }
    }
}

public void terminate() {
    if(deviceTimer!= null){
        deviceTimer.clearService();
    }
    if(deviceListener!=null)
    AndroidDebugBridge.removeDeviceChangeListener(deviceListener);
    AndroidDebugBridge.terminate();
}

public IDevice[] getDevices() {
    return adb.getDevices();
}

public IDevice getCurrentDevice() {
    return deviceListener.mCurrentDevice;
}

public IDevice getDevice(String serialNum) {
    IDevice[] devices = adb.getDevices();
    int nums = devices.length;
    for (int i = 0; i < nums; i++) {
        String curSerialNum = devices[i].getSerialNumber();
        if (serialNum.equals(curSerialNum))
            return devices[i];
    }
    return null;
}

public String getCurrentActivity(String monitorPackage){
    SFActivityService sf = new SFActivityService(getCurrentDevice(), monitorPackage);
    sf.executeCmd();
    return sf.getCurActivity();
}


}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

CPU

/**
 * 使用 dumpsys cpuinfo xxx.package.name 获取Cpu的性能指标
 * @author zejun.lzj
 *
 */
public class CpuInfoService extends Observable implements AdbShellService {

protected IDevice device;

private CpuInfoReceiver receiver = null;

protected String monitorPackage;

public CpuInfoService(IDevice device,String monitorPackage) {
    this.device = device;
    this.monitorPackage = monitorPackage;
    receiver = new CpuInfoReceiver();           }


public Float getCpuRatio(){
    Float cpuRatio = receiver.getCpuRatio();
    if(cpuRatio == null || cpuRatio <0 ) 
        cpuRatio = -1f;
    return cpuRatio;
}

public void executeCmd() {
    if(StringUtils.isEmpty(monitorPackage))
        return;
    String cmd = "dumpsys cpuinfo " + monitorPackage;
    try {
        device.executeShellCommand(cmd, receiver);
    } catch (TimeoutException e) {
        logger.error(LOG_TAG,"TimeoutException");
        e.printStackTrace();
    } catch (AdbCommandRejectedException e) {
        logger.error(LOG_TAG,"AdbCommandRejectedException");
        e.printStackTrace();
    } catch (ShellCommandUnresponsiveException e) {
        logger.error(LOG_TAG,"ShellCommandUnresponsiveException");
        e.printStackTrace();
    } catch (IOException e) {
        logger.error(LOG_TAG,"IOException");
        e.printStackTrace();
    }
    Float ratio = getCpuRatio();
    if(ratio >=0.0){
        setChanged();
        notifyObservers(ratio);
    }
}

public void stop() {
    receiver.setCanceledFlag(true);             
}

private class CpuInfoReceiver extends MultiLineReceiver {


    private Pattern CPUINFO_MATCHER;

    private Float mCpuRatio = null;

    protected Boolean isCanceled = false;

    public CpuInfoReceiver() {
        super();
        CPUINFO_MATCHER = Pattern.compile(".*"+ monitorPackage);
    }


    public void setCanceledFlag(Boolean isCanceled) {
        this.isCanceled = isCanceled;
    }

    public Float getCpuRatio() {
        return mCpuRatio;
    }

    public boolean isCancelled() {
        return isCanceled;
    }

    @Override
    public void processNewLines(String[] lines) {
        for (String line : lines) {
            Matcher cpuMatch = CPUINFO_MATCHER.matcher(line);
            if (cpuMatch.find()) {
                try {
                    mCpuRatio = Float.parseFloat(cpuMatch.group().split("%")[0].trim());
                    break;
                } catch (NumberFormatException e) {
                    System.out.println(String.format("Failed to parse %s as an integer",
                            cpuMatch.group(1)));
                }
            }
        }
    }

}

public void clear() {

}

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107

内存

public class MemInfoService  extends Observable implements AdbShellService {

protected IDevice device;

private MemInfoReceiver receiver = new MemInfoReceiver();
protected String monitorPackage;

public MemInfoService(IDevice device,String monitorPackage) {
    this.device = device;
    this.monitorPackage = monitorPackage;

    this.addObserver(DataManager.getInstance().getMemObserver());
}

/**
 * 从Receive中拿到内存数据
 * @return
 */
public Float getMemInfo(){
    Float memInfo = receiver.getMemInfo();
    if(memInfo == null || memInfo <0 ) 
        memInfo = -1f;
    return memInfo/1000;
}

public Float getDalvInfo(){
    Float dalvInfo = receiver.getDalvInfo();
    if(dalvInfo == null || dalvInfo <0 ) 
        dalvInfo =-1f;
    return dalvInfo/1000;
}

public Float getNativeInfo () {
    Float nativeInfo = receiver.getNativeInfo();
    if (nativeInfo == null || nativeInfo < 0) 
        nativeInfo = -1f;

    return nativeInfo / 1000;
}

public void executeCmd() {

    if(StringUtils.isEmpty(monitorPackage))
        return;
    String cmd = "dumpsys meminfo " + monitorPackage;
    try {
        device.executeShellCommand(cmd, receiver);
    } catch (TimeoutException e) {
        logger.error(LOG_TAG,"TimeoutException");
        e.printStackTrace();
    } catch (AdbCommandRejectedException e) {
        logger.error(LOG_TAG,"AdbCommandRejectedException");
        e.printStackTrace();
    } catch (ShellCommandUnresponsiveException e) {
        logger.error(LOG_TAG,"ShellCommandUnresponsiveException");
        e.printStackTrace();
    } catch (IOException e) {
        logger.error(LOG_TAG,"IOException");
        e.printStackTrace();
    }
    Float ratio1 = getMemInfo();
    Float ratio2 = getDalvInfo();
    Float ratio3 = getNativeInfo();
    if(ratio1>0 && ratio2>0 && ratio3 >0){
        setChanged();
        notifyObservers(new DataManager.MEM_DATA(ratio1,ratio2,ratio3));
    }
}

private static final  class MemInfoReceiver extends MultiLineReceiver {
    protected Boolean isCanceled = false;
    public MemInfoReceiver() {
        super();
    }

    public void setCanceledFlag(Boolean isCanceled) {
        this.isCanceled = isCanceled;
    }

    public Float getMemInfo() {
        return memInfo;
    }

    public Float getDalvInfo() {
        return dalvInfo;
    }

    public Float getNativeInfo() {
        return nativeInfo;
    }

    private static final String DALVIK_MATCHER = "Dalvik";
    private static final String NATIVE_MATCHER = "Native";
    private static final String TOTAL_MATCHER = "TOTAL";

    protected Float memInfo = null;
    protected Float dalvInfo = null;
    protected Float nativeInfo = null;

    public boolean isCancelled() {
        return isCanceled;
    }
    private List<String> tem = new ArrayList<String>();
    @Override
    public void processNewLines(String[] lines) {
        for(String line:lines) { //将输出的数据缓存起来
            tem.add(line);
            if(line.contains(TOTAL_MATCHER)) {
                getMemInfo(tem);
                tem.clear();
            }
        }
    }

    public void getMemInfo(List<String> lines) {
        //这里使用的arrNative[index] ,index会有不同,原因是不同的版本输出的信息,有的叫Dalvik,有得叫Dalvik Heap
        for (String line : lines) {

            if (line.contains(NATIVE_MATCHER)) {
                String[] arrNative = line.split("\\s+");
                if (9 == arrNative.length) {
                    nativeInfo = Float.parseFloat(arrNative[7]);
                }
                else if ( 8 == arrNative.length) {
                    nativeInfo = Float.parseFloat(arrNative[6]);
                }
                else if ( 7 == arrNative.length) {
                    nativeInfo = Float.parseFloat(arrNative[5]);
                }
                continue;
            }
            if(line.contains(DALVIK_MATCHER)){
                String[] arrDalvik = line.split("\\s+");
                if (9 == arrDalvik.length) {
                    dalvInfo = Float.parseFloat(arrDalvik[7]);
                } else if (8 == arrDalvik.length) {
                    dalvInfo = Float.parseFloat(arrDalvik[6]);
                } else if ( 7 == arrDalvik.length) {
                    dalvInfo = Float.parseFloat(arrDalvik[5]);
                }
                continue;
            }
            if(line.contains(TOTAL_MATCHER)){
                String arrTotal[] = line.split("\\s+");
                if (8 == arrTotal.length) {
                    memInfo = Float.parseFloat(arrTotal[6]);
                }else if ( 7 == arrTotal.length) {
                    memInfo = Float.parseFloat(arrTotal[5]);
                }
                break;
            }
        }
    }
}

public void stop() {
    receiver.setCanceledFlag(true);             
}

public void clear() {
}

}   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164

FPS

/**
 * dumpsys SurfaceFlinger --latency
 */
public class SFLatencyService  extends Observable implements AdbShellService{
    private static String FRAME_LATENCY_CMD = "dumpsys SurfaceFlinger --latency";
    protected IDevice device;
    private String monitorPackage;
    private String windowName;

protected Logger logger = LoggerFactory.getLogger(this.getClass());
private static final String LOG_TAG = "[SFLatencyService]-";

public SFLatencyService(IDevice device,String monitorPackage) {
    this.device = device;
    this.monitorPackage = monitorPackage;
    clearSFBuffer();
    receiver = new LatencyReceiver(this);
    this.addObserver(DataManager.getInstance().getFpsObserver());
}

private LatencyReceiver receiver = null;

private Float laterFps;

public Float getLaterFps() {
    return laterFps;
}

public void setLaterFps(Float laterFps) {
    this.laterFps = laterFps;
    if(laterFps>0){
        setChanged();
        notifyObservers(new DataManager.FPS_DATA(windowName,laterFps));
    }
}

public String getActivity(){
    SFActivityService sf = new SFActivityService(device,monitorPackage);
    sf.executeCmd();
    return sf.getCurActivity();
}

public void clearSFBuffer(){
    SFClearService sc = new SFClearService(device);
    sc.executeCmd();
}

public void executeCmd() {
    if(StringUtils.isEmpty(monitorPackage))
        return;
    windowName = getActivity();
    String command = FRAME_LATENCY_CMD;

    command = String.format("%s %s", FRAME_LATENCY_CMD, windowName);
    try {
        device.executeShellCommand(command,receiver);
    } catch (TimeoutException e) {
        logger.error(LOG_TAG,"TimeoutException");
        e.printStackTrace();
    } catch (AdbCommandRejectedException e) {
        logger.error(LOG_TAG,"AdbCommandRejectedException");
        e.printStackTrace();
    } catch (ShellCommandUnresponsiveException e) {
        logger.error(LOG_TAG,"ShellCommandUnresponsiveException");
        e.printStackTrace();
    } catch (IOException e) {
        logger.error(LOG_TAG,"IOException");
        e.printStackTrace();
    }
}


public void stop() {    
}

private static final  class LatencyReceiver extends MultiLineReceiver {
    SFLatencyService parent;

    public LatencyReceiver(SFLatencyService parent) {
        this.parent = parent;
    }
    protected Float fps = 0f;
    private static int BUFFER_SIZE = 128;
    private static int BUFFER_NUMBER = 3;

    /* An array list which includes the raw buffer information from frame latency tool */
    private static List<List<String>> mFrameBufferData = new ArrayList<List<String>>(BUFFER_SIZE);

    /* Record the refresh period returned from driver */
    //private static long mRefreshPeriod = -1;

    // Symbol of unfinished frame time */
    public static final String PENDING_FENCE_TIME = new Long(Long.MAX_VALUE).toString();
    private static final Pattern CHECK_MATCHER = Pattern.compile("^[\\d\\s]+$");

    public boolean isCancelled() {
        return false;
    }

    public void clearBuffer(){
        mFrameBufferData.clear();
    }

    @Override
    public void processNewLines(String[] lines) {
        if(lines.length<2)
            return;
        for(String line:lines){
            Matcher matcher = CHECK_MATCHER.matcher(line);
            if(!matcher.matches()) continue;

            String[] bufferValues = line.split("\\s+");

            if(bufferValues.length==1){
                if(line.trim().isEmpty())
                    continue;

                if(mFrameBufferData.isEmpty()) {
                    //mRefreshPeriod = Long.parseLong(line.trim());
                    continue;
                } else {
                    fps = (float)getFrameRate();
                    parent.setLaterFps(fps);
                    //mRefreshPeriod = Long.parseLong(line.trim());
                    clearBuffer();
                    continue;
                }
            }

            if(bufferValues.length!=BUFFER_NUMBER)
                return;

            if (bufferValues[0].trim().compareTo("0") == 0) {
                continue;
            } else if (bufferValues[1].trim().compareTo(PENDING_FENCE_TIME) == 0 ) {
                System.out.println(LOG_TAG + "the data contains unfinished frame time");
                continue;
            }
            List<String> delayArray = Arrays.asList(bufferValues);
            mFrameBufferData.add(delayArray);               
        } 
    }


    /**
     * Calculate frame rate
     * @return
     */
    public static double getFrameRate() {
        int mFrameLatencySampleSize = mFrameBufferData.size() - 1;
        long startTime = Long.parseLong(mFrameBufferData.get(0).get(1));
        long endTime =  Long.parseLong(mFrameBufferData.get(mFrameLatencySampleSize).get(1));
        long totalDuration = endTime - startTime;
        return (double)((mFrameLatencySampleSize - 1) * Math.pow(10, 9))/totalDuration;
    }
}


public void clear() {
    if(receiver!=null)
        receiver.clearBuffer();
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164

流量

public class TrafficInfoService extends Observable implements AdbShellService {

protected IDevice device;
protected String uid = null;

private TrafficReceiver receiver;
private String monitorPackage;

Integer rcv = null;
Integer snd = null;
Integer flow = null;

private static Integer firstRcv = null;
private static Integer firstSnd = null;

public Integer getRcv() {
    return rcv;
}

public Integer getSnd() {
    return snd;
}

public Integer getFlow() {
    return flow;
}


public TrafficInfoService(IDevice device, String monitorPackage) {
    this.device = device;
    receiver = new TrafficReceiver();
    this.monitorPackage = monitorPackage;
    this.addObserver(DataManager.getInstance().getFlowObserver());

} 

protected String getUid(){
    UidService service = new UidService(device,monitorPackage);
    service.executeCmd();
    return service.getUid();
}

public void executeCmd(){
    if(StringUtils.isEmpty(monitorPackage))
        return;
    if(uid == null)
        uid = getUid();
    String cmd = String.format("cat /proc/uid_stat/%s/tcp_rcv;cat /proc/uid_stat/%s/tcp_snd", uid,uid);
    try {
        device.executeShellCommand(cmd, receiver);
    } catch (TimeoutException e) {
        logger.error(LOG_TAG,"TimeoutException");
        e.printStackTrace();
    } catch (AdbCommandRejectedException e) {
        logger.error(LOG_TAG,"AdbCommandRejectedException");
        e.printStackTrace();
    } catch (ShellCommandUnresponsiveException e) {
        logger.error(LOG_TAG,"ShellCommandUnresponsiveException");
        e.printStackTrace();
    } catch (IOException e) {
        logger.error(LOG_TAG,"IOException");
        e.printStackTrace();
    }
    notifyData();
}

public void notifyData(){
    rcv = receiver.getRcv();
    snd = receiver.getSnd();
    if(rcv <0 || snd<0)
        return;

    if(firstRcv == null) firstRcv = rcv;
    if(firstSnd == null) firstSnd = snd;
    rcv = rcv - firstRcv;
    snd = snd - firstSnd;
    flow = rcv + snd;
    setChanged();
    notifyObservers(new DataManager.FLOW_DATA(flow,snd,rcv));
}

public void stop() {
    receiver.setCanceledFlag(true);
    firstRcv = null;
    firstSnd = null;
}

private static final  class TrafficReceiver extends MultiLineReceiver {
    private Integer mrcv = null;
    private Integer msnd = null;

    protected Boolean isCanceled = false;

    public void setCanceledFlag(Boolean isCanceled) {
        this.isCanceled = isCanceled;
    }

    public Integer getRcv() {
        if(mrcv == null || mrcv <0)
            mrcv = -1;
        return mrcv;
    }

    public Integer getSnd() {
        if(msnd == null || msnd <0)
            msnd = -1;
        return msnd;
    }
    public boolean isCancelled() {
        return isCanceled;
    }

    @Override
    public void processNewLines(String[] lines) {
        //System.out.println(lines[0]);
        if(lines.length<2) return;
        try{
            mrcv = Integer.parseInt(lines[0].trim())/1000;
            msnd = Integer.parseInt(lines[1].trim())/1000;
        } catch (NumberFormatException e) {
            System.out.println(LOG_TAG + String.format(":Failed to parse %s to traffic",
                    lines[0]+lines[1]));
        }
    }
}

public void clear() {
    firstRcv = null;
    firstSnd = null;
}

}   



posted @ 2018-01-07 12:13  sky20080101  阅读(264)  评论(0)    收藏  举报