MoreRSS

site iconGityuan | 袁辉辉

字节跳动国际化直播电商客户端负责人,曾就职于小米、联想、IBM。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Gityuan | 袁辉辉的 RSS 预览

字节跳动为什么选用Flutter:并非跨平台终极之选,但它可能是不一样的未来

2020-03-27 06:12:40

2018 年 12 月 ,Google 宣布 Flutter 1.0 版本正式发布。截至目前, Flutter 在 Github 上已获得 88000+ 的关注和 11000+ 的 Fork ,其发展速度相当惊人,是今年移动端最火热的开发框架之一。

Flutter 大火背后的原因是什么?为什么越来越多的企业和开发者会选择使用 Flutter?Flutter 会成为跨平台开发的终极之选吗?我认为“ Flutter 并非跨平台终极之选,最初选择 Flutter,不是因为它一定会成为未来终极之选,而是因为它有可能成为不一样的未来。”

Flutter 大火的原因

有人说 Flutter 大火主要原因是它选择了 Dart 语言,Dart 有着高性能的表现和可快速分配内存的能力,能同时支持 JIT 和 AOT 模式,允许在带类型的语言中支持形变和有状态热重载,能编译出高效率的 ARM 机器码指令,Dart 作为面向对象的语言也能让绝大多数开发者更快速上手。我认可 Dart 语言有一定的优势,但这样的优势并非 Dart 独有,我想这更不会是大家选择 Flutter 的核心原因,这是因果倒置。事实上,Dart 是 2011 年推出的,在 Flutter 出现之前,Dart 曾一度几乎被人遗忘。正是因为近年来 Flutter 的火爆,才让 Dart 重新进入大众的视线。Flutter 当初选择 Dart,或者仅因为 Google 的 Flutter 和 Dart 这两个团队离得比较近,交流比较方便。 我认为 Flutter 之所以大火,主要是以下几个原因:

1. 现有跨平台技术存在缺陷

在移动互联网时代,Android 和 iOS 两大阵营长期共存,再加上体系成熟的 Web 前端技术,导致出现同一个应用需多端重复开发的人力成本问题。正因如此,移动时代下的跨平台技术是一个需要长期研究的课题。如果当下的跨平台技术已经有比较完美的解决方案,可能就没有新技术萌芽的机会。而事实上,目前业界比较成熟的跨平台技术都存在一定的缺陷,比如小程序(WebView)渲染耗时过长,白屏率会影响转化收益,能实现的功能非常受限;再比如 React Native 的性能不足、问题排除难、维护成本高等。而 Flutter 的出现,让这些跨平台开发问题有所改善,它还是 Google 开源的技术,自身也具备一定的热度。另外,一直备受关注且神秘的 Fuchsia 系统在 UI 框架上使用的也是 Flutter,可作为长期战略投入,这也增强了大家对 Flutter 的信心。

2. 研发效率就是竞争力

移动互联网进入下半场,出现一些新兴互联网独角兽、小巨头,在没有历史包袱的情况下,更愿意尝试技术上限更高的新技术。从校招和社招的难度上不难发现:客户端的人才相比之前更为稀缺,尤其是 iOS 工程师。而下半场会有更多竞争和更为激烈的赛道,比如教育等方向。Flutter 本身非常适合从零开始的没有历史包袱的应用开发,对于新业务尤其是在团队人力紧缺的情况下,在技术选型上考虑 Flutter,能加快产品在多端落地、快速试错。

3. 集漂亮与流畅集于一身

Flutter “一出生”就以“UI 漂亮、像素级可控、性能流畅、可媲美原生性能”等特点吸引广大开发者的眼球,自渲染引擎甚至具备开发游戏的能力。移动下半场,没有人口红利,竞争更为激烈,如何能更好地满足用户对高品质、高流畅的需求,便是移动端一种强有力的竞争力。跨平台技术想要拥有更高的流畅度,采用自渲染技术的方案便是更优解,也是一个更为彻底的跨平台技术方向。

字节跳动选择 Flutter 的初心

说到这里,先分享一下 Flutter 最初是如何诞生的故事。Flutter 创始人 Eric 之前在 Chrome 团队工作,期间遇到一些难以解决的问题, 希望 Web 中的一部分能够拥有更加平滑的体验, 为此他花了几周时间做了一个实验,不考虑 Web 的兼容方式,删除了大量为了兼容访问的代码和一些 Web 开发者不常用的功能, 删除到有不少 Web 元素的渲染已经不支持了,然后做了一个基准测试,得出结论是某些关注指标的速度快了 20 倍。于是,Eric 决定再做点什么,后面投入了大量研究和开发,便有了现在的 Flutter 。

听到这里给人的感觉是,对于 Web 工程师而言 Flutter 应该容易上手。我跟公司很多正在使用或者调研 Flutter 的业务团队做过沟通,发现客户端比前端的同学对 Flutter 接受度更高,我个人从 Android 端技术出身,的确觉得学习 Flutter 还是非常容易上手的,但公司内前端的同学对 Flutter 使用的吐槽会多一点。所以,我认为 Flutter 更像是以客户端视角的跨平台技术,Flutter 与其说是大前端技术,不如说是大移动端技术。Flutter 发展的 Roadmap 也是先全面支持 Android/iOS 端能力,再进一步完善 Web 端能力支持的。

字节跳动对于客户端技术还是非常重视的,字节跳动有很多客户端工程师,之前客户端深入点的基础技术更多是搞插件化、热修复、性能优化、安全加固等,跨平台方向一直都是前端工程师在不遗余力地推进,属于大前端方向。而 Flutter 是客户端更有主导的跨平台技术方案。另外说明,字节跳动并不是说只有一套跨平台技术栈,公司内部也是多套跨端技术栈并存,也包括自研的方案。

在字节跳动,跨平台技术并没有形成大规模的落地,之前也提到没有历史包袱,所以在面对跨平台技术选型的时候,更关注跨平台技术的技术上限以及发展潜力,自渲染技术的 Flutter 可以理解为更彻底更纯粹的跨平台技术,伴随着媲美原生的流畅度,这便是我们选择 Flutter 的初心。

Flutter 落地过程中的“坑”

截至目前,字节跳动有很多业务落地了 Flutter 技术方案,包括今日头条、西瓜视频、皮皮虾等 20 多个业务在使用 Flutter 开发,有纯 Flutter 工程,也有 Flutter 与 Native 的混合工程。如果大家想要了解更多业务落地情况,后续我会在今年的 QCon 北京 2020 大会上分享。

Flutter 虽潜力上限很高,但仍需打磨和雕琢,我们在 Flutter 推动过程中遇到很多问题,比如包体积过大的问题、性能达不到预期、对混合工程的支持不够友好、各宿主 Flutter 引擎版本不一致、基础库不完善、Flutter 改造后各项数据打平等。除此之外,还有很多非技术的困难,比如业务团队并不认可 Flutter 新技术,工程师缺乏 Flutter 经验,担忧审核风险等,都会影响业务方是否采用 Flutter 技术,每一个困难都需要去解决,不然就难以落地。下面就其中两个难点,我来展开聊一下。

1. 包体积问题

字节跳动内的大型 APP,比如今日头条、抖音等对包体积的增量非常敏感,Flutter 的包体积涉及两个部分,一个是一次性 Flutter 引擎的包体积问题,一个是每次写 Dart 代码比写 OC 代码代码增量的问题。这两个问题对我们来说都非常棘手,我们成立了包体积优化专项进行全力攻坚,同时跟 Google 工程师多次会议沟通,不断精简包体积。最终我们通过一系列优化手段,包含 Data 压缩、编译优化、Skia 裁剪、BoringSSL/ICU 库 / 文字渲染 /libwebp 等库裁剪取得了不少的效果;通过实践我们发现用 OC 代码和 Dart 代码写相同的业务逻辑,Dart 生成的机器码指令比 OC 多,主要在生成的二进制指令头、指令冗余、指令对齐的精简,以及 StackMap 和 CodeSourceMap 的精简等方面。同时我们也向 Google 反馈了这些情况。关于指令精简,可以查看 Issue 进展,里面有记录详细的推进过程: https://github.com/flutter/flutter/issues/40345

2. 性能优化问题

这是我们遇到的棘手问题之一,我们用 Flutter 官方提供的性能分析工具 Timeline 来分析一个比较诡异的性能问题,始终无法发现任何异常。困扰已久,后来干脆重新撸了一遍 Timeline 整个性能分析工具的源码,最终找到了其缺陷,并向 Flutter 社区提及,合入了 10 个 PR ,基于此让我有幸成为了 Flutter Member ,后续会持续向社区贡献更多力量:https://github.com/flutter/flutter/issues/47771.

Flutter 是一个自渲染的跨平台技术,有着很高的性能上限,但并不代表现在性能各方面都很优秀,毕竟 Flutter 作为一个“新生儿”,还是有一些需要进一步改造的地方。除性能工具改造之外,其实在 Flutter 落地场景中,我们也解决了不少性能问题,同时优化了自身的引擎,比如 UI 预加载策略、Flutter Turbo 技术、Vsync 调度策略等,让引擎提速,争取让 Flutter 性能发挥到极致。

Flutter 在业务层面的发展阻力

引入 Flutter 之后,在公司的业务也创造了不少价值。主要体现在这几个方面:其一,Flutter 多端一致性上表现良好,能做到所见即所得,无需针对某一平台做额外适配工作;其二,热重载技术使得设计团队和工程团队可以非常快速的修改和调试 UI,设计师只需要关注一个平台实现,UI 验收效率明显提高,跨端开发可以提高工程师的人效(有团队初步估算人效大致提升了 1.8 倍);其三,性能流畅度提升,相较于 H5 版本首屏时间有较大提升,最后,产品商业化数据都有明显的收益,能直观地看到 Flutter 给公司带来的创收。

不过,现阶段 Flutter 的发展仍有一些阻力:

1. Flutter 采用的是 Dart 语言,没能引入前端成熟的生态体系

作为前端工程师可能更希望是 Flutter 上层采用的是 JavaScript 或者 TypeScript,未来可考虑提供高性能的 Dart 与 JS 互转能力。另外,Flutter 开发对于前端开发工程师而言,还是有一些挑战的,纯前端不一定能 Cover 的技术,比如 Flutter 的一个硬件相关的 Plugin 只在某款手机出现 Bug,如果社区没有现存解决方案,可能就需要花比较大的时间成本去学习 Native 技术,或者请教客户端工程师。

2. 开源库相对比较欠缺,更新频次不足

Flutter 生态还不够完善,新业务接入需要自己造轮子,尤其是在业务团队对 Flutter 掌握不够熟练的情况下,会增加额外的成本,Flutter 在大中型企业会更容易推广起来,有人力可以去造轮子让公司内其他的业务复用;另外,Flutter 文档有点少,能借鉴的经验不多,未来需加强和鼓励更多开发者加入到生态共建。

3. 跟原生系统生态存在着一定的竞争关系

有朋友跟我说起过这样一件事,看到 Flutter 这么火,Android 开发团队就问他,“大家为什么要用 Flutter 开发 App,我们 Android 哪一点不好,告诉我们,我们可以改进它”。姑且不说他们对跨平台理解不够,但至少能看出原生平台对跨端技术的担忧,不少 Android 团队在推出 Kotlin Multiplatform ,希望能争夺更多市场。 另外,苹果商店的审核风险也是大家所担忧的,官方的公告原意是说应用程序的核心特性和功能必须包含在软件的二进制文件中,而不是通过类似 HTML5 的技术来实现动态更新,苹果要打压的是动态更新技术,考虑到 Flutter 的合规性,Google 主动把 Flutter 的 iOS 动态化能力去掉了,Flutter 最终打包生成的产物就是 IPA,Flutter 其实是完全符合规范的,甚至还有使用 Flutter 开发的应用还被 Apple 推荐过。相反,React Native、Weex、H5 等技术都是一种动态化解决方案,这正是苹果要管控的,目前苹果的态度更多的是不提倡,但也不保证不封杀。即便如此,苹果不希望原生开发生态被其他跨平台技术抢占,苹果也在不断推行 SwiftUI 框架,力图抵挡 Flutter 等跨平台技术对原生开发的蚕食。Flutter 未来要加强推进步伐,让更多的大型 App 通过 Flutter 技术得到收益,只有用户群体上来,未来的地位和话语权才会更高,就像现在小程序,原则上是不符合苹果的审核要求的,但各大型应用基本都上线了小程序功能,目前来看不至于说苹果把小程序直接干掉。

Flutter 并非跨平台终极之选

从 Hybrid App 到 React Native,再到 Flutter,跨平台技术层出不穷。目前来看,Flutter 是跨平台开发的最热门技术,但我并不认为 Flutter 就一定是跨平台开发的终极之选,它有着历史局限性,我只能说 Flutter 可能是当下最有潜力的跨平台技术。如果你对性能流畅度有高要求,或者有多个产品希望快速在多端试错迭代,我会推荐你尝试 Flutter。

未来一段时间,还应该是多套跨平台技术并存的时代, 目前 Flutter 也没有全面做到可以碾压其他跨平台技术,可根据团队以及业务特点来考虑更适合的方案。有一定客户端经验的同学入手 Flutter 会更快一些,如果团队在 React Native 上有很好落地,业务没有遇到性能等瓶颈,且团队缺少客户端能力,建议先做技术调研和沉淀,不要盲目追求新技术,只有当团队有能力且业务有需求的情况下,建议再考虑切换技术栈。

我们前面提到过,一直备受关注且神秘的 Fuchsia 系统在 UI 框架上使用的也是 Flutter。Fuchsia 是 Google 开发的下一代操作系统。Fuchsia 是采用全新模块化设计思想、跨平台框架技术的系统。它能支持快捷裁剪定制,更能适应未来的多元化设备,包含手机、平板、笔记本、路由器、智能设备、机器人等,Fuchsia 有可能成为一个全新的跨全平台的通用操作系统。

在现阶段,开始尝试探索和积累沉淀 Flutter 技术能力,并在业务上使用 Flutter 技术的应用,从战略上来将已经处于领先。选择 Flutter,正可谓是“进可攻退可守”,往前进一步,Flutter 应用未来可无缝迁移到 Fuchsia 系统,借用 Fuchsia 系统的能量扩展到更广泛的用户场景;退一步,Flutter 技术自身在 Android/iOS 平台的表现相比其他跨平台技术已经是很优秀。

最初选择 Flutter,不是因为它一定会成为未来终极之选,而是它有可能成为不一样的未来。

Flutter 展望:终将走向多端一体化

