.Net调试引用数据意料外变更的问题

127 阅读10分钟

问题描述

经常写bug的你是否经常面对这样的情况:你有一个集合的数据需要放到消息队列里,或者需要交给某个职责链处理。处理完后你发现这个集合的虽然还是之前的引用,数据却不对而且由于处理过程复杂完全找不到哪里!(正是在下51假期遇到的)。

大概比划个例子如下:

List<object> list = new List<object>();
System.Diagnostics.Debugger.Break();
// 在某个地方处理list
list.Add(new object());
// 在另一个地方发现list数据多了

这种问题最直接的解决方法肯定是打断点,但是有些断点可能并不好打。下面分享下我的思路。(主要是思路)

处理思路

数据断点或硬件实现(不行)

  • 非托管代码里允许设置数据断点,方法是 断点窗口 - > 新建 - > 数据断点;经过设置后,就可以在数据内容改变时中断到调试器。
  • 托管代码在从dotnet core 3.0起也可以新增数据断点了。但是启用的方法和非托管不一致,你需要光标选择变量后在右键菜单选择当值改变时中断。
  • vs实现数据断点看起来是利用了调试寄存器实现的。关于调试寄存器,可以参考张银奎的《软件调试》硬件部分。大概的原理就是把需要监视的地址放到调试寄存器里,然后设好相关标志位就可以了。
  • 这里并不能实现我们的需求,因为vs的数据断点有些莫名其妙的限制,比如vs要求不能调试静态变量(按理静态变量是被Pined的对象,GC不会移动,实现起来更简单才对)。具体参考官方文档使用限制

WinDbg调试

我们可以在相关的方法打上断点来调试问题,如本例中的Add方法。

使用WinDbg启动程序,继续执行到断点处:

0:000> g

查找托管堆上的List对象

0:000> !dumpheap -type List
         Address               MT     Size
000001a243f2bdc0 00007ffde37c0cd8       32     
000001a243f2c050 00007ffde37c3c18       32     

Statistics:
              MT    Count    TotalSize Class Name
