Merge branch 'master' of https://github.com/javapathfinder/jpf-core
[jpf-core.git] / docs / devel / listener.md
1 # Listeners #
2 Listeners are perhaps the most important extension mechanism of JPF. They provide a way to observe, interact with and extend JPF execution with your own classes. Since listeners are dynamically configured at runtime, they do not require any modification to the JPF core. Listeners are executed at the same level like JPF, so there is hardly any limit of what you can do with them.
3
4 ![Figure 1: JPF Listeners](../graphics/listener-overview.svg){align=center width=800}
5
6 The general principle is simple: JPF provides an observer pattern implementation that notifies registered observer instances about certain events at the search level and the VM level. These notifications cover a broad spectrum of JPF operations, from low level events like `instructionExecuted` to high level events like `searchFinished`. Each notification is parameterized with the corresponding source (either the `Search` or the `VM` instance), which can be then used by the notified listener to obtain more information about the event and the JPF's internal state.
7
8 Configuration is usually done with the `listener` property, either from the command line, or a .jpf property file. Listeners can also be associated with annotations, to be automatically loaded whenever JPF encounters such an annotation. Applications can use the `@JPFConfig` annotation to explicitly specify JPF listeners. Lastly, if JPF is used in an embedded mode, listeners can be registered with an API.
9
10
11 ## Listener Types ##
12
13
14 There are two basic listener interfaces, depending on corresponding event sources: `SearchListeners` and `VMListeners`. Since these interfaces are quite large, and listeners often need to implement both, we also provide "adapter" classes, i.e. implementors that contain all required method definitions with empty method bodies. Concrete listeners that extend these adapters therefore only have to override the notification methods they are interested in.
15
16 The adapter classes are used for the majority of listener implementations, especially since they also support two other interfaces/extension mechanisms that are often used in conjunction with `Search` and `VMListeners`: 
17
18  1. `Property` - to define program properties
19  2. `PublisherExtension` - to produce output within [the JPF reporting system](report)
20
21 `ListenerAdapter` is the bare adapter implementation for `SearchListener`, `VMListener` and `PublisherExtension`. This is what is mostly used to collect information during JPF execution (e.g. `CoverageAnalyzer` and `DeadlockAnalyzer`).
22
23 `PropertyListenerAdapter` is used in case the listener implements a program property, i.e. it can terminate the search process. A prominent example of this category is `PreciseRaceDetector`.
24
25 ![Figure 2: Listener Types](../graphics/listeners.svg){align=center width=800}
26
27 Choosing the right type for your listener is important, since JPF automatically registers listeners (and properties) based on this type. You can bypass and directly implement single listener interfaces, but then you also have to do the proper registrations.
28
29 Usually, the notification alone is not enough, and the listener needs to acquire more information from JPF. For this purpose, we provide either the `Search` or the `VM` instance as notification arguments, and the listener has to use these as "Facades" to query or interact JPF. It therefore matters to implement the listener within the right package.
30
31
32 ## SearchListener ##
33
34
35 `SearchListener` instances are used to monitor the state space search process, e.g. to create graphical representations of the state-graph. They provide notification methods for all major Search actions.
36
37 ~~~~~~~~ {.java}
38 package gov.nasa.jpf.search;
39 public interface SearchListener extends JPFListener {
40   void searchStarted (Search search);  
41   void stateAdvanced (Search search);       // got next state
42   void stateProcessed (Search search);      // state is fully explored
43   void stateBacktracked (Search search);    // state was backtracked one step (same path)
44   void stateStored (Search search);         // somebody stored the state
45   void stateRestored (Search search);       // previously generated state was restored (any path)
46   void propertyViolated (Search search);    // JPF encountered a property violation
47   void searchConstraintHit (Search search); // e.g. max search depth
48   void searchFinished (Search search);
49 }}
50 ~~~~~~~~
51
52
53 For the standard depth first search (`gov.nasa.jpf.search.DFSearch`), listener implementations can assume the following notification model:
54
55
56 ![Figure 3: Depth first listener notifications](../graphics/DFSListener.svg){align=center width=500}
57
58 The most frequently used notifications are:
59
60 `stateAdvanced` - to store additional, backtrackable state information in an associative array
61
62 `stateBacktracked` - to restore additional state information
63
64 `searchFinished` - to process listener results
65
66
67 ## VMListener ##
68
69
70 This is a fat interface, reflecting various VM operations
71
72 ~~~~~~~~ {.java}
73 package gov.nasa.jpf.jvm;
74 public interface VMListener extends JPFListener {
75   //--- basic bytecode execution
76   void executeInstruction (JVM vm);  // JVM is about to execute the next instruction
77   void instructionExecuted (JVM vm); // JVM has executed an instruction
78
79   //--- thread operations (scheduling)
80   void threadStarted (JVM vm);       // new Thread entered run()
81   void threadBlocked (JVM vm);       // thread waits to acquire a lock
82   void threadWaiting (JVM vm);       // thread is waiting for signal
83   void threadNotified (JVM vm);      // thread got notified
84   void threadInterrupted (JVM vm);   // thread got interrupted
85   void threadTerminated (JVM vm);    // Thread exited run()
86   void threadScheduled (JVM vm);     // new thread was scheduled by JVM
87
88   //--- class management
89   void classLoaded (JVM vm);         // new class was loaded
90
91   //--- object operations
92   void objectCreated (JVM vm);       // new object was created
93   void objectReleased (JVM vm);      // object was garbage collected
94   void objectLocked (JVM vm);        // object lock acquired
95   void objectUnlocked (JVM vm);      // object lock released
96   void objectWait (JVM vm);          // somebody waits for object lock
97   void objectNotify (JVM vm);        // notify single waiter for object lock
98   void objectNotifyAll (JVM vm);     // notify all waiters for object lock
99
100   void gcBegin (JVM vm);             // start garbage collection
101   void gcEnd (JVM vm);               // garbage collection finished
102
103   void exceptionThrown (JVM vm);     // exception was thrown
104
105   //--- ChoiceGenerator operations  
106   void choiceGeneratorSet (JVM vm);  // new ChoiceGenerator registered
107   void choiceGeneratorAdvanced (JVM vm); // new choice from current ChoiceGenerator
108   void choiceGeneratorProcessed (JVM vm); // current ChoiceGenerator processed all choices  
109 }
110 ~~~~~~~~
111
112
113 The most commonly used methods are the instruction notifications:
114
115 `executeInstruction` - is called before a bytecode instruction gets executed by the VM. The listener can even use this to skip and/or replace this instruction, which is useful for non-invasive instrumentation.
116
117 `instructionExecuted` - is the post-execution notification, which is suitable to keep track of execution results (method invocations, assigned field values, branch results etc.)
118
119
120 ## Example ##
121
122 The following example is a slightly abbreviated form our race detector. The basic idea is simple: every time we encounter a new scheduling point (i.e. new `ThreadChoiceGenerator` object) that is due to a field access on a shared object, we check if any of the other runnable threads is currently accessing the same field on the same object. If at least one operation is a `putfield`, we have a potential race.
123
124 The example shows three aspects that are quite typical: 
125
126   1. listeners often use only a small number of notification methods
127
128   2. they often do not require a huge amount of code (most expensive operations are performed by the `VM` and the `Search` objects)
129
130   3. sometimes you have to dig deep into JPF internal constructs, to extract things like `ThreadInfo`, `FieldInfo` and `ChoiceGenerator` instances
131
132 ~~~~~~~~ {.java}
133 public class PreciseRaceDetector extends PropertyListenerAdapter {
134   FieldInfo raceField;
135   ...
136   //--- the Property part
137   public boolean check(Search search, JVM vm) {
138     return (raceField == null);
139   }
140
141   //--- the VMListener part
142   public void choiceGeneratorSet(JVM vm) {
143     ChoiceGenerator<?> cg = vm.getLastChoiceGenerator();
144
145     if (cg instanceof ThreadChoiceFromSet) {
146       ThreadInfo[] threads = ((ThreadChoiceFromSet)cg).getAllThreadChoices();
147       ElementInfo[eiCandidates = new ElementInfo[threads.length](]);
148       FieldInfo[fiCandidates = new FieldInfo[threads.length](]);
149
150       for (int i=0; i<threads.length; i++) {
151         ThreadInfo ti = threads[i];
152         Instruction insn = ti.getPC();
153         
154         if (insn instanceof FieldInstruction) { // Ok, its a get/putfield
155           FieldInstruction finsn = (FieldInstruction)insn;
156           FieldInfo fi = finsn.getFieldInfo();
157
158           if (StringSetMatcher.isMatch(fi.getFullName(), includes, excludes)) {
159             ElementInfo ei = finsn.peekElementInfo(ti);
160
161             // check if we have seen it before from another thread
162             int idx=-1;
163             for (int j=0; j<i; j++) {
164               if ((ei ## eiCandidates[&& (fi ## fiCandidates[j](j])))) {
165                 idx = j;
166                 break;
167               }
168             }
169
170             if (idx >= 0){ // yes, we have multiple accesses on the same object/field
171               Instruction otherInsn = threads[idx].getPC();
172               if (isPutInsn(otherInsn) || isPutInsn(insn)) {
173                 raceField = ((FieldInstruction)insn).getFieldInfo();
174                 ..
175                 return;
176               }
177             } else {
178               eiCandidates[i] = ei;
179               fiCandidates[i] = fi;
180             }
181           }
182         }
183       }
184     }
185   }
186
187   public void executeInstruction (JVM jvm) {
188     if (raceField != null) {  // we're done, report as quickly as possible
189       ThreadInfo ti = jvm.getLastThreadInfo();
190       ti.breakTransition();
191     }
192   }
193 }
194 ~~~~~~~~
195
196 ## Instantiation ##
197
198 Explicit instantiation of a listener (e.g. from a JPF shell) can be done in any way. If the listener is specified as a JPF property, it's class either needs to have a default constructor, or a constructor that takes a single `gov.nasa.jpf.Config` argument. The `Config` object that is passed into this constructor by JPF is the same that was used for the initialization of JPF itself. This is the preferred method if the listener has to be parameterized. In case of the `PreciseRaceDetector` example, this can be used to filter relevant fields with regular expressions:
199
200 ~~~~~~~~ {.java}
201 public class PreciseRaceDetector extends PropertyListenerAdapter {
202   ...
203   StringSetMatcher includes = null;
204   StringSetMatcher excludes = null;
205   
206   public PreciseRaceDetector (Config conf) {
207     includes = StringSetMatcher.getNonEmpty(conf.getStringArray("race.include"));
208     excludes = StringSetMatcher.getNonEmpty(conf.getStringArray("race.exclude"));
209   }
210   ...
211   public void choiceGeneratorSet(JVM vm) {
212     ...
213         FieldInfo fi =..
214         if (StringSetMatcher.isMatch(fi.getFullName(), includes, excludes))
215      ...
216   }
217 ~~~~~~~~
218
219
220 ## Configuration ##
221
222 Listener configuration can be done in a number of ways: via JPF properties from the command line or a .jpf file, via JPF APIs from a JPF shell (a program invoking JPF), or from the system under test by using Java annotations (i.e. without code modification).
223
224 Since listeners are executed by the host VM, they have to be in the `CLASSPATH` (`jpf-core.native_classpath` property).
225
226
227 ### command line ###
228
229
230 the *listener* property can be used to specify a colon separated list of listener class names:
231
232 ~~~~~~~~ {.bash}
233 bin/jpf ... +listener=x.y.MyFirstListener,x.z.MySecondListener ...
234 ~~~~~~~~
235
236
237 ### .jpf property file ###
238
239
240 If you have several listeners and/or a number of other JPF options, it is more convenient to add the `listener` property to a .jpf file:
241
242 ~~~~~~~~ {.bash}
243 # Racer-listener.jpf - JPF mode property file to detect data races in jpftest.Racer
244 target = jpftest.Racer
245 listener=gov.nasa.jpf.tools.PreciseRaceDetector
246 ~~~~~~~~
247
248
249 ### Autoload Annotations ###
250
251
252 Consider your system under test is marked up with a Java annotation that represent properties. For example, you can use the `@NonNull` annotation to express that a method is not allowed to return a `null` value:
253
254 ~~~~~~~~ {.java}
255 import gov.nasa.jpf.NonNull; 
256   ...
257   @NonNull X computeX (..) {
258     //.. some complex computation
259   }
260   ...
261 ~~~~~~~~
262
263 You can use .jpf property files (or the command line, if you love to type) to tell JPF that it should automatically load and register a corresponding listener (e.g. `NonNullChecker`) if it encounters such a `@NonNull` annotation during class loading:
264
265 ~~~~~~~~ {.bash}
266 ..
267 listener.autoload = gov.nasa.jpf.NonNull,...
268 listener.gov.nasa.jpf.NonNull = gov.nasa.jpf.tools.NonNullChecker
269 ...
270 ~~~~~~~~
271
272
273 ### @JPFConfig annotation (SuT) ###
274
275
276 You can also explicitly direct JPF to load the listener from within your application by using the `@JPFConfig` annotation:
277
278 ~~~~~~~~ {.java}
279 import gov.nasa.jpf.JPFConfig;
280 ..
281 // set JPF properties via properties at class load time
282 @JPFConfig ({"listener+=.tools.SharedChecker", ..})
283 public class TestNonShared implements Runnable {
284   ...
285 }
286 ~~~~~~~~
287
288 However, this is not recommended outside JPF tests - the application would run, but not compile without JPF.
289
290
291 ### Verify API (SuT) ###
292
293
294 A less often used method is to set listeners is to use the `gov.nasa.jpf.vm.Verify` API from within your application. With this, you can control the exact load time of the listener (but be aware of backtracking). With this, the above example would become
295
296 ~~~~~~~~ {.java}
297 import gov.nasa.jpf.vm.Verify;
298 ..
299 public class TestNonShared implements Runnable {
300   ...
301   public static void main (String[] args){
302     
303     // set JPF properties programmatically
304     Verify.setProperties("listener+=.tools.SharedChecker", ...);
305     ..
306   }
307 }
308 ~~~~~~~~
309
310 This method should only be used in special cases (models written explicitly for JPF verification), since it does not run outside JPF.
311
312
313 ### JPF API (embedded mode) ###
314
315
316 If JPF is explicitly started from within another application, listeners can be instantiated at will and configured via the `JPF.addListener(..)` API:
317
318 ~~~~~~~~ {.java}
319 MyListener listener=new MyListener(..);
320 ..
321 Config config = JPF.createConfig( args);
322 JPF jpf = new JPF( config);
323 jpf.addListener(listener);
324 jpf.run();
325 ..
326 ~~~~~~~~
327
328 Most listeners tend to fall into three major categories: 
329
330 1. system class (e.g. for logging) - is usually configured via the default.properties.
331 2. complex properties - is configured with an application specific mode property file.
332 3. JPF debugging - is specified via the command line (`+key=value` overrides).