From 4275c98b22bdcf146e1d128616f132b61ea97c5a Mon Sep 17 00:00:00 2001 From: Vaclav Haisman Date: Sun, 19 Jul 2026 23:11:28 +0200 Subject: [PATCH 1/2] Run lib.profiler unit tests in CI Assisted-by: OpenAI GPT-5 Codex --- .github/workflows/main.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 56fa5c9e6488..4c990a09b55c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1655,6 +1655,10 @@ jobs: - name: Extract run: tar --zstd -xf build.tar.zst + - name: lib.profiler + # test-unit's build dependency also compiles this module's legacy QA-functional sources. + run: ant $OPTS -f profiler/lib.profiler test-unit -Ddisable.qa-functional.tests=true + - name: profiler run: ant $OPTS -f profiler/profiler test-unit From d9a1a8f1acbba7e9a8f0e16618ac71072b79536c Mon Sep 17 00:00:00 2001 From: Vaclav Haisman Date: Sun, 19 Jul 2026 23:11:45 +0200 Subject: [PATCH 2/2] Stabilize lib.profiler heap dump output Assisted-by: OpenAI GPT-5 Codex --- .../netbeans/lib/profiler/heap/HeapTest.java | 14 +- .../lib/profiler/heap/heapDumpLog.txt | 83 ++++++----- .../cpu/StackTraceSnapshotBuilderTest.java | 139 +++++++++--------- 3 files changed, 121 insertions(+), 115 deletions(-) diff --git a/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapTest.java b/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapTest.java index 4417aab474f3..88dbc05d9811 100644 --- a/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapTest.java +++ b/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/HeapTest.java @@ -19,8 +19,6 @@ package org.netbeans.lib.profiler.heap; import java.io.BufferedOutputStream; -import java.util.Map; -import java.util.Date; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -36,6 +34,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -45,6 +44,7 @@ import java.util.Properties; import java.util.Set; import java.util.TimeZone; +import java.util.TreeSet; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -248,10 +248,12 @@ public void testHeapDumpLog() throws IOException, URISyntaxException { Collection classes = heap.getAllClasses(); out.println("Classes size " + classes.size()); out.println("System properties: "); - for (Object en : heap.getSystemProperties().entrySet()) { - Map.Entry entry = (Map.Entry) en; - - out.println(entry.getKey() + " " + entry.getValue()); + Properties properties = heap.getSystemProperties(); + for (String key : new TreeSet<>(properties.stringPropertyNames())) { + String value = properties.getProperty(key) + .replace("\r", "\\r") + .replace("\n", "\\n"); + out.println(key + (value.isEmpty() ? "" : " " + value)); } for (Object c : classes) { JavaClass jc = (JavaClass) c; diff --git a/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/heapDumpLog.txt b/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/heapDumpLog.txt index 0788e3477148..ee29dbddda62 100644 --- a/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/heapDumpLog.txt +++ b/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/heap/heapDumpLog.txt @@ -6,59 +6,58 @@ Total alloc instances -1 Total alloc bytes -1 Classes size 474 System properties: -java.runtime.name OpenJDK Runtime Environment -java.vm.version 25.151-b12 -sun.boot.library.path /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64 -java.vendor.url http://java.oracle.com/ -java.vm.vendor Oracle Corporation -path.separator : +awt.toolkit sun.awt.X11.XToolkit +file.encoding UTF-8 file.encoding.pkg sun.io -java.vm.name OpenJDK 64-Bit Server VM -sun.os.patch.level unknown -sun.java.launcher SUN_STANDARD -user.country US -user.dir /home/hector/Developments/Netbeans/incubator-netbeans/lib.profiler/test/qa-functional/data/projects/j2se-simple -java.vm.specification.name Java Virtual Machine Specification -java.runtime.version 1.8.0_151-8u151-b12-0ubuntu0.16.04.2-b12 +file.separator / java.awt.graphicsenv sun.awt.X11GraphicsEnvironment -os.arch amd64 +java.awt.printerjob sun.print.PSPrinterJob +java.class.path /home/hector/Developments/Netbeans/incubator-netbeans/lib.profiler/test/qa-functional/data/projects/j2se-simple/build/classes +java.class.version 52.0 java.endorsed.dirs /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/endorsed +java.ext.dirs /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext:/usr/java/packages/lib/ext +java.home /usr/lib/jvm/java-8-openjdk-amd64/jre java.io.tmpdir /tmp -line.separator - -java.vm.specification.vendor Oracle Corporation -os.name Linux -sun.jnu.encoding UTF-8 java.library.path /usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/amd64:/usr/lib/jvm/java-1.8.0-openjdk-amd64/jre/lib/i386::/usr/java/packages/lib/amd64:/usr/lib/x86_64-linux-gnu/jni:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/usr/lib/jni:/lib:/usr/lib +java.runtime.name OpenJDK Runtime Environment +java.runtime.version 1.8.0_151-8u151-b12-0ubuntu0.16.04.2-b12 java.specification.name Java Platform API Specification -java.class.version 52.0 -sun.management.compiler HotSpot 64-Bit Tiered Compilers -os.version 4.13.0-26-generic -user.home /home/hector -user.timezone -java.awt.printerjob sun.print.PSPrinterJob -file.encoding UTF-8 +java.specification.vendor Oracle Corporation java.specification.version 1.8 -user.name hector -java.class.path /home/hector/Developments/Netbeans/incubator-netbeans/lib.profiler/test/qa-functional/data/projects/j2se-simple/build/classes +java.vendor Oracle Corporation +java.vendor.url http://java.oracle.com/ +java.vendor.url.bug http://bugreport.sun.com/bugreport/ +java.version 1.8.0_151 +java.vm.info mixed mode +java.vm.name OpenJDK 64-Bit Server VM +java.vm.specification.name Java Virtual Machine Specification +java.vm.specification.vendor Oracle Corporation java.vm.specification.version 1.8 +java.vm.vendor Oracle Corporation +java.vm.version 25.151-b12 +line.separator \n +os.arch amd64 +os.name Linux +os.version 4.13.0-26-generic +path.separator : sun.arch.data.model 64 -sun.java.command simple.Monitor -java.home /usr/lib/jvm/java-8-openjdk-amd64/jre -user.language en -java.specification.vendor Oracle Corporation -awt.toolkit sun.awt.X11.XToolkit -java.vm.info mixed mode -java.version 1.8.0_151 -java.ext.dirs /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/ext:/usr/java/packages/lib/ext sun.boot.class.path /home/hector/Developments/Netbeans/incubator-netbeans/lib.profiler/test/qa-functional/data/projects/j2se-simple/${endorsed.classpath}:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/resources.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/rt.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/sunrsasign.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jsse.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jce.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/charsets.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/lib/jfr.jar:/usr/lib/jvm/java-8-openjdk-amd64/jre/classes -java.vendor Oracle Corporation -file.separator / -java.vendor.url.bug http://bugreport.sun.com/bugreport/ -sun.io.unicode.encoding UnicodeLittle +sun.boot.library.path /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64 sun.cpu.endian little +sun.cpu.isalist sun.desktop gnome -sun.cpu.isalist +sun.io.unicode.encoding UnicodeLittle +sun.java.command simple.Monitor +sun.java.launcher SUN_STANDARD +sun.jnu.encoding UTF-8 +sun.management.compiler HotSpot 64-Bit Tiered Compilers +sun.os.patch.level unknown +user.country US +user.dir /home/hector/Developments/Netbeans/incubator-netbeans/lib.profiler/test/qa-functional/data/projects/j2se-simple +user.home /home/hector +user.language en +user.name hector +user.timezone Id 0xd6ffd630 Class simple.Producer SuperClass java.lang.Thread Instance size 183 Instance count 2 All Instances Size 366 Static Field name type object value 3606861808 Ref object sun.misc.Launcher$AppClassLoader#1 @@ -43796,7 +43795,7 @@ sun.cpu.isalist Static Field name MIN_EXPONENT type int value -126 Static Field name MAX_EXPONENT type int value 127 Static Field name MIN_VALUE type float value 1.4E-45 - Static Field name MIN_NORMAL type float value 1.17549435E-38 + Static Field name MIN_NORMAL type float value 1.1754944E-38 Static Field name MAX_VALUE type float value 3.4028235E38 Static Field name NaN type float value NaN Static Field name NEGATIVE_INFINITY type float value -Infinity diff --git a/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/results/cpu/StackTraceSnapshotBuilderTest.java b/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/results/cpu/StackTraceSnapshotBuilderTest.java index 1824f94f74f6..9f2a4af68f95 100644 --- a/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/results/cpu/StackTraceSnapshotBuilderTest.java +++ b/profiler/lib.profiler/test/unit/src/org/netbeans/lib/profiler/results/cpu/StackTraceSnapshotBuilderTest.java @@ -22,20 +22,14 @@ import java.lang.management.ManagementFactory; import java.lang.management.ThreadInfo; import java.lang.management.ThreadMXBean; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; import java.util.Collections; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.management.openmbean.CompositeData; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import org.netbeans.lib.profiler.filters.InstrumentationFilter; import org.netbeans.lib.profiler.results.CCTNode; -import sun.management.ThreadInfoCompositeData; import static org.junit.Assert.*; @@ -82,11 +76,11 @@ public class StackTraceSnapshotBuilderTest { private Thread thread1; private Thread thread2; - private java.lang.management.ThreadInfo[] stack0; - private java.lang.management.ThreadInfo[] stackPlus; - private java.lang.management.ThreadInfo[] stackMinus; - private java.lang.management.ThreadInfo[] stackDif; - private java.lang.management.ThreadInfo[] stackDup; + private ThreadSample[] stack0; + private ThreadSample[] stackPlus; + private ThreadSample[] stackMinus; + private ThreadSample[] stackDif; + private ThreadSample[] stackDup; public StackTraceSnapshotBuilderTest() { @@ -108,28 +102,28 @@ public void setUp() { thread1 = new Thread("Test thread 1"); thread2 = new Thread("Test thread 2"); - stack0 = new java.lang.management.ThreadInfo[] { - createThreadInfo(thread0, elements0), - createThreadInfo(thread1, elements0) + stack0 = new ThreadSample[] { + createThreadSample(thread0, elements0), + createThreadSample(thread1, elements0) }; - stackPlus = new java.lang.management.ThreadInfo[] { - createThreadInfo(thread0, elementsPlus), - createThreadInfo(thread1, elements0), - createThreadInfo(thread2, elements0) + stackPlus = new ThreadSample[] { + createThreadSample(thread0, elementsPlus), + createThreadSample(thread1, elements0), + createThreadSample(thread2, elements0) }; - stackMinus = new java.lang.management.ThreadInfo[] { - createThreadInfo(thread0, elementsMinus) + stackMinus = new ThreadSample[] { + createThreadSample(thread0, elementsMinus) }; - stackDif = new java.lang.management.ThreadInfo[] { - createThreadInfo(thread0, elementsDif) + stackDif = new ThreadSample[] { + createThreadSample(thread0, elementsDif) }; - stackDup = new java.lang.management.ThreadInfo[] { - createThreadInfo(thread0, elementsDup), - createThreadInfo(thread1, elements0) + stackDup = new ThreadSample[] { + createThreadSample(thread0, elementsDup), + createThreadSample(thread1, elements0) }; } @@ -274,6 +268,19 @@ public void testAddStacktrace() { assertFalse(-1L == instance.currentDumpTimeStamp); } + @Test + public void testAddStacktraceThreadInfo() { + ThreadMXBean tbean = ManagementFactory.getThreadMXBean(); + ThreadInfo tinfo = tbean.getThreadInfo(Thread.currentThread().getId(), Integer.MAX_VALUE); + + assertNotNull(tinfo); + instance.addStacktrace(new ThreadInfo[] { tinfo }, 0); + + assertTrue(instance.threadIds.contains(tinfo.getThreadId())); + assertTrue(instance.threadNames.contains(tinfo.getThreadName())); + assertTrue(instance.lastStackTrace.get().containsKey(tinfo.getThreadId())); + } + @Test public void testAddStacktraceDuplicate() { System.out.println("add stacktrace : duplicate"); @@ -732,8 +739,8 @@ public void testAddStackTraceWaitBlocked() { public void testReset() { System.out.println("reset"); ThreadMXBean tbean = ManagementFactory.getThreadMXBean(); - addStacktrace(tbean.getThreadInfo(tbean.getAllThreadIds(), Integer.MAX_VALUE), System.nanoTime()); - addStacktrace(tbean.getThreadInfo(tbean.getAllThreadIds(), Integer.MAX_VALUE), System.nanoTime()); + instance.addStacktrace(tbean.getThreadInfo(tbean.getAllThreadIds(), Integer.MAX_VALUE), System.nanoTime()); + instance.addStacktrace(tbean.getThreadInfo(tbean.getAllThreadIds(), Integer.MAX_VALUE), System.nanoTime()); instance.reset(); assertTrue(instance.methodInfos.size()-1 == 0); @@ -761,51 +768,49 @@ public void testIgnoredThreadName() { assertFalse(instance.threadNames.contains(ignoredThread)); } - private java.lang.management.ThreadInfo createThreadInfo(Thread t, StackTraceElement[] stack) { - try { - Constructor tinfoConstructor = java.lang.management.ThreadInfo.class.getDeclaredConstructor( - Thread.class,Integer.TYPE,Object.class,Thread.class,Long.TYPE,Long.TYPE, - Long.TYPE,Long.TYPE,StackTraceElement[].class); - tinfoConstructor.setAccessible(true); - ThreadInfo tinfo = (ThreadInfo) tinfoConstructor.newInstance(t,0,null,null,0,0,0,0,stack); - setState(tinfo,State.RUNNABLE); - return tinfo; - } catch (NoSuchMethodException ex) { - Logger.getLogger(StackTraceSnapshotBuilderTest.class.getName()).log(Level.SEVERE, ex.getMessage(), ex); - } catch (InstantiationException ex) { - Logger.getLogger(StackTraceSnapshotBuilderTest.class.getName()).log(Level.SEVERE, ex.getMessage(), ex); - } catch (IllegalAccessException ex) { - Logger.getLogger(StackTraceSnapshotBuilderTest.class.getName()).log(Level.SEVERE, ex.getMessage(), ex); - } catch (IllegalArgumentException ex) { - Logger.getLogger(StackTraceSnapshotBuilderTest.class.getName()).log(Level.SEVERE, ex.getMessage(), ex); - } catch (InvocationTargetException ex) { - Logger.getLogger(StackTraceSnapshotBuilderTest.class.getName()).log(Level.SEVERE, ex.getMessage(), ex); - } - return null; + private ThreadSample createThreadSample(Thread t, StackTraceElement[] stack) { + return new ThreadSample(t.getName(), t.getId(), State.RUNNABLE, stack); } - private void setState(java.lang.management.ThreadInfo tinfo, State s) { - try { - Field tstateField = tinfo.getClass().getDeclaredField("threadState"); - tstateField.setAccessible(true); - tstateField.set(tinfo, s); - } catch (IllegalArgumentException ex) { - Logger.getLogger(StackTraceSnapshotBuilderTest.class.getName()).log(Level.SEVERE, null, ex); - } catch (IllegalAccessException ex) { - Logger.getLogger(StackTraceSnapshotBuilderTest.class.getName()).log(Level.SEVERE, null, ex); - } catch (NoSuchFieldException ex) { - Logger.getLogger(StackTraceSnapshotBuilderTest.class.getName()).log(Level.SEVERE, null, ex); + private void setState(ThreadSample tinfo, State s) { + tinfo.threadState = s; + } + + private void addStacktrace(ThreadSample[] tinfos, long time) { + StackTraceSnapshotBuilder.SampledThreadInfo[] samples = + new StackTraceSnapshotBuilder.SampledThreadInfo[tinfos.length]; + for (int i = 0; i < tinfos.length; i++) { + samples[i] = tinfos[i].toSampledThreadInfo(instance.getFilter()); } + instance.addStacktrace(samples, time); } - private void addStacktrace(java.lang.management.ThreadInfo[] tinfos, long time) { - java.lang.management.ThreadInfo[] newInfo = new java.lang.management.ThreadInfo[tinfos.length]; - int i = 0; + private static final class ThreadSample { + private final String threadName; + private final long threadId; + private State threadState; + private final StackTraceElement[] stackTrace; + + private ThreadSample(String threadName, long threadId, State threadState, + StackTraceElement[] stackTrace) { + this.threadName = threadName; + this.threadId = threadId; + this.threadState = threadState; + this.stackTrace = stackTrace; + } + + private long getThreadId() { + return threadId; + } + + private String getThreadName() { + return threadName; + } - for (java.lang.management.ThreadInfo tinfo : tinfos) { - CompositeData aaa = ThreadInfoCompositeData.toCompositeData(tinfo); - newInfo[i++] = ThreadInfo.from(aaa); + private StackTraceSnapshotBuilder.SampledThreadInfo toSampledThreadInfo( + InstrumentationFilter filter) { + return new StackTraceSnapshotBuilder.SampledThreadInfo( + threadName, threadId, threadState, stackTrace, filter); } - instance.addStacktrace(newInfo, time); } }