KSCrash 源码笔记 - 记录篇

本篇文章主要针对 KSCrash 捕获到崩溃后的记录操作。看看 KSCrash 都记录些什么内容,怎样记录的。特别是对堆栈信息的处理,对每个线程实现堆栈回溯和尝试符号解析的处理方式。

本篇文章主要都是贴的 KSCrash 的源码。会尽量对代码做注释说明。

前文已经分析过了,kscm_handleException 函数调用了 g_onExceptionEvent,而 g_onExceptionEvent 函数就是保存的 onCrash 函数。我们再来看一下 onCrash 的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static void onCrash(struct KSCrash_MonitorContext* monitorContext)
{
if (monitorContext->currentSnapshotUserReported == false) {
KSLOG_DEBUG("Updating application state to note crash.");
kscrashstate_notifyAppCrash();
}
monitorContext->consoleLogPath = g_shouldAddConsoleLogToReport ? g_consoleLogPath : NULL;

if(monitorContext->crashedDuringCrashHandling)
{
kscrashreport_writeRecrashReport(monitorContext, g_lastCrashReportFilePath);
}
else
{
char crashReportFilePath[KSFU_MAX_PATH_LENGTH];
int64_t reportID = kscrs_getNextCrashReport(crashReportFilePath);
strncpy(g_lastCrashReportFilePath, crashReportFilePath, sizeof(g_lastCrashReportFilePath));
kscrashreport_writeStandardReport(monitorContext, crashReportFilePath);

if(g_reportWrittenCallback)
{
g_reportWrittenCallback(reportID);
}
}
}

其中调用了 kscrashreport_writeStandardReport 方法,这个方法里实现了对各种信息的记录操作,将所有信息写入到本地文件里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
void kscrashreport_writeStandardReport(const KSCrash_MonitorContext* const monitorContext, const char* const path)
{
KSLOG_INFO("Writing crash report to %s", path);
char writeBuffer[1024];
KSBufferedWriter bufferedWriter;

if(!ksfu_openBufferedWriter(&bufferedWriter, path, writeBuffer, sizeof(writeBuffer)))
{
return;
}

ksccd_freeze();

KSJSONEncodeContext jsonContext;
jsonContext.userData = &bufferedWriter;
KSCrashReportWriter concreteWriter;
KSCrashReportWriter* writer = &concreteWriter;
prepareReportWriter(writer, &jsonContext);

// addJSONData 函数赋值给 writer 的 context
ksjson_beginEncode(getJsonContext(writer), true, addJSONData, &bufferedWriter);

// 1、写入 "report": "{"
writer->beginObject(writer, KSCrashField_Report);
{
// 2、写入 report info("report": "{")
writeReportInfo(writer,
KSCrashField_Report,
KSCrashReportType_Standard,
monitorContext->eventID,
monitorContext->System.processName);
// 将 buffer 数据写入磁盘
ksfu_flushBufferedWriter(&bufferedWriter);

// 3、写入 binary images("binary_images": "[")
writeBinaryImages(writer, KSCrashField_BinaryImages);
ksfu_flushBufferedWriter(&bufferedWriter);

// 4、写入 process state("process": "{")
writeProcessState(writer, KSCrashField_ProcessState, monitorContext);
ksfu_flushBufferedWriter(&bufferedWriter);

// 5、写入 system info("system": "{")
writeSystemInfo(writer, KSCrashField_System, monitorContext);
ksfu_flushBufferedWriter(&bufferedWriter);

// 写入 "crash": "{"
writer->beginObject(writer, KSCrashField_Crash);
{
// 6、写入崩溃错误信息
writeError(writer, KSCrashField_Error, monitorContext);
ksfu_flushBufferedWriter(&bufferedWriter);
// 7、写入各线程堆栈信息("threads": "[")
writeAllThreads(writer,
KSCrashField_Threads,
monitorContext,
g_introspectionRules.enabled);
ksfu_flushBufferedWriter(&bufferedWriter);
}
// 写入 "}"
writer->endContainer(writer);

if(g_userInfoJSON != NULL)
{
addJSONElement(writer, KSCrashField_User, g_userInfoJSON, false);
ksfu_flushBufferedWriter(&bufferedWriter);
}
else
{
writer->beginObject(writer, KSCrashField_User);
}
if(g_userSectionWriteCallback != NULL)
{
ksfu_flushBufferedWriter(&bufferedWriter);
if (monitorContext->currentSnapshotUserReported == false) {
g_userSectionWriteCallback(writer);
}
}
writer->endContainer(writer);
ksfu_flushBufferedWriter(&bufferedWriter);

writeDebugInfo(writer, KSCrashField_Debug, monitorContext);
}
// 写入 "}"
writer->endContainer(writer);

ksjson_endEncode(getJsonContext(writer));
ksfu_closeBufferedWriter(&bufferedWriter);
ksccd_unfreeze();
}

接下来,我们一一查看上述调用的方法的具体内容,看看它们都具体做了什么工作。

1、写入方法:beginObject

beginObject 最终调用的即 addJSONData 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 写入 name 及 "{"
int ksjson_beginObject(KSJSONEncodeContext* const context,
const char* const name)
{
likely_if(context->containerLevel >= 0)
{
int result = ksjson_beginElement(context, name);
unlikely_if(result != KSJSON_OK)
{
return result;
}
}

context->containerLevel++;
context->isObject[context->containerLevel] = true;
context->containerFirstEntry = true;

return addJSONData(context, "{", 1);
}

