Source code of Windows XP (NT5)
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.

614 lines
16 KiB

  1. /****************************************************************************
  2. debug.c
  3. winmm debug support module
  4. Copyright (c) Microsoft Corporation 1990 - 1995. All rights reserved
  5. History
  6. 10/1/92 Updated for NT by Robin Speed (RobinSp)
  7. ****************************************************************************/
  8. #include "winmmi.h"
  9. //#include <wchar.h>
  10. #include <tchar.h>
  11. #include <stdarg.h>
  12. // BUGBUG - no REAL logging for now ! - NT doesn't have Dr Watson !
  13. #define LogParamError(a, b)
  14. //String constants.
  15. #ifdef DEBUG
  16. DBGPARAM dpCurSettings = {
  17. TEXT("mmio.lib"), {
  18. TEXT("Function"),TEXT("Warning"),TEXT("Error"),TEXT("Verbose"),
  19. TEXT("n/a"),TEXT("n/a"),TEXT("n/a"),TEXT("n/a"),
  20. TEXT("n/a"),TEXT("n/a"),TEXT("n/a"),TEXT("n/a"),
  21. TEXT("n/a"),TEXT("n/a"),TEXT("n/a"),TEXT("n/a") },
  22. 0x00000006
  23. };
  24. #endif //DEBUG
  25. HANDLE hHeap;
  26. PHNDL pHandleList;
  27. CRITICAL_SECTION HandleListCritSec;
  28. /***************************************************************************
  29. * @doc INTERNAL
  30. *
  31. * @func HANDLE | NewHandle | allocate a fixed handle in MMSYSTEM's local heap
  32. *
  33. * @parm UINT | uType | unique id describing handle type
  34. * @parm UINT | uSize | size in bytes to be allocated
  35. *
  36. * @rdesc Returns pointer/handle to memory object
  37. *
  38. * @comm a standard handle header (HNDL) will be added to the object,
  39. * and it will be linked into the list of MMSYSTEM handles.
  40. *
  41. ***************************************************************************/
  42. HANDLE NewHandle(UINT uType, UINT uSize)
  43. {
  44. PHNDL pHandle;
  45. hHeap = GetProcessHeap();
  46. pHandle = (PHNDL)HeapAlloc(hHeap, 0, sizeof(HNDL) + uSize);
  47. if (pHandle == NULL) {
  48. UINT err = GetLastError();
  49. return pHandle;
  50. } else {
  51. memset(pHandle, 0, sizeof(HNDL) + uSize);
  52. EnterCriticalSection(&HandleListCritSec);
  53. pHandle->pNext = pHandleList;
  54. pHandle->hThread = (HANDLE)GetCurrentThreadId(); // For WOW validation
  55. pHandle->uType = uType;
  56. InitializeCriticalSection(&pHandle->CritSec);
  57. pHandleList = pHandle;
  58. LeaveCriticalSection(&HandleListCritSec);
  59. }
  60. return PHtoH(pHandle);
  61. }
  62. /***************************************************************************
  63. * @doc INTERNAL
  64. *
  65. * @func HANDLE | FreeHandle | free handle allocated with NewHandle
  66. *
  67. * @parm HANDLE | hUser | handle returned from NewHandle
  68. *
  69. * @comm handle will be unlinked from list, and memory will be freed
  70. *
  71. *
  72. ***************************************************************************/
  73. void FreeHandle(HANDLE hUser)
  74. {
  75. /* Find handle and free from list */
  76. PHNDL pHandle;
  77. PHNDL *pSearch;
  78. if (hUser == NULL) {
  79. return;
  80. }
  81. //
  82. // Point to our handle data
  83. //
  84. pHandle = HtoPH(hUser);
  85. EnterCriticalSection(&HandleListCritSec);
  86. pSearch = &pHandleList;
  87. while (*pSearch != NULL) {
  88. if (*pSearch == pHandle) {
  89. //
  90. // Found it
  91. // Remove it from the list
  92. //
  93. *pSearch = pHandle->pNext;
  94. LeaveCriticalSection(&HandleListCritSec);
  95. DeleteCriticalSection(&pHandle->CritSec);
  96. pHandle->uType = 0;
  97. pHandle->hThread = NULL;
  98. pHandle->pNext = NULL;
  99. HeapFree(hHeap, 0, (LPTSTR)pHandle);
  100. return;
  101. } else {
  102. pSearch = &(*pSearch)->pNext;
  103. }
  104. }
  105. DEBUGMSG(1, (TEXT("Freeing handle which is not in the list !")));
  106. DEBUGCHK(FALSE);
  107. LeaveCriticalSection(&HandleListCritSec);
  108. }
  109. #ifndef UNDER_CExxx
  110. int winmmDebugLevel = 0;
  111. /**************************************************************************
  112. @doc INTERNAL
  113. @api void | winmmSetDebugLevel | Set the current debug level
  114. @parm int | iLevel | The new level to set
  115. @rdesc There is no return value
  116. **************************************************************************/
  117. void winmmSetDebugLevel(int level)
  118. {
  119. #ifdef DEBUG
  120. winmmDebugLevel = level;
  121. DEBUGMSG(1, (TEXT("debug level set to %d"), winmmDebugLevel));
  122. #endif
  123. }
  124. UINT inited = 0;
  125. #ifdef DEBUG
  126. extern int mciDebugLevel;
  127. #endif
  128. #ifdef DEBUG
  129. void InitDebugLevel(void)
  130. {
  131. if (!inited) {
  132. inited = 1;
  133. }
  134. DEBUGMSG(2, (TEXT("Starting, debug level=%d"), winmmDebugLevel));
  135. }
  136. #endif
  137. #ifdef DEBUG_RETAIL
  138. /***************************************************************************
  139. * @doc INTERNAL WAVE MIDI
  140. *
  141. * @func BOOL | ValidateHeader | validates a wave or midi date header
  142. *
  143. * @parm LPVOID | lpHeader| pointer to wave/midi header
  144. * @parm UINT | wSize | size of header passed by app
  145. * @parm UINT | wType | unique id describing header/handle type
  146. *
  147. * @rdesc Returns TRUE if <p> is non NULL and <wSize> is the correct size
  148. * Returns FALSE otherwise
  149. *
  150. * @comm if the header is invalid an error will be generated.
  151. *
  152. ***************************************************************************/
  153. BOOL ValidateHeader(PVOID pHdr, UINT uSize, UINT uType)
  154. {
  155. // Detect bad header
  156. if (!ValidateWritePointer(pHdr, uSize)) {
  157. DebugErr(DBF_ERROR, "Invalid header pointer");
  158. return FALSE;
  159. }
  160. // Check type
  161. switch (uType) {
  162. case TYPE_MIDIIN:
  163. case TYPE_MIDIOUT:
  164. case TYPE_MIDISTRM:
  165. {
  166. PMIDIHDR pHeader = pHdr;
  167. if ((TYPE_MIDISTRM == uType) &&
  168. uSize < sizeof(MIDIHDR))
  169. {
  170. DebugErr(DBF_ERROR, "Invalid header size");
  171. LogParamError(ERR_BAD_VALUE, uSize);
  172. return FALSE;
  173. }
  174. if (pHeader->dwFlags & ~MHDR_VALID) {
  175. LogParamError(ERR_BAD_FLAGS, ((PMIDIHDR)pHeader)->dwFlags);
  176. return FALSE;
  177. }
  178. /* NOTE: For Dreamcast, because we don't actually require a valid lpData
  179. in some cases, don't bother validating */
  180. }
  181. break;
  182. default:
  183. DEBUGCHK(FALSE);
  184. break;
  185. }
  186. return TRUE;
  187. }
  188. #ifndef USE_KERNEL_VALIDATION
  189. /***************************************************************************
  190. * @doc INTERNAL
  191. *
  192. * @func BOOL | ValidateReadPointer | validates that a pointer is valid to
  193. * read from.
  194. *
  195. * @parm LPVOID | lpPoint| pointer to validate
  196. * @parm DWORD | dLen | supposed length of said pointer
  197. *
  198. * @rdesc Returns TRUE if <p> is a valid pointer
  199. * Returns FALSE if <p> is not a valid pointer
  200. *
  201. * @comm will generate error if the pointer is invalid
  202. *
  203. ***************************************************************************/
  204. BOOL ValidateReadPointer(PVOID pPoint, ULONG Len)
  205. {
  206. // For now just check access to first and last byte
  207. try {
  208. volatile BYTE b;
  209. b = ((PBYTE)pPoint)[0];
  210. b = ((PBYTE)pPoint)[Len - 1];
  211. } except(EXCEPTION_EXECUTE_HANDLER) {
  212. LogParamError(ERR_BAD_PTR, pPoint);
  213. return FALSE;
  214. }
  215. return TRUE;
  216. }
  217. /***************************************************************************
  218. * @doc INTERNAL
  219. *
  220. * @func BOOL | ValidateWritePointer | validates that a pointer is valid to
  221. * write to.
  222. *
  223. * @parm LPVOID | lpPoint| pointer to validate
  224. * @parm DWORD | dLen | supposed length of said pointer
  225. *
  226. * @rdesc Returns TRUE if <p> is a valid pointer
  227. * Returns FALSE if <p> is not a valid pointer
  228. *
  229. * @comm will generate error if the pointer is invalid
  230. *
  231. ***************************************************************************/
  232. BOOL ValidateWritePointer(PVOID pPoint, ULONG Len)
  233. {
  234. // For now just check read and write access to first and last byte
  235. try {
  236. volatile BYTE b;
  237. b = ((PBYTE)pPoint)[0];
  238. ((PBYTE)pPoint)[0] = b;
  239. b = ((PBYTE)pPoint)[Len - 1];
  240. ((PBYTE)pPoint)[Len - 1] = b;
  241. } except(EXCEPTION_EXECUTE_HANDLER) {
  242. LogParamError(ERR_BAD_PTR, pPoint);
  243. return FALSE;
  244. }
  245. return TRUE;
  246. }
  247. #endif // USE_KERNEL_VALIDATION
  248. /***************************************************************************
  249. * @doc INTERNAL
  250. *
  251. * @func BOOL | ValidDriverCallback |
  252. *
  253. * validates that a driver callback is valid, to be valid a driver
  254. * callback must be a valid window, task, or a function in a FIXED DLL
  255. * code segment.
  256. *
  257. * @parm DWORD | dwCallback | callback to validate
  258. * @parm DWORD | wFlags | driver callback flags
  259. *
  260. * @rdesc Returns 0 if <dwCallback> is a valid callback
  261. * Returns error condition if <dwCallback> is not a valid callback
  262. ***************************************************************************/
  263. BOOL ValidDriverCallback(HANDLE hCallback, DWORD dwFlags)
  264. {
  265. switch (dwFlags & DCB_TYPEMASK) {
  266. case DCB_WINDOW:
  267. if (!IsWindow(hCallback)) {
  268. LogParamError(ERR_BAD_HWND, hCallback);
  269. return FALSE;
  270. }
  271. break;
  272. case DCB_EVENT:
  273. // BUGBUG enhance ! - how do we test that this is an event handle?
  274. //if (hCallback is not an event)
  275. // LogParamError(ERR_BAD_CALLBACK, hCallback);
  276. // return FALSE;
  277. //}
  278. break;
  279. case DCB_TASK:
  280. // BUGBUG enhance !
  281. //if (IsBadCodePtr((FARPROC)hCallback)) {
  282. // LogParamError(ERR_BAD_CALLBACK, hCallback);
  283. // return FALSE;
  284. //}
  285. break;
  286. case DCB_FUNCTION:
  287. // BUGBUG enhance !
  288. if (IsBadCodePtr((FARPROC)hCallback)) {
  289. LogParamError(ERR_BAD_CALLBACK, hCallback);
  290. return FALSE;
  291. }
  292. break;
  293. }
  294. return TRUE;
  295. }
  296. #ifndef USE_KERNEL_VALIDATION
  297. /* REVIEW peted: this appears to be unused */
  298. /**************************************************************************
  299. * @doc INTERNAL
  300. *
  301. * @func BOOL | ValidateStringW |
  302. *
  303. **************************************************************************/
  304. BOOL ValidateStringW(LPCTSTR pPoint, DWORD Len)
  305. {
  306. // For now just check access - do a 'strnlen'
  307. try {
  308. volatile TCHAR b;
  309. LPCTSTR p = pPoint;
  310. while (Len--) {
  311. b = *p;
  312. if (!b) {
  313. break;
  314. }
  315. p++;
  316. }
  317. } except(EXCEPTION_EXECUTE_HANDLER) {
  318. LogParamError(ERR_BAD_STRING_PTR, pPoint);
  319. return FALSE;
  320. }
  321. return TRUE;
  322. }
  323. #endif //USE_KERNEL_VALIDATION
  324. /**************************************************************************
  325. * @doc INTERNAL
  326. *
  327. * @func BOOL | ValidateHandle | validates a handle created with NewHandle
  328. *
  329. * @parm PHNDL | hLocal | handle returned from NewHandle
  330. * @parm UINT | wType | unique id describing handle type
  331. *
  332. * @rdesc Returns TRUE if <h> is a valid handle of type <wType>
  333. * Returns FALSE if <h> is not a valid handle
  334. *
  335. * @comm if the handle is invalid an error will be generated.
  336. *
  337. **************************************************************************/
  338. BOOL ValidateHandle(HANDLE hLocal, UINT uType)
  339. {
  340. BOOL OK;
  341. //
  342. // if the handle is less than 64k or a mapper id then
  343. // don't bother with the overhead of the try-except.
  344. //
  345. if ((UINT)hLocal < 0x10000)
  346. {
  347. LogParamError(ERR_BAD_HANDLE, hLocal);
  348. return FALSE;
  349. }
  350. try {
  351. OK = HtoPH(hLocal)->uType == uType;
  352. } except(EXCEPTION_EXECUTE_HANDLER) {
  353. LogParamError(ERR_BAD_HANDLE, hLocal);
  354. return FALSE;
  355. }
  356. return OK;
  357. }
  358. #ifdef DEBUG
  359. char * Types[4] = {"Unknown callback type",
  360. "Window callback",
  361. "Task callback",
  362. "Function callback"};
  363. #endif
  364. /**************************************************************************
  365. * @doc INTERNAL
  366. *
  367. * @func BOOL | ValidateCallbackType | validates a callback address,
  368. * window handle, or task handle
  369. *
  370. * @parm PHNDL | hLocal | handle returned from NewHandle
  371. * @parm UINT | wType | unique id describing handle type
  372. *
  373. * @rdesc Returns TRUE if <h> is a valid handle of type <wType>
  374. * Returns FALSE if <h> is not a valid handle
  375. *
  376. * @comm if the handle is invalid an error will be generated.
  377. *
  378. **************************************************************************/
  379. BOOL ValidateCallbackType(DWORD dwCallback, UINT uType)
  380. {
  381. #define DCALLBACK_WINDOW HIWORD(CALLBACK_WINDOW) // dwCallback is a HWND
  382. #define DCALLBACK_TASK HIWORD(CALLBACK_TASK) // dwCallback is a HTASK
  383. #define DCALLBACK_FUNCTION HIWORD(CALLBACK_FUNCTION) // dwCallback is a FARPROC
  384. #define DCALLBACK_EVENT HIWORD(CALLBACK_EVENT) // dwCallback is an EVENT
  385. UINT type = uType & HIWORD(CALLBACK_TYPEMASK);
  386. #ifdef DEBUG
  387. if (type>5) {
  388. type = 0;
  389. }
  390. DEBUGMSG(3, TEXT("Validating Callback, type=%d (%hs), handle=%8x"), type, Types[type], dwCallback);
  391. #endif
  392. switch (type) {
  393. case DCALLBACK_WINDOW:
  394. return(IsWindow((HWND)dwCallback));
  395. break;
  396. case DCALLBACK_EVENT:
  397. {
  398. // ?? how to verify that this is an event handle??
  399. //DWORD dwFlags;
  400. //GetHandleInformation((HANDLE)dwCallback, &dwFlags);
  401. return TRUE;
  402. }
  403. break;
  404. case DCALLBACK_FUNCTION:
  405. return(!(IsBadCodePtr((FARPROC)dwCallback)));
  406. break;
  407. case DCALLBACK_TASK:
  408. if (THREAD_PRIORITY_ERROR_RETURN == GetThreadPriority((HANDLE)dwCallback)) {
  409. DEBUGMSG(1, TEXT("Invalid callback task handle"));
  410. // I suspect we do not have the correct thread handle, in
  411. // which case we can only return TRUE.
  412. //return(FALSE);
  413. }
  414. return(TRUE);
  415. break;
  416. }
  417. return TRUE;
  418. }
  419. /**************************************************************************
  420. @doc INTERNAL
  421. @func void | dout | Output debug string if debug flag is set
  422. @parm LPTSTR | szString
  423. **************************************************************************/
  424. #ifdef DEBUG
  425. int fDebug = 1;
  426. #else
  427. int fDebug = 0;
  428. #endif
  429. //void dout(LPTSTR szString)
  430. //{
  431. // if (fDebug) {
  432. // OutputDebugStringA(szString);
  433. // }
  434. //}
  435. #ifdef LATER
  436. This routine should probably be replaced in the headers by redefining
  437. to use OutputDebugString
  438. #endif
  439. #undef OutputDebugStr
  440. // Make our function visible
  441. /*****************************************************************************
  442. * @doc EXTERNAL DDK
  443. *
  444. * @api void | OutputDebugStr | This function sends a debugging message
  445. * directly to the COM1 port or to a secondary monochrome display
  446. * adapter. Because it bypasses DOS, it can be called by low-level
  447. * callback functions and other code at interrupt time.
  448. *
  449. * @parm LPTSTR | lpOutputString | Specifies a far pointer to a
  450. * null-terminated string.
  451. *
  452. * @comm This function is available only in the debugging version of
  453. * Windows. The DebugOutput keyname in the [mmsystem]
  454. * section of SYSTEM.INI controls where the debugging information is
  455. * sent. If fDebugOutput is 0, all debug output is disabled.
  456. ******************************************************************************/
  457. #endif // DEBUG_RETAIL
  458. #endif // !UNDER_CE
  459. #ifdef DEBUG
  460. /***************************************************************************
  461. @doc INTERNAL
  462. @api void | winmmDbgOut | This function sends output to the current
  463. debug output device.
  464. @parm LPTSTR | lpszFormat | Pointer to a printf style format string.
  465. @parm ??? | ... | Args.
  466. @rdesc There is no return value.
  467. ****************************************************************************/
  468. extern BOOL Quiet = FALSE;
  469. void winmmDbgOut(LPTSTR lpszFormat, ...)
  470. {
  471. TCHAR buf[512];
  472. UINT n;
  473. va_list va;
  474. if (Quiet) {
  475. return;
  476. }
  477. n = wsprintf(buf, TEXT("WINMM: (tid %x) "), GetCurrentThreadId());
  478. va_start(va, lpszFormat);
  479. n += wvsprintf(buf+n, lpszFormat, va);
  480. va_end(va);
  481. buf[n++] = '\n';
  482. buf[n] = 0;
  483. OutputDebugString(buf);
  484. Sleep(0); // let terminal catch up
  485. }
  486. /***************************************************************************
  487. @doc INTERNAL
  488. @api void | dDbgAssert | This function prints an assertion message.
  489. @parm LPTSTR | exp | Pointer to the expression string.
  490. @parm LPTSTR | file | Pointer to the file name.
  491. @parm int | line | The line number.
  492. @rdesc There is no return value.
  493. ****************************************************************************/
  494. void dDbgAssert(LPTSTR exp, LPTSTR file, int line)
  495. {
  496. DEBUGMSG(1, (TEXT("Assertion failure:")));
  497. DEBUGMSG(1, (TEXT(" Exp: %s"), exp));
  498. DEBUGMSG(1, (TEXT(" File: %s, line: %d"), file, line));
  499. DebugBreak();
  500. }
  501. #else // Still need to export this thing to help others
  502. void winmmDbgOut(LPTSTR lpszFormat, ...)
  503. {
  504. }
  505. #endif // DEBUG