You need to enable JavaScript to run this app.
导航
崩溃监控
最近更新时间:2024.08.14 14:18:06首次发布时间:2022.06.20 11:34:32

Parfait 内部已集成 Google Crashpad 作为崩溃收集工具,可适配 Windows 及 MacOS 平台。此外,以 Crashpad 为基础,还拓展开发出了 Windows VEH、post handler 以及MacOS 沙盒监控等功能。

注意

仅支持捕获C/C++ native异常。不支持其它语言异常(除非该异常能引起Native异常)。

注意事项

  • 由于崩溃监控以进程为维度,SDK最好不要接,宿主进程接即可。
  • 初始化Parfait SDK。持有初始化成功的parfait_wrapper_ptr指针。
  • 提供一个有权限操作的路径,用于存储崩溃报告。

步骤一:选择崩溃监控方式

您可以选择Parfait的崩溃监控,或者使用自带的崩溃监控。

崩溃监控方式

优点

缺点

Parfait崩溃监控

  • 一键式生成上传崩溃报告。
  • 崩溃后立刻上传报告,时效性最好。
  • 相较于原始的Crashpad,拓展了更多能力,如Windows堆破坏、abort、purecall监控;macOS沙盒监控等等。

非Native用户需要写桥接代码来开启此能力。

业务自带崩溃监控

非Native用户接入更方便。

  • 需要自行管理崩溃报告(dump文件)的生命周期,Parfait仅负责上传崩溃报告文件。
  • 时效性较差,只能在应用重启后上报崩溃。

说明

Electron/Unreal Engine用户自带Crash Reporter。

步骤二:接入崩溃监控

Parfait崩溃监控

完整流程

  1. 初始化SDK,在全局变量中设置崩溃监控启动参数。
  2. 调用InitCrashServer/InitCrashServerOnWin,确保返回值为true。
  3. 发生crash后,crash文件存储路径下会生成.dmp结尾的文件。
    • mac/linux:在dump_dir(业务方传入)/pending文件夹下
    • windows:在dump_dir(业务方传入)/reports文件夹下
  4. 发生crash后,应用退出,crash报告直接上传到APMPlus平台。
  5. 两三分钟后,可以在APMPlus PC平台的Crash列表查看该crash。
  6. 如果没有上传相关符号表,堆栈表示为unknown。在符号表 - 仅缺失 - 上传页面上传相关符号表,再单击详情页的重新解析,crash堆栈解析成功。

