在Visual Studio中,是否有任何方法可以在输入某个文件(或类)时使调试器中断?请不要回答“只需在每个方法的开头设置一个断点”:)
我正在使用C#。
答案 0 :(得分:43)
宏可以成为你的朋友。这是一个宏,它将为当前类中的每个方法添加一个断点(在运行之前将光标放在类中的某个位置)。
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
编辑:已更新,按功能名称添加断点,而不是文件/行号。它感觉更好,并且在断点窗口中更容易识别。
答案 1 :(得分:13)
您可以从介绍某种面向方面的编程开始 - 例如,参见 this explanation - 然后在单个OnEnter方法中放置一个断点。
根据您选择的AOP框架,它需要在您的代码中进行一些修饰并引入一些开销(您可以稍后删除),但至少您不需要在任何地方设置断点。在某些框架中,您甚至可以在没有代码更改的情况下引入它,只是侧面的XML文件?
答案 2 :(得分:10)
也许您可以使用PostPharp等AOP框架在输入方法时进入调试器。看一下关于this page的非常简短的教程,举例说明如何在输入方法时记录/跟踪。
在您的情况下,您可以将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的方法。
答案 3 :(得分:8)
嗯,正如大家所说,它涉及在每个方法的开头设置一个断点。但是你没有看到更大的图景。
要使其工作,必须在每个方法的开头设置断点。无论您是手动执行,还是调试器自动执行此操作,都必须设置这些断点才能使其正常工作。
所以,问题确实变成了,“如果有足够的需要这个功能,那么值得在调试器中构建一个自动设置所有断点的方法吗?”。答案是,“不是真的”。
答案 4 :(得分:8)
此功能在VS中针对本机C ++实现。 crtl-B并将'function'指定为“Classname :: *”,这会在类的每个方法的开头设置一个断点。断点集在断点窗口(ctrl-alt-B)中组合在一起,因此可以将它们作为一组启用,禁用和删除。
可悲的是,宏可能是托管代码的最佳选择。
答案 5 :(得分:6)
这在WinDbg中运行良好:
bm exename!CSomeClass::*
(只是为了澄清,上面的行设置了类中所有函数的断点,就像OP要求的那样,而不是诉诸于CRT黑客攻击或宏观愚蠢)
答案 6 :(得分:4)
您可以编写一个Visual Studio宏来获取所有类方法的列表(例如,通过读取可执行文件旁边生成的.map
文件并搜索正确的符号名称(然后对这些名称进行解码) )),然后使用Breakpoints.add()
以编程方式向这些函数添加断点。
答案 7 :(得分:3)
System.Diagnostics.Debugger.Break();
(在每个方法的开头)
答案 8 :(得分:1)
要删除接受答案设置的断点,请使用以下代码添加另一个宏
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
答案 9 :(得分:1)
假设您只对公共方法感兴趣,即当“来自外部”调用类方法时,我将再次插入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)调用另一个函数。
当然这是额外的代码,但
对于始终有效的对象,Invariant()函数只有一个返回true的主体。你仍然可以在那里设置一个断点。
这只是一个想法,它确实有一个脚步,所以只要考虑它并使用它,如果你喜欢它。
答案 10 :(得分:1)
最简单的方法是不是最接近于此,只需在构造函数中设置一个断点(假设你只有一个 - 或者在多个构造函数的情况下每个都有)?
当在非静态构造函数的情况下首次实例化类时,这将进入调试,并且在静态构造函数/类的情况下,一旦Visual Studio决定初始化您,您将进入调试阶段类。
这肯定会阻止您在类中的每个方法中设置断点。
当然,在后续重新输入类的代码时你不会继续进行调试(假设你下次使用相同的实例化对象),但是,如果你重新实例化一个新对象每次从调用代码中,你都可以模拟它。
然而,在传统的术语中,没有简单的方法可以在一个地方设置一个断点(例如),并且每次进入类的代码(来自任何一种方法)时都会进入调试阶段(据我所知) )。
答案 11 :(得分:1)
使用Debugger.Break();(来自System.Diagnostics名称空间)
将它放在您希望“破碎”的每个功能的顶部
void MyFunction()
{
Debugger.Break();
Console.WriteLine("More stuff...");
}
答案 12 :(得分:1)
没有。或者更确切地说,是的,但它涉及在每个方法的开头设置一个断点。
答案 13 :(得分:0)
使用反射的Mad方法。有关详细信息,请参阅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作为运行时参数传递(如果需要,可以通过命令行),以便在给定类的所有方法和构造函数上设置断点。
答案 14 :(得分:0)
文件在运行时不存在(考虑到部分类在代码方面没有区别 - 将所有内容放在单个文件中)。因此,需要宏方法(或每种方法中的代码)。
对于类型(在运行时确实存在)也可以这样做,但可能是高度侵入性的,为heisenbug创造了更多的潜力。 “最简单”的途径可能是利用.NET远程处理的代理基础设施(参见MOQ的实现,以获取使用透明代理的示例)。
摘要:使用宏,或选择all后跟set breakpoint(ctrl-A,F9)。
答案 15 :(得分:0)
答案 16 :(得分:0)
如果您愿意使用宏,则可以使用this question
中接受的答案通过使搜索功能搜索方法,属性和构造函数(根据需要),应该可以轻松地转换为您需要的东西,很可能还有一种方法可以从ide /符号中获取更稳定的信息(虽然可能有点复杂)。
答案 17 :(得分:0)
您可以在程序集Debugger.Launch()
Debugger.Break()
和System.Diagnostics
答案 18 :(得分:0)
如果这是你正在谈论的C ++,那么你可能会逃避,(很多工作)在CRT中的前导码中设置一个断点,或编写修改前导码的代码坚持INT 3在那里只有从有问题的类生成的函数...这,BTW,可以在运行时完成...你必须让生成的PE文件修改自己,可能在重新定位之前,坚持所有在那里休息......
我唯一的另一个建议是编写一个使用预定义宏__FUNCTION__的宏,在其中查找属于该类所属的任何函数,并在必要时粘贴
__asm { int 3 }
在你的宏中使VS中断...这将阻止你在每个函数的开头都设置断点,但你仍然需要坚持一个宏调用,这要好得多,如果你问我。我想我在某处读到了如何定义或重新定义每个函数调用的前导码。我会看到我能找到的内容。
我认为我可以使用类似的hack来检测你输入的文件,但是你仍然必须在你的代码中放置你的函数宏,否则它永远不会被调用,而且,这就是你所做的不想做。
答案 19 :(得分:0)
you can use the following macro:
#ifdef _DEBUG
#define DEBUG_METHOD(x) x DebugBreak();
#else
#define DEBUG_METHOD(x) x
#endif
#include <windows.h>
DEBUG_METHOD(int func(int arg) {)
return 0;
}
on function enter它将进入调试器
答案 20 :(得分:0)
您可以在此处设置内存断点,并将其设置为on read。我认为大多数时候你应该读一个成员函数。我不确定静态函数。
答案 21 :(得分:0)
您可以编写一个包装器方法,通过该方法可以在应用中进行每次调用。然后在该单个方法中设置断点。但是......你做这样的事情会很疯狂。
答案 22 :(得分:0)
不是我知道的。您可以做的最好的事情是在文件或类的每个方法中放置一个断点。你想做什么?您是否在试图找出导致某些变化的方法?如果是这样,也许数据断点更合适。