diff mbox series

[2/4] cooker/process/utils: Create profiling common function to remove code duplication

Message ID 20250716152249.96126-2-richard.purdie@linuxfoundation.org
State Accepted, archived
Commit b4f6bae97ac9607420fc49fd4c9e957d89c9a5f3
Headers show
Series [1/4] asyncrpc: Avoid file not found traceback in logs | expand

Commit Message

Richard Purdie July 16, 2025, 3:22 p.m. UTC
We have code duplication in the way we handle profiling of code sections.
Create a common function in utils which covers this.

Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
---
 lib/bb/cooker.py         | 16 +---------------
 lib/bb/server/process.py | 33 ++-------------------------------
 lib/bb/utils.py          | 28 ++++++++++++++++++++++++++++
 3 files changed, 31 insertions(+), 46 deletions(-)
diff mbox series

Patch

diff --git a/lib/bb/cooker.py b/lib/bb/cooker.py
index dc131939ed0..74b2d075d00 100644
--- a/lib/bb/cooker.py
+++ b/lib/bb/cooker.py
@@ -2027,21 +2027,7 @@  class Parser(multiprocessing.Process):
             self.exit = True
 
     def run(self):
-
-        if not self.profile:
-            self.realrun()
-            return
-
-        try:
-            import cProfile as profile
-        except:
-            import profile
-        prof = profile.Profile()
-        try:
-            profile.Profile.runcall(prof, self.realrun)
-        finally:
-            logfile = "profile-parse-%s.log" % multiprocessing.current_process().name
-            prof.dump_stats(logfile)
+        bb.utils.profle_function(self.profile, self.realrun, "profile-parse-%s.log" % multiprocessing.current_process().name, process=False)
 
     def realrun(self):
         # Signal handling here is hard. We must not terminate any process or thread holding the write
diff --git a/lib/bb/server/process.py b/lib/bb/server/process.py
index 4b35be62cd7..78d0a606e23 100644
--- a/lib/bb/server/process.py
+++ b/lib/bb/server/process.py
@@ -140,23 +140,7 @@  class ProcessServer():
             serverlog("Error writing to lock file: %s" % str(e))
             pass
 
-        if self.cooker.configuration.profile:
-            try:
-                import cProfile as profile
-            except:
-                import profile
-            prof = profile.Profile()
-
-            ret = profile.Profile.runcall(prof, self.main)
-
-            prof.dump_stats("profile.log")
-            bb.utils.process_profilelog("profile.log")
-            serverlog("Raw profiling information saved to profile.log and processed statistics to profile.log.processed")
-
-        else:
-            ret = self.main()
-
-        return ret
+        return bb.utils.profle_function(self.cooker.configuration.profile, self.main, "profile.log")
 
     def _idle_check(self):
         return len(self._idlefuns) == 0 and self.cooker.command.currentAsyncCommand is None
@@ -417,20 +401,7 @@  class ProcessServer():
                 serverlog("".join(msg))
 
     def idle_thread(self):
-        if self.cooker.configuration.profile:
-            try:
-                import cProfile as profile
-            except:
-                import profile
-            prof = profile.Profile()
-
-            ret = profile.Profile.runcall(prof, self.idle_thread_internal)
-
-            prof.dump_stats("profile-mainloop.log")
-            bb.utils.process_profilelog("profile-mainloop.log")
-            serverlog("Raw profiling information saved to profile-mainloop.log and processed statistics to profile-mainloop.log.processed")
-        else:
-            self.idle_thread_internal()
+        bb.utils.profle_function(self.cooker.configuration.profile, self.idle_thread_internal, "profile-idleloop.log")
 
     def idle_thread_internal(self):
         def remove_idle_func(function):
diff --git a/lib/bb/utils.py b/lib/bb/utils.py
index 1cc74ed5466..0289862c5dd 100644
--- a/lib/bb/utils.py
+++ b/lib/bb/utils.py
@@ -1418,6 +1418,34 @@  def cpu_count():
 def nonblockingfd(fd):
     fcntl.fcntl(fd, fcntl.F_SETFL, fcntl.fcntl(fd, fcntl.F_GETFL) | os.O_NONBLOCK)
 
+def profle_function(profile, function, output_fn, process=True):
+    """Common function to profile a code block and optionally process the
+    output using or processing function.
+
+    Arguments:
+
+    -  ``profile``: a boolean saying whether to enable profiling or not
+    -  ``function``: the function call to profile/run
+    -  ``outputfn``: where to write the profiling data
+    -  ``process``: whether to process the profiling data and write a report
+
+    Returns the wrapped function return value
+    """
+    if profile:
+        try:
+            import cProfile as profile
+        except:
+             import profile
+        prof = profile.Profile()
+        ret = profile.Profile.runcall(prof, function)
+        prof.dump_stats(output_fn)
+        if process:
+            process_profilelog(output_fn)
+            serverlog("Raw profiling information saved to %s and processed statistics to %s.processed" % (output_fn, output_fn))
+        return ret
+    else:
+        return function()
+
 def process_profilelog(fn, pout = None):
     # Either call with a list of filenames and set pout or a filename and optionally pout.
     if not pout: