1 /*
2  * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 
25 package org.graalvm.compiler.debug.test;
26 
27 import static org.graalvm.compiler.debug.DebugContext.NO_DESCRIPTION;
28 import static org.graalvm.compiler.debug.DebugContext.NO_GLOBAL_METRIC_VALUES;
29 
30 import java.io.ByteArrayOutputStream;
31 import java.io.DataInputStream;
32 import java.io.IOException;
33 import java.io.PrintStream;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.Formatter;
37 import java.util.List;
38 
39 import jdk.internal.vm.compiler.collections.EconomicMap;
40 import org.graalvm.compiler.debug.Assertions;
41 import org.graalvm.compiler.debug.CounterKey;
42 import org.graalvm.compiler.debug.DebugCloseable;
43 import org.graalvm.compiler.debug.DebugContext;
44 import org.graalvm.compiler.debug.DebugContext.Scope;
45 import org.graalvm.compiler.debug.DebugDumpHandler;
46 import org.graalvm.compiler.debug.DebugHandler;
47 import org.graalvm.compiler.debug.DebugHandlersFactory;
48 import org.graalvm.compiler.debug.DebugOptions;
49 import org.graalvm.compiler.debug.DebugVerifyHandler;
50 import org.graalvm.compiler.options.OptionKey;
51 import org.graalvm.compiler.options.OptionValues;
52 import org.junit.Assert;
53 import org.junit.Assume;
54 import org.junit.Test;
55 
56 @SuppressWarnings("try")
57 public class DebugContextTest {
58 
59     static class DebugContextSetup {
60         final Formatter dumpOutput = new Formatter();
61         final Formatter verifyOutput = new Formatter();
62         final ByteArrayOutputStream logOutput = new ByteArrayOutputStream();
63         DebugHandlersFactory handlers = new DebugHandlersFactory() {
64             @Override
65             public List<DebugHandler> createHandlers(OptionValues options) {
66                 return Arrays.asList(new DebugDumpHandler() {
67                     @Override
68                     public void dump(DebugContext ignore, Object object, String format, Object... arguments) {
69                         dumpOutput.format("Dumping %s with label \"%s\"%n", object, String.format(format, arguments));
70                     }
71                 }, new DebugVerifyHandler() {
72                     @Override
73                     public void verify(DebugContext ignore, Object object, String format, Object... args) {
74                         verifyOutput.format("Verifying %s with label \"%s\"%n", object, String.format(format, args));
75                     }
76                 });
77             }
78         };
79 
openDebugContext(OptionValues options)80         DebugContext openDebugContext(OptionValues options) {
81             return DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, new PrintStream(logOutput), Collections.singletonList(handlers));
82 
83         }
84     }
85 
86     @Test
testDisabledScopes()87     public void testDisabledScopes() {
88         OptionValues options = new OptionValues(EconomicMap.create());
89         DebugContextSetup setup = new DebugContextSetup();
90         try (DebugContext debug = setup.openDebugContext(options);
91                         DebugContext.Scope d = debug.scope("TestDisabledScoping")) {
92             for (int level = DebugContext.BASIC_LEVEL; level <= DebugContext.VERY_DETAILED_LEVEL; level++) {
93                 debug.dump(level, "an object", "at level %d", level);
94                 debug.verify("an object", "at level %d", level);
95                 debug.log(level, "log statement at level %d", level);
96             }
97         }
98         String log = setup.logOutput.toString();
99         String dumpOutput = setup.dumpOutput.toString();
100         String verifyOutput = setup.verifyOutput.toString();
101         Assert.assertTrue(log, log.isEmpty());
102         Assert.assertTrue(dumpOutput, dumpOutput.isEmpty());
103         Assert.assertTrue(verifyOutput, verifyOutput.isEmpty());
104     }
105 
106     @Test
testDumping()107     public void testDumping() {
108         for (int level = DebugContext.BASIC_LEVEL; level <= DebugContext.VERY_DETAILED_LEVEL; level++) {
109             OptionValues options = new OptionValues(EconomicMap.create());
110             options = new OptionValues(options, DebugOptions.Dump, "Scope" + level + ":" + level);
111             DebugContextSetup setup = new DebugContextSetup();
112             try (DebugContext debug = setup.openDebugContext(options);
113                             DebugContext.Scope s0 = debug.scope("TestDumping")) {
114                 try (DebugContext.Scope s1 = debug.scope("Scope1")) {
115                     try (DebugContext.Scope s2 = debug.scope("Scope2")) {
116                         try (DebugContext.Scope s3 = debug.scope("Scope3")) {
117                             try (DebugContext.Scope s4 = debug.scope("Scope4")) {
118                                 try (DebugContext.Scope s5 = debug.scope("Scope5")) {
119                                     debug.dump(level, "an object", "at level %d", level);
120                                 }
121                             }
122                         }
123                     }
124                 }
125 
126             }
127 
128             String expect = String.format("Dumping an object with label \"at level %d\"%n", level);
129             String dump = setup.dumpOutput.toString();
130             Assert.assertEquals(expect, dump);
131         }
132     }
133 
134     @Test
testLogging()135     public void testLogging() throws IOException {
136         OptionValues options = new OptionValues(EconomicMap.create());
137         options = new OptionValues(options, DebugOptions.Log, ":5");
138         DebugContextSetup setup = new DebugContextSetup();
139         try (DebugContext debug = setup.openDebugContext(options)) {
140             for (int level = DebugContext.BASIC_LEVEL; level <= DebugContext.VERY_DETAILED_LEVEL; level++) {
141                 try (DebugContext.Scope s0 = debug.scope("TestLogging")) {
142                     debug.log(level, "log statement at level %d", level);
143                     try (DebugContext.Scope s1 = debug.scope("Level1")) {
144                         debug.log(level, "log statement at level %d", level);
145                         try (DebugContext.Scope s2 = debug.scope("Level2")) {
146                             debug.log(level, "log statement at level %d", level);
147                             try (DebugContext.Scope s3 = debug.scope("Level3")) {
148                                 debug.log(level, "log statement at level %d", level);
149                                 try (DebugContext.Scope s4 = debug.scope("Level4")) {
150                                     debug.log(level, "log statement at level %d", level);
151                                     try (DebugContext.Scope s5 = debug.scope("Level5")) {
152                                         debug.log(level, "log statement at level %d", level);
153                                     }
154                                 }
155                             }
156                         }
157                     }
158                 }
159             }
160         }
161         DataInputStream in = new DataInputStream(getClass().getResourceAsStream(getClass().getSimpleName() + ".testLogging.input"));
162         byte[] buf = new byte[in.available()];
163         in.readFully(buf);
164         String threadLabel = "[thread:" + Thread.currentThread().getId() + "]";
165         String expect = new String(buf).replace("[thread:1]", threadLabel);
166 
167         String log = setup.logOutput.toString();
168         Assert.assertEquals(expect, log);
169     }
170 
171     @Test
testContextScope()172     public void testContextScope() {
173         OptionValues options = new OptionValues(EconomicMap.create());
174         options = new OptionValues(options, DebugOptions.Log, ":5");
175         DebugContextSetup setup = new DebugContextSetup();
176         try (DebugContext debug = setup.openDebugContext(options)) {
177             try (DebugContext.Scope s0 = debug.scope("TestLogging")) {
178                 try (DebugContext.Scope s1 = debug.withContext("A")) {
179                     for (Object o : debug.context()) {
180                         Assert.assertEquals(o, "A");
181                     }
182                 } catch (Throwable t) {
183                     throw debug.handle(t);
184                 }
185                 try (DebugContext.Scope s1 = debug.withContext("B")) {
186                     for (Object o : debug.context()) {
187                         Assert.assertEquals(o, "B");
188                     }
189                 } catch (Throwable t) {
190                     throw debug.handle(t);
191                 }
192             }
193         }
194 
195     }
196 
197     @Test
testEnabledSandbox()198     public void testEnabledSandbox() {
199         EconomicMap<OptionKey<?>, Object> map = EconomicMap.create();
200         // Configure with an option that enables scopes
201         map.put(DebugOptions.DumpOnError, true);
202         OptionValues options = new OptionValues(map);
203         ByteArrayOutputStream baos = new ByteArrayOutputStream();
204         DebugContext debug = DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, new PrintStream(baos), DebugHandlersFactory.LOADER);
205         Exception e = new Exception("testEnabledSandbox");
206         String scopeName = "";
207         try {
208             try (DebugContext.Scope d = debug.sandbox("TestExceptionHandling", debug.getConfig())) {
209                 scopeName = d.getQualifiedName();
210                 throw e;
211             } catch (Throwable t) {
212                 assert e == t;
213                 debug.handle(t);
214             }
215         } catch (Throwable t) {
216             // The exception object should propagate all the way out through
217             // a enabled sandbox scope
218             Assert.assertEquals(e, t);
219         }
220         String logged = baos.toString();
221         String expected = String.format("Exception raised in scope %s: %s", scopeName, e);
222         String line = "-------------------------------------------------------";
223         Assert.assertTrue(String.format("Could not find \"%s\" in content between lines below:%n%s%n%s%s", expected, line, logged, line), logged.contains(expected));
224     }
225 
226     @Test
testDisabledSandbox()227     public void testDisabledSandbox() {
228         EconomicMap<OptionKey<?>, Object> map = EconomicMap.create();
229         // Configure with an option that enables scopes
230         map.put(DebugOptions.DumpOnError, true);
231         OptionValues options = new OptionValues(map);
232         ByteArrayOutputStream baos = new ByteArrayOutputStream();
233         DebugContext debug = DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, new PrintStream(baos), DebugHandlersFactory.LOADER);
234         Exception e = new Exception("testDisabledSandbox");
235         try {
236             // Test a disabled sandbox scope
237             try (DebugContext.Scope d = debug.sandbox("TestExceptionHandling", null)) {
238                 throw e;
239             } catch (Throwable t) {
240                 assert e == t;
241                 debug.handle(t);
242             }
243         } catch (Throwable t) {
244             // The exception object should propagate all the way out through
245             // a disabled sandbox scope
246             Assert.assertEquals(e, t);
247         }
248         String logged = baos.toString();
249         Assert.assertTrue(logged, logged.isEmpty());
250     }
251 
252     /**
253      * Tests that using a {@link DebugContext} on a thread other than the one on which it was
254      * created causes an assertion failure.
255      */
256     @Test
testInvariantChecking()257     public void testInvariantChecking() throws InterruptedException {
258         Assume.assumeTrue(Assertions.assertionsEnabled());
259         EconomicMap<OptionKey<?>, Object> map = EconomicMap.create();
260         // Configure with an option that enables counters
261         map.put(DebugOptions.Counters, "");
262         OptionValues options = new OptionValues(map);
263         DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER);
264         CounterKey counter = DebugContext.counter("DebugContextTestCounter");
265         AssertionError[] result = {null};
266         Thread thread = new Thread() {
267 
268             @Override
269             public void run() {
270                 try {
271                     counter.add(debug, 1);
272                 } catch (AssertionError e) {
273                     result[0] = e;
274                 }
275             }
276         };
277         thread.start();
278         thread.join();
279 
280         Assert.assertNotNull("Expected thread to throw AssertionError", result[0]);
281     }
282 
283     @Test
testDisableIntercept()284     public void testDisableIntercept() {
285         EconomicMap<OptionKey<?>, Object> map = EconomicMap.create();
286         // Configure with an option that enables scopes
287         map.put(DebugOptions.DumpOnError, true);
288         OptionValues options = new OptionValues(map);
289         ByteArrayOutputStream baos = new ByteArrayOutputStream();
290         DebugContext debug = DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, new PrintStream(baos), DebugHandlersFactory.LOADER);
291         Exception e = new Exception();
292         try {
293             try (DebugCloseable disabled = debug.disableIntercept(); Scope s1 = debug.scope("ScopeWithDisabledIntercept")) {
294                 try (Scope s2 = debug.scope("InnerScopeInheritsDisabledIntercept")) {
295                     throw e;
296                 }
297             } catch (Throwable t) {
298                 assert e == t;
299                 debug.handle(t);
300             }
301         } catch (Throwable t) {
302             // The exception object should propagate all the way out through
303             // an intercept disabled scope
304             Assert.assertEquals(e, t);
305         }
306         String logged = baos.toString();
307         Assert.assertEquals("Exception should not have been intercepted", "", logged);
308     }
309 }
310