• 1 使用说明
  • 2 CrashSight组件下载
  • 3 CrashSight接入
  • 4 CrashSight上报机制及数据字段
  • 5 功能接口
  • 6 参考代码
  • 7 功能测试验证
  • 8 CrashSight测试及影响
  • 9 上传符号表
  • 10 查看dll版本号
  • 11 附录

PC端SDK接入流程

本文是介绍PC SDK使用的详细文档,包含了基本的接入流程和高级的接口使用介绍。
如果想快速接入,验证平台和SDK功能,建议查看项目菜单中的“接入引导”。引导中已经按项目具体的信息(平台,引擎,国内/海外,AppID)生成了针对此项目的初始化代码,可以直接复制使用。如下图所示:

项目创建:公司外部项目支持自助创建项目,但有免费试用时长。公司内部项目,企业微信联系“CrashSight小助手”开通。

1 使用说明

异常捕获上报服务CrashSight SDK PC版,接入时,游戏客户端需要自己加载异常捕获的dll,并传入用户id及游戏版本等参数。 下表中列出了国内服及海外服的web地址及上报地址。用户可以选择qq登录或者企业微信登录:

版本区分Web地址上报地址(配置文件中需要)
国内服https://crashsight.qq.compc.crashsight.qq.com
海外服https://crashsight.wetest.netpc.crashsight.wetest.net

PS:海外版本及国内版本完全相同,只需要修改不同上报地址就可以上报到指定区域(注意相应的appid及key也需要对应做修改,否则服务器会拒绝)

2 CrashSight组件下载

新版CrashSight主要提供64位版本,32位版本和64位版本相比除文件名称不同外,功能及用法都是一致的。目前提供的64位TQM客户端版本包括三个文件:

文件名放置的位置功能说明
CrashSight64.dll与游戏进程exe放置相同目录负责收集Crash信息
TQM64文件夹与游戏进程exe放置相同目录负责上传Crash信息
GameBabyConfig64.dat与游戏进程exe放置相同目录负责提供配置信息

以某端游接入CrashSight为例,文件位置(打包文件路径中_Data\Plugins\x86_64下)相对关系如下图:

3 CrashSight接入

在平台成功创建项目后,在侧边栏的“接入引导”中,可以下载到对应的SDK。如下图所示:

由于本SDK采用客户端直接加载dll的方式,具体接入方式如下:

  1. 登录web端注册项目,项目类型选择PC,注册成功后会生成appid(如:0620edc732)及app-key(如3edb3c5f-eca4-4033-ba60-5993452dff397,key需要保密)

  2. 将提供的文件按说明放到指定位置,客户端在启动后加载同级目录下的CrashSight64.dll

  3. C++中通过HINSTANCE dllDemo = LoadLibraryA("CrashSight64.dll");加载dll,其他语言使用语言对应的dll加载方式即可.

  4. 声明导出函数(6. 参考代码) typedef void(*CS_InitContext)(const char* id, const char* version, const char* key); 并调用这个函数,将用户账号传递给TQM,参数说明:

参数类型说明
userIdconst char*用户的id 可以为qq或railid,推荐使用const char
versionconst char*当前游戏的版本号,如”3.1.0.13”
keyconst char*注册项目时生成的字符串,用于加密,如:”3edb3c5f-eca4-4033-ba60-59947eb3f397”
  1. 配置GameBabyConfig64.dat(类似xml格式)

    项目打包完成后,找到打包文件路径中_Data\Plugins\x86_64下CrashSight64.dll,在同级目录下创建TQM64/dump目录和GameBabyConfig64.dat文件(新建文本文档,然后修改后缀即可)

    GameBabyConfig64.dat文件需设置三个参数:

    1)游戏进程exe名称,即GameName。

    2)上报地址,即DomainUrl。

    3)项目ID,即文件中的AppId。

<GameBabyConfig __version="1">
	<GameName>Test-Game.exe</GameName>  
	<LobbyName></LobbyName>
	<IgnoreDllCnt>2</IgnoreDllCnt> 
	<IgnoreDlls>TenSLX.dll</IgnoreDlls>
	<IgnoreDlls>Tensafe.dll</IgnoreDlls> 
	<AppId>0620edc732</AppId>    
	<DomainUrl>pc.crashsight.qq.com</DomainUrl> 
