diff --git a/README.md b/README.md index 6c4a486..6dfd73f 100644 --- a/README.md +++ b/README.md @@ -7,23 +7,33 @@ without further processing. This project uses [Quarkus](https://quarkus.io), the Supersonic Subatomic Java Framework and comprises 3 modules: - `analysis`, which contains utilities helpful to analyze power measures (computations, statistics, histograms, etc…) -- `if-manifest-export`, which provides a means to export a stopped measure as a [Green Software Foundation](https://greensoftware.foundation/) [Impact Framework](https://if.greensoftware.foundation/) manifest +- `if-manifest-export`, which provides a means to export a stopped measure as + a [Green Software Foundation](https://greensoftware.foundation/) [Impact Framework](https://if.greensoftware.foundation/) + manifest - `measure`, which provides classes to help record and process measures in client applications - `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 - in `[application.properties](https://github.com/metacosm/power-server/blob/87bba3196fa0e552665b4f1d22006377779b0959/server/src/main/resources/application.properties#L1)`) - -The server provides two endpoints: `/power/metadata` and `/power/{pid}` where `pid` is a String representation of a -process identifier, identifying a process running on the machine where `power-server` is running. -The metadata endpoint provides information about how measures streamed from the main endpoint is formatted as well as -information about power components. The main endpoint streams `SensorMeasure` objects as defined in the `metadata` -module as an array of double measures. Typically, main sensor components are measured in milli Watts for easier -consumption but clients should check the information provided by the metadata endpoint to learn the layout of the -measures array and which meaning they carry. For example, the macOS implementation provides a decimal percentage measure -of the CPU share of the measured process as part of the returned measure. This information doesn't exist on the Linux -information where the measures are actually energy counters for the whole system, the client being then in charge of -computing the process attribution. + in + `[application.properties](https://github.com/metacosm/power-server/blob/87bba3196fa0e552665b4f1d22006377779b0959/server/src/main/resources/application.properties#L1)`) + +The server provides the following endpoints: + +- `/power/{pid}` where `pid` is a String representation of a process identifier, identifying a process running on the + machine where `power-server` is running. +- `/power/metadata` provides information about how measures streamed from the main endpoint is formatted as well as + information about power components. +- `/power/sampling` provides the currently configured power measure sampling period, which can be configured using the + `net.laprun.sustainability.power.sampling-period` property, passing it a `String` that can be converted to a Java + `Duration`. + +The main endpoint streams `SensorMeasure` objects as defined in the `metadata`module as an array of double measures. +Typically, main sensor components are measured in milli Watts for easier consumption but clients should check the +information provided by the metadata endpoint to learn the layout of the measures array and which meaning they carry. +For example, the macOS implementation provides a decimal percentage +measure of the CPU share of the measured process as part of the returned measure. This information doesn't exist on the +Linux implementation where the measures are actually energy counters for the whole system, the client being then in +charge of computing the process attribution. Only Linux/amd64 and macOS (amd64/apple silicon) are supported at the moment. Of note, this tool needs to be run via `sudo` because power consumption information is considered as security sensitive (as it can @@ -33,7 +43,8 @@ information. ### macOS Power monitoring is performed using the -bundled `[powermetrics](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/PrioritizeWorkAtTheTaskLevel.html#//apple_ref/doc/uid/TP40013929-CH35-SW10)` +bundled +`[powermetrics](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/PrioritizeWorkAtTheTaskLevel.html#//apple_ref/doc/uid/TP40013929-CH35-SW10)` tool, which is run with specific parameters and which output is then parsed into a usable representation. @@ -69,5 +80,6 @@ Once you've performed a full build, you can also compile the server to a native Once build, you can run the server as follows: - JVM mode: `sudo java -jar server/target/quarkus-app/quarkus-run.jar` from the project root -- Dev mode: `cd server; sudo mvn quarkus:dev` or `cd server; sudo quarkus dev` (will only work if `root` has Java properly configured) +- Dev mode: `cd server; sudo mvn quarkus:dev` or `cd server; sudo quarkus dev` (will only work if `root` has Java + properly configured) - Native mode: `sudo ./server/target/*-runner` 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..4e2ed6c 100644 --- a/server/src/main/java/net/laprun/sustainability/power/PowerMeasurer.java +++ b/server/src/main/java/net/laprun/sustainability/power/PowerMeasurer.java @@ -5,6 +5,8 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; +import org.eclipse.microprofile.config.inject.ConfigProperty; + import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.infrastructure.Infrastructure; import net.laprun.sustainability.power.sensors.Measures; @@ -13,10 +15,13 @@ @ApplicationScoped public class PowerMeasurer { - public static final int SAMPLING_FREQUENCY_IN_MILLIS = 500; + public static final String DEFAULT_SAMPLING_PERIOD = "PT0.5S"; @Inject PowerSensor sensor; + @ConfigProperty(name = "net.laprun.sustainability.power.sampling-period", defaultValue = DEFAULT_SAMPLING_PERIOD) + Duration samplingPeriod; + private Multi periodicSensorCheck; public Multi startTracking(String pid) throws Exception { @@ -24,9 +29,9 @@ public Multi startTracking(String pid) throws Exception { final var parsedPID = validPIDOrFail(pid); if (!sensor.isStarted()) { - sensor.start(SAMPLING_FREQUENCY_IN_MILLIS); + sensor.start(samplingPeriod.toMillis()); periodicSensorCheck = Multi.createFrom().ticks() - .every(Duration.ofMillis(SAMPLING_FREQUENCY_IN_MILLIS)) + .every(samplingPeriod) .map(sensor::update) .broadcast() .withCancellationAfterLastSubscriberDeparture() @@ -50,4 +55,8 @@ protected long validPIDOrFail(String pid) { public SensorMetadata metadata() { return sensor.metadata(); } + + public Duration getSamplingPeriod() { + return samplingPeriod; + } } 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..8b3da4a 100644 --- a/server/src/main/java/net/laprun/sustainability/power/PowerResource.java +++ b/server/src/main/java/net/laprun/sustainability/power/PowerResource.java @@ -1,5 +1,7 @@ package net.laprun.sustainability.power; +import java.time.Duration; + import jakarta.inject.Inject; import jakarta.ws.rs.*; import jakarta.ws.rs.core.MediaType; @@ -29,4 +31,10 @@ public Multi powerFor(@PathParam("pid") String pid) throws Except public SensorMetadata metadata() { return measurer.metadata(); } + + @GET + @Path("sampling") + public Duration samplingPeriod() { + return measurer.getSamplingPeriod(); + } } 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 fe49907..d2ed1a5 100644 --- a/server/src/test/java/net/laprun/sustainability/power/PowerResourceTest.java +++ b/server/src/test/java/net/laprun/sustainability/power/PowerResourceTest.java @@ -3,6 +3,7 @@ import static io.restassured.RestAssured.given; import static org.junit.jupiter.api.Assertions.*; +import java.time.Duration; import java.util.Set; import org.junit.jupiter.api.Test; @@ -24,6 +25,16 @@ public void testPowerEndpoint() { .statusCode(200); } + @Test + public void samplingPeriod() { + final Duration duration = given() + .when().get("/power/sampling") + .then() + .statusCode(200) + .extract().body().as(Duration.class); + assertEquals(Duration.parse(PowerMeasurer.DEFAULT_SAMPLING_PERIOD), duration); + } + protected long getPid() { return ProcessHandle.current().pid(); }