安卓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. Linux网络问题排查

    Linux网络问题排查 用于排查Linux系统的网络故障. 网络排查一般是有一定的思路和顺序的,其实排查的思路就是根据具体的问题逐段排除故障可能发生的地方,最终确定问题. 所以首先要问一问,网络问题是 ...

  2. 12、SpringMVC之拦截器

    12.1.环境搭建 创建名为spring_mvc_interceptor的新module,过程参考9.1节和9.5节 12.1.1.页面请求示例 <a th:href="@{/test ...

  3. 【Spring Data JPA】04 JPQL和原生SQL

    @Transactional注解 让Spring处理事务 不需要自己每次都手动开启提交回滚 FINDONE & GETONE的区别? findone是立即加载 getone是延迟加载,配合事务 ...

  4. 【Spring-Security】Re12 JsonWebToken

    一.认证机制种类: 1.HTTP-Basic-Auth 每次请求接口必须提供账号信息[username + password] 但是信息有暴露风险,配合RestFul风格使用,逐渐淘汰 2.Cooki ...

  5. 易拍照 —— 毕业生图像采集操作指南——如何使用 “易拍照” 微信小程序进行图像采集

    易拍照 -- 毕业生图像采集操作指南--如何使用 "易拍照" 微信小程序进行图像采集 ============================================

  6. xshell打开vim后颜色异常——xshell连接ubuntu打开vim后界面覆盖一层绿色

    参考原文: https://blog.csdn.net/Blank_Shen/article/details/106527312 =================================== ...

  7. vscode 设置窗口菜单栏显示字体大小

    最近换了一块大些的显示屏,发现vscode的窗口字体有些小了,不是很方便,于是研究了一下如何设置vscode的窗口字体大小. 需要注意的是这里的设置是对窗口字体的而不是编辑器的字体. 1 .  通过主 ...

  8. Apache DolphinScheduler PMC:开源不一定也要九死一生

    点亮 ️ Star · 照亮开源之路 GitHub:https://github.com/apache/dolphinscheduler   ​   参与开源已经快3年了,这次在Meetup上没有分享 ...

  9. Unreal使用GooglePAD生成AAB包,并加在fast-follow资源

    1.修改obbfilter,设置需要添加到obb的pak文件 2.修改项目设置,打AAB包 3.cook stage生成所有Paks文件 4.将部分pak文件拷贝到Intermediate/Andro ...

  10. idea汉化包安装失败解决方法

    idea安装中文插件时提示: Plugin "Chinese (Simplified) Language Pack / 中文语言包" was not installed: 查看自己 ...