使用现代工具调试WebAssembly

2020-12-11 07:53:19

我们展示了基本的步进支持,并讨论了DWARF信息的机会使用,而不是将来为我们打开源地图:

今天,我们很高兴展示承诺的功能实现,以及Emscripten和Chrome DevTools团队在这一年中取得的进步,尤其是在C和C ++应用程序方面。

在开始之前,请记住,这仍然是新体验的测试版,您需要自担风险使用所有工具的最新版本,如果遇到任何问题,请将其报告给https:// bugs .chromium.org / p / chromium / issues / entry?template = DevTools + issue。

#include< stdlib.h>无效assert_less(int x,int y){if(x> = y){abort(); }} int main(){assert_less(10,20); assert_less(30,20);}

要对其进行编译,我们就像在原始文章中一样,使用LatestEmscripten并传递-g标志以包含debuginformation:

现在,我们可以从本地HTTP服务器(例如,使用serve)提供生成的页面,并在最新的ChromeCanary中打开它。

这次,我们还需要一个与ChromeDevTools集成的帮助程序扩展程序,以帮助您理解WebAssembly文件中编码的所有调试信息。请通过以下链接进行安装:goo.gle/wasm-debugging-extension

您还需要在DevTools实验中启用WebAssembly调试。打开Chrome DevTools,单击DevTools窗格右上角的齿轮(⚙)图标,转到“实验”面板,然后勾选WebAssembly Debugging:启用DWARF支持。

关闭设置后,DevTools会建议您重新加载自身以应用设置,所以就这样吧。一站式服务就是这样。

现在,我们可以返回“源”面板,启用“暂停onexceptions”(⏸图标),然后选中“在捕获的异常上暂停”并重新加载页面。您应该看到DevTools因异常而暂停:

默认情况下,它在Emscripten生成的粘合代码上停止,但是在右侧,您可以看到代表错误的堆栈跟踪的“调用堆栈”视图,并且可以导航到调用中止的原始C行:

现在,如果您在“作用域”视图中查看,您可以在C / C ++代码中看到变量的原始名称和值,而不必再弄清楚像$ localN这样的错误名称的含义以及它们与您编写的源代码的关系。

这不仅适用于整数之类的原始值,而且适用于诸如结构,类,数组等的复合类型!

让我们看一个更复杂的例子来展示它们。这次,我们将使用以下C ++代码绘制Mandelbrotfractal:

#include< SDL2 / SDL.h> #include< complex> int main(){//初始化SDL。 int宽度= 600,高度= 600; SDL_Init(SDL_INIT_VIDEO); SDL_Window *窗口; SDL_Renderer *渲染器; SDL_CreateWindowAndRenderer(宽度,高度,SDL_WINDOW_OPENGL,&窗口,& renderer); //生成具有随机颜色的调色板。枚举{MAX_ITER_COUNT = 256}; SDL_调色板[MAX_ITER_COUNT]; srand(time(0));对于(int i = 0; i< MAX_ITER_COUNT; ++ i){调色板[i] = {.r =(uint8_t)rand(),.g =(uint8_t)rand(),.b =(uint8_t)rand (),.a = 255,}; } //计算并绘制Mandelbrot集。 std :: complex< double>中心(0.5,0.5);双倍= 4.0; for(int y = 0; y 2.0)中断; } SDL_Color color =调色板[i]; SDL_SetRenderDrawColor(renderer,color.r,color.g,color.b,color.a); SDL_RenderDrawPoint(renderer,x,y); }} //渲染我们绘制到画布上的所有内容。 SDL_RenderPresent(renderer); // SDL_Quit();}

您可以看到该应用程序仍然很小-它是一个包含50行代码的单个文件-但这次我还使用了一些外部API,例如SDLlibrary forgraphics以及C ++标准库中的复数。

我将使用与上面相同的-g标志进行编译,以包含调试信息,并且我还将要求Emscripten提供SDL2库并允许任意大小的内存:

当我在浏览器中访问生成的页面时,可以看到带有一些随机颜色的美丽分形:

当我打开DevTools时,再次可以看到原始的C ++文件。但是,这一次,我们的代码没有错误(哇!),因此让我们在代码的开头设置一些断点。

我们已经可以在右侧看到所有变量,但是目前仅初始化了width和height,因此检查的内容并不多。

让我们在主Mandelbrot循环中设置另一个断点,然后恢复执行以向前跳过一点。

此时,我们的调色板已经填充了一些随机的颜色,我们可以扩展数组本身以及单个SDL_Color结构,并检查其组件以验证一切看起来不错(例如,始终将“ alpha”通道设置为完全不透明)。类似地,我们可以展开并检查存储在中心变量中的复数的实部和虚部。

如果要访问深层嵌套的属性(否则很难通过Scope视图访问),则也可以使用Consoleevaluation!但是,请注意,尚不支持更复杂的C ++表达式。

让我们继续执行几次,我们可以看到内部x的变化方式-再次在Scope视图中查看,将变量名添加到监视列表,在控制台中对其进行评估,或者将鼠标悬停在源代码中:

从这里,我们可以进入或进入C ++语句,并观察其他变量的变化情况:

好的,所以当调试信息可用时,这一切都很好用,但是如果我们要调试不是使用debugoptions构建的代码,那该怎么办呢?

例如,我们要求Emscripten提供一个预先构建的SDL库论坛,而不是从源代码本身进行编译,因此-至少当前-调试器无法找到关联的源代码。让我们再次进入SDL_RenderDrawColor:

现在,它看起来有点吓人,并不是大多数Web开发人员都需要处理的东西,但是有时候您可能想调试没有调试信息的库,这是因为它是您无法控制的第三方库?您会遇到仅在生产环境中发生的错误之一。

为了在这种情况下提供帮助,我们还对基本调试体验进行了一些改进。

首先,如果您以前使用过原始WebAssembly调试,则可能会注意到整个反汇编现在都显示在单个文件中-不再猜测Sources条目wasm-53834e3e / wasm-53834e3e-7可能对应的功能。

我们也在反汇编视图中改进了名称。以前,您只能看到数字索引,如果使用函数,则根本没有名称。

现在,我们通过使用来自WebAssembly名称部分的提示,导入/导出路径,以及与其他反汇编工具类似的方法来生成名称,最后,如果其他所有操作失败,则根据项的类型和索引(例如$ func123)生成它们。您可以在上面的屏幕截图中看到如何这已经有助于获得稍微可读的堆栈跟踪和反汇编。

如果没有可用的类型信息,则可能难以检查除基元以外的任何值-例如,指针将显示为规则整数,而无法知道它们背后存储的内容。

以前,您只能扩展WebAssembly内存对象(由Scope视图中的env.memory表示)来查找单个字节。这在某些琐碎的情况下可行,但是扩展起来特别不方便,并且不允许以字节值以外的格式重新解释数据。我们还添加了一个新功能来帮助解决此问题:线性内存检查器。

如果右键单击env.memory,现在应该看到一个名为“检查内存”的新选项:

单击后,它将弹出一个内存检查器,您可以在其中以十六进制和ASCII视图检查WebAssembly内存,导航到特定地址,以及解释数据的不同格式:

当您打开DevTools时,WebAssembly代码将“分层”为未优化的版本以启用调试。这个版本慢很多,这意味着在打开DevTools时,您不能依赖console.time,performance.now和其他测量代码速度的方法,因为您获得的数字根本无法代表真实的性能。 。

相反,您应该使用DevTools Performancepanel,它将以最快的速度运行代码,并为您详细分解各种功能所花费的时间:

或者,您可以在关闭DevTools的情况下运行应用程序,并在完成后将其打开以检查控制台。

将来,我们将改进分析方案,但是现在要注意。如果您想了解有关WebAssemblytiering方案的更多信息,请查看有关WebAssembly编译管道的文档。

