DTracing objc_msgSend 不打印接收器类名称
Posted
技术标签:
【中文标题】DTracing objc_msgSend 不打印接收器类名称【英文标题】:DTracing objc_msgSend doesn't print the receiver class name 【发布时间】:2014-03-06 08:26:59 【问题描述】:我正在使用 dtrace 打印代码中的所有 objc_msgSend。到目前为止,我可以看到选择器的名称,但无法获得正确的类名。
这是我的 dtrace 脚本:
#!/usr/sbin/dtrace -qs
pid$target::objc_msgSend:entry
self->isa = *(long *)copyin(arg0, 8);
printf("-[%s %s]\n",
copyinstr(*(long *)copyin(self->isa + 16, 8)),
copyinstr(arg1));
我假设 id 接收器对象具有以下结构:
typedef struct objc_class
struct objc_class *isa;
struct objc_class *super_class;
char *name;
...
在我的脑海中,为了到达名称,指针必须移动 2 * sizeof(objc_class*) 等于 16,我们得到大小为 8 的名称的指针。因此我希望看到类名但是我打印了一些垃圾。
对我做错了什么有任何想法吗?
我的系统是 Mavericks x64。
【问题讨论】:
struct objc_class
的定义在任何地方都不正确,除了 32 位 Mac。
你知道正确的吗?
【参考方案1】:
在查看Obj-C runtime source code for 64 bit architecture 和“objc-private.h”文件之后,这是从类指针中获取类名的“公式”:
#define RW_REALIZED (1<<31)
#define RW_FUTURE (1<<30)
#define CLASS_FAST_FLAG_MASK 3
#define TAG_MASK 1
#define TAG_SLOT_SHIFT 0
#define TAG_SLOT_MASK 0xf
extern "C" Class objc_debug_taggedpointer_classes[]; // Available in 10.9 for tagged pointers decoding
static const char* ClassNameFromInstance(id instance)
char* ptr0 = (char*)instance;
char* ptr1;
if ((long)ptr0 & TAG_MASK)
long slot = ((long)ptr0 >> TAG_SLOT_SHIFT) & TAG_SLOT_MASK;
ptr1 = (char*)objc_debug_taggedpointer_classes[slot]; // struct objc_class pointer
else
ptr1 = *(char**)ptr0; // struct objc_class pointer i.e. instance ISA
char* ptr2 = *((char**)(((long)ptr1 + 32) & ~CLASS_FAST_FLAG_MASK)); // struct class_ro_t or struct class_rw_t pointer
uint32_t flags = *((uint32_t*)ptr2); // struct class_ro_t or struct class_rw_t flags
char* ptr3;
if ((flags & RW_REALIZED) || (flags & RW_FUTURE))
ptr3 = *((char**)((long)ptr2 + 8)); // struct class_ro_t pointer from struct class_rw_t pointer
else
ptr3 = ptr2; // struct class_ro_t pointer same as struct class_rw_t pointer
const char* name = *((char**)((long)ptr3 + 24)); // Name string pointer from struct class_ro_t pointer
return name;
在 dtrace 中成为记录 Obj-C 对象创建和销毁的示例脚本:
#!/usr/bin/env dtrace -s
#pragma D option quiet
pid$target:libobjc.A.dylib:class_createInstance:entry
ptr1 = *(long*)copyin(arg0, 8); /* arg0 is Class pointer */
ptr2 = *(long*)copyin((ptr1 + 32) & ~3, 8);
flags = *(int*)copyin(ptr2, 4);
ptr3 = (flags & (1 << 31)) || (flags & (1 << 30)) ? *(long*)copyin(ptr2 + 8, 8) : ptr2;
ptr4 = *(long*)copyin(ptr3 + 24, 8);
self->class = copyinstr(ptr4);
pid$target:libobjc.A.dylib:class_createInstance:return
printf("[+] %s = %p\n", self->class, arg1); /* arg1 is instance pointer */
self->class = 0;
pid$target:libobjc.A.dylib:object_dispose:entry
/arg0 != 0/
ptr0 = *(long*)copyin(arg0, 8); /* arg0 is instance pointer */
ptr1 = *(long*)copyin(ptr0, 8); /* TODO: Handle tagged pointers */
ptr2 = *(long*)copyin((ptr1 + 32) & ~3, 8);
ptr3 = (flags & (1 << 31)) || (flags & (1 << 30)) ? *(long*)copyin(ptr2 + 8, 8) : ptr2;
ptr4 = *(long*)copyin(ptr3 + 24, 8);
class = copyinstr(ptr4);
printf("[-] %s = %p\n", class, arg0);
重要 此 dtrace 脚本不处理 tagged pointers。使用此脚本时,请务必按照here 的说明设置环境变量“DYLD_SHARED_REGION=avoid”。
【讨论】:
以上是关于DTracing objc_msgSend 不打印接收器类名称的主要内容,如果未能解决你的问题,请参考以下文章