从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);
 }
相关推荐
奔跑吧 android1 小时前
【android bluetooth 框架分析 02】【Module详解 6】【StorageModule 模块介绍】
android·bluetooth·bt·aosp13·storagemodule
weifont4 小时前
聊一聊Electron中Chromium多进程架构
javascript·架构·electron
田一一一5 小时前
Android framework 中间件开发(三)
android·中间件·framework·jni
国际云,接待7 小时前
云服务器的运用自如
服务器·架构·云计算·腾讯云·量子计算
androidwork10 小时前
掌握 Kotlin Android 单元测试:MockK 框架深度实践指南
android·kotlin
田一一一10 小时前
Android framework 中间件开发(二)
android·中间件·framework
追随远方10 小时前
FFmpeg在Android开发中的核心价值是什么?
android·ffmpeg
好吃的肘子10 小时前
Elasticsearch架构原理
开发语言·算法·elasticsearch·架构·jenkins
编程星空11 小时前
架构与UML4+1视图
架构
神探阿航11 小时前
HNUST湖南科技大学-安卓Android期中复习
android·安卓·hnust