GT获取android性能参数

该文档主要是分析GT获取memory,cpu,network数据的方案,主要是读取系统虚拟文件/proc,如需深入了解可以看Linux Programmer's Manual proc参考文档

  1. 内存获取:

    • GT核心代码:com.tencent.wstt.gt.collector.monitor.NormalMonitor
    public static int getMemory_app(int pid, Context context) {
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        int[] myMempid = new int[] { pid };
        assert am != null;
        Debug.MemoryInfo[] memoryInfo = am.getProcessMemoryInfo(myMempid);
        return memoryInfo[0].getTotalPss();
    }
    
    • 时序图


      GT获取android性能参数_第1张图片
      image.png
  • 第4步读取/proc/pid/smaps内容:

f61e3000-f61e7000 rw-p 00000000 00:05 9015 /dev/ashmem/dalvik-thread local mark stack (deleted)
Size: 16 kB
Rss: 0 kB
Pss: 0 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 0 kB
Private_Dirty: 0 kB
Referenced: 0 kB
Anonymous: 0 kB
AnonHugePages: 0 kB
Shared_Hugetlb: 0 kB
Private_Hugetlb: 0 kB
Swap: 0 kB
SwapPss: 0 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Locked: 0 kB
VmFlags: rd wr mr mw me ac

字段含义:

第几个 字段名 含义
1 地址空间的开始地址 - 结束地址
2 Size 是进程使用内存空间,并不一定实际分配了物理内存
3 Rss 实际驻留”在内存中”的内存数. 不包括已经交换出去的页面。RSS还包括了与其它进程共享的内存区域,通常用于共享库;
4 Pss Rss中私有的内存页面
5 Shared_Clean Rss中和其他进程共享的未改写页面
6 Shared_Dirty Rss中和其他进程共享的已改写页面
7 Private_Clean Rss中改写的私有页面页面
8 Private_Dirty Rss中已改写的私有页面页面

参考文档

Android系统相关核心代码:

//object就是java层传递进来接收数据的MemoryInfo对象
248 static void android_os_Debug_getDirtyPagesPid(JNIEnv *env, jobject clazz,
249         jint pid, jobject object)
250 {
251     stats_t stats[_NUM_HEAP];
252     memset(&stats, 0, sizeof(stats));
254     load_maps(pid, stats);
            .....
283 }

235 static void load_maps(int pid, stats_t* stats)
236 {
237     char tmp[128];
238     FILE *fp;
239 
240     sprintf(tmp, "/proc/%d/smaps", pid);
241     fp = fopen(tmp, "r");
242     if (fp == 0) return;
244     read_mapinfo(fp, stats);
245     fclose(fp);
246 }
//逐行读取文件
121 static void read_mapinfo(FILE *fp, stats_t* stats)
          ......
144     while (!done) {
            ......
154         if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &nam    e_pos) != 2) {
155             skip = true;
156         } else {
157             while (isspace(line[name_pos])) {
158                 name_pos += 1;
159             }
160             name = line + name_pos;
161             nameLen = strlen(name);
162 
163             if ((strstr(name, "[heap]") == name) ||
164                 (strstr(name, "/dev/ashmem/libc malloc") == name)) {
165                 whichHeap = HEAP_NATIVE;
166             } else if (strstr(name, "/dev/ashmem/dalvik-") == name) {
167                 whichHeap = HEAP_DALVIK;
168             } else if (strstr(name, "[stack") == name) {
169                 whichHeap = HEAP_STACK;
170             } else if (strstr(name, "/dev/ashmem/CursorWindow") == name) {
171                 whichHeap = HEAP_CURSOR;
172             } else if (strstr(name, "/dev/ashmem/") == name) {
173                 whichHeap = HEAP_ASHMEM;
174             } else if (strstr(name, "/dev/") == name) {
175                 whichHeap = HEAP_UNKNOWN_DEV;
176             } else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) {
177                 whichHeap = HEAP_SO;
178             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".jar") == 0) {
179                 whichHeap = HEAP_JAR;
180             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".apk") == 0) {
181                 whichHeap = HEAP_APK;
182             } else if (nameLen > 4 && strcmp(name+nameLen-4, ".ttf") == 0) {
183                 whichHeap = HEAP_TTF;
184             } else if ((nameLen > 4 && strcmp(name+nameLen-4, ".dex") == 0)     ||
185                        (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0)    ) {
186                 whichHeap = HEAP_DEX;
187             } else if (nameLen > 0) {
188                 whichHeap = HEAP_UNKNOWN_MAP;
189             } else if (start == prevEnd && prevHeap == HEAP_SO) {
190                 // bss section of a shared library.
191                 whichHeap = HEAP_SO;
192             }
193         }
                ......
198         while (true) {
199             if (fgets(line, 1024, fp) == 0) {
200                 done = true;
201                 break;
202             }
203 
204             if (sscanf(line, "Size: %d kB", &temp) == 1) {
205                 size = temp;
206             } else if (sscanf(line, "Rss: %d kB", &temp) == 1) {
207                 resident = temp;
208             } else if (sscanf(line, "Pss: %d kB", &temp) == 1) {
209                 pss = temp;
210             } else if (sscanf(line, "Shared_Clean: %d kB", &temp) == 1) {
211                 shared_clean = temp;
212             } else if (sscanf(line, "Shared_Dirty: %d kB", &temp) == 1) {
213                 shared_dirty = temp;
214             } else if (sscanf(line, "Private_Clean: %d kB", &temp) == 1) {
215                 private_clean = temp;
216             } else if (sscanf(line, "Private_Dirty: %d kB", &temp) == 1) {
217                 private_dirty = temp;
218             } else if (sscanf(line, "Referenced: %d kB", &temp) == 1) {
219                 referenced = temp;
220             } else if (strlen(line) > 30 && line[8] == '-' && line[17] == '     ') {
221                 // looks like a new mapping
222                 // example: "10000000-10001000 ---p 10000000 00:00 0"
223                 break;
224             }
225         }
          ......
232     }
233 }
  1. 读取一个进程cpu数据

    • GT核心代码:
    public static long getCPU_app(int pid) {
         Scanner scanner = null;
         long cpuApp = 0;
         try {
             scanner = new Scanner(new File("/proc/" + pid + "/stat"));
             int i = 0;
             while (scanner.hasNext() && i < 13) {
                 scanner.next();
                 i++;
             }
             //这里是 usage_in_time_units = utime + stime + cutime + cstime;
             cpuApp = scanner.nextLong() +
                     scanner.nextLong() +
                     scanner.nextLong() +
                     scanner.nextLong();
         } catch (IOException e) {
             e.printStackTrace();
         } finally {
             if (scanner != null) {
                 scanner.close();
             }
         }
         return cpuApp;
     }
    
    • 读取/proc/pid/stat内容:

9473 (tencent.wstt.gt) S 1715 1715 0 0 -1 1077952832 20672 14376 36 0 2573 869 0 0 20 0 36 0 1245958 1516355584 17253 18446744073709551615 1585926144 1585942752 4292355520 4292344336 4131982169 0 4612 1 1073775864 0 0 0 17 3 0 0 1 0 0 1585950004 1585950720 1606168576 4292356986 4292357062 4292357062 4292358112 0

第几个 字段名 含义
1 pid 进程号
2 comm 应用程序或命令的名字
S state 任务的状态,R:runnign, S:sleeping (TASK_INTERRUPTIBLE), D:disk sleep (TASK_UNINTERRUPTIBLE), T: stopped, T:tracing stop,Z:zombie, X:dead
14 utime 用户态运行的时间,包含guest time
15 stime 内核态运行的时间
16 cutime 该进程spawn的子进程在用户态消耗的jiffies
17 cstime 该进程spawn的子进程在内核态消耗的jiffies

process jiffies是内核提供的该进程在DeltaT时间内消耗的jiffies
process jiffies = utime + stime + cutime + cstime

参考文章 进程cpu使用率的计算

  • 读取/proc/stat系统cpu数据

