本文档面向 AI 编程助手和开发者,详细说明项目的技术架构、调试器适配策略和代码组织方式。
src/
├── extension.ts # 扩展入口,变量可视化主逻辑
├── cvVariablesProvider.ts # TreeView 变量列表提供器
├── utils/
│ ├── debugger.ts # 调试器适配层(核心)
│ ├── opencv.ts # OpenCV 类型检测(纯字符串匹配)
│ ├── panelManager.ts # Webview 面板管理
│ └── syncManager.ts # 变量配对同步管理
├── matImage/
│ ├── matProvider.ts # 2D 图像数据读取
│ └── matWebview.ts # 图像 Webview 渲染
├── plot/
│ ├── plotProvider.ts # 1D 数据读取
│ └── plotWebview.ts # 曲线图 Webview 渲染
└── pointCloud/
├── pointCloudProvider.ts # 3D 点云数据读取
└── pointCloudWebview.ts # 点云 Webview 渲染
| 调试器类型 | debugSession.type |
编译器/平台 | 标准库实现 |
|---|---|---|---|
| LLDB (CodeLLDB) | "lldb" |
Clang (macOS/Linux) | libc++ |
| GDB (cppdbg) | "cppdbg" |
GCC (Linux/MinGW) | libstdc++ |
| MSVC (cppvsdbg) | "cppvsdbg" |
MSVC (Windows) | MSVC STL |
// 检测当前调试器类型
export function isUsingLLDB(debugSession: vscode.DebugSession): boolean {
return debugSession.type === "lldb";
}
export function isUsingCppdbg(debugSession: vscode.DebugSession): boolean {
return debugSession.type === "cppdbg";
}
export function isUsingMSVC(debugSession: vscode.DebugSession): boolean {
return debugSession.type === "cppvsdbg";
}不同调试器对 evaluate 请求的 context 参数处理不同:
export function getEvaluateContext(debugSession: vscode.DebugSession): string {
// CodeLLDB 将 "repl" 视为命令模式,需要用 "watch" 进行表达式求值
if (isUsingLLDB(debugSession)) {
return "watch";
}
// cppdbg 和 cppvsdbg 使用 "repl" 即可
return "repl";
}std::array 和 std::vector 的内部数据成员在不同标准库实现中名称不同:
| 标准库 | std::array 内部成员 |
std::vector 数据指针 |
|---|---|---|
| libc++ (Clang) | __elems_ |
__begin_ |
| libstdc++ (GCC) | _M_elems |
_M_start |
| MSVC STL | _Elems |
_Myfirst |
代码中定义了常量来统一处理:
export const STL_ARRAY_MEMBERS = ["__elems_", "_M_elems", "_Elems"];
export const STL_VECTOR_DATA_MEMBERS = ["__begin_", "_M_start", "_Myfirst"];不同调试器对表达式语法的支持不同,使用辅助函数构建:
// 构建获取数据指针的表达式
export function buildDataPointerExpressions(
debugSession: vscode.DebugSession,
variableName: string,
accessPath: string = ".data()"
): string[]
// 构建获取容器大小的表达式
export function buildSizeExpressions(
debugSession: vscode.DebugSession,
variableName: string
): string[]项目采用两层检测架构:
-
基础检测层 (
src/utils/opencv.ts)- 纯字符串匹配,基于
variableInfo.type字符串 - 不依赖调试器,速度快
- 用于 TreeView 变量列表的快速分类
- 纯字符串匹配,基于
-
增强检测层 (
src/utils/debugger.ts)- 使用调试器命令获取更准确的类型信息
- 仅在 LLDB 下使用(因为 LLDB 的类型字符串可能不完整)
- 用于实际可视化时的精确检测
- 回退到基础检测层
| 数据类型 | 基础检测 (opencv.ts) |
增强检测 (debugger.ts) |
|---|---|---|
3D std::array |
is3DStdArray() |
- |
| 3D C 风格数组 | is3DCStyleArray() |
is3DCStyleArrayEnhanced() |
2D std::array |
is2DStdArray() |
is2DStdArrayEnhanced() |
| 2D C 风格数组 | is2DCStyleArray() |
is2DCStyleArrayEnhanced() |
1D std::array |
is1DStdArray() |
- |
| 1D C 风格数组 | is1DCStyleArray() |
is1DCStyleArrayEnhanced() |
cv::Mat |
isMat() |
- |
cv::Matx |
isMatx() |
- |
std::vector<Point3> |
isPoint3Vector() |
- |
std::array<Point3> |
isPoint3StdArray() |
- |
1D std::vector |
is1DVector() |
- |
1D std::set |
is1DSet() |
- |
is2DStdArrayEnhanced() 和 is2DCStyleArrayEnhanced() 使用调试器命令获取类型信息:
// LLDB: 使用 frame variable 命令
`frame variable --show-types --depth 0 ${variableName}`
// 输出示例: (int[2][3]) rawArr = {...}
// GDB: 使用 ptype 命令
`-exec ptype ${variableName}`
// 输出示例: type = int [2][3]
// MSVC: 使用格式说明符
`${variableName},t`增强检测函数会先尝试调试器命令,失败后自动回退到基础检测:
export async function is2DStdArrayEnhanced(...) {
// 1. 尝试调试器命令
const typeInfo = await getArrayTypeInfo(debugSession, variableName, frameId);
if (typeInfo) {
// 成功,返回结果
return { is2DArray: true, rows, cols, elementType, depth };
}
// 2. 回退到基础检测
return is2DStdArray(variableInfo);
}由于不同调试器返回的信息格式不同,获取数据指针采用多策略尝试:
策略 1: 通过 variablesReference 展开变量树
// 展开变量获取子成员
const varsResponse = await debugSession.customRequest("variables", {
variablesReference: variableInfo.variablesReference
});
// 查找 [0] 元素或内部成员
for (const v of varsResponse.variables) {
if (v.memoryReference) {
return v.memoryReference; // 直接获取内存引用
}
}策略 2: 通过表达式求值
// 使用辅助函数构建调试器特定的表达式
const expressions = buildDataPointerExpressions(debugSession, variableName);
dataPtr = await tryGetDataPointer(debugSession, variableName, expressions, frameId, context);大数据量使用并行分块读取:
export async function readMemoryChunked(
debugSession: vscode.DebugSession,
memoryReference: string,
totalBytes: number,
progress?: vscode.Progress<{ message?: string; increment?: number }>
): Promise<Buffer | null> {
const CHUNK_SIZE = 8 * 1024 * 1024; // 8MB per chunk
const CONCURRENCY = Math.min(8, Math.max(2, os.cpus().length));
// ... 并行读取逻辑
}位置: src/utils/debugger.ts
功能: 使用调试器命令检测 2D std::array,支持所有调试器类型。
为什么需要这个函数:
- LLDB 返回的
variableInfo.type字符串可能不包含完整的模板参数 - 通过
frame variable --show-types命令可以获取完整类型信息
调用时机:
// extension.ts 中
let stdArray2D;
if (isLLDB) {
stdArray2D = await is2DStdArrayEnhanced(debugSession, variableName, frameId, variableInfo);
} else {
stdArray2D = is2DStdArray(variableInfo); // 使用基础检测
}位置: src/utils/debugger.ts
功能: 使用调试器命令检测 2D C 风格数组(如 int[2][3])。
位置: src/utils/debugger.ts
功能: 使用调试器命令检测 1D C 风格数组(如 int[10])。
工作原理:
- 使用与 2D 检测相同的调试器命令获取类型信息
- 解析输出时排除 2D 数组模式(
type[rows][cols]) - 仅匹配 1D 数组模式(
type[size]) - 失败时回退到基础检测
is1DCStyleArray()
位置: src/utils/opencv.ts
功能: 检测 C 风格 3D 数组(如 uint8_t[480][640][3]),用于多通道图像可视化。
类型字符串匹配模式:
unsigned char [480][640][3]uint8_t[100][100][3]float [H][W][C]
返回值: { is3DArray, height, width, channels, elementType, depth }
通道数限制: 仅当最内层维度为 1、3 或 4 时才识别为图像(其他通道数可能是非图像 3D 数据)
位置: src/utils/opencv.ts
功能: 检测 3 层嵌套的 std::array(如 std::array<std::array<std::array<uint8_t, 3>, 640>, 480>)。
类型字符串匹配模式:
std::array<std::array<std::array<uint8_t, 3>, 640>, 480>std::__1::array<std::__1::array<std::__1::array<float, 3>, 100>, 100>(libc++)class std::array<class std::array<class std::array<unsigned char, 3>, 640>, 480>(MSVC)
返回值: { is3DArray, height, width, channels, elementType, depth }
位置: src/utils/debugger.ts
功能: 使用调试器命令增强检测 C 风格 3D 数组。
工作原理:
- 先尝试调试器命令获取准确类型信息
- 失败时回退到基础检测
is3DCStyleArray()
位置: src/utils/debugger.ts
功能: 获取 3D 数组的数据指针,支持 C 风格和 std::array 两种类型。
数据指针获取策略:
- Variables 方法(优先):通过 DAP variables 请求遍历到
[0][0][0]元素 - Evaluate 方法(回退):使用表达式
&arr[0][0][0]
位置: src/matImage/matProvider.ts
功能: 绘制 3D 数组图像,复用现有的 Image Viewer webview。
实现逻辑:
- 计算总字节数:
height * width * channels * bytesPerElement - 调用
get3DArrayDataPointer()获取数据指针 - 使用
readMemoryChunked()读取内存数据 - 发送到 webview(现有 Image Viewer 已支持多通道)
位置: src/utils/debugger.ts
功能: 底层函数,执行调试器命令并解析类型信息。
被调用者: is2DStdArrayEnhanced(), is2DCStyleArrayEnhanced()
职责:
- 基于字符串匹配的类型检测
- 不依赖调试器 API
- 提供 OpenCV 深度类型转换
导出函数:
isMat(),isMatx()- cv::Mat 类型检测is3DStdArray(),is3DCStyleArray()- 3D 数组检测(多通道图像)is2DStdArray(),is1DStdArray()- std::array 检测is2DCStyleArray()- C 风格数组检测isPoint3Vector(),isPoint3StdArray()- 点云类型检测is1DVector(),is1DSet()- 1D 容器检测getDepthFromCppType()- C++ 类型到 OpenCV 深度映射getBytesPerElement(),convertBytesToValues()- 数据转换
职责:
- 调试器类型检测
- 调试器特定的表达式构建
- 增强类型检测(使用调试器命令,回退到基础检测)
- 内存读取
- 数据指针获取
导出函数:
isUsingLLDB(),isUsingCppdbg(),isUsingMSVC()- 调试器检测getEvaluateContext()- 获取求值上下文buildDataPointerExpressions(),buildSizeExpressions()- 表达式构建evaluateWithTimeout()- 带超时的表达式求值getCurrentFrameId()- 获取当前栈帧 IDreadMemoryChunked()- 分块内存读取getVectorSize()- 获取 vector 大小getStdArrayDataPointer(),get2DStdArrayDataPointer()- std::array 数据指针getCStyle2DArrayDataPointer()- C 风格数组数据指针get3DArrayDataPointer()- 3D 数组数据指针(C 风格和 std::array)is2DStdArrayEnhanced(),is2DCStyleArrayEnhanced()- 增强类型检测is3DCStyleArrayEnhanced()- 3D C 风格数组增强检测getArrayTypeInfo()- 调试器命令获取类型信息
职责:
- 提供 TreeView 数据
- 快速分类变量(使用基础检测)
- 管理变量配对
职责:
- 注册命令和事件
- 协调可视化流程
- 根据调试器类型选择检测策略
- 在
opencv.ts添加基础检测函数 - 如果需要调试器命令支持,在
debugger.ts添加增强检测函数(使用基础检测作为回退) - 在
cvVariablesProvider.ts添加 TreeView 分类逻辑 - 在
extension.ts添加可视化分支 - 在相应的 Provider 中添加数据读取逻辑
新功能需要在以下环境测试:
- macOS + Clang + CodeLLDB
- Linux + GCC + cppdbg
- Windows + MSVC + cppvsdbg
- Windows + MinGW + cppdbg
Q: 为什么 LLDB 需要特殊处理?
A: CodeLLDB 的 evaluate 请求在 context: "repl" 时会进入命令模式而非表达式求值模式,需要使用 context: "watch"。此外,LLDB 返回的类型字符串可能不完整,需要使用 frame variable 命令获取完整信息。
Q: 为什么有两层检测架构?
A: TreeView 需要快速响应,使用纯字符串匹配的基础检测。实际可视化时可以花更多时间使用调试器命令获取准确信息。增强检测会自动回退到基础检测,避免代码重复。
Q: 如何处理标准库差异?
A: 在访问内部成员时,使用 STL_ARRAY_MEMBERS 和 STL_VECTOR_DATA_MEMBERS 常量同时检查所有可能的命名。
- 新增 3D 数组(多通道图像)支持
- 添加
is3DCStyleArray()和is3DStdArray()基础检测函数 - 添加
is3DCStyleArrayEnhanced()增强检测函数 - 添加
get3DArrayDataPointer()数据指针获取函数 - 添加
draw3DArrayImage()可视化函数 - 支持
T[H][W][C]C 风格 3D 数组 - 支持
std::array<std::array<std::array<T, C>, W>, H>嵌套数组 - 通道数限制为 1、3、4(灰度、RGB/BGR、RGBA/BGRA)
- 重构增强检测函数,使用基础检测作为回退,消除代码重复
- 将
is2DStdArrayLLDB重命名为is2DStdArrayEnhanced(保留别名兼容) - 添加
buildDataPointerExpressions()和buildSizeExpressions()辅助函数 - 添加
STL_ARRAY_MEMBERS和STL_VECTOR_DATA_MEMBERS常量 - 改进代码组织和文档
- 新增 2D C 风格数组支持
- 增强 LLDB 下的 2D std::array 检测
- 使用调试器命令获取更准确的类型信息