mcpField
C
crashdx

crashdx

本地服务
github.com/r00tify开发工具713 周前更新

关于 crashdx

crashdx 是面向 Apple 平台开发者和支持团队的崩溃报告诊断引擎。它结合符号化与基于规则的假设排序,并将证据关联回原始报告;证据不足时会给出无法确定的结果。

主要功能

  1. 01解析并符号化 .ips 报告
  2. 02对规则诊断假设排序
  3. 03将证据追溯至报告路径
  4. 04按层级输出 JSON
  5. 05提供 Swift 库和命令行工具

适用场景

  1. 01调查 Apple 应用崩溃原因
  2. 02核对诊断引用的证据
  3. 03在 CI 中解析崩溃分析
  4. 04通过 MCP 客户端检查报告

MCP 相关

`crashdx-mcp` 服务通过 stdio MCP 工具 `crashdx_analyze` 和 `crashdx_symbolicate` 暴露相同的分析与符号化流程。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

暂未收录安装方式

我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。

代码仓库
安装包
mcpbcrashdx-mcp-v0.1.0.mcpb

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

crashdx

CI

适用于 Apple 平台的崩溃诊断引擎:解析 .ips 崩溃报告,根据 dSYM 符号化,并生成引用证据、按可信度排序的诊断,而不只是符号化后的堆栈跟踪。

它以无依赖的 Swift 库(CrashDXCore 仅导入 Foundation)、命令行工具(crashdx)和 MCP 服务(crashdx-mcp)的形式提供,让 Agent 和人类使用同一套引擎。

为什么选择 crashdx

符号化后的堆栈跟踪能告诉你进程在哪里崩溃,却不能说明原因。crashdx 在符号化之上增加了第二个阶段:确定性、基于规则的证据提取(看门狗预算、jetsam 表、寄存器和内存状态、lastExceptionBacktrace/asi),以此生成一组按可信度排序的竞争性假设。每个假设都会引用支持它的具体事实,并指出这些事实在原始报告中的位置。

它刻意坚持两项原则:

  • 每项判断都可追溯。 事实会携带指向原始报告的 JSON 路径,因此每项结论都能得到核验,而不是只能盲目信任。
  • 确实无法确定时,就会说“无法确定”。 只有当最优假设得到强有力支持,且明显领先第二名时,才会给出结论;否则会列出按可信度排序的候选项,以及如何确认结论。

引擎不会调用大模型:它会生成可验证的事实和按可信度排序的解释,并将叙述留给使用这些结果的工具。完整架构请参阅 docs/DESIGN.md。

输出示例

以下内容均为本仓库中测试样例的真实原始输出,未作编辑(省略部分已标注)。 路径相对于仓库根目录,--dsym 标志指向仓库随附的 dSYM,因此可以直接复现;如果尚未安装二进制文件,请使用 swift run crashdx ...。

一项诊断结论
$ crashdx analyze Tests/CrashDXCoreTests/Fixtures/nsexcrash.ips \
      --dsym Tests/CrashDXCoreTests/Fixtures/nsexcrash.dSYM

process:    nsexcrash
bug_type:   309
os:         macOS 26.3.1 (25D2128)
exception:  EXC_CRASH (SIGABRT)
terminated: Abort trap: 6
faulting thread (15 frames):
  libsystem_kernel.dylib  __pthread_kill
  libsystem_pthread.dylib  pthread_kill
  libsystem_c.dylib  abort
  libc++abi.dylib  __abort_message
  libc++abi.dylib  demangling_terminate_handler()
  libobjc.A.dylib  _objc_terminate()
  libc++abi.dylib  std::__terminate(void (*)())
  libc++abi.dylib  __cxxabiv1::failed_throw(__cxxabiv1::__cxa_exception*)
  libc++abi.dylib  __cxa_throw
  libobjc.A.dylib  objc_exception_throw
  CoreFoundation  -[NSException raise]
  nsexcrash  throwingHelper() (main.swift:8)
  nsexcrash  doWork() (main.swift:12)
  nsexcrash  main (main.swift:15)
  dyld  start
last exception backtrace (7 frames):
  CoreFoundation  __exceptionPreprocess
  libobjc.A.dylib  objc_exception_throw
  CoreFoundation  -[NSException raise]
  nsexcrash  throwingHelper() (main.swift:8)
  nsexcrash  doWork() (main.swift:12)
  nsexcrash  main (main.swift:15)
  dyld  start
symbolication (engine: crashSymbolicator):
  nsexcrash  symbolicated
  libsystem_kernel.dylib  no_dsym
  libsystem_pthread.dylib  no_dsym
  ...
DIAGNOSIS: Uncaught Objective-C exception (NSException)   (strong, score 5)
  A lastExceptionBacktrace is present and an objc_exception_throw frame appears in it (or on the
  faulting thread) — together these are pathognomonic for an uncaught NSException: `-raise` walked up
  through objc_exception_throw and was never caught, so the runtime called terminate and aborted. The
  asi "Terminating app due to uncaught exception" message is only corroboration when present; plain
  CLI/Foundation processes never write it, so its absence is not evidence against this hypothesis.
  evidence: lastExceptionBacktrace is present with 7 frame(s); lastExceptionBacktrace frame 1 matches
            sentinel 'objc-exception-throw': objc_exception_throw; Faulting thread frame 9 matches
            sentinel 'objc-exception-throw': objc_exception_throw
  inspect:  nsexcrash throwingHelper() (main.swift:8)
  confirm:  Symbolicate the app frames in lastExceptionBacktrace to find the throw site; Check the
            asi/console log for the exception name and reason, if available
  also considered: cxx-terminate (moderate, 2), abort-generic (weak, 1)

evidence 行引用规则评分所依据的支持性事实,最多列出四项;更多事实则会显示累计总数。每项事实都附有指向原始报告的路径,可通过 --json --tier standard 查看。also considered 是按可信度排序的完整剩余列表,因此你可以看到哪些候选项被这项诊断结论超过,以及领先多少。

无法确定的结果

如果最优假设没有得到强有力支持,且没有明显领先,你会看到候选项,而不是一个标签:

$ crashdx analyze Tests/CrashDXCoreTests/Fixtures/synthetic/wild-address.ips \
      --dsym corpus/fixtures/nullderef/nullderef.dSYM

process:    synthetic-wild-address
bug_type:   309
os:         macOS 26.3.1 (25D2128)
exception:  EXC_BAD_ACCESS (SIGSEGV)
terminated: Segmentation fault: 11
faulting thread (4 frames):
  nullderef  readThroughNullPointer() (main.swift:9)
  nullderef  run() (main.swift:13)
  nullderef  main (main.swift:16)
  dyld  start
symbolication (engine: crashSymbolicator):
  nullderef  symbolicated
  dyld  no_dsym
DIAGNOSIS: INCONCLUSIVE — competing hypotheses:
  1. Wild pointer or use-after-free [wild-or-uaf-address]   (moderate, score 3)
     The faulting address is outside the null page, and vmregioninfo reports it is not inside any known
     VM region at all — consistent with either a WILD pointer (an uninitialized or garbage value used as
     an address) or a USE-AFTER-FREE into memory the OS has since unmapped. This is deliberately a
     lower-confidence hypothesis than null-dereference: absence of region information doesn't distinguish
     between these two causes.
     evidence: Faulting address: 0x1deadbeef; Exception type: EXC_BAD_ACCESS
     inspect:  nullderef readThroughNullPointer() (main.swift:9)
     confirm:  Re-run with Address Sanitizer enabled to catch the use-after-free at the free/access site;
               Enable NSZombies (Malloc Scribble/Guard Malloc) to turn this into an immediate,
               informative crash; Profile with Instruments' Allocations tool, recording reference counts
其他崩溃类型

同一格式适用于整个规则集。以下是另外四个测试样例的诊断结论和证据行,解释和后续步骤已省略:

# synthetic/watchdog-scene-create.ips
DIAGNOSIS: Watchdog timeout (main thread stall)   (strong, score 6)
  evidence: Termination code: 2343432205 (0x8badf00d); Termination namespace: FRONTBOARD; Watchdog
            event: scene-create, allowance 19.97s

# synthetic/jetsam-per-process-limit.ips
DIAGNOSIS: Jetsam memory kill   (strong, score 5)
  evidence: Exception subtype: RESOURCE_TYPE_MEMORY; Jetsam per-process-limit indicator:
            per-process-limit 350808KB exceeds task limit 335872KB

# synthetic/stack-overflow.ips
DIAGNOSIS: Stack overflow   (strong, score 5)
  evidence: Faulting address 0x3000 is near the stack region: vmregioninfo names a STACK GUARD region;
            within 500 bytes of sp (0x31f4); Faulting thread has 5 consecutive frames with identical
            symbol 'recurse(_:)' starting at frame 0 — a recursion signature

# crashspike-unsymbolicated.ips, with --dsym Tests/CrashDXCoreTests/Fixtures/crashspike.dSYM
DIAGNOSIS: Swift runtime fatal trap   (strong, score 6)
  evidence: Exception type: EXC_BREAKPOINT; Signal: SIGTRAP; Faulting thread frame 0 matches sentinel
            'assertion-failure': _assertionFailure(_:_:file:line:flags:)

这几个样例也会输出第一个示例中展示的完整解释、inspect 位置和 confirm 步骤。

JSON

--json 会以结构化的 AnalysisReport 输出相同的分析结果。这正是 MCP 服务返回的内容,也是你在 CI 中会解析的内容。以下是与第一个示例相同的崩溃数据,内容已缩略(/* ... */ 表示省略部分)。实际输出是单行、经过压缩且键已排序的 JSON,因此可以通过管道传给 jq 或 python3 -m json.tool;为便于阅读,此处已格式化并重新排序:

{
  "schemaVersion": "0.2",
  "tier": "summary",
  "diagnosis": {
    "status": "verdict",
    "verdict": {
      "id": "uncaught-objc-exception",
      "title": "Uncaught Objective-C exception (NSException)",
      "category": "objc-exception",
      "explanation": "A lastExceptionBacktrace is present and an objc_exception_throw frame ...",
      "supporting": [
        { "factID": "leb.present", "weight": 1 },
        { "factID": "leb.sentinel.objc-exception-throw", "weight": 3 },
        { "factID": "frames.sentinel.objc-exception-throw", "weight": 1 }
      ],
      "contradicting": [],
      "inspect": [
        { "frameIndex": 3, "leb": true, "symbol": "throwingHelper()",
          "sourceFile": "main.swift", "sourceLine": 8 }
      ],
      "confirmFurtherBy": [ /* ... */ ]
    },
    "hypotheses": [ /* all 3, each with its band + score */ ]
  },
  "event": {
    "bugType": "309", "exceptionType": "EXC_CRASH", "signal": "SIGABRT",
    "terminationIndicator": "Abort trap: 6", "terminationNamespace": "SIGNAL"
  },
  "faultingThread": { "index": 0, "queue": "com.apple.main-thread", "triggered": true,
                      "frames": [ /* ... */ ] },
  "symbolication": { "engine": "crashSymbolicator", "images": [ /* per-image outcome + uuid */ ] },
  "process": { "name": "nsexcrash", "osVersion": "macOS 26.3.1 (25D2128)",
               "captureTime": "2026-07-23 00:22:01.2793 +0000" }
}

请注意检查位置上的 "leb": true:抛出位置是从 lastExceptionBacktrace 而非故障线程恢复的,报告会明确说明这一点,而不会抹平两者的区别。

--tier standard(请参阅报告层级)会添加 diagnosis.factsConsidered,将每个 factID 解析为易读的说明,以及指向原始报告的 sourcePath:

{ "id": "leb.sentinel.objc-exception-throw",
  "statement": "lastExceptionBacktrace frame 1 matches sentinel 'objc-exception-throw': objc_exception_throw",
  "sourcePath": "lastExceptionBacktrace[1]" }

Fact 中的 sourcePath 是必填项,因此诊断引用的每项事实都可以追溯到读取它的报告部分。

如果你想查看完整、未经缩略的格式,已提交的两个层级的黄金快照位于 Tests/CrashDXCoreTests/Fixtures/(nsexcrash-summary-golden.json、nullderef-standard-golden.json)。

