Updated to r11 of app_profiler
authorSverre Rabbelier <srabbelier@gmail.com>
Wed, 02 Sep 2009 20:42:23 +0200
changeset 2857 bc793800116e
parent 2856 f446e019825c
child 2858 9b59d89e6707
Updated to r11 of app_profiler
app/app_profiler/app_profiler.py
app/app_profiler/ppstats.py
--- a/app/app_profiler/app_profiler.py	Wed Sep 02 10:52:04 2009 +0200
+++ b/app/app_profiler/app_profiler.py	Wed Sep 02 20:42:23 2009 +0200
@@ -5,10 +5,14 @@
 from google.appengine.api import memcache
 import google.appengine.ext.webapp.util
 
-import os.path
+from email.MIMEMultipart import MIMEMultipart
+from email.Message import Message
+
+import httplib
 import logging
+import os.path
+import random
 import re
-import random
 import string
 import zlib
 
@@ -107,6 +111,11 @@
 
     return global_profiler
     
+def new_global_profiler():
+    global global_profiler
+    global_profiler = GAEProfiler()
+    return global_profiler
+
 def cache_key_for_profile(profile_key):
     "generate a memcache key"
     return "ProfileData.%s" % profile_key
@@ -123,12 +132,30 @@
     "get pstats for a key, or the global pstats"
     key = request_obj.get('key', '')
     if key:
-        return load_pstats_from_memcache(key)
+        gp = GAEProfiler()
+        gp.profile_obj = load_pstats_from_memcache(key)
+        gp.profile_key = key
+        return gp
     else:
         gp = get_global_profiler()
         if not gp.has_profiler():
             return None
-        return gp.get_pstats()
+        return gp
+
+def mime_upload_data_as_file(field_name, filename, body):
+    part = Message()
+    part['Content-Disposition'] = 'form-data; name="%s"; filename="%s"' % (field_name, filename)
+    part['Content-Transfer-Encoding'] = 'binary'
+    part['Content-Type'] = 'application/octet-stream'
+    part['Content-Length'] = str(len(body))
+    part.set_payload(body)
+    return part
+
+def mime_form_value(name, value):
+    part = Message()
+    part['Content-Disposition'] = 'form-data; name="%s"' % name
+    part.set_payload(value)
+    return part
 
 class show_profile(webapp.RequestHandler):
     def get(self):
@@ -137,11 +164,11 @@
             self.response.out.write("<body><html><h3>No profiler.</h3><html></body>")
             return
 
-        ps.set_output(self.response.out)
+        ps.profile_obj.set_output(self.response.out)
         sort = self.request.get('sort', 'time')
-        ps.sort_stats(sort)
+        ps.profile_obj.sort_stats(sort)
         self.response.out.write("<body><html><pre>\n")
-        ps.print_stats(30)
+        ps.profile_obj.print_stats(30)
         self.response.out.write("</pre></html></body>")
 
 class download_profile_data(webapp.RequestHandler):
@@ -151,12 +178,44 @@
             self.response.out.write("<body><html><h3>No profiler.</h3><html></body>")
             return            
 
-        output = ps.dump_stats_pickle()
+        output = ps.profile_obj.dump_stats_pickle()
 
         self.response.headers['Content-Type'] = 'application/octet-stream'
 
         self.response.out.write(output)
 
