1fb3fb4f3Stomee /*
2fb3fb4f3Stomee  * CDDL HEADER START
3fb3fb4f3Stomee  *
4fb3fb4f3Stomee  * The contents of this file are subject to the terms of the
5fb3fb4f3Stomee  * Common Development and Distribution License (the "License").
6fb3fb4f3Stomee  * You may not use this file except in compliance with the License.
7fb3fb4f3Stomee  *
8fb3fb4f3Stomee  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fb3fb4f3Stomee  * or http://www.opensolaris.org/os/licensing.
10fb3fb4f3Stomee  * See the License for the specific language governing permissions
11fb3fb4f3Stomee  * and limitations under the License.
12fb3fb4f3Stomee  *
13fb3fb4f3Stomee  * When distributing Covered Code, include this CDDL HEADER in each
14fb3fb4f3Stomee  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fb3fb4f3Stomee  * If applicable, add the following below this CDDL HEADER, with the
16fb3fb4f3Stomee  * fields enclosed by brackets "[]" replaced with your own identifying
17fb3fb4f3Stomee  * information: Portions Copyright [yyyy] [name of copyright owner]
18fb3fb4f3Stomee  *
19fb3fb4f3Stomee  * CDDL HEADER END
20fb3fb4f3Stomee  */
21fb3fb4f3Stomee 
22fb3fb4f3Stomee /*
23e77b06d2Stomee  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24fb3fb4f3Stomee  * Use is subject to license terms.
25fb3fb4f3Stomee  */
26fb3fb4f3Stomee package org.opensolaris.os.dtrace;
27fb3fb4f3Stomee 
28fb3fb4f3Stomee import java.util.*;
29fb3fb4f3Stomee import java.io.*;
30fb3fb4f3Stomee import java.beans.*;
31fb3fb4f3Stomee 
32fb3fb4f3Stomee /**
33fb3fb4f3Stomee  * Data generated when a DTrace probe fires, contains one record for
34fb3fb4f3Stomee  * every record-generating action in the probe.  (Some D actions, such
35fb3fb4f3Stomee  * as {@code clear()}, do not generate a {@code ProbeData} record.)  A
36fb3fb4f3Stomee  * {@link Consumer} gets data from DTrace by registering a {@link
37fb3fb4f3Stomee  * ConsumerListener listener} to get probe data whenever a probe fires:
38fb3fb4f3Stomee  * <pre><code>
39fb3fb4f3Stomee  *     Consumer consumer = new LocalConsumer();
40fb3fb4f3Stomee  *     consumer.addConsumerListener(new ConsumerAdapter() {
41fb3fb4f3Stomee  *         public void dataReceived(DataEvent e) {
42fb3fb4f3Stomee  *             ProbeData probeData = e.getProbeData();
43fb3fb4f3Stomee  *             System.out.println(probeData);
44fb3fb4f3Stomee  *         }
45fb3fb4f3Stomee  *     });
46fb3fb4f3Stomee  * </code></pre>
47fb3fb4f3Stomee  * Getting DTrace to generate that probe data involves compiling,
48fb3fb4f3Stomee  * enabling, and running a D program:
49fb3fb4f3Stomee  * <pre><code>
50fb3fb4f3Stomee  *     try {
51fb3fb4f3Stomee  *         consumer.open();
52fb3fb4f3Stomee  *         consumer.compile(program);
53fb3fb4f3Stomee  *         consumer.enable(); // instruments code at matching probe points
54fb3fb4f3Stomee  *         consumer.go(); // non-blocking; generates probe data in background
55fb3fb4f3Stomee  *     } catch (DTraceException e) {
56fb3fb4f3Stomee  *         e.printStackTrace();
57fb3fb4f3Stomee  *     }
58fb3fb4f3Stomee  * </code></pre>
59fb3fb4f3Stomee  * Currently the {@code ProbeData} instance does not record a timestamp.
60fb3fb4f3Stomee  * If you need a timestamp, trace the built-in {@code timestamp}
61fb3fb4f3Stomee  * variable in your D program.  (See the
62*3a931819SPeter Tribble  * <a href=http://dtrace.org/guide/chp-variables.html#chp-variables-5>
63fb3fb4f3Stomee  * <b>Built-in Variables</b></a> section of the <b>Variables</b> chapter of
64*3a931819SPeter Tribble  * the <i>Dynamic Tracing Guide</i>).
65fb3fb4f3Stomee  * <p>
66fb3fb4f3Stomee  * Immutable.  Supports persistence using {@link java.beans.XMLEncoder}.
67fb3fb4f3Stomee  *
68fb3fb4f3Stomee  * @see Consumer#addConsumerListener(ConsumerListener l)
69fb3fb4f3Stomee  * @see ConsumerListener#dataReceived(DataEvent e)
70fb3fb4f3Stomee  *
71fb3fb4f3Stomee  * @author Tom Erickson
72fb3fb4f3Stomee  */
73fb3fb4f3Stomee public final class ProbeData implements Serializable, Comparable <ProbeData> {
74fb3fb4f3Stomee     static final long serialVersionUID = -7021504416192099215L;
75fb3fb4f3Stomee 
76fb3fb4f3Stomee     static {
77fb3fb4f3Stomee 	try {
78fb3fb4f3Stomee 	    BeanInfo info = Introspector.getBeanInfo(ProbeData.class);
79fb3fb4f3Stomee 	    PersistenceDelegate persistenceDelegate =
80fb3fb4f3Stomee 		    new DefaultPersistenceDelegate(
81fb3fb4f3Stomee 		    new String[] {"enabledProbeID", "CPU",
82fb3fb4f3Stomee 		    "enabledProbeDescription", "flow", "records"});
83fb3fb4f3Stomee 	    BeanDescriptor d = info.getBeanDescriptor();
84fb3fb4f3Stomee 	    d.setValue("persistenceDelegate", persistenceDelegate);
85fb3fb4f3Stomee 	} catch (IntrospectionException e) {
86fb3fb4f3Stomee 	    System.out.println(e);
87fb3fb4f3Stomee 	}
88fb3fb4f3Stomee     }
89fb3fb4f3Stomee 
90fb3fb4f3Stomee     private static Comparator <ProbeData> DEFAULT_CMP;
91fb3fb4f3Stomee 
92fb3fb4f3Stomee     static {
93fb3fb4f3Stomee 	try {
94fb3fb4f3Stomee 	    DEFAULT_CMP = ProbeData.getComparator(KeyField.RECORDS,
95fb3fb4f3Stomee 		    KeyField.EPID);
96fb3fb4f3Stomee 	} catch (Throwable e) {
97fb3fb4f3Stomee 	    e.printStackTrace();
98fb3fb4f3Stomee 	    System.exit(1);
99fb3fb4f3Stomee 	}
100fb3fb4f3Stomee     }
101fb3fb4f3Stomee 
102fb3fb4f3Stomee     /** @serial */
103fb3fb4f3Stomee     private int epid;
104fb3fb4f3Stomee     /** @serial */
105fb3fb4f3Stomee     private int cpu;
106fb3fb4f3Stomee     /** @serial */
107fb3fb4f3Stomee     private ProbeDescription enabledProbeDescription;
108fb3fb4f3Stomee     /** @serial */
109fb3fb4f3Stomee     private Flow flow;
110fb3fb4f3Stomee     // Scratch data, one element per native probedata->dtpda_edesc->dtepd_nrecs
111fb3fb4f3Stomee     // element, cleared after records list is fully populated.
112127bbe13Stomee     private transient List <Record> nativeElements;
113fb3fb4f3Stomee     /** @serial */
114fb3fb4f3Stomee     private List <Record> records;
115fb3fb4f3Stomee 
116fb3fb4f3Stomee     /**
117fb3fb4f3Stomee      * Enumerates the fields by which {@link ProbeData} may be sorted
118fb3fb4f3Stomee      * using the {@link #getComparator(KeyField[] f) getComparator()}
119fb3fb4f3Stomee      * convenience method.
120fb3fb4f3Stomee      */
121fb3fb4f3Stomee     public enum KeyField {
122fb3fb4f3Stomee 	/** Specifies {@link ProbeData#getCPU()} */
123fb3fb4f3Stomee 	CPU,
124fb3fb4f3Stomee 	/** Specifies {@link ProbeData#getEnabledProbeDescription()} */
125fb3fb4f3Stomee 	PROBE,
126fb3fb4f3Stomee 	/** Specifies {@link ProbeData#getEnabledProbeID()} */
127fb3fb4f3Stomee 	EPID,
128fb3fb4f3Stomee 	/** Specifies {@link ProbeData#getRecords()} */
129fb3fb4f3Stomee 	RECORDS
130fb3fb4f3Stomee     }
131fb3fb4f3Stomee 
132fb3fb4f3Stomee     /**
133fb3fb4f3Stomee      * Called by native code.
134fb3fb4f3Stomee      */
135fb3fb4f3Stomee     private
ProbeData(int enabledProbeID, int cpuID, ProbeDescription p, Flow f, int nativeElementCount)136fb3fb4f3Stomee     ProbeData(int enabledProbeID, int cpuID, ProbeDescription p,
137fb3fb4f3Stomee 	    Flow f, int nativeElementCount)
138fb3fb4f3Stomee     {
139fb3fb4f3Stomee 	epid = enabledProbeID;
140fb3fb4f3Stomee 	cpu = cpuID;
141fb3fb4f3Stomee 	enabledProbeDescription = p;
142fb3fb4f3Stomee 	flow = f;
143127bbe13Stomee 	nativeElements = new ArrayList <Record> (nativeElementCount);
144fb3fb4f3Stomee 	records = new ArrayList <Record> ();
145fb3fb4f3Stomee 	validate();
146fb3fb4f3Stomee     }
147fb3fb4f3Stomee 
148fb3fb4f3Stomee     /**
149fb3fb4f3Stomee      * Creates a probe data instance with the given properties and list
150fb3fb4f3Stomee      * of records.  Supports XML persistence.
151fb3fb4f3Stomee      *
152fb3fb4f3Stomee      * @param enabledProbeID identifies the enabled probe that fired;
153fb3fb4f3Stomee      * the ID is generated by the native DTrace library to distinguish
154fb3fb4f3Stomee      * all probes enabled by the source consumer (as opposed to
155fb3fb4f3Stomee      * all probes on the system)
156fb3fb4f3Stomee      * @param cpuID non-negative ID, identifies the CPU on which the
157fb3fb4f3Stomee      * probe fired
158fb3fb4f3Stomee      * @param p identifies the enabled probe that fired
159fb3fb4f3Stomee      * @param f current state of control flow (entry or return and depth
160fb3fb4f3Stomee      * in call stack) at time of probe firing, included if {@link
161fb3fb4f3Stomee      * Option#flowindent flowindent} option used, {@code null} otherwise
162fb3fb4f3Stomee      * @param recordList list of records generated by D actions in the
163fb3fb4f3Stomee      * probe that fired, one record per action, may be empty
164fb3fb4f3Stomee      * @throws NullPointerException if the given probe description or
165fb3fb4f3Stomee      * list of records is {@code null}
166fb3fb4f3Stomee      */
167fb3fb4f3Stomee     public
ProbeData(int enabledProbeID, int cpuID, ProbeDescription p, Flow f, List <Record> recordList)168fb3fb4f3Stomee     ProbeData(int enabledProbeID, int cpuID, ProbeDescription p,
169fb3fb4f3Stomee 	    Flow f, List <Record> recordList)
170fb3fb4f3Stomee     {
171fb3fb4f3Stomee 	epid = enabledProbeID;
172fb3fb4f3Stomee 	cpu = cpuID;
173fb3fb4f3Stomee 	enabledProbeDescription = p;
174fb3fb4f3Stomee 	flow = f;
175fb3fb4f3Stomee 	records = new ArrayList <Record> (recordList.size());
176fb3fb4f3Stomee 	records.addAll(recordList);
177fb3fb4f3Stomee 	validate();
178fb3fb4f3Stomee     }
179fb3fb4f3Stomee 
18091cfa10aStomee     private final void
validate()181fb3fb4f3Stomee     validate()
182fb3fb4f3Stomee     {
183fb3fb4f3Stomee 	if (enabledProbeDescription == null) {
184fb3fb4f3Stomee 	    throw new NullPointerException(
185fb3fb4f3Stomee 		    "enabled probe description is null");
186fb3fb4f3Stomee 	}
187fb3fb4f3Stomee 	if (records == null) {
188fb3fb4f3Stomee 	    throw new NullPointerException("record list is null");
189fb3fb4f3Stomee 	}
190fb3fb4f3Stomee     }
191fb3fb4f3Stomee 
192fb3fb4f3Stomee     private void
addDataElement(Record o)193127bbe13Stomee     addDataElement(Record o)
194fb3fb4f3Stomee     {
195127bbe13Stomee 	// Early error detection if native code adds the wrong type
196127bbe13Stomee 	Record r = Record.class.cast(o);
197127bbe13Stomee 
198fb3fb4f3Stomee 	nativeElements.add(o);
199fb3fb4f3Stomee     }
200fb3fb4f3Stomee 
201fb3fb4f3Stomee     /**
202fb3fb4f3Stomee      * Called by native code.
203fb3fb4f3Stomee      */
204fb3fb4f3Stomee     private void
addRecord(Record record)205fb3fb4f3Stomee     addRecord(Record record)
206fb3fb4f3Stomee     {
207fb3fb4f3Stomee 	records.add(record);
208fb3fb4f3Stomee     }
209fb3fb4f3Stomee 
210fb3fb4f3Stomee     /**
211fb3fb4f3Stomee      * Called by native code.
212fb3fb4f3Stomee      */
213fb3fb4f3Stomee     private void
addTraceRecord(int i)214fb3fb4f3Stomee     addTraceRecord(int i)
215fb3fb4f3Stomee     {
216fb3fb4f3Stomee 	// trace() value is preceded by one null for every D program
217fb3fb4f3Stomee 	// statement preceding trace() that is not a D action, such as
218fb3fb4f3Stomee 	// assignment to a variable (results in a native probedata
219fb3fb4f3Stomee 	// record with no data).
220fb3fb4f3Stomee 	int len = nativeElements.size();
221127bbe13Stomee 	Record rec = null;
222127bbe13Stomee 	for (; ((rec = nativeElements.get(i)) == null) && (i < len); ++i);
223127bbe13Stomee 	records.add(rec);
224127bbe13Stomee     }
225127bbe13Stomee 
226127bbe13Stomee     /**
227127bbe13Stomee      * Called by native code.
228127bbe13Stomee      */
229127bbe13Stomee     private void
addSymbolRecord(int i, String lookupString)230127bbe13Stomee     addSymbolRecord(int i, String lookupString)
231127bbe13Stomee     {
232127bbe13Stomee 	int len = nativeElements.size();
233127bbe13Stomee 	Record rec = null;
234127bbe13Stomee 	for (; ((rec = nativeElements.get(i)) == null) && (i < len); ++i);
235127bbe13Stomee 	SymbolValueRecord symbol = SymbolValueRecord.class.cast(rec);
236127bbe13Stomee 	if (symbol instanceof KernelSymbolRecord) {
237127bbe13Stomee 	    KernelSymbolRecord.class.cast(symbol).setSymbol(lookupString);
238127bbe13Stomee 	} else if (symbol instanceof UserSymbolRecord) {
239127bbe13Stomee 	    UserSymbolRecord.class.cast(symbol).setSymbol(lookupString);
240127bbe13Stomee 	} else {
241127bbe13Stomee 	    throw new IllegalStateException("no symbol record at index " + i);
242127bbe13Stomee 	}
243127bbe13Stomee 	records.add(symbol);
244fb3fb4f3Stomee     }
245fb3fb4f3Stomee 
246fb3fb4f3Stomee     /**
247fb3fb4f3Stomee      * Called by native code.
248fb3fb4f3Stomee      */
249fb3fb4f3Stomee     private void
addStackRecord(int i, String framesString)250fb3fb4f3Stomee     addStackRecord(int i, String framesString)
251fb3fb4f3Stomee     {
252fb3fb4f3Stomee 	int len = nativeElements.size();
253127bbe13Stomee 	Record rec = null;
254127bbe13Stomee 	for (; ((rec = nativeElements.get(i)) == null) && (i < len); ++i);
255127bbe13Stomee 	StackValueRecord stack = StackValueRecord.class.cast(rec);
256fb3fb4f3Stomee 	StackFrame[] frames = KernelStackRecord.parse(framesString);
257fb3fb4f3Stomee 	if (stack instanceof KernelStackRecord) {
258127bbe13Stomee 	    KernelStackRecord.class.cast(stack).setStackFrames(frames);
259fb3fb4f3Stomee 	} else if (stack instanceof UserStackRecord) {
260127bbe13Stomee 	    UserStackRecord.class.cast(stack).setStackFrames(frames);
261fb3fb4f3Stomee 	} else {
262fb3fb4f3Stomee 	    throw new IllegalStateException("no stack record at index " + i);
263fb3fb4f3Stomee 	}
264fb3fb4f3Stomee 	records.add(stack);
265fb3fb4f3Stomee     }
266fb3fb4f3Stomee 
267fb3fb4f3Stomee     /**
268fb3fb4f3Stomee      * Called by native code.
269fb3fb4f3Stomee      */
270fb3fb4f3Stomee     private void
addPrintfRecord()271fb3fb4f3Stomee     addPrintfRecord()
272fb3fb4f3Stomee     {
273fb3fb4f3Stomee 	records.add(new PrintfRecord());
274fb3fb4f3Stomee     }
275fb3fb4f3Stomee 
276fb3fb4f3Stomee     /**
277fb3fb4f3Stomee      * Called by native code.
278fb3fb4f3Stomee      */
279fb3fb4f3Stomee     private void
addPrintaRecord(long snaptimeNanos, boolean isFormatString)280fb3fb4f3Stomee     addPrintaRecord(long snaptimeNanos, boolean isFormatString)
281fb3fb4f3Stomee     {
282fb3fb4f3Stomee 	records.add(new PrintaRecord(snaptimeNanos, isFormatString));
283fb3fb4f3Stomee     }
284fb3fb4f3Stomee 
285fb3fb4f3Stomee     private PrintaRecord
getLastPrinta()286fb3fb4f3Stomee     getLastPrinta()
287fb3fb4f3Stomee     {
288fb3fb4f3Stomee 	ListIterator <Record> itr = records.listIterator(records.size());
289fb3fb4f3Stomee 	PrintaRecord printa = null;
290fb3fb4f3Stomee 	Record record;
291fb3fb4f3Stomee 	while (itr.hasPrevious() && (printa == null)) {
292fb3fb4f3Stomee 	    record = itr.previous();
293fb3fb4f3Stomee 	    if (record instanceof PrintaRecord) {
294127bbe13Stomee 		printa = PrintaRecord.class.cast(record);
295fb3fb4f3Stomee 	    }
296fb3fb4f3Stomee 	}
297fb3fb4f3Stomee 	return printa;
298fb3fb4f3Stomee     }
299fb3fb4f3Stomee 
300fb3fb4f3Stomee     /**
301fb3fb4f3Stomee      * Called by native code.
302fb3fb4f3Stomee      */
303fb3fb4f3Stomee     private void
addAggregationRecord(String aggregationName, long aggid, AggregationRecord rec)304fb3fb4f3Stomee     addAggregationRecord(String aggregationName, long aggid,
305fb3fb4f3Stomee 	    AggregationRecord rec)
306fb3fb4f3Stomee     {
307fb3fb4f3Stomee 	PrintaRecord printa = getLastPrinta();
308fb3fb4f3Stomee 	if (printa == null) {
309fb3fb4f3Stomee 	    throw new IllegalStateException(
310fb3fb4f3Stomee 		    "No PrintaRecord in this ProbeData");
311fb3fb4f3Stomee 	}
312fb3fb4f3Stomee 	printa.addRecord(aggregationName, aggid, rec);
313fb3fb4f3Stomee     }
314fb3fb4f3Stomee 
315fb3fb4f3Stomee     /**
316fb3fb4f3Stomee      * Called by native code.
317fb3fb4f3Stomee      */
318fb3fb4f3Stomee     private void
invalidatePrintaRecord()319fb3fb4f3Stomee     invalidatePrintaRecord()
320fb3fb4f3Stomee     {
321fb3fb4f3Stomee 	PrintaRecord printa = getLastPrinta();
322fb3fb4f3Stomee 	if (printa == null) {
323fb3fb4f3Stomee 	    throw new IllegalStateException(
324fb3fb4f3Stomee 		    "No PrintaRecord in this ProbeData");
325fb3fb4f3Stomee 	}
326fb3fb4f3Stomee 	printa.invalidate();
327fb3fb4f3Stomee     }
328fb3fb4f3Stomee 
329fb3fb4f3Stomee     /**
330fb3fb4f3Stomee      * Called by native code.
331fb3fb4f3Stomee      */
332fb3fb4f3Stomee     private void
addPrintaFormattedString(Tuple tuple, String s)333fb3fb4f3Stomee     addPrintaFormattedString(Tuple tuple, String s)
334fb3fb4f3Stomee     {
335fb3fb4f3Stomee 	PrintaRecord printa = getLastPrinta();
336fb3fb4f3Stomee 	if (printa == null) {
337fb3fb4f3Stomee 	    throw new IllegalStateException(
338fb3fb4f3Stomee 		    "No PrintaRecord in this ProbeData");
339fb3fb4f3Stomee 	}
340fb3fb4f3Stomee 	printa.addFormattedString(tuple, s);
341fb3fb4f3Stomee     }
342fb3fb4f3Stomee 
343fb3fb4f3Stomee     /**
344fb3fb4f3Stomee      * Called by native code.
345fb3fb4f3Stomee      */
346fb3fb4f3Stomee     private void
addExitRecord(int i)347fb3fb4f3Stomee     addExitRecord(int i)
348fb3fb4f3Stomee     {
349fb3fb4f3Stomee 	int len = nativeElements.size();
350127bbe13Stomee 	Record rec = null;
351127bbe13Stomee 	for (; ((rec = nativeElements.get(i)) == null) && (i < len); ++i);
352127bbe13Stomee 	ScalarRecord scalar = ScalarRecord.class.cast(rec);
353127bbe13Stomee 	Integer exitStatus = Integer.class.cast(scalar.getValue());
354fb3fb4f3Stomee 	records.add(new ExitRecord(exitStatus));
355fb3fb4f3Stomee     }
356fb3fb4f3Stomee 
357fb3fb4f3Stomee     /**
358fb3fb4f3Stomee      * Called by native code.  Attaches native probedata elements cached
359fb3fb4f3Stomee      * between the given first index and last index inclusive to the most
360fb3fb4f3Stomee      * recently added record if applicable.
361fb3fb4f3Stomee      */
362fb3fb4f3Stomee     private void
attachRecordElements(int first, int last)363fb3fb4f3Stomee     attachRecordElements(int first, int last)
364fb3fb4f3Stomee     {
365fb3fb4f3Stomee 	Record record = records.get(records.size() - 1);
366fb3fb4f3Stomee 	if (record instanceof PrintfRecord) {
367127bbe13Stomee 	    PrintfRecord printf = PrintfRecord.class.cast(record);
368127bbe13Stomee 	    Record e;
369fb3fb4f3Stomee 	    for (int i = first; i <= last; ++i) {
370fb3fb4f3Stomee 		e = nativeElements.get(i);
371fb3fb4f3Stomee 		if (e == null) {
372fb3fb4f3Stomee 		    // printf() unformatted elements are preceded by one
373fb3fb4f3Stomee 		    // null for every D program statement preceding the
374fb3fb4f3Stomee 		    // printf() that is not a D action, such as
375fb3fb4f3Stomee 		    // assignment to a variable (generates a probedata
376fb3fb4f3Stomee 		    // record with no data).
377fb3fb4f3Stomee 		    continue;
378fb3fb4f3Stomee 		}
379127bbe13Stomee 		printf.addUnformattedElement(ScalarRecord.class.cast(e));
380fb3fb4f3Stomee 	    }
381fb3fb4f3Stomee 	}
382fb3fb4f3Stomee     }
383fb3fb4f3Stomee 
384fb3fb4f3Stomee     /**
385fb3fb4f3Stomee      * Called by native code.
386fb3fb4f3Stomee      */
387fb3fb4f3Stomee     void
clearNativeElements()388fb3fb4f3Stomee     clearNativeElements()
389fb3fb4f3Stomee     {
390fb3fb4f3Stomee 	nativeElements = null;
391fb3fb4f3Stomee     }
392fb3fb4f3Stomee 
393fb3fb4f3Stomee     /**
394fb3fb4f3Stomee      * Called by native code.
395fb3fb4f3Stomee      */
396fb3fb4f3Stomee     private void
setFormattedString(String s)397fb3fb4f3Stomee     setFormattedString(String s)
398fb3fb4f3Stomee     {
399fb3fb4f3Stomee 	Record record = records.get(records.size() - 1);
400fb3fb4f3Stomee 	if (record instanceof PrintfRecord) {
401127bbe13Stomee 	    PrintfRecord printf = PrintfRecord.class.cast(record);
402fb3fb4f3Stomee 	    printf.setFormattedString(s);
403fb3fb4f3Stomee 	}
404fb3fb4f3Stomee     }
405fb3fb4f3Stomee 
406fb3fb4f3Stomee     /**
407fb3fb4f3Stomee      * Convenience method, gets a comparator that sorts multiple {@link
408fb3fb4f3Stomee      * ProbeDescription} instances by the specified field or fields.  If
409fb3fb4f3Stomee      * more than one sort field is specified, the probe data are sorted
410fb3fb4f3Stomee      * by the first field, and in case of a tie, by the second field,
411fb3fb4f3Stomee      * and so on, in the order that the fields are specified.
412fb3fb4f3Stomee      *
413fb3fb4f3Stomee      * @param f field specifiers given in descending order of sort
414fb3fb4f3Stomee      * priority; lower priority fields are only compared (as a tie
415fb3fb4f3Stomee      * breaker) when all higher priority fields are equal
416fb3fb4f3Stomee      * @return non-null probe data comparator that sorts by the
417fb3fb4f3Stomee      * specified sort fields in the given order
418fb3fb4f3Stomee      */
419fb3fb4f3Stomee     public static Comparator <ProbeData>
getComparator(KeyField .... f)420fb3fb4f3Stomee     getComparator(KeyField ... f)
421fb3fb4f3Stomee     {
422fb3fb4f3Stomee 	return new Cmp(f);
423fb3fb4f3Stomee     }
424fb3fb4f3Stomee 
425fb3fb4f3Stomee     private static class Cmp implements Comparator <ProbeData> {
426fb3fb4f3Stomee 	private KeyField[] sortFields;
427fb3fb4f3Stomee 
428fb3fb4f3Stomee 	private
Cmp(KeyField .... f)429fb3fb4f3Stomee 	Cmp(KeyField ... f)
430fb3fb4f3Stomee 	{
431fb3fb4f3Stomee 	    sortFields = f;
432fb3fb4f3Stomee 	}
433fb3fb4f3Stomee 
434fb3fb4f3Stomee 	public int
compare(ProbeData d1, ProbeData d2)435fb3fb4f3Stomee 	compare(ProbeData d1, ProbeData d2)
436fb3fb4f3Stomee 	{
437fb3fb4f3Stomee 	    return ProbeData.compare(d1, d2, sortFields);
438fb3fb4f3Stomee 	}
439fb3fb4f3Stomee     }
440fb3fb4f3Stomee 
441127bbe13Stomee     static int
compareUnsigned(int i1, int i2)442127bbe13Stomee     compareUnsigned(int i1, int i2)
443127bbe13Stomee     {
444127bbe13Stomee 	int cmp;
445127bbe13Stomee 
446127bbe13Stomee 	if (i1 < 0) {
447127bbe13Stomee 	    if (i2 < 0) {
448127bbe13Stomee 		cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0));
449127bbe13Stomee 	    } else {
450127bbe13Stomee 		cmp = 1; // negative > positive
451127bbe13Stomee 	    }
452127bbe13Stomee 	} else if (i2 < 0) {
453127bbe13Stomee 	    cmp = -1; // positive < negative
454127bbe13Stomee 	} else {
455127bbe13Stomee 	    cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0));
456127bbe13Stomee 	}
457127bbe13Stomee 
458127bbe13Stomee 	return cmp;
459127bbe13Stomee     }
460127bbe13Stomee 
461127bbe13Stomee     static int
compareUnsigned(long i1, long i2)462127bbe13Stomee     compareUnsigned(long i1, long i2)
463127bbe13Stomee     {
464127bbe13Stomee 	int cmp;
465127bbe13Stomee 
466127bbe13Stomee 	if (i1 < 0) {
467127bbe13Stomee 	    if (i2 < 0) {
468127bbe13Stomee 		cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0));
469127bbe13Stomee 	    } else {
470127bbe13Stomee 		cmp = 1; // negative > positive
471127bbe13Stomee 	    }
472127bbe13Stomee 	} else if (i2 < 0) {
473127bbe13Stomee 	    cmp = -1; // positive < negative
474127bbe13Stomee 	} else {
475127bbe13Stomee 	    cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0));
476127bbe13Stomee 	}
477127bbe13Stomee 
478127bbe13Stomee 	return cmp;
479127bbe13Stomee     }
480127bbe13Stomee 
481127bbe13Stomee     static int
compareUnsigned(byte i1, byte i2)482127bbe13Stomee     compareUnsigned(byte i1, byte i2)
483127bbe13Stomee     {
484127bbe13Stomee 	int cmp;
485127bbe13Stomee 
486127bbe13Stomee 	if (i1 < 0) {
487127bbe13Stomee 	    if (i2 < 0) {
488127bbe13Stomee 		cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0));
489127bbe13Stomee 	    } else {
490127bbe13Stomee 		cmp = 1; // negative > positive
491127bbe13Stomee 	    }
492127bbe13Stomee 	} else if (i2 < 0) {
493127bbe13Stomee 	    cmp = -1; // positive < negative
494127bbe13Stomee 	} else {
495127bbe13Stomee 	    cmp = (i1 < i2 ? -1 : (i1 > i2 ? 1 : 0));
496127bbe13Stomee 	}
497127bbe13Stomee 
498127bbe13Stomee 	return cmp;
499127bbe13Stomee     }
500127bbe13Stomee 
501127bbe13Stomee     static int
compareByteArrays(byte[] a1, byte[] a2)502127bbe13Stomee     compareByteArrays(byte[] a1, byte[] a2)
503127bbe13Stomee     {
504127bbe13Stomee 	int cmp = 0;
505127bbe13Stomee 	int len1 = a1.length;
506127bbe13Stomee 	int len2 = a2.length;
507127bbe13Stomee 
508127bbe13Stomee 	for (int i = 0; (cmp == 0) && (i < len1) && (i < len2); ++i) {
509127bbe13Stomee 	    cmp = compareUnsigned(a1[i], a2[i]);
510127bbe13Stomee 	}
511127bbe13Stomee 
512127bbe13Stomee 	if (cmp == 0) {
513127bbe13Stomee 	    cmp = (len1 < len2 ? -1 : (len1 > len2 ? 1 : 0));
514127bbe13Stomee 	}
515127bbe13Stomee 
516127bbe13Stomee 	return cmp;
517127bbe13Stomee     }
518127bbe13Stomee 
519127bbe13Stomee     @SuppressWarnings("unchecked")
520127bbe13Stomee     static int
compareUnsigned(Comparable v1, Comparable v2)521127bbe13Stomee     compareUnsigned(Comparable v1, Comparable v2)
522127bbe13Stomee     {
523127bbe13Stomee 	int cmp;
524127bbe13Stomee 
525127bbe13Stomee 	if (v1 instanceof Integer) {
526127bbe13Stomee 	    int i1 = Integer.class.cast(v1);
527127bbe13Stomee 	    int i2 = Integer.class.cast(v2);
528127bbe13Stomee 	    cmp = compareUnsigned(i1, i2);
529127bbe13Stomee 	} else if (v1 instanceof Long) {
530127bbe13Stomee 	    long i1 = Long.class.cast(v1);
531127bbe13Stomee 	    long i2 = Long.class.cast(v2);
532127bbe13Stomee 	    cmp = compareUnsigned(i1, i2);
533127bbe13Stomee 	} else {
534127bbe13Stomee 	    cmp = v1.compareTo(v2);
535127bbe13Stomee 	}
536127bbe13Stomee 
537127bbe13Stomee 	return cmp;
538127bbe13Stomee     }
539127bbe13Stomee 
540fb3fb4f3Stomee     /**
541127bbe13Stomee      * @throws ClassCastException if records or their data are not
542fb3fb4f3Stomee      * mutually comparable
543fb3fb4f3Stomee      */
544fb3fb4f3Stomee     @SuppressWarnings("unchecked")
545fb3fb4f3Stomee     private static int
compareRecords(Record r1, Record r2)546fb3fb4f3Stomee     compareRecords(Record r1, Record r2)
547fb3fb4f3Stomee     {
548fb3fb4f3Stomee 	int cmp;
549fb3fb4f3Stomee 	if (r1 instanceof ScalarRecord) {
550fb3fb4f3Stomee 	    ScalarRecord t1 = ScalarRecord.class.cast(r1);
551fb3fb4f3Stomee 	    ScalarRecord t2 = ScalarRecord.class.cast(r2);
552127bbe13Stomee 	    Object o1 = t1.getValue();
553127bbe13Stomee 	    Object o2 = t2.getValue();
554127bbe13Stomee 	    if (o1 instanceof byte[]) {
555127bbe13Stomee 		byte[] a1 = byte[].class.cast(o1);
556127bbe13Stomee 		byte[] a2 = byte[].class.cast(o2);
557127bbe13Stomee 		cmp = compareByteArrays(a1, a2);
558127bbe13Stomee 	    } else {
559127bbe13Stomee 		Comparable v1 = Comparable.class.cast(o1);
560127bbe13Stomee 		Comparable v2 = Comparable.class.cast(o2);
561127bbe13Stomee 		cmp = v1.compareTo(v2); // compare signed values
562127bbe13Stomee 	    }
563127bbe13Stomee 	} else if (r1 instanceof Comparable) {
564127bbe13Stomee 	    // StackValueRecord, SymbolValueRecord
565127bbe13Stomee 	    Comparable v1 = Comparable.class.cast(r1);
566127bbe13Stomee 	    Comparable v2 = Comparable.class.cast(r2);
567fb3fb4f3Stomee 	    cmp = v1.compareTo(v2);
568fb3fb4f3Stomee 	} else if (r1 instanceof ExitRecord) {
569fb3fb4f3Stomee 	    ExitRecord e1 = ExitRecord.class.cast(r1);
570fb3fb4f3Stomee 	    ExitRecord e2 = ExitRecord.class.cast(r2);
571fb3fb4f3Stomee 	    int status1 = e1.getStatus();
572fb3fb4f3Stomee 	    int status2 = e2.getStatus();
573fb3fb4f3Stomee 	    cmp = (status1 < status2 ? -1 : (status1 > status2 ? 1 : 0));
574fb3fb4f3Stomee 	} else {
575127bbe13Stomee 	    // PrintfRecord, PrintaRecord
576127bbe13Stomee 	    r1.getClass().cast(r2);
577127bbe13Stomee 	    String s1 = r1.toString();
578127bbe13Stomee 	    String s2 = r2.toString();
579127bbe13Stomee 	    cmp = s1.compareTo(s2);
580fb3fb4f3Stomee 	}
581fb3fb4f3Stomee 
582fb3fb4f3Stomee 	return cmp;
583fb3fb4f3Stomee     }
584fb3fb4f3Stomee 
585fb3fb4f3Stomee     /**
586fb3fb4f3Stomee      * @throws ClassCastException if lists are not mutually comparable
587fb3fb4f3Stomee      * because corresponding list elements are not comparable or the
588fb3fb4f3Stomee      * list themselves are different lengths
589fb3fb4f3Stomee      */
590fb3fb4f3Stomee     private static int
compareRecordLists(ProbeData d1, ProbeData d2)591fb3fb4f3Stomee     compareRecordLists(ProbeData d1, ProbeData d2)
592fb3fb4f3Stomee     {
593fb3fb4f3Stomee 	List <Record> list1 = d1.getRecords();
594fb3fb4f3Stomee 	List <Record> list2 = d2.getRecords();
595fb3fb4f3Stomee 	int len1 = list1.size();
596fb3fb4f3Stomee 	int len2 = list2.size();
597fb3fb4f3Stomee 	if (len1 != len2) {
598fb3fb4f3Stomee 	    throw new ClassCastException("Record lists of different " +
599fb3fb4f3Stomee 		    "length are not comparable (lengths are " +
600fb3fb4f3Stomee 		    len1 + " and " + len2 + ").");
601fb3fb4f3Stomee 	}
602fb3fb4f3Stomee 
603fb3fb4f3Stomee 	int cmp;
604fb3fb4f3Stomee 	Record r1;
605fb3fb4f3Stomee 	Record r2;
606fb3fb4f3Stomee 
607fb3fb4f3Stomee 	for (int i = 0; (i < len1) && (i < len2); ++i) {
608fb3fb4f3Stomee 	    r1 = list1.get(i);
609fb3fb4f3Stomee 	    r2 = list2.get(i);
610fb3fb4f3Stomee 
611fb3fb4f3Stomee 	    cmp = compareRecords(r1, r2);
612fb3fb4f3Stomee 	    if (cmp != 0) {
613fb3fb4f3Stomee 		return cmp;
614fb3fb4f3Stomee 	    }
615fb3fb4f3Stomee 	}
616fb3fb4f3Stomee 
617fb3fb4f3Stomee 	return 0;
618fb3fb4f3Stomee     }
619fb3fb4f3Stomee 
620fb3fb4f3Stomee     private static int
compare(ProbeData d1, ProbeData d2, KeyField[] comparedFields)621fb3fb4f3Stomee     compare(ProbeData d1, ProbeData d2, KeyField[] comparedFields)
622fb3fb4f3Stomee     {
623fb3fb4f3Stomee 	int cmp;
624fb3fb4f3Stomee 	for (KeyField f : comparedFields) {
625fb3fb4f3Stomee 	    switch (f) {
626fb3fb4f3Stomee 		case CPU:
627fb3fb4f3Stomee 		    int cpu1 = d1.getCPU();
628fb3fb4f3Stomee 		    int cpu2 = d2.getCPU();
629fb3fb4f3Stomee 		    cmp = (cpu1 < cpu2 ? -1 : (cpu1 > cpu2 ? 1 : 0));
630fb3fb4f3Stomee 		    break;
631fb3fb4f3Stomee 		case PROBE:
632fb3fb4f3Stomee 		    ProbeDescription p1 = d1.getEnabledProbeDescription();
633fb3fb4f3Stomee 		    ProbeDescription p2 = d2.getEnabledProbeDescription();
634fb3fb4f3Stomee 		    cmp = p1.compareTo(p2);
635fb3fb4f3Stomee 		    break;
636fb3fb4f3Stomee 		case EPID:
637fb3fb4f3Stomee 		    int epid1 = d1.getEnabledProbeID();
638fb3fb4f3Stomee 		    int epid2 = d2.getEnabledProbeID();
639fb3fb4f3Stomee 		    cmp = (epid1 < epid2 ? -1 : (epid1 > epid2 ? 1 : 0));
640fb3fb4f3Stomee 		    break;
641fb3fb4f3Stomee 		case RECORDS:
642fb3fb4f3Stomee 		    cmp = compareRecordLists(d1, d2);
643fb3fb4f3Stomee 		    break;
644fb3fb4f3Stomee 		default:
645fb3fb4f3Stomee 		    throw new IllegalArgumentException(
646fb3fb4f3Stomee 			    "Unexpected sort field " + f);
647fb3fb4f3Stomee 	    }
648fb3fb4f3Stomee 
649fb3fb4f3Stomee 	    if (cmp != 0) {
650fb3fb4f3Stomee 		return cmp;
651fb3fb4f3Stomee 	    }
652fb3fb4f3Stomee 	}
653fb3fb4f3Stomee 
654fb3fb4f3Stomee 	return 0;
655fb3fb4f3Stomee     }
656fb3fb4f3Stomee 
657fb3fb4f3Stomee     /**
658fb3fb4f3Stomee      * Gets the enabled probe ID.  Identifies the enabled probe that
659fb3fb4f3Stomee      * fired and generated this {@code ProbeData}.  The "epid" is
660fb3fb4f3Stomee      * different from {@link ProbeDescription#getID()} in that it
661fb3fb4f3Stomee      * identifies a probe among all probes enabled by the source {@link
662fb3fb4f3Stomee      * Consumer}, rather than among all the probes on the system.
663fb3fb4f3Stomee      *
664fb3fb4f3Stomee      * @return the enabled probe ID generated by the native DTrace
665fb3fb4f3Stomee      * library
666fb3fb4f3Stomee      */
667fb3fb4f3Stomee     public int
getEnabledProbeID()668fb3fb4f3Stomee     getEnabledProbeID()
669fb3fb4f3Stomee     {
670fb3fb4f3Stomee 	return epid;
671fb3fb4f3Stomee     }
672fb3fb4f3Stomee 
673fb3fb4f3Stomee     /**
674fb3fb4f3Stomee      * Gets the ID of the CPU on which the probe fired.
675fb3fb4f3Stomee      *
676fb3fb4f3Stomee      * @return ID of the CPU on which the probe fired
677fb3fb4f3Stomee      */
678fb3fb4f3Stomee     public int
getCPU()679fb3fb4f3Stomee     getCPU()
680fb3fb4f3Stomee     {
681fb3fb4f3Stomee 	return cpu;
682fb3fb4f3Stomee     }
683fb3fb4f3Stomee 
684fb3fb4f3Stomee     /**
685fb3fb4f3Stomee      * Gets the enabled probe description.  Identifies the enabled probe
686fb3fb4f3Stomee      * that fired and generated this {@code ProbeData}.
687fb3fb4f3Stomee      *
688fb3fb4f3Stomee      * @return non-null probe description
689fb3fb4f3Stomee      */
690fb3fb4f3Stomee     public ProbeDescription
getEnabledProbeDescription()691fb3fb4f3Stomee     getEnabledProbeDescription()
692fb3fb4f3Stomee     {
693fb3fb4f3Stomee 	return enabledProbeDescription;
694fb3fb4f3Stomee     }
695fb3fb4f3Stomee 
696fb3fb4f3Stomee     /**
697fb3fb4f3Stomee      * Gets the current state of control flow (function entry or return,
698fb3fb4f3Stomee      * and depth in call stack) at the time of the probe firing that
699fb3fb4f3Stomee      * generated this {@code ProbeData} instance, or {@code null} if
700fb3fb4f3Stomee      * such information was not requested with the {@code flowindent}
701fb3fb4f3Stomee      * option.
702fb3fb4f3Stomee      *
703fb3fb4f3Stomee      * @return a description of control flow across function boundaries,
704fb3fb4f3Stomee      * or {@code null} if {@code Consumer.getOption(Option.flowindent)}
705fb3fb4f3Stomee      * returns {@link Option#UNSET}
706fb3fb4f3Stomee      * @see Consumer#setOption(String option)
707fb3fb4f3Stomee      * @see Option#flowindent
708fb3fb4f3Stomee      */
709fb3fb4f3Stomee     public Flow
getFlow()710fb3fb4f3Stomee     getFlow()
711fb3fb4f3Stomee     {
712fb3fb4f3Stomee 	return flow;
713fb3fb4f3Stomee     }
714fb3fb4f3Stomee 
715fb3fb4f3Stomee     /**
716fb3fb4f3Stomee      * Gets the records generated by the actions of the probe that
717fb3fb4f3Stomee      * fired, in the same order as the actions that generated the
718fb3fb4f3Stomee      * records.  The returned list includes one record for every
719fb3fb4f3Stomee      * record-generating D action (some D actions, such as {@code
720fb3fb4f3Stomee      * clear()}, do not generate records).
721fb3fb4f3Stomee      *
722fb3fb4f3Stomee      * @return non-null, unmodifiable list view of the records belonging
723fb3fb4f3Stomee      * to this {@code ProbeData} in the order of the actions in the
724fb3fb4f3Stomee      * DTrace probe that generated them (record-producing actions are
725fb3fb4f3Stomee      * generally those that produce output, such as {@code printf()},
726fb3fb4f3Stomee      * but also the {@code exit()} action)
727fb3fb4f3Stomee      */
728fb3fb4f3Stomee     public List <Record>
getRecords()729fb3fb4f3Stomee     getRecords()
730fb3fb4f3Stomee     {
731e77b06d2Stomee 	return Collections. <Record> unmodifiableList(records);
732fb3fb4f3Stomee     }
733fb3fb4f3Stomee 
734fb3fb4f3Stomee     /**
735fb3fb4f3Stomee      * Natural ordering of probe data.  Sorts probe data by records
736fb3fb4f3Stomee      * first, then if record data is equal, by enabled probe ID.
737fb3fb4f3Stomee      *
738fb3fb4f3Stomee      * @param d probe data to be compared with this probe data
739fb3fb4f3Stomee      * @return a negative number, zero, or a positive number as this
740fb3fb4f3Stomee      * probe data is less than, equal to, or greater than the given
741fb3fb4f3Stomee      * probe data
742fb3fb4f3Stomee      * @see ProbeData#getComparator(KeyField[] f)
743fb3fb4f3Stomee      * @throws NullPointerException if the given probe data is
744fb3fb4f3Stomee      * {@code null}
745fb3fb4f3Stomee      * @throws ClassCastException if record lists of both {@code
746fb3fb4f3Stomee      * ProbeData} instances are not mutually comparable because
747fb3fb4f3Stomee      * corresponding list elements are not comparable or the lists
748fb3fb4f3Stomee      * themselves are different lengths
749fb3fb4f3Stomee      */
750fb3fb4f3Stomee     public int
compareTo(ProbeData d)751fb3fb4f3Stomee     compareTo(ProbeData d)
752fb3fb4f3Stomee     {
753fb3fb4f3Stomee 	return DEFAULT_CMP.compare(this, d);
754fb3fb4f3Stomee     }
755fb3fb4f3Stomee 
756fb3fb4f3Stomee     private void
readObject(ObjectInputStream s)757fb3fb4f3Stomee     readObject(ObjectInputStream s)
758fb3fb4f3Stomee             throws IOException, ClassNotFoundException
759fb3fb4f3Stomee     {
760fb3fb4f3Stomee 	s.defaultReadObject();
761fb3fb4f3Stomee 	// Defensively copy record list _before_ validating.
762fb3fb4f3Stomee 	int len = records.size();
763fb3fb4f3Stomee 	ArrayList <Record> copy = new ArrayList <Record> (len);
764fb3fb4f3Stomee 	copy.addAll(records);
765fb3fb4f3Stomee 	records = copy;
766fb3fb4f3Stomee 	// Check class invariants
767fb3fb4f3Stomee 	try {
768fb3fb4f3Stomee 	    validate();
769fb3fb4f3Stomee 	} catch (Exception e) {
7704ae67516Stomee 	    InvalidObjectException x = new InvalidObjectException(
7714ae67516Stomee 		    e.getMessage());
7724ae67516Stomee 	    x.initCause(e);
7734ae67516Stomee 	    throw x;
774fb3fb4f3Stomee 	}
775fb3fb4f3Stomee     }
776fb3fb4f3Stomee 
777fb3fb4f3Stomee     /**
778fb3fb4f3Stomee      * Gets a string representation of this {@code ProbeData} instance
779fb3fb4f3Stomee      * useful for logging and not intended for display.  The exact
780fb3fb4f3Stomee      * details of the representation are unspecified and subject to
781fb3fb4f3Stomee      * change, but the following format may be regarded as typical:
782fb3fb4f3Stomee      * <pre><code>
783fb3fb4f3Stomee      * class-name[property1 = value1, property2 = value2]
784fb3fb4f3Stomee      * </code></pre>
785fb3fb4f3Stomee      */
786fb3fb4f3Stomee     public String
toString()787fb3fb4f3Stomee     toString()
788fb3fb4f3Stomee     {
7894ae67516Stomee 	StringBuilder buf = new StringBuilder();
790fb3fb4f3Stomee 	buf.append(ProbeData.class.getName());
791fb3fb4f3Stomee 	buf.append("[epid = ");
792fb3fb4f3Stomee 	buf.append(epid);
793fb3fb4f3Stomee 	buf.append(", cpu = ");
794fb3fb4f3Stomee 	buf.append(cpu);
795fb3fb4f3Stomee 	buf.append(", enabledProbeDescription = ");
796fb3fb4f3Stomee 	buf.append(enabledProbeDescription);
797fb3fb4f3Stomee 	buf.append(", flow = ");
798fb3fb4f3Stomee 	buf.append(flow);
799fb3fb4f3Stomee 	buf.append(", records = ");
800fb3fb4f3Stomee 
801fb3fb4f3Stomee 	Record record;
802fb3fb4f3Stomee 	Object value;
803fb3fb4f3Stomee 	buf.append('[');
804fb3fb4f3Stomee 	for (int i = 0; i < records.size(); ++i) {
805fb3fb4f3Stomee 	    if (i > 0) {
806fb3fb4f3Stomee 		buf.append(", ");
807fb3fb4f3Stomee 	    }
808fb3fb4f3Stomee 	    record = records.get(i);
809fb3fb4f3Stomee 	    if (record instanceof ValueRecord) {
810127bbe13Stomee 		value = ValueRecord.class.cast(record).getValue();
811fb3fb4f3Stomee 		if (value instanceof String) {
812fb3fb4f3Stomee 		    buf.append("\"");
813127bbe13Stomee 		    buf.append(String.class.cast(value));
814fb3fb4f3Stomee 		    buf.append("\"");
815fb3fb4f3Stomee 		} else {
816fb3fb4f3Stomee 		    buf.append(record);
817fb3fb4f3Stomee 		}
818fb3fb4f3Stomee 	    } else {
819fb3fb4f3Stomee 		buf.append(record);
820fb3fb4f3Stomee 	    }
821fb3fb4f3Stomee 	}
822fb3fb4f3Stomee 	buf.append(']');
823fb3fb4f3Stomee 
824fb3fb4f3Stomee 	buf.append(']');
825fb3fb4f3Stomee 	return buf.toString();
826fb3fb4f3Stomee     }
827fb3fb4f3Stomee }
828