Skip to content

Commit 04a05b6

Browse files
huertasmcolmant
authored andcommitted
feature(rapl): implement RAPL module
1 parent ee3dc34 commit 04a05b6

File tree

17 files changed

+737
-2
lines changed

17 files changed

+737
-2
lines changed

powerapi-core/src/main/scala/org/powerapi/core/OSHelper.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import org.apache.logging.log4j.LogManager
2828
import org.powerapi.core.FileHelper.using
2929
import org.powerapi.core.target.{Application, Process, Target}
3030
import scala.collection.JavaConversions._
31-
import scala.sys.process.stringSeqToProcess
31+
import scala.sys.process._
3232

3333
/**
3434
* This is not a monitoring target. It's an internal wrapper for the Thread IDentifier.

powerapi-core/src/main/scala/org/powerapi/core/power/Power.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,16 @@
2222
*/
2323
package org.powerapi.core.power
2424

25+
import scala.concurrent.duration._
2526
import org.apache.logging.log4j.LogManager
2627
import RawPower._
2728

2829
object Power {
2930
def apply(value: Double, unit: PowerUnit): Power = new RawPower(value, unit)
3031
def apply(value: Double, unit: String): Power = new RawPower(value, PowerUnitSystem(unit))
3132

33+
def fromJoule(joule: Double, duration: FiniteDuration = 1.second) = new RawPower(joule / (duration.toMillis / 1000.0), WATTS)
34+
3235
/**
3336
* The natural ordering of powers matches the natural ordering for Double.
3437
*/

powerapi-core/src/main/scala/org/powerapi/core/target/Target.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,6 @@ case class TargetUsageRatio(ratio: Double)
6969
* @author <a href="mailto:[email protected]">Romain Rouvoy</a>
7070
* @author <a href="mailto:[email protected]">Maxime Colmant</a>
7171
*/
72-
object All extends Target
72+
object All extends Target {
73+
override def toString = "All"
74+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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-2014 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.module.rapl
24+
25+
import java.util.UUID
26+
27+
import akka.actor.ActorRef
28+
import org.powerapi.core.ClockChannel.ClockTick
29+
import org.powerapi.core.{Channel, MessageBus}
30+
import org.powerapi.core.target.{Target, TargetUsageRatio}
31+
import org.powerapi.core.power.Power
32+
import org.powerapi.module.SensorChannel.SensorReport
33+
34+
/**
35+
* RAPLChannel channel and messages.
36+
*
37+
* @author <a href="mailto:[email protected]">Loïc Huertas</a>
38+
*/
39+
object RAPLChannel extends Channel {
40+
41+
type M = org.powerapi.module.SensorChannel.M
42+
43+
/**
44+
* RAPLPower is represented as a dedicated type of message.
45+
*
46+
* @param topic: subject used for routing the message.
47+
* @param muid: monitor unique identifier (MUID), which is at the origin of the report flow.
48+
* @param target: monitor target.
49+
* @param power: power consumption got by an external device.
50+
* @param targetRatio: target cpu ratio usage.
51+
* @param tick: tick origin.
52+
*/
53+
case class RAPLPower(topic: String,
54+
muid: UUID,
55+
target: Target,
56+
power: Power,
57+
targetRatio: TargetUsageRatio,
58+
tick: ClockTick) extends SensorReport
59+
60+
/**
61+
* Topic for communicating with the Formula actors.
62+
*/
63+
private val topic = "sensor:rapl"
64+
65+
/**
66+
* Publish a RAPLPower in the event bus.
67+
*/
68+
def publishRAPLPower(muid: UUID, target: Target, power: Power, targetRatio: TargetUsageRatio, tick: ClockTick): MessageBus => Unit = {
69+
publish(RAPLPower(topic = topic,
70+
muid = muid,
71+
target = target,
72+
power = power,
73+
targetRatio = targetRatio,
74+
tick = tick))
75+
}
76+
77+
/**
78+
* External method used by the Formula for interacting with the bus.
79+
*/
80+
def subscribeRAPLPower: MessageBus => ActorRef => Unit = {
81+
subscribe(topic)
82+
}
83+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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-2014 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.module.rapl
24+
25+
import org.powerapi.core.MessageBus
26+
import org.powerapi.module.FormulaComponent
27+
import org.powerapi.module.rapl.RAPLChannel.{RAPLPower, subscribeRAPLPower}
28+
import org.powerapi.module.PowerChannel.publishRawPowerReport
29+
30+
31+
/**
32+
* Implements a CpuFormula by making the ratio between current CPU power (obtained by collecting
33+
* data from RAPL registers) and the process CPU usage.
34+
*
35+
* @author <a href="mailto:[email protected]">Loïc Huertas</a>
36+
*/
37+
class RAPLFormula(eventBus: MessageBus) extends FormulaComponent[RAPLPower](eventBus) {
38+
def subscribeSensorReport(): Unit = {
39+
subscribeRAPLPower(eventBus)(self)
40+
}
41+
42+
def compute(sensorReport: RAPLPower): Unit = {
43+
lazy val power = sensorReport.power * sensorReport.targetRatio.ratio
44+
publishRawPowerReport(sensorReport.muid, sensorReport.target, power, "cpu", sensorReport.tick)(eventBus)
45+
}
46+
}
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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-2014 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.module.rapl
24+
25+
import java.io.{IOException, FileNotFoundException, FileInputStream}
26+
import java.nio.channels.FileChannel
27+
import java.nio.{ByteOrder, ByteBuffer}
28+
import org.apache.logging.log4j.LogManager
29+
import org.powerapi.core.{ConfigValue, Configuration}
30+
import scala.sys.process.stringSeqToProcess
31+
32+
/**
33+
* Collecting energy information contained into RAPL registers (MSR)
34+
* and providing the CPU energy.
35+
*
36+
* @author <a href="mailto:[email protected]">Loïc Huertas</a>
37+
*/
38+
class RAPLHelper extends Configuration {
39+
private val log = LogManager.getLogger
40+
41+
/**
42+
* CPU info file, giving information about specifications of the processor.
43+
*/
44+
lazy val cpuInfoURL = load { _.getString("rapl.cpuInfoURL") } match {
45+
case ConfigValue(url) => url
46+
case _ => "/proc/cpuinfo"
47+
}
48+
49+
/**
50+
* MSR registers URL, giving information about estimation (from RAPL model) of CPU energy consumption.
51+
*/
52+
lazy val msrURL = load { _.getString("rapl.msrURL") } match {
53+
case ConfigValue(url) => url
54+
case _ => "/dev/cpu/0/msr"
55+
}
56+
57+
/* Platform specific RAPL Domains */
58+
private val MSR_RAPL_POWER_UNIT = 0x606
59+
private val MSR_PKG_ENERGY_STATUS = 0x611
60+
61+
private val data = ByteBuffer.allocate(java.lang.Long.SIZE / java.lang.Byte.SIZE)
62+
data.order(ByteOrder.nativeOrder)
63+
64+
/* Architecture ID */
65+
private val archs = collection.mutable.Map(
66+
42 -> "Sandybridge",
67+
45 -> "Sandybridge-EP",
68+
58 -> "Ivybridge",
69+
62 -> "Ivybridge-EP",
70+
60 -> "Haswell"
71+
)
72+
73+
/* Related to MSR reading */
74+
lazy val msrFile: Option[FileChannel] = {
75+
if(detectCpu) {
76+
Seq("modprobe", "msr").!
77+
Some(new FileInputStream(msrURL).getChannel)
78+
}
79+
else None
80+
}
81+
82+
lazy val powerUnits = Math.pow(0.5, readMsr(MSR_RAPL_POWER_UNIT) & 0xf)
83+
lazy val energyUnits = Math.pow(0.5,(readMsr(MSR_RAPL_POWER_UNIT) >> 8) & 0x1f)
84+
lazy val timeUnits = Math.pow(0.5, (readMsr(MSR_RAPL_POWER_UNIT) >> 16) & 0xf)
85+
86+
def getRAPLEnergy: Double = readMsr(MSR_PKG_ENERGY_STATUS) * energyUnits
87+
88+
def close(): Unit = {
89+
msrFile match {
90+
case Some(file) => file.close()
91+
case _ => {}
92+
}
93+
}
94+
95+
private def readMsr(which: Int): Long = {
96+
msrFile match {
97+
case Some(file) => {
98+
try {
99+
data.clear()
100+
file.read(data, which)
101+
data.rewind()
102+
data.getLong
103+
}
104+
catch {
105+
case fnfe: FileNotFoundException => { //if this exception occur, type 'modprobe msr'
106+
log.error("rdmsr: file not found exception: {}", fnfe.getMessage)
107+
0l
108+
}
109+
case ioe: IOException => {
110+
log.error("rdmsr: i/o exception: {}", ioe.getMessage)
111+
0l
112+
}
113+
case npe: NullPointerException => {
114+
log.error("rdmsr: null pointer exception: {}", npe.getMessage)
115+
0l
116+
}
117+
}
118+
}
119+
case _ => 0l
120+
}
121+
}
122+
123+
private def detectCpu: Boolean = {
124+
val source = io.Source.fromFile(cpuInfoURL).getLines
125+
source.find(l => l.startsWith("vendor_id") && l.endsWith("GenuineIntel")) match {
126+
case Some(_) => source.find(l => l.startsWith("cpu family") && l.endsWith("6")) match {
127+
case Some(_) => source.find(_.startsWith("model")) match {
128+
case Some(model) => archs.getOrElse(model.split("\\s").last.toInt, "") match {
129+
case "" => log.error("cpuinfo: Unsupported model {}", model); false
130+
case modelCheck => log.info("Found {} CPU", modelCheck); true
131+
}
132+
case None => log.error("cpuinfo: CPU model missing"); false
133+
}
134+
case None => log.error("cpuinfo: Wrong CPU family"); false
135+
}
136+
case None => log.error("cpuinfo: This CPU is not an Intel chip"); false
137+
}
138+
}
139+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
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-2014 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.module.rapl
24+
25+
import org.powerapi.PowerModule
26+
import org.powerapi.core.LinuxHelper
27+
28+
class RAPLModule extends PowerModule {
29+
lazy val underlyingSensorsClasses = Seq((classOf[RAPLSensor], Seq(new LinuxHelper, new RAPLHelper)))
30+
lazy val underlyingFormulaeClasses = Seq((classOf[RAPLFormula], Seq()))
31+
}
32+
33+
object RAPLModule {
34+
def apply(): RAPLModule = {
35+
new RAPLModule()
36+
}
37+
}

0 commit comments

Comments
 (0)