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.

855 lines
19 KiB

  1. //============================================================================
  2. // Copyright (c) 1995, Microsoft Corporation
  3. //
  4. // File: api.c
  5. //
  6. // History:
  7. // Abolade Gbadegesin July-25-1995 Created
  8. //
  9. // API entry-points for tracing dll
  10. //============================================================================
  11. #include <nt.h>
  12. #include <ntrtl.h>
  13. #include <nturtl.h>
  14. #include <windows.h>
  15. #include <rtutils.h>
  16. #include <stdlib.h>
  17. #include "trace.h"
  18. #define ENTER_TRACE_API(lpserver) \
  19. (((lpserver)!=NULL) && ((lpserver)->TS_StopEvent != NULL))
  20. //
  21. // called before any other functions; responsible for creating
  22. // and initializing a client structure for the caller
  23. // and notifying the server of the new client
  24. //
  25. DWORD
  26. APIENTRY
  27. TraceRegisterEx(
  28. IN LPCTSTR lpszCallerName,
  29. IN DWORD dwFlags
  30. ) {
  31. DWORD dwErr, dwClientID;
  32. LPTRACE_SERVER lpserver;
  33. LPTRACE_CLIENT lpclient, *lplpc, *lplpcstart, *lplpcend;
  34. lpserver = GET_TRACE_SERVER();
  35. ASSERTMSG ("Could not create trace server ", lpserver!=NULL);
  36. if (!lpserver)
  37. return INVALID_TRACEID;
  38. TRACE_ACQUIRE_WRITELOCK(lpserver);
  39. //
  40. // complete the console thread event creations if not done before
  41. //
  42. if (lpserver->TS_TableEvent == NULL) {
  43. dwErr = TraceCreateServerComplete(lpserver);
  44. if (dwErr != 0) {
  45. TRACE_RELEASE_WRITELOCK(lpserver);
  46. return INVALID_TRACEID;
  47. }
  48. }
  49. lpclient = TraceFindClient(lpserver, lpszCallerName);
  50. if (lpclient != NULL) {
  51. //
  52. // client already exists
  53. //
  54. TRACE_RELEASE_WRITELOCK(lpserver);
  55. return lpclient->TC_ClientID;
  56. }
  57. //
  58. // find an empty space
  59. //
  60. lplpcstart = lpserver->TS_ClientTable;
  61. lplpcend = lplpcstart + MAX_CLIENT_COUNT;
  62. for (lplpc = lplpcstart; lplpc < lplpcend; lplpc++) {
  63. if (*lplpc == NULL) { break; }
  64. }
  65. if (lplpc >= lplpcend) {
  66. //
  67. // no space in table
  68. //
  69. TRACE_RELEASE_WRITELOCK(lpserver);
  70. return INVALID_TRACEID;
  71. }
  72. //
  73. // create the new client and enable it
  74. //
  75. dwErr = TraceCreateClient(lplpc);
  76. if (dwErr != 0) {
  77. //
  78. // something wrong, so abort
  79. //
  80. TRACE_RELEASE_WRITELOCK(lpserver);
  81. return INVALID_TRACEID;
  82. }
  83. lpclient = *lplpc;
  84. lpclient->TC_ClientID = dwClientID = (DWORD)(lplpc - lplpcstart);
  85. lstrcpyn(
  86. lpclient->TC_ClientName, lpszCallerName,
  87. MAX_CLIENTNAME_LENGTH - 1
  88. );
  89. //
  90. // copy the client name in the other format as well
  91. //
  92. #ifdef UNICODE
  93. wcstombs(
  94. lpclient->TC_ClientNameA, lpclient->TC_ClientNameW,
  95. MAX_CLIENTNAME_LENGTH - 1
  96. );
  97. #else
  98. mbstowcs(
  99. lpclient->TC_ClientNameW, lpclient->TC_ClientNameA,
  100. MAX_CLIENTNAME_LENGTH - 1
  101. );
  102. #endif
  103. if ((dwFlags & TRACE_USE_FILE) || (dwFlags & TRACE_USE_CONSOLE)) {
  104. if (dwFlags & TRACE_USE_FILE) {
  105. lpclient->TC_Flags |= TRACEFLAGS_USEFILE;
  106. }
  107. if (dwFlags & TRACE_USE_CONSOLE) {
  108. lpclient->TC_Flags |= TRACEFLAGS_USECONSOLE;
  109. }
  110. }
  111. else {
  112. lpclient->TC_Flags |= TRACEFLAGS_REGCONFIG;
  113. }
  114. //
  115. // load client's configuration and open its file
  116. // and its console buffer if necessary
  117. //
  118. dwErr = TraceEnableClient(lpserver, lpclient, TRUE);
  119. if (dwErr != 0) {
  120. //
  121. // something wrong, so abort
  122. //
  123. TraceDeleteClient(lpserver, lplpc);
  124. TRACE_RELEASE_WRITELOCK(lpserver);
  125. return INVALID_TRACEID;
  126. }
  127. //
  128. // Create trace server thread if required
  129. //
  130. if (g_serverThread==NULL) {
  131. dwErr = TraceCreateServerThread(dwFlags, TRUE,TRUE); //have lock,check
  132. if (NO_ERROR != dwErr){
  133. TRACE_RELEASE_WRITELOCK(lpserver);
  134. return INVALID_TRACEID;
  135. }
  136. }
  137. TRACE_RELEASE_WRITELOCK(lpserver);
  138. //
  139. // tell server there is a new client in the table
  140. //
  141. SetEvent(lpserver->TS_TableEvent);
  142. return dwClientID;
  143. }
  144. DWORD
  145. APIENTRY
  146. TraceDeregisterEx(
  147. IN DWORD dwTraceID,
  148. IN DWORD dwFlags
  149. );
  150. //
  151. // called to stop tracing.
  152. // frees client state and notifies server of change
  153. //
  154. DWORD
  155. APIENTRY
  156. TraceDeregister(
  157. IN DWORD dwTraceID
  158. ) {
  159. return TraceDeregisterEx(dwTraceID, 0);
  160. }
  161. DWORD
  162. APIENTRY
  163. TraceDeregisterEx(
  164. IN DWORD dwTraceID,
  165. IN DWORD dwFlags
  166. ) {
  167. DWORD dwErr;
  168. LPTRACE_CLIENT *lplpc;
  169. LPTRACE_SERVER lpserver;
  170. if (dwTraceID == INVALID_TRACEID || dwTraceID >= MAX_CLIENT_COUNT) {
  171. return ERROR_INVALID_PARAMETER;
  172. }
  173. lpserver = GET_TRACE_SERVER_NO_INIT ();
  174. //ASSERTMSG ("Server not initialized ", lpserver);
  175. if (!ENTER_TRACE_API(lpserver)) { return ERROR_CAN_NOT_COMPLETE; }
  176. //
  177. // lock the server, unless the flag says not to.
  178. //
  179. if (!(dwFlags & TRACE_NO_SYNCH)) { TRACE_ACQUIRE_WRITELOCK(lpserver); }
  180. //
  181. // get the client pointer
  182. //
  183. lplpc = lpserver->TS_ClientTable + dwTraceID;
  184. dwErr = TraceDeleteClient(lpserver, lplpc);
  185. //
  186. // reset array for client change notifications.
  187. // only used if server thread is not created
  188. //
  189. if (!g_serverThread) {
  190. SetWaitArray(lpserver);
  191. }
  192. if (!(dwFlags & TRACE_NO_SYNCH)) { TRACE_RELEASE_WRITELOCK(lpserver); }
  193. //
  194. // tell the server that a client has left
  195. //
  196. SetEvent(lpserver->TS_TableEvent);
  197. return 0;
  198. }
  199. DWORD
  200. APIENTRY
  201. TraceGetConsole(
  202. IN DWORD dwTraceID,
  203. OUT LPHANDLE lphConsole
  204. ) {
  205. LPTRACE_CLIENT lpclient;
  206. LPTRACE_SERVER lpserver;
  207. if (dwTraceID == INVALID_TRACEID || dwTraceID >= MAX_CLIENT_COUNT ||
  208. lphConsole == NULL) {
  209. return ERROR_INVALID_PARAMETER;
  210. }
  211. lpserver = GET_TRACE_SERVER_NO_INIT ();
  212. ASSERTMSG ("Server not initialized ", lpserver);
  213. if (!ENTER_TRACE_API(lpserver)) { return ERROR_CAN_NOT_COMPLETE; }
  214. *lphConsole = NULL;
  215. TRACE_ACQUIRE_READLOCK(lpserver);
  216. lpclient = lpserver->TS_ClientTable[dwTraceID];
  217. if (lpclient == NULL) {
  218. TRACE_RELEASE_READLOCK(lpserver);
  219. return ERROR_INVALID_PARAMETER;
  220. }
  221. TRACE_ACQUIRE_READLOCK(lpclient);
  222. *lphConsole = lpclient->TC_Console;
  223. TRACE_RELEASE_READLOCK(lpclient);
  224. TRACE_RELEASE_READLOCK(lpserver);
  225. return 0;
  226. }
  227. DWORD
  228. APIENTRY
  229. TracePrintf(
  230. IN DWORD dwTraceID,
  231. IN LPCTSTR lpszFormat,
  232. IN ... OPTIONAL
  233. ) {
  234. DWORD dwSize;
  235. va_list arglist;
  236. if (dwTraceID == INVALID_TRACEID || dwTraceID >= MAX_CLIENT_COUNT) {
  237. return 0;
  238. }
  239. CREATE_SERVER_THREAD_IF_REQUIRED();
  240. va_start(arglist, lpszFormat);
  241. dwSize = TraceVprintfInternal(dwTraceID, 0, lpszFormat, arglist);
  242. va_end(arglist);
  243. return dwSize;
  244. }
  245. DWORD
  246. APIENTRY
  247. TracePrintfEx(
  248. IN DWORD dwTraceID,
  249. IN DWORD dwFlags,
  250. IN LPCTSTR lpszFormat,
  251. IN ... OPTIONAL
  252. ) {
  253. DWORD dwSize;
  254. va_list arglist;
  255. if (dwTraceID == INVALID_TRACEID || dwTraceID >= MAX_CLIENT_COUNT) {
  256. return 0;
  257. }
  258. CREATE_SERVER_THREAD_IF_REQUIRED();
  259. va_start(arglist, lpszFormat);
  260. dwSize = TraceVprintfInternal(dwTraceID, dwFlags, lpszFormat, arglist);
  261. va_end(arglist);
  262. return dwSize;
  263. }
  264. DWORD
  265. APIENTRY
  266. TraceVprintfEx(
  267. IN DWORD dwTraceID,
  268. IN DWORD dwFlags,
  269. IN LPCTSTR lpszFormat,
  270. IN va_list arglist
  271. ) {
  272. if (dwTraceID == INVALID_TRACEID || dwTraceID >= MAX_CLIENT_COUNT) {
  273. return 0;
  274. }
  275. CREATE_SERVER_THREAD_IF_REQUIRED();
  276. return TraceVprintfInternal(dwTraceID, dwFlags, lpszFormat, arglist);
  277. }
  278. DWORD
  279. TraceVprintfInternal(
  280. IN DWORD dwTraceID,
  281. IN DWORD dwFlags,
  282. IN LPCTSTR lpszFormat,
  283. IN va_list arglist
  284. ) {
  285. SYSTEMTIME st;
  286. DWORD dwThread;
  287. DWORD dwErr, dwSize;
  288. LPTRACE_CLIENT lpclient;
  289. LPTRACE_SERVER lpserver;
  290. PTCHAR szFormat, szBuffer;
  291. lpserver = GET_TRACE_SERVER_NO_INIT ();
  292. ASSERTMSG ("Server not initialized ", lpserver);
  293. if (!ENTER_TRACE_API(lpserver)) { return ERROR_CAN_NOT_COMPLETE; }
  294. //
  295. // return quickly if no output will be generated;
  296. //
  297. if (dwFlags & TRACE_USE_MASK) {
  298. if (!(*(lpserver->TS_FlagsCache + dwTraceID) & (dwFlags & 0xffff0000))) {
  299. return 0;
  300. }
  301. }
  302. else {
  303. if (!*(lpserver->TS_FlagsCache + dwTraceID)) {
  304. return 0;
  305. }
  306. }
  307. TRACE_ACQUIRE_READLOCK(lpserver);
  308. lpclient = lpserver->TS_ClientTable[dwTraceID];
  309. if (lpclient == NULL) {
  310. TRACE_RELEASE_READLOCK(lpserver);
  311. return 0;
  312. }
  313. TRACE_ACQUIRE_READLOCK(lpclient);
  314. if (TRACE_CLIENT_IS_DISABLED(lpclient)) {
  315. TRACE_RELEASE_READLOCK(lpclient);
  316. TRACE_RELEASE_READLOCK(lpserver);
  317. return 0;
  318. }
  319. szFormat = (PTCHAR) HeapAlloc(GetProcessHeap(), 0, DEF_PRINT_BUFSIZE);
  320. if (!szFormat) {
  321. TRACE_RELEASE_READLOCK(lpclient);
  322. TRACE_RELEASE_READLOCK(lpserver);
  323. return ERROR_NOT_ENOUGH_MEMORY;
  324. }
  325. szBuffer = (PTCHAR) HeapAlloc(GetProcessHeap(), 0, DEF_PRINT_BUFSIZE);
  326. if (!szBuffer) {
  327. TRACE_RELEASE_READLOCK(lpclient);
  328. TRACE_RELEASE_READLOCK(lpserver);
  329. HeapFree(GetProcessHeap(), 0, szFormat);
  330. return ERROR_NOT_ENOUGH_MEMORY;
  331. }
  332. //
  333. // default format for output is
  334. // \n<time>:
  335. //
  336. if (dwFlags & TRACE_NO_STDINFO) {
  337. wvsprintf(szBuffer, lpszFormat, arglist);
  338. }
  339. else {
  340. GetLocalTime(&st);
  341. if ((dwFlags & TRACE_USE_MSEC) == 0) {
  342. if (dwFlags & TRACE_USE_DATE) {
  343. wsprintf(
  344. szFormat, TEXT("\r\n[%03d] %02u-%02u %02u:%02u:%02u: %s") ,
  345. GetCurrentThreadId(), st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,
  346. lpszFormat
  347. );
  348. }
  349. else {
  350. wsprintf(
  351. szFormat, TEXT("\r\n[%03d] %02u:%02u:%02u: %s") ,
  352. GetCurrentThreadId(), st.wHour, st.wMinute, st.wSecond,
  353. lpszFormat
  354. );
  355. }
  356. }
  357. else {
  358. if (dwFlags & TRACE_USE_DATE) {
  359. wsprintf(
  360. szFormat, TEXT("\r\n[%03d] %02u-%02u %02u:%02u:%02u:%03u: %s") ,
  361. GetCurrentThreadId(), st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,
  362. st.wMilliseconds, lpszFormat
  363. );
  364. }
  365. else {
  366. wsprintf(
  367. szFormat, TEXT("\r\n[%03d] %02u:%02u:%02u:%03u: %s") ,
  368. GetCurrentThreadId(), st.wHour, st.wMinute, st.wSecond,
  369. st.wMilliseconds, lpszFormat
  370. );
  371. }
  372. }
  373. wvsprintf(szBuffer, szFormat, arglist);
  374. }
  375. dwSize = TraceWriteOutput(lpserver, lpclient, dwFlags, szBuffer);
  376. TRACE_RELEASE_READLOCK(lpclient);
  377. TRACE_RELEASE_READLOCK(lpserver);
  378. HeapFree(GetProcessHeap(), 0, szFormat);
  379. HeapFree(GetProcessHeap(), 0, szBuffer);
  380. return dwSize;
  381. }
  382. DWORD
  383. APIENTRY
  384. TracePutsEx(
  385. IN DWORD dwTraceID,
  386. IN DWORD dwFlags,
  387. IN LPCTSTR lpszString
  388. ) {
  389. SYSTEMTIME st;
  390. DWORD dwErr, dwSize;
  391. LPTRACE_CLIENT lpclient;
  392. LPTRACE_SERVER lpserver;
  393. LPCTSTR lpszOutput;
  394. PTCHAR szBuffer;
  395. if (dwTraceID == INVALID_TRACEID || dwTraceID >= MAX_CLIENT_COUNT) {
  396. return 0;
  397. }
  398. lpserver = GET_TRACE_SERVER_NO_INIT ();
  399. ASSERTMSG ("Server not initialized ", lpserver);
  400. if (!ENTER_TRACE_API(lpserver)) { return ERROR_CAN_NOT_COMPLETE; }
  401. CREATE_SERVER_THREAD_IF_REQUIRED();
  402. //
  403. // return quickly if no output will be generated;
  404. //
  405. if (dwFlags & TRACE_USE_MASK) {
  406. if (!(*(lpserver->TS_FlagsCache + dwTraceID) & (dwFlags & 0xffff0000))) {
  407. return 0;
  408. }
  409. }
  410. else {
  411. if (!*(lpserver->TS_FlagsCache + dwTraceID)) { return 0; }
  412. }
  413. TRACE_ACQUIRE_READLOCK(lpserver);
  414. lpclient = lpserver->TS_ClientTable[dwTraceID];
  415. if (lpclient == NULL) {
  416. TRACE_RELEASE_READLOCK(lpserver);
  417. return 0;
  418. }
  419. TRACE_ACQUIRE_READLOCK(lpclient);
  420. if (TRACE_CLIENT_IS_DISABLED(lpclient)) {
  421. TRACE_RELEASE_READLOCK(lpclient);
  422. TRACE_RELEASE_READLOCK(lpserver);
  423. return 0;
  424. }
  425. szBuffer = (PTCHAR) HeapAlloc(GetProcessHeap(), 0, DEF_PRINT_BUFSIZE);
  426. if (!szBuffer) {
  427. TRACE_RELEASE_READLOCK(lpclient);
  428. TRACE_RELEASE_READLOCK(lpserver);
  429. return ERROR_NOT_ENOUGH_MEMORY;
  430. }
  431. if (dwFlags & TRACE_NO_STDINFO) {
  432. lpszOutput = lpszString;
  433. }
  434. else {
  435. GetLocalTime(&st);
  436. if ((dwFlags & TRACE_USE_MSEC) == 0) {
  437. if (dwFlags & TRACE_USE_DATE) {
  438. wsprintf(
  439. szBuffer, TEXT("\r\n[%03d] %02u-%02u %02u:%02u:%02u: %s") ,
  440. GetCurrentThreadId(), st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,
  441. lpszString
  442. );
  443. }
  444. else {
  445. wsprintf(
  446. szBuffer, TEXT("\r\n[%03d] %02u:%02u:%02u: %s"),
  447. GetCurrentThreadId(), st.wHour, st.wMinute, st.wSecond,
  448. lpszString
  449. );
  450. }
  451. }
  452. else {
  453. if (dwFlags & TRACE_USE_DATE) {
  454. wsprintf(
  455. szBuffer, TEXT("\r\n[%03d] %02u-%02u %02u:%02u:%02u:%03u: %s") ,
  456. GetCurrentThreadId(), st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,
  457. st.wMilliseconds, lpszString
  458. );
  459. }
  460. else {
  461. wsprintf(
  462. szBuffer, TEXT("\r\n[%03d] %02u:%02u:%02u:%03u: %s"),
  463. GetCurrentThreadId(), st.wHour, st.wMinute, st.wSecond,
  464. st.wMilliseconds, lpszString
  465. );
  466. }
  467. }
  468. lpszOutput = szBuffer;
  469. }
  470. dwSize = TraceWriteOutput(lpserver, lpclient, dwFlags, lpszOutput);
  471. HeapFree(GetProcessHeap(), 0, szBuffer);
  472. TRACE_RELEASE_READLOCK(lpclient);
  473. TRACE_RELEASE_READLOCK(lpserver);
  474. return dwSize;
  475. }
  476. DWORD
  477. APIENTRY
  478. TraceDumpEx(
  479. IN DWORD dwTraceID,
  480. IN DWORD dwFlags,
  481. IN LPBYTE lpbBytes,
  482. IN DWORD dwByteCount,
  483. IN DWORD dwGroupSize,
  484. IN BOOL bAddressPrefix,
  485. IN LPCTSTR lpszPrefix
  486. ) {
  487. SYSTEMTIME st;
  488. DWORD dwThread;
  489. LPTRACE_SERVER lpserver;
  490. LPTRACE_CLIENT lpclient;
  491. DWORD dwLine, dwBytesOutput;
  492. TCHAR szPrefix[MAX_CLIENTNAME_LENGTH + 48] = TEXT("");
  493. BYTE szBuffer[BYTES_PER_DUMPLINE];
  494. if (dwTraceID == INVALID_TRACEID ||
  495. dwTraceID >= MAX_CLIENT_COUNT ||
  496. lpbBytes == NULL ||
  497. dwByteCount == 0 ||
  498. (dwGroupSize != 1 && dwGroupSize != 2 && dwGroupSize != 4)
  499. ) {
  500. return ERROR_INVALID_PARAMETER;
  501. }
  502. lpserver = GET_TRACE_SERVER_NO_INIT ();
  503. ASSERTMSG ("Server not initialized ", lpserver);
  504. if (!ENTER_TRACE_API(lpserver)) { return ERROR_CAN_NOT_COMPLETE; }
  505. CREATE_SERVER_THREAD_IF_REQUIRED();
  506. //
  507. // return quickly if no output will be generated;
  508. //
  509. if (dwFlags & TRACE_USE_MASK) {
  510. if (!(*(lpserver->TS_FlagsCache + dwTraceID) & (dwFlags & 0xffff0000))) {
  511. return 0;
  512. }
  513. }
  514. else {
  515. if (!*(lpserver->TS_FlagsCache + dwTraceID)) { return 0; }
  516. }
  517. TRACE_ACQUIRE_READLOCK(lpserver);
  518. lpclient = lpserver->TS_ClientTable[dwTraceID];
  519. if (lpclient == NULL) {
  520. TRACE_RELEASE_READLOCK(lpserver);
  521. return 0;
  522. }
  523. TRACE_ACQUIRE_READLOCK(lpclient);
  524. if (TRACE_CLIENT_IS_DISABLED(lpclient)) {
  525. TRACE_RELEASE_READLOCK(lpclient);
  526. TRACE_RELEASE_READLOCK(lpserver);
  527. return 0;
  528. }
  529. dwBytesOutput = 0;
  530. if ((dwFlags & TRACE_NO_STDINFO) == 0) {
  531. GetLocalTime(&st);
  532. if ((dwFlags & TRACE_USE_MSEC) == 0) {
  533. wsprintf(
  534. szPrefix,
  535. TEXT("[%03d] %02u:%02u:%02u: "),
  536. GetCurrentThreadId(), st.wHour, st.wMinute, st.wSecond
  537. );
  538. }
  539. else {
  540. wsprintf(
  541. szPrefix,
  542. TEXT("[%03d] %02u:%02u:%02u:%03u: "),
  543. GetCurrentThreadId(), st.wHour, st.wMinute, st.wSecond,
  544. st.wMilliseconds
  545. );
  546. }
  547. }
  548. if (lpszPrefix != NULL) {
  549. lstrcat(szPrefix, lpszPrefix);
  550. }
  551. //
  552. // see if the start of the byte buffer is not aligned correctly
  553. // on a DWORD boundary
  554. //
  555. if ((ULONG_PTR)lpbBytes & (dwGroupSize - 1)) {
  556. DWORD dwPad;
  557. //
  558. // it is, so first dump the leading bytes:
  559. // get size of misalignment, and make certain
  560. // misalignment size isn't greater than total size
  561. //
  562. dwPad = (DWORD) ((ULONG_PTR)lpbBytes & (dwGroupSize - 1));
  563. dwPad = (dwPad > dwByteCount) ? dwByteCount : dwPad;
  564. dwLine = BYTES_PER_DUMPLINE;
  565. //
  566. // copy the misaligned bytes into the buffer
  567. //
  568. ZeroMemory(szBuffer, dwLine);
  569. CopyMemory(szBuffer + (dwLine - dwPad), lpbBytes, dwPad);
  570. //
  571. // now dump the line, but give the helper function a pointer
  572. // to the byte buffer passed in as an argument
  573. // to print as the prefix (actually, give it the place
  574. // in the real byte buffer that it would be dumping from
  575. // if things weren't misaligned
  576. //
  577. dwBytesOutput +=
  578. TraceDumpLine(
  579. lpserver, lpclient, dwFlags, szBuffer, dwLine, dwGroupSize,
  580. bAddressPrefix,
  581. (LPBYTE) ((ULONG_PTR)lpbBytes - (dwLine - dwPad)), szPrefix
  582. );
  583. (ULONG_PTR)lpbBytes += dwPad;
  584. dwByteCount -= dwPad;
  585. }
  586. //
  587. // now loop through until we can't print out any more
  588. //
  589. dwLine = BYTES_PER_DUMPLINE;
  590. while (dwByteCount > 0) {
  591. //
  592. // there is a line or more left in the buffer
  593. // no special processing needed
  594. //
  595. if (dwByteCount >= BYTES_PER_DUMPLINE) {
  596. dwBytesOutput +=
  597. TraceDumpLine(
  598. lpserver, lpclient, dwFlags, lpbBytes, dwLine, dwGroupSize,
  599. bAddressPrefix, lpbBytes, szPrefix
  600. );
  601. lpbBytes += dwLine;
  602. dwByteCount -= dwLine;
  603. }
  604. else {
  605. //
  606. // for the last line, copy the stuff to a buffer and then
  607. // print that buffer's contents, passing the argument buffer
  608. // as the address to use as a prefix
  609. //
  610. ZeroMemory(szBuffer, dwLine);
  611. CopyMemory(szBuffer, lpbBytes, dwByteCount);
  612. dwBytesOutput +=
  613. TraceDumpLine(
  614. lpserver, lpclient, dwFlags, szBuffer, dwLine,
  615. dwGroupSize, bAddressPrefix, lpbBytes, szPrefix
  616. );
  617. lpbBytes += dwLine;
  618. dwByteCount = 0;
  619. }
  620. }
  621. TRACE_RELEASE_READLOCK(lpclient);
  622. TRACE_RELEASE_READLOCK(lpserver);
  623. return dwBytesOutput;
  624. }