安卓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. SEO初学指南之关键词研究(1) - 入门

    Hi,我是听风. 第一篇给大家科普了什么是SEO以及搜索引擎的原理,这篇文章我们正式进入关键词研究的章节. 本片文章将学习什么是关键词以及如何挑选符合预期的关键词. 一.什么是SEO关键词 简单来说就 ...

  2. 简单聊聊WebDAV

    1.什么是WebDAV? WebDAV是一种基于HTTP协议的扩展,旨在提供在Web服务器上进行文件管理的标准化解决方案.它允许用户通过网络对远程主机上的文件进行读写.编辑和删除操作.与传统的HTTP ...

  3. P1973 [NOI2011] NOI 嘉年华

    思路: 先将时间进行离散化,设总时间为 \(cnt\),然后考虑求出 \(W(l,r)\),即在时间段 \([l,r]\) 内的所有节目,可以 \(n^2\) 前缀和,也可以 \(n^3\) 暴力. ...

  4. .NET 开源快捷的数据库文档查询和生成工具

    前言 在实际项目开发中,需求变更和项目迭代是常态.要求我们能够迅速响应,对数据库结构进行相应的调整,如添加新表.更新现有表结构或增加字段等. 为了确保团队成员之间的信息同步,实时更新和维护数据库文档变 ...

  5. BCLinux 8.2安装配置图解教程--龙蜥社区国产移动云系统

    社区镜像下载地址:https://openanolis.cn/download 安装参考地址:https://www.osyunwei.com/archives/13017.html 1安装系统 界面 ...

  6. 【Vue2】Direct 指令

    1.内容渲染指令 1.插值表达式 2.V - TEXT 3.V - HTML <!DOCTYPE html> <html lang="en"> <he ...

  7. 【RabbitMQ】10 深入部分P3 死信队列(交换机)

    1.死信交换机 说是死信队列,是因为RabbitMQ和其他中间件产品不一样 有交换机的概念和这个东西存在,别的产品只有队列一说 DeadLetterExchange 消息成为DeadMessage之后 ...

  8. 机器人的运动控制是否可以引入生物学信息,生物学信息是否可以辅助机器人的智能控制算法的训练:《Robotic prosthetic ankles improve 'natural' movement, stability》

    相关内容: Robotic prosthetic ankles improve 'natural' movement, stability 看了上面的论文的介绍(内容没看到,不是open access ...

  9. Ubuntu18.04 环境下 解决VScode中空格长度减小的问题

       问题:   Ubuntu下安装vscode后发现空格特别小,只有正常空格的一半大小 ======================================================= ...

  10. MFC实现屏幕截屏

    屏幕截屏 void CMainFormDlg::GetScreenPic(Rect area, OUT Mat &img, float rate, bool gray) { CDC *pDC ...