加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

adb shell 中的 dumpsys 命令调用过程 探究 (16/04/05)

发布时间:2020-12-15 16:55:46 所属栏目:安全 来源:网络整理
导读:《代码里的世界》 — 原理篇 用文字札记描绘自己 android学习之路 转载请保留出处 by Qiao http://www.jb51.cc/article/p-gtxxakbe-bps.html 之前在研究png 和svg在绘制过程中的内存占用和绘制效率对比问题的时候,使用了比较便捷的adb shell 命令 adb shell

《代码里的世界》原理篇

用文字札记描绘自己 android学习之路

转载请保留出处 by Qiao
http://www.52php.cn/article/p-gtxxakbe-bps.html

  之前在研究png 和svg在绘制过程中的内存占用和绘制效率对比问题的时候,使用了比较便捷的adb shell 命令 adb shell dumpsys gfxinfo来查看具体数据。由于使用需要,就稍微跟进了一下代码,这里记录下命令行调用输出的详细过程。
  

1.场景举例

  以com.xxx.demo举例,在terminal终端调用 adb shell dumpsys gfxinfo com.xxx.demo,得到的信息大概有两部分:
- Recent DisplayList operations
- Caches
详细日志如下:

?   >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文件入手。
通过遍历ServiceManager来获取对应参数的Service,并且调用其dump(FileDescriptor fd,PrintWriter pw,String[] args)方法.

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文件。
有个static setSystemProcess()方法:

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
ApplicationThread集成自ApplicationThreadNative,这里主要看dumpGfxInfo方法。

private class ApplicationThread extends ApplicationThreadNative {
        //...

        @Override
        public void dumpGfxInfo(FileDescriptor fd,String[] args) {
            dumpGraphicsInfo(fd);
            WindowManagerImpl.getDefault().dumpGfxInfo(fd);
        }        

    }

  它先调用了ApplicationThreaddumpGraphicsInfo()方法,然后又调用了默认WindowManagerImpl实例的dumpGfxInfo()
  先看ApplicationThread.dumpGraphicsInfo方法:

private native void dumpGraphicsInfo(FileDescriptor fd);

就一行代码,调用了native方法,其实现实在corejniandroid_view_GLES20Canvas.cpp文件中。(注:最新的源码版本在core/jni/android_view_DisplayListCanvas.cpp)

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
}

执行的是DisplayListoutputLogBuffer()。代码在libshwuiDisplayListRenderer.cpp

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);
}

  可以看到如图两个关键部分,就是输出DisplayListCaches 的两个调用。
OP_NAMES是对于不同的DrawRecderNode所做相应的操作。
这里跟进Cache.dumpMemoryUsage()方法。位于libshwuiCaches.cpp

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);
}

  及相关的打印信息。
  回到前面 ApplicationThread的第二个调用WindowManagerImpl.getDefault().dumpGfxInfo(fd);。在拿到WindowManagerImpl默认实例后执行的动作,参见corejavaandroidviewWindowManagerImpl.java

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数量和占用大小记录并输出。
    其中,corejavaandroidviewViewRootImpl.java的实现,加入了递归调用

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 输出数据到界面的小例子。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读