• .netcore利用perf分析高cpu使用率


    教程:官方文档 https://docs.microsoft.com/zh-cn/dotnet/core/diagnostics/debug-highcpu?tabs=linux
    环境:Linux、Docker、.NET Core 3.1 SDK及更高版本
    示例代码:https://github.com/dotnet/samples/tree/master/core/diagnostics/DiagnosticScenarios

    利用dotnet-dump分析docker容器内存泄露 跟上篇一样,继续使用官方示例代码演示CPU占用情况。

    一 在宿主机运行perf

    依旧跟随官网教程的步伐,唯一不同的是,我的示例运行在容器中。
    还是跟上一篇的观念一样,当生产环境出现问题时一定要当场把问题样本保存下来用于事后分析。
    在宿主机中安装好perf工具,然后捕获容器内占用过高的dotnet进程,最后生成火焰图,整个过程一气呵成非常简单;但问题就出现在生成的火焰图根本不显示JIT生成的函数,只显示perf-PID.map,如下图

    问题的原因可能是:捕获进程时无法生成CPU地址和JIT生成函数的映射文件,生成火焰图时找不到待分析进程所依赖的库。
    接下来,依然本着不污染宿主机的原则,选择在容器内完成一切工作。

    二 容器内安装perf

    1,重新构建镜像

    修改Dockerfile,在原先的基础上加上一个环境变量COMPlus_PerfMapEnabled=1,它的作用是使.NET Core应用在/tmp目录中有权限创建map文件,perf使用此map文件按名称将CPU地址映射到JIT生成的函数。
    进入到容器执行export COMPlus_PerfMapEnabled=1,然后perf是无法创建map文件的,所以我们必须在启动容器时就要加上。

    FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base
    WORKDIR /app
    EXPOSE 80
    
    COPY  bin/Release/netcoreapp3.1 .
    
    ENV COMPlus_PerfMapEnabled=1
    
    ENTRYPOINT ["dotnet", "DiagnosticScenarios.dll"]
    

    修改完保存之后,构建镜像,启动容器

    [root@localhost Diagnostic_scenarios_sample_debug_target]# docker build -t dumptest .
    Sending build context to Docker daemon   1.47MB
    ...
    Successfully tagged dumptest:latest
    [root@localhost Diagnostic_scenarios_sample_debug_target]# docker rm -f diagnostic
    diagnostic
    [root@localhost Diagnostic_scenarios_sample_debug_target]# docker run --name diagnostic --privileged=true -p 888:80 -d dumptest
    d2a7037858ed6af0676990bc56ae2b73519c3a8c073db06af639ef30d9588628
    

    2,下载火焰图生成脚本

    进入容器之前,可以先做一个准备工作,将生成火焰图需要用到的pl脚本先clone到宿主机(以后会反复用到),当然这一步也可以到容器内操作。

    [root@localhost ~]# cd ~
    [root@localhost ~]# git clone --depth=1 https://github.com/BrendanGregg/FlameGraph
    Cloning into 'FlameGraph'...
    Resolving deltas: 100% (61/61), done.
    [root@localhost ~]# ls
    Diagnostic_scenarios_sample_debug_target  FlameGraph
    

    又get到新的知识点git clone --depth=1
    加了个--depth=1的好处就是限制clone的深度,不会下载git协作的历史记录,这样可以大大加快克隆的速度。
    接下来把FlameGraph拷贝到容器内,然后进入容器:

    [root@localhost ~]# docker cp FlameGraph diagnostic:/app/FlameGraph
    [root@localhost ~]# docker exec -it diagnostic bash
    root@822a953f9f53:/app# ls
    DiagnosticScenarios            DiagnosticScenarios.dll  DiagnosticScenarios.runtimeconfig.dev.json  FlameGraph                          Microsoft.AspNetCore.Mvc.NewtonsoftJson.dll  Newtonsoft.Json.dll  appsettings.Development.json
    DiagnosticScenarios.deps.json  DiagnosticScenarios.pdb  DiagnosticScenarios.runtimeconfig.json      Microsoft.AspNetCore.JsonPatch.dll  Newtonsoft.Json.Bson.dll                     Properties           appsettings.json
    

    3,安装linux-perf

    root@822a953f9f53:/app# apt update && apt install -y linux-perf vim
    ...
    root@822a953f9f53:/app# perf --version
    /usr/bin/perf: line 13: exec: perf_3.10: not found
    E: linux-tools-3.10 is not installed.
    

    安装成功后查看perf版本会提示perf_3.10: not found和linux-tools-3.10 is not installed。
    linux-tools误导,一顿抓瞎折腾半天之后,找遍全网没有任何有用的信息。
    索性回到perf本身看看/usr/bin/perf第13行到底是个啥,结果发现好像是内核版本和perf不匹配的原因,uname -r得到的结果是Linux 3.10...
    然而在/usr/bin目录下并没有找到perf_3.10只有perf_4.19,直接把下图中的perf_$version替换成perf_4.19。


    root@822a953f9f53:/app# vi /usr/bin/perf
    root@822a953f9f53:/usr/bin# perf --version
    perf version 4.19.160
    

    三 CPU占用分析

    1,perf record捕获进程

    先运行能引发高CPU的链接60000ms, http://192.168.0.161:888/api/diagscenario/highcpu/60000
    然后在CPU飙升期间执行perf record捕获进程,容器里面只运行了一个应用,所以进程id为1,这里就不在查找了。

    root@1834a15133ed:/app# perf record -p 1 -g -- sleep 20
    [ perf record: Woken up 72 times to write data ]
    [ perf record: Captured and wrote 17.330 MB perf.data (80022 samples) ]
    root@1834a15133ed:/app# ls
    DiagnosticScenarios            DiagnosticScenarios.pdb                     FlameGraph                                   Newtonsoft.Json.Bson.dll  appsettings.Development.json
    DiagnosticScenarios.deps.json  DiagnosticScenarios.runtimeconfig.dev.json  Microsoft.AspNetCore.JsonPatch.dll           Newtonsoft.Json.dll       appsettings.json
    DiagnosticScenarios.dll        DiagnosticScenarios.runtimeconfig.json      Microsoft.AspNetCore.Mvc.NewtonsoftJson.dll  Properties                perf.data
    

    捕获完成之后会在当前目录下生成一个perf.data文件,在/tmp目录下生成perf-PID.mapperfinfo-PID.map文件。

    root@d2a7037858ed:/app# cd /tmp
    root@d2a7037858ed:/tmp# ls
    clr-debug-pipe-1-32163779-in  clr-debug-pipe-1-32163779-out  dotnet-diagnostic-1-32163779-socket  perf-1.map  perfinfo-1.map
    

    2,生成火焰图

    之前cp到/app目录下的FlameGraph项目在此刻派上用场。

    root@d2a7037858ed:/tmp# cd /app
    root@d2a7037858ed:/app# perf script | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flamegraph.svg
    ...
    root@d2a7037858ed:/app# ls
    DiagnosticScenarios            DiagnosticScenarios.pdb                     FlameGraph                                   Newtonsoft.Json.Bson.dll  appsettings.Development.json  perf.data
    DiagnosticScenarios.deps.json  DiagnosticScenarios.runtimeconfig.dev.json  Microsoft.AspNetCore.JsonPatch.dll           Newtonsoft.Json.dll       appsettings.json
    DiagnosticScenarios.dll        DiagnosticScenarios.runtimeconfig.json      Microsoft.AspNetCore.Mvc.NewtonsoftJson.dll  Properties                flamegraph.svg
    

    生成完之后取出flamegraph.svg文件,到浏览器打开。
    找到了这个又高又宽的“大平顶”,是由highcpu这个Action导致。
    关于如何看懂火焰图,可以自行百度,底部参考也有给出基础教程链接。


    示例项目中就这一个方法在运行,所在找起来非常简单。
    实际项目中如果业务复杂、函数多、调用链深话,打开图就是一片密密麻麻。


    参考:
    Linux性能优化实战学习笔记:第十七讲 https://www.cnblogs.com/luoahong/p/10833689.html
    如何读懂火焰图? - 阮一峰的网络日志 http://www.ruanyifeng.com/blog/2017/09/flame-graph.html

  • 相关阅读:
    linux常用命令(一)
    并发与高并发(十九) 高并发の消息队列思路
    鉴别web服务器的工具类
    并发与高并发(十八) 高并发之缓存思路
    记一次多线程下使用while出现的问题
    并发与高并发(十七)高并发之扩容思路
    并发与高并发(十六)多线程并发拓展
    并发与高并发(十五)线程池
    并发与高并发(十四)J.U.C组件拓展
    基于springboot实现Java阿里短信发送
  • 原文地址:https://www.cnblogs.com/wu_u/p/14263349.html
Copyright © 2020-2023  润新知