paint-brush
断点:他们解释的巨大力量经过@shai.almog

断点:他们解释的巨大力量

经过 Shai Almog10m2023/01/02
Read on Terminal Reader

太長; 讀書

了解跟踪点(又名日志点)异常断点如何不必糟糕、观察点、过滤器以及为什么方法断点有问题。
featured image - 断点:他们解释的巨大力量
Shai Almog HackerNoon profile picture

这是重要的一周。我目前正在审阅我即将出版的调试书的最终草稿。这始终是一个发人深省和激动人心的时刻。突然间,所有几个月的工作都变得“真实”了。


我还为 youtube 录制了 9 个视频,使课程中的视频总数达到 29 个(还有更多正在制作中)。我正在开设另外两门免费课程,其中一门针对的是绝对初学者


另一个是关于现代 Java 编程的,我很快就会推出。两者都将在上免费(必须“喜欢、订阅和分享”)。


在本周的帖子中,我们将讨论断点。这很长,因为当我们说断点时,我们的意思实际上只是“行断点”。正如您在下面看到的那样,它们还有很多。

成绩单

欢迎回到 Scale 调试的第四部分,在这里我们开始讨论并取名。变量名!


在本节中,我们将讨论调试时最基本的工作单元断点。但对他们来说,不仅仅是打破。


我们在第一部分讨论了最基本的断点。这一次,我们将更深入地挖掘一些鲜为人知的细微差别。

条件断点

我们从条件断点开始。条件断点让我们定义断点命中的条件。这可以防止线程不断地在断点处停止。我之前用我们在上一个视频中创建的 myThread 标记对象讨论过这个问题。


在这种情况下,我们只有在它与当前线程不同时才会停止。这意味着这个断点只有在不同的线程调用它时才会命中。这是检测与线程相关的问题(如死锁或竞争)的好方法。此功能值得重复,因为它是如此重要。

方法断点

方法断点是很有问题的。


作为复习,我们可以在方法中的任何一行放置一个行断点,断点将停在那里。这无处不在,我们称之为“断点”。


我们可以使用 control-F8 或在 mac 上使用Command-F8切换标准行断点。


当我们进入一个方法时,方法断点就会停止。你可能认为这没有用。为什么不使用行断点?


你是对的。方法断点要慢得多,你不应该使用它们:为此……虽然方法断点有一个用例。


请注意,由于方法断点非常慢,它们通常由 IDE 模拟。它只是使用行断点来模拟方法断点。这对我们作为 IDE 的用户来说几乎是无缝的,但我们需要了解它,因为在过去,情况并非如此。


您仍然可以在 StackOverflow 上找到用户抱怨方法断点速度缓慢的消息。


要查看方法断点的用例,让我们进入管理断点窗口。假设我们要中断名称以 Prime 开头的类中以字母 I 和 S 开头的所有方法。


我们可以通过使用这样的表达式来停止基于模式的所有此类方法来做到这一点。这可能看起来很牵强,但如果您有一个抽象基类,其子类遵循命名约定并且它们有许多相关方法,那么实际上非常有用。


你想跟踪一切,你可以使用这种方法来做到这一点。请注意,您还可以在此处使用跟踪点并获得非常深入的日志记录。我们将在几分钟内讨论跟踪点。

现场观察点

现场观察点不是典型的断点。每次字段值更改或每次读取时,观察点都会停止。这是一种非常酷的方式来捕获某些代码改变变量或找出字段值如何传播到代码中的情况。


请注意,我们可以使用此对话框调整它是在读取操作、写入操作还是同时停止。我们还可以像使用任何其他断点一样使观察点有条件。


它被称为观察点而不是断点,因为它不是代码停止的点。它在访问点停止,而不是在字段本身。

断点管理

IDE 为所有断点提供管理 UI。我们可以管理已有的断点并在视图断点菜单中创建新的断点。我可以通过查看断点菜单选项打开它,或者我可以使用Shift-Control-F8组合键。


请注意,在 Mac 上,我们需要使用命令而不是控制键。


在此对话框中,我们可以禁用、删除和编辑断点。您会注意到我们在这里有很多选项,我们很快就会讨论这些选项。我们可以从这里添加断点。有几个有趣的选项,但现在,我只添加一个简单的字段断点。

异常断点很糟糕(或者它们真的很糟糕吗?)

我们可以设置一个断点,在抛出异常时停止。但这有点问题。我有两个选择。首先,我可以通过名称捕获特定的异常。如果您事先知道将抛出的异常,这将很有用。


但是我想不出有多少发生这种情况的情况,而且我也不知道抛出异常的那一行。


更有价值的用例是捕获所有异常。这很有用的原因是我有时在调试时可能不会看控制台。异常可能会记录在那里,我可能会完全错过它们。


我可能会重新启动调试会话并可能会错过这些异常。但是,如果断点突然因异常而停止,则很难错过。问题是默认情况下捕获所有异常是错误的!


不幸的是,这很难在一个简单的 prime 主应用程序中展示,所以我将演示切换到一个简单的 spring boot 应用程序。应用程序的内容对于这种情况并不重要。让我们启用捕获任何异常,看看会发生什么……


启用捕获后,我尝试继续,但它立即一次又一次地击中断点。该代码在后台轮询 WebService。该 WebService 缺少 HTTP 标头,因此解析该标头的代码因 NumberFormatException 而失败。


我们被困在抛出该异常的代码上,这是有效的,因为在编写该代码时 Java 没有提供另一种解析数字标头的方法。


所以,我们能做些什么?这有效地使任何异常停止对几乎所有现实世界的应用程序都无用。


让我们稍微移动一下窗口,然后放大看看我们在这里做什么。


