命令dumpsys之gfxinfo调用流程

本文基于Android 8.0源码

启动的位置是在/frameworks/native/cmds/dumpsys/main.cpp:

int main(int argc, char* const argv[]) {
    signal(SIGPIPE, SIG_IGN);
    sp sm = defaultServiceManager(); //1
    fflush(stdout);
    if (sm == nullptr) {
        ALOGE("Unable to get default service manager!");
        aerr << "dumpsys: Unable to get default service manager!" << endl;
        return 20;
    }

    Dumpsys dumpsys(sm.get());//2
    return dumpsys.main(argc, argv);//3
}

注释1:通过Binder获取ServiceManager对象,这块知识点可以参考大神的博客Binder系列4—获取ServiceManager,Binder系列6—获取服务(getService)。

注释2:创建Dumpsys对象,sm.get()是获取IServiceManager数组第一个元素的地址,传给Dumpsys对象

注释3:调用Dumpsys的main方法:

int Dumpsys::main(int argc, char* const argv[]) {
...
    for (size_t i = 0; i < N; i++) {
            String16 service_name = std::move(services[i]);
            if (IsSkipped(skippedServices, service_name)) continue;
    
            sp service = sm_->checkService(service_name);//1
            ...
            // dump blocks until completion, so spawn a thread..
            std::thread dump_thread([=, remote_end { std::move(remote_end) }]() mutable {
                int err = service->dump(remote_end.get(), args);//2

                // It'd be nice to be able to close the remote end of the socketpair before the dump
                // call returns, to terminate our reads if the other end closes their copy of the
                // file descriptor, but then hangs for some reason. There doesn't seem to be a good
                // way to do this, though.
                remote_end.reset();

                if (err != 0) {
                    aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name
                         << endl;
                }
            });
            ...
    }
}

注释1:sm->checkService(),获取系统中指定的Service;

注释2:service->dump(),dumpsys命令的核心还是调用远程服务中的dump()方法来获取相应的dump信息。

因此需要查找gfxinfo对应的Service和dump方法:

/frameworks/base/services/java/com/android/server/SystemServer.java:

private void startBootstrapServices() {
    ...
    // Set up the Application instance for the system process and get started.
        traceBeginAndSlog("SetSystemProcess");
        mActivityManagerService.setSystemProcess();//1
        traceEnd();
    ...
}

注释1:设置系统进程:

/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java:

public void setSystemProcess() {
        try {
            ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
            ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
            ServiceManager.addService("meminfo", new MemBinder(this));
            ServiceManager.addService("gfxinfo", new GraphicsBinder(this));//1
            ServiceManager.addService("dbinfo", new DbBinder(this));
            if (MONITOR_CPU_USAGE) {
                ServiceManager.addService("cpuinfo", new CpuBinder(this));
            }
            ServiceManager.addService("permission", new PermissionController(this));
            ServiceManager.addService("processinfo", new ProcessInfoService(this));

            ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
                    "android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY);
            mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());

            synchronized (this) {
                ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
                app.persistent = true;
                app.pid = MY_PID;
                app.maxAdj = ProcessList.SYSTEM_ADJ;
                app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
                synchronized (mPidsSelfLocked) {
                    mPidsSelfLocked.put(app.pid, app);
                }
                updateLruProcessLocked(app, false, null);
                updateOomAdjLocked();
            }
        } catch (PackageManager.NameNotFoundException e) {
            throw new RuntimeException(
                    "Unable to find android system package", e);
        }
    }

注释1:可以看到里面就有我们熟悉的dumpsys命令的第一个可选项的字段,已经对应的Service类,其中gfxinfo对应GraphicsBinder类,GraphicsBinder是ActivityManagerService的内部类。

继续接着上面的native代码,执行GraphicsBinder的dump方法: /frameworks/base/services/core/java/com/android/server/am/ActivityManagerService#GraphicsBinder.java:

static class GraphicsBinder extends Binder {
        ActivityManagerService mActivityManagerService;
        GraphicsBinder(ActivityManagerService activityManagerService) {
            mActivityManagerService = activityManagerService;
        }

        @Override
        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            if (!DumpUtils.checkDumpAndUsageStatsPermission(mActivityManagerService.mContext,
                    "gfxinfo", pw)) return;
            mActivityManagerService.dumpGraphicsHardwareUsage(fd, pw, args);//1
        }
    }

注释1:调用ActivityManagerService的dumpGraphicsHardwareUsag方法:

final void dumpGraphicsHardwareUsage(FileDescriptor fd,
            PrintWriter pw, String[] args) {
        ArrayList procs = collectProcesses(pw, 0, false, args);
        if (procs == null) {
            pw.println("No process found for: " + args[0]);
            return;
        }

        long uptime = SystemClock.uptimeMillis();
        long realtime = SystemClock.elapsedRealtime();
        pw.println("Applications Graphics Acceleration Info:");
        pw.println("Uptime: " + uptime + " Realtime: " + realtime);

        for (int i = procs.size() - 1 ; i >= 0 ; i--) {
            ProcessRecord r = procs.get(i);
            if (r.thread != null) {
                pw.println("\n** Graphics info for pid " + r.pid + " [" + r.processName + "] **");
                pw.flush();
                try {
                    TransferPipe tp = new TransferPipe();
                    try {
                        r.thread.dumpGfxInfo(tp.getWriteFd(), args);//1
                        tp.go(fd);
                    } finally {
                        tp.kill();
                    }
                } catch (IOException e) {
                    pw.println("Failure while dumping the app: " + r);
                    pw.flush();
                } catch (RemoteException e) {
                    pw.println("Got a RemoteException while dumping the app " + r);
                    pw.flush();
                }
            }
        }
    }

注释1:其中r.thread是IApplicationThread类,对应ActivityThread的内部类ApplicationThread类,其中有各种dump信息对应的方法:

/frameworks/base/core/java/android/app/ActivityThread#ApplicationThread.java:

private class ApplicationThread extends IApplicationThread.Stub {
    ...
    @Override
    public void dumpGfxInfo(ParcelFileDescriptor pfd, String[] args) {
        nDumpGraphicsInfo(pfd.getFileDescriptor());//1
        WindowManagerGlobal.getInstance().dumpGfxInfo(pfd.getFileDescriptor(), args);//2
        IoUtils.closeQuietly(pfd);
    }
    ...
}

注释2:这里我们看到非常熟悉的WindowManagerGlobal,跟Window息息相关,dumpGfxInfo方法里,会遍历所有的ViewRootImpl,然后层层遍历每个ViewRootImpl的子View,获取RenderNode相关信息,并打印。

注释1:这里nDumpGraphicsInfo是一个native方法,跳到native层:

/frameworks/base/core/jni/android_view_DisplayListCanvas.cpp:

这里采用动态注册:

static JNINativeMethod gActivityThreadMethods[] = {
        // ------------ Regular JNI ------------------
    { "nDumpGraphicsInfo",        "(Ljava/io/FileDescriptor;)V",
                                               (void*) android_app_ActivityThread_dumpGraphics }
};
static void
android_app_ActivityThread_dumpGraphics(JNIEnv* env, jobject clazz, jobject javaFileDescriptor) {
    int fd = jniGetFDFromFileDescriptor(env, javaFileDescriptor);
    android::uirenderer::renderthread::RenderProxy::dumpGraphicsMemory(fd);//1
}

注释1:调用RenderProxy的静态方法dumpGraphicsMemory:

/frameworks/base/libs/hwui/renderthread/RenderProxy.cpp:

void RenderProxy::dumpGraphicsMemory(int fd) {
    if (!RenderThread::hasInstance()) return;
    SETUP_TASK(dumpGraphicsMemory);//1
    args->fd = fd;
    args->thread = &RenderThread::getInstance();
    staticPostAndWait(task);
}

注释1:这里使用了函数指针,创建一个任务,执行dumpGraphicsMemory方法:

CREATE_BRIDGE2(dumpGraphicsMemory, int fd, RenderThread* thread) {
    args->thread->jankTracker().dump(args->fd);//1

    FILE *file = fdopen(args->fd, "a");
    if (Caches::hasInstance()) {
        String8 cachesLog;
        Caches::getInstance().dumpMemoryUsage(cachesLog);//2
        fprintf(file, "\nCaches:\n%s\n", cachesLog.string());
    } else {
        fprintf(file, "\nNo caches instance.\n");
    }
    fprintf(file, "\nPipeline=FrameBuilder\n");
    fflush(file);
    return nullptr;
}

注释1:dump,这里将打印出dump命令最开始的那一串信息:

void JankTracker::dumpData(int fd, const ProfileDataDescription* description, const ProfileData* data) {
    if (description) {
        switch (description->type) {
            case JankTrackerType::Generic:
                break;
            case JankTrackerType::Package:
                dprintf(fd, "\nPackage: %s", description->name.c_str());
                break;
            case JankTrackerType::Window:
                dprintf(fd, "\nWindow: %s", description->name.c_str());
                break;
        }
    }
    if (sFrameStart != FrameInfoIndex::IntendedVsync) {
        dprintf(fd, "\nNote: Data has been filtered!");
    }
    dprintf(fd, "\nStats since: %" PRIu64 "ns", data->statStartTime);
    dprintf(fd, "\nTotal frames rendered: %u", data->totalFrameCount);
    dprintf(fd, "\nJanky frames: %u (%.2f%%)", data->jankFrameCount,
            (float) data->jankFrameCount / (float) data->totalFrameCount * 100.0f);
    dprintf(fd, "\n50th percentile: %ums", findPercentile(data, 50));
    dprintf(fd, "\n90th percentile: %ums", findPercentile(data, 90));
    dprintf(fd, "\n95th percentile: %ums", findPercentile(data, 95));
    dprintf(fd, "\n99th percentile: %ums", findPercentile(data, 99));
    for (int i = 0; i < NUM_BUCKETS; i++) {
        dprintf(fd, "\nNumber %s: %u", JANK_TYPE_NAMES[i], data->jankTypeCounts[i]);
    }
    dprintf(fd, "\nHISTOGRAM:");
    for (size_t i = 0; i < data->frameCounts.size(); i++) {
        dprintf(fd, " %ums=%u", frameTimeForFrameCountIndex(i),
                data->frameCounts[i]);
    }
    for (size_t i = 0; i < data->slowFrameCounts.size(); i++) {
        dprintf(fd, " %ums=%u", frameTimeForSlowFrameCountIndex(i),
                data->slowFrameCounts[i]);
    }
    dprintf(fd, "\n");
}

