diff options
| author | Clément Pit--Claudel | 2016-12-05 00:52:14 -0500 |
|---|---|---|
| committer | Clément Pit--Claudel | 2016-12-12 17:41:27 -0500 |
| commit | 27cada035a79b633e856a437dd0e037acc1d61c6 (patch) | |
| tree | b02c80fe4e7b4ce9fe54912118e4fa5e723723c5 /src | |
| parent | a41ded87b318ce3cbeb0ba3624bcb83ae3b8a437 (diff) | |
| download | emacs-27cada035a79b633e856a437dd0e037acc1d61c6.tar.gz emacs-27cada035a79b633e856a437dd0e037acc1d61c6.zip | |
Move backtrace to ELisp using a new mapbacktrace primitive
* src/eval.c (get_backtrace_starting_at, backtrace_frame_apply)
(Fmapbacktrace, Fbacktrace_frame_internal): New functions.
(get_backtrace_frame, Fbacktrace_debug): Use `get_backtrace_starting_at'.
* lisp/subr.el (backtrace--print-frame): New function.
(backtrace): Reimplement using `backtrace--print-frame' and `mapbacktrace'.
(backtrace-frame): Reimplement using `backtrace-frame--internal'.
* lisp/emacs-lisp/debug.el (debugger-setup-buffer): Pass a base to
`mapbacktrace' instead of searching for "(debug" in the output of
`backtrace'.
* test/lisp/subr-tests.el (subr-test-backtrace-simple-tests)
(subr-test-backtrace-integration-test): New tests.
* doc/lispref/debugging.texi (Internals of Debugger): Document
`mapbacktrace' and missing argument BASE of `backtrace-frame'.
Diffstat (limited to 'src')
| -rw-r--r-- | src/eval.c | 157 |
1 files changed, 69 insertions, 88 deletions
diff --git a/src/eval.c b/src/eval.c index f1e0ae7d586..7852ef700ba 100644 --- a/src/eval.c +++ b/src/eval.c | |||
| @@ -3541,87 +3541,29 @@ context where binding is lexical by default. */) | |||
| 3541 | } | 3541 | } |
| 3542 | 3542 | ||
| 3543 | 3543 | ||
| 3544 | DEFUN ("backtrace-debug", Fbacktrace_debug, Sbacktrace_debug, 2, 2, 0, | 3544 | static union specbinding * |
| 3545 | doc: /* Set the debug-on-exit flag of eval frame LEVEL levels down to FLAG. | 3545 | get_backtrace_starting_at (Lisp_Object base) |
| 3546 | The debugger is entered when that frame exits, if the flag is non-nil. */) | ||
| 3547 | (Lisp_Object level, Lisp_Object flag) | ||
| 3548 | { | ||
| 3549 | union specbinding *pdl = backtrace_top (); | ||
| 3550 | register EMACS_INT i; | ||
| 3551 | |||
| 3552 | CHECK_NUMBER (level); | ||
| 3553 | |||
| 3554 | for (i = 0; backtrace_p (pdl) && i < XINT (level); i++) | ||
| 3555 | pdl = backtrace_next (pdl); | ||
| 3556 | |||
| 3557 | if (backtrace_p (pdl)) | ||
| 3558 | set_backtrace_debug_on_exit (pdl, !NILP (flag)); | ||
| 3559 | |||
| 3560 | return flag; | ||
| 3561 | } | ||
| 3562 | |||
| 3563 | DEFUN ("backtrace", Fbacktrace, Sbacktrace, 0, 0, "", | ||
| 3564 | doc: /* Print a trace of Lisp function calls currently active. | ||
| 3565 | Output stream used is value of `standard-output'. */) | ||
| 3566 | (void) | ||
| 3567 | { | 3546 | { |
| 3568 | union specbinding *pdl = backtrace_top (); | 3547 | union specbinding *pdl = backtrace_top (); |
| 3569 | Lisp_Object tem; | ||
| 3570 | Lisp_Object old_print_level = Vprint_level; | ||
| 3571 | 3548 | ||
| 3572 | if (NILP (Vprint_level)) | 3549 | if (!NILP (base)) |
| 3573 | XSETFASTINT (Vprint_level, 8); | 3550 | { /* Skip up to `base'. */ |
| 3574 | 3551 | base = Findirect_function (base, Qt); | |
| 3575 | while (backtrace_p (pdl)) | 3552 | while (backtrace_p (pdl) |
| 3576 | { | 3553 | && !EQ (base, Findirect_function (backtrace_function (pdl), Qt))) |
| 3577 | write_string (backtrace_debug_on_exit (pdl) ? "* " : " "); | 3554 | pdl = backtrace_next (pdl); |
| 3578 | if (backtrace_nargs (pdl) == UNEVALLED) | ||
| 3579 | { | ||
| 3580 | Fprin1 (Fcons (backtrace_function (pdl), *backtrace_args (pdl)), | ||
| 3581 | Qnil); | ||
| 3582 | write_string ("\n"); | ||
| 3583 | } | ||
| 3584 | else | ||
| 3585 | { | ||
| 3586 | tem = backtrace_function (pdl); | ||
| 3587 | if (debugger_stack_frame_as_list) | ||
| 3588 | write_string ("("); | ||
| 3589 | Fprin1 (tem, Qnil); /* This can QUIT. */ | ||
| 3590 | if (!debugger_stack_frame_as_list) | ||
| 3591 | write_string ("("); | ||
| 3592 | { | ||
| 3593 | ptrdiff_t i; | ||
| 3594 | for (i = 0; i < backtrace_nargs (pdl); i++) | ||
| 3595 | { | ||
| 3596 | if (i || debugger_stack_frame_as_list) | ||
| 3597 | write_string(" "); | ||
| 3598 | Fprin1 (backtrace_args (pdl)[i], Qnil); | ||
| 3599 | } | ||
| 3600 | } | ||
| 3601 | write_string (")\n"); | ||
| 3602 | } | ||
| 3603 | pdl = backtrace_next (pdl); | ||
| 3604 | } | 3555 | } |
| 3605 | 3556 | ||
| 3606 | Vprint_level = old_print_level; | 3557 | return pdl; |
| 3607 | return Qnil; | ||
| 3608 | } | 3558 | } |
| 3609 | 3559 | ||
| 3610 | static union specbinding * | 3560 | static union specbinding * |
| 3611 | get_backtrace_frame (Lisp_Object nframes, Lisp_Object base) | 3561 | get_backtrace_frame (Lisp_Object nframes, Lisp_Object base) |
| 3612 | { | 3562 | { |
| 3613 | union specbinding *pdl = backtrace_top (); | ||
| 3614 | register EMACS_INT i; | 3563 | register EMACS_INT i; |
| 3615 | 3564 | ||
| 3616 | CHECK_NATNUM (nframes); | 3565 | CHECK_NATNUM (nframes); |
| 3617 | 3566 | union specbinding *pdl = get_backtrace_starting_at (base); | |
| 3618 | if (!NILP (base)) | ||
| 3619 | { /* Skip up to `base'. */ | ||
| 3620 | base = Findirect_function (base, Qt); | ||
| 3621 | while (backtrace_p (pdl) | ||
| 3622 | && !EQ (base, Findirect_function (backtrace_function (pdl), Qt))) | ||
| 3623 | pdl = backtrace_next (pdl); | ||
| 3624 | } | ||
| 3625 | 3567 | ||
| 3626 | /* Find the frame requested. */ | 3568 | /* Find the frame requested. */ |
| 3627 | for (i = XFASTINT (nframes); i > 0 && backtrace_p (pdl); i--) | 3569 | for (i = XFASTINT (nframes); i > 0 && backtrace_p (pdl); i--) |
| @@ -3630,33 +3572,71 @@ get_backtrace_frame (Lisp_Object nframes, Lisp_Object base) | |||
| 3630 | return pdl; | 3572 | return pdl; |
| 3631 | } | 3573 | } |
| 3632 | 3574 | ||
| 3633 | DEFUN ("backtrace-frame", Fbacktrace_frame, Sbacktrace_frame, 1, 2, NULL, | 3575 | static Lisp_Object |
| 3634 | doc: /* Return the function and arguments NFRAMES up from current execution point. | 3576 | backtrace_frame_apply (Lisp_Object function, union specbinding *pdl) |
| 3635 | If that frame has not evaluated the arguments yet (or is a special form), | ||
| 3636 | the value is (nil FUNCTION ARG-FORMS...). | ||
| 3637 | If that frame has evaluated its arguments and called its function already, | ||
| 3638 | the value is (t FUNCTION ARG-VALUES...). | ||
| 3639 | A &rest arg is represented as the tail of the list ARG-VALUES. | ||
| 3640 | FUNCTION is whatever was supplied as car of evaluated list, | ||
| 3641 | or a lambda expression for macro calls. | ||
| 3642 | If NFRAMES is more than the number of frames, the value is nil. | ||
| 3643 | If BASE is non-nil, it should be a function and NFRAMES counts from its | ||
| 3644 | nearest activation frame. */) | ||
| 3645 | (Lisp_Object nframes, Lisp_Object base) | ||
| 3646 | { | 3577 | { |
| 3647 | union specbinding *pdl = get_backtrace_frame (nframes, base); | ||
| 3648 | |||
| 3649 | if (!backtrace_p (pdl)) | 3578 | if (!backtrace_p (pdl)) |
| 3650 | return Qnil; | 3579 | return Qnil; |
| 3580 | |||
| 3581 | Lisp_Object flags = Qnil; | ||
| 3582 | if (backtrace_debug_on_exit (pdl)) | ||
| 3583 | flags = Fcons (QCdebug_on_exit, Fcons (Qt, Qnil)); | ||
| 3584 | |||
| 3651 | if (backtrace_nargs (pdl) == UNEVALLED) | 3585 | if (backtrace_nargs (pdl) == UNEVALLED) |
| 3652 | return Fcons (Qnil, | 3586 | return call4 (function, Qnil, backtrace_function (pdl), *backtrace_args (pdl), flags); |
| 3653 | Fcons (backtrace_function (pdl), *backtrace_args (pdl))); | ||
| 3654 | else | 3587 | else |
| 3655 | { | 3588 | { |
| 3656 | Lisp_Object tem = Flist (backtrace_nargs (pdl), backtrace_args (pdl)); | 3589 | Lisp_Object tem = Flist (backtrace_nargs (pdl), backtrace_args (pdl)); |
| 3590 | return call4 (function, Qt, backtrace_function (pdl), tem, flags); | ||
| 3591 | } | ||
| 3592 | } | ||
| 3657 | 3593 | ||
| 3658 | return Fcons (Qt, Fcons (backtrace_function (pdl), tem)); | 3594 | DEFUN ("backtrace-debug", Fbacktrace_debug, Sbacktrace_debug, 2, 2, 0, |
| 3595 | doc: /* Set the debug-on-exit flag of eval frame LEVEL levels down to FLAG. | ||
| 3596 | The debugger is entered when that frame exits, if the flag is non-nil. */) | ||
| 3597 | (Lisp_Object level, Lisp_Object flag) | ||
| 3598 | { | ||
| 3599 | CHECK_NUMBER (level); | ||
| 3600 | union specbinding *pdl = get_backtrace_frame(level, Qnil); | ||
| 3601 | |||
| 3602 | if (backtrace_p (pdl)) | ||
| 3603 | set_backtrace_debug_on_exit (pdl, !NILP (flag)); | ||
| 3604 | |||
| 3605 | return flag; | ||
| 3606 | } | ||
| 3607 | |||
| 3608 | DEFUN ("mapbacktrace", Fmapbacktrace, Smapbacktrace, 1, 2, 0, | ||
| 3609 | doc: /* Call FUNCTION for each frame in backtrace. | ||
| 3610 | If BASE is non-nil, it should be a function and iteration will start | ||
| 3611 | from its nearest activation frame. | ||
| 3612 | FUNCTION is called with 4 arguments: EVALD, FUNC, ARGS, and FLAGS. If | ||
| 3613 | a frame has not evaluated its arguments yet or is a special form, | ||
| 3614 | EVALD is nil and ARGS is a list of forms. If a frame has evaluated | ||
| 3615 | its arguments and called its function already, EVALD is t and ARGS is | ||
| 3616 | a list of values. | ||
| 3617 | FLAGS is a plist of properties of the current frame: currently, the | ||
| 3618 | only supported property is :debug-on-exit. `mapbacktrace' always | ||
| 3619 | returns nil. */) | ||
| 3620 | (Lisp_Object function, Lisp_Object base) | ||
| 3621 | { | ||
| 3622 | union specbinding *pdl = get_backtrace_starting_at (base); | ||
| 3623 | |||
| 3624 | while (backtrace_p (pdl)) | ||
| 3625 | { | ||
| 3626 | backtrace_frame_apply (function, pdl); | ||
| 3627 | pdl = backtrace_next (pdl); | ||
| 3659 | } | 3628 | } |
| 3629 | |||
| 3630 | return Qnil; | ||
| 3631 | } | ||
| 3632 | |||
| 3633 | DEFUN ("backtrace-frame--internal", Fbacktrace_frame_internal, | ||
| 3634 | Sbacktrace_frame_internal, 3, 3, NULL, | ||
| 3635 | doc: /* Call FUNCTION on stack frame NFRAMES away from BASE. | ||
| 3636 | Return the result of FUNCTION, or nil if no matching frame could be found. */) | ||
| 3637 | (Lisp_Object function, Lisp_Object nframes, Lisp_Object base) | ||
| 3638 | { | ||
| 3639 | return backtrace_frame_apply (function, get_backtrace_frame (nframes, base)); | ||
| 3660 | } | 3640 | } |
| 3661 | 3641 | ||
| 3662 | /* For backtrace-eval, we want to temporarily unwind the last few elements of | 3642 | /* For backtrace-eval, we want to temporarily unwind the last few elements of |
| @@ -4114,8 +4094,9 @@ alist of active lexical bindings. */); | |||
| 4114 | defsubr (&Srun_hook_wrapped); | 4094 | defsubr (&Srun_hook_wrapped); |
| 4115 | defsubr (&Sfetch_bytecode); | 4095 | defsubr (&Sfetch_bytecode); |
| 4116 | defsubr (&Sbacktrace_debug); | 4096 | defsubr (&Sbacktrace_debug); |
| 4117 | defsubr (&Sbacktrace); | 4097 | DEFSYM (QCdebug_on_exit, ":debug-on-exit"); |
| 4118 | defsubr (&Sbacktrace_frame); | 4098 | defsubr (&Smapbacktrace); |
| 4099 | defsubr (&Sbacktrace_frame_internal); | ||
| 4119 | defsubr (&Sbacktrace_eval); | 4100 | defsubr (&Sbacktrace_eval); |
| 4120 | defsubr (&Sbacktrace__locals); | 4101 | defsubr (&Sbacktrace__locals); |
| 4121 | defsubr (&Sspecial_variable_p); | 4102 | defsubr (&Sspecial_variable_p); |