</GameBabyConfig>
  1. 当客户端配置完成后,正常启动游戏,发现游戏进程中存在CrashSight64.dll,说明CrashSight加载启动成功。

4 CrashSight上报机制及数据字段

游戏客户端加载dll并调用导出函数后:

  1. 该dll会读取配置文件中的域名上报联网数据。
  2. 用于当dll捕获到崩溃异常时将dll生成的minidmp上报到服务端。

目前CrashSight上报的数据字段如下:

字段说明
userId用户id
appId项目的注册id
MacMac地址
osName操作系统名称
displayCard显卡名称
CpuCpu类型
phyMemAll机器内存
osBit机器位数
resolution分辨率
pidName进程名
bootTime启动时间
IpIp地址

5 功能接口

5.1 错误上报

声明导出函数

typedef void(*CS_ReportException)(int type, const char* name, const char* message, const char* stackTrace, const char * extras);
CS_ReportException theReportException = (CS_ReportException)GetProcAddress(dllDemo, "CS_ReportException");

(代码参考6. 参考代码

其中各项参数在页面展示的具体位置如图所示。自定义日志要求写在dll同目录下的CSLog/customLog.txt中,请每次启动时清理文件中的内容,否则无法正常上传。日志上传体积最大为2M,超出部分会进行截断。expName/expMessage 为问题简要的说明,请限制在128字节以内。(特别的,使用UE引擎,通过UTF8转码的,尽可能限制在96字符内)stackTrace为当前错误的主要内容部分,仅限制在2M内。extras字段为保留字段,当前填写“”即可。

5.2 设置回调

声明导出函数,以及加载函数地址

	typedef void(*CS_SetCrashCallback)(CrashCallbackFuncPtr callback);
	CS_SetCrashCallback theSetCrashCallback = (CS_SetCrashCallback)GetProcAddress(dllDemo, "CS_SetCrashCallback");

其中CrashCallbackFuncPtr callback 可以是用户自己的任何函数实现方式,只要参数符合,声明周期一直存在,不会变为空指针即可。

5.3 回调函数

声明导出函数

	typedef void(*CrashCallbackFuncPtr)(int type, const char* guid);

type为回调的类型,目前仅有crash回调,回调类型都为1. 后续视具体需求,会有进一步优化。 guid为长度64的唯一标识字符串,每次上报均有一个独立的字符串,可以通过拼接 https://{网站域名}/crash-reporting/client-report-id/{APP_ID}/{GID}?pid={平台ID} 得到相关上报的访问URL. 其中CrashCallbackFuncPtr callback 可以是用户自己的任何函数实现方式,只要参数符合,声明周期一直存在,不会变为空指针即可。此处要注意的是,callback函数不可以为类成员的非静态函数,因为类成员的非静态函数默认第一个参数是‘this’

5.4 自定义接口日志

声明导出函数,以及加载函数地址(版本2.0.9新增, 版本2.0.10优化)

enum LogSeverity {
  Log,
  LogDebug,
  LogInfo,
  LogWarning,
  LogAssert,
  LogError,
  LogException
};
typedef void(*CS_PrintLog)(LogSeverity level, const char* tag, const char *format, ...);
CS_PrintLog thePrintLog = (CS_PrintLog)GetProcAddress(dllDemo, "CS_PrintLog");

在程序运行过程当中,可以调用该函数向SDK写入日志,在错误上报,崩溃上报时会将此日志一并上报。日志内容为1M的循环队列。

5.5 自定义KV

声明导出函数,以及加载函数地址(版本2.0.9新增)

typedef void(*CS_SetUserValue)(const char* key, const char* value);
CS_SetUserValue theSetUserValue = (CS_SetUserValue)GetProcAddress(dllDemo, "CS_SetUserValue");

在程序运行过程当中,可以调用该函数向SDK写入自定义KV,用于保存一些关键属性,在错误上报,崩溃上报时会将此KV一并上报。KV内容上限为1M,相同Key,不同Value会做更新。建议KV按照自定义 数据格式文档open in new window中的规范进行设置。

5.6 关闭VEH异常

声明导出函数,以及加载函数地址(版本2.0.10新增)

typedef void (*CS_SetVehEnable)(bool enable);
CS_SetVehEnable theSetVehEnable = (CS_SetVehEnable)GetProcAddress(dllDemo, "CS_SetVehEnable");

设置VEH异常处理状态,默认为开启,与旧版保持一致。 关闭后,仅上报未处理的异常,这个方案可以优化一些异常的捕获,例如:关闭后,会上报throw导致的崩溃(开启则不会),不会误报已经处理的坏内存访问等。需要注意的是,由于UE在引擎层面通过__try __except (宏PLATFORM_SEH_EXCEPTIONS_DISABLED相关区域)对所有的异常进行了捕获,也就不存在‘未处理的异常’。因此,在通过修改引擎代码关闭此捕获前,请勿修改此选项,否则会导致大量异常无法上报。

5.7 开启额外异常捕获

声明导出函数,以及加载函数地址(版本2.0.10新增)

typedef void (*CS_SetExtraHandler)(bool extra_handle_enable);
CS_SetExtraHandler theSetExtraHandler = (CS_SetExtraHandler)GetProcAddress(dllDemo, "CS_SetExtraHandler");

设置额外的异常处理机制,默认为关闭,与旧版保持一致。 开启后,可以捕获上报strcpy_s一类的安全函数抛出的非法参数崩溃,以及,虚函数调用purecall错误导致的崩溃。

5.8 自定义文件数据

崩溃/错误会携带指定目录的文件内容,一般文件内容为日志,因此该功能又称为自定义文件日志。与接口日志不同,文件日志读取指定文件,并没有限定写入文件的内容以及方法。该功能无需主动开启,只要在对应上报触发是,指定目录有文件均会进行上报。

具体的:

崩溃时,会上报.\TQM\CSLog\customLog.txt

错误时,会上报 .\CSLog\customLog.txt. 这里以dll所在目录为基准目录。

5.9 主动上报崩溃

在某些情况下期望主动上报dump可以调用此函数。主要用于上报UE的Fatal错误。

声明导出函数,以及加载函数地址(版本2.0.11新增)

typedef void (*CS_ReportCrash)();
CS_ReportCrash theReportCrash = (CS_ReportCrash)GetProcAddress(dllDemo, "CS_ReportCrash");

FCoreDelegates::OnShutdownAfterError.AddStatic(cs_report_crash);

5.10 指定日志目录

修改默认的日志文件目录,仅在纯净模式下生效。

声明导出函数,以及加载函数地址(版本2.0.11新增)

typedef int (*CS_SetCustomLogDir)(const char* log_path);
CS_SetCustomLogDir theSetCustomLogDir = (CS_SetCustomLogDir)GetProcAddress(dllDemo, "CS_SetCustomLogDir");

5.11 增加错误码

将指定RaiseException的错误码认为是崩溃,进行上报(一般不建议修改,修改前请进行具体的咨询)

声明导出函数,以及加载函数地址(版本2.1.1新增)

typedef int (*CS_AddValidExpCode)(const char* log_path);
CS_AddValidExpCode theAddValidExpCode = (CS_AddValidExpCode)GetProcAddress(dllDemo, "CS_AddValidExpCode");

5.12 主动上报dump

在某些情况下期望主动上报dump可以调用此函数。

声明导出函数,以及加载函数地址(版本2.1.1新增)

typedef int (*CS_ReportDump)(const char* log_path);
CS_ReportDump theReportDump = (CS_ReportDump)GetProcAddress(dllDemo, "CS_ReportDump");

5.13 设置用户Id

声明导出函数,以及加载函数地址(版本2.1.1新增)

typedef int (*CS_SetUserId)(const char* log_path);
CS_SetUserId theSetUserId = (CS_SetUserId)GetProcAddress(dllDemo, "CS_SetUserId");

5.14 上报指定路径下的dump文件

声明导出函数,以及加载函数地址(版本2.1.1新增)

typedef int (*CS_UploadGivenPathDump)(const char* log_path);
CS_UploadGivenPathDump theUploadGivenPathDump = (CS_UploadGivenPathDump)GetProcAddress(dllDemo, "CS_UploadGivenPathDump");

6 参考代码

C++参考代码如下:

typedef void(*CS_InitContext)(const char* id, const char* version, const char* key);
typedef void(*CS_ReportException)(int type, const char* name, const char* message, const char* stackTrace, const char * extras);
int main()
{
	// 加载dll
	HINSTANCE dllDemo = LoadLibraryA("CrashSight64.dll");
	// 执行初始化
	if (dllDemo)
	{
		CS_InitContext theInitContext = NULL;
		theInitContext = (CS_InitContext)GetProcAddress(dllDemo, "CS_InitContext");
		if (theInitContext != NULL)
		{
			theInitContext("userid", "version", "key");
		}
	}
	// 上报一个自定义错误
	if (dllDemo)
	{
		CS_ReportException theReportException = NULL;
		theReportException = (CS_ReportException)GetProcAddress(dllDemo, "CS_ReportException");
		if (theReportException != NULL)
		{
			int type = 1;
			theReportException(type, "exp name", "exp message", "stack", "extras");
		}
	}
	return 1;
}

C# 参考代码如下:

函数声明:
[DllImport("CrashSight64.dll")] 
static extern void CS_InitContext([MarshalAs(UnmanagedType.LPUTF8Str)]string userId, [MarshalAs(UnmanagedType.LPUTF8Str)]string version, [MarshalAs(UnmanagedType.LPUTF8Str)]string key);

[DllImport("CrashSight64.dll")] 
static extern void CS_ReportException (int type, [MarshalAs(UnmanagedType.LPUTF8Str)]string name, [MarshalAs(UnmanagedType.LPUTF8Str)]string message, [MarshalAs(UnmanagedType.LPUTF8Str)]string stackTrace, [MarshalAs(UnmanagedType.LPUTF8Str)]string extras);

函数调用:
CS_InitContext("userId", "version", "key");
CS_ReportException(1, "name", "message","stackTrace", "extras");

7 功能测试验证

接入完成后,请务必验证接入的结果是否符合预期。包括以下几点:

    1. 初始化CrashSight后,联网上报是否正常,具体验证方法如下:
       1. 初始化CrashSight;
       2. 5分钟后,在管理端页面的 异常概览 --> 崩溃趋势 --> 联网设备数 中可以看到统计数值大于等于1.
    2. 游戏发生崩溃,是否能正确上报,具体验证方法如下:
       1. 初始化CrashSight;
       2. 在游戏内由代码 int *a = NULL; a[0] = 1; 触发崩溃。(其他类似的坏内存访问也可以)
       3. 查看TQM64/dump路径下是否有dmp文件生成。如果没有,说明无法成功捕获崩溃,请联系CrashSight开发。
       4. 查看管理端页面 崩溃分析 中,是否有对应时间点的上报。如果3有,4没有,说明没有成功上报。请检查APP ID配置(两个配置文件均要正确),以及APP KEY配置,是否对应且与应用设置中一样。如果配置正确,还无法上报,请联系CrashSight开发。
    3. 游戏发生错误,是否能正确上报(可选),具体验证方法如下:
       1. 初始化CrashSight;
       2. 在游戏内触发CS_ReportException()
       3. 查看管理端页面 错误分析 中,是否有对应时间点的上报。如果没有,说明没有成功上报。请检查APP ID配置(两个配置文件均要正确),以及APP KEY配置,是否对应且与应用设置中一样。如果配置正确,还无法上报,请联系CrashSight开发。
    4. 游戏发生错误,是否能正确上报自定义日志;(可选)
    5. 崩溃上报的版本号,用户名是否与设置一致;
    6. 错误上报的版本号,用户名是否与设置一致;

8 CrashSight测试及影响

成功接入后,正常启动游戏后会加载dll拉起exe进程。CrashSight启动后会立即上传联网信息,web端可以直接查看上报的联网数据,说明CrashSight联网数据上报功能。通过某种方式使游戏进程崩溃,在CrashSight网站该项目下的异常列表中能查看到该项目的dump信息,说明CrashSight异常捕获功能正常。

对接入CrashSight前后的客户端性能进行测试,CrashSight对游戏的性能影响不超过1%。

加载dll时需要判断dll是否成功加载。接入后直接移除TQM dll及文件夹,不会对游戏产生额外影响。

9 上传符号表

以上内容介绍了SDK的接入,崩溃上报和验证,但要在页面上看到可读的还原堆栈,还需要上传对应的符号表。PC端符号表,pdb或者exe+dll文件,可以直接在web页面上(异常配置->符号表管理)上传。后续也会提供单独的符号表上传工具。至少需要包含dump里面崩溃堆栈涉及的所有模块。

10 查看dll版本号

在一些情况下,开发者可能需要查看CrashSight64.dll的版本号。dll版本号可见于属性->详细信息中,如下图所示:

11 附录

1. CrashSight支持崩溃一览表. [微软崩溃定义](https://docs.microsoft.com/en-us/windows/win32/debug/getexceptioncode) 
#define STATUS_ACCESS_VIOLATION          ((DWORD   )0xC0000005L)    
#define STATUS_IN_PAGE_ERROR             ((DWORD   )0xC0000006L)    
#define STATUS_INVALID_HANDLE            ((DWORD   )0xC0000008L)    
#define STATUS_INVALID_PARAMETER         ((DWORD   )0xC000000DL)    
#define STATUS_NO_MEMORY                 ((DWORD   )0xC0000017L)    
#define STATUS_ILLEGAL_INSTRUCTION       ((DWORD   )0xC000001DL)    
#define STATUS_NONCONTINUABLE_EXCEPTION  ((DWORD   )0xC0000025L)    
#define STATUS_INVALID_DISPOSITION       ((DWORD   )0xC0000026L)    
#define STATUS_ARRAY_BOUNDS_EXCEEDED     ((DWORD   )0xC000008CL)    
#define STATUS_FLOAT_DENORMAL_OPERAND    ((DWORD   )0xC000008DL)    
#define STATUS_FLOAT_DIVIDE_BY_ZERO      ((DWORD   )0xC000008EL)    
#define STATUS_FLOAT_INEXACT_RESULT      ((DWORD   )0xC000008FL)    
#define STATUS_FLOAT_INVALID_OPERATION   ((DWORD   )0xC0000090L)    
#define STATUS_FLOAT_OVERFLOW            ((DWORD   )0xC0000091L)    
#define STATUS_FLOAT_STACK_CHECK         ((DWORD   )0xC0000092L)    
#define STATUS_FLOAT_UNDERFLOW           ((DWORD   )0xC0000093L)    
#define STATUS_INTEGER_DIVIDE_BY_ZERO    ((DWORD   )0xC0000094L)    
#define STATUS_INTEGER_OVERFLOW          ((DWORD   )0xC0000095L)    
#define STATUS_PRIVILEGED_INSTRUCTION    ((DWORD   )0xC0000096L)    
#define STATUS_STACK_OVERFLOW            ((DWORD   )0xC00000FDL)    
#define STATUS_DLL_NOT_FOUND             ((DWORD   )0xC0000135L)    
#define STATUS_ORDINAL_NOT_FOUND         ((DWORD   )0xC0000138L)    
#define STATUS_ENTRYPOINT_NOT_FOUND      ((DWORD   )0xC0000139L)    
#define STATUS_CONTROL_C_EXIT            ((DWORD   )0xC000013AL)    
#define STATUS_DLL_INIT_FAILED           ((DWORD   )0xC0000142L)    
#define STATUS_FLOAT_MULTIPLE_FAULTS     ((DWORD   )0xC00002B4L)    
#define STATUS_FLOAT_MULTIPLE_TRAPS      ((DWORD   )0xC00002B5L)    
#define STATUS_REG_NAT_CONSUMPTION       ((DWORD   )0xC00002C9L)    
#define STATUS_HEAP_CORRUPTION           ((DWORD   )0xC0000374L)    
#define STATUS_STACK_BUFFER_OVERRUN      ((DWORD   )0xC0000409L)    
#define STATUS_INVALID_CRUNTIME_PARAMETER ((DWORD   )0xC0000417L)    
#define STATUS_ASSERTION_FAILURE         ((DWORD   )0xC0000420L)    
#define STATUS_ENCLAVE_VIOLATION         ((DWORD   )0xC00004A2L)    
#if defined(STATUS_SUCCESS) || (_WIN32_WINNT > 0x0500) || (_WIN32_FUSION >= 0x0100) 
#define STATUS_SXS_EARLY_DEACTIVATION    ((DWORD   )0xC015000FL)    
#define STATUS_SXS_INVALID_DEACTIVATION  ((DWORD   )0xC0150010L)   
Last Updated: