#!/usr/bin/env python3 # # Copyright (C) 2017 Codethink Limited # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library. If not, see . # # Authors: # Tristan Van Berkom import cProfile import pstats import os import datetime import time # Track what profile topics are active active_topics = {} active_profiles = {} initialized = False # Use the topic values here to decide what to profile # by setting them in the BST_PROFILE environment variable. # # Multiple topics can be set with the ':' separator. # # E.g.: # # BST_PROFILE=circ-dep-check:sort-deps bst # # The special 'all' value will enable all profiles. class Topics(): CIRCULAR_CHECK = 'circ-dep-check' SORT_DEPENDENCIES = 'sort-deps' LOAD_LOADER = 'load-loader' LOAD_CONTEXT = 'load-context' LOAD_PROJECT = 'load-project' LOAD_PIPELINE = 'load-pipeline' SHOW = 'show' ARTIFACT_RECEIVE = 'artifact-receive' ALL = 'all' class Profile(): def __init__(self, topic, key, message): self.message = message self.key = topic + '-' + key self.start = time.time() self.profiler = cProfile.Profile() self.profiler.enable() def end(self): self.profiler.disable() filename = self.key.replace('/', '-') filename = filename.replace('.', '-') filename = os.path.join(os.getcwd(), 'profile-' + filename + '.log') with open(filename, "a", encoding="utf-8") as f: dt = datetime.datetime.fromtimestamp(self.start) time_ = dt.strftime('%Y-%m-%d %H:%M:%S') heading = '================================================================\n' heading += 'Profile for key: {}\n'.format(self.key) heading += 'Started at: {}\n'.format(time_) if self.message: heading += '\n {}'.format(self.message) heading += '================================================================\n' f.write(heading) ps = pstats.Stats(self.profiler, stream=f).sort_stats('cumulative') ps.print_stats() # profile_start() # # Start profiling for a given topic. # # Args: # topic (str): A topic name # key (str): A key for this profile run # message (str): An optional message to print in profile results # def profile_start(topic, key, message=None): if not profile_enabled(topic): return # Start profiling and hold on to the key profile = Profile(topic, key, message) assert active_profiles.get(profile.key) is None active_profiles[profile.key] = profile # profile_end() # # Ends a profiling session previously # started with profile_start() # # Args: # topic (str): A topic name # key (str): A key for this profile run # def profile_end(topic, key): if not profile_enabled(topic): return topic_key = topic + '-' + key profile = active_profiles.get(topic_key) assert profile profile.end() del active_profiles[topic_key] def profile_init(): global initialized # pylint: disable=global-statement if not initialized: setting = os.getenv('BST_PROFILE') if setting: topics = setting.split(':') for topic in topics: active_topics[topic] = True initialized = True def profile_enabled(topic): profile_init() if active_topics.get(topic): return True if active_topics.get(Topics.ALL): return True return False