android 中的dumpsys

源码基于:Android R

首先来看一下dumpsys的source code:

path:frameworks/native/cmds/dumpsys/

1. Android.bp

package {
    default_applicable_licenses: ["frameworks_native_cmds_dumpsys_license"],
}

// Added automatically by a large-scale-change
// See: http://go/android-license-faq
license {
    name: "frameworks_native_cmds_dumpsys_license",
    visibility: [":__subpackages__"],
    license_kinds: [
        "SPDX-license-identifier-Apache-2.0",
    ],
    license_text: [
        "NOTICE",
    ],
}

cc_defaults {
    name: "dumpsys_defaults",

    cflags: [
        "-Wall",
        "-Werror",
    ],

    srcs: [
        "dumpsys.cpp",
    ],

    shared_libs: [
        "libbase",
        "libutils",
        "liblog",
        "libbinder",
        "libbinderdebug",
    ],

    static_libs: [
        "libserviceutils",
    ],
}

cc_library_static {
    name: "libdumpsys",

    defaults: ["dumpsys_defaults"],

    export_include_dirs: ["."],
}

cc_binary {
    name: "dumpsys",

    defaults: ["dumpsys_defaults"],

    srcs: [
        "main.cpp",
    ],
}

cc_binary {
    name: "dumpsys_vendor",
    stem: "dumpsys",

    vendor: true,

    defaults: ["dumpsys_defaults"],

    srcs: [
        "main.cpp",
    ],
}

从Android.bp 知道:

  • 系统会通过 dumpsys.cpp 编译一个静态库 libdumpsys.so;
  • 系统会通过main.cpp 和 dumpsys.cpp 编译出 dumpsys 文件,在system/bin/和/vendor/bin 下都有;

2. main.cpp

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

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

没什么东西,获取 IServiceManager 对象,带入Dumpsys 构造出一个对象并启动main();

3. Dumpsys::main()

