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

Commit 0fac1fa

Browse files
committed
Merge pull request #49 from Spirals-Team/develop
feat: version 3.0 of PowerAPI
2 parents 4369693 + e594b47 commit 0fac1fa

File tree

241 files changed

+43541
-13
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

241 files changed

+43541
-13
lines changed

.gitignore

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,40 @@
11
*.class
22
*.log
3+
*~
4+
5+
# activator specific
6+
activator*
37

48
# sbt specific
5-
.cache/
69
.history/
710
.lib/
811
dist/*
9-
target/
1012
lib_managed/
1113
src_managed/
12-
project/boot/
13-
project/plugins/project/
14+
*#
15+
*.iml
16+
*.ipr
17+
*.iws
18+
*.pyc
19+
*.tm.epoch
20+
*.vim
21+
*-shim.sbt
22+
.idea/
23+
/project/plugins/project
24+
project/boot
25+
target/
26+
/logs
27+
.cache
28+
.classpath
29+
.project
30+
.settings
1431

1532
# Scala-IDE specific
1633
.scala_dependencies
1734
.worksheet
35+
36+
#bluecove
37+
*/*/bluecove*
38+
39+
#tokens
40+
secrets.tar

.travis.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
language: scala
2+
scala:
3+
- 2.11.4
4+
script:
5+
- sbt clean coverage test
6+
before_install:
7+
- openssl aes-256-cbc -K $encrypted_48ebb0d1c0b9_key -iv $encrypted_48ebb0d1c0b9_iv
8+
-in secrets.tar.enc -out secrets.tar -d
9+
- tar xvf secrets.tar
10+
after_success:
11+
- sbt coverageReport
12+
- sbt coverageAggregate
13+
- sbt codacyCoverage

LICENSE

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -659,4 +659,3 @@ specific requirements.
659659
if any, to sign a "copyright disclaimer" for the program, if necessary.
660660
For more information on this, and how to apply and follow the GNU AGPL, see
661661
<http://www.gnu.org/licenses/>.
662-

README.md

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,48 @@
11
# PowerAPI
2-
PowerAPI is a middleware toolkit for building software-defined power meters. Software-defined power meters are configurable software libraries that can estimate the power consumption of software in real-time. PowerAPI supports the acquisition of raw metrics from a wide diversity of sensors (*eg.*, physical meters, processor interfaces, hardware counters, OS counters) and the delivery of power consumptions via different channels (including file system, network, web, graphical). As a middleware toolkit, PowerAPI offers the capability of assembling power meters *«à la carte»* to accommodate user requirements.
2+
[![Build Status](https://travis-ci.org/Spirals-Team/powerapi.svg?branch=develop)](https://travis-ci.org/Spirals-Team/powerapi)
3+
[![Codacy Badge](https://www.codacy.com/project/badge/e0b0e331ca414250a7240b6be74aaa7b)](https://www.codacy.com/public/maximecolmant/powerapi)
4+
5+
PowerAPI is a middleware toolkit for building software-defined power meters.
6+
Software-defined power meters are configurable software libraries that can estimate the power consumption of software in real-time.
7+
PowerAPI supports the acquisition of raw metrics from a wide diversity of sensors (*eg.*, physical meters, processor interfaces, hardware counters, OS counters) and the delivery of power consumptions via different channels (including file system, network, web, graphical).
8+
As a middleware toolkit, PowerAPI offers the capability of assembling power meters *«à la carte»* to accommodate user requirements.
39

410
# About
5-
PowerAPI is an open-source project developed by the [Spirals research group](https://team.inria.fr/spirals) (University of Lille 1 and Inria).
11+
PowerAPI is an open-source project developed by the [Spirals research group](https://team.inria.fr/spirals) (University of Lille 1 and Inria) and fully managed with [sbt](http://www.scala-sbt.org/).
612

7-
# Mailing list
13+
## Mailing list
814
You can follow the latest news and asks questions by subscribing to our [mailing list](https://sympa.inria.fr/sympa/info/powerapi).
915

10-
# Contributing
16+
## Contributing
1117
If you would like to contribute code you can do so through GitHub by forking the repository and sending a pull request.
1218

1319
When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible.
1420

15-
# Acknowledgments
16-
We all stand on the shoulders of giants and get by with a little help from our friends. PowerAPI is written in [Scala](http://www.scala-lang.org) (version 2.11.4 under [3-clause BSD license](http://www.scala-lang.org/license.html)) and built on top of:
17-
* [Akka](http://akka.io) (version 2.2.4 under [Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0)), for asynchronous processing
21+
## Publications
22+
* **Process-level Power Estimation in VM-based Systems**: M. Colmant, M. Kurpicz, L. Huertas, R. Rouvoy, P. Felber, A. Sobe. *European Conference on Computer Systems* (EuroSys). April 2015, Bordeaux, France. pp.1-14. To appear.
23+
* **[Monitoring Energy Hotspots in Software](https://hal.inria.fr/hal-01069142)**: A. Noureddine, R. Rouvoy, L. Seinturier. *Journal of Automated Software Engineering*, Springer, 2015, pp.1-42.
24+
* **[Unit Testing of Energy Consumption of Software Libraries](https://hal.inria.fr/hal-00912613)**: A. Noureddine, R. Rouvoy, L. Seinturier. *International Symposium On Applied Computing* (SAC), Mar 2014, Gyeongju, South Korea. pp.1200-1205.
25+
* **[Informatique : Des logiciels mis au vert](http://www.jinnove.com/Actualites/Informatique-des-logiciels-mis-au-vert)**: L. Seinturier, R. Rouvoy. *J'innove en Nord Pas de Calais*, [NFID](http://www.jinnove.com), 2013.
26+
* **[PowerAPI: A Software Library to Monitor the Energy Consumed at the Process-Level](http://ercim-news.ercim.eu/en92/special/powerapi-a-software-library-to-monitor-the-energy-consumed-at-the-process-level)**: A. Bourdon, A. Noureddine, R. Rouvoy, L. Seinturier. *ERCIM News, Special Theme: Smart Energy Systems*, 92, pp.43-44. [ERCIM](http://www.ercim.eu), 2013.
27+
* **[Mesurer la consommation en énergie des logiciels avec précision](http://www.lifl.fr/digitalAssets/0/807_01info_130110_16_39.pdf)**: A. Bourdon, R. Rouvoy, L. Seinturier. *01 Business & Technologies*, 2013.
28+
* **[A review of energy measurement approaches](https://hal.inria.fr/hal-00912996v2)**: A. Noureddine, R. Rouvoy, L. Seinturier. *ACM SIGOPS Operating Systems Review*, ACM, 2013, 47 (3), pp.42-49.
29+
* **[Runtime Monitoring of Software Energy Hotspots](https://hal.inria.fr/hal-00715331)**: A. Noureddine, A. Bourdon, R. Rouvoy, L. Seinturier. *International Conference on Automated Software Engineering* (ASE), Sep 2012, Essen, Germany. pp.160-169.
30+
* **[A Preliminary Study of the Impact of Software Engineering on GreenIT](https://hal.inria.fr/hal-00681560)**: A. Noureddine, A. Bourdon, R. Rouvoy, L. Seinturier. *International Workshop on Green and Sustainable Software* (GREENS), Jun 2012, Zurich, Switzerland. pp.21-27.
1831

19-
# Licence
32+
33+
## Acknowledgments
34+
We all stand on the shoulders of giants and get by with a little help from our friends. PowerAPI is written in [Scala](http://www.scala-lang.org) (version 2.11.4 under [3-clause BSD license](http://www.scala-lang.org/license.html)) and built on top of:
35+
* [Akka](http://akka.io) (version 2.3.6 under [Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0)), for asynchronous processing
36+
* [Typesage Config](https://github.com/typesafehub/config) (version 1.2.1 under [Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0)), for reading configuration files.
37+
* [Apache log4j2](http://logging.apache.org/log4j/2.x/) (version 2.1 under [Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0)), for logging outside actors.
38+
* [powerspy.scala](https://github.com/Spirals-Team/powerspy.scala) (version 1.0.1 under [AGPL license](http://www.gnu.org/licenses/agpl-3.0.html)), for using the [PowerSpy powermeter](http://www.alciom.com/en/products/powerspy2-en-gb-2.html).
39+
* [BridJ](https://code.google.com/p/bridj/) (version 0.6.2 under [3-clause BSD license](https://github.com/ochafik/nativelibs4java/blob/master/libraries/BridJ/LICENSE)), for system or C calls.
40+
* [perfmon2](http://sourceforge.net/p/perfmon2/libpfm4/ci/master/tree/) (version 4.6.0 under [MIT license](http://sourceforge.net/p/perfmon2/libpfm4/ci/master/tree/COPYING)), for accessing hardware performance counters.
41+
* [JFreeChart](http://www.jfree.org/jfreechart/) (version 1.0.19 under [LGPL license](https://www.gnu.org/licenses/lgpl.html)), for creation of interactive and animated charts.
42+
* [Scala IO](http://jesseeichar.github.io/scala-io-doc/0.4.3/index.html#!/overview) (version 0.4.3 under [3-clause BSD license](http://www.scala-lang.org/license.html)), for an extensions of IO.
43+
* [Saddle](http://saddle.github.io/) (version 1.3.3 under [Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0)), for data manipulation.
44+
45+
# License
2046
This software is licensed under the *GNU Affero General Public License*, quoted below.
2147

2248
Copyright (C) 2011-2014 Inria, University of Lille 1.

build.sbt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
name := "powerapi"
2+
3+
version in ThisBuild := "3.0"
4+
5+
scalaVersion in ThisBuild := "2.11.4"
6+
7+
scalacOptions in ThisBuild ++= Seq(
8+
"-language:reflectiveCalls",
9+
"-language:implicitConversions",
10+
"-feature",
11+
"-deprecation"
12+
)
13+
14+
// Logging
15+
libraryDependencies in ThisBuild ++= Seq(
16+
"org.apache.logging.log4j" % "log4j-api" % "2.1",
17+
"org.apache.logging.log4j" % "log4j-core" % "2.1"
18+
)
19+
20+
parallelExecution in (ThisBuild, Test) := false
21+
22+
codacyProjectTokenFile := Some("./codacy-token.txt")

powerapi-cli/build.sbt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
name := "powerapi-cli"
2+
3+
lazy val downloadBluecoveApp = taskKey[File]("download-bluecove-app")
4+
lazy val downloadBluecoveGplApp = taskKey[File]("download-bluecove-gpl-app")
5+
6+
downloadBluecoveApp := {
7+
val locationBluecove = baseDirectory.value / "lib" / "bluecove-2.1.0.jar"
8+
if(!locationBluecove.getParentFile.exists()) locationBluecove.getParentFile.mkdirs()
9+
if(!locationBluecove.exists()) IO.download(url("https://bluecove.googlecode.com/files/bluecove-2.1.0.jar"), locationBluecove)
10+
locationBluecove
11+
}
12+
13+
downloadBluecoveGplApp := {
14+
val locationBluecoveGpl = baseDirectory.value / "lib" / "bluecove-gpl-2.1.0.jar"
15+
if(!locationBluecoveGpl.getParentFile.exists()) locationBluecoveGpl.getParentFile.mkdirs()
16+
if(!locationBluecoveGpl.exists()) IO.download(url("https://bluecove.googlecode.com/files/bluecove-gpl-2.1.0.jar"), locationBluecoveGpl)
17+
locationBluecoveGpl
18+
}
19+
20+
mappings in Universal += downloadBluecoveApp.value -> s"lib/${downloadBluecoveApp.value.name}"
21+
22+
mappings in Universal += downloadBluecoveGplApp.value -> s"lib/${downloadBluecoveGplApp.value.name}"
23+
24+
mappings in Universal ++= {
25+
((file("../") * "README*").get map {
26+
readmeFile: File =>
27+
readmeFile -> readmeFile.getName
28+
}) ++
29+
((file("../") * "LICENSE*").get map {
30+
licenseFile: File =>
31+
licenseFile -> licenseFile.getName
32+
})
33+
}
34+
35+
scriptClasspath ++= Seq("../conf", "../scripts")
36+
37+
NativePackagerKeys.executableScriptName := "powerapi"
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
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.app
24+
25+
import java.lang.management.ManagementFactory
26+
27+
import org.powerapi.core.target.{Application, All, Process, Target}
28+
import org.powerapi.reporter.{FileDisplay, JFreeChartDisplay, ConsoleDisplay}
29+
import org.powerapi.{PowerMonitoring, PowerMeter, PowerModule}
30+
import org.powerapi.core.power._
31+
import org.powerapi.module.cpu.dvfs.CpuDvfsModule
32+
import org.powerapi.module.cpu.simple.CpuSimpleModule
33+
import org.powerapi.module.libpfm.{LibpfmHelper, LibpfmCoreProcessModule, LibpfmCoreModule}
34+
import org.powerapi.module.powerspy.PowerSpyModule
35+
import scala.concurrent.duration.DurationInt
36+
import scala.sys
37+
import scala.sys.process.stringSeqToProcess
38+
39+
/**
40+
* PowerAPI CLI.
41+
*
42+
* @author <a href="mailto:maxime.colmant@gmail.com">Maxime Colmant</a>
43+
* @author <a href="mailto:l.huertas.pro@gmail.com">Loïc Huertas</a>
44+
*/
45+
object PowerAPI extends App {
46+
val modulesR = """(cpu-simple|cpu-dvfs|libpfm-core|libpfm-core-process|powerspy)(,(cpu-simple|cpu-dvfs|libpfm-core|libpfm-core-process|powerspy))*""".r
47+
val aggR = """max|min|geomean|logsum|mean|median|stdev|sum|variance""".r
48+
val durationR = """\d+""".r
49+
val pidR = """(\d+)""".r
50+
val appR = """(.+)""".r
51+
52+
@volatile var powerMeters = Seq[PowerMeter]()
53+
@volatile var monitors = Seq[PowerMonitoring]()
54+
55+
val shutdownHookThread = scala.sys.ShutdownHookThread {
56+
monitors.foreach(monitor => monitor.cancel())
57+
monitors = Seq()
58+
powerMeters.foreach(powerMeter => powerMeter.shutdown())
59+
powerMeters = Seq()
60+
}
61+
62+
def validateModules(str: String) = str match {
63+
case modulesR(_*) => true
64+
case _ => false
65+
}
66+
67+
implicit def modulesStrToPowerModules(str: String): Seq[PowerModule] = {
68+
(for(module <- str.split(",")) yield {
69+
module match {
70+
case "cpu-simple" => CpuSimpleModule()
71+
case "cpu-dvfs" => CpuDvfsModule()
72+
case "libpfm-core" => LibpfmCoreModule()
73+
case "libpfm-core-process" => LibpfmCoreProcessModule()
74+
case "powerspy" => PowerSpyModule()
75+
}
76+
}).toSeq
77+
}
78+
79+
def validateAgg(str: String): Boolean = str match {
80+
case aggR(_*) => true
81+
case _ => false
82+
}
83+
84+
implicit def aggStrToAggFunction(str: String): Seq[Power] => Power = {
85+
str match {
86+
case "max" => MAX
87+
case "min" => MIN
88+
case "geomean" => GEOMEAN
89+
case "logsum" => LOGSUM
90+
case "mean" => MEAN
91+
case "median" => MEDIAN
92+
case "stdev" => STDEV
93+
case "sum" => SUM
94+
case "variance" => VARIANCE
95+
}
96+
}
97+
98+
def validateDuration(str: String): Boolean = str match {
99+
case durationR(_*) => true
100+
case _ => false
101+
}
102+
103+
implicit def targetsStrToTargets(str: String): Seq[Target] = {
104+
val strTargets = if(str.split(",").contains("all")) {
105+
"all"
106+
}
107+
else str
108+
109+
(for(target <- strTargets.split(",")) yield {
110+
target match {
111+
case "" => Process(ManagementFactory.getRuntimeMXBean.getName.split("@")(0).toInt)
112+
case "all" => All
113+
case pidR(pid) => Process(pid.toInt)
114+
case appR(app) => Application(app)
115+
}
116+
}).toSeq
117+
}
118+
119+
def printHelp(): Unit = {
120+
val str =
121+
"""
122+
|PowerAPI, Spirals Team"
123+
|
124+
|Build a software-defined power meter. Do not forget to configure correctly the modules (see the documentation).
125+
|
126+
|usage: ./powerapi modules [cpu-simple|cpu-dvfs|libpfm-core|libpfm-core-proces|powerspy, ...] \
127+
| monitor --frequency [ms] --targets [pid, ..., app, ...)|all] --agg [max|min|geomean|logsum|mean|median|stdev|sum|variance] --[console,file [filepath],chart] \
128+
| duration [s]
129+
|
130+
|example: ./powerapi modules cpu-simple monitor --frequency 1000 --targets firefox --agg max --console monitor --targets chrome --agg max --console \
131+
| modules powerspy monitor --frequency 1000 --targets all --agg max --console \
132+
| duration 30
133+
""".stripMargin
134+
135+
println(str)
136+
}
137+
138+
def cli(options: List[Map[Symbol, Any]], duration: String, args: List[String]): (List[Map[Symbol, Any]], String) = args match {
139+
case Nil => (options, duration)
140+
case "modules" :: value :: "monitor" :: tail if validateModules(value) => {
141+
val (remainingArgs, monitors) = cliMonitorsSubcommand(List(), Map(), tail.map(_.toString))
142+
cli(options :+ Map('modules -> value, 'monitors -> monitors), duration, remainingArgs)
143+
}
144+
case "duration" :: value :: tail if validateDuration(value) => cli(options, value, tail)
145+
case option :: tail => println(s"unknown cli option $option"); sys.exit(1)
146+
}
147+
148+
def cliMonitorsSubcommand(options: List[Map[Symbol, Any]], currentMonitor: Map[Symbol, Any], args: List[String]): (List[String], List[Map[Symbol, Any]]) = args match {
149+
case Nil => (List(), options :+ currentMonitor)
150+
case "modules" :: value :: "monitor" :: tail if validateModules(value) => (List("modules", value, "monitor") ++ tail, options :+ currentMonitor)
151+
case "duration" :: value :: tail if validateDuration(value) => (List("duration", value) ++ tail, options :+ currentMonitor)
152+
case "monitor" :: tail => cliMonitorsSubcommand(options :+ currentMonitor, Map(), tail)
153+
case "--frequency" :: value :: tail if validateDuration(value) => cliMonitorsSubcommand(options, currentMonitor ++ Map('frequency -> value), tail)
154+
case "--targets" :: value :: tail => cliMonitorsSubcommand(options, currentMonitor ++ Map('targets -> value), tail)
155+
case "--agg" :: value :: tail if validateAgg(value) => cliMonitorsSubcommand(options, currentMonitor ++ Map('agg -> value), tail)
156+
case "--console" :: tail => cliMonitorsSubcommand(options, currentMonitor ++ Map('console -> "true"), tail)
157+
case "--file" :: value :: tail => cliMonitorsSubcommand(options, currentMonitor ++ Map('file -> value), tail)
158+
case "--chart" :: tail => cliMonitorsSubcommand(options, currentMonitor ++ Map('chart -> "true"), tail)
159+
case option :: tail => println(s"unknown monitor option $option"); sys.exit(1)
160+
}
161+
162+
if(args.size == 0) {
163+
printHelp()
164+
sys.exit(1)
165+
}
166+
167+
else {
168+
Seq("bash", "scripts/system.bash").!
169+
val (configuration, duration) = cli(List(), "3600", args.toList)
170+
171+
for(powerMeterConf <- configuration) {
172+
val modules = powerMeterConf('modules).toString
173+
if(modules.contains("libpfm-core") || modules.contains("libpfm-core-process")) LibpfmHelper.init()
174+
175+
val powerMeter = PowerMeter.loadModule(powerMeterConf('modules).toString: _*)
176+
powerMeters :+= powerMeter
177+
178+
for(monitorConf <- powerMeterConf('monitors).asInstanceOf[List[Map[Symbol, Any]]]) {
179+
val frequency = monitorConf.getOrElse('frequency, "1000").toString.toInt.milliseconds
180+
val targets: Seq[Target] = monitorConf.getOrElse('targets, "").toString.toLowerCase
181+
val agg: Seq[Power] => Power = aggStrToAggFunction(monitorConf.getOrElse('agg, "max").toString.toLowerCase)
182+
val console = monitorConf.getOrElse('console, "").toString
183+
val file = monitorConf.getOrElse('file, "").toString
184+
val chart = monitorConf.getOrElse('chart, "").toString
185+
186+
val monitor = powerMeter.monitor(frequency)(targets: _*)(agg)
187+
monitors :+= monitor
188+
189+
if(console != "") {
190+
val consoleDisplay = new ConsoleDisplay()
191+
monitor.to(consoleDisplay)
192+
}
193+
194+
if(file != "") {
195+
val fileDisplay = new FileDisplay(file)
196+
monitor.to(fileDisplay)
197+
}
198+
199+
if(chart != "") {
200+
val chartDisplay = new JFreeChartDisplay()
201+
monitor.to(chartDisplay)
202+
}
203+
}
204+
}
205+
206+
Thread.sleep(duration.toInt.seconds.toMillis)
207+
208+
val isLibpfmInit = configuration.count(powerMeterConf => powerMeterConf('modules).toString.contains("libpfm-core") || powerMeterConf('modules).toString.contains("libpfm-core-process")) != 0
209+
if(isLibpfmInit) LibpfmHelper.deinit()
210+
}
211+
212+
shutdownHookThread.start()
213+
shutdownHookThread.join()
214+
shutdownHookThread.remove()
215+
sys.exit(0)
216+
}

0 commit comments

Comments
 (0)