Skip to content

Commit b8c657d

Browse files
committed
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.
1 parent 12b0214 commit b8c657d

File tree

7 files changed

+64
-5
lines changed

7 files changed

+64
-5
lines changed

docs/api_reference.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,16 @@ can be used just like types obtained from :meth:`Program.type()`.
12821282
Miscellaneous
12831283
-------------
12841284

1285+
.. function:: sizeof(type_or_obj)
1286+
1287+
Get the size of a :class:`Type` or :class:`Object` in bytes.
1288+
1289+
:param type_or_obj: Entity to get the size of.
1290+
:type type_or_obj: Type or Object
1291+
:rtype: int
1292+
:raises TypeError: if the type does not have a size (e.g., because it is
1293+
incomplete or void)
1294+
12851295
.. autofunction:: execscript
12861296

12871297
Exceptions

drgn/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
program_from_kernel,
8080
program_from_pid,
8181
reinterpret,
82+
sizeof,
8283
struct_type,
8384
typedef_type,
8485
union_type,
@@ -121,6 +122,7 @@
121122
'program_from_kernel',
122123
'program_from_pid',
123124
'reinterpret',
125+
'sizeof',
124126
'struct_type',
125127
'typedef_type',
126128
'union_type',

drgn/internal/cli.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,15 @@ def main() -> None:
104104
from drgn.internal.rlcompleter import Completer
105105

106106
init_globals['drgn'] = drgn
107-
drgn_globals = ['cast', 'container_of', 'execscript', 'NULL', 'Object',
108-
'reinterpret']
107+
drgn_globals = [
108+
'NULL',
109+
'Object',
110+
'cast',
111+
'container_of',
112+
'execscript',
113+
'reinterpret',
114+
'sizeof',
115+
]
109116
for attr in drgn_globals:
110117
init_globals[attr] = getattr(drgn, attr)
111118
init_globals['__name__'] = '__main__'

