cProfile.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. #! /usr/bin/env python
  2. """Python interface for the 'lsprof' profiler.
  3. Compatible with the 'profile' module.
  4. """
  5. __all__ = ["run", "runctx", "help", "Profile"]
  6. import _lsprof
  7. # ____________________________________________________________
  8. # Simple interface
  9. def run(statement, filename=None, sort=-1):
  10. """Run statement under profiler optionally saving results in filename
  11. This function takes a single argument that can be passed to the
  12. "exec" statement, and an optional file name. In all cases this
  13. routine attempts to "exec" its first argument and gather profiling
  14. statistics from the execution. If no file name is present, then this
  15. function automatically prints a simple profiling report, sorted by the
  16. standard name string (file/line/function-name) that is presented in
  17. each line.
  18. """
  19. prof = Profile()
  20. result = None
  21. try:
  22. try:
  23. prof = prof.run(statement)
  24. except SystemExit:
  25. pass
  26. finally:
  27. if filename is not None:
  28. prof.dump_stats(filename)
  29. else:
  30. result = prof.print_stats(sort)
  31. return result
  32. def runctx(statement, globals, locals, filename=None, sort=-1):
  33. """Run statement under profiler, supplying your own globals and locals,
  34. optionally saving results in filename.
  35. statement and filename have the same semantics as profile.run
  36. """
  37. prof = Profile()
  38. result = None
  39. try:
  40. try:
  41. prof = prof.runctx(statement, globals, locals)
  42. except SystemExit:
  43. pass
  44. finally:
  45. if filename is not None:
  46. prof.dump_stats(filename)
  47. else:
  48. result = prof.print_stats(sort)
  49. return result
  50. # Backwards compatibility.
  51. def help():
  52. print "Documentation for the profile/cProfile modules can be found "
  53. print "in the Python Library Reference, section 'The Python Profiler'."
  54. # ____________________________________________________________
  55. class Profile(_lsprof.Profiler):
  56. """Profile(custom_timer=None, time_unit=None, subcalls=True, builtins=True)
  57. Builds a profiler object using the specified timer function.
  58. The default timer is a fast built-in one based on real time.
  59. For custom timer functions returning integers, time_unit can
  60. be a float specifying a scale (i.e. how long each integer unit
  61. is, in seconds).
  62. """
  63. # Most of the functionality is in the base class.
  64. # This subclass only adds convenient and backward-compatible methods.
  65. def print_stats(self, sort=-1):
  66. import pstats
  67. pstats.Stats(self).strip_dirs().sort_stats(sort).print_stats()
  68. def dump_stats(self, file):
  69. import marshal
  70. f = open(file, 'wb')
  71. self.create_stats()
  72. marshal.dump(self.stats, f)
  73. f.close()
  74. def create_stats(self):
  75. self.disable()
  76. self.snapshot_stats()
  77. def snapshot_stats(self):
  78. entries = self.getstats()
  79. self.stats = {}
  80. callersdicts = {}
  81. # call information
  82. for entry in entries:
  83. func = label(entry.code)
  84. nc = entry.callcount # ncalls column of pstats (before '/')
  85. cc = nc - entry.reccallcount # ncalls column of pstats (after '/')
  86. tt = entry.inlinetime # tottime column of pstats
  87. ct = entry.totaltime # cumtime column of pstats
  88. callers = {}
  89. callersdicts[id(entry.code)] = callers
  90. self.stats[func] = cc, nc, tt, ct, callers
  91. # subcall information
  92. for entry in entries:
  93. if entry.calls:
  94. func = label(entry.code)
  95. for subentry in entry.calls:
  96. try:
  97. callers = callersdicts[id(subentry.code)]
  98. except KeyError:
  99. continue
  100. nc = subentry.callcount
  101. cc = nc - subentry.reccallcount
  102. tt = subentry.inlinetime
  103. ct = subentry.totaltime
  104. if func in callers:
  105. prev = callers[func]
  106. nc += prev[0]
  107. cc += prev[1]
  108. tt += prev[2]
  109. ct += prev[3]
  110. callers[func] = nc, cc, tt, ct
  111. # The following two methods can be called by clients to use
  112. # a profiler to profile a statement, given as a string.
  113. def run(self, cmd):
  114. import __main__
  115. dict = __main__.__dict__
  116. return self.runctx(cmd, dict, dict)
  117. def runctx(self, cmd, globals, locals):
  118. self.enable()
  119. try:
  120. exec cmd in globals, locals
  121. finally:
  122. self.disable()
  123. return self
  124. # This method is more useful to profile a single function call.
  125. def runcall(self, func, *args, **kw):
  126. self.enable()
  127. try:
  128. return func(*args, **kw)
  129. finally:
  130. self.disable()
  131. # ____________________________________________________________
  132. def label(code):
  133. if isinstance(code, str):
  134. return ('~', 0, code) # built-in functions ('~' sorts at the end)
  135. else:
  136. return (code.co_filename, code.co_firstlineno, code.co_name)
  137. # ____________________________________________________________
  138. def main():
  139. import os, sys
  140. from optparse import OptionParser
  141. usage = "cProfile.py [-o output_file_path] [-s sort] scriptfile [arg] ..."
  142. parser = OptionParser(usage=usage)
  143. parser.allow_interspersed_args = False
  144. parser.add_option('-o', '--outfile', dest="outfile",
  145. help="Save stats to <outfile>", default=None)
  146. parser.add_option('-s', '--sort', dest="sort",
  147. help="Sort order when printing to stdout, based on pstats.Stats class",
  148. default=-1)
  149. if not sys.argv[1:]:
  150. parser.print_usage()
  151. sys.exit(2)
  152. (options, args) = parser.parse_args()
  153. sys.argv[:] = args
  154. if len(args) > 0:
  155. progname = args[0]
  156. sys.path.insert(0, os.path.dirname(progname))
  157. with open(progname, 'rb') as fp:
  158. code = compile(fp.read(), progname, 'exec')
  159. globs = {
  160. '__file__': progname,
  161. '__name__': '__main__',
  162. '__package__': None,
  163. }
  164. runctx(code, globs, None, options.outfile, options.sort)
  165. else:
  166. parser.print_usage()
  167. return parser
  168. # When invoked as main program, invoke the profiler on a script
  169. if __name__ == '__main__':
  170. main()