在 Visual Studio Code 与微信开发者工具中调试使用 emscripten 基于 C 生成的 WASM 代码
最近在尝试将一些 C/C++、Lua 项目挪到 Web 上跑, 接触到了 emscripten. 这里会介绍下在 Visual Studio Code 与微信开发者工具中调试使用 emscripten 基于 C 生成的 WASM 代码 (WebAssembly) 的一些方法.
Emscripten 与 WebAssebmly
WebAssembly 是一种新的编码方式, 可以在现代的 Web 浏览器中运行——它是一种低级的类汇编语言, 具有紧凑的二进制格式, 可以接近原生的性能运行, 并为诸如 C/C++、C# 和 Rust 等语言提供编译目标, 以便它们可以在 Web 上运行. 它也被设计为可以与 JavaScript 共存, 允许两者一起工作. –来自 MDN
Emscripten 基于大名鼎鼎的 LLVM 提供了 C/C++ 生态下的编译工具链, 可以很方便的将 C/C++ 项目编译到 WASM, 然后放到 JS 环境 (Web、微信小程序/游戏、nodejs 等) 执行.
有很多著名的 C/C++ 生态下的工具通过它移植到了现代浏览器 (chrome、firefox 等) 中执行.
Emscripten 官方提供了 emsdk 可以很方便的我们管理多个版本的编译工具链.
vscode 中调试 WebAssembly 的基本方法
现在在 vscode 中调试 WebAssembly 还是很方便的, 巨硬 (Microsoft) 在 2023 年就开发好了一个 vscode 插件去做支持, 见: WebAssembly DWARF Debugging.
请确保你已经安装并启用了该扩展插件. 同时安装好了 Emscripten 相关编译工具链, 这里我们使用的版本如下:
❯ emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 3.1.74 (1092ec30a3fb1d46b1782ff1b4db5094d3d06ae5)
clang version 20.0.0git (https:/github.com/llvm/llvm-project 322eb1a92e6d4266184060346616fa0dbe39e731)
Target: wasm32-unknown-emscripten
Thread model: posix
InstalledDir: /home/yeshan333/workspace/github/emsdk/upstream/bin
这里我们已简单的单个 fib.c
文件的调试为例, fib.c
的内容如下:
#include <stdio.h>// 递归方法
int fibonacci_recursive(int n) {if (n <= 1)return n;return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2);
}// 迭代方法
int fibonacci_iterative(int n) {if (n <= 1)return n;int a = 0, b = 1, c;for(int i = 2; i <= n; ++i){c = a + b;a = b;b = c;}return b;
}int main() {int number = 10;printf("Recursive Fibonacci of %d is %d\n", number, fibonacci_recursive(number));printf("Iterative Fibonacci of %d is %d\n", number, fibonacci_iterative(number));return 0;
}
我们先通过 emcc 将 C 代码编译出 WASM, 如下:
emcc -v fib.c -o fib.html
上述命令执行完成后, 会生成三个文件: fib.wasm
、fib.html
、fib.js
, 如果我们通过浏览器访问 fib.html
, 可以在浏览器的调试控制台 (F12) 看到对应的斐波那契数的输出.
fib.html
是 emscripten 生成的演示页面, 背后会调用fib.js
胶水层代码, 加载生成的 WEbAssembly 并执行对应 C 代码中的 main 函数. 具体原理可以查看源码并了解相关知识去理解.
如果本地环境安装有 Node.js. 那么我们也可以通过 node 执行胶水层代码 fib.js
, 结果如下:
❯ node fib.js
Recursive Fibonacci of 10 is 55
Iterative Fibonacci of 10 is 55
接下来我们将演示通过 vscode 的 debugger 调试器在 C 文件和 JS 文件中打断点调试生成的 WASM & 胶水层 JS 代码, 实现单步调试.
nodejs 中调试演示
这里我们使用如下 launch.json
配置去调试 fib.js
:
{// Use IntelliSense to learn about possible attributes.// Hover to view descriptions of existing attributes.// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387"version": "0.2.0","configurations": [{"type": "node","request": "launch","name": "WASM Debug","skipFiles": ["<node_internals>/**"],"program": "${workspaceFolder}/fib.js"}]
}
vscode 下的 C 代码断点调试需要依赖 DWARF 调试信息 (注: 如果没有调试信息, 我们只能调试生成的 js 代码, 而不能直接在 C 中打断点), 我们使用 emcc 的 -g 编译参数, 让生成的 wasm 带上调试信息. 我们先通过如下命令编译 C 文件:
emcc -g -v fib.c -o fib.html
在 run()
函数处打一个断点, 然后在 fib.c
中 main 函数的两个 printf 中各打一个断点, 使用 F5 启动调试器即可开始调试. 演示 (GIF 加载可能稍久):
https://img-home.csdnimg.cn/images/20230724024159.png?be=1&origin_url=https://pub-a8b9801c20ad491b964fc0e49c81cdb7.r2.dev/debug_in_nodejs.gif
连接到浏览器进行调试
区别于上一小节中提到的 Node.js 环境下的调试方法, vscode 会负责启动 node 执行 fib.js
. 这里介绍的 vscode 结合浏览器的调试方法, WASM 和 JS 代码将由浏览器负责执行, 我们使用 vscode 的 task 让 vscode 帮我们启动浏览器.
我们使用的 vscode launch.json
调试配置如下:
{// Use IntelliSense to learn about possible attributes.// Hover to view descriptions of existing attributes.// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387"version": "0.2.0","configurations": [{"name": "Launch Chrome (fib.html)","type": "chrome","request": "launch","url": "http://127.0.0.1:3000/fib.html","preLaunchTask": "StartHTTPServer",},]
}
"preLaunchTask": "StartHTTPServer"
说明会在调试开始前, 先执行一个名为 StartHTTPServer
的 vscode task. task 的配置同样可以放置于 .vscode 目录的 tasks.json
中
tasks.json
配置如下, 这里会使用到微软提供的插件 Live Preview, 它会帮我们起一个 HTTP Server 去托管 HTML 文件 (fib.html
):
{"version": "2.0.0","tasks": [{"label": "StartHTTPServer","type": "process","command": "${input:StartHTTPServer}"}],"inputs": [{"id": "StartHTTPServer","type": "command","command": "livePreview.runServerLoggingTask"}]
}
我们延用之前打断点的位置: 在 run()
函数处打一个断点, 然后在 fib.c
中 main 函数的两个 printf 中各打一个断点. 启动配置好的调试配置.
注: 编译命令仍然是:
emcc -g -v fib.c -o fib.html
演示 (GIF 加载可能稍久):
https://img-home.csdnimg.cn/images/20230724024159.png?be=1&origin_url=https://pub-a8b9801c20ad491b964fc0e49c81cdb7.r2.dev/debug_in_chrome.gif
F5 启动调试后, 会有一个 chrome 浏览器调试窗口被拉起, 在 vscode 编译器可以观察到, 断点能正常执行. 于此同时, 我们也可以在浏览器开发者工具的 Debugger 中观察到断点的执行.
如果你细心观察可以看到, 调试器执行到 C 文件时, 区别于 vscode 编辑器会跳转到对应的 C 代码行, chrome 浏览器开发者工具跳转的却是 wasm 文本格式代码, 这个问题我们可以在编译的时候生成 wasm 文件的 source-map 去解决, 编译命令如下:
# 确保生成的 source-map 文件 fib.wasm.map 能在 --source-map-base 指定的 HTTP Server 中找到
emcc -g -v fib.c -o fib.html -gsource-map --source-map-base=http://localhost:3000/
此后, 重新启动调试器, 我们也可以在浏览器的开发者工具中观察到随着调试的执行, 可以正确跳到被打断点的对应 C 代码行, 而不是对应的 wasm 文本表示格式中的代码行, 浏览器会自动读取 source-map 文件找到对应代码文件的位置.
演示 (GIF 加载可能稍久):
https://img-home.csdnimg.cn/images/20230724024159.png?be=1&origin_url=https://pub-a8b9801c20ad491b964fc0e49c81cdb7.r2.dev/debug_in_chrome_with_sourcemap.gif
微信开发者工具中的调试
现在有很多的基于 C/C++ 写的游戏移植到了微信平台上, 基于上文浏览器的调试方法, 我们可以在微信开发者工具中达到类似的效果, 在 C 中打断点, 进行小程序/小游戏项目的调试. 我创建了一个小型项目, 可以将其导入 微信的开发者工具 进行尝试. 快速尝试下:
git clone https://github.com/yeshan333/emcc_playground.gitcd emcc_playground/debug-blogpost# 编译出 wasm
emcc -g -v unalign.cc -o unalign.html -gsource-map --source-map-base=http://localhost:3000/ -sSAFE_HEAP=1# 启动一个 http server 将 debug-blogpost 目录暴露出去, 使用的地址端口需要与 --source-map-base 中指定的一致, 方便微信开发者工具读取
npx serve .
微信开发者工具打开 debug-blogpost 目录, 打开调试器, 在 Sources -> Page --> localhost:3000 出能看到对应的 C 文件, 并且可以使用 debugger 打断点:
演示(Windows + 微信开发者工具预发布版 RC Build (1.06.2412031) + Wechat Lib:3.7.2, 2024.12.23 10:35:40): (GIF 加载可能稍久)
https://img-home.csdnimg.cn/images/20230724024159.png?be=1&origin_url=https://pub-a8b9801c20ad491b964fc0e49c81cdb7.r2.dev/debug_in_wechatdev.gif
注意
Node.js 环境目前尚未支持读取 WebAssembly 的 source-map, 编译出的 wasm 即便带了 DWARF 调试信息, 堆栈只能看到符号, 看不到 C 符号对应的源文件, 例如有这样
一个 C++ 文件 unalign.cc
:
// https://github.com/3dgen/cppwasm-book/blob/master/wasm-in-action-book-examples/ch5/02/unaligned.cc
#ifndef EM_PORT_API
# if defined(__EMSCRIPTEN__)
# include <emscripten.h>
# if defined(__cplusplus)
# define EM_PORT_API(rettype) extern "C" rettype EMSCRIPTEN_KEEPALIVE
# else
# define EM_PORT_API(rettype) rettype EMSCRIPTEN_KEEPALIVE
# endif
# else
# if defined(__cplusplus)
# define EM_PORT_API(rettype) extern "C" rettype
# else
# define EM_PORT_API(rettype) rettype
# endif
# endif
#endif#include <stdio.h>
#include <malloc.h>
#include <memory.h>
#include <stdint.h>struct ST {uint8_t c[4];float f;
};void throw_unalign_err() {printf("Hello, World!\n");char *buf = (char*)malloc(100);ST *pst = (ST*)(buf + 2);pst->c[0] = pst->c[1] = pst->c[2] = pst->c[3] = 123;pst->f = 3.14f;printf("c[0]:%d,c[1]:%d,c[2]:%d,c[3]:%d,f:%f\n",pst->c[0], pst->c[1], pst->c[2], pst->c[3], pst->f);free(buf);
}int main() {throw_unalign_err();return 0;
}
使用 emcc 编译它, 命令如下:
emcc -g -v unalign.cc -o unalign.html -gsource-map --source-map-base=http://localhost:3000/ -sSAFE_HEAP=1
然后使用 Node.js 执行编译出来的胶水文件, 会得到类似下面的结果, 有一个内存对齐错误, 堆栈上可以看到问题出现在 throw_unalign_err
. 但我们看不到符号对应的源文件.
❯ node --enable-source-maps unalign.js
Hello, World!
Aborted(alignment fault)
/home/yeshan333/workspace/playground/emcc_playground/debug-blogpost/unalign.js:613/** @suppress {checkTypes} */ var e = new WebAssembly.RuntimeError(what);^RuntimeError: Aborted(alignment fault)at abort (/home/yeshan333/workspace/playground/emcc_playground/debug-blogpost/unalign.js:613:41)at alignfault (/home/yeshan333/workspace/playground/emcc_playground/debug-blogpost/unalign.js:335:3)at unalign.wasm (wasm://wasm/unalign.wasm-00091ee6:wasm-function[107]:0x56e4)at unalign.wasm.throw_unalign_err() (wasm://wasm/unalign.wasm-00091ee6:wasm-function[6]:0x42f)at unalign.wasm.__original_main (wasm://wasm/unalign.wasm-00091ee6:wasm-function[7]:0x4f2)at unalign.wasm.main (wasm://wasm/unalign.wasm-00091ee6:wasm-function[8]:0x50f)at /home/yeshan333/workspace/playground/emcc_playground/debug-blogpost/unalign.js:682:12at callMain (/home/yeshan333/workspace/playground/emcc_playground/debug-blogpost/unalign.js:1383:15)at doRun (/home/yeshan333/workspace/playground/emcc_playground/debug-blogpost/unalign.js:1421:23)at run (/home/yeshan333/workspace/playground/emcc_playground/debug-blogpost/unalign.js:1431:5)
但是如果是在 Web 浏览器环境(chrome)中, 我们能看到符号所在的 C 源文件, 打开 unalign.html
, 我们能看到如下堆栈:
参考
- https://marketplace.visualstudio.com/items?itemName=ms-vscode.wasm-dwarf-debugging
- VSCode调试的两种模式: launch 和 attach
本文由博客一文多发平台 OpenWrite 发布!
相关文章:

在 Visual Studio Code 与微信开发者工具中调试使用 emscripten 基于 C 生成的 WASM 代码
最近在尝试将一些 C/C、Lua 项目挪到 Web 上跑, 接触到了 emscripten. 这里会介绍下在 Visual Studio Code 与微信开发者工具中调试使用 emscripten 基于 C 生成的 WASM 代码 (WebAssembly) 的一些方法. Emscripten 与 WebAssebmly WebAssembly 是一种新的编码方式, 可以在现代…...
elasticsearch实战应用从入门到高效使用java集成es快速上手
Elasticsearch 因其出色的性能、可扩展性和易用性,成为了处理大规模数据和构建搜索引擎的首选工具。本文将通过一个实际案例,详细讲解如何在 Spring Boot 项目中集成 Elasticsearch,进行数据索引、搜索、聚合分析等操作。 一、Elasticsearch 简介 Elasticsearch 是一个基于…...
【OneAPI】通过网页预渲染让搜索引擎收录网页
API简介 网页预渲染,适用于动态网页以及单页面的SEO,支持网页缓存。 您无须更改代码即可让搜索引擎收录您的网页。只要将需要预渲染的页面转发的本接口即可。 如果您使用Nginx作为网页服务器,推荐使用以下配置: #您的网站locat…...
【网络安全.渗透测试】Cobalt strike(CS)工具使用说明
目录 前言 一、工具显著优势 二、安装 Java 运行环境 三、实验环境搭建要点 四、核心操作流程详解 (一)环境准备与连接步骤 (二)主机上线与深度渗透流程 五、其他实用功能应用指南 (一)office 宏 payload 应用 (二)Https Payload 应用 (三)信息收集策略 …...

港中文腾讯提出可穿戴3D资产生成方法BAG,可自动生成服装和配饰等3D资产如,并适应特定的人体模型。
今天给大家介绍一种名为BAG(Body-Aligned 3D Wearable Asset Generation)的新方法,可以自动生成可穿戴的3D资产,如服装和配饰,以适应特定的人体模型。BAG方法通过构建一个多视图图像扩散模型,生成与人体对齐…...
【C语言标准库函数】标准输入输出函数详解[4]:二进制文件读写函数
目录 一、fread() 函数 1.1. 函数简介 1.2. fread 使用场景 1.3. 注意事项 1.4. 示例 二、fwrite() 函数 2.1. 函数简介 2.2. fwrite 使用场景 2.3. 注意事项 2.4. 示例 三、总结 在 C 语言中,二进制文件读写函数允许以二进制形式对文件进行读写操作&…...

Python:凯撒密码
题目内容: 凯撒密码是古罗马恺撒大帝用来对军事情报进行加密的算法,它采用了替换方法对信息中的每一个英文字符循环替换为字母表序列该字符后面第三个字符,对应关系如下: 原文:A B C D E F G H I J K L M N O P Q R …...

C++引用深度详解
C引用深度详解 前言1. 引用的本质与核心特性1.1 引用概念1.2 核心特性 2. 常引用与权限控制2.1 权限传递规则2.2 常量引用2.3 临时变量保护1. 样例2. 样例3. 测试 三、引用使用场景分析3.1 函数参数传递输出型参数避免多级指针高效传参 3.2 做函数返回值正确使用危险案例 4. 性…...

C++ Primer 语句作用域
欢迎阅读我的 【CPrimer】专栏 专栏简介:本专栏主要面向C初学者,解释C的一些基本概念和基础语言特性,涉及C标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级…...
github - 使用
注册账户以及创建仓库 要想使用github第一步当然是注册github账号了, github官网地址:https://github.com/。 之后就可以创建仓库了(免费用户只能建公共仓库),Create a New Repository,填好名称后Create,之后会出现一些仓库的配置信息,这也是一个git的简单教程。 Git…...

内网ip网段记录
1.介绍 常见的内网IP段有: A类: 10.0.0.0/8 大型企业内部网络(如 AWS、阿里云) 10.0.0.0 - 10.255.255.255 B类:172.16.0.0/12 中型企业、学校 172.16.0.0 - 172.31.255.255 C类:192.168.0.0/16 家庭…...

k8s部署logstash
1. 编写logstash.yaml配置文件 --- apiVersion: v1 kind: Service metadata:name: logstash spec:type: ClusterIPclusterIP: Noneports:- name: logstash-tcpport: 5000targetPort: 5000- name: logstash-beatsport: 5044targetPort: 5044- name: logstash-apiport: 9600targ…...

EF Core中实现值对象
目录 值对象优点 值对象的需求 值类型的实现 值类型GEO的实现 值类型MultilingualString的实现 案例:构建表达式树,简化值对象的比较 值对象优点 把有紧密关系的属性打包为一个类型把领域知识放到类的定义中 class shangjia {long id;string nam…...

【分布式理论9】分布式协同:分布式系统进程互斥与互斥算法
文章目录 一、互斥问题及分布式系统的特性二、分布式互斥算法1. 集中互斥算法调用流程优缺点 2. 基于许可的互斥算法(Lamport 算法)调用流程优缺点 3. 令牌环互斥算法调用流程优缺点 三、三种算法对比 在分布式系统中,多个应用服务可能会同时…...

木材表面缺陷检测数据集,支持YOLO+COCO JSON+PASICAL VOC XML+DARKNET格式标注信息,平均正确识别率95.0%
数据集说明 木材表面缺陷检测数据集是用于训练和验证人工智能算法,以帮助自动识别和检测木材表面的缺陷,如裂纹、疤痕、孔洞等。这对于木材行业非常重要,可以提高生产过程的效率和质量控制水平。 本文提供的木材表面缺陷检测数据集࿰…...
Leetcodehot 力扣热题100 二叉搜索树中第 K 小的元素
class Solution { public:int res; // 用于存储第 k 小的元素int kthSmallest(TreeNode* root, int k) {inorder(root, k); // 进行中序遍历并找到第 k 小的元素return res; // 返回结果}private:// 中序遍历:遍历树的左子树、根节点和右子树void inorder(TreeNod…...
Awtk 如何添加开机画面
场景 我们知道在工程中,Ui是一个线程,并且需要一直存在,当我们使用的开机画面在这个线程开启就直接展示的时候,因为awtk的界面是window_open入栈的,即首次打开的窗口会记录在top,往后的窗口会依次往后存放&…...

关于多语言商城系统的开发流程
建设多语言商城系统是现在很多传统外贸企业的选择,外贸企业通过多语言电商系统开展海外业务,那么多语言商城系统的开发流程是怎么样的呢?接下来就跟着小来一起来看看吧。 1、页面UI设计 多语言商城系统的原型图经过反复推敲修正后࿰…...

IDEA中常见问题汇总
🍓 简介:java系列技术分享(👉持续更新中…🔥) 🍓 初衷:一起学习、一起进步、坚持不懈 🍓 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正🙏 🍓 希望这篇文章对你有所帮助,欢…...

计算机视觉-拟合
一、拟合 拟合的作用主要是给物体有一个更好的描述 根据任务选择对应的方法(最小二乘,全最小二乘,鲁棒最小二乘,RANSAC) 边缘提取只能告诉边,但是给不出来数学描述(应该告诉这个点线是谁的&a…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...

Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...

免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...