接入步骤

  1. 设置崩溃监控启动参数。
    初始化SDK时,可以设置GlobalEnv的参数,修改崩溃监控的默认行为。

    参数

    是否必填

    含义

    默认值

    UseMainProcessParamAsChildProcessExceptionUploadParam

    选填

    允许已接入崩溃监控,但未初始化Parfait SDK的子进程使用主进程的参数上报崩溃。一旦子进程初始化Parfait SDK,将会使用他们自己的参数。必须在主进程初始化崩溃监控前设置。子进程调用此API无效。

    子进程使用自己的参数上报报告

    IrreplaceableExceptionMonitor

    选填

    调用后,崩溃监控不可被其他模块的崩溃监控顶替,默认可被顶替。必须在主进程初始化崩溃监控前设置。子进程调用此API无效。1.4.2.0~版本开始支持。

    崩溃监控可被顶替

    IgnoreExceptionInChildProcess

    选填

    调用后,子进程崩溃不生成崩溃报告。默认生成崩溃报告。此接口仅在mac平台上生效,必须在主进程初始化崩溃监控前设置。子进程调用此API无效。1.4.2.0~版本开始支持。

    mac子进程生成崩溃报告

  2. 初始化SDK。

    主进程

    调用InitCrashServer方法初始化主进程的崩溃监控,dump_dir为存放崩溃报告的绝对路径,同步返回Crashpad初始化结果。

    /**
     * 仅Mac/Linux可用
     * 初始化Crashpad,并为调用进程注册Crash监听
     * 调用进程的子进程会自动注册Crash监听,不需要调用InitCrashClient方法
     * @param  dump_dir crash文件存储路径,绝对路径,最后以‘/’结尾
     * @return  Crashpad是否初始化成功
     */
     bool ParfaitWrapperBase::InitCrashServer(const char* dump_dir);
     
    /**
     * 仅Mac/Linux可用
     * 在parfait初始化前初始化Crashpad,参数和上面一致
     * 初始化parfait后仍需要调用上述的InitCrashServer,才能上传Crash文件
     * ⚠️应用生命周期内,Crashpad只会初始化一次,参数以第一次初始化为准
     */
     static bool ParfaitWrapperBase::InitCrashServerEarly(const char* dump_dir); 
    
    示例代码: 二选一即可
    //场景1(推荐): 主进程在初始化parfait sdk后注册崩溃监控
    bool init_res = parfait_wrapper_ptr->InitCrashServer("your_absolute_path/");//传入绝对路径
    
    //场景2: 适用于特殊场景,主进程需要在初始化parfait sdk之前就注册崩溃监控
    bool init_res = parfait::ParfaitWrapperBase::InitCrashServerEarly("your_absolute_path/");
    parfait sdk 初始化代码
    //仅提醒parfait上传Crash文件,dmp_path务必保持和InitCrashServerEarly保持一致
    parfait_wrapper_ptr->InitCrashServer("your_absolute_path/");
    

    子进程

    • 同一进程组:主进程初始化崩溃监控后,该进程的所有子进程会自动注册崩溃监控,不需要再进行别的操作。请注意主进程必须在拉起子进程前初始化崩溃监控!
    • 不同进程组:需要主动为子进程重新注册崩溃监控,流程和主进程一样。

    沙盒应用

    沙盒应用需要额外操作。

    1. 选定一个identifier。例如,macdemo

      • 格式为xxxx
      • 中间可用.作分隔,如xxx.xxxx
      • 只能是字母组合。
      • identifier不要太长。
    2. 主应用配置entitlements。

      • 新增两个用于支持bootstrap服务的key。
        • com.apple.security.temporary-exception.mach-lookup.global-name
        • com.apple.security.temporary-exception.mach-register.global-name
          图片
      • 类型是String, Value是com.bytedance.parfait.child_port_handshake.$identifier
        图片
        这里key的Value都是com.bytedance.parfait.child_port_handshake.macdemo
    3. parfait_crash_handler二进制签名。
      AppStore要求应用内所有的macho文件都必须开启沙箱才能过审,所以您需要为parfait_crash_handler二进制签名。parfait_crash_handler二进制位于Parfait.framework/Versions/A/Resources目录下。
      执行以下命令,为parfait_crash_handler开启sandbox, 并继承主应用的权限。

      codesign --force --sign "$YOUR_INDENTITY" --entitlements "$PATH/parfait_crash_handler.entitlements" "$PATH/parfait_crash_handler"
      
      parfait_crash_handler.entitlements.zip
      1.03KB
    4. 验证parfait_crash_handler签名是否成功。

      codesign -dvvv --entitlements :- $PATH/parfait_crash_handler
      
    5. 修改代码。
      在初始化崩溃监控之前, 调用SetCrashServerIdentifierOnMac注入identifier信息。

      #define CRASH_FILE_DIR "crash_databse"
      
      std::string homeDir = getenv("HOME");
      homeDir.append(CRASH_FILE_DIR);
      parfait::ParfaitWrapperBase::SetCrashServerIdentifierOnMac("macdemo");
      bool res = parfait_wrapper_ptr->InitCrashServer(homeDir.c_str());
      if (res)
          printf("Init Crashpad success");
      else
          printf("Init Crashpad failed");
      
    6. 验证是否调用成功。

      • InitCrashServer API 返回true或者打开parfait debug log,控制台会输出Crashpad init success!
      • 非调试模式下,发生崩溃后崩溃会立即上报到APMPlus平台。
    7. 提审时说明原因。
      因为使用了Temporary Exception Entitlements,提审时需要说明使用原因。此权限是为了注册崩溃监控,崩溃监控用于收集程序运行情况。
      图片

  3. 上报Crash。
    parfait会在崩溃后立即上传报告,如上报失败,报告会留到下次启动再传。业务无需关心上报时机。

业务自带崩溃监控

完整流程

  1. 业务自带崩溃监控,发生crash,生成.dmp文件。
  2. 重启进程,初始化Parfait SDK。
  3. 调用UploadCrashFile上报.dmp文件,上报成功后,业务方自行删除.dmp文件。
  4. 两三分钟后,可以在APMPlus PC平台的Crash列表查看该crash。
  5. 如果没有上传相关符号表,堆栈表示为unknown。在符号表 - 仅缺失 - 上传页面上传相关符号表,再单击详情页的重新解析,crash堆栈解析成功。

