34

如何为WinDbg编写ClrMD插件

 5 years ago
source link: http://4hou.win/wordpress/?p=30831&%3Butm_source=tuicool&%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在之前的 如何为WinDbg编写ClrMD扩展的CriteoLabs 文章中,我们介绍了在Windows上面写插件。当迁移到Linux时,由于LLDB是Linux上.NET网络核心的通用调试器,因此我决定编写一个兼容层,以便能够在新环境中加载扩展。

什么是ClrMD插件

如何创建一个适用于WinDbg和LLDB的扩展?第一步仍然是创建一个新的类库项目。无论是.NET Framework和.NET Standard都可以,但有些事情需要注意:

1.如果选择.NET Framework,请确保不要使用与.NET Core不兼容的任何功能(例如AppDomain),否则您将无法在LLDB中运行扩展;
2.如果选择.NET Standard,请记住发布项目以将所有依赖项包含在一个文件夹中,因为在编译时默认情况下不会这样做

创建项目后,添加 对ClrMDExports nuget 包的引用。它会自动将ClrMD和 UnmanagedExports.Repack 作为依赖项。UnmanagedExports.Repack是UnmanagedExports的一个分支,它增加了与.NET Framework 4.7+和.NET Standard的兼容性,并支持PackageReference。

请注意,新的Init.cs文件将添加到您的项目中(如果您使用包引用,则不应该看到它)。 不要对此文件进行任何更改。 每次更新nuget包时都会被覆盖。

Init文件负责导出WinDbg所需的DebugExtensionInitialize方法,并设置所有内容,只要依赖项与扩展位于相同的文件夹中,就可以正确加载它们。

下一步是添加自定义命令。您需要为每个命令创建一个静态方法,并使用以下签名:

public static void HelloWorld(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
}

然后使用UnmanagedExports附带的DllExport属性装饰它。您可以使用 ExportName 属性的参数来定义WinDbg / LLDB可见的命令名称。请记住,名称区分大小写!

[DllExport("helloworld")]
public static void HelloWorld(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
}

在该方法中,您应该只调用 DebuggingContext.Execute 由ClrMDExports提供的方法。它接受 clientargs 作为参数的值,以及带有 (ClrRuntime runtime, string args) 签名的另一个静态方法的委托。在静态回调方法中,实现命令。

[DllExport("helloworld")]
public static void HelloWorld(IntPtr client, [MarshalAs(UnmanagedType.LPStr)] string args)
{
    DebuggingContext.Execute(client, args, HelloWorld);
}

private static void HelloWorld(ClrRuntime runtime, string args)
{
    Console.WriteLine("The first 10 types on the heap are: ");

    foreach (var type in runtime.Heap.EnumerateTypes().Take(10))
    {
        Console.WriteLine(type);
    }
}

为方便起见,控制台输出会自动重定向到调试器。

您可以直接在WinDbg中加载和使用您的扩展:

AfiYruN.jpg!web

在Linux上运行LLDB

由于扩展是为WinDbg API编写的,因此无法直接加载到LLDB中。相反,我编写了一个进行翻译 的插件

首先, 下载最新版本 的LLDB-LoadManaged元插件并将其解压缩到一个文件夹中。

然后启动LLDB并附加到目标:

./lldb -c dump.dmp

接下来,加载元插件:

plugin load ./loadmanaged/libloadmanaged.so

确保Mono.Cecil.dll和PluginInterop.dll文件与libloadmanaged.so位于同一文件夹中。

加载后,LLDB-LoadManaged将尝试通过浏览调试目标中加载的模块来定位CoreCLR。如果失败(例如,因为您在与目标不同的机器上运行lldb),您可以通过调用 SetClrPath 以下命令手动设置路径:

SetClrPath /usr/local/share/dotnet/shared/Microsoft.NETCore.App/2.2.0/

最后,使用以下 LoadManaged 命令加载WinDbg扩展:

LoadManaged /home/k.gosse/TestExtension.dll

(该 LoadManaged 命令尚不支持相对路径)

就是这样!现在,您可以像在WinDbg中一样调用扩展程序。

QZnqiqU.jpg!web

WinDbg的ClrMD扩展在Linux上的LLDB中完美运行

注意:libloadmanaged.so和libsosplugin.so都会根据自己的需要托管CLR。但是, .NET Core CLR不支持并排方案 。这意味着我们不能同时使用LoadManaged和SOS插件。这可以讲是一个限制,它不太可能在.NET Core端修复。我们这里有一种解决方法,可以通过LoadManaged加载并替换libsosplugin.so的SOS托管版本。

接下来的路

这虽然是LLDB-LoadManaged的早期版本。在接下来的几周里,我想改进错误处理并使CLR路径检测变得更加智能。还有一点,其实我们已经在Criteo上使用它,这么来说它对于常见的用例应该足够稳定。使用LLDB与独立ClrMD应用程序的主要附加价值是可以附加到实时进程(Linux上的ClrMD尚不支持)。我也想知道基于ClrMD( https://github.com/dotnet/diagnostics/tree/master/src/Tools )的跨平台REPL环境有一些知识,所以很高兴看到这两项工作如何汇合。

*参考来源 medium ,由周大涛编译,转载请注明来自FreeBuf.COM


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK