1 | /* $Id: DBGCNtCommands.cpp 105538 2024-07-30 07:00:02Z vboxsync $ */
|
---|
2 | /** @file
|
---|
3 | * DBGC - Debugger Console, Windows NT Related Commands.
|
---|
4 | */
|
---|
5 |
|
---|
6 | /*
|
---|
7 | * Copyright (C) 2024 Oracle and/or its affiliates.
|
---|
8 | *
|
---|
9 | * This file is part of VirtualBox base platform packages, as
|
---|
10 | * available from https://www.215389.xyz.
|
---|
11 | *
|
---|
12 | * This program is free software; you can redistribute it and/or
|
---|
13 | * modify it under the terms of the GNU General Public License
|
---|
14 | * as published by the Free Software Foundation, in version 3 of the
|
---|
15 | * License.
|
---|
16 | *
|
---|
17 | * This program is distributed in the hope that it will be useful, but
|
---|
18 | * WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
20 | * General Public License for more details.
|
---|
21 | *
|
---|
22 | * You should have received a copy of the GNU General Public License
|
---|
23 | * along with this program; if not, see <https://www.gnu.org/licenses>.
|
---|
24 | *
|
---|
25 | * SPDX-License-Identifier: GPL-3.0-only
|
---|
26 | */
|
---|
27 |
|
---|
28 |
|
---|
29 | /*********************************************************************************************************************************
|
---|
30 | * Header Files *
|
---|
31 | *********************************************************************************************************************************/
|
---|
32 | #define LOG_GROUP LOG_GROUP_DBGC
|
---|
33 | #include <VBox/dbg.h>
|
---|
34 | #include <VBox/vmm/dbgf.h>
|
---|
35 | #include <VBox/param.h>
|
---|
36 | #include <iprt/errcore.h>
|
---|
37 | #include <VBox/log.h>
|
---|
38 |
|
---|
39 | #include <iprt/assert.h>
|
---|
40 | #include <iprt/ctype.h>
|
---|
41 | #include <iprt/dir.h>
|
---|
42 | #include <iprt/env.h>
|
---|
43 | #include <iprt/ldr.h>
|
---|
44 | #include <iprt/mem.h>
|
---|
45 | #include <iprt/path.h>
|
---|
46 | #include <iprt/string.h>
|
---|
47 |
|
---|
48 | #include "DBGCInternal.h"
|
---|
49 |
|
---|
50 |
|
---|
51 | /*********************************************************************************************************************************
|
---|
52 | * Structures and Typedefs *
|
---|
53 | *********************************************************************************************************************************/
|
---|
54 | /**
|
---|
55 | * 64-bit _RTL_BALANCED_NODE
|
---|
56 | */
|
---|
57 | typedef struct NT_RTL_BALANCED_NODE64
|
---|
58 | {
|
---|
59 | uint64_t Left;
|
---|
60 | uint64_t Right;
|
---|
61 |
|
---|
62 | /**
|
---|
63 | * Pointer to the parent node and flags in the least significant bits.
|
---|
64 | *
|
---|
65 | * RB: 1 bit: Set if RED, clear if BLACK.
|
---|
66 | * AVL: 2 bits: Balance something.
|
---|
67 | */
|
---|
68 | uint64_t ParentAndFlags;
|
---|
69 | } NT_RTL_BALANCED_NODE64;
|
---|
70 |
|
---|
71 | /**
|
---|
72 | * 64-bit _RTL_RB_TREE
|
---|
73 | */
|
---|
74 | typedef struct NT_RTL_RB_TREE64
|
---|
75 | {
|
---|
76 | uint64_t Root; /**< Address of the root node (NT_RTL_BALANCED_NODE64). */
|
---|
77 | uint64_t Min; /**< Address of the left most node (NT_RTL_BALANCED_NODE64). */
|
---|
78 | } NT_RTL_RB_TREE64;
|
---|
79 |
|
---|
80 |
|
---|
81 | /** Initializes a DBGC variable with a GC flat pointer. */
|
---|
82 | DECLINLINE(PDBGCVAR) dbgCmdNtVarFromGCFlatPtr(PDBGCVAR pVar, RTGCPTR GCFlat)
|
---|
83 | {
|
---|
84 | pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
|
---|
85 | pVar->u.GCFlat = GCFlat;
|
---|
86 |
|
---|
87 | pVar->enmRangeType = DBGCVAR_RANGE_NONE;
|
---|
88 | pVar->u64Range = 0;
|
---|
89 | pVar->pDesc = NULL;
|
---|
90 | pVar->pNext = NULL;
|
---|
91 | return pVar;
|
---|
92 | }
|
---|
93 |
|
---|
94 |
|
---|
95 | /** Worker for dbgcCmdNtRbTree. */
|
---|
96 | template<typename TreeType = NT_RTL_RB_TREE64, typename NodeType = NT_RTL_BALANCED_NODE64, typename PtrType = uint64_t>
|
---|
97 | int dbgCmdNtRbTreeWorker(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PCDBGCVAR pRootAddr)
|
---|
98 | {
|
---|
99 | PtrType const fAlignMask = ~(PtrType)(sizeof(PtrType) - 1);
|
---|
100 | PtrType const fAlignInfoMask = (PtrType)(sizeof(PtrType) - 1);
|
---|
101 |
|
---|
102 | /*
|
---|
103 | * Read the root structure.
|
---|
104 | */
|
---|
105 | TreeType Root;
|
---|
106 | int rc = DBGCCmdHlpMemRead(pCmdHlp, &Root, sizeof(Root), pRootAddr, NULL /*cbRead*/);
|
---|
107 | if (RT_FAILURE(rc))
|
---|
108 | return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "Failed to read tree structure");
|
---|
109 |
|
---|
110 | DBGCCmdHlpPrintf(pCmdHlp,
|
---|
111 | sizeof(PtrType) == sizeof(uint64_t)
|
---|
112 | ? "RB Root %DV: Root=%#016RX64 Min=%#016RX64\n"
|
---|
113 | : "RB Root %DV: Root=%#010RX32 Min=%#010RX32\n",
|
---|
114 | pRootAddr, Root.Root, Root.Min);
|
---|
115 | if ((Root.Root & fAlignMask) == 0 || (Root.Min & fAlignMask) == 0)
|
---|
116 | {
|
---|
117 | if ((Root.Root & fAlignMask) == 0 && (Root.Min & fAlignMask) == 0)
|
---|
118 | DBGCCmdHlpPrintf(pCmdHlp, "RB Root %DV: Empty\n", pRootAddr);
|
---|
119 | else
|
---|
120 | DBGCCmdHlpPrintf(pCmdHlp, "RB Root %DV: Bogus root state!\n", pRootAddr);
|
---|
121 | return VINF_SUCCESS;
|
---|
122 | }
|
---|
123 |
|
---|
124 | /*
|
---|
125 | * To properly validate and avoid unnecessary memory reads, we use a stack
|
---|
126 | * track tree traversal progress. The stack consists of a copy of the node
|
---|
127 | * and a boolean indicating whether or not the node has be processed.
|
---|
128 | */
|
---|
129 | struct
|
---|
130 | {
|
---|
131 | NodeType Node; /**< Copy of the node. */
|
---|
132 | PtrType Ptr; /**< The node address. */
|
---|
133 | uint8_t bState; /**< 0 for going down left tree, 1 for the right, and 2 for done. */
|
---|
134 | uint8_t cBlacks; /**< Number of black nodes thus far, including this one. */
|
---|
135 | } aStack[28];
|
---|
136 | uint32_t cStackEntries = 0;
|
---|
137 |
|
---|
138 | /*
|
---|
139 | * Push the root-node onto the stack.
|
---|
140 | */
|
---|
141 | PtrType Ptr = Root.Root & fAlignMask;
|
---|
142 | DBGCVAR Var;
|
---|
143 | rc = DBGCCmdHlpMemRead(pCmdHlp, &aStack[0].Node, sizeof(aStack[0].Node), dbgCmdNtVarFromGCFlatPtr(&Var, Ptr), NULL);
|
---|
144 | if (RT_FAILURE(rc))
|
---|
145 | return DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "Failed to read the root node!");
|
---|
146 | aStack[0].Ptr = Ptr;
|
---|
147 | aStack[0].bState = 0;
|
---|
148 | aStack[0].cBlacks = 1;
|
---|
149 | if (aStack[0].Node.ParentAndFlags & fAlignMask)
|
---|
150 | return DBGCCmdHlpFail(pCmdHlp, pCmd, "Root->Parent != NULL: %#RX64", (uint64_t)aStack[0].Node.ParentAndFlags);
|
---|
151 | if (aStack[0].Node.ParentAndFlags & 1)
|
---|
152 | return DBGCCmdHlpFail(pCmdHlp, pCmd, "Root node is RED! Parent=%#RX64", (uint64_t)aStack[0].Node.ParentAndFlags);
|
---|
153 | cStackEntries++;
|
---|
154 |
|
---|
155 | int rcRet = VINF_SUCCESS;
|
---|
156 | uint8_t cReqBlacks = 0; /**< Number of black nodes required in each path. Set when we reach the left most node. */
|
---|
157 | uint8_t cchMaxDepth = 32;
|
---|
158 | uint32_t idxNode = 0;
|
---|
159 | uint32_t cErrors = 0;
|
---|
160 | while (cStackEntries > 0)
|
---|
161 | {
|
---|
162 | uint32_t const idxCurEntry = cStackEntries - 1;
|
---|
163 | switch (aStack[idxCurEntry].bState)
|
---|
164 | {
|
---|
165 | case 0:
|
---|
166 | /*
|
---|
167 | * Descend into the left tree, if any.
|
---|
168 | */
|
---|
169 | if (aStack[idxCurEntry].Node.Left & fAlignInfoMask)
|
---|
170 | {
|
---|
171 | rcRet = DBGCCmdHlpFail(pCmdHlp, pCmd, "Misaligned Left pointer in node at %#RX64: %#RX64",
|
---|
172 | (uint64_t)aStack[idxCurEntry].Ptr, (uint64_t)aStack[idxCurEntry].Node.Left);
|
---|
173 | cErrors++;
|
---|
174 | }
|
---|
175 | Ptr = aStack[idxCurEntry].Node.Left & fAlignMask;
|
---|
176 | if (Ptr)
|
---|
177 | {
|
---|
178 | if (cStackEntries < RT_ELEMENTS(aStack))
|
---|
179 | {
|
---|
180 | rc = DBGCCmdHlpMemRead(pCmdHlp, &aStack[cStackEntries].Node, sizeof(aStack[cStackEntries].Node),
|
---|
181 | dbgCmdNtVarFromGCFlatPtr(&Var, Ptr), NULL);
|
---|
182 | if (RT_SUCCESS(rc))
|
---|
183 | {
|
---|
184 | if ((aStack[cStackEntries].Node.ParentAndFlags & fAlignMask) == aStack[idxCurEntry].Ptr)
|
---|
185 | {
|
---|
186 | aStack[idxCurEntry].bState = 1;
|
---|
187 |
|
---|
188 | aStack[cStackEntries].Ptr = Ptr;
|
---|
189 | aStack[cStackEntries].cBlacks = aStack[idxCurEntry].cBlacks
|
---|
190 | + !(aStack[cStackEntries].Node.ParentAndFlags & 1);
|
---|
191 | aStack[cStackEntries].bState = 0;
|
---|
192 | cStackEntries++;
|
---|
193 | continue;
|
---|
194 | }
|
---|
195 | rc = DBGCCmdHlpFail(pCmdHlp, pCmd,
|
---|
196 | "Left node of %#RX64 at #%RX64 has an incorrect parent value: %#RX64 (Left=%#RX64, Right=%#RX64)",
|
---|
197 | (uint64_t)aStack[idxCurEntry].Ptr, Ptr, aStack[cStackEntries].Node.ParentAndFlags,
|
---|
198 | aStack[cStackEntries].Node.Left, aStack[cStackEntries].Node.Right);
|
---|
199 | }
|
---|
200 | else
|
---|
201 | rcRet = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc,
|
---|
202 | "Failed to read the node left of %#RX64 at address %#RX64!",
|
---|
203 | (uint64_t)aStack[idxCurEntry].Ptr, Ptr);
|
---|
204 | }
|
---|
205 | else
|
---|
206 | rcRet = DBGCCmdHlpFail(pCmdHlp, pCmd, "Stack overflow descending to the left!");
|
---|
207 | cErrors++;
|
---|
208 | }
|
---|
209 | else if (idxNode != 0)
|
---|
210 | {
|
---|
211 | uint8_t const cBlacksForCur = aStack[idxCurEntry].cBlacks + 1;
|
---|
212 | if (cBlacksForCur != cReqBlacks)
|
---|
213 | {
|
---|
214 | rcRet = DBGCCmdHlpFail(pCmdHlp, pCmd, "Wrong black count to the left of %#RX64: %u, expected %u",
|
---|
215 | aStack[idxCurEntry].Ptr, cBlacksForCur, cReqBlacks);
|
---|
216 | cErrors++;
|
---|
217 | }
|
---|
218 | }
|
---|
219 | else
|
---|
220 | {
|
---|
221 | cReqBlacks = aStack[idxCurEntry].cBlacks + 1;
|
---|
222 | cchMaxDepth = RT_MIN(cReqBlacks * 4, RT_ELEMENTS(aStack) * 2 + 2);
|
---|
223 |
|
---|
224 | if (Root.Min != aStack[idxCurEntry].Ptr)
|
---|
225 | {
|
---|
226 | rcRet = DBGCCmdHlpFail(pCmdHlp, pCmd,
|
---|
227 | "Bogus Min node in tree anchor! Left most node is %#RU64, expected %#RU64",
|
---|
228 | aStack[idxCurEntry].Ptr, Root.Min);
|
---|
229 | cErrors++;
|
---|
230 | }
|
---|
231 | }
|
---|
232 | RT_FALL_THRU();
|
---|
233 |
|
---|
234 | case 1:
|
---|
235 | {
|
---|
236 | /*
|
---|
237 | * Process the current node.
|
---|
238 | */
|
---|
239 | #if 1
|
---|
240 | char szStack[72];
|
---|
241 | uint32_t cchStack = cchMaxDepth - idxCurEntry * 2;
|
---|
242 | //memset(szStack, ' ', cchStack);
|
---|
243 | for (uint32_t volatile x = 0; x < cchStack; x++)
|
---|
244 | szStack[x] = ' ';
|
---|
245 |
|
---|
246 | if (aStack[idxCurEntry].Node.Left & fAlignMask)
|
---|
247 | szStack[cchStack++] = aStack[idxCurEntry].Node.Right & fAlignMask ? '>' : '`';
|
---|
248 | else
|
---|
249 | szStack[cchStack++] = aStack[idxCurEntry].Node.Right & fAlignMask ? ',' : ' ';
|
---|
250 | szStack[cchStack++] = aStack[idxCurEntry].Node.ParentAndFlags & 1 ? 'r' : 'B';
|
---|
251 |
|
---|
252 | uint32_t volatile offLast = cchStack;
|
---|
253 | if (idxCurEntry > 0)
|
---|
254 | {
|
---|
255 | uint32_t idxTmp = idxCurEntry - 1;
|
---|
256 | uint8_t bPrev = aStack[idxTmp].bState;
|
---|
257 | while (idxTmp-- > 0)
|
---|
258 | {
|
---|
259 | cchStack += 2;
|
---|
260 | if (aStack[idxTmp].bState != bPrev)
|
---|
261 | {
|
---|
262 | bPrev = aStack[idxTmp].bState;
|
---|
263 | while (offLast < cchStack)
|
---|
264 | szStack[offLast++] = ' ';
|
---|
265 | //memset(&szStack[offLast], ' ', cchStack - offLast);
|
---|
266 | szStack[cchStack] = '|';
|
---|
267 | offLast = cchStack + 1;
|
---|
268 | }
|
---|
269 | }
|
---|
270 | }
|
---|
271 | szStack[offLast] = '\0';
|
---|
272 |
|
---|
273 | DBGCCmdHlpPrintf(pCmdHlp,
|
---|
274 | sizeof(PtrType) == sizeof(uint64_t)
|
---|
275 | ? "#%4u/%2u at %#018RX64: Left=%#018RX64 Right=%#018RX64 Parent=%#018RX64 %s %s\n"
|
---|
276 | : "#%4u/%2u at %#010RX32: Left=%#010RX32 Right=%#010RX32 Parent=%#010RX32 %s %s\n",
|
---|
277 | idxNode, cStackEntries, aStack[idxCurEntry].Ptr, aStack[idxCurEntry].Node.Left,
|
---|
278 | aStack[idxCurEntry].Node.Right, aStack[idxCurEntry].Node.ParentAndFlags & ~(PtrType)1,
|
---|
279 | aStack[idxCurEntry].Node.ParentAndFlags & 1 ? "Red " : "Black", szStack);
|
---|
280 | #else
|
---|
281 | DBGCCmdHlpPrintf(pCmdHlp,
|
---|
282 | sizeof(PtrType) == sizeof(uint64_t)
|
---|
283 | ? "#%4u/%2u at %#018RX64: Left=%#018RX64 Right=%#018RX64 Parent=%#018RX64 %s %*s%s\n"
|
---|
284 | : "#%4u/%2u at %#010RX32: Left=%#010RX32 Right=%#010RX32 Parent=%#010RX32 %s %*s%s\n",
|
---|
285 | idxNode, cStackEntries, aStack[idxCurEntry].Ptr, aStack[idxCurEntry].Node.Left,
|
---|
286 | aStack[idxCurEntry].Node.Right, aStack[idxCurEntry].Node.ParentAndFlags & ~(PtrType)1,
|
---|
287 | aStack[idxCurEntry].Node.ParentAndFlags & 1 ? "Red " : "Black",
|
---|
288 | (cMaxDepth - idxCurEntry) * 2,
|
---|
289 | aStack[idxCurEntry].Node.Left & fAlignMask
|
---|
290 | ? (aStack[idxCurEntry].Node.Right & fAlignMask ? ">" : "`")
|
---|
291 | : aStack[idxCurEntry].Node.Right & fAlignMask ? "," : " ",
|
---|
292 | aStack[idxCurEntry].Node.ParentAndFlags & 1 ? "r" : "B");
|
---|
293 | #endif
|
---|
294 | idxNode++;
|
---|
295 |
|
---|
296 | /* Check that there are no RED -> RED sequences. */
|
---|
297 | if ( (aStack[idxCurEntry].Node.ParentAndFlags & 1)
|
---|
298 | && idxCurEntry > 0
|
---|
299 | && (aStack[idxCurEntry - 1].Node.ParentAndFlags & 1))
|
---|
300 | {
|
---|
301 | rcRet = DBGCCmdHlpFail(pCmdHlp, pCmd, "RED child (%#RX64) with RED (%#RX64) parent!\n",
|
---|
302 | (uint64_t)aStack[idxCurEntry].Ptr, (uint64_t)aStack[idxCurEntry - 1].Ptr);
|
---|
303 | cErrors++;
|
---|
304 | }
|
---|
305 |
|
---|
306 | /*
|
---|
307 | * Descend into the right tree, if present.
|
---|
308 | */
|
---|
309 | if (aStack[idxCurEntry].Node.Right & fAlignInfoMask)
|
---|
310 | {
|
---|
311 | rcRet = DBGCCmdHlpFail(pCmdHlp, pCmd, "Misaligned Right pointer in node at %#RX64: %#RX64",
|
---|
312 | (uint64_t)aStack[idxCurEntry].Ptr, (uint64_t)aStack[idxCurEntry].Node.Right);
|
---|
313 | cErrors++;
|
---|
314 | }
|
---|
315 | Ptr = aStack[idxCurEntry].Node.Right & fAlignMask;
|
---|
316 | if (Ptr)
|
---|
317 | {
|
---|
318 | if (cStackEntries < RT_ELEMENTS(aStack))
|
---|
319 | {
|
---|
320 | rc = DBGCCmdHlpMemRead(pCmdHlp, &aStack[cStackEntries].Node, sizeof(aStack[cStackEntries].Node),
|
---|
321 | dbgCmdNtVarFromGCFlatPtr(&Var, Ptr), NULL);
|
---|
322 | if (RT_SUCCESS(rc))
|
---|
323 | {
|
---|
324 | if ((aStack[cStackEntries].Node.ParentAndFlags & fAlignMask) == aStack[idxCurEntry].Ptr)
|
---|
325 | {
|
---|
326 | aStack[idxCurEntry].bState = 2;
|
---|
327 |
|
---|
328 | aStack[cStackEntries].Ptr = Ptr;
|
---|
329 | aStack[cStackEntries].cBlacks = aStack[idxCurEntry].cBlacks
|
---|
330 | + !(aStack[cStackEntries].Node.ParentAndFlags & 1);
|
---|
331 | aStack[cStackEntries].bState = 0;
|
---|
332 | cStackEntries++;
|
---|
333 | continue;
|
---|
334 | }
|
---|
335 | rcRet = DBGCCmdHlpFail(pCmdHlp, pCmd,
|
---|
336 | "Right node of #%RX64 at #%RX64 has an incorrect parent value: %#RX64 (Left=%#RX64, Right=%#RX64)",
|
---|
337 | (uint64_t)aStack[idxCurEntry].Ptr, Ptr, aStack[cStackEntries].Node.ParentAndFlags,
|
---|
338 | aStack[cStackEntries].Node.Left, aStack[cStackEntries].Node.Right);
|
---|
339 | }
|
---|
340 | else
|
---|
341 | rcRet = DBGCCmdHlpFailRc(pCmdHlp, pCmd, rc, "Failed to read the node right of %#RX64 at address %#RX64!",
|
---|
342 | (uint64_t)aStack[idxCurEntry].Ptr, Ptr);
|
---|
343 | }
|
---|
344 | else
|
---|
345 | rcRet = DBGCCmdHlpFail(pCmdHlp, pCmd, "Stack overflow descending to the right!");
|
---|
346 | cErrors++;
|
---|
347 | }
|
---|
348 | else
|
---|
349 | {
|
---|
350 | uint8_t const cBlacksForCur = aStack[idxCurEntry].cBlacks + 1;
|
---|
351 | if (cBlacksForCur != cReqBlacks)
|
---|
352 | {
|
---|
353 | rcRet = DBGCCmdHlpFail(pCmdHlp, pCmd, "Wrong black count to the right of %#RX64: %u, expected %u",
|
---|
354 | aStack[idxCurEntry].Ptr, cBlacksForCur, cReqBlacks);
|
---|
355 | cErrors++;
|
---|
356 | }
|
---|
357 | }
|
---|
358 | RT_FALL_THRU();
|
---|
359 | }
|
---|
360 |
|
---|
361 | case 2:
|
---|
362 | /* We've returned from the right tree and should pop this entry now. */
|
---|
363 | cStackEntries--;
|
---|
364 | break;
|
---|
365 |
|
---|
366 | default:
|
---|
367 | AssertFailedReturn(DBGCCmdHlpFail(pCmdHlp, pCmd, "Internal stack state error: [%u] = %u",
|
---|
368 | idxCurEntry, aStack[idxCurEntry].bState));
|
---|
369 | }
|
---|
370 |
|
---|
371 | }
|
---|
372 |
|
---|
373 | if (!cErrors)
|
---|
374 | DBGCCmdHlpPrintf(pCmdHlp, "No obvious errors\n");
|
---|
375 | else
|
---|
376 | DBGCCmdHlpPrintf(pCmdHlp, "%u tree errors\n", cErrors);
|
---|
377 | return rcRet;
|
---|
378 | }
|
---|
379 |
|
---|
380 | /**
|
---|
381 | * @callback_method_impl{FNDBGCCMD, The 'ntrbtree' command.}
|
---|
382 | */
|
---|
383 | DECLCALLBACK(int) dbgcCmdNtRbTree(PCDBGCCMD pCmd, PDBGCCMDHLP pCmdHlp, PUVM pUVM, PCDBGCVAR paArgs, unsigned cArgs)
|
---|
384 | {
|
---|
385 | /* Check parsing. */
|
---|
386 | DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, cArgs == 1);
|
---|
387 | DBGC_CMDHLP_ASSERT_PARSER_RET(pCmdHlp, pCmd, 0, DBGCVAR_ISGCPOINTER(paArgs[0].enmType));
|
---|
388 | RT_NOREF(pUVM);
|
---|
389 |
|
---|
390 | /* ASSUME 64-bit for now. */
|
---|
391 | return dbgCmdNtRbTreeWorker<NT_RTL_RB_TREE64, NT_RTL_BALANCED_NODE64, uint64_t>(pCmd, pCmdHlp, &paArgs[0]);
|
---|
392 | }
|
---|
393 |
|
---|