从Matrix-ResourceCanary看内存快照生成-ForkAnalyseProcessor(1)

news2024/9/24 11:21:46

前文看到AutoDumpProcessor的处理逻辑主要是生成,裁剪hprof文件并回调到PluginListener中,接下来我们来看下ForkAnalyseProcessor的处理逻辑。

ForkAnalyseProcessor

public class ForkAnalyseProcessor extends BaseLeakProcessor {

    private static final String TAG = "Matrix.LeakProcessor.ForkAnalyse";

    public ForkAnalyseProcessor(ActivityRefWatcher watcher) {
        super(watcher);
    }

    @Override
    public boolean process(DestroyedActivityInfo destroyedActivityInfo) {
        ......
        getWatcher().triggerGc();

        if (dumpAndAnalyse(
                destroyedActivityInfo.mActivityName,
                destroyedActivityInfo.mKey
        )) {
            getWatcher().markPublished(destroyedActivityInfo.mActivityName, false);
            return true;
        }
        return false;
    }

    private boolean dumpAndAnalyse(String activity, String key) {

        /* Dump */

        final long dumpStart = System.currentTimeMillis();

        File hprof = null;
        try {
            hprof = HprofFileManager.INSTANCE.prepareHprofFile("FAP", true);
        } catch (FileNotFoundException e) {
            MatrixLog.printErrStackTrace(TAG, e, "");
        }

        if (hprof != null) {
            if (!MemoryUtil.dump(hprof.getPath(), 600)) {
                MatrixLog.e(TAG, String.format("heap dump for further analyzing activity with key [%s] was failed, just ignore.",
                        key));
                return false;
            }
        }

        if (hprof == null || hprof.length() == 0) {
            publishIssue(
                    SharePluginInfo.IssueType.ERR_FILE_NOT_FOUND,
                    ResourceConfig.DumpMode.FORK_ANALYSE,
                    activity, key, "FileNull", "0");
            MatrixLog.e(TAG, "cannot create hprof file");
            return false;
        }

        MatrixLog.i(TAG, String.format("dump cost=%sms refString=%s path=%s",
                System.currentTimeMillis() - dumpStart, key, hprof.getPath()));

        /* Analyse */

        try {
            final long analyseStart = System.currentTimeMillis();

            final ActivityLeakResult leaks = analyze(hprof, key);
            MatrixLog.i(TAG, String.format("analyze cost=%sms refString=%s",
                    System.currentTimeMillis() - analyseStart, key));

            if (leaks.mLeakFound) {
                final String leakChain = leaks.toString();
                publishIssue(
                        SharePluginInfo.IssueType.LEAK_FOUND,
                        ResourceConfig.DumpMode.FORK_ANALYSE,
                        activity, key, leakChain,
                        String.valueOf(System.currentTimeMillis() - dumpStart));
                MatrixLog.i(TAG, leakChain);
            } else {
                MatrixLog.i(TAG, "leak not found");
            }

        } catch (OutOfMemoryError error) {
            publishIssue(
                    SharePluginInfo.IssueType.ERR_ANALYSE_OOM,
                    ResourceConfig.DumpMode.FORK_ANALYSE,
                    activity, key, "OutOfMemoryError",
                    "0");
            MatrixLog.printErrStackTrace(TAG, error.getCause(), "");
        } finally {
            //noinspection ResultOfMethodCallIgnored
            hprof.delete();
        }

        /* Done */

        return true;
    }
}

从上述代码可以看到在ForkAnalyseProcessor中,主要是通过dumpAndAnalyse来处理发现的内存泄漏问题,在该函数内,主要分为以下几步:

  1. prepareHprofFile:创建hprof文件
  2. MemoryUtil.dump:生成hprof文件内容
  3. analyze:分析hprof文件
  4. publishIssue:报告问题
  5. hprof.delete():删除hprof文件

prepareHprofFile

在HprofFileManager.INSTANCE.prepareHprofFile主要是进行hprof文件的预创建工作,包含清理历史文件,确保有足够的存储空间,判断存储空间是否可用,拼接hprof文件名等操作,这里预创建的hprof文件并没有数据内容,prepareHprofFile实现代码如下:

@Throws(FileNotFoundException::class)
fun prepareHprofFile(prefix: String = "", deleteSoon: Boolean = false): File {
    hprofStorageDir.prepare(deleteSoon)
    return File(hprofStorageDir, getHprofFileName(prefix))
}

