# DExTer : Debugging Experience Tester
# ~~~~~~   ~         ~~         ~   ~~
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

from collections import namedtuple
from ctypes import *
from enum import *
from functools import reduce, partial

from .symgroup import SymbolGroup, IDebugSymbolGroup2
from .utils import *


class SymbolOptionFlags(IntFlag):
    SYMOPT_CASE_INSENSITIVE = 0x00000001
    SYMOPT_UNDNAME = 0x00000002
    SYMOPT_DEFERRED_LOADS = 0x00000004
    SYMOPT_NO_CPP = 0x00000008
    SYMOPT_LOAD_LINES = 0x00000010
    SYMOPT_OMAP_FIND_NEAREST = 0x00000020
    SYMOPT_LOAD_ANYTHING = 0x00000040
    SYMOPT_IGNORE_CVREC = 0x00000080
    SYMOPT_NO_UNQUALIFIED_LOADS = 0x00000100
    SYMOPT_FAIL_CRITICAL_ERRORS = 0x00000200
    SYMOPT_EXACT_SYMBOLS = 0x00000400
    SYMOPT_ALLOW_ABSOLUTE_SYMBOLS = 0x00000800
    SYMOPT_IGNORE_NT_SYMPATH = 0x00001000
    SYMOPT_INCLUDE_32BIT_MODULES = 0x00002000
    SYMOPT_PUBLICS_ONLY = 0x00004000
    SYMOPT_NO_PUBLICS = 0x00008000
    SYMOPT_AUTO_PUBLICS = 0x00010000
    SYMOPT_NO_IMAGE_SEARCH = 0x00020000
    SYMOPT_SECURE = 0x00040000
    SYMOPT_NO_PROMPTS = 0x00080000
    SYMOPT_DEBUG = 0x80000000


class ScopeGroupFlags(IntFlag):
    DEBUG_SCOPE_GROUP_ARGUMENTS = 0x00000001
    DEBUG_SCOPE_GROUP_LOCALS = 0x00000002
    DEBUG_SCOPE_GROUP_ALL = 0x00000003
    DEBUG_SCOPE_GROUP_BY_DATAMODEL = 0x00000004


class DebugModuleNames(IntEnum):
    DEBUG_MODNAME_IMAGE = 0x00000000
    DEBUG_MODNAME_MODULE = 0x00000001
    DEBUG_MODNAME_LOADED_IMAGE = 0x00000002
    DEBUG_MODNAME_SYMBOL_FILE = 0x00000003
    DEBUG_MODNAME_MAPPED_IMAGE = 0x00000004


class DebugModuleFlags(IntFlag):
    DEBUG_MODULE_LOADED = 0x00000000
    DEBUG_MODULE_UNLOADED = 0x00000001
    DEBUG_MODULE_USER_MODE = 0x00000002
    DEBUG_MODULE_EXE_MODULE = 0x00000004
    DEBUG_MODULE_EXPLICIT = 0x00000008
    DEBUG_MODULE_SECONDARY = 0x00000010
    DEBUG_MODULE_SYNTHETIC = 0x00000020
    DEBUG_MODULE_SYM_BAD_CHECKSUM = 0x00010000


class DEBUG_MODULE_PARAMETERS(Structure):
    _fields_ = [
        ("Base", c_ulonglong),
        ("Size", c_ulong),
        ("TimeDateStamp", c_ulong),
        ("Checksum", c_ulong),
        ("Flags", c_ulong),
        ("SymbolType", c_ulong),
        ("ImageNameSize", c_ulong),
        ("ModuleNameSize", c_ulong),
        ("LoadedImageNameSize", c_ulong),
        ("SymbolFileNameSize", c_ulong),
        ("MappedImageNameSize", c_ulong),
        ("Reserved", c_ulonglong * 2),
    ]


PDEBUG_MODULE_PARAMETERS = POINTER(DEBUG_MODULE_PARAMETERS)


class DEBUG_MODULE_AND_ID(Structure):
    _fields_ = [("ModuleBase", c_ulonglong), ("Id", c_ulonglong)]


PDEBUG_MODULE_AND_ID = POINTER(DEBUG_MODULE_AND_ID)


class DEBUG_SYMBOL_ENTRY(Structure):
    _fields_ = [
        ("ModuleBase", c_ulonglong),
        ("Offset", c_ulonglong),
        ("Id", c_ulonglong),
        ("Arg64", c_ulonglong),
        ("Size", c_ulong),
        ("Flags", c_ulong),
        ("TypeId", c_ulong),
        ("NameSize", c_ulong),
        ("Token", c_ulong),
        ("Tag", c_ulong),
        ("Arg32", c_ulong),
        ("Reserved", c_ulong),
    ]


PDEBUG_SYMBOL_ENTRY = POINTER(DEBUG_SYMBOL_ENTRY)

# UUID for DebugSymbols5 interface.
DebugSymbols5IID = IID(
    0xC65FA83E,
    0x1E69,
    0x475E,
    IID_Data4_Type(0x8E, 0x0E, 0xB5, 0xD7, 0x9E, 0x9C, 0xC1, 0x7E),
)


class IDebugSymbols5(Structure):
    pass


class IDebugSymbols5Vtbl(Structure):
    wrp = partial(WINFUNCTYPE, c_long, POINTER(IDebugSymbols5))
    ids_getsymboloptions = wrp(c_ulong_p)
    ids_setsymboloptions = wrp(c_ulong)
    ids_getmoduleparameters = wrp(
        c_ulong, c_ulong64_p, c_ulong, PDEBUG_MODULE_PARAMETERS
    )
    ids_getmodulenamestring = wrp(
        c_ulong, c_ulong, c_ulonglong, c_char_p, c_ulong, c_ulong_p
    )
    ids_getoffsetbyname = wrp(c_char_p, c_ulong64_p)
    ids_getlinebyoffset = wrp(
        c_ulonglong, c_ulong_p, c_char_p, c_ulong, c_ulong_p, c_ulong64_p
    )
    ids_getsymbolentriesbyname = wrp(
        c_char_p, c_ulong, PDEBUG_MODULE_AND_ID, c_ulong, c_ulong_p
    )
    ids_getsymbolentrystring = wrp(
        PDEBUG_MODULE_AND_ID, c_ulong, c_char_p, c_ulong, c_ulong_p
    )
    ids_getsymbolentryinformation = wrp(PDEBUG_MODULE_AND_ID, PDEBUG_SYMBOL_ENTRY)
    ids_getcurrentscopeframeindex = wrp(c_ulong_p)
    ids_getnearnamebyoffset = wrp(
        c_ulonglong, c_long, c_char_p, c_ulong, c_ulong_p, c_ulong64_p
    )
    ids_setscopeframebyindex = wrp(c_ulong)
    ids_getscopesymbolgroup2 = wrp(
        c_ulong, POINTER(IDebugSymbolGroup2), POINTER(POINTER(IDebugSymbolGroup2))
    )
    ids_getnamebyinlinecontext = wrp(
        c_ulonglong, c_ulong, c_char_p, c_ulong, c_ulong_p, c_ulong64_p
    )
    ids_getlinebyinlinecontext = wrp(
        c_ulonglong, c_ulong, c_ulong_p, c_char_p, c_ulong, c_ulong_p, c_ulong64_p
    )
    _fields_ = [
        ("QueryInterface", c_void_p),
        ("AddRef", c_void_p),
        ("Release", c_void_p),
        ("GetSymbolOptions", ids_getsymboloptions),
        ("AddSymbolOptions", c_void_p),
        ("RemoveSymbolOptions", c_void_p),
        ("SetSymbolOptions", ids_setsymboloptions),
        ("GetNameByOffset", c_void_p),
        ("GetOffsetByName", ids_getoffsetbyname),
        ("GetNearNameByOffset", ids_getnearnamebyoffset),
        ("GetLineByOffset", ids_getlinebyoffset),
        ("GetOffsetByLine", c_void_p),
        ("GetNumberModules", c_void_p),
        ("GetModuleByIndex", c_void_p),
        ("GetModuleByModuleName", c_void_p),
        ("GetModuleByOffset", c_void_p),
        ("GetModuleNames", c_void_p),
        ("GetModuleParameters", ids_getmoduleparameters),
        ("GetSymbolModule", c_void_p),
        ("GetTypeName", c_void_p),
        ("GetTypeId", c_void_p),
        ("GetTypeSize", c_void_p),
        ("GetFieldOffset", c_void_p),
        ("GetSymbolTypeId", c_void_p),
        ("GetOffsetTypeId", c_void_p),
        ("ReadTypedDataVirtual", c_void_p),
        ("WriteTypedDataVirtual", c_void_p),
        ("OutputTypedDataVirtual", c_void_p),
        ("ReadTypedDataPhysical", c_void_p),
        ("WriteTypedDataPhysical", c_void_p),
        ("OutputTypedDataPhysical", c_void_p),
        ("GetScope", c_void_p),
        ("SetScope", c_void_p),
        ("ResetScope", c_void_p),
        ("GetScopeSymbolGroup", c_void_p),
        ("CreateSymbolGroup", c_void_p),
        ("StartSymbolMatch", c_void_p),
        ("GetNextSymbolMatch", c_void_p),
        ("EndSymbolMatch", c_void_p),
        ("Reload", c_void_p),
        ("GetSymbolPath", c_void_p),
        ("SetSymbolPath", c_void_p),
        ("AppendSymbolPath", c_void_p),
        ("GetImagePath", c_void_p),
        ("SetImagePath", c_void_p),
        ("AppendImagePath", c_void_p),
        ("GetSourcePath", c_void_p),
        ("GetSourcePathElement", c_void_p),
        ("SetSourcePath", c_void_p),
        ("AppendSourcePath", c_void_p),
        ("FindSourceFile", c_void_p),
        ("GetSourceFileLineOffsets", c_void_p),
        ("GetModuleVersionInformation", c_void_p),
        ("GetModuleNameString", ids_getmodulenamestring),
        ("GetConstantName", c_void_p),
        ("GetFieldName", c_void_p),
        ("GetTypeOptions", c_void_p),
        ("AddTypeOptions", c_void_p),
        ("RemoveTypeOptions", c_void_p),
        ("SetTypeOptions", c_void_p),
        ("GetNameByOffsetWide", c_void_p),
        ("GetOffsetByNameWide", c_void_p),
        ("GetNearNameByOffsetWide", c_void_p),
        ("GetLineByOffsetWide", c_void_p),
        ("GetOffsetByLineWide", c_void_p),
        ("GetModuleByModuleNameWide", c_void_p),
        ("GetSymbolModuleWide", c_void_p),
        ("GetTypeNameWide", c_void_p),
        ("GetTypeIdWide", c_void_p),
        ("GetFieldOffsetWide", c_void_p),
        ("GetSymbolTypeIdWide", c_void_p),
        ("GetScopeSymbolGroup2", ids_getscopesymbolgroup2),
        ("CreateSymbolGroup2", c_void_p),
        ("StartSymbolMatchWide", c_void_p),
        ("GetNextSymbolMatchWide", c_void_p),
        ("ReloadWide", c_void_p),
        ("GetSymbolPathWide", c_void_p),
        ("SetSymbolPathWide", c_void_p),
        ("AppendSymbolPathWide", c_void_p),
        ("GetImagePathWide", c_void_p),
        ("SetImagePathWide", c_void_p),
        ("AppendImagePathWide", c_void_p),
        ("GetSourcePathWide", c_void_p),
        ("GetSourcePathElementWide", c_void_p),
        ("SetSourcePathWide", c_void_p),
        ("AppendSourcePathWide", c_void_p),
        ("FindSourceFileWide", c_void_p),
        ("GetSourceFileLineOffsetsWide", c_void_p),
        ("GetModuleVersionInformationWide", c_void_p),
        ("GetModuleNameStringWide", c_void_p),
        ("GetConstantNameWide", c_void_p),
        ("GetFieldNameWide", c_void_p),
        ("IsManagedModule", c_void_p),
        ("GetModuleByModuleName2", c_void_p),
        ("GetModuleByModuleName2Wide", c_void_p),
        ("GetModuleByOffset2", c_void_p),
        ("AddSyntheticModule", c_void_p),
        ("AddSyntheticModuleWide", c_void_p),
        ("RemoveSyntheticModule", c_void_p),
        ("GetCurrentScopeFrameIndex", ids_getcurrentscopeframeindex),
        ("SetScopeFrameByIndex", ids_setscopeframebyindex),
        ("SetScopeFromJitDebugInfo", c_void_p),
        ("SetScopeFromStoredEvent", c_void_p),
        ("OutputSymbolByOffset", c_void_p),
        ("GetFunctionEntryByOffset", c_void_p),
        ("GetFieldTypeAndOffset", c_void_p),
        ("GetFieldTypeAndOffsetWide", c_void_p),
        ("AddSyntheticSymbol", c_void_p),
        ("AddSyntheticSymbolWide", c_void_p),
        ("RemoveSyntheticSymbol", c_void_p),
        ("GetSymbolEntriesByOffset", c_void_p),
        ("GetSymbolEntriesByName", ids_getsymbolentriesbyname),
        ("GetSymbolEntriesByNameWide", c_void_p),
        ("GetSymbolEntryByToken", c_void_p),
        ("GetSymbolEntryInformation", ids_getsymbolentryinformation),
        ("GetSymbolEntryString", ids_getsymbolentrystring),
        ("GetSymbolEntryStringWide", c_void_p),
        ("GetSymbolEntryOffsetRegions", c_void_p),
        ("GetSymbolEntryBySymbolEntry", c_void_p),
        ("GetSourceEntriesByOffset", c_void_p),
        ("GetSourceEntriesByLine", c_void_p),
        ("GetSourceEntriesByLineWide", c_void_p),
        ("GetSourceEntryString", c_void_p),
        ("GetSourceEntryStringWide", c_void_p),
        ("GetSourceEntryOffsetRegions", c_void_p),
        ("GetsourceEntryBySourceEntry", c_void_p),
        ("GetScopeEx", c_void_p),
        ("SetScopeEx", c_void_p),
        ("GetNameByInlineContext", ids_getnamebyinlinecontext),
        ("GetNameByInlineContextWide", c_void_p),
        ("GetLineByInlineContext", ids_getlinebyinlinecontext),
        ("GetLineByInlineContextWide", c_void_p),
        ("OutputSymbolByInlineContext", c_void_p),
        ("GetCurrentScopeFrameIndexEx", c_void_p),
        ("SetScopeFrameByIndexEx", c_void_p),
    ]