接入步骤

  1. 初始化一个CrashUploadRequest类型的request对象。
  2. 实现request对象中的result callback,用于获取上报结果。
  3. 设置request对象参数,传入.dmp结尾文件路径和应用版本号等信息。
  4. 调用UploadCrashFile异步上传崩溃报告,上传结束后Parfait通过result callback返回上报结果。用户自行处理崩溃报告。
    struct ParfaitWrapperBase::CrashUploadRequest {
        const unsigned int struct_size = sizeof(CrashUploadRequest);
        
       /**
        * 返回dump文件上传结果,回调是在子线程
        * @param dump_path 本次上传文件的路径
        * @param is_success 上传是否成功
        */
        virtual void result(const char* dump_path, bool is_success) = 0;
        const char* dump_path = nullptr;          // 必填,dmp文件绝对路径
        long long crashtime_s = 0;                // 崩溃时间,单位s,不填默认unknown
        const char* process_name = nullptr;       // 崩溃进程名,不填默认为当前实例的进程名
        const char* app_version = nullptr;        // 崩溃版本号,不填默认为当前版本号
        const char* app_minor_version = nullptr;  // 崩溃小版本号,不填默认为当前小版本号
        const char* build_id = nullptr;           // 崩溃build_id,不填默认为当前build_id
        const char* session_id = nullptr;         // 崩溃session_id,不填默认为空
        const char* crash_scene = nullptr;        // 崩溃场景,不填默认为空
     };
       
     /**
      * 上报崩溃.dmp文件到slardar,可同步调用,上传结果通过result方法异步返回
      * @param request 请求参数,具体细节参考上面的结构体说明
      */
    void ParfaitWrapperBase::UploadCrashFile(const CrashUploadRequest* request);
    
    示例代码:
    // 1.实现result callback
    struct CrashUploadRequest: parfait::ParfaitWrapperBase::CrashUploadRequest {
        void result(const char* dump_file, bool is_success) {
            if (is_success)
                printf("file: %s is uploaded successfully\n", dump_file);
            else
                printf("failed to upload file: %s\n", dump_file);
            delete this;
        }
    };
    // 2.注入上报参数
    auto request = new CrashUploadRequest();
    request->dump_path = "/xxxx/crash_file.dmp";
    request->app_version = "1.0.0";
    request->process_name = "test";
    request->crashtime_s = xxxxxxx;
    request->crash_scene = "login";
    // 3.上传crash文件
    parfait_wrapper_ptr->UploadCrashFile(request);
    

步骤三:消费Crash

Crash查询

崩溃上传后,您可以在崩溃趋势页面,查看已上报的崩溃。

符号表上传

查看部分上传符号表,请参见崩溃趋势
崩溃报告中只会记录堆栈地址,要查看堆栈符号,需要上传符号表。
PC端监控会收集Windows和Mac的系统符号表,业务仅需要上传业务符号表。
APMPlus通过模块名(exe/dll/dylib/so等文件的名字)+ uuid(每次编译唯一)匹配符号表。如果崩溃详情页没有符号信息,代表没有上传模块对应的符号表。

(可选)步骤四:接入其他功能

崩溃附加Filter Context

上下文信息用于筛选Crash信息。筛选条件为键值对。

  1. 初始化SDK时,实例环境变量可调用ParfaitEnvBuilderBase::AddCrashContext新增CrashContext信息。如果用该实例初始化Parfait Crashpad,Crashpad会带上该实例的CrashContext信息。
  2. 初始化SDK后,可调用ParfaitWrapperBase::AddCrashContext新增或修改当前实例CrashContext信息。
  3. 不同的parfait实例,可以拥有不同的CrashContext信息。如果需要更新Parfait Crashpad中的Crash Context信息,还需要调用RefreshCrashContextInCrashServer方法。
    /**
     * 添加崩溃的Context信息,用于单点展示以及崩溃列表页面的过滤
     * @param  key 键
     * @param  value 值
     */
    ParfaitEnvBuilderBase& ParfaitEnvBuilderBase::AddCrashContext(const char* key, const char* value);
    /**
     * 三端可用,更新当前实例的CrashContext,用于单点展示以及崩溃列表页面的过滤,上传崩溃文件/抓获崩溃时附带上传
     * 如使用了parfait的崩溃监听服务,需要再调用RefreshCrashContextInCrashServer方法更新CrashServer中的CrashContext
     * @param  key key值,不可为空
     * @param  value value值,不可为空
     */
     void ParfaitWrapperBase::AddCrashContext(const char* nonullable_key, const char* nonullable_value);
    /**
     * 三端可用,更新Crashpad的CrashContext为当前实例的CrashContext
     */
     void ParfaitWrapperBase::RefreshCrashContextInCrashServer();
    