回顾整移动操作系统的演变历程,从塞班功能机到 Android/iOS 智能机,从小屏手机到全面屏、刘海屏、水滴屏。任何系统的演变最终体现在输入和输出两个环节,接收到输入信号后经过操作系统处理后输出信息给用户。从按键式交互到触屏式交互,伴随着塞班系统到 Android 系统的转变,未来的交互方式一定会更加生物智能化,当下的触屏交互可以理解成人类的触觉输入方式,未来将朝着人们更常见的听觉(语音)输入和视觉(身体姿势、表情等)输入,甚至嗅觉(气味变化)输入,这些都会伴随着新的操作系统而诞生。屏幕从小尺寸到大尺寸,并没有引发操作系统变革,因为技术创新是非连续性,非连续性才会引发第二曲线,诞生新技术。从 1960 年大型机,到 1990 年个人笔记本,再到现在的智能手机,设备本身越来越小。未来的设备如果发展非连续变革,可能不再需要实体硬件,随处可输出,一张白纸、一面墙,到那时操作系统的 UI 架构必然会有全新的变化。

随着科技的发展,5G 时代的到来,人工智能的日趋成熟,端到底会有哪些变化?是否会出现新的操作系统?系统的 UI 架构是否会出现新的变革?Android/iOS 平台是否能与之并存?搭载 Flutter UI 框架的 Fuchsia 系统能否在 IOT 领域以及新的交互方式大放异彩,再领风骚?是否有万物互联互通的超级平台出现?

技术在不断演变中螺旋前进,平台自身也随之演进,未来 Flutter 会朝着多端一体化的方向发展,能支持更多的端(包括平板、笔记本、智能设备等)。作为一套跨平台的 UI 框架,Flutter 采用自渲染的技术方案,是一个上限很高的跨平台技术,但 Flutter 更重要的是需要提升工程化能力以及生态圈的建设,才能吸引更多的开发者加入。

加入我们

欢迎更多小伙伴加入字节跳动移动平台部,探索和深耕移动端技术,团队技术氛围浓厚,和我们开创可能成为不一样的未来,Android/iOS/Flutter等在北上深杭招聘各级别岗位,可简历发送至[email protected], 详细内推岗位见 http://gityuan.com/job

彻底掌握Timeline原理(一)

2019-10-20 06:15:40

一、概述

1.1 Timeline事件总览

  • C++引擎层层采用的是采用的是Embedder Stream;
  • Dart应用层采用的是Dart Stream;
  • DartVM采用的API Stream;

1.2 C++层Timeline用法

1.2.1 C++同步事件

#include "flutter/fml/trace_event.h"
#define TRACE_EVENT0(category_group, name)           
#define TRACE_EVENT1(category_group, name, arg1_name, arg1_val)
#define TRACE_EVENT2(category_group, name, arg1_name, arg1_val, arg2_name, arg2_val)   
#define FML_TRACE_EVENT(category_group, name, ...)                 
fml::tracing::TraceEvent0(category_group, name);
fml::tracing::TraceEventEnd(name);

//示例:
TRACE_EVENT0("flutter", "PipelineConsume");
TRACE_EVENT2("flutter", "Framework Workload",
                "mode", "basic", "frame", FrameParity());

1.2.2 C++异步事件

#include "flutter/fml/trace_event.h"
#define TRACE_EVENT_ASYNC_BEGIN0(category_group, name, id)
#define TRACE_EVENT_ASYNC_END0(category_group, name, id)
#define TRACE_EVENT_ASYNC_BEGIN1(category_group, name, id, arg1_name, arg1_val)
#define TRACE_EVENT_ASYNC_END1(category_group, name, id, arg1_name, arg1_val)
void TraceEventAsyncComplete(TraceArg category_group, TraceArg name,
                             TimePoint begin, TimePoint end)
//示例:
TRACE_EVENT_ASYNC_BEGIN0("flutter", "Frame Request Pending", frame_number);
TRACE_EVENT_ASYNC_END0("flutter", "Frame Request Pending", frame_number_++);

另外,这里还有一种比较特别的异步事件,事件的开始和结束都是可以手动指定的,TraceEventAsyncComplete 等于TRACE_EVENT_ASYNC_BEGIN0 + TRACE_EVENT_ASYNC_END0, 如下所示:

void TraceEventAsyncComplete(TraceArg category_group, TraceArg name,
                             TimePoint begin, TimePoint end);

1.2.3 C++事件流

#include "flutter/fml/trace_event.h"
#define TRACE_FLOW_BEGIN(category, name, id)
#define TRACE_FLOW_STEP(category, name, id)
#define TRACE_FLOW_END(category, name, id)

//示例:
TRACE_FLOW_BEGIN("flutter", "PipelineItem", trace_id_)
TRACE_FLOW_STEP("flutter", "PipelineItem", trace_id_)
TRACE_FLOW_END("flutter", "PipelineItem", trace_id_)

1.2.4 DartVM事件

#define API_TIMELINE_DURATION(thread)   
#define API_TIMELINE_BEGIN_END(thread)  

// 示例:
#define T (thread())
API_TIMELINE_DURATION(T);
API_TIMELINE_BEGIN_END(T);

1.3 dart层Timeline用法

1.3.1 dart同步事件

import 'dart:developer';
Timeline.startSync(String name, {Map arguments, Flow flow});
Timeline.finishSync();

//示例:
Timeline.startSync('Warm-up frame');  //静态方法
Timeline.finishSync();

1.3.2 dart异步事件

import 'dart:developer';
TimelineTask.start(String name, {Map arguments});
TimelineTask.finish();

//示例:
final TimelineTask timelineTask = TimelineTask();  //普通方法,需要实例化
timelineTask.start('Warm-up shader');
timelineTask.finish();

1.3.3 dart事件流

Timeline.timeSync('flow_test', () {
  doSomething();
}, flow: flow);

Timeline.timeSync('flow_test', () {
  doSomething();
}, flow: Flow.step(flow.id));

Timeline.timeSync('flow_test', () {
  doSomething();
}, flow: Flow.end(flow.id));

1.4 TimelineEventRecorder总览

TimelineEventRingRecorder默认大小为32KB,也就是说该Recorder共有512个TimelineEventBlock(事件块),每个TimelineEventBlock有64个TimelineEvent(事件)。

1)TimelineEventRecorder:主要还有以下四种:ring, endless, startup, systrace

  • TimelineEventRingRecorder:这是默认的记录器,其父类TimelineEventFixedBufferRecorder有固定缓存区,
    • capacity_: 记录当前缓存区可记录的事件总数,默认值为32KB个;
    • num_blocks_: 记录事件块的数量,默认值为512个,其中每个事件块能记录64个事件;
  • TimelineEventEndlessRecorder:用于记录无上限的trace
  • TimelineEventStartupRecorder:用于记录有限缓存,且记录满则不再记录trace
  • TimelineEventSystraceRecorder:systrace_fd_记录的是/sys/kernel/debug/tracing/trace_marker的文件描述符;

2)TimelineEventBlock

  • length_:记录该事件块已记录的事件个数,每一次StartEvent()则执行加1操作,上限为64;
  • block_index_:记录当前事件块的索引ID,用于无限缓存大小的记录器;

另外关于时间计数,以Android为例,此处调用的是clock_gettime()系统方法,精确到纳秒,这里有CLOCK_MONOTONIC和CLOCK_THREAD_CPUTIME_ID两种不同的参数,Timeline最终记录的事件事件信息单位是微秒。

  • _start:采用参数CLOCK_MONOTONIC,记录从系统启动开始的计时时间点;
  • _startCpu: 采用参数CLOCK_THREAD_CPUTIME_ID,记录当前线程执行代码所花费的CPU时间;

二、c++层Timeline

所有的这些最终都调用Dart_TimelineEvent()方法。

2.1 事件类型

2.1.1 同步事件

[-> flutter/fml/trace_event.cc]

#define TRACE_EVENT0(category_group, name)           \
  ::fml::tracing::TraceEvent0(category_group, name); \  //事件开始
  __FML__AUTO_TRACE_END(name)

#define __FML__AUTO_TRACE_END(name)                                  \
  ::fml::tracing::ScopedInstantEnd __FML__TOKEN_CAT__2(__trace_end_, __LINE__)(name);

class ScopedInstantEnd {
 public:
  ScopedInstantEnd(const char* str) : label_(str) {}
  ~ScopedInstantEnd() { TraceEventEnd(label_); }  //事件结束

 private:
  const char* label_;
};
void TraceEvent0(TraceArg category_group, TraceArg name) {
  Dart_TimelineEvent(DCHECK_LITERAL(name),
                    Dart_TimelineGetMicros(), 0,   
                    Dart_Timeline_Event_Begin, 0,
                    nullptr,  nullptr);
}

void TraceEventEnd(TraceArg name) {
  Dart_TimelineEvent(DCHECK_LITERAL(name),
                    Dart_TimelineGetMicros(), 0,   
                    Dart_Timeline_Event_End, 0,
                    nullptr,  nullptr);
}

2.1.2 异步事件

[-> flutter/fml/trace_event.cc]

void TraceEventAsyncBegin0(TraceArg category_group, TraceArg name, TraceIDArg id) {
  Dart_TimelineEvent(DCHECK_LITERAL(name),
                    Dart_TimelineGetMicros(), id,   
                    Dart_Timeline_Event_Async_Begin, 0,
                    nullptr,  nullptr);
}

void TRACE_EVENT_ASYNC_END0(TraceArg category_group, TraceArg name, TraceIDArg id) {
  Dart_TimelineEvent(DCHECK_LITERAL(name),
                    Dart_TimelineGetMicros(), id,   
                    Dart_Timeline_Event_Async_End, 0,
                    nullptr,  nullptr);
}
void TraceEventAsyncComplete(TraceArg category_group, TraceArg name,
                             TimePoint begin, TimePoint end) {
  auto identifier = TraceNonce();
  if (begin > end) {
    std::swap(begin, end);
  }

  Dart_TimelineEvent(DCHECK_LITERAL(name),                  
                     begin.ToEpochDelta().ToMicroseconds(), identifier,                    
                     Dart_Timeline_Event_Async_Begin, 0,                               
                     nullptr, nullptr);

  Dart_TimelineEvent(DCHECK_LITERAL(name),                  
                     begin.ToEpochDelta().ToMicroseconds(), identifier,                    
                     Dart_Timeline_Event_Async_End, 0,                               
                     nullptr, nullptr);
}

2.1.3 流事件

[-> flutter/fml/trace_event.cc]

void TraceEventFlowBegin0(TraceArg category_group, TraceArg name, TraceIDArg id) {
  Dart_TimelineEvent(DCHECK_LITERAL(name),           
                     Dart_TimelineGetMicros(), id,                             
                     Dart_Timeline_Event_Flow_Begin, 0,                             
                     nullptr, nullptr);
}

void TraceEventFlowStep0(TraceArg category_group, TraceArg name, TraceIDArg id) {
  Dart_TimelineEvent(DCHECK_LITERAL(name),           
                     Dart_TimelineGetMicros(), id,                             
                     Dart_Timeline_Event_Flow_Step, 0,                             
                     nullptr, nullptr);
}

void TraceEventFlowEnd0(TraceArg category_group, TraceArg name, TraceIDArg id) {
  Dart_TimelineEvent(DCHECK_LITERAL(name),           
                     Dart_TimelineGetMicros(), id,                             
                     Dart_Timeline_Event_Flow_End, 0,                             
                     nullptr, nullptr);
}

可见,所有这些事件最终都是调用Dart_TimelineEvent,不同的主要在事件类型:

  • Dart_Timeline_Event_Begin:同步事件开始
  • Dart_Timeline_Event_End:同步事件结束
  • Dart_Timeline_Event_Async_Begin:同步事件开始
  • Dart_Timeline_Event_Async_End:同步事件结束
  • Dart_Timeline_Event_Flow_Begin:流事件开始

时间戳的获取除了TraceEventAsyncComplete()是直接传递的时间参数,其他都是通过Dart_TimelineGetMicros()命令获取执行该命令当下的时间戳,单位是微秒。

Dart_TimelineGetMicros

DART_EXPORT int64_t Dart_TimelineGetMicros() {
  return OS::GetCurrentMonotonicMicros();
}

//以Android为例
int64_t OS::GetCurrentMonotonicMicros() {
  int64_t ticks = GetCurrentMonotonicTicks();
  return ticks / kNanosecondsPerMicrosecond;
}

int64_t OS::GetCurrentMonotonicTicks() {
  struct timespec ts;
  if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
    return 0;
  }
  int64_t result = ts.tv_sec;
  result *= kNanosecondsPerSecond;
  result += ts.tv_nsec;
  return result;
}

通过clock_gettime系统调用获取CLOCK_MONOTONIC类型的时间,也就是系统从启动到现在的时间戳;

2.2 Dart_TimelineEvent

[-> third_party/dart/runtime/vm/dart_api_impl.cc]

DART_EXPORT void Dart_TimelineEvent(const char* label,
                                    int64_t timestamp0,
                                    int64_t timestamp1_or_async_id,
                                    Dart_Timeline_Event_Type type,
                                    intptr_t argument_count,
                                    const char** argument_names,
                                    const char** argument_values) {
#if defined(SUPPORT_TIMELINE)
  //采用的是Embedder Stream
  TimelineStream* stream = Timeline::GetEmbedderStream();
  //[见小节2.3.1]
  TimelineEvent* event = stream->StartEvent();

  switch (type) {
    case Dart_Timeline_Event_Begin:
      event->Begin(label, timestamp0); //[见小节2.3.3]
      break;
    case Dart_Timeline_Event_End:
      event->End(label, timestamp0);
      break;
    case Dart_Timeline_Event_Instant:
      event->Instant(label, timestamp0);
      break;
    case Dart_Timeline_Event_Duration:
      event->Duration(label, timestamp0, timestamp1_or_async_id);
      break;
    case Dart_Timeline_Event_Async_Begin:
      event->AsyncBegin(label, timestamp1_or_async_id, timestamp0);
      break;
    case Dart_Timeline_Event_Async_End:
      event->AsyncEnd(label, timestamp1_or_async_id, timestamp0);
      break;
    case Dart_Timeline_Event_Async_Instant:
      event->AsyncInstant(label, timestamp1_or_async_id, timestamp0);
      break;
    case Dart_Timeline_Event_Counter:
      event->Counter(label, timestamp0);
      break;
    case Dart_Timeline_Event_Flow_Begin:
      event->FlowBegin(label, timestamp1_or_async_id, timestamp0);
      break;
    case Dart_Timeline_Event_Flow_Step:
      event->FlowStep(label, timestamp1_or_async_id, timestamp0);
      break;
    case Dart_Timeline_Event_Flow_End:
      event->FlowEnd(label, timestamp1_or_async_id, timestamp0);
      break;
    default:
      FATAL("Unknown Dart_Timeline_Event_Type");
  }
  event->SetNumArguments(argument_count);
  for (intptr_t i = 0; i < argument_count; i++) {
    event->CopyArgument(i, argument_names[i], argument_values[i]);
  }
  // [见小节2.3.4]
  event->Complete();  
#endif
}

先以目前timeline的默认记录器TimelineEventRingRecorder来展开说明。

2.3 ring类型记录器

2.3.1 StartEvent

[-> third_party/dart/runtime/vm/timeline.cc]

TimelineEvent* TimelineEventFixedBufferRecorder::StartEvent() {
  return ThreadBlockStartEvent();  //【见下方】
}
TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() {
  OSThread* thread = OSThread::Current();
  Mutex* thread_block_lock = thread->timeline_block_lock();
  thread_block_lock->Lock();   //这个锁会一直持有,直到调用CompleteEvent()

  TimelineEventBlock* thread_block = thread->timeline_block();
  if ((thread_block != NULL) && thread_block->IsFull()) {
    MutexLocker ml(&lock_); //加锁,保证每次只有一个线程申请新的事件块
    thread_block->Finish();  //该事件块已满【见小节2.3.5】
    thread_block = GetNewBlockLocked(); //分配新的事件块
    thread->set_timeline_block(thread_block);
  } else if (thread_block == NULL) {
    MutexLocker ml(&lock_);
    thread_block = GetNewBlockLocked(); //没有事件块,则创建事件块
    thread->set_timeline_block(thread_block);
  }
  if (thread_block != NULL) {
    //持锁状态退出该函数  见下方】
    TimelineEvent* event = thread_block->StartEvent();
    return event;
  }
  thread_block_lock->Unlock(); //没有任何事件,则释放锁
  return NULL;
}
TimelineEvent* TimelineEventBlock::StartEvent() {
  return &events_[length_++];
}

说明:

  1. 如果当前线程中的thread_block为空,则创建新的事件块,并将当前线程id记录到该事件块,同时将该事件块再记录其成员变量timeline_block_事件块;
  2. 如果当前线程中的thread_block已满,则先将该事件块finish,再创建新的事件块;
  3. 从当前线程的事件块中分配一个可用的TimelineEvent事件;

2.3.2 GetNewBlockLocked

TimelineEventBlock* TimelineEventRingRecorder::GetNewBlockLocked() {
  if (block_cursor_ == num_blocks_) {
    block_cursor_ = 0;
  }
  TimelineEventBlock* block = &blocks_[block_cursor_++];
  block->Reset(); //事件重置
  block->Open(); // [见下文]
  return block;
}

void TimelineEventBlock::Open() {
  OSThread* os_thread = OSThread::Current();
  //将当前线程id记录到该事件块中的thread_id_
  thread_id_ = os_thread->trace_id();
  in_use_ = true;
}

OSThread对象里面有一个TimelineEventBlock指针,记录着当前正在操作的事件块。事件块里面有一个events_,记录着TimelineEvent数组,大小为kBlockSize=64。

2.3.3 TimelineEvent::Begin

void Begin(const char* label,
           int64_t micros = OS::GetCurrentMonotonicMicros(),
           int64_t thread_micros = OS::GetCurrentThreadCPUMicros());
void AsyncBegin(const char* label,
    int64_t async_id,
    int64_t micros = OS::GetCurrentMonotonicMicros());

事件定义过程会设置默认值,同步与异步的区别是异步事件会记录async_id,同步事件会记录当前线程的cpu运行时间戳。

void TimelineEvent::Begin(const char* label,
                          int64_t micros,
                          int64_t thread_micros) {
  Init(kBegin, label);
  set_timestamp0(micros);  //系统启动后运行的时间戳
  set_thread_timestamp0(thread_micros); //该线程CPU运行的时间戳
}

void TimelineEvent::End(const char* label,
                        int64_t micros,
                        int64_t thread_micros) {
  Init(kEnd, label);
  set_timestamp0(micros); //系统启动后运行的时间戳
  set_thread_timestamp0(thread_micros);//该线程CPU运行的时间戳
}

void TimelineEvent::AsyncBegin(const char* label,
                               int64_t async_id,
                               int64_t micros) {
  Init(kAsyncBegin, label);
  set_timestamp0(micros);
  set_timestamp1(async_id); //async_id记录到timestamp1_
}

void TimelineEvent::AsyncEnd(const char* label,
                             int64_t async_id,
                             int64_t micros) {
  Init(kAsyncEnd, label);
  set_timestamp0(micros);
  set_timestamp1(async_id); //async_id记录到timestamp1_
}
void TimelineEvent::Init(EventType event_type, const char* label) {
  state_ = 0;
  timestamp0_ = 0;
  timestamp1_ = 0;
  thread_timestamp0_ = -1;
  thread_timestamp1_ = -1;
  OSThread* os_thread = OSThread::Current();
  thread_ = os_thread->trace_id();  //记录线程id
  Isolate* isolate = Isolate::Current();
  if (isolate != NULL) {
    isolate_id_ = isolate->main_port(); //记录isolate端口号
  } else {
    isolate_id_ = ILLEGAL_PORT;
  }
  label_ = label; //事件标签名
  arguments_.Free();
  set_event_type(event_type); //事件类型,比如kBegin,kEnd
  set_pre_serialized_args(false);
  set_owns_label(false);
}

2.3.4 Complete

void TimelineEvent::Complete() {
  TimelineEventRecorder* recorder = Timeline::recorder();
  if (recorder != NULL) {
    recorder->CompleteEvent(this); //[见下文]
  }
}
void TimelineEventFixedBufferRecorder::CompleteEvent(TimelineEvent* event) {
  ThreadBlockCompleteEvent(event);  //[见下文]
}
void TimelineEventRecorder::ThreadBlockCompleteEvent(TimelineEvent* event) {
  OSThread* thread = OSThread::Current();
  Mutex* thread_block_lock = thread->timeline_block_lock();
  thread_block_lock->Unlock(); //释放同步锁
}

当OSThread的thread_block写满,则需要执行Finish(),将该事件块的数据发送给ServiceIsolate来处理,如下所示。

2.3.5 TimelineEventBlock::Finish

void TimelineEventBlock::Finish() {
  in_use_ = false;
#ifndef PRODUCT
  if (Service::timeline_stream.enabled()) {
    ServiceEvent service_event(NULL, ServiceEvent::kTimelineEvents);
    service_event.set_timeline_event_block(this);
    Service::HandleEvent(&service_event); //[见下文]
  }
#endif
}

2.3.6 Service::HandleEvent

[-> third_party/dart/runtime/vm/service.cc]

void Service::HandleEvent(ServiceEvent* event) {
  if (event->stream_info() != NULL && !event->stream_info()->enabled()) {
    return; //当没有地方监听事件流,则忽略
  }
  if (!ServiceIsolate::IsRunning()) {
    return; //当ServiceIsolate没有运行,则停止
  }
  JSONStream js;
  const char* stream_id = event->stream_id();
  {
    JSONObject jsobj(&js);
    jsobj.AddProperty("jsonrpc", "2.0");
    jsobj.AddProperty("method", "streamNotify");
    JSONObject params(&jsobj, "params");
    params.AddProperty("streamId", stream_id);
    params.AddProperty("event", event);
  }
  //此处isoalte为空,[见小节]
  PostEvent(event->isolate(), stream_id, event->KindAsCString(), &js);

  if (event->stream_info() != nullptr &&
      event->stream_info()->consumer() != nullptr) {
    auto length = js.buffer()->length();
    event->stream_info()->consumer()(
        reinterpret_cast<uint8_t*>(js.buffer()->buf()), length);
  }
}

可通过Dart_SetNativeServiceStreamCallback()来设置Dart_NativeStreamConsumer回调方法。

2.3.7 Service::PostEvent

[-> third_party/dart/runtime/vm/service.cc]

void Service::PostEvent(Isolate* isolate,
                        const char* stream_id,
                        const char* kind,
                        JSONStream* event) {
  //消息格式[<stream id>, <json string>]
  Dart_CObject list_cobj;
  Dart_CObject* list_values[2];
  list_cobj.type = Dart_CObject_kArray;
  list_cobj.value.as_array.length = 2;
  list_cobj.value.as_array.values = list_values;

  Dart_CObject stream_id_cobj;
  stream_id_cobj.type = Dart_CObject_kString;
  stream_id_cobj.value.as_string = const_cast<char*>(stream_id); //stream_id
  list_values[0] = &stream_id_cobj;

  Dart_CObject json_cobj;
  json_cobj.type = Dart_CObject_kString;
  json_cobj.value.as_string = const_cast<char*>(event->ToCString()); //event
  list_values[1] = &json_cobj;

  auto thread = Thread::Current();
  if (thread != nullptr) {
    TransitionVMToNative transition(thread);
    //向service isolate发送事件
    Dart_PostCObject(ServiceIsolate::Port(), &list_cobj);
  } else {
    Dart_PostCObject(ServiceIsolate::Port(), &list_cobj);
  }
}

2.3.8 Dart_PostCObject

DART_EXPORT bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message) {
  return PostCObjectHelper(port_id, message);
}

static bool PostCObjectHelper(Dart_Port port_id, Dart_CObject* message) {
  ApiMessageWriter writer;
  Message* msg = writer.WriteCMessage(message, port_id, Message::kNormalPriority);
  if (msg == NULL) {  
    return false;
  }
  return PortMap::PostMessage(msg);
}

最终会把数据传递给ServiceIsolate来进行处理。

2.4 systrace类型记录器

2.4.1 StartEvent

[-> third_party/dart/runtime/vm/timeline.cc]

TimelineEvent* TimelineEventPlatformRecorder::StartEvent() {
  TimelineEvent* event = new TimelineEvent();
  return event;
}

2.4.2 TimelineEvent初始化

[-> third_party/dart/runtime/vm/timeline.cc]

TimelineEvent::TimelineEvent()
    : timestamp0_(0),
      timestamp1_(0),
      thread_timestamp0_(-1),
      thread_timestamp1_(-1),
      state_(0),
      label_(NULL),
      stream_(NULL),
      thread_(OSThread::kInvalidThreadId),
      isolate_id_(ILLEGAL_PORT) {}

2.4.3 TimelineEvent::Begin

[-> third_party/dart/runtime/vm/timeline.cc]

void TimelineEvent::Begin(const char* label,
                          int64_t micros,
                          int64_t thread_micros) {
  Init(kBegin, label);
  set_timestamp0(micros);  //系统启动后运行的时间戳
  set_thread_timestamp0(thread_micros); //该线程CPU运行的时间戳
}

2.4.4 Complete

[-> third_party/dart/runtime/vm/timeline.cc]

void TimelineEvent::Complete() {
  TimelineEventRecorder* recorder = Timeline::recorder();
  if (recorder != NULL) {
    recorder->CompleteEvent(this); //[见下文]
  }
}
void TimelineEventPlatformRecorder::CompleteEvent(TimelineEvent* event) {
  OnEvent(event);
  delete event;
}

2.4.5 OnEvent

[-> third_party/dart/runtime/vm/timeline_android.cc]

void TimelineEventSystraceRecorder::OnEvent(TimelineEvent* event) {
  const intptr_t kBufferLength = 1024;
  char buffer[kBufferLength];
  //[小节2.4.6]
  const intptr_t event_length = PrintSystrace(event, &buffer[0], kBufferLength);
  if (event_length > 0) {
    ssize_t result;
    do {
      result = write(systrace_fd_, buffer, event_length);
    } while ((result == -1L) && (errno == EINTR));
  }
}

此处的systrace_fd_是指/sys/kernel/debug/tracing/trace_marker,也就是ftrace的buffer。

其中PrintSystrace过程如下所示:

2.4.6 PrintSystrace

intptr_t TimelineEventSystraceRecorder::PrintSystrace(TimelineEvent* event,
                                                      char* buffer,
                                                      intptr_t buffer_size) {
  buffer[0] = '\0';
  intptr_t length = 0;
  int64_t pid = OS::ProcessId();
  switch (event->event_type()) {
    case TimelineEvent::kBegin: {
      length = Utils::SNPrint(buffer, buffer_size, "B|%" Pd64 "|%s", pid,
                              event->label());
      break;
    }
    case TimelineEvent::kEnd: {
      length = Utils::SNPrint(buffer, buffer_size, "E");
      break;
    }
    case TimelineEvent::kCounter: {
      if (event->arguments_length() > 0) {
        length = Utils::SNPrint(buffer, buffer_size, "C|%" Pd64 "|%s|%s", pid,
                                event->label(), event->arguments()[0].value);
      }
      break;
    }
    default:
      break;
  }
  return length;
}

可见kBegin、kEnd、kCounter记录的信息如下所示:

B|pid|name
E
C|pid|name|count

2.5 小结

每一个Timeline时间的执行流程:

  • 通过StartEvent来获取TimelineEvent;
  • 根据不同事件类型来填充相应event;
  • 最后执行Complete;

ring和endless、startup记录器实现方式非常相近,都需要ServiceIsolate才能工作,其核心区别是GetNewBlockLocked()中buffer分配的方式。ring和startup都是采用的是固定大小,ring是在buffer已满的情况下循环写;startup则是写满buffer则不再写入;endless采用的是无上限的buffer空间。 systrace实现方式则跟前三者完全不同,systrace依赖的是linux底层的ftrace,无需额外开辟buffer。

三、dart层Timeline

3.1 同步事件类型

3.1.1 Timeline.startSync

[-> third_party/dart/sdk/lib/developer/timeline.dart]

class Timeline {
  static final List<_SyncBlock> _stack = new List<_SyncBlock>();

  static void startSync(String name, {Map arguments, Flow flow}) {
    if (!_hasTimeline) return;
    if (!_isDartStreamEnabled()) {
      _stack.add(null);
      return;
    }
    //创建同步块
    var block = new _SyncBlock._(name, _getTraceClock(), _getThreadCpuClock());
    if (arguments != null) {
      block._arguments = arguments;
    }
    if (flow != null) {
      block.flow = flow;
    }
    _stack.add(block);
  }
}
  • 该过程如果出现_stack列表为空,则会抛出异常,说明调用startSync/finishSync不是成对出现;
  • 调用finishSync()过程,从列表弹出最近加入的一个数据然后调用其finish()方法;
  • 每一条timeline都是通过创建_SyncBlock对象来记录,并保存到_stack列表。其中_hasTimeline是通过判断dart.developer.timeline值来决定,默认为true。

关于时间的两个方法如下所示:

DEFINE_NATIVE_ENTRY(Timeline_getTraceClock, 0, 0) {
  return Integer::New(OS::GetCurrentMonotonicMicros(), Heap::kNew);
}

DEFINE_NATIVE_ENTRY(Timeline_getThreadCpuClock, 0, 0) {
  return Integer::New(OS::GetCurrentThreadCPUMicros(), Heap::kNew);
}

3.1.2 _SyncBlock初始化

class _SyncBlock {
  final String category = 'Dart';
  final String name;
  Map _arguments;
  final int _start;  //启动运行时间戳
  final int _startCpu; //该线程CPU运行时间戳

  Flow _flow;

  _SyncBlock._(this.name, this._start, this._startCpu);
}

3.1.3 Timeline.finishSync

[-> third_party/dart/sdk/lib/developer/timeline.dart]

class Timeline {

  static void finishSync() {
    if (!_hasTimeline) {
      return;
    }
    if (_stack.length == 0) {
      throw new StateError('Uneven calls to startSync and finishSync');
    }
    var block = _stack.removeLast();
    block.finish();  //[见小节]
  }
}

3.1.4 _SyncBlock.finish

[-> third_party/dart/sdk/lib/developer/timeline.dart]

class _SyncBlock {

  void finish() {
    // [见小节]
    _reportCompleteEvent(
        _start, _startCpu, category, name, _argumentsAsJson(_arguments));
    if (_flow != null) {
      _reportFlowEvent(_start, _startCpu, category, name, _flow._type, _flow.id,
          _argumentsAsJson(null));
    }
  }
}

3.1.5 _reportCompleteEvent

[-> third_party/dart/runtime/lib/timeline.cc]

DEFINE_NATIVE_ENTRY(Timeline_reportCompleteEvent, 0, 5) {
#if defined(SUPPORT_TIMELINE)
  GET_NON_NULL_NATIVE_ARGUMENT(Integer, start, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(Integer, start_cpu, arguments->NativeArgAt(1));
  GET_NON_NULL_NATIVE_ARGUMENT(String, category, arguments->NativeArgAt(2));
  GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(3));
  GET_NON_NULL_NATIVE_ARGUMENT(String, args, arguments->NativeArgAt(4));

  TimelineEventRecorder* recorder = Timeline::recorder();
  //获取TimelineEvent对象
  TimelineEvent* event = Timeline::GetDartStream()->StartEvent();
  //见小节
  DartTimelineEventHelpers::ReportCompleteEvent(
      thread, event, start.AsInt64Value(), start_cpu.AsInt64Value(),
      category.ToCString(), name.ToMallocCString(), args.ToMallocCString());
#endif
  return Object::null();
}

3.1.6 ReportCompleteEvent

[-> third_party/dart/runtime/vm/timeline.cc]

void DartTimelineEventHelpers::ReportCompleteEvent(Thread* thread,
                                                   TimelineEvent* event,
                                                   int64_t start,
                                                   int64_t start_cpu,
                                                   const char* category,
                                                   char* name,
                                                   char* args) {
  const int64_t end = OS::GetCurrentMonotonicMicros();
  const int64_t end_cpu = OS::GetCurrentThreadCPUMicros();
  //将四个时间戳记录到TimelineEvent
  event->Duration(name, start, end, start_cpu, end_cpu);
  event->set_owns_label(true);
  // 见小节
  event->CompleteWithPreSerializedArgs(args);
}
void TimelineEvent::Duration(const char* label,
                             int64_t start_micros,
                             int64_t end_micros,
                             int64_t thread_start_micros,
                             int64_t thread_end_micros) {
  Init(kDuration, label);
  set_timestamp0(start_micros);
  set_timestamp1(end_micros);
  set_thread_timestamp0(thread_start_micros);
  set_thread_timestamp1(thread_end_micros);
}

将名字记录到TimelineEvent的成员变量label_,将四个时间戳记录到TimelineEvent的相应成员变量。

3.1.7 CompleteWithPreSerializedArgs

[-> third_party/dart/runtime/vm/timeline.cc]

void TimelineEvent::CompleteWithPreSerializedArgs(char* args_json) {
  set_pre_serialized_args(true);
  SetNumArguments(1);
  SetArgument(0, "Dart Arguments", args_json);
  Complete();
}

接着执行Complete(),根据不同的recorder回到了前面已介绍[小节2.3.4]/[小节2.4.4]过程。

3.2 异步事件类型

3.2.1 TimelineTask.start

class TimelineTask {
  final int _taskId;
  final List<_AsyncBlock> _stack = [];

  //需要先初始化
  TimelineTask() : _taskId = _getNextAsyncId() {}

  void start(String name, {Map arguments}) {
    if (!_hasTimeline) return;
    var block = new _AsyncBlock._(name, _taskId);
    if (arguments != null) {
      block._arguments = arguments;
    }
    _stack.add(block);
    block._start();  //[见小节]
  }
}

3.2.2 _AsyncBlock._start

class _AsyncBlock {
  final String category = 'Dart';
  final String name;
  final int _taskId;
  Map _arguments;

  void _start() {
    _reportTaskEvent(
        _getTraceClock(), _taskId, 'b', category, name, _argumentsAsJson(null));
  }
}

_AsyncBlock相比_SyncBlock少了,少了两个时间相关的成员变量,多了一个记录taskid的成员变量

3.2.3 TimelineTask.finish

class TimelineTask {

  void finish() {
    if (!_hasTimeline) {
      return;
    }
    var block = _stack.removeLast();
    block._finish(); //[见小节]
  }
}

3.2.4 _AsyncBlock._finish

class TimelineTask {

  void _finish() {
    _reportTaskEvent(_getTraceClock(), _taskId, 'e', category, name,
        _argumentsAsJson(_arguments));
  }
}

3.2.5 _reportTaskEvent

DEFINE_NATIVE_ENTRY(Timeline_reportTaskEvent, 0, 6) {
#if defined(SUPPORT_TIMELINE)
  GET_NON_NULL_NATIVE_ARGUMENT(Integer, start, arguments->NativeArgAt(0));
  GET_NON_NULL_NATIVE_ARGUMENT(Integer, id, arguments->NativeArgAt(1));
  GET_NON_NULL_NATIVE_ARGUMENT(String, phase, arguments->NativeArgAt(2));
  GET_NON_NULL_NATIVE_ARGUMENT(String, category, arguments->NativeArgAt(3));
  GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(4));
  GET_NON_NULL_NATIVE_ARGUMENT(String, args, arguments->NativeArgAt(5));

  TimelineEventRecorder* recorder = Timeline::recorder();
  //获取TimelineEvent对象
  TimelineEvent* event = Timeline::GetDartStream()->StartEvent();
  //见小节
  DartTimelineEventHelpers::ReportTaskEvent(
      thread, event, start.AsInt64Value(), id.AsInt64Value(), phase.ToCString(),
      category.ToCString(), name.ToMallocCString(), args.ToMallocCString());
#endif  // SUPPORT_TIMELINE
  return Object::null();
}

3.2.6 ReportTaskEvent

void DartTimelineEventHelpers::ReportTaskEvent(Thread* thread,
                                               TimelineEvent* event,
                                               int64_t start,
                                               int64_t id,
                                               const char* phase,
                                               const char* category,
                                               char* name,
                                               char* args) {
  switch (phase[0]) {
    case 'n':
      event->AsyncInstant(name, id, start);
      break;
    case 'b':
      event->AsyncBegin(name, id, start);
      break;
    case 'e':
      event->AsyncEnd(name, id, start);
      break;
    default:
      UNREACHABLE();
  }
  event->set_owns_label(true);
  event->CompleteWithPreSerializedArgs(args);
}

3.2.7 CompleteWithPreSerializedArgs

void TimelineEvent::CompleteWithPreSerializedArgs(char* args_json) {
  set_pre_serialized_args(true);
  SetNumArguments(1);
  SetArgument(0, "Dart Arguments", args_json);
  Complete();
}

接着执行Complete(),根据不同的recorder回到了前面已介绍[小节2.3.4]/[小节2.4.4]过程。

四、dart虚拟机Timeline

这两个宏目前主要用于dart_api_impl.cc文件

#define API_TIMELINE_DURATION(thread)                                          \
  TimelineDurationScope api_tds(thread, Timeline::GetAPIStream(), CURRENT_FUNC)
#define API_TIMELINE_BEGIN_END(thread)                                         \
  TimelineBeginEndScope api_tbes(thread, Timeline::GetAPIStream(), CURRENT_FUNC)

4.1 API_TIMELINE_DURATION

4.1.1 TimelineDurationScope初始化

TimelineDurationScope::TimelineDurationScope(Thread* thread,
                                             TimelineStream* stream,
                                             const char* label)
    : TimelineEventScope(thread, stream, label) {
  if (!enabled()) {
    return;
  }
  timestamp_ = OS::GetCurrentMonotonicMicros();
  thread_timestamp_ = OS::GetCurrentThreadCPUMicros();
}

该timeline事件的标签名为CURRENT_FUNC,也就是函数名;

4.1.2 TimelineDurationScope析构

TimelineDurationScope::~TimelineDurationScope() {
  if (!ShouldEmitEvent()) {
    return;
  }
  TimelineEvent* event = stream()->StartEvent();
  if (event == NULL) {
    return;
  }
  //创建事件
  event->Duration(label(), timestamp_, OS::GetCurrentMonotonicMicros(),
                  thread_timestamp_, OS::GetCurrentThreadCPUMicros());
  StealArguments(event);
  //事件完成
  event->Complete();
}

4.2 API_TIMELINE_BEGIN_END

4.1.1 TimelineBeginEndScope

TimelineBeginEndScope::TimelineBeginEndScope(Thread* thread,
                                             TimelineStream* stream,
                                             const char* label)
    : TimelineEventScope(thread, stream, label) {
  EmitBegin();
}

TimelineBeginEndScope::~TimelineBeginEndScope() {
  EmitEnd();
}

该timeline事件的标签名为CURRENT_FUNC,也就是函数名

4.1.2 EmitBegin

void TimelineBeginEndScope::EmitBegin() {
  if (!ShouldEmitEvent()) {
    return;
  }
  TimelineEvent* event = stream()->StartEvent();
  if (event == NULL) {
    set_enabled(false);
    return;
  }
  event->Begin(label());  //事件开始
  event->Complete();
}

4.1.3 EmitEnd

void TimelineBeginEndScope::EmitEnd() {
  if (!ShouldEmitEvent()) {
    return;
  }
  TimelineEvent* event = stream()->StartEvent();
  if (event == NULL) {
    set_enabled(false);
    return;
  }
  event->End(label());  //事件结束
  StealArguments(event);
  event->Complete();
}

对于timeline来说功能是一致的,但对于systrace则不同,因为事件的方式不同,建议时候用API_TIMELINE_BEGIN_END,不推荐使用API_TIMELINE_DURATION。

五、总结

TimelineEventRecorder:主要还有以下四种:ring, endless, startup, systrace,本身timeline是能够转换为systrace的,但是在转换过程中有很多坑,需要去解决,下一篇文章再进一步讲解如何解决对systrace更友好的支持。

附录:

flutter/fml/trace_event.cc
third_party/dart/sdk/lib/developer/timeline.dart
third_party/dart/runtime/lib/timeline.cc

third_party/dart/runtime/vm/
  - dart_api_impl.cc
  - timeline.cc
  - timeline_android.cc
  - service.cc

ServiceIsolate工作原理

2019-10-13 06:15:40

一、概述

在前面文章深入理解Dart虚拟机启动中,有讲到Dart虚拟机中有一个比较重要的isolate,创建的isolate名为”vm-service”,运行在独立的线程,也就是ServiceIsolate,这是用于系统调试相关功能的一个isolate,提供虚拟机相关服务, 比如hot reload,timeline等。这里先来看看ServiceIsolate的启动过程以及监听处理流程。

二、启动ServiceIsolate

在dart虚拟机启动过程,会执行Dart::Init()操作,该过程中初始化timeline以及在线程池中启动ServiceIsolate。

2.1 Dart::Init

[-> third_party/dart/runtime/vm/dart.cc]

char* Dart::Init(Dart_IsolateCreateCallback create,
                 Dart_IsolateShutdownCallback shutdown,
                 Dart_IsolateCleanupCallback cleanup,
                 Dart_ThreadExitCallback thread_exit,
                 ...) {
  ...
#if defined(SUPPORT_TIMELINE)
  Timeline::Init();
#endif

  Isolate::SetCreateCallback(create);
  Isolate::SetShutdownCallback(shutdown);
  Isolate::SetCleanupCallback(cleanup);
  const bool is_dart2_aot_precompiler = FLAG_precompiled_mode && !kDartPrecompiledRuntime;
  if (!is_dart2_aot_precompiler &&
      (FLAG_support_service || !kDartPrecompiledRuntime)) {
    ServiceIsolate::Run();  //[见小节]
  }
  ...
}

启动ServiceIsolate的条件,需要满足以下两者之一:

  • 当kDartPrecompiledRuntime = true,且FLAG_support_service = true;
  • 当kDartPrecompiledRuntime = false,且FLAG_precompiled_mode = false;

再来看一看DartVM初始化过程

DartVM::DartVM(std::shared_ptr<const DartVMData> vm_data,
               std::shared_ptr<IsolateNameServer> isolate_name_server) {
    ...
    TRACE_EVENT0("flutter", "Dart_Initialize");
    Dart_InitializeParams params = {};
    params.create = reinterpret_cast<decltype(params.create)>(
    DartIsolate::DartIsolateCreateCallback);
    params.shutdown = reinterpret_cast<decltype(params.shutdown)>(
    DartIsolate::DartIsolateShutdownCallback);
    params.cleanup = reinterpret_cast<decltype(params.cleanup)>(
    DartIsolate::DartIsolateCleanupCallback);
    params.thread_exit = ThreadExitCallback;
    ...
}

可见,ServiceIsolate的创建callback方法是指DartIsolate::DartIsolateCreateCallback(),后面会用到这个信息。

2.2 ServiceIsolate::Run

[-> third_party/dart/runtime/vm/service_isolate.cc]

void ServiceIsolate::Run() {
  {
    MonitorLocker ml(monitor_);
    state_ = kStarting;
    ml.NotifyAll();
  }
  //将任务交给线程池中的worker线程来执行任务[见小节]
  bool task_started = Dart::thread_pool()->Run(new RunServiceTask());
}

将RunServiceTask任务交给线程池中的worker线程来执行任务,过程会创建创建名为“vm-service”的isolate。

2.3 RunServiceTask

[-> third_party/dart/runtime/vm/service_isolate.cc]

