检测结构或哈希表何时被修改或损坏

时间:2018-11-21 19:10:39

标签: runtime-error runtime common-lisp hashtable

Common Lisp中是否可以使用以下工具来调试条件?

有一个被修改的哈希表(键的值之一变为NIL),我正在尝试对其进行调试。我能够将其范围缩小到可能发生的地方,但是该表并没有通过官方访问器在那里明确修改。

我无法发布代码,因为它是专有的。

1 个答案:

答案 0 :(得分:2)

我建议两件事。第一种是尝试跟踪修改哈希表的函数。那就是(setf gethash),除了gethash访问者而不是功能。要找出实现使用哪个函数来更新哈希表,请执行例如

(disassemble (compile (defun foo (x y) (setf (gethash x y) t))))

其中显示了汇编代码,其中应包含对内部函数的调用,其名称类似于puthash

(trace puthash)的操作可能会导致问题,因为实现可能会在此调用中更新哈希表。但是假设它可行,它可能会向您显示意外的哈希表更新发生的位置。

某些实现扩展了trace的功能。例如,Allegro CL报价many trace options。例如。如果您的函数分别称为foobar,并且您想查看这些函数中对excl::%puthash的调用,您会说:

(trace (excl::%puthash :inside (foo bar))

,如果您想查看调用的上下文,请添加一些堆栈:

cl-user(1): (trace (excl::%puthash :inside foo :show-stack 3))
(excl::%puthash)
cl-user(2): (setf (gethash 3 (make-hash-table)) 2)
;; Note: no trace output, because not in foo
2
cl-user(3): (foo 3 (make-hash-table))
;; Note: within foo, so trace output which includes 3 stack frames
 0[2]: (excl::%puthash 3 #<eql hash-table with 0 entries @ #x100192cfa72> t)
 0^      <- (system::..runtime-operation "fwrap_start" 3 #<eql hash-table with 0 entries @ #x100192cfa72> t)
 0^      <- (foo 3 #<eql hash-table with 0 entries @ #x100192cfa72>)
 0^      <- (eval (foo 3 (make-hash-table)))
 0[2]: returned t
t

在处理未知代码时,这样的跟踪非常有用。

第二件事是检查代码,以确保未修改哈希表中存储的键。例如

(let ((x (list 1 2))
      (ht (make-hash-table :test 'equal))
   (setf (gethash x ht) t)
   (setf (car x) 10) ;; not allowed!
   (gethash x ht)) ;; now in a funny state: may return T, or nil

请参见18.1.2 Modifying Hash Table Keys。 此外,literal objects可能也不会被修改,这也可能引起怪异:

(let ((x '(1 2)))
   (setf (car x) 10) ;; not allowed!