| ;; 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)) |