资讯专栏INFORMATION COLUMN

Emscripten教程之如何调试代码(六)

cod7ce / 958人阅读

摘要:值启用了更详细的堆栈保护检查,它以牺牲一些性能的代价提供更精确的。这种可以由任何类型的编码错误引起,但表现为函数指针错误,因为在运行时的预期表中无法找到函数。

翻译:云荒杯倾
本文是Emscripten-WebAssembly专栏系列文章之一,更多文章请查看专栏。
也可以去作者的博客阅读文章。
欢迎加入Wasm和emscripten技术交流群,群聊号码:939206522。

调试Emscripten代码的主要优点之一是,源代码既可以在本地平台上进行调试,也可以使用web浏览器日益强大的工具集——包括调试器、分析器和其他工具。

Emscripten提供了许多帮助调试的功能和工具:

编译器调试信息flags,允许您在已编译的代码中保存调试信息,甚至创建源映射,以便在浏览器中调试时可以单步调试c++源代码。

调试模式,它产生调试日志和存储 编译时产生的中间文件 进行分析。

编译器设置,使运行时检查内存访问和公共分配错误。

还支持手动打印调试emscripten生成代码,这在某些方面甚至比本地平台工作效果更好。

自动调试器,它会自动地使用LLVM的中间代码写到内存。

本文描述了由Emscripten提供的用于调试的主要工具和设置,以及如何调试一些Emscripten特有的问题。

调试信息

默认下,如果是优化编译,Emcc会删除大部分调试信息。Optimisation级-01和以上删除LLVM调试信息,也禁用了运行时断言检查。优化级别-02以上,代码被压缩编译器改编,变得几乎不可读。

emcc -g标志可用于在编译的输出中保存调试信息。默认情况下,此选项保护空白、函数名和变量名。

你可以使用五个级别中的一个来指定标记:-g0、-g1、-g2、-g3和-g4。每个级别都在最后编译,以在编译后的输出中逐步提供更多的调试信息。g3标志提供与-g标志相同级别的调试信息。

g4选项提供了最多的调试信息—--—它生成了源映射(source map),允许您在Firefox、Chrome或Safari浏览器的调试器中查看和调试C/C++源代码。

note:
当你既用调试flag又用优化flag时,有些优化可能会被禁掉,比如,如果你使用-O3 -g4 编译,为了给你提供足够多的调试信息,有一些-O3的优化就得禁用掉。
调试模式(EMCC_DEBUG)

EMCC_DEBUG环境变量可以用来设置启用/不启用Emscripten的调试模式:

    # Linux or Mac OS X
    EMCC_DEBUG=1 ./emcc tests/hello_world.cpp -o hello.html

    # Windows
    set EMCC_DEBUG=1
    emcc tests/hello_world.cpp -o hello.html
    set EMCC_DEBUG=0

使用EMCC_DEBUG = 1设置,emcc会产生调试输出文件,并为编译器的各个编译阶段生成中间文件。
EMCC_DEBUG= 2还为每趟JavaScript优化器遍历(pass)生成中间文件。

调试日志和中间文件输出到TEMP_DIR/emscripten_temp,其中TEMP_DIR默认在/tmp(/tmp的位置在.emscripten配置文件定义)。

可以对调试日志进行分析,以对每个步骤中所做的更改进行分析和检查。

编译器设置

Emscripten有许多可以用于调试的编译器设置。使用emcc -s选项选择这些设置,他们将覆盖任何优化标志。例如:

./emcc -01 -s ASSERTIONS=1 tests/hello_world

最重要的设置是:

ASSERTIONS=1 用于为内存分配错误启用运行时检查(例如,写入比分配更多的内存)。它还定义了Emscripten如何处理程序流中的错误。可以将值设置为ASSERTIONS=2,以便运行额外的测试。
不优化编译时,ASSERTIONS=1是默认开启的。对于优化编译的代码(-01和以上级别)它是关闭的。

SAFE_HEAP= 1增加了额外的内存访问检查,并将为诸如非内联化0(dereferencing 0)和内存对齐等问题提供清晰的错误。你也可以设置SAFE_HEAP_LOG以打印SAFE_HEAP操作。

