162 lines
5.8 KiB
Python
162 lines
5.8 KiB
Python
|
"""Module for displaying information about Numba's gdb set up"""
|
||
|
from collections import namedtuple
|
||
|
import os
|
||
|
import re
|
||
|
import subprocess
|
||
|
from textwrap import dedent
|
||
|
from numba import config
|
||
|
|
||
|
# Container for the output of the gdb info data collection
|
||
|
_fields = ('binary_loc, extension_loc, py_ver, np_ver, supported')
|
||
|
_gdb_info = namedtuple('_gdb_info', _fields)
|
||
|
|
||
|
|
||
|
class _GDBTestWrapper():
|
||
|
"""Wraps the gdb binary and has methods for checking what the gdb binary
|
||
|
has support for (Python and NumPy)."""
|
||
|
|
||
|
def __init__(self,):
|
||
|
gdb_binary = config.GDB_BINARY
|
||
|
if gdb_binary is None:
|
||
|
msg = ("No valid binary could be found for gdb named: "
|
||
|
f"{config.GDB_BINARY}")
|
||
|
raise ValueError(msg)
|
||
|
self._gdb_binary = gdb_binary
|
||
|
|
||
|
def _run_cmd(self, cmd=()):
|
||
|
gdb_call = [self.gdb_binary, '-q',]
|
||
|
for x in cmd:
|
||
|
gdb_call.append('-ex')
|
||
|
gdb_call.append(x)
|
||
|
gdb_call.extend(['-ex', 'q'])
|
||
|
return subprocess.run(gdb_call, capture_output=True, timeout=10,
|
||
|
text=True)
|
||
|
|
||
|
@property
|
||
|
def gdb_binary(self):
|
||
|
return self._gdb_binary
|
||
|
|
||
|
@classmethod
|
||
|
def success(cls, status):
|
||
|
return status.returncode == 0
|
||
|
|
||
|
def check_launch(self):
|
||
|
"""Checks that gdb will launch ok"""
|
||
|
return self._run_cmd()
|
||
|
|
||
|
def check_python(self):
|
||
|
cmd = ("python from __future__ import print_function; "
|
||
|
"import sys; print(sys.version_info[:2])")
|
||
|
return self._run_cmd((cmd,))
|
||
|
|
||
|
def check_numpy(self):
|
||
|
cmd = ("python from __future__ import print_function; "
|
||
|
"import types; import numpy; "
|
||
|
"print(isinstance(numpy, types.ModuleType))")
|
||
|
return self._run_cmd((cmd,))
|
||
|
|
||
|
def check_numpy_version(self):
|
||
|
cmd = ("python from __future__ import print_function; "
|
||
|
"import types; import numpy;"
|
||
|
"print(numpy.__version__)")
|
||
|
return self._run_cmd((cmd,))
|
||
|
|
||
|
|
||
|
def collect_gdbinfo():
|
||
|
"""Prints information to stdout about the gdb setup that Numba has found"""
|
||
|
|
||
|
# State flags:
|
||
|
gdb_state = None
|
||
|
gdb_has_python = False
|
||
|
gdb_has_numpy = False
|
||
|
gdb_python_version = 'No Python support'
|
||
|
gdb_python_numpy_version = "No NumPy support"
|
||
|
|
||
|
# There are so many ways for gdb to not be working as expected. Surround
|
||
|
# the "is it working" tests with try/except and if there's an exception
|
||
|
# store it for processing later.
|
||
|
try:
|
||
|
# Check gdb exists
|
||
|
gdb_wrapper = _GDBTestWrapper()
|
||
|
|
||
|
# Check gdb works
|
||
|
status = gdb_wrapper.check_launch()
|
||
|
if not gdb_wrapper.success(status):
|
||
|
msg = (f"gdb at '{gdb_wrapper.gdb_binary}' does not appear to work."
|
||
|
f"\nstdout: {status.stdout}\nstderr: {status.stderr}")
|
||
|
raise ValueError(msg)
|
||
|
gdb_state = gdb_wrapper.gdb_binary
|
||
|
except Exception as e:
|
||
|
gdb_state = f"Testing gdb binary failed. Reported Error: {e}"
|
||
|
else:
|
||
|
# Got this far, so gdb works, start checking what it supports
|
||
|
status = gdb_wrapper.check_python()
|
||
|
if gdb_wrapper.success(status):
|
||
|
version_match = re.match(r'\((\d+),\s+(\d+)\)',
|
||
|
status.stdout.strip())
|
||
|
if version_match is not None:
|
||
|
pymajor, pyminor = version_match.groups()
|
||
|
gdb_python_version = f"{pymajor}.{pyminor}"
|
||
|
gdb_has_python = True
|
||
|
|
||
|
status = gdb_wrapper.check_numpy()
|
||
|
if gdb_wrapper.success(status):
|
||
|
if "Traceback" not in status.stderr.strip():
|
||
|
if status.stdout.strip() == 'True':
|
||
|
gdb_has_numpy = True
|
||
|
gdb_python_numpy_version = "Unknown"
|
||
|
# NumPy is present find the version
|
||
|
status = gdb_wrapper.check_numpy_version()
|
||
|
if gdb_wrapper.success(status):
|
||
|
if "Traceback" not in status.stderr.strip():
|
||
|
gdb_python_numpy_version = \
|
||
|
status.stdout.strip()
|
||
|
|
||
|
# Work out what level of print-extension support is present in this gdb
|
||
|
if gdb_has_python:
|
||
|
if gdb_has_numpy:
|
||
|
print_ext_supported = "Full (Python and NumPy supported)"
|
||
|
else:
|
||
|
print_ext_supported = "Partial (Python only, no NumPy support)"
|
||
|
else:
|
||
|
print_ext_supported = "None"
|
||
|
|
||
|
# Work out print ext location
|
||
|
print_ext_file = "gdb_print_extension.py"
|
||
|
print_ext_path = os.path.join(os.path.dirname(__file__), print_ext_file)
|
||
|
|
||
|
# return!
|
||
|
return _gdb_info(gdb_state, print_ext_path, gdb_python_version,
|
||
|
gdb_python_numpy_version, print_ext_supported)
|
||
|
|
||
|
|
||
|
def display_gdbinfo(sep_pos=45):
|
||
|
"""Displays the information collected by collect_gdbinfo.
|
||
|
"""
|
||
|
gdb_info = collect_gdbinfo()
|
||
|
print('-' * 80)
|
||
|
fmt = f'%-{sep_pos}s : %-s'
|
||
|
# Display the information
|
||
|
print(fmt % ("Binary location", gdb_info.binary_loc))
|
||
|
print(fmt % ("Print extension location", gdb_info.extension_loc))
|
||
|
print(fmt % ("Python version", gdb_info.py_ver))
|
||
|
print(fmt % ("NumPy version", gdb_info.np_ver))
|
||
|
print(fmt % ("Numba printing extension support", gdb_info.supported))
|
||
|
|
||
|
print("")
|
||
|
print("To load the Numba gdb printing extension, execute the following "
|
||
|
"from the gdb prompt:")
|
||
|
print(f"\nsource {gdb_info.extension_loc}\n")
|
||
|
print('-' * 80)
|
||
|
warn = """
|
||
|
=============================================================
|
||
|
IMPORTANT: Before sharing you should remove any information
|
||
|
in the above that you wish to keep private e.g. paths.
|
||
|
=============================================================
|
||
|
"""
|
||
|
print(dedent(warn))
|
||
|
|
||
|
|
||
|
if __name__ == '__main__':
|
||
|
display_gdbinfo()
|