- 人工智能
- 大模型
- 编程语言
- 编译器
- 标准库
- 算子库
- 模型推理服务
- 模型量化
【免费下载链接】mojo
The Modular Platform (includes MAX & Mojo)
本文围绕 Modular 平台(MAX 与 Mojo 编译器/运行时)中的崩溃报告(Crash Reporting)基础设施展开,系统讲解其在 Support/lib/CrashReporting/CrashReporting.cpp 中围绕 Chromium Crashpad 库构建的封装实现。读者将掌握崩溃报告从进程初始化、handler 定位、崩溃数据库落盘到上传的完整链路,理解crash_reporting.*配置项与MODULAR_*环境变量的实战用法,并了解如何通过仓库自带的测试工具验证崩溃转储的生成。
一、崩溃报告库的定位:一个面向 Crashpad 的轻量封装
根据 Support/docs/CrashReporting.md 的说明,这个库的本质是一个围绕Crashpad(Chromium 项目维护的跨平台崩溃捕获与上报库)的包装层,专门用于为 Modular 平台的各类可执行程序提供崩溃报告能力。整个封装的核心全部集中在两个文件中:
- 头文件:Support/include/Support/CrashReporting/CrashReporting.h —— 对外暴露 4 个 API;
- 实现文件:Support/lib/CrashReporting/CrashReporting.cpp —— 完成 handler 定位、数据库初始化、注解装配与 Crashpad 客户端启动。
头文件的注释里强调了一个重要约束:该库不属于 MSupport CMake 目标的一部分,使用方必须显式链接名为MCrashReporting的目标。在 Bazel 构建系统中则对应独立的CrashReporting库目标,定义于 Support/BUILD.bazel,它依赖:Base、:Configuration、//Config以及@crashpad//:client——从依赖关系可以推断,配置模块(Configuration)是解析崩溃报告各项设置的前提。
二、核心组件:handler、崩溃数据库与上报 URL
崩溃报告机制中三个最关键的实体,在 CrashReporting.cpp 的开头常量与实现中被明确定义:
| 组件 | 默认值 / 生成规则 | 源码证据 |
|---|---|---|
| Handler 可执行文件 | modular-crashpad-handler | kHandlerProgramName常量,第 41-42 行 |
| 崩溃数据库目录 | modular data 目录下的crashdb子目录 | getCrashDatabasePath返回dataFolder / "crashdb",第 46-49 行 |
| 上报 URL 默认值 | https://crash-reporting.modular.com | kDefaultURL常量,第 43-44 行 |
Handler 的职责与查找顺序
Handler(即modular-crashpad-handler)运行在主程序(如 Mojo driver)旁边:当主进程崩溃时,Crashpad 机制会让 handler 在崩溃现场接管,检查已崩溃进程的内存状态并生成崩溃报告(dmp 文件)。因此定位 handler 可执行文件是初始化的第一步,由getCrashpadHandlerPath完成,查找顺序为:
- 若配置中指定了
crash_reporting.handler_path,直接采用(配置优先级最高); - 否则通过
llvm::sys::findProgramByName在系统PATH中查找名为modular-crashpad-handler的可执行文件; - 都找不到时返回错误
"unable to locate crashpad handler executable"。
这一行为被 AsyncRT/test/crash-reporting/handler-path-from-config.mlir 等测试用例逐一验证:当通过配置指定非标准路径的 handler 时,crash-report-path-info工具输出的正是配置中给出的路径。
崩溃数据库:上传前的本地暂存区
getCrashDatabasePath的逻辑非常简单——在 Modular 数据目录下追加crashdb。数据目录本身由Config::getModularDataFolderPath()决定,其在 Support/include/Support/Configuration.h 中声明的优先级如下:
- 设置了
MODULAR_HOME时:$MODULAR_HOME - 设置了
MODULAR_DERIVED_PATH时:$MODULAR_DERIVED_PATH - 设置了
TEST_TMPDIR时:$TEST_TMPDIR $HOME/.modular目录已存在时:$HOME/.modular- 否则遵循 XDG Base Directory 规范:
$XDG_DATA_HOME或默认$HOME/.local/share/modular
崩溃转储会先写入crashdb下的pending或completed目录(取决于是否成功上传),这一点在测试的 FileCheck 断言./crashdb/{{pending|completed}}/{{.*}}.dmp中可以看到。
三、对外 API 一览
CrashReporting.h 暴露了 4 个函数,构成了整个封装的使用面:
| 函数 | 作用 | 关键参数 |
|---|---|---|
getCrashpadHandlerPath(Config *settings) | 定位 Crashpad handler 可执行文件路径 | settings:配置对象,可选 |
getCrashDatabasePath(dataPath) | 计算崩溃数据库目录(dataPath/crashdb) | dataPath:modular 数据目录 |
initCrashpadForProgram(program, machineID, sessionID, settings) | 为当前进程初始化崩溃上报 | program:固定程序名(如"mojo");machineID/sessionID:用于与使用事件关联 |
generateNonFatalDump() | 在不终止进程的前提下生成崩溃转储 | 无;须先调用过initCrashpadForProgram |
头文件注释对program参数的约束值得注意:它用于服务端聚类(clustering)与崩溃分析,应使用简单固定的名称,例如"mojo"。machineID与sessionID则用于把崩溃报告与使用事件(usage events)关联起来。
四、初始化流程:从配置到 Crashpad 启动的完整链路
initCrashpadForProgram是崩溃报告的入口,其内部委托给tryInitCrashpad。从 CrashReporting.cpp 的实现可以梳理出完整流程:
Step 1:确定崩溃数据库路径。调用Config::getModularDataFolderPath()得到数据目录,再通过getCrashDatabasePath拼接出crashdb路径。
Step 2:定位 handler。调用getCrashpadHandlerPath(settings),失败则报错"while locating crashpad handler: ..."。
Step 3:确定上报 URL。优先读取配置crash_reporting.url,为空则使用默认值https://crash-reporting.modular.com。随后在 URL 末尾追加/<program>,即每个程序拥有独立的接收端点。源码中以assert(!url.empty())保证 URL 非空。
Step 4:初始化崩溃数据库并强制启用上传。通过crashpad::CrashReportDatabase::Initialize打开(或创建)数据库,然后检查GetUploadsEnabled():若上传未启用,则调用SetUploadsEnabled(true)将其打开。注释说明这在多数场景下只是读取现有设置而不产生变更。
Step 5:装配注解(annotations)。崩溃报告中会携带以下关键元数据,用于服务端索引与分析:
annotations["program"] = program; // 程序名 annotations["version"] = getModularVersionString(); // 平台版本 annotations["machineid"] = machineID; // 机器标识 annotations["sessionid"] = sessionID; // 会话标识其中version来自 Config/lib/Version.cpp,而machineid/sessionid必须与使用遥测(usage telemetry)通道保持一致(详见下文第六节)。
Step 6:启动 Crashpad 客户端。调用crashpad::CrashpadClient::StartHandler,参数中值得注意的几点:
metrics_dir直接复用数据库路径;- 附加参数
{"--no-rate-limit"}表示关闭上传限流; restartable = true:handler 可被重新拉起;asynchronous_start = false:同步启动,保证初始化完成后 handler 已就绪。
若StartHandler返回失败,则整体返回错误"crashpad failed to start handler"。
失败降级策略:initCrashpadForProgram本身不抛出异常,而是把错误信息写入llvm::errs()后静默返回,日志形如"Failed to initialize Crashpad. Crash reporting will not be available. Cause: ...",保证崩溃报告不可用时不影响主程序正常运行。
五、配置项、配置文件与环境变量实战
5.1 INI 风格配置文件
配置通过modular.cfg提供,其路径由Config::getConfigFilePath()计算(通常是$XDG_CONFIG_HOME/modular/modular.cfg或$HOME/.modular/modular.cfg,Support/include/Support/Configuration.h)。解析器实现在 Support/lib/Configuration.cpp,关键规则:
- 支持
[section]分节,节内键值被展开为section.key形式存储; - 支持
#和;行尾注释; - 节名与属性名大小写不敏感(统一转小写);
- 键值格式为
key = value,形如key=value(无空格)也可接受。
崩溃报告相关的三个配置键汇总:
| 配置键 | 含义 | 默认值 |
|---|---|---|
crash_reporting.enabled | 是否启用崩溃报告 | 跟随telemetry.enabled(见下文) |
crash_reporting.handler_path | 指定 Crashpad handler 可执行文件绝对路径 | 空(此时从 PATH 查找modular-crashpad-handler) |
crash_reporting.url | 崩溃报告上传 URL | https://crash-reporting.modular.com |
一个完整的配置示例(与仓库测试 AsyncRT/test/crash-reporting/crash.mlir 中的写法一致):
[crash_reporting] url = http://invalid.若同时指定 handler 路径(见 AsyncRT/test/crash-reporting/handler-path-from-config.mlir):
[crash_reporting] handler_path = /absolute/path/to/nonstandard-handlercrash_reporting.enabled的判定逻辑位于 Support/lib/Telemetry/TelemetryContext.cpp:getValueAsBool("crash_reporting.enabled", isTelemetryEnabled(settings))——即默认跟随遥测总开关,而telemetry.enabled在生产构建(MODULAR_PRODUCTION)下默认为true,非生产构建默认为false。
5.2 环境变量
在 Bazel 测试与工具目标中,崩溃报告还可以通过环境变量驱动。以 AsyncRT/tools/crash-test-dummy/BUILD.bazel 为例:
MODULAR_CRASH_REPORTING_ENABLED=true MODULAR_CRASH_REPORTING_HANDLER_PATH=$(rootpath @crashpad//:modular-crashpad-handler) MODULAR_CRASH_REPORTING_URL=https://crash-reporting.dev.modular.com其中MODULAR_CRASH_REPORTING_HANDLER_PATH与配置文件中的crash_reporting.handler_path语义一致,且配置系统支持运行时覆盖(setGlobalValue机制,见 Configuration.cpp),环境变量注入的值拥有最高优先级。
六、与使用遥测(Telemetry)通道的协同设计
崩溃报告并非孤立功能,它与遥测系统紧密耦合,体现在两个层面:
其一,ID 共享。崩溃报告与使用遥测必须携带相同的machineid/sessionid,才能把崩溃事件与使用事件在服务端关联(join)。Telemetry::createLocalIDs()(TelemetryContext.cpp)被设计为进程内记忆化(memoized)的静态局部变量,任何调用者拿到的都是同一对 ID:机器 ID 由本机 MAC 地址列表经 BLAKE3 哈希后再做 URL-safe Base64 编码生成;会话 ID 在此基础上混入随机字节后再次哈希编码。注释明确写道:"the crash reporting and usage telemetry lanes must share machineid/sessionid for their events to be joinable"。
其二,状态上报。每次进程启动时,遥测系统会发出program.initialized事件,其中携带crash_reporting.enabled属性(TelemetryContext.cpp)。注释解释了这一设计的精妙之处:该事件始终记录崩溃通道是否开启,而不以崩溃通道是否开启作为事件本身是否发出的条件——这样,主动关闭崩溃报告的用户仍会计入采纳统计,而"本就不可能上报崩溃"的会话永远不会被误判为"没有崩溃"("a session that could not have reported a crash must never read as a crash-free one")。
七、Init 集成:谁在何时启用崩溃报告
崩溃报告的初始化并非由每个可执行程序自行调用,而是收敛在统一的上下文创建入口Init::createContext中。从 Init/lib/Init.cpp 可以看到分支逻辑:
bool crashReportingEnabled = Telemetry::isCrashReportingEnabled(settings); // 非生产构建且未启用崩溃报告:注册开发用信号处理器 if (!isProductionBuild() && !crashReportingEnabled) Init::registerDevelopmentSignalHandler(programName); // 启用了崩溃报告(且未被强制关闭):初始化 Crashpad else if (!options.forceDisableCrashReportingEnabled() && crashReportingEnabled) { const auto &localIDs = Telemetry::createLocalIDs(); initCrashpadForProgram(programName, localIDs.machine, localIDs.session, &settings); }两种路径互为补充:
- 生产构建 / 已启用崩溃报告:走 Crashpad 通道,由独立 handler 进程接管崩溃捕获与上传;
- 非生产构建且未启用:注册开发信号处理器
registerDevelopmentSignalHandler。根据 Init/include/Init/DevelopmentSignalHandler.h 的说明,它覆盖 SIGSEGV、SIGABRT、SIGFPE、SIGILL、SIGBUS、SIGTRAP、SIGSYS 等信号,捕获信号码、故障地址与进程信息后链入 LLVM 的信号处理设施输出堆栈。
此外Init::Options::withForceDisableCrashReporting()(Init/include/Init/Init.h)提供了程序化强制关闭的逃生阀,适合那些不希望进程被 Crashpad 侵入的嵌入场景。头文件对此有重要警告:initCrashpadForProgram会对进程环境做侵入性修改(移除既有信号处理器并注册新处理器、派生子进程可能干扰 SIGCHLD 处理、在 Darwin 上修改进程级异常端口等),因此只应从"合理拥有进程"的代码中调用,而不应从无法掌控进程其余部分的库代码中调用。
八、非致命转储与调试工具
8.1 generateNonFatalDump:模拟崩溃
generateNonFatalDump()的实现只有一行——CRASHPAD_SIMULATE_CRASH()。它触发 Crashpad 的模拟崩溃路径:在进程不真正终止的前提下,为当前进程生成一份崩溃转储。这在测试与故障注入场景中非常有用。
仓库提供了两个配套工具来验证崩溃报告行为:
- crash-test-dummy(AsyncRT/tools/crash-test-dummy/crash-test-dummy.cpp):一个"崩溃试验桩"。带
-simulate参数时调用generateNonFatalDump()生成非致命转储;不带参数时直接std::abort()制造真实崩溃; - crash-report-path-info(AsyncRT/tools/crash-report-path-info/crash-report-path-info.cpp):路径查询工具,
-get crashdb输出崩溃数据库路径,-get crashpad-handler输出实际解析到的 handler 路径,用于诊断配置是否正确。
8.2 测试矩阵:行为可验证
AsyncRT/test/crash-reporting/ 目录下的 lit 测试用 FileCheck 断言逐一验证了崩溃报告的各个行为分支,是理解该库行为的绝佳参考:
| 测试文件 | 验证点 | 关键断言 |
|---|---|---|
| crash.mlir | 真实崩溃(abort)后生成 dmp | ./crashdb/{{pending|completed}}/{{.*}}.dmp |
| simulated-crash.mlir | -simulate非致命转储同样落盘 | 同上 |
| crashdb-default.mlir | 崩溃库路径位于$MODULAR_HOME/crashdb | {{.*}}home{{[\\/]}}crashdb |
| default-find-handler.mlir | 默认通过 PATH 找到 handler | {{.*}}modular-crashpad-handler{{(\.exe)?}} |
| handler-on-path.mlir | 自定义 PATH 中的 handler 被解析 | {{.*}}fake-path{{[\\/]}}modular-crashpad-handler |
| handler-not-found.mlir | handler 缺失时返回可读错误 | could not determine crashpad handler path: {{.*}} |
| handler-path-from-config.mlir | 配置中的handler_path优先于 PATH | {{.*}}nonstandard-handler |
这些测试同样展示了标准的实验流程:先用MODULAR_HOME指向一个临时目录(隔离数据与配置),写入modular.cfg,再运行崩溃工具,最后在临时目录下检查crashdb中是否出现.dmp文件。这套流程完全可以复用到真实环境的崩溃报告排障中。
九、总结:崩溃报告链路的完整视图
把全文串起来,Modular 平台的崩溃报告机制形成了一条清晰的处理链:
进程启动 → Init::createContext → Telemetry::isCrashReportingEnabled(settings) 判定开关 → Telemetry::createLocalIDs() 生成 machineID/sessionID(与遥测共享) → initCrashpadForProgram → 解析 modular.cfg / 环境变量(handler_path、url、enabled) → 定位 modular-crashpad-handler(配置 > PATH) → 计算 $MODULAR_HOME/crashdb 并初始化 Crashpad 数据库 → 装配 program/version/machineid/sessionid 注解 → 同步启动 CrashpadClient(--no-rate-limit,可重启) → 进程崩溃时 handler 接管 → 生成 .dmp → 暂存 crashdb → 上传至 {url}/{program}这一设计的关键工程取舍值得借鉴:失败静默降级(初始化失败只写 stderr,绝不阻塞主程序)、ID 跨通道复用(崩溃与遥测可关联分析)、配置多级覆盖(配置文件 → 环境变量 → 运行时 override)以及测试先行(用 lit 测试把 handler 定位、配置优先级、转储落盘等每个行为都固化为可回归的断言)。对于希望为自家产品接入第三方崩溃收集库的团队而言,Support/lib/CrashReporting 这套"薄封装 + 配置抽象 + 统一初始化 + 可测试性"的组合拳是一个可以直接参考的实现范本。
- 人工智能
- 大模型
- 编程语言
- 编译器
- 标准库
- 算子库
- 模型推理服务
- 模型量化
【免费下载链接】mojo
The Modular Platform (includes MAX & Mojo)
相关推荐
Crashpad 崩溃报告系统完整配置指南
Crashpad 崩溃报告系统完整配置指南 让我们来了解如何快速部署和使用Crashpad这个强大的崩溃报告系统。Crashpad是一个跨平台的崩溃报告库,能够
可观测性开发工具如何快速入门视频分析?Awesome-Deep-Learning-for-Video-Analysis项目新手教程
如何快速入门视频分析?Awesome Deep Learning for Video Analysis项目新手教程 视频分析是计算机视觉领域的热门方向,结合深度
PyGaze眼动追踪工具箱:开源跨平台实验编程的终极指南
PyGaze眼动追踪工具箱:开源跨平台实验编程的终极指南 PyGaze是一款开源跨平台的眼动追踪实验编程工具箱,旨在帮助研究人员以最小的努力实现专业的眼动追踪实
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考