性能测试工具 源码解读腾讯 GT 的性能测试方案

李雷雷 · 2017年06月21日 · 最后由 Heyniu 回复于 2017年07月11日 · 6981 次阅读
本帖已被设为精华帖!

机智的防爬虫标识
原创博客地址:https://testerhome.com/topics/9094
博客求关注: http://www.cnblogs.com/alexkn

前言

本文将整理腾讯 GT 各个性能测试项的测试方法,目的是为了帮助移动性能专项测试同学快速过一遍腾讯 GT 各个性能数据是如何获取的。
另外对腾讯 GT 还不了解或者不知道它能做什么的同学可以看看这篇文章:https://testerhome.com/topics/9092

一.GT 性能测试方案之 CPU 测试

1.简要流程

  • 初始化 cpu 的数据
  • 提供了两种方法获取 CPU 数据 getCpuUsage: 整机的 CPU 使用水平,主要用于实时刷新 GT 上的 CPU 数据。通过读取/proc/stat 的数据,将每一个核的 cpu 使用跟闲置数据提取。使用率永远是增量式计算。计算方法为 100*(cpu 忙时增量-cpu 整体增量),从计算方法来看,可能会导致负数出现。 getProcessCpuUsage:计算进程的 CPU 使用率,主要通过"/proc/" + pid + "/stat"来计算,在这里回京过一系列计算,拿到进程的 CPU 时间片

2.代码流程

  • cpu 数据初始化 经过初始化,让 CPU 整体使用跟进程的 CPU 占用都为 0
public CpuUtils() {
   initCpuData();
}

private void initCpuData() {
   pCpu = o_pCpu = 0.0;
   aCpu = o_aCpu = 0.0;

}
  • 通过不同的调用栈,来监控不同的 CPU 数据
    整体 CPU 使用率:由于 getCpuUsage 是通过后台线程不断刷新来实现的,因此,o_cpu/o_idle 数据不断在实时更新

    RandomAccessFile reader = null;
    try {
    reader = new RandomAccessFile("/proc/stat", "r");
    String load;
    load = reader.readLine();
    String[] toks = load.split(" ");
    double c_idle = Double.parseDouble(toks[5]);
    double c_cpu = Double.parseDouble(toks[2])
         + Double.parseDouble(toks[3])
         + Double.parseDouble(toks[4])
         + Double.parseDouble(toks[6])
         + Double.parseDouble(toks[8])
         + Double.parseDouble(toks[7]);
    if (0 != ((c_cpu + c_idle) - (o_cpu + o_idle))) {
      // double value = (100.00 * ((c_cpu - o_cpu) ) / ((c_cpu +
      // c_idle) - (o_cpu + o_idle)));
      usage = DoubleUtils.div((100.00 * ((c_cpu - o_cpu))),
            ((c_cpu + c_idle) - (o_cpu + o_idle)), 2);
      // Log.d("CPU", "usage: " + usage);
      if (usage < 0) {
         usage = 0;
      }
      else if (usage > 100)
      {
         usage = 100;
      }
      // BigDecimal b = new BigDecimal(Double.toString(value));
    
      // usage = b.setScale(2,
      // BigDecimal.ROUND_HALF_UP).doubleValue();
      // Log.d("CPU", "usage: " + usage);
    }
    o_cpu = c_cpu;
    o_idle = c_idle;
    } catch (IOException e) {
    e.printStackTrace();
    } finally {
    FileUtil.closeRandomAccessFile(reader);
    }
    
  • 进程的 CPU 使用时间片获取