注释2:dumpMemoryUsage:

void Caches::dumpMemoryUsage(String8 &log) {
    uint32_t total = 0;
    log.appendFormat("Current memory usage / total memory usage (bytes):\n");
    log.appendFormat("  TextureCache         %8d / %8d\n",
            textureCache.getSize(), textureCache.getMaxSize());
    if (mRenderState) {
        int memused = 0;
        for (std::set::iterator it = mRenderState->mActiveLayers.begin();
                it != mRenderState->mActiveLayers.end(); it++) {
            const Layer* layer = *it;
            LOG_ALWAYS_FATAL_IF(layer->getApi() != Layer::Api::OpenGL);
            const GlLayer* glLayer = static_cast(layer);
            log.appendFormat("    GlLayer size %dx%d; texid=%u refs=%d\n",
                    layer->getWidth(), layer->getHeight(),
                    glLayer->getTextureId(),
                    layer->getStrongCount());
            memused += layer->getWidth() * layer->getHeight() * 4;
        }
        log.appendFormat("  Layers total   %8d (numLayers = %zu)\n",
                memused, mRenderState->mActiveLayers.size());
        total += memused;
    }
    log.appendFormat("  RenderBufferCache    %8d / %8d\n",
            renderBufferCache.getSize(), renderBufferCache.getMaxSize());
    log.appendFormat("  GradientCache        %8d / %8d\n",
            gradientCache.getSize(), gradientCache.getMaxSize());
    log.appendFormat("  PathCache            %8d / %8d\n",
            pathCache.getSize(), pathCache.getMaxSize());
    log.appendFormat("  TessellationCache    %8d / %8d\n",
            tessellationCache.getSize(), tessellationCache.getMaxSize());
    log.appendFormat("  TextDropShadowCache  %8d / %8d\n", dropShadowCache.getSize(),
            dropShadowCache.getMaxSize());
    log.appendFormat("  PatchCache           %8d / %8d\n",
            patchCache.getSize(), patchCache.getMaxSize());

    fontRenderer.dumpMemoryUsage(log);

    log.appendFormat("Other:\n");
    log.appendFormat("  FboCache             %8d / %8d\n",
            fboCache.getSize(), fboCache.getMaxSize());

    total += textureCache.getSize();
    total += renderBufferCache.getSize();
    total += gradientCache.getSize();
    total += pathCache.getSize();
    total += tessellationCache.getSize();
    total += dropShadowCache.getSize();
    total += patchCache.getSize();
    total += fontRenderer.getSize();

    log.appendFormat("Total memory usage:\n");
    log.appendFormat("  %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f);

#ifdef BUGREPORT_FONT_CACHE_USAGE
    fontRenderer.getFontRenderer().historyTracker().dump(log);
#endif
}

至此,dumpsys的调用流程就分析完了,如果想知道dump出的信息具体是怎么来的,可以参考该流程,去分析数据的来源。

 

dumpsys gfxinfo的信息详解:

  • Graphics info for pid 31148 [com.android.settings]: 表明当前dump的为设置界面的帧信息,pid为31148
  • Total frames rendered: 105 本次dump搜集了105帧的信息
  • Janky frames: 2 (1.90%) 105帧中有2帧的耗时超过了16ms,卡顿概率为1.9%
  • Number Missed Vsync: 0 垂直同步失败的帧
  • Number High input latency: 0 处理input时间超时的帧数
  • Number Slow UI thread: 2 因UI线程上的工作导致超时的帧数
  • Number Slow bitmap uploads: 0 因bitmap的加载耗时的帧数
  • Number Slow issue draw commands: 1 因绘制导致耗时的帧数
  • HISTOGRAM: 5ms=78 6ms=16 7ms=4 ... 直方图数据,表面耗时为0-5ms的帧数为78,耗时为5-6ms的帧数为16,同理类推。

你可能感兴趣的:(安卓开发)