blob: 9db9bcdbf2c33674acb21f682ed64f26facbc376 [file] [log] [blame]
;; Scheme side of the gdb module.
;;
;; Copyright (C) 2014-2021 Free Software Foundation, Inc.
;;
;; This file is part of GDB.
;;
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;; This file is included by (gdb).
;; The original i/o ports. In case the user wants them back.
(define %orig-input-port #f)
(define %orig-output-port #f)
(define %orig-error-port #f)
;; Keys for GDB-generated exceptions.
;; gdb:with-stack is handled separately.
(define %exception-keys '(gdb:error
gdb:invalid-object-error
gdb:memory-error
gdb:pp-type-error
gdb:user-error))
;; Printer for gdb exceptions, used when Scheme tries to print them directly.
(define (%exception-printer port key args default-printer)
(apply (case-lambda
((subr msg args . rest)
(if subr
(format port "In procedure ~a: " subr))
(apply format port msg (or args '())))
(_ (default-printer)))
args))
;; Print the message part of a gdb:with-stack exception.
;; The arg list is the way it is because it's passed to set-exception-printer!.
;; We don't print a backtrace here because Guile will have already printed a
;; backtrace.
(define (%with-stack-exception-printer port key args default-printer)
(let ((real-key (car args))
(real-args (cddr args)))
(%exception-printer port real-key real-args default-printer)))
;; Copy of Guile's print-exception that tweaks the output for our purposes.
;; TODO: It's not clear the tweaking is still necessary.
(define (%print-exception-message-worker port key args)
(define (default-printer)
(format port "Throw to key `~a' with args `~s'." key args))
(format port "ERROR: ")
;; Pass #t for tag to catch all errors.
(catch #t
(lambda ()
(%exception-printer port key args default-printer))
(lambda (k . args)
(format port "Error while printing gdb exception: ~a ~s."
k args)))
(newline port)
(force-output port))
;; Called from the C code to print an exception.
;; Guile prints them a little differently than we want.
;; See boot-9.scm:print-exception.
(define (%print-exception-message port frame key args)
(cond ((memq key %exception-keys)
(%print-exception-message-worker port key args))
(else
(print-exception port frame key args)))
*unspecified*)
;; Called from the C code to print an exception according to the setting
;; of "guile print-stack".
;;
;; If PORT is #f, use the standard error port.
;; If STACK is #f, never print the stack, regardless of whether printing it
;; is enabled. If STACK is #t, then print it if it is contained in ARGS
;; (i.e., KEY is gdb:with-stack). Otherwise STACK is the result of calling
;; scm_make_stack (which will be ignored in favor of the stack in ARGS if
;; KEY is gdb:with-stack).
;; KEY, ARGS are the standard arguments to scm_throw, et.al.
(define (%print-exception-with-stack port stack key args)
(let ((style (%exception-print-style)))
(if (not (eq? style 'none))
(let ((error-port (current-error-port))
(frame #f))
(if (not port)
(set! port error-port))
(if (eq? port error-port)
(begin
(force-output (current-output-port))
;; In case the current output port is not gdb's output port.
(force-output (output-port))))
;; If the exception is gdb:with-stack, unwrap it to get the stack and
;; underlying exception. If the caller happens to pass in a stack,
;; we ignore it and use the one in ARGS instead.
(if (eq? key 'gdb:with-stack)
(begin
(set! key (car args))
(if stack
(set! stack (cadr args)))
(set! args (cddr args))))
;; If caller wanted a stack and there isn't one, disable backtracing.
(if (eq? stack #t)
(set! stack #f))
;; At this point if stack is true, then it is assumed to be a stack.
(if stack
(set! frame (stack-ref stack 0)))
(if (and (eq? style 'full) stack)
(begin
;; This is derived from libguile/throw.c:handler_message.
;; We include "Guile" in "Guile Backtrace" whereas the Guile
;; version does not so that tests can know it's us printing
;; the backtrace. Plus it could help beginners.
(display "Guile Backtrace:\n" port)
(display-backtrace stack port #f #f '())
(newline port)))
(%print-exception-message port frame key args)))))
;; Internal utility called during startup to initialize the Scheme side of
;; GDB+Guile.
(define (%initialize!)
(for-each (lambda (key)
(set-exception-printer! key %exception-printer))
%exception-keys)
(set-exception-printer! 'gdb:with-stack %with-stack-exception-printer)
(set! %orig-input-port (set-current-input-port (input-port)))
(set! %orig-output-port (set-current-output-port (output-port)))
(set! %orig-error-port (set-current-error-port (error-port))))
;; Dummy routine to silence "possibly unused local top-level variable"
;; warnings from the compiler.
(define-public (%silence-compiler-warnings%)
(list %print-exception-with-stack %initialize!))
;; Public routines.
(define-public (orig-input-port) %orig-input-port)
(define-public (orig-output-port) %orig-output-port)
(define-public (orig-error-port) %orig-error-port)
;; Utility to throw gdb:user-error for use in writing gdb commands.
;; The requirements for the arguments to "throw" are a bit obscure,
;; so give the user something simpler.
(define-public (throw-user-error message . args)
(throw 'gdb:user-error #f message args))