从Matrix-ResourceCanary看内存快照生成-NativeForkAnalyzeProcessor.md

前面我们已经了解了fork子进程dump生成hprof文件的机制,相信大家也清楚了,即然能在native层创建子进程进行dump hprof的操作,那么自然也可以在子进程中同时进行hprof文件的解析,此时应该用哪种Processor呢?没错,NativeForkAnalyzeProcessor。

NativeForkAnalyzeProcessor.process

NativeForkAnalyzeProcessor.process的代码如下所示,可以看到主要的流程如下所示:

  1. HprofFileManager.prepareHprofFile:准备hprof文件
  2. MemoryUtil.dumpAndAnalyze:生成hprof文件内容并解析
  3. publishIssue:报告泄漏问题
  4. hprof.deleteIfExist:删除hprof文件

其中1,3,4我们在前文中已做介绍,不再赘述,接下来看下MemoryUtil.dumpAndAnalyze的实现。

kotlin 复制代码
 override fun process(destroyedActivityInfo: DestroyedActivityInfo): Boolean {
     publishIssue(SharePluginInfo.IssueType.LEAK_FOUND, ResourceConfig.DumpMode.NO_DUMP, destroyedActivityInfo.mActivityName, destroyedActivityInfo.mKey, "no dump", "0")
 ​
     val hprof = safeLetOrNull { HprofFileManager.prepareHprofFile("NFAP", true) } ?: run {
         publishIssue(
             SharePluginInfo.IssueType.LEAK_FOUND,
             ResourceConfig.DumpMode.FORK_ANALYSE,
             destroyedActivityInfo.mActivityName, "[unknown]", "Failed to create hprof file.", "0"
         )
         return true
     }
 ​
     val activity = destroyedActivityInfo.mActivityName
     val key = destroyedActivityInfo.mKey
 ​
     watcher.triggerGc()
 ​
     MatrixLog.i(TAG, "fork dump and analyse")
     val result = MemoryUtil.dumpAndAnalyze(hprof.absolutePath, key, timeout = 600)
     MatrixLog.i(TAG, "fork dump and analyse done")
     if (result.mFailure != null) {
         // Copies file to retry repository and analyzes it again when the screen is locked.
         MatrixLog.i(TAG, "Process failed, move into retry repository.")
         val failure = result.mFailure.toString()
         if (retryRepo?.save(hprof, activity, key, failure) != true) {
             publishIssue(
                 SharePluginInfo.IssueType.ERR_EXCEPTION,
                 ResourceConfig.DumpMode.FORK_ANALYSE,
                 activity, key, failure,
                 result.mAnalysisDurationMs.toString()
             )
         }
     } else {
         if (result.mLeakFound) {
             watcher.markPublished(activity, false)
             result.toString().let {
                 publishIssue(
                     SharePluginInfo.IssueType.LEAK_FOUND,
                     ResourceConfig.DumpMode.FORK_ANALYSE,
                     activity, key, it, result.mAnalysisDurationMs.toString()
                 )
                 MatrixLog.i(TAG, "leak found: $it")
             }
         }
     }
 ​
     hprof.deleteIfExist()
 ​
     return true
 }

MemoryUtil.dumpAndAnalyze

MemoryUtil.dumpAndAnalyze函数实现如上,可以看到其最终调用的是native层实现的forkDumpAndAnalyze函数,该函数代码如下:

c 复制代码
 extern "C"
 JNIEXPORT jint JNICALL
 Java_com_tencent_matrix_resource_MemoryUtil_forkDumpAndAnalyze(JNIEnv *env, jobject,
                                                                jstring java_hprof_path,
                                                                jstring java_result_path,
                                                                jstring java_reference_key,
                                                                jlong timeout) {
     const std::string hprof_path = extract_string(env, java_hprof_path);
     const std::string result_path = extract_string(env, java_result_path);
     const std::string reference_key = extract_string(env, java_reference_key);
 ​
     int task_pid = fork_task("matrix_mem_d&a", timeout);
     if (task_pid != 0) {
         return task_pid;
     } else {
         /* dump */
         execute_dump(hprof_path.c_str());
         /* analyze */
         const std::optional<std::vector<LeakChain>> result =
                 execute_analyze(hprof_path.c_str(), reference_key.c_str());
         if (!result.has_value()) _exit(TC_ANALYZE_ERROR);
         /* serialize result */
         const bool serialized = execute_serialize(result_path.c_str(), result.value());
         if (!serialized) _exit(TC_SERIALIZE_ERROR);
         /* end */
         _exit(TC_NO_ERROR);
     }
 }

其中fork_task,execute_dump和_exit在前文中我们均已介绍,这里重点看下execute_analyze的实现。

execute_analyze

execute_analyze实现代码如下所示:

c 复制代码
 static std::optional<std::vector<LeakChain>>
 execute_analyze(const char *hprof_path, const char *reference_key) {
     _info_log(TAG, "task_process %d: analyze", getpid());
 ​
     update_task_state(TS_ANALYZER_CREATE);
     const int hprof_fd = open(hprof_path, O_RDONLY);
     if (hprof_fd == -1) {
         std::stringstream error_builder;
         error_builder << "invoke open() failed on HPROF with errno " << errno;
         on_error(error_builder.str().c_str());
         return std::nullopt;
     }
     HprofAnalyzer::SetErrorListener(analyzer_error_listener);
     // 初始化分析器
     HprofAnalyzer analyzer(hprof_fd);
 ​
     update_task_state(TS_ANALYZER_INITIALIZE);
     if (!exclude_default_references(analyzer)) {
         on_error("exclude default references rules failed");
         return std::nullopt;
     }
 ​
     update_task_state(TS_ANALYZER_EXECUTE);
     // 分析DestroyedActivityInfo实例对象
     return analyzer.Analyze([reference_key](const HprofHeap &heap) {
         const object_id_t leak_ref_class_id = unwrap_optional(
                 heap.FindClassByName(
                         "com.tencent.matrix.resource.analyzer.model.DestroyedActivityInfo"),
                 return std::vector<object_id_t>());
         std::vector<object_id_t> leaks;
         for (const object_id_t leak_ref: heap.GetInstances(leak_ref_class_id)) {
             const object_id_t key_string_id = unwrap_optional(
                     heap.GetFieldReference(leak_ref, "mKey"), continue);
             const std::string &key_string = unwrap_optional(
                     heap.GetValueFromStringInstance(key_string_id), continue);
             if (key_string != reference_key)
                 continue;
             const object_id_t weak_ref = unwrap_optional(
                     heap.GetFieldReference(leak_ref, "mActivityRef"), continue);
             const object_id_t leak = unwrap_optional(
                     heap.GetFieldReference(weak_ref, "referent"), continue);
             leaks.emplace_back(leak);
         }
         return leaks;
     });
 }
解析hprof文件生成HprofHeap
css 复制代码
 // HprofAnalyzer的实现类,可以看到通过parser解析数据
 HprofAnalyzerImpl::HprofAnalyzerImpl(void *data, size_t data_size) :
         data_(data),
         data_size_(data_size),
         parser_(new internal::parser::HeapParser()),
         exclude_matcher_group_() {}
 ​
 // HeapParser的核心实现逻辑在HeapParserEngineImpl
 HeapParser::HeapParser() :
             engine_(new HeapParserEngineImpl()) {}
c 复制代码
 // HeapParserEngineImpl解析hprof文件头
 void HeapParserEngineImpl::ParseHeader(reader::Reader &reader, heap::Heap &heap) const {
     // Version string.
     const std::string version = reader.ReadNullTerminatedString();
     if (version != "JAVA PROFILE 1.0" &&
         version != "JAVA PROFILE 1.0.1" &&
         version != "JAVA PROFILE 1.0.2" &&
         version != "JAVA PROFILE 1.0.3") {
         pub_fatal("invalid HPROF header");
     }
     // Identifier size.
     heap.InitializeIdSize(reader.ReadU4());
     // Skip timestamp.
     reader.SkipU8();
 }