cpu 165726 66054 180319 10448201 5747 0 4368 0 0 0
cpu0 39122 16057 46328 2596897 853 0 3906 0 0 0
cpu1 40228 16678 44921 2619101 1723 0 182 0 0 0
cpu2 41079 16139 46316 2613505 1646 0 159 0 0 0
cpu3 45297 17180 42754 2618698 1525 0 121 0 0 0
intr 17549673 16 0 0 0 7 0 0 0 1 0 284778 119089 0 0 0 0 1 4526 1282982 1 0 102878 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ctxt 35200542
btime 1535412445

processes 67824
procs_running 1
procs_blocked 0
softirq 10561214 275826 2046001 36060 46525 0 0 4188132 1910413 0 2058257

第几个 字段名 含义
1 user 用户态消耗的时间,不包含 nice值为负进程
2 nice 用户态消耗的时间,nice值为负的进程所占用的CPU时间
3 system 内核态运行的时间
4 idle 除IO等待时间以外的其它等待时间iowait
5 iowait IO等待时间(不可靠)
6 irq 硬中断时间
7 softirq 软中断时间
8 steal 虚拟化环境中运行时在其他操作系统上花费的时间

算法:

  1. 系统占用cpu时间
    int64_t load = user + nice + system + irq + softirq + steal;
  2. 总流失时间
    int64_t elapsed = load + idle + iowait;
  • 读取线程占cpu用时间:

/proc/[pid]/task/[tid]/stat 参数含义同/proc/[pid]/stat

获取流量方式:

  • GT核心代码:
    long flowUpload = TrafficStats.getUidTxBytes(android.os.Process.myUid()); long flowDownload = TrafficStats.getUidRxBytes(android.os.Process.myUid());

  • 读取系统文件:/proc/net/xt_qtaguid/stats 内容 (这个文件记录所有uid的数据,可以根据uid_tag_int字段进行过滤)

idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
2 wlan0 0x0 0 0 32685 494 17136 261 8597 184 0 0 24088 310 2412 46 0 0 14724 215
61 radio0 0x0 0 0 15020 196 5132 79 1964 49 0 0 13056 147 1080 19 0 0 4052 60
70 lo 0x0 0 0 0 0 200 5 0 0 0 0 0 0 0 0 0 0 200 5

字段名 含义
idx 文件头
iface 网络接口
acct_tag_hex socket
uid_tag_int 是UID
cnt_set 标志位,0代表前台流量,1代表后台流量
rx_bytes 接收数据
tx_bytes 传输数据
  • Android中android_net_TrafficStats.cpp中获取uid流量的算法:
 121 static int parseUidStats(const uint32_t uid, struct Stats* stats) {
122     FILE *fp = fopen(QTAGUID_UID_STATS, "r");
123     if (fp == NULL) {
124         return -1;
125     }
126 
127     char buffer[384];
128     char iface[32];
129     uint32_t idx, cur_uid, set;
130     uint64_t tag, rxBytes, rxPackets, txBytes, txPackets;
131 
132     while (fgets(buffer, sizeof(buffer), fp) != NULL) {
133         if (sscanf(buffer, "%d %31s 0x%llx %u %u %llu %llu %llu %llu", &idx,
134                 iface, &tag, &cur_uid, &set, &rxBytes, &rxPackets, &txBytes,
135                 &txPackets) == 9) {
136             if (uid == cur_uid && tag == 0L) {
137                 stats->rxBytes += rxBytes;
138                 stats->rxPackets += rxPackets;
139                 stats->txBytes += txBytes;
140                 stats->txPackets += txPackets;
141             }
142         }
143     }
144 
145     if (fclose(fp) != 0) {
146         return -1;
147     }
148     return 0;
149 }

GT存在的问题:

1.只能获取主进程数据,默认收集多种数据
2.内存数据没有细分,只返回来memoryInfo[0].getTotalPss(),流量数据只有上传下载的总量,不能细化到每个请求,IP
3.植入sdk会导致主进程性能数据偏高

解决方案:
1.可以添加一个broadcast广播或server端来接收切换进程的请求
2.1)内存数据可以返回memoryInfo,后续可以根据需要提取。
2)流量数据可以考虑hook请求class达到细化分类(方案待定)
3.sdk是java层做性能收集及密集计算,可以考虑在Native中处理,减小对主进程影响(方案待定)

GT获取android性能参数_第2张图片
image.png

你可能感兴趣的:(GT获取android性能参数)