# Copyright (c) 2010-2012 OpenStack, LLC. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import cgi import os import random import re import string import tempfile from swift import gettext_ as _ from exceptions import PLOTLIBNotInstalled, ODFLIBNotInstalled,\ NotFoundException, MethodNotAllowed, DataLoadFailure, ProfileException from profile_model import Stats2 PLOTLIB_INSTALLED = True try: import matplotlib # use agg backend for writing to file, not for rendering in a window. # otherwise some platform will complain "no display name and $DISPLAY # environment variable" matplotlib.use('agg') import matplotlib.pyplot as plt except ImportError: PLOTLIB_INSTALLED = False empty_description = """ The default profile of current process or the profile you requested is empty. """ profile_tmpl = """ """ sort_tmpl = """ """ limit_tmpl = """ """ fulldirs_tmpl = """ """ mode_tmpl = """ """ nfl_filter_tmpl = """ """ formelements_tmpl = """
Profile Sort Limit Full Path Filter Plot Metric Plot Type Format
${profile} ${sort} ${limit} ${fulldirs} ${nfl_filter}
""" index_tmpl = """ profile results
${description}

${formelements}
${profilehtml}
    
""" class HTMLViewer(object): format_dict = {'default': 'application/octet-stream', 'json': 'application/json', 'csv': 'text/csv', 'ods': 'application/vnd.oasis.opendocument.spreadsheet', 'python': 'text/html'} def __init__(self, app_path, profile_module, profile_log): self.app_path = app_path self.profile_module = profile_module self.profile_log = profile_log def _get_param(self, query_dict, key, default=None, multiple=False): value = query_dict.get(key, default) if value is None or value == '': return default if multiple: return value if isinstance(value, list): return eval(value[0]) if isinstance(default, int) else value[0] else: return value def render(self, url, method, path_entry, query_dict, clear_callback): plot = self._get_param(query_dict, 'plot', None) download = self._get_param(query_dict, 'download', None) clear = self._get_param(query_dict, 'clear', None) action = plot or download or clear profile_id = self._get_param(query_dict, 'profile', 'current') sort = self._get_param(query_dict, 'sort', 'time') limit = self._get_param(query_dict, 'limit', -1) fulldirs = self._get_param(query_dict, 'fulldirs', 0) nfl_filter = self._get_param(query_dict, 'nfl_filter', '').strip() metric_selected = self._get_param(query_dict, 'metric', 'cc') plot_type = self._get_param(query_dict, 'plottype', 'bar') download_format = self._get_param(query_dict, 'format', 'default') content = '' # GET /__profile, POST /__profile if len(path_entry) == 2 and method in ['GET', 'POST']: log_files = self.profile_log.get_logfiles(profile_id) if action == 'plot': content, headers = self.plot(log_files, sort, limit, nfl_filter, metric_selected, plot_type) elif action == 'download': content, headers = self.download(log_files, sort, limit, nfl_filter, download_format) else: if action == 'clear': self.profile_log.clear(profile_id) clear_callback and clear_callback() content, headers = self.index_page(log_files, sort, limit, fulldirs, nfl_filter, profile_id, url) # GET /__profile__/all # GET /__profile__/current # GET /__profile__/profile_id # GET /__profile__/profile_id/ # GET /__profile__/profile_id/account.py:50(GETorHEAD) # GET /__profile__/profile_id/swift/proxy/controllers # /account.py:50(GETorHEAD) # with QUERY_STRING: ?format=[default|json|csv|ods] elif len(path_entry) > 2 and method == 'GET': profile_id = path_entry[2] log_files = self.profile_log.get_logfiles(profile_id) pids = self.profile_log.get_all_pids() # return all profiles in a json format by default. # GET /__profile__/ if profile_id == '': content = '{"profile_ids": ["' + '","'.join(pids) + '"]}' headers = [('content-type', self.format_dict['json'])] else: if len(path_entry) > 3 and path_entry[3] != '': nfl_filter = '/'.join(path_entry[3:]) if path_entry[-1].find(':0') == -1: nfl_filter = '/' + nfl_filter content, headers = self.download(log_files, sort, -1, nfl_filter, download_format) headers.append(('Access-Control-Allow-Origin', '*')) else: raise MethodNotAllowed(_('method %s is not allowed.') % method) return content, headers def index_page(self, log_files=None, sort='time', limit=-1, fulldirs=0, nfl_filter='', profile_id='current', url='#'): headers = [('content-type', 'text/html')] if len(log_files) == 0: return empty_description, headers try: stats = Stats2(*log_files) except (IOError, ValueError): raise DataLoadFailure(_('Can not load profile data from %s.') % log_files) if not fulldirs: stats.strip_dirs() stats.sort_stats(sort) nfl_filter_esc =\ nfl_filter.replace('(', '\(').replace(')', '\)') amount = [nfl_filter_esc, limit] if nfl_filter_esc else [limit] profile_html = self.generate_stats_html(stats, self.app_path, profile_id, *amount) description = "Profiling information is generated by using\ '%s' profiler." % self.profile_module sort_repl = '' % (p, p) for p in self.profile_log.get_all_pids()]) profile_element = string.Template(profile_tmpl).substitute( {'profile_list': plist}) profile_repl = '