File: map-backtrace.lisp

package info (click to toggle)
cl-trivial-backtrace 20230111.git7f90b4a-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 156 kB
  • sloc: lisp: 395; makefile: 2
file content (125 lines) | stat: -rw-r--r-- 4,524 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
;;;; -*- Mode: LISP; Syntax: Ansi-Common-Lisp; Package: TRIVIAL-BACKTRACE; Base: 10; -*-
(in-package #:trivial-backtrace)

(defstruct frame
  func
  source-filename
  source-pos
  vars)

(defstruct var
  name
  value)

(defstruct pos-form-number
  number)

(defmethod print-object ((pos-form-number pos-form-number) stream)
  (cond 
    (*print-readably* (call-next-method))
    (t
     (format stream "f~A" (pos-form-number-number pos-form-number)))))


(defvar *trivial-backtrace-frame-print-specials*
  '((*print-length* . 100)
    (*print-level* . 20)
    (*print-lines* . 5)
    (*print-pretty* . t)
    (*print-readably* . nil)))

(defun print-frame (frame stream)
  (format stream "~A:~@[~A:~] ~A: ~%" 
	  (or (ignore-errors (translate-logical-pathname (frame-source-filename frame))) (frame-source-filename frame) "<unknown>")
	  (frame-source-pos frame)
	  (frame-func frame))
  (loop for var in (frame-vars frame)
	do 
	(format stream " ~A = ~A~%" (var-name var) 
		(or (ignore-errors 	
			(progv 
			    (mapcar #'car *trivial-backtrace-frame-print-specials*)
			    (mapcar #'cdr *trivial-backtrace-frame-print-specials*)
			  (prin1-to-string 
			   (var-value var))))
		    "<error>"))))

(defun map-backtrace (function)
  (impl-map-backtrace function))

(defun print-map-backtrace (&optional (stream *debug-io*) &rest args)
  (apply 'map-backtrace 
	 (lambda (frame)
	   (print-frame frame stream)) args))

(defun backtrace-string (&rest args)
  (with-output-to-string (stream)
    (apply 'print-map-backtrace stream args)))


#+ccl
(defun impl-map-backtrace (func)
  (ccl::map-call-frames (lambda (ptr &optional context) 
			  (multiple-value-bind (lfun pc)
			      (ccl::cfp-lfun ptr)
			    (let ((source-note (ccl:function-source-note lfun)))
			      (funcall func 
				       (make-frame :func (ccl::lfun-name lfun)
						   :source-filename (ccl:source-note-filename source-note)
						   :source-pos (let ((form-number (ccl:source-note-start-pos source-note)))
								 (when form-number (make-pos-form-number :number form-number)))
						   :vars (loop for (name . value) in (ccl::arguments-and-locals nil ptr lfun pc)
							       collect (make-var :name name :value value)))))))))

#+sbcl
(defun impl-map-backtrace (func)
  (loop for f = (or sb-debug:*stack-top-hint* (sb-di:top-frame)) then (sb-di:frame-down f)
	while f
	do (funcall func 
		    (make-frame :func 
				(ignore-errors
				  (sb-di:debug-fun-name 			    
				   (sb-di:frame-debug-fun f)))
				:source-filename 
				(ignore-errors
				  (sb-di:debug-source-namestring (sb-di:code-location-debug-source (sb-di:frame-code-location f))))
				:source-pos
				(ignore-errors ;;; XXX does not work
				  (let ((cloc (sb-di:frame-code-location f)))
				    (unless (sb-di:code-location-unknown-p cloc)
				      (format nil "tlf~Dfn~D"
					      (sb-di:code-location-toplevel-form-offset cloc)
					      (sb-di:code-location-form-number cloc)))))
				:vars
				(remove-if 'not 
					   (map 'list (lambda(v)
							(ignore-errors
							  (when (eq :valid
							     (sb-di:debug-var-validity v (sb-di:frame-code-location f)))
							    (make-var :name (sb-di:debug-var-symbol v)
								      :value (sb-di:debug-var-value v f)))))
						(ignore-errors (sb-di::debug-fun-debug-vars (sb-di:frame-debug-fun f)))))))))

#+clasp
(defun impl-map-backtrace (func)
  (clasp-debug:with-stack (stack)
      (clasp-debug:map-stack
       #'(lambda(current-frame)
           (funcall func
                    (let ((source (clasp-debug:frame-source-position current-frame)))
                      (make-frame :func (clasp-debug:frame-function current-frame)
                                  :source-filename (if source (clasp-debug:code-source-line-pathname source) nil)
                                  :source-pos (if source (clasp-debug:code-source-line-line-number source) nil)
                                  :vars (let ((index 0))
                                           (mapcar #'(lambda(argument)
                                                       (prog1
                                                           (make-var :name (format nil "Arg-~a" index) :value argument)
                                                         (incf index)))
                                                   (clasp-debug:frame-arguments current-frame)))))))
       
       stack)))

#-(or ccl sbcl clasp)
(defun impl-map-backtrace (func)
  (declare (ignore func))
  (warn "unable to map backtrace for ~a" (lisp-implementation-type)))