Beispiel #1
0
 def test_set_pid(self):
     # Debug the running Python interpreter itself.
     prog = Program()
     self.assertIsNone(prog.platform)
     self.assertFalse(prog.flags & ProgramFlags.IS_LIVE)
     prog.set_pid(os.getpid())
     self.assertEqual(prog.platform, host_platform)
     self.assertTrue(prog.flags & ProgramFlags.IS_LIVE)
     data = b"hello, world!"
     buf = ctypes.create_string_buffer(data)
     self.assertEqual(prog.read(ctypes.addressof(buf), len(data)), data)
     self.assertRaisesRegex(
         ValueError,
         "program memory was already initialized",
         prog.set_pid,
         os.getpid(),
     )
Beispiel #2
0
 def test_different_programs_typedef(self):
     self.assertRaisesRegex(
         ValueError,
         "type is from different program",
         self.prog.typedef_type,
         "INT",
         Program().int_type("int", 4, True),
     )
Beispiel #3
0
def _for_each_block_device(prog: Program) -> Iterator[Object]:
    try:
        class_in_private = prog.cache["knode_class_in_device_private"]
    except KeyError:
        # Linux kernel commit 570d0200123f ("driver core: move
        # device->knode_class to device_private") (in v5.1) moved the list
        # node.
        class_in_private = prog.type("struct device_private").has_member("knode_class")
        prog.cache["knode_class_in_device_private"] = class_in_private
    devices = prog["block_class"].p.klist_devices.k_list.address_of_()
    if class_in_private:
        for device_private in list_for_each_entry(
            "struct device_private", devices, "knode_class.n_node"
        ):
            yield device_private.device
    else:
        yield from list_for_each_entry("struct device", devices, "knode_class.n_node")
Beispiel #4
0
 def test_decode_enum_type_flags_incomplete(self):
     self.assertRaisesRegex(
         TypeError,
         "incomplete",
         decode_enum_type_flags,
         2,
         Program().enum_type(None),
     )
Beispiel #5
0
 def test_different_programs_function_parameter(self):
     self.assertRaisesRegex(
         ValueError,
         "type is from different program",
         self.prog.function_type,
         self.prog.void_type(),
         (TypeParameter(Program().int_type("int", 4, True)), ),
     )
Beispiel #6
0
 def test_different_programs_function_return(self):
     self.assertRaisesRegex(
         ValueError,
         "type is from different program",
         self.prog.function_type,
         Program().int_type("int", 4, True),
         (),
     )
Beispiel #7
0
 def test_different_programs_enum(self):
     self.assertRaisesRegex(
         ValueError,
         "type is from different program",
         self.prog.enum_type,
         None,
         Program().int_type("int", 4, True),
         (),
     )
Beispiel #8
0
 def test_different_programs_compound(self):
     self.assertRaisesRegex(
         ValueError,
         "type is from different program",
         self.prog.struct_type,
         None,
         4,
         (TypeMember(Program().int_type("int", 4, True)), ),
     )
Beispiel #9
0
 def test_different_programs_complex(self):
     self.assertRaisesRegex(
         ValueError,
         "type is from different program",
         self.prog.complex_type,
         "double _Complex",
         16,
         Program().float_type("double", 8),
     )
Beispiel #10
0
 def test_not_core_dump(self):
     prog = Program()
     self.assertRaisesRegex(ValueError, "not an ELF core file",
                            prog.set_core_dump, "/dev/null")
     with tempfile.NamedTemporaryFile() as f:
         f.write(create_elf_file(ET.EXEC, []))
         f.flush()
         self.assertRaisesRegex(ValueError, "not an ELF core file",
                                prog.set_core_dump, f.name)
Beispiel #11
0
 def test_overlap_same_address_smaller_size(self):
     # Existing segment: |_______|
     # New segment:      |___|
     prog = Program(MOCK_PLATFORM)
     segment1 = unittest.mock.Mock(side_effect=zero_memory_read)
     segment2 = unittest.mock.Mock(side_effect=zero_memory_read)
     prog.add_memory_segment(0xFFFF0000, 128, segment1)
     prog.add_memory_segment(0xFFFF0000, 64, segment2)
     prog.read(0xFFFF0000, 128)
     segment1.assert_called_once_with(0xFFFF0040, 64, 64, False)
     segment2.assert_called_once_with(0xFFFF0000, 64, 0, False)
Beispiel #12
0
 def test_overlap_same_address_larger_size(self):
     # Existing segment: |___|
     # New segment:      |_______|
     prog = Program()
     segment1 = unittest.mock.Mock(side_effect=zero_memory_read)
     segment2 = unittest.mock.Mock(side_effect=zero_memory_read)
     prog.add_memory_segment(0xffff0000, 64, segment1)
     prog.add_memory_segment(0xffff0000, 128, segment2)
     prog.read(0xffff0000, 128)
     segment1.assert_not_called()
     segment2.assert_called_once_with(0xffff0000, 128, 0, False)
Beispiel #13
0
def _for_each_block_device(prog: Program) -> Iterator[Object]:
    try:
        class_in_private = prog.cache["knode_class_in_device_private"]
    except KeyError:
        # We need a proper has_member(), but this is fine for now.
        class_in_private = any(
            member.name == "knode_class" for member in prog.type(
                "struct device_private").members  # type: ignore[union-attr]
        )
        prog.cache["knode_class_in_device_private"] = class_in_private
    devices = prog["block_class"].p.klist_devices.k_list.address_of_()
    if class_in_private:
        for device_private in list_for_each_entry("struct device_private",
                                                  devices,
                                                  "knode_class.n_node"):
            yield device_private.device
    else:
        yield from list_for_each_entry("struct device", devices,
                                       "knode_class.n_node")
Beispiel #14
0
def load_debug_info(prog: drgn.Program, dpaths: [str]) -> None:
    """
    Iterates over all the paths provided (`dpaths`) and attempts
    to load any debug information it finds. If the path provided
    is a directory, the whole directory is traversed in search
    of debug info.
    """
    for path in dpaths:
        if os.path.isfile(path):
            prog.load_debug_info([path])
        elif os.path.isdir(path):
            kos = []
            for (ppath, __, files) in os.walk(path):
                for i in files:
                    if i.endswith(".ko"):
                        kos.append(os.sep.join([ppath, i]))
            prog.load_debug_info(kos)
        else:
            print("sdb: " + path + " is not a regular file or directory")
Beispiel #15
0
 def test_zero_fill(self):
     data = b"hello, world"
     prog = Program()
     with tempfile.NamedTemporaryFile() as f:
         f.write(
             create_elf_file(
                 ET.CORE,
                 [
                     ElfSection(
                         p_type=PT.LOAD,
                         vaddr=0xFFFF0000,
                         data=data,
                         memsz=len(data) + 4,
                     ),
                 ],
             ))
         f.flush()
         prog.set_core_dump(f.name)
     self.assertEqual(prog.read(0xFFFF0000, len(data) + 4), data + bytes(4))
Beispiel #16
0
    def test_decode_enum_type_flags(self):
        prog = Program(MOCK_PLATFORM)

        for bit_numbers, flags in (
            (True, self.FLAGS_BIT_NUMBERS),
            (False, self.FLAGS_VALUES),
        ):
            with self.subTest(bit_numbers=bit_numbers):
                type = prog.enum_type(
                    None,
                    prog.int_type("int", 4, True),
                    [TypeEnumerator(*flag) for flag in flags],
                )
                self.assertEqual(
                    decode_enum_type_flags(4, type, bit_numbers), "UNDERLINE"
                )
                self.assertEqual(
                    decode_enum_type_flags(27, type, bit_numbers), "BOLD|ITALIC|0x18"
                )