int Dumpsys::main(int argc, char* const argv[]) {
    ...

    int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL;
    static struct option longOptions[] = {{"pid", no_argument, 0, 0},
                                          {"priority", required_argument, 0, 0},
                                          {"proto", no_argument, 0, 0},
                                          {"skip", no_argument, 0, 0},
                                          {"help", no_argument, 0, 0},
                                          {0, 0, 0, 0}};

    optind = 1;
    while (1) {  //------ step1
        int c;
        int optionIndex = 0;

        c = getopt_long(argc, argv, "+t:T:l", longOptions, &optionIndex);

        if (c == -1) {
            break;
        }

        switch (c) {
        case 0: //-------- step2
            if (!strcmp(longOptions[optionIndex].name, "skip")) {
                skipServices = true;
            } else if (!strcmp(longOptions[optionIndex].name, "proto")) {
                asProto = true;
            } else if (!strcmp(longOptions[optionIndex].name, "help")) {
                usage();
                return 0;
            } else if (!strcmp(longOptions[optionIndex].name, "priority")) {
                priorityType = String16(String8(optarg));
                if (!ConvertPriorityTypeToBitmask(priorityType, priorityFlags)) {
                    fprintf(stderr, "\n");
                    usage();
                    return -1;
                }
            } else if (!strcmp(longOptions[optionIndex].name, "pid")) {
                type = Type::PID;
            }
            break;

        case 't':  //-------- step3
            {
                char* endptr;
                timeoutArgMs = strtol(optarg, &endptr, 10);
                timeoutArgMs = timeoutArgMs * 1000;
                if (*endptr != '\0' || timeoutArgMs <= 0) {
                    fprintf(stderr, "Error: invalid timeout(seconds) number: '%s'\n", optarg);
                    return -1;
                }
            }
            break;

        case 'T':  //-------- step3
            {
                char* endptr;
                timeoutArgMs = strtol(optarg, &endptr, 10);
                if (*endptr != '\0' || timeoutArgMs <= 0) {
                    fprintf(stderr, "Error: invalid timeout(milliseconds) number: '%s'\n", optarg);
                    return -1;
                }
            }
            break;

        case 'l':   //-------- step4
            showListOnly = true;
            break;

        default:
            fprintf(stderr, "\n");
            usage();
            return -1;
        }
    }

    for (int i = optind; i < argc; i++) { //-------- step5
        if (skipServices) {
            skippedServices.add(String16(argv[i]));
        } else {
            if (i == optind) {
                services.add(String16(argv[i]));
            } else {
                const String16 arg(argv[i]);
                args.add(arg);
                // For backward compatible, if the proto argument is passed to the service, the
                // dump request is also considered to use proto.
                if (!asProto && !arg.compare(String16(PriorityDumper::PROTO_ARG))) {
                    asProto = true;
                }
            }
        }
    }

    if ((skipServices && skippedServices.empty()) ||     //-------- step6
            (showListOnly && (!services.empty() || !skippedServices.empty()))) {
        usage();
        return -1;
    }

    if (services.empty() || showListOnly) {   //-------- step7
        services = listServices(priorityFlags, asProto);
        setServiceArgs(args, asProto, priorityFlags);
    }

    const size_t N = services.size();
    if (N > 1) {    //-------- step8
        // first print a list of the current services
        std::cout << "Currently running services:" << std::endl;

        for (size_t i=0; i<N; i++) {
            sp<IBinder> service = sm_->checkService(services[i]);

            if (service != nullptr) {
                bool skipped = IsSkipped(skippedServices, services[i]);
                std::cout << "  " << services[i] << (skipped ? " (skipped)" : "") << std::endl;
            }
        }
    }

    if (showListOnly) {   //-------- step9
        return 0;
    }

    for (size_t i = 0; i < N; i++) {   //-------- step10
        const String16& serviceName = services[i];
        if (IsSkipped(skippedServices, serviceName)) continue;

        if (startDumpThread(type, serviceName, args) == OK) {
            bool addSeparator = (N > 1);
            if (addSeparator) {
                writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags);
            }
            std::chrono::duration<double> elapsedDuration;
            size_t bytesWritten = 0;
            status_t status =
                writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(timeoutArgMs),
                          asProto, elapsedDuration, bytesWritten);

            if (status == TIMED_OUT) {
                std::cout << std::endl
                     << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs
                     << "ms) EXPIRED ***" << std::endl
                     << std::endl;
            }

            if (addSeparator) {
                writeDumpFooter(STDOUT_FILENO, serviceName, elapsedDuration);
            }
            bool dumpComplete = (status == OK);
            stopDumpThread(dumpComplete);
        }
    }

    return 0;
}

代码比较多,将其划分为10 个部分,分别从step1 ~ step10

step1. 启动while 循环,解析dumpsys 的命令的参数;

主要是启动一个死循环,通过 getopt_long() 解析命令行参数;

step2. 确认特殊命令

确认dumpsys 命令是否含有 --skip、--proto、--priority、--help、--skip

来看下dumpsys 的命令行参数选项:

static void usage() {
    fprintf(stderr,
            "usage: dumpsys\n"
            "         To dump all services.\n"
            "or:\n"
            "       dumpsys [-t TIMEOUT] [--priority LEVEL] [--pid] [--help | -l | --skip SERVICES "
            "| SERVICE [ARGS]]\n"
            "         --help: shows this help\n"
            "         -l: only list services, do not dump them\n"
            "         -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n"
            "         -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n"
            "         --pid: dump PID instead of usual dump\n"
            "         --proto: filter services that support dumping data in proto format. Dumps\n"
            "               will be in proto format.\n"
            "         --priority LEVEL: filter services based on specified priority\n"
            "               LEVEL must be one of CRITICAL | HIGH | NORMAL\n"
            "         --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n"
            "         SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n");
}
  • --help:显示usage;
  • --skip SERVICES:dumpsys 的时候过滤掉这些services;
  • --priority LEVEL:传入给对应service(例如meminfo) 的参数,以--dump-priority 选项传入;
  • --pid:以PID 的形式代替传统的 dump() 函数,下面 step10 会说明;
  • --proto:类似与--prriority,这里会将 --proto 选项作为参数带入对应的service;
  • -t:设置timeout,秒为单位;
  • -T:设置timeout,毫秒为单位;
  • -l:列出所有的services,但不取dump;
  • SERVICE [ARGS]:可以直接指定service,只dump 指定的service

step3. 设定timeout

        case 't':  //-------- step3
            {
                char* endptr;
                timeoutArgMs = strtol(optarg, &endptr, 10);
                timeoutArgMs = timeoutArgMs * 1000;
                if (*endptr != '\0' || timeoutArgMs <= 0) {
                    fprintf(stderr, "Error: invalid timeout(seconds) number: '%s'\n", optarg);
                    return -1;
                }
            }
            break;

        case 'T':  //-------- step3
            {
                char* endptr;
                timeoutArgMs = strtol(optarg, &endptr, 10);
                if (*endptr != '\0' || timeoutArgMs <= 0) {
                    fprintf(stderr, "Error: invalid timeout(milliseconds) number: '%s'\n", optarg);
                    return -1;
                }
            }
            break;

通过-t 或 -T 传入 poll 所需要的timeout;

-t 设定的是以秒为单位,-T 设定的是以毫秒为单位;

默认值为10秒,如果 10 秒的时间还不足够 poll 响应,那么可以通过这两个参数适当的加大timeout 时长。

step4. 通过-l 列举所有可以dump 的services

        case 'l':   //-------- step4
            showListOnly = true;
            break;

 如果有-l 这个参数,代码会将 showListOnly 置为true,用以确定是否进行打印;

step5. 确定需要skip 的services 和args

如果设定了 --skip,变量 skipServices 会被置为true;

如果没有设定 --skip,那就确认指定的service 和 其args;

step6. 确认命令是否出错

如果出现错误会打印 usage 作为提醒;

step7. 统计所有的services

    if (services.empty() || showListOnly) {   //-------- step7
        services = listServices(priorityFlags, asProto);
        setServiceArgs(args, asProto, priorityFlags);
    }

当通过step5 确认之后,发现没有指定service,或者dumpsys 携带了 -l 的选项,会统计所有的servcies,并且指定args;

其中需要注意的是listServices() 的参数priorityFlags,这里列举的是符合该flags 的services,默认值为IServiceManager::DUMP_FLAG_PRIORITY_ALL,也就是说,如果某service(例如,meminfo) 需要通过dumpsys 命令进行dump 操作,必要将指定dump flag。

step8. 打印services

    const size_t N = services.size();
    if (N > 1) {    //-------- step8
        // first print a list of the current services
        std::cout << "Currently running services:" << std::endl;

        for (size_t i=0; i<N; i++) {
            sp<IBinder> service = sm_->checkService(services[i]);

            if (service != nullptr) {
                bool skipped = IsSkipped(skippedServices, services[i]);
                std::cout << "  " << services[i] << (skipped ? " (skipped)" : "") << std::endl;
            }
        }
    }

如果不是指定的services,或者dumpsys 携带了-l,那么就会列举出来;

注意的是,如果某个service 在skip 中,则在列举的时候在最后加上  (skipped):

127|shift:/ # dumpsys --skip window SurfaceFlinger activity gfxinfo input package activity
Currently running services:
  DockObserver
  SurfaceFlinger (skipped)
  accessibility
  account
  activity (skipped)
  activity_task

step9. -l 的参数不进行dump

    if (showListOnly) {
        return 0;
    }

如果携带了 -l 的参数,不会进入 dump 操作;

step10. dumpsys 的核心dump操作

    for (size_t i = 0; i < N; i++) {
        const String16& serviceName = services[i];
        if (IsSkipped(skippedServices, serviceName)) continue;

        if (startDumpThread(type, serviceName, args) == OK) {
            bool addSeparator = (N > 1);
            if (addSeparator) {
                writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags);
            }
            std::chrono::duration<double> elapsedDuration;
            size_t bytesWritten = 0;
            status_t status =
                writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(timeoutArgMs),
                          asProto, elapsedDuration, bytesWritten);

            if (status == TIMED_OUT) {
                std::cout << std::endl
                     << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs
                     << "ms) EXPIRED ***" << std::endl
                     << std::endl;
            }

            if (addSeparator) {
                writeDumpFooter(STDOUT_FILENO, serviceName, elapsedDuration);
            }
            bool dumpComplete = (status == OK);
            stopDumpThread(dumpComplete);
        }
    }

经过上面 step5 ~ step7 之后skip services 和目标service 已经统计完毕,如果没有指定service,那就是dumpsys 所有的符合dump flag 的service(不包括 --skip的),那么这里的 N 应该很大,通过for 循环进行dump;如果指定了service,例如dumpsys meminfo(详细的meminfo 可以查看:dumpsys meminfo 详解 ),那么这里的 N 应该是为 1.

meminfo 有三种命令模式:

  • dumpsys meminfo (后面有可能加--skip)
  • dumpsys meminfo com.android.launcher3
  • dumpsys meminfo --pid 1925

第一种是查看系统所有的进程的内存使用情况;第二种是单独查看应用Luancher3 的内存使用情况;第三种是指定了Launcher3 的pid 通过pid 查询Launcher3 的内存使用情况;

IsSkipped() 用来确定当前的serviceName 是否在需要skip 的list 中,如果在就跳过,不去dump 操作;

startDumpThread() 会创建一个阻塞线程,等待dump 处理完成;

addSeparator 用来确认是否有多个service dump,如果有,在servcie 之间加上说明dump 到哪里了;

进入writeDump(),创建死循环,通过poll 机制等待service 端数据写入,如果有数据到来会输出到终端 STDOUT_FILENO

下面一节来看下startDumpThread 做了什么操作。

 4. startDumpThread()

status_t Dumpsys::startDumpThread(Type type, const String16& serviceName,
                                  const Vector<String16>& args) {
    sp<IBinder> service = sm_->checkService(serviceName);
    if (service == nullptr) {
        std::cerr << "Can't find service: " << serviceName << std::endl;
        return NAME_NOT_FOUND;
    }

    int sfd[2];
    if (pipe(sfd) != 0) {
        std::cerr << "Failed to create pipe to dump service info for " << serviceName << ": "
             << strerror(errno) << std::endl;
        return -errno;
    }

    redirectFd_ = unique_fd(sfd[0]);
    unique_fd remote_end(sfd[1]);
    sfd[0] = sfd[1] = -1;

    // dump blocks until completion, so spawn a thread..
    activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable {
        status_t err = 0;

        switch (type) {
        case Type::DUMP:
            err = service->dump(remote_end.get(), args);
            break;
        case Type::PID:
            err = dumpPidToFd(service, remote_end);
            break;
        default:
            std::cerr << "Unknown dump type" << static_cast<int>(type) << std::endl;
            return;
        }

        if (err != OK) {
            std::cerr << "Error dumping service info status_t: " << statusToString(err) << " "
                 << serviceName << std::endl;
        }
    });
    return OK;
}
  • 首先通过checkservice() 来确认serviceName 的正确性;
  • 创建一个pipe 管道;
  • 通过std::thread() 创建一个thread(C++11 特性),用以触发service dump;

dumpsys 创建了一个pipe,把pipe[1] 通过dump 方式传递到services端,然后在函数 writeDump() 中通过 poll 机制开始监听pipe[0],当产生通讯之后或者 timeout 之后会结束此次service dump。

由于时间原因, 其他的过程暂时不进行剖析了,总结下dumpsys 的流程大致如下:

5. 系统中所有的services

130|frost:/ # dumpsys -l
Currently running services:
  DockObserver
  SurfaceFlinger
  accessibility
  account
  activity
  activity_task
  adb
  alarm
  android.frameworks.stats.IStats/default
  android.hardware.light.ILights/default
  android.hardware.power.IPower/default
  android.hardware.vibrator.IVibrator/default
  android.os.UpdateEngineService
  android.os.UpdateEngineStableService
  android.security.apc
  android.security.authorization
  android.security.compat
  android.security.identity
  android.security.legacykeystore
  android.security.maintenance
  android.security.metrics
  android.service.gatekeeper.IGateKeeperService
  android.system.keystore2.IKeystoreService/default
  app_binding
  app_hibernation
  app_integrity
  app_search
  appops
  appwidget
  audio
  auth
  autofill
  backup
  battery
  batteryproperties
  batterystats
  binder_calls_stats
  biometric
  blob_store
  bluetooth_manager
  bugreport
  cacheinfo
  carrier_config
  clipboard
  color_display
  companiondevice
  connectivity
  connmetrics
  consumer_ir
  content
  country_detector
  cpuinfo
  crossprofileapps
  dataloader_manager
  dbinfo
  device_config
  device_identifiers
  device_policy
  device_state
  deviceidle
  devicestoragemonitor
  diskstats
  display
  dnsresolver
  domain_verification
  dpmservice
  dreams
  drm.drmManager
  dropbox
  dynamic_system
  emergency_affordance
  external_vibrator_service
  extphone
  file_integrity
  fingerprint
  font
  game
  gfxinfo
  gpu
  graphicsstats
  hardware_properties
  imms
  incident
  incidentcompanion
  incremental
  input
  input_method
  inputflinger
  installd
  ions
  iphonesubinfo
  ipsec
  isms
  isub
  jobscheduler
  launcherapps
  legacy_permission
  lights
  location
  location_time_zone_manager
  lock_settings
  looper_stats
  manager
  media.audio_flinger
  media.audio_policy
  media.camera
  media.camera.proxy
  media.extractor
  media.metrics
  media.player
  media.resource_manager
  media.resource_observer
  media.wfd
  media_communication
  media_metrics
  media_projection
  media_resource_monitor
  media_router
  media_session
  meminfo
  memtrack.proxy
  midi
  mount
  netd
  netd_listener
  netpolicy
  netstats
  network_management
  network_score
  network_stack
  network_time_update_service
  network_watchlist
  notification
  oem_lock
  otadexopt
  overlay
  pac_proxy
  package
  package_native
  people
  performance_hint
  perfservice
  permission
  permission_checker
  permissionmgr
  persistent_data_block
  phone
  pinner
  platform_compat
  platform_compat_native
  power
  powerstats
  print
  processinfo
  procstats
  qti.radio.extphone
  reboot_readiness
  recovery
  restrictions
  role
  rollback
  runtime
  scheduling_policy
  search
  search_ui
  sec_key_att_app_id_provider
  secure_element
  sensor_privacy
  sensorservice
  serial
  servicediscovery
  settings
  shortcut
  simphonebook
  sip
  slice
  smartspace
  soundtrigger
  soundtrigger_middleware
  speech_recognition
  stats
  statscompanion
  statsmanager
  statusbar
  storaged
  storaged_pri
  storagestats
  suspend_control
  suspend_control_internal
  system_config
  system_server_dumper
  system_update
  telecom
  telephony.registry
  telephony_ims
  testharness
  tethering
  textclassification
  textservices
  texttospeech
  thermalservice
  time_detector
  time_zone_detector
  tracing.proxy
  trust
  uce
  uimode
  updatelock
  uri_grants
  usagestats
  usb
  user
  vcn_management
  vendor.logagent
  vendor.logserver
  vendor.sysagent
  vibrator_manager
  voiceinteraction
  vold
  vpn_management
  wallpaper
  webviewupdate
  wifi
  wifiaware
  wifinl80211
  wifip2p
  wifirtt
  wifiscanner
  window

下面来详细说明一下这个service。

1. meminfo

这个是比较常用的service,可以看一下:android 查看内存使用情况

2. cpuinfo

可以看一下:dumpsys cpuinfo

3. window

4. surfaceflinger

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

私房菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值