#define addJSONData(CONTEXT,DATA,LENGTH) \
(CONTEXT)->addJSONData(DATA, LENGTH, (CONTEXT)->userData)

static int addJSONData(const char* restrict const data, const int length, void* restrict userData)
{
KSBufferedWriter* writer = (KSBufferedWriter*)userData;
// 将目标数据写入 Buffer 或磁盘
const bool success = ksfu_writeBufferedWriter(writer, data, length);
return success ? KSJSON_OK : KSJSON_ERROR_CANNOT_ADD_DATA;
}

bool ksfu_writeBufferedWriter(KSBufferedWriter* writer, const char* restrict const data, const int length)
{
// 如果数据大小超出 buffer 剩余空间,则将数据写入文件
if(length > writer->bufferLength - writer->position)
{
ksfu_flushBufferedWriter(writer);
}
if(length > writer->bufferLength)
{
return ksfu_writeBytesToFD(writer->fd, data, length);
}
// 否则将数据写入 buffer
memcpy(writer->buffer + writer->position, data, length);
// 更新 position 用于标记 buffer 剩余空间
writer->position += length;
return true;
}

bool ksfu_writeBytesToFD(const int fd, const char* const bytes, int length)
{
const char* pos = bytes;
while(length > 0)
{
// write 函数将数据写入文件,bytesWritten 为写入文档的字节数,出错为 -1
int bytesWritten = (int)write(fd, pos, (unsigned)length);
if(bytesWritten == -1)
{
KSLOG_ERROR("Could not write to fd %d: %s", fd, strerror(errno));
return false;
}
length -= bytesWritten;
pos += bytesWritten;
}
return true;
}

2、写入 report info:writeReportInfo

写入一些基础信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static void writeReportInfo(const KSCrashReportWriter* const writer,
const char* const key,
const char* const type,
const char* const reportID,
const char* const processName)
{
writer->beginObject(writer, key);
{
struct timeval tp;
gettimeofday(&tp, NULL);
int64_t microseconds = ((int64_t)tp.tv_sec) * 1000000 + tp.tv_usec;

// "version"
writer->addStringElement(writer, KSCrashField_Version, KSCRASH_REPORT_VERSION);
// "id"
writer->addStringElement(writer, KSCrashField_ID, reportID);
// "process_name"
writer->addStringElement(writer, KSCrashField_ProcessName, processName);
// "timestamp"
writer->addIntegerElement(writer, KSCrashField_Timestamp, microseconds);
// "type"
writer->addStringElement(writer, KSCrashField_Type, type);
}
writer->endContainer(writer);
}

3、写入 binary images:writeBinaryImages

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
static void writeBinaryImages(const KSCrashReportWriter* const writer, const char* const key)
{
// 调用 _dyld_image_count 获取 image count
const int imageCount = ksdl_imageCount();

writer->beginArray(writer, key);
{
for(int iImg = 0; iImg < imageCount; iImg++)
{
// 遍历 image count,写入各 image 信息
writeBinaryImage(writer, NULL, iImg);
}
}
writer->endContainer(writer);
}

static void writeBinaryImage(const KSCrashReportWriter* const writer,
const char* const key,
const int index)
{
KSBinaryImage image = {0};
// 获取 image 详细信息
if(!ksdl_getBinaryImage(index, &image))
{
return;
}

writer->beginObject(writer, key);
{
// "image_addr"
writer->addUIntegerElement(writer, KSCrashField_ImageAddress, image.address);
// "image_vmaddr"
writer->addUIntegerElement(writer, KSCrashField_ImageVmAddress, image.vmAddress);
// "image_size"
writer->addUIntegerElement(writer, KSCrashField_ImageSize, image.size);
// "name"
writer->addStringElement(writer, KSCrashField_Name, image.name);
// "uuid"
writer->addUUIDElement(writer, KSCrashField_UUID, image.uuid);
// "cpu_type"
writer->addIntegerElement(writer, KSCrashField_CPUType, image.cpuType);
// "cpu_subtype"
writer->addIntegerElement(writer, KSCrashField_CPUSubType, image.cpuSubType);
// "major_version"
writer->addUIntegerElement(writer, KSCrashField_ImageMajorVersion, image.majorVersion);
// "minor_version"
writer->addUIntegerElement(writer, KSCrashField_ImageMinorVersion, image.minorVersion);
// "revision_version"
writer->addUIntegerElement(writer, KSCrashField_ImageRevisionVersion, image.revisionVersion);
if(image.crashInfoMessage != NULL)
{
// "crash_info_message"
writer->addStringElement(writer, KSCrashField_ImageCrashInfoMessage, image.crashInfoMessage);
}
if(image.crashInfoMessage2 != NULL)
{
// "crash_info_message2"
writer->addStringElement(writer, KSCrashField_ImageCrashInfoMessage2, image.crashInfoMessage2);
}
}
writer->endContainer(writer);
}


bool ksdl_getBinaryImage(int index, KSBinaryImage* buffer)
{
// 获取 image header
const struct mach_header* header = _dyld_get_image_header((unsigned)index);
if(header == NULL)
{
return false;
}

return ksdl_getBinaryImageForHeader((const void*)header, _dyld_get_image_name((unsigned)index), buffer);
}

