转储文件跨机分析
我们在前面已经在Linux中收集核心转储文件,并在同一 Linux VM 上的调试器中打开这些文件。
如果无法分析同一 VM 上的转储,会发生什么情况?
通常,我们会从生产环境中收集核心转储文件,目的是排查问题。但是并不总是可以在同一台生产计算机上运行调试会话分析转储文件。
这里给出两种方法,用于分析在 Linux VM 上收集的核心转储文件:
- 使用 dotnet-dump 分析在 Windows Subsystem for Linux (WSL) 环境中运行的 Linux VM 上的核心转储文件。
- 使用 dotnet-dump 分析在 docker 容器内运行的 Linux VM 上的核心转储文件。
核心转储文件复制到本地
首先我们需要把核心转储文件复制到本地VM:
请记住,核心转储文件包含进程内存。 因此,如果进程内存使用率较高,核心转储的大小可能会很大。 可以优化传输,而不是逐个复制每个核心转储文件。 为此,请使用 tar 命令打包和压缩它们,以便有一个包含所有三个核心转储文件 的压缩 coredumps.tar.gz 文件。
可以使用该 tar -czvf coredumps.tar.gz coredump.manual.*
命令创建压缩文件。
我这里用sz命令将Linux上的压缩包传输到本地;类似的还有pscp命令, 还有很多办法可以将文件从Linux复制到Windows VM;
好了,成功传输后,我们的Windows VM 上应至少有一个核心转储文件。
接着,我们来了解如何分析转储文件;
使用WSL在另一台计算机打开核心转储
安装WSL2
设置密码
这里我选择了与 抓源dump包的主机相同的Linux 操作系统,也就是Ubuntu 18.04 LTS。 这里系统将使用 WSL2 运行。
安装 WSL2 后,使用 sudo apt update
这些命令和 sudo apt upgrade
命令更新包管理器。
现在应在基于 Windows 的计算机内运行 WSL2。 在上一部分中,从基于 Windows 的计算机上的 C:\Users\Administrator\Desktop\.netcore跨平台\.netcore实践\dumps
文件夹复制了核心转储文件,并假定文件名为 coredumps.tar.gz。 现在的目标是将 coredumps.tar.gz 复制到在 WSL2 中运行的 Linux VM。
这里是将 coredumps.tar.gz 文件复制并提取到 Linux 主目录中的转储文件文件夹;
安装.NET Core SDK
|
|
现在,可以再次安装最新的 SDK,如 安装 SDK 中所述。 以下截图显示了显示命令结果的Windows 终端。
安装dotnet-dump
安装完成后,即可开始浏览核心转储文件。使用 dotnet-dump 打开核心转储文件。
我这里尝试运行clrthreads以显示托管线程,但是命令没有出现预计失败并生成错误的信息, 这是因为我WSL2 Linux VM安装了相同版本.netcore运行时;
如果.本机netcore运行时和抓dump机的应用程序的.netcore运行时版本不一样,一般这里会有错误消息如下:
无法加载或初始化mscordaccore.dll。 目标运行时可能无法初始化
发生此错误是因为 Linux VM 上未安装所需的 .NET Core 运行时。 如 调试 Linux 转储中所述,为了分析 .NET Core 转储,LLDB 和 SOS 都需要以下来自在以下环境中创建转储文件的 .NET Core 二进制文件:
- libmscordaccore.so
- libcoreclr.so
- dotnet (用于启动应用的主机)
有人会问了,为什么之前实验的那台机没有这种问题,那是因为抓dump那台机这些 .NET Core 二进制文件已经存在。 现在,必须从 (某处复制这些文件的正确版本,例如从获取核心转储文件的 VM,或者必须在 WSL2 Linux VM 上安装相同的 .NET 运行时。
有几种方法可确定目标 .NET Core 运行时。 最简单的方法是询问应用程序所有者。 或者,如果你就是所有者,则会知道应用程序的目标 .NET Core 运行时。 如果你根本没有这些信息,该怎么办? 在这种情况下,可以使用 SOS 扩展命令, lm
或 modules
列出在进程中加载的本机模块。 运行此命令以确定 .NET 安装的版本。
这里我们知道.NET Core 运行时版本为 3.1.32。 需要正确版本的 libmscordaccore.so、libcoreclr.so 和 .NET。 可以从生成核心转储文件的 VM 复制这些文件, 或者安装相同版本的NET Core 运行时。
但是不能通过使用 sudo apt install dotnet-runtime-3.1.32
这种方式来安装;因为核心转储文件适用于 .NET Core 3.1.10 进程。 必须具有这些必需文件的匹配版本。
幸运的是,有一个 dotnet 符号工具 可以下载调试核心转储文件所需的 (符号、DAC、模块等) 的文件。 你可能还记得前面部分中的此工具。 若要安装此工具,请运行该 dotnet tool install -g dotnet-symbol
命令。
现在,必须要求 dotnet 符号工具下载针对我们的核心转储文件的必要文件。 这会传递核心转储文件名和其他必需参数:
dotnet-symbol --host-only --debugging ~/dumps/coredump.manual.4.3149
可以忽略列表中的最后两个错误。 必需的文件将下载到 ~/dumps 文件夹。
现在使用dotnet-dump打开核心转储文件,并尝试运行相同的clrthreads
这时候我们就能够很方便的在Windows上使用WSL2打开Linux生产机器上的核心转储文件了;
如果要使用lldb
分析转储文件,则需要要安装并使用 lldb
进行调试, 这个参考这个系列的第二篇,已经手把手的告知如何安装、配置和使用lldb了,看前面的文档就好;
dotnet符号没有所需的文件
Dotnet 符号适用于大多数核心转储文件。 如果出于某种原因,它无法下载必要的文件,则可以通过一些方法来解决此阻塞问题。 可以从官方 Microsoft 网页下载和提取二进制文件。 然后,手动将所需文件复制到包含核心转储文件的同一文件夹。
例如,如果需要 .NET Core 3.1.10 x64 的文件,可以从此 ASP.NET Core 3.1 运行时 (v3.1.10) - Linux x64 二进制文件下载页下载二进制文件,并手动复制文件:
- .\shared\Microsoft.NETCore.App\3.1.10\libcoreclr.so
- .\shared\Microsoft.NETCore.App\3.1.10\libmscordaccore.so
- .\shared\Microsoft.NETCore.App\3.1.10\libmscordbi.so
- *.\shared\Microsoft.NETCore.App\3.1.10*
- .\dotnet
如果目标运行时版本是专用版本, (请记住 .NET Core 已开放源代码,并且可以生成和使用自己的专用版本) ,则必须从生成核心转储文件的 Linux VM 复制这些文件。
使用Docker在另一台机打开核心转储
简单来说,这里讨论如何使用 Docker 在另一个 Linux VM 上打开核心转储文件。因为docker确实使用起来很方便;
大部分人已经很熟悉docker了,这里就直接创建一个 Ubuntu 容器来分析核心转储文件。
创建dockerfile
dockerfile 只是一组用于创建容器的说明。 “dockerfile”文件名区分大小写,应全为小写,不带任何文件扩展名。
可以使用 RUN 命令在目标容器上运行命令。 例如,本部分中的 dockerfile 显示可用于 RUN mkdir /dumps
在目标容器 OS 上运行 mkdir /dumps
命令。
这里将使用最新的 Ubuntu 映像创建 Ubuntu 容器、安装最新的 dotnet SDK、更新 OS、安装 dotnet-dump 和 dotnet 符号工具、复制和提取转储文件,以及通过在其中一个核心转储文件上使用 dotnet 符号工具下载所需文件。
|
|
在包含 coredumps.tar.gz 存档文件的同一目录中创建名为 dockerfile 的文件。 请记住:文件名区分大小写,没有扩展名。
若要生成 docker 容器,请运行 docker build -t dotnettools .
。
下面是生成命令的输出。 由于同一命令已运行多次,因此它对目标映像使用其内部缓存。 首次运行此命令时,它将下载必要的文件,然后在必要时缓存这些文件供以后使用。 首次运行命令时,生成映像可能需要一些时间。
第一遍dockerfile报错了, 因为我用的是.netcore3.1, dockerfile版本修改如下:
|
|
成功构建了dotnettools
的镜像:
然后,使用 docker container run -it dotnettools /bin/bash
命令运行容器。
现在位于新建的 Linux 容器内。 其余部分与之前相同:使用相同的 dotnet-dump 命令打开核心转储: dotnet-dump analyze /dumps/coredump.manual.4.3149
下面是用于生成示例环境的结果。
并运行SOS命令(例如clrthreads)来显示托管线程:
好了,现在也可以愉快的用Docker 分析其他Linux主机中抓取的核心转储文件了;