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 不打印接收器类名称的主要内容,如果未能解决你的问题,请参考以下文章

iOS objc_msgSend 崩溃,没有给出报告或警告

objc_msgSend 报错

Xcode12 踩坑之 objc_msgSend

Xcode12 踩坑之 objc_msgSend

IOS objc_msgSend 32位和64位

[OC学习笔记]objc_msgSend:方法快速查找