From 2804d1996ec2b6ccb2bfab87af61a3bd56e8e168 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Tue, 3 Jan 2023 13:13:44 -0800 Subject: [PATCH 1/5] Implement new FunctionHook module. --- src/FunctionHook/FunctionHook.c | 111 +++++++ src/FunctionHook/FunctionHook.def | 12 + src/FunctionHook/FunctionHook.rc | 49 ++++ src/FunctionHook/FunctionHook.vcxproj | 77 +++++ src/FunctionHook/_penter_x64.asm | 402 ++++++++++++++++++++++++++ src/FunctionHook/dllmain.c | 45 +++ src/FunctionHook/stdafx.c | 17 ++ src/FunctionHook/stdafx.h | 23 ++ src/FunctionHook/targetver.h | 1 + src/PerfectHash.sln | 29 +- 10 files changed, 764 insertions(+), 2 deletions(-) create mode 100644 src/FunctionHook/FunctionHook.c create mode 100644 src/FunctionHook/FunctionHook.def create mode 100644 src/FunctionHook/FunctionHook.rc create mode 100644 src/FunctionHook/FunctionHook.vcxproj create mode 100644 src/FunctionHook/_penter_x64.asm create mode 100644 src/FunctionHook/dllmain.c create mode 100644 src/FunctionHook/stdafx.c create mode 100644 src/FunctionHook/stdafx.h create mode 100644 src/FunctionHook/targetver.h diff --git a/src/FunctionHook/FunctionHook.c b/src/FunctionHook/FunctionHook.c new file mode 100644 index 00000000..86f24b3a --- /dev/null +++ b/src/FunctionHook/FunctionHook.c @@ -0,0 +1,111 @@ +/*++ + +Copyright (c) 2022-2023 Trent Nelson + +Module Name: + + FunctionHook.c + +Abstract: + + This module implements function hooking glue. + +--*/ + +#include "stdafx.h" + +// +// Globals +// + +volatile PFUNCTION_ENTRY_CALLBACK FunctionEntryCallback = NULL; +volatile PVOID FunctionEntryCallbackContext = NULL; +volatile PVOID HookedModuleBaseAddress = NULL; +volatile ULONG HookedModuleSizeInBytes = 0; +volatile ULONG HookedModuleIgnoreRip = 0; + +// +// Functions +// + +VOID +SetFunctionEntryCallback ( + _In_ PFUNCTION_ENTRY_CALLBACK Callback, + _In_ PVOID Context, + _In_ PVOID ModuleBaseAddress, + _In_ ULONG ModuleSizeInBytes, + _In_ ULONG IgnoreRip + ) +{ + FunctionEntryCallbackContext = Context; + HookedModuleSizeInBytes = ModuleSizeInBytes; + HookedModuleBaseAddress = ModuleBaseAddress; + HookedModuleIgnoreRip = IgnoreRip; + FunctionEntryCallback = Callback; +} + +VOID +GetFunctionEntryCallback ( + _Out_ PFUNCTION_ENTRY_CALLBACK *Callback, + _Out_ PVOID *Context, + _Out_ PVOID *ModuleBaseAddress, + _Out_ ULONG *ModuleSizeInBytes, + _Out_ ULONG *IgnoreRip + ) +{ + *Context = FunctionEntryCallbackContext; + *ModuleSizeInBytes = HookedModuleSizeInBytes; + *ModuleBaseAddress = HookedModuleBaseAddress; + *IgnoreRip = HookedModuleIgnoreRip; + *Callback = FunctionEntryCallback; +} + +BOOLEAN +IsFunctionEntryCallbackEnabled ( + VOID + ) +{ + return ( + FunctionEntryCallback != NULL && + HookedModuleBaseAddress != NULL && + HookedModuleSizeInBytes > 0 + ); +} + +VOID +ClearFunctionEntryCallback ( + _Out_opt_ PFUNCTION_ENTRY_CALLBACK *Callback, + _Out_opt_ PVOID *Context, + _Out_opt_ PVOID *ModuleBaseAddress, + _Out_opt_ ULONG *ModuleSizeInBytes, + _Out_opt_ ULONG *IgnoreRip + ) +{ + if (ARGUMENT_PRESENT(Context)) { + *Context = FunctionEntryCallbackContext; + } + + if (ARGUMENT_PRESENT(ModuleSizeInBytes)) { + *ModuleSizeInBytes = HookedModuleSizeInBytes; + } + + if (ARGUMENT_PRESENT(ModuleBaseAddress)) { + *ModuleBaseAddress = HookedModuleBaseAddress; + } + + if (ARGUMENT_PRESENT(IgnoreRip)) { + *IgnoreRip = HookedModuleIgnoreRip; + } + + if (ARGUMENT_PRESENT(Callback)) { + *Callback = FunctionEntryCallback; + } + + FunctionEntryCallback = NULL; + HookedModuleBaseAddress = NULL; + HookedModuleSizeInBytes = 0; + HookedModuleIgnoreRip = 0; + FunctionEntryCallbackContext = NULL; +} + +// vim:set ts=8 sw=4 sts=4 tw=80 expandtab : diff --git a/src/FunctionHook/FunctionHook.def b/src/FunctionHook/FunctionHook.def new file mode 100644 index 00000000..f9d189a5 --- /dev/null +++ b/src/FunctionHook/FunctionHook.def @@ -0,0 +1,12 @@ +LIBRARY FunctionHook +EXPORTS + _penter=_penter_slow + HookedModuleIgnoreRip + HookedModuleBaseAddress + HookedModuleSizeInBytes + FunctionEntryCallback + FunctionEntryCallbackContext + SetFunctionEntryCallback + GetFunctionEntryCallback + ClearFunctionEntryCallback + IsFunctionEntryCallbackEnabled diff --git a/src/FunctionHook/FunctionHook.rc b/src/FunctionHook/FunctionHook.rc new file mode 100644 index 00000000..acc1d08c --- /dev/null +++ b/src/FunctionHook/FunctionHook.rc @@ -0,0 +1,49 @@ +/*++ + +Copyright (c) 2023 Trent Nelson + +Module Name: + + FunctionHookExe.rc + +Abstract: + + This is the main resource compiler for the perfect hash library's bulk + create executable. It is responsible for providing version information. + +--*/ + +#include "../PerfectHashVersion.rc" + +#define VER_FILEDESCRIPTION_STR "Perfect Hash Function Hooking Library" +#define VER_ORIGINALFILENAME_STR "FunctionHook.dll" +#define VER_INTERNALNAME_STR VER_ORIGINALFILENAME_STR + +VS_VERSION_INFO VERSIONINFO +FILEVERSION VER_FILEVERSION +PRODUCTVERSION VER_PRODUCTVERSION +FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +FILEFLAGS VER_FILEFLAGS +FILEOS VOS_NT +FILETYPE VFT_APP +FILESUBTYPE VFT2_UNKNOWN +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "CompanyName", VER_COMPANYNAME_STR + VALUE "FileDescription", VER_FILEDESCRIPTION_STR + VALUE "LegalCopyright", VER_LEGALCOPYRIGHT_STR + VALUE "OriginalFilename", VER_ORIGINALFILENAME_STR + VALUE "ProductName", VER_PRODUCTNAME_STR + VALUE "ProductVersion", VER_PRODUCTVERSION_STR + END + END + + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + diff --git a/src/FunctionHook/FunctionHook.vcxproj b/src/FunctionHook/FunctionHook.vcxproj new file mode 100644 index 00000000..b085b064 --- /dev/null +++ b/src/FunctionHook/FunctionHook.vcxproj @@ -0,0 +1,77 @@ + + + + + Debug + x64 + + + Release + x64 + + + PGInstrument + x64 + + + PGUpdate + x64 + + + PGOptimize + x64 + + + + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850} + FunctionHook + en-US + 10.0 + FunctionHook + + + + DynamicLibrary + + + + + + + + + + + + + + + + + FunctionHook.def + + + ../../include + + + + + + + + + + Create + + + + + + + + + + + + + diff --git a/src/FunctionHook/_penter_x64.asm b/src/FunctionHook/_penter_x64.asm new file mode 100644 index 00000000..38359da9 --- /dev/null +++ b/src/FunctionHook/_penter_x64.asm @@ -0,0 +1,402 @@ + title "FunctionHook _penter" + +;++ +; +; Copyright (c) 2022-2023 Trent Nelson +; +; Module Name: +; +; _penter_x64.asm +; +; Abstract: +; +; This module implements _penter routines that will call a configured callback +; routine with the appropriate RIP address. It is used to evaluate runtime +; performance of compiled perfect hash tables that have been generated from +; PerfectHash.dll binaries compiled with /Gh (hook functions with _penter). +; +;-- + +include PerfectHash.inc + +; +; Externs defined in FunctionHook.c. +; + +EXTERN FunctionEntryCallback:PTR PROC +EXTERN FunctionEntryCallbackContext:PTR PVOID +EXTERN HookedModuleBaseAddress:PVOID +EXTERN HookedModuleSizeInBytes:ULONG +EXTERN HookedModuleIgnoreRip:ULONG + +; +; Locals struct. +; + +Locals struct + + ; + ; Define home parameter space. + ; + + CalleeHomeRcx dq ? + CalleeHomeRdx dq ? + CalleeHomeR8 dq ? + CalleeHomeR9 dq ? + + ; + ; Saved volatile registers. + ; + + SavedRax dq ? + SavedRcx dq ? + SavedRdx dq ? + SavedR8 dq ? + SavedR9 dq ? + SavedR10 dq ? + SavedR11 dq ? + SavedXmm0 xmmword ? + + Padding1 dq ? + Padding2 dq ? + + ReturnAddress dq ? + +Locals ends + +; +; Exclude the return address onward from the frame calculation size. +; + +LOCALS_SIZE equ ((sizeof Locals) + (Locals.ReturnAddress - (sizeof Locals))) + +;++ +; +; VOID +; _penter_fast( +; VOID +; ); +; +; Routine Description: +; +; "Fast" implementation of _penter, which only persists volatile registers +; as and when needed. +; +; N.B. Hooking _penter is one of the rare occasions where volatile registers +; need to be persisted; if we didn't do this, optimized builds would +; crash in weird and wonderful ways. This is because the compiler knows +; everything related to register usage when calling internal routines, +; and will regularly rely on volatile registers having their values +; persist between function calls because it *knows*, for example, no +; function in calls from a given point is going to write to RDX. +; +; _penter breaks this assumption. Or rather, we break this assumption +; because we need to use volatile registers in order to carry out the +; _penter tasks. So, we *always* persist any volatile register we mutate +; in this routine. As an optimization, we only persist those we mutate, +; prior to mutating them. This avoids unnecessary ops if no callback is +; configured, for example. +; +; Arguments: +; +; None. +; +; Return Value: +; +; None. +; +;-- + + NESTED_ENTRY _penter_fast, _TEXT$00 + alloc_stack LOCALS_SIZE ; Allocate stack space. + END_PROLOGUE + + mov Locals.SavedR10[rsp], r10 ; Save r10. + +; +; Check if the extern pointer HookedModuleBaseAddress has been set. If it +; hasn't, fast-path exit. +; + + mov r10, HookedModuleBaseAddress ; Load module info ptr. + test r10, r10 ; Is null? + jz Pe99 ; Yes, exit. + +; +; Save rax and rcx prior to use. Next jump target for cleanup is Pe97. +; + + mov Locals.SavedRax[rsp], rax ; Save rax. + mov Locals.SavedRcx[rsp], rcx ; Save rcx. + +; +; Load the RIP from the return address. +; +; rcx: Return RIP +; r10: Base module address +; eax: Module size in bytes +; rdx: Normalized return RIP (i.e. return RIP - base module address) +; + + lea rax, Locals.ReturnAddress[rsp] ; Load RSP address. + mov rcx, [rax] ; Load return RIP. + sub rcx, 5 ; Sub the call _penter len. + + cmp rcx, r10 ; Is RIP < base address? + jl Pe97 ; Yes, out of bounds, exit. + +; +; Save rdx prior to use. Next jump target for cleanup is Pe95. +; + + mov Locals.SavedRdx[rsp], rdx + +; +; Load the RIP into rdx and then subtract the base image address. Compare this +; value to the image size: if it's greater than, then we're out of bounds (i.e. +; not being called from a module we know about), so exit. +; + + xor rax, rax ; Clear rax. + mov eax, HookedModuleSizeInBytes ; Load image size. + mov rdx, rcx ; Load RIP into rdx. + sub rdx, r10 ; Sub base address. + cmp rdx, rax ; Is RIP > base + size? + jge Pe95 ; Yes, out of bounds, exit. + +; +; Save r11 prior to use. Next jump target for cleanup is Pe93. +; + + mov Locals.SavedR11[rsp], r11 + +; +; Load the HookedModuleIgnoreRip into r11, and, if it's not zero, check to see +; if it matches our relative RIP in rdx. If it does, then we've been asked to +; ignore this RIP, so jump to the end. +; + + xor r11, r11 ; Clear r11. + mov r11d, HookedModuleIgnoreRip ; Load ignored RIP. + test r11d, r11d ; Is 0? + jz @F ; Yes, skip check. + cmp edx, r11d ; Does RIP match ignored? + je Pe93 ; Yes, exit. + +; +; The normalized return RIP is in rdx. Final step: load the callback pointer, +; and ensure it's not null. +; + +@@: mov r10, FunctionEntryCallback ; Load callback ptr. + test r10, r10 ; Is null? + jz Pe93 ; Yes, exit. + +; +; All of our preconditions have been satisified. Load the normalized return +; RIP into rcx (1st param), context into rdx (2nd param), save the remaining +; volatile registers, then call the callback. +; + + mov rcx, rdx ; Load RIP into 1st param. + mov rdx, FunctionEntryCallbackContext ; Load context ptr 2nd param. + +; +; Save the remaining volatile registers prior to invoking the callback. +; + + mov Locals.SavedR8[rsp], r8 + mov Locals.SavedR9[rsp], r9 + vmovdqu xmmword ptr Locals.SavedXmm0[rsp], xmm0 + +; +; Invoke the callback. +; + + xor r8, r8 ; Clear r8. + xor r9, r9 ; Clear r9. + call r10 ; Call the function callback. + +; +; Restore volatile registers in the order we used them. +; + + mov r8, Locals.SavedR8[rsp] + mov r9, Locals.SavedR9[rsp] + vmovdqu xmm0, xmmword ptr Locals.SavedXmm0[rsp] + +Pe93: mov r11, Locals.SavedR11[rsp] + +Pe95: mov rdx, Locals.SavedRdx[rsp] + +Pe97: mov rax, Locals.SavedRax[rsp] + mov rcx, Locals.SavedRcx[rsp] + +Pe99: mov r10, Locals.SavedR10[rsp] + +; +; Begin epilogue. +; + + add rsp, LOCALS_SIZE ; Deallocate stack space. + ret + NESTED_END _penter_fast, _TEXT$00 + +;++ +; +; VOID +; _penter_slow( +; VOID +; ); +; +; Routine Description: +; +; "Slow" implementation of _penter, which saves all applicable volatile +; registers at the start of the routine, regardless of whether or not +; a callback function is active. +; +; This routine, despite being slower than _penter_fast, can still be useful +; as it lowers the gap between _penter overhead when a callback is active +; versus not. +; +; For example, using _penter fast, solving HologramWorld-31016.keys with +; Chm01 MultiplyShiftR And 24 (on a 24-core box), I get about 21,000 attempts +; per second with the Index() callback active, and ~34,000 attempts with the +; callback disabled. (A non _penter build clocks in at nearly 40,000 attempts +; per second.) +; +; A slow _penter build clocks in at about 24,000 attempts per second when +; no callback is active, and about 21,000 attempts per second when the Index() +; routine is active as a callback. So we can see that our Index() routine is +; responsible for only ~3,000 attempts per second, not ~13,000 attempts per +; second if we use the fast routine. +; +; Arguments: +; +; None. +; +; Return Value: +; +; None. +; +;-- + + NESTED_ENTRY _penter_slow, _TEXT$00 + alloc_stack LOCALS_SIZE ; Allocate stack space. + END_PROLOGUE + +; +; Save all registers up front. +; + + mov Locals.SavedRax[rsp], rax + mov Locals.SavedRcx[rsp], rcx + mov Locals.SavedRdx[rsp], rdx + mov Locals.SavedR8[rsp], r8 + mov Locals.SavedR9[rsp], r9 + mov Locals.SavedR10[rsp], r10 + mov Locals.SavedR11[rsp], r11 + vmovdqu xmmword ptr Locals.SavedXmm0[rsp], xmm0 + +; +; Check if the extern pointer HookedModuleBaseAddress has been set. If it +; hasn't, fast-path exit. +; + + mov r10, HookedModuleBaseAddress ; Load module info ptr. + test r10, r10 ; Is null? + jz Ps90 ; Yes, exit. + +; +; Load the RIP from the return address. +; +; rcx: Return RIP +; r10: Base module address +; eax: Module size in bytes +; rdx: Normalized return RIP (i.e. return RIP - base module address) +; + + lea rax, Locals.ReturnAddress[rsp] ; Load RSP address. + mov rcx, [rax] ; Load return RIP. + sub rcx, 5 ; Sub the call _penter len. + + cmp rcx, r10 ; Is RIP < base address? + jl Ps90 ; Yes, out of bounds, exit. + +; +; Load the RIP into rdx and then subtract the base image address. Compare this +; value to the image size: if it's greater than, then we're out of bounds (i.e. +; not being called from a module we know about), so exit. +; + + xor rax, rax ; Clear rax. + mov eax, HookedModuleSizeInBytes ; Load image size. + mov rdx, rcx ; Load RIP into rdx. + sub rdx, r10 ; Sub base address. + cmp rdx, rax ; Is RIP > base + size? + jge Ps90 ; Yes, out of bounds, exit. + +; +; Load the HookedModuleIgnoreRip into r11, and, if it's not zero, check to see +; if it matches our relative RIP in rdx. If it does, then we've been asked to +; ignore this RIP, so jump to the end. +; + + xor r11, r11 ; Clear r11. + mov r11d, HookedModuleIgnoreRip ; Load ignored RIP. + test r11d, r11d ; Is 0? + jz @F ; Yes, skip check. + cmp edx, r11d ; Does RIP match ignored? + je Ps90 ; Yes, exit. + +; +; The normalized return RIP is in rdx. Final step: load the callback pointer, +; and ensure it's not null. +; + +@@: mov r10, FunctionEntryCallback ; Load callback ptr. + test r10, r10 ; Is null? + jz Ps90 ; Yes, exit. + +; +; All of our preconditions have been satisified. Load the normalized return +; RIP into rcx (1st param), context into rdx (2nd param), save the remaining +; volatile registers, then call the callback. +; + + mov rcx, rdx ; Load RIP into 1st param. + mov rdx, FunctionEntryCallbackContext ; Load context ptr 2nd param. + +; +; Invoke the callback. +; + + xor r8, r8 ; Clear r8. + xor r9, r9 ; Clear r9. + call r10 ; Call the function callback. + +; +; Restore volatile registers. +; + +Ps90: mov rax, Locals.SavedRax[rsp] + mov rcx, Locals.SavedRcx[rsp] + mov rdx, Locals.SavedRdx[rsp] + mov r8, Locals.SavedR8[rsp] + mov r9, Locals.SavedR9[rsp] + mov r10, Locals.SavedR10[rsp] + mov r11, Locals.SavedR11[rsp] + vmovdqu xmm0, xmmword ptr Locals.SavedXmm0[rsp] + +; +; Begin epilogue. +; + + add rsp, LOCALS_SIZE ; Deallocate stack space. + ret + NESTED_END _penter_slow, _TEXT$00 + + +; vim:set tw=80 ts=8 sw=4 sts=4 et syntax=masm fo=croql comments=\:; : + +end diff --git a/src/FunctionHook/dllmain.c b/src/FunctionHook/dllmain.c new file mode 100644 index 00000000..ad3f4313 --- /dev/null +++ b/src/FunctionHook/dllmain.c @@ -0,0 +1,45 @@ +/*++ + +Copyright (c) 2022-2023 Trent Nelson + +Module Name: + + dllmain.c + +Abstract: + + This is the DLL main entry point for the FunctionHook component. + +--*/ + +#include "stdafx.h" + +HMODULE FunctionHookModule = NULL; + +BOOL +APIENTRY +_DllMainCRTStartup( + _In_ HMODULE Module, + _In_ DWORD Reason, + _In_ LPVOID Reserved + ) +{ + UNREFERENCED_PARAMETER(Reserved); + + switch (Reason) { + case DLL_PROCESS_ATTACH: + FunctionHookModule = Module; + __security_init_cookie(); + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + break; + } + + return TRUE; +} + +// vim:set ts=8 sw=4 sts=4 tw=80 expandtab : diff --git a/src/FunctionHook/stdafx.c b/src/FunctionHook/stdafx.c new file mode 100644 index 00000000..b89fee05 --- /dev/null +++ b/src/FunctionHook/stdafx.c @@ -0,0 +1,17 @@ +/*++ + +Copyright (c) 2022 Trent Nelson + +Module Name: + + stdafx.c + +Abstract: + + This is the precompiled source file for the FunctionHook component. + +--*/ + +#include "stdafx.h" + +// vim:set ts=8 sw=4 sts=4 tw=80 expandtab : diff --git a/src/FunctionHook/stdafx.h b/src/FunctionHook/stdafx.h new file mode 100644 index 00000000..a5bfc84e --- /dev/null +++ b/src/FunctionHook/stdafx.h @@ -0,0 +1,23 @@ +/*++ + +Copyright (c) 2022 Trent Nelson + +Module Name: + + stdafx.h + +Abstract: + + This is the precompiled header file for the FunctionHook component. + +--*/ + +#pragma once + +#include "targetver.h" + +#include +#include + + +// vim:set ts=8 sw=4 sts=4 tw=80 expandtab : diff --git a/src/FunctionHook/targetver.h b/src/FunctionHook/targetver.h new file mode 100644 index 00000000..cfae052b --- /dev/null +++ b/src/FunctionHook/targetver.h @@ -0,0 +1 @@ +#include diff --git a/src/PerfectHash.sln b/src/PerfectHash.sln index 9a6e676f..a7a77ed7 100644 --- a/src/PerfectHash.sln +++ b/src/PerfectHash.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27004.2009 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33122.133 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A70C4C43-F7F1-4D59-8478-3DA0797E362C}" ProjectSection(SolutionItems) = preProject @@ -9,6 +9,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PerfectHash", "PerfectHash\PerfectHash.vcxproj", "{14D9F1FD-1EC4-47FF-BF73-3868ED05FEB8}" + ProjectSection(ProjectDependencies) = postProject + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850} = {B9EF04DA-0AD9-45C4-97B0-857A13AA4850} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PerfectHashSelfTestExe", "PerfectHashSelfTestExe\PerfectHashSelfTestExe.vcxproj", "{741C0AD4-D461-4E15-96E3-B6D30B7068EA}" ProjectSection(ProjectDependencies) = postProject @@ -30,6 +33,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PerfectHashCreateExe", "Per {14D9F1FD-1EC4-47FF-BF73-3868ED05FEB8} = {14D9F1FD-1EC4-47FF-BF73-3868ED05FEB8} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FunctionHook", "FunctionHook\FunctionHook.vcxproj", "{B9EF04DA-0AD9-45C4-97B0-857A13AA4850}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -124,6 +129,26 @@ Global {244D25EC-A8AA-4AAD-A66C-D64AFA836CAE}.Release|x64.Build.0 = Release|x64 {244D25EC-A8AA-4AAD-A66C-D64AFA836CAE}.Release|x86.ActiveCfg = Release|Win32 {244D25EC-A8AA-4AAD-A66C-D64AFA836CAE}.Release|x86.Build.0 = Release|Win32 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.Debug|x64.ActiveCfg = Debug|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.Debug|x64.Build.0 = Debug|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.Debug|x86.ActiveCfg = Debug|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.Debug|x86.Build.0 = Debug|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.PGInstrument|x86.ActiveCfg = PGInstrument|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.PGInstrument|x86.Build.0 = PGInstrument|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.PGOptimize|x64.ActiveCfg = PGOptimize|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.PGOptimize|x64.Build.0 = PGOptimize|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.PGOptimize|x86.ActiveCfg = PGOptimize|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.PGOptimize|x86.Build.0 = PGOptimize|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.PGUpdate|x86.ActiveCfg = PGUpdate|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.PGUpdate|x86.Build.0 = PGUpdate|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.Release|x64.ActiveCfg = Release|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.Release|x64.Build.0 = Release|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.Release|x86.ActiveCfg = Release|x64 + {B9EF04DA-0AD9-45C4-97B0-857A13AA4850}.Release|x86.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From d05e61affee9ee86f281b5d2f5925490f7676f00 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Tue, 3 Jan 2023 14:20:13 -0800 Subject: [PATCH 2/5] Relocate PerfectHash.inc to top-level include directory. --- {src/PerfectHash => include}/PerfectHash.inc | 57 ++++++++++++++++++-- src/PerfectHash.props | 5 ++ 2 files changed, 58 insertions(+), 4 deletions(-) rename {src/PerfectHash => include}/PerfectHash.inc (56%) diff --git a/src/PerfectHash/PerfectHash.inc b/include/PerfectHash.inc similarity index 56% rename from src/PerfectHash/PerfectHash.inc rename to include/PerfectHash.inc index 504c7db2..4a59e3ef 100644 --- a/src/PerfectHash/PerfectHash.inc +++ b/include/PerfectHash.inc @@ -1,17 +1,17 @@ - title "PerfectHashTable" + title "PerfectHash Assembly Include File" option nokeyword: ;++ ; -; Copyright (c) Trent Nelson, 2016. +; Copyright (c) Trent Nelson, 2016-2022. ; ; Module Name: ; -; PerfectHashTable.inc +; PerfectHash.inc ; ; Abstract: ; -; This is an include module for the PerfectHashTable component. +; This is an assembly include module for the PerfectHash library. ; ;-- @@ -47,6 +47,7 @@ IndexMask equ 0bbbbbbbbh ; CHAR typedef byte +UCHAR typedef byte PCHAR typedef ptr byte USHORT typedef word WCHAR typedef word @@ -55,6 +56,9 @@ LONG typedef dword ULONG typedef dword ULONG64 typedef qword ULONGLONG typedef qword +PULONG typedef ptr ULONG +PULONGLONG typedef ptr ULONGLONG +PUCHAR typedef ptr UCHAR PARAMS struct ReturnAddress dq ? @@ -99,5 +103,50 @@ PERFECT_HASH_TABLE ends Table typedef PERFECT_HASH_TABLE +; +; ETW glue. +; + +TRACEHANDLE typedef ULONG64 + +MCGEN_TRACE_CONTEXT struct + RegistrationHandle TRACEHANDLE ? + Logger TRACEHANDLE ? + MatchAnyKeyword ULONGLONG ? + MatchAllKeyword ULONGLONG ? + Flags ULONG ? + IsEnabled ULONG ? + Level UCHAR ? + Reserve UCHAR ? + EnableBitsCount USHORT ? + EnableBitMask PULONG ? + EnableKeyWords PULONGLONG ? + EnableLevel PUCHAR ? +MCGEN_TRACE_CONTEXT ends + +EVENT_DATA_DESCRIPTOR struct + Pointer dq ? + SizeInBytes dd ? + + union + Reserved dw ? + struct + EventType db ? + Reserved1 db ? + Reserved2 dw ? + ends + ends +EVENT_DATA_DESCRIPTOR ends + +EventDataDescriptor typedef EVENT_DATA_DESCRIPTOR + +_MODULEINFO struct + BaseAddress PVOID ? + SizeOfImage DWORD ? + EntryPoint PVOID ? +_MODULEINFO ends + +ModuleInfo typedef _MODULEINFO + ; vim:set tw=80 ts=8 sw=4 sts=4 expandtab syntax=masm : diff --git a/src/PerfectHash.props b/src/PerfectHash.props index 1ab91814..88f9e025 100644 --- a/src/PerfectHash.props +++ b/src/PerfectHash.props @@ -56,6 +56,7 @@ MultiThreadedDLL true _WINDOWS;_USRDLL;%(PreprocessorDefinitions) + $(InstructionSet) @@ -162,6 +163,10 @@ 0x0409 + + $(SolutionDir)\..\include + + From 75724050bf091c6e47fc460f8849edf046da8c86 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Tue, 3 Jan 2023 14:27:35 -0800 Subject: [PATCH 3/5] Add InterlockedIncrement() routine to CompiledPerfectHash. --- include/CompiledPerfectHash.h | 35 +++++++++- include/CompiledPerfectHashMacroGlue.h | 97 ++++++++++++++++++++++---- 2 files changed, 115 insertions(+), 17 deletions(-) diff --git a/include/CompiledPerfectHash.h b/include/CompiledPerfectHash.h index 26b628f6..7f561e3e 100644 --- a/include/CompiledPerfectHash.h +++ b/include/CompiledPerfectHash.h @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -50,10 +50,14 @@ extern "C" { #endif // -// Clang doesn't appear to support the rotate intrinsics _rotr and _rotl, -// so, define some static inline versions here. +// Older versions of clang didn't appear to support the rotate intrinsics _rotr +// and _rotl, so, we used some static inline versions, below. Recent versions +// (10.0+) appear to have the intrinsics, so, disable this block for now. If +// you are compiling on an older version of clang, change the 0 to 1 to get the +// rotate intrinsics back. // +#if 0 static inline unsigned int _rotl( @@ -75,6 +79,7 @@ _rotr( b &= 31; return (a >> b) | (a << (32 - b)); } +#endif #elif defined(__GNUC__) #include @@ -264,6 +269,30 @@ Return Value: --*/ typedef COMPILED_PERFECT_HASH_TABLE_DELETE *PCOMPILED_PERFECT_HASH_TABLE_DELETE; +typedef +CPHAPI +CPHVALUE +(CPHCALLTYPE COMPILED_PERFECT_HASH_TABLE_INTERLOCKED_INCREMENT)( + _In_ CPHKEY Key + ); +/*++ + +Routine Description: + + Increments the value associated with a key. + +Arguments: + + Key - Supplies the key to increment. + +Return Value: + + Previous value. + +--*/ +typedef COMPILED_PERFECT_HASH_TABLE_INTERLOCKED_INCREMENT + *PCOMPILED_PERFECT_HASH_TABLE_INTERLOCKED_INCREMENT; + // // Typedefs of methods for testing and benchmarking. // diff --git a/include/CompiledPerfectHashMacroGlue.h b/include/CompiledPerfectHashMacroGlue.h index 32e479ec..d93083ee 100644 --- a/include/CompiledPerfectHashMacroGlue.h +++ b/include/CompiledPerfectHashMacroGlue.h @@ -83,14 +83,18 @@ #define CPH_INDEX_ROUTINE_NAME(T) CompiledPerfectHash_##T##_Index #define CPH_INDEX_IACA_ROUTINE_NAME(T) CompiledPerfectHash_##T##_IndexIaca +#define CPH_INDEX_BSEARCH_ROUTINE_NAME(T) CompiledPerfectHash_##T##_IndexBsearch #define CPH_LOOKUP_ROUTINE_NAME(T) CompiledPerfectHash_##T##_Lookup #define CPH_INSERT_ROUTINE_NAME(T) CompiledPerfectHash_##T##_Insert #define CPH_DELETE_ROUTINE_NAME(T) CompiledPerfectHash_##T##_Delete +#define CPH_INTERLOCKED_INCREMENT_ROUTINE_NAME(T) CompiledPerfectHash_##T##_InterlockedIncrement #define CPH_INDEX_INLINE_ROUTINE_NAME(T) CompiledPerfectHash_##T##_IndexInline +#define CPH_INDEX_BSEARCH_INLINE_ROUTINE_NAME(T) CompiledPerfectHash_##T##_IndexBsearchInline #define CPH_LOOKUP_INLINE_ROUTINE_NAME(T) CompiledPerfectHash_##T##_LookupInline #define CPH_INSERT_INLINE_ROUTINE_NAME(T) CompiledPerfectHash_##T##_InsertInline #define CPH_DELETE_INLINE_ROUTINE_NAME(T) CompiledPerfectHash_##T##_DeleteInline +#define CPH_INTERLOCKED_INCREMENT_INLINE_ROUTINE_NAME(T) CompiledPerfectHash_##T##_InterlockedIncrementInline //////////////////////////////////////////////////////////////////////////////// // Index @@ -120,6 +124,16 @@ CompiledPerfectHash_##T##_IndexIaca( \ CPHKEY Key \ ) +#define CPH_INDEX_BSEARCH_ROUTINE_HEADER(T) \ +CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX \ + CompiledPerfectHash_##T##_IndexBsearch; \ + \ +_Use_decl_annotations_ \ +CPHINDEX \ +CompiledPerfectHash_##T##_IndexBsearch( \ + CPHKEY Key \ + ) + // // Inline // @@ -131,6 +145,13 @@ CompiledPerfectHash_##T##_IndexInline( \ CPHKEY Key \ ) +#define CPH_INDEX_BSEARCH_INLINE_ROUTINE_HEADER(T) \ +FORCEINLINE \ +CPHINDEX \ +CompiledPerfectHash_##T##_IndexBsearchInline( \ + CPHKEY Key \ + ) + //////////////////////////////////////////////////////////////////////////////// // Lookup //////////////////////////////////////////////////////////////////////////////// @@ -221,6 +242,35 @@ CompiledPerfectHash_##T##_DeleteInline( \ CPHKEY Key \ ) +//////////////////////////////////////////////////////////////////////////////// +// InterlockedIncrement +//////////////////////////////////////////////////////////////////////////////// + +// +// Normal +// + +#define CPH_INTERLOCKED_INCREMENT_ROUTINE_HEADER(T) \ +CPHAPI COMPILED_PERFECT_HASH_TABLE_INTERLOCKED_INCREMENT \ + CompiledPerfectHash_##T##_InterlockedIncrement; \ + \ +_Use_decl_annotations_ \ +CPHVALUE \ +CompiledPerfectHash_##T##_InterlockedIncrement( \ + CPHKEY Key \ + ) + +// +// Inline +// + +#define CPH_INTERLOCKED_INCREMENT_INLINE_ROUTINE_HEADER(T) \ +FORCEINLINE \ +CPHVALUE \ +CompiledPerfectHash_##T##_InterlockedIncrementInline( \ + CPHKEY Key \ + ) + //////////////////////////////////////////////////////////////////////////////// // Test and Benchmarking @@ -266,9 +316,11 @@ BenchmarkIndexCompiledPerfectHashTable_##T( \ //////////////////////////////////////////////////////////////////////////////// #ifdef CPH_INDEX_ONLY -#define CPH_DEFINE_TABLE_ROUTINES(T) \ -CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_Index; \ -CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_IndexIaca + +#define CPH_DEFINE_TABLE_ROUTINES(T) \ +CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_Index; \ +CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_IndexIaca; \ +CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_IndexBsearch #define CPH_INDEX_ROUTINE(T) CPH_INDEX_ROUTINE_NAME(T) #define CPH_INDEX_IACA_ROUTINE(T) CPH_INDEX_IACA_ROUTINE_NAME(T) @@ -279,12 +331,14 @@ extern BENCHMARK_INDEX_COMPILED_PERFECT_HASH_TABLE \ BenchmarkIndexCompiledPerfectHashTable_##T #else -#define CPH_DEFINE_TABLE_ROUTINES(T) \ -CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_Index; \ -CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_IndexIaca; \ -CPHAPI COMPILED_PERFECT_HASH_TABLE_LOOKUP CompiledPerfectHash_##T##_Lookup; \ -CPHAPI COMPILED_PERFECT_HASH_TABLE_INSERT CompiledPerfectHash_##T##_Insert; \ -CPHAPI COMPILED_PERFECT_HASH_TABLE_DELETE CompiledPerfectHash_##T##_Delete +#define CPH_DEFINE_TABLE_ROUTINES(T) \ +CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_Index; \ +CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_IndexIaca; \ +CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_IndexBsearch; \ +CPHAPI COMPILED_PERFECT_HASH_TABLE_LOOKUP CompiledPerfectHash_##T##_Lookup; \ +CPHAPI COMPILED_PERFECT_HASH_TABLE_INSERT CompiledPerfectHash_##T##_Insert; \ +CPHAPI COMPILED_PERFECT_HASH_TABLE_DELETE CompiledPerfectHash_##T##_Delete; \ +CPHAPI COMPILED_PERFECT_HASH_TABLE_INTERLOCKED_INCREMENT CompiledPerfectHash_##T##_InterlockedIncrement #define CPH_DEFINE_TEST_AND_BENCHMARKING_ROUTINES(T) \ extern TEST_COMPILED_PERFECT_HASH_TABLE \ @@ -298,14 +352,18 @@ extern BENCHMARK_INDEX_COMPILED_PERFECT_HASH_TABLE \ #define CPH_INDEX_ROUTINE(T) CPH_INDEX_ROUTINE_NAME(T) #define CPH_INDEX_IACA_ROUTINE(T) CPH_INDEX_IACA_ROUTINE_NAME(T) +#define CPH_INDEX_BSEARCH_ROUTINE(T) CPH_INDEX_BSEARCH_ROUTINE_NAME(T) #define CPH_LOOKUP_ROUTINE(T) CPH_LOOKUP_ROUTINE_NAME(T) #define CPH_INSERT_ROUTINE(T) CPH_INSERT_ROUTINE_NAME(T) #define CPH_DELETE_ROUTINE(T) CPH_DELETE_ROUTINE_NAME(T) +#define CPH_INTERLOCKED_INCREMENT_ROUTINE(T) CPH_INTERLOCKED_INCREMENT_ROUTINE_NAME(T) #define CPH_INDEX_INLINE_ROUTINE(T) CPH_INDEX_INLINE_ROUTINE_NAME(T) +#define CPH_INDEX_BSEARCH_INLINE_ROUTINE(T) CPH_INDEX_BSEARCH_INLINE_ROUTINE_NAME(T) #define CPH_LOOKUP_INLINE_ROUTINE(T) CPH_LOOKUP_INLINE_ROUTINE_NAME(T) #define CPH_INSERT_INLINE_ROUTINE(T) CPH_INSERT_INLINE_ROUTINE_NAME(T) #define CPH_DELETE_INLINE_ROUTINE(T) CPH_DELETE_INLINE_ROUTINE_NAME(T) +#define CPH_INTERLOCKED_INCREMENT_INLINE_ROUTINE(T) CPH_INTERLOCKED_INCREMENT_INLINE_ROUTINE_NAME(T) #endif #define EXPAND_SEED1(U) CPH_SEED1(U) @@ -391,25 +449,34 @@ extern BENCHMARK_INDEX_COMPILED_PERFECT_HASH_TABLE \ #define EXPAND_INDEX_ROUTINE(T) CPH_INDEX_ROUTINE(T) #define EXPAND_INDEX_IACA_ROUTINE(T) CPH_INDEX_IACA_ROUTINE(T) +#define EXPAND_INDEX_BSEARCH_ROUTINE(T) CPH_INDEX_BSEARCH_ROUTINE(T) #define EXPAND_LOOKUP_ROUTINE(T) CPH_LOOKUP_ROUTINE(T) #define EXPAND_INSERT_ROUTINE(T) CPH_INSERT_ROUTINE(T) #define EXPAND_DELETE_ROUTINE(T) CPH_DELETE_ROUTINE(T) +#define EXPAND_INTERLOCKED_INCREMENT_ROUTINE(T) CPH_INTERLOCKED_INCREMENT_ROUTINE(T) #define EXPAND_INDEX_INLINE_ROUTINE(T) CPH_INDEX_INLINE_ROUTINE(T) +#define EXPAND_INDEX_BSEARCH_INLINE_ROUTINE(T) CPH_INDEX_BSEARCH_INLINE_ROUTINE(T) #define EXPAND_LOOKUP_INLINE_ROUTINE(T) CPH_LOOKUP_INLINE_ROUTINE(T) #define EXPAND_INSERT_INLINE_ROUTINE(T) CPH_INSERT_INLINE_ROUTINE(T) #define EXPAND_DELETE_INLINE_ROUTINE(T) CPH_DELETE_INLINE_ROUTINE(T) +#define EXPAND_INTERLOCKED_INCREMENT_INLINE_ROUTINE(T) CPH_INTERLOCKED_INCREMENT_INLINE_ROUTINE(T) +#define EXPAND_INDEX_BSEARCH_INLINE_ROUTINE(T) CPH_INDEX_BSEARCH_INLINE_ROUTINE(T) #define EXPAND_INDEX_ROUTINE_HEADER(T) CPH_INDEX_ROUTINE_HEADER(T) #define EXPAND_INDEX_IACA_ROUTINE_HEADER(T) CPH_INDEX_IACA_ROUTINE_HEADER(T) +#define EXPAND_INDEX_BSEARCH_ROUTINE_HEADER(T) CPH_INDEX_BSEARCH_ROUTINE_HEADER(T) #define EXPAND_LOOKUP_ROUTINE_HEADER(T) CPH_LOOKUP_ROUTINE_HEADER(T) #define EXPAND_INSERT_ROUTINE_HEADER(T) CPH_INSERT_ROUTINE_HEADER(T) #define EXPAND_DELETE_ROUTINE_HEADER(T) CPH_DELETE_ROUTINE_HEADER(T) +#define EXPAND_INTERLOCKED_INCREMENT_ROUTINE_HEADER(T) CPH_INTERLOCKED_INCREMENT_ROUTINE_HEADER(T) #define EXPAND_INDEX_INLINE_ROUTINE_HEADER(T) CPH_INDEX_INLINE_ROUTINE_HEADER(T) +#define EXPAND_INDEX_BSEARCH_INLINE_ROUTINE_HEADER(T) CPH_INDEX_BSEARCH_INLINE_ROUTINE_HEADER(T) #define EXPAND_LOOKUP_INLINE_ROUTINE_HEADER(T) CPH_LOOKUP_INLINE_ROUTINE_HEADER(T) #define EXPAND_INSERT_INLINE_ROUTINE_HEADER(T) CPH_INSERT_INLINE_ROUTINE_HEADER(T) #define EXPAND_DELETE_INLINE_ROUTINE_HEADER(T) CPH_DELETE_INLINE_ROUTINE_HEADER(T) +#define EXPAND_INTERLOCKED_INCREMENT_INLINE_ROUTINE_HEADER(T) CPH_INTERLOCKED_INCREMENT_INLINE_ROUTINE_HEADER(T) #define EXPAND_TEST_CPH_ROUTINE_HEADER(T) TEST_CPH_ROUTINE_HEADER(T) #define EXPAND_BENCHMARK_FULL_CPH_ROUTINE_HEADER(T) BENCHMARK_FULL_CPH_ROUTINE_HEADER(T) @@ -503,20 +570,27 @@ extern BENCHMARK_INDEX_COMPILED_PERFECT_HASH_TABLE \ #define ROTATE_KEY_RIGHT EXPAND_ROTATE_KEY_RIGHT(CPH_TABLENAME_UPPER) #define DECLARE_INDEX_ROUTINE_HEADER() EXPAND_INDEX_ROUTINE_HEADER(CPH_TABLENAME) +#define DECLARE_INDEX_BSEARCH_ROUTINE_HEADER() EXPAND_INDEX_BSEARCH_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_INDEX_IACA_ROUTINE_HEADER() EXPAND_INDEX_IACA_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_LOOKUP_ROUTINE_HEADER() EXPAND_LOOKUP_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_INSERT_ROUTINE_HEADER() EXPAND_INSERT_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_DELETE_ROUTINE_HEADER() EXPAND_DELETE_ROUTINE_HEADER(CPH_TABLENAME) +#define DECLARE_INTERLOCKED_INCREMENT_ROUTINE_HEADER() EXPAND_INTERLOCKED_INCREMENT_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_INDEX_INLINE_ROUTINE_HEADER() EXPAND_INDEX_INLINE_ROUTINE_HEADER(CPH_TABLENAME) +#define DECLARE_INDEX_BSEARCH_ROUTINE_HEADER() EXPAND_INDEX_BSEARCH_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_LOOKUP_INLINE_ROUTINE_HEADER() EXPAND_LOOKUP_INLINE_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_INSERT_INLINE_ROUTINE_HEADER() EXPAND_INSERT_INLINE_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_DELETE_INLINE_ROUTINE_HEADER() EXPAND_DELETE_INLINE_ROUTINE_HEADER(CPH_TABLENAME) +#define DECLARE_INTERLOCKED_INCREMENT_INLINE_ROUTINE_HEADER() \ + EXPAND_INTERLOCKED_INCREMENT_INLINE_ROUTINE_HEADER(CPH_TABLENAME) #define INDEX_INLINE_ROUTINE EXPAND_INDEX_INLINE_ROUTINE(CPH_TABLENAME) +#define INDEX_BSEARCH_INLINE_ROUTINE EXPAND_INDEX_BSEARCH_INLINE_ROUTINE(CPH_TABLENAME) #define LOOKUP_INLINE_ROUTINE EXPAND_LOOKUP_INLINE_ROUTINE(CPH_TABLENAME) #define INSERT_INLINE_ROUTINE EXPAND_INSERT_INLINE_ROUTINE(CPH_TABLENAME) #define DELETE_INLINE_ROUTINE EXPAND_DELETE_INLINE_ROUTINE(CPH_TABLENAME) +#define INTERLOCKED_INCREMENT_INLINE_ROUTINE EXPAND_INTERLOCKED_INCREMENT_INLINE_ROUTINE(CPH_TABLENAME) #define TEST_CPH_ROUTINE EXPAND_TEST_CPH_ROUTINE_NAME(CPH_TABLENAME) #define BENCHMARK_FULL_CPH_ROUTINE EXPAND_BENCHMARK_FULL_CPH_ROUTINE_NAME(CPH_TABLENAME) @@ -529,8 +603,3 @@ extern BENCHMARK_INDEX_COMPILED_PERFECT_HASH_TABLE \ #define DEFINE_TABLE_ROUTINES() EXPAND_DEFINE_TABLE_ROUTINES(CPH_TABLENAME) #define DEFINE_TEST_AND_BENCHMARKING_ROUTINES() EXPAND_DEFINE_TEST_AND_BENCHMARKING_ROUTINES(CPH_TABLENAME) - -// -// End CompiledPerfectHashMacroGlue.h. -// - From 96dae7d6756e028e761fd962b96e5cf206135925 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Tue, 3 Jan 2023 15:29:49 -0800 Subject: [PATCH 4/5] Add new function hooking capabilities to PerfectHash. --- include/PerfectHash.h | 158 ++++- include/PerfectHashErrors.h | 191 ++++- include/PerfectHashEvents.h | 91 ++- python/perfecthash/analysis.py | 51 ++ python/perfecthash/config.py | 16 + python/perfecthash/dumpbin.py | 123 +++- python/perfecthash/sourcefile.py | 30 +- python/perfecthash/util.py | 8 + .../CompiledPerfectHashTableRoutines.c | 45 ++ .../CompiledPerfectHashTableRoutinesPost.c | 8 + .../CompiledPerfectHashTableRoutinesPre.c | 9 + src/PerfectHash/Chm01.c | 202 +++++- src/PerfectHash/Chm01FileWork.h | 3 +- src/PerfectHash/Chm01FileWorkCHeaderFile.c | 50 +- .../Chm01FileWorkCSourceKeysFile.c | 22 +- .../Chm01FileWorkCSourceTableDataFile.c | 8 +- .../Chm01FileWorkCSourceTableValuesFile.c | 25 +- .../Chm01FileWorkMakefileSoMkFile.c | 4 +- src/PerfectHash/Chm01FileWorkModuleDefFile.c | 104 +++ ...m01FileWorkVCProjectBenchmarkFullExeFile.c | 10 +- ...01FileWorkVCProjectBenchmarkIndexExeFile.c | 10 +- .../Chm01FileWorkVCProjectDllFile.c | 53 +- .../Chm01FileWorkVCProjectTestExeFile.c | 10 +- src/PerfectHash/Chunk.c | 33 +- src/PerfectHash/Chunk.h | 31 +- ...dPerfectHashMacroGlue_CHeader_RawCString.h | 92 ++- ...HashTableRoutinesPost_CSource_RawCString.h | 8 + ...tHashTableRoutinesPre_CSource_RawCString.h | 9 + ...fectHashTableRoutines_CSource_RawCString.h | 45 ++ .../CompiledPerfectHash_CHeader_RawCString.h | 35 +- src/PerfectHash/Component.c | 4 +- src/PerfectHash/ExtractArg.c | 36 +- src/PerfectHash/Graph.c | 8 +- src/PerfectHash/PerfectHash.vcxproj | 11 +- src/PerfectHash/PerfectHash.vcxproj.filters | 5 +- src/PerfectHash/PerfectHashConstants.c | 11 +- src/PerfectHash/PerfectHashConstants.h | 5 +- src/PerfectHash/PerfectHashContext.c | 664 +++++++++++++++++- src/PerfectHash/PerfectHashContext.h | 91 ++- .../PerfectHashContextBulkCreate.c | 39 +- src/PerfectHash/PerfectHashContextSelfTest.c | 16 +- .../PerfectHashContextTableCreate.c | 38 +- src/PerfectHash/PerfectHashDirectory.c | 6 +- src/PerfectHash/PerfectHashErrors.dbg | 15 + src/PerfectHash/PerfectHashErrors.mc | 186 ++++- src/PerfectHash/PerfectHashErrorsTEMP.BIN | Bin 27494 -> 28326 bytes src/PerfectHash/PerfectHashErrors_English.bin | Bin 94380 -> 100760 bytes src/PerfectHash/PerfectHashEvents.man | 44 ++ src/PerfectHash/PerfectHashFile.c | 6 +- src/PerfectHash/PerfectHashFileWork.h | 22 +- src/PerfectHash/PerfectHashPath.c | 4 +- src/PerfectHash/PerfectHashPrivate.h | 24 +- src/PerfectHash/PerfectHashTable.h | 6 +- src/PerfectHash/PerfectHashTableCompile.c | 4 +- src/PerfectHash/PerfectHashTableCreate.c | 16 +- src/PerfectHash/PerfectHashTableLoad.c | 4 +- src/PerfectHash/PerfectHashTls.h | 5 +- src/PerfectHash/Rtl.h | 18 +- src/PerfectHash/RtlOutput.c | 35 +- src/PerfectHash/RtlOutput.h | 63 +- src/PerfectHash/dllmain.c | 10 +- src/PerfectHash/stdafx.h | 4 +- .../PerfectHashBulkCreateExe.c | 14 +- .../PerfectHashCreateExe.c | 14 +- src/build-pgo-avx.bat | 28 + src/build-pgo-avx2.bat | 28 + src/build-pgo-avx512.bat | 28 + src/build-pgo-hooked.bat | 28 + src/build-release-hooked.bat | 1 + src/build-release.bat | 1 + 70 files changed, 2800 insertions(+), 226 deletions(-) create mode 100644 src/PerfectHash/Chm01FileWorkModuleDefFile.c create mode 100644 src/build-pgo-avx.bat create mode 100644 src/build-pgo-avx2.bat create mode 100644 src/build-pgo-avx512.bat create mode 100644 src/build-pgo-hooked.bat create mode 100644 src/build-release-hooked.bat create mode 100644 src/build-release.bat diff --git a/include/PerfectHash.h b/include/PerfectHash.h index 596713d9..6656b247 100644 --- a/include/PerfectHash.h +++ b/include/PerfectHash.h @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -2517,7 +2517,7 @@ IsValidContextTableCreateFlags( typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { - struct _Struct_size_bytes_(sizeof(ULONG)) { + struct _Struct_size_bytes_(sizeof(ULONGLONG)) { // // When set, disables the default "first solved graph wins" behavior @@ -2539,7 +2539,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // the best memory coverage behavior. // - ULONG FindBestGraph:1; + ULONGLONG FindBestGraph:1; // // When set, skips the internal graph verfication check that ensures a @@ -2558,7 +2558,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // correctly detects that invalid solutions are being generated. // - ULONG SkipGraphVerification:1; + ULONGLONG SkipGraphVerification:1; // // When set, indicates that the resulting table will not be used after @@ -2574,7 +2574,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // like Index() etc will result in an access violation. // - ULONG CreateOnly:1; + ULONGLONG CreateOnly:1; // // When set, tries to allocate the table data using large pages. This @@ -2584,7 +2584,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // Analogous to TryLargePagesForTableData flag in table load flags. // - ULONG TryLargePagesForTableData:1; + ULONGLONG TryLargePagesForTableData:1; // // When set, tries to allocate the values array using large pages. @@ -2594,7 +2594,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // Analogous to TryLargePagesForValuesArray flag in table load flags. // - ULONG TryLargePagesForValuesArray:1; + ULONGLONG TryLargePagesForValuesArray:1; // // When set, uses any previous table size information associated with @@ -2602,7 +2602,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // and masking type. // - ULONG UsePreviousTableSize:1; + ULONGLONG UsePreviousTableSize:1; // // When set, incorporates the number of table resize events encountered @@ -2610,14 +2610,14 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // name. // - ULONG IncludeNumberOfTableResizeEventsInOutputPath:1; + ULONGLONG IncludeNumberOfTableResizeEventsInOutputPath:1; // // When set, incorporates the number of table elements (i.e. the size) // of the winning perfect hash solution into the final output name. // - ULONG IncludeNumberOfTableElementsInOutputPath:1; + ULONGLONG IncludeNumberOfTableElementsInOutputPath:1; // // When set, disables all file work (I/O). This will prevent generation @@ -2625,7 +2625,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // supporting source files for the compiled perfect hash table. // - ULONG NoFileIo:1; + ULONGLONG NoFileIo:1; // // When set, does not print any console output related to table creation @@ -2634,7 +2634,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // N.B. Incompatible with flag Quiet. // - ULONG Silent:1; + ULONGLONG Silent:1; // // Enables redundant checks in the routine that determines whether or @@ -2643,42 +2643,42 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // the graph solving logic, though. // - ULONG Paranoid:1; + ULONGLONG Paranoid:1; // // Skips calculating assigned memory coverage when in "first graph wins" // mode. // - ULONG SkipMemoryCoverageInFirstGraphWinsMode:1; + ULONGLONG SkipMemoryCoverageInFirstGraphWinsMode:1; // // When set, tries to allocate the edge and vertex arrays used by graphs // during solving using large pages. // - ULONG TryLargePagesForGraphEdgeAndVertexArrays:1; + ULONGLONG TryLargePagesForGraphEdgeAndVertexArrays:1; // // When set, tries to allocate the table data used by graphs during // solving using large pages. // - ULONG TryLargePagesForGraphTableData:1; + ULONGLONG TryLargePagesForGraphTableData:1; // // When set, omits writing a row in the applicable .csv file if table // creation failed. // - ULONG OmitCsvRowIfTableCreateFailed:1; + ULONGLONG OmitCsvRowIfTableCreateFailed:1; // // When set, omits writing a row in the applicable .csv file if table // creation succeeded. // - ULONG OmitCsvRowIfTableCreateSucceeded:1; + ULONGLONG OmitCsvRowIfTableCreateSucceeded:1; // // When set, causes the C preprocessor macro CPH_INDEX_ONLY to be @@ -2691,7 +2691,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // table values independently. // - ULONG IndexOnly:1; + ULONGLONG IndexOnly:1; // // When set, uses a shared read-write section for the table values @@ -2701,7 +2701,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // N.B. Has no effect if --IndexOnly is also specified. // - ULONG UseRwsSectionForTableValues:1; + ULONGLONG UseRwsSectionForTableValues:1; // // When set, uses implementations of RtlCopyPages and RtlFillPages that @@ -2710,13 +2710,13 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // more info. // - ULONG UseNonTemporalAvx2Routines:1; + ULONGLONG UseNonTemporalAvx2Routines:1; // // When set, disables writing the output .csv file. // - ULONG DisableCsvOutputFile:1; + ULONGLONG DisableCsvOutputFile:1; // // When set, clamps the number of edges to always be equal to the @@ -2732,7 +2732,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // And masking (i.e. not modulus masking). // - ULONG ClampNumberOfEdges:1; + ULONGLONG ClampNumberOfEdges:1; // // When set, uses the original (slower) seeded hash routines (the ones @@ -2743,7 +2743,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // N.B. This flag is incompatible with HashAllKeysFirst. // - ULONG UseOriginalSeededHashRoutines:1; + ULONGLONG UseOriginalSeededHashRoutines:1; // // When set, changes the graph solving logic such that vertices (i.e. @@ -2753,7 +2753,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // N.B. This flag is incompatible with UseOriginalSeededHashRoutines. // - ULONG HashAllKeysFirst:1; + ULONGLONG HashAllKeysFirst:1; // // When set, allocates the memory for the vertex pairs array with @@ -2763,7 +2763,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // TryLargePagesForVertexPairs. // - ULONG EnableWriteCombineForVertexPairs:1; + ULONGLONG EnableWriteCombineForVertexPairs:1; // // When set, automatically changes the page protection of the vertex @@ -2774,7 +2774,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // and HashAllKeysFirst is set. // - ULONG RemoveWriteCombineAfterSuccessfulHashKeys:1; + ULONGLONG RemoveWriteCombineAfterSuccessfulHashKeys:1; // // When set, tries to allocate the array for vertex pairs using large @@ -2784,7 +2784,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // EnableWriteCombineForVertexPairs. // - ULONG TryLargePagesForVertexPairs:1; + ULONGLONG TryLargePagesForVertexPairs:1; // // When set, if a non-zero value is available in the table's predicted @@ -2793,14 +2793,14 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // dispatching parallel graph solving attempts. // - ULONG TryUsePredictedAttemptsToLimitMaxConcurrency:1; + ULONGLONG TryUsePredictedAttemptsToLimitMaxConcurrency:1; // // When set, if uses a random seed obtained from the operating system to // initialize the selected RNG. Requires --Rng. // - ULONG RngUseRandomStartSeed:1; + ULONGLONG RngUseRandomStartSeed:1; // // When set, tries to use optimized AVX2 routines for hashing keys, if @@ -2811,7 +2811,7 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // N.B. Currently only implemented for the MultiplyShiftR hash function. // - ULONG TryUseAvx2HashFunction:1; + ULONGLONG TryUseAvx2HashFunction:1; // // When set, tries to use optimized AVX512 routines for hashing keys, if @@ -2822,14 +2822,14 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // N.B. Currently only implemented for the MultiplyShiftR hash function. // - ULONG TryUseAvx512HashFunction:1; + ULONGLONG TryUseAvx512HashFunction:1; // // When set, disables automatically using the AVX2 version of the // calculate memory coverage routine. // - ULONG DoNotTryUseAvx2MemoryCoverageFunction:1; + ULONGLONG DoNotTryUseAvx2MemoryCoverageFunction:1; // // When set, disables the best graph console output and only prints the @@ -2838,18 +2838,34 @@ typedef union _PERFECT_HASH_TABLE_CREATE_FLAGS { // N.B. Incompatible with flag Silent. // - ULONG Quiet:1; + ULONGLONG Quiet:1; + + // + // When set, includes the table keys in the generated compiled perfect + // hash table DLL. + // + + ULONGLONG IncludeKeysInCompiledDll:1; + + // + // When set, disables saving the table values if function hooking is + // active and the callback DLL has a TableValues export (which it will + // if it's a perfect hash compiled DLL). + // + + ULONGLONG DisableSavingCallbackTableValues:1; // - // No more unused bits! Create PERFECT_HASH_TABLE_CREATE_FLAGS2 (or - // make this a ULONGLONG) when more flags are needed. + // Unused bits. // + + ULONGLONG Unused:30; }; - LONG AsLong; - ULONG AsULong; + LONGLONG AsLongLong; + ULONGLONG AsULongLong; } PERFECT_HASH_TABLE_CREATE_FLAGS; -C_ASSERT(sizeof(PERFECT_HASH_TABLE_CREATE_FLAGS) == sizeof(ULONG)); +C_ASSERT(sizeof(PERFECT_HASH_TABLE_CREATE_FLAGS) == sizeof(ULONGLONG)); typedef PERFECT_HASH_TABLE_CREATE_FLAGS *PPERFECT_HASH_TABLE_CREATE_FLAGS; @@ -3251,6 +3267,9 @@ typedef RNG_VTBL *PRNG_VTBL; ENTRY(Seed3Byte2MaskCounts) \ ENTRY(MaxSolveTimeInSeconds) \ ENTRY(AutoResizeWhenKeysToEdgesRatioExceeds) \ + ENTRY(FunctionHookCallbackDllPath) \ + ENTRY(FunctionHookCallbackFunctionName) \ + ENTRY(FunctionHookCallbackIgnoreRip) \ LAST_ENTRY(Remark) #define TABLE_CREATE_PARAMETER_TABLE_ENTRY(ENTRY) \ @@ -3461,6 +3480,7 @@ typedef struct _PERFECT_HASH_TABLE_CREATE_PARAMETER { ULONGLONG AsULongLong; LARGE_INTEGER AsLargeInteger; ULARGE_INTEGER AsULargeInteger; + STRING AsString; UNICODE_STRING AsUnicodeString; TP_CALLBACK_PRIORITY AsTpCallbackPriority; PERFECT_HASH_RNG_ID AsRngId; @@ -4122,6 +4142,66 @@ typedef __SECURITY_INIT_COOKIE *P__SECURITY_INIT_COOKIE; extern __SECURITY_INIT_COOKIE __security_init_cookie; +// +// _penter function hooking scaffolding. +// + +typedef +VOID +(NTAPI FUNCTION_ENTRY_CALLBACK)( + _In_ PVOID ReturnRip, + _In_ PVOID Context + ); +typedef FUNCTION_ENTRY_CALLBACK *PFUNCTION_ENTRY_CALLBACK; + +typedef +VOID +(NTAPI SET_FUNCTION_ENTRY_CALLBACK)( + _In_ PFUNCTION_ENTRY_CALLBACK Callback, + _In_ PVOID Context, + _In_ PVOID ModuleBaseAddress, + _In_ ULONG ModuleSizeInBytes, + _In_ ULONG IgnoreRip + ); +typedef SET_FUNCTION_ENTRY_CALLBACK *PSET_FUNCTION_ENTRY_CALLBACK; + +typedef +VOID +(NTAPI GET_FUNCTION_ENTRY_CALLBACK)( + _Out_ PFUNCTION_ENTRY_CALLBACK *Callback, + _Out_ PVOID *Context, + _Out_ PVOID *ModuleBaseAddress, + _Out_ ULONG *ModuleSizeInBytes, + _Out_ ULONG *IgnoreRip + ); +typedef GET_FUNCTION_ENTRY_CALLBACK *PGET_FUNCTION_ENTRY_CALLBACK; + +typedef +VOID +(NTAPI CLEAR_FUNCTION_ENTRY_CALLBACK)( + _Out_opt_ PFUNCTION_ENTRY_CALLBACK *Callback, + _Out_opt_ PVOID *Context, + _Out_opt_ PVOID *ModuleBaseAddress, + _Out_opt_ ULONG *ModuleSizeInBytes, + _Out_opt_ ULONG *IgnoreRip + ); +typedef CLEAR_FUNCTION_ENTRY_CALLBACK *PCLEAR_FUNCTION_ENTRY_CALLBACK; + +typedef +BOOLEAN +(NTAPI IS_FUNCTION_ENTRY_CALLBACK_ENABLED)( + VOID + ); +typedef IS_FUNCTION_ENTRY_CALLBACK_ENABLED + *PIS_FUNCTION_ENTRY_CALLBACK_ENABLED; + +typedef +VOID +(_PENTER)( + VOID + ); +typedef _PENTER *P_PENTER; + // // Define bootstrap helpers. // diff --git a/include/PerfectHashErrors.h b/include/PerfectHashErrors.h index fd5caa3f..d7b911a5 100644 --- a/include/PerfectHashErrors.h +++ b/include/PerfectHashErrors.h @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -162,6 +162,15 @@ Module Name: // #define PH_S_FIXED_ATTEMPTS_REACHED ((HRESULT)0x2004000BL) +// +// MessageId: PH_S_FUNCTION_HOOK_CALLBACK_DLL_INITIALIZED +// +// MessageText: +// +// Function hook callback DLL initialized. +// +#define PH_S_FUNCTION_HOOK_CALLBACK_DLL_INITIALIZED ((HRESULT)0x2004000CL) + //////////////////////////////////////////////////////////////////////////////// // PH_SEVERITY_INFORMATIONAL @@ -941,6 +950,22 @@ Module Name: // Supplies the maximum number of seconds to try and solve an individual // graph. // +// --FunctionHookCallbackDllPath= +// +// Supplies a fully-qualified path to a .dll file that will be used as the +// callback handler for hooked functions. +// +// --FunctionHookCallbackFunctionName= +// +// Supplies the exported function name to resolve from the callback module +// (above) and use as the callback for hooked functions. The default is +// InterlockedIncrement. +// +// --FunctionHookCallbackIgnoreRip= +// +// Supplies a relative RIP to ignore during function callback. That is, +// if a caller matches the supplied relative RIP, the function callback +// will not be executed. // // Console Output Character Legend // @@ -1016,10 +1041,55 @@ Module Name: // // MessageText: // -// [s] Status [q] Quit solving this graph [r] Force table resize [v] Toggle quiet +// [r] Refresh [f] Finish [e] Resize [c] Toggle Callback [?] More Help // #define PH_MSG_PERFECT_HASH_CONSOLE_KEYS_HELP ((HRESULT)0x60040104L) +// +// MessageId: PH_MSG_PERFECT_HASH_CONSOLE_KEYS_MORE_HELP +// +// MessageText: +// +// [r] Refresh +// +// Press this at any time to refresh the current solving status for a given +// graph. +// +// [f] Finish +// +// Finish solving the current table. If in bulk create mode, this only applies +// to the active table; subsequent tables will still be processed. +// +// When in find best graph mode, It is safe to "finish" a table prior to it +// hitting the target coverage goal; i.e. the best graph solved at that time +// will be the winner for which all the usual post-processing (i.e. writing +// the output files, testing, etc.) will occur. +// +// If no solution has been found at all when finish is pressed, this is just +// treated as a failure to solve the table. If in bulk create mode, the next +// table will be handled normally. +// +// [e] Resize +// +// Force a table resize event. This immediately stops graph solving and +// requests the next size up table (i.e. the next power of two up for the +// number of edges), then resumes solving with a new table size. You can +// view the impact of resizes via the "Number of Table Resize Events:" in +// the console output. +// +// [c] Toggle Callback +// +// If a hooked version of PerfectHash.dll is running, and a function hook +// callback DLL has been configured via --FunctionHookCallbackDllPath, this +// command allows you to quickly toggle the callback on and off and observe +// the immediate performance impact in the console via the "Current Attempts +// Per Second" metric. +// +// If function hooking is active, this doesn't do anything. +// +// +#define PH_MSG_PERFECT_HASH_CONSOLE_KEYS_MORE_HELP ((HRESULT)0x60040105L) + //////////////////////////////////////////////////////////////////////////////// // PH_SEVERITY_FAIL @@ -4159,3 +4229,120 @@ Module Name: // #define PH_E_SILENT_INCOMPATIBLE_WITH_QUIET ((HRESULT)0xE00403D7L) +// +// MessageId: PH_E_FAILED_TO_LOAD_FUNCTION_HOOK_CALLBACK_DLL +// +// MessageText: +// +// Failed to load the provided function hook callback DLL. +// +#define PH_E_FAILED_TO_LOAD_FUNCTION_HOOK_CALLBACK_DLL ((HRESULT)0xE00403D8L) + +// +// MessageId: PH_E_FAILED_TO_GET_ADDRESS_OF_FUNCTION_HOOK_CALLBACK +// +// MessageText: +// +// Failed to obtain the address of the requested function from the function hook callback DLL. +// +#define PH_E_FAILED_TO_GET_ADDRESS_OF_FUNCTION_HOOK_CALLBACK ((HRESULT)0xE00403D9L) + +// +// MessageId: PH_E_FAILED_TO_GET_ADDRESS_OF_SET_FUNCTION_ENTRY_CALLBACK +// +// MessageText: +// +// Failed to obtain the address of SetFunctionEntryCallback from FunctionHook.dll. +// +#define PH_E_FAILED_TO_GET_ADDRESS_OF_SET_FUNCTION_ENTRY_CALLBACK ((HRESULT)0xE00403DAL) + +// +// MessageId: PH_E_FAILED_TO_GET_ADDRESS_OF_CLEAR_FUNCTION_ENTRY_CALLBACK +// +// MessageText: +// +// Failed to obtain the address of ClearFunctionEntryCallback from FunctionHook.dll. +// +#define PH_E_FAILED_TO_GET_ADDRESS_OF_CLEAR_FUNCTION_ENTRY_CALLBACK ((HRESULT)0xE00403DBL) + +// +// MessageId: PH_E_FAILED_TO_LOAD_FUNCTION_HOOK_DLL +// +// MessageText: +// +// Failed to load the function hook DLL. +// +#define PH_E_FAILED_TO_LOAD_FUNCTION_HOOK_DLL ((HRESULT)0xE00403DCL) + +// +// MessageId: PH_E_INVALID_NUMBER_OF_CONDITIONALS +// +// MessageText: +// +// Encountered incorrect number of conditionals during chunk processing. +// +#define PH_E_INVALID_NUMBER_OF_CONDITIONALS ((HRESULT)0xE00403DDL) + +// +// MessageId: PH_E_NUMBER_OF_CONDITIONALS_MISMATCHED +// +// MessageText: +// +// Encountered mismatched number of conditionals during chunk processing. +// +#define PH_E_NUMBER_OF_CONDITIONALS_MISMATCHED ((HRESULT)0xE00403DEL) + +// +// MessageId: PH_E_ERROR_DURING_CLOSE_MODULE_DEF_FILE +// +// MessageText: +// +// Error closing module def file. +// +#define PH_E_ERROR_DURING_CLOSE_MODULE_DEF_FILE ((HRESULT)0xE00403DFL) + +// +// MessageId: PH_E_ERROR_DURING_PREPARE_MODULE_DEF_FILE +// +// MessageText: +// +// Error preparing module def file. +// +#define PH_E_ERROR_DURING_PREPARE_MODULE_DEF_FILE ((HRESULT)0xE00403E0L) + +// +// MessageId: PH_E_ERROR_DURING_SAVE_MODULE_DEF_FILE +// +// MessageText: +// +// Error saving module def file. +// +#define PH_E_ERROR_DURING_SAVE_MODULE_DEF_FILE ((HRESULT)0xE00403E1L) + +// +// MessageId: PH_E_STRING_BUFFER_TOO_SMALL +// +// MessageText: +// +// A provided string buffer was too small to carry out the requested operation. +// +#define PH_E_STRING_BUFFER_TOO_SMALL ((HRESULT)0xE00403E2L) + +// +// MessageId: PH_E_FAILED_TO_GET_ADDRESS_OF_GET_FUNCTION_ENTRY_CALLBACK +// +// MessageText: +// +// Failed to obtain the address of GetFunctionEntryCallback from FunctionHook.dll. +// +#define PH_E_FAILED_TO_GET_ADDRESS_OF_GET_FUNCTION_ENTRY_CALLBACK ((HRESULT)0xE00403E3L) + +// +// MessageId: PH_E_FAILED_TO_GET_ADDRESS_OF_IS_FUNCTION_ENTRY_CALLBACK_ENABLED +// +// MessageText: +// +// Failed to obtain the address of IsFunctionEntryCallbackEnabled from FunctionHook.dll. +// +#define PH_E_FAILED_TO_GET_ADDRESS_OF_IS_FUNCTION_ENTRY_CALLBACK_ENABLED ((HRESULT)0xE00403E4L) + diff --git a/include/PerfectHashEvents.h b/include/PerfectHashEvents.h index 82d03cdc..4bd10363 100644 --- a/include/PerfectHashEvents.h +++ b/include/PerfectHashEvents.h @@ -659,7 +659,7 @@ Routine Description: #endif // MCGEN_DISABLE_PROVIDER_CODE_GENERATION //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -// Provider "PerfectHash" event count 13 +// Provider "PerfectHash" event count 15 //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // Provider GUID = d0b3028e-70a7-410f-af7e-4d495b4a3c8b @@ -687,6 +687,8 @@ EXTERN_C __declspec(selectany) const GUID PerfectHashEvents = {0xd0b3028e, 0x70a #define PerfectHashEvents_TASK_GenerateRandomBytes 0x8 #define PerfectHashEvents_TASK_IsAcyclic 0x9 #define PerfectHashEvents_TASK_MemoryCoverageCacheLineCounts 0xa +#define PerfectHashEvents_TASK_FunctionEntry 0xb +#define PerfectHashEvents_TASK_PerfectHashIndex32 0xc // // Keyword @@ -699,6 +701,8 @@ EXTERN_C __declspec(selectany) const GUID PerfectHashEvents = {0xd0b3028e, 0x70a #define PH_ETW_RTL_RANDOM 0x20 #define PH_ETW_GRAPH_IS_ACYCLIC 0x40 #define PH_ETW_GRAPH_MEMORY_COVERAGE_CACHE_LINE_COUNTS 0x80 +#define PH_ETW_FUNCTION_ENTRY 0x100 +#define PH_ETW_INDEX32 0x200 // // Event Descriptors @@ -729,6 +733,10 @@ EXTERN_C __declspec(selectany) const EVENT_DESCRIPTOR GraphIsAcyclicEvent = {0xb #define GraphIsAcyclicEvent_value 0xb EXTERN_C __declspec(selectany) const EVENT_DESCRIPTOR GraphMemoryCoverageCacheLineCountsEvent = {0xc, 0x0, 0x10, 0x4, 0x0, 0xa, 0x8000000000000080}; #define GraphMemoryCoverageCacheLineCountsEvent_value 0xc +EXTERN_C __declspec(selectany) const EVENT_DESCRIPTOR FunctionEntryEvent = {0xd, 0x0, 0x10, 0x4, 0x0, 0xb, 0x8000000000000100}; +#define FunctionEntryEvent_value 0xd +EXTERN_C __declspec(selectany) const EVENT_DESCRIPTOR PerfectHashIndex32Event = {0xe, 0x0, 0x10, 0x4, 0x0, 0xc, 0x8000000000000200}; +#define PerfectHashIndex32Event_value 0xe // // MCGEN_DISABLE_PROVIDER_CODE_GENERATION macro: @@ -742,13 +750,13 @@ EXTERN_C __declspec(selectany) const EVENT_DESCRIPTOR GraphMemoryCoverageCacheLi // These variables are for use by MC-generated code and should not be used directly. // EXTERN_C __declspec(selectany) DECLSPEC_CACHEALIGN ULONG PerfectHashEnableBits[1]; -EXTERN_C __declspec(selectany) const ULONGLONG PerfectHashKeywords[8] = {0x8000000000000001, 0x8000000000000002, 0x8000000000000004, 0x8000000000000008, 0x8000000000000010, 0x8000000000000020, 0x8000000000000040, 0x8000000000000080}; -EXTERN_C __declspec(selectany) const unsigned char PerfectHashLevels[8] = {4, 4, 4, 4, 4, 4, 4, 4}; +EXTERN_C __declspec(selectany) const ULONGLONG PerfectHashKeywords[10] = {0x8000000000000001, 0x8000000000000002, 0x8000000000000004, 0x8000000000000008, 0x8000000000000010, 0x8000000000000020, 0x8000000000000040, 0x8000000000000080, 0x8000000000000100, 0x8000000000000200}; +EXTERN_C __declspec(selectany) const unsigned char PerfectHashLevels[10] = {4, 4, 4, 4, 4, 4, 4, 4, 4, 4}; // // Provider context // -EXTERN_C __declspec(selectany) MCGEN_TRACE_CONTEXT PerfectHashEvents_Context = {0, (ULONG_PTR)PerfectHashEvents_Traits, 0, 0, 0, 0, 0, 0, 8, PerfectHashEnableBits, PerfectHashKeywords, PerfectHashLevels}; +EXTERN_C __declspec(selectany) MCGEN_TRACE_CONTEXT PerfectHashEvents_Context = {0, (ULONG_PTR)PerfectHashEvents_Traits, 0, 0, 0, 0, 0, 0, 10, PerfectHashEnableBits, PerfectHashKeywords, PerfectHashLevels}; // // Provider REGHANDLE @@ -843,7 +851,7 @@ _mcgen_PASTE2(_mcgen_RegisterForContext_PerfectHash_, MCGEN_EVENTREGISTER)( { RtlZeroMemory(pContext, sizeof(*pContext)); pContext->Context.Logger = (ULONG_PTR)PerfectHashEvents_Traits; - pContext->Context.EnableBitsCount = 8; + pContext->Context.EnableBitsCount = 10; pContext->Context.EnableBitMask = pContext->EnableBits; pContext->Context.EnableKeyWords = PerfectHashKeywords; pContext->Context.EnableLevel = PerfectHashLevels; @@ -1164,6 +1172,52 @@ _mcgen_CheckContextType_PerfectHash(_In_ McGenContext_PerfectHash* pContext) // This macro is for use by MC-generated code and should not be used directly. #define _mcgen_TEMPLATE_FOR_GraphMemoryCoverageCacheLineCountsEvent _mcgen_PASTE2(McTemplateK0ziqquuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu_, MCGEN_EVENTWRITETRANSFER) +// +// Enablement check macro for event "FunctionEntryEvent" +// +#define EventEnabledFunctionEntryEvent() _mcgen_EVENT_BIT_SET(PerfectHashEnableBits, 8) +#define EventEnabledFunctionEntryEvent_ForContext(pContext) _mcgen_EVENT_BIT_SET(_mcgen_CheckContextType_PerfectHash(pContext)->EnableBits, 8) + +// +// Event write macros for event "FunctionEntryEvent" +// +#define EventWriteFunctionEntryEvent(Activity, FunctionAddress) \ + MCGEN_EVENT_ENABLED(FunctionEntryEvent) \ + ? _mcgen_TEMPLATE_FOR_FunctionEntryEvent(&PerfectHashEvents_Context, &FunctionEntryEvent, Activity, FunctionAddress) : 0 +#define EventWriteFunctionEntryEvent_AssumeEnabled(FunctionAddress) \ + _mcgen_TEMPLATE_FOR_FunctionEntryEvent(&PerfectHashEvents_Context, &FunctionEntryEvent, NULL, FunctionAddress) +#define EventWriteFunctionEntryEvent_ForContext(pContext, Activity, FunctionAddress) \ + MCGEN_EVENT_ENABLED_FORCONTEXT(pContext, FunctionEntryEvent) \ + ? _mcgen_TEMPLATE_FOR_FunctionEntryEvent(&(pContext)->Context, &FunctionEntryEvent, Activity, FunctionAddress) : 0 +#define EventWriteFunctionEntryEvent_ForContextAssumeEnabled(pContext, FunctionAddress) \ + _mcgen_TEMPLATE_FOR_FunctionEntryEvent(&_mcgen_CheckContextType_PerfectHash(pContext)->Context, &FunctionEntryEvent, NULL, FunctionAddress) + +// This macro is for use by MC-generated code and should not be used directly. +#define _mcgen_TEMPLATE_FOR_FunctionEntryEvent _mcgen_PASTE2(McTemplateK0p_, MCGEN_EVENTWRITETRANSFER) + +// +// Enablement check macro for event "PerfectHashIndex32Event" +// +#define EventEnabledPerfectHashIndex32Event() _mcgen_EVENT_BIT_SET(PerfectHashEnableBits, 9) +#define EventEnabledPerfectHashIndex32Event_ForContext(pContext) _mcgen_EVENT_BIT_SET(_mcgen_CheckContextType_PerfectHash(pContext)->EnableBits, 9) + +// +// Event write macros for event "PerfectHashIndex32Event" +// +#define EventWritePerfectHashIndex32Event(Activity, Key, Index) \ + MCGEN_EVENT_ENABLED(PerfectHashIndex32Event) \ + ? _mcgen_TEMPLATE_FOR_PerfectHashIndex32Event(&PerfectHashEvents_Context, &PerfectHashIndex32Event, Activity, Key, Index) : 0 +#define EventWritePerfectHashIndex32Event_AssumeEnabled(Key, Index) \ + _mcgen_TEMPLATE_FOR_PerfectHashIndex32Event(&PerfectHashEvents_Context, &PerfectHashIndex32Event, NULL, Key, Index) +#define EventWritePerfectHashIndex32Event_ForContext(pContext, Activity, Key, Index) \ + MCGEN_EVENT_ENABLED_FORCONTEXT(pContext, PerfectHashIndex32Event) \ + ? _mcgen_TEMPLATE_FOR_PerfectHashIndex32Event(&(pContext)->Context, &PerfectHashIndex32Event, Activity, Key, Index) : 0 +#define EventWritePerfectHashIndex32Event_ForContextAssumeEnabled(pContext, Key, Index) \ + _mcgen_TEMPLATE_FOR_PerfectHashIndex32Event(&_mcgen_CheckContextType_PerfectHash(pContext)->Context, &PerfectHashIndex32Event, NULL, Key, Index) + +// This macro is for use by MC-generated code and should not be used directly. +#define _mcgen_TEMPLATE_FOR_PerfectHashIndex32Event _mcgen_PASTE2(McTemplateK0qq_, MCGEN_EVENTWRITETRANSFER) + #endif // MCGEN_DISABLE_PROVIDER_CODE_GENERATION // @@ -1177,6 +1231,31 @@ _mcgen_CheckContextType_PerfectHash(_In_ McGenContext_PerfectHash* pContext) // Template Functions // +// +// Function for template "FunctionEntryTemplate_x64" (and possibly others). +// This function is for use by MC-generated code and should not be used directly. +// +#ifndef McTemplateK0p_def +#define McTemplateK0p_def +ETW_INLINE +ULONG +_mcgen_PASTE2(McTemplateK0p_, MCGEN_EVENTWRITETRANSFER)( + _In_ PMCGEN_TRACE_CONTEXT Context, + _In_ PCEVENT_DESCRIPTOR Descriptor, + _In_opt_ const GUID* Activity, + _In_opt_ const void* _Arg0 + ) +{ +#define McTemplateK0p_ARGCOUNT 1 + + EVENT_DATA_DESCRIPTOR EventData[McTemplateK0p_ARGCOUNT + 1]; + + EventDataDescCreate(&EventData[1],&_Arg0, sizeof(const void*) ); + + return McGenEventWrite(Context, Descriptor, Activity, McTemplateK0p_ARGCOUNT + 1, EventData); +} +#endif // McTemplateK0p_def + // // Function for template "GenerateRandomBytesStartTemplate" (and possibly others). // This function is for use by MC-generated code and should not be used directly. @@ -1968,6 +2047,8 @@ _mcgen_PASTE2(McTemplateK0zqqqqiiqqqqqqqq_, MCGEN_EVENTWRITETRANSFER)( #define MSG_PerfectHash_event_10_message 0xB000000AL #define MSG_PerfectHash_event_11_message 0xB000000BL #define MSG_PerfectHash_event_12_message 0xB000000CL +#define MSG_PerfectHash_event_13_message 0xB000000DL +#define MSG_PerfectHash_event_14_message 0xB000000EL #define MSG_PerfectHash_event_0_message 0xB0010000L #define MSG_PerfectHash_event_1_message 0xB0010001L #define MSG_PerfectHash_event_2_message 0xB0010002L diff --git a/python/perfecthash/analysis.py b/python/perfecthash/analysis.py index 971f6b51..58f0110b 100644 --- a/python/perfecthash/analysis.py +++ b/python/perfecthash/analysis.py @@ -3,6 +3,7 @@ #=============================================================================== from .path import ( dirname, + basename, join_path, ) @@ -1286,6 +1287,49 @@ 'CacheLine_63', 'CacheLine_64', ) + +# FunctionEntry + +FUNCTION_ENTRY = ( + 'PerfectHash/FunctionEntry/win:Info' +) + +FUNCTION_ENTRY_ETW_HEADER = ( + 'PerfectHash/FunctionEntry/win:Info,' + ' TimeStamp,' + ' Process Name ( PID),' + ' ThreadID,' + ' CPU,' + ' etw:ActivityId,' + ' etw:Related ActivityId,' + ' etw:UserSid,' + ' etw:SessionId,' + ' Rsp' +) + +FUNCTION_ENTRY_CSV_HEADER = ( + 'EventName', + 'TimeStamp', + 'ProcessID', + 'ThreadID', + 'CPU', + 'ActivityId', + 'RelatedActivityId', + 'UserSid', + 'SessionId', + 'Rsp', +) + +FUNCTION_ENTRY_CSV_HEADER_SLIM = ( + 'LineNumber', + 'TimeStamp', + 'ProcessID', + 'ThreadID', + 'CPU', + 'ActivityId', + 'Rsp', +) + # Maps EVENT_NAME_TO_ETW_HEADER = { @@ -1300,6 +1344,7 @@ IS_ACYCLIC: IS_ACYCLIC_ETW_HEADER, MEMORY_COVERAGE_CACHE_LINE_COUNTS: MEMORY_COVERAGE_CACHE_LINE_COUNTS_ETW_HEADER, + FUNCTION_ENTRY: FUNCTION_ENTRY_ETW_HEADER, } EVENT_NAME_TO_CSV_HEADER = { @@ -1319,6 +1364,7 @@ IS_ACYCLIC: IS_ACYCLIC_CSV_HEADER, MEMORY_COVERAGE_CACHE_LINE_COUNTS: MEMORY_COVERAGE_CACHE_LINE_COUNTS_CSV_HEADER, + FUNCTION_ENTRY: FUNCTION_ENTRY_CSV_HEADER, } EVENT_NAME_TO_CSV_HEADER_SLIM = { @@ -1338,6 +1384,7 @@ IS_ACYCLIC: IS_ACYCLIC_CSV_HEADER_SLIM, MEMORY_COVERAGE_CACHE_LINE_COUNTS: MEMORY_COVERAGE_CACHE_LINE_COUNTS_CSV_HEADER_SLIM, + FUNCTION_ENTRY: FUNCTION_ENTRY_CSV_HEADER_SLIM, } HAS_SEED_DATA = { @@ -2536,6 +2583,7 @@ def process_xperf_perfecthash_csv(path, out=None): pmc = PMC is_acyclic = IS_ACYCLIC memory_coverage_cache_line_counts = MEMORY_COVERAGE_CACHE_LINE_COUNTS + function_entry = FUNCTION_ENTRY assign_io = io.StringIO() generate_random_bytes_io = io.StringIO() @@ -2555,6 +2603,7 @@ def process_xperf_perfecthash_csv(path, out=None): pmc: io.StringIO(), is_acyclic: io.StringIO(), memory_coverage_cache_line_counts: io.StringIO(), + function_entry: io.StringIO(), } paths = { @@ -2573,6 +2622,7 @@ def process_xperf_perfecthash_csv(path, out=None): is_acyclic: f'{prefix}_IsAcyclic.csv', memory_coverage_cache_line_counts: f'{prefix}_MemoryCoverageCacheLineCounts.csv', + function_entry: f'{prefix}_FunctionEntry.csv', } counts = { @@ -2590,6 +2640,7 @@ def process_xperf_perfecthash_csv(path, out=None): pmc: 0, is_acyclic: 0, memory_coverage_cache_line_counts: 0, + function_entry: 0, } names = set(counts.keys()) diff --git a/python/perfecthash/config.py b/python/perfecthash/config.py index 9d568e7c..82b95a89 100644 --- a/python/perfecthash/config.py +++ b/python/perfecthash/config.py @@ -375,6 +375,22 @@ def perfecthash_x64_dll_release_path(self): self.get('perfecthash', 'perfecthash_x64_dll_release'), ) + @property + @memoize + def perfecthash_x64_dll_pgi_path(self): + return join_path( + self.project_root, + self.get('perfecthash', 'perfecthash_x64_dll_pgi'), + ) + + @property + @memoize + def perfecthash_x64_dll_pgo_path(self): + return join_path( + self.project_root, + self.get('perfecthash', 'perfecthash_x64_dll_pgo'), + ) + @property @memoize def current_architecture(self): diff --git a/python/perfecthash/dumpbin.py b/python/perfecthash/dumpbin.py index d4e91306..bf0a5723 100644 --- a/python/perfecthash/dumpbin.py +++ b/python/perfecthash/dumpbin.py @@ -101,7 +101,7 @@ class Dumpbin(InvariantAwareObject): class PathArg(PathInvariant): pass - def __init__(self, path): + def __init__(self, path, disasm=None): InvariantAwareObject.__init__(self) self.path = path self.conf = get_or_create_config() @@ -115,6 +115,9 @@ def __init__(self, path): self._load() + if disasm: + self._load_disasm() + def _load(self): if os.path.exists(self.conf.dumpbin_exe_path): @@ -165,6 +168,90 @@ def _load(self): self.text = text self.lines = lines + def _load_disasm(self): + + import numpy as np + + if os.path.exists(self.conf.dumpbin_exe_path): + dumpbin_exe = self.conf.dumpbin_exe_path + else: + dumpbin_exe = 'dumpbin.exe' + + cmd = [ + dumpbin_exe, + '/disasm', + self.path + ] + + from subprocess import check_output + raw = check_output(cmd) + text = raw.decode(sys.stdout.encoding) + + # Add a dummy line at the start of the array so that we can index + # lines directly by line number instead of having to subtract one + # first (to account for 0-based indexing). + + lines = [ '', ] + text.splitlines() + + self.disasm_text = text + self.disasm_lines = lines + + (func_linenos, names) = zip(*[ + (i, l[:-1]) for (i, l) in enumerate(lines) if ( + l and l[0] != ' ' and l[-1] == ':' + ) + ]) + self.disasm_func_linenos = np.array(func_linenos) + self.disasm_func_names = names + + if '_penter' in text: + base = self.image_base + call_ilt = 'call @ILT' + call_penter = 'call _penter' + (linenos, addresses) = zip(*[ + (i, ((int(l[2:18], base=16)) - base)) + for (i, l) in enumerate(lines) if ( + l and ( + l.endswith('(_penter)') and call_ilt in l + ) or l.endswith(call_penter) + ) + ]) + + self.disasm_call_penter_linenos = np.array(linenos) + self.disasm_call_penter_rips = np.array(addresses, dtype='uint32') + + rip_to_name = { } + func_linenos = self.disasm_func_linenos + for (lineno, rip) in zip(linenos, addresses): + i = np.searchsorted(func_linenos, v=lineno) - 1 + name = names[i] + rip_to_name[rip] = name + + self.penter_rip_to_name = rip_to_name + self.penter_names = np.array(list(rip_to_name.values()), dtype='str') + + if '__Pogo' in text: + base = self.image_base + call_prefix = 'call ' + (linenos, addresses) = zip(*[ + (i, ((int(l[2:18], base=16)) - base)) + for (i, l) in enumerate(lines) if ( + '__Pogo' in l and call_prefix in l + ) + ]) + + self.disasm_call_pogo_linenos = np.array(linenos) + self.disasm_call_pogo_rips = np.array(addresses, dtype='uint32') + + pogo_rip_to_name = { } + func_linenos = self.disasm_func_linenos + for (lineno, rip) in zip(linenos, addresses): + i = np.searchsorted(func_linenos, v=lineno) - 1 + name = names[i] + pogo_rip_to_name[rip] = name + + self.pogo_rip_to_name = pogo_rip_to_name + @property def is_cf_instrumented(self): return self._guard_cf_func_table_lineno is not None @@ -182,16 +269,20 @@ def guard_cf_func_table_lines(self): @property def image_base_start(self): - return self._image_base_start + return int(self._image_base_start, base=16) @property def image_base_end(self): - return self._image_base_end + return int(self._image_base_end, base=16) @property def image_base(self): return self._image_base + @property + def image_size(self): + return self.image_base_end - self.image_base_start + @property @memoize def guard_cf_func_table_addresses(self): @@ -219,7 +310,7 @@ def guard_cf_func_table_addresses_base0(self): array = self.guard_cf_func_table_address_array_base0 return [ hex(i)[2:].zfill(8).encode('ascii') for i in array ] - def binary_path(self, output_dir): + def binary_path(self, output_dir, a): from .path import ( basename, splitext, @@ -244,6 +335,30 @@ def binary_path(self, output_dir): def plot_path(self, output_dir): return self.binary_path(output_dir).replace('.keys', '.png') + def _save_rips_as_keys(self, rips, suffix): + + import numpy as np + + from .path import ( + dirname, + join_path, + ) + + a = np.sort(np.unique(rips)) + + binary_path = self.path.replace('.dll', f'{suffix}.keys') + fp = np.memmap(binary_path, dtype='uint32', mode='w+', shape=a.shape) + fp[:] = a[:] + del fp + + print(f'Wrote {binary_path}.') + + def save_penter_rips(self): + self._save_rips_as_keys(self.disasm_call_penter_rips, 'PenterRips') + + def save_pogo_rips(self): + self._save_rips_as_keys(self.disasm_call_pogo_rips, 'PogoRips') + def save(self, output_dir): import numpy as np diff --git a/python/perfecthash/sourcefile.py b/python/perfecthash/sourcefile.py index 953470b5..566a1731 100644 --- a/python/perfecthash/sourcefile.py +++ b/python/perfecthash/sourcefile.py @@ -651,11 +651,15 @@ def enums(self): return self.extract_elements(elems) def parse_enums(self, name, skip_if_startswith=None, - remove=None, split_on=None): + remove=None, split_on=None, include_values=False): lines = self.enums[name][3:-2] - results = [] + if include_values: + assert split_on + results = {} + else: + results = [] for line in lines: line = line[2:] if skip_if_startswith and line.startswith(skip_if_startswith): @@ -668,9 +672,17 @@ def parse_enums(self, name, skip_if_startswith=None, line = line.replace(remove, '') if split_on: - line = line.split(split_on)[0] + if not include_values: + line = line.split(split_on)[0] + else: + parts = line.split(split_on) + (name, value) = parts + value = value.replace(' ', '').replace(',', '') + results[name] = value + continue results.append(line) + return results @property @@ -683,6 +695,18 @@ def best_coverage_types(self): split_on='Id =' ) + @property + @memoize + def best_coverage_types_dict(self): + return self.parse_enums( + 'PERFECT_HASH_TABLE_BEST_COVERAGE_TYPE_ID', + skip_if_startswith='PerfectHash', + remove='BestCoverageType', + split_on='Id = ', + include_values=True, + ) + + @property def disabled_hash_functions(self): funcs = self.parse_enums( diff --git a/python/perfecthash/util.py b/python/perfecthash/util.py index 78ac3095..fb799133 100644 --- a/python/perfecthash/util.py +++ b/python/perfecthash/util.py @@ -26,6 +26,7 @@ from os.path import ( join, isdir, + isfile, abspath, dirname, basename, @@ -1263,6 +1264,13 @@ def first_writable_file_that_preferably_exists(files): raise RuntimeError("no writable files found") +def list_files_by_latest(base, file_filter=None): + paths = [ join(base, p) for p in os.listdir(base) ] + files = [ f for f in paths if isfile(f) ] + if file_filter: + files = [ f for f in paths if file_filter(f) ] + return [ f.path for f in file_timestamps(files) ] + def list_directories_by_latest(base, directory_filter=None): paths = [ join(base, p) for p in os.listdir(base) ] dirs = [ d for d in paths if isdir(d) ] diff --git a/src/CompiledPerfectHashTable/CompiledPerfectHashTableRoutines.c b/src/CompiledPerfectHashTable/CompiledPerfectHashTableRoutines.c index 9fec745b..bcea289b 100644 --- a/src/CompiledPerfectHashTable/CompiledPerfectHashTableRoutines.c +++ b/src/CompiledPerfectHashTable/CompiledPerfectHashTableRoutines.c @@ -31,4 +31,49 @@ DECLARE_DELETE_ROUTINE() return Previous; } +DECLARE_INTERLOCKED_INCREMENT_ROUTINE() +{ + CPHINDEX Index; + CPHVALUE New; + + Index = INDEX_ROUTINE(Key); +#ifdef _WIN32 + New = _InterlockedIncrement((volatile LONG *)&TABLE_VALUES[Index]); +#else + New = __sync_add_and_fetch(&TABLE_VALUES[Index], 1); +#endif + return New; +} + +#ifdef CPH_HAS_KEYS +DECLARE_INDEX_BSEARCH_ROUTINE() +{ + CPHINDEX Low; + CPHINDEX High; + CPHINDEX Middle; + CPHDKEY Value; + + Low = 0; + High = NUMBER_OF_KEYS - 1; + Middle = (Low + High) / 2; + + while (Low <= High) { + + Value = KEYS[Middle]; + + if (Value == Key) { + break; + } else if (Value < Key) { + Low = Middle + 1; + } else { + High = Middle - 1; + } + + Middle = (Low + High) / 2; + } + + return Middle; +} +#endif + #endif diff --git a/src/CompiledPerfectHashTable/CompiledPerfectHashTableRoutinesPost.c b/src/CompiledPerfectHashTable/CompiledPerfectHashTableRoutinesPost.c index 2558c539..cf80eab7 100644 --- a/src/CompiledPerfectHashTable/CompiledPerfectHashTableRoutinesPost.c +++ b/src/CompiledPerfectHashTable/CompiledPerfectHashTableRoutinesPost.c @@ -4,30 +4,38 @@ #undef CPH_INLINE_ROUTINES #undef INDEX_ROUTINE +#undef INDEX_BSEARCH_ROUTINE #undef DECLARE_INDEX_ROUTINE +#undef DECLARE_INDEX_BSEARCH_ROUTINE #ifndef CPH_INDEX_ONLY #undef LOOKUP_ROUTINE #undef INSERT_ROUTINE #undef DELETE_ROUTINE +#undef INTERLOCKED_INCREMENT_ROUTINE #undef DECLARE_LOOKUP_ROUTINE #undef DECLARE_INSERT_ROUTINE #undef DECLARE_DELETE_ROUTINE +#undef DECLARE_INTERLOCKED_INCREMENT_ROUTINE #endif #define INDEX_ROUTINE EXPAND_INDEX_ROUTINE(CPH_TABLENAME) +#define INDEX_BSEARCH_ROUTINE EXPAND_INDEX_BSEARCH_ROUTINE(CPH_TABLENAME) #define DECLARE_INDEX_ROUTINE() EXPAND_INDEX_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_INDEX_IACA_ROUTINE() EXPAND_INDEX_IACA_ROUTINE_HEADER(CPH_TABLENAME) +#define DECLARE_INDEX_BSEARCH_ROUTINE() EXPAND_INDEX_BSEARCH_ROUTINE_HEADER(CPH_TABLENAME) #ifndef CPH_INDEX_ONLY #define LOOKUP_ROUTINE EXPAND_LOOKUP_ROUTINE(CPH_TABLENAME) #define INSERT_ROUTINE EXPAND_INSERT_ROUTINE(CPH_TABLENAME) #define DELETE_ROUTINE EXPAND_DELETE_ROUTINE(CPH_TABLENAME) +#define INTERLOCKED_INCREMENT_ROUTINE EXPAND_INTERLOCKED_INCREMENT_ROUTINE(CPH_TABLENAME) #define DECLARE_LOOKUP_ROUTINE() EXPAND_LOOKUP_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_INSERT_ROUTINE() EXPAND_INSERT_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_DELETE_ROUTINE() EXPAND_DELETE_ROUTINE_HEADER(CPH_TABLENAME) +#define DECLARE_INTERLOCKED_INCREMENT_ROUTINE() EXPAND_INTERLOCKED_INCREMENT_ROUTINE_HEADER(CPH_TABLENAME) #endif #endif diff --git a/src/CompiledPerfectHashTable/CompiledPerfectHashTableRoutinesPre.c b/src/CompiledPerfectHashTable/CompiledPerfectHashTableRoutinesPre.c index 4fab2cc5..24d49408 100644 --- a/src/CompiledPerfectHashTable/CompiledPerfectHashTableRoutinesPre.c +++ b/src/CompiledPerfectHashTable/CompiledPerfectHashTableRoutinesPre.c @@ -2,31 +2,40 @@ #ifdef CPH_INLINE_ROUTINES #define INDEX_ROUTINE EXPAND_INDEX_INLINE_ROUTINE(CPH_TABLENAME) +#define INDEX_BSEARCH_ROUTINE EXPAND_INDEX_BSEARCH_INLINE_ROUTINE(CPH_TABLENAME) #define DECLARE_INDEX_ROUTINE() EXPAND_INDEX_INLINE_ROUTINE_HEADER(CPH_TABLENAME) +#define DECLARE_INDEX_BSEARCH_ROUTINE() EXPAND_INDEX_BSEARCH_INLINE_ROUTINE_HEADER(CPH_TABLENAME) #ifndef CPH_INDEX_ONLY #define LOOKUP_ROUTINE EXPAND_LOOKUP_INLINE_ROUTINE(CPH_TABLENAME) #define INSERT_ROUTINE EXPAND_INSERT_INLINE_ROUTINE(CPH_TABLENAME) #define DELETE_ROUTINE EXPAND_DELETE_INLINE_ROUTINE(CPH_TABLENAME) +#define INTERLOCKED_INCREMENT_ROUTINE EXPAND_INTERLOCKED_INCREMENT_INLINE_ROUTINE(CPH_TABLENAME) #define DECLARE_LOOKUP_ROUTINE() EXPAND_LOOKUP_INLINE_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_INSERT_ROUTINE() EXPAND_INSERT_INLINE_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_DELETE_ROUTINE() EXPAND_DELETE_INLINE_ROUTINE_HEADER(CPH_TABLENAME) +#define DECLARE_INTERLOCKED_INCREMENT_ROUTINE() \ + EXPAND_INTERLOCKED_INCREMENT_INLINE_ROUTINE_HEADER(CPH_TABLENAME) #endif #else #define INDEX_ROUTINE EXPAND_INDEX_ROUTINE(CPH_TABLENAME) +#define INDEX_BSEARCH_ROUTINE EXPAND_INDEX_BSEARCH_ROUTINE(CPH_TABLENAME) #define DECLARE_INDEX_ROUTINE() EXPAND_INDEX_ROUTINE_HEADER(CPH_TABLENAME) +#define DECLARE_INDEX_BSEARCH_ROUTINE() EXPAND_INDEX_BSEARCH_ROUTINE_HEADER(CPH_TABLENAME) #ifndef CPH_INDEX_ONLY #define LOOKUP_ROUTINE EXPAND_LOOKUP_ROUTINE(CPH_TABLENAME) #define INSERT_ROUTINE EXPAND_INSERT_ROUTINE(CPH_TABLENAME) #define DELETE_ROUTINE EXPAND_DELETE_ROUTINE(CPH_TABLENAME) +#define INTERLOCKED_INCREMENT_ROUTINE EXPAND_INTERLOCKED_INCREMENT_ROUTINE(CPH_TABLENAME) #define DECLARE_LOOKUP_ROUTINE() EXPAND_LOOKUP_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_INSERT_ROUTINE() EXPAND_INSERT_ROUTINE_HEADER(CPH_TABLENAME) #define DECLARE_DELETE_ROUTINE() EXPAND_DELETE_ROUTINE_HEADER(CPH_TABLENAME) +#define DECLARE_INTERLOCKED_INCREMENT_ROUTINE() EXPAND_INTERLOCKED_INCREMENT_ROUTINE_HEADER(CPH_TABLENAME) #endif #endif diff --git a/src/PerfectHash/Chm01.c b/src/PerfectHash/Chm01.c index 95f25816..23665aaf 100644 --- a/src/PerfectHash/Chm01.c +++ b/src/PerfectHash/Chm01.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -235,7 +235,7 @@ Return Value: return E_POINTER; } - TableCreateFlags.AsULong = Table->TableCreateFlags.AsULong; + TableCreateFlags.AsULongLong = Table->TableCreateFlags.AsULongLong; Silent = (TableCreateFlags.Silent != FALSE); Context = Table->Context; @@ -494,7 +494,7 @@ Return Value: // used by GraphInitialize() to tweak vtbl construction. // - TlsContext->TableCreateFlags.AsULong = TableCreateFlags.AsULong; + TlsContext->TableCreateFlags.AsULongLong = TableCreateFlags.AsULongLong; // // ....and, many years later, we find ourselves in the position of wanting @@ -555,13 +555,17 @@ Return Value: // Copy relevant flags over, then save the graph instance in the array. // - Graph->Flags.SkipVerification = TableCreateFlags.SkipGraphVerification; + Graph->Flags.SkipVerification = ( + TableCreateFlags.SkipGraphVerification != FALSE + ); - Graph->Flags.WantsWriteCombiningForVertexPairsArray = - TableCreateFlags.EnableWriteCombineForVertexPairs; + Graph->Flags.WantsWriteCombiningForVertexPairsArray = ( + TableCreateFlags.EnableWriteCombineForVertexPairs != FALSE + ); - Graph->Flags.RemoveWriteCombineAfterSuccessfulHashKeys = - TableCreateFlags.RemoveWriteCombineAfterSuccessfulHashKeys; + Graph->Flags.RemoveWriteCombineAfterSuccessfulHashKeys = ( + TableCreateFlags.RemoveWriteCombineAfterSuccessfulHashKeys != FALSE + ); Graph->Index = Index; Graphs[Index] = Graph; @@ -1748,7 +1752,7 @@ Return Value: PopulationCount32 = Rtl->PopulationCount32; RoundUpPowerOfTwo32 = Rtl->RoundUpPowerOfTwo32; RoundUpNextPowerOfTwo32 = Rtl->RoundUpNextPowerOfTwo32; - TableCreateFlags.AsULong = Table->TableCreateFlags.AsULong; + TableCreateFlags.AsULongLong = Table->TableCreateFlags.AsULongLong; // // If a previous Info struct pointer has been passed, copy the current @@ -2800,8 +2804,11 @@ Return Value: SYSTEMTIME LocalTime; ULONG PredictedAttempts; ULARGE_INTEGER Duration; + LONGLONG CurrentAttempts; DOUBLE AttemptsPerSecond; DOUBLE DurationInSeconds; + DOUBLE CurrentAttemptsPerSecond; + DOUBLE CurrentDurationInSeconds; PFILETIME64 TableFileTime; DOUBLE SolutionsFoundRatio; ULONGLONG BufferSizeInBytes; @@ -2827,6 +2834,18 @@ Return Value: OUTPUT_UNICODE_STRING_FAST \ ) \ \ + ENTRY( \ + "Number of Keys: ", \ + Context->Table->Keys->NumberOfKeys.QuadPart, \ + OUTPUT_INT \ + ) \ + \ + ENTRY( \ + "Number of Table Resize Events: ", \ + Context->NumberOfTableResizeEvents, \ + OUTPUT_INT \ + ) \ + \ ENTRY( \ "Keys to Edges Ratio: ", \ Context->Table->KeysToEdgesRatio, \ @@ -2857,6 +2876,18 @@ Return Value: OUTPUT_DOUBLE \ ) \ \ + ENTRY( \ + "Current Attempts: ", \ + CurrentAttempts, \ + OUTPUT_INT \ + ) \ + \ + ENTRY( \ + "Current Attempts Per Second: ", \ + CurrentAttemptsPerSecond, \ + OUTPUT_DOUBLE \ + ) \ + \ ENTRY( \ "Successful Attempts: ", \ Context->FinishedCount, \ @@ -2939,12 +2970,6 @@ Return Value: "Highest Deleted Edges Count: ", \ Context->HighestDeletedEdgesCount, \ OUTPUT_INT \ - ) \ - \ - ENTRY( \ - "Number of Table Resize Events: ", \ - Context->NumberOfTableResizeEvents, \ - OUTPUT_INT \ ) #define CONTEXT_STATS_BEST_GRAPH_TABLE(ENTRY) \ @@ -3048,7 +3073,7 @@ Return Value: // Fast-path exit if we're not in quiet mode. // - TableCreateFlags.AsULong = Context->Table->TableCreateFlags.AsULong; + TableCreateFlags.AsULongLong = Context->Table->TableCreateFlags.AsULongLong; if (TableCreateFlags.Quiet) { return S_OK; @@ -3104,7 +3129,7 @@ Return Value: DurationString.MaximumLength = (USHORT)(Chars << 1); // - // Calculate attempts per second. + // Calculate total attempts per second. // DurationInSeconds = (DOUBLE)( @@ -3114,6 +3139,29 @@ Return Value: AttemptsPerSecond = (((DOUBLE)Context->Attempts) / DurationInSeconds); + // + // Calculate "current" values. + // + + CurrentAttempts = Context->Attempts - Context->BaselineAttempts; + if (Context->BaselineFileTime.AsULongLong > 0) { + Duration.QuadPart = ( + FileTime.AsULongLong - + Context->BaselineFileTime.AsULongLong + ); + + CurrentDurationInSeconds = (DOUBLE)( + ((DOUBLE)Duration.QuadPart) / + ((DOUBLE)1e7) + ); + } else { + CurrentDurationInSeconds = DurationInSeconds; + } + + CurrentAttemptsPerSecond = ( + ((DOUBLE)CurrentAttempts) / CurrentDurationInSeconds + ); + // // If we've found at least one solution, calculate solutions found ratio // and perform some predictions based on the current solving rate. @@ -3321,26 +3369,38 @@ Return Value: { COORD Coord; BOOL Success; - BOOL IsQuit; - BOOL IsStatus; + BOOL IsFinish; + BOOL IsRefresh; BOOL IsResize; BOOL IsVerbose; + BOOL IsMoreHelp; + BOOL IsToggleCallback; + BOOL DoFlushOnExit; HRESULT Result; ULONG WaitResult; + DWORD LastError; HANDLE InputHandle; INPUT_RECORD Input; + FILETIME64 FileTime; + SYSTEMTIME LocalTime; ULONG NumberOfEvents; DWORD NumberOfEventsRead; HANDLE Events[3] = { 0, }; KEY_EVENT_RECORD *KeyEvent; PERFECT_HASH_TABLE_CREATE_FLAGS TableCreateFlags; + PSET_FUNCTION_ENTRY_CALLBACK SetFunctionEntryCallback; + PCLEAR_FUNCTION_ENTRY_CALLBACK ClearFunctionEntryCallback; + PIS_FUNCTION_ENTRY_CALLBACK_ENABLED IsFunctionEntryCallbackEnabled; // // Initialize aliases. // InputHandle = Context->InputHandle; - TableCreateFlags.AsULong = Context->Table->TableCreateFlags.AsULong; + TableCreateFlags.AsULongLong = Context->Table->TableCreateFlags.AsULongLong; + SetFunctionEntryCallback = Context->SetFunctionEntryCallback; + ClearFunctionEntryCallback = Context->ClearFunctionEntryCallback; + IsFunctionEntryCallbackEnabled = Context->IsFunctionEntryCallbackEnabled; // // Initialize the event handles upon which we will wait. @@ -3367,6 +3427,8 @@ Return Value: // Enter our console loop. // + DoFlushOnExit = TRUE; + while (TRUE) { WaitResult = WaitForMultipleObjects(NumberOfEvents, @@ -3390,10 +3452,12 @@ Return Value: // A new best graph has been found. // - IsStatus = TRUE; + IsRefresh = TRUE; IsResize = FALSE; - IsQuit = FALSE; + IsFinish = FALSE; IsVerbose = FALSE; + IsMoreHelp = FALSE; + IsToggleCallback = FALSE; // // As all the context's events are created as manual reset, we need @@ -3419,7 +3483,20 @@ Return Value: &NumberOfEventsRead); if (!Success) { - SYS_ERROR(ReadConsoleInput); + LastError = GetLastError(); + + // + // ERROR_INVALID_FUNCTION will be returned if the console has + // been redirected or isn't otherwise available; this is not + // considered fatal. Otherwise, log the error. In all cases, + // disable the final console flush and exit the routine. + // + + if (LastError != ERROR_INVALID_FUNCTION) { + SYS_ERROR(ReadConsoleInput); + } + + DoFlushOnExit = FALSE; goto End; } @@ -3459,22 +3536,22 @@ Return Value: continue; } - IsStatus = ( - (KeyEvent->uChar.AsciiChar == 's') || - (KeyEvent->uChar.AsciiChar == 'S') || - (KeyEvent->wVirtualKeyCode == 0x0053) - ); - - IsResize = ( + IsRefresh = ( (KeyEvent->uChar.AsciiChar == 'r') || (KeyEvent->uChar.AsciiChar == 'R') || (KeyEvent->wVirtualKeyCode == 0x0052) ); - IsQuit = ( - (KeyEvent->uChar.AsciiChar == 'q') || - (KeyEvent->uChar.AsciiChar == 'Q') || - (KeyEvent->wVirtualKeyCode == 0x0051) + IsResize = ( + (KeyEvent->uChar.AsciiChar == 'e') || + (KeyEvent->uChar.AsciiChar == 'E') || + (KeyEvent->wVirtualKeyCode == 0x0045) + ); + + IsFinish = ( + (KeyEvent->uChar.AsciiChar == 'f') || + (KeyEvent->uChar.AsciiChar == 'F') || + (KeyEvent->wVirtualKeyCode == 0x0045) ); IsVerbose = ( @@ -3482,9 +3559,20 @@ Return Value: (KeyEvent->uChar.AsciiChar == 'V') || (KeyEvent->wVirtualKeyCode == 0x0056) ); + + IsMoreHelp = ( + (KeyEvent->uChar.AsciiChar == '?') || + (KeyEvent->wVirtualKeyCode == VK_OEM_2) + ); + + IsToggleCallback = ( + (KeyEvent->uChar.AsciiChar == 'c') || + (KeyEvent->uChar.AsciiChar == 'C') || + (KeyEvent->wVirtualKeyCode == 0x0043) + ); } - if (IsQuit) { + if (IsFinish) { SetStopSolving(Context); if (!SetEvent(Context->ShutdownEvent)) { @@ -3501,7 +3589,7 @@ Return Value: } break; - } else if (IsStatus) { + } else if (IsRefresh) { Result = PrintCurrentContextStatsChm01(Context); if (FAILED(Result)) { @@ -3534,6 +3622,46 @@ Return Value: ); } + } else if (IsToggleCallback) { + + if (!Context->State.HasFunctionHooking) { + continue; + } + + if (IsFunctionEntryCallbackEnabled()) { + ClearFunctionEntryCallback(&Context->CallbackFunction, + &Context->CallbackContext, + &Context->CallbackModuleBaseAddress, + &Context->CallbackModuleSizeInBytes, + &Context->CallbackModuleIgnoreRip); + } else { + SetFunctionEntryCallback(Context->CallbackFunction, + Context->CallbackContext, + Context->CallbackModuleBaseAddress, + Context->CallbackModuleSizeInBytes, + Context->CallbackModuleIgnoreRip); + } + + // + // Update the baseline attempts and file time. + // + + GetLocalTime(&LocalTime); + if (!SystemTimeToFileTime(&LocalTime, &FileTime.AsFileTime)) { + SYS_ERROR(SystemTimeToFileTime); + continue; + } + + Context->BaselineAttempts = Context->Attempts; + Context->BaselineFileTime.AsULongLong = FileTime.AsULongLong; + + continue; + + } else if (IsMoreHelp) { + + PerfectHashPrintMessage(PH_MSG_PERFECT_HASH_CONSOLE_KEYS_MORE_HELP); + continue; + } else { // @@ -3551,7 +3679,7 @@ Return Value: End: - if (!FlushConsoleInputBuffer(InputHandle)) { + if (DoFlushOnExit && !FlushConsoleInputBuffer(InputHandle)) { SYS_ERROR(FlushConsoleInputBuffer); } } diff --git a/src/PerfectHash/Chm01FileWork.h b/src/PerfectHash/Chm01FileWork.h index 64698c6e..03371f9a 100644 --- a/src/PerfectHash/Chm01FileWork.h +++ b/src/PerfectHash/Chm01FileWork.h @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -76,6 +76,7 @@ SAVE_FILE_WORK_TABLE_ENTRY(EXPAND_AS_CALLBACK_DECL); #define SaveMakefileBenchmarkIndexMkFileChm01 NULL #define SaveMakefileBenchmarkFullMkFileChm01 NULL #define SaveCHeaderNoSal2FileChm01 NULL +#define SaveModuleDefFileChm01 NULL extern FILE_WORK_CALLBACK_IMPL *FileCallbacks[]; diff --git a/src/PerfectHash/Chm01FileWorkCHeaderFile.c b/src/PerfectHash/Chm01FileWorkCHeaderFile.c index 622ed49e..9c45b5e4 100644 --- a/src/PerfectHash/Chm01FileWorkCHeaderFile.c +++ b/src/PerfectHash/Chm01FileWorkCHeaderFile.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -131,6 +131,10 @@ PrepareCHeaderFileChm01( OUTPUT_INT(NumberOfSeeds); OUTPUT_RAW("];\n"); + OUTPUT_RAW("\n\nextern const BYTE "); + OUTPUT_STRING(Name); + OUTPUT_RAW("_NumberOfSeeds;\n"); + for (Index = 0, Count = 1; Index < NumberOfSeeds; Index++, Count++) { OUTPUT_RAW("extern const CPHSEED "); OUTPUT_STRING(Name); @@ -155,25 +159,57 @@ PrepareCHeaderFileChm01( OUTPUT_RAW("\nextern const CPHDKEY "); OUTPUT_STRING(Name); - OUTPUT_RAW("_HashMask;\nextern const CPHDKEY "); + OUTPUT_RAW("_HashMask;\n"); + + OUTPUT_RAW("extern const CPHDKEY "); OUTPUT_STRING(Name); - OUTPUT_RAW("_IndexMask;\n\nextern const "); + OUTPUT_RAW("_IndexMask;\n\n"); + + OUTPUT_RAW("extern const "); OUTPUT_STRING(Table->TableDataArrayTypeName); OUTPUT_RAW(" "); OUTPUT_STRING(Name); - OUTPUT_RAW("_TableData[];\nextern CPHVALUE "); + OUTPUT_RAW("_TableData[];\n"); + + OUTPUT_RAW("extern CPHVALUE "); OUTPUT_STRING(Name); - OUTPUT_RAW("_TableValues[];\n\n"); + OUTPUT_RAW("_TableValues[];\n"); + + OUTPUT_RAW("extern const ULONG "); + OUTPUT_STRING(Name); + OUTPUT_RAW("_TableValueSizeInBytes;\n"); + + OUTPUT_RAW("extern const ULONG "); + OUTPUT_STRING(Name); + OUTPUT_RAW("_NumberOfTableValues;\n\n"); OUTPUT_RAW("extern const CPHKEY "); OUTPUT_STRING(Name); OUTPUT_RAW("_Keys[];\n"); + OUTPUT_RAW("extern const CPHDKEY "); OUTPUT_STRING(Name); OUTPUT_RAW("_DownsizedKeys[];\n"); - OUTPUT_RAW("extern const CPHDKEY "); + + OUTPUT_RAW("extern const ULONG "); + OUTPUT_STRING(Name); + OUTPUT_RAW("_NumberOfKeys;\n"); + + OUTPUT_RAW("extern const ULONG "); + OUTPUT_STRING(Name); + OUTPUT_RAW("_KeySizeInBytes;\n"); + + OUTPUT_RAW("extern const ULONG "); + OUTPUT_STRING(Name); + OUTPUT_RAW("_OriginalKeySizeInBytes;\n"); + + OUTPUT_RAW("extern const ULONG "); OUTPUT_STRING(Name); - OUTPUT_RAW("_NumberOfKeys;\n\n"); + OUTPUT_RAW("_DownsizedKeySizeInBytes;\n\n"); + + if (Table->TableCreateFlags.IncludeKeysInCompiledDll != FALSE) { + OUTPUT_RAW("#define CPH_HAS_KEYS 1\n\n"); + } // // If key downsizing has occurred, output the bitmap that was used. diff --git a/src/PerfectHash/Chm01FileWorkCSourceKeysFile.c b/src/PerfectHash/Chm01FileWorkCSourceKeysFile.c index 868a044f..e886a1e7 100644 --- a/src/PerfectHash/Chm01FileWorkCSourceKeysFile.c +++ b/src/PerfectHash/Chm01FileWorkCSourceKeysFile.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022. Trent Nelson +Copyright (c) 2018-2023. Trent Nelson Module Name: @@ -74,12 +74,30 @@ PrepareCSourceKeysFileChm01( OUTPUT_RAW("#ifdef _WIN32\n#pragma const_seg(\".cphkeys\")\n#endif\n"); - OUTPUT_RAW("const CPHDKEY "); + OUTPUT_RAW("const ULONG "); OUTPUT_STRING(Name); OUTPUT_RAW("_NumberOfKeys = "); OUTPUT_INT(NumberOfKeys); OUTPUT_RAW(";\n"); + OUTPUT_RAW("const ULONG "); + OUTPUT_STRING(Name); + OUTPUT_RAW("_KeySizeInBytes = "); + OUTPUT_INT(Keys->OriginalKeySizeInBytes); + OUTPUT_RAW(";\n"); + + OUTPUT_RAW("const ULONG "); + OUTPUT_STRING(Name); + OUTPUT_RAW("_OriginalKeySizeInBytes = "); + OUTPUT_INT(Keys->OriginalKeySizeInBytes); + OUTPUT_RAW(";\n"); + + OUTPUT_RAW("const ULONG "); + OUTPUT_STRING(Name); + OUTPUT_RAW("_DownsizedKeySizeInBytes = "); + OUTPUT_INT(4); + OUTPUT_RAW(";\n"); + OUTPUT_RAW("const CPHKEY "); OUTPUT_STRING(Name); OUTPUT_RAW("_Keys["); diff --git a/src/PerfectHash/Chm01FileWorkCSourceTableDataFile.c b/src/PerfectHash/Chm01FileWorkCSourceTableDataFile.c index 3e58c2a9..50b21047 100644 --- a/src/PerfectHash/Chm01FileWorkCSourceTableDataFile.c +++ b/src/PerfectHash/Chm01FileWorkCSourceTableDataFile.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2019 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -85,6 +85,12 @@ SaveCSourceTableDataFileChm01( // OUTPUT_RAW("#ifdef _WIN32\n#pragma const_seg(\".cphsm\")\n#endif\n"); + OUTPUT_RAW("const BYTE "); + OUTPUT_STRING(Name); + OUTPUT_RAW("_NumberOfSeeds = "); + OUTPUT_INT(NumberOfSeeds); + OUTPUT_RAW(";\n"); + OUTPUT_RAW("const CPHSEED "); OUTPUT_STRING(Name); OUTPUT_RAW("_Seeds["); diff --git a/src/PerfectHash/Chm01FileWorkCSourceTableValuesFile.c b/src/PerfectHash/Chm01FileWorkCSourceTableValuesFile.c index f4cdcdd5..ce0a3278 100644 --- a/src/PerfectHash/Chm01FileWorkCSourceTableValuesFile.c +++ b/src/PerfectHash/Chm01FileWorkCSourceTableValuesFile.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2019 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -65,11 +65,26 @@ PrepareCSourceTableValuesFileChm01( // Write the table values array. // - OUTPUT_RAW("#ifndef CPH_INDEX_ONLY\n\n" - "#ifdef _WIN32\n" + OUTPUT_RAW("#ifndef CPH_INDEX_ONLY\n\n"); + + OUTPUT_RAW("const ULONG "); + OUTPUT_STRING(Name); + OUTPUT_RAW("_TableValueSizeInBytes = "); + OUTPUT_INT(Table->ValueSizeInBytes == 0 ? + sizeof(ULONG) : Table->ValueSizeInBytes); + OUTPUT_RAW(";\n"); + + OUTPUT_RAW("const ULONG "); + OUTPUT_STRING(Name); + OUTPUT_RAW("_NumberOfTableValues = "); + OUTPUT_INT(NumberOfElements); + OUTPUT_RAW(";\n\n"); + + OUTPUT_RAW("#ifdef _WIN32\n" "#pragma data_seg(\".cphval\")\n" - "#endif\n" - "CPHVALUE "); + "#endif\n"); + + OUTPUT_RAW("CPHVALUE "); OUTPUT_STRING(Name); OUTPUT_RAW("_TableValues["); OUTPUT_INT(NumberOfElements); diff --git a/src/PerfectHash/Chm01FileWorkMakefileSoMkFile.c b/src/PerfectHash/Chm01FileWorkMakefileSoMkFile.c index 69087caf..9ccdcc8c 100644 --- a/src/PerfectHash/Chm01FileWorkMakefileSoMkFile.c +++ b/src/PerfectHash/Chm01FileWorkMakefileSoMkFile.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2019 Trent Nelson +Copyright (c) 2019-2023. Trent Nelson Module Name: @@ -69,6 +69,8 @@ PrepareMakefileSoMkFileChm01( MAYBE_OUTPUT_INCLUDE_TABLE_VALUES_DOT_C(); + MAYBE_OUTPUT_INCLUDE_KEYS_DOT_C(); + // // TableData.c // diff --git a/src/PerfectHash/Chm01FileWorkModuleDefFile.c b/src/PerfectHash/Chm01FileWorkModuleDefFile.c new file mode 100644 index 00000000..419c068f --- /dev/null +++ b/src/PerfectHash/Chm01FileWorkModuleDefFile.c @@ -0,0 +1,104 @@ +/*++ + +Copyright (c) 2022-2023. Trent Nelson + +Module Name: + + Chm01FileWorkModuleDefFile.c + +Abstract: + + This module implements the prepare file work callback routine for the + .def file as part of the CHM v1 algorithm implementation for + the perfect hash library. + +--*/ + +#include "stdafx.h" + +_Use_decl_annotations_ +HRESULT +PrepareModuleDefFileChm01( + PPERFECT_HASH_CONTEXT Context, + PFILE_WORK_ITEM Item + ) +{ + PCHAR Base; + PCHAR Output; + PCSTRING BaseName; + PPERFECT_HASH_PATH Path; + PPERFECT_HASH_FILE File; + PPERFECT_HASH_TABLE Table; + + // + // Initialize aliases. + // + + File = *Item->FilePointer; + Path = GetActivePath(File); + BaseName = &Path->BaseNameA; + Table = Context->Table; + + Base = (PCHAR)File->BaseAddress; + Output = Base; + + // + // Write the module definition file. + // + + OUTPUT_RAW("EXPORTS\n"); + +#define WRITE_PH_EXPORT(Export) \ + OUTPUT_RAW(" "); \ + OUTPUT_RAW(#Export); \ + OUTPUT_RAW(" = "); \ + OUTPUT_STRING(BaseName); \ + OUTPUT_RAW("_"); \ + OUTPUT_RAW(#Export); \ + OUTPUT_RAW("\n") + + WRITE_PH_EXPORT(TableData); + + WRITE_PH_EXPORT(TableValues); + WRITE_PH_EXPORT(NumberOfTableValues); + WRITE_PH_EXPORT(TableValueSizeInBytes); + + WRITE_PH_EXPORT(Seeds); + WRITE_PH_EXPORT(NumberOfSeeds); + + if (Table->TableCreateFlags.IncludeKeysInCompiledDll != FALSE) { + WRITE_PH_EXPORT(Keys); + WRITE_PH_EXPORT(NumberOfKeys); + WRITE_PH_EXPORT(KeySizeInBytes); + WRITE_PH_EXPORT(OriginalKeySizeInBytes); + WRITE_PH_EXPORT(DownsizedKeySizeInBytes); + } + +#define WRITE_CPH_EXPORT(Export) \ + OUTPUT_RAW(" "); \ + OUTPUT_RAW(#Export); \ + OUTPUT_RAW(" = CompiledPerfectHash_"); \ + OUTPUT_STRING(BaseName); \ + OUTPUT_RAW("_"); \ + OUTPUT_RAW(#Export); \ + OUTPUT_RAW("\n"); + + WRITE_CPH_EXPORT(Index); + WRITE_CPH_EXPORT(IndexIaca); + WRITE_CPH_EXPORT(IndexBsearch); + WRITE_CPH_EXPORT(Insert); + WRITE_CPH_EXPORT(Lookup); + WRITE_CPH_EXPORT(Delete); + WRITE_CPH_EXPORT(InterlockedIncrement); + + // + // Finish up. + // + + File->NumberOfBytesWritten.QuadPart = RtlPointerToOffset(Base, Output); + + return S_OK; +} + + +// vim:set ts=8 sw=4 sts=4 tw=80 expandtab : diff --git a/src/PerfectHash/Chm01FileWorkVCProjectBenchmarkFullExeFile.c b/src/PerfectHash/Chm01FileWorkVCProjectBenchmarkFullExeFile.c index 37c5f5b0..8040f824 100644 --- a/src/PerfectHash/Chm01FileWorkVCProjectBenchmarkFullExeFile.c +++ b/src/PerfectHash/Chm01FileWorkVCProjectBenchmarkFullExeFile.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -73,7 +73,13 @@ PrepareVCProjectBenchmarkFullExeFileChm01( Values.TargetPrefix = &BenchmarkFullTargetPrefix; Values.ConfigurationType = &ApplicationConfigurationTypeA; - Result = ProcessChunks(Rtl, Chunks, NumberOfChunks, &Values, &Output); + Result = ProcessChunks(Rtl, + Chunks, + NumberOfChunks, + &Values, + 0, + NULL, + &Output); if (FAILED(Result)) { PH_ERROR(ProcessChunks, Result); diff --git a/src/PerfectHash/Chm01FileWorkVCProjectBenchmarkIndexExeFile.c b/src/PerfectHash/Chm01FileWorkVCProjectBenchmarkIndexExeFile.c index 4abc6acd..5617987d 100644 --- a/src/PerfectHash/Chm01FileWorkVCProjectBenchmarkIndexExeFile.c +++ b/src/PerfectHash/Chm01FileWorkVCProjectBenchmarkIndexExeFile.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023. Trent Nelson Module Name: @@ -73,7 +73,13 @@ PrepareVCProjectBenchmarkIndexExeFileChm01( Values.TargetPrefix = &BenchmarkIndexTargetPrefix; Values.ConfigurationType = &ApplicationConfigurationTypeA; - Result = ProcessChunks(Rtl, Chunks, NumberOfChunks, &Values, &Output); + Result = ProcessChunks(Rtl, + Chunks, + NumberOfChunks, + &Values, + 0, + NULL, + &Output); if (FAILED(Result)) { PH_ERROR(ProcessChunks, Result); diff --git a/src/PerfectHash/Chm01FileWorkVCProjectDllFile.c b/src/PerfectHash/Chm01FileWorkVCProjectDllFile.c index b7678695..4fd799e1 100644 --- a/src/PerfectHash/Chm01FileWorkVCProjectDllFile.c +++ b/src/PerfectHash/Chm01FileWorkVCProjectDllFile.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023. Trent Nelson Module Name: @@ -38,6 +38,7 @@ PrepareVCProjectDllFileChm01( PPERFECT_HASH_FILE File; PPERFECT_HASH_TABLE Table; PCCHUNK Chunks = VCProjectDllFileChunks; + BOOLEAN Conditionals[3] = { FALSE, }; const ULONG NumberOfChunks = VCProjectDllFileNumberOfChunks; // @@ -72,7 +73,19 @@ PrepareVCProjectDllFileChm01( Values.FileSuffix = &DllFileSuffix; Values.ConfigurationType = &DynamicLibraryConfigurationTypeA; - Result = ProcessChunks(Rtl, Chunks, NumberOfChunks, &Values, &Output); + if (Table->TableCreateFlags.IncludeKeysInCompiledDll != FALSE) { + Conditionals[0] = TRUE; + Conditionals[1] = TRUE; + Conditionals[2] = TRUE; + } + + Result = ProcessChunks(Rtl, + Chunks, + NumberOfChunks, + &Values, + ARRAYSIZE(Conditionals), + &Conditionals[0], + &Output); if (FAILED(Result)) { PH_ERROR(ProcessChunks, Result); @@ -203,6 +216,16 @@ CHUNK VCProjectDllFileChunks[] = { " \r\n" " \r\n" " true\r\n" + " " + ), + }, + + { ChunkOpInsertBaseName, }, + + { + ChunkOpRaw, + RCS( + ".def\r\n" " \r\n" " \r\n" " \r\n" @@ -276,10 +299,34 @@ CHUNK VCProjectDllFileChunks[] = { { ChunkOpInsertBaseName, }, + { + ChunkOpRaw, + RCS("_TableData.c\" />\r\n"), + }, + + // + // Conditionally include the keys file if requested. + // + + { + ChunkOpRawConditional, + RCS(" \r\n"), + }, + + // + // End conditional inclusion of keys. Conditional op count = 3. + // + { ChunkOpRaw, RCS( - "_TableData.c\" />\r\n" " \r\n" " \r\n" "\r\n" diff --git a/src/PerfectHash/Chm01FileWorkVCProjectTestExeFile.c b/src/PerfectHash/Chm01FileWorkVCProjectTestExeFile.c index 4bdcc1ac..1c3604bd 100644 --- a/src/PerfectHash/Chm01FileWorkVCProjectTestExeFile.c +++ b/src/PerfectHash/Chm01FileWorkVCProjectTestExeFile.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023. Trent Nelson Module Name: @@ -73,7 +73,13 @@ PrepareVCProjectTestExeFileChm01( Values.TargetPrefix = &TestTargetPrefix; Values.ConfigurationType = &ApplicationConfigurationTypeA; - Result = ProcessChunks(Rtl, Chunks, NumberOfChunks, &Values, &Output); + Result = ProcessChunks(Rtl, + Chunks, + NumberOfChunks, + &Values, + 0, + NULL, + &Output); if (FAILED(Result)) { PH_ERROR(ProcessChunks, Result); diff --git a/src/PerfectHash/Chunk.c b/src/PerfectHash/Chunk.c index b69849f7..22a1ef0a 100644 --- a/src/PerfectHash/Chunk.c +++ b/src/PerfectHash/Chunk.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023. Trent Nelson Module Name: @@ -23,6 +23,8 @@ ProcessChunks( PCCHUNK Chunks, ULONG NumberOfChunks, PCCHUNK_VALUES Values, + ULONG NumberOfConditionals, + PBOOLEAN Conditionals, PCHAR *BufferPointer ) /*++ @@ -42,6 +44,12 @@ Routine Description: Values - Supplies the chunk values to use for substitution as applicable. + NumberOfConditionals - Supplies the number of conditional chunk ops in the + array of chunks. + + Conditionals - Supplies the array of boolean conditions that dictate if + a conditional chunk is written. + BufferPointer - Supplies a pointer to the buffer that will receive the processed chunk strings. This pointer will also be updated to point at the new end of buffer after this routine completes successfully. @@ -59,6 +67,8 @@ Return Value: --*/ { ULONG Index; + CHUNK_OP NewOp; + ULONG NumberOfConditionalsSeen = 0; PCCHUNK Chunk; HRESULT Result = S_OK; PCSTRING String; @@ -107,6 +117,22 @@ Return Value: String = &Chunk->RawString; } else if (Chunk->Op == ChunkOpStringPointer) { String = Chunk->StringPointer; + } else if (IsConditionalChunkOp(Chunk->Op, &NewOp)) { + if (++NumberOfConditionalsSeen > NumberOfConditionals) { + Result = PH_E_INVALID_NUMBER_OF_CONDITIONALS; + goto Error; + } + if (Conditionals[NumberOfConditionalsSeen-1] != FALSE) { + if (NewOp == ChunkOpRaw) { + String = &Chunk->RawString; + } else if (Chunk->Op == ChunkOpStringPointer) { + String = Chunk->StringPointer; + } else { + String = *(&Values->First + NewOp); + } + } else { + continue; + } } else { String = *(&Values->First + Chunk->Op); } @@ -125,6 +151,11 @@ Return Value: Output += String->Length; } + if (NumberOfConditionals != NumberOfConditionalsSeen) { + Result = PH_E_NUMBER_OF_CONDITIONALS_MISMATCHED; + goto Error; + } + goto End; Error: diff --git a/src/PerfectHash/Chunk.h b/src/PerfectHash/Chunk.h index 75bf9d4c..c994b7d1 100644 --- a/src/PerfectHash/Chunk.h +++ b/src/PerfectHash/Chunk.h @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023. Trent Nelson Module Name: @@ -35,9 +35,11 @@ typedef enum _CHUNK_OP { ChunkOpInsertFileSuffix, ChunkOpInsertTargetPrefix, ChunkOpInsertTargetSuffix, + ChunkOpInsertBaseNameConditional, + ChunkOpRawConditional, ChunkOpInvalid, ChunkOpLast = ChunkOpInvalid - 1, -} CHUNK_OP; +} CHUNK_OP, *PCHUNK_OP; FORCEINLINE BOOLEAN @@ -48,6 +50,29 @@ IsValidChunkOp( return Op >= ChunkOpRaw && Op <= ChunkOpLast; } +FORCEINLINE +BOOLEAN +IsConditionalChunkOp( + _In_ CHUNK_OP Op, + _Out_ PCHUNK_OP NewOp + ) +{ + BOOLEAN Success; + + if (Op == ChunkOpRawConditional) { + Success = TRUE; + *NewOp = ChunkOpRaw; + } else if (Op == ChunkOpInsertBaseNameConditional) { + Success = TRUE; + *NewOp = ChunkOpInsertBaseName; + } else { + Success = FALSE; + *NewOp = ChunkOpInvalid; + } + + return Success; +} + // // Define the values used at runtime in corresponding with the matching chunk // op above. @@ -117,6 +142,8 @@ HRESULT _In_reads_(NumberOfChunks) PCCHUNK Chunks, _In_ ULONG NumberOfChunks, _In_ PCCHUNK_VALUES Values, + _In_ ULONG NumberOfConditionals, + _In_reads_(NumberOfConditionals) PBOOLEAN Conditionals, _Inout_ PCHAR *BufferPointer ); typedef PROCESS_CHUNKS *PPROCESS_CHUNKS; diff --git a/src/PerfectHash/CompiledPerfectHashMacroGlue_CHeader_RawCString.h b/src/PerfectHash/CompiledPerfectHashMacroGlue_CHeader_RawCString.h index 825f05d5..4c564864 100644 --- a/src/PerfectHash/CompiledPerfectHashMacroGlue_CHeader_RawCString.h +++ b/src/PerfectHash/CompiledPerfectHashMacroGlue_CHeader_RawCString.h @@ -94,14 +94,18 @@ const CHAR CompiledPerfectHashMacroGlueCHeaderRawCStr[] = "\n" "#define CPH_INDEX_ROUTINE_NAME(T) CompiledPerfectHash_##T##_Index\n" "#define CPH_INDEX_IACA_ROUTINE_NAME(T) CompiledPerfectHash_##T##_IndexIaca\n" + "#define CPH_INDEX_BSEARCH_ROUTINE_NAME(T) CompiledPerfectHash_##T##_IndexBsearch\n" "#define CPH_LOOKUP_ROUTINE_NAME(T) CompiledPerfectHash_##T##_Lookup\n" "#define CPH_INSERT_ROUTINE_NAME(T) CompiledPerfectHash_##T##_Insert\n" "#define CPH_DELETE_ROUTINE_NAME(T) CompiledPerfectHash_##T##_Delete\n" + "#define CPH_INTERLOCKED_INCREMENT_ROUTINE_NAME(T) CompiledPerfectHash_##T##_InterlockedIncrement\n" "\n" "#define CPH_INDEX_INLINE_ROUTINE_NAME(T) CompiledPerfectHash_##T##_IndexInline\n" + "#define CPH_INDEX_BSEARCH_INLINE_ROUTINE_NAME(T) CompiledPerfectHash_##T##_IndexBsearchInline\n" "#define CPH_LOOKUP_INLINE_ROUTINE_NAME(T) CompiledPerfectHash_##T##_LookupInline\n" "#define CPH_INSERT_INLINE_ROUTINE_NAME(T) CompiledPerfectHash_##T##_InsertInline\n" "#define CPH_DELETE_INLINE_ROUTINE_NAME(T) CompiledPerfectHash_##T##_DeleteInline\n" + "#define CPH_INTERLOCKED_INCREMENT_INLINE_ROUTINE_NAME(T) CompiledPerfectHash_##T##_InterlockedIncrementInline\n" "\n" "////////////////////////////////////////////////////////////////////////////////\n" "// Index\n" @@ -131,6 +135,16 @@ const CHAR CompiledPerfectHashMacroGlueCHeaderRawCStr[] = " CPHKEY Key \\\n" " )\n" "\n" + "#define CPH_INDEX_BSEARCH_ROUTINE_HEADER(T) \\\n" + "CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX \\\n" + " CompiledPerfectHash_##T##_IndexBsearch; \\\n" + " \\\n" + "_Use_decl_annotations_ \\\n" + "CPHINDEX \\\n" + "CompiledPerfectHash_##T##_IndexBsearch( \\\n" + " CPHKEY Key \\\n" + " )\n" + "\n" "//\n" "// Inline\n" "//\n" @@ -142,6 +156,13 @@ const CHAR CompiledPerfectHashMacroGlueCHeaderRawCStr[] = " CPHKEY Key \\\n" " )\n" "\n" + "#define CPH_INDEX_BSEARCH_INLINE_ROUTINE_HEADER(T) \\\n" + "FORCEINLINE \\\n" + "CPHINDEX \\\n" + "CompiledPerfectHash_##T##_IndexBsearchInline( \\\n" + " CPHKEY Key \\\n" + " )\n" + "\n" "////////////////////////////////////////////////////////////////////////////////\n" "// Lookup\n" "////////////////////////////////////////////////////////////////////////////////\n" @@ -232,6 +253,35 @@ const CHAR CompiledPerfectHashMacroGlueCHeaderRawCStr[] = " CPHKEY Key \\\n" " )\n" "\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "// InterlockedIncrement\n" + "////////////////////////////////////////////////////////////////////////////////\n" + "\n" + "//\n" + "// Normal\n" + "//\n" + "\n" + "#define CPH_INTERLOCKED_INCREMENT_ROUTINE_HEADER(T) \\\n" + "CPHAPI COMPILED_PERFECT_HASH_TABLE_INTERLOCKED_INCREMENT \\\n" + " CompiledPerfectHash_##T##_InterlockedIncrement; \\\n" + " \\\n" + "_Use_decl_annotations_ \\\n" + "CPHVALUE \\\n" + "CompiledPerfectHash_##T##_InterlockedIncrement( \\\n" + " CPHKEY Key \\\n" + " )\n" + "\n" + "//\n" + "// Inline\n" + "//\n" + "\n" + "#define CPH_INTERLOCKED_INCREMENT_INLINE_ROUTINE_HEADER(T) \\\n" + "FORCEINLINE \\\n" + "CPHVALUE \\\n" + "CompiledPerfectHash_##T##_InterlockedIncrementInline( \\\n" + " CPHKEY Key \\\n" + " )\n" + "\n" "\n" "////////////////////////////////////////////////////////////////////////////////\n" "// Test and Benchmarking\n" @@ -279,7 +329,8 @@ const CHAR CompiledPerfectHashMacroGlueCHeaderRawCStr[] = "#ifdef CPH_INDEX_ONLY\n" "#define CPH_DEFINE_TABLE_ROUTINES(T) \\\n" "CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_Index; \\\n" - "CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_IndexIaca\n" + "CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_IndexIaca; \\\n" + "CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_IndexBsearch \\\n" "\n" "#define CPH_INDEX_ROUTINE(T) CPH_INDEX_ROUTINE_NAME(T)\n" "#define CPH_INDEX_IACA_ROUTINE(T) CPH_INDEX_IACA_ROUTINE_NAME(T)\n" @@ -290,12 +341,14 @@ const CHAR CompiledPerfectHashMacroGlueCHeaderRawCStr[] = " BenchmarkIndexCompiledPerfectHashTable_##T\n" "\n" "#else\n" - "#define CPH_DEFINE_TABLE_ROUTINES(T) \\\n" - "CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_Index; \\\n" - "CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_IndexIaca; \\\n" - "CPHAPI COMPILED_PERFECT_HASH_TABLE_LOOKUP CompiledPerfectHash_##T##_Lookup; \\\n" - "CPHAPI COMPILED_PERFECT_HASH_TABLE_INSERT CompiledPerfectHash_##T##_Insert; \\\n" - "CPHAPI COMPILED_PERFECT_HASH_TABLE_DELETE CompiledPerfectHash_##T##_Delete\n" + "#define CPH_DEFINE_TABLE_ROUTINES(T) \\\n" + "CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_Index; \\\n" + "CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_IndexIaca; \\\n" + "CPHAPI COMPILED_PERFECT_HASH_TABLE_INDEX CompiledPerfectHash_##T##_IndexBsearch; \\\n" + "CPHAPI COMPILED_PERFECT_HASH_TABLE_LOOKUP CompiledPerfectHash_##T##_Lookup; \\\n" + "CPHAPI COMPILED_PERFECT_HASH_TABLE_INSERT CompiledPerfectHash_##T##_Insert; \\\n" + "CPHAPI COMPILED_PERFECT_HASH_TABLE_DELETE CompiledPerfectHash_##T##_Delete; \\\n" + "CPHAPI COMPILED_PERFECT_HASH_TABLE_INTERLOCKED_INCREMENT CompiledPerfectHash_##T##_InterlockedIncrement\n" "\n" "#define CPH_DEFINE_TEST_AND_BENCHMARKING_ROUTINES(T) \\\n" "extern TEST_COMPILED_PERFECT_HASH_TABLE \\\n" @@ -309,14 +362,18 @@ const CHAR CompiledPerfectHashMacroGlueCHeaderRawCStr[] = "\n" "#define CPH_INDEX_ROUTINE(T) CPH_INDEX_ROUTINE_NAME(T)\n" "#define CPH_INDEX_IACA_ROUTINE(T) CPH_INDEX_IACA_ROUTINE_NAME(T)\n" + "#define CPH_INDEX_BSEARCH_ROUTINE(T) CPH_INDEX_BSEARCH_ROUTINE_NAME(T)\n" "#define CPH_LOOKUP_ROUTINE(T) CPH_LOOKUP_ROUTINE_NAME(T)\n" "#define CPH_INSERT_ROUTINE(T) CPH_INSERT_ROUTINE_NAME(T)\n" "#define CPH_DELETE_ROUTINE(T) CPH_DELETE_ROUTINE_NAME(T)\n" + "#define CPH_INTERLOCKED_INCREMENT_ROUTINE(T) CPH_INTERLOCKED_INCREMENT_ROUTINE_NAME(T)\n" "\n" "#define CPH_INDEX_INLINE_ROUTINE(T) CPH_INDEX_INLINE_ROUTINE_NAME(T)\n" + "#define CPH_INDEX_BSEARCH_INLINE_ROUTINE(T) CPH_INDEX_BSEARCH_INLINE_ROUTINE_NAME(T)\n" "#define CPH_LOOKUP_INLINE_ROUTINE(T) CPH_LOOKUP_INLINE_ROUTINE_NAME(T)\n" "#define CPH_INSERT_INLINE_ROUTINE(T) CPH_INSERT_INLINE_ROUTINE_NAME(T)\n" "#define CPH_DELETE_INLINE_ROUTINE(T) CPH_DELETE_INLINE_ROUTINE_NAME(T)\n" + "#define CPH_INTERLOCKED_INCREMENT_INLINE_ROUTINE(T) CPH_INTERLOCKED_INCREMENT_INLINE_ROUTINE_NAME(T)\n" "#endif\n" "\n" "#define EXPAND_SEED1(U) CPH_SEED1(U)\n" @@ -402,25 +459,34 @@ const CHAR CompiledPerfectHashMacroGlueCHeaderRawCStr[] = "\n" "#define EXPAND_INDEX_ROUTINE(T) CPH_INDEX_ROUTINE(T)\n" "#define EXPAND_INDEX_IACA_ROUTINE(T) CPH_INDEX_IACA_ROUTINE(T)\n" + "#define EXPAND_INDEX_BSEARCH_ROUTINE(T) CPH_INDEX_BSEARCH_ROUTINE(T)\n" "#define EXPAND_LOOKUP_ROUTINE(T) CPH_LOOKUP_ROUTINE(T)\n" "#define EXPAND_INSERT_ROUTINE(T) CPH_INSERT_ROUTINE(T)\n" "#define EXPAND_DELETE_ROUTINE(T) CPH_DELETE_ROUTINE(T)\n" + "#define EXPAND_INTERLOCKED_INCREMENT_ROUTINE(T) CPH_INTERLOCKED_INCREMENT_ROUTINE(T)\n" "\n" "#define EXPAND_INDEX_INLINE_ROUTINE(T) CPH_INDEX_INLINE_ROUTINE(T)\n" + "#define EXPAND_INDEX_BSEARCH_INLINE_ROUTINE(T) CPH_INDEX_BSEARCH_INLINE_ROUTINE(T)\n" "#define EXPAND_LOOKUP_INLINE_ROUTINE(T) CPH_LOOKUP_INLINE_ROUTINE(T)\n" "#define EXPAND_INSERT_INLINE_ROUTINE(T) CPH_INSERT_INLINE_ROUTINE(T)\n" "#define EXPAND_DELETE_INLINE_ROUTINE(T) CPH_DELETE_INLINE_ROUTINE(T)\n" + "#define EXPAND_INTERLOCKED_INCREMENT_INLINE_ROUTINE(T) CPH_INTERLOCKED_INCREMENT_INLINE_ROUTINE(T)\n" + "#define EXPAND_INDEX_BSEARCH_INLINE_ROUTINE(T) CPH_INDEX_BSEARCH_INLINE_ROUTINE(T)\n" "\n" "#define EXPAND_INDEX_ROUTINE_HEADER(T) CPH_INDEX_ROUTINE_HEADER(T)\n" "#define EXPAND_INDEX_IACA_ROUTINE_HEADER(T) CPH_INDEX_IACA_ROUTINE_HEADER(T)\n" + "#define EXPAND_INDEX_BSEARCH_ROUTINE_HEADER(T) CPH_INDEX_BSEARCH_ROUTINE_HEADER(T)\n" "#define EXPAND_LOOKUP_ROUTINE_HEADER(T) CPH_LOOKUP_ROUTINE_HEADER(T)\n" "#define EXPAND_INSERT_ROUTINE_HEADER(T) CPH_INSERT_ROUTINE_HEADER(T)\n" "#define EXPAND_DELETE_ROUTINE_HEADER(T) CPH_DELETE_ROUTINE_HEADER(T)\n" + "#define EXPAND_INTERLOCKED_INCREMENT_ROUTINE_HEADER(T) CPH_INTERLOCKED_INCREMENT_ROUTINE_HEADER(T)\n" "\n" "#define EXPAND_INDEX_INLINE_ROUTINE_HEADER(T) CPH_INDEX_INLINE_ROUTINE_HEADER(T)\n" + "#define EXPAND_INDEX_BSEARCH_INLINE_ROUTINE_HEADER(T) CPH_INDEX_BSEARCH_INLINE_ROUTINE_HEADER(T)\n" "#define EXPAND_LOOKUP_INLINE_ROUTINE_HEADER(T) CPH_LOOKUP_INLINE_ROUTINE_HEADER(T)\n" "#define EXPAND_INSERT_INLINE_ROUTINE_HEADER(T) CPH_INSERT_INLINE_ROUTINE_HEADER(T)\n" "#define EXPAND_DELETE_INLINE_ROUTINE_HEADER(T) CPH_DELETE_INLINE_ROUTINE_HEADER(T)\n" + "#define EXPAND_INTERLOCKED_INCREMENT_INLINE_ROUTINE_HEADER(T) CPH_INTERLOCKED_INCREMENT_INLINE_ROUTINE_HEADER(T)\n" "\n" "#define EXPAND_TEST_CPH_ROUTINE_HEADER(T) TEST_CPH_ROUTINE_HEADER(T)\n" "#define EXPAND_BENCHMARK_FULL_CPH_ROUTINE_HEADER(T) BENCHMARK_FULL_CPH_ROUTINE_HEADER(T)\n" @@ -514,20 +580,27 @@ const CHAR CompiledPerfectHashMacroGlueCHeaderRawCStr[] = "#define ROTATE_KEY_RIGHT EXPAND_ROTATE_KEY_RIGHT(CPH_TABLENAME_UPPER)\n" "\n" "#define DECLARE_INDEX_ROUTINE_HEADER() EXPAND_INDEX_ROUTINE_HEADER(CPH_TABLENAME)\n" + "#define DECLARE_INDEX_BSEARCH_ROUTINE_HEADER() EXPAND_INDEX_BSEARCH_ROUTINE_HEADER(CPH_TABLENAME)\n" "#define DECLARE_INDEX_IACA_ROUTINE_HEADER() EXPAND_INDEX_IACA_ROUTINE_HEADER(CPH_TABLENAME)\n" "#define DECLARE_LOOKUP_ROUTINE_HEADER() EXPAND_LOOKUP_ROUTINE_HEADER(CPH_TABLENAME)\n" "#define DECLARE_INSERT_ROUTINE_HEADER() EXPAND_INSERT_ROUTINE_HEADER(CPH_TABLENAME)\n" "#define DECLARE_DELETE_ROUTINE_HEADER() EXPAND_DELETE_ROUTINE_HEADER(CPH_TABLENAME)\n" + "#define DECLARE_INTERLOCKED_INCREMENT_ROUTINE_HEADER() EXPAND_INTERLOCKED_INCREMENT_ROUTINE_HEADER(CPH_TABLENAME)\n" "\n" "#define DECLARE_INDEX_INLINE_ROUTINE_HEADER() EXPAND_INDEX_INLINE_ROUTINE_HEADER(CPH_TABLENAME)\n" + "#define DECLARE_INDEX_BSEARCH_ROUTINE_HEADER() EXPAND_INDEX_BSEARCH_ROUTINE_HEADER(CPH_TABLENAME)\n" "#define DECLARE_LOOKUP_INLINE_ROUTINE_HEADER() EXPAND_LOOKUP_INLINE_ROUTINE_HEADER(CPH_TABLENAME)\n" "#define DECLARE_INSERT_INLINE_ROUTINE_HEADER() EXPAND_INSERT_INLINE_ROUTINE_HEADER(CPH_TABLENAME)\n" "#define DECLARE_DELETE_INLINE_ROUTINE_HEADER() EXPAND_DELETE_INLINE_ROUTINE_HEADER(CPH_TABLENAME)\n" + "#define DECLARE_INTERLOCKED_INCREMENT_INLINE_ROUTINE_HEADER() \\\n" + " EXPAND_INTERLOCKED_INCREMENT_INLINE_ROUTINE_HEADER(CPH_TABLENAME)\n" "\n" "#define INDEX_INLINE_ROUTINE EXPAND_INDEX_INLINE_ROUTINE(CPH_TABLENAME)\n" + "#define INDEX_BSEARCH_INLINE_ROUTINE EXPAND_INDEX_BSEARCH_INLINE_ROUTINE(CPH_TABLENAME)\n" "#define LOOKUP_INLINE_ROUTINE EXPAND_LOOKUP_INLINE_ROUTINE(CPH_TABLENAME)\n" "#define INSERT_INLINE_ROUTINE EXPAND_INSERT_INLINE_ROUTINE(CPH_TABLENAME)\n" "#define DELETE_INLINE_ROUTINE EXPAND_DELETE_INLINE_ROUTINE(CPH_TABLENAME)\n" + "#define INTERLOCKED_INCREMENT_INLINE_ROUTINE EXPAND_INTERLOCKED_INCREMENT_INLINE_ROUTINE(CPH_TABLENAME)\n" "\n" "#define TEST_CPH_ROUTINE EXPAND_TEST_CPH_ROUTINE_NAME(CPH_TABLENAME)\n" "#define BENCHMARK_FULL_CPH_ROUTINE EXPAND_BENCHMARK_FULL_CPH_ROUTINE_NAME(CPH_TABLENAME)\n" @@ -545,11 +618,6 @@ const CHAR CompiledPerfectHashMacroGlueCHeaderRawCStr[] = "// End CompiledPerfectHashMacroGlue.h.\n" "//\n" "\n" - "\n" - "//\n" - "// End CompiledPerfectHashMacroGlue.h.\n" - "//\n" - "\n" ; const STRING CompiledPerfectHashMacroGlueCHeaderRawCString = { diff --git a/src/PerfectHash/CompiledPerfectHashTableRoutinesPost_CSource_RawCString.h b/src/PerfectHash/CompiledPerfectHashTableRoutinesPost_CSource_RawCString.h index a34306f7..382f97c5 100644 --- a/src/PerfectHash/CompiledPerfectHashTableRoutinesPost_CSource_RawCString.h +++ b/src/PerfectHash/CompiledPerfectHashTableRoutinesPost_CSource_RawCString.h @@ -15,30 +15,38 @@ const CHAR CompiledPerfectHashTableRoutinesPostCSourceRawCStr[] = "#undef CPH_INLINE_ROUTINES\n" "\n" "#undef INDEX_ROUTINE\n" + "#undef INDEX_BSEARCH_ROUTINE\n" "#undef DECLARE_INDEX_ROUTINE\n" + "#undef DECLARE_INDEX_BSEARCH_ROUTINE\n" "\n" "#ifndef CPH_INDEX_ONLY\n" "#undef LOOKUP_ROUTINE\n" "#undef INSERT_ROUTINE\n" "#undef DELETE_ROUTINE\n" + "#undef INTERLOCKED_INCREMENT_ROUTINE\n" "\n" "#undef DECLARE_LOOKUP_ROUTINE\n" "#undef DECLARE_INSERT_ROUTINE\n" "#undef DECLARE_DELETE_ROUTINE\n" + "#undef DECLARE_INTERLOCKED_INCREMENT_ROUTINE\n" "#endif\n" "\n" "#define INDEX_ROUTINE EXPAND_INDEX_ROUTINE(CPH_TABLENAME)\n" + "#define INDEX_BSEARCH_ROUTINE EXPAND_INDEX_BSEARCH_ROUTINE(CPH_TABLENAME)\n" "#define DECLARE_INDEX_ROUTINE() EXPAND_INDEX_ROUTINE_HEADER(CPH_TABLENAME)\n" "#define DECLARE_INDEX_IACA_ROUTINE() EXPAND_INDEX_IACA_ROUTINE_HEADER(CPH_TABLENAME)\n" + "#define DECLARE_INDEX_BSEARCH_ROUTINE() EXPAND_INDEX_BSEARCH_ROUTINE_HEADER(CPH_TABLENAME)\n" "\n" "#ifndef CPH_INDEX_ONLY\n" "#define LOOKUP_ROUTINE EXPAND_LOOKUP_ROUTINE(CPH_TABLENAME)\n" "#define INSERT_ROUTINE EXPAND_INSERT_ROUTINE(CPH_TABLENAME)\n" "#define DELETE_ROUTINE EXPAND_DELETE_ROUTINE(CPH_TABLENAME)\n" + "#define INTERLOCKED_INCREMENT_ROUTINE EXPAND_INTERLOCKED_INCREMENT_ROUTINE(CPH_TABLENAME)\n" "\n" "#define DECLARE_LOOKUP_ROUTINE() EXPAND_LOOKUP_ROUTINE_HEADER(CPH_TABLENAME)\n" "#define DECLARE_INSERT_ROUTINE() EXPAND_INSERT_ROUTINE_HEADER(CPH_TABLENAME)\n" "#define DECLARE_DELETE_ROUTINE() EXPAND_DELETE_ROUTINE_HEADER(CPH_TABLENAME)\n" + "#define DECLARE_INTERLOCKED_INCREMENT_ROUTINE() EXPAND_INTERLOCKED_INCREMENT_ROUTINE_HEADER(CPH_TABLENAME)\n" "#endif\n" "\n" "#endif\n" diff --git a/src/PerfectHash/CompiledPerfectHashTableRoutinesPre_CSource_RawCString.h b/src/PerfectHash/CompiledPerfectHashTableRoutinesPre_CSource_RawCString.h index 0a29e915..7e3efded 100644 --- a/src/PerfectHash/CompiledPerfectHashTableRoutinesPre_CSource_RawCString.h +++ b/src/PerfectHash/CompiledPerfectHashTableRoutinesPre_CSource_RawCString.h @@ -13,31 +13,40 @@ const CHAR CompiledPerfectHashTableRoutinesPreCSourceRawCStr[] = "#ifdef CPH_INLINE_ROUTINES\n" "\n" "#define INDEX_ROUTINE EXPAND_INDEX_INLINE_ROUTINE(CPH_TABLENAME)\n" + "#define INDEX_BSEARCH_ROUTINE EXPAND_INDEX_BSEARCH_INLINE_ROUTINE(CPH_TABLENAME)\n" "#define DECLARE_INDEX_ROUTINE() EXPAND_INDEX_INLINE_ROUTINE_HEADER(CPH_TABLENAME)\n" + "#define DECLARE_INDEX_BSEARCH_ROUTINE() EXPAND_INDEX_BSEARCH_INLINE_ROUTINE_HEADER(CPH_TABLENAME)\n" "\n" "#ifndef CPH_INDEX_ONLY\n" "#define LOOKUP_ROUTINE EXPAND_LOOKUP_INLINE_ROUTINE(CPH_TABLENAME)\n" "#define INSERT_ROUTINE EXPAND_INSERT_INLINE_ROUTINE(CPH_TABLENAME)\n" "#define DELETE_ROUTINE EXPAND_DELETE_INLINE_ROUTINE(CPH_TABLENAME)\n" + "#define INTERLOCKED_INCREMENT_ROUTINE EXPAND_INTERLOCKED_INCREMENT_INLINE_ROUTINE(CPH_TABLENAME)\n" "\n" "#define DECLARE_LOOKUP_ROUTINE() EXPAND_LOOKUP_INLINE_ROUTINE_HEADER(CPH_TABLENAME)\n" "#define DECLARE_INSERT_ROUTINE() EXPAND_INSERT_INLINE_ROUTINE_HEADER(CPH_TABLENAME)\n" "#define DECLARE_DELETE_ROUTINE() EXPAND_DELETE_INLINE_ROUTINE_HEADER(CPH_TABLENAME)\n" + "#define DECLARE_INTERLOCKED_INCREMENT_ROUTINE() \\\n" + " EXPAND_INTERLOCKED_INCREMENT_INLINE_ROUTINE_HEADER(CPH_TABLENAME)\n" "#endif\n" "\n" "#else\n" "\n" "#define INDEX_ROUTINE EXPAND_INDEX_ROUTINE(CPH_TABLENAME)\n" + "#define INDEX_BSEARCH_ROUTINE EXPAND_INDEX_BSEARCH_ROUTINE(CPH_TABLENAME)\n" "#define DECLARE_INDEX_ROUTINE() EXPAND_INDEX_ROUTINE_HEADER(CPH_TABLENAME)\n" + "#define DECLARE_INDEX_BSEARCH_ROUTINE() EXPAND_INDEX_BSEARCH_ROUTINE_HEADER(CPH_TABLENAME)\n" "\n" "#ifndef CPH_INDEX_ONLY\n" "#define LOOKUP_ROUTINE EXPAND_LOOKUP_ROUTINE(CPH_TABLENAME)\n" "#define INSERT_ROUTINE EXPAND_INSERT_ROUTINE(CPH_TABLENAME)\n" "#define DELETE_ROUTINE EXPAND_DELETE_ROUTINE(CPH_TABLENAME)\n" + "#define INTERLOCKED_INCREMENT_ROUTINE EXPAND_INTERLOCKED_INCREMENT_ROUTINE(CPH_TABLENAME)\n" "\n" "#define DECLARE_LOOKUP_ROUTINE() EXPAND_LOOKUP_ROUTINE_HEADER(CPH_TABLENAME)\n" "#define DECLARE_INSERT_ROUTINE() EXPAND_INSERT_ROUTINE_HEADER(CPH_TABLENAME)\n" "#define DECLARE_DELETE_ROUTINE() EXPAND_DELETE_ROUTINE_HEADER(CPH_TABLENAME)\n" + "#define DECLARE_INTERLOCKED_INCREMENT_ROUTINE() EXPAND_INTERLOCKED_INCREMENT_ROUTINE_HEADER(CPH_TABLENAME)\n" "#endif\n" "\n" "#endif\n" diff --git a/src/PerfectHash/CompiledPerfectHashTableRoutines_CSource_RawCString.h b/src/PerfectHash/CompiledPerfectHashTableRoutines_CSource_RawCString.h index e54eea6b..c434dc6f 100644 --- a/src/PerfectHash/CompiledPerfectHashTableRoutines_CSource_RawCString.h +++ b/src/PerfectHash/CompiledPerfectHashTableRoutines_CSource_RawCString.h @@ -42,6 +42,51 @@ const CHAR CompiledPerfectHashTableRoutinesCSourceRawCStr[] = " return Previous;\n" "}\n" "\n" + "DECLARE_INTERLOCKED_INCREMENT_ROUTINE()\n" + "{\n" + " CPHINDEX Index;\n" + " CPHVALUE New;\n" + "\n" + " Index = INDEX_ROUTINE(Key);\n" + "#ifdef _WIN32\n" + " New = _InterlockedIncrement((volatile LONG *)&TABLE_VALUES[Index]);\n" + "#else\n" + " New = __sync_add_and_fetch(&TABLE_VALUES[Index], 1);\n" + "#endif\n" + " return New;\n" + "}\n" + "\n" + "#ifdef CPH_HAS_KEYS\n" + "DECLARE_INDEX_BSEARCH_ROUTINE()\n" + "{\n" + " CPHINDEX Low;\n" + " CPHINDEX High;\n" + " CPHINDEX Middle;\n" + " CPHDKEY Value;\n" + "\n" + " Low = 0;\n" + " High = NUMBER_OF_KEYS - 1;\n" + " Middle = (Low + High) / 2;\n" + "\n" + " while (Low <= High) {\n" + "\n" + " Value = KEYS[Middle];\n" + "\n" + " if (Value == Key) {\n" + " break;\n" + " } else if (Value < Key) {\n" + " Low = Middle + 1;\n" + " } else {\n" + " High = Middle - 1;\n" + " }\n" + "\n" + " Middle = (Low + High) / 2;\n" + " }\n" + "\n" + " return Middle;\n" + "}\n" + "#endif\n" + "\n" "#endif\n" "\n" "//\n" diff --git a/src/PerfectHash/CompiledPerfectHash_CHeader_RawCString.h b/src/PerfectHash/CompiledPerfectHash_CHeader_RawCString.h index dff0f7a0..93a8c7a7 100644 --- a/src/PerfectHash/CompiledPerfectHash_CHeader_RawCString.h +++ b/src/PerfectHash/CompiledPerfectHash_CHeader_RawCString.h @@ -11,7 +11,7 @@ const CHAR CompiledPerfectHashCHeaderRawCStr[] = "\n" "/*++\n" "\n" - "Copyright (c) 2018-2022 Trent Nelson \n" + "Copyright (c) 2018-2023 Trent Nelson \n" "\n" "Module Name:\n" "\n" @@ -61,10 +61,14 @@ const CHAR CompiledPerfectHashCHeaderRawCStr[] = "#endif\n" "\n" "//\n" - "// Clang doesn't appear to support the rotate intrinsics _rotr and _rotl,\n" - "// so, define some static inline versions here.\n" + "// Older versions of clang didn't appear to support the rotate intrinsics _rotr\n" + "// and _rotl, so, we used some static inline versions, below. Recent versions\n" + "// (10.0+) appear to have the intrinsics, so, disable this block for now. If\n" + "// you are compiling on an older version of clang, change the 0 to 1 to get the\n" + "// rotate intrinsics back.\n" "//\n" "\n" + "#if 0\n" "static inline\n" "unsigned int\n" "_rotl(\n" @@ -86,6 +90,7 @@ const CHAR CompiledPerfectHashCHeaderRawCStr[] = " b &= 31;\n" " return (a >> b) | (a << (32 - b));\n" "}\n" + "#endif\n" "\n" "#elif defined(__GNUC__)\n" "#include \n" @@ -275,6 +280,30 @@ const CHAR CompiledPerfectHashCHeaderRawCStr[] = "--*/\n" "typedef COMPILED_PERFECT_HASH_TABLE_DELETE *PCOMPILED_PERFECT_HASH_TABLE_DELETE;\n" "\n" + "typedef\n" + "CPHAPI\n" + "CPHVALUE\n" + "(CPHCALLTYPE COMPILED_PERFECT_HASH_TABLE_INTERLOCKED_INCREMENT)(\n" + " _In_ CPHKEY Key\n" + " );\n" + "/*++\n" + "\n" + "Routine Description:\n" + "\n" + " Increments the value associated with a key.\n" + "\n" + "Arguments:\n" + "\n" + " Key - Supplies the key to increment.\n" + "\n" + "Return Value:\n" + "\n" + " Previous value.\n" + "\n" + "--*/\n" + "typedef COMPILED_PERFECT_HASH_TABLE_INTERLOCKED_INCREMENT\n" + " *PCOMPILED_PERFECT_HASH_TABLE_INTERLOCKED_INCREMENT;\n" + "\n" "//\n" "// Typedefs of methods for testing and benchmarking.\n" "//\n" diff --git a/src/PerfectHash/Component.c b/src/PerfectHash/Component.c index 3ea3b1de..08d508c9 100644 --- a/src/PerfectHash/Component.c +++ b/src/PerfectHash/Component.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023. Trent Nelson Module Name: @@ -238,7 +238,9 @@ CreateComponent( // Unknown = &Component->Unknown; + ASSERT(Unknown->ReferenceCount == 0); Unknown->Vtbl->AddRef(Unknown); + ASSERT(Unknown->ReferenceCount == 1); if (InterlockedIncrement(&ComponentCount) == 1) { diff --git a/src/PerfectHash/ExtractArg.c b/src/PerfectHash/ExtractArg.c index fefbef73..4adf134f 100644 --- a/src/PerfectHash/ExtractArg.c +++ b/src/PerfectHash/ExtractArg.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -175,6 +175,7 @@ TryExtractArgTableCreateFlags( DECL_ARG(TryUseAvx2HashFunction); DECL_ARG(TryUseAvx512HashFunction); DECL_ARG(DoNotTryUseAvx2MemoryCoverageFunction); + DECL_ARG(IncludeKeysInCompiledDll); UNREFERENCED_PARAMETER(Allocator); @@ -223,6 +224,7 @@ TryExtractArgTableCreateFlags( SET_FLAG_AND_RETURN_IF_EQUAL(TryUseAvx2HashFunction); SET_FLAG_AND_RETURN_IF_EQUAL(TryUseAvx512HashFunction); SET_FLAG_AND_RETURN_IF_EQUAL(DoNotTryUseAvx2MemoryCoverageFunction); + SET_FLAG_AND_RETURN_IF_EQUAL(IncludeKeysInCompiledDll); return S_FALSE; } @@ -735,6 +737,7 @@ Return Value: LONG64 Value64 = 0; PWSTR Source; DOUBLE Double; + PCHAR Buffer; PWSTR End; PWSTR Expected; PALLOCATOR Allocator; @@ -743,6 +746,7 @@ Return Value: BOOLEAN EqualSignFound = FALSE; BOOLEAN TableParamFound = FALSE; HRESULT Result = S_FALSE; + PSTRING LocalStringA; PUNICODE_STRING Arg; UNICODE_STRING LocalArg = { 0 }; UNICODE_STRING Temp = { 0 }; @@ -963,6 +967,8 @@ Return Value: ADD_PARAM_IF_EQUAL_AND_VALUE_IS_INTEGER(MaxSolveTimeInSeconds); + ADD_PARAM_IF_EQUAL_AND_VALUE_IS_INTEGER(FunctionHookCallbackIgnoreRip); + #define IS_VALUE_EQUAL(ValueName) \ Rtl->RtlEqualUnicodeString(ValueString, &ValueName, TRUE) @@ -1166,6 +1172,34 @@ Return Value: goto AddParam; } + if (IS_EQUAL(FunctionHookCallbackDllPath)) { + SET_PARAM_ID(FunctionHookCallbackDllPath); + LocalString = &LocalParam.AsUnicodeString; + LocalString->Length = ValueString->Length; + LocalString->MaximumLength = ValueString->MaximumLength; + LocalString->Buffer = ValueString->Buffer; + goto AddParam; + } + + if (IS_EQUAL(FunctionHookCallbackFunctionName)) { + + // + // Do an inline conversion of the wide character function name to a + // normal ASCII representation. (GetProcAddress() only works for + // ASCII names.) + // + + SET_PARAM_ID(FunctionHookCallbackFunctionName); + LocalStringA = &LocalParam.AsString; + LocalStringA->Length = ValueString->Length >> 1; + LocalStringA->MaximumLength = ValueString->MaximumLength >> 1; + LocalStringA->Buffer = (PCHAR)ValueString->Buffer; + Buffer = LocalStringA->Buffer; + AppendWStrToCharBufferFast(&Buffer, ValueString->Buffer); + LocalStringA->Buffer[LocalStringA->Length] = '\0'; + goto AddParam; + } + // // We shouldn't ever get here; we've already determined that a valid param // was detected earlier in the routine. diff --git a/src/PerfectHash/Graph.c b/src/PerfectHash/Graph.c index 514b3daf..4491d021 100644 --- a/src/PerfectHash/Graph.c +++ b/src/PerfectHash/Graph.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -120,7 +120,7 @@ Return Value: // TlsContext = PerfectHashTlsEnsureContext(); - TableCreateFlags.AsULong = TlsContext->TableCreateFlags.AsULong; + TableCreateFlags.AsULongLong = TlsContext->TableCreateFlags.AsULongLong; HashFunctionId = TlsContext->Table->HashFunctionId; Rtl = Graph->Rtl; @@ -3321,7 +3321,7 @@ Return Value: Allocator = Graph->Allocator; Table = Context->Table; TableInfoOnDisk = Table->TableInfoOnDisk; - TableCreateFlags.AsULong = Table->TableCreateFlags.AsULong; + TableCreateFlags.AsULongLong = Table->TableCreateFlags.AsULongLong; // // Set the relevant graph fields based on the provided info. @@ -3771,7 +3771,7 @@ Return Value: Context = Graph->Context; Info = Graph->Info; Rtl = Context->Rtl; - TableCreateFlags.AsULong = Context->Table->TableCreateFlags.AsULong; + TableCreateFlags.AsULongLong = Context->Table->TableCreateFlags.AsULongLong; MAYBE_STOP_GRAPH_SOLVING(Graph); diff --git a/src/PerfectHash/PerfectHash.vcxproj b/src/PerfectHash/PerfectHash.vcxproj index b498d441..a1925889 100644 --- a/src/PerfectHash/PerfectHash.vcxproj +++ b/src/PerfectHash/PerfectHash.vcxproj @@ -69,6 +69,7 @@ _PERFECT_HASH_INTERNAL_BUILD;%(PreprocessorDefinitions) + /Gh %(AdditionalOptions) false @@ -223,6 +224,7 @@ + @@ -292,6 +294,7 @@ + @@ -299,11 +302,13 @@ - - + + + {b9ef04da-0ad9-45c4-97b0-857a13aa4850} + - \ No newline at end of file + diff --git a/src/PerfectHash/PerfectHash.vcxproj.filters b/src/PerfectHash/PerfectHash.vcxproj.filters index a51bf23f..36300dcf 100644 --- a/src/PerfectHash/PerfectHash.vcxproj.filters +++ b/src/PerfectHash/PerfectHash.vcxproj.filters @@ -316,6 +316,9 @@ Source Files + + Source Files + @@ -665,4 +668,4 @@ Resource Files - \ No newline at end of file + diff --git a/src/PerfectHash/PerfectHashConstants.c b/src/PerfectHash/PerfectHashConstants.c index 6d6102e2..4aab6a6b 100644 --- a/src/PerfectHash/PerfectHashConstants.c +++ b/src/PerfectHash/PerfectHashConstants.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -596,12 +596,18 @@ const UNICODE_STRING DotCSourceSuffix = RCS(L".c"); const UNICODE_STRING NullUnicodeString = RCS(L""); const UNICODE_STRING KeysWildcardSuffix = RCS(L"*.keys"); const UNICODE_STRING KeysTableSizeSuffix = RCS(L".TableSize"); +const UNICODE_STRING TableValuesSuffix = RCS(L"_TableValues_"); +const UNICODE_STRING TableValuesExtension = RCS(L"bin"); + const UNICODE_STRING PerfectHashBulkCreateCsvBaseName = RCS(L"PerfectHashBulkCreate"); + const UNICODE_STRING PerfectHashBulkCreateBestCsvBaseName = RCS(L"PerfectHashBulkCreateBest"); + const UNICODE_STRING PerfectHashTableCreateCsvBaseName = RCS(L"PerfectHashTableCreate"); + const UNICODE_STRING PerfectHashTableCreateBestCsvBaseName = RCS(L"PerfectHashTableCreateBest"); @@ -611,6 +617,8 @@ const STRING DotDllSuffixA = RCS(".dll"); const STRING DotLibSuffixA = RCS(".lib"); const STRING DynamicLibraryConfigurationTypeA = RCS("DynamicLibrary"); const STRING ApplicationConfigurationTypeA = RCS("Application"); +const STRING FunctionHookCallbackDefaultFunctionNameA = + RCS("InterlockedIncrement"); // // VCProject and Makefile string constants. Consumed by the file work callback @@ -648,6 +656,7 @@ const UNICODE_STRING BatchFileExtension = RCS(L"bat"); const UNICODE_STRING CSourceFileExtension = RCS(L"c"); const UNICODE_STRING CHeaderFileExtension = RCS(L"h"); const UNICODE_STRING TableFileExtension = RCS(L"pht1"); +const UNICODE_STRING ModuleDefFileExtension = RCS(L"def"); const UNICODE_STRING VCPropsFileExtension = RCS(L"props"); const UNICODE_STRING MakefileMkFileExtension = RCS(L"mk"); const UNICODE_STRING VCProjectFileExtension = RCS(L"vcxproj"); diff --git a/src/PerfectHash/PerfectHashConstants.h b/src/PerfectHash/PerfectHashConstants.h index b6cda12f..3a78f1c9 100644 --- a/src/PerfectHash/PerfectHashConstants.h +++ b/src/PerfectHash/PerfectHashConstants.h @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -380,6 +380,8 @@ extern const UNICODE_STRING NullUnicodeString; extern const UNICODE_STRING KeysWildcardSuffix; extern const UNICODE_STRING TableInfoStreamName; extern const UNICODE_STRING KeysTableSizeSuffix; +extern const UNICODE_STRING TableValuesSuffix; +extern const UNICODE_STRING TableValuesExtension; extern const UNICODE_STRING PerfectHashBulkCreateCsvBaseName; extern const UNICODE_STRING PerfectHashBulkCreateBestCsvBaseName; extern const UNICODE_STRING PerfectHashTableCreateCsvBaseName; @@ -391,6 +393,7 @@ extern const STRING DotDllSuffixA; extern const STRING DotLibSuffixA; extern const STRING DynamicLibraryConfigurationTypeA; extern const STRING ApplicationConfigurationTypeA; +extern const STRING FunctionHookCallbackDefaultFunctionNameA; extern const STRING BestCoverageTypeNamesA[]; diff --git a/src/PerfectHash/PerfectHashContext.c b/src/PerfectHash/PerfectHashContext.c index 823b679c..e8f8dc60 100644 --- a/src/PerfectHash/PerfectHashContext.c +++ b/src/PerfectHash/PerfectHashContext.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -854,6 +854,21 @@ Return Value: } } + Result = PerfectHashContextTryRundownCallbackTableValuesFile(Context); + if (FAILED(Result)) { + PH_ERROR(TryRundownCallbackTableValuesFile, Result); + } + + if (Context->State.HasFunctionHooking != FALSE) { + Context->ClearFunctionEntryCallback(NULL, NULL, NULL, NULL, NULL); + if (!FreeLibrary(Context->CallbackModule)) { + SYS_ERROR(FreeLibrary_CallbackModule); + } + if (!FreeLibrary(Context->FunctionHookModule)) { + SYS_ERROR(FreeLibrary_FunctionHookModule); + } + } + // // Free the array of PH_CU_DEVICE structs if applicable. // @@ -1048,6 +1063,9 @@ Return Value: Context->FailedAttempts = 0; Context->FinishedCount = 0; + Context->BaselineAttempts = 0; + Context->BaselineFileTime.AsULongLong = 0; + Context->GraphRegisterSolvedTsxStarted = 0; Context->GraphRegisterSolvedTsxSuccess = 0; Context->GraphRegisterSolvedTsxFailed = 0; @@ -2395,5 +2413,649 @@ Return Value: return Result; } +_Use_decl_annotations_ +HRESULT +PerfectHashContextTryPrepareCallbackTableValuesFile ( + PPERFECT_HASH_CONTEXT Context, + PERFECT_HASH_TABLE_CREATE_FLAGS TableCreateFlags + ) +/*++ + +Routine Description: + + Prepares a file to save a callback DLL's table values. + +Arguments: + + Context - Supplies a pointer to the PERFECT_HASH_CONTEXT instance for which + function hook callback DLL table values are to be persisted. + + TableCreateFlags - Supplies the table create flags. + +Return Value: + + S_FALSE - User requested that table saving be disabled, or the callback + DLL did not export the expected symbols. + + S_OK - File prepared successfully. + + Otherwise, an appropriate error code. + +--*/ +{ + PRTL Rtl; + HRESULT Result; + PCHAR Output; + PWCHAR WideOutput; + PCHAR Buffer = NULL; + ULONG SizeInBytes; + STRING TimestampA = { 0, }; + UNICODE_STRING SuffixW; + UNICODE_STRING TimestampW; + LARGE_INTEGER EndOfFile; + PCUNICODE_STRING BaseName; + ULONGLONG BufferSizeInBytes; + ULONGLONG RemainingBytes; + ULONG NumberOfPagesForBuffer; + PCUNICODE_STRING NewDirectory; + PPERFECT_HASH_PATH Path = NULL; + PPERFECT_HASH_FILE File = NULL; + PPERFECT_HASH_PATH ExistingPath; + PPERFECT_HASH_PATH DllPath = NULL; + PPERFECT_HASH_PATH_PARTS DllParts = NULL; + + // + // Validate arguments. + // + + if (!ARGUMENT_PRESENT(Context)) { + return E_POINTER; + } + + // + // Determine if we should proceed with preparing a table values file. + // + + if (TableCreateFlags.DisableSavingCallbackTableValues != FALSE) { + return S_FALSE; + } + + if ((Context->CallbackModuleNumberOfTableValues == 0) || + (Context->CallbackModuleTableValueSizeInBytes == 0) || + (Context->CallbackDllPath == NULL) || + (!IsValidUnicodeStringWithMinimumLengthInChars(Context->CallbackDllPath, + 4))) { + + return S_FALSE; + } + + ASSERT(Context->CallbackModuleTableValuesFile == NULL); + + ASSERT(Context->BaseOutputDirectory != NULL); + ASSERT(Context->BaseOutputDirectory->Path != NULL); + ASSERT(Context->BaseOutputDirectory->Path->FullPath.Buffer != NULL); + + // + // Initialize aliases. + // + + Rtl = Context->Rtl; + + // + // Create a path instance for the callback DLL path. + // + + Result = Context->Vtbl->CreateInstance(Context, + NULL, + &IID_PERFECT_HASH_PATH, + &DllPath); + + if (FAILED(Result)) { + PH_ERROR(PerfectHashPathCreateInstance_CallbackDllPath, Result); + goto Error; + } + + Result = DllPath->Vtbl->Copy(DllPath, + Context->CallbackDllPath, + &DllParts, + NULL); + if (FAILED(Result)) { + PH_ERROR(PerfectHashPathCopy_CallbackDllPath, Result); + goto Error; + } + + // + // Create a path instance for the table values file. + // + + Result = Context->Vtbl->CreateInstance(Context, + NULL, + &IID_PERFECT_HASH_PATH, + &Path); + + if (FAILED(Result)) { + PH_ERROR(PerfectHashPathCreateInstance_TableValuesFile, Result); + goto Error; + } + + BaseName = &DllParts->BaseName; + ExistingPath = Context->BaseOutputDirectory->Path; + NewDirectory = &Context->BaseOutputDirectory->Path->FullPath; + + // + // Create a temporary buffer to use for path construction. + // + + NumberOfPagesForBuffer = 1; + Result = Rtl->Vtbl->CreateBuffer(Rtl, + &Context->ProcessHandle, + NumberOfPagesForBuffer, + NULL, + &BufferSizeInBytes, + &Buffer); + + if (FAILED(Result)) { + PH_ERROR(TryPrepareCallbackTableValuesFile_CreateBuffer, Result); + goto Error; + } + + SizeInBytes = RTL_TIMESTAMP_FORMAT_FILE_SUFFIX_LENGTH; + Result = InitializeTimestampStringForFileSuffix(Buffer, + SizeInBytes, + &TimestampA, + &Context->SystemTime); + if (FAILED(Result)) { + PH_ERROR(TryPrepareCallbackTableValuesFile_InitTimestampFile, Result); + goto Error; + } + + // + // Convert the char timestamp to a wide timestamp. + // + + Output = Buffer; + RemainingBytes = BufferSizeInBytes - TimestampA.Length; + + Output += TimestampA.Length; + SuffixW.Length = 0; + SuffixW.MaximumLength = 0; + SuffixW.Buffer = WideOutput = (PWSTR)Output; + + WIDE_OUTPUT_UNICODE_STRING(WideOutput, &TableValuesSuffix); + + ASSERT(*WideOutput == L'\0'); + + SuffixW.Length = (USHORT)(RtlPointerToOffset(SuffixW.Buffer, WideOutput)); + RemainingBytes -= SuffixW.Length; + + TimestampW.Buffer = WideOutput; + TimestampW.Length = 0; + TimestampW.MaximumLength = (USHORT)min(0xffff, RemainingBytes); + + Result = AppendStringToUnicodeStringFast(&TimestampA, &TimestampW); + if (FAILED(Result)) { + PH_ERROR( + TryPrepareCallbackTableValuesFile_AppendStringToUnicodeString, + Result + ); + goto Error; + } + + RemainingBytes -= TimestampW.Length; + SuffixW.Length += TimestampW.Length; + SuffixW.MaximumLength -= (USHORT)min(0xffff, RemainingBytes); + + // + // Create the path name for the table values file. + // + + Result = Path->Vtbl->Create(Path, + ExistingPath, + NewDirectory, // NewDirectory + NULL, // DirectorySuffix + BaseName, // NewBaseName + &SuffixW, // BaseNameSuffix + &TableValuesExtension, // NewExtension + NULL, // NewStreamName + NULL, // Parts + NULL); // Reserved + + if (FAILED(Result)) { + PH_ERROR(PerfectHashPathCreate_TableValuesFile, Result); + goto Error; + } + + EndOfFile.QuadPart = ( + (LONGLONG)Context->CallbackModuleNumberOfTableValues * + (LONGLONG)Context->CallbackModuleTableValueSizeInBytes + ); + + // + // Create the file instance. + // + + Result = Context->Vtbl->CreateInstance(Context, + NULL, + &IID_PERFECT_HASH_FILE, + &File); + if (FAILED(Result)) { + PH_ERROR(TryPrepareCallbackTableValuesFile_CreateFileInstance, Result); + goto Error; + } + + Result = File->Vtbl->Create(File, Path, &EndOfFile, NULL, NULL); + if (FAILED(Result)) { + PH_ERROR(TryPrepareCallbackTableValuesFile_CreateFile, Result); + goto Error; + } + + // + // File created successfully. Save to context then finish up. + // + + Context->CallbackModuleTableValuesFile = File; + Context->CallbackModuleTableValuesEndOfFile.QuadPart = EndOfFile.QuadPart; + File->Vtbl->AddRef(File); + + // + // We're done! Indicate success and finish up. + // + + Result = S_OK; + goto End; + +Error: + + if (Result == S_OK) { + Result = E_UNEXPECTED; + } + + // + // Intentional follow-on to End. + // + +End: + + // + // Release the path and file objects, destroy the temporary buffer if + // applicable, then return. + // + + RELEASE(Path); + RELEASE(File); + + if (Buffer != NULL) { + Result = Rtl->Vtbl->DestroyBuffer(Rtl, + Context->ProcessHandle, + &Buffer); + if (FAILED(Result)) { + PH_ERROR(TryPrepareCallbackTableValuesFile_DestroyBuffer, Result); + } + } + + return Result; +} + +_Use_decl_annotations_ +HRESULT +PerfectHashContextTryRundownCallbackTableValuesFile ( + PPERFECT_HASH_CONTEXT Context + ) +/*++ + +Routine Description: + + Runs down a table values file if applicable. + +Arguments: + + Context - Supplies a pointer to the PERFECT_HASH_CONTEXT instance for which + function hook callback DLL table value rundown is to be performed. + +Return Value: + + S_FALSE - No table values file is active. + + S_OK - Rundown successful. + + Otherwise, an appropriate error code. + +--*/ +{ + PRTL Rtl; + HRESULT Result; + ULONGLONG SizeInBytes; + PPERFECT_HASH_FILE File; + + // + // Validate arguments. + // + + if (!ARGUMENT_PRESENT(Context)) { + return E_POINTER; + } + + // + // Determine if we should proceed with rundown. + // + + File = Context->CallbackModuleTableValuesFile; + if (File == NULL) { + return S_FALSE; + } + + ASSERT(Context->CallbackModuleTableValues != NULL); + + // + // Copy the callback module's table values to the memory mapped file. + // + + Rtl = Context->Rtl; + + SizeInBytes = Context->CallbackModuleTableValuesEndOfFile.QuadPart; + CopyMemory(File->BaseAddress, + Context->CallbackModuleTableValues, + Context->CallbackModuleTableValuesEndOfFile.QuadPart); + + File->NumberOfBytesWritten.QuadPart = SizeInBytes; + + // + // Close the file mapping. + // + + Result = File->Vtbl->Close(File, NULL); + if (FAILED(Result)) { + PH_ERROR(TryRundownCallbackTableValuesFile_FileClose, Result); + } + + // + // And finally, release the reference. + // + + RELEASE(Context->CallbackModuleTableValuesFile); + + return Result; +} + +PERFECT_HASH_CONTEXT_INITIALIZE_FUNCTION_HOOK_CALLBACK_DLL + PerfectHashContextInitializeFunctionHookCallbackDll; + +_Use_decl_annotations_ +HRESULT +PerfectHashContextInitializeFunctionHookCallbackDll( + PPERFECT_HASH_CONTEXT Context, + PPERFECT_HASH_TABLE_CREATE_FLAGS TableCreateFlags, + PPERFECT_HASH_TABLE_CREATE_PARAMETERS TableCreateParameters + ) +/*++ + +Routine Description: + + Enumerates the given table create parameters, identifies if a function + hook callback DLL has been requested, and if so, initializes the required + infrastructure. + +Arguments: + + Context - Supplies a pointer to the PERFECT_HASH_CONTEXT instance for which + function hook callback DLL initialization is to be performed. + + TableCreateFlags - Supplies a pointer to the table create flags. + + TableCreateParameters - Supplies a pointer to the table create params. + +Return Value: + + S_OK - No function hook callback DLL requested. + + PH_S_FUNCTION_HOOK_CALLBACK_DLL_INITIALIZED - Successfully initialized the + requested function hook callback DLL. + + Otherwise, an appropriate error code. + +--*/ +{ + PRTL Rtl; + PVOID Proc; + ULONG Index; + ULONG Count; + HRESULT Result; + PCSTRING Name = NULL; + PUNICODE_STRING Path = NULL; + HMODULE CallbackModule = NULL; + HMODULE FunctionHookModule = NULL; + PPERFECT_HASH_PATH CallbackDllPath = NULL; + PPERFECT_HASH_TABLE_CREATE_PARAMETER Param; + PFUNCTION_ENTRY_CALLBACK FunctionEntryCallback; + PSET_FUNCTION_ENTRY_CALLBACK SetFunctionEntryCallback; + PGET_FUNCTION_ENTRY_CALLBACK GetFunctionEntryCallback; + PCLEAR_FUNCTION_ENTRY_CALLBACK ClearFunctionEntryCallback; + PIS_FUNCTION_ENTRY_CALLBACK_ENABLED IsFunctionEntryCallbackEnabled; + + UNREFERENCED_PARAMETER(TableCreateFlags); + + // + // Initialize aliases. + // + + Rtl = Context->Rtl; + Count = TableCreateParameters->NumberOfElements; + Param = TableCreateParameters->Params; + + // + // Disable "enum not handled in switch statement" warning. + // + // warning C4061: enumerator 'TableCreateParameterNullId' in switch + // of enum 'PERFECT_HASH_TABLE_CREATE_PARAMETER_ID' + // is not explicitly handled by a case label + // + +#pragma warning(push) +#pragma warning(disable: 4061) + + for (Index = 0; Index < Count; Index++, Param++) { + + switch (Param->Id) { + + case TableCreateParameterFunctionHookCallbackDllPathId: + Path = &Param->AsUnicodeString; + break; + + case TableCreateParameterFunctionHookCallbackFunctionNameId: + Name = &Param->AsString; + break; + + case TableCreateParameterFunctionHookCallbackIgnoreRipId: + Context->CallbackModuleIgnoreRip = Param->AsULong; + break; + + default: + break; + } + } + +#pragma warning(pop) + + if (Path == NULL) { + + // + // No function hook callback DLL specified, finish up. + // + + Result = S_OK; + goto End; + } + + // + // If no name has been requested, use the default. + // + + if (Name == NULL) { + Name = &FunctionHookCallbackDefaultFunctionNameA; + } + + // + // A DLL has been specified. Attempt to load it. + // + + CallbackModule = LoadLibraryW(Path->Buffer); + if (!CallbackModule) { + SYS_ERROR(LoadLibrary); + Result = PH_E_FAILED_TO_LOAD_FUNCTION_HOOK_CALLBACK_DLL; + goto Error; + } + Context->CallbackDllPath = Path; + + // + // Attempt to resolve the callback function. + // + + Proc = (PVOID)GetProcAddress(CallbackModule, Name->Buffer); + if (!Proc) { + SYS_ERROR(GetProcAddress); + Result = PH_E_FAILED_TO_GET_ADDRESS_OF_FUNCTION_HOOK_CALLBACK; + goto Error; + } + FunctionEntryCallback = (PFUNCTION_ENTRY_CALLBACK)Proc; + + // + // Attempt to get the table values from the callback DLL. If we can't, + // that's fine, it's not considered a fatal error. + // + + Proc = (PVOID)GetProcAddress(CallbackModule, "TableValues"); + if (Proc) { + Context->CallbackModuleTableValues = (PVOID)Proc; + } + + Proc = (PVOID)GetProcAddress(CallbackModule, "NumberOfTableValues"); + if (Proc) { + Context->CallbackModuleNumberOfTableValues = *((SIZE_T *)Proc); + } + + Proc = (PVOID)GetProcAddress(CallbackModule, "TableValueSizeInBytes"); + if (Proc) { + Context->CallbackModuleTableValueSizeInBytes = *((SIZE_T *)Proc); + } + + // + // Successfully resolved the callback function. Obtain a reference to the + // FunctionHook.dll module, then resolve the public methods. + // + + FunctionHookModule = LoadLibraryA("FunctionHook.dll"); + if (!FunctionHookModule) { + SYS_ERROR(LoadLibrary); + Result = PH_E_FAILED_TO_LOAD_FUNCTION_HOOK_DLL; + goto Error; + } + + Proc = (PVOID)GetProcAddress(FunctionHookModule, + "SetFunctionEntryCallback"); + if (!Proc) { + SYS_ERROR(GetProcAddress); + Result = PH_E_FAILED_TO_GET_ADDRESS_OF_SET_FUNCTION_ENTRY_CALLBACK; + goto Error; + } + SetFunctionEntryCallback = (PSET_FUNCTION_ENTRY_CALLBACK)Proc; + + Proc = (PVOID)GetProcAddress(FunctionHookModule, + "GetFunctionEntryCallback"); + if (!Proc) { + SYS_ERROR(GetProcAddress); + Result = PH_E_FAILED_TO_GET_ADDRESS_OF_GET_FUNCTION_ENTRY_CALLBACK; + goto Error; + } + GetFunctionEntryCallback = (PGET_FUNCTION_ENTRY_CALLBACK)Proc; + + Proc = (PVOID)GetProcAddress(FunctionHookModule, + "ClearFunctionEntryCallback"); + if (!Proc) { + SYS_ERROR(GetProcAddress); + Result = PH_E_FAILED_TO_GET_ADDRESS_OF_CLEAR_FUNCTION_ENTRY_CALLBACK; + goto Error; + } + ClearFunctionEntryCallback = (PCLEAR_FUNCTION_ENTRY_CALLBACK)Proc; + + Proc = (PVOID)GetProcAddress(FunctionHookModule, + "IsFunctionEntryCallbackEnabled"); + if (!Proc) { + SYS_ERROR(GetProcAddress); + Result = + PH_E_FAILED_TO_GET_ADDRESS_OF_IS_FUNCTION_ENTRY_CALLBACK_ENABLED; + goto Error; + } + IsFunctionEntryCallbackEnabled = (PIS_FUNCTION_ENTRY_CALLBACK_ENABLED)Proc; + + // + // Everything has been loaded successfully. Save all of the module and + // function pointers to the context, update the state, then as the final + // step, set the function entry callback. + // + + Context->CallbackModule = CallbackModule; + Context->FunctionHookModule = FunctionHookModule; + Context->FunctionEntryCallback = FunctionEntryCallback; + Context->SetFunctionEntryCallback = SetFunctionEntryCallback; + Context->ClearFunctionEntryCallback = ClearFunctionEntryCallback; + Context->IsFunctionEntryCallbackEnabled = IsFunctionEntryCallbackEnabled; + + Context->State.HasFunctionHooking = TRUE; + + SetFunctionEntryCallback(FunctionEntryCallback, + CallbackModule, + PerfectHashModuleInfo.lpBaseOfDll, + PerfectHashModuleInfo.SizeOfImage, + Context->CallbackModuleIgnoreRip); + + // + // Save the runtime values such that the hooking can be toggled on and off + // at runtime via console input. + // + + GetFunctionEntryCallback(&Context->CallbackFunction, + &Context->CallbackContext, + &Context->CallbackModuleBaseAddress, + &Context->CallbackModuleSizeInBytes, + &Context->CallbackModuleIgnoreRip); + + // + // We're done! Indicate success and finish up. + // + + Result = PH_S_FUNCTION_HOOK_CALLBACK_DLL_INITIALIZED; + goto End; + +Error: + + if (Result == S_OK) { + Result = E_UNEXPECTED; + } + + if (CallbackModule) { + if (!FreeLibrary(CallbackModule)) { + SYS_ERROR(FreeLibrary_CallbackModule); + } + CallbackModule = NULL; + } + + if (FunctionHookModule) { + if (!FreeLibrary(FunctionHookModule)) { + SYS_ERROR(FreeLibrary_FunctionHookModule); + } + FunctionHookModule = NULL; + } + + // + // Intentional follow-on to End. + // + +End: + + RELEASE(CallbackDllPath); + + return Result; +} + // vim:set ts=8 sw=4 sts=4 tw=80 expandtab : diff --git a/src/PerfectHash/PerfectHashContext.h b/src/PerfectHash/PerfectHashContext.h index 81ae0d7f..e2830bf5 100644 --- a/src/PerfectHash/PerfectHashContext.h +++ b/src/PerfectHash/PerfectHashContext.h @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -156,11 +156,17 @@ typedef union _PERFECT_HASH_CONTEXT_STATE { ULONG UserRequestedResize:1; + // + // When set, indicates function hook infrastructure is active. + // + + ULONG HasFunctionHooking:1; + // // Unused bits. // - ULONG Unused:20; + ULONG Unused:19; }; LONG AsLong; ULONG AsULong; @@ -769,6 +775,13 @@ typedef struct _Struct_size_bytes_(SizeOfStruct) _PERFECT_HASH_CONTEXT { volatile LONGLONG Attempts; + // + // Used to capture current vs total attempts per second in console output. + // + + LONGLONG BaselineAttempts; + FILETIME64 BaselineFileTime; + // // Counters used for capturing performance information. We capture both a // cycle count, using __rdtsc(), plus a "performance counter" count, via @@ -1045,6 +1058,43 @@ typedef struct _Struct_size_bytes_(SizeOfStruct) _PERFECT_HASH_CONTEXT { volatile LONG GraphRegisterSolvedTsxFailed; ULONG Padding6; + // + // Pointers to function hook related infrastructure. + // + + HMODULE CallbackModule; + HMODULE FunctionHookModule; + PCUNICODE_STRING CallbackDllPath; + PFUNCTION_ENTRY_CALLBACK FunctionEntryCallback; + PSET_FUNCTION_ENTRY_CALLBACK SetFunctionEntryCallback; + PGET_FUNCTION_ENTRY_CALLBACK GetFunctionEntryCallback; + PCLEAR_FUNCTION_ENTRY_CALLBACK ClearFunctionEntryCallback; + PIS_FUNCTION_ENTRY_CALLBACK_ENABLED IsFunctionEntryCallbackEnabled; + PVOID CallbackModuleTableValues; + SIZE_T CallbackModuleTableValueSizeInBytes; + SIZE_T CallbackModuleNumberOfTableValues; + LARGE_INTEGER CallbackModuleTableValuesEndOfFile; + + // + // If function hooking is enabled, and the callback DLL is a compiled + // perfect hash table, this file will save a copy of the table values + // during context rundown. + // + + struct _PERFECT_HASH_FILE *CallbackModuleTableValuesFile; + + // + // We allow toggling of the callback function on and off during runtime + // via the console. The following variables capture the necessary state + // required to effectuate this. + // + + PVOID CallbackContext; + PVOID CallbackModuleBaseAddress; + PFUNCTION_ENTRY_CALLBACK CallbackFunction; + ULONG CallbackModuleSizeInBytes; + ULONG CallbackModuleIgnoreRip; + // // Backing vtbl. // @@ -1058,7 +1108,7 @@ typedef struct _Struct_size_bytes_(SizeOfStruct) _PERFECT_HASH_CONTEXT { // warnings. // - PVOID Padding7; + PVOID Padding8; } PERFECT_HASH_CONTEXT; typedef PERFECT_HASH_CONTEXT *PPERFECT_HASH_CONTEXT; @@ -1229,6 +1279,16 @@ HRESULT typedef PERFECT_HASH_CONTEXT_INITIALIZE_CUDA *PPERFECT_HASH_CONTEXT_INITIALIZE_CUDA; +typedef +HRESULT +(NTAPI PERFECT_HASH_CONTEXT_INITIALIZE_FUNCTION_HOOK_CALLBACK_DLL)( + _In_ PPERFECT_HASH_CONTEXT Context, + _In_ PPERFECT_HASH_TABLE_CREATE_FLAGS TableCreateFlags, + _In_ PPERFECT_HASH_TABLE_CREATE_PARAMETERS TableCreateParameters + ); +typedef PERFECT_HASH_CONTEXT_INITIALIZE_FUNCTION_HOOK_CALLBACK_DLL + *PPERFECT_HASH_CONTEXT_INITIALIZE_FUNCTION_HOOK_CALLBACK_DLL; + typedef _Must_inspect_result_ HRESULT @@ -1240,6 +1300,25 @@ HRESULT typedef PERFECT_HASH_CONTEXT_INITIALIZE_KEY_SIZE *PPERFECT_HASH_CONTEXT_INITIALIZE_KEY_SIZE; +typedef +_Must_inspect_result_ +HRESULT +(NTAPI PERFECT_HASH_CONTEXT_TRY_PREPARE_CALLBACK_TABLE_VALUES_FILE)( + _In_ PPERFECT_HASH_CONTEXT Context, + _In_ PERFECT_HASH_TABLE_CREATE_FLAGS TableCreateFlags + ); +typedef PERFECT_HASH_CONTEXT_TRY_PREPARE_CALLBACK_TABLE_VALUES_FILE + *PPERFECT_HASH_CONTEXT_TRY_PREPARE_CALLBACK_TABLE_VALUES_FILE; + +typedef +_Must_inspect_result_ +HRESULT +(NTAPI PERFECT_HASH_CONTEXT_TRY_RUNDOWN_CALLBACK_TABLE_VALUES_FILE)( + _In_ PPERFECT_HASH_CONTEXT Context + ); +typedef PERFECT_HASH_CONTEXT_TRY_RUNDOWN_CALLBACK_TABLE_VALUES_FILE + *PPERFECT_HASH_CONTEXT_TRY_RUNDOWN_CALLBACK_TABLE_VALUES_FILE; + // // Function decls. // @@ -1279,6 +1358,12 @@ extern PERFECT_HASH_CONTEXT_INITIALIZE_RNG PerfectHashContextInitializeRng; extern PERFECT_HASH_CONTEXT_INITIALIZE_CUDA PerfectHashContextInitializeCuda; +extern PERFECT_HASH_CONTEXT_INITIALIZE_FUNCTION_HOOK_CALLBACK_DLL + PerfectHashContextInitializeFunctionHookCallbackDll; +extern PERFECT_HASH_CONTEXT_TRY_PREPARE_CALLBACK_TABLE_VALUES_FILE + PerfectHashContextTryPrepareCallbackTableValuesFile; +extern PERFECT_HASH_CONTEXT_TRY_RUNDOWN_CALLBACK_TABLE_VALUES_FILE + PerfectHashContextTryRundownCallbackTableValuesFile; #endif // vim:set ts=8 sw=4 sts=4 tw=80 expandtab : diff --git a/src/PerfectHash/PerfectHashContextBulkCreate.c b/src/PerfectHash/PerfectHashContextBulkCreate.c index ae5680a1..c9877625 100644 --- a/src/PerfectHash/PerfectHashContextBulkCreate.c +++ b/src/PerfectHash/PerfectHashContextBulkCreate.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2021 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -214,9 +214,9 @@ Return Value: Allocator = Context->Allocator; } - VALIDATE_FLAGS(ContextBulkCreate, CONTEXT_BULK_CREATE); - VALIDATE_FLAGS(KeysLoad, KEYS_LOAD); - VALIDATE_FLAGS(TableCompile, TABLE_COMPILE); + VALIDATE_FLAGS(ContextBulkCreate, CONTEXT_BULK_CREATE, ULong); + VALIDATE_FLAGS(KeysLoad, KEYS_LOAD, ULong); + VALIDATE_FLAGS(TableCompile, TABLE_COMPILE, ULong); // // IsValidTableCreateFlags() returns a more specific error code than the @@ -229,10 +229,10 @@ Return Value: if (FAILED(Result)) { return Result; } else { - TableCreateFlags.AsULong = TableCreateFlagsPointer->AsULong; + TableCreateFlags.AsULongLong = TableCreateFlagsPointer->AsULongLong; } } else { - TableCreateFlags.AsULong = 0; + TableCreateFlags.AsULongLong = 0; } if (!ARGUMENT_PRESENT(KeysDirectory)) { @@ -282,6 +282,16 @@ Return Value: FindBestGraph = (TableCreateFlags.FindBestGraph != FALSE); ZeroStruct(EmptyCoverage); + Result = PerfectHashContextTryPrepareCallbackTableValuesFile( + Context, + TableCreateFlags + ); + + if (FAILED(Result)) { + PH_ERROR(PerfectHashContextTryPrepareCallbackTableValuesFile, Result); + goto Error; + } + // // Create a buffer we can use for temporary path construction. We want it // to be MAX_USHORT in size, so (1 << 16) >> PAGE_SHIFT converts this into @@ -1502,7 +1512,7 @@ Return Value: ContextBulkCreateFlags->AsULong = 0; KeysLoadFlags->AsULong = 0; - TableCreateFlags->AsULong = 0; + TableCreateFlags->AsULongLong = 0; TableCompileFlags->AsULong = 0; for (; CurrentArg < NumberOfArguments; CurrentArg++, ArgW++) { @@ -1714,6 +1724,21 @@ Return Value: return Result; } + Result = PerfectHashContextInitializeFunctionHookCallbackDll( + Context, + &TableCreateFlags, + &TableCreateParameters + ); + + if (FAILED(Result)) { + PH_ERROR( + PerfectHashContextTableCreateArgvW_InitFunctionHookCallbackDll, + Result + ); + return Result; + } + + if (MaximumConcurrency > 0) { Result = Context->Vtbl->SetMaximumConcurrency(Context, MaximumConcurrency); diff --git a/src/PerfectHash/PerfectHashContextSelfTest.c b/src/PerfectHash/PerfectHashContextSelfTest.c index 3abf859b..ce753f81 100644 --- a/src/PerfectHash/PerfectHashContextSelfTest.c +++ b/src/PerfectHash/PerfectHashContextSelfTest.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023. Trent Nelson Module Name: @@ -175,11 +175,11 @@ Return Value: Allocator = Context->Allocator; } - VALIDATE_FLAGS(ContextSelfTest, CONTEXT_SELF_TEST); - VALIDATE_FLAGS(KeysLoad, KEYS_LOAD); - VALIDATE_FLAGS(TableCreate, TABLE_CREATE); - VALIDATE_FLAGS(TableLoad, TABLE_LOAD); - VALIDATE_FLAGS(TableCompile, TABLE_COMPILE); + VALIDATE_FLAGS(ContextSelfTest, CONTEXT_SELF_TEST, ULong); + VALIDATE_FLAGS(KeysLoad, KEYS_LOAD, ULong); + VALIDATE_FLAGS(TableCreate, TABLE_CREATE, ULongLong); + VALIDATE_FLAGS(TableLoad, TABLE_LOAD, ULong); + VALIDATE_FLAGS(TableCompile, TABLE_COMPILE, ULong); if (!ARGUMENT_PRESENT(TestDataDirectory)) { return E_POINTER; @@ -430,7 +430,7 @@ Return Value: KeysBaseAddress = NULL; NumberOfKeys.QuadPart = 0; KeysFlags.AsULong = 0; - TableCreateFlags.AsULong = 0; + TableCreateFlags.AsULongLong = 0; CpuArchId = PerfectHashGetCurrentCpuArch(); ASSERT(IsValidPerfectHashCpuArchId(CpuArchId)); @@ -1459,7 +1459,7 @@ Return Value: // KeysLoadFlags->AsULong = 0; - TableCreateFlags->AsULong = 0; + TableCreateFlags->AsULongLong = 0; TableLoadFlags->AsULong = 0; TableCompileFlags->AsULong = 0; diff --git a/src/PerfectHash/PerfectHashContextTableCreate.c b/src/PerfectHash/PerfectHashContextTableCreate.c index 6f2d76e9..34d6f054 100644 --- a/src/PerfectHash/PerfectHashContextTableCreate.c +++ b/src/PerfectHash/PerfectHashContextTableCreate.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -190,9 +190,9 @@ Return Value: Allocator = Context->Allocator; } - VALIDATE_FLAGS(ContextTableCreate, CONTEXT_TABLE_CREATE); - VALIDATE_FLAGS(KeysLoad, KEYS_LOAD); - VALIDATE_FLAGS(TableCompile, TABLE_COMPILE); + VALIDATE_FLAGS(ContextTableCreate, CONTEXT_TABLE_CREATE, ULong); + VALIDATE_FLAGS(KeysLoad, KEYS_LOAD, ULong); + VALIDATE_FLAGS(TableCompile, TABLE_COMPILE, ULong); // // IsValidTableCreateFlags() returns a more specific error code than the @@ -205,10 +205,10 @@ Return Value: if (FAILED(Result)) { return Result; } else { - TableCreateFlags.AsULong = TableCreateFlagsPointer->AsULong; + TableCreateFlags.AsULongLong = TableCreateFlagsPointer->AsULongLong; } } else { - TableCreateFlags.AsULong = 0; + TableCreateFlags.AsULongLong = 0; } if (!ARGUMENT_PRESENT(KeysPath)) { @@ -258,6 +258,16 @@ Return Value: FindBestGraph = (TableCreateFlags.FindBestGraph != FALSE); ZeroStruct(EmptyCoverage); + Result = PerfectHashContextTryPrepareCallbackTableValuesFile( + Context, + TableCreateFlags + ); + + if (FAILED(Result)) { + PH_ERROR(PerfectHashContextTryPrepareCallbackTableValuesFile, Result); + goto Error; + } + // // Create a "row buffer" we can use for the CSV file. // @@ -1164,7 +1174,7 @@ Return Value: // KeysLoadFlags->AsULong = 0; - TableCreateFlags->AsULong = 0; + TableCreateFlags->AsULongLong = 0; TableCompileFlags->AsULong = 0; for (; CurrentArg < NumberOfArguments; CurrentArg++, ArgW++) { @@ -1367,6 +1377,20 @@ Return Value: return Result; } + Result = PerfectHashContextInitializeFunctionHookCallbackDll( + Context, + &TableCreateFlags, + &TableCreateParameters + ); + + if (FAILED(Result)) { + PH_ERROR( + PerfectHashContextTableCreateArgvW_InitFunctionHookCallbackDll, + Result + ); + return Result; + } + if (MaximumConcurrency > 0) { Result = Context->Vtbl->SetMaximumConcurrency(Context, MaximumConcurrency); diff --git a/src/PerfectHash/PerfectHashDirectory.c b/src/PerfectHash/PerfectHashDirectory.c index 99879b01..a5373e3d 100644 --- a/src/PerfectHash/PerfectHashDirectory.c +++ b/src/PerfectHash/PerfectHashDirectory.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023. Trent Nelson Module Name: @@ -231,7 +231,7 @@ Return Value: return E_POINTER; } - VALIDATE_FLAGS(DirectoryOpen, DIRECTORY_OPEN); + VALIDATE_FLAGS(DirectoryOpen, DIRECTORY_OPEN, ULong); if (!TryAcquirePerfectHashPathLockShared(SourcePath)) { return PH_E_SOURCE_PATH_LOCKED; @@ -416,7 +416,7 @@ Return Value: return E_POINTER; } - VALIDATE_FLAGS(DirectoryCreate, DIRECTORY_CREATE); + VALIDATE_FLAGS(DirectoryCreate, DIRECTORY_CREATE, ULong); if (!TryAcquirePerfectHashPathLockShared(SourcePath)) { return PH_E_SOURCE_PATH_LOCKED; diff --git a/src/PerfectHash/PerfectHashErrors.dbg b/src/PerfectHash/PerfectHashErrors.dbg index 28eca48e..36c2b5cf 100644 --- a/src/PerfectHash/PerfectHashErrors.dbg +++ b/src/PerfectHash/PerfectHashErrors.dbg @@ -19,6 +19,7 @@ struct { (HRESULT) PH_S_NO_KEY_SIZE_EXTRACTED_FROM_FILENAME, "PH_S_NO_KEY_SIZE_EXTRACTED_FROM_FILENAME", (HRESULT) PH_S_MAX_ATTEMPTS_REACHED, "PH_S_MAX_ATTEMPTS_REACHED", (HRESULT) PH_S_FIXED_ATTEMPTS_REACHED, "PH_S_FIXED_ATTEMPTS_REACHED", + (HRESULT) PH_S_FUNCTION_HOOK_CALLBACK_DLL_INITIALIZED, "PH_S_FUNCTION_HOOK_CALLBACK_DLL_INITIALIZED", (HRESULT) PH_I_CREATE_TABLE_ROUTINE_RECEIVED_SHUTDOWN_EVENT, "PH_I_CREATE_TABLE_ROUTINE_RECEIVED_SHUTDOWN_EVENT", (HRESULT) PH_I_CREATE_TABLE_ROUTINE_FAILED_TO_FIND_SOLUTION, "PH_I_CREATE_TABLE_ROUTINE_FAILED_TO_FIND_SOLUTION", (HRESULT) PH_I_MAXIMUM_NUMBER_OF_TABLE_RESIZE_EVENTS_REACHED, "PH_I_MAXIMUM_NUMBER_OF_TABLE_RESIZE_EVENTS_REACHED", @@ -32,6 +33,7 @@ struct { (HRESULT) PH_MSG_PERFECT_HASH_USAGE, "PH_MSG_PERFECT_HASH_USAGE", (HRESULT) PH_MSG_PERFECT_HASH_SELF_TEST_EXE_USAGE, "PH_MSG_PERFECT_HASH_SELF_TEST_EXE_USAGE", (HRESULT) PH_MSG_PERFECT_HASH_CONSOLE_KEYS_HELP, "PH_MSG_PERFECT_HASH_CONSOLE_KEYS_HELP", + (HRESULT) PH_MSG_PERFECT_HASH_CONSOLE_KEYS_MORE_HELP, "PH_MSG_PERFECT_HASH_CONSOLE_KEYS_MORE_HELP", (HRESULT) PH_E_CREATE_TABLE_ALREADY_IN_PROGRESS, "PH_E_CREATE_TABLE_ALREADY_IN_PROGRESS", (HRESULT) PH_E_TOO_MANY_KEYS, "PH_E_TOO_MANY_KEYS", (HRESULT) PH_E_INFO_FILE_SMALLER_THAN_HEADER, "PH_E_INFO_FILE_SMALLER_THAN_HEADER", @@ -369,5 +371,18 @@ struct { (HRESULT) PH_E_SELF_TEST_OF_HASH_TABLE_FAILED, "PH_E_SELF_TEST_OF_HASH_TABLE_FAILED", (HRESULT) PH_E_INVALID_AUTO_RESIZE_WHEN_KEYS_TO_EDGES_RATIO_EXCEEDS, "PH_E_INVALID_AUTO_RESIZE_WHEN_KEYS_TO_EDGES_RATIO_EXCEEDS", (HRESULT) PH_E_SILENT_INCOMPATIBLE_WITH_QUIET, "PH_E_SILENT_INCOMPATIBLE_WITH_QUIET", + (HRESULT) PH_E_FAILED_TO_LOAD_FUNCTION_HOOK_CALLBACK_DLL, "PH_E_FAILED_TO_LOAD_FUNCTION_HOOK_CALLBACK_DLL", + (HRESULT) PH_E_FAILED_TO_GET_ADDRESS_OF_FUNCTION_HOOK_CALLBACK, "PH_E_FAILED_TO_GET_ADDRESS_OF_FUNCTION_HOOK_CALLBACK", + (HRESULT) PH_E_FAILED_TO_GET_ADDRESS_OF_SET_FUNCTION_ENTRY_CALLBACK, "PH_E_FAILED_TO_GET_ADDRESS_OF_SET_FUNCTION_ENTRY_CALLBACK", + (HRESULT) PH_E_FAILED_TO_GET_ADDRESS_OF_CLEAR_FUNCTION_ENTRY_CALLBACK, "PH_E_FAILED_TO_GET_ADDRESS_OF_CLEAR_FUNCTION_ENTRY_CALLBACK", + (HRESULT) PH_E_FAILED_TO_LOAD_FUNCTION_HOOK_DLL, "PH_E_FAILED_TO_LOAD_FUNCTION_HOOK_DLL", + (HRESULT) PH_E_INVALID_NUMBER_OF_CONDITIONALS, "PH_E_INVALID_NUMBER_OF_CONDITIONALS", + (HRESULT) PH_E_NUMBER_OF_CONDITIONALS_MISMATCHED, "PH_E_NUMBER_OF_CONDITIONALS_MISMATCHED", + (HRESULT) PH_E_ERROR_DURING_CLOSE_MODULE_DEF_FILE, "PH_E_ERROR_DURING_CLOSE_MODULE_DEF_FILE", + (HRESULT) PH_E_ERROR_DURING_PREPARE_MODULE_DEF_FILE, "PH_E_ERROR_DURING_PREPARE_MODULE_DEF_FILE", + (HRESULT) PH_E_ERROR_DURING_SAVE_MODULE_DEF_FILE, "PH_E_ERROR_DURING_SAVE_MODULE_DEF_FILE", + (HRESULT) PH_E_STRING_BUFFER_TOO_SMALL, "PH_E_STRING_BUFFER_TOO_SMALL", + (HRESULT) PH_E_FAILED_TO_GET_ADDRESS_OF_GET_FUNCTION_ENTRY_CALLBACK, "PH_E_FAILED_TO_GET_ADDRESS_OF_GET_FUNCTION_ENTRY_CALLBACK", + (HRESULT) PH_E_FAILED_TO_GET_ADDRESS_OF_IS_FUNCTION_ENTRY_CALLBACK_ENABLED, "PH_E_FAILED_TO_GET_ADDRESS_OF_IS_FUNCTION_ENTRY_CALLBACK_ENABLED", (HRESULT) 0xFFFFFFFF, NULL }; diff --git a/src/PerfectHash/PerfectHashErrors.mc b/src/PerfectHash/PerfectHashErrors.mc index 79d235ce..76ef8e54 100644 --- a/src/PerfectHash/PerfectHashErrors.mc +++ b/src/PerfectHash/PerfectHashErrors.mc @@ -1,6 +1,6 @@ ;/*++ ; -;Copyright (c) 2018-2022 Trent Nelson +;Copyright (c) 2018-2023 Trent Nelson ; ;Module Name: ; @@ -112,6 +112,14 @@ Language=English Fixed attempts at solving reached. . +MessageId=0x00c +Severity=Success +Facility=ITF +SymbolicName=PH_S_FUNCTION_HOOK_CALLBACK_DLL_INITIALIZED +Language=English +Function hook callback DLL initialized. +. + ; ;//////////////////////////////////////////////////////////////////////////////// ;// PH_SEVERITY_INFORMATIONAL @@ -599,6 +607,15 @@ Table Create Flags: When set, disables automatically using the AVX2 memory coverage calculation routine when the CPU supports the AVX2 instruction set. + --IncludeKeysInCompiledDll + + When set, includes the keys in the compiled DLL file. If you want to + benchmark a compiled perfect hash table DLL's index routine against a + normal binary search routine (i.e. IndexBsearch()), you'll need to + supply this flag to ensure the keys get built into the binary. We + don't do this by default as they're not needed for a normal perfect + hash table binary. + Table Compile Flags: N/A @@ -883,6 +900,22 @@ Table Create Parameters: Supplies the maximum number of seconds to try and solve an individual graph. + --FunctionHookCallbackDllPath= + + Supplies a fully-qualified path to a .dll file that will be used as the + callback handler for hooked functions. + + --FunctionHookCallbackFunctionName= + + Supplies the exported function name to resolve from the callback module + (above) and use as the callback for hooked functions. The default is + InterlockedIncrement. + + --FunctionHookCallbackIgnoreRip= + + Supplies a relative RIP to ignore during function callback. That is, + if a caller matches the supplied relative RIP, the function callback + will not be executed. Console Output Character Legend @@ -957,7 +990,51 @@ Severity=Informational Facility=ITF SymbolicName=PH_MSG_PERFECT_HASH_CONSOLE_KEYS_HELP Language=English -[s] Status [f] Quit solving this graph [r] Force table resize [v] Toggle quiet +[r] Refresh [f] Finish [e] Resize [c] Toggle Callback [?] More Help +. + +MessageId=0x105 +Severity=Informational +Facility=ITF +SymbolicName=PH_MSG_PERFECT_HASH_CONSOLE_KEYS_MORE_HELP +Language=English +[r] Refresh + + Press this at any time to refresh the current solving status for a given + graph. + +[f] Finish + + Finish solving the current table. If in bulk create mode, this only applies + to the active table; subsequent tables will still be processed. + + When in find best graph mode, It is safe to "finish" a table prior to it + hitting the target coverage goal; i.e. the best graph solved at that time + will be the winner for which all the usual post-processing (i.e. writing + the output files, testing, etc.) will occur. + + If no solution has been found at all when finish is pressed, this is just + treated as a failure to solve the table. If in bulk create mode, the next + table will be handled normally. + +[e] Resize + + Force a table resize event. This immediately stops graph solving and + requests the next size up table (i.e. the next power of two up for the + number of edges), then resumes solving with a new table size. You can + view the impact of resizes via the "Number of Table Resize Events:" in + the console output. + +[c] Toggle Callback + + If a hooked version of PerfectHash.dll is running, and a function hook + callback DLL has been configured via --FunctionHookCallbackDllPath, this + command allows you to quickly toggle the callback on and off and observe + the immediate performance impact in the console via the "Current Attempts + Per Second" metric. + + If function hooking is active, this doesn't do anything. + . ; @@ -3776,3 +3853,108 @@ Language=English --Silent is incompatible with --Quiet. . +MessageId=0x3d8 +Severity=Fail +Facility=ITF +SymbolicName=PH_E_FAILED_TO_LOAD_FUNCTION_HOOK_CALLBACK_DLL +Language=English +Failed to load the provided function hook callback DLL. +. + +MessageId=0x3d9 +Severity=Fail +Facility=ITF +SymbolicName=PH_E_FAILED_TO_GET_ADDRESS_OF_FUNCTION_HOOK_CALLBACK +Language=English +Failed to obtain the address of the requested function from the function hook callback DLL. +. + +MessageId=0x3da +Severity=Fail +Facility=ITF +SymbolicName=PH_E_FAILED_TO_GET_ADDRESS_OF_SET_FUNCTION_ENTRY_CALLBACK +Language=English +Failed to obtain the address of SetFunctionEntryCallback from FunctionHook.dll. +. + +MessageId=0x3db +Severity=Fail +Facility=ITF +SymbolicName=PH_E_FAILED_TO_GET_ADDRESS_OF_CLEAR_FUNCTION_ENTRY_CALLBACK +Language=English +Failed to obtain the address of ClearFunctionEntryCallback from FunctionHook.dll. +. + +MessageId=0x3dc +Severity=Fail +Facility=ITF +SymbolicName=PH_E_FAILED_TO_LOAD_FUNCTION_HOOK_DLL +Language=English +Failed to load the function hook DLL. +. + +MessageId=0x3dd +Severity=Fail +Facility=ITF +SymbolicName=PH_E_INVALID_NUMBER_OF_CONDITIONALS +Language=English +Encountered incorrect number of conditionals during chunk processing. +. + +MessageId=0x3de +Severity=Fail +Facility=ITF +SymbolicName=PH_E_NUMBER_OF_CONDITIONALS_MISMATCHED +Language=English +Encountered mismatched number of conditionals during chunk processing. +. + + +MessageId=0x3df +Severity=Fail +Facility=ITF +SymbolicName=PH_E_ERROR_DURING_CLOSE_MODULE_DEF_FILE +Language=English +Error closing module def file. +. + +MessageId=0x3e0 +Severity=Fail +Facility=ITF +SymbolicName=PH_E_ERROR_DURING_PREPARE_MODULE_DEF_FILE +Language=English +Error preparing module def file. +. + +MessageId=0x3e1 +Severity=Fail +Facility=ITF +SymbolicName=PH_E_ERROR_DURING_SAVE_MODULE_DEF_FILE +Language=English +Error saving module def file. +. + +MessageId=0x3e2 +Severity=Fail +Facility=ITF +SymbolicName=PH_E_STRING_BUFFER_TOO_SMALL +Language=English +A provided string buffer was too small to carry out the requested operation. +. + +MessageId=0x3e3 +Severity=Fail +Facility=ITF +SymbolicName=PH_E_FAILED_TO_GET_ADDRESS_OF_GET_FUNCTION_ENTRY_CALLBACK +Language=English +Failed to obtain the address of GetFunctionEntryCallback from FunctionHook.dll. +. + +MessageId=0x3e4 +Severity=Fail +Facility=ITF +SymbolicName=PH_E_FAILED_TO_GET_ADDRESS_OF_IS_FUNCTION_ENTRY_CALLBACK_ENABLED +Language=English +Failed to obtain the address of IsFunctionEntryCallbackEnabled from FunctionHook.dll. +. + diff --git a/src/PerfectHash/PerfectHashErrorsTEMP.BIN b/src/PerfectHash/PerfectHashErrorsTEMP.BIN index fd716650d84d1dd987f3b9baadbae58e2327c5ae..f841a878b3ca55b18be8daf95481bb5a1628ac00 100644 GIT binary patch delta 1330 zcmZ8hT}YEr7=FLG+0>cSnW@E+h%gbG24$kq(utO?F{f#SKe_3g@)fp=8ImIm%zkhk z2?>b=sR$xNFN&ZmZ;EaTiHM?`(M5RCRZvLldB2@cS7*HEeV*rgzH{F5^X=OQ<-r#v zz24j0x~Rs|_Zp&Vtju2486Ywc(Ihgr4P*tmc7P}ixq67mh&(kM8}*vz{6v;#ub-Z) z^toC+AEC3CqYzIx+Y-_0S63F=0*z^R+Rr>$&ruXo5{VFrcXf7=d-#C(Sz|48k{`cO z3eZLT4B13hBhSLkYD;GB%l+a{ZGrP&I}uNA#%x?TN4_ljW$&+>$FJ^be;B`>+fuM& zznuJ*Ho2`5>_;YM?#}BZ!J_8}hT#=p^bG<0`~g)4_~pQc+ugBI4T7U5&MJU(9Cs zWov?F0ZzV$ih(!Z0`_=2oaM;89|-G6Kuq^n6t8GQVLON%Z#8V->K*Zbc;DY`$R7|@ z{)&tZNIbYA7W_L+bAwnRP@ys??)i&NtKdfMNgT+eAxn@bT|?DC+ce|@GHWOZl%b(n zpiH1bBS4lWtpiyY87xD~mjfM&QX-T(Teb^(5XSk8p^)-etY~6ud55S9A{OuJI^BX= zGl6-b+!APUTo^tRZp#6d(f@)?StG%?c}C<^5MKr0rijzy^p-qrH$x7?}6rp)1~=(sEM4^ zhQ0^yxN&)0)Jgg#@Jp@^w{JH-l^pRRxF>H02W!T+(}dt{3990I(7q}Ar97gF;-M`$ zD&-5RIFTaHOF68HODXbmDMwWCI7MEQ^0+EKL7tbD{E%`~6`3J>-lPBINmbN9*4-9d zE3SJ=6`>TjF5ghaUC8<>^|5DE@g_wsm4nQx;&;eCp}QNTJf|j4Q2XpDOFz9R_)?^w zNcZ9@ze*>-};s|%-L--M4#H5T_WDe$wh!w;t IVhus`7YGw-}k+I-@dnRChxyHKfgJ_{p!Of zQL7W&aLcXE#I=d`I+066JBYllMnn$c3gQsrVNB#9>SpIf)!$zuDt`R>VQsWht<6m$ z&syx#+x7JP#+CGS{nFT>d6mQI@Wf!3ON3sOeyTrrr=xTfdYfZ(N)f%S)jZ@6`+Dan?OZI?pD0_u2FN6NAu5~_RvpW zI!2&BSgVbHUvK#)IDf6>JK*fGya!$y;C=A%0e-PbR36|VxFQ-hDK5C&+&kV_bVfKa zUt3)Ij46zyo!0e4V^TLU^d>esrLbkJz)s1-{5XfD!1Zsqtp1JYr$rAX^h5vk;Q9bY ztKSy=jOcO5(0hrw1zZ3L-7aK?t{7&;;2DEYh`bcyDSc1$WzqXaAJ{o6@KgG}(I-R? z#1OE-$=N|GaJ_qrs4EjH;TrHX7DS(8y_@O5^~r6bFK6_RMUMl;`}6kT6uAD8+{p?| zN`ZOrvJ`2MC diff --git a/src/PerfectHash/PerfectHashErrors_English.bin b/src/PerfectHash/PerfectHashErrors_English.bin index 6be0b742f2835380f39878e61179a097ea6038a7..c5e4bdee104879ea60047ad00ac8929e03a82b62 100644 GIT binary patch delta 4853 zcmcgwT})iZ6`l*bfVX)0S(Y`~Zn#i8#&W=Ru~R3G>)-@1ZhnYOB}bMe@a}>MKa1JL z2GHjcYNgh^^`S{?DT?|M1tK?9)K;=3D{|E;?f1>> zz-1RCJF3dn?A$x|%=!J!Idi$b%(=|#dNBP`xjJvqU-_9b8e-|s|Q>vR|FMcj4 zU+<4Gj^SQ8zTP5?*)q^z_VfM=rLbi-h_ScWA<=9RMt6y$oFd<16~jJw}<)I*q~7{?Ad>S4@{7^=%r zn=#K|ynEPDbC@~4KIy3UFms&8{SC~wFe;}&50g99xaw2#g%g}FcditD>loHL)Px#U zag|YnDup{&ox+?_L%55BdKlM9%yGT)jCxi*D}7%^N)xI@ZB&8znc@S1;%lEdvQ!bB z|8isRhf?|1GPzLeYs=?+D988dJg(1Z8QoaTK=yw1q{`jP<)vonz3gl6hmO=^N;Rv~ zxTjWwsvje%63}xjx7P)S6rTGqTJVN@TunEulJd!qV)FOzmHGKTEidkj$=DA{c6hBj zg*T&mUlN|;&Xk_5M<7jR7D95aY17dCT9;4vJYA9~Wa1BMvDBNGJQ6rwX+r+vN6s(gg8nKQN_@?HWJ%f1fwT)rP;L_=#cb}5q zJ?58}CxWuOwK_C_-K@_0@NN(_IEhlQ|BZT?{gqF;UnyxY;5R{~hwPmRD?|04P@P=; zplq6+7{#@^5}(z;h-<8x-b4#bfnwP*y@8gb@RUKnpiMa+V#Vs+WVM5yXSLPVrku6% z$B+E7d$Lx7i-CF*ah6w0#g!zU;;?a>eE(uZ4u+~X7{DMkrSA_RxwOZfw(vDErS?fr z{A|`4BvDh!VDU}lq0CUctV{Fih-CCG%CWvRX+wbeux4Nu5@G3SsNCmmlI#|y8jt9(vqkG;fd)2h>>Iu~-eZLP$W@~NS)p}&WpZ&lakNYX(On_VxKwlS~)OI!RF=+1jI{EN+ z?e>*pmWkjvWHN0{qA^X(kqR?ATJy^B(ksLE%lw1p<=f$rIIJ1frE37Z83&@y`l=Zz ze!pjj)dx*)8DG{#mi`t((lT9Bh!=4+m5Zt=h{W6=qBaBy(~-O_(dJE|J?3a_eUXAa z#;fMUl|qD>_T!A`=2h;uEb~%BK7KDIH)18lfRNm5jmU+q0UTAVTkNhzbZI(`ZX-}! z)h2J=ihiS83v<;`%*Me9#g4$_V?}|9(iRF8Blry4iIuR~3Tndb?QUL&@)(r@uQa4? z)pGf48#%Qze9Sk*{GFc`Wfun>Uo=0Itg_U5TusRfzHl4ASd8xMZ0xK&V&FWlJ3<#| z6Ih?bYLjYJ-vl^LLK3Nlus(%a4KDK?cl*z0?>**h%D&a-1mx|-hveEqr3ae@=PoeN zpFX*4QtC$bX8|X&gMniQr8O(b-3Kia+UKn1Oh*PAR?FP$brdB(KVS2}BHz^_ryoQl z!<#9{k9Ow_sQ9L|zRSm@g|+Sd_Z{;Celc>`B6H;{{T_vTn6dd#FIS zp6ovoPJ>^k1A$z>0_6ON{sho!m($lHRSYJR!lVr=^pZdFU7IF^llfF;onUzRMV0$v zz!MKAtbzQ1UhD8--FC&ws~7>lGCfIQcS6~7Jx+CYXSq|eKFMcKfaIx-PH072F<^1^3(IbB@pj# delta 412 zcmW-cze_?<6vw~kzS~2r9)$+|A}s=ggqk9vrKK8;8UiT{Dh&eBUQL3I4MET|ik1`_ z8VZaD+0f7*5wt$18d`#i5Qo0cclq%7o^#JV@Gc$muF+8p0}$wFC;4XPH?7Ex0carM z<1v?@Q^&+_LUc-z^#EsRpYRvJv04{-X68^$k!3CwrM+HK{sBUCgk)O4sM1Amf<@dA zOwy-h*c6=6*W@E6+^W5P@);Kl(nBOMBN(B_$RG1KT_u%S!2|6R-W5F2J~s0U{YC<5 z&hwX-kjECBU$LB0Q@cGl-*pr zyTW?USvVPRHbbU!bsy~Z@AB=tMPx6k6Vm5YGFBvsqjqx*S)_UPvBNbv|2dOgpEBfJ K)f1*{7ybddG-G1` diff --git a/src/PerfectHash/PerfectHashEvents.man b/src/PerfectHash/PerfectHashEvents.man index 6178cbce..28cdca76 100644 --- a/src/PerfectHash/PerfectHashEvents.man +++ b/src/PerfectHash/PerfectHashEvents.man @@ -214,6 +214,20 @@ + + @@ -228,6 +242,8 @@ + + @@ -240,6 +256,8 @@ + + + + + + @@ -425,6 +467,8 @@ + + diff --git a/src/PerfectHash/PerfectHashFile.c b/src/PerfectHash/PerfectHashFile.c index 9ebdd221..3cb4d2a8 100644 --- a/src/PerfectHash/PerfectHashFile.c +++ b/src/PerfectHash/PerfectHashFile.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -297,7 +297,7 @@ Return Value: return E_POINTER; } - VALIDATE_FLAGS(FileLoad, FILE_LOAD); + VALIDATE_FLAGS(FileLoad, FILE_LOAD, ULong); if (!TryAcquirePerfectHashPathLockShared(SourcePath)) { return PH_E_SOURCE_PATH_LOCKED; @@ -554,7 +554,7 @@ Return Value: return E_POINTER; } - VALIDATE_FLAGS(FileCreate, FILE_CREATE); + VALIDATE_FLAGS(FileCreate, FILE_CREATE, ULong); if (!ARGUMENT_PRESENT(EndOfFilePointer)) { return E_POINTER; diff --git a/src/PerfectHash/PerfectHashFileWork.h b/src/PerfectHash/PerfectHashFileWork.h index fa3a2a0a..65846d63 100644 --- a/src/PerfectHash/PerfectHashFileWork.h +++ b/src/PerfectHash/PerfectHashFileWork.h @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023. Trent Nelson Module Name: @@ -430,6 +430,19 @@ Module Name: BASE_NAME(main) \ ) \ \ + ENTRY( \ + Verb, \ + VUpper, \ + ModuleDefFile, \ + MODULE_DEF_FILE, \ + EofInitTypeDefault, \ + NO_EOF_VALUE, \ + NO_SUFFIX, \ + &ModuleDefFileExtension, \ + NO_STREAM_NAME, \ + NO_BASE_NAME \ + ) \ + \ ENTRY( \ Verb, \ VUpper, \ @@ -1541,6 +1554,13 @@ typedef FILE_WORK_CALLBACK_IMPL *PFILE_WORK_CALLBACK_IMPL; OUTPUT_SPACE_SLASH_NEWLINE_TAB(); \ } +#define MAYBE_OUTPUT_INCLUDE_KEYS_DOT_C() \ + if (Context->Table->TableCreateFlags.IncludeKeysInCompiledDll != FALSE) { \ + OUTPUT_STRING(Name); \ + OUTPUT_RAW("_Keys.c"); \ + OUTPUT_SPACE_SLASH_NEWLINE_TAB(); \ + } + #define OUTPUT_MAKEFILE_SPLASH_COMMENT(N) \ OUTPUT_RAW("# Compiled Perfect Hash Table " # N); \ OUTPUT_RAW(" Makefile.\n" \ diff --git a/src/PerfectHash/PerfectHashPath.c b/src/PerfectHash/PerfectHashPath.c index 6ff80bc5..67408fe1 100644 --- a/src/PerfectHash/PerfectHashPath.c +++ b/src/PerfectHash/PerfectHashPath.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023. Trent Nelson Module Name: @@ -1009,7 +1009,7 @@ Return Value: return E_POINTER; } - VALIDATE_FLAGS(PathCreate, PATH_CREATE); + VALIDATE_FLAGS(PathCreate, PATH_CREATE, ULong); if (!TryAcquirePerfectHashPathLockShared(ExistingPath)) { return PH_E_EXISTING_PATH_LOCKED; diff --git a/src/PerfectHash/PerfectHashPrivate.h b/src/PerfectHash/PerfectHashPrivate.h index f9ff43e7..fbe77114 100644 --- a/src/PerfectHash/PerfectHashPrivate.h +++ b/src/PerfectHash/PerfectHashPrivate.h @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2020 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -29,10 +29,12 @@ Module Name: // A handle to the PerfectHash.dll module will be captured in this variable // via the DLL_PROCESS_ATTACH message. This is required in order for proper // operation of FormatMessage() when specifying FORMAT_MESSAGE_FROM_HMODULE and -// using our own internal error codes. +// using our own internal error codes. Additionally, MODULEINFO will be saved +// to PerfectHashModuleInfo. // extern HMODULE PerfectHashModule; +extern MODULEINFO PerfectHashModuleInfo; // // Components can check this variable to determine if Ctrl-C has been pressed. @@ -44,15 +46,15 @@ extern volatile ULONG CtrlCPressed; // Define a helper macro for validating flags passed as parameters to routines. // -#define VALIDATE_FLAGS(Name, Upper) \ - if (ARGUMENT_PRESENT(##Name##FlagsPointer)) { \ - if (FAILED(IsValid##Name##Flags(##Name##FlagsPointer))) { \ - return PH_E_INVALID_##Upper##_FLAGS; \ - } else { \ - ##Name##Flags.AsULong = ##Name##FlagsPointer->AsULong; \ - } \ - } else { \ - ##Name##Flags.AsULong = 0; \ +#define VALIDATE_FLAGS(Name, Upper, Type) \ + if (ARGUMENT_PRESENT(##Name##FlagsPointer)) { \ + if (FAILED(IsValid##Name##Flags(##Name##FlagsPointer))) { \ + return PH_E_INVALID_##Upper##_FLAGS; \ + } else { \ + ##Name##Flags.As##Type = ##Name##FlagsPointer->As##Type; \ + } \ + } else { \ + ##Name##Flags.As##Type = 0; \ } // diff --git a/src/PerfectHash/PerfectHashTable.h b/src/PerfectHash/PerfectHashTable.h index bf24e6dd..5e2b1aee 100644 --- a/src/PerfectHash/PerfectHashTable.h +++ b/src/PerfectHash/PerfectHashTable.h @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -154,7 +154,7 @@ typedef struct _Struct_size_bytes_(SizeOfStruct) _PERFECT_HASH_TABLE { // either 4 or 8 bytes.) // - ULONG ValueSizeInBytes; + SIZE_T ValueSizeInBytes; // // Optional table creation parameters specified to Create(). @@ -495,8 +495,6 @@ typedef struct _Struct_size_bytes_(SizeOfStruct) _PERFECT_HASH_TABLE { PERFECT_HASH_TABLE_VTBL Interface; - //PVOID Padding4; - } PERFECT_HASH_TABLE; typedef PERFECT_HASH_TABLE *PPERFECT_HASH_TABLE; diff --git a/src/PerfectHash/PerfectHashTableCompile.c b/src/PerfectHash/PerfectHashTableCompile.c index 1143ee9f..52ddd658 100644 --- a/src/PerfectHash/PerfectHashTableCompile.c +++ b/src/PerfectHash/PerfectHashTableCompile.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023. Trent Nelson Module Name: @@ -110,7 +110,7 @@ Return Value: return PH_E_INVALID_CPU_ARCH_ID; } - VALIDATE_FLAGS(TableCompile, TABLE_COMPILE); + VALIDATE_FLAGS(TableCompile, TABLE_COMPILE, ULong); if (!TryAcquirePerfectHashTableLockExclusive(Table)) { return PH_E_TABLE_LOCKED; diff --git a/src/PerfectHash/PerfectHashTableCreate.c b/src/PerfectHash/PerfectHashTableCreate.c index c1fa0f51..3318950a 100644 --- a/src/PerfectHash/PerfectHashTableCreate.c +++ b/src/PerfectHash/PerfectHashTableCreate.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -201,7 +201,7 @@ Return Value: return PH_E_TOO_MANY_KEYS; } - VALIDATE_FLAGS(TableCreate, TABLE_CREATE); + VALIDATE_FLAGS(TableCreate, TABLE_CREATE, ULongLong); if (!IsValidPerfectHashAlgorithmId(AlgorithmId)) { return PH_E_INVALID_ALGORITHM_ID; @@ -284,7 +284,7 @@ Return Value: // Copy create flags. // - Table->TableCreateFlags.AsULong = TableCreateFlags.AsULong; + Table->TableCreateFlags.AsULongLong = TableCreateFlags.AsULongLong; // // Enable non-temporal AVX2 routines here if requested. This is a bit @@ -729,6 +729,16 @@ Return Value: break; + case TableCreateParameterFunctionHookCallbackDllPathId: + case TableCreateParameterFunctionHookCallbackFunctionNameId: + case TableCreateParameterFunctionHookCallbackIgnoreRipId: + + // + // Handled by the context. + // + + break; + case TableCreateParameterSolutionsFoundRatioId: Table->PriorSolutionsFoundRatio = Param->AsDouble; Result = CalculatePredictedAttempts( diff --git a/src/PerfectHash/PerfectHashTableLoad.c b/src/PerfectHash/PerfectHashTableLoad.c index aac5ba9a..6dd76ec4 100644 --- a/src/PerfectHash/PerfectHashTableLoad.c +++ b/src/PerfectHash/PerfectHashTableLoad.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022. Trent Nelson +Copyright (c) 2018-2023. Trent Nelson Module Name: @@ -139,7 +139,7 @@ Return Value: return E_INVALIDARG; } - VALIDATE_FLAGS(TableLoad, TABLE_LOAD); + VALIDATE_FLAGS(TableLoad, TABLE_LOAD, ULong); if (!TryAcquirePerfectHashTableLockExclusive(Table)) { return PH_E_TABLE_LOCKED; diff --git a/src/PerfectHash/PerfectHashTls.h b/src/PerfectHash/PerfectHashTls.h index 50dbe09f..99ceeed7 100644 --- a/src/PerfectHash/PerfectHashTls.h +++ b/src/PerfectHash/PerfectHashTls.h @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023. Trent Nelson Module Name: @@ -111,10 +111,11 @@ typedef PERFECT_HASH_TLS_CONTEXT_FLAGS *PPERFECT_HASH_TLS_CONTEXT_FLAGS; (TlsContext && TlsContext->Flags.CustomAllocatorDetailsPresent) typedef struct _PERFECT_HASH_TLS_CONTEXT { - PERFECT_HASH_TLS_CONTEXT_FLAGS Flags; PERFECT_HASH_TABLE_CREATE_FLAGS TableCreateFlags; + PERFECT_HASH_TLS_CONTEXT_FLAGS Flags; ULONG LastError; HRESULT LastResult; + ULONG Padding1; PPERFECT_HASH_KEYS Keys; PPERFECT_HASH_CONTEXT Context; PPERFECT_HASH_TABLE Table; diff --git a/src/PerfectHash/Rtl.h b/src/PerfectHash/Rtl.h index a57986c6..de1b87ad 100644 --- a/src/PerfectHash/Rtl.h +++ b/src/PerfectHash/Rtl.h @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -2106,14 +2106,14 @@ IsAligned( // a given power-of-2 value. // -typedef enum _TYPE { - ByteType = 0, - ShortType = 1, - LongType = 2, - LongLongType = 3, - XmmType = 4, - YmmType = 5, - ZmmType = 6, +typedef enum _TYPE { // Number of bytes + ByteType = 0, // 1 + ShortType = 1, // 2 + LongType = 2, // 4 + LongLongType = 3, // 8 + XmmType = 4, // 16 + YmmType = 5, // 32 + ZmmType = 6, // 64 } TYPE; typedef TYPE *PTYPE; diff --git a/src/PerfectHash/RtlOutput.c b/src/PerfectHash/RtlOutput.c index c8155b69..bb30cae2 100644 --- a/src/PerfectHash/RtlOutput.c +++ b/src/PerfectHash/RtlOutput.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -1298,6 +1298,39 @@ AppendStringToWideCharBufferFast( return; } +_Use_decl_annotations_ +HRESULT +AppendStringToUnicodeStringFast ( + PCSTRING String, + PUNICODE_STRING UnicodeString + ) +{ + CHAR Char; + WCHAR Wide; + PSTR Source; + PWSTR Dest; + USHORT Count; + USHORT Index; + + if (String->Length > (UnicodeString->MaximumLength >> 1)) { + return PH_E_STRING_BUFFER_TOO_SMALL; + } + + Count = String->Length; + Source = String->Buffer; + Dest = UnicodeString->Buffer; + + for (Index = 0; Index < Count; Index++) { + Char = Source[Index]; + Wide = (WCHAR)Char; + Dest[Index] = Wide; + } + + UnicodeString->Length = (USHORT)(Count << 1); + + return S_OK; +} + _Use_decl_annotations_ VOID AppendCharBufferToCharBuffer( diff --git a/src/PerfectHash/RtlOutput.h b/src/PerfectHash/RtlOutput.h index 964b1c6e..ccd630dc 100644 --- a/src/PerfectHash/RtlOutput.h +++ b/src/PerfectHash/RtlOutput.h @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -251,6 +251,15 @@ VOID typedef APPEND_STRING_TO_WIDE_CHAR_BUFFER_FAST *PAPPEND_STRING_TO_WIDE_CHAR_BUFFER_FAST; +typedef +HRESULT +(NTAPI APPEND_STRING_TO_UNICODE_STRING_FAST)( + _In_ PCSTRING String, + _Inout_ PUNICODE_STRING UnicodeString + ); +typedef APPEND_STRING_TO_UNICODE_STRING_FAST + *PAPPEND_STRING_TO_UNICODE_STRING_FAST; + typedef VOID (NTAPI APPEND_CHAR_BUFFER_TO_CHAR_BUFFER)( @@ -403,6 +412,7 @@ extern APPEND_STRING_TO_CHAR_BUFFER AppendStringToCharBuffer; extern APPEND_UNICODE_STRING_TO_CHAR_BUFFER_FAST AppendUnicodeStringToCharBufferFast; extern APPEND_STRING_TO_WIDE_CHAR_BUFFER_FAST AppendStringToWideCharBufferFast; +extern APPEND_STRING_TO_UNICODE_STRING_FAST AppendStringToUnicodeStringFast; extern APPEND_CHAR_BUFFER_TO_CHAR_BUFFER AppendCharBufferToCharBuffer; extern APPEND_CSTR_TO_CHAR_BUFFER AppendCStrToCharBuffer; extern APPEND_WSTR_TO_CHAR_BUFFER_FAST AppendWStrToCharBufferFast; @@ -478,6 +488,57 @@ InitializeTimestampString( return Result; } +#define RTL_TIMESTAMP_FORMAT_FILE_SUFFIX "yyyy-MM-dd_HH-mm-ss.000" +#define RTL_TIMESTAMP_FORMAT_FILE_SUFFIX_LENGTH 24 +C_ASSERT(sizeof(RTL_TIMESTAMP_FORMAT_FILE_SUFFIX) == + RTL_TIMESTAMP_FORMAT_FILE_SUFFIX_LENGTH); + +FORCEINLINE +_Must_inspect_result_ +_Success_(return >= 0) +HRESULT +InitializeTimestampStringForFileSuffix ( + _Inout_ PCHAR Buffer, + _In_ ULONG SizeOfBufferInBytes, + _Inout_ PSTRING String, + _In_ PSYSTEMTIME SystemTime + ) +{ + ULONG Value; + HRESULT Result = S_OK; + + // + // Validate arguments. + // + + if (SizeOfBufferInBytes != RTL_TIMESTAMP_FORMAT_FILE_SUFFIX_LENGTH) { + return E_INVALIDARG; + } + + String->Buffer = Buffer; + String->Length = 0; + String->MaximumLength = (USHORT)RTL_TIMESTAMP_FORMAT_FILE_SUFFIX_LENGTH; + +#define RTL_APPEND_TIME_FIELD(Field, Digits, Trailer) \ + Value = SystemTime->Field; \ + if (!AppendIntegerToString(String, Value, Digits, Trailer)) { \ + Result = PH_E_STRING_BUFFER_OVERFLOW; \ + goto End; \ + } + + RTL_APPEND_TIME_FIELD(wYear, 4, '-'); + RTL_APPEND_TIME_FIELD(wMonth, 2, '-'); + RTL_APPEND_TIME_FIELD(wDay, 2, '_'); + RTL_APPEND_TIME_FIELD(wHour, 2, '-'); + RTL_APPEND_TIME_FIELD(wMinute, 2, '-'); + RTL_APPEND_TIME_FIELD(wSecond, 2, '.'); + RTL_APPEND_TIME_FIELD(wMilliseconds, 3, 0); + +End: + + return Result; +} + // // Output helpers. // diff --git a/src/PerfectHash/dllmain.c b/src/PerfectHash/dllmain.c index 75e57d6d..71a1e71b 100644 --- a/src/PerfectHash/dllmain.c +++ b/src/PerfectHash/dllmain.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -19,6 +19,7 @@ Module Name: #include "PerfectHashEventsPrivate.h" HMODULE PerfectHashModule; +MODULEINFO PerfectHashModuleInfo; // // We need to define a _fltused ULONG symbol as we're working with floats and @@ -76,6 +77,13 @@ _DllMainCRTStartup( __security_init_cookie(); PerfectHashModule = Module; + if (!GetModuleInformation(GetCurrentProcess(), + PerfectHashModule, + &PerfectHashModuleInfo, + sizeof(PerfectHashModuleInfo))) { + return FALSE; + } + if (!PerfectHashTlsProcessAttach(Module, Reason, Reserved)) { return FALSE; } diff --git a/src/PerfectHash/stdafx.h b/src/PerfectHash/stdafx.h index b2e11780..110be8fb 100644 --- a/src/PerfectHash/stdafx.h +++ b/src/PerfectHash/stdafx.h @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -79,6 +79,8 @@ Module Name: #include #include +#include + // // Include intrinsic headers if we're x64. // diff --git a/src/PerfectHashBulkCreateExe/PerfectHashBulkCreateExe.c b/src/PerfectHashBulkCreateExe/PerfectHashBulkCreateExe.c index e1360ab3..8c9d1083 100644 --- a/src/PerfectHashBulkCreateExe/PerfectHashBulkCreateExe.c +++ b/src/PerfectHashBulkCreateExe/PerfectHashBulkCreateExe.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2021 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -34,7 +34,7 @@ mainCRTStartup( LPWSTR CommandLineW; PICLASSFACTORY ClassFactory; PPERFECT_HASH_CONTEXT Context; - PPERFECT_HASH_PRINT_ERROR PerfectHashPrintError; + PPERFECT_HASH_PRINT_ERROR PerfectHashPrintError = NULL; PPERFECT_HASH_PRINT_MESSAGE PerfectHashPrintMessage; PICLASSFACTORY_CREATE_INSTANCE CreateInstance; INT NumberOfArguments = 0; @@ -48,7 +48,15 @@ mainCRTStartup( &Module); if (FAILED(Result)) { - PH_ERROR(PerfectHashBootstrap, Result); + + // + // We can only use PH_ERROR() if PerfectHashPrintError is available. + // + + if (PerfectHashPrintError != NULL) { + PH_ERROR(PerfectHashBootstrap, Result); + } + goto Error; } diff --git a/src/PerfectHashCreateExe/PerfectHashCreateExe.c b/src/PerfectHashCreateExe/PerfectHashCreateExe.c index 44af58b0..de1e9a21 100644 --- a/src/PerfectHashCreateExe/PerfectHashCreateExe.c +++ b/src/PerfectHashCreateExe/PerfectHashCreateExe.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2021 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -34,7 +34,7 @@ mainCRTStartup( LPWSTR CommandLineW; PICLASSFACTORY ClassFactory; PPERFECT_HASH_CONTEXT Context; - PPERFECT_HASH_PRINT_ERROR PerfectHashPrintError; + PPERFECT_HASH_PRINT_ERROR PerfectHashPrintError = NULL; PPERFECT_HASH_PRINT_MESSAGE PerfectHashPrintMessage; PICLASSFACTORY_CREATE_INSTANCE CreateInstance; INT NumberOfArguments = 0; @@ -48,7 +48,15 @@ mainCRTStartup( &Module); if (FAILED(Result)) { - PH_ERROR(PerfectHashBootstrap, Result); + + // + // We can only use PH_ERROR() if PerfectHashPrintError is available. + // + + if (PerfectHashPrintError != NULL) { + PH_ERROR(PerfectHashBootstrap, Result); + } + goto Error; } diff --git a/src/build-pgo-avx.bat b/src/build-pgo-avx.bat new file mode 100644 index 00000000..72a101eb --- /dev/null +++ b/src/build-pgo-avx.bat @@ -0,0 +1,28 @@ +@echo off +setlocal enableextensions + +echo on +del /q .\x64\*.pgd +del /q .\x64\PGInstrument\*.pgc + +msbuild /nologo /m /t:Build /p:Configuration=PGInstrument;Platform=x64;InstructionSet=AdvancedVectorExtensions PerfectHash.sln +..\bin\timemem.exe .\x64\PGInstrument\PerfectHashCreate.exe c:\src\perfecthash-keys\sys32\HologramWorld-31016.keys c:\temp\ph.pgi Chm01 MultiplyShiftR And 12 --FindBestGraph --BestCoverageType=HighestScore --BestCoverageAttempts=4 --HashAllKeysFirst --TryUseAvx2HashFunction --InitialNumberOfTableResizes=1 +..\bin\timemem.exe .\x64\PGInstrument\PerfectHashBulkCreate.exe c:\src\perfecthash-keys\hard c:\temp\ph.pgi Chm01 RotateMultiplyXorRotate2 And 3 --AttemptsBeforeTableResize=10000 --MaxNumberOfTableResizes=0 --FindBestGraph --BestCoverageAttempts=2 --BestCoverageType=HighestNumberOfEmptyCacheLines --MainWorkThreadpoolPriority=Low + +@echo off + +rem If input is PerfectHash!1.pgc, then: +rem i = PerfectHash +rem j = 1 +rem k = pgc +rem +rem We want to generate: +rem pgomgr /merge x64\PGInstrument\PerfectHash!1.pgc x64\PerfectHash.pgd + +echo on + +@for /f "usebackq tokens=1,2,3 delims=!." %%i in (`dir /b x64\PGInstrument\*.pgc`) do ( + pgomgr /merge "x64\PGInstrument\%%i!%%j.%%k" x64\%%i.pgd +) + +msbuild /nologo /m /t:Build /p:Configuration=PGOptimize;Platform=x64;InstructionSet=AdvancedVectorExtensions PerfectHash.sln diff --git a/src/build-pgo-avx2.bat b/src/build-pgo-avx2.bat new file mode 100644 index 00000000..282aa7da --- /dev/null +++ b/src/build-pgo-avx2.bat @@ -0,0 +1,28 @@ +@echo off +setlocal enableextensions + +echo on +del /q .\x64\*.pgd +del /q .\x64\PGInstrument\*.pgc + +msbuild /nologo /m /t:Build /p:Configuration=PGInstrument;Platform=x64;InstructionSet=AdvancedVectorExtensions2 PerfectHash.sln +..\bin\timemem.exe .\x64\PGInstrument\PerfectHashCreate.exe c:\src\perfecthash-keys\sys32\HologramWorld-31016.keys c:\temp\ph.pgi Chm01 MultiplyShiftR And 12 --FindBestGraph --BestCoverageType=HighestScore --BestCoverageAttempts=4 --HashAllKeysFirst --TryUseAvx2HashFunction --InitialNumberOfTableResizes=1 +..\bin\timemem.exe .\x64\PGInstrument\PerfectHashBulkCreate.exe c:\src\perfecthash-keys\hard c:\temp\ph.pgi Chm01 RotateMultiplyXorRotate2 And 3 --AttemptsBeforeTableResize=10000 --MaxNumberOfTableResizes=0 --FindBestGraph --BestCoverageAttempts=2 --BestCoverageType=HighestNumberOfEmptyCacheLines --MainWorkThreadpoolPriority=Low + +@echo off + +rem If input is PerfectHash!1.pgc, then: +rem i = PerfectHash +rem j = 1 +rem k = pgc +rem +rem We want to generate: +rem pgomgr /merge x64\PGInstrument\PerfectHash!1.pgc x64\PerfectHash.pgd + +echo on + +@for /f "usebackq tokens=1,2,3 delims=!." %%i in (`dir /b x64\PGInstrument\*.pgc`) do ( + pgomgr /merge "x64\PGInstrument\%%i!%%j.%%k" x64\%%i.pgd +) + +msbuild /nologo /m /t:Build /p:Configuration=PGOptimize;Platform=x64;InstructionSet=AdvancedVectorExtensions2 PerfectHash.sln diff --git a/src/build-pgo-avx512.bat b/src/build-pgo-avx512.bat new file mode 100644 index 00000000..3ec5deb5 --- /dev/null +++ b/src/build-pgo-avx512.bat @@ -0,0 +1,28 @@ +@echo off +setlocal enableextensions + +echo on +del /q .\x64\*.pgd +del /q .\x64\PGInstrument\*.pgc + +msbuild /nologo /m /t:Build /p:Configuration=PGInstrument;Platform=x64;InstructionSet=AdvancedVectorExtensions512 PerfectHash.sln +..\bin\timemem.exe .\x64\PGInstrument\PerfectHashCreate.exe c:\src\perfecthash-keys\sys32\HologramWorld-31016.keys c:\temp\ph.pgi Chm01 MultiplyShiftR And 12 --FindBestGraph --BestCoverageType=HighestScore --BestCoverageAttempts=4 --HashAllKeysFirst --TryUseAvx2HashFunction --InitialNumberOfTableResizes=1 +..\bin\timemem.exe .\x64\PGInstrument\PerfectHashBulkCreate.exe c:\src\perfecthash-keys\hard c:\temp\ph.pgi Chm01 RotateMultiplyXorRotate2 And 3 --AttemptsBeforeTableResize=10000 --MaxNumberOfTableResizes=0 --FindBestGraph --BestCoverageAttempts=2 --BestCoverageType=HighestNumberOfEmptyCacheLines --MainWorkThreadpoolPriority=Low + +@echo off + +rem If input is PerfectHash!1.pgc, then: +rem i = PerfectHash +rem j = 1 +rem k = pgc +rem +rem We want to generate: +rem pgomgr /merge x64\PGInstrument\PerfectHash!1.pgc x64\PerfectHash.pgd + +echo on + +@for /f "usebackq tokens=1,2,3 delims=!." %%i in (`dir /b x64\PGInstrument\*.pgc`) do ( + pgomgr /merge "x64\PGInstrument\%%i!%%j.%%k" x64\%%i.pgd +) + +msbuild /nologo /m /t:Build /p:Configuration=PGOptimize;Platform=x64;InstructionSet=AdvancedVectorExtensions512 PerfectHash.sln diff --git a/src/build-pgo-hooked.bat b/src/build-pgo-hooked.bat new file mode 100644 index 00000000..a860ee00 --- /dev/null +++ b/src/build-pgo-hooked.bat @@ -0,0 +1,28 @@ +@echo off +setlocal enableextensions + +echo on +del /q .\x64\*.pgd +del /q .\x64\PGInstrument\*.pgc + +msbuild /nologo /m /t:Build /p:Configuration=PGInstrument;Platform=x64;HookPenter=1 PerfectHash.sln +..\bin\timemem.exe .\x64\PGInstrument\PerfectHashCreate.exe c:\src\perfecthash-keys\sys32\HologramWorld-31016.keys c:\temp\ph.pgi Chm01 MultiplyShiftR And 12 --FindBestGraph --BestCoverageType=HighestScore --BestCoverageAttempts=4 --HashAllKeysFirst --TryUseAvx2HashFunction --InitialNumberOfTableResizes=1 +..\bin\timemem.exe .\x64\PGInstrument\PerfectHashBulkCreate.exe c:\src\perfecthash-keys\hard c:\temp\ph.pgi Chm01 RotateMultiplyXorRotate2 And 3 --AttemptsBeforeTableResize=10000 --MaxNumberOfTableResizes=0 --FindBestGraph --BestCoverageAttempts=2 --BestCoverageType=HighestNumberOfEmptyCacheLines --MainWorkThreadpoolPriority=Low + +@echo off + +rem If input is PerfectHash!1.pgc, then: +rem i = PerfectHash +rem j = 1 +rem k = pgc +rem +rem We want to generate: +rem pgomgr /merge x64\PGInstrument\PerfectHash!1.pgc x64\PerfectHash.pgd + +echo on + +@for /f "usebackq tokens=1,2,3 delims=!." %%i in (`dir /b x64\PGInstrument\*.pgc`) do ( + pgomgr /merge "x64\PGInstrument\%%i!%%j.%%k" x64\%%i.pgd +) + +msbuild /nologo /m /t:Build /p:Configuration=PGOptimize;Platform=x64;HookPenter=1 PerfectHash.sln diff --git a/src/build-release-hooked.bat b/src/build-release-hooked.bat new file mode 100644 index 00000000..82cd2464 --- /dev/null +++ b/src/build-release-hooked.bat @@ -0,0 +1 @@ +msbuild /nologo /m /t:Rebuild /p:Configuration=Release;Platform=x64;HookPenter=1 PerfectHash.sln diff --git a/src/build-release.bat b/src/build-release.bat new file mode 100644 index 00000000..7c0eb412 --- /dev/null +++ b/src/build-release.bat @@ -0,0 +1 @@ +msbuild /nologo /m /t:Rebuild /p:Configuration=Release;Platform=x64 From dedf8fdcd6988c2463ec204d3ac8330a17b48fbe Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Tue, 3 Jan 2023 15:30:44 -0800 Subject: [PATCH 5/5] Optimize hot loop in KeysLoad(). --- src/PerfectHash/PerfectHashKeysLoad.c | 67 ++++++++++++++++++++------- 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/src/PerfectHash/PerfectHashKeysLoad.c b/src/PerfectHash/PerfectHashKeysLoad.c index 90fdea23..1700134f 100644 --- a/src/PerfectHash/PerfectHashKeysLoad.c +++ b/src/PerfectHash/PerfectHashKeysLoad.c @@ -1,6 +1,6 @@ /*++ -Copyright (c) 2018-2022 Trent Nelson +Copyright (c) 2018-2023 Trent Nelson Module Name: @@ -103,7 +103,7 @@ Return Value: return E_INVALIDARG; } - VALIDATE_FLAGS(KeysLoad, KEYS_LOAD); + VALIDATE_FLAGS(KeysLoad, KEYS_LOAD, ULong); if (!TryAcquirePerfectHashKeysLockExclusive(Keys)) { return PH_E_KEYS_LOCKED; @@ -423,27 +423,60 @@ Return Value: KeysBitmap->Flags.HasZero = TRUE; } - for (Index = 0; Index < NumberOfKeys; Index++) { - Key = *Values++; + if (Rtl->CpuFeatures.BMI1 && Rtl->CpuFeatures.POPCNT) { - if (Index > 0) { - if (Prev > Key) { - return PH_E_KEYS_NOT_SORTED; - } else if (Prev == Key) { - return PH_E_DUPLICATE_KEYS_DETECTED; + // + // Fast version that will inline popcnt and tzcnt. + // + + for (Index = 0; Index < NumberOfKeys; Index++) { + Key = *Values++; + + if (Index > 0) { + if (Prev > Key) { + return PH_E_KEYS_NOT_SORTED; + } else if (Prev == Key) { + return PH_E_DUPLICATE_KEYS_DETECTED; + } + } + + Prev = Key; + + PopCount = (BYTE)PopulationCount32_POPCNT(Key); + Stats.PopCount[PopCount] += 1; + + while (Key) { + Bit = TrailingZeros32_BMI1(Key); + Key &= Key - 1; + Bitmap |= (1 << Bit); + Stats.BitCount[Bit] += 1; } } - Prev = Key; + } else { - PopCount = (BYTE)Rtl->PopulationCount32(Key); - Stats.PopCount[PopCount] += 1; + for (Index = 0; Index < NumberOfKeys; Index++) { + Key = *Values++; - while (Key) { - Bit = Rtl->TrailingZeros32(Key); - Key &= Key - 1; - Bitmap |= (1 << Bit); - Stats.BitCount[Bit] += 1; + if (Index > 0) { + if (Prev > Key) { + return PH_E_KEYS_NOT_SORTED; + } else if (Prev == Key) { + return PH_E_DUPLICATE_KEYS_DETECTED; + } + } + + Prev = Key; + + PopCount = (BYTE)Rtl->PopulationCount32(Key); + Stats.PopCount[PopCount] += 1; + + while (Key) { + Bit = Rtl->TrailingZeros32(Key); + Key &= Key - 1; + Bitmap |= (1 << Bit); + Stats.BitCount[Bit] += 1; + } } }