CLR探索系列:Windbg+SOS动态调试分析托管代码

本文介绍如何使用Windbg结合SOS扩展调试模块来调试托管应用程序中的计算堆栈及强制类型转换后的JIT代码。通过具体示例,展示了如何加载调试模块、查看线程状态及方法地址,并深入分析了C#编译器生成的IL代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在使用VS进行托管应用程序的调试的时候,有的时候总感觉有些力不从心。譬如查看一个托管堆或者计算堆栈的时候,VS就不能胜任了。这个时候,Windbg+SOS扩展调试模块就为我们提供了一个很好的解决方案。
我们看一段代码:
class Program
    {
        static void Main(string[] args)
        {
            Program b = new Program();
            b.test();
            System.Console.ReadLine();
        }

        public void test()
        {
            int i = 67;
            System.Console.WriteLine((char)i);
            System.Console.WriteLine((char)67);
            i = 1;
        }
    }

这是C#里面的一个强制类型转换,我们现在用windbg+SOS来分析下计算堆栈,以及强制类型转换之后的JIT代码。
在windbg里面加载这个正在运行的程序,attach to this process,然后加载SOS扩展调试模块:
0:003> .load C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\SOS.dll
然后显示当前的线程:
0:003> ~
   0  Id: cf0.450 Suspend: 1 Teb: 7ffdf000 Unfrozen
   1  Id: cf0.be8 Suspend: 1 Teb: 7ffdd000 Unfrozen
   2  Id: cf0.168 Suspend: 1 Teb: 7ffdc000 Unfrozen
.  3  Id: cf0.7d0 Suspend: 1 Teb: 7ffde000 Unfrozen
切换到第0个线程:
0:003> ~0s
eax=0012f2e4 ebx=00000000 ecx=0012f400 edx=00000008 esi=0012f1f4 edi=00250688
eip=7c92eb94 esp=0012f194 ebp=0012f1b4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCallRet:
7c92eb94 c3              ret
显示test方法相关的地址:
0:000> !name2ee TestConcoleApp.exe TestConcoleApp.Program.test
Module: 00ab2c24 (TestConcoleApp.exe)
Token: 0x06000002
MethodDesc: 00ab2ff0
Name: TestConcoleApp.Program.test()
JITTED Code Address: 00d000f8
显示这个方法被C#编译器编译之后的IL代码:
0:000> !dumpil 00ab2ff0
ilAddr = 00402074
IL_0000: nop
IL_0001: ldc.i4.s 67
IL_0003: stloc.0
IL_0004: ldloc.0
IL_0005: conv.u2
IL_0006: call System.Console::WriteLine
IL_000b: nop
IL_000c: ldc.i4.s 67
IL_000e: call System.Console::WriteLine
IL_0013: nop
IL_0014: ldc.i4.1
IL_0015: stloc.0
IL_0016: ret

这里,sandwi对conv.u2这条指令一直困惑良多。我也对这个问题困惑了好久,翻阅了很多资料也没找到,后来准备在sscli的C#编译器里面找到答案,不过没找到地方......
后来被证实,这条指令是C#编译器为了类型安全,而生成的一条指令。作用在于把一个integer转换称为一个unsigned int16,然后前面补0成为int32压入堆栈里面去。
这是一个语言编译器行为,为了证实这个想法,同时写了一段同样的VB代码来证实我们的想法:
Module Module1

    Sub Main()
        Dim i As Integer

        i = 67
        System.Console.WriteLine(Chr(i))
        System.Console.WriteLine(Chr(67))
        System.Console.ReadLine()

    End Sub
  
End Module

编译之后的IL代码也同样支持上面的想法。
这里,感谢微软的张翼证实了我关于conv.u2的存在原因的猜想。但是,zhangyi说在test方法中的conv.u2指令在JIT生成的本地代码中被优化掉了,我却不同意这种看法:
0:000> !u 00d000f8
这条指令,是显示JIT编译了的test方法的本地代码,根据
JITTED Code Address: 00d000f8
这一行得来的。显示结果如下:
Normal JIT generated code
TestConcoleApp.Program.test()

push          esi
push          eax
mov          dword ptr [esp],ecx
cmp          dword ptr ds:[0AB2DD8h],0
je              00d0010b  (跳到xor  esi,esi这里)
call            mscorwks!CorLaunchApplication+0x108b4 (7a08e179)
xor            esi,esi
nop
mov           esi,43h
movzx       ecx,si
call            mscorlib_ni+0x2f8b9c (793b8b9c) (System.Console.WriteLine(Char), mdToken: 06000759)
nop
mov           ecx,43h
call            mscorlib_ni+0x2f8b9c (793b8b9c) (System.Console.WriteLine(Char), mdToken: 06000759)
nop
mov           esi,1
pop            ecx
pop            esi
ret
这里,movzx   ecx,si这条指令就对应了IL代码里面的Conv.u2,把对应的int前面补0放入到
ecx寄存器里面去。

后记:关于动态调试托管代码,我也是刚接触不久,上面有不准确的地方,欢迎大家多多指正。

### 关于ArcGIS License Server无法启动的解决方案 当遇到ArcGIS License Server无法启动的情况,可以从以下几个方面排查并解决问题: #### 1. **检查网络配置** 确保License Server所在的计算机能够被其他客户端正常访问。如果是在局域网环境中部署了ArcGIS Server Local,则需要确认该环境下的网络设置是否允许远程连接AO组件[^1]。 #### 2. **验证服务状态** 检查ArcGIS Server Object Manager (SOM) 的运行情况。通常情况下,在Host SOM机器上需将此服务更改为由本地系统账户登录,并重启相关服务来恢复其正常工作流程[^2]。 #### 3. **审查日志文件** 查看ArcGIS License Manager的日志记录,寻找任何可能指示错误原因的信息。这些日志可以帮助识别具体是什么阻止了许可服务器的成功初始化。 #### 4. **权限问题** 确认用于启动ArcGIS License Server的服务账号具有足够的权限执行所需操作。这包括但不限于读取/写入特定目录的权利以及与其他必要进程通信的能力。 #### 5. **软件版本兼容性** 保证所使用的ArcGIS产品及其依赖项之间存在良好的版本匹配度。不一致可能会导致意外行为完全失败激活license server的功能。 #### 示例代码片段:修改服务登录身份 以下是更改Windows服务登录凭据的一个简单PowerShell脚本例子: ```powershell $serviceName = "ArcGISServerObjectManager" $newUsername = ".\LocalSystemUser" # 替换为实际用户名 $newPassword = ConvertTo-SecureString "" -AsPlainText -Force Set-Service -Name $serviceName -StartupType Automatic New-ServiceCredential -ServiceName $serviceName -Account $newUsername -Password $newPassword Restart-Service -Name $serviceName ``` 上述脚本仅作为示范用途,请依据实际情况调整参数值后再实施。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值