【Android 内存优化】 native内存泄漏监控方案源码分析-hook的实现

leak_monitor.cpp

bool LeakMonitor::Install(std::vector<std::string> *selected_list,
                          std::vector<std::string> *ignore_list) {
  KCHECK(!has_install_monitor_);

  // Reinstall can't hook again
  if (has_install_monitor_) {
    return true;
  }

  memory_analyzer_ = std::make_unique<MemoryAnalyzer>();
  if (!memory_analyzer_->IsValid()) {
    ALOGE("memory_analyzer_ NOT Valid");
    return false;
  }

  std::vector<const std::string> register_pattern = {"^/data/.*\\.so$"};
  std::vector<const std::string> ignore_pattern = {".*/libkoom-native.so$",
                                                   ".*/libxhook_lib.so$"};

  if (ignore_list != nullptr) {
    for (std::string &item : *ignore_list) {
      ignore_pattern.push_back(".*/" + item + ".so$");
    }
  }
  if (selected_list != nullptr && !selected_list->empty()) {
    // only hook the so in selected list
    register_pattern.clear();
    for (std::string &item : *selected_list) {
      register_pattern.push_back("^/data/.*/" + item + ".so$");
    }
  }
  std::vector<std::pair<const std::string, void *const>> hook_entries = {
      std::make_pair("malloc", reinterpret_cast<void *>(WRAP(malloc))),
      std::make_pair("realloc", reinterpret_cast<void *>(WRAP(realloc))),
      std::make_pair("calloc", reinterpret_cast<void *>(WRAP(calloc))),
      std::make_pair("memalign", reinterpret_cast<void *>(WRAP(memalign))),
      std::make_pair("posix_memalign",
                     reinterpret_cast<void *>(WRAP(posix_memalign))),
      std::make_pair("free", reinterpret_cast<void *>(WRAP(free)))};

  if (HookHelper::HookMethods(register_pattern, ignore_pattern, hook_entries)) {
    has_install_monitor_ = true;
    return true;
  }

  HookHelper::UnHookMethods();
  live_alloc_records_.Clear();
  memory_analyzer_.reset(nullptr);
  ALOGE("%s Fail", __FUNCTION__);
  return false;
}

这个函数前面通过正则表达式定义了需要hook和不需要hook的动态库。

后续就是把需要hook的系统内存管理函数put到容器里面,接着传给HookHelper去实现hook。

hook_helper.cpp

bool HookHelper::HookMethods(
    std::vector<const std::string> &register_pattern,
    std::vector<const std::string> &ignore_pattern,
    std::vector<std::pair<const std::string, void *const>> &methods) {
  if (register_pattern.empty() || methods.empty()) {
    ALOGE("Hook nothing");
    return false;
  }

  register_pattern_ = std::move(register_pattern);
  ignore_pattern_ = std::move(ignore_pattern);
  methods_ = std::move(methods);
  DlopenCb::GetInstance().AddCallback(Callback);
  return HookImpl();
}

void HookHelper::Callback(std::set<std::string> &, int, std::string &) {
  HookImpl();
}

做了一些数据准备工作,顺便加了个回调,便于后续的hook操作。
来看下HookImpl


bool HookHelper::HookImpl() {
  pthread_mutex_lock(&DlopenCb::hook_mutex);
  xhook_clear();
  for (auto &pattern : register_pattern_) {
    for (auto &method : methods_) {
      if (xhook_register(pattern.c_str(), method.first.c_str(), method.second,
                         nullptr) != EXIT_SUCCESS) {
        ALOGE("xhook_register pattern %s method %s fail", pattern.c_str(),
              method.first.c_str());
        pthread_mutex_unlock(&DlopenCb::hook_mutex);
        return false;
      }
    }
  }

  for (auto &pattern : ignore_pattern_) {
    for (auto &method : methods_) {
      if (xhook_ignore(pattern.c_str(), method.first.c_str()) != EXIT_SUCCESS) {
        ALOGE("xhook_ignore pattern %s method %s fail", pattern.c_str(),
              method.first.c_str());
        pthread_mutex_unlock(&DlopenCb::hook_mutex);
        return false;
      }
    }
  }

  int ret = xhook_refresh(0);
  pthread_mutex_unlock(&DlopenCb::hook_mutex);
  return ret == 0;
}

这里就是hook的调用了,使用了爱奇艺的开源框架xhook。

看下实现类:
dlopencb.cpp

int Callback(struct dl_phdr_info *info, size_t size, void *data) {
  auto *pair = static_cast<std::pair<std::set<std::string> *, std::set<std::string> *> *>(data);
  auto origin = pair->first;
  auto add = pair->second;
  auto name = info->dlpi_name;
  if (name != nullptr && hookDlopen(name) && origin->insert(name).second) {
    add->insert(name);
  }
  return 0;
}

Callback 函数是一个回调函数,它用于迭代动态链接器的程序头部信息。它的功能如下:

接受三个参数:struct dl_phdr_info* info,size_t size,void* data。
将 data 转换为 std::pair<std::setstd::string, std::setstd::string> 类型的指针。
从 pair 中获取 origin(原始共享库集合)和 add(新增共享库集合)。
判断动态链接库的名称是否非空,并且是否需要 hookDlopen。如果是,则将其添加到 origin 集合,并且添加到 add 集合中。

