Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion lldb/source/ValueObject/DILEval.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,12 @@ Interpreter::Visit(const UnaryOpNode *node) {
rhs = dynamic_rhs;

lldb::ValueObjectSP child_sp = rhs->Dereference(error);
if (!child_sp && m_use_synthetic) {
if (lldb::ValueObjectSP synth_obj_sp = rhs->GetSyntheticValue()) {
error.Clear();
child_sp = synth_obj_sp->Dereference(error);
}
}
if (error.Fail())
return llvm::make_error<DILDiagnosticError>(m_expr, error.AsCString(),
node->GetLocation());
Expand Down Expand Up @@ -280,6 +286,7 @@ Interpreter::Visit(const MemberOfNode *node) {
auto base_or_err = Evaluate(node->GetBase());
if (!base_or_err)
return base_or_err;
bool expr_is_ptr = node->GetIsArrow();
lldb::ValueObjectSP base = *base_or_err;

// Perform some basic type & correctness checking.
Expand Down Expand Up @@ -319,11 +326,11 @@ Interpreter::Visit(const MemberOfNode *node) {
return llvm::make_error<DILDiagnosticError>(
m_expr, errMsg, node->GetLocation(), node->GetFieldName().size());
}
expr_is_ptr = false;
}
}

if (m_check_ptr_vs_member) {
bool expr_is_ptr = node->GetIsArrow();
bool base_is_ptr = base->IsPointerType();

if (expr_is_ptr != base_is_ptr) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""
Make sure 'frame var' using DIL parser/evaluator works for bit fields.
"""

import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
from lldbsuite.test import lldbutil

import os
import shutil
import time


class TestFrameVarDILBitField(TestBase):
# If your test case doesn't stress debug info, then
# set this to true. That way it won't be run once for
# each debug info format.
NO_DEBUG_INFO_TESTCASE = True

def test_frame_var(self):
self.build()
lldbutil.run_to_source_breakpoint(
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)

self.runCmd("settings set target.experimental.use-DIL true")
self.expect_var_path("bf.a", value="1023")
self.expect_var_path("bf.b", value="9")
self.expect_var_path("bf.c", value="false")
self.expect_var_path("bf.d", value="true")

self.expect_var_path("abf.a", value="1023")
self.expect_var_path("abf.b", value="'\\x0f'")
self.expect_var_path("abf.c", value="3")

# Perform an operation to ensure we actually read the value.
# Address-of is not allowed for bit-fields.
self.expect(
"frame variable '&bf.a'",
error=True,
substrs=["'bf.a' doesn't have a valid address"],
)
42 changes: 42 additions & 0 deletions lldb/test/API/commands/frame/var-dil/basics/BitField/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#include <cstdint>

int main(int argc, char **argv) {
enum BitFieldEnum : uint32_t { kZero, kOne };

struct BitFieldStruct {
uint16_t a : 10;
uint32_t b : 4;
bool c : 1;
bool d : 1;
int32_t e : 32;
uint32_t f : 32;
uint32_t g : 31;
uint64_t h : 31;
uint64_t i : 33;
BitFieldEnum j : 10;
};

BitFieldStruct bf;
bf.a = 0b1111111111;
bf.b = 0b1001;
bf.c = 0b0;
bf.d = 0b1;
bf.e = 0b1;
bf.f = 0b1;
bf.g = 0b1;
bf.h = 0b1;
bf.i = 0b1;
bf.j = BitFieldEnum::kOne;

struct AlignedBitFieldStruct {
uint16_t a : 10;
uint8_t b : 4;
unsigned char : 0;
uint16_t c : 2;
};

uint32_t data = ~0;
AlignedBitFieldStruct abf = (AlignedBitFieldStruct &)data;

return 0; // Set a breakpoint here
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""
Make sure 'frame var' using DIL parser/evaluator works for indirection.
"""

import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
from lldbsuite.test import lldbutil

import os
import shutil
import time


class TestFrameVarDILIndirection(TestBase):
# If your test case doesn't stress debug info, then
# set this to true. That way it won't be run once for
# each debug info format.
NO_DEBUG_INFO_TESTCASE = True

def test_frame_var(self):
self.build()
lldbutil.run_to_source_breakpoint(
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)

self.runCmd("settings set target.experimental.use-DIL true")
self.expect_var_path("*p", value="1")
self.expect_var_path("p", type="int *")
self.expect_var_path("*my_p", value="1")
self.expect_var_path("my_p", type="myp")
self.expect_var_path("*my_pr", type="int *")
self.expect_var_path("my_pr", type="mypr")

self.expect(
"frame variable '*1'",
error=True,
substrs=["Unexpected token: <'1' (numeric_constant)>"],
)
self.expect(
"frame variable '*val'",
error=True,
substrs=[
"dereference failed: not a pointer, reference or array type: (int) val"
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
int main(int argc, char **argv) {
int val = 1;
int *p = &val;

typedef int *myp;
myp my_p = &val;

typedef int *&mypr;
mypr my_pr = p;

return 0; // Set a breakpoint here
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""
Test DIL pointer dereferencing.
"""

import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
from lldbsuite.test import lldbutil

import os
import shutil
import time


class TestFrameVarDILPointerDereference(TestBase):
NO_DEBUG_INFO_TESTCASE = True

def test_frame_var(self):
self.build()
lldbutil.run_to_source_breakpoint(
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)

self.runCmd("settings set target.experimental.use-DIL true")
self.expect_var_path("*p_int0", value="0")
self.expect_var_path("*cp_int5", value="5")
self.expect_var_path("&pp_void0[2]", type="void **")
self.expect_var_path("**pp_int0", value="0")
self.expect_var_path("&**pp_int0", type="int *")
Comment on lines +25 to +29
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the goal here is to explicitly check handling pointer arithmetic, you may want to add additional checks for the values you get this way. expect_var_path returns the SBValue it has located, so you could do something like:

pp_void0_2_got = self.expect_var_path("&pp_void0[2]", type="void **")
pp_void0_2_exp = self.expect_var_path("pp_void0_2", type="void **") # Initialized in C++ code to point to the same value
self.assertEqual(pp_void0_2_got.GetValueAsAddress(), pp_void0_2_exp.GetValueAsAddress())

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion! Done.

self.expect(
"frame variable '&p_void[0]'",
error=True,
substrs=["subscript of pointer to incomplete type 'void'"],
)

# Verify some of the returned values.
pp_void0_2_got = self.expect_var_path("&pp_void0[2]", type="void **")
# Initialized in C++ code to point to the same value
pp_void0_2_exp = self.expect_var_path("pp_void0_2", type="void **")
self.assertEqual(
pp_void0_2_got.GetValueAsAddress(), pp_void0_2_exp.GetValueAsAddress()
)
pp_int0_2stars_got = self.expect_var_path("&**pp_int0", type="int *")
pp_int0_2stars_exp = self.expect_var_path("pp_int0_2stars", type="int *")
self.assertEqual(
pp_int0_2stars_got.GetValueAsAddress(),
pp_int0_2stars_exp.GetValueAsAddress(),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include <cstddef>

int main(int argc, char **argv) {
int *p_null = nullptr;
const char *p_char1 = "hello";

typedef const char *my_char_ptr;
my_char_ptr my_p_char1 = p_char1;

int offset = 5;
int array[10];
array[0] = 0;
array[offset] = offset;

int (&array_ref)[10] = array;

int *p_int0 = &array[0];
int **pp_int0 = &p_int0;
const int *cp_int0 = &array[0];
const int *cp_int5 = &array[offset];

typedef int *td_int_ptr_t;
td_int_ptr_t td_int_ptr0 = &array[0];

void *p_void = (void *)p_char1;
void **pp_void0 = &p_void;
void **pp_void1 = pp_void0 + 1;

void **pp_void0_2 = &pp_void0[2];
int *pp_int0_2stars = &**pp_int0;
std::nullptr_t std_nullptr_t = nullptr;

return 0; // Set a breakpoint here
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""
Make sure 'frame var' using DIL parser/evaluator works for namespaces.
"""

import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
from lldbsuite.test import lldbutil

import os
import shutil
import time


class TestFrameVarDILQualifiedId(TestBase):
# If your test case doesn't stress debug info, then
# set this to true. That way it won't be run once for
# each debug info format.
NO_DEBUG_INFO_TESTCASE = True

def test_frame_var(self):
self.build()
lldbutil.run_to_source_breakpoint(
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)

self.runCmd("settings set target.experimental.use-DIL true")
self.expect_var_path("::ns::i", value="1")
self.expect_var_path("ns::i", value="1")
self.expect_var_path("::ns::ns::i", value="2")
self.expect_var_path("ns::ns::i", value="2")
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace ns {

int i = 1;

namespace ns {

int i = 2;

} // namespace ns

} // namespace ns

int main(int argc, char **argv) {

return 0; // Set a breakpoint here
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""
Test code for dereferencing synthetic wrapped pointers.
"""

import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil


class DILSyntheticDereferenceTestCase(TestBase):
# If your test case doesn't stress debug info, then
# set this to true. That way it won't be run once for
# each debug info format.
NO_DEBUG_INFO_TESTCASE = True

def test_frame_var(self):
self.build()
lldbutil.run_to_source_breakpoint(
self, "Set a breakpoint here", lldb.SBFileSpec("main.cpp")
)

self.runCmd("settings set target.experimental.use-DIL true")
self.runCmd("script from wrapPtrSynthProvider import *")
self.runCmd("type synth add -l wrapPtrSynthProvider wrap_ptr")

self.expect_var_path("ptr_node->value", value="1")
self.expect_var_path("ptr_node->next->value", value="2")
self.expect_var_path("(*ptr_node).value", value="1")
self.expect_var_path("(*(*ptr_node).next).value", value="2")

self.expect_var_path("ptr_node.ptr", type="NodeS *")
self.expect_var_path("ptr_node.ptr->value", value="1")
self.expect_var_path("ptr_node.ptr->next.ptr->value", value="2")
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include <memory>

struct NodeS;

// Class to wrap pointers.
class wrap_ptr {
public:
NodeS *ptr;

wrap_ptr(NodeS *n_ptr) : ptr(n_ptr) {}
};

struct NodeS {
wrap_ptr next;
int value;

NodeS(NodeS *n_ptr, int val) : next(wrap_ptr(n_ptr)), value(val) {}
};

int main(int argc, char **argv) {

// Make a short linked list of fake smart pointers.
auto ptr_node = wrap_ptr(new NodeS(new NodeS(nullptr, 2), 1));

return 0; // Set a breakpoint here
}
Loading
Loading