[PATCH 3/7] advapi32: Add initial support for querying performance counters data.

Dmitry Timoshkov dmitry at baikal.ru
Tue Jul 10 23:16:07 CDT 2018


Signed-off-by: Dmitry Timoshkov <dmitry at baikal.ru>
---
 dlls/advapi32/registry.c       | 244 ++++++++++++++++++++++++++++++++-
 dlls/advapi32/tests/registry.c |  17 +--
 2 files changed, 249 insertions(+), 12 deletions(-)

diff --git a/dlls/advapi32/registry.c b/dlls/advapi32/registry.c
index 087d47f772..ac75e7b577 100644
--- a/dlls/advapi32/registry.c
+++ b/dlls/advapi32/registry.c
@@ -2,6 +2,7 @@
  * Registry management
  *
  * Copyright (C) 1999 Alexandre Julliard
+ * Copyright (C) 2017 Dmitry Timoshkov
  *
  * Based on misc/registry.c code
  * Copyright (C) 1996 Marcus Meissner
@@ -36,6 +37,7 @@
 #include "winreg.h"
 #include "winerror.h"
 #include "winternl.h"
+#include "winperf.h"
 #include "winuser.h"
 #include "sddl.h"
 #include "advapi32_misc.h"
@@ -1480,6 +1482,234 @@ LONG WINAPI RegSetKeyValueA( HKEY hkey, LPCSTR subkey, LPCSTR name, DWORD type,
     return ret;
 }
 
