~bzr-pqm/bzr/bzr.dev

2367.1.7 by Robert Collins
Added ``bzrlib.strace.strace`` which will strace a single callable and
1
# Copyright (C) 2007 Canonical Ltd
2
#   Authors: Robert Collins <robert.collins@canonical.com>
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17
18
"""Support for running strace against the current process."""
19
2386.1.2 by John Arbash Meinel
Make sure errno is imported if we are going to use it later.
20
import errno
2367.1.7 by Robert Collins
Added ``bzrlib.strace.strace`` which will strace a single callable and
21
import os
22
import signal
23
import subprocess
24
import tempfile
25
26
# this is currently test-focused, so importing bzrlib.tests is ok. We might
27
# want to move feature to its own module though.
28
from bzrlib.tests import Feature
29
2367.1.9 by Robert Collins
Review feedback.
30
2367.1.7 by Robert Collins
Added ``bzrlib.strace.strace`` which will strace a single callable and
31
def strace(function, *args, **kwargs):
32
    """Invoke strace on function.
33
2367.1.9 by Robert Collins
Review feedback.
34
    :return: a tuple: function-result, a StraceResult.
2367.1.7 by Robert Collins
Added ``bzrlib.strace.strace`` which will strace a single callable and
35
    """
2566.3.4 by Vincent Ladeuil
Take Martin and Robert comments into account.
36
    return strace_detailed(function, args, kwargs)
37
38
39
def strace_detailed(function, args, kwargs, follow_children=True):
2566.3.1 by Vincent Ladeuil
Fix #102019 by not asking strace to follow children forks during tests.
40
    # FIXME: strace is buggy
41
    # (https://bugs.launchpad.net/ubuntu/+source/strace/+bug/103133) and the
42
    # test suite hangs if the '-f' is given to strace *and* more than one
2566.3.4 by Vincent Ladeuil
Take Martin and Robert comments into account.
43
    # thread is running. Using follow_children=False allows the test suite to
44
    # disable fork following to work around the bug.
45
2367.1.7 by Robert Collins
Added ``bzrlib.strace.strace`` which will strace a single callable and
46
    # capture strace output to a file
2394.1.1 by Andrew Bennetts
Wait for strace to attach before running the function we want to trace.
47
    log_file = tempfile.NamedTemporaryFile()
2367.1.7 by Robert Collins
Added ``bzrlib.strace.strace`` which will strace a single callable and
48
    log_file_fd = log_file.fileno()
49
    pid = os.getpid()
50
    # start strace
2566.3.1 by Vincent Ladeuil
Fix #102019 by not asking strace to follow children forks during tests.
51
    strace_cmd = ['strace', '-r', '-tt', '-p', str(pid), '-o', log_file.name]
2566.3.2 by Vincent Ladeuil
Fix typo, add comment, following Martin's review.
52
    if follow_children:
2566.3.1 by Vincent Ladeuil
Fix #102019 by not asking strace to follow children forks during tests.
53
        strace_args.append('-f')
54
    proc = subprocess.Popen(strace_cmd,
55
                            stdout=subprocess.PIPE,
56
                            stderr=subprocess.STDOUT)
2394.1.1 by Andrew Bennetts
Wait for strace to attach before running the function we want to trace.
57
    # Wait for strace to attach
58
    attached_notice = proc.stdout.readline()
59
    # Run the function to strace
2367.1.9 by Robert Collins
Review feedback.
60
    result = function(*args, **kwargs)
2367.1.7 by Robert Collins
Added ``bzrlib.strace.strace`` which will strace a single callable and
61
    # stop strace
62
    os.kill(proc.pid, signal.SIGQUIT)
63
    proc.communicate()
64
    # grab the log
65
    log_file.seek(0)
66
    log = log_file.read()
67
    log_file.close()
2367.1.9 by Robert Collins
Review feedback.
68
    return result, StraceResult(log)
2367.1.7 by Robert Collins
Added ``bzrlib.strace.strace`` which will strace a single callable and
69
70
71
class StraceResult(object):
72
    """The result of stracing a function."""
73
74
    def __init__(self, raw_log):
75
        """Create a StraceResult.
76
77
        :param raw_log: The output that strace created.
78
        """
79
        self.raw_log = raw_log
80
81
82
class _StraceFeature(Feature):
83
84
    def _probe(self):
85
        try:
86
            proc = subprocess.Popen(['strace'],
87
                stderr=subprocess.PIPE,
88
                stdout=subprocess.PIPE)
89
            proc.communicate()
90
            return True
91
        except OSError, e:
92
            if e.errno == errno.ENOENT:
93
                # strace is not installed
94
                return False
95
            else:
96
                raise
97
98
    def feature_name(self):
99
        return 'strace'
100
101
StraceFeature = _StraceFeature()