X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fsource-cache.c;h=9196e3a19e3f60fcdab1225bd306e6a594f7bf7e;hb=refs%2Fheads%2Fconcurrent-displaced-stepping-2020-04-01;hp=94d82073bccb06a327952794c01540890d6c3355;hpb=268a13a5a3f7c6b9b6ffc5ac2d1b24eb41f3fbdc;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/source-cache.c b/gdb/source-cache.c index 94d82073bc..9196e3a19e 100644 --- a/gdb/source-cache.c +++ b/gdb/source-cache.c @@ -1,5 +1,5 @@ /* Cache of styled source file text - Copyright (C) 2018-2019 Free Software Foundation, Inc. + Copyright (C) 2018-2020 Free Software Foundation, Inc. This file is part of GDB. @@ -21,6 +21,10 @@ #include "gdbsupport/scoped_fd.h" #include "source.h" #include "cli/cli-style.h" +#include "symtab.h" +#include "gdbsupport/selftest.h" +#include "objfiles.h" +#include "exec.h" #ifdef HAVE_SOURCE_HIGHLIGHT /* If Gnulib redirects 'open' and 'close' to its replacements @@ -29,7 +33,6 @@ when GDB is linked. Happens, e.g., in the MinGW build. */ #undef open #undef close -#include #include #include #include @@ -45,71 +48,50 @@ source_cache g_source_cache; /* See source-cache.h. */ -bool -source_cache::get_plain_source_lines (struct symtab *s, int first_line, - int last_line, std::string *lines) +std::string +source_cache::get_plain_source_lines (struct symtab *s, + const std::string &fullname) { - scoped_fd desc (open_source_file_with_line_charpos (s)); + scoped_fd desc (open_source_file (s)); if (desc.get () < 0) - return false; - - if (first_line < 1 || first_line > s->nlines || last_line < 1) - return false; - - if (lseek (desc.get (), s->line_charpos[first_line - 1], SEEK_SET) < 0) perror_with_name (symtab_to_filename_for_display (s)); - int last_charpos; - if (last_line >= s->nlines) - { - struct stat st; - - if (fstat (desc.get (), &st) < 0) - perror_with_name (symtab_to_filename_for_display (s)); - /* We could cache this in line_charpos... */ - last_charpos = st.st_size; - } - else - last_charpos = s->line_charpos[last_line]; + struct stat st; + if (fstat (desc.get (), &st) < 0) + perror_with_name (symtab_to_filename_for_display (s)); - lines->resize (last_charpos - s->line_charpos[first_line - 1]); - if (myread (desc.get (), &(*lines)[0], lines->size ()) < 0) + std::string lines; + lines.resize (st.st_size); + if (myread (desc.get (), &lines[0], lines.size ()) < 0) perror_with_name (symtab_to_filename_for_display (s)); - return true; -} + time_t mtime = 0; + if (SYMTAB_OBJFILE (s) != NULL && SYMTAB_OBJFILE (s)->obfd != NULL) + mtime = SYMTAB_OBJFILE (s)->mtime; + else if (exec_bfd) + mtime = exec_bfd_mtime; -/* See source-cache.h. */ + if (mtime && mtime < st.st_mtime) + warning (_("Source file is more recent than executable.")); -std::string -source_cache::extract_lines (const struct source_text &text, int first_line, - int last_line) -{ - int lineno = 1; - std::string::size_type pos = 0; - std::string::size_type first_pos = std::string::npos; - - while (pos != std::string::npos && lineno <= last_line) + std::vector offsets; + offsets.push_back (0); + for (size_t offset = lines.find ('\n'); + offset != std::string::npos; + offset = lines.find ('\n', offset)) { - std::string::size_type new_pos = text.contents.find ('\n', pos); - - if (lineno == first_line) - first_pos = pos; - - pos = new_pos; - if (lineno == last_line || pos == std::string::npos) - { - if (first_pos == std::string::npos) - return {}; - if (pos == std::string::npos) - pos = text.contents.size (); - return text.contents.substr (first_pos, pos - first_pos); - } - ++lineno; - ++pos; + ++offset; + /* A newline at the end does not start a new line. It would + seem simpler to just strip the newline in this function, but + then "list" won't print the final newline. */ + if (offset != lines.size ()) + offsets.push_back (offset); } - return {}; + offsets.shrink_to_fit (); + m_offset_cache.emplace (fullname, std::move (offsets)); + + return lines; } #ifdef HAVE_SOURCE_HIGHLIGHT @@ -153,8 +135,7 @@ get_language_name (enum language lang) break; case language_rust: - /* Not handled by Source Highlight. */ - break; + return "rust.lang"; case language_ada: return "ada.lang"; @@ -171,74 +152,201 @@ get_language_name (enum language lang) /* See source-cache.h. */ bool -source_cache::get_source_lines (struct symtab *s, int first_line, - int last_line, std::string *lines) +source_cache::ensure (struct symtab *s) { - if (first_line < 1 || last_line < 1 || first_line > last_line) - return false; + std::string fullname = symtab_to_fullname (s); -#ifdef HAVE_SOURCE_HIGHLIGHT - if (source_styling && gdb_stdout->can_emit_style_escape ()) + size_t size = m_source_map.size (); + for (int i = 0; i < size; ++i) { - const char *fullname = symtab_to_fullname (s); - - for (const auto &item : m_source_map) + if (m_source_map[i].fullname == fullname) { - if (item.fullname == fullname) - { - *lines = extract_lines (item, first_line, last_line); - return true; - } + /* This should always hold, because we create the file + offsets when reading the file, and never free them + without also clearing the contents cache. */ + gdb_assert (m_offset_cache.find (fullname) + != m_offset_cache.end ()); + /* Not strictly LRU, but at least ensure that the most + recently used entry is always the last candidate for + deletion. Note that this property is relied upon by at + least one caller. */ + if (i != size - 1) + std::swap (m_source_map[i], m_source_map[size - 1]); + return true; } + } + + std::string contents; + try + { + contents = get_plain_source_lines (s, fullname); + } + catch (const gdb_exception_error &e) + { + /* If 's' is not found, an exception is thrown. */ + return false; + } + if (source_styling && gdb_stdout->can_emit_style_escape ()) + { +#ifdef HAVE_SOURCE_HIGHLIGHT + bool already_styled = false; const char *lang_name = get_language_name (SYMTAB_LANGUAGE (s)); if (lang_name != nullptr) { - std::ifstream input (fullname); - if (input.is_open ()) - { - /* The global source highlight object, or null if one - was never constructed. This is stored here rather - than in the class so that we don't need to include - anything or do conditional compilation in - source-cache.h. */ - static srchilite::SourceHighlight *highlighter; - - if (s->line_charpos == 0) - { - scoped_fd desc (open_source_file_with_line_charpos (s)); - if (desc.get () < 0) - return false; - - /* FULLNAME points to a value owned by the symtab - (symtab::fullname). Calling open_source_file reallocates - that value, so we must refresh FULLNAME to avoid a - use-after-free. */ - fullname = symtab_to_fullname (s); - } + /* The global source highlight object, or null if one was + never constructed. This is stored here rather than in + the class so that we don't need to include anything or do + conditional compilation in source-cache.h. */ + static srchilite::SourceHighlight *highlighter; + try + { if (highlighter == nullptr) { highlighter = new srchilite::SourceHighlight ("esc.outlang"); highlighter->setStyleFile ("esc.style"); } + std::istringstream input (contents); std::ostringstream output; highlighter->highlight (input, output, lang_name, fullname); + contents = output.str (); + already_styled = true; + } + catch (...) + { + /* Source Highlight will throw an exception if + highlighting fails. One possible reason it can fail + is if the language is unknown -- which matters to gdb + because Rust support wasn't added until after 3.1.8. + Ignore exceptions here and fall back to + un-highlighted text. */ + } + } + + if (!already_styled) +#endif /* HAVE_SOURCE_HIGHLIGHT */ + { + gdb::optional ext_contents; + ext_contents = ext_lang_colorize (fullname, contents); + if (ext_contents.has_value ()) + contents = std::move (*ext_contents); + } + } - source_text result = { fullname, output.str () }; - m_source_map.push_back (std::move (result)); + source_text result = { std::move (fullname), std::move (contents) }; + m_source_map.push_back (std::move (result)); - if (m_source_map.size () > MAX_ENTRIES) - m_source_map.erase (m_source_map.begin ()); + if (m_source_map.size () > MAX_ENTRIES) + m_source_map.erase (m_source_map.begin ()); - *lines = extract_lines (m_source_map.back (), first_line, - last_line); - return true; - } + return true; +} + +/* See source-cache.h. */ + +bool +source_cache::get_line_charpos (struct symtab *s, + const std::vector **offsets) +{ + std::string fullname = symtab_to_fullname (s); + + auto iter = m_offset_cache.find (fullname); + if (iter == m_offset_cache.end ()) + { + if (!ensure (s)) + return false; + iter = m_offset_cache.find (fullname); + /* cache_source_text ensured this was entered. */ + gdb_assert (iter != m_offset_cache.end ()); + } + + *offsets = &iter->second; + return true; +} + +/* A helper function that extracts the desired source lines from TEXT, + putting them into LINES_OUT. The arguments are as for + get_source_lines. Returns true on success, false if the line + numbers are invalid. */ + +static bool +extract_lines (const std::string &text, int first_line, int last_line, + std::string *lines_out) +{ + int lineno = 1; + std::string::size_type pos = 0; + std::string::size_type first_pos = std::string::npos; + + while (pos != std::string::npos && lineno <= last_line) + { + std::string::size_type new_pos = text.find ('\n', pos); + + if (lineno == first_line) + first_pos = pos; + + pos = new_pos; + if (lineno == last_line || pos == std::string::npos) + { + /* A newline at the end does not start a new line. */ + if (first_pos == std::string::npos + || first_pos == text.size ()) + return false; + if (pos == std::string::npos) + pos = text.size (); + else + ++pos; + *lines_out = text.substr (first_pos, pos - first_pos); + return true; } + ++lineno; + ++pos; } -#endif /* HAVE_SOURCE_HIGHLIGHT */ - return get_plain_source_lines (s, first_line, last_line, lines); + return false; +} + +/* See source-cache.h. */ + +bool +source_cache::get_source_lines (struct symtab *s, int first_line, + int last_line, std::string *lines) +{ + if (first_line < 1 || last_line < 1 || first_line > last_line) + return false; + + if (!ensure (s)) + return false; + + return extract_lines (m_source_map.back ().contents, + first_line, last_line, lines); +} + +#if GDB_SELF_TEST +namespace selftests +{ +static void extract_lines_test () +{ + std::string input_text = "abc\ndef\nghi\njkl\n"; + std::string result; + + SELF_CHECK (extract_lines (input_text, 1, 1, &result) + && result == "abc\n"); + SELF_CHECK (!extract_lines (input_text, 2, 1, &result)); + SELF_CHECK (extract_lines (input_text, 1, 2, &result) + && result == "abc\ndef\n"); + SELF_CHECK (extract_lines ("abc", 1, 1, &result) + && result == "abc"); +} +} +#endif + +void _initialize_source_cache (); +void +_initialize_source_cache () +{ +#if GDB_SELF_TEST + selftests::register_test ("source-cache", selftests::extract_lines_test); +#endif }