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 }