From b6581d6059bd337d4bda6103138375a0b4a42fad Mon Sep 17 00:00:00 2001 From: denglibing5 Date: Wed, 20 Dec 2023 15:01:50 +0800 Subject: [PATCH] =?UTF-8?q?bugfix1:=20=E9=97=AE=E9=A2=98=E6=8F=8F=E8=BF=B0?= =?UTF-8?q?=EF=BC=9A=E6=96=B9=E6=B3=95=E7=9A=84=E8=B0=83=E7=94=A8=E9=93=BE?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E6=A0=B9=E6=8D=AE=20callDepth=20=E6=9D=A5?= =?UTF-8?q?=E5=88=A4=E6=96=AD=EF=BC=8C=E4=B8=8D=E7=84=B6=E6=89=80=E6=9C=89?= =?UTF-8?q?=E5=B1=82=E7=BA=A7=E7=9B=B8=E7=AD=89=E7=9A=84=E6=B7=B1=E5=BA=A6?= =?UTF-8?q?=EF=BC=8C=E9=83=BD=E5=9C=A8=E4=B8=80=E4=B8=AA=E8=B0=83=E7=94=A8?= =?UTF-8?q?=E9=93=BE=E8=B7=AF=E4=B8=AD=E4=BA=86=E3=80=82=20=E8=A7=A3?= =?UTF-8?q?=E5=86=B3=E6=96=B9=E6=A1=88=EF=BC=9A=E9=80=9A=E8=BF=87=20lr=20?= =?UTF-8?q?=E4=BD=9C=E4=B8=BA=E6=96=B9=E6=B3=95=E8=B0=83=E7=94=A8=E7=9A=84?= =?UTF-8?q?=E6=A0=87=E8=AF=86=EF=BC=8C=E4=BD=9C=E4=B8=BA=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E9=93=BE=E6=9B=B4=E5=8A=A0=E5=90=88=E7=90=86?= =?UTF-8?q?=E3=80=82=20=E6=96=B0=E5=A2=9E=EF=BC=9A=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E7=9A=84=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=20lr=20=E7=9A=84=E5=90=88=E7=90=86=E6=80=A7=E3=80=82=20?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E5=8F=AF=E4=BB=A5=E5=8F=82=E8=80=83=EF=BC=9A?= =?UTF-8?q?https://github.com/ming1016/GCDFetchFeed/pull/30?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bugfix2: 问题描述:iOS15及以上的系统运行崩溃 解决方案:更新 fishhook 代码解决,详细问题参考 https://github.com/facebook/fishhook/pull/87 --- .../xcschemes/xcschememanagement.plist | 24 ++ .../Qi_ObjcMsgHook.xcodeproj/project.pbxproj | 30 +- .../xcschemes/xcschememanagement.plist | 14 + .../UserInterfaceState.xcuserstate | Bin 0 -> 19152 bytes .../Qi_ObjcMsgHook/QiLagMonitor/QiCallTrace.m | 16 +- .../QiLagMonitor/QiCallTraceCore.c | 216 +------------- .../QiLagMonitor/QiCallTraceCore.h | 8 +- .../QiLagMonitor/QiCallTraceTimeCostModel.h | 3 + .../QiLagMonitor/fishhook/fishhook.c | 264 ++++++++++++++++++ .../QiLagMonitor/fishhook/fishhook.h | 76 +++++ Qi_ObjcMsgHook/Qi_ObjcMsgHook/SceneDelegate.m | 51 ++++ 11 files changed, 493 insertions(+), 209 deletions(-) create mode 100644 Qi_ObjcMsgHook/Pods/Pods.xcodeproj/xcuserdata/denglibing.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 Qi_ObjcMsgHook/Qi_ObjcMsgHook.xcodeproj/xcuserdata/denglibing.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 Qi_ObjcMsgHook/Qi_ObjcMsgHook.xcworkspace/xcuserdata/denglibing.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/fishhook/fishhook.c create mode 100644 Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/fishhook/fishhook.h diff --git a/Qi_ObjcMsgHook/Pods/Pods.xcodeproj/xcuserdata/denglibing.xcuserdatad/xcschemes/xcschememanagement.plist b/Qi_ObjcMsgHook/Pods/Pods.xcodeproj/xcuserdata/denglibing.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..951edd1 --- /dev/null +++ b/Qi_ObjcMsgHook/Pods/Pods.xcodeproj/xcuserdata/denglibing.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,24 @@ + + + + + SchemeUserState + + FMDB.xcscheme_^#shared#^_ + + orderHint + 3 + + Pods-Qi_ObjcMsgHook.xcscheme_^#shared#^_ + + orderHint + 2 + + ReactiveCocoa.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Qi_ObjcMsgHook/Qi_ObjcMsgHook.xcodeproj/project.pbxproj b/Qi_ObjcMsgHook/Qi_ObjcMsgHook.xcodeproj/project.pbxproj index 35d90b1..3b7c563 100644 --- a/Qi_ObjcMsgHook/Qi_ObjcMsgHook.xcodeproj/project.pbxproj +++ b/Qi_ObjcMsgHook/Qi_ObjcMsgHook.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 9164681BB0A298AD454193ED /* libPods-Qi_ObjcMsgHook.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D47E09EA3AA7D83E2DB7869D /* libPods-Qi_ObjcMsgHook.a */; }; + D9DCCA7C2B32C77A00465F0F /* fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = D9DCCA7A2B32C77A00465F0F /* fishhook.c */; }; F14ED94C23850FF400D35A9D /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F14ED94B23850FF400D35A9D /* AppDelegate.m */; }; F14ED94F23850FF400D35A9D /* SceneDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F14ED94E23850FF400D35A9D /* SceneDelegate.m */; }; F14ED95723850FF500D35A9D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F14ED95623850FF500D35A9D /* Assets.xcassets */; }; @@ -28,6 +29,8 @@ 4976B0886AEEBF116F87F1BF /* Pods-Qi_ObjcMsgHook.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Qi_ObjcMsgHook.debug.xcconfig"; path = "Target Support Files/Pods-Qi_ObjcMsgHook/Pods-Qi_ObjcMsgHook.debug.xcconfig"; sourceTree = ""; }; 7D50652F404D4AE35BD9BF75 /* Pods-Qi_ObjcMsgHook.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Qi_ObjcMsgHook.release.xcconfig"; path = "Target Support Files/Pods-Qi_ObjcMsgHook/Pods-Qi_ObjcMsgHook.release.xcconfig"; sourceTree = ""; }; D47E09EA3AA7D83E2DB7869D /* libPods-Qi_ObjcMsgHook.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Qi_ObjcMsgHook.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + D9DCCA7A2B32C77A00465F0F /* fishhook.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = fishhook.c; sourceTree = ""; }; + D9DCCA7B2B32C77A00465F0F /* fishhook.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = fishhook.h; sourceTree = ""; }; F14ED94723850FF400D35A9D /* Qi_ObjcMsgHook.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Qi_ObjcMsgHook.app; sourceTree = BUILT_PRODUCTS_DIR; }; F14ED94A23850FF400D35A9D /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; F14ED94B23850FF400D35A9D /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; @@ -79,6 +82,15 @@ path = Pods; sourceTree = ""; }; + D9DCCA792B32C77A00465F0F /* fishhook */ = { + isa = PBXGroup; + children = ( + D9DCCA7A2B32C77A00465F0F /* fishhook.c */, + D9DCCA7B2B32C77A00465F0F /* fishhook.h */, + ); + path = fishhook; + sourceTree = ""; + }; F14ED93E23850FF400D35A9D = { isa = PBXGroup; children = ( @@ -118,6 +130,7 @@ F14ED9632385108800D35A9D /* QiLagMonitor */ = { isa = PBXGroup; children = ( + D9DCCA792B32C77A00465F0F /* fishhook */, F14ED9762385193C00D35A9D /* QiLagMonitor.h */, F14ED9772385193C00D35A9D /* QiLagMonitor.m */, F14ED964238516FC00D35A9D /* QiCPUMonitor.h */, @@ -254,6 +267,7 @@ F14ED94F23850FF400D35A9D /* SceneDelegate.m in Sources */, F14ED97B2385196500D35A9D /* QiLagDB.m in Sources */, F14ED9752385184000D35A9D /* QiCallStackModel.m in Sources */, + D9DCCA7C2B32C77A00465F0F /* fishhook.c in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -386,8 +400,10 @@ baseConfigurationReference = 4976B0886AEEBF116F87F1BF /* Pods-Qi_ObjcMsgHook.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 8ZX24TX624; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = TKRM4J3XJV; GCC_C_LANGUAGE_STANDARD = gnu99; INFOPLIST_FILE = Qi_ObjcMsgHook/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -396,6 +412,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.qishare.ios.lsq; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = JingxiDev_18; TARGETED_DEVICE_FAMILY = 1; }; name = Debug; @@ -405,8 +423,10 @@ baseConfigurationReference = 7D50652F404D4AE35BD9BF75 /* Pods-Qi_ObjcMsgHook.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 8ZX24TX624; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = TKRM4J3XJV; GCC_C_LANGUAGE_STANDARD = gnu99; INFOPLIST_FILE = Qi_ObjcMsgHook/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -415,6 +435,8 @@ ); PRODUCT_BUNDLE_IDENTIFIER = com.qishare.ios.lsq; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = JingxiDev_18; TARGETED_DEVICE_FAMILY = 1; }; name = Release; diff --git a/Qi_ObjcMsgHook/Qi_ObjcMsgHook.xcodeproj/xcuserdata/denglibing.xcuserdatad/xcschemes/xcschememanagement.plist b/Qi_ObjcMsgHook/Qi_ObjcMsgHook.xcodeproj/xcuserdata/denglibing.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..b3f678a --- /dev/null +++ b/Qi_ObjcMsgHook/Qi_ObjcMsgHook.xcodeproj/xcuserdata/denglibing.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + Qi_ObjcMsgHook.xcscheme_^#shared#^_ + + orderHint + 1 + + + + diff --git a/Qi_ObjcMsgHook/Qi_ObjcMsgHook.xcworkspace/xcuserdata/denglibing.xcuserdatad/UserInterfaceState.xcuserstate b/Qi_ObjcMsgHook/Qi_ObjcMsgHook.xcworkspace/xcuserdata/denglibing.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..28d19be98098b83d64f3392e225c30273409fa69 GIT binary patch literal 19152 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 M^8*Gcnl$PE0TXOB^8f$< literal 0 HcmV?d00001 diff --git a/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTrace.m b/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTrace.m index 98ec038..636d317 100644 --- a/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTrace.m +++ b/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTrace.m @@ -90,6 +90,12 @@ + (void)appendRecord:(QiCallTraceTimeCostModel *)cost to:(NSMutableString *)mStr model.isClassMethod = class_isMetaClass(rd->cls); model.timeCost = (double)rd->time / 1000000.0; model.callDepth = rd->depth; + model.lr = rd->lr; + + if (rd->caller_record != NULL) { + model.callerLr = rd->caller_record->lr; + } + [arr addObject:model]; } NSUInteger count = arr.count; @@ -99,14 +105,18 @@ + (void)appendRecord:(QiCallTraceTimeCostModel *)cost to:(NSMutableString *)mStr [arr removeObjectAtIndex:i]; //Todo:不需要循环,直接设置下一个,然后判断好边界就行 for (NSUInteger j = i; j < count - 1; j++) { - //下一个深度小的话就开始将后面的递归的往 sub array 里添加 - if (arr[j].callDepth + 1 == model.callDepth) { + // 下一个深度小的话就开始将后面的递归的往 sub array 里添加 + // ⚠️⚠️ 这里的bug:不能根据 callDepth 来判断,不然所有层级相等的深度,都在一个调用链路中了 + // 需要根据调用链路来关联 + if (arr[j].lr == model.callerLr) { NSMutableArray *sub = (NSMutableArray *)arr[j].subCosts; if (!sub) { sub = [NSMutableArray new]; arr[j].subCosts = sub; } - [sub insertObject:model atIndex:0]; + if (![sub containsObject:model]) { + [sub addObject:model]; + } } } i--; diff --git a/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTraceCore.c b/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTraceCore.c index 25c2660..903dc48 100644 --- a/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTraceCore.c +++ b/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTraceCore.c @@ -11,205 +11,7 @@ #ifdef __aarch64__ #pragma mark - fishhook -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* - * A structure representing a particular intended rebinding from a symbol - * name to its replacement - */ -struct rebinding { - const char *name; - void *replacement; - void **replaced; -}; - -/* - * For each rebinding in rebindings, rebinds references to external, indirect - * symbols with the specified name to instead point at replacement for each - * image in the calling process as well as for all future images that are loaded - * by the process. If rebind_functions is called more than once, the symbols to - * rebind are added to the existing list of rebindings, and if a given symbol - * is rebound more than once, the later rebinding will take precedence. - */ -static int fish_rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel); - - -#ifdef __LP64__ -typedef struct mach_header_64 mach_header_t; -typedef struct segment_command_64 segment_command_t; -typedef struct section_64 section_t; -typedef struct nlist_64 nlist_t; -#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 -#else -typedef struct mach_header mach_header_t; -typedef struct segment_command segment_command_t; -typedef struct section section_t; -typedef struct nlist nlist_t; -#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT -#endif - -#ifndef SEG_DATA_CONST -#define SEG_DATA_CONST "__DATA_CONST" -#endif - -struct rebindings_entry { - struct rebinding *rebindings; - size_t rebindings_nel; - struct rebindings_entry *next; -}; - -static struct rebindings_entry *_rebindings_head; - -static int prepend_rebindings(struct rebindings_entry **rebindings_head, - struct rebinding rebindings[], - size_t nel) { - struct rebindings_entry *new_entry = malloc(sizeof(struct rebindings_entry)); - if (!new_entry) { - return -1; - } - new_entry->rebindings = malloc(sizeof(struct rebinding) * nel); - if (!new_entry->rebindings) { - free(new_entry); - return -1; - } - memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel); - new_entry->rebindings_nel = nel; - new_entry->next = *rebindings_head; - *rebindings_head = new_entry; - return 0; -} - -static void perform_rebinding_with_section(struct rebindings_entry *rebindings, - section_t *section, - intptr_t slide, - nlist_t *symtab, - char *strtab, - uint32_t *indirect_symtab) { - uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; - void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); - for (uint i = 0; i < section->size / sizeof(void *); i++) { - uint32_t symtab_index = indirect_symbol_indices[i]; - if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL || - symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { - continue; - } - uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; - char *symbol_name = strtab + strtab_offset; - if (strnlen(symbol_name, 2) < 2) { - continue; - } - struct rebindings_entry *cur = rebindings; - while (cur) { - for (uint j = 0; j < cur->rebindings_nel; j++) { - if (strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) { - if (cur->rebindings[j].replaced != NULL && - indirect_symbol_bindings[i] != cur->rebindings[j].replacement) { - *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i]; - } - indirect_symbol_bindings[i] = cur->rebindings[j].replacement; - goto symbol_loop; - } - } - cur = cur->next; - } - symbol_loop:; - } -} - -static void rebind_symbols_for_image(struct rebindings_entry *rebindings, - const struct mach_header *header, - intptr_t slide) { - Dl_info info; - if (dladdr(header, &info) == 0) { - return; - } - - segment_command_t *cur_seg_cmd; - segment_command_t *linkedit_segment = NULL; - struct symtab_command* symtab_cmd = NULL; - struct dysymtab_command* dysymtab_cmd = NULL; - - uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); - for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { - cur_seg_cmd = (segment_command_t *)cur; - if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { - if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { - linkedit_segment = cur_seg_cmd; - } - } else if (cur_seg_cmd->cmd == LC_SYMTAB) { - symtab_cmd = (struct symtab_command*)cur_seg_cmd; - } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) { - dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd; - } - } - - if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment || - !dysymtab_cmd->nindirectsyms) { - return; - } - - // Find base symbol/string table addresses - uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; - nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); - char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); - - // Get indirect symbol table (array of uint32_t indices into symbol table) - uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); - - cur = (uintptr_t)header + sizeof(mach_header_t); - for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { - cur_seg_cmd = (segment_command_t *)cur; - if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { - if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 && - strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) { - continue; - } - for (uint j = 0; j < cur_seg_cmd->nsects; j++) { - section_t *sect = - (section_t *)(cur + sizeof(segment_command_t)) + j; - if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { - perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); - } - if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { - perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); - } - } - } - } -} - -static void _rebind_symbols_for_image(const struct mach_header *header, - intptr_t slide) { - rebind_symbols_for_image(_rebindings_head, header, slide); -} - -static int fish_rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) { - int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel); - if (retval < 0) { - return retval; - } - // If this was the first call, register callback for image additions (which is also invoked for - // existing images, otherwise, just run on existing images - //首先是遍历 dyld 里的所有的 image,取出 image header 和 slide。注意第一次调用时主要注册 callback - if (!_rebindings_head->next) { - _dyld_register_func_for_add_image(_rebind_symbols_for_image); - } else { - uint32_t c = _dyld_image_count(); - for (uint32_t i = 0; i < c; i++) { - _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); - } - } - return retval; -} - +#include "fishhook.h" #pragma mark - Record @@ -238,12 +40,13 @@ static qiCallRecord *_qiCallRecords; static int _qiRecordNum; static int _qiRecordAlloc; -typedef struct { +typedef struct thread_call_record { id self; //通过 object_getClass 能够得到 Class 再通过 NSStringFromClass 能够得到类名 Class cls; SEL cmd; //通过 NSStringFromSelector 方法能够得到方法名 uint64_t time; //us uintptr_t lr; // link register + struct thread_call_record *caller_record; //调用该方法的信息 } thread_call_record; typedef struct { @@ -286,6 +89,9 @@ static inline void push_call_record(id _self, Class _cls, SEL _cmd, uintptr_t lr newRecord->cls = _cls; newRecord->cmd = _cmd; newRecord->lr = lr; + if (nextIndex > 0) { + newRecord->caller_record = cs->stack; + } if (cs->is_main_thread && _call_record_enabled) { struct timeval now; gettimeofday(&now, NULL); @@ -323,6 +129,14 @@ static inline uintptr_t pop_call_record() { log->depth = curIndex; log->sel = pRecord->cmd; log->time = cost; + log->lr = pRecord->lr; + if (pRecord->caller_record != NULL) { + qiCallRecord *caller_record = (qiCallRecord *)malloc(sizeof(qiCallRecord)); + caller_record->cls = pRecord->caller_record->cls; + caller_record->sel = pRecord->caller_record->cmd; + caller_record->lr = pRecord->caller_record->lr; + log->caller_record = caller_record; + } } } return pRecord->lr; @@ -413,7 +227,7 @@ void qiCallTraceStart() { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ pthread_key_create(&_thread_key, &release_thread_call_stack); - fish_rebind_symbols((struct rebinding[6]){ + rebind_symbols((struct rebinding[6]){ {"objc_msgSend", (void *)hook_Objc_msgSend, (void **)&orig_objc_msgSend}, }, 1); }); diff --git a/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTraceCore.h b/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTraceCore.h index f1a5bd8..a2be4cb 100644 --- a/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTraceCore.h +++ b/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTraceCore.h @@ -12,11 +12,17 @@ #include #include -typedef struct { +typedef struct qiCallRecord { __unsafe_unretained Class cls; SEL sel; uint64_t time; // us (1/1000 ms) int depth; + // 在计算机体系结构中,lr 通常代表 "Link Register"。Link Register 是一个特殊的寄存器,用于存储函数或子程序调用的返回地址。当一个函数或子程序被调用时,返回地址(即调用指令后的下一条指令的地址)被保存到 Link Register 中,以便在函数执行完毕后可以返回到调用点继续执行。 + // 在 ARM 架构中,lr 是一个常见的寄存器名,表示 Link Register。在其他架构中,可能有不同的寄存器或方法来保存返回地址。 + // 在多线程环境中,thread_call_record 可能是一个结构体或记录,用于保存线程调用的相关信息,包括线程的状态、调用堆栈、寄存器值等。在这个上下文中,lr 可能是该记录中的一个字段,用于保存线程当前函数调用的 Link Register 值。这对于调试、性能分析或线程状态恢复等操作是非常有用的。 + // Link Register (lr) 的值并不是每个方法唯一的。lr 存储的是函数或方法调用时的返回地址,这个地址指向的是调用该函数后应当执行的下一条指令。因此,lr 的值取决于函数被调用的具体位置。 + uintptr_t lr; // link register + struct qiCallRecord *caller_record; } qiCallRecord; extern void qiCallTraceStart(void); diff --git a/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTraceTimeCostModel.h b/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTraceTimeCostModel.h index 7d4afb6..7dd8009 100644 --- a/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTraceTimeCostModel.h +++ b/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/QiCallTraceTimeCostModel.h @@ -22,6 +22,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) NSUInteger frequency; //访问频次 @property (nonatomic, strong) NSArray *subCosts; +@property (nonatomic, assign) uintptr_t lr; // 自身的 Link Register +@property (nonatomic, assign) uintptr_t callerLr; // 调用者的 Link Register + - (NSString *)des; @end diff --git a/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/fishhook/fishhook.c b/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/fishhook/fishhook.c new file mode 100644 index 0000000..fb41e8e --- /dev/null +++ b/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/fishhook/fishhook.c @@ -0,0 +1,264 @@ +// Copyright (c) 2013, Facebook, Inc. +// All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name Facebook nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific +// prior written permission. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "fishhook.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __LP64__ +typedef struct mach_header_64 mach_header_t; +typedef struct segment_command_64 segment_command_t; +typedef struct section_64 section_t; +typedef struct nlist_64 nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64 +#else +typedef struct mach_header mach_header_t; +typedef struct segment_command segment_command_t; +typedef struct section section_t; +typedef struct nlist nlist_t; +#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT +#endif + +#ifndef SEG_DATA_CONST +#define SEG_DATA_CONST "__DATA_CONST" +#endif + +struct rebindings_entry { + struct rebinding *rebindings; + size_t rebindings_nel; + struct rebindings_entry *next; +}; + +static struct rebindings_entry *_rebindings_head; + +static int prepend_rebindings(struct rebindings_entry **rebindings_head, + struct rebinding rebindings[], + size_t nel) { + struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry)); + if (!new_entry) { + return -1; + } + new_entry->rebindings = (struct rebinding *) malloc(sizeof(struct rebinding) * nel); + if (!new_entry->rebindings) { + free(new_entry); + return -1; + } + memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel); + new_entry->rebindings_nel = nel; + new_entry->next = *rebindings_head; + *rebindings_head = new_entry; + return 0; +} + +#if 0 +static int get_protection(void *addr, vm_prot_t *prot, vm_prot_t *max_prot) { + mach_port_t task = mach_task_self(); + vm_size_t size = 0; + vm_address_t address = (vm_address_t)addr; + memory_object_name_t object; +#ifdef __LP64__ + mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; + vm_region_basic_info_data_64_t info; + kern_return_t info_ret = vm_region_64( + task, &address, &size, VM_REGION_BASIC_INFO_64, (vm_region_info_64_t)&info, &count, &object); +#else + mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT; + vm_region_basic_info_data_t info; + kern_return_t info_ret = vm_region(task, &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &count, &object); +#endif + if (info_ret == KERN_SUCCESS) { + if (prot != NULL) + *prot = info.protection; + + if (max_prot != NULL) + *max_prot = info.max_protection; + + return 0; + } + + return -1; +} +#endif + +static void perform_rebinding_with_section(struct rebindings_entry *rebindings, + section_t *section, + intptr_t slide, + nlist_t *symtab, + char *strtab, + uint32_t *indirect_symtab) { + uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1; + void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr); + + for (uint i = 0; i < section->size / sizeof(void *); i++) { + uint32_t symtab_index = indirect_symbol_indices[i]; + if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL || + symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) { + continue; + } + uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx; + char *symbol_name = strtab + strtab_offset; + bool symbol_name_longer_than_1 = symbol_name[0] && symbol_name[1]; + struct rebindings_entry *cur = rebindings; + while (cur) { + for (uint j = 0; j < cur->rebindings_nel; j++) { + if (symbol_name_longer_than_1 && strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) { + kern_return_t err; + + if (cur->rebindings[j].replaced != NULL && indirect_symbol_bindings[i] != cur->rebindings[j].replacement) + *(cur->rebindings[j].replaced) = indirect_symbol_bindings[i]; + + /** + * 1. Moved the vm protection modifying codes to here to reduce the + * changing scope. + * 2. Adding VM_PROT_WRITE mode unconditionally because vm_region + * API on some iOS/Mac reports mismatch vm protection attributes. + * -- Lianfu Hao Jun 16th, 2021 + **/ + err = vm_protect (mach_task_self (), (uintptr_t)indirect_symbol_bindings, section->size, 0, VM_PROT_READ | VM_PROT_WRITE | VM_PROT_COPY); + if (err == KERN_SUCCESS) { + /** + * Once we failed to change the vm protection, we + * MUST NOT continue the following write actions! + * iOS 15 has corrected the const segments prot. + * -- Lionfore Hao Jun 11th, 2021 + **/ + indirect_symbol_bindings[i] = cur->rebindings[j].replacement; + } + goto symbol_loop; + } + } + cur = cur->next; + } + symbol_loop:; + } +} + +static void rebind_symbols_for_image(struct rebindings_entry *rebindings, + const struct mach_header *header, + intptr_t slide) { + Dl_info info; + if (dladdr(header, &info) == 0) { + return; + } + + segment_command_t *cur_seg_cmd; + segment_command_t *linkedit_segment = NULL; + struct symtab_command* symtab_cmd = NULL; + struct dysymtab_command* dysymtab_cmd = NULL; + + uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t); + for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { + cur_seg_cmd = (segment_command_t *)cur; + if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { + if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) { + linkedit_segment = cur_seg_cmd; + } + } else if (cur_seg_cmd->cmd == LC_SYMTAB) { + symtab_cmd = (struct symtab_command*)cur_seg_cmd; + } else if (cur_seg_cmd->cmd == LC_DYSYMTAB) { + dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd; + } + } + + if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment || + !dysymtab_cmd->nindirectsyms) { + return; + } + + // Find base symbol/string table addresses + uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff; + nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff); + char *strtab = (char *)(linkedit_base + symtab_cmd->stroff); + + // Get indirect symbol table (array of uint32_t indices into symbol table) + uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff); + + cur = (uintptr_t)header + sizeof(mach_header_t); + for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) { + cur_seg_cmd = (segment_command_t *)cur; + if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) { + if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 && + strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) { + continue; + } + for (uint j = 0; j < cur_seg_cmd->nsects; j++) { + section_t *sect = + (section_t *)(cur + sizeof(segment_command_t)) + j; + if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) { + perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); + } + if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) { + perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab); + } + } + } + } +} + +static void _rebind_symbols_for_image(const struct mach_header *header, + intptr_t slide) { + rebind_symbols_for_image(_rebindings_head, header, slide); +} + +int rebind_symbols_image(void *header, + intptr_t slide, + struct rebinding rebindings[], + size_t rebindings_nel) { + struct rebindings_entry *rebindings_head = NULL; + int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel); + rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide); + if (rebindings_head) { + free(rebindings_head->rebindings); + } + free(rebindings_head); + return retval; +} + +int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) { + int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel); + if (retval < 0) { + return retval; + } + // If this was the first call, register callback for image additions (which is also invoked for + // existing images, otherwise, just run on existing images + if (!_rebindings_head->next) { + _dyld_register_func_for_add_image(_rebind_symbols_for_image); + } else { + uint32_t c = _dyld_image_count(); + for (uint32_t i = 0; i < c; i++) { + _rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i)); + } + } + return retval; +} diff --git a/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/fishhook/fishhook.h b/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/fishhook/fishhook.h new file mode 100644 index 0000000..0d8e36a --- /dev/null +++ b/Qi_ObjcMsgHook/Qi_ObjcMsgHook/QiLagMonitor/fishhook/fishhook.h @@ -0,0 +1,76 @@ +// Copyright (c) 2013, Facebook, Inc. +// All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name Facebook nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific +// prior written permission. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef fishhook_h +#define fishhook_h + +#include +#include + +#if !defined(FISHHOOK_EXPORT) +#define FISHHOOK_VISIBILITY __attribute__((visibility("hidden"))) +#else +#define FISHHOOK_VISIBILITY __attribute__((visibility("default"))) +#endif + +#ifdef __cplusplus +extern "C" { +#endif //__cplusplus + +/* + * A structure representing a particular intended rebinding from a symbol + * name to its replacement + */ +struct rebinding { + const char *name; + void *replacement; + void **replaced; +}; + +/* + * For each rebinding in rebindings, rebinds references to external, indirect + * symbols with the specified name to instead point at replacement for each + * image in the calling process as well as for all future images that are loaded + * by the process. If rebind_functions is called more than once, the symbols to + * rebind are added to the existing list of rebindings, and if a given symbol + * is rebound more than once, the later rebinding will take precedence. + */ +FISHHOOK_VISIBILITY +int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel); + +/* + * Rebinds as above, but only in the specified image. The header should point + * to the mach-o header, the slide should be the slide offset. Others as above. + */ +FISHHOOK_VISIBILITY +int rebind_symbols_image(void *header, + intptr_t slide, + struct rebinding rebindings[], + size_t rebindings_nel); + +#ifdef __cplusplus +} +#endif //__cplusplus + +#endif //fishhook_h + diff --git a/Qi_ObjcMsgHook/Qi_ObjcMsgHook/SceneDelegate.m b/Qi_ObjcMsgHook/Qi_ObjcMsgHook/SceneDelegate.m index 5e3d8cf..b2ace1d 100644 --- a/Qi_ObjcMsgHook/Qi_ObjcMsgHook/SceneDelegate.m +++ b/Qi_ObjcMsgHook/Qi_ObjcMsgHook/SceneDelegate.m @@ -25,12 +25,63 @@ - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session op self.window.rootViewController = homeNav; [self.window makeKeyAndVisible]; + [self test1_1]; + [self test2_1]; + [self test3_1]; + + [self test1_1]; + [self test2_1]; + [self test3_1]; + [QiCallTrace stop]; [QiCallTrace save]; } } +- (void)test1_1 { + usleep(11 * 1000); + [self test1_2]; + [self test1_3]; +} + +- (void)test1_2 { + usleep(12 * 1000); +} + +- (void)test1_3 { + usleep(13 * 1000); +} + +- (void)test2_1 { + usleep(21 * 1000); + [self test2_2]; + [self test2_3]; +} + +- (void)test2_2 { + usleep(22 * 1000); +} + +- (void)test2_3 { + usleep(23 * 1000); +} + + +- (void)test3_1 { + usleep(31 * 1000); + [self test3_2]; + [self test3_3]; +} + +- (void)test3_2 { + usleep(32 * 1000); +} + +- (void)test3_3 { + usleep(33 * 1000); +} + - (void)sceneDidDisconnect:(UIScene *)scene { // Called as the scene is being released by the system. // This occurs shortly after the scene enters the background, or when its session is discarded.