Automatic date update in version.in
[binutils-gdb.git] / gdb / source-cache.c
blob6af984fb88bff67f6ec8e93d6ce48248bf8ea12e
1 /* Cache of styled source file text
2 Copyright (C) 2018-2024 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/>. */
19 #include "source-cache.h"
20 #include "gdbsupport/scoped_fd.h"
21 #include "source.h"
22 #include "cli/cli-style.h"
23 #include "symtab.h"
24 #include "objfiles.h"
25 #include "exec.h"
26 #include "cli/cli-cmds.h"
28 #ifdef HAVE_SOURCE_HIGHLIGHT
29 /* If Gnulib redirects 'open' and 'close' to its replacements
30 'rpl_open' and 'rpl_close' via cpp macros, including <fstream>
31 below with those macros in effect will cause unresolved externals
32 when GDB is linked. Happens, e.g., in the MinGW build. */
33 #undef open
34 #undef close
35 #include <sstream>
36 #include <srchilite/sourcehighlight.h>
37 #include <srchilite/langmap.h>
38 #include <srchilite/settings.h>
39 #endif
41 #if GDB_SELF_TEST
42 #include "gdbsupport/selftest.h"
43 #endif
45 /* The number of source files we'll cache. */
47 #define MAX_ENTRIES 5
49 /* See source-cache.h. */
51 source_cache g_source_cache;
53 /* When this is true we will use the GNU Source Highlight to add styling to
54 source code (assuming the library is available). This is initialized to
55 true (if appropriate) in _initialize_source_cache below. */
57 static bool use_gnu_source_highlight;
59 /* The "maint show gnu-source-highlight enabled" command. */
61 static void
62 show_use_gnu_source_highlight_enabled (struct ui_file *file, int from_tty,
63 struct cmd_list_element *c,
64 const char *value)
66 gdb_printf (file,
67 _("Use of GNU Source Highlight library is \"%s\".\n"),
68 value);
71 /* The "maint set gnu-source-highlight enabled" command. */
73 static void
74 set_use_gnu_source_highlight_enabled (const char *ignore_args,
75 int from_tty,
76 struct cmd_list_element *c)
78 #ifndef HAVE_SOURCE_HIGHLIGHT
79 /* If the library is not available and the user tried to enable use of
80 the library, then disable use of the library, and give an error. */
81 if (use_gnu_source_highlight)
83 use_gnu_source_highlight = false;
84 error (_("the GNU Source Highlight library is not available"));
86 #else
87 /* We (might) have just changed how we style source code, discard any
88 previously cached contents. */
89 forget_cached_source_info ();
90 #endif
93 /* See source-cache.h. */
95 std::string
96 source_cache::get_plain_source_lines (struct symtab *s,
97 const std::string &fullname)
99 scoped_fd desc (open_source_file (s));
100 if (desc.get () < 0)
101 perror_with_name (symtab_to_filename_for_display (s), -desc.get ());
103 struct stat st;
104 if (fstat (desc.get (), &st) < 0)
105 perror_with_name (symtab_to_filename_for_display (s));
107 std::string lines;
108 lines.resize (st.st_size);
109 if (myread (desc.get (), &lines[0], lines.size ()) < 0)
110 perror_with_name (symtab_to_filename_for_display (s));
112 time_t mtime = 0;
113 if (s->compunit ()->objfile () != NULL
114 && s->compunit ()->objfile ()->obfd != NULL)
115 mtime = s->compunit ()->objfile ()->mtime;
116 else if (current_program_space->exec_bfd ())
117 mtime = current_program_space->ebfd_mtime;
119 if (mtime && mtime < st.st_mtime)
120 warning (_("Source file is more recent than executable."));
122 std::vector<off_t> offsets;
123 offsets.push_back (0);
124 for (size_t offset = lines.find ('\n');
125 offset != std::string::npos;
126 offset = lines.find ('\n', offset))
128 ++offset;
129 /* A newline at the end does not start a new line. It would
130 seem simpler to just strip the newline in this function, but
131 then "list" won't print the final newline. */
132 if (offset != lines.size ())
133 offsets.push_back (offset);
136 offsets.shrink_to_fit ();
137 m_offset_cache.emplace (fullname, std::move (offsets));
139 return lines;
142 #ifdef HAVE_SOURCE_HIGHLIGHT
144 /* Return the Source Highlight language name, given a gdb language
145 LANG. Returns NULL if the language is not known. */
147 static const char *
148 get_language_name (enum language lang)
150 switch (lang)
152 case language_c:
153 case language_objc:
154 return "c.lang";
156 case language_cplus:
157 return "cpp.lang";
159 case language_d:
160 return "d.lang";
162 case language_go:
163 return "go.lang";
165 case language_fortran:
166 return "fortran.lang";
168 case language_m2:
169 /* Not handled by Source Highlight. */
170 break;
172 case language_asm:
173 return "asm.lang";
175 case language_pascal:
176 return "pascal.lang";
178 case language_opencl:
179 /* Not handled by Source Highlight. */
180 break;
182 case language_rust:
183 return "rust.lang";
185 case language_ada:
186 return "ada.lang";
188 default:
189 break;
192 return nullptr;
195 #endif /* HAVE_SOURCE_HIGHLIGHT */
197 /* Try to highlight CONTENTS from file FULLNAME in language LANG using
198 the GNU source-higlight library. Return true if highlighting
199 succeeded. */
201 static bool
202 try_source_highlight (std::string &contents ATTRIBUTE_UNUSED,
203 enum language lang ATTRIBUTE_UNUSED,
204 const std::string &fullname ATTRIBUTE_UNUSED)
206 #ifdef HAVE_SOURCE_HIGHLIGHT
207 if (!use_gnu_source_highlight)
208 return false;
210 const char *lang_name = get_language_name (lang);
212 /* The global source highlight object, or null if one was
213 never constructed. This is stored here rather than in
214 the class so that we don't need to include anything or do
215 conditional compilation in source-cache.h. */
216 static srchilite::SourceHighlight *highlighter;
218 /* The global source highlight language map object. */
219 static srchilite::LangMap *langmap;
221 bool styled = false;
224 if (highlighter == nullptr)
226 highlighter = new srchilite::SourceHighlight ("esc.outlang");
227 highlighter->setStyleFile ("esc.style");
229 const std::string &datadir = srchilite::Settings::retrieveDataDir ();
230 langmap = new srchilite::LangMap (datadir, "lang.map");
233 std::string detected_lang;
234 if (lang_name == nullptr)
236 detected_lang = langmap->getMappedFileNameFromFileName (fullname);
237 if (detected_lang.empty ())
238 return false;
239 lang_name = detected_lang.c_str ();
242 std::istringstream input (contents);
243 std::ostringstream output;
244 highlighter->highlight (input, output, lang_name, fullname);
245 contents = std::move (output).str ();
246 styled = true;
248 catch (...)
250 /* Source Highlight will throw an exception if
251 highlighting fails. One possible reason it can fail
252 is if the language is unknown -- which matters to gdb
253 because Rust support wasn't added until after 3.1.8.
254 Ignore exceptions here. */
257 return styled;
258 #else
259 return false;
260 #endif /* HAVE_SOURCE_HIGHLIGHT */
263 #ifdef HAVE_SOURCE_HIGHLIGHT
264 #if GDB_SELF_TEST
265 namespace selftests
267 static void gnu_source_highlight_test ()
269 const std::string prog
270 = ("int\n"
271 "foo (void)\n"
272 "{\n"
273 " return 0;\n"
274 "}\n");
275 const std::string fullname = "test.c";
276 std::string styled_prog;
278 bool res = false;
279 bool saw_exception = false;
280 styled_prog = prog;
283 res = try_source_highlight (styled_prog, language_c, fullname);
285 catch (...)
287 saw_exception = true;
290 SELF_CHECK (!saw_exception);
291 if (res)
292 SELF_CHECK (prog.size () < styled_prog.size ());
293 else
294 SELF_CHECK (prog == styled_prog);
297 #endif /* GDB_SELF_TEST */
298 #endif /* HAVE_SOURCE_HIGHLIGHT */
300 /* See source-cache.h. */
302 bool
303 source_cache::ensure (struct symtab *s)
305 std::string fullname = symtab_to_fullname (s);
307 size_t size = m_source_map.size ();
308 for (int i = 0; i < size; ++i)
310 if (m_source_map[i].fullname == fullname)
312 /* This should always hold, because we create the file offsets
313 when reading the file. */
314 gdb_assert (m_offset_cache.find (fullname)
315 != m_offset_cache.end ());
316 /* Not strictly LRU, but at least ensure that the most
317 recently used entry is always the last candidate for
318 deletion. Note that this property is relied upon by at
319 least one caller. */
320 if (i != size - 1)
321 std::swap (m_source_map[i], m_source_map[size - 1]);
322 return true;
326 std::string contents;
329 contents = get_plain_source_lines (s, fullname);
331 catch (const gdb_exception_error &e)
333 /* If 's' is not found, an exception is thrown. */
334 return false;
337 if (source_styling && gdb_stdout->can_emit_style_escape ()
338 && m_no_styling_files.count (fullname) == 0)
340 bool already_styled
341 = try_source_highlight (contents, s->language (), fullname);
343 if (!already_styled)
345 std::optional<std::string> ext_contents;
346 ext_contents = ext_lang_colorize (fullname, contents);
347 if (ext_contents.has_value ())
349 contents = std::move (*ext_contents);
350 already_styled = true;
354 if (!already_styled)
356 /* Styling failed. Styling can fail for instance for these
357 reasons:
358 - the language is not supported.
359 - the language cannot not be auto-detected from the file name.
360 - no stylers available.
362 Since styling failed, don't try styling the file again after it
363 drops from the cache.
365 Note that clearing the source cache also clears
366 m_no_styling_files. */
367 m_no_styling_files.insert (fullname);
371 source_text result = { std::move (fullname), std::move (contents) };
372 m_source_map.push_back (std::move (result));
374 if (m_source_map.size () > MAX_ENTRIES)
376 auto iter = m_source_map.begin ();
377 m_offset_cache.erase (iter->fullname);
378 m_source_map.erase (iter);
381 return true;
384 /* See source-cache.h. */
386 bool
387 source_cache::get_line_charpos (struct symtab *s,
388 const std::vector<off_t> **offsets)
390 std::string fullname = symtab_to_fullname (s);
392 auto iter = m_offset_cache.find (fullname);
393 if (iter == m_offset_cache.end ())
395 if (!ensure (s))
396 return false;
397 iter = m_offset_cache.find (fullname);
398 /* cache_source_text ensured this was entered. */
399 gdb_assert (iter != m_offset_cache.end ());
402 *offsets = &iter->second;
403 return true;
406 /* A helper function that extracts the desired source lines from TEXT,
407 putting them into LINES_OUT. The arguments are as for
408 get_source_lines. Returns true on success, false if the line
409 numbers are invalid. */
411 static bool
412 extract_lines (const std::string &text, int first_line, int last_line,
413 std::string *lines_out)
415 int lineno = 1;
416 std::string::size_type pos = 0;
417 std::string::size_type first_pos = std::string::npos;
419 while (pos != std::string::npos && lineno <= last_line)
421 std::string::size_type new_pos = text.find ('\n', pos);
423 if (lineno == first_line)
424 first_pos = pos;
426 pos = new_pos;
427 if (lineno == last_line || pos == std::string::npos)
429 /* A newline at the end does not start a new line. */
430 if (first_pos == std::string::npos
431 || first_pos == text.size ())
432 return false;
433 if (pos == std::string::npos)
434 pos = text.size ();
435 else
436 ++pos;
437 *lines_out = text.substr (first_pos, pos - first_pos);
438 return true;
440 ++lineno;
441 ++pos;
444 return false;
447 /* See source-cache.h. */
449 bool
450 source_cache::get_source_lines (struct symtab *s, int first_line,
451 int last_line, std::string *lines)
453 if (first_line < 1 || last_line < 1 || first_line > last_line)
454 return false;
456 if (!ensure (s))
457 return false;
459 return extract_lines (m_source_map.back ().contents,
460 first_line, last_line, lines);
463 /* Implement 'maint flush source-cache' command. */
465 static void
466 source_cache_flush_command (const char *command, int from_tty)
468 forget_cached_source_info ();
469 gdb_printf (_("Source cache flushed.\n"));
472 #if GDB_SELF_TEST
473 namespace selftests
475 static void extract_lines_test ()
477 std::string input_text = "abc\ndef\nghi\njkl\n";
478 std::string result;
480 SELF_CHECK (extract_lines (input_text, 1, 1, &result)
481 && result == "abc\n");
482 SELF_CHECK (!extract_lines (input_text, 2, 1, &result));
483 SELF_CHECK (extract_lines (input_text, 1, 2, &result)
484 && result == "abc\ndef\n");
485 SELF_CHECK (extract_lines ("abc", 1, 1, &result)
486 && result == "abc");
489 #endif
491 void _initialize_source_cache ();
492 void
493 _initialize_source_cache ()
495 add_cmd ("source-cache", class_maintenance, source_cache_flush_command,
496 _("Force gdb to flush its source code cache."),
497 &maintenanceflushlist);
499 /* All the 'maint set|show gnu-source-highlight' sub-commands. */
500 static struct cmd_list_element *maint_set_gnu_source_highlight_cmdlist;
501 static struct cmd_list_element *maint_show_gnu_source_highlight_cmdlist;
503 /* Adds 'maint set|show gnu-source-highlight'. */
504 add_setshow_prefix_cmd ("gnu-source-highlight", class_maintenance,
505 _("Set gnu-source-highlight specific variables."),
506 _("Show gnu-source-highlight specific variables."),
507 &maint_set_gnu_source_highlight_cmdlist,
508 &maint_show_gnu_source_highlight_cmdlist,
509 &maintenance_set_cmdlist,
510 &maintenance_show_cmdlist);
512 /* Adds 'maint set|show gnu-source-highlight enabled'. */
513 add_setshow_boolean_cmd ("enabled", class_maintenance,
514 &use_gnu_source_highlight, _("\
515 Set whether the GNU Source Highlight library should be used."), _("\
516 Show whether the GNU Source Highlight library is being used."),_("\
517 When enabled, GDB will use the GNU Source Highlight library to apply\n\
518 styling to source code lines that are shown."),
519 set_use_gnu_source_highlight_enabled,
520 show_use_gnu_source_highlight_enabled,
521 &maint_set_gnu_source_highlight_cmdlist,
522 &maint_show_gnu_source_highlight_cmdlist);
524 /* Enable use of GNU Source Highlight library, if we have it. */
525 #ifdef HAVE_SOURCE_HIGHLIGHT
526 use_gnu_source_highlight = true;
527 #endif
529 #if GDB_SELF_TEST
530 selftests::register_test ("source-cache", selftests::extract_lines_test);
531 #ifdef HAVE_SOURCE_HIGHLIGHT
532 selftests::register_test ("gnu-source-highlight",
533 selftests::gnu_source_highlight_test);
534 #endif
535 #endif