/* * Copyright (c) 2008-2017, Hazelcast, Inc. All Rights Reserved. * * 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.hazelcast.test.jitter; import com.hazelcast.test.JenkinsDetector; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import java.text.DateFormat; import java.text.SimpleDateFormat; import static com.hazelcast.util.QuickMath.nextPowerOfTwo; import static com.hazelcast.util.StringUtil.LINE_SEPARATOR; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.SECONDS; /** * JUnit rule for detecting JVM/OS hiccups. It's meant to give you an insight into your environment * in the case of a test failure. This is useful for troubleshooting of spuriously failing tests. */ public class JitterRule implements TestRule { /** * Time interval aggregated into a single bucket. Smaller interval provides * a clearer picture about hiccups in time, too small intervals may use too * much of memory and can also generate overwhelming amount of data. */ public static final long AGGREGATION_INTERVAL_MILLIS = SECONDS.toMillis(5); /** * Number of buckets to be created. Jitter monitor records a floating window * where the length of the window can be calculated as * <code>AGGREGATION_INTERVAL_MILLIS * CAPACITY</code> * * It has to be a power of two. */ public static final int CAPACITY = nextPowerOfTwo(720); /** * Resolution of the measurement. Smaller number can detect shorter pauses, * but it can cause too much of overhead. Too long value causes less overhead, * but it may miss shorter pauses. */ public static final long RESOLUTION_NANOS = MILLISECONDS.toNanos(10); /** * Hiccups over this threshold will be counted separately. This is useful for counting * serious hiccups. */ public static final long LONG_HICCUP_THRESHOLD = SECONDS.toNanos(1); private static final String MODE_PROPERTY_NAME = "hazelcast.jitterMonitor.mode"; private static final Mode DEFAULT_MODE = Mode.JENKINS; private static final boolean ENABLED = isEnabled(); static { if (ENABLED) { JitterMonitor.ensureRunning(); } } private static boolean isEnabled() { String modePropertyValue = System.getProperty(MODE_PROPERTY_NAME, DEFAULT_MODE.name()); Mode mode = Mode.valueOf(modePropertyValue); switch (mode) { case DISABLED: return false; case ENABLED: return true; case JENKINS: return JenkinsDetector.isOnJenkins(); default: throw new IllegalArgumentException("Unknown mode: " + mode); } } @Override public Statement apply(final Statement base, final Description description) { if (!ENABLED) { return base; } return new Statement() { @Override public void evaluate() throws Throwable { long startTime = System.currentTimeMillis(); try { base.evaluate(); } catch (Throwable t) { printJitters(startTime); throw t; } } private void printJitters(long startTime) { long endTime = System.currentTimeMillis(); Iterable<Slot> slotsBetween = JitterMonitor.getSlotsBetween(startTime, endTime); StringBuilder sb = new StringBuilder("Hiccups measured while running test '") .append(description.getDisplayName()) .append(":'") .append(LINE_SEPARATOR); DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); for (Slot slot : slotsBetween) { sb.append(slot.toHumanFriendly(dateFormat)).append(LINE_SEPARATOR); } System.out.println(sb); } }; } }