如何为WinDbg编写ClrMD插件 – 作者:周大涛

在之前的如何为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中加载和使用您的扩展:

1.jpg

在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中一样调用扩展程序。

2.jpg

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

来源:freebuf.com 2019-04-06 13:00:50 by: 周大涛

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论