Signed-off-by: Eric Pouech eric.pouech@gmail.com
--- dlls/dbghelp/tests/dbghelp.c | 318 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 317 insertions(+), 1 deletion(-)
diff --git a/dlls/dbghelp/tests/dbghelp.c b/dlls/dbghelp/tests/dbghelp.c index be3418b9ff5..b43acf2dda9 100644 --- a/dlls/dbghelp/tests/dbghelp.c +++ b/dlls/dbghelp/tests/dbghelp.c @@ -16,9 +16,14 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <stdarg.h> + #include "windef.h" +#include "winbase.h" +#include "winnls.h" #include "verrsrc.h" #include "dbghelp.h" +#include "cvconst.h" #include "wine/test.h"
#define DEBUGGEE_NODEFINE @@ -147,6 +152,317 @@ static void test_stack_walk(const struct debuggee* dbg)
#endif /* __i386__ || __x86_64__ */
+static BOOL endswithW(const WCHAR* in, const WCHAR* with) +{ + SIZE_T inlen = lstrlenW(in); + SIZE_T withlen = lstrlenW(with); + + if (inlen < withlen) return FALSE; + return _wcsnicmp(in + inlen - withlen, with, withlen) == 0; +} + +static BOOL isobjfile(const char* str, WCHAR* name) +{ + DWORD sz = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); + WCHAR* nameW; + BOOL ret = FALSE; + if ((nameW = HeapAlloc(GetProcessHeap(), 0, (sz + 4) * sizeof(WCHAR))) != NULL) + { + MultiByteToWideChar(CP_ACP, 0, str, -1, nameW, sz); + nameW[sz - 1] = '.'; + nameW[sz + 0] = 'o'; + nameW[sz + 1] = '\0'; + ret = endswithW(name, nameW); + if (!ret) + { + nameW[sz + 1] = 'b'; + nameW[sz + 2] = 'j'; + nameW[sz + 3] = '\0'; + ret = endswithW(name, nameW); + } + HeapFree(GetProcessHeap(), 0, nameW); + } + return ret; +} + +/* When looking at a typedef (like DWORD), compilers & debug formats differ: + * - some use the type of the typedef + * - some others use the target of the typedef (especially, when it's a well defined + * type for the debug format) + * Cope with this by removing all typedef:s from the debug info to get to the + * target type. + */ +static DWORD untypedef(const struct debuggee* dbg, DWORD type) +{ + DWORD tag; + BOOL ret = TRUE; + + while (ret && + SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_SYMTAG, &tag) && + tag == SymTagTypedef) + { + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_TYPE, &type); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + } + return type; +} + +static TI_FINDCHILDREN_PARAMS* retrieve_children(const struct debuggee* dbg, DWORD id) +{ + DWORD count; + BOOL ret; + + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, id, TI_GET_CHILDRENCOUNT, &count); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + + if (ret) + { + TI_FINDCHILDREN_PARAMS* tfp; + + tfp = HeapAlloc(GetProcessHeap(), 0, sizeof(*tfp) + count * sizeof(tfp->ChildId[0])); + if (tfp) + { + tfp->Count = count; + tfp->Start = 0; + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, id, TI_FINDCHILDREN, tfp); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + if (ret) return tfp; + HeapFree(GetProcessHeap(), 0, tfp); + } + } + return NULL; +} + +static void test_symbols(const struct debuggee* dbg) +{ + /* we inspect (with dbghelp) all those local variables & function parameters + * so check out the code below when changing those variables + */ + char si_buf[sizeof(SYMBOL_INFO) + 200]; + SYMBOL_INFO* si = (SYMBOL_INFO*)si_buf; + BOOL ret; + DWORD type, tag, bt, kind; + ULONG64 len; + WCHAR* name; + TI_FINDCHILDREN_PARAMS* tfp; + + memset(si, 0, sizeof(*si)); + si->SizeOfStruct = sizeof(SYMBOL_INFO); + si->MaxNameLen = 200; + + ret = SymFromName(dbg->hProcess, "function_AA", si); + ok(ret, "SymFromName failed: %u\n", GetLastError()); + ok(si->NameLen == 11, "Wrong name len %u\n", si->NameLen); + ok(!strcmp(si->Name, "function_AA"), "Wrong name %s\n", si->Name); + ok(si->Size, "Should have a size\n"); + ok(si->ModBase == dbg->base, "Wrong module base: expecting 0x%s but got 0x%s\n", + wine_dbgstr_longlong(dbg->base), wine_dbgstr_longlong(si->ModBase)); + // FIXME on W10, we get 0 in Flags... + ok(si->Tag == SymTagFunction, "Wrong tag %u\n", si->Tag); + + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, si->Index, TI_GET_TYPEID, &type); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(type == si->TypeIndex, "wrong typeid %u (expecting %u)\n", type, si->TypeIndex); + + /* checking return type in signature */ + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, si->TypeIndex, TI_GET_TYPEID, &type); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_SYMTAG, &tag); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(tag == SymTagBaseType, "wrong tag %u\n", tag); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_SYMNAME, &name); + todo_wine + ok(!ret, "SymGetTypeInfo should fail\n"); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_BASETYPE, &bt); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(bt == btUInt, "Wrong base-type %u\n", bt); + + /* checking return parameters's type in signature */ + tfp = retrieve_children(dbg, si->TypeIndex); + if (tfp) + { + ok(tfp->Count == 2, "Wrong number of parameters' type\n"); + + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, tfp->ChildId[0], TI_GET_SYMTAG, &tag); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(tag == SymTagFunctionArgType, "wrong tag %u\n", tag); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, tfp->ChildId[0], TI_GET_TYPE, &type); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_SYMNAME, &name); + ok(!ret, "SymGetTypeInfo should fail\n"); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_SYMTAG, &tag); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(tag == SymTagPointerType, "Wrong base-type %u\n", bt); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_TYPE, &type); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_SYMTAG, &tag); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(tag == SymTagUDT, "wrong tag %u\n", tag); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_SYMNAME, &name); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + if (ret) + { + ok(!lstrcmpW(name, L"foobar"), "UDT name is %ls\n", name); + HeapFree(GetProcessHeap(), 0, name); + } + + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, tfp->ChildId[1], TI_GET_SYMTAG, &tag); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(tag == SymTagFunctionArgType, "wrong tag %u\n", tag); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, tfp->ChildId[1], TI_GET_TYPE, &type); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + type = untypedef(dbg, type); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_SYMNAME, &name); + todo_wine + ok(!ret, "SymGetTypeInfo should fail\n"); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_BASETYPE, &bt); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(bt == btUInt, "Wrong base-type %u\n", bt); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_LENGTH, &len); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(len == sizeof(DWORD64), "Wrong length %su (expecting %u)\n", + wine_dbgstr_longlong(len), sizeof(DWORD64)); + + HeapFree(GetProcessHeap(), 0, tfp); + } + + /* Checking children (params, local variables) */ + tfp = retrieve_children(dbg, si->Index); + if (tfp) + { + DWORD i, j; + static struct + { + const WCHAR* name; + SIZE_T size; + enum DataKind kind; + enum DataKind other_kind; /* appears with CL.EXE /O2 (but not with CL.EXE; maybe a bug?) */ + } locpmts[] = + { + // we assume we have same size on this exec and children process + {L"base", sizeof(DWORD64), DataIsParam, DataIsLocal}, + {L"other", sizeof(DWORD), DataIsLocal, DataIsLocal}, + }; + BOOL labelfound = FALSE; + + /* 4 made of: 2 parameters, one local, one label */ + todo_wine_if(sizeof(void*)==4) + ok(tfp->Count >= 4, "Wrong number of parameters+local variables' type %u\n", tfp->Count); + + /* there are other children than parameters / local variables, so need to lookup */ + for (j = 0; j < ARRAYSIZE(locpmts); ++j) + { + BOOL found = FALSE; + for (i = 0; i < tfp->Count; ++i) + { + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, tfp->ChildId[i], TI_GET_SYMTAG, &tag); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + switch (tag) + { + case SymTagData: /* the parameters and local variables */ + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, tfp->ChildId[i], TI_GET_SYMNAME, &name); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + if (ret) + { + if (!lstrcmpW(name, locpmts[j].name)) + { + found = TRUE; + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, tfp->ChildId[i], TI_GET_TYPE, &type); + ok(ret, "SymGetTypeInfo in #%u failed: %u\n", j, GetLastError()); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_LENGTH, &len); + ok(ret, "SymGetTypeInfo in #%u failed: %u\n", j, GetLastError()); + ok(len == locpmts[j].size, "Local variable/parameter %ls's size is wrong 0x%s (expecting 0x%s)\n", + name, wine_dbgstr_longlong(len), wine_dbgstr_longlong(locpmts[j].size)); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, tfp->ChildId[i], TI_GET_OFFSET, &len); + ok(ret, "SymGetTypeInfo in #%u failed: %u\n", j, GetLastError()); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, tfp->ChildId[i], TI_GET_DATAKIND, &kind); + ok(ret, "SymGetTypeInfo in #%u failed: %u\n", j, GetLastError()); + ok((enum DataKind)kind == locpmts[j].kind || (enum DataKind)kind == locpmts[j].other_kind, + "Local variable/parameter %ls's kind is wrong %u (expecting %u)\n", name, kind, locpmts[j].kind); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, tfp->ChildId[i], TI_GET_LEXICALPARENT, &type); + ok(ret, "SymGetTypeInfo in #%u failed: %u\n", j, GetLastError()); + ok(type == si->Index, "wrong lexical parent %u\n", type); + } + HeapFree(GetProcessHeap(), 0, name); + } + break; + case SymTagLabel: /* labels inside the function */ + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, tfp->ChildId[i], TI_GET_SYMNAME, &name); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + if (ret) + { + labelfound |= endswithW(name, L"scaramouche"); /* CL.EXE prepends a '$' to the name of the label */ + HeapFree(GetProcessHeap(), 0, name); + } + break; + default: + break; + } + } + if (!found) todo_wine + ok(found, "Couldn't find local variable/parameter %ls\n", locpmts[j].name); + } + ok(labelfound, "Couldn't find the label scaramouche\n"); + HeapFree(GetProcessHeap(), 0, tfp); + } + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, si->Index, TI_GET_ADDRESS, &len); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(dbg->base <= si->Address && si->Address < dbg->base + dbg->size, "Wrong address\n"); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, si->Index, TI_GET_LEXICALPARENT, &type); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_SYMTAG, &tag); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(tag == SymTagCompiland, "Wrong tag %u\n", tag); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_SYMNAME, &name); + todo_wine + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + if (ret) + { + todo_wine + ok(isobjfile("debuggee1", name), "Wrong compiland name %ls\n", name); + HeapFree(GetProcessHeap(), 0, name); + } + + memset(si, 0, sizeof(*si)); + si->SizeOfStruct = sizeof(SYMBOL_INFO); + si->MaxNameLen = 200; + + ret = SymFromName(dbg->hProcess, "myfoobar", si); + ok(ret, "SymFromName failed: %u\n", GetLastError()); + ok(si->NameLen == 8, "Wrong name len %u\n", si->NameLen); + ok(!strcmp(si->Name, "myfoobar"), "Wrong name %s\n", si->Name); + ok(si->Size == dbg->manifest[1], "Wrong size %u\n", si->Size); + ok(si->ModBase == dbg->base, "Wrong module base: expecting 0x%s but got 0x%s\n", + wine_dbgstr_longlong(dbg->base), wine_dbgstr_longlong(si->ModBase)); + // FIXME on W10, we get 0 in Flags... + ok(dbg->base <= si->Address && si->Address < dbg->base + dbg->size, "Wrong address\n"); + ok(si->Tag == SymTagData, "Wrong tag %u\n", si->Tag); + + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, si->Index, TI_GET_ADDRESS, &len); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(dbg->base <= si->Address && si->Address < dbg->base + dbg->size, "Wrong address\n"); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, si->Index, TI_GET_TYPE, &type); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(type == si->TypeIndex, "Wrong type\n"); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, si->TypeIndex, TI_GET_LENGTH, &len); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(len == dbg->manifest[1], "Wrong size 0x%s\n", wine_dbgstr_longlong(len)); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, si->Index, TI_GET_LEXICALPARENT, &type); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_SYMTAG, &tag); + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + ok(tag == SymTagCompiland, "Wrong tag %u\n", tag); + ret = SymGetTypeInfo(dbg->hProcess, dbg->base, type, TI_GET_SYMNAME, &name); + todo_wine + ok(ret, "SymGetTypeInfo failed: %u\n", GetLastError()); + if (ret) + { + todo_wine + ok(isobjfile("debuggee1", name), "Wrong compiland name %ls\n", name); + HeapFree(GetProcessHeap(), 0, name); + } +} + static BOOL CALLBACK find_main_module_cb(PCSTR name, DWORD64 base, PVOID in_user) { struct debuggee* dbg = in_user; @@ -297,7 +613,7 @@ START_TEST(dbghelp) if (start_process(&dbg, other)) { test_stack_walk(&dbg); - + test_symbols(&dbg); close_process(&dbg); } }