Skip to content
This repository was archived by the owner on Mar 11, 2019. It is now read-only.

Commit 173c0fd

Browse files
huertasmcolmant
authored andcommitted
feature(daemon): implement the PowerAPI daemon
1 parent 45d82c8 commit 173c0fd

File tree

14 files changed

+585
-1
lines changed

14 files changed

+585
-1
lines changed

powerapi-daemon/build.sbt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
enablePlugins(JavaServerAppPackaging)
2+
3+
name := "powerapi-daemon"
4+
5+
libraryDependencies ++= Seq(
6+
"commons-daemon" % "commons-daemon" % "1.0.15"
7+
)
8+
9+
lazy val downloadBluecoveDaemon = taskKey[File]("download-bluecove-daemon")
10+
lazy val downloadBluecoveGplDaemon = taskKey[File]("download-bluecove-gpl-daemon")
11+
12+
downloadBluecoveDaemon := {
13+
val locationBluecove = baseDirectory.value / "lib" / "bluecove-2.1.0.jar"
14+
if(!locationBluecove.getParentFile.exists()) locationBluecove.getParentFile.mkdirs()
15+
if(!locationBluecove.exists()) IO.download(url("https://bluecove.googlecode.com/files/bluecove-2.1.0.jar"), locationBluecove)
16+
locationBluecove
17+
}
18+
19+
downloadBluecoveGplDaemon := {
20+
val locationBluecoveGpl = baseDirectory.value / "lib" / "bluecove-gpl-2.1.0.jar"
21+
if(!locationBluecoveGpl.getParentFile.exists()) locationBluecoveGpl.getParentFile.mkdirs()
22+
if(!locationBluecoveGpl.exists()) IO.download(url("https://bluecove.googlecode.com/files/bluecove-gpl-2.1.0.jar"), locationBluecoveGpl)
23+
locationBluecoveGpl
24+
}
25+
26+
mappings in Universal += downloadBluecoveDaemon.value -> s"lib/${downloadBluecoveDaemon.value.name}"
27+
28+
mappings in Universal += downloadBluecoveGplDaemon.value -> s"lib/${downloadBluecoveGplDaemon.value.name}"
29+
30+
mappings in Universal ++= {
31+
((file("../") * "README*").get map {
32+
readmeFile: File =>
33+
readmeFile -> readmeFile.getName
34+
}) ++
35+
((file("../") * "LICENSE*").get map {
36+
licenseFile: File =>
37+
licenseFile -> licenseFile.getName
38+
})
39+
}
40+
41+
scriptClasspath ++= Seq("../conf", "../scripts")
42+
43+
NativePackagerKeys.executableScriptName := "powerapid"
44+
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* This software is licensed under the GNU Affero General Public License, quoted below.
3+
*
4+
* This file is a part of PowerAPI.
5+
*
6+
* Copyright (C) 2011-2015 Inria, University of Lille 1.
7+
*
8+
* PowerAPI is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as
10+
* published by the Free Software Foundation, either version 3 of
11+
* the License, or (at your option) any later version.
12+
*
13+
* PowerAPI is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License
19+
* along with PowerAPI.
20+
*
21+
* If not, please consult http://www.gnu.org/licenses/agpl-3.0.html.
22+
*/
23+
package org.powerapi.daemon
24+
25+
import java.util.concurrent.TimeUnit
26+
27+
import scala.collection.JavaConversions._
28+
import scala.concurrent.duration.{FiniteDuration, DurationLong}
29+
30+
import com.typesafe.config.Config
31+
32+
import org.powerapi.core.{Configuration, ConfigValue}
33+
34+
/**
35+
* Main configuration.
36+
*
37+
* @author <a href="mailto:l.huertas.pro@gmail.com">Loïc Huertas</a>
38+
*/
39+
trait DaemonConfiguration extends Configuration {
40+
/**
41+
* List of power-meter which the PowerAPI daemon has to load at his startup.
42+
* A power-meter is described by:
43+
* - a list of power modules
44+
* - a list of monitors described by:
45+
* _ a list of target
46+
* _ a frequency
47+
* _ an aggregator
48+
* _ an output
49+
*/
50+
lazy val powerMeters: List[(Set[String], List[(Set[String], FiniteDuration, String, String)])] = load { conf =>
51+
(for (powerMeter: Config <- conf.getConfigList("powerapi.daemon.load"))
52+
yield (
53+
powerMeter.getStringList("power-modules").toSet,
54+
(for (monitor: Config <- powerMeter.getConfigList("monitors"))
55+
yield (
56+
monitor.getStringList("targets").toSet,
57+
monitor.getDuration("frequency", TimeUnit.MILLISECONDS).milliseconds,
58+
monitor.getString("agg"),
59+
monitor.getString("output")
60+
)).toList
61+
)).toList
62+
} match {
63+
case ConfigValue(values) => values
64+
case _ => List()
65+
}
66+
}
67+
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*
2+
* This software is licensed under the GNU Affero General Public License, quoted below.
3+
*
4+
* This file is a part of PowerAPI.
5+
*
6+
* Copyright (C) 2011-2015 Inria, University of Lille 1.
7+
*
8+
* PowerAPI is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU Affero General Public License as
10+
* published by the Free Software Foundation, either version 3 of
11+
* the License, or (at your option) any later version.
12+
*
13+
* PowerAPI is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License
19+
* along with PowerAPI.
20+
*
21+
* If not, please consult http://www.gnu.org/licenses/agpl-3.0.html.
22+
*/
23+
package org.powerapi.daemon
24+
25+
import java.lang.management.ManagementFactory
26+
27+
import scala.concurrent.duration.{FiniteDuration, Duration, DurationInt}
28+
import scala.sys
29+
import scala.sys.process.stringSeqToProcess
30+
31+
import akka.actor.Props
32+
33+
import org.apache.commons.daemon.{ Daemon, DaemonContext, DaemonInitException }
34+
35+
import org.powerapi.core.target.{Application, All, Process, Target}
36+
import org.powerapi.module.rapl.RAPLModule
37+
import org.powerapi.reporter.{FileDisplay, JFreeChartDisplay, ConsoleDisplay}
38+
import org.powerapi.{PowerMonitoring, PowerMeter}
39+
import org.powerapi.core.power._
40+
import org.powerapi.module.cpu.dvfs.CpuDvfsModule
41+
import org.powerapi.module.cpu.simple.{SigarCpuSimpleModule, ProcFSCpuSimpleModule}
42+
import org.powerapi.module.libpfm.{LibpfmHelper, LibpfmCoreProcessModule, LibpfmCoreModule}
43+
import org.powerapi.module.powerspy.PowerSpyModule
44+
45+
/**
46+
* PowerAPI daemon.
47+
*
48+
* @author <a href="mailto:l.huertas.pro@gmail.com">Loïc Huertas</a>
49+
* @author <a href="mailto:maxime.colmant@gmail.com">Maxime Colmant</a>
50+
*/
51+
object PowerAPId extends App
52+
class PowerAPId extends Daemon {
53+
54+
// =====================
55+
// --- PowerAPI part ---
56+
57+
val configuration = new DaemonConfiguration {}
58+
59+
val pidR = """(\d+)""".r
60+
val appR = """(.+)""".r
61+
62+
@volatile var launchedPowerMeters = Seq[PowerMeter]()
63+
@volatile var launchedMonitors = Seq[PowerMonitoring]()
64+
65+
var libpfmHelper: Option[LibpfmHelper] = None
66+
67+
68+
implicit def aggStrToAggFunction(str: String): Seq[Power] => Power = {
69+
str match {
70+
case "max" => MAX
71+
case "min" => MIN
72+
case "geomean" => GEOMEAN
73+
case "logsum" => LOGSUM
74+
case "mean" => MEAN
75+
case "median" => MEDIAN
76+
case "stdev" => STDEV
77+
case "variance" => VARIANCE
78+
case _ => SUM
79+
}
80+
}
81+
82+
implicit def targetsStrToTargets(targets: Set[String]): Seq[Target] = {
83+
if(targets.contains("all")) {
84+
Seq(All)
85+
}
86+
87+
(for(target <- targets) yield {
88+
target match {
89+
case "all" => All
90+
case pidR(pid) => Process(pid.toInt)
91+
case appR(app) => Application(app)
92+
case _ => Process(ManagementFactory.getRuntimeMXBean.getName.split("@")(0).toInt)
93+
}
94+
}).toSeq
95+
}
96+
97+
def beforeStart() {
98+
if(System.getProperty("os.name").toLowerCase.indexOf("nix") >= 0 || System.getProperty("os.name").toLowerCase.indexOf("nux") >= 0) Seq("bash", "scripts/system.bash").!
99+
100+
for ((powerModules, monitors) <- configuration.powerMeters) {
101+
val modules = (for(module <- powerModules) yield {
102+
module match {
103+
case "procfs-cpu-simple" => ProcFSCpuSimpleModule()
104+
case "sigar-cpu-simple" => SigarCpuSimpleModule()
105+
case "cpu-dvfs" => CpuDvfsModule()
106+
case "libpfm-core" => {
107+
libpfmHelper = Some(new LibpfmHelper)
108+
libpfmHelper.get.init()
109+
LibpfmCoreModule(None, libpfmHelper.get)
110+
}
111+
case "libpfm-core-process" => {
112+
libpfmHelper = Some(new LibpfmHelper)
113+
libpfmHelper.get.init()
114+
LibpfmCoreProcessModule(None, libpfmHelper.get)
115+
}
116+
case "powerspy" => PowerSpyModule()
117+
case "rapl" => RAPLModule()
118+
}
119+
}).toSeq
120+
121+
val powerMeter = PowerMeter.loadModule(modules: _*)
122+
launchedPowerMeters :+= powerMeter
123+
124+
for ((targets, frequency, agg, output) <- monitors) {
125+
val monitor = powerMeter.monitor(frequency)(targets: _*)(agg)
126+
launchedMonitors :+= monitor
127+
128+
output match {
129+
case file:String if output.startsWith("file") => {
130+
val fileDisplay = new FileDisplay(file.split(":")(1))
131+
monitor.to(fileDisplay)
132+
}
133+
case "chart" => {
134+
val chartDisplay = new JFreeChartDisplay()
135+
monitor.to(chartDisplay)
136+
}
137+
case _ => {
138+
val consoleDisplay = new ConsoleDisplay()
139+
monitor.to(consoleDisplay)
140+
}
141+
}
142+
}
143+
}
144+
}
145+
def beforeEnd() {
146+
launchedMonitors.foreach(monitor => monitor.cancel())
147+
launchedMonitors = Seq()
148+
launchedPowerMeters.foreach(powerMeter => powerMeter.shutdown())
149+
launchedPowerMeters = Seq()
150+
}
151+
152+
153+
// ===================
154+
// --- Daemon part ---
155+
156+
var stopped = false
157+
val monitor = new Thread(){
158+
override def start() {
159+
this.synchronized {
160+
PowerAPId.this.stopped = false
161+
super.start
162+
}
163+
}
164+
override def run() {
165+
while (!stopped) Thread.sleep(2000)
166+
}
167+
}
168+
169+
override def init(daemonContext: DaemonContext) {
170+
val args = daemonContext.getArguments
171+
beforeStart
172+
}
173+
174+
override def start() {
175+
monitor.start
176+
}
177+
178+
override def stop() {
179+
stopped = true
180+
try {
181+
monitor.join(1000)
182+
} catch {
183+
case e: InterruptedException => {
184+
System.err.println(e.getMessage)
185+
throw e
186+
}
187+
}
188+
}
189+
190+
override def destroy() {
191+
beforeEnd
192+
}
193+
}
194+

0 commit comments

Comments
 (0)