Index: lldb/bindings/interface/SBDebugger.i =================================================================== --- lldb/bindings/interface/SBDebugger.i +++ lldb/bindings/interface/SBDebugger.i @@ -389,6 +389,17 @@ bool EnableLog (const char *channel, const char ** types); + %feature("autodoc", "Returns a list of all registered log channels. + Returns an empty list if the SBDebugger object is invalid.") GetLogChannels; + lldb::SBStringList + GetLogChannels (); + + %feature("autodoc", "Returns the log categories of the given log channel. + Returns an empty list if the log channel doesn't exist or the SBDebugger + object is invalid.") GetLogCat; + lldb::SBStringList + GetLogCategories (const char *channel); + void SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton); Index: lldb/include/lldb/API/SBDebugger.h =================================================================== --- lldb/include/lldb/API/SBDebugger.h +++ lldb/include/lldb/API/SBDebugger.h @@ -259,6 +259,23 @@ bool EnableLog(const char *channel, const char **categories); + /// Returns a list of all registered log channels. + /// + /// Returns an empty list if SBDebugger object is invalid. + /// + /// \see SBDebugger::EnableLog + /// \see SBDebugger::GetLogCategories + SBStringList GetLogChannels(); + + /// Returns the log categories of the given log channel. + /// + /// Returns an empty list if the log channel doesn't exist or the SBDebugger + /// object is invalid. + /// + /// \see SBDebugger::EnableLog + /// \see SBDebugger::GetLogChannels + SBStringList GetLogCategories(const char *channel); + void SetLoggingCallback(lldb::LogOutputCallback log_callback, void *baton); // DEPRECATED Index: lldb/include/lldb/Core/Debugger.h =================================================================== --- lldb/include/lldb/Core/Debugger.h +++ lldb/include/lldb/Core/Debugger.h @@ -268,6 +268,12 @@ llvm::StringRef log_file, uint32_t log_options, llvm::raw_ostream &error_stream); + /// \copydoc lldb::SBDebugger::GetLogChannels + StringList GetLogChannels(); + + /// \copydoc lldb::SBDebugger::GetLogCategories + StringList GetLogCategories(llvm::StringRef channel); + void SetLoggingCallback(lldb::LogOutputCallback log_callback, void *baton); // Properties Functions Index: lldb/include/lldb/Utility/StringList.h =================================================================== --- lldb/include/lldb/Utility/StringList.h +++ lldb/include/lldb/Utility/StringList.h @@ -9,6 +9,7 @@ #ifndef LLDB_UTILITY_STRINGLIST_H #define LLDB_UTILITY_STRINGLIST_H +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include @@ -32,6 +33,9 @@ StringList(const char **strv, int strc); + /// Creates a StringList with the contents of the given array of strings. + explicit StringList(llvm::ArrayRef strs); + virtual ~StringList(); void AppendString(const std::string &s); Index: lldb/source/API/SBDebugger.cpp =================================================================== --- lldb/source/API/SBDebugger.cpp +++ lldb/source/API/SBDebugger.cpp @@ -1687,6 +1687,25 @@ } } +lldb::SBStringList lldb::SBDebugger::GetLogChannels() { + LLDB_RECORD_METHOD_NO_ARGS(lldb::SBStringList, SBDebugger, GetLogChannels); + + if (!m_opaque_sp) + return LLDB_RECORD_RESULT(SBStringList()); + return LLDB_RECORD_RESULT( + SBStringList(new StringList(m_opaque_sp->GetLogChannels()))); +} + +SBStringList SBDebugger::GetLogCategories(const char *channel) { + LLDB_RECORD_METHOD(lldb::SBStringList, SBDebugger, GetLogCategories, + (const char *), channel); + + if (!m_opaque_sp) + return LLDB_RECORD_RESULT(SBStringList()); + return LLDB_RECORD_RESULT( + SBStringList(new StringList(m_opaque_sp->GetLogCategories(channel)))); +} + namespace lldb_private { namespace repro { Index: lldb/source/Core/Debugger.cpp =================================================================== --- lldb/source/Core/Debugger.cpp +++ lldb/source/Core/Debugger.cpp @@ -1262,6 +1262,19 @@ error_stream); } +StringList Debugger::GetLogChannels() { + return StringList(Log::ListChannels()); +} + +StringList Debugger::GetLogCategories(llvm::StringRef channel) { + StringList result; + Log::ForEachChannelCategory( + channel, [&result](llvm::StringRef name, llvm::StringRef description) { + result.AppendString(name); + }); + return result; +} + ScriptInterpreter * Debugger::GetScriptInterpreter(bool can_create, llvm::Optional language) { Index: lldb/source/Utility/StringList.cpp =================================================================== --- lldb/source/Utility/StringList.cpp +++ lldb/source/Utility/StringList.cpp @@ -33,6 +33,11 @@ } } +StringList::StringList(llvm::ArrayRef strs) { + for (llvm::StringRef s : strs) + m_strings.push_back(s.str()); +} + StringList::~StringList() {} void StringList::AppendString(const char *str) { Index: lldb/test/API/functionalities/logging/TestLoggingAPI.py =================================================================== --- /dev/null +++ lldb/test/API/functionalities/logging/TestLoggingAPI.py @@ -0,0 +1,62 @@ +""" +Tests the API for querying and manipulating LLDB's logging functionality. +""" + + + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class TestCase(TestBase): + + mydir = TestBase.compute_mydir(__file__) + + @no_debug_info_test + def test_list_log_channels(self): + # Invalid SBDebugger returns always an empty list. + channels = list(lldb.SBDebugger().GetLogChannels()) + self.assertEqual([], channels) + + # Otherwise check for our known log channels in LLDB. + channels = list(self.dbg.GetLogChannels()) + self.assertEqual(sort(["dwarf", "lldb", "gdb-remote", "kdp-remote"]), + sort(channels)) + + @no_debug_info_test + def test_list_log_catgories(self): + valid_channel = "lldb" + + # An empty or invalid channel should also return an empty list. + categories = list(self.dbg.GetLogCategories("")) + self.assertEqual([], channels) + categories = list(self.dbg.GetLogCategories("gibberish")) + self.assertEqual([], channels) + + + # With an invalid SBDebugger invalid/empty channel also returns an + # an empty list. + categories = list(lldb.SBDebugger().GetLogCategories("")) + self.assertEqual([], channels) + categories = list(lldb.SBDebugger().GetLogCategories("gibberish")) + self.assertEqual([], channels) + # Invalid SBDebugger returns always an empty list even with a valid + # channel. The log channels are globals but that might change + # in the future, so let's require a valid SBDebugger. + categories = list(lldb.SBDebugger().GetLogCategories(valid_channel)) + self.assertEqual([], channels) + + # Take the valid channel with a valid SBDebugger and make sure LLDB + # returns the right categories. + categories = list(self.dbg.GetLogChannels(channel)) + # 'expression' and 'types' are 'lldb' categories. + self.assertIn("expression", categories) + self.assertIn("types", categories) + + # Test for a category from the 'dwarf' channel which shouldn't be here. + self.assertNotIn("lookups", categories) + # Some sanity checks for things that should never be in the list. + self.assertNotIn("", categories) + self.assertNotIn(valid_channel, categories) Index: lldb/unittests/Utility/StringListTest.cpp =================================================================== --- lldb/unittests/Utility/StringListTest.cpp +++ lldb/unittests/Utility/StringListTest.cpp @@ -18,6 +18,16 @@ EXPECT_EQ(0U, s.GetSize()); } +TEST(StringListTest, ArrayRefConstructor) { + StringList s(llvm::ArrayRef({"a", "bc"})); + EXPECT_EQ(2U, s.GetSize()); + EXPECT_STREQ("a", s.GetStringAtIndex(0)); + EXPECT_STREQ("bc", s.GetStringAtIndex(1)); + + StringList empty(llvm::ArrayRef({})); + EXPECT_EQ(0U, empty.GetSize()); +} + TEST(StringListTest, Assignment) { StringList orig; orig.AppendString("foo");