1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 *
26 * ident	"%Z%%M%	%I%	%E% SMI"
27 */
28
29import org.opensolaris.os.dtrace.*;
30import java.util.*;
31import java.io.*;
32import java.beans.*;
33import java.lang.reflect.*;
34
35/**
36 * Regression test for serialization and XML encoding/decoding.  Tests
37 * every Serializable class in the Java DTrace API by creating a dummy
38 * instance, writing it to a file, then reading it back in and comparing
39 * the string values of the object before and after, as well as
40 * verifying object equality before and after if the class overrides the
41 * equals() method.
42 */
43public class TestBean {
44    public static final String[] TESTS = new String[] {
45	"ExitRecord",
46	"AggregationRecord",
47	"Aggregation",
48	"Tuple",
49	"ScalarRecord",
50	"KernelStackRecord",
51	"LogDistribution",
52	"LinearDistribution",
53	"Option",
54	"ProcessState",
55	"ProbeDescription",
56	"PrintaRecord",
57	"PrintfRecord",
58	"ProbeData",
59	"Aggregate",
60	"UserStackRecord",
61	"AvgValue",
62	"CountValue",
63	"SumValue",
64	"MinValue",
65	"MaxValue",
66	"Error",
67	"Drop",
68	"InterfaceAttributes",
69	"ProgramInfo",
70	"ProbeInfo",
71	"Probe",
72	"Flow",
73	"KernelSymbolRecord",
74	"UserSymbolRecord",
75	"UserSymbolRecord$Value",
76	"Program",
77	"Program$File",
78	"StddevValue"
79    };
80
81    static File file;
82
83    static void
84    exit(int status)
85    {
86	System.out.flush();
87	System.err.flush();
88	System.exit(status);
89    }
90
91    public static XMLEncoder
92    getXMLEncoder(File file)
93    {
94        XMLEncoder encoder = null;
95        try {
96            OutputStream out = new BufferedOutputStream
97                    (new FileOutputStream(file));
98            encoder = new XMLEncoder(out);
99        } catch (Exception e) {
100	    e.printStackTrace();
101	    exit(1);
102        }
103        return encoder;
104    }
105
106    public static XMLDecoder
107    getXMLDecoder(File file)
108    {
109        return getXMLDecoder(file, null);
110    }
111
112    public static XMLDecoder
113    getXMLDecoder(File file, ExceptionListener exceptionListener)
114    {
115        XMLDecoder decoder = null;
116        try {
117            InputStream in = new BufferedInputStream
118                    (new FileInputStream(file));
119            decoder = new XMLDecoder(in, null, exceptionListener);
120        } catch (Exception e) {
121	    e.printStackTrace();
122	    exit(1);
123        }
124        return decoder;
125    }
126
127    public static ExitRecord
128    getExitRecord()
129    {
130	ExitRecord r = new ExitRecord(1);
131	return r;
132    }
133
134    public static AggregationRecord
135    getAggregationRecord()
136    {
137	Tuple tuple = getTuple();
138	AggregationValue value = new CountValue(7);
139	AggregationRecord r = new AggregationRecord(tuple, value);
140	return r;
141    }
142
143    public static Aggregation
144    getAggregation()
145    {
146	List < AggregationRecord > list =
147	    new ArrayList < AggregationRecord > ();
148	AggregationRecord r;
149	r = getAggregationRecord();
150	list.add(r);
151
152	ValueRecord v1 = new ScalarRecord(new byte[] {(byte)1, (byte)2,
153	    (byte)3}, 3);
154	ValueRecord v2 = new ScalarRecord("shebang!", 256);
155	Tuple tuple = new Tuple(v1, v2);
156	AggregationValue value = getLinearDistribution();
157	r = new AggregationRecord(tuple, value);
158	list.add(r);
159
160	Aggregation a = new Aggregation("counts", 2, list);
161	return a;
162    }
163
164    public static Tuple
165    getTuple()
166    {
167	ValueRecord r1 = new ScalarRecord("cat", 256);
168	ValueRecord r2 = new ScalarRecord(new Integer(9), 2);
169	ValueRecord r3 = new KernelStackRecord(
170		new StackFrame[] {
171		    new StackFrame("has"),
172		    new StackFrame("nine"),
173		    new StackFrame("lives")},
174		new byte[] { (byte)0, (byte)1, (byte)2 });
175	ValueRecord r4 = new ScalarRecord(new byte[] {(byte)1, (byte)2,
176	    (byte)3}, 3);
177
178	Tuple tuple = new Tuple(r1, r2, r3, r4);
179	return tuple;
180    }
181
182    public static ScalarRecord
183    getScalarRecord()
184    {
185	Object v = new byte[] {(byte)1, (byte)2, (byte)3};
186	ScalarRecord r = new ScalarRecord(v, 3);
187	return r;
188    }
189
190    public static KernelStackRecord
191    getKernelStackRecord()
192    {
193	StackFrame[] stackFrames = new StackFrame[] {
194	    new StackFrame("Frame 1"),
195	    new StackFrame("Frame 2"),
196	    new StackFrame("Frame 3")
197	};
198	KernelStackRecord r = new KernelStackRecord(stackFrames,
199		new byte[] { (byte)0, (byte)1, (byte)2 });
200	return r;
201    }
202
203    public static LogDistribution
204    getLogDistribution()
205    {
206	List < Distribution.Bucket > buckets =
207		new ArrayList < Distribution.Bucket > ();
208	Distribution.Bucket bucket;
209	int n = 0;
210	long base = 0;
211	long i;
212	long sign;
213	long nextSign;
214	long power;
215	long nextPower;
216	long lowerBound;
217	long upperBound;
218	for (i = -62; i <= 62; ++i) {
219	    if (i == 0) {
220		bucket = new Distribution.Bucket(-1, -1, n++);
221		buckets.add(bucket);
222		bucket = new Distribution.Bucket(0, 0, n++);
223		buckets.add(bucket);
224		bucket = new Distribution.Bucket(1, 1, n++);
225		buckets.add(bucket);
226		continue;
227	    }
228	    sign = ((i < 0) ? -1L : 1L);
229	    power = (sign * i);
230	    nextSign = (((i + 1) < 0) ? -1L : 1L);
231	    nextPower = (nextSign * (i + 1));
232	    lowerBound = sign * ((long) Math.pow(2L, power));
233	    upperBound = (nextPower == 0 ? -2L :
234		    (nextSign * ((long) Math.pow(2L, nextPower))) - 1);
235	    if ((upperBound > 0) && ((upperBound * 2L) < 0)) {
236		upperBound = Long.MAX_VALUE;
237	    }
238	    bucket = new Distribution.Bucket(lowerBound, upperBound, n++);
239	    buckets.add(bucket);
240	}
241	LogDistribution d = new LogDistribution(buckets);
242	return d;
243    }
244
245    public static LinearDistribution
246    getLinearDistribution()
247    {
248	List < Distribution.Bucket > buckets =
249		new ArrayList < Distribution.Bucket > ();
250	Distribution.Bucket bucket;
251	int n = 10; // number of buckets
252	int base = 1;
253	int step = 10;
254	bucket = new Distribution.Bucket(Long.MIN_VALUE, (base - 1), 0);
255	buckets.add(bucket);
256	for (int i = base; i < (n * step); i += step) {
257	    bucket = new Distribution.Bucket(i, (i + (step - 1)),
258		    ((i - 1) / step));
259	    buckets.add(bucket);
260	}
261	bucket = new Distribution.Bucket((n * step) + 1, Long.MAX_VALUE, 0);
262	buckets.add(bucket);
263	LinearDistribution d = new LinearDistribution(base, step, buckets);
264	return d;
265    }
266
267    public static Option
268    getOption()
269    {
270	Option option = new Option("aggrate", "1s");
271	return option;
272    }
273
274    public static ProcessState
275    getProcessState()
276    {
277	ProcessState p = new ProcessState(123456, "UNDEAD",
278		3, "SIGSTOP",
279		-2, "Process stopped on dime");
280	return p;
281    }
282
283    public static ProbeDescription
284    getProbeDescription()
285    {
286	ProbeDescription d = new ProbeDescription(256, "syscall", null,
287	    "malloc", "entry");
288	return d;
289    }
290
291    public static PrintaRecord
292    getPrintaRecord()
293    {
294	List < Aggregation > aggregations = new ArrayList < Aggregation > ();
295	Aggregation a = getAggregation();
296	aggregations.add(a);
297	aggregations.add(a);
298	Map < Tuple, String > formattedOutput =
299		new HashMap < Tuple, String > ();
300	for (Tuple t : a.asMap().keySet()) {
301	    formattedOutput.put(t, "cat");
302	}
303	List < Tuple > tuples = new ArrayList < Tuple > ();
304	for (Tuple t : a.asMap().keySet()) {
305	    tuples.add(t);
306	}
307	Collections.sort(tuples);
308	PrintaRecord r = new PrintaRecord(1234567890L,
309	    aggregations, formattedOutput, tuples,
310	    "Yes, this is the formatted printa() output");
311	return r;
312    }
313
314    public static PrintfRecord
315    getPrintfRecord()
316    {
317	List < ValueRecord > list = new ArrayList < ValueRecord > ();
318	ValueRecord v1 = getScalarRecord();
319	ValueRecord v2 = new ScalarRecord(new Integer(7), 4);
320	list.add(v1);
321	list.add(v2);
322	PrintfRecord r = new PrintfRecord(list,
323		"long formatted string");
324	return r;
325    }
326
327    public static ProbeData
328    getProbeData()
329    {
330	List < Record > list = new ArrayList < Record > ();
331	list.add(getPrintaRecord());
332	list.add(getPrintfRecord());
333	list.add(getScalarRecord());
334	list.add(getUserSymbolRecord());
335	list.add(getUserStackRecord());
336	list.add(getExitRecord());
337	ProbeData d = new ProbeData(7, 1, getProbeDescription(),
338	    getFlow(), list);
339	return d;
340    }
341
342    public static Aggregate
343    getAggregate()
344    {
345	List < Aggregation > list = new ArrayList < Aggregation > ();
346	list.add(getAggregation());
347
348	List < AggregationRecord > reclist =
349	    new ArrayList < AggregationRecord > ();
350	AggregationRecord r;
351	ValueRecord v1 = new ScalarRecord("cat", 256);
352	ValueRecord v2 = new ScalarRecord("dog", 256);
353	ValueRecord v3 = new ScalarRecord("mouse", 256);
354	ValueRecord v4 = new ScalarRecord("mouse", 256);
355	ValueRecord v5 = new ScalarRecord(new Byte((byte) 'C'), 1);
356	ValueRecord v6 = new ScalarRecord(new Short((short) 7), 2);
357	Tuple tuple = new Tuple(v1, v2, v3, v4, v5, v6);
358	AggregationValue value = getCountValue();
359	r = new AggregationRecord(tuple, value);
360	reclist.add(r);
361	list.add(new Aggregation("times", 1, reclist));
362
363        Aggregate a = new Aggregate(1234567890L, list);
364	return a;
365    }
366
367    public static UserStackRecord
368    getUserStackRecord()
369    {
370	StackFrame[] frames = new StackFrame[] {
371	    new StackFrame("User Stack Frame 1"),
372	    new StackFrame("User Stack Frame 2"),
373	    new StackFrame("User Stack Frame 3")
374	};
375	UserStackRecord r = new UserStackRecord(123456, frames,
376		new byte[] { (byte)0, (byte)1, (byte)2 });
377	return r;
378    }
379
380    public static AvgValue
381    getAvgValue()
382    {
383	AvgValue v = new AvgValue(5, 20, 4);
384	return v;
385    }
386
387    public static CountValue
388    getCountValue()
389    {
390	CountValue v = new CountValue(9);
391	return v;
392    }
393
394    public static MinValue
395    getMinValue()
396    {
397	MinValue v = new MinValue(101);
398	return v;
399    }
400
401    public static MaxValue
402    getMaxValue()
403    {
404	MaxValue v = new MaxValue(101);
405	return v;
406    }
407
408    public static SumValue
409    getSumValue()
410    {
411	SumValue v = new SumValue(25);
412	return v;
413    }
414
415    public static org.opensolaris.os.dtrace.Error
416    getError()
417    {
418	ProbeDescription probe = getProbeDescription();
419	org.opensolaris.os.dtrace.Error e =
420	    new org.opensolaris.os.dtrace.Error(probe, 8, 3,
421	    1, 20, "DTRACEFLT_BADALIGN", -1, "error on enabled probe ID 8 " +
422	    "(ID " + probe.getID() + ": " + probe + "): Bad alignment " +
423	    "(0x33ef) in action #1 at DIF offset 20");
424	return e;
425    }
426
427    public static Drop
428    getDrop()
429    {
430	Drop drop = new Drop(2, "SPECBUSY", 72, 1041,
431	    "Guess we dropped stuff all over the place.");
432	return drop;
433    }
434
435    public static InterfaceAttributes
436    getInterfaceAttributes()
437    {
438	InterfaceAttributes a = new InterfaceAttributes(
439                InterfaceAttributes.Stability.UNSTABLE,
440                InterfaceAttributes.Stability.EVOLVING,
441                InterfaceAttributes.DependencyClass.ISA);
442	return a;
443    }
444
445    public static ProgramInfo
446    getProgramInfo()
447    {
448	ProgramInfo info = new ProgramInfo(getInterfaceAttributes(),
449		getInterfaceAttributes(), 256);
450	return info;
451    }
452
453    public static ProbeInfo
454    getProbeInfo()
455    {
456	ProbeInfo info = new ProbeInfo(getInterfaceAttributes(),
457		getInterfaceAttributes());
458	return info;
459    }
460
461    public static Probe
462    getProbe()
463    {
464	Probe p = new Probe(getProbeDescription(), getProbeInfo());
465	return p;
466    }
467
468    public static Flow
469    getFlow()
470    {
471	Flow f = new Flow(Flow.Kind.RETURN.name(), 3);
472	return f;
473    }
474
475    public static KernelSymbolRecord
476    getKernelSymbolRecord()
477    {
478	KernelSymbolRecord r = new KernelSymbolRecord("mod`func+0x4", -1L);
479	return r;
480    }
481
482    public static UserSymbolRecord
483    getUserSymbolRecord()
484    {
485	UserSymbolRecord r = new UserSymbolRecord(7, "mod`func+0x4", -1L);
486	return r;
487    }
488
489    public static UserSymbolRecord.Value
490    getUserSymbolRecord$Value()
491    {
492	UserSymbolRecord.Value v = new UserSymbolRecord.Value(7, -1L);
493	return v;
494    }
495
496    public static Program
497    getProgram()
498    {
499	final String PROGRAM = "syscall:::entry { @[execname] = count(); }";
500	Consumer consumer = new LocalConsumer();
501	Program p;
502	try {
503	    consumer.open();
504	    p = consumer.compile(PROGRAM);
505	    consumer.close();
506	} catch (DTraceException e) {
507	    e.printStackTrace();
508	    p = null;
509	}
510	return p;
511    }
512
513    public static Program.File
514    getProgram$File()
515    {
516	final String PROGRAM = "syscall:::entry { @[execname] = count(); }";
517	Consumer consumer = new LocalConsumer();
518	Program p;
519	try {
520            OutputStream out = new FileOutputStream(file);
521	    out.write(PROGRAM.getBytes(), 0, PROGRAM.length());
522	    out.flush();
523	    out.close();
524	    consumer.open();
525	    p = consumer.compile(file);
526	    consumer.close();
527	} catch (Exception e) {
528	    e.printStackTrace();
529	    p = null;
530	}
531	return Program.File.class.cast(p);
532    }
533
534    public static StddevValue
535    getStddevValue()
536    {
537	StddevValue v = new StddevValue(37, 114, 5, Integer.toString(9544));
538	return v;
539    }
540
541    @SuppressWarnings("unchecked")
542    static String
543    getString(Object o)
544    {
545	String s;
546	if (o instanceof ScalarRecord) {
547	    o = ((ScalarRecord)o).getValue();
548	}
549
550	if (o instanceof byte[]) {
551	    s = Arrays.toString((byte[])o);
552	} else if (o instanceof Object[]) {
553	    s = Arrays.toString((Object[])o);
554	} else {
555	    Class c = o.getClass();
556	    try {
557		Method m = c.getDeclaredMethod("toLogString");
558		s = (String)m.invoke(o);
559	    } catch (Exception e) {
560		s = o.toString();
561	    }
562	}
563	return s;
564    }
565
566    static void
567    checkEquality(Object obj, Object newobj)
568    {
569	// If the class overrides equals(), make sure the re-created
570	// object still equals the original object
571	try {
572	    Method eq = obj.getClass().getDeclaredMethod("equals",
573		    Object.class);
574	    Boolean ret = (Boolean) eq.invoke(obj, newobj);
575	    if (ret != true) {
576		System.err.println("serialization failed: " +
577			obj.getClass().getName());
578		exit(1);
579	    }
580	} catch (Exception e) {
581	    // Does not override equals(), although a super-class might.
582	    // A better test would check for any superclass other than
583	    // Object.class.
584	}
585    }
586
587    static void
588    performSerializationTest(File file, String classname)
589            throws IOException, ClassNotFoundException
590    {
591	String methodName = "get" + classname;
592	Object obj = null;
593	Object newobj = null;
594	try {
595	    Method method = TestBean.class.getDeclaredMethod(methodName);
596	    obj = method.invoke(null);
597	} catch (Exception e) {
598	    e.printStackTrace();
599	    exit(1);
600	}
601
602	System.out.println(classname + ":");
603	String serialized = getString(obj);
604	System.out.println("  serialized: " + serialized);
605	FileOutputStream fos = new FileOutputStream(file);
606	ObjectOutputStream out = new ObjectOutputStream(fos);
607	out.writeObject(obj);
608	out.close();
609	FileInputStream fis = new FileInputStream(file);
610	ObjectInputStream in = new ObjectInputStream(fis);
611	newobj = in.readObject();
612	in.close();
613	String deserialized = getString(newobj);
614	System.out.println("  deserialized: " + deserialized);
615
616	if (!serialized.equals(deserialized)) {
617	    System.err.println("serialization failed: " + classname);
618	    exit(1);
619	}
620	checkEquality(obj, newobj);
621    }
622
623    static void
624    performBeanTest(File file, String classname)
625    {
626	String methodName = "get" + classname;
627	Object obj = null;
628	Object newobj = null;
629	try {
630	    Method method = TestBean.class.getDeclaredMethod(methodName);
631	    obj = method.invoke(null);
632	} catch (Exception e) {
633	    e.printStackTrace();
634	    exit(1);
635	}
636
637	Class c = obj.getClass();
638	if (c.getConstructors().length == 0) {
639	    return;
640	}
641
642	System.out.println(classname + ":");
643	XMLEncoder encoder = getXMLEncoder(file);
644	String encoded = getString(obj);
645	System.out.println("  encoded: " + encoded);
646	encoder.writeObject(obj);
647	encoder.close();
648	XMLDecoder decoder = getXMLDecoder(file);
649	newobj = decoder.readObject();
650	String decoded = getString(newobj);
651	System.out.println("  decoded: " + decoded);
652	decoder.close();
653
654	if (!encoded.equals(decoded)) {
655	    System.err.println("bean persistence failed: " + classname);
656	    exit(1);
657	}
658	checkEquality(obj, newobj);
659    }
660
661    public static void
662    main(String[] args)
663    {
664	if ((args.length != 1) && (args.length != 2)) {
665	    System.err.println("usage: java TestBean < filename > " +
666		    "[ < classname > ]");
667	    exit(1);
668	}
669
670	String filename = args[0];
671	String classname = null;
672	if (args.length >= 2) {
673	    classname = args[1];
674	}
675
676	file = new File(filename);
677	try {
678	    if (!file.canRead()) {
679		try {
680		    file.createNewFile();
681		} catch (Exception e) {
682		    System.err.println("failed to create " + filename);
683		    exit(1);
684		}
685	    }
686	} catch (SecurityException e) {
687	    System.err.println("failed to open " + filename);
688	    exit(1);
689	}
690
691	String[] tests = (classname == null ? TESTS:
692		new String[] { classname });
693	try {
694	    for (int i = 0; i < tests.length; ++i) {
695		performSerializationTest(file, tests[i]);
696		performBeanTest(file, tests[i]);
697	    }
698	} catch (IOException e) {
699	    e.printStackTrace();
700	    exit(1);
701	} catch (ClassNotFoundException e) {
702	    e.printStackTrace();
703	    exit(1);
704	}
705    }
706}
707