1# Copyright (C) 2010-2023 Free Software Foundation, Inc.
2
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 3 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
16import traceback
17import os
18import sys
19import _gdb
20from contextlib import contextmanager
21
22# Python 3 moved "reload"
23if sys.version_info >= (3, 4):
24    from importlib import reload
25else:
26    from imp import reload
27
28from _gdb import *
29
30# Historically, gdb.events was always available, so ensure it's
31# still available without an explicit import.
32import _gdbevents as events
33
34sys.modules["gdb.events"] = events
35
36
37class _GdbFile(object):
38    # These two are needed in Python 3
39    encoding = "UTF-8"
40    errors = "strict"
41
42    def __init__(self, stream):
43        self.stream = stream
44
45    def close(self):
46        # Do nothing.
47        return None
48
49    def isatty(self):
50        return False
51
52    def writelines(self, iterable):
53        for line in iterable:
54            self.write(line)
55
56    def flush(self):
57        flush(stream=self.stream)
58
59    def write(self, s):
60        write(s, stream=self.stream)
61
62
63sys.stdout = _GdbFile(STDOUT)
64
65sys.stderr = _GdbFile(STDERR)
66
67# Default prompt hook does nothing.
68prompt_hook = None
69
70# Ensure that sys.argv is set to something.
71# We do not use PySys_SetArgvEx because it did not appear until 2.6.6.
72sys.argv = [""]
73
74# Initial pretty printers.
75pretty_printers = []
76
77# Initial type printers.
78type_printers = []
79# Initial xmethod matchers.
80xmethods = []
81# Initial frame filters.
82frame_filters = {}
83# Initial frame unwinders.
84frame_unwinders = []
85
86
87def _execute_unwinders(pending_frame):
88    """Internal function called from GDB to execute all unwinders.
89
90    Runs each currently enabled unwinder until it finds the one that
91    can unwind given frame.
92
93    Arguments:
94        pending_frame: gdb.PendingFrame instance.
95
96    Returns:
97        Tuple with:
98
99          [0] gdb.UnwindInfo instance
100          [1] Name of unwinder that claimed the frame (type `str`)
101
102        or None, if no unwinder has claimed the frame.
103    """
104    for objfile in objfiles():
105        for unwinder in objfile.frame_unwinders:
106            if unwinder.enabled:
107                unwind_info = unwinder(pending_frame)
108                if unwind_info is not None:
109                    return (unwind_info, unwinder.name)
110
111    for unwinder in current_progspace().frame_unwinders:
112        if unwinder.enabled:
113            unwind_info = unwinder(pending_frame)
114            if unwind_info is not None:
115                return (unwind_info, unwinder.name)
116
117    for unwinder in frame_unwinders:
118        if unwinder.enabled:
119            unwind_info = unwinder(pending_frame)
120            if unwind_info is not None:
121                return (unwind_info, unwinder.name)
122
123    return None
124
125
126def _execute_file(filepath):
127    """This function is used to replace Python 2's PyRun_SimpleFile.
128
129    Loads and executes the given file.
130
131    We could use the runpy module, but its documentation says:
132    "Furthermore, any functions and classes defined by the executed code are
133    not guaranteed to work correctly after a runpy function has returned."
134    """
135    globals = sys.modules["__main__"].__dict__
136    set_file = False
137    # Set file (if not set) so that the imported file can use it (e.g. to
138    # access file-relative paths). This matches what PyRun_SimpleFile does.
139    if not hasattr(globals, "__file__"):
140        globals["__file__"] = filepath
141        set_file = True
142    try:
143        with open(filepath, "rb") as file:
144            # We pass globals also as locals to match what Python does
145            # in PyRun_SimpleFile.
146            compiled = compile(file.read(), filepath, "exec")
147            exec(compiled, globals, globals)
148    finally:
149        if set_file:
150            del globals["__file__"]
151
152
153# Convenience variable to GDB's python directory
154PYTHONDIR = os.path.dirname(os.path.dirname(__file__))
155
156# Auto-load all functions/commands.
157
158# Packages to auto-load.
159
160packages = ["function", "command", "printer"]
161
162# pkgutil.iter_modules is not available prior to Python 2.6.  Instead,
163# manually iterate the list, collating the Python files in each module
164# path.  Construct the module name, and import.
165
166
167def _auto_load_packages():
168    for package in packages:
169        location = os.path.join(os.path.dirname(__file__), package)
170        if os.path.exists(location):
171            py_files = filter(
172                lambda x: x.endswith(".py") and x != "__init__.py", os.listdir(location)
173            )
174
175            for py_file in py_files:
176                # Construct from foo.py, gdb.module.foo
177                modname = "%s.%s.%s" % (__name__, package, py_file[:-3])
178                try:
179                    if modname in sys.modules:
180                        # reload modules with duplicate names
181                        reload(__import__(modname))
182                    else:
183                        __import__(modname)
184                except:
185                    sys.stderr.write(traceback.format_exc() + "\n")
186
187
188_auto_load_packages()
189
190
191def GdbSetPythonDirectory(dir):
192    """Update sys.path, reload gdb and auto-load packages."""
193    global PYTHONDIR
194
195    try:
196        sys.path.remove(PYTHONDIR)
197    except ValueError:
198        pass
199    sys.path.insert(0, dir)
200
201    PYTHONDIR = dir
202
203    # note that reload overwrites the gdb module without deleting existing
204    # attributes
205    reload(__import__(__name__))
206    _auto_load_packages()
207
208
209def current_progspace():
210    "Return the current Progspace."
211    return selected_inferior().progspace
212
213
214def objfiles():
215    "Return a sequence of the current program space's objfiles."
216    return current_progspace().objfiles()
217
218
219def solib_name(addr):
220    """solib_name (Long) -> String.\n\
221Return the name of the shared library holding a given address, or None."""
222    return current_progspace().solib_name(addr)
223
224
225def block_for_pc(pc):
226    "Return the block containing the given pc value, or None."
227    return current_progspace().block_for_pc(pc)
228
229
230def find_pc_line(pc):
231    """find_pc_line (pc) -> Symtab_and_line.
232    Return the gdb.Symtab_and_line object corresponding to the pc value."""
233    return current_progspace().find_pc_line(pc)
234
235
236def set_parameter(name, value):
237    """Set the GDB parameter NAME to VALUE."""
238    # Handle the specific cases of None and booleans here, because
239    # gdb.parameter can return them, but they can't be passed to 'set'
240    # this way.
241    if value is None:
242        value = "unlimited"
243    elif isinstance(value, bool):
244        if value:
245            value = "on"
246        else:
247            value = "off"
248    execute("set " + name + " " + str(value), to_string=True)
249
250
251@contextmanager
252def with_parameter(name, value):
253    """Temporarily set the GDB parameter NAME to VALUE.
254    Note that this is a context manager."""
255    old_value = parameter(name)
256    set_parameter(name, value)
257    try:
258        # Nothing that useful to return.
259        yield None
260    finally:
261        set_parameter(name, old_value)
262