根和堆写危害的静态分析¶
Treeherder 可以运行两种静态分析构建:完整浏览器 (linux64-haz) 和仅 JS shell (linux64-shell-haz)。它们在 Treeherder 上显示为 H
和 SM(H)
。
诊断危害故障¶
第一步是查看报告了哪种类型的危害。有两种类型的危害会导致作业失败:垃圾回收的堆栈根危害和 stylo 的堆写线程安全危害。
摘要输出将包含字符串 <N> rooting hazards detected
或 <N> heap write hazards detected out of <M> allowed
。请参阅下面每个部分的相应内容。
诊断根危害故障¶
点击 H
构建链接,选择左下角的“工件”窗格,然后下载 public/build/hazards.txt.gz
和 public/build/hazards.html.gz
文件。在本地运行分析时,HTML 文件最有用,因为它会链接到相关代码的确切部分,但在本文中讨论文本文件更容易。
hazards.txt
中的示例片段
Function 'jsopcode.cpp:uint8 DecompileExpressionFromStack(JSContext*, int32, int32, class JS::Handle<JS::Value>, int8**)' has unrooted 'ed' of type 'ExpressionDecompiler' live across GC call 'uint8 ExpressionDecompiler::decompilePC(uint8*)' at js/src/jsopcode.cpp:1866
js/src/jsopcode.cpp:1866: Assume(74,75, !__temp_23*, true)
js/src/jsopcode.cpp:1867: Assign(75,76, return := 0)
js/src/jsopcode.cpp:1867: Call(76,77, ed.~ExpressionDecompiler())
GC Function: uint8 ExpressionDecompiler::decompilePC(uint8*)
JSString* js::ValueToSource(JSContext*, class JS::Handle<JS::Value>)
uint8 js::Invoke(JSContext*, JS::Value*, JS::Value*, uint32, JS::Value*, class JS::MutableHandle<JS::Value>)
uint8 js::Invoke(JSContext*, JS::CallArgs, uint32)
JSScript* JSFunction::getOrCreateScript(JSContext*)
uint8 JSFunction::createScriptForLazilyInterpretedFunction(JSContext*, class JS::Handle<JSFunction*>)
uint8 JSRuntime::cloneSelfHostedFunctionScript(JSContext*, class JS::Handle<js::PropertyName*>, class JS::Handle<JSFunction*>)
JSScript* js::CloneScript(JSContext*, class JS::Handle<JSObject*>, class JS::Handle<JSFunction*>, const class JS::Handle<JSScript*>, uint32)
JSObject* js::CloneStaticBlockObject(JSContext*, class JS::Handle<JSObject*>, class JS::Handle<js::StaticBlockObject*>)
js::StaticBlockObject* js::StaticBlockObject::create(js::ExclusiveContext*)
js::Shape* js::EmptyShape::getInitialShape(js::ExclusiveContext*, js::Class*, js::TaggedProto, JSObject*, JSObject*, uint32, uint32)
js::Shape* js::EmptyShape::getInitialShape(js::ExclusiveContext*, js::Class*, js::TaggedProto, JSObject*, JSObject*, uint64, uint32)
js::UnownedBaseShape* js::BaseShape::getUnowned(js::ExclusiveContext*, js::StackBaseShape*)
js::BaseShape* js_NewGCBaseShape(js::ThreadSafeContext*) [with js::AllowGC allowGC = (js::AllowGC)1u]
js::BaseShape* js::gc::NewGCThing(js::ThreadSafeContext*, uint32, uint64, uint32) [with T = js::BaseShape; js::AllowGC allowGC = (js::AllowGC)1u; size_t = long unsigned int]
void js::gc::RunDebugGC(JSContext*)
void js::MinorGC(JSRuntime*, uint32)
GC
这意味着在 js/src/jsopcode.cpp
的第 1866 行,在函数 DecompileExpressionFromStack
中发现了根危害(它以文件名作为前缀,因为它是一个静态函数)。问题在于,有一个未根化的变量 ed
持有一个 ExpressionDecompiler
,该变量在调用 decompilePC
期间保持活动状态。“活动”表示变量在调用 decompilePC
返回后被使用。decompilePC
可能会根据从以“GC Function:
”开头的行开始给出的静态调用堆栈触发 GC。
危害本身有一些几乎无法理解的 Assume(...)
和 Call(...)
乱码,描述了变量进入函数调用的确切数据流路径。这些内容很少有用——通常,只有在它抱怨临时变量并且想知道临时变量来自哪里时,才需要查看它。类型 ExpressionDecompiler
被认为持有指向某种 GC 控制对象的指针。当前分析不会描述它担心的确切字段。
为了稍微解释一下,分析表示以下情况可能会发生
ExpressionDecompiler
包含指向某个 GC 对象的指针。例如,它可能有一个类型为JSObject*
的字段obj
。(在hazardIntermediates.tar.xz
内部的gcTypes.txt
文件中,将提供所有类型的详细说明。)DecompileExpressionFromStack
被调用。一个指针存储在
ed
变量的该字段中。decompilePC
被调用,它调用ValueToSource
,后者调用Invoke
,最终调用js::MinorGC
在由此产生的垃圾回收期间,
ed.obj
指向的对象被移动到另一个位置。JS 堆中存储的所有指针都会自动更新,根指针也是如此。ed.obj
不会更新,因为 GC 不知道它。decompilePC
返回后,某些内容访问ed.obj
。这现在是一个陈旧的指针,可能指向任何东西——错误的对象、无效的对象或任何其他东西。正如 TeX 所说,**糟糕程度 10000**。
诊断堆写危害故障¶
已过时:堆写危害分析已多年未更新,并且正在查找不再存在的事物,因此始终会报告零问题。
对于线程不安全的堆写分析,危害意味着某些 Gecko_* 函数直接或间接调用了写入堆上某些内容的代码,或者调用了可能写入堆上某些内容的未知函数。该分析需要相当多的注释来描述实际上安全的方面。随着我们获得更多分析经验,本节将进行扩展,但以下是一些常见问题
添加新的 Gecko_* 函数:通常,需要在
js/src/devtools/rootAnalysis/analyzeHeapWrites.js
中的treatAsSafeArgument
函数中注释任何 outparams 或拥有(线程本地)的参数。调用某些 libc 函数:如果添加对某些随机 libc 函数的调用(例如
sin()
或floor()
或ceil()
,尽管后两个函数已被注释),分析将报告“外部函数”。将其添加到checkExternalFunction
中,假设它没有可能写入共享堆内存。如果调用分析不知道的某些不返回(崩溃)的函数,则需要将其添加到
ignoreContents
中。
另一方面,您可能遇到了真正的线程安全问题。共享缓存是常见的问题。修复它。
分析实现¶
这些构建执行以下操作
设置构建环境并在其中运行分析,然后上传结果文件
编译优化的 JS shell 以供稍后运行分析
使用 gcc 编译浏览器,使用六鳃鲨(http://svn.sixgill.org)gcc 插件的略微修改版本
生成一组描述编译过程中遇到的所有内容的
.xdb
文件使用
js/src/devtools/rootAnalysis
中的脚本分析.xdb
文件
存储在这些文件中的信息的格式已有一些文档记录。
运行分析¶
推送到 Try¶
运行分析最简单的方法是使用 mach try fuzzy -q "'haz"
推送到 Try(或者,如果感兴趣的危害完全包含在 js/src
中,请使用 mach try fuzzy -q "'shell-haz"
以获得更快的结果)。linux64-haz 的预期周转时间略低于 1.5 小时(hazard-linux64-shell-haz
大约 20 分钟)。
输出将被上传,并且输出文件 hazards.txt.xz
将被放置到 Treeherder 上的“工件”信息窗格中。
在本地运行¶
所以您通过添加危害破坏了分析。现在怎么办?¶
回退、修复危害,或(最后手段)更新 js/src/devtools/rootAnalysis/expect.browser.json
中的预期危害数量(但不要这样做)。
修复危害最常见的方法是将变量更改为 Rooted
类型,如 RootingAPI.h 中所述
对于更复杂的情况,请在 Matrix 频道上询问(有关联系信息,请参阅 spidermonkey.dev)。如果您没有收到回复,请 ping sfink 或 jonco(根危害),bholley 或 sfink(堆写危害)。