From c39ca8dfbc2439bff3127c46e27b23af6017262d Mon Sep 17 00:00:00 2001 From: Devin Nusbaum Date: Wed, 10 Sep 2025 14:21:16 -0400 Subject: [PATCH 1/3] Update to standalone heaplib dependency --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 822e2b4..1bd1579 100644 --- a/pom.xml +++ b/pom.xml @@ -7,8 +7,8 @@ org.gridkit.jvmtool - hprof-heap - 0.8.1 + heaplib + 0.2 From 471296abe0c77fcba87c20fc11bf61cd425014ea Mon Sep 17 00:00:00 2001 From: Devin Nusbaum Date: Fri, 19 Sep 2025 13:12:10 -0400 Subject: [PATCH 2/3] https://github.com/jenkinsci/workflow-api-plugin/pull/370 --- src/main/java/Main.java | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/Main.java b/src/main/java/Main.java index bf99c80..d76a0ee 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -19,12 +19,25 @@ public static void main(String[] args) throws Exception { Heap heap = HeapFactory.createFastHeap(dump); System.err.println(); System.err.print("Looking for builds thought be running according to FlowExecutionList..."); - JavaClass felC = heap.getJavaClassByName("org.jenkinsci.plugins.workflow.flow.FlowExecutionList"); - List fels = felC.getInstances(); - if (fels.size() != 1) { - throw new IllegalStateException("unexpected FlowExecutionList count: " + fels); + Instance fel = null; + JavaClass feldsC = heap.getJavaClassByName("org.jenkinsci.plugins.workflow.flow.FlowExecutionList$DefaultStorage"); + if (feldsC != null) { + List feldss = feldsC.getInstances(); + if (feldss.size() != 1) { + throw new IllegalStateException("unexpected FlowExecutionList$DefaultStorage count: " + feldss); + } + fel = feldss.get(0); + } else { + JavaClass felC = heap.getJavaClassByName("org.jenkinsci.plugins.workflow.flow.FlowExecutionList"); + List fels = felC.getInstances(); + if (fels.size() != 1) { + throw new IllegalStateException("unexpected FlowExecutionList count: " + fels); + } + fel = fels.get(0); + } + if (fel == null) { + throw new IllegalStateException("FlowExecutionList custom storage not supported"); } - Instance fel = fels.get(0); Instance list = HeapWalker.valueOf(fel, "runningTasks.core"); // ArrayList ObjectArrayInstance elementsA = HeapWalker.valueOf(list, "elementData"); // TODO docs claim this would return Object[], but CONVERTERS does not actually do that List elements = elementsA.getValues(); From 257bd6e3e9fcd57c654a40084a1aa6eed34ee61c Mon Sep 17 00:00:00 2001 From: Devin Nusbaum Date: Fri, 19 Sep 2025 13:13:42 -0400 Subject: [PATCH 3/3] Print addresses as hex and include address of object related to WorkflowRun --- src/main/java/Main.java | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main/java/Main.java b/src/main/java/Main.java index d76a0ee..b0ba71f 100644 --- a/src/main/java/Main.java +++ b/src/main/java/Main.java @@ -45,7 +45,7 @@ public static void main(String[] args) throws Exception { Set listed = new TreeSet<>(); for (int i = 0; i < size; i++) { System.err.print("."); - Build b = Build.of(elements.get(i)); + Build b = Build.of(elements.get(i), fel); if (!listed.add(b)) { System.err.print("(duplicated " + b + ")"); } @@ -61,7 +61,7 @@ public static void main(String[] args) throws Exception { if (owner == null) { continue; // running something else, fine } - Build b = Build.of(owner); + Build b = Build.of(owner, ooe); if (listed.contains(b)) { continue; // actually running, fine } @@ -77,7 +77,7 @@ public static void main(String[] args) throws Exception { for (Instance ctg : heap.getJavaClassByName("org.jenkinsci.plugins.workflow.cps.CpsThreadGroup").getInstances()) { System.err.print("."); Instance owner = HeapWalker.valueOf(ctg, "execution.owner"); - Build b = Build.of(owner); + Build b = Build.of(owner, ctg); Integer scriptSize = HeapWalker.valueOf(ctg, "scripts.size"); // TreeMap if (scriptSize == null) { System.err.print("(possibly broken " + b + ")"); @@ -130,7 +130,7 @@ public static void main(String[] args) throws Exception { Set leaked = new TreeSet<>(); for (Instance cgstl : heap.getJavaClassByName("org.jenkinsci.plugins.workflow.cps.CpsGroovyShell$TimingLoader").getInstances()) { System.err.print("."); - Build b = Build.of(HeapWalker.valueOf(cgstl, "execution.owner")); + Build b = Build.of(HeapWalker.valueOf(cgstl, "execution.owner"), cgstl); if (listed.contains(b)) { continue; // actually running, fine } @@ -144,24 +144,31 @@ public static void main(String[] args) throws Exception { leaked.forEach(System.err::println); } static class Build implements Comparable { - static Build of(Instance owner) { // WorkflowRun$Owner + static Build of(Instance owner, Instance related) { // WorkflowRun$Owner Instance run = HeapWalker.valueOf(owner, "run"); - return new Build(HeapWalker.valueOf(owner, "job"), Integer.parseInt(HeapWalker.valueOf(owner, "id")), run == null ? null : run.getInstanceId()); + return new Build( + HeapWalker.valueOf(owner, "job"), + Integer.parseInt(HeapWalker.valueOf(owner, "id")), + run == null ? null : run.getInstanceId(), + related == null ? null : related.getInstanceId()); } final String job; final int num; final Long id; // Heap address - Build(String job, int num, Long id) { + final Long relatedId; // Heap address of whatever object was used to find the WorkflowRun object + Build(String job, int num, Long id, Long relatedId) { this.job = job; this.num = num; this.id = id; + this.relatedId = relatedId; } @Override public int compareTo(Build o) { int c = job.compareTo(o.job); return c != 0 ? c : num - o.num; } @Override public String toString() { - return job + " #" + num + (id == null ? "" : "@" + id); + String relatedString = relatedId == null ? "" : " (via: 0x" + Long.toHexString(relatedId) + ")"; + return job + " #" + num + (id == null ? "" : "@0x" + Long.toHexString(id)) + relatedString; } @Override public int hashCode() { return job.hashCode() ^ num;