MemoryUtil.dump

MemoryUtil.dump函数主要完成了hprof文件的真实内容填充工作,代码如下所示:

@JvmStatic
@JvmOverloads
fun dump(
    hprofPath: String,
    timeout: Long = DEFAULT_TASK_TIMEOUT
): Boolean = initSafe { exception ->
    if (exception != null) {
        error("", exception)
        return@initSafe false
    }
    return when (val pid = forkDump(hprofPath, timeout)) {
        -1 -> run {
            error("Failed to fork task executing process.")
            false
        }
        else -> run { // current process
            info("Wait for task process [${pid}] complete executing.")
            val result = waitTask(pid)
            result.exception?.let {
                info("Task process [${pid}] complete with error: ${it.message}.")
            } ?: info("Task process [${pid}] complete without error.")
            return result.exception == null
        }
    }
}

private external fun forkDump(hprofPath: String, timeout: Long): Int
private external fun waitTask(pid: Int): TaskResult

可以看到代码中主要逻辑是执行forkDump获取进程id,如果进程id为-1,则返回false,dump失败,如果进程id不为-1,则执行waitTask方法,如果返回的TaskResult对象中没有异常,说明dump成功,否则失败,而forkDump和waitTask都是native方法,接下来我们一起看下这两函数的实现。

forkDump

MemoryUtil.dump对应的native实现如下所示:

extern "C"
JNIEXPORT jint JNICALL
Java_com_tencent_matrix_resource_MemoryUtil_forkDump(JNIEnv *env, jobject,
                                                     jstring java_hprof_path,
                                                     jlong timeout) {
    const std::string hprof_path = extract_string(env, java_hprof_path);

    int task_pid = fork_task("matrix_mem_dump", timeout);
    if (task_pid != 0) {
        return task_pid;
    } else {
        /* dump生成hprof文件 */
        execute_dump(hprof_path.c_str());
        /* 退出进程 */
        _exit(TC_NO_ERROR);
    }
}
static int fork_task(const char *task_name, unsigned int timeout) {
    auto *thread = current_thread();
    // 调用art::Dbg::SuspendVM()暂停进程运行
    suspend_runtime(thread);
    // fork创建进程
    int pid = fork();
    if (pid == 0) {
        task_process = true;
        if (timeout != 0) {
            alarm(timeout);
        }
        // 设置线程名称
        prctl(PR_SET_NAME, task_name);
    } else {
        // 调用art::Dbg::ResumeVM()恢复进程运行
        resume_runtime(thread);
    }
    return pid;
}

结合注释,我们可以看出这是一段创建子进程并根据子进程pid运行逻辑的代码,那么这一段代码是怎么执行的呢?

要了解上述代码怎么执行的,我们首先应该清楚fork函数创建子进程的作用和特点,针对fork创建的子进程而言,其和父进程拥有相同的内存空间,fork函数返回值如下所示:

image-20230827113118648

可以看出fork在父进程执行时返回所创建子进程的pid信息,在子进程自身执行时返回0,结合代码,可以得到下图:

forkDump.drawio

接下来我们继续来看下子进程execute_dump和_exit的实现。

execute_dump
static void execute_dump(const char *file_name) {
    _info_log(TAG, "task_process %d: dump", getpid());
    update_task_state(TS_DUMP);
    dump_heap(file_name);
}

static void (*dump_heap_)(const char *, int, bool) = nullptr;

void dump_heap(const char *file_name) {
    dump_heap_(file_name, -1, false);
}

// xhook
load_symbol(dump_heap_,
                void(*)(const char *, int, bool ),
                "_ZN3art5hprof8DumpHeapEPKcib",
                "cannot find symbol art::hprof::DumpHeap()")

可以看到execute_dump最终调用的是art::hprof::DumpHeap()方法,Debug.dumpHprofData最终也是通过jni调用该方法,生成hprof文件。

_exit

image-20230827123154594

结合文档可以看出_exit函数主要用于停止进程运行。