public String getProcessCpuUsage(int pid) {

   String result = "";
   String[] result1 = null;
   String[] result2 = null;
   if (pid >= 0) {

      result1 = getProcessCpuAction(pid);
      if (null != result1) {
         pCpu = Double.parseDouble(result1[1])
               + Double.parseDouble(result1[2]);
      }
      result2 = getCpuAction();
      if (null != result2) {
         aCpu = 0.0;
         for (int i = 2; i < result2.length; i++) {

            aCpu += Double.parseDouble(result2[i]);
         }
      }
      double usage = 0.0;
      if ((aCpu - o_aCpu) != 0) {
         usage = DoubleUtils.div(((pCpu - o_pCpu) * 100.00),
               (aCpu - o_aCpu), 2);
         if (usage < 0) {
            usage = 0;
         }
         else if (usage > 100)
         {
            usage = 100;
         }

      }
      o_pCpu = pCpu;
      o_aCpu = aCpu;
      result = String.valueOf(usage) + "%";
   }
   p_jif = pCpu;
   return result;
}

二.GT 性能测试方案之内存测试

1.简要流程

内存测试主要通过 handleMessage 来触发,根据 msg 参数来决定任务执行

  • 内存数据获取:仅仅简单通过 dumpsys meminfo 来获取内存数据,然后通过解析来得到 pss_Native/naticeHeapSize/naticeAllocated/pss_OtherDev/pss_graphics/pss_gl/pss_UnKnown/pss_total 数据
  • dumpHeap:通过 am dumpheap 来获取 heap 文件
  • GC:直接 kill -10 $pid 来完成 GC

2.测试方法

  • 内存数据获取
public static MemInfo getMemInfo(String packageName)
{
   MemInfo result = null;
   String resultString = null;
   try {
      resultString = runCMD("dumpsys meminfo " + packageName);
   } catch (Exception e) {
      e.printStackTrace();
      return MemInfo.EMPTY;
   }

   if(Env.API < 14)
   {
      result = parseMemInfoFrom2x(resultString);
   }
   else if (Env.API < 19)
   {
      result = parseMemInfoFrom4x(resultString);
   }
   else
   {
      result = parseMemInfoFrom44(resultString);
   }

   return result;
}
  • dumpHeap
private void dumpHeap() {
   String pid = String.valueOf(ProcessUtils
         .getProcessPID(AUTManager.pkn.toString()));

   if (!pid.equals("-1")) {
      boolean isSucess = true;
      ProcessBuilder pb = null;

      String sFolder = Env.S_ROOT_DUMP_FOLDER + AUTManager.pkn.toString() + "/";
      File folder = new File(sFolder);
      if (!folder.exists())
      {
         folder.mkdirs();
      }

      String cmd = "am dumpheap " + pid + " "// 命令
            + Env.S_ROOT_DUMP_FOLDER + AUTManager.pkn.toString() + "/"// 输出路径
            + "dump_" + pid + "_" + GTUtils.getSaveDate() + ".hprof"; // 输出文件名
      pb = new ProcessBuilder("su", "-c", cmd);

      Process exec = null;

      pb.redirectErrorStream(true);
      try {
         exec = pb.start();

         InputStream is = exec.getInputStream();
         BufferedReader reader = new BufferedReader(
               new InputStreamReader(is));

         while ((reader.readLine()) != null) {
            isSucess = false;
         }
      } catch (Exception e) {
         e.printStackTrace();
         isSucess = false;
      }
      // 至此命令算是执行成功
      if (isSucess)
      {
         handler.sendEmptyMessage(6);
      }

   } else {
      Log.d("dump error", "pid not found!");
   }
}
  • GC
private void gc() {
   String pid = String.valueOf(ProcessUtils
         .getProcessPID(AUTManager.pkn.toString()));

   if (!pid.equals("-1")) {
      boolean isSucess = true;
      ProcessBuilder pb = null;

      String cmd = "kill -10 " + pid;
      pb = new ProcessBuilder("su", "-c", cmd);

      Process exec = null;

      pb.redirectErrorStream(true);
      try {
         exec = pb.start();

         InputStream is = exec.getInputStream();
         BufferedReader reader = new BufferedReader(
               new InputStreamReader(is));

         while ((reader.readLine()) != null) {
            isSucess = false;
         }
      } catch (Exception e) {
         e.printStackTrace();
         isSucess = false;
      }
      // 至此命令算是执行成功
      if (isSucess)
      {
         handler.sendEmptyMessage(5);
      }

   } else {
      Log.d("gc error", "pid not found!");
   }
}

三.GT 性能测试方案之内存填充

1.简要流程

内存填充,主要是通过调用系统的 malloc 来分配内存。内存释放,则是通过系统 free 来释放。

2. 代码流程

  • 内存分配以及释放的函数
const int BASE_SIZE = 1024*1024; // 1M

int fill(int blockNum)
{
    int memSize = blockNum * BASE_SIZE;
    p = (char *)malloc(memSize);
    int i;
    for (i = 0; i < memSize; i++)
    {
        p[i] = 0;
    }
    return 0;
}

int freeMem()
{
    free(p);
    return 0;
}
  • 加载 com_tencent_wstt_gt_api_utils_MemFillTool.c,并提供度应对操作接口
public class MemFillTool {

   public MemFillTool() {
   }

   public static MemFillTool instance = null;

   public static MemFillTool getInstance() {
      if (instance == null) {
         System.loadLibrary("mem_fill_tool");
         instance = new MemFillTool();
      }
      return instance;
   }

   // 填充xxxMB内存
   public native int fillMem(int blockNum);

   // 释放刚才填充的内存
   public native int freeMem();
}

四.GT 性能测试方案之帧率测试

1.简要流程

FPS 数据收集是一个定时任务(4.3 后 1s 一次),通过异步线程中不断获取 FPS 数据来刷新到前端页面。而广播模式调用,则直接从缓存的 field 中获取数据即可。
在这里 GT 获取 fps 数据,也是通过采用 surfaceflinger 来获取,但我感觉好像是有问题的。因为,一般 surfaceflinger 数据获取的命令是adb shell dumpsys SurfaceFlinger --latency <window name>在这里直接定义了把"service call SurfaceFlinger 1013"字符串写到流里,没看明白这个操作跟帧率获取有什么关系。刚去了解了下,Runtime.getRuntime()原来执行多条命令时后续只要拿到processDataOutputStream对象,继续writeBytes就可以保证是在同一个上下文中执行多条命令了。

2.代码流程

  • 判断当前 root 状态,如果没有 root 直接返回,避免消耗系统资源
if (! GTFrameUtils.isHasSu())
{
   return;
}
  • 计算一个周期内的帧率数据,由于比较简单(除了在 1 中 surfaceflinger 数据存疑外),直接把核心代码发出来
startTime = System.nanoTime();
if (testCount == 0) {
   try {
      lastFrameNum = getFrameNum();
   } catch (IOException e) {
      e.printStackTrace();
   }
}
int currentFrameNum = 0;
try {
   currentFrameNum = getFrameNum();
} catch (IOException e) {
   e.printStackTrace();
}
int FPS = currentFrameNum - lastFrameNum;
if (realCostTime > 0.0F) {
   int fpsResult = (int) (FPS * 1000 / realCostTime);
   defaultClient.setOutPara("FPS", fpsResult);
}
lastFrameNum = currentFrameNum;

testCount += 1;
  • 帧率获取的部分也发一下。另外感谢@codeskyblue 的指点,service call SurfaceFlinger 1013这个命令是获取系统的总的刷新帧率(返回的是 16 进制)
public static synchronized int getFrameNum() throws IOException {
   String frameNumString = "";
   String getFps40 = "service call SurfaceFlinger 1013";

   if (process == null)
   {
      process = Runtime.getRuntime().exec("su");
      os = new DataOutputStream(process.getOutputStream());
      ir = new BufferedReader(
            new InputStreamReader(process.getInputStream()));
   }

   os.writeBytes(getFps40 + "\n");
   os.flush();

   String str = "";
   int index1 = 0;
   int index2 = 0;
   while ((str = ir.readLine()) != null) {
      if (str.indexOf("(") != -1) {
         index1 = str.indexOf("(");
         index2 = str.indexOf("  ");

         frameNumString = str.substring(index1 + 1, index2);
         break;
      }
   }

   int frameNum;
   if (!frameNumString.equals("")) {
      frameNum = Integer.parseInt(frameNumString, 16);
   } else {
      frameNum = 0;
   }
   return frameNum;
}

