(root)/
Python-3.12.0/
Lib/
cProfile.py
       1  #! /usr/bin/env python3
       2  
       3  """Python interface for the 'lsprof' profiler.
       4     Compatible with the 'profile' module.
       5  """
       6  
       7  __all__ = ["run", "runctx", "Profile"]
       8  
       9  import _lsprof
      10  import importlib.machinery
      11  import io
      12  import profile as _pyprofile
      13  
      14  # ____________________________________________________________
      15  # Simple interface
      16  
      17  def run(statement, filename=None, sort=-1):
      18      return _pyprofile._Utils(Profile).run(statement, filename, sort)
      19  
      20  def runctx(statement, globals, locals, filename=None, sort=-1):
      21      return _pyprofile._Utils(Profile).runctx(statement, globals, locals,
      22                                               filename, sort)
      23  
      24  run.__doc__ = _pyprofile.run.__doc__
      25  runctx.__doc__ = _pyprofile.runctx.__doc__
      26  
      27  # ____________________________________________________________
      28  
      29  class ESC[4;38;5;81mProfile(ESC[4;38;5;149m_lsprofESC[4;38;5;149m.ESC[4;38;5;149mProfiler):
      30      """Profile(timer=None, timeunit=None, subcalls=True, builtins=True)
      31  
      32      Builds a profiler object using the specified timer function.
      33      The default timer is a fast built-in one based on real time.
      34      For custom timer functions returning integers, timeunit can
      35      be a float specifying a scale (i.e. how long each integer unit
      36      is, in seconds).
      37      """
      38  
      39      # Most of the functionality is in the base class.
      40      # This subclass only adds convenient and backward-compatible methods.
      41  
      42      def print_stats(self, sort=-1):
      43          import pstats
      44          pstats.Stats(self).strip_dirs().sort_stats(sort).print_stats()
      45  
      46      def dump_stats(self, file):
      47          import marshal
      48          with open(file, 'wb') as f:
      49              self.create_stats()
      50              marshal.dump(self.stats, f)
      51  
      52      def create_stats(self):
      53          self.disable()
      54          self.snapshot_stats()
      55  
      56      def snapshot_stats(self):
      57          entries = self.getstats()
      58          self.stats = {}
      59          callersdicts = {}
      60          # call information
      61          for entry in entries:
      62              func = label(entry.code)
      63              nc = entry.callcount         # ncalls column of pstats (before '/')
      64              cc = nc - entry.reccallcount # ncalls column of pstats (after '/')
      65              tt = entry.inlinetime        # tottime column of pstats
      66              ct = entry.totaltime         # cumtime column of pstats
      67              callers = {}
      68              callersdicts[id(entry.code)] = callers
      69              self.stats[func] = cc, nc, tt, ct, callers
      70          # subcall information
      71          for entry in entries:
      72              if entry.calls:
      73                  func = label(entry.code)
      74                  for subentry in entry.calls:
      75                      try:
      76                          callers = callersdicts[id(subentry.code)]
      77                      except KeyError:
      78                          continue
      79                      nc = subentry.callcount
      80                      cc = nc - subentry.reccallcount
      81                      tt = subentry.inlinetime
      82                      ct = subentry.totaltime
      83                      if func in callers:
      84                          prev = callers[func]
      85                          nc += prev[0]
      86                          cc += prev[1]
      87                          tt += prev[2]
      88                          ct += prev[3]
      89                      callers[func] = nc, cc, tt, ct
      90  
      91      # The following two methods can be called by clients to use
      92      # a profiler to profile a statement, given as a string.
      93  
      94      def run(self, cmd):
      95          import __main__
      96          dict = __main__.__dict__
      97          return self.runctx(cmd, dict, dict)
      98  
      99      def runctx(self, cmd, globals, locals):
     100          self.enable()
     101          try:
     102              exec(cmd, globals, locals)
     103          finally:
     104              self.disable()
     105          return self
     106  
     107      # This method is more useful to profile a single function call.
     108      def runcall(self, func, /, *args, **kw):
     109          self.enable()
     110          try:
     111              return func(*args, **kw)
     112          finally:
     113              self.disable()
     114  
     115      def __enter__(self):
     116          self.enable()
     117          return self
     118  
     119      def __exit__(self, *exc_info):
     120          self.disable()
     121  
     122  # ____________________________________________________________
     123  
     124  def label(code):
     125      if isinstance(code, str):
     126          return ('~', 0, code)    # built-in functions ('~' sorts at the end)
     127      else:
     128          return (code.co_filename, code.co_firstlineno, code.co_name)
     129  
     130  # ____________________________________________________________
     131  
     132  def main():
     133      import os
     134      import sys
     135      import runpy
     136      import pstats
     137      from optparse import OptionParser
     138      usage = "cProfile.py [-o output_file_path] [-s sort] [-m module | scriptfile] [arg] ..."
     139      parser = OptionParser(usage=usage)
     140      parser.allow_interspersed_args = False
     141      parser.add_option('-o', '--outfile', dest="outfile",
     142          help="Save stats to <outfile>", default=None)
     143      parser.add_option('-s', '--sort', dest="sort",
     144          help="Sort order when printing to stdout, based on pstats.Stats class",
     145          default=2,
     146          choices=sorted(pstats.Stats.sort_arg_dict_default))
     147      parser.add_option('-m', dest="module", action="store_true",
     148          help="Profile a library module", default=False)
     149  
     150      if not sys.argv[1:]:
     151          parser.print_usage()
     152          sys.exit(2)
     153  
     154      (options, args) = parser.parse_args()
     155      sys.argv[:] = args
     156  
     157      # The script that we're profiling may chdir, so capture the absolute path
     158      # to the output file at startup.
     159      if options.outfile is not None:
     160          options.outfile = os.path.abspath(options.outfile)
     161  
     162      if len(args) > 0:
     163          if options.module:
     164              code = "run_module(modname, run_name='__main__')"
     165              globs = {
     166                  'run_module': runpy.run_module,
     167                  'modname': args[0]
     168              }
     169          else:
     170              progname = args[0]
     171              sys.path.insert(0, os.path.dirname(progname))
     172              with io.open_code(progname) as fp:
     173                  code = compile(fp.read(), progname, 'exec')
     174              spec = importlib.machinery.ModuleSpec(name='__main__', loader=None,
     175                                                    origin=progname)
     176              globs = {
     177                  '__spec__': spec,
     178                  '__file__': spec.origin,
     179                  '__name__': spec.name,
     180                  '__package__': None,
     181                  '__cached__': None,
     182              }
     183          try:
     184              runctx(code, globs, None, options.outfile, options.sort)
     185          except BrokenPipeError as exc:
     186              # Prevent "Exception ignored" during interpreter shutdown.
     187              sys.stdout = None
     188              sys.exit(exc.errno)
     189      else:
     190          parser.print_usage()
     191      return parser
     192  
     193  # When invoked as main program, invoke the profiler on a script
     194  if __name__ == '__main__':
     195      main()