/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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 org.apache.geode.internal.util;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
/**
* Collects unique stack traces for later examination. This works best and is most useful when
* traces are added at the same point of origin. This will collect all unique code paths while the
* collector is turned on. This class will affect timing and performance so its use is suspect while
* diagnosing concurrency issues.
*
* Example: <code>
* public void doSomeWork() {
* // This will collect all unique code paths to this method.
* this.traceCollector.add(new Exception())
* }
* </code>
*
*
* @since Geode 1.0
*/
public class StackTraceCollector {
/**
* Maximum stack trace size.
*/
private static int MAX_TRACE_SIZE = 128 * 1024;
/**
* Contains unique stack traces.
*/
private Set<String> stackTraceSet = new ConcurrentSkipListSet<String>();
/**
* The {@link StackTraceCollector#add(Throwable)} method will do no work when this is false.
*/
private volatile boolean on = true;
/**
* Contains converted stack traces (Throwable -> String).
*/
private ByteArrayOutputStream ostream = new ByteArrayOutputStream(MAX_TRACE_SIZE);
/**
* Used to convert stack traces (Throwable -> String).
*/
private PrintWriter writer = new PrintWriter(this.ostream);
/**
* Returns true if this collector is on.
*/
public boolean isOn() {
return this.on;
}
/**
* Returns true if this collector is off.
*/
public boolean isOff() {
return !isOn();
}
/**
* Turns this collector on.
*/
public void on() {
this.on = true;
}
/**
* Turns this collector off.
*/
public void off() {
this.on = false;
}
/**
* Adds a stack trace to this collector if it is unique.
*
* @param throwable used to generate the stack trace.
* @return true if the stack trace was not already present in this collector. This will always
* return false if the collector is turned off.
*/
public boolean add(final Throwable throwable) {
return add(this.stackTraceSet, throwable);
}
/**
* Adds a stack trace to a collection Set if it is unique.
*
* @param throwable used to generate the stack trace.
* @return true if the stack trace was not already present in this collector. This will always
* return false if the collector is turned off.
*/
protected synchronized boolean add(Set<String> stackTraceSet, final Throwable throwable) {
if (this.on) {
String trace = convert(throwable);
return stackTraceSet.add(trace);
}
return false;
}
/**
* Clears this collector of all stack traces.
*
* @return true if there were stack traces to clear.
*/
public boolean clear() {
boolean value = (this.stackTraceSet.size() > 0);
this.stackTraceSet.clear();
return value;
}
/**
* Returns true if the throwable parameter is contained by this collector.
*
* @param throwable a stack trace to check.
*/
public boolean contains(final Throwable throwable) {
return contains(convert(throwable));
}
/**
* Returns true if the stackTrace parameter is contained by this collector.
*
* @param stackTrace a stack trace.
*/
public boolean contains(final String stackTrace) {
return this.stackTraceSet.contains(stackTrace);
}
/**
* Returns the stack trace associated with a Throwable as a String.
*
* @param throwable contains a stack trace to be extracted and returned.
*/
public synchronized String convert(final Throwable throwable) {
throwable.printStackTrace(this.writer);
this.writer.flush();
String stackTrace = this.ostream.toString();
this.ostream.reset();
return stackTrace;
}
/**
* Returns an unmodifiable Set of the unique stack traces contained by this collector.
*/
public Set<String> view() {
return Collections.unmodifiableSet(this.stackTraceSet);
}
}