VS Code插件开发:如何跟踪调试过程中添加的断点?
解决VSCode插件调试过程中新增断点跟踪问题
核心问题分析
- 原代码用数组索引作为断点ID的映射键,调试中新增断点会打乱索引顺序,导致
hitBreakpointIds无法匹配到正确的数据库断点记录。 - 未正确利用
onDidChangeBreakpoints事件处理动态增删的断点,也未关联VSCode断点实例与调试适配器返回的断点ID。
具体解决方案
1. 重构断点映射结构
将原map拆分为两个独立映射,分别存储不同维度的关联关系:
private vscodeBreakpointToDbId: Record<string, string> = {}; // VSCode断点ID → 数据库断点ID private debugAdapterBreakpointToVscodeId: Record<string | number, string> = {}; // 调试适配器断点ID → VSCode断点ID private currentSessionId: string | null = null; // 当前会话ID
2. 完善动态断点变更处理
实现procesBreakpointDuringDebug方法,处理调试过程中断点的新增与移除:
private async procesBreakpointDuringDebug(event: vscode.BreakpointsChangeEvent): Promise<void> { if (!this.currentSessionId) return; // 处理新增的断点 for (const breakpoint of event.added) { if (breakpoint instanceof vscode.SourceBreakpoint && breakpoint.enabled) { const activeSession = vscode.debug.activeDebugSession; if (!activeSession) continue; const bp = await insertBp(breakpoint, activeSession.id, {}); await insertEvent(BREAKPOINT.SET, { breakpoint_id: bp.breakpoint_id, session_id: this.currentSessionId }); this.vscodeBreakpointToDbId[breakpoint.id] = bp.breakpoint_id; } } // 处理移除的断点(可选:记录移除事件) for (const breakpoint of event.removed) { if (breakpoint instanceof vscode.SourceBreakpoint) { const dbBpId = this.vscodeBreakpointToDbId[breakpoint.id]; if (dbBpId) { await insertEvent(BREAKPOINT.REMOVE, { breakpoint_id: dbBpId, session_id: this.currentSessionId }); delete this.vscodeBreakpointToDbId[breakpoint.id]; // 清理调试适配器ID映射 Object.keys(this.debugAdapterBreakpointToVscodeId).forEach(key => { if (this.debugAdapterBreakpointToVscodeId[key] === breakpoint.id) { delete this.debugAdapterBreakpointToVscodeId[key]; } }); } } } }
3. 监听调试适配器响应,关联断点ID
修改createDebugAdapterTracker,添加onDidReceiveMessage方法处理setBreakpoints响应,建立调试端与VSCode端的断点ID关联:
private createDebugAdapterTracker(session: vscode.DebugSession) { return { onDidSendMessage: this.handleDebugAdapterMessage.bind(this), onDidReceiveMessage: this.handleDebugAdapterResponse.bind(this), onError: (error: any) => console.error("Debug Adapter Error:", error), onExit: (code: any, signal: any) => console.log(`Debug Adapter closed: code=${code}, signal=${signal}`) }; } private async handleDebugAdapterResponse(message: any): Promise<void> { // 处理setBreakpoints响应,关联两端断点ID if (message.type === 'response' && message.command === 'setBreakpoints') { const breakpoints = message.body.breakpoints || []; const sourcePath = message.arguments.source.path; // 匹配对应文件的VSCode断点 const vscodeBreakpoints = vscode.debug.breakpoints.filter(bp => bp instanceof vscode.SourceBreakpoint && bp.source?.path === sourcePath ); // 按顺序关联ID(调试适配器返回的断点顺序与请求一致) breakpoints.forEach((debugBp: any, index: number) => { const vscodeBp = vscodeBreakpoints[index]; if (vscodeBp && debugBp.id) { this.debugAdapterBreakpointToVscodeId[debugBp.id] = vscodeBp.id; } }); } }
4. 调整断点触发时的匹配逻辑
修改processBreakpointHit,通过调试适配器ID反向查找数据库断点记录:
private async processBreakpointHit(message: any): Promise<void> { if (!this.currentSessionId) return; const breakpointIds = message.body.hitBreakpointIds || []; console.log(`Breakpoint triggered IDs: ${breakpointIds}`); for (const debugBpId of breakpointIds) { const vscodeBpId = this.debugAdapterBreakpointToVscodeId[debugBpId]; if (vscodeBpId) { const dbBpId = this.vscodeBreakpointToDbId[vscodeBpId]; if (dbBpId) { await insertEvent(BREAKPOINT.TRIGGER, { breakpoint_id: dbBpId, session_id: this.currentSessionId }); } } } // 保留 fallback 逻辑 if (breakpointIds.length === 0) { await this.handleFallbackBreakpoint(message); } }
5. 修正会话初始化与清理逻辑
更新会话相关方法,替换原map的使用:
private async finalizeSession(session: vscode.DebugSession, ss: any): Promise<void> { this.currentSessionId = ss.session_id; ss.is_debug = !session.configuration?.noDebug; await ss.save(); } private async handleTerminateDebugSession(session: vscode.DebugSession): Promise<void> { if (this.currentSessionId && this.deletedSession === null) { this.deletedSession = session.id; await Event.create({ session_id: this.currentSessionId, type: SESSION.END }); } // 清理所有映射 this.vscodeBreakpointToDbId = {}; this.debugAdapterBreakpointToVscodeId = {}; this.currentSessionId = null; }
6. 修正初始断点处理逻辑
修改processBreakpoints,改用VSCode断点ID作为映射键:
private async processBreakpoints(session: vscode.DebugSession, ss: any): Promise<void> { this.currentSessionId = ss.session_id; for (const breakpoint of vscode.debug.breakpoints) { if (breakpoint instanceof vscode.SourceBreakpoint) { let bp = await insertBp(breakpoint, session.id, {}); await insertEvent(BREAKPOINT.SET, { breakpoint_id: bp.breakpoint_id, session_id: ss.session_id }); this.vscodeBreakpointToDbId[breakpoint.id] = bp.breakpoint_id; } } }
关键说明
- 利用VSCode断点实例的
id属性作为唯一标识,彻底避免数组索引带来的顺序问题。 - 通过监听调试适配器的
setBreakpoints响应,建立调试端与VSCode端的断点ID关联,确保断点触发时能精准匹配数据库记录。 - 动态处理断点的增删事件,保证调试过程中新增的断点也能被完整跟踪。
内容的提问来源于stack exchange,提问作者I try so hard but I cry harder
相关产品推荐
相关产品推荐

