1 /* Cache of styled source file text
2 Copyright (C) 2018-2021 Free Software Foundation, Inc.
4 This file is part of GDB.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 #include "source-cache.h"
21 #include "gdbsupport/scoped_fd.h"
23 #include "cli/cli-style.h"
25 #include "gdbsupport/selftest.h"
29 #ifdef HAVE_SOURCE_HIGHLIGHT
30 /* If Gnulib redirects 'open' and 'close' to its replacements
31 'rpl_open' and 'rpl_close' via cpp macros, including <fstream>
32 below with those macros in effect will cause unresolved externals
33 when GDB is linked. Happens, e.g., in the MinGW build. */
37 #include <srchilite/sourcehighlight.h>
38 #include <srchilite/langmap.h>
41 /* The number of source files we'll cache. */
45 /* See source-cache.h. */
47 source_cache g_source_cache
;
49 /* See source-cache.h. */
52 source_cache::get_plain_source_lines (struct symtab
*s
,
53 const std::string
&fullname
)
55 scoped_fd
desc (open_source_file (s
));
57 perror_with_name (symtab_to_filename_for_display (s
));
60 if (fstat (desc
.get (), &st
) < 0)
61 perror_with_name (symtab_to_filename_for_display (s
));
64 lines
.resize (st
.st_size
);
65 if (myread (desc
.get (), &lines
[0], lines
.size ()) < 0)
66 perror_with_name (symtab_to_filename_for_display (s
));
69 if (SYMTAB_OBJFILE (s
) != NULL
&& SYMTAB_OBJFILE (s
)->obfd
!= NULL
)
70 mtime
= SYMTAB_OBJFILE (s
)->mtime
;
71 else if (current_program_space
->exec_bfd ())
72 mtime
= current_program_space
->ebfd_mtime
;
74 if (mtime
&& mtime
< st
.st_mtime
)
75 warning (_("Source file is more recent than executable."));
77 std::vector
<off_t
> offsets
;
78 offsets
.push_back (0);
79 for (size_t offset
= lines
.find ('\n');
80 offset
!= std::string::npos
;
81 offset
= lines
.find ('\n', offset
))
84 /* A newline at the end does not start a new line. It would
85 seem simpler to just strip the newline in this function, but
86 then "list" won't print the final newline. */
87 if (offset
!= lines
.size ())
88 offsets
.push_back (offset
);
91 offsets
.shrink_to_fit ();
92 m_offset_cache
.emplace (fullname
, std::move (offsets
));
97 #ifdef HAVE_SOURCE_HIGHLIGHT
99 /* Return the Source Highlight language name, given a gdb language
100 LANG. Returns NULL if the language is not known. */
103 get_language_name (enum language lang
)
120 case language_fortran
:
121 return "fortran.lang";
124 /* Not handled by Source Highlight. */
130 case language_pascal
:
131 return "pascal.lang";
133 case language_opencl
:
134 /* Not handled by Source Highlight. */
150 #endif /* HAVE_SOURCE_HIGHLIGHT */
152 /* See source-cache.h. */
155 source_cache::ensure (struct symtab
*s
)
157 std::string fullname
= symtab_to_fullname (s
);
159 size_t size
= m_source_map
.size ();
160 for (int i
= 0; i
< size
; ++i
)
162 if (m_source_map
[i
].fullname
== fullname
)
164 /* This should always hold, because we create the file
165 offsets when reading the file, and never free them
166 without also clearing the contents cache. */
167 gdb_assert (m_offset_cache
.find (fullname
)
168 != m_offset_cache
.end ());
169 /* Not strictly LRU, but at least ensure that the most
170 recently used entry is always the last candidate for
171 deletion. Note that this property is relied upon by at
174 std::swap (m_source_map
[i
], m_source_map
[size
- 1]);
179 std::string contents
;
182 contents
= get_plain_source_lines (s
, fullname
);
184 catch (const gdb_exception_error
&e
)
186 /* If 's' is not found, an exception is thrown. */
190 if (source_styling
&& gdb_stdout
->can_emit_style_escape ())
192 #ifdef HAVE_SOURCE_HIGHLIGHT
193 bool already_styled
= false;
194 const char *lang_name
= get_language_name (SYMTAB_LANGUAGE (s
));
195 if (lang_name
!= nullptr)
197 /* The global source highlight object, or null if one was
198 never constructed. This is stored here rather than in
199 the class so that we don't need to include anything or do
200 conditional compilation in source-cache.h. */
201 static srchilite::SourceHighlight
*highlighter
;
205 if (highlighter
== nullptr)
207 highlighter
= new srchilite::SourceHighlight ("esc.outlang");
208 highlighter
->setStyleFile ("esc.style");
211 std::istringstream
input (contents
);
212 std::ostringstream output
;
213 highlighter
->highlight (input
, output
, lang_name
, fullname
);
214 contents
= output
.str ();
215 already_styled
= true;
219 /* Source Highlight will throw an exception if
220 highlighting fails. One possible reason it can fail
221 is if the language is unknown -- which matters to gdb
222 because Rust support wasn't added until after 3.1.8.
223 Ignore exceptions here and fall back to
224 un-highlighted text. */
229 #endif /* HAVE_SOURCE_HIGHLIGHT */
231 gdb::optional
<std::string
> ext_contents
;
232 ext_contents
= ext_lang_colorize (fullname
, contents
);
233 if (ext_contents
.has_value ())
234 contents
= std::move (*ext_contents
);
238 source_text result
= { std::move (fullname
), std::move (contents
) };
239 m_source_map
.push_back (std::move (result
));
241 if (m_source_map
.size () > MAX_ENTRIES
)
242 m_source_map
.erase (m_source_map
.begin ());
247 /* See source-cache.h. */
250 source_cache::get_line_charpos (struct symtab
*s
,
251 const std::vector
<off_t
> **offsets
)
253 std::string fullname
= symtab_to_fullname (s
);
255 auto iter
= m_offset_cache
.find (fullname
);
256 if (iter
== m_offset_cache
.end ())
260 iter
= m_offset_cache
.find (fullname
);
261 /* cache_source_text ensured this was entered. */
262 gdb_assert (iter
!= m_offset_cache
.end ());
265 *offsets
= &iter
->second
;
269 /* A helper function that extracts the desired source lines from TEXT,
270 putting them into LINES_OUT. The arguments are as for
271 get_source_lines. Returns true on success, false if the line
272 numbers are invalid. */
275 extract_lines (const std::string
&text
, int first_line
, int last_line
,
276 std::string
*lines_out
)
279 std::string::size_type pos
= 0;
280 std::string::size_type first_pos
= std::string::npos
;
282 while (pos
!= std::string::npos
&& lineno
<= last_line
)
284 std::string::size_type new_pos
= text
.find ('\n', pos
);
286 if (lineno
== first_line
)
290 if (lineno
== last_line
|| pos
== std::string::npos
)
292 /* A newline at the end does not start a new line. */
293 if (first_pos
== std::string::npos
294 || first_pos
== text
.size ())
296 if (pos
== std::string::npos
)
300 *lines_out
= text
.substr (first_pos
, pos
- first_pos
);
310 /* See source-cache.h. */
313 source_cache::get_source_lines (struct symtab
*s
, int first_line
,
314 int last_line
, std::string
*lines
)
316 if (first_line
< 1 || last_line
< 1 || first_line
> last_line
)
322 return extract_lines (m_source_map
.back ().contents
,
323 first_line
, last_line
, lines
);
329 static void extract_lines_test ()
331 std::string input_text
= "abc\ndef\nghi\njkl\n";
334 SELF_CHECK (extract_lines (input_text
, 1, 1, &result
)
335 && result
== "abc\n");
336 SELF_CHECK (!extract_lines (input_text
, 2, 1, &result
));
337 SELF_CHECK (extract_lines (input_text
, 1, 2, &result
)
338 && result
== "abc\ndef\n");
339 SELF_CHECK (extract_lines ("abc", 1, 1, &result
)
345 void _initialize_source_cache ();
347 _initialize_source_cache ()
350 selftests::register_test ("source-cache", selftests::extract_lines_test
);