安卓go平台启动时间发现如下ueventd耗时1.907s问题:

01-11 00:20:02.854     0     0 I init    : Parsing file /odm/etc/init...
01-11 00:20:02.854 0 0 E init : Unable to open '/odm/etc/init': No such file or directory
01-11 00:20:02.854 0 0 I init : processing action (early-init) from (/init.rc:14)
01-11 00:20:02.855 0 0 D SELinux : initialized (dev cgroup, type cgroup), uses genfs_contexts
01-11 00:20:02.856 0 0 I init : starting service 'ueventd'...
01-11 00:20:02.857 0 0 W cgroup : init (1) created nested cgroup for controller "memory" which has incomplete hierarchy support. Nested cgroups may change behavior in the future.
01-11 00:20:02.857 0 0 W cgroup : "memory" requires setting use_hierarchy to 1 on the root
01-11 00:20:02.857 0 0 I init : processing action (early-init) from (/vendor/etc/init/hw/init.qcom.rc:35)
01-11 00:20:02.858 0 0 I init : processing action (early-init) from (/vendor/etc/init/hw/init.target.rc:32)
01-11 00:20:02.859 0 0 I init : processing action (wait_for_coldboot_done) from (<Builtin Action>:0)
01-11 00:20:02.861 0 0 I ueventd : ueventd started!
01-11 00:20:02.861 0 0 I ueventd : Parsing file /ueventd.rc...
01-11 00:20:02.867 0 0 I ueventd : Parsing file /vendor/ueventd.rc...
01-11 00:20:02.871 0 0 I ueventd : Parsing file /odm/ueventd.rc...
01-11 00:20:02.871 0 0 E ueventd : Unable to open '/odm/ueventd.rc': No such file or directory
01-11 00:20:02.871 0 0 I ueventd : Parsing file /ueventd.qcom.rc...
01-11 00:20:02.871 0 0 E ueventd : Unable to open '/ueventd.qcom.rc': No such file or directory
01-11 00:20:02.881 0 0 I selinux : SELinux: Loaded file_contexts
01-11 00:20:03.137 0 0 I chatty : uid=0(root) logd identical 4 lines
01-11 00:20:03.168 0 0 I selinux : SELinux: Loaded file_contexts
01-11 00:20:04.789 0 0 I ueventd : Coldboot took 1.907 seconds
01-11 00:20:04.791 0 0 I init : Command 'wait_for_coldboot_done' action=wait_for_coldboot_done (<Builtin Action>:0) returned 0 took 1932ms.
01-11 00:20:04.792 0 0 I init : processing action (mix_hwrng_into_linux_rng) from (<Builtin Action>:0)

对应code在system/core/init/ueventd.cpp:

void ColdBoot::Run() {
android::base::Timer cold_boot_timer; RegenerateUevents(); ForkSubProcesses(); DoRestoreCon(); WaitForSubProcesses(); close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
}

在ueventd main中运行:

int ueventd_main(int argc, char** argv) {
/*
* init sets the umask to 077 for forked processes. We need to
* create files with exact permissions, without modification by
* the umask.
*/
umask(000); InitKernelLogging(argv); LOG(INFO) << "ueventd started!"; selinux_callback cb;
cb.func_log = selinux_klog_callback;
selinux_set_callback(SELINUX_CB_LOG, cb); DeviceHandler device_handler = CreateDeviceHandler();
UeventListener uevent_listener; if (access(COLDBOOT_DONE, F_OK) != 0) {
ColdBoot cold_boot(uevent_listener, device_handler);
cold_boot.Run();
}

init.rc会触发ueventd启动:

on early-init
...
start ueventd

system/core/init/init.cpp会解析init.rc:

int main(int argc, char** argv) {
...
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
parser.set_is_system_etc_init_loaded(
parser.ParseConfig("/system/etc/init"));
parser.set_is_vendor_etc_init_loaded(
parser.ParseConfig("/vendor/etc/init"));
parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
} else {
parser.ParseConfig(bootscript);
parser.set_is_system_etc_init_loaded(true);
parser.set_is_vendor_etc_init_loaded(true);
parser.set_is_odm_etc_init_loaded(true);
}

解析完后会继续触发boot actions,首先是early-init:

am.QueueEventTrigger("early-init");

// Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...
am.QueueBuiltinAction(wait_for_coldboot_done_action, "wait_for_coldboot_done");
// ... so that we can start queuing up actions that require stuff from /dev.
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
am.QueueBuiltinAction(set_mmap_rnd_bits_action, "set_mmap_rnd_bits");
am.QueueBuiltinAction(set_kptr_restrict_action, "set_kptr_restrict");
am.QueueBuiltinAction(keychord_init_action, "keychord_init");
am.QueueBuiltinAction(console_init_action, "console_init"); // Trigger all the boot actions to get us started.
am.QueueEventTrigger("init");

builtin action入队看下:

void ActionManager::QueueEventTrigger(const std::string& trigger) {
event_queue_.emplace(trigger);
} void Action::AddCommand(BuiltinFunction f, const std::vector<std::string>& args, int line) {
commands_.emplace_back(f, args, line);
} void ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {
auto action = std::make_unique<Action>(true, "<Builtin Action>", 0);
std::vector<std::string> name_vector{name}; if (!action->InitSingleTrigger(name)) {
return;
} action->AddCommand(func, name_vector, 0); event_queue_.emplace(action.get()); //也queue event
actions_.emplace_back(std::move(action));
}

由上可见,最先处理action是early-init,然后是wait_for_coldboot_done:

入队以后会执行命令了:

while (true) {
// By default, sleep until something happens.
int epoll_timeout_ms = -1; if (do_shutdown && !shutting_down) {
do_shutdown = false;
if (HandlePowerctlMessage(shutdown_command)) {
shutting_down = true;
}
} if (!(waiting_for_prop || sm.IsWaitingForExec())) {
am.ExecuteOneCommand();
}

am执行命令接口,先从queue里找到最先入队action,就是early-init了:

void ActionManager::ExecuteOneCommand() {
// Loop through the event queue until we have an action to execute
while (current_executing_actions_.empty() && !event_queue_.empty()) {
for (const auto& action : actions_) {
if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
event_queue_.front())) {
current_executing_actions_.emplace(action.get());
}
}
event_queue_.pop();
} if (current_executing_actions_.empty()) {
return;
} auto action = current_executing_actions_.front(); if (current_command_ == 0) {
std::string trigger_name = action->BuildTriggersString();
LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
<< ":" << action->line() << ")";
} action->ExecuteOneCommand(current_command_);

action的执行命令,超过50ms才会报个log,从log看显然early-init没有,是wait_for_coldboot_done超时了:

void Action::ExecuteCommand(const Command& command) const {
android::base::Timer t;
int result = command.InvokeFunc(); //这里 auto duration = t.duration();
// Any action longer than 50ms will be warned to user as slow operation
if (duration > 50ms || android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {
std::string trigger_name = BuildTriggersString();
std::string cmd_str = command.BuildCommandString(); LOG(INFO) << "Command '" << cmd_str << "' action=" << trigger_name << " (" << filename_
<< ":" << command.line() << ") returned " << result << " took "
<< duration.count() << "ms.";
}
}
int Command::InvokeFunc() const {
std::vector<std::string> expanded_args;
expanded_args.resize(args_.size());
expanded_args[0] = args_[0];
for (std::size_t i = 1; i < args_.size(); ++i) {
if (!expand_props(args_[i], &expanded_args[i])) {
LOG(ERROR) << args_[0] << ": cannot expand '" << args_[i] << "'";
return -EINVAL;
}
} return func_(expanded_args);
}

这里的func_就先是do_start了,这个command就是一开始解析init.rc时add command了:

bool Action::AddCommand(const std::vector<std::string>& args, int line, std::string* err) {
if (!function_map_) {
*err = "no function map available";
return false;
} auto function = function_map_->FindFunction(args, err); //从function map找到start命令对应的func:do_start
if (!function) {
return false;
} AddCommand(function, args, line);
return true;
} bool ActionParser::ParseLineSection(std::vector<std::string>&& args, int line, std::string* err) {
return action_ ? action_->AddCommand(std::move(args), line, err) : false;
}

function map里start命令对应的接口是do_start:

const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
// clang-format off
static const Map builtin_functions = {
...
{"start", {1, 1, do_start}},

do_start:

static int do_start(const std::vector<std::string>& args) {
Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
if (!svc) {
LOG(ERROR) << "do_start: Service " << args[1] << " not found";
return -1;
}
if (!svc->Start())
return -1;
return 0;
}

服务启动入口:

bool Service::Start() {
// Starting a service removes it from the disabled or reset state and
// immediately takes it out of the restarting state if it was in there.
flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START)); // Running processes require no additional work --- if they're in the
// process of exiting, we've ensured that they will immediately restart
// on exit, unless they are ONESHOT.
if (flags_ & SVC_RUNNING) {
return false;
}
...
LOG(INFO) << "starting service '" << name_ << "'...";

这个service就是ueventd了,入口是ueventd_main:

int ueventd_main(int argc, char** argv) {
/*
* init sets the umask to 077 for forked processes. We need to
* create files with exact permissions, without modification by
* the umask.
*/
umask(000); InitKernelLogging(argv); LOG(INFO) << "ueventd started!";

ok,接下来就会从queue里取出coldboot_done_action:

static int wait_for_coldboot_done_action(const std::vector<std::string>& args) {
Timer t; LOG(ERROR) << "Waiting for " COLDBOOT_DONE "..."; // Historically we had a 1s timeout here because we weren't otherwise
// tracking boot time, and many OEMs made their sepolicy regular
// expressions too expensive (http://b/19899875). // Now we're tracking boot time, just log the time taken to a system
// property. We still panic if it takes more than a minute though,
// because any build that slow isn't likely to boot at all, and we'd
// rather any test lab devices fail back to the bootloader.
if (wait_for_file(COLDBOOT_DONE, 60s) < 0) {
LOG(ERROR) << "Timed out waiting for " COLDBOOT_DONE;
panic();
} property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));
return 0;
}

他会等待COLDBOOT_DONE文件生成,看log就是下面这个文件了:

[    2.180281] init: Waiting for /dev/.coldboot_done...

也就是等待ueventd ColdBoot跑完:

void ColdBoot::Run() {
...
LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";
}

最后发现竟然是DoRestoreCon()耗时,看来是我们自家的sepolicy file_context太多了?

ps: 编译boot.img烧录验证。

分析ueventd Coldboot耗时问题的更多相关文章

  1. android init进程分析 ueventd

    转自:http://blog.csdn.net/freshui/article/details/2132299 (懒人最近想起我还有csdn好久没打理了,这个Android init躺在我的草稿箱中快 ...

  2. mysql索引无效且sending data耗时巨大原因分析

    一朋友最近新上线一个项目,本地测试环境跑得好好的,部署到线上却慢得像蜗牛一样.后来查询了一下发现一个sql执行了16秒,有些长的甚至80秒.本地运行都是毫秒级别的查询.下面记录一下困扰了两天的,其中一 ...

  3. Java 性能分析工具 , 第 2 部分:Java 内置监控工具

    引言 本文为 Java 性能分析工具系列文章第二篇,第一篇:操作系统工具.在本文中将介绍如何使用 Java 内置监控工具更加深入的了解 Java 应用程序和 JVM 本身.在 JDK 中有许多内置的工 ...

  4. Traceview 性能分析工具

    简介 TraceView 是 Android 平台配备一个很好的性能分析的工具.它可以通过图形化的方式让我们了解我们要跟踪的程序的性能,并且能具体到 method.详细内容参考:http://deve ...

  5. MySQL 索引性能分析概要

    上一篇文章 MySQL 索引设计概要 介绍了影响索引设计的几大因素,包括过滤因子.索引片的宽窄与大小以及匹配列和过滤列.在文章的后半部分介绍了 数据库索引设计与优化 一书中,理想的三星索引的设计流程和 ...

  6. 稳定性 耗时 gc 过长问题排查 和工具

    自己的另外一篇: http://www.cnblogs.com/fei33423/p/7805186.html 偶有耗时抖动? gc 也有长耗时? fullgc 也是? 有同学反馈 swap 可能导致 ...

  7. 一次 Laravel 性能分析全程笔记

    大家都知道 laravel 项目写起来是挺爽,但是在生产环境性能不高,我们来抽丝剥茧分析我自己项目的运行时间消耗: Bootstrap 耗时 步骤 耗时 Illuminate\Foundation\B ...

  8. Android Activity启动耗时统计方案

    作者:林基宗 Activity的启动速度是很多开发者关心的问题,当页面跳转耗时过长时,App就会给人一种非常笨重的感觉.在遇到某个页面启动过慢的时候,开发的第一直觉一般是onCreate执行速度太慢了 ...

  9. 前端性能优化之利用 Chrome Dev Tools 进行页面性能分析

    背景 我们经常使用 Chrome Dev Tools 来开发调试,但是很少知道怎么利用它来分析页面性能,这篇文章,我将详细说明怎样利用 Chrome Dev Tools 进行页面性能分析及性能报告数据 ...

  10. 页面性能分析-Chrome Dev Tools

    一.分析面板介绍 进行页面性能快速分析的主要是图中圈出来的几个模块功能: Network : 页面中各种资源请求的情况,这里能看到资源的名称.状态.使用的协议(http1/http2/quic...) ...

随机推荐

  1. 写几个有用的lambda

    List<String> list = Arrays.asList("app", "ban", "ora"); //循环输出 f ...

  2. 买卖股票相关算法-动态规划-python

    要求1: 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格. 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票.设计 ...

  3. Linux环境 yum,apt-get,rpm,wget 区别

    Linux环境 yum,apt-get,rpm,wget 区别 一般来说linux系统基本上分两大类:cat /etc/issue查看linux系统版本RedHat系列:Redhat.Centos.F ...

  4. Python+requests编写接口用例

    1.python编写接口用例需要依赖requests模块,我们先确认本地是否安装了requests模块,没有安装的先进行安装. pycharm工具查看已安装的模块:File->Setting-& ...

  5. 【Mybatis】02 快速入门Part2 补完CRUD

    这是我们的UserMapper.xml文件 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE ...

  6. 【Tool】常用软件地址(装机备用)

    浏览器: 360极速 https://browser.360.cn/ee/ 谷歌 https://www.google.cn/chrome/ 社交通讯 微信 https://weixin.qq.com ...

  7. 【Tutorial C】01 概述

    历史 History 欢迎来到C语言的世界!C语言是一种强大的专业化编程语言,深受业余和专业编程人员的欢迎. 在学习之前先让我们了解和认识它! C语言的原型是A语言(ALGOL 60语言). 1963 ...

  8. 【JSON】JavaScript Object Notation JS对象表示规则

    什么是 JSON? JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式. 易于人阅读和编写.同时也易于机器解析和生成. JSON采用完全独立于语言的文本格式 ...

  9. 【SqlServer】02 SSMS工具基本使用入门

    之前的安装中除了SqlServer,还有一个SSMS管理工具 数据库的访问依赖于工具 SSMS提供了两种登陆方式: 创建用户: 删除用户: 创建数据库: 删除数据库: 创建表: 设置表的字段,字段名称 ...

  10. 利用强化学习算法解释人类脑对高维状态的抽象表示:how humans can map high-dimensional sensory inputs in actions

    论文: <Using deep reinforcement learning to reveal how the brain encodes abstract state-space repre ...