// 获取 image 详细信息
bool ksdl_getBinaryImageForHeader(const void* const header_ptr, const char* const image_name, KSBinaryImage* buffer)
{
const struct mach_header* header = (const struct mach_header*)header_ptr;
uintptr_t cmdPtr = firstCmdAfterHeader(header);
if(cmdPtr == 0)
{
return false;
}

// Look for the TEXT segment to get the image size.
// Also look for a UUID command.
uint64_t imageSize = 0;
uint64_t imageVmAddr = 0;
uint64_t version = 0;
uint8_t* uuid = NULL;

for(uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++)
{
struct load_command* loadCmd = (struct load_command*)cmdPtr;
switch(loadCmd->cmd)
{
case LC_SEGMENT:
{
struct segment_command* segCmd = (struct segment_command*)cmdPtr;
if(strcmp(segCmd->segname, SEG_TEXT) == 0)
{
imageSize = segCmd->vmsize;
imageVmAddr = segCmd->vmaddr;
}
break;
}
case LC_SEGMENT_64:
{
struct segment_command_64* segCmd = (struct segment_command_64*)cmdPtr;
if(strcmp(segCmd->segname, SEG_TEXT) == 0)
{
imageSize = segCmd->vmsize;
imageVmAddr = segCmd->vmaddr;
}
break;
}
case LC_UUID:
{
struct uuid_command* uuidCmd = (struct uuid_command*)cmdPtr;
uuid = uuidCmd->uuid;
break;
}
case LC_ID_DYLIB:
{

struct dylib_command* dc = (struct dylib_command*)cmdPtr;
version = dc->dylib.current_version;
break;
}
}
cmdPtr += loadCmd->cmdsize;
}

buffer->address = (uintptr_t)header;
buffer->vmAddress = imageVmAddr;
buffer->size = imageSize;
buffer->name = image_name;
buffer->uuid = uuid;
buffer->cpuType = header->cputype;
buffer->cpuSubType = header->cpusubtype;
buffer->majorVersion = version >> 16;
buffer->minorVersion = (version >> 8) & 0xff;
buffer->revisionVersion = version & 0xff;
getCrashInfo(header, buffer);

return true;
}

4、写入 process state:writeProcessState

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static void writeProcessState(const KSCrashReportWriter* const writer,
const char* const key,
const KSCrash_MonitorContext* const monitorContext)
{
// "process": "{"
writer->beginObject(writer, key);
{
if(monitorContext->ZombieException.address != 0)
{
// "last_dealloced_nsexception": "{"
writer->beginObject(writer, KSCrashField_LastDeallocedNSException);
{
// "address"
writer->addUIntegerElement(writer, KSCrashField_Address, monitorContext->ZombieException.address);
// "name"
writer->addStringElement(writer, KSCrashField_Name, monitorContext->ZombieException.name);
// "reason"
writer->addStringElement(writer, KSCrashField_Reason, monitorContext->ZombieException.reason);
// "referenced_object"
writeAddressReferencedByString(writer, KSCrashField_ReferencedObject, monitorContext->ZombieException.reason);
}
writer->endContainer(writer);
}
}
writer->endContainer(writer);
}

5、写入 system info:writeSystemInfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
static void writeSystemInfo(const KSCrashReportWriter* const writer,
const char* const key,
const KSCrash_MonitorContext* const monitorContext)
{
writer->beginObject(writer, key);
{
// "system_name"
writer->addStringElement(writer, KSCrashField_SystemName, monitorContext->System.systemName);
// "system_version"
writer->addStringElement(writer, KSCrashField_SystemVersion, monitorContext->System.systemVersion);
// "machine"
writer->addStringElement(writer, KSCrashField_Machine, monitorContext->System.machine);
// "model"
writer->addStringElement(writer, KSCrashField_Model, monitorContext->System.model);
// "kernel_version"
writer->addStringElement(writer, KSCrashField_KernelVersion, monitorContext->System.kernelVersion);
// "os_version"
writer->addStringElement(writer, KSCrashField_OSVersion, monitorContext->System.osVersion);
// "jailbroken"
writer->addBooleanElement(writer, KSCrashField_Jailbroken, monitorContext->System.isJailbroken);
// "boot_time"
writer->addStringElement(writer, KSCrashField_BootTime, monitorContext->System.bootTime);
// "app_start_time"
writer->addStringElement(writer, KSCrashField_AppStartTime, monitorContext->System.appStartTime);
// "CFBundleExecutablePath"
writer->addStringElement(writer, KSCrashField_ExecutablePath, monitorContext->System.executablePath);
// "CFBundleExecutable"
writer->addStringElement(writer, KSCrashField_Executable, monitorContext->System.executableName);
// "CFBundleIdentifier"
writer->addStringElement(writer, KSCrashField_BundleID, monitorContext->System.bundleID);
// "CFBundleName"
writer->addStringElement(writer, KSCrashField_BundleName, monitorContext->System.bundleName);
// "CFBundleVersion"
writer->addStringElement(writer, KSCrashField_BundleVersion, monitorContext->System.bundleVersion);
// "CFBundleShortVersionString"
writer->addStringElement(writer, KSCrashField_BundleShortVersion, monitorContext->System.bundleShortVersion);
// "app_uuid"
writer->addStringElement(writer, KSCrashField_AppUUID, monitorContext->System.appID);
// "cpu_arch"
writer->addStringElement(writer, KSCrashField_CPUArch, monitorContext->System.cpuArchitecture);
// "cpu_type"
writer->addIntegerElement(writer, KSCrashField_CPUType, monitorContext->System.cpuType);
// "cpu_subtype"
writer->addIntegerElement(writer, KSCrashField_CPUSubType, monitorContext->System.cpuSubType);
// "binary_cpu_type"
writer->addIntegerElement(writer, KSCrashField_BinaryCPUType, monitorContext->System.binaryCPUType);
// "binary_cpu_subtype"
writer->addIntegerElement(writer, KSCrashField_BinaryCPUSubType, monitorContext->System.binaryCPUSubType);
// "time_zone"
writer->addStringElement(writer, KSCrashField_TimeZone, monitorContext->System.timezone);
// "process_name"
writer->addStringElement(writer, KSCrashField_ProcessName, monitorContext->System.processName);
// "process_id"
writer->addIntegerElement(writer, KSCrashField_ProcessID, monitorContext->System.processID);
// "parent_process_id"
writer->addIntegerElement(writer, KSCrashField_ParentProcessID, monitorContext->System.parentProcessID);
// "device_app_hash"
writer->addStringElement(writer, KSCrashField_DeviceAppHash, monitorContext->System.deviceAppHash);
// "build_type"
writer->addStringElement(writer, KSCrashField_BuildType, monitorContext->System.buildType);
// "storage"
writer->addIntegerElement(writer, KSCrashField_Storage, (int64_t)monitorContext->System.storageSize);

// 写入内存信息("memory": "{")
writeMemoryInfo(writer, KSCrashField_Memory, monitorContext);
// 写入App运行状态等信息("application_stats": "{")
writeAppStats(writer, KSCrashField_AppStats, monitorContext);
}
writer->endContainer(writer);

}

