| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> JavaScript知识库 -> 大厂程序员的调试技巧,偷学到了! -> 正文阅读 |
|
[JavaScript知识库]大厂程序员的调试技巧,偷学到了! |
最近在研究 WebAssembly,也写了几篇全面介绍的文章: 本文是学习 WebAssembly 系列的第三篇文章,也是想探究一下 Chrome 开发者工具对 WebAssembly 的调试支持度如何,通过这个探究的过程,我们会了解到 Chrome 调试工具各种方面的使用方法以及作用,发掘你可能不知道的一些知识点。 所以本文既可以当做学习使用 Chrome Devtools 调试工具的一篇比较全面的文章,也可以当做是介绍现阶段我们如何在浏览器中对 WebAssembly 相关的代码进行调试,帮助你成为一个合格的调试工程师 :)。 WebAssembly 的原始调试方式Chrome 开发者工具目前已经支持 WebAssembly 的调试,虽然存在一些限制,但是针对 WebAssembly 的文本格式的文件能进行单个指令的分析以及查看原始的堆栈追踪,具体见如下图: 上述的方法对于一些无其他依赖函数的 WebAssembly 模块来说可以很好的运行,因为这些模块只涉及到很小的调试范围。但是对于复杂的应用来说,如 C/C++ 编写的复杂应用,一个模块依赖其他很多模块,且源代码与编译后的 WebAssembly 的文本格式的映射有较大的区别时,上述的调试方式就不太直观了,只能靠猜的方式才能理解其中的代码运行方式,且大多数人很难以看懂复杂的汇编代码。 更加直观的调试方式现代的 JavaScript 项目在开发时通常也会存在编译的过程,使用 ES6 进行开发,编译到 ES5 及以下的版本进行运行,这个时候如果需要调试代码,就涉及到 Source Map 的概念,source map 用于映射编译后的对应代码在源代码中的位置,source map 使得客户端的代码更具可读性、更方便调试,但是又不会对性能造成很大的影响。 而 C/C++ 到 WebAssembly 代码的编译器 Emscripten 则支持在编译时,为代码注入相关的调试信息,生成对应的 source map,然后安装 Chrome 团队编写的 C/C++ Devtools Support 浏览器扩展,就可以使用 Chrome 开发者工具调试 C/C++ 代码了。 这里的原理其实就是,Emscripten 在编译时,会生成一种 DWARF 格式的调试文件,这是一种被大多数编译器使用的通用调试文件格式,而 C/C++ Devtools Support 则会解析 DWARF 文件,为 Chrome Devtools 在调试时提供 source map 相关的信息,使得开发者可以在 89+ 版本以上的 Chrome Devtools 上调试 C/C++ 代码。 调试简单的 C 应用因为 DWARF 格式的调试文件可以提供处理变量名、格式化类型打印消化、在源代码中执行表达式等等,现在就让我们实际来编写一个简单的 C 程序,然后编译到 WebAssembly 并在浏览器中运行,查看实际的调试效果吧。 首先让我们进入到之前创建的 WebAssembly 目录下,激活 emcc 相关的命令,然后查看激活效果:
接着在 WebAssembly 创建一个
上述代码在执行 在终端切换目录到
上述命令在普通的编译形式上,加入了 现在可以开启一个 HTTP 服务器,可以使用
为了查看调试效果,需要设置一些内容。
设置完之后,在工具栏顶部会出现一个 Reload 的蓝色按钮,需要重新加载配置,点击一下就好。
可以看到,我们成功在 Chrome Devtools 里面查看了 C 代码,并且代码停在了 如上述可以查看 查看复杂类型值实际上 Chrome Devtools 不仅可以查看原 C/C++ 代码中一些变量的普通类型值,如数字、字符串,还可以查看更加复杂的结构,如结构体、数组、类等内容,我们拿另外一个例子来展现这个效果。 我们通过一个在 C++ 里面绘制 曼德博图形 的例子来展示上述的效果,同样在 WebAssembly 目录下创建
上述代码差不多 50 行左右,但是引用了两个 C++ 标准库:SDL 和 complex numbers ,这使得我们的代码变得有一点复杂了,我们接下来编译上述代码,来看看 Chrome Devtools 的调试效果如何。 通过在编译时带上
同样使用 打开开发者工具,然后可以搜索到 我们可以在第一个 for 循环里面的 使用 Scope 面板我们可以看到复杂类型如 直接在程序中查看同时将鼠标移动到 在控制台中使用同时在控制台里面也可以通过输入变量名获取到值,依然可以查看复杂类型: 还可以对复杂类型进行取值、计算相关的操作: 使用 watch 功能我们也可以把使用调试面板里面的 watch 功能,添加 for 循环里面的 i 到 watch 列表,然后恢复程序执行就可以看到 i 的变化: 更加复杂的步进调试我们同样可以使用另外几个调试工具:step over、step in、step out、step 等,如我们使用 step over,向后执行两步: 可以查看到当前步的变量值,也可以在 Scope 面板中看到对应的值。 针对非源码编译的第三方库进行调试在之前我们只编译了 如我们在 41 行,SDL_SetRenderDrawColor 调用处打上断点,并使用 step in 进入到函数内部: 会变成如下的形式: 我们又回到了原始的 WebAssembly 的调试形式,这也是难以避免的一种情况,因为我们在开发过程中可能会遇到各种第三方库,但是我们并不能保证每个库都能从源码编译而来且带上了类似 DWARF 的调试信息,绝大部分情况下我们无法控制第三方库的行为;而另外一种情况则是有时我们会在生产情况下遇到问题,而生产环境也是没有调试信息的。 上述情况暂时还没有比较好的处理方法,但是开发者工具却改进了上述的调试体验,将所有的代码都打包成单一的 WebAssembly 文件,对应到我们这次就是 新的命名生成策略之前的调试面板里面,针对 WebAssembly 只有一些数字索引,而对于函数则连名字都没有,如果没有必要的类型信息,那么很难追踪到某个具体的值,因为指针将以整数的形式展示出来,但你不知道这些整数背后存储着什么。 新的命名策略参考了其他反汇编工具的命名策略,使用了 WebAssembly 命名策略部分的内容、import/export 的路径相关的内容,可以看到我们现在的调试面板中针对函数可以展示函数名相关的信息: 即使遇到了程序错误,基于语句的类型和索引也可以生成类似 查看内存面板如果想要调试此时程序占用的内存相关的内容,可以在 WebAssembly 的上下文下,查看 Scope 面板里的 或者点击 可以打开内存面板: 从内存面板里面可以查看以十六进制或 ASCII 的形式查看 WebAssembly 的内存,导航到特定的内存地址,将特定数据解析成各种不同的格式,如十六进制 65 代表的 e 这个 ASCII 字符。 对 WebAssembly 代码进行性能分析因为我们在编译时为代码注入了很多调试信息,运行的代码是未经优化且冗长的代码,所以运行时会很慢,所以如果为了评估程序运行的性能,你不能使用 所以如果需要对代码进行性能分析,你需要使用开发者工具提供的性能面板,性能面板里面会全速运行代码,并且提供不同函数执行时花费时间的明确断点信息: 可以看到上述几个比较典型的时间点如 161ms,或者 461ms 的 LCP 与 FCP ,这些都是能反应真实世界下的性能指标。 或者你可以在加载网页时关闭控制台,这样就不会涉及到调试信息等相关内容的调用,可以确保比较真实的效果,等到页面加载完成,然后再打开控制台查看相关的指标信息。 在不同的机器上进行调试当在 Docker、虚拟机或者其他原创服务器上进行构建时,你可能会遇到那种构建时使用的源文件路径和本地文件系统上的文件路径不一致,这会导致开发者工具在运行时可以在 Sources 面板里展示出有这个文件,但是无法加载文件内容。 为了解决这个问题,我们需要在之前安装的 C/C++ Devtools Support 配置里面设置路径映射,点击扩展的 “选项”: 然后添加路径映射,在 old/path 里填入之前的源文件构建时的路径,在 new/path 里填入现在存在本地文件系统上的文件路径: 上述映射的功能和一些 C++ 的调试器如 GDB 的 调试优化性构建的代码如果你想调试一些在构建时进行优化后的代码,可能会获得不太理想的调试体验,因为进行优化构建时,函数内联在一起,可能还会对代码进行重排序或去除一部分无用的代码,这些都可能会混淆调试者。 目前开发者工具除了对函数内联时不能搞很好的支持外,能够支持绝大部分优化后代码的调试体验,为了减少函数内联支持能力欠缺带来的调试影响,建议在对代码进行编译时加入
将调试信息单独存储调试信息包含代码的详细信息,定义的类型、变量、函数、函数作用域、以及文件位置等任何有利于调试器使用的信息,所以通常调试信息比源代码还要大。 为了加速 WebAssembly 模块的编译和加载速度,你可以在编译时将调试信息拆分成独立的 WebAssembly 文件,然后单独加载,为了实现拆分单独文件,可以在编译时加入
进行上述操作之后,编译之后的主应用代码只会存储一个 如果我们想同时进行优化构建,并将调试信息单独拆分,并在之后需要调试时,加载本地的调试文件进行调试,在这种场景下,我们需要重载调试文件存储的地址来帮助插件能够找到这个文件,可以运行如下命令来处理:
在浏览器中调试 ffmpeg 代码通过这篇文章我们深入了解了如何在浏览器中调试通过 Emscripten 构建而来的 C/C++ 代码,上述讲解了一个普通无依赖的例子以及一个依赖于 C++ 标准库 SDL 的例子,并且讲解了现阶段调试工具可以做的事情和限制,接下来我们就通过学到的知识来了解如何在浏览器中调试 ffmpeg 相关的代码。 带上调试信息的构建我们只需要修改在之前的文章中提到的构建脚本
然后以此执行其他操作,最后通过 可以看到,我们在 Sources 面板里面可以搜索到构建后的 然后在网页中上传一个 可以发现,我们依然可以像之前一样在程序中鼠标移动上去查看变量值,以及在右侧的 Scope 面板里查看变量值,以及可以在控制台中查看变量值。 类似的,我们也可以进行 step over、step in、step out、step 等复杂调试操作,或者 watch 某个变量值,或查看此时的内存等。 可以看到通过这篇文章介绍的知识,你可以在浏览器中对任意大小的 C/C++ 项目进行调试,并且可以使用目前开发者工具提供的绝大部分功能。 参考链接
??/ 感谢支持 /以上便是本次分享的全部内容,希望对你有所帮助_ 喜欢的话别忘了 分享、点赞、收藏 三连哦~ 欢迎关注公众号 程序员巴士,来自字节、虾皮、招银的三端兄弟,分享编程经验、技术干货与职业规划,助你少走弯路进大厂。 |
|
JavaScript知识库 最新文章 |
ES6的相关知识点 |
react 函数式组件 & react其他一些总结 |
Vue基础超详细 |
前端JS也可以连点成线(Vue中运用 AntVG6) |
Vue事件处理的基本使用 |
Vue后台项目的记录 (一) |
前后端分离vue跨域,devServer配置proxy代理 |
TypeScript |
初识vuex |
vue项目安装包指令收集 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 1:33:15- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |