wenmo8 发布的文章

“托管调试器(如Visual Studio)的哪些版本可以调试哪些版本的CLR?怎么办?”


这是调试器版本控制的基本问题。


以下是从版本控制的角度来看的“调试堆栈”,包括每个层之间的协议和进程边界:

  1. 用户源代码协议:
    由编译器处理的源语言。---编译器/调试对象进程边界----
  2. 正在调试的用户应用程序。
    协议:IL操作码,元数据。这些都在ECMA标准中公开定义。
  3. 运行应用程序的CLR。(mscorwks.dll)协议:
    专用CLR调试通信。---调试对象/调试器进程边界----
  4. CLR调试API(ICorDebug)(mscordbi.dll)
    协议:公共ICorDebug API。这是一个高版本的COM经典API。
  5. 调试器,如visualstudio或MDbg。
    协议:任意调试器扩展接口。也可能暴露ICorDebug。
  6. 调试器扩展、表达式计算器等。

所以这里可能有6个不同的组件可以被版本化!


加载哪个CLR?


生成一个单独的进程,它将在一个单独的进程中执行。

加载的CLR版本由shim/loader/config策略确定。调试器在这里是不可知的,尽管它确实可以做一些事情来影响这一点,比如在启动应用程序之前放置配置文件。由于托管可执行文件是IL操作码和元数据,这些都是经过很好指定的,因此有一些版本控制选项可用于编译.NET版本X的应用程序,但在.NET版本Y上运行它。例如,为.NET 1.1编译的应用程序可以在.NET 2.0上运行。

加载哪个ICorDebug?

一旦CLR版本(mscorwks.dll)确定后,下一个问题变成:调试器加载哪个mscordbi?


我们选择在ICorDebug而不是专用协议上对调试进行版本设置:

-ICorDebug已经是一个公共的comapi,并且已经通过像QueryInterface这样的东西来进行版本控制。

-这减少了测试组合。允许混合和匹配mscorwks.dll一个武断的mscordbi.dll会产生一个不断增长的测试矩阵。如果mscordbi.dll如果需要能够调试mscorwks的多个版本,它的复杂性将不断增加。

-这使得CLR可以在私有CLR调试协议上自由创新。它的一个优点是,它允许我们调整协议的聊天时间。


结果就是mscordbi.dll必须选择以匹配的版本mscorwks.dll那是装的。这是在第一次通过CreateDebuggingInterfaceFromVersion创建ICorDebug对象时完成的。

ICorDebug


调试器通常需要是最新版本,以便能够理解它在调试对象中看到的内容。例如,在V2中添加了泛型。V1.1调试器很难在V2应用程序中看到泛型。现在,关于是否可以通过“优雅的降级”来缓解这种混乱,通常是通过:

-让调试器忽略它不理解的内容(例如,不要在调用堆栈中显示泛型方法)

-构造一个近似的V2。

有时这些技术可以奏效,但这是一个非常滑的斜坡。
这就是VS2003无法调试.NET2.0应用程序的原因。

 

这是个公平的问题。部分原因是我们不相信人们能正确地使用它。我们发现人们问的主要原因是:


1) 用户好奇心:

用户只想在调试时知道这些琐事。当调试你认为是单线程应用程序时,你会在VSThreads窗口中看到6个线程,你想知道原因。请参阅Steve关于为调试器命名线程的帖子。不幸的是,CLR没有命名终结器线程。。我认为正确的解决方法是让终结器线程的命名和其他线程命名一样工作(并修复之前出现的任何perf问题);而不是添加一个特殊的调试器API来识别它。


2) 解决错误:

人们碰到了由终结器线程暴露的线程错误,然后希望能够识别终结器线程,以识别特殊情况下该错误的某些行为。终结器使用与其他线程相同的规则,因此线程错误(无论是在调试器中还是在常规托管应用程序中),发生在终结器线程上的线程错误也很可能发生在其他线程中。


现在根据经验,终结器的行为与主线程不同。在运行终结器之前,终结器实际上不会出现在托管代码中,这可能在任何随机时间发生。这是因为托管调试器在实际运行托管代码之前不会看到线程。由于主线程立即运行托管main()函数,因此这不是问题。


但是,其他线程的行为可能与终结器相同。通常,本机线程可以随时进入托管代码。在MC++中,线程可以在应用程序的本地部分启动,然后调用C++编译到IL,然后再进行管理。

我想讨论一个我们都非常熟悉的场景。在过去的一年里你一直在拼命工作,在过去的几个月里,你甚至在晚上和周末工作。管理层对你的团队给予两周的休息,以感谢你的努力。但是现在你回到了办公室,你听到了来自你的技术支持部门的流言,说有些情况下你的应用程序因为神秘的原因在现场崩溃。你是做什么的?
应用程序恰好是用通过注册的未经处理的异常筛选器生成的AppDomain.UnhandledException事件。因此,您至少知道应用程序因InvalidCastException而失败,但是您无法想象为什么会发生这种情况。
如果您可以在受影响的系统上进行实时调试,那不是很好吗?除非您在现场为您的客户工作,或者您的软件在笔记本电脑上,并且您的客户愿意将其发送给您,否则我怀疑您是否会获得此机会。您需要的是一个工具来捕获应用程序失败时的状态。然后客户可以捕获这些信息并将其发送给您。

输入ADPlus。ADPlus是Debugging Tools for Windows包中的一个免费工具,它为CDB调试器编写脚本,允许您捕获系统上一个或多个进程的转储。它还具有以下优点:

  • ADPlus可以监视桌面应用程序、服务等。
  • ADPlus可以监视系统上的多个进程。当它收集这些进程的转储时,它会同时冻结和转储它们。这对于跟踪进程间通信的问题至关重要。
  • ADPlus支持xcopy部署,这意味着客户不需要通过Windows安装程序等来安装任何东西。这将最大限度地减少机器上的配置更改,这对客户来说是一种音乐。

注意:尽管ADPlus可以xcopy安装,但您仍然必须通过Windows安装程序安装Windows调试工具包,因为这是微软发布它的唯一方式。但是,一旦安装了一次Windows调试工具,就可以xcopy将ADPlus或整个Windows调试工具包部署到另一台计算机上。事实上,在开发过程中,我发现将开发工具签入源存储库非常方便。Windows调试工具支持这一点,因为它是xcopy可安装的。
对于熟悉Windows Installer的用户,您可以使用msi执行管理安装,以便调试Windows工具,这将允许您提取文件,而无需在计算机上实际安装软件包,例如:msiexec /a dbg_x86_6.11.1.404.msi。

综上所述,让我们看看ADPlus如何帮助您诊断.NET应用程序的问题。

示例应用程序

我将引用我放在一起的C#3.0示例应用程序来演示如何使用ADPlus捕获.NET应用程序中未经处理的异常。代码如下:

usingSystem;usingSystem.Linq;usingSystem.Runtime.Serialization;classA

{
public voidSaySomething() {

Console.WriteLine(
"Yeah, Peter....");throw newBadDesignException();

}

}
classB : A

{

}
classC

{

}
classEntryPoint

{
static voidMain() {

DoSomething();

}
static voidDoSomething() {

Func
<int, object> generatorFunc = (x) =>{if( x == 7) {return newC();

}
else{return newB();

}

};
var collection = from i in Enumerable.Range( 0, 10)selectgeneratorFunc(i);//Let's iterate over each of the items in the collection// //ASSUMING THEY ARE ALL DERIVED FROM A !!!! foreach( var item incollection ) {

A a
=(A) item;try{

a.SaySomething();

}
catch( BadDesignException ) {//Swallow these here. The programmer chose to//use exceptions for normal control flow which//is *very* poor design. }

}

}

}
public classBadDesignException : Exception

{
publicBadDesignException() { }public BadDesignException( string msg ) : base( msg ) { }public BadDesignException( string msg, Exception x ) : base( msg, x ) { }protectedBadDesignException( SerializationInfo si,

StreamingContext ctx )

:
base( si, ctx ) { }

}