Skip to content

Commit a6fdd27

Browse files
quicklispxach
authored andcommitted
Snapshot of various works in progress.
1 parent 5713773 commit a6fdd27

12 files changed

+390
-44
lines changed

daily.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22

33
cd `dirname $0`
44
PATH=$PATH:/usr/local/bin
5-
screen -c daily.screenrc -dmS daily-build
5+
screen -c daily.screenrc -dmLS daily-build

depcheck.lisp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@
200200
(when (equalp (second argv) "--sbcl-version")
201201
(format t "~A~%" (lisp-implementation-version))
202202
(sb-ext:exit :code 0))
203+
(when (getenv "DEPCHECK_HIDEBUG")
204+
(sb-ext:restrict-compiler-policy 'debug 3)
205+
(sb-ext:restrict-compiler-policy 'safety 3))
203206
(unless (getenv "DEPCHECK_DEBUG")
204207
(sb-ext:disable-debugger))
205208
(setenv "SBCL_HOME"

failure-report.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
pre.snippet {
2+
overflow-wrap: break-word;
3+
color: black;
4+
font-weight: normal;
5+
white-space: pre-wrap;
6+
width: 75em;
7+
}
8+
19
.failing-system pre {
210
color: #111;
311
margin-left: 2em;

html-failure-report.lisp

Lines changed: 183 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,88 @@ the string is returned unchanged."
154154
(subseq line end))))
155155

156156

157+
;;; Scraping error logs for the real problem
158+
159+
(defstruct peekstream
160+
stream
161+
buffered-line)
162+
163+
(defun next-line (peekstream)
164+
(if (peekstream-buffered-line peekstream)
165+
(shiftf (peekstream-buffered-line peekstream) nil)
166+
(read-line (peekstream-stream peekstream) nil)))
167+
168+
(defun peek-line (peekstream)
169+
(or (peekstream-buffered-line peekstream)
170+
(setf (peekstream-buffered-line peekstream)
171+
(read-line (peekstream-stream peekstream) nil))))
172+
173+
(defun unread-line (line peekstream)
174+
(setf (peekstream-buffered-line peekstream) line))
175+
176+
(defun =line-matches (pattern)
177+
(let ((scanner (ppcre:create-scanner pattern)))
178+
(lambda (line)
179+
(ppcre:scan scanner line))))
180+
181+
(defun callfun (object)
182+
(lambda (fun)
183+
(funcall fun object)))
184+
185+
(defun =or (&rest funs)
186+
(lambda (object)
187+
(some (callfun object) funs)))
188+
189+
(defun =not (fun)
190+
(complement fun))
191+
192+
(defun skip-text-until (fun stream)
193+
(loop for line = (peek-line stream)
194+
while line
195+
do
196+
(cond ((funcall fun line)
197+
(return line))
198+
(t
199+
(next-line stream)))))
200+
201+
(defun collect-text-while (fun stream)
202+
(with-output-to-string (s)
203+
(loop for line = (peek-line stream)
204+
while (and line (funcall fun line))
205+
do (write-line (next-line stream) s))))
206+
207+
(defun collect-text-between (start-fun end-fun stream)
208+
(let* ((first-line (skip-text-until start-fun stream))
209+
(rest (and first-line (next-line stream) (collect-text-while (=not end-fun) stream))))
210+
(when first-line
211+
(concatenate 'string
212+
first-line #(#\newline)
213+
rest))))
214+
215+
(defun extract-warnings-and-errors (file)
216+
"Scrape warnings and errors out of the logfile FILE. Repeated
217+
consecutive strings are coalesced into a single string to avoid
218+
redundant info."
219+
(flet ((coalesce-consecutive-strings (strings)
220+
(let ((out '()))
221+
(dolist (string strings (nreverse out))
222+
(unless (equal (first out) string)
223+
(push string out))))))
224+
(coalesce-consecutive-strings
225+
(with-open-file (stream file)
226+
(let ((pstream (make-peekstream :stream stream)))
227+
(let (result)
228+
(loop
229+
(let ((match (collect-text-between (=or (=line-matches "^; caught (COMMON-LISP:)?(WARNING|ERROR):")
230+
(=line-matches "^Unhandled"))
231+
(=not (=line-matches "^;"))
232+
pstream)))
233+
(if match
234+
(push match result)
235+
(return (nreverse result)))))))))))
236+
237+
238+
157239
;;; Posting to S3
158240

159241
(defun report-publishing-enabled-p ()
@@ -208,6 +290,8 @@ the string is returned unchanged."
208290
(defgeneric write-html-failure-report-content (object stream))
209291
(defgeneric write-html-failure-report-footer (object stream))
210292

293+
(defgeneric failure-impact (object))
294+
211295
(defmethod failure-report-html-file (base object)
212296
(relative-to base (failure-report-url object)))
213297

@@ -225,12 +309,39 @@ the string is returned unchanged."
225309
:reader source)
226310
(failure-log-file
227311
:initarg :failure-log-file
228-
:reader failure-log-file)))
312+
:reader failure-log-file)
313+
(warnings-and-errors
314+
:reader warnings-and-errors)
315+
(breaks
316+
:accessor breaks
317+
:initform nil)
318+
(broken-by
319+
:accessor broken-by
320+
:initform nil)))
229321

230322
(defmethod print-object ((object failing-system) stream)
231323
(print-unreadable-object (object stream :type t)
232324
(write-string (system-name object) stream)))
233325

326+
(defmethod failure-impact ((object failing-system))
327+
(length (breaks object)))
328+
329+
(defmethod slot-unbound ((class t) (instance failing-system) (slot (eql 'warnings-and-errors)))
330+
(setf (slot-value instance 'warnings-and-errors)
331+
(extract-warnings-and-errors (failure-log-file instance))))
332+
333+
(defun broken-by-name (failing-system)
334+
(let* ((unhandled-log-line
335+
(find "Unhandled" (warnings-and-errors failing-system)
336+
:test #'search))
337+
(responsible-system-name
338+
(ppcre:register-groups-bind (system-name)
339+
("^Unhandled.*while compiling.*SOURCE-FILE \"(.*?)\""
340+
unhandled-log-line)
341+
system-name)))
342+
(or responsible-system-name
343+
(system-name failing-system))))
344+
234345
(defmethod new-failure-p ((object failing-system))
235346
(let* ((dist (ql-dist:find-dist "quicklisp"))
236347
(existing-system
@@ -239,22 +350,11 @@ the string is returned unchanged."
239350
(< (days-old (source object)) 30))))
240351

241352
(defmethod failure-data ((source upstream-source))
242-
(let ((result '()))
243-
(map-source-systems
244-
source
245-
(lambda (system-file-name system-name)
246-
(write-char #\. *trace-output*)
247-
(force-output *trace-output*)
248-
(let ((file (winfail-file "fail" source system-file-name system-name)))
249-
(when (probe-file file)
250-
(push (make-instance 'failing-system
251-
:system-name system-name
252-
:system-file-name
253-
system-file-name
254-
:source source
255-
:failure-log-file file)
256-
result)))))
257-
result))
353+
(remove (name source)
354+
(failing-systems)
355+
:test-not 'string=
356+
:key (lambda (system)
357+
(name (source system)))))
258358

259359
(defmethod name ((object failing-system))
260360
(name (source object)))
@@ -282,6 +382,9 @@ the string is returned unchanged."
282382
(name (source object))
283383
(length (failure-data object)))))
284384

385+
(defmethod failure-impact ((object failing-source))
386+
(reduce #'+ (mapcar #'failure-impact (failure-data object))))
387+
285388
(defmethod source-link ((source failing-source))
286389
(source-link (source source)))
287390

@@ -347,9 +450,29 @@ source is found that matches the filename, return nil."
347450
(directory fail-wild)))
348451

349452
(defun failing-systems ()
350-
(remove nil
351-
(mapcar #'parse-failure-file-name
352-
(failing-source-log-files))))
453+
;; This is the best way to get failure info, because it populates
454+
;; useful failure cross-reference data.
455+
(let* ((systems (remove nil
456+
(mapcar #'parse-failure-file-name
457+
(failing-source-log-files))))
458+
(table (make-hash-table :test 'equal)))
459+
(dolist (system systems)
460+
(setf (gethash (system-name system) table) system))
461+
(dolist (system systems)
462+
(let ((broken-by (or (gethash (broken-by-name system) table)
463+
system)))
464+
(unless (eq broken-by system)
465+
(setf (broken-by system)
466+
broken-by)
467+
(push system (breaks broken-by)))))
468+
systems))
469+
470+
(defun who-is-broken-by (name)
471+
(remove name (failing-systems)
472+
:test-not #'string=
473+
:key (lambda (failing-system)
474+
(and (broken-by failing-system)
475+
(system-name (broken-by failing-system))))))
353476

354477
(defun failure-log-failure-report ()
355478
"Scan the failure log files of all projects to produce a failure report."
@@ -402,6 +525,13 @@ source is found that matches the filename, return nil."
402525
(loop for scanner in *log-lines-that-are-boring*
403526
thereis (ppcre:scan scanner line)))
404527

528+
(defun failure-snippet (object)
529+
(etypecase object
530+
(failing-system
531+
(format nil "~{~A~^...~%~}~%"
532+
(extract-warnings-and-errors (failure-log-file object))))
533+
(failing-source
534+
(format nil "~{~A~}" (mapcar #'failure-snippet (failure-data object))))))
405535

406536
(defmethod write-html-failure-report-header (object stream)
407537
(format stream "<html><head><title>~A</title>~
@@ -476,22 +606,45 @@ source is found that matches the filename, return nil."
476606
(new (remove-if-not #'new-failure-p sources))
477607
(old (remove-if #'new-failure-p sources)))
478608
(flet ((show (sources)
479-
(dolist (source sources)
609+
(dolist (source (sort (copy-seq sources) #'string< :key #'name))
480610
(let ((link (source-link source)))
481611
(format stream "<li~@[ ~*class='new-failure'~]> ~A:<br>"
482612
(new-failure-p source)
483613
(name source))
614+
(let ((age (source-cache-age-or-nil (source source))))
615+
(when age
616+
(format stream "last modified ~A ago<br>~%" (how-long-ago age))))
484617
(if link
485618
(format stream "<a class='source-link' href='~A'>~A</a>" link link)
486619
(format stream "<span class='source-location'>~A</span>" (location (source source))))
487620
(format stream "</li>~%")
488621
(format stream "<ul>")
489-
(dolist (system (failure-data source))
490-
(format stream "<li~@[ ~*class='new-failure'~]> <a href='~A'>~A</a></li>~%"
491-
(new-failure-p system)
492-
(failure-report-url system)
493-
(system-name system))))
494-
(format stream "</ul>~%"))))
622+
(dolist (system (sort (copy-seq (failure-data source)) #'string< :key #'system-name))
623+
(let ((responsible (broken-by system))
624+
(system-name (system-name system)))
625+
(format stream "<li~@[ ~*class='new-failure'~]> <a name='~A'></a><a href='~A'>~A</a>"
626+
(new-failure-p system)
627+
system-name
628+
(failure-report-url system)
629+
system-name)
630+
(when responsible
631+
(format stream " <i>caused by <a href='#~A'>~A</a></i>~%"
632+
(system-name responsible)
633+
(system-name responsible)))
634+
(when (breaks system)
635+
(format stream "<br>Breaks: ")
636+
(dolist (broken (breaks system))
637+
(format stream "<a href='#~A'>~A</a> "
638+
(system-name broken)
639+
(system-name broken)))
640+
(format stream "<br>"))
641+
(unless responsible
642+
(format stream "<pre class='snippet'>~A</pre>"
643+
(cl-who:escape-string (failure-snippet system))))
644+
(when responsible
645+
(format stream "<br>"))
646+
(format stream "</li>~%"))))
647+
(format stream "</ul>"))))
495648
(show new)
496649
(format stream "<br><br>")
497650
(show old)))
@@ -503,6 +656,9 @@ source is found that matches the filename, return nil."
503656
(let ((link (source-link (source object))))
504657
(when link
505658
(format stream "<li> site: <a href='~A'>~A</a>~%" link link)))
659+
(let ((age (source-cache-age-or-nil (source object))))
660+
(when age
661+
(format stream "<li> last updated: ~A ago" (how-long-ago age))))
506662
(format stream "</ul>~%")
507663
(format stream "<p>~A~%" (versions-and-such)))
508664

misc.lisp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@
66
(run "rm" "-rf" (merge-pathnames ".cache/common-lisp/"
77
(user-homedir-pathname))))
88

9+
(defun clear-all-caches ()
10+
(clear-fasl-cache)
11+
(clear-dist-caches)
12+
(clear-system-file-magic-cache))
13+
914
(defun system-from-release (system-name dist)
1015
(let* ((dist (ql-dist:dist dist))
1116
(system (ql-dist:find-system-in-dist system-name dist))
@@ -162,15 +167,27 @@
162167
(check-for-program program)))
163168

164169
(defun crank (&optional (source *last-source*))
170+
(setf source (source-designator source))
165171
(check-critical-programs)
166172
(unless (source-designator source)
167173
(warn "Not a known source -- ~S" source)
168174
(return-from crank nil))
169175
(setf *last-source* source)
170176
(update-system-file-index)
171-
(let ((wins (find-more-winning-systems source)))
172-
(list :fails (missing-components source)
173-
:wins wins)))
177+
(find-more-winning-systems source)
178+
(let ((fails (failure-data source)))
179+
(when fails
180+
(format t "FAILURES:~%")
181+
(dolist (fail fails)
182+
(cond ((broken-by fail)
183+
(let ((responsible-system (broken-by fail)))
184+
(format t "System ~A broken by ~A~%"
185+
(system-name fail)
186+
(system-name responsible-system))))
187+
(t
188+
(format t "~S:~%~A"
189+
(system-name fail)
190+
(failure-snippet fail))))))))
174191

175192
(defun source-pathname (project-name)
176193
(let ((directory `(:relative "quicklisp-controller"

0 commit comments

Comments
 (0)