Beispiel #17
0
    def print_metaslab(prog: drgn.Program, msp, print_header, indent):
        spacemap = msp.ms_sm

        if print_header:
            print(
                "".ljust(indent),
                "ADDR".ljust(18),
                "ID".rjust(4),
                "OFFSET".rjust(16),
                "FREE".rjust(8),
                "FRAG".rjust(5),
                "UCMU".rjust(8),
            )
            print("".ljust(indent), "-" * 65)

        free = msp.ms_size
        if spacemap != drgn.NULL(prog, spacemap.type_):
            free -= spacemap.sm_phys.smp_alloc

        ufrees = msp.ms_unflushed_frees.rt_space
        uallocs = msp.ms_unflushed_allocs.rt_space
        free = free + ufrees - uallocs

        uchanges_free_mem = msp.ms_unflushed_frees.rt_root.avl_numnodes
        uchanges_free_mem *= prog.type("range_seg_t").type.size
        uchanges_alloc_mem = msp.ms_unflushed_allocs.rt_root.avl_numnodes
        uchanges_alloc_mem *= prog.type("range_seg_t").type.size
        uchanges_mem = uchanges_free_mem + uchanges_alloc_mem

        print(
            "".ljust(indent),
            hex(msp).ljust(16),
            str(int(msp.ms_id)).rjust(4),
            hex(msp.ms_start).rjust(16),
            nicenum(free).rjust(8),
            end="",
        )
        if msp.ms_fragmentation == -1:
            print("-".rjust(6), end="")
        else:
            print((str(msp.ms_fragmentation) + "%").rjust(6), end="")
        print(nicenum(uchanges_mem).rjust(9))
Beispiel #18
0
 def test_overlap_within_segment(self):
     # Existing segment: |_______|
     # New segment:        |___|
     prog = Program()
     segment1 = unittest.mock.Mock(side_effect=zero_memory_read)
     segment2 = unittest.mock.Mock(side_effect=zero_memory_read)
     prog.add_memory_segment(0xffff0000, 128, segment1)
     prog.add_memory_segment(0xffff0020, 64, segment2)
     prog.read(0xffff0000, 128)
     segment1.assert_has_calls([
         unittest.mock.call(0xffff0000, 32, 00, False),
         unittest.mock.call(0xffff0060, 32, 96, False),
     ])
     segment2.assert_called_once_with(0xffff0020, 64, 0, False)
Beispiel #19
0
 def test_overlap_segment_head_and_tail(self):
     # Existing segment: |_______||_______|
     # New segment:          |_______|
     prog = Program()
     segment1 = unittest.mock.Mock(side_effect=zero_memory_read)
     segment2 = unittest.mock.Mock(side_effect=zero_memory_read)
     segment3 = unittest.mock.Mock(side_effect=zero_memory_read)
     prog.add_memory_segment(0xffff0000, 128, segment1)
     prog.add_memory_segment(0xffff0080, 128, segment2)
     prog.add_memory_segment(0xffff0040, 128, segment3)
     prog.read(0xffff0000, 256)
     segment1.assert_called_once_with(0xffff0000, 64, 0, False)
     segment2.assert_called_once_with(0xffff00c0, 64, 64, False)
     segment3.assert_called_once_with(0xffff0040, 128, 0, False)
Beispiel #20
0
 def _test_module_debug_info(self, use_proc_and_sys):
     old_use_proc_and_sys = (int(
         os.environ.get("DRGN_USE_PROC_AND_SYS_MODULES", "1")) != 0)
     with setenv("DRGN_USE_PROC_AND_SYS_MODULES",
                 "1" if use_proc_and_sys else "0"):
         if old_use_proc_and_sys == use_proc_and_sys:
             prog = self.prog
         else:
             prog = Program()
             prog.set_kernel()
             prog.load_default_debug_info()
         self.assertEqual(
             prog.symbol(self.SYMBOL).address, self.symbol_address)
