There are a number of tools in addition to the listener debugger that help track down bugs in embedded and standalone Prolog programs.
When you are first developing your Prolog components, you can usually create a suite of predicates that simulate the way the Prolog code will be called from a host language.
By using a simulated front-end written in Prolog, you can get most of the Prolog code working correctly using the normal development tools of the Windows-IDE.
You can also create Prolog versions of any extended predicates you write, where the Prolog versions simply return values that simulate the results you expect returned by your extended predicates.
There are a number of ways to gather information from a running Prolog program. These are described in this section.
The most basic debugging tool is the write statement. In the case of an embedded Prolog module, especially in a Windows environment, the best way to add 'write's is to use the logging capabilities of Amzi!.
You can specify that a logfile be opened either from the Prolog program:
openlog('log.log'),
or from the .cfg file. For example, if your .xpl file is foo.xpl then you can add this line to your foo.cfg file:
logfile = log.log
Once you've got a log file you can write to it using the writelog/1 predicate and the nllog/0 (for newlines) predicate.
writelog($The funny term is: $), writelog(FunnyTerm), nllog,
You close the logfile with:
closelog.
If you're having trouble with the flow-of-control in the running program, you can add some special statements that provide output similar to the debugger's for each of the four ports, CALL, EXIT, FAIL, and REDO.
To do this include the bug.plm library in your project or add a :-load(bug). directive in your program. Then simply put the operator '?' in front of lines that you want to create output for. Or, use the bug/1 predicate like a call/1 predicate.
For example, if you suspected problems with goal2 below, the ? operator will generate diagnostic information for each of the ports. It will work for both compiled and interpreted code.
failing_predicate(W,Z) :- goal1(W,X), ? goal2(X,Y), goal3(Y,Z).
These predicates allow you to implement better error handling in your own code. catch/3 lets you make a normal call to a goal with the added feature that it also 'catch'es any exceptions that are 'throw'n further down the code. This makes it easier for you to implement your own error catching code. For example:
main :- catch(dostuff, error(X,Y), process_err(X,Y)). process_err(badpredarg, X) :- write($Bad argument given to predicate: $), write(X), nl. process_err(X, Y) :- write($Some other error: $), write(X:Y), nl. dostuff :- foo1, foo2, ... foo2 :- getfromsomewhere(X), foon(X), ... foon(case1) :- ... foon(case2) :- ... ... foon(X) :- throw(error(badpredarg, foon(X)). |
See the example samples/prolog/misc/catch.pro for more details.
Sometimes error messages are not retrieved fully from an executing Prolog .xpl file. You can often look in the file foo.err, where foo is the name of the .cfg or .xpl file, to find the error messages emitted during a run.
This same file can be useful in the IDE as well. The listener writes its messages to wideA.err (or wideW.err) and the compiler writes messages to acmp.err. These correspond to the two .xpl Files that are run by the IDE.
The existence of the error file does not eliminate the need to check error conditions from Logic Server API calls.
It is often the case that the Prolog program is running just fine, but the Logic Server API calls are not communicating with it correctly.
The various techniques listed in the above section can be used by the Prolog program to indicate that it has, indeed, been called correctly from the host program, and to verify that the terms being passed back to the host program are the correct terms.
Given that Prolog is working correctly, then the problem is somewhere in the LSAPI calls that are retrieving the information.
To help debug host applications calling the Logic Server API, we've added an API trace facility. You initiate it by setting the .cfg parameter 'apitrace' to 'on'. You must specify a 'logfile' as well, for the trace output goes to the log file.
If you specify 'apitrace' in amzi.cfg, then you'll catch errors in the lsInit() call if there are any. If you specify it in myprog.cfg, where 'myprog' is the name of your .xpl file, you'll get trace information as long as the initialization completed OK.
What follows is some apitrace output from the xgene.c sample program.
lsInit(0, "xgene") returns OK lsLoad(0, "xgene") returns OK lsStrToTerm(0, 00442380, "sibling(mary, Y)") returns OK lsCall(0, 00442380) returns TRUE, term bound to "sibling(mary,mary)" lsGetFA(0, 004b7060, 00442358, 00442384) returns OK, functor set to "sibling" arity 2 lsGetArg(0, 004b7060, 1, 1, 004422e0) returns 0 arg 1 = mary lsGetArg(0, 004b7060, 2, 1, 00442330) returns 0 arg 2 = mary lsRedo(0) returns TRUE lsGetFA(0, 004b7060, 00442358, 00442384) returns OK, functor set to "sibling" arity 2 lsGetArg(0, 004b7060, 1, 1, 004422e0) returns 0 arg 1 = mary lsGetArg(0, 004b7060, 2, 1, 00442330) returns 0 arg 2 = kathy |
In the API calls, make sure you always check for the appropriate return codes. Add an if statement that stops when an API error is encountered. In that code you can get error message using the API call lsGetExceptMsg(). For read errors you can also get the input read buffer using lsGetExceptReadBuffer().
If you are doing hacking of Prolog terms, you can verify that the terms are what you expect by using lsTermToStr(). It converts a term into a string, just as a write(X) would write whatever the term X was from a Prolog listener. You can then look at the string to see if you are picking up the term correctly.
For example:
tf = lsCallStr(eid, &term, "foo(X,Y)"); #ifdef DEBUG lsTermToStr(eid, &term, buffer, 255); printf("Called: %s\n", buffer); #endif if (tf == TRUE) { #ifdef DEBUG lsGetArg(eid, term, 2, cTERM, &targ2); lsTermToStr(eid, &targ2, buffer, 255); printf("Arg2: %s\n", buffer); #endif lsGetArg(eid, term, 2, cSTR, arg2); ...do the right thing... } else if (tf == FALSE) printf("Call failed\n"); else { lsErrMsg(eid, buffer); printf("Prolog error: %d, %s\n", tf, buffer); } } |
This section of code will report the error messages of any erroneous results, as well as, during debug mode, display the values of various terms during the course of execution.
Copyright ©1987-2000 Amzi! inc. All Rights Reserved.