mirror of
https://github.com/python/cpython.git
synced 2024-11-27 20:04:41 +08:00
remove hotshot profiler from Py3k
This commit is contained in:
parent
b62e8a8062
commit
0e474a801a
@ -12,6 +12,5 @@ allowing you to identify bottlenecks in your programs.
|
||||
bdb.rst
|
||||
pdb.rst
|
||||
profile.rst
|
||||
hotshot.rst
|
||||
timeit.rst
|
||||
trace.rst
|
@ -1,144 +0,0 @@
|
||||
|
||||
:mod:`hotshot` --- High performance logging profiler
|
||||
====================================================
|
||||
|
||||
.. module:: hotshot
|
||||
:synopsis: High performance logging profiler, mostly written in C.
|
||||
.. moduleauthor:: Fred L. Drake, Jr. <fdrake@acm.org>
|
||||
.. sectionauthor:: Anthony Baxter <anthony@interlink.com.au>
|
||||
|
||||
|
||||
This module provides a nicer interface to the :mod:`_hotshot` C module. Hotshot
|
||||
is a replacement for the existing :mod:`profile` module. As it's written mostly
|
||||
in C, it should result in a much smaller performance impact than the existing
|
||||
:mod:`profile` module.
|
||||
|
||||
.. note::
|
||||
|
||||
The :mod:`hotshot` module focuses on minimizing the overhead while profiling, at
|
||||
the expense of long data post-processing times. For common usages it is
|
||||
recommended to use :mod:`cProfile` instead. :mod:`hotshot` is not maintained and
|
||||
might be removed from the standard library in the future.
|
||||
|
||||
.. warning::
|
||||
|
||||
The :mod:`hotshot` profiler does not yet work well with threads. It is useful to
|
||||
use an unthreaded script to run the profiler over the code you're interested in
|
||||
measuring if at all possible.
|
||||
|
||||
|
||||
.. class:: Profile(logfile[, lineevents[, linetimings]])
|
||||
|
||||
The profiler object. The argument *logfile* is the name of a log file to use for
|
||||
logged profile data. The argument *lineevents* specifies whether to generate
|
||||
events for every source line, or just on function call/return. It defaults to
|
||||
``0`` (only log function call/return). The argument *linetimings* specifies
|
||||
whether to record timing information. It defaults to ``1`` (store timing
|
||||
information).
|
||||
|
||||
|
||||
.. _hotshot-objects:
|
||||
|
||||
Profile Objects
|
||||
---------------
|
||||
|
||||
Profile objects have the following methods:
|
||||
|
||||
|
||||
.. method:: Profile.addinfo(key, value)
|
||||
|
||||
Add an arbitrary labelled value to the profile output.
|
||||
|
||||
|
||||
.. method:: Profile.close()
|
||||
|
||||
Close the logfile and terminate the profiler.
|
||||
|
||||
|
||||
.. method:: Profile.fileno()
|
||||
|
||||
Return the file descriptor of the profiler's log file.
|
||||
|
||||
|
||||
.. method:: Profile.run(cmd)
|
||||
|
||||
Profile an :func:`exec`\ -compatible string in the script environment. The
|
||||
globals from the :mod:`__main__` module are used as both the globals and locals
|
||||
for the script.
|
||||
|
||||
|
||||
.. method:: Profile.runcall(func, *args, **keywords)
|
||||
|
||||
Profile a single call of a callable. Additional positional and keyword arguments
|
||||
may be passed along; the result of the call is returned, and exceptions are
|
||||
allowed to propagate cleanly, while ensuring that profiling is disabled on the
|
||||
way out.
|
||||
|
||||
|
||||
.. method:: Profile.runctx(cmd, globals, locals)
|
||||
|
||||
Profile an :func:`exec`\ -compatible string in a specific environment. The
|
||||
string is compiled before profiling begins.
|
||||
|
||||
|
||||
.. method:: Profile.start()
|
||||
|
||||
Start the profiler.
|
||||
|
||||
|
||||
.. method:: Profile.stop()
|
||||
|
||||
Stop the profiler.
|
||||
|
||||
|
||||
Using hotshot data
|
||||
------------------
|
||||
|
||||
.. module:: hotshot.stats
|
||||
:synopsis: Statistical analysis for Hotshot
|
||||
|
||||
|
||||
This module loads hotshot profiling data into the standard :mod:`pstats` Stats
|
||||
objects.
|
||||
|
||||
|
||||
.. function:: load(filename)
|
||||
|
||||
Load hotshot data from *filename*. Returns an instance of the
|
||||
:class:`pstats.Stats` class.
|
||||
|
||||
|
||||
.. seealso::
|
||||
|
||||
Module :mod:`profile`
|
||||
The :mod:`profile` module's :class:`Stats` class
|
||||
|
||||
|
||||
.. _hotshot-example:
|
||||
|
||||
Example Usage
|
||||
-------------
|
||||
|
||||
Note that this example runs the python "benchmark" pystones. It can take some
|
||||
time to run, and will produce large output files. ::
|
||||
|
||||
>>> import hotshot, hotshot.stats, test.pystone
|
||||
>>> prof = hotshot.Profile("stones.prof")
|
||||
>>> benchtime, stones = prof.runcall(test.pystone.pystones)
|
||||
>>> prof.close()
|
||||
>>> stats = hotshot.stats.load("stones.prof")
|
||||
>>> stats.strip_dirs()
|
||||
>>> stats.sort_stats('time', 'calls')
|
||||
>>> stats.print_stats(20)
|
||||
850004 function calls in 10.090 CPU seconds
|
||||
|
||||
Ordered by: internal time, call count
|
||||
|
||||
ncalls tottime percall cumtime percall filename:lineno(function)
|
||||
1 3.295 3.295 10.090 10.090 pystone.py:79(Proc0)
|
||||
150000 1.315 0.000 1.315 0.000 pystone.py:203(Proc7)
|
||||
50000 1.313 0.000 1.463 0.000 pystone.py:229(Func2)
|
||||
.
|
||||
.
|
||||
.
|
||||
|
@ -57,7 +57,7 @@ This profiler provides :dfn:`deterministic profiling` of any Python programs.
|
||||
It also provides a series of report generation tools to allow users to rapidly
|
||||
examine the results of a profile operation.
|
||||
|
||||
The Python standard library provides three different profilers:
|
||||
The Python standard library provides two different profilers:
|
||||
|
||||
#. :mod:`profile`, a pure Python module, described in the sequel. Copyright ©
|
||||
1994, by InfoSeek Corporation.
|
||||
@ -66,15 +66,11 @@ The Python standard library provides three different profilers:
|
||||
it suitable for profiling long-running programs. Based on :mod:`lsprof`,
|
||||
contributed by Brett Rosen and Ted Czotter.
|
||||
|
||||
#. :mod:`hotshot`, a C module focusing on minimizing the overhead while
|
||||
profiling, at the expense of long data post-processing times.
|
||||
|
||||
The :mod:`profile` and :mod:`cProfile` modules export the same interface, so
|
||||
they are mostly interchangeables; :mod:`cProfile` has a much lower overhead but
|
||||
is not so far as well-tested and might not be available on all systems.
|
||||
:mod:`cProfile` is really a compatibility layer on top of the internal
|
||||
:mod:`_lsprof` module. The :mod:`hotshot` module is reserved to specialized
|
||||
usages.
|
||||
:mod:`_lsprof` module.
|
||||
|
||||
.. % \section{How Is This Profiler Different From The Old Profiler?}
|
||||
.. % \nodename{Profiler Changes}
|
||||
|
@ -1,76 +0,0 @@
|
||||
"""High-perfomance logging profiler, mostly written in C."""
|
||||
|
||||
import _hotshot
|
||||
|
||||
from _hotshot import ProfilerError
|
||||
|
||||
|
||||
class Profile:
|
||||
def __init__(self, logfn, lineevents=0, linetimings=1):
|
||||
self.lineevents = lineevents and 1 or 0
|
||||
self.linetimings = (linetimings and lineevents) and 1 or 0
|
||||
self._prof = p = _hotshot.profiler(
|
||||
logfn, self.lineevents, self.linetimings)
|
||||
|
||||
# Attempt to avoid confusing results caused by the presence of
|
||||
# Python wrappers around these functions, but only if we can
|
||||
# be sure the methods have not been overridden or extended.
|
||||
if self.__class__ is Profile:
|
||||
self.close = p.close
|
||||
self.start = p.start
|
||||
self.stop = p.stop
|
||||
self.addinfo = p.addinfo
|
||||
|
||||
def close(self):
|
||||
"""Close the logfile and terminate the profiler."""
|
||||
self._prof.close()
|
||||
|
||||
def fileno(self):
|
||||
"""Return the file descriptor of the profiler's log file."""
|
||||
return self._prof.fileno()
|
||||
|
||||
def start(self):
|
||||
"""Start the profiler."""
|
||||
self._prof.start()
|
||||
|
||||
def stop(self):
|
||||
"""Stop the profiler."""
|
||||
self._prof.stop()
|
||||
|
||||
def addinfo(self, key, value):
|
||||
"""Add an arbitrary labelled value to the profile log."""
|
||||
self._prof.addinfo(key, value)
|
||||
|
||||
# These methods offer the same interface as the profile.Profile class,
|
||||
# but delegate most of the work to the C implementation underneath.
|
||||
|
||||
def run(self, cmd):
|
||||
"""Profile an exec-compatible string in the script
|
||||
environment.
|
||||
|
||||
The globals from the __main__ module are used as both the
|
||||
globals and locals for the script.
|
||||
"""
|
||||
import __main__
|
||||
dict = __main__.__dict__
|
||||
return self.runctx(cmd, dict, dict)
|
||||
|
||||
def runctx(self, cmd, globals, locals):
|
||||
"""Evaluate an exec-compatible string in a specific
|
||||
environment.
|
||||
|
||||
The string is compiled before profiling begins.
|
||||
"""
|
||||
code = compile(cmd, "<string>", "exec")
|
||||
self._prof.runcode(code, globals, locals)
|
||||
return self
|
||||
|
||||
def runcall(self, func, *args, **kw):
|
||||
"""Profile a single call of a callable.
|
||||
|
||||
Additional positional and keyword arguments may be passed
|
||||
along; the result of the call is returned, and exceptions are
|
||||
allowed to propogate cleanly, while ensuring that profiling is
|
||||
disabled on the way out.
|
||||
"""
|
||||
return self._prof.runcall(func, args, kw)
|
@ -1,192 +0,0 @@
|
||||
import _hotshot
|
||||
import os.path
|
||||
import parser
|
||||
import symbol
|
||||
import sys
|
||||
|
||||
from _hotshot import \
|
||||
WHAT_ENTER, \
|
||||
WHAT_EXIT, \
|
||||
WHAT_LINENO, \
|
||||
WHAT_DEFINE_FILE, \
|
||||
WHAT_DEFINE_FUNC, \
|
||||
WHAT_ADD_INFO
|
||||
|
||||
|
||||
__all__ = ["LogReader", "ENTER", "EXIT", "LINE"]
|
||||
|
||||
|
||||
ENTER = WHAT_ENTER
|
||||
EXIT = WHAT_EXIT
|
||||
LINE = WHAT_LINENO
|
||||
|
||||
|
||||
class LogReader:
|
||||
def __init__(self, logfn):
|
||||
# fileno -> filename
|
||||
self._filemap = {}
|
||||
# (fileno, lineno) -> filename, funcname
|
||||
self._funcmap = {}
|
||||
|
||||
self._reader = _hotshot.logreader(logfn)
|
||||
self._nextitem = self._reader.__next__
|
||||
self._info = self._reader.info
|
||||
if 'current-directory' in self._info:
|
||||
self.cwd = self._info['current-directory']
|
||||
else:
|
||||
self.cwd = None
|
||||
|
||||
# This mirrors the call stack of the profiled code as the log
|
||||
# is read back in. It contains tuples of the form:
|
||||
#
|
||||
# (file name, line number of function def, function name)
|
||||
#
|
||||
self._stack = []
|
||||
self._append = self._stack.append
|
||||
self._pop = self._stack.pop
|
||||
|
||||
def close(self):
|
||||
self._reader.close()
|
||||
|
||||
def fileno(self):
|
||||
"""Return the file descriptor of the log reader's log file."""
|
||||
return self._reader.fileno()
|
||||
|
||||
def addinfo(self, key, value):
|
||||
"""This method is called for each additional ADD_INFO record.
|
||||
|
||||
This can be overridden by applications that want to receive
|
||||
these events. The default implementation does not need to be
|
||||
called by alternate implementations.
|
||||
|
||||
The initial set of ADD_INFO records do not pass through this
|
||||
mechanism; this is only needed to receive notification when
|
||||
new values are added. Subclasses can inspect self._info after
|
||||
calling LogReader.__init__().
|
||||
"""
|
||||
pass
|
||||
|
||||
def get_filename(self, fileno):
|
||||
try:
|
||||
return self._filemap[fileno]
|
||||
except KeyError:
|
||||
raise ValueError("unknown fileno")
|
||||
|
||||
def get_filenames(self):
|
||||
return self._filemap.values()
|
||||
|
||||
def get_fileno(self, filename):
|
||||
filename = os.path.normcase(os.path.normpath(filename))
|
||||
for fileno, name in self._filemap.items():
|
||||
if name == filename:
|
||||
return fileno
|
||||
raise ValueError("unknown filename")
|
||||
|
||||
def get_funcname(self, fileno, lineno):
|
||||
try:
|
||||
return self._funcmap[(fileno, lineno)]
|
||||
except KeyError:
|
||||
raise ValueError("unknown function location")
|
||||
|
||||
# Iteration support:
|
||||
# This adds an optional (& ignored) parameter to next() so that the
|
||||
# same bound method can be used as the __getitem__() method -- this
|
||||
# avoids using an additional method call which kills the performance.
|
||||
|
||||
def __next__(self, index=0):
|
||||
while 1:
|
||||
# This call may raise StopIteration:
|
||||
what, tdelta, fileno, lineno = self._nextitem()
|
||||
|
||||
# handle the most common cases first
|
||||
|
||||
if what == WHAT_ENTER:
|
||||
filename, funcname = self._decode_location(fileno, lineno)
|
||||
t = (filename, lineno, funcname)
|
||||
self._append(t)
|
||||
return what, t, tdelta
|
||||
|
||||
if what == WHAT_EXIT:
|
||||
return what, self._pop(), tdelta
|
||||
|
||||
if what == WHAT_LINENO:
|
||||
filename, firstlineno, funcname = self._stack[-1]
|
||||
return what, (filename, lineno, funcname), tdelta
|
||||
|
||||
if what == WHAT_DEFINE_FILE:
|
||||
filename = os.path.normcase(os.path.normpath(tdelta))
|
||||
self._filemap[fileno] = filename
|
||||
elif what == WHAT_DEFINE_FUNC:
|
||||
filename = self._filemap[fileno]
|
||||
self._funcmap[(fileno, lineno)] = (filename, tdelta)
|
||||
elif what == WHAT_ADD_INFO:
|
||||
# value already loaded into self.info; call the
|
||||
# overridable addinfo() handler so higher-level code
|
||||
# can pick up the new value
|
||||
if tdelta == 'current-directory':
|
||||
self.cwd = lineno
|
||||
self.addinfo(tdelta, lineno)
|
||||
else:
|
||||
raise ValueError("unknown event type")
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
#
|
||||
# helpers
|
||||
#
|
||||
|
||||
def _decode_location(self, fileno, lineno):
|
||||
try:
|
||||
return self._funcmap[(fileno, lineno)]
|
||||
except KeyError:
|
||||
#
|
||||
# This should only be needed when the log file does not
|
||||
# contain all the DEFINE_FUNC records needed to allow the
|
||||
# function name to be retrieved from the log file.
|
||||
#
|
||||
if self._loadfile(fileno):
|
||||
filename = funcname = None
|
||||
try:
|
||||
filename, funcname = self._funcmap[(fileno, lineno)]
|
||||
except KeyError:
|
||||
filename = self._filemap.get(fileno)
|
||||
funcname = None
|
||||
self._funcmap[(fileno, lineno)] = (filename, funcname)
|
||||
return filename, funcname
|
||||
|
||||
def _loadfile(self, fileno):
|
||||
try:
|
||||
filename = self._filemap[fileno]
|
||||
except KeyError:
|
||||
print("Could not identify fileId", fileno)
|
||||
return 1
|
||||
if filename is None:
|
||||
return 1
|
||||
absname = os.path.normcase(os.path.join(self.cwd, filename))
|
||||
|
||||
try:
|
||||
fp = open(absname)
|
||||
except IOError:
|
||||
return
|
||||
st = parser.suite(fp.read())
|
||||
fp.close()
|
||||
|
||||
# Scan the tree looking for def and lambda nodes, filling in
|
||||
# self._funcmap with all the available information.
|
||||
funcdef = symbol.funcdef
|
||||
lambdef = symbol.lambdef
|
||||
|
||||
stack = [st.totuple(1)]
|
||||
|
||||
while stack:
|
||||
tree = stack.pop()
|
||||
try:
|
||||
sym = tree[0]
|
||||
except (IndexError, TypeError):
|
||||
continue
|
||||
if sym == funcdef:
|
||||
self._funcmap[(fileno, tree[2][2])] = filename, tree[2][1]
|
||||
elif sym == lambdef:
|
||||
self._funcmap[(fileno, tree[1][2])] = filename, "<lambda>"
|
||||
stack.extend(list(tree[1:]))
|
@ -1,93 +0,0 @@
|
||||
"""Statistics analyzer for HotShot."""
|
||||
|
||||
import profile
|
||||
import pstats
|
||||
|
||||
import hotshot.log
|
||||
|
||||
from hotshot.log import ENTER, EXIT
|
||||
|
||||
|
||||
def load(filename):
|
||||
return StatsLoader(filename).load()
|
||||
|
||||
|
||||
class StatsLoader:
|
||||
def __init__(self, logfn):
|
||||
self._logfn = logfn
|
||||
self._code = {}
|
||||
self._stack = []
|
||||
self.pop_frame = self._stack.pop
|
||||
|
||||
def load(self):
|
||||
# The timer selected by the profiler should never be used, so make
|
||||
# sure it doesn't work:
|
||||
p = Profile()
|
||||
p.get_time = _brokentimer
|
||||
log = hotshot.log.LogReader(self._logfn)
|
||||
taccum = 0
|
||||
for event in log:
|
||||
what, (filename, lineno, funcname), tdelta = event
|
||||
if tdelta > 0:
|
||||
taccum += tdelta
|
||||
|
||||
# We multiply taccum to convert from the microseconds we
|
||||
# have to the seconds that the profile/pstats module work
|
||||
# with; this allows the numbers to have some basis in
|
||||
# reality (ignoring calibration issues for now).
|
||||
|
||||
if what == ENTER:
|
||||
frame = self.new_frame(filename, lineno, funcname)
|
||||
p.trace_dispatch_call(frame, taccum * .000001)
|
||||
taccum = 0
|
||||
|
||||
elif what == EXIT:
|
||||
frame = self.pop_frame()
|
||||
p.trace_dispatch_return(frame, taccum * .000001)
|
||||
taccum = 0
|
||||
|
||||
# no further work for line events
|
||||
|
||||
assert not self._stack
|
||||
return pstats.Stats(p)
|
||||
|
||||
def new_frame(self, *args):
|
||||
# args must be filename, firstlineno, funcname
|
||||
# our code objects are cached since we don't need to create
|
||||
# new ones every time
|
||||
try:
|
||||
code = self._code[args]
|
||||
except KeyError:
|
||||
code = FakeCode(*args)
|
||||
self._code[args] = code
|
||||
# frame objects are create fresh, since the back pointer will
|
||||
# vary considerably
|
||||
if self._stack:
|
||||
back = self._stack[-1]
|
||||
else:
|
||||
back = None
|
||||
frame = FakeFrame(code, back)
|
||||
self._stack.append(frame)
|
||||
return frame
|
||||
|
||||
|
||||
class Profile(profile.Profile):
|
||||
def simulate_cmd_complete(self):
|
||||
pass
|
||||
|
||||
|
||||
class FakeCode:
|
||||
def __init__(self, filename, firstlineno, funcname):
|
||||
self.co_filename = filename
|
||||
self.co_firstlineno = firstlineno
|
||||
self.co_name = self.__name__ = funcname
|
||||
|
||||
|
||||
class FakeFrame:
|
||||
def __init__(self, code, back):
|
||||
self.f_back = back
|
||||
self.f_code = code
|
||||
|
||||
|
||||
def _brokentimer():
|
||||
raise RuntimeError("this timer should not be called")
|
@ -1,31 +0,0 @@
|
||||
import errno
|
||||
import hotshot
|
||||
import hotshot.stats
|
||||
import os
|
||||
import sys
|
||||
import test.pystone
|
||||
|
||||
def main(logfile):
|
||||
p = hotshot.Profile(logfile)
|
||||
benchtime, stones = p.runcall(test.pystone.pystones)
|
||||
p.close()
|
||||
|
||||
print("Pystone(%s) time for %d passes = %g" % \
|
||||
(test.pystone.__version__, test.pystone.LOOPS, benchtime))
|
||||
print("This machine benchmarks at %g pystones/second" % stones)
|
||||
|
||||
stats = hotshot.stats.load(logfile)
|
||||
stats.strip_dirs()
|
||||
stats.sort_stats('time', 'calls')
|
||||
try:
|
||||
stats.print_stats(20)
|
||||
except IOError as e:
|
||||
if e.errno != errno.EPIPE:
|
||||
raise
|
||||
|
||||
if __name__ == '__main__':
|
||||
if sys.argv[1:]:
|
||||
main(sys.argv[1])
|
||||
else:
|
||||
import tempfile
|
||||
main(tempfile.NamedTemporaryFile().name)
|
@ -1,132 +0,0 @@
|
||||
import hotshot
|
||||
import hotshot.log
|
||||
import os
|
||||
import pprint
|
||||
import unittest
|
||||
|
||||
from test import test_support
|
||||
|
||||
from hotshot.log import ENTER, EXIT, LINE
|
||||
|
||||
|
||||
def shortfilename(fn):
|
||||
# We use a really shortened filename since an exact match is made,
|
||||
# and the source may be either a Python source file or a
|
||||
# pre-compiled bytecode file.
|
||||
if fn:
|
||||
return os.path.splitext(os.path.basename(fn))[0]
|
||||
else:
|
||||
return fn
|
||||
|
||||
|
||||
class UnlinkingLogReader(hotshot.log.LogReader):
|
||||
"""Extend the LogReader so the log file is unlinked when we're
|
||||
done with it."""
|
||||
|
||||
def __init__(self, logfn):
|
||||
self.__logfn = logfn
|
||||
hotshot.log.LogReader.__init__(self, logfn)
|
||||
|
||||
def next(self, index=None):
|
||||
try:
|
||||
return hotshot.log.LogReader.next(self)
|
||||
except StopIteration:
|
||||
self.close()
|
||||
os.unlink(self.__logfn)
|
||||
raise
|
||||
|
||||
|
||||
class HotShotTestCase(unittest.TestCase):
|
||||
def new_profiler(self, lineevents=0, linetimings=1):
|
||||
self.logfn = test_support.TESTFN
|
||||
return hotshot.Profile(self.logfn, lineevents, linetimings)
|
||||
|
||||
def get_logreader(self):
|
||||
return UnlinkingLogReader(self.logfn)
|
||||
|
||||
def get_events_wotime(self):
|
||||
L = []
|
||||
for event in self.get_logreader():
|
||||
what, (filename, lineno, funcname), tdelta = event
|
||||
L.append((what, (shortfilename(filename), lineno, funcname)))
|
||||
return L
|
||||
|
||||
def check_events(self, expected):
|
||||
events = self.get_events_wotime()
|
||||
if events != expected:
|
||||
self.fail(
|
||||
"events did not match expectation; got:\n%s\nexpected:\n%s"
|
||||
% (pprint.pformat(events), pprint.pformat(expected)))
|
||||
|
||||
def run_test(self, callable, events, profiler=None):
|
||||
if profiler is None:
|
||||
profiler = self.new_profiler()
|
||||
self.failUnless(not profiler._prof.closed)
|
||||
profiler.runcall(callable)
|
||||
self.failUnless(not profiler._prof.closed)
|
||||
profiler.close()
|
||||
self.failUnless(profiler._prof.closed)
|
||||
self.check_events(events)
|
||||
|
||||
def test_addinfo(self):
|
||||
def f(p):
|
||||
p.addinfo("test-key", "test-value")
|
||||
profiler = self.new_profiler()
|
||||
profiler.runcall(f, profiler)
|
||||
profiler.close()
|
||||
log = self.get_logreader()
|
||||
info = log._info
|
||||
list(log)
|
||||
self.assertEqual(info["test-key"], ["test-value"])
|
||||
|
||||
def test_line_numbers(self):
|
||||
def f():
|
||||
y = 2
|
||||
x = 1
|
||||
def g():
|
||||
f()
|
||||
f_lineno = f.__code__.co_firstlineno
|
||||
g_lineno = g.__code__.co_firstlineno
|
||||
events = [(ENTER, ("test_hotshot", g_lineno, "g")),
|
||||
(LINE, ("test_hotshot", g_lineno+1, "g")),
|
||||
(ENTER, ("test_hotshot", f_lineno, "f")),
|
||||
(LINE, ("test_hotshot", f_lineno+1, "f")),
|
||||
(LINE, ("test_hotshot", f_lineno+2, "f")),
|
||||
(EXIT, ("test_hotshot", f_lineno, "f")),
|
||||
(EXIT, ("test_hotshot", g_lineno, "g")),
|
||||
]
|
||||
self.run_test(g, events, self.new_profiler(lineevents=1))
|
||||
|
||||
def test_start_stop(self):
|
||||
# Make sure we don't return NULL in the start() and stop()
|
||||
# methods when there isn't an error. Bug in 2.2 noted by
|
||||
# Anthony Baxter.
|
||||
profiler = self.new_profiler()
|
||||
profiler.start()
|
||||
profiler.stop()
|
||||
profiler.close()
|
||||
os.unlink(self.logfn)
|
||||
|
||||
def test_bad_sys_path(self):
|
||||
import sys
|
||||
import os
|
||||
orig_path = sys.path
|
||||
coverage = hotshot._hotshot.coverage
|
||||
try:
|
||||
# verify we require a list for sys.path
|
||||
sys.path = 'abc'
|
||||
self.assertRaises(RuntimeError, coverage, test_support.TESTFN)
|
||||
# verify that we require sys.path exists
|
||||
del sys.path
|
||||
self.assertRaises(RuntimeError, coverage, test_support.TESTFN)
|
||||
finally:
|
||||
sys.path = orig_path
|
||||
if os.path.exists(test_support.TESTFN):
|
||||
os.remove(test_support.TESTFN)
|
||||
|
||||
def test_main():
|
||||
test_support.run_unittest(HotShotTestCase)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_main()
|
@ -737,7 +737,7 @@ PLATMACDIRS= plat-mac plat-mac/Carbon plat-mac/lib-scriptpackages \
|
||||
PLATMACPATH=:plat-mac:plat-mac/lib-scriptpackages
|
||||
LIBSUBDIRS= lib-tk site-packages test test/output test/data \
|
||||
test/decimaltestdata \
|
||||
encodings hotshot \
|
||||
encodings \
|
||||
email email/mime email/test email/test/data \
|
||||
sqlite3 sqlite3/test \
|
||||
logging bsddb bsddb/test csv wsgiref \
|
||||
|
10
Misc/NEWS
10
Misc/NEWS
@ -4,6 +4,16 @@ Python News
|
||||
|
||||
(editors: check NEWS.help for information about editing NEWS using ReST.)
|
||||
|
||||
What's New in Python 3.0a2?
|
||||
|
||||
*Unreleased*
|
||||
|
||||
Extension Modules
|
||||
-----------------
|
||||
|
||||
- The `hotshot` profiler has been removed; use `cProfile` instead.
|
||||
|
||||
|
||||
What's New in Python 3.0a1?
|
||||
==========================
|
||||
|
||||
|
1645
Modules/_hotshot.c
1645
Modules/_hotshot.c
File diff suppressed because it is too large
Load Diff
@ -1,60 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: iso-8859-1 -*-
|
||||
|
||||
"""
|
||||
Run a Python script under hotshot's control.
|
||||
|
||||
Adapted from a posting on python-dev by Walter Dörwald
|
||||
|
||||
usage %prog [ %prog args ] filename [ filename args ]
|
||||
|
||||
Any arguments after the filename are used as sys.argv for the filename.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import optparse
|
||||
import os
|
||||
import hotshot
|
||||
import hotshot.stats
|
||||
|
||||
PROFILE = "hotshot.prof"
|
||||
|
||||
def run_hotshot(filename, profile, args):
|
||||
prof = hotshot.Profile(profile)
|
||||
sys.path.insert(0, os.path.dirname(filename))
|
||||
sys.argv = [filename] + args
|
||||
fp = open(filename)
|
||||
try:
|
||||
script = fp.read()
|
||||
finally:
|
||||
fp.close()
|
||||
prof.run("exec(%r)" % script)
|
||||
prof.close()
|
||||
stats = hotshot.stats.load(profile)
|
||||
stats.sort_stats("time", "calls")
|
||||
|
||||
# print_stats uses unadorned print statements, so the only way
|
||||
# to force output to stderr is to reassign sys.stdout temporarily
|
||||
save_stdout = sys.stdout
|
||||
sys.stdout = sys.stderr
|
||||
stats.print_stats()
|
||||
sys.stdout = save_stdout
|
||||
|
||||
return 0
|
||||
|
||||
def main(args):
|
||||
parser = optparse.OptionParser(__doc__)
|
||||
parser.disable_interspersed_args()
|
||||
parser.add_option("-p", "--profile", action="store", default=PROFILE,
|
||||
dest="profile", help='Specify profile file to use')
|
||||
(options, args) = parser.parse_args(args)
|
||||
|
||||
if len(args) == 0:
|
||||
parser.print_help("missing script to execute")
|
||||
return 1
|
||||
|
||||
filename = args[0]
|
||||
return run_hotshot(filename, options.profile, args[1:])
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv[1:]))
|
3
setup.py
3
setup.py
@ -413,8 +413,7 @@ class PyBuildExt(build_ext):
|
||||
exts.append( Extension("atexit", ["atexitmodule.c"]) )
|
||||
# Python C API test module
|
||||
exts.append( Extension('_testcapi', ['_testcapimodule.c']) )
|
||||
# profilers (_lsprof is for cProfile.py)
|
||||
exts.append( Extension('_hotshot', ['_hotshot.c']) )
|
||||
# profiler (_lsprof is for cProfile.py)
|
||||
exts.append( Extension('_lsprof', ['_lsprof.c', 'rotatingtree.c']) )
|
||||
# static Unicode character database
|
||||
exts.append( Extension('unicodedata', ['unicodedata.c']) )
|
||||
|
Loading…
Reference in New Issue
Block a user