Initial support for VDB python-based variable objects
Due to a limitation of GDB itself, one cannot create variable
object for "synthetic" argument/local created by custom frame
decorator. Therefore one cannot "dig deeper" and inspect contents
of such a variable in case it is a kind of composite - like an
object on a heap referring to another objects.
This is a problem when a debugee is does not have debug information
and / or it's language is not C - such as when debugging JITed code.
This (experimental) commit adds a parallel implementation of variable
objects done completely in python. This new varobj MI interface is a
super-set of GDB's builtin varobj MI interface.
#
# jv:vdb - Visual / VM Debugger
# Copyright (C) 2015-now Jan Vrany
#
# This software is licensed under 'Creative Commons Attribution-NonCommercial 4.0 International License'
#
# You may find a full license text in LICENSE.txt or at http://creativecommons.org/licenses/by-nc/4.0/
#
"""
This script contains tests Pythin vdb support.
To run all tests
gdb -q --ex "source vdbtests.py" -ex 'py run()' -ex "quit"
To run single test, for example, _instanceTests.test_context_03():
gdb -q --ex "source vdbtests.py" -ex 'py run(parse_and_eval_test("test_01"))' -ex "quit"
"""
import os
import sys
import unittest
vdb_tests_python_dir = os.path.dirname(__file__)
vdb_python_dir = os.path.join(vdb_tests_python_dir, '..' , '..' , 'python')
libgdbs_tests_dir = os.path.join(vdb_python_dir, '..', '..' , 'libgdbs' , 'tests')
sys.path.append(vdb_python_dir)
import gdb
import vdb
class parse_and_eval_test(unittest.TestCase):
def setUp(self):
test_prog_dir = os.path.join(libgdbs_tests_dir, 'c', 'x86_64-pc-linux-gnu')
gdb.execute('set startup-with-shell off')
gdb.execute('set auto-load safe-path /')
gdb.execute('set python print-stack full')
gdb.execute('set breakpoint pending on')
gdb.execute('set confirm off')
gdb.execute('set pagination off')
gdb.execute('file "%s"' % os.path.join(test_prog_dir, 'py-varobj'))
gdb.execute('source %s' % os.path.join(test_prog_dir, '..', 'py-varobj.py'))
gdb.execute('source %s' % os.path.join(test_prog_dir, '..', 'py-framedecorator.py'))
gdb.execute('b py-varobj.c : 22')
gdb.execute('r')
gdb.execute('frame 0')
def tearDown(self):
vdb.unregister_evaluators()
gdb.execute("del")
try:
gdb.execute('kill')
except:
pass
def test_01a(self):
self.assertRaises(vdb.Error, vdb.parse_and_eval, 'foo')
self.assertEqual(gdb.Value(1), vdb.parse_and_eval('1'))
def test_01b(self):
v = vdb.parse_and_eval('&c1')
self.assertEqual('struct _cons *', str(v.type))
def test_01c(self):
v = vdb.parse_and_eval('&c1')
self.assertEqual('struct _cons *', str(v.type))
def test_02a(self):
self.assertEqual(gdb.Value(12), vdb.parse_and_eval('syntheticLocal0'))
self.assertEqual(gdb.Value(42), vdb.parse_and_eval('syntheticLocal2'))
def test_03(self):
def parse_and_eval(expr):
if expr == 'a':
return gdb.Value(1)
elif expr == 'b':
return None
elif expr == 'c':
raise Exception("Bad")
vdb.register_evaluator(parse_and_eval)
self.assertEqual(gdb.Value(1), vdb.parse_and_eval('a'))
self.assertRaises(vdb.Error, vdb.parse_and_eval, 'b')
self.assertRaises(Exception, vdb.parse_and_eval, 'c')
self.assertRaises(vdb.Error, vdb.parse_and_eval, 'd')
vdb.unregister_evaluator(parse_and_eval)
self.assertRaises(vdb.Error, vdb.parse_and_eval, 'a')
class VarObj_test(unittest.TestCase):
def setUp(self):
test_prog_dir = os.path.join(libgdbs_tests_dir, 'c', 'x86_64-pc-linux-gnu')
gdb.execute('set startup-with-shell off')
gdb.execute('set auto-load safe-path /')
gdb.execute('set python print-stack full')
gdb.execute('set breakpoint pending on')
gdb.execute('set confirm off')
gdb.execute('set pagination off')
gdb.execute('file "%s"' % os.path.join(test_prog_dir, 'py-varobj'))
gdb.execute('source %s' % os.path.join(test_prog_dir, '..', 'py-varobj.py'))
gdb.execute('source %s' % os.path.join(test_prog_dir, '..', 'py-framedecorator.py'))
gdb.execute('b py-varobj.c : 22')
gdb.execute('r')
gdb.execute('frame 0')
def tearDown(self):
vdb.unregister_evaluators()
gdb.execute("del")
try:
gdb.execute('kill')
except:
pass
def test_01(self):
varobj = vdb._VarObj('test_01', '1', gdb.Value(1))
self.assertEqual({ 'name' : 'test_01' ,
'numchild' : 0,
'exp' : '1',
'value' : '1',
'type' : 'long long',
'has_more' : 0,
'dynamic' : 1,
'frozen' : 0
},
varobj.to_mi())
def test_02(self):
varobj = vdb._VarObj('test_02', '&c1', gdb.parse_and_eval('&c1'))
self.assertEqual({ 'name' : 'test_02' ,
'numchild' : 0,
'exp' : '&c1',
'value' : '(...)',
'type' : 'struct _cons *',
'has_more' : 1,
'dynamic' : 1,
'frozen' : 0
},
varobj.to_mi())
children = list(varobj.children())
self.assertEqual(2, len(children))
self.assertEqual('car', children[0].exp)
self.assertEqual('cdr', children[1].exp)
self.assertEqual('test_02.car', children[0].name)
self.assertEqual('test_02.cdr', children[1].name)
self.assertEqual(varobj, children[0].parent)
self.assertEqual(varobj, children[1].parent)
children2 = list(varobj.children())
self.assertEqual(children[0], children2[0])
self.assertEqual(children[1], children2[1])
def test_03(self):
varobj = vdb._VarObj('test_02', '&c1', gdb.parse_and_eval('&c1'))
children = list(varobj.children())
self.assertEqual(2, len(children))
varobj.set_visualizer(vdb._DefaultVisualizer(varobj._value))
children = list(varobj.children())
self.assertEqual(0, len(children))
class MI_test(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
vdb._VarObjs.unregister_all()
def test_01(self):
self.assertEqual({ 'name' : 'test_01' ,
'numchild' : 0,
'exp' : '1',
'value' : '1',
'type' : 'int',
'has_more' : 0,
'dynamic' : 1,
'frozen' : 0
},
vdb._var_create(['test_01', '*' , '1']))
self.assertEqual(None,
vdb._var_delete(['test_01']))
def run(test = None):
if test == None:
unittest.main()
else:
suite = unittest.TestSuite()
suite.addTest(test)
runner = unittest.TextTestRunner()
runner.run(suite)