- 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.com | pc.crashsight.qq.com |
海外服 | https://crashsight.wetest.net | pc.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的方式,具体接入方式如下:
登录web端注册项目,项目类型选择PC,注册成功后会生成appid(如:0620edc732)及app-key(如3edb3c5f-eca4-4033-ba60-5993452dff397,key需要保密)
将提供的文件按说明放到指定位置,客户端在启动后加载同级目录下的CrashSight64.dll
C++中通过HINSTANCE dllDemo = LoadLibraryA("CrashSight64.dll");加载dll,其他语言使用语言对应的dll加载方式即可.
声明导出函数(6. 参考代码) typedef void(*CS_InitContext)(const char* id, const char* version, const char* key); 并调用这个函数,将用户账号传递给TQM,参数说明:
参数 | 类型 | 说明 |
---|---|---|
userId | const char* | 用户的id 可以为qq或railid,推荐使用const char |
version | const char* | 当前游戏的版本号,如”3.1.0.13” |
key | const char* | 注册项目时生成的字符串,用于加密,如:”3edb3c5f-eca4-4033-ba60-59947eb3f397” |
配置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>
- 当客户端配置完成后,正常启动游戏,发现游戏进程中存在CrashSight64.dll,说明CrashSight加载启动成功。
4 CrashSight上报机制及数据字段
游戏客户端加载dll并调用导出函数后:
- 该dll会读取配置文件中的域名上报联网数据。
- 用于当dll捕获到崩溃异常时将dll生成的minidmp上报到服务端。
目前CrashSight上报的数据字段如下:
字段 | 说明 |
---|---|
userId | 用户id |
appId | 项目的注册id |
Mac | Mac地址 |
osName | 操作系统名称 |
displayCard | 显卡名称 |
Cpu | Cpu类型 |
phyMemAll | 机器内存 |
osBit | 机器位数 |
resolution | 分辨率 |
pidName | 进程名 |
bootTime | 启动时间 |
Ip | Ip地址 |
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按照自定义 数据格式文档中的规范进行设置。
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)