6、写入崩溃错误信息:writeError

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
static void writeError(const KSCrashReportWriter* const writer,
const char* const key,
const KSCrash_MonitorContext* const crash)
{
// 写入 "error": "{"
writer->beginObject(writer, key);
{
#if KSCRASH_HOST_APPLE
// 写入 "mach": "{"
writer->beginObject(writer, KSCrashField_Mach);
{
// 写入 mach 崩溃信息
const char* machExceptionName = ksmach_exceptionName(crash->mach.type);
const char* machCodeName = crash->mach.code == 0 ? NULL : ksmach_kernelReturnCodeName(crash->mach.code);
writer->addUIntegerElement(writer, KSCrashField_Exception, (unsigned)crash->mach.type);
if(machExceptionName != NULL)
{
writer->addStringElement(writer, KSCrashField_ExceptionName, machExceptionName);
}
writer->addUIntegerElement(writer, KSCrashField_Code, (unsigned)crash->mach.code);
if(machCodeName != NULL)
{
writer->addStringElement(writer, KSCrashField_CodeName, machCodeName);
}
writer->addUIntegerElement(writer, KSCrashField_Subcode, (size_t)crash->mach.subcode);
}
// 写入 "}"
writer->endContainer(writer);
#endif
// 写入 "signal": "{"
writer->beginObject(writer, KSCrashField_Signal);
{
// 写入 signal 崩溃信息
const char* sigName = kssignal_signalName(crash->signal.signum);
const char* sigCodeName = kssignal_signalCodeName(crash->signal.signum, crash->signal.sigcode);
writer->addUIntegerElement(writer, KSCrashField_Signal, (unsigned)crash->signal.signum);
if(sigName != NULL)
{
writer->addStringElement(writer, KSCrashField_Name, sigName);
}
writer->addUIntegerElement(writer, KSCrashField_Code, (unsigned)crash->signal.sigcode);
if(sigCodeName != NULL)
{
writer->addStringElement(writer, KSCrashField_CodeName, sigCodeName);
}
}
// 写入 "}"
writer->endContainer(writer);

// 写入崩溃地址
writer->addUIntegerElement(writer, KSCrashField_Address, crash->faultAddress);
if(crash->crashReason != NULL)
{
// 写入引起崩溃原因的简短描述
writer->addStringElement(writer, KSCrashField_Reason, crash->crashReason);
}

// Gather specific info.
switch(crash->crashType)
{
// 写入 "type": "deadlock"
case KSCrashMonitorTypeMainThreadDeadlock:
writer->addStringElement(writer, KSCrashField_Type, KSCrashExcType_Deadlock);
break;

// 写入 "type": "mach"
case KSCrashMonitorTypeMachException:
writer->addStringElement(writer, KSCrashField_Type, KSCrashExcType_Mach);
break;

// 写入 "type": "cpp_exception"
case KSCrashMonitorTypeCPPException:
{
writer->addStringElement(writer, KSCrashField_Type, KSCrashExcType_CPPException);
writer->beginObject(writer, KSCrashField_CPPException);
{
writer->addStringElement(writer, KSCrashField_Name, crash->CPPException.name);
}
writer->endContainer(writer);
break;
}
// 写入 "type": "nsexception"
case KSCrashMonitorTypeNSException:
{
writer->addStringElement(writer, KSCrashField_Type, KSCrashExcType_NSException);
writer->beginObject(writer, KSCrashField_NSException);
{
writer->addStringElement(writer, KSCrashField_Name, crash->NSException.name);
writer->addStringElement(writer, KSCrashField_UserInfo, crash->NSException.userInfo);
writeAddressReferencedByString(writer, KSCrashField_ReferencedObject, crash->crashReason);
}
writer->endContainer(writer);
break;
}
// 写入 "type": "signal"
case KSCrashMonitorTypeSignal:
writer->addStringElement(writer, KSCrashField_Type, KSCrashExcType_Signal);
break;

// 写入 "type": "user"
case KSCrashMonitorTypeUserReported:
{
writer->addStringElement(writer, KSCrashField_Type, KSCrashExcType_User);
writer->beginObject(writer, KSCrashField_UserReported);
{
writer->addStringElement(writer, KSCrashField_Name, crash->userException.name);
if(crash->userException.language != NULL)
{
writer->addStringElement(writer, KSCrashField_Language, crash->userException.language);
}
if(crash->userException.lineOfCode != NULL)
{
writer->addStringElement(writer, KSCrashField_LineOfCode, crash->userException.lineOfCode);
}
if(crash->userException.customStackTrace != NULL)
{
writer->addJSONElement(writer, KSCrashField_Backtrace, crash->userException.customStackTrace, true);
}
}
writer->endContainer(writer);
break;
}
case KSCrashMonitorTypeSystem:
case KSCrashMonitorTypeApplicationState:
case KSCrashMonitorTypeZombie:
KSLOG_ERROR("Crash monitor type 0x%x shouldn't be able to cause events!", crash->crashType);
break;
}
}
// 写入 "}"
writer->endContainer(writer);
}