arduino 复制代码
 // 根据不同的tag解析Record内容
 void
 HeapParserEngineImpl::Parse(reader::Reader &reader, heap::Heap &heap,
                             const ExcludeMatcherGroup &exclude_matcher_group,
                             const HeapParserEngine &next) const {
     next.ParseHeader(reader, heap);
 ​
     while (true) {
         const uint8_t tag = reader.ReadU1();
         reader.SkipU4(); // Skip timestamp.
         const uint32_t length = reader.ReadU4();
         switch (tag) {
             case tag::kStrings:
                 next.ParseStringRecord(reader, heap, length);
                 break;
             case tag::kLoadClasses:
                 next.ParseLoadClassRecord(reader, heap, length);
                 break;
             case tag::kHeapDump:
             case tag::kHeapDumpSegment:
                 next.ParseHeapContent(reader, heap, length, exclude_matcher_group, next);
                 break;
             case tag::kHeapDumpEnd:
                 goto break_read_loop;
             default:
                 reader.Skip(length);
                 break;
         }
     }
     break_read_loop:
     next.LazyParse(heap, exclude_matcher_group);
 }
查找Gc Root path
arduino 复制代码
 std::map<heap::object_id_t, std::vector<std::pair<heap::object_id_t, std::optional<heap::reference_t>>>>
 find_leak_chains(const heap::Heap &heap, const std::vector<heap::object_id_t> &tracked) {
 ​
     /* Use Breadth-First(广度优先遍历算法) Search algorithm to find all the references to tracked objects. */
     std::map<heap::object_id_t, std::vector<std::pair<heap::object_id_t, std::optional<heap::reference_t>>>> ret;
 ​
     for (const auto &leak: tracked) {
         std::map<heap::object_id_t, ref_node_t> traversed;
         std::deque<ref_node_t> waiting;
 ​
         for (const heap::object_id_t gc_root: heap.GetGcRoots()) {
             ref_node_t node = {
                     .referent_id = gc_root,
                     .super = std::nullopt,
                     .depth = 0
             };
             traversed[gc_root] = node;
             waiting.push_back(node);
         }
 ​
         bool found = false;
         while (!waiting.empty()) {
             const ref_node_t node = waiting.front();
             waiting.pop_front();
             const heap::object_id_t referrer_id = node.referent_id;
             if (heap.GetLeakReferenceGraph().count(referrer_id) == 0) continue;
             for (const auto &[referent, reference]: heap.GetLeakReferenceGraph().at(referrer_id)) {
                 try {
                     if (traversed.at(referent).depth <= node.depth + 1) continue;
                 } catch (const std::out_of_range &) {}
                 ref_node_t next_node = {
                         .referent_id = referent,
                         .super = ref_super_t{
                                 .referrer_id = referrer_id,
                                 .reference = reference
                         },
                         .depth = node.depth + 1
                 };
                 traversed[referent] = next_node;
                 if (leak == referent) {
                     found = true;
                     goto traverse_complete;
                 } else {
                     waiting.push_back(next_node);
                 }
             }
         }
         traverse_complete:
         if (found) {
             ret[leak] = std::vector<std::pair<heap::object_id_t, std::optional<heap::reference_t>>>();
             std::optional<heap::object_id_t> current = leak;
             std::optional<heap::reference_t> current_reference = std::nullopt;
             while (current != std::nullopt) {
                 ret[leak].push_back(std::make_pair(current.value(), current_reference));
                 const auto &super = traversed.at(current.value()).super;
                 if (super.has_value()) {
                     current = super.value().referrer_id;
                     current_reference = super.value().reference;
                 } else {
                     current = std::nullopt;
                 }
             }
             std::reverse(ret[leak].begin(), ret[leak].end());
         }
     }
 ​
     return std::move(ret);
 }
相关推荐
飞的肖19 分钟前
前端使用 Element Plus架构vue3.0实现图片拖拉拽,后等比压缩,上传到Spring Boot后端
前端·spring boot·架构
ThisIsClark30 分钟前
【后端面试总结】MySQL主从复制逻辑的技术介绍
mysql·面试·职场和发展
小屁不止是运维38 分钟前
麒麟操作系统服务架构保姆级教程(五)NGINX中间件详解
linux·运维·服务器·nginx·中间件·架构
程序猿进阶1 小时前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
Hacker_Fuchen2 小时前
天融信网络架构安全实践
网络·安全·架构
ProtonBase2 小时前
如何从 0 到 1 ,打造全新一代分布式数据架构
java·网络·数据库·数据仓库·分布式·云原生·架构
LCG元9 小时前
【面试问题】JIT 是什么?和 JVM 什么关系?
面试·职场和发展
工业甲酰苯胺11 小时前
分布式系统架构:服务容错
数据库·架构
拭心11 小时前
Google 提供的 Android 端上大模型组件:MediaPipe LLM 介绍
android