//===-- DumpValueObjectOptionsTests.cpp -----------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Plugins/Platform/Linux/PlatformLinux.h" #include "Plugins/ScriptInterpreter/None/ScriptInterpreterNone.h" #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" #include "TestingSupport/SubsystemRAII.h" #include "TestingSupport/Symbol/ClangTestUtils.h" #include "lldb/Core/Debugger.h" #include "lldb/DataFormatters/DumpValueObjectOptions.h" #include "lldb/ValueObject/ValueObject.h" #include "lldb/ValueObject/ValueObjectConstResult.h" #include "gtest/gtest.h" #include using namespace lldb; using namespace lldb_private; struct MockProcess : Process { MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp) : Process(target_sp, listener_sp) {} llvm::StringRef GetPluginName() override { return "mock process"; } bool CanDebug(lldb::TargetSP target, bool plugin_specified_by_name) override { return false; }; Status DoDestroy() override { return {}; } void RefreshStateAfterStop() override {} bool DoUpdateThreadList(ThreadList &old_thread_list, ThreadList &new_thread_list) override { return false; }; size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size, Status &error) override { // No need to read memory in these tests. return size; } }; class ValueObjectMockProcessTest : public ::testing::Test { public: void SetUp() override { ArchSpec arch("i386-pc-linux"); Platform::SetHostPlatform( platform_linux::PlatformLinux::CreateInstance(true, &arch)); m_debugger_sp = Debugger::CreateInstance(); ASSERT_TRUE(m_debugger_sp); m_debugger_sp->GetTargetList().CreateTarget(*m_debugger_sp, "", arch, eLoadDependentsNo, m_platform_sp, m_target_sp); ASSERT_TRUE(m_target_sp); ASSERT_TRUE(m_target_sp->GetArchitecture().IsValid()); ASSERT_TRUE(m_platform_sp); m_listener_sp = Listener::MakeListener("dummy"); m_process_sp = std::make_shared(m_target_sp, m_listener_sp); ASSERT_TRUE(m_process_sp); m_exe_ctx = ExecutionContext(m_process_sp); m_holder = std::make_unique("test"); m_type_system = m_holder->GetAST(); } template void TestDumpEnum( const std::vector> enumerators, const std::vector> &tests) { CompilerType enum_type = MakeEnumType(enumerators); StreamString strm; ConstString var_name("test_var"); ByteOrder endian = endian::InlHostByteOrder(); ExecutionContextScope *exe_scope = m_exe_ctx.GetBestExecutionContextScope(); for (auto [value, options, expected] : tests) { DataExtractor data_extractor{&value, sizeof(value), endian, 4}; auto valobj_sp = ValueObjectConstResult::Create(exe_scope, enum_type, var_name, data_extractor); if (llvm::Error error = valobj_sp->Dump(strm, options)) llvm::consumeError(std::move(error)); ASSERT_STREQ(strm.GetString().str().c_str(), expected); strm.Clear(); } } template CompilerType MakeEnumType( const std::vector> enumerators) { CompilerType int_type = m_type_system->GetBuiltinTypeForEncodingAndBitSize( std::is_same::value ? lldb::eEncodingSint : lldb::eEncodingUint, 32); CompilerType enum_type = m_type_system->CreateEnumerationType( "TestEnum", m_type_system->GetTranslationUnitDecl(), OptionalClangModuleID(), Declaration(), int_type, false); m_type_system->StartTagDeclarationDefinition(enum_type); Declaration decl; for (auto [name, value] : enumerators) m_type_system->AddEnumerationValueToEnumerationType(enum_type, decl, name, value, 32); m_type_system->CompleteTagDeclarationDefinition(enum_type); return enum_type; } ExecutionContext m_exe_ctx; TypeSystemClang *m_type_system; private: SubsystemRAII m_subsystems; std::unique_ptr m_holder; lldb::DebuggerSP m_debugger_sp; lldb::TargetSP m_target_sp; lldb::PlatformSP m_platform_sp; lldb::ListenerSP m_listener_sp; lldb::ProcessSP m_process_sp; }; TEST_F(ValueObjectMockProcessTest, EmptyEnum) { // All values of an empty enum should be shown as plain numbers. TestDumpEnum({}, {{0, {}, "(TestEnum) test_var = 0\n"}, {1, {}, "(TestEnum) test_var = 1\n"}, {2, {}, "(TestEnum) test_var = 2\n"}}); TestDumpEnum({}, {{-2, {}, "(TestEnum) test_var = -2\n"}, {-1, {}, "(TestEnum) test_var = -1\n"}, {0, {}, "(TestEnum) test_var = 0\n"}, {1, {}, "(TestEnum) test_var = 1\n"}, {2, {}, "(TestEnum) test_var = 2\n"}}); } TEST_F(ValueObjectMockProcessTest, Enum) { // This is not a bitfield-like enum, so values are printed as decimal by // default. Also we only show the enumerator name if the value is an // exact match. TestDumpEnum( {{"test_2", 2}, {"test_3", 3}}, {{0, {}, "(TestEnum) test_var = 0\n"}, {1, {}, "(TestEnum) test_var = 1\n"}, {2, {}, "(TestEnum) test_var = test_2\n"}, {3, {}, "(TestEnum) test_var = test_3\n"}, {4, {}, "(TestEnum) test_var = 4\n"}, {5, {}, "(TestEnum) test_var = 5\n"}, {1, DumpValueObjectOptions().SetHideRootName(true), "(TestEnum) 1\n"}, {1, DumpValueObjectOptions().SetHideRootType(true), "test_var = 1\n"}, {1, DumpValueObjectOptions().SetHideRootName(true).SetHideRootType(true), "1\n"}, {1, DumpValueObjectOptions().SetHideName(true), "(TestEnum) 1\n"}, {1, DumpValueObjectOptions().SetHideValue(true), "(TestEnum) test_var =\n"}, {1, DumpValueObjectOptions().SetHideName(true).SetHideValue(true), "(TestEnum) \n"}}); } TEST_F(ValueObjectMockProcessTest, BitFieldLikeEnum) { // These enumerators set individual bits in the value, as if it were a flag // set. lldb treats this as a "bitfield like enum". This means we show values // as hex, and values without exact matches are shown as a combination of // enumerators and any remaining value left over. TestDumpEnum( {{"test_2", 2}, {"test_4", 4}}, { {0, {}, "(TestEnum) test_var = 0x0\n"}, {1, {}, "(TestEnum) test_var = 0x1\n"}, {2, {}, "(TestEnum) test_var = test_2\n"}, {4, {}, "(TestEnum) test_var = test_4\n"}, {6, {}, "(TestEnum) test_var = test_2 | test_4\n"}, {7, {}, "(TestEnum) test_var = test_2 | test_4 | 0x1\n"}, {8, {}, "(TestEnum) test_var = 0x8\n"}, {1, DumpValueObjectOptions().SetHideRootName(true), "(TestEnum) 0x1\n"}, {1, DumpValueObjectOptions().SetHideRootType(true), "test_var = 0x1\n"}, {1, DumpValueObjectOptions().SetHideRootName(true).SetHideRootType(true), "0x1\n"}, {1, DumpValueObjectOptions().SetHideName(true), "(TestEnum) 0x1\n"}, {1, DumpValueObjectOptions().SetHideValue(true), "(TestEnum) test_var =\n"}, {1, DumpValueObjectOptions().SetHideName(true).SetHideValue(true), "(TestEnum) \n"}, }); }