Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: solven-eu/pepper
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 3647feac90c7a6d114375f4508369e222e3e80c3
Choose a base ref
..
head repository: solven-eu/pepper
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2bb05945d040bcf6fd89b287d9cc2f9b64e56f20
Choose a head ref
4 changes: 2 additions & 2 deletions java/pom.xml
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@

<parent>
<groupId>io.github.solven-eu.pepper</groupId>
<artifactId>aggregator-pepper</artifactId>
<artifactId>pepper-aggregator</artifactId>
<version>5.1-SNAPSHOT</version>
</parent>

@@ -45,7 +45,7 @@
<!--https://github.com/wrandelshofer/FastDoubleParser -->
<groupId>ch.randelshofer</groupId>
<artifactId>fastdoubleparser</artifactId>
<version>1.0.90</version>
<version>2.0.1</version>
<!-- Optional as this is used only by deprecated methods -->
<optional>true</optional>
</dependency>
Original file line number Diff line number Diff line change
@@ -26,21 +26,19 @@
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.annotations.Beta;

import lombok.extern.slf4j.Slf4j;

/**
* Wrap a {@link MappedByteBuffer} or an {@link IntBuffer} into {@link AutoCloseable}
*
* @author Benoit Lacelle
*
*/
@Beta
@Slf4j
public class CloseableIntBuffer implements AutoCloseable {
protected static final Logger LOGGER = LoggerFactory.getLogger(CloseableIntBuffer.class);

protected final MappedByteBuffer buffer;
protected final IntBuffer heapBuffer;

@@ -78,7 +76,7 @@ public static void closeBuffer(MappedByteBuffer buffer) {
} catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
// JDK9?
LOGGER.trace("Ouch", e);
log.trace("Ouch", e);
}
}

55 changes: 8 additions & 47 deletions java/src/main/java/eu/solven/pepper/jvm/GCInspector.java
Original file line number Diff line number Diff line change
@@ -81,7 +81,6 @@
import eu.solven.pepper.core.PepperLogHelper;
import eu.solven.pepper.jmx.PepperJMXHelper;
import eu.solven.pepper.memory.PepperMemoryHelper;
import eu.solven.pepper.system.PepperEnvHelper;
import eu.solven.pepper.system.PepperTimeHelper;
import eu.solven.pepper.thread.IThreadDumper;
import eu.solven.pepper.thread.PepperThreadDumper;
@@ -195,7 +194,7 @@ public class GCInspector implements NotificationListener, InitializingBean, Disp
/**
* Print the heap histogram only up to given % of total heap
*/
private static final int HEAP_HISTO_LIMIT_NB_ROWS = 20;
static final int HEAP_HISTO_LIMIT_NB_ROWS = 20;

public GCInspector(IThreadDumper apexThreadDumper) {
this.pepperThreadDumper = apexThreadDumper;
@@ -241,44 +240,12 @@ public void afterPropertiesSet() throws MalformedObjectNameException, InstanceNo

}

// We prefer to submit a closing status when the bean is disposed, as the JVM may never terminate correctly in case
// of OOM, or Dead/LiveLock
@Deprecated
protected void addShutdownHook() {
Runtime.getRuntime()
.addShutdownHook(
new Thread(this::executeDuringShutdown, this.getClass().getSimpleName() + "-ShutdownHook"));
}

protected void executeDuringShutdown() {
// On shutdown, do not print too many information as, very often, it is a clean closing (e.g. unit-tests).
// Still, if something is wrong, it is very beneficial to have core information

if (inUnitTest()) {
LOGGER.info("Skip GCInspector closing information as current run is a unit-test");
} else {
printSmartThreadDump();
printHeapHistogram(HEAP_HISTO_LIMIT_NB_ROWS);
}
}

/**
*
* @deprecated Use {@link PepperEnvHelper#inUnitTest()}
*/
@Deprecated
public static boolean inUnitTest() {
return PepperEnvHelper.inUnitTest();
}

/**
* Clean the MBean registration. Else, unit-test would register several GCInexpector (one for each Context loaded)
*/
@Override
public void destroy() throws Exception {
removeNotificationListener();

executeDuringShutdown();
}

