Skip to content

Commit

Permalink
Fix a crasher in StackFrame::GetValueForVariableExpressionPath()
Browse files Browse the repository at this point in the history
There was a crash that would happen if an IDE would ask for a child of a shared pointer via any SB API call that ends up calling StackFrame::GetValueForVariableExpressionPath(). The previous code expects an error to be set describing why the synthetic child of a type was not able to be found, but we have some synthetic child providers that weren't setting the error and returning an empty value object shared pointer. This fixes that to ensure we don't lose our debug session by crashing, fully tests GetValueForVariableExpressionPath functionality, and ensures we don't crash on GetValueForVariableExpressionPath() in the future.

Differential Revision: https://reviews.llvm.org/D59200

llvm-svn: 355850
  • Loading branch information
Greg Clayton committed Mar 11, 2019
1 parent 06ae025 commit 0d6f681
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 1 deletion.
@@ -0,0 +1,5 @@
LEVEL = ../../make

CXX_SOURCES := main.cpp

include $(LEVEL)/Makefile.rules
@@ -0,0 +1,103 @@
"""
Make sure the getting a variable path works and doesn't crash.
"""

from __future__ import print_function


import os
import time
import re
import lldb
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.lldbtest import *


class TestVarPath(TestBase):

mydir = TestBase.compute_mydir(__file__)

# If your test case doesn't stress debug info, the
# 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()
self.do_test()

def setUp(self):
# Call super's setUp().
TestBase.setUp(self)

def verify_point(self, frame, var_name, var_typename, x_value, y_value):
v = frame.GetValueForVariablePath(var_name)
self.assertTrue(v.GetError().Success(), "Make sure we find '%s'" % (var_name))
self.assertTrue(v.GetType().GetName() == var_typename,
"Make sure '%s' has type '%s'" % (var_name, var_typename))

if '*' in var_typename:
valid_prefix = var_name + '->'
invalid_prefix = var_name + '.'
else:
valid_prefix = var_name + '.'
invalid_prefix = var_name + '->'

valid_x_path = valid_prefix + 'x'
valid_y_path = valid_prefix + 'y'
invalid_x_path = invalid_prefix + 'x'
invalid_y_path = invalid_prefix + 'y'
invalid_m_path = invalid_prefix + 'm'

v = frame.GetValueForVariablePath(valid_x_path)
self.assertTrue(v.GetError().Success(), "Make sure we find '%s'" % (valid_x_path))
self.assertTrue(v.GetValue() == str(x_value), "Make sure '%s' has a value of %i" % (valid_x_path, x_value))
self.assertTrue(v.GetType().GetName() == "int", "Make sure '%s' has type 'int'" % (valid_x_path))
v = frame.GetValueForVariablePath(invalid_x_path)
self.assertTrue(v.GetError().Fail(), "Make sure we don't find '%s'" % (invalid_x_path))

v = frame.GetValueForVariablePath(valid_y_path)
self.assertTrue(v.GetError().Success(), "Make sure we find '%s'" % (valid_y_path))
self.assertTrue(v.GetValue() == str(y_value), "Make sure '%s' has a value of %i" % (valid_y_path, y_value))
self.assertTrue(v.GetType().GetName() == "int", "Make sure '%s' has type 'int'" % (valid_y_path))
v = frame.GetValueForVariablePath(invalid_y_path)
self.assertTrue(v.GetError().Fail(), "Make sure we don't find '%s'" % (invalid_y_path))

v = frame.GetValueForVariablePath(invalid_m_path)
self.assertTrue(v.GetError().Fail(), "Make sure we don't find '%s'" % (invalid_m_path))

def do_test(self):
(target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
self, "// Set a breakpoint here", lldb.SBFileSpec("main.cpp"))

frame = thread.GetFrameAtIndex(0)
v = frame.GetValueForVariablePath('no_such_variable')
self.assertTrue(v.GetError().Fail(), "Make sure we don't find 'no_such_variable'")

# Test an instance
self.verify_point(frame, 'pt', 'Point', 1, 2)
# Test a pointer
self.verify_point(frame, 'pt_ptr', 'Point *', 3030, 4040)
# Test using a pointer as an array
self.verify_point(frame, 'pt_ptr[-1]', 'Point', 1010, 2020)
self.verify_point(frame, 'pt_ptr[0]', 'Point', 3030, 4040)
self.verify_point(frame, 'pt_ptr[1]', 'Point', 5050, 6060)
# Test arrays
v = frame.GetValueForVariablePath('points')
self.assertTrue(v.GetError().Success(),
"Make sure we find 'points'")
self.verify_point(frame, 'points[0]', 'Point', 1010, 2020)
self.verify_point(frame, 'points[1]', 'Point', 3030, 4040)
self.verify_point(frame, 'points[2]', 'Point', 5050, 6060)
# Test a reference
self.verify_point(frame, 'pt_ref', 'Point &', 1, 2)
v = frame.GetValueForVariablePath('pt_sp')
self.assertTrue(v.GetError().Success(), "Make sure we find 'pt_sp'")
# Make sure we don't crash when looking for non existant child
# in type with synthetic children. This used to cause a crash.
v = frame.GetValueForVariablePath('pt_sp->not_valid_child')
self.assertTrue(v.GetError().Fail(),
"Make sure we don't find 'pt_sp->not_valid_child'")



@@ -0,0 +1,15 @@
#include <memory>

struct Point {
int x, y;
};

int main() {
Point pt = { 1, 2 };
Point points[] = {{1010,2020}, {3030,4040}, {5050,6060}};
Point *pt_ptr = &points[1];
Point &pt_ref = pt;
std::shared_ptr<Point> pt_sp(new Point{111,222});
return 0; // Set a breakpoint here
}

7 changes: 6 additions & 1 deletion lldb/source/Target/StackFrame.cpp
Expand Up @@ -642,7 +642,12 @@ ValueObjectSP StackFrame::GetValueForVariableExpressionPath(
valobj_sp = valobj_sp->Dereference(deref_error);
if (error.Fail()) {
error.SetErrorStringWithFormatv(
"Failed to dereference sythetic value: %s", deref_error);
"Failed to dereference sythetic value: {0}", deref_error);
return ValueObjectSP();
}
// Some synthetic plug-ins fail to set the error in Dereference
if (!valobj_sp) {
error.SetErrorString("Failed to dereference sythetic value");
return ValueObjectSP();
}
expr_is_ptr = false;
Expand Down

0 comments on commit 0d6f681

Please sign in to comment.