要求

  • macOS 14+
  • Swift 6.2+ 工具链。清单本身的 swift-tools-version:6.0,但 MCP 服务固定版本的依赖最高声明为 6.2,而 SwiftPM 会解析整个软件包依赖图,因此所有目标(包括 CrashDXCore)实际最低都需要 6.2。
  • Xcode(不仅是 Command Line Tools):符号化会调用 Xcode CoreSymbolicationDT.framework 中的 Apple CrashSymbolicator.py,并通过 xcode-select -p 定位。没有 Xcode 时,crashdx 会退回使用 atos,它能解析的源代码位置较少。无论哪种情况,解析和诊断都能正常工作。
  • /usr/bin/python3 和 /usr/bin/dwarfdump(两者均由 Xcode 的命令行工具提供),用于运行 CrashSymbolicator.py 和验证 dSYM UUID。
支持的报告

明确说明支持范围,因为在你的平台上悄悄降低表现的崩溃诊断器,还不如坦诚说明限制:

  • 完全支持 arm64 崩溃报告:Apple 芯片 Mac、iOS/iPadOS/watchOS/tvOS 设备,以及在 Apple 芯片上运行的模拟器。这些是测试样本库覆盖的范围。
  • x86_64 报告(Intel Mac、经 Rosetta 转译的进程、Intel 上的模拟器)可以解析并符号化;引擎也会读取 x86_THREAD_STATE 寄存器和 4 KB 页面大小。测试样本库中没有 Intel 样本,因此这条路径是基于分析设计的,尚未用真实报告进行测试。
  • 仅支持崩溃报告(bug_type 309/109)。Jetsam 事件报告(JetsamEvent-*.ips,bug_type 298)和卡顿/stackshot 报告(bug_type 288)的负载结构不同,不含线程或异常对象;crashdx 可以无错误地解析它们,但没有可用事实进行分析,因此会报告无法确定结论。
隐私

crashdx 完全在你的机器上运行,不会进行任何网络调用。崩溃报告、dSYM 及其所有派生内容都保留在本地。这一点很重要,因为 .ips 文件包含可识别身份的数据(crashReporterKey、启动/睡眠唤醒 UUID、设备型号,以及路径中的用户名),因此,如果你在错误报告中附上此类文件,请先清理其中的信息。

安装 / 构建

git clone https://github.com/r00tify/crashdx.git
cd crashdx
swift build -c release

二进制文件会放在 .build/release/ 中。要将它们添加到你的 PATH:

sudo mkdir -p /usr/local/bin
sudo cp .build/release/crashdx .build/release/crashdx-mcp /usr/local/bin/

(或者将它们复制到你已有权限的位置,例如 ~/.local/bin。)

用法

在 macOS 上,崩溃报告位于 ~/Library/Logs/DiagnosticReports/(也可在 Console.app → Crash Reports 中查看);在 iOS 上,前往“设置” →“隐私与安全性” →“分析与改进” →“分析数据”,TestFlight/App Store 崩溃报告则可在 Xcode 的 Organizer 中查看。

crashdx analyze report.ips                           # human-readable summary + diagnosis
crashdx analyze report.ips --json                    # structured AnalysisReport JSON
crashdx analyze report.ips --json --tier full        # ...with every thread included
crashdx symbolicate report.ips --dsym path/to.dSYM   # just symbolicate, print enriched .ips
crashdx --version

--dsym 接受 .dSYM bundle、.xcarchive 或用于递归搜索的目录(可重复指定);--no-spotlight 会跳过基于 Spotlight 的发现,--no-archives 则会跳过 Xcode 的归档目录。运行 crashdx <subcommand> --help 查看完整选项列表。请注意,--json 和 --tier 仅适用于 analyze;symbolicate 始终会输出完整的 .ips。

两个子命令都会自动搜索 Spotlight 和 Xcode 的归档,以查找匹配的 dSYM。对于外部报告(用户提交、TestFlight、CI 构建产物),请使用 --dsym 显式传入该构建的 dSYM。