+class send_profile_data(webapp.RequestHandler):
+    def get(self):
+        ps = get_stats_from_global_or_request(self.request)
+        if not ps:
+            self.response.out.write("<body><html><h3>No profiler.</h3><html></body>")
+            return            
+
+        dest = self.request.get('dest', '')
+        if not dest:
+            self.response.out.write("<body><html>No destination</html></body>")
+
+        upload_form = MIMEMultipart('form-data')
+
+        upload_filename =  'profile.%s.pstats' % ps.profile_key
+        upload_field_name = 'profile_file'
+
+        upload_form.attach(mime_upload_data_as_file('profile_file', upload_field_name, zlib.compress(ps.profile_obj.dump_stats_pickle())))
+        upload_form.attach(mime_form_value('key_only', '1'))
+
+        http_conn = httplib.HTTPConnection(dest)
+        http_conn.connect()
+        http_conn.request('POST', '/upload_profile', upload_form.as_string(), 
+                          {'Content-Type': 'multipart/form-data; boundary=%s' % upload_form.get_boundary()})
+
+        http_resp = http_conn.getresponse()
+        remote_data = http_resp.read()
+        if http_resp.status == 200:
+            remote_url = "http://%s/view_profile?key=%s" % (dest, remote_data)
+            self.response.out.write("<html><body>Success! <a href='%s'>%s</a></body></html>" % (remote_url, remote_url))
+        else:
+            self.response.out.write("Failure!\n%s: %s\n%s" % (http_resp.status, http_resp.reason, remote_data))
+            
 class show_profiler_status(webapp.RequestHandler):
     def get(self):
         gp = get_global_profiler()
@@ -174,13 +233,11 @@
 
 class start_profiler(webapp.RequestHandler):
     def get(self):
-        gp = get_global_profiler()
+        gp = new_global_profiler()
         gp.start_profiling()
-        self.response.out.write("<html><body>")
-        self.response.out.write("Started profiling (key: %s). <br />" % gp.profile_key)
-        self.response.out.write("Retrieve saved results at "
-            "<a href='/profiler/show?key=%(key)s'>/profiler/show?key=%(key)s</a>. <br />" % {'key':gp.profile_key})
-        self.response.out.write("</body></html>")
+        self.response.headers['Content-Type'] = "text/plain"
+        self.response.out.write("Started profiling (key: %s).\n" % gp.profile_key)
+        self.response.out.write("Retrieve saved results at <a href='/profiler/show?key=%(key)s'>/profiler/show?key=%(key)s).\n" % {'key':gp.profile_key})
 
 class stop_profiler(webapp.RequestHandler):
     def get(self):
@@ -231,6 +288,7 @@
      ('/profiler/show', show_profile),
      ('/profiler/download', download_profile_data),
      ('/profiler/status', show_profiler_status),
+     ('/profiler/send', send_profile_data),
      ],
     debug=True)
 
--- a/app/app_profiler/ppstats.py	Wed Sep 02 10:52:04 2009 +0200
+++ b/app/app_profiler/ppstats.py	Wed Sep 02 20:42:23 2009 +0200
@@ -1,6 +1,7 @@
 import pstats
 import cPickle
 import zlib
+import re
 
 class PickleStats(object):
     def __init__(self, stats):
@@ -48,6 +49,7 @@
     def __init__(self, *args, **kwargs):
         pstats.Stats.__init__(self, *args)
         self.replace_dirs = {}
+        self.replace_regexes = {}
 
     def set_output(self, stream):
         "redirect output of print_stats to the file object <stream>"
@@ -56,6 +58,10 @@
     def hide_directory(self, dirname, replacement=''):
         "replace occurences of <dirname> in filenames with <replacement>"
         self.replace_dirs[dirname] = replacement
+        
+    def hide_regex(self, pattern, replacement=''):
+        "call re.sub(pattern, replacement) on each filename"
+        self.replace_regexes[pattern] = replacement
 
     def func_strip_path(self, func_name):
         "take a filename, line, name tuple and mangle appropiately"
@@ -64,11 +70,15 @@
         for dirname in self.replace_dirs:
             filename = filename.replace(dirname, self.replace_dirs[dirname])
 
+        for pattern in self.replace_regexes:
+            filename = re.sub(pattern, self.replace_regexes[pattern], filename)
+
         return filename, line, name
 
     def strip_dirs(self):
         "strip irrelevant/redundant directories from filenames in profile data"
         func_std_string = pstats.func_std_string
+        add_func_stats = pstats.add_func_stats
 
         oldstats = self.stats
         self.stats = newstats = {}