/** * Copyright 2005-2012 Akiban Technologies, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.persistit.util; import java.io.PrintWriter; import java.io.StringWriter; import java.util.ArrayList; import java.util.Random; import java.util.StringTokenizer; import java.util.concurrent.atomic.AtomicLong; /** * Internal helper methods used to help verify code correctness. * * @author peter * */ public class Debug { public final static boolean ENABLED = false; public final static boolean VERIFY_PAGES = false; public final static Random RANDOM = new Random(123); private final static AtomicLong PAUSES = new AtomicLong(); public interface Dbg { void t(boolean b); } private static class Null implements Dbg { @Override public void t(final boolean b) { } } private static class Assert implements Dbg { private final String _name; private Assert(final String name) { _name = name; } @Override public void t(final boolean b) { if (!b) { logDebugMessage(_name); setSuspended(true); // // Put a breakpoint on the next statement. // setSuspended(false); // <-- BREAKPOINT HERE } } } public static Dbg $assert0 = ENABLED ? new Assert("assert0") : new Null(); public static Dbg $assert1 = new Assert("assert1"); private static int _suspendedCount; private static ArrayList<Thread> _brokenThreads = new ArrayList<Thread>(); private static long _startTime; public static void setStartTime(final long startTime) { _startTime = startTime; } public static long elapsedTime() { return now() - _startTime; } public static long now() { return System.currentTimeMillis(); } private static void logDebugMessage(final String msg) { final RuntimeException exception = new RuntimeException(); exception.fillInStackTrace(); final String s = asString(exception).replace('\r', ' '); final StringTokenizer st = new StringTokenizer(s, "\n"); final StringBuilder sb = new StringBuilder(msg); sb.append(Util.NEW_LINE); while (st.hasMoreTokens()) { sb.append(" "); sb.append(st.nextToken()); sb.append(Util.NEW_LINE); } System.err.println("Debug " + sb.toString()); } public static String trace(final int from, final int to) { return " " + Thread.currentThread().getName() + " {" + Debug.callStack(from + 2, to + 2) + "}"; } public static String callStack(final int from, final int to) { final RuntimeException exception = new RuntimeException(); exception.fillInStackTrace(); final StackTraceElement[] elements = exception.getStackTrace(); final int a = Math.max(0, from); final int b = Math.min(to, elements.length); final StringBuilder sb = new StringBuilder(); for (int index = b; index >= a; index--) { final StackTraceElement t = exception.getStackTrace()[index]; if (index != b) { sb.append("->"); } sb.append(t.getClassName()); sb.append('#'); sb.append(t.getMethodName()); sb.append('['); sb.append(t.getLineNumber()); sb.append("]"); } return sb.toString(); } /** * Set the suspend flag so that callers to the suspend method either do or * do not suspend. * * @param b */ synchronized static void setSuspended(final boolean b) { if (b) { _suspendedCount++; _brokenThreads.add(Thread.currentThread()); } else { _suspendedCount--; _brokenThreads.remove(Thread.currentThread()); if (_suspendedCount == 0) { $assert1.t(_brokenThreads.size() == _suspendedCount); } } } /** * @return The state of the suspend flag. */ synchronized static boolean isSuspended() { return _suspendedCount > 0; } /** * Assert this method invocation anywhere you want to suspend a thread. For * example, add this to cause execution to be suspended: * * assert(Debug.suspend()); * * This method always returns true so there will never be an AssertionError * thrown. * * @return <i>true</i> */ public static boolean suspend() { if (ENABLED) { // Never suspend the AWT thread when. The AWT thread is now // a daemon thread when running the diagnostic GUI utility. // long time = -1; while (isSuspended() && !Thread.currentThread().isDaemon()) { if (time < 0) time = elapsedTime(); try { Thread.sleep(1000); } catch (final InterruptedException ie) { } } } return true; } public static String asString(final Throwable t) { final StringWriter sw = new StringWriter(); t.printStackTrace(new PrintWriter(sw)); return sw.toString(); } /** * Debugging aid: code can invoke this method to introduce a pause. * * @param probability * Probability of pausing: 0 - 1.0f * @param millis * time interval in milliseconds */ public static void debugPause(final float probability, final long millis) { if (RANDOM.nextInt(1000000000) < (int) (1000000000f * probability)) { try { Thread.sleep(millis); PAUSES.incrementAndGet(); } catch (final InterruptedException ie) { Thread.currentThread().interrupt(); } } } }