分析ueventd Coldboot耗时问题
安卓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耗时问题的更多相关文章
- android init进程分析 ueventd
转自:http://blog.csdn.net/freshui/article/details/2132299 (懒人最近想起我还有csdn好久没打理了,这个Android init躺在我的草稿箱中快 ...
- mysql索引无效且sending data耗时巨大原因分析
一朋友最近新上线一个项目,本地测试环境跑得好好的,部署到线上却慢得像蜗牛一样.后来查询了一下发现一个sql执行了16秒,有些长的甚至80秒.本地运行都是毫秒级别的查询.下面记录一下困扰了两天的,其中一 ...
- Java 性能分析工具 , 第 2 部分:Java 内置监控工具
引言 本文为 Java 性能分析工具系列文章第二篇,第一篇:操作系统工具.在本文中将介绍如何使用 Java 内置监控工具更加深入的了解 Java 应用程序和 JVM 本身.在 JDK 中有许多内置的工 ...
- Traceview 性能分析工具
简介 TraceView 是 Android 平台配备一个很好的性能分析的工具.它可以通过图形化的方式让我们了解我们要跟踪的程序的性能,并且能具体到 method.详细内容参考:http://deve ...
- MySQL 索引性能分析概要
上一篇文章 MySQL 索引设计概要 介绍了影响索引设计的几大因素,包括过滤因子.索引片的宽窄与大小以及匹配列和过滤列.在文章的后半部分介绍了 数据库索引设计与优化 一书中,理想的三星索引的设计流程和 ...
- 稳定性 耗时 gc 过长问题排查 和工具
自己的另外一篇: http://www.cnblogs.com/fei33423/p/7805186.html 偶有耗时抖动? gc 也有长耗时? fullgc 也是? 有同学反馈 swap 可能导致 ...
- 一次 Laravel 性能分析全程笔记
大家都知道 laravel 项目写起来是挺爽,但是在生产环境性能不高,我们来抽丝剥茧分析我自己项目的运行时间消耗: Bootstrap 耗时 步骤 耗时 Illuminate\Foundation\B ...
- Android Activity启动耗时统计方案
作者:林基宗 Activity的启动速度是很多开发者关心的问题,当页面跳转耗时过长时,App就会给人一种非常笨重的感觉.在遇到某个页面启动过慢的时候,开发的第一直觉一般是onCreate执行速度太慢了 ...
- 前端性能优化之利用 Chrome Dev Tools 进行页面性能分析
背景 我们经常使用 Chrome Dev Tools 来开发调试,但是很少知道怎么利用它来分析页面性能,这篇文章,我将详细说明怎样利用 Chrome Dev Tools 进行页面性能分析及性能报告数据 ...
- 页面性能分析-Chrome Dev Tools
一.分析面板介绍 进行页面性能快速分析的主要是图中圈出来的几个模块功能: Network : 页面中各种资源请求的情况,这里能看到资源的名称.状态.使用的协议(http1/http2/quic...) ...
随机推荐
- Linux网络问题排查
Linux网络问题排查 用于排查Linux系统的网络故障. 网络排查一般是有一定的思路和顺序的,其实排查的思路就是根据具体的问题逐段排除故障可能发生的地方,最终确定问题. 所以首先要问一问,网络问题是 ...
- 12、SpringMVC之拦截器
12.1.环境搭建 创建名为spring_mvc_interceptor的新module,过程参考9.1节和9.5节 12.1.1.页面请求示例 <a th:href="@{/test ...
- 【Spring Data JPA】04 JPQL和原生SQL
@Transactional注解 让Spring处理事务 不需要自己每次都手动开启提交回滚 FINDONE & GETONE的区别? findone是立即加载 getone是延迟加载,配合事务 ...
- 【Spring-Security】Re12 JsonWebToken
一.认证机制种类: 1.HTTP-Basic-Auth 每次请求接口必须提供账号信息[username + password] 但是信息有暴露风险,配合RestFul风格使用,逐渐淘汰 2.Cooki ...
- 易拍照 —— 毕业生图像采集操作指南——如何使用 “易拍照” 微信小程序进行图像采集
易拍照 -- 毕业生图像采集操作指南--如何使用 "易拍照" 微信小程序进行图像采集 ============================================
- xshell打开vim后颜色异常——xshell连接ubuntu打开vim后界面覆盖一层绿色
参考原文: https://blog.csdn.net/Blank_Shen/article/details/106527312 =================================== ...
- vscode 设置窗口菜单栏显示字体大小
最近换了一块大些的显示屏,发现vscode的窗口字体有些小了,不是很方便,于是研究了一下如何设置vscode的窗口字体大小. 需要注意的是这里的设置是对窗口字体的而不是编辑器的字体. 1 . 通过主 ...
- Apache DolphinScheduler PMC:开源不一定也要九死一生
点亮 ️ Star · 照亮开源之路 GitHub:https://github.com/apache/dolphinscheduler 参与开源已经快3年了,这次在Meetup上没有分享 ...
- Unreal使用GooglePAD生成AAB包,并加在fast-follow资源
1.修改obbfilter,设置需要添加到obb的pak文件 2.修改项目设置,打AAB包 3.cook stage生成所有Paks文件 4.将部分pak文件拷贝到Intermediate/Andro ...
- idea汉化包安装失败解决方法
idea安装中文插件时提示: Plugin "Chinese (Simplified) Language Pack / 中文语言包" was not installed: 查看自己 ...