libdrgn/object.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1423,7 +1423,7 @@ LIBDRGN_PUBLIC struct drgn_error *
14231423
drgn_object_sizeof(const struct drgn_object *obj, uint64_t *ret)
14241424
{
14251425
if (obj->is_bit_field) {
1426-
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
1426+
return drgn_error_create(DRGN_ERROR_TYPE,
14271427
"cannot get size of bit field");
14281428
}
14291429
return drgn_type_sizeof(obj->type, ret);

libdrgn/python/module.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,31 @@ static PyObject *filename_matches(PyObject *self, PyObject *args,
5050
Py_RETURN_FALSE;
5151
}
5252

53+
static PyObject *sizeof_(PyObject *self, PyObject *arg)
54+
{
55+
struct drgn_error *err;
56+
uint64_t size;
57+
58+
if (PyObject_TypeCheck(arg, &DrgnType_type)) {
59+
err = drgn_type_sizeof(((DrgnType *)arg)->type, &size);
60+
} else if (PyObject_TypeCheck(arg, &DrgnObject_type)) {
61+
err = drgn_object_sizeof(&((DrgnObject *)arg)->obj, &size);
62+
} else {
63+
return PyErr_Format(PyExc_TypeError,
64+
"expected Type or Object, not %s",
65+
Py_TYPE(arg)->tp_name);
66+
}
67+
if (err)
68+
return set_drgn_error(err);
69+
return PyLong_FromUnsignedLongLong(size);
70+
}
71+
5372
static PyMethodDef drgn_methods[] = {
5473
{"filename_matches", (PyCFunction)filename_matches,
5574
METH_VARARGS | METH_KEYWORDS, drgn_filename_matches_DOC},
5675
{"NULL", (PyCFunction)DrgnObject_NULL, METH_VARARGS | METH_KEYWORDS,
5776
drgn_NULL_DOC},
77+
{"sizeof", (PyCFunction)sizeof_, METH_O, drgn_sizeof_DOC},
5878
{"cast", (PyCFunction)cast, METH_VARARGS | METH_KEYWORDS,
5979
drgn_cast_DOC},
6080
{"reinterpret", (PyCFunction)reinterpret, METH_VARARGS | METH_KEYWORDS,

tests/test_object.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
int_type,
1717
pointer_type,
1818
reinterpret,
19+
sizeof,
1920
struct_type,
2021
typedef_type,
2122
union_type,
@@ -116,13 +117,15 @@ def test_basic(self):
116117
self.assertEqual(obj.byteorder_, 'big')
117118
self.assertEqual(obj.value_(), -402456576)
118119
self.assertEqual(repr(obj), "Object(prog, 'int', address=0xffff0000, byteorder='big')")
120+
self.assertEqual(sizeof(obj), 4)
119121

120122
obj = Object(prog, 'unsigned int', address=0xffff0000,
121123
bit_field_size=4)
122124
self.assertEqual(obj.bit_offset_, 0)
123125
self.assertEqual(obj.bit_field_size_, 4)
124126
self.assertEqual(obj.value_(), 8)
125127
self.assertEqual(repr(obj), "Object(prog, 'unsigned int', address=0xffff0000, bit_field_size=4)")
128+
self.assertRaises(TypeError, sizeof, obj)
126129

127130
obj = Object(prog, 'unsigned int', address=0xffff0000,
128131
bit_field_size=4, bit_offset=4)
@@ -182,7 +185,7 @@ def test_read_float(self):
182185
byteorder=byteorder)
183186
self.assertEqual(obj.value_(), expected)
184187

185-
def test_read_struct(self):
188+
def test_struct(self):
186189
segment = ((99).to_bytes(4, 'little') +
187190
(-1).to_bytes(4, 'little', signed=True) +
188191
(12345).to_bytes(4, 'little') +
@@ -193,6 +196,7 @@ def test_read_struct(self):
193196

194197
obj = Object(prog, 'struct point', address=0xffff0000)
195198
self.assertEqual(obj.value_(), {'x': 99, 'y': -1})
199+
self.assertEqual(sizeof(obj), 8)
196200

197201
type_ = struct_type('foo', 16, (
198202
(point_type, 'point'),
@@ -205,7 +209,7 @@ def test_read_struct(self):
205209
self.assertEqual(obj.value_(),
206210
{'point': {'x': 99, 'y': -1}, 'bar': 12345, 'baz': 0})
207211

208-
def test_read_array(self):
212+
def test_array(self):
209213
segment = bytearray()
210214
for i in range(10):
211215
segment.extend(i.to_bytes(4, 'little'))
@@ -215,6 +219,7 @@ def test_read_array(self):
215219

216220
obj = Object(prog, 'int [5]', address=0xffff0000)
217221
self.assertEqual(obj.value_(), [0, 1, 2, 3, 4])
222+
self.assertEqual(sizeof(obj), 20)
218223

219224
obj = Object(prog, 'int [2][5]', address=0xffff0000)
220225
self.assertEqual(obj.value_(), [[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]])
@@ -235,6 +240,7 @@ def test_void(self):
235240
obj.value_)
236241
self.assertRaisesRegex(TypeError, 'cannot read object with void type',
237242
obj.read_)
243+
self.assertRaises(TypeError, sizeof, obj)
238244

239245
def test_function(self):
240246
obj = Object(self.prog,
@@ -251,6 +257,7 @@ def test_function(self):
251257
self.assertRaisesRegex(TypeError,
252258
'cannot read object with function type',
253259
obj.read_)
260+
self.assertRaises(TypeError, sizeof, obj)
254261

255262
def test_incomplete(self):
256263
# It's valid to create references with incomplete type, but not to read
@@ -262,6 +269,7 @@ def test_incomplete(self):
262269
self.assertRaisesRegex(TypeError,
263270
'cannot read object with incomplete structure type',
264271
obj.read_)
272+
self.assertRaises(TypeError, sizeof, obj)
265273

266274
obj = Object(self.prog, union_type('foo'), address=0)
267275
self.assertRaisesRegex(TypeError, 'cannot read object with incomplete union type',

tests/test_type.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
function_type,
1313
int_type,
1414
pointer_type,
15+
sizeof,
1516
struct_type,
1617
typedef_type,
1718
union_type,
@@ -43,6 +44,7 @@ def test_int(self):
4344
self.assertNotEqual(t, int_type('int', 4, False))
4445

4546
self.assertEqual(repr(t), "int_type(name='int', size=4, is_signed=True)")
47+
self.assertEqual(sizeof(t), 4)
4648

4749
self.assertRaises(TypeError, int_type, None, 4, True)
4850

@@ -62,6 +64,7 @@ def test_bool(self):
6264
self.assertNotEqual(t, bool_type('_Bool', 2))
6365

6466
self.assertEqual(repr(t), "bool_type(name='_Bool', size=1)")
67+
self.assertEqual(sizeof(t), 1)
6568

6669
self.assertRaises(TypeError, bool_type, None, 1)
6770

@@ -78,6 +81,7 @@ def test_float(self):
7881
self.assertNotEqual(t, float_type('float', 8))
7982

8083
self.assertEqual(repr(t), "float_type(name='float', size=4)")
84+
self.assertEqual(sizeof(t), 4)
8185

8286
self.assertRaises(TypeError, float_type, None, 4)
8387

@@ -96,6 +100,7 @@ def test_complex(self):
96100
self.assertNotEqual(t, complex_type('double _Complex', 16, float_type('float', 4)))
97101

98102
self.assertEqual(repr(t), "complex_type(name='double _Complex', size=16, type=float_type(name='double', size=8))")
103+
self.assertEqual(sizeof(t), 16)
99104

100105
self.assertRaises(TypeError, complex_type, None, 16, float_type('double', 8))
101106
self.assertRaises(TypeError, complex_type, 'double _Complex', 16, None)
@@ -161,6 +166,7 @@ def test_struct(self):
161166
self.assertNotEqual(t, struct_type('point'))
162167

163168
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)))")
169+
self.assertEqual(sizeof(t), 8)
164170

165171
t = struct_type(None, 8, (
166172
(int_type('int', 4, True), 'x', 0),
@@ -287,6 +293,7 @@ def test_union(self):
287293
self.assertNotEqual(t, union_type('option'))
288294

289295
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)))")
296+
self.assertEqual(sizeof(t), 4)
290297

291298
t = union_type(None, 4, (
292299
(int_type('int', 4, True), 'x'),
@@ -399,6 +406,7 @@ def test_enum(self):
399406

400407
self.assertEqual(repr(t),
401408
"enum_type(tag='color', type=int_type(name='unsigned int', size=4, is_signed=False), enumerators=(('RED', 0), ('GREEN', 1), ('BLUE', 2)))")
409+
self.assertEqual(sizeof(t), 4)
402410

403411
t = enum_type('color', None, None)
404412
self.assertEqual(t.kind, TypeKind.ENUM)
@@ -477,6 +485,7 @@ def test_typedef(self):
477485
'INT', int_type('int', 4, True, Qualifiers.CONST)))
478486

479487
self.assertEqual(repr(t), "typedef_type(name='INT', type=int_type(name='int', size=4, is_signed=True))")
488+
self.assertEqual(sizeof(t), 4)
480489

481490
t = typedef_type('VOID', void_type())
482491
self.assertFalse(t.is_complete())
@@ -511,6 +520,7 @@ def test_pointer(self):
511520
self.assertNotEqual(t, pointer_type(8, void_type(Qualifiers.CONST)))
512521

513522
self.assertEqual(repr(t), "pointer_type(size=8, type=int_type(name='int', size=4, is_signed=True))")
523+
self.assertEqual(sizeof(t), 8)
514524

515525
self.assertRaises(TypeError, pointer_type, None,
516526
int_type('int', 4, True))
@@ -535,6 +545,7 @@ def test_array(self):
535545
self.assertNotEqual(t, array_type(10, void_type(Qualifiers.CONST)))
536546

537547
self.assertEqual(repr(t), "array_type(length=10, type=int_type(name='int', size=4, is_signed=True))")
548+
self.assertEqual(sizeof(t), 40)
538549

539550
t = array_type(0, int_type('int', 4, True))
540551
self.assertEqual(t.kind, TypeKind.ARRAY)
@@ -584,6 +595,7 @@ def test_function(self):
584595
void_type(), ((int_type('int', 4, True), 'n'),), True))
585596

586597
self.assertEqual(repr(t), "function_type(type=void_type(), parameters=((int_type(name='int', size=4, is_signed=True), 'n'),), is_variadic=False)")
598+
self.assertRaises(TypeError, sizeof, t)
587599

588600
self.assertFalse(function_type(void_type(), (), False).is_variadic)
589601
self.assertTrue(function_type(void_type(), (), True).is_variadic)

0 commit comments

Comments
 (0)