This solution taps onto SWI debugger interface so its sort of a hack to keep track every goal using the builtin tracer (keeps the current trace on the dynamic database). It seems to work fine for simple queries like your example.
:-dynamic '$trace'/1.
get_trace(Pred, L):-
retractall('$trace'(_)),
assertz('$trace'([])),
trace_notrace(t,t),
call(Pred),
call('$trace'(Trace)),
trace_notrace(nt,nt),
reverse(Trace, RTrace),
maplist(arg(2), RTrace, L).
trace_notrace(t,_):-
trace.
trace_notrace(T, _):-
nodebug,
T=nt.
trace_notrace(nt, T):-
trace,
T=t.
prolog_trace_interception(call, Frame, _PC, continue) :-
retract('$trace'(Trace)),
assertz('$trace'([frame(Frame, _Goal)|Trace])),
!.
prolog_trace_interception(exit, Frame, _PC, continue) :-
prolog_frame_attribute(Frame, goal, Goal),
prolog_trace_interception_goal(Goal, Goal1),
retract('$trace'(Trace)),
( append(Head, [frame(Frame, _)|MTrace], Trace)
-> append(Head, [frame(Frame, Goal1)|MTrace], NTrace)
; NTrace=Trace
),
assertz('$trace'(NTrace)),
!.
prolog_trace_interception(fail, Frame, _PC, continue) :-
retract('$trace'(Trace)),
append(_, [frame(Frame, _)|NTrace], Trace),
assertz('$trace'(NTrace)),
!.
prolog_trace_interception(redo(_), Frame, _PC, continue) :-
retract('$trace'(Trace)),
append(_, [frame(Frame, Goal)|NTrace], Trace),
assertz('$trace'([frame(Frame, Goal)|NTrace])),
!.
prolog_trace_interception(_, _, _, continue).
prolog_trace_interception_goal(Goal, Goal2):-
(
(Goal=(Module:Goal1), memberchk(Module, [system, lists, '$bags']))
-> copy_term(Goal1, Goal2)
; copy_term(Goal, Goal2)
).
Sample runs:
?- findall(L, get_trace(test, L), Traces).
Traces = [[test, a, v(1)], [test, a, v(2)]].
?- get_trace(member(X, [a,b]), Trace).
X = a,
Trace = [member(a, [a, b])] ;
X = b,
Trace = [member(b, [a, b])] ;
false.