wenmo8 发布的文章

什么是std::length_error异常

长度错误。它报告由于试图超出某些对象的实现定义的长度限制而导致的错误。一般由std::basic_string和std::vector::reserve等成员函数抛出。

继承关系

 

异常结构填充

ExceptionAddress: 747cc5af (KERNELBASE!RaiseException+0x00000058)
   ExceptionCode: e06d7363 (C++ EH exception)
  ExceptionFlags: 00000001
NumberParameters: 3
   Parameter[0]: 19930520
   Parameter[1]: 0039a178//std::length_error对象指针
   Parameter[2]: 66805744


0:000> dt std::length_error 0039a178
DIYHome!std::length_error
   +0x000 __VFN_table : 0x667c147c
   +0x004 _Mywhat          : 0x154882d8  "vector<T> too long"
   +0x008 _Mydofree   

备注

这个异常既不是内存不够,也不是越界,而是要生成的容器size超过了容器最大值(max_size())而引发。当然引起超过最大值的原因就多种多样了。

 

对于纯C#应用来说,这是一个没有实际意义的问题,但是如果你用MC++(或其他一些“混合”语言)编写,并且你想知道一个函数是被编译成托管代码还是本机代码呢?
您可以尝试检查源代码并根据语言规则进行推断。例如,在MC++中,查找#pragma managed/#pragma unmanaged。然而,这是有风险的,因为可能有一些你不知道的语言规则。(小测验:你知道MC++将函数编译成本机代码而不是IL的所有规则吗?)

所以如果你想要更多偏执的验证。。。

  • 使用ILDasm查看函数是否实际为IL。
  • 如果它在仅托管调试时出现,则它是托管代码。所以请检查您是否正在进行互操作调试,如果不是,您看到的任何内容都是托管的。
  • 在调用堆栈上查找托管2本机标记。
  • 如果调试时实际在函数中停止,请查看反汇编。

VS显示从偏移量0开始的托管调试反汇编,例如:

int main(array<System::String ^> ^args)
{
Console::WriteLine(L
"Hello World");00000000push edi00000001push esi00000002 push ebx

有时,您会查看异常抛出后处理程序中的调用堆栈。如果附加到弹出watson对话框的未处理异常,这是非常常见的。
它可能看起来像:

kernel32!WaitForSingleObject+0xf

devenv!DwCreateProcess+0xbb

devenv!fExceptionHandling+0x1cb

devenv!DwExceptionFilter+0x8b

0x535ef48

那没什么用。您真正想要的是查看抛出异常时的调用堆栈。在x86上执行此操作有一个技巧。(这可以调整为在64位平台上工作。)
它可以在实时调试和小型转储中工作,甚至在没有任何符号的情况下也可以工作。我将首先给出如何做到这一点的快速步骤,然后我将解释它的工作原理。

我怎么找到它?

 

当调试代码中的一个讨厌的问题时,你能得到的最有用的东西之一就是一个小型转储。当你的应用程序在崩溃、挂起或内存开始激增时,你得到了一个很大的提示,可以开始你的探索。虽然有很多工具,比如奇妙的prodump,以及调试器自己来创建小转储,但真正的关键时刻是您必须查看这些小型转储。一个或两个很容易做到,但是如果你有200个呢?在我的工作领域,我调试别人的软件问题,我通常会面对来自客户的数百个转储。尽管我很想仔细打开每个小型垃圾场,并且一遍又一遍地输入相同的命令。
我真正需要的是这样一种方式:“这里有一堆.DMP文件;在所有这些文件中运行这些WinDBG命令。”事实证明,当你结合一点WinDBG知识和一点PowerShell魔法时,完成这项基本任务一点都不难。在开始使用脚本之前,我需要谈谈它是如何工作的。这样你就能更好地理解它的用法。

为了编写WinDBG脚本,以便它在一个文件中执行一组命令,使用$$<命令就可以做到这一点。该命令将读入一个文本文件,并依次执行每一行,就像您在命令区域中键入了它一样。您可以在WinDBG帮助中找到$$<的其他变体,它们提供了稍微不同的功能,但是这个命令对于我来说已经足够了。

如果我想运行!analyze并获取已加载模块的列表,下面的调试脚本文本文件名为,BasicAnalysis.txt,显示这些命令。星号被认为是$$<的注释行。

    * Run !analyze!analyze v*Get the list of loaded modules
lmv

“托管调试器(如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应用程序的原因。