dlopencb.cpp

void DlopenCb::Refresh(int source, std::string &loadLibName) {
//一开始输出日志,表示刷新操作开始。
  XH_LOG_INFO("Refresh start %d", source);
  //接着创建一个空的 addLibs 集合,用于存储新增的共享库。
  std::set<std::string> addLibs;
  pthread_mutex_lock(&add_lib_mutex);
  //获取 hooked_libs 和 addLibs 的指针对,并调用 dl_iterate_phdr 函数进行迭代,每次迭代调用 Callback 函数。
  auto callbackData =
      make_pair(&hooked_libs, &addLibs);
  dl_iterate_phdr(Callback, &callbackData);
  pthread_mutex_unlock(&add_lib_mutex);

//如果 addLibs 集合不为空,则对 hook_mutex 进行加锁,清除现有的 xhook 钩子,并根据新增的共享库重新注册钩子。
  if (!addLibs.empty()) {
    pthread_mutex_lock(&hook_mutex);
    xhook_clear();
    //根据调试模式进行设置。
    if (is_debug) {
      xhook_enable_sigsegv_protection(0);
      xhook_enable_debug(1);
    } else {
      xhook_enable_sigsegv_protection(1);
    }
    for (const auto &lib : addLibs) {
      auto lib_ctr = lib.c_str();
      xhook_register(lib_ctr, "android_dlopen_ext", (void *) (HookDlopenExt), nullptr);
//      xhook_register(lib_ctr, "dlopen", (void *) (HookDlopen), nullptr);
//输出日志,表示新增的共享库已添加。
      XH_LOG_INFO("Refresh new lib added %s", lib_ctr);
    }
    //刷新 xhook 钩子。
    xhook_refresh(0);
    pthread_mutex_unlock(&hook_mutex);

    // notify
    XH_LOG_INFO("Refresh hooked");
    pthread_mutex_lock(&callback_mutex);
     //对回调函数进行通知,传递新增的共享库信息。
    for (auto &callback:callbacks) {
      callback(addLibs, source, loadLibName);
    }
    pthread_mutex_unlock(&callback_mutex);
  } else {//如果 addLibs 集合为空,则输出日志,表示没有发现新增的共享库。
    XH_LOG_INFO("Refresh no lib found");
  }
}

前面该宏定义:

// 兼容编译失败,实际API 21以下不支持开启
#if __ANDROID_API__ < 21
void* android_dlopen_ext(const char* __filename, int __flags, const android_dlextinfo* __info) {
    return 0;
}
int dl_iterate_phdr(int (*__callback)(struct dl_phdr_info*, size_t, void*), void* __data) {
  return 0;
}
#endif

这段代码是一个条件编译,它检查了当前的 Android API 版本是否低于 21。如果当前的 Android API 版本低于 21,则定义了两个函数 android_dlopen_ext 和 dl_iterate_phdr,但这两个函数的实现只是简单地返回了 0,所以在 API 21 以下的版本中这两个函数不支持。

至于怎么理解这两个函数。

android_dlopen_ext和dl_iterate_phdr


void* android_dlopen_ext(const char* __filename, int __flags, const android_dlextinfo* __info) :

这是一个函数声明,它声明了一个名为 android_dlopen_ext 的函数,该函数用于以扩展方式动态加载共享库(动态链接库)。

参数说明如下:

  • __filename:这是一个指向要加载的共享库文件名的 C 字符串。
  • __flags:这是一个整数,用于指定加载共享库的选项标志。
  • __info:这是一个指向 android_dlextinfo 结构体的指针,该结构体用于传递扩展加载选项的详细信息。如果不需要传递额外信息,可以传入 nullptr

函数返回一个 void* 类型的指针,该指针通常用于表示加载的共享库的句柄或者标识符。在这个声明中,函数总是返回 0,表示加载失败或出错。

android_dlopen_ext 函数的具体实现通常由 Android 系统提供,它是 Android 平台上 dlopen 函数的一个扩展版本,用于支持更多的加载选项和功能。

int dl_iterate_phdr(int (*__callback)(struct dl_phdr_info*, size_t, void*), void* __data)

这是一个函数声明,它声明了一个名为 dl_iterate_phdr 的函数,该函数是用来迭代动态链接器的程序头部信息的。这个函数通常在操作系统中用于获取运行时链接器(Runtime Linker)加载的动态链接库的信息。

参数说明如下:

  • __callback: 这是一个函数指针,指向一个函数,该函数用于处理迭代过程中获取的动态链接库的信息。它接受三个参数:
    • struct dl_phdr_info*: 这是一个结构体指针,用于存储动态链接库的程序头部信息。
    • size_t: 这是一个表示结构体的大小的参数。
    • void*: 这是一个指向用户自定义数据的指针,可以在回调函数中使用。
  • __data: 这是一个指向用户自定义数据的指针,会传递给回调函数 __callback
上一篇:指导基于指令的图像编辑通过多模态大型语言模型


下一篇:设计模式(行为型设计模式——状态模式)