Рубрики
Без рубрики

Отслеживание программы во время ее выполнения

Автор оригинала: Doug Hellmann.

Есть два способа ввести код для наблюдения за выполнением программы: трассировка и профилирование . Они похожи, но предназначены для разных целей и поэтому имеют разные ограничения. Самый простой, но наименее эффективный способ контролировать программу – использовать обработчик трассировки , который можно использовать для написания отладчика, отслеживания покрытия кода или достижения многих других целей.

Ловушка трассировки изменяется путем передачи функции обратного вызова в sys.settrace () . Обратный вызов получит три аргумента: кадр стека из выполняемого кода, строку с указанием типа уведомления и значение аргумента для конкретного события. в таблице ниже перечислены семь типов событий для различных уровней информации, возникающих при выполнении программы.

Перехватчики событий для settrace ()

Мероприятие

Когда это происходит

Значение аргумента

вызов

Перед выполнением строки

Никто

линия

Перед выполнением строки

Никто

возвращаться

Прежде чем функция вернется

Возвращаемое значение

исключение

После возникновения исключения

Кортеж (исключение, значение, трассировка)

c_call

Перед вызовом функции C

Объект функции C

c_return

После возврата функции C

Никто

c_exception

После того, как функция C выдает ошибку

Никто

Вызов функций отслеживания

Событие call генерируется перед каждым вызовом функции. Кадр, переданный в обратный вызов, можно использовать, чтобы узнать, какая функция вызывается и откуда.

sys_settrace_call.py

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/env python3
# encoding: utf-8

import sys


def trace_calls(frame, event, arg):
    if event  'call':
        return
    co  frame.f_code
    func_name  co.co_name
    if func_name  'write':
        # Ignore write() calls from printing
        return
    func_line_no  frame.f_lineno
    func_filename  co.co_filename
    if not func_filename.endswith('sys_settrace_call.py'):
        # Ignore calls not in this module
        return
    caller  frame.f_back
    caller_line_no  caller.f_lineno
    caller_filename  caller.f_code.co_filename
    print('* Call to', func_name)
    print('*  on line {} of {}'.format(
        func_line_no, func_filename))
    print('*  from line {} of {}'.format(
        caller_line_no, caller_filename))
    return


def b():
    print('inside b()\n')


def a():
    print('inside a()\n')
    b()


sys.settrace(trace_calls)
a()

В этом примере игнорируются вызовы write () , которые используются print для записи в sys.stdout .

$ python3 sys_settrace_call.py

* Call to a
*  on line 35 of sys_settrace_call.py
*  from line 41 of sys_settrace_call.py
inside a()

* Call to b
*  on line 31 of sys_settrace_call.py
*  from line 37 of sys_settrace_call.py
inside b()

Трассировка внутренних функций

Ловушка трассировки может возвращать новую ловушку, которая будет использоваться внутри новой области (функция трассировки local ). Например, можно управлять трассировкой только построчно в определенных модулях или функциях.

sys_settrace_line.py

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#!/usr/bin/env python3
# encoding: utf-8

import functools
import sys


def trace_lines(frame, event, arg):
    if event  'line':
        return
    co  frame.f_code
    func_name  co.co_name
    line_no  frame.f_lineno
    print('*  {} line {}'.format(func_name, line_no))


def trace_calls(frame, event, arg, to_be_traced):
    if event  'call':
        return
    co  frame.f_code
    func_name  co.co_name
    if func_name  'write':
        # Ignore write() calls from printing
        return
    line_no  frame.f_lineno
    filename  co.co_filename
    if not filename.endswith('sys_settrace_line.py'):
        # Ignore calls not in this module
        return
    print('* Call to {} on line {} of {}'.format(
        func_name, line_no, filename))
    if func_name in to_be_traced:
        # Trace into this function
        return trace_lines
    return


def c(input):
    print('input =', input)
    print('Leaving c()')


def b(arg):
    val  arg * 5
    c(val)
    print('Leaving b()')


def a():
    b(2)
    print('Leaving a()')


tracer  functools.partial(trace_calls, to_be_traced['b'])
sys.settrace(tracer)
a()

В этом примере список функций хранится в переменной: py“to_be_traced“, поэтому при запуске trace_calls () он может вернуть trace_lines () для включения трассировки. внутри b () .

$ python3 sys_settrace_line.py

* Call to a on line 49 of sys_settrace_line.py
* Call to b on line 43 of sys_settrace_line.py
*  b line 44
*  b line 45
* Call to c on line 38 of sys_settrace_line.py
input = 10
Leaving c()
*  b line 46
Leaving b()
Leaving a()

Наблюдая за стеком

Еще один полезный способ использования ловушек – следить за тем, какие функции вызываются и каковы их возвращаемые значения. Чтобы отслеживать возвращаемые значения, следите за событием return .

sys_settrace_return.py

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/usr/bin/env python3
# encoding: utf-8

import sys


def trace_calls_and_returns(frame, event, arg):
    co  frame.f_code
    func_name  co.co_name
    if func_name  'write':
        # Ignore write() calls from printing
        return
    line_no  frame.f_lineno
    filename  co.co_filename
    if not filename.endswith('sys_settrace_return.py'):
        # Ignore calls not in this module
        return
    if event  'call':
        print('* Call to {} on line {} of {}'.format(
            func_name, line_no, filename))
        return trace_calls_and_returns
    elif event  'return':
        print('* {} => {}'.format(func_name, arg))
    return


def b():
    print('inside b()')
    return 'response_from_b '


def a():
    print('inside a()')
    val  b()
    return val * 2


sys.settrace(trace_calls_and_returns)
a()

Функция локальной трассировки используется для отслеживания возвращаемых событий, поэтому trace_calls_and_returns () должен возвращать ссылку на себя при вызове функции, поэтому возвращаемое значение можно отслеживать.

$ python3 sys_settrace_return.py

* Call to a on line 32 of sys_settrace_return.py
inside a()
* Call to b on line 27 of sys_settrace_return.py
inside b()
* b => response_from_b
* a => response_from_b response_from_b

Распространение исключений

Исключения можно отслеживать, ища событие exception в локальной функции трассировки. Когда возникает исключение, ловушка трассировки вызывается с кортежем, содержащим тип исключения, объект исключения и объект трассировки.

sys_settrace_exception.py

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#!/usr/bin/env python3
# encoding: utf-8

import sys


def trace_exceptions(frame, event, arg):
    if event  'exception':
        return
    co  frame.f_code
    func_name  co.co_name
    line_no  frame.f_lineno
    exc_type, exc_value, exc_traceback  arg
    print(('* Tracing exception:\n'
           '* {} "{}"\n'
           '* on line {} of {}\n').format(
               exc_type.__name__, exc_value, line_no,
               func_name))


def trace_calls(frame, event, arg):
    if event  'call':
        return
    co  frame.f_code
    func_name  co.co_name
    if func_name in TRACE_INTO:
        return trace_exceptions


def c():
    raise RuntimeError('generating exception in c()')


def b():
    c()
    print('Leaving b()')


def a():
    b()
    print('Leaving a()')


TRACE_INTO  ['a', 'b', 'c']

sys.settrace(trace_calls)
try:
    a()
except Exception as e:
    print('Exception handler:', e)

Позаботьтесь о том, чтобы ограничить область применения локальной функции, поскольку некоторые внутренние элементы сообщений об ошибках форматирования генерируют и игнорируют свои собственные исключения. Перехватчик трассировки видит каждое исключение, независимо от того, перехватывает и игнорирует вызывающий объект.

$ python3 sys_settrace_exception.py

* Tracing exception:
* RuntimeError "generating exception in c()"
* on line 31 of c

* Tracing exception:
* RuntimeError "generating exception in c()"
* on line 35 of b

* Tracing exception:
* RuntimeError "generating exception in c()"
* on line 40 of a

Exception handler: generating exception in c()

Смотрите также

  • profile – в документации модуля profile показано, как использовать готовый профилировщик.
  • trace – модуль trace реализует несколько функций анализа кода.
  • Типы и члены – описания объектов фрейма и кода и их атрибутов.
  • Отслеживание кода Python – Другой settrace () учебник.
  • Wicked hack: трассировка байт-кода Python – эксперименты Неда Батчелдера с трассировкой с большей детализацией, чем на уровне исходной строки.
  • смайлик – Python Application Tracer