class RunServiceTask : public ThreadPool::Task {
 public:
  virtual void Run() {
    char* error = NULL;
    Isolate* isolate = NULL;

    Dart_IsolateCreateCallback create_callback =
        ServiceIsolate::create_callback();

    Dart_IsolateFlags api_flags;
    Isolate::FlagsInitialize(&api_flags);
    //创建ServiceIsolate [见小节2.3]
    isolate = reinterpret_cast<Isolate*>(
        create_callback(ServiceIsolate::kName, ServiceIsolate::kName, NULL,
                        NULL, &api_flags, NULL, &error));


    bool got_unwind;
    {
      StartIsolateScope start_scope(isolate);
      //[见小节2.4]
      got_unwind = RunMain(isolate);
    }
    ...
    isolate->message_handler()->Run(Dart::thread_pool(), NULL, ShutdownIsolate,
                                    reinterpret_cast<uword>(isolate));
  }

此处的create_callback便是DartIsolate::DartIsolateCreateCallback,如下所示。

2.3.1 DartIsolateCreateCallback

[-> flutter/runtime/dart_isolate.cc]

Dart_Isolate DartIsolate::DartIsolateCreateCallback(
    const char* advisory_script_uri,
    const char* advisory_script_entrypoint,
    const char* package_root,
    const char* package_config,
    Dart_IsolateFlags* flags,
    std::shared_ptr<DartIsolate>* parent_embedder_isolate,
    char** error) {
  if (parent_embedder_isolate == nullptr &&
      strcmp(advisory_script_uri, DART_VM_SERVICE_ISOLATE_NAME) == 0) {
    return DartCreateAndStartServiceIsolate(package_root,package_config,  
                                            flags, error
    );
  }
  ...
}

此处DART_VM_SERVICE_ISOLATE_NAME就是”vm-service”。

Dart_Isolate DartIsolate::DartCreateAndStartServiceIsolate(
    const char* package_root,
    const char* package_config,
    Dart_IsolateFlags* flags,
    char** error) {
  auto vm_data = DartVMRef::GetVMData();

  const auto& settings = vm_data->GetSettings();

  if (!settings.enable_observatory) {
    return nullptr;
  }

  TaskRunners null_task_runners("io.flutter." DART_VM_SERVICE_ISOLATE_NAME,
                                nullptr, nullptr, nullptr, nullptr);

  flags->load_vmservice_library = true;

  std::weak_ptr<DartIsolate> weak_service_isolate =
      DartIsolate::CreateRootIsolate(
          vm_data->GetSettings(),         // settings
          vm_data->GetIsolateSnapshot(),  // isolate snapshot
          vm_data->GetSharedSnapshot(),   // shared snapshot
          null_task_runners,              // task runners
          nullptr,                        // window
          {},                             // snapshot delegate
          {},                             // IO Manager
          DART_VM_SERVICE_ISOLATE_NAME,   // script uri
          DART_VM_SERVICE_ISOLATE_NAME,   // script entrypoint
          flags                           // flags
      );


  tonic::DartState::Scope scope(service_isolate);
  //[见小节2.3.2]
  if (!DartServiceIsolate::Startup(
          settings.ipv6 ? "::1" : "127.0.0.1",  // server IP address
          settings.observatory_port,            // server observatory port
          tonic::DartState::HandleLibraryTag,   // embedder library tag handler
          false,  //  disable websocket origin check
          settings.disable_service_auth_codes,  // disable VM service auth codes
          error                                 // error (out)
          )) {
    return nullptr;
  }
  ...
  return service_isolate->isolate();
}
  • 可通过settings.enable_observatory来决定是否开启observatory的isolate;
  • 启动ip为127.0.0.1的服务;

2.3.2 DartServiceIsolate::Startup

[-> flutter/runtime/dart_service_isolate.cc]

bool DartServiceIsolate::Startup(std::string server_ip,
                                 intptr_t server_port,
                                 Dart_LibraryTagHandler embedder_tag_handler,
                                 bool disable_origin_check,
                                 bool disable_service_auth_codes,
                                 char** error) {
  Dart_Isolate isolate = Dart_CurrentIsolate();
  ...

  Dart_Handle uri = Dart_NewStringFromCString("dart:vmservice_io");
  Dart_Handle library = Dart_LookupLibrary(uri);
  Dart_Handle result = Dart_SetRootLibrary(library);
  result = Dart_SetNativeResolver(library, GetNativeFunction, GetSymbol);

  Dart_ExitScope();
  Dart_ExitIsolate();
  //该过程会执行Isolate::Run()方法
  Dart_IsolateMakeRunnable(isolate);

  Dart_EnterIsolate(isolate);
  Dart_EnterScope();
  library = Dart_RootLibrary();

  //设置HTTP server的ip地址,对于ipv4则为127.0.0.1
  Dart_SetField(library, Dart_NewStringFromCString("_ip"),
                         Dart_NewStringFromCString(server_ip.c_str()));
  //当指定端口号,则立即启动服务;如果没有指定,则找到第一个可用的端口号
  bool auto_start = server_port >= 0;
  if (server_port < 0) {
    server_port = 0;
  }
  //设置HTTP server的端口号,
  Dart_SetField(library, Dart_NewStringFromCString("_port"),
                         Dart_NewInteger(server_port));
  Dart_SetField(library, Dart_NewStringFromCString("_autoStart"),
                         Dart_NewBoolean(auto_start));
  Dart_SetField(library, Dart_NewStringFromCString("_originCheckDisabled"),
                    Dart_NewBoolean(disable_origin_check));
  Dart_SetField(library, Dart_NewStringFromCString("_authCodesDisabled"),
                    Dart_NewBoolean(disable_service_auth_codes));
  return true;
}

设置root library为dart:vmservice_io。

2.4 RunMain

[-> third_party/dart/runtime/vm/service_isolate.cc]

bool RunMain(Isolate* I) {
  Thread* T = Thread::Current();
  StackZone zone(T);
  HANDLESCOPE(T);

  const Library& root_library = Library::Handle(Z, I->object_store()->root_library());
  const String& entry_name = String::Handle(Z, String::New("main"));
  const Function& entry = Function::Handle(
      Z, root_library.LookupFunctionAllowPrivate(entry_name));
  //找到并执行dart:vmservice_io库的main()方法 [见小节2.5]
  const Object& result = Object::Handle(
      Z, DartEntry::InvokeFunction(entry, Object::empty_array()));
  const ReceivePort& rp = ReceivePort::Cast(result);
  ServiceIsolate::SetLoadPort(rp.Id());
  return false;
}

2.5 vmservice_io.main

[-> third_party/dart/runtime/bin/vmservice/vmservice_io.dart]

main() {
  new VMService();
  if (_autoStart) {
    _lazyServerBoot();  //[见小节2.5.1]
    server.startup();   //[见小节2.6]
    Timer.run(() {});   //用于执行所有的microtasks.
  }
  scriptLoadPort.handler = _processLoadRequest;
  _registerSignalHandlerTimer = new Timer(shortDelay, _registerSignalHandler);
  return scriptLoadPort;
}

2.5.1 _lazyServerBoot

[-> third_party/dart/runtime/bin/vmservice/vmservice_io.dart]

_lazyServerBoot() {
  if (server != null) {
    return;
  }
   //[见小节2.5.2]
  var service = new VMService();
   //[见小节2.5.4]
  server = new Server(service, _ip, _port, _originCheckDisabled, _authCodesDisabled);
}

该过程创建VMService和Server对象

2.5.2 VMService初始化

[-> third_party/dart/sdk/lib/vmservice/vmservice.dart]

final RawReceivePort isolateControlPort = new RawReceivePort();

class VMService extends MessageRouter {

  factory VMService() {
    if (VMService._instance == null) {
      VMService._instance = new VMService._internal(); //如下文
      _onStart(); //通知虚拟机,服务正在运行
    }
    return _instance;
  }

  VMService._internal() : eventPort = isolateControlPort {
    eventPort.handler = messageHandler; //设置消息处理handler
  }
}

2.5.3 messageHandler

[-> third_party/dart/sdk/lib/vmservice/vmservice.dart]

  void messageHandler(message) {
    if (message is List) {
      if (message.length == 2) {
        // 事件处理
        _eventMessageHandler(message[0], new Response.from(message[1]));
        return;
      }
      if (message.length == 1) {
        _exit();  //vm service退出的消息
        return;
      }
      if (message.length == 3) {
        final opcode = message[0];
        if (opcode == Constants.METHOD_CALL_FROM_NATIVE) {
          _handleNativeRpcCall(message[1], message[2]);
          return;
        } else {
          assert((opcode == Constants.WEB_SERVER_CONTROL_MESSAGE_ID) ||
              (opcode == Constants.SERVER_INFO_MESSAGE_ID));
          _serverMessageHandler(message[0], message[1], message[2]);
          return;
        }
      }
      if (message.length == 4) {
        //关于isolate的创建和销毁的消息
        _controlMessageHandler(message[0], message[1], message[2], message[3]);
        return;
      }
    }
  }

根据消息长度调用相应的Handler:

  • 当长度等于1,则调用_exit来退出vm service;
  • 当长度等于2,则调用_eventMessageHandler来处理事件;
  • 当长度等于3,分为两种情况:
    • 当消息opcode等于METHOD_CALL_FROM_NATIVE,则调用_handleNativeRpcCall来处理Native的RPC调用;
    • 否则,则调用_serverMessageHandler来处理消息;
  • 当长度等于4,则调用_controlMessageHandler来通知创建或销毁一个isolate;

2.5.4 Server初始化

[-> third_party/dart/runtime/bin/vmservice/server.dart]

class Server {
  static const WEBSOCKET_PATH = '/ws';
  static const ROOT_REDIRECT_PATH = '/index.html';

  final VMService _service;
  final String _ip;
  final int _port;
  final bool _originCheckDisabled;
  final bool _authCodesDisabled;
  HttpServer _server;

  Server(this._service, this._ip, this._port, this._originCheckDisabled,
      bool authCodesDisabled)
      : _authCodesDisabled = (authCodesDisabled || Platform.isFuchsia);
}

2.6 server.startup

[-> third_party/dart/runtime/bin/vmservice/server.dart]

Future startup() async {
  ...
  Future<bool> poll() async {
    try {
      var address;
      var addresses = await InternetAddress.lookup(_ip);
      for (var i = 0; i < addresses.length; i++) {
        address = addresses[i];
        if (address.type == InternetAddressType.IP_V4) break;
      }
      //监听HTTP请求
      _server = await HttpServer.bind(address, _port);
      return true;
    } catch (e, st) {
      return false;
    }
  }

  //轮询尝试,最多10次没有连接成功,则退出
  int attempts = 0;
  final int maxAttempts = 10;
  //尝试跟指定地址和端口建立http请求进行绑定
  while (!await poll()) {
    attempts++;
    if (attempts > maxAttempts) {
      _notifyServerState("");
      onServerAddressChange(null);
      return this;
    }
    await new Future<Null>.delayed(const Duration(seconds: 1));
  }
  //进入监听状态
  _server.listen(_requestHandler, cancelOnError: true);
  //这便是熟悉的那行,标志着服务启动成功
  serverPrint('Observatory listening on $serverAddress');

  //将服务地址通知到VmService,写入其成员变量server_uri_
  _notifyServerState(serverAddress.toString());
  //将服务地址通知到ServiceIsolate,写入其成员变量server_address_
  onServerAddressChange('$serverAddress');
  return this;
}

服务进入监听状态,一旦收到请求,则会调用_requestHandler。

三、处理请求

Server._requestHandler
  client.onRequest
    vmservice.routeRequest
      vmservice._routeRequestImpl
        message.sendToVM
          message.sendRootServiceMessage  (接下来进入C++)
            Service::HandleRootMessage
              Service::InvokeMethod

3.1 Server._requestHandler

[-> third_party/dart/runtime/bin/vmservice/server.dart]

Future _requestHandler(HttpRequest request) async {
  ...
  final String path = _checkAuthTokenAndGetPath(request.uri);
  //创建一个client对象,内有vmservice [见小节3.1.1]
  final client = new HttpRequestClient(request, _service);
  //创建message对象,[见小节3.1.3]
  final message = new Message.fromUri(client, Uri.parse(path));
  // [见小节3.2]
  client.onRequest(message);
}

3.1.1 HttpRequestClient初始化

[-> third_party/dart/runtime/bin/vmservice/server.dart]

class HttpRequestClient extends Client {
  final HttpRequest request;

  HttpRequestClient(this.request, VMService service)
      : super(service, sendEvents: false); //见下文
}

[-> third_party/dart/sdk/lib/vmservice/client.dart]

abstract class Client {
  final VMService service;
  final bool sendEvents;

  Client(this.service, {bool sendEvents: true}) : this.sendEvents = sendEvents {
    service._addClient(this);  //将当前的client添加到VMService
  }
}

3.1.2 VMService._addClient

[-> third_party/dart/sdk/lib/vmservice/vmservice.dart]

class VMService extends MessageRouter {
  static const serviceNamespace = 's';

  final NamedLookup<Client> clients =
      new NamedLookup<Client>(prologue: serviceNamespace);

  void _addClient(Client client) {
    clients.add(client);
  }
}

3.1.3 Message.fromUri

[-> third_party/dart/sdk/lib/vmservice/message.dart]

Message.fromUri(this.client, Uri uri)
    : type = MessageType.Request,
      serial = '',
      method = _methodNameFromUri(uri) { //从uri中获取方法名
  params.addAll(uri.queryParameters);
}

Message对象都有一个成员变量method

  • 方法名method,对于fromUri是从分割后的第一部分
  • 消息类型type有有3大类:Request, Notification, Response

3.2 client.onRequest

[-> third_party/dart/sdk/lib/vmservice/client.dart]

abstract class Client {
  final VMService service;

  void onRequest(Message message) {
    // 【见小节3.3/38】
    service.routeRequest(service, message).then(post);
  }
}

这里有两个过程:

  • vmservice.routeRequest
  • post

3.3 vmservice.routeRequest

[-> third_party/dart/sdk/lib/vmservice/vmservice.dart]

Future<Response> routeRequest(VMService _, Message message) async {
  return new Response.from(await _routeRequestImpl(message));
}

Future _routeRequestImpl(Message message) async {
  try {
    if (message.completed) {
      return await message.response;
    }
    if (message.method == 'streamListen') {
      return await _streamListen(message);
    }
    if (message.method == 'streamCancel') {
      return await _streamCancel(message);
    }
    if (message.method == '_registerService') {
      return await _registerService(message);
    }
    if (message.method == '_spawnUri') {
      return await _spawnUri(message);
    }
    if (devfs.shouldHandleMessage(message)) {
      return await devfs.handleMessage(message);
    }
    if (_hasNamespace(message.method)) {
      return await _handleService(message);
    }
    if (message.params['isolateId'] != null) {
      return await runningIsolates.routeRequest(this, message);
    }
    return await message.sendToVM(); //[见小节3.4]
  } catch (e, st) {
    return message.response;
  }
}

3.4 message.sendToVM

[-> third_party/dart/sdk/lib/vmservice/message.dart]

Future<Response> sendToVM() {
  final receivePort = new RawReceivePort();
  receivePort.handler = (value) {
    receivePort.close();
    _setResponseFromPort(value);
  };
  var keys = params.keys.toList(growable: false);
  var values = params.values.toList(growable: false);
  if (!_methodNeedsObjectParameters(method)) {
    keys = _makeAllString(keys);
    values = _makeAllString(values);
  }

  final request = new List(6)
    ..[0] = 0
    ..[1] = receivePort.sendPort
    ..[2] = serial
    ..[3] = method
    ..[4] = keys
    ..[5] = values;

  if (_methodNeedsObjectParameters(method)) {
    sendObjectRootServiceMessage(request);  //[见小节3.5]
  } else {
    sendRootServiceMessage(request);  //[见小节3.5]
  }

  return _completer.future;
}

sendRootServiceMessage,这是一个native方法,调用到vmservice.cc中相应的方法

3.5 message.sendRootServiceMessage

[-> third_party/dart/runtime/lib/vmservice.cc]

DEFINE_NATIVE_ENTRY(VMService_SendRootServiceMessage, 0, 1) {
#ifndef PRODUCT
  GET_NON_NULL_NATIVE_ARGUMENT(Array, message, arguments->NativeArgAt(0));
  if (FLAG_support_service) {
    return Service::HandleRootMessage(message);  //[见小节3.6]
  }
#endif
  return Object::null();
}
DEFINE_NATIVE_ENTRY(VMService_SendObjectRootServiceMessage, 0, 1) {
#ifndef PRODUCT
  GET_NON_NULL_NATIVE_ARGUMENT(Array, message, arguments->NativeArgAt(0));
  if (FLAG_support_service) {
    return Service::HandleObjectRootMessage(message);
  }
#endif
  return Object::null();
}

3.6 Service::HandleRootMessage

[-> third_party/dart/runtime/vm/service.cc]

RawError* Service::HandleRootMessage(const Array& msg_instance) {
  Isolate* isolate = Isolate::Current();
  return InvokeMethod(isolate, msg_instance);  //[见小节3.7]
}

RawError* Service::HandleObjectRootMessage(const Array& msg_instance) {
  Isolate* isolate = Isolate::Current();
  return InvokeMethod(isolate, msg_instance, true);  //[见小节3.7]
}

3.7 Service::InvokeMethod

[-> third_party/dart/runtime/vm/service.cc]

RawError* Service::InvokeMethod(Isolate* I, const Array& msg,
                                bool parameters_are_dart_objects) {
  Thread* T = Thread::Current();
  {
    StackZone zone(T);
    HANDLESCOPE(T);
    Instance& reply_port = Instance::Handle(Z);
    Instance& seq = String::Handle(Z);
    String& method_name = String::Handle(Z);
    Array& param_keys = Array::Handle(Z);
    Array& param_values = Array::Handle(Z);
    reply_port ^= msg.At(1);
    seq ^= msg.At(2);
    method_name ^= msg.At(3);
    param_keys ^= msg.At(4);
    param_values ^= msg.At(5);

    JSONStream js;
    Dart_Port reply_port_id =
        (reply_port.IsNull() ? ILLEGAL_PORT : SendPort::Cast(reply_port).Id());
    js.Setup(zone.GetZone(), reply_port_id, seq, method_name, param_keys,
             param_values, parameters_are_dart_objects);
    ...
    const char* c_method_name = method_name.ToCString();
    //从service_methods_[]找到目标方法 [3.7.1]
    const ServiceMethodDescriptor* method = FindMethod(c_method_name);
    if (method != NULL) {
      if (method->entry(T, &js)) {  //执行相应方法
        js.PostReply();
      }
      return T->StealStickyError();
    }
    ...
  }
}

3.7.1 FindMethod

[-> third_party/dart/runtime/vm/service.cc]

const ServiceMethodDescriptor* FindMethod(const char* method_name) {
  intptr_t num_methods = sizeof(service_methods_) / sizeof(service_methods_[0]);
  for (intptr_t i = 0; i < num_methods; i++) {
    const ServiceMethodDescriptor& method = service_methods_[i];
    if (strcmp(method_name, method.name) == 0) {
      return &method;
    }
  }
  return NULL;
}

在service.cc中有一个成员变量service_methods_[]记录了所有的定义的方法。比如获取timeline的过程,如下所示。

3.7.2 service_methods_数组

struct ServiceMethodDescriptor {
  const char* name;
  const ServiceMethodEntry entry;
  const MethodParameter* const* parameters;
};

static const ServiceMethodDescriptor service_methods_[] = {
  { "_echo", Echo,
    NULL },
  { "_respondWithMalformedJson", RespondWithMalformedJson,
    NULL },
  { "_respondWithMalformedObject", RespondWithMalformedObject,
    NULL },
  { "_triggerEchoEvent", TriggerEchoEvent,
    NULL },
  { "addBreakpoint", AddBreakpoint,
    add_breakpoint_params },
  { "addBreakpointWithScriptUri", AddBreakpointWithScriptUri,
    add_breakpoint_with_script_uri_params },
  { "addBreakpointAtEntry", AddBreakpointAtEntry,
    add_breakpoint_at_entry_params },
  { "_addBreakpointAtActivation", AddBreakpointAtActivation,
    add_breakpoint_at_activation_params },
  { "_buildExpressionEvaluationScope", BuildExpressionEvaluationScope,
    build_expression_evaluation_scope_params },
  { "_clearCpuProfile", ClearCpuProfile,
    clear_cpu_profile_params },
  { "_clearVMTimeline", ClearVMTimeline,
    clear_vm_timeline_params, },
  { "_compileExpression", CompileExpression, compile_expression_params },
  { "_enableProfiler", EnableProfiler,
    enable_profiler_params, },
  { "evaluate", Evaluate,
    evaluate_params },
  { "evaluateInFrame", EvaluateInFrame,
    evaluate_in_frame_params },
  { "_getAllocationProfile", GetAllocationProfile,
    get_allocation_profile_params },
  { "_getAllocationSamples", GetAllocationSamples,
      get_allocation_samples_params },
  { "_getNativeAllocationSamples", GetNativeAllocationSamples,
      get_native_allocation_samples_params },
  { "getClassList", GetClassList,
    get_class_list_params },
  { "_getCpuProfile", GetCpuProfile,
    get_cpu_profile_params },
  { "_getCpuProfileTimeline", GetCpuProfileTimeline,
    get_cpu_profile_timeline_params },
  { "_writeCpuProfileTimeline", WriteCpuProfileTimeline,
    write_cpu_profile_timeline_params },
  { "getFlagList", GetFlagList,
    get_flag_list_params },
  { "_getHeapMap", GetHeapMap,
    get_heap_map_params },
  { "_getInboundReferences", GetInboundReferences,
    get_inbound_references_params },
  { "_getInstances", GetInstances,
    get_instances_params },
  { "getIsolate", GetIsolate,
    get_isolate_params },
  { "getMemoryUsage", GetMemoryUsage,
    get_memory_usage_params },
  { "_getIsolateMetric", GetIsolateMetric,
    get_isolate_metric_params },
  { "_getIsolateMetricList", GetIsolateMetricList,
    get_isolate_metric_list_params },
  { "getObject", GetObject,
    get_object_params },
  { "_getObjectStore", GetObjectStore,
    get_object_store_params },
  { "_getObjectByAddress", GetObjectByAddress,
    get_object_by_address_params },
  { "_getPersistentHandles", GetPersistentHandles,
      get_persistent_handles_params, },
  { "_getPorts", GetPorts,
    get_ports_params },
  { "_getReachableSize", GetReachableSize,
    get_reachable_size_params },
  { "_getRetainedSize", GetRetainedSize,
    get_retained_size_params },
  { "_getRetainingPath", GetRetainingPath,
    get_retaining_path_params },
  { "getScripts", GetScripts,
    get_scripts_params },
  { "getSourceReport", GetSourceReport,
    get_source_report_params },
  { "getStack", GetStack,
    get_stack_params },
  { "_getUnusedChangesInLastReload", GetUnusedChangesInLastReload,
    get_unused_changes_in_last_reload_params },
  { "_getTagProfile", GetTagProfile,
    get_tag_profile_params },
  { "_getTypeArgumentsList", GetTypeArgumentsList,
    get_type_arguments_list_params },
  { "getVersion", GetVersion,
    get_version_params },
  { "getVM", GetVM,
    get_vm_params },
  { "_getVMMetric", GetVMMetric,
    get_vm_metric_params },
  { "_getVMMetricList", GetVMMetricList,
    get_vm_metric_list_params },
  { "_getVMTimeline", GetVMTimeline,
    get_vm_timeline_params },
  { "_getVMTimelineFlags", GetVMTimelineFlags,
    get_vm_timeline_flags_params },
  { "invoke", Invoke, invoke_params },
  { "kill", Kill, kill_params },
  { "pause", Pause,
    pause_params },
  { "removeBreakpoint", RemoveBreakpoint,
    remove_breakpoint_params },
  { "reloadSources", ReloadSources,
    reload_sources_params },
  { "_reloadSources", ReloadSources,
    reload_sources_params },
  { "resume", Resume,
    resume_params },
  { "_requestHeapSnapshot", RequestHeapSnapshot,
    request_heap_snapshot_params },
  { "_evaluateCompiledExpression", EvaluateCompiledExpression,
    evaluate_compiled_expression_params },
  { "setExceptionPauseMode", SetExceptionPauseMode,
    set_exception_pause_mode_params },
  { "setFlag", SetFlag,
    set_flags_params },
  { "setLibraryDebuggable", SetLibraryDebuggable,
    set_library_debuggable_params },
  { "setName", SetName,
    set_name_params },
  { "_setTraceClassAllocation", SetTraceClassAllocation,
    set_trace_class_allocation_params },
  { "setVMName", SetVMName,
    set_vm_name_params },
  { "_setVMTimelineFlags", SetVMTimelineFlags,
    set_vm_timeline_flags_params },
  { "_collectAllGarbage", CollectAllGarbage,
    collect_all_garbage_params },
  { "_getDefaultClassesAliases", GetDefaultClassesAliases,
    get_default_classes_aliases_params },
};

3.8 HttpRequestClient

class HttpRequestClient extends Client {
  static ContentType jsonContentType =
      new ContentType("application", "json", charset: "utf-8");
  final HttpRequest request;


  void post(Response result) {
    HttpResponse response = request.response;
    response.headers.add('Access-Control-Allow-Origin', '*');
    response.headers.contentType = jsonContentType;
    switch (result.kind) {
      case ResponsePayloadKind.String:
        response.write(result.payload);
        break;
      case ResponsePayloadKind.Utf8String:
        response.add(result.payload);
        break;
      case ResponsePayloadKind.Binary:
        throw 'Can not handle binary responses';
    }
    response.close();
    close();
  }

四、小结

ServiceIsolate处理监听状态,根据具体的命令,最终会执行执行到service.cc的成service_methods_数组中所定义的方法。看到这里,你可能还不了解其功能。 这只是为下一篇文章介绍timeline工作原理做铺垫而已。

Dart虚拟机运行原理

2019-10-06 06:15:40

一、Dart虚拟机

1.1 引言

Dart VM是一种虚拟机,为高级编程语言Dart提供执行环境,但这并意味着Dart在D虚拟机上执行时,总是采用解释执行或者JIT编译。 例如还可以使用Dart虚拟机的AOT管道将Dart代码编译为机器代码,然后运行在Dart虚拟机的精简版环境,称之为预编译运行时(precompiled runtime)环境,该环境不包含任何编译器组件,且无法动态加载Dart源代码。

1.2 虚拟机如何运行Dart代码

Dart VM有多钟方式来执行代码:

  • 源码或者Kernel二进制(JIT)
  • snapshot
    • AOT snapshot
    • AppJIT snapshot

区别主要在于什么时机以及如何将Dart代码转换为可执行的代码。

1.3 Isolate组成

先来看看dart虚拟机中isolate的组成:

  • isolate堆是运该isolate中代码分配的所有对象的GC管理的内存存储;
  • vm isolate是一个伪isolate,里面包含不可变对象,比如null,true,false;
  • isolate堆能引用vm isolate堆中的对象,但vm isolate不能引用isolate堆;
  • isolate彼此之间不能相互引用
  • 每个isolate都有一个执行dart代码的Mutator thread,一个处理虚拟机内部任务(比如GC, JIT等)的helper thread;

isolate拥有内存堆和控制线程,虚拟机中可以有很多isolate,但彼此之间不能直接状态,只能通过dart特有的端口;isolate除了拥有一个mutator控制线程,还有一些其他辅助线程:

  • 后台JIT编译线程;
  • GC清理线程;
  • GC并发标记线程;

线程和isolate的关系是什么呢?

  • 同一个线程在同一时间只能进入一个isolate,当需要进入另一个isolate则必须先退出当前的isolate;
  • 一次只能有一个Mutator线程关联对应的isolate,Mutator线程是执行Dart代码并使用虚拟机的公共的C语言API的线程

1.4 ThreadPool组成

虚拟机采用线程池的方式来管理线程,定义在runtime/vm/thread_pool.h

ThreadPool的核心成员变量:

  • all_workers_:记录所有的workers;
  • idle_workers:_记录所有空闲的workers;
  • count_started_:记录该线程池的历史累计启动workers个数;
  • count_stopped_:记录该线程池的历史累计关闭workers个数;
  • count_running_:记录该线程池当前正在运行的worker个数;
  • count_idle_:记录该线程池当前处于空闲的worker个数,也就是idle_workers的长度;

ThreadPool核心方法:

  • Run(Task*): 执行count_running_加1,并将Task设置到该Worker,
    • 当idle_workers_为空,则创建新的Worker并添加到all_workers_队列头部,count_started_加1;
    • 当idle_workers_不为空,则取走idle_workers_队列头部的Worker,count_idle_减1;
  • Shutdown(): 将all_workers_和idle_workers_队列置为NULL,并将count_running_和count_idle_清零,将关闭的all_workers_个数累加到count_stopped_;
  • SetIdleLocked(Worker*):将该Worker添加到idle_workers_队列头部,count_idle_加1, count_running_减1;
  • ReleaseIdleWorker(Worker*):从all_workers_和idle_workers_队列中移除该Worker,count_idle_减1,count_stopped_加1;

对应关系图:

  count_started_ count_stopped_ count_running_ count_idle_
Run() +1(无空闲worker)   +1 -1(有空闲worker)
Shutdown()   +all_workers_个数 清零 清零
SetIdleLocked()     -1 +1
ReleaseIdleWorker()   +1   -1

可见,count_started_ - count_stopped_ = count_running_ + count_idle_;

二、JIT运行模式

2.1 CFE前端编译器

看看dart是如何直接理解并执行dart源码

// gityuan.dart
main() => print('Hello Gityuan!');

//dart位于flutter/bin/cache/dart-sdk/bin/dart
$ dart gityuan.dart
Hello, World!

说明:

  • Dart虚拟机并不能直接从Dart源码执行,而是执行dill二进制文件,该二进制文件包括序列化的Kernel AST(抽象语法树)。
  • Dart Kernel是一种从Dart中衍生而来的高级语言,设计之初用于程序分析与转换(transformations)的中间产物,可用于代码生成与后端编译器,该kernel语言有一个内存表示,可以序列化为二进制或文本。
  • 将Dart转换为Kernel AST的是CFE(common front-end)通用前端编译器。
  • 生成的Kernel AST可交由Dart VM、dev_compiler以及dart2js等各种Dart工具直接使用。

2.2 kernel service

有一个辅助类isolate叫作kernel service,其核心工作就是CFE,将dart转为Kernel二进制,然后VM可直接使用Kernel二进制运行在主isolate里面运行。

2.3 debug运行

将dart代码转换为kernel二进制和执行kernel二进制,这两个过程也可以分离开来,在两个不同的机器执行,比如host机器执行编译,移动设备执行kernel文件。

图解:

  • 这个编译过程并不是flutter tools自身完成,而是交给另一个进程frontend_server来执行,它包括CFE和一些flutter专有的kernel转换器。
  • hot reload:热重载机制正是依赖这一点,frontend_server重用上一次编译中的CFE状态,只重新编译实际更改的部分。

2.4 RawClass内部结构

虚拟机内部对象的命名约定:使用C++定义的,其名称在头文件raw_object.h中以Raw开头,比如RawClass是描述Dart类的VM对象,RawField是描述Dart类中的Dart字段的VM对象。

1)将内核二进制文件加载到VM后,将对其进行解析以创建表示各种程序实体的对象。这里采用了懒加载模式,一开始只有库和类的基本信息被加载,内核二进制文件中的每一个实体都会保留指向该二进制文件的指针,以便后续可根据需要加载更多信息。

2)仅在以后需要运行时,才完全反序列化有关类的信息。(例如查找类的成员变量,创建类的实例对象等),便会从内核二进制文件中读取类的成员信息。 但功能完整的主体(FunctionNode)在此阶段并不会反序列化,而只是获取其签名。

到此,已从内核二进制文件加载了足够的信息以供运行时成功解析和调用的方法。

所有函数的主体都具有占位符code_,而不是实际的可执行代码:它们指向LazyCompileStub,该Stub只是简单地要求系统Runtime为当前函数生成可执行代码,然后对这些新生成的代码进行尾部调用。

2.5 查看Kernel文件格式

gen_kernel.dart利用CFE将Dart源码编译为kernel binary文件(也就是dill),可利用dump_kernel.dart能反解kernel binary文件,命令如下所示:

//将hello.dart编译成hello.dill
$ cd <FLUTTER_ENGINE_ROOT>
$ dart third_party/dart/pkg/vm/bin/gen_kernel.dart          \
       --platform out/android_debug/vm_platform_strong.dill \
       -o hello.dill                                        \
       hello.dart

//转储AST的文本表示形式
$ dart third_party/dart/pkg/vm/bin/dump_kernel.dart hello.dill hello.kernel.txt

gen_kernel.dart文件,需要平台dill文件,这是一个包括所有核心库(dart:core, dart:async等)的AST的kernel binary文件。如果Dart SDK已经编译过,可直接使用out/ReleaseX64/vm_platform_strong.dill,否则需要使用compile_platform.dart来生成平台dill文件,如下命令:

//根据给定的库列表,来生成platform和outline文件
$ cd <FLUTTER_ENGINE_ROOT>
$ dart third_party/dart/pkg/front_end/tool/_fasta/compile_platform.dart \
       dart:core                                                        \        
       third_party/dart/sdk/lib/libraries.json                          \
       vm_outline.dill vm_platform.dill vm_outline.dill                 

2.6 未优化编译器

首次编译函数时,这是通过未优化编译器来完成的。

未优化的编译器分两步生成机器代码:

  • AST -> CFG: 对函数主体的序列化AST进行遍历,以生成函数主体的控制流程图(CFG),CFG是由填充中间语言(IL)指令的基本块组成。此阶段使用的IL指令类似于基于堆栈的虚拟机的指令:它们从堆栈中获取操作数,执行操作,然后将结果压入同一堆栈
  • IL -> 机器指令:使用一对多的IL指令,将生成的CFG直接编译为机器代码:每个IL指令扩展为多条机器指令。

在此阶段没有执行优化,未优化编译器的主要目标是快速生成可执行代码。

2.7 内联缓存

未优化编译过程,编译器不会尝试静态解析任何未在Kernel二进制文件中解析的调用,因此(MethodInvocation或PropertyGet AST节点)的调用被编译为完全动态的。虚拟机当前不使用任何形式的基于虚拟表(virtual table)或接口表(interface table)的调度,而是使用内联缓存实现动态调用。

虚拟机的内联缓存的核心思想是缓存方法解析后的站点结果信息,对于内联缓存最初是为了解决函数的本地代码:

  • 站点调用的特定缓存(RawICData对象)将接受者的类映射到方法,缓存中记录着一些辅助信息,比如方法和基本块的调用频次计数器,该计数器记录着被跟踪类的调用频次;
  • 共享的查找存根,用于实现方法调用的快速路径。该存根在给定的高速缓存中进行搜索,以查看其是否包含与接收者的类别匹配的条目。 如果找到该条目,则存根将增加频率计数器和尾部调用缓存的方法。否则,存根将调用系统Runtime来解析方法实现的逻辑,如果方法解析成功,则将更新缓存,并且随后的调用无需进入系统Runtime。

2.8 编译优化

未优化编译器产生的代码执行比较慢,需要自适应优化,通过profile配置文件来驱动优化策略。内联优化,当与某个功能关联的执行计数器达到某个阈值时,该功能将提交给后台优化编译器进行优化。

优化编译的方式与未优化编译的方式相同:通过序列化内核AST来构建未优化的IL。但是,优化编译器不是直接将IL编译为机器码,而是将未优化的IL转换为基于静态单分配(SSA)形式的优化的IL。

对基于SSA的IL通过基于收集到的类型反馈,内联,范围分析,类型传播,表示选择,存储到加载,加载到加载转发,全局值编号,分配接收等一系列经典和Dart特定的优化来进行专业化推测。最后,使用线性扫描寄存器分配器和一个简单的一对多的IL指令。优化编译完成后,后台编译器会请求mutator线程输入安全点,并将优化的代码附加到该函数。下次调用该函数时,它将使用优化的代码。

另外,有些函数包含很长的运行循环,因此在函数仍在运行时将执行从未优化的代码切换到优化的代码是有意义的,此过程之所以称为“堆栈替换”(OSR)。

VM还具有可用于控制JIT并使其转储IL以及用于JIT正在编译的功能的机器代码的标志

$ dart --print-flow-graph-optimized         \
       --disassemble-optimized              \
       --print-flow-graph-filter=myFunc     \
       --no-background-compilation          \
       hel.dart

2.9 反优化

优化是基于统计的,可能出现违反优化的情况

void printAnimal(obj) {
  print('Animal {');
  print('  ${obj.toString()}');
  print('}');
}

// 大量调用的情况下,会推测printAnimal假设总是Cat的情况下来优化代码
for (var i = 0; i < 50000; i++)
  printAnimal(Cat());

// 此处出现的是Dog,优化版本失效,则触发反优化
printAnimal(Dog());

每当只要优化版本遇到无法解决的情况,它就会将执行转移到未优化功能的匹配点,然后继续执行,这个恢复过程称为去优化:未优化的功能版本不做任何假设,可以处理所有可能的输入。

虚拟机通常会在执行一次反优化后,放弃该功能的优化版本,然后在以后使用优化的类型反馈再次对其进行重新优化。虚拟机保护编译器进行推测性假设的方式有两种:

  • 内联检查(例如CheckSmi,CheckClass IL指令),以验证假设是否在编译器做出此假设的使用场所成立。例如,将动态调用转换为直接调用时,编译器会在直接调用之前添加这些检查。 在此类检查中发生的取消优化称为“急切优化”,因为它在达到检查时就急于发生。
  • 运行时在更改优化代码所依赖的内容时,将会丢弃优化代码。例如,优化编译器可能会发现某些类从未扩展过,并且在类型传播过程中使用了此信息。 但是,随后的动态代码加载或类最终确定可能会引入C的子类,导致假设无效。此时,运行时需要查找并丢弃所有在C没有子类的假设下编译的优化代码。 运行时可能会在执行堆栈上找到一些现在无效的优化代码,在这种情况下,受影响的帧将被标记为不优化,并且当执行返回时将进行不优化。 这种取消优化称为延迟取消优化,因为它会延迟到控制权返回到优化代码为止。

三、Snapshots运行模式

3.1 通过Snapshots运行

1)虚拟机有能力将isolate的堆(驻留在堆上的对象图)序列化成二进制的快照,启动虚拟机isolate的时候可以从快照中重新创建相同的状态。

Snapshot的格式是低级的,并且针对快速启动进行了优化,本质上是要创建的对象列表以及如何将它们连接在一起的说明。那是快照背后的原始思想:代替解析Dart源码并逐步创建虚拟机内部的数据结构,这样虚拟机通过快照中的所有必要数据结构来快速启动isolate。

2)最初,快照不包括机器代码,但是后来在开发AOT编译器时添加了此功能。开发AOT编译器和带代码快照的动机是为了允许虚拟机在由于平台级别限制而无法进行JIT的平台上使用。

带代码的快照的工作方式几乎与普通快照相同,只是有一点点不同:它们包括一个代码部分,该部分与快照的其余部分不同,不需要反序列化。该代码节的放置方式使其可以在映射到内存后直接成为堆的一部分

3.2 通过AppJIT Snapshots运行

引入AppJIT快照可减少大型Dart应用程序(如dartanalyzer或dart2js)的JIT预热时间。当这些工具用于小型项目时,它们花费的实际时间与VM花费的JIT编译这些应用程序的时间一样多。

AppJIT快照可以解决此问题:可以使用一些模拟训练数据在VM上运行应用程序,然后将所有生成的代码和VM内部数据结构序列化为AppJIT快照。然后可以分发此快照,而不是以源(或内核二进制)形式分发应用程序。如果出现实际数据上的执行配置文件与培训期间观察到的执行配置文件不匹配,快照开始的VM仍可以采用JIT模式执行。

3.3 通过AppAOT Snapshots运行

AOT快照最初是为无法进行JIT编译的平台引入的,对于无法进行JIT意味着:

  • AOT快照必须包含应用程序执行期间可能调用的每个功能的可执行代码;
  • 可执行代码不得依赖于执行期间可能违反的任何推测性假设

为了满足这些要求,AOT编译过程会进行全局静态分析(类型流分析, TFA),以确定从已知入口点集中可访问应用程序的哪些部分,分配了哪些类的实例以及类型如何在程序中流动。 所有这些分析都是保守的:这意味着它们会在正确性方面出错,与可以在性能方面出错的JIT形成鲜明对比,因为它始终可以取消优化为未优化的代码以实现正确的行为。

然后,所有可能达到的功能都将编译为本地代码,而无需进行任何推测性优化。但是,类型流信息仍用于专门化代码(例如,取消虚拟化调用),编译完所有函数后,即可获取堆的快照。

最终的快照snapshot可以运行在预编译Runtime,该Runtime是Dart VM的特殊变体,其中不包括诸如JIT和动态代码加载工具之类的组件。

AOT编译工具没有包含进Dart SDK。

//需要构建正常的dart可执行文件和运行AOT代码的runtime
$ tool/build.py -m release -a x64 runtime dart_precompiled_runtime

// 使用AOT编译器来编译APP
$ pkg/vm/tool/precompiler2 hello.dart hello.aot

//执行AOT快照
$ out/ReleaseX64/dart_precompiled_runtime hello.aot
Hello, World!

3.3.1 Switchable Calls

1)即使进行了全局和局部分析,AOT编译的代码仍可能包含无法静态的去虚拟化的调用站点。为了补偿此AOT编译代码和运行时,采用JIT中使用的内联缓存技术的扩展。此扩展版本称为可切换呼叫 (Switchable Calls)。

JIT部分已经描述过,与调用站点关联的每个内联缓存均由两部分组成:一个缓存对象(由RawICData实例表示)和一个要调用的本机代码块(例如InlineCacheStub)。在JIT模式下,运行时只会更新缓存本身。但在AOT运行时中,可以根据内联缓存的状态选择同时替换缓存和要调用的本机代码。

最初,所有动态呼叫均以未链接状态开始。首次调用此类呼叫站点时,将调用UnlinkedCallStub,它只是调用运行时帮助程序DRT_UnlinkedCall来链接此呼叫站点。

2)如果可能,DRT_UnlinkedCall尝试将呼叫站点转换为单态状态。在这种状态下,呼叫站点变成直接呼叫,该呼叫通过特殊的单态入口点进入方法,该入口点验证接收方是否具有预期的类。

在上面的示例中,假设第一次执行obj.method()时,obj是C的实例,而obj.method则解析为C.method。

下次执行相同的调用站点时,它将直接调用C.method,从而绕过任何类型的方法查找过程。但是,它将通过特殊的入口点(已验证obj仍然是C的实例)进入C.method。如果不是这种情况,将调用DRT_MonomorphicMiss并将尝试选择下一个调用站点状态。

3)C.method可能仍然是调用的有效目标,例如obj是C的扩展类但不覆盖C.method的D类的实例。在这种情况下,检查呼叫站点是否可以转换为由SingleTargetCallStub实现的单个目标状态(见RawSingleTargetCache)。

此存根基于以下事实:对于AOT编译,大多数类都使用继承层次结构的深度优先遍历来分配整数ID。如果C是具有D0,…,Dn子类的基类,并且没有一个覆盖C.method,则C.:cid <= classId(obj)<= max(D0.:cid,…,Dn .:cid)表示obj.method解析为C.method。在这种情况下,我们可以将类ID范围检查(单个目标状态)用于C的所有子类,而不是与单个类(单态)进行比较

否则,呼叫站点将切换为使用线性搜索内联缓存,类似于在JIT模式下使用的缓存。

最后,如果线性数组中的检查数量超过阈值,则呼叫站点将切换为使用类似字典的结构

四、附录

4.1 源码说明

整个过程相关的核心源码,简要说明:

  • runtime/vm/isolate.h: isolate对象
  • runtime/vm/thread.h:与连接到isolate对象的线程关联的状态
  • runtime/vm/heap/heap.h: isolate的堆
  • raw_object.h: 虚拟机内部对象
  • ast.dart:定义描述内核AST的类
  • kernel_loader.cc:其中的LoadEntireProgram()方法用于将内核AST反序列化为相应虚拟机对象的入口点
  • kernel_service.dart:实现了Kernel Service isolate
  • kernel_isolate.cc:将Dart实现粘合到VM的其余部分
  • pkg/front_end:用于解析Dart源码和构建内核AST
  • pkg/vm: 托管了大多数基于内核的VM特定功能,例如各种内核到内核的转换;由于历史原因,某些转换还位于pkg/kernel;
  • runtime/vm/compiler: 编译器源码
  • runtime/vm/compiler/jit/compiler.cc:编译管道入口点
  • runtime/vm/compiler/backend/il.h: IL的定义
  • runtime/vm/compiler/frontend/kernel_binary_flowgraph.cc: 其中的BuildGraph(), 内核到IL的翻译开始,处理各种人工功能的IL的构建
  • runtime/vm/stub_code_x64.cc:其中StubCode::GenerateNArgsCheckInlineCacheStub(),为内联缓存存根生成机器代码
  • runtime/vm/runtime_entry.cc:其中InlineCacheMissHandler()处理IC没有命中的情况
  • runtime/vm/compiler/compiler_pass.cc: 定义优化编译器的遍历及其顺序
  • runtime/vm/compiler/jit/jit_call_specializer.h:进行大多数基于类型反馈的专业化
  • runtime/vm/deopt_instructions.cc: 反优化过程
  • runtime/vm/clustered_snapshot.cc:处理快照的序列化和反序列化。API函数家族Dart_CreateXyzSnapshot[AsAssembly]负责写出堆的快照,比如 Dart_CreateAppJITSnapshotAsBlobs 和Dart_CreateAppAOTSnapshotAsAssembly。
  • runtime/vm/dart_api_impl.cc: 其中Dart_CreateIsolate可以选择获取快照数据以开始isolate。
  • pkg/vm/lib/transformations/type_flow/transformer.dart: TFA(类型流分析)以及基于TFA结果转换的切入点
  • runtime/vm/compiler/aot/precompiler.cc:其中Precompiler::DoCompileAll()是整个AOT编译的切入点

4.2 参考资料

https://mrale.ph/dartvm/

解读Dart虚拟机的参数列表

2019-09-23 05:15:40

一、概述

在third_party/dart/runtime/vm/flag_list.h定义了Dart虚拟机中所有标志的列表,标志分为以下几大类别:

  • Product标记:可以在任何部署模式中设置,包括Product模式
  • Release标志:通常可用的标志,除Product模式以外
  • Precompile标志:通常可用的标志,除Product模式或已预编译的运行时以外
  • Debug标志:只能在启用C++断言的VM调试模式中设置

Product、Release、Precompile、Debug这四类可控参数,可使用的范围逐次递减,比如Product flags可用于所有模式,Debug flags只能用于调试模式。Dart虚拟机总共有106个flags参数

二、flags参数

2.1 Product flags

用法:PRODUCT_FLAG_MARCO(名称,类型,默认值,注解)

名称 默认值 注解
collect_dynamic_function_names true 收集所有动态函数名称以标识唯一目标
enable_kernel_expression_compilation true 启用内核前端来编译表达式
enable_mirrors true 允许导入dart:mirrors
enable_ffi true 允许导入dart:ffi
guess_icdata_cid true 创建算法等操作的类型反馈
lazy_dispatchers true 懒惰地生成调度程序
polymorphic_with_deopt true 反优化的多态调用、巨形调用
reorder_basic_blocks true 对基本块重新排序
use_bare_instructions true 启用裸指令模式
truncating_left_shift true 尽可能优化左移以截断
use_cha_deopt true 使用类层次分析,即使会导致反优化
use_strong_mode_types true 基于强模式类型的优化
enable_slow_path_sharing true 启用共享慢速路径代码
enable_multiple_entrypoints true 启用多个入口点
experimental_unsafe_mode_use_ at_your_own_risk false 省略运行时强模式类型检查并禁用基于类型的优化
abort_on_oom false 如果内存分配失败则中止,仅与–old-gen-heap-size一起使用
collect_code false 尝试GC不常用代码
dwarf_stack_traces false 在dylib快照中发出dwarf行号和内联信息,而不表示堆栈跟踪
fields_may_be_reset false 不要优化静态字段初始化
link_natives_lazily false 懒加载链接本地调用
precompiled_mode false 预编译编译器模式
print_snapshot_sizes false 打印生成snapshot的大小
print_snapshot_sizes_verbose false 打印生成snapshot的详细大小
print_benchmarking_metrics false 打印其他内存和延迟指标以进行基准测试
shared_slow_path_triggers_gc false 测试:慢路径触发GC
trace_strong_mode_types false 跟踪基于强模式类型的优化
use_bytecode_compiler false 从字节码编译
use_compactor false 当在旧空间执行GC时则压缩堆
enable_testing_pragmas false 启用神奇的编译指示以进行测试
enable_interpreter false 启用解释内核字节码
verify_entry_points false 通过native API访问无效成员时抛出API错误
background_compilation USING_MULTICORE 根据是否多核来决定是否后台运行优化编译
concurrent_mark USING_MULTICORE 老年代的并发标记
concurrent_sweep USING_MULTICORE 老年代的并发扫描
use_field_guards !USING_DBC 使用字段gurad,跟踪字段类型
interpret_irregexp USING_DBC 使用irregexp字节码解释器
causal_async_stacks !USING_PRODUCT 非product 则开启改进异步堆栈
marker_tasks USING_MULTICORE ? 2 : 0 老生代GC标记的任务数,0代表在主线程执行
idle_timeout_micros 1000 * 1000 长时间后将空闲任务从线程池隔离,单位微秒
idle_duration_micros 500 * 1000 允许空闲任务运行的时长
old_gen_heap_size (kWordSize <= 4) ? 1536 : 0 旧一代堆的最大大小,或0(无限制),单位MB
new_gen_semi_max_size (kWordSize <= 4) ? 8 : 16 新一代半空间的最大大小,单位MB
new_gen_semi_initial_size (kWordSize <= 4) ? 1 : 2 新一代半空间的最大初始大小,单位MB
compactor_tasks 2 并行压缩使用的任务数
getter_setter_ratio 13 用于double拆箱启发式的getter/setter使用率?
huge_method_cutoff_in_tokens 20000 令牌中的大量方法中断:禁用大量方法的优化?
max_polymorphic_checks 4 多态检查的最大数量,否则为巨形的?
max_equality_polymorphic_checks 32 等式运算符中的多态检查的最大数量
compilation_counter_threshold 10 在解释执行函数编译完成前的函数使用次数要求,-1表示从不
optimization_counter_threshold 30000 函数在优化前的用法计数值,-1表示从不
optimization_level 2 优化级别:1(有利大小),2(默认),3(有利速度)

optimization_level这是一个可以尝试的参数

2.2 Release flags

用法:RELEASE_FLAG_MARCO(名称,product_value,类型,默认值,注解)

名称 product值 默认值 注解
eliminate_type_checks true true 静态类型分析允许时消除类型检查
dedup_instructions true false 预编译时规范化指令
support_disassembler false true 支持反汇编
support_il_printer false true 支持IL打印
support_service false true 支持服务协议
disable_alloc_stubs_after_gc false false 压力测试标识
disassemble false false 反汇编dart代码
disassemble_optimized false false 反汇编优化代码
dump_megamorphic_stats false false dump巨形缓存统计信息
dump_symbol_stats false false dump符合表统计信息
enable_asserts false false 启用断言语句
log_marker_tasks false false 记录老年代GC标记任务的调试信息
randomize_optimization_counter false false 基于每个功能随机化优化计数器阈值,用于测试
pause_isolates_on_start false false 在isolate开始前暂停
pause_isolates_on_exit false false 在isolate退出前暂停
pause_isolates_on_unhandled_exceptions false false 在isolate发生未捕获异常前暂停
print_ssa_liveranges false false 内存分配后打印有效范围
print_stacktrace_at_api_error false false 当API发生错误时,打印native堆栈
profiler false false 开启profiler
profiler_native_memory false false 开启native内存统计收集
trace_profiler false false 跟踪profiler
trace_field_guards false false 跟踪字段cids的变化
verify_after_gc false false 在GC之后启用堆验证
verify_before_gc false false 在GC之前启用堆验证
verbose_gc false false 开启详细GC
verbose_gc_hdr 40 40 打印详细的GC标头间隔

2.3 Precompile flags

用法:PRECOMPILE_FLAG_MARCO(名称,precompiled_value,product_value,类型,默认值,注释)

名称 precompiled值 product值 默认值 说明
load_deferred_eagerly true true false 急切加载延迟的库
use_osr false true true 使用OSR
async_debugger false false true 调试器支持异步功能
support_reload false false true 支持isolate重新加载
force_clone_compiler_objects false false false 强制克隆编译器中所需的对象(ICData和字段)
stress_async_stacks false false false 压测异步堆栈
trace_irregexp false false false 跟踪irregexps
deoptimize_alot false false false 取消优化,从native条目返回到dart代码
deoptimize_every 0 0 0 在每N次堆栈溢出检查中取消优化

2.4 Debug flags

用法:DEBUG_FLAG_MARCO(名称,类型,默认值,注解)

名称 默认值 注解
print_variable_descriptors false 在反汇编中打印变量描述符
trace_cha false 跟踪类层次分析(CHA)操作
trace_ic false 跟踪IC处理?
trace_ic_miss_in_optimized false 跟踪优化中的IC未命中情况
trace_intrinsified_natives false 跟踪是否调用固有native
trace_isolates false 跟踪isolate的创建与关闭
trace_handles false 跟踪handles的分配
trace_kernel_binary false 跟踪内核的读写
trace_natives false 跟踪native调用
trace_optimization false 打印优化详情
trace_profiler_verbose false 跟踪profiler详情
trace_runtime_calls false 跟踪runtime调用
trace_ssa_allocator false 跟踪通过SSA的寄存器分配
trace_type_checks false 跟踪运行时类型检测
trace_patching false 跟踪代码修补
trace_optimized_ic_calls false 跟踪优化代码中的IC调用?
trace_zones false 跟踪zone的内存分配大小
verify_gc_contains false 在GC期间开启地址是否包含的验证
verify_on_transition false 验证dart/vm的过渡?
support_rr false 支持在RR中运行?

默认值全部都为false,

三、实现原理

3.1 FLAG_LIST

[-> third_party/dart/runtime/vm/flags.cc]

FLAG_LIST(PRODUCT_FLAG_MARCO,
          RELEASE_FLAG_MARCO,
          DEBUG_FLAG_MARCO,
          PRECOMPILE_FLAG_MARCO)

FLAG_LIST列举了所有的宏定义,这里有四种不同的宏,接下来逐一展开说明

3.2 flag宏定义

[-> third_party/dart/runtime/vm/flags.cc]

// (1) Product标记:可以在任何部署模式中设置
#define PRODUCT_FLAG_MARCO(name, type, default_value, comment)                 \
  type FLAG_##name = Flags::Register_##type(&FLAG_##name, #name, default_value, comment);

// (2) Release标志:通常可用的标志,除Product模式以外
#if !defined(PRODUCT)
#define RELEASE_FLAG_MARCO(name, product_value, type, default_value, comment)  \
  type FLAG_##name = Flags::Register_##type(&FLAG_##name, #name, default_value, comment);

// (3) Precompile标志:通常可用的标志,除Product模式或已预编译的运行时以外
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
#define PRECOMPILE_FLAG_MARCO(name, pre_value, product_value, type, default_value, comment) \
  type FLAG_##name = Flags::Register_##type(&FLAG_##name, #name, default_value, comment);

// (4) Debug标志:只能在debug调试模式运行
#if defined(DEBUG)  
#define DEBUG_FLAG_MARCO(name, type, default_value, comment)                   \
  type FLAG_##name =  Flags::Register_##type(&FLAG_##name, #name, default_value, comment);

这里涉及到3个宏定义:

  • PRODUCT:代表Product模式;
  • DART_PRECOMPILED_RUNTIME:代表运行时已预编译模式;
  • DEBUG:代表调试模式;

可见,宏定义最终都是调用Flags::Register_XXX()方法,这里以FLAG_LIST中的其中一条定义来展开说明:

P(collect_code, bool, false, "Attempt to GC infrequently used code.")
//展开后等价如下
type FLAG_collect_code = Flags::Register_bool(&FLAG_collect_code, collect_code, false,
    "Attempt to GC infrequently used code.");

3.3 Flags::Register_bool

[-> third_party/dart/runtime/vm/flags.cc]

bool Flags::Register_bool(bool* addr,
                          const char* name,
                          bool default_value,
                          const char* comment) {
  Flag* flag = Lookup(name);  //[见小节3.4]
  if (flag != NULL) {
    return default_value;
  }
  flag = new Flag(name, comment, addr, Flag::kBoolean);
  AddFlag(flag);
  return default_value;
}

3.4 Flags::Lookup

[-> third_party/dart/runtime/vm/flags.cc]

Flag* Flags::Lookup(const char* name) {
  //遍历flags_来查找是否已存在
  for (intptr_t i = 0; i < num_flags_; i++) {
    Flag* flag = flags_[i];
    if (strcmp(flag->name_, name) == 0) {
      return flag;
    }
  }
  return NULL;
}

Flags类中有3个重要的静态成员变量:

static Flag** flags_; //记录所有的flags对象指针
static intptr_t capacity_;  //代表数组的容量大小
static intptr_t num_flags_;  //代表当前flags对象指针的个数

3.5 Flag初始化

[-> third_party/dart/runtime/vm/flags.cc]

class Flag {
  Flag(const char* name, const char* comment, void* addr, FlagType type)
      : name_(name), comment_(comment), addr_(addr), type_(type) {}

  const char* name_;
  const char* comment_;
  union {
    void* addr_;
    bool* bool_ptr_;
    int* int_ptr_;
    uint64_t* uint64_ptr_;
    charp* charp_ptr_;
    FlagHandler flag_handler_;
    OptionHandler option_handler_;
  };
  FlagType type_;
}

3.6 Flags::AddFlag

[-> third_party/dart/runtime/vm/flags.cc]

class Flag {

  void Flags::AddFlag(Flag* flag) {
    if (num_flags_ == capacity_) {
      if (flags_ == NULL) {
        capacity_ = 256;  //初始化大小为256
        flags_ = new Flag*[capacity_];
      } else {
        intptr_t new_capacity = capacity_ * 2; //扩容
        Flag** new_flags = new Flag*[new_capacity];
        for (intptr_t i = 0; i < num_flags_; i++) {
          new_flags[i] = flags_[i];
        }
        delete[] flags_;
        flags_ = new_flags;
        capacity_ = new_capacity;
      }
    }
    //将flag记录到flags_
    flags_[num_flags_++] = flag;
  }
}

最终,所有的flag信息都记录在Flags类的静态成员变量flags_中。

四、总结

  • Product标记:可以在任何部署模式中设置
  • Release标志:通常可用的标志,除Product模式以外
  • Precompile标志:通常可用的标志,除Product模式或已预编译的运行时以外
  • Debug标志:只能在启用C++断言的VM调试模式中设置