Windows 下如何调试 PowerShell

背景

最近在用 PowerShell 的时候,发现一些地方特别有意思。于是就萌生了看源代码的想法,单看肯定不过瘾,调试起来才有意思。于是就有了这个,记录下。

调试 PowerShell 主要分为两种方式:通过 VS 直接编译运行源代码和通过 WinDbg 来调试。

由于 PowerShell 跨平台的特性,由于我目前只有 Windows 的诉求,所以以下内容将围绕 Windows 来进行。其他平台可以参照官方文档,可以在这里看到:

PowerShell/docs/building at master · PowerShell/PowerShell

准备工作

拉代码

第一步自然就是去 github 把代码 clone 下来了。地址是:

https://github.com/PowerShell/PowerShell

截至到2022年7月16日,建议不要直接直接用 master 分支,根据文档介绍,master 分支为最新版本,并非稳定版本。也就是我们平常看到的 preview 的版本。而且,这里强烈建议不用是因为目前 preview 的版本已经切换到 .net 7 了,而 .net 7 的 sos 我没找到(如果知道怎么找到的小伙伴可以告诉我,感谢~)。所以我这里是采用最新的稳定版,也就是 7.2.5 版本的源代码进行。

准备环境

第二步就是准备好编译环境。因为 PowerShell 全部是 C# 实现的,自然就是依赖 dotnet 环境的了。

官方给了我们一个非常好的脚本,非常便利,在源代码的根目录下执行:

Import-Module ./build.psm1 Start-PSBootstrap 

或者直接:

Install-Dotnet 

就可以完成安装了。

如果只是这样,就结束了,是不是过于顺利了,就没有这篇文章的必要了。

由于我安装了 VS2022 preview 的版本,它顺带帮我安装了 .net 6.0.400-preview.22330.6 的版本,而且还不让卸载。因此我最新的版本始终是 6.0.400-preview,后续的编译,它就死命安装不了要的版本。也就是 6.0.203。

于是就只能从 dot.net 手动下载安装了。

方式一:通过 WinDbg 调试

这种方式适用于不想安装 VS,但需要详细调试的同学。(不要问我为啥不用 VSC,看代码 VSC 确实不错,但调试,还得 WinDbg 来)

编译代码

这里我们直接采用官方推荐的方式:

Import-Module ./build.psm1 Start-PSBuild 

然后就出现下面的错误:

VERBOSE: In Find-DotNet WARNING: The 'dotnet' in the current path can't find SDK version 6.0.203, prepending C:UsersfrendAppDataLocalMicrosoftdotnet to PATH. WARNING: The currently installed .NET Command Line Tools is not the required version.  Installed version: 6.0.400-preview.22330 Required version: 6.0.203  Fix steps:  1. Remove the installed version from:     - on windows '$env:LOCALAPPDATAMicrosoftdotnet'     - on macOS and linux '$env:HOME/.dotnet' 2. Run Start-PSBootstrap or Install-Dotnet 3. Start-PSBuild -Clean 

可是我明明安装了的呀:

- dotnet --list-sdks 6.0.203 [C:Program Filesdotnetsdk] 6.0.302 [C:Program Filesdotnetsdk] 6.0.400-preview.22330.6 [C:Program Filesdotnetsdk] 

于是,就去找了下对应的脚本。在 [ps code root]/build.psm1 文件中的 Start-PSBuild → Find-Dotnet → Get-LatestInstalledSDK

function Get-LatestInstalledSDK {     Start-NativeExecution -sb {         dotnet --list-sdks | Select-String -Pattern 'd*.d*.d*(-w*.d*)?' | ForEach-Object { [System.Management.Automation.SemanticVersion]::new($_.matches.value) } | Sort-Object -Descending | Select-Object -First 1     } -IgnoreExitcode 2> $null } 

好家伙,直接就是拿最新的作为我安装的版本,所以导致匹配不上,无法开始编译。由于我这里安装了 6.0.203 的,所以我就修改了下。根据我的安装情况,改成让他返回我想要让他返回的版本了(如下加粗版本)。这里需要根据自己实际情况修改!!!

dotnet --list-sdks | Select-String -Pattern 'd*.d*.d*(-w*.d*)?' | ForEach-Object { [System.Management.Automation.SemanticVersion]::new($_.matches.value) } | Sort-Object -Descending | Select-Object -First **3 | Sort-Object | Select-Object -First 1** 

