Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
5e9f11d
Added new dependencies for storage measurements (#23).
HansMartinA Jul 26, 2023
3c86d0d
Extended the performance data with storage data (#23).
HansMartinA Jul 26, 2023
2ed8d2b
Extracted and generalized the output of Java models in the XMI format…
HansMartinA Jul 27, 2023
f477804
Extended the performance test to also calculate the taken storage for…
HansMartinA Jul 30, 2023
e1b27f7
The JDT parser only resolves bindings if the JaMoPP parser option is …
HansMartinA Jul 30, 2023
5242d48
The trivial recovery creates an artificial Object class if it cannot …
HansMartinA Jul 30, 2023
4ef98d5
The class file parser creates blocks for methods and constructors (#23).
HansMartinA Jul 31, 2023
e269a38
The trivial recovery also recovers EnumConstants (#23).
HansMartinA Jul 31, 2023
1e06e87
Added the trivial recovery to the performance test (#23).
HansMartinA Aug 2, 2023
f077fbf
Updated the CHANEGLOG (#23).
HansMartinA Aug 2, 2023
57a7eb2
Added some null checks (#23).
HansMartinA Aug 4, 2023
7c31de2
Added some length checks when printing the name of a module or packag…
HansMartinA Aug 12, 2023
e393762
Preventing OutOfMemoryErrors by deleting all proxy objects after ever…
HansMartinA Aug 12, 2023
c44f0c7
Added Apache Commons Math for statistical calculations (#23).
HansMartinA Oct 5, 2023
758b7ee
The overall average and standard deviation are calculated (#23).
HansMartinA Oct 5, 2023
d35da40
Added JGit as dependency for step-wise executions (#23).
HansMartinA Oct 5, 2023
fb76077
During the trivial recovery, an empty name for packages and the artif…
HansMartinA Mar 31, 2024
c2b7f6e
Enum constants are also recovered by the trivial recovery.
HansMartinA Apr 3, 2024
712457c
Merge remote-tracking branch 'origin/23_performance_measurements' int…
HansMartinA Jan 2, 2026
e405e26
Added a data structure for storing results of a stepwise performance …
HansMartinA Feb 2, 2026
67e436b
Moved the number of repetitions to a separate method so that it can b…
HansMartinA Mar 2, 2026
621c968
Extended the OutputUtility, which could be also used outside of tests…
HansMartinA Mar 3, 2026
2a9f451
Added an executor for a step-wise performance test, and adjusted the …
HansMartinA Mar 3, 2026
3bbe8ac
The performance tests are ignored for the test execution.
HansMartinA Mar 5, 2026
7622c17
After all performance tests are executed, statistics are calculated a…
HansMartinA Mar 5, 2026
b05508d
Added a performance test, which uses a reduced number of repetitions …
HansMartinA Mar 5, 2026
df45387
Moved the performance test-related classes to the main folder (#23).
HansMartinA Mar 9, 2026
bb85e7b
Added a simple main class for standalone execution of the performance…
HansMartinA Mar 12, 2026
7aa4f98
Added a chart library to generate charts for the performance test res…
HansMartinA Mar 12, 2026
b1d705b
A chart with the overall time is also generated (#23).
HansMartinA Mar 12, 2026
df53ff6
Charts are generated ffor the step-wise performance test (#23).
HansMartinA Mar 15, 2026
6c1220c
Fixed the step-wise performance test executor: the parser options are…
HansMartinA Mar 16, 2026
2efddea
The OutputUtility assumes an absolute path for the output directory (…
HansMartinA Mar 16, 2026
3f7fa7c
Extended the main class to execute different performance tests via CL…
HansMartinA Mar 16, 2026
9bd8893
The performance JUnit tests use the main classes (#23).
HansMartinA Mar 16, 2026
5962d2b
Added micrometer as dependency to monitor the JVM heap size (#23).
HansMartinA Apr 13, 2026
4f96cf9
Added a StepMeterRegistry for storing JVM heap size measurements (#23).
HansMartinA Apr 13, 2026
bc0d359
Added and integrated a monitor for the used heap memory (#23).
HansMartinA Apr 24, 2026
fd8521c
Merge pull request #32 from MDSD-Tools/validation-fixes
HansMartinA Jul 17, 2026
c1777dc
The test jar can be executed (#23).
HansMartinA Aug 10, 2026
5f77db1
Added a docker file for a JDK 17, which will include the src.zip dire…
HansMartinA Aug 10, 2026
ae93023
Added a docker file for the performance tests (#23).
HansMartinA Aug 10, 2026
158857f
Corrected the start of the jar file in the Docker image so that argum…
HansMartinA Aug 17, 2026
2a49f81
Added an execution script and README for the Docker files (#23).
HansMartinA Aug 17, 2026
ddac5d3
Merge remote-tracking branch 'official/23_performance_measurements' i…
HansMartinA Aug 17, 2026
972bd1e
Removed duplicated code after the last merge (#23).
HansMartinA Aug 20, 2026
25c42f9
Fixed minor formatting issues (#23).
HansMartinA Aug 20, 2026
314b951
Minor improvements in the error handling (#23).
HansMartinA Aug 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

- Trivial recovery strategy to generate model elements for unresolved proxy objects
- Parser: `TextBlock`s are converted to `TextBockReference`s so that model elements are generated for text blocks
- Performance Test:
- Performs trivial recovery
- Measures model storage

### Changed

Expand All @@ -34,6 +37,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
### Fixed

- First variant: always returns an empty model (temporary fix to not end in an endless loop)
- Class file parser: creates bodies for methods and constructors

### Security

Expand Down
31 changes: 31 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
FROM eclipse-temurin:11.0.31_11-jdk-alpine-3.23 AS teastore-build

COPY . /etc/jamopp
WORKDIR /etc/jamopp
RUN apk update &&\
apk add git &&\
git submodule init &&\
git submodule update &&\
apk del git
WORKDIR /etc/jamopp/jamopp.tests/target/src-bulk/TeaStore
RUN cp /etc/jamopp/mvnw ./mvnw &&\
cp -r /etc/jamopp/.mvn ./.mvn &&\
./mvnw install -Dmaven.test.skip=true &&\
./mvnw dependency:copy-dependencies &&\
rm -r ./.mvn &&\
rm ./mvnw

FROM jdk-17-src AS build

COPY . /etc/jamopp
WORKDIR /etc/jamopp
RUN ./mvnw package -Dmaven.test.skip=true

FROM jdk-17-src

COPY --from=build /etc/jamopp/jamopp.tests/target/jamopp.tests-6.0.0-SNAPSHOT.jar /app/jamopp.tests-6.0.0-SNAPSHOT.jar
COPY --from=build /etc/jamopp/jamopp.tests/target/dependency /app/dependency/
COPY --from=teastore-build /etc/jamopp/jamopp.tests/target/src-bulk/TeaStore /app/jamopp.tests/target/src-bulk/TeaStore/

WORKDIR /app
ENTRYPOINT ["java", "-jar", "/app/jamopp.tests-6.0.0-SNAPSHOT.jar"]
14 changes: 14 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Docker Files for JaMoPP Performance Tests

This directory contains Docker files to build and execute the JaMoPP performance tests in Docker.

* It currently supports **Linux only**.
* We provide the `execute.sh` script, which executes the relevant Docker commands for building and executing the Docker image.
* For execution, the current working directory must point to this directory.
* The resources for the executed Docker container are limited to *4 CPU cores* and *16 GB RAM*. If you want to decrease or increase these limits, you can change them directly in the `execute.sh` script.
* The performance tests support three modes. To enable the `full` or `stepwise` mode, you need to append the word ` full` or ` stepwise` (with the preceding space) in the `execute.sh` script at the end of the `docker run` command.
1. By default, the performance tests execute one run and measurement per configuration. Currently, three different parsing configurations of JaMoPP are considered.
2. `full`: In this mode, the performance tests execute 100 runs and measurements per configuration (the same three configurations as before). This execution takes several hours.
3. `stepwise`: In this special mode, the performance tests execute one run and measurement of the complete second reference resolution variant. This can take more than 24 hours. Contrary to the previous modes, this mode measures metrics for each resolution step.
* The results are stored in the `target` directory within this directory.
* The actual Docker image for the JaMoPP performance tests are based on an adapted Docker image for the JDK 17, which is also built during building the actual Docker image. In contrast to the official JDK 17 images, the adapted Docker image contains the `src.zip` directory, which contains the source code of the Java standard library, which is currently required by JaMoPP to run.
18 changes: 18 additions & 0 deletions docker/docker-bake.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
group "default" {
targets = ["jamopp"]
}

target "jdk-17-src" {
context = "."
dockerfile = "jdk-17-src.Dockerfile"
}

target "jamopp" {
context = ".."
dockerfile = "./docker/Dockerfile"
contexts = {
jdk-17-src = "target:jdk-17-src"
}
network = "host"
tags = ["tools.mdsd/jamopp-performance-tests:6.0.0-SNAPSHOT"]
}
157 changes: 157 additions & 0 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#!/usr/bin/env sh
# ------------------------------------------------------------------------------
# NOTE: THIS FILE IS GENERATED VIA "generate_dockerfiles.py"
#
# PLEASE DO NOT EDIT IT DIRECTLY.
# ------------------------------------------------------------------------------
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This script defines `sh` as the interpreter, which is available in all POSIX environments. However, it might get
# started with `bash` as the shell to support dotted.environment.variable.names which are not supported by POSIX, but
# are supported by `sh` in some Linux flavours.

set -e

TMPDIR=${TMPDIR:-/tmp}

# JDK truststore location
JRE_CACERTS_PATH=$JAVA_HOME/lib/security/cacerts

# Opt-in is only activated if the environment variable is set
if [ -n "$USE_SYSTEM_CA_CERTS" ]; then

if [ ! -w "$TMPDIR" ]; then
echo "Using additional CA certificates requires write permissions to $TMPDIR. Cannot create truststore."
exit 1
fi

# Wrap keytool truststore access. JDK 9+ uses -cacerts (added in JDK 9)
# to avoid the "Warning: use -cacerts option to access cacerts keystore"
# that keytool emits when -keystore points at the default cacerts file.
# JDK 8 has no -cacerts and uses -keystore. The temporary-truststore
# branch below rebinds the JDK 9+ wrapper to -keystore as well, since
# -cacerts would still resolve to the read-only default. -importkeystore
# is not routed through this wrapper: its -destkeystore/-srckeystore do
# not trigger the warning and have no -cacerts form.
keytool_truststore() {
keytool -cacerts "$@"
}

# Figure out whether we can write to the JVM truststore. If we can, we'll add the certificates there. If not,
# we'll use a temporary truststore.
if [ ! -w "$JRE_CACERTS_PATH" ]; then
# We cannot write to the JVM truststore, so we create a temporary one
JRE_CACERTS_PATH_NEW=$(mktemp)
echo "Using a temporary truststore at $JRE_CACERTS_PATH_NEW"
cp "$JRE_CACERTS_PATH" "$JRE_CACERTS_PATH_NEW"
JRE_CACERTS_PATH=$JRE_CACERTS_PATH_NEW
# If we use a custom truststore, we need to make sure that the JVM uses it
export JAVA_TOOL_OPTIONS="${JAVA_TOOL_OPTIONS} -Djavax.net.ssl.trustStore=${JRE_CACERTS_PATH} -Djavax.net.ssl.trustStorePassword=changeit"
# Rebind: -cacerts would still resolve to the read-only default.
keytool_truststore() {
keytool -keystore "$JRE_CACERTS_PATH" "$@"
}
fi

tmp_store=$(mktemp)

# Copy full system CA store to a temporary location
trust extract --overwrite --format=java-cacerts --filter=ca-anchors --purpose=server-auth "$tmp_store" > /dev/null

# Add the system CA certificates to the JVM truststore.
keytool -importkeystore -destkeystore "$JRE_CACERTS_PATH" -srckeystore "$tmp_store" -srcstorepass changeit -deststorepass changeit -noprompt > /dev/null

# Clean up the temporary truststore
rm -f "$tmp_store"

# Import the additional certificate into JVM truststore
find -L /certificates -path '*/..*' -prune -o -type f -name "*crt" -print 2>/dev/null | sort | while IFS= read -r i; do
tmp_dir=$(mktemp -d)
BASENAME=$(basename "$i" .crt)

# We might have multiple certificates in the file. Split this file into single files. The reason is that
# `keytool` does not accept multi-certificate files
csplit -s -z -b %02d.crt -f "$tmp_dir/$BASENAME-" "$i" '/-----BEGIN CERTIFICATE-----/' '{*}'

for crt in "$tmp_dir/$BASENAME"-*; do
# Extract the Common Name (CN) from the certificate
CN=$(openssl x509 -in "$crt" -noout -subject -nameopt -space_eq | sed -n 's/^.*CN=\([^,]*\).*$/\1/p')

# Compute the certificate SHA-256 fingerprint. It is used both to skip certificates that are
# already present and to build a collision-free alias below. A certificate that openssl cannot
# parse yields an empty fingerprint; skip it rather than risk a non-unique alias.
FINGERPRINT=$(openssl x509 -in "$crt" -noout -fingerprint -sha256 2>/dev/null | cut -d'=' -f2)
if [ -z "$FINGERPRINT" ]; then
echo "Could not read the fingerprint of a certificate in $i, skipping"
continue
fi

# Check if the certificate is already in the JVM truststore by fingerprint. This prevents
# failures on container restart when the certificate was added to the system CA store in a
# previous run and is now being re-imported via keytool -importkeystore.
if keytool_truststore -list -storepass changeit -v 2>/dev/null | grep -qiF "$FINGERPRINT"; then
echo "Certificate with CN=$CN is already in the JVM truststore, skipping"
continue
fi

# Normalized, globally-unique fingerprint suffix used to disambiguate aliases. The serial
# number is not reliable for this: CA roots can share a non-unique serial (e.g. 00) and may
# have no CN at all, which previously collapsed every such cert to the same alias.
FP=$(printf '%s' "$FINGERPRINT" | tr -d ':' | tr 'A-Z' 'a-z')

if [ -n "$CN" ]; then
# Use the CN as the alias, falling back to the fingerprint on collision
ALIAS=$CN
if keytool_truststore -list -storepass changeit -alias "$ALIAS" >/dev/null 2>&1; then
ALIAS="${CN}_${FP}"
fi
else
# No CN available: derive a unique, deterministic alias from the fingerprint
ALIAS="adoptium_${FP}"
fi

echo "Adding certificate with alias $ALIAS to the JVM truststore"

# Add the certificate to the JVM truststore
keytool_truststore -import -noprompt -alias "$ALIAS" -file "$crt" -storepass changeit >/dev/null
done
done

# Add additional certificates to the system CA store. This requires write permissions to several system
# locations, which is not possible in a container with read-only filesystem and/or non-root container.
if [ "$(id -u)" -eq 0 ]; then

# Copy certificates from /certificates to the system truststore, but only if the directory exists and is not empty.
# The reason why this is not part of the opt-in is because it leaves open the option to mount certificates at the
# system location, for whatever reason.
if [ -d /certificates ] && [ "$(ls -A /certificates 2>/dev/null)" ]; then
find -L /certificates -path '*/..*' -prune -o -type f -name "*crt" -print 2>/dev/null | while IFS= read -r _crt; do
_rel="${_crt#/certificates/}"
_dst_rel="${_rel//_/__}"
_dst_rel="${_dst_rel//\//_}"
cp -L "$_crt" "/usr/local/share/ca-certificates/${_dst_rel}"
done
fi
update-ca-certificates
else
# If we are not root, we cannot update the system truststore. That's bad news for tools like `curl` and `wget`,
# but since the JVM is the primary focus here, we can live with that.
true
fi
fi

# Let's provide a variable with the correct path for tools that want or need to use it
export JRE_CACERTS_PATH

exec "$@"
3 changes: 3 additions & 0 deletions docker/execute.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mkdir target
docker buildx bake --allow=network.host
docker run --cpus 4 --memory 16GB --mount type=bind,source=./target,target=/app/jamopp.tests/target/tests/output_performance tools.mdsd/jamopp-performance-tests:6.0.0-SNAPSHOT
93 changes: 93 additions & 0 deletions docker/jdk-17-src.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# ------------------------------------------------------------------------------
# NOTE: THIS FILE IS GENERATED VIA "generate_dockerfiles.py"
#
# PLEASE DO NOT EDIT IT DIRECTLY.
# ------------------------------------------------------------------------------
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

FROM alpine:3.24

ENV JAVA_HOME=/opt/java/openjdk
ENV PATH=$JAVA_HOME/bin:$PATH

# Default to UTF-8 file.encoding
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8'

RUN set -eux; \
apk add --no-cache \
# java.lang.UnsatisfiedLinkError: libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory
# java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11FontManager
# https://github.com/docker-library/openjdk/pull/235#issuecomment-424466077
fontconfig ttf-dejavu \
# gnupg required to verify the signature
gnupg \
# utilities for keeping Alpine and OpenJDK CA certificates in sync
# https://github.com/adoptium/containers/issues/293
ca-certificates p11-kit-trust \
# locales ensures proper character encoding and locale-specific behaviors using en_US.UTF-8
musl-locales musl-locales-lang \
# jlink --strip-debug on 13+ needs objcopy: https://github.com/docker-library/openjdk/issues/351
# Error: java.io.IOException: Cannot run program "objcopy": error=2, No such file or directory
binutils \
tzdata \
# Contains `csplit` used for splitting multiple certificates in one file to multiple files, since keytool can
# only import one at a time.
coreutils \
# Needed to extract CN and generate aliases for certificates
openssl \
; \
rm -rf /var/cache/apk/*

ENV JAVA_VERSION=jdk-17.0.20+8

RUN set -eux; \
ARCH="$(apk --print-arch)"; \
case "${ARCH}" in \
x86_64) \
ESUM='c8bb5bc6984762dbce2ab7403d90832b6897c07f36f8706e4a315aa7a566d04d'; \
BINARY_URL='https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.20%2B8/OpenJDK17U-jdk_x64_alpine-linux_hotspot_17.0.20_8.tar.gz'; \
;; \
*) \
echo "Unsupported arch: ${ARCH}"; \
exit 1; \
;; \
esac; \
wget -O /tmp/openjdk.tar.gz ${BINARY_URL}; \
wget -O /tmp/openjdk.tar.gz.sig ${BINARY_URL}.sig; \
export GNUPGHOME="$(mktemp -d)"; \
# gpg: key 843C48A565F8F04B: "Adoptium GPG Key (DEB/RPM Signing Key) <temurin-dev@eclipse.org>" imported
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 3B04D753C9050D9A5D343F39843C48A565F8F04B; \
gpg --batch --verify /tmp/openjdk.tar.gz.sig /tmp/openjdk.tar.gz; \
rm -rf "${GNUPGHOME}" /tmp/openjdk.tar.gz.sig; \
echo "${ESUM} */tmp/openjdk.tar.gz" | sha256sum -c -; \
mkdir -p "$JAVA_HOME"; \
tar --extract \
--file /tmp/openjdk.tar.gz \
--directory "$JAVA_HOME" \
--strip-components 1 \
--no-same-owner \
; \
rm -f /tmp/openjdk.tar.gz;

RUN set -eux; \
echo "Verifying install ..."; \
fileEncoding="$(echo 'System.out.println(System.getProperty("file.encoding"))' | jshell -s -)"; [ "$fileEncoding" = 'UTF-8' ]; rm -rf ~/.java; \
echo "javac --version"; javac --version; \
echo "java --version"; java --version; \
echo "Complete."
COPY --chmod=755 entrypoint.sh /__cacert_entrypoint.sh
ENTRYPOINT ["/__cacert_entrypoint.sh"]

CMD ["jshell"]
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static Type getTargetType(MethodReferenceExpression me) {
public static TypeReference getTargetTypeReference(MethodReferenceExpression me) {
TypeReference targetType = null;
EObject parentContainer = me;
while (!(parentContainer.eContainer() instanceof MethodCall
while (parentContainer != null && !(parentContainer.eContainer() instanceof MethodCall
|| parentContainer.eContainer() instanceof LocalVariable
|| parentContainer.eContainer() instanceof AdditionalLocalVariable
|| parentContainer.eContainer() instanceof AssignmentExpression
Expand All @@ -51,6 +51,9 @@ public static TypeReference getTargetTypeReference(MethodReferenceExpression me)
|| parentContainer.eContainer() instanceof AdditionalField)) {
parentContainer = parentContainer.eContainer();
}
if (parentContainer == null) {
return null;
}
if (parentContainer.eContainer() instanceof MethodCall) {
MethodCall call = (MethodCall) parentContainer.eContainer();
Method m = (Method) call.getTarget();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,9 @@ private static TypeReference searchForTypeParameter(TypeParameter me, TypeRefere
&& targetReference instanceof TypeArgumentable) {
TypeArgumentable typeArg = (TypeArgumentable) searchReference;
TypeArgumentable targetArg = (TypeArgumentable) targetReference;
if (targetArg.getTypeArguments().size() != typeArg.getTypeArguments().size()) {
return null;
}
for (int i = 0; i < typeArg.getTypeArguments().size(); i++) {
TypeArgument arg = typeArg.getTypeArguments().get(i);
TypeReference refOfArg = TypeReferenceExtension.getTypeReferenceOfTypeArgument(arg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ public static TypeReference getReferencedTypeReference(Reference me) {
Type thisClass = null;
if (me.getPrevious() != null) {
thisClassRef = me.getPrevious().getReferencedTypeReference();
if (thisClassRef == null) {
return null;
}
thisClass = thisClassRef.getTarget();
} else {
AnonymousClass anonymousContainer = me.getContainingAnonymousClass();
Expand Down
Loading