Commit c0447b1f authored by Hanno Schlichting's avatar Hanno Schlichting

Remove profiling support via `publisher-profile-file` directive.

parent d157aa80
...@@ -21,6 +21,8 @@ Features Added ...@@ -21,6 +21,8 @@ Features Added
Restructuring Restructuring
+++++++++++++ +++++++++++++
- Remove profiling support via `publisher-profile-file` directive.
- Create new `Products.Sessions` distribution including Products.Sessions - Create new `Products.Sessions` distribution including Products.Sessions
and Products.Transience code. and Products.Transience code.
......
...@@ -139,10 +139,9 @@ class DebugManager(Item, Implicit): ...@@ -139,10 +139,9 @@ class DebugManager(Item, Implicit):
name = title = 'Debug Information' name = title = 'Debug Information'
meta_type = name meta_type = name
manage_options=(( manage_options = ((
{'label':'Debugging Info', 'action':'manage_main'}, {'label': 'Debugging Info', 'action': 'manage_main'},
{'label':'Profiling', 'action':'manage_profile'}, ))
))
manage_debug = DTMLFile('dtml/debug', globals()) manage_debug = DTMLFile('dtml/debug', globals())
...@@ -211,32 +210,6 @@ class DebugManager(Item, Implicit): ...@@ -211,32 +210,6 @@ class DebugManager(Item, Implicit):
import Zope2 # for data import Zope2 # for data
return Zope2.DB.connectionDebugInfo() return Zope2.DB.connectionDebugInfo()
# Profiling support
manage_profile = DTMLFile('dtml/profile', globals())
def manage_profile_stats(self, sort='time',
limit=200, stripDirs=1, mode='stats'):
"""Return profile data if available
"""
stats = getattr(sys, '_ps_', None)
if stats is None:
return None
if stripDirs:
from copy import copy
stats = copy(stats)
stats.strip_dirs()
stats.sort_stats(sort)
stats.stream = output = StringIO()
getattr(stats, 'print_%s' % mode)(limit)
return output.getvalue()
def manage_profile_reset(self):
""" Reset profile data
"""
Publish._pstat = sys._ps_ = None
def manage_getSysPath(self): def manage_getSysPath(self):
return list(sys.path) return list(sys.path)
......
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<dtml-if "REQUEST.get('reset')">
<dtml-call "manage_profile_reset()">
<br />
<div class="form-text">
Profiling data was reset.
</div>
<dtml-else>
<dtml-let sort="REQUEST.get('sort', 'time')"
limit="REQUEST.get('limit', 100)"
mode="REQUEST.get('mode', 'stats')"
stripDirs="REQUEST.get('stripDirs', 1)"
stats="manage_profile_stats(sort, limit, stripDirs, mode)">
<dtml-if stats>
<p class="form-help">
Profiling information is generated using the standard Python
profiler. To learn how to interpret the profiler statistics,
see the <a href="http://www.python.org/doc/current/lib/module-profile.html">
Python profiler documentation</a>.
</p>
<br />
<form action="&dtml-URL;" method="POST">
<table>
<tr>
<td><strong>Sort</strong>:
<select name="sort">
<dtml-in "('time', 'cumulative', 'calls', 'pcalls',
'name', 'file', 'module', 'line',
'nfl', 'stdname')">
<option value="&dtml-sequence-item;"<dtml-if
"sort==_['sequence-item']"> selected</dtml-if>>&dtml-sequence-item;
</dtml-in>
</select>
</td>
<td><strong>Limit</strong>:
<select name="limit:int">
<dtml-in "(100, 200, 300, 400, 500)">
<option value="&dtml-sequence-item;"<dtml-if
"limit==_['sequence-item']"> selected</dtml-if>>&dtml-sequence-item;
</dtml-in>
</select>
</td>
<td><strong>strip Dirs</strong>:
<input type=hidden name="stripDirs:int:default" value="0">
<input type=checkbox name="stripDirs:int" value="1" <dtml-if stripDirs>checked</dtml-if>>
</td>
<td><strong>Mode</strong>:
<select name="mode">
<dtml-in "('stats', 'callees', 'callers',)">
<option value="<dtml-var sequence-item>"<dtml-if
"mode==_['sequence-item']"> selected</dtml-if>><dtml-var
sequence-item>
</dtml-in>
</select>
</td>
<td>
<input type="submit" name="submit" value="Update">
&nbsp;
<input type="submit" name="reset" value="Reset data">
</td>
</tr>
</table>
</form>
<hr>
<pre>
&dtml-stats;
</pre>
<dtml-else>
<br />
<div class="form-text">
Profiling is not currently enabled or there is not yet any profiling
data to report. To enable profiling, restart the Zope process with
the configuration setting 'publisher-profile-file' defined. The value
of this variable should be the full system path to a file that will be
used to dump a profile report when the process restarts or exits.
</div>
</dtml-if>
</dtml-let>
</dtml-if>
<dtml-var manage_page_footer>
...@@ -235,25 +235,7 @@ class DebugManagerTests(unittest.TestCase): ...@@ -235,25 +235,7 @@ class DebugManagerTests(unittest.TestCase):
self.assertTrue('pc' in mapping) self.assertTrue('pc' in mapping)
self.assertEqual(mapping['delta'], mapping['rc'] - mapping['pc']) self.assertEqual(mapping['delta'], mapping['rc'] - mapping['pc'])
#def test_dbconnections(self): XXX -- TOO UGLY TO TEST # def test_dbconnections(self): XXX -- TOO UGLY TO TEST
#def test_manage_profile_stats(self): XXX -- TOO UGLY TO TEST
def test_manage_profile_reset(self):
import sys
from ZPublisher import Publish
_old_sys__ps_ = getattr(sys, '_ps_', self)
_old_Publish_pstat = getattr(Publish, '_pstat', self)
sys._ps_ = Publish._pstat = object()
try:
dm = self._makeOne('test')
dm.manage_profile_reset()
finally:
if _old_sys__ps_ is not self:
sys._ps_ = _old_sys__ps_
if _old_Publish_pstat is not self:
Publish._pstat = _old_Publish_pstat
self.assertTrue(sys._ps_ is None)
self.assertTrue(Publish._pstat is None)
def test_manage_getSysPath(self): def test_manage_getSysPath(self):
import sys import sys
......
...@@ -380,82 +380,10 @@ class DefaultTransactionsManager: ...@@ -380,82 +380,10 @@ class DefaultTransactionsManager:
if auth_user is not None: if auth_user is not None:
T.setUser(auth_user, request_get('AUTHENTICATION_PATH')) T.setUser(auth_user, request_get('AUTHENTICATION_PATH'))
# profiling support
_pfile = None # profiling filename
_plock=allocate_lock() # profiling lock
_pfunc=publish_module_standard
_pstat=None
def install_profiling(filename):
global _pfile
_pfile = filename
def pm(module_name, stdin, stdout, stderr,
environ, debug, request, response):
try:
r=_pfunc(module_name, stdin=stdin, stdout=stdout,
stderr=stderr, environ=environ, debug=debug,
request=request, response=response)
except: r=None
sys._pr_=r
def publish_module_profiled(module_name, stdin=sys.stdin, stdout=sys.stdout,
stderr=sys.stderr, environ=os.environ, debug=0,
request=None, response=None):
try:
import cProfile as profile
profile # pyflakes
except ImportError:
import profile
import pstats
global _pstat
_plock.acquire()
try:
if request is not None:
path_info=request.get('PATH_INFO')
else: path_info=environ.get('PATH_INFO')
if path_info[-14:]=='manage_profile':
return _pfunc(module_name, stdin=stdin, stdout=stdout,
stderr=stderr, environ=environ, debug=debug,
request=request, response=response)
pobj=profile.Profile()
pobj.runcall(pm, module_name, stdin, stdout, stderr,
environ, debug, request, response)
result=sys._pr_
pobj.create_stats()
if _pstat is None:
_pstat = sys._ps_ = pstats.Stats(pobj)
else: _pstat.add(pobj)
finally:
_plock.release()
if result is None:
try:
error=sys.exc_info()
file=open(_pfile, 'w')
file.write(
"See the url "
"http://www.python.org/doc/current/lib/module-profile.html"
"\n for information on interpreting profiler statistics.\n\n"
)
sys.stdout=file
_pstat.strip_dirs().sort_stats('cumulative').print_stats(250)
_pstat.strip_dirs().sort_stats('time').print_stats(250)
file.flush()
file.close()
except: pass
raise error[0], error[1], error[2]
return result
def publish_module(module_name, def publish_module(module_name,
stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr, stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr,
environ=os.environ, debug=0, request=None, response=None): environ=os.environ, debug=0, request=None, response=None):
""" publish a Python module, with or without profiling enabled """ """ publish a Python module, with or without profiling enabled """
if _pfile: # profiling is enabled return publish_module_standard(module_name, stdin, stdout, stderr,
return publish_module_profiled(module_name, stdin, stdout, stderr, environ, debug, request, response)
environ, debug, request, response)
else:
return publish_module_standard(module_name, stdin, stdout, stderr,
environ, debug, request, response)
This diff is collapsed.
...@@ -818,13 +818,4 @@ if __name__ == '__main__': ...@@ -818,13 +818,4 @@ if __name__ == '__main__':
cs = chat_server.chat_server ('', 7777) cs = chat_server.chat_server ('', 7777)
sh = status_handler.status_extension([hs,ms,ftp,cs,rs]) sh = status_handler.status_extension([hs,ms,ftp,cs,rs])
hs.install_handler (sh) hs.install_handler (sh)
if ('-p' in sys.argv): asyncore.loop()
def profile_loop ():
try:
asyncore.loop()
except KeyboardInterrupt:
pass
import profile
profile.run ('profile_loop()', 'profile.out')
else:
asyncore.loop()
#!/usr/bin/env python
# -*- Mode: Python; tab-width: 4 -*-
import asyncore
import socket
import string
import sys
def blurt (thing):
sys.stdout.write (thing)
sys.stdout.flush ()
total_sessions = 0
class http_client (asyncore.dispatcher_with_send):
def __init__ (self, host='127.0.0.1', port=80, uri='/', num=10):
asyncore.dispatcher_with_send.__init__ (self)
self.create_socket (socket.AF_INET, socket.SOCK_STREAM)
self.host = host
self.port = port
self.uri = uri
self.num = num
self.bytes = 0
self.connect ((host, port))
def log (self, *info):
pass
def handle_connect (self):
self.connected = 1
# blurt ('o')
self.send ('GET %s HTTP/1.0\r\n\r\n' % self.uri)
def handle_read (self):
# blurt ('.')
d = self.recv (8192)
self.bytes = self.bytes + len(d)
def handle_close (self):
global total_sessions
# blurt ('(%d)' % (self.bytes))
self.close()
total_sessions = total_sessions + 1
if self.num:
http_client (self.host, self.port, self.uri, self.num-1)
import time
class timer:
def __init__ (self):
self.start = time.time()
def end (self):
return time.time() - self.start
from asyncore import socket_map, poll
MAX = 0
def loop (timeout=30.0):
global MAX
while socket_map:
if len(socket_map) > MAX:
MAX = len(socket_map)
poll (timeout)
if __name__ == '__main__':
if len(sys.argv) < 6:
print 'usage: %s <host> <port> <uri> <hits> <num_clients>' % sys.argv[0]
else:
[host, port, uri, hits, num] = sys.argv[1:]
hits = string.atoi (hits)
num = string.atoi (num)
port = string.atoi (port)
t = timer()
clients = map (lambda x: http_client (host, port, uri, hits-1), range(num))
#import profile
#profile.run ('loop')
loop()
total_time = t.end()
print (
'\n%d clients\n%d hits/client\n'
'total_hits:%d\n%.3f seconds\ntotal hits/sec:%.3f' % (
num,
hits,
total_sessions,
total_time,
total_sessions / total_time
)
)
print 'Max. number of concurrent sessions: %d' % (MAX)
# linux 2.x, talking to medusa
# 50 clients
# 1000 hits/client
# total_hits:50000
# 2255.858 seconds
# total hits/sec:22.165
# Max. number of concurrent sessions: 50
...@@ -136,9 +136,6 @@ class ZopeStarter: ...@@ -136,9 +136,6 @@ class ZopeStarter:
ZPublisher.Publish.set_default_debug_mode(self.cfg.debug_mode) ZPublisher.Publish.set_default_debug_mode(self.cfg.debug_mode)
ZPublisher.Publish.set_default_authentication_realm( ZPublisher.Publish.set_default_authentication_realm(
self.cfg.http_realm) self.cfg.http_realm)
if self.cfg.publisher_profile_file:
filename = self.cfg.publisher_profile_file
ZPublisher.Publish.install_profiling(filename)
if self.cfg.trusted_proxies: if self.cfg.trusted_proxies:
# DM 2004-11-24: added host name mapping (such that examples in conf file really have a chance to work # DM 2004-11-24: added host name mapping (such that examples in conf file really have a chance to work
mapped = [] mapped = []
......
...@@ -57,12 +57,6 @@ def large_file_threshold(value): ...@@ -57,12 +57,6 @@ def large_file_threshold(value):
import ZServer import ZServer
ZServer.LARGE_FILE_THRESHOLD = value ZServer.LARGE_FILE_THRESHOLD = value
def publisher_profile_file(value):
value is not None and _setenv('PROFILE_PUBLISHER', value)
from ZPublisher.Publish import install_profiling
install_profiling(value)
return value
def http_realm(value): def http_realm(value):
value is not None and _setenv('Z_REALM', value) value is not None and _setenv('Z_REALM', value)
return value return value
......
...@@ -456,15 +456,6 @@ ...@@ -456,15 +456,6 @@
</description> </description>
</key> </key>
<key name="publisher-profile-file">
<description>
Causing this directive to point to a file on the filesystem will
cause Zope's profiling capabilities to be enabled. For more
information, see the Debug/Profiling tab of Zope's Control_Panel.
</description>
<metadefault>unset</metadefault>
</key>
<section type="cgi-environment" attribute="cgi_environment" name="*"> <section type="cgi-environment" attribute="cgi_environment" name="*">
<description> <description>
A section which allows a user to define arbitrary key-value pairs for A section which allows a user to define arbitrary key-value pairs for
......
This diff is collapsed.
...@@ -275,23 +275,6 @@ instancehome $INSTANCE ...@@ -275,23 +275,6 @@ instancehome $INSTANCE
# trusted-proxy www.example.com # trusted-proxy www.example.com
# trusted-proxy 192.168.1.1 # trusted-proxy 192.168.1.1
# Directive: publisher-profile-file
#
# Description:
# Names a file on the filesystem which causes Zope's Python
# profiling capabilities to be enabled. For more information, see
# the Debug Information - > Profiling tab of Zope's Control_Panel
# via the Zope Management Interface. IMPORTANT: setting this
# filename will cause Zope code to be executed much more slowly
# than normal. This should not be enabled in production.
#
# Default: unset
#
# Example:
#
# publisher-profile-file $INSTANCE/var/profile.dat
# Directive: security-policy-implementation # Directive: security-policy-implementation
# #
# Description: # Description:
......
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