输入文件(或类)时中断

在Visual Studio中,是否有任何方法可以在输入某个文件(或类)时使调试器中断? 请不要回答“只需在每个方法的开头设置一个断点”:)

我正在使用C#。

宏可以成为你的朋友。 这是一个宏,它将为当前类中的每个方法添加一个断点(在运行之前将光标放在类中的某个位置)。

Public Module ClassBreak Public Sub BreakOnAnyMember() Dim debugger As EnvDTE.Debugger = DTE.Debugger Dim sel As EnvDTE.TextSelection = DTE.ActiveDocument.Selection Dim editPoint As EnvDTE.EditPoint = sel.ActivePoint.CreateEditPoint() Dim classElem As EnvDTE.CodeElement = editPoint.CodeElement(vsCMElement.vsCMElementClass) If Not classElem Is Nothing Then For Each member As EnvDTE.CodeElement In classElem.Children If member.Kind = vsCMElement.vsCMElementFunction Then debugger.Breakpoints.Add(member.FullName) End If Next End If End Sub End Module 

编辑:更新为按function名称添加断点,而不是文件/行号。 它感觉更好,在断点窗口中更容易识别。

您可以从介绍某种面向方面的编程开始 – 例如参见此解释 – 然后在单个OnEnter方法中添加断点。

根据您选择的AOP框架,它需要在您的代码中进行一些修饰并引入一些开销(您可以稍后删除),但至少您不需要在任何地方设置断点。 在某些框架中,您甚至可以在没有代码更改的情况下引入它,只是侧面的XML文件?

也许你可以使用AOP框架(如PostSharp)在输入方法时进入调试器。 看一下这个页面上非常简短的教程,举例说明如何在输入方法时记录/跟踪。

在您的情况下,您可以将Debugger.Break()语句放入OnEntry-handler中,而不是记录。 虽然,调试器不会在你的方法中停止,但是在OnEntry-handler中(所以我不确定这是否真的有帮助)。

这是一个非常基本的样本:

Aspect类定义了一个OnEntry处理程序,它调用Debugger.Break():

 [Serializable] public sealed class DebugBreakAttribute : PostSharp.Laos.OnMethodBoundaryAspect { public DebugBreakAttribute() {} public DebugBreakAttribute(string category) {} public string Category { get { return "DebugBreak"; } } public override void OnEntry(PostSharp.Laos.MethodExecutionEventArgs eventArgs) { base.OnEntry(eventArgs); // debugger will break here. Press F10 to continue to the "real" method System.Diagnostics.Debugger.Break(); } } 

然后,我可以将此方面应用于我的类,我希望调用器在调用方法时中断:

 [DebugBreak("DebugBreak")] public class MyClass { public MyClass() { // ... } public void Test() { // ... } } 

现在,如果我构建并运行应用程序,只要调用MyClass的某个方法,调试器就会在OnEntry()处理程序中停止。 我所要做的就是按F10,我就是MyClass的方法。

好吧,正如大家所说,它涉及在每个方法的开头设置一个断点。 但你没有看到更大的图景。

为了使其工作,必须在每个方法的开头设置断点。 无论是手动执行还是调试器自动执行,都必须设置这些断点才能使其正常工作。

所以,问题确实变成了,“如果有足够的需要这个function,那么值得在调试器中构建一个自动设置所有断点的方法吗?”。 答案是,“不是真的”。

此function在VS中实现,用于本机C ++。 crtl-B并将’function’指定为“Classname :: *”,这会在类的每个方法的开头设置一个断点。 断点集在断点窗口(ctrl-alt-B)中组合在一起,因此可以作为一组启用,禁用和删除它们。

可悲的是,宏可能是托管代码的最佳选择。

这在WinDbg中工作正常:

 bm exename!CSomeClass::* 

(只是为了澄清一下,上面的行设置了类中所有函数的断点,就像OP要求的那样,而不是诉诸CRT黑客攻击或宏观愚蠢)

您可以编写一个Visual Studio宏来获取所有类方法的列表(例如,通过读取与可执行文件一起生成的.map文件并搜索正确的符号名称(然后解析这些名称)),然后使用Breakpoints.add()以编程方式向这些函数添加断点。

 System.Diagnostics.Debugger.Break(); 

(在每个方法的开头)

不,或者更确切地说,是的,但它涉及在每个方法的开头设置一个断点。

使用Debugger.Break(); (来自System.Diagnostics名称空间)

把它放在你希望“破碎”的每个function的顶部

 void MyFunction() { Debugger.Break(); Console.WriteLine("More stuff..."); } 

是不是最简单的方法来最接近这个只是在构造函数中设置一个断点(假设你只有一个 – 或者在多个构造函数的情况下每个都有)?

在非静态构造函数的情况下首次实例化时,这将进入调试阶段,而在静态构造函数/类的情况下,只要Visual Studio决定初始化类,您就会进入调试阶段。

这当然可以防止您必须在类中的每个方法中设置断点。

当然,你不会继续在后续重新进入类的代码时进行调试(假设你下次使用相同的实例化对象),但是,如果每次重新实例化一个新对象,在调用代码中,你可以模拟这个。

但是,在传统的术语中,没有简单的方法可以在一个地方设置一个断点(例如),并且每次输入一个类的代码(来自任何一种方法)时都会进入调试阶段(据我所知)。

假设你只对公共方法感兴趣,即当“从外部”调用类方法时,我将再次插入Design by Contract。

你可以养成这样编写公共函数的习惯:

 public int Whatever(int blah, bool duh) { // INVARIANT (i) // PRECONDITION CHECK (ii) // BODY (iii) // POSTCONDITION CHECK (iv) // INVARIANT (v) } 

然后你可以使用你将在(i)中调用的Invariant()函数并在其中设置断点 。 然后检查调用堆栈以了解您的来源。 当然你也会在(v)中称呼它; 如果你真的只对入口点感兴趣,可以使用辅助函数从(i)调用Invariant,从(v)调用另一个函数。

当然这是额外的代码但是

  1. 无论如何它都是有用的代码,如果你使用契约式设计,结构就是样板。
  2. 有时您希望断点来调查一些不正确的行为,例如无效的对象状态,在这种情况下,不变量可能是无价的。

对于始终有效的对象,Invariant()函数只有一个返回true的主体。 你仍然可以在那里设置一个断点。

这只是一个想法,它确实有一个脚步,所以只要考虑它并使用它,如果你喜欢它。

要删除接受的答案设置的断点,请使用以下代码添加另一个宏

 Public Sub RemoveBreakOnAnyMember() Dim debugger As EnvDTE.Debugger = DTE.Debugger Dim bps As Breakpoints bps = debugger.Breakpoints If (bps.Count > 0) Then Dim bp As Breakpoint For Each bp In bps Dim split As String() = bp.File.Split(New [Char]() {"\"c}) If (split.Length > 0) Then Dim strName = split(split.Length - 1) If (strName.Equals(DTE.ActiveDocument.Name)) Then bp.Delete() End If End If Next End If End Sub 

不是我知道的。 您可以做的最好的事情是在文件或类的每个方法中放置一个断点。 你想做什么? 您是否在试图找出导致某些变化的方法? 如果是这样,也许数据断点更合适。

您可以编写一个包装器方法,通过该方法可以在应用程序中进行每次调用。 然后在该单个方法中设置断点。 但是……你做这样的事情会很疯狂。

您可以在此处设置内存断点,并将其设置为on read。 我认为大多数时候你应该读一个成员函数。 我不确定静态函数。

 you can use the following macro: #ifdef _DEBUG #define DEBUG_METHOD(x) x DebugBreak(); #else #define DEBUG_METHOD(x) x #endif #include  DEBUG_METHOD(int func(int arg) {) return 0; } 

在函数输入它将进入调试器

如果这是你正在谈论的C ++,那么你可能会逃避,(很多工作)在CRT的前导码中设置一个断点,或者编写修改前导码的代码来坚持INT 3的在那里只有从有问题的类生成的函数…这,BTW,可以在运行时完成…你必须让生成的PE文件自己修改,可能在重定位之前,以保持所有的中断那里…

我唯一的另一个建议是编写一个使用预定义宏__FUNCTION__的宏,在其中查找属于该类所属的任何函数,并在必要时粘贴一个

 __asm { int 3 } 

在你的宏中使VS中断…这将阻止你在每个函数的开头设置断点,但是如果你问我,你仍然必须坚持一个宏调用,这要好得多。 我想我在某处读到了如何定义或重新定义每个函数调用的前导码。我会看到我能找到的东西。

我认为我可以使用类似的hack来检测你输入的文件,但你仍然必须将你的函数宏放在你的代码上,否则它永远不会被调用,而且,这就是你不想要的去做。

如果你愿意使用宏,那么这个问题的答案是可以接受的

通过使搜索函数搜索方法,属性和构造函数(根据需要),应该可以简单地转换为您的需要,也很可能有一种方法可以从ide /符号获得相同的信息,这将更稳定(尽管可能更复杂一点)。

您可以在程序集System.Diagnostics使用Debugger.Launch()Debugger.Break()

文件在运行时不存在(考虑到部分类在代码方面没有区别 – 将所有内容放在单个文件中)。 因此,需要宏方法(或每种方法中的代码)。

对类型(在运行时确实存在)做同样的事情也许能够完成,但可能是高度侵入性的,为heisenbug创造了更多的潜力。 “最简单”的途径可能是利用.NET远程处理的代理基础设施(参见MOQ的实现,以获取使用透明代理的示例)。

摘要:使用宏,或选择all后跟set breakpoint(ctrl-A,F9)。

乔尔,答案似乎是“不”。 每种方法都没有断点。

使用reflection的疯狂方法。 有关详细信息,请参阅MethodRental.SwapMethodBody的文档。 在伪代码中:

 void SetBreakpointsForAllMethodsAndConstructorsInClass (string classname) { find type information for class classname for each constructor and method get MSIL bytes prepend call to System.Diagnostics.Debugger.Break to MSIL bytes fix up MSIL code (I'm not familiar with the MSIL spec. Generally, absolute jump targets need fixing up) call SwapMethodBody with new MSIL } 

然后,您可以将classname作为运行时参数传递(如果需要,可以通过命令行),以在给定类的所有方法和构造函数上设置断点。