1
# Copyright (C) 2007, 2009, 2010 Canonical Ltd
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Tests for profiling data collection."""
25
from bzrlib import errors, tests
28
class _LSProfFeature(tests.Feature):
32
from bzrlib import lsprof
39
LSProfFeature = _LSProfFeature()
42
_TXT_HEADER = " CallCount Recursive Total(ms) " + \
43
"Inline(ms) module:lineno(function)\n"
47
"A simple routine to profile."
48
result = sorted(['abc', 'def', 'ghi'])
52
"Collect and return some dummy profile data."
53
from bzrlib.lsprof import profile
54
ret, stats = profile(_junk_callable)
58
class TestStatsSave(tests.TestCaseInTempDir):
60
_test_needs_features = [LSProfFeature]
63
super(tests.TestCaseInTempDir, self).setUp()
64
self.stats = _collect_stats()
66
def _tempfile(self, ext):
68
return bzrlib.osutils.pathjoin(dir, "tmp_profile_data." + ext)
70
def test_stats_save_to_txt(self):
71
f = self._tempfile("txt")
73
lines = open(f).readlines()
74
self.assertEqual(lines[0], _TXT_HEADER)
76
def test_stats_save_to_callgrind(self):
77
f = self._tempfile("callgrind")
79
lines = open(f).readlines()
80
self.assertEqual(lines[0], "events: Ticks\n")
81
f = bzrlib.osutils.pathjoin(self.test_dir, "callgrind.out.foo")
83
lines = open(f).readlines()
84
self.assertEqual(lines[0], "events: Ticks\n")
85
# Test explicit format nommination
86
f2 = self._tempfile("txt")
87
self.stats.save(f2, format="callgrind")
88
lines2 = open(f2).readlines()
89
self.assertEqual(lines2[0], "events: Ticks\n")
91
def test_stats_save_to_pickle(self):
92
f = self._tempfile("pkl")
94
data1 = cPickle.load(open(f))
95
self.assertEqual(type(data1), bzrlib.lsprof.Stats)
98
class TestBzrProfiler(tests.TestCase):
100
_test_needs_features = [LSProfFeature]
102
def test_start_call_stuff_stop(self):
103
profiler = bzrlib.lsprof.BzrProfiler()
110
stats = profiler.stop()
112
lines = [str(data) for data in stats.data]
113
lines = [line for line in lines if 'a_function' in line]
114
self.assertLength(1, lines)
116
def test_block_0(self):
117
# When profiler_block is 0, reentrant profile requests fail.
118
self.overrideAttr(bzrlib.lsprof.BzrProfiler, 'profiler_block', 0)
121
profiler = bzrlib.lsprof.BzrProfiler()
122
self.assertRaises(errors.BzrError, profiler.start)
123
inner_calls.append(True)
124
bzrlib.lsprof.profile(inner)
125
self.assertLength(1, inner_calls)
127
def test_block_1(self):
128
# When profiler_block is 1, concurrent profiles serialise.
129
# This is tested by manually acquiring the profiler lock, then
130
# starting a thread that tries to profile, and releasing the lock.
131
# We know due to test_block_0 that two profiles at once hit the lock,
132
# so while this isn't perfect (we'd want a callback on the lock being
133
# entered to allow lockstep evaluation of the actions), its good enough
134
# to be confident regressions would be caught. Alternatively, if this
135
# is flakey, a fake Lock object can be used to trace the calls made.
138
calls.append('profiled')
140
bzrlib.lsprof.profile(profiled)
141
calls.append('after_profiled')
142
thread = threading.Thread(target=do_profile)
143
bzrlib.lsprof.BzrProfiler.profiler_lock.acquire()
148
bzrlib.lsprof.BzrProfiler.profiler_lock.release()
151
self.assertLength(2, calls)