// weibo: http://weibo.com/xiaoqing28 // blog: http://www.alonemonkey.com // // LLDBTools.m // MonkeyDev // // Created by AloneMonkey on 2018/3/8. // Copyright © 2018年 AloneMonkey. All rights reserved. // #pragma GCC diagnostic ignored "-Wundeclared-selector" #import "LLDBTools.h" #import #import #import #import #import #import enum { BLOCK_HAS_COPY_DISPOSE = (1 << 25), BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code BLOCK_IS_GLOBAL = (1 << 28), BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE BLOCK_HAS_SIGNATURE = (1 << 30), }; struct Block_literal_1 { void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock int flags; int reserved; void (*invoke)(void *, ...); struct Block_descriptor_1 { unsigned long int reserved; // NULL unsigned long int size; // sizeof(struct Block_literal_1) // optional helper functions void (*copy_helper)(void *dst, void *src); // IFF (1<<25) void (*dispose_helper)(void *src); // IFF (1<<25) // required ABI.2010.3.16 const char *signature; // IFF (1<<30) } *descriptor; // imported variables }; NSString* decode(NSString* code); NSArray* choose_inner(const char * classname); char * protection_bits_to_rwx (vm_prot_t p); const char * unparse_inheritance (vm_inherit_t i); char * behavior_to_text (vm_behavior_t b); NSString* decode(NSString* code){ NSDictionary * encodeMap = @{ @"c": @"char", @"i": @"int", @"s": @"short", @"l": @"long", @"q": @"long long", @"C": @"unsigned char", @"I": @"unsigned int", @"S": @"unsigned short", @"L": @"unsigned long", @"Q": @"unsigned long long", @"f": @"float", @"d": @"double", @"B": @"bool", @"v": @"void", @"*": @"char *", @"@": @"id", @"#": @"Class", @":": @"SEL" }; if(encodeMap[code]){ return encodeMap[code]; }else if([code characterAtIndex:0] == '@'){ if([code characterAtIndex:1] == '?'){ return code; }else if([code characterAtIndex:2] == '<'){ return [NSString stringWithFormat:@"id%@", [[code substringWithRange:NSMakeRange(2, code.length-3)] stringByReplacingOccurrencesOfString:@"><" withString:@", "]]; }else{ return [NSString stringWithFormat:@"%@ *", [code substringWithRange:NSMakeRange(2, code.length-3)]]; } }else if([code characterAtIndex:0] == '^'){ return [NSString stringWithFormat:@"%@ *", decode([code substringFromIndex:1])]; } return code; } NSString* pvc(){ return [[[UIWindow performSelector:@selector(keyWindow)] performSelector:@selector(rootViewController)] performSelector:@selector(_printHierarchy)]; } NSString* pviews(){ return [[[UIApplication sharedApplication] keyWindow] performSelector:@selector(recursiveDescription)]; } NSString* pactions(vm_address_t address){ NSMutableString* result = [NSMutableString new]; UIControl* control = (__bridge UIControl*)(void*)address; NSArray* targets = [[control allTargets] allObjects]; for (id item in targets) { NSArray* actions = [control actionsForTarget:item forControlEvent:0]; [result appendFormat:@"<%s: 0x%lx>: %@\n", object_getClassName(item), (unsigned long)item, [actions componentsJoinedByString:@","]]; } return result; } NSString* pblock(vm_address_t address){ struct Block_literal_1 real = *((struct Block_literal_1 *)(void*)address); NSMutableDictionary *dict = [NSMutableDictionary dictionary]; [dict setObject:[NSNumber numberWithLong:(long)real.invoke] forKey:@"invoke"]; if (real.flags & BLOCK_HAS_SIGNATURE) { char *signature; if (real.flags & BLOCK_HAS_COPY_DISPOSE) { signature = (char *)(real.descriptor)->signature; } else { signature = (char *)(real.descriptor)->copy_helper; } NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes:signature]; NSMutableArray *types = [NSMutableArray array]; [types addObject:[NSString stringWithUTF8String:(char *)[sig methodReturnType]]]; for (NSUInteger i = 0; i < sig.numberOfArguments; i++) { char *type = (char *)[sig getArgumentTypeAtIndex:i]; [types addObject:[NSString stringWithUTF8String:type]]; } [dict setObject:types forKey:@"signature"]; } NSMutableArray* sigArr = dict[@"signature"]; if(!sigArr){ return [NSString stringWithFormat:@"Imp: 0x%lx", [dict[@"invoke"] longValue]]; }else{ NSMutableString* sig = [NSMutableString stringWithFormat:@"%@ ^(", decode(sigArr[0])]; for (int i = 2; i < sigArr.count; i++) { if(i == sigArr.count - 1){ [sig appendFormat:@"%@", decode(sigArr[i])]; }else{ [sig appendFormat:@"%@ ,", decode(sigArr[i])]; } } [sig appendString:@");"]; return [NSString stringWithFormat:@"Imp: 0x%lx Signature: %s", [dict[@"invoke"] longValue], [sig UTF8String]]; } } struct CYChoice { std::set query_; std::set results_; }; struct CYObjectStruct { Class isa_; }; static void choose_(task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned count) { CYChoice *choice(reinterpret_cast(baton)); for (unsigned i(0); i != count; ++i) { vm_range_t &range(ranges[i]); void *data(reinterpret_cast(range.address)); size_t size(range.size); if (size < sizeof(CYObjectStruct)) continue; uintptr_t *pointers(reinterpret_cast(data)); #ifdef __arm64__ Class isa = (__bridge Class)((void *)(pointers[0] & 0x1fffffff8)); #else Class isa =(__bridge Class)(void *)pointers[0]; #endif std::set::const_iterator result(choice->query_.find(isa)); if (result == choice->query_.end()) continue; size_t needed(class_getInstanceSize(*result)); // XXX: if (size < needed) size_t boundary(496); #ifdef __LP64__ boundary *= 2; #endif if ((needed <= boundary && (needed + 15) / 16 * 16 != size) || (needed > boundary && (needed + 511) / 512 * 512 != size)) continue; choice->results_.insert((__bridge id)(data)); } } static Class *CYCopyClassList(size_t &size) { size = objc_getClassList(NULL, 0); Class *data(reinterpret_cast(malloc(sizeof(Class) * size))); for (;;) { size_t writ(objc_getClassList(data, (int)size)); if (writ <= size) { size = writ; return data; } Class *copy(reinterpret_cast(realloc(data, sizeof(Class) * writ))); if (copy == NULL) { free(data); return NULL; } data = copy; size = writ; } } static kern_return_t CYReadMemory(task_t task, vm_address_t address, vm_size_t size, void **data) { *data = reinterpret_cast(address); return KERN_SUCCESS; } NSArray* choose_inner(const char * classname){ Class _class = NSClassFromString([NSString stringWithUTF8String:classname]); vm_address_t *zones = NULL; unsigned size = 0; //获取所有的zone信息 堆上的区域 kern_return_t error = malloc_get_all_zones(mach_task_self(), &CYReadMemory, &zones, &size); assert(error == KERN_SUCCESS); size_t number; Class *classes(CYCopyClassList(number)); assert(classes != NULL); CYChoice choice; //找到目标Class for (size_t i(0); i != number; ++i) for (Class current(classes[i]); current != Nil; current = class_getSuperclass(current)) if (current == _class) { choice.query_.insert(classes[i]); break; } for (unsigned i(0); i != size; ++i) { const malloc_zone_t *zone(reinterpret_cast(zones[i])); if (zone == NULL || zone->introspect == NULL) continue; //枚举堆上的对象 zone->introspect->enumerator(mach_task_self(), &choice, MALLOC_PTR_IN_USE_RANGE_TYPE, zones[i], &CYReadMemory, &choose_); } NSMutableArray * result = [[NSMutableArray alloc] init]; for (auto iter = choice.results_.begin(); iter != choice.results_.end(); iter++) { [result addObject:(id)*iter]; } return result; } NSString* choose(const char* classname){ NSMutableString* result = [NSMutableString new]; NSArray* results = choose_inner(classname); [result appendFormat:@"Find %lu instance objects in memory!\n" , (unsigned long)results.count]; for (id item in results) { [result appendFormat:@"<%s: 0x%llx>\n", object_getClassName(item), (long long)item]; } return result; } NSString* methods(const char * classname){ return [objc_getClass(classname) performSelector:@selector(_shortMethodDescription)]; } NSString* ivars(vm_address_t address){ id target = (__bridge id)(void*)address; return [target performSelector:@selector(_ivarDescription)]; } char * protection_bits_to_rwx (vm_prot_t p){ static char returned[4]; returned[0] = (p & VM_PROT_READ ? 'r' : '-'); returned[1] = (p & VM_PROT_WRITE ? 'w' : '-'); returned[2] = (p & VM_PROT_EXECUTE ? 'x' : '-'); returned[3] = '\0'; // memory leak here. No biggy return (strdup(returned)); } const char * unparse_inheritance (vm_inherit_t i){ switch (i){ case VM_INHERIT_SHARE: return "share"; case VM_INHERIT_COPY: return "copy"; case VM_INHERIT_NONE: return "none"; default: return "???"; } } char * behavior_to_text (vm_behavior_t b){ switch (b){ case VM_BEHAVIOR_DEFAULT: return((char*)"default"); case VM_BEHAVIOR_RANDOM: return((char*)"random"); case VM_BEHAVIOR_SEQUENTIAL: return((char*)"fwd-seq"); case VM_BEHAVIOR_RSEQNTL: return((char*)"rev-seq"); case VM_BEHAVIOR_WILLNEED: return((char*)"will-need"); case VM_BEHAVIOR_DONTNEED: return((char*)"will-need"); case VM_BEHAVIOR_FREE: return((char*)"free-nowb"); case VM_BEHAVIOR_ZERO_WIRED_PAGES: return((char*)"zero-wire"); case VM_BEHAVIOR_REUSABLE: return((char*)"reusable"); case VM_BEHAVIOR_REUSE: return((char*)"reuse"); case VM_BEHAVIOR_CAN_REUSE: return((char*)"canreuse"); default: return ((char*)"?"); } } __BEGIN_DECLS extern kern_return_t mach_vm_region ( vm_map_t target_task, mach_vm_address_t *address, mach_vm_size_t *size, vm_region_flavor_t flavor, vm_region_info_t info, mach_msg_type_number_t *infoCnt, mach_port_t *object_name ); __END_DECLS NSString* vmmap(){ vm_region_basic_info_data_t info, prev_info; mach_vm_address_t address = 1, prev_address; mach_vm_size_t size, prev_size; mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; mach_port_t object_name; int nsubregions = 0; kern_return_t kr = mach_vm_region(mach_task_self(), &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &count, &object_name); NSMutableString* result = [[NSMutableString alloc] init]; if(kr != KERN_SUCCESS){ [result appendFormat:@"mach_vm_region: Error %d - %s", kr, mach_error_string(kr)]; return [result copy]; } //保存之前查到的info memcpy (&prev_info, &info, sizeof (vm_region_basic_info_data_t)); prev_address = address; prev_size = size; nsubregions = 1; while (true) { int print = 0, done = 0; address = prev_address + prev_size; if (address == 0){ print = done = 1; } if (!done){ kr = mach_vm_region (mach_task_self(), &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &count, &object_name); if (kr != KERN_SUCCESS){ [result appendFormat:@"mach_vm_region failed for address %llu - Error: %x\n",address, (kr)]; print = done = 1; } } //等于才是连续的内存,不等于才打印 if (address != prev_address + prev_size) print = 1; //或者权限信息改变了也打印 if ((info.protection != prev_info.protection) || (info.max_protection != prev_info.max_protection) || (info.inheritance != prev_info.inheritance) || (info.shared != prev_info.reserved) || (info.reserved != prev_info.reserved)) print = 1; if (print){ char *print_size_unit = NULL; mach_vm_size_t print_size = prev_size; if (print_size > 1024) { print_size /= 1024; print_size_unit = (char*)"K"; } if (print_size > 1024) { print_size /= 1024; print_size_unit = (char*)"M"; } if (print_size > 1024) { print_size /= 1024; print_size_unit = (char*)"G"; } [result appendFormat:@" %p-%p [%llu%s](%s/%s; %s, %s, %s) %s", (void*)(prev_address), (void*)(prev_address + prev_size), print_size, print_size_unit, protection_bits_to_rwx (prev_info.protection), protection_bits_to_rwx (prev_info.max_protection), unparse_inheritance (prev_info.inheritance), prev_info.shared ? "shared" : "private", prev_info.reserved ? "reserved" : "not-reserved", behavior_to_text (prev_info.behavior)]; if (nsubregions > 1) [result appendFormat:@" (%d sub-regions)", nsubregions]; [result appendFormat:@"\n"]; prev_address = address; prev_size = size; memcpy (&prev_info, &info, sizeof (vm_region_basic_info_data_t)); nsubregions = 1; }else{ prev_size += size; nsubregions++; } if (done) break; } return [result copy]; }