├── LICENSE ├── OCMethodTrace ├── OCMethodTrace.h ├── OCMethodTrace.m ├── OCSelectorTrampolines.h ├── OCSelectorTrampolines.mm ├── a1a2-selectortramps-arm.s ├── a1a2-selectortramps-arm64.s ├── a1a2-selectortramps-i386.s ├── a1a2-selectortramps-x86_64.s ├── a2a3-selectortramps-arm.s ├── a2a3-selectortramps-i386.s ├── a2a3-selectortramps-x86_64.s └── selectortramps.h ├── OCMethodTraceDemo ├── OCMethodTraceDemo.xcodeproj │ └── project.pbxproj ├── OCMethodTraceDemo │ ├── Animal.h │ ├── Animal.m │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── MDConfig.plist │ ├── MDConfigManager.h │ ├── MDConfigManager.m │ ├── MDMethodTrace.h │ ├── MDMethodTrace.m │ ├── TestUtils.h │ ├── Tiger.h │ ├── Tiger.m │ ├── ViewController.h │ ├── ViewController.m │ └── main.m └── OCMethodTraceDemoTests │ ├── Info.plist │ └── OCMethodTraceDemoTests.m └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /OCMethodTrace/OCMethodTrace.h: -------------------------------------------------------------------------------- 1 | /* 2 | * OCMethodTrace.h 3 | * OCMethodTrace 4 | * 5 | * https://github.com/omxcodec/OCMethodTrace.git 6 | * 7 | * Copyright (C) 2018 Michael Chen 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #import 23 | 24 | // 方法跟踪条件block 25 | // @param sel 方法名 26 | typedef BOOL (^OMTConditionBlock)(SEL sel); 27 | 28 | // 方法调用前会调用该block 29 | // @param target 跟踪目标对象 30 | // @param cls 调用方法所在的类(可以是target所在的类,也可以是target的父类) 31 | // @param sel 方法名 32 | // @param args 参数列表 33 | // @param deep 调用层次 34 | typedef void (^OMTBeforeBlock)(id target, Class cls, SEL sel, NSArray *args, int deep); 35 | 36 | // 方法调用后会调用该block 37 | // @param target 跟踪目标对象 38 | // @param cls 调用方法所在的类(可以是target所在的类,也可以是target的父类) 39 | // @param sel 方法名 40 | // @param ret 返回值 41 | // @param deep 调用层次 42 | // @param interval 执行方法的ms耗时 43 | typedef void (^OMTAfterBlock)(id target, Class cls, SEL sel, id ret, int deep, NSTimeInterval interval); 44 | 45 | // target位置 46 | typedef NS_ENUM(NSUInteger, OMTTargetPosition) { 47 | OMTTargetPositionBeforeSelf = 0, // before-调用者self 48 | OMTTargetPositionBeforeArgument, // before-参数 49 | OMTTargetPositionAfterSelf, // after-调用者self 50 | OMTTargetPositionAfterReturnValue, // after-返回值 51 | OMTTargetPositionMax, 52 | }; 53 | 54 | // 日志级别 55 | typedef NS_ENUM(NSUInteger, OMTLogLevel) { 56 | OMTLogLevelError = 0, 57 | OMTLogLevelDebug, 58 | OMTLogLevelMax, 59 | }; 60 | 61 | @protocol OCMethodTraceDelegate 62 | 63 | @optional 64 | // 获取target的description回调 65 | - (NSString *)descriptionWithTarget:(id)target class:(Class)cls selector:(SEL)sel targetPosition:(OMTTargetPosition)targetPosition; 66 | // 日志回调 67 | - (void)log:(OMTLogLevel)level format:(NSString *)format, ... NS_FORMAT_FUNCTION(2,3); 68 | 69 | @end 70 | 71 | @interface OCMethodTrace : NSObject 72 | 73 | @property (nonatomic, assign) BOOL disableTrace; // 屏蔽before和after调用,hook完成后默认打开 74 | @property (nonatomic, weak) id delegate; // 回调 75 | @property (nonatomic, assign) OMTLogLevel logLevel; // 日志级别,默认OMTLogLevelDebug 76 | 77 | + (OCMethodTrace *)sharedInstance; 78 | 79 | // 跟踪打印目标(实例或类)方法调用 80 | // @param cls 跟踪打印目标类 81 | // @param condition 判断是否跟踪方法的block 82 | // @param before 被跟踪的方法调用前会调用该block 83 | // @param after 被跟踪的方法调用后会调用该block 84 | - (void)traceMethodWithClass:(Class)cls 85 | condition:(OMTConditionBlock)condition 86 | before:(OMTBeforeBlock)before 87 | after:(OMTAfterBlock)after; 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /OCMethodTrace/OCSelectorTrampolines.h: -------------------------------------------------------------------------------- 1 | /* 2 | * OCSelectorTrampolines.mm 3 | * OCMethodTrace 4 | * 5 | * https://github.com/omxcodec/OCMethodTrace.git 6 | * 7 | * Copyright (C) 2018 Michael Chen 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #import 23 | 24 | FOUNDATION_EXTERN IMP imp_implementationWithSelector(SEL aSel, const char *signature); 25 | FOUNDATION_EXTERN SEL imp_getSelector(IMP anImp); 26 | FOUNDATION_EXTERN BOOL imp_removeSelector(IMP anImp); 27 | -------------------------------------------------------------------------------- /OCMethodTrace/OCSelectorTrampolines.mm: -------------------------------------------------------------------------------- 1 | /* 2 | * OCSelectorTrampolines.mm 3 | * OCMethodTrace 4 | * 5 | * https://github.com/omxcodec/OCMethodTrace.git 6 | * 7 | * Copyright (C) 2018 Michael Chen 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #import "OCSelectorTrampolines.h" 23 | #import 24 | #import 25 | #import 26 | #import 27 | 28 | // Define SUPPORT_STRET on architectures that need separate struct-return ABI. 29 | #if defined(__arm64__) 30 | # define SUPPORT_STRET 0 31 | #else 32 | # define SUPPORT_STRET 1 33 | #endif 34 | 35 | #define _ost_fatal(fmt, ...) [NSException raise:@"OCSelectorTrampolines" format:fmt, ##__VA_ARGS__] 36 | 37 | // symbols defined in assembly files 38 | // Don't use the symbols directly; they're thumb-biased on some ARM archs. 39 | #define TRAMP(tramp) \ 40 | static inline __unused uintptr_t tramp(void) { \ 41 | extern void *_##tramp; \ 42 | return ((uintptr_t)&_##tramp) & ~1UL; \ 43 | } 44 | // Scalar return 45 | TRAMP(a1a2_selectorTrampHead); // trampoline header code 46 | TRAMP(a1a2_firstSelectorTramp); // first trampoline 47 | TRAMP(a1a2_selectorTrampEnd); // after the last trampoline 48 | 49 | #if SUPPORT_STRET 50 | // Struct return 51 | TRAMP(a2a3_selectorTrampHead); 52 | TRAMP(a2a3_firstSelectorTramp); 53 | TRAMP(a2a3_selectorTrampEnd); 54 | #endif 55 | 56 | // argument mode identifier 57 | typedef enum { 58 | ReturnValueInRegisterArgumentMode, 59 | #if SUPPORT_STRET 60 | ReturnValueOnStackArgumentMode, 61 | #endif 62 | 63 | ArgumentModeCount 64 | } ArgumentMode; 65 | 66 | 67 | // We must take care with our data layout on architectures that support 68 | // multiple page sizes. 69 | // 70 | // The trampoline template in __TEXT is sized and aligned with PAGE_MAX_SIZE. 71 | // On some platforms this requires additional linker flags. 72 | // 73 | // When we allocate a page pair, we use PAGE_MAX_SIZE size. 74 | // This allows trampoline code to find its data by subtracting PAGE_MAX_SIZE. 75 | // 76 | // When we allocate a page pair, we use the process's page alignment. 77 | // This simplifies allocation because we don't need to force greater than 78 | // default alignment when running with small pages, but it also means 79 | // the trampoline code MUST NOT look for its data by masking with PAGE_MAX_MASK. 80 | 81 | struct TrampolineSelectorPagePair 82 | { 83 | IMP msgSend; // msg send hander 84 | 85 | TrampolineSelectorPagePair *nextPagePair; // linked list of all pages 86 | TrampolineSelectorPagePair *nextAvailablePage; // linked list of pages with available slots 87 | 88 | uintptr_t nextAvailable; // index of next available slot, endIndex() if no more available 89 | 90 | // Payload data: selector and free list. 91 | // Bytes parallel with trampoline header code are the fields above or unused 92 | // uint8_t selectors[ PAGE_MAX_SIZE - sizeof(TrampolineSelectorPagePair) ] 93 | 94 | // Code: trampoline header followed by trampolines. 95 | // uint8_t trampolines[PAGE_MAX_SIZE]; 96 | 97 | // Per-trampoline selector data format: 98 | // initial value is 0 while page data is filled sequentially 99 | // when filled, value is reference to selector 100 | // when empty, value is index of next available slot OR 0 if never used yet 101 | 102 | union Payload { 103 | SEL selector; 104 | uintptr_t nextAvailable; // free list 105 | }; 106 | 107 | static uintptr_t headerSize() { 108 | return (uintptr_t) (a1a2_firstSelectorTramp() - a1a2_selectorTrampHead()); 109 | } 110 | 111 | static uintptr_t slotSize() { 112 | return 8; 113 | } 114 | 115 | static uintptr_t startIndex() { 116 | // headerSize is assumed to be slot-aligned 117 | return headerSize() / slotSize(); 118 | } 119 | 120 | static uintptr_t endIndex() { 121 | return (uintptr_t)PAGE_MAX_SIZE / slotSize(); 122 | } 123 | 124 | static bool validIndex(uintptr_t index) { 125 | return (index >= startIndex() && index < endIndex()); 126 | } 127 | 128 | Payload *payload(uintptr_t index) { 129 | assert(validIndex(index)); 130 | return (Payload *)((char *)this + index*slotSize()); 131 | } 132 | 133 | IMP trampoline(uintptr_t index) { 134 | assert(validIndex(index)); 135 | char *imp = (char *)this + index*slotSize() + PAGE_MAX_SIZE; 136 | #if __arm__ 137 | imp++; // trampoline is Thumb instructions 138 | #endif 139 | return (IMP)imp; 140 | } 141 | 142 | uintptr_t indexForTrampoline(IMP tramp) { 143 | uintptr_t tramp0 = (uintptr_t)this + PAGE_MAX_SIZE; 144 | uintptr_t start = tramp0 + headerSize(); 145 | uintptr_t end = tramp0 + PAGE_MAX_SIZE; 146 | uintptr_t address = (uintptr_t)tramp; 147 | if (address >= start && address < end) { 148 | return (uintptr_t)(address - tramp0) / slotSize(); 149 | } 150 | return 0; 151 | } 152 | 153 | static void check() { 154 | assert(TrampolineSelectorPagePair::slotSize() == 8); 155 | assert(TrampolineSelectorPagePair::headerSize() >= sizeof(TrampolineSelectorPagePair)); 156 | assert(TrampolineSelectorPagePair::headerSize() % TrampolineSelectorPagePair::slotSize() == 0); 157 | 158 | // _objc_inform("%p %p %p", a1a2_selectorTrampHead(), a1a2_firstSelectorTramp(), 159 | // a1a2_selectorTrampEnd()); 160 | assert(a1a2_selectorTrampHead() % PAGE_SIZE == 0); // not PAGE_MAX_SIZE 161 | assert(a1a2_selectorTrampHead() + PAGE_MAX_SIZE == a1a2_selectorTrampEnd()); 162 | #if SUPPORT_STRET 163 | // _objc_inform("%p %p %p", a2a3_selectorTrampHead(), a2a3_firstSelectorTramp(), 164 | // a2a3_selectorTrampEnd()); 165 | assert(a2a3_selectorTrampHead() % PAGE_SIZE == 0); // not PAGE_MAX_SIZE 166 | assert(a2a3_selectorTrampHead() + PAGE_MAX_SIZE == a2a3_selectorTrampEnd()); 167 | #endif 168 | 169 | #if __arm__ 170 | // make sure trampolines are Thumb 171 | extern void *_a1a2_firstSelectorTramp; 172 | extern void *_a2a3_firstSelectorTramp; 173 | assert(((uintptr_t)&_a1a2_firstSelectorTramp) % 2 == 1); 174 | assert(((uintptr_t)&_a2a3_firstSelectorTramp) % 2 == 1); 175 | #endif 176 | } 177 | 178 | }; 179 | 180 | // two sets of trampoline pages; one for stack returns and one for register returns 181 | static TrampolineSelectorPagePair *headPagePairs[ArgumentModeCount]; 182 | 183 | #pragma mark Utility Functions 184 | 185 | static pthread_rwlock_t trampolinesLock = PTHREAD_RWLOCK_INITIALIZER; 186 | 187 | static inline void _lock() { 188 | int err = pthread_rwlock_wrlock(&trampolinesLock); 189 | if (err) _ost_fatal(@"pthread_rwlock_wrlock failed (%d)", err); 190 | } 191 | 192 | static inline void _unlock() { 193 | int err = pthread_rwlock_unlock(&trampolinesLock); 194 | if (err) _ost_fatal(@"pthread_rwlock_unlock failed (%d)", err); 195 | } 196 | 197 | static inline void _assert_locked() { 198 | } 199 | 200 | #pragma mark Trampoline Management Functions 201 | static TrampolineSelectorPagePair *_allocateTrampolinesAndData(ArgumentMode aMode) 202 | { 203 | _assert_locked(); 204 | 205 | vm_address_t dataAddress; 206 | 207 | TrampolineSelectorPagePair::check(); 208 | 209 | TrampolineSelectorPagePair *headPagePair = headPagePairs[aMode]; 210 | 211 | assert(headPagePair == nil || headPagePair->nextAvailablePage == nil); 212 | 213 | kern_return_t result; 214 | result = vm_allocate(mach_task_self(), &dataAddress, PAGE_MAX_SIZE * 2, 215 | VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_FOUNDATION)); 216 | if (result != KERN_SUCCESS) { 217 | _ost_fatal(@"vm_allocate trampolines failed (%d)", result); 218 | } 219 | 220 | vm_address_t codeAddress = dataAddress + PAGE_MAX_SIZE; 221 | 222 | uintptr_t codePage = NULL; 223 | IMP sendImp = NULL; 224 | switch(aMode) { 225 | case ReturnValueInRegisterArgumentMode: 226 | codePage = a1a2_selectorTrampHead(); 227 | sendImp = objc_msgSend; 228 | break; 229 | #if SUPPORT_STRET 230 | case ReturnValueOnStackArgumentMode: 231 | codePage = a2a3_selectorTrampHead(); 232 | sendImp = objc_msgSend_stret; 233 | break; 234 | #endif 235 | default: 236 | _ost_fatal(@"unknown return mode %d", (int)aMode); 237 | break; 238 | } 239 | 240 | vm_prot_t currentProtection, maxProtection; 241 | result = vm_remap(mach_task_self(), &codeAddress, PAGE_MAX_SIZE, 242 | 0, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, 243 | mach_task_self(), codePage, TRUE, 244 | ¤tProtection, &maxProtection, VM_INHERIT_SHARE); 245 | if (result != KERN_SUCCESS) { 246 | // vm_deallocate(mach_task_self(), dataAddress, PAGE_MAX_SIZE * 2); 247 | _ost_fatal(@"vm_remap trampolines failed (%d)", result); 248 | } 249 | 250 | TrampolineSelectorPagePair *pagePair = (TrampolineSelectorPagePair *) dataAddress; 251 | pagePair->nextAvailable = pagePair->startIndex(); 252 | pagePair->nextPagePair = nil; 253 | pagePair->nextAvailablePage = nil; 254 | pagePair->msgSend = sendImp; 255 | 256 | if (headPagePair) { 257 | TrampolineSelectorPagePair *lastPagePair = headPagePair; 258 | while(lastPagePair->nextPagePair) { 259 | lastPagePair = lastPagePair->nextPagePair; 260 | } 261 | lastPagePair->nextPagePair = pagePair; 262 | headPagePairs[aMode]->nextAvailablePage = pagePair; 263 | } else { 264 | headPagePairs[aMode] = pagePair; 265 | } 266 | 267 | return pagePair; 268 | } 269 | 270 | static TrampolineSelectorPagePair * 271 | _getOrAllocatePagePairWithNextAvailable(ArgumentMode aMode) 272 | { 273 | _assert_locked(); 274 | 275 | TrampolineSelectorPagePair *headPagePair = headPagePairs[aMode]; 276 | 277 | if (!headPagePair) 278 | return _allocateTrampolinesAndData(aMode); 279 | 280 | // make sure head page is filled first 281 | if (headPagePair->nextAvailable != headPagePair->endIndex()) 282 | return headPagePair; 283 | 284 | if (headPagePair->nextAvailablePage) // check if there is a page w/a hole 285 | return headPagePair->nextAvailablePage; 286 | 287 | return _allocateTrampolinesAndData(aMode); // tack on a new one 288 | } 289 | 290 | static TrampolineSelectorPagePair * 291 | _pageAndIndexContainingIMP(IMP anImp, uintptr_t *outIndex, 292 | TrampolineSelectorPagePair **outHeadPagePair) 293 | { 294 | _assert_locked(); 295 | 296 | for (int arg = 0; arg < ArgumentModeCount; arg++) { 297 | for (TrampolineSelectorPagePair *pagePair = headPagePairs[arg]; 298 | pagePair; 299 | pagePair = pagePair->nextPagePair) 300 | { 301 | uintptr_t index = pagePair->indexForTrampoline(anImp); 302 | if (index) { 303 | if (outIndex) *outIndex = index; 304 | if (outHeadPagePair) *outHeadPagePair = headPagePairs[arg]; 305 | return pagePair; 306 | } 307 | } 308 | } 309 | 310 | return nil; 311 | } 312 | 313 | static ArgumentMode 314 | _argumentModeForSignature(const char *signature) 315 | { 316 | ArgumentMode aMode = ReturnValueInRegisterArgumentMode; 317 | 318 | #if SUPPORT_STRET 319 | if (signature && signature[0] == '{') { 320 | @try { 321 | // In some cases that returns struct, we should use the '_stret' API: 322 | // http://sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html 323 | // NSMethodSignature knows the detail but has no API to return, we can only get the info from debugDescription. 324 | NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:signature]; 325 | if ([methodSignature.debugDescription rangeOfString:@"is special struct return? YES"].location != NSNotFound) { 326 | aMode = ReturnValueOnStackArgumentMode; 327 | } 328 | } @catch (__unused NSException *e) {} 329 | } 330 | #endif 331 | 332 | return aMode; 333 | } 334 | 335 | #pragma mark Public API 336 | IMP imp_implementationWithSelector(SEL aSel, const char *signature) 337 | { 338 | IMP returnIMP; 339 | 340 | _lock(); 341 | 342 | ArgumentMode aMode = _argumentModeForSignature(signature); 343 | 344 | TrampolineSelectorPagePair *pagePair = 345 | _getOrAllocatePagePairWithNextAvailable(aMode); 346 | if (!headPagePairs[aMode]) 347 | headPagePairs[aMode] = pagePair; 348 | 349 | uintptr_t index = pagePair->nextAvailable; 350 | assert(index >= pagePair->startIndex() && index < pagePair->endIndex()); 351 | TrampolineSelectorPagePair::Payload *payload = pagePair->payload(index); 352 | 353 | uintptr_t nextAvailableIndex = payload->nextAvailable; 354 | if (nextAvailableIndex == 0) { 355 | // First time through (unused slots are zero). Fill sequentially. 356 | // If the page is now full this will now be endIndex(), handled below. 357 | nextAvailableIndex = index + 1; 358 | } 359 | pagePair->nextAvailable = nextAvailableIndex; 360 | if (nextAvailableIndex == pagePair->endIndex()) { 361 | // PagePair is now full (free list or wilderness exhausted) 362 | // Remove from available page linked list 363 | TrampolineSelectorPagePair *iterator = headPagePairs[aMode]; 364 | while(iterator && (iterator->nextAvailablePage != pagePair)) { 365 | iterator = iterator->nextAvailablePage; 366 | } 367 | if (iterator) { 368 | iterator->nextAvailablePage = pagePair->nextAvailablePage; 369 | pagePair->nextAvailablePage = nil; 370 | } 371 | } 372 | 373 | payload->selector = aSel; 374 | returnIMP = pagePair->trampoline(index); 375 | 376 | _unlock(); 377 | 378 | return returnIMP; 379 | } 380 | 381 | SEL imp_getSelector(IMP anImp) { 382 | uintptr_t index; 383 | TrampolineSelectorPagePair *pagePair; 384 | 385 | if (!anImp) return nil; 386 | 387 | _lock(); 388 | 389 | pagePair = _pageAndIndexContainingIMP(anImp, &index, nil); 390 | 391 | if (!pagePair) { 392 | _unlock(); 393 | return nil; 394 | } 395 | 396 | TrampolineSelectorPagePair::Payload *payload = pagePair->payload(index); 397 | 398 | if (payload->nextAvailable <= TrampolineSelectorPagePair::endIndex()) { 399 | // unallocated 400 | _unlock(); 401 | return nil; 402 | } 403 | 404 | _unlock(); 405 | 406 | return payload->selector; 407 | } 408 | 409 | BOOL imp_removeSelector(IMP anImp) { 410 | TrampolineSelectorPagePair *pagePair; 411 | TrampolineSelectorPagePair *headPagePair; 412 | uintptr_t index; 413 | 414 | if (!anImp) return NO; 415 | 416 | _lock(); 417 | pagePair = _pageAndIndexContainingIMP(anImp, &index, &headPagePair); 418 | 419 | if (!pagePair) { 420 | _unlock(); 421 | return NO; 422 | } 423 | 424 | TrampolineSelectorPagePair::Payload *payload = pagePair->payload(index); 425 | 426 | payload->nextAvailable = pagePair->nextAvailable; 427 | pagePair->nextAvailable = index; 428 | 429 | // make sure this page is on available linked list 430 | TrampolineSelectorPagePair *pagePairIterator = headPagePair; 431 | 432 | // see if page is the next available page for any existing pages 433 | while (pagePairIterator->nextAvailablePage && 434 | pagePairIterator->nextAvailablePage != pagePair) 435 | { 436 | pagePairIterator = pagePairIterator->nextAvailablePage; 437 | } 438 | 439 | if (! pagePairIterator->nextAvailablePage) { 440 | // if iteration stopped because nextAvail was nil 441 | // add to end of list. 442 | pagePairIterator->nextAvailablePage = pagePair; 443 | pagePair->nextAvailablePage = nil; 444 | } 445 | 446 | _unlock(); 447 | 448 | return YES; 449 | } 450 | -------------------------------------------------------------------------------- /OCMethodTrace/a1a2-selectortramps-arm.s: -------------------------------------------------------------------------------- 1 | /* 2 | * a1a2-selectortramps-arm.s 3 | * OCMethodTrace 4 | * 5 | * https://github.com/omxcodec/OCMethodTrace.git 6 | * 7 | * Copyright (C) 2018 Michael Chen 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #if __arm__ 23 | 24 | #include 25 | #include 26 | 27 | .syntax unified 28 | 29 | .text 30 | 31 | .private_extern __a1a2_selectorTrampHead 32 | .private_extern __a1a2_firstSelectorTramp 33 | .private_extern __a1a2_selectorTrampEnd 34 | 35 | // Trampoline machinery assumes the trampolines are Thumb function pointers 36 | #if !__thumb2__ 37 | # error sorry 38 | #endif 39 | 40 | .thumb 41 | .thumb_func __a1a2_selectorTrampHead 42 | .thumb_func __a1a2_firstSelectorTramp 43 | .thumb_func __a1a2_selectorTrampEnd 44 | 45 | .align PAGE_MAX_SHIFT 46 | __a1a2_selectorTrampHead: 47 | // Trampoline's data is one page before the trampoline text. 48 | // Also correct PC bias of 4 bytes. 49 | // 1. selector 50 | sub r12, #PAGE_MAX_SIZE 51 | ldr r1, [r12, #-4] // selector -> _cmd 52 | // 2. msgSend 53 | mov r12, pc 54 | sub r12, #PAGE_MAX_SIZE 55 | ldr pc, [r12, #-12] // tail call msgSend 56 | // not reached 57 | nop 58 | 59 | // Align trampolines to 8 bytes 60 | .align 3 61 | 62 | .macro TrampolineEntry 63 | mov r12, pc 64 | b __a1a2_selectorTrampHead 65 | .align 3 66 | .endmacro 67 | 68 | .macro TrampolineEntryX16 69 | TrampolineEntry 70 | TrampolineEntry 71 | TrampolineEntry 72 | TrampolineEntry 73 | 74 | TrampolineEntry 75 | TrampolineEntry 76 | TrampolineEntry 77 | TrampolineEntry 78 | 79 | TrampolineEntry 80 | TrampolineEntry 81 | TrampolineEntry 82 | TrampolineEntry 83 | 84 | TrampolineEntry 85 | TrampolineEntry 86 | TrampolineEntry 87 | TrampolineEntry 88 | .endmacro 89 | 90 | .macro TrampolineEntryX256 91 | TrampolineEntryX16 92 | TrampolineEntryX16 93 | TrampolineEntryX16 94 | TrampolineEntryX16 95 | 96 | TrampolineEntryX16 97 | TrampolineEntryX16 98 | TrampolineEntryX16 99 | TrampolineEntryX16 100 | 101 | TrampolineEntryX16 102 | TrampolineEntryX16 103 | TrampolineEntryX16 104 | TrampolineEntryX16 105 | 106 | TrampolineEntryX16 107 | TrampolineEntryX16 108 | TrampolineEntryX16 109 | TrampolineEntryX16 110 | .endmacro 111 | 112 | .private_extern __a1a2_firstSelectorTramp 113 | __a1a2_firstSelectorTramp: 114 | // 2048-3 trampolines to fill 16K page 115 | TrampolineEntryX256 116 | TrampolineEntryX256 117 | TrampolineEntryX256 118 | TrampolineEntryX256 119 | 120 | TrampolineEntryX256 121 | TrampolineEntryX256 122 | TrampolineEntryX256 123 | 124 | TrampolineEntryX16 125 | TrampolineEntryX16 126 | TrampolineEntryX16 127 | TrampolineEntryX16 128 | 129 | TrampolineEntryX16 130 | TrampolineEntryX16 131 | TrampolineEntryX16 132 | TrampolineEntryX16 133 | 134 | TrampolineEntryX16 135 | TrampolineEntryX16 136 | TrampolineEntryX16 137 | TrampolineEntryX16 138 | 139 | TrampolineEntryX16 140 | TrampolineEntryX16 141 | TrampolineEntryX16 142 | 143 | TrampolineEntry 144 | TrampolineEntry 145 | TrampolineEntry 146 | TrampolineEntry 147 | 148 | TrampolineEntry 149 | TrampolineEntry 150 | TrampolineEntry 151 | TrampolineEntry 152 | 153 | TrampolineEntry 154 | TrampolineEntry 155 | TrampolineEntry 156 | TrampolineEntry 157 | 158 | TrampolineEntry 159 | // TrampolineEntry 160 | // TrampolineEntry 161 | // TrampolineEntry 162 | 163 | .private_extern __a1a2_selectorTrampEnd 164 | __a1a2_selectorTrampEnd: 165 | 166 | #endif 167 | -------------------------------------------------------------------------------- /OCMethodTrace/a1a2-selectortramps-arm64.s: -------------------------------------------------------------------------------- 1 | /* 2 | * a1a2-selectortramps-arm64.s 3 | * OCMethodTrace 4 | * 5 | * https://github.com/omxcodec/OCMethodTrace.git 6 | * 7 | * Copyright (C) 2018 Michael Chen 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #if __arm64__ 23 | 24 | #include 25 | 26 | .text 27 | 28 | .private_extern __a1a2_selectorTrampHead 29 | .private_extern __a1a2_firstSelectorTramp 30 | .private_extern __a1a2_selectorTrampEnd 31 | 32 | msgSend: 33 | .quad 0 34 | 35 | .align PAGE_MAX_SHIFT 36 | __a1a2_selectorTrampHead: 37 | L_a1a2_selectorTrampHead: 38 | // 1. selector 39 | ldr x1, [x17] // selector -> _cmd 40 | // 2. msgSend 41 | adr x17, L_a1a2_selectorTrampHead 42 | sub x17, x17, #PAGE_MAX_SIZE 43 | ldr x16, [x17] 44 | br x16 // tail call msgSend 45 | 46 | // pad up to TrampolineSelectorPagePair header size 47 | nop 48 | nop 49 | nop 50 | 51 | .macro TrampolineEntry 52 | // load address of trampoline data (one page before this instruction) 53 | adr x17, -PAGE_MAX_SIZE 54 | b L_a1a2_selectorTrampHead 55 | .endmacro 56 | 57 | .macro TrampolineEntryX16 58 | TrampolineEntry 59 | TrampolineEntry 60 | TrampolineEntry 61 | TrampolineEntry 62 | 63 | TrampolineEntry 64 | TrampolineEntry 65 | TrampolineEntry 66 | TrampolineEntry 67 | 68 | TrampolineEntry 69 | TrampolineEntry 70 | TrampolineEntry 71 | TrampolineEntry 72 | 73 | TrampolineEntry 74 | TrampolineEntry 75 | TrampolineEntry 76 | TrampolineEntry 77 | .endmacro 78 | 79 | .macro TrampolineEntryX256 80 | TrampolineEntryX16 81 | TrampolineEntryX16 82 | TrampolineEntryX16 83 | TrampolineEntryX16 84 | 85 | TrampolineEntryX16 86 | TrampolineEntryX16 87 | TrampolineEntryX16 88 | TrampolineEntryX16 89 | 90 | TrampolineEntryX16 91 | TrampolineEntryX16 92 | TrampolineEntryX16 93 | TrampolineEntryX16 94 | 95 | TrampolineEntryX16 96 | TrampolineEntryX16 97 | TrampolineEntryX16 98 | TrampolineEntryX16 99 | .endmacro 100 | 101 | .align 3 102 | .private_extern __a1a2_firstSelectorTramp 103 | __a1a2_firstSelectorTramp: 104 | // 2048-4 trampolines to fill 16K page 105 | TrampolineEntryX256 106 | TrampolineEntryX256 107 | TrampolineEntryX256 108 | TrampolineEntryX256 109 | 110 | TrampolineEntryX256 111 | TrampolineEntryX256 112 | TrampolineEntryX256 113 | 114 | TrampolineEntryX16 115 | TrampolineEntryX16 116 | TrampolineEntryX16 117 | TrampolineEntryX16 118 | 119 | TrampolineEntryX16 120 | TrampolineEntryX16 121 | TrampolineEntryX16 122 | TrampolineEntryX16 123 | 124 | TrampolineEntryX16 125 | TrampolineEntryX16 126 | TrampolineEntryX16 127 | TrampolineEntryX16 128 | 129 | TrampolineEntryX16 130 | TrampolineEntryX16 131 | TrampolineEntryX16 132 | 133 | TrampolineEntry 134 | TrampolineEntry 135 | TrampolineEntry 136 | TrampolineEntry 137 | 138 | TrampolineEntry 139 | TrampolineEntry 140 | TrampolineEntry 141 | TrampolineEntry 142 | 143 | TrampolineEntry 144 | TrampolineEntry 145 | TrampolineEntry 146 | TrampolineEntry 147 | 148 | // TrampolineEntry 149 | // TrampolineEntry 150 | // TrampolineEntry 151 | // TrampolineEntry 152 | 153 | .private_extern __a1a2_selectorTrampEnd 154 | __a1a2_selectorTrampEnd: 155 | 156 | #endif 157 | -------------------------------------------------------------------------------- /OCMethodTrace/a1a2-selectortramps-i386.s: -------------------------------------------------------------------------------- 1 | /* 2 | * a1a2-selectortramps-i386.s 3 | * OCMethodTrace 4 | * 5 | * https://github.com/omxcodec/OCMethodTrace.git 6 | * 7 | * Copyright (C) 2018 Michael Chen 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #ifdef __i386__ 23 | 24 | #include 25 | #include "selectortramps.h" 26 | 27 | .text 28 | .private_extern __a1a2_selectorTrampHead 29 | .private_extern __a1a2_firstSelectorTramp 30 | .private_extern __a1a2_nextSelectorTramp 31 | .private_extern __a1a2_selectorTrampEnd 32 | 33 | .align PAGE_SHIFT 34 | __a1a2_selectorTrampHead: 35 | // 1. selector 36 | popl %eax 37 | andl $0xFFFFFFF8, %eax 38 | subl $ PAGE_SIZE, %eax 39 | movl (%eax), %ecx // selectorPtr -> ecx 40 | movl %ecx, 8(%esp) // ecx -> _cmd 41 | // 2. msgSend 42 | INIT_PIC(__a1a2_selectorTrampHead) 43 | PRELOAD(__a1a2_selectorTrampHead, __a1a2_selectorTrampHead) 44 | subl $ PAGE_SIZE, LABEL_ADDR(__a1a2_selectorTrampHead, __a1a2_selectorTrampHead) 45 | END_PIC() 46 | jmp *0(%eax) // tail call msgSend 47 | 48 | .macro TrampolineEntry 49 | call __a1a2_selectorTrampHead 50 | nop 51 | nop 52 | nop 53 | .endmacro 54 | 55 | .align 5 56 | __a1a2_firstSelectorTramp: 57 | TrampolineEntry 58 | __a1a2_nextSelectorTramp: // used to calculate size of each trampoline 59 | TrampolineEntry 60 | TrampolineEntry 61 | TrampolineEntry 62 | TrampolineEntry 63 | TrampolineEntry 64 | TrampolineEntry 65 | TrampolineEntry 66 | TrampolineEntry 67 | TrampolineEntry 68 | TrampolineEntry 69 | TrampolineEntry 70 | TrampolineEntry 71 | TrampolineEntry 72 | TrampolineEntry 73 | TrampolineEntry 74 | TrampolineEntry 75 | TrampolineEntry 76 | TrampolineEntry 77 | TrampolineEntry 78 | TrampolineEntry 79 | TrampolineEntry 80 | TrampolineEntry 81 | TrampolineEntry 82 | TrampolineEntry 83 | TrampolineEntry 84 | TrampolineEntry 85 | TrampolineEntry 86 | TrampolineEntry 87 | TrampolineEntry 88 | TrampolineEntry 89 | TrampolineEntry 90 | TrampolineEntry 91 | TrampolineEntry 92 | TrampolineEntry 93 | TrampolineEntry 94 | TrampolineEntry 95 | TrampolineEntry 96 | TrampolineEntry 97 | TrampolineEntry 98 | TrampolineEntry 99 | TrampolineEntry 100 | TrampolineEntry 101 | TrampolineEntry 102 | TrampolineEntry 103 | TrampolineEntry 104 | TrampolineEntry 105 | TrampolineEntry 106 | TrampolineEntry 107 | TrampolineEntry 108 | TrampolineEntry 109 | TrampolineEntry 110 | TrampolineEntry 111 | TrampolineEntry 112 | TrampolineEntry 113 | TrampolineEntry 114 | TrampolineEntry 115 | TrampolineEntry 116 | TrampolineEntry 117 | TrampolineEntry 118 | TrampolineEntry 119 | TrampolineEntry 120 | TrampolineEntry 121 | TrampolineEntry 122 | TrampolineEntry 123 | TrampolineEntry 124 | TrampolineEntry 125 | TrampolineEntry 126 | TrampolineEntry 127 | TrampolineEntry 128 | TrampolineEntry 129 | TrampolineEntry 130 | TrampolineEntry 131 | TrampolineEntry 132 | TrampolineEntry 133 | TrampolineEntry 134 | TrampolineEntry 135 | TrampolineEntry 136 | TrampolineEntry 137 | TrampolineEntry 138 | TrampolineEntry 139 | TrampolineEntry 140 | TrampolineEntry 141 | TrampolineEntry 142 | TrampolineEntry 143 | TrampolineEntry 144 | TrampolineEntry 145 | TrampolineEntry 146 | TrampolineEntry 147 | TrampolineEntry 148 | TrampolineEntry 149 | TrampolineEntry 150 | TrampolineEntry 151 | TrampolineEntry 152 | TrampolineEntry 153 | TrampolineEntry 154 | TrampolineEntry 155 | TrampolineEntry 156 | TrampolineEntry 157 | TrampolineEntry 158 | TrampolineEntry 159 | TrampolineEntry 160 | TrampolineEntry 161 | TrampolineEntry 162 | TrampolineEntry 163 | TrampolineEntry 164 | TrampolineEntry 165 | TrampolineEntry 166 | TrampolineEntry 167 | TrampolineEntry 168 | TrampolineEntry 169 | TrampolineEntry 170 | TrampolineEntry 171 | TrampolineEntry 172 | TrampolineEntry 173 | TrampolineEntry 174 | TrampolineEntry 175 | TrampolineEntry 176 | TrampolineEntry 177 | TrampolineEntry 178 | TrampolineEntry 179 | TrampolineEntry 180 | TrampolineEntry 181 | TrampolineEntry 182 | TrampolineEntry 183 | TrampolineEntry 184 | TrampolineEntry 185 | TrampolineEntry 186 | TrampolineEntry 187 | TrampolineEntry 188 | TrampolineEntry 189 | TrampolineEntry 190 | TrampolineEntry 191 | TrampolineEntry 192 | TrampolineEntry 193 | TrampolineEntry 194 | TrampolineEntry 195 | TrampolineEntry 196 | TrampolineEntry 197 | TrampolineEntry 198 | TrampolineEntry 199 | TrampolineEntry 200 | TrampolineEntry 201 | TrampolineEntry 202 | TrampolineEntry 203 | TrampolineEntry 204 | TrampolineEntry 205 | TrampolineEntry 206 | TrampolineEntry 207 | TrampolineEntry 208 | TrampolineEntry 209 | TrampolineEntry 210 | TrampolineEntry 211 | TrampolineEntry 212 | TrampolineEntry 213 | TrampolineEntry 214 | TrampolineEntry 215 | TrampolineEntry 216 | TrampolineEntry 217 | TrampolineEntry 218 | TrampolineEntry 219 | TrampolineEntry 220 | TrampolineEntry 221 | TrampolineEntry 222 | TrampolineEntry 223 | TrampolineEntry 224 | TrampolineEntry 225 | TrampolineEntry 226 | TrampolineEntry 227 | TrampolineEntry 228 | TrampolineEntry 229 | TrampolineEntry 230 | TrampolineEntry 231 | TrampolineEntry 232 | TrampolineEntry 233 | TrampolineEntry 234 | TrampolineEntry 235 | TrampolineEntry 236 | TrampolineEntry 237 | TrampolineEntry 238 | TrampolineEntry 239 | TrampolineEntry 240 | TrampolineEntry 241 | TrampolineEntry 242 | TrampolineEntry 243 | TrampolineEntry 244 | TrampolineEntry 245 | TrampolineEntry 246 | TrampolineEntry 247 | TrampolineEntry 248 | TrampolineEntry 249 | TrampolineEntry 250 | TrampolineEntry 251 | TrampolineEntry 252 | TrampolineEntry 253 | TrampolineEntry 254 | TrampolineEntry 255 | TrampolineEntry 256 | TrampolineEntry 257 | TrampolineEntry 258 | TrampolineEntry 259 | TrampolineEntry 260 | TrampolineEntry 261 | TrampolineEntry 262 | TrampolineEntry 263 | TrampolineEntry 264 | TrampolineEntry 265 | TrampolineEntry 266 | TrampolineEntry 267 | TrampolineEntry 268 | TrampolineEntry 269 | TrampolineEntry 270 | TrampolineEntry 271 | TrampolineEntry 272 | TrampolineEntry 273 | TrampolineEntry 274 | TrampolineEntry 275 | TrampolineEntry 276 | TrampolineEntry 277 | TrampolineEntry 278 | TrampolineEntry 279 | TrampolineEntry 280 | TrampolineEntry 281 | TrampolineEntry 282 | TrampolineEntry 283 | TrampolineEntry 284 | TrampolineEntry 285 | TrampolineEntry 286 | TrampolineEntry 287 | TrampolineEntry 288 | TrampolineEntry 289 | TrampolineEntry 290 | TrampolineEntry 291 | TrampolineEntry 292 | TrampolineEntry 293 | TrampolineEntry 294 | TrampolineEntry 295 | TrampolineEntry 296 | TrampolineEntry 297 | TrampolineEntry 298 | TrampolineEntry 299 | TrampolineEntry 300 | TrampolineEntry 301 | TrampolineEntry 302 | TrampolineEntry 303 | TrampolineEntry 304 | TrampolineEntry 305 | TrampolineEntry 306 | TrampolineEntry 307 | TrampolineEntry 308 | TrampolineEntry 309 | TrampolineEntry 310 | TrampolineEntry 311 | TrampolineEntry 312 | TrampolineEntry 313 | TrampolineEntry 314 | TrampolineEntry 315 | TrampolineEntry 316 | TrampolineEntry 317 | TrampolineEntry 318 | TrampolineEntry 319 | TrampolineEntry 320 | TrampolineEntry 321 | TrampolineEntry 322 | TrampolineEntry 323 | TrampolineEntry 324 | TrampolineEntry 325 | TrampolineEntry 326 | TrampolineEntry 327 | TrampolineEntry 328 | TrampolineEntry 329 | TrampolineEntry 330 | TrampolineEntry 331 | TrampolineEntry 332 | TrampolineEntry 333 | TrampolineEntry 334 | TrampolineEntry 335 | TrampolineEntry 336 | TrampolineEntry 337 | TrampolineEntry 338 | TrampolineEntry 339 | TrampolineEntry 340 | TrampolineEntry 341 | TrampolineEntry 342 | TrampolineEntry 343 | TrampolineEntry 344 | TrampolineEntry 345 | TrampolineEntry 346 | TrampolineEntry 347 | TrampolineEntry 348 | TrampolineEntry 349 | TrampolineEntry 350 | TrampolineEntry 351 | TrampolineEntry 352 | TrampolineEntry 353 | TrampolineEntry 354 | TrampolineEntry 355 | TrampolineEntry 356 | TrampolineEntry 357 | TrampolineEntry 358 | TrampolineEntry 359 | TrampolineEntry 360 | TrampolineEntry 361 | TrampolineEntry 362 | TrampolineEntry 363 | TrampolineEntry 364 | TrampolineEntry 365 | TrampolineEntry 366 | TrampolineEntry 367 | TrampolineEntry 368 | TrampolineEntry 369 | TrampolineEntry 370 | TrampolineEntry 371 | TrampolineEntry 372 | TrampolineEntry 373 | TrampolineEntry 374 | TrampolineEntry 375 | TrampolineEntry 376 | TrampolineEntry 377 | TrampolineEntry 378 | TrampolineEntry 379 | TrampolineEntry 380 | TrampolineEntry 381 | TrampolineEntry 382 | TrampolineEntry 383 | TrampolineEntry 384 | TrampolineEntry 385 | TrampolineEntry 386 | TrampolineEntry 387 | TrampolineEntry 388 | TrampolineEntry 389 | TrampolineEntry 390 | TrampolineEntry 391 | TrampolineEntry 392 | TrampolineEntry 393 | TrampolineEntry 394 | TrampolineEntry 395 | TrampolineEntry 396 | TrampolineEntry 397 | TrampolineEntry 398 | TrampolineEntry 399 | TrampolineEntry 400 | TrampolineEntry 401 | TrampolineEntry 402 | TrampolineEntry 403 | TrampolineEntry 404 | TrampolineEntry 405 | TrampolineEntry 406 | TrampolineEntry 407 | TrampolineEntry 408 | TrampolineEntry 409 | TrampolineEntry 410 | TrampolineEntry 411 | TrampolineEntry 412 | TrampolineEntry 413 | TrampolineEntry 414 | TrampolineEntry 415 | TrampolineEntry 416 | TrampolineEntry 417 | TrampolineEntry 418 | TrampolineEntry 419 | TrampolineEntry 420 | TrampolineEntry 421 | TrampolineEntry 422 | TrampolineEntry 423 | TrampolineEntry 424 | TrampolineEntry 425 | TrampolineEntry 426 | TrampolineEntry 427 | TrampolineEntry 428 | TrampolineEntry 429 | TrampolineEntry 430 | TrampolineEntry 431 | TrampolineEntry 432 | TrampolineEntry 433 | TrampolineEntry 434 | TrampolineEntry 435 | TrampolineEntry 436 | TrampolineEntry 437 | TrampolineEntry 438 | TrampolineEntry 439 | TrampolineEntry 440 | TrampolineEntry 441 | TrampolineEntry 442 | TrampolineEntry 443 | TrampolineEntry 444 | TrampolineEntry 445 | TrampolineEntry 446 | TrampolineEntry 447 | TrampolineEntry 448 | TrampolineEntry 449 | TrampolineEntry 450 | TrampolineEntry 451 | TrampolineEntry 452 | TrampolineEntry 453 | TrampolineEntry 454 | TrampolineEntry 455 | TrampolineEntry 456 | TrampolineEntry 457 | TrampolineEntry 458 | TrampolineEntry 459 | TrampolineEntry 460 | TrampolineEntry 461 | TrampolineEntry 462 | TrampolineEntry 463 | TrampolineEntry 464 | TrampolineEntry 465 | TrampolineEntry 466 | TrampolineEntry 467 | TrampolineEntry 468 | TrampolineEntry 469 | TrampolineEntry 470 | TrampolineEntry 471 | TrampolineEntry 472 | TrampolineEntry 473 | TrampolineEntry 474 | TrampolineEntry 475 | TrampolineEntry 476 | TrampolineEntry 477 | TrampolineEntry 478 | TrampolineEntry 479 | TrampolineEntry 480 | TrampolineEntry 481 | TrampolineEntry 482 | TrampolineEntry 483 | TrampolineEntry 484 | TrampolineEntry 485 | TrampolineEntry 486 | TrampolineEntry 487 | TrampolineEntry 488 | TrampolineEntry 489 | TrampolineEntry 490 | TrampolineEntry 491 | TrampolineEntry 492 | TrampolineEntry 493 | TrampolineEntry 494 | TrampolineEntry 495 | TrampolineEntry 496 | TrampolineEntry 497 | TrampolineEntry 498 | TrampolineEntry 499 | TrampolineEntry 500 | TrampolineEntry 501 | TrampolineEntry 502 | TrampolineEntry 503 | TrampolineEntry 504 | TrampolineEntry 505 | TrampolineEntry 506 | TrampolineEntry 507 | TrampolineEntry 508 | TrampolineEntry 509 | TrampolineEntry 510 | TrampolineEntry 511 | TrampolineEntry 512 | TrampolineEntry 513 | TrampolineEntry 514 | TrampolineEntry 515 | TrampolineEntry 516 | TrampolineEntry 517 | TrampolineEntry 518 | TrampolineEntry 519 | TrampolineEntry 520 | TrampolineEntry 521 | TrampolineEntry 522 | TrampolineEntry 523 | TrampolineEntry 524 | TrampolineEntry 525 | TrampolineEntry 526 | TrampolineEntry 527 | TrampolineEntry 528 | TrampolineEntry 529 | TrampolineEntry 530 | TrampolineEntry 531 | TrampolineEntry 532 | TrampolineEntry 533 | TrampolineEntry 534 | TrampolineEntry 535 | TrampolineEntry 536 | TrampolineEntry 537 | TrampolineEntry 538 | TrampolineEntry 539 | TrampolineEntry 540 | TrampolineEntry 541 | TrampolineEntry 542 | TrampolineEntry 543 | TrampolineEntry 544 | TrampolineEntry 545 | TrampolineEntry 546 | TrampolineEntry 547 | TrampolineEntry 548 | TrampolineEntry 549 | TrampolineEntry 550 | TrampolineEntry 551 | TrampolineEntry 552 | TrampolineEntry 553 | TrampolineEntry 554 | TrampolineEntry 555 | TrampolineEntry 556 | TrampolineEntry 557 | TrampolineEntry 558 | TrampolineEntry 559 | TrampolineEntry 560 | TrampolineEntry 561 | TrampolineEntry 562 | // TrampolineEntry 563 | // TrampolineEntry 564 | // TrampolineEntry 565 | // TrampolineEntry 566 | 567 | __a1a2_selectorTrampEnd: 568 | 569 | #endif 570 | -------------------------------------------------------------------------------- /OCMethodTrace/a1a2-selectortramps-x86_64.s: -------------------------------------------------------------------------------- 1 | /* 2 | * a1a2-selectortramps-x86_64.s 3 | * OCMethodTrace 4 | * 5 | * https://github.com/omxcodec/OCMethodTrace.git 6 | * 7 | * Copyright (C) 2018 Michael Chen 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #ifdef __x86_64__ 23 | 24 | #include 25 | #include "selectortramps.h" 26 | 27 | .text 28 | .private_extern __a1a2_selectorTrampHead 29 | .private_extern __a1a2_firstSelectorTramp 30 | .private_extern __a1a2_nextSelectorTramp 31 | .private_extern __a1a2_selectorTrampEnd 32 | 33 | .align PAGE_SHIFT 34 | __a1a2_selectorTrampHead: 35 | // 1. selector 36 | popq %r10 37 | andq $0xFFFFFFFFFFFFFFF8, %r10 38 | subq $ PAGE_SIZE, %r10 39 | movq (%r10), %rsi // selector -> _cmd 40 | // 2. msgSend 41 | INIT_PIC(__a1a2_selectorTrampHead) 42 | PRELOAD(__a1a2_selectorTrampHead, __a1a2_selectorTrampHead) 43 | subq $ PAGE_SIZE, LABEL_ADDR(__a1a2_selectorTrampHead, __a1a2_selectorTrampHead) 44 | END_PIC() 45 | jmp *0(%r10) // tail call msgSend 46 | 47 | .macro TrampolineEntry 48 | callq __a1a2_selectorTrampHead 49 | nop 50 | nop 51 | nop 52 | .endmacro 53 | 54 | .align 5 55 | __a1a2_firstSelectorTramp: 56 | TrampolineEntry 57 | __a1a2_nextSelectorTramp: 58 | TrampolineEntry 59 | TrampolineEntry 60 | TrampolineEntry 61 | TrampolineEntry 62 | TrampolineEntry 63 | TrampolineEntry 64 | TrampolineEntry 65 | TrampolineEntry 66 | TrampolineEntry 67 | TrampolineEntry 68 | TrampolineEntry 69 | TrampolineEntry 70 | TrampolineEntry 71 | TrampolineEntry 72 | TrampolineEntry 73 | TrampolineEntry 74 | TrampolineEntry 75 | TrampolineEntry 76 | TrampolineEntry 77 | TrampolineEntry 78 | TrampolineEntry 79 | TrampolineEntry 80 | TrampolineEntry 81 | TrampolineEntry 82 | TrampolineEntry 83 | TrampolineEntry 84 | TrampolineEntry 85 | TrampolineEntry 86 | TrampolineEntry 87 | TrampolineEntry 88 | TrampolineEntry 89 | TrampolineEntry 90 | TrampolineEntry 91 | TrampolineEntry 92 | TrampolineEntry 93 | TrampolineEntry 94 | TrampolineEntry 95 | TrampolineEntry 96 | TrampolineEntry 97 | TrampolineEntry 98 | TrampolineEntry 99 | TrampolineEntry 100 | TrampolineEntry 101 | TrampolineEntry 102 | TrampolineEntry 103 | TrampolineEntry 104 | TrampolineEntry 105 | TrampolineEntry 106 | TrampolineEntry 107 | TrampolineEntry 108 | TrampolineEntry 109 | TrampolineEntry 110 | TrampolineEntry 111 | TrampolineEntry 112 | TrampolineEntry 113 | TrampolineEntry 114 | TrampolineEntry 115 | TrampolineEntry 116 | TrampolineEntry 117 | TrampolineEntry 118 | TrampolineEntry 119 | TrampolineEntry 120 | TrampolineEntry 121 | TrampolineEntry 122 | TrampolineEntry 123 | TrampolineEntry 124 | TrampolineEntry 125 | TrampolineEntry 126 | TrampolineEntry 127 | TrampolineEntry 128 | TrampolineEntry 129 | TrampolineEntry 130 | TrampolineEntry 131 | TrampolineEntry 132 | TrampolineEntry 133 | TrampolineEntry 134 | TrampolineEntry 135 | TrampolineEntry 136 | TrampolineEntry 137 | TrampolineEntry 138 | TrampolineEntry 139 | TrampolineEntry 140 | TrampolineEntry 141 | TrampolineEntry 142 | TrampolineEntry 143 | TrampolineEntry 144 | TrampolineEntry 145 | TrampolineEntry 146 | TrampolineEntry 147 | TrampolineEntry 148 | TrampolineEntry 149 | TrampolineEntry 150 | TrampolineEntry 151 | TrampolineEntry 152 | TrampolineEntry 153 | TrampolineEntry 154 | TrampolineEntry 155 | TrampolineEntry 156 | TrampolineEntry 157 | TrampolineEntry 158 | TrampolineEntry 159 | TrampolineEntry 160 | TrampolineEntry 161 | TrampolineEntry 162 | TrampolineEntry 163 | TrampolineEntry 164 | TrampolineEntry 165 | TrampolineEntry 166 | TrampolineEntry 167 | TrampolineEntry 168 | TrampolineEntry 169 | TrampolineEntry 170 | TrampolineEntry 171 | TrampolineEntry 172 | TrampolineEntry 173 | TrampolineEntry 174 | TrampolineEntry 175 | TrampolineEntry 176 | TrampolineEntry 177 | TrampolineEntry 178 | TrampolineEntry 179 | TrampolineEntry 180 | TrampolineEntry 181 | TrampolineEntry 182 | TrampolineEntry 183 | TrampolineEntry 184 | TrampolineEntry 185 | TrampolineEntry 186 | TrampolineEntry 187 | TrampolineEntry 188 | TrampolineEntry 189 | TrampolineEntry 190 | TrampolineEntry 191 | TrampolineEntry 192 | TrampolineEntry 193 | TrampolineEntry 194 | TrampolineEntry 195 | TrampolineEntry 196 | TrampolineEntry 197 | TrampolineEntry 198 | TrampolineEntry 199 | TrampolineEntry 200 | TrampolineEntry 201 | TrampolineEntry 202 | TrampolineEntry 203 | TrampolineEntry 204 | TrampolineEntry 205 | TrampolineEntry 206 | TrampolineEntry 207 | TrampolineEntry 208 | TrampolineEntry 209 | TrampolineEntry 210 | TrampolineEntry 211 | TrampolineEntry 212 | TrampolineEntry 213 | TrampolineEntry 214 | TrampolineEntry 215 | TrampolineEntry 216 | TrampolineEntry 217 | TrampolineEntry 218 | TrampolineEntry 219 | TrampolineEntry 220 | TrampolineEntry 221 | TrampolineEntry 222 | TrampolineEntry 223 | TrampolineEntry 224 | TrampolineEntry 225 | TrampolineEntry 226 | TrampolineEntry 227 | TrampolineEntry 228 | TrampolineEntry 229 | TrampolineEntry 230 | TrampolineEntry 231 | TrampolineEntry 232 | TrampolineEntry 233 | TrampolineEntry 234 | TrampolineEntry 235 | TrampolineEntry 236 | TrampolineEntry 237 | TrampolineEntry 238 | TrampolineEntry 239 | TrampolineEntry 240 | TrampolineEntry 241 | TrampolineEntry 242 | TrampolineEntry 243 | TrampolineEntry 244 | TrampolineEntry 245 | TrampolineEntry 246 | TrampolineEntry 247 | TrampolineEntry 248 | TrampolineEntry 249 | TrampolineEntry 250 | TrampolineEntry 251 | TrampolineEntry 252 | TrampolineEntry 253 | TrampolineEntry 254 | TrampolineEntry 255 | TrampolineEntry 256 | TrampolineEntry 257 | TrampolineEntry 258 | TrampolineEntry 259 | TrampolineEntry 260 | TrampolineEntry 261 | TrampolineEntry 262 | TrampolineEntry 263 | TrampolineEntry 264 | TrampolineEntry 265 | TrampolineEntry 266 | TrampolineEntry 267 | TrampolineEntry 268 | TrampolineEntry 269 | TrampolineEntry 270 | TrampolineEntry 271 | TrampolineEntry 272 | TrampolineEntry 273 | TrampolineEntry 274 | TrampolineEntry 275 | TrampolineEntry 276 | TrampolineEntry 277 | TrampolineEntry 278 | TrampolineEntry 279 | TrampolineEntry 280 | TrampolineEntry 281 | TrampolineEntry 282 | TrampolineEntry 283 | TrampolineEntry 284 | TrampolineEntry 285 | TrampolineEntry 286 | TrampolineEntry 287 | TrampolineEntry 288 | TrampolineEntry 289 | TrampolineEntry 290 | TrampolineEntry 291 | TrampolineEntry 292 | TrampolineEntry 293 | TrampolineEntry 294 | TrampolineEntry 295 | TrampolineEntry 296 | TrampolineEntry 297 | TrampolineEntry 298 | TrampolineEntry 299 | TrampolineEntry 300 | TrampolineEntry 301 | TrampolineEntry 302 | TrampolineEntry 303 | TrampolineEntry 304 | TrampolineEntry 305 | TrampolineEntry 306 | TrampolineEntry 307 | TrampolineEntry 308 | TrampolineEntry 309 | TrampolineEntry 310 | TrampolineEntry 311 | TrampolineEntry 312 | TrampolineEntry 313 | TrampolineEntry 314 | TrampolineEntry 315 | TrampolineEntry 316 | TrampolineEntry 317 | TrampolineEntry 318 | TrampolineEntry 319 | TrampolineEntry 320 | TrampolineEntry 321 | TrampolineEntry 322 | TrampolineEntry 323 | TrampolineEntry 324 | TrampolineEntry 325 | TrampolineEntry 326 | TrampolineEntry 327 | TrampolineEntry 328 | TrampolineEntry 329 | TrampolineEntry 330 | TrampolineEntry 331 | TrampolineEntry 332 | TrampolineEntry 333 | TrampolineEntry 334 | TrampolineEntry 335 | TrampolineEntry 336 | TrampolineEntry 337 | TrampolineEntry 338 | TrampolineEntry 339 | TrampolineEntry 340 | TrampolineEntry 341 | TrampolineEntry 342 | TrampolineEntry 343 | TrampolineEntry 344 | TrampolineEntry 345 | TrampolineEntry 346 | TrampolineEntry 347 | TrampolineEntry 348 | TrampolineEntry 349 | TrampolineEntry 350 | TrampolineEntry 351 | TrampolineEntry 352 | TrampolineEntry 353 | TrampolineEntry 354 | TrampolineEntry 355 | TrampolineEntry 356 | TrampolineEntry 357 | TrampolineEntry 358 | TrampolineEntry 359 | TrampolineEntry 360 | TrampolineEntry 361 | TrampolineEntry 362 | TrampolineEntry 363 | TrampolineEntry 364 | TrampolineEntry 365 | TrampolineEntry 366 | TrampolineEntry 367 | TrampolineEntry 368 | TrampolineEntry 369 | TrampolineEntry 370 | TrampolineEntry 371 | TrampolineEntry 372 | TrampolineEntry 373 | TrampolineEntry 374 | TrampolineEntry 375 | TrampolineEntry 376 | TrampolineEntry 377 | TrampolineEntry 378 | TrampolineEntry 379 | TrampolineEntry 380 | TrampolineEntry 381 | TrampolineEntry 382 | TrampolineEntry 383 | TrampolineEntry 384 | TrampolineEntry 385 | TrampolineEntry 386 | TrampolineEntry 387 | TrampolineEntry 388 | TrampolineEntry 389 | TrampolineEntry 390 | TrampolineEntry 391 | TrampolineEntry 392 | TrampolineEntry 393 | TrampolineEntry 394 | TrampolineEntry 395 | TrampolineEntry 396 | TrampolineEntry 397 | TrampolineEntry 398 | TrampolineEntry 399 | TrampolineEntry 400 | TrampolineEntry 401 | TrampolineEntry 402 | TrampolineEntry 403 | TrampolineEntry 404 | TrampolineEntry 405 | TrampolineEntry 406 | TrampolineEntry 407 | TrampolineEntry 408 | TrampolineEntry 409 | TrampolineEntry 410 | TrampolineEntry 411 | TrampolineEntry 412 | TrampolineEntry 413 | TrampolineEntry 414 | TrampolineEntry 415 | TrampolineEntry 416 | TrampolineEntry 417 | TrampolineEntry 418 | TrampolineEntry 419 | TrampolineEntry 420 | TrampolineEntry 421 | TrampolineEntry 422 | TrampolineEntry 423 | TrampolineEntry 424 | TrampolineEntry 425 | TrampolineEntry 426 | TrampolineEntry 427 | TrampolineEntry 428 | TrampolineEntry 429 | TrampolineEntry 430 | TrampolineEntry 431 | TrampolineEntry 432 | TrampolineEntry 433 | TrampolineEntry 434 | TrampolineEntry 435 | TrampolineEntry 436 | TrampolineEntry 437 | TrampolineEntry 438 | TrampolineEntry 439 | TrampolineEntry 440 | TrampolineEntry 441 | TrampolineEntry 442 | TrampolineEntry 443 | TrampolineEntry 444 | TrampolineEntry 445 | TrampolineEntry 446 | TrampolineEntry 447 | TrampolineEntry 448 | TrampolineEntry 449 | TrampolineEntry 450 | TrampolineEntry 451 | TrampolineEntry 452 | TrampolineEntry 453 | TrampolineEntry 454 | TrampolineEntry 455 | TrampolineEntry 456 | TrampolineEntry 457 | TrampolineEntry 458 | TrampolineEntry 459 | TrampolineEntry 460 | TrampolineEntry 461 | TrampolineEntry 462 | TrampolineEntry 463 | TrampolineEntry 464 | TrampolineEntry 465 | TrampolineEntry 466 | TrampolineEntry 467 | TrampolineEntry 468 | TrampolineEntry 469 | TrampolineEntry 470 | TrampolineEntry 471 | TrampolineEntry 472 | TrampolineEntry 473 | TrampolineEntry 474 | TrampolineEntry 475 | TrampolineEntry 476 | TrampolineEntry 477 | TrampolineEntry 478 | TrampolineEntry 479 | TrampolineEntry 480 | TrampolineEntry 481 | TrampolineEntry 482 | TrampolineEntry 483 | TrampolineEntry 484 | TrampolineEntry 485 | TrampolineEntry 486 | TrampolineEntry 487 | TrampolineEntry 488 | TrampolineEntry 489 | TrampolineEntry 490 | TrampolineEntry 491 | TrampolineEntry 492 | TrampolineEntry 493 | TrampolineEntry 494 | TrampolineEntry 495 | TrampolineEntry 496 | TrampolineEntry 497 | TrampolineEntry 498 | TrampolineEntry 499 | TrampolineEntry 500 | TrampolineEntry 501 | TrampolineEntry 502 | TrampolineEntry 503 | TrampolineEntry 504 | TrampolineEntry 505 | TrampolineEntry 506 | TrampolineEntry 507 | TrampolineEntry 508 | TrampolineEntry 509 | TrampolineEntry 510 | TrampolineEntry 511 | TrampolineEntry 512 | TrampolineEntry 513 | TrampolineEntry 514 | TrampolineEntry 515 | TrampolineEntry 516 | TrampolineEntry 517 | TrampolineEntry 518 | TrampolineEntry 519 | TrampolineEntry 520 | TrampolineEntry 521 | TrampolineEntry 522 | TrampolineEntry 523 | TrampolineEntry 524 | TrampolineEntry 525 | TrampolineEntry 526 | TrampolineEntry 527 | TrampolineEntry 528 | TrampolineEntry 529 | TrampolineEntry 530 | TrampolineEntry 531 | TrampolineEntry 532 | TrampolineEntry 533 | TrampolineEntry 534 | TrampolineEntry 535 | TrampolineEntry 536 | TrampolineEntry 537 | TrampolineEntry 538 | TrampolineEntry 539 | TrampolineEntry 540 | TrampolineEntry 541 | TrampolineEntry 542 | TrampolineEntry 543 | TrampolineEntry 544 | TrampolineEntry 545 | TrampolineEntry 546 | TrampolineEntry 547 | TrampolineEntry 548 | TrampolineEntry 549 | TrampolineEntry 550 | TrampolineEntry 551 | TrampolineEntry 552 | TrampolineEntry 553 | TrampolineEntry 554 | TrampolineEntry 555 | TrampolineEntry 556 | TrampolineEntry 557 | TrampolineEntry 558 | TrampolineEntry 559 | TrampolineEntry 560 | TrampolineEntry 561 | // TrampolineEntry 562 | // TrampolineEntry 563 | // TrampolineEntry 564 | // TrampolineEntry 565 | 566 | __a1a2_selectorTrampEnd: 567 | 568 | #endif 569 | -------------------------------------------------------------------------------- /OCMethodTrace/a2a3-selectortramps-arm.s: -------------------------------------------------------------------------------- 1 | /* 2 | * a2a3-selectortramps-arm.s 3 | * OCMethodTrace 4 | * 5 | * https://github.com/omxcodec/OCMethodTrace.git 6 | * 7 | * Copyright (C) 2018 Michael Chen 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #if __arm__ 23 | 24 | #include 25 | #include 26 | 27 | .syntax unified 28 | 29 | .text 30 | 31 | .private_extern __a2a3_selectorTrampHead 32 | .private_extern __a2a3_firstSelectorTramp 33 | .private_extern __a2a3_selectorTrampEnd 34 | 35 | // Trampoline machinery assumes the trampolines are Thumb function pointers 36 | #if !__thumb2__ 37 | # error sorry 38 | #endif 39 | 40 | .thumb 41 | .thumb_func __a2a3_selectorTrampHead 42 | .thumb_func __a2a3_firstSelectorTramp 43 | .thumb_func __a2a3_selectorTrampEnd 44 | 45 | .align PAGE_MAX_SHIFT 46 | __a2a3_selectorTrampHead: 47 | // Trampoline's data is one page before the trampoline text. 48 | // Also correct PC bias of 4 bytes. 49 | // 1. selector 50 | sub r12, #PAGE_MAX_SIZE 51 | ldr r2, [r12, #-4] // _cmd = selector 52 | // 2. msgSend. Can't "ldr r12, msgSend", error: out of range pc-relative fixup value 53 | mov r12, pc 54 | sub r12, #PAGE_MAX_SIZE 55 | ldr pc, [r12, #-12] 56 | // not reached 57 | nop 58 | 59 | // Align trampolines to 8 bytes 60 | .align 3 61 | 62 | .macro TrampolineEntry 63 | mov r12, pc 64 | b __a2a3_selectorTrampHead 65 | .align 3 66 | .endmacro 67 | 68 | .macro TrampolineEntryX16 69 | TrampolineEntry 70 | TrampolineEntry 71 | TrampolineEntry 72 | TrampolineEntry 73 | 74 | TrampolineEntry 75 | TrampolineEntry 76 | TrampolineEntry 77 | TrampolineEntry 78 | 79 | TrampolineEntry 80 | TrampolineEntry 81 | TrampolineEntry 82 | TrampolineEntry 83 | 84 | TrampolineEntry 85 | TrampolineEntry 86 | TrampolineEntry 87 | TrampolineEntry 88 | .endmacro 89 | 90 | .macro TrampolineEntryX256 91 | TrampolineEntryX16 92 | TrampolineEntryX16 93 | TrampolineEntryX16 94 | TrampolineEntryX16 95 | 96 | TrampolineEntryX16 97 | TrampolineEntryX16 98 | TrampolineEntryX16 99 | TrampolineEntryX16 100 | 101 | TrampolineEntryX16 102 | TrampolineEntryX16 103 | TrampolineEntryX16 104 | TrampolineEntryX16 105 | 106 | TrampolineEntryX16 107 | TrampolineEntryX16 108 | TrampolineEntryX16 109 | TrampolineEntryX16 110 | .endmacro 111 | 112 | .private_extern __a2a3_firstSelectorTramp 113 | __a2a3_firstSelectorTramp: 114 | // 2048-2 trampolines to fill 16K page 115 | TrampolineEntryX256 116 | TrampolineEntryX256 117 | TrampolineEntryX256 118 | TrampolineEntryX256 119 | 120 | TrampolineEntryX256 121 | TrampolineEntryX256 122 | TrampolineEntryX256 123 | 124 | TrampolineEntryX16 125 | TrampolineEntryX16 126 | TrampolineEntryX16 127 | TrampolineEntryX16 128 | 129 | TrampolineEntryX16 130 | TrampolineEntryX16 131 | TrampolineEntryX16 132 | TrampolineEntryX16 133 | 134 | TrampolineEntryX16 135 | TrampolineEntryX16 136 | TrampolineEntryX16 137 | TrampolineEntryX16 138 | 139 | TrampolineEntryX16 140 | TrampolineEntryX16 141 | TrampolineEntryX16 142 | 143 | TrampolineEntry 144 | TrampolineEntry 145 | TrampolineEntry 146 | TrampolineEntry 147 | 148 | TrampolineEntry 149 | TrampolineEntry 150 | TrampolineEntry 151 | TrampolineEntry 152 | 153 | TrampolineEntry 154 | TrampolineEntry 155 | TrampolineEntry 156 | TrampolineEntry 157 | 158 | TrampolineEntry 159 | // TrampolineEntry 160 | // TrampolineEntry 161 | // TrampolineEntry 162 | 163 | .private_extern __a2a3_selectorTrampEnd 164 | __a2a3_selectorTrampEnd: 165 | 166 | #endif 167 | -------------------------------------------------------------------------------- /OCMethodTrace/a2a3-selectortramps-i386.s: -------------------------------------------------------------------------------- 1 | /* 2 | * a2a3-selectortramps-i386.s 3 | * OCMethodTrace 4 | * 5 | * https://github.com/omxcodec/OCMethodTrace.git 6 | * 7 | * Copyright (C) 2018 Michael Chen 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #ifdef __i386__ 23 | 24 | #include 25 | #include "selectortramps.h" 26 | 27 | .text 28 | .private_extern __a2a3_selectorTrampHead 29 | .private_extern __a2a3_firstSelectorTramp 30 | .private_extern __a2a3_nextSelectorTramp 31 | .private_extern __a2a3_selectorTrampEnd 32 | 33 | .align PAGE_SHIFT 34 | __a2a3_selectorTrampHead: 35 | // 1. selector 36 | popl %eax 37 | andl $0xFFFFFFF8, %eax 38 | subl $ PAGE_SIZE, %eax 39 | movl (%eax), %ecx // selectorPtr -> ecx 40 | movl %ecx, 12(%esp) // ecx -> _cmd 41 | // 2. msgSend 42 | INIT_PIC(__a2a3_selectorTrampHead) 43 | PRELOAD(__a2a3_selectorTrampHead, __a2a3_selectorTrampHead) 44 | subl $ PAGE_SIZE, LABEL_ADDR(__a2a3_selectorTrampHead, __a2a3_selectorTrampHead) 45 | END_PIC() 46 | jmp *0(%eax) // tail call msgSend 47 | 48 | .macro TrampolineEntry 49 | call __a2a3_selectorTrampHead 50 | nop 51 | nop 52 | nop 53 | .endmacro 54 | 55 | .align 5 56 | __a2a3_firstSelectorTramp: 57 | TrampolineEntry 58 | __a2a3_nextSelectorTramp: 59 | TrampolineEntry 60 | TrampolineEntry 61 | TrampolineEntry 62 | TrampolineEntry 63 | TrampolineEntry 64 | TrampolineEntry 65 | TrampolineEntry 66 | TrampolineEntry 67 | TrampolineEntry 68 | TrampolineEntry 69 | TrampolineEntry 70 | TrampolineEntry 71 | TrampolineEntry 72 | TrampolineEntry 73 | TrampolineEntry 74 | TrampolineEntry 75 | TrampolineEntry 76 | TrampolineEntry 77 | TrampolineEntry 78 | TrampolineEntry 79 | TrampolineEntry 80 | TrampolineEntry 81 | TrampolineEntry 82 | TrampolineEntry 83 | TrampolineEntry 84 | TrampolineEntry 85 | TrampolineEntry 86 | TrampolineEntry 87 | TrampolineEntry 88 | TrampolineEntry 89 | TrampolineEntry 90 | TrampolineEntry 91 | TrampolineEntry 92 | TrampolineEntry 93 | TrampolineEntry 94 | TrampolineEntry 95 | TrampolineEntry 96 | TrampolineEntry 97 | TrampolineEntry 98 | TrampolineEntry 99 | TrampolineEntry 100 | TrampolineEntry 101 | TrampolineEntry 102 | TrampolineEntry 103 | TrampolineEntry 104 | TrampolineEntry 105 | TrampolineEntry 106 | TrampolineEntry 107 | TrampolineEntry 108 | TrampolineEntry 109 | TrampolineEntry 110 | TrampolineEntry 111 | TrampolineEntry 112 | TrampolineEntry 113 | TrampolineEntry 114 | TrampolineEntry 115 | TrampolineEntry 116 | TrampolineEntry 117 | TrampolineEntry 118 | TrampolineEntry 119 | TrampolineEntry 120 | TrampolineEntry 121 | TrampolineEntry 122 | TrampolineEntry 123 | TrampolineEntry 124 | TrampolineEntry 125 | TrampolineEntry 126 | TrampolineEntry 127 | TrampolineEntry 128 | TrampolineEntry 129 | TrampolineEntry 130 | TrampolineEntry 131 | TrampolineEntry 132 | TrampolineEntry 133 | TrampolineEntry 134 | TrampolineEntry 135 | TrampolineEntry 136 | TrampolineEntry 137 | TrampolineEntry 138 | TrampolineEntry 139 | TrampolineEntry 140 | TrampolineEntry 141 | TrampolineEntry 142 | TrampolineEntry 143 | TrampolineEntry 144 | TrampolineEntry 145 | TrampolineEntry 146 | TrampolineEntry 147 | TrampolineEntry 148 | TrampolineEntry 149 | TrampolineEntry 150 | TrampolineEntry 151 | TrampolineEntry 152 | TrampolineEntry 153 | TrampolineEntry 154 | TrampolineEntry 155 | TrampolineEntry 156 | TrampolineEntry 157 | TrampolineEntry 158 | TrampolineEntry 159 | TrampolineEntry 160 | TrampolineEntry 161 | TrampolineEntry 162 | TrampolineEntry 163 | TrampolineEntry 164 | TrampolineEntry 165 | TrampolineEntry 166 | TrampolineEntry 167 | TrampolineEntry 168 | TrampolineEntry 169 | TrampolineEntry 170 | TrampolineEntry 171 | TrampolineEntry 172 | TrampolineEntry 173 | TrampolineEntry 174 | TrampolineEntry 175 | TrampolineEntry 176 | TrampolineEntry 177 | TrampolineEntry 178 | TrampolineEntry 179 | TrampolineEntry 180 | TrampolineEntry 181 | TrampolineEntry 182 | TrampolineEntry 183 | TrampolineEntry 184 | TrampolineEntry 185 | TrampolineEntry 186 | TrampolineEntry 187 | TrampolineEntry 188 | TrampolineEntry 189 | TrampolineEntry 190 | TrampolineEntry 191 | TrampolineEntry 192 | TrampolineEntry 193 | TrampolineEntry 194 | TrampolineEntry 195 | TrampolineEntry 196 | TrampolineEntry 197 | TrampolineEntry 198 | TrampolineEntry 199 | TrampolineEntry 200 | TrampolineEntry 201 | TrampolineEntry 202 | TrampolineEntry 203 | TrampolineEntry 204 | TrampolineEntry 205 | TrampolineEntry 206 | TrampolineEntry 207 | TrampolineEntry 208 | TrampolineEntry 209 | TrampolineEntry 210 | TrampolineEntry 211 | TrampolineEntry 212 | TrampolineEntry 213 | TrampolineEntry 214 | TrampolineEntry 215 | TrampolineEntry 216 | TrampolineEntry 217 | TrampolineEntry 218 | TrampolineEntry 219 | TrampolineEntry 220 | TrampolineEntry 221 | TrampolineEntry 222 | TrampolineEntry 223 | TrampolineEntry 224 | TrampolineEntry 225 | TrampolineEntry 226 | TrampolineEntry 227 | TrampolineEntry 228 | TrampolineEntry 229 | TrampolineEntry 230 | TrampolineEntry 231 | TrampolineEntry 232 | TrampolineEntry 233 | TrampolineEntry 234 | TrampolineEntry 235 | TrampolineEntry 236 | TrampolineEntry 237 | TrampolineEntry 238 | TrampolineEntry 239 | TrampolineEntry 240 | TrampolineEntry 241 | TrampolineEntry 242 | TrampolineEntry 243 | TrampolineEntry 244 | TrampolineEntry 245 | TrampolineEntry 246 | TrampolineEntry 247 | TrampolineEntry 248 | TrampolineEntry 249 | TrampolineEntry 250 | TrampolineEntry 251 | TrampolineEntry 252 | TrampolineEntry 253 | TrampolineEntry 254 | TrampolineEntry 255 | TrampolineEntry 256 | TrampolineEntry 257 | TrampolineEntry 258 | TrampolineEntry 259 | TrampolineEntry 260 | TrampolineEntry 261 | TrampolineEntry 262 | TrampolineEntry 263 | TrampolineEntry 264 | TrampolineEntry 265 | TrampolineEntry 266 | TrampolineEntry 267 | TrampolineEntry 268 | TrampolineEntry 269 | TrampolineEntry 270 | TrampolineEntry 271 | TrampolineEntry 272 | TrampolineEntry 273 | TrampolineEntry 274 | TrampolineEntry 275 | TrampolineEntry 276 | TrampolineEntry 277 | TrampolineEntry 278 | TrampolineEntry 279 | TrampolineEntry 280 | TrampolineEntry 281 | TrampolineEntry 282 | TrampolineEntry 283 | TrampolineEntry 284 | TrampolineEntry 285 | TrampolineEntry 286 | TrampolineEntry 287 | TrampolineEntry 288 | TrampolineEntry 289 | TrampolineEntry 290 | TrampolineEntry 291 | TrampolineEntry 292 | TrampolineEntry 293 | TrampolineEntry 294 | TrampolineEntry 295 | TrampolineEntry 296 | TrampolineEntry 297 | TrampolineEntry 298 | TrampolineEntry 299 | TrampolineEntry 300 | TrampolineEntry 301 | TrampolineEntry 302 | TrampolineEntry 303 | TrampolineEntry 304 | TrampolineEntry 305 | TrampolineEntry 306 | TrampolineEntry 307 | TrampolineEntry 308 | TrampolineEntry 309 | TrampolineEntry 310 | TrampolineEntry 311 | TrampolineEntry 312 | TrampolineEntry 313 | TrampolineEntry 314 | TrampolineEntry 315 | TrampolineEntry 316 | TrampolineEntry 317 | TrampolineEntry 318 | TrampolineEntry 319 | TrampolineEntry 320 | TrampolineEntry 321 | TrampolineEntry 322 | TrampolineEntry 323 | TrampolineEntry 324 | TrampolineEntry 325 | TrampolineEntry 326 | TrampolineEntry 327 | TrampolineEntry 328 | TrampolineEntry 329 | TrampolineEntry 330 | TrampolineEntry 331 | TrampolineEntry 332 | TrampolineEntry 333 | TrampolineEntry 334 | TrampolineEntry 335 | TrampolineEntry 336 | TrampolineEntry 337 | TrampolineEntry 338 | TrampolineEntry 339 | TrampolineEntry 340 | TrampolineEntry 341 | TrampolineEntry 342 | TrampolineEntry 343 | TrampolineEntry 344 | TrampolineEntry 345 | TrampolineEntry 346 | TrampolineEntry 347 | TrampolineEntry 348 | TrampolineEntry 349 | TrampolineEntry 350 | TrampolineEntry 351 | TrampolineEntry 352 | TrampolineEntry 353 | TrampolineEntry 354 | TrampolineEntry 355 | TrampolineEntry 356 | TrampolineEntry 357 | TrampolineEntry 358 | TrampolineEntry 359 | TrampolineEntry 360 | TrampolineEntry 361 | TrampolineEntry 362 | TrampolineEntry 363 | TrampolineEntry 364 | TrampolineEntry 365 | TrampolineEntry 366 | TrampolineEntry 367 | TrampolineEntry 368 | TrampolineEntry 369 | TrampolineEntry 370 | TrampolineEntry 371 | TrampolineEntry 372 | TrampolineEntry 373 | TrampolineEntry 374 | TrampolineEntry 375 | TrampolineEntry 376 | TrampolineEntry 377 | TrampolineEntry 378 | TrampolineEntry 379 | TrampolineEntry 380 | TrampolineEntry 381 | TrampolineEntry 382 | TrampolineEntry 383 | TrampolineEntry 384 | TrampolineEntry 385 | TrampolineEntry 386 | TrampolineEntry 387 | TrampolineEntry 388 | TrampolineEntry 389 | TrampolineEntry 390 | TrampolineEntry 391 | TrampolineEntry 392 | TrampolineEntry 393 | TrampolineEntry 394 | TrampolineEntry 395 | TrampolineEntry 396 | TrampolineEntry 397 | TrampolineEntry 398 | TrampolineEntry 399 | TrampolineEntry 400 | TrampolineEntry 401 | TrampolineEntry 402 | TrampolineEntry 403 | TrampolineEntry 404 | TrampolineEntry 405 | TrampolineEntry 406 | TrampolineEntry 407 | TrampolineEntry 408 | TrampolineEntry 409 | TrampolineEntry 410 | TrampolineEntry 411 | TrampolineEntry 412 | TrampolineEntry 413 | TrampolineEntry 414 | TrampolineEntry 415 | TrampolineEntry 416 | TrampolineEntry 417 | TrampolineEntry 418 | TrampolineEntry 419 | TrampolineEntry 420 | TrampolineEntry 421 | TrampolineEntry 422 | TrampolineEntry 423 | TrampolineEntry 424 | TrampolineEntry 425 | TrampolineEntry 426 | TrampolineEntry 427 | TrampolineEntry 428 | TrampolineEntry 429 | TrampolineEntry 430 | TrampolineEntry 431 | TrampolineEntry 432 | TrampolineEntry 433 | TrampolineEntry 434 | TrampolineEntry 435 | TrampolineEntry 436 | TrampolineEntry 437 | TrampolineEntry 438 | TrampolineEntry 439 | TrampolineEntry 440 | TrampolineEntry 441 | TrampolineEntry 442 | TrampolineEntry 443 | TrampolineEntry 444 | TrampolineEntry 445 | TrampolineEntry 446 | TrampolineEntry 447 | TrampolineEntry 448 | TrampolineEntry 449 | TrampolineEntry 450 | TrampolineEntry 451 | TrampolineEntry 452 | TrampolineEntry 453 | TrampolineEntry 454 | TrampolineEntry 455 | TrampolineEntry 456 | TrampolineEntry 457 | TrampolineEntry 458 | TrampolineEntry 459 | TrampolineEntry 460 | TrampolineEntry 461 | TrampolineEntry 462 | TrampolineEntry 463 | TrampolineEntry 464 | TrampolineEntry 465 | TrampolineEntry 466 | TrampolineEntry 467 | TrampolineEntry 468 | TrampolineEntry 469 | TrampolineEntry 470 | TrampolineEntry 471 | TrampolineEntry 472 | TrampolineEntry 473 | TrampolineEntry 474 | TrampolineEntry 475 | TrampolineEntry 476 | TrampolineEntry 477 | TrampolineEntry 478 | TrampolineEntry 479 | TrampolineEntry 480 | TrampolineEntry 481 | TrampolineEntry 482 | TrampolineEntry 483 | TrampolineEntry 484 | TrampolineEntry 485 | TrampolineEntry 486 | TrampolineEntry 487 | TrampolineEntry 488 | TrampolineEntry 489 | TrampolineEntry 490 | TrampolineEntry 491 | TrampolineEntry 492 | TrampolineEntry 493 | TrampolineEntry 494 | TrampolineEntry 495 | TrampolineEntry 496 | TrampolineEntry 497 | TrampolineEntry 498 | TrampolineEntry 499 | TrampolineEntry 500 | TrampolineEntry 501 | TrampolineEntry 502 | TrampolineEntry 503 | TrampolineEntry 504 | TrampolineEntry 505 | TrampolineEntry 506 | TrampolineEntry 507 | TrampolineEntry 508 | TrampolineEntry 509 | TrampolineEntry 510 | TrampolineEntry 511 | TrampolineEntry 512 | TrampolineEntry 513 | TrampolineEntry 514 | TrampolineEntry 515 | TrampolineEntry 516 | TrampolineEntry 517 | TrampolineEntry 518 | TrampolineEntry 519 | TrampolineEntry 520 | TrampolineEntry 521 | TrampolineEntry 522 | TrampolineEntry 523 | TrampolineEntry 524 | TrampolineEntry 525 | TrampolineEntry 526 | TrampolineEntry 527 | TrampolineEntry 528 | TrampolineEntry 529 | TrampolineEntry 530 | TrampolineEntry 531 | TrampolineEntry 532 | TrampolineEntry 533 | TrampolineEntry 534 | TrampolineEntry 535 | TrampolineEntry 536 | TrampolineEntry 537 | TrampolineEntry 538 | TrampolineEntry 539 | TrampolineEntry 540 | TrampolineEntry 541 | TrampolineEntry 542 | TrampolineEntry 543 | TrampolineEntry 544 | TrampolineEntry 545 | TrampolineEntry 546 | TrampolineEntry 547 | TrampolineEntry 548 | TrampolineEntry 549 | TrampolineEntry 550 | TrampolineEntry 551 | TrampolineEntry 552 | TrampolineEntry 553 | TrampolineEntry 554 | TrampolineEntry 555 | TrampolineEntry 556 | TrampolineEntry 557 | TrampolineEntry 558 | TrampolineEntry 559 | TrampolineEntry 560 | TrampolineEntry 561 | TrampolineEntry 562 | // TrampolineEntry 563 | // TrampolineEntry 564 | // TrampolineEntry 565 | // TrampolineEntry 566 | 567 | __a2a3_selectorTrampEnd: 568 | 569 | #endif 570 | -------------------------------------------------------------------------------- /OCMethodTrace/a2a3-selectortramps-x86_64.s: -------------------------------------------------------------------------------- 1 | /* 2 | * a2a3-selectortramps-x86_64.s 3 | * OCMethodTrace 4 | * 5 | * https://github.com/omxcodec/OCMethodTrace.git 6 | * 7 | * Copyright (C) 2018 Michael Chen 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #ifdef __x86_64__ 23 | 24 | #include 25 | #include "selectortramps.h" 26 | 27 | .text 28 | .private_extern __a2a3_selectorTrampHead 29 | .private_extern __a2a3_firstSelectorTramp 30 | .private_extern __a2a3_nextSelectorTramp 31 | .private_extern __a2a3_selectorTrampEnd 32 | 33 | .align PAGE_SHIFT 34 | __a2a3_selectorTrampHead: 35 | // 1. selector 36 | popq %r10 37 | andq $0xFFFFFFFFFFFFFFF8, %r10 38 | subq $ PAGE_SIZE, %r10 39 | movq (%r10), %rdx // selector -> _cmd 40 | // 2. msgSend 41 | INIT_PIC(__a2a3_selectorTrampHead) 42 | PRELOAD(__a2a3_selectorTrampHead, __a2a3_selectorTrampHead) 43 | subq $ PAGE_SIZE, LABEL_ADDR(__a2a3_selectorTrampHead, __a2a3_selectorTrampHead) 44 | END_PIC() 45 | jmp *0(%r10) // tail call msgSend 46 | 47 | .macro TrampolineEntry 48 | callq __a2a3_selectorTrampHead 49 | nop 50 | nop 51 | nop 52 | .endmacro 53 | 54 | .align 5 55 | __a2a3_firstSelectorTramp: 56 | TrampolineEntry 57 | __a2a3_nextSelectorTramp: 58 | TrampolineEntry 59 | TrampolineEntry 60 | TrampolineEntry 61 | TrampolineEntry 62 | TrampolineEntry 63 | TrampolineEntry 64 | TrampolineEntry 65 | TrampolineEntry 66 | TrampolineEntry 67 | TrampolineEntry 68 | TrampolineEntry 69 | TrampolineEntry 70 | TrampolineEntry 71 | TrampolineEntry 72 | TrampolineEntry 73 | TrampolineEntry 74 | TrampolineEntry 75 | TrampolineEntry 76 | TrampolineEntry 77 | TrampolineEntry 78 | TrampolineEntry 79 | TrampolineEntry 80 | TrampolineEntry 81 | TrampolineEntry 82 | TrampolineEntry 83 | TrampolineEntry 84 | TrampolineEntry 85 | TrampolineEntry 86 | TrampolineEntry 87 | TrampolineEntry 88 | TrampolineEntry 89 | TrampolineEntry 90 | TrampolineEntry 91 | TrampolineEntry 92 | TrampolineEntry 93 | TrampolineEntry 94 | TrampolineEntry 95 | TrampolineEntry 96 | TrampolineEntry 97 | TrampolineEntry 98 | TrampolineEntry 99 | TrampolineEntry 100 | TrampolineEntry 101 | TrampolineEntry 102 | TrampolineEntry 103 | TrampolineEntry 104 | TrampolineEntry 105 | TrampolineEntry 106 | TrampolineEntry 107 | TrampolineEntry 108 | TrampolineEntry 109 | TrampolineEntry 110 | TrampolineEntry 111 | TrampolineEntry 112 | TrampolineEntry 113 | TrampolineEntry 114 | TrampolineEntry 115 | TrampolineEntry 116 | TrampolineEntry 117 | TrampolineEntry 118 | TrampolineEntry 119 | TrampolineEntry 120 | TrampolineEntry 121 | TrampolineEntry 122 | TrampolineEntry 123 | TrampolineEntry 124 | TrampolineEntry 125 | TrampolineEntry 126 | TrampolineEntry 127 | TrampolineEntry 128 | TrampolineEntry 129 | TrampolineEntry 130 | TrampolineEntry 131 | TrampolineEntry 132 | TrampolineEntry 133 | TrampolineEntry 134 | TrampolineEntry 135 | TrampolineEntry 136 | TrampolineEntry 137 | TrampolineEntry 138 | TrampolineEntry 139 | TrampolineEntry 140 | TrampolineEntry 141 | TrampolineEntry 142 | TrampolineEntry 143 | TrampolineEntry 144 | TrampolineEntry 145 | TrampolineEntry 146 | TrampolineEntry 147 | TrampolineEntry 148 | TrampolineEntry 149 | TrampolineEntry 150 | TrampolineEntry 151 | TrampolineEntry 152 | TrampolineEntry 153 | TrampolineEntry 154 | TrampolineEntry 155 | TrampolineEntry 156 | TrampolineEntry 157 | TrampolineEntry 158 | TrampolineEntry 159 | TrampolineEntry 160 | TrampolineEntry 161 | TrampolineEntry 162 | TrampolineEntry 163 | TrampolineEntry 164 | TrampolineEntry 165 | TrampolineEntry 166 | TrampolineEntry 167 | TrampolineEntry 168 | TrampolineEntry 169 | TrampolineEntry 170 | TrampolineEntry 171 | TrampolineEntry 172 | TrampolineEntry 173 | TrampolineEntry 174 | TrampolineEntry 175 | TrampolineEntry 176 | TrampolineEntry 177 | TrampolineEntry 178 | TrampolineEntry 179 | TrampolineEntry 180 | TrampolineEntry 181 | TrampolineEntry 182 | TrampolineEntry 183 | TrampolineEntry 184 | TrampolineEntry 185 | TrampolineEntry 186 | TrampolineEntry 187 | TrampolineEntry 188 | TrampolineEntry 189 | TrampolineEntry 190 | TrampolineEntry 191 | TrampolineEntry 192 | TrampolineEntry 193 | TrampolineEntry 194 | TrampolineEntry 195 | TrampolineEntry 196 | TrampolineEntry 197 | TrampolineEntry 198 | TrampolineEntry 199 | TrampolineEntry 200 | TrampolineEntry 201 | TrampolineEntry 202 | TrampolineEntry 203 | TrampolineEntry 204 | TrampolineEntry 205 | TrampolineEntry 206 | TrampolineEntry 207 | TrampolineEntry 208 | TrampolineEntry 209 | TrampolineEntry 210 | TrampolineEntry 211 | TrampolineEntry 212 | TrampolineEntry 213 | TrampolineEntry 214 | TrampolineEntry 215 | TrampolineEntry 216 | TrampolineEntry 217 | TrampolineEntry 218 | TrampolineEntry 219 | TrampolineEntry 220 | TrampolineEntry 221 | TrampolineEntry 222 | TrampolineEntry 223 | TrampolineEntry 224 | TrampolineEntry 225 | TrampolineEntry 226 | TrampolineEntry 227 | TrampolineEntry 228 | TrampolineEntry 229 | TrampolineEntry 230 | TrampolineEntry 231 | TrampolineEntry 232 | TrampolineEntry 233 | TrampolineEntry 234 | TrampolineEntry 235 | TrampolineEntry 236 | TrampolineEntry 237 | TrampolineEntry 238 | TrampolineEntry 239 | TrampolineEntry 240 | TrampolineEntry 241 | TrampolineEntry 242 | TrampolineEntry 243 | TrampolineEntry 244 | TrampolineEntry 245 | TrampolineEntry 246 | TrampolineEntry 247 | TrampolineEntry 248 | TrampolineEntry 249 | TrampolineEntry 250 | TrampolineEntry 251 | TrampolineEntry 252 | TrampolineEntry 253 | TrampolineEntry 254 | TrampolineEntry 255 | TrampolineEntry 256 | TrampolineEntry 257 | TrampolineEntry 258 | TrampolineEntry 259 | TrampolineEntry 260 | TrampolineEntry 261 | TrampolineEntry 262 | TrampolineEntry 263 | TrampolineEntry 264 | TrampolineEntry 265 | TrampolineEntry 266 | TrampolineEntry 267 | TrampolineEntry 268 | TrampolineEntry 269 | TrampolineEntry 270 | TrampolineEntry 271 | TrampolineEntry 272 | TrampolineEntry 273 | TrampolineEntry 274 | TrampolineEntry 275 | TrampolineEntry 276 | TrampolineEntry 277 | TrampolineEntry 278 | TrampolineEntry 279 | TrampolineEntry 280 | TrampolineEntry 281 | TrampolineEntry 282 | TrampolineEntry 283 | TrampolineEntry 284 | TrampolineEntry 285 | TrampolineEntry 286 | TrampolineEntry 287 | TrampolineEntry 288 | TrampolineEntry 289 | TrampolineEntry 290 | TrampolineEntry 291 | TrampolineEntry 292 | TrampolineEntry 293 | TrampolineEntry 294 | TrampolineEntry 295 | TrampolineEntry 296 | TrampolineEntry 297 | TrampolineEntry 298 | TrampolineEntry 299 | TrampolineEntry 300 | TrampolineEntry 301 | TrampolineEntry 302 | TrampolineEntry 303 | TrampolineEntry 304 | TrampolineEntry 305 | TrampolineEntry 306 | TrampolineEntry 307 | TrampolineEntry 308 | TrampolineEntry 309 | TrampolineEntry 310 | TrampolineEntry 311 | TrampolineEntry 312 | TrampolineEntry 313 | TrampolineEntry 314 | TrampolineEntry 315 | TrampolineEntry 316 | TrampolineEntry 317 | TrampolineEntry 318 | TrampolineEntry 319 | TrampolineEntry 320 | TrampolineEntry 321 | TrampolineEntry 322 | TrampolineEntry 323 | TrampolineEntry 324 | TrampolineEntry 325 | TrampolineEntry 326 | TrampolineEntry 327 | TrampolineEntry 328 | TrampolineEntry 329 | TrampolineEntry 330 | TrampolineEntry 331 | TrampolineEntry 332 | TrampolineEntry 333 | TrampolineEntry 334 | TrampolineEntry 335 | TrampolineEntry 336 | TrampolineEntry 337 | TrampolineEntry 338 | TrampolineEntry 339 | TrampolineEntry 340 | TrampolineEntry 341 | TrampolineEntry 342 | TrampolineEntry 343 | TrampolineEntry 344 | TrampolineEntry 345 | TrampolineEntry 346 | TrampolineEntry 347 | TrampolineEntry 348 | TrampolineEntry 349 | TrampolineEntry 350 | TrampolineEntry 351 | TrampolineEntry 352 | TrampolineEntry 353 | TrampolineEntry 354 | TrampolineEntry 355 | TrampolineEntry 356 | TrampolineEntry 357 | TrampolineEntry 358 | TrampolineEntry 359 | TrampolineEntry 360 | TrampolineEntry 361 | TrampolineEntry 362 | TrampolineEntry 363 | TrampolineEntry 364 | TrampolineEntry 365 | TrampolineEntry 366 | TrampolineEntry 367 | TrampolineEntry 368 | TrampolineEntry 369 | TrampolineEntry 370 | TrampolineEntry 371 | TrampolineEntry 372 | TrampolineEntry 373 | TrampolineEntry 374 | TrampolineEntry 375 | TrampolineEntry 376 | TrampolineEntry 377 | TrampolineEntry 378 | TrampolineEntry 379 | TrampolineEntry 380 | TrampolineEntry 381 | TrampolineEntry 382 | TrampolineEntry 383 | TrampolineEntry 384 | TrampolineEntry 385 | TrampolineEntry 386 | TrampolineEntry 387 | TrampolineEntry 388 | TrampolineEntry 389 | TrampolineEntry 390 | TrampolineEntry 391 | TrampolineEntry 392 | TrampolineEntry 393 | TrampolineEntry 394 | TrampolineEntry 395 | TrampolineEntry 396 | TrampolineEntry 397 | TrampolineEntry 398 | TrampolineEntry 399 | TrampolineEntry 400 | TrampolineEntry 401 | TrampolineEntry 402 | TrampolineEntry 403 | TrampolineEntry 404 | TrampolineEntry 405 | TrampolineEntry 406 | TrampolineEntry 407 | TrampolineEntry 408 | TrampolineEntry 409 | TrampolineEntry 410 | TrampolineEntry 411 | TrampolineEntry 412 | TrampolineEntry 413 | TrampolineEntry 414 | TrampolineEntry 415 | TrampolineEntry 416 | TrampolineEntry 417 | TrampolineEntry 418 | TrampolineEntry 419 | TrampolineEntry 420 | TrampolineEntry 421 | TrampolineEntry 422 | TrampolineEntry 423 | TrampolineEntry 424 | TrampolineEntry 425 | TrampolineEntry 426 | TrampolineEntry 427 | TrampolineEntry 428 | TrampolineEntry 429 | TrampolineEntry 430 | TrampolineEntry 431 | TrampolineEntry 432 | TrampolineEntry 433 | TrampolineEntry 434 | TrampolineEntry 435 | TrampolineEntry 436 | TrampolineEntry 437 | TrampolineEntry 438 | TrampolineEntry 439 | TrampolineEntry 440 | TrampolineEntry 441 | TrampolineEntry 442 | TrampolineEntry 443 | TrampolineEntry 444 | TrampolineEntry 445 | TrampolineEntry 446 | TrampolineEntry 447 | TrampolineEntry 448 | TrampolineEntry 449 | TrampolineEntry 450 | TrampolineEntry 451 | TrampolineEntry 452 | TrampolineEntry 453 | TrampolineEntry 454 | TrampolineEntry 455 | TrampolineEntry 456 | TrampolineEntry 457 | TrampolineEntry 458 | TrampolineEntry 459 | TrampolineEntry 460 | TrampolineEntry 461 | TrampolineEntry 462 | TrampolineEntry 463 | TrampolineEntry 464 | TrampolineEntry 465 | TrampolineEntry 466 | TrampolineEntry 467 | TrampolineEntry 468 | TrampolineEntry 469 | TrampolineEntry 470 | TrampolineEntry 471 | TrampolineEntry 472 | TrampolineEntry 473 | TrampolineEntry 474 | TrampolineEntry 475 | TrampolineEntry 476 | TrampolineEntry 477 | TrampolineEntry 478 | TrampolineEntry 479 | TrampolineEntry 480 | TrampolineEntry 481 | TrampolineEntry 482 | TrampolineEntry 483 | TrampolineEntry 484 | TrampolineEntry 485 | TrampolineEntry 486 | TrampolineEntry 487 | TrampolineEntry 488 | TrampolineEntry 489 | TrampolineEntry 490 | TrampolineEntry 491 | TrampolineEntry 492 | TrampolineEntry 493 | TrampolineEntry 494 | TrampolineEntry 495 | TrampolineEntry 496 | TrampolineEntry 497 | TrampolineEntry 498 | TrampolineEntry 499 | TrampolineEntry 500 | TrampolineEntry 501 | TrampolineEntry 502 | TrampolineEntry 503 | TrampolineEntry 504 | TrampolineEntry 505 | TrampolineEntry 506 | TrampolineEntry 507 | TrampolineEntry 508 | TrampolineEntry 509 | TrampolineEntry 510 | TrampolineEntry 511 | TrampolineEntry 512 | TrampolineEntry 513 | TrampolineEntry 514 | TrampolineEntry 515 | TrampolineEntry 516 | TrampolineEntry 517 | TrampolineEntry 518 | TrampolineEntry 519 | TrampolineEntry 520 | TrampolineEntry 521 | TrampolineEntry 522 | TrampolineEntry 523 | TrampolineEntry 524 | TrampolineEntry 525 | TrampolineEntry 526 | TrampolineEntry 527 | TrampolineEntry 528 | TrampolineEntry 529 | TrampolineEntry 530 | TrampolineEntry 531 | TrampolineEntry 532 | TrampolineEntry 533 | TrampolineEntry 534 | TrampolineEntry 535 | TrampolineEntry 536 | TrampolineEntry 537 | TrampolineEntry 538 | TrampolineEntry 539 | TrampolineEntry 540 | TrampolineEntry 541 | TrampolineEntry 542 | TrampolineEntry 543 | TrampolineEntry 544 | TrampolineEntry 545 | TrampolineEntry 546 | TrampolineEntry 547 | TrampolineEntry 548 | TrampolineEntry 549 | TrampolineEntry 550 | TrampolineEntry 551 | TrampolineEntry 552 | TrampolineEntry 553 | TrampolineEntry 554 | TrampolineEntry 555 | TrampolineEntry 556 | TrampolineEntry 557 | TrampolineEntry 558 | TrampolineEntry 559 | TrampolineEntry 560 | TrampolineEntry 561 | // TrampolineEntry 562 | // TrampolineEntry 563 | // TrampolineEntry 564 | // TrampolineEntry 565 | 566 | __a2a3_selectorTrampEnd: 567 | 568 | #endif 569 | -------------------------------------------------------------------------------- /OCMethodTrace/selectortramps.h: -------------------------------------------------------------------------------- 1 | /* 2 | * selectortramps.mac 3 | * OCMethodTrace 4 | * 5 | * https://github.com/omxcodec/OCMethodTrace.git 6 | * 7 | * Copyright (C) 2018 Michael Chen 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | 22 | #ifdef __x86_64__ 23 | #define PRELOAD(x,f) movq x@GOTPCREL(%rip), %r10; 24 | #define LABEL_ADDR(x,f) %r10 25 | #define LABEL_VALUE(x,f) (%r10) 26 | #define INIT_PIC(f) 27 | #define END_PIC(f) 28 | #elif __i386__ 29 | #define PRELOAD(x,f) leal x-L0## f ##$pb(%ebx), %eax; 30 | #define LABEL_ADDR(x,f) %eax 31 | #define LABEL_VALUE(x,f) (%eax) 32 | #define INIT_PIC(x) \ 33 | push %ebx; \ 34 | call L0## x ##$pb; \ 35 | L0## x ##$pb:; \ 36 | pop %ebx; 37 | #define END_PIC(x) pop %ebx 38 | #endif 39 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4C09CF26214DFFBD00870465 /* a1a2-selectortramps-arm64.s in Sources */ = {isa = PBXBuildFile; fileRef = 4C09CF1C214DFFBD00870465 /* a1a2-selectortramps-arm64.s */; }; 11 | 4C09CF27214DFFBD00870465 /* a2a3-selectortramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 4C09CF1D214DFFBD00870465 /* a2a3-selectortramps-arm.s */; }; 12 | 4C09CF28214DFFBD00870465 /* selectortramps.mac in Resources */ = {isa = PBXBuildFile; fileRef = 4C09CF1F214DFFBD00870465 /* selectortramps.mac */; }; 13 | 4C09CF29214DFFBD00870465 /* a1a2-selectortramps-arm.s in Sources */ = {isa = PBXBuildFile; fileRef = 4C09CF20214DFFBD00870465 /* a1a2-selectortramps-arm.s */; }; 14 | 4C09CF2A214DFFBD00870465 /* OCSelectorTrampolines.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4C09CF21214DFFBD00870465 /* OCSelectorTrampolines.mm */; }; 15 | 4C09CF2B214DFFBD00870465 /* a2a3-selectortramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = 4C09CF22214DFFBD00870465 /* a2a3-selectortramps-x86_64.s */; }; 16 | 4C09CF2C214DFFBD00870465 /* a1a2-selectortramps-x86_64.s in Sources */ = {isa = PBXBuildFile; fileRef = 4C09CF23214DFFBD00870465 /* a1a2-selectortramps-x86_64.s */; }; 17 | 4C09CF2D214DFFBD00870465 /* a1a2-selectortramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 4C09CF24214DFFBD00870465 /* a1a2-selectortramps-i386.s */; }; 18 | 4C09CF2E214DFFBD00870465 /* a2a3-selectortramps-i386.s in Sources */ = {isa = PBXBuildFile; fileRef = 4C09CF25214DFFBD00870465 /* a2a3-selectortramps-i386.s */; }; 19 | 4CB2B78E2144BC9F00474F3B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CB2B78D2144BC9F00474F3B /* AppDelegate.m */; }; 20 | 4CB2B7912144BC9F00474F3B /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CB2B7902144BC9F00474F3B /* ViewController.m */; }; 21 | 4CB2B7942144BC9F00474F3B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4CB2B7922144BC9F00474F3B /* Main.storyboard */; }; 22 | 4CB2B7962144BCA000474F3B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4CB2B7952144BCA000474F3B /* Assets.xcassets */; }; 23 | 4CB2B7992144BCA000474F3B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4CB2B7972144BCA000474F3B /* LaunchScreen.storyboard */; }; 24 | 4CB2B79C2144BCA000474F3B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CB2B79B2144BCA000474F3B /* main.m */; }; 25 | 4CB2B7A62144BCA000474F3B /* OCMethodTraceDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CB2B7A52144BCA000474F3B /* OCMethodTraceDemoTests.m */; }; 26 | 4CB2B7B62144BEDE00474F3B /* OCMethodTrace.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CB2B7B12144BEDD00474F3B /* OCMethodTrace.m */; }; 27 | 4CB2B7BD2144BEF200474F3B /* MDMethodTrace.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CB2B7BB2144BEF200474F3B /* MDMethodTrace.m */; }; 28 | 4CB2B7BE2144BEF200474F3B /* MDConfigManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CB2B7BC2144BEF200474F3B /* MDConfigManager.m */; }; 29 | 4CB2B7C02144BF6900474F3B /* MDConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4CB2B7BF2144BF6900474F3B /* MDConfig.plist */; }; 30 | 4CB2B7C52144C25F00474F3B /* Tiger.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CB2B7C32144C25F00474F3B /* Tiger.m */; }; 31 | 4CB2B7C62144C25F00474F3B /* Animal.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CB2B7C42144C25F00474F3B /* Animal.m */; }; 32 | /* End PBXBuildFile section */ 33 | 34 | /* Begin PBXContainerItemProxy section */ 35 | 4CB2B7A22144BCA000474F3B /* PBXContainerItemProxy */ = { 36 | isa = PBXContainerItemProxy; 37 | containerPortal = 4CB2B7812144BC9F00474F3B /* Project object */; 38 | proxyType = 1; 39 | remoteGlobalIDString = 4CB2B7882144BC9F00474F3B; 40 | remoteInfo = OCMethodTraceDemo; 41 | }; 42 | /* End PBXContainerItemProxy section */ 43 | 44 | /* Begin PBXFileReference section */ 45 | 4C09CF1C214DFFBD00870465 /* a1a2-selectortramps-arm64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = "a1a2-selectortramps-arm64.s"; sourceTree = ""; }; 46 | 4C09CF1D214DFFBD00870465 /* a2a3-selectortramps-arm.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = "a2a3-selectortramps-arm.s"; sourceTree = ""; }; 47 | 4C09CF1E214DFFBD00870465 /* OCSelectorTrampolines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCSelectorTrampolines.h; sourceTree = ""; }; 48 | 4C09CF1F214DFFBD00870465 /* selectortramps.mac */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = selectortramps.mac; sourceTree = ""; }; 49 | 4C09CF20214DFFBD00870465 /* a1a2-selectortramps-arm.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = "a1a2-selectortramps-arm.s"; sourceTree = ""; }; 50 | 4C09CF21214DFFBD00870465 /* OCSelectorTrampolines.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OCSelectorTrampolines.mm; sourceTree = ""; }; 51 | 4C09CF22214DFFBD00870465 /* a2a3-selectortramps-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = "a2a3-selectortramps-x86_64.s"; sourceTree = ""; }; 52 | 4C09CF23214DFFBD00870465 /* a1a2-selectortramps-x86_64.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = "a1a2-selectortramps-x86_64.s"; sourceTree = ""; }; 53 | 4C09CF24214DFFBD00870465 /* a1a2-selectortramps-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = "a1a2-selectortramps-i386.s"; sourceTree = ""; }; 54 | 4C09CF25214DFFBD00870465 /* a2a3-selectortramps-i386.s */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = "a2a3-selectortramps-i386.s"; sourceTree = ""; }; 55 | 4CB2B7892144BC9F00474F3B /* OCMethodTraceDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = OCMethodTraceDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 56 | 4CB2B78C2144BC9F00474F3B /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 57 | 4CB2B78D2144BC9F00474F3B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 58 | 4CB2B78F2144BC9F00474F3B /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; 59 | 4CB2B7902144BC9F00474F3B /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 60 | 4CB2B7932144BC9F00474F3B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 61 | 4CB2B7952144BCA000474F3B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 62 | 4CB2B7982144BCA000474F3B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 63 | 4CB2B79A2144BCA000474F3B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 64 | 4CB2B79B2144BCA000474F3B /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 65 | 4CB2B7A12144BCA000474F3B /* OCMethodTraceDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OCMethodTraceDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 4CB2B7A52144BCA000474F3B /* OCMethodTraceDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OCMethodTraceDemoTests.m; sourceTree = ""; }; 67 | 4CB2B7A72144BCA000474F3B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | 4CB2B7B12144BEDD00474F3B /* OCMethodTrace.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OCMethodTrace.m; sourceTree = ""; }; 69 | 4CB2B7B42144BEDD00474F3B /* OCMethodTrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OCMethodTrace.h; sourceTree = ""; }; 70 | 4CB2B7B92144BEF200474F3B /* MDMethodTrace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MDMethodTrace.h; sourceTree = ""; }; 71 | 4CB2B7BA2144BEF200474F3B /* MDConfigManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MDConfigManager.h; sourceTree = ""; }; 72 | 4CB2B7BB2144BEF200474F3B /* MDMethodTrace.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MDMethodTrace.m; sourceTree = ""; }; 73 | 4CB2B7BC2144BEF200474F3B /* MDConfigManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MDConfigManager.m; sourceTree = ""; }; 74 | 4CB2B7BF2144BF6900474F3B /* MDConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = MDConfig.plist; sourceTree = ""; }; 75 | 4CB2B7C12144C25E00474F3B /* Tiger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Tiger.h; sourceTree = ""; }; 76 | 4CB2B7C22144C25E00474F3B /* Animal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Animal.h; sourceTree = ""; }; 77 | 4CB2B7C32144C25F00474F3B /* Tiger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Tiger.m; sourceTree = ""; }; 78 | 4CB2B7C42144C25F00474F3B /* Animal.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Animal.m; sourceTree = ""; }; 79 | 4CB2B7C72144C35B00474F3B /* TestUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TestUtils.h; sourceTree = ""; }; 80 | /* End PBXFileReference section */ 81 | 82 | /* Begin PBXFrameworksBuildPhase section */ 83 | 4CB2B7862144BC9F00474F3B /* Frameworks */ = { 84 | isa = PBXFrameworksBuildPhase; 85 | buildActionMask = 2147483647; 86 | files = ( 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | 4CB2B79E2144BCA000474F3B /* Frameworks */ = { 91 | isa = PBXFrameworksBuildPhase; 92 | buildActionMask = 2147483647; 93 | files = ( 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | /* End PBXFrameworksBuildPhase section */ 98 | 99 | /* Begin PBXGroup section */ 100 | 4CB2B7802144BC9F00474F3B = { 101 | isa = PBXGroup; 102 | children = ( 103 | 4CB2B7B02144BEDD00474F3B /* OCMethodTrace */, 104 | 4CB2B78B2144BC9F00474F3B /* OCMethodTraceDemo */, 105 | 4CB2B7A42144BCA000474F3B /* OCMethodTraceDemoTests */, 106 | 4CB2B78A2144BC9F00474F3B /* Products */, 107 | ); 108 | sourceTree = ""; 109 | }; 110 | 4CB2B78A2144BC9F00474F3B /* Products */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 4CB2B7892144BC9F00474F3B /* OCMethodTraceDemo.app */, 114 | 4CB2B7A12144BCA000474F3B /* OCMethodTraceDemoTests.xctest */, 115 | ); 116 | name = Products; 117 | sourceTree = ""; 118 | }; 119 | 4CB2B78B2144BC9F00474F3B /* OCMethodTraceDemo */ = { 120 | isa = PBXGroup; 121 | children = ( 122 | 4CB2B7C22144C25E00474F3B /* Animal.h */, 123 | 4CB2B7C42144C25F00474F3B /* Animal.m */, 124 | 4CB2B78C2144BC9F00474F3B /* AppDelegate.h */, 125 | 4CB2B78D2144BC9F00474F3B /* AppDelegate.m */, 126 | 4CB2B7952144BCA000474F3B /* Assets.xcassets */, 127 | 4CB2B79A2144BCA000474F3B /* Info.plist */, 128 | 4CB2B7972144BCA000474F3B /* LaunchScreen.storyboard */, 129 | 4CB2B79B2144BCA000474F3B /* main.m */, 130 | 4CB2B7922144BC9F00474F3B /* Main.storyboard */, 131 | 4CB2B7BF2144BF6900474F3B /* MDConfig.plist */, 132 | 4CB2B7BA2144BEF200474F3B /* MDConfigManager.h */, 133 | 4CB2B7BC2144BEF200474F3B /* MDConfigManager.m */, 134 | 4CB2B7B92144BEF200474F3B /* MDMethodTrace.h */, 135 | 4CB2B7BB2144BEF200474F3B /* MDMethodTrace.m */, 136 | 4CB2B7C72144C35B00474F3B /* TestUtils.h */, 137 | 4CB2B7C12144C25E00474F3B /* Tiger.h */, 138 | 4CB2B7C32144C25F00474F3B /* Tiger.m */, 139 | 4CB2B78F2144BC9F00474F3B /* ViewController.h */, 140 | 4CB2B7902144BC9F00474F3B /* ViewController.m */, 141 | ); 142 | path = OCMethodTraceDemo; 143 | sourceTree = ""; 144 | }; 145 | 4CB2B7A42144BCA000474F3B /* OCMethodTraceDemoTests */ = { 146 | isa = PBXGroup; 147 | children = ( 148 | 4CB2B7A52144BCA000474F3B /* OCMethodTraceDemoTests.m */, 149 | 4CB2B7A72144BCA000474F3B /* Info.plist */, 150 | ); 151 | path = OCMethodTraceDemoTests; 152 | sourceTree = ""; 153 | }; 154 | 4CB2B7B02144BEDD00474F3B /* OCMethodTrace */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 4C09CF20214DFFBD00870465 /* a1a2-selectortramps-arm.s */, 158 | 4C09CF1C214DFFBD00870465 /* a1a2-selectortramps-arm64.s */, 159 | 4C09CF24214DFFBD00870465 /* a1a2-selectortramps-i386.s */, 160 | 4C09CF23214DFFBD00870465 /* a1a2-selectortramps-x86_64.s */, 161 | 4C09CF1D214DFFBD00870465 /* a2a3-selectortramps-arm.s */, 162 | 4C09CF25214DFFBD00870465 /* a2a3-selectortramps-i386.s */, 163 | 4C09CF22214DFFBD00870465 /* a2a3-selectortramps-x86_64.s */, 164 | 4CB2B7B42144BEDD00474F3B /* OCMethodTrace.h */, 165 | 4CB2B7B12144BEDD00474F3B /* OCMethodTrace.m */, 166 | 4C09CF1E214DFFBD00870465 /* OCSelectorTrampolines.h */, 167 | 4C09CF21214DFFBD00870465 /* OCSelectorTrampolines.mm */, 168 | 4C09CF1F214DFFBD00870465 /* selectortramps.mac */, 169 | ); 170 | name = OCMethodTrace; 171 | path = ../OCMethodTrace; 172 | sourceTree = ""; 173 | }; 174 | /* End PBXGroup section */ 175 | 176 | /* Begin PBXNativeTarget section */ 177 | 4CB2B7882144BC9F00474F3B /* OCMethodTraceDemo */ = { 178 | isa = PBXNativeTarget; 179 | buildConfigurationList = 4CB2B7AA2144BCA000474F3B /* Build configuration list for PBXNativeTarget "OCMethodTraceDemo" */; 180 | buildPhases = ( 181 | 4CB2B7852144BC9F00474F3B /* Sources */, 182 | 4CB2B7862144BC9F00474F3B /* Frameworks */, 183 | 4CB2B7872144BC9F00474F3B /* Resources */, 184 | ); 185 | buildRules = ( 186 | ); 187 | dependencies = ( 188 | ); 189 | name = OCMethodTraceDemo; 190 | productName = OCMethodTraceDemo; 191 | productReference = 4CB2B7892144BC9F00474F3B /* OCMethodTraceDemo.app */; 192 | productType = "com.apple.product-type.application"; 193 | }; 194 | 4CB2B7A02144BCA000474F3B /* OCMethodTraceDemoTests */ = { 195 | isa = PBXNativeTarget; 196 | buildConfigurationList = 4CB2B7AD2144BCA000474F3B /* Build configuration list for PBXNativeTarget "OCMethodTraceDemoTests" */; 197 | buildPhases = ( 198 | 4CB2B79D2144BCA000474F3B /* Sources */, 199 | 4CB2B79E2144BCA000474F3B /* Frameworks */, 200 | 4CB2B79F2144BCA000474F3B /* Resources */, 201 | ); 202 | buildRules = ( 203 | ); 204 | dependencies = ( 205 | 4CB2B7A32144BCA000474F3B /* PBXTargetDependency */, 206 | ); 207 | name = OCMethodTraceDemoTests; 208 | productName = OCMethodTraceDemoTests; 209 | productReference = 4CB2B7A12144BCA000474F3B /* OCMethodTraceDemoTests.xctest */; 210 | productType = "com.apple.product-type.bundle.unit-test"; 211 | }; 212 | /* End PBXNativeTarget section */ 213 | 214 | /* Begin PBXProject section */ 215 | 4CB2B7812144BC9F00474F3B /* Project object */ = { 216 | isa = PBXProject; 217 | attributes = { 218 | LastUpgradeCheck = 0940; 219 | ORGANIZATIONNAME = example; 220 | TargetAttributes = { 221 | 4CB2B7882144BC9F00474F3B = { 222 | CreatedOnToolsVersion = 9.4.1; 223 | }; 224 | 4CB2B7A02144BCA000474F3B = { 225 | CreatedOnToolsVersion = 9.4.1; 226 | TestTargetID = 4CB2B7882144BC9F00474F3B; 227 | }; 228 | }; 229 | }; 230 | buildConfigurationList = 4CB2B7842144BC9F00474F3B /* Build configuration list for PBXProject "OCMethodTraceDemo" */; 231 | compatibilityVersion = "Xcode 9.3"; 232 | developmentRegion = en; 233 | hasScannedForEncodings = 0; 234 | knownRegions = ( 235 | en, 236 | Base, 237 | ); 238 | mainGroup = 4CB2B7802144BC9F00474F3B; 239 | productRefGroup = 4CB2B78A2144BC9F00474F3B /* Products */; 240 | projectDirPath = ""; 241 | projectRoot = ""; 242 | targets = ( 243 | 4CB2B7882144BC9F00474F3B /* OCMethodTraceDemo */, 244 | 4CB2B7A02144BCA000474F3B /* OCMethodTraceDemoTests */, 245 | ); 246 | }; 247 | /* End PBXProject section */ 248 | 249 | /* Begin PBXResourcesBuildPhase section */ 250 | 4CB2B7872144BC9F00474F3B /* Resources */ = { 251 | isa = PBXResourcesBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | 4CB2B7992144BCA000474F3B /* LaunchScreen.storyboard in Resources */, 255 | 4CB2B7962144BCA000474F3B /* Assets.xcassets in Resources */, 256 | 4C09CF28214DFFBD00870465 /* selectortramps.mac in Resources */, 257 | 4CB2B7942144BC9F00474F3B /* Main.storyboard in Resources */, 258 | 4CB2B7C02144BF6900474F3B /* MDConfig.plist in Resources */, 259 | ); 260 | runOnlyForDeploymentPostprocessing = 0; 261 | }; 262 | 4CB2B79F2144BCA000474F3B /* Resources */ = { 263 | isa = PBXResourcesBuildPhase; 264 | buildActionMask = 2147483647; 265 | files = ( 266 | ); 267 | runOnlyForDeploymentPostprocessing = 0; 268 | }; 269 | /* End PBXResourcesBuildPhase section */ 270 | 271 | /* Begin PBXSourcesBuildPhase section */ 272 | 4CB2B7852144BC9F00474F3B /* Sources */ = { 273 | isa = PBXSourcesBuildPhase; 274 | buildActionMask = 2147483647; 275 | files = ( 276 | 4C09CF29214DFFBD00870465 /* a1a2-selectortramps-arm.s in Sources */, 277 | 4CB2B7B62144BEDE00474F3B /* OCMethodTrace.m in Sources */, 278 | 4C09CF26214DFFBD00870465 /* a1a2-selectortramps-arm64.s in Sources */, 279 | 4CB2B7912144BC9F00474F3B /* ViewController.m in Sources */, 280 | 4CB2B7C52144C25F00474F3B /* Tiger.m in Sources */, 281 | 4C09CF2A214DFFBD00870465 /* OCSelectorTrampolines.mm in Sources */, 282 | 4CB2B79C2144BCA000474F3B /* main.m in Sources */, 283 | 4C09CF2C214DFFBD00870465 /* a1a2-selectortramps-x86_64.s in Sources */, 284 | 4CB2B78E2144BC9F00474F3B /* AppDelegate.m in Sources */, 285 | 4C09CF2B214DFFBD00870465 /* a2a3-selectortramps-x86_64.s in Sources */, 286 | 4CB2B7BD2144BEF200474F3B /* MDMethodTrace.m in Sources */, 287 | 4CB2B7C62144C25F00474F3B /* Animal.m in Sources */, 288 | 4CB2B7BE2144BEF200474F3B /* MDConfigManager.m in Sources */, 289 | 4C09CF27214DFFBD00870465 /* a2a3-selectortramps-arm.s in Sources */, 290 | 4C09CF2E214DFFBD00870465 /* a2a3-selectortramps-i386.s in Sources */, 291 | 4C09CF2D214DFFBD00870465 /* a1a2-selectortramps-i386.s in Sources */, 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | }; 295 | 4CB2B79D2144BCA000474F3B /* Sources */ = { 296 | isa = PBXSourcesBuildPhase; 297 | buildActionMask = 2147483647; 298 | files = ( 299 | 4CB2B7A62144BCA000474F3B /* OCMethodTraceDemoTests.m in Sources */, 300 | ); 301 | runOnlyForDeploymentPostprocessing = 0; 302 | }; 303 | /* End PBXSourcesBuildPhase section */ 304 | 305 | /* Begin PBXTargetDependency section */ 306 | 4CB2B7A32144BCA000474F3B /* PBXTargetDependency */ = { 307 | isa = PBXTargetDependency; 308 | target = 4CB2B7882144BC9F00474F3B /* OCMethodTraceDemo */; 309 | targetProxy = 4CB2B7A22144BCA000474F3B /* PBXContainerItemProxy */; 310 | }; 311 | /* End PBXTargetDependency section */ 312 | 313 | /* Begin PBXVariantGroup section */ 314 | 4CB2B7922144BC9F00474F3B /* Main.storyboard */ = { 315 | isa = PBXVariantGroup; 316 | children = ( 317 | 4CB2B7932144BC9F00474F3B /* Base */, 318 | ); 319 | name = Main.storyboard; 320 | sourceTree = ""; 321 | }; 322 | 4CB2B7972144BCA000474F3B /* LaunchScreen.storyboard */ = { 323 | isa = PBXVariantGroup; 324 | children = ( 325 | 4CB2B7982144BCA000474F3B /* Base */, 326 | ); 327 | name = LaunchScreen.storyboard; 328 | sourceTree = ""; 329 | }; 330 | /* End PBXVariantGroup section */ 331 | 332 | /* Begin XCBuildConfiguration section */ 333 | 4CB2B7A82144BCA000474F3B /* Debug */ = { 334 | isa = XCBuildConfiguration; 335 | buildSettings = { 336 | ALWAYS_SEARCH_USER_PATHS = NO; 337 | CLANG_ANALYZER_NONNULL = YES; 338 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 339 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 340 | CLANG_CXX_LIBRARY = "libc++"; 341 | CLANG_ENABLE_MODULES = YES; 342 | CLANG_ENABLE_OBJC_ARC = YES; 343 | CLANG_ENABLE_OBJC_WEAK = YES; 344 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 345 | CLANG_WARN_BOOL_CONVERSION = YES; 346 | CLANG_WARN_COMMA = YES; 347 | CLANG_WARN_CONSTANT_CONVERSION = YES; 348 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 349 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 350 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 351 | CLANG_WARN_EMPTY_BODY = YES; 352 | CLANG_WARN_ENUM_CONVERSION = YES; 353 | CLANG_WARN_INFINITE_RECURSION = YES; 354 | CLANG_WARN_INT_CONVERSION = YES; 355 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 356 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 357 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 358 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 359 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 360 | CLANG_WARN_STRICT_PROTOTYPES = YES; 361 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 362 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 363 | CLANG_WARN_UNREACHABLE_CODE = YES; 364 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 365 | CODE_SIGN_IDENTITY = "iPhone Developer"; 366 | COPY_PHASE_STRIP = NO; 367 | DEBUG_INFORMATION_FORMAT = dwarf; 368 | ENABLE_STRICT_OBJC_MSGSEND = YES; 369 | ENABLE_TESTABILITY = YES; 370 | GCC_C_LANGUAGE_STANDARD = gnu11; 371 | GCC_DYNAMIC_NO_PIC = NO; 372 | GCC_NO_COMMON_BLOCKS = YES; 373 | GCC_OPTIMIZATION_LEVEL = 0; 374 | GCC_PREPROCESSOR_DEFINITIONS = ( 375 | "DEBUG=1", 376 | "$(inherited)", 377 | ); 378 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 379 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 380 | GCC_WARN_UNDECLARED_SELECTOR = YES; 381 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 382 | GCC_WARN_UNUSED_FUNCTION = YES; 383 | GCC_WARN_UNUSED_VARIABLE = YES; 384 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 385 | MTL_ENABLE_DEBUG_INFO = YES; 386 | ONLY_ACTIVE_ARCH = YES; 387 | SDKROOT = iphoneos; 388 | }; 389 | name = Debug; 390 | }; 391 | 4CB2B7A92144BCA000474F3B /* Release */ = { 392 | isa = XCBuildConfiguration; 393 | buildSettings = { 394 | ALWAYS_SEARCH_USER_PATHS = NO; 395 | CLANG_ANALYZER_NONNULL = YES; 396 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 397 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 398 | CLANG_CXX_LIBRARY = "libc++"; 399 | CLANG_ENABLE_MODULES = YES; 400 | CLANG_ENABLE_OBJC_ARC = YES; 401 | CLANG_ENABLE_OBJC_WEAK = YES; 402 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 403 | CLANG_WARN_BOOL_CONVERSION = YES; 404 | CLANG_WARN_COMMA = YES; 405 | CLANG_WARN_CONSTANT_CONVERSION = YES; 406 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 407 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 408 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 409 | CLANG_WARN_EMPTY_BODY = YES; 410 | CLANG_WARN_ENUM_CONVERSION = YES; 411 | CLANG_WARN_INFINITE_RECURSION = YES; 412 | CLANG_WARN_INT_CONVERSION = YES; 413 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 414 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 415 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 416 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 417 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 418 | CLANG_WARN_STRICT_PROTOTYPES = YES; 419 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 420 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 421 | CLANG_WARN_UNREACHABLE_CODE = YES; 422 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 423 | CODE_SIGN_IDENTITY = "iPhone Developer"; 424 | COPY_PHASE_STRIP = NO; 425 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 426 | ENABLE_NS_ASSERTIONS = NO; 427 | ENABLE_STRICT_OBJC_MSGSEND = YES; 428 | GCC_C_LANGUAGE_STANDARD = gnu11; 429 | GCC_NO_COMMON_BLOCKS = YES; 430 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 431 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 432 | GCC_WARN_UNDECLARED_SELECTOR = YES; 433 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 434 | GCC_WARN_UNUSED_FUNCTION = YES; 435 | GCC_WARN_UNUSED_VARIABLE = YES; 436 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 437 | MTL_ENABLE_DEBUG_INFO = NO; 438 | SDKROOT = iphoneos; 439 | VALIDATE_PRODUCT = YES; 440 | }; 441 | name = Release; 442 | }; 443 | 4CB2B7AB2144BCA000474F3B /* Debug */ = { 444 | isa = XCBuildConfiguration; 445 | buildSettings = { 446 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 447 | CODE_SIGN_IDENTITY = "iPhone Developer"; 448 | CODE_SIGN_STYLE = Automatic; 449 | DEVELOPMENT_TEAM = ""; 450 | INFOPLIST_FILE = OCMethodTraceDemo/Info.plist; 451 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 452 | LD_RUNPATH_SEARCH_PATHS = ( 453 | "$(inherited)", 454 | "@executable_path/Frameworks", 455 | ); 456 | PRODUCT_BUNDLE_IDENTIFIER = com.example.OCMethodTraceDemo; 457 | PRODUCT_NAME = "$(TARGET_NAME)"; 458 | PROVISIONING_PROFILE = ""; 459 | PROVISIONING_PROFILE_SPECIFIER = ""; 460 | TARGETED_DEVICE_FAMILY = "1,2"; 461 | }; 462 | name = Debug; 463 | }; 464 | 4CB2B7AC2144BCA000474F3B /* Release */ = { 465 | isa = XCBuildConfiguration; 466 | buildSettings = { 467 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 468 | CODE_SIGN_IDENTITY = "iPhone Developer"; 469 | CODE_SIGN_STYLE = Automatic; 470 | DEVELOPMENT_TEAM = ""; 471 | INFOPLIST_FILE = OCMethodTraceDemo/Info.plist; 472 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 473 | LD_RUNPATH_SEARCH_PATHS = ( 474 | "$(inherited)", 475 | "@executable_path/Frameworks", 476 | ); 477 | PRODUCT_BUNDLE_IDENTIFIER = com.example.OCMethodTraceDemo; 478 | PRODUCT_NAME = "$(TARGET_NAME)"; 479 | PROVISIONING_PROFILE_SPECIFIER = ""; 480 | TARGETED_DEVICE_FAMILY = "1,2"; 481 | }; 482 | name = Release; 483 | }; 484 | 4CB2B7AE2144BCA000474F3B /* Debug */ = { 485 | isa = XCBuildConfiguration; 486 | buildSettings = { 487 | BUNDLE_LOADER = "$(TEST_HOST)"; 488 | CODE_SIGN_IDENTITY = "iPhone Developer"; 489 | CODE_SIGN_STYLE = Automatic; 490 | DEVELOPMENT_TEAM = ""; 491 | INFOPLIST_FILE = OCMethodTraceDemoTests/Info.plist; 492 | LD_RUNPATH_SEARCH_PATHS = ( 493 | "$(inherited)", 494 | "@executable_path/Frameworks", 495 | "@loader_path/Frameworks", 496 | ); 497 | PRODUCT_BUNDLE_IDENTIFIER = com.example.OCMethodTraceDemoTests; 498 | PRODUCT_NAME = "$(TARGET_NAME)"; 499 | PROVISIONING_PROFILE_SPECIFIER = ""; 500 | TARGETED_DEVICE_FAMILY = "1,2"; 501 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OCMethodTraceDemo.app/OCMethodTraceDemo"; 502 | }; 503 | name = Debug; 504 | }; 505 | 4CB2B7AF2144BCA000474F3B /* Release */ = { 506 | isa = XCBuildConfiguration; 507 | buildSettings = { 508 | BUNDLE_LOADER = "$(TEST_HOST)"; 509 | CODE_SIGN_IDENTITY = "iPhone Developer"; 510 | CODE_SIGN_STYLE = Automatic; 511 | DEVELOPMENT_TEAM = ""; 512 | INFOPLIST_FILE = OCMethodTraceDemoTests/Info.plist; 513 | LD_RUNPATH_SEARCH_PATHS = ( 514 | "$(inherited)", 515 | "@executable_path/Frameworks", 516 | "@loader_path/Frameworks", 517 | ); 518 | PRODUCT_BUNDLE_IDENTIFIER = com.example.OCMethodTraceDemoTests; 519 | PRODUCT_NAME = "$(TARGET_NAME)"; 520 | PROVISIONING_PROFILE_SPECIFIER = ""; 521 | TARGETED_DEVICE_FAMILY = "1,2"; 522 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/OCMethodTraceDemo.app/OCMethodTraceDemo"; 523 | }; 524 | name = Release; 525 | }; 526 | /* End XCBuildConfiguration section */ 527 | 528 | /* Begin XCConfigurationList section */ 529 | 4CB2B7842144BC9F00474F3B /* Build configuration list for PBXProject "OCMethodTraceDemo" */ = { 530 | isa = XCConfigurationList; 531 | buildConfigurations = ( 532 | 4CB2B7A82144BCA000474F3B /* Debug */, 533 | 4CB2B7A92144BCA000474F3B /* Release */, 534 | ); 535 | defaultConfigurationIsVisible = 0; 536 | defaultConfigurationName = Release; 537 | }; 538 | 4CB2B7AA2144BCA000474F3B /* Build configuration list for PBXNativeTarget "OCMethodTraceDemo" */ = { 539 | isa = XCConfigurationList; 540 | buildConfigurations = ( 541 | 4CB2B7AB2144BCA000474F3B /* Debug */, 542 | 4CB2B7AC2144BCA000474F3B /* Release */, 543 | ); 544 | defaultConfigurationIsVisible = 0; 545 | defaultConfigurationName = Release; 546 | }; 547 | 4CB2B7AD2144BCA000474F3B /* Build configuration list for PBXNativeTarget "OCMethodTraceDemoTests" */ = { 548 | isa = XCConfigurationList; 549 | buildConfigurations = ( 550 | 4CB2B7AE2144BCA000474F3B /* Debug */, 551 | 4CB2B7AF2144BCA000474F3B /* Release */, 552 | ); 553 | defaultConfigurationIsVisible = 0; 554 | defaultConfigurationName = Release; 555 | }; 556 | /* End XCConfigurationList section */ 557 | }; 558 | rootObject = 4CB2B7812144BC9F00474F3B /* Project object */; 559 | } 560 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/Animal.h: -------------------------------------------------------------------------------- 1 | // 2 | // Animal.h 3 | // OCMethodTraceDemo 4 | // 5 | // Created by Michael Chen on 2018/9/9. 6 | // Copyright (C) 2018 Michael Chen 7 | // 8 | 9 | #import 10 | #import "TestUtils.h" 11 | 12 | @interface Animal : NSObject 13 | 14 | @property (nonatomic, strong) NSString *name; 15 | @property (nonatomic, assign) NSUInteger age; 16 | 17 | + (void)setLogLevel:(int)level; 18 | 19 | - (void)setName:(NSString *)name age:(NSUInteger)age; 20 | - (NSString *)getName; 21 | - (NSUInteger)getAge; 22 | 23 | - (ObjStruct1)setObjStruct1:(char)a; 24 | - (ObjStruct4)setObjStruct4:(int32_t)a; 25 | - (ObjStruct8)setObjStruct8:(int64_t)a; 26 | - (ObjStruct16)setObjStruct16:(int64_t)a b:(int64_t)b; 27 | - (ObjStruct24)setObjStruct24:(int64_t)a b:(int64_t)b c:(int64_t)c; 28 | - (ObjStruct32)setObjStruct32:(int64_t)a b:(int64_t)b c:(int64_t)c d:(int64_t)d; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/Animal.m: -------------------------------------------------------------------------------- 1 | // 2 | // Animal.m 3 | // OCMethodTraceDemo 4 | // 5 | // Created by Michael Chen on 2018/9/9. 6 | // Copyright (C) 2018 Michael Chen 7 | // 8 | 9 | #import "Animal.h" 10 | 11 | @implementation Animal 12 | 13 | + (void)setLogLevel:(int)level 14 | { 15 | TestLog(@"level: %d", level); 16 | } 17 | 18 | - (void)setName:(NSString *)name 19 | { 20 | TestLog(@"name: %@", name); 21 | _name = name; 22 | } 23 | 24 | - (NSString *)getName 25 | { 26 | TestLog(@""); 27 | return _name; 28 | } 29 | 30 | - (void)setAge:(NSUInteger)age 31 | { 32 | TestLog(@"age: %tu", age); 33 | _age = age; 34 | } 35 | 36 | - (NSUInteger)getAge 37 | { 38 | TestLog(@""); 39 | return _age; 40 | } 41 | 42 | - (void)setName:(NSString *)name age:(NSUInteger)age 43 | { 44 | TestLog(@"name: %@ age: %tu", name, age); 45 | _name = name; 46 | _age = age; 47 | } 48 | 49 | - (ObjStruct1)setObjStruct1:(char)a 50 | { 51 | TestLog(@"setObjStruct1: a: %c", a); 52 | 53 | ObjStruct1 st; 54 | st.a = a; 55 | return st; 56 | } 57 | 58 | - (ObjStruct4)setObjStruct4:(int32_t)a 59 | { 60 | TestLog(@"setObjStruct4: a: %d", a); 61 | 62 | ObjStruct4 st; 63 | st.a = a; 64 | return st; 65 | } 66 | 67 | - (ObjStruct8)setObjStruct8:(int64_t)a 68 | { 69 | TestLog(@"setObjStruct8: a: %lld", a); 70 | 71 | ObjStruct8 st; 72 | st.a = a; 73 | return st; 74 | } 75 | 76 | - (ObjStruct16)setObjStruct16:(int64_t)a b:(int64_t)b 77 | { 78 | TestLog(@"setObjStruct16: a: %lld b: %lld", a, b); 79 | 80 | ObjStruct16 st; 81 | st.a = a; 82 | st.b = b; 83 | return st; 84 | } 85 | 86 | - (ObjStruct24)setObjStruct24:(int64_t)a b:(int64_t)b c:(int64_t)c 87 | { 88 | TestLog(@"setObjStruct24: a: %lld b: %lld c: %lld", a, b, c); 89 | 90 | ObjStruct24 st; 91 | st.a = a; 92 | st.b = b; 93 | st.c = c; 94 | return st; 95 | } 96 | 97 | - (ObjStruct32)setObjStruct32:(int64_t)a b:(int64_t)b c:(int64_t)c d:(int64_t)d 98 | { 99 | TestLog(@"setObjStruct32: a: %lld b: %lld c: %lld d: %lld", a, b, c, d); 100 | 101 | ObjStruct32 st; 102 | st.a = a; 103 | st.b = b; 104 | st.c = c; 105 | st.d = d; 106 | return st; 107 | } 108 | 109 | //// 问题:如果description方法里内调用已经被trace的方法,MDMethodTrace before和after输出日志时一旦调用description方法就容易出现递归死循环 110 | //// 解决:在recursiveCallExistsAtFuncArray中判断递归,避开再次调用before和after 111 | //- (NSString *)description 112 | //{ 113 | // return [NSString stringWithFormat:@"<%@: %p name: %@ age: %tu>", 114 | // NSStringFromClass([self class]), self, [self getName], self.age]; 115 | //} 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // OCMethodTraceDemo 4 | // 5 | // Created by Michael Chen on 2018/9/9. 6 | // Copyright (C) 2018 Michael Chen 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // OCMethodTraceDemo 4 | // 5 | // Created by Michael Chen on 2018/9/9. 6 | // Copyright (C) 2018 Michael Chen 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application { 25 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 26 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 27 | } 28 | 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application { 31 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 32 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 33 | } 34 | 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application { 37 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 38 | } 39 | 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application { 42 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 43 | } 44 | 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application { 47 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 48 | } 49 | 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/MDConfig.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MethodTrace 6 | 7 | LogLevel 8 | 0 9 | LogWhen 10 | 0 11 | LogRegexString 12 | Tiger 13 | TraceFlag 14 | 2 15 | TraceObject 16 | 2 17 | ClassRegexString 18 | 19 | CORE_CLASS_LIST 20 | 21 | MDConfigManager 22 | 23 | MDMethodTrace 24 | 25 | MDTraceClassInfo 26 | 27 | FastCoder 28 | 29 | NSManagedObject 30 | 31 | NSObject 32 | 33 | NSException 34 | 35 | NSTypesetter 36 | 37 | NSString 38 | 39 | NSCache 40 | 41 | NSPlaceholderString 42 | 43 | __NSCFString 44 | 45 | NSURL 46 | 47 | TraceMode 48 | 0 49 | MethodBlackList 50 | 51 | isKindOfClass: 52 | 53 | 54 | NSFont 55 | 56 | OS_object 57 | 58 | OS_xpc_object 59 | 60 | OS_dispatch_object 61 | 62 | OS_dispatch_queue 63 | 64 | NSSet 65 | 66 | NSOrderedSet 67 | 68 | __NSPlaceholderSet 69 | 70 | __NSPlaceholderOrderedSet 71 | 72 | NSMutableOrderedSet 73 | 74 | NSDictionary 75 | 76 | NSMutableDictionary 77 | 78 | __NSCFDictionary 79 | 80 | __NSDictionaryM 81 | 82 | __NSPlaceholderDictionary 83 | 84 | NSArray 85 | 86 | NSMutableArray 87 | 88 | __NSCFArray 89 | 90 | __NSArrayM 91 | 92 | __NSArrayI 93 | 94 | __NSPlaceholderArray 95 | 96 | NSDate 97 | 98 | __NSDate 99 | 100 | __NSPlaceholderDate 101 | 102 | NSNumber 103 | 104 | NSPlaceholderNumber 105 | 106 | NSPredicate 107 | 108 | NSRecursiveLock 109 | 110 | NSConcreteMapTable 111 | 112 | _UIPointVector 113 | 114 | NSConcreteHashTable 115 | 116 | FBSWorkspace 117 | 118 | FBSSerialQueue 119 | 120 | BSDescriptionBuilder 121 | 122 | FBSSceneImpl 123 | 124 | BSSettings 125 | 126 | FBSWorkspaceClient 127 | 128 | AVAudioSession 129 | 130 | 131 | USER_CLASS_LIST 132 | 133 | Animal 134 | 135 | TraceMode 136 | 1 137 | TraceFlag 138 | 0 139 | MethodWhiteList 140 | 141 | MethodBlackList 142 | 143 | 144 | Tiger 145 | 146 | TraceMode 147 | 1 148 | TraceFlag 149 | 0 150 | MethodWhiteList 151 | 152 | MethodBlackList 153 | 154 | 155 | 156 | 157 | Cycript 158 | 159 | nslog 160 | 161 | LoadAtLaunch 162 | 163 | priority 164 | 0 165 | content 166 | NSLog = function() { var types = 'v', args = [], count = arguments.length; for (var i = 0; i != count; ++i) { types += '@'; args.push(arguments[i]); } new Functor(dlsym(RTLD_DEFAULT, "NSLog"), types).apply(null, args); } 167 | 168 | ms 169 | 170 | LoadAtLaunch 171 | 172 | priority 173 | 1 174 | url 175 | https://raw.githubusercontent.com/AloneMonkey/MDCycript/master/MS.cy 176 | 177 | hook 178 | 179 | LoadAtLaunch 180 | 181 | priority 182 | 2 183 | content 184 | try{ 185 | var oldm = {}; 186 | HookMessage(CustomViewController, @selector(showChangeLog:), function(log) { 187 | NSLog("hooked by cycript!!!"); 188 | return oldm->call(this,log); 189 | }, oldm); 190 | }catch(err){ 191 | NSLog(err.toString()) 192 | } 193 | 194 | md 195 | 196 | LoadAtLaunch 197 | 198 | priority 199 | 3 200 | url 201 | https://raw.githubusercontent.com/AloneMonkey/MDCycript/master/md.cy 202 | 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/MDConfigManager.h: -------------------------------------------------------------------------------- 1 | // weibo: http://weibo.com/xiaoqing28 2 | // blog: http://www.alonemonkey.com 3 | // 4 | // MDConfigManager.h 5 | // MonkeyDev 6 | // 7 | // Created by AloneMonkey on 2018/4/24. 8 | // Copyright © 2018年 AloneMonkey. All rights reserved. 9 | // 10 | 11 | #import 12 | 13 | #define MDCONFIG_CYCRIPT_KEY @"Cycript" 14 | #define MDCONFIG_LOADATLAUNCH_KEY @"LoadAtLaunch" 15 | 16 | #define MDCONFIG_TRACE_KEY @"MethodTrace" 17 | #define MDCONFIG_LOG_LEVEL_KEY @"LogLevel" 18 | #define MDCONFIG_LOG_WHEN_KEY @"LogWhen" 19 | #define MDCONFIG_LOG_REGEX_STRING_KEY @"LogRegexString" // 仅LogWhen=MDTraceLogWhenRegexString有效 20 | #define MDCONFIG_TRACE_FLAG_KEY @"TraceFlag" 21 | #define MDCONFIG_TRACE_OBJECT_KEY @"TraceObject" 22 | #define MDCONFIG_CLASS_REGEX_STRING_KEY @"ClassRegexString" // 仅TraceObject=MDTraceObjectRegexClass有效 23 | #define MDCONFIG_CORE_CLASS_LIST @"CORE_CLASS_LIST" 24 | #define MDCONFIG_USER_CLASS_LIST @"USER_CLASS_LIST" 25 | #define MDCONFIG_TRACE_MODE_KEY @"TraceMode" 26 | #define MDCONFIG_METHOD_WHITE_LIST_KEY @"MethodWhiteList" 27 | #define MDCONFIG_METHOD_BLACK_LIST_KEY @"MethodBlackList" 28 | 29 | // Trace日志级别 30 | typedef NS_ENUM(NSUInteger, MDTraceLogLevel) { 31 | MDTraceLogLeveError = 0, // 错误 32 | MDTraceLogLeveDebug = 1, // 调试 33 | MDTraceLogLeveMax 34 | }; 35 | 36 | // Trace日志输出时机 37 | typedef NS_ENUM(NSUInteger, MDTraceLogWhen) { 38 | MDTraceLogWhenStartup = 0, // 启动即输出日志 39 | MDTraceLogWhenVolume = 1, // 根据音量键控制输出日志(增加音量:输出日志;降低音量:关闭日志;默认时关闭日志) 40 | MDTraceLogWhenRegexString = 2, // 日志包含指定正则字符串才输出日志 41 | MDTraceLogWhenMax 42 | }; 43 | 44 | // Trace控制位(尽量在该处扩展) 45 | typedef NS_OPTIONS(NSUInteger, MDTraceFlag) { 46 | MDTraceFlagDoesNotUseDescription = 1 << 0, // 跳过调用对象description方法,避免不正规的description实现导致递归 47 | MDTraceFlagDumpClassListInfo = 1 << 1, // 打印类列表信息,便于调试 48 | MDTraceFlagDumpClassMethod = 1 << 2, // 打印某个类的方法(不包括父类方法),便于调试 49 | MDTraceFlagDumpSuperClassMethod = 1 << 3, // 打印某个类的父类方法(包括递归父类的方法),便于调试 50 | MDTraceFlagMask = 0xF, 51 | 52 | MDTraceFlagDefault = 0, 53 | }; 54 | 55 | // Trace对象 56 | typedef NS_ENUM(NSUInteger, MDTraceObject) { 57 | // 屏蔽trace所有类 58 | MDTraceObjectNone = 0, 59 | // trace引擎指定类的方法(仅测试验证使用),仅需要考虑CORE_CLASS_LIST 60 | MDTraceObjectCoreClass = 1, 61 | // trace用户指定类的方法,需要考虑USER_CLASS_LIST + "USER_CLASS_LIST和CORE_CLASS_LIST交集" 62 | MDTraceObjectUserClass = 2, 63 | // trace用户指定类 + 正则匹配类的方法,需要考虑USER_CLASS_LIST + "USER_CLASS_LIST和CORE_CLASS_LIST交集" + 64 | // "匹配ClassRegexString的CLASS_LIST和CORE_CLASS_LIST交集" 65 | MDTraceObjectRegexClass = 3, 66 | 67 | MDTraceObjectMax 68 | }; 69 | 70 | // Trace模式 71 | typedef NS_ENUM(NSUInteger, MDTraceMode) { 72 | MDTraceModeOff = 0, // 屏蔽trace方法 73 | MDTraceModeAll = 1, // trace所有方法 74 | MDTraceModeIncludeWhiteList = 2, // trace包含"白名单方法列表"的方法 75 | MDTraceModeExcludeBlackList = 3, // trace排除"黑名单方法列表"的方法 76 | MDTraceModeMax 77 | }; 78 | 79 | @interface MDConfigManager : NSObject 80 | 81 | + (instancetype)sharedInstance; 82 | 83 | - (NSDictionary*)readConfigByKey:(NSString*) key; 84 | 85 | @end 86 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/MDConfigManager.m: -------------------------------------------------------------------------------- 1 | // weibo: http://weibo.com/xiaoqing28 2 | // blog: http://www.alonemonkey.com 3 | // 4 | // MDConfigManager.m 5 | // MonkeyDev 6 | // 7 | // Created by AloneMonkey on 2018/4/24. 8 | // Copyright © 2018年 AloneMonkey. All rights reserved. 9 | // 10 | 11 | #define CONFIG_FILE_NAME @"MDConfig" 12 | 13 | #import "MDConfigManager.h" 14 | 15 | @implementation MDConfigManager{ 16 | NSString* _filepath; 17 | } 18 | 19 | + (instancetype)sharedInstance{ 20 | static MDConfigManager *sharedInstance = nil; 21 | if (!sharedInstance){ 22 | sharedInstance = [[MDConfigManager alloc] init]; 23 | } 24 | return sharedInstance; 25 | } 26 | 27 | - (BOOL)isActive{ 28 | _filepath = [[NSBundle mainBundle] pathForResource:CONFIG_FILE_NAME ofType:@"plist"]; 29 | if(_filepath == nil){ 30 | return NO; 31 | } 32 | return YES; 33 | } 34 | 35 | - (NSDictionary*) readConfigByKey:(NSString*) key{ 36 | if([self isActive]){ 37 | NSDictionary* contentDict = [NSDictionary dictionaryWithContentsOfFile:_filepath]; 38 | if([contentDict.allKeys containsObject:key]){ 39 | return contentDict[key]; 40 | }else{ 41 | return nil; 42 | } 43 | }else{ 44 | return nil; 45 | } 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/MDMethodTrace.h: -------------------------------------------------------------------------------- 1 | // weibo: http://weibo.com/xiaoqing28 2 | // blog: http://www.alonemonkey.com 3 | // 4 | // MDMethodTrace.h 5 | // MonkeyDev 6 | // 7 | // Created by AloneMonkey on 2017/9/7. 8 | // Copyright © 2017年 AloneMonkey. All rights reserved. 9 | // 10 | 11 | #ifndef MethodTrace_h 12 | #define MethodTrace_h 13 | 14 | #define TRACE_README @"\n📚--------------------OCMethodTrace(Usage)-------------------📚\nhttps://github.com/omxcodec/OCMethodTrace/blob/master/README.md\n📚--------------------OCMethodTrace(Usage)-------------------📚" 15 | 16 | #import 17 | 18 | @interface MDMethodTrace : NSObject 19 | 20 | + (instancetype)sharedInstance; 21 | 22 | - (void)addClassTrace:(NSString *)className; 23 | 24 | - (void)addClassTrace:(NSString *)className methodName:(NSString *)methodName; 25 | 26 | - (void)addClassTrace:(NSString *)className methodList:(NSArray *)methodList; 27 | 28 | @end 29 | 30 | #endif /* MethodTrace_h */ 31 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/MDMethodTrace.m: -------------------------------------------------------------------------------- 1 | // weibo: http://weibo.com/xiaoqing28 2 | // blog: http://www.alonemonkey.com 3 | // 4 | // MDMethodTrace.m 5 | // MonkeyDev 6 | // 7 | // Created by AloneMonkey on 2017/9/6. 8 | // Copyright © 2017年 AloneMonkey. All rights reserved. 9 | // 10 | 11 | #import "MDMethodTrace.h" 12 | #import "MDConfigManager.h" 13 | #import "OCMethodTrace.h" 14 | 15 | #import 16 | #import 17 | 18 | //#define USE_DDLOG // 使用外部日志系统,日志多的时候特别有用 19 | #ifdef USE_DDLOG 20 | #import "CocoaLumberjack.h" 21 | static const int ddLogLevel = DDLogLevelVerbose; 22 | #define MDLog(fmt, ...) DDLogDebug((@"[MethodTrace] " fmt), ##__VA_ARGS__) 23 | #else 24 | #define MDLog(fmt, ...) NSLog(@"[MethodTrace] %s", [[NSString stringWithFormat:fmt, ##__VA_ARGS__] UTF8String]); 25 | #endif 26 | 27 | #define SAFE_CHECK(_object, _type) (_type *)[[self class] safeCheck:_object class:[_type class]] 28 | #define MDFatal(fmt, ...) [NSException raise:@"MDMethodTrace" format:fmt, ##__VA_ARGS__] 29 | 30 | // 指定ClassInfo源 31 | typedef NS_ENUM(NSUInteger, MDTraceSource) { 32 | MDTraceSourceCore = 0, // 引擎指定(引擎指OCMethodTrace内部实现框架) 33 | MDTraceSourceUser = 1, // 用户指定 34 | MDTraceSourceMerge = 2, // 引擎和用户合并 35 | MDTraceSourceMax 36 | }; 37 | 38 | //////////////////////////////////////////////////////////////////////////////// 39 | #pragma mark - Class Define 40 | 41 | @interface MDTraceClassInfo : NSObject 42 | 43 | @property (nonatomic, strong) NSString *name; // 类名 44 | @property (nonatomic, assign) MDTraceSource source; // 指定源 45 | @property (nonatomic, assign) MDTraceMode mode; // trace模式 46 | @property (nonatomic, assign) MDTraceFlag flag; // flag 47 | @property (nonatomic, strong) NSMutableArray *methodList; // 黑名单或者白名单,根据mode决定 48 | 49 | // 解析类配置,无key则加key,设置默认值,并校验值类型 50 | + (instancetype)infoWithName:(NSString *)name source:(MDTraceSource)source config:(NSDictionary *)config; 51 | // 根据类型返回引擎指定的默认ClassInfo 52 | + (instancetype)defaultCoreClassInfo:(NSString *)name; 53 | // 根据类型返回用户指定的默认ClassInfo 54 | + (instancetype)defaultUserClassInfo:(NSString *)name; 55 | // 安全检查 56 | + (id)safeCheck:(id)content class:(Class)cls; 57 | // 数组相减: arrayA - arrayB 58 | + (NSArray *)minusWithArrayA:(NSArray *)arrayA arrayB:(NSArray *)arrayB; 59 | // 数组相加: arrayA + arrayB,可去重,相同元素只保留一个 60 | + (NSArray *)unionWithArrayA:(NSArray *)arrayA arrayB:(NSArray *)arrayB; 61 | // 合并引擎和用户ClassInfo,合并时core的优先级比user大,具体见函数内部实现 62 | + (MDTraceClassInfo *)mergeInfoWithCoreInfo:(MDTraceClassInfo *)coreInfo userInfo:(MDTraceClassInfo *)userInfo; 63 | // 合并引擎和用户config 64 | + (MDTraceClassInfo *)mergeInfoWithName:(NSString *)name coreConfig:(NSDictionary *)coreConfig userConfig:(NSDictionary *)userConfig; 65 | 66 | @end 67 | 68 | @interface MDMethodTrace () 69 | 70 | @property (nonatomic, strong) NSMutableDictionary *config; 71 | @property (nonatomic, assign) MDTraceLogLevel logLevel; // 日志级别 72 | @property (nonatomic, assign) MDTraceLogWhen logWhen; // 日志输出时机 73 | @property (nonatomic, strong) NSString *logRegexString; // 日志正则匹配字符串,仅当logWhen=MDTraceLogWhenRegexString有效 74 | @property (nonatomic, assign) NSInteger numberOfPendingLog; // logRegexString匹配后,待输出afer日志个数 75 | @property (nonatomic, assign) CGFloat lastSystemVolume; // 上一次系统音量 76 | @property (nonatomic, assign) MDTraceFlag traceFlag; // 控制trace行为的一些特殊flag 77 | @property (nonatomic, assign) MDTraceObject traceObject; // trace对象 78 | @property (nonatomic, strong) NSString *classRegexString; // 类正则匹配字符串 79 | @property (nonatomic, strong) NSMutableArray *coreClassInfoList; // 引擎类信息列表 80 | @property (nonatomic, strong) NSMutableArray *userClassInfoList; // 用户类信息列表 81 | @property (nonatomic, strong) NSMutableArray *regexClassInfoList; // 正则类信息列表 82 | 83 | // 解析配置文件 84 | - (void)parseConfig:(NSDictionary *)config; 85 | 86 | @end 87 | 88 | //////////////////////////////////////////////////////////////////////////////// 89 | #pragma mark - MDTraceClassInfo 90 | 91 | @implementation MDTraceClassInfo 92 | 93 | - (instancetype)init 94 | { 95 | self = [super init]; 96 | if (self) { 97 | self.name = @"NoName"; 98 | self.source = MDTraceSourceUser; 99 | self.mode = MDTraceModeAll; 100 | self.flag = MDTraceFlagDefault; 101 | self.methodList = [NSMutableArray array]; 102 | } 103 | return self; 104 | } 105 | 106 | - (NSString *)description 107 | { 108 | return [NSString stringWithFormat:@"<%@: %p name: %@ source: %tu mode: %tu flag: %tu methodList: %@>", 109 | NSStringFromClass([self class]), self, _name, _source, _mode, _flag, _methodList]; 110 | } 111 | 112 | + (instancetype)infoWithName:(NSString *)name source:(MDTraceSource)source config:(NSDictionary *)config 113 | { 114 | // 无对应字典说明用户没有指定类,不处理 115 | if (nil == config) { 116 | return nil; 117 | } 118 | 119 | MDTraceClassInfo *info = [[MDTraceClassInfo alloc] init]; 120 | info.name = name; 121 | info.source = source; 122 | // 1 mode 123 | info.mode = info.source == MDTraceSourceCore ? MDTraceModeOff: MDTraceModeAll; 124 | if (![config valueForKey:MDCONFIG_TRACE_MODE_KEY]) { 125 | // MDLog(@"Cannot find class %@ %@, set it to default %d", info.name, MDCONFIG_TRACE_MODE_KEY, (int)info.mode); 126 | } else { 127 | info.mode = [SAFE_CHECK(config[MDCONFIG_TRACE_MODE_KEY], NSNumber) integerValue]; 128 | } 129 | 130 | // 2 flag 131 | info.flag = MDTraceFlagDefault; 132 | if (![config valueForKey:MDCONFIG_TRACE_FLAG_KEY]) { 133 | // MDLog(@"Cannot find class %@ %@, set it to default %d", info.name, MDCONFIG_TRACE_FLAG_KEY, (int)info.flag); 134 | } else { 135 | info.flag = [SAFE_CHECK(config[MDCONFIG_TRACE_FLAG_KEY], NSNumber) integerValue]; 136 | } 137 | 138 | // 3 whiteList 139 | if ([config valueForKey:MDCONFIG_METHOD_WHITE_LIST_KEY]) { 140 | id methodList = config[MDCONFIG_METHOD_WHITE_LIST_KEY]; 141 | if (![methodList isKindOfClass:[NSArray class]]) { 142 | MDFatal(@"Class %@ %@ should be array", info.name, MDCONFIG_METHOD_WHITE_LIST_KEY); 143 | } else if (info.mode == MDTraceModeIncludeWhiteList) { 144 | info.methodList = methodList; 145 | } 146 | } 147 | 148 | // 4 blackList 149 | if ([config valueForKey:MDCONFIG_METHOD_BLACK_LIST_KEY]) { 150 | id methodList = config[MDCONFIG_METHOD_BLACK_LIST_KEY]; 151 | if (![methodList isKindOfClass:[NSArray class]]) { 152 | MDFatal(@"Class %@ %@ should be array", info.name, MDCONFIG_METHOD_BLACK_LIST_KEY); 153 | } else if (info.mode == MDTraceModeExcludeBlackList) { 154 | info.methodList = methodList; 155 | } 156 | } 157 | 158 | return info; 159 | } 160 | 161 | + (instancetype)defaultCoreClassInfo:(NSString *)name 162 | { 163 | MDTraceClassInfo *info = [[MDTraceClassInfo alloc] init]; 164 | info.name = name; 165 | info.source = MDTraceSourceCore; 166 | info.mode = MDTraceModeOff; 167 | return info; 168 | } 169 | 170 | + (instancetype)defaultUserClassInfo:(NSString *)name 171 | { 172 | MDTraceClassInfo *info = [[MDTraceClassInfo alloc] init]; 173 | info.name = name; 174 | info.source = MDTraceSourceUser; 175 | info.mode = MDTraceModeAll; 176 | return info; 177 | } 178 | 179 | + (id)safeCheck:(id)content class:(Class)cls 180 | { 181 | if ([content isKindOfClass:cls]) { 182 | return content; 183 | } 184 | MDFatal(@"safeCheck failed, content(%@) should be class(%@) type", content, NSStringFromClass(cls)); 185 | return nil; 186 | } 187 | 188 | + (NSArray *)minusWithArrayA:(NSArray *)arrayA arrayB:(NSArray *)arrayB 189 | { 190 | NSMutableOrderedSet *setA = [NSMutableOrderedSet orderedSetWithArray:arrayA]; 191 | NSMutableOrderedSet *setB = [NSMutableOrderedSet orderedSetWithArray:arrayB]; 192 | [setA minusOrderedSet:setB]; 193 | return [setA array]; 194 | } 195 | 196 | + (NSArray *)unionWithArrayA:(NSArray *)arrayA arrayB:(NSArray *)arrayB 197 | { 198 | NSMutableOrderedSet *setA = [NSMutableOrderedSet orderedSetWithArray:arrayA]; 199 | NSMutableOrderedSet *setB = [NSMutableOrderedSet orderedSetWithArray:arrayB]; 200 | [setA unionOrderedSet:setB]; 201 | return [setA array]; 202 | } 203 | 204 | - (void)setSource:(MDTraceSource)source 205 | { 206 | NSAssert(source >= MDTraceSourceCore && source < MDTraceSourceMax, @"invalid TraceSource"); 207 | _source = source; 208 | } 209 | 210 | - (void)setMode:(MDTraceMode)mode 211 | { 212 | NSAssert(mode >= MDTraceModeOff && mode < MDTraceModeMax, @"invalid TraceMode"); 213 | _mode = mode; 214 | } 215 | 216 | - (void)setFlag:(MDTraceFlag)flag 217 | { 218 | NSAssert(flag >= MDTraceModeOff && flag <= MDTraceFlagMask, @"invalid TraceFlag"); 219 | _flag = flag; 220 | } 221 | 222 | - (id)copyWithZone:(NSZone *)zone 223 | { 224 | MDTraceClassInfo *info = [[self.class allocWithZone:zone] init]; 225 | 226 | info.name = self.name; 227 | info.source = self.source; 228 | info.mode = self.mode; 229 | info.flag = self.flag; 230 | info.methodList = [NSMutableArray arrayWithArray:self.methodList]; 231 | 232 | return info; 233 | } 234 | 235 | + (MDTraceClassInfo *)mergeInfoWithCoreInfo:(MDTraceClassInfo *)coreInfo userInfo:(MDTraceClassInfo *)userInfo 236 | { 237 | if (nil == coreInfo && nil == userInfo) { 238 | NSAssert(0, @"invalid info"); 239 | return nil; 240 | } 241 | 242 | MDTraceClassInfo *mergeInfo = nil; 243 | if (nil != coreInfo && nil == userInfo) { 244 | NSAssert(coreInfo.source == MDTraceSourceCore, @"invalid core source"); 245 | mergeInfo = [coreInfo copy]; 246 | } else if (nil == coreInfo && nil != userInfo) { 247 | NSAssert(userInfo.source == MDTraceSourceUser, @"invalid user source"); 248 | mergeInfo = [userInfo copy]; 249 | } else { 250 | NSAssert([coreInfo.name isEqualToString:userInfo.name], @"invalid name"); 251 | NSAssert(coreInfo.source == MDTraceSourceCore, @"invalid core source"); 252 | NSAssert(coreInfo.mode == MDTraceModeOff || coreInfo.mode == MDTraceModeExcludeBlackList, @"invalid core mode"); 253 | NSAssert(userInfo.source == MDTraceSourceUser, @"invalid user source"); 254 | 255 | mergeInfo = [[MDTraceClassInfo alloc] init]; 256 | mergeInfo.name = coreInfo.name; 257 | mergeInfo.source = MDTraceSourceMerge; 258 | mergeInfo.flag = coreInfo.flag | userInfo.flag; 259 | // core优先级比user优先级高 260 | if (coreInfo.mode == MDTraceModeOff) { 261 | // core关闭则无需关心user配置 262 | mergeInfo.mode = MDTraceModeOff; 263 | } else if (coreInfo.mode == MDTraceModeExcludeBlackList) { 264 | switch (userInfo.mode) { 265 | case MDTraceModeOff: 266 | // user关闭就不trace这个类 267 | mergeInfo.mode = MDTraceModeOff; 268 | break; 269 | case MDTraceModeAll: 270 | // 排除core指定的黑名单 271 | mergeInfo.mode = MDTraceModeExcludeBlackList; 272 | [mergeInfo.methodList addObjectsFromArray:coreInfo.methodList]; 273 | break; 274 | case MDTraceModeIncludeWhiteList: 275 | // user白名单排除掉core黑名单 276 | mergeInfo.mode = MDTraceModeIncludeWhiteList; 277 | [mergeInfo.methodList addObjectsFromArray:[[self class] minusWithArrayA:userInfo.methodList arrayB:coreInfo.methodList]]; 278 | break; 279 | case MDTraceModeExcludeBlackList: 280 | // user黑名单加上core黑名单 281 | mergeInfo.mode = MDTraceModeExcludeBlackList; 282 | [mergeInfo.methodList addObjectsFromArray:[[self class] unionWithArrayA:userInfo.methodList arrayB:coreInfo.methodList]]; 283 | break; 284 | default: 285 | break; 286 | } 287 | } 288 | } 289 | 290 | return mergeInfo; 291 | } 292 | 293 | + (MDTraceClassInfo *)mergeInfoWithName:(NSString *)name coreConfig:(NSDictionary *)coreConfig userConfig:(NSDictionary *)userConfig; 294 | { 295 | MDTraceClassInfo *coreInfo = [MDTraceClassInfo infoWithName:name source:MDTraceSourceCore config:coreConfig]; 296 | MDTraceClassInfo *userInfo = [MDTraceClassInfo infoWithName:name source:MDTraceSourceUser config:userConfig]; 297 | return [MDTraceClassInfo mergeInfoWithCoreInfo:coreInfo userInfo:userInfo]; 298 | } 299 | 300 | @end 301 | 302 | //////////////////////////////////////////////////////////////////////////////// 303 | #pragma mark - MDMethodTrace 304 | 305 | @implementation MDMethodTrace 306 | 307 | + (instancetype)sharedInstance { 308 | static MDMethodTrace *sharedInstance = nil; 309 | static dispatch_once_t onceToken; 310 | dispatch_once(&onceToken, ^{ 311 | sharedInstance = [[MDMethodTrace alloc] init]; 312 | }); 313 | return sharedInstance; 314 | } 315 | 316 | - (instancetype)init 317 | { 318 | self = [super init]; 319 | if (self) { 320 | self.logLevel = MDTraceLogLeveDebug; 321 | self.logWhen = MDTraceLogWhenStartup; 322 | self.traceFlag = MDTraceFlagDefault; 323 | self.traceObject = MDTraceObjectNone; 324 | self.coreClassInfoList = [[NSMutableArray alloc] init]; 325 | self.userClassInfoList = [[NSMutableArray alloc] init]; 326 | self.regexClassInfoList = [[NSMutableArray alloc] init]; 327 | } 328 | return self; 329 | } 330 | 331 | - (void)dealloc 332 | { 333 | #if !TARGET_OS_SIMULATOR 334 | [self removeVolumeObserver]; 335 | #endif 336 | } 337 | 338 | #pragma mark - Trace utils 339 | 340 | // 安全检查 341 | + (id)safeCheck:(id)content class:(Class)cls 342 | { 343 | if ([content isKindOfClass:cls]) { 344 | return content; 345 | } 346 | MDFatal(@"safeCheck failed, content(%@) should be class(%@) type", content, NSStringFromClass(cls)); 347 | return nil; 348 | } 349 | 350 | // 正则匹配 optionss是否需要更多参数??? 351 | // FIXME -[NSString rangeOfString:options:NSRegularExpressionSearch]在实际运行中会死循环,不太明白原因 352 | + (BOOL)isMatchRegexString:(NSString *)regexString inputString:(NSString *)inputString 353 | { 354 | NSError *error = NULL; 355 | NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString options:NSRegularExpressionDotMatchesLineSeparators | NSRegularExpressionAnchorsMatchLines error:&error]; 356 | NSTextCheckingResult *result = [regex firstMatchInString:inputString options:0 range:NSMakeRange(0, [inputString length])]; 357 | return nil != result; 358 | } 359 | 360 | // 获取object名称 361 | + (NSString *)NSStringFromTraceObject:(MDTraceObject)traceObject 362 | { 363 | NSString *name; 364 | switch (traceObject) { 365 | case MDTraceObjectNone: 366 | name = @"未指定类"; 367 | break; 368 | case MDTraceObjectCoreClass: 369 | name = @"引擎类"; 370 | break; 371 | case MDTraceObjectUserClass: 372 | name = @"用户类"; 373 | break; 374 | case MDTraceObjectRegexClass: 375 | name = @"正则类"; 376 | break; 377 | 378 | default: 379 | NSAssert1(0, @"Unkown trace object: %tu", traceObject); 380 | break; 381 | } 382 | 383 | return name; 384 | } 385 | 386 | // 判断object是否属于测试模式 387 | + (BOOL)isTestTraceObject:(MDTraceObject)traceObject 388 | { 389 | return (traceObject == MDTraceObjectCoreClass); 390 | } 391 | 392 | // 获取当前classInfoList 393 | - (NSArray *)classInfoList 394 | { 395 | if (self.traceObject == MDTraceObjectCoreClass) { 396 | return self.coreClassInfoList; 397 | } else if (self.traceObject == MDTraceObjectUserClass) { 398 | return self.userClassInfoList; 399 | } else if (self.traceObject == MDTraceObjectRegexClass) { 400 | return self.regexClassInfoList; 401 | } 402 | return nil; 403 | } 404 | 405 | // 查找某个类是否存在classInfoList中 406 | - (MDTraceClassInfo *)infoInClassInfoList:(NSString *)className 407 | { 408 | for (MDTraceClassInfo *info in [self classInfoList]) { 409 | if ([info.name isEqualToString:className]) { 410 | return info; 411 | } 412 | } 413 | return nil; 414 | } 415 | 416 | // dump输出ClassListInfo 417 | - (void)dumpClassListInfo 418 | { 419 | if (!(self.traceFlag & MDTraceFlagDumpClassListInfo)) { 420 | return; 421 | } 422 | 423 | MDLog(@"Dump %@ class list info: ", [[self class] NSStringFromTraceObject:self.traceObject]); 424 | MDLog(@"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 425 | NSArray *classInfoList = [self classInfoList]; 426 | for (int i = 0; i < classInfoList.count; i++) { 427 | MDLog(@"ClassList[%05d]: %@", i, classInfoList[i]); 428 | } 429 | MDLog(@"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 430 | } 431 | 432 | // dump类所有方法信息 433 | - (void)dumpClassMethodInfo:(MDTraceClassInfo *)classInfo 434 | { 435 | if (!(self.traceFlag & MDTraceFlagDumpClassMethod || classInfo.flag & MDTraceFlagDumpClassMethod)) { 436 | return; 437 | } 438 | 439 | MDLog(@"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 440 | 441 | Class cls = NSClassFromString(classInfo.name); 442 | while (nil != cls) { 443 | unsigned int numMethods = 0; 444 | Method *methodList = class_copyMethodList(cls, &numMethods); 445 | if (NULL != methodList) { 446 | for (unsigned int i = 0; i < numMethods; i ++) { 447 | MDLog(@"-Class[%@] method[%03d]: %@", cls, i, [[self class] methodInfo:methodList[i]]); 448 | } 449 | } 450 | 451 | Class metaClass = object_getClass(cls); 452 | methodList = class_copyMethodList(metaClass, &numMethods); 453 | if (NULL != methodList) { 454 | for (unsigned int i = 0; i < numMethods; i ++) { 455 | MDLog(@"+Class[%@] method[%03d]: %@", cls, i, [[self class] methodInfo:methodList[i]]); 456 | } 457 | } 458 | 459 | if (!(self.traceFlag & MDTraceFlagDumpSuperClassMethod || classInfo.flag & MDTraceFlagDumpSuperClassMethod)) { 460 | break; 461 | } 462 | cls = [cls superclass]; 463 | } 464 | 465 | MDLog(@"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"); 466 | } 467 | 468 | // 某方法信息 469 | + (NSString *)methodInfo:(Method)method 470 | { 471 | return [NSString stringWithFormat:@"name[%s] IMP[%p]", sel_getName(method_getName(method)), method_getImplementation(method)]; 472 | } 473 | 474 | #pragma mark - Trace log 475 | 476 | // 初始化日志系统 477 | - (void)initLogger 478 | { 479 | #ifdef USE_DDLOG 480 | // 外部Lumberjack logger, 日志存放于目录"~/Library/Caches/Logs" 481 | [DDLog addLogger:[DDTTYLogger sharedInstance]]; // TTY = Xcode 控制台 482 | // [[DDTTYLogger sharedInstance] setColorsEnabled:YES]; 483 | // [DDLog addLogger:[DDASLLogger sharedInstance]]; // ASL = Apple System Logs 苹果系统日志 484 | 485 | DDFileLogger *fileLogger = [[DDFileLogger alloc] init]; // 本地文件日志 486 | fileLogger.doNotReuseLogFiles = YES; 487 | fileLogger.logFileManager.maximumNumberOfLogFiles = 1; // 永远创建一个文件,便于观察日志 488 | fileLogger.maximumFileSize = 0; 489 | fileLogger.rollingFrequency = 0; 490 | [DDLog addLogger:fileLogger]; 491 | #endif 492 | 493 | // 内部logger 494 | [[OCMethodTrace sharedInstance] setLogLevel:self.logLevel == MDTraceLogLeveError ? OMTLogLevelError : OMTLogLevelDebug]; 495 | [[OCMethodTrace sharedInstance] setDelegate:self]; 496 | } 497 | 498 | // trace开关 499 | - (void)enableTrace:(BOOL)enable 500 | { 501 | if (!enable) { 502 | [OCMethodTrace sharedInstance].disableTrace = YES; 503 | } else { 504 | if (self.logWhen == MDTraceLogWhenVolume) { 505 | #if TARGET_OS_SIMULATOR 506 | [OCMethodTrace sharedInstance].disableTrace = NO; 507 | self.logWhen = MDTraceLogWhenStartup; 508 | MDLog(@"Volume control log is not supported when simulator, reset logWhen to %tu", self.logWhen); 509 | #else 510 | // 必须屏蔽该句,否则在Tweak会导致Tweak无法工作!!! 511 | //self.lastSystemVolume = [[AVAudioSession sharedInstance] outputVolume]; 512 | // 需要异步注册通知,否则无法工作 513 | dispatch_async(dispatch_get_main_queue(), ^{ 514 | [self addVolumeObserver]; 515 | }); 516 | #endif 517 | } else { 518 | [OCMethodTrace sharedInstance].disableTrace = NO; 519 | } 520 | } 521 | } 522 | 523 | #if !TARGET_OS_SIMULATOR 524 | - (void)addVolumeObserver 525 | { 526 | [[NSNotificationCenter defaultCenter] addObserver:self 527 | selector:@selector(onSystemVolumeChanged:) name:@"AVSystemController_SystemVolumeDidChangeNotification" 528 | object:nil]; 529 | [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; 530 | } 531 | 532 | - (void)removeVolumeObserver 533 | { 534 | [[NSNotificationCenter defaultCenter] removeObserver:self 535 | name:@"AVSystemController_SystemVolumeDidChangeNotification" 536 | object:nil]; 537 | [[UIApplication sharedApplication] endReceivingRemoteControlEvents]; 538 | } 539 | 540 | - (void)onSystemVolumeChanged:(NSNotification *)notification 541 | { 542 | bool shouldShow = false; 543 | NSString *category = notification.userInfo[@"AVSystemController_AudioCategoryNotificationParameter"]; 544 | NSString *changeReason = notification.userInfo[@"AVSystemController_AudioVolumeChangeReasonNotificationParameter"]; 545 | if ([category isEqualToString:@"Audio/Video"] || // 正常场景下控制音量 546 | [category isEqualToString:@"PhoneCall"]) { // 视频电话中控制音量,比如微信视频通话中 547 | if ([changeReason isEqualToString:@"ExplicitVolumeChange"]) { 548 | shouldShow = true; 549 | } 550 | } 551 | if (!shouldShow) { 552 | return; 553 | } 554 | 555 | CGFloat volume = [[notification userInfo][@"AVSystemController_AudioVolumeNotificationParameter"] floatValue]; 556 | [OCMethodTrace sharedInstance].disableTrace = volume < self.lastSystemVolume; // 音量升高触发日志输出 557 | self.lastSystemVolume = volume; 558 | } 559 | #endif 560 | 561 | #pragma mark - Trace parse config 562 | 563 | - (void)parseConfig:(NSDictionary *)config 564 | { 565 | if (nil == config) { 566 | return; 567 | } 568 | 569 | MDLog(TRACE_README); 570 | 571 | self.config = [NSMutableDictionary dictionaryWithDictionary:config]; 572 | self.logLevel = [SAFE_CHECK(self.config[MDCONFIG_LOG_LEVEL_KEY], NSNumber) unsignedIntegerValue]; 573 | self.logWhen = [SAFE_CHECK(self.config[MDCONFIG_LOG_WHEN_KEY], NSNumber) unsignedIntegerValue]; 574 | self.traceFlag = [SAFE_CHECK(self.config[MDCONFIG_TRACE_FLAG_KEY], NSNumber) unsignedIntegerValue]; 575 | self.traceObject = [SAFE_CHECK(self.config[MDCONFIG_TRACE_OBJECT_KEY], NSNumber) unsignedIntegerValue]; 576 | 577 | MDLog(@"logLevel: %tu: logWhen: %tu traceFlag: %tu traceObject: %tu(%@%@)", 578 | self.logLevel, self.logWhen, self.traceFlag, self.traceObject, 579 | [[self class] NSStringFromTraceObject:self.traceObject], 580 | [[self class] isTestTraceObject:self.traceObject] ? @"(仅测试验证使用,不建议开启)" : @""); 581 | 582 | // 检查配置有效性 583 | [self checkConfig]; 584 | 585 | // 初始化日志 586 | [self initLogger]; 587 | 588 | // 屏蔽trace输出。避免hook准备过程中导致的递归调用 589 | [self enableTrace:NO]; 590 | 591 | // 实际hook类的各种方法 592 | [self traceClassObject]; 593 | 594 | // 恢复trace输出 595 | [self enableTrace:YES]; 596 | } 597 | 598 | - (void)checkConfig 599 | { 600 | NSAssert(self.logLevel >= MDTraceLogLeveError && self.logLevel < MDTraceLogLeveMax, @"invalid loglevel"); 601 | NSAssert(self.logWhen >= MDTraceLogWhenStartup && self.logWhen < MDTraceLogWhenMax, @"invalid logWhen"); 602 | NSAssert(self.traceFlag >= 0 && self.traceFlag <= MDTraceFlagMask, @"invalid traceFlag"); 603 | NSAssert(self.traceObject >= MDTraceObjectNone && self.traceObject < MDTraceObjectMax, @"invalid traceObject"); 604 | 605 | if (self.logWhen == MDTraceLogWhenRegexString) { 606 | self.logRegexString = SAFE_CHECK(self.config[MDCONFIG_LOG_REGEX_STRING_KEY], NSString); 607 | if (self.logRegexString.length == 0) { 608 | MDFatal(@"LogRegexString is nil"); 609 | } 610 | MDLog(@"LogRegexString: %@", self.logRegexString); 611 | } 612 | 613 | if (self.traceObject == MDTraceObjectRegexClass) { 614 | self.classRegexString = SAFE_CHECK(self.config[MDCONFIG_CLASS_REGEX_STRING_KEY], NSString); 615 | if (self.classRegexString.length == 0) { 616 | MDFatal(@"ClassRegexString is nil"); 617 | } 618 | MDLog(@"ClassRegexString: %@", self.classRegexString); 619 | } 620 | } 621 | 622 | - (void)traceClassObject 623 | { 624 | if (self.traceObject == MDTraceObjectNone) { 625 | MDLog(@"Method Trace is disabled"); 626 | return; 627 | } 628 | 629 | [self parseClassListInfo]; 630 | [self dumpClassListInfo]; 631 | [self traceClassListInfo]; 632 | } 633 | 634 | - (void)parseClassListInfo 635 | { 636 | id object = nil; 637 | NSDictionary *coreClassListDict = nil; 638 | NSDictionary *userClassListDict = nil; 639 | NSMutableArray *classList = [[NSMutableArray alloc] init]; 640 | NSMutableArray *regexClassList = [[NSMutableArray alloc] init]; 641 | 642 | object = [self.config valueForKey:MDCONFIG_CORE_CLASS_LIST]; 643 | if (nil != object) { 644 | coreClassListDict = SAFE_CHECK(object, NSDictionary); 645 | } 646 | object = [self.config valueForKey:MDCONFIG_USER_CLASS_LIST]; 647 | if (nil != object) { 648 | userClassListDict = SAFE_CHECK(object, NSDictionary); 649 | } 650 | 651 | // 1 获取真实存在的类 652 | int numberOfClasses = objc_getClassList(NULL, 0); 653 | Class *tmpClassList = (Class *)malloc(sizeof(Class) * numberOfClasses); 654 | if (NULL != tmpClassList) { 655 | objc_getClassList(tmpClassList, numberOfClasses); 656 | for (int i = 0; i < numberOfClasses; i++) { 657 | NSString *className = NSStringFromClass(tmpClassList[i]); 658 | [classList addObject:className]; 659 | if (self.traceObject == MDTraceObjectRegexClass && [[self class] isMatchRegexString:self.classRegexString inputString:className]) { 660 | // 多次匹配可能是重复类,需要去重 661 | if (![regexClassList containsObject:className]) { 662 | [regexClassList addObject:className]; 663 | } 664 | } 665 | } 666 | free(tmpClassList); 667 | } 668 | 669 | // 2 获取classInfo 670 | if (self.traceObject == MDTraceObjectCoreClass) { 671 | // 2.1 core 672 | for (NSString *className in coreClassListDict.allKeys) { 673 | if (![classList containsObject:className]) { 674 | MDLog(@"Cannot find class %@", className); 675 | continue; 676 | } 677 | 678 | NSDictionary *coreConfig = coreClassListDict[className]; 679 | MDTraceClassInfo *coreInfo = [MDTraceClassInfo infoWithName:className source:MDTraceSourceCore config:coreConfig]; 680 | [self.coreClassInfoList addObject:coreInfo]; 681 | } 682 | } else if (self.traceObject == MDTraceObjectUserClass) { 683 | // 2.2 user,注意,user需要跟core合并,优先级:core > user 684 | for (NSString *className in userClassListDict.allKeys) { 685 | if (![classList containsObject:className]) { 686 | MDLog(@"Cannot find class %@", className); 687 | continue; 688 | } 689 | 690 | NSDictionary *coreConfig = coreClassListDict[className]; 691 | NSDictionary *userConfig = userClassListDict[className]; 692 | MDTraceClassInfo *mergeinfo = [MDTraceClassInfo mergeInfoWithName:className coreConfig:coreConfig userConfig:userConfig]; 693 | [self.userClassInfoList addObject:mergeinfo]; 694 | } 695 | } else if (self.traceObject == MDTraceObjectRegexClass) { 696 | // 2.3 regex, 优先级:core > user > regex 697 | 698 | // 2.3.1 user需要跟core合并,优先级:core > user 699 | for (NSString *className in userClassListDict.allKeys) { 700 | if (![classList containsObject:className]) { 701 | MDLog(@"Cannot find class %@", className); 702 | continue; 703 | } 704 | 705 | NSDictionary *coreConfig = coreClassListDict[className]; 706 | NSDictionary *userConfig = userClassListDict[className]; 707 | MDTraceClassInfo *mergeinfo = [MDTraceClassInfo mergeInfoWithName:className coreConfig:coreConfig userConfig:userConfig]; 708 | [self.regexClassInfoList addObject:mergeinfo]; 709 | } 710 | 711 | // 2.3.2 regex需要跟core合并,优先级:core > regex。regex匹配的类可理解成更低优先级的user类 712 | NSDictionary *defaultUserConfig = [[NSDictionary alloc] init]; 713 | for (NSString *className in regexClassList) { 714 | if (nil == [self infoInClassInfoList:className]) { 715 | NSDictionary *coreConfig = coreClassListDict[className]; 716 | MDTraceClassInfo *mergeinfo = [MDTraceClassInfo mergeInfoWithName:className coreConfig:coreConfig userConfig:defaultUserConfig]; 717 | [self.regexClassInfoList addObject:mergeinfo]; 718 | } 719 | } 720 | } 721 | } 722 | 723 | - (void)traceClassListInfo 724 | { 725 | MDLog(@" "); 726 | MDLog(@"////////////////////////////////////////////////////////////////////////////////"); 727 | MDLog(@" "); 728 | 729 | int j = 0; 730 | NSArray *classInfoList = [self classInfoList]; 731 | for (int i = 0; i < classInfoList.count; i++) { 732 | MDTraceClassInfo *info = classInfoList[i]; 733 | // 该类关闭trace也跳过,避免打印太多日志 734 | if (nil != objc_getClass([info.name UTF8String])) { 735 | if (info.mode != MDTraceModeOff) { 736 | MDLog(@"ClassList[%05d]: %@", j++, info.name); 737 | [self traceClass:info]; 738 | } 739 | } else { 740 | MDLog(@"Cannot find class %@", info.name); 741 | } 742 | } 743 | 744 | MDLog(@" "); 745 | MDLog(@"////////////////////////////////////////////////////////////////////////////////"); 746 | MDLog(@" "); 747 | } 748 | 749 | - (void)traceClass:(MDTraceClassInfo *)classInfo 750 | { 751 | if (nil == classInfo) { 752 | return; 753 | } 754 | 755 | [self dumpClassMethodInfo:classInfo]; 756 | 757 | if (classInfo.mode == MDTraceModeOff) { 758 | } else if (classInfo.mode == MDTraceModeAll) { 759 | [self addClassTrace:classInfo.name]; 760 | } else if (classInfo.mode == MDTraceModeIncludeWhiteList) { 761 | [self addClassTrace:classInfo.name methodList:classInfo.methodList white:YES]; 762 | } else if (classInfo.mode == MDTraceModeExcludeBlackList) { 763 | [self addClassTrace:classInfo.name methodList:classInfo.methodList white:NO]; 764 | } 765 | 766 | [self dumpClassMethodInfo:classInfo]; 767 | } 768 | 769 | #pragma mark - Trace method 770 | 771 | - (void)addClassTrace:(NSString *)className{ 772 | [self addClassTrace:className methodList:nil]; 773 | } 774 | 775 | - (void)addClassTrace:(NSString *)className methodName:(NSString *)methodName { 776 | [self addClassTrace:className methodList:@[methodName]]; 777 | } 778 | 779 | - (void)addClassTrace:(NSString *)className methodList:(NSArray*)methodList { 780 | [self addClassTrace:className methodList:methodList white:YES]; 781 | } 782 | 783 | - (void)addClassTrace:(NSString *)className methodList:(NSArray *)methodList white:(BOOL)white { 784 | Class targetClass = objc_getClass([className UTF8String]); 785 | if (targetClass != nil) { 786 | [[OCMethodTrace sharedInstance] traceMethodWithClass:NSClassFromString(className) condition:^BOOL(SEL sel) { 787 | if (methodList == nil || methodList.count == 0) { 788 | return YES; 789 | } 790 | for (id object in methodList) { 791 | NSString *methodName = SAFE_CHECK(object, NSString); 792 | // 方法可以是正则表达式 793 | if ([[self class] isMatchRegexString:methodName inputString:NSStringFromSelector(sel)]) { 794 | return white; 795 | } 796 | } 797 | return !white; 798 | } before:^(id target, Class cls, SEL sel, NSArray *args, int deep) { 799 | NSString *selector = NSStringFromSelector(sel); 800 | NSMutableString *selectorString = [NSMutableString new]; 801 | if ([selector containsString:@":"]) { 802 | NSArray *selectorArrary = [selector componentsSeparatedByString:@":"]; 803 | selectorArrary = [selectorArrary filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"length > 0"]]; 804 | for (int i = 0; i < selectorArrary.count; i++) { 805 | [selectorString appendFormat:@"%@:%@ ", selectorArrary[i], args[i]]; 806 | } 807 | } else { 808 | [selectorString appendString:selector]; 809 | } 810 | 811 | NSMutableString *deepString = [NSMutableString new]; 812 | for (int i = 0; i < deep; i++) { 813 | [deepString appendString:@"-"]; 814 | } 815 | 816 | // [obj class]则分两种情况: 817 | // 1 当obj为实例对象时,[obj class]中class是实例方法:- (Class)class,返回的obj对象中的isa指针; 818 | // 2 当obj为类对象(包括元类和根类以及根元类)时,调用的是类方法:+ (Class)class,返回的结果为其本身。 819 | NSString *prefix = target == [target class] ? @"+" : @"-"; 820 | // target不是强引用,如果打印接口异步,可能未实际调用description就被释放了,所以提前获取desc,保证线程安全 821 | NSString *description = [self descriptionWithTarget:target class:cls selector:sel targetPosition:OMTTargetPositionBeforeSelf]; 822 | NSString *logString = nil; 823 | if ([target class] != [cls class]) { 824 | // 如果是子类调用基类方法,则()内打印基类名 825 | logString = [NSString stringWithFormat:@"%@%@[%@(%@) %@]", deepString, prefix, description, NSStringFromClass(cls), selectorString]; 826 | } else { 827 | logString = [NSString stringWithFormat:@"%@%@[%@ %@]", deepString, prefix, description, selectorString]; 828 | } 829 | 830 | if (self.logWhen == MDTraceLogWhenStartup || 831 | self.logWhen == MDTraceLogWhenVolume || 832 | (self.logWhen == MDTraceLogWhenRegexString && [[self class] isMatchRegexString:self.logRegexString inputString:logString])) { 833 | self.numberOfPendingLog++; 834 | MDLog(@"%@", logString); 835 | } 836 | } after:^(id target, Class cls, SEL sel, id ret, int deep, NSTimeInterval interval) { 837 | // 因为多线程并发,numberOfPendingLog变量维护的输出状态有可能并不是那么准,但是打印调试可以容忍 838 | if (self.numberOfPendingLog > 0) { 839 | self.numberOfPendingLog--; 840 | NSMutableString *deepString = [NSMutableString new]; 841 | for (int i = 0; i < deep; i++) { 842 | [deepString appendString:@"-"]; 843 | } 844 | 845 | NSString *prefix = target == [target class] ? @"+" : @"-"; 846 | MDLog(@"%@%@ret:%@", deepString, prefix, ret); 847 | } 848 | }]; 849 | } else { 850 | MDLog(@"Canot find class %@", className); 851 | } 852 | } 853 | 854 | #pragma mark - OCMethodTraceDelegate 855 | 856 | - (NSString *)descriptionWithTarget:(id)target class:(Class)cls selector:(SEL)sel targetPosition:(OMTTargetPosition)targetPosition 857 | { 858 | if (nil == target) { 859 | return @"nil"; 860 | } 861 | 862 | NSString *targetClassName = NSStringFromClass([target class]); 863 | 864 | // 全局跳过对象description方法 865 | if (self.traceFlag & MDTraceFlagDoesNotUseDescription) { 866 | return [NSString stringWithFormat:@"<%@: %p>", targetClassName, target]; 867 | } 868 | 869 | // 类跳过对象description方法,粒度更小一点 870 | MDTraceClassInfo *info = [self infoInClassInfoList:targetClassName]; 871 | BOOL doesNotUseDescription = (nil != info && info.flag & MDTraceFlagDoesNotUseDescription); 872 | if (!doesNotUseDescription) { 873 | // 构造初始化函数特殊处理, 系统类初始化比较喜欢"_init"这样的方式 874 | NSString *selectorName = NSStringFromSelector(sel); 875 | BOOL isAllocFunc = [selectorName hasPrefix:@"new"] || [selectorName hasPrefix:@"alloc"]; 876 | BOOL maybeInitFunc = [selectorName hasPrefix:@"init"] || [selectorName hasPrefix:@"_init"]; 877 | BOOL isDeallocFunc = [selectorName isEqualToString:@"dealloc"]; 878 | if (isAllocFunc || maybeInitFunc) { 879 | switch (targetPosition) { 880 | case OMTTargetPositionBeforeSelf: 881 | // 调用构造函数时,此时实例还没初始化完全,不能调用description 882 | doesNotUseDescription = YES; 883 | break; 884 | case OMTTargetPositionBeforeArgument: 885 | break; 886 | case OMTTargetPositionAfterSelf: 887 | case OMTTargetPositionAfterReturnValue: 888 | if (isAllocFunc) { 889 | doesNotUseDescription = YES; 890 | } else if (maybeInitFunc) { 891 | // 调用构造函数时,可能是调用父类的构造函数,此时实例还没初始化完全,不能调用description 892 | if (![targetClassName isEqualToString:NSStringFromClass(cls)]) { 893 | doesNotUseDescription = YES; 894 | } 895 | } 896 | break; 897 | 898 | default: 899 | break; 900 | } 901 | } else if (isDeallocFunc) { 902 | // 析构函数所有只打印指针,因为dealloc after时,对象已被释放 903 | doesNotUseDescription = YES; 904 | } 905 | } 906 | 907 | return doesNotUseDescription ? [NSString stringWithFormat:@"<%@: %p>", targetClassName, target] : [target description]; 908 | } 909 | 910 | - (void)log:(OMTLogLevel)level format:(NSString *)format, ... 911 | { 912 | va_list args; 913 | if (format) { 914 | va_start(args, format); 915 | NSString *message = [[NSString alloc] initWithFormat:format arguments:args]; 916 | va_end(args); 917 | 918 | MDLog(@"%@", message); 919 | } 920 | } 921 | 922 | @end 923 | 924 | //////////////////////////////////////////////////////////////////////////////// 925 | #pragma mark - Entry 926 | 927 | static __attribute__((constructor)) void entry() 928 | { 929 | NSDictionary *config = [[MDConfigManager sharedInstance] readConfigByKey:MDCONFIG_TRACE_KEY]; 930 | [[MDMethodTrace sharedInstance] parseConfig:config]; 931 | } 932 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/TestUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestUtils.h 3 | // OCMethodTraceDemo 4 | // 5 | // Created by Michael Chen on 2018/9/9. 6 | // Copyright (C) 2018 Michael Chen 7 | // 8 | 9 | #import 10 | 11 | #define TestLog(fmt, ...) NSLog((@"on %s, " fmt), __PRETTY_FUNCTION__, ##__VA_ARGS__) 12 | 13 | typedef struct { 14 | char a; 15 | } __attribute__((packed)) ObjStruct1; 16 | 17 | 18 | typedef struct { 19 | int32_t a; 20 | } __attribute__((packed)) ObjStruct4; 21 | 22 | 23 | typedef struct { 24 | int64_t a; 25 | } __attribute__((packed)) ObjStruct8; 26 | 27 | typedef struct { 28 | int64_t a; 29 | int64_t b; 30 | } __attribute__ ((packed)) ObjStruct16; 31 | 32 | typedef struct { 33 | int64_t a; 34 | int64_t b; 35 | int64_t c; 36 | } __attribute__ ((packed)) ObjStruct24; 37 | 38 | typedef struct { 39 | int64_t a; 40 | int64_t b; 41 | int64_t c; 42 | int64_t d; 43 | } __attribute__ ((packed)) ObjStruct32; 44 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/Tiger.h: -------------------------------------------------------------------------------- 1 | // 2 | // Tiger.h 3 | // OCMethodTraceDemo 4 | // 5 | // Created by Michael Chen on 2018/9/9. 6 | // Copyright (C) 2018 Michael Chen 7 | // 8 | 9 | #import "Animal.h" 10 | 11 | @interface Tiger : Animal 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/Tiger.m: -------------------------------------------------------------------------------- 1 | // 2 | // Tiger.m 3 | // OCMethodTraceDemo 4 | // 5 | // Created by Michael Chen on 2018/9/9. 6 | // Copyright (C) 2018 Michael Chen 7 | // 8 | 9 | #import "Tiger.h" 10 | 11 | @implementation Tiger 12 | 13 | + (void)setLogLevel:(int)level 14 | { 15 | TestLog(@"level: %d", level); 16 | [super setLogLevel:level]; 17 | } 18 | 19 | - (void)setName:(NSString *)name 20 | { 21 | TestLog(@"name: %@", name); 22 | [super setName:name]; 23 | } 24 | 25 | - (NSString *)getName 26 | { 27 | NSString *ret = [super getName]; 28 | TestLog(@"name: %@", ret); 29 | return ret; 30 | } 31 | 32 | - (void)setAge:(NSUInteger)age 33 | { 34 | TestLog(@"age: %tu", age); 35 | [super setAge:age]; 36 | } 37 | 38 | - (NSUInteger)getAge 39 | { 40 | NSUInteger ret = [super getAge]; 41 | TestLog(@"age: %tu", ret); 42 | return ret; 43 | } 44 | 45 | - (void)setName:(NSString *)name age:(NSUInteger)age 46 | { 47 | TestLog(@"name: %@ age: %tu", name, age); 48 | [super setName:name age:age]; 49 | } 50 | 51 | - (ObjStruct1)setObjStruct1:(char)a 52 | { 53 | ObjStruct1 st = [super setObjStruct1:a]; 54 | TestLog(@"setObjStruct1: a: %c", st.a); 55 | return st; 56 | } 57 | 58 | - (ObjStruct4)setObjStruct4:(int32_t)a 59 | { 60 | ObjStruct4 st = [super setObjStruct4:a]; 61 | TestLog(@"setObjStruct4: a: %d", st.a); 62 | return st; 63 | } 64 | 65 | - (ObjStruct8)setObjStruct8:(int64_t)a 66 | { 67 | ObjStruct8 st = [super setObjStruct8:a]; 68 | TestLog(@"setObjStruct8: a: %lld", st.a); 69 | return st; 70 | } 71 | 72 | - (ObjStruct16)setObjStruct16:(int64_t)a b:(int64_t)b 73 | { 74 | ObjStruct16 st = [super setObjStruct16:a b:b]; 75 | TestLog(@"setObjStruct16: a: %lld b: %lld", st.a, st.b); 76 | return st; 77 | } 78 | 79 | - (ObjStruct24)setObjStruct24:(int64_t)a b:(int64_t)b c:(int64_t)c 80 | { 81 | ObjStruct24 st = [super setObjStruct24:a b:b c:c]; 82 | TestLog(@"setObjStruct24: a: %lld b: %lld c: %lld", st.a, st.b, st.c); 83 | return st; 84 | } 85 | 86 | - (ObjStruct32)setObjStruct32:(int64_t)a b:(int64_t)b c:(int64_t)c d:(int64_t)d 87 | { 88 | ObjStruct32 st = [super setObjStruct32:a b:b c:c d:d]; 89 | TestLog(@"setObjStruct32: a: %lld b: %lld c: %lld d: %lld", st.a, st.b, st.c, st.d); 90 | return st; 91 | } 92 | 93 | //- (NSString *)description 94 | //{ 95 | // return [NSString stringWithFormat:@"<%@: %p name: %@ age: %tu>", 96 | // NSStringFromClass([self class]), self, [self getName], self.age]; 97 | //} 98 | 99 | @end 100 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // OCMethodTraceDemo 4 | // 5 | // Created by Michael Chen on 2018/9/9. 6 | // Copyright (C) 2018 Michael Chen 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // OCMethodTraceDemo 4 | // 5 | // Created by Michael Chen on 2018/9/9. 6 | // Copyright (C) 2018 Michael Chen 7 | // 8 | 9 | #import "ViewController.h" 10 | #import "Tiger.h" 11 | 12 | @interface ViewController () 13 | 14 | @end 15 | 16 | @implementation ViewController 17 | 18 | - (void)viewDidLoad { 19 | [super viewDidLoad]; 20 | // Do any additional setup after loading the view, typically from a nib. 21 | 22 | // insert code here... 23 | [self testMain]; 24 | } 25 | 26 | - (void)didReceiveMemoryWarning { 27 | [super didReceiveMemoryWarning]; 28 | // Dispose of any resources that can be recreated. 29 | } 30 | 31 | - (void)testMain { 32 | [self test1]; 33 | [self test2]; 34 | } 35 | 36 | - (void)test1 { 37 | [super viewDidLoad]; 38 | // Do any additional setup after loading the view, typically from a nib. 39 | 40 | // insert code here... 41 | NSLog(@"Hello, OCMethodTrace!"); 42 | 43 | NSLog(@" "); 44 | NSLog(@"=============================="); 45 | NSLog(@"Tiger setLogLevel:"); 46 | [Tiger setLogLevel:1]; 47 | NSLog(@"=============================="); 48 | NSLog(@" "); 49 | 50 | Tiger *obj = [[Tiger alloc] init]; 51 | 52 | NSString *name = nil; 53 | NSUInteger age = 0; 54 | 55 | /////////////////////////////////////////////////////////////// 56 | 57 | NSLog(@" "); 58 | NSLog(@"=============================="); 59 | NSLog(@"Tiger setName:age:"); 60 | [obj setName:@"Tiger-0" age:5]; 61 | NSLog(@"=============================="); 62 | NSLog(@" "); 63 | 64 | NSLog(@" "); 65 | NSLog(@"=============================="); 66 | name = [obj getName]; 67 | NSLog(@"Tiger name: %@", name); 68 | NSLog(@"=============================="); 69 | NSLog(@" "); 70 | 71 | NSLog(@" "); 72 | NSLog(@"=============================="); 73 | age = [obj getAge]; 74 | NSLog(@"Tiger age: %tu", age); 75 | NSLog(@"=============================="); 76 | NSLog(@" "); 77 | 78 | 79 | NSLog(@" "); 80 | NSLog(@"=============================="); 81 | ObjStruct1 st1 = [obj setObjStruct1:'a']; 82 | NSLog(@"Tiger setObjStruct1: a: %c", st1.a); 83 | NSLog(@"=============================="); 84 | NSLog(@" "); 85 | 86 | NSLog(@" "); 87 | NSLog(@"=============================="); 88 | ObjStruct4 st4 = [obj setObjStruct4:1]; 89 | NSLog(@"Tiger setObjStruct4: a: %d", st4.a); 90 | NSLog(@"=============================="); 91 | NSLog(@" "); 92 | 93 | 94 | NSLog(@" "); 95 | NSLog(@"=============================="); 96 | ObjStruct8 st8 = [obj setObjStruct8:1]; 97 | NSLog(@"Tiger setObjStruct8: a: %lld", st8.a); 98 | NSLog(@"=============================="); 99 | NSLog(@" "); 100 | 101 | NSLog(@" "); 102 | NSLog(@"=============================="); 103 | ObjStruct16 st16 = [obj setObjStruct16:1 b:2]; 104 | NSLog(@"Tiger setObjStruct16: a: %lld b: %lld", st16.a, st16.b); 105 | NSLog(@"=============================="); 106 | NSLog(@" "); 107 | 108 | NSLog(@" "); 109 | NSLog(@"=============================="); 110 | ObjStruct24 st24 = [obj setObjStruct24:1 b:2 c:3]; 111 | NSLog(@"Tiger setObjStruct24: a: %lld b: %lld b: %lld", st24.a, st24.b, st24.c); 112 | NSLog(@"=============================="); 113 | NSLog(@" "); 114 | 115 | NSLog(@" "); 116 | NSLog(@"=============================="); 117 | ObjStruct32 st32 = [obj setObjStruct32:1 b:2 c:3 d:4]; 118 | NSLog(@"Tiger setObjStruct32: a: %lld b: %lld c: %lld d: %lld", st32.a, st32.b, st32.c, st32.d); 119 | NSLog(@"=============================="); 120 | NSLog(@" "); 121 | 122 | /////////////////////////////////////////////////////////////// 123 | 124 | NSLog(@" "); 125 | NSLog(@"=============================="); 126 | NSLog(@"Tiger setName:"); 127 | [obj setName:@"Tiger-1"]; 128 | NSLog(@"=============================="); 129 | NSLog(@" "); 130 | 131 | NSLog(@" "); 132 | NSLog(@"=============================="); 133 | name = [obj getName]; 134 | NSLog(@"Tiger name: %@", name); 135 | NSLog(@"=============================="); 136 | NSLog(@" "); 137 | 138 | /////////////////////////////////////////////////////////////// 139 | 140 | NSLog(@" "); 141 | NSLog(@"=============================="); 142 | NSLog(@"Tiger setAge:"); 143 | [obj setAge:10]; 144 | NSLog(@"=============================="); 145 | NSLog(@" "); 146 | 147 | age = [obj getAge]; 148 | NSLog(@"Tiger age: %tu", age); 149 | NSLog(@"=============================="); 150 | NSLog(@" "); 151 | 152 | /////////////////////////////////////////////////////////////// 153 | 154 | Animal *baseObj = [[Animal alloc] init]; 155 | 156 | NSLog(@" "); 157 | NSLog(@"=============================="); 158 | age = [baseObj getAge]; 159 | NSLog(@"Animal age: %tu", age); 160 | NSLog(@"=============================="); 161 | NSLog(@" "); 162 | } 163 | 164 | - (void)test2 { 165 | dispatch_queue_t concurrentQueue = dispatch_queue_create("com.summer.concurrent", DISPATCH_QUEUE_CONCURRENT); 166 | dispatch_async(concurrentQueue, ^{ 167 | int level = 0; 168 | do { 169 | [Animal setLogLevel:level++]; 170 | Animal *baseObj = [[Animal alloc] init]; 171 | [baseObj setName:@"123"]; 172 | sleep(1); 173 | } while(1); 174 | }); 175 | } 176 | 177 | @end 178 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // OCMethodTraceDemo 4 | // 5 | // Created by Michael Chen on 2018/9/9. 6 | // Copyright (C) 2018 Michael Chen 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemoTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /OCMethodTraceDemo/OCMethodTraceDemoTests/OCMethodTraceDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // OCMethodTraceDemoTests.m 3 | // OCMethodTraceDemoTests 4 | // 5 | // Created by Michael Chen on 2018/9/9. 6 | // Copyright (C) 2018 Michael Chen 7 | // 8 | 9 | #import 10 | #import "OCMethodTrace.h" 11 | #import "TestUtils.h" 12 | 13 | // Copy from ANYMethodLogDemoTests.m 14 | // https://github.com/qhd/ANYMethodLog.git 15 | 16 | #pragma mark - 测试类TestObject 17 | 18 | @interface TestObject : NSObject 19 | 20 | - (BOOL)methodWithA:(char)a 21 | b:(int)b 22 | c:(short)c 23 | d:(long)d 24 | e:(long long)e 25 | f:(unsigned char)f 26 | g:(unsigned int)g 27 | h:(unsigned short)h 28 | i:(unsigned long)i 29 | j:(unsigned long long)j 30 | k:(float)k 31 | l:(double)l 32 | m:(bool)m 33 | //n:(void)n 34 | o:(char *)o 35 | p:(id)p 36 | q:(Class)q 37 | r:(SEL)r 38 | s:(CGRect)s 39 | t:(CGPoint)t 40 | u:(CGSize)u 41 | v:(CGVector)v 42 | w:(CGAffineTransform)w 43 | x:(UIOffset)x 44 | y:(UIEdgeInsets)y; 45 | - (char)method1:(char)a; 46 | - (int)method2:(int)a; 47 | - (short)method3:(short)a; 48 | - (long)method4:(long)a; 49 | - (long long)method5:(long long)a; 50 | - (unsigned char)method6:(unsigned char)a; 51 | - (unsigned int)method7:(unsigned int)a; 52 | - (unsigned short)method8:(unsigned short)a; 53 | - (unsigned long)method9:(unsigned long)a; 54 | - (unsigned long long)method10:(unsigned long long)a; 55 | - (float)method11:(float)a ; 56 | - (double)method12:(double)a; 57 | - (BOOL)method13:(BOOL)a; 58 | - (void)method14; 59 | - (char *)method15:(char *)a; 60 | - (id)method16:(id)a; 61 | - (Class)method17:(Class)a; 62 | - (SEL)method18:(SEL)a; 63 | - (CGRect)method19:(CGRect)a; 64 | - (CGPoint)method20:(CGPoint)a; 65 | - (CGSize)method21:(CGSize)a; 66 | - (CGVector)method22:(CGVector)a; 67 | - (CGAffineTransform)method23:(CGAffineTransform)a; 68 | - (UIEdgeInsets)method24:(UIEdgeInsets)a; 69 | - (UIOffset)method25:(UIOffset)a; 70 | - (void (^)())method26:(void (^)())a; 71 | - (ObjStruct1)method27:(ObjStruct1)a; 72 | - (ObjStruct4)method28:(ObjStruct4)a; 73 | - (ObjStruct8)method29:(ObjStruct8)a; 74 | - (ObjStruct16)method30:(ObjStruct16)a; 75 | - (ObjStruct24)method31:(ObjStruct24)a; 76 | - (ObjStruct32)method32:(ObjStruct32)a; 77 | 78 | @end 79 | 80 | @implementation TestObject 81 | 82 | - (BOOL)methodWithA:(char)a 83 | b:(int)b 84 | c:(short)c 85 | d:(long)d 86 | e:(long long)e 87 | f:(unsigned char)f 88 | g:(unsigned int)g 89 | h:(unsigned short)h 90 | i:(unsigned long)i 91 | j:(unsigned long long)j 92 | k:(float)k 93 | l:(double)l 94 | m:(bool)m 95 | //n:(void)n 96 | o:(char *)o 97 | p:(id)p 98 | q:(Class)q 99 | r:(SEL)r 100 | s:(CGRect)s 101 | t:(CGPoint)t 102 | u:(CGSize)u 103 | v:(CGVector)v 104 | w:(CGAffineTransform)w 105 | x:(UIOffset)x 106 | y:(UIEdgeInsets)y 107 | { 108 | TestLog(@"a: %@, b: %@, c: %@, d: %@, e: %@, f: %@, g: %@, h: %@, i: %@, j: %@, k: %@, l: %@, m: %@, n: void,o: %@, p: %@, q: %@, r: %@, s:%@, t:%@, u: %@, v: %@, w: %@, x: %@, y: %@", 109 | [@(a) stringValue], 110 | [@(b) stringValue], 111 | [@(c) stringValue], 112 | [@(d) stringValue], 113 | [@(e) stringValue], 114 | [@(f) stringValue], 115 | [@(g) stringValue], 116 | [@(h) stringValue], 117 | [@(i) stringValue], 118 | [@(j) stringValue], 119 | [@(k) stringValue], 120 | [@(l) stringValue], 121 | [@(m) stringValue], 122 | [NSString stringWithUTF8String:o], 123 | p, 124 | NSStringFromClass(q), 125 | NSStringFromSelector(r), 126 | NSStringFromCGRect(s), 127 | NSStringFromCGPoint(t), 128 | NSStringFromCGSize(u), 129 | NSStringFromCGVector(v), 130 | NSStringFromCGAffineTransform(w), 131 | NSStringFromUIOffset(x), 132 | NSStringFromUIEdgeInsets(y) 133 | ); 134 | return YES; 135 | } 136 | 137 | - (char)method1:(char)a { 138 | TestLog(); 139 | return a; 140 | } 141 | 142 | - (int)method2:(int)a { 143 | TestLog(); 144 | return a; 145 | } 146 | 147 | - (short)method3:(short)a { 148 | TestLog(); 149 | return a; 150 | } 151 | 152 | - (long)method4:(long)a { 153 | TestLog(); 154 | return a; 155 | } 156 | 157 | - (long long)method5:(long long)a { 158 | TestLog(); 159 | return a; 160 | } 161 | 162 | - (unsigned char)method6:(unsigned char)a { 163 | TestLog(); 164 | return a; 165 | } 166 | 167 | - (unsigned int)method7:(unsigned int)a { 168 | TestLog(); 169 | return a; 170 | } 171 | 172 | - (unsigned short)method8:(unsigned short)a { 173 | TestLog(); 174 | return a; 175 | } 176 | 177 | - (unsigned long)method9:(unsigned long)a { 178 | TestLog(); 179 | return a; 180 | } 181 | 182 | - (unsigned long long)method10:(unsigned long long)a { 183 | TestLog(); 184 | return a; 185 | } 186 | 187 | - (float)method11:(float)a { 188 | TestLog(); 189 | return a; 190 | } 191 | 192 | - (double)method12:(double)a { 193 | TestLog(); 194 | return a; 195 | } 196 | 197 | - (BOOL)method13:(BOOL)a { 198 | TestLog(); 199 | return a; 200 | } 201 | 202 | - (void)method14 { 203 | TestLog(); 204 | } 205 | 206 | - (char *)method15:(char *)a { 207 | TestLog(); 208 | return a; 209 | } 210 | 211 | - (id)method16:(id)a { 212 | TestLog(); 213 | return a; 214 | } 215 | 216 | - (Class)method17:(Class)a { 217 | TestLog(); 218 | return a; 219 | } 220 | 221 | - (SEL)method18:(SEL)a { 222 | TestLog(); 223 | return a; 224 | } 225 | 226 | - (CGRect)method19:(CGRect)a { 227 | TestLog(); 228 | return a; 229 | } 230 | 231 | - (CGPoint)method20:(CGPoint)a { 232 | TestLog(); 233 | return a; 234 | } 235 | 236 | - (CGSize)method21:(CGSize)a { 237 | TestLog(); 238 | return a; 239 | } 240 | 241 | - (CGVector)method22:(CGVector)a { 242 | TestLog(); 243 | return a; 244 | } 245 | 246 | - (CGAffineTransform)method23:(CGAffineTransform)a { 247 | TestLog(); 248 | return a; 249 | } 250 | 251 | - (UIEdgeInsets)method24:(UIEdgeInsets)a { 252 | TestLog(); 253 | return a; 254 | } 255 | 256 | - (UIOffset)method25:(UIOffset)a { 257 | TestLog(); 258 | return a; 259 | } 260 | 261 | - (void (^)())method26:(void (^)())a { 262 | TestLog(); 263 | return a; 264 | } 265 | 266 | - (ObjStruct1)method27:(ObjStruct1)a { 267 | TestLog(); 268 | return a; 269 | } 270 | - (ObjStruct4)method28:(ObjStruct4)a { 271 | TestLog(); 272 | return a; 273 | } 274 | 275 | - (ObjStruct8)method29:(ObjStruct8)a { 276 | TestLog(); 277 | return a; 278 | } 279 | 280 | - (ObjStruct16)method30:(ObjStruct16)a { 281 | TestLog(); 282 | return a; 283 | } 284 | 285 | - (ObjStruct24)method31:(ObjStruct24)a { 286 | TestLog(); 287 | return a; 288 | } 289 | 290 | - (ObjStruct32)method32:(ObjStruct32)a { 291 | TestLog(); 292 | return a; 293 | } 294 | 295 | @end 296 | 297 | //////////////////////////////////////////////////////////////////////////////// 298 | 299 | @interface OCMethodTraceDemoTests : XCTestCase 300 | 301 | @end 302 | 303 | @implementation OCMethodTraceDemoTests 304 | 305 | - (void)setUp { 306 | [super setUp]; 307 | // Put setup code here. This method is called before the invocation of each test method in the class. 308 | } 309 | 310 | - (void)tearDown { 311 | // Put teardown code here. This method is called after the invocation of each test method in the class. 312 | [super tearDown]; 313 | } 314 | 315 | - (void)testExample { 316 | // This is an example of a functional test case. 317 | // Use XCTAssert and related functions to verify your tests produce the correct results. 318 | } 319 | 320 | - (void)testPerformanceExample { 321 | // This is an example of a performance test case. 322 | [self measureBlock:^{ 323 | // Put the code you want to measure the time of here. 324 | }]; 325 | } 326 | 327 | - (void)testMyTestObject { 328 | [[OCMethodTrace sharedInstance] traceMethodWithClass:[TestObject class] condition:^BOOL(SEL sel) { 329 | return YES; 330 | } before:^(id target, Class cls, SEL sel, NSArray *args, int deep) { 331 | NSLog(@" "); 332 | NSLog(@"=============================="); 333 | NSLog(@"target :%@ sel: %@ args: %@", target, NSStringFromSelector(sel), args); 334 | } after:^(id target, Class cls, SEL sel, id ret, int deep, NSTimeInterval interval) { 335 | NSLog(@"target: %@ sel: %@ ret: %@", target, NSStringFromSelector(sel), ret); 336 | NSLog(@"=============================="); 337 | NSLog(@" "); 338 | }]; 339 | 340 | TestObject *o = [[TestObject alloc] init]; 341 | 342 | XCTAssertTrue([o methodWithA:'a' 343 | b:1 344 | c:12 345 | d:123 346 | e:1234 347 | f:'u' 348 | g:3 349 | h:4 350 | i:5 351 | j:6 352 | k:7.1 353 | l:7.12345 354 | m:YES 355 | o:"hello" 356 | p:o 357 | q:[o class] 358 | r:NSSelectorFromString(@"viewDidLoad") 359 | s:CGRectMake(1.1, 1.2, 1.3, 1.4) 360 | t:CGPointMake(2.11, 2.12) 361 | u:CGSizeMake(3.123, 3.1234) 362 | v:CGVectorMake(4.456, 4.567) 363 | w:CGAffineTransformMake(6.123, 6.124, 6.125, 6.126, 6.127, 6.128) 364 | x:UIOffsetMake(7.123456, 7.1234567) 365 | y:UIEdgeInsetsMake(8.0, 8.0001, 8.0002, 8.0003)]); 366 | 367 | XCTAssertTrue('a' == [o method1:'a']); 368 | XCTAssertTrue(12 == [o method2:12]); 369 | XCTAssertTrue(13 == [o method3:13]); 370 | XCTAssertTrue(1444444 == [o method4:1444444]); 371 | 372 | long long ll = 155; 373 | long long llr = [o method5:ll]; 374 | XCTAssertTrue(ll == llr); 375 | 376 | XCTAssertTrue('u' == [o method6:'u']); 377 | XCTAssertTrue(17 == [o method7:17]); 378 | XCTAssertTrue(18 == [o method8:18]); 379 | XCTAssertTrue(19000 == [o method9:19000]); 380 | XCTAssertTrue(20 == [o method10:20]); 381 | XCTAssertTrue(21 == [o method11:21]); 382 | XCTAssertTrue(22.345678 == [o method12:22.345678]); 383 | XCTAssertTrue(YES == [o method13:YES]); 384 | XCTAssertTrue(NO == [o method13:NO]); 385 | [o method14]; 386 | XCTAssertTrue("c255555" == [o method15:"c255555"]); 387 | XCTAssertTrue(o == [o method16:o]); 388 | XCTAssertTrue(NSClassFromString(@"TestObject") == [o method17:NSClassFromString(@"TestObject")]); 389 | XCTAssertTrue(NSSelectorFromString(@"method17:") == [o method18:NSSelectorFromString(@"method17:")]); 390 | XCTAssertTrue(CGRectEqualToRect(CGRectMake(1.2, 1.23, 1.234, 1.2345), [o method19:CGRectMake(1.2, 1.23, 1.234, 1.2345)])); 391 | 392 | XCTAssertTrue(CGPointEqualToPoint(CGPointMake(2.3, 2.34), [o method20:CGPointMake(2.3, 2.34)])); 393 | XCTAssertTrue(CGSizeEqualToSize(CGSizeMake(3.4, 3.45), [o method21:CGSizeMake(3.4, 3.45)])); 394 | 395 | CGVector vector = CGVectorMake(9.234, 9.2345); 396 | XCTAssertTrue(vector.dx == [o method22:vector].dx && vector.dy == [o method22:vector].dy); 397 | 398 | XCTAssertTrue(CGAffineTransformEqualToTransform(CGAffineTransformMake(4.1, 4.12, 4.123, 4.1234, 4.12345, 4.123456),[o method23:CGAffineTransformMake(4.1, 4.12, 4.123, 4.1234, 4.12345, 4.123456)])); 399 | 400 | XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(UIEdgeInsetsMake(5.1, 5.12, 5.123, 5.1234), [o method24:UIEdgeInsetsMake(5.1, 5.12, 5.123, 5.1234)])); 401 | 402 | XCTAssertTrue(UIOffsetEqualToOffset(UIOffsetMake(6.123, 6.1234),[o method25:UIOffsetMake(6.123, 6.1234)])); 403 | 404 | void (^aBlock)() = ^() { 405 | 406 | }; 407 | XCTAssertTrue(aBlock == [o method26:aBlock]); 408 | 409 | ObjStruct1 st1_1 = { 'a' }; 410 | ObjStruct1 st1_2 = [o method27:st1_1]; 411 | XCTAssertTrue(0 == memcmp(&st1_1, &st1_2, sizeof(st1_1))); 412 | 413 | ObjStruct4 st4_1 = { 1 }; 414 | ObjStruct4 st4_2 = [o method28:st4_1]; 415 | XCTAssertTrue(0 == memcmp(&st4_1, &st4_2, sizeof(st4_1))); 416 | 417 | ObjStruct8 st8_1 = { 1 }; 418 | ObjStruct8 st8_2 = [o method29:st8_1]; 419 | XCTAssertTrue(0 == memcmp(&st8_1, &st8_2, sizeof(st8_1))); 420 | 421 | ObjStruct16 st16_1 = { 1, 2 }; 422 | ObjStruct16 st16_2 = [o method30:st16_1]; 423 | XCTAssertTrue(0 == memcmp(&st16_1, &st16_2, sizeof(st16_1))); 424 | 425 | ObjStruct24 st24_1 = { 1, 2, 3 }; 426 | ObjStruct24 st24_2 = [o method31:st24_1]; 427 | XCTAssertTrue(0 == memcmp(&st24_1, &st24_2, sizeof(st24_1))); 428 | 429 | ObjStruct32 st32_1 = { 1, 2, 3, 4 }; 430 | ObjStruct32 st32_2 = [o method32:st32_1]; 431 | XCTAssertTrue(0 == memcmp(&st32_1, &st32_2, sizeof(st32_1))); 432 | } 433 | 434 | @end 435 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | OCMethodTrace - Trace Any Objective-C Method Calls 2 | =========================== 3 | 跟踪打印Objective-C任何实例(类)方法 4 | 5 | ## 功能: 6 | 1. 支持任意OC实例(类)方法的跟踪打印 7 | 2. 支持多架构(arm32,arm64,i386,x86_64) 8 | 3. 支持跟踪父类和子类相同的方法 9 | 4. 支持各种灵活配置,包括方法黑白名单等 10 | 11 | ## 配置说明: 12 | ### 全局配置: 13 | #### LogLevel:Trace日志级别,对应MDTraceLogLevel,必须定义,默认值:0 14 | 0:MDTraceLogLeveError,错误级别 15 | 1:MDTraceLogLeveDebug,调试级别 16 | 17 | #### LogWhen:Trace日志输出时机,对应MDTraceLogWhen,必须定义,默认值:0 18 | 0:MDTraceLogWhenStartup ,启动即输出日志 19 | 1:MDTraceLogWhenVolume ,根据音量键控制输出日志(增加音量:输出日志;降低音量:关闭日志;默认时关闭日志) 20 | 2:MDTraceLogWhenRegexString ,输出日志包含匹配字符串才输出日志 21 | 22 | LogRegexString:日志正则匹配字符串,仅当logWhen=MDTraceLogWhenRegexString有效。日志包含指定正则字符串才输出日志 23 | 24 | #### TraceFlag:Trace控制位(尽量在该处扩展),对应MDTraceFlag,全局必须定义,默认值:0x02,指定类可选定义,默认值:0x00 25 | 0x01:MDTraceFlagDoesNotUseDescription,跳过调用对象description方法,避免不正规的description实现导致递归 26 | 0x02:MDTraceFlagDumpClassListInfo,打印类列表信息,便于调试 27 | 0x04:MDTraceFlagDumpClassMethod,打印某个类的方法(不包括父类方法),便于调试 28 | 0x08:MDTraceFlagDumpSuperClassMethod,打印某个类的父类方法(包括递归父类的方法),便于调试 29 | 30 | #### TraceObject:Trace对象,对应TraceObject,必须定义,默认值:1 31 | 0:MDTraceObjectNone,屏蔽trace所有类 32 | 1:MDTraceObjectCoreClass,trace引擎指定类的方法(仅测试验证使用),仅需要考虑CORE_CLASS_LIST 33 | 2:MDTraceObjectUserClass,trace用户指定类的方法,需要考虑USER_CLASS_LIST + "USER_CLASS_LIST和CORE_CLASS_LIST交集" 34 | 3:MDTraceObjectRegexClass,trace用户指定类 + 正则匹配类的方法,需要考虑USER_CLASS_LIST + "USER_CLASS_LIST和CORE_CLASS_LIST交集" + "匹配ClassRegexString的CLASS_LIST和CORE_CLASS_LIST交集" 35 | 36 | MDTraceObjectUserClass和MDTraceObjectRegexClass的区别: 37 | * MDTraceObjectUserClass:在考虑USER_CLASS_LIST指定的类的时候,如果USER_CLASS_LIST和CORE_CLASS_LIST存在部分交集,交集对应的类需要合并,合并算法详见:+[MDTraceClassInfo mergeInfoWithCoreInfo:userInfo:userInfo:] 38 | * MDTraceObjectRegexClass:在考虑MDTraceObjectUserClass的基础上,再考虑ClassRegexString正则匹配的类。 39 | 40 | 处理TraceObject的详细算法见:-[ MDMethodTrace parseClassListInfo],算法内部注意优先级顺序:core > user > regex 41 | 42 | ### 指定类配置: 43 | #### USER_CLASS_LIST:用户指定类列表,必须定义 44 | TraceMode: Trace模式,对应MDTraceMode,可选定义,默认值:1 45 | 0:MDTraceModeOff,屏蔽trace方法 46 | 1:MDTraceModeAll,trace所有方法 47 | 2:MDTraceModeIncludeWhiteList,trace包含"白名单方法列表"的方法 48 | 3:MDTraceModeExcludeBlackList,trace排除"黑名单方法列表"的方法 49 | TraceFlag:定义同全局配置,但是粒度更小,属于某个类范围内,可选定义,默认值:0x00 50 | MethodWhiteList:白名单方法列表,可选定义,但MDTraceModeIncludeWhiteList时必须定义 51 | MethodBlackList:黑名单方法列表,可选定义,但MDTraceModeExcludeBlackList时必须定义 52 | 53 | 跟踪某些类的时候,该类的某些方法有可能会导致app崩溃。这时候,你可以通过多次的试验,找出导致崩溃的方法,使用黑名单模式,然后把这些异常方法放在黑名单方法列表中。当然,如果你并不需要跟踪该类全部的方法的话,也可以使用白名单模式。 54 | 55 | #### CORE_CLASS_LIST:引擎指定类列表,必须定义 56 | TraceMode:定义同USER_CLASS_LIST的TraceMode,但是仅支持MDTraceModeOff和MDTraceModeExcludeBlackList 57 | TraceFlag:定义同全局配置,但是粒度更小,属于某个类范围内,可选定义,默认值:0x00 58 | MethodBlackList:黑名单方法列表,可选定义,但MDTraceModeExcludeBlackList时必须定义 59 | 60 | #### ClassRegexString:类正则匹配字符串,仅TraceObject=MDTraceObjectRegexClass有效 61 | 每个匹配的类使用默认类配置(可以理解成一个默认的User类)。使用场景:在不确定trace哪些类的情况下,批量trace一些有规律命名方式的类,匹配字符串如:“Notification|^CM|(F|f)igCaptureSource” 62 | 63 | 64 | #### USER_CLASS_LIST和CORE_CLASS_LIST的关系: 65 | * CORE_CLASS_LIST:存在的意义在于,OCMethodTrace框架(引擎)内部也需要调用一些OC类方法,为了保证整个框架可以跑起来,避免trace到框架内部使用到的类导致的打印递归循环,所以,需排除这些指定类的全部方法或者部分方法。 66 | * USER_CLASS_LIST:用户自己指定的类,可以任意发挥。但是如果CORE_CLASS_LIST有相同的类,会优先CORE_CLASS_LIST中的配置。合并算法详见"+[MDTraceClassInfo mergeInfoWithCoreInfo:userInfo:userInfo:]" 67 | 68 | ## 技术要点: 69 | ### 支持跟踪父类和子类相同的方法 70 | SatanWoo提出了一个方法(见感谢2),使用“runtime的消息转发机制+跳板原理(桥)”实现,流程: 71 | 每个方法置换到不同的IMP桥上 -> 从桥上反推出当前的调用关系(class和selector)-> 构造一个中间态新名字 -> forwardingTargetForSelector(self, 中间态新名字) 。 72 | 但是SatanWoo的demo(见感谢3)有两个比较大的缺陷: 73 | * 仅支持arm64平台,不支持多架构; 74 | * 仅支持实例方法,不支持类方法。 75 | 为此我做了一些改进,如下: 76 | * 支持多架构:参考objc4(见感谢4)的objc-block-trampolines.m,编写了一个通用的跳板实现,其中通过汇编支持多架构。 77 | * 支持类方法:NSObject (OCMethodTrace)需hook类方法"+[NSObject forwardingTargetForSelector:]" 78 | ### Trace输出无限循环递归的特殊处理 79 | 递归调用都发生在"-[OCMethodTrace omt_forwardInvocation:]"函数内部,有两个地方: 80 | * 对象调用自身的description导致的无限递归:为了打印对象的描述,需要调用对象的description方法,而该方法内部很可能会调用对象的其它内部方法,继而递归又调用"-[OCMethodTrace omt_forwardInvocation:]",于是无限循环。当前解决的方法是:在"-[OCMethodTrace omt_forwardInvocation:]"开始处通过遍历函数堆栈链查找"OMTBlockRunBeforeSelector","OMTBlockRunAfterSelector"和"OMTDescriptionWithTargetSelector"这几个方法,可以知道是否存在递归,发现递归就不要再调用runBefore和runAfter回调即可。缺点:遍历函数堆栈链的方法实在不优雅,而且性能也受影响,需要优化! 81 | * OCMethodTrace框架使用到的基础类导致的无限递归:比如"-[OCMethodTrace omt_forwardInvocation:]"函数范围内用到一些类:如NSDate,NSString或者runBefore或runAfter回调用到的方法等,一旦他们也被trace,也会导致循环递归。解决的思路是:把这些类放在黑名单里,不再trace。当然还有降低循环递归的方式是,内部实现尽量使用C或者C++实现。 82 | 83 | ## TODO: 84 | 1. 通过遍历堆栈链,判断是否存在循环递归调用的算法性能太差,需优化。 85 | 2. 开启全局dump类方法(MDTraceFlagDumpClassMethod)容易导致异常,不建议开启。 86 | 87 | ## 感谢: 88 | 1. https://github.com/qhd/ANYMethodLog 89 | 2. https://satanwoo.github.io/2017/09/24/mainthreadchecker1 90 | 3. https://github.com/SatanWoo/MainThreadChecker.git 91 | 4. https://opensource.apple.com/tarballs/objc4/objc4-723.tar.gz 92 | 93 | 欢迎提Issues和Pull requests,如有问题,可以联系 Michael Chen (omxcodec@gmail.com) 94 | --------------------------------------------------------------------------------