misc: remote anr util
This commit is contained in:
parent
32b8569c7e
commit
f4614a3738
@ -7,9 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0AE14FEF25A35685007EF89B /* BSBacktraceLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 0AE14FEC25A35685007EF89B /* BSBacktraceLogger.m */; };
|
||||
0AE14FF025A35685007EF89B /* AnrDetectUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE14FED25A35685007EF89B /* AnrDetectUtil.swift */; };
|
||||
0AE14FF125A35685007EF89B /* AnrDetectThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AE14FEE25A35685007EF89B /* AnrDetectThread.swift */; };
|
||||
12E1BA6D1DA59D9C2D48A175 /* libPods-ClashX Pro.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 6D4E9113CB2DD62F13AD213C /* libPods-ClashX Pro.a */; };
|
||||
4913C82321157D0200F6B87C /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4913C82221157D0200F6B87C /* Notification.swift */; };
|
||||
491E6203258A424D00313AEF /* CommonUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 491E61FD258A424500313AEF /* CommonUtils.m */; };
|
||||
49228457270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49228456270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift */; };
|
||||
@ -125,10 +123,6 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
0AE14FEB25A35685007EF89B /* BSBacktraceLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSBacktraceLogger.h; sourceTree = "<group>"; };
|
||||
0AE14FEC25A35685007EF89B /* BSBacktraceLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSBacktraceLogger.m; sourceTree = "<group>"; };
|
||||
0AE14FED25A35685007EF89B /* AnrDetectUtil.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnrDetectUtil.swift; sourceTree = "<group>"; };
|
||||
0AE14FEE25A35685007EF89B /* AnrDetectThread.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnrDetectThread.swift; sourceTree = "<group>"; };
|
||||
4913C82221157D0200F6B87C /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = "<group>"; };
|
||||
491E61FC258A424500313AEF /* CommonUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonUtils.h; sourceTree = "<group>"; };
|
||||
491E61FD258A424500313AEF /* CommonUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommonUtils.m; sourceTree = "<group>"; };
|
||||
@ -246,25 +240,6 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0AE14FE925A35685007EF89B /* ANR */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0AE14FEA25A35685007EF89B /* Backtrace */,
|
||||
0AE14FED25A35685007EF89B /* AnrDetectUtil.swift */,
|
||||
0AE14FEE25A35685007EF89B /* AnrDetectThread.swift */,
|
||||
);
|
||||
path = ANR;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0AE14FEA25A35685007EF89B /* Backtrace */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0AE14FEB25A35685007EF89B /* BSBacktraceLogger.h */,
|
||||
0AE14FEC25A35685007EF89B /* BSBacktraceLogger.m */,
|
||||
);
|
||||
path = Backtrace;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4913C82021157CEB00F6B87C /* Macro */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -369,7 +344,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4929F683258CE07500A435F6 /* UserDefaultWrapper.swift */,
|
||||
0AE14FE925A35685007EF89B /* ANR */,
|
||||
F976275D23634F18000EDEFE /* LoginServiceKit */,
|
||||
49722FE9211F338B00650A41 /* Witness */,
|
||||
);
|
||||
@ -706,7 +680,6 @@
|
||||
files = (
|
||||
49ABB749236B0F9E00535CD7 /* UnsafePointer+bridge.swift in Sources */,
|
||||
499A485522ED707300F6C675 /* RemoteConfigViewController.swift in Sources */,
|
||||
0AE14FF125A35685007EF89B /* AnrDetectThread.swift in Sources */,
|
||||
49CF3B5C20CE8068001EBF94 /* ClashResourceManager.swift in Sources */,
|
||||
4952C3D02117027C004A4FA8 /* ConfigFileManager.swift in Sources */,
|
||||
49BC061C212931F4005A0FE7 /* AboutViewController.swift in Sources */,
|
||||
@ -733,12 +706,10 @@
|
||||
492C4869210EE6B9004554A0 /* ApiRequest.swift in Sources */,
|
||||
49CF3B6520CEE06C001EBF94 /* ConfigManager.swift in Sources */,
|
||||
F9E754D0239CC21F00CEE7CC /* WebPortalManager.swift in Sources */,
|
||||
0AE14FF025A35685007EF89B /* AnrDetectUtil.swift in Sources */,
|
||||
495BFB8821919B9800C8779D /* RemoteConfigManager.swift in Sources */,
|
||||
4982F51F2344A216008804B0 /* Cgo+Convert.swift in Sources */,
|
||||
49722FF1211F338B00650A41 /* Witness.swift in Sources */,
|
||||
49722FF0211F338B00650A41 /* EventStream.swift in Sources */,
|
||||
0AE14FEF25A35685007EF89B /* BSBacktraceLogger.m in Sources */,
|
||||
499A486522EEA3FD00F6C675 /* Array+Safe.swift in Sources */,
|
||||
F92D0B24236BC12000575E15 /* SavedProxyModel.swift in Sources */,
|
||||
F92D0B2A236C759100575E15 /* NSTextField+Vibrancy.swift in Sources */,
|
||||
|
@ -815,17 +815,6 @@ extension AppDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: ANR
|
||||
extension AppDelegate {
|
||||
private func startAnrDetect() {
|
||||
#if DEBUG
|
||||
return
|
||||
#else
|
||||
AnrDetectUtil.shared.start()
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Memory
|
||||
|
||||
extension AppDelegate {
|
||||
|
81
ClashX/Vendor/ANR/AnrDetectThread.swift
vendored
81
ClashX/Vendor/ANR/AnrDetectThread.swift
vendored
@ -1,81 +0,0 @@
|
||||
//
|
||||
// ANRDetectorThread.swift
|
||||
// MacAnrDemo
|
||||
//
|
||||
// Created by miniLV on 2021/1/4.
|
||||
// Copyright © 2020 miniLV. All rights reserved.
|
||||
//
|
||||
public typealias AnrDetectCallBack = (_ report: String) -> Void
|
||||
|
||||
class AnrDetectThread: Thread {
|
||||
private var threshold: Double = 0
|
||||
private let maxThreadToPrint = 25
|
||||
private var handler: AnrDetectCallBack?
|
||||
|
||||
private var mainThreadId: mach_port_t!
|
||||
private let semaphore = DispatchSemaphore(value: 0)
|
||||
private var isMainThreadBlock = false
|
||||
|
||||
func start(threshold: Double, handler: @escaping AnrDetectCallBack) {
|
||||
if Thread.isMainThread {
|
||||
mainThreadId = mach_thread_self()
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
mainThreadId = mach_thread_self()
|
||||
}
|
||||
}
|
||||
|
||||
self.handler = handler
|
||||
self.threshold = threshold
|
||||
name = "com.west2online.ClashX.anrDetectThread"
|
||||
start()
|
||||
}
|
||||
|
||||
override func main() {
|
||||
while !isCancelled {
|
||||
isMainThreadBlock = true
|
||||
DispatchQueue.main.async {
|
||||
self.isMainThreadBlock = false
|
||||
self.semaphore.signal()
|
||||
}
|
||||
|
||||
usleep(useconds_t(threshold * 1000000))
|
||||
if isMainThreadBlock {
|
||||
didAnr()
|
||||
}
|
||||
|
||||
_ = semaphore.wait(timeout: DispatchTime.distantFuture)
|
||||
}
|
||||
}
|
||||
|
||||
func didAnr() {
|
||||
let allThreadTrace = traceAllThread(max: maxThreadToPrint)
|
||||
let report = "-----------\n main thread is \(Int(mainThreadId))\n\(allThreadTrace)\n---------"
|
||||
|
||||
handler?(report)
|
||||
}
|
||||
|
||||
func traceMainThread() -> String {
|
||||
return BSBacktraceLogger.backtrace(ofMachthread: mainThreadId)
|
||||
}
|
||||
|
||||
func traceAllThread(max: Int = Int.max) -> String {
|
||||
var threads: thread_act_array_t?
|
||||
var totalThreadCount = mach_msg_type_number_t()
|
||||
|
||||
if task_threads(mach_task_self_, &threads, &totalThreadCount) != KERN_SUCCESS || threads == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
let targetCount = min(max, Int(totalThreadCount))
|
||||
var resultString = "Call Backtrace of \(targetCount)/\(totalThreadCount) threads:\n"
|
||||
|
||||
for i in 0..<targetCount {
|
||||
let index = Int(i)
|
||||
if let bt = BSBacktraceLogger.backtrace(ofMachthread: threads![index]) {
|
||||
resultString.append(bt)
|
||||
}
|
||||
}
|
||||
return resultString
|
||||
}
|
||||
}
|
30
ClashX/Vendor/ANR/AnrDetectUtil.swift
vendored
30
ClashX/Vendor/ANR/AnrDetectUtil.swift
vendored
@ -1,30 +0,0 @@
|
||||
//
|
||||
// AnrDetectUtil.swift
|
||||
// Rooms
|
||||
//
|
||||
// Created by miniLV on 2021/1/4.
|
||||
// Copyright © 2020 miniLV. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
import Cocoa
|
||||
|
||||
class AnrDetectUtil {
|
||||
static let shared = AnrDetectUtil()
|
||||
|
||||
private init() {}
|
||||
lazy var thread = AnrDetectThread()
|
||||
|
||||
|
||||
func start(threshold: Double = 10) {
|
||||
thread.start(threshold: threshold) {
|
||||
[weak thread] allThreadBackTrace in
|
||||
Logger.log("[ANR] \(allThreadBackTrace)", level: .error)
|
||||
thread?.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
func stop() {
|
||||
thread.cancel()
|
||||
}
|
||||
}
|
25
ClashX/Vendor/ANR/Backtrace/BSBacktraceLogger.h
vendored
25
ClashX/Vendor/ANR/Backtrace/BSBacktraceLogger.h
vendored
@ -1,25 +0,0 @@
|
||||
//
|
||||
// BSBacktraceLogger.h
|
||||
// BSBacktraceLogger
|
||||
//
|
||||
// Created by 张星宇 on 16/8/27.
|
||||
// Copyright © 2016年 bestswifter. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <mach/mach.h>
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach-o/nlist.h>
|
||||
|
||||
@interface BSBacktraceLogger : NSObject
|
||||
|
||||
+ (NSString *)backtraceOfMachthread:(thread_t)thread;
|
||||
|
||||
@end
|
||||
|
353
ClashX/Vendor/ANR/Backtrace/BSBacktraceLogger.m
vendored
353
ClashX/Vendor/ANR/Backtrace/BSBacktraceLogger.m
vendored
@ -1,353 +0,0 @@
|
||||
//
|
||||
// BSBacktraceLogger.m
|
||||
// BSBacktraceLogger
|
||||
//
|
||||
// Created by 张星宇 on 16/8/27.
|
||||
// Copyright © 2016年 bestswifter. All rights reserved.
|
||||
//
|
||||
|
||||
#import "BSBacktraceLogger.h"
|
||||
#import <mach/mach.h>
|
||||
#include <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach-o/nlist.h>
|
||||
|
||||
#pragma -mark DEFINE MACRO FOR DIFFERENT CPU ARCHITECTURE
|
||||
#if defined(__arm64__)
|
||||
#define DETAG_INSTRUCTION_ADDRESS(A) ((A) & ~(3UL))
|
||||
#define BS_THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT
|
||||
#define BS_THREAD_STATE ARM_THREAD_STATE64
|
||||
#define BS_FRAME_POINTER __fp
|
||||
#define BS_STACK_POINTER __sp
|
||||
#define BS_INSTRUCTION_ADDRESS __pc
|
||||
|
||||
#elif defined(__arm__)
|
||||
#define DETAG_INSTRUCTION_ADDRESS(A) ((A) & ~(1UL))
|
||||
#define BS_THREAD_STATE_COUNT ARM_THREAD_STATE_COUNT
|
||||
#define BS_THREAD_STATE ARM_THREAD_STATE
|
||||
#define BS_FRAME_POINTER __r[7]
|
||||
#define BS_STACK_POINTER __sp
|
||||
#define BS_INSTRUCTION_ADDRESS __pc
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
#define DETAG_INSTRUCTION_ADDRESS(A) (A)
|
||||
#define BS_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT
|
||||
#define BS_THREAD_STATE x86_THREAD_STATE64
|
||||
#define BS_FRAME_POINTER __rbp
|
||||
#define BS_STACK_POINTER __rsp
|
||||
#define BS_INSTRUCTION_ADDRESS __rip
|
||||
|
||||
#elif defined(__i386__)
|
||||
#define DETAG_INSTRUCTION_ADDRESS(A) (A)
|
||||
#define BS_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT
|
||||
#define BS_THREAD_STATE x86_THREAD_STATE32
|
||||
#define BS_FRAME_POINTER __ebp
|
||||
#define BS_STACK_POINTER __esp
|
||||
#define BS_INSTRUCTION_ADDRESS __eip
|
||||
|
||||
#endif
|
||||
|
||||
#define CALL_INSTRUCTION_FROM_RETURN_ADDRESS(A) (DETAG_INSTRUCTION_ADDRESS((A)) - 1)
|
||||
|
||||
#if defined(__LP64__)
|
||||
#define TRACE_FMT "%-4d%-31s 0x%016lx %s + %lu"
|
||||
#define POINTER_FMT "0x%016lx"
|
||||
#define POINTER_SHORT_FMT "0x%lx"
|
||||
#define BS_NLIST struct nlist_64
|
||||
#else
|
||||
#define TRACE_FMT "%-4d%-31s 0x%08lx %s + %lu"
|
||||
#define POINTER_FMT "0x%08lx"
|
||||
#define POINTER_SHORT_FMT "0x%lx"
|
||||
#define BS_NLIST struct nlist
|
||||
#endif
|
||||
|
||||
|
||||
typedef struct BSStackFrameEntry{
|
||||
const struct BSStackFrameEntry *const previous;
|
||||
const uintptr_t return_address;
|
||||
} BSStackFrameEntry;
|
||||
|
||||
@implementation BSBacktraceLogger
|
||||
|
||||
+ (NSString *)backtraceOfMachthread:(thread_t)thread {
|
||||
return _bs_backtraceOfThread(thread);
|
||||
}
|
||||
|
||||
#pragma -mark Get call backtrace of a mach_thread
|
||||
NSString *_bs_backtraceOfThread(thread_t thread) {
|
||||
uintptr_t backtraceBuffer[50];
|
||||
int i = 0;
|
||||
NSMutableString *resultString = [[NSMutableString alloc] initWithFormat:@"Backtrace of Thread %u:\n", thread];
|
||||
|
||||
_STRUCT_MCONTEXT machineContext;
|
||||
if(!bs_fillThreadStateIntoMachineContext(thread, &machineContext)) {
|
||||
return [NSString stringWithFormat:@"Fail to get information about thread: %u", thread];
|
||||
}
|
||||
|
||||
const uintptr_t instructionAddress = bs_mach_instructionAddress(&machineContext);
|
||||
backtraceBuffer[i] = instructionAddress;
|
||||
++i;
|
||||
|
||||
uintptr_t linkRegister = bs_mach_linkRegister(&machineContext);
|
||||
if (linkRegister) {
|
||||
backtraceBuffer[i] = linkRegister;
|
||||
i++;
|
||||
}
|
||||
|
||||
if(instructionAddress == 0) {
|
||||
return @"Fail to get instruction address";
|
||||
}
|
||||
|
||||
BSStackFrameEntry frame = {0};
|
||||
const uintptr_t framePtr = bs_mach_framePointer(&machineContext);
|
||||
if(framePtr == 0 ||
|
||||
bs_mach_copyMem((void *)framePtr, &frame, sizeof(frame)) != KERN_SUCCESS) {
|
||||
return @"Fail to get frame pointer";
|
||||
}
|
||||
|
||||
for(; i < 50; i++) {
|
||||
backtraceBuffer[i] = frame.return_address;
|
||||
if(backtraceBuffer[i] == 0 ||
|
||||
frame.previous == 0 ||
|
||||
bs_mach_copyMem(frame.previous, &frame, sizeof(frame)) != KERN_SUCCESS) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int backtraceLength = i;
|
||||
Dl_info symbolicated[backtraceLength];
|
||||
bs_symbolicate(backtraceBuffer, symbolicated, backtraceLength, 0);
|
||||
for (int i = 0; i < backtraceLength; ++i) {
|
||||
[resultString appendFormat:@"%@", bs_logBacktraceEntry(i, backtraceBuffer[i], &symbolicated[i])];
|
||||
}
|
||||
[resultString appendFormat:@"\n"];
|
||||
return [resultString copy];
|
||||
}
|
||||
|
||||
#pragma -mark GenerateBacbsrackEnrty
|
||||
NSString* bs_logBacktraceEntry(const int entryNum,
|
||||
const uintptr_t address,
|
||||
const Dl_info* const dlInfo) {
|
||||
char faddrBuff[20];
|
||||
char saddrBuff[20];
|
||||
|
||||
const char* fname = bs_lastPathEntry(dlInfo->dli_fname);
|
||||
if(fname == NULL) {
|
||||
sprintf(faddrBuff, POINTER_FMT, (uintptr_t)dlInfo->dli_fbase);
|
||||
fname = faddrBuff;
|
||||
}
|
||||
|
||||
uintptr_t offset = address - (uintptr_t)dlInfo->dli_saddr;
|
||||
const char* sname = dlInfo->dli_sname;
|
||||
if(sname == NULL) {
|
||||
sprintf(saddrBuff, POINTER_SHORT_FMT, (uintptr_t)dlInfo->dli_fbase);
|
||||
sname = saddrBuff;
|
||||
offset = address - (uintptr_t)dlInfo->dli_fbase;
|
||||
}
|
||||
return [NSString stringWithFormat:@"%-30s 0x%08" PRIxPTR " %s + %lu\n" ,fname, (uintptr_t)address, sname, offset];
|
||||
}
|
||||
|
||||
const char* bs_lastPathEntry(const char* const path) {
|
||||
if(path == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* lastFile = strrchr(path, '/');
|
||||
return lastFile == NULL ? path : lastFile + 1;
|
||||
}
|
||||
|
||||
#pragma -mark HandleMachineContext
|
||||
bool bs_fillThreadStateIntoMachineContext(thread_t thread, _STRUCT_MCONTEXT *machineContext) {
|
||||
mach_msg_type_number_t state_count = BS_THREAD_STATE_COUNT;
|
||||
kern_return_t kr = thread_get_state(thread, BS_THREAD_STATE, (thread_state_t)&machineContext->__ss, &state_count);
|
||||
return (kr == KERN_SUCCESS);
|
||||
}
|
||||
|
||||
uintptr_t bs_mach_framePointer(mcontext_t const machineContext){
|
||||
return machineContext->__ss.BS_FRAME_POINTER;
|
||||
}
|
||||
|
||||
uintptr_t bs_mach_stackPointer(mcontext_t const machineContext){
|
||||
return machineContext->__ss.BS_STACK_POINTER;
|
||||
}
|
||||
|
||||
uintptr_t bs_mach_instructionAddress(mcontext_t const machineContext){
|
||||
return machineContext->__ss.BS_INSTRUCTION_ADDRESS;
|
||||
}
|
||||
|
||||
uintptr_t bs_mach_linkRegister(mcontext_t const machineContext){
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
return 0;
|
||||
#else
|
||||
return machineContext->__ss.__lr;
|
||||
#endif
|
||||
}
|
||||
|
||||
kern_return_t bs_mach_copyMem(const void *const src, void *const dst, const size_t numBytes){
|
||||
vm_size_t bytesCopied = 0;
|
||||
return vm_read_overwrite(mach_task_self(), (vm_address_t)src, (vm_size_t)numBytes, (vm_address_t)dst, &bytesCopied);
|
||||
}
|
||||
|
||||
#pragma -mark Symbolicate
|
||||
void bs_symbolicate(const uintptr_t* const backtraceBuffer,
|
||||
Dl_info* const symbolsBuffer,
|
||||
const int numEntries,
|
||||
const int skippedEntries){
|
||||
int i = 0;
|
||||
|
||||
if(!skippedEntries && i < numEntries) {
|
||||
bs_dladdr(backtraceBuffer[i], &symbolsBuffer[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
for(; i < numEntries; i++) {
|
||||
bs_dladdr(CALL_INSTRUCTION_FROM_RETURN_ADDRESS(backtraceBuffer[i]), &symbolsBuffer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool bs_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;
|
||||
|
||||
const uint32_t idx = bs_imageIndexContainingAddress(address);
|
||||
if(idx == UINT_MAX) {
|
||||
return false;
|
||||
}
|
||||
const struct mach_header* header = _dyld_get_image_header(idx);
|
||||
const uintptr_t imageVMAddrSlide = (uintptr_t)_dyld_get_image_vmaddr_slide(idx);
|
||||
const uintptr_t addressWithSlide = address - imageVMAddrSlide;
|
||||
const uintptr_t segmentBase = bs_segmentBaseOfImageIndex(idx) + imageVMAddrSlide;
|
||||
if(segmentBase == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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 BS_NLIST* bestMatch = NULL;
|
||||
uintptr_t bestDistance = ULONG_MAX;
|
||||
uintptr_t cmdPtr = bs_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;
|
||||
const BS_NLIST* symbolTable = (BS_NLIST*)(segmentBase + symtabCmd->symoff);
|
||||
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) {
|
||||
info->dli_saddr = (void*)(bestMatch->n_value + imageVMAddrSlide);
|
||||
info->dli_sname = (char*)((intptr_t)stringTable + (intptr_t)bestMatch->n_un.n_strx);
|
||||
if(*info->dli_sname == '_') {
|
||||
info->dli_sname++;
|
||||
}
|
||||
// This happens if all symbols have been stripped.
|
||||
if(info->dli_saddr == info->dli_fbase && bestMatch->n_type == 3) {
|
||||
info->dli_sname = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
cmdPtr += loadCmd->cmdsize;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uintptr_t bs_firstCmdAfterHeader(const struct mach_header* const header) {
|
||||
switch(header->magic) {
|
||||
case MH_MAGIC:
|
||||
case MH_CIGAM:
|
||||
return (uintptr_t)(header + 1);
|
||||
case MH_MAGIC_64:
|
||||
case MH_CIGAM_64:
|
||||
return (uintptr_t)(((struct mach_header_64*)header) + 1);
|
||||
default:
|
||||
return 0; // Header is corrupt
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t bs_imageIndexContainingAddress(const uintptr_t address) {
|
||||
const uint32_t imageCount = _dyld_image_count();
|
||||
const struct mach_header* header = 0;
|
||||
|
||||
for(uint32_t iImg = 0; iImg < imageCount; iImg++) {
|
||||
header = _dyld_get_image_header(iImg);
|
||||
if(header != NULL) {
|
||||
// Look for a segment command with this address within its range.
|
||||
uintptr_t addressWSlide = address - (uintptr_t)_dyld_get_image_vmaddr_slide(iImg);
|
||||
uintptr_t cmdPtr = bs_firstCmdAfterHeader(header);
|
||||
if(cmdPtr == 0) {
|
||||
continue;
|
||||
}
|
||||
for(uint32_t iCmd = 0; iCmd < header->ncmds; iCmd++) {
|
||||
const struct load_command* loadCmd = (struct load_command*)cmdPtr;
|
||||
if(loadCmd->cmd == LC_SEGMENT) {
|
||||
const struct segment_command* segCmd = (struct segment_command*)cmdPtr;
|
||||
if(addressWSlide >= segCmd->vmaddr &&
|
||||
addressWSlide < segCmd->vmaddr + segCmd->vmsize) {
|
||||
return iImg;
|
||||
}
|
||||
}
|
||||
else if(loadCmd->cmd == LC_SEGMENT_64) {
|
||||
const struct segment_command_64* segCmd = (struct segment_command_64*)cmdPtr;
|
||||
if(addressWSlide >= segCmd->vmaddr &&
|
||||
addressWSlide < segCmd->vmaddr + segCmd->vmsize) {
|
||||
return iImg;
|
||||
}
|
||||
}
|
||||
cmdPtr += loadCmd->cmdsize;
|
||||
}
|
||||
}
|
||||
}
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
uintptr_t bs_segmentBaseOfImageIndex(const uint32_t idx) {
|
||||
const struct mach_header* header = _dyld_get_image_header(idx);
|
||||
|
||||
// Look for a segment command and return the file image address.
|
||||
uintptr_t cmdPtr = bs_firstCmdAfterHeader(header);
|
||||
if(cmdPtr == 0) {
|
||||
return 0;
|
||||
}
|
||||
for(uint32_t i = 0;i < header->ncmds; i++) {
|
||||
const struct load_command* loadCmd = (struct load_command*)cmdPtr;
|
||||
if(loadCmd->cmd == LC_SEGMENT) {
|
||||
const struct segment_command* segmentCmd = (struct segment_command*)cmdPtr;
|
||||
if(strcmp(segmentCmd->segname, SEG_LINKEDIT) == 0) {
|
||||
return segmentCmd->vmaddr - segmentCmd->fileoff;
|
||||
}
|
||||
}
|
||||
else if(loadCmd->cmd == LC_SEGMENT_64) {
|
||||
const struct segment_command_64* segmentCmd = (struct segment_command_64*)cmdPtr;
|
||||
if(strcmp(segmentCmd->segname, SEG_LINKEDIT) == 0) {
|
||||
return (uintptr_t)(segmentCmd->vmaddr - segmentCmd->fileoff);
|
||||
}
|
||||
}
|
||||
cmdPtr += loadCmd->cmdsize;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue
Block a user