[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