现在我们可以定义一个类过滤器。请注意,我在两个过滤器前面都加上了减号字符,以将其变成排除过滤器。在这里,我为所有java包和所有sun包定义了一个过滤器。这意味着在这些包中处理的每个异常都不会中断。


一旦我按下 OK,我就可以按下继续,应用程序运行而不会中断有问题的异常。其他异常将照常工作,并在我们的代码出现问题时通知我们!


令人惊讶的是,这不是 IDE 的默认设置。没有它,该功能几乎毫无用处。

跟踪点(又名日志点)

Tracepoints 或 LogPoints 是我们拥有的一些最重要的断点类型。我们可以通过按住 Shift 键并单击装订线来添加跟踪点。这将打开一个熟悉的断点对话框,但它看起来有点不同。首先,请注意这一点:


暂停选项未选中;请注意,我们可以通过取消选中暂停复选框将任何断点转换为跟踪点。默认情况下,断点中断。它停止并挂起当前线程,这样我们就可以从容地检查应用程序堆栈,看看发生了什么。


跟踪点不会挂起当前线程。应用程序命中断点,然后继续运行而不停止。这非常具有开创性,您如何跨过?


好吧,你不知道。相反,我们可以做其他几件事……


每当我们遇到断点时,我们都可以记录“断点命中”一词,但这并没有多大帮助,除非我们只有一个跟踪点并且只想知道我们是否到达了那个点。我们可以在每次到达跟踪点时打印堆栈跟踪,这确实更有用。但不是很多。


很难阅读列表中的大量痕迹并跟进。我想关注的是别的东西。


请注意,evaluate 和 log 中已经有cnt值,因为我在 IDE 中选择了cnt值,然后在装订线中按住 shift 键单击。


我们可以在这里写任何我们想要打印的表达式。我可以调用一个方法来编写描述性字符串等。这实际上是一个标准的日志语句,我可以将其动态添加到代码中。请注意,我仍然可以像任何条件断点一样使此跟踪点成为条件断点。


这意味着我可以在这个时间点打印任何值。就像任何记录器一样。这是一个了不起的功能。想象一下我们之前使用这些跟踪点讨论的方法断点;我们可以立即大规模登录。让我们按确定。


然后运行这个程序;我们可以看到我们在tracepoint中添加的日志打印到控制台,就像我们在代码中写的一样!

分组和命名

一旦我们扩大调试规模,分组和命名就变得至关重要。


我们可以使用描述来命名断点以指示其语义。当我们处理许多断点时,这非常有用。我们还可以根据文件、包等对它们进行分组。


但很棒的是,我们可以为断点创建自定义组,并通过一次切换禁用组中的断点。那很方便!


它使我们免于在尝试达到某个状态时单调地按继续键,并让我们管理许多断点。


但这里的真正价值在于规模。假设您有一个复杂的调试会话,其中同时运行多个跟踪点。


您可以对会话进行分组并禁用断点,同时切换到不同的分支以调试其他内容。然后回到完成后的位置。

禁用断点

有时断点不断,我们只需要一个特定的堆栈。


我们可以禁用断点,直到遇到不同的断点或异常,此时断点将自动启用。如果我们只想测试一个特定的路径,这就省去了一直按 continue 的需要。


这也适用于异常和异常断点,我们可以决定之后的行为。我们要再次禁用它还是照常继续?


这对于仅通过特定路径的故障情况非常有用。我可以向第一个方法添加一个跟踪点。然后在该跟踪点中禁用我想要的实际断点。

实例过滤器

实例过滤器让我们只接受来自特定对象实例的断点。

注意当前对象的实例在手表中标记为“this”。注意它有一个 at 符号,后面跟着数字 656。


这是 Java 对象 ID,相当于其他编程语言中的指针。在实例过滤器中,我们可以限制断点,使其仅针对特定对象命中。


假设此行断点被多个实例命中很多,但我只关心特定对象实例的结果,并想过滤掉所有噪音。我可以通过单击此处打开高级详细信息对话框。


我需要检查实例过滤器选项,然后输入我要过滤的实例的对象 ID。这意味着断点不会因其他实例类型而停止。要应用此更改,我按完成。


此时,我们可以看到实例过滤器仍在预期的断点处停止......

所以下一步是将实例过滤器更改为不同的对象实例。我正在编一个数字,因为这只是为了测试。


请注意,当我右键单击断点时,我得到了此对话框的自定义版本,因为我们有一个实例过滤器。这是一个非常巧妙的功能,使 UI 更易于使用。


现在我们已经做了那个改变,断点不再中断。

类过滤器

类过滤器对于典型的行断点没有意义。使用字段观察点或异常断点时,类过滤器很有意义。


在这种情况下,我有一个公共领域。我过滤对该字段的访问以忽略来自主要主类的所有访问。如果不同的类访问该字段,则会命中断点。


这非常有用,否则,我可能会在当前班级的观察点上获得很多点击。但我想看看其他情况。

来电过滤器

调用者过滤器根据调用方法的签名实现过滤器。要使用它,我们需要再次进入高级菜单。它支持通配符,所以我可以限制调用者只允许运行方法。


请注意,它使用 JVM 符号表示方法签名,这是一个非常复杂的主题,我将在稍后讨论。我的调试书中有一节介绍了这一点。


该方法按预期一直在断点处停止,因为正如我们在这里看到的那样,run 方法确实在堆栈跟踪中。因此,过滤器会正确应用并到达断点。


我们可以再次在过滤器的快速编辑 UI 中自定义断点。在这种情况下,我更改了过滤器以查找一个不存在的名为 stop 的方法,并且确实,断点不再中断。

最后一句话

这是一段很长的视频,希望您觉得它具有教育意义且内容全面。在下一个视频中,我们将讨论调试流和集合。


如果您有任何疑问,请使用评论部分。谢谢!