diff --git a/.gitignore b/.gitignore
index 8c7863e..579f9a8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -41,3 +41,6 @@ nb-configuration.xml
# Plugin directory
/.quarkus/cli/plugins/
+
+/output.txt
+cli/output.txt
diff --git a/backend/pom.xml b/backend/pom.xml
new file mode 100644
index 0000000..1a01f33
--- /dev/null
+++ b/backend/pom.xml
@@ -0,0 +1,53 @@
+
+
+ 4.0.0
+
+ net.laprun.sustainability
+ power-server-parent
+ 0.3.0-SNAPSHOT
+
+
+ power-server-backend
+ power-server : backend
+ Power consumption measuring backend
+
+
+
+ net.laprun.sustainability
+ power-server-metadata
+ ${project.version}
+
+
+ io.quarkus
+ quarkus-hibernate-orm-panache
+
+
+ io.quarkus
+ quarkus-scheduler
+
+
+ io.quarkiverse.jdbc
+ quarkus-jdbc-sqlite4j
+ ${sqlite4j.version}
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+
+
+
+ net.revelc.code.formatter
+ formatter-maven-plugin
+
+ ../contributing/eclipse-format.xml
+
+
+
+
+
+
diff --git a/server/src/main/java/net/laprun/sustainability/power/Security.java b/backend/src/main/java/net/laprun/sustainability/power/Security.java
similarity index 95%
rename from server/src/main/java/net/laprun/sustainability/power/Security.java
rename to backend/src/main/java/net/laprun/sustainability/power/Security.java
index a2db4aa..7bbabed 100644
--- a/server/src/main/java/net/laprun/sustainability/power/Security.java
+++ b/backend/src/main/java/net/laprun/sustainability/power/Security.java
@@ -21,7 +21,8 @@ public Security(SmallRyeConfig config) {
pwd = config.getConfigValue(SECRET_PROPERTY_KEY).getValue();
if (pwd == null && !isRunningAsAdministrator()) {
throw new IllegalStateException(
- "This application requires sudo access. Either provide a sudo secret using the 'power-server.sudo.secret' property or run using sudo.");
+ "This application requires sudo access. Either provide a sudo secret using the '" +
+ SECRET_PROPERTY_KEY + "' property or run using sudo.");
}
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/persistence/Measure.java b/backend/src/main/java/net/laprun/sustainability/power/persistence/Measure.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/persistence/Measure.java
rename to backend/src/main/java/net/laprun/sustainability/power/persistence/Measure.java
diff --git a/server/src/main/java/net/laprun/sustainability/power/persistence/Persistence.java b/backend/src/main/java/net/laprun/sustainability/power/persistence/Persistence.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/persistence/Persistence.java
rename to backend/src/main/java/net/laprun/sustainability/power/persistence/Persistence.java
diff --git a/server/src/main/java/net/laprun/sustainability/power/persistence/SQLiteFilePersister.java b/backend/src/main/java/net/laprun/sustainability/power/persistence/SQLiteFilePersister.java
similarity index 98%
rename from server/src/main/java/net/laprun/sustainability/power/persistence/SQLiteFilePersister.java
rename to backend/src/main/java/net/laprun/sustainability/power/persistence/SQLiteFilePersister.java
index 0998bf7..7e6e26d 100644
--- a/server/src/main/java/net/laprun/sustainability/power/persistence/SQLiteFilePersister.java
+++ b/backend/src/main/java/net/laprun/sustainability/power/persistence/SQLiteFilePersister.java
@@ -46,6 +46,7 @@ void scheduled() {
// Execute a backup during shutdown
public void onShutdown(@Observes ShutdownEvent event) {
+ Log.info("Persisting database on shutdown");
backup();
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/AbstractPowerSensor.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/AbstractPowerSensor.java
similarity index 87%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/AbstractPowerSensor.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/AbstractPowerSensor.java
index fc52ce4..eb3d3aa 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/AbstractPowerSensor.java
+++ b/backend/src/main/java/net/laprun/sustainability/power/sensors/AbstractPowerSensor.java
@@ -3,6 +3,7 @@
import io.quarkus.logging.Log;
public abstract class AbstractPowerSensor implements PowerSensor {
+ // private static final Logger log = Logger.getLogger(AbstractPowerSensor.class);
protected final M measures;
public AbstractPowerSensor(M measures) {
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/MapMeasures.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/MapMeasures.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/MapMeasures.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/MapMeasures.java
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/Measures.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/Measures.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/Measures.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/Measures.java
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/PowerSensor.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/PowerSensor.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/PowerSensor.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/PowerSensor.java
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/PowerSensorProducer.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/PowerSensorProducer.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/PowerSensorProducer.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/PowerSensorProducer.java
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/RegisteredPID.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/RegisteredPID.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/RegisteredPID.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/RegisteredPID.java
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/ByteBufferRAPLFile.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/ByteBufferRAPLFile.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/ByteBufferRAPLFile.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/ByteBufferRAPLFile.java
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensor.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensor.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensor.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensor.java
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFile.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFile.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFile.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFile.java
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/SingleMeasureMeasures.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/SingleMeasureMeasures.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/SingleMeasureMeasures.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/linux/rapl/SingleMeasureMeasures.java
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/AppleSiliconCPU.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/AppleSiliconCPU.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/AppleSiliconCPU.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/AppleSiliconCPU.java
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/CPU.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/CPU.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/CPU.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/CPU.java
diff --git a/backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/FileMacOSPowermetricsSensor.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/FileMacOSPowermetricsSensor.java
new file mode 100644
index 0000000..90ee33d
--- /dev/null
+++ b/backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/FileMacOSPowermetricsSensor.java
@@ -0,0 +1,44 @@
+package net.laprun.sustainability.power.sensors.macos.powermetrics;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+
+/**
+ * The aim of this sensor is to only perform one long measure and then read the power information from it once done,
+ */
+public class FileMacOSPowermetricsSensor extends MacOSPowermetricsSensor {
+ private final File file;
+ private boolean started;
+
+ public FileMacOSPowermetricsSensor(File file) {
+ this.file = file;
+ }
+
+ @Override
+ protected InputStream getInputStream() {
+ try {
+ return new FileInputStream(file);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public boolean isStarted() {
+ return started;
+ }
+
+ @Override
+ public void start(long samplingFrequencyInMillis) {
+ if (!started) {
+ started = true;
+ }
+ }
+
+ @Override
+ public void stop() {
+ started = false;
+ initMetadata(getInputStream());
+ }
+}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/IntelCPU.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/IntelCPU.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/IntelCPU.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/IntelCPU.java
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java
similarity index 98%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java
index dc7c625..899eb58 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java
+++ b/backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensor.java
@@ -151,6 +151,8 @@ Measures extractPowerMeasure(InputStream powerMeasureInput, Long tick) {
pidsToProcess.remove(pid);
break;
}
+
+ // todo? if pid is not found, this will loop forever and we should break if ALL_TASKS is reached without draining the pids to process
}
continue;
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ProcessMacOSPowermetricsSensor.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ProcessMacOSPowermetricsSensor.java
similarity index 95%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ProcessMacOSPowermetricsSensor.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ProcessMacOSPowermetricsSensor.java
index 8ebb651..4d84d02 100644
--- a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ProcessMacOSPowermetricsSensor.java
+++ b/backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ProcessMacOSPowermetricsSensor.java
@@ -37,7 +37,8 @@ public ProcessMacOSPowermetricsSensor(Security security) {
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);
+ frequency = Math.min(0, frequency - 50);
+ final var freq = Long.toString(frequency);
powermetrics = security.execPowermetrics("cpu_power,tasks", "--show-process-samp-norm", "--show-process-gpu", "-i",
freq);
}
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ResourceMacOSPowermetricsSensor.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ResourceMacOSPowermetricsSensor.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ResourceMacOSPowermetricsSensor.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/macos/powermetrics/ResourceMacOSPowermetricsSensor.java
diff --git a/server/src/main/java/net/laprun/sustainability/power/sensors/test/TestPowerSensor.java b/backend/src/main/java/net/laprun/sustainability/power/sensors/test/TestPowerSensor.java
similarity index 100%
rename from server/src/main/java/net/laprun/sustainability/power/sensors/test/TestPowerSensor.java
rename to backend/src/main/java/net/laprun/sustainability/power/sensors/test/TestPowerSensor.java
diff --git a/backend/src/main/resources/META-INF/beans.xml b/backend/src/main/resources/META-INF/beans.xml
new file mode 100644
index 0000000..e69de29
diff --git a/server/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensorTest.java b/backend/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensorTest.java
similarity index 100%
rename from server/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensorTest.java
rename to backend/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/IntelRAPLSensorTest.java
diff --git a/server/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFileTest.java b/backend/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFileTest.java
similarity index 100%
rename from server/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFileTest.java
rename to backend/src/test/java/net/laprun/sustainability/power/sensors/linux/rapl/RAPLFileTest.java
diff --git a/server/src/test/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensorTest.java b/backend/src/test/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensorTest.java
similarity index 100%
rename from server/src/test/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensorTest.java
rename to backend/src/test/java/net/laprun/sustainability/power/sensors/macos/powermetrics/MacOSPowermetricsSensorTest.java
diff --git a/server/src/test/resources/monterey-m2.txt b/backend/src/test/resources/monterey-m2.txt
similarity index 100%
rename from server/src/test/resources/monterey-m2.txt
rename to backend/src/test/resources/monterey-m2.txt
diff --git a/server/src/test/resources/rapl/intel-rapl_1/energy_uj b/backend/src/test/resources/rapl/intel-rapl_1/energy_uj
similarity index 100%
rename from server/src/test/resources/rapl/intel-rapl_1/energy_uj
rename to backend/src/test/resources/rapl/intel-rapl_1/energy_uj
diff --git a/server/src/test/resources/rapl/intel-rapl_1/name b/backend/src/test/resources/rapl/intel-rapl_1/name
similarity index 100%
rename from server/src/test/resources/rapl/intel-rapl_1/name
rename to backend/src/test/resources/rapl/intel-rapl_1/name
diff --git a/server/src/test/resources/rapl/intel-rapl_2/energy_uj b/backend/src/test/resources/rapl/intel-rapl_2/energy_uj
similarity index 100%
rename from server/src/test/resources/rapl/intel-rapl_2/energy_uj
rename to backend/src/test/resources/rapl/intel-rapl_2/energy_uj
diff --git a/server/src/test/resources/rapl/intel-rapl_2/name b/backend/src/test/resources/rapl/intel-rapl_2/name
similarity index 100%
rename from server/src/test/resources/rapl/intel-rapl_2/name
rename to backend/src/test/resources/rapl/intel-rapl_2/name
diff --git a/server/src/test/resources/sonoma-intel.txt b/backend/src/test/resources/sonoma-intel.txt
similarity index 100%
rename from server/src/test/resources/sonoma-intel.txt
rename to backend/src/test/resources/sonoma-intel.txt
diff --git a/backend/src/test/resources/sonoma-m1max.txt b/backend/src/test/resources/sonoma-m1max.txt
new file mode 100644
index 0000000..e9da7f8
--- /dev/null
+++ b/backend/src/test/resources/sonoma-m1max.txt
@@ -0,0 +1,178 @@
+Machine model: MacBookPro18,4
+OS version: 22G120
+Boot arguments:
+Boot time: Sat Oct 21 19:09:01 2023
+
+
+
+*** Sampled system activity (Mon Oct 23 16:51:12 2023 +0200) (1012.07ms elapsed) ***
+
+*** Running tasks ***
+
+Name ID CPU ms/s samp ms/s User% Deadlines (<2 ms, 2-5 ms) Wakeups (Intr, Pkg idle) GPU ms/s
+WindowServer 391 281.98 283.25 82.97 1.97 0.00 428.87 8.85 0.00
+mdworker_shared 29420 183.26 112.83 65.49 0.00 0.00 0.00 0.00 0.00
+powermetrics 29419 23.78 23.88 6.05 0.00 0.00 0.98 0.00 0.00
+firefox 678 151.38 152.06 75.55 0.00 0.98 34.43 0.98 0.00
+kernel_task 0 116.12 116.64 0.00 422.96 0.00 805.60 74.76 0.00
+idea 21834 126.37 126.94 64.94 708.22 4.92 898.06 26.56 0.00
+iTerm2 1008 32.62 32.77 76.15 0.00 0.00 22.62 0.00 0.00
+plugin-container 1096 28.85 28.98 84.36 0.00 0.00 6.89 0.00 0.00
+Music 669 41.97 42.16 72.99 0.00 0.00 86.56 0.98 0.00
+coreaudiod 412 35.09 35.25 87.47 0.00 0.00 86.56 1.97 0.00
+plugin-container 1088 41.86 42.05 93.85 9.84 4.92 44.26 0.00 0.00
+plugin-container 1037 24.55 24.67 84.93 4.92 3.93 17.71 0.00 0.00
+plugin-container 1089 13.66 13.72 85.91 0.00 0.00 3.93 0.00 0.00
+cfprefsd 604 6.33 6.36 45.20 0.00 0.00 0.00 0.00 0.00
+DEAD_TASKS -1 12.64 12.64 29.96 0.00 0.00 0.00 0.00 0.00
+launchd 1 7.86 7.90 38.55 0.00 0.00 0.00 0.00 0.00
+jetbrains-toolbox 1015 10.18 10.23 63.26 0.98 0.00 46.23 1.97 0.00
+sentineld 824 10.29 10.34 75.10 0.00 0.00 0.98 0.00 0.00
+cfprefsd 392 4.59 4.61 45.98 0.00 0.00 0.00 0.00 0.00
+splunkd 1501 9.18 9.22 37.18 4.92 0.00 40.33 1.97 0.00
+WindowManager 619 4.12 4.13 88.28 0.00 0.00 0.00 0.00 0.00
+mds_stores 610 5.72 5.75 46.17 0.00 0.00 0.00 0.00 0.00
+mds 350 5.55 5.58 53.73 0.00 0.00 1.97 0.00 0.00
+plugin-container 1085 7.85 7.89 88.19 1.97 0.98 11.80 2.95 0.00
+opendirectoryd 362 3.59 3.61 50.33 0.00 0.00 0.00 0.00 0.00
+Signal Helper (GPU) 26193 2.58 2.59 26.50 0.00 0.00 0.00 0.00 0.00
+notifyd 385 3.49 3.50 59.15 0.00 0.00 0.00 0.00 0.00
+1Password Helper (GPU) 1027 2.08 2.09 28.14 0.00 0.00 0.00 0.00 0.00
+plugin-container 1187 3.58 3.59 88.38 0.00 0.00 13.77 0.98 0.00
+LaunchBar 952 4.79 4.81 69.92 0.00 0.00 52.13 2.95 0.00
+logd 322 3.60 3.62 47.91 0.00 0.00 0.00 0.00 0.00
+OnlySwitch 1017 3.85 3.86 72.79 0.00 0.00 42.30 1.97 0.00
+Ferdi 12523 3.97 3.99 80.01 0.98 0.00 1.97 0.00 0.00
+plugin-container 1186 2.74 2.75 83.76 0.00 0.00 10.82 0.00 0.00
+plugin-container 1119 2.77 2.78 81.36 0.00 0.00 11.80 0.00 0.00
+java 22015 2.95 2.96 59.12 0.00 0.00 24.59 0.00 0.00
+java 21951 3.22 3.24 52.44 0.00 0.00 24.59 0.98 0.00
+plugin-container 23298 2.72 2.73 81.68 0.00 0.00 10.82 0.98 0.00
+java 21923 2.87 2.88 56.29 0.00 0.00 25.57 1.97 0.00
+java 22095 2.85 2.86 57.81 0.00 0.00 24.59 0.98 0.00
+Ferdi Helper (GPU) 12531 1.70 1.71 49.64 0.00 0.00 1.97 0.00 0.00
+jcef Helper (GPU) 21932 1.11 1.12 32.20 0.00 0.00 0.00 0.00 0.00
+diskarbitrationd 353 0.38 0.38 59.74 0.00 0.00 0.00 0.00 0.00
+plugin-container 1114 1.48 1.48 72.19 0.00 0.00 0.98 0.00 0.00
+SentinelAgent 936 1.06 1.07 32.01 0.00 0.00 0.00 0.00 0.00
+fseventsd 327 1.82 1.83 19.75 0.00 0.00 2.95 0.00 0.00
+corebrightnessd 387 2.47 2.48 53.16 0.00 0.00 0.98 0.00 0.00
+com.apple.AmbientDisplayAgent 822 1.73 1.73 56.59 0.00 0.00 0.00 0.00 0.00
+searchpartyd 534 3.02 3.04 85.12 0.00 0.00 0.00 0.00 0.00
+Python 931 2.57 2.59 71.71 0.00 0.00 5.90 0.00 0.00
+Ferdi Helper (Renderer) 12533 1.44 1.44 92.33 0.00 0.00 0.98 0.00 0.00
+sentineld_helper 585 1.60 1.60 43.98 0.00 0.00 0.00 0.00 0.00
+locationd 370 2.31 2.32 71.00 0.00 0.00 0.00 0.00 0.00
+bluetoothd 384 2.60 2.61 61.28 0.00 0.00 0.00 0.00 0.00
+pboard 666 1.44 1.44 47.17 0.00 0.00 0.00 0.00 0.00
+lsd 621 0.84 0.84 53.44 0.00 0.00 0.00 0.00 0.00
+plugin-container 9519 3.07 3.09 91.80 0.00 0.00 2.95 0.00 0.00
+Ferdi Helper 12532 1.37 1.38 77.37 0.00 0.00 3.93 0.00 0.00
+coreservicesd 397 0.76 0.76 54.01 0.00 0.00 0.00 0.00 0.00
+plugin-container 9000 1.11 1.12 75.19 0.00 0.00 3.93 0.00 0.00
+plugin-container 1118 0.84 0.84 80.90 0.00 0.00 3.93 0.00 0.00
+Ferdi Helper (Renderer) 12541 1.09 1.09 93.65 0.00 0.00 0.00 0.00 0.00
+Ferdi Helper (Renderer) 29240 1.49 1.50 90.96 0.00 0.00 1.97 0.00 0.00
+plugin-container 9198 1.00 1.01 77.50 0.00 0.00 3.93 0.00 0.00
+Ferdi Helper (Renderer) 12543 0.78 0.79 91.89 0.00 0.00 0.00 0.00 0.00
+plugin-container 23543 1.34 1.35 92.66 0.00 0.00 1.97 0.00 0.00
+distnoted 603 0.51 0.52 37.12 0.00 0.00 0.00 0.00 0.00
+Finder 700 0.49 0.50 51.78 0.00 0.00 0.00 0.00 0.00
+audioclocksyncd 543 0.84 0.84 57.84 0.00 0.00 0.00 0.00 0.00
+PerfPowerServices 17302 0.96 0.97 83.19 0.00 0.00 0.00 0.00 0.00
+plugin-container 9055 0.63 0.64 87.29 0.00 0.00 1.97 0.00 0.00
+storagekitd 7040 0.34 0.34 53.18 0.00 0.00 0.98 0.00 0.00
+heard 922 0.35 0.35 67.45 0.00 0.00 0.98 0.00 0.00
+lockoutagent 641 0.28 0.29 41.08 0.00 0.00 0.98 0.00 0.00
+nearbyd 1024 0.20 0.20 37.90 0.00 0.00 0.98 0.00 0.00
+plugin-container 1091 0.36 0.36 84.72 0.00 0.00 1.97 0.00 0.00
+plugin-container 1190 0.35 0.35 86.92 0.00 0.00 2.95 0.00 0.00
+fsnotifier 21921 0.21 0.21 32.94 0.00 0.00 0.00 0.00 0.00
+plugin-container 9001 0.52 0.52 76.73 0.00 0.00 1.97 0.00 0.00
+plugin-container 9190 0.49 0.49 87.71 0.00 0.00 1.97 0.00 0.00
+plugin-container 28295 0.38 0.38 85.60 0.00 0.00 1.97 0.00 0.00
+gamecontrollerd 613 0.25 0.25 50.77 0.00 0.00 0.98 0.00 0.00
+plugin-container 9191 0.37 0.37 84.36 0.00 0.00 1.97 0.00 0.00
+launchservicesd 364 0.22 0.22 52.01 0.00 0.00 0.98 0.00 0.00
+1Password 1021 0.31 0.31 41.60 0.00 0.00 3.93 0.00 0.00
+thermalmonitord 361 0.31 0.32 43.75 0.00 0.00 0.98 0.00 0.00
+askpermissiond 938 0.09 0.09 20.67 0.00 0.00 0.98 0.00 0.00
+ContinuityCaptureAgent 728 0.15 0.15 28.65 0.00 0.00 1.97 0.00 0.00
+appleh13camerad 545 0.12 0.12 29.55 0.00 0.00 0.98 0.00 0.00
+com.apple.WebKit.WebContent 1874 0.18 0.18 58.02 0.00 0.00 0.98 0.00 0.00
+CAReportingService 562 0.07 0.07 37.92 0.00 0.00 0.98 0.00 0.00
+appstoreagent 1107 0.16 0.16 26.86 0.00 0.00 0.98 0.00 0.00
+contextstored 400 0.18 0.18 20.74 0.00 0.00 0.98 0.00 0.00
+akd 731 0.08 0.08 45.70 0.00 0.00 0.98 0.00 0.00
+mDNSResponderHelper 508 0.08 0.08 35.75 0.00 0.00 0.98 0.00 0.00
+AMPArtworkAgent 799 0.11 0.11 71.12 0.00 0.00 0.98 0.00 0.00
+weatherd 1912 0.09 0.09 47.37 0.00 0.00 0.98 0.00 0.00
+photoanalysisd 16189 0.09 0.09 32.44 0.00 0.00 0.98 0.00 0.00
+bookdatastored 9301 0.07 0.07 42.43 0.00 0.00 0.98 0.00 0.00
+nbagent 6922 0.08 0.08 56.92 0.00 0.00 0.98 0.00 0.00
+osqueryd 24140 0.13 0.13 86.31 0.00 0.00 0.98 0.00 0.00
+cloudphotod 1575 0.06 0.06 43.66 0.00 0.00 0.98 0.00 0.00
+storekitagent 800 0.06 0.06 44.65 0.00 0.00 0.98 0.00 0.00
+mlruntimed 1542 0.06 0.06 44.98 0.00 0.00 0.98 0.00 0.00
+newsd 6844 0.06 0.06 45.11 0.00 0.00 0.98 0.00 0.00
+ALL_TASKS -2 1222.65 1222.65 65.70 1161.97 15.81 2875.29 132.40 0.00
+
+**** Processor usage ****
+
+E-Cluster Online: 100%
+E-Cluster HW active frequency: 1499 MHz
+E-Cluster HW active residency: 67.87% (600 MHz: 0% 972 MHz: 27% 1332 MHz: 24% 1704 MHz: 26% 2064 MHz: 23%)
+E-Cluster idle residency: 32.13%
+E-Cluster instructions retired: 1.20745e+09
+E-Cluster instructions per clock: 0.703911
+CPU 0 frequency: 1527 MHz
+CPU 0 active residency: 54.12% (600 MHz: 0% 972 MHz: 12% 1332 MHz: 15% 1704 MHz: 13% 2064 MHz: 14%)
+CPU 0 idle residency: 45.88%
+CPU 1 frequency: 1530 MHz
+CPU 1 active residency: 55.47% (600 MHz: 0% 972 MHz: 13% 1332 MHz: 15% 1704 MHz: 13% 2064 MHz: 15%)
+CPU 1 idle residency: 44.53%
+
+P0-Cluster Online: 100%
+P0-Cluster HW active frequency: 876 MHz
+P0-Cluster HW active residency: 16.05% (600 MHz: 62% 828 MHz: 5.7% 1056 MHz: 18% 1296 MHz: 4.9% 1524 MHz: 3.3% 1752 MHz: 1.3% 1980 MHz: 1.5% 2208 MHz: 1.4% 2448 MHz: .39% 2676 MHz: .37% 2904 MHz: 0% 3036 MHz: .07% 3132 MHz: .28% 3168 MHz: .19% 3228 MHz: 1.2%)
+P0-Cluster idle residency: 83.95%
+P0-Cluster instructions retired: 4.30488e+08
+P0-Cluster instructions per clock: 1.87471
+CPU 2 frequency: 1237 MHz
+CPU 2 active residency: 12.66% (600 MHz: 1.6% 828 MHz: .53% 1056 MHz: 5.9% 1296 MHz: 1.9% 1524 MHz: 1.3% 1752 MHz: .40% 1980 MHz: .10% 2208 MHz: .17% 2448 MHz: .01% 2676 MHz: .08% 2904 MHz: 0% 3036 MHz: .01% 3132 MHz: .00% 3168 MHz: .00% 3228 MHz: .62%)
+CPU 2 idle residency: 87.34%
+CPU 3 frequency: 1066 MHz
+CPU 3 active residency: 4.02% (600 MHz: .96% 828 MHz: .46% 1056 MHz: 1.3% 1296 MHz: .68% 1524 MHz: .44% 1752 MHz: .07% 1980 MHz: .02% 2208 MHz: .00% 2448 MHz: .00% 2676 MHz: .03% 2904 MHz: 0% 3036 MHz: .00% 3132 MHz: 0% 3168 MHz: 0% 3228 MHz: .05%)
+CPU 3 idle residency: 95.98%
+CPU 4 frequency: 1212 MHz
+CPU 4 active residency: 2.01% (600 MHz: .12% 828 MHz: .34% 1056 MHz: .92% 1296 MHz: .26% 1524 MHz: .17% 1752 MHz: .04% 1980 MHz: .05% 2208 MHz: .03% 2448 MHz: .01% 2676 MHz: .01% 2904 MHz: 0% 3036 MHz: .01% 3132 MHz: .01% 3168 MHz: .00% 3228 MHz: .06%)
+CPU 4 idle residency: 97.99%
+CPU 5 frequency: 1104 MHz
+CPU 5 active residency: 0.82% (600 MHz: .02% 828 MHz: .16% 1056 MHz: .46% 1296 MHz: .10% 1524 MHz: .06% 1752 MHz: .00% 1980 MHz: 0% 2208 MHz: .00% 2448 MHz: 0% 2676 MHz: .00% 2904 MHz: 0% 3036 MHz: 0% 3132 MHz: 0% 3168 MHz: 0% 3228 MHz: .01%)
+CPU 5 idle residency: 99.18%
+
+P1-Cluster Online: 100%
+P1-Cluster HW active frequency: 629 MHz
+P1-Cluster HW active residency: 0.61% (600 MHz: 94% 828 MHz: 2.1% 1056 MHz: 2.7% 1296 MHz: 1.5% 1524 MHz: .13% 1752 MHz: 0% 1980 MHz: 0% 2208 MHz: 0% 2448 MHz: 0% 2676 MHz: 0% 2904 MHz: 0% 3036 MHz: 0% 3132 MHz: 0% 3168 MHz: 0% 3228 MHz: 0%)
+P1-Cluster idle residency: 99.39%
+P1-Cluster instructions retired: 4.84998e+06
+P1-Cluster instructions per clock: 0.692523
+CPU 6 frequency: 962 MHz
+CPU 6 active residency: 0.56% (600 MHz: .07% 828 MHz: .21% 1056 MHz: .17% 1296 MHz: .11% 1524 MHz: .00% 1752 MHz: 0% 1980 MHz: 0% 2208 MHz: 0% 2448 MHz: 0% 2676 MHz: 0% 2904 MHz: 0% 3036 MHz: 0% 3132 MHz: 0% 3168 MHz: 0% 3228 MHz: 0%)
+CPU 6 idle residency: 99.44%
+CPU 7 frequency: 1074 MHz
+CPU 7 active residency: 0.10% (600 MHz: .01% 828 MHz: .01% 1056 MHz: .06% 1296 MHz: .02% 1524 MHz: .00% 1752 MHz: 0% 1980 MHz: 0% 2208 MHz: 0% 2448 MHz: 0% 2676 MHz: 0% 2904 MHz: 0% 3036 MHz: 0% 3132 MHz: 0% 3168 MHz: 0% 3228 MHz: 0%)
+CPU 7 idle residency: 99.90%
+CPU 8 frequency: 1245 MHz
+CPU 8 active residency: 0.02% (600 MHz: 0% 828 MHz: .00% 1056 MHz: .00% 1296 MHz: .02% 1524 MHz: .00% 1752 MHz: 0% 1980 MHz: 0% 2208 MHz: 0% 2448 MHz: 0% 2676 MHz: 0% 2904 MHz: 0% 3036 MHz: 0% 3132 MHz: 0% 3168 MHz: 0% 3228 MHz: 0%)
+CPU 8 idle residency: 99.98%
+CPU 9 frequency: 1289 MHz
+CPU 9 active residency: 0.02% (600 MHz: 0% 828 MHz: 0% 1056 MHz: .00% 1296 MHz: .02% 1524 MHz: .00% 1752 MHz: 0% 1980 MHz: 0% 2208 MHz: 0% 2448 MHz: 0% 2676 MHz: 0% 2904 MHz: 0% 3036 MHz: 0% 3132 MHz: 0% 3168 MHz: 0% 3228 MHz: 0%)
+CPU 9 idle residency: 99.98%
+
+System instructions retired: 1.64279e+09
+System instructions per clock: 0.841601
+CPU Power: 211 mW
+GPU Power: 147 mW
+ANE Power: 0 mW
+Combined Power (CPU + GPU + ANE): 359 mW
diff --git a/cli/pom.xml b/cli/pom.xml
new file mode 100644
index 0000000..78df7d4
--- /dev/null
+++ b/cli/pom.xml
@@ -0,0 +1,51 @@
+
+
+ 4.0.0
+
+ net.laprun.sustainability
+ power-server-parent
+ 0.3.0-SNAPSHOT
+
+
+ power-server-cli
+ power-server : cli
+ A CLI command to measure power consumption of the specified process
+
+
+ net.laprun.sustainability
+ power-server-backend
+ ${project.version}
+
+
+ net.laprun.sustainability
+ power-server-measure
+ ${project.version}
+
+
+ io.quarkus
+ quarkus-picocli
+
+
+ org.awaitility
+ awaitility
+
+
+ io.smallrye.config
+ smallrye-config-crypto
+ true
+
+
+
+
+
+
+ net.revelc.code.formatter
+ formatter-maven-plugin
+
+ ../contributing/eclipse-format.xml
+
+
+
+
+
+
diff --git a/cli/src/main/java/net/laprun/sustainability/cli/Power.java b/cli/src/main/java/net/laprun/sustainability/cli/Power.java
new file mode 100644
index 0000000..368bb8b
--- /dev/null
+++ b/cli/src/main/java/net/laprun/sustainability/cli/Power.java
@@ -0,0 +1,206 @@
+package net.laprun.sustainability.cli;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import io.quarkus.logging.Log;
+import net.laprun.sustainability.power.Measure;
+import net.laprun.sustainability.power.SensorUnit;
+import net.laprun.sustainability.power.analysis.total.TotalSyntheticComponent;
+import net.laprun.sustainability.power.sensors.PowerSensor;
+import net.laprun.sustainability.power.sensors.macos.powermetrics.FileMacOSPowermetricsSensor;
+import picocli.CommandLine;
+
+@CommandLine.Command
+public class Power implements Runnable {
+
+ @CommandLine.Option(names = { "-n",
+ "--name" }, description = "Optional name for the application, defaults to passed command line")
+ String name;
+
+ @CommandLine.Option(names = "-p", description = "Optional process id of process to measure consumption for")
+ String pid;
+
+ @CommandLine.Option(names = "-c", description = "Command to measure energy consumption for")
+ String command;
+
+ private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(3);
+ private final Path outputFile;
+ private final PowerSensor sensor;
+
+ private record ProcessId(long pid) {
+ }
+
+ public Power() throws IOException {
+ this.outputFile = Path.of("output.txt").toAbsolutePath();
+ Files.deleteIfExists(this.outputFile);
+ Log.infof("Output file: %s", this.outputFile);
+ sensor = new FileMacOSPowermetricsSensor(this.outputFile.toFile());
+ }
+
+ private void checkInputs() {
+ if ((pid == null) && (command == null)) {
+ throw new IllegalArgumentException("Must provide either -p or -c");
+ }
+ }
+
+ @Override
+ public void run() {
+ checkInputs();
+
+ var powermetricsProcess = runPowermetrics();
+ var powermetricsPid = powermetricsProcess.pid();
+ Log.infof("Powermetrics pid: %d", powermetricsPid);
+
+ var commandProcessId = runCommandToMeasure();
+ Log.infof("Process pid: %d", commandProcessId.pid());
+
+ if (configWithExistingProcess()) {
+ try {
+ TimeUnit.SECONDS.sleep(10);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ var commandProcessHandle = ProcessHandle.of(commandProcessId.pid())
+ .filter(ProcessHandle::isAlive)
+ .map(p -> p.onExit().join())
+ .orElseThrow(
+ () -> new IllegalArgumentException("Process %s is not alive".formatted(commandProcessId.pid())));
+
+ // By now the underlying process should be done
+ Log.infof("Process [%s] with pid %d exited", commandProcessHandle.info().commandLine().orElse(""),
+ commandProcessId.pid());
+ }
+
+ stopPowermetrics(powermetricsProcess, commandProcessId);
+ }
+
+ private Measure extractPowerConsumption(ProcessId processId, CountDownLatch latch) {
+ var pid = processId.pid();
+ Log.infof("Extracting power consumption for process %d", pid);
+ sensor.stop();
+ // first read metadata
+ final var metadata = sensor.metadata();
+ Log.infof("Metadata:\n%s", metadata);
+ // register pid
+ final var registeredPID = sensor.register(pid);
+ // re-open output file to read process info
+ final var measure = sensor.update(0L);
+ final var power = new TotalSyntheticComponent(metadata, SensorUnit.W, 0, 1, 2)
+ .asMeasure(measure.getOrDefault(registeredPID).components());
+
+ Log.info("Measured power: " + power);
+
+ // latch.countDown();
+
+ return power;
+ }
+
+ private void stopPowermetrics(Process powermetricsProcess, Power.ProcessId commandPid) {
+ var powermetricsPid = powermetricsProcess.pid();
+ // var powermetricsExit = powermetricsProcess.onExit().join();
+ try {
+ Log.info("powermetrics process complete");
+
+ // await()
+ // .atMost(Duration.ofMinutes(1))
+ // .until(() -> Files.exists(this.outputFile)
+ // && Files.readString(this.outputFile).contains("Combined Power (CPU + GPU + ANE):"));
+
+ // var latch = new CountDownLatch(1);
+ // extractPowerConsumption(commandPid, latch);
+
+ // latch.await();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ try {
+ Log.infof("Sending SIGIO to powermetrics process %d", powermetricsPid);
+ new ProcessBuilder("bash", "-c", "kill -SIGIO %s".formatted(String.valueOf(powermetricsPid))).start().waitFor();
+ var exitVal = invokeOnAnotherThread(() -> {
+ Log.infof("Sending SIGTERM to powermetrics process %d", powermetricsPid);
+ return new ProcessBuilder("bash", "-c", "kill %s".formatted(String.valueOf(powermetricsPid)));
+ }).waitFor();
+ Log.infof("Sent SIGTERM to powermetrics. Received exit status %d", exitVal);
+
+ var powermetricsExitStatus = powermetricsProcess.onExit().get().waitFor();
+ // var powermetricsExitStatus = powermetricsExit.get().waitFor();
+ Log.infof("powermetrics process %d exited with status %d", powermetricsPid, powermetricsProcess.exitValue());
+
+ Log.infof("Output file: %s", Files.readString(this.outputFile));
+ extractPowerConsumption(commandPid, new CountDownLatch(0));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Process runPowermetrics() {
+ return invokeOnAnotherThread(() -> {
+ Log.infof("Starting powermetrics - outputting to %s", this.outputFile);
+
+ return new ProcessBuilder()
+ .command(
+ List.of("sudo", "bash", "-c",
+ "powermetrics --samplers cpu_power,tasks --show-process-samp-norm --show-process-gpu --show-usage-summary -i 0"))
+ // .command("sudo", "powermetrics", "--samplers",
+ // "cpu_power,tasks",
+ // "--show-process-samp-norm",
+ // "--show-process-gpu",
+ // "--show-usage-summary",
+ // "-i", "0") // no sampling frequency, just record aggregate
+ .redirectOutput(this.outputFile.toFile());
+ });
+ }
+
+ private static Optional stripped(String s) {
+ return Optional.ofNullable(s)
+ .map(String::strip)
+ .filter(s2 -> !s2.isBlank());
+ }
+
+ private Optional pidProcessId() {
+ return stripped(this.pid)
+ .map(Long::parseLong)
+ .map(ProcessId::new);
+ }
+
+ private Optional commandProcessId() {
+ return stripped(this.command)
+ // .map(s -> s.split("\\s+"))
+ .map(cmd -> invokeOnAnotherThread(() -> {
+ Log.infof("Invoking command: %s", cmd);
+ return new ProcessBuilder().command("bash", "-c", cmd);
+ }))
+ .map(p -> new ProcessId(p.pid()));
+ }
+
+ private ProcessId runCommandToMeasure() {
+ return pidProcessId()
+ .or(this::commandProcessId)
+ .orElseThrow(() -> new IllegalArgumentException("Must provide either -p or -c"));
+ }
+
+ private boolean configWithExistingProcess() {
+ return pidProcessId().isPresent();
+ }
+
+ private static Process invokeOnAnotherThread(Callable processBuilder) {
+ try {
+ // Trying with everything on the same thread
+ return processBuilder.call().start();
+ // return EXECUTOR.submit(() -> processBuilder.call().start()).get();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/cli/src/main/resources/application.properties b/cli/src/main/resources/application.properties
new file mode 100644
index 0000000..d1c7e3f
--- /dev/null
+++ b/cli/src/main/resources/application.properties
@@ -0,0 +1,9 @@
+quarkus.datasource.jdbc.url=jdbc:sqlite:${power-server.db.backup.location}
+quarkus.datasource.db-kind=sqlite
+quarkus.datasource.jdbc.min-size=1
+
+power-server.db.backup.period=5m
+power-server.db.backup.location=${HOME}/.power-server/db.sqlite
+
+# Only use this property once to create the initial database
+# quarkus.hibernate-orm.database.generation=create
diff --git a/measure/src/main/java/net/laprun/sustainability/power/analysis/total/TotalSyntheticComponent.java b/measure/src/main/java/net/laprun/sustainability/power/analysis/total/TotalSyntheticComponent.java
index 701f0f6..0488b13 100644
--- a/measure/src/main/java/net/laprun/sustainability/power/analysis/total/TotalSyntheticComponent.java
+++ b/measure/src/main/java/net/laprun/sustainability/power/analysis/total/TotalSyntheticComponent.java
@@ -1,5 +1,6 @@
package net.laprun.sustainability.power.analysis.total;
+import net.laprun.sustainability.power.Measure;
import net.laprun.sustainability.power.SensorMetadata;
import net.laprun.sustainability.power.SensorUnit;
import net.laprun.sustainability.power.analysis.SyntheticComponent;
@@ -32,4 +33,8 @@ public SensorMetadata.ComponentMetadata metadata() {
public double synthesizeFrom(double[] components, long timestamp) {
return totaler.computeTotalFrom(components);
}
+
+ public Measure asMeasure(double[] components) {
+ return new Measure(synthesizeFrom(components, 0L), metadata.unit());
+ }
}
diff --git a/measure/src/main/java/net/laprun/sustainability/power/analysis/total/Totaler.java b/measure/src/main/java/net/laprun/sustainability/power/analysis/total/Totaler.java
index f6c11e2..1b34b92 100644
--- a/measure/src/main/java/net/laprun/sustainability/power/analysis/total/Totaler.java
+++ b/measure/src/main/java/net/laprun/sustainability/power/analysis/total/Totaler.java
@@ -13,6 +13,7 @@ class Totaler {
private final SensorUnit expectedResultUnit;
private final Function formula;
private final String name;
+ private final int[] totalComponentIndices;
private Errors errors;
Totaler(SensorMetadata metadata, SensorUnit expectedResultUnit, int... totalComponentIndices) {
@@ -27,6 +28,7 @@ class Totaler {
.map(TotalComponent::name)
.collect(Collectors.joining(" + ", "total (", ")"));
formula = formulaFrom(totalComponents);
+ this.totalComponentIndices = totalComponentIndices;
}
void validate() {
@@ -52,6 +54,11 @@ public SensorUnit expectedResultUnit() {
}
public double computeTotalFrom(double[] measure) {
+ if (measure.length < totalComponentIndices.length) {
+ throw new IllegalArgumentException("Provided measure " + Arrays.toString(measure) +
+ " doesn't countain components for required total indices: " + Arrays.toString(totalComponentIndices));
+ }
+
checkValidated();
return convertToExpectedUnit(formula.apply(measure));
}
diff --git a/metadata/src/main/java/net/laprun/sustainability/power/Measure.java b/metadata/src/main/java/net/laprun/sustainability/power/Measure.java
new file mode 100644
index 0000000..6edfbde
--- /dev/null
+++ b/metadata/src/main/java/net/laprun/sustainability/power/Measure.java
@@ -0,0 +1,8 @@
+package net.laprun.sustainability.power;
+
+public record Measure(double value, SensorUnit unit) {
+ @Override
+ public String toString() {
+ return value + unit.toString();
+ }
+}
diff --git a/pom.xml b/pom.xml
index 5867fc7..c53cb3b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,8 +71,10 @@
metadata
measure
- server
+ backend
analysis
+ server
+ cli
if-manifest-export
diff --git a/server/pom.xml b/server/pom.xml
index 92af41f..da6e094 100644
--- a/server/pom.xml
+++ b/server/pom.xml
@@ -12,14 +12,11 @@
${project.build.directory}/distributions
- 21
- 21
- UTF-8
net.laprun.sustainability
- power-server-metadata
+ power-server-backend
${project.version}
@@ -27,17 +24,9 @@
quarkus-rest-jackson
- io.quarkus
- quarkus-hibernate-orm-panache
-
-
- io.quarkus
- quarkus-scheduler
-
-
- io.quarkiverse.jdbc
- quarkus-jdbc-sqlite4j
- ${sqlite4j.version}
+ org.assertj
+ assertj-core
+ test
io.quarkus
@@ -49,11 +38,6 @@
rest-assured
test
-
- org.assertj
- assertj-core
- test
-
io.quarkus
quarkus-rest-client
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 b733ab5..649325b 100644
--- a/server/src/main/java/net/laprun/sustainability/power/PowerMeasurer.java
+++ b/server/src/main/java/net/laprun/sustainability/power/PowerMeasurer.java
@@ -9,6 +9,7 @@
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.infrastructure.Infrastructure;
+import io.smallrye.mutiny.subscription.Cancellable;
import net.laprun.sustainability.power.persistence.Persistence;
import net.laprun.sustainability.power.sensors.Measures;
import net.laprun.sustainability.power.sensors.PowerSensor;
@@ -30,18 +31,24 @@ public class PowerMeasurer {
private Multi periodicSensorCheck;
public Multi stream(String pid) throws Exception {
+ final var parsedPID = validPIDOrFail(pid);
+ return uncheckedStream(parsedPID);
+ }
+
+ public Multi uncheckedStream(long pid) throws Exception {
final var registeredPID = track(pid);
return periodicSensorCheck.map(measures -> measures.getOrDefault(registeredPID));
}
- public void startTrackingApp(String appName, String pid) throws Exception {
- stream(pid).subscribe().with(m -> persistence.save(m, appName));
+ public Cancellable startTrackingApp(String appName, long pid) throws Exception {
+ return uncheckedStream(pid).subscribe().with(m -> persistence.save(m, appName));
}
- private RegisteredPID track(String pid) throws Exception {
- // first make sure that the process with that pid exists
- final var parsedPID = validPIDOrFail(pid);
+ public Cancellable startTrackingProcess(Process process) throws Exception {
+ return startTrackingApp(process.info().commandLine().orElseThrow(), process.pid());
+ }
+ private RegisteredPID track(long pid) throws Exception {
if (!sensor.isStarted()) {
sensor.start(samplingPeriod.toMillis());
periodicSensorCheck = Multi.createFrom().ticks()
@@ -52,14 +59,14 @@ private RegisteredPID track(String pid) throws Exception {
.toAtLeast(1)
.runSubscriptionOn(Infrastructure.getDefaultWorkerPool());
}
- final var registeredPID = sensor.register(parsedPID);
+ final var registeredPID = sensor.register(pid);
// 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
periodicSensorCheck = periodicSensorCheck.onCancellation().invoke(() -> sensor.unregister(registeredPID));
return registeredPID;
}
- protected long validPIDOrFail(String pid) {
+ public long validPIDOrFail(String pid) {
final var parsedPID = Long.parseLong(pid);
ProcessHandle.of(parsedPID).orElseThrow(() -> new IllegalArgumentException("Unknown process: " + pid));
return parsedPID;
@@ -72,4 +79,8 @@ public SensorMetadata metadata() {
public Duration getSamplingPeriod() {
return samplingPeriod;
}
+
+ public Persistence persistence() {
+ return persistence;
+ }
}
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 b768327..37dd641 100644
--- a/server/src/main/java/net/laprun/sustainability/power/PowerResource.java
+++ b/server/src/main/java/net/laprun/sustainability/power/PowerResource.java
@@ -44,7 +44,7 @@ public Multi streamMeasuresFor(@PathParam("pid") String pid) thro
@Path("start/{appName}/{pid}")
public void startMeasure(@PathParam("appName") String appName, @PathParam("pid") String pid) throws Exception {
try {
- measurer.startTrackingApp(appName, pid);
+ measurer.startTrackingApp(appName, measurer.validPIDOrFail(pid));
} catch (IllegalArgumentException e) {
throw new NotFoundException("Unknown process: " + pid);
}
diff --git a/server/src/test/java/net/laprun/sustainability/power/MockPersistence.java b/server/src/test/java/net/laprun/sustainability/power/MockPersistence.java
new file mode 100644
index 0000000..ce71ef1
--- /dev/null
+++ b/server/src/test/java/net/laprun/sustainability/power/MockPersistence.java
@@ -0,0 +1,8 @@
+package net.laprun.sustainability.power;
+
+import io.quarkus.test.Mock;
+import net.laprun.sustainability.power.persistence.Persistence;
+
+@Mock
+public class MockPersistence extends Persistence {
+}
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..b080060 100644
--- a/server/src/test/java/net/laprun/sustainability/power/MockPowerMeasurer.java
+++ b/server/src/test/java/net/laprun/sustainability/power/MockPowerMeasurer.java
@@ -7,7 +7,7 @@
public class MockPowerMeasurer extends PowerMeasurer {
@Override
- protected long validPIDOrFail(String pid) {
+ public long validPIDOrFail(String pid) {
return Long.parseLong(pid);
}
}