libdrgn: python: add sizeof()

It's annoying to do obj.type_.size, and that doesn't even work for every
type. Add sizeof() that does the right thing whether it's given a Type
or Object.
This commit is contained in:
Omar Sandoval 2019-10-18 11:47:32 -07:00
parent 12b0214b4d
commit b8c657d760
7 changed files with 64 additions and 5 deletions

View File

@ -1282,6 +1282,16 @@ can be used just like types obtained from :meth:`Program.type()`.
Miscellaneous
-------------
.. function:: sizeof(type_or_obj)
Get the size of a :class:`Type` or :class:`Object` in bytes.
:param type_or_obj: Entity to get the size of.
:type type_or_obj: Type or Object
:rtype: int
:raises TypeError: if the type does not have a size (e.g., because it is
incomplete or void)
.. autofunction:: execscript
Exceptions

View File

@ -79,6 +79,7 @@ from _drgn import (
program_from_kernel,
program_from_pid,
reinterpret,
sizeof,
struct_type,
typedef_type,
union_type,
@ -121,6 +122,7 @@ __all__ = [
'program_from_kernel',
'program_from_pid',
'reinterpret',
'sizeof',
'struct_type',
'typedef_type',
'union_type',

View File

@ -104,8 +104,15 @@ def main() -> None:
from drgn.internal.rlcompleter import Completer
init_globals['drgn'] = drgn
drgn_globals = ['cast', 'container_of', 'execscript', 'NULL', 'Object',
'reinterpret']
drgn_globals = [
'NULL',
'Object',
'cast',
'container_of',
'execscript',
'reinterpret',
'sizeof',
]
for attr in drgn_globals:
init_globals[attr] = getattr(drgn, attr)
init_globals['__name__'] = '__main__'

View File

@ -1423,7 +1423,7 @@ LIBDRGN_PUBLIC struct drgn_error *
drgn_object_sizeof(const struct drgn_object *obj, uint64_t *ret)
{
if (obj->is_bit_field) {
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
return drgn_error_create(DRGN_ERROR_TYPE,
"cannot get size of bit field");
}
return drgn_type_sizeof(obj->type, ret);

View File

@ -50,11 +50,31 @@ static PyObject *filename_matches(PyObject *self, PyObject *args,
Py_RETURN_FALSE;
}
static PyObject *sizeof_(PyObject *self, PyObject *arg)
{
struct drgn_error *err;
uint64_t size;
if (PyObject_TypeCheck(arg, &DrgnType_type)) {
err = drgn_type_sizeof(((DrgnType *)arg)->type, &size);
} else if (PyObject_TypeCheck(arg, &DrgnObject_type)) {
err = drgn_object_sizeof(&((DrgnObject *)arg)->obj, &size);
} else {
return PyErr_Format(PyExc_TypeError,
"expected Type or Object, not %s",
Py_TYPE(arg)->tp_name);
}
if (err)
return set_drgn_error(err);
return PyLong_FromUnsignedLongLong(size);
}
static PyMethodDef drgn_methods[] = {
{"filename_matches", (PyCFunction)filename_matches,
METH_VARARGS | METH_KEYWORDS, drgn_filename_matches_DOC},
{"NULL", (PyCFunction)DrgnObject_NULL, METH_VARARGS | METH_KEYWORDS,
drgn_NULL_DOC},
{"sizeof", (PyCFunction)sizeof_, METH_O, drgn_sizeof_DOC},
{"cast", (PyCFunction)cast, METH_VARARGS | METH_KEYWORDS,
drgn_cast_DOC},
{"reinterpret", (PyCFunction)reinterpret, METH_VARARGS | METH_KEYWORDS,

View File

@ -16,6 +16,7 @@ from drgn import (
int_type,
pointer_type,
reinterpret,
sizeof,
struct_type,
typedef_type,
union_type,
@ -116,6 +117,7 @@ class TestReference(ObjectTestCase):
self.assertEqual(obj.byteorder_, 'big')
self.assertEqual(obj.value_(), -402456576)
self.assertEqual(repr(obj), "Object(prog, 'int', address=0xffff0000, byteorder='big')")
self.assertEqual(sizeof(obj), 4)
obj = Object(prog, 'unsigned int', address=0xffff0000,
bit_field_size=4)
@ -123,6 +125,7 @@ class TestReference(ObjectTestCase):
self.assertEqual(obj.bit_field_size_, 4)
self.assertEqual(obj.value_(), 8)
self.assertEqual(repr(obj), "Object(prog, 'unsigned int', address=0xffff0000, bit_field_size=4)")
self.assertRaises(TypeError, sizeof, obj)
obj = Object(prog, 'unsigned int', address=0xffff0000,
bit_field_size=4, bit_offset=4)
@ -182,7 +185,7 @@ class TestReference(ObjectTestCase):
byteorder=byteorder)
self.assertEqual(obj.value_(), expected)
def test_read_struct(self):
def test_struct(self):
segment = ((99).to_bytes(4, 'little') +
(-1).to_bytes(4, 'little', signed=True) +
(12345).to_bytes(4, 'little') +
@ -193,6 +196,7 @@ class TestReference(ObjectTestCase):
obj = Object(prog, 'struct point', address=0xffff0000)
self.assertEqual(obj.value_(), {'x': 99, 'y': -1})
self.assertEqual(sizeof(obj), 8)
type_ = struct_type('foo', 16, (
(point_type, 'point'),
@ -205,7 +209,7 @@ class TestReference(ObjectTestCase):
self.assertEqual(obj.value_(),
{'point': {'x': 99, 'y': -1}, 'bar': 12345, 'baz': 0})
def test_read_array(self):
def test_array(self):
segment = bytearray()
for i in range(10):
segment.extend(i.to_bytes(4, 'little'))
@ -215,6 +219,7 @@ class TestReference(ObjectTestCase):
obj = Object(prog, 'int [5]', address=0xffff0000)
self.assertEqual(obj.value_(), [0, 1, 2, 3, 4])
self.assertEqual(sizeof(obj), 20)
obj = Object(prog, 'int [2][5]', address=0xffff0000)
self.assertEqual(obj.value_(), [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]])
@ -235,6 +240,7 @@ class TestReference(ObjectTestCase):
obj.value_)
self.assertRaisesRegex(TypeError, 'cannot read object with void type',
obj.read_)
self.assertRaises(TypeError, sizeof, obj)
def test_function(self):
obj = Object(self.prog,
@ -251,6 +257,7 @@ class TestReference(ObjectTestCase):
self.assertRaisesRegex(TypeError,
'cannot read object with function type',
obj.read_)
self.assertRaises(TypeError, sizeof, obj)
def test_incomplete(self):
# It's valid to create references with incomplete type, but not to read
@ -262,6 +269,7 @@ class TestReference(ObjectTestCase):
self.assertRaisesRegex(TypeError,
'cannot read object with incomplete structure type',
obj.read_)
self.assertRaises(TypeError, sizeof, obj)
obj = Object(self.prog, union_type('foo'), address=0)
self.assertRaisesRegex(TypeError, 'cannot read object with incomplete union type',

View File

@ -12,6 +12,7 @@ from drgn import (
function_type,
int_type,
pointer_type,
sizeof,
struct_type,
typedef_type,
union_type,
@ -43,6 +44,7 @@ class TestType(unittest.TestCase):
self.assertNotEqual(t, int_type('int', 4, False))
self.assertEqual(repr(t), "int_type(name='int', size=4, is_signed=True)")
self.assertEqual(sizeof(t), 4)
self.assertRaises(TypeError, int_type, None, 4, True)
@ -62,6 +64,7 @@ class TestType(unittest.TestCase):
self.assertNotEqual(t, bool_type('_Bool', 2))
self.assertEqual(repr(t), "bool_type(name='_Bool', size=1)")
self.assertEqual(sizeof(t), 1)
self.assertRaises(TypeError, bool_type, None, 1)
@ -78,6 +81,7 @@ class TestType(unittest.TestCase):
self.assertNotEqual(t, float_type('float', 8))
self.assertEqual(repr(t), "float_type(name='float', size=4)")
self.assertEqual(sizeof(t), 4)
self.assertRaises(TypeError, float_type, None, 4)
@ -96,6 +100,7 @@ class TestType(unittest.TestCase):
self.assertNotEqual(t, complex_type('double _Complex', 16, float_type('float', 4)))
self.assertEqual(repr(t), "complex_type(name='double _Complex', size=16, type=float_type(name='double', size=8))")
self.assertEqual(sizeof(t), 16)
self.assertRaises(TypeError, complex_type, None, 16, float_type('double', 8))
self.assertRaises(TypeError, complex_type, 'double _Complex', 16, None)
@ -161,6 +166,7 @@ class TestType(unittest.TestCase):
self.assertNotEqual(t, struct_type('point'))
self.assertEqual(repr(t), "struct_type(tag='point', size=8, members=((int_type(name='int', size=4, is_signed=True), 'x', 0, 0), (int_type(name='int', size=4, is_signed=True), 'y', 32, 0)))")
self.assertEqual(sizeof(t), 8)
t = struct_type(None, 8, (
(int_type('int', 4, True), 'x', 0),
@ -287,6 +293,7 @@ class TestType(unittest.TestCase):
self.assertNotEqual(t, union_type('option'))
self.assertEqual(repr(t), "union_type(tag='option', size=4, members=((int_type(name='int', size=4, is_signed=True), 'x', 0, 0), (int_type(name='unsigned int', size=4, is_signed=False), 'y', 0, 0)))")
self.assertEqual(sizeof(t), 4)
t = union_type(None, 4, (
(int_type('int', 4, True), 'x'),
@ -399,6 +406,7 @@ class TestType(unittest.TestCase):
self.assertEqual(repr(t),
"enum_type(tag='color', type=int_type(name='unsigned int', size=4, is_signed=False), enumerators=(('RED', 0), ('GREEN', 1), ('BLUE', 2)))")
self.assertEqual(sizeof(t), 4)
t = enum_type('color', None, None)
self.assertEqual(t.kind, TypeKind.ENUM)
@ -477,6 +485,7 @@ class TestType(unittest.TestCase):
'INT', int_type('int', 4, True, Qualifiers.CONST)))
self.assertEqual(repr(t), "typedef_type(name='INT', type=int_type(name='int', size=4, is_signed=True))")
self.assertEqual(sizeof(t), 4)
t = typedef_type('VOID', void_type())
self.assertFalse(t.is_complete())
@ -511,6 +520,7 @@ class TestType(unittest.TestCase):
self.assertNotEqual(t, pointer_type(8, void_type(Qualifiers.CONST)))
self.assertEqual(repr(t), "pointer_type(size=8, type=int_type(name='int', size=4, is_signed=True))")
self.assertEqual(sizeof(t), 8)
self.assertRaises(TypeError, pointer_type, None,
int_type('int', 4, True))
@ -535,6 +545,7 @@ class TestType(unittest.TestCase):
self.assertNotEqual(t, array_type(10, void_type(Qualifiers.CONST)))
self.assertEqual(repr(t), "array_type(length=10, type=int_type(name='int', size=4, is_signed=True))")
self.assertEqual(sizeof(t), 40)
t = array_type(0, int_type('int', 4, True))
self.assertEqual(t.kind, TypeKind.ARRAY)
@ -584,6 +595,7 @@ class TestType(unittest.TestCase):
void_type(), ((int_type('int', 4, True), 'n'),), True))
self.assertEqual(repr(t), "function_type(type=void_type(), parameters=((int_type(name='int', size=4, is_signed=True), 'n'),), is_variadic=False)")
self.assertRaises(TypeError, sizeof, t)
self.assertFalse(function_type(void_type(), (), False).is_variadic)
self.assertTrue(function_type(void_type(), (), True).is_variadic)