forked from bloomberg/pystack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtypes.py
More file actions
168 lines (141 loc) Β· 4.15 KB
/
types.py
File metadata and controls
168 lines (141 loc) Β· 4.15 KB
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import enum
from dataclasses import dataclass
from typing import Dict
from typing import Iterable
from typing import List
from typing import NamedTuple
from typing import Optional
from typing import Tuple
SYMBOL_IGNORELIST = {
"PyObject_Call",
"call_function",
"classmethoddescr_call",
"cmpwrapper_call",
"fast_function",
"function_call",
"instance_call",
"instancemethod_call",
"instancemethod_call",
"methoddescr_call",
"proxy_call",
"slot_tp_call",
"type_call",
"weakref_call",
"wrap_call",
"wrapper_call",
"wrapperdescr_call",
"do_call_core",
}
@dataclass
class NativeFrame:
address: int
symbol: str
path: str
linenumber: int
colnumber: int
library: str
class FrameType(enum.Enum):
IGNORE = 0
EVAL = 1
OTHER = 3
def _is_eval_frame(symbol: str, python_version: Tuple[int, int]) -> bool:
if python_version < (3, 6):
return "PyEval_EvalFrameEx" in symbol
return "_PyEval_EvalFrameDefault" in symbol
def frame_type(
frame: NativeFrame, python_version: Optional[Tuple[int, int]]
) -> NativeFrame.FrameType:
symbol = frame.symbol
if python_version and _is_eval_frame(symbol, python_version):
return frame.FrameType.EVAL
if symbol.startswith("PyEval") or symbol.startswith("_PyEval"):
return frame.FrameType.IGNORE
if symbol.startswith("_Py"):
return frame.FrameType.IGNORE
if python_version and python_version >= (3, 8) and "vectorcall" in symbol.lower():
return frame.FrameType.IGNORE
if any(symbol.startswith(ignored_symbol) for ignored_symbol in SYMBOL_IGNORELIST):
return frame.FrameType.IGNORE
return frame.FrameType.OTHER
class LocationInfo(NamedTuple):
lineno: int
end_lineno: int
column: int
end_column: int
@dataclass
class PyCodeObject:
filename: str
scope: str
location: LocationInfo
@dataclass
class LocalVariable:
name: int
value: str
is_argument: bool
@dataclass
class PyFrame:
prev: Optional["PyFrame"]
next: Optional["PyFrame"]
code: PyCodeObject
arguments: Dict[str, str]
locals: Dict[str, str]
is_entry: bool
is_shim: bool
@dataclass
class PyThread:
tid: int
frame: Optional[PyFrame]
native_frames: List[NativeFrame]
holds_the_gil: int
is_gc_collecting: int
python_version: Optional[Tuple[int, int]]
name: Optional[str] = None
@property
def frames(self) -> Iterable[PyFrame]:
yield from filter(lambda frame: not frame.is_shim, self.all_frames)
@property
def first_frame(self) -> Optional[PyFrame]:
return next(iter(self.frames), None)
@property
def all_frames(self) -> Iterable[PyFrame]:
current_frame = self.frame
while current_frame is not None:
yield current_frame
current_frame = current_frame.next
@property
def status(self) -> str:
status = []
gil_status = self.gil_status
gc_status = self.gc_status
if self.tid == 0:
status.append("Thread terminated")
if gil_status:
status.append(gil_status)
if gc_status:
status.append(gc_status)
return "[" + ",".join(status) + "]"
@property
def gc_status(self) -> str:
if self.native_frames:
gc_symbols = {"gc_collect", "collect", "collect.constrprop"}
if any(
gc_symbol in frame.symbol
for gc_symbol in gc_symbols
for frame in self.native_frames
):
return "Garbage collecting"
return ""
if self.is_gc_collecting != -1:
if self.is_gc_collecting == 1 and self.holds_the_gil:
return "Garbage collecting"
return ""
return ""
@property
def gil_status(self) -> str:
if self.holds_the_gil > 0:
return "Has the GIL"
if any(frame.symbol == "take_gil" for frame in self.native_frames):
return "Waiting for the GIL"
if any(frame.symbol == "drop_gil" for frame in self.native_frames):
return "Dropping the GIL"
return ""