+struct perf_provider
+{
+    HMODULE perflib;
+    PM_OPEN_PROC *pOpen;
+    PM_CLOSE_PROC *pClose;
+    PM_COLLECT_PROC *pCollect;
+};
+
+static void *get_provider_entry(HKEY perf, HMODULE perflib, const char *name)
+{
+    char buf[MAX_PATH];
+    DWORD err, type, len;
+
+    len = sizeof(buf) - 1;
+    err = RegQueryValueExA(perf, name, NULL, &type, (BYTE *)buf, &len);
+    if (err != ERROR_SUCCESS || type != REG_SZ)
+        return NULL;
+
+    buf[len] = 0;
+    TRACE("Loading function pointer for %s: %s\n", name, debugstr_a(buf));
+
+    return GetProcAddress(perflib, buf);
+}
+
+static BOOL load_provider(HKEY root, const WCHAR *name, struct perf_provider *provider)
+{
+    static const WCHAR performanceW[] = { 'P','e','r','f','o','r','m','a','n','c','e',0 };
+    static const WCHAR libraryW[] = { 'L','i','b','r','a','r','y',0 };
+    WCHAR buf[MAX_PATH], buf2[MAX_PATH];
+    DWORD err, type, len;
+    HKEY service, perf;
+
+    err = RegOpenKeyExW(root, name, 0, KEY_READ, &service);
+    if (err != ERROR_SUCCESS)
+        return FALSE;
+
+    err = RegOpenKeyExW(service, performanceW, 0, KEY_READ, &perf);
+    RegCloseKey(service);
+    if (err != ERROR_SUCCESS)
+        return FALSE;
+
+    len = sizeof(buf) - sizeof(WCHAR);
+    err = RegQueryValueExW(perf, libraryW, NULL, &type, (BYTE *)buf, &len);
+    if (err != ERROR_SUCCESS || !(type == REG_SZ || type == REG_EXPAND_SZ))
+        goto error;
+
+    buf[len / sizeof(WCHAR)] = 0;
+    if (type == REG_EXPAND_SZ)
+    {
+        len = ExpandEnvironmentStringsW(buf, buf2, MAX_PATH);
+        if (!len || len > MAX_PATH) goto error;
+        strcpyW(buf, buf2);
+    }
+
+    if (!(provider->perflib = LoadLibraryW(buf)))
+    {
+        WARN("Failed to load %s\n", debugstr_w(buf));
+        goto error;
+    }
+
+    GetModuleFileNameW(provider->perflib, buf, MAX_PATH);
+    TRACE("Loaded provider %s\n", wine_dbgstr_w(buf));
+
+    provider->pOpen = get_provider_entry(perf, provider->perflib, "Open");
+    provider->pClose = get_provider_entry(perf, provider->perflib, "Close");
+    provider->pCollect = get_provider_entry(perf, provider->perflib, "Collect");
+    if (provider->pOpen && provider->pClose && provider->pCollect)
+    {
+        RegCloseKey(perf);
+        return TRUE;
+    }
+
+    TRACE("Provider is missing required exports\n");
+    FreeLibrary(provider->perflib);
+
+error:
+    RegCloseKey(perf);
+    return FALSE;
+}
+
+static DWORD collect_data(struct perf_provider *provider, const WCHAR *query, void **data, DWORD *size, DWORD *obj_count)
+{
+    DWORD err;
+
+    err = provider->pOpen(NULL);
+    if (err != ERROR_SUCCESS)
+    {
+        TRACE("Open error %u (%#x)\n", err, err);
+        return err;
+    }
+
+    *obj_count = 0;
+    err = provider->pCollect((WCHAR *)query, data, size, obj_count);
+    if (err != ERROR_SUCCESS)
+    {
+        TRACE("Collect error %u (%#x)\n", err, err);
+        *obj_count = 0;
+    }
+
+    provider->pClose();
+    return err;
+}
+
+#define MAX_SERVICE_NAME 260
+
+static DWORD query_perf_data(const WCHAR *query, DWORD *type, void *data, DWORD *ret_size)
+{
+    static const WCHAR SZ_SERVICES_KEY[] = { 'S','y','s','t','e','m','\\',
+        'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
+        'S','e','r','v','i','c','e','s',0 };
+    DWORD err, i, data_size;
+    HKEY root;
+    PERF_DATA_BLOCK *pdb;
+
+    if (!ret_size)
+        return ERROR_INVALID_PARAMETER;
+
+    data_size = *ret_size;
+    *ret_size = 0;
+
+    if (type)
+        *type = REG_BINARY;
+
+    if (!data || data_size < sizeof(*pdb))
+        return ERROR_MORE_DATA;
+
+    pdb = data;
+
+    pdb->Signature[0] = 'P';
+    pdb->Signature[1] = 'E';
+    pdb->Signature[2] = 'R';
+    pdb->Signature[3] = 'F';
+#ifdef WORDS_BIGENDIAN
+    pdb->LittleEndian = FALSE;
+#else
+    pdb->LittleEndian = TRUE;
+#endif
+    pdb->Version = PERF_DATA_VERSION;
+    pdb->Revision = PERF_DATA_REVISION;
+    pdb->TotalByteLength = 0;
+    pdb->HeaderLength = sizeof(*pdb);
+    pdb->NumObjectTypes = 0;
+    pdb->DefaultObject = 0;
+    QueryPerformanceCounter(&pdb->PerfTime);
+    QueryPerformanceFrequency(&pdb->PerfFreq);
+
+    data = pdb + 1;
+    pdb->SystemNameOffset = sizeof(*pdb);
+    pdb->SystemNameLength = (data_size - sizeof(*pdb)) / sizeof(WCHAR);
+    if (!GetComputerNameW(data, &pdb->SystemNameLength))
+        return ERROR_MORE_DATA;
+
+    pdb->SystemNameLength++;
+    pdb->SystemNameLength *= sizeof(WCHAR);
+
+    pdb->HeaderLength += pdb->SystemNameLength;
+
+    /* align to 8 bytes */
+    if (pdb->SystemNameLength & 7)
+        pdb->HeaderLength += 8 - (pdb->SystemNameLength & 7);
+
+    if (data_size < pdb->HeaderLength)
+        return ERROR_MORE_DATA;
+
+    pdb->TotalByteLength = pdb->HeaderLength;
+
+    data_size -= pdb->HeaderLength;
+    data = (char *)data + pdb->HeaderLength;
+
+    err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, SZ_SERVICES_KEY, 0, KEY_READ, &root);
+    if (err != ERROR_SUCCESS)
+        return err;
+
+    i = 0;
+    for (;;)
+    {
+        DWORD collected_size = data_size, obj_count = 0;
+        struct perf_provider provider;
+        WCHAR name[MAX_SERVICE_NAME];
+        void *collected_data = data;
+
+        err = RegEnumKeyW(root, i++, name, MAX_SERVICE_NAME);
+        if (err == ERROR_NO_MORE_ITEMS)
+        {
+            err = ERROR_SUCCESS;
+            break;
+        }
+
+        if (err != ERROR_SUCCESS)
+            continue;
+
+        if (!load_provider(root, name, &provider))
+            continue;
+
+        err = collect_data(&provider, query, &collected_data, &collected_size, &obj_count);
+        FreeLibrary(provider.perflib);
+
+        if (err == ERROR_MORE_DATA)
+            break;
+
+        if (err == ERROR_SUCCESS)
+        {
+            PERF_OBJECT_TYPE *obj = (PERF_OBJECT_TYPE *)data;
+
+            TRACE("Collect: obj->TotalByteLength %u, collected_size %u\n",
+                obj->TotalByteLength, collected_size);
+
+            data_size -= collected_size;
+            data = collected_data;
+
+            pdb->TotalByteLength += collected_size;
+            pdb->NumObjectTypes += obj_count;
+        }
+    }
+
+    RegCloseKey(root);
+
+    if (err == ERROR_SUCCESS)
+    {
+        *ret_size = pdb->TotalByteLength;
+
+        GetSystemTime(&pdb->SystemTime);
+        GetSystemTimeAsFileTime((FILETIME *)&pdb->PerfTime100nSec);
+    }
+
+    return err;
+}
+
 /******************************************************************************
  * RegQueryValueExW   [ADVAPI32.@]
  *
@@ -1500,6 +1730,10 @@ LSTATUS WINAPI RegQueryValueExW( HKEY hkey, LPCWSTR name, LPDWORD reserved, LPDW
           (count && data) ? *count : 0 );
 
     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
+
+    if (hkey == HKEY_PERFORMANCE_DATA)
+        return query_perf_data(name, type, data, count);
+
     if (!(hkey = get_special_root_hkey( hkey, 0 ))) return ERROR_INVALID_HANDLE;
 
     RtlInitUnicodeString( &name_str, name );
@@ -1590,7 +1824,8 @@ LSTATUS WINAPI DECLSPEC_HOTPATCH RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWO
           hkey, debugstr_a(name), reserved, type, data, count, count ? *count : 0 );
 
     if ((data && !count) || reserved) return ERROR_INVALID_PARAMETER;
-    if (!(hkey = get_special_root_hkey( hkey, 0 ))) return ERROR_INVALID_HANDLE;
+    if (hkey != HKEY_PERFORMANCE_DATA && !(hkey = get_special_root_hkey( hkey, 0 )))
+        return ERROR_INVALID_HANDLE;
 
     if (count) datalen = *count;
     if (!data && count) *count = 0;
@@ -1602,6 +1837,13 @@ LSTATUS WINAPI DECLSPEC_HOTPATCH RegQueryValueExA( HKEY hkey, LPCSTR name, LPDWO
     if ((status = RtlAnsiStringToUnicodeString( &nameW, &nameA, TRUE )))
         return RtlNtStatusToDosError(status);
 
+    if (hkey == HKEY_PERFORMANCE_DATA)
+    {
+        DWORD ret = query_perf_data( nameW.Buffer, type, data, count );
+        RtlFreeUnicodeString( &nameW );
+        return ret;
+    }
+
     status = NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation,
                               buffer, sizeof(buffer), &total_size );
     if (status && status != STATUS_BUFFER_OVERFLOW) goto done;
diff --git a/dlls/advapi32/tests/registry.c b/dlls/advapi32/tests/registry.c
index 1573b6c03b..5e3535354c 100644
--- a/dlls/advapi32/tests/registry.c
+++ b/dlls/advapi32/tests/registry.c
@@ -3652,10 +3652,10 @@ static void test_RegQueryValueExPerformanceData(void)
 
     /* Test with data == NULL */
     dwret = RegQueryValueExA( HKEY_PERFORMANCE_DATA, "Global", NULL, NULL, NULL, &cbData );
-    todo_wine ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret );
+    ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret );
 
     dwret = RegQueryValueExW( HKEY_PERFORMANCE_DATA, globalW, NULL, NULL, NULL, &cbData );
-    todo_wine ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret );
+    ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret );
 
     /* Test ERROR_MORE_DATA, start with small buffer */
     len = 10;
@@ -3663,8 +3663,7 @@ static void test_RegQueryValueExPerformanceData(void)
     cbData = len;
     type = 0xdeadbeef;
     dwret = RegQueryValueExA( HKEY_PERFORMANCE_DATA, "Global", NULL, &type, value, &cbData );
-    todo_wine ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret );
-todo_wine
+    ok( dwret == ERROR_MORE_DATA, "expected ERROR_MORE_DATA, got %d\n", dwret );
     ok(type == REG_BINARY, "got %u\n", type);
     while( dwret == ERROR_MORE_DATA && limit)
     {
@@ -3677,14 +3676,13 @@ todo_wine
     }
     ok(limit > 0, "too many times ERROR_MORE_DATA returned\n");
 
-    todo_wine ok(dwret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", dwret);
-todo_wine
+    ok(dwret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", dwret);
     ok(type == REG_BINARY, "got %u\n", type);
 
     /* Check returned data */
     if (dwret == ERROR_SUCCESS)
     {
-        todo_wine ok(len >= sizeof(PERF_DATA_BLOCK), "got size %d\n", len);
+        ok(len >= sizeof(PERF_DATA_BLOCK), "got size %d\n", len);
         if (len >= sizeof(PERF_DATA_BLOCK)) {
             pdb = (PERF_DATA_BLOCK*) value;
             ok(pdb->Signature[0] == 'P', "expected Signature[0] = 'P', got 0x%x\n", pdb->Signature[0]);
@@ -3701,13 +3699,11 @@ todo_wine
     {
         cbData = 0xdeadbeef;
         dwret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, names[i], NULL, NULL, NULL, &cbData);
-todo_wine
         ok(dwret == ERROR_MORE_DATA, "%u/%s: got %u\n", i, names[i], dwret);
         ok(cbData == 0, "got %u\n", cbData);
 
         cbData = 0;
         dwret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, names[i], NULL, NULL, NULL, &cbData);
-todo_wine
         ok(dwret == ERROR_MORE_DATA, "%u/%s: got %u\n", i, names[i], dwret);
         ok(cbData == 0, "got %u\n", cbData);
 
@@ -3740,9 +3736,7 @@ todo_wine
     type = 0xdeadbeef;
     cbData = sizeof(buf);
     dwret = RegQueryValueExA(HKEY_PERFORMANCE_DATA, "invalid counter name", NULL, &type, buf, &cbData);
-todo_wine
     ok(dwret == ERROR_SUCCESS, "got %u\n", dwret);
-todo_wine
     ok(type == REG_BINARY, "got %u\n", type);
     if (dwret == ERROR_SUCCESS)
     {
@@ -3772,6 +3766,7 @@ todo_wine
         ok(pdb->TotalByteLength == len, "got %u vs %u\n", pdb->TotalByteLength, len);
         ok(pdb->HeaderLength == pdb->TotalByteLength, "got %u\n", pdb->HeaderLength);
         ok(pdb->NumObjectTypes == 0, "got %u\n", pdb->NumObjectTypes);
+todo_wine
         ok(pdb->DefaultObject != 0, "got %u\n", pdb->DefaultObject);
         ok(pdb->SystemTime.wYear == st.wYear, "got %u\n", pdb->SystemTime.wYear);
         ok(pdb->SystemTime.wMonth == st.wMonth, "got %u\n", pdb->SystemTime.wMonth);
-- 
2.17.1




More information about the wine-devel mailing list