崩溃附加场景信息

添加场景信息后,您可筛选目标场景下的Crash。

struct ParfaitWrapperBase::CrashAnnotation {
    const char* scene = nullptr; // 当前场景
};
   
/**
 * 三端可用,崩溃后parfait crashpad会自动带上crash annotation字段信息,所有进程共享字段,可随时调用
 * e.g. 应用刚启动,设置scene为launch
 */
static void ParfaitWrapperBase::SetCrashAnnotation(const CrashAnnotation* annotation);

崩溃后回调

注意

  • 暂时只有windows系统支持崩溃后回调。
  • 只有Parfait崩溃监控支持此回调,业务自带的崩溃监控不支持此回调。
  • 1.3.0.0及之后的SDK版本支持崩溃后回调。
  • 崩溃后回调请谨慎操作,如果有不当操作可能会导致二次崩溃。

崩溃后,业务希望在客户端做一些操作,可以调用此方法。crashpad上报完crash后,会在崩溃进程调用传入的nonnullable_crash_callback方法。

/**
 * @brief Type of crash callback func ptr.
 */
typedef void (*CrashCallback)();


/**
 * @brief Only avaliable on Windows temporarily.
 * Sets the Crash Callback object in current process. This callback will be called after crash. Be careful to use
 * since some operations would result in second crash.
 *
 * @param nonnullable_crash_callback callback
 */
static void ParfaitWrapperBase::SetCrashCallback(const CrashCallback nonnullable_crash_callback);

崩溃后拉起业务UI处理进程

注意

  • 暂时只有windows系统支持崩溃后拉起业务UI处理进程。
  • 只有Parfait崩溃监控支持此回调,业务自带的崩溃监控不支持此回调。

在崩溃后,业务希望有更多的业务交互,可以调用此方法。parfait支持功能有:

  • (Required) 新起一个业务自定义进程。

  • (Optional) 拷贝当前崩溃dmp文件到业务指定目录下,同时该文件路径作为参数传递给自定义进程。

  • (Optional) Parfait是否需要依据自定义进程返回结果来决定崩溃的后续处理(上传/不上传直接删除),同时可以指定Parfait的最长等待时间。自定义进程的返回结果必须是以下三种:

    /**
     * @brief List of legal authorization value returned by crash post handler.
     */
    enum CrashPostHandlerResult {
      // Unknown. The dmp file will be deleted.
      Crash_Post_Handler_Result_Unknown     = 0,  
      // Allows to upload dmp file.
      Crash_Post_Handler_Result_Upload_Dump = 1,  
      // Doesn't allow to upload dmp file. The dmp file will be deleted.
      Crash_Post_Handler_Result_Delete_Dump = 2, 
    }
    
    struct CrashPostHandlerData {
      const unsigned int struct_size = sizeof(CrashPostHandlerData);
      // 必填,业务自定义处理进程地址,崩溃后会自动拉起
      const char* process_path = nullptr;  
      // 选填,拷贝dmp的目的目录,parfait会自动copy dmp到此目录下,并传递dump_path参数给自定义处理进程
      const char* dump_copy_dir = nullptr; 
      // 选填,如果为‘true’,parfait会等待自定义处理进程返回授权结果再来处理当前崩溃,等待最长时间为wait_result_timeout_s,超时后直接删除。可返回结果看CrashPostHandlerResult详情。默认'false'直接上传。
      bool wait_result_for_dump_handle = false;
      // 选填,如果wait_result_for_dump_handle为true,parfait等待的最长时间
      unsigned long wait_result_timeout_s = 120;
    } 
    
    /** 在崩溃监听进程内设置业务自定义处理进程,崩溃后parfait会帮忙拉起相应进程,并传入dmp地址(如有)
     * ⚠️注意:暂时只支持windows
     * @param  data 业务自定义处理进程相关信息
     * @return  是否成功设置
     */
     static bool ParfaitWrapperBase::SetCrashPostHandlerInCrashServer(const CrashPostHandlerData* data);