/** * iOS dSYM 符号化解析器 * * 使用 atos 命令进行符号化: * atos -o MyApp.app.dSYM/Contents/Resources/DWARF/MyApp -arch arm64 -l 0x102345000 0x102345678 */ import { execSync } from 'child_process' import { StackFrame } from './stack-parser' export interface DsymConfig { /** dSYM 文件路径 */ dsymPath: string /** 应用加载地址 */ loadAddress: string /** 架构 (arm64, x86_64) */ arch?: string } /** * 使用 atos 命令符号化 iOS 堆栈 */ export function symbolicateWithDsym( frames: StackFrame[], config: DsymConfig ): string[] { const { dsymPath, loadAddress, arch = 'arm64' } = config // 提取需要符号化的地址 const addresses = frames .filter(f => f.file === '') .map(f => `0x${f.column.toString(16)}`) if (addresses.length === 0) { return frames.map(f => `${f.function} (${f.file}:${f.line})`) } try { // 调用 atos 命令 const cmd = `atos -o "${dsymPath}" -arch ${arch} -l ${loadAddress} ${addresses.join(' ')}` const output = execSync(cmd, { encoding: 'utf-8', timeout: 30000 }) const symbolicated = output.trim().split('\n') let addrIndex = 0 return frames.map(f => { if (f.file === '' && addrIndex < symbolicated.length) { return symbolicated[addrIndex++] } return `${f.function} (${f.file}:${f.line})` }) } catch (err) { console.error('atos symbolication failed:', err) return frames.map(f => `${f.function} (${f.file}:${f.line})`) } } /** * 解析 iOS 崩溃日志中的线程帧 * * 格式示例: * 0 MyApp 0x0000000102345678 myFunction + 123 * 1 MyApp 0x0000000102345678 myFunction + 123 */ export function parseIOSCrashFrames(crashLog: string): StackFrame[] { const frames: StackFrame[] = [] const lines = crashLog.split('\n') for (const line of lines) { const match = line.trim().match( /(\d+)\s+(\S+)\s+(0x[\da-f]+)\s+(\S+)\s+\+\s+(\d+)/ ) if (match) { frames.push({ function: match[4], file: match[2], line: 0, column: parseInt(match[5], 10), }) } } return frames }