于是我们重来一遍导入,编译。就能成功啦!

然后就能找到啦 ./src/powershell-win-core/bin/Debug/net6.0/win7-x64/publish/pwsh.exe

调试代码

打开 WinDbg preview → Launch executable(advanced)

Executable 中填入上一步编译出来的地址,我的是这样的:C:UsersfrendsourcereposdotnetPowerShellsrcpowershell-win-corebinDebugnet6.0win7-x64pwsh.exe

Arguments,填入你想调试的命令就好啦。我的是:ex bypass -nop -Command Invoke-webRequest www.baidu.com

其他就可以不管了。来,开始调试~

由于我们要调试的是托管代码,在启动点我们没办法打断点,得先等 sos 和 clr 加载好了之后才行。

这里我先打了一个clr上的断点:

bp coreclr!CallDescrWorkerInternal 

待断点断到了,再加上 PowerShell 启动代码的断点。注意,托管代码需要用 sos 的 !bpmd 来打断点:

0:000> !bpmd pwsh Microsoft.PowerShell.ManagedPSEntry.Main Adding pending breakpoints... 0:000> bd 0 0:000> g ModLoad: 00007ff8`d7460000 00007ff8`d7478000   C:WINDOWSSYSTEM32kernel.appcore.dll ModLoad: 00000288`fa860000 00000288`fa884000   C:UsersfrendsourcereposdotnetPowerShellsrcpowershell-win-corebinDebugnet6.0win7-x64pwsh.dll ModLoad: 00000288`fa860000 00000288`fa884000   C:UsersfrendsourcereposdotnetPowerShellsrcpowershell-win-corebinDebugnet6.0win7-x64pwsh.dll (45e4.6834): CLR notification exception - code e0444143 (first chance) ModLoad: 00000288`fa8a0000 00000288`fa8ae000   C:UsersfrendsourcereposdotnetPowerShellsrcpowershell-win-corebinDebugnet6.0win7-x64System.Runtime.dll (45e4.6834): CLR notification exception - code e0444143 (first chance) ModLoad: 00000288`fa8b0000 00000288`fa90e000   C:UsersfrendsourcereposdotnetPowerShellsrcpowershell-win-corebinDebugnet6.0win7-x64Microsoft.PowerShell.ConsoleHost.dll (45e4.6834): CLR notification exception - code e0444143 (first chance) (45e4.6834): CLR notification exception - code e0444143 (first chance) JITTED pwsh!Microsoft.PowerShell.ManagedPSEntry.Main(System.String[]) Setting breakpoint: bp 00007FF7E6F82530 [Microsoft.PowerShell.ManagedPSEntry.Main(System.String[])] Breakpoint 1 hit *** WARNING: Unable to verify checksum for C:UsersfrendsourcereposdotnetPowerShellsrcpowershell-win-corebinDebugnet6.0win7-x64pwsh.dll pwsh!Microsoft.PowerShell.ManagedPSEntry.Main: 00007ff7`e6f82530 55              push    rbp 

然后,就可以命中断点了。

这里可能不会自动打开并且跳转到源代码中,再 t 几下,就会自动打开的。最终就会变成这样。

Windows 下如何调试 PowerShell

于是,我们就可以开始顺利的调试了。

方式二:通过 VS 调试

这种方式相对就比较简单了,这里用 VS2022 来做演示。

记得一定要安装对应的 dotnet sdk。具体的版本可以在源代码根目录的 global.json 中查看。

直接在源代码中打开 PowerShell.sln 文件即可打开 PowerShell 项目。然后 build 一下,记得,Windows 下启动目录应该是:powershell-win-core

然后就可以配置调试的参数了:

在项目 powershell-win-core 上鼠标右击,选择属性 → 调试 → Open debug launch profiles UI

然后就是跟 WinDbg 调试类似的参数了。如下图:

Windows 下如何调试 PowerShell

然后,再你想观察的代码处打上断点,点击运行就好啦。

总结

其实官方文档已经比较完善了。基本都比较简单。特别是通过 VS,简直方便的不要不要的。

对于基于 dotnet 的 PowerShell,肯定还会遇到 dotnet 上的问题,那怎么调试 dotnet 呢?

看这里:

https://github.com/dotnet/runtime

发表评论

相关文章