GDB (xrefs)
Loading...
Searching...
No Matches
source-cache.c
Go to the documentation of this file.
1/* Cache of styled source file text
2 Copyright (C) 2018-2023 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
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
55
56/* The "maint show gnu-source-highlight enabled" command. */
57
58static void
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
70static void
71set_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. */
79 {
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. */
87#endif
88}
89
90/* See source-cache.h. */
91
92std::string
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), -desc.get ());
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->compunit ()->objfile () != NULL
111 && s->compunit ()->objfile ()->obfd != NULL)
112 mtime = s->compunit ()->objfile ()->mtime;
113 else if (current_program_space->exec_bfd ())
115
116 if (mtime && mtime < st.st_mtime)
117 warning (_("Source file is more recent than executable."));
118
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))
124 {
125 ++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);
131 }
132
133 offsets.shrink_to_fit ();
134 m_offset_cache.emplace (fullname, std::move (offsets));
135
136 return lines;
137}
138
139#ifdef HAVE_SOURCE_HIGHLIGHT
140
141/* Return the Source Highlight language name, given a gdb language
142 LANG. Returns NULL if the language is not known. */
143
144static const char *
145get_language_name (enum language lang)
146{
147 switch (lang)
148 {
149 case language_c:
150 case language_objc:
151 return "c.lang";
152
153 case language_cplus:
154 return "cpp.lang";
155
156 case language_d:
157 return "d.lang";
158
159 case language_go:
160 return "go.lang";
161
162 case language_fortran:
163 return "fortran.lang";
164
165 case language_m2:
166 /* Not handled by Source Highlight. */
167 break;
168
169 case language_asm:
170 return "asm.lang";
171
172 case language_pascal:
173 return "pascal.lang";
174
175 case language_opencl:
176 /* Not handled by Source Highlight. */
177 break;
178
179 case language_rust:
180 return "rust.lang";
181
182 case language_ada:
183 return "ada.lang";
184
185 default:
186 break;
187 }
188
189 return nullptr;
190}
191
192#endif /* HAVE_SOURCE_HIGHLIGHT */
193
194/* See source-cache.h. */
195
196bool
198{
199 std::string fullname = symtab_to_fullname (s);
200
201 size_t size = m_source_map.size ();
202 for (int i = 0; i < size; ++i)
203 {
204 if (m_source_map[i].fullname == fullname)
205 {
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
213 least one caller. */
214 if (i != size - 1)
215 std::swap (m_source_map[i], m_source_map[size - 1]);
216 return true;
217 }
218 }
219
220 std::string contents;
221 try
222 {
223 contents = get_plain_source_lines (s, fullname);
224 }
225 catch (const gdb_exception_error &e)
226 {
227 /* If 's' is not found, an exception is thrown. */
228 return false;
229 }
230
231 if (source_styling && gdb_stdout->can_emit_style_escape ())
232 {
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)
237 {
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;
243
244 try
245 {
246 if (highlighter == nullptr)
247 {
248 highlighter = new srchilite::SourceHighlight ("esc.outlang");
249 highlighter->setStyleFile ("esc.style");
250 }
251
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;
257 }
258 catch (...)
259 {
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. */
266 }
267 }
268
269 if (!already_styled)
270#endif /* HAVE_SOURCE_HIGHLIGHT */
271 {
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);
276 }
277 }
278
279 source_text result = { std::move (fullname), std::move (contents) };
280 m_source_map.push_back (std::move (result));
281
282 if (m_source_map.size () > MAX_ENTRIES)
283 {
284 auto iter = m_source_map.begin ();
285 m_offset_cache.erase (iter->fullname);
286 m_source_map.erase (iter);
287 }
288
289 return true;
290}
291
292/* See source-cache.h. */
293
294bool
296 const std::vector<off_t> **offsets)
297{
298 std::string fullname = symtab_to_fullname (s);
299
300 auto iter = m_offset_cache.find (fullname);
301 if (iter == m_offset_cache.end ())
302 {
303 if (!ensure (s))
304 return false;
305 iter = m_offset_cache.find (fullname);
306 /* cache_source_text ensured this was entered. */
307 gdb_assert (iter != m_offset_cache.end ());
308 }
309
310 *offsets = &iter->second;
311 return true;
312}
313
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. */
318
319static bool
320extract_lines (const std::string &text, int first_line, int last_line,
321 std::string *lines_out)
322{
323 int lineno = 1;
324 std::string::size_type pos = 0;
325 std::string::size_type first_pos = std::string::npos;
326
327 while (pos != std::string::npos && lineno <= last_line)
328 {
329 std::string::size_type new_pos = text.find ('\n', pos);
330
331 if (lineno == first_line)
332 first_pos = pos;
333
334 pos = new_pos;
335 if (lineno == last_line || pos == std::string::npos)
336 {
337 /* A newline at the end does not start a new line. */
338 if (first_pos == std::string::npos
339 || first_pos == text.size ())
340 return false;
341 if (pos == std::string::npos)
342 pos = text.size ();
343 else
344 ++pos;
345 *lines_out = text.substr (first_pos, pos - first_pos);
346 return true;
347 }
348 ++lineno;
349 ++pos;
350 }
351
352 return false;
353}
354
355/* See source-cache.h. */
356
357bool
358source_cache::get_source_lines (struct symtab *s, int first_line,
359 int last_line, std::string *lines)
360{
361 if (first_line < 1 || last_line < 1 || first_line > last_line)
362 return false;
363
364 if (!ensure (s))
365 return false;
366
367 return extract_lines (m_source_map.back ().contents,
368 first_line, last_line, lines);
369}
370
371/* Implement 'maint flush source-cache' command. */
372
373static void
374source_cache_flush_command (const char *command, int from_tty)
375{
377 gdb_printf (_("Source cache flushed.\n"));
378}
379
380#if GDB_SELF_TEST
381namespace selftests
382{
383static void extract_lines_test ()
384{
385 std::string input_text = "abc\ndef\nghi\njkl\n";
386 std::string result;
387
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)
394 && result == "abc");
395}
396}
397#endif
398
400void
402{
404 _("Force gdb to flush its source code cache."),
406
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;
410
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,
419
420 /* Adds 'maint set|show gnu-source-highlight enabled'. */
423Set whether the GNU Source Highlight library should be used."), _("\
424Show whether the GNU Source Highlight library is being used."),_("\
425When enabled, GDB will use the GNU Source Highlight library to apply\n\
426styling to source code lines that are shown."),
429 &maint_set_gnu_source_highlight_cmdlist,
430 &maint_show_gnu_source_highlight_cmdlist);
431
432 /* Enable use of GNU Source Highlight library, if we have it. */
433#ifdef HAVE_SOURCE_HIGHLIGHT
435#endif
436
437#if GDB_SELF_TEST
438 selftests::register_test ("source-cache", selftests::extract_lines_test);
439#endif
440}
bool get_source_lines(struct symtab *s, int first_line, int last_line, std::string *lines_out)
std::vector< source_text > m_source_map
bool ensure(struct symtab *s)
bool get_line_charpos(struct symtab *s, const std::vector< off_t > **offsets)
std::string get_plain_source_lines(struct symtab *s, const std::string &fullname)
std::unordered_map< std::string, std::vector< off_t > > m_offset_cache
struct cmd_list_element * maintenanceflushlist
Definition cli-cmds.c:159
struct cmd_list_element * maintenance_show_cmdlist
Definition maint.c:752
struct cmd_list_element * maintenance_set_cmdlist
Definition maint.c:751
struct cmd_list_element * add_cmd(const char *name, enum command_class theclass, const char *doc, struct cmd_list_element **list)
Definition cli-decode.c:233
set_show_commands add_setshow_prefix_cmd(const char *name, command_class theclass, const char *set_doc, const char *show_doc, cmd_list_element **set_subcommands_list, cmd_list_element **show_subcommands_list, cmd_list_element **set_list, cmd_list_element **show_list)
Definition cli-decode.c:428
set_show_commands add_setshow_boolean_cmd(const char *name, enum command_class theclass, bool *var, const char *set_doc, const char *show_doc, const char *help_doc, cmd_func_ftype *set_func, show_value_ftype *show_func, struct cmd_list_element **set_list, struct cmd_list_element **show_list)
Definition cli-decode.c:809
bool source_styling
Definition cli-style.c:39
@ class_maintenance
Definition command.h:65
language
Definition defs.h:211
@ language_ada
Definition defs.h:225
@ language_m2
Definition defs.h:220
@ language_cplus
Definition defs.h:216
@ language_go
Definition defs.h:218
@ language_asm
Definition defs.h:221
@ language_fortran
Definition defs.h:219
@ language_opencl
Definition defs.h:223
@ language_pascal
Definition defs.h:222
@ language_rust
Definition defs.h:215
@ language_c
Definition defs.h:213
@ language_d
Definition defs.h:217
@ language_objc
Definition defs.h:214
gdb::optional< std::string > ext_lang_colorize(const std::string &filename, const std::string &contents)
Definition extension.c:943
size_t size
Definition go32-nat.c:239
struct program_space * current_program_space
Definition progspace.c:40
static void set_use_gnu_source_highlight_enabled(const char *ignore_args, int from_tty, struct cmd_list_element *c)
static bool use_gnu_source_highlight
source_cache g_source_cache
void _initialize_source_cache()
#define MAX_ENTRIES
static void source_cache_flush_command(const char *command, int from_tty)
static bool extract_lines(const std::string &text, int first_line, int last_line, std::string *lines_out)
static void show_use_gnu_source_highlight_enabled(struct ui_file *file, int from_tty, struct cmd_list_element *c, const char *value)
const char * symtab_to_fullname(struct symtab *s)
Definition source.c:1234
const char * symtab_to_filename_for_display(struct symtab *symtab)
Definition source.c:1269
void forget_cached_source_info(void)
Definition source.c:417
scoped_fd open_source_file(struct symtab *s)
Definition source.c:1143
struct objfile * objfile() const
Definition symtab.h:1788
gdb_bfd_ref_ptr obfd
Definition objfiles.h:740
long mtime
Definition objfiles.h:755
bfd * exec_bfd() const
Definition progspace.h:268
enum language language() const
Definition symtab.h:1697
struct compunit_symtab * compunit() const
Definition symtab.h:1677
Definition value.h:130
void gdb_printf(struct ui_file *stream, const char *format,...)
Definition utils.c:1886
int myread(int desc, char *addr, int len)
Definition utils.c:702
#define gdb_stdout
Definition utils.h:182