Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dump leak report on a schedule #54

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 29 additions & 5 deletions src/main/java/org/kohsuke/file_leak_detector/AgentMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipFile;

import org.kohsuke.args4j.CmdLineException;
Expand All @@ -49,13 +52,16 @@
*/
@SuppressWarnings("Since15")
public class AgentMain {

private static ScheduledExecutorService executorService;

public static void agentmain(String agentArguments, Instrumentation instrumentation) throws Exception {
premain(agentArguments,instrumentation);
}

public static void premain(String agentArguments, Instrumentation instrumentation) throws Exception {
int serverPort = -1;

if(agentArguments!=null) {
// used by Main to prevent the termination of target JVM
boolean quit = true;
Expand Down Expand Up @@ -95,9 +101,26 @@ public static void premain(String agentArguments, Instrumentation instrumentatio
@Override
public void run() {
Listener.dump(System.err);
executorService.shutdownNow();
}
});
} else
if(t.startsWith("dumpatinterval=")) {
String value = t.substring(15);
try {
int interval = Integer.parseInt(value, 10);
if (interval < 1 ) {
System.err.println("Invalid number for interval - ignoring no scheduled dumps");
} else {
executorService = Executors.newSingleThreadScheduledExecutor();
Runnable runnable = new ScheduledDump();
executorService.scheduleAtFixedRate(runnable, 0, interval, TimeUnit.SECONDS);
}
} catch (NumberFormatException e) {
System.err.println("Invalid number for interval - ignoring no scheduled dumps");
// ignore invalid setting
}
} else
if(t.startsWith("excludes=")) {
BufferedReader reader = new BufferedReader(new FileReader(t.substring(9)));
try {
Expand Down Expand Up @@ -133,7 +156,7 @@ public void run() {

Listener.AGENT_INSTALLED = true;
instrumentation.addTransformer(new TransformerImpl(createSpec()),true);

instrumentation.retransformClasses(
FileInputStream.class,
FileOutputStream.class,
Expand Down Expand Up @@ -210,6 +233,7 @@ static void printOptions() {
System.err.println(" strong - Don't let GC auto-close leaking file descriptors");
System.err.println(" listener=S - Specify the fully qualified name of ActivityListener class to activate from beginning");
System.err.println(" dumpatshutdown- Dump open file handles at shutdown");
System.err.println(" dumpatinterval- Dump open file handles at interval in seconds after threshold is reached");
System.err.println(" excludes=FILE - Ignore files opened directly/indirectly in specific methods.");
System.err.println(" File lists 'some.pkg.ClassName.methodName' patterns.");
}
Expand Down Expand Up @@ -398,7 +422,7 @@ private OpenInterceptionAdapter(MethodVisitor base, int access, String desc) {
* Decide if this is the method that needs interception.
*/
protected abstract boolean toIntercept(String owner, String name);

protected Class<? extends Exception> getExpectedException() {
return IOException.class;
}
Expand All @@ -407,7 +431,7 @@ protected Class<? extends Exception> getExpectedException() {
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if(toIntercept(owner,name)) {
Type exceptionType = Type.getType(getExpectedException());

CodeGenerator g = new CodeGenerator(mv);
Label s = new Label(); // start of the try block
Label e = new Label(); // end of the try block
Expand Down
35 changes: 35 additions & 0 deletions src/main/java/org/kohsuke/file_leak_detector/ScheduledDump.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.kohsuke.file_leak_detector;

import java.util.concurrent.Callable;

public class ScheduledDump implements Runnable {
private int threshold;

public ScheduledDump(){
this.threshold = Listener.THRESHOLD;
}
/**
* When an object implementing interface <code>Runnable</code> is used to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may take any action whatsoever.
*
* @see Thread#run()
*/
@Override
public void run() {
// check if size is greater than threshold
int size = Listener.getCurrentOpenFiles().size();
if(Listener.TRACE!=null) {
System.err.println("Current size is " + size + " Threshold is " + threshold);
}
// if yes then call dump
if (threshold != 999999 && size > threshold) {
// if threshold was set use it in decision to dump
Listener.dump(Listener.ERROR);
} else if (threshold == 999999 ){
Listener.dump(Listener.ERROR);
}
}
}