IDebugSymbols5._fields_ = [("lpVtbl", POINTER(IDebugSymbols5Vtbl))]

SymbolId = namedtuple("SymbolId", ["ModuleBase", "Id"])
SymbolEntry = namedtuple(
    "SymbolEntry",
    [
        "ModuleBase",
        "Offset",
        "Id",
        "Arg64",
        "Size",
        "Flags",
        "TypeId",
        "NameSize",
        "Token",
        "Tag",
        "Arg32",
    ],
)
DebugModuleParams = namedtuple(
    "DebugModuleParams",
    [
        "Base",
        "Size",
        "TimeDateStamp",
        "Checksum",
        "Flags",
        "SymbolType",
        "ImageNameSize",
        "ModuleNameSize",
        "LoadedImageNameSize",
        "SymbolFileNameSize",
        "MappedImageNameSize",
    ],
)


class SymTags(IntEnum):
    Null = 0
    Exe = 1
    SymTagFunction = 5


def make_debug_module_params(cdata):
    fieldvalues = map(lambda y: getattr(cdata, y), DebugModuleParams._fields)
    return DebugModuleParams(*fieldvalues)


class Symbols(object):
    def __init__(self, symbols):
        self.ptr = symbols
        self.symbols = symbols.contents
        self.vt = self.symbols.lpVtbl.contents
        # Keep some handy ulongs for passing into C methods.
        self.ulong = c_ulong()
        self.ulong64 = c_ulonglong()

    def GetCurrentScopeFrameIndex(self):
        res = self.vt.GetCurrentScopeFrameIndex(self.symbols, byref(self.ulong))
        aborter(res, "GetCurrentScopeFrameIndex")
        return self.ulong.value

    def SetScopeFrameByIndex(self, idx):
        res = self.vt.SetScopeFrameByIndex(self.symbols, idx)
        aborter(res, "SetScopeFrameByIndex", ignore=[E_EINVAL])
        return res != E_EINVAL

    def GetOffsetByName(self, name):
        res = self.vt.GetOffsetByName(
            self.symbols, name.encode("ascii"), byref(self.ulong64)
        )
        aborter(res, "GetOffsetByName {}".format(name))
        return self.ulong64.value

    def GetNearNameByOffset(self, addr):
        ptr = create_string_buffer(256)
        pulong = c_ulong()
        disp = c_ulonglong()
        # Zero arg -> "delta" indicating how many symbols to skip
        res = self.vt.GetNearNameByOffset(
            self.symbols, addr, 0, ptr, 255, byref(pulong), byref(disp)
        )
        if res == E_NOINTERFACE:
            return "{noname}"
        aborter(res, "GetNearNameByOffset")
        ptr[255] = "\0".encode("ascii")
        return "{}+{}".format(string_at(ptr).decode("ascii"), disp.value)

    def GetModuleByModuleName2(self, name):
        # First zero arg -> module index to search from, second zero arg ->
        # DEBUG_GETMOD_* flags, none of which we use.
        res = self.vt.GetModuleByModuleName2(
            self.symbols, name, 0, 0, None, byref(self.ulong64)
        )
        aborter(res, "GetModuleByModuleName2")
        return self.ulong64.value

    def GetScopeSymbolGroup2(self):
        retptr = POINTER(IDebugSymbolGroup2)()
        res = self.vt.GetScopeSymbolGroup2(
            self.symbols, ScopeGroupFlags.DEBUG_SCOPE_GROUP_ALL, None, retptr
        )
        aborter(res, "GetScopeSymbolGroup2")
        return SymbolGroup(retptr)

    def GetSymbolEntryString(self, idx, module):
        symid = DEBUG_MODULE_AND_ID()
        symid.ModuleBase = module
        symid.Id = idx
        ptr = create_string_buffer(1024)
        # Zero arg is the string index -- symbols can have multiple names, for now
        # only support the first one.
        res = self.vt.GetSymbolEntryString(
            self.symbols, symid, 0, ptr, 1023, byref(self.ulong)
        )
        aborter(res, "GetSymbolEntryString")
        return string_at(ptr).decode("ascii")

    def GetSymbolEntryInformation(self, module, theid):
        symid = DEBUG_MODULE_AND_ID()
        symentry = DEBUG_SYMBOL_ENTRY()
        symid.ModuleBase = module
        symid.Id = theid
        res = self.vt.GetSymbolEntryInformation(self.symbols, symid, symentry)
        aborter(res, "GetSymbolEntryInformation")
        # Fetch fields into SymbolEntry object
        fields = map(lambda x: getattr(symentry, x), SymbolEntry._fields)
        return SymbolEntry(*fields)

    def GetSymbolEntriesByName(self, symstr):
        # Initial query to find number of symbol entries
        res = self.vt.GetSymbolEntriesByName(
            self.symbols, symstr.encode("ascii"), 0, None, 0, byref(self.ulong)
        )
        aborter(res, "GetSymbolEntriesByName")

        # Build a buffer and query for 'length' entries
        length = self.ulong.value
        symrecs = (DEBUG_MODULE_AND_ID * length)()
        # Zero arg -> flags, of which there are none defined.
        res = self.vt.GetSymbolEntriesByName(
            self.symbols, symstr.encode("ascii"), 0, symrecs, length, byref(self.ulong)
        )
        aborter(res, "GetSymbolEntriesByName")

        # Extract 'length' number of SymbolIds
        length = self.ulong.value

        def extract(x):
            sym = symrecs[x]
            return SymbolId(sym.ModuleBase, sym.Id)

        return [extract(x) for x in range(length)]

    def GetSymbolPath(self):
        # Query for length of buffer to allocate
        res = self.vt.GetSymbolPath(self.symbols, None, 0, byref(self.ulong))
        aborter(res, "GetSymbolPath", ignore=[S_FALSE])

        # Fetch 'length' length symbol path string
        length = self.ulong.value
        arr = create_string_buffer(length)
        res = self.vt.GetSymbolPath(self.symbols, arr, length, byref(self.ulong))
        aborter(res, "GetSymbolPath")

        return string_at(arr).decode("ascii")

    def GetSourcePath(self):
        # Query for length of buffer to allocate
        res = self.vt.GetSourcePath(self.symbols, None, 0, byref(self.ulong))
        aborter(res, "GetSourcePath", ignore=[S_FALSE])

        # Fetch a string of len 'length'
        length = self.ulong.value
        arr = create_string_buffer(length)
        res = self.vt.GetSourcePath(self.symbols, arr, length, byref(self.ulong))
        aborter(res, "GetSourcePath")

        return string_at(arr).decode("ascii")

    def SetSourcePath(self, string):
        res = self.vt.SetSourcePath(self.symbols, string.encode("ascii"))
        aborter(res, "SetSourcePath")
        return

    def GetModuleParameters(self, base):
        self.ulong64.value = base
        params = DEBUG_MODULE_PARAMETERS()
        # Fetch one module params struct, starting at idx zero
        res = self.vt.GetModuleParameters(
            self.symbols, 1, byref(self.ulong64), 0, byref(params)
        )
        aborter(res, "GetModuleParameters")
        return make_debug_module_params(params)

    def GetSymbolOptions(self):
        res = self.vt.GetSymbolOptions(self.symbols, byref(self.ulong))
        aborter(res, "GetSymbolOptions")
        return SymbolOptionFlags(self.ulong.value)

    def SetSymbolOptions(self, opts):
        assert isinstance(opts, SymbolOptionFlags)
        res = self.vt.SetSymbolOptions(self.symbols, opts.value)
        aborter(res, "SetSymbolOptions")
        return

    def GetLineByOffset(self, offs):
        # Initial query for filename buffer size
        res = self.vt.GetLineByOffset(
            self.symbols, offs, None, None, 0, byref(self.ulong), None
        )
        if res == E_FAIL:
            return None  # Sometimes we just can't get line numbers, of course
        aborter(res, "GetLineByOffset", ignore=[S_FALSE])

        # Allocate filename buffer and query for line number too
        filenamelen = self.ulong.value
        text = create_string_buffer(filenamelen)
        line = c_ulong()
        res = self.vt.GetLineByOffset(
            self.symbols, offs, byref(line), text, filenamelen, byref(self.ulong), None
        )
        aborter(res, "GetLineByOffset")

        return string_at(text).decode("ascii"), line.value

    def GetModuleNameString(self, whichname, base):
        # Initial query for name string length
        res = self.vt.GetModuleNameString(
            self.symbols, whichname, DEBUG_ANY_ID, base, None, 0, byref(self.ulong)
        )
        aborter(res, "GetModuleNameString", ignore=[S_FALSE])

        module_name_len = self.ulong.value
        module_name = (c_char * module_name_len)()
        res = self.vt.GetModuleNameString(
            self.symbols,
            whichname,
            DEBUG_ANY_ID,
            base,
            module_name,
            module_name_len,
            None,
        )
        aborter(res, "GetModuleNameString")

        return string_at(module_name).decode("ascii")

    def GetNameByInlineContext(self, pc, ctx):
        # None args -> ignore output name size and displacement
        buf = create_string_buffer(256)
        res = self.vt.GetNameByInlineContext(
            self.symbols, pc, ctx, buf, 255, None, None
        )
        aborter(res, "GetNameByInlineContext")
        return string_at(buf).decode("ascii")

    def GetLineByInlineContext(self, pc, ctx):
        # None args -> ignore output filename size and displacement
        buf = create_string_buffer(256)
        res = self.vt.GetLineByInlineContext(
            self.symbols, pc, ctx, byref(self.ulong), buf, 255, None, None
        )
        aborter(res, "GetLineByInlineContext")
        return string_at(buf).decode("ascii"), self.ulong.value

    def get_all_symbols(self):
        main_module_name = self.get_exefile_module_name()
        idnumbers = self.GetSymbolEntriesByName("{}!*".format(main_module_name))
        lst = []
        for symid in idnumbers:
            s = self.GetSymbolEntryString(symid.Id, symid.ModuleBase)
            symentry = self.GetSymbolEntryInformation(symid.ModuleBase, symid.Id)
            lst.append((s, symentry))
        return lst

    def get_all_functions(self):
        syms = self.get_all_symbols()
        return [x for x in syms if x[1].Tag == SymTags.SymTagFunction]

    def get_all_modules(self):
        params = DEBUG_MODULE_PARAMETERS()
        idx = 0
        res = 0
        all_modules = []
        while res != E_EINVAL:
            res = self.vt.GetModuleParameters(self.symbols, 1, None, idx, byref(params))
            aborter(res, "GetModuleParameters", ignore=[E_EINVAL])
            all_modules.append(make_debug_module_params(params))
            idx += 1
        return all_modules

    def get_exefile_module(self):
        all_modules = self.get_all_modules()
        reduce_func = (
            lambda x, y: y if y.Flags & DebugModuleFlags.DEBUG_MODULE_EXE_MODULE else x
        )
        main_module = reduce(reduce_func, all_modules, None)
        if main_module is None:
            raise Exception("Couldn't find the exefile module")
        return main_module

    def get_module_name(self, base):
        return self.GetModuleNameString(DebugModuleNames.DEBUG_MODNAME_MODULE, base)

    def get_exefile_module_name(self):
        return self.get_module_name(self.get_exefile_module().Base)