waitTask
extern "C" JNIEXPORT jobject JNICALL
Java_com_tencent_matrix_resource_MemoryUtil_waitTask(JNIEnv *env, jobject, jint pid) {
    int status;
    // 通过waitpid等待子进程状态通知
    if (waitpid(pid, &status, 0) == -1) {
        _error_log(TAG, "invoke waitpid failed with errno %d", errno);
        return create_task_result(env, TR_TYPE_WAIT_FAILED, errno, TS_UNKNOWN, "none");
    }

    const int8_t task_state = get_task_state_and_cleanup(pid);
    const std::string task_error = get_task_error_and_cleanup(pid);
    if (WIFEXITED(status)) {
        return create_task_result(env, TR_TYPE_EXIT, WEXITSTATUS(status), task_state, task_error);
    } else if (WIFSIGNALED(status)) {
        return create_task_result(env, TR_TYPE_SIGNALED, WTERMSIG(status), task_state, task_error);
    } else {
        return create_task_result(env, TR_TYPE_UNKNOWN, 0, task_state, task_error);
    }
}

从waitpid阻塞等待获取到子进程退出状态后,将子进程执行结果包装成TaskResult对象返回。

waitpid

waitpid说明如下图,可以看到waitpid用于阻塞当前线程执行,直到给定的pid关联的子进程状态发生改变时唤醒,唤醒后说明子进程已退出,查看子进程退出原因,并返回结果给上层,至此MemoryUtil.dump流程结束。

image-20230827124402500

image-20230827124551875

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/986041.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

$nextTick属性使用与介绍

属性介绍 $nextTick 是 Vue.js 中的一个重要方法,之前我们也说过$ref 等一些重要的属性,这次我们说$nextTick,$nextTick用于在 DOM 更新后执行回调函数。它通常用于处理 DOM 更新后的操作,因为 Vue 在更新 DOM 后不会立即触发回调…

【0906 C高级day1】 Linux中的文件相关指令

一、使用cut截取出Ubuntu用户的家目录,要求:不能使用":"作为分割 grep "ubuntu" /etc/passwd | cut -d "/" -n -f 2-3 | cut -c 1-11 二、思维导图 文件相关指令:

【Java Web】Servlet规范讲解

目录 一、前言 二、Servlet规范介绍 2.1 常见版本及新功能 2.2 Servlet的作用 2.3 Servlet的本质 三、Servlet接口和实现类 3.1 Servlet接口 3.2 Servlet接口实现类示例 3.3 Servlet接口实现类开发步骤 3.3.1 关键点 3.3.2 引入Servlet源码包 1、描述 Servlet接口…

LeetCode 725. Split Linked List in Parts【链表】中等

本文属于「征服LeetCode」系列文章之一,这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁,本系列将至少持续到刷完所有无锁题之日为止;由于LeetCode还在不断地创建新题,本系列的终止日期可能是永远。在这一系列刷题文章…

【python手写算法】逻辑回归实现分类(含公式推导)

公式推导: 代码实现: # codingutf-8 import matplotlib.pyplot as plt import numpy as npdef f(w1,x1,w2,x2,b):zw1*x1w2*x2breturn 1/(1np.exp(-z)) if __name__ __main__:X1 [12.46, 0.25, 5.22, 11.3, 6.81, 4.59, 0.66, 14.53, 15.49, 14.43,2.1…

优先发展非化石能源

生态兴则文明兴。面对气候变化、环境风险挑战、能源资源约束等日益严峻的全球问题,中国树立人类命运共同体理念,促进经济社会发展全面绿色转型,努力推动本国能源清洁低碳发展。 智慧光伏遮阳伞,搭配座椅设置智能补给休息区&#x…

数据结构--- 树

