1 /* Cache of styled source file text
2 Copyright (C) 2018-2022 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"
28 #include "cli/cli-cmds.h"
30 #ifdef HAVE_SOURCE_HIGHLIGHT
31 /* If Gnulib redirects 'open' and 'close' to its replacements
32 'rpl_open' and 'rpl_close' via cpp macros, including <fstream>
33 below with those macros in effect will cause unresolved externals
34 when GDB is linked. Happens, e.g., in the MinGW build. */
38 #include <srchilite/sourcehighlight.h>
39 #include <srchilite/langmap.h>
42 /* The number of source files we'll cache. */
46 /* See source-cache.h. */
48 source_cache g_source_cache
;
50 /* When this is true we will use the GNU Source Highlight to add styling to
51 source code (assuming the library is available). This is initialized to
52 true (if appropriate) in _initialize_source_cache below. */
54 static bool use_gnu_source_highlight
;
56 /* The "maint show gnu-source-highlight enabled" command. */
59 show_use_gnu_source_highlight_enabled (struct ui_file
*file
, int from_tty
,
60 struct cmd_list_element
*c
,
64 _("Use of GNU Source Highlight library is \"%s\".\n"),
68 /* The "maint set gnu-source-highlight enabled" command. */
71 set_use_gnu_source_highlight_enabled (const char *ignore_args
,
73 struct cmd_list_element
*c
)
75 #ifndef HAVE_SOURCE_HIGHLIGHT
76 /* If the library is not available and the user tried to enable use of
77 the library, then disable use of the library, and give an error. */
78 if (use_gnu_source_highlight
)
80 use_gnu_source_highlight
= false;
81 error (_("the GNU Source Highlight library is not available"));
84 /* We (might) have just changed how we style source code, discard any
85 previously cached contents. */
86 forget_cached_source_info ();
90 /* See source-cache.h. */
93 source_cache::get_plain_source_lines (struct symtab
*s
,
94 const std::string
&fullname
)
96 scoped_fd
desc (open_source_file (s
));
98 perror_with_name (symtab_to_filename_for_display (s
));
101 if (fstat (desc
.get (), &st
) < 0)
102 perror_with_name (symtab_to_filename_for_display (s
));
105 lines
.resize (st
.st_size
);
106 if (myread (desc
.get (), &lines
[0], lines
.size ()) < 0)
107 perror_with_name (symtab_to_filename_for_display (s
));
110 if (s
->compunit ()->objfile () != NULL
111 && s
->compunit ()->objfile ()->obfd
!= NULL
)
112 mtime
= s
->compunit ()->objfile ()->mtime
;
113 else if (current_program_space
->exec_bfd ())
114 mtime
= current_program_space
->ebfd_mtime
;
116 if (mtime
&& mtime
< st
.st_mtime
)
117 warning (_("Source file is more recent than executable."));
119 std::vector
<off_t
> offsets
;
120 offsets
.push_back (0);
121 for (size_t offset
= lines
.find ('\n');
122 offset
!= std::string::npos
;
123 offset
= lines
.find ('\n', offset
))
126 /* A newline at the end does not start a new line. It would
127 seem simpler to just strip the newline in this function, but
128 then "list" won't print the final newline. */
129 if (offset
!= lines
.size ())
130 offsets
.push_back (offset
);
133 offsets
.shrink_to_fit ();
134 m_offset_cache
.emplace (fullname
, std::move (offsets
));
139 #ifdef HAVE_SOURCE_HIGHLIGHT
141 /* Return the Source Highlight language name, given a gdb language
142 LANG. Returns NULL if the language is not known. */
145 get_language_name (enum language lang
)
162 case language_fortran
:
163 return "fortran.lang";
166 /* Not handled by Source Highlight. */
172 case language_pascal
:
173 return "pascal.lang";
175 case language_opencl
:
176 /* Not handled by Source Highlight. */
192 #endif /* HAVE_SOURCE_HIGHLIGHT */
194 /* See source-cache.h. */
197 source_cache::ensure (struct symtab
*s
)
199 std::string fullname
= symtab_to_fullname (s
);
201 size_t size
= m_source_map
.size ();
202 for (int i
= 0; i
< size
; ++i
)
204 if (m_source_map
[i
].fullname
== fullname
)
206 /* This should always hold, because we create the file offsets
207 when reading the file. */
208 gdb_assert (m_offset_cache
.find (fullname
)
209 != m_offset_cache
.end ());
210 /* Not strictly LRU, but at least ensure that the most
211 recently used entry is always the last candidate for
212 deletion. Note that this property is relied upon by at
215 std::swap (m_source_map
[i
], m_source_map
[size
- 1]);
220 std::string contents
;
223 contents
= get_plain_source_lines (s
, fullname
);
225 catch (const gdb_exception_error
&e
)
227 /* If 's' is not found, an exception is thrown. */
231 if (source_styling
&& gdb_stdout
->can_emit_style_escape ())
233 #ifdef HAVE_SOURCE_HIGHLIGHT
234 bool already_styled
= false;
235 const char *lang_name
= get_language_name (s
->language ());
236 if (lang_name
!= nullptr && use_gnu_source_highlight
)
238 /* The global source highlight object, or null if one was
239 never constructed. This is stored here rather than in
240 the class so that we don't need to include anything or do
241 conditional compilation in source-cache.h. */
242 static srchilite::SourceHighlight
*highlighter
;
246 if (highlighter
== nullptr)
248 highlighter
= new srchilite::SourceHighlight ("esc.outlang");
249 highlighter
->setStyleFile ("esc.style");
252 std::istringstream
input (contents
);
253 std::ostringstream output
;
254 highlighter
->highlight (input
, output
, lang_name
, fullname
);
255 contents
= output
.str ();
256 already_styled
= true;
260 /* Source Highlight will throw an exception if
261 highlighting fails. One possible reason it can fail
262 is if the language is unknown -- which matters to gdb
263 because Rust support wasn't added until after 3.1.8.
264 Ignore exceptions here and fall back to
265 un-highlighted text. */
270 #endif /* HAVE_SOURCE_HIGHLIGHT */
272 gdb::optional
<std::string
> ext_contents
;
273 ext_contents
= ext_lang_colorize (fullname
, contents
);
274 if (ext_contents
.has_value ())
275 contents
= std::move (*ext_contents
);
279 source_text result
= { std::move (fullname
), std::move (contents
) };
280 m_source_map
.push_back (std::move (result
));
282 if (m_source_map
.size () > MAX_ENTRIES
)
284 auto iter
= m_source_map
.begin ();
285 m_offset_cache
.erase (iter
->fullname
);
286 m_source_map
.erase (iter
);
292 /* See source-cache.h. */
295 source_cache::get_line_charpos (struct symtab
*s
,
296 const std::vector
<off_t
> **offsets
)
298 std::string fullname
= symtab_to_fullname (s
);
300 auto iter
= m_offset_cache
.find (fullname
);
301 if (iter
== m_offset_cache
.end ())
305 iter
= m_offset_cache
.find (fullname
);
306 /* cache_source_text ensured this was entered. */
307 gdb_assert (iter
!= m_offset_cache
.end ());
310 *offsets
= &iter
->second
;
314 /* A helper function that extracts the desired source lines from TEXT,
315 putting them into LINES_OUT. The arguments are as for
316 get_source_lines. Returns true on success, false if the line
317 numbers are invalid. */
320 extract_lines (const std::string
&text
, int first_line
, int last_line
,
321 std::string
*lines_out
)
324 std::string::size_type pos
= 0;
325 std::string::size_type first_pos
= std::string::npos
;
327 while (pos
!= std::string::npos
&& lineno
<= last_line
)
329 std::string::size_type new_pos
= text
.find ('\n', pos
);
331 if (lineno
== first_line
)
335 if (lineno
== last_line
|| pos
== std::string::npos
)
337 /* A newline at the end does not start a new line. */
338 if (first_pos
== std::string::npos
339 || first_pos
== text
.size ())
341 if (pos
== std::string::npos
)
345 *lines_out
= text
.substr (first_pos
, pos
- first_pos
);
355 /* See source-cache.h. */
358 source_cache::get_source_lines (struct symtab
*s
, int first_line
,
359 int last_line
, std::string
*lines
)
361 if (first_line
< 1 || last_line
< 1 || first_line
> last_line
)
367 return extract_lines (m_source_map
.back ().contents
,
368 first_line
, last_line
, lines
);
371 /* Implement 'maint flush source-cache' command. */
374 source_cache_flush_command (const char *command
, int from_tty
)
376 forget_cached_source_info ();
377 gdb_printf (_("Source cache flushed.\n"));
383 static void extract_lines_test ()
385 std::string input_text
= "abc\ndef\nghi\njkl\n";
388 SELF_CHECK (extract_lines (input_text
, 1, 1, &result
)
389 && result
== "abc\n");
390 SELF_CHECK (!extract_lines (input_text
, 2, 1, &result
));
391 SELF_CHECK (extract_lines (input_text
, 1, 2, &result
)
392 && result
== "abc\ndef\n");
393 SELF_CHECK (extract_lines ("abc", 1, 1, &result
)
399 void _initialize_source_cache ();
401 _initialize_source_cache ()
403 add_cmd ("source-cache", class_maintenance
, source_cache_flush_command
,
404 _("Force gdb to flush its source code cache."),
405 &maintenanceflushlist
);
407 /* All the 'maint set|show gnu-source-highlight' sub-commands. */
408 static struct cmd_list_element
*maint_set_gnu_source_highlight_cmdlist
;
409 static struct cmd_list_element
*maint_show_gnu_source_highlight_cmdlist
;
411 /* Adds 'maint set|show gnu-source-highlight'. */
412 add_setshow_prefix_cmd ("gnu-source-highlight", class_maintenance
,
413 _("Set gnu-source-highlight specific variables."),
414 _("Show gnu-source-highlight specific variables."),
415 &maint_set_gnu_source_highlight_cmdlist
,
416 &maint_show_gnu_source_highlight_cmdlist
,
417 &maintenance_set_cmdlist
,
418 &maintenance_show_cmdlist
);
420 /* Adds 'maint set|show gnu-source-highlight enabled'. */
421 add_setshow_boolean_cmd ("enabled", class_maintenance
,
422 &use_gnu_source_highlight
, _("\
423 Set whether the GNU Source Highlight library should be used."), _("\
424 Show whether the GNU Source Highlight library is being used."),_("\
425 When enabled, GDB will use the GNU Source Highlight library to apply\n\
426 styling to source code lines that are shown."),
427 set_use_gnu_source_highlight_enabled
,
428 show_use_gnu_source_highlight_enabled
,
429 &maint_set_gnu_source_highlight_cmdlist
,
430 &maint_show_gnu_source_highlight_cmdlist
);
432 /* Enable use of GNU Source Highlight library, if we have it. */
433 #ifdef HAVE_SOURCE_HIGHLIGHT
434 use_gnu_source_highlight
= true;
438 selftests::register_test ("source-cache", selftests::extract_lines_test
);