Skip to content

Commit 115b794

Browse files
committed
feat: 根据 Mach-O 符号表 解析符号
1 parent 52790eb commit 115b794

File tree

11 files changed

+351
-61
lines changed

11 files changed

+351
-61
lines changed

Example/Pods/Pods.xcodeproj/project.pbxproj

Lines changed: 12 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

SKApmTools/Classes/ANR/SKANRMonitor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ open class SKANRMonitor: NSObject{
4444
// for (index, symbol) in callStackSymbols.enumerated() {
4545
// print("symbol【\(index)】: \(symbol)")
4646
// }
47-
let traces = SKStackTrace.stackTrace(of: Thread.main)
47+
let traces = SKBackTrace.backTrace(of: Thread.main)
4848
for (index, symbol) in traces.enumerated() {
4949
print("symbol【\(index)】: \(symbol.info)")
5050
}

SKApmTools/Classes/StackTrace/SKStackTrace.swift renamed to SKApmTools/Classes/BackTrace/SKBackTrace.swift

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// SKStackTrace.swift
2+
// SKBackTrace.swift
33
// SKApmTools
44
//
55
// Created by KUN on 2022/10/26.
@@ -8,48 +8,48 @@
88
import Foundation
99
import MachO
1010

11-
public class SKStackTrace {
11+
public class SKBackTrace {
1212

1313
/// 获取指定线程的堆栈信息
14-
public static func stackTrace(of thread: Thread) -> [SKStackSymbol] {
14+
public static func backTrace(of thread: Thread) -> [SKStackSymbol] {
1515
let mach_thread = _machThread(from: thread)
1616
var symbols : [SKStackSymbol] = []
1717
let stackSize : UInt32 = 128
1818
let addrs = UnsafeMutablePointer<UnsafeMutableRawPointer?>.allocate(capacity: Int(stackSize))
1919
defer { addrs.deallocate() }
20-
let frameCount = mach_stack_trace(mach_thread, stack: addrs, maxSymbols: Int32(stackSize))
20+
let frameCount = mach_back_trace(mach_thread, stack: addrs, maxSymbols: Int32(stackSize))
2121
let buf = UnsafeBufferPointer(start: addrs, count: Int(frameCount))
22-
22+
/// 解析堆栈地址
2323
for (index, addr) in buf.enumerated() {
2424
guard let addr = addr else { continue }
2525
let address = UInt(bitPattern: addr)
26-
let symbol = SKStackSymbol(symbol: "not symbol",
27-
file: "",
28-
address: address,
29-
symbolAddress: 0,
30-
image: "dylib",
31-
offset: 0,
32-
index: index)
26+
let symbol: SKStackSymbol
27+
symbol = mach_O_parseSymbol(with: address, index: index)
3328
symbols.append(symbol)
3429
}
3530
return symbols
3631
}
3732

3833
/// Thread to mach thread
34+
/// 获取线程对应的线程标识
3935
private static func _machThread(from thread: Thread) -> thread_t {
4036
guard let (threads, count) = _machAllThread() else {
4137
return mach_thread_self()
4238
}
4339

40+
// 判断目标 thread, 如果是主线程,直接返回对应标识
4441
if thread.isMainThread {
45-
return get_main_thread_t()
42+
return get_main_thread_id()
4643
}
4744

4845
var name : [Int8] = []
4946
let originName = thread.name
5047

5148
for i in 0 ..< count {
5249
let index = Int(i)
50+
/// NSThread 取到的 name 和 pthread name 是一致的
51+
/// 遍历 threads,通过 pthread_from_mach_thread_np 逐个获取 name 进行比对
52+
/// 匹配则返回 NSThread 对应的线程标识
5353
if let p_thread = pthread_from_mach_thread_np((threads[index])) {
5454
name.append(Int8(Character("\0").ascii ?? 0))
5555
pthread_getname_np(p_thread, &name, MemoryLayout<Int8>.size * 256)
@@ -68,8 +68,10 @@ public class SKStackTrace {
6868
private static func _machAllThread() -> (thread_act_array_t, mach_msg_type_number_t)? {
6969
var thread_array : thread_act_array_t?
7070
var number_t : mach_msg_type_number_t = 0
71+
/// 进程 ID
7172
let mach_task = mach_task_self_
7273

74+
/// 通过 task_threads 获取当前进程中线程列表 thread_act_array_t
7375
guard task_threads(mach_task, &(thread_array), &number_t) == KERN_SUCCESS else {
7476
return nil
7577
}
@@ -80,7 +82,7 @@ public class SKStackTrace {
8082
}
8183

8284
@_silgen_name("mach_backtrace")
83-
public func mach_stack_trace(_ thread: thread_t,
85+
public func mach_back_trace(_ thread: thread_t,
8486
stack: UnsafeMutablePointer<UnsafeMutableRawPointer?>,
8587
maxSymbols: Int32) -> Int32
8688

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//
2+
// SKDynamicSymbolParser.swift
3+
// SKApmTools
4+
//
5+
// Created by KUN on 2022/10/26.
6+
//
7+
8+
import Foundation
9+
10+
/// ASLR
11+
public private(set) var _appASLR : Int = 0
12+
13+
/// 根据 Mach-O 符号表 解析符号
14+
func mach_O_parseSymbol(with address: UInt, index: Int) -> SKStackSymbol {
15+
var info = dl_info()
16+
sk_dladdr(address, &info)
17+
18+
return SKStackSymbol(symbol: _symbol(info: info),
19+
file: _dli_fname(with: info),
20+
address: address,
21+
symbolAddress: unsafeBitCast(info.dli_saddr, to: UInt.self),
22+
image: _image(info: info),
23+
offset: _offset(info: info, address: address),
24+
index: index)
25+
}
26+
27+
/// 从动态符号表查找符号
28+
private func _dynamicParseSymbol(with address: UInt, aslr: Int) -> SKBacktraceEntry? {
29+
return nil
30+
}
31+
32+
/// the symbol nearest the address
33+
private func _symbol(info: dl_info) -> String {
34+
if
35+
let dli_sname = info.dli_sname,
36+
let sname = String(validatingUTF8: dli_sname) {
37+
return sname
38+
}
39+
else if
40+
let dli_fname = info.dli_fname,
41+
let _ = String(validatingUTF8: dli_fname) {
42+
return _image(info: info)
43+
}
44+
else {
45+
return String(format: "0x%1x", UInt(bitPattern: info.dli_saddr))
46+
}
47+
}
48+
49+
/// thanks to https://github.com/mattgallagher/CwlUtils/blob/master/Sources/CwlUtils/CwlAddressInfo.swift
50+
/// the "image" (shared object pathname) for the instruction
51+
private func _image(info: dl_info) -> String {
52+
guard
53+
let dli_fname = info.dli_fname,
54+
let fname = String(validatingUTF8: dli_fname),
55+
let _ = fname.range(of: "/", options: .backwards, range: nil, locale: nil)
56+
else {
57+
return "???"
58+
}
59+
60+
return (fname as NSString).lastPathComponent
61+
}
62+
63+
/// the address' offset relative to the nearest symbol
64+
private func _offset(info: dl_info, address: UInt) -> UInt {
65+
if
66+
let dli_sname = info.dli_sname,
67+
let _ = String(validatingUTF8: dli_sname) {
68+
return address - UInt(bitPattern: info.dli_saddr)
69+
}
70+
else if
71+
let dli_fname = info.dli_fname,
72+
let _ = String(validatingUTF8: dli_fname) {
73+
return address - UInt(bitPattern: info.dli_fbase)
74+
}
75+
else {
76+
return address - UInt(bitPattern: info.dli_saddr)
77+
}
78+
}
79+
80+
private func _dli_fname(with info: dl_info) -> String {
81+
if sk_has_dli_fname(info) {
82+
return String(cString: info.dli_fname)
83+
}
84+
else {
85+
return "-"
86+
}
87+
}
88+
89+

SKApmTools/Classes/StackTrace/SKStackSymbol.swift renamed to SKApmTools/Classes/BackTrace/SKStackSymbol.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import Foundation
99

1010
public struct SKStackSymbol {
11-
1211
public let symbol: String
1312
public let file: String
1413
public let address: UInt
@@ -32,6 +31,16 @@ public struct SKStackSymbol {
3231
}
3332
}
3433

34+
public struct SKBacktraceEntry: Codable {
35+
public let `class`: String
36+
public let name: String
37+
public let address: UInt
38+
39+
public var log: String {
40+
return "calss: \(self.class) name:\(name) address:\(String(address, radix: 16))\n"
41+
}
42+
}
43+
3544
@_silgen_name("swift_demangle")
3645
func _stdlib_demangleImpl(
3746
mangledName: UnsafePointer<CChar>?,

SKApmTools/Classes/StackTrace/mach_backtrace.c renamed to SKApmTools/Classes/BackTrace/mach_backtrace.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
int mach_backtrace(thread_t thread, void** stack, int maxSymbols) {
9393
_STRUCT_MCONTEXT machineContext;
9494
mach_msg_type_number_t stateCount = THREAD_STATE_COUNT;
95-
95+
/// 根据线程标识 通过 thread_get_state 获取到线程寄存器状态结构体
9696
kern_return_t kret = thread_get_state(thread, THREAD_STATE_FLAVOR, (thread_state_t)&(machineContext.__ss), &stateCount);
9797
if (kret != KERN_SUCCESS) {
9898
return 0;
@@ -105,6 +105,7 @@ int mach_backtrace(thread_t thread, void** stack, int maxSymbols) {
105105
#endif
106106
void **currentFramePointer = (void **)machineContext.__ss.__framePointer;
107107
while (i < maxSymbols) {
108+
/// 构建一个递归结构体,分别指向自己和 lr, 递归找到所有地址
108109
void **previousFramePointer = *currentFramePointer;
109110
if (!previousFramePointer) break;
110111
stack[i] = *(currentFramePointer+1);

SKApmTools/Classes/StackTrace/mach_backtrace.h renamed to SKApmTools/Classes/BackTrace/mach_backtrace.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@
55

66
#include <mach/mach.h>
77

8+
#include <dlfcn.h>
9+
#include <pthread.h>
10+
#include <sys/types.h>
11+
#include <limits.h>
12+
#include <string.h>
13+
#include <mach-o/dyld.h>
14+
#include <mach-o/nlist.h>
15+
816
/**
917
* fill a backtrace call stack array of given thread
1018
*

0 commit comments

Comments
 (0)