根和堆写危害的静态分析¶
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(堆写危害)。