7、写入各线程堆栈信息:writeAllThreads

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
static void writeAllThreads(const KSCrashReportWriter* const writer,
const char* const key,
const KSCrash_MonitorContext* const crash,
bool writeNotableAddresses)
{
const struct KSMachineContext* const context = crash->offendingMachineContext;
KSThread offendingThread = ksmc_getThreadFromContext(context);
int threadCount = ksmc_getThreadCount(context);
KSMC_NEW_CONTEXT(machineContext);

// Fetch info for all threads.
writer->beginArray(writer, key);
{
KSLOG_DEBUG("Writing %d threads.", threadCount);
// 遍历线程
for(int i = 0; i < threadCount; i++)
{
KSThread thread = ksmc_getThreadAtIndex(context, i);
if(thread == offendingThread)
{
// 如果是当前线程
writeThread(writer, NULL, crash, context, i, writeNotableAddresses);
}
else
{
// 如果不是当前线程
ksmc_getContextForThread(thread, machineContext, false);
writeThread(writer, NULL, crash, machineContext, i, writeNotableAddresses);
}
}
}
writer->endContainer(writer);
}

static void writeThread(const KSCrashReportWriter* const writer,
const char* const key,
const KSCrash_MonitorContext* const crash,
const struct KSMachineContext* const machineContext,
const int threadIndex,
const bool shouldWriteNotableAddresses)
{
bool isCrashedThread = ksmc_isCrashedContext(machineContext);
KSThread thread = ksmc_getThreadFromContext(machineContext);
KSLOG_DEBUG("Writing thread %x (index %d). is crashed: %d", thread, threadIndex, isCrashedThread);

KSStackCursor stackCursor;
bool hasBacktrace = getStackCursor(crash, machineContext, &stackCursor);

writer->beginObject(writer, key);
{
if(hasBacktrace)
{
// 写入堆栈("backtrace": "{")
writeBacktrace(writer, KSCrashField_Backtrace, &stackCursor);
}
if(ksmc_canHaveCPUState(machineContext))
{
// 写入寄存器("registers": "{")
writeRegisters(writer, KSCrashField_Registers, machineContext);
}
// 写入线程 index
writer->addIntegerElement(writer, KSCrashField_Index, threadIndex);
const char* name = ksccd_getThreadName(thread);
if(name != NULL)
{
// 写入线程 name
writer->addStringElement(writer, KSCrashField_Name, name);
}
name = ksccd_getQueueName(thread);
if(name != NULL)
{
// 写入线程 queue
writer->addStringElement(writer, KSCrashField_DispatchQueue, name);
}
// 当前线程是否是 crash thread
writer->addBooleanElement(writer, KSCrashField_Crashed, isCrashedThread);
// 当前线程是否是 current thread
writer->addBooleanElement(writer, KSCrashField_CurrentThread, thread == ksthread_self());
if(isCrashedThread)
{
// 如果是崩溃线程,写入部分堆栈信息
writeStackContents(writer, KSCrashField_Stack, machineContext, stackCursor.state.hasGivenUp);
if(shouldWriteNotableAddresses)
{
// 写入寄存器和栈上的地址信息
writeNotableAddresses(writer, KSCrashField_NotableAddresses, machineContext);
}
}
}
writer->endContainer(writer);
}

writeBacktrace

