diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -76,8 +76,6 @@ class Debugger : public std::enable_shared_from_this, public UserID, public Properties { - friend class SourceManager; // For GetSourceFileCache. - public: /// Broadcaster event bits definitions. enum { @@ -515,6 +513,10 @@ void FlushProcessOutput(Process &process, bool flush_stdout, bool flush_stderr); + SourceManager::SourceFileCache &GetSourceFileCache() { + return m_source_file_cache; + } + protected: friend class CommandInterpreter; friend class REPL; @@ -599,10 +601,6 @@ // Ensures two threads don't attempt to flush process output in parallel. std::mutex m_output_flush_mutex; - SourceManager::SourceFileCache &GetSourceFileCache() { - return m_source_file_cache; - } - void InstanceInitialize(); // these should never be NULL diff --git a/lldb/include/lldb/Core/SourceManager.h b/lldb/include/lldb/Core/SourceManager.h --- a/lldb/include/lldb/Core/SourceManager.h +++ b/lldb/include/lldb/Core/SourceManager.h @@ -65,6 +65,8 @@ uint32_t GetNumLines(); + llvm::sys::TimePoint<> GetTimestamp() const { return m_mod_time; } + protected: bool CalculateLineOffsets(uint32_t line = UINT32_MAX); @@ -105,6 +107,8 @@ // Removes all elements from the cache. void Clear() { m_file_cache.clear(); } + void Dump(Stream &stream) const; + protected: typedef std::map FileCache; FileCache m_file_cache; diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -1201,6 +1201,62 @@ std::string m_reverse_name; }; +class CommandObjectSourceCacheDump : public CommandObjectParsed { +public: + CommandObjectSourceCacheDump(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "source cache dump", + "Dump the state of the source code cache. Intended " + "to be used for debugging LLDB itself.", + nullptr) {} + + ~CommandObjectSourceCacheDump() override = default; + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + SourceManager::SourceFileCache &cache = GetDebugger().GetSourceFileCache(); + cache.Dump(result.GetOutputStream()); + result.SetStatus(eReturnStatusSuccessFinishResult); + return result.Succeeded(); + } +}; + +class CommandObjectSourceCacheClear : public CommandObjectParsed { +public: + CommandObjectSourceCacheClear(CommandInterpreter &interpreter) + : CommandObjectParsed(interpreter, "source cache clear", + "Clear the source code cache.\n", nullptr) {} + + ~CommandObjectSourceCacheClear() override = default; + +protected: + bool DoExecute(Args &command, CommandReturnObject &result) override { + SourceManager::SourceFileCache &cache = GetDebugger().GetSourceFileCache(); + cache.Clear(); + result.SetStatus(eReturnStatusSuccessFinishNoResult); + return result.Succeeded(); + } +}; + +class CommandObjectSourceCache : public CommandObjectMultiword { +public: + CommandObjectSourceCache(CommandInterpreter &interpreter) + : CommandObjectMultiword(interpreter, "source cache", + "Commands for managing the source code cache.", + "source cache ") { + LoadSubCommand( + "dump", CommandObjectSP(new CommandObjectSourceCacheDump(interpreter))); + LoadSubCommand("clear", CommandObjectSP(new CommandObjectSourceCacheClear( + interpreter))); + } + + ~CommandObjectSourceCache() override = default; + +private: + CommandObjectSourceCache(const CommandObjectSourceCache &) = delete; + const CommandObjectSourceCache & + operator=(const CommandObjectSourceCache &) = delete; +}; + #pragma mark CommandObjectMultiwordSource // CommandObjectMultiwordSource @@ -1216,6 +1272,8 @@ CommandObjectSP(new CommandObjectSourceInfo(interpreter))); LoadSubCommand("list", CommandObjectSP(new CommandObjectSourceList(interpreter))); + LoadSubCommand("cache", + CommandObjectSP(new CommandObjectSourceCache(interpreter))); } CommandObjectMultiwordSource::~CommandObjectMultiwordSource() = default; diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -5105,8 +5105,8 @@ CommandObjectTargetDumpTypesystem(CommandInterpreter &interpreter) : CommandObjectParsed( interpreter, "target dump typesystem", - "Dump the state of the target's internal type system.\n" - "Intended to be used for debugging LLDB itself.", + "Dump the state of the target's internal type system. Intended to " + "be used for debugging LLDB itself.", nullptr, eCommandRequiresTarget) {} ~CommandObjectTargetDumpTypesystem() override = default; diff --git a/lldb/source/Core/SourceManager.cpp b/lldb/source/Core/SourceManager.cpp --- a/lldb/source/Core/SourceManager.cpp +++ b/lldb/source/Core/SourceManager.cpp @@ -727,3 +727,15 @@ file_sp = pos->second; return file_sp; } + +void SourceManager::SourceFileCache::Dump(Stream &stream) const { + stream << "Modification time Lines Path\n"; + stream << "------------------- -------- --------------------------------\n"; + for (auto &entry : m_file_cache) { + if (!entry.second) + continue; + FileSP file = entry.second; + stream.Format("{0:%Y-%m-%d %H:%M:%S} {1,8:d} {2}\n", file->GetTimestamp(), + file->GetNumLines(), entry.first.GetPath()); + } +} diff --git a/lldb/test/API/source-manager/TestSourceManager.py b/lldb/test/API/source-manager/TestSourceManager.py --- a/lldb/test/API/source-manager/TestSourceManager.py +++ b/lldb/test/API/source-manager/TestSourceManager.py @@ -317,3 +317,24 @@ "that has no source code associated " "with it.", ], ) + + def test_source_cache_dump_and_clear(self): + self.build() + exe = self.getBuildArtifact("a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + lldbutil.run_break_set_by_file_and_line( + self, self.file, self.line, num_expected_locations=1, loc_exact=True + ) + self.runCmd("run", RUN_SUCCEEDED) + + # Make sure the main source file is in the source cache. + self.expect( + "source cache dump", + substrs=["Modification time", "Lines", "Path", " 7", self.file], + ) + + # Clear the cache. + self.expect("source cache clear") + + # Make sure the main source file is no longer in the source cache. + self.expect("source cache dump", matching=False, substrs=[self.file])