3.3 Fuzzy Logic Programming
4.1.2 Representational Layer
4.1.2.3 Reifying the Source Model
The reification process varies with the form of dynamic analysis that is deployed. Mapping Objects to Unique Identifiers In an ad-hoc analysis, the symbiosis be- tween Smalltalk and SOUL can be exploited to use the actual Smalltalk objects in- volved in the execution trace as arguments of the above predicates. We could do the same with the post-mortem analysis variant, but the equivalent transcription to Prolog
forces us to map each object to a unique object identifying integer. During a post- mortem analysis within SOUL, it is however possible to retrieve the object correspond- ing to a given integer using the objectMap(?ObjectNumber,?Object) predicate, but when using this functionality it is important to bear in mind that the retrieved ob- ject has already been manipulated during the entire execution trace and that its values no longer represent the values it had during the run-time event it was involved in at a particular time.
Post-mortem Analysis Under post-mortem analysis, the process consists of instru- menting the classes under investigation followed by an execution of a well-determined scenario that focuses the execution trace on interesting object interactions and be- haviour. The events generated during the execution of the traced classes are gathered into a knowledge base of logic facts after which the declarative analysis can start. In other words: we do not start reasoning until the application has ended. This al- lows advanced (i.e. we can consider multiple candidate solutions when looking for a particular run-time event in the execution history through backtracking) reasoning pat- terns over the entire run-time behaviour of a program. It however also means that large amounts of data need to be stored and processed which might be overkill and even slash the performance of very simple rules.
Ad-hoc Analysis In the alternative ad-hoc analysis variant, the declarative reasoning process runs as a coroutine alongside the application. The execution of the application is interleaved with the evaluation of the logic program which can suspend and resume the execution of the former.
We declaratively request a particular execution event and the execution of the applica- tion that is being analysed is continued until this event is encountered. Control then returns to the reasoning process where we analyse the current event and make a request for the following event.
As a consequence, we have at no time during the evaluation of our query a complete execution history to our disposal. The applicability of this approach is thus limited to lightweight rules: since the execution of the program is advanced whenever we back- track upon a choice for a run-time event we can’t access past events in this way and cannot consider alternatives for an event that is requested to prove the body of a rule without advancing the application.
To interweave the evaluation of the application and the logic program, SOUL and the program under execution need to be started in separate processes. We can then query for the next execution event by simply launching a goal with one of the predicates from the meta-model. These predicates are no longer facts but full-grown rules that suspend and resume the application’s execution. By using the same predicates for ad-hoc and post-mortem dynamic analysis, we ensure a maximal reuse of common rules.
The following rule illustrates how the methodEntry predicate used to model method invocation events now steers the application’s execution. Also note how the rule’s arguments now contain the actual Smalltalk objects involved in the run-time event: methodEntry(?sequenceNumber,?sendingInstance,
?receivingInstance,?receivedSelector, ?receivedArguments) if event(?event), [?event isMethodEntryEvent], equals(?sequenceNumber,[?event sequenceNumber]), equals(?sendingInstance,[?event sender]), equals(?receivingInstance,[?event receiver]), equals(?receivedSelector,[?event receivedSelector]), listAsCollection(?receivedArguments,[?event receivedArguments]) The rule first asks for a new run-time event which must be of the methodEntryEvent type. If it is not, the system will simply backtrack and ask for another event un- til one of the correct type is encountered. The events themselves are instances from the classes depicted in figure 4.2 to which we can send regular Smalltalk messages. We use this property in the above rule to dissect an event into its sequenceNumber, sendingInstance, receivingInstance, receivedSelector and receivedArguments components.
Because the execution of the analysed application is paused when a suitable event is encountered, the information in this event is always accurate and up-to-date. We there- fore do not have to map the objects participating in the event to unique integer identi- fiers and can instead use the actual objects themselves. This is a major advantage over post-mortem traces as we can now access and manipulate the objects in the analysed program during its execution with declarative meta-programming which is of use in advanced debugging sessions.
The application’s execution is interweaved with the evaluation of the logic program using the threefold event predicate:
event(?e) if
equals(?e,[Tracing.Trace current event]),
[Tracing.Trace current event isProgramEndEvent], !
event(?e) if
not([Tracing.Trace current event isProgramEndEvent]), equals(?e,[Tracing.Trace current event])
event(?e) if
not([Tracing.Trace current event isProgramEndEvent]), [ Tracing.Trace current semaphore signal.
Tracing.Trace current semaphore wait. true],
event(?e)
The first part of the rule defines the obvious case in which the program has ended and no more solutions can be found to this rule. The second part simply unifies the argument of the predicate with the last encountered execution event. When we ask for more solutions to this rule, the third part is invoked which signals a semaphore shared by the application process and SOUL’s process. This signal results in the execution of the application being resumed. We also put our own reasoning process to sleep by sending
the wait message to the shared semaphore. When a run-time event is encountered, the exact opposite will happen at the application’s side: it will signal the semaphore which results in the reasoning process to be resumed and put itself to sleep again. The entire process is depicted schematically in figure 4.3.
Figure 4.3: Schematic representation of the ad-hoc analysis process.