buildstats: add system state sampling
/proc/[diskstats|meminfo|stat] get sampled and written to the same proc_<filename>.log files as during normal bootchat logging. This will allow rendering the CPU, disk and memory usage charts. Right now sampling happens once a second, triggered by the heartbeat event.That produces quite a bit of data for long builds, which will be addressed in a separate commit by storing the data in a more compact form. (From OE-Core rev: 6f4e8180b5b4857eaf6caf410fd3a4a41ed85930) Signed-off-by: Patrick Ohly <patrick.ohly@intel.com> Signed-off-by: Ross Burton <ross.burton@intel.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
parent
5956492c20
commit
8f475b78c9
|
@ -188,3 +188,27 @@ python run_buildstats () {
|
|||
addhandler run_buildstats
|
||||
run_buildstats[eventmask] = "bb.event.BuildStarted bb.event.BuildCompleted bb.build.TaskStarted bb.build.TaskSucceeded bb.build.TaskFailed"
|
||||
|
||||
python runqueue_stats () {
|
||||
import buildstats
|
||||
from bb import event, runqueue
|
||||
# We should not record any samples before the first task has started,
|
||||
# because that's the first activity shown in the process chart.
|
||||
# Besides, at that point we are sure that the build variables
|
||||
# are available that we need to find the output directory.
|
||||
# The persistent SystemStats is stored in the datastore and
|
||||
# closed when the build is done.
|
||||
system_stats = d.getVar('_buildstats_system_stats', True)
|
||||
if not system_stats and isinstance(e, (bb.runqueue.sceneQueueTaskStarted, bb.runqueue.runQueueTaskStarted)):
|
||||
system_stats = buildstats.SystemStats(d)
|
||||
d.setVar('_buildstats_system_stats', system_stats)
|
||||
if system_stats:
|
||||
# Ensure that we sample at important events.
|
||||
done = isinstance(e, bb.event.BuildCompleted)
|
||||
system_stats.sample(force=done)
|
||||
if done:
|
||||
system_stats.close()
|
||||
d.delVar('_buildstats_system_stats')
|
||||
}
|
||||
|
||||
addhandler runqueue_stats
|
||||
runqueue_stats[eventmask] = "bb.runqueue.sceneQueueTaskStarted bb.runqueue.runQueueTaskStarted bb.event.HeartbeatEvent bb.event.BuildCompleted"
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
# Implements system state sampling. Called by buildstats.bbclass.
|
||||
# Because it is a real Python module, it can hold persistent state,
|
||||
# like open log files and the time of the last sampling.
|
||||
|
||||
import time
|
||||
|
||||
class SystemStats:
|
||||
def __init__(self, d):
|
||||
bn = d.getVar('BUILDNAME', True)
|
||||
bsdir = os.path.join(d.getVar('BUILDSTATS_BASE', True), bn)
|
||||
bb.utils.mkdirhier(bsdir)
|
||||
|
||||
self.proc_files = []
|
||||
for filename in ('diskstats', 'meminfo', 'stat'):
|
||||
# In practice, this class gets instantiated only once in
|
||||
# the bitbake cooker process. Therefore 'append' mode is
|
||||
# not strictly necessary, but using it makes the class
|
||||
# more robust should two processes ever write
|
||||
# concurrently.
|
||||
self.proc_files.append((filename,
|
||||
open(os.path.join(bsdir, 'proc_%s.log' % filename), 'ab')))
|
||||
# Last time that we sampled data.
|
||||
self.last = 0
|
||||
# Minimum number of seconds between recording a sample. This
|
||||
# becames relevant when we get called very often while many
|
||||
# short tasks get started. Sampling during quiet periods
|
||||
# depends on the heartbeat event, which fires less often.
|
||||
self.min_seconds = 1
|
||||
|
||||
def close(self):
|
||||
self.monitor_disk.close()
|
||||
for _, output, _ in self.proc_files:
|
||||
output.close()
|
||||
|
||||
def sample(self, force):
|
||||
now = time.time()
|
||||
if (now - self.last > self.min_seconds) or force:
|
||||
for filename, output in self.proc_files:
|
||||
with open(os.path.join('/proc', filename), 'rb') as input:
|
||||
data = input.read()
|
||||
# Unbuffered raw write, less overhead and useful
|
||||
# in case that we end up with concurrent writes.
|
||||
os.write(output.fileno(),
|
||||
('%.0f\n' % now).encode('ascii') +
|
||||
data +
|
||||
b'\n')
|
||||
self.last = now
|
Loading…
Reference in New Issue