35
36:- module(pce_profile,
37 [ pce_show_profile/0
38 ]). 39:- use_module(library(pce)). 40:- use_module(library(lists)). 41:- use_module(library(persistent_frame)). 42:- use_module(library(toolbar)). 43:- use_module(library(pce_report)). 44:- use_module(library(tabular)). 45:- use_module(library(prolog_predicate)). 46
47:- require([ auto_call/1,
48 reset_profiler/0,
49 is_dict/1,
50 profile_data/1,
51 www_open_url/1,
52 pi_head/2,
53 predicate_label/2,
54 predicate_sort_key/2,
55 get_chain/3,
56 send_list/3
57 ]).
69pce_show_profile :-
70 profile_data(Data),
71 in_pce_thread(show_profile(Data)).
72
73show_profile(Data) :-
74 send(new(F, prof_frame), open),
75 send(F, wait),
76 send(F, load_profile, Data).
77
78
79 82
83:- pce_begin_class(prof_frame, persistent_frame,
84 ).
85
86variable(samples, int, get, ).
87variable(ticks, int, get, ).
88variable(accounting_ticks, int, get, ).
89variable(time, real, get, ).
90variable(nodes, int, get, ).
91variable(ports, {true,false,classic}, get, ).
92variable(time_view, {percentage,seconds} := percentage,
93 get, ).
94
95class_variable(auto_reset, bool, @on, ).
96
97initialise(F) :->
98 send_super(F, initialise, 'SWI-Prolog profiler'),
99 send(F, append, new(TD, tool_dialog(F))),
100 send(new(B, prof_browser), left, new(prof_details)),
101 send(B, below, TD),
102 send(new(report_dialog), below, B),
103 send(F, fill_dialog, TD).
104
105fill_dialog(F, TD:tool_dialog) :->
106 send(TD, append, new(File, popup(file))),
107 send(TD, append, new(Sort, popup(sort))),
108 send(TD, append, new(Time, popup(time))),
109 send(TD, append, new(Help, popup(help))),
110 send_list(File, append,
111 [ menu_item(quit,
112 message(F, destroy))
113 ]),
114 forall(sort_by(Label, Field, Order),
115 send(Sort, append,
116 menu_item(Label, message(F, sort_by, Field, Order)))),
117 get(F?class, instance_variable, time_view, TV),
118 get(TV, type, Type),
119 get_chain(Type, value_set, Values),
120 forall(member(TimeView, Values),
121 send(Time, append,
122 menu_item(TimeView, message(F, time_view, TimeView)))),
123 send_list(Help, append,
124 [ menu_item(help,
125 message(F, help))
126 ]).
127
128
129load_profile(F, ProfData0:[prolog]) :->
130 ::
131 ( is_dict(ProfData0)
132 -> ProfData = ProfData0
133 ; profile_data(ProfData)
134 ),
135 Summary = ProfData.summary,
136 send(F, slot, samples, Summary.samples),
137 send(F, slot, ticks, Summary.ticks),
138 send(F, slot, accounting_ticks, Summary.accounting),
139 send(F, slot, time, Summary.time),
140 send(F, slot, nodes, Summary.nodes),
141 send(F, slot, ports, Summary.ports),
142 get(F, member, prof_browser, B),
143 send(F, report, progress, 'Loading profile data ...'),
144 send(B, load_profile, ProfData.nodes),
145 send(F, report, done),
146 send(F, show_statistics),
147 ( get(F, auto_reset, @on)
148 -> reset_profiler
149 ; true
150 ).
151
152
153show_statistics(F) :->
154 ::
155 get(F, samples, Samples),
156 get(F, ticks, Ticks),
157 get(F, accounting_ticks, Account),
158 get(F, time, Time),
159 get(F, slot, nodes, Nodes),
160 get(F, member, prof_browser, B),
161 get(B?dict?members, size, Predicates),
162 ( Ticks == 0
163 -> Distortion = 0.0
164 ; Distortion is 100.0*(Account/Ticks)
165 ),
166 send(F, report, inform,
167 '%d samples in %.2f sec; %d predicates; \c
168 %d nodes in call-graph; distortion %.0f%%',
169 Samples, Time, Predicates, Nodes, Distortion).
170
171
172details(F, From:prolog) :->
173 ::
174 get(F, member, prof_details, W),
175 ( is_dict(From)
176 -> send(W, node, From)
177 ; get(F, member, prof_browser, B),
178 get(B?dict, find,
179 message(@arg1, has_predicate, prolog(From)),
180 DI)
181 -> get(DI, data, Node),
182 send(W, node, Node)
183 ).
184
185sort_by(F, SortBy:name, Order:[{normal,reverse}]) :->
186 ::
187 get(F, member, prof_browser, B),
188 send(B, sort_by, SortBy, Order).
189
190time_view(F, TV:name) :->
191 send(F, slot, time_view, TV),
192 get(F, member, prof_browser, B),
193 get(F, member, prof_details, W),
194 send(B, update_labels),
195 send(W, refresh).
196
197render_time(F, Ticks:int, Rendered:any) :<-
198 ::
199 get(F, time_view, View),
200 ( View == percentage
201 -> get(F, ticks, Total),
202 get(F, accounting_ticks, Accounting),
203 ( Total-Accounting =:= 0
204 -> Rendered = '0.0%'
205 ; Percentage is 100.0 * (Ticks/(Total-Accounting)),
206 new(Rendered, string('%.1f%%', Percentage))
207 )
208 ; View == seconds
209 -> get(F, ticks, Total),
210 ( Total == 0
211 -> Rendered = '0.0 s.'
212 ; get(F, time, TotalTime),
213 Time is TotalTime*(Ticks/float(Total)),
214 new(Rendered, string('%.2f s.', Time))
215 )
216 ).
217
218help(_F) :->
219 send(@display, confirm,
220 'No online help yet\n\c
221 The profiler is described on the SWI-Prolog web site\n\c
222 Press OK to open the page in your browser'),
223 www_open_url('https://www.swi-prolog.org/profile.html').
224
225:- pce_end_class(prof_frame).
226
227
228 231
232:- pce_begin_class(prof_browser, browser,
233 ).
234
235class_variable(size, size, size(40,20)).
236
237variable(sort_by, name := ticks, get, ).
238
239initialise(B) :->
240 send_super(B, initialise),
241 send(B, update_label),
242 send(B, select_message, message(@arg1, details)).
243
244resize(B) :->
245 get(B?visible, width, W),
246 get(B?font, ex, Ex),
247 send(B, tab_stops, vector(W-10*Ex)),
248 send_super(B, resize).
249
250load_profile(B, Nodes:prolog) :->
251 ::
252 get(B, frame, Frame),
253 get(B, sort_by, SortBy),
254 forall(member(Node, Nodes),
255 send(B, append, prof_dict_item(Node, SortBy, Frame))),
256 send(B, sort).
257
258update_label(B) :->
259 get(B, sort_by, Sort),
260 sort_by(Human, Sort, _How),
261 send(B, label, Human?label_name).
262
263sort_by(B, SortBy:name, Order:[{normal,reverse}]) :->
264 ::
265 send(B, slot, sort_by, SortBy),
266 send(B, update_label),
267 send(B, sort, Order),
268 send(B, update_labels).
269
270sort(B, Order:[{normal,reverse}]) :->
271 get(B, sort_by, Sort),
272 ( Order == @default
273 -> sort_by(_, Sort, TheOrder)
274 ; TheOrder = Order
275 ),
276 send_super(B, sort, ?(@arg1, compare, @arg2, Sort, TheOrder)).
277
278update_labels(B) :->
279 ::
280 get(B, sort_by, SortBy),
281 get(B, frame, F),
282 send(B?dict, for_all, message(@arg1, update_label, SortBy, F)).
283
284:- pce_end_class(prof_browser).
285
286:- pce_begin_class(prof_dict_item, dict_item,
287 ).
288
289variable(data, prolog, get, ).
290
291initialise(DI, Node:prolog, SortBy:name, F:prof_frame) :->
292 ::
293 send(DI, slot, data, Node),
294 pce_predicate_label(Node.predicate, Key),
295 send_super(DI, initialise, Key),
296 send(DI, update_label, SortBy, F).
297
298value(DI, Name:name, Value:prolog) :<-
299 ::
300 get(DI, data, Data),
301 value(Name, Data, Value).
302
303has_predicate(DI, Test:prolog) :->
304 get(DI, data, Data),
305 same_pred(Test, Data.predicate).
306
307same_pred(X, X) :- !.
308same_pred(QP1, QP2) :-
309 unqualify(QP1, P1),
310 unqualify(QP2, P2),
311 same_pred_(P1, P2).
312
313unqualify(user:X, X) :- !.
314unqualify(X, X).
315
316same_pred_(X, X) :- !.
317same_pred_(Head, Name/Arity) :-
318 pi_head(Name/Arity, Head).
319same_pred_(Head, user:Name/Arity) :-
320 pi_head(Name/Arity, Head).
321
322compare(DI, DI2:prof_dict_item,
323 SortBy:name, Order:{normal,reverse},
324 Result:name) :<-
325 ::
326 get(DI, value, SortBy, K1),
327 get(DI2, value, SortBy, K2),
328 ( Order == normal
329 -> get(K1, compare, K2, Result)
330 ; get(K2, compare, K1, Result)
331 ).
332
333update_label(DI, SortBy:name, F:prof_frame) :->
334 ::
335 get(DI, key, Key),
336 ( SortBy == name
337 -> send(DI, update_label, ticks_self, F)
338 ; get(DI, value, SortBy, Value),
339 ( time_key(SortBy)
340 -> get(F, render_time, Value, Rendered)
341 ; Rendered = Value
342 ),
343 send(DI, label, string('%s\t%s', Key, Rendered))
344 ).
345
346time_key(ticks).
347time_key(ticks_self).
348time_key(ticks_children).
349
350details(DI) :->
351 ::
352 get(DI, data, Data),
353 send(DI?dict?browser?frame, details, Data).
354
355:- pce_end_class(prof_dict_item).
356
357
358 361
362:- pce_begin_class(prof_details, window,
363 ).
364
365variable(tabular, tabular, get, ).
366variable(node, prolog, get, ).
367
368initialise(W) :->
369 send_super(W, initialise),
370 send(W, pen, 0),
371 send(W, label, 'Details'),
372 send(W, background, colour(grey80)),
373 send(W, scrollbars, vertical),
374 send(W, display, new(T, tabular)),
375 send(T, rules, all),
376 send(T, cell_spacing, -1),
377 send(W, slot, tabular, T).
378
379resize(W) :->
380 send_super(W, resize),
381 get(W?visible, width, Width),
382 send(W?tabular, table_width, Width-3).
383
384title(W) :->
385 ::
386 get(W, tabular, T),
387 BG = (background := khaki1),
388 send(T, append, 'Time', bold, center, colspan := 2, BG),
389 ( get(W?frame, ports, false)
390 -> send(T, append, '# Calls', bold, center, colspan := 1,
391 valign := center, BG, rowspan := 2)
392 ; send(T, append, 'Port', bold, center, colspan := 4, BG)
393 ),
394 send(T, append, 'Predicate', bold, center,
395 valign := center, BG,
396 rowspan := 2),
397 send(T, next_row),
398 send(T, append, 'Self', bold, center, BG),
399 send(T, append, 'Children', bold, center, BG),
400 ( get(W?frame, ports, false)
401 -> true
402 ; send(T, append, 'Call', bold, center, BG),
403 send(T, append, 'Redo', bold, center, BG),
404 send(T, append, 'Exit', bold, center, BG),
405 send(T, append, 'Fail', bold, center, BG)
406 ),
407 send(T, next_row).
408
409cluster_title(W, Cycle:int) :->
410 get(W, tabular, T),
411 ( get(W?frame, ports, false)
412 -> Colspan = 4
413 ; Colspan = 7
414 ),
415 send(T, append, string('Cluster <%d>', Cycle),
416 bold, center, colspan := Colspan,
417 background := navyblue, colour := yellow),
418 send(T, next_row).
419
420refresh(W) :->
421 ::
422 ( get(W, node, Data),
423 Data \== @nil
424 -> send(W, node, Data)
425 ; true
426 ).
427
428node(W, Data:prolog) :->
429 ::
430 send(W, slot, node, Data),
431 send(W?tabular, clear),
432 send(W, scroll_to, point(0,0)),
433 send(W, title),
434 clusters(Data.callers, CallersCycles),
435 clusters(Data.callees, CalleesCycles),
436 ( CallersCycles = [_]
437 -> show_clusters(CallersCycles, CalleesCycles, Data, 0, W)
438 ; show_clusters(CallersCycles, CalleesCycles, Data, 1, W)
439 ).
440
441show_clusters([], [], _, _, _) :- !.
442show_clusters([P|PT], [C|CT], Data, Cycle, W) :-
443 show_cluster(P, C, Data, Cycle, W),
444 Next is Cycle+1,
445 show_clusters(PT, CT, Data, Next, W).
446show_clusters([P|PT], [], Data, Cycle, W) :-
447 show_cluster(P, [], Data, Cycle, W),
448 Next is Cycle+1,
449 show_clusters(PT, [], Data, Next, W).
450show_clusters([], [C|CT], Data, Cycle, W) :-
451 show_cluster([], C, Data, Cycle, W),
452 Next is Cycle+1,
453 show_clusters([], CT, Data, Next, W).
454
455
456show_cluster(Callers, Callees, Data, Cycle, W) :-
457 ( Cycle == 0
458 -> true
459 ; send(W, cluster_title, Cycle)
460 ),
461 sort_relatives(Callers, Callers1),
462 show_relatives(Callers1, parent, W),
463 ticks(Callers1, Self, Children, Call, Redo, Exit),
464 send(W, show_predicate, Data, Self, Children, Call, Redo, Exit),
465 sort_relatives(Callees, Callees1),
466 reverse(Callees1, Callees2),
467 show_relatives(Callees2, child, W).
468
469ticks(Callers, Self, Children, Call, Redo, Exit) :-
470 ticks(Callers, 0, Self, 0, Children, 0, Call, 0, Redo, 0, Exit).
471
472ticks([], Self, Self, Sibl, Sibl, Call, Call, Redo, Redo, Exit, Exit).
473ticks([H|T],
474 Self0, Self, Sibl0, Sibl, Call0, Call, Redo0, Redo, Exit0, Exit) :-
475 arg(1, H, '<recursive>'),
476 !,
477 ticks(T, Self0, Self, Sibl0, Sibl, Call0, Call, Redo0, Redo, Exit0, Exit).
478ticks([H|T], Self0, Self, Sibl0, Sibl, Call0, Call, Redo0, Redo, Exit0, Exit) :-
479 arg(3, H, ThisSelf),
480 arg(4, H, ThisSibings),
481 arg(5, H, ThisCall),
482 arg(6, H, ThisRedo),
483 arg(7, H, ThisExit),
484 Self1 is ThisSelf + Self0,
485 Sibl1 is ThisSibings + Sibl0,
486 Call1 is ThisCall + Call0,
487 Redo1 is ThisRedo + Redo0,
488 Exit1 is ThisExit + Exit0,
489 ticks(T, Self1, Self, Sibl1, Sibl, Call1, Call, Redo1, Redo, Exit1, Exit).
490
491
495
496clusters(Relatives, Cycles) :-
497 clusters(Relatives, 0, Cycles).
498
499clusters([], _, []).
500clusters(R, C, [H|T]) :-
501 cluster(R, C, H, T0),
502 C2 is C + 1,
503 clusters(T0, C2, T).
504
505cluster([], _, [], []).
506cluster([H|T0], C, [H|TC], R) :-
507 arg(2, H, C),
508 !,
509 cluster(T0, C, TC, R).
510cluster([H|T0], C, TC, [H|T]) :-
511 cluster(T0, C, TC, T).
512
516
517sort_relatives(List, Sorted) :-
518 key_with_calls(List, Keyed),
519 keysort(Keyed, KeySorted),
520 unkey(KeySorted, Sorted).
521
522key_with_calls([], []).
523key_with_calls([H|T0], [0-H|T]) :- 524 arg(1, H, '<recursive>'),
525 !,
526 key_with_calls(T0, T).
527key_with_calls([H|T0], [K-H|T]) :-
528 arg(4, H, Calls),
529 arg(5, H, Redos),
530 K is Calls+Redos,
531 key_with_calls(T0, T).
532
533unkey([], []).
534unkey([_-H|T0], [H|T]) :-
535 unkey(T0, T).
536
540
541show_relatives([], _, _) :- !.
542show_relatives([H|T], Role, W) :-
543 send(W, show_relative, H, Role),
544 show_relatives(T, Role, W).
545
546show_predicate(W, Data:prolog,
547 Ticks:int, ChildTicks:int,
548 Call:int, Redo:int, Exit:int) :->
549 ::
550 Pred = Data.predicate,
551 get(W, frame, Frame),
552 get(Frame, render_time, Ticks, Self),
553 get(Frame, render_time, ChildTicks, Children),
554 get(W, tabular, T),
555 BG = (background := khaki1),
556 Fail is Call+Redo-Exit,
557 send(T, append, Self, halign := right, BG),
558 send(T, append, Children, halign := right, BG),
559 ( get(W?frame, ports, false)
560 -> send(T, append, Call, halign := right, BG)
561 ; send(T, append, Call, halign := right, BG),
562 send(T, append, Redo, halign := right, BG),
563 send(T, append, Exit, halign := right, BG),
564 send(T, append, Fail, halign := right, BG)
565 ),
566 ( object(Pred)
567 -> new(Txt, prof_node_text(Pred, self))
568 ; new(Txt, prof_predicate_text(Pred, self))
569 ),
570 send(T, append, Txt, BG),
571 send(W, label, string('Details -- %s', Txt?string)),
572 send(T, next_row).
573
574show_relative(W, Caller:prolog, Role:name) :->
575 Caller = node(Pred, _Cluster, Ticks, ChildTicks, Calls, Redos, Exits),
576 get(W, tabular, T),
577 get(W, frame, Frame),
578 ( Pred == '<recursive>'
579 -> send(T, append, new(graphical), colspan := 2),
580 send(T, append, Calls, halign := right),
581 ( get(W?frame, ports, false)
582 -> true
583 ; send(T, append, new(graphical), colspan := 3)
584 ),
585 send(T, append, Pred, italic)
586 ; get(Frame, render_time, Ticks, Self),
587 get(Frame, render_time, ChildTicks, Children),
588 send(T, append, Self, halign := right),
589 send(T, append, Children, halign := right),
590 ( get(W?frame, ports, false)
591 -> send(T, append, Calls, halign := right)
592 ; Fails is Calls+Redos-Exits,
593 send(T, append, Calls, halign := right),
594 send(T, append, Redos, halign := right),
595 send(T, append, Exits, halign := right),
596 send(T, append, Fails, halign := right)
597 ),
598 ( Pred == '<spontaneous>'
599 -> send(T, append, Pred, italic)
600 ; object(Pred)
601 -> send(T, append, prof_node_text(Pred, Role))
602 ; send(T, append, prof_predicate_text(Pred, Role))
603 )
604 ),
605 send(T, next_row).
606
607
608:- pce_end_class(prof_details).
609
610
611:- pce_begin_class(prof_node_text, text,
612 ).
613
614variable(context, any, get, ).
615variable(role, {parent,self,child}, get, ).
616
617initialise(T, Context:any, Role:{parent,self,child}, Cycle:[int]) :->
618 send(T, slot, context, Context),
619 send(T, slot, role, Role),
620 get(T, label, Label),
621 ( ( Cycle == 0
622 ; Cycle == @default
623 )
624 -> TheLabel = Label
625 ; N is Cycle+1, 626 TheLabel = string('%s <%d>', Label, N)
627 ),
628 send_super(T, initialise, TheLabel),
629 send(T, colour, blue),
630 send(T, underline, @on),
631 ( Role == self
632 -> send(T, font, bold)
633 ; true
634 ).
635
636
637label(T, Label:char_array) :<-
638 get(T?context, print_name, Label).
639
640
641:- free(@prof_node_text_recogniser). 642:- pce_global(@prof_node_text_recogniser,
643 make_prof_node_text_recogniser). 644
645make_prof_node_text_recogniser(G) :-
646 Text = @arg1,
647 Pred = @arg1?context,
648 new(P, popup),
649 send_list(P, append,
650 [ menu_item(details,
651 message(Text, details),
652 condition := Text?role \== self),
653 menu_item(edit,
654 message(Pred, edit),
655 condition := Pred?source),
656 menu_item(documentation,
657 message(Pred, help),
658 condition := message(Text, has_help))
659 ]),
660 new(C, click_gesture(left, '', single,
661 message(@receiver, details))),
662 new(G, handler_group(C, popup_gesture(P))).
663
664
665event(T, Ev:event) :->
666 ( send_super(T, event, Ev)
667 -> true
668 ; send(@prof_node_text_recogniser, event, Ev)
669 ).
670
671has_help(T) :->
672 get(T, context, Ctx),
673 ( send(Ctx, instance_of, method) 674 -> auto_call(manpce)
675 ; true
676 ),
677 send(Ctx, has_send_method, has_help),
678 send(Ctx, has_help).
679
680details(T) :->
681 ::
682 get(T, context, Context),
683 send(T?frame, details, Context).
684
685:- pce_end_class(prof_node_text).
686
687
688:- pce_begin_class(prof_predicate_text, prof_node_text,
689 ).
690
691initialise(T, Pred:prolog, Role:{parent,self,child}, Cycle:[int]) :->
692 send_super(T, initialise, prolog_predicate(Pred), Role, Cycle).
693
694details(T) :->
695 ::
696 get(T?context, pi, @on, Head),
697 send(T?frame, details, Head).
698
699:- pce_end_class(prof_predicate_text).
700
701
702 705
706value(name, Data, Name) :-
707 !,
708 predicate_sort_key(Data.predicate, Name).
709value(label, Data, Label) :-
710 !,
711 pce_predicate_label(Data.predicate, Label).
712value(ticks, Data, Ticks) :-
713 !,
714 Ticks is Data.ticks_self + Data.ticks_siblings.
715value(Name, Data, Value) :-
716 Value = Data.Name.
717
718sort_by(cumulative_profile_by_time, ticks, reverse).
719sort_by(flat_profile_by_time_self, ticks_self, reverse).
720sort_by(cumulative_profile_by_time_children, ticks_siblings, reverse).
721sort_by(flat_profile_by_number_of_calls, call, reverse).
722sort_by(flat_profile_by_number_of_redos, redo, reverse).
723sort_by(flat_profile_by_name, name, normal).
731pce_predicate_label(Obj, Label) :-
732 object(Obj),
733 !,
734 get(Obj, print_name, Label).
735pce_predicate_label(PI, Label) :-
736 predicate_label(PI, Label)
GUI frontend for the profiler
This module hooks into profile/1 and provides a graphical UI for the profiler output. */