xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/trace.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker#!/usr/bin/env python3
2*cda5da8dSAndroid Build Coastguard Worker
3*cda5da8dSAndroid Build Coastguard Worker# portions copyright 2001, Autonomous Zones Industries, Inc., all rights...
4*cda5da8dSAndroid Build Coastguard Worker# err...  reserved and offered to the public under the terms of the
5*cda5da8dSAndroid Build Coastguard Worker# Python 2.2 license.
6*cda5da8dSAndroid Build Coastguard Worker# Author: Zooko O'Whielacronx
7*cda5da8dSAndroid Build Coastguard Worker# http://zooko.com/
8*cda5da8dSAndroid Build Coastguard Worker# mailto:[email protected]
9*cda5da8dSAndroid Build Coastguard Worker#
10*cda5da8dSAndroid Build Coastguard Worker# Copyright 2000, Mojam Media, Inc., all rights reserved.
11*cda5da8dSAndroid Build Coastguard Worker# Author: Skip Montanaro
12*cda5da8dSAndroid Build Coastguard Worker#
13*cda5da8dSAndroid Build Coastguard Worker# Copyright 1999, Bioreason, Inc., all rights reserved.
14*cda5da8dSAndroid Build Coastguard Worker# Author: Andrew Dalke
15*cda5da8dSAndroid Build Coastguard Worker#
16*cda5da8dSAndroid Build Coastguard Worker# Copyright 1995-1997, Automatrix, Inc., all rights reserved.
17*cda5da8dSAndroid Build Coastguard Worker# Author: Skip Montanaro
18*cda5da8dSAndroid Build Coastguard Worker#
19*cda5da8dSAndroid Build Coastguard Worker# Copyright 1991-1995, Stichting Mathematisch Centrum, all rights reserved.
20*cda5da8dSAndroid Build Coastguard Worker#
21*cda5da8dSAndroid Build Coastguard Worker#
22*cda5da8dSAndroid Build Coastguard Worker# Permission to use, copy, modify, and distribute this Python software and
23*cda5da8dSAndroid Build Coastguard Worker# its associated documentation for any purpose without fee is hereby
24*cda5da8dSAndroid Build Coastguard Worker# granted, provided that the above copyright notice appears in all copies,
25*cda5da8dSAndroid Build Coastguard Worker# and that both that copyright notice and this permission notice appear in
26*cda5da8dSAndroid Build Coastguard Worker# supporting documentation, and that the name of neither Automatrix,
27*cda5da8dSAndroid Build Coastguard Worker# Bioreason or Mojam Media be used in advertising or publicity pertaining to
28*cda5da8dSAndroid Build Coastguard Worker# distribution of the software without specific, written prior permission.
29*cda5da8dSAndroid Build Coastguard Worker#
30*cda5da8dSAndroid Build Coastguard Worker"""program/module to trace Python program or function execution
31*cda5da8dSAndroid Build Coastguard Worker
32*cda5da8dSAndroid Build Coastguard WorkerSample use, command line:
33*cda5da8dSAndroid Build Coastguard Worker  trace.py -c -f counts --ignore-dir '$prefix' spam.py eggs
34*cda5da8dSAndroid Build Coastguard Worker  trace.py -t --ignore-dir '$prefix' spam.py eggs
35*cda5da8dSAndroid Build Coastguard Worker  trace.py --trackcalls spam.py eggs
36*cda5da8dSAndroid Build Coastguard Worker
37*cda5da8dSAndroid Build Coastguard WorkerSample use, programmatically
38*cda5da8dSAndroid Build Coastguard Worker  import sys
39*cda5da8dSAndroid Build Coastguard Worker
40*cda5da8dSAndroid Build Coastguard Worker  # create a Trace object, telling it what to ignore, and whether to
41*cda5da8dSAndroid Build Coastguard Worker  # do tracing or line-counting or both.
42*cda5da8dSAndroid Build Coastguard Worker  tracer = trace.Trace(ignoredirs=[sys.base_prefix, sys.base_exec_prefix,],
43*cda5da8dSAndroid Build Coastguard Worker                       trace=0, count=1)
44*cda5da8dSAndroid Build Coastguard Worker  # run the new command using the given tracer
45*cda5da8dSAndroid Build Coastguard Worker  tracer.run('main()')
46*cda5da8dSAndroid Build Coastguard Worker  # make a report, placing output in /tmp
47*cda5da8dSAndroid Build Coastguard Worker  r = tracer.results()
48*cda5da8dSAndroid Build Coastguard Worker  r.write_results(show_missing=True, coverdir="/tmp")
49*cda5da8dSAndroid Build Coastguard Worker"""
50*cda5da8dSAndroid Build Coastguard Worker__all__ = ['Trace', 'CoverageResults']
51*cda5da8dSAndroid Build Coastguard Worker
52*cda5da8dSAndroid Build Coastguard Workerimport io
53*cda5da8dSAndroid Build Coastguard Workerimport linecache
54*cda5da8dSAndroid Build Coastguard Workerimport os
55*cda5da8dSAndroid Build Coastguard Workerimport sys
56*cda5da8dSAndroid Build Coastguard Workerimport sysconfig
57*cda5da8dSAndroid Build Coastguard Workerimport token
58*cda5da8dSAndroid Build Coastguard Workerimport tokenize
59*cda5da8dSAndroid Build Coastguard Workerimport inspect
60*cda5da8dSAndroid Build Coastguard Workerimport gc
61*cda5da8dSAndroid Build Coastguard Workerimport dis
62*cda5da8dSAndroid Build Coastguard Workerimport pickle
63*cda5da8dSAndroid Build Coastguard Workerfrom time import monotonic as _time
64*cda5da8dSAndroid Build Coastguard Worker
65*cda5da8dSAndroid Build Coastguard Workerimport threading
66*cda5da8dSAndroid Build Coastguard Worker
67*cda5da8dSAndroid Build Coastguard WorkerPRAGMA_NOCOVER = "#pragma NO COVER"
68*cda5da8dSAndroid Build Coastguard Worker
69*cda5da8dSAndroid Build Coastguard Workerclass _Ignore:
70*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, modules=None, dirs=None):
71*cda5da8dSAndroid Build Coastguard Worker        self._mods = set() if not modules else set(modules)
72*cda5da8dSAndroid Build Coastguard Worker        self._dirs = [] if not dirs else [os.path.normpath(d)
73*cda5da8dSAndroid Build Coastguard Worker                                          for d in dirs]
74*cda5da8dSAndroid Build Coastguard Worker        self._ignore = { '<string>': 1 }
75*cda5da8dSAndroid Build Coastguard Worker
76*cda5da8dSAndroid Build Coastguard Worker    def names(self, filename, modulename):
77*cda5da8dSAndroid Build Coastguard Worker        if modulename in self._ignore:
78*cda5da8dSAndroid Build Coastguard Worker            return self._ignore[modulename]
79*cda5da8dSAndroid Build Coastguard Worker
80*cda5da8dSAndroid Build Coastguard Worker        # haven't seen this one before, so see if the module name is
81*cda5da8dSAndroid Build Coastguard Worker        # on the ignore list.
82*cda5da8dSAndroid Build Coastguard Worker        if modulename in self._mods:  # Identical names, so ignore
83*cda5da8dSAndroid Build Coastguard Worker            self._ignore[modulename] = 1
84*cda5da8dSAndroid Build Coastguard Worker            return 1
85*cda5da8dSAndroid Build Coastguard Worker
86*cda5da8dSAndroid Build Coastguard Worker        # check if the module is a proper submodule of something on
87*cda5da8dSAndroid Build Coastguard Worker        # the ignore list
88*cda5da8dSAndroid Build Coastguard Worker        for mod in self._mods:
89*cda5da8dSAndroid Build Coastguard Worker            # Need to take some care since ignoring
90*cda5da8dSAndroid Build Coastguard Worker            # "cmp" mustn't mean ignoring "cmpcache" but ignoring
91*cda5da8dSAndroid Build Coastguard Worker            # "Spam" must also mean ignoring "Spam.Eggs".
92*cda5da8dSAndroid Build Coastguard Worker            if modulename.startswith(mod + '.'):
93*cda5da8dSAndroid Build Coastguard Worker                self._ignore[modulename] = 1
94*cda5da8dSAndroid Build Coastguard Worker                return 1
95*cda5da8dSAndroid Build Coastguard Worker
96*cda5da8dSAndroid Build Coastguard Worker        # Now check that filename isn't in one of the directories
97*cda5da8dSAndroid Build Coastguard Worker        if filename is None:
98*cda5da8dSAndroid Build Coastguard Worker            # must be a built-in, so we must ignore
99*cda5da8dSAndroid Build Coastguard Worker            self._ignore[modulename] = 1
100*cda5da8dSAndroid Build Coastguard Worker            return 1
101*cda5da8dSAndroid Build Coastguard Worker
102*cda5da8dSAndroid Build Coastguard Worker        # Ignore a file when it contains one of the ignorable paths
103*cda5da8dSAndroid Build Coastguard Worker        for d in self._dirs:
104*cda5da8dSAndroid Build Coastguard Worker            # The '+ os.sep' is to ensure that d is a parent directory,
105*cda5da8dSAndroid Build Coastguard Worker            # as compared to cases like:
106*cda5da8dSAndroid Build Coastguard Worker            #  d = "/usr/local"
107*cda5da8dSAndroid Build Coastguard Worker            #  filename = "/usr/local.py"
108*cda5da8dSAndroid Build Coastguard Worker            # or
109*cda5da8dSAndroid Build Coastguard Worker            #  d = "/usr/local.py"
110*cda5da8dSAndroid Build Coastguard Worker            #  filename = "/usr/local.py"
111*cda5da8dSAndroid Build Coastguard Worker            if filename.startswith(d + os.sep):
112*cda5da8dSAndroid Build Coastguard Worker                self._ignore[modulename] = 1
113*cda5da8dSAndroid Build Coastguard Worker                return 1
114*cda5da8dSAndroid Build Coastguard Worker
115*cda5da8dSAndroid Build Coastguard Worker        # Tried the different ways, so we don't ignore this module
116*cda5da8dSAndroid Build Coastguard Worker        self._ignore[modulename] = 0
117*cda5da8dSAndroid Build Coastguard Worker        return 0
118*cda5da8dSAndroid Build Coastguard Worker
119*cda5da8dSAndroid Build Coastguard Workerdef _modname(path):
120*cda5da8dSAndroid Build Coastguard Worker    """Return a plausible module name for the path."""
121*cda5da8dSAndroid Build Coastguard Worker
122*cda5da8dSAndroid Build Coastguard Worker    base = os.path.basename(path)
123*cda5da8dSAndroid Build Coastguard Worker    filename, ext = os.path.splitext(base)
124*cda5da8dSAndroid Build Coastguard Worker    return filename
125*cda5da8dSAndroid Build Coastguard Worker
126*cda5da8dSAndroid Build Coastguard Workerdef _fullmodname(path):
127*cda5da8dSAndroid Build Coastguard Worker    """Return a plausible module name for the path."""
128*cda5da8dSAndroid Build Coastguard Worker
129*cda5da8dSAndroid Build Coastguard Worker    # If the file 'path' is part of a package, then the filename isn't
130*cda5da8dSAndroid Build Coastguard Worker    # enough to uniquely identify it.  Try to do the right thing by
131*cda5da8dSAndroid Build Coastguard Worker    # looking in sys.path for the longest matching prefix.  We'll
132*cda5da8dSAndroid Build Coastguard Worker    # assume that the rest is the package name.
133*cda5da8dSAndroid Build Coastguard Worker
134*cda5da8dSAndroid Build Coastguard Worker    comparepath = os.path.normcase(path)
135*cda5da8dSAndroid Build Coastguard Worker    longest = ""
136*cda5da8dSAndroid Build Coastguard Worker    for dir in sys.path:
137*cda5da8dSAndroid Build Coastguard Worker        dir = os.path.normcase(dir)
138*cda5da8dSAndroid Build Coastguard Worker        if comparepath.startswith(dir) and comparepath[len(dir)] == os.sep:
139*cda5da8dSAndroid Build Coastguard Worker            if len(dir) > len(longest):
140*cda5da8dSAndroid Build Coastguard Worker                longest = dir
141*cda5da8dSAndroid Build Coastguard Worker
142*cda5da8dSAndroid Build Coastguard Worker    if longest:
143*cda5da8dSAndroid Build Coastguard Worker        base = path[len(longest) + 1:]
144*cda5da8dSAndroid Build Coastguard Worker    else:
145*cda5da8dSAndroid Build Coastguard Worker        base = path
146*cda5da8dSAndroid Build Coastguard Worker    # the drive letter is never part of the module name
147*cda5da8dSAndroid Build Coastguard Worker    drive, base = os.path.splitdrive(base)
148*cda5da8dSAndroid Build Coastguard Worker    base = base.replace(os.sep, ".")
149*cda5da8dSAndroid Build Coastguard Worker    if os.altsep:
150*cda5da8dSAndroid Build Coastguard Worker        base = base.replace(os.altsep, ".")
151*cda5da8dSAndroid Build Coastguard Worker    filename, ext = os.path.splitext(base)
152*cda5da8dSAndroid Build Coastguard Worker    return filename.lstrip(".")
153*cda5da8dSAndroid Build Coastguard Worker
154*cda5da8dSAndroid Build Coastguard Workerclass CoverageResults:
155*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, counts=None, calledfuncs=None, infile=None,
156*cda5da8dSAndroid Build Coastguard Worker                 callers=None, outfile=None):
157*cda5da8dSAndroid Build Coastguard Worker        self.counts = counts
158*cda5da8dSAndroid Build Coastguard Worker        if self.counts is None:
159*cda5da8dSAndroid Build Coastguard Worker            self.counts = {}
160*cda5da8dSAndroid Build Coastguard Worker        self.counter = self.counts.copy() # map (filename, lineno) to count
161*cda5da8dSAndroid Build Coastguard Worker        self.calledfuncs = calledfuncs
162*cda5da8dSAndroid Build Coastguard Worker        if self.calledfuncs is None:
163*cda5da8dSAndroid Build Coastguard Worker            self.calledfuncs = {}
164*cda5da8dSAndroid Build Coastguard Worker        self.calledfuncs = self.calledfuncs.copy()
165*cda5da8dSAndroid Build Coastguard Worker        self.callers = callers
166*cda5da8dSAndroid Build Coastguard Worker        if self.callers is None:
167*cda5da8dSAndroid Build Coastguard Worker            self.callers = {}
168*cda5da8dSAndroid Build Coastguard Worker        self.callers = self.callers.copy()
169*cda5da8dSAndroid Build Coastguard Worker        self.infile = infile
170*cda5da8dSAndroid Build Coastguard Worker        self.outfile = outfile
171*cda5da8dSAndroid Build Coastguard Worker        if self.infile:
172*cda5da8dSAndroid Build Coastguard Worker            # Try to merge existing counts file.
173*cda5da8dSAndroid Build Coastguard Worker            try:
174*cda5da8dSAndroid Build Coastguard Worker                with open(self.infile, 'rb') as f:
175*cda5da8dSAndroid Build Coastguard Worker                    counts, calledfuncs, callers = pickle.load(f)
176*cda5da8dSAndroid Build Coastguard Worker                self.update(self.__class__(counts, calledfuncs, callers=callers))
177*cda5da8dSAndroid Build Coastguard Worker            except (OSError, EOFError, ValueError) as err:
178*cda5da8dSAndroid Build Coastguard Worker                print(("Skipping counts file %r: %s"
179*cda5da8dSAndroid Build Coastguard Worker                                      % (self.infile, err)), file=sys.stderr)
180*cda5da8dSAndroid Build Coastguard Worker
181*cda5da8dSAndroid Build Coastguard Worker    def is_ignored_filename(self, filename):
182*cda5da8dSAndroid Build Coastguard Worker        """Return True if the filename does not refer to a file
183*cda5da8dSAndroid Build Coastguard Worker        we want to have reported.
184*cda5da8dSAndroid Build Coastguard Worker        """
185*cda5da8dSAndroid Build Coastguard Worker        return filename.startswith('<') and filename.endswith('>')
186*cda5da8dSAndroid Build Coastguard Worker
187*cda5da8dSAndroid Build Coastguard Worker    def update(self, other):
188*cda5da8dSAndroid Build Coastguard Worker        """Merge in the data from another CoverageResults"""
189*cda5da8dSAndroid Build Coastguard Worker        counts = self.counts
190*cda5da8dSAndroid Build Coastguard Worker        calledfuncs = self.calledfuncs
191*cda5da8dSAndroid Build Coastguard Worker        callers = self.callers
192*cda5da8dSAndroid Build Coastguard Worker        other_counts = other.counts
193*cda5da8dSAndroid Build Coastguard Worker        other_calledfuncs = other.calledfuncs
194*cda5da8dSAndroid Build Coastguard Worker        other_callers = other.callers
195*cda5da8dSAndroid Build Coastguard Worker
196*cda5da8dSAndroid Build Coastguard Worker        for key in other_counts:
197*cda5da8dSAndroid Build Coastguard Worker            counts[key] = counts.get(key, 0) + other_counts[key]
198*cda5da8dSAndroid Build Coastguard Worker
199*cda5da8dSAndroid Build Coastguard Worker        for key in other_calledfuncs:
200*cda5da8dSAndroid Build Coastguard Worker            calledfuncs[key] = 1
201*cda5da8dSAndroid Build Coastguard Worker
202*cda5da8dSAndroid Build Coastguard Worker        for key in other_callers:
203*cda5da8dSAndroid Build Coastguard Worker            callers[key] = 1
204*cda5da8dSAndroid Build Coastguard Worker
205*cda5da8dSAndroid Build Coastguard Worker    def write_results(self, show_missing=True, summary=False, coverdir=None):
206*cda5da8dSAndroid Build Coastguard Worker        """
207*cda5da8dSAndroid Build Coastguard Worker        Write the coverage results.
208*cda5da8dSAndroid Build Coastguard Worker
209*cda5da8dSAndroid Build Coastguard Worker        :param show_missing: Show lines that had no hits.
210*cda5da8dSAndroid Build Coastguard Worker        :param summary: Include coverage summary per module.
211*cda5da8dSAndroid Build Coastguard Worker        :param coverdir: If None, the results of each module are placed in its
212*cda5da8dSAndroid Build Coastguard Worker                         directory, otherwise it is included in the directory
213*cda5da8dSAndroid Build Coastguard Worker                         specified.
214*cda5da8dSAndroid Build Coastguard Worker        """
215*cda5da8dSAndroid Build Coastguard Worker        if self.calledfuncs:
216*cda5da8dSAndroid Build Coastguard Worker            print()
217*cda5da8dSAndroid Build Coastguard Worker            print("functions called:")
218*cda5da8dSAndroid Build Coastguard Worker            calls = self.calledfuncs
219*cda5da8dSAndroid Build Coastguard Worker            for filename, modulename, funcname in sorted(calls):
220*cda5da8dSAndroid Build Coastguard Worker                print(("filename: %s, modulename: %s, funcname: %s"
221*cda5da8dSAndroid Build Coastguard Worker                       % (filename, modulename, funcname)))
222*cda5da8dSAndroid Build Coastguard Worker
223*cda5da8dSAndroid Build Coastguard Worker        if self.callers:
224*cda5da8dSAndroid Build Coastguard Worker            print()
225*cda5da8dSAndroid Build Coastguard Worker            print("calling relationships:")
226*cda5da8dSAndroid Build Coastguard Worker            lastfile = lastcfile = ""
227*cda5da8dSAndroid Build Coastguard Worker            for ((pfile, pmod, pfunc), (cfile, cmod, cfunc)) \
228*cda5da8dSAndroid Build Coastguard Worker                    in sorted(self.callers):
229*cda5da8dSAndroid Build Coastguard Worker                if pfile != lastfile:
230*cda5da8dSAndroid Build Coastguard Worker                    print()
231*cda5da8dSAndroid Build Coastguard Worker                    print("***", pfile, "***")
232*cda5da8dSAndroid Build Coastguard Worker                    lastfile = pfile
233*cda5da8dSAndroid Build Coastguard Worker                    lastcfile = ""
234*cda5da8dSAndroid Build Coastguard Worker                if cfile != pfile and lastcfile != cfile:
235*cda5da8dSAndroid Build Coastguard Worker                    print("  -->", cfile)
236*cda5da8dSAndroid Build Coastguard Worker                    lastcfile = cfile
237*cda5da8dSAndroid Build Coastguard Worker                print("    %s.%s -> %s.%s" % (pmod, pfunc, cmod, cfunc))
238*cda5da8dSAndroid Build Coastguard Worker
239*cda5da8dSAndroid Build Coastguard Worker        # turn the counts data ("(filename, lineno) = count") into something
240*cda5da8dSAndroid Build Coastguard Worker        # accessible on a per-file basis
241*cda5da8dSAndroid Build Coastguard Worker        per_file = {}
242*cda5da8dSAndroid Build Coastguard Worker        for filename, lineno in self.counts:
243*cda5da8dSAndroid Build Coastguard Worker            lines_hit = per_file[filename] = per_file.get(filename, {})
244*cda5da8dSAndroid Build Coastguard Worker            lines_hit[lineno] = self.counts[(filename, lineno)]
245*cda5da8dSAndroid Build Coastguard Worker
246*cda5da8dSAndroid Build Coastguard Worker        # accumulate summary info, if needed
247*cda5da8dSAndroid Build Coastguard Worker        sums = {}
248*cda5da8dSAndroid Build Coastguard Worker
249*cda5da8dSAndroid Build Coastguard Worker        for filename, count in per_file.items():
250*cda5da8dSAndroid Build Coastguard Worker            if self.is_ignored_filename(filename):
251*cda5da8dSAndroid Build Coastguard Worker                continue
252*cda5da8dSAndroid Build Coastguard Worker
253*cda5da8dSAndroid Build Coastguard Worker            if filename.endswith(".pyc"):
254*cda5da8dSAndroid Build Coastguard Worker                filename = filename[:-1]
255*cda5da8dSAndroid Build Coastguard Worker
256*cda5da8dSAndroid Build Coastguard Worker            if coverdir is None:
257*cda5da8dSAndroid Build Coastguard Worker                dir = os.path.dirname(os.path.abspath(filename))
258*cda5da8dSAndroid Build Coastguard Worker                modulename = _modname(filename)
259*cda5da8dSAndroid Build Coastguard Worker            else:
260*cda5da8dSAndroid Build Coastguard Worker                dir = coverdir
261*cda5da8dSAndroid Build Coastguard Worker                if not os.path.exists(dir):
262*cda5da8dSAndroid Build Coastguard Worker                    os.makedirs(dir)
263*cda5da8dSAndroid Build Coastguard Worker                modulename = _fullmodname(filename)
264*cda5da8dSAndroid Build Coastguard Worker
265*cda5da8dSAndroid Build Coastguard Worker            # If desired, get a list of the line numbers which represent
266*cda5da8dSAndroid Build Coastguard Worker            # executable content (returned as a dict for better lookup speed)
267*cda5da8dSAndroid Build Coastguard Worker            if show_missing:
268*cda5da8dSAndroid Build Coastguard Worker                lnotab = _find_executable_linenos(filename)
269*cda5da8dSAndroid Build Coastguard Worker            else:
270*cda5da8dSAndroid Build Coastguard Worker                lnotab = {}
271*cda5da8dSAndroid Build Coastguard Worker            source = linecache.getlines(filename)
272*cda5da8dSAndroid Build Coastguard Worker            coverpath = os.path.join(dir, modulename + ".cover")
273*cda5da8dSAndroid Build Coastguard Worker            with open(filename, 'rb') as fp:
274*cda5da8dSAndroid Build Coastguard Worker                encoding, _ = tokenize.detect_encoding(fp.readline)
275*cda5da8dSAndroid Build Coastguard Worker            n_hits, n_lines = self.write_results_file(coverpath, source,
276*cda5da8dSAndroid Build Coastguard Worker                                                      lnotab, count, encoding)
277*cda5da8dSAndroid Build Coastguard Worker            if summary and n_lines:
278*cda5da8dSAndroid Build Coastguard Worker                percent = int(100 * n_hits / n_lines)
279*cda5da8dSAndroid Build Coastguard Worker                sums[modulename] = n_lines, percent, modulename, filename
280*cda5da8dSAndroid Build Coastguard Worker
281*cda5da8dSAndroid Build Coastguard Worker
282*cda5da8dSAndroid Build Coastguard Worker        if summary and sums:
283*cda5da8dSAndroid Build Coastguard Worker            print("lines   cov%   module   (path)")
284*cda5da8dSAndroid Build Coastguard Worker            for m in sorted(sums):
285*cda5da8dSAndroid Build Coastguard Worker                n_lines, percent, modulename, filename = sums[m]
286*cda5da8dSAndroid Build Coastguard Worker                print("%5d   %3d%%   %s   (%s)" % sums[m])
287*cda5da8dSAndroid Build Coastguard Worker
288*cda5da8dSAndroid Build Coastguard Worker        if self.outfile:
289*cda5da8dSAndroid Build Coastguard Worker            # try and store counts and module info into self.outfile
290*cda5da8dSAndroid Build Coastguard Worker            try:
291*cda5da8dSAndroid Build Coastguard Worker                with open(self.outfile, 'wb') as f:
292*cda5da8dSAndroid Build Coastguard Worker                    pickle.dump((self.counts, self.calledfuncs, self.callers),
293*cda5da8dSAndroid Build Coastguard Worker                                f, 1)
294*cda5da8dSAndroid Build Coastguard Worker            except OSError as err:
295*cda5da8dSAndroid Build Coastguard Worker                print("Can't save counts files because %s" % err, file=sys.stderr)
296*cda5da8dSAndroid Build Coastguard Worker
297*cda5da8dSAndroid Build Coastguard Worker    def write_results_file(self, path, lines, lnotab, lines_hit, encoding=None):
298*cda5da8dSAndroid Build Coastguard Worker        """Return a coverage results file in path."""
299*cda5da8dSAndroid Build Coastguard Worker        # ``lnotab`` is a dict of executable lines, or a line number "table"
300*cda5da8dSAndroid Build Coastguard Worker
301*cda5da8dSAndroid Build Coastguard Worker        try:
302*cda5da8dSAndroid Build Coastguard Worker            outfile = open(path, "w", encoding=encoding)
303*cda5da8dSAndroid Build Coastguard Worker        except OSError as err:
304*cda5da8dSAndroid Build Coastguard Worker            print(("trace: Could not open %r for writing: %s "
305*cda5da8dSAndroid Build Coastguard Worker                                  "- skipping" % (path, err)), file=sys.stderr)
306*cda5da8dSAndroid Build Coastguard Worker            return 0, 0
307*cda5da8dSAndroid Build Coastguard Worker
308*cda5da8dSAndroid Build Coastguard Worker        n_lines = 0
309*cda5da8dSAndroid Build Coastguard Worker        n_hits = 0
310*cda5da8dSAndroid Build Coastguard Worker        with outfile:
311*cda5da8dSAndroid Build Coastguard Worker            for lineno, line in enumerate(lines, 1):
312*cda5da8dSAndroid Build Coastguard Worker                # do the blank/comment match to try to mark more lines
313*cda5da8dSAndroid Build Coastguard Worker                # (help the reader find stuff that hasn't been covered)
314*cda5da8dSAndroid Build Coastguard Worker                if lineno in lines_hit:
315*cda5da8dSAndroid Build Coastguard Worker                    outfile.write("%5d: " % lines_hit[lineno])
316*cda5da8dSAndroid Build Coastguard Worker                    n_hits += 1
317*cda5da8dSAndroid Build Coastguard Worker                    n_lines += 1
318*cda5da8dSAndroid Build Coastguard Worker                elif lineno in lnotab and not PRAGMA_NOCOVER in line:
319*cda5da8dSAndroid Build Coastguard Worker                    # Highlight never-executed lines, unless the line contains
320*cda5da8dSAndroid Build Coastguard Worker                    # #pragma: NO COVER
321*cda5da8dSAndroid Build Coastguard Worker                    outfile.write(">>>>>> ")
322*cda5da8dSAndroid Build Coastguard Worker                    n_lines += 1
323*cda5da8dSAndroid Build Coastguard Worker                else:
324*cda5da8dSAndroid Build Coastguard Worker                    outfile.write("       ")
325*cda5da8dSAndroid Build Coastguard Worker                outfile.write(line.expandtabs(8))
326*cda5da8dSAndroid Build Coastguard Worker
327*cda5da8dSAndroid Build Coastguard Worker        return n_hits, n_lines
328*cda5da8dSAndroid Build Coastguard Worker
329*cda5da8dSAndroid Build Coastguard Workerdef _find_lines_from_code(code, strs):
330*cda5da8dSAndroid Build Coastguard Worker    """Return dict where keys are lines in the line number table."""
331*cda5da8dSAndroid Build Coastguard Worker    linenos = {}
332*cda5da8dSAndroid Build Coastguard Worker
333*cda5da8dSAndroid Build Coastguard Worker    for _, lineno in dis.findlinestarts(code):
334*cda5da8dSAndroid Build Coastguard Worker        if lineno not in strs:
335*cda5da8dSAndroid Build Coastguard Worker            linenos[lineno] = 1
336*cda5da8dSAndroid Build Coastguard Worker
337*cda5da8dSAndroid Build Coastguard Worker    return linenos
338*cda5da8dSAndroid Build Coastguard Worker
339*cda5da8dSAndroid Build Coastguard Workerdef _find_lines(code, strs):
340*cda5da8dSAndroid Build Coastguard Worker    """Return lineno dict for all code objects reachable from code."""
341*cda5da8dSAndroid Build Coastguard Worker    # get all of the lineno information from the code of this scope level
342*cda5da8dSAndroid Build Coastguard Worker    linenos = _find_lines_from_code(code, strs)
343*cda5da8dSAndroid Build Coastguard Worker
344*cda5da8dSAndroid Build Coastguard Worker    # and check the constants for references to other code objects
345*cda5da8dSAndroid Build Coastguard Worker    for c in code.co_consts:
346*cda5da8dSAndroid Build Coastguard Worker        if inspect.iscode(c):
347*cda5da8dSAndroid Build Coastguard Worker            # find another code object, so recurse into it
348*cda5da8dSAndroid Build Coastguard Worker            linenos.update(_find_lines(c, strs))
349*cda5da8dSAndroid Build Coastguard Worker    return linenos
350*cda5da8dSAndroid Build Coastguard Worker
351*cda5da8dSAndroid Build Coastguard Workerdef _find_strings(filename, encoding=None):
352*cda5da8dSAndroid Build Coastguard Worker    """Return a dict of possible docstring positions.
353*cda5da8dSAndroid Build Coastguard Worker
354*cda5da8dSAndroid Build Coastguard Worker    The dict maps line numbers to strings.  There is an entry for
355*cda5da8dSAndroid Build Coastguard Worker    line that contains only a string or a part of a triple-quoted
356*cda5da8dSAndroid Build Coastguard Worker    string.
357*cda5da8dSAndroid Build Coastguard Worker    """
358*cda5da8dSAndroid Build Coastguard Worker    d = {}
359*cda5da8dSAndroid Build Coastguard Worker    # If the first token is a string, then it's the module docstring.
360*cda5da8dSAndroid Build Coastguard Worker    # Add this special case so that the test in the loop passes.
361*cda5da8dSAndroid Build Coastguard Worker    prev_ttype = token.INDENT
362*cda5da8dSAndroid Build Coastguard Worker    with open(filename, encoding=encoding) as f:
363*cda5da8dSAndroid Build Coastguard Worker        tok = tokenize.generate_tokens(f.readline)
364*cda5da8dSAndroid Build Coastguard Worker        for ttype, tstr, start, end, line in tok:
365*cda5da8dSAndroid Build Coastguard Worker            if ttype == token.STRING:
366*cda5da8dSAndroid Build Coastguard Worker                if prev_ttype == token.INDENT:
367*cda5da8dSAndroid Build Coastguard Worker                    sline, scol = start
368*cda5da8dSAndroid Build Coastguard Worker                    eline, ecol = end
369*cda5da8dSAndroid Build Coastguard Worker                    for i in range(sline, eline + 1):
370*cda5da8dSAndroid Build Coastguard Worker                        d[i] = 1
371*cda5da8dSAndroid Build Coastguard Worker            prev_ttype = ttype
372*cda5da8dSAndroid Build Coastguard Worker    return d
373*cda5da8dSAndroid Build Coastguard Worker
374*cda5da8dSAndroid Build Coastguard Workerdef _find_executable_linenos(filename):
375*cda5da8dSAndroid Build Coastguard Worker    """Return dict where keys are line numbers in the line number table."""
376*cda5da8dSAndroid Build Coastguard Worker    try:
377*cda5da8dSAndroid Build Coastguard Worker        with tokenize.open(filename) as f:
378*cda5da8dSAndroid Build Coastguard Worker            prog = f.read()
379*cda5da8dSAndroid Build Coastguard Worker            encoding = f.encoding
380*cda5da8dSAndroid Build Coastguard Worker    except OSError as err:
381*cda5da8dSAndroid Build Coastguard Worker        print(("Not printing coverage data for %r: %s"
382*cda5da8dSAndroid Build Coastguard Worker                              % (filename, err)), file=sys.stderr)
383*cda5da8dSAndroid Build Coastguard Worker        return {}
384*cda5da8dSAndroid Build Coastguard Worker    code = compile(prog, filename, "exec")
385*cda5da8dSAndroid Build Coastguard Worker    strs = _find_strings(filename, encoding)
386*cda5da8dSAndroid Build Coastguard Worker    return _find_lines(code, strs)
387*cda5da8dSAndroid Build Coastguard Worker
388*cda5da8dSAndroid Build Coastguard Workerclass Trace:
389*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, count=1, trace=1, countfuncs=0, countcallers=0,
390*cda5da8dSAndroid Build Coastguard Worker                 ignoremods=(), ignoredirs=(), infile=None, outfile=None,
391*cda5da8dSAndroid Build Coastguard Worker                 timing=False):
392*cda5da8dSAndroid Build Coastguard Worker        """
393*cda5da8dSAndroid Build Coastguard Worker        @param count true iff it should count number of times each
394*cda5da8dSAndroid Build Coastguard Worker                     line is executed
395*cda5da8dSAndroid Build Coastguard Worker        @param trace true iff it should print out each line that is
396*cda5da8dSAndroid Build Coastguard Worker                     being counted
397*cda5da8dSAndroid Build Coastguard Worker        @param countfuncs true iff it should just output a list of
398*cda5da8dSAndroid Build Coastguard Worker                     (filename, modulename, funcname,) for functions
399*cda5da8dSAndroid Build Coastguard Worker                     that were called at least once;  This overrides
400*cda5da8dSAndroid Build Coastguard Worker                     `count' and `trace'
401*cda5da8dSAndroid Build Coastguard Worker        @param ignoremods a list of the names of modules to ignore
402*cda5da8dSAndroid Build Coastguard Worker        @param ignoredirs a list of the names of directories to ignore
403*cda5da8dSAndroid Build Coastguard Worker                     all of the (recursive) contents of
404*cda5da8dSAndroid Build Coastguard Worker        @param infile file from which to read stored counts to be
405*cda5da8dSAndroid Build Coastguard Worker                     added into the results
406*cda5da8dSAndroid Build Coastguard Worker        @param outfile file in which to write the results
407*cda5da8dSAndroid Build Coastguard Worker        @param timing true iff timing information be displayed
408*cda5da8dSAndroid Build Coastguard Worker        """
409*cda5da8dSAndroid Build Coastguard Worker        self.infile = infile
410*cda5da8dSAndroid Build Coastguard Worker        self.outfile = outfile
411*cda5da8dSAndroid Build Coastguard Worker        self.ignore = _Ignore(ignoremods, ignoredirs)
412*cda5da8dSAndroid Build Coastguard Worker        self.counts = {}   # keys are (filename, linenumber)
413*cda5da8dSAndroid Build Coastguard Worker        self.pathtobasename = {} # for memoizing os.path.basename
414*cda5da8dSAndroid Build Coastguard Worker        self.donothing = 0
415*cda5da8dSAndroid Build Coastguard Worker        self.trace = trace
416*cda5da8dSAndroid Build Coastguard Worker        self._calledfuncs = {}
417*cda5da8dSAndroid Build Coastguard Worker        self._callers = {}
418*cda5da8dSAndroid Build Coastguard Worker        self._caller_cache = {}
419*cda5da8dSAndroid Build Coastguard Worker        self.start_time = None
420*cda5da8dSAndroid Build Coastguard Worker        if timing:
421*cda5da8dSAndroid Build Coastguard Worker            self.start_time = _time()
422*cda5da8dSAndroid Build Coastguard Worker        if countcallers:
423*cda5da8dSAndroid Build Coastguard Worker            self.globaltrace = self.globaltrace_trackcallers
424*cda5da8dSAndroid Build Coastguard Worker        elif countfuncs:
425*cda5da8dSAndroid Build Coastguard Worker            self.globaltrace = self.globaltrace_countfuncs
426*cda5da8dSAndroid Build Coastguard Worker        elif trace and count:
427*cda5da8dSAndroid Build Coastguard Worker            self.globaltrace = self.globaltrace_lt
428*cda5da8dSAndroid Build Coastguard Worker            self.localtrace = self.localtrace_trace_and_count
429*cda5da8dSAndroid Build Coastguard Worker        elif trace:
430*cda5da8dSAndroid Build Coastguard Worker            self.globaltrace = self.globaltrace_lt
431*cda5da8dSAndroid Build Coastguard Worker            self.localtrace = self.localtrace_trace
432*cda5da8dSAndroid Build Coastguard Worker        elif count:
433*cda5da8dSAndroid Build Coastguard Worker            self.globaltrace = self.globaltrace_lt
434*cda5da8dSAndroid Build Coastguard Worker            self.localtrace = self.localtrace_count
435*cda5da8dSAndroid Build Coastguard Worker        else:
436*cda5da8dSAndroid Build Coastguard Worker            # Ahem -- do nothing?  Okay.
437*cda5da8dSAndroid Build Coastguard Worker            self.donothing = 1
438*cda5da8dSAndroid Build Coastguard Worker
439*cda5da8dSAndroid Build Coastguard Worker    def run(self, cmd):
440*cda5da8dSAndroid Build Coastguard Worker        import __main__
441*cda5da8dSAndroid Build Coastguard Worker        dict = __main__.__dict__
442*cda5da8dSAndroid Build Coastguard Worker        self.runctx(cmd, dict, dict)
443*cda5da8dSAndroid Build Coastguard Worker
444*cda5da8dSAndroid Build Coastguard Worker    def runctx(self, cmd, globals=None, locals=None):
445*cda5da8dSAndroid Build Coastguard Worker        if globals is None: globals = {}
446*cda5da8dSAndroid Build Coastguard Worker        if locals is None: locals = {}
447*cda5da8dSAndroid Build Coastguard Worker        if not self.donothing:
448*cda5da8dSAndroid Build Coastguard Worker            threading.settrace(self.globaltrace)
449*cda5da8dSAndroid Build Coastguard Worker            sys.settrace(self.globaltrace)
450*cda5da8dSAndroid Build Coastguard Worker        try:
451*cda5da8dSAndroid Build Coastguard Worker            exec(cmd, globals, locals)
452*cda5da8dSAndroid Build Coastguard Worker        finally:
453*cda5da8dSAndroid Build Coastguard Worker            if not self.donothing:
454*cda5da8dSAndroid Build Coastguard Worker                sys.settrace(None)
455*cda5da8dSAndroid Build Coastguard Worker                threading.settrace(None)
456*cda5da8dSAndroid Build Coastguard Worker
457*cda5da8dSAndroid Build Coastguard Worker    def runfunc(self, func, /, *args, **kw):
458*cda5da8dSAndroid Build Coastguard Worker        result = None
459*cda5da8dSAndroid Build Coastguard Worker        if not self.donothing:
460*cda5da8dSAndroid Build Coastguard Worker            sys.settrace(self.globaltrace)
461*cda5da8dSAndroid Build Coastguard Worker        try:
462*cda5da8dSAndroid Build Coastguard Worker            result = func(*args, **kw)
463*cda5da8dSAndroid Build Coastguard Worker        finally:
464*cda5da8dSAndroid Build Coastguard Worker            if not self.donothing:
465*cda5da8dSAndroid Build Coastguard Worker                sys.settrace(None)
466*cda5da8dSAndroid Build Coastguard Worker        return result
467*cda5da8dSAndroid Build Coastguard Worker
468*cda5da8dSAndroid Build Coastguard Worker    def file_module_function_of(self, frame):
469*cda5da8dSAndroid Build Coastguard Worker        code = frame.f_code
470*cda5da8dSAndroid Build Coastguard Worker        filename = code.co_filename
471*cda5da8dSAndroid Build Coastguard Worker        if filename:
472*cda5da8dSAndroid Build Coastguard Worker            modulename = _modname(filename)
473*cda5da8dSAndroid Build Coastguard Worker        else:
474*cda5da8dSAndroid Build Coastguard Worker            modulename = None
475*cda5da8dSAndroid Build Coastguard Worker
476*cda5da8dSAndroid Build Coastguard Worker        funcname = code.co_name
477*cda5da8dSAndroid Build Coastguard Worker        clsname = None
478*cda5da8dSAndroid Build Coastguard Worker        if code in self._caller_cache:
479*cda5da8dSAndroid Build Coastguard Worker            if self._caller_cache[code] is not None:
480*cda5da8dSAndroid Build Coastguard Worker                clsname = self._caller_cache[code]
481*cda5da8dSAndroid Build Coastguard Worker        else:
482*cda5da8dSAndroid Build Coastguard Worker            self._caller_cache[code] = None
483*cda5da8dSAndroid Build Coastguard Worker            ## use of gc.get_referrers() was suggested by Michael Hudson
484*cda5da8dSAndroid Build Coastguard Worker            # all functions which refer to this code object
485*cda5da8dSAndroid Build Coastguard Worker            funcs = [f for f in gc.get_referrers(code)
486*cda5da8dSAndroid Build Coastguard Worker                         if inspect.isfunction(f)]
487*cda5da8dSAndroid Build Coastguard Worker            # require len(func) == 1 to avoid ambiguity caused by calls to
488*cda5da8dSAndroid Build Coastguard Worker            # new.function(): "In the face of ambiguity, refuse the
489*cda5da8dSAndroid Build Coastguard Worker            # temptation to guess."
490*cda5da8dSAndroid Build Coastguard Worker            if len(funcs) == 1:
491*cda5da8dSAndroid Build Coastguard Worker                dicts = [d for d in gc.get_referrers(funcs[0])
492*cda5da8dSAndroid Build Coastguard Worker                             if isinstance(d, dict)]
493*cda5da8dSAndroid Build Coastguard Worker                if len(dicts) == 1:
494*cda5da8dSAndroid Build Coastguard Worker                    classes = [c for c in gc.get_referrers(dicts[0])
495*cda5da8dSAndroid Build Coastguard Worker                                   if hasattr(c, "__bases__")]
496*cda5da8dSAndroid Build Coastguard Worker                    if len(classes) == 1:
497*cda5da8dSAndroid Build Coastguard Worker                        # ditto for new.classobj()
498*cda5da8dSAndroid Build Coastguard Worker                        clsname = classes[0].__name__
499*cda5da8dSAndroid Build Coastguard Worker                        # cache the result - assumption is that new.* is
500*cda5da8dSAndroid Build Coastguard Worker                        # not called later to disturb this relationship
501*cda5da8dSAndroid Build Coastguard Worker                        # _caller_cache could be flushed if functions in
502*cda5da8dSAndroid Build Coastguard Worker                        # the new module get called.
503*cda5da8dSAndroid Build Coastguard Worker                        self._caller_cache[code] = clsname
504*cda5da8dSAndroid Build Coastguard Worker        if clsname is not None:
505*cda5da8dSAndroid Build Coastguard Worker            funcname = "%s.%s" % (clsname, funcname)
506*cda5da8dSAndroid Build Coastguard Worker
507*cda5da8dSAndroid Build Coastguard Worker        return filename, modulename, funcname
508*cda5da8dSAndroid Build Coastguard Worker
509*cda5da8dSAndroid Build Coastguard Worker    def globaltrace_trackcallers(self, frame, why, arg):
510*cda5da8dSAndroid Build Coastguard Worker        """Handler for call events.
511*cda5da8dSAndroid Build Coastguard Worker
512*cda5da8dSAndroid Build Coastguard Worker        Adds information about who called who to the self._callers dict.
513*cda5da8dSAndroid Build Coastguard Worker        """
514*cda5da8dSAndroid Build Coastguard Worker        if why == 'call':
515*cda5da8dSAndroid Build Coastguard Worker            # XXX Should do a better job of identifying methods
516*cda5da8dSAndroid Build Coastguard Worker            this_func = self.file_module_function_of(frame)
517*cda5da8dSAndroid Build Coastguard Worker            parent_func = self.file_module_function_of(frame.f_back)
518*cda5da8dSAndroid Build Coastguard Worker            self._callers[(parent_func, this_func)] = 1
519*cda5da8dSAndroid Build Coastguard Worker
520*cda5da8dSAndroid Build Coastguard Worker    def globaltrace_countfuncs(self, frame, why, arg):
521*cda5da8dSAndroid Build Coastguard Worker        """Handler for call events.
522*cda5da8dSAndroid Build Coastguard Worker
523*cda5da8dSAndroid Build Coastguard Worker        Adds (filename, modulename, funcname) to the self._calledfuncs dict.
524*cda5da8dSAndroid Build Coastguard Worker        """
525*cda5da8dSAndroid Build Coastguard Worker        if why == 'call':
526*cda5da8dSAndroid Build Coastguard Worker            this_func = self.file_module_function_of(frame)
527*cda5da8dSAndroid Build Coastguard Worker            self._calledfuncs[this_func] = 1
528*cda5da8dSAndroid Build Coastguard Worker
529*cda5da8dSAndroid Build Coastguard Worker    def globaltrace_lt(self, frame, why, arg):
530*cda5da8dSAndroid Build Coastguard Worker        """Handler for call events.
531*cda5da8dSAndroid Build Coastguard Worker
532*cda5da8dSAndroid Build Coastguard Worker        If the code block being entered is to be ignored, returns `None',
533*cda5da8dSAndroid Build Coastguard Worker        else returns self.localtrace.
534*cda5da8dSAndroid Build Coastguard Worker        """
535*cda5da8dSAndroid Build Coastguard Worker        if why == 'call':
536*cda5da8dSAndroid Build Coastguard Worker            code = frame.f_code
537*cda5da8dSAndroid Build Coastguard Worker            filename = frame.f_globals.get('__file__', None)
538*cda5da8dSAndroid Build Coastguard Worker            if filename:
539*cda5da8dSAndroid Build Coastguard Worker                # XXX _modname() doesn't work right for packages, so
540*cda5da8dSAndroid Build Coastguard Worker                # the ignore support won't work right for packages
541*cda5da8dSAndroid Build Coastguard Worker                modulename = _modname(filename)
542*cda5da8dSAndroid Build Coastguard Worker                if modulename is not None:
543*cda5da8dSAndroid Build Coastguard Worker                    ignore_it = self.ignore.names(filename, modulename)
544*cda5da8dSAndroid Build Coastguard Worker                    if not ignore_it:
545*cda5da8dSAndroid Build Coastguard Worker                        if self.trace:
546*cda5da8dSAndroid Build Coastguard Worker                            print((" --- modulename: %s, funcname: %s"
547*cda5da8dSAndroid Build Coastguard Worker                                   % (modulename, code.co_name)))
548*cda5da8dSAndroid Build Coastguard Worker                        return self.localtrace
549*cda5da8dSAndroid Build Coastguard Worker            else:
550*cda5da8dSAndroid Build Coastguard Worker                return None
551*cda5da8dSAndroid Build Coastguard Worker
552*cda5da8dSAndroid Build Coastguard Worker    def localtrace_trace_and_count(self, frame, why, arg):
553*cda5da8dSAndroid Build Coastguard Worker        if why == "line":
554*cda5da8dSAndroid Build Coastguard Worker            # record the file name and line number of every trace
555*cda5da8dSAndroid Build Coastguard Worker            filename = frame.f_code.co_filename
556*cda5da8dSAndroid Build Coastguard Worker            lineno = frame.f_lineno
557*cda5da8dSAndroid Build Coastguard Worker            key = filename, lineno
558*cda5da8dSAndroid Build Coastguard Worker            self.counts[key] = self.counts.get(key, 0) + 1
559*cda5da8dSAndroid Build Coastguard Worker
560*cda5da8dSAndroid Build Coastguard Worker            if self.start_time:
561*cda5da8dSAndroid Build Coastguard Worker                print('%.2f' % (_time() - self.start_time), end=' ')
562*cda5da8dSAndroid Build Coastguard Worker            bname = os.path.basename(filename)
563*cda5da8dSAndroid Build Coastguard Worker            print("%s(%d): %s" % (bname, lineno,
564*cda5da8dSAndroid Build Coastguard Worker                                  linecache.getline(filename, lineno)), end='')
565*cda5da8dSAndroid Build Coastguard Worker        return self.localtrace
566*cda5da8dSAndroid Build Coastguard Worker
567*cda5da8dSAndroid Build Coastguard Worker    def localtrace_trace(self, frame, why, arg):
568*cda5da8dSAndroid Build Coastguard Worker        if why == "line":
569*cda5da8dSAndroid Build Coastguard Worker            # record the file name and line number of every trace
570*cda5da8dSAndroid Build Coastguard Worker            filename = frame.f_code.co_filename
571*cda5da8dSAndroid Build Coastguard Worker            lineno = frame.f_lineno
572*cda5da8dSAndroid Build Coastguard Worker
573*cda5da8dSAndroid Build Coastguard Worker            if self.start_time:
574*cda5da8dSAndroid Build Coastguard Worker                print('%.2f' % (_time() - self.start_time), end=' ')
575*cda5da8dSAndroid Build Coastguard Worker            bname = os.path.basename(filename)
576*cda5da8dSAndroid Build Coastguard Worker            print("%s(%d): %s" % (bname, lineno,
577*cda5da8dSAndroid Build Coastguard Worker                                  linecache.getline(filename, lineno)), end='')
578*cda5da8dSAndroid Build Coastguard Worker        return self.localtrace
579*cda5da8dSAndroid Build Coastguard Worker
580*cda5da8dSAndroid Build Coastguard Worker    def localtrace_count(self, frame, why, arg):
581*cda5da8dSAndroid Build Coastguard Worker        if why == "line":
582*cda5da8dSAndroid Build Coastguard Worker            filename = frame.f_code.co_filename
583*cda5da8dSAndroid Build Coastguard Worker            lineno = frame.f_lineno
584*cda5da8dSAndroid Build Coastguard Worker            key = filename, lineno
585*cda5da8dSAndroid Build Coastguard Worker            self.counts[key] = self.counts.get(key, 0) + 1
586*cda5da8dSAndroid Build Coastguard Worker        return self.localtrace
587*cda5da8dSAndroid Build Coastguard Worker
588*cda5da8dSAndroid Build Coastguard Worker    def results(self):
589*cda5da8dSAndroid Build Coastguard Worker        return CoverageResults(self.counts, infile=self.infile,
590*cda5da8dSAndroid Build Coastguard Worker                               outfile=self.outfile,
591*cda5da8dSAndroid Build Coastguard Worker                               calledfuncs=self._calledfuncs,
592*cda5da8dSAndroid Build Coastguard Worker                               callers=self._callers)
593*cda5da8dSAndroid Build Coastguard Worker
594*cda5da8dSAndroid Build Coastguard Workerdef main():
595*cda5da8dSAndroid Build Coastguard Worker    import argparse
596*cda5da8dSAndroid Build Coastguard Worker
597*cda5da8dSAndroid Build Coastguard Worker    parser = argparse.ArgumentParser()
598*cda5da8dSAndroid Build Coastguard Worker    parser.add_argument('--version', action='version', version='trace 2.0')
599*cda5da8dSAndroid Build Coastguard Worker
600*cda5da8dSAndroid Build Coastguard Worker    grp = parser.add_argument_group('Main options',
601*cda5da8dSAndroid Build Coastguard Worker            'One of these (or --report) must be given')
602*cda5da8dSAndroid Build Coastguard Worker
603*cda5da8dSAndroid Build Coastguard Worker    grp.add_argument('-c', '--count', action='store_true',
604*cda5da8dSAndroid Build Coastguard Worker            help='Count the number of times each line is executed and write '
605*cda5da8dSAndroid Build Coastguard Worker                 'the counts to <module>.cover for each module executed, in '
606*cda5da8dSAndroid Build Coastguard Worker                 'the module\'s directory. See also --coverdir, --file, '
607*cda5da8dSAndroid Build Coastguard Worker                 '--no-report below.')
608*cda5da8dSAndroid Build Coastguard Worker    grp.add_argument('-t', '--trace', action='store_true',
609*cda5da8dSAndroid Build Coastguard Worker            help='Print each line to sys.stdout before it is executed')
610*cda5da8dSAndroid Build Coastguard Worker    grp.add_argument('-l', '--listfuncs', action='store_true',
611*cda5da8dSAndroid Build Coastguard Worker            help='Keep track of which functions are executed at least once '
612*cda5da8dSAndroid Build Coastguard Worker                 'and write the results to sys.stdout after the program exits. '
613*cda5da8dSAndroid Build Coastguard Worker                 'Cannot be specified alongside --trace or --count.')
614*cda5da8dSAndroid Build Coastguard Worker    grp.add_argument('-T', '--trackcalls', action='store_true',
615*cda5da8dSAndroid Build Coastguard Worker            help='Keep track of caller/called pairs and write the results to '
616*cda5da8dSAndroid Build Coastguard Worker                 'sys.stdout after the program exits.')
617*cda5da8dSAndroid Build Coastguard Worker
618*cda5da8dSAndroid Build Coastguard Worker    grp = parser.add_argument_group('Modifiers')
619*cda5da8dSAndroid Build Coastguard Worker
620*cda5da8dSAndroid Build Coastguard Worker    _grp = grp.add_mutually_exclusive_group()
621*cda5da8dSAndroid Build Coastguard Worker    _grp.add_argument('-r', '--report', action='store_true',
622*cda5da8dSAndroid Build Coastguard Worker            help='Generate a report from a counts file; does not execute any '
623*cda5da8dSAndroid Build Coastguard Worker                 'code. --file must specify the results file to read, which '
624*cda5da8dSAndroid Build Coastguard Worker                 'must have been created in a previous run with --count '
625*cda5da8dSAndroid Build Coastguard Worker                 '--file=FILE')
626*cda5da8dSAndroid Build Coastguard Worker    _grp.add_argument('-R', '--no-report', action='store_true',
627*cda5da8dSAndroid Build Coastguard Worker            help='Do not generate the coverage report files. '
628*cda5da8dSAndroid Build Coastguard Worker                 'Useful if you want to accumulate over several runs.')
629*cda5da8dSAndroid Build Coastguard Worker
630*cda5da8dSAndroid Build Coastguard Worker    grp.add_argument('-f', '--file',
631*cda5da8dSAndroid Build Coastguard Worker            help='File to accumulate counts over several runs')
632*cda5da8dSAndroid Build Coastguard Worker    grp.add_argument('-C', '--coverdir',
633*cda5da8dSAndroid Build Coastguard Worker            help='Directory where the report files go. The coverage report '
634*cda5da8dSAndroid Build Coastguard Worker                 'for <package>.<module> will be written to file '
635*cda5da8dSAndroid Build Coastguard Worker                 '<dir>/<package>/<module>.cover')
636*cda5da8dSAndroid Build Coastguard Worker    grp.add_argument('-m', '--missing', action='store_true',
637*cda5da8dSAndroid Build Coastguard Worker            help='Annotate executable lines that were not executed with '
638*cda5da8dSAndroid Build Coastguard Worker                 '">>>>>> "')
639*cda5da8dSAndroid Build Coastguard Worker    grp.add_argument('-s', '--summary', action='store_true',
640*cda5da8dSAndroid Build Coastguard Worker            help='Write a brief summary for each file to sys.stdout. '
641*cda5da8dSAndroid Build Coastguard Worker                 'Can only be used with --count or --report')
642*cda5da8dSAndroid Build Coastguard Worker    grp.add_argument('-g', '--timing', action='store_true',
643*cda5da8dSAndroid Build Coastguard Worker            help='Prefix each line with the time since the program started. '
644*cda5da8dSAndroid Build Coastguard Worker                 'Only used while tracing')
645*cda5da8dSAndroid Build Coastguard Worker
646*cda5da8dSAndroid Build Coastguard Worker    grp = parser.add_argument_group('Filters',
647*cda5da8dSAndroid Build Coastguard Worker            'Can be specified multiple times')
648*cda5da8dSAndroid Build Coastguard Worker    grp.add_argument('--ignore-module', action='append', default=[],
649*cda5da8dSAndroid Build Coastguard Worker            help='Ignore the given module(s) and its submodules '
650*cda5da8dSAndroid Build Coastguard Worker                 '(if it is a package). Accepts comma separated list of '
651*cda5da8dSAndroid Build Coastguard Worker                 'module names.')
652*cda5da8dSAndroid Build Coastguard Worker    grp.add_argument('--ignore-dir', action='append', default=[],
653*cda5da8dSAndroid Build Coastguard Worker            help='Ignore files in the given directory '
654*cda5da8dSAndroid Build Coastguard Worker                 '(multiple directories can be joined by os.pathsep).')
655*cda5da8dSAndroid Build Coastguard Worker
656*cda5da8dSAndroid Build Coastguard Worker    parser.add_argument('--module', action='store_true', default=False,
657*cda5da8dSAndroid Build Coastguard Worker                        help='Trace a module. ')
658*cda5da8dSAndroid Build Coastguard Worker    parser.add_argument('progname', nargs='?',
659*cda5da8dSAndroid Build Coastguard Worker            help='file to run as main program')
660*cda5da8dSAndroid Build Coastguard Worker    parser.add_argument('arguments', nargs=argparse.REMAINDER,
661*cda5da8dSAndroid Build Coastguard Worker            help='arguments to the program')
662*cda5da8dSAndroid Build Coastguard Worker
663*cda5da8dSAndroid Build Coastguard Worker    opts = parser.parse_args()
664*cda5da8dSAndroid Build Coastguard Worker
665*cda5da8dSAndroid Build Coastguard Worker    if opts.ignore_dir:
666*cda5da8dSAndroid Build Coastguard Worker        _prefix = sysconfig.get_path("stdlib")
667*cda5da8dSAndroid Build Coastguard Worker        _exec_prefix = sysconfig.get_path("platstdlib")
668*cda5da8dSAndroid Build Coastguard Worker
669*cda5da8dSAndroid Build Coastguard Worker    def parse_ignore_dir(s):
670*cda5da8dSAndroid Build Coastguard Worker        s = os.path.expanduser(os.path.expandvars(s))
671*cda5da8dSAndroid Build Coastguard Worker        s = s.replace('$prefix', _prefix).replace('$exec_prefix', _exec_prefix)
672*cda5da8dSAndroid Build Coastguard Worker        return os.path.normpath(s)
673*cda5da8dSAndroid Build Coastguard Worker
674*cda5da8dSAndroid Build Coastguard Worker    opts.ignore_module = [mod.strip()
675*cda5da8dSAndroid Build Coastguard Worker                          for i in opts.ignore_module for mod in i.split(',')]
676*cda5da8dSAndroid Build Coastguard Worker    opts.ignore_dir = [parse_ignore_dir(s)
677*cda5da8dSAndroid Build Coastguard Worker                       for i in opts.ignore_dir for s in i.split(os.pathsep)]
678*cda5da8dSAndroid Build Coastguard Worker
679*cda5da8dSAndroid Build Coastguard Worker    if opts.report:
680*cda5da8dSAndroid Build Coastguard Worker        if not opts.file:
681*cda5da8dSAndroid Build Coastguard Worker            parser.error('-r/--report requires -f/--file')
682*cda5da8dSAndroid Build Coastguard Worker        results = CoverageResults(infile=opts.file, outfile=opts.file)
683*cda5da8dSAndroid Build Coastguard Worker        return results.write_results(opts.missing, opts.summary, opts.coverdir)
684*cda5da8dSAndroid Build Coastguard Worker
685*cda5da8dSAndroid Build Coastguard Worker    if not any([opts.trace, opts.count, opts.listfuncs, opts.trackcalls]):
686*cda5da8dSAndroid Build Coastguard Worker        parser.error('must specify one of --trace, --count, --report, '
687*cda5da8dSAndroid Build Coastguard Worker                     '--listfuncs, or --trackcalls')
688*cda5da8dSAndroid Build Coastguard Worker
689*cda5da8dSAndroid Build Coastguard Worker    if opts.listfuncs and (opts.count or opts.trace):
690*cda5da8dSAndroid Build Coastguard Worker        parser.error('cannot specify both --listfuncs and (--trace or --count)')
691*cda5da8dSAndroid Build Coastguard Worker
692*cda5da8dSAndroid Build Coastguard Worker    if opts.summary and not opts.count:
693*cda5da8dSAndroid Build Coastguard Worker        parser.error('--summary can only be used with --count or --report')
694*cda5da8dSAndroid Build Coastguard Worker
695*cda5da8dSAndroid Build Coastguard Worker    if opts.progname is None:
696*cda5da8dSAndroid Build Coastguard Worker        parser.error('progname is missing: required with the main options')
697*cda5da8dSAndroid Build Coastguard Worker
698*cda5da8dSAndroid Build Coastguard Worker    t = Trace(opts.count, opts.trace, countfuncs=opts.listfuncs,
699*cda5da8dSAndroid Build Coastguard Worker              countcallers=opts.trackcalls, ignoremods=opts.ignore_module,
700*cda5da8dSAndroid Build Coastguard Worker              ignoredirs=opts.ignore_dir, infile=opts.file,
701*cda5da8dSAndroid Build Coastguard Worker              outfile=opts.file, timing=opts.timing)
702*cda5da8dSAndroid Build Coastguard Worker    try:
703*cda5da8dSAndroid Build Coastguard Worker        if opts.module:
704*cda5da8dSAndroid Build Coastguard Worker            import runpy
705*cda5da8dSAndroid Build Coastguard Worker            module_name = opts.progname
706*cda5da8dSAndroid Build Coastguard Worker            mod_name, mod_spec, code = runpy._get_module_details(module_name)
707*cda5da8dSAndroid Build Coastguard Worker            sys.argv = [code.co_filename, *opts.arguments]
708*cda5da8dSAndroid Build Coastguard Worker            globs = {
709*cda5da8dSAndroid Build Coastguard Worker                '__name__': '__main__',
710*cda5da8dSAndroid Build Coastguard Worker                '__file__': code.co_filename,
711*cda5da8dSAndroid Build Coastguard Worker                '__package__': mod_spec.parent,
712*cda5da8dSAndroid Build Coastguard Worker                '__loader__': mod_spec.loader,
713*cda5da8dSAndroid Build Coastguard Worker                '__spec__': mod_spec,
714*cda5da8dSAndroid Build Coastguard Worker                '__cached__': None,
715*cda5da8dSAndroid Build Coastguard Worker            }
716*cda5da8dSAndroid Build Coastguard Worker        else:
717*cda5da8dSAndroid Build Coastguard Worker            sys.argv = [opts.progname, *opts.arguments]
718*cda5da8dSAndroid Build Coastguard Worker            sys.path[0] = os.path.dirname(opts.progname)
719*cda5da8dSAndroid Build Coastguard Worker
720*cda5da8dSAndroid Build Coastguard Worker            with io.open_code(opts.progname) as fp:
721*cda5da8dSAndroid Build Coastguard Worker                code = compile(fp.read(), opts.progname, 'exec')
722*cda5da8dSAndroid Build Coastguard Worker            # try to emulate __main__ namespace as much as possible
723*cda5da8dSAndroid Build Coastguard Worker            globs = {
724*cda5da8dSAndroid Build Coastguard Worker                '__file__': opts.progname,
725*cda5da8dSAndroid Build Coastguard Worker                '__name__': '__main__',
726*cda5da8dSAndroid Build Coastguard Worker                '__package__': None,
727*cda5da8dSAndroid Build Coastguard Worker                '__cached__': None,
728*cda5da8dSAndroid Build Coastguard Worker            }
729*cda5da8dSAndroid Build Coastguard Worker        t.runctx(code, globs, globs)
730*cda5da8dSAndroid Build Coastguard Worker    except OSError as err:
731*cda5da8dSAndroid Build Coastguard Worker        sys.exit("Cannot run file %r because: %s" % (sys.argv[0], err))
732*cda5da8dSAndroid Build Coastguard Worker    except SystemExit:
733*cda5da8dSAndroid Build Coastguard Worker        pass
734*cda5da8dSAndroid Build Coastguard Worker
735*cda5da8dSAndroid Build Coastguard Worker    results = t.results()
736*cda5da8dSAndroid Build Coastguard Worker
737*cda5da8dSAndroid Build Coastguard Worker    if not opts.no_report:
738*cda5da8dSAndroid Build Coastguard Worker        results.write_results(opts.missing, opts.summary, opts.coverdir)
739*cda5da8dSAndroid Build Coastguard Worker
740*cda5da8dSAndroid Build Coastguard Workerif __name__=='__main__':
741*cda5da8dSAndroid Build Coastguard Worker    main()
742