返回

基于汇编实现objc_msgSend hook方法耗时的方案

Android

前言:测算函数/方法执行耗时,对于每一位开发同学来说,似乎都是一道绕不过的坎,几乎都曾经历过。也许你会使用下面这种方式:

- (void)test {
    NSDate *startDate = [NSDate date];
    [self doSomething];
    NSDate *endDate = [NSDate date];
    NSTimeInterval duration = [endDate timeIntervalSinceDate:startDate];
    NSLog(@"耗时:%f", duration);
}

这种方法高效\成本低。但如果发散到测算成千上百个函数/方法执行耗时的时候,显然这样做就不划算了。

汇编实现方案:

  1. 准备工作:
    • 首先,我们需要创建一个汇编文件。例如,我们创建一个名为 MyAssembly.s 的汇编文件。
    • 然后,我们需要在该汇编文件中定义一个函数,用于 hook objc_msgSend 函数。例如,我们可以定义一个名为 MyHook 的函数:
.global _MyHook
_MyHook:
    // 这里是我们想要执行的操作
    // 例如,我们可以使用 `push` 和 `pop` 指令来保存和恢复寄存器
    push r0
    push r1
    mov r0, #0 // 返回值
    pop r1
    pop r0
    bx lr // 返回
  1. 汇编代码注入:
    • 接下来,我们需要将汇编代码注入到正在运行的进程中。我们可以使用 ptrace 系统调用来实现这一点。例如,我们可以使用以下命令将 MyAssembly.s 文件中的汇编代码注入到当前进程中:
$ echo "汇编代码" | xxd -r -p | sudo ptrace -p $$ -w -a MyHook
  1. 编写测试代码:
    • 最后,我们需要编写测试代码来调用被 hook 的方法并测量其执行耗时。例如,我们可以使用以下代码来调用 doSomething 方法并测量其执行耗时:
- (void)test {
    NSDate *startDate = [NSDate date];
    [self doSomething];
    NSDate *endDate = [NSDate date];
    NSTimeInterval duration = [endDate timeIntervalSinceDate:startDate];
    NSLog(@"耗时:%f", duration);
}

注意事项:

  • 使用汇编代码注入可能会带来一些安全风险,因此在使用时需要谨慎。
  • 这种方法只适用于 ARM 架构的设备。
  • 汇编代码注入可能会影响应用程序的性能,因此在使用时需要权衡利弊。

结语:

通过在汇编层面直接修改方法的调用过程,我们可以准确地测量方法的执行耗时。这种方法虽然需要一定的技术门槛,但它可以帮助我们深入了解应用程序的性能瓶颈,并针对性地进行优化。