[PATCH 7/9] dbghelp/tests: Added tests about getting symbol information

Eric Pouech eric.pouech at gmail.com
Mon Jul 12 07:20:45 CDT 2021


Signed-off-by: Eric Pouech <eric.pouech at 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);
     }
 }




More information about the wine-devel mailing list