Skip to content

Commit 24fb445

Browse files
committed
ADDED: Support for debug/1 and debug/3 inside pengines.
1 parent 7e70b96 commit 24fb445

File tree

3 files changed

+352
-3
lines changed

3 files changed

+352
-3
lines changed

lib/swish_debug.pl

+326
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
/* Part of SWI-Prolog
2+
3+
Author: Jan Wielemaker
4+
5+
WWW: http://www.swi-prolog.org
6+
Copyright (c) 2002-2023, University of Amsterdam
7+
VU University Amsterdam
8+
CWI, Amsterdam
9+
SWI-Prolog Solutions b.v.
10+
All rights reserved.
11+
12+
Redistribution and use in source and binary forms, with or without
13+
modification, are permitted provided that the following conditions
14+
are met:
15+
16+
1. Redistributions of source code must retain the above copyright
17+
notice, this list of conditions and the following disclaimer.
18+
19+
2. Redistributions in binary form must reproduce the above copyright
20+
notice, this list of conditions and the following disclaimer in
21+
the documentation and/or other materials provided with the
22+
distribution.
23+
24+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27+
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28+
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29+
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30+
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34+
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35+
POSSIBILITY OF SUCH DAMAGE.
36+
*/
37+
38+
:- module(swish_debug,
39+
[ swish_debug/3, % +Topic, +Format, :Args
40+
debug/1, % +Topic
41+
nodebug/1, % +Topic
42+
swish_debugging/1, % ?Topic
43+
list_debug_topics/0,
44+
list_debug_topics/1, % +Options
45+
46+
swish_debug_sentinel/0
47+
]).
48+
:- autoload(library(option), [option/3, option/2]).
49+
:- autoload(library(pengines), [pengine_self/1, pengine_output/1]).
50+
:- autoload(library(dcg/high_order), [sequence/5]).
51+
:- autoload(library(pengines_io), [pengine_format/2]).
52+
:- autoload(library(http/html_write), [html/3, print_html/1]).
53+
54+
55+
:- meta_predicate
56+
debug(+,+,:).
57+
58+
:- thread_local
59+
debugging/2. % Topic, Enabled
60+
:- dynamic
61+
shared_debug_topic/1.
62+
63+
/** <module> Print debug messages and test assertions
64+
65+
This library is a SWISH specific replacement for library(debug) that
66+
allows for debug/3 messages in SWISH code as well as libraries loaded
67+
into SWISH. Unlike library(debug), this library maintains the debug
68+
state per thread (Pengine).
69+
70+
This library allows for debug/3 calls inside user programs stored in
71+
SWISH as well as libraries preloaded into SWISH. Libraries that wish to
72+
enable debugging by SWISH users must include this file rather than
73+
library(debug). This may be done dynamically using the following
74+
snippet.
75+
76+
```
77+
:- if(exists_source(swish(lib/swish_debug))).
78+
:- use_module(swish(lib/swish_debug)).
79+
:- else.
80+
:- use_module(library(debug)).
81+
:- endif.
82+
```
83+
*/
84+
85+
%! swish_debugging(+Topic) is semidet.
86+
%! swish_debugging(-Topic) is nondet.
87+
%
88+
% Examine debug topics. The form debugging(+Topic) may be used to
89+
% perform more complex debugging tasks. A typical usage skeleton
90+
% is:
91+
%
92+
% ```
93+
% ( debugging(mytopic)
94+
% -> <perform debugging actions>
95+
% ; true
96+
% ),
97+
% ...
98+
% ```
99+
%
100+
% The other two calls are intended to examine existing and enabled
101+
% debugging tokens and are typically not used in user programs.
102+
103+
swish_debugging(Topic) :-
104+
debugging(Topic, true).
105+
106+
%! debug(+Topic) is det.
107+
%! nodebug(+Topic) is det.
108+
%
109+
% Add/remove a topic from being printed. nodebug(_) removes all
110+
% topics. Gives a warning if the topic is not defined unless it is
111+
% used from a directive. The latter allows placing debug topics at the
112+
% start of a (load-)file without warnings.
113+
%
114+
% For debug/1, Topic can be a term `Topic > Out`, where `Out` is
115+
% either a stream or stream-alias or a filename (an atom). This
116+
% redirects debug information on this topic to the given output. On
117+
% Linux systems redirection can be used to make the message appear,
118+
% even if the `user_error` stream is redefined using
119+
%
120+
% ?- debug(Topic > '/proc/self/fd/2').
121+
%
122+
% A platform independent way to get debug messages in the current
123+
% console (for example, a `swipl-win` window, or login using `ssh` to
124+
% Prolog running an SSH server from the `libssh` pack) is to use:
125+
%
126+
% ?- stream_property(S, alias(user_error)),
127+
% debug(Topic > S).
128+
%
129+
% Do not forget to disable the debugging using nodebug/1 before
130+
% quitting the console if Prolog must remain running.
131+
132+
debug(Topic) :-
133+
debug(Topic, true).
134+
nodebug(Topic) :-
135+
debug(Topic, false).
136+
137+
debug(Topic, Enabled) :-
138+
( retract(debugging(Topic, _))
139+
-> true
140+
; shared_debug_topic(Topic)
141+
-> true
142+
; print_message(warning, debug_no_topic(Topic))
143+
),
144+
assert(debugging(Topic, Enabled)).
145+
146+
%! debug_topic(+Topic) is det.
147+
%
148+
% Declare a topic for debugging. This can be used to find all
149+
% topics available for debugging.
150+
151+
debug_topic(Topic) :-
152+
pengine_self(_),
153+
!,
154+
( debugging(Registered, _),
155+
Registered =@= Topic
156+
-> true
157+
; assertz(debugging(Topic, false))
158+
).
159+
debug_topic(Topic) :-
160+
( shared_debug_topic(Registered),
161+
Registered =@= Topic
162+
-> true
163+
; assertz(shared_debug_topic(Topic))
164+
).
165+
166+
%! list_debug_topics is det.
167+
%! list_debug_topics(+Options) is det.
168+
%
169+
% List currently known topics for debug/3 and their setting. Options
170+
% is either an atom or string, which is a shorthand for
171+
% `[search(String)]` or a normal option list. Defined options are:
172+
%
173+
% - search(String)
174+
% Only show topics that match String. Match is case insensitive
175+
% on the printed representation of the term.
176+
% - active(+Boolean)
177+
% Only print topics that are active (`true`) or inactive
178+
% (`false`).
179+
% - output(+To)
180+
% Only print topics whose target location matches To. This option
181+
% implicitly restricts the output to active topics.
182+
183+
list_debug_topics :-
184+
list_debug_topics([]).
185+
186+
list_debug_topics(Options) :-
187+
( atom(Options)
188+
; string(Options)
189+
),
190+
!,
191+
list_debug_topics([search(Options)]).
192+
list_debug_topics(Options) :-
193+
option(active(Activated), Options, _),
194+
findall(debug_topic(Topic, String, Activated),
195+
matching_topic(Topic, String, Activated, Options),
196+
Triples),
197+
print_message(information, debug_topics(Triples)).
198+
199+
matching_topic(Topic, String, Activated, Options) :-
200+
known_debug_topic(Topic, Activated),
201+
topic_to_string(Topic, String),
202+
( option(search(Search), Options)
203+
-> sub_atom_icasechk(String, _, Search)
204+
; true
205+
).
206+
207+
topic_to_string(Topic, String) :-
208+
numbervars(Topic, 0, _, [singletons(true)]),
209+
term_string(Topic, String, [quoted(true), numbervars(true)]).
210+
211+
known_debug_topic(Topic, Value) :-
212+
debugging(Topic, Value).
213+
known_debug_topic(Topic, false) :-
214+
shared_debug_topic(Topic),
215+
\+ debugging(Topic, _).
216+
217+
218+
:- multifile
219+
prolog_debug_tools:debugging_hook/0.
220+
221+
prolog_debug_tools:debugging_hook :-
222+
( debugging(_, true)
223+
-> list_debug_topics([active(true)])
224+
).
225+
226+
%! swish_debug(+Topic, +Format, :Args) is det.
227+
%
228+
% Format a message if debug topic is enabled. Similar to format/3
229+
% to =user_error=, but only prints if Topic is activated through
230+
% debug/1. Args is a meta-argument to deal with goal for the
231+
% @-command. Output is first handed to the hook
232+
% prolog:debug_print_hook/3. If this fails, Format+Args is
233+
% translated to text using the message-translation (see
234+
% print_message/2) for the term debug(Format, Args) and then
235+
% printed to every matching destination (controlled by debug/1)
236+
% using print_message_lines/3.
237+
%
238+
% The message is preceded by '% ' and terminated with a newline.
239+
%
240+
% @see format/3.
241+
242+
swish_debug(Topic, Format, Args) :-
243+
debugging(Topic, true),
244+
!,
245+
print_debug(Topic, Format, Args).
246+
swish_debug(_, _, _).
247+
248+
249+
print_debug(_Topic, _Format, _Args) :-
250+
nb_current(swish_debug_printing, true),
251+
!.
252+
print_debug(Topic, Format, Args) :-
253+
setup_call_cleanup(
254+
nb_setval(swish_debug_printing, true),
255+
print_debug_guarded(Topic, Format, Args),
256+
nb_delete(swish_debug_printing)).
257+
258+
:- html_meta(send_html(html)).
259+
260+
print_debug_guarded(Topic, Format, Args) :-
261+
topic_to_string(Topic, String),
262+
format(string(Msg0), Format, Args),
263+
split_string(Msg0, "", "\n", [Msg]),
264+
send_html(\debug_msg(Msg, String)).
265+
266+
send_html(HTML) :-
267+
phrase(html(HTML), Tokens),
268+
with_output_to(string(HTMlString), print_html(Tokens)),
269+
pengine_output(HTMlString).
270+
271+
debug_msg(Msg, Topic) -->
272+
html(div(class('debug-msg'),
273+
[ span([class('topic'), title('Debug topic')], Topic)
274+
| pre([class('msg'), title('Debug message')], Msg)
275+
])).
276+
277+
278+
/*******************************
279+
* EXPANSION *
280+
*******************************/
281+
282+
swish_debug_sentinel.
283+
284+
imports_this_module :-
285+
prolog_load_context(module, M),
286+
predicate_property(M:swish_debug_sentinel, imported_from(swish_debug)),
287+
!.
288+
289+
:- multifile
290+
user:goal_expansion/2.
291+
292+
user:goal_expansion(debug(Topic, Format, Args),
293+
swish_debug(Topic, Format, Args)) :-
294+
imports_this_module,
295+
debug_topic(Topic).
296+
user:goal_expansion(debugging(Topic),
297+
swish_debugging(Topic)) :-
298+
imports_this_module,
299+
debug_topic(Topic).
300+
301+
302+
/*******************************
303+
* MESSAGES *
304+
*******************************/
305+
306+
:- multifile
307+
prolog:message/3.
308+
309+
prolog:message(debug_topics(List)) -->
310+
[ ansi(bold, '~w~t ~w~35|~n', ['Debug Topic', 'Activated']),
311+
'~`\u2015t~35|'-[], nl
312+
],
313+
sequence(debug_topic, [nl], List).
314+
315+
debug_topic(debug_topic(_Topic, TopicString, true)) -->
316+
[ ansi(bold, '~s~t \u2714~35|', [TopicString]) ].
317+
debug_topic(debug_topic(_Topic, TopicString, false)) -->
318+
[ '~s~t -~35|'-[TopicString] ].
319+
320+
:- multifile
321+
sandbox:safe_global_variable/1,
322+
sandbox:safe_meta/2.
323+
324+
sandbox:safe_global_variable(swish_debug_printing).
325+
sandbox:safe_meta(swish_debug:print_debug(_Topic, Format, Args), Calls) :-
326+
sandbox:format_calls(Format, Args, Calls).

