Cython 3.2.0__cp39-abi3-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Cython/Build/BuildExecutable.py +169 -0
- Cython/Build/Cache.py +199 -0
- Cython/Build/Cythonize.py +350 -0
- Cython/Build/Dependencies.py +1314 -0
- Cython/Build/Distutils.py +1 -0
- Cython/Build/Inline.py +463 -0
- Cython/Build/IpythonMagic.py +560 -0
- Cython/Build/SharedModule.py +94 -0
- Cython/Build/Tests/TestCyCache.py +194 -0
- Cython/Build/Tests/TestCythonizeArgsParser.py +481 -0
- Cython/Build/Tests/TestDependencies.py +133 -0
- Cython/Build/Tests/TestInline.py +177 -0
- Cython/Build/Tests/TestIpythonMagic.py +287 -0
- Cython/Build/Tests/TestRecythonize.py +212 -0
- Cython/Build/Tests/TestStripLiterals.py +155 -0
- Cython/Build/Tests/__init__.py +1 -0
- Cython/Build/__init__.py +11 -0
- Cython/CodeWriter.py +815 -0
- Cython/Compiler/AnalysedTreeTransforms.py +97 -0
- Cython/Compiler/Annotate.py +328 -0
- Cython/Compiler/AutoDocTransforms.py +320 -0
- Cython/Compiler/Buffer.py +680 -0
- Cython/Compiler/Builtin.py +984 -0
- Cython/Compiler/CmdLine.py +263 -0
- Cython/Compiler/Code.pxd +149 -0
- Cython/Compiler/Code.py +3746 -0
- Cython/Compiler/Code.pyd +0 -0
- Cython/Compiler/CodeGeneration.py +33 -0
- Cython/Compiler/CythonScope.py +191 -0
- Cython/Compiler/Dataclass.py +864 -0
- Cython/Compiler/DebugFlags.py +24 -0
- Cython/Compiler/Errors.py +297 -0
- Cython/Compiler/ExprNodes.py +15562 -0
- Cython/Compiler/FlowControl.pxd +97 -0
- Cython/Compiler/FlowControl.py +1451 -0
- Cython/Compiler/FlowControl.pyd +0 -0
- Cython/Compiler/FusedNode.py +971 -0
- Cython/Compiler/FusedNode.pyd +0 -0
- Cython/Compiler/Future.py +16 -0
- Cython/Compiler/Interpreter.py +57 -0
- Cython/Compiler/Lexicon.py +421 -0
- Cython/Compiler/LineTable.py +114 -0
- Cython/Compiler/LineTable.pyd +0 -0
- Cython/Compiler/Main.py +857 -0
- Cython/Compiler/MatchCaseNodes.py +259 -0
- Cython/Compiler/MemoryView.py +905 -0
- Cython/Compiler/ModuleNode.py +4235 -0
- Cython/Compiler/Naming.py +363 -0
- Cython/Compiler/Nodes.py +10831 -0
- Cython/Compiler/Optimize.py +5288 -0
- Cython/Compiler/Options.py +843 -0
- Cython/Compiler/ParseTreeTransforms.pxd +78 -0
- Cython/Compiler/ParseTreeTransforms.py +4638 -0
- Cython/Compiler/Parsing.pxd +9 -0
- Cython/Compiler/Parsing.py +4775 -0
- Cython/Compiler/Parsing.pyd +0 -0
- Cython/Compiler/Pipeline.py +439 -0
- Cython/Compiler/PyrexTypes.py +5870 -0
- Cython/Compiler/Pythran.py +232 -0
- Cython/Compiler/Scanning.pxd +48 -0
- Cython/Compiler/Scanning.py +701 -0
- Cython/Compiler/Scanning.pyd +0 -0
- Cython/Compiler/StringEncoding.py +298 -0
- Cython/Compiler/Symtab.py +3073 -0
- Cython/Compiler/Tests/TestBuffer.py +105 -0
- Cython/Compiler/Tests/TestBuiltin.py +72 -0
- Cython/Compiler/Tests/TestCmdLine.py +586 -0
- Cython/Compiler/Tests/TestCode.py +144 -0
- Cython/Compiler/Tests/TestFlowControl.py +65 -0
- Cython/Compiler/Tests/TestGrammar.py +202 -0
- Cython/Compiler/Tests/TestMemView.py +71 -0
- Cython/Compiler/Tests/TestParseTreeTransforms.py +285 -0
- Cython/Compiler/Tests/TestScanning.py +134 -0
- Cython/Compiler/Tests/TestSignatureMatching.py +73 -0
- Cython/Compiler/Tests/TestStringEncoding.py +21 -0
- Cython/Compiler/Tests/TestTreeFragment.py +63 -0
- Cython/Compiler/Tests/TestTreePath.py +103 -0
- Cython/Compiler/Tests/TestTypes.py +75 -0
- Cython/Compiler/Tests/TestUtilityLoad.py +112 -0
- Cython/Compiler/Tests/TestVisitor.py +61 -0
- Cython/Compiler/Tests/Utils.py +36 -0
- Cython/Compiler/Tests/__init__.py +1 -0
- Cython/Compiler/TreeFragment.py +278 -0
- Cython/Compiler/TreePath.py +303 -0
- Cython/Compiler/TypeInference.py +591 -0
- Cython/Compiler/TypeSlots.py +1174 -0
- Cython/Compiler/UFuncs.py +311 -0
- Cython/Compiler/UtilNodes.py +389 -0
- Cython/Compiler/UtilityCode.py +344 -0
- Cython/Compiler/Version.py +8 -0
- Cython/Compiler/Visitor.pxd +53 -0
- Cython/Compiler/Visitor.py +861 -0
- Cython/Compiler/Visitor.pyd +0 -0
- Cython/Compiler/__init__.py +1 -0
- Cython/Coverage.py +448 -0
- Cython/Debugger/Cygdb.py +177 -0
- Cython/Debugger/DebugWriter.py +82 -0
- Cython/Debugger/Tests/TestLibCython.py +275 -0
- Cython/Debugger/Tests/__init__.py +1 -0
- Cython/Debugger/Tests/cfuncs.c +8 -0
- Cython/Debugger/Tests/codefile +49 -0
- Cython/Debugger/Tests/test_libcython_in_gdb.py +578 -0
- Cython/Debugger/Tests/test_libpython_in_gdb.py +90 -0
- Cython/Debugger/__init__.py +1 -0
- Cython/Debugger/libcython.py +1548 -0
- Cython/Debugger/libpython.py +2821 -0
- Cython/Debugging.py +20 -0
- Cython/Distutils/__init__.py +2 -0
- Cython/Distutils/build_ext.py +139 -0
- Cython/Distutils/extension.py +96 -0
- Cython/Distutils/old_build_ext.py +351 -0
- Cython/Includes/cpython/__init__.pxd +173 -0
- Cython/Includes/cpython/array.pxd +178 -0
- Cython/Includes/cpython/bool.pxd +37 -0
- Cython/Includes/cpython/buffer.pxd +112 -0
- Cython/Includes/cpython/bytearray.pxd +33 -0
- Cython/Includes/cpython/bytes.pxd +200 -0
- Cython/Includes/cpython/cellobject.pxd +35 -0
- Cython/Includes/cpython/ceval.pxd +8 -0
- Cython/Includes/cpython/codecs.pxd +121 -0
- Cython/Includes/cpython/complex.pxd +60 -0
- Cython/Includes/cpython/contextvars.pxd +145 -0
- Cython/Includes/cpython/conversion.pxd +36 -0
- Cython/Includes/cpython/datetime.pxd +395 -0
- Cython/Includes/cpython/descr.pxd +26 -0
- Cython/Includes/cpython/dict.pxd +187 -0
- Cython/Includes/cpython/exc.pxd +263 -0
- Cython/Includes/cpython/fileobject.pxd +57 -0
- Cython/Includes/cpython/float.pxd +47 -0
- Cython/Includes/cpython/function.pxd +65 -0
- Cython/Includes/cpython/genobject.pxd +25 -0
- Cython/Includes/cpython/getargs.pxd +12 -0
- Cython/Includes/cpython/instance.pxd +25 -0
- Cython/Includes/cpython/iterator.pxd +36 -0
- Cython/Includes/cpython/iterobject.pxd +24 -0
- Cython/Includes/cpython/list.pxd +92 -0
- Cython/Includes/cpython/long.pxd +149 -0
- Cython/Includes/cpython/longintrepr.pxd +14 -0
- Cython/Includes/cpython/mapping.pxd +63 -0
- Cython/Includes/cpython/marshal.pxd +66 -0
- Cython/Includes/cpython/mem.pxd +120 -0
- Cython/Includes/cpython/memoryview.pxd +50 -0
- Cython/Includes/cpython/method.pxd +49 -0
- Cython/Includes/cpython/module.pxd +208 -0
- Cython/Includes/cpython/number.pxd +258 -0
- Cython/Includes/cpython/object.pxd +433 -0
- Cython/Includes/cpython/pycapsule.pxd +143 -0
- Cython/Includes/cpython/pylifecycle.pxd +68 -0
- Cython/Includes/cpython/pyport.pxd +8 -0
- Cython/Includes/cpython/pystate.pxd +95 -0
- Cython/Includes/cpython/pythread.pxd +53 -0
- Cython/Includes/cpython/ref.pxd +141 -0
- Cython/Includes/cpython/sequence.pxd +134 -0
- Cython/Includes/cpython/set.pxd +119 -0
- Cython/Includes/cpython/slice.pxd +70 -0
- Cython/Includes/cpython/time.pxd +129 -0
- Cython/Includes/cpython/tuple.pxd +72 -0
- Cython/Includes/cpython/type.pxd +53 -0
- Cython/Includes/cpython/unicode.pxd +639 -0
- Cython/Includes/cpython/version.pxd +32 -0
- Cython/Includes/cpython/weakref.pxd +78 -0
- Cython/Includes/libc/__init__.pxd +1 -0
- Cython/Includes/libc/complex.pxd +35 -0
- Cython/Includes/libc/errno.pxd +127 -0
- Cython/Includes/libc/float.pxd +43 -0
- Cython/Includes/libc/limits.pxd +28 -0
- Cython/Includes/libc/locale.pxd +46 -0
- Cython/Includes/libc/math.pxd +209 -0
- Cython/Includes/libc/setjmp.pxd +10 -0
- Cython/Includes/libc/signal.pxd +64 -0
- Cython/Includes/libc/stddef.pxd +9 -0
- Cython/Includes/libc/stdint.pxd +105 -0
- Cython/Includes/libc/stdio.pxd +80 -0
- Cython/Includes/libc/stdlib.pxd +72 -0
- Cython/Includes/libc/string.pxd +50 -0
- Cython/Includes/libc/threads.pxd +234 -0
- Cython/Includes/libc/time.pxd +52 -0
- Cython/Includes/libcpp/__init__.pxd +4 -0
- Cython/Includes/libcpp/algorithm.pxd +320 -0
- Cython/Includes/libcpp/any.pxd +16 -0
- Cython/Includes/libcpp/atomic.pxd +59 -0
- Cython/Includes/libcpp/barrier.pxd +22 -0
- Cython/Includes/libcpp/bit.pxd +29 -0
- Cython/Includes/libcpp/cast.pxd +12 -0
- Cython/Includes/libcpp/cmath.pxd +518 -0
- Cython/Includes/libcpp/complex.pxd +106 -0
- Cython/Includes/libcpp/condition_variable.pxd +322 -0
- Cython/Includes/libcpp/deque.pxd +165 -0
- Cython/Includes/libcpp/exception.pxd +86 -0
- Cython/Includes/libcpp/execution.pxd +15 -0
- Cython/Includes/libcpp/forward_list.pxd +63 -0
- Cython/Includes/libcpp/functional.pxd +26 -0
- Cython/Includes/libcpp/future.pxd +103 -0
- Cython/Includes/libcpp/iterator.pxd +34 -0
- Cython/Includes/libcpp/latch.pxd +17 -0
- Cython/Includes/libcpp/limits.pxd +61 -0
- Cython/Includes/libcpp/list.pxd +117 -0
- Cython/Includes/libcpp/map.pxd +252 -0
- Cython/Includes/libcpp/memory.pxd +115 -0
- Cython/Includes/libcpp/mutex.pxd +387 -0
- Cython/Includes/libcpp/numbers.pxd +15 -0
- Cython/Includes/libcpp/numeric.pxd +131 -0
- Cython/Includes/libcpp/optional.pxd +34 -0
- Cython/Includes/libcpp/pair.pxd +1 -0
- Cython/Includes/libcpp/queue.pxd +25 -0
- Cython/Includes/libcpp/random.pxd +166 -0
- Cython/Includes/libcpp/semaphore.pxd +43 -0
- Cython/Includes/libcpp/set.pxd +228 -0
- Cython/Includes/libcpp/shared_mutex.pxd +96 -0
- Cython/Includes/libcpp/span.pxd +87 -0
- Cython/Includes/libcpp/stack.pxd +11 -0
- Cython/Includes/libcpp/stop_token.pxd +117 -0
- Cython/Includes/libcpp/string.pxd +355 -0
- Cython/Includes/libcpp/string_view.pxd +183 -0
- Cython/Includes/libcpp/typeindex.pxd +15 -0
- Cython/Includes/libcpp/typeinfo.pxd +10 -0
- Cython/Includes/libcpp/unordered_map.pxd +193 -0
- Cython/Includes/libcpp/unordered_set.pxd +152 -0
- Cython/Includes/libcpp/utility.pxd +30 -0
- Cython/Includes/libcpp/vector.pxd +186 -0
- Cython/Includes/numpy/math.pxd +150 -0
- Cython/Includes/openmp.pxd +50 -0
- Cython/Includes/posix/__init__.pxd +1 -0
- Cython/Includes/posix/dlfcn.pxd +14 -0
- Cython/Includes/posix/fcntl.pxd +86 -0
- Cython/Includes/posix/ioctl.pxd +4 -0
- Cython/Includes/posix/mman.pxd +101 -0
- Cython/Includes/posix/resource.pxd +57 -0
- Cython/Includes/posix/select.pxd +21 -0
- Cython/Includes/posix/signal.pxd +73 -0
- Cython/Includes/posix/stat.pxd +98 -0
- Cython/Includes/posix/stdio.pxd +37 -0
- Cython/Includes/posix/stdlib.pxd +29 -0
- Cython/Includes/posix/strings.pxd +9 -0
- Cython/Includes/posix/time.pxd +71 -0
- Cython/Includes/posix/types.pxd +30 -0
- Cython/Includes/posix/uio.pxd +26 -0
- Cython/Includes/posix/unistd.pxd +271 -0
- Cython/Includes/posix/wait.pxd +38 -0
- Cython/Plex/Actions.pxd +24 -0
- Cython/Plex/Actions.py +119 -0
- Cython/Plex/Actions.pyd +0 -0
- Cython/Plex/DFA.pxd +14 -0
- Cython/Plex/DFA.py +164 -0
- Cython/Plex/DFA.pyd +0 -0
- Cython/Plex/Errors.py +48 -0
- Cython/Plex/Lexicons.py +178 -0
- Cython/Plex/Machines.pxd +36 -0
- Cython/Plex/Machines.py +238 -0
- Cython/Plex/Machines.pyd +0 -0
- Cython/Plex/Regexps.py +535 -0
- Cython/Plex/Scanners.pxd +45 -0
- Cython/Plex/Scanners.py +328 -0
- Cython/Plex/Scanners.pyd +0 -0
- Cython/Plex/Transitions.pxd +14 -0
- Cython/Plex/Transitions.py +239 -0
- Cython/Plex/Transitions.pyd +0 -0
- Cython/Plex/__init__.py +34 -0
- Cython/Runtime/__init__.py +1 -0
- Cython/Runtime/refnanny.pyd +0 -0
- Cython/Runtime/refnanny.pyx +237 -0
- Cython/Shadow.py +690 -0
- Cython/Shadow.pyi +521 -0
- Cython/StringIOTree.py +170 -0
- Cython/StringIOTree.pyd +0 -0
- Cython/Tempita/__init__.py +4 -0
- Cython/Tempita/_looper.py +154 -0
- Cython/Tempita/_tempita.py +1091 -0
- Cython/Tempita/_tempita.pyd +0 -0
- Cython/TestUtils.py +422 -0
- Cython/Tests/TestCodeWriter.py +128 -0
- Cython/Tests/TestCythonUtils.py +202 -0
- Cython/Tests/TestJediTyper.py +223 -0
- Cython/Tests/TestShadow.py +114 -0
- Cython/Tests/TestStringIOTree.py +67 -0
- Cython/Tests/TestTestUtils.py +90 -0
- Cython/Tests/__init__.py +1 -0
- Cython/Tests/xmlrunner.py +390 -0
- Cython/Utility/AsyncGen.c +1031 -0
- Cython/Utility/Buffer.c +865 -0
- Cython/Utility/BufferFormatFromTypeInfo.pxd +2 -0
- Cython/Utility/Builtins.c +810 -0
- Cython/Utility/CConvert.pyx +134 -0
- Cython/Utility/CMath.c +104 -0
- Cython/Utility/CommonStructures.c +226 -0
- Cython/Utility/Complex.c +378 -0
- Cython/Utility/Coroutine.c +2300 -0
- Cython/Utility/CpdefEnums.pyx +103 -0
- Cython/Utility/CppConvert.pyx +282 -0
- Cython/Utility/CppSupport.cpp +151 -0
- Cython/Utility/CythonFunction.c +1832 -0
- Cython/Utility/Dataclasses.c +101 -0
- Cython/Utility/Embed.c +121 -0
- Cython/Utility/Exceptions.c +1016 -0
- Cython/Utility/ExtensionTypes.c +996 -0
- Cython/Utility/FunctionArguments.c +1043 -0
- Cython/Utility/FusedFunction.pyx +44 -0
- Cython/Utility/ImportExport.c +907 -0
- Cython/Utility/MemoryView.pxd +188 -0
- Cython/Utility/MemoryView.pyx +1482 -0
- Cython/Utility/MemoryView_C.c +927 -0
- Cython/Utility/ModuleSetupCode.c +3203 -0
- Cython/Utility/NumpyImportArray.c +46 -0
- Cython/Utility/ObjectHandling.c +3273 -0
- Cython/Utility/Optimize.c +1603 -0
- Cython/Utility/Overflow.c +384 -0
- Cython/Utility/Printing.c +86 -0
- Cython/Utility/Profile.c +732 -0
- Cython/Utility/StringTools.c +1379 -0
- Cython/Utility/Synchronization.c +399 -0
- Cython/Utility/TString.c +356 -0
- Cython/Utility/TestCyUtilityLoader.pyx +8 -0
- Cython/Utility/TestCythonScope.pyx +75 -0
- Cython/Utility/TestUtilityLoader.c +12 -0
- Cython/Utility/TypeConversion.c +1385 -0
- Cython/Utility/UFuncs.pyx +50 -0
- Cython/Utility/UFuncs_C.c +89 -0
- Cython/Utility/__init__.py +28 -0
- Cython/Utility/arrayarray.h +167 -0
- Cython/Utils.py +687 -0
- Cython/Utils.pyd +0 -0
- Cython/__init__.py +10 -0
- Cython/__init__.pyi +7 -0
- Cython/py.typed +0 -0
- cython-3.2.0.dist-info/METADATA +85 -0
- cython-3.2.0.dist-info/RECORD +333 -0
- cython-3.2.0.dist-info/WHEEL +5 -0
- cython-3.2.0.dist-info/entry_points.txt +4 -0
- cython-3.2.0.dist-info/top_level.txt +3 -0
- cython.py +29 -0
- pyximport/__init__.py +4 -0
- pyximport/pyxbuild.py +160 -0
- pyximport/pyximport.py +482 -0
|
@@ -0,0 +1,1548 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GDB extension that adds Cython support.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
import textwrap
|
|
8
|
+
import functools
|
|
9
|
+
import itertools
|
|
10
|
+
import collections
|
|
11
|
+
|
|
12
|
+
import gdb
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
from lxml import etree
|
|
16
|
+
have_lxml = True
|
|
17
|
+
except ImportError:
|
|
18
|
+
from xml.etree import ElementTree as etree
|
|
19
|
+
have_lxml = False
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
import pygments.lexers
|
|
23
|
+
import pygments.formatters
|
|
24
|
+
except ImportError:
|
|
25
|
+
pygments = None
|
|
26
|
+
sys.stderr.write("Install pygments for colorized source code.\n")
|
|
27
|
+
|
|
28
|
+
if hasattr(gdb, 'string_to_argv'):
|
|
29
|
+
from gdb import string_to_argv
|
|
30
|
+
else:
|
|
31
|
+
from shlex import split as string_to_argv
|
|
32
|
+
|
|
33
|
+
from Cython.Debugger import libpython
|
|
34
|
+
|
|
35
|
+
# C or Python type
|
|
36
|
+
CObject = 'CObject'
|
|
37
|
+
PythonObject = 'PythonObject'
|
|
38
|
+
|
|
39
|
+
_data_types = dict(CObject=CObject, PythonObject=PythonObject)
|
|
40
|
+
_filesystemencoding = sys.getfilesystemencoding() or 'UTF-8'
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# decorators
|
|
44
|
+
|
|
45
|
+
def default_selected_gdb_frame(err=True):
|
|
46
|
+
def decorator(function):
|
|
47
|
+
@functools.wraps(function)
|
|
48
|
+
def wrapper(self, frame=None, *args, **kwargs):
|
|
49
|
+
try:
|
|
50
|
+
frame = frame or gdb.selected_frame()
|
|
51
|
+
except RuntimeError:
|
|
52
|
+
raise gdb.GdbError("No frame is currently selected.")
|
|
53
|
+
|
|
54
|
+
if err and frame.name() is None:
|
|
55
|
+
raise NoFunctionNameInFrameError()
|
|
56
|
+
|
|
57
|
+
return function(self, frame, *args, **kwargs)
|
|
58
|
+
return wrapper
|
|
59
|
+
return decorator
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def require_cython_frame(function):
|
|
63
|
+
@functools.wraps(function)
|
|
64
|
+
@require_running_program
|
|
65
|
+
def wrapper(self, *args, **kwargs):
|
|
66
|
+
frame = kwargs.get('frame') or gdb.selected_frame()
|
|
67
|
+
if not self.is_cython_function(frame):
|
|
68
|
+
raise gdb.GdbError('Selected frame does not correspond with a '
|
|
69
|
+
'Cython function we know about.')
|
|
70
|
+
return function(self, *args, **kwargs)
|
|
71
|
+
return wrapper
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def dispatch_on_frame(c_command, python_command=None):
|
|
75
|
+
def decorator(function):
|
|
76
|
+
@functools.wraps(function)
|
|
77
|
+
def wrapper(self, *args, **kwargs):
|
|
78
|
+
is_cy = self.is_cython_function()
|
|
79
|
+
is_py = self.is_python_function()
|
|
80
|
+
|
|
81
|
+
if is_cy or (is_py and not python_command):
|
|
82
|
+
function(self, *args, **kwargs)
|
|
83
|
+
elif is_py:
|
|
84
|
+
gdb.execute(python_command)
|
|
85
|
+
elif self.is_relevant_function():
|
|
86
|
+
gdb.execute(c_command)
|
|
87
|
+
else:
|
|
88
|
+
raise gdb.GdbError("Not a function cygdb knows about. "
|
|
89
|
+
"Use the normal GDB commands instead.")
|
|
90
|
+
|
|
91
|
+
return wrapper
|
|
92
|
+
return decorator
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def require_running_program(function):
|
|
96
|
+
@functools.wraps(function)
|
|
97
|
+
def wrapper(*args, **kwargs):
|
|
98
|
+
try:
|
|
99
|
+
gdb.selected_frame()
|
|
100
|
+
except RuntimeError:
|
|
101
|
+
raise gdb.GdbError("No frame is currently selected.")
|
|
102
|
+
|
|
103
|
+
return function(*args, **kwargs)
|
|
104
|
+
return wrapper
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def gdb_function_value_to_unicode(function):
|
|
108
|
+
@functools.wraps(function)
|
|
109
|
+
def wrapper(self, string, *args, **kwargs):
|
|
110
|
+
if isinstance(string, gdb.Value):
|
|
111
|
+
string = string.string()
|
|
112
|
+
|
|
113
|
+
return function(self, string, *args, **kwargs)
|
|
114
|
+
return wrapper
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# Classes that represent the debug information
|
|
118
|
+
# Don't rename the parameters of these classes, they come directly from the XML
|
|
119
|
+
|
|
120
|
+
def simple_repr(self, renamed=None, state=True):
|
|
121
|
+
"""Prints out all instance variables needed to recreate an object.
|
|
122
|
+
|
|
123
|
+
Following the python convention for __repr__, this function prints all the
|
|
124
|
+
information stored in an instance as opposed to its class. The working
|
|
125
|
+
assumption is that most initialization arguments are stored as a property
|
|
126
|
+
using the same name.
|
|
127
|
+
|
|
128
|
+
The object contents are displayed as the initialization call followed by,
|
|
129
|
+
optionally, the value of each of the instance's properties in the form:
|
|
130
|
+
```
|
|
131
|
+
ClassName(
|
|
132
|
+
init_arg_1 = "repr of some example str",
|
|
133
|
+
...
|
|
134
|
+
)
|
|
135
|
+
self.state_based_property = ...
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Function arguments:
|
|
139
|
+
self Instance to be represented
|
|
140
|
+
|
|
141
|
+
renamed Dictionary of initialization arguments that are stored under a
|
|
142
|
+
different property name in the form { argument: property }
|
|
143
|
+
|
|
144
|
+
state Boolean representing whether properties outside the
|
|
145
|
+
initialization parameters should be printed (self.prop = ...).
|
|
146
|
+
Using `False` may make the class more amenable to recursive repr
|
|
147
|
+
"""
|
|
148
|
+
import inspect
|
|
149
|
+
init_arg_names = tuple(inspect.signature(self.__init__).parameters)
|
|
150
|
+
init_attrs = [renamed.get(arg, arg) for arg in init_arg_names] \
|
|
151
|
+
if renamed else init_arg_names
|
|
152
|
+
state_repr = ()
|
|
153
|
+
if state:
|
|
154
|
+
instance_attrs = sorted(vars(self).keys())
|
|
155
|
+
state_repr = [attr for attr in instance_attrs if attr not in init_attrs]
|
|
156
|
+
|
|
157
|
+
def names_and_values(prefix, attrs, args=None):
|
|
158
|
+
for attr, arg in zip(attrs, args or attrs):
|
|
159
|
+
param = repr(getattr(self, attr)).replace("\n", "\n\t\t")
|
|
160
|
+
yield f'{prefix}{arg} = {param}'
|
|
161
|
+
|
|
162
|
+
return "".join([
|
|
163
|
+
self.__class__.__qualname__, "(",
|
|
164
|
+
",".join(names_and_values("\n\t\t", init_attrs, init_arg_names)),
|
|
165
|
+
"\n\t)", *names_and_values("\nself.", state_repr)
|
|
166
|
+
])
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class CythonModule:
|
|
170
|
+
def __init__(self, module_name, filename, c_filename):
|
|
171
|
+
self.name = module_name
|
|
172
|
+
self.filename = filename
|
|
173
|
+
self.c_filename = c_filename
|
|
174
|
+
self.globals = {}
|
|
175
|
+
# {cython_lineno: min(c_linenos)}
|
|
176
|
+
self.lineno_cy2c = {}
|
|
177
|
+
# {c_lineno: cython_lineno}
|
|
178
|
+
self.lineno_c2cy = {}
|
|
179
|
+
self.functions = {}
|
|
180
|
+
|
|
181
|
+
def __repr__(self):
|
|
182
|
+
return simple_repr(self, renamed={"module_name": "name"}, state=False)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
class CythonVariable:
|
|
186
|
+
|
|
187
|
+
def __init__(self, name, cname, qualified_name, type, lineno):
|
|
188
|
+
self.name = name
|
|
189
|
+
self.cname = cname
|
|
190
|
+
self.qualified_name = qualified_name
|
|
191
|
+
self.type = type
|
|
192
|
+
self.lineno = int(lineno)
|
|
193
|
+
|
|
194
|
+
def __repr__(self):
|
|
195
|
+
return simple_repr(self)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class CythonFunction(CythonVariable):
|
|
199
|
+
def __init__(self,
|
|
200
|
+
module,
|
|
201
|
+
name,
|
|
202
|
+
cname,
|
|
203
|
+
pf_cname,
|
|
204
|
+
qualified_name,
|
|
205
|
+
lineno,
|
|
206
|
+
type=CObject,
|
|
207
|
+
is_initmodule_function="False"):
|
|
208
|
+
super().__init__(name,
|
|
209
|
+
cname,
|
|
210
|
+
qualified_name,
|
|
211
|
+
type,
|
|
212
|
+
lineno)
|
|
213
|
+
self.module = module
|
|
214
|
+
self.pf_cname = pf_cname
|
|
215
|
+
self.is_initmodule_function = is_initmodule_function == "True"
|
|
216
|
+
self.locals = {}
|
|
217
|
+
self.arguments = []
|
|
218
|
+
self.step_into_functions = set()
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
# General purpose classes
|
|
222
|
+
|
|
223
|
+
frame_repr_whitelist = {
|
|
224
|
+
"Frame.is_valid",
|
|
225
|
+
"Frame.name",
|
|
226
|
+
"Frame.architecture",
|
|
227
|
+
"Frame.type",
|
|
228
|
+
"Frame.pc",
|
|
229
|
+
"Frame.block",
|
|
230
|
+
"Frame.function",
|
|
231
|
+
"Frame.older",
|
|
232
|
+
"Frame.newer",
|
|
233
|
+
"Frame.find_sal",
|
|
234
|
+
"Frame.select",
|
|
235
|
+
"Frame.static_link",
|
|
236
|
+
"Frame.level",
|
|
237
|
+
"Frame.language",
|
|
238
|
+
"Symbol.is_valid",
|
|
239
|
+
"Symbol.value",
|
|
240
|
+
"Symtab_and_line.is_valid",
|
|
241
|
+
"Symtab.is_valid",
|
|
242
|
+
"Symtab.fullname",
|
|
243
|
+
"Symtab.global_block",
|
|
244
|
+
"Symtab.static_block",
|
|
245
|
+
"Symtab.linetable",
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
def frame_repr(frame):
|
|
249
|
+
"""Returns a string representing the internal state of a provided GDB frame
|
|
250
|
+
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Frames-In-Python.html
|
|
251
|
+
|
|
252
|
+
Created to serve as GDB.Frame.__repr__ for debugging purposes. GDB has many
|
|
253
|
+
layers of abstraction separating the state of the debugger from the
|
|
254
|
+
corresponding source code. This prints a tree of instance properties,
|
|
255
|
+
expanding the values for Symtab_and_line, Symbol, and Symtab.
|
|
256
|
+
|
|
257
|
+
Most of these properties require computation to determine, meaning much of
|
|
258
|
+
relevant info is behind a monad, a subset of which are evaluated.
|
|
259
|
+
|
|
260
|
+
Arguments
|
|
261
|
+
frame The GDB.Frame instance to be represented as a string
|
|
262
|
+
"""
|
|
263
|
+
res = f"{frame}\n"
|
|
264
|
+
for attribute in sorted(dir(frame)):
|
|
265
|
+
if attribute.startswith("__"):
|
|
266
|
+
continue
|
|
267
|
+
value = getattr(frame, attribute)
|
|
268
|
+
if callable(value) and value.__qualname__ in frame_repr_whitelist:
|
|
269
|
+
value = value()
|
|
270
|
+
|
|
271
|
+
if type(value) in [gdb.Symtab_and_line, gdb.Symbol, gdb.Symtab]:
|
|
272
|
+
# strip last line since it will get added on at the end of the loop
|
|
273
|
+
value = frame_repr(value).rstrip("\n").replace("\n", "\n\t")
|
|
274
|
+
res += f"{attribute}: " + (
|
|
275
|
+
f"{value:x}\n" if isinstance(value, int) and attribute != "line"
|
|
276
|
+
else f"{value}\n")
|
|
277
|
+
return res
|
|
278
|
+
|
|
279
|
+
class CythonBase:
|
|
280
|
+
|
|
281
|
+
@default_selected_gdb_frame(err=False)
|
|
282
|
+
def is_cython_function(self, frame):
|
|
283
|
+
return frame.name() in self.cy.functions_by_cname
|
|
284
|
+
|
|
285
|
+
@default_selected_gdb_frame(err=False)
|
|
286
|
+
def is_python_function(self, frame):
|
|
287
|
+
"""
|
|
288
|
+
Tells if a frame is associated with a Python function.
|
|
289
|
+
If we can't read the Python frame information, don't regard it as such.
|
|
290
|
+
"""
|
|
291
|
+
if frame.name() == 'PyEval_EvalFrameEx':
|
|
292
|
+
pyframe = libpython.Frame(frame).get_pyop()
|
|
293
|
+
return pyframe and not pyframe.is_optimized_out()
|
|
294
|
+
return False
|
|
295
|
+
|
|
296
|
+
@default_selected_gdb_frame()
|
|
297
|
+
def get_c_function_name(self, frame):
|
|
298
|
+
return frame.name()
|
|
299
|
+
|
|
300
|
+
@default_selected_gdb_frame()
|
|
301
|
+
def get_c_lineno(self, frame):
|
|
302
|
+
return frame.find_sal().line
|
|
303
|
+
|
|
304
|
+
@default_selected_gdb_frame()
|
|
305
|
+
def get_cython_function(self, frame):
|
|
306
|
+
result = self.cy.functions_by_cname.get(frame.name())
|
|
307
|
+
if result is None:
|
|
308
|
+
raise NoCythonFunctionInFrameError()
|
|
309
|
+
|
|
310
|
+
return result
|
|
311
|
+
|
|
312
|
+
@default_selected_gdb_frame()
|
|
313
|
+
def get_cython_lineno(self, frame):
|
|
314
|
+
"""
|
|
315
|
+
Get the current Cython line number. Returns ("<no filename>", 0) if there is no
|
|
316
|
+
correspondence between the C and Cython code.
|
|
317
|
+
"""
|
|
318
|
+
cyfunc = self.get_cython_function(frame)
|
|
319
|
+
return cyfunc.module.lineno_c2cy.get(self.get_c_lineno(frame), ("<no filename>", 0))
|
|
320
|
+
|
|
321
|
+
@default_selected_gdb_frame()
|
|
322
|
+
def get_source_desc(self, frame):
|
|
323
|
+
filename = lineno = lexer = None
|
|
324
|
+
if self.is_cython_function(frame):
|
|
325
|
+
filename = self.get_cython_function(frame).module.filename
|
|
326
|
+
filename_and_lineno = self.get_cython_lineno(frame)
|
|
327
|
+
assert filename == filename_and_lineno[0]
|
|
328
|
+
lineno = filename_and_lineno[1]
|
|
329
|
+
if pygments:
|
|
330
|
+
lexer = pygments.lexers.CythonLexer(stripall=False)
|
|
331
|
+
elif self.is_python_function(frame):
|
|
332
|
+
pyframeobject = libpython.Frame(frame).get_pyop()
|
|
333
|
+
|
|
334
|
+
if not pyframeobject:
|
|
335
|
+
raise gdb.GdbError(
|
|
336
|
+
'Unable to read information on python frame')
|
|
337
|
+
|
|
338
|
+
filename = pyframeobject.filename()
|
|
339
|
+
lineno = pyframeobject.current_line_num()
|
|
340
|
+
|
|
341
|
+
if pygments:
|
|
342
|
+
lexer = pygments.lexers.PythonLexer(stripall=False)
|
|
343
|
+
else:
|
|
344
|
+
symbol_and_line_obj = frame.find_sal()
|
|
345
|
+
if not symbol_and_line_obj or not symbol_and_line_obj.symtab:
|
|
346
|
+
filename = None
|
|
347
|
+
lineno = 0
|
|
348
|
+
else:
|
|
349
|
+
filename = symbol_and_line_obj.symtab.fullname()
|
|
350
|
+
lineno = symbol_and_line_obj.line
|
|
351
|
+
if pygments:
|
|
352
|
+
lexer = pygments.lexers.CLexer(stripall=False)
|
|
353
|
+
|
|
354
|
+
return SourceFileDescriptor(filename, lexer), lineno
|
|
355
|
+
|
|
356
|
+
@default_selected_gdb_frame()
|
|
357
|
+
def get_source_line(self, frame):
|
|
358
|
+
source_desc, lineno = self.get_source_desc()
|
|
359
|
+
return source_desc.get_source(lineno)
|
|
360
|
+
|
|
361
|
+
@default_selected_gdb_frame()
|
|
362
|
+
def is_relevant_function(self, frame):
|
|
363
|
+
"""
|
|
364
|
+
returns whether we care about a frame on the user-level when debugging
|
|
365
|
+
Cython code
|
|
366
|
+
"""
|
|
367
|
+
name = frame.name()
|
|
368
|
+
older_frame = frame.older()
|
|
369
|
+
if self.is_cython_function(frame) or self.is_python_function(frame):
|
|
370
|
+
return True
|
|
371
|
+
elif older_frame and self.is_cython_function(older_frame):
|
|
372
|
+
# check for direct C function call from a Cython function
|
|
373
|
+
cython_func = self.get_cython_function(older_frame)
|
|
374
|
+
return name in cython_func.step_into_functions
|
|
375
|
+
|
|
376
|
+
return False
|
|
377
|
+
|
|
378
|
+
@default_selected_gdb_frame(err=False)
|
|
379
|
+
def print_stackframe(self, frame, index, is_c=False):
|
|
380
|
+
"""
|
|
381
|
+
Print a C, Cython or Python stack frame and the line of source code
|
|
382
|
+
if available.
|
|
383
|
+
"""
|
|
384
|
+
# do this to prevent the require_cython_frame decorator from
|
|
385
|
+
# raising GdbError when calling self.cy.cy_cvalue.invoke()
|
|
386
|
+
selected_frame = gdb.selected_frame()
|
|
387
|
+
frame.select()
|
|
388
|
+
|
|
389
|
+
try:
|
|
390
|
+
source_desc, lineno = self.get_source_desc(frame)
|
|
391
|
+
except NoFunctionNameInFrameError:
|
|
392
|
+
print('#%-2d Unknown Frame (compile with -g)' % index)
|
|
393
|
+
return
|
|
394
|
+
|
|
395
|
+
if not is_c and self.is_python_function(frame):
|
|
396
|
+
pyframe = libpython.Frame(frame).get_pyop()
|
|
397
|
+
if pyframe is None or pyframe.is_optimized_out():
|
|
398
|
+
# print this python function as a C function
|
|
399
|
+
return self.print_stackframe(frame, index, is_c=True)
|
|
400
|
+
|
|
401
|
+
func_name = pyframe.co_name
|
|
402
|
+
func_cname = 'PyEval_EvalFrameEx'
|
|
403
|
+
func_args = []
|
|
404
|
+
elif self.is_cython_function(frame):
|
|
405
|
+
cyfunc = self.get_cython_function(frame)
|
|
406
|
+
f = lambda arg: self.cy.cy_cvalue.invoke(arg, frame=frame)
|
|
407
|
+
|
|
408
|
+
func_name = cyfunc.name
|
|
409
|
+
func_cname = cyfunc.cname
|
|
410
|
+
func_args = [] # [(arg, f(arg)) for arg in cyfunc.arguments]
|
|
411
|
+
else:
|
|
412
|
+
source_desc, lineno = self.get_source_desc(frame)
|
|
413
|
+
func_name = frame.name()
|
|
414
|
+
func_cname = func_name
|
|
415
|
+
func_args = []
|
|
416
|
+
|
|
417
|
+
try:
|
|
418
|
+
gdb_value = gdb.parse_and_eval(func_cname)
|
|
419
|
+
except RuntimeError:
|
|
420
|
+
func_address = 0
|
|
421
|
+
else:
|
|
422
|
+
func_address = gdb_value.address
|
|
423
|
+
if not isinstance(func_address, int):
|
|
424
|
+
# Seriously? Why is the address not an int?
|
|
425
|
+
if not isinstance(func_address, (str, bytes)):
|
|
426
|
+
func_address = str(func_address)
|
|
427
|
+
func_address = int(func_address.split()[0], 0)
|
|
428
|
+
|
|
429
|
+
a = ', '.join('%s=%s' % (name, val) for name, val in func_args)
|
|
430
|
+
sys.stdout.write('#%-2d 0x%016x in %s(%s)' % (index, func_address, func_name, a))
|
|
431
|
+
|
|
432
|
+
if source_desc.filename is not None:
|
|
433
|
+
sys.stdout.write(' at %s:%s' % (source_desc.filename, lineno))
|
|
434
|
+
|
|
435
|
+
sys.stdout.write('\n')
|
|
436
|
+
|
|
437
|
+
try:
|
|
438
|
+
sys.stdout.write(f' {source_desc.get_source(lineno)}\n')
|
|
439
|
+
except gdb.GdbError:
|
|
440
|
+
pass
|
|
441
|
+
|
|
442
|
+
selected_frame.select()
|
|
443
|
+
|
|
444
|
+
def get_remote_cython_globals_dict(self):
|
|
445
|
+
m = gdb.parse_and_eval('__pyx_m')
|
|
446
|
+
|
|
447
|
+
try:
|
|
448
|
+
PyModuleObject = gdb.lookup_type('PyModuleObject')
|
|
449
|
+
except RuntimeError:
|
|
450
|
+
raise gdb.GdbError(textwrap.dedent("""\
|
|
451
|
+
Unable to lookup type PyModuleObject, did you compile python
|
|
452
|
+
with debugging support (-g)?"""))
|
|
453
|
+
|
|
454
|
+
m = m.cast(PyModuleObject.pointer())
|
|
455
|
+
return m['md_dict']
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def get_cython_globals_dict(self):
|
|
459
|
+
"""
|
|
460
|
+
Get the Cython globals dict where the remote names are turned into
|
|
461
|
+
local strings.
|
|
462
|
+
"""
|
|
463
|
+
remote_dict = self.get_remote_cython_globals_dict()
|
|
464
|
+
pyobject_dict = libpython.PyObjectPtr.from_pyobject_ptr(remote_dict)
|
|
465
|
+
|
|
466
|
+
result = {}
|
|
467
|
+
seen = set()
|
|
468
|
+
for k, v in pyobject_dict.iteritems():
|
|
469
|
+
result[k.proxyval(seen)] = v
|
|
470
|
+
|
|
471
|
+
return result
|
|
472
|
+
|
|
473
|
+
def print_gdb_value(self, name, value, max_name_length=None, prefix=''):
|
|
474
|
+
if libpython.pretty_printer_lookup(value):
|
|
475
|
+
typename = ''
|
|
476
|
+
else:
|
|
477
|
+
typename = '(%s) ' % (value.type,)
|
|
478
|
+
|
|
479
|
+
if max_name_length is None:
|
|
480
|
+
print('%s%s = %s%s' % (prefix, name, typename, value))
|
|
481
|
+
else:
|
|
482
|
+
print('%s%-*s = %s%s' % (prefix, max_name_length, name, typename, value))
|
|
483
|
+
|
|
484
|
+
def is_initialized(self, cython_func, local_name):
|
|
485
|
+
cyvar = cython_func.locals[local_name]
|
|
486
|
+
cur_lineno = self.get_cython_lineno()[1]
|
|
487
|
+
|
|
488
|
+
if '->' in cyvar.cname:
|
|
489
|
+
# Closed over free variable
|
|
490
|
+
if cur_lineno > cython_func.lineno:
|
|
491
|
+
if cyvar.type == PythonObject:
|
|
492
|
+
return int(gdb.parse_and_eval(cyvar.cname))
|
|
493
|
+
return True
|
|
494
|
+
return False
|
|
495
|
+
|
|
496
|
+
return cur_lineno > cyvar.lineno
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
class SourceFileDescriptor:
|
|
500
|
+
def __init__(self, filename, lexer, formatter=None):
|
|
501
|
+
self.filename = filename
|
|
502
|
+
self.lexer = lexer
|
|
503
|
+
self.formatter = formatter
|
|
504
|
+
|
|
505
|
+
def valid(self):
|
|
506
|
+
return self.filename is not None
|
|
507
|
+
|
|
508
|
+
def lex(self, code):
|
|
509
|
+
if pygments and self.lexer and parameters.colorize_code:
|
|
510
|
+
bg = parameters.terminal_background.value
|
|
511
|
+
if self.formatter is None:
|
|
512
|
+
formatter = pygments.formatters.TerminalFormatter(bg=bg)
|
|
513
|
+
else:
|
|
514
|
+
formatter = self.formatter
|
|
515
|
+
|
|
516
|
+
return pygments.highlight(code, self.lexer, formatter)
|
|
517
|
+
|
|
518
|
+
return code
|
|
519
|
+
|
|
520
|
+
def _get_source(self, start, stop, lex_source, mark_line, lex_entire):
|
|
521
|
+
with open(self.filename) as f:
|
|
522
|
+
# to provide "correct" colouring, the entire code needs to be
|
|
523
|
+
# lexed. However, this makes a lot of things terribly slow, so
|
|
524
|
+
# we decide not to. Besides, it's unlikely to matter.
|
|
525
|
+
|
|
526
|
+
if lex_source and lex_entire:
|
|
527
|
+
f = self.lex(f.read()).splitlines()
|
|
528
|
+
|
|
529
|
+
slice = itertools.islice(f, start - 1, stop - 1)
|
|
530
|
+
|
|
531
|
+
for idx, line in enumerate(slice):
|
|
532
|
+
if start + idx == mark_line:
|
|
533
|
+
prefix = '>'
|
|
534
|
+
else:
|
|
535
|
+
prefix = ' '
|
|
536
|
+
|
|
537
|
+
if lex_source and not lex_entire:
|
|
538
|
+
line = self.lex(line)
|
|
539
|
+
|
|
540
|
+
yield '%s %4d %s' % (prefix, start + idx, line.rstrip())
|
|
541
|
+
|
|
542
|
+
def get_source(self, start, stop=None, lex_source=True, mark_line=0,
|
|
543
|
+
lex_entire=False):
|
|
544
|
+
exc = gdb.GdbError('Unable to retrieve source code')
|
|
545
|
+
|
|
546
|
+
if not self.filename:
|
|
547
|
+
raise exc
|
|
548
|
+
|
|
549
|
+
start = max(start, 1)
|
|
550
|
+
if stop is None:
|
|
551
|
+
stop = start + 1
|
|
552
|
+
|
|
553
|
+
try:
|
|
554
|
+
return '\n'.join(
|
|
555
|
+
self._get_source(start, stop, lex_source, mark_line, lex_entire))
|
|
556
|
+
except OSError:
|
|
557
|
+
raise exc
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
# Errors
|
|
561
|
+
|
|
562
|
+
class CyGDBError(gdb.GdbError):
|
|
563
|
+
"""
|
|
564
|
+
Base class for Cython-command related errors
|
|
565
|
+
"""
|
|
566
|
+
|
|
567
|
+
def __init__(self, *args):
|
|
568
|
+
args = args or (self.msg,)
|
|
569
|
+
super().__init__(*args)
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
class NoCythonFunctionInFrameError(CyGDBError):
|
|
573
|
+
"""
|
|
574
|
+
raised when the user requests the current cython function, which is
|
|
575
|
+
unavailable
|
|
576
|
+
"""
|
|
577
|
+
msg = "Current function is a function cygdb doesn't know about"
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
class NoFunctionNameInFrameError(NoCythonFunctionInFrameError):
|
|
581
|
+
"""
|
|
582
|
+
raised when the name of the C function could not be determined
|
|
583
|
+
in the current C stack frame
|
|
584
|
+
"""
|
|
585
|
+
msg = ('C function name could not be determined in the current C stack '
|
|
586
|
+
'frame')
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
# Parameters
|
|
590
|
+
|
|
591
|
+
class CythonParameter(gdb.Parameter):
|
|
592
|
+
"""
|
|
593
|
+
Base class for cython parameters
|
|
594
|
+
"""
|
|
595
|
+
|
|
596
|
+
def __init__(self, name, command_class, parameter_class, default=None):
|
|
597
|
+
self.show_doc = self.set_doc = self.__class__.__doc__
|
|
598
|
+
super().__init__(name, command_class,
|
|
599
|
+
parameter_class)
|
|
600
|
+
if default is not None:
|
|
601
|
+
self.value = default
|
|
602
|
+
|
|
603
|
+
def __bool__(self):
|
|
604
|
+
return bool(self.value)
|
|
605
|
+
|
|
606
|
+
__nonzero__ = __bool__ # Python 2
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
class CompleteUnqualifiedFunctionNames(CythonParameter):
|
|
611
|
+
"""
|
|
612
|
+
Have 'cy break' complete unqualified function or method names.
|
|
613
|
+
"""
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
class ColorizeSourceCode(CythonParameter):
|
|
617
|
+
"""
|
|
618
|
+
Tell cygdb whether to colorize source code.
|
|
619
|
+
"""
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
class TerminalBackground(CythonParameter):
|
|
623
|
+
"""
|
|
624
|
+
Tell cygdb about the user's terminal background (light or dark).
|
|
625
|
+
"""
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
class CythonParameters:
|
|
629
|
+
"""
|
|
630
|
+
Simple container class that might get more functionality in the distant
|
|
631
|
+
future (mostly to remind us that we're dealing with parameters).
|
|
632
|
+
"""
|
|
633
|
+
|
|
634
|
+
def __init__(self):
|
|
635
|
+
self.complete_unqualified = CompleteUnqualifiedFunctionNames(
|
|
636
|
+
'cy_complete_unqualified',
|
|
637
|
+
gdb.COMMAND_BREAKPOINTS,
|
|
638
|
+
gdb.PARAM_BOOLEAN,
|
|
639
|
+
True)
|
|
640
|
+
self.colorize_code = ColorizeSourceCode(
|
|
641
|
+
'cy_colorize_code',
|
|
642
|
+
gdb.COMMAND_FILES,
|
|
643
|
+
gdb.PARAM_BOOLEAN,
|
|
644
|
+
True)
|
|
645
|
+
self.terminal_background = TerminalBackground(
|
|
646
|
+
'cy_terminal_background_color',
|
|
647
|
+
gdb.COMMAND_FILES,
|
|
648
|
+
gdb.PARAM_STRING,
|
|
649
|
+
"dark")
|
|
650
|
+
|
|
651
|
+
parameters = CythonParameters()
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
# Commands
|
|
655
|
+
|
|
656
|
+
class CythonCommand(gdb.Command, CythonBase):
|
|
657
|
+
"""
|
|
658
|
+
Base class for Cython commands
|
|
659
|
+
"""
|
|
660
|
+
|
|
661
|
+
command_class = gdb.COMMAND_NONE
|
|
662
|
+
|
|
663
|
+
@classmethod
|
|
664
|
+
def _register(cls, clsname, args, kwargs):
|
|
665
|
+
if not hasattr(cls, 'completer_class'):
|
|
666
|
+
return cls(clsname, cls.command_class, *args, **kwargs)
|
|
667
|
+
else:
|
|
668
|
+
return cls(clsname, cls.command_class, cls.completer_class,
|
|
669
|
+
*args, **kwargs)
|
|
670
|
+
|
|
671
|
+
@classmethod
|
|
672
|
+
def register(cls, *args, **kwargs):
|
|
673
|
+
alias = getattr(cls, 'alias', None)
|
|
674
|
+
if alias:
|
|
675
|
+
cls._register(cls.alias, args, kwargs)
|
|
676
|
+
|
|
677
|
+
return cls._register(cls.name, args, kwargs)
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
class CyCy(CythonCommand):
|
|
681
|
+
"""
|
|
682
|
+
Invoke a Cython command. Available commands are:
|
|
683
|
+
|
|
684
|
+
cy import
|
|
685
|
+
cy break
|
|
686
|
+
cy step
|
|
687
|
+
cy next
|
|
688
|
+
cy run
|
|
689
|
+
cy cont
|
|
690
|
+
cy finish
|
|
691
|
+
cy up
|
|
692
|
+
cy down
|
|
693
|
+
cy select
|
|
694
|
+
cy bt / cy backtrace
|
|
695
|
+
cy list
|
|
696
|
+
cy print
|
|
697
|
+
cy set
|
|
698
|
+
cy locals
|
|
699
|
+
cy globals
|
|
700
|
+
cy exec
|
|
701
|
+
"""
|
|
702
|
+
|
|
703
|
+
name = 'cy'
|
|
704
|
+
command_class = gdb.COMMAND_NONE
|
|
705
|
+
completer_class = gdb.COMPLETE_COMMAND
|
|
706
|
+
|
|
707
|
+
def __init__(self, name, command_class, completer_class):
|
|
708
|
+
# keep the signature 2.5 compatible (i.e. do not use f(*a, k=v)
|
|
709
|
+
super(CythonCommand, self).__init__(name, command_class,
|
|
710
|
+
completer_class, prefix=True)
|
|
711
|
+
|
|
712
|
+
commands = dict(
|
|
713
|
+
# GDB commands
|
|
714
|
+
import_ = CyImport.register(),
|
|
715
|
+
break_ = CyBreak.register(),
|
|
716
|
+
step = CyStep.register(),
|
|
717
|
+
next = CyNext.register(),
|
|
718
|
+
run = CyRun.register(),
|
|
719
|
+
cont = CyCont.register(),
|
|
720
|
+
finish = CyFinish.register(),
|
|
721
|
+
up = CyUp.register(),
|
|
722
|
+
down = CyDown.register(),
|
|
723
|
+
select = CySelect.register(),
|
|
724
|
+
bt = CyBacktrace.register(),
|
|
725
|
+
list = CyList.register(),
|
|
726
|
+
print_ = CyPrint.register(),
|
|
727
|
+
locals = CyLocals.register(),
|
|
728
|
+
globals = CyGlobals.register(),
|
|
729
|
+
exec_ = libpython.FixGdbCommand('cy exec', '-cy-exec'),
|
|
730
|
+
_exec = CyExec.register(),
|
|
731
|
+
set = CySet.register(),
|
|
732
|
+
|
|
733
|
+
# GDB functions
|
|
734
|
+
cy_cname = CyCName('cy_cname'),
|
|
735
|
+
cy_cvalue = CyCValue('cy_cvalue'),
|
|
736
|
+
cy_lineno = CyLine('cy_lineno'),
|
|
737
|
+
cy_eval = CyEval('cy_eval'),
|
|
738
|
+
)
|
|
739
|
+
|
|
740
|
+
for command_name, command in commands.items():
|
|
741
|
+
command.cy = self
|
|
742
|
+
setattr(self, command_name, command)
|
|
743
|
+
|
|
744
|
+
self.cy = self
|
|
745
|
+
|
|
746
|
+
# Cython module namespace
|
|
747
|
+
self.cython_namespace = {}
|
|
748
|
+
|
|
749
|
+
# maps (unique) qualified function names (e.g.
|
|
750
|
+
# cythonmodule.ClassName.method_name) to the CythonFunction object
|
|
751
|
+
self.functions_by_qualified_name = {}
|
|
752
|
+
|
|
753
|
+
# unique cnames of Cython functions
|
|
754
|
+
self.functions_by_cname = {}
|
|
755
|
+
|
|
756
|
+
# map function names like method_name to a list of all such
|
|
757
|
+
# CythonFunction objects
|
|
758
|
+
self.functions_by_name = collections.defaultdict(list)
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
class CyImport(CythonCommand):
|
|
762
|
+
"""
|
|
763
|
+
Import debug information outputted by the Cython compiler
|
|
764
|
+
Example: cy import FILE...
|
|
765
|
+
"""
|
|
766
|
+
|
|
767
|
+
name = 'cy import'
|
|
768
|
+
command_class = gdb.COMMAND_STATUS
|
|
769
|
+
completer_class = gdb.COMPLETE_FILENAME
|
|
770
|
+
|
|
771
|
+
@libpython.dont_suppress_errors
|
|
772
|
+
def invoke(self, args, from_tty):
|
|
773
|
+
if isinstance(args, bytes):
|
|
774
|
+
args = args.decode(_filesystemencoding)
|
|
775
|
+
for arg in string_to_argv(args):
|
|
776
|
+
try:
|
|
777
|
+
f = open(arg)
|
|
778
|
+
except OSError as e:
|
|
779
|
+
raise gdb.GdbError('Unable to open file %r: %s' % (args, e.args[1]))
|
|
780
|
+
|
|
781
|
+
t = etree.parse(f)
|
|
782
|
+
|
|
783
|
+
for module in t.getroot():
|
|
784
|
+
cython_module = CythonModule(**module.attrib)
|
|
785
|
+
self.cy.cython_namespace[cython_module.name] = cython_module
|
|
786
|
+
|
|
787
|
+
for variable in module.find('Globals'):
|
|
788
|
+
d = variable.attrib
|
|
789
|
+
cython_module.globals[d['name']] = CythonVariable(**d)
|
|
790
|
+
|
|
791
|
+
for function in module.find('Functions'):
|
|
792
|
+
cython_function = CythonFunction(module=cython_module,
|
|
793
|
+
**function.attrib)
|
|
794
|
+
|
|
795
|
+
# update the global function mappings
|
|
796
|
+
name = cython_function.name
|
|
797
|
+
qname = cython_function.qualified_name
|
|
798
|
+
|
|
799
|
+
self.cy.functions_by_name[name].append(cython_function)
|
|
800
|
+
self.cy.functions_by_qualified_name[
|
|
801
|
+
cython_function.qualified_name] = cython_function
|
|
802
|
+
self.cy.functions_by_cname[
|
|
803
|
+
cython_function.cname] = cython_function
|
|
804
|
+
|
|
805
|
+
d = cython_module.functions[qname] = cython_function
|
|
806
|
+
|
|
807
|
+
for local in function.find('Locals'):
|
|
808
|
+
d = local.attrib
|
|
809
|
+
cython_function.locals[d['name']] = CythonVariable(**d)
|
|
810
|
+
|
|
811
|
+
for step_into_func in function.find('StepIntoFunctions'):
|
|
812
|
+
d = step_into_func.attrib
|
|
813
|
+
cython_function.step_into_functions.add(d['name'])
|
|
814
|
+
|
|
815
|
+
cython_function.arguments.extend(
|
|
816
|
+
funcarg.tag for funcarg in function.find('Arguments'))
|
|
817
|
+
|
|
818
|
+
for marker in module.find('LineNumberMapping'):
|
|
819
|
+
src_lineno = int(marker.attrib['src_lineno'])
|
|
820
|
+
src_path = marker.attrib['src_path']
|
|
821
|
+
c_linenos = list(map(int, marker.attrib['c_linenos'].split()))
|
|
822
|
+
cython_module.lineno_cy2c[src_path, src_lineno] = min(c_linenos)
|
|
823
|
+
for c_lineno in c_linenos:
|
|
824
|
+
cython_module.lineno_c2cy[c_lineno] = (src_path, src_lineno)
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
class CyBreak(CythonCommand):
|
|
828
|
+
"""
|
|
829
|
+
Set a breakpoint for Cython code using Cython qualified name notation, e.g.:
|
|
830
|
+
|
|
831
|
+
cy break cython_modulename.ClassName.method_name...
|
|
832
|
+
|
|
833
|
+
or normal notation:
|
|
834
|
+
|
|
835
|
+
cy break function_or_method_name...
|
|
836
|
+
|
|
837
|
+
or for a line number:
|
|
838
|
+
|
|
839
|
+
cy break cython_module:lineno...
|
|
840
|
+
|
|
841
|
+
Set a Python breakpoint:
|
|
842
|
+
Break on any function or method named 'func' in module 'modname'
|
|
843
|
+
|
|
844
|
+
cy break -p modname.func...
|
|
845
|
+
|
|
846
|
+
Break on any function or method named 'func'
|
|
847
|
+
|
|
848
|
+
cy break -p func...
|
|
849
|
+
"""
|
|
850
|
+
|
|
851
|
+
name = 'cy break'
|
|
852
|
+
command_class = gdb.COMMAND_BREAKPOINTS
|
|
853
|
+
|
|
854
|
+
def _break_pyx(self, name):
|
|
855
|
+
modulename, _, lineno = name.partition(':')
|
|
856
|
+
lineno = int(lineno)
|
|
857
|
+
if modulename:
|
|
858
|
+
cython_module = self.cy.cython_namespace[modulename]
|
|
859
|
+
else:
|
|
860
|
+
cython_module = self.get_cython_function().module
|
|
861
|
+
|
|
862
|
+
if (cython_module.filename, lineno) in cython_module.lineno_cy2c:
|
|
863
|
+
c_lineno = cython_module.lineno_cy2c[cython_module.filename, lineno]
|
|
864
|
+
breakpoint = '%s:%s' % (cython_module.c_filename, c_lineno)
|
|
865
|
+
gdb.execute('break ' + breakpoint)
|
|
866
|
+
else:
|
|
867
|
+
raise gdb.GdbError("Not a valid line number. "
|
|
868
|
+
"Does it contain actual code?")
|
|
869
|
+
|
|
870
|
+
def _break_funcname(self, funcname):
|
|
871
|
+
func = self.cy.functions_by_qualified_name.get(funcname)
|
|
872
|
+
|
|
873
|
+
if func and func.is_initmodule_function:
|
|
874
|
+
func = None
|
|
875
|
+
|
|
876
|
+
break_funcs = [func]
|
|
877
|
+
|
|
878
|
+
if not func:
|
|
879
|
+
funcs = self.cy.functions_by_name.get(funcname) or []
|
|
880
|
+
funcs = [f for f in funcs if not f.is_initmodule_function]
|
|
881
|
+
|
|
882
|
+
if not funcs:
|
|
883
|
+
gdb.execute('break ' + funcname)
|
|
884
|
+
return
|
|
885
|
+
|
|
886
|
+
if len(funcs) > 1:
|
|
887
|
+
# multiple functions, let the user pick one
|
|
888
|
+
print('There are multiple such functions:')
|
|
889
|
+
for idx, func in enumerate(funcs):
|
|
890
|
+
print('%3d) %s' % (idx, func.qualified_name))
|
|
891
|
+
|
|
892
|
+
while True:
|
|
893
|
+
try:
|
|
894
|
+
result = input(
|
|
895
|
+
"Select a function, press 'a' for all "
|
|
896
|
+
"functions or press 'q' or '^D' to quit: ")
|
|
897
|
+
except EOFError:
|
|
898
|
+
return
|
|
899
|
+
else:
|
|
900
|
+
if result.lower() == 'q':
|
|
901
|
+
return
|
|
902
|
+
elif result.lower() == 'a':
|
|
903
|
+
break_funcs = funcs
|
|
904
|
+
break
|
|
905
|
+
elif (result.isdigit() and
|
|
906
|
+
0 <= int(result) < len(funcs)):
|
|
907
|
+
break_funcs = [funcs[int(result)]]
|
|
908
|
+
break
|
|
909
|
+
else:
|
|
910
|
+
print('Not understood...')
|
|
911
|
+
else:
|
|
912
|
+
break_funcs = [funcs[0]]
|
|
913
|
+
|
|
914
|
+
for func in break_funcs:
|
|
915
|
+
gdb.execute('break %s' % func.cname)
|
|
916
|
+
if func.pf_cname:
|
|
917
|
+
gdb.execute('break %s' % func.pf_cname)
|
|
918
|
+
|
|
919
|
+
@libpython.dont_suppress_errors
|
|
920
|
+
def invoke(self, function_names, from_tty):
|
|
921
|
+
if isinstance(function_names, bytes):
|
|
922
|
+
function_names = function_names.decode(_filesystemencoding)
|
|
923
|
+
argv = string_to_argv(function_names)
|
|
924
|
+
if function_names.startswith('-p'):
|
|
925
|
+
argv = argv[1:]
|
|
926
|
+
python_breakpoints = True
|
|
927
|
+
else:
|
|
928
|
+
python_breakpoints = False
|
|
929
|
+
|
|
930
|
+
for funcname in argv:
|
|
931
|
+
if python_breakpoints:
|
|
932
|
+
gdb.execute('py-break %s' % funcname)
|
|
933
|
+
elif ':' in funcname:
|
|
934
|
+
self._break_pyx(funcname)
|
|
935
|
+
else:
|
|
936
|
+
self._break_funcname(funcname)
|
|
937
|
+
|
|
938
|
+
@libpython.dont_suppress_errors
|
|
939
|
+
def complete(self, text, word):
|
|
940
|
+
# https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/python/py-cmd.c;h=7143c1c5f7fdce9316a8c41fc2246bc6a07630d4;hb=HEAD#l140
|
|
941
|
+
word = word or ""
|
|
942
|
+
# Filter init-module functions (breakpoints can be set using
|
|
943
|
+
# modulename:linenumber).
|
|
944
|
+
names = [n for n, L in self.cy.functions_by_name.items()
|
|
945
|
+
if any(not f.is_initmodule_function for f in L)]
|
|
946
|
+
qnames = [n for n, f in self.cy.functions_by_qualified_name.items()
|
|
947
|
+
if not f.is_initmodule_function]
|
|
948
|
+
|
|
949
|
+
if parameters.complete_unqualified:
|
|
950
|
+
all_names = itertools.chain(qnames, names)
|
|
951
|
+
else:
|
|
952
|
+
all_names = qnames
|
|
953
|
+
|
|
954
|
+
words = text.strip().split()
|
|
955
|
+
if not words or '.' not in words[-1]:
|
|
956
|
+
# complete unqualified
|
|
957
|
+
seen = set(text[:-len(word)].split())
|
|
958
|
+
return [n for n in all_names
|
|
959
|
+
if n.startswith(word) and n not in seen]
|
|
960
|
+
|
|
961
|
+
# complete qualified name
|
|
962
|
+
lastword = words[-1]
|
|
963
|
+
compl = [n for n in qnames if n.startswith(lastword)]
|
|
964
|
+
|
|
965
|
+
if len(lastword) > len(word):
|
|
966
|
+
# readline sees something (e.g. a '.') as a word boundary, so don't
|
|
967
|
+
# "recomplete" this prefix
|
|
968
|
+
strip_prefix_length = len(lastword) - len(word)
|
|
969
|
+
compl = [n[strip_prefix_length:] for n in compl]
|
|
970
|
+
|
|
971
|
+
return compl
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
class CythonInfo(CythonBase, libpython.PythonInfo):
|
|
975
|
+
"""
|
|
976
|
+
Implementation of the interface dictated by libpython.LanguageInfo.
|
|
977
|
+
"""
|
|
978
|
+
|
|
979
|
+
def lineno(self, frame):
|
|
980
|
+
# Take care of the Python and Cython levels. We need to care for both
|
|
981
|
+
# as we can't simply dispatch to 'py-step', since that would work for
|
|
982
|
+
# stepping through Python code, but it would not step back into Cython-
|
|
983
|
+
# related code. The C level should be dispatched to the 'step' command.
|
|
984
|
+
if self.is_cython_function(frame):
|
|
985
|
+
return self.get_cython_lineno(frame)[1]
|
|
986
|
+
return super().lineno(frame)
|
|
987
|
+
|
|
988
|
+
def get_source_line(self, frame):
|
|
989
|
+
try:
|
|
990
|
+
line = super().get_source_line(frame)
|
|
991
|
+
except gdb.GdbError:
|
|
992
|
+
return None
|
|
993
|
+
else:
|
|
994
|
+
return line.strip() or None
|
|
995
|
+
|
|
996
|
+
def exc_info(self, frame):
|
|
997
|
+
if self.is_python_function:
|
|
998
|
+
return super().exc_info(frame)
|
|
999
|
+
|
|
1000
|
+
def runtime_break_functions(self):
|
|
1001
|
+
if self.is_cython_function():
|
|
1002
|
+
return self.get_cython_function().step_into_functions
|
|
1003
|
+
return ()
|
|
1004
|
+
|
|
1005
|
+
def static_break_functions(self):
|
|
1006
|
+
result = ['PyEval_EvalFrameEx']
|
|
1007
|
+
result.extend(self.cy.functions_by_cname)
|
|
1008
|
+
return result
|
|
1009
|
+
|
|
1010
|
+
|
|
1011
|
+
class CythonExecutionControlCommand(CythonCommand,
|
|
1012
|
+
libpython.ExecutionControlCommandBase):
|
|
1013
|
+
|
|
1014
|
+
@classmethod
|
|
1015
|
+
def register(cls):
|
|
1016
|
+
return cls(cls.name, cython_info)
|
|
1017
|
+
|
|
1018
|
+
|
|
1019
|
+
class CyStep(CythonExecutionControlCommand, libpython.PythonStepperMixin):
|
|
1020
|
+
"Step through Cython, Python or C code."
|
|
1021
|
+
|
|
1022
|
+
name = 'cy -step'
|
|
1023
|
+
stepinto = True
|
|
1024
|
+
|
|
1025
|
+
@libpython.dont_suppress_errors
|
|
1026
|
+
def invoke(self, args, from_tty):
|
|
1027
|
+
if self.is_python_function():
|
|
1028
|
+
self.python_step(self.stepinto)
|
|
1029
|
+
elif not self.is_cython_function():
|
|
1030
|
+
if self.stepinto:
|
|
1031
|
+
command = 'step'
|
|
1032
|
+
else:
|
|
1033
|
+
command = 'next'
|
|
1034
|
+
|
|
1035
|
+
self.finish_executing(gdb.execute(command, to_string=True))
|
|
1036
|
+
else:
|
|
1037
|
+
self.step(stepinto=self.stepinto)
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
class CyNext(CyStep):
|
|
1041
|
+
"Step-over Cython, Python or C code."
|
|
1042
|
+
|
|
1043
|
+
name = 'cy -next'
|
|
1044
|
+
stepinto = False
|
|
1045
|
+
|
|
1046
|
+
|
|
1047
|
+
class CyRun(CythonExecutionControlCommand):
|
|
1048
|
+
"""
|
|
1049
|
+
Run a Cython program. This is like the 'run' command, except that it
|
|
1050
|
+
displays Cython or Python source lines as well
|
|
1051
|
+
"""
|
|
1052
|
+
|
|
1053
|
+
name = 'cy run'
|
|
1054
|
+
|
|
1055
|
+
invoke = libpython.dont_suppress_errors(CythonExecutionControlCommand.run)
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
class CyCont(CythonExecutionControlCommand):
|
|
1059
|
+
"""
|
|
1060
|
+
Continue a Cython program. This is like the 'run' command, except that it
|
|
1061
|
+
displays Cython or Python source lines as well.
|
|
1062
|
+
"""
|
|
1063
|
+
|
|
1064
|
+
name = 'cy cont'
|
|
1065
|
+
invoke = libpython.dont_suppress_errors(CythonExecutionControlCommand.cont)
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
class CyFinish(CythonExecutionControlCommand):
|
|
1069
|
+
"""
|
|
1070
|
+
Execute until the function returns.
|
|
1071
|
+
"""
|
|
1072
|
+
name = 'cy finish'
|
|
1073
|
+
|
|
1074
|
+
invoke = libpython.dont_suppress_errors(CythonExecutionControlCommand.finish)
|
|
1075
|
+
|
|
1076
|
+
|
|
1077
|
+
class CyUp(CythonCommand):
|
|
1078
|
+
"""
|
|
1079
|
+
Go up a Cython, Python or relevant C frame.
|
|
1080
|
+
"""
|
|
1081
|
+
name = 'cy up'
|
|
1082
|
+
_command = 'up'
|
|
1083
|
+
|
|
1084
|
+
@libpython.dont_suppress_errors
|
|
1085
|
+
def invoke(self, *args):
|
|
1086
|
+
try:
|
|
1087
|
+
gdb.execute(self._command, to_string=True)
|
|
1088
|
+
while not self.is_relevant_function(gdb.selected_frame()):
|
|
1089
|
+
gdb.execute(self._command, to_string=True)
|
|
1090
|
+
except RuntimeError as e:
|
|
1091
|
+
raise gdb.GdbError(*e.args)
|
|
1092
|
+
|
|
1093
|
+
frame = gdb.selected_frame()
|
|
1094
|
+
index = 0
|
|
1095
|
+
while frame:
|
|
1096
|
+
frame = frame.older()
|
|
1097
|
+
index += 1
|
|
1098
|
+
|
|
1099
|
+
self.print_stackframe(index=index - 1)
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
class CyDown(CyUp):
|
|
1103
|
+
"""
|
|
1104
|
+
Go down a Cython, Python or relevant C frame.
|
|
1105
|
+
"""
|
|
1106
|
+
|
|
1107
|
+
name = 'cy down'
|
|
1108
|
+
_command = 'down'
|
|
1109
|
+
|
|
1110
|
+
|
|
1111
|
+
class CySelect(CythonCommand):
|
|
1112
|
+
"""
|
|
1113
|
+
Select a frame. Use frame numbers as listed in `cy backtrace`.
|
|
1114
|
+
This command is useful because `cy backtrace` prints a reversed backtrace.
|
|
1115
|
+
"""
|
|
1116
|
+
|
|
1117
|
+
name = 'cy select'
|
|
1118
|
+
|
|
1119
|
+
@libpython.dont_suppress_errors
|
|
1120
|
+
def invoke(self, stackno, from_tty):
|
|
1121
|
+
try:
|
|
1122
|
+
stackno = int(stackno)
|
|
1123
|
+
except ValueError:
|
|
1124
|
+
raise gdb.GdbError("Not a valid number: %r" % (stackno,))
|
|
1125
|
+
|
|
1126
|
+
frame = gdb.selected_frame()
|
|
1127
|
+
while frame.newer():
|
|
1128
|
+
frame = frame.newer()
|
|
1129
|
+
|
|
1130
|
+
stackdepth = libpython.stackdepth(frame)
|
|
1131
|
+
|
|
1132
|
+
try:
|
|
1133
|
+
gdb.execute('select %d' % (stackdepth - stackno - 1,))
|
|
1134
|
+
except RuntimeError as e:
|
|
1135
|
+
raise gdb.GdbError(*e.args)
|
|
1136
|
+
|
|
1137
|
+
|
|
1138
|
+
class CyBacktrace(CythonCommand):
|
|
1139
|
+
'Print the Cython stack'
|
|
1140
|
+
|
|
1141
|
+
name = 'cy bt'
|
|
1142
|
+
alias = 'cy backtrace'
|
|
1143
|
+
command_class = gdb.COMMAND_STACK
|
|
1144
|
+
completer_class = gdb.COMPLETE_NONE
|
|
1145
|
+
|
|
1146
|
+
@libpython.dont_suppress_errors
|
|
1147
|
+
@require_running_program
|
|
1148
|
+
def invoke(self, args, from_tty):
|
|
1149
|
+
# get the first frame
|
|
1150
|
+
frame = gdb.selected_frame()
|
|
1151
|
+
while frame.older():
|
|
1152
|
+
frame = frame.older()
|
|
1153
|
+
|
|
1154
|
+
print_all = args == '-a'
|
|
1155
|
+
|
|
1156
|
+
index = 0
|
|
1157
|
+
while frame:
|
|
1158
|
+
try:
|
|
1159
|
+
is_relevant = self.is_relevant_function(frame)
|
|
1160
|
+
except CyGDBError:
|
|
1161
|
+
is_relevant = False
|
|
1162
|
+
|
|
1163
|
+
if print_all or is_relevant:
|
|
1164
|
+
self.print_stackframe(frame, index)
|
|
1165
|
+
|
|
1166
|
+
index += 1
|
|
1167
|
+
frame = frame.newer()
|
|
1168
|
+
|
|
1169
|
+
|
|
1170
|
+
class CyList(CythonCommand):
|
|
1171
|
+
"""
|
|
1172
|
+
List Cython source code. To disable to customize colouring see the cy_*
|
|
1173
|
+
parameters.
|
|
1174
|
+
"""
|
|
1175
|
+
|
|
1176
|
+
name = 'cy list'
|
|
1177
|
+
command_class = gdb.COMMAND_FILES
|
|
1178
|
+
completer_class = gdb.COMPLETE_NONE
|
|
1179
|
+
|
|
1180
|
+
@libpython.dont_suppress_errors
|
|
1181
|
+
# @dispatch_on_frame(c_command='list')
|
|
1182
|
+
def invoke(self, _, from_tty):
|
|
1183
|
+
sd, lineno = self.get_source_desc()
|
|
1184
|
+
source = sd.get_source(lineno - 5, lineno + 5, mark_line=lineno,
|
|
1185
|
+
lex_entire=True)
|
|
1186
|
+
print(source)
|
|
1187
|
+
|
|
1188
|
+
|
|
1189
|
+
class CyPrint(CythonCommand):
|
|
1190
|
+
"""
|
|
1191
|
+
Print a Cython variable using 'cy-print x' or 'cy-print module.function.x'
|
|
1192
|
+
"""
|
|
1193
|
+
|
|
1194
|
+
name = 'cy print'
|
|
1195
|
+
command_class = gdb.COMMAND_DATA
|
|
1196
|
+
|
|
1197
|
+
@libpython.dont_suppress_errors
|
|
1198
|
+
def invoke(self, name, from_tty):
|
|
1199
|
+
global_python_dict = self.get_cython_globals_dict()
|
|
1200
|
+
module_globals = self.get_cython_function().module.globals
|
|
1201
|
+
|
|
1202
|
+
if name in global_python_dict:
|
|
1203
|
+
value = global_python_dict[name].get_truncated_repr(libpython.MAX_OUTPUT_LEN)
|
|
1204
|
+
print('%s = %s' % (name, value))
|
|
1205
|
+
#This also would work, but because the output of cy exec is not captured in gdb.execute, TestPrint would fail
|
|
1206
|
+
#self.cy.exec_.invoke("print('"+name+"','=', type(" + name + "), "+name+", flush=True )", from_tty)
|
|
1207
|
+
elif name in module_globals:
|
|
1208
|
+
cname = module_globals[name].cname
|
|
1209
|
+
try:
|
|
1210
|
+
value = gdb.parse_and_eval(cname)
|
|
1211
|
+
except RuntimeError:
|
|
1212
|
+
print("unable to get value of %s" % name)
|
|
1213
|
+
else:
|
|
1214
|
+
if not value.is_optimized_out:
|
|
1215
|
+
self.print_gdb_value(name, value)
|
|
1216
|
+
else:
|
|
1217
|
+
print("%s is optimized out" % name)
|
|
1218
|
+
elif self.is_python_function():
|
|
1219
|
+
return gdb.execute('py-print ' + name)
|
|
1220
|
+
elif self.is_cython_function():
|
|
1221
|
+
value = self.cy.cy_cvalue.invoke(name.lstrip('*'))
|
|
1222
|
+
for c in name:
|
|
1223
|
+
if c == '*':
|
|
1224
|
+
value = value.dereference()
|
|
1225
|
+
else:
|
|
1226
|
+
break
|
|
1227
|
+
|
|
1228
|
+
self.print_gdb_value(name, value)
|
|
1229
|
+
else:
|
|
1230
|
+
gdb.execute('print ' + name)
|
|
1231
|
+
|
|
1232
|
+
def complete(self):
|
|
1233
|
+
if self.is_cython_function():
|
|
1234
|
+
f = self.get_cython_function()
|
|
1235
|
+
return list(itertools.chain(f.locals, f.globals))
|
|
1236
|
+
else:
|
|
1237
|
+
return []
|
|
1238
|
+
|
|
1239
|
+
|
|
1240
|
+
sortkey = lambda item: item[0].lower()
|
|
1241
|
+
|
|
1242
|
+
|
|
1243
|
+
class CyLocals(CythonCommand):
|
|
1244
|
+
"""
|
|
1245
|
+
List the locals from the current Cython frame.
|
|
1246
|
+
"""
|
|
1247
|
+
|
|
1248
|
+
name = 'cy locals'
|
|
1249
|
+
command_class = gdb.COMMAND_STACK
|
|
1250
|
+
completer_class = gdb.COMPLETE_NONE
|
|
1251
|
+
|
|
1252
|
+
@libpython.dont_suppress_errors
|
|
1253
|
+
@dispatch_on_frame(c_command='info locals', python_command='py-locals')
|
|
1254
|
+
def invoke(self, args, from_tty):
|
|
1255
|
+
cython_function = self.get_cython_function()
|
|
1256
|
+
|
|
1257
|
+
if cython_function.is_initmodule_function:
|
|
1258
|
+
self.cy.globals.invoke(args, from_tty)
|
|
1259
|
+
return
|
|
1260
|
+
|
|
1261
|
+
local_cython_vars = cython_function.locals
|
|
1262
|
+
max_name_length = len(max(local_cython_vars, key=len))
|
|
1263
|
+
for name, cyvar in sorted(local_cython_vars.items(), key=sortkey):
|
|
1264
|
+
if self.is_initialized(self.get_cython_function(), cyvar.name):
|
|
1265
|
+
value = gdb.parse_and_eval(cyvar.cname)
|
|
1266
|
+
if not value.is_optimized_out:
|
|
1267
|
+
self.print_gdb_value(cyvar.name, value,
|
|
1268
|
+
max_name_length, '')
|
|
1269
|
+
|
|
1270
|
+
|
|
1271
|
+
class CyGlobals(CyLocals):
|
|
1272
|
+
"""
|
|
1273
|
+
List the globals from the current Cython module.
|
|
1274
|
+
"""
|
|
1275
|
+
|
|
1276
|
+
name = 'cy globals'
|
|
1277
|
+
command_class = gdb.COMMAND_STACK
|
|
1278
|
+
completer_class = gdb.COMPLETE_NONE
|
|
1279
|
+
|
|
1280
|
+
@libpython.dont_suppress_errors
|
|
1281
|
+
@dispatch_on_frame(c_command='info variables', python_command='py-globals')
|
|
1282
|
+
def invoke(self, args, from_tty):
|
|
1283
|
+
global_python_dict = self.get_cython_globals_dict()
|
|
1284
|
+
module_globals = self.get_cython_function().module.globals
|
|
1285
|
+
|
|
1286
|
+
max_globals_len = 0
|
|
1287
|
+
max_globals_dict_len = 0
|
|
1288
|
+
if module_globals:
|
|
1289
|
+
max_globals_len = len(max(module_globals, key=len))
|
|
1290
|
+
if global_python_dict:
|
|
1291
|
+
max_globals_dict_len = len(max(global_python_dict))
|
|
1292
|
+
|
|
1293
|
+
max_name_length = max(max_globals_len, max_globals_dict_len)
|
|
1294
|
+
|
|
1295
|
+
seen = set()
|
|
1296
|
+
print('Python globals:')
|
|
1297
|
+
|
|
1298
|
+
for k, v in sorted(global_python_dict.items(), key=sortkey):
|
|
1299
|
+
v = v.get_truncated_repr(libpython.MAX_OUTPUT_LEN)
|
|
1300
|
+
seen.add(k)
|
|
1301
|
+
print(' %-*s = %s' % (max_name_length, k, v))
|
|
1302
|
+
|
|
1303
|
+
print('C globals:')
|
|
1304
|
+
for name, cyvar in sorted(module_globals.items(), key=sortkey):
|
|
1305
|
+
if name not in seen:
|
|
1306
|
+
try:
|
|
1307
|
+
value = gdb.parse_and_eval(cyvar.cname)
|
|
1308
|
+
except RuntimeError:
|
|
1309
|
+
pass
|
|
1310
|
+
else:
|
|
1311
|
+
if not value.is_optimized_out:
|
|
1312
|
+
self.print_gdb_value(cyvar.name, value,
|
|
1313
|
+
max_name_length, ' ')
|
|
1314
|
+
|
|
1315
|
+
|
|
1316
|
+
class EvaluateOrExecuteCodeMixin:
|
|
1317
|
+
"""
|
|
1318
|
+
Evaluate or execute Python code in a Cython or Python frame. The 'evalcode'
|
|
1319
|
+
method evaluations Python code, prints a traceback if an exception went
|
|
1320
|
+
uncaught, and returns any return value as a gdb.Value (NULL on exception).
|
|
1321
|
+
"""
|
|
1322
|
+
|
|
1323
|
+
def _fill_locals_dict(self, executor, local_dict_pointer):
|
|
1324
|
+
"Fill a remotely allocated dict with values from the Cython C stack"
|
|
1325
|
+
cython_func = self.get_cython_function()
|
|
1326
|
+
|
|
1327
|
+
for name, cyvar in cython_func.locals.items():
|
|
1328
|
+
if (cyvar.type == PythonObject
|
|
1329
|
+
and self.is_initialized(cython_func, name)):
|
|
1330
|
+
|
|
1331
|
+
try:
|
|
1332
|
+
val = gdb.parse_and_eval(cyvar.cname)
|
|
1333
|
+
except RuntimeError:
|
|
1334
|
+
continue
|
|
1335
|
+
else:
|
|
1336
|
+
if val.is_optimized_out:
|
|
1337
|
+
continue
|
|
1338
|
+
|
|
1339
|
+
pystringp = executor.alloc_pystring(name)
|
|
1340
|
+
code = '''
|
|
1341
|
+
(PyObject *) PyDict_SetItem(
|
|
1342
|
+
(PyObject *) %d,
|
|
1343
|
+
(PyObject *) %d,
|
|
1344
|
+
(PyObject *) %s)
|
|
1345
|
+
''' % (local_dict_pointer, pystringp, cyvar.cname)
|
|
1346
|
+
|
|
1347
|
+
try:
|
|
1348
|
+
if gdb.parse_and_eval(code) < 0:
|
|
1349
|
+
gdb.parse_and_eval('PyErr_Print()')
|
|
1350
|
+
raise gdb.GdbError("Unable to execute Python code.")
|
|
1351
|
+
finally:
|
|
1352
|
+
# PyDict_SetItem doesn't steal our reference
|
|
1353
|
+
executor.xdecref(pystringp)
|
|
1354
|
+
|
|
1355
|
+
def _find_first_cython_or_python_frame(self):
|
|
1356
|
+
frame = gdb.selected_frame()
|
|
1357
|
+
while frame:
|
|
1358
|
+
if (self.is_cython_function(frame)
|
|
1359
|
+
or self.is_python_function(frame)):
|
|
1360
|
+
frame.select()
|
|
1361
|
+
return frame
|
|
1362
|
+
|
|
1363
|
+
frame = frame.older()
|
|
1364
|
+
|
|
1365
|
+
raise gdb.GdbError("There is no Cython or Python frame on the stack.")
|
|
1366
|
+
|
|
1367
|
+
def _evalcode_cython(self, executor, code, input_type):
|
|
1368
|
+
with libpython.FetchAndRestoreError():
|
|
1369
|
+
# get the dict of Cython globals and construct a dict in the
|
|
1370
|
+
# inferior with Cython locals
|
|
1371
|
+
global_dict = gdb.parse_and_eval(
|
|
1372
|
+
'(PyObject *) PyModule_GetDict(__pyx_m)')
|
|
1373
|
+
local_dict = gdb.parse_and_eval('(PyObject *) PyDict_New()')
|
|
1374
|
+
|
|
1375
|
+
try:
|
|
1376
|
+
self._fill_locals_dict(executor,
|
|
1377
|
+
libpython.pointervalue(local_dict))
|
|
1378
|
+
result = executor.evalcode(code, input_type, global_dict,
|
|
1379
|
+
local_dict)
|
|
1380
|
+
finally:
|
|
1381
|
+
executor.xdecref(libpython.pointervalue(local_dict))
|
|
1382
|
+
|
|
1383
|
+
return result
|
|
1384
|
+
|
|
1385
|
+
def evalcode(self, code, input_type):
|
|
1386
|
+
"""
|
|
1387
|
+
Evaluate `code` in a Python or Cython stack frame using the given
|
|
1388
|
+
`input_type`.
|
|
1389
|
+
"""
|
|
1390
|
+
frame = self._find_first_cython_or_python_frame()
|
|
1391
|
+
executor = libpython.PythonCodeExecutor()
|
|
1392
|
+
if self.is_python_function(frame):
|
|
1393
|
+
return libpython._evalcode_python(executor, code, input_type)
|
|
1394
|
+
return self._evalcode_cython(executor, code, input_type)
|
|
1395
|
+
|
|
1396
|
+
|
|
1397
|
+
class CyExec(CythonCommand, libpython.PyExec, EvaluateOrExecuteCodeMixin):
|
|
1398
|
+
"""
|
|
1399
|
+
Execute Python code in the nearest Python or Cython frame.
|
|
1400
|
+
"""
|
|
1401
|
+
|
|
1402
|
+
name = '-cy-exec'
|
|
1403
|
+
command_class = gdb.COMMAND_STACK
|
|
1404
|
+
completer_class = gdb.COMPLETE_NONE
|
|
1405
|
+
|
|
1406
|
+
@libpython.dont_suppress_errors
|
|
1407
|
+
def invoke(self, expr, from_tty):
|
|
1408
|
+
expr, input_type = self.readcode(expr)
|
|
1409
|
+
executor = libpython.PythonCodeExecutor()
|
|
1410
|
+
executor.xdecref(self.evalcode(expr, executor.Py_file_input))
|
|
1411
|
+
|
|
1412
|
+
|
|
1413
|
+
class CySet(CythonCommand):
|
|
1414
|
+
"""
|
|
1415
|
+
Set a Cython variable to a certain value
|
|
1416
|
+
|
|
1417
|
+
cy set my_cython_c_variable = 10
|
|
1418
|
+
cy set my_cython_py_variable = $cy_eval("{'doner': 'kebab'}")
|
|
1419
|
+
|
|
1420
|
+
This is equivalent to
|
|
1421
|
+
|
|
1422
|
+
set $cy_value("my_cython_variable") = 10
|
|
1423
|
+
"""
|
|
1424
|
+
|
|
1425
|
+
name = 'cy set'
|
|
1426
|
+
command_class = gdb.COMMAND_DATA
|
|
1427
|
+
completer_class = gdb.COMPLETE_NONE
|
|
1428
|
+
|
|
1429
|
+
@libpython.dont_suppress_errors
|
|
1430
|
+
@require_cython_frame
|
|
1431
|
+
def invoke(self, expr, from_tty):
|
|
1432
|
+
name_and_expr = expr.split('=', 1)
|
|
1433
|
+
if len(name_and_expr) != 2:
|
|
1434
|
+
raise gdb.GdbError("Invalid expression. Use 'cy set var = expr'.")
|
|
1435
|
+
|
|
1436
|
+
varname, expr = name_and_expr
|
|
1437
|
+
cname = self.cy.cy_cname.invoke(varname.strip())
|
|
1438
|
+
gdb.execute("set %s = %s" % (cname, expr))
|
|
1439
|
+
|
|
1440
|
+
|
|
1441
|
+
# Functions
|
|
1442
|
+
|
|
1443
|
+
class CyCName(gdb.Function, CythonBase):
|
|
1444
|
+
"""
|
|
1445
|
+
Get the C name of a Cython variable in the current context.
|
|
1446
|
+
Examples:
|
|
1447
|
+
|
|
1448
|
+
print $cy_cname("function")
|
|
1449
|
+
print $cy_cname("Class.method")
|
|
1450
|
+
print $cy_cname("module.function")
|
|
1451
|
+
"""
|
|
1452
|
+
|
|
1453
|
+
@libpython.dont_suppress_errors
|
|
1454
|
+
@require_cython_frame
|
|
1455
|
+
@gdb_function_value_to_unicode
|
|
1456
|
+
def invoke(self, cyname, frame=None):
|
|
1457
|
+
frame = frame or gdb.selected_frame()
|
|
1458
|
+
cname = None
|
|
1459
|
+
|
|
1460
|
+
if self.is_cython_function(frame):
|
|
1461
|
+
cython_function = self.get_cython_function(frame)
|
|
1462
|
+
if cyname in cython_function.locals:
|
|
1463
|
+
cname = cython_function.locals[cyname].cname
|
|
1464
|
+
elif cyname in cython_function.module.globals:
|
|
1465
|
+
cname = cython_function.module.globals[cyname].cname
|
|
1466
|
+
else:
|
|
1467
|
+
qname = '%s.%s' % (cython_function.module.name, cyname)
|
|
1468
|
+
if qname in cython_function.module.functions:
|
|
1469
|
+
cname = cython_function.module.functions[qname].cname
|
|
1470
|
+
|
|
1471
|
+
if not cname:
|
|
1472
|
+
cname = self.cy.functions_by_qualified_name.get(cyname)
|
|
1473
|
+
|
|
1474
|
+
if not cname:
|
|
1475
|
+
raise gdb.GdbError('No such Cython variable: %s' % cyname)
|
|
1476
|
+
|
|
1477
|
+
return cname
|
|
1478
|
+
|
|
1479
|
+
|
|
1480
|
+
class CyCValue(CyCName):
|
|
1481
|
+
"""
|
|
1482
|
+
Get the value of a Cython variable.
|
|
1483
|
+
"""
|
|
1484
|
+
|
|
1485
|
+
@libpython.dont_suppress_errors
|
|
1486
|
+
@require_cython_frame
|
|
1487
|
+
@gdb_function_value_to_unicode
|
|
1488
|
+
def invoke(self, cyname, frame=None):
|
|
1489
|
+
globals_dict = self.get_cython_globals_dict()
|
|
1490
|
+
cython_function = self.get_cython_function(frame)
|
|
1491
|
+
|
|
1492
|
+
if self.is_initialized(cython_function, cyname):
|
|
1493
|
+
cname = super().invoke(cyname, frame=frame)
|
|
1494
|
+
return gdb.parse_and_eval(cname)
|
|
1495
|
+
elif cyname in globals_dict:
|
|
1496
|
+
return globals_dict[cyname]._gdbval
|
|
1497
|
+
else:
|
|
1498
|
+
raise gdb.GdbError("Variable %s is not initialized." % cyname)
|
|
1499
|
+
|
|
1500
|
+
|
|
1501
|
+
class CyLine(gdb.Function, CythonBase):
|
|
1502
|
+
"""
|
|
1503
|
+
Get the current Cython line.
|
|
1504
|
+
"""
|
|
1505
|
+
|
|
1506
|
+
@libpython.dont_suppress_errors
|
|
1507
|
+
@require_cython_frame
|
|
1508
|
+
def invoke(self):
|
|
1509
|
+
return self.get_cython_lineno()[1]
|
|
1510
|
+
|
|
1511
|
+
|
|
1512
|
+
class CyEval(gdb.Function, CythonBase, EvaluateOrExecuteCodeMixin):
|
|
1513
|
+
"""
|
|
1514
|
+
Evaluate Python code in the nearest Python or Cython frame and return
|
|
1515
|
+
"""
|
|
1516
|
+
|
|
1517
|
+
@libpython.dont_suppress_errors
|
|
1518
|
+
@gdb_function_value_to_unicode
|
|
1519
|
+
def invoke(self, python_expression):
|
|
1520
|
+
input_type = libpython.PythonCodeExecutor.Py_eval_input
|
|
1521
|
+
return self.evalcode(python_expression, input_type)
|
|
1522
|
+
|
|
1523
|
+
|
|
1524
|
+
cython_info = CythonInfo()
|
|
1525
|
+
cy = CyCy.register()
|
|
1526
|
+
cython_info.cy = cy
|
|
1527
|
+
|
|
1528
|
+
|
|
1529
|
+
def register_defines():
|
|
1530
|
+
libpython.source_gdb_script(textwrap.dedent("""\
|
|
1531
|
+
define cy step
|
|
1532
|
+
cy -step
|
|
1533
|
+
end
|
|
1534
|
+
|
|
1535
|
+
define cy next
|
|
1536
|
+
cy -next
|
|
1537
|
+
end
|
|
1538
|
+
|
|
1539
|
+
document cy step
|
|
1540
|
+
%s
|
|
1541
|
+
end
|
|
1542
|
+
|
|
1543
|
+
document cy next
|
|
1544
|
+
%s
|
|
1545
|
+
end
|
|
1546
|
+
""") % (CyStep.__doc__, CyNext.__doc__))
|
|
1547
|
+
|
|
1548
|
+
register_defines()
|