diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..dce0df9e
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,11 @@
+# To get started with Dependabot version updates, you'll need to specify which
+# package ecosystems to update and where the package manifests are located.
+# Please see the documentation for all configuration options:
+# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
+
+version: 2
+updates:
+ - package-ecosystem: "maven"
+ directory: "/"
+ schedule:
+ interval: "daily"
diff --git a/.github/project.yml b/.github/project.yml
new file mode 100644
index 00000000..74cefe42
--- /dev/null
+++ b/.github/project.yml
@@ -0,0 +1,5 @@
+name: power-server
+release:
+ current-version: 0.0.1
+ next-version: 0.0.2-SNAPSHOT
+
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000..3806c4be
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,62 @@
+name: Release
+
+on:
+ pull_request:
+ types: [ closed ]
+ paths:
+ - '.github/project.yml'
+
+concurrency:
+ group: "${{ github.workflow }}-${{ github.ref }}"
+ cancel-in-progress: true
+
+defaults:
+ run:
+ shell: bash
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ name: Release power-server
+ if: ${{github.event.pull_request.merged == true}}
+
+ steps:
+ - uses: radcortez/project-metadata-action@main
+ name: Retrieve project metadata
+ id: metadata
+ with:
+ github-token: ${{secrets.GITHUB_TOKEN}}
+ metadata-file-path: '.github/project.yml'
+
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: 21
+ cache: 'maven'
+ server-id: ossrh
+ server-username: MAVEN_USERNAME
+ server-password: MAVEN_PASSWORD
+ gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
+ gpg-passphrase: MAVEN_GPG_PASSPHRASE
+
+ - name: Configure Git author
+ run: |
+ git config --local user.email "action@github.com"
+ git config --local user.name "GitHub Action"
+
+ - name: Maven release ${{steps.metadata.outputs.current-version}}
+ run: |
+ git checkout -b release
+ mvn -B release:prepare -Prelease -Darguments="-DperformRelease -Dno-samples -DskipTests" -DreleaseVersion=${{steps.metadata.outputs.current-version}} -DdevelopmentVersion=${{steps.metadata.outputs.next-version}}
+ git checkout ${{github.base_ref}}
+ git rebase release
+ mvn -B release:perform -Darguments="-DperformRelease -Dno-samples -DskipTests" -DperformRelease -Prelease
+ env:
+ MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
+ MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }}
+ MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
+
+ - name: Push tags
+ run: git push && git push --tags
\ No newline at end of file
diff --git a/build-tools/pom.xml b/build-tools/pom.xml
index 0b44d213..cd03407f 100644
--- a/build-tools/pom.xml
+++ b/build-tools/pom.xml
@@ -7,10 +7,11 @@
net.laprun.sustainability
power-server-parent
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
build-tools
-
+ power-server : code formatting support
+ Support to automatically format the code for the power-server project
\ No newline at end of file
diff --git a/metadata/pom.xml b/metadata/pom.xml
index 25e1514d..adcc1c04 100644
--- a/metadata/pom.xml
+++ b/metadata/pom.xml
@@ -7,10 +7,12 @@
net.laprun.sustainability
power-server-parent
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
power-server-metadata
+ power-server : metadata model
+ Metadata model used by the power-server project, extracted to allow reuse in client projects
@@ -22,7 +24,7 @@
net.laprun.sustainability
build-tools
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
diff --git a/metadata/src/main/java/net/laprun/sustainability/power/SensorMeasure.java b/metadata/src/main/java/net/laprun/sustainability/power/SensorMeasure.java
index 24fb0b7a..efdc9126 100644
--- a/metadata/src/main/java/net/laprun/sustainability/power/SensorMeasure.java
+++ b/metadata/src/main/java/net/laprun/sustainability/power/SensorMeasure.java
@@ -1,4 +1,12 @@
package net.laprun.sustainability.power;
+/**
+ * A power consumption measure as recorded by a sensor, recorded over a given period of time, with an ordering information
+ * provided by a tick. The meaning of each component measure is provided by the {@link SensorMetadata} information associated
+ * with the sensor.
+ *
+ * @param components an array recording the power consumption reported by each component of this sensor
+ * @param tick the ordinal tick associated with this measure
+ */
public record SensorMeasure(double[] components, long tick) {
}
diff --git a/metadata/src/main/java/net/laprun/sustainability/power/SensorMetadata.java b/metadata/src/main/java/net/laprun/sustainability/power/SensorMetadata.java
index c7b12d75..175a6441 100644
--- a/metadata/src/main/java/net/laprun/sustainability/power/SensorMetadata.java
+++ b/metadata/src/main/java/net/laprun/sustainability/power/SensorMetadata.java
@@ -6,10 +6,33 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
+/**
+ * The metadata associated with a power-consumption recording sensor. This allows to make sense of the data sent by the power
+ * server by providing information about each component (e.g. CPU) recorded by the sensor during each periodical measure.
+ */
public class SensorMetadata {
+ /**
+ * The information associated with a recorded component
+ *
+ * @param name the name of the component (e.g. CPU)
+ * @param index the index at which the measure for this component is recorded in the {@link SensorMeasure#components} array
+ * @param description a short textual description of what this component is about when available (for automatically
+ * extracted components, this might be identical to the name)
+ * @param isAttributed whether or not this component provides an attributed value i.e. whether the value is already computed
+ * for the process during a measure or, to the contrary, if the measure is done globally and the computation of the
+ * attributed share for each process needs to be performed. This is needed because some sensors only provide
+ * system-wide measures instead of on a per-process basis.
+ * @param unit a textual representation of the unit used for measures associated with this component (e.g. mW)
+ */
public record ComponentMetadata(String name, int index, String description, boolean isAttributed, String unit) {
}
+ /**
+ * Initializes sensor metadata information
+ *
+ * @param components a map describing the metadata for each component
+ * @param documentation a text providing any relevant information associated with the described sensor
+ */
@JsonCreator
public SensorMetadata(@JsonProperty("metadata") Map components,
@JsonProperty("documentation") String documentation) {
@@ -23,11 +46,24 @@ public SensorMetadata(@JsonProperty("metadata") Map c
@JsonProperty("documentation")
private final String documentation;
+ /**
+ * Determines whether a component with the specified name is known for this sensor
+ *
+ * @param component the name of the component
+ * @return {@code true} if a component with the specified name exists for the associated sensor, {@code false} otherwise
+ */
public boolean exists(String component) {
return components.containsKey(component);
}
- public ComponentMetadata metadataFor(String component) {
+ /**
+ * Retrieves the {@link ComponentMetadata} associated with the specified component name if it exists
+ *
+ * @param component the name of the component which metadata is to be retrieved
+ * @return the {@link ComponentMetadata} associated with the specified name
+ * @throws IllegalArgumentException if no component with the specified name is known for the associated sensor
+ */
+ public ComponentMetadata metadataFor(String component) throws IllegalArgumentException {
final var componentMetadata = components.get(component);
if (componentMetadata == null) {
throw new IllegalArgumentException("Unknown component: " + component);
@@ -35,14 +71,30 @@ public ComponentMetadata metadataFor(String component) {
return componentMetadata;
}
+ /**
+ * Retrieves the number of known components for the associated sensor
+ *
+ * @return the cardinality of known components
+ */
public int componentCardinality() {
return components.size();
}
+ /**
+ * Retrieves the known {@link ComponentMetadata} for the associated sensor as an unmodifiable Map keyed by the components'
+ * name
+ *
+ * @return an unmodifiable Map of the known {@link ComponentMetadata}
+ */
public Map components() {
return Collections.unmodifiableMap(components);
}
+ /**
+ * Retrieves the documentation, if any, associated with this SensorMetadata
+ *
+ * @return the documentation relevant for the associated sensor
+ */
public String documentation() {
return documentation;
}
diff --git a/pom.xml b/pom.xml
index e46fe4aa..1f38ef22 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,199 +1,318 @@
-
- 4.0.0
- net.laprun.sustainability
- power-server-parent
- 1.0.0-SNAPSHOT
- pom
+
+ 4.0.0
+ net.laprun.sustainability
+ power-server-parent
+ 0.0.1-SNAPSHOT
+ pom
-
- 3.11.0
- 21
- UTF-8
- UTF-8
- quarkus-bom
- io.quarkus
- 3.7.1
- true
- 3.1.2
- 2.23.0
- 1.9.0
-
+ power-server : parent
+ An application allowing to retrieve power consumption and associated metadata on a per-process basis,
+ via a RESTful endpoint
+
+ https://github.com/metacosm/power-server
-
- build-tools
- server
- metadata
-
+
+
+ Apache 2 License
+ https://www.apache.org/licenses/LICENSE-2.0.html
+
+
+
+
+ Christophe Laprun
+ metacosm@gmail.com
+
+
-
+
+ scm:git:git://github.com/metacosm/power-server.git
+ scm:git:git@github.com/metacosm/power-server.git
+ https://github.com/metacosm/power-server/tree/main
+
+
+
+
+ 3.11.0
+ 21
+ UTF-8
+ UTF-8
+ quarkus-bom
+ io.quarkus
+ 3.7.1
+ true
+ 3.1.2
+ 2.23.0
+ 1.9.0
+ 3.6.3
+ 3.3.0
+ 3.1.0
+ 1.6.13
+ 0.3.0
+
+
+
+ build-tools
+ server
+ metadata
+
+
+
+
+ ossrh
+ https://s01.oss.sonatype.org/content/repositories/snapshots/
+
+ true
+ always
+
+
+
+
+
+
+ ossrh
+ https://s01.oss.sonatype.org/content/repositories/snapshots
+
+
+ ossrh
+ https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
+
+
+
+
+ ${quarkus.platform.group-id}
+ ${quarkus.platform.artifact-id}
+ ${quarkus.platform.version}
+ pom
+ import
+
+
+
-
- ${quarkus.platform.group-id}
- ${quarkus.platform.artifact-id}
- ${quarkus.platform.version}
- pom
- import
-
+
+ io.quarkus
+ quarkus-arc
+
+
+ io.quarkus
+ quarkus-resteasy-reactive-jackson
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ io.rest-assured
+ rest-assured
+ test
+
-
-
-
- io.quarkus
- quarkus-arc
-
-
- io.quarkus
- quarkus-resteasy-reactive-jackson
-
-
- io.quarkus
- quarkus-junit5
- test
-
-
- io.rest-assured
- rest-assured
- test
-
-
-
-
-
- ${quarkus.platform.group-id}
- quarkus-maven-plugin
- ${quarkus.platform.version}
- true
-
-
-
- build
- generate-code
- generate-code-tests
-
-
-
-
-
- maven-compiler-plugin
- ${compiler-plugin.version}
-
-
- -parameters
-
-
-
-
- maven-surefire-plugin
- ${surefire-plugin.version}
-
-
- org.jboss.logmanager.LogManager
- ${maven.home}
-
-
-
-
- maven-failsafe-plugin
- ${surefire-plugin.version}
-
-
-
- integration-test
- verify
-
-
-
- ${project.build.directory}/${project.build.finalName}-runner
- org.jboss.logmanager.LogManager
- ${maven.home}
-
-
-
-
-
-
- net.revelc.code.formatter
- formatter-maven-plugin
- ${formatter-plugin.version}
-
-
- 17
- eclipse-format.xml
- LF
- ${format.skip}
-
-
-
- net.revelc.code
- impsort-maven-plugin
- ${impsort-plugin.version}
-
-
- 17
- java.,javax.,jakarta.,org.,com.
- *
- ${format.skip}
- true
-
-
-
-
-
-
- format
-
-
-
- !no-format
-
-
-
+
-
- net.revelc.code.formatter
- formatter-maven-plugin
-
-
- format-sources
- process-sources
-
- format
-
-
-
-
-
- net.revelc.code
- impsort-maven-plugin
-
- true
-
-
-
- sort-imports
- process-sources
-
- sort
-
-
-
-
+
+ ${quarkus.platform.group-id}
+ quarkus-maven-plugin
+ ${quarkus.platform.version}
+ true
+
+
+
+ build
+ generate-code
+ generate-code-tests
+
+
+
+
+
+ maven-compiler-plugin
+ ${compiler-plugin.version}
+
+
+ -parameters
+
+
+
+
+ maven-surefire-plugin
+ ${surefire-plugin.version}
+
+
+ org.jboss.logmanager.LogManager
+ ${maven.home}
+
+
+
+
+ maven-failsafe-plugin
+ ${surefire-plugin.version}
+
+
+
+ integration-test
+ verify
+
+
+
+ ${project.build.directory}/${project.build.finalName}-runner
+
+ org.jboss.logmanager.LogManager
+ ${maven.home}
+
+
+
+
+
+
+ net.revelc.code.formatter
+ formatter-maven-plugin
+ ${formatter-plugin.version}
+
+
+ 17
+ eclipse-format.xml
+ LF
+ ${format.skip}
+
+
+
+ net.revelc.code
+ impsort-maven-plugin
+ ${impsort-plugin.version}
+
+
+ 17
+ java.,javax.,jakarta.,org.,com.
+ *
+ ${format.skip}
+ true
+
+
-
-
-
- native
-
-
- native
-
-
-
- false
- native
-
-
-
+
+
+
+ format
+
+
+
+ !no-format
+
+
+
+
+
+ net.revelc.code.formatter
+ formatter-maven-plugin
+
+
+ format-sources
+ process-sources
+
+ format
+
+
+
+
+
+ net.revelc.code
+ impsort-maven-plugin
+
+ true
+
+
+
+ sort-imports
+ process-sources
+
+ sort
+
+
+
+
+
+
+
+
+ release
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven-javadoc-plugin.version}
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ ${maven-source-plugin.version}
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ ${maven-gpg-plugin.version}
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+ --pinentry-mode
+ loopback
+
+
+
+
+
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ ${central-maven-publishing-plugin.version}
+ true
+
+ central
+ true
+ build-tools
+
+
+
+
+
+
+ native
+
+
+ native
+
+
+
+ false
+ native
+
+
+
diff --git a/server/pom.xml b/server/pom.xml
index 56c8cf00..7c6c445b 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -7,9 +7,12 @@
net.laprun.sustainability
power-server-parent
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
power-server
+ power-server : server
+ A RESTful endpoint to retrieve power consumption information on a per-process basis
+
21
21
@@ -19,7 +22,7 @@
net.laprun.sustainability
power-server-metadata
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
@@ -32,7 +35,7 @@
net.laprun.sustainability
build-tools
- 1.0.0-SNAPSHOT
+ 0.0.1-SNAPSHOT
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/Measures.java b/server/src/main/java/net/laprun/sustainability/power/sensors/Measures.java
index e4bf6760..169ff8cd 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/Measures.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/Measures.java
@@ -4,18 +4,62 @@
import net.laprun.sustainability.power.SensorMeasure;
+/**
+ * A representation of ongoing {@link PowerSensor} measures.
+ */
public interface Measures {
+ /**
+ * Represents an invalid or somehow missed measure.
+ */
SensorMeasure missing = new SensorMeasure(new double[] { -1.0 }, -1);
+ /**
+ * Tracks the provided process identifier (pid) in the measures. For sensors that only provide system-wide measures, this
+ * probably won't be doing much more than track which processes are of interest to clients of the sensor.
+ *
+ * @param pid the process identifier which power consumption is supposed to be tracked
+ * @return a {@link RegisteredPID} recording the tracking of the specified pid by the sensor
+ */
RegisteredPID register(long pid);
+ /**
+ * Unregisters the specified {@link RegisteredPID} thus signaling that clients are not interested in tracking the
+ * consumption of the associated process anymore
+ *
+ * @param registeredPID the {@link RegisteredPID} that was returned when the process we want to stop tracking was first
+ * registered
+ */
void unregister(RegisteredPID registeredPID);
+ /**
+ * Retrieves the set of tracked process identifiers
+ *
+ * @return the set of tracked process identifiers
+ */
Set trackedPIDs();
+ /**
+ * Retrieves the number of tracked processes
+ *
+ * @return the number of tracked processes
+ */
int numberOfTrackerPIDs();
+ /**
+ * Records the specified measure and associates it to the specified tracked process, normally called once per tick
+ *
+ * @param pid the {@link RegisteredPID} representing the tracked process with which the recorded measure needs to be
+ * associated
+ * @param sensorMeasure the {@link SensorMeasure} to be recorded
+ */
void record(RegisteredPID pid, SensorMeasure sensorMeasure);
+ /**
+ * Retrieves the last recorded {@link SensorMeasure} associated with the specified {@link RegisteredPID}
+ *
+ * @param pid the tracked process identifier which measure we want to retrieve
+ * @return the last recorded {@link SensorMeasure} associated with the specified process or {@link #missing} if it cannot be
+ * retrieved for any reason
+ */
SensorMeasure getOrDefault(RegisteredPID pid);
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/PowerSensor.java b/server/src/main/java/net/laprun/sustainability/power/sensors/PowerSensor.java
index 901f8fdb..912a4bf1 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/PowerSensor.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/PowerSensor.java
@@ -2,20 +2,65 @@
import net.laprun.sustainability.power.SensorMetadata;
+/**
+ * A representation of a power-consumption sensor.
+ */
public interface PowerSensor {
+ /**
+ * Stops measuring power consumption
+ */
default void stop() {
}
+ /**
+ * Retrieves the metadata associated with the sensor, in particular, which components are supported and how they are laid
+ * out in the {@link net.laprun.sustainability.power.SensorMeasure} that the sensor outputs
+ *
+ * @return the metadata associated with the sensor
+ */
SensorMetadata metadata();
+ /**
+ * Whether the sensor has started measuring power consumption or not
+ *
+ * @return {@code true} if measures are ongoing, {@code false} otherwise
+ */
boolean isStarted();
+ /**
+ * Starts emitting power consumption measures at the given frequency
+ *
+ * @param samplingFrequencyInMillis the number of milliseconds between emitted measures
+ * @throws Exception if the sensor couldn't be started for some reason
+ */
void start(long samplingFrequencyInMillis) throws Exception;
+ /**
+ * Registers the provided process identifier (pid) with the sensor in case it can provide per-process measures. For sensors
+ * that only provide system-wide measures, this probably won't be doing much more than track which processes are of interest
+ * to clients of the sensor.
+ *
+ * @param pid the process identifier which power consumption is supposed to be tracked
+ * @return a {@link RegisteredPID} recording the tracking of the specified pid by the sensor
+ */
RegisteredPID register(long pid);
+ /**
+ * Updates the ongoing {@link Measures} being recorded by this sensor for the given tick
+ *
+ * @param tick an ordinal value tracking the number of recorded measures being taken by the sensor since it started
+ * measuring power consumption
+ * @return the {@link Measures} object recording the measures this sensor has taken since it started measuring
+ */
Measures update(Long tick);
+ /**
+ * Unregisters the specified {@link RegisteredPID} with this sensor thus signaling that clients are not interested in
+ * tracking the consumption of the associated process anymore
+ *
+ * @param registeredPID the {@link RegisteredPID} that was returned when the process we want to stop tracking was first
+ * registered with this sensor
+ */
void unregister(RegisteredPID registeredPID);
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensor.java b/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensor.java
index a7feed03..900f7376 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensor.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensor.java
@@ -11,6 +11,9 @@
import net.laprun.sustainability.power.sensors.PowerSensor;
import net.laprun.sustainability.power.sensors.RegisteredPID;
+/**
+ * A sensor using Intel's RAPL accessed via Linux' powercap system.
+ */
public class IntelRAPLSensor implements PowerSensor {
private final RAPLFile[] raplFiles;
private final SensorMetadata metadata;
@@ -18,6 +21,9 @@ public class IntelRAPLSensor implements PowerSensor {
private long frequency;
private final SingleMeasureMeasures measures = new SingleMeasureMeasures();
+ /**
+ * Initializes the RAPL sensor
+ */
public IntelRAPLSensor() {
// if we total system energy is not available, read package and DRAM if possible
// todo: check Intel doc
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java
index efd3d994..3c6a9698 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java
@@ -16,15 +16,36 @@
import net.laprun.sustainability.power.sensors.PowerSensor;
import net.laprun.sustainability.power.sensors.RegisteredPID;
+/**
+ * A macOS powermetrics based {@link PowerSensor} implementation.
+ */
public abstract class MacOSPowermetricsSensor implements PowerSensor {
+ /**
+ * The Central Processing Unit component name
+ */
public static final String CPU = "CPU";
+ /**
+ * The Graphics Procssing Unit component name
+ */
public static final String GPU = "GPU";
+ /**
+ * The Apple Neural Engine component name
+ */
public static final String ANE = "ANE";
+ /**
+ * The Dynamic Random Access Memory component name
+ */
@SuppressWarnings("unused")
public static final String DRAM = "DRAM";
@SuppressWarnings("unused")
public static final String DCS = "DCS";
+ /**
+ * The package component name
+ */
public static final String PACKAGE = "Package";
+ /**
+ * The extracted CPU share component name, this represents the process' share of the measured power consumption
+ */
public static final String CPU_SHARE = "cpuShare";
private final Measures measures = new MapMeasures();