Index: lldb/packages/Python/lldbsuite/test/functionalities/dw_op_piece/Makefile =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/functionalities/dw_op_piece/Makefile @@ -0,0 +1,3 @@ +CXX_SOURCES := main.cpp +CXXFLAGS_EXTRAS := -O2 +include Makefile.rules Index: lldb/packages/Python/lldbsuite/test/functionalities/dw_op_piece/TestDW_OP_piece.py =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/functionalities/dw_op_piece/TestDW_OP_piece.py @@ -0,0 +1,5 @@ +from lldbsuite.test import lldbinline +from lldbsuite.test import decorators + +lldbinline.MakeInlineTest(__file__, globals(), + [decorators.skipUnlessArch('x86_64')]) Index: lldb/packages/Python/lldbsuite/test/functionalities/dw_op_piece/main.cpp =================================================================== --- /dev/null +++ lldb/packages/Python/lldbsuite/test/functionalities/dw_op_piece/main.cpp @@ -0,0 +1,62 @@ +// The point of this test is to exercise lldb's DW_OP_piece evaluation logic. +// The CHECK lines in the test depend on aspects of the optimizer's behavior +// that aren't guaranteed. If the test fails after a compiler change, run it in +// trace mode (./bin/lldb-dotest -t ...): this should print out the new location +// expressions. + +__attribute__((noinline, optnone)) int use(int x) { return x; } + +volatile int sink; + +struct S1 { + int f1; + int *f2; +}; + +struct S2 { + char a, b; + int pad; + S2(int x) { + a = x & 0xff; + b = x & 0xff00; + } +}; + +int main() { + S1 v1; + v1.f1 = sink; + v1.f2 = nullptr; + + // 1) Check that "v1" is described by 3 pieces (one each for "f1" and "f2" + // (in registers), and a third for padding (a literal)). + // + //% self.filecheck("image lookup -va $pc", "main.cpp", "-check-prefix=INFO-V1") + // INFO-V1: name = "v1", type = "S1", location = DW_OP_reg0 RAX, DW_OP_piece 0x4, DW_OP_piece 0x4, DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x8, decl + sink++; + + // 2) Check that lldb displays "v1" correctly. + // + //% self.filecheck("frame variable v1", "main.cpp", "-check-prefix=SHOW-V1") + // SHOW-V1: (S1) v1 = { + // SHOW-V1-NEXT: f1 = 0 + // SHOW-V1-NEXT: f2 = 0x{{0+$}} + // SHOW-V1-NEXT: } + + S2 v2(v1.f1); + + // 3) Check that "v2" is described by 2 pieces (one each for "a" and "b"). + // Note that a piece for "pad" is left out: this is crucial, as it means that + // DWARFExpression::Evaluate() will produce a 2-byte Value. We want to test + // that lldb can handle the Value without smashing memory (this is a + // regression test for a bug found by ASan). + sink += use(v2.a); + //% self.filecheck("image lookup -va $pc", "main.cpp", "-check-prefix=INFO-V2") + // INFO-V2: name = "v2", type = "S2", location = DW_OP_piece 0x1, DW_OP_lit0, DW_OP_stack_value, DW_OP_piece 0x1, decl + + // 4) Check that lldb displays "v2" correctly. + sink += use(v2.pad); + //% self.filecheck("frame variable v2", "main.cpp", "-check-prefix=SHOW-V2") + // SHOW-V2: (S2) v2 = (a = '\0', b = '\0', pad = {{.*}}) + + return 0; +} Index: lldb/source/Core/Value.cpp =================================================================== --- lldb/source/Core/Value.cpp +++ lldb/source/Core/Value.cpp @@ -222,6 +222,12 @@ case eContextTypeLLDBType: // Type * case eContextTypeVariable: // Variable * { + // The size of this Value may be less than the size of the type of its + // source variable due to truncating operations such as DW_OP_piece. + if (m_value_type == eValueTypeHostAddress) + if (uint64_t buffer_size = GetBuffer().GetByteSize()) + return buffer_size; + auto *scope = exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr; if (llvm::Optional size = GetCompilerType().GetByteSize(scope)) { if (error_ptr) Index: lldb/unittests/Expression/DWARFExpressionTest.cpp =================================================================== --- lldb/unittests/Expression/DWARFExpressionTest.cpp +++ lldb/unittests/Expression/DWARFExpressionTest.cpp @@ -360,4 +360,27 @@ // Note that the "00" should really be "undef", but we can't // represent that yet. llvm::HasValue(GetScalar(16, 0xff00, true))); + + for (unsigned char ByteSize = 1; ByteSize <= 8; ++ByteSize) { + llvm::Expected empty = Evaluate({DW_OP_piece, ByteSize}); + // Note that the "0" should really be "undef", but we can't + // represent that yet. + EXPECT_THAT_EXPECTED(empty, + llvm::HasValue(GetScalar(ByteSize * 8, 0, true))); + + Value pieces; + ASSERT_EQ(pieces.AppendDataToHostBuffer(empty.get()), ByteSize); + ASSERT_EQ(pieces.GetValueByteSize(nullptr, nullptr), ByteSize); + } + + // When CommandObjectFrameVariable constructs an empty Value, check that + // we do not successfully report its size as 0. A CompilerType is needed to + // get the size in this special case. + { + Value empty; + empty.ResizeData(0); + Status error; + ASSERT_EQ(empty.GetValueByteSize(&error, nullptr), 0); + ASSERT_TRUE(error.Fail()); + } }