通过STACK_OVERFLOW_CHECK =1 标记在堆栈的末尾添加一个运行时的令牌值,令牌值会在某些位置被检查,以验证用户代码是否意外地写出了堆栈的末尾。虽然溢出Emscripten堆栈不是一个安全问题(JavaScript已经被沙箱化了),但写出堆栈将会导致全局数据内存损坏和Emscripten堆中动态分配的内存碎片化,这使得应用程序以意想不到的方式失败。值STACK_OVERFLOW_CHECK = 2启用了更详细的堆栈保护检查,它以牺牲一些性能的代价提供更精确的callstack。如果ASSERTIONS= 1,STACK_OVERFLOW_CHECK默认值为2,ASSERTIONS为其他值时STACK_OVERFLOW_CHECK默认不启用。

在src/settings.js中定义了许多其他有用的调试设置。有关更多信息,请搜索“check”和“debug”关键字的文件。

emcc详细输出

用emcc -v选项编译,将-v传递给LLVM,然后在工具链上运行Emscripten的内部完整性检查。

verbose模式还能启动Emscripten的调试模式(EMCC_DEBUG)以生成编译器的各个阶段的中间文件。

手动打印调试

您还可以用printf()语句手工编写源代码,然后编译并运行代码来研究问题。

如果你对问题行有很好的了解,你可以在JavaScript添加print(新的Error().stack)代码,以得到堆栈跟踪。另外还有stackTrace(),它发出堆栈跟踪,并尝试使用c++的去除改编的函数名(如果你不想或者不需要让c++ 函数名去除改编,你可以调用jsStackTrace())。

调试打印输出甚至可以执行任意的JavaScript。例如:

    function _addAndPrint($left, $right) {
            $left = $left | 0;
            $right = $right | 0;
            //---
            if ($left < $right) console.log("l
禁止优化

有时候,编译的时候,禁用LLVM优化(llvm-opts)或禁用JavaScript优化(js-opts)是很有用的。

比如说,以下命令即允许调试信息又使用-O2优化(既llvm和js都优化),但是又明显关闭了js的优化器。

./emcc -O2 --js-opts 0 -g4 tests/hello_world_loop.cpp

这样就能产生相对于llvm优化的代码来说更易调试的js代码:

    function _main() {
            var label = 0;
            var $puts=_puts(((8)|0)); //@line 4 "tests/hello_world.c"
            return 1; //@line 5 "tests/hello_world.c"
    }
Emscripten特有问题 内存对齐问题

Emscripten内存表示假定加载和存储是对齐的。在未对齐的地址上执行正常的加载或存储可能会失败。

SAFE_HEAP可以用来显示内存对齐问题。

一般来说,最好避免不对齐的读写-----他们通常是由于未定义的行为导致的。然而,在某些情况下,它们是不可避免的—----例如,如果要移植的代码从一些预先存在的数据格式的打包结构(packed structure)中读取int。

Emscripten支持未对齐的读写,但它们要慢得多,而且必须在绝对必要时使用。执行一个不对齐的读或写你可以:

手动读取单个字节并重新构造全部值

使用emscripten_align*类型,它定义了基本类型的不对齐版本(short,int,float,double)。这些类型的所有操作都是不完全对齐的(在大多数情况下使用1个variants,这意味着没有任何对齐)。

函数指针问题

如果你的函数指针调用得到一个abort(),那么问题是在调用时,没有在预期的函数指针表中找到这个函数指针。

note:
nullFunc是函数指针表中用于填充空索引的函数(b0和b1是优化编译下它的别名)。指向无效索引的函数指针会调用这个函数,然后调用abort().

有几个可能的原因:

您的代码调用了一个从另一个类型转换来的函数指针(这是未定义的行为,但它确实发生在真实的代码中)。在优化的Emscripten输出中,每个函数指针类型都基于它的原始签名,存储在一个多带带的表中,因此您必须调用具有相同签名的函数指针以获得正确的行为(更多信息参见代码可移植性部分中的函数指针问题)。

您的代码在空指针或者dereferencing 0上调用方法。这种bug可以由任何类型的编码错误引起,但表现为函数指针错误,因为在运行时的预期表中无法找到函数。

为了调试这些问题:

使用-Werror编译。这就把警告变成了错误,这些错误可能有用,因为一些未定义行为的情况会显示警告。

使用-s ASSERTIONS=2,得到一些 关于被调用的函数指针和它的类型 有用的信息。

查看浏览器堆栈跟踪,查看错误发生的地方以及应该调用哪个函数。

使用SAFE_HEAP=1和禁用函数指针别名(aliasing_function_pointer = 0)编译。这使得错误类型的函数指针 不可能在不引起错误的情况下 调用,换句话说就是这样编译会使 错误类型的函数指针调用 一定会报错。调用命令:-s SAFE_HEAP=1 -s aliasing_function_pointer =0

aliasing_function_pointer = 0也很有用,因为它确保调用错误表中的指针地址会导致明显的错误。如果没有这样的设置,这样的调用只执行地址上的任何函数,这将很难进行调试。

死循环

无限循环导致您的页面挂起。在一段时间之后,浏览器将通知用户该页面被卡住并提供停止或关闭它的选择。

如果您的代码中有无限循环,那么找到问题代码的一个简单方法就是使用JavaScript profiler。在Firefox profiler中,如果代码进入无限循环,您将看到在profiler的末尾有一块代码重复执行相同的操作。

AutoDebugger
警告:
这个选项主要为Emscripten核心开发者提供使用。
Emscripten代码移植系列文章

Emscripten代码移植主题系列文章是emscripten中文站点的一部分内容。
本文是第三个主题第二篇文章。
第一个主题介绍代码可移植性与限制
第二个主题介绍Emscripten的运行时环境
第三个主题第一篇文章介绍连接C++和JavaScript
第三个主题第二篇文章介绍embind
第四个主题介绍文件和文件系统
第六个主题介绍Emscripten如何调试代码

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91969.html

相关文章

  • Emscripten教程代码可移植性与限制(一)

    摘要:教程之代码可移植性与限制一翻译云荒杯倾本文是专栏系列文章之一,更多文章请查看专栏。下面是正文代码可移植性与限制几乎可以编译任何可移植的代码到。如果标准机构将共享状态添加到中,支持多线程代码将成为可能。 Emscripten教程之代码可移植性与限制(一) 翻译:云荒杯倾本文是Emscripten-WebAssembly专栏系列文章之一,更多文章请查看专栏。也可以去作者的博客阅读文章。欢迎...

    yangrd 评论0 收藏0
  • Emscripten教程代码可移植性与限制(一)

    摘要:教程之代码可移植性与限制一翻译云荒杯倾本文是专栏系列文章之一,更多文章请查看专栏。下面是正文代码可移植性与限制几乎可以编译任何可移植的代码到。如果标准机构将共享状态添加到中,支持多线程代码将成为可能。 Emscripten教程之代码可移植性与限制(一) 翻译:云荒杯倾本文是Emscripten-WebAssembly专栏系列文章之一,更多文章请查看专栏。也可以去作者的博客阅读文章。欢迎...

    Yang_River 评论0 收藏0
  • Emscripten教程emcc编译命令

    摘要:优化级别越高,编译时间越长启用的。允许优化,有两个值不允许优化器允许使用优化器。规定是否单独生成一个内存初始化文件。使生成的代码能够感知命令行工具。设置一个绝对路径的白名单,以防止关于绝对路径的警告。 emcc(Emscripten Compiler Frontend)介绍 翻译:云荒杯倾本文是Emscripten-WebAssembly专栏系列文章之一,更多文章请查看专栏。也可以去作...

    LiuZh 评论0 收藏0
  • Emscripten教程emcc编译命令

    摘要:优化级别越高,编译时间越长启用的。允许优化,有两个值不允许优化器允许使用优化器。规定是否单独生成一个内存初始化文件。使生成的代码能够感知命令行工具。设置一个绝对路径的白名单,以防止关于绝对路径的警告。 emcc(Emscripten Compiler Frontend)介绍 翻译:云荒杯倾本文是Emscripten-WebAssembly专栏系列文章之一,更多文章请查看专栏。也可以去作...

    Julylovin 评论0 收藏0
  • Emscripten教程emcc编译命令

    摘要:优化级别越高,编译时间越长启用的。允许优化,有两个值不允许优化器允许使用优化器。规定是否单独生成一个内存初始化文件。使生成的代码能够感知命令行工具。设置一个绝对路径的白名单,以防止关于绝对路径的警告。 emcc(Emscripten Compiler Frontend)介绍 翻译:云荒杯倾本文是Emscripten-WebAssembly专栏系列文章之一,更多文章请查看专栏。也可以去作...

    NickZhou 评论0 收藏0

发表评论

0条评论

cod7ce

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<