Skip to content

Commit

Permalink
Merge pull request powerapi-ng#49 from Spirals-Team/develop
Browse files Browse the repository at this point in the history
feat: version 3.0 of PowerAPI
  • Loading branch information
rouvoy committed Mar 10, 2015
2 parents 4369693 + e594b47 commit 0fac1fa
Show file tree
Hide file tree
Showing 241 changed files with 43,541 additions and 13 deletions.
31 changes: 27 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
*.class
*.log
*~

# activator specific
activator*

# sbt specific
.cache/
.history/
.lib/
dist/*
target/
lib_managed/
src_managed/
project/boot/
project/plugins/project/
*#
*.iml
*.ipr
*.iws
*.pyc
*.tm.epoch
*.vim
*-shim.sbt
.idea/
/project/plugins/project
project/boot
target/
/logs
.cache
.classpath
.project
.settings

# Scala-IDE specific
.scala_dependencies
.worksheet

#bluecove
*/*/bluecove*

#tokens
secrets.tar
13 changes: 13 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
language: scala
scala:
- 2.11.4
script:
- sbt clean coverage test
before_install:
- openssl aes-256-cbc -K $encrypted_48ebb0d1c0b9_key -iv $encrypted_48ebb0d1c0b9_iv
-in secrets.tar.enc -out secrets.tar -d
- tar xvf secrets.tar
after_success:
- sbt coverageReport
- sbt coverageAggregate
- sbt codacyCoverage
1 change: 0 additions & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -659,4 +659,3 @@ specific requirements.
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.

42 changes: 34 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,48 @@
# PowerAPI
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.
[![Build Status](https://travis-ci.org/Spirals-Team/powerapi.svg?branch=develop)](https://travis-ci.org/Spirals-Team/powerapi)
[![Codacy Badge](https://www.codacy.com/project/badge/e0b0e331ca414250a7240b6be74aaa7b)](https://www.codacy.com/public/maximecolmant/powerapi)

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.

# About
PowerAPI is an open-source project developed by the [Spirals research group](https://team.inria.fr/spirals) (University of Lille 1 and Inria).
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/).

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

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

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

# Acknowledgments
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:
* [Akka](http://akka.io) (version 2.2.4 under [Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0)), for asynchronous processing
## Publications
* **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.
* **[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.
* **[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.
* **[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.
* **[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.
* **[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.
* **[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.
* **[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.
* **[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.

# Licence

## Acknowledgments
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:
* [Akka](http://akka.io) (version 2.3.6 under [Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0)), for asynchronous processing
* [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.
* [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.
* [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).
* [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.
* [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.
* [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.
* [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.
* [Saddle](http://saddle.github.io/) (version 1.3.3 under [Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0)), for data manipulation.

# License
This software is licensed under the *GNU Affero General Public License*, quoted below.

Copyright (C) 2011-2014 Inria, University of Lille 1.
Expand Down
22 changes: 22 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name := "powerapi"

version in ThisBuild := "3.0"

scalaVersion in ThisBuild := "2.11.4"

scalacOptions in ThisBuild ++= Seq(
"-language:reflectiveCalls",
"-language:implicitConversions",
"-feature",
"-deprecation"
)

// Logging
libraryDependencies in ThisBuild ++= Seq(
"org.apache.logging.log4j" % "log4j-api" % "2.1",
"org.apache.logging.log4j" % "log4j-core" % "2.1"
)

parallelExecution in (ThisBuild, Test) := false

codacyProjectTokenFile := Some("./codacy-token.txt")
37 changes: 37 additions & 0 deletions powerapi-cli/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name := "powerapi-cli"

lazy val downloadBluecoveApp = taskKey[File]("download-bluecove-app")
lazy val downloadBluecoveGplApp = taskKey[File]("download-bluecove-gpl-app")

downloadBluecoveApp := {
val locationBluecove = baseDirectory.value / "lib" / "bluecove-2.1.0.jar"
if(!locationBluecove.getParentFile.exists()) locationBluecove.getParentFile.mkdirs()
if(!locationBluecove.exists()) IO.download(url("https://bluecove.googlecode.com/files/bluecove-2.1.0.jar"), locationBluecove)
locationBluecove
}

downloadBluecoveGplApp := {
val locationBluecoveGpl = baseDirectory.value / "lib" / "bluecove-gpl-2.1.0.jar"
if(!locationBluecoveGpl.getParentFile.exists()) locationBluecoveGpl.getParentFile.mkdirs()
if(!locationBluecoveGpl.exists()) IO.download(url("https://bluecove.googlecode.com/files/bluecove-gpl-2.1.0.jar"), locationBluecoveGpl)
locationBluecoveGpl
}

mappings in Universal += downloadBluecoveApp.value -> s"lib/${downloadBluecoveApp.value.name}"

mappings in Universal += downloadBluecoveGplApp.value -> s"lib/${downloadBluecoveGplApp.value.name}"

mappings in Universal ++= {
((file("../") * "README*").get map {
readmeFile: File =>
readmeFile -> readmeFile.getName
}) ++
((file("../") * "LICENSE*").get map {
licenseFile: File =>
licenseFile -> licenseFile.getName
})
}

scriptClasspath ++= Seq("../conf", "../scripts")

NativePackagerKeys.executableScriptName := "powerapi"
216 changes: 216 additions & 0 deletions powerapi-cli/src/main/scala/org/powerapi/app/Application.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/*
* This software is licensed under the GNU Affero General Public License, quoted below.
*
* This file is a part of PowerAPI.
*
* Copyright (C) 2011-2015 Inria, University of Lille 1.
*
* PowerAPI is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* PowerAPI is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with PowerAPI.
*
* If not, please consult http://www.gnu.org/licenses/agpl-3.0.html.
*/
package org.powerapi.app

import java.lang.management.ManagementFactory

import org.powerapi.core.target.{Application, All, Process, Target}
import org.powerapi.reporter.{FileDisplay, JFreeChartDisplay, ConsoleDisplay}
import org.powerapi.{PowerMonitoring, PowerMeter, PowerModule}
import org.powerapi.core.power._
import org.powerapi.module.cpu.dvfs.CpuDvfsModule
import org.powerapi.module.cpu.simple.CpuSimpleModule
import org.powerapi.module.libpfm.{LibpfmHelper, LibpfmCoreProcessModule, LibpfmCoreModule}
import org.powerapi.module.powerspy.PowerSpyModule
import scala.concurrent.duration.DurationInt
import scala.sys
import scala.sys.process.stringSeqToProcess

/**
* PowerAPI CLI.
*
* @author <a href="mailto:[email protected]">Maxime Colmant</a>
* @author <a href="mailto:[email protected]">Loïc Huertas</a>
*/
object PowerAPI extends App {
val modulesR = """(cpu-simple|cpu-dvfs|libpfm-core|libpfm-core-process|powerspy)(,(cpu-simple|cpu-dvfs|libpfm-core|libpfm-core-process|powerspy))*""".r
val aggR = """max|min|geomean|logsum|mean|median|stdev|sum|variance""".r
val durationR = """\d+""".r
val pidR = """(\d+)""".r
val appR = """(.+)""".r

@volatile var powerMeters = Seq[PowerMeter]()
@volatile var monitors = Seq[PowerMonitoring]()

val shutdownHookThread = scala.sys.ShutdownHookThread {
monitors.foreach(monitor => monitor.cancel())
monitors = Seq()
powerMeters.foreach(powerMeter => powerMeter.shutdown())
powerMeters = Seq()
}

def validateModules(str: String) = str match {
case modulesR(_*) => true
case _ => false
}

implicit def modulesStrToPowerModules(str: String): Seq[PowerModule] = {
(for(module <- str.split(",")) yield {
module match {
case "cpu-simple" => CpuSimpleModule()
case "cpu-dvfs" => CpuDvfsModule()
case "libpfm-core" => LibpfmCoreModule()
case "libpfm-core-process" => LibpfmCoreProcessModule()
case "powerspy" => PowerSpyModule()
}
}).toSeq
}

def validateAgg(str: String): Boolean = str match {
case aggR(_*) => true
case _ => false
}

implicit def aggStrToAggFunction(str: String): Seq[Power] => Power = {
str match {
case "max" => MAX
case "min" => MIN
case "geomean" => GEOMEAN
case "logsum" => LOGSUM
case "mean" => MEAN
case "median" => MEDIAN
case "stdev" => STDEV
case "sum" => SUM
case "variance" => VARIANCE
}
}

def validateDuration(str: String): Boolean = str match {
case durationR(_*) => true
case _ => false
}

implicit def targetsStrToTargets(str: String): Seq[Target] = {
val strTargets = if(str.split(",").contains("all")) {
"all"
}
else str

(for(target <- strTargets.split(",")) yield {
target match {
case "" => Process(ManagementFactory.getRuntimeMXBean.getName.split("@")(0).toInt)
case "all" => All
case pidR(pid) => Process(pid.toInt)
case appR(app) => Application(app)
}
}).toSeq
}

def printHelp(): Unit = {
val str =
"""
|PowerAPI, Spirals Team"
|
|Build a software-defined power meter. Do not forget to configure correctly the modules (see the documentation).
|
|usage: ./powerapi modules [cpu-simple|cpu-dvfs|libpfm-core|libpfm-core-proces|powerspy, ...] \
| monitor --frequency [ms] --targets [pid, ..., app, ...)|all] --agg [max|min|geomean|logsum|mean|median|stdev|sum|variance] --[console,file [filepath],chart] \
| duration [s]
|
|example: ./powerapi modules cpu-simple monitor --frequency 1000 --targets firefox --agg max --console monitor --targets chrome --agg max --console \
| modules powerspy monitor --frequency 1000 --targets all --agg max --console \
| duration 30
""".stripMargin

println(str)
}

def cli(options: List[Map[Symbol, Any]], duration: String, args: List[String]): (List[Map[Symbol, Any]], String) = args match {
case Nil => (options, duration)
case "modules" :: value :: "monitor" :: tail if validateModules(value) => {
val (remainingArgs, monitors) = cliMonitorsSubcommand(List(), Map(), tail.map(_.toString))
cli(options :+ Map('modules -> value, 'monitors -> monitors), duration, remainingArgs)
}
case "duration" :: value :: tail if validateDuration(value) => cli(options, value, tail)
case option :: tail => println(s"unknown cli option $option"); sys.exit(1)
}

def cliMonitorsSubcommand(options: List[Map[Symbol, Any]], currentMonitor: Map[Symbol, Any], args: List[String]): (List[String], List[Map[Symbol, Any]]) = args match {
case Nil => (List(), options :+ currentMonitor)
case "modules" :: value :: "monitor" :: tail if validateModules(value) => (List("modules", value, "monitor") ++ tail, options :+ currentMonitor)
case "duration" :: value :: tail if validateDuration(value) => (List("duration", value) ++ tail, options :+ currentMonitor)
case "monitor" :: tail => cliMonitorsSubcommand(options :+ currentMonitor, Map(), tail)
case "--frequency" :: value :: tail if validateDuration(value) => cliMonitorsSubcommand(options, currentMonitor ++ Map('frequency -> value), tail)
case "--targets" :: value :: tail => cliMonitorsSubcommand(options, currentMonitor ++ Map('targets -> value), tail)
case "--agg" :: value :: tail if validateAgg(value) => cliMonitorsSubcommand(options, currentMonitor ++ Map('agg -> value), tail)
case "--console" :: tail => cliMonitorsSubcommand(options, currentMonitor ++ Map('console -> "true"), tail)
case "--file" :: value :: tail => cliMonitorsSubcommand(options, currentMonitor ++ Map('file -> value), tail)
case "--chart" :: tail => cliMonitorsSubcommand(options, currentMonitor ++ Map('chart -> "true"), tail)
case option :: tail => println(s"unknown monitor option $option"); sys.exit(1)
}

if(args.size == 0) {
printHelp()
sys.exit(1)
}

else {
Seq("bash", "scripts/system.bash").!
val (configuration, duration) = cli(List(), "3600", args.toList)

for(powerMeterConf <- configuration) {
val modules = powerMeterConf('modules).toString
if(modules.contains("libpfm-core") || modules.contains("libpfm-core-process")) LibpfmHelper.init()

val powerMeter = PowerMeter.loadModule(powerMeterConf('modules).toString: _*)
powerMeters :+= powerMeter

for(monitorConf <- powerMeterConf('monitors).asInstanceOf[List[Map[Symbol, Any]]]) {
val frequency = monitorConf.getOrElse('frequency, "1000").toString.toInt.milliseconds
val targets: Seq[Target] = monitorConf.getOrElse('targets, "").toString.toLowerCase
val agg: Seq[Power] => Power = aggStrToAggFunction(monitorConf.getOrElse('agg, "max").toString.toLowerCase)
val console = monitorConf.getOrElse('console, "").toString
val file = monitorConf.getOrElse('file, "").toString
val chart = monitorConf.getOrElse('chart, "").toString

val monitor = powerMeter.monitor(frequency)(targets: _*)(agg)
monitors :+= monitor

if(console != "") {
val consoleDisplay = new ConsoleDisplay()
monitor.to(consoleDisplay)
}

if(file != "") {
val fileDisplay = new FileDisplay(file)
monitor.to(fileDisplay)
}

if(chart != "") {
val chartDisplay = new JFreeChartDisplay()
monitor.to(chartDisplay)
}
}
}

Thread.sleep(duration.toInt.seconds.toMillis)

val isLibpfmInit = configuration.count(powerMeterConf => powerMeterConf('modules).toString.contains("libpfm-core") || powerMeterConf('modules).toString.contains("libpfm-core-process")) != 0
if(isLibpfmInit) LibpfmHelper.deinit()
}

shutdownHookThread.start()
shutdownHookThread.join()
shutdownHookThread.remove()
sys.exit(0)
}
Loading

0 comments on commit 0fac1fa

Please sign in to comment.