gdb: Add maint set ignore-prologue-end-flag
[binutils-gdb.git] / gdb / source-cache.c
1 /* Cache of styled source file text
2 Copyright (C) 2018-2022 Free Software Foundation, Inc.
3
4 This file is part of GDB.
5
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.
10
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.
15
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/>. */
18
19 #include "defs.h"
20 #include "source-cache.h"
21 #include "gdbsupport/scoped_fd.h"
22 #include "source.h"
23 #include "cli/cli-style.h"
24 #include "symtab.h"
25 #include "gdbsupport/selftest.h"
26 #include "objfiles.h"
27 #include "exec.h"
28 #include "cli/cli-cmds.h"
29
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. */
35 #undef open
36 #undef close
37 #include <sstream>
38 #include <srchilite/sourcehighlight.h>
39 #include <srchilite/langmap.h>
40 #endif
41
42 /* The number of source files we'll cache. */
43
44 #define MAX_ENTRIES 5
45
46 /* See source-cache.h. */
47
48 source_cache g_source_cache;
49
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. */
53
54 static bool use_gnu_source_highlight;
55
56 /* The "maint show gnu-source-highlight enabled" command. */
57
58 static void
59 show_use_gnu_source_highlight_enabled (struct ui_file *file, int from_tty,
60 struct cmd_list_element *c,
61 const char *value)
62 {
63 gdb_printf (file,
64 _("Use of GNU Source Highlight library is \"%s\".\n"),
65 value);
66 }
67
68 /* The "maint set gnu-source-highlight enabled" command. */
69
70 static void
71 set_use_gnu_source_highlight_enabled (const char *ignore_args,
72 int from_tty,
73 struct cmd_list_element *c)
74 {
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)
79 {
80 use_gnu_source_highlight = false;
81 error (_("the GNU Source Highlight library is not available"));
82 }
83 #else
84 /* We (might) have just changed how we style source code, discard any
85 previously cached contents. */
86 forget_cached_source_info ();
87 #endif
88 }
89
90 /* See source-cache.h. */
91
92 std::string
93 source_cache::get_plain_source_lines (struct symtab *s,
94 const std::string &fullname)
95 {
96 scoped_fd desc (open_source_file (s));
97 if (desc.get () < 0)
98 perror_with_name (symtab_to_filename_for_display (s));
99
100 struct stat st;
101 if (fstat (desc.get (), &st) < 0)
102 perror_with_name (symtab_to_filename_for_display (s));
103
104 std::string lines;
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));
108
109 time_t mtime = 0;
110 if (s->objfile () != NULL && s->objfile ()->obfd != NULL)
111 mtime = s->objfile ()->mtime;
112 else if (current_program_space->exec_bfd ())
113 mtime = current_program_space->ebfd_mtime;
114
115 if (mtime && mtime < st.st_mtime)
116 warning (_("Source file is more recent than executable."));
117
118 std::vector<off_t> offsets;
119 offsets.push_back (0);
120 for (size_t offset = lines.find ('\n');
121 offset != std::string::npos;
122 offset = lines.find ('\n', offset))
123 {
124 ++offset;
125 /* A newline at the end does not start a new line. It would
126 seem simpler to just strip the newline in this function, but
127 then "list" won't print the final newline. */
128 if (offset != lines.size ())
129 offsets.push_back (offset);
130 }
131
132 offsets.shrink_to_fit ();
133 m_offset_cache.emplace (fullname, std::move (offsets));
134
135 return lines;
136 }
137
138 #ifdef HAVE_SOURCE_HIGHLIGHT
139
140 /* Return the Source Highlight language name, given a gdb language
141 LANG. Returns NULL if the language is not known. */
142
143 static const char *
144 get_language_name (enum language lang)
145 {
146 switch (lang)
147 {
148 case language_c:
149 case language_objc:
150 return "c.lang";
151
152 case language_cplus:
153 return "cpp.lang";
154
155 case language_d:
156 return "d.lang";
157
158 case language_go:
159 return "go.lang";
160
161 case language_fortran:
162 return "fortran.lang";
163
164 case language_m2:
165 /* Not handled by Source Highlight. */
166 break;
167
168 case language_asm:
169 return "asm.lang";
170
171 case language_pascal:
172 return "pascal.lang";
173
174 case language_opencl:
175 /* Not handled by Source Highlight. */
176 break;
177
178 case language_rust:
179 return "rust.lang";
180
181 case language_ada:
182 return "ada.lang";
183
184 default:
185 break;
186 }
187
188 return nullptr;
189 }
190
191 #endif /* HAVE_SOURCE_HIGHLIGHT */
192
193 /* See source-cache.h. */
194
195 bool
196 source_cache::ensure (struct symtab *s)
197 {
198 std::string fullname = symtab_to_fullname (s);
199
200 size_t size = m_source_map.size ();
201 for (int i = 0; i < size; ++i)
202 {
203 if (m_source_map[i].fullname == fullname)
204 {
205 /* This should always hold, because we create the file offsets
206 when reading the file. */
207 gdb_assert (m_offset_cache.find (fullname)
208 != m_offset_cache.end ());
209 /* Not strictly LRU, but at least ensure that the most
210 recently used entry is always the last candidate for
211 deletion. Note that this property is relied upon by at
212 least one caller. */
213 if (i != size - 1)
214 std::swap (m_source_map[i], m_source_map[size - 1]);
215 return true;
216 }
217 }
218
219 std::string contents;
220 try
221 {
222 contents = get_plain_source_lines (s, fullname);
223 }
224 catch (const gdb_exception_error &e)
225 {
226 /* If 's' is not found, an exception is thrown. */
227 return false;
228 }
229
230 if (source_styling && gdb_stdout->can_emit_style_escape ())
231 {
232 #ifdef HAVE_SOURCE_HIGHLIGHT
233 bool already_styled = false;
234 const char *lang_name = get_language_name (s->language ());
235 if (lang_name != nullptr && use_gnu_source_highlight)
236 {
237 /* The global source highlight object, or null if one was
238 never constructed. This is stored here rather than in
239 the class so that we don't need to include anything or do
240 conditional compilation in source-cache.h. */
241 static srchilite::SourceHighlight *highlighter;
242
243 try
244 {
245 if (highlighter == nullptr)
246 {
247 highlighter = new srchilite::SourceHighlight ("esc.outlang");
248 highlighter->setStyleFile ("esc.style");
249 }
250
251 std::istringstream input (contents);
252 std::ostringstream output;
253 highlighter->highlight (input, output, lang_name, fullname);
254 contents = output.str ();
255 already_styled = true;
256 }
257 catch (...)
258 {
259 /* Source Highlight will throw an exception if
260 highlighting fails. One possible reason it can fail
261 is if the language is unknown -- which matters to gdb
262 because Rust support wasn't added until after 3.1.8.
263 Ignore exceptions here and fall back to
264 un-highlighted text. */
265 }
266 }
267
268 if (!already_styled)
269 #endif /* HAVE_SOURCE_HIGHLIGHT */
270 {
271 gdb::optional<std::string> ext_contents;
272 ext_contents = ext_lang_colorize (fullname, contents);
273 if (ext_contents.has_value ())
274 contents = std::move (*ext_contents);
275 }
276 }
277
278 source_text result = { std::move (fullname), std::move (contents) };
279 m_source_map.push_back (std::move (result));
280
281 if (m_source_map.size () > MAX_ENTRIES)
282 {
283 auto iter = m_source_map.begin ();
284 m_offset_cache.erase (iter->fullname);
285 m_source_map.erase (iter);
286 }
287
288 return true;
289 }
290
291 /* See source-cache.h. */
292
293 bool
294 source_cache::get_line_charpos (struct symtab *s,
295 const std::vector<off_t> **offsets)
296 {
297 std::string fullname = symtab_to_fullname (s);
298
299 auto iter = m_offset_cache.find (fullname);
300 if (iter == m_offset_cache.end ())
301 {
302 if (!ensure (s))
303 return false;
304 iter = m_offset_cache.find (fullname);
305 /* cache_source_text ensured this was entered. */
306 gdb_assert (iter != m_offset_cache.end ());
307 }
308
309 *offsets = &iter->second;
310 return true;
311 }
312
313 /* A helper function that extracts the desired source lines from TEXT,
314 putting them into LINES_OUT. The arguments are as for
315 get_source_lines. Returns true on success, false if the line
316 numbers are invalid. */
317
318 static bool
319 extract_lines (const std::string &text, int first_line, int last_line,
320 std::string *lines_out)
321 {
322 int lineno = 1;
323 std::string::size_type pos = 0;
324 std::string::size_type first_pos = std::string::npos;
325
326 while (pos != std::string::npos && lineno <= last_line)
327 {
328 std::string::size_type new_pos = text.find ('\n', pos);
329
330 if (lineno == first_line)
331 first_pos = pos;
332
333 pos = new_pos;
334 if (lineno == last_line || pos == std::string::npos)
335 {
336 /* A newline at the end does not start a new line. */
337 if (first_pos == std::string::npos
338 || first_pos == text.size ())
339 return false;
340 if (pos == std::string::npos)
341 pos = text.size ();
342 else
343 ++pos;
344 *lines_out = text.substr (first_pos, pos - first_pos);
345 return true;
346 }
347 ++lineno;
348 ++pos;
349 }
350
351 return false;
352 }
353
354 /* See source-cache.h. */
355
356 bool
357 source_cache::get_source_lines (struct symtab *s, int first_line,
358 int last_line, std::string *lines)
359 {
360 if (first_line < 1 || last_line < 1 || first_line > last_line)
361 return false;
362
363 if (!ensure (s))
364 return false;
365
366 return extract_lines (m_source_map.back ().contents,
367 first_line, last_line, lines);
368 }
369
370 /* Implement 'maint flush source-cache' command. */
371
372 static void
373 source_cache_flush_command (const char *command, int from_tty)
374 {
375 forget_cached_source_info ();
376 gdb_printf (_("Source cache flushed.\n"));
377 }
378
379 #if GDB_SELF_TEST
380 namespace selftests
381 {
382 static void extract_lines_test ()
383 {
384 std::string input_text = "abc\ndef\nghi\njkl\n";
385 std::string result;
386
387 SELF_CHECK (extract_lines (input_text, 1, 1, &result)
388 && result == "abc\n");
389 SELF_CHECK (!extract_lines (input_text, 2, 1, &result));
390 SELF_CHECK (extract_lines (input_text, 1, 2, &result)
391 && result == "abc\ndef\n");
392 SELF_CHECK (extract_lines ("abc", 1, 1, &result)
393 && result == "abc");
394 }
395 }
396 #endif
397
398 void _initialize_source_cache ();
399 void
400 _initialize_source_cache ()
401 {
402 add_cmd ("source-cache", class_maintenance, source_cache_flush_command,
403 _("Force gdb to flush its source code cache."),
404 &maintenanceflushlist);
405
406 /* All the 'maint set|show gnu-source-highlight' sub-commands. */
407 static struct cmd_list_element *maint_set_gnu_source_highlight_cmdlist;
408 static struct cmd_list_element *maint_show_gnu_source_highlight_cmdlist;
409
410 /* Adds 'maint set|show gnu-source-highlight'. */
411 add_setshow_prefix_cmd ("gnu-source-highlight", class_maintenance,
412 _("Set gnu-source-highlight specific variables."),
413 _("Show gnu-source-highlight specific variables."),
414 &maint_set_gnu_source_highlight_cmdlist,
415 &maint_show_gnu_source_highlight_cmdlist,
416 &maintenance_set_cmdlist,
417 &maintenance_show_cmdlist);
418
419 /* Adds 'maint set|show gnu-source-highlight enabled'. */
420 add_setshow_boolean_cmd ("enabled", class_maintenance,
421 &use_gnu_source_highlight, _("\
422 Set whether the GNU Source Highlight library should be used."), _("\
423 Show whether the GNU Source Highlight library is being used."),_("\
424 When enabled, GDB will use the GNU Source Highlight library to apply\n\
425 styling to source code lines that are shown."),
426 set_use_gnu_source_highlight_enabled,
427 show_use_gnu_source_highlight_enabled,
428 &maint_set_gnu_source_highlight_cmdlist,
429 &maint_show_gnu_source_highlight_cmdlist);
430
431 /* Enable use of GNU Source Highlight library, if we have it. */
432 #ifdef HAVE_SOURCE_HIGHLIGHT
433 use_gnu_source_highlight = true;
434 #endif
435
436 #if GDB_SELF_TEST
437 selftests::register_test ("source-cache", selftests::extract_lines_test);
438 #endif
439 }