代码之家  ›  专栏  ›  技术社区  ›  JoelFan

每当输入文件(或类)时中断

  •  18
  • JoelFan  · 技术社区  · 16 年前

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

    我用C语言。

    23 回复  |  直到 12 年前
        1
  •  43
  •   Richard Szalay    15 年前

    宏可以是您的朋友。下面是一个宏,它将向当前类中的每个方法添加断点(在运行前将光标放在类中的某个位置)。

    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
    

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

        2
  •  13
  •   Steve Eisner    15 年前

    您可以从引入某种面向方面的编程开始——例如,请参见 this explanation -然后在单个onenter方法中放置一个断点。

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

        3
  •  10
  •   M4N    15 年前

    也许您可以使用一个AOP框架,比如Postharp,在每次输入一个方法时都可以闯入调试器。请看一下关于 this page 例如,如何在输入方法时记录/跟踪。

    在这种情况下,您可以将debugger.break()语句放入OnEntry处理程序,而不是日志记录。尽管,调试器不会在您的方法中停止,而是在OnEntry处理程序中停止(所以我不确定这是否真的有帮助)。

    下面是一个非常基本的示例:

    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方法。

        4
  •  8
  •   James Curran    16 年前

    正如大家所说,它涉及到在每个方法的开头设置一个断点。但你看不到大局。

    要使其工作,必须在每个方法的开头设置一个断点。无论是手动操作,还是调试器自动操作,都必须设置这些断点才能使其工作。

    因此,问题真的变成了,“如果对这个功能有足够的需求,那么值得在调试器中构建一种自动设置所有断点的方法?”答案是“不是真的”。

        5
  •  8
  •   Steve Steiner    15 年前

    这个特性是在VS中实现的。crtl-b并将“函数”指定为“classname::*”,这将在类上每个方法的开头设置断点。断点集在“断点”窗口(ctrl-alt-b)中分组在一起,因此可以作为一个组启用、禁用和删除它们。

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

        6
  •  6
  •   Ana Betts    15 年前

    这在windbg中很有效:

    bm exename!CSomeClass::*
    

    (为了澄清,上面这行在类中的所有函数上都设置了一个断点,就像OP所要求的那样,而不需要诉诸CRT攻击或宏的愚蠢。)

        7
  •  4
  •   Adam Rosenfield    15 年前

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

        8
  •  3
  •   JC.    16 年前
    System.Diagnostics.Debugger.Break();
    

    (在每种方法的开头)

        9
  •  1
  •   Brian    16 年前

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

        10
  •  1
  •   abelenky    15 年前

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

    把它放在你想要“破坏”的每个函数的顶部

    void MyFunction()
    {
        Debugger.Break();
        Console.WriteLine("More stuff...");
    }
    
        11
  •  1
  •   CraigTP    15 年前

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

    当类在非静态构造函数的情况下第一次被实例化时,这将进入调试;而在静态构造函数/类的情况下,一旦Visual Studio决定初始化类,您将立即进入调试。

    这当然可以防止您必须在 每一个 类中的方法。

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

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

        12
  •  1
  •   Daniel Daranas    15 年前

    假设您只对公共方法感兴趣,即当类方法被称为“从外部”时,我将再次按契约插入设计。

    你可以养成这样写公共职能的习惯:

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

    然后可以使用将调用的invariant()函数(i)和 在其中设置断点 . 然后检查调用堆栈以了解您来自哪里。当然,您也将在(v)中调用它;如果您真的只对入口点感兴趣,可以使用一个助手函数从(i)调用不变量,从(v)调用另一个。

    当然这是额外的代码,但是

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

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

    这只是一个想法,诚然它有一个脚印,所以只要考虑一下,如果你喜欢就用它。

        13
  •  1
  •   Gustaf Carleson    14 年前

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

    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
    
        14
  •  0
  •   Adam Rosenfield    16 年前

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

        15
  •  0
  •   ScottCher    16 年前

    您可以编写一个包装方法,通过它可以在应用程序中进行每个调用。然后在那个方法中设置一个断点。但是…你做这种事会疯的。

        16
  •  0
  •   BigSandwich    15 年前

    你可以在这上面放一个记忆断点,然后把它设置为“读”。我认为在调用成员函数的大部分时间都应该有一个read。我不确定静态函数。

        17
  •  0
  •   devdimi    15 年前
    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;
    }
    

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

        18
  •  0
  •   LarryF    15 年前

    如果这是C++,你可能会侥幸逃脱,(大量的工作)在CRT的前导码中设置一个断点,或者编写修改前导码的代码,在其中只插入int 3的代码,用于从所讨论的类生成的函数……顺便说一句,这可以在运行时完成…你必须让生成的PE文件修改自己,可能在重新定位之前,把所有的中断都粘在那里…

    我唯一的另一个建议是编写一个使用预先定义的宏函数的宏,在该宏中,您可以查找属于相关类的任何函数,如有必要,可以使用

    __asm { int 3 }
    

    在你的宏中使vs中断…这将防止您在每个函数开始时设置断点,但是如果您问我,您仍然需要执行宏调用,这更好。我想我在某个地方读到了如何定义或重新定义每个函数调用的前导码。我看看能找到什么。

    我想我类似的黑客可以用来检测你输入的文件,但是你仍然需要把你的函数宏放在你的代码上,否则它将永远不会被调用,而且,好吧,这几乎是你不想做的。

        19
  •  0
  •   Community gview    7 年前

    如果你愿意使用宏,那么接受的答案来自 this question

    通过使搜索函数搜索方法、属性和构造函数(如需要),应该可以根据需要进行细微的转换,也很可能有一种方法从IDE/符号中获取相同的信息,这将更稳定(尽管可能更复杂)。

        20
  •  0
  •   thinkbeforecoding    15 年前

    你可以使用 Debugger.Launch() Debugger.Break() 在大会中 System.Diagnostics

        21
  •  0
  •   Richard    15 年前

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

    对一个类型(在运行时确实存在)也可以这样做,但很可能是高度侵入性的,为HeisenBug创造了更多的潜力。到这一点的“最简单”的途径很可能是利用.NET远程处理的代理基础结构(有关使用透明代理的示例,请参见MOQ的实现)。

    摘要:使用宏,或选择“全部”,然后选择“设置断点”(ctrl-a,f9)。

        22
  •  0
  •   Andrew Arnott    15 年前

    乔尔,答案似乎是“不”。在每个方法上都有一个断点是不可能的。

        23
  •  0
  •   Tim Cooper    12 年前

    使用反射的疯狂方法。参见文档 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作为运行时参数传递(如果需要,可以通过命令行)以在给定类的所有方法和构造函数上设置断点。