1/* Part of SWI-Prolog 2 3 Author: Jan Wielemaker 4 E-mail: jan@swi-prolog.org 5 WWW: http://www.swi-prolog.org 6 Copyright (c) 2021-2022, SWI-Prolog Solutions b.v. 7 All rights reserved. 8 9 Redistribution and use in source and binary forms, with or without 10 modification, are permitted provided that the following conditions 11 are met: 12 13 1. Redistributions of source code must retain the above copyright 14 notice, this list of conditions and the following disclaimer. 15 16 2. Redistributions in binary form must reproduce the above copyright 17 notice, this list of conditions and the following disclaimer in 18 the documentation and/or other materials provided with the 19 distribution. 20 21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 POSSIBILITY OF SUCH DAMAGE. 33*/ 34 35:- module(prolog_debug_tools, 36 [ (spy)/1, % :Spec (some users tend to define these as 37 (nospy)/1, % :Spec an operator) 38 nospyall/0, 39 debugging/0, 40 trap/1, % +Exception 41 notrap/1 % +Exception 42 ]). 43:- use_module(library(broadcast), [broadcast/1]). 44:- autoload(library(edinburgh), [debug/0]). 45:- autoload(library(gensym), [gensym/2]). 46 47:- set_prolog_flag(generate_debug_info, false).
63:- multifile 64 prolog:debug_control_hook/1. % +Action 65 66:- meta_predicate 67 spy( ), 68 nospy( ).
informational
, with one of
the following terms, where Spec is of the form M:Head.
spy(Spec)
nospy(Spec)
85spy(Spec) :- 86 '$notrace'(spy_(Spec)). 87 88spy_(_:X) :- 89 var(X), 90 throw(error(instantiation_error, _)). 91spy_(_:[]) :- !. 92spy_(M:[H|T]) :- 93 !, 94 spy(M:H), 95 spy(M:T). 96spy_(Spec) :- 97 prolog:debug_control_hook(spy(Spec)), 98 !. 99spy_(Spec) :- 100 '$find_predicate'(Spec, Preds), 101 '$member'(PI, Preds), 102 pi_to_head(PI, Head), 103 '$define_predicate'(Head), 104 '$spy'(Head), 105 fail. 106spy_(_). 107 108nospy(Spec) :- 109 '$notrace'(nospy_(Spec)). 110 111nospy_(_:X) :- 112 var(X), 113 throw(error(instantiation_error, _)). 114nospy_(_:[]) :- !. 115nospy_(M:[H|T]) :- 116 !, 117 nospy(M:H), 118 nospy(M:T). 119nospy_(Spec) :- 120 prolog:debug_control_hook(nospy(Spec)), 121 !. 122nospy_(Spec) :- 123 '$find_predicate'(Spec, Preds), 124 '$member'(PI, Preds), 125 pi_to_head(PI, Head), 126 '$nospy'(Head), 127 fail. 128nospy_(_). 129 130nospyall :- 131 '$notrace'(nospyall_). 132 133nospyall_ :- 134 prolog:debug_control_hook(nospyall), 135 fail. 136nospyall_ :- 137 spy_point(Head), 138 '$nospy'(Head), 139 fail. 140nospyall_. 141 142pi_to_head(M:PI, M:Head) :- 143 !, 144 pi_to_head(PI, Head). 145pi_to_head(Name/Arity, Head) :- 146 functor(Head, Name, Arity).
152debugging :- 153 '$notrace'(debugging_). 154 155debugging_ :- 156 prolog:debug_control_hook(debugging), 157 !. 158debugging_ :- 159 ( current_prolog_flag(debug, true) 160 -> print_message(informational, debugging(on)), 161 findall(H, spy_point(H), SpyPoints), 162 print_message(informational, spying(SpyPoints)) 163 ; print_message(informational, debugging(off)) 164 ), 165 trapping. 166 167spy_point(Module:Head) :- 168 current_predicate(_, Module:Head), 169 '$get_predicate_attribute'(Module:Head, spy, 1), 170 \+ predicate_property(Module:Head, imported_from(_)). 171 172 173 /******************************* 174 * EXCEPTIONS * 175 *******************************/
error(Formal, Context)
exceptions that unify. The
tracer is started when a matching exception is raised. This
predicate enables debug mode using debug/0 to get more context
about the exception. Even with debug mode disabled exceptions are
still trapped and thus one may call nodebug/0 to run in normal mode
after installing a trap. Exceptions are trapped in any thread. Debug
mode is only enabled in the calling thread. To enable debug mode in
all threads use tdebug/0.
Calling debugging/0 lists the enabled traps. The predicate notrap/1 removes matching (unifying) traps.
In many cases debugging an exception that is caught is as simple as below (assuming run/0 starts your program).
?- gtrap(_). ?- run.
204:- dynamic 205 exception/4, % Name, Term, NotCaught, Caught 206 installed/1. % ClauseRef 207 208trap(Error) :- 209 '$notrace'(trap_(Error)). 210 211trap_(Error) :- 212 gensym(ex, Rule), 213 asserta(exception(Rule, error(Error, _), true, true)), 214 print_message(informational, trap(Rule, error(Error, _), true, true)), 215 install_exception_hook, 216 debug. 217 218notrap(Error) :- 219 '$notrace'(notrap_(Error)). 220 221notrap_(Error) :- 222 Exception = error(Error, _), 223 findall(exception(Name, Exception, NotCaught, Caught), 224 retract(exception(Name, error(Error, _), Caught, NotCaught)), 225 Trapping), 226 print_message(informational, notrap(Trapping)). 227 228 229trapping :- 230 findall(exception(Name, Term, NotCaught, Caught), 231 exception(Name, Term, NotCaught, Caught), 232 Trapping), 233 print_message(information, trapping(Trapping)). 234 235:- dynamic 236 user:prolog_exception_hook/4.
242:- public exception_hook/4. 243 244exception_hook(Ex, Ex, _Frame, Catcher) :- 245 thread_self(Me), 246 thread_property(Me, debug(true)), 247 broadcast(debug(exception(Ex))), 248 exception(_, Ex, NotCaught, Caught), 249 !, 250 ( Caught == true 251 -> true 252 ; Catcher == none, 253 NotCaught == true 254 ), 255 trace, fail.
262install_exception_hook :- 263 installed(Ref), 264 ( nth_clause(_, I, Ref) 265 -> I == 1, ! % Ok, we are the first 266 ; retractall(installed(Ref)), 267 erase(Ref), % Someone before us! 268 fail 269 ). 270install_exception_hook :- 271 asserta((user:prolog_exception_hook(Ex, Out, Frame, Catcher) :- 272 exception_hook(Ex, Out, Frame, Catcher)), Ref), 273 assert(installed(Ref)). 274 275 276 /******************************* 277 * MESSAGES * 278 *******************************/ 279 280:- multifile 281 prolog:message//1. 282 283prologmessage(trapping([])) --> 284 [ 'No exception traps'-[] ]. 285prologmessage(trapping(Trapping)) --> 286 [ 'Exception traps on'-[], nl ], 287 trapping(Trapping). 288prologmessage(trap(_Rule, Error, _Caught, _NotCaught)) --> 289 [ 'Installed trap for exception '-[] ], 290 exception(Error), 291 [ nl ]. 292prologmessage(notrap([])) --> 293 [ 'No matching traps'-[] ]. 294prologmessage(notrap(Trapping)) --> 295 [ 'Removed traps from exceptions'-[], nl ], 296 trapping(Trapping). 297 298trapping([]) --> []. 299trapping([exception(_Rule, Error, _Caught, _NotCaught)|T]) --> 300 [ ' '-[] ], 301 exception(Error), 302 [ nl ], 303 trapping(T). 304 305exception(Term) --> 306 { copy_term(Term, T2), 307 numbervars(T2, 0, _, [singletons(true)]) 308 }, 309 [ '~p'-[T2] ]
User level debugging tools
This library provides tools to control the Prolog debuggers. Traditionally this code was built-in. Because these tools are only required in (interactive) debugging sessions they have been moved into the library. */