windows-nt/Source/XPSP1/NT/enduser/speech/ce/mmio/debug.c
2020-09-26 16:20:57 +08:00

615 lines
16 KiB
C

/****************************************************************************
debug.c
winmm debug support module
Copyright (c) Microsoft Corporation 1990 - 1995. All rights reserved
History
10/1/92 Updated for NT by Robin Speed (RobinSp)
****************************************************************************/
#include "winmmi.h"
//#include <wchar.h>
#include <tchar.h>
#include <stdarg.h>
// BUGBUG - no REAL logging for now ! - NT doesn't have Dr Watson !
#define LogParamError(a, b)
//String constants.
#ifdef DEBUG
DBGPARAM dpCurSettings = {
TEXT("mmio.lib"), {
TEXT("Function"),TEXT("Warning"),TEXT("Error"),TEXT("Verbose"),
TEXT("n/a"),TEXT("n/a"),TEXT("n/a"),TEXT("n/a"),
TEXT("n/a"),TEXT("n/a"),TEXT("n/a"),TEXT("n/a"),
TEXT("n/a"),TEXT("n/a"),TEXT("n/a"),TEXT("n/a") },
0x00000006
};
#endif //DEBUG
HANDLE hHeap;
PHNDL pHandleList;
CRITICAL_SECTION HandleListCritSec;
/***************************************************************************
* @doc INTERNAL
*
* @func HANDLE | NewHandle | allocate a fixed handle in MMSYSTEM's local heap
*
* @parm UINT | uType | unique id describing handle type
* @parm UINT | uSize | size in bytes to be allocated
*
* @rdesc Returns pointer/handle to memory object
*
* @comm a standard handle header (HNDL) will be added to the object,
* and it will be linked into the list of MMSYSTEM handles.
*
***************************************************************************/
HANDLE NewHandle(UINT uType, UINT uSize)
{
PHNDL pHandle;
hHeap = GetProcessHeap();
pHandle = (PHNDL)HeapAlloc(hHeap, 0, sizeof(HNDL) + uSize);
if (pHandle == NULL) {
UINT err = GetLastError();
return pHandle;
} else {
memset(pHandle, 0, sizeof(HNDL) + uSize);
EnterCriticalSection(&HandleListCritSec);
pHandle->pNext = pHandleList;
pHandle->hThread = (HANDLE)GetCurrentThreadId(); // For WOW validation
pHandle->uType = uType;
InitializeCriticalSection(&pHandle->CritSec);
pHandleList = pHandle;
LeaveCriticalSection(&HandleListCritSec);
}
return PHtoH(pHandle);
}
/***************************************************************************
* @doc INTERNAL
*
* @func HANDLE | FreeHandle | free handle allocated with NewHandle
*
* @parm HANDLE | hUser | handle returned from NewHandle
*
* @comm handle will be unlinked from list, and memory will be freed
*
*
***************************************************************************/
void FreeHandle(HANDLE hUser)
{
/* Find handle and free from list */
PHNDL pHandle;
PHNDL *pSearch;
if (hUser == NULL) {
return;
}
//
// Point to our handle data
//
pHandle = HtoPH(hUser);
EnterCriticalSection(&HandleListCritSec);
pSearch = &pHandleList;
while (*pSearch != NULL) {
if (*pSearch == pHandle) {
//
// Found it
// Remove it from the list
//
*pSearch = pHandle->pNext;
LeaveCriticalSection(&HandleListCritSec);
DeleteCriticalSection(&pHandle->CritSec);
pHandle->uType = 0;
pHandle->hThread = NULL;
pHandle->pNext = NULL;
HeapFree(hHeap, 0, (LPTSTR)pHandle);
return;
} else {
pSearch = &(*pSearch)->pNext;
}
}
DEBUGMSG(1, (TEXT("Freeing handle which is not in the list !")));
DEBUGCHK(FALSE);
LeaveCriticalSection(&HandleListCritSec);
}
#ifndef UNDER_CExxx
int winmmDebugLevel = 0;
/**************************************************************************
@doc INTERNAL
@api void | winmmSetDebugLevel | Set the current debug level
@parm int | iLevel | The new level to set
@rdesc There is no return value
**************************************************************************/
void winmmSetDebugLevel(int level)
{
#ifdef DEBUG
winmmDebugLevel = level;
DEBUGMSG(1, (TEXT("debug level set to %d"), winmmDebugLevel));
#endif
}
UINT inited = 0;
#ifdef DEBUG
extern int mciDebugLevel;
#endif
#ifdef DEBUG
void InitDebugLevel(void)
{
if (!inited) {
inited = 1;
}
DEBUGMSG(2, (TEXT("Starting, debug level=%d"), winmmDebugLevel));
}
#endif
#ifdef DEBUG_RETAIL
/***************************************************************************
* @doc INTERNAL WAVE MIDI
*
* @func BOOL | ValidateHeader | validates a wave or midi date header
*
* @parm LPVOID | lpHeader| pointer to wave/midi header
* @parm UINT | wSize | size of header passed by app
* @parm UINT | wType | unique id describing header/handle type
*
* @rdesc Returns TRUE if <p> is non NULL and <wSize> is the correct size
* Returns FALSE otherwise
*
* @comm if the header is invalid an error will be generated.
*
***************************************************************************/
BOOL ValidateHeader(PVOID pHdr, UINT uSize, UINT uType)
{
// Detect bad header
if (!ValidateWritePointer(pHdr, uSize)) {
DebugErr(DBF_ERROR, "Invalid header pointer");
return FALSE;
}
// Check type
switch (uType) {
case TYPE_MIDIIN:
case TYPE_MIDIOUT:
case TYPE_MIDISTRM:
{
PMIDIHDR pHeader = pHdr;
if ((TYPE_MIDISTRM == uType) &&
uSize < sizeof(MIDIHDR))
{
DebugErr(DBF_ERROR, "Invalid header size");
LogParamError(ERR_BAD_VALUE, uSize);
return FALSE;
}
if (pHeader->dwFlags & ~MHDR_VALID) {
LogParamError(ERR_BAD_FLAGS, ((PMIDIHDR)pHeader)->dwFlags);
return FALSE;
}
/* NOTE: For Dreamcast, because we don't actually require a valid lpData
in some cases, don't bother validating */
}
break;
default:
DEBUGCHK(FALSE);
break;
}
return TRUE;
}
#ifndef USE_KERNEL_VALIDATION
/***************************************************************************
* @doc INTERNAL
*
* @func BOOL | ValidateReadPointer | validates that a pointer is valid to
* read from.
*
* @parm LPVOID | lpPoint| pointer to validate
* @parm DWORD | dLen | supposed length of said pointer
*
* @rdesc Returns TRUE if <p> is a valid pointer
* Returns FALSE if <p> is not a valid pointer
*
* @comm will generate error if the pointer is invalid
*
***************************************************************************/
BOOL ValidateReadPointer(PVOID pPoint, ULONG Len)
{
// For now just check access to first and last byte
try {
volatile BYTE b;
b = ((PBYTE)pPoint)[0];
b = ((PBYTE)pPoint)[Len - 1];
} except(EXCEPTION_EXECUTE_HANDLER) {
LogParamError(ERR_BAD_PTR, pPoint);
return FALSE;
}
return TRUE;
}
/***************************************************************************
* @doc INTERNAL
*
* @func BOOL | ValidateWritePointer | validates that a pointer is valid to
* write to.
*
* @parm LPVOID | lpPoint| pointer to validate
* @parm DWORD | dLen | supposed length of said pointer
*
* @rdesc Returns TRUE if <p> is a valid pointer
* Returns FALSE if <p> is not a valid pointer
*
* @comm will generate error if the pointer is invalid
*
***************************************************************************/
BOOL ValidateWritePointer(PVOID pPoint, ULONG Len)
{
// For now just check read and write access to first and last byte
try {
volatile BYTE b;
b = ((PBYTE)pPoint)[0];
((PBYTE)pPoint)[0] = b;
b = ((PBYTE)pPoint)[Len - 1];
((PBYTE)pPoint)[Len - 1] = b;
} except(EXCEPTION_EXECUTE_HANDLER) {
LogParamError(ERR_BAD_PTR, pPoint);
return FALSE;
}
return TRUE;
}
#endif // USE_KERNEL_VALIDATION
/***************************************************************************
* @doc INTERNAL
*
* @func BOOL | ValidDriverCallback |
*
* validates that a driver callback is valid, to be valid a driver
* callback must be a valid window, task, or a function in a FIXED DLL
* code segment.
*
* @parm DWORD | dwCallback | callback to validate
* @parm DWORD | wFlags | driver callback flags
*
* @rdesc Returns 0 if <dwCallback> is a valid callback
* Returns error condition if <dwCallback> is not a valid callback
***************************************************************************/
BOOL ValidDriverCallback(HANDLE hCallback, DWORD dwFlags)
{
switch (dwFlags & DCB_TYPEMASK) {
case DCB_WINDOW:
if (!IsWindow(hCallback)) {
LogParamError(ERR_BAD_HWND, hCallback);
return FALSE;
}
break;
case DCB_EVENT:
// BUGBUG enhance ! - how do we test that this is an event handle?
//if (hCallback is not an event)
// LogParamError(ERR_BAD_CALLBACK, hCallback);
// return FALSE;
//}
break;
case DCB_TASK:
// BUGBUG enhance !
//if (IsBadCodePtr((FARPROC)hCallback)) {
// LogParamError(ERR_BAD_CALLBACK, hCallback);
// return FALSE;
//}
break;
case DCB_FUNCTION:
// BUGBUG enhance !
if (IsBadCodePtr((FARPROC)hCallback)) {
LogParamError(ERR_BAD_CALLBACK, hCallback);
return FALSE;
}
break;
}
return TRUE;
}
#ifndef USE_KERNEL_VALIDATION
/* REVIEW peted: this appears to be unused */
/**************************************************************************
* @doc INTERNAL
*
* @func BOOL | ValidateStringW |
*
**************************************************************************/
BOOL ValidateStringW(LPCTSTR pPoint, DWORD Len)
{
// For now just check access - do a 'strnlen'
try {
volatile TCHAR b;
LPCTSTR p = pPoint;
while (Len--) {
b = *p;
if (!b) {
break;
}
p++;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
LogParamError(ERR_BAD_STRING_PTR, pPoint);
return FALSE;
}
return TRUE;
}
#endif //USE_KERNEL_VALIDATION
/**************************************************************************
* @doc INTERNAL
*
* @func BOOL | ValidateHandle | validates a handle created with NewHandle
*
* @parm PHNDL | hLocal | handle returned from NewHandle
* @parm UINT | wType | unique id describing handle type
*
* @rdesc Returns TRUE if <h> is a valid handle of type <wType>
* Returns FALSE if <h> is not a valid handle
*
* @comm if the handle is invalid an error will be generated.
*
**************************************************************************/
BOOL ValidateHandle(HANDLE hLocal, UINT uType)
{
BOOL OK;
//
// if the handle is less than 64k or a mapper id then
// don't bother with the overhead of the try-except.
//
if ((UINT)hLocal < 0x10000)
{
LogParamError(ERR_BAD_HANDLE, hLocal);
return FALSE;
}
try {
OK = HtoPH(hLocal)->uType == uType;
} except(EXCEPTION_EXECUTE_HANDLER) {
LogParamError(ERR_BAD_HANDLE, hLocal);
return FALSE;
}
return OK;
}
#ifdef DEBUG
char * Types[4] = {"Unknown callback type",
"Window callback",
"Task callback",
"Function callback"};
#endif
/**************************************************************************
* @doc INTERNAL
*
* @func BOOL | ValidateCallbackType | validates a callback address,
* window handle, or task handle
*
* @parm PHNDL | hLocal | handle returned from NewHandle
* @parm UINT | wType | unique id describing handle type
*
* @rdesc Returns TRUE if <h> is a valid handle of type <wType>
* Returns FALSE if <h> is not a valid handle
*
* @comm if the handle is invalid an error will be generated.
*
**************************************************************************/
BOOL ValidateCallbackType(DWORD dwCallback, UINT uType)
{
#define DCALLBACK_WINDOW HIWORD(CALLBACK_WINDOW) // dwCallback is a HWND
#define DCALLBACK_TASK HIWORD(CALLBACK_TASK) // dwCallback is a HTASK
#define DCALLBACK_FUNCTION HIWORD(CALLBACK_FUNCTION) // dwCallback is a FARPROC
#define DCALLBACK_EVENT HIWORD(CALLBACK_EVENT) // dwCallback is an EVENT
UINT type = uType & HIWORD(CALLBACK_TYPEMASK);
#ifdef DEBUG
if (type>5) {
type = 0;
}
DEBUGMSG(3, TEXT("Validating Callback, type=%d (%hs), handle=%8x"), type, Types[type], dwCallback);
#endif
switch (type) {
case DCALLBACK_WINDOW:
return(IsWindow((HWND)dwCallback));
break;
case DCALLBACK_EVENT:
{
// ?? how to verify that this is an event handle??
//DWORD dwFlags;
//GetHandleInformation((HANDLE)dwCallback, &dwFlags);
return TRUE;
}
break;
case DCALLBACK_FUNCTION:
return(!(IsBadCodePtr((FARPROC)dwCallback)));
break;
case DCALLBACK_TASK:
if (THREAD_PRIORITY_ERROR_RETURN == GetThreadPriority((HANDLE)dwCallback)) {
DEBUGMSG(1, TEXT("Invalid callback task handle"));
// I suspect we do not have the correct thread handle, in
// which case we can only return TRUE.
//return(FALSE);
}
return(TRUE);
break;
}
return TRUE;
}
/**************************************************************************
@doc INTERNAL
@func void | dout | Output debug string if debug flag is set
@parm LPTSTR | szString
**************************************************************************/
#ifdef DEBUG
int fDebug = 1;
#else
int fDebug = 0;
#endif
//void dout(LPTSTR szString)
//{
// if (fDebug) {
// OutputDebugStringA(szString);
// }
//}
#ifdef LATER
This routine should probably be replaced in the headers by redefining
to use OutputDebugString
#endif
#undef OutputDebugStr
// Make our function visible
/*****************************************************************************
* @doc EXTERNAL DDK
*
* @api void | OutputDebugStr | This function sends a debugging message
* directly to the COM1 port or to a secondary monochrome display
* adapter. Because it bypasses DOS, it can be called by low-level
* callback functions and other code at interrupt time.
*
* @parm LPTSTR | lpOutputString | Specifies a far pointer to a
* null-terminated string.
*
* @comm This function is available only in the debugging version of
* Windows. The DebugOutput keyname in the [mmsystem]
* section of SYSTEM.INI controls where the debugging information is
* sent. If fDebugOutput is 0, all debug output is disabled.
******************************************************************************/
#endif // DEBUG_RETAIL
#endif // !UNDER_CE
#ifdef DEBUG
/***************************************************************************
@doc INTERNAL
@api void | winmmDbgOut | This function sends output to the current
debug output device.
@parm LPTSTR | lpszFormat | Pointer to a printf style format string.
@parm ??? | ... | Args.
@rdesc There is no return value.
****************************************************************************/
extern BOOL Quiet = FALSE;
void winmmDbgOut(LPTSTR lpszFormat, ...)
{
TCHAR buf[512];
UINT n;
va_list va;
if (Quiet) {
return;
}
n = wsprintf(buf, TEXT("WINMM: (tid %x) "), GetCurrentThreadId());
va_start(va, lpszFormat);
n += wvsprintf(buf+n, lpszFormat, va);
va_end(va);
buf[n++] = '\n';
buf[n] = 0;
OutputDebugString(buf);
Sleep(0); // let terminal catch up
}
/***************************************************************************
@doc INTERNAL
@api void | dDbgAssert | This function prints an assertion message.
@parm LPTSTR | exp | Pointer to the expression string.
@parm LPTSTR | file | Pointer to the file name.
@parm int | line | The line number.
@rdesc There is no return value.
****************************************************************************/
void dDbgAssert(LPTSTR exp, LPTSTR file, int line)
{
DEBUGMSG(1, (TEXT("Assertion failure:")));
DEBUGMSG(1, (TEXT(" Exp: %s"), exp));
DEBUGMSG(1, (TEXT(" File: %s, line: %d"), file, line));
DebugBreak();
}
#else // Still need to export this thing to help others
void winmmDbgOut(LPTSTR lpszFormat, ...)
{
}
#endif // DEBUG