Skip to content
Permalink
Browse files

[JENKINS-39150] expose diagnostics across all the channels

To be used by support-core, we need to be able to enumerate all active
channels. We do this via WeakHashMap so that references get
automatically garbage collected.

Unclosed channel will remain in memory forever, which also helps us find
those leaks.
  • Loading branch information
kohsuke committed Oct 20, 2016
1 parent 0c7df25 commit 522a022ae961d31b7b4346f90d04c3d08115e7f7
Showing with 72 additions and 0 deletions.
  1. +60 −0 src/main/java/hudson/remoting/Channel.java
  2. +12 −0 src/test/java/hudson/remoting/ChannelTest.java
@@ -45,6 +45,7 @@
import java.lang.ref.WeakReference;
import java.net.URL;
import java.util.Collections;
import java.util.Date;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
@@ -204,8 +205,32 @@
*/
private final Vector<Listener> listeners = new Vector<Listener>();
private int gcCounter;

/**
* Number of {@link Command} objects sent to the other side.
*/
private int commandsSent;

/**
* Number of {@link Command} objects received from the other side.
*
* When a transport is functioning correctly, {@link #commandsSent} of one side
* and {@link #commandsReceived} of the other side should closely match.
*/
private int commandsReceived;

/**
* Timestamp of the last {@link Command} object sent/received, in
* {@link System#currentTimeMillis()} format.
*/
private long lastCommandSent, lastCommandReceived;

/**
* Timestamp of when this channel was connected/created, in
* {@link System#currentTimeMillis()} format.
*/
private final long createdAt = System.currentTimeMillis();

/**
* Total number of nanoseconds spent for remote class loading.
* <p>
@@ -494,6 +519,8 @@ protected Channel(ChannelBuilder settings, CommandTransport transport) throws IO

transport.setup(this, new CommandReceiver() {
public void handle(Command cmd) {
commandsReceived++;
lastCommandReceived = System.currentTimeMillis();
updateLastHeard();
if (logger.isLoggable(Level.FINE))
logger.fine("Received " + cmd);
@@ -509,6 +536,7 @@ public void terminate(IOException e) {
Channel.this.terminate(e);
}
});
ACTIVE_CHANNELS.put(this,ref());
}

/**
@@ -581,6 +609,7 @@ private ExecutorService createPipeWriterExecutor() {

transport.write(cmd, cmd instanceof CloseCommand);
commandsSent++;
lastCommandSent = System.currentTimeMillis();
}

/**
@@ -1148,6 +1177,20 @@ public void dumpPerformanceCounters(PrintWriter w) throws IOException {
w.printf(Locale.ENGLISH, "Resource loading time=%,dms%n", resourceLoadingTime.get() / (1000 * 1000));
}

/**
* Print the diagnostic information.
*/
public void dumpDiagnostics(PrintWriter w) throws IOException {
w.printf("Channel %s%n",name);
w.printf(" Created=%s%n", new Date(createdAt));
w.printf(" Commands sent=%d%n", commandsSent);
w.printf(" Commands received=%d%n", commandsReceived);
w.printf(" Last command sent=%s%n", new Date(lastCommandSent));
w.printf(" Last command received=%s%n", new Date(lastCommandReceived));
w.printf(" Pending outgoing calls=%d%n", pendingCalls.size());
w.printf(" Pending incoming calls=%d%n", pendingCalls.size());
}

/**
* {@inheritDoc}
*/
@@ -1498,6 +1541,18 @@ public static Channel current() {
return CURRENT.get();
}

/**
* Calls {@link #dumpDiagnostics(PrintWriter)} across all the active channels in this system.
* Used for diagnostics.
*/
public static void dumpDiagnosticsForAll(PrintWriter w) throws IOException {
for (Ref ref : ACTIVE_CHANNELS.values()) {
Channel ch = ref.channel();
if (ch!=null)
ch.dumpDiagnostics(w);
}
}

/**
* Remembers the current "channel" associated for this thread.
*/
@@ -1522,6 +1577,11 @@ public static Channel current() {
*/
public static final int PIPE_WINDOW_SIZE = Integer.getInteger(Channel.class.getName()+".pipeWindowSize",1024*1024);

/**
* Keep track of active channels in the system for diagnostics purposes.
*/
private static final Map<Channel,Ref> ACTIVE_CHANNELS = Collections.synchronizedMap(new WeakHashMap<Channel, Ref>());

static final Class jarLoaderProxy;

static {
@@ -5,7 +5,9 @@

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.TimeUnit;
@@ -163,4 +165,14 @@ public T call() throws RuntimeException {
return t;
}
}

public void testDiagnostics() throws Exception {
StringWriter sw = new StringWriter();
Channel.dumpDiagnosticsForAll(new PrintWriter(sw));
System.out.println(sw);
assertTrue(sw.toString().contains("Channel north"));
assertTrue(sw.toString().contains("Channel south"));
assertTrue(sw.toString().contains("Commands sent=0"));
assertTrue(sw.toString().contains("Commands received=0"));
}
}

0 comments on commit 522a022

Please sign in to comment.
You can’t perform that action at this time.