打印人类可读摘要前,会对取自报告的字符串(进程名称、符号、路径、异常文本)进行转义:C0 控制字符、DEL 和双向文本覆盖字符会显示为 \x0A / \u{202E},而不会直接输出,因此精心构造的报告无法伪造 DIAGNOSIS: 行或篡改终端显示。--json 和 symbolicate 会原样输出报告中的字符串;请将其输出视为数据,而非终端文本。

如果 dSYM 来自不同的构建,系统会拒绝使用它:过时的 dSYM 会产生看似可信、实则错误的符号,这比没有符号更糟。这种情况会报告为 uuid_mismatch(reason 中包含有问题的路径),并与 no_dsym 区分开来,因为两者的解决方法不同:找到与报告构建 UUID 匹配的归档,而不是去寻找一个你可能已经有了的文件。

报告层级

--tier 控制输出报告的内容量;它不会改变诊断结果,诊断始终基于完整报告计算:

层级内容
summary(默认)故障线程(最多 15 帧)、lastExceptionBacktrace,以及诊断结论和按排名排列的假设
standard取消帧数上限,并添加二进制映像、包含应用帧的其他线程,以及 factsConsidered(每个假设背后的证据)
full添加所有剩余线程,包括不包含应用帧的线程
退出代码

0 成功 · 64 用法错误 · 65 无法将输入解析为 .ips 报告,或符号化失败 · 66 文件不存在或不可读。

诊断信息输出到 stderr;stdout 输出报告内容(以及 --help/--version)。

MCP 服务

crashdx-mcp 通过 stdio(以换行符分隔的 JSON-RPC)提供与 crashdx_analyze 和 crashdx_symbolicate 工具相同的处理流程。使用 Claude Code 时:

claude mcp add crashdx /usr/local/bin/crashdx-mcp

配套的 crash-triage Agent Skill 位于 .claude/skills/crash-triage/,介绍如何阅读诊断结果,以及各类崩溃特有的解读陷阱。

作为库使用

将 https://github.com/r00tify/crashdx.git 添加到 Package.swift,依赖 CrashDXCore 产品,并调用 AnalyzePipeline.analyze(path:tier:dsymPaths:useSpotlight:searchArchives:)。两个可执行程序都使用同一个入口。

CrashDXCore 除了 Foundation 外不导入任何内容,但依赖它之前有两个限制值得了解:

  • 仅支持 macOS。 符号化会通过 Foundation.Process 调用 atos/dwarfdump/mdfind,而 iOS 上不存在 Foundation.Process。将此软件包添加到 iOS 目标会在依赖项解析阶段失败,不会进入编译。
  • SwiftPM 会解析整个软件包依赖图,而非单个产品,因此依赖此软件包会将 MCP 服务的依赖项(swift-sdk 及其传递依赖 NIO/collections/log 软件包)固定到你的 Package.resolved 中,即使你从未导入它们也是如此。

AnalysisReport 和诊断模型均为 Sendable,因此并发分析目录中的报告时,表现会符合预期。

项目结构

Sources/CrashDXCore/   Parsing, symbolication, and the diagnosis engine (imports only Foundation)
Sources/crashdx/       CLI
Sources/crashdx-mcp/   MCP server (depends on swift-sdk)
Tests/                 Unit + integration tests, golden snapshots, crash fixtures
corpus/                Ground-truth crash fixtures and their verified findings
docs/DESIGN.md         Diagnosis engine architecture
Scripts/               Fixture scrubbing and the CI privacy guard

测试

swift test

贡献

请参阅 CONTRIBUTING.md。有一条规则比其他规则都重要:提交前必须清理崩溃报告和 dSYM。它们包含设备标识符和你的用户名。Scripts/scrub-fixture.py 可处理 .ips 文件,而 Scripts/check-fixtures-scrubbed.sh(也会在 CI 中运行)会提示是否有内容漏网。

安全与隐私

crashdx 不会进行网络调用;任何内容都不会离开你的设备。有关漏洞报告,以及 .ips 文件在分享前实际包含哪些内容,请参阅 SECURITY.md。

许可证

MIT。请参阅 LICENSE。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。