从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);
 }
相关推荐
Eastsea.Chen1 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年8 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
周三有雨10 小时前
【面试题系列Vue07】Vuex是什么?使用Vuex的好处有哪些?
前端·vue.js·面试·typescript
爱米的前端小笔记10 小时前
前端八股自学笔记分享—页面布局(二)
前端·笔记·学习·面试·求职招聘
好学近乎知o10 小时前
解决sql字符串
面试
建群新人小猿11 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
哔哥哔特商务网11 小时前
一文探究48V新型电气架构下的汽车连接器
架构·汽车
007php00711 小时前
GoZero 上传文件File到阿里云 OSS 报错及优化方案
服务器·开发语言·数据库·python·阿里云·架构·golang
1024小神12 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛12 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee