~ubuntu-branches/ubuntu/trusty/python3.4/trusty-proposed

« back to all changes in this revision

Viewing changes to Tools/scripts/analyze_dxp.py

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2013-11-25 09:44:27 UTC
  • Revision ID: package-import@ubuntu.com-20131125094427-lzxj8ap5w01lmo7f
Tags: upstream-3.4~b1
ImportĀ upstreamĀ versionĀ 3.4~b1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
Some helper functions to analyze the output of sys.getdxp() (which is
 
3
only available if Python was built with -DDYNAMIC_EXECUTION_PROFILE).
 
4
These will tell you which opcodes have been executed most frequently
 
5
in the current process, and, if Python was also built with -DDXPAIRS,
 
6
will tell you which instruction _pairs_ were executed most frequently,
 
7
which may help in choosing new instructions.
 
8
 
 
9
If Python was built without -DDYNAMIC_EXECUTION_PROFILE, importing
 
10
this module will raise a RuntimeError.
 
11
 
 
12
If you're running a script you want to profile, a simple way to get
 
13
the common pairs is:
 
14
 
 
15
$ PYTHONPATH=$PYTHONPATH:<python_srcdir>/Tools/scripts \
 
16
./python -i -O the_script.py --args
 
17
...
 
18
> from analyze_dxp import *
 
19
> s = render_common_pairs()
 
20
> open('/tmp/some_file', 'w').write(s)
 
21
"""
 
22
 
 
23
import copy
 
24
import opcode
 
25
import operator
 
26
import sys
 
27
import threading
 
28
 
 
29
if not hasattr(sys, "getdxp"):
 
30
    raise RuntimeError("Can't import analyze_dxp: Python built without"
 
31
                       " -DDYNAMIC_EXECUTION_PROFILE.")
 
32
 
 
33
 
 
34
_profile_lock = threading.RLock()
 
35
_cumulative_profile = sys.getdxp()
 
36
 
 
37
# If Python was built with -DDXPAIRS, sys.getdxp() returns a list of
 
38
# lists of ints.  Otherwise it returns just a list of ints.
 
39
def has_pairs(profile):
 
40
    """Returns True if the Python that produced the argument profile
 
41
    was built with -DDXPAIRS."""
 
42
 
 
43
    return len(profile) > 0 and isinstance(profile[0], list)
 
44
 
 
45
 
 
46
def reset_profile():
 
47
    """Forgets any execution profile that has been gathered so far."""
 
48
    with _profile_lock:
 
49
        sys.getdxp()  # Resets the internal profile
 
50
        global _cumulative_profile
 
51
        _cumulative_profile = sys.getdxp()  # 0s out our copy.
 
52
 
 
53
 
 
54
def merge_profile():
 
55
    """Reads sys.getdxp() and merges it into this module's cached copy.
 
56
 
 
57
    We need this because sys.getdxp() 0s itself every time it's called."""
 
58
 
 
59
    with _profile_lock:
 
60
        new_profile = sys.getdxp()
 
61
        if has_pairs(new_profile):
 
62
            for first_inst in range(len(_cumulative_profile)):
 
63
                for second_inst in range(len(_cumulative_profile[first_inst])):
 
64
                    _cumulative_profile[first_inst][second_inst] += (
 
65
                        new_profile[first_inst][second_inst])
 
66
        else:
 
67
            for inst in range(len(_cumulative_profile)):
 
68
                _cumulative_profile[inst] += new_profile[inst]
 
69
 
 
70
 
 
71
def snapshot_profile():
 
72
    """Returns the cumulative execution profile until this call."""
 
73
    with _profile_lock:
 
74
        merge_profile()
 
75
        return copy.deepcopy(_cumulative_profile)
 
76
 
 
77
 
 
78
def common_instructions(profile):
 
79
    """Returns the most common opcodes in order of descending frequency.
 
80
 
 
81
    The result is a list of tuples of the form
 
82
      (opcode, opname, # of occurrences)
 
83
 
 
84
    """
 
85
    if has_pairs(profile) and profile:
 
86
        inst_list = profile[-1]
 
87
    else:
 
88
        inst_list = profile
 
89
    result = [(op, opcode.opname[op], count)
 
90
              for op, count in enumerate(inst_list)
 
91
              if count > 0]
 
92
    result.sort(key=operator.itemgetter(2), reverse=True)
 
93
    return result
 
94
 
 
95
 
 
96
def common_pairs(profile):
 
97
    """Returns the most common opcode pairs in order of descending frequency.
 
98
 
 
99
    The result is a list of tuples of the form
 
100
      ((1st opcode, 2nd opcode),
 
101
       (1st opname, 2nd opname),
 
102
       # of occurrences of the pair)
 
103
 
 
104
    """
 
105
    if not has_pairs(profile):
 
106
        return []
 
107
    result = [((op1, op2), (opcode.opname[op1], opcode.opname[op2]), count)
 
108
              # Drop the row of single-op profiles with [:-1]
 
109
              for op1, op1profile in enumerate(profile[:-1])
 
110
              for op2, count in enumerate(op1profile)
 
111
              if count > 0]
 
112
    result.sort(key=operator.itemgetter(2), reverse=True)
 
113
    return result
 
114
 
 
115
 
 
116
def render_common_pairs(profile=None):
 
117
    """Renders the most common opcode pairs to a string in order of
 
118
    descending frequency.
 
119
 
 
120
    The result is a series of lines of the form:
 
121
      # of occurrences: ('1st opname', '2nd opname')
 
122
 
 
123
    """
 
124
    if profile is None:
 
125
        profile = snapshot_profile()
 
126
    def seq():
 
127
        for _, ops, count in common_pairs(profile):
 
128
            yield "%s: %s\n" % (count, ops)
 
129
    return ''.join(seq())