(一)知识补充 定义 树是一种数据结构,它是由n(n≥0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。​ 它具有以下的特点: 每个节点有零个或多个子节点; 没有父节点的节点称为根节点;每一个非根…

unipush2.0实现APP消息推送(1)

一、 基础概念 1.1 首先理解什么是消息推送? 比如:你手机收到未读消息后会在通知栏显示,该条消息便是某APP的消息推送。多用于有需要从服务端主动发出消息提醒到客户端的场景。 1.2 uniPush & 个推 & 厂商推送的区别 方式个推uni…

js摄像头动态检测

利用摄像头每一秒截图一次图像。然后计算2次图像之间的相似度。 如果相似度低于98%就会报警。 var video document.getElementsByClassName(inputvideo)[0]; video.innerHTML "<video classinput_video idcamera autoplay width640px height380px></video>…

LeetCode 1123. Lowest Common Ancestor of Deepest Leaves【树,DFS,BFS,哈希表】1607

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

2023高教社杯数学建模C题思路模型 - 蔬菜类商品的自动定价与补货决策

# 1 赛题 在生鲜商超中&#xff0c;一般蔬菜类商品的保鲜期都比较短&#xff0c;且品相随销售时间的增加而变差&#xff0c; 大部分品种如当日未售出&#xff0c;隔日就无法再售。因此&#xff0c; 商超通常会根据各商品的历史销售和需 求情况每天进行补货。 由于商超销售的蔬菜…

X86_64函数调用汇编程序分析

X86_64函数调用汇编程序分析 1 X86_64寄存器使用标准2 对应代码的分析2.1 main函数及其对应的汇编程序2.1.1 main的C代码实现2.1.2 main函数对应汇编及其分析2.1.3 执行完成之后栈的存放情况 2.2 test_fun_a函数及其对应的汇编程序2.2.1 test_fun_a函数的C实现2.2.2 test_fun_a…

【工作技术栈】【源码解读】一次springboot注入bean失败问题的排查过程

目录 前言现象分析原因解决方法思考感悟 前言 对这次的过程排查如果要形容的话&#xff0c;我觉得更像是悬疑剧&#xff0c;bean not found 这种错误&#xff0c;已经看腻了&#xff0c;甚至有时候都看不起这种错误&#xff0c;但是似乎这个想法被springboot听见了&#xff0c…

spring-security-源码解析+自定义拓展

1.参考文档 https://docs.spring.io/spring-security/reference/5.7/servlet/architecture.html 1.1.各种filterchain 1.1.1.SecurityFilterChain 1.1.2.springSecurityFilterChain 1.1.3.Security Filters 2.几个重要的注解 2.1.EnableXXX EnableWebMvcSecurity–deprecate…

C语言之初阶总结篇

目录 NO.1 NO.2 NO.3 NO.4 NO.5 NO.6 NO.7 NO.8 NO.9 NO.10 NO.11 NO.12.概念tips NO.13.求最小公倍数 NO.14.最大公因数 NO.15.输入读取字符串 NO.16.倒置字符串 今天是一些C语言题目&#xff0c;最近天气炎热&#xff0c;多喝水。 NO.1 下面程序执行后&am…

浅谈泛在电力物联网、能源互联网与虚拟电厂

导读&#xff1a;从能源互联网推进受阻&#xff0c;到泛在电力物联网名噪一时&#xff0c;到虚拟电厂再次走向火爆&#xff0c;能源领域亟需更进一步的数智化发展。如今&#xff0c;随着新型电力系统建设推进&#xff0c;虚拟电厂有望迎来快速发展。除了国网和南网公司下属的电…

LLMs之Baichuan 2:《Baichuan 2: Open Large-scale Language Models》翻译与解读

LLMs之Baichuan 2&#xff1a;《Baichuan 2: Open Large-scale Language Models》翻译与解读 导读&#xff1a;2023年9月6日&#xff0c;百川智能重磅发布Baichuan 2。科技论文主要介绍了Baichuan 2&#xff0c;一个开源的大规模语言模型&#xff0c;以及其在多个领域的性能表现…

与 vmx86 驱动程序的版本不匹配: 预期为 410.0,实际为 401.0

与 vmx86 驱动程序的版本不匹配: 预期为 410.0&#xff0c;实际为 401.0。 驱动程序“vmx86.sys”的版本不正确。请尝试重新安装 VMware Workstation。 我电脑历史上装过几个版本的vmware workstation: 怀疑是不兼容版本生成的vmx.86.sys 在系统中和该软件冲突&#xff0c;又没…

【完整代码】2023数学建模国赛C题代码--蔬菜类商品的自动定价与补货决策

C 题 蔬菜类商品的自动定价与补货决策 在生鲜商超中&#xff0c;一般蔬菜类商品的保鲜期都比较短&#xff0c;且品相随销售时间的增加而变差&#xff0c; 大部分品种如当日未售出&#xff0c;隔日就无法再售。因此&#xff0c;商超通常会根据各商品的历史销售和需 求情况每天进…

java+ssh+mysql智能化办公管理系统

项目介绍&#xff1a; 本系统为基于jspsshmysql的OA智能办公管理系统&#xff0c;包含管理员、领导、员工角色&#xff0c;功能如下&#xff1a; 管理员&#xff1a;公告信息&#xff1b;工作计划&#xff1b;公司资料&#xff1b;部门管理&#xff1b;员工管理&#xff1b;员…