swish.pl

+1-3
Original file line numberDiff line numberDiff line change
@@ -233,12 +233,10 @@
233233
:- use_module(swish:lib/md_eval).
234234
:- use_module(swish:lib/html_output).
235235
:- use_module(swish:lib/swish_diagnostics).
236+
:- use_module(swish:lib/swish_debug).
236237
:- use_module(swish:library(pengines_io)).
237238
:- use_module(swish:library(solution_sequences)).
238239
:- use_module(swish:library(aggregate)).
239-
:- if((\+current_predicate((table)/1),exists_source(library(tabling)))).
240-
:- use_module(swish:library(tabling)).
241-
:- endif.
242240

243241
pengines:prepare_module(Module, swish, _Options) :-
244242
pengines_io:pengine_bind_io_to_html(Module).

web/css/answer.css

+25
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,31 @@ a.download span.glyphicon
7777
{ margin-left: 1ex;
7878
}
7979

80+
/*******************************
81+
* DEBUG *
82+
*******************************/
83+
84+
div.debug-msg
85+
{ margin-left: 2em;
86+
border: 1px solid #888;
87+
border-radius: 5px;
88+
background: #beb;
89+
}
90+
91+
div.debug-msg span.topic
92+
{ padding-right: 1ex;
93+
float: right;
94+
font-size: 80%;
95+
font-style: italic;
96+
}
97+
98+
div.debug-msg pre
99+
{ border: 0;
100+
background: initial;
101+
padding: 2px 0px 0px 5px;
102+
margin: 0;
103+
}
104+
80105

81106
/*******************************
82107
* WFS *

0 commit comments

Comments
 (0)