1
2
3
4 package java.lang.jdynpur;
5
6 import java.io.BufferedOutputStream;
7 import java.io.DataOutputStream;
8 import java.io.FileOutputStream;
9 import java.io.IOException;
10 import java.util.HashSet;
11
12 import org.softevo.util.ObjectIdMapper;
13 import org.softevo.util.asm.FieldIdentifier;
14 import org.softevo.util.asm.MethodIdentifier;
15 import org.softevo.util.asm.MethodIdentifierMapGenerator;
16
17
18 /**
19 * Methods in this class are called by injected code to write trace events. <BR>
20 * This class continously writes a trace of events into file
21 * <code>trace.out</code>. If a suffix file (a file named
22 * <code>suffix_*</code> exists, then the suffix of the filename (e.g. file
23 * name suffix_0 -> suffix = 0 -> file name = trace.out_0) is appended to the
24 * name of the outputfile.
25 *
26 * @author dallmeier
27 */
28 public class Tracer {
29
30 public static MethodIdentifierMapGenerator methodIdentifierMap = null;
31
32 public static ObjectIdMapper<FieldIdentifier> fieldIdentfierMap = null;
33
34 private static HashSet<Integer> writtenMethodIdentifiers = new HashSet<Integer>(1000);
35
36 private static HashSet<Integer> writtenFieldIdentifiers = new HashSet<Integer>(5000);
37
38 /**
39 * The stream used to write events. <BR>
40 */
41 private static DataOutputStream dataOutputStream = null;
42
43 /**
44 * The size for the write buffer. <BR>
45 */
46 private static final int BUFFERSIZE = 16777216;
47
48 /**
49 * The base name for the output file.
50 */
51 public static final String OUTPUTFILENAME = "trace.out";
52
53 /**
54 * Constant for object creation event.
55 */
56 public static final int EV_OBJECTCREATION = 1;
57
58 /**
59 * Constant for field write event.
60 */
61 public static final int EV_FIELDWRITE = 2;
62
63 /**
64 * Constant for static field write event.
65 */
66 public static final int EV_STATICFIELDWRITE = 3;
67
68 /**
69 * Constant for start of dynamic method event.
70 */
71 public static final int EV_DYNAMICMETHODSTART = 4;
72
73 /**
74 * Constant for start of static method event.
75 */
76 public static final int EV_STATICMETHODSTART = 5;
77
78 /**
79 * Constant for start of dynamic method event.
80 */
81 public static final int EV_DYNAMICMETHODEND = 6;
82
83 /**
84 * Constant for end of static method end event.
85 */
86 public static final int EV_STATICMETHODEND = 7;
87
88 /**
89 * Constant for array creation event.
90 */
91 public static final int EV_ARRAYCREATED = 8;
92
93 /**
94 * Constant for array modification event.
95 */
96 public static final int EV_ARRAYMODIFIED = 9;
97
98 public static final int EV_METHODID= 10;
99
100 public static final int EV_PARAMETER = 11;
101
102 public static final int EV_FIELDID = 12;
103
104 public static final int EV_OBJECTFIELDWRITE = 13;
105
106 public static final int EV_OBJECTARRAYMODIFIED = 14;
107
108 public static final boolean traceParameterMutability = System.getProperty("jdynpur.traceparametermutability", "false").equalsIgnoreCase("true");
109
110 /**
111 * Constant indicating access to a static variable or execution of a static
112 * method.
113 */
114 public static final int STATIC = -1;
115
116 /**
117 * Constant for null value.
118 */
119 public static final int NULL = -1;
120
121
122 /**
123 * Mutex object that protects access to the <code>DataOutputStream</code>
124 */
125 private static Object mutex = null;
126
127
128
129 private static boolean debug = false;
130
131 /**
132 * Static initializer creates the output file and adds a shutdown hook such
133 * that the class is notified when the vm exits.
134 */
135 static {
136 String fileName;
137
138 mutex = new Object();
139 try {
140 fileName = System.getProperty("jdynpur.resultfilename", OUTPUTFILENAME);
141 dataOutputStream = new DataOutputStream(new BufferedOutputStream(
142 new FileOutputStream(fileName), BUFFERSIZE));
143 Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownThread()));
144 } catch (IOException exception) {
145 System.out.println("Unable to create trace outputStream.");
146 exception.printStackTrace(System.out);
147 System.exit(5);
148 }
149 }
150
151 /**
152 * This method is used to safely write events to the output stream. It is
153 * thread safe. If anything goes wrong writing to the stream, the vm is
154 * terminated with error code <code>5</code>.
155 *
156 * @param eventId
157 * the id of the event to be written
158 * @param value
159 * the value of the event
160 */
161 private static void writeEvent(int eventId, int value) {
162 try {
163 synchronized (mutex) {
164 if (dataOutputStream != null) {
165 dataOutputStream.writeInt(eventId);
166 dataOutputStream.writeInt(System.identityHashCode(Thread
167 .currentThread()));
168 dataOutputStream.writeInt(value);
169 }
170 }
171 } catch (IOException exception) {
172 System.out.println("An exception occured writing to the trace.");
173 exception.printStackTrace(System.out);
174 System.exit(5);
175 }
176 }
177
178 /**
179 * This method writes events that have 2 parameters and adds
180 * the class of the receiver object as another parameter.
181 *
182 * @param eventId
183 * the id of the event
184 * @param value1
185 * the first value
186 * @param value2
187 * the second value
188 */
189 private static void writeDynamicMethodEvent(int eventId, int value1, Object object) {
190 try {
191 synchronized (mutex) {
192 if (dataOutputStream != null) {
193 dataOutputStream.writeInt(eventId);
194 dataOutputStream.writeInt(System.identityHashCode(Thread
195 .currentThread()));
196 dataOutputStream.writeInt(value1);
197 dataOutputStream.writeInt(System.identityHashCode(object));
198 dataOutputStream.writeUTF(object.getClass().getName());
199 }
200 }
201 } catch (IOException exception) {
202 System.out.println("An exception occured writing to the trace.");
203 exception.printStackTrace(System.out);
204 System.exit(5);
205 }
206 }
207 /**
208 * This method writes events that have 2 parameters and adds
209 * the class of the receiver object as another parameter.
210 *
211 * @param eventId
212 * the id of the event
213 * @param value1
214 * the first value
215 * @param value2
216 * the second value
217 */
218 private static void writeParameterEvent(int index, Object parameter) {
219 try {
220 synchronized (mutex) {
221 if (dataOutputStream != null) {
222 dataOutputStream.writeInt(EV_PARAMETER);
223 dataOutputStream.writeInt(System.identityHashCode(Thread
224 .currentThread()));
225 dataOutputStream.writeInt(index);
226 if (parameter != null) {
227 dataOutputStream.writeInt(System.identityHashCode(parameter));
228 } else {
229 dataOutputStream.writeInt(System.identityHashCode(NULL));
230 }
231 }
232 }
233 } catch (IOException exception) {
234 System.out.println("An exception occured writing to the trace.");
235 exception.printStackTrace(System.out);
236 System.exit(5);
237 }
238 }
239
240 private static void writeMethodIdentifier(int methodId) {
241 try {
242 synchronized (mutex) {
243 if (dataOutputStream != null) {
244 if (!writtenMethodIdentifiers.contains(methodId)) {
245 MethodIdentifier identifier = methodIdentifierMap.getMethodIdentifier(methodId);
246 if (identifier != null) {
247 String methodName = identifier.toString();
248 dataOutputStream.writeInt(EV_METHODID);
249 dataOutputStream.writeInt(methodId);
250 dataOutputStream.writeInt(identifier.getAccess());
251 dataOutputStream.writeUTF(methodName);
252 writtenMethodIdentifiers.add(methodId);
253 } else {
254 System.out.println("Method identifier " + methodId + " not found in identifier map.");
255 System.out.println("Will now exit!");
256 System.out.flush();
257 System.exit(-1);
258 }
259 }
260 }
261 }
262 } catch (IOException exception) {
263 System.out.println("An exception occured writing to the trace.");
264 exception.printStackTrace(System.out);
265 System.exit(5);
266 }
267
268 }
269
270 private static void writeFieldIdentifier(int fieldId) {
271 try {
272 synchronized (mutex) {
273 if (dataOutputStream != null) {
274 if (!writtenFieldIdentifiers.contains(fieldId)) {
275 FieldIdentifier identifier = fieldIdentfierMap.getObject(fieldId);
276 if (identifier != null) {
277 String fieldName = identifier.toString();
278 dataOutputStream.writeInt(EV_FIELDID);
279 dataOutputStream.writeInt(fieldId);
280 dataOutputStream.writeUTF(fieldName);
281 writtenFieldIdentifiers.add(fieldId);
282 } else {
283 System.out.println("Field identifier " + fieldId + " not found in identifier map.");
284 System.out.println("Will now exit!");
285 System.out.flush();
286 System.exit(-1);
287 }
288 }
289 }
290 }
291 } catch (IOException exception) {
292 System.out.println("An exception occured writing to the trace.");
293 exception.printStackTrace(System.out);
294 System.exit(5);
295 }
296
297 }
298
299 public static void objectFieldWritten(Object receiver, Object value, int fieldId) {
300 writeFieldIdentifier(fieldId);
301 try {
302 synchronized (mutex) {
303 if (dataOutputStream != null) {
304 dataOutputStream.writeInt(EV_OBJECTFIELDWRITE);
305 dataOutputStream.writeInt(System.identityHashCode(Thread
306 .currentThread()));
307 dataOutputStream.writeInt(fieldId);
308 if (receiver != null) {
309 dataOutputStream.writeInt(System.identityHashCode(receiver));
310 } else {
311 dataOutputStream.writeInt(System.identityHashCode(NULL));
312 }
313 if (value != null) {
314 dataOutputStream.writeInt(System.identityHashCode(value));
315 } else {
316 dataOutputStream.writeInt(System.identityHashCode(NULL));
317 }
318 }
319 }
320 } catch (IOException exception) {
321 System.out.println("An exception occured writing to the trace.");
322 exception.printStackTrace(System.out);
323 System.exit(5);
324 }
325 }
326
327 /**
328 * Called whenever a field is written in an object.
329 *
330 * @param object
331 * the modified object
332 */
333 public static void fieldWritten(Object object) {
334 if (debug) {
335 System.out.println("Field written.");
336 }
337 writeEvent(EV_FIELDWRITE, System.identityHashCode(object));
338 }
339
340 /**
341 * Called whenever a new object is created. This method is called after the
342 * constructor call.
343 *
344 * @param object
345 * the object that is created
346 */
347 public static void objectCreated(Object object) {
348 if (debug) {
349 System.out.println("Object created.");
350 }
351 writeEvent(EV_OBJECTCREATION, System.identityHashCode(object));
352 }
353
354 /**
355 * Called whenever a dynamic method is started.
356 *
357 * @param methodId
358 * the id of the method that is started
359 * @param thisObject
360 * the object the method is invoked on
361 */
362 public static void dynamicMethodStarted(int methodId, Object thisObject) {
363 writeMethodIdentifier(methodId);
364 if (debug) {
365 System.out.println("Method started " + methodId);
366 }
367 writeDynamicMethodEvent(EV_DYNAMICMETHODSTART, methodId, thisObject);
368 }
369
370 public static void parameterPassed(int index, Object parameter) {
371 writeParameterEvent(index, parameter);
372 }
373
374 /**
375 * Called whenever a static method is started.
376 *
377 * @param methodId
378 * the id of the method that is started
379 */
380 public static void staticMethodStarted(int methodId) {
381 writeMethodIdentifier(methodId);
382 if (debug) {
383 System.out.println("Static method started " + methodId);
384 }
385 writeEvent(EV_STATICMETHODSTART, methodId);
386 }
387
388 /**
389 * Called whenever a dynamic method has just finished execution.
390 *
391 * @param methodId
392 * the id of the method
393 */
394 public static void dynamicMethodEnded(int methodId) {
395 if (debug) {
396 System.out.println("Dynamic method ended " + methodId);
397 }
398 writeEvent(EV_DYNAMICMETHODEND, methodId);
399 }
400
401 /**
402 * Called whenever a static method has just finished execution.
403 *
404 * @param methodId
405 * the id of the method
406 */
407 public static void staticMethodEnded(int methodId) {
408 if (debug) {
409 System.out.println("Static method ended " + methodId);
410 }
411 writeEvent(EV_STATICMETHODEND, methodId);
412 }
413
414 /**
415 * Called whenever a static field was written.
416 */
417 public static void staticFieldWritten() {
418 if (debug) {
419 System.out.println("Static field written.");
420 }
421 writeEvent(EV_STATICFIELDWRITE, STATIC);
422 }
423
424 /**
425 * Called whenever a new array was created.
426 *
427 * @param object
428 * the array created
429 */
430 public static void arrayCreated(Object object) {
431 if (debug) {
432 System.out.println("Array created.");
433 }
434 writeEvent(EV_ARRAYCREATED, System.identityHashCode(object));
435 }
436
437 /**
438 * Called whenever an entry in an array is modified.
439 *
440 * @param object
441 * the array that was modified
442 */
443 public static void arrayModified(Object object) {
444 if (debug) {
445 System.out.println("Array modified.");
446 }
447 writeEvent(EV_ARRAYMODIFIED, System.identityHashCode(object));
448 }
449
450 public static void objectArrayModified(Object array, int index, Object value) {
451 try {
452 synchronized (mutex) {
453 if (dataOutputStream != null) {
454 dataOutputStream.writeInt(EV_OBJECTARRAYMODIFIED);
455 dataOutputStream.writeInt(System.identityHashCode(Thread
456 .currentThread()));
457 if (array != null) {
458 dataOutputStream.writeInt(System.identityHashCode(array));
459 } else {
460 dataOutputStream.writeInt(System.identityHashCode(NULL));
461 }
462 dataOutputStream.writeInt(System.identityHashCode(index));
463 if (value != null) {
464 dataOutputStream.writeInt(System.identityHashCode(value));
465 } else {
466 dataOutputStream.writeInt(System.identityHashCode(NULL));
467 }
468 }
469 }
470 } catch (IOException exception) {
471 System.out.println("An exception occured writing to the trace.");
472 exception.printStackTrace(System.out);
473 System.exit(5);
474 }
475 }
476
477 /**
478 * This method is called to record creation of multi dimensional arrays. This
479 * is necessary because otherwise we don't know about the creation of all
480 * arrays.
481 *
482 * @param array
483 * the object array, possibly containing more object arrays
484 * @param depth
485 * the recursion depth
486 */
487 public static void addArraysRecursively(Object[] array, int depth) {
488 for (int counter = 0; counter < array.length; counter++) {
489 arrayCreated(array[counter]);
490 objectArrayModified(array, counter, array[counter]);
491 if (depth > 0) {
492 addArraysRecursively((Object[]) array[counter], depth - 1);
493 }
494 }
495 }
496
497 /**
498 * This class implements the shutdown hook needed to assure that all trace
499 * information gets written at vm shutdown time.
500 *
501 * @author dallmeier
502 */
503 private static class ShutdownThread implements Runnable {
504
505 /**
506 * Flushes the stream and closes it.
507 */
508 public void run() {
509
510 synchronized (mutex) {
511 try {
512 if (dataOutputStream != null) {
513 dataOutputStream.flush();
514 dataOutputStream.close();
515 dataOutputStream = null;
516 }
517 } catch (IOException finalizeException) {
518 System.out
519 .println("An exception occured finalizing trace output stream.");
520 finalizeException.printStackTrace(System.out);
521 }
522 }
523 }
524
525 }
526
527 }