在Docker,虚拟机或远程构建服务器中进行构建时,您可能会遇到以下情况:构建期间使用的源文件的路径与运行Chrome DevTools的文件系统中的路径不匹配。在这种情况下,文件将显示在“源”面板中,但无法加载。

为了解决此问题,我们在C / C ++扩展选项中实现了路径映射功能。您可以使用它来重新映射任意路径,并帮助DevTools查找源。

例如,如果主机上的项目位于路径C:\ src \ my_project下,但在Docker容器中构建,该路径表示为/ mnt / c / src / my_project,则可以在调试期间通过指定重新映射这些路径作为前缀:

第一个匹配的前缀“ wins”。如果您熟悉其他C ++调试器,则此选项类似于GDB中的set replace-path命令或LLDB中的target.source-map设置。

与其他任何语言一样,如果禁用了优化,则调试效果最佳。优化可能会将函数彼此内联,重新排序代码或完全删除部分代码-所有这些都有可能使调试器(因此,您是用户)感到困惑。

如果您不介意有限的调试经验,但仍想调试优化的构建,则除了函数内联之外,大多数优化都可以按预期进行。我们计划在将来解决剩余的问题,但是目前,在通过任何-O级别优化进行编译时,请使用-fno-inline禁用它,例如:

调试信息保留了有关代码,定义的类型,变量,函数,作用域和位置的许多详细信息-可能对调试器有用的任何信息。结果,它通常可以大于代码本身。

为了加快WebAssembly模块的加载和编译速度,您可能希望将此调试信息拆分为单独的WebAssembly文件。要在Emscripten中做到这一点,请传递-gseparate-dwarf =…标志并带有所需的文件名:

在这种情况下,主应用程序将仅存储文件名temp.debug.wasm,并且在打开DevTools时,helper扩展将能够找到并加载它。

当与如上所述的优化方法结合使用时,该功能甚至可以用于交付您应用程序的几乎优化的生产版本,然后使用本地边文件调试它们。在这种情况下,我们还需要覆盖存储的URL,以帮助扩展程序找到辅助文件,例如:

emcc -g temp.c -o temp.html \ -O3 -fno-inline \ -gseparate-dwarf = temp.debug.wasm \ -s SEPARATE_DWARF_URL = file:// [到temp.debug.wasm的本地路径]

有了所有这些新的集成,Chrome DevTools不仅成为JavaScript的强大功能,而且还成为C和C ++应用程序的强大调试器,比以往任何时候都更容易采用内置于各种技术中的应用程序,并将它们带到一个共享的,跨平台的平台的Web。

但是,我们的旅程尚未结束。从现在开始,我们将要进行的一些工作:

同时,请通过在您自己的代码上尝试当前的Beta并将任何发现的问题报告给https://bugs.chromium.org/p/chromium/issues/entry?template=DevTools+issue来帮助我们。

感谢您的反馈。如果您对如何改进此页面有特定的想法,请创建一个问题。

感谢您的反馈。如果您对如何改进此页面有特定的想法,请创建一个问题。

感谢您的反馈。 如果您对如何改进此页面有特定的想法,请创建一个问题。 感谢您的反馈。 如果您对如何改进此页面有特定的想法,请创建一个问题。 感谢您的反馈。 如果您对如何改进此页面有特定的想法,请创建一个问题。 感谢您的反馈。 如果您对如何改进此页面有特定的想法,请创建一个问题。 感谢您的反馈。 如果您对如何改进此页面有特定的想法,请创建一个问题。 感谢您的反馈。 如果您对如何改进此页面有特定的想法,请创建一个问题。 感谢您的反馈。 如果您对如何改进此页面有特定的想法,请创建一个问题。

感谢您的反馈。 如果您对如何改进此页面有特定的想法,请创建一个问题。 要讨论本文中的新功能和更改,或与DevTools相关的其他任何内容: 以下列出了Chrome DevTools工程博客系列中介绍的所有内容。 rss_feed订阅我们的RSS或Atom提要,并在您喜欢的提要阅读器中获取最新更新!