我们来重点关注下 writeBacktrace 函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
static void writeBacktrace(const KSCrashReportWriter* const writer,
const char* const key,
KSStackCursor* stackCursor)
{
// 写入 "backtrace": "{"
writer->beginObject(writer, key);
{
// 写入 "contents": "["
writer->beginArray(writer, KSCrashField_Contents);
{
// 循环进行堆栈符号化
while(stackCursor->advanceCursor(stackCursor))
{
writer->beginObject(writer, NULL);
{
// 尝试对当前地址进行符号化
if(stackCursor->symbolicate(stackCursor))
{
if(stackCursor->stackEntry.imageName != NULL)
{
// 写入 image name
writer->addStringElement(writer, KSCrashField_ObjectName, ksfu_lastPathEntry(stackCursor->stackEntry.imageName));
}
// 写入 image address
writer->addUIntegerElement(writer, KSCrashField_ObjectAddr, stackCursor->stackEntry.imageAddress);
if(stackCursor->stackEntry.symbolName != NULL)
{
// 写入符号名称
writer->addStringElement(writer, KSCrashField_SymbolName, stackCursor->stackEntry.symbolName);
}
// 写入符号地址
writer->addUIntegerElement(writer, KSCrashField_SymbolAddr, stackCursor->stackEntry.symbolAddress);
}
// 写入堆栈地址
writer->addUIntegerElement(writer, KSCrashField_InstructionAddr, stackCursor->stackEntry.address);
}
writer->endContainer(writer);
}
}
writer->endContainer(writer);
writer->addIntegerElement(writer, KSCrashField_Skipped, 0);
}
writer->endContainer(writer);
}

1、其中 advanceCursor 函数用于遍历栈帧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
static bool advanceCursor(KSStackCursor *cursor)
{
MachineContextCursor* context = (MachineContextCursor*)cursor->context;
uintptr_t nextAddress = 0;

// 如果当前遍历的栈帧深度大于设定的栈帧深度,则直接返回
if(cursor->state.currentDepth >= context->maxStackDepth)
{
cursor->state.hasGivenUp = true;
return false;
}

// 如果 pc 寄存器为 0 并且循环栈帧深度为 0,则直接进入 successful 块
if(context->instructionAddress == 0 && cursor->state.currentDepth == 0)
{
// 获取 pc 寄存器地址进行赋值
context->instructionAddress = kscpu_instructionAddress(context->machineContext);
nextAddress = context->instructionAddress;
goto successfulExit;
}

// 如果 lr 寄存器为 0 并且为经历循环,则直接进入 successful 块
if(context->linkRegister == 0 && !context->isPastFramePointer)
{
// Link register, if available, is the second address in the trace.
context->linkRegister = kscpu_linkRegister(context->machineContext);
if(context->linkRegister != 0)
{
nextAddress = context->linkRegister;
goto successfulExit;
}
}

// 如果 currentFrame.previous 为空,则获取 fp 寄存器地址进行赋值
if(context->currentFrame.previous == NULL)
{
if(context->isPastFramePointer)
{
return false;
}
context->currentFrame.previous = (struct FrameEntry*)kscpu_framePointer(context->machineContext);
context->isPastFramePointer = true;
}

// 将 currentFrame.previous 地址拷贝到 currentFrame,向前遍历一桢栈帧
if(!ksmem_copySafely(context->currentFrame.previous, &context->currentFrame, sizeof(context->currentFrame)))
{
return false;
}
// 如果 currentFrame.previous 为 0 或者 currentFrame.return_address 为 0 代表栈帧结束,则直接返回
if(context->currentFrame.previous == 0 || context->currentFrame.return_address == 0)
{
return false;
}

nextAddress = context->currentFrame.return_address;

successfulExit:
// 记录当前堆栈地址
cursor->stackEntry.address = kscpu_normaliseInstructionPointer(nextAddress);
cursor->state.currentDepth++;
return true;
}

2、其中 symbolicate 函数尝试对地址进行符号化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
bool kssymbolicator_symbolicate(KSStackCursor *cursor)
{
Dl_info symbolsBuffer;
// 地址符号化
if(ksdl_dladdr(CALL_INSTRUCTION_FROM_RETURN_ADDRESS(cursor->stackEntry.address), &symbolsBuffer))
{
cursor->stackEntry.imageAddress = (uintptr_t)symbolsBuffer.dli_fbase;
cursor->stackEntry.imageName = symbolsBuffer.dli_fname;
cursor->stackEntry.symbolAddress = (uintptr_t)symbolsBuffer.dli_saddr;
cursor->stackEntry.symbolName = symbolsBuffer.dli_sname;
return true;
}

cursor->stackEntry.imageAddress = 0;
cursor->stackEntry.imageName = 0;
cursor->stackEntry.symbolAddress = 0;
cursor->stackEntry.symbolName = 0;
return false;
}



