Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow/python/util/tf_stack.py: 46%
80 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
1# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""Functions used to extract and analyze stacks. Faster than Python libs."""
16# pylint: disable=g-bad-name
17import collections
18import inspect
19import threading
21# TODO(b/138203821): change to from ...util import ... once the bug is fixed.
22from tensorflow.python.util import _tf_stack
24# Generally such lookups should be done using `threading.local()`. See
25# https://blogs.gnome.org/jamesh/2008/06/11/tls-python/ for a detailed
26# explanation of why. However the transform stacks are expected to be empty
27# when a thread is joined, so reusing the key does not introduce a correctness
28# issue. Moreover, get_ident is faster than storing and retrieving a unique
29# key in a thread local store.
30_get_thread_key = threading.get_ident
33# TODO(mdan): Move these to C++ as well.
34# Moving to C++ can further avoid extra copies made by get_effective_map.
35_source_mapper_stacks = collections.defaultdict(lambda: [SentinelMapper()])
36_source_filter_stacks = collections.defaultdict(lambda: [SentinelFilter()])
39class StackTraceTransform(object):
40 """Base class for stack trace transformation functions."""
42 _stack_dict = None # Subclasses should override
43 _thread_key = None
45 def __enter__(self):
46 # Any given instance is assumed to be used by a single thread, which reduces
47 # expensive thread local lookups.
48 if self._thread_key is None:
49 self._thread_key = _get_thread_key()
50 else:
51 assert self._thread_key == _get_thread_key(), 'Shared across threads?'
53 stack = self._stack_dict[self._thread_key]
54 self.parent = stack[-1]
55 stack.append(self)
56 self.update()
57 return self
59 def __exit__(self, unused_type, unused_value, unused_traceback):
60 top = self._stack_dict[self._thread_key].pop()
61 assert top is self, 'Concurrent access?'
63 def update(self):
64 raise NotImplementedError('subclasses need to override this')
67class StackTraceMapper(StackTraceTransform):
68 """Allows remapping traceback information to different source code."""
69 _stack_dict = _source_mapper_stacks
71 def __init__(self):
72 self.internal_map = _tf_stack.PyBindSourceMap()
74 def update(self):
75 self.internal_map.update_to(tuple(self.get_effective_source_map().items()))
77 def get_effective_source_map(self):
78 """Returns a map (filename, lineno) -> (filename, lineno, function_name)."""
79 raise NotImplementedError('subclasses need to override this')
82EMPTY_DICT = {}
85class SentinelMapper(StackTraceMapper):
87 def get_effective_source_map(self):
88 return EMPTY_DICT
91class StackTraceFilter(StackTraceTransform):
92 """Allows filtering traceback information by removing superfluous frames."""
93 _stack_dict = _source_filter_stacks
95 def __init__(self):
96 self.internal_set = _tf_stack.PyBindFileSet()
98 def update(self):
99 self.internal_set.update_to(set(self.get_filtered_filenames()))
101 def get_filtered_filenames(self):
102 raise NotImplementedError('subclasses need to override this')
105EMPTY_SET = frozenset()
108class SentinelFilter(StackTraceFilter):
110 def get_filtered_filenames(self):
111 return EMPTY_SET
114class CurrentModuleFilter(StackTraceFilter):
115 """Filters stack frames from the module where this is used (best effort)."""
117 def __init__(self):
118 super().__init__()
119 filter_filename = None
120 outer_f = None
121 f = inspect.currentframe()
122 try:
123 if f is not None:
124 # The current frame is __init__. The first outer frame should be the
125 # caller.
126 outer_f = f.f_back
127 if outer_f is not None:
128 filter_filename = inspect.getsourcefile(outer_f)
129 self._filename = filter_filename
130 # This may be called repeatedly: once on entry by the superclass, then by
131 # each child context manager.
132 self._cached_set = None
133 finally:
134 # Avoid reference cycles, see:
135 # https://docs.python.org/3.7/library/inspect.html#the-interpreter-stack
136 del f
137 del outer_f
139 def get_filtered_filenames(self):
140 if self._cached_set is not None:
141 return self._cached_set
143 filtered_filenames = frozenset((self._filename,))
144 if self.parent is not None:
145 filtered_filenames |= self.parent.get_filtered_filenames()
146 self._cached_set = filtered_filenames
147 return filtered_filenames
150def extract_stack():
151 """An eager-friendly alternative to traceback.extract_stack.
153 Returns:
154 A list-like FrameSummary containing StackFrame-like objects, which are
155 namedtuple-like objects with the following fields: filename, lineno, name,
156 line, meant to masquerade as traceback.FrameSummary objects.
157 """
158 # N.B ExtractStack in tf_stack.cc will drop this frame prior to
159 # traversing the stack.
160 # TODO(cheshire): Remove this function, use extract_stack_for_op or Python
161 # traceback module.
162 thread_key = _get_thread_key()
163 return _tf_stack.extract_stack(
164 _source_mapper_stacks[thread_key][-1].internal_map,
165 _source_filter_stacks[thread_key][-1].internal_set)
168# TODO(mdan): Revisit these - a single location is almost always sufficient.
169def extract_stack_for_op(c_op, stacklevel=1):
170 """Attaches the current stack trace to `c_op`.
172 Args:
173 c_op: a TF_Operation object.
174 stacklevel: An integer for ignoring Python wrapper stack frames.
175 The default value of 1 ignores this function from the frame.
176 """
177 # N.B ExtractStack in tf_stack.cc will drop this frame prior to
178 # traversing the stack.
179 thread_key = _get_thread_key()
180 _tf_stack.extract_stack_for_op(
181 _source_mapper_stacks[thread_key][-1].internal_map,
182 _source_filter_stacks[thread_key][-1].internal_set, c_op, stacklevel)
185StackSummary = _tf_stack.StackTraceWrapper
186FrameSummary = _tf_stack.StackFrame