2020-05-15 23:13:02 +01:00
|
|
|
# Copyright (c) Facebook, Inc. and its affiliates.
|
|
|
|
# SPDX-License-Identifier: GPL-3.0+
|
|
|
|
|
2019-07-23 23:41:30 +01:00
|
|
|
import functools
|
2019-07-24 00:26:29 +01:00
|
|
|
from typing import Any, NamedTuple, Optional
|
2019-07-23 23:41:30 +01:00
|
|
|
import unittest
|
|
|
|
|
2019-05-10 07:53:16 +01:00
|
|
|
from drgn import (
|
2019-07-23 23:41:30 +01:00
|
|
|
Architecture,
|
|
|
|
FindObjectFlags,
|
2020-02-26 21:22:51 +00:00
|
|
|
Language,
|
2019-07-23 23:41:30 +01:00
|
|
|
Object,
|
2019-07-29 08:57:28 +01:00
|
|
|
Platform,
|
|
|
|
PlatformFlags,
|
2019-07-23 23:41:30 +01:00
|
|
|
Program,
|
2019-07-24 00:26:29 +01:00
|
|
|
Type,
|
2020-02-11 19:33:22 +00:00
|
|
|
TypeEnumerator,
|
2019-07-23 23:41:30 +01:00
|
|
|
TypeKind,
|
2020-02-12 20:04:36 +00:00
|
|
|
TypeMember,
|
2019-11-15 01:12:47 +00:00
|
|
|
class_type,
|
2019-07-23 23:41:30 +01:00
|
|
|
enum_type,
|
2019-05-10 07:53:16 +01:00
|
|
|
float_type,
|
2019-07-23 23:41:30 +01:00
|
|
|
int_type,
|
2019-05-10 07:53:16 +01:00
|
|
|
struct_type,
|
|
|
|
typedef_type,
|
2019-07-23 23:41:30 +01:00
|
|
|
union_type,
|
2019-05-10 07:53:16 +01:00
|
|
|
)
|
2019-03-31 20:31:20 +01:00
|
|
|
|
2020-02-26 21:22:51 +00:00
|
|
|
DEFAULT_LANGUAGE = Language.C
|
|
|
|
|
|
|
|
|
2019-11-15 01:12:47 +00:00
|
|
|
coord_type = class_type(
|
|
|
|
"coord",
|
|
|
|
12,
|
|
|
|
(
|
2020-02-12 20:04:36 +00:00
|
|
|
TypeMember(int_type("int", 4, True), "x", 0),
|
|
|
|
TypeMember(int_type("int", 4, True), "y", 32),
|
|
|
|
TypeMember(int_type("int", 4, True), "z", 64),
|
2020-01-14 19:43:58 +00:00
|
|
|
),
|
2019-11-15 01:12:47 +00:00
|
|
|
)
|
2019-05-10 07:53:16 +01:00
|
|
|
point_type = struct_type(
|
|
|
|
"point",
|
|
|
|
8,
|
2020-02-12 20:04:36 +00:00
|
|
|
(
|
|
|
|
TypeMember(int_type("int", 4, True), "x", 0),
|
|
|
|
TypeMember(int_type("int", 4, True), "y", 32),
|
|
|
|
),
|
2019-05-10 07:53:16 +01:00
|
|
|
)
|
|
|
|
line_segment_type = struct_type(
|
2020-02-12 20:04:36 +00:00
|
|
|
"line_segment", 16, (TypeMember(point_type, "a"), TypeMember(point_type, "b", 64))
|
2019-05-10 07:53:16 +01:00
|
|
|
)
|
|
|
|
option_type = union_type(
|
2020-02-12 20:04:36 +00:00
|
|
|
"option",
|
|
|
|
4,
|
|
|
|
(
|
|
|
|
TypeMember(int_type("int", 4, True), "i"),
|
|
|
|
TypeMember(float_type("float", 4), "f"),
|
|
|
|
),
|
2019-05-10 07:53:16 +01:00
|
|
|
)
|
|
|
|
color_type = enum_type(
|
2020-02-11 19:33:22 +00:00
|
|
|
"color",
|
|
|
|
int_type("unsigned int", 4, False),
|
|
|
|
(TypeEnumerator("RED", 0), TypeEnumerator("GREEN", 1), TypeEnumerator("BLUE", 2)),
|
2019-05-10 07:53:16 +01:00
|
|
|
)
|
|
|
|
pid_type = typedef_type("pid_t", int_type("int", 4, True))
|
2020-01-14 19:43:58 +00:00
|
|
|
|
|
|
|
|
2019-07-29 08:57:28 +01:00
|
|
|
MOCK_32BIT_PLATFORM = Platform(Architecture.UNKNOWN, PlatformFlags.IS_LITTLE_ENDIAN)
|
|
|
|
MOCK_PLATFORM = Platform(
|
|
|
|
Architecture.UNKNOWN, PlatformFlags.IS_64_BIT | PlatformFlags.IS_LITTLE_ENDIAN
|
|
|
|
)
|
2019-07-23 23:41:30 +01:00
|
|
|
|
|
|
|
|
|
|
|
class MockMemorySegment(NamedTuple):
|
|
|
|
buf: bytes
|
|
|
|
virt_addr: Optional[int] = None
|
|
|
|
phys_addr: Optional[int] = None
|
|
|
|
|
|
|
|
|
|
|
|
def mock_memory_read(data, address, count, offset, physical):
|
|
|
|
return data[offset : offset + count]
|
|
|
|
|
|
|
|
|
2019-07-24 00:26:29 +01:00
|
|
|
class MockObject(NamedTuple):
|
|
|
|
name: str
|
|
|
|
type: Type
|
|
|
|
address: Optional[int] = None
|
|
|
|
value: Any = None
|
|
|
|
|
|
|
|
|
2019-07-29 08:57:28 +01:00
|
|
|
def mock_program(platform=MOCK_PLATFORM, *, segments=None, types=None, objects=None):
|
2019-07-23 23:41:30 +01:00
|
|
|
def mock_find_type(kind, name, filename):
|
|
|
|
if filename:
|
|
|
|
return None
|
|
|
|
for type in types:
|
|
|
|
if type.kind == kind:
|
|
|
|
try:
|
|
|
|
type_name = type.name
|
|
|
|
except AttributeError:
|
|
|
|
try:
|
|
|
|
type_name = type.tag
|
|
|
|
except AttributeError:
|
|
|
|
continue
|
|
|
|
if type_name == name:
|
|
|
|
return type
|
|
|
|
return None
|
|
|
|
|
2019-07-24 00:26:29 +01:00
|
|
|
def mock_object_find(prog, name, flags, filename):
|
2019-07-23 23:41:30 +01:00
|
|
|
if filename:
|
|
|
|
return None
|
2019-07-24 00:26:29 +01:00
|
|
|
for obj in objects:
|
|
|
|
if obj.name == name:
|
|
|
|
if obj.value is not None:
|
2019-07-23 23:41:30 +01:00
|
|
|
if flags & FindObjectFlags.CONSTANT:
|
|
|
|
break
|
2019-07-24 00:26:29 +01:00
|
|
|
elif obj.type.kind == TypeKind.FUNCTION:
|
2019-07-23 23:41:30 +01:00
|
|
|
if flags & FindObjectFlags.FUNCTION:
|
|
|
|
break
|
|
|
|
elif flags & FindObjectFlags.VARIABLE:
|
|
|
|
break
|
|
|
|
else:
|
|
|
|
return None
|
2019-07-24 00:26:29 +01:00
|
|
|
return Object(prog, obj.type, address=obj.address, value=obj.value)
|
2019-07-23 23:41:30 +01:00
|
|
|
|
2019-07-29 08:57:28 +01:00
|
|
|
prog = Program(platform)
|
2019-07-23 23:41:30 +01:00
|
|
|
if segments is not None:
|
|
|
|
for segment in segments:
|
|
|
|
if segment.virt_addr is not None:
|
|
|
|
prog.add_memory_segment(
|
|
|
|
segment.virt_addr,
|
|
|
|
len(segment.buf),
|
|
|
|
functools.partial(mock_memory_read, segment.buf),
|
|
|
|
)
|
|
|
|
if segment.phys_addr is not None:
|
|
|
|
prog.add_memory_segment(
|
|
|
|
segment.phys_addr,
|
|
|
|
len(segment.buf),
|
|
|
|
functools.partial(mock_memory_read, segment.buf),
|
|
|
|
True,
|
|
|
|
)
|
|
|
|
if types is not None:
|
|
|
|
prog.add_type_finder(mock_find_type)
|
2019-07-24 00:26:29 +01:00
|
|
|
if objects is not None:
|
|
|
|
prog.add_object_finder(mock_object_find)
|
2019-07-23 23:41:30 +01:00
|
|
|
return prog
|
|
|
|
|
|
|
|
|
|
|
|
class ObjectTestCase(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
|
|
super().setUp()
|
|
|
|
self.prog = mock_program()
|
|
|
|
# For testing, we want to compare the raw objects rather than using the
|
|
|
|
# language's equality operator.
|
|
|
|
def object_equality_func(a, b, msg=None):
|
|
|
|
if a.prog_ is not b.prog_:
|
|
|
|
raise self.failureException(msg or "objects have different program")
|
|
|
|
if a.type_ != b.type_:
|
|
|
|
raise self.failureException(
|
|
|
|
msg or f"object types differ: {a.type_!r} != {b.type_!r}"
|
|
|
|
)
|
|
|
|
if a.address_ != b.address_:
|
|
|
|
a_address = "None" if a.address_ is None else hex(a.address_)
|
|
|
|
b_address = "None" if b.address_ is None else hex(b.address_)
|
|
|
|
raise self.failureException(
|
|
|
|
msg or f"object addresses differ: {a_address} != {b_address}"
|
|
|
|
)
|
|
|
|
if a.byteorder_ != b.byteorder_:
|
|
|
|
raise self.failureException(
|
|
|
|
msg or f"object byteorders differ: {a.byteorder_} != {b.byteorder_}"
|
|
|
|
)
|
|
|
|
if a.bit_offset_ != b.bit_offset_:
|
|
|
|
raise self.failureException(
|
|
|
|
msg
|
|
|
|
or f"object bit offsets differ: {a.bit_offset_} != {b.bit_offset_}"
|
|
|
|
)
|
|
|
|
if a.bit_field_size_ != b.bit_field_size_:
|
|
|
|
raise self.failureException(
|
|
|
|
msg
|
|
|
|
or f"object bit field sizes differ: {a.bit_field_size_} != {b.bit_field_size_}"
|
|
|
|
)
|
|
|
|
exc_a = exc_b = False
|
|
|
|
try:
|
|
|
|
value_a = a.value_()
|
|
|
|
except Exception:
|
|
|
|
exc_a = True
|
|
|
|
try:
|
|
|
|
value_b = b.value_()
|
|
|
|
except Exception:
|
|
|
|
exc_b = True
|
|
|
|
if exc_a and not exc_b:
|
|
|
|
raise self.failureException(
|
|
|
|
msg or f"exception raised while reading {a!r}"
|
|
|
|
)
|
|
|
|
if not exc_a and exc_b:
|
|
|
|
raise self.failureException(
|
|
|
|
msg or f"exception raised while reading {b!r}"
|
|
|
|
)
|
|
|
|
if not exc_a and value_a != value_b:
|
|
|
|
raise self.failureException(
|
|
|
|
msg or f"object values differ: {value_a!r} != {value_b!r}"
|
|
|
|
)
|
2020-01-14 19:43:58 +00:00
|
|
|
|
2019-07-23 23:41:30 +01:00
|
|
|
self.addTypeEqualityFunc(Object, object_equality_func)
|
|
|
|
|
|
|
|
def bool(self, value):
|
|
|
|
return Object(self.prog, "_Bool", value=value)
|
|
|
|
|
|
|
|
def int(self, value):
|
|
|
|
return Object(self.prog, "int", value=value)
|
|
|
|
|
|
|
|
def unsigned_int(self, value):
|
|
|
|
return Object(self.prog, "unsigned int", value=value)
|
|
|
|
|
|
|
|
def long(self, value):
|
|
|
|
return Object(self.prog, "long", value=value)
|
|
|
|
|
|
|
|
def double(self, value):
|
|
|
|
return Object(self.prog, "double", value=value)
|