]> Git Repo - binutils.git/blob - gdb/source-cache.c
Unify gdb printf functions
[binutils.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 }
This page took 0.051654 seconds and 4 git commands to generate.