Files
binutils-gdb/gdb/python/lib/gdb/dap/sources.py
Tom Tromey de5b5fcb89 Automatically run (most) DAP requests in gdb thread
Nearly every DAP request implementation forwards its work to the gdb
thread, using send_gdb_with_response.  This patch refactors the
'request' decorator to make this automatic, and to provide some
parameters so that the unusual requests can express their needs as
well.

In a few spots this simplifies the code by removing an unnecessary
helper function.  This could be done in more places as well if we
wanted.

The main motivation for this patch is that I thought it would be
helpful for cancellation.  I am still working on that, but meanwhile
the parameterization of 'request' makes it easy to handle the
'notStopped' response as well.

Reviewed-by: Kévin Le Gouguec <legouguec@adacore.com>
(cherry picked from commit c98921b258)
2023-11-17 08:43:04 -07:00

103 lines
2.9 KiB
Python

# Copyright 2023 Free Software Foundation, Inc.
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import gdb
from .server import request, capability
from .startup import in_gdb_thread
# The next available source reference ID. Must be greater than 0.
_next_source = 1
# Map from full paths to Source dictionaries.
_source_map = {}
# Map from a source reference ID back to the same Source that is
# stored in _source_map.
_id_map = {}
@in_gdb_thread
def make_source(fullname, filename):
"""Return the Source for a given file name.
FULLNAME is the full name. This is used as the key.
FILENAME is the base name.
"""
global _source_map
if fullname in _source_map:
result = _source_map[fullname]
else:
result = {
"name": filename,
"path": fullname,
}
if not os.path.exists(fullname):
global _next_source
result["sourceReference"] = _next_source
global _id_map
_id_map[_next_source] = result
_next_source += 1
_source_map[fullname] = result
return result
@in_gdb_thread
def decode_source(source):
"""Decode a Source object.
Finds and returns the filename of a given Source object."""
if "path" in source:
return source["path"]
if "sourceReference" not in source:
raise Exception("either 'path' or 'sourceReference' must appear in Source")
ref = source["sourceReference"]
global _id_map
if ref not in _id_map:
raise Exception("no sourceReference " + str(ref))
return _id_map[ref]["path"]
@request("loadedSources")
@capability("supportsLoadedSourcesRequest")
def loaded_sources(**extra):
result = []
for elt in gdb.execute_mi("-file-list-exec-source-files")["files"]:
result.append(make_source(elt["fullname"], elt["file"]))
return {
"sources": result,
}
@request("source")
def source(*, source=None, sourceReference: int, **extra):
# The 'sourceReference' parameter is required by the spec, but is
# for backward compatibility, which I take to mean that the
# 'source' is preferred.
if source is None:
source = {"sourceReference": sourceReference}
filename = decode_source(source)
with open(filename) as f:
content = f.read()
return {
"content": content,
}