diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 0f6e824..d88a5ce 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,17 +1,17 @@
# IDE Config and Code Style
This project has a strictly enforced code style. Code formatting is done by the Eclipse code
-formatter, using the config files found in the `build-tools` directory. By
+formatter, using the config files found in the `contributing` directory. By
default when you run `mvn install` the code will be formatted automatically. When submitting a
pull request the CI build will fail if running the formatter results in any code changes, so it is
recommended that you always run a full Maven build before submitting a pull request.
-If you want to run the formatting without doing a full build, you can run `mvn process-sources`.
+If you want to run the formatting without doing a full build, you can run `mvn spotless:apply`.
#### Eclipse Setup
Open the *Preferences* window, and then navigate to _Java_ -> _Code Style_ -> _Formatter_. Click _
-Import_ and then select the `eclipse-format.xml` file in the `build-tools`
+Import_ and then select the `eclipse-format.xml` file in the `contributing`
directory.
Next navigate to _Java_ -> _Code Style_ -> _Organize Imports_. Click _Import_ and select
@@ -29,7 +29,7 @@ Settings_ -> _Eclipse Code Formatter_.
Select _Use the Eclipse Code Formatter_, then change the _Eclipse Java Formatter Config File_ to
point to the
-`eclipse-format.xml` file in the `build-tools` directory. Make sure the _
+`eclipse-format.xml` file in the `contributing` directory. Make sure the _
Optimize Imports_ box is ticked, and select the `eclipse.importorder` file as the import order
config file.
diff --git a/README.md b/README.md
index 7abe133..0c4ffa2 100644
--- a/README.md
+++ b/README.md
@@ -4,10 +4,8 @@ This project is meant to provide a REST endpoint streaming power consumption, in
by [JoularJX](https://github.com/joular/joularjx) but focusing on exposing power information and metadata over REST
without further processing.
-This project uses [Quarkus](https://quarkus.io), the Supersonic Subatomic Java Framework and comprises 3 modules:
+This project uses [Quarkus](https://quarkus.io), the Supersonic Subatomic Java Framework and comprises 2 modules:
-- `build-tools`, which contains formatting configuration that you can use to set your IDE up to contribute to this
- project
- `metadata`, which contains the metadata API that the RESTful server uses to provide information about what is returned
by the power sensors. This artifact contains classes that can be reused in client projects.
- `server` contains the RESTful server, listening by default on port `20432` (as specified
diff --git a/build-tools/pom.xml b/build-tools/pom.xml
deleted file mode 100644
index 9c46d68..0000000
--- a/build-tools/pom.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
- 4.0.0
-
-
- net.laprun.sustainability
- power-server-parent
- 0.0.9-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/build-tools/src/main/resources/eclipse-format.xml b/build-tools/src/main/resources/eclipse-format.xml
deleted file mode 100644
index f6e4206..0000000
--- a/build-tools/src/main/resources/eclipse-format.xml
+++ /dev/null
@@ -1,322 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/contributing/eclipse-format.xml b/contributing/eclipse-format.xml
new file mode 100644
index 0000000..783eed6
--- /dev/null
+++ b/contributing/eclipse-format.xml
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build-tools/src/main/resources/eclipse.importorder b/contributing/eclipse.importorder
similarity index 100%
rename from build-tools/src/main/resources/eclipse.importorder
rename to contributing/eclipse.importorder
diff --git a/metadata/pom.xml b/metadata/pom.xml
index 9add523..c6b250d 100644
--- a/metadata/pom.xml
+++ b/metadata/pom.xml
@@ -1,32 +1,15 @@
- 4.0.0
+ 4.0.0
-
- net.laprun.sustainability
- power-server-parent
- 0.0.9-SNAPSHOT
-
+
+ net.laprun.sustainability
+ power-server-parent
+ 0.0.9-SNAPSHOT
+
- power-server-metadata
- power-server : metadata model
- Metadata model used by the power-server project, extracted to allow reuse in client projects
+ power-server-metadata
+ power-server : metadata model
+ Metadata model used by the power-server project, extracted to allow reuse in client projects
-
-
-
-
- net.revelc.code.formatter
- formatter-maven-plugin
-
-
- net.laprun.sustainability
- build-tools
- 0.0.9-SNAPSHOT
-
-
-
-
-
-
-
\ No newline at end of file
+
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 efdc912..e8007b1 100644
--- a/metadata/src/main/java/net/laprun/sustainability/power/SensorMeasure.java
+++ b/metadata/src/main/java/net/laprun/sustainability/power/SensorMeasure.java
@@ -1,12 +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.
+ * 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
+ * @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 8215352..cbd4044 100644
--- a/metadata/src/main/java/net/laprun/sustainability/power/SensorMetadata.java
+++ b/metadata/src/main/java/net/laprun/sustainability/power/SensorMetadata.java
@@ -7,95 +7,102 @@
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.
+ * 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) {
- }
+ /**
+ * 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) {
- this.components = components;
- this.documentation = documentation;
- }
+ /**
+ * 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) {
+ this.components = components;
+ this.documentation = documentation;
+ }
- @JsonProperty("metadata")
- private final Map components;
+ @JsonProperty("metadata")
+ private final Map components;
- @JsonProperty("documentation")
- private final String documentation;
+ @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);
- }
+ /**
+ * 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);
+ }
- /**
- * 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);
- }
- return componentMetadata;
+ /**
+ * 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);
}
+ 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 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 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;
- }
+ /**
+ * 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 000c66d..4ac65cd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,294 +1,295 @@
-
+
- 4.0.0
- net.laprun.sustainability
- power-server-parent
- 0.0.9-SNAPSHOT
- pom
+ 4.0.0
+ net.laprun.sustainability
+ power-server-parent
+ 0.0.9-SNAPSHOT
+ pom
- 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
+ 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
+ 2023
-
-
- Apache 2 License
- https://www.apache.org/licenses/LICENSE-2.0.html
-
-
-
-
- Christophe Laprun
- metacosm@gmail.com
-
- author
-
-
-
- 2023
-
-
- 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
- HEAD
-
+
+
+ Apache 2 License
+ https://www.apache.org/licenses/LICENSE-2.0.html
+
+
+
+
+ Christophe Laprun
+ metacosm@gmail.com
+
+ author
+
+
+
+
+ server
+ metadata
+
-
- 3.13.0
- 17
- UTF-8
- UTF-8
- quarkus-bom
- io.quarkus
- 3.10.0
- true
- 3.2.5
- 3.2.5
- 2.23.0
- 1.9.0
- 3.6.3
- 3.3.1
- 3.2.4
- 1.6.13
- 0.4.0
- 3.0.1
- 3.1.2
- 3.2.0
- 3.4.1
- 1.7.1
- 3.7.1
- 1.12.0
-
+
+ scm:git:git://github.com/metacosm/power-server.git
+ scm:git:git@github.com/metacosm/power-server.git
+ HEAD
+ https://github.com/metacosm/power-server/tree/main
+
-
- build-tools
- server
- metadata
-
+
+ 3.13.0
+ 17
+ UTF-8
+ UTF-8
+ quarkus-bom
+ io.quarkus
+ 3.10.0
+ true
+ 3.2.5
+ 3.2.5
+ 2.23.0
+ 1.9.0
+ 3.6.3
+ 3.3.1
+ 3.2.4
+ 1.6.13
+ 0.4.0
+ 3.0.1
+ 3.1.2
+ 3.2.0
+ 3.4.1
+ 1.7.1
+ 3.7.1
+ 1.12.0
+ 2.43.0
+
-
-
-
- ${quarkus.platform.group-id}
- ${quarkus.platform.artifact-id}
- ${quarkus.platform.version}
- pom
- import
-
-
-
+
-
- io.quarkus
- quarkus-arc
-
-
- io.quarkus
- quarkus-rest-jackson
-
-
- io.quarkus
- quarkus-junit5
- test
-
-
- io.rest-assured
- rest-assured
- test
-
+
+ ${quarkus.platform.group-id}
+ ${quarkus.platform.artifact-id}
+ ${quarkus.platform.version}
+ pom
+ import
+
-
+
+
+
+ io.quarkus
+ quarkus-arc
+
+
+ io.quarkus
+ quarkus-rest-jackson
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ ${maven-deploy-plugin.version}
+
+
+ ${quarkus.platform.group-id}
+ quarkus-maven-plugin
+ ${quarkus.platform.version}
+ true
+
+
+
+ build
+ generate-code
+ generate-code-tests
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${compiler-plugin.version}
+
+
+ -parameters
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+ plain
+
+ true
+
+
+ UNICODE
+
+
+ org.jboss.logmanager.LogManager
+ ${maven.home}
+
+
+
+
+ me.fabriciorby
+ maven-surefire-junit5-tree-reporter
+ 1.2.1
+
+
+
+
+ org.apache.maven.plugins
+ maven-failsafe-plugin
+ ${maven-failsafe-plugin.version}
+
+
+
+ integration-test
+ verify
+
+
+
+ ${project.build.directory}/${project.build.finalName}-runner
+ org.jboss.logmanager.LogManager
+ ${maven.home}
+
+
+
+
+
+
+ com.diffplug.spotless
+ spotless-maven-plugin
+ ${spotless.version}
+
+
+
+ pom.xml
+ ./**/pom.xml
+
+
+ false
+
+
+
+
+ ./contributing/eclipse-format.xml
+
+
+ ./contributing/eclipse.importorder
+
+
+
+
+
+
+
+ apply
+
+ compile
+
+
+
+
+
+
+
+ format
+
+
+
+ !no-format
+
+
+
-
- org.apache.maven.plugins
- maven-deploy-plugin
- ${maven-deploy-plugin.version}
-
-
- ${quarkus.platform.group-id}
- quarkus-maven-plugin
- ${quarkus.platform.version}
- true
-
-
-
- build
- generate-code
- generate-code-tests
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- ${compiler-plugin.version}
-
-
- -parameters
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- ${maven-surefire-plugin.version}
-
-
- me.fabriciorby
- maven-surefire-junit5-tree-reporter
- 1.2.1
-
-
-
- plain
-
- true
-
-
- UNICODE
-
-
- org.jboss.logmanager.LogManager
- ${maven.home}
-
-
-
-
- org.apache.maven.plugins
- maven-failsafe-plugin
- ${maven-failsafe-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
-
-
+
+ net.revelc.code.formatter
+ formatter-maven-plugin
+
+
+ format-sources
+
+ format
+
+ process-sources
+
+
+
+
+ net.revelc.code
+ impsort-maven-plugin
+
+ true
+
+
+
+ sort-imports
+
+ sort
+
+ process-sources
+
+
+
-
-
-
- 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.jreleaser
- jreleaser-maven-plugin
- ${jreleaser.version}
-
-
-
-
-
- ALWAYS
- conventional-commits
-
-
-
-
-
- power-server
- BINARY
-
- RELEASE
- true
-
-
- RELEASE
-
-
-
-
- {{artifactsDir}}/{{distributionName}}-{{projectVersion}}-linux-x86_64.tar.gz
-
-
- artifacts/{{distributionName}}-{{projectEffectiveVersion}}-linux-x86_64.tar.gz
-
- linux-x86_64
-
-
-
-
- {{artifactsDir}}/{{distributionName}}-{{projectVersion}}-osx-x86_64.tar.gz
-
-
- artifacts/{{distributionName}}-{{projectEffectiveVersion}}-osx-x86_64.tar.gz
-
- osx-x86_64
-
-
-
- {{artifactsDir}}/{{distributionName}}-{{projectVersion}}-osx-aarch_64.tar.gz
-
-
- artifacts/{{distributionName}}-{{projectEffectiveVersion}}-osx-aarch_64.tar.gz
-
- osx-aarch_64
-
-
-
-
-
-
-
-
-
- org.codehaus.mojo
- buildnumber-maven-plugin
- ${buildnumber-maven-plugin.version}
-
-
- get-scm-revision
- initialize
-
- create
-
-
- false
- false
- UNKNOWN
- true
-
-
-
-
-
- org.apache.maven.plugins
- maven-jar-plugin
- ${maven-jar-plugin.version}
-
-
- true
-
-
- true
-
-
- true
-
-
-
- ${project.url}
- ${java.version}
- ${java.vendor}
- ${os.name}
- ${os.arch}
- ${os.version}
- ${project.scm.url}
- ${project.scm.connection}
- ${buildNumber}
-
-
-
-
-
- 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}
-
-
- true
-
-
- true
-
-
- true
-
-
-
- ${project.url}
- ${java.version}
- ${java.vendor}
- ${os.name}
- ${os.arch}
- ${os.version}
- ${project.scm.url}
- ${project.scm.connection}
- ${buildNumber}
-
-
-
-
-
- attach-sources
-
- jar-no-fork
-
-
-
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- ${maven-gpg-plugin.version}
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
- --pinentry-mode
- loopback
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-release-plugin
- ${maven-release-plugin.version}
-
- clean install
- true
- false
- release
- deploy
- @{project.version}
- false
- true
- false
-
-
-
- org.sonatype.central
- central-publishing-maven-plugin
- ${central-maven-publishing-plugin.version}
- true
-
- true
- true
- build-tools
-
-
-
-
-
-
- native
-
-
- native
-
-
-
- false
- native
-
-
-
+
+ {{artifactsDir}}/{{distributionName}}-{{projectVersion}}-osx-x86_64.tar.gz
+ artifacts/{{distributionName}}-{{projectEffectiveVersion}}-osx-x86_64.tar.gz
+ osx-x86_64
+
+
+ {{artifactsDir}}/{{distributionName}}-{{projectVersion}}-osx-aarch_64.tar.gz
+ artifacts/{{distributionName}}-{{projectEffectiveVersion}}-osx-aarch_64.tar.gz
+ osx-aarch_64
+
+
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ buildnumber-maven-plugin
+ ${buildnumber-maven-plugin.version}
+
+
+ get-scm-revision
+
+ create
+
+ initialize
+
+ false
+ false
+ UNKNOWN
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ ${maven-jar-plugin.version}
+
+
+ true
+
+ true
+ true
+
+
+ ${project.url}
+ ${java.version}
+ ${java.vendor}
+ ${os.name}
+ ${os.arch}
+ ${os.version}
+ ${project.scm.url}
+ ${project.scm.connection}
+ ${buildNumber}
+
+
+
+
+
+ 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}
+
+
+ true
+
+ true
+ true
+
+
+ ${project.url}
+ ${java.version}
+ ${java.vendor}
+ ${os.name}
+ ${os.arch}
+ ${os.version}
+ ${project.scm.url}
+ ${project.scm.connection}
+ ${buildNumber}
+
+
+
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ ${maven-gpg-plugin.version}
+
+
+ sign-artifacts
+
+ sign
+
+ verify
+
+
+ --pinentry-mode
+ loopback
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-release-plugin
+ ${maven-release-plugin.version}
+
+ clean install
+ true
+ false
+ release
+ deploy
+ @{project.version}
+ false
+ true
+ false
+
+
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+ ${central-maven-publishing-plugin.version}
+ true
+
+ true
+ true
+ build-tools
+
+
+
+
+
+
+ native
+
+
+ native
+
+
+
+ false
+ native
+
+
+
diff --git a/server/pom.xml b/server/pom.xml
index db1ac33..55112e7 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -11,7 +11,7 @@
A RESTful endpoint to retrieve power consumption information on a per-process basis
${project.build.directory}/distributions
-
+
21
21
UTF-8
@@ -24,13 +24,6 @@
-
-
- kr.motd.maven
- os-maven-plugin
- ${os-maven-plugin.version}
-
-
@@ -46,6 +39,13 @@
+
+
+ kr.motd.maven
+ os-maven-plugin
+ ${os-maven-plugin.version}
+
+
@@ -55,15 +55,6 @@
maven-assembly-plugin
${maven-assembly-plugin.version}
-
-
- make-distribution
- package
-
- single
-
-
-
false
false
@@ -74,6 +65,15 @@
src/main/assembly/assembly.xml
+
+
+ make-distribution
+
+ single
+
+ package
+
+
diff --git a/server/src/main/java/net/laprun/sustainability/power/PowerMeasurer.java b/server/src/main/java/net/laprun/sustainability/power/PowerMeasurer.java
index b82910f..a07278e 100644
--- a/server/src/main/java/net/laprun/sustainability/power/PowerMeasurer.java
+++ b/server/src/main/java/net/laprun/sustainability/power/PowerMeasurer.java
@@ -13,41 +13,37 @@
@ApplicationScoped
public class PowerMeasurer {
- public static final int SAMPLING_FREQUENCY_IN_MILLIS = 500;
- @Inject
- PowerSensor sensor;
-
- private Multi periodicSensorCheck;
-
- public Multi startTracking(String pid) throws Exception {
- // first make sure that the process with that pid exists
- final var parsedPID = validPIDOrFail(pid);
-
- if (!sensor.isStarted()) {
- sensor.start(SAMPLING_FREQUENCY_IN_MILLIS);
- periodicSensorCheck = Multi.createFrom().ticks()
- .every(Duration.ofMillis(SAMPLING_FREQUENCY_IN_MILLIS))
- .map(sensor::update)
- .broadcast()
- .withCancellationAfterLastSubscriberDeparture()
- .toAtLeast(1)
- .runSubscriptionOn(Infrastructure.getDefaultWorkerPool());
- }
- final var registeredPID = sensor.register(parsedPID);
- // todo: the timing of things could make it so that the pid has been removed before the map operation occurs so
- // currently return -1 instead of null but this needs to be properly addressed
- return periodicSensorCheck
- .map(measures -> measures.getOrDefault(registeredPID))
- .onCancellation().invoke(() -> sensor.unregister(registeredPID));
- }
+ public static final int SAMPLING_FREQUENCY_IN_MILLIS = 500;
- protected long validPIDOrFail(String pid) {
- final var parsedPID = Long.parseLong(pid);
- ProcessHandle.of(parsedPID).orElseThrow(() -> new IllegalArgumentException("Unknown process: " + pid));
- return parsedPID;
- }
+ @Inject
+ PowerSensor sensor;
+
+ private Multi periodicSensorCheck;
+
+ public Multi startTracking(String pid) throws Exception {
+ // first make sure that the process with that pid exists
+ final var parsedPID = validPIDOrFail(pid);
- public SensorMetadata metadata() {
- return sensor.metadata();
+ if (!sensor.isStarted()) {
+ sensor.start(SAMPLING_FREQUENCY_IN_MILLIS);
+ periodicSensorCheck = Multi.createFrom().ticks().every(Duration.ofMillis(SAMPLING_FREQUENCY_IN_MILLIS))
+ .map(sensor::update).broadcast().withCancellationAfterLastSubscriberDeparture().toAtLeast(1)
+ .runSubscriptionOn(Infrastructure.getDefaultWorkerPool());
}
+ final var registeredPID = sensor.register(parsedPID);
+ // todo: the timing of things could make it so that the pid has been removed before the map operation occurs so
+ // currently return -1 instead of null but this needs to be properly addressed
+ return periodicSensorCheck.map(measures -> measures.getOrDefault(registeredPID)).onCancellation()
+ .invoke(() -> sensor.unregister(registeredPID));
+ }
+
+ protected long validPIDOrFail(String pid) {
+ final var parsedPID = Long.parseLong(pid);
+ ProcessHandle.of(parsedPID).orElseThrow(() -> new IllegalArgumentException("Unknown process: " + pid));
+ return parsedPID;
+ }
+
+ public SensorMetadata metadata() {
+ return sensor.metadata();
+ }
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/PowerResource.java b/server/src/main/java/net/laprun/sustainability/power/PowerResource.java
index 56b6d05..9e2cb96 100644
--- a/server/src/main/java/net/laprun/sustainability/power/PowerResource.java
+++ b/server/src/main/java/net/laprun/sustainability/power/PowerResource.java
@@ -10,23 +10,23 @@
@Path("/power")
public class PowerResource {
- @Inject
- PowerMeasurer measurer;
+ @Inject
+ PowerMeasurer measurer;
- @GET
- @RestStreamElementType(MediaType.APPLICATION_JSON)
- @Path("{pid}")
- public Multi powerFor(@PathParam("pid") String pid) throws Exception {
- try {
- return measurer.startTracking(pid);
- } catch (IllegalArgumentException e) {
- throw new NotFoundException("Unknown process: " + pid);
- }
+ @GET
+ @RestStreamElementType(MediaType.APPLICATION_JSON)
+ @Path("{pid}")
+ public Multi powerFor(@PathParam("pid") String pid) throws Exception {
+ try {
+ return measurer.startTracking(pid);
+ } catch (IllegalArgumentException e) {
+ throw new NotFoundException("Unknown process: " + pid);
}
+ }
- @GET
- @Path("metadata")
- public SensorMetadata metadata() {
- return measurer.metadata();
- }
+ @GET
+ @Path("metadata")
+ public SensorMetadata metadata() {
+ return measurer.metadata();
+ }
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/AbstractPowerSensor.java b/server/src/main/java/net/laprun/sustainability/power/sensors/AbstractPowerSensor.java
index fc52ce4..5da321f 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/AbstractPowerSensor.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/AbstractPowerSensor.java
@@ -3,21 +3,21 @@
import io.quarkus.logging.Log;
public abstract class AbstractPowerSensor implements PowerSensor {
- protected final M measures;
+ protected final M measures;
- public AbstractPowerSensor(M measures) {
- this.measures = measures;
- }
+ public AbstractPowerSensor(M measures) {
+ this.measures = measures;
+ }
- @Override
- public RegisteredPID register(long pid) {
- Log.info("Registered pid: " + pid);
- return measures.register(pid);
- }
+ @Override
+ public RegisteredPID register(long pid) {
+ Log.info("Registered pid: " + pid);
+ return measures.register(pid);
+ }
- @Override
- public void unregister(RegisteredPID registeredPID) {
- measures.unregister(registeredPID);
- Log.info("Unregistered pid: " + registeredPID.pid());
- }
+ @Override
+ public void unregister(RegisteredPID registeredPID) {
+ measures.unregister(registeredPID);
+ Log.info("Unregistered pid: " + registeredPID.pid());
+ }
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/MapMeasures.java b/server/src/main/java/net/laprun/sustainability/power/sensors/MapMeasures.java
index 1075557..77e95c7 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/MapMeasures.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/MapMeasures.java
@@ -7,37 +7,37 @@
import net.laprun.sustainability.power.SensorMeasure;
public class MapMeasures implements Measures {
- private final ConcurrentMap measures = new ConcurrentHashMap<>();
-
- @Override
- public RegisteredPID register(long pid) {
- final var key = new RegisteredPID(pid);
- measures.put(key, missing);
- return key;
- }
-
- @Override
- public void unregister(RegisteredPID registeredPID) {
- measures.remove(registeredPID);
- }
-
- @Override
- public Set trackedPIDs() {
- return measures.keySet();
- }
-
- @Override
- public int numberOfTrackerPIDs() {
- return measures.size();
- }
-
- @Override
- public void record(RegisteredPID pid, SensorMeasure sensorMeasure) {
- measures.put(pid, sensorMeasure);
- }
-
- @Override
- public SensorMeasure getOrDefault(RegisteredPID pid) {
- return measures.getOrDefault(pid, missing);
- }
+ private final ConcurrentMap measures = new ConcurrentHashMap<>();
+
+ @Override
+ public RegisteredPID register(long pid) {
+ final var key = new RegisteredPID(pid);
+ measures.put(key, missing);
+ return key;
+ }
+
+ @Override
+ public void unregister(RegisteredPID registeredPID) {
+ measures.remove(registeredPID);
+ }
+
+ @Override
+ public Set trackedPIDs() {
+ return measures.keySet();
+ }
+
+ @Override
+ public int numberOfTrackerPIDs() {
+ return measures.size();
+ }
+
+ @Override
+ public void record(RegisteredPID pid, SensorMeasure sensorMeasure) {
+ measures.put(pid, sensorMeasure);
+ }
+
+ @Override
+ public SensorMeasure getOrDefault(RegisteredPID pid) {
+ return measures.getOrDefault(pid, missing);
+ }
}
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 17bd85b..129a4c0 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,62 +4,60 @@
import net.laprun.sustainability.power.SensorMeasure;
-/**
- * A representation of ongoing {@link PowerSensor} measures.
- */
+/** 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);
+ /** 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 015100f..fad270d 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
@@ -7,60 +7,61 @@
*/
public interface PowerSensor {
- /**
- * Stops measuring power consumption
- */
- default void stop() {
- }
+ /** 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();
+ /**
+ * 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();
+ /**
+ * 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;
+ /**
+ * 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);
+ /**
+ * 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);
+ /**
+ * 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);
+ /**
+ * 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/PowerSensorProducer.java b/server/src/main/java/net/laprun/sustainability/power/sensors/PowerSensorProducer.java
index 820a204..6ac3914 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/PowerSensorProducer.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/PowerSensorProducer.java
@@ -8,21 +8,21 @@
@Singleton
public class PowerSensorProducer {
- private static final String OS_NAME = System.getProperty("os.name").toLowerCase();
+ private static final String OS_NAME = System.getProperty("os.name").toLowerCase();
- @Produces
- public PowerSensor sensor() {
- return determinePowerSensor();
- }
+ @Produces
+ public PowerSensor sensor() {
+ return determinePowerSensor();
+ }
- public static PowerSensor determinePowerSensor() {
- if (OS_NAME.contains("mac os x")) {
- return new ProcessMacOSPowermetricsSensor();
- }
+ public static PowerSensor determinePowerSensor() {
+ if (OS_NAME.contains("mac os x")) {
+ return new ProcessMacOSPowermetricsSensor();
+ }
- if (!OS_NAME.contains("linux")) {
- throw new RuntimeException("Unsupported platform: " + System.getProperty("os.name"));
- }
- return new IntelRAPLSensor();
+ if (!OS_NAME.contains("linux")) {
+ throw new RuntimeException("Unsupported platform: " + System.getProperty("os.name"));
}
+ return new IntelRAPLSensor();
+ }
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/RegisteredPID.java b/server/src/main/java/net/laprun/sustainability/power/sensors/RegisteredPID.java
index c744a8f..f619081 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/RegisteredPID.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/RegisteredPID.java
@@ -2,15 +2,15 @@
public record RegisteredPID(String stringForMatching) {
- public RegisteredPID(long pid) {
- this(prepare(pid));
- }
+ public RegisteredPID(long pid) {
+ this(prepare(pid));
+ }
- public static String prepare(long pid) {
- return " " + pid + " ";
- }
+ public static String prepare(long pid) {
+ return " " + pid + " ";
+ }
- public long pid() {
- return Long.parseLong(stringForMatching().trim());
- }
+ public long pid() {
+ return Long.parseLong(stringForMatching().trim());
+ }
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/ByteBufferRAPLFile.java b/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/ByteBufferRAPLFile.java
index ae3b7d3..927620a 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/ByteBufferRAPLFile.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/ByteBufferRAPLFile.java
@@ -8,43 +8,44 @@
import java.nio.file.Path;
class ByteBufferRAPLFile implements RAPLFile {
- private static final int CAPACITY = 64;
- private final ByteBuffer buffer;
- private final FileChannel channel;
+ private static final int CAPACITY = 64;
+ private final ByteBuffer buffer;
+ private final FileChannel channel;
- private ByteBufferRAPLFile(FileChannel channel) {
- this.channel = channel;
- buffer = ByteBuffer.allocate(CAPACITY);
- }
+ private ByteBufferRAPLFile(FileChannel channel) {
+ this.channel = channel;
+ buffer = ByteBuffer.allocate(CAPACITY);
+ }
- static RAPLFile createFrom(Path file) {
- try {
- return new ByteBufferRAPLFile(new RandomAccessFile(file.toFile(), "r").getChannel());
- } catch (FileNotFoundException e) {
- throw new RuntimeException(e);
- }
+ static RAPLFile createFrom(Path file) {
+ try {
+ return new ByteBufferRAPLFile(new RandomAccessFile(file.toFile(), "r").getChannel());
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
}
+ }
- public long extractEnergyInMicroJoules() {
- try {
- channel.read(buffer);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- long value = 0;
- // will work even better if we can hard code as a static final const the length, in case won't change or is defined by spec
- for (int i = 0; i < CAPACITY; i++) {
- byte digit = buffer.get(i);
- if (digit >= '0' && digit <= '9') {
- value = value * 10 + (digit - '0');
- } else {
- if (digit == '\n') {
- return value;
- }
- // Invalid character; handle accordingly or throw an exception
- throw new NumberFormatException("Invalid character in input: '" + Character.toString(digit) + "'");
- }
+ public long extractEnergyInMicroJoules() {
+ try {
+ channel.read(buffer);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ long value = 0;
+ // will work even better if we can hard code as a static final const the length, in case won't change or is
+ // defined by spec
+ for (int i = 0; i < CAPACITY; i++) {
+ byte digit = buffer.get(i);
+ if (digit >= '0' && digit <= '9') {
+ value = value * 10 + (digit - '0');
+ } else {
+ if (digit == '\n') {
+ return value;
}
- return value;
+ // Invalid character; handle accordingly or throw an exception
+ throw new NumberFormatException("Invalid character in input: '" + Character.toString(digit) + "'");
+ }
}
+ return value;
+ }
}
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 67c9ba6..eab2219 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
@@ -15,130 +15,129 @@
* A sensor using Intel's RAPL accessed via Linux' powercap system.
*/
public class IntelRAPLSensor extends AbstractPowerSensor {
- private final RAPLFile[] raplFiles;
- private final SensorMetadata metadata;
- private final double[] lastMeasuredSensorValues;
- private long frequency;
-
- /**
- * Initializes the RAPL sensor
- */
- public IntelRAPLSensor() {
- this(defaultRAPLFiles());
+ private final RAPLFile[] raplFiles;
+ private final SensorMetadata metadata;
+ private final double[] lastMeasuredSensorValues;
+ private long frequency;
+
+ /** Initializes the RAPL sensor */
+ public IntelRAPLSensor() {
+ this(defaultRAPLFiles());
+ }
+
+ protected IntelRAPLSensor(String... raplFilePaths) {
+ this(fromPaths(raplFilePaths));
+ }
+
+ private static SortedMap fromPaths(String... raplFilePaths) {
+ if (raplFilePaths == null || raplFilePaths.length == 0) {
+ throw new IllegalArgumentException("Must provide at least one RAPL file");
}
- protected IntelRAPLSensor(String... raplFilePaths) {
- this(fromPaths(raplFilePaths));
+ final var files = new TreeMap();
+ for (String raplFilePath : raplFilePaths) {
+ addFileIfReadable(raplFilePath, files);
}
-
- private static SortedMap fromPaths(String... raplFilePaths) {
- if (raplFilePaths == null || raplFilePaths.length == 0) {
- throw new IllegalArgumentException("Must provide at least one RAPL file");
- }
-
- final var files = new TreeMap();
- for (String raplFilePath : raplFilePaths) {
- addFileIfReadable(raplFilePath, files);
- }
- return files;
+ return files;
+ }
+
+ private static SortedMap defaultRAPLFiles() {
+ // if we total system energy is not available, read package and DRAM if possible
+ // todo: check Intel doc
+ final var files = new TreeMap();
+ if (!addFileIfReadable("/sys/class/powercap/intel-rapl/intel-rapl:1/energy_uj", files)) {
+ addFileIfReadable("/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj", files);
+ addFileIfReadable("/sys/class/powercap/intel-rapl/intel-rapl:0/intel-rapl:0:2/energy_uj", files);
}
-
- private static SortedMap defaultRAPLFiles() {
- // if we total system energy is not available, read package and DRAM if possible
- // todo: check Intel doc
- final var files = new TreeMap();
- if (!addFileIfReadable("/sys/class/powercap/intel-rapl/intel-rapl:1/energy_uj", files)) {
- addFileIfReadable("/sys/class/powercap/intel-rapl/intel-rapl:0/energy_uj", files);
- addFileIfReadable("/sys/class/powercap/intel-rapl/intel-rapl:0/intel-rapl:0:2/energy_uj", files);
- }
- return files;
- }
-
- private IntelRAPLSensor(SortedMap files) {
- super(new SingleMeasureMeasures());
- if (files.isEmpty())
- throw new RuntimeException("Failed to get RAPL energy readings, probably due to lack of read access ");
-
- raplFiles = files.values().toArray(new RAPLFile[0]);
- final var rawOffset = files.size();
- final var metadata = new HashMap(rawOffset * 2);
- int fileNb = 0;
- for (String name : files.keySet()) {
- metadata.put(name, new SensorMetadata.ComponentMetadata(name, fileNb, name, false, "mW"));
- final var rawName = name + "_uj";
- metadata.put(rawName, new SensorMetadata.ComponentMetadata(rawName, fileNb + rawOffset,
- name + " (raw micro Joule data)", false, "µJ"));
- fileNb++;
- }
- this.metadata = new SensorMetadata(metadata,
- "Linux RAPL derived information, see https://www.kernel.org/doc/html/latest/power/powercap/powercap.html");
- lastMeasuredSensorValues = new double[raplFiles.length];
+ return files;
+ }
+
+ private IntelRAPLSensor(SortedMap files) {
+ super(new SingleMeasureMeasures());
+ if (files.isEmpty())
+ throw new RuntimeException("Failed to get RAPL energy readings, probably due to lack of read access ");
+
+ raplFiles = files.values().toArray(new RAPLFile[0]);
+ final var rawOffset = files.size();
+ final var metadata = new HashMap(rawOffset * 2);
+ int fileNb = 0;
+ for (String name : files.keySet()) {
+ metadata.put(name, new SensorMetadata.ComponentMetadata(name, fileNb, name, false, "mW"));
+ final var rawName = name + "_uj";
+ metadata.put(rawName, new SensorMetadata.ComponentMetadata(rawName, fileNb + rawOffset,
+ name + " (raw micro Joule data)", false, "µJ"));
+ fileNb++;
}
-
- private static boolean addFileIfReadable(String raplFileAsString, SortedMap files) {
- final var raplFile = Path.of(raplFileAsString);
- if (isReadable(raplFile)) {
- // get metric name
- final var nameFile = raplFile.resolveSibling("name");
- if (!isReadable(nameFile)) {
- throw new IllegalStateException("No name associated with " + raplFileAsString);
- }
-
- try {
- final var name = Files.readString(nameFile).trim();
- files.put(name, RAPLFile.createFrom(raplFile));
- } catch (IOException e) {
- Log.debug("Couldn't read file: " + nameFile, e);
- return false;
- }
- return true;
- }
+ this.metadata = new SensorMetadata(metadata,
+ "Linux RAPL derived information, see https://www.kernel.org/doc/html/latest/power/powercap/powercap.html");
+ lastMeasuredSensorValues = new double[raplFiles.length];
+ }
+
+ private static boolean addFileIfReadable(String raplFileAsString, SortedMap files) {
+ final var raplFile = Path.of(raplFileAsString);
+ if (isReadable(raplFile)) {
+ // get metric name
+ final var nameFile = raplFile.resolveSibling("name");
+ if (!isReadable(nameFile)) {
+ throw new IllegalStateException("No name associated with " + raplFileAsString);
+ }
+
+ try {
+ final var name = Files.readString(nameFile).trim();
+ files.put(name, RAPLFile.createFrom(raplFile));
+ } catch (IOException e) {
+ Log.debug("Couldn't read file: " + nameFile, e);
return false;
+ }
+ return true;
}
-
- private static boolean isReadable(Path file) {
- return Files.exists(file) && Files.isReadable(file);
- }
-
- @Override
- public void start(long frequency) {
- this.frequency = frequency;
-
- // perform an initial measure to prime the data
- update(0L);
- }
-
- /**
- * Computes the power in mW based on the current, previous energy (in micro Joules) measures and sampling frequency.
- *
- * @param componentIndex the index of the component being measured
- * @param sensorValue the micro Joules energy reading
- * @return the power over the interval defined by the sampling frequency in mW
- */
- private double computePowerInMilliWatt(int componentIndex, long sensorValue) {
- return (sensorValue - lastMeasuredSensorValues[componentIndex]) / frequency / 1000;
- }
-
- @Override
- public SensorMetadata metadata() {
- return metadata;
- }
-
- @Override
- public boolean isStarted() {
- return frequency > 0;
- }
-
- @Override
- public Measures update(Long tick) {
- final var measure = new double[raplFiles.length];
- for (int i = 0; i < raplFiles.length; i++) {
- final var value = raplFiles[i].extractEnergyInMicroJoules();
- final var newComponentValue = computePowerInMilliWatt(i, value);
- measure[i] = newComponentValue;
- lastMeasuredSensorValues[i] = newComponentValue;
- }
- measures.singleMeasure(new SensorMeasure(measure, tick));
- return measures;
+ return false;
+ }
+
+ private static boolean isReadable(Path file) {
+ return Files.exists(file) && Files.isReadable(file);
+ }
+
+ @Override
+ public void start(long frequency) {
+ this.frequency = frequency;
+
+ // perform an initial measure to prime the data
+ update(0L);
+ }
+
+ /**
+ * Computes the power in mW based on the current, previous energy (in micro Joules) measures and sampling frequency.
+ *
+ * @param componentIndex the index of the component being measured
+ * @param sensorValue the micro Joules energy reading
+ *
+ * @return the power over the interval defined by the sampling frequency in mW
+ */
+ private double computePowerInMilliWatt(int componentIndex, long sensorValue) {
+ return (sensorValue - lastMeasuredSensorValues[componentIndex]) / frequency / 1000;
+ }
+
+ @Override
+ public SensorMetadata metadata() {
+ return metadata;
+ }
+
+ @Override
+ public boolean isStarted() {
+ return frequency > 0;
+ }
+
+ @Override
+ public Measures update(Long tick) {
+ final var measure = new double[raplFiles.length];
+ for (int i = 0; i < raplFiles.length; i++) {
+ final var value = raplFiles[i].extractEnergyInMicroJoules();
+ final var newComponentValue = computePowerInMilliWatt(i, value);
+ measure[i] = newComponentValue;
+ lastMeasuredSensorValues[i] = newComponentValue;
}
+ measures.singleMeasure(new SensorMeasure(measure, tick));
+ return measures;
+ }
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFile.java b/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFile.java
index 5c39eef..ff396d0 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFile.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFile.java
@@ -3,9 +3,9 @@
import java.nio.file.Path;
interface RAPLFile {
- long extractEnergyInMicroJoules();
+ long extractEnergyInMicroJoules();
- static RAPLFile createFrom(Path file) {
- return ByteBufferRAPLFile.createFrom(file);
- }
+ static RAPLFile createFrom(Path file) {
+ return ByteBufferRAPLFile.createFrom(file);
+ }
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/SingleMeasureMeasures.java b/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/SingleMeasureMeasures.java
index 8128b5a..53d4aee 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/SingleMeasureMeasures.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/SingleMeasureMeasures.java
@@ -8,42 +8,43 @@
import net.laprun.sustainability.power.sensors.RegisteredPID;
class SingleMeasureMeasures implements Measures {
- private final Set trackedPIDs = new HashSet<>();
- private SensorMeasure measure;
-
- void singleMeasure(SensorMeasure sensorMeasure) {
- this.measure = sensorMeasure;
- }
-
- @Override
- public RegisteredPID register(long pid) {
- final var registeredPID = new RegisteredPID(pid);
- trackedPIDs.add(registeredPID);
- return registeredPID;
- }
-
- @Override
- public void unregister(RegisteredPID registeredPID) {
- trackedPIDs.remove(registeredPID);
- }
-
- @Override
- public Set trackedPIDs() {
- return trackedPIDs;
- }
-
- @Override
- public int numberOfTrackerPIDs() {
- return trackedPIDs.size();
- }
-
- @Override
- public void record(RegisteredPID pid, SensorMeasure sensorMeasure) {
- throw new UnsupportedOperationException("Shouldn't be needed");
- }
-
- @Override
- public SensorMeasure getOrDefault(RegisteredPID pid) {
- return trackedPIDs.contains(pid) && measure != null ? measure : Measures.missing;
- }
+ private final Set trackedPIDs = new HashSet<>();
+
+ private SensorMeasure measure;
+
+ void singleMeasure(SensorMeasure sensorMeasure) {
+ this.measure = sensorMeasure;
+ }
+
+ @Override
+ public RegisteredPID register(long pid) {
+ final var registeredPID = new RegisteredPID(pid);
+ trackedPIDs.add(registeredPID);
+ return registeredPID;
+ }
+
+ @Override
+ public void unregister(RegisteredPID registeredPID) {
+ trackedPIDs.remove(registeredPID);
+ }
+
+ @Override
+ public Set trackedPIDs() {
+ return trackedPIDs;
+ }
+
+ @Override
+ public int numberOfTrackerPIDs() {
+ return trackedPIDs.size();
+ }
+
+ @Override
+ public void record(RegisteredPID pid, SensorMeasure sensorMeasure) {
+ throw new UnsupportedOperationException("Shouldn't be needed");
+ }
+
+ @Override
+ public SensorMeasure getOrDefault(RegisteredPID pid) {
+ return trackedPIDs.contains(pid) && measure != null ? measure : Measures.missing;
+ }
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/AppleSiliconCPU.java b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/AppleSiliconCPU.java
index 1197e1c..c2d53a0 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/AppleSiliconCPU.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/AppleSiliconCPU.java
@@ -8,75 +8,77 @@
import net.laprun.sustainability.power.SensorMetadata;
class AppleSiliconCPU extends CPU {
- private static final SensorMetadata.ComponentMetadata cpuComponent = new SensorMetadata.ComponentMetadata(CPU, 0,
- "CPU power", true, "mW");
- private static final SensorMetadata.ComponentMetadata gpuComponent = new SensorMetadata.ComponentMetadata(GPU, 1,
- "GPU power", true, "mW");
- private static final SensorMetadata.ComponentMetadata aneComponent = new SensorMetadata.ComponentMetadata(ANE, 2,
- "Apple Neural Engine power", false, "mW");
- private static final SensorMetadata.ComponentMetadata cpuShareComponent = new SensorMetadata.ComponentMetadata(CPU_SHARE, 3,
- "Computed share of CPU", false, "decimal percentage");
- private static final String COMBINED = "Combined";
- private static final String POWER_INDICATOR = " Power: ";
- private static final int POWER_INDICATOR_LENGTH = POWER_INDICATOR.length();
+ private static final SensorMetadata.ComponentMetadata cpuComponent = new SensorMetadata.ComponentMetadata(CPU, 0,
+ "CPU power", true, "mW");
+ private static final SensorMetadata.ComponentMetadata gpuComponent = new SensorMetadata.ComponentMetadata(GPU, 1,
+ "GPU power", true, "mW");
+ private static final SensorMetadata.ComponentMetadata aneComponent = new SensorMetadata.ComponentMetadata(ANE, 2,
+ "Apple Neural Engine power", false, "mW");
+ private static final SensorMetadata.ComponentMetadata cpuShareComponent = new SensorMetadata.ComponentMetadata(
+ CPU_SHARE, 3, "Computed share of CPU", false, "decimal percentage");
+ private static final String COMBINED = "Combined";
+ private static final String POWER_INDICATOR = " Power: ";
+ private static final int POWER_INDICATOR_LENGTH = POWER_INDICATOR.length();
- public AppleSiliconCPU() {
+ public AppleSiliconCPU() {
+ }
+ private static void addComponentTo(String name, Map components) {
+ switch (name) {
+ case CPU, GPU, ANE:
+ // already pre-added
+ break;
+ case COMBINED:
+ // should be ignored
+ break;
+ default:
+ components.put(name, new SensorMetadata.ComponentMetadata(name, components.size(), name, false, "mW"));
}
+ }
- @Override
- public void addComponentIfFound(String line, Map components) {
- // looking for line fitting the: " Power: xxx mW" pattern, where "name" will be a considered metadata component
- final var powerIndex = line.indexOf(" Power");
- // lines with `-` as the second char are disregarded as of the form: "E-Cluster Power: 6 mW" which fits the metadata pattern but shouldn't be considered
- if (powerIndex >= 0 && '-' != line.charAt(1)) {
- addComponentTo(line.substring(0, powerIndex), components);
- }
+ @Override
+ public void addComponentIfFound(String line, Map components) {
+ // looking for line fitting the: " Power: xxx mW" pattern, where "name" will be a considered metadata
+ // component
+ final var powerIndex = line.indexOf(" Power");
+ // lines with `-` as the second char are disregarded as of the form: "E-Cluster Power: 6 mW"
+ // which fits the metadata pattern but shouldn't be considered
+ if (powerIndex >= 0 && '-' != line.charAt(1)) {
+ addComponentTo(line.substring(0, powerIndex), components);
}
+ }
- private static void addComponentTo(String name, Map components) {
- switch (name) {
- case CPU, GPU, ANE:
- // already pre-added
- break;
- case COMBINED:
- // should be ignored
- break;
- default:
- components.put(name, new SensorMetadata.ComponentMetadata(name, components.size(), name, false, "mW"));
- }
+ @Override
+ public boolean doneExtractingPowerComponents(String line, HashMap powerComponents) {
+ // looking for line fitting the: " Power: xxx mW" pattern and add all of the associated values together
+ final var powerIndex = line.indexOf(POWER_INDICATOR);
+ // lines with `-` as the second char are disregarded as of the form: "E-Cluster Power: 6 mW" which fits the
+ // pattern but shouldn't be considered also ignore Combined Power if available since it is the sum of the other
+ // components
+ if (powerIndex >= 0 && '-' != line.charAt(1) && !line.startsWith("Combined")) {
+ // get component name
+ final var name = line.substring(0, powerIndex);
+ // extract power value
+ final int value;
+ try {
+ value = Integer.parseInt(line.substring(powerIndex + POWER_INDICATOR_LENGTH, line.indexOf('m') - 1));
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot parse power value from line '" + line + "'", e);
+ }
+ powerComponents.put(name, value);
}
- @Override
- public boolean doneExtractingPowerComponents(String line, HashMap powerComponents) {
- // looking for line fitting the: " Power: xxx mW" pattern and add all of the associated values together
- final var powerIndex = line.indexOf(POWER_INDICATOR);
- // lines with `-` as the second char are disregarded as of the form: "E-Cluster Power: 6 mW" which fits the pattern but shouldn't be considered
- // also ignore Combined Power if available since it is the sum of the other components
- if (powerIndex >= 0 && '-' != line.charAt(1) && !line.startsWith("Combined")) {
- // get component name
- final var name = line.substring(0, powerIndex);
- // extract power value
- final int value;
- try {
- value = Integer.parseInt(line.substring(powerIndex + POWER_INDICATOR_LENGTH, line.indexOf('m') - 1));
- } catch (Exception e) {
- throw new IllegalStateException("Cannot parse power value from line '" + line + "'", e);
- }
- powerComponents.put(name, value);
- }
+ // we break out once we 've found all the extracted components (in this case, only cpuShare is not extracted)
+ return powerComponents.size() == metadata().componentCardinality() - 1;
+ }
- // we break out once we 've found all the extracted components (in this case, only cpuShare is not extracted)
- return powerComponents.size() == metadata().componentCardinality() - 1;
- }
-
- @Override
- boolean doneAfterComponentsInitialization(Map components) {
- // init map with known components
- components.put(MacOSPowermetricsSensor.CPU, cpuComponent);
- components.put(GPU, gpuComponent);
- components.put(ANE, aneComponent);
- components.put(CPU_SHARE, cpuShareComponent);
- return false;
- }
+ @Override
+ boolean doneAfterComponentsInitialization(Map components) {
+ // init map with known components
+ components.put(MacOSPowermetricsSensor.CPU, cpuComponent);
+ components.put(GPU, gpuComponent);
+ components.put(ANE, aneComponent);
+ components.put(CPU_SHARE, cpuShareComponent);
+ return false;
+ }
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/CPU.java b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/CPU.java
index e178d85..d5b381a 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/CPU.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/CPU.java
@@ -6,21 +6,21 @@
import net.laprun.sustainability.power.SensorMetadata;
abstract class CPU {
- private SensorMetadata metadata;
+ private SensorMetadata metadata;
- void addComponentIfFound(String line, Map components) {
- throw new IllegalStateException("Shouldn't be called as this processing is unneeded for this implementation");
- }
+ void addComponentIfFound(String line, Map components) {
+ throw new IllegalStateException("Shouldn't be called as this processing is unneeded for this implementation");
+ }
- abstract boolean doneExtractingPowerComponents(String line, HashMap powerComponents);
+ abstract boolean doneExtractingPowerComponents(String line, HashMap powerComponents);
- SensorMetadata metadata() {
- return metadata;
- }
+ SensorMetadata metadata() {
+ return metadata;
+ }
- void setMetadata(SensorMetadata metadata) {
- this.metadata = metadata;
- }
+ void setMetadata(SensorMetadata metadata) {
+ this.metadata = metadata;
+ }
- abstract boolean doneAfterComponentsInitialization(Map components);
+ abstract boolean doneAfterComponentsInitialization(Map components);
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/IntelCPU.java b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/IntelCPU.java
index 538f19a..48a07f9 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/IntelCPU.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/IntelCPU.java
@@ -10,34 +10,34 @@
class IntelCPU extends CPU {
- private static final SensorMetadata.ComponentMetadata packageComponent = new SensorMetadata.ComponentMetadata(PACKAGE, 0,
- "Intel energy model derived package power (CPUs+GT+SA)", true, "W");
- private static final SensorMetadata.ComponentMetadata cpuShareComponent = new SensorMetadata.ComponentMetadata(CPU_SHARE, 1,
- "Computed share of CPU", false, "decimal percentage");
-
- @Override
- public boolean doneExtractingPowerComponents(String line, HashMap powerComponents) {
- // line should look like: Intel energy model derived package power (CPUs+GT+SA): 8.53W
- final var powerIndex = line.indexOf("Intel ");
- if (powerIndex >= 0) {
- final var powerStartIndex = line.indexOf(':') + 1;
- final float value;
- try {
- value = Float.parseFloat(line.substring(powerStartIndex, line.indexOf('W')));
- } catch (Exception e) {
- throw new IllegalStateException("Cannot parse power value from line '" + line + "'", e);
- }
- powerComponents.put(PACKAGE, value);
- return true;
- }
-
- return false;
+ private static final SensorMetadata.ComponentMetadata packageComponent = new SensorMetadata.ComponentMetadata(
+ PACKAGE, 0, "Intel energy model derived package power (CPUs+GT+SA)", true, "W");
+ private static final SensorMetadata.ComponentMetadata cpuShareComponent = new SensorMetadata.ComponentMetadata(
+ CPU_SHARE, 1, "Computed share of CPU", false, "decimal percentage");
+
+ @Override
+ public boolean doneExtractingPowerComponents(String line, HashMap powerComponents) {
+ // line should look like: Intel energy model derived package power (CPUs+GT+SA): 8.53W
+ final var powerIndex = line.indexOf("Intel ");
+ if (powerIndex >= 0) {
+ final var powerStartIndex = line.indexOf(':') + 1;
+ final float value;
+ try {
+ value = Float.parseFloat(line.substring(powerStartIndex, line.indexOf('W')));
+ } catch (Exception e) {
+ throw new IllegalStateException("Cannot parse power value from line '" + line + "'", e);
+ }
+ powerComponents.put(PACKAGE, value);
+ return true;
}
- @Override
- boolean doneAfterComponentsInitialization(Map components) {
- components.put(PACKAGE, packageComponent);
- components.put(CPU_SHARE, cpuShareComponent);
- return true;
- }
+ return false;
+ }
+
+ @Override
+ boolean doneAfterComponentsInitialization(Map components) {
+ components.put(PACKAGE, packageComponent);
+ components.put(CPU_SHARE, cpuShareComponent);
+ return true;
+ }
}
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 c3d3f6b..ef4e62b 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
@@ -21,202 +21,206 @@
* A macOS powermetrics based {@link PowerSensor} implementation.
*/
public abstract class MacOSPowermetricsSensor extends AbstractPowerSensor {
- /**
- * 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 CPU cpu;
-
- public MacOSPowermetricsSensor() {
- super(new MapMeasures());
- }
-
- void initMetadata(InputStream inputStream) {
- try (BufferedReader input = new BufferedReader(new InputStreamReader(inputStream))) {
- String line;
- Map components = new HashMap<>();
- while ((line = input.readLine()) != null) {
- if (cpu == null) {
- // if we reached the OS line while cpu is still null, we're looking at an Apple Silicon CPU
- if (line.startsWith("OS ")) {
- cpu = new AppleSiliconCPU();
- } else if (line.startsWith("EFI ")) {
- cpu = new IntelCPU();
- }
-
- if (cpu != null && cpu.doneAfterComponentsInitialization(components)) {
- break;
- }
- } else {
- // skip empty / header lines
- if (line.isEmpty() || line.startsWith("*")) {
- continue;
- }
-
- cpu.addComponentIfFound(line, components);
- }
- }
+ /**
+ * 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 CPU cpu;
+
+ public MacOSPowermetricsSensor() {
+ super(new MapMeasures());
+ }
+
+ void initMetadata(InputStream inputStream) {
+ try (BufferedReader input = new BufferedReader(new InputStreamReader(inputStream))) {
+ String line;
+ Map components = new HashMap<>();
+ while ((line = input.readLine()) != null) {
+ if (cpu == null) {
+ // if we reached the OS line while cpu is still null, we're looking at an Apple Silicon CPU
+ if (line.startsWith("OS ")) {
+ cpu = new AppleSiliconCPU();
+ } else if (line.startsWith("EFI ")) {
+ cpu = new IntelCPU();
+ }
+
+ if (cpu != null && cpu.doneAfterComponentsInitialization(components)) {
+ break;
+ }
+ } else {
+ // skip empty / header lines
+ if (line.isEmpty() || line.startsWith("*")) {
+ continue;
+ }
+
+ cpu.addComponentIfFound(line, components);
+ }
+ }
- if (cpu == null) {
- throw new IllegalStateException("Couldn't determine CPU family from powermetrics output");
- }
+ if (cpu == null) {
+ throw new IllegalStateException("Couldn't determine CPU family from powermetrics output");
+ }
- final var metadata = new SensorMetadata(components,
- "macOS powermetrics derived information, see https://firefox-source-docs.mozilla.org/performance/powermetrics.html");
- cpu.setMetadata(metadata);
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
+ final var metadata = new SensorMetadata(components,
+ "macOS powermetrics derived information, see https://firefox-source-docs.mozilla.org/performance/powermetrics.html");
+ cpu.setMetadata(metadata);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
}
-
- @Override
- public SensorMetadata metadata() {
- return cpu.metadata();
+ }
+
+ @Override
+ public SensorMetadata metadata() {
+ return cpu.metadata();
+ }
+
+ private static class ProcessRecord {
+ final double cpu;
+ final double gpu;
+ final String pid;
+ private static final Pattern spaces = Pattern.compile("\\s+");
+
+ public ProcessRecord(String line) throws IllegalArgumentException {
+ // Name ID CPU ms/s samp ms/s User% Deadlines (<2 ms, 2-5 ms) Wakeups (Intr, Pkg idle) GPU ms/s
+ // iTerm2 1008 46.66 46.91 83.94 0.00 0.00 30.46 0.00 0.00
+ final var processData = spaces.split(line, 10);
+ if (processData.length != 10) {
+ throw new IllegalArgumentException("Received line doesn't conform to expected format: " + line);
+ }
+ pid = " " + processData[1] + " "; // pad to match prepared version for matching
+ cpu = Double.parseDouble(processData[3]);
+ gpu = Double.parseDouble(processData[9]);
}
+ }
+
+ Measures extractPowerMeasure(InputStream powerMeasureInput, Long tick) {
+ try {
+ // Should not be closed since it closes the process
+ BufferedReader input = new BufferedReader(new InputStreamReader(powerMeasureInput));
+ String line;
+
+ double totalSampledCPU = -1;
+ double totalSampledGPU = -1;
+ int headerLinesToSkip = 10;
+ // copy the pids so that we can remove them as soon as we've processed them
+ final var pidsToProcess = new HashSet<>(measures.trackedPIDs());
+ // start measure
+ final var pidMeasures = new HashMap(measures.numberOfTrackerPIDs());
+ final var metadata = cpu.metadata();
+ final var powerComponents = new HashMap(metadata.componentCardinality());
+ while ((line = input.readLine()) != null) {
+ if (headerLinesToSkip != 0) {
+ headerLinesToSkip--;
+ continue;
+ }
- private static class ProcessRecord {
- final double cpu;
- final double gpu;
- final String pid;
- private static final Pattern spaces = Pattern.compile("\\s+");
-
- public ProcessRecord(String line) throws IllegalArgumentException {
- //Name ID CPU ms/s samp ms/s User% Deadlines (<2 ms, 2-5 ms) Wakeups (Intr, Pkg idle) GPU ms/s
- //iTerm2 1008 46.66 46.91 83.94 0.00 0.00 30.46 0.00 0.00
- final var processData = spaces.split(line, 10);
- if (processData.length != 10) {
- throw new IllegalArgumentException("Received line doesn't conform to expected format: " + line);
- }
- pid = " " + processData[1] + " "; // pad to match prepared version for matching
- cpu = Double.parseDouble(processData[3]);
- gpu = Double.parseDouble(processData[9]);
+ if (line.isEmpty() || line.charAt(0) == '*') {
+ continue;
}
- }
- Measures extractPowerMeasure(InputStream powerMeasureInput, Long tick) {
- try {
- // Should not be closed since it closes the process
- BufferedReader input = new BufferedReader(new InputStreamReader(powerMeasureInput));
- String line;
-
- double totalSampledCPU = -1;
- double totalSampledGPU = -1;
- int headerLinesToSkip = 10;
- // copy the pids so that we can remove them as soon as we've processed them
- final var pidsToProcess = new HashSet<>(measures.trackedPIDs());
- // start measure
- final var pidMeasures = new HashMap(measures.numberOfTrackerPIDs());
- final var metadata = cpu.metadata();
- final var powerComponents = new HashMap(metadata.componentCardinality());
- while ((line = input.readLine()) != null) {
- if (headerLinesToSkip != 0) {
- headerLinesToSkip--;
- continue;
- }
-
- if (line.isEmpty() || line.charAt(0) == '*') {
- continue;
- }
-
- // first, look for process line detailing share
- if (!pidsToProcess.isEmpty()) {
- for (RegisteredPID pid : pidsToProcess) {
- if (line.contains(pid.stringForMatching())) {
- pidMeasures.put(pid, new ProcessRecord(line));
- pidsToProcess.remove(pid);
- break;
- }
- }
- continue;
- }
-
- if (totalSampledCPU < 0) {
- // then skip all lines until we get the totals
- if (line.startsWith("ALL_TASKS")) {
- final var totals = new ProcessRecord(line);
- // compute ratio
- totalSampledCPU = totals.cpu;
- totalSampledGPU = totals.gpu > 0 ? totals.gpu : 0;
- }
- continue;
- }
-
- // we need an exit condition to break out of the loop, otherwise we'll just keep looping forever since there are always new lines since the process is periodical
- // fixme: perhaps we should relaunch the process on each update loop instead of keeping it running? Not sure which is more efficient
- if (cpu.doneExtractingPowerComponents(line, powerComponents)) {
- break;
- }
+ // first, look for process line detailing share
+ if (!pidsToProcess.isEmpty()) {
+ for (RegisteredPID pid : pidsToProcess) {
+ if (line.contains(pid.stringForMatching())) {
+ pidMeasures.put(pid, new ProcessRecord(line));
+ pidsToProcess.remove(pid);
+ break;
}
-
- final var hasGPU = totalSampledGPU != 0;
- double finalTotalSampledGPU = totalSampledGPU;
- double finalTotalSampledCPU = totalSampledCPU;
- pidMeasures.forEach((pid, record) -> {
- final var cpuShare = record.cpu / finalTotalSampledCPU;
- final var measure = new double[metadata.componentCardinality()];
-
- metadata.components().forEach((name, cm) -> {
- final var index = cm.index();
- final var value = CPU_SHARE.equals(name) ? cpuShare : powerComponents.getOrDefault(name, 0).doubleValue();
-
- if (cm.isAttributed()) {
- final double attributionFactor;
- if (GPU.equals(name)) {
- attributionFactor = hasGPU ? record.gpu / finalTotalSampledGPU : 0.0;
- } else {
- attributionFactor = cpuShare;
- }
- measure[index] = value * attributionFactor;
- } else {
- measure[index] = value;
- }
- });
-
- measures.record(pid, new SensorMeasure(measure, tick));
- });
- } catch (Exception exception) {
- throw new RuntimeException(exception);
+ }
+ continue;
}
- return measures;
- }
-
- @Override
- public Measures update(Long tick) {
- return extractPowerMeasure(getInputStream(), tick);
- }
- protected abstract InputStream getInputStream();
+ if (totalSampledCPU < 0) {
+ // then skip all lines until we get the totals
+ if (line.startsWith("ALL_TASKS")) {
+ final var totals = new ProcessRecord(line);
+ // compute ratio
+ totalSampledCPU = totals.cpu;
+ totalSampledGPU = totals.gpu > 0 ? totals.gpu : 0;
+ }
+ continue;
+ }
- @Override
- public void unregister(RegisteredPID registeredPID) {
- super.unregister(registeredPID);
- // if we're not tracking any processes anymore, stop powermetrics as well
- if (measures.numberOfTrackerPIDs() == 0) {
- stop();
+ // we need an exit condition to break out of the loop, otherwise we'll just keep looping forever since
+ // there are always new lines since the process is periodical
+ // fixme: perhaps we should relaunch the process on each update loop instead of keeping it running? Not
+ // sure which is more efficient
+ if (cpu.doneExtractingPowerComponents(line, powerComponents)) {
+ break;
}
+ }
+
+ final var hasGPU = totalSampledGPU != 0;
+ double finalTotalSampledGPU = totalSampledGPU;
+ double finalTotalSampledCPU = totalSampledCPU;
+ pidMeasures.forEach((pid, record) -> {
+ final var cpuShare = record.cpu / finalTotalSampledCPU;
+ final var measure = new double[metadata.componentCardinality()];
+
+ metadata.components().forEach((name, cm) -> {
+ final var index = cm.index();
+ final var value = CPU_SHARE.equals(name)
+ ? cpuShare
+ : powerComponents.getOrDefault(name, 0).doubleValue();
+
+ if (cm.isAttributed()) {
+ final double attributionFactor;
+ if (GPU.equals(name)) {
+ attributionFactor = hasGPU ? record.gpu / finalTotalSampledGPU : 0.0;
+ } else {
+ attributionFactor = cpuShare;
+ }
+ measure[index] = value * attributionFactor;
+ } else {
+ measure[index] = value;
+ }
+ });
+
+ measures.record(pid, new SensorMeasure(measure, tick));
+ });
+ } catch (Exception exception) {
+ throw new RuntimeException(exception);
+ }
+ return measures;
+ }
+
+ @Override
+ public Measures update(Long tick) {
+ return extractPowerMeasure(getInputStream(), tick);
+ }
+
+ protected abstract InputStream getInputStream();
+
+ @Override
+ public void unregister(RegisteredPID registeredPID) {
+ super.unregister(registeredPID);
+ // if we're not tracking any processes anymore, stop powermetrics as well
+ if (measures.numberOfTrackerPIDs() == 0) {
+ stop();
}
+ }
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ProcessMacOSPowermetricsSensor.java b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ProcessMacOSPowermetricsSensor.java
index 72105a5..d898e02 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ProcessMacOSPowermetricsSensor.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ProcessMacOSPowermetricsSensor.java
@@ -3,41 +3,41 @@
import java.io.InputStream;
public class ProcessMacOSPowermetricsSensor extends MacOSPowermetricsSensor {
- private Process powermetrics;
+ private Process powermetrics;
- public ProcessMacOSPowermetricsSensor() {
- // extract metadata
- try {
- final var exec = new ProcessBuilder()
- .command("sudo", "powermetrics", "--samplers", "cpu_power", "-i", "10", "-n", "1")
- .start();
- initMetadata(exec.getInputStream());
- } catch (Exception e) {
- throw new RuntimeException("Couldn't execute powermetrics to extract metadata", e);
- }
+ public ProcessMacOSPowermetricsSensor() {
+ // extract metadata
+ try {
+ final var exec = new ProcessBuilder()
+ .command("sudo", "powermetrics", "--samplers", "cpu_power", "-i", "10", "-n", "1").start();
+ initMetadata(exec.getInputStream());
+ } catch (Exception e) {
+ throw new RuntimeException("Couldn't execute powermetrics to extract metadata", e);
}
+ }
- public void start(long frequency) throws Exception {
- if (!isStarted()) {
- // it takes some time for the external process in addition to the sampling time so adjust the sampling frequency to account for this so that at most one measure occurs during the sampling time window
- final var freq = Long.toString(frequency - 50);
- powermetrics = new ProcessBuilder().command("sudo", "powermetrics", "--samplers", "cpu_power,tasks",
- "--show-process-samp-norm", "--show-process-gpu", "-i", freq).start();
- }
+ public void start(long frequency) throws Exception {
+ if (!isStarted()) {
+ // it takes some time for the external process in addition to the sampling time so adjust the sampling
+ // frequency to account for this so that at most one measure occurs during the sampling time window
+ final var freq = Long.toString(frequency - 50);
+ powermetrics = new ProcessBuilder().command("sudo", "powermetrics", "--samplers", "cpu_power,tasks",
+ "--show-process-samp-norm", "--show-process-gpu", "-i", freq).start();
}
+ }
- @Override
- public boolean isStarted() {
- return powermetrics != null && powermetrics.isAlive();
- }
+ @Override
+ public boolean isStarted() {
+ return powermetrics != null && powermetrics.isAlive();
+ }
- @Override
- protected InputStream getInputStream() {
- return powermetrics.getInputStream();
- }
+ @Override
+ protected InputStream getInputStream() {
+ return powermetrics.getInputStream();
+ }
- @Override
- public void stop() {
- powermetrics.destroy();
- }
+ @Override
+ public void stop() {
+ powermetrics.destroy();
+ }
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ResourceMacOSPowermetricsSensor.java b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ResourceMacOSPowermetricsSensor.java
index 7f20722..d8c0ad9 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ResourceMacOSPowermetricsSensor.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ResourceMacOSPowermetricsSensor.java
@@ -3,28 +3,29 @@
import java.io.InputStream;
public class ResourceMacOSPowermetricsSensor extends MacOSPowermetricsSensor {
- private final String resourceName;
- private boolean started;
+ private final String resourceName;
- public ResourceMacOSPowermetricsSensor(String resourceName) {
- this.resourceName = resourceName;
- initMetadata(getInputStream());
- }
+ private boolean started;
- @Override
- protected InputStream getInputStream() {
- return Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName);
- }
+ public ResourceMacOSPowermetricsSensor(String resourceName) {
+ this.resourceName = resourceName;
+ initMetadata(getInputStream());
+ }
- @Override
- public boolean isStarted() {
- return started;
- }
+ @Override
+ protected InputStream getInputStream() {
+ return Thread.currentThread().getContextClassLoader().getResourceAsStream(resourceName);
+ }
+
+ @Override
+ public boolean isStarted() {
+ return started;
+ }
- @Override
- public void start(long samplingFrequencyInMillis) {
- if (!started) {
- started = true;
- }
+ @Override
+ public void start(long samplingFrequencyInMillis) {
+ if (!started) {
+ started = true;
}
+ }
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/test/TestPowerSensor.java b/server/src/main/java/net/laprun/sustainability/power/sensors/test/TestPowerSensor.java
index d53fed7..32f936d 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/test/TestPowerSensor.java
+++ b/server/src/main/java/net/laprun/sustainability/power/sensors/test/TestPowerSensor.java
@@ -10,42 +10,43 @@
@SuppressWarnings("unused")
public class TestPowerSensor extends AbstractPowerSensor {
- public static final String CPU = "cpu";
- public static final SensorMetadata DEFAULT = new SensorMetadata(
- Map.of(CPU, new SensorMetadata.ComponentMetadata(CPU, 0, "CPU", true, "mW")),
- "Test PowerSensor returning random values for a single 'cpu' component");
- private final SensorMetadata metadata;
- private boolean started;
-
- public TestPowerSensor() {
- this(DEFAULT);
- }
-
- public TestPowerSensor(SensorMetadata metadata) {
- super(new MapMeasures());
- this.metadata = metadata;
- }
-
- @Override
- public SensorMetadata metadata() {
- return metadata;
- }
-
- @Override
- public boolean isStarted() {
- return started;
- }
-
- @Override
- public void start(long samplingFrequencyInMillis) {
- if (!started) {
- started = true;
- }
- }
-
- @Override
- public Measures update(Long tick) {
- measures.trackedPIDs().forEach(pid -> measures.record(pid, new SensorMeasure(new double[] { Math.random() }, tick)));
- return measures;
+ public static final String CPU = "cpu";
+ public static final SensorMetadata DEFAULT = new SensorMetadata(
+ Map.of(CPU, new SensorMetadata.ComponentMetadata(CPU, 0, "CPU", true, "mW")),
+ "Test PowerSensor returning random values for a single 'cpu' component");
+ private final SensorMetadata metadata;
+ private boolean started;
+
+ public TestPowerSensor() {
+ this(DEFAULT);
+ }
+
+ public TestPowerSensor(SensorMetadata metadata) {
+ super(new MapMeasures());
+ this.metadata = metadata;
+ }
+
+ @Override
+ public SensorMetadata metadata() {
+ return metadata;
+ }
+
+ @Override
+ public boolean isStarted() {
+ return started;
+ }
+
+ @Override
+ public void start(long samplingFrequencyInMillis) {
+ if (!started) {
+ started = true;
}
+ }
+
+ @Override
+ public Measures update(Long tick) {
+ measures.trackedPIDs()
+ .forEach(pid -> measures.record(pid, new SensorMeasure(new double[]{Math.random()}, tick)));
+ return measures;
+ }
}
diff --git a/server/src/test/java/net/laprun/sustainability/power/CIPowerResourceTest.java b/server/src/test/java/net/laprun/sustainability/power/CIPowerResourceTest.java
index 39ea748..717c407 100644
--- a/server/src/test/java/net/laprun/sustainability/power/CIPowerResourceTest.java
+++ b/server/src/test/java/net/laprun/sustainability/power/CIPowerResourceTest.java
@@ -11,43 +11,38 @@
import io.quarkus.test.junit.TestProfile;
@QuarkusTest
-@TestProfile(CIQuarkusTestProfile.class) // only activated when quarkus.test.profile.tags='ci', uses @Mock annotated beans
+// only activated when quarkus.test.profile.tags='ci', uses @Mock annotated beans
+@TestProfile(CIQuarkusTestProfile.class)
public class CIPowerResourceTest {
- protected long getPid() {
- return 29419;
- }
-
- @Test
- public void testPowerEndpoint() {
- final var pid = getPid();
- given()
- .when().get("/power/" + pid)
- .then()
- .statusCode(200);
- }
-
- @Test
- public void testMacOSAppleSiliconMetadataEndpoint() {
- final var metadata = given()
- .when().get("/power/metadata")
- .then()
- .statusCode(200)
- .extract().body().as(SensorMetadata.class);
- assertEquals(4, metadata.componentCardinality());
- assertTrue(metadata.documentation().contains("powermetrics"));
- assertTrue(metadata.components().keySet().containsAll(Set.of("CPU", "GPU", "ANE", "cpuShare")));
-
- final var cpu = metadata.metadataFor("CPU");
- assertEquals(0, cpu.index());
- assertEquals("CPU", cpu.name());
- assertEquals("mW", cpu.unit());
- assertTrue(cpu.isAttributed());
-
- final var cpuShare = metadata.metadataFor("cpuShare");
- assertEquals(3, cpuShare.index());
- assertEquals("cpuShare", cpuShare.name());
- assertEquals("decimal percentage", cpuShare.unit());
- assertFalse(cpuShare.isAttributed());
- }
+ protected long getPid() {
+ return 29419;
+ }
+
+ @Test
+ public void testPowerEndpoint() {
+ final var pid = getPid();
+ given().when().get("/power/" + pid).then().statusCode(200);
+ }
+
+ @Test
+ public void testMacOSAppleSiliconMetadataEndpoint() {
+ final var metadata = given().when().get("/power/metadata").then().statusCode(200).extract().body()
+ .as(SensorMetadata.class);
+ assertEquals(4, metadata.componentCardinality());
+ assertTrue(metadata.documentation().contains("powermetrics"));
+ assertTrue(metadata.components().keySet().containsAll(Set.of("CPU", "GPU", "ANE", "cpuShare")));
+
+ final var cpu = metadata.metadataFor("CPU");
+ assertEquals(0, cpu.index());
+ assertEquals("CPU", cpu.name());
+ assertEquals("mW", cpu.unit());
+ assertTrue(cpu.isAttributed());
+
+ final var cpuShare = metadata.metadataFor("cpuShare");
+ assertEquals(3, cpuShare.index());
+ assertEquals("cpuShare", cpuShare.name());
+ assertEquals("decimal percentage", cpuShare.unit());
+ assertFalse(cpuShare.isAttributed());
+ }
}
diff --git a/server/src/test/java/net/laprun/sustainability/power/CIQuarkusTestProfile.java b/server/src/test/java/net/laprun/sustainability/power/CIQuarkusTestProfile.java
index e0271fe..3dd8729 100644
--- a/server/src/test/java/net/laprun/sustainability/power/CIQuarkusTestProfile.java
+++ b/server/src/test/java/net/laprun/sustainability/power/CIQuarkusTestProfile.java
@@ -6,8 +6,8 @@
public class CIQuarkusTestProfile implements QuarkusTestProfile {
- @Override
- public Set tags() {
- return Set.of("ci");
- }
+ @Override
+ public Set tags() {
+ return Set.of("ci");
+ }
}
diff --git a/server/src/test/java/net/laprun/sustainability/power/MockPowerMeasurer.java b/server/src/test/java/net/laprun/sustainability/power/MockPowerMeasurer.java
index 5b1919e..31faa66 100644
--- a/server/src/test/java/net/laprun/sustainability/power/MockPowerMeasurer.java
+++ b/server/src/test/java/net/laprun/sustainability/power/MockPowerMeasurer.java
@@ -6,8 +6,8 @@
@SuppressWarnings("unused")
public class MockPowerMeasurer extends PowerMeasurer {
- @Override
- protected long validPIDOrFail(String pid) {
- return Long.parseLong(pid);
- }
+ @Override
+ protected long validPIDOrFail(String pid) {
+ return Long.parseLong(pid);
+ }
}
diff --git a/server/src/test/java/net/laprun/sustainability/power/MockPowerSensor.java b/server/src/test/java/net/laprun/sustainability/power/MockPowerSensor.java
index fe9cd1e..f1806ff 100644
--- a/server/src/test/java/net/laprun/sustainability/power/MockPowerSensor.java
+++ b/server/src/test/java/net/laprun/sustainability/power/MockPowerSensor.java
@@ -6,7 +6,7 @@
@Mock
@SuppressWarnings("unused")
public class MockPowerSensor extends ResourceMacOSPowermetricsSensor {
- public MockPowerSensor() {
- super("sonoma-m1max.txt");
- }
+ public MockPowerSensor() {
+ super("sonoma-m1max.txt");
+ }
}
diff --git a/server/src/test/java/net/laprun/sustainability/power/PowerResourceIT.java b/server/src/test/java/net/laprun/sustainability/power/PowerResourceIT.java
index cd174de..0eb1098 100644
--- a/server/src/test/java/net/laprun/sustainability/power/PowerResourceIT.java
+++ b/server/src/test/java/net/laprun/sustainability/power/PowerResourceIT.java
@@ -4,5 +4,5 @@
@QuarkusIntegrationTest
public class PowerResourceIT extends PowerResourceTest {
- // Execute the same tests but in packaged mode.
+ // Execute the same tests but in packaged mode.
}
diff --git a/server/src/test/java/net/laprun/sustainability/power/PowerResourceTest.java b/server/src/test/java/net/laprun/sustainability/power/PowerResourceTest.java
index 10117e6..bf3cb9d 100644
--- a/server/src/test/java/net/laprun/sustainability/power/PowerResourceTest.java
+++ b/server/src/test/java/net/laprun/sustainability/power/PowerResourceTest.java
@@ -15,79 +15,67 @@
@QuarkusTest
public class PowerResourceTest {
- @Test
- public void testPowerEndpoint() {
- final var pid = getPid();
- given()
- .when().get("/power/" + pid)
- .then()
- .statusCode(200);
- }
+ @Test
+ public void testPowerEndpoint() {
+ final var pid = getPid();
+ given().when().get("/power/" + pid).then().statusCode(200);
+ }
- protected long getPid() {
- return ProcessHandle.current().pid();
- }
+ protected long getPid() {
+ return ProcessHandle.current().pid();
+ }
- @Test
- @EnabledOnOs(OS.MAC)
- @EnabledIfSystemProperty(named = "os.arch", matches = "aarch64")
- public void testMacOSAppleSiliconMetadataEndpoint() {
- final var metadata = given()
- .when().get("/power/metadata")
- .then()
- .statusCode(200)
- .extract().body().as(SensorMetadata.class);
- assertEquals(4, metadata.componentCardinality());
- assertTrue(metadata.documentation().contains("powermetrics"));
- assertTrue(metadata.components().keySet().containsAll(Set.of("CPU", "GPU", "ANE", "cpuShare")));
+ @Test
+ @EnabledOnOs(OS.MAC)
+ @EnabledIfSystemProperty(named = "os.arch", matches = "aarch64")
+ public void testMacOSAppleSiliconMetadataEndpoint() {
+ final var metadata = given().when().get("/power/metadata").then().statusCode(200).extract().body()
+ .as(SensorMetadata.class);
+ assertEquals(4, metadata.componentCardinality());
+ assertTrue(metadata.documentation().contains("powermetrics"));
+ assertTrue(metadata.components().keySet().containsAll(Set.of("CPU", "GPU", "ANE", "cpuShare")));
- final var cpu = metadata.metadataFor("CPU");
- assertEquals(0, cpu.index());
- assertEquals("CPU", cpu.name());
- assertEquals("mW", cpu.unit());
- assertTrue(cpu.isAttributed());
+ final var cpu = metadata.metadataFor("CPU");
+ assertEquals(0, cpu.index());
+ assertEquals("CPU", cpu.name());
+ assertEquals("mW", cpu.unit());
+ assertTrue(cpu.isAttributed());
- final var cpuShare = metadata.metadataFor("cpuShare");
- assertEquals(3, cpuShare.index());
- assertEquals("cpuShare", cpuShare.name());
- assertEquals("decimal percentage", cpuShare.unit());
- assertFalse(cpuShare.isAttributed());
- }
+ final var cpuShare = metadata.metadataFor("cpuShare");
+ assertEquals(3, cpuShare.index());
+ assertEquals("cpuShare", cpuShare.name());
+ assertEquals("decimal percentage", cpuShare.unit());
+ assertFalse(cpuShare.isAttributed());
+ }
- @Test
- @EnabledOnOs(OS.MAC)
- @EnabledIfSystemProperty(named = "os.arch", matches = "x86_64")
- public void testMacOSIntelMetadataEndpoint() {
- final var metadata = given()
- .when().get("/power/metadata")
- .then()
- .statusCode(200)
- .extract().body().as(SensorMetadata.class);
- assertEquals(2, metadata.componentCardinality());
- assertTrue(metadata.documentation().contains("powermetrics"));
- assertTrue(metadata.components().keySet().containsAll(Set.of("Package", "cpuShare")));
+ @Test
+ @EnabledOnOs(OS.MAC)
+ @EnabledIfSystemProperty(named = "os.arch", matches = "x86_64")
+ public void testMacOSIntelMetadataEndpoint() {
+ final var metadata = given().when().get("/power/metadata").then().statusCode(200).extract().body()
+ .as(SensorMetadata.class);
+ assertEquals(2, metadata.componentCardinality());
+ assertTrue(metadata.documentation().contains("powermetrics"));
+ assertTrue(metadata.components().keySet().containsAll(Set.of("Package", "cpuShare")));
- final var cpu = metadata.metadataFor("Package");
- assertEquals(0, cpu.index());
- assertEquals("W", cpu.unit());
- assertTrue(cpu.isAttributed());
+ final var cpu = metadata.metadataFor("Package");
+ assertEquals(0, cpu.index());
+ assertEquals("W", cpu.unit());
+ assertTrue(cpu.isAttributed());
- final var cpuShare = metadata.metadataFor("cpuShare");
- assertEquals(1, cpuShare.index());
- assertEquals("cpuShare", cpuShare.name());
- assertEquals("decimal percentage", cpuShare.unit());
- assertFalse(cpuShare.isAttributed());
- }
+ final var cpuShare = metadata.metadataFor("cpuShare");
+ assertEquals(1, cpuShare.index());
+ assertEquals("cpuShare", cpuShare.name());
+ assertEquals("decimal percentage", cpuShare.unit());
+ assertFalse(cpuShare.isAttributed());
+ }
- @Test
- @EnabledOnOs(OS.LINUX)
- public void testLinuxMetadataEndpoint() {
- final var metadata = given()
- .when().get("/power/metadata")
- .then()
- .statusCode(200)
- .extract().body().as(SensorMetadata.class);
- assertTrue(metadata.documentation().contains("RAPL"));
- }
+ @Test
+ @EnabledOnOs(OS.LINUX)
+ public void testLinuxMetadataEndpoint() {
+ final var metadata = given().when().get("/power/metadata").then().statusCode(200).extract().body()
+ .as(SensorMetadata.class);
+ assertTrue(metadata.documentation().contains("RAPL"));
+ }
}
diff --git a/server/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensorTest.java b/server/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensorTest.java
index d12b876..22b8e9c 100644
--- a/server/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensorTest.java
+++ b/server/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensorTest.java
@@ -10,27 +10,27 @@
import net.laprun.sustainability.power.SensorMetadata;
public class IntelRAPLSensorTest {
- @Test
- void checkMetadata() {
- var metadata = loadMetadata("rapl/intel-rapl_1/energy_uj", "rapl/intel-rapl_2/energy_uj");
- assertEquals(4, metadata.componentCardinality());
- checkComponent(metadata, "CPU", 0);
- checkComponent(metadata, "GPU", 1);
- checkComponent(metadata, "CPU_uj", 2);
- checkComponent(metadata, "GPU_uj", 3);
- }
+ @Test
+ void checkMetadata() {
+ var metadata = loadMetadata("rapl/intel-rapl_1/energy_uj", "rapl/intel-rapl_2/energy_uj");
+ assertEquals(4, metadata.componentCardinality());
+ checkComponent(metadata, "CPU", 0);
+ checkComponent(metadata, "GPU", 1);
+ checkComponent(metadata, "CPU_uj", 2);
+ checkComponent(metadata, "GPU_uj", 3);
+ }
- private SensorMetadata loadMetadata(String... fileNames) {
- final var files = Arrays.stream(fileNames)
- .map(name -> new File(getClass().getClassLoader().getResource(name).getFile()).getAbsolutePath())
- .toArray(String[]::new);
- return new IntelRAPLSensor(files).metadata();
- }
+ private SensorMetadata loadMetadata(String... fileNames) {
+ final var files = Arrays.stream(fileNames)
+ .map(name -> new File(getClass().getClassLoader().getResource(name).getFile()).getAbsolutePath())
+ .toArray(String[]::new);
+ return new IntelRAPLSensor(files).metadata();
+ }
- private static void checkComponent(SensorMetadata metadata, String name, int index) {
- // check string instead of constants to ensure "API" compatibility as these keys will be published
- final var component = metadata.metadataFor(name);
- assertEquals(name, component.name());
- assertEquals(index, component.index());
- }
+ private static void checkComponent(SensorMetadata metadata, String name, int index) {
+ // check string instead of constants to ensure "API" compatibility as these keys will be published
+ final var component = metadata.metadataFor(name);
+ assertEquals(name, component.name());
+ assertEquals(index, component.index());
+ }
}
diff --git a/server/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFileTest.java b/server/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFileTest.java
index 00589eb..f47c0fd 100644
--- a/server/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFileTest.java
+++ b/server/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFileTest.java
@@ -10,21 +10,21 @@
import org.junit.jupiter.api.Test;
public class RAPLFileTest {
- @Test
- void periodicReadingShouldWork() throws IOException, InterruptedException {
- for (int i = 0; i < 5; i++) {
- writeThenRead();
- }
+ @Test
+ void periodicReadingShouldWork() throws IOException, InterruptedException {
+ for (int i = 0; i < 5; i++) {
+ writeThenRead();
}
+ }
- private static void writeThenRead() throws IOException, InterruptedException {
- final var file = Path.of("target/test.txt");
- final var value = Math.abs(new Random().nextLong());
- Files.writeString(file, value + "\n");
- Thread.sleep(50);
+ private static void writeThenRead() throws IOException, InterruptedException {
+ final var file = Path.of("target/test.txt");
+ final var value = Math.abs(new Random().nextLong());
+ Files.writeString(file, value + "\n");
+ Thread.sleep(50);
- final var raplFile = ByteBufferRAPLFile.createFrom(file);
- final var measure = raplFile.extractEnergyInMicroJoules();
- assertEquals(value, measure);
- }
+ final var raplFile = ByteBufferRAPLFile.createFrom(file);
+ final var measure = raplFile.extractEnergyInMicroJoules();
+ assertEquals(value, measure);
+ }
}
diff --git a/server/src/test/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensorTest.java b/server/src/test/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensorTest.java
index 075a4ab..cbec482 100644
--- a/server/src/test/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensorTest.java
+++ b/server/src/test/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensorTest.java
@@ -10,84 +10,85 @@
class MacOSPowermetricsSensorTest {
- @Test
- void checkMetadata() {
- var metadata = loadMetadata("sonoma-m1max.txt");
- assertEquals(4, metadata.componentCardinality());
- checkComponent(metadata, "CPU", 0);
- checkComponent(metadata, "GPU", 1);
- checkComponent(metadata, "ANE", 2);
- checkComponent(metadata, "cpuShare", 3);
+ @Test
+ void checkMetadata() {
+ var metadata = loadMetadata("sonoma-m1max.txt");
+ assertEquals(4, metadata.componentCardinality());
+ checkComponent(metadata, "CPU", 0);
+ checkComponent(metadata, "GPU", 1);
+ checkComponent(metadata, "ANE", 2);
+ checkComponent(metadata, "cpuShare", 3);
- metadata = loadMetadata("monterey-m2.txt");
- assertEquals(7, metadata.componentCardinality());
- checkComponent(metadata, "CPU", 0);
- checkComponent(metadata, "GPU", 1);
- checkComponent(metadata, "ANE", 2);
- checkComponent(metadata, "cpuShare", 3);
- checkComponent(metadata, "DRAM", 4);
- checkComponent(metadata, "DCS", 5);
- checkComponent(metadata, "Package", 6);
+ metadata = loadMetadata("monterey-m2.txt");
+ assertEquals(7, metadata.componentCardinality());
+ checkComponent(metadata, "CPU", 0);
+ checkComponent(metadata, "GPU", 1);
+ checkComponent(metadata, "ANE", 2);
+ checkComponent(metadata, "cpuShare", 3);
+ checkComponent(metadata, "DRAM", 4);
+ checkComponent(metadata, "DCS", 5);
+ checkComponent(metadata, "Package", 6);
- metadata = loadMetadata("sonoma-intel.txt");
- assertEquals(2, metadata.componentCardinality());
- checkComponent(metadata, "Package", 0);
- checkComponent(metadata, "cpuShare", 1);
- }
+ metadata = loadMetadata("sonoma-intel.txt");
+ assertEquals(2, metadata.componentCardinality());
+ checkComponent(metadata, "Package", 0);
+ checkComponent(metadata, "cpuShare", 1);
+ }
- private static SensorMetadata loadMetadata(String fileName) {
- return new ResourceMacOSPowermetricsSensor(fileName).metadata();
- }
+ private static SensorMetadata loadMetadata(String fileName) {
+ return new ResourceMacOSPowermetricsSensor(fileName).metadata();
+ }
- private static void checkComponent(SensorMetadata metadata, String name, int index) {
- // check string instead of constants to ensure "API" compatibility as these keys will be published
- final var component = metadata.metadataFor(name);
- assertEquals(name, component.name());
- assertEquals(index, component.index());
- }
+ private static void checkComponent(SensorMetadata metadata, String name, int index) {
+ // check string instead of constants to ensure "API" compatibility as these keys will be published
+ final var component = metadata.metadataFor(name);
+ assertEquals(name, component.name());
+ assertEquals(index, component.index());
+ }
- @Test
- void extractPowerMeasureForM1Max() {
- checkPowerMeasure("sonoma-m1max.txt", 211, MacOSPowermetricsSensor.CPU);
- }
+ @Test
+ void extractPowerMeasureForM1Max() {
+ checkPowerMeasure("sonoma-m1max.txt", 211, MacOSPowermetricsSensor.CPU);
+ }
- @Test
- void extractPowerMeasureForM2() {
- checkPowerMeasure("monterey-m2.txt", 10, MacOSPowermetricsSensor.CPU);
- }
+ @Test
+ void extractPowerMeasureForM2() {
+ checkPowerMeasure("monterey-m2.txt", 10, MacOSPowermetricsSensor.CPU);
+ }
- @Test
- void extractPowerMeasureForIntel() {
- checkPowerMeasure("sonoma-intel.txt", 8.53f, MacOSPowermetricsSensor.PACKAGE);
- }
+ @Test
+ void extractPowerMeasureForIntel() {
+ checkPowerMeasure("sonoma-intel.txt", 8.53f, MacOSPowermetricsSensor.PACKAGE);
+ }
- private static void checkPowerMeasure(String testFileName, float total, String totalMeasureName) {
- final var sensor = new ResourceMacOSPowermetricsSensor(testFileName);
- final var metadata = sensor.metadata();
- final var pid1 = sensor.register(29419);
- final var pid2 = sensor.register(391);
+ private static void checkPowerMeasure(String testFileName, float total, String totalMeasureName) {
+ final var sensor = new ResourceMacOSPowermetricsSensor(testFileName);
+ final var metadata = sensor.metadata();
+ final var pid1 = sensor.register(29419);
+ final var pid2 = sensor.register(391);
- // re-open the stream to read the measure this time
- final var measure = sensor.update(0L);
- final var totalMeasureMetadata = metadata.metadataFor(totalMeasureName);
- final var pid1CPUShare = 23.88 / 1222.65;
- assertEquals((pid1CPUShare * total), getComponent(measure, pid1, totalMeasureMetadata));
- final var pid2CPUShare = 283.25 / 1222.65;
- assertEquals((pid2CPUShare * total), getComponent(measure, pid2, totalMeasureMetadata));
- // check cpu share
- final var cpuShareMetadata = metadata.metadataFor(MacOSPowermetricsSensor.CPU_SHARE);
- assertEquals(pid1CPUShare, getComponent(measure, pid1, cpuShareMetadata));
- assertEquals(pid2CPUShare, getComponent(measure, pid2, cpuShareMetadata));
- if (metadata.exists(MacOSPowermetricsSensor.GPU)) {
- // check that gpu should be 0
- final var gpuMetadata = metadata.metadataFor(MacOSPowermetricsSensor.GPU);
- assertEquals(0.0, getComponent(measure, pid1, gpuMetadata));
- assertEquals(0.0, getComponent(measure, pid2, gpuMetadata));
- }
+ // re-open the stream to read the measure this time
+ final var measure = sensor.update(0L);
+ final var totalMeasureMetadata = metadata.metadataFor(totalMeasureName);
+ final var pid1CPUShare = 23.88 / 1222.65;
+ assertEquals((pid1CPUShare * total), getComponent(measure, pid1, totalMeasureMetadata));
+ final var pid2CPUShare = 283.25 / 1222.65;
+ assertEquals((pid2CPUShare * total), getComponent(measure, pid2, totalMeasureMetadata));
+ // check cpu share
+ final var cpuShareMetadata = metadata.metadataFor(MacOSPowermetricsSensor.CPU_SHARE);
+ assertEquals(pid1CPUShare, getComponent(measure, pid1, cpuShareMetadata));
+ assertEquals(pid2CPUShare, getComponent(measure, pid2, cpuShareMetadata));
+ if (metadata.exists(MacOSPowermetricsSensor.GPU)) {
+ // check that gpu should be 0
+ final var gpuMetadata = metadata.metadataFor(MacOSPowermetricsSensor.GPU);
+ assertEquals(0.0, getComponent(measure, pid1, gpuMetadata));
+ assertEquals(0.0, getComponent(measure, pid2, gpuMetadata));
}
+ }
- private static double getComponent(Measures measure, RegisteredPID pid1, SensorMetadata.ComponentMetadata metadata) {
- final var index = metadata.index();
- return measure.getOrDefault(pid1).components()[index];
- }
+ private static double getComponent(Measures measure, RegisteredPID pid1,
+ SensorMetadata.ComponentMetadata metadata) {
+ final var index = metadata.index();
+ return measure.getOrDefault(pid1).components()[index];
+ }
}