adb shell 中的 dumpsys 命令调用过程 探究 (16/04/05)
《代码里的世界》 —原理篇
之前在研究png 和svg在绘制过程中的内存占用和绘制效率对比问题的时候,使用了比较便捷的adb shell 命令 adb shell dumpsys gfxinfo来查看具体数据。由于使用需要,就稍微跟进了一下代码,这里记录下命令行调用输出的详细过程。 1.场景举例 以com.xxx.demo举例,在terminal终端调用 adb shell dumpsys gfxinfo com.xxx.demo,得到的信息大概有两部分: ? >adb shell dumpsys com.xxx.demo
? Applications Graphics Acceleration Info:
? Uptime: 24363222 Realtime: 26646172
?
? ** Graphics info for pid 30503 [com.xxx.demo] **
?
? Recent DisplayList operations
? Save
? DrawRenderNode
? DrawRenderNode
? DrawRect
? DrawRenderNode
? Save
? ClipRect
? DrawRenderNode
? DrawRenderNode
? Save
? ClipRect
? DrawPath
? DrawPath
? RestoreToCount
? DrawBitmapRect
? RestoreToCount
? DrawRenderNode
? DrawRenderNode
? DrawRect
? RestoreToCount
?
? Caches:
? Current memory usage / total memory usage (bytes):
? TextureCache 65536 / 50331648
? LayerCache 0 / 33554432 (numLayers = 0)
? Layers total 0 (numLayers = 0)
? RenderBufferCache 0 / 8388608
? GradientCache 0 / 3145728
? PathCache 14850 / 33554432
? TessellationCache 0 / 1048576
? TextDropShadowCache 0 / 6291456
? PatchCache 0 / 131072
? FontRenderer 0 A8 0 / 0
? FontRenderer 0 RGBA 0 / 0
? FontRenderer 0 total 0 / 0
? Other:
? FboCache 0 / 25
? Total memory usage:
? 80386 bytes,0.08 MB
?
? Profile data in ms:
?
? com.xxx.demo/com.xxx.demo.MainActivity/android.view.ViewRootImpl@3719de6c (visibility=0)
View hierarchy:
这里从android-4.0.1_r1源码跟进一下dumpsys gfxinfo 的调用过程。 源码探究 首先从入口 cmdsdumpsysdumpsys.cpp文件入手。 int main(int argc,char* const argv[])
{
//...
const size_t N = services.size();
if (N > 1) {
// first print a list of the current services
aout << "Currently running services:" << endl;
for (size_t i=0; i<N; i++) {
sp<IBinder> service = sm->checkService(services[i]);
if (service != NULL) {
aout << " " << services[i] << endl;
}
}
}
for (size_t i=0; i<N; i++) {
sp<IBinder> service = sm->checkService(services[i]); //获取service
if (service != NULL) {
if (N > 1) {
aout << "------------------------------------------------------------"
"-------------------" << endl;
aout << "DUMP OF SERVICE " << services[i] << ":" << endl;
}
int err = service->dump(STDOUT_FILENO,args); //调用dump
if (err != 0) {
aerr << "Error dumping service info: (" << strerror(err)
<< ") " << services[i] << endl;
}
} else {
aerr << "Can't find service: " << services[i] << endl;
}
}
return 0;
}
而这些service是什么时候注册的呢,可以看一下servicesjavacomandroidserveramActivityManagerService.java文件。 public static void setSystemProcess() {
try {
ActivityManagerService m = mSelf;
ServiceManager.addService("activity",m);
ServiceManager.addService("meminfo",new MemBinder(m));
ServiceManager.addService("gfxinfo",new GraphicsBinder(m));
if (MONITOR_CPU_USAGE) {
ServiceManager.addService("cpuinfo",new CpuBinder(m));
}
ServiceManager.addService("permission",new PermissionController(m));
//...
} catch (PackageManager.NameNotFoundException e) {
//...
}
}
可以看到对应 gfxinfo所对应的service(Binder)是 GraphicsBinder,它是ActivityManagerService 的一个内部类。 static class GraphicsBinder extends Binder {
ActivityManagerService mActivityManagerService;
GraphicsBinder(ActivityManagerService activityManagerService) {
mActivityManagerService = activityManagerService;
}
@Override
protected void dump(FileDescriptor fd,String[] args) {
if (mActivityManagerService.checkCallingPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump gfxinfo from from pid="
+ Binder.getCallingPid() + ",uid=" + Binder.getCallingUid()
+ " without permission " + android.Manifest.permission.DUMP);
return;
}
mActivityManagerService.dumpGraphicsHardwareUsage(fd,args);
}
}
前面调用的dump最终会调用ActivityManagerService.dumpGraphicsHardwareUsage方法。 final void dumpGraphicsHardwareUsage(FileDescriptor fd,String[] args) {
//...
for (int i = procs.size() - 1 ; i >= 0 ; i--) {
ProcessRecord r = procs.get(i);
if (r.thread != null) {
//..
try {
TransferPipe tp = new TransferPipe();
try {
//这里r是processRecord,即最后调用ApplicationThread的dumpGfxInfo方法
r.thread.dumpGfxInfo(tp.getWriteFd().getFileDescriptor(),args);
tp.go(fd);
} finally {
tp.kill();
}
} catch (IOException e) {
//..
}
}
}
}
继续跟进,查看ApplicationThread实现。该类是ActivtyThread的一个内部类,该ActivtyThread.java文件路径corejavaandroidappActivityThread.java。 private class ApplicationThread extends ApplicationThreadNative {
//...
@Override
public void dumpGfxInfo(FileDescriptor fd,String[] args) {
dumpGraphicsInfo(fd);
WindowManagerImpl.getDefault().dumpGfxInfo(fd);
}
}
它先调用了ApplicationThread的dumpGraphicsInfo()方法,然后又调用了默认WindowManagerImpl实例的dumpGfxInfo()。 private native void dumpGraphicsInfo(FileDescriptor fd);
就一行代码,调用了native方法,其实现实在 static void
android_app_ActivityThread_dumpGraphics(JNIEnv* env,jobject javaFileDescriptor) {
#ifdef USE_OPENGL_RENDERER
int fd = jniGetFDFromFileDescriptor(env,javaFileDescriptor);
android::uirenderer::DisplayList::outputLogBuffer(fd);
#endif // USE_OPENGL_RENDERER
}
执行的是DisplayList 的outputLogBuffer()。代码在 void DisplayList::outputLogBuffer(int fd) {
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
if (logBuffer.isEmpty()) {
return;
}
FILE *file = fdopen(fd,"a");
fprintf(file,"nRecent DisplayList operationsn");
logBuffer.outputCommands(file,OP_NAMES); //输出DisplayList
String8 cachesLog;
Caches::getInstance().dumpMemoryUsage(cachesLog); //dumpCache
fprintf(file,"nCaches:n%s",cachesLog.string());
fprintf(file,"n");
fflush(file);
}
可以看到如图两个关键部分,就是输出DisplayList 和 Caches 的两个调用。 void Caches::dumpMemoryUsage(String8 &log) {
log.appendFormat("Current memory usage / total memory usage (bytes):n");
log.appendFormat(" TextureCache %8d / %8dn",textureCache.getSize(),textureCache.getMaxSize());
log.appendFormat(" LayerCache %8d / %8dn",layerCache.getSize(),layerCache.getMaxSize());
log.appendFormat(" GradientCache %8d / %8dn",gradientCache.getSize(),gradientCache.getMaxSize());
log.appendFormat(" PathCache %8d / %8dn",pathCache.getSize(),pathCache.getMaxSize());
log.appendFormat(" CircleShapeCache %8d / %8dn",circleShapeCache.getSize(),circleShapeCache.getMaxSize());
log.appendFormat(" OvalShapeCache %8d / %8dn",ovalShapeCache.getSize(),ovalShapeCache.getMaxSize());
log.appendFormat(" RoundRectShapeCache %8d / %8dn",roundRectShapeCache.getSize(),roundRectShapeCache.getMaxSize());
log.appendFormat(" RectShapeCache %8d / %8dn",rectShapeCache.getSize(),rectShapeCache.getMaxSize());
log.appendFormat(" ArcShapeCache %8d / %8dn",arcShapeCache.getSize(),arcShapeCache.getMaxSize());
log.appendFormat(" TextDropShadowCache %8d / %8dn",dropShadowCache.getSize(),dropShadowCache.getMaxSize());
for (uint32_t i = 0; i < fontRenderer.getFontRendererCount(); i++) {
const uint32_t size = fontRenderer.getFontRendererSize(i);
log.appendFormat(" FontRenderer %d %8d / %8dn",size);
}
log.appendFormat("Other:n");
log.appendFormat(" FboCache %8d / %8dn",fboCache.getSize(),fboCache.getMaxSize());
log.appendFormat(" PatchCache %8d / %8dn",patchCache.getSize(),patchCache.getMaxSize());
uint32_t total = 0;
total += textureCache.getSize();
total += layerCache.getSize();
total += gradientCache.getSize();
total += pathCache.getSize();
total += dropShadowCache.getSize();
total += roundRectShapeCache.getSize();
total += circleShapeCache.getSize();
total += ovalShapeCache.getSize();
total += rectShapeCache.getSize();
total += arcShapeCache.getSize();
for (uint32_t i = 0; i < fontRenderer.getFontRendererCount(); i++) {
total += fontRenderer.getFontRendererSize(i);
}
log.appendFormat("Total memory usage:n");
log.appendFormat(" %d bytes,%.2f MBn",total / 1024.0f / 1024.0f);
}
及相关的打印信息。 public void dumpGfxInfo(FileDescriptor fd) {
FileOutputStream fout = new FileOutputStream(fd);
PrintWriter pw = new PrintWriter(fout);
try {
synchronized (this) {
if (mViews != null) {
pw.println("View hierarchy:");
final int count = mViews.length;
int viewsCount = 0;
int displayListsSize = 0;
int[] info = new int[2];
for (int i = 0; i < count; i++) {
ViewRootImpl root = mRoots[i];
root.dumpGfxInfo(pw,info);
String name = root.getClass().getName() + '@' +
Integer.toHexString(hashCode());
pw.printf(" %s: %d views,%.2f kB (display lists)n",info[0],info[1] / 1024.0f);
viewsCount += info[0];
displayListsSize += info[1];
}
pw.printf("nTotal ViewRootImpl: %dn",count);
pw.printf("Total Views: %dn",viewsCount);
pw.printf("Total DisplayList: %.2f kBnn",displayListsSize / 1024.0f);
}
}
} finally {
pw.flush();
}
}
通过ViewRootImpl的dumpGfxInfo方法,将View数量和占用大小记录并输出。 public void dumpGfxInfo(PrintWriter pw,int[] info) {
if (mView != null) {
getGfxInfo(mView,info);
} else {
info[0] = info[1] = 0;
}
}
private void getGfxInfo(View view,int[] info) {
DisplayList displayList = view.mDisplayList;
info[0]++;
if (displayList != null) {
info[1] += displayList.getSize();
}
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
int count = group.getChildCount();
for (int i = 0; i < count; i++) {
getGfxInfo(group.getChildAt(i),info);
}
}
}
以上,就是关于 gfxinfo 的dumpsys 命令实现。其他命令也是相同方式,不再赘述。感兴趣的可以自行查看相关内容。 最后,附上一个 利用 dumpsys 输出数据到界面的小例子。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |