楔子
因为在默认的构造函数.Ctor上面下断点,导致了程序的崩溃,参见上一篇:一个奇怪的错误,找到了错误点,也找到了解决方法,但是结果不满意
因为没有相应的结构体在即时窗口赋值(除了),上篇解决的方案是八字节一个单元复制地址值,这种解决方案非常拙劣。因为如果高达几百个八字节,那岂不是复制几百次?本篇来看下另外一种解决方案。.
代码
internal class Program
{
static void Main(string[] args)
{
Program pm = new Program();
Console.WriteLine("Hello, World!");
GC.Collect();
}
}
原理
在Main函数运行之前,RunMainInternal处下断点,然后再在WriteCodeBytes
函数里面的memcpy(codeWriterHolder.GetRW(), m_CodeHeaderRW,
m_codeWriteBufferSize);这一行代码下断点,不要在Program.Ctor汇编地址处下断点。
这样的话,变量m_CodeHeader,m_CodeHeaderRW的MethodDesc第一次运行是
Program.Main函数,第二次运行就是Program.Ctor函数了。当第二次运行到
WriteCodeBytes的memcpy(codeWriterHolder.GetRW(), m_CodeHeaderRW,
m_codeWriteBufferSize);这句代码的时候,m_CodeHeader奇迹般的被赋值了。
而如果上面在Program.Ctor处下了断点,则以上步骤在WriteCodeBytes函数里调用的memcpy(codeWriterHolder.GetRW(), m_CodeHeaderRW,
m_codeWriteBufferSize)处,变量被MapViewOfFile映射出来的
codeWriterHolder.GetRW()(它实际上是
codeWriterHolder.m_addressRW)被m_CodeHeaderRW赋值,但是原值
codeWriterHolder.m_addressRX则不会被赋值映射。
这种截然不同的运作和调试方式,可以看到,上面的原理是可以支持调试.Ctor
的,也不需要8字节赋值如此的麻烦。
最后可以让它从WriteCodeBytes函数运行出来,直到运行到pCode,它是
Program.Ctor函数的起始地址,也就是函数头的上一个地址(机器码push地址的上
一个地址),然后再里面下个断点,F5跳转到断点,F10运行到前面的
Program.Ctor的下一句汇编代码。这样的话就完美的避开了关于打断点产生的异
常。
验证
用lldb验证下以上的说法
在RunMainInternal处下个断点,r运行到断点处
在WriteCodeBytes函数处下个断点,C运行到断点出,单步n运行到memcpy函数处
此时就可以看下当前它编译的是哪个函数了运行命令
p *(m_CodeHeaderRW.pRealCodeHeader)
把它里面的MethodDesc dump下
可以看到当前编译的是Program.Main函数
继续C命令,重复上面的步骤
然后dump methoddesc
可以看到它调用的是Program.Ctor函数。
单步n运行到pCode处
看下pcode的值
反汇编下pCode处函数
在函数头也就是机器码push处打个断点
把WriteCodeBytes处的断点删掉
运行C命令
程序的结果:Hello World打印出来了,也没有出错。
总结
这种.Ctor断点的错误,可以通过上述方法避免,而补丁式的修改。从而通达的调试整个项目,而仅仅多了几次步过。