View memory snapshot generation from Matrix-ResourceCanary-NativeForkAnalyzeProcessor.md

We have already understood the mechanism of dumping a fork sub-process to generate an hprof file. I believe everyone is also aware that since a sub-process can be created in the native layer to perform the dump hprof operation, it is natural that the hprof file can also be parsed in the sub-process at the same time. , which Processor should be used at this time? That's right, NativeForkAnalyzeProcessor.

NativeForkAnalyzeProcessor.process

The code of NativeForkAnalyzeProcessor.process is as follows. You can see that the main process is as follows:

  1. HprofFileManager.prepareHprofFile: Prepare hprof file
  2. MemoryUtil.dumpAndAnalyze: Generate hprof file content and parse it
  3. publishIssue: Report a leak issue
  4. hprof.deleteIfExist: delete hprof file

Among them, 1, 3, and 4 have been introduced in the previous article and will not be repeated. Next, let’s look at the implementation of MemoryUtil.dumpAndAnalyze.

 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

image-20230827215322564

The MemoryUtil.dumpAndAnalyze function is implemented as above. You can see that it ultimately calls the forkDumpAndAnalyze function implemented in the native layer. The function code is as follows:

 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);
     }
 }

Among them, fork_task, execute_dump and _exit have been introduced in the previous article. Here we focus on the implementation of execute_analyze.

execute_analyze

The execute_analyze implementation code is as follows:

 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;
     });
 }

Parse the hprof file to generate HprofHeap
 // 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()) {}

 // 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();
 }

 // 根据不同的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);
 }

Find Gc Root path
 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);
 }

at last

If you want to become an architect or want to break through the 20-30K salary range, then don't be limited to coding and business, you must be able to select and expand, and improve your programming thinking. In addition, good career planning is also very important, and learning habits are important, but the most important thing is to be able to persevere. Any plan that cannot be implemented consistently is empty talk.

If you have no direction, here is a set of "Advanced Notes on the Eight Modules of Android" written by a senior architect at Alibaba to help you systematically organize messy, scattered, and fragmented knowledge, so that you can systematically and efficiently Master various knowledge points of Android development.
Insert image description here
Compared with the fragmented content we usually read, the knowledge points in this note are more systematic, easier to understand and remember, and are strictly arranged according to the knowledge system.

Full set of video materials:

1. Interview collection

Insert image description here
2. Source code analysis collection
Insert image description here

3. The collection of open source frameworks
Insert image description here
welcomes everyone to support with one click and three links. If you need the information in the article, just click the CSDN official certification WeChat card at the end of the article to get it for free↓↓↓

Guess you like

Origin blog.csdn.net/Eqiqi/article/details/132733774