API 深度解析)
编程语言编译器语言运行时标准库开发工具【免费下载链接】sdkThe Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.项目地址https://gitcode.com/gh_mirrors/sdk1/sdk点击查看免费下载本文以 Dart SDK 仓库中 pkg/vm_service_protos/CHANGELOG.md 记录的版本历史为主线,结合包内源码、第三方 Perfetto proto 定义与 VM 服务协议的测试用例,完整梳理vm_service_protos包从 1.0.0 到 2.0.1 的每次 API 变更。读完本文,你将理解该包在 Dart VM 服务协议中的定位、2.0.0 围绕字符串驻留机制新增的全部类与访问器、Perfetto 驻留索引(interning index)的工作原理,以及读者端(reader-side)如何配合InternedData还原跟踪事件名称的官方实践。vm_service_protos 是什么按包 README 的定义,vm_service_protos是 A library for decoding protos returned by a service implementing the Dart VM service protocol——一个用于解码实现 Dart VM 服务协议的服务所返回的 protobuf 消息的库。VM 在提供 Perfetto 格式的时间线/CPU 采样数据时,输出的是 Perfetto 跟踪格式的 protobuf 字节流;vm_service_protos就是把这些字节流转译为类型化 Dart 对象(TracePacket、TrackEvent、InternedData等)的生成代码包。从 pubspec.yaml 可以看到当前包的工程事实:version: 2.0.1,与 CHANGELOG 顶层条目一致;environment: sdk: ^3.5.0,即要求 Dart 3.5 及以上;resolution: workspace,说明它是 SDK 仓库 pub workspace 的组成部分,版本约束直接来自仓库的 DEPS 文件(pubspec 中的注释专门解释了这是特殊情形,与一般包应写明兼容版本区间的良好实践不同);运行时依赖只有两个:fixnum: ^1.0.0与protobuf: ^6.0.0,dev 依赖为lints: any。公开入口是 lib/vm_service_protos.dart,它只由export语句组成,一次性暴露 35 个生成文件,覆盖 Perfetto 跟踪体系中的核心消息族:builtin_clock、clock_snapshot、trace、trace_packet、interned_data、profile_common、profile_packet,以及track_event目录下的debug_annotation、process_descriptor、thread_descriptor、track_descriptor、track_event等。这正是 1.0.0 版本日志中 code for working with Perfetto protos, which can be imported frompackage:vm_service_protos/vm_service_protos.dart 所指的导入路径。版本历史:逐条解读 CHANGELOG1.0.0 —— 初始版本CHANGELOG 记录 1.0.0 仅有一条实质内容:Initial version 加上Add code for working with Perfetto protos, which can be imported frompackage:vm_service_protos/vm_service_protos.dart。即 1.0.0 交付的是一套可解码 VM 服务协议返回的 Perfetto 消息的生成代码,入口统一收敛在 vm_service_protos.dart。从仓库结构看,该版本对应的就是当前lib/src/protos/perfetto/下的消息族,只是尚未包含 2.0.0 引入的驻留相关访问器。2.0.0 —— 以驻留机制为核心的 API 大幅扩充2.0.0 是功能大版本,CHANGELOG 逐条记录了以下变更,全部可在当前生成代码中得到印证:提升package:protobuf依赖约束到 4.0.0。当前仓库 pubspec 中该约束已演进为protobuf: ^6.0.0(见上节),2.0.0 时点则以 4.0.0为下限;引入EventCategory、EventName、DebugAnnotationName三个类。它们是驻留字典的条目类型,定义在 track_event.pb.dart 与 debug_annotation.pb.dart 中,并被 InternedData 的工厂构造器作为eventCategories、eventNames、debugAnnotationNames三个Iterable参数接收;为InternedData增加eventCategories、eventNames、debugAnnotationNames、debugAnnotationStringValues四个 getter。对照 interned_data.pb.dart 的BuilderInfo注册,这些字段编号分别为 1、2、3 和 29,与 interned_data.proto 中的repeated字段一一对应(proto 中Next id: 29的注释正对应debugAnnotationStringValues这一新增字段);为DebugAnnotation增加nameIid与stringValueIid两个 getter,以及hasNameIid、clearNameIid、hasStringValueIid、clearStringValueIid四个方法。has/clear方法是 protobuf 生成的标准字段是否设置/清空配套,供读者端判断一个注解名或字符串值是内联存储还是驻留引用;为TrackEvent增加categoryIids与nameIid两个 getter,以及hasNameIid、clearNameIid两个方法。这让事件可以直接携带类别驻留 ID 列表与名称驻留 ID,而不必内嵌重复的字符串。这一组变更的共同目的,是支持 Perfetto 的字符串驻留(interning)写入模式:写入端(writer)不再把每个跟踪事件的名称字符串重复序列化,而是把新字符串登记到InternedData字典中,后续事件只引用字典位置的整数 ID(interning ID,缩写 iid)。对解码方而言,2.0.0 之前拿不到这些 iid 字段,也就无法还原驻留写入的数据——这正是 2.0.0 大版本升级的原因。2.0.1 —— 兼容 protobuf 5.0 系列的维护版本2.0.1 在 CHANGELOG 中只有一条:Include support forpackage:protobuf5.0,即为package:protobuf5.x 系列保持/纳入兼容性,属于不改变公共 API 的维护性发布。需要说明的适用前提是:仓库当前检出的 pubspec.yaml 已将protobuf固定为^6.0.0且要求 Dart SDK^3.5.0,这是面向后续版本的开发态配置;2.0.1 所声明的兼容范围以该版本发布时的依赖解析为准。2.0.0 API 背后的原理:Perfetto 驻留索引如何工作要理解上面那批 getter 和类的必要性,需要读一下 proto 定义。interned_data.proto 文件头部的注释(第 33~50 行)系统解释了驻留机制:驻留索引是按包序列(packet sequence,即同一 producer/TraceWriter 产出的TracePacket链)逐步构建的,包只能引用同一序列内的驻留数据;写入端遇到字典中尚不存在的新值时,就追加一个新条目;当前及后续的包用该条目在字典中的位置(iid)来引用它,iid 取 0 表示无效(未设置);由于索引是增量构建的,若中间TracePacket丢失(例如环形缓冲区中某块数据被覆盖),索引就会缺失数据。为避免整个跟踪因此作废,索引会被周期性重置——对应的控制位就是SEQ_INCREMENTAL_STATE_CLEARED序列标志。生成的 Dart 侧 InternedData 完整承载了这张字典:除 2.0.0 新增的四个 getter 对应的字段外,还包括面向 CPU 采样剖析的functionNames(字段 5)、frames(6)、callstacks(7)、buildIds(16)、mappingPaths(17)、mappings(19)。其工厂构造器接受这些字段的Iterable参数并逐个addAll,体现了 protobuf 生成代码repeated 字段 Dart 集合视图的一贯风格。实战:读取端如何用新 API 还原驻留字符串包 README 明确指引读者到两个测试学习 API 用法:get_perfetto_vm_timeline_rpc_test 与 get_perfetto_cpu_samples_rpc_test。时间线测试完整展示了 2.0.0 全部新 API 的协作方式,值得精读:VM 侧开关。测试文件头部的VMOptions--intern_strings_when_writing_perfetto_timeline(第 5~6 行)表明,VM 需要该选项才会以驻留模式写出时间线;测试据此判断stringsShouldBeInterned并切换断言逻辑;字典维护(Deinterner.update)。测试中自定义的Deinterner类在 第 29~57 行 对每个TracePacket先检查packet.sequenceFlags TracePacket_SequenceFlags.SEQ_INCREMENTAL_STATE_CLEARED:命中则清空四张 iid→字符串映射(debugAnnotationNames、debugAnnotationStringValues、eventNames、eventCategories),随后若packet.hasInternedData()成立,就把internedData里各条目的iid.toInt()作为键登记——这正是 proto 注释所述周期性重置语义在读者端的实现;事件还原(Deinterner.deintern)。第 60~90 行 处理单个TrackEvent:若event.hasNameIid()为真,则用eventNames[event.nameIid.toInt()]回填event.name并event.clearNameIid();若event.categoryIids非空,则逐 iid 查eventCategories映射后写回event.categories;对debugAnnotations中的每个DebugAnnotation同样以stringValueIid/stringValueIid完成还原。断言还验证了互斥不变量:驻留模式下hasNameIid()成立时hasName()必为假——这恰好是 2.0.0 在TrackEvent/DebugAnnotation上补充has*/clear*方法所服务的判断场景。从该测试的结构可以推断,任何自建 Perfetto 数据消费方(性能工具、DevTools 类前端)都应采用同样的包级字典 iid 查表还原模式,且必须处理SEQ_INCREMENTAL_STATE_CLEARED重置,否则在包丢失场景下会读到过期字典项。生成管线:proto 如何变成包内 Dart 代码vm_service_protos内所有.pb.dart均为生成文件,文件头(以 interned_data.pb.dart 第 5~11 行 为例)统一声明:This file should only ever be modified by modifying the corresponding .proto file and then runningdart third_party/perfetto/tools/compile_perfetto_protos.dartfrom the SDK root directory。完整的再生成流程在 third_party/perfetto/README.md 中说明:由于 Perfetto 的protozero_plugin(protoc 插件)不以二进制分发,需先从 Perfetto 源码树 out-of-tree 构建(README 给出了gn args/ninja -C out/linux protoc protozero_plugin的步骤,且需将 Perfetto 检出到本仓库 DEPS 文件指定的 revision);然后设置PATH指向构建产物并运行dart third_party/perfetto/tools/compile_perfetto_protos.dart,一次性生成 C 侧.pbzero.{cc,h}与 Dart 侧.pb.dart/.pbenum.dart/.pbjson.dart。该 README 还解释了仓库中 proto 为何是手工裁剪版(interned_data.proto 第 17~18 行的注释也写明 This is a manually minified version of Perfettos interned_data.proto):C 侧使用 header-only 的 protozero,裁剪与否对 VM 代码体积几乎无影响;但 Dart 侧不同,若采用 Perfetto 原版完整消息定义,会把大量消费方(如 DevTools)根本用不到的生成代码打进包中,而每个消息与其子消息强关联、默认无法被 tree-shake。因此仓库选择人工裁剪的最小 proto 集,这也决定了 2.0.0 新增的EventCategory/EventName/DebugAnnotationName等类型只包含 VM 实际用到的字段。此外,proto 头部注释要求对 proto 的任何改动都必须重新生成对应的.pbzero.h,因为 VM 的 C 序列化端(如 builtin_clock.pbzero.h 等)与 Dart 解码端必须共享同一份消息定义,两端字段编号必须严格一致——这也是 interned_data.proto 与 interned_data.pb.dart 中字段编号(1/2/3/5/6/7/16/17/19/29)完全吻合的原因。小结与参考路径vm_service_protos的版本史很短但每步都有明确动机:1.0.0 建立 Perfetto 解码能力,2.0.0 补齐字符串驻留的完整读取面(三个驻留条目类、InternedData四个字典 getter、DebugAnnotation/TrackEvent的 iid getter 与 has/clear 方法,并提升 protobuf 依赖下限),2.0.1 则维护对 protobuf 5.x 系列的兼容。当前仓库检出的 2.0.1 版本配套 Dart SDK^3.5.0与protobuf ^6.0.0约束,且属于 SDK pub workspace。继续深入时可查阅:版本日志:pkg/vm_service_protos/CHANGELOG.md包说明与用法指引:pkg/vm_service_protos/README.md、pkg/vm_service_protos/pubspec.yaml公开导出:pkg/vm_service_protos/lib/vm_service_protos.dart驻留字典 proto 与生成代码:third_party/perfetto/protos/perfetto/trace/interned_data/interned_data.proto、pkg/vm_service_protos/lib/src/protos/perfetto/trace/interned_data/interned_data.pb.dartproto 裁剪动机与再生成流程:third_party/perfetto/README.md、third_party/perfetto/tools/compile_perfetto_protos.dart官方用法示例(时间线与 CPU 采样解码):pkg/vm_service/test/get_perfetto_vm_timeline_rpc_test.dart、pkg/vm_service/test/get_perfetto_cpu_samples_rpc_test.dart赞分享编程语言编译器语言运行时标准库开发工具【免费下载链接】sdkThe Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.项目地址https://gitcode.com/gh_mirrors/sdk1/sdk点击查看免费下载相关推荐Dart SDK 中 analysis_server_plugin 包的版本演进与插件机制深度解析Dart SDK 中 analysis_server_plugin 包的版本演进与插件机制深度解析 analysis_server_plugin 是 Dart编程语言编译器语言运行时标准库开发工具PHP 引擎的 zend_string 深度解析结构布局、API 与字符串驻留机制PHP 引擎的 zend_string 深度解析结构布局、API 与字符串驻留机制 PHP 解释器php src中所有字符串——变量值、数组键、类名、属性编程语言语言运行时解释器wtfpython源码解读深入理解CPython的字符串驻留机制wtfpython源码解读深入理解CPython的字符串驻留机制 引言Python字符串的诡异行为 你是否曾经遇到过这样的Python代码行为 pyt文档教程上一篇BindingX革命性跨平台动作绑定引擎轻松实现流畅交互体验下一篇KMS_VL_ALL_AIO使用指南轻松管理Windows和Office授权创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考