五.GT 性能测试方案之流畅度测试

1.简要流程

腾讯的流畅度测试比较简单粗暴,测试方式是通过初始化 choreographer 日志级别,生成 Choreographer 日志来得到当前操作的丢帧。通过一系列计算后来计算流畅度。

2.测试方法

  • 执行setprop debug.choreographer.skipwarning 1
View.OnClickListener button_write_property = new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            String cmd = "setprop debug.choreographer.skipwarning 1";
            ProcessBuilder execBuilder = new ProcessBuilder("su", "-c", cmd);
            execBuilder.redirectErrorStream(true);
            try {
                execBuilder.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };
  • 执行getprop debug.choreographer.skipwarning判断,为 1 则可以进行测试
View.OnClickListener button_check_status = new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            String cmd = "getprop debug.choreographer.skipwarning";
            ProcessBuilder execBuilder = new ProcessBuilder("sh", "-c", cmd);
            execBuilder.redirectErrorStream(true);
            try {
                TextView textview = (TextView) findViewById(R.id.textviewInformation);
                Process p = execBuilder.start();
                InputStream is = p.getInputStream();
                InputStreamReader isr = new InputStreamReader(is);
                BufferedReader br = new BufferedReader(isr);
                Boolean flag = false;
                String line;
                while ((line = br.readLine()) != null) {
                    if (line.compareTo("1") == 0) {
                        flag = true;
                        break;
                    }
                }

                if (flag) {
                    textview.setText("OK");
                } else {
                    textview.setText("NOT OK");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    };
  • 执行adb logcat -v time -s Choreographer:I *:S
  • 过滤获取当前 pid 丢帧值
protected void onHandleIntent(Intent intent) {
        try {

            String str = intent.getStringExtra("pid");
            int pid = Integer.parseInt(str);

            List<String> args = new ArrayList<String>(Arrays.asList("logcat", "-v", "time", "Choreographer:I", "*:S"));

            dumpLogcatProcess = RuntimeHelper.exec(args);
            reader = new BufferedReader(new InputStreamReader(dumpLogcatProcess.getInputStream()), 8192);

            String line;

            while ((line = reader.readLine()) != null && !killed) {

                // filter "The application may be doing too much work on its main thread."
                if (!line.contains("uch work on its main t")) {
                    continue;
                }
                int pID = LogLine.newLogLine(line, false).getProcessId();
                if (pID != pid){
                    continue;
                }

                line = line.substring(50, line.length() - 71);
                Integer value = Integer.parseInt(line.trim());

                SMServiceHelper.getInstance().dataQueue.offer(value);
            }
        } catch (IOException e) {
            Log.e(TAG, e.toString() + "unexpected exception");
        } finally {
            killProcess();
        }
    }
  • 数据处理得到 sm 值 腾讯这边的处理方案是:当丢帧<60 时,流畅度 SM =60-frame; 当丢帧 frame>60 时,流畅度 SM = 60-frame%60。不过这种处理方式是有问题的。在这里要先说下流畅度计算的原理:
    • VSync 机制可以通过其 Loop 来了解当前 App 最高绘制能力,固定每隔 16.6ms 执行一次,这样最高的刷新的帧率就控制在 60FPS 以内,Choreographer 日志可以打印当前丢帧数,因此通过计算,得到当前 APP 的流畅度。
    • 而计算这样来计算可能会更加准确(个人看法,欢迎讨论): SM= 60-丢帧 frame/每两行同一线程的丢帧时间差(单位:s),如果只关心 UI 线程,那就只需要统计 UI 线程即可。
while (true) {
    if (pause) {
        break;
    }
    int x = count.getAndSet(0);
    // 卡顿大于60时,要将之前几次SM计数做修正
    if (x > 60) {
        int n = x / 60;
        int v = x % 60;
        TagTimeEntry tte = OpPerfBridge.getProfilerData(key);
        int len = tte.getRecordSize();
        // 补偿参数
        int p = n;
        //Math.min(len, n);
        /*
        * n > len是刚启动测试的情况,日志中的亡灵作祟,这种情况不做补偿;
        * 并且本次也记为60。本逻辑在两次测试间会清理数据的情况生效。
        */
        if (n > len) {
            globalClient.setOutPara(key, 60);
//          globalClient.setOutPara(SFKey, 0);
        } else {
            for (int i = 0; i < p; i++) {
            TimeEntry te = tte.getRecord(len - 1 - i);
            te.reduce = 0;
            }
        globalClient.setOutPara(key, v);
//      globalClient.setOutPara(SFKey, 60 - v);
        }
    } else {
        int sm = 60 - x;
        globalClient.setOutPara(key, sm);
//      globalClient.setOutPara(SFKey, x);
    }

六.GT 性能测试方案之流量测试

1.简要流程

流量测试有三种方案,默认采用方案 1

  • 通过读取"/proc/uid_stat/" + uid + "/tcp_snd"获取发送跟接收流量
  • 直接调用 android 的api:TrafficStats.getUidTxBytes(uid)来获取流量数据(该方法号称是获取到指定 uid 发送流量的总和,但实测情况是只有 tcp 层的流量)
  • 第三种方案居然空在那里,那实际上只有两种方案

2.代码流程

  • 初始化流量
public void initProcessNetValue(String pName) {

   p_t_base = getOutOctets(pName);
   p_r_base = getInOctets(pName);

   p_t_add = 0;
   p_r_add = 0;
}

其中 getOutOctets/getInOctets 具体对应什么方法,需要看设备是不是支持 uid 流量数据获取

  • 获取增加的流量
public String getProcessNetValue(String pName) {
   StringBuffer sb = new StringBuffer();

   java.text.DecimalFormat df = new java.text.DecimalFormat("#.##");
   p_t_cur = getOutOctets(pName);
   p_r_cur = getInOctets(pName);
   p_t_add = (p_t_cur - p_t_base) / B2K;
   p_r_add = (p_r_cur - p_r_base) / B2K;

   sb.append("t");
   sb.append(df.format(p_t_add));
   sb.append("KB|r");
   sb.append(df.format(p_r_add));
   sb.append("KB");

   return sb.toString();
}
  • 矫正处理

// modify on 20120616 过滤有的手机进程流量偶尔输出负数的情况
if ((nowT != lastT || nowR != lastR) && nowT >= 0 && nowR >= 0) {
   OpPerfBridge.addHistory(op, value, new long[]{(long) nowT, (long) nowR});
}

return value;

七.GT 性能测试方案之电量测试

1.简单流程

  • 关注指标:
    电量测试关注的指标有四个: 电流,电压,电量跟温度。
  • 数据获取方式:
    通过ReadPowerTimerTask任务去 set 关注的电量指标,当 update 方法调用时,才把数据 set 进去。电量数据调用的系统命令/sys/class/power_supply/battery/uevent
  • 具体流程:
    • 接收"com.tencent.wstt.gt.plugin.battery.startTest"广播后,update 各个指标
    • 注册跟设置出参,并设置刷新频率跟初始化屏幕电量
    • 开启定时任务ReadPowerTimerTask,这个任务的作用就是去获取/sys/class/power_supply/battery/uevent下的电量数据并解析,最后设置出参。刷新频率默认是 250ms 一次
    • 最后 stop 时,销毁异步定时任务

2.代码流程

整个生命周期如下,当BATTERY_START_TEST行为被捕获时,开始执行电量测试

String action = intent.getAction();
if (action == null) return;
if (action.equals(BATTERY_START_TEST)) {
   int refreshRate = intent.getIntExtra("refreshRate", 250);
   int brightness = intent.getIntExtra("brightness", 100);

   boolean updateI = intent.getBooleanExtra("I", true);
   GTBatteryEngine.getInstance().updateI(updateI);

   boolean updateU = intent.getBooleanExtra("U", false);
   GTBatteryEngine.getInstance().updateU(updateU);

   boolean updateT = intent.getBooleanExtra("T", false);
   GTBatteryEngine.getInstance().updateT(updateT);

   boolean updateP = intent.getBooleanExtra("P", false);
   GTBatteryEngine.getInstance().updateP(updateP);

   GTBatteryEngine.getInstance().doStart(refreshRate, brightness);
} else if (action.equals(BATTERY_END_TEST)) {
   GTBatteryEngine.getInstance().doStop();
}
  • update 操作很简单,只是注册跟 set 出参
public void updateI(boolean isChecked)
{
   if (isChecked)
   {
      globalClient.registerOutPara(GTBatteryEngine.OPI, "I");
      globalClient.setOutparaMonitor(GTBatteryEngine.OPI, true);
   }
   else
   {
      globalClient.unregisterOutPara(GTBatteryEngine.OPI);
   }
   state_cb_I = isChecked;
   GTPref.getGTPref().edit().putBoolean(GTBatteryEngine.KEY_I, isChecked).commit();

   for (BatteryPluginListener listener : listeners)
   {
      listener.onUpdateI(isChecked);
   }
}
  • 初始化操作略过,这里展示异步任务,处理流程直接看下面的关键代码即可,方法在GTBatteryEngine.java
timer = new Timer(true);
timer.schedule(new ReadPowerTimerTask(), refreshRate, refreshRate);

@Override
public void run() {

   BufferedReader br = null;
   try {
      FileReader fr = new FileReader(f);
      br = new BufferedReader(fr);
      String line = "";
      while((line = br.readLine()) != null){

         int found = 0;
         if (line.startsWith("POWER_SUPPLY_VOLTAGE_NOW="))
         {
            U = line.substring(line.lastIndexOf("=") + 1);
            // since 2.1.1 从μV转成mV
            long volt = Long.parseLong(U) / 1000;
            globalClient.setOutPara(OPU, volt + "mV");

            OutPara op = globalClient.getOutPara(OPU);
            if (null != op)
            {
               OpPerfBridge.addHistory(op, U, volt);
            }

            found++;
         }
         if (line.startsWith("POWER_SUPPLY_CURRENT_NOW="))
         {
            I = line.substring(line.lastIndexOf("=") + 1);
            // since 2.1.1 从μA转成mA since 2.2.4 华为本身就是mA
            long current = Long.parseLong(I);
            if (isHuawei)
            {
               current = -current;
            }
            else if (isLGg3)
            {
               current = current >> 1; // 经验值估算LG g3的数据除以2后比较接近真实
            }
            else
            {
               current = current / 1000;
            }
            globalClient.setOutPara(OPI, current + "mA");

            OutPara op = globalClient.getOutPara(OPI);
            if (null != op)
            {
               OpPerfBridge.addHistory(op, I, current);
            }

            found++;
         }
         if (line.startsWith("POWER_SUPPLY_CAPACITY="))
         {
            String lastBattery = POW;
            POW =  line.substring(line.lastIndexOf("=") + 1);
            if (! lastBattery.equals(POW)) // 电池百分比变化了
            {
               if (startBattry != -1)
               {
                  lastBatteryChangeTime = (System.currentTimeMillis() - startBattry)/1000 + "s";
                  String tempValue = POW + "% | -1% time:" + lastBatteryChangeTime;
                  globalClient.setOutPara(OPPow, tempValue);
                  GTLog.logI(LOG_TAG, tempValue);
                  // 将电量加入历史记录
                  OutPara op = globalClient.getOutPara(OPPow);
                  if (null != op)
                  {
                     OpPerfBridge.addHistory(op, tempValue, Long.parseLong(POW));
                  }
               }

               startBattry = System.currentTimeMillis();
            }

            globalClient.setOutPara(OPPow, POW + "% | -1% time:" + lastBatteryChangeTime);
            found++;
         }
         if (line.startsWith("POWER_SUPPLY_TEMP="))
         {
            TEMP = line.substring(line.lastIndexOf("=") + 1);
            int iTemp = Integer.parseInt(TEMP);
            iTemp = iTemp/10;
            if (iTemp > -273)
            {
               TEMP = iTemp + "℃";
            }

            globalClient.setOutPara(OPTemp, TEMP);

            OutPara op = globalClient.getOutPara(OPTemp);
            if (null != op && iTemp != INT_TEMP)
            {
               OpPerfBridge.addHistory(op, TEMP, iTemp);
               GTLog.logI(LOG_TAG, TEMP);
               INT_TEMP = iTemp;
            }

            found++;
         }
         if (found >= 4)
         {
            return;
         }

      }
   } catch (Exception e) {
      doStop();
   }
   finally
   {
      FileUtil.closeReader(br);
   }
}
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 22 条回复 时间 点赞

很腻害的样子哦

很棒 先马一下

如果抓取性能测试数据的方法能集成到 macaca 中去,倒是挺好的,想要获取对应的性能数据,就抓一次就好了。
@xdf

bauul 回复

GT Tools 支持 AndroidJUnit 的测试脚本用于性能指标的采集和数据监控,但是也需要你自己去调用对应的 API。
另一种集成方式就是自己简单封装一层,用广播模式调用 GT:http://gt.qq.com/docs/a/UseGtWithBroadcast.txt ,然后去驱动自己的用例,我在这里有一个整体介绍:https://testerhome.com/topics/9092

李雷雷 GT-非官方介绍 中提及了此贴 06月22日 08:20

service call SurfaceFlinger 1013 这个命令是获取系统的总的刷新帧率(返回的是 16 进制),另外这个还需要 root 之后才行

思寒_seveniruby 将本帖设为了精华贴 06月22日 09:43
李雷雷 回复

广播方式的我实践有些问题,gt 大概率会被后台杀掉就丢数据了

手机能设白名单的话就不会有问题了,现在大部分国产手机都能设置白名单

codeskyblue 回复

哦哦,我的错,当时主要疑惑的是第二条命令是怎么执行的。
刚查了下,Runtime.getRuntime()原来执行多条命令时后续只要拿到processDataOutputStream对象,继续writeBytes就可以保证是在同一个上下文中执行多条命令了。

李雷雷 回复

是的,如果不这么搞得话,执行一段时间,系统就不让调用了。至于为什么不能调用,我也不太清楚

匿名 #13 · 2017年06月22日

在 APP 里执行 dumpsys meminfo 这个命令应该需要相应的系统权限或者 root,但是没有 root 的手机也可以监测到内存数据,这个是为什么呢?

因为runCMD(String cmdString)在内部都是用su -c $cmdString 去执行的

匿名 #14 · 2017年06月22日
李雷雷 回复

对的,“su” 需要 root,尝试过在 APP 里使用这种方式,但是没有权限。所以我在想是不是有其他的方式实现

最近正好使用 GT 开始读源码,有了这个感觉方便多了。后期会有通过解读源码后自己开发一个实时监控 APP 性能的分享吗。期待!!!

顺流而下 回复

暂无计划😂

我用 gt 测试流畅度,结果 SM 值一直是 60😂 用的是 root 过的酷派手机..真是让人郁闷啊

八个鸭鹿 回复

用 Choreographer 来测试这个倒不是腾讯的问题,丢帧数据确实基本上是不输出的,但不太清楚怎么回事。。不过尽管如此,腾讯在流畅度测试的计算上还是有点问题的。。

文章很有用,赞。
最近在用 GT 做性能测试,但是在做电量的时候,电流值始终是 0 ,用命令看/sys/class/power_supply/battery/uevent。也确实是 0 ,但是 adb shell dumpsys battery 却是有值得,就很尴尬。因为电量测试,始终是断掉 usb 比较好测,没有想到比较好的办法

Heyniu 回复

是指设置什么白名单?

应用白名单,设置了系统就不会 kill 这个应用

Heyniu 回复

我这用的三星 s6 完全没找到有这个设置。

三星的应该在电池管理,然后未监视的应用程序,添加应用就是白名单了

25楼 已删除
李雷雷 源码解读腾讯 GT 的性能测试方案 中提及了此贴 11月06日 11:26
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册