protected void removeNotificationListener() throws MalformedObjectNameException, ListenerNotFoundException {
@@ -290,7 +257,11 @@ protected void removeNotificationListener() throws MalformedObjectNameException,
MBEAN_SERVER.removeNotificationListener(name, this);
} catch (InstanceNotFoundException | RuntimeException e) {
// Log in debug as no big-deal to fail disconnecting beans
LOGGER.debug("Failure for " + name, e);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Failure for {}", name, e);
} else {
LOGGER.info("Failure for {}", name);
}
}
}
}
@@ -795,20 +766,10 @@ protected void printThreadDump() {
LOGGER.warn("Thread Dump: {}", threadDumpAsString);
}

protected void printSmartThreadDump() {
LocalDateTime beforeThreadDump = LocalDateTime.now();

String threadDumpAsString = pepperThreadDumper.getSmartThreadDumpAsString(false);

this.latestThreadDump.set(beforeThreadDump);

LOGGER.info("Thread Dump: {}", threadDumpAsString);
}

// https://github.com/javamelody/javamelody/blob/master/javamelody-core/src/main/java/net/bull/javamelody/internal/model/VirtualMachine.java#L163
protected void printHeapHistogram(int nbRows) {
String threadDumpAsString = "";
LOGGER.debug("HeapHistogram: {}{}", System.lineSeparator(), threadDumpAsString);
String threadDumpAsString = "HeapHistogram is Disabled";
LOGGER.info("HeapHistogram: {}{}", System.lineSeparator(), threadDumpAsString);
}

/**
74 changes: 74 additions & 0 deletions java/src/main/java/eu/solven/pepper/jvm/LogThreadsOnShutdown.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* The MIT License
* Copyright (c) 2025 Benoit Lacelle - SOLVEN
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package eu.solven.pepper.jvm;

import eu.solven.pepper.system.PepperEnvHelper;
import eu.solven.pepper.thread.IThreadDumper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
* This can be useful when one want to understand what prevented a nice shutdown, or what processes were interrupted by
* the shutdown.
*
* @author Benoit Lacelle
*
*/
@RequiredArgsConstructor
@Slf4j
public class LogThreadsOnShutdown {

final IThreadDumper pepperThreadDumper;

// We prefer to submit a closing status when the bean is disposed, as the JVM may never terminate correctly in case
// of OOM, or Dead/LiveLock
@Deprecated
protected void addShutdownHook() {
Runtime.getRuntime()
.addShutdownHook(
new Thread(this::executeDuringShutdown, this.getClass().getSimpleName() + "-ShutdownHook"));
}

@SuppressWarnings("PMD.AvoidSynchronizedStatement")
protected void executeDuringShutdown() {
// synchronized as this may be called by Bean `.destroy` or by JVM shutdown
// so we try to prevent these long logs to interleave
synchronized (this) {
// On shutdown, do not print too many information as, very often, it is a clean closing (e.g. unit-tests).
// Still, if something is wrong, it is very beneficial to have core information

if (PepperEnvHelper.inUnitTest()) {
log.info("Skip GCInspector closing information as current run is a unit-test");
} else {
printSmartThreadDump();
// printHeapHistogram(GCInspector.HEAP_HISTO_LIMIT_NB_ROWS);
}
}
}

protected void printSmartThreadDump() {
String threadDumpAsString = pepperThreadDumper.getSmartThreadDumpAsString(false);

log.info("Thread Dump: {}", threadDumpAsString);
}
}
5 changes: 0 additions & 5 deletions java/src/test/java/eu/solven/pepper/jvm/TestGCInspector.java
Original file line number Diff line number Diff line change
@@ -95,11 +95,6 @@ public void testReporter() throws Exception {
gcInspector.destroy();
}

@Test
public void testDetectUnitTest() {
Assertions.assertTrue(GCInspector.inUnitTest());
}

@Test
public void testGetThreadNameAllocatedHeap() {
GCInspector gcInspector = new GCInspector(Mockito.mock(IThreadDumper.class));
Original file line number Diff line number Diff line change
@@ -31,4 +31,5 @@ public class TestPepperEnvHelper {
public void testDetectUnitTest() {
Assertions.assertTrue(PepperEnvHelper.inUnitTest());
}

}
13 changes: 12 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
@@ -144,14 +144,20 @@
</dependencies>
</dependencyManagement>

<!-- dependencies AFTER dependencyManagement -->
<dependencies>
<!-- Always added, to facilitate logging, as it is used by all modules -->
<!-- Do not add logback, as the library user may prefer another logging framework -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
@@ -179,6 +185,11 @@
<artifactId>guava-beta-checker</artifactId>
<version>${betachecker.version}</version>
</path>

<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
</annotationProcessorPaths>

<!-- Remove these compilerArgs to keep all checks enabled -->