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 package org.graalvm.compiler.debug.test; 24 25 import static org.graalvm.compiler.debug.DebugContext.NO_DESCRIPTION; 26 import static org.graalvm.compiler.debug.DebugContext.NO_GLOBAL_METRIC_VALUES; 27 28 import java.io.ByteArrayOutputStream; 29 import java.io.DataInputStream; 30 import java.io.IOException; 31 import java.io.PrintStream; 32 import java.util.Arrays; 33 import java.util.Collections; 34 import java.util.Formatter; 35 import java.util.List; 36 37 import org.graalvm.compiler.debug.Assertions; 38 import org.graalvm.compiler.debug.CounterKey; 39 import org.graalvm.compiler.debug.DebugCloseable; 40 import org.graalvm.compiler.debug.DebugContext; 41 import org.graalvm.compiler.debug.DebugContext.Scope; 42 import org.graalvm.compiler.debug.DebugDumpHandler; 43 import org.graalvm.compiler.debug.DebugHandler; 44 import org.graalvm.compiler.debug.DebugHandlersFactory; 45 import org.graalvm.compiler.debug.DebugOptions; 46 import org.graalvm.compiler.debug.DebugVerifyHandler; 47 import org.graalvm.compiler.options.OptionKey; 48 import org.graalvm.compiler.options.OptionValues; 49 import org.graalvm.util.EconomicMap; 50 import org.junit.Assert; 51 import org.junit.Assume; 52 import org.junit.Test; 53 54 @SuppressWarnings("try") 55 public class DebugContextTest { 56 57 static class DebugContextSetup { 58 final Formatter dumpOutput = new Formatter(); 59 final Formatter verifyOutput = new Formatter(); 60 final ByteArrayOutputStream logOutput = new ByteArrayOutputStream(); 61 DebugHandlersFactory handlers = new DebugHandlersFactory() { 62 @Override 63 public List<DebugHandler> createHandlers(OptionValues options) { 64 return Arrays.asList(new DebugDumpHandler() { 65 @Override 66 public void dump(DebugContext ignore, Object object, String format, Object... arguments) { 67 dumpOutput.format("Dumping %s with label \"%s\"%n", object, String.format(format, arguments)); 68 } 69 }, new DebugVerifyHandler() { 70 @Override 71 public void verify(DebugContext ignore, Object object, String format, Object... args) { 72 verifyOutput.format("Verifying %s with label \"%s\"%n", object, String.format(format, args)); 73 } 74 }); 75 } 76 }; 77 78 DebugContext openDebugContext(OptionValues options) { 79 return DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, new PrintStream(logOutput), Collections.singletonList(handlers)); 80 81 } 82 } 83 84 @Test 85 public void testDisabledScopes() { 86 OptionValues options = new OptionValues(EconomicMap.create()); 87 DebugContextSetup setup = new DebugContextSetup(); 88 try (DebugContext debug = setup.openDebugContext(options); 89 DebugContext.Scope d = debug.scope("TestDisabledScoping")) { 90 for (int level = DebugContext.BASIC_LEVEL; level <= DebugContext.VERY_DETAILED_LEVEL; level++) { 91 debug.dump(level, "an object", "at level %d", level); 92 debug.verify("an object", "at level %d", level); 93 debug.log(level, "log statement at level %d", level); 94 } 95 } 96 String log = setup.logOutput.toString(); 97 String dumpOutput = setup.dumpOutput.toString(); 98 String verifyOutput = setup.verifyOutput.toString(); 99 Assert.assertTrue(log, log.isEmpty()); 100 Assert.assertTrue(dumpOutput, dumpOutput.isEmpty()); 101 Assert.assertTrue(verifyOutput, verifyOutput.isEmpty()); 102 } 103 104 @Test 105 public void testDumping() { 106 for (int level = DebugContext.BASIC_LEVEL; level <= DebugContext.VERY_DETAILED_LEVEL; level++) { 107 OptionValues options = new OptionValues(EconomicMap.create()); 108 options = new OptionValues(options, DebugOptions.Dump, "Scope" + level + ":" + level); 109 DebugContextSetup setup = new DebugContextSetup(); 110 try (DebugContext debug = setup.openDebugContext(options); 111 DebugContext.Scope s0 = debug.scope("TestDumping")) { 112 try (DebugContext.Scope s1 = debug.scope("Scope1")) { 113 try (DebugContext.Scope s2 = debug.scope("Scope2")) { 114 try (DebugContext.Scope s3 = debug.scope("Scope3")) { 115 try (DebugContext.Scope s4 = debug.scope("Scope4")) { 116 try (DebugContext.Scope s5 = debug.scope("Scope5")) { 117 debug.dump(level, "an object", "at level %d", level); 118 } 119 } 120 } 121 } 122 } 123 124 } 125 126 String expect = String.format("Dumping an object with label \"at level %d\"%n", level); 127 String dump = setup.dumpOutput.toString(); 128 Assert.assertEquals(expect, dump); 129 } 130 } 131 132 @Test 133 public void testLogging() throws IOException { 134 OptionValues options = new OptionValues(EconomicMap.create()); 135 options = new OptionValues(options, DebugOptions.Log, ":5"); 136 DebugContextSetup setup = new DebugContextSetup(); 137 try (DebugContext debug = setup.openDebugContext(options)) { 138 for (int level = DebugContext.BASIC_LEVEL; level <= DebugContext.VERY_DETAILED_LEVEL; level++) { 139 try (DebugContext.Scope s0 = debug.scope("TestLogging")) { 140 debug.log(level, "log statement at level %d", level); 141 try (DebugContext.Scope s1 = debug.scope("Level1")) { 142 debug.log(level, "log statement at level %d", level); 143 try (DebugContext.Scope s2 = debug.scope("Level2")) { 144 debug.log(level, "log statement at level %d", level); 145 try (DebugContext.Scope s3 = debug.scope("Level3")) { 146 debug.log(level, "log statement at level %d", level); 147 try (DebugContext.Scope s4 = debug.scope("Level4")) { 148 debug.log(level, "log statement at level %d", level); 149 try (DebugContext.Scope s5 = debug.scope("Level5")) { 150 debug.log(level, "log statement at level %d", level); 151 } 152 } 153 } 154 } 155 } 156 } 157 } 158 } 159 DataInputStream in = new DataInputStream(getClass().getResourceAsStream(getClass().getSimpleName() + ".testLogging.input")); 160 byte[] buf = new byte[in.available()]; 161 in.readFully(buf); 162 String threadLabel = "[thread:" + Thread.currentThread().getId() + "]"; 163 String expect = new String(buf).replace("[thread:1]", threadLabel); 164 165 String log = setup.logOutput.toString(); 166 Assert.assertEquals(expect, log); 167 } 168 169 @Test 170 public void testEnabledSandbox() { 171 EconomicMap<OptionKey<?>, Object> map = EconomicMap.create(); 172 // Configure with an option that enables scopes 173 map.put(DebugOptions.DumpOnError, true); 174 OptionValues options = new OptionValues(map); 175 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 176 DebugContext debug = DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, new PrintStream(baos), DebugHandlersFactory.LOADER); 177 Exception e = new Exception("testEnabledSandbox"); 178 String scopeName = ""; 179 try { 180 try (DebugContext.Scope d = debug.sandbox("TestExceptionHandling", debug.getConfig())) { 181 scopeName = d.getQualifiedName(); 182 throw e; 183 } catch (Throwable t) { 184 assert e == t; 185 debug.handle(t); 186 } 187 } catch (Throwable t) { 188 // The exception object should propagate all the way out through 189 // a enabled sandbox scope 190 Assert.assertEquals(e, t); 191 } 192 String logged = baos.toString(); 193 String expected = String.format("Exception raised in scope %s: %s", scopeName, e); 194 String line = "-------------------------------------------------------"; 195 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)); 196 } 197 198 @Test 199 public void testDisabledSandbox() { 200 EconomicMap<OptionKey<?>, Object> map = EconomicMap.create(); 201 // Configure with an option that enables scopes 202 map.put(DebugOptions.DumpOnError, true); 203 OptionValues options = new OptionValues(map); 204 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 205 DebugContext debug = DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, new PrintStream(baos), DebugHandlersFactory.LOADER); 206 Exception e = new Exception("testDisabledSandbox"); 207 try { 208 // Test a disabled sandbox scope 209 try (DebugContext.Scope d = debug.sandbox("TestExceptionHandling", null)) { 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 disabled sandbox scope 218 Assert.assertEquals(e, t); 219 } 220 String logged = baos.toString(); 221 Assert.assertTrue(logged, logged.isEmpty()); 222 } 223 224 /** 225 * Tests that using a {@link DebugContext} on a thread other than the one on which it was 226 * created causes an assertion failure. 227 */ 228 @Test 229 public void testInvariantChecking() throws InterruptedException { 230 Assume.assumeTrue(Assertions.assertionsEnabled()); 231 EconomicMap<OptionKey<?>, Object> map = EconomicMap.create(); 232 // Configure with an option that enables counters 233 map.put(DebugOptions.Counters, ""); 234 OptionValues options = new OptionValues(map); 235 DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER); 236 CounterKey counter = DebugContext.counter("DebugContextTestCounter"); 237 AssertionError[] result = {null}; 238 Thread thread = new Thread() { 239 240 @Override 241 public void run() { 242 try { 243 counter.add(debug, 1); 244 } catch (AssertionError e) { 245 result[0] = e; 246 } 247 } 248 }; 249 thread.start(); 250 thread.join(); 251 252 Assert.assertNotNull("Expected thread to throw AssertionError", result[0]); 253 } 254 255 @Test 256 public void testDisableIntercept() { 257 EconomicMap<OptionKey<?>, Object> map = EconomicMap.create(); 258 // Configure with an option that enables scopes 259 map.put(DebugOptions.DumpOnError, true); 260 OptionValues options = new OptionValues(map); 261 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 262 DebugContext debug = DebugContext.create(options, NO_DESCRIPTION, NO_GLOBAL_METRIC_VALUES, new PrintStream(baos), DebugHandlersFactory.LOADER); 263 Exception e = new Exception(); 264 try { 265 try (DebugCloseable disabled = debug.disableIntercept(); Scope s1 = debug.scope("ScopeWithDisabledIntercept")) { 266 try (Scope s2 = debug.scope("InnerScopeInheritsDisabledIntercept")) { 267 throw e; 268 } 269 } catch (Throwable t) { 270 assert e == t; 271 debug.handle(t); 272 } 273 } catch (Throwable t) { 274 // The exception object should propagate all the way out through 275 // an intercept disabled scope 276 Assert.assertEquals(e, t); 277 } 278 String logged = baos.toString(); 279 Assert.assertEquals("Exception should not have been intercepted", "", logged); 280 } 281 }