bool ksdl_dladdr(const uintptr_t address, Dl_info* const info)
{
info->dli_fname = NULL;
info->dli_fbase = NULL;
info->dli_sname = NULL;
info->dli_saddr = NULL;

// 查找目标地址所在的 image index
const uint32_t idx = imageIndexContainingAddress(address);
if(idx == UINT_MAX)
{
return false;
}
// 获取 image header
const struct mach_header* header = _dyld_get_image_header(idx);
// 获取 image 虚拟地址偏移量
const uintptr_t imageVMAddrSlide = (uintptr_t)_dyld_get_image_vmaddr_slide(idx);
// 获取 image 有效地址
const uintptr_t addressWithSlide = address - imageVMAddrSlide;
// 根据 image index 获取 image 基地址,加上 image 虚拟地址偏移量得到 image 的起始地址
const uintptr_t segmentBase = segmentBaseOfImageIndex(idx) + imageVMAddrSlide;
if(segmentBase == 0)
{
return false;
}

// 获取 image name
info->dli_fname = _dyld_get_image_name(idx);
info->dli_fbase = (void*)header;

// 查找符号表,在符号表里查找目标地址最近的符号地址及符号名称
// Find symbol tables and get whichever symbol is closest to the address.
const nlist_t* bestMatch = NULL;
uintptr_t bestDistance = ULONG_MAX;
uintptr_t cmdPtr = firstCmdAfterHeader(header);
if(cmdPtr == 0)
{
return false;
}
for(uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++)
{
const struct load_command* loadCmd = (struct load_command*)cmdPtr;
if(loadCmd->cmd == LC_SYMTAB)
{
const struct symtab_command* symtabCmd = (struct symtab_command*)cmdPtr;
// 符号表(image 起始地址 + 符号表偏移量)
const nlist_t* symbolTable = (nlist_t*)(segmentBase + symtabCmd->symoff);
// 字符串表(image 起始地址 + 字符串表偏移量)
const uintptr_t stringTable = segmentBase + symtabCmd->stroff;

for(uint32_t iSym = 0; iSym < symtabCmd->nsyms; iSym++)
{
// If n_value is 0, the symbol refers to an external object.
if(symbolTable[iSym].n_value != 0)
{
uintptr_t symbolBase = symbolTable[iSym].n_value;
uintptr_t currentDistance = addressWithSlide - symbolBase;
if((addressWithSlide >= symbolBase) &&
(currentDistance <= bestDistance))
{
// 找到离目标地址最近的符号
bestMatch = symbolTable + iSym;
bestDistance = currentDistance;
}
}
}
if(bestMatch != NULL)
{
// 符号地址(符号偏移量 + image 虚拟地址偏移量)
info->dli_saddr = (void*)(bestMatch->n_value + imageVMAddrSlide);
if(bestMatch->n_desc == 16)
{
// This image has been stripped. The name is meaningless, and
// almost certainly resolves to "_mh_execute_header"
info->dli_sname = NULL;
}
else
{
// 符号名称(字符串表位置 + 符号所在字符串表 index)
info->dli_sname = (char*)((intptr_t)stringTable + (intptr_t)bestMatch->n_un.n_strx);
if(*info->dli_sname == '_')
{
info->dli_sname++;
}
}
break;
}
}
cmdPtr += loadCmd->cmdsize;
}

return true;
}

writeRegisters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
static void writeRegisters(const KSCrashReportWriter* const writer,
const char* const key,
const struct KSMachineContext* const machineContext)
{
writer->beginObject(writer, key);
{
// 写入各寄存器内容
writeBasicRegisters(writer, KSCrashField_Basic, machineContext);
if(ksmc_hasValidExceptionRegisters(machineContext))
{
// 写入异常寄存器内容
writeExceptionRegisters(writer, KSCrashField_Exception, machineContext);
}
}
writer->endContainer(writer);
}


static void writeBasicRegisters(const KSCrashReportWriter* const writer,
const char* const key,
const struct KSMachineContext* const machineContext)
{
char registerNameBuff[30];
const char* registerName;
writer->beginObject(writer, key);
{
// 遍历寄存器
const int numRegisters = kscpu_numRegisters();
for(int reg = 0; reg < numRegisters; reg++)
{
// 获取寄存器名称
registerName = kscpu_registerName(reg);
if(registerName == NULL)
{
snprintf(registerNameBuff, sizeof(registerNameBuff), "r%d", reg);
registerName = registerNameBuff;
}
// kscpu_registerValue:获取寄存器内容
writer->addUIntegerElement(writer, registerName,
kscpu_registerValue(machineContext, reg));
}
}
writer->endContainer(writer);
}


static const char* g_registerNames[] =
{
"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
"x24", "x25", "x26", "x27", "x28", "x29",
"fp", "lr", "sp", "pc", "cpsr"
};

uint64_t kscpu_registerValue(const KSMachineContext* const context, const int regNumber)
{
if(regNumber <= 29)
{
return context->machineContext.__ss.__x[regNumber];
}

switch(regNumber)
{
case 30: return context->machineContext.__ss.__fp;
case 31: return context->machineContext.__ss.__lr;
case 32: return context->machineContext.__ss.__sp;
case 33: return context->machineContext.__ss.__pc;
case 34: return context->machineContext.__ss.__cpsr;
}

KSLOG_ERROR("Invalid register number: %d", regNumber);
return 0;
}

writeStackContents & writeNotableAddresses

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
static void writeStackContents(const KSCrashReportWriter* const writer,
const char* const key,
const struct KSMachineContext* const machineContext,
const bool isStackOverflow)
{
uintptr_t sp = kscpu_stackPointer(machineContext);
if((void*)sp == NULL)
{
return;
}

uintptr_t lowAddress = sp + (uintptr_t)(kStackContentsPushedDistance * (int)sizeof(sp) * kscpu_stackGrowDirection() * -1);
uintptr_t highAddress = sp + (uintptr_t)(kStackContentsPoppedDistance * (int)sizeof(sp) * kscpu_stackGrowDirection());
if(highAddress < lowAddress)
{
uintptr_t tmp = lowAddress;
lowAddress = highAddress;
highAddress = tmp;
}
writer->beginObject(writer, key);
{
writer->addStringElement(writer, KSCrashField_GrowDirection, kscpu_stackGrowDirection() > 0 ? "+" : "-");
writer->addUIntegerElement(writer, KSCrashField_DumpStart, lowAddress);
writer->addUIntegerElement(writer, KSCrashField_DumpEnd, highAddress);
writer->addUIntegerElement(writer, KSCrashField_StackPtr, sp);
writer->addBooleanElement(writer, KSCrashField_Overflow, isStackOverflow);
uint8_t stackBuffer[kStackContentsTotalDistance * sizeof(sp)];
int copyLength = (int)(highAddress - lowAddress);
if(ksmem_copySafely((void*)lowAddress, stackBuffer, copyLength))
{
writer->addDataElement(writer, KSCrashField_Contents, (void*)stackBuffer, copyLength);
}
else
{
writer->addStringElement(writer, KSCrashField_Error, "Stack contents not accessible");
}
}
writer->endContainer(writer);
}


static void writeNotableAddresses(const KSCrashReportWriter* const writer,
const char* const key,
const struct KSMachineContext* const machineContext)
{
writer->beginObject(writer, key);
{
// 写入寄存器的地址信息
writeNotableRegisters(writer, machineContext);
// 写入栈上的地址信息
writeNotableStackContents(writer,
machineContext,
kStackNotableSearchBackDistance,
kStackNotableSearchForwardDistance);
}
writer->endContainer(writer);
}

因篇幅原因,对上述 writeNotableRegisters 和 writeNotableStackContents 就不做过多详细的分析了。其中,他们都调用了 writeMemoryContents 关键方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
static void writeMemoryContents(const KSCrashReportWriter* const writer,
const char* const key,
const uintptr_t address,
int* limit)
{
(*limit)--;
const void* object = (const void*)address;
writer->beginObject(writer, key);
{
writer->addUIntegerElement(writer, KSCrashField_Address, address);
writeZombieIfPresent(writer, KSCrashField_LastDeallocObject, address);
if(!writeObjCObject(writer, address, limit))
{
if(object == NULL)
{
writer->addStringElement(writer, KSCrashField_Type, KSCrashMemType_NullPointer);
}
else if(isValidString(object))
{
writer->addStringElement(writer, KSCrashField_Type, KSCrashMemType_String);
writer->addStringElement(writer, KSCrashField_Value, (const char*)object);
}
else
{
writer->addStringElement(writer, KSCrashField_Type, KSCrashMemType_Unknown);
}
}
}
writer->endContainer(writer);
}

static bool writeObjCObject(const KSCrashReportWriter* const writer,
const uintptr_t address,
int* limit)
{
#if KSCRASH_HAS_OBJC
const void* object = (const void*)address;
switch(ksobjc_objectType(object))
{
case KSObjCTypeClass:
writer->addStringElement(writer, KSCrashField_Type, KSCrashMemType_Class);
writer->addStringElement(writer, KSCrashField_Class, ksobjc_className(object));
return true;
case KSObjCTypeObject:
{
writer->addStringElement(writer, KSCrashField_Type, KSCrashMemType_Object);
const char* className = ksobjc_objectClassName(object);
writer->addStringElement(writer, KSCrashField_Class, className);
if(!isRestrictedClass(className))
{
switch(ksobjc_objectClassType(object))
{
case KSObjCClassTypeString:
writeNSStringContents(writer, KSCrashField_Value, address, limit);
return true;
case KSObjCClassTypeURL:
writeURLContents(writer, KSCrashField_Value, address, limit);
return true;
case KSObjCClassTypeDate:
writeDateContents(writer, KSCrashField_Value, address, limit);
return true;
case KSObjCClassTypeArray:
if(*limit > 0)
{
writeArrayContents(writer, KSCrashField_FirstObject, address, limit);
}
return true;
case KSObjCClassTypeNumber:
writeNumberContents(writer, KSCrashField_Value, address, limit);
return true;
case KSObjCClassTypeDictionary:
case KSObjCClassTypeException:
// TODO: Implement these.
if(*limit > 0)
{
writeUnknownObjectContents(writer, KSCrashField_Ivars, address, limit);
}
return true;
case KSObjCClassTypeUnknown:
if(*limit > 0)
{
writeUnknownObjectContents(writer, KSCrashField_Ivars, address, limit);
}
return true;
}
}
break;
}
case KSObjCTypeBlock:
writer->addStringElement(writer, KSCrashField_Type, KSCrashMemType_Block);
const char* className = ksobjc_objectClassName(object);
writer->addStringElement(writer, KSCrashField_Class, className);
return true;
case KSObjCTypeUnknown:
break;
}
#endif

return false;
}

以上方法判断了 OC 对象类型,String、URL、Date、Array、Number、Dictionary、Block 等类型,并对这些类型分别做了取值与写入操作。因篇幅原因就不再进行详细分析了。

小结

KSCrash 对崩溃后日志的记录,主要是系统信息、崩溃信息、堆栈信息等等。尤其是堆栈信息,将各线程挂起,一一记录堆栈,并且会对堆栈地址尝试符号化。

对 KSCrash 的源码笔记写了好久,本系列笔记也只是按代码执行顺序过了一遍,大概了解了 KSCrash 的工作内容和工作过程。后续有精力的话再对其实现原理与细节进行补充吧。