Clozure CL中文版006:调试
Category:帮助手册调试
打破循环
*break-on-warnings*[变量]
如果为true,warn将进入中断循环。
此变量已从ANSI CL中删除。理由是可以实现同样的效果(setq *break-on-signals* ‘warning)。
*break-on-errors*[变量]
如果为true(默认值),则在发出错误信号时,lisp将进入中断循环。
*show-restarts-on-break*[变量]
如果为true,则在进入中断循环之前自动打印可用的重新启动。
跟踪
Clozure CL的跟踪工具由Common Lisp trace宏的扩展版本调用。扩展允许跟踪方法,以及更好地控制跟踪操作。
trace { keyword global-value }* { spec | ( spec { keyword local-value }*)}* [Macro]
该trace宏封装了命名的功能 规格 S,导致跟踪行动发生在每个函数入口和出口。默认操作在函数进入和退出时打印消息。关键字 / 值选项可用于指定默认行为的更改。
(trace)不带参数 调用会返回被跟踪的函数列表。
甲规格可以是一个符号,它是一个函数的名称,或形式的表达,或在形式的通用函数的具体方法,其中,专用函数可以是一个类或一个的名称 专用函数。(:method gf-name {qualifier}* ({specializer}*)) EQL
一个规范也可以是一个字符串,指定一个包,或者等价的列表,以请求包中的所有功能进行跟踪。(:package package-name)
默认情况下,只要输入或退出跟踪函数,就会打印一条短消息,*trace-output*显示条目上的参数和退出时的值。指定为键/值对的选项可用于修改此行为。函数规范之前的选项适用于所跟踪的所有函数。与规范一起指定的选项 仅适用于该规范并覆盖任何全局选项。支持以下选项:
:methods {T | nil}
如果为true,并且如果应用于命名泛型函数的规范,则除了泛型函数本身之外,还要安排跟踪泛型函数的所有方法。
:inside outside-spec | ({outside-spec}*)
禁止所有跟踪操作,除非被跟踪函数的当前调用位于外部规范之一,即除非外部规范之一的函数当前在堆栈中。 如上所述,outside-spec可以命名函数,方法或包。
:if form,:condition form
每当要跟踪的函数即将被输入时评估表单,并在表单返回nil时禁止所有跟踪操作。表单可以引用词法变量ccl::args,它是此调用中的参数列表。:condition它只是一个同义词:if,但如果两者都被指定,则两者都必须返回非零值。
:before-if form
每当要跟踪的函数即将被输入时评估表单,并在表单返回nil时禁止条目跟踪操作。表单可以引用词法变量ccl::args,它是此调用中的参数列表。如果同时指定了两者 :if,:before-if则两者都必须返回非nil才能执行before entry操作。
:after-if form
每当被跟踪的函数刚刚退出时评估表单,并在表单返回nil时禁止退出跟踪操作。表单可以引用词法变量ccl::vals,该变量是此调用返回的值列表。如果同时指定了两者:if ,:after-if则两者都必须返回非nil才能执行after exit操作。
:print-before form
每当要输入被跟踪的函数时评估表单,并在打印标准条目消息之前打印结果。表单可以引用词法变量ccl::args,它是此调用中的参数列表。要查看多个表单,请使用values: :print-before (values (one-thing) (another-thing))。
:print-after form
每当被跟踪的函数刚刚退出时评估表单,并在打印标准退出消息后打印结果。表单可以引用词法变量ccl::vals,该变量是此调用返回的值列表。要查看多个表单,请使用values: :print-after (values (one-thing) (another-thing))。
:print form
相当于。:print-before form :print-after form
:eval-before form
只要输入要跟踪的功能,就评估表单。表单可以引用词法变量ccl::args,它是此调用中的参数列表。
:eval-after form
只要函数刚刚退出,就可以评估表单。表单可以引用词法变量ccl::vals,该变量是此调用返回的值列表。
:eval form
相当于。:eval-before form :eval-after form
:break-before form
只要输入要跟踪的函数,就会计算表单,如果结果是非零,则进入调试器中断循环。表单可以引用词法变量ccl::args,它是此调用中的参数列表。
:break-after form
评估形式每当被跟踪的功能刚刚退出,并且如果结果是非零,进入调试中断循环。表单可以引用词法变量ccl::vals,该变量是此调用返回的值列表。
:break form
相当于。:break-before form :break-after form
:backtrace-before form,:backtrace form
只要输入要跟踪的功能,就评估表单。表单可以引用词法变量ccl::args,它是此调用中的参数列表。表单返回的值 解释如下:
nil
什么也没做
:detailed
打印详细的回溯*trace-output*。
(:detailed integer)
打印 详细回溯的顶部整数帧*trace-output*。
整数
打印简洁回溯的顶部 整数帧 *trace-output*。
还要别的吗
打印简洁的回溯*trace-output*。
请注意,与其他选项不同,它只:backtrace相当于 :backtrace-before前后两个,因为在函数调用之前和之后打印相同的回溯通常没有帮助。
:backtrace-after form
只要被跟踪的函数刚刚退出,就会对表单进行评估。表单可以引用词法变量ccl::vals,该变量是此调用返回的值列表。表单返回的值 解释如下:
nil
什么也没做
:detailed
打印详细的回溯*trace-output*。
(:detailed integer)
打印 详细回溯的顶部整数帧*trace-output*。
整数
打印简洁回溯的顶部整数帧 *trace-output*。
还要别的吗
打印简洁的回溯*trace-output*。
:before行动
指定在输入跟踪函数之前要采取的操作。 行动是以下之一:
默认情况下,打印一条缩短的缩进消息,显示函数名称和调用参数
:break
相当于 :before :print :break-before t
:backtrace
相当于 :before :print :backtrace-before t
功能
任何其他值都被解释为在输入时调用而不是打印标准条目消息的函数。它的第一个参数是被跟踪函数的名称,其余参数是被跟踪函数的所有参数,并 *trace-level*绑定到当前嵌套操作的嵌套级别。
:after行动
指定在跟踪函数退出后立即执行的操作。 行动是以下之一:
默认情况下,打印一个缩短的缩进消息,显示函数名称和返回值
:break
相当于 :after :print :break-after t
:backtrace
相当于 :after :print :backtrace-after t
功能
任何其他值都被解释为在退出时调用而不是打印标准退出消息的函数。调用它的第一个参数是被跟踪函数的名称,其余参数是被跟踪函数返回的所有值,ccl:* trace-level *绑定到当前嵌套操作的嵌套级别。
ccl:*trace-level*[变量]
在执行跟踪操作之前和之后执行时绑定到当前嵌套级别的变量。默认打印操作使用它来确定缩进量。
ccl:*trace-max-indent*[变量]
*trace-max-indent* 无论当前跟踪级别如何,打印操作之前和之后的默认值都不会缩进超过的值。
ccl:trace-function spec &key { keyword value } *[功能]
这是TRACE宏的功能版本。 spec和关键字 s与TRACE相同,除了评估所有参数。
ccl:*trace-print-level*[变量]
打印时,默认打印操作将绑定*print-level* 到此值。请注意,此重新绑定仅在默认的进入和退出消息期间有效。它不适用于:print-before/:print-after表单打印或用户代码完成的任何显式打印。
ccl:*trace-print-length*[变量]
打印时,默认打印操作将绑定*print-length* 到此值。请注意,此重新绑定仅在默认的进入和退出消息期间有效。它不适用于:print-before/:print-after表单打印或用户代码完成的任何显式打印。
ccl:*trace-bar-frequency*[变量]
默认情况下,这是零。如果非nil它应该是一个整数,并且默认的入口和出口消息将打印| 而不是空间每一个这么多级别的缩进。
指导
该advise宏可以被认为是一个更普遍的版本trace。它允许您指定的代码在给定函数之前,之后或周围运行,以便更改函数的行为。每条添加的代码都称为一条建议。每一条建议都有一个唯一的名称,这样就可以有同样的功能有多个通知,包括多 :before,:after和 :around咨询件。
在:name和:when 关键字用于识别忠告。到以后的呼叫 advise用的相同的值 :name和:when将取代现有的忠告; 具有不同值的呼叫不会。
advise spec form &key when name [宏]
根据表单向 spec 指定的函数或方法添加一条建议。
规范
提出建议的功能规范。这可以是作为函数或泛型函数名称的符号,也可以是表单(setf 符号)的表达式,或者是表单中的泛型函数的特定方法(:method symbol {qualifiers}(specializer {specializer} ))。
形成
在建议函数之前,之后或周围执行的表单。表单可以引用绑定到调用建议函数的参数的变量arglist。您可以使用(返回)退出表单。
名称
标识建议的名称。
什么时候
一个参数,指定何时运行建议。有三个允许的值。默认值为 :before,指定在调用建议函数之前执行表单。其他可能的值是:after,指定在调用建议函数后执行表单,并指定在对建议函数的调用:around周围执行表单。(:do-it) 在表单中使用以指示原始定义的调用。
foo已定义 的函数使用数字列表执行某些操作。如果任何参数不是数字,则以下代码使用一条建议使foo返回零。使用:围绕建议,您可以执行以下操作:
(advise foo (if (some #'(lambda (n) (not (numberp n))) arglist) 0 (:do-it)) :when :around :name :zero-if-not-nums)
使用a之前做同样的事情:在建议之前:
(advise foo (if (some #'(lambda (n) (not (numberp n))) arglist) (return 0)) :when :before :name :zero-if-not-nums)
unadvise spec &key when name [宏]
删除符合规范,时间和名称的一条或多条建议。
所述的Unadvise宏移除片或建议的匹配件spec,when和name。当值为 spect且值为when 和namenil时,unadvise会删除每条建议; 当spect为t时,参数when为nil,并且 name为非nil,unadvise将删除具有给定名称的所有建议。
这些论点与中的含义相同 advise。
advisedp spec &key when name [宏]
返回与spec,when和name匹配的建议列表。
该advisedp宏返回意见的现有块匹配的列表spec,when和name。当值为 spect且值为 when和namenil时,advisoryp将返回所有现有的建议。
这些论点与中的含义相同 advise。
看了对象
从版本1.4开始,Clozure CL提供了一种监视lisp对象的方法,以便在线程尝试写入被监视对象时发出信号。对于某类错误(有人正在改变这个值,但我不知道是谁),这可能非常有用。
watch &optional object [功能]
监视lisp对象以进行写入。
目的
任何内存分配的lisp对象。
WATCH函数安排监视指定对象的写入。这是通过将对象复制到其自己的一组虚拟内存页面来实现的,然后对其进行写保护。这种保护由计算机的内存管理硬件强制执行; 写保护根本不会减慢读取速度。
当尝试对对象的任何写入时,将发信号通知WRITE-TO-WATCHED-OBJECT条件。
当没有参数调用时,WATCH返回当前正在监视的对象的新分辨率列表。
如果无法监视对象,则WATCH返回NIL(通常因为对象位于静态或纯内存区域)。
WATCH的运作水平相当低; 无法避免对象内部表示的细节。然而,为方便起见,WATCHing标准实例,散列表或多维或非简单CL阵列将分别观察基础槽矢量,散列表矢量或数据矢量。
WATCH可以监视任何内存分配的lisp对象。
在Clozure CL中,内存分配的对象是cons单元或uvector。
WATCH在cons单元上运行,而不是列表。为了观察cons细胞链,每个cons细胞必须单独观察。因为每个观看的cons单元占用自己的虚拟内存页(4 KB),所以只能观看相对较短的列表。
如果内存分配的对象不是cons单元,那么它就是一个类似矢量的对象,称为uvector。uvector是一个内存分配的lisp对象,其第一个单词是一个标题,用于描述对象的类型和它包含的元素数。
因此,哈希表是一个uvector,如字符串,标准实例,双浮点数,CL数组或向量,等等。
一些CL对象(如字符串和其他简单向量)以直接的方式映射到uvector表示。很容易理解在这种情况下会发生什么。uvector索引直接对应于向量索引:
? (defvar *s* “xxxxx”)*S*? (watch *s*)”xxxxx”? (setf (char *s* 3) #\o)> Error: Write to watched uvector “xxxxx” at index 3> Faulting instruction: (movl (% eax) (@ -5 (% r15) (% rcx)))> While executing: SET-CHAR, in process listener(1).> Type :POP to abort, :R for a list of available restarts.> Type 😕 for other options.
在更复杂的对象(例如,散列表,标准实例,包等)的情况下,uvector的元素就像结构中的槽。有必要知道哪些“槽”包含在写入对象时将被更改的数据。
如上所述,watch了解数组,散列表和标准实例,并将自动观察包含相应数据的元素。
一个例子可能会使这更清楚。
? (defclass foo () (slot-a slot-b slot-c))#<STANDARD-CLASS FOO>? (defvar *a-foo* (make-instance ‘foo))*A-FOO*? (watch *a-foo*)#<SLOT-VECTOR #xDB00D>;;; Note that WATCH has watched the internal slot-vector object? (setf (slot-value *a-foo* ‘slot-a) ‘foo)> Error: Write to watched uvector #<SLOT-VECTOR #xDB00D> at index 1> Faulting instruction: (movq (% rsi) (@ -5 (% r8) (% rdi)))> While executing: %MAYBE-STD-SETF-SLOT-VALUE-USING-CLASS, in process listener(1).> Type :POP to abort, :R for a list of available restarts.> Type 😕 for other options.
查看回溯可能会显示写入的对象和插槽名称。
请注意,即使写入了slot-a,uvector索引也是1(不是0)。这是因为slot-vector的第一个元素是指向拥有槽的实例的指针。我们可以检索它以查看被修改的对象:
1 > (uvref (write-to-watched-object-object *break-condition*) 0)#<FOO #x30004113502D>1 > (describe *)#<FOO #x30004113502D>Class: #<STANDARD-CLASS FOO>Wrapper: #<CLASS-WRAPPER FOO #x300041135EBD>Instance slotsSLOT-A: #<Unbound>SLOT-B: #<Unbound>SLOT-C: #<Unbound>1 >
unwatch object对象[功能]
停止监视lisp对象以进行写入。
UNWATCH函数确保指定的对象位于正常的非监视内存中。如果当前没有观察对象,则UNWATCH不执行任何操作并返回NIL。否则,返回新的未观察对象。
write-to-watched-object[条件]
在尝试写入被监视对象时发出信号。
写入监视对象时会发出此信号。有三个感兴趣的插槽:
目的
作为写入目标的实际对象。
抵消
从标记对象指针到写入地址的字节偏移量。
指令
尝试写入的反汇编机器指令。
提供了一些重启:一个将跳过错误的写入指令并继续; 另一个提供观察对象并继续。
还有一个模拟重启。在一些常见情况下,可以模拟错误写入指令,使得能够执行写入而无需取消监视对象(因此让其他线程可能写入它)。如果无法识别错误指令,则不会提供模拟重启。
笔记
虽然已经采取了一些措施来尽量减少因观察和取消多个线程中的对象而产生的潜在问题,但可能存在可能导致不良行为的微妙竞争条件。
例如,假设一个线程试图写入被监视的对象。这会导致操作系统生成异常。lisp内核计算出异常是什么,并调用回lisp来发出写入对象的状态并可能处理错误。
现在,很快lisp代码再次开始运行(对于回调),其他一些线程可能无法观察导致异常的被监视对象,可能在我们甚至有机会发出信号之前,更不用说它了。
从处理程序下面取出对象可能至少会混淆它,如果不是造成更深层次的麻烦。用手表小心。
例子
除了上面观察字符串和标准实例的示例之外,还有一些示例。
花式阵列
? (defvar *f* (make-array ‘(2 3) :element-type ‘double-float))*F*? (watch *f*)#(0.0D0 0.0D0 0.0D0 0.0D0 0.0D0 0.0D0);;; Note that the above vector is the underlying data-vector for the array? (setf (aref *f* 1 2) pi)> Error: Write to watched uvector #<VECTOR 6 type DOUBLE-FLOAT, simple> at index 5> Faulting instruction: (movq (% rax) (@ -5 (% r8) (% rdi)))> While executing: ASET, in process listener(1).> Type :POP to abort, :R for a list of available restarts.> Type 😕 for other options.1 >
在这种情况下,报表中的uvector索引是写入的元素的行主索引。
哈希表
哈希表非常复杂。散列表的表示包括称为散列表向量的元素。元素的键和值在该向量中成对存储。
尝试监视写入散列表的一个问题是,当散列表被重新散列时,底层散列表向量将被全新的替换。重新散列后,哈希表不会使用先前监视的哈希表向量,并且不会捕获对新向量的写入。
? (defvar *h* (make-hash-table))*H*? (setf (gethash ‘noise *h*) ‘feep)FEEP? (watch *h*)#<HASH-TABLE-VECTOR #xDD00D>;;; underlying hash-table-vector? (setf (gethash ‘noise *h*) ‘ding)> Error: Write to watched uvector #<HASH-TABLE-VECTOR #xDD00D> at index 35> Faulting instruction: (lock)> (cmpxchgq (% rsi) (@ (% r8) (% rdx)))> While executing: %STORE-NODE-CONDITIONAL, in process listener(1).> Type :POP to abort, :R for a list of available restarts.> Type 😕 for other options.;;; see what value is being replaced…1 > (uvref (write-to-watched-object-object *break-condition*) 35)FEEP;;; backtrace shows useful context1 > :b*(1A109F8) : 0 (%STORE-NODE-CONDITIONAL ???) NIL (1A10A50) : 1 (LOCK-FREE-PUTHASH NOISE #<HASH-TABLE :TEST EQL size 1/60 #x30004117D47D> DING) 653 (1A10AC8) : 2 (CALL-CHECK-REGS PUTHASH NOISE #<HASH-TABLE :TEST EQL size 1/60 #x30004117D47D> DING) 229 (1A10B00) : 3 (TOPLEVEL-EVAL (SETF (GETHASH # *H*) ‘DING) NIL) 709 …
清单
如前所述,WATCH只监视个体利弊细胞。
? (defun watch-list (list) (maplist #’watch list))WATCH-LIST? (defvar *l* (list 1 2 3))*L*? (watch-list *l*)((1 2 3) (2 3) (3))? (setf (nth 2 *l*) ‘foo)> Error: Write to the CAR of watched cons cell (3)> Faulting instruction: (movq (% rsi) (@ 5 (% rdi)))> While executing: %SETNTH, in process listener(1).> Type :POP to abort, :R for a list of available restarts.> Type 😕 for other options.h
记忆
heap-utilization &key (stream * debug-io *)(gc-first t)区域 单位 (sort :size)类 启动 阈值)[功能]
此函数遍历lisp堆,收集有关存储在堆上的对象的信息,并将结果报告打印到由关键字参数指定的流中:stream。它显示了每种类型的对象数量,它们的逻辑大小的总和(对象的数据部分的大小)以及它们的物理大小的总和(由计算的总大小object-direct-size)。
如果:gc-first为true(默认值),heap-utilization 则在扫描堆之前执行完整的gc。
如果:classes为true,则按类而不是基本类型对对象进行分类。
关键字参数:area可用于将walk限制为一个内存区域或区域列表。一些可能的值是:dynamic,:static,:managed-static,和 :readonly。默认情况下,将检查所有区域(包括堆栈)。
所述keword参数:sort可以是一个:count,:logical-size或:physical-size通过计数或大小进行排序输出。
默认情况下,大小以字节为单位显示。关键字参数 :unit可以是:kb,, :mb或:gb 以这些单位显示大小。
如果:start是非零的,它应该是一个返回的对象 ccl::get-allocation-sentinel; 只扫描较高地址的对象(粗略地说,只有在它之后分配的对象)。
如果阈值为非零,则它应该是介于0和1之间的数字。堆的份额小于阈值的所有类型将在“所有其他”行中集中在一起,而不是单独列出。
object-direct-size thing事[功能]
此函数以字节为单位返回事物的大小,包括任何标头和对齐开销。它不会下降到对象的组件中。
%address-of[功能]
此函数以整数形式返回thing的地址。如果事情是一个Fixnum,事情简直是返回。
请注意,除了fixnums之外的其他类型表示为立即值而不是堆分配的对象。在各种平台上,这些可能包括字符和单个flosts,以及可能包含的其他值。该%address-of函数将为这些对象返回相当无用的值。
返回的值%address-of仅对调试有用,因为GC可以随时运行并可以在内存中移动对象,从而更改其地址。
拆卸
*disassemble-verbose*[变量]
当为真时,输出disassemble可以包括依赖于平台的附加信息。例如,在x86端口上,输出将包含x86操作码字节。
默认值为nil。
来源说明
源位置被记录在源音符对象,具有存取source-note-filename,source-note-start-pos,source-note-end-pos,和source-note-text。
开始和结束位置是文件位置而不是字符位置。nil除非源记录在读取时打开,否则文本将是。如果原始源文件仍然可用,ensure-source-note-text 将强制从文件中读取缺少的源文本。
源注释与定义(via record-source-file)相关联,并且还存储在函数对象(包括匿名和本地函数)中。前者可以通过 find-definition-sources,后者通过 function-source-note。
源记录由变量控制 *save-source-locations*。
PC到源映射由*record-pc-mapping*。
*record-source-file*[变量]
如果为true,则记录定义的源文件信息。
*save-source-locations*[变量]
此变量控制如何记录源位置信息。
nil
不记录源位置信息。如果*record-source-file*为true,仍将保存定义的文件名信息。
t
存储功能对象和定义的源位置信息,包括原始源文本。
:no-text
存储源位置信息,但不存储原始源文本的副本。这是一种优化,可用于编译不希望更改的文件。
*record-pc-mapping*[变量]
如果为true,则记录PC到源映射(但前提*save-source-locations*是也是如此)。
function-source-note f[功能]
返回函数f的源注释对象。
source-note-filename source来源[功能]
返回source的文件名。
source-note-start-pos source-note来源记录[功能]
返回source-note描述的东西的起始文件位置(不是字符位置)。
source-note-end-pos source-note来源记录[功能]
返回source-note描述的东西的结束文件位置(不是字符位置)。
source-note-text source-note &optional start end [功能]
返回由start和end分隔的已保存源文本。
ensure-source-note-text source-note &key if-does-not-exist [功能]
如果原始文件中的源文本尚未存在于源文档中,请从源文本中读取。
http://mip.i3geek.com