Beispiel #21
0
    def test_pid_memory(self):
        data = b"hello, world!"
        buf = ctypes.create_string_buffer(data)
        address = ctypes.addressof(buf)

        # QEMU user-mode emulation doesn't seem to emulate /proc/$pid/mem
        # correctly on a 64-bit host with a 32-bit guest; see
        # https://gitlab.com/qemu-project/qemu/-/issues/698. Packit uses mock
        # to cross-compile and test packages, which in turn uses QEMU user-mode
        # emulation. Skip this test if /proc/$pid/mem doesn't work so that
        # those builds succeed.
        try:
            with open("/proc/self/mem", "rb") as f:
                f.seek(address)
                functional_proc_pid_mem = f.read(len(data)) == data
        except OSError:
            functional_proc_pid_mem = False
        if not functional_proc_pid_mem:
            self.skipTest("/proc/$pid/mem is not functional")

        prog = Program()
        prog.set_pid(os.getpid())

        self.assertEqual(prog.read(ctypes.addressof(buf), len(data)), data)
Beispiel #22
0
 def _test_by_pid(self, orc):
     old_orc = int(os.environ.get("DRGN_PREFER_ORC_UNWINDER", "0")) != 0
     with setenv("DRGN_PREFER_ORC_UNWINDER", "1" if orc else "0"):
         if orc == old_orc:
             prog = self.prog
         else:
             prog = Program()
             prog.set_kernel()
             prog.load_default_debug_info()
         pid = fork_and_pause()
         wait_until(lambda: proc_state(pid) == "S")
         self.assertIn("pause", str(prog.stack_trace(pid)))
         os.kill(pid, signal.SIGKILL)
         os.waitpid(pid, 0)
Beispiel #23
0
 def setUpClass(cls):
     super().setUpClass()
     with tempfile.NamedTemporaryFile() as core_dump_file:
         try:
             subprocess.check_call(
                 [
                     "zstd",
                     "--quiet",
                     "--force",
                     "--decompress",
                     "--stdout",
                     os.path.join(os.path.dirname(__file__),
                                  "sample.coredump.zst"),
                 ],
                 stdout=core_dump_file,
             )
         except FileNotFoundError:
             raise unittest.SkipTest("zstd not found")
         cls.prog = Program()
         cls.prog.set_core_dump(core_dump_file.name)
Beispiel #24
0
 def test_physical(self):
     data = b'hello, world'
     prog = Program()
     with tempfile.NamedTemporaryFile() as f:
         f.write(
             create_elf_file(ET.CORE, [
                 ElfSection(
                     p_type=PT.LOAD,
                     vaddr=0xffff0000,
                     paddr=0xa0,
                     data=data,
                 ),
             ]))
         f.flush()
         prog.set_core_dump(f.name)
     self.assertEqual(prog.read(0xffff0000, len(data)), data)
     self.assertEqual(prog.read(0xa0, len(data), physical=True), data)
Beispiel #25
0
def mock_program(platform=MOCK_PLATFORM,
                 *,
                 segments=None,
                 types=None,
                 objects=None):
    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

    def mock_object_find(prog, name, flags, filename):
        if filename:
            return None
        for obj in objects:
            if obj.name == name:
                if obj.value is not None:
                    if flags & FindObjectFlags.CONSTANT:
                        break
                elif obj.type.kind == TypeKind.FUNCTION:
                    if flags & FindObjectFlags.FUNCTION:
                        break
                elif flags & FindObjectFlags.VARIABLE:
                    break
        else:
            return None
        return Object(prog, obj.type, address=obj.address, value=obj.value)

    prog = Program(platform)
    if segments is not None:
        add_mock_memory_segments(prog, segments)
    if types is not None:
        prog.add_type_finder(mock_find_type)
    if objects is not None:
        prog.add_object_finder(mock_object_find)
    return prog
Beispiel #26
0
 def test_unsaved(self):
     data = b"hello, world"
     prog = Program()
     with tempfile.NamedTemporaryFile() as f:
         f.write(
             create_elf_file(
                 ET.CORE,
                 [
                     ElfSection(
                         p_type=PT.LOAD,
                         vaddr=0xFFFF0000,
                         data=data,
                         memsz=len(data) + 4,
                     ),
                 ],
             )
         )
         f.flush()
         prog.set_core_dump(f.name)
     with self.assertRaisesRegex(FaultError, "memory not saved in core dump") as cm:
         prog.read(0xFFFF0000, len(data) + 4)
     self.assertEqual(cm.exception.address, 0xFFFF000C)
Beispiel #27
0
 def test_not_elf(self):
     prog = Program()
     self.assertRaisesRegex(FileFormatError, 'not an ELF file',
                            prog.set_core_dump, '/dev/null')
Beispiel #28
0
 def test_debug_info(self):
     Program().load_debug_info([])
Beispiel #29
0
 def finder(kind, name, filename):
     if kind == TypeKind.TYPEDEF and name == "foo":
         prog = Program()
         return prog.typedef_type("foo", prog.void_type())
     else:
         return None
Beispiel #30
0
def _get_printk_records_lockless(prog: Program,
                                 prb: Object) -> List[PrintkRecord]:
    ulong_size = sizeof(prog.type("unsigned long"))
    DESC_SV_BITS = ulong_size * 8
    DESC_FLAGS_SHIFT = DESC_SV_BITS - 2
    DESC_FLAGS_MASK = 3 << DESC_FLAGS_SHIFT
    DESC_ID_MASK = DESC_FLAGS_MASK ^ ((1 << DESC_SV_BITS) - 1)

    LOG_CONT = prog["LOG_CONT"].value_()
    desc_committed = prog["desc_committed"].value_()
    desc_finalized = prog["desc_finalized"].value_()

    def record_committed(current_id: int, state_var: int) -> bool:
        state_desc_id = state_var & DESC_ID_MASK
        state = 3 & (state_var >> DESC_FLAGS_SHIFT)
        return (current_id == state_desc_id) and (state == desc_committed
                                                  or state == desc_finalized)

    desc_ring = prb.desc_ring
    descs = desc_ring.descs.read_()
    infos = desc_ring.infos.read_()
    desc_ring_mask = (1 << desc_ring.count_bits.value_()) - 1
    text_data_ring = prb.text_data_ring
    text_data_ring_data = text_data_ring.data.read_()
    text_data_ring_mask = (1 << text_data_ring.size_bits) - 1

    result = []

    def add_record(current_id: int) -> None:
        idx = current_id & desc_ring_mask
        desc = descs[idx].read_()
        if not record_committed(current_id, desc.state_var.counter.value_()):
            return

        lpos_begin = desc.text_blk_lpos.begin & text_data_ring_mask
        lpos_next = desc.text_blk_lpos.next & text_data_ring_mask
        lpos_begin += ulong_size

        if lpos_begin == lpos_next:
            # Data-less record.
            return
        if lpos_begin > lpos_next:
            # Data wrapped.
            lpos_begin -= lpos_begin
        info = infos[idx].read_()
        text_len = info.text_len
        if lpos_next - lpos_begin < text_len:
            # Truncated record.
            text_len = lpos_next - lpos_begin

        caller_tid, caller_cpu = _caller_id(info.caller_id.value_())

        context = {}
        subsystem = info.dev_info.subsystem.string_()
        device = info.dev_info.device.string_()
        if subsystem:
            context[b"SUBSYSTEM"] = subsystem
        if device:
            context[b"DEVICE"] = device

        result.append(
            PrintkRecord(
                text=prog.read(text_data_ring_data + lpos_begin, text_len),
                facility=info.facility.value_(),
                level=info.level.value_(),
                seq=info.seq.value_(),
                timestamp=info.ts_nsec.value_(),
                caller_tid=caller_tid,
                caller_cpu=caller_cpu,
                continuation=bool(info.flags.value_() & LOG_CONT),
                context=context,
            ))

    head_id = desc_ring.head_id.counter.value_()
    current_id = desc_ring.tail_id.counter.value_()
    while current_id != head_id:
        add_record(current_id)
        current_id = (current_id + 1) & DESC_ID_MASK
    add_record(current_id)
    return result