Commit 03ebc20d authored by Simon Glass's avatar Simon Glass
Browse files

binman: Add basic support for debugging performance

One of binman's attributes is that it is extremely fast, at least for a
Python program. Add some simple timing around operations that might take
a while, such as reading an image and compressing it. This should help
to maintain the performance as new features are added.

This is for debugging purposes only.
Signed-off-by: Simon Glass's avatarSimon Glass <>
parent c31d0cb6
......@@ -646,6 +646,9 @@ def Binman(args):
if missing:
tout.Warning("\nSome images are invalid")
# Use this to debug the time take to pack the image
......@@ -6,6 +6,7 @@
from binman.entry import Entry
from binman import state
from dtoc import fdt_util
from patman import tools
from patman import tout
......@@ -59,8 +60,12 @@ class Entry_blob(Entry):
the data in chunks and avoid reading it all at once. For now
this seems like an unnecessary complication.
indata = tools.ReadFile(self._pathname)
data = self.CompressData(indata)
return True
......@@ -4568,6 +4568,14 @@ class TestFunctional(unittest.TestCase):
self.assertIn("Node '/binman/section@0': Timed out obtaining contents",
def testTiming(self):
"""Test output of timing information"""
data = self._DoReadFile('055_sections.dts')
with test_util.capture_sys_output() as (stdout, stderr):
self.assertIn('read:', stdout.getvalue())
self.assertIn('compress:', stdout.getvalue())
if __name__ == "__main__":
......@@ -5,8 +5,10 @@
# Holds and modifies the state information held by binman
from collections import defaultdict
import hashlib
import re
import time
import threading
from dtoc import fdt
......@@ -59,6 +61,27 @@ allow_entry_contraction = False
# Number of threads to use for binman (None means machine-dependent)
num_threads = None
class Timing:
"""Holds information about an operation that is being timed
name: Operation name (only one of each name is stored)
start: Start time of operation in seconds (None if not start)
accum:: Amount of time spent on this operation so far, in seconds
def __init__(self, name): = name
self.start = None # cause an error if TimingStart() is not called
self.accum = 0.0
# Holds timing info for each name:
# key: name of Timing info (
# value: Timing object
timing_info = {}
def GetFdtForEtype(etype):
"""Get the Fdt object for a particular device-tree entry
......@@ -443,3 +466,52 @@ def GetThreads():
Number of threads to use (None for default, 0 for single-threaded)
return num_threads
def GetTiming(name):
"""Get the timing info for a particular operation
The object is created if it does not already exist.
name: Operation name to get
Timing object for the current thread
threaded_name = '%s:%d' % (name, threading.get_ident())
timing = timing_info.get(threaded_name)
if not timing:
timing = Timing(threaded_name)
timing_info[threaded_name] = timing
return timing
def TimingStart(name):
"""Start the timer for an operation
name: Operation name to start
timing = GetTiming(name)
timing.start = time.monotonic()
def TimingAccum(name):
"""Stop and accumlate the time for an operation
This measures the time since the last TimingStart() and adds that to the
accumulated time.
name: Operation name to start
timing = GetTiming(name)
timing.accum += time.monotonic() - timing.start
def TimingShow():
"""Show all timing information"""
duration = defaultdict(float)
for threaded_name, timing in timing_info.items():
name = threaded_name.split(':')[0]
duration[name] += timing.accum
for name, seconds in duration.items():
print('%10s: %10.1fms' % (name, seconds * 1000))
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment