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.

908 lines
18 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. util.c - Generic Debugger Extension Utilities
  5. Abstract:
  6. Taken from AliD's ndiskd(ndiskd.c).
  7. Revision History:
  8. Who When What
  9. -------- -------- ----------------------------------------------
  10. josephj 03-30-98 Created (taken fron AliD's ndiskd (ndiskd.c).
  11. Notes:
  12. --*/
  13. #include "precomp.h"
  14. #include "common.h"
  15. WINDBG_EXTENSION_APIS ExtensionApis;
  16. EXT_API_VERSION ApiVersion = { 5, 0, EXT_API_VERSION_NUMBER, 0 };
  17. #define ERRPRT dprintf
  18. #define NL 1
  19. #define NONL 0
  20. USHORT SavedMajorVersion;
  21. USHORT SavedMinorVersion;
  22. BOOL ChkTarget; // is debuggee a CHK build?
  23. /*
  24. * Print out an optional message, an ANSI_STRING, and maybe a new-line
  25. */
  26. BOOL
  27. PrintStringA( IN LPSTR msg OPTIONAL, IN PANSI_STRING pStr, IN BOOL nl )
  28. {
  29. PCHAR StringData;
  30. ULONG BytesRead;
  31. if( msg )
  32. dprintf( msg );
  33. if( pStr->Length == 0 ) {
  34. if( nl )
  35. dprintf( "\n" );
  36. return TRUE;
  37. }
  38. StringData = (PCHAR)LocalAlloc( LPTR, pStr->Length + 1 );
  39. if( StringData == NULL ) {
  40. ERRPRT( "Out of memory!\n" );
  41. return FALSE;
  42. }
  43. ReadMemory((ULONG) pStr->Buffer,
  44. StringData,
  45. pStr->Length,
  46. &BytesRead );
  47. if ( BytesRead ) {
  48. StringData[ pStr->Length ] = '\0';
  49. dprintf("%s%s", StringData, nl ? "\n" : "" );
  50. }
  51. LocalFree((HLOCAL)StringData);
  52. return BytesRead;
  53. }
  54. /*
  55. * Get 'size' bytes from the debuggee program at 'dwAddress' and place it
  56. * in our address space at 'ptr'. Use 'type' in an error printout if necessary
  57. */
  58. BOOL
  59. GetData( IN LPVOID ptr, IN DWORD dwAddress, IN ULONG size, IN PCSTR type )
  60. {
  61. BOOL b;
  62. ULONG BytesRead;
  63. ULONG count = size;
  64. while( size > 0 ) {
  65. if (count >= 3000)
  66. count = 3000;
  67. b = ReadMemory((ULONG) dwAddress, ptr, count, &BytesRead );
  68. if (!b || BytesRead != count ) {
  69. ERRPRT( "Unable to read %u bytes at %X, for %s\n", size, dwAddress, type );
  70. return FALSE;
  71. }
  72. dwAddress += count;
  73. size -= count;
  74. ptr = (LPVOID)((ULONG)ptr + count);
  75. }
  76. return TRUE;
  77. }
  78. /*
  79. * Follow a LIST_ENTRY list beginning with a head at dwListHeadAddr in the debugee's
  80. * address space. For each element in the list, print out the pointer value at 'offset'
  81. */
  82. BOOL
  83. PrintListEntryList( IN DWORD dwListHeadAddr, IN LONG offset )
  84. {
  85. LIST_ENTRY ListEntry;
  86. ULONG i=0;
  87. BOOL retval = TRUE;
  88. ULONG count = 20;
  89. if( !GetData( &ListEntry, dwListHeadAddr, sizeof( ListEntry ), "LIST_ENTRY" ) )
  90. return FALSE;
  91. while( count-- ) {
  92. if( (DWORD)ListEntry.Flink == dwListHeadAddr || (DWORD)ListEntry.Flink == 0 )
  93. break;
  94. if( !GetData( &ListEntry, (DWORD)ListEntry.Flink, sizeof( ListEntry ), "ListEntry" ) ) {
  95. retval = FALSE;
  96. break;
  97. }
  98. dprintf( "%16X%s", (LONG)ListEntry.Flink + offset, (i && !(i&3)) ? "\n" : "" );
  99. i++;
  100. }
  101. if( count == 0 && (DWORD)ListEntry.Flink != dwListHeadAddr && ListEntry.Flink ) {
  102. dprintf( "\nTruncated list dump\n" );
  103. } else if( ! ( i && !(i&3) ) ) {
  104. dprintf( "\n" );
  105. }
  106. return retval;
  107. }
  108. /*
  109. * Print out a single HEX character
  110. */
  111. VOID
  112. PrintHexChar( IN UCHAR c )
  113. {
  114. dprintf( "%c%c", "0123456789abcdef"[ (c>>4)&0xf ], "0123456789abcdef"[ c&0xf ] );
  115. }
  116. /*
  117. * Print out 'buf' of 'cbuf' bytes as HEX characters
  118. */
  119. VOID
  120. PrintHexBuf( IN PUCHAR buf, IN ULONG cbuf )
  121. {
  122. while( cbuf-- ) {
  123. PrintHexChar( *buf++ );
  124. dprintf( " " );
  125. }
  126. }
  127. /*
  128. * Fetch the null terminated UNICODE string at dwAddress into buf
  129. */
  130. BOOL
  131. GetString( IN DWORD dwAddress, IN LPWSTR buf, IN ULONG MaxChars )
  132. {
  133. do {
  134. if( !GetData( buf, dwAddress, sizeof( *buf ), "Character" ) )
  135. return FALSE;
  136. dwAddress += sizeof( *buf );
  137. } while( --MaxChars && *buf++ != '\0' );
  138. return TRUE;
  139. }
  140. char *mystrtok ( char *string, char * control )
  141. {
  142. static UCHAR *str;
  143. char *p, *s;
  144. if( string )
  145. str = string;
  146. if( str == NULL || *str == '\0' )
  147. return NULL;
  148. //
  149. // Skip leading delimiters...
  150. //
  151. for( ; *str; str++ ) {
  152. for( s=control; *s; s++ ) {
  153. if( *str == *s )
  154. break;
  155. }
  156. if( *s == '\0' )
  157. break;
  158. }
  159. //
  160. // Was it was all delimiters?
  161. //
  162. if( *str == '\0' ) {
  163. str = NULL;
  164. return NULL;
  165. }
  166. //
  167. // We've got a string, terminate it at first delimeter
  168. //
  169. for( p = str+1; *p; p++ ) {
  170. for( s = control; *s; s++ ) {
  171. if( *p == *s ) {
  172. s = str;
  173. *p = '\0';
  174. str = p+1;
  175. return s;
  176. }
  177. }
  178. }
  179. //
  180. // We've got a string that ends with the NULL
  181. //
  182. s = str;
  183. str = NULL;
  184. return s;
  185. }
  186. VOID
  187. WinDbgExtensionDllInit(
  188. PWINDBG_EXTENSION_APIS lpExtensionApis,
  189. USHORT MajorVersion,
  190. USHORT MinorVersion
  191. )
  192. {
  193. ExtensionApis = *lpExtensionApis;
  194. g_pfnDbgPrintf = dprintf;
  195. SavedMajorVersion = MajorVersion;
  196. SavedMinorVersion = MinorVersion;
  197. ChkTarget = SavedMajorVersion == 0x0c ? TRUE : FALSE;
  198. }
  199. DECLARE_API( version )
  200. {
  201. #if DBG
  202. PCSTR kind = "Checked";
  203. #else
  204. PCSTR kind = "Free";
  205. #endif
  206. dprintf(
  207. "%s IPATM Extension dll for Build %d debugging %s kernel for Build %d\n",
  208. kind,
  209. VER_PRODUCTBUILD,
  210. SavedMajorVersion == 0x0c ? "Checked" : "Free",
  211. SavedMinorVersion
  212. );
  213. }
  214. VOID
  215. CheckVersion(
  216. VOID
  217. )
  218. {
  219. //
  220. // for now don't bother to version check
  221. //
  222. return;
  223. #if DBG
  224. if ((SavedMajorVersion != 0x0c) || (SavedMinorVersion != VER_PRODUCTBUILD)) {
  225. dprintf("\r\n*** Extension DLL(%d Checked) does not match target system(%d %s)\r\n\r\n",
  226. VER_PRODUCTBUILD, SavedMinorVersion, (SavedMajorVersion==0x0f) ? "Free" : "Checked" );
  227. }
  228. #else
  229. if ((SavedMajorVersion != 0x0f) || (SavedMinorVersion != VER_PRODUCTBUILD)) {
  230. dprintf("\r\n*** Extension DLL(%d Free) does not match target system(%d %s)\r\n\r\n",
  231. VER_PRODUCTBUILD, SavedMinorVersion, (SavedMajorVersion==0x0f) ? "Free" : "Checked" );
  232. }
  233. #endif
  234. }
  235. LPEXT_API_VERSION
  236. ExtensionApiVersion(
  237. VOID
  238. )
  239. {
  240. return &ApiVersion;
  241. }
  242. //
  243. // VOID
  244. // PrintName(
  245. // PUNICODE_STRING Name
  246. // );
  247. // print a unicode string
  248. // Note: the Buffer field in unicode string is unmapped
  249. //
  250. VOID
  251. PrintName(
  252. PUNICODE_STRING Name
  253. )
  254. {
  255. USHORT i;
  256. WCHAR ubuf[256];
  257. UCHAR abuf[256];
  258. if (!GetString((DWORD)Name->Buffer, ubuf, (ULONG)Name->Length))
  259. {
  260. return;
  261. }
  262. for (i = 0; i < Name->Length/2; i++)
  263. {
  264. abuf[i] = (UCHAR)ubuf[i];
  265. }
  266. abuf[i] = 0;
  267. dprintf("%s",abuf);
  268. }
  269. MYPWINDBG_OUTPUT_ROUTINE g_pfnDbgPrintf = NULL;
  270. bool
  271. dbgextReadMemory(
  272. UINT_PTR uOffset,
  273. void * pvBuffer,
  274. UINT cb,
  275. char *pszDescription
  276. )
  277. {
  278. UINT cbBytesRead=0;
  279. bool fRet = ReadMemory(
  280. uOffset,
  281. pvBuffer,
  282. cb,
  283. &cbBytesRead
  284. );
  285. if (!fRet || cbBytesRead != cb)
  286. {
  287. ERRPRT("Read failed: 0x%X(%s, %u bytes)\n",uOffset,pszDescription,cb);
  288. fRet = FALSE;
  289. }
  290. return fRet;
  291. }
  292. bool
  293. dbgextWriteMemory(
  294. UINT_PTR uOffset,
  295. void * pvBuffer,
  296. UINT cb,
  297. char *pszDescription
  298. )
  299. {
  300. UINT cbBytesWritten=0;
  301. bool fRet = WriteMemory(
  302. uOffset,
  303. pvBuffer,
  304. cb,
  305. &cbBytesWritten
  306. );
  307. if (!fRet || cbBytesWritten != cb)
  308. {
  309. ERRPRT("Write failed: 0x%X(%s, %u bytes)\n",uOffset,pszDescription,cb);
  310. fRet = FALSE;
  311. }
  312. return 0;
  313. }
  314. bool
  315. dbgextReadUINT_PTR(
  316. UINT_PTR uOffset,
  317. UINT_PTR *pu,
  318. char *pszDescription
  319. )
  320. {
  321. UINT cbBytesRead=0;
  322. bool fRet = ReadMemory(
  323. uOffset,
  324. pu,
  325. sizeof(*pu),
  326. &cbBytesRead
  327. );
  328. if (!fRet || cbBytesRead != sizeof(*pu))
  329. {
  330. ERRPRT("Read failed: 0x%X(%s, UINT_PTR)\n",uOffset,pszDescription);
  331. fRet = FALSE;
  332. }
  333. return fRet;
  334. }
  335. bool
  336. dbgextWriteUINT_PTR(
  337. UINT_PTR uOffset,
  338. UINT_PTR u,
  339. char *pszDescription
  340. )
  341. {
  342. UINT cbBytesWritten=0;
  343. bool fRet = WriteMemory(
  344. uOffset,
  345. &u,
  346. sizeof(uOffset),
  347. &cbBytesWritten
  348. );
  349. if (!fRet || cbBytesWritten != sizeof(u))
  350. {
  351. ERRPRT("Write failed: 0x%X(%s, UINT_PTR)\n",uOffset,pszDescription);
  352. fRet = FALSE;
  353. }
  354. return fRet;
  355. }
  356. UINT_PTR
  357. dbgextGetExpression(
  358. const char *pcszExpression
  359. )
  360. {
  361. UINT_PTR uRet = GetExpression(pcszExpression);
  362. //
  363. // At such a point we use this for something besides pointers,
  364. // we will remove the check below.
  365. //
  366. if (!uRet)
  367. {
  368. ERRPRT("Eval failed: \"%s\"\n", pcszExpression);
  369. }
  370. return uRet;
  371. }
  372. void
  373. DumpObjects(TYPE_INFO *pType, UINT_PTR uAddr, UINT cObjects, UINT uFlags)
  374. {
  375. //
  376. // Print object's type and size
  377. //
  378. dprintf(
  379. "%s@0x%X (%lu Bytes)\n",
  380. pType->szName,
  381. uAddr,
  382. pType->cbSize
  383. );
  384. DumpMemory(
  385. uAddr,
  386. pType->cbSize,
  387. 0,
  388. pType->szName
  389. );
  390. //
  391. // Dump bytes...
  392. //
  393. return;
  394. }
  395. BYTE rgbScratchBuffer[100000];
  396. bool
  397. DumpMemory(
  398. UINT_PTR uAddr,
  399. UINT cb,
  400. UINT uFlags,
  401. const char *pszDescription
  402. )
  403. {
  404. bool fTruncated = FALSE;
  405. bool fRet = FALSE;
  406. UINT cbLeft = cb;
  407. char *pbSrc = rgbScratchBuffer;
  408. if (cbLeft>1024)
  409. {
  410. cbLeft = 1024;
  411. fTruncated = TRUE;
  412. }
  413. fRet = dbgextReadMemory(
  414. uAddr,
  415. rgbScratchBuffer,
  416. cbLeft,
  417. (char*)pszDescription
  418. );
  419. if (!fRet) goto end;
  420. #define ROWSIZE 16 // bytes
  421. //
  422. // Dump away...
  423. //
  424. while (cbLeft)
  425. {
  426. char rgTmp_dwords[ROWSIZE];
  427. char rgTmp_bytes[ROWSIZE];
  428. char *pb=NULL;
  429. UINT cbRow = ROWSIZE;
  430. if (cbRow > cbLeft)
  431. {
  432. cbRow = cbLeft;
  433. }
  434. memset(rgTmp_dwords, 0xff, sizeof(rgTmp_dwords));
  435. memset(rgTmp_bytes, ' ', sizeof(rgTmp_bytes));
  436. memcpy(rgTmp_dwords, pbSrc, cbRow);
  437. memcpy(rgTmp_bytes, pbSrc, cbRow);
  438. // sanitize bytes
  439. for (pb=rgTmp_bytes; pb<(rgTmp_bytes+sizeof(rgTmp_bytes)); pb++)
  440. {
  441. char c = *pb;
  442. if (c>=0x20 && c<0x7f) // isprint is too permissive.
  443. {
  444. if (*pb=='\t')
  445. {
  446. *pb=' ';
  447. }
  448. }
  449. else
  450. {
  451. *pb='.';
  452. }
  453. }
  454. dprintf(
  455. " %08lx: %08lx %08lx %08lx %08lx |%4.4s|%4.4s|%4.4s|%4.4s|\n",
  456. uAddr,
  457. ((DWORD*) rgTmp_dwords)[0],
  458. ((DWORD*) rgTmp_dwords)[1],
  459. ((DWORD*) rgTmp_dwords)[2],
  460. ((DWORD*) rgTmp_dwords)[3],
  461. #if 1
  462. rgTmp_bytes+0,
  463. rgTmp_bytes+4,
  464. rgTmp_bytes+8,
  465. rgTmp_bytes+12
  466. #else
  467. "aaaabbbbccccdddd",
  468. "bbbb",
  469. "cccc",
  470. "dddd"
  471. #endif
  472. );
  473. cbLeft -= cbRow;
  474. pbSrc += cbRow;
  475. uAddr += cbRow;
  476. }
  477. #if 0
  478. 0x00000000: 00000000 00000000 00000000 00000000 |xxxx|xxxx|xxxx|xxxx|
  479. 0x00000000: 00000000 00000000 00000000 00000000 |xxxx|xxxx|xxxx|xxxx|
  480. 0x00000000: 00000000 00000000 00000000 00000000 |xxxx|xxxx|xxxx|xxxx|
  481. #endif //
  482. end:
  483. return fRet;
  484. }
  485. bool
  486. MatchPrefix(const char *szPattern, const char *szString)
  487. {
  488. BOOL fRet = FALSE;
  489. ULONG uP = lstrlenA(szPattern);
  490. ULONG uS = lstrlenA(szString);
  491. if (uP<=uS)
  492. {
  493. fRet = (_memicmp(szPattern, szString, uP)==0);
  494. }
  495. return fRet;
  496. }
  497. bool
  498. MatchSuffix(const char *szPattern, const char *szString)
  499. {
  500. BOOL fRet = FALSE;
  501. ULONG uP = lstrlenA(szPattern);
  502. ULONG uS = lstrlenA(szString);
  503. if (uP<=uS)
  504. {
  505. szString += (uS-uP);
  506. fRet = (_memicmp(szPattern, szString, uP)==0);
  507. }
  508. return fRet;
  509. }
  510. bool
  511. MatchSubstring(const char *szPattern, const char *szString)
  512. {
  513. BOOL fRet = FALSE;
  514. ULONG uP = lstrlenA(szPattern);
  515. ULONG uS = lstrlenA(szString);
  516. if (uP<=uS)
  517. {
  518. const char *szLast = szString + (uS-uP);
  519. do
  520. {
  521. fRet = (_memicmp(szPattern, szString, uP)==0);
  522. } while (!fRet && szString++ < szLast);
  523. }
  524. return fRet;
  525. }
  526. bool
  527. MatchExactly(const char *szPattern, const char *szString)
  528. {
  529. BOOL fRet = FALSE;
  530. ULONG uP = lstrlenA(szPattern);
  531. ULONG uS = lstrlenA(szString);
  532. if (uP==uS)
  533. {
  534. fRet = (_memicmp(szPattern, szString, uP)==0);
  535. }
  536. return fRet;
  537. }
  538. bool
  539. MatchAlways(const char *szPattern, const char *szString)
  540. {
  541. return TRUE;
  542. }
  543. void
  544. DumpBitFields(
  545. ULONG Flags,
  546. BITFIELD_INFO rgBitFieldInfo[]
  547. )
  548. {
  549. BITFIELD_INFO *pbf = rgBitFieldInfo;
  550. for(;pbf->szName; pbf++)
  551. {
  552. if ((Flags & pbf->Mask) == pbf->Value)
  553. {
  554. MyDbgPrintf(" %s", pbf->szName);
  555. }
  556. }
  557. }
  558. void
  559. DumpStructure(
  560. TYPE_INFO *pType,
  561. UINT_PTR uAddr,
  562. char *szFieldSpec,
  563. UINT uFlags
  564. )
  565. {
  566. //
  567. // Determine field comparision function ...
  568. //
  569. PFNMATCHINGFUNCTION pfnMatchingFunction = MatchAlways;
  570. //
  571. // Pick a selection function ...
  572. //
  573. if (szFieldSpec)
  574. {
  575. if (uFlags & fMATCH_SUBSTRING)
  576. {
  577. pfnMatchingFunction = MatchSubstring;
  578. }
  579. else if (uFlags & fMATCH_SUFFIX)
  580. {
  581. pfnMatchingFunction = MatchSuffix;
  582. }
  583. else if (uFlags & fMATCH_PREFIX)
  584. {
  585. pfnMatchingFunction = MatchPrefix;
  586. }
  587. else
  588. {
  589. pfnMatchingFunction = MatchExactly;
  590. }
  591. }
  592. //
  593. // Print object's type and size
  594. //
  595. dprintf(
  596. "%s@0x%X (%lu Bytes)\n",
  597. pType->szName,
  598. uAddr,
  599. pType->cbSize
  600. );
  601. //
  602. // Run through all the fields in this type, and if the entry is selected,
  603. // we will display it.
  604. //
  605. {
  606. STRUCT_FIELD_INFO *pField = pType->rgFields;
  607. for (;pField->szFieldName; pField++)
  608. {
  609. bool fMatch = !szFieldSpec
  610. || pfnMatchingFunction(szFieldSpec, pField->szFieldName);
  611. if (fMatch)
  612. {
  613. UINT_PTR uFieldAddr = uAddr + pField->uFieldOffset;
  614. // special-case small fields...
  615. if (pField->uFieldSize<=sizeof(ULONG_PTR))
  616. {
  617. ULONG_PTR Buf=0;
  618. BOOL fRet = dbgextReadMemory(
  619. uFieldAddr,
  620. &Buf,
  621. pField->uFieldSize,
  622. (char*)pField->szFieldName
  623. );
  624. if (fRet)
  625. {
  626. // print it as a hex number
  627. MyDbgPrintf(
  628. "\n%s\t[%lx,%lx]: 0x%lx",
  629. pField->szFieldName,
  630. pField->uFieldOffset,
  631. pField->uFieldSize,
  632. Buf
  633. );
  634. //
  635. // If it's an embedded object and it's a bitfield,
  636. // print the bitfields...
  637. //
  638. if ( FIELD_IS_EMBEDDED_TYPE(pField)
  639. && TYPEISBITFIELD(pField->pBaseType) )
  640. {
  641. DumpBitFields(
  642. Buf,
  643. pField->pBaseType->rgBitFieldInfo
  644. );
  645. }
  646. MyDbgPrintf("\n");
  647. }
  648. continue;
  649. }
  650. #if 0
  651. MyDbgPrintf(
  652. "%s\ndc 0x%08lx L %03lx %s\n",
  653. pField->szSourceText,
  654. uFieldAddr,
  655. pField->uFieldSize,
  656. pField->szFieldName
  657. );
  658. #else // 1
  659. MyDbgPrintf(
  660. "\n%s\t[%lx,%lx]\n",
  661. pField->szFieldName,
  662. pField->uFieldOffset,
  663. pField->uFieldSize
  664. );
  665. #endif // 1
  666. // if (szFieldSpec)
  667. {
  668. #if 0
  669. MyDumpObjects(
  670. pCmd,
  671. pgi->pBaseType,
  672. pgi->uAddr,
  673. pgi->cbSize,
  674. pgi->szName
  675. );
  676. #endif // 0
  677. DumpMemory(
  678. uFieldAddr,
  679. pField->uFieldSize,
  680. 0,
  681. pField->szFieldName
  682. );
  683. }
  684. }
  685. }
  686. }
  687. return;
  688. }
  689. DECLARE_API( help )
  690. {
  691. do_help(args);
  692. }
  693. DECLARE_API( aac )
  694. {
  695. do_aac(args);
  696. }
  697. ULONG
  698. NodeFunc_DumpAddress (
  699. UINT_PTR uNodeAddr,
  700. UINT uIndex,
  701. void *pvContext
  702. )
  703. {
  704. MyDbgPrintf("[%lu] 0x%08lx\n", uIndex, uNodeAddr);
  705. return 0;
  706. }
  707. UINT
  708. WalkList(
  709. UINT_PTR uStartAddress,
  710. UINT uNextOffset,
  711. UINT uStartIndex,
  712. UINT uEndIndex,
  713. void *pvContext,
  714. PFNNODEFUNC pFunc,
  715. char *pszDescription
  716. )
  717. //
  718. // Visit each node in the list in turn,
  719. // reading just the next pointers. It calls pFunc for each list node
  720. // between uStartIndex and uEndIndex. It terminates under the first of
  721. // the following conditions:
  722. // * Null pointer
  723. // * ReadMemoryError
  724. // * Read past uEndIndex
  725. // * pFunc returns FALSE
  726. //
  727. {
  728. UINT uIndex = 0;
  729. UINT_PTR uAddress = uStartAddress;
  730. BOOL fRet = TRUE;
  731. UINT uRet = 0;
  732. //
  733. // First skip until we get to uStart Index
  734. //
  735. for (;fRet && uAddress && uIndex < uStartIndex; uIndex++)
  736. {
  737. fRet = dbgextReadUINT_PTR(
  738. uAddress+uNextOffset,
  739. &uAddress,
  740. pszDescription
  741. );
  742. }
  743. //
  744. // Now call pFunc with each node
  745. //
  746. for (;fRet && uAddress && uIndex <= uEndIndex; uIndex++)
  747. {
  748. uRet = pFunc(uAddress, uIndex, pvContext);
  749. fRet = dbgextReadUINT_PTR(
  750. uAddress+uNextOffset,
  751. &uAddress,
  752. pszDescription
  753. );
  754. }
  755. pFunc = NodeFunc_DumpAddress;
  756. return uRet;
  757. }