Skip to content

Commit

Permalink
Add file handle leak detection (jenkinsci#5612)
Browse files Browse the repository at this point in the history
Co-Authored-By: Jeff Thompson <[email protected]>

Co-authored-by: Jeff Thompson <[email protected]>
  • Loading branch information
Wadeck and jeffret-b authored Aug 15, 2021
1 parent 95d9b55 commit fd133da
Showing 1 changed file with 83 additions and 0 deletions.
83 changes: 83 additions & 0 deletions test/src/test/java/hudson/model/DirectoryBrowserSupportTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.gargoylesoftware.htmlunit.UnexpectedPage;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.util.NameValuePair;
import com.sun.management.UnixOperatingSystemMXBean;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.ExtensionList;
import hudson.FilePath;
Expand Down Expand Up @@ -62,6 +63,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
Expand All @@ -73,6 +76,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
Expand Down Expand Up @@ -210,6 +214,85 @@ public void zipDownload() throws Exception {
zipfile.delete();
}

@Test
public void zipDownloadFileLeakMx_hypothesis() throws Exception {
// this test is meant to just ensure zipDownloadFileLeakMx hypothesis about the UI work fine

String content = "Hello world!";
FreeStyleProject p = j.createFreeStyleProject();
p.setScm(new SingleFileSCM("artifact.out", content));
p.getPublishersList().add(new ArtifactArchiver("*", "", true));
assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult());

HtmlPage page = j.createWebClient().goTo("job/" + p.getName() + "/lastSuccessfulBuild/artifact/");
Page downloadPage = page.getAnchorByHref("artifact.out").click();
assertEquals(content, downloadPage.getWebResponse().getContentAsString());
}

@Test
@Issue({"JENKINS-64632", "JENKINS-61121"})
public void zipDownloadFileLeakMx() throws Exception {
Assume.assumeFalse(Functions.isWindows());

int numOfClicks = 10;
int totalRuns = 10;
boolean freeFromLeak = false;
long[][] openFds = new long[totalRuns][2];
for (int runs = 0; runs < totalRuns && !freeFromLeak; runs++) {
long initialOpenFds = getOpenFdCount();
FreeStyleProject p = j.createFreeStyleProject();

// add randomness just to prevent any potential caching issue
p.setScm(new SingleFileSCM("artifact.out", "Hello world! " + Math.random()));
p.getPublishersList().add(new ArtifactArchiver("*", "", true));
assertEquals(Result.SUCCESS, p.scheduleBuild2(0).get().getResult());

HtmlPage page = j.createWebClient().goTo("job/" + p.getName() + "/lastSuccessfulBuild/artifact/");
for (int clicks = 0; clicks < numOfClicks; clicks++) {
page.getAnchorByHref("artifact.out").click();
}
long finalOpenFds = getOpenFdCount();

if (finalOpenFds < initialOpenFds + numOfClicks) {
// when there was a file leak, the number of open file handle was always
// greater or equal to the number of download
// in reverse, since the correction, the likelihood to overpass the limit was less than 1%
freeFromLeak = true;
}

openFds[runs][0] = initialOpenFds;
openFds[runs][1] = finalOpenFds;
}

List<String> messages = new ArrayList<>();
Map<Long, Long> differences = new TreeMap<>();
for (int runs = 0; runs < totalRuns; runs++) {
long difference = openFds[runs][1] - openFds[runs][0];
Long storedDifference = differences.get(difference);
if (storedDifference == null) {
differences.put(difference, 1L);
} else {
differences.put(difference, ++storedDifference);
}
messages.add("Initial=" + openFds[runs][0] + ", Final=" + openFds[runs][1] + ", difference=" + difference);
}
for (Long difference : differences.keySet()) {
messages.add("Difference=" + difference + " occurs " + differences.get(difference) + " times");
}

String summary = String.join("\n", messages);
System.out.println("Summary of the test: \n"+ summary);
assertTrue("There should be no difference greater than "+numOfClicks+", but the output was: \n"+ summary, freeFromLeak);
}

private long getOpenFdCount() {
OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
if(os instanceof UnixOperatingSystemMXBean){
return ((UnixOperatingSystemMXBean) os).getOpenFileDescriptorCount();
}
return -1;
}

@Issue("SECURITY-95")
@Test
public void contentSecurityPolicy() throws Exception {
Expand Down

0 comments on commit fd133da

Please sign in to comment.