00007ffde37c3c18        1           32 System.Collections.Generic.List`1[[System.Object, System.Private.CoreLib]]
00007ffde37c0cd8        1           32 System.Collections.Generic.List`1[[System.WeakReference`1[[System.Diagnostics.Tracing.EventSource, System.Private.CoreLib]], System.Private.CoreLib]]
Total 2 objects

列出目标List的方法

0:000> !dumpmt -md 00007ffde37c3c18
EEClass:         00007ffde37b5fc8
Module:          00007ffde3584000
Name:            System.Collections.Generic.List`1[[System.Object, System.Private.CoreLib]]
mdToken:         00000000020008C3
File:            C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.3\System.Private.CoreLib.dll
BaseSize:        0x20
ComponentSize:   0x0
DynamicStatics:  true
ContainsPointers true
Slots in VTable: 78
Number of IFaces in IFaceMap: 8
--------------------------------------
MethodDesc Table
           Entry       MethodDesc    JIT Name
00007FFDE36C0030 00007ffde3695608   NONE System.Object.Finalize()
00007FFDE36C0038 00007ffde3695618   NONE System.Object.ToString()
00007FFDE36C0040 00007ffde3695628   NONE System.Object.Equals(System.Object)
00007FFDE36C0058 00007ffde3695668   NONE System.Object.GetHashCode()
00007FFDE36D9728 00007ffde37c0258   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].get_Count()
00007FFDE36D9730 00007ffde37c0268   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.IList.get_IsFixedSize()
00007FFDE36D9738 00007ffde37c0288   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.Generic.ICollection.get_IsReadOnly()
00007FFDE36D9740 00007ffde37c02a8   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.IList.get_IsReadOnly()
00007FFDE36D9748 00007ffde37c02c8   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.ICollection.get_IsSynchronized()
00007FFDE36D9750 00007ffde37c02e8   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.ICollection.get_SyncRoot()
00007FFDE36D9758 00007ffde37c0308   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].get_Item(Int32)
00007FFDE36D9760 00007ffde37c0318   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].set_Item(Int32, System.__Canon)
00007FFDE36D9770 00007ffde37c0338   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.IList.get_Item(Int32)
00007FFDE36D9778 00007ffde37c0358   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.IList.set_Item(Int32, System.Object)
00007FFDE36DB530 00007ffde37c0378    JIT System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Add(System.__Canon)
00007FFDE36D9790 00007ffde37c0398   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.IList.Add(System.Object)
00007FFDE36D97C0 00007ffde37c0408   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Clear()
00007FFDE36D97C8 00007ffde37c0418   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Contains(System.__Canon)
00007FFDE36D97D0 00007ffde37c0428   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.IList.Contains(System.Object)
00007FFDE36D97E8 00007ffde37c0480   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.ICollection.CopyTo(System.Array, Int32)
00007FFDE36D97F8 00007ffde37c04b0   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].CopyTo(System.__Canon[], Int32)
00007FFDE36D9870 00007ffde37c05a0   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.Generic.IEnumerable.GetEnumerator()
00007FFDE36D9878 00007ffde37c05c0   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.IEnumerable.GetEnumerator()
00007FFDE36D9888 00007ffde37c05f0   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].IndexOf(System.__Canon)
00007FFDE36D9890 00007ffde37c0600   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.IList.IndexOf(System.Object)
00007FFDE36D98A8 00007ffde37c0640   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Insert(Int32, System.__Canon)
00007FFDE36D98B0 00007ffde37c0650   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.IList.Insert(Int32, System.Object)
00007FFDE36D98D8 00007ffde37c06b0   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Remove(System.__Canon)
00007FFDE36D98E0 00007ffde37c06c0   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].System.Collections.IList.Remove(System.Object)
00007FFDE36D98F0 00007ffde37c06f0   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].RemoveAt(Int32)
00007FFDE36D9948 00007ffde37c07a0    JIT System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]]..cctor()
00007FFDE36D9700 00007ffde37c0208    JIT System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]]..ctor()
00007FFDE36D9708 00007ffde37c0218    JIT System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]]..ctor(Int32)
00007FFDE36D9710 00007ffde37c0228   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]]..ctor(System.Collections.Generic.IEnumerable`1<System.__Canon>)
00007FFDE36D9718 00007ffde37c0238   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].get_Capacity()
00007FFDE36D9720 00007ffde37c0248    JIT System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].set_Capacity(Int32)
00007FFDE36D9768 00007ffde37c0328   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].IsCompatibleObject(System.Object)
00007FFDE36D9788 00007ffde37c0388    JIT System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].AddWithResize(System.__Canon)
00007FFDE36D9798 00007ffde37c03b8   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].AddRange(System.Collections.Generic.IEnumerable`1<System.__Canon>)
00007FFDE36D97A0 00007ffde37c03c8   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].AsReadOnly()
00007FFDE36D97A8 00007ffde37c03d8   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].BinarySearch(Int32, Int32, System.__Canon, System.Collections.Generic.IComparer`1<System.__Canon>)
00007FFDE36D97B0 00007ffde37c03e8   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].BinarySearch(System.__Canon)
00007FFDE36D97B8 00007ffde37c03f8   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].BinarySearch(System.__Canon, System.Collections.Generic.IComparer`1<System.__Canon>)
00007FFDE36D97D8 00007ffde37c0448   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].ConvertAll(System.Converter`2<System.__Canon,!!0>)
00007FFDE36D97E0 00007ffde37c0470   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].CopyTo(System.__Canon[])
00007FFDE36D97F0 00007ffde37c04a0   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].CopyTo(Int32, System.__Canon[], Int32, Int32)
00007FFDE36D9800 00007ffde37c04c0   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].EnsureCapacity(Int32)
00007FFDE36D9808 00007ffde37c04d0    JIT System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Grow(Int32)
00007FFDE36D9810 00007ffde37c04e0   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Exists(System.Predicate`1<System.__Canon>)
00007FFDE36D9818 00007ffde37c04f0   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Find(System.Predicate`1<System.__Canon>)
00007FFDE36D9820 00007ffde37c0500   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].FindAll(System.Predicate`1<System.__Canon>)
00007FFDE36D9828 00007ffde37c0510   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].FindIndex(System.Predicate`1<System.__Canon>)
00007FFDE36D9830 00007ffde37c0520   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].FindIndex(Int32, System.Predicate`1<System.__Canon>)
00007FFDE36D9838 00007ffde37c0530   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].FindIndex(Int32, Int32, System.Predicate`1<System.__Canon>)
00007FFDE36D9840 00007ffde37c0540   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].FindLast(System.Predicate`1<System.__Canon>)
00007FFDE36D9848 00007ffde37c0550   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].FindLastIndex(System.Predicate`1<System.__Canon>)
00007FFDE36D9850 00007ffde37c0560   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].FindLastIndex(Int32, System.Predicate`1<System.__Canon>)
00007FFDE36D9858 00007ffde37c0570   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].FindLastIndex(Int32, Int32, System.Predicate`1<System.__Canon>)
00007FFDE36D9860 00007ffde37c0580   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].ForEach(System.Action`1<System.__Canon>)
00007FFDE36D9868 00007ffde37c0590   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].GetEnumerator()
00007FFDE36D9880 00007ffde37c05e0   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].GetRange(Int32, Int32)
00007FFDE36D9898 00007ffde37c0620   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].IndexOf(System.__Canon, Int32)
00007FFDE36D98A0 00007ffde37c0630   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].IndexOf(System.__Canon, Int32, Int32)
00007FFDE36D98B8 00007ffde37c0670   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].InsertRange(Int32, System.Collections.Generic.IEnumerable`1<System.__Canon>)
00007FFDE36D98C0 00007ffde37c0680   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].LastIndexOf(System.__Canon)
00007FFDE36D98C8 00007ffde37c0690   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].LastIndexOf(System.__Canon, Int32)
00007FFDE36D98D0 00007ffde37c06a0   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].LastIndexOf(System.__Canon, Int32, Int32)
00007FFDE36D98E8 00007ffde37c06e0   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].RemoveAll(System.Predicate`1<System.__Canon>)
00007FFDE36D98F8 00007ffde37c0700   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].RemoveRange(Int32, Int32)
00007FFDE36D9900 00007ffde37c0710   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Reverse()
00007FFDE36D9908 00007ffde37c0720   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Reverse(Int32, Int32)
00007FFDE36D9910 00007ffde37c0730   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Sort()
00007FFDE36D9918 00007ffde37c0740   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Sort(System.Collections.Generic.IComparer`1<System.__Canon>)
00007FFDE36D9920 00007ffde37c0750   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Sort(Int32, Int32, System.Collections.Generic.IComparer`1<System.__Canon>)
00007FFDE36D9928 00007ffde37c0760   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Sort(System.Comparison`1<System.__Canon>)
00007FFDE36D9930 00007ffde37c0770   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].ToArray()
00007FFDE36D9938 00007ffde37c0780   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].TrimExcess()
00007FFDE36D9940 00007ffde37c0790   NONE System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].TrueForAll(System.Predicate`1<System.__Canon>)

找到目标方法,打印出实际的执行地址

0:000> !DumpMD /d 00007ffde37c0378
Method Name:          System.Collections.Generic.List`1[[System.__Canon, System.Private.CoreLib]].Add(System.__Canon)
Class:                00007ffde37b5fc8
MethodTable:          00007ffde37c07c8
mdToken:              0000000006006DBC
Module:               00007ffde3584000
IsJitted:             yes
Current CodeAddr:     00007ffde36db530
Version History:
  ILCodeVersion:      0000000000000000
  ReJIT ID:           0
  IL Addr:            00007ffe42d1dde4
     CodeAddr:           00007ffde36db530  (QuickJitted)
     NativeCodeVersion:  0000000000000000

使用bp指令设置断点,并继续执行至断点处

0:000> bp 00007ffde36db530
0:000> g
Breakpoint 1 hit
00007ffd`e36db530 55              push    rbp

这里其实是可以直接使用sosbpmd指令或者sosexmbm相关延迟断点指令的,但我经过测试有不生效的情况,非常奇怪也许是新版本的bug,参考Windbg+SOS: !bpmd not work? (microsoft.com),事实上《.Net高级调试》大概69页3.6节里说到调试断点的时候也说到了bpmd指令存在bug。

直接调试源代码

如果是.Net5或者是.Net Core的话,实际上是可以通过直接调试BCL源代码来解决问题的。这里可以参考官方文档如何编译调试。

结语

虽然最后并没有选择上述的方法解决问题(公司内网不联网,没法连微软的符号服务器调试),而是另辟蹊径猥琐的解决了,但是把思路记下来,也许有人用得到。