# HG changeset patch # User Sverre Rabbelier # Date 1251916943 -7200 # Node ID bc793800116e5cf7bc7e64c2589363cd446836b1 # Parent f446e019825c89eca49a518e13b7ef7fbb451950 Updated to r11 of app_profiler diff -r f446e019825c -r bc793800116e app/app_profiler/app_profiler.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("

No profiler.

") 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("
\n")
-        ps.print_stats(30)
+        ps.profile_obj.print_stats(30)
         self.response.out.write("
") class download_profile_data(webapp.RequestHandler): @@ -151,12 +178,44 @@ self.response.out.write("

No profiler.

") 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("

No profiler.

") + return + + dest = self.request.get('dest', '') + if not dest: + self.response.out.write("No destination") + + 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("Success! %s" % (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("") - self.response.out.write("Started profiling (key: %s).
" % gp.profile_key) - self.response.out.write("Retrieve saved results at " - "/profiler/show?key=%(key)s.
" % {'key':gp.profile_key}) - self.response.out.write("") + 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 /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) diff -r f446e019825c -r bc793800116e app/app_profiler/ppstats.py --- 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 " @@ -56,6 +58,10 @@ def hide_directory(self, dirname, replacement=''): "replace occurences of in filenames with " 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 = {}