You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
616 lines
17 KiB
616 lines
17 KiB
#include "stdinc.h"
|
|
#include "debmacro.h"
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include "fusionbuffer.h"
|
|
#include "setupapi.h"
|
|
#include "shlwapi.h"
|
|
#if !defined(NT_INCLUDED)
|
|
#define DPFLTR_FUSION_ID 54
|
|
#endif
|
|
|
|
extern "C" { const extern char FusionpIsPrint_Data[256]; }
|
|
#define PRINTABLE(_ch) (FusionpIsPrint_Data[(_ch) & 0xff] ? (_ch) : '.')
|
|
|
|
#if !defined(FUSION_DEFAULT_DBG_LEVEL_MASK)
|
|
#define FUSION_DEFAULT_DBG_LEVEL_MASK (0x00000000)
|
|
#endif
|
|
|
|
extern "C" DWORD kd_fusion_mask = (FUSION_DEFAULT_DBG_LEVEL_MASK & ~DPFLTR_MASK);
|
|
extern "C" DWORD kd_kernel_fusion_mask = 0;
|
|
extern "C" bool g_FusionEnterExitTracingEnabled = false;
|
|
|
|
typedef ULONG (NTAPI* RTL_V_DBG_PRINT_EX_FUNCTION)(
|
|
IN ULONG ComponentId,
|
|
IN ULONG Level,
|
|
IN PCH Format,
|
|
va_list arglist
|
|
);
|
|
|
|
typedef ULONG (*RTL_V_DBG_PRINT_EX_WITH_PREFIX_FUNCTION)(
|
|
IN PCH Prefix,
|
|
IN ULONG ComponentId,
|
|
IN ULONG Level,
|
|
IN PCH Format,
|
|
va_list arglist
|
|
);
|
|
|
|
RTL_V_DBG_PRINT_EX_FUNCTION g_pfnvDbgPrintEx;
|
|
RTL_V_DBG_PRINT_EX_WITH_PREFIX_FUNCTION g_pfnvDbgPrintExWithPrefix;
|
|
|
|
VOID
|
|
FusionpvDbgPrintToSetupLog(
|
|
IN LogSeverity Severity,
|
|
IN PCSTR Format,
|
|
IN va_list ap
|
|
);
|
|
|
|
void
|
|
FusionpReportCondition(
|
|
bool fBreak,
|
|
PCSTR pszFormat,
|
|
...
|
|
)
|
|
{
|
|
char rgach[128];
|
|
char rgach2[128];
|
|
FRAME_INFO FrameInfo;
|
|
|
|
va_list ap;
|
|
va_start(ap, pszFormat);
|
|
|
|
::_vsnprintf(rgach, NUMBER_OF(rgach), pszFormat, ap);
|
|
rgach[NUMBER_OF(rgach) - 1] = '\0';
|
|
|
|
::FusionpGetActiveFrameInfo(FrameInfo);
|
|
|
|
::_snprintf(
|
|
rgach2,
|
|
NUMBER_OF(rgach2),
|
|
"%s(%d): Break-in requested:\n"
|
|
" %s\n",
|
|
FrameInfo.pszFile,
|
|
FrameInfo.nLine,
|
|
rgach);
|
|
|
|
rgach2[NUMBER_OF(rgach2) - 1] = '\0';
|
|
|
|
if (::IsDebuggerPresent())
|
|
{
|
|
::OutputDebugStringA(rgach2);
|
|
}
|
|
if (fBreak)
|
|
{
|
|
#if DBG
|
|
::FusionpRtlAssert(
|
|
const_cast<PVOID>(reinterpret_cast<const void*>("Break-in requested")),
|
|
const_cast<PVOID>(reinterpret_cast<const void*>(FrameInfo.pszFile)),
|
|
FrameInfo.nLine,
|
|
const_cast<PSTR>(rgach));
|
|
#endif
|
|
}
|
|
|
|
va_end(ap);
|
|
return;
|
|
}
|
|
|
|
#if DBG
|
|
|
|
bool
|
|
FusionpAssertionFailed(
|
|
const FRAME_INFO &rFrameInfo,
|
|
PCSTR pszExpression,
|
|
PCSTR pszText
|
|
)
|
|
{
|
|
CSxsPreserveLastError ple;
|
|
|
|
if (::IsDebuggerPresent())
|
|
{
|
|
char rgach[512];
|
|
// c:\foo.cpp(35): Assertion Failure. Expression: "m_cch != 0". Text: "Must have nonzero length"
|
|
static const char szFormatWithText[] = "%s(%d): Assertion failure in %s. Expression: \"%s\". Text: \"%s\"\n";
|
|
static const char szFormatNoText[] = "%s(%d): Assertion failure in %s. Expression: \"%s\".\n";
|
|
PCSTR pszFormat = ((pszText == NULL) || (pszText == pszExpression)) ? szFormatNoText : szFormatWithText;
|
|
|
|
::_snprintf(
|
|
rgach,
|
|
NUMBER_OF(rgach),
|
|
pszFormat,
|
|
rFrameInfo.pszFile,
|
|
rFrameInfo.nLine,
|
|
rFrameInfo.pszFunction,
|
|
pszExpression,
|
|
pszText);
|
|
rgach[NUMBER_OF(rgach) - 1] = '\0';
|
|
::OutputDebugStringA(rgach);
|
|
|
|
ple.Restore();
|
|
return true;
|
|
}
|
|
|
|
::FusionpRtlAssert(
|
|
const_cast<PVOID>(reinterpret_cast<const void*>(pszExpression)),
|
|
const_cast<PVOID>(reinterpret_cast<const void*>(rFrameInfo.pszFile)),
|
|
rFrameInfo.nLine,
|
|
const_cast<PSTR>(pszText));
|
|
|
|
ple.Restore();
|
|
return false;
|
|
}
|
|
|
|
|
|
bool
|
|
FusionpAssertionFailed(
|
|
PCSTR pszFile,
|
|
PCSTR pszFunctionName,
|
|
INT nLine,
|
|
PCSTR pszExpression,
|
|
PCSTR pszText
|
|
)
|
|
{
|
|
FRAME_INFO FrameInfo;
|
|
::FusionpPopulateFrameInfo(FrameInfo, pszFile, pszFunctionName, nLine);
|
|
return ::FusionpAssertionFailed(FrameInfo, pszExpression, pszText);
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
VOID
|
|
FusionpSoftAssertFailed(
|
|
const FRAME_INFO &rFrameInfo,
|
|
PCSTR pszExpression,
|
|
PCSTR pszMessage
|
|
)
|
|
{
|
|
CSxsPreserveLastError ple;
|
|
char rgach[256];
|
|
// c:\foo.cpp(35): [Fusion] Soft Assertion Failure. Expression: "m_cch != 0". Text: "Must have nonzero length"
|
|
static const char szFormatWithText[] = "%s(%d): Soft Assertion Failure in %s! Log a bug!\n Expression: %s\n Message: %s\n";
|
|
static const char szFormatNoText[] = "%s(%d): Soft Assertion Failure in %s! Log a bug!\n Expression: %s\n";
|
|
PCSTR pszFormat = ((pszMessage == NULL) || (pszMessage == pszExpression)) ? szFormatNoText : szFormatWithText;
|
|
|
|
::_snprintf(rgach, NUMBER_OF(rgach), pszFormat, rFrameInfo.pszFile, rFrameInfo.nLine, rFrameInfo.pszFunction, pszExpression, pszMessage);
|
|
rgach[NUMBER_OF(rgach) - 1] = '\0';
|
|
|
|
::OutputDebugStringA(rgach);
|
|
|
|
ple.Restore();
|
|
}
|
|
|
|
VOID
|
|
FusionpSoftAssertFailed(
|
|
PCSTR pszFile,
|
|
PCSTR pszFunction,
|
|
INT nLine,
|
|
PCSTR pszExpression,
|
|
PCSTR pszMessage
|
|
)
|
|
{
|
|
FRAME_INFO FrameInfo;
|
|
|
|
::FusionpPopulateFrameInfo(FrameInfo, pszFile, pszFunction, nLine);
|
|
::FusionpSoftAssertFailed(FrameInfo, pszExpression, pszMessage);
|
|
}
|
|
|
|
VOID
|
|
FusionpSoftAssertFailed(
|
|
PCSTR pszExpression,
|
|
PCSTR pszMessage
|
|
)
|
|
{
|
|
FRAME_INFO FrameInfo;
|
|
::FusionpGetActiveFrameInfo(FrameInfo);
|
|
::FusionpSoftAssertFailed(FrameInfo, pszExpression, pszMessage);
|
|
}
|
|
|
|
ULONG
|
|
FusionpvDbgPrintExNoNTDLL(
|
|
IN ULONG ComponentId,
|
|
IN ULONG Level,
|
|
IN PCH Format,
|
|
va_list arglist
|
|
)
|
|
{
|
|
if ((ComponentId == DPFLTR_FUSION_ID) &&
|
|
(((Level < 32) &&
|
|
(((1 << Level) & kd_fusion_mask) != 0)) ||
|
|
((Level >= 32) &&
|
|
((Level & kd_fusion_mask) != 0))))
|
|
{
|
|
CSxsPreserveLastError ple;
|
|
CHAR rgchBuffer[512];
|
|
ULONG n = ::_vsnprintf(rgchBuffer, NUMBER_OF(rgchBuffer), Format, arglist);
|
|
rgchBuffer[NUMBER_OF(rgchBuffer) - 1] = '\0';
|
|
::OutputDebugStringA(rgchBuffer);
|
|
ple.Restore();
|
|
return n;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
HMODULE g_setupapiDll = NULL;
|
|
|
|
typedef BOOL (WINAPI * PSETUPCLOSELOG_ROUTINE)(
|
|
);
|
|
|
|
typedef BOOL (WINAPI * PSETUPLOGERRORA_ROUTINE)(
|
|
IN LPCSTR MessageString,
|
|
IN LogSeverity Severity
|
|
);
|
|
|
|
typedef BOOL (WINAPI * PSETUPLOGERRORW_ROUTINE)(
|
|
IN LPCWSTR MessageString,
|
|
IN LogSeverity Severity
|
|
);
|
|
|
|
typedef BOOL (WINAPI * PSETUPOPENLOG_ROUTINE)(
|
|
BOOL Erase
|
|
);
|
|
|
|
PSETUPLOGERRORA_ROUTINE g_pfnSetupLogError;
|
|
PSETUPCLOSELOG_ROUTINE g_pfnSetupCloseLog;
|
|
|
|
BOOL
|
|
WINAPI
|
|
DllStartup_SetupLog(
|
|
HINSTANCE Module,
|
|
DWORD Reason,
|
|
PVOID Reserved
|
|
)
|
|
{
|
|
BOOL fSuccess = FALSE;
|
|
|
|
if ((Reason == DLL_PROCESS_DETACH) &&
|
|
(g_setupapiDll != NULL))
|
|
{
|
|
if (Reserved != NULL)
|
|
{
|
|
if (g_pfnSetupCloseLog != NULL)
|
|
{
|
|
(*g_pfnSetupCloseLog)();
|
|
}
|
|
if (!FreeLibrary(g_setupapiDll))
|
|
{
|
|
::FusionpDbgPrint("SXS.DLL : FreeLibrary failed to free setupapi.dll with LastError = %d\n", ::FusionpGetLastWin32Error());
|
|
}
|
|
}
|
|
g_pfnSetupCloseLog = NULL;
|
|
g_pfnSetupLogError = NULL;
|
|
g_setupapiDll = NULL;
|
|
}
|
|
|
|
fSuccess = TRUE;
|
|
return fSuccess;
|
|
}
|
|
|
|
VOID
|
|
FusionpvDbgPrintToSetupLog(
|
|
IN LogSeverity Severity,
|
|
IN PCSTR Format,
|
|
IN va_list ap
|
|
)
|
|
{
|
|
//
|
|
// first, let us check whether this is ActCtxGen (by csrss.exe) or Setup Installation(by setup.exe)
|
|
// during GUI mode setup; do not log for ActCtxGen, only setup
|
|
//
|
|
if (::GetModuleHandleW(L"csrss.exe") != NULL)
|
|
return;
|
|
|
|
static BOOL s_fEverBeenCalled = FALSE;
|
|
PCSTR ProcName = "";
|
|
|
|
if ((g_pfnSetupLogError == NULL) && !s_fEverBeenCalled)
|
|
{
|
|
g_setupapiDll = ::LoadLibraryW(L"SETUPAPI.DLL");
|
|
if (g_setupapiDll == NULL)
|
|
{
|
|
::FusionpDbgPrint("SXS.DLL : Loadlibrary Failed to load setupapi.dll with LastError = %d\n", ::FusionpGetLastWin32Error());
|
|
goto Exit;
|
|
}
|
|
|
|
ProcName = "SetupOpenLog";
|
|
PSETUPOPENLOG_ROUTINE pfnOpenSetupLog = (PSETUPOPENLOG_ROUTINE) ::GetProcAddress(g_setupapiDll, ProcName);
|
|
if (pfnOpenSetupLog == NULL)
|
|
{
|
|
goto GetProcAddressFailed;
|
|
}
|
|
ProcName = "SetupLogErrorA";
|
|
PSETUPLOGERRORA_ROUTINE pfnSetupLogError = (PSETUPLOGERRORA_ROUTINE) ::GetProcAddress(g_setupapiDll, ProcName);
|
|
if (pfnSetupLogError == NULL)
|
|
{
|
|
goto GetProcAddressFailed;
|
|
}
|
|
ProcName = "SetupCloseLog";
|
|
PSETUPCLOSELOG_ROUTINE pfnSetupCloseLog = (PSETUPCLOSELOG_ROUTINE) ::GetProcAddress(g_setupapiDll, "SetupCloseLog");
|
|
if (pfnSetupLogError == NULL)
|
|
{
|
|
goto GetProcAddressFailed;
|
|
}
|
|
|
|
if (!(*pfnOpenSetupLog)(FALSE))
|
|
{
|
|
::FusionpDbgPrint("SXS.DLL : SetupOpenLog failed with LastError = %d\n", ::FusionpGetLastWin32Error());
|
|
goto Exit;
|
|
}
|
|
|
|
g_pfnSetupCloseLog = pfnSetupCloseLog;
|
|
g_pfnSetupLogError = pfnSetupLogError;
|
|
}
|
|
|
|
if (g_pfnSetupLogError != NULL)
|
|
{
|
|
CHAR rgchBuffer[512];
|
|
|
|
::_vsnprintf(rgchBuffer, NUMBER_OF(rgchBuffer), Format, ap);
|
|
|
|
rgchBuffer[NUMBER_OF(rgchBuffer) - 1] = '\0';
|
|
|
|
if (!(*g_pfnSetupLogError)(rgchBuffer, Severity))
|
|
{
|
|
::FusionpDbgPrint("SXS.DLL : SetupLogErrorA failed with LastError = %d\n", ::FusionpGetLastWin32Error());
|
|
}
|
|
}
|
|
return;
|
|
Exit:
|
|
s_fEverBeenCalled = TRUE;
|
|
return;
|
|
|
|
GetProcAddressFailed:
|
|
::FusionpDbgPrint("SXS.DLL : %s failed to be located in setupapi.dll with LastError = %d\n", ProcName, ::FusionpGetLastWin32Error());
|
|
goto Exit;
|
|
}
|
|
|
|
ULONG
|
|
FusionpvDbgPrintEx(
|
|
ULONG Level,
|
|
PCSTR Format,
|
|
va_list ap
|
|
)
|
|
{
|
|
CSxsPreserveLastError ple;
|
|
ULONG ulResult = 0;
|
|
|
|
if (g_pfnvDbgPrintEx == NULL)
|
|
{
|
|
HINSTANCE hInstNTDLL = ::GetModuleHandleW(L"NTDLL.DLL");
|
|
if (hInstNTDLL != NULL)
|
|
g_pfnvDbgPrintEx = (RTL_V_DBG_PRINT_EX_FUNCTION)(::GetProcAddress(hInstNTDLL, "vDbgPrintEx"));
|
|
|
|
if (g_pfnvDbgPrintEx == NULL)
|
|
g_pfnvDbgPrintEx = &::FusionpvDbgPrintExNoNTDLL;
|
|
}
|
|
|
|
if (g_pfnvDbgPrintEx)
|
|
{
|
|
ulResult = (*g_pfnvDbgPrintEx)(
|
|
DPFLTR_FUSION_ID,
|
|
Level,
|
|
const_cast<PSTR>(Format),
|
|
ap);
|
|
}
|
|
|
|
if (::IsDebuggerPresent())
|
|
{
|
|
ulResult = ::FusionpvDbgPrintExNoNTDLL(DPFLTR_FUSION_ID, Level, const_cast<PSTR>(Format), ap);
|
|
// Gross, but msdev chokes under too much debug output
|
|
if (ulResult != 0)
|
|
::Sleep(10);
|
|
}
|
|
|
|
// Special handling of reflection out to the setup log...
|
|
if (Level & FUSION_DBG_LEVEL_SETUPLOG & ~DPFLTR_MASK)
|
|
::FusionpvDbgPrintToSetupLog(
|
|
(Level== FUSION_DBG_LEVEL_ERROR) || (Level & FUSION_DBG_LEVEL_ERROR & ~DPFLTR_MASK) ? LogSevError : LogSevInformation,
|
|
Format, ap);
|
|
|
|
ple.Restore();
|
|
|
|
return ulResult;
|
|
}
|
|
|
|
ULONG
|
|
FusionpDbgPrintEx(
|
|
ULONG Level,
|
|
PCSTR Format,
|
|
...
|
|
)
|
|
{
|
|
ULONG rv = 0;
|
|
va_list ap;
|
|
va_start(ap, Format);
|
|
if ((Level & FUSION_DBG_LEVEL_SETUPLOG) || (::FusionpDbgWouldPrintAtFilterLevel(Level)))
|
|
{
|
|
rv = ::FusionpvDbgPrintEx(Level, Format, ap);
|
|
}
|
|
va_end(ap);
|
|
return rv;
|
|
}
|
|
|
|
// Finds out whether or not the given level would print, given our
|
|
// current mask. Knows to look at kd_fusion_mask both in nt as well
|
|
// as in the current process.
|
|
|
|
#define RECHECK_INTERVAL (1000)
|
|
|
|
#if DBG
|
|
EXTERN_C BOOL g_fIgnoreSystemLevelFilterMask = FALSE;
|
|
#endif
|
|
|
|
bool
|
|
FusionpDbgWouldPrintAtFilterLevel(
|
|
ULONG Level
|
|
)
|
|
{
|
|
// The time quanta here is milliseconds (1000 per second)
|
|
|
|
#if DBG
|
|
if ( !g_fIgnoreSystemLevelFilterMask )
|
|
{
|
|
#endif
|
|
static ULONG s_ulNextTimeToCheck;
|
|
ULONG ulCapturedNextTimeToCheck = s_ulNextTimeToCheck;
|
|
|
|
ULONG ulCurrentTime = NtGetTickCount();
|
|
|
|
// Look for either the tick count to have wrapped or to be over the next time to check.
|
|
// There's some opportunity for loss here if we only run this function every 49 days but
|
|
// that's pretty darned unlikely.
|
|
if ((ulCurrentTime >= ulCapturedNextTimeToCheck) ||
|
|
((ulCapturedNextTimeToCheck > RECHECK_INTERVAL) &&
|
|
(ulCurrentTime < (ulCapturedNextTimeToCheck - RECHECK_INTERVAL))))
|
|
{
|
|
DWORD dwNewFusionMask = 0;
|
|
ULONG i;
|
|
|
|
// Time to check again...
|
|
for (i=0; i<31; i++)
|
|
{
|
|
if (::FusionpNtQueryDebugFilterState(DPFLTR_FUSION_ID, (DPFLTR_MASK | (1 << i))) == TRUE)
|
|
dwNewFusionMask |= (1 << i);
|
|
}
|
|
|
|
if (s_ulNextTimeToCheck == ulCapturedNextTimeToCheck)
|
|
{
|
|
// Check again in 1000ms (1 second)
|
|
s_ulNextTimeToCheck = (ulCurrentTime + RECHECK_INTERVAL);
|
|
|
|
// Nobody got in before us. Let's write it...
|
|
kd_kernel_fusion_mask = dwNewFusionMask;
|
|
|
|
g_FusionEnterExitTracingEnabled = (((kd_fusion_mask | kd_kernel_fusion_mask) & FUSION_DBG_LEVEL_ENTEREXIT) != 0);
|
|
}
|
|
}
|
|
#if DBG
|
|
}
|
|
#endif
|
|
|
|
ULONG mask = Level;
|
|
// If level < 32, it's actually a single-bit test
|
|
if (Level < 32)
|
|
mask = (1 << Level);
|
|
|
|
// Are these bits set in our mask?
|
|
return (((kd_fusion_mask | kd_kernel_fusion_mask) & mask) != 0);
|
|
}
|
|
|
|
|
|
VOID
|
|
FusionppDbgPrintBlob(
|
|
ULONG Level,
|
|
PVOID Data,
|
|
SIZE_T Length,
|
|
PCWSTR PerLinePrefix
|
|
)
|
|
{
|
|
ULONG Offset = 0;
|
|
|
|
if (PerLinePrefix == NULL)
|
|
PerLinePrefix = L"";
|
|
|
|
// we'll output in 8-byte chunks as shown:
|
|
//
|
|
// [prefix] 00000000: xx-xx-xx-xx-xx-xx-xx-xx (........)
|
|
// [prefix] 00000008: xx-xx-xx-xx-xx-xx-xx-xx (........)
|
|
// [prefix] 00000010: xx-xx-xx-xx-xx-xx-xx-xx (........)
|
|
//
|
|
|
|
while (Length >= 16)
|
|
{
|
|
BYTE *pb = (BYTE *) (((ULONG_PTR) Data) + Offset);
|
|
|
|
::FusionpDbgPrintEx(
|
|
Level,
|
|
"%S%08lx: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x (%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c)\n",
|
|
PerLinePrefix,
|
|
Offset,
|
|
pb[0], pb[1], pb[2], pb[3], pb[4], pb[5], pb[6], pb[7],
|
|
pb[8], pb[9], pb[10], pb[11], pb[12], pb[13], pb[14], pb[15],
|
|
PRINTABLE(pb[0]),
|
|
PRINTABLE(pb[1]),
|
|
PRINTABLE(pb[2]),
|
|
PRINTABLE(pb[3]),
|
|
PRINTABLE(pb[4]),
|
|
PRINTABLE(pb[5]),
|
|
PRINTABLE(pb[6]),
|
|
PRINTABLE(pb[7]),
|
|
PRINTABLE(pb[8]),
|
|
PRINTABLE(pb[9]),
|
|
PRINTABLE(pb[10]),
|
|
PRINTABLE(pb[11]),
|
|
PRINTABLE(pb[12]),
|
|
PRINTABLE(pb[13]),
|
|
PRINTABLE(pb[14]),
|
|
PRINTABLE(pb[15]));
|
|
|
|
Offset += 16;
|
|
Length -= 16;
|
|
}
|
|
|
|
if (Length != 0)
|
|
{
|
|
WCHAR wchLeft[64], wchRight[64];
|
|
WCHAR rgTemp2[16]; // arbitrary big enough size
|
|
bool First = true;
|
|
ULONG i;
|
|
BYTE *pb = (BYTE *) (((ULONG_PTR) Data) + Offset);
|
|
|
|
// init output buffers
|
|
wchLeft[0] = 0;
|
|
::wcscpy( wchRight, L" (" );
|
|
|
|
for (i=0; i<16; i++)
|
|
{
|
|
if (Length > 0)
|
|
{
|
|
// left
|
|
::_snwprintf(rgTemp2, NUMBER_OF(rgTemp2), L"%ls%02x", First ? L" " : L"-", pb[i]);
|
|
rgTemp2[NUMBER_OF(rgTemp2) - 1] = L'\0';
|
|
|
|
First = false;
|
|
::wcscat(wchLeft, rgTemp2);
|
|
|
|
// right
|
|
::_snwprintf(rgTemp2, NUMBER_OF(rgTemp2), L"%c", PRINTABLE(pb[i]));
|
|
rgTemp2[NUMBER_OF(rgTemp2) - 1] = L'\0';
|
|
|
|
wcscat(wchRight, rgTemp2);
|
|
Length--;
|
|
}
|
|
else
|
|
{
|
|
::wcscat(wchLeft, L" ");
|
|
}
|
|
}
|
|
|
|
::wcscat(wchRight, L")");
|
|
|
|
::FusionpDbgPrintEx(
|
|
Level,
|
|
"%S %08lx:%ls%ls\n",
|
|
PerLinePrefix,
|
|
Offset,
|
|
wchLeft,
|
|
wchRight);
|
|
|
|
}
|
|
}
|
|
|
|
VOID
|
|
FusionpDbgPrintBlob(
|
|
ULONG Level,
|
|
PVOID Data,
|
|
SIZE_T Length,
|
|
PCWSTR PerLinePrefix
|
|
)
|
|
{
|
|
//
|
|
// This extra function reduces stack usage when the data
|
|
// isn't actually printed (and increases stack usage slightly otherwise).
|
|
//
|
|
if (!::FusionpDbgWouldPrintAtFilterLevel(Level))
|
|
return;
|
|
::FusionppDbgPrintBlob(Level, Data, Length, PerLinePrefix);
|
|
}
|