Add a fourth parameter to the DEFINE_DACVAR macro that is the actual fully qualified...
[platform/upstream/coreclr.git] / src / ToolBox / SOS / Strike / strike.cpp
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information. 
4 //
5
6 // ==++==
7 // 
8  
9 // 
10 // ==--==
11
12 // ===========================================================================
13 // STRIKE.CPP
14 // ===========================================================================
15 //
16 // History:
17 //   09/07/99  Microsoft  Created
18 //
19 //************************************************************************************************
20 // SOS is the native debugging extension designed to support investigations into CLR (mis-)
21 // behavior by both users of the runtime as well as the code owners. It allows inspection of 
22 // internal structures, of user visible entities, as well as execution control.
23 // 
24 // This is the main SOS file hosting the implementation of all the exposed commands. A good 
25 // starting point for understanding the semantics of these commands is the sosdocs.txt file.
26 // 
27 // #CrossPlatformSOS
28 // SOS currently supports cross platform debugging from x86 to ARM. It takes a different approach 
29 // from the DAC: whereas for the DAC we produce one binary for each supported host-target 
30 // architecture pair, for SOS we produce only one binary for each host architecture; this one 
31 // binary contains code for all supported target architectures. In doing this SOS depends on two
32 // assumptions:
33 //   . that the debugger will load the appropriate DAC, and 
34 //   . that the host and target word size is identical.
35 // The second assumption is identical to the DAC assumption, and there will be considerable effort
36 // required (in the EE, the DAC, and SOS) if we ever need to remove it.
37 // 
38 // In an ideal world SOS would be able to retrieve all platform specific information it needs 
39 // either from the debugger or from DAC. However, SOS has taken some subtle and not so subtle
40 // dependencies on the CLR and the target platform.
41 // To resolve this problem, SOS now abstracts the target behind the IMachine interface, and uses 
42 // calls on IMachine to take target-specific actions. It implements X86Machine, ARMMachine, and 
43 // AMD64Machine. An instance of these exists in each appropriate host (e.g. the X86 version of SOS
44 // contains instaces of X86Machine and ARMMachine, the ARM version contains an instance of 
45 // ARMMachine, and the AMD64 version contains an instance of AMD64Machine). The code included in 
46 // each version if determined by the SosTarget*** MSBuild symbols, and SOS_TARGET_*** conditional 
47 // compilation symbols (as specified in sos.targets).
48 // 
49 // Most of the target specific code is hosted in disasm.h/.cpp, and disasmX86.cpp, disasmARM.cpp.
50 // Some code currently under _TARGET_*** ifdefs may need to be reviewed/revisited.
51 // 
52 // Issues:
53 // The one-binary-per-host decision does have some drawbacks: 
54 //   . Currently including system headers or even CLR headers will only account for the host 
55 //     target, IOW, when building the X86 version of SOS, CONTEXT will refer to the X86 CONTEXT 
56 //     structure, so we need to be careful when debugging ARM targets. The CONTEXT issue is 
57 //     partially resolved by CROSS_PLATFORM_CONTEXT (there is still a need to be very careful 
58 //     when handling arrays of CONTEXTs - see _EFN_StackTrace for details on this).
59 //   . For larger includes (e.g. GC info), we will need to include files in specific namespaces, 
60 //     with specific _TARGET_*** macros defined in order to avoid name clashes and ensure correct
61 //     system types are used.
62 // -----------------------------------------------------------------------------------------------
63
64 #define DO_NOT_DISABLE_RAND //this is a standalone tool, and can use rand()
65
66 #include <windows.h>
67 #include <winver.h>
68 #include <winternl.h>
69 #include <psapi.h>
70 #ifndef FEATURE_PAL
71 #include <list>   
72 #endif // !FEATURE_PAL
73 #include <wchar.h>
74
75 #include "platformspecific.h"
76
77 #define NOEXTAPI
78 #define KDEXT_64BIT
79 #include <wdbgexts.h>
80 #undef DECLARE_API
81 #undef StackTrace
82
83 #include <dbghelp.h>
84
85 #include <stdio.h>
86 #include <stdlib.h>
87 #include <string.h>
88
89 #include <malloc.h>
90 #include <stddef.h>
91
92 #include "strike.h"
93 #include "sos.h"
94
95 #ifndef STRESS_LOG
96 #define STRESS_LOG
97 #endif // STRESS_LOG
98 #define STRESS_LOG_READONLY
99 #include "stresslog.h"
100
101 #include "util.h"
102
103 #include "corhdr.h"
104 #include "cor.h"
105 #include "cordebug.h"
106 #include "dacprivate.h"
107 #include "corexcep.h"
108
109 #define  CORHANDLE_MASK 0x1
110 #define SWITCHED_OUT_FIBER_OSID 0xbaadf00d;
111
112 #define DEFINE_EXT_GLOBALS
113
114 #include "data.h"
115 #ifndef FEATURE_PAL
116 #include "disasm.h"
117 #endif // !FEATURE_PAL
118
119 #include "predeftlsslot.h"
120
121 #include "hillclimbing.h"
122
123 #include "sos_md.h"
124
125 #ifndef FEATURE_PAL
126
127 #include "ExpressionNode.h"
128 #include "WatchCmd.h"
129
130 #include <set>
131 #include <algorithm>
132 #include <vector>
133
134 #include "tls.h"
135
136 typedef struct _VM_COUNTERS {
137     SIZE_T PeakVirtualSize;
138     SIZE_T VirtualSize;
139     ULONG PageFaultCount;
140     SIZE_T PeakWorkingSetSize;
141     SIZE_T WorkingSetSize;
142     SIZE_T QuotaPeakPagedPoolUsage;
143     SIZE_T QuotaPagedPoolUsage;
144     SIZE_T QuotaPeakNonPagedPoolUsage;
145     SIZE_T QuotaNonPagedPoolUsage;
146     SIZE_T PagefileUsage;
147     SIZE_T PeakPagefileUsage;
148 } VM_COUNTERS;
149 typedef VM_COUNTERS *PVM_COUNTERS;
150
151 const PROCESSINFOCLASS ProcessVmCounters = static_cast<PROCESSINFOCLASS>(3);
152
153 #endif // !FEATURE_PAL
154
155 BOOL CallStatus;
156 BOOL ControlC = FALSE;
157
158 IMetaDataDispenserEx *pDisp = NULL;
159 WCHAR g_mdName[mdNameLen];
160
161 #ifndef FEATURE_PAL
162 HMODULE g_hInstance = NULL;
163 #include <vector>
164 #include <algorithm>
165 #endif // !FEATURE_PAL
166
167 #ifdef _MSC_VER
168 #pragma warning(disable:4244)   // conversion from 'unsigned int' to 'unsigned short', possible loss of data
169 #pragma warning(disable:4189)   // local variable is initialized but not referenced
170 #endif
171
172
173
174 #if defined _X86_ && !defined FEATURE_PAL
175 // disable FPO for X86 builds
176 #pragma optimize("y", off)
177 #endif
178
179
180
181 #undef assert
182
183 #ifdef _MSC_VER
184 #pragma warning(default:4244)
185 #pragma warning(default:4189)
186 #endif
187
188 #ifndef FEATURE_PAL
189 #include "ntinfo.h"
190 #endif // FEATURE_PAL
191
192 // Size of a fixed array:
193 #ifndef ARRAYSIZE
194 #define ARRAYSIZE(a)            (sizeof(a)/sizeof((a)[0]))
195 #endif // !ARRAYSIZE
196
197
198 #ifndef IfFailRet
199 #define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
200 #endif
201
202 #define NOTHROW (std::nothrow)
203
204 #define MINIDUMP_NOT_SUPPORTED()   \
205     if (IsMiniDumpFile())      \
206     {                          \
207         ExtOut("This command is not supported in a minidump without full memory\n"); \
208         ExtOut("To try the command anyway, run !MinidumpMode 0\n"); \
209         return Status;         \
210     }
211
212 #ifndef FEATURE_PAL
213 #include "safemath.h"
214
215 DECLARE_API (MinidumpMode)
216 {
217     INIT_API ();
218     DWORD_PTR Value=0;
219
220     CMDValue arg[] = 
221     {   // vptr, type
222         {&Value, COHEX}
223     };
224
225     size_t nArg;
226     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) 
227     {
228         return Status;
229     }    
230     if (nArg == 0)
231     {
232         // Print status of current mode
233        ExtOut("Current mode: %s - unsafe minidump commands are %s.\n",
234                g_InMinidumpSafeMode ? "1" : "0",
235                g_InMinidumpSafeMode ? "disabled" : "enabled");
236     }
237     else
238     {
239         if (Value != 0 && Value != 1)
240         {
241             ExtOut("Mode must be 0 or 1\n");
242             return Status;
243         }
244
245         g_InMinidumpSafeMode = (BOOL) Value;
246         ExtOut("Unsafe minidump commands are %s.\n",
247                 g_InMinidumpSafeMode ? "disabled" : "enabled");
248     }
249
250     return Status;
251 }
252
253 #endif // FEATURE_PAL
254
255 /**********************************************************************\
256 * Routine Description:                                                 *
257 *                                                                      *
258 *    This function is called to get the MethodDesc for a given eip     *  
259 *                                                                      *
260 \**********************************************************************/
261 DECLARE_API(IP2MD)
262 {
263     INIT_API();
264     MINIDUMP_NOT_SUPPORTED();
265
266     BOOL dml = FALSE;
267     TADDR IP = 0;
268     CMDOption option[] = 
269     {   // name, vptr, type, hasValue
270         {"/d", &dml, COBOOL, FALSE},
271     };
272     CMDValue arg[] = 
273     {   // vptr, type
274         {&IP, COHEX},
275     };
276     size_t nArg;
277     
278     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
279     {
280         return Status;
281     }
282     EnableDMLHolder dmlHolder(dml);
283
284     if (IP == 0)
285     {
286         ExtOut("%s is not IP\n", args);
287         return Status;
288     }
289
290     CLRDATA_ADDRESS cdaStart = TO_CDADDR(IP);
291     CLRDATA_ADDRESS pMD;
292
293     
294     if ((Status = g_sos->GetMethodDescPtrFromIP(cdaStart, &pMD)) != S_OK)
295     {
296         ExtOut("Failed to request MethodData, not in JIT code range\n");
297         return Status;
298     }
299
300     DMLOut("MethodDesc:   %s\n", DMLMethodDesc(pMD));
301     DumpMDInfo(TO_TADDR(pMD), cdaStart, FALSE /* fStackTraceFormat */);
302
303 #ifndef FEATURE_PAL
304     char  filename[MAX_PATH+1];
305     ULONG linenum;
306     // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
307     ULONG symlines = 0;
308     if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
309     {
310         symlines &= SYMOPT_LOAD_LINES;
311     }
312
313     if (symlines != 0
314         && SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), 
315                          &linenum,
316                          filename,
317                          MAX_PATH+1)))
318     {
319         ExtOut("Source file:  %s @ %d\n", filename, linenum);
320     }
321 #endif
322
323     return Status;
324 }
325
326 #ifndef FEATURE_PAL
327
328 // (MAX_STACK_FRAMES is also used by x86 to prevent infinite loops in _EFN_StackTrace)
329 #define MAX_STACK_FRAMES 1000
330
331
332 #ifdef _TARGET_WIN64_
333 // I use a global set of frames for stack walking on win64 because the debugger's
334 // GetStackTrace function doesn't provide a way to find out the total size of a stackwalk,
335 // and I'd like to have a reasonably big maximum without overflowing the stack by declaring
336 // the buffer locally and I also want to get a managed trace in a low memory environment
337 // (so no dynamic allocation if possible).
338 DEBUG_STACK_FRAME g_Frames[MAX_STACK_FRAMES];
339 AMD64_CONTEXT g_X64FrameContexts[MAX_STACK_FRAMES];
340 #endif
341
342
343 /**********************************************************************\
344 * Routine Description:                                                 *
345 *                                                                      *
346 *    This function displays the stack trace.  It looks at each DWORD   *  
347 *    on stack.  If the DWORD is a return address, the symbol name or
348 *    managed function name is displayed.                               *
349 *                                                                      *
350 \**********************************************************************/
351 void DumpStackInternal(DumpStackFlag *pDSFlag)
352 {    
353     ReloadSymbolWithLineInfo();
354     
355     ULONG64 StackOffset;
356     g_ExtRegisters->GetStackOffset (&StackOffset);
357     if (pDSFlag->top == 0) {
358         pDSFlag->top = TO_TADDR(StackOffset);
359     }
360     size_t value;
361     while (g_ExtData->ReadVirtual(TO_CDADDR(pDSFlag->top), &value, sizeof(size_t), NULL) != S_OK) {
362         if (IsInterrupt())
363             return;
364         pDSFlag->top = NextOSPageAddress(pDSFlag->top);
365     }
366
367 #ifndef FEATURE_PAL     
368     if (pDSFlag->end == 0) {
369         // Find the current stack range
370         NT_TIB teb;
371         ULONG64 dwTebAddr=0;
372
373         g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
374         if (SafeReadMemory(TO_TADDR(dwTebAddr), &teb, sizeof(NT_TIB), NULL))
375         {
376             if (pDSFlag->top > TO_TADDR(teb.StackLimit)
377             && pDSFlag->top <= TO_TADDR(teb.StackBase))
378             {
379                 if (pDSFlag->end == 0 || pDSFlag->end > TO_TADDR(teb.StackBase))
380                     pDSFlag->end = TO_TADDR(teb.StackBase);
381             }
382         }
383     }
384 #endif // FEATURE_PAL
385     
386     if (pDSFlag->end == 0)
387     {
388         ExtOut("TEB information is not available so a stack size of 0xFFFF is assumed\n");
389         pDSFlag->end = pDSFlag->top + 0xFFFF;
390     }
391     
392     if (pDSFlag->end < pDSFlag->top)
393     {
394         ExtOut("Wrong option: stack selection wrong\n");
395         return;
396     }
397
398     DumpStackWorker(*pDSFlag);
399 }
400
401
402 DECLARE_API(DumpStack)
403 {
404     INIT_API_NO_RET_ON_FAILURE();
405
406     MINIDUMP_NOT_SUPPORTED();
407
408     DumpStackFlag DSFlag;
409     DSFlag.fEEonly = FALSE;
410     DSFlag.top = 0;
411     DSFlag.end = 0;
412
413     BOOL dml = FALSE;
414     CMDOption option[] = {
415         // name, vptr, type, hasValue
416         {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
417         {"-n",  &DSFlag.fSuppressSrcInfo, COBOOL, FALSE},
418         {"/d", &dml, COBOOL, FALSE}
419     };
420     CMDValue arg[] = {
421         // vptr, type
422         {&DSFlag.top, COHEX},
423         {&DSFlag.end, COHEX}
424     };
425     size_t nArg;
426     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
427         return Status;
428
429     // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
430     ULONG symlines = 0;
431     if (!DSFlag.fSuppressSrcInfo && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
432     {
433         symlines &= SYMOPT_LOAD_LINES;
434     }
435     DSFlag.fSuppressSrcInfo = DSFlag.fSuppressSrcInfo || (symlines == 0);
436
437     EnableDMLHolder enabledml(dml);
438
439     ULONG id = 0;
440     g_ExtSystem->GetCurrentThreadSystemId(&id);
441     ExtOut("OS Thread Id: 0x%x ", id);
442     g_ExtSystem->GetCurrentThreadId(&id);
443     ExtOut("(%d)\n", id);
444
445     DumpStackInternal(&DSFlag);
446     return Status;
447 }
448
449
450 /**********************************************************************\
451 * Routine Description:                                                 *
452 *                                                                      *
453 *    This function displays the stack trace for threads that EE knows  *  
454 *    from ThreadStore.                                                 *
455 *                                                                      *
456 \**********************************************************************/
457 DECLARE_API (EEStack)
458 {
459     INIT_API();    
460
461     MINIDUMP_NOT_SUPPORTED();  
462
463     DumpStackFlag DSFlag;
464     DSFlag.fEEonly = FALSE;
465     DSFlag.top = 0;
466     DSFlag.end = 0;
467
468     BOOL bShortList = FALSE;
469     BOOL dml = FALSE;
470     CMDOption option[] = 
471     {   // name, vptr, type, hasValue
472         {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
473         {"-short", &bShortList, COBOOL, FALSE},
474         {"/d", &dml, COBOOL, FALSE}
475     };    
476
477     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
478     {
479         return Status;
480     }
481
482     EnableDMLHolder enableDML(dml);
483
484     ULONG Tid;
485     g_ExtSystem->GetCurrentThreadId(&Tid);
486
487     DacpThreadStoreData ThreadStore;
488     if ((Status = ThreadStore.Request(g_sos)) != S_OK)
489     {
490         ExtOut("Failed to request ThreadStore\n");
491         return Status;
492     }    
493
494     CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
495     while (CurThread)
496     {
497         if (IsInterrupt())
498             break;
499
500         DacpThreadData Thread;        
501         if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
502         {
503             ExtOut("Failed to request Thread at %p\n", CurThread);
504             return Status;
505         }
506
507         ULONG id=0;
508         if (g_ExtSystem->GetThreadIdBySystemId (Thread.osThreadId, &id) != S_OK)
509         {
510             CurThread = Thread.nextThread;    
511             continue;
512         }
513         
514         ExtOut("---------------------------------------------\n");
515         ExtOut("Thread %3d\n", id);
516         BOOL doIt = FALSE;
517
518         
519 #define TS_Hijacked 0x00000080
520
521         if (!bShortList) 
522         {
523             doIt = TRUE;
524         }
525         else if ((Thread.lockCount > 0) || (Thread.state & TS_Hijacked)) 
526         {             
527             // TODO: bring back || (int)vThread.m_pFrame != -1  {
528             doIt = TRUE;
529         }
530         else 
531         {
532             ULONG64 IP;
533             g_ExtRegisters->GetInstructionOffset (&IP);
534             JITTypes jitType;
535             TADDR methodDesc;
536             TADDR gcinfoAddr;
537             IP2MethodDesc (TO_TADDR(IP), methodDesc, jitType, gcinfoAddr);
538             if (methodDesc)
539             {
540                 doIt = TRUE;
541             }
542         }
543         
544         if (doIt) 
545         {
546             g_ExtSystem->SetCurrentThreadId(id);
547             DSFlag.top = 0;
548             DSFlag.end = 0;
549             DumpStackInternal(&DSFlag);
550         }
551
552         CurThread = Thread.nextThread;
553     }
554
555     g_ExtSystem->SetCurrentThreadId(Tid);
556     return Status;
557 }
558
559 HRESULT DumpStackObjectsRaw(size_t nArg, __in_z LPSTR exprBottom, __in_z LPSTR exprTop, BOOL bVerify)
560 {
561     size_t StackTop = 0;
562     size_t StackBottom = 0;
563     if (nArg==0)
564     {
565         ULONG64 StackOffset;
566         g_ExtRegisters->GetStackOffset(&StackOffset);
567
568         StackTop = TO_TADDR(StackOffset);
569     }
570     else
571     {
572         StackTop = GetExpression(exprTop);
573         if (StackTop == 0)
574         {
575             ExtOut("wrong option: %s\n", exprTop);
576             return E_FAIL;
577         }
578
579         if (nArg==2)
580         {
581             StackBottom = GetExpression(exprBottom);
582             if (StackBottom == 0)
583             {
584                 ExtOut("wrong option: %s\n", exprBottom);
585                 return E_FAIL;
586             }
587         }
588     }
589     
590 #ifndef FEATURE_PAL
591     NT_TIB teb;
592     ULONG64 dwTebAddr=0;
593     HRESULT hr = g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
594     if (SUCCEEDED(hr) && SafeReadMemory (TO_TADDR(dwTebAddr), &teb, sizeof (NT_TIB), NULL))
595     {
596         if (StackTop > TO_TADDR(teb.StackLimit) && StackTop <= TO_TADDR(teb.StackBase))
597         {
598             if (StackBottom == 0 || StackBottom > TO_TADDR(teb.StackBase))
599                 StackBottom = TO_TADDR(teb.StackBase);
600         }
601     }
602 #endif
603     
604     if (StackBottom == 0)
605         StackBottom = StackTop + 0xFFFF;
606     
607     if (StackBottom < StackTop)
608     {
609         ExtOut("Wrong option: stack selection wrong\n");
610         return E_FAIL;
611     }
612
613 #ifndef FEATURE_PAL
614     // We can use the gc snapshot to eliminate object addresses that are
615     // not on the gc heap. 
616     if (!g_snapshot.Build())
617     {
618         ExtOut("Unable to determine bounds of gc heap\n");
619         return E_FAIL;
620     }   
621 #endif // !FEATURE_PAL
622
623     // Print thread ID.
624     ULONG id = 0;
625     g_ExtSystem->GetCurrentThreadSystemId (&id);
626     ExtOut("OS Thread Id: 0x%x ", id);
627     g_ExtSystem->GetCurrentThreadId (&id);
628     ExtOut("(%d)\n", id);
629     
630     DumpStackObjectsHelper(StackTop, StackBottom, bVerify);
631     return S_OK;
632 }
633
634 /**********************************************************************\
635 * Routine Description:                                                 *
636 *                                                                      *
637 *    This function is called to dump the address and name of all       *
638 *    Managed Objects on the stack.                                     *  
639 *                                                                      *
640 \**********************************************************************/
641 DECLARE_API(DumpStackObjects)
642 {
643     INIT_API();
644     MINIDUMP_NOT_SUPPORTED();
645     StringHolder exprTop, exprBottom;
646
647     BOOL bVerify = FALSE;
648     BOOL dml = FALSE;
649     CMDOption option[] = 
650     {   // name, vptr, type, hasValue
651         {"-verify", &bVerify, COBOOL, FALSE},
652         {"/d", &dml, COBOOL, FALSE}
653     };    
654     CMDValue arg[] = 
655     {   // vptr, type
656         {&exprTop.data, COSTRING},
657         {&exprBottom.data, COSTRING}
658     };
659     size_t nArg;
660
661     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
662     {
663         return Status;
664     }
665
666     EnableDMLHolder enableDML(dml);
667     
668     return DumpStackObjectsRaw(nArg, exprBottom.data, exprTop.data, bVerify);
669 }
670
671 /**********************************************************************\
672 * Routine Description:                                                 *
673 *                                                                      *
674 *    This function is called to dump the contents of a MethodDesc      *
675 *    for a given address                                               *  
676 *                                                                      *
677 \**********************************************************************/
678 DECLARE_API(DumpMD)
679 {
680     INIT_API();
681     MINIDUMP_NOT_SUPPORTED();
682     
683     DWORD_PTR dwStartAddr = NULL;
684     BOOL dml = FALSE;
685
686     CMDOption option[] = 
687     {   // name, vptr, type, hasValue
688         {"/d", &dml, COBOOL, FALSE},
689     };
690     CMDValue arg[] = 
691     {   // vptr, type
692         {&dwStartAddr, COHEX},
693     };
694     size_t nArg;
695
696     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
697     {
698         return Status;
699     }
700
701     EnableDMLHolder dmlHolder(dml);
702     
703     DumpMDInfo(dwStartAddr);
704     
705     return Status;
706 }
707
708 BOOL GatherDynamicInfo(TADDR DynamicMethodObj, DacpObjectData *codeArray, 
709                        DacpObjectData *tokenArray, TADDR *ptokenArrayAddr)
710 {
711     BOOL bRet = FALSE;
712     int iOffset;
713     DacpObjectData objData; // temp object
714
715     if (codeArray == NULL || tokenArray == NULL)
716         return bRet;
717     
718     if (objData.Request(g_sos, TO_CDADDR(DynamicMethodObj)) != S_OK)
719         return bRet;
720     
721     iOffset = GetObjFieldOffset(DynamicMethodObj, objData.MethodTable, L"m_resolver");
722     if (iOffset <= 0)
723         return bRet;
724     
725     TADDR resolverPtr;
726     if (FAILED(MOVE(resolverPtr, DynamicMethodObj + iOffset)))
727         return bRet;
728
729     if (objData.Request(g_sos, TO_CDADDR(resolverPtr)) != S_OK)
730         return bRet;
731     
732     iOffset = GetObjFieldOffset(resolverPtr, objData.MethodTable, L"m_code");
733     if (iOffset <= 0)
734         return bRet;
735
736     TADDR codePtr;
737     if (FAILED(MOVE(codePtr, resolverPtr + iOffset)))
738         return bRet;
739
740     if (codeArray->Request(g_sos, TO_CDADDR(codePtr)) != S_OK)
741         return bRet;
742     
743     if (codeArray->dwComponentSize != 1)
744         return bRet;
745         
746     // We also need the resolution table
747     iOffset = GetObjFieldOffset (resolverPtr, objData.MethodTable, L"m_scope");
748     if (iOffset <= 0)
749         return bRet;
750
751     TADDR scopePtr;
752     if (FAILED(MOVE(scopePtr, resolverPtr + iOffset)))
753         return bRet;
754
755     if (objData.Request(g_sos, TO_CDADDR(scopePtr)) != S_OK)
756         return bRet;
757     
758     iOffset = GetObjFieldOffset (scopePtr, objData.MethodTable, L"m_tokens");
759     if (iOffset <= 0)
760         return bRet;
761
762     TADDR tokensPtr;
763     if (FAILED(MOVE(tokensPtr, scopePtr + iOffset)))
764         return bRet;
765
766     if (objData.Request(g_sos, TO_CDADDR(tokensPtr)) != S_OK)
767         return bRet;
768     
769     iOffset = GetObjFieldOffset(tokensPtr, objData.MethodTable, L"_items");
770     if (iOffset <= 0)
771         return bRet;
772
773     TADDR itemsPtr;
774     MOVE (itemsPtr, tokensPtr + iOffset);
775
776     *ptokenArrayAddr = itemsPtr;
777     
778     if (tokenArray->Request(g_sos, TO_CDADDR(itemsPtr)) != S_OK)
779         return bRet;
780
781     bRet = TRUE; // whew.
782     return bRet;
783 }
784
785 DECLARE_API(DumpIL)
786 {
787     INIT_API();
788     MINIDUMP_NOT_SUPPORTED();
789     DWORD_PTR dwStartAddr = NULL;
790     DWORD_PTR dwDynamicMethodObj = NULL;
791     BOOL dml = FALSE;
792     BOOL fILPointerDirectlySpecified = FALSE;
793
794     CMDOption option[] = 
795     {   // name, vptr, type, hasValue
796         {"/d", &dml, COBOOL, FALSE},
797         {"/i", &fILPointerDirectlySpecified, COBOOL, FALSE},
798     };
799     CMDValue arg[] = 
800     {   // vptr, type
801         {&dwStartAddr, COHEX},
802     };
803     size_t nArg;
804
805     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
806     {
807         return Status;
808     }
809
810     EnableDMLHolder dmlHolder(dml);    
811     if (dwStartAddr == NULL)
812     {
813         ExtOut("Must pass a valid expression\n");
814         return Status;
815     }
816
817     if (fILPointerDirectlySpecified)
818     {
819         return DecodeILFromAddress(NULL, dwStartAddr);
820     }
821
822     if (!g_snapshot.Build())
823     {
824         ExtOut("Unable to build snapshot of the garbage collector state\n");
825         return Status;
826     }
827
828     if (g_snapshot.GetHeap(dwStartAddr) != NULL)
829     {
830         dwDynamicMethodObj = dwStartAddr;
831     }
832     
833     if (dwDynamicMethodObj == NULL)
834     {
835         // We have been given a MethodDesc
836         DacpMethodDescData MethodDescData;
837         if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK)
838         {
839             ExtOut("%p is not a MethodDesc\n", (ULONG64)dwStartAddr);
840             return Status;
841         }
842
843         if (MethodDescData.bIsDynamic && MethodDescData.managedDynamicMethodObject)
844         {
845             dwDynamicMethodObj = TO_TADDR(MethodDescData.managedDynamicMethodObject);
846             if (dwDynamicMethodObj == NULL)
847             {
848                 ExtOut("Unable to print IL for DynamicMethodDesc %p\n", (ULONG64) dwDynamicMethodObj);
849                 return Status;
850             }
851         }
852         else
853         {
854             // This is not a dynamic method, print the IL for it.
855             // Get the module
856             DacpModuleData dmd;    
857             if (dmd.Request(g_sos, MethodDescData.ModulePtr) != S_OK)
858             {
859                 ExtOut("Unable to get module\n");
860                 return Status;
861             }
862
863             ToRelease<IMetaDataImport> pImport = MDImportForModule(&dmd);
864             if (pImport == NULL)
865             {
866                 ExtOut("bad import\n");
867                 return Status;
868             }
869
870             ULONG pRva;
871             DWORD dwFlags;
872             if (pImport->GetRVA(MethodDescData.MDToken, &pRva, &dwFlags) != S_OK)
873             {
874                 ExtOut("error in import\n");
875                 return Status;
876             }    
877
878             CLRDATA_ADDRESS ilAddrClr;
879             if (g_sos->GetILForModule(MethodDescData.ModulePtr, pRva, &ilAddrClr) != S_OK)
880             {
881                 ExtOut("FindIL failed\n");
882                 return Status;
883             }
884
885             TADDR ilAddr = TO_TADDR(ilAddrClr);
886             IfFailRet(DecodeILFromAddress(pImport, ilAddr));
887         }
888     }
889     
890     if (dwDynamicMethodObj != NULL)
891     {
892         // We have a DynamicMethod managed object, let us visit the town and paint.        
893         DacpObjectData codeArray;
894         DacpObjectData tokenArray;
895         DWORD_PTR tokenArrayAddr;
896         if (!GatherDynamicInfo (dwDynamicMethodObj, &codeArray, &tokenArray, &tokenArrayAddr))
897         {
898             DMLOut("Error gathering dynamic info from object at %s.\n", DMLObject(dwDynamicMethodObj));
899             return Status;
900         }
901         
902         // Read the memory into a local buffer
903         BYTE *pArray = new NOTHROW BYTE[(SIZE_T)codeArray.dwNumComponents];
904         if (pArray == NULL)
905         {
906             ExtOut("Not enough memory to read IL\n");
907             return Status;
908         }
909         
910         Status = g_ExtData->ReadVirtual(UL64_TO_CDA(codeArray.ArrayDataPtr), pArray, (ULONG)codeArray.dwNumComponents, NULL);
911         if (Status != S_OK)
912         {
913             ExtOut("Failed to read memory\n");
914             delete [] pArray;
915             return Status;
916         }
917
918         // Now we have a local copy of the IL, and a managed array for token resolution.
919         // Visit our IL parser with this info.        
920         ExtOut("This is dynamic IL. Exception info is not reported at this time.\n");
921         ExtOut("If a token is unresolved, run \"!do <addr>\" on the addr given\n");
922         ExtOut("in parenthesis. You can also look at the token table yourself, by\n");
923         ExtOut("running \"!DumpArray %p\".\n\n", SOS_PTR(tokenArrayAddr));
924         DecodeDynamicIL(pArray, (ULONG)codeArray.dwNumComponents, tokenArray);
925         
926         delete [] pArray;                
927     }    
928     return Status;
929 }
930
931 void DumpSigWorker (
932         DWORD_PTR dwSigAddr,
933         DWORD_PTR dwModuleAddr,
934         BOOL fMethod)
935 {
936     //
937     // Find the length of the signature and copy it into the debugger process.
938     //
939
940     ULONG cbSig = 0;
941     const ULONG cbSigInc = 256;
942     ArrayHolder<COR_SIGNATURE> pSig = new NOTHROW COR_SIGNATURE[cbSigInc];
943     if (pSig == NULL)
944     {
945         ReportOOM();        
946         return;
947     }
948     
949     CQuickBytes sigString;
950     for (;;)
951     {
952         if (IsInterrupt())
953             return;
954
955         ULONG cbCopied;
956         if (!SafeReadMemory(TO_TADDR(dwSigAddr + cbSig), pSig + cbSig, cbSigInc, &cbCopied))
957             return;
958         cbSig += cbCopied;
959
960         sigString.ReSize(0);
961         GetSignatureStringResults result;
962         if (fMethod)
963             result = GetMethodSignatureString(pSig, cbSig, dwModuleAddr, &sigString);
964         else
965             result = GetSignatureString(pSig, cbSig, dwModuleAddr, &sigString);
966
967         if (GSS_ERROR == result)
968             return;
969
970         if (GSS_SUCCESS == result)
971             break;
972
973         // If we didn't get the full amount back, and we failed to parse the
974         // signature, it's not valid because of insufficient data
975         if (cbCopied < 256)
976         {
977             ExtOut("Invalid signature\n");
978             return;
979         }
980
981 #ifdef _PREFAST_
982 #pragma warning(push)
983 #pragma warning(disable:6280) // "Suppress PREFast warning about mismatch alloc/free"
984 #endif
985
986         PCOR_SIGNATURE pSigNew = (PCOR_SIGNATURE)realloc(pSig, cbSig+cbSigInc);
987
988 #ifdef _PREFAST_
989 #pragma warning(pop)
990 #endif
991
992         if (pSigNew == NULL)
993         {
994             ExtOut("Out of memory\n");
995             return;
996         }
997         
998         pSig = pSigNew;
999     }
1000
1001     ExtOut("%S\n", (PCWSTR)sigString.Ptr());
1002 }
1003
1004
1005 /**********************************************************************\
1006 * Routine Description:                                                 *
1007 *                                                                      *
1008 *    This function is called to dump a signature object.               *
1009 *                                                                      *
1010 \**********************************************************************/
1011 DECLARE_API(DumpSig)
1012 {
1013     INIT_API();
1014
1015     MINIDUMP_NOT_SUPPORTED();
1016     
1017     //
1018     // Fetch arguments
1019     //
1020
1021     StringHolder sigExpr;
1022     StringHolder moduleExpr;
1023     CMDValue arg[] = 
1024     {
1025         {&sigExpr.data, COSTRING},
1026         {&moduleExpr.data, COSTRING}
1027     };
1028     size_t nArg;
1029     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
1030     {
1031         return Status;
1032     }
1033     if (nArg != 2)
1034     {
1035         ExtOut("!DumpSig <sigaddr> <moduleaddr>\n");
1036         return Status;
1037     }
1038
1039     DWORD_PTR dwSigAddr = GetExpression(sigExpr.data);        
1040     DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data);
1041
1042     if (dwSigAddr == 0 || dwModuleAddr == 0)
1043     {
1044         ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data);
1045         return Status;
1046     }
1047     
1048     DumpSigWorker(dwSigAddr, dwModuleAddr, TRUE);
1049     return Status;
1050 }
1051
1052
1053
1054 /**********************************************************************\
1055 * Routine Description:                                                 *
1056 *                                                                      *
1057 *    This function is called to dump a portion of a signature object.  *
1058 *                                                                      *
1059 \**********************************************************************/
1060 DECLARE_API(DumpSigElem)
1061 {
1062     INIT_API();
1063
1064     MINIDUMP_NOT_SUPPORTED();
1065     
1066
1067     //
1068     // Fetch arguments
1069     //
1070
1071     StringHolder sigExpr;
1072     StringHolder moduleExpr;
1073     CMDValue arg[] = 
1074     {
1075         {&sigExpr.data, COSTRING},
1076         {&moduleExpr.data, COSTRING}
1077     };
1078     size_t nArg;
1079     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
1080     {
1081         return Status;
1082     }
1083
1084     if (nArg != 2)
1085     {
1086         ExtOut("!DumpSigElem <sigaddr> <moduleaddr>\n");
1087         return Status;
1088     }
1089
1090     DWORD_PTR dwSigAddr = GetExpression(sigExpr.data);        
1091     DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data);
1092
1093     if (dwSigAddr == 0 || dwModuleAddr == 0)
1094     {
1095         ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data);
1096         return Status;
1097     }
1098
1099     DumpSigWorker(dwSigAddr, dwModuleAddr, FALSE);
1100     return Status;
1101 }
1102
1103
1104 /**********************************************************************\
1105 * Routine Description:                                                 *
1106 *                                                                      *
1107 *    This function is called to dump the contents of an EEClass from   *  
1108 *    a given address
1109 *                                                                      *
1110 \**********************************************************************/
1111 DECLARE_API(DumpClass)
1112 {
1113     INIT_API();
1114     MINIDUMP_NOT_SUPPORTED();
1115     
1116     DWORD_PTR dwStartAddr = 0;
1117     BOOL dml = FALSE;
1118
1119     CMDOption option[] = 
1120     {   // name, vptr, type, hasValue
1121         {"/d", &dml, COBOOL, FALSE},
1122     };
1123     CMDValue arg[] = 
1124     {   // vptr, type
1125         {&dwStartAddr, COHEX}
1126     };
1127
1128     size_t nArg;
1129     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
1130     {
1131         return Status;
1132     }
1133
1134     if (nArg == 0) 
1135     {
1136         ExtOut("Missing EEClass address\n");
1137         return Status;
1138     }
1139
1140     EnableDMLHolder dmlHolder(dml);
1141
1142     CLRDATA_ADDRESS methodTable;
1143     if ((Status=g_sos->GetMethodTableForEEClass(TO_CDADDR(dwStartAddr), &methodTable)) != S_OK)
1144     {
1145         ExtOut("Invalid EEClass address\n");
1146         return Status;
1147     }
1148
1149     DacpMethodTableData mtdata;
1150     if ((Status=mtdata.Request(g_sos, TO_CDADDR(methodTable)))!=S_OK)
1151     {
1152         ExtOut("EEClass has an invalid MethodTable address\n");
1153         return Status;
1154     }            
1155
1156     sos::MethodTable mt = TO_TADDR(methodTable);
1157     ExtOut("Class Name:      %S\n", mt.GetName());
1158
1159     WCHAR fileName[MAX_PATH];
1160     FileNameForModule(TO_TADDR(mtdata.Module), fileName);
1161     ExtOut("mdToken:         %p\n", mtdata.cl);
1162     ExtOut("File:            %S\n", fileName);
1163
1164     CLRDATA_ADDRESS ParentEEClass = NULL;
1165     if (mtdata.ParentMethodTable)
1166     {
1167         DacpMethodTableData mtdataparent;
1168         if ((Status=mtdataparent.Request(g_sos, TO_CDADDR(mtdata.ParentMethodTable)))!=S_OK)
1169         {
1170             ExtOut("EEClass has an invalid MethodTable address\n");
1171             return Status;
1172         }                     
1173         ParentEEClass = mtdataparent.Class;
1174     }
1175
1176     DMLOut("Parent Class:    %s\n", DMLClass(ParentEEClass));
1177     DMLOut("Module:          %s\n", DMLModule(mtdata.Module));
1178     DMLOut("Method Table:    %s\n", DMLMethodTable(methodTable));
1179     ExtOut("Vtable Slots:    %x\n", mtdata.wNumVirtuals);
1180     ExtOut("Total Method Slots:  %x\n", mtdata.wNumVtableSlots);
1181     ExtOut("Class Attributes:    %x  ", mtdata.dwAttrClass);
1182
1183     if (IsTdInterface(mtdata.dwAttrClass))
1184         ExtOut("Interface, ");
1185     if (IsTdAbstract(mtdata.dwAttrClass))
1186         ExtOut("Abstract, ");
1187     if (IsTdImport(mtdata.dwAttrClass))
1188         ExtOut("ComImport, ");
1189     
1190     ExtOut("\n");        
1191
1192     DacpMethodTableTransparencyData transparency;
1193     if (SUCCEEDED(transparency.Request(g_sos, methodTable)))
1194     {
1195         ExtOut("Transparency:        %s\n", GetTransparency(transparency));
1196     }
1197
1198     DacpMethodTableFieldData vMethodTableFields;
1199     if (SUCCEEDED(vMethodTableFields.Request(g_sos, methodTable)))
1200     {
1201         ExtOut("NumInstanceFields:   %x\n", vMethodTableFields.wNumInstanceFields);
1202         ExtOut("NumStaticFields:     %x\n", vMethodTableFields.wNumStaticFields);
1203
1204         if (vMethodTableFields.wNumThreadStaticFields != 0)
1205         {
1206             ExtOut("NumThreadStaticFields: %x\n", vMethodTableFields.wNumThreadStaticFields);
1207         }
1208
1209
1210         if (vMethodTableFields.wContextStaticsSize)
1211         {
1212             ExtOut("ContextStaticOffset: %x\n", vMethodTableFields.wContextStaticOffset);
1213             ExtOut("ContextStaticsSize:  %x\n", vMethodTableFields.wContextStaticsSize);
1214         }
1215
1216     
1217         if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1218         {
1219             DisplayFields(methodTable, &mtdata, &vMethodTableFields, NULL, TRUE, FALSE);
1220         }
1221     }
1222
1223     return Status;
1224 }
1225
1226
1227 /**********************************************************************\
1228 * Routine Description:                                                 *
1229 *                                                                      *
1230 *    This function is called to dump the contents of a MethodTable     *  
1231 *    from a given address                                              *
1232 *                                                                      *
1233 \**********************************************************************/
1234 DECLARE_API(DumpMT)
1235 {
1236     DWORD_PTR dwStartAddr=0;
1237     DWORD_PTR dwOriginalAddr;
1238     
1239     INIT_API();
1240
1241     MINIDUMP_NOT_SUPPORTED();
1242     
1243     BOOL bDumpMDTable = FALSE;
1244     BOOL dml = FALSE;
1245
1246     CMDOption option[] = 
1247     {   // name, vptr, type, hasValue
1248         {"-MD", &bDumpMDTable, COBOOL, FALSE},
1249         {"/d", &dml, COBOOL, FALSE}
1250     };
1251     CMDValue arg[] = 
1252     {   // vptr, type
1253         {&dwStartAddr, COHEX}
1254     };
1255     size_t nArg;
1256     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
1257     {
1258         return Status;
1259     }
1260
1261     EnableDMLHolder dmlHolder(dml);
1262     TableOutput table(2, 16, AlignLeft, false);
1263
1264     if (nArg == 0)
1265     {
1266         Print("Missing MethodTable address\n");
1267         return Status;
1268     }
1269
1270     dwOriginalAddr = dwStartAddr;
1271     dwStartAddr = dwStartAddr&~3;
1272     
1273     if (!IsMethodTable(dwStartAddr))
1274     {
1275         Print(dwOriginalAddr, " is not a MethodTable\n");
1276         return Status;
1277     }
1278  
1279     DacpMethodTableData vMethTable;
1280     vMethTable.Request(g_sos, TO_CDADDR(dwStartAddr));    
1281
1282     if (vMethTable.bIsFree) 
1283     {
1284         Print("Free MethodTable\n");
1285         return Status;
1286     }
1287
1288     table.WriteRow("EEClass:", EEClassPtr(vMethTable.Class));
1289
1290     table.WriteRow("Module:", ModulePtr(vMethTable.Module));
1291
1292     sos::MethodTable mt = (TADDR)dwStartAddr;
1293     table.WriteRow("Name:", mt.GetName());
1294
1295     WCHAR fileName[MAX_PATH];
1296     FileNameForModule(TO_TADDR(vMethTable.Module), fileName);
1297     table.WriteRow("mdToken:", Pointer(vMethTable.cl));
1298     table.WriteRow("File:", fileName[0] ? fileName : L"Unknown Module");
1299
1300     table.WriteRow("BaseSize:", PrefixHex(vMethTable.BaseSize));
1301     table.WriteRow("ComponentSize:", PrefixHex(vMethTable.ComponentSize));
1302     table.WriteRow("Slots in VTable:", Decimal(vMethTable.wNumMethods));
1303     
1304     table.SetColWidth(0, 29);
1305     table.WriteRow("Number of IFaces in IFaceMap:", Decimal(vMethTable.wNumInterfaces));
1306
1307     if (bDumpMDTable)
1308     {
1309         table.ReInit(4, POINTERSIZE_HEX, AlignRight);
1310         table.SetColAlignment(3, AlignLeft);
1311         table.SetColWidth(2, 6);
1312
1313         Print("--------------------------------------\n");
1314         Print("MethodDesc Table\n");
1315
1316         table.WriteRow("Entry", "MethodDesc", "JIT", "Name");
1317
1318         for (DWORD n = 0; n < vMethTable.wNumMethods; n++)
1319         {
1320             JITTypes jitType;
1321             DWORD_PTR methodDesc=0;
1322             DWORD_PTR gcinfoAddr;
1323
1324             CLRDATA_ADDRESS entry;
1325             if (g_sos->GetMethodTableSlot(dwStartAddr, n, &entry) != S_OK)
1326             {
1327                 PrintLn("<error getting slot ", Decimal(n), ">");
1328                 continue;
1329             }
1330
1331             IP2MethodDesc((DWORD_PTR)entry, methodDesc, jitType, gcinfoAddr);
1332             table.WriteColumn(0, entry);
1333             table.WriteColumn(1, MethodDescPtr(methodDesc));
1334
1335             if (jitType == TYPE_UNKNOWN && methodDesc != NULL)
1336             {
1337                 // We can get a more accurate jitType from NativeCodeAddr of the methoddesc,
1338                 // because the methodtable entry hasn't always been patched.
1339                 DacpMethodDescData tmpMethodDescData;
1340                 if (tmpMethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
1341                 {
1342                     DacpCodeHeaderData codeHeaderData;                        
1343                     if (codeHeaderData.Request(g_sos,tmpMethodDescData.NativeCodeAddr) == S_OK)
1344                     {        
1345                         jitType = (JITTypes) codeHeaderData.JITType;
1346                     }
1347                 }
1348             }
1349
1350             const char *pszJitType = "NONE";
1351             if (jitType == TYPE_JIT)
1352                 pszJitType = "JIT";
1353             else if (jitType == TYPE_PJIT)
1354                 pszJitType = "PreJIT";
1355             else
1356             {
1357                 DacpMethodDescData MethodDescData;
1358                 if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
1359                 {
1360                     // Is it an fcall?
1361                     if ((TO_TADDR(MethodDescData.NativeCodeAddr) >=  TO_TADDR(moduleInfo[MSCORWKS].baseAddr)) &&
1362                         ((TO_TADDR(MethodDescData.NativeCodeAddr) <  TO_TADDR(moduleInfo[MSCORWKS].baseAddr + moduleInfo[MSCORWKS].size))))
1363                     {
1364                         pszJitType = "FCALL";
1365                     }
1366                 }
1367             }
1368
1369             table.WriteColumn(2, pszJitType);
1370             
1371             NameForMD_s(methodDesc,g_mdName,mdNameLen);                        
1372             table.WriteColumn(3, g_mdName);
1373         }
1374     }
1375     return Status;    
1376 }
1377
1378 extern size_t Align (size_t nbytes);
1379
1380 HRESULT PrintVC(TADDR taMT, TADDR taObject, BOOL bPrintFields = TRUE)
1381 {       
1382     HRESULT Status;
1383     DacpMethodTableData mtabledata;
1384     if ((Status = mtabledata.Request(g_sos, TO_CDADDR(taMT)))!=S_OK)
1385         return Status;
1386     
1387     size_t size = mtabledata.BaseSize;
1388     if ((Status=g_sos->GetMethodTableName(TO_CDADDR(taMT), mdNameLen, g_mdName, NULL))!=S_OK)
1389         return Status;
1390
1391     ExtOut("Name:        %S\n", g_mdName);
1392     DMLOut("MethodTable: %s\n", DMLMethodTable(taMT));
1393     DMLOut("EEClass:     %s\n", DMLClass(mtabledata.Class));
1394     ExtOut("Size:        %d(0x%x) bytes\n", size, size);
1395
1396     FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
1397     ExtOut("File:        %S\n", g_mdName[0] ? g_mdName : L"Unknown Module");
1398
1399     if (bPrintFields)
1400     {
1401         DacpMethodTableFieldData vMethodTableFields;
1402         if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(taMT)))!=S_OK)
1403             return Status;
1404
1405         ExtOut("Fields:\n");
1406
1407         if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1408             DisplayFields(TO_CDADDR(taMT), &mtabledata, &vMethodTableFields, taObject, TRUE, TRUE);
1409     }
1410
1411     return S_OK;
1412 }
1413
1414 void PrintRuntimeTypeInfo(TADDR p_rtObject, const DacpObjectData & rtObjectData)
1415 {
1416     // Get the method table
1417     int iOffset = GetObjFieldOffset(p_rtObject, rtObjectData.MethodTable, L"m_handle");
1418     if (iOffset > 0)
1419     {            
1420         TADDR mtPtr;
1421         if (SUCCEEDED(GetMTOfObject(p_rtObject + iOffset, &mtPtr)))
1422         {
1423             sos::MethodTable mt = mtPtr;
1424             ExtOut("Type Name:   %S\n", mt.GetName());
1425             DMLOut("Type MT:     %s\n", DMLMethodTable(mtPtr));
1426         }                        
1427     }        
1428 }
1429
1430 HRESULT PrintObj(TADDR taObj, BOOL bPrintFields = TRUE)
1431 {
1432     if (!sos::IsObject(taObj, true))
1433     {
1434         ExtOut("<Note: this object has an invalid CLASS field>\n");
1435     }
1436
1437     DacpObjectData objData;
1438     HRESULT Status;
1439     if ((Status=objData.Request(g_sos, TO_CDADDR(taObj))) != S_OK)
1440     {        
1441         ExtOut("Invalid object\n");
1442         return Status;
1443     }
1444
1445     if (objData.ObjectType==OBJ_FREE)
1446     {
1447         ExtOut("Free Object\n");
1448         DWORD_PTR size = (DWORD_PTR)objData.Size;
1449         ExtOut("Size:        %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1450         return S_OK;
1451     }
1452     
1453     sos::Object obj = taObj;
1454     ExtOut("Name:        %S\n", obj.GetTypeName());
1455     DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));
1456
1457     
1458     DacpMethodTableData mtabledata;
1459     if ((Status=mtabledata.Request(g_sos,objData.MethodTable)) == S_OK)
1460     {
1461         DMLOut("EEClass:     %s\n", DMLClass(mtabledata.Class));
1462     }
1463     else        
1464     {
1465         ExtOut("Invalid EEClass address\n");
1466         return Status;
1467     }
1468
1469     if (objData.RCW != NULL)
1470     {
1471         DMLOut("RCW:         %s\n", DMLRCWrapper(objData.RCW));
1472     }
1473     if (objData.CCW != NULL)
1474     {
1475         DMLOut("CCW:         %s\n", DMLCCWrapper(objData.CCW));
1476     }
1477
1478     DWORD_PTR size = (DWORD_PTR)objData.Size;
1479     ExtOut("Size:        %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1480
1481     if (wcscmp(obj.GetTypeName(), L"System.RuntimeType") == 0)
1482     {
1483         PrintRuntimeTypeInfo(taObj, objData);
1484     }
1485
1486     if (wcscmp(obj.GetTypeName(), L"System.RuntimeType+RuntimeTypeCache") == 0)
1487     {
1488         // Get the method table
1489         int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, L"m_runtimeType");
1490         if (iOffset > 0)
1491         {            
1492             TADDR rtPtr;
1493             if (MOVE(rtPtr, taObj + iOffset) == S_OK)
1494             {
1495                 DacpObjectData rtObjectData;
1496                 if ((Status=rtObjectData.Request(g_sos, TO_CDADDR(rtPtr))) != S_OK)
1497                 {        
1498                     ExtOut("Error when reading RuntimeType field\n");
1499                     return Status;
1500                 }
1501
1502                 PrintRuntimeTypeInfo(rtPtr, rtObjectData);
1503             }                        
1504         }        
1505     }
1506
1507     if (objData.ObjectType==OBJ_ARRAY)
1508     {
1509         ExtOut("Array:       Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s",
1510                 objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType));
1511
1512         IfDMLOut(" (<exec cmd=\"!DumpArray /d %p\">Print Array</exec>)", SOS_PTR(taObj));
1513         ExtOut("\n");
1514         
1515         if (objData.ElementType == ELEMENT_TYPE_I1 ||
1516             objData.ElementType == ELEMENT_TYPE_U1 ||
1517             objData.ElementType == ELEMENT_TYPE_CHAR)
1518         {
1519             bool wide = objData.ElementType == ELEMENT_TYPE_CHAR;
1520
1521             // Get the size of the character array, but clamp it to a reasonable length.
1522             TADDR pos = taObj + (2 * sizeof(DWORD_PTR));
1523             DWORD_PTR num;
1524             moveN(num, taObj + sizeof(DWORD_PTR));
1525
1526             if (IsDMLEnabled())
1527                 DMLOut("<exec cmd=\"%s %x L%x\">Content</exec>:     ", (wide) ? "dw" : "db", pos, num);
1528             else
1529                 ExtOut("Content:     ");
1530             CharArrayContent(pos, (ULONG)(num <= 128 ? num : 128), wide);
1531             ExtOut("\n");
1532         }
1533     }
1534     else
1535     {
1536         FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
1537         ExtOut("File:        %S\n", g_mdName[0] ? g_mdName : L"Unknown Module");
1538     }
1539
1540     if (objData.ObjectType == OBJ_STRING)
1541     {
1542         ExtOut("String:      ");
1543         StringObjectContent(taObj);
1544         ExtOut("\n");
1545     }
1546     else if (objData.ObjectType == OBJ_OBJECT)
1547     {
1548         ExtOut("Object\n");
1549     }    
1550
1551     if (bPrintFields)
1552     {
1553         DacpMethodTableFieldData vMethodTableFields;
1554         if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(objData.MethodTable)))!=S_OK)
1555             return Status;
1556
1557         ExtOut("Fields:\n");
1558         if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1559         {
1560             DisplayFields(objData.MethodTable, &mtabledata, &vMethodTableFields, taObj, TRUE, FALSE);
1561         }
1562         else
1563         {
1564             ExtOut("None\n");
1565         }
1566     }
1567
1568     sos::ThinLockInfo lockInfo;
1569     if (obj.GetThinLock(lockInfo))
1570     {
1571         ExtOut("ThinLock owner %x (%p), Recursive %x\n", lockInfo.ThreadId, 
1572             SOS_PTR(lockInfo.ThreadPtr), lockInfo.Recursion);
1573     }
1574     
1575     return S_OK;
1576 }
1577
1578 BOOL IndicesInRange (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank)
1579 {
1580     int i = 0;
1581     if (!ClrSafeInt<int>::subtraction((int)rank, 1, i))
1582     {
1583         ExtOut("<integer underflow>\n");
1584         return FALSE;
1585     }
1586
1587     for (; i >= 0; i--)
1588     {
1589         if (indices[i] >= bounds[i] + lowerBounds[i])
1590         {
1591             if (i == 0)
1592             {
1593                 return FALSE;
1594             }
1595             
1596             indices[i] = lowerBounds[i];
1597             indices[i - 1]++;
1598         }
1599     }
1600
1601     return TRUE;
1602 }
1603
1604 void ExtOutIndices (DWORD * indices, DWORD rank)
1605 {
1606     for (DWORD i = 0; i < rank; i++)
1607     {
1608         ExtOut("[%d]", indices[i]);
1609     }
1610 }
1611
1612 size_t OffsetFromIndices (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank)
1613 {
1614     _ASSERTE(rank >= 0);
1615     size_t multiplier = 1;
1616     size_t offset = 0;
1617     int i = 0;
1618     if (!ClrSafeInt<int>::subtraction((int)rank, 1, i))
1619     {
1620         ExtOut("<integer underflow>\n");
1621         return 0;
1622     }
1623
1624     for (; i >= 0; i--) 
1625     {
1626         DWORD curIndex = indices[i] - lowerBounds[i];
1627         offset += curIndex * multiplier;
1628         multiplier *= bounds[i];
1629     }
1630
1631     return offset;
1632 }
1633 HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint);
1634 #ifdef _DEBUG
1635 HRESULT PrintPermissionSet (TADDR p_PermSet)
1636 {
1637     HRESULT Status = S_OK;
1638
1639     DacpObjectData PermSetData;
1640     if ((Status=PermSetData.Request(g_sos, TO_CDADDR(p_PermSet))) != S_OK)
1641     {        
1642         ExtOut("Invalid object\n");
1643         return Status;
1644     }
1645
1646     
1647     sos::MethodTable mt = TO_TADDR(PermSetData.MethodTable);
1648     if (wcscmp (L"System.Security.PermissionSet", mt.GetName()) != 0 && wcscmp(L"System.Security.NamedPermissionSet", mt.GetName()) != 0)
1649     {
1650         ExtOut("Invalid PermissionSet object\n");
1651         return S_FALSE;
1652     }
1653
1654     ExtOut("PermissionSet object: %p\n", (ULONG64)p_PermSet);
1655     
1656     // Print basic info
1657
1658     // Walk the fields, printing some fields in a special way.
1659
1660     int iOffset = GetObjFieldOffset (p_PermSet, PermSetData.MethodTable, L"m_Unrestricted");
1661     
1662     if (iOffset > 0)        
1663     {
1664         BYTE unrestricted;
1665         MOVE(unrestricted, p_PermSet + iOffset);
1666         if (unrestricted)
1667             ExtOut("Unrestricted: TRUE\n");
1668         else
1669             ExtOut("Unrestricted: FALSE\n");
1670     }
1671
1672     iOffset = GetObjFieldOffset (p_PermSet, PermSetData.MethodTable, L"m_permSet");
1673     if (iOffset > 0)
1674     {
1675         TADDR tbSetPtr;
1676         MOVE(tbSetPtr, p_PermSet + iOffset);
1677         if (tbSetPtr != NULL)
1678         {
1679             DacpObjectData tbSetData;
1680             if ((Status=tbSetData.Request(g_sos, TO_CDADDR(tbSetPtr))) != S_OK)
1681             {        
1682                 ExtOut("Invalid object\n");
1683                 return Status;
1684             }
1685
1686             iOffset = GetObjFieldOffset (tbSetPtr, tbSetData.MethodTable, L"m_Set");
1687             if (iOffset > 0)
1688             {
1689                 DWORD_PTR PermsArrayPtr;
1690                 MOVE(PermsArrayPtr, tbSetPtr + iOffset);
1691                 if (PermsArrayPtr != NULL)
1692                 {
1693                     // Print all the permissions in the array
1694                     DacpObjectData objData;
1695                     if ((Status=objData.Request(g_sos, TO_CDADDR(PermsArrayPtr))) != S_OK)
1696                     {        
1697                         ExtOut("Invalid object\n");
1698                         return Status;
1699                     }
1700                     DumpArrayFlags flags;
1701                     flags.bDetail = TRUE;
1702                     return PrintArray(objData, flags, TRUE);
1703                 }
1704             }
1705
1706             iOffset = GetObjFieldOffset (tbSetPtr, tbSetData.MethodTable, L"m_Obj");
1707             if (iOffset > 0)
1708             {
1709                 DWORD_PTR PermObjPtr;
1710                 MOVE(PermObjPtr, tbSetPtr + iOffset);
1711                 if (PermObjPtr != NULL)
1712                 {
1713                     // Print the permission object
1714                     return PrintObj(PermObjPtr);
1715                 }
1716             }
1717             
1718
1719         }
1720     }
1721     return Status;
1722 }
1723
1724 #endif // _DEBUG
1725
1726 /**********************************************************************\
1727 * Routine Description:                                                 *
1728 *                                                                      *
1729 *    This function is called to dump the contents of an object from a  *  
1730 *    given address
1731 *                                                                      *
1732 \**********************************************************************/
1733 DECLARE_API(DumpArray)
1734 {
1735     INIT_API();
1736
1737     DumpArrayFlags flags;
1738     
1739     MINIDUMP_NOT_SUPPORTED();
1740
1741     BOOL dml = FALSE;
1742     
1743     CMDOption option[] = 
1744     {   // name, vptr, type, hasValue
1745         {"-start", &flags.startIndex, COSIZE_T, TRUE},
1746         {"-length", &flags.Length, COSIZE_T, TRUE},
1747         {"-details", &flags.bDetail, COBOOL, FALSE},
1748         {"-nofields", &flags.bNoFieldsForElement, COBOOL, FALSE},
1749         {"/d", &dml, COBOOL, FALSE},
1750     };
1751     CMDValue arg[] = 
1752     {   // vptr, type
1753         {&flags.strObject, COSTRING}
1754     };
1755     size_t nArg;
1756     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
1757     {
1758         return Status;
1759     }
1760
1761     EnableDMLHolder dmlHolder(dml);
1762     DWORD_PTR p_Object = GetExpression (flags.strObject);
1763     if (p_Object == 0)
1764     {
1765         ExtOut("Invalid parameter %s\n", flags.strObject);
1766         return Status;
1767     }
1768
1769     if (!sos::IsObject(p_Object, true))
1770     {
1771         ExtOut("<Note: this object has an invalid CLASS field>\n");
1772     }
1773     
1774     DacpObjectData objData;
1775     if ((Status=objData.Request(g_sos, TO_CDADDR(p_Object))) != S_OK)
1776     {  
1777         ExtOut("Invalid object\n");
1778         return Status;
1779     }
1780
1781     if (objData.ObjectType != OBJ_ARRAY)
1782     {
1783         ExtOut("Not an array, please use !DumpObj instead\n");
1784         return S_OK;
1785     }
1786     return PrintArray(objData, flags, FALSE);
1787 }
1788
1789
1790 HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint)
1791 {
1792     HRESULT Status = S_OK;
1793
1794     if (objData.dwRank != 1 && (flags.Length != (DWORD_PTR)-1 ||flags.startIndex != 0))
1795     {
1796         ExtOut("For multi-dimension array, length and start index are supported\n");
1797         return S_OK;
1798     }
1799
1800     if (flags.startIndex > objData.dwNumComponents)
1801     {
1802         ExtOut("Start index out of range\n");
1803         return S_OK;
1804     }
1805
1806     if (!flags.bDetail && flags.bNoFieldsForElement)
1807     {
1808         ExtOut("-nofields has no effect unless -details is specified\n");
1809     }
1810     
1811     DWORD i;
1812     if (!isPermSetPrint)
1813     {
1814         // TODO: don't depend on this being a MethodTable
1815         NameForMT_s(TO_TADDR(objData.ElementTypeHandle), g_mdName, mdNameLen);
1816
1817         ExtOut("Name:        %S[", g_mdName);
1818         for (i = 1; i < objData.dwRank; i++)
1819             ExtOut(",");
1820         ExtOut("]\n");
1821         
1822         DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));
1823
1824         {
1825             DacpMethodTableData mtdata;
1826             if (SUCCEEDED(mtdata.Request(g_sos, objData.MethodTable)))
1827             {
1828                 DMLOut("EEClass:     %s\n", DMLClass(mtdata.Class));
1829             }            
1830         }
1831
1832         DWORD_PTR size = (DWORD_PTR)objData.Size;
1833         ExtOut("Size:        %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1834
1835         ExtOut("Array:       Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s\n", 
1836                 objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType));
1837         DMLOut("Element Methodtable: %s\n", DMLMethodTable(objData.ElementTypeHandle));
1838     }
1839
1840     BOOL isElementValueType = IsElementValueType(objData.ElementType);
1841
1842     DWORD dwRankAllocSize;
1843     if (!ClrSafeInt<DWORD>::multiply(sizeof(DWORD), objData.dwRank, dwRankAllocSize))
1844     {
1845         ExtOut("Integer overflow on array rank\n");
1846         return Status;
1847     }
1848
1849     DWORD *lowerBounds = (DWORD *)alloca(dwRankAllocSize);
1850     if (!SafeReadMemory(objData.ArrayLowerBoundsPtr, lowerBounds, dwRankAllocSize, NULL))
1851     {
1852         ExtOut("Failed to read lower bounds info from the array\n");        
1853         return S_OK;
1854     }
1855
1856     DWORD *bounds = (DWORD *)alloca(dwRankAllocSize);
1857     if (!SafeReadMemory (objData.ArrayBoundsPtr, bounds, dwRankAllocSize, NULL))
1858     {
1859         ExtOut("Failed to read bounds info from the array\n");        
1860         return S_OK;
1861     }
1862
1863     //length is only supported for single-dimension array
1864     if (objData.dwRank == 1 && flags.Length != (DWORD_PTR)-1)
1865     {
1866         bounds[0] = min(bounds[0], (DWORD)(flags.Length + flags.startIndex) - lowerBounds[0]);
1867     }
1868     
1869     DWORD *indices = (DWORD *)alloca(dwRankAllocSize);
1870     for (i = 0; i < objData.dwRank; i++)
1871     {
1872         indices[i] = lowerBounds[i];
1873     }
1874
1875     //start index is only supported for single-dimension array
1876     if (objData.dwRank == 1)
1877     {
1878         indices[0] = (DWORD)flags.startIndex;
1879     }
1880     
1881     //Offset should be calculated by OffsetFromIndices. However because of the way 
1882     //how we grow indices, incrementing offset by one happens to match indices in every iteration    
1883     for (size_t offset = OffsetFromIndices (indices, lowerBounds, bounds, objData.dwRank);
1884         IndicesInRange (indices, lowerBounds, bounds, objData.dwRank); 
1885         indices[objData.dwRank - 1]++, offset++)
1886     {      
1887         if (IsInterrupt())
1888         {
1889             ExtOut("interrupted by user\n");
1890             break;
1891         }
1892
1893         TADDR elementAddress = TO_TADDR(objData.ArrayDataPtr + offset * objData.dwComponentSize);
1894         TADDR p_Element = NULL;
1895         if (isElementValueType)
1896         {
1897             p_Element = elementAddress;        
1898         }
1899         else if (!SafeReadMemory (elementAddress, &p_Element, sizeof (p_Element), NULL))
1900         {
1901             ExtOut("Failed to read element at ");        
1902             ExtOutIndices(indices, objData.dwRank);
1903             ExtOut("\n");
1904             continue;
1905         }
1906
1907         if (p_Element)
1908         {
1909             ExtOutIndices(indices, objData.dwRank);
1910
1911             if (isElementValueType)
1912             {
1913                 DMLOut( " %s\n", DMLValueClass(objData.MethodTable, p_Element));
1914             }
1915             else
1916             {
1917                 DMLOut(" %s\n", DMLObject(p_Element));
1918             }
1919         }
1920         else if (!isPermSetPrint)
1921         {
1922             ExtOutIndices(indices, objData.dwRank);
1923             ExtOut(" null\n");
1924         }
1925
1926         if (flags.bDetail)
1927         {
1928             IncrementIndent();
1929             if (isElementValueType)
1930             {
1931                 PrintVC(TO_TADDR(objData.ElementTypeHandle), elementAddress, !flags.bNoFieldsForElement);
1932             }
1933             else if (p_Element != NULL)
1934             {
1935                 PrintObj(p_Element, !flags.bNoFieldsForElement);
1936             }
1937             DecrementIndent();
1938         }
1939     }
1940     
1941     return S_OK;
1942 }
1943
1944 /**********************************************************************\
1945 * Routine Description:                                                 *
1946 *                                                                      *
1947 *    This function is called to dump the contents of an object from a  *  
1948 *    given address
1949 *                                                                      *
1950 \**********************************************************************/
1951 DECLARE_API(DumpObj)    
1952 {
1953     INIT_API();
1954
1955     MINIDUMP_NOT_SUPPORTED();    
1956
1957     BOOL dml = FALSE;
1958     BOOL bNoFields = FALSE;
1959     BOOL bRefs = FALSE;
1960     StringHolder str_Object;
1961     CMDOption option[] = 
1962     {   // name, vptr, type, hasValue
1963         {"-nofields", &bNoFields, COBOOL, FALSE},
1964         {"-refs", &bRefs, COBOOL, FALSE},
1965         {"/d", &dml, COBOOL, FALSE},
1966     };
1967     CMDValue arg[] = 
1968     {   // vptr, type
1969         {&str_Object.data, COSTRING}
1970     };
1971     size_t nArg;
1972     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
1973     {
1974         return Status;
1975     }
1976     
1977     DWORD_PTR p_Object = GetExpression(str_Object.data);
1978     EnableDMLHolder dmlHolder(dml);
1979     if (p_Object == 0)
1980     {
1981         ExtOut("Invalid parameter %s\n", args);
1982         return Status;
1983     }
1984
1985     Status = PrintObj(p_Object, !bNoFields);
1986     
1987     if (SUCCEEDED(Status) && bRefs)
1988     {
1989         ExtOut("GC Refs:\n");
1990         TableOutput out(2, POINTERSIZE_HEX, AlignRight, 4);
1991         out.WriteRow("offset", "object");
1992         for (sos::RefIterator itr(TO_TADDR(p_Object)); itr; ++itr)
1993             out.WriteRow(Hex(itr.GetOffset()), ObjectPtr(*itr));
1994     }
1995     
1996     return Status;
1997 }
1998
1999 CLRDATA_ADDRESS isExceptionObj(CLRDATA_ADDRESS mtObj)
2000 {
2001     // We want to follow back until we get the mt for System.Exception
2002     DacpMethodTableData dmtd;
2003     CLRDATA_ADDRESS walkMT = mtObj;
2004     while(walkMT != NULL)
2005     {
2006         if (dmtd.Request(g_sos, walkMT) != S_OK)
2007         {
2008             break;            
2009         }
2010         if (walkMT == g_special_usefulGlobals.ExceptionMethodTable)
2011         {
2012             return walkMT;
2013         }
2014         walkMT = dmtd.ParentMethodTable;
2015     }
2016     return NULL;
2017 }
2018
2019 CLRDATA_ADDRESS isSecurityExceptionObj(CLRDATA_ADDRESS mtObj)
2020 {
2021     // We want to follow back until we get the mt for System.Exception
2022     DacpMethodTableData dmtd;
2023     CLRDATA_ADDRESS walkMT = mtObj;
2024     while(walkMT != NULL)
2025     {
2026         if (dmtd.Request(g_sos, walkMT) != S_OK)
2027         {
2028             break;            
2029         }
2030         NameForMT_s(TO_TADDR(walkMT), g_mdName, mdNameLen);                
2031         if (wcscmp(L"System.Security.SecurityException", g_mdName) == 0)
2032         {
2033             return walkMT;
2034         }
2035         walkMT = dmtd.ParentMethodTable;
2036     }
2037     return NULL;
2038 }
2039
2040 // Fill the passed in buffer with a text header for generated exception information.
2041 // Returns the number of characters in the wszBuffer array on exit.
2042 // If NULL is passed for wszBuffer, just returns the number of characters needed.
2043 size_t AddExceptionHeader (__out_ecount (bufferLength) __out_opt WCHAR *wszBuffer, size_t bufferLength)
2044 {
2045 #ifdef _TARGET_WIN64_
2046     const WCHAR *wszHeader = L"    SP               IP               Function\n";
2047 #else
2048     const WCHAR *wszHeader = L"    SP       IP       Function\n";
2049 #endif // _TARGET_WIN64_
2050     if (wszBuffer)
2051     {
2052         swprintf_s(wszBuffer, bufferLength, wszHeader);
2053     }
2054     return wcslen(wszHeader);
2055 }
2056
2057 struct StackTraceElement 
2058 {
2059     UINT_PTR        ip;
2060     UINT_PTR        sp;
2061     DWORD_PTR       pFunc;  // MethodDesc
2062 #if defined(FEATURE_EXCEPTIONDISPATCHINFO)
2063     // TRUE if this element represents the last frame of the foreign
2064     // exception stack trace.
2065     BOOL                        fIsLastFrameFromForeignStackTrace;
2066 #endif // defined(FEATURE_EXCEPTIONDISPATCHINFO)
2067
2068 };
2069
2070 #include "sos_stacktrace.h"
2071
2072 class StringOutput
2073 {
2074 public:
2075     CQuickString cs;
2076     StringOutput()
2077     {
2078         cs.Alloc(1024);
2079         cs.String()[0] = L'\0';
2080     }
2081
2082     BOOL Append(__in_z LPWSTR pszStr)
2083     {
2084         size_t iInputLen = wcslen (pszStr);        
2085         size_t iCurLen = wcslen (cs.String());
2086         if ((iCurLen + iInputLen + 1) > cs.Size())
2087         {
2088             if (cs.ReSize(iCurLen + iInputLen + 1) != S_OK)
2089             {
2090                 return FALSE;
2091             }
2092         }
2093
2094         wcsncat_s (cs.String(), cs.Size(), pszStr, _TRUNCATE);
2095         return TRUE;
2096     }
2097     
2098     size_t Length()
2099     {
2100         return wcslen(cs.String());
2101     }
2102
2103     WCHAR *String()
2104     {
2105         return cs.String();
2106     }
2107 };
2108
2109 static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp, ULONG64 IPAddr, StringOutput& so);
2110
2111 // Using heuristics to determine if an exception object represented an async (hardware) or a 
2112 // managed exception
2113 // We need to use these heuristics when the System.Exception object is not the active exception
2114 // on some thread, but it's something found somewhere on the managed heap.
2115
2116 // uses the MapWin32FaultToCOMPlusException to figure out how we map async exceptions
2117 // to managed exceptions and their HRESULTs
2118 static const HRESULT AsyncHResultValues[] =
2119 {
2120     COR_E_ARITHMETIC,    // kArithmeticException
2121     COR_E_OVERFLOW,      // kOverflowException
2122     COR_E_DIVIDEBYZERO,  // kDivideByZeroException
2123     COR_E_FORMAT,        // kFormatException
2124     COR_E_NULLREFERENCE, // kNullReferenceException
2125     E_POINTER,           // kAccessViolationException
2126     // the EE is raising the next exceptions more often than the OS will raise an async 
2127     // exception for these conditions, so in general treat these as Synchronous
2128       // COR_E_INDEXOUTOFRANGE, // kIndexOutOfRangeException
2129       // COR_E_OUTOFMEMORY,   // kOutOfMemoryException
2130       // COR_E_STACKOVERFLOW, // kStackOverflowException
2131     COR_E_DATAMISALIGNED, // kDataMisalignedException
2132     
2133 };
2134 BOOL IsAsyncException(TADDR taObj, TADDR mtObj)
2135 {
2136     // by default we'll treat exceptions as synchronous
2137     UINT32 xcode = EXCEPTION_COMPLUS;
2138     int iOffset = GetObjFieldOffset (taObj, mtObj, L"_xcode");
2139     if (iOffset > 0)
2140     {
2141         HRESULT hr = MOVE(xcode, taObj + iOffset);
2142         if (hr != S_OK)
2143         {
2144             xcode = EXCEPTION_COMPLUS;
2145             goto Done;
2146         }
2147     }
2148
2149     if (xcode == EXCEPTION_COMPLUS)
2150     {
2151         HRESULT ehr = 0;
2152         iOffset = GetObjFieldOffset (taObj, mtObj, L"_HResult");
2153         if (iOffset > 0)
2154         {
2155             HRESULT hr = MOVE(ehr, taObj + iOffset);
2156             if (hr != S_OK)
2157             {
2158                 xcode = EXCEPTION_COMPLUS;
2159                 goto Done;
2160             }
2161             for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
2162             {
2163                 if (ehr == AsyncHResultValues[idx])
2164                 {
2165                     xcode = ehr;
2166                     break;
2167                 }
2168             }
2169         }
2170     }
2171 Done:
2172     return xcode != EXCEPTION_COMPLUS;
2173 }
2174
2175 // Overload that mirrors the code above when the ExceptionObjectData was already retrieved from LS
2176 BOOL IsAsyncException(const DacpExceptionObjectData & excData)
2177 {
2178     if (excData.XCode != EXCEPTION_COMPLUS)
2179         return TRUE;
2180
2181     HRESULT ehr = excData.HResult;
2182     for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
2183     {
2184         if (ehr == AsyncHResultValues[idx])
2185         {
2186             return TRUE;
2187         }
2188     }
2189
2190     return FALSE;
2191 }
2192
2193
2194 #define SOS_STACKTRACE_SHOWEXPLICITFRAMES  0x00000002
2195 size_t FormatGeneratedException (DWORD_PTR dataPtr, 
2196     UINT bytes, 
2197     __out_ecount (bufferLength) __out_opt WCHAR *wszBuffer, 
2198     size_t bufferLength, 
2199     BOOL bAsync,
2200     BOOL bNestedCase=FALSE,
2201     BOOL bLineNumbers=FALSE)
2202 {
2203     UINT count = bytes / sizeof(StackTraceElement);
2204     size_t Length = 0;
2205
2206     if (wszBuffer && bufferLength>0)
2207     {
2208         wszBuffer[0] = L'\0';
2209     }
2210     
2211     // Buffer is calculated for sprintf below ("   %p %p %S\n");
2212     WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2 + MAX_PATH + 8];
2213
2214     if (count==0)
2215     {
2216         return 0;
2217     }
2218     
2219     if (bNestedCase)
2220     {
2221         // If we are computing the call stack for a nested exception, we
2222         // don't want to print the last frame, because the outer exception
2223         // will have that frame.
2224         count--;
2225     }
2226     
2227     for (UINT i = 0; i < count; i++)
2228     {
2229         StackTraceElement ste;
2230         MOVE (ste, dataPtr + i*sizeof(StackTraceElement));
2231
2232         // ste.ip must be adjusted because of an ancient workaround in the exception 
2233         // infrastructure. The workaround is that the exception needs to have
2234         // an ip address that will map to the line number where the exception was thrown.
2235         // (It doesn't matter that it's not a valid instruction). (see /vm/excep.cpp)
2236         //
2237         // This "counterhack" is not 100% accurate
2238         // The biggest issue is that !PrintException must work with exception objects 
2239         // that may not be currently active; as a consequence we cannot rely on the 
2240         // state of some "current thread" to infer whether the IP values stored in 
2241         // the exception object have been adjusted or not. If we could, we may examine 
2242         // the topmost "Frame" and make the decision based on whether it's a 
2243         // FaultingExceptionFrame or not.
2244         // 1. On IA64 the IP values are never adjusted by the EE so there's nothing 
2245         //    to adjust back.
2246         // 2. On AMD64:
2247         //    (a) if the exception was an async (hardware) exception add 1 to all 
2248         //        IP values in the exception object
2249         //    (b) if the exception was a managed exception (either raised by the 
2250         //        EE or thrown by managed code) do not adjust any IP values
2251         // 3. On X86:
2252         //    (a) if the exception was an async (hardware) exception add 1 to 
2253         //        all but the topmost IP value in the exception object
2254         //    (b) if the exception was a managed exception (either raised by 
2255         //        the EE or thrown by managed code) add 1 to all IP values in 
2256         //        the exception object
2257 #if defined(_TARGET_AMD64_)
2258         if (bAsync)
2259         {
2260             ste.ip += 1;
2261         }
2262 #elif defined(_TARGET_X86_)
2263         if (IsDbgTargetX86() && (!bAsync || i != 0))
2264         {
2265             ste.ip += 1;
2266         }
2267 #endif // defined(_TARGET_AMD64_) || defined(_TARGET__X86_)
2268
2269         StringOutput so;
2270         HRESULT Status = DumpMDInfoBuffer(ste.pFunc, SOS_STACKTRACE_SHOWADDRESSES|SOS_STACKTRACE_SHOWEXPLICITFRAMES, ste.sp, ste.ip, so);
2271
2272         // If DumpMDInfoBuffer failed (due to out of memory or missing metadata), 
2273         // or did not update so (when ste is an explicit frames), do not update wszBuffer
2274         if (Status == S_OK)
2275         {
2276             char filename[MAX_PATH+1] = "";
2277             ULONG linenum = 0;
2278             if (bLineNumbers
2279                     && FAILED(GetLineByOffset(TO_CDADDR(ste.ip), 
2280                                      &linenum,
2281                                      filename,
2282                                      _countof(filename))))
2283             {
2284                 bLineNumbers = FALSE;
2285             }
2286
2287             if (!bLineNumbers)
2288             {
2289                 swprintf_s(wszLineBuffer, _countof(wszLineBuffer), L"    %s\n", so.String());
2290             }
2291             else
2292             {
2293                 swprintf_s(wszLineBuffer, _countof(wszLineBuffer), L"    %s [%S @ %d]\n", so.String(), filename, linenum);
2294             }
2295
2296             Length += wcslen(wszLineBuffer);
2297
2298             if (wszBuffer)
2299             {
2300                 wcsncat_s(wszBuffer, bufferLength, wszLineBuffer, _TRUNCATE);
2301             }
2302         }
2303     }
2304
2305     return Length;
2306 }
2307
2308 // ExtOut has an internal limit for the string size
2309 void SosExtOutLargeString(__inout_z __inout_ecount_opt(len) WCHAR * pwszLargeString, size_t len)
2310 {
2311     const size_t chunkLen = 2048;
2312
2313     WCHAR *pwsz = pwszLargeString;  // beginning of a chunk
2314     size_t count = len/chunkLen;
2315     // write full chunks
2316     for (size_t idx = 0; idx < count; ++idx)
2317     {
2318         WCHAR *pch = pwsz + chunkLen; // after the chunk
2319         // zero terminate the chunk
2320         WCHAR ch = *pch;
2321         *pch = L'\0';
2322
2323         ExtOut("%S", pwsz);
2324
2325         // restore whacked char
2326         *pch = ch;
2327
2328         // advance to next chunk
2329         pwsz += chunkLen;
2330     }
2331
2332     // last chunk
2333     ExtOut("%S", pwsz);
2334 }
2335
2336 HRESULT FormatException(TADDR taObj, BOOL bLineNumbers = FALSE)
2337 {
2338     HRESULT Status = S_OK;
2339
2340     DacpObjectData objData;
2341     if ((Status=objData.Request(g_sos, TO_CDADDR(taObj))) != S_OK)
2342     {        
2343         ExtOut("Invalid object\n");
2344         return Status;
2345     }
2346
2347     // Make sure it is an exception object, and get the MT of Exception
2348     CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable);
2349     if (exceptionMT == NULL)
2350     {
2351         ExtOut("Not a valid exception object\n");
2352         return Status;
2353     }
2354
2355     DMLOut("Exception object: %s\n", DMLObject(taObj));
2356     
2357     if (NameForMT_s(TO_TADDR(objData.MethodTable), g_mdName, mdNameLen))
2358     {
2359         ExtOut("Exception type:   %S\n", g_mdName);
2360     }
2361     else
2362     {
2363         ExtOut("Exception type:   <Unknown>\n");
2364     }
2365
2366     // Print basic info
2367
2368     // First try to get exception object data using ISOSDacInterface2
2369     DacpExceptionObjectData excData;
2370     BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, TO_CDADDR(taObj)));
2371
2372     // Walk the fields, printing some fields in a special way.
2373     // HR, InnerException, Message, StackTrace, StackTraceString
2374
2375     {
2376         TADDR taMsg;
2377         if (bGotExcData)
2378         {
2379             taMsg = TO_TADDR(excData.Message);
2380         }
2381         else
2382         {
2383             int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, L"_message");
2384             if (iOffset > 0)
2385             {
2386                 MOVE (taMsg, taObj + iOffset);
2387             }
2388         }
2389
2390         ExtOut("Message:          ");
2391
2392         if (taMsg)
2393             StringObjectContent(taMsg);
2394         else
2395             ExtOut("<none>");
2396
2397         ExtOut("\n");
2398     }
2399
2400     {
2401         TADDR taInnerExc;
2402         if (bGotExcData)
2403         {
2404             taInnerExc = TO_TADDR(excData.InnerException);
2405         }
2406         else
2407         {
2408             int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, L"_innerException");
2409             if (iOffset > 0)
2410             {
2411                 MOVE (taInnerExc, taObj + iOffset);
2412             }
2413         }
2414
2415         ExtOut("InnerException:   ");
2416         if (taInnerExc)
2417         {
2418             TADDR taMT;
2419             if (SUCCEEDED(GetMTOfObject(taInnerExc, &taMT)))
2420             {
2421                 NameForMT_s(taMT, g_mdName, mdNameLen);                
2422                 ExtOut("%S, ", g_mdName);
2423                 if (IsDMLEnabled())
2424                     DMLOut("Use <exec cmd=\"!PrintException /d %p\">!PrintException %p</exec> to see more.\n", taInnerExc, taInnerExc);
2425                 else
2426                     ExtOut("Use !PrintException %p to see more.\n", SOS_PTR(taInnerExc));
2427             }
2428             else
2429             {
2430                 ExtOut("<invalid MethodTable of inner exception>");
2431             }
2432         }
2433         else
2434         {
2435             ExtOut("<none>\n");
2436         }
2437     }
2438
2439     BOOL bAsync = bGotExcData ? IsAsyncException(excData)
2440                               : IsAsyncException(taObj, TO_TADDR(objData.MethodTable));
2441
2442     {
2443         TADDR taStackTrace;
2444         if (bGotExcData)
2445         {
2446             taStackTrace = TO_TADDR(excData.StackTrace);
2447         }
2448         else
2449         {
2450             int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, L"_stackTrace");
2451             if (iOffset > 0)        
2452             {
2453                 MOVE(taStackTrace, taObj + iOffset);
2454             }
2455         }
2456
2457         ExtOut("StackTrace (generated):\n");
2458         if (taStackTrace)
2459         {
2460             DWORD arrayLen;
2461             HRESULT hr = MOVE(arrayLen, taStackTrace + sizeof(DWORD_PTR));
2462
2463             if (arrayLen != 0 && hr == S_OK)
2464             {
2465 #ifdef _TARGET_WIN64_
2466                 DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD);
2467 #else
2468                 DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD);
2469 #endif // _TARGET_WIN64_
2470                 size_t stackTraceSize = 0;
2471                 MOVE (stackTraceSize, dataPtr);
2472
2473                 DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement));
2474                 dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data
2475             
2476                 if (stackTraceSize == 0)
2477                 {
2478                     ExtOut("Unable to decipher generated stack trace\n");
2479                 }
2480                 else
2481                 {
2482                     size_t iHeaderLength = AddExceptionHeader (NULL, 0);
2483                     size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, FALSE, bLineNumbers);
2484                     WCHAR *pwszBuffer = new NOTHROW WCHAR[iHeaderLength + iLength + 1];
2485                     if (pwszBuffer)
2486                     {
2487                         AddExceptionHeader(pwszBuffer, iHeaderLength + 1);
2488                         FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer + iHeaderLength, iLength + 1, bAsync, FALSE, bLineNumbers);
2489                         SosExtOutLargeString(pwszBuffer, iHeaderLength + iLength + 1);
2490                         delete[] pwszBuffer;
2491                     }
2492                     ExtOut("\n");
2493                 }
2494             }
2495             else
2496             {
2497                 ExtOut("<Not Available>\n");
2498             }
2499         }                   
2500         else
2501         {
2502             ExtOut("<none>\n");
2503         }
2504     }
2505
2506     {
2507         TADDR taStackString;
2508         if (bGotExcData)
2509         {
2510             taStackString = TO_TADDR(excData.StackTraceString);
2511         }
2512         else
2513         {
2514             int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, L"_stackTraceString");
2515             MOVE (taStackString, taObj + iOffset);
2516         }
2517
2518         ExtOut("StackTraceString: ");
2519         if (taStackString)
2520         {
2521             StringObjectContent(taStackString);
2522             ExtOut("\n\n"); // extra newline looks better
2523         }
2524         else
2525         {
2526             ExtOut("<none>\n");
2527         }
2528     }
2529
2530     {
2531         DWORD hResult;
2532         if (bGotExcData)
2533         {
2534             hResult = excData.HResult;
2535         }
2536         else
2537         {
2538             int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, L"_HResult");
2539             MOVE (hResult, taObj + iOffset);
2540         }
2541
2542         ExtOut("HResult: %lx\n", hResult);
2543     }
2544
2545     if (isSecurityExceptionObj(objData.MethodTable) != NULL)
2546     {
2547         // We have a SecurityException Object: print out the debugString if present
2548         int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, L"m_debugString");
2549         if (iOffset > 0)        
2550         {
2551             TADDR taDebugString;
2552             MOVE (taDebugString, taObj + iOffset);                
2553             
2554             if (taDebugString)
2555             {
2556                 ExtOut("SecurityException Message: ");
2557                 StringObjectContent(taDebugString);
2558                 ExtOut("\n\n"); // extra newline looks better
2559             }
2560         }            
2561     }
2562
2563     return Status;
2564 }
2565
2566 DECLARE_API(PrintException)
2567 {
2568     INIT_API();
2569     
2570     BOOL dml = FALSE;
2571     BOOL bShowNested = FALSE;   
2572     BOOL bLineNumbers = FALSE;
2573     BOOL bCCW = FALSE;
2574     StringHolder strObject;
2575     CMDOption option[] = 
2576     {   // name, vptr, type, hasValue
2577         {"-nested", &bShowNested, COBOOL, FALSE},
2578         {"-lines", &bLineNumbers, COBOOL, FALSE},
2579         {"-l", &bLineNumbers, COBOOL, FALSE},
2580         {"-ccw", &bCCW, COBOOL, FALSE},
2581         {"/d", &dml, COBOOL, FALSE}
2582     };
2583     CMDValue arg[] = 
2584     {   // vptr, type
2585         {&strObject, COSTRING}
2586     };
2587     size_t nArg;
2588     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
2589     {
2590         return Status;
2591     }
2592     if (bLineNumbers)
2593     {
2594         ULONG symlines = 0;
2595         if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
2596         {
2597             symlines &= SYMOPT_LOAD_LINES;
2598         }
2599         if (symlines == 0)
2600         {
2601             ExtOut("In order for the option -lines to enable display of source information\n"
2602                    "the debugger must be configured to load the line number information from\n"
2603                    "the symbol files. Use the \".lines; .reload\" command to achieve this.\n");
2604             // don't even try
2605             bLineNumbers = FALSE;
2606         }
2607     }
2608
2609     EnableDMLHolder dmlHolder(dml);
2610     DWORD_PTR p_Object = NULL;
2611     if (nArg == 0)
2612     {
2613         if (bCCW)
2614         {
2615             ExtOut("No CCW pointer specified\n");
2616             return Status;
2617         }
2618
2619         // Look at the last exception object on this thread
2620
2621         CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
2622         DacpThreadData Thread;
2623         
2624         if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
2625         {
2626             ExtOut("The current thread is unmanaged\n");
2627             return Status;
2628         }
2629
2630         DWORD_PTR dwAddr = NULL;
2631         if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
2632                             &dwAddr,
2633                             sizeof(dwAddr), NULL)) || (dwAddr==NULL))
2634         {
2635             ExtOut("There is no current managed exception on this thread\n");            
2636         }    
2637         else
2638         {        
2639             p_Object = dwAddr;        
2640         }
2641     }
2642     else
2643     {
2644         p_Object = GetExpression(strObject.data);
2645         if (p_Object == 0)
2646         {
2647             if (bCCW)
2648             {
2649                 ExtOut("Invalid CCW pointer %s\n", args);
2650             }
2651             else
2652             {
2653                 ExtOut("Invalid exception object %s\n", args);
2654             }
2655             return Status;
2656         }
2657
2658         if (bCCW)
2659         {
2660             // check if the address is a CCW pointer and then
2661             // get the exception object from it
2662             DacpCCWData ccwData;
2663             if (ccwData.Request(g_sos, p_Object) == S_OK)
2664             {
2665                 p_Object = TO_TADDR(ccwData.managedObject);
2666             }
2667         }
2668     }
2669
2670     if (p_Object)
2671     {
2672         FormatException(p_Object, bLineNumbers);
2673     }
2674
2675     // Are there nested exceptions?
2676     CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
2677     DacpThreadData Thread;
2678     
2679     if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
2680     {
2681         ExtOut("The current thread is unmanaged\n");
2682         return Status;
2683     }
2684
2685     if (Thread.firstNestedException)
2686     {
2687         if (!bShowNested)
2688         {
2689             ExtOut("There are nested exceptions on this thread. Run with -nested for details\n");
2690             return Status;
2691         }
2692         
2693         CLRDATA_ADDRESS currentNested = Thread.firstNestedException;
2694         do
2695         {
2696             CLRDATA_ADDRESS obj = 0, next = 0;
2697             Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next);
2698
2699             if (Status != S_OK)
2700             {
2701                 ExtOut("Error retrieving nested exception info %p\n", (ULONG64)currentNested);
2702                 return Status;
2703             }
2704
2705             if (IsInterrupt())
2706             {
2707                 ExtOut("<aborted>\n");
2708                 return Status;
2709             }
2710
2711             ExtOut("\nNested exception -------------------------------------------------------------\n");
2712             Status = FormatException((DWORD_PTR) obj, bLineNumbers);
2713             if (Status != S_OK)
2714             {
2715                 return Status;
2716             }
2717             
2718             currentNested = next;
2719         }
2720         while(currentNested != NULL);        
2721     }
2722     return Status;
2723 }
2724
2725
2726 /**********************************************************************\
2727 * Routine Description:                                                 *
2728 *                                                                      *
2729 *    This function is called to dump the contents of an object from a  *  
2730 *    given address
2731 *                                                                      *
2732 \**********************************************************************/
2733 DECLARE_API(DumpVC)
2734 {
2735     INIT_API();
2736     MINIDUMP_NOT_SUPPORTED();    
2737     
2738     DWORD_PTR p_MT = NULL;
2739     DWORD_PTR p_Object = NULL;
2740     BOOL dml = FALSE;
2741
2742     CMDOption option[] = 
2743     {   // name, vptr, type, hasValue
2744         {"/d", &dml, COBOOL, FALSE}
2745     };
2746     CMDValue arg[] = 
2747     {   // vptr, type
2748         {&p_MT, COHEX},
2749         {&p_Object, COHEX},
2750     };
2751     size_t nArg;
2752     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
2753     {
2754         return Status;
2755     }
2756
2757     EnableDMLHolder dmlHolder(dml);
2758     if (nArg!=2)
2759     {
2760         ExtOut("Usage: !DumpVC <Method Table> <Value object start addr>\n");
2761         return Status;
2762     }
2763     
2764     if (!IsMethodTable(p_MT))
2765     {
2766         ExtOut("Not a managed object\n");
2767         return S_OK;
2768     } 
2769
2770     return PrintVC(p_MT, p_Object);
2771 }
2772
2773 #ifdef FEATURE_COMINTEROP
2774
2775 DECLARE_API(DumpRCW)
2776 {
2777     INIT_API();
2778     
2779     BOOL dml = FALSE;
2780     StringHolder strObject;
2781
2782     CMDOption option[] = 
2783     {   // name, vptr, type, hasValue
2784         {"/d", &dml, COBOOL, FALSE}
2785     };
2786     CMDValue arg[] = 
2787     {   // vptr, type
2788         {&strObject, COSTRING}
2789     };
2790     size_t nArg;
2791     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
2792     {
2793         return Status;
2794     }
2795
2796     EnableDMLHolder dmlHolder(dml);
2797     if (nArg == 0)
2798     {
2799         ExtOut("Missing RCW address\n");
2800         return Status;
2801     }
2802     else
2803     {
2804         DWORD_PTR p_RCW = GetExpression(strObject.data);
2805         if (p_RCW == 0)
2806         {
2807             ExtOut("Invalid RCW %s\n", args);
2808         }
2809         else
2810         {
2811             DacpRCWData rcwData;
2812             if ((Status = rcwData.Request(g_sos, p_RCW)) != S_OK)
2813             {
2814                 ExtOut("Error requesting RCW data\n");
2815                 return Status;
2816             }
2817             BOOL isDCOMProxy;
2818             if (FAILED(rcwData.IsDCOMProxy(g_sos, p_RCW, &isDCOMProxy)))
2819             {
2820                 isDCOMProxy = FALSE;
2821             }
2822
2823             DMLOut("Managed object:             %s\n", DMLObject(rcwData.managedObject));
2824             DMLOut("Creating thread:            %p\n", SOS_PTR(rcwData.creatorThread));
2825             ExtOut("IUnknown pointer:           %p\n", SOS_PTR(rcwData.unknownPointer));
2826             ExtOut("COM Context:                %p\n", SOS_PTR(rcwData.ctxCookie));
2827             ExtOut("Managed ref count:          %d\n", rcwData.refCount);
2828             ExtOut("IUnknown V-table pointer :  %p (captured at RCW creation time)\n", SOS_PTR(rcwData.vtablePtr));
2829
2830             ExtOut("Flags:                      %s%s%s%s%s%s%s%s\n", 
2831                 (rcwData.isDisconnected? "IsDisconnected " : ""),
2832                 (rcwData.supportsIInspectable? "SupportsIInspectable " : ""),
2833                 (rcwData.isAggregated? "IsAggregated " : ""),
2834                 (rcwData.isContained? "IsContained " : ""),
2835                 (rcwData.isJupiterObject? "IsJupiterObject " : ""),
2836                 (rcwData.isFreeThreaded? "IsFreeThreaded " : ""),
2837                 (rcwData.identityPointer == TO_CDADDR(p_RCW)? "IsUnique " : ""),
2838                 (isDCOMProxy ? "IsDCOMProxy " : "")
2839                 );
2840
2841             // Jupiter data hidden by default
2842             if (rcwData.isJupiterObject)
2843             {
2844                 ExtOut("IJupiterObject:    %p\n", SOS_PTR(rcwData.jupiterObject));            
2845             }
2846             
2847             ExtOut("COM interface pointers:\n");
2848
2849             ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[rcwData.interfaceCount];
2850             if (pArray == NULL)
2851             {
2852                 ReportOOM();            
2853                 return Status;
2854             }
2855
2856             if ((Status = g_sos->GetRCWInterfaces(p_RCW, rcwData.interfaceCount, pArray, NULL)) != S_OK)
2857             {
2858                 ExtOut("Error requesting COM interface pointers\n");
2859                 return Status;
2860             }
2861
2862             ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "Context", "MT");
2863             for (int i = 0; i < rcwData.interfaceCount; i++)
2864             {
2865                 // Ignore any NULL MethodTable interface cache. At this point only IJupiterObject 
2866                 // is saved as NULL MethodTable at first slot, and we've already printed outs its 
2867                 // value earlier.
2868                 if (pArray[i].methodTable == NULL)
2869                     continue;
2870                 
2871                 NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen);
2872                 
2873                 DMLOut("%p %p %s %S\n", SOS_PTR(pArray[i].interfacePtr), SOS_PTR(pArray[i].comContext), DMLMethodTable(pArray[i].methodTable), g_mdName);
2874             }
2875         }
2876     }
2877
2878     return Status;
2879 }
2880
2881 DECLARE_API(DumpCCW)
2882 {
2883     INIT_API();
2884     
2885     BOOL dml = FALSE;
2886     StringHolder strObject;
2887
2888     CMDOption option[] = 
2889     {   // name, vptr, type, hasValue
2890         {"/d", &dml, COBOOL, FALSE}
2891     };
2892     CMDValue arg[] = 
2893     {   // vptr, type
2894         {&strObject, COSTRING}
2895     };
2896     size_t nArg;
2897     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
2898     {
2899         return Status;
2900     }
2901
2902     EnableDMLHolder dmlHolder(dml);
2903     if (nArg == 0)
2904     {
2905         ExtOut("Missing CCW address\n");
2906         return Status;
2907     }
2908     else
2909     {
2910         DWORD_PTR p_CCW = GetExpression(strObject.data);
2911         if (p_CCW == 0)
2912         {
2913             ExtOut("Invalid CCW %s\n", args);
2914         }
2915         else
2916         {
2917             DacpCCWData ccwData;
2918             if ((Status = ccwData.Request(g_sos, p_CCW)) != S_OK)
2919             {
2920                 ExtOut("Error requesting CCW data\n");
2921                 return Status;
2922             }
2923
2924             if (ccwData.ccwAddress != p_CCW)
2925                 ExtOut("CCW:               %p\n", SOS_PTR(ccwData.ccwAddress));
2926             
2927             DMLOut("Managed object:    %s\n", DMLObject(ccwData.managedObject));
2928             ExtOut("Outer IUnknown:    %p\n", SOS_PTR(ccwData.outerIUnknown));
2929             ExtOut("Ref count:         %d%s\n", ccwData.refCount, ccwData.isNeutered ? " (NEUTERED)" : "");
2930             ExtOut("Flags:             %s%s\n",
2931                 (ccwData.isExtendsCOMObject? "IsExtendsCOMObject " : ""),
2932                 (ccwData.isAggregated? "IsAggregated " : "")
2933                 );
2934                 
2935             // Jupiter information hidden by default
2936             if (ccwData.jupiterRefCount > 0)
2937             {
2938                 ExtOut("Jupiter ref count: %d%s%s%s%s\n", 
2939                     ccwData.jupiterRefCount, 
2940                     (ccwData.isPegged || ccwData.isGlobalPegged) ? ", Pegged by" : "",
2941                     ccwData.isPegged ? " Jupiter " : "",
2942                     (ccwData.isPegged && ccwData.isGlobalPegged) ? "&" : "",
2943                     ccwData.isGlobalPegged ? " CLR " : ""
2944                     );
2945             }
2946             
2947             ExtOut("RefCounted Handle: %p%s\n", 
2948                 SOS_PTR(ccwData.handle), 
2949                 (ccwData.hasStrongRef ? " (STRONG)" : " (WEAK)"));
2950
2951             ExtOut("COM interface pointers:\n");            
2952
2953             ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[ccwData.interfaceCount];
2954             if (pArray == NULL)
2955             {
2956                 ReportOOM();            
2957                 return Status;
2958             }
2959
2960             if ((Status = g_sos->GetCCWInterfaces(p_CCW, ccwData.interfaceCount, pArray, NULL)) != S_OK)
2961             {
2962                 ExtOut("Error requesting COM interface pointers\n");
2963                 return Status;
2964             }
2965
2966             ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "MT", "Type");
2967             for (int i = 0; i < ccwData.interfaceCount; i++)
2968             {
2969                 if (pArray[i].methodTable == NULL)
2970                 {
2971                     wcscpy_s(g_mdName, mdNameLen, L"IDispatch/IUnknown");
2972                 }
2973                 else
2974                 {
2975                     NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen);
2976                 }
2977                 
2978                 DMLOut("%p %s %S\n", pArray[i].interfacePtr, DMLMethodTable(pArray[i].methodTable), g_mdName);
2979             }
2980         }
2981     }
2982
2983     return Status;
2984 }
2985
2986 #endif // FEATURE_COMINTEROP
2987
2988 #ifdef _DEBUG
2989 /**********************************************************************\
2990 * Routine Description:                                                 *
2991 *                                                                      *
2992 *    This function is called to dump the contents of a PermissionSet   *
2993 *    from a given address.                                             * 
2994 *                                                                      *
2995 \**********************************************************************/
2996 /* 
2997     COMMAND: dumppermissionset.
2998     !DumpPermissionSet <PermissionSet object address>
2999
3000     This command allows you to examine a PermissionSet object. Note that you can 
3001     also use DumpObj such an object in greater detail. DumpPermissionSet attempts 
3002     to extract all the relevant information from a PermissionSet that you might be 
3003     interested in when performing Code Access Security (CAS) related debugging.
3004
3005     Here is a simple PermissionSet object:
3006
3007     0:000> !DumpPermissionSet 014615f4 
3008     PermissionSet object: 014615f4
3009     Unrestricted: TRUE
3010
3011     Note that this is an unrestricted PermissionSet object that does not contain 
3012     any individual permissions. 
3013
3014     Here is another example of a PermissionSet object, one that is not unrestricted 
3015     and contains a single permission:
3016
3017     0:003> !DumpPermissionSet 01469fa8 
3018     PermissionSet object: 01469fa8
3019     Unrestricted: FALSE
3020     Name: System.Security.Permissions.ReflectionPermission
3021     MethodTable: 5b731308
3022     EEClass: 5b7e0d78
3023     Size: 12(0xc) bytes
3024      (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.
3025     0.0__b77a5c561934e089\mscorlib.dll)
3026
3027     Fields:
3028           MT    Field   Offset                 Type VT     Attr    Value Name
3029     5b73125c  4001d66        4         System.Int32  0 instance        2 m_flags
3030
3031     Here is another example of an unrestricted PermissionSet, one that contains 
3032     several permissions. The numbers in parentheses before each Permission object 
3033     represents the index of that Permission in the PermissionSet.
3034
3035     0:003> !DumpPermissionSet 01467bd8
3036     PermissionSet object: 01467bd8
3037     Unrestricted: FALSE
3038     [1] 01467e90
3039         Name: System.Security.Permissions.FileDialogPermission
3040         MethodTable: 5b73023c
3041         EEClass: 5b7dfb18
3042         Size: 12(0xc) bytes
3043          (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
3044         Fields:
3045               MT    Field   Offset                 Type VT     Attr    Value Name
3046         5b730190  4001cc2        4         System.Int32  0 instance        1 access
3047     [4] 014682a8
3048         Name: System.Security.Permissions.ReflectionPermission
3049         MethodTable: 5b731308
3050         EEClass: 5b7e0d78
3051         Size: 12(0xc) bytes
3052          (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
3053         Fields:
3054               MT    Field   Offset                 Type VT     Attr    Value Name
3055         5b73125c  4001d66        4         System.Int32  0 instance        0 m_flags
3056     [17] 0146c060
3057         Name: System.Diagnostics.EventLogPermission
3058         MethodTable: 569841c4
3059         EEClass: 56a03e5c
3060         Size: 28(0x1c) bytes
3061          (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3062         Fields:
3063               MT    Field   Offset                 Type VT     Attr    Value Name
3064         5b6d65d4  4003078        4      System.Object[]  0 instance 0146c190 tagNames
3065         5b6c9ed8  4003079        8          System.Type  0 instance 0146c17c permissionAccessType
3066         5b6cd928  400307a       10       System.Boolean  0 instance        0 isUnrestricted
3067         5b6c45f8  400307b        c ...ections.Hashtable  0 instance 0146c1a4 rootTable
3068         5b6c090c  4003077      bfc        System.String  0   static 00000000 computerName
3069         56984434  40030e7       14 ...onEntryCollection  0 instance 00000000 innerCollection
3070     [18] 0146ceb4
3071         Name: System.Net.WebPermission
3072         MethodTable: 5696dfc4
3073         EEClass: 569e256c
3074         Size: 20(0x14) bytes
3075          (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3076         Fields:
3077               MT    Field   Offset                 Type VT     Attr    Value Name
3078         5b6cd928  400238e        c       System.Boolean  0 instance        0 m_Unrestricted
3079         5b6cd928  400238f        d       System.Boolean  0 instance        0 m_UnrestrictedConnect
3080         5b6cd928  4002390        e       System.Boolean  0 instance        0 m_UnrestrictedAccept
3081         5b6c639c  4002391        4 ...ections.ArrayList  0 instance 0146cf3c m_connectList
3082         5b6c639c  4002392        8 ...ections.ArrayList  0 instance 0146cf54 m_acceptList
3083         569476f8  4002393      8a4 ...Expressions.Regex  0   static 00000000 s_MatchAllRegex
3084     [19] 0146a5fc
3085         Name: System.Net.DnsPermission
3086         MethodTable: 56966408
3087         EEClass: 569d3c08
3088         Size: 12(0xc) bytes
3089          (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3090         Fields:
3091               MT    Field   Offset                 Type VT     Attr    Value Name
3092         5b6cd928  4001d2c        4       System.Boolean  0 instance        1 m_noRestriction
3093     [20] 0146d8ec
3094         Name: System.Web.AspNetHostingPermission
3095         MethodTable: 569831bc
3096         EEClass: 56a02ccc
3097         Size: 12(0xc) bytes
3098          (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3099         Fields:
3100               MT    Field   Offset                 Type VT     Attr    Value Name
3101         56983090  4003074        4         System.Int32  0 instance      600 _level
3102     [21] 0146e394
3103         Name: System.Net.NetworkInformation.NetworkInformationPermission
3104         MethodTable: 5697ac70
3105         EEClass: 569f7104
3106         Size: 16(0x10) bytes
3107          (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3108         Fields:
3109               MT    Field   Offset                 Type VT     Attr    Value Name
3110         5697ab38  4002c34        4         System.Int32  0 instance        0 access
3111         5b6cd928  4002c35        8       System.Boolean  0 instance        0 unrestricted
3112
3113
3114     The abbreviation !dps can be used for brevity.
3115
3116     \\
3117 */
3118 DECLARE_API(DumpPermissionSet)
3119 {
3120     INIT_API();
3121     MINIDUMP_NOT_SUPPORTED();    
3122     
3123     DWORD_PTR p_Object = NULL;
3124
3125     CMDValue arg[] = 
3126     {
3127         {&p_Object, COHEX}
3128     };
3129     size_t nArg;
3130     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) 
3131     {
3132         return Status;
3133     }
3134     if (nArg!=1)
3135     {
3136         ExtOut("Usage: !DumpPermissionSet <PermissionSet object addr>\n");
3137         return Status;
3138     }
3139     
3140
3141     return PrintPermissionSet(p_Object);
3142 }
3143
3144 #endif // _DEBUG
3145
3146 void GCPrintGenerationInfo(DacpGcHeapDetails &heap);
3147 void GCPrintSegmentInfo(DacpGcHeapDetails &heap, DWORD_PTR &total_size);
3148
3149 void DisplayInvalidStructuresMessage()
3150 {
3151     ExtOut("The garbage collector data structures are not in a valid state for traversal.\n");
3152     ExtOut("It is either in the \"plan phase,\" where objects are being moved around, or\n");
3153     ExtOut("we are at the initialization or shutdown of the gc heap. Commands related to \n");
3154     ExtOut("displaying, finding or traversing objects as well as gc heap segments may not \n");
3155     ExtOut("work properly. !dumpheap and !verifyheap may incorrectly complain of heap \n");
3156     ExtOut("consistency errors.\n");
3157 }
3158
3159 /**********************************************************************\
3160 * Routine Description:                                                 *
3161 *                                                                      *
3162 *    This function dumps GC heap size.                                 *  
3163 *                                                                      *
3164 \**********************************************************************/
3165 DECLARE_API(EEHeap)
3166 {
3167     INIT_API();
3168     MINIDUMP_NOT_SUPPORTED();    
3169     
3170     BOOL dml = FALSE;
3171     BOOL showgc = FALSE;
3172     BOOL showloader = FALSE;
3173
3174     CMDOption option[] = 
3175     {   // name, vptr, type, hasValue
3176         {"-gc", &showgc, COBOOL, FALSE},
3177         {"-loader", &showloader, COBOOL, FALSE},
3178         {"/d", &dml, COBOOL, FALSE},
3179     };
3180
3181     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
3182     {
3183         return Status;
3184     }
3185
3186     EnableDMLHolder dmlHolder(dml);
3187     if (showloader || !showgc)
3188     {
3189         // Loader heap.
3190         DWORD_PTR allHeapSize = 0;
3191         DWORD_PTR wasted = 0;    
3192         DacpAppDomainStoreData adsData;
3193         if ((Status=adsData.Request(g_sos))!=S_OK)
3194         {
3195             ExtOut("Unable to get AppDomain information\n");
3196             return Status;
3197         }
3198
3199         // The first one is the system domain.
3200         ExtOut("Loader Heap:\n");
3201         IfFailRet(PrintDomainHeapInfo("System Domain", adsData.systemDomain, &allHeapSize, &wasted));
3202         IfFailRet(PrintDomainHeapInfo("Shared Domain", adsData.sharedDomain, &allHeapSize, &wasted));
3203         
3204         ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
3205
3206         if (pArray==NULL)
3207         {
3208             ReportOOM();            
3209             return Status;
3210         }
3211
3212         if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
3213         {
3214             ExtOut("Unable to get the array of all AppDomains.\n");
3215             return Status;
3216         }
3217
3218         for (int n=0;n<adsData.DomainCount;n++)
3219         {
3220             if (IsInterrupt())
3221                 break;
3222
3223             char domain[16];
3224             sprintf_s(domain, _countof(domain), "Domain %d", n+1);
3225
3226             IfFailRet(PrintDomainHeapInfo(domain, pArray[n], &allHeapSize, &wasted));
3227
3228         }
3229
3230         // Jit code heap
3231         ExtOut("--------------------------------------\n");
3232         ExtOut("Jit code heap:\n");
3233
3234         if (IsMiniDumpFile())
3235         {
3236             ExtOut("<no information>\n");
3237         }
3238         else
3239         {
3240             allHeapSize += JitHeapInfo();
3241         }
3242
3243     
3244         // Module Data
3245         {
3246             int numModule;
3247             ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(NULL, &numModule);   
3248             if (moduleList == NULL)
3249             {
3250                 ExtOut("Failed to request module list.\n");
3251             }
3252             else
3253             {
3254                 // Module Thunk Heaps
3255                 ExtOut("--------------------------------------\n");
3256                 ExtOut("Module Thunk heaps:\n");
3257                 allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_ThunkHeap, &wasted);
3258
3259                 // Module Lookup Table Heaps
3260                 ExtOut("--------------------------------------\n");
3261                 ExtOut("Module Lookup Table heaps:\n");
3262                 allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_LookupTableHeap, &wasted);
3263             }
3264         }
3265
3266         ExtOut("--------------------------------------\n");
3267         ExtOut("Total LoaderHeap size:   ");
3268         PrintHeapSize(allHeapSize, wasted);
3269         ExtOut("=======================================\n");
3270     }
3271
3272     if (showgc || !showloader)
3273     {   
3274         // GC Heap
3275         DWORD dwNHeaps = 1;
3276
3277         if (!GetGcStructuresValid())
3278         {
3279             DisplayInvalidStructuresMessage();
3280         }
3281         
3282         DacpGcHeapData gcheap;
3283         if (gcheap.Request(g_sos) != S_OK)
3284         {
3285             ExtOut("Error requesting GC Heap data\n");
3286             return Status;
3287         }
3288
3289         if (gcheap.bServerMode)
3290         {
3291             dwNHeaps = gcheap.HeapCount;
3292         }
3293
3294         ExtOut("Number of GC Heaps: %d\n", dwNHeaps);
3295         DWORD_PTR totalSize = 0;
3296         if (!gcheap.bServerMode)
3297         {
3298             DacpGcHeapDetails heapDetails;
3299             if (heapDetails.Request(g_sos) != S_OK)
3300             {
3301                 ExtOut("Error requesting details\n");
3302                 return Status;
3303             }
3304
3305             GCHeapInfo (heapDetails, totalSize);
3306             ExtOut("Total Size:              ");
3307             PrintHeapSize(totalSize, 0);
3308         }
3309         else
3310         {
3311             DWORD dwAllocSize;
3312             if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
3313             {
3314                 ExtOut("Failed to get GCHeaps: integer overflow\n");
3315                 return Status;
3316             }
3317
3318             CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
3319             if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
3320             {
3321                 ExtOut("Failed to get GCHeaps\n");
3322                 return Status;
3323             }
3324                         
3325             DWORD n;
3326             for (n = 0; n < dwNHeaps; n ++)
3327             {
3328                 DacpGcHeapDetails heapDetails;
3329                 if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
3330                 {
3331                     ExtOut("Error requesting details\n");
3332                     return Status;
3333                 }
3334                 ExtOut("------------------------------\n");
3335                 ExtOut("Heap %d (%p)\n", n, (ULONG64)heapAddrs[n]);
3336                 DWORD_PTR heapSize = 0;
3337                 GCHeapInfo (heapDetails, heapSize);
3338                 totalSize += heapSize;
3339                 ExtOut("Heap Size:       " WIN86_8SPACES);
3340                 PrintHeapSize(heapSize, 0);
3341             }
3342         }
3343         ExtOut("------------------------------\n");
3344         ExtOut("GC Heap Size:    " WIN86_8SPACES);
3345         PrintHeapSize(totalSize, 0);
3346     }
3347     return Status;
3348 }
3349
3350 void PrintGCStat(HeapStat *inStat, const char* label=NULL)
3351 {
3352     if (inStat)
3353     {
3354         bool sorted = false;
3355         try
3356         {
3357             inStat->Sort();
3358             sorted = true;
3359             inStat->Print(label);
3360         }
3361         catch(...)
3362         {
3363             ExtOut("Exception occurred while trying to %s the GC stats.\n", sorted ? "print" : "sort");
3364         }
3365
3366         inStat->Delete();
3367     }
3368 }
3369
3370 DECLARE_API(TraverseHeap)
3371 {
3372     INIT_API();
3373     MINIDUMP_NOT_SUPPORTED();    
3374     
3375     BOOL bXmlFormat = FALSE;
3376     BOOL bVerify = FALSE;
3377     StringHolder Filename;
3378
3379     CMDOption option[] = 
3380     {   // name, vptr,        type, hasValue
3381         {"-xml", &bXmlFormat, COBOOL, FALSE},
3382         {"-verify", &bVerify, COBOOL, FALSE},
3383     };
3384     CMDValue arg[] = 
3385     {   // vptr, type
3386         {&Filename.data, COSTRING},
3387     };
3388     size_t nArg;
3389     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
3390     {
3391         return Status;
3392     }
3393
3394     if (nArg != 1)
3395     {
3396         ExtOut("usage: HeapTraverse [-xml] filename\n");
3397         return Status;
3398     }
3399
3400     if (!g_snapshot.Build())
3401     {
3402         ExtOut("Unable to build snapshot of the garbage collector state\n");
3403         return Status;
3404     }
3405
3406     FILE* file = NULL;
3407     if (fopen_s(&file, Filename.data, "w") != 0) {
3408         ExtOut("Unable to open file\n");
3409         return Status;
3410     }
3411
3412     if (!bVerify)
3413         ExtOut("Assuming a uncorrupted GC heap.  If this is a crash dump consider -verify option\n"); 
3414
3415     HeapTraverser traverser(bVerify != FALSE);
3416
3417     ExtOut("Writing %s format to file %s\n", bXmlFormat ? "Xml" : "CLRProfiler", Filename.data);    
3418     ExtOut("Gathering types...\n");
3419
3420     // TODO: there may be a canonical list of methodtables in the runtime that we can
3421     // traverse instead of exploring the gc heap for that list. We could then simplify the
3422     // tree structure to a sorted list of methodtables, and the index is the ID.
3423
3424     // TODO: "Traversing object members" code should be generalized and shared between
3425     // !gcroot and !traverseheap. Also !dumpheap can begin using GCHeapsTraverse.
3426
3427     if (!traverser.Initialize())
3428     {
3429         ExtOut("Error initializing heap traversal\n");
3430         fclose(file);
3431         return Status;
3432     }
3433
3434     if (!traverser.CreateReport (file, bXmlFormat ? FORMAT_XML : FORMAT_CLRPROFILER))
3435     {
3436         ExtOut("Unable to write heap report\n");
3437         fclose(file);
3438         return Status;
3439     }
3440
3441     fclose(file);                
3442     ExtOut("\nfile %s saved\n", Filename.data);
3443     
3444     return Status;
3445 }
3446
3447 struct PrintRuntimeTypeArgs
3448 {
3449     DWORD_PTR mtOfRuntimeType;
3450     int handleFieldOffset;
3451     DacpAppDomainStoreData adstore;
3452 };
3453
3454 void PrintRuntimeTypes(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable,LPVOID token)
3455 {
3456     PrintRuntimeTypeArgs *pArgs = (PrintRuntimeTypeArgs *)token;
3457
3458     if (pArgs->mtOfRuntimeType == NULL)
3459     {
3460         NameForMT_s(methodTable, g_mdName, mdNameLen);
3461
3462         if (wcscmp(g_mdName, L"System.RuntimeType") == 0)
3463         {
3464             pArgs->mtOfRuntimeType = methodTable;
3465             pArgs->handleFieldOffset = GetObjFieldOffset(objAddr, methodTable, L"m_handle");
3466             if (pArgs->handleFieldOffset <= 0)
3467                 ExtOut("Error getting System.RuntimeType.m_handle offset\n");
3468
3469             pArgs->adstore.Request(g_sos);
3470         }
3471     }
3472
3473     if ((methodTable == pArgs->mtOfRuntimeType) && (pArgs->handleFieldOffset > 0))
3474     {
3475         // Get the method table and display the information.
3476         DWORD_PTR mtPtr;
3477         if (MOVE(mtPtr, objAddr + pArgs->handleFieldOffset) == S_OK)
3478         {
3479             DMLOut(DMLObject(objAddr));
3480
3481             CLRDATA_ADDRESS appDomain = GetAppDomainForMT(mtPtr);
3482             if (appDomain != NULL)
3483             {
3484                 if (appDomain == pArgs->adstore.sharedDomain)
3485                     ExtOut(" %" POINTERSIZE "s", "Shared");
3486
3487                 else if (appDomain == pArgs->adstore.systemDomain)
3488                     ExtOut(" %" POINTERSIZE "s", "System");
3489                 else
3490                     DMLOut(" %s", DMLDomain(appDomain));
3491             }
3492             else
3493             {
3494                 ExtOut(" %" POINTERSIZE "s", "?");
3495             }
3496         
3497             NameForMT_s(mtPtr, g_mdName, mdNameLen);
3498             DMLOut(" %s %S\n", DMLMethodTable(mtPtr), g_mdName);
3499         }
3500     }
3501 }
3502
3503
3504 DECLARE_API(DumpRuntimeTypes)
3505 {
3506     INIT_API();
3507     MINIDUMP_NOT_SUPPORTED();    
3508
3509     BOOL dml = FALSE;
3510
3511     CMDOption option[] = 
3512     {   // name, vptr, type, hasValue
3513         {"/d", &dml, COBOOL, FALSE},
3514     };
3515
3516     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
3517         return Status;
3518
3519     EnableDMLHolder dmlHolder(dml);
3520
3521     ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type Name              \n",
3522            "Address", "Domain", "MT");
3523     ExtOut("------------------------------------------------------------------------------\n");
3524
3525     PrintRuntimeTypeArgs pargs;
3526     ZeroMemory(&pargs, sizeof(PrintRuntimeTypeArgs));
3527
3528     GCHeapsTraverse(PrintRuntimeTypes, (LPVOID)&pargs);
3529     return Status;
3530 }
3531
3532 #define MIN_FRAGMENTATIONBLOCK_BYTES (1024*512)
3533 namespace sos
3534 {
3535     class FragmentationBlock
3536     {
3537     public:
3538         FragmentationBlock(TADDR addr, size_t size, TADDR next, TADDR mt)
3539             : mAddress(addr), mSize(size), mNext(next), mNextMT(mt)
3540         {
3541         }
3542
3543         inline TADDR GetAddress() const
3544         {
3545             return mAddress;
3546         }
3547         inline size_t GetSize() const
3548         {
3549             return mSize;
3550         }
3551
3552         inline TADDR GetNextObject() const
3553         {
3554             return mNext;
3555         }
3556
3557         inline TADDR GetNextMT() const
3558         {
3559             return mNextMT;
3560         }
3561
3562     private:
3563         TADDR mAddress;
3564         size_t mSize;
3565         TADDR mNext;
3566         TADDR mNextMT;
3567     };
3568 }
3569
3570 class DumpHeapImpl
3571 {
3572 public:
3573     DumpHeapImpl(PCSTR args)
3574         : mStart(0), mStop(0), mMT(0),  mMinSize(0), mMaxSize(~0),
3575           mStat(FALSE), mStrings(FALSE), mVerify(FALSE),
3576           mThinlock(FALSE), mShort(FALSE), mDML(FALSE),
3577           mLive(FALSE), mDead(FALSE), mType(NULL)
3578     {
3579         ArrayHolder<char> type = NULL;
3580
3581         TADDR minTemp = 0;
3582         CMDOption option[] = 
3583         {   // name, vptr, type, hasValue
3584             {"-mt", &mMT, COHEX, TRUE},              // dump objects with a given MethodTable
3585             {"-type", &type, COSTRING, TRUE},        // list objects of specified type
3586             {"-stat", &mStat, COBOOL, FALSE},        // dump a summary of types and the number of instances of each
3587             {"-strings", &mStrings, COBOOL, FALSE},  // dump a summary of string objects
3588             {"-verify", &mVerify, COBOOL, FALSE},    // verify heap objects (!heapverify)
3589             {"-thinlock", &mThinlock, COBOOL, FALSE},// list only thinlocks
3590             {"-short", &mShort, COBOOL, FALSE},      // list only addresses
3591             {"-min", &mMinSize, COHEX, TRUE},        // min size of objects to display
3592             {"-max", &mMaxSize, COHEX, TRUE},        // max size of objects to display
3593             {"-live", &mLive, COHEX, FALSE},         // only print live objects
3594             {"-dead", &mDead, COHEX, FALSE},         // only print dead objects
3595             {"/d", &mDML, COBOOL, FALSE},            // Debugger Markup Language
3596         };
3597
3598         CMDValue arg[] = 
3599         {   // vptr, type
3600             {&mStart, COHEX},
3601             {&mStop, COHEX}
3602         };
3603
3604         size_t nArgs = 0;
3605         if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArgs))
3606             sos::Throw<sos::Exception>("Failed to parse command line arguments.");
3607
3608         if (mStart == 0)
3609             mStart = minTemp;
3610
3611         if (mStop == 0)
3612             mStop = sos::GCHeap::HeapEnd;
3613
3614         if (type && mMT)
3615         {
3616             sos::Throw<sos::Exception>("Cannot specify both -mt and -type");
3617         }
3618         
3619         if (mLive && mDead)
3620         {
3621             sos::Throw<sos::Exception>("Cannot specify both -live and -dead.");
3622         }
3623
3624         if (mMinSize > mMaxSize)
3625         {
3626             sos::Throw<sos::Exception>("wrong argument");
3627         }
3628         
3629         // If the user gave us a type, convert it to unicode and clean up "type".
3630         if (type && !mStrings)
3631         {
3632             size_t iLen = strlen(type) + 1;
3633             mType = new WCHAR[iLen];
3634             MultiByteToWideChar(CP_ACP, 0, type, -1, mType, (int)iLen);
3635         }
3636     }
3637
3638     ~DumpHeapImpl()
3639     {
3640         if (mType)
3641             delete [] mType;
3642     }
3643
3644     void Run()
3645     {
3646         // enable Debugger Markup Language
3647         EnableDMLHolder dmlholder(mDML); 
3648         sos::GCHeap gcheap;
3649
3650         if (!gcheap.AreGCStructuresValid())
3651             DisplayInvalidStructuresMessage();
3652         
3653         if (IsMiniDumpFile())
3654         {
3655             ExtOut("In a minidump without full memory, most gc heap structures will not be valid.\n");
3656             ExtOut("If you need this functionality, get a full memory dump with \".dump /ma mydump.dmp\"\n");
3657         }
3658
3659 #ifndef FEATURE_PAL
3660         if (mLive || mDead)
3661         {
3662             GCRootImpl gcroot;
3663             mLiveness = gcroot.GetLiveObjects();
3664         }
3665 #endif
3666
3667         // Some of the "specialty" versions of DumpHeap have slightly
3668         // different implementations than the standard version of DumpHeap.
3669         // We seperate them out to not clutter the standard DumpHeap function.
3670         if (mShort)
3671             DumpHeapShort(gcheap);
3672         else if (mThinlock)
3673             DumpHeapThinlock(gcheap);
3674         else if (mStrings)
3675             DumpHeapStrings(gcheap);
3676         else
3677             DumpHeap(gcheap);
3678
3679         if (mVerify)
3680             ValidateSyncTable(gcheap);
3681     }
3682
3683     static bool ValidateSyncTable(sos::GCHeap &gcheap)
3684     {
3685         bool succeeded = true;
3686         for (sos::SyncBlkIterator itr; itr; ++itr)
3687         {
3688             sos::CheckInterrupt();
3689
3690             if (!itr->IsFree())
3691             {
3692                 if (!sos::IsObject(itr->GetObject(), true))
3693                 {
3694                     ExtOut("SyncBlock %d corrupted, points to invalid object %p\n", 
3695                             itr->GetIndex(), SOS_PTR(itr->GetObject()));
3696                         succeeded = false;
3697                 }
3698                 else
3699                 {
3700                     // Does the object header point to this syncblock index?
3701                     sos::Object obj = itr->GetObject();
3702                     unsigned long header = 0;
3703
3704                     if (!obj.TryGetHeader(header))
3705                     {
3706                         ExtOut("Failed to get object header for object %p while inspecting syncblock at index %d.\n",
3707                                 SOS_PTR(itr->GetObject()), itr->GetIndex());
3708                         succeeded = false;
3709                     }
3710                     else
3711                     {
3712                         bool valid = false;
3713                         if ((header & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0 && (header & BIT_SBLK_IS_HASHCODE) == 0)
3714                         {
3715                             unsigned long index = header & MASK_SYNCBLOCKINDEX;
3716                             valid = (unsigned long)itr->GetIndex() == index;
3717                         }
3718                         
3719                         if (!valid)
3720                         {
3721                             ExtOut("Object header for %p should have a SyncBlock index of %d.\n",
3722                                     SOS_PTR(itr->GetObject()), itr->GetIndex());
3723                             succeeded = false;
3724                         }
3725                     }
3726                 }
3727             }
3728         }
3729
3730         return succeeded;
3731     }
3732 private:
3733     DumpHeapImpl(const DumpHeapImpl &);
3734
3735     bool Verify(const sos::ObjectIterator &itr)
3736     {
3737         if (mVerify)
3738         {
3739             char buffer[1024];
3740             if (!itr.Verify(buffer, _countof(buffer)))
3741             {
3742                 ExtOut(buffer);
3743                 return false;
3744             }
3745         }
3746         
3747         return true;
3748     }
3749
3750     bool IsCorrectType(const sos::Object &obj)
3751     {
3752         if (mMT != NULL)
3753             return mMT == obj.GetMT();
3754
3755         if (mType != NULL)
3756         {
3757             WString name = obj.GetTypeName();
3758             return wcsstr(name.c_str(), mType) != NULL;
3759         }
3760
3761         return true;
3762     }
3763
3764     bool IsCorrectSize(const sos::Object &obj)
3765     {
3766         size_t size = obj.GetSize();
3767         return size >= mMinSize && size <= mMaxSize;
3768     }
3769
3770     bool IsCorrectLiveness(const sos::Object &obj)
3771     {
3772 #ifndef FEATURE_PAL
3773         if (mLive && mLiveness.find(obj.GetAddress()) == mLiveness.end())
3774             return false;
3775
3776         if (mDead && (mLiveness.find(obj.GetAddress()) != mLiveness.end() || obj.IsFree()))
3777             return false;
3778 #endif
3779         return true;
3780     }
3781
3782
3783
3784     inline void PrintHeader()
3785     {
3786         ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s\n", "Address", "MT", "Size");
3787     }
3788
3789     void DumpHeap(sos::GCHeap &gcheap)
3790     {
3791         HeapStat stats;
3792
3793         // For heap fragmentation tracking.
3794         TADDR lastFreeObj = NULL;
3795         size_t lastFreeSize = 0;
3796
3797         if (!mStat)
3798             PrintHeader();
3799
3800         for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
3801         {
3802             if (!Verify(itr))
3803                 return;
3804
3805             bool onLOH = itr.IsCurrObjectOnLOH();
3806
3807             // Check for free objects to report fragmentation
3808             if (lastFreeObj != NULL)
3809                 ReportFreeObject(lastFreeObj, lastFreeSize, itr->GetAddress(), itr->GetMT());
3810
3811             if (!onLOH && itr->IsFree())
3812             {
3813                 lastFreeObj = *itr;
3814                 lastFreeSize = itr->GetSize();
3815             }
3816             else
3817             {
3818                 lastFreeObj = NULL;
3819             }
3820
3821             if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
3822             {
3823                 stats.Add((DWORD_PTR)itr->GetMT(), (DWORD)itr->GetSize());
3824                 if (!mStat)
3825                     DMLOut("%s %s %8d%s\n", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize(), 
3826                                             itr->IsFree() ? " Free":"     ");
3827             }
3828         }
3829
3830         if (!mStat)
3831             ExtOut("\n");
3832
3833         stats.Sort();
3834         stats.Print();
3835
3836         PrintFragmentationReport();
3837     }
3838
3839     struct StringSetEntry
3840     {
3841         StringSetEntry() : count(0), size(0)
3842         {
3843             str[0] = 0;
3844         }
3845         
3846         StringSetEntry(__in_ecount(64) wchar_t tmp[64], size_t _size)
3847             : count(1), size(_size)
3848         {
3849             memcpy(str, tmp, sizeof(str));
3850         }
3851         
3852         void Add(size_t _size) const
3853         {
3854             count++;
3855             size += _size;
3856         }
3857         
3858         mutable size_t count;
3859         mutable size_t size;
3860         wchar_t str[64];
3861         
3862         bool operator<(const StringSetEntry &rhs) const
3863         {
3864             return wcscmp(str, rhs.str) == -1;
3865         }
3866     };
3867
3868     
3869     static bool StringSetCompare(const StringSetEntry &a1, const StringSetEntry &a2)
3870     {
3871         return a1.size < a2.size;
3872     }
3873
3874     void DumpHeapStrings(sos::GCHeap &gcheap)
3875     {
3876 #ifdef FEATURE_PAL
3877         ExtOut("Not implemented.\n");
3878 #else
3879         const int offset = sos::Object::GetStringDataOffset();
3880         typedef std::set<StringSetEntry> Set;
3881         Set set;            // A set keyed off of the string's text
3882         
3883         StringSetEntry tmp;  // Temp string used to keep track of the set
3884         ULONG fetched = 0;
3885
3886         TableOutput out(3, POINTERSIZE_HEX, AlignRight);
3887         for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
3888         {
3889             if (IsInterrupt())
3890                 break;
3891                 
3892             if (itr->IsString() && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
3893             {
3894                 CLRDATA_ADDRESS addr = itr->GetAddress();
3895                 size_t size = itr->GetSize();
3896                 
3897                 if (!mStat)
3898                     out.WriteRow(ObjectPtr(addr), Pointer(itr->GetMT()), Decimal(size));
3899
3900                 // Don't bother calculating the size of the string, just read the full 64 characters of the buffer.  The null
3901                 // terminator we read will terminate the string.
3902                 HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(addr+offset), tmp.str, sizeof(wchar_t)*(_countof(tmp.str)-1), &fetched);
3903                 if (SUCCEEDED(hr))
3904                 {
3905                     // Ensure we null terminate the string.  Note that this will not overrun the buffer as we only
3906                     // wrote a max of 63 characters into the 64 character buffer.
3907                     tmp.str[fetched/sizeof(wchar_t)] = 0;
3908                     Set::iterator sitr = set.find(tmp);
3909                     if (sitr == set.end())
3910                     {
3911                         tmp.size = size;
3912                         tmp.count = 1;
3913                         set.insert(tmp);
3914                     }
3915                     else
3916                     {
3917                         sitr->Add(size);
3918                     }
3919                 }
3920             }
3921         }
3922
3923         ExtOut("\n");
3924
3925         // Now flatten the set into a vector.  This is much faster than keeping two sets, or using a multimap.
3926         typedef std::vector<StringSetEntry> Vect;
3927         Vect v(set.begin(), set.end());
3928         std::sort(v.begin(), v.end(), &DumpHeapImpl::StringSetCompare);
3929
3930         // Now print out the data.  The call to Flatten ensures that we don't print newlines to break up the
3931         // output in strange ways.
3932         for (Vect::iterator vitr = v.begin(); vitr != v.end(); ++vitr)
3933         {
3934             if (IsInterrupt())
3935                 break;
3936                 
3937             Flatten(vitr->str, (unsigned int)wcslen(vitr->str));
3938             out.WriteRow(Decimal(vitr->size), Decimal(vitr->count), vitr->str);
3939         }
3940 #endif
3941     }
3942
3943     void DumpHeapShort(sos::GCHeap &gcheap)
3944     {
3945         for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
3946         {
3947             if (!Verify(itr))
3948                 return;
3949
3950             if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
3951                 DMLOut("%s\n", DMLObject(itr->GetAddress()));
3952         }
3953     }
3954
3955     void DumpHeapThinlock(sos::GCHeap &gcheap)
3956     {
3957         int count = 0;
3958
3959         PrintHeader();
3960         for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
3961         {
3962             if (!Verify(itr))
3963                 return;
3964
3965             sos::ThinLockInfo lockInfo; 
3966             if (IsCorrectType(*itr) && itr->GetThinLock(lockInfo))
3967             {
3968                 DMLOut("%s %s %8d", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize());
3969                 ExtOut(" ThinLock owner %x (%p) Recursive %x\n", lockInfo.ThreadId,
3970                                         (ULONG64)lockInfo.ThreadPtr, lockInfo.Recursion);
3971
3972                 count++;
3973             }
3974         }
3975
3976         ExtOut("Found %d objects.\n", count);
3977     }
3978
3979 private:
3980     TADDR mStart,
3981           mStop,
3982           mMT,
3983           mMinSize,
3984           mMaxSize;
3985
3986     BOOL mStat,
3987          mStrings,
3988          mVerify,
3989          mThinlock,
3990          mShort,
3991          mDML,
3992          mLive,
3993          mDead;
3994
3995
3996     wchar_t *mType;
3997
3998 private:
3999 #if !defined(FEATURE_PAL)
4000     // Windows only
4001     std::hash_set<TADDR> mLiveness;
4002     typedef std::list<sos::FragmentationBlock> FragmentationList;
4003     FragmentationList mFrag;
4004
4005     void InitFragmentationList()
4006     {
4007         mFrag.clear();
4008     }
4009
4010     void ReportFreeObject(TADDR addr, size_t size, TADDR next, TADDR mt)
4011     {
4012         if (size >= MIN_FRAGMENTATIONBLOCK_BYTES)
4013             mFrag.push_back(sos::FragmentationBlock(addr, size, next, mt));
4014     }
4015
4016     void PrintFragmentationReport()
4017     {
4018         if (mFrag.size() > 0)
4019         {
4020             ExtOut("Fragmented blocks larger than 0.5 MB:\n");
4021             ExtOut("%" POINTERSIZE "s %8s %16s\n", "Addr", "Size", "Followed by");
4022  
4023             for (FragmentationList::const_iterator itr = mFrag.begin(); itr != mFrag.end(); ++itr)
4024             {
4025                 sos::MethodTable mt = itr->GetNextMT();
4026                 ExtOut("%p %6.1fMB " WIN64_8SPACES "%p %S\n",
4027                             SOS_PTR(itr->GetAddress()),
4028                             ((double)itr->GetSize()) / 1024.0 / 1024.0,
4029                             SOS_PTR(itr->GetNextObject()),
4030                             mt.GetName());
4031             }
4032         }
4033     }
4034 #else
4035     void InitFragmentationList() {}
4036     void ReportFreeObject(TADDR, TADDR, size_t, TADDR) {}
4037     void PrintFragmentationReport() {}
4038 #endif
4039 };
4040
4041
4042 /**********************************************************************\
4043 * Routine Description:                                                 *
4044 *                                                                      *
4045 *    This function dumps all objects on GC heap. It also displays      *  
4046 *    statistics of objects.  If GC heap is corrupted, it will stop at 
4047 *    the bad place.  (May not work if GC is in progress.)              *
4048 *                                                                      *
4049 \**********************************************************************/
4050 DECLARE_API(DumpHeap)
4051 {
4052     INIT_API();
4053     MINIDUMP_NOT_SUPPORTED();
4054
4055     if (!g_snapshot.Build())
4056     {
4057         ExtOut("Unable to build snapshot of the garbage collector state\n");
4058         return E_FAIL;
4059     }
4060
4061     try
4062     {
4063         DumpHeapImpl dumpHeap(args);
4064         dumpHeap.Run();
4065
4066         return S_OK;
4067     }
4068     catch(const sos::Exception &e)
4069     {
4070         ExtOut("%s\n", e.what());
4071         return E_FAIL;
4072     }
4073 }
4074
4075 DECLARE_API(VerifyHeap)
4076 {    
4077     INIT_API();
4078     MINIDUMP_NOT_SUPPORTED();
4079     
4080     if (!g_snapshot.Build())
4081     {
4082         ExtOut("Unable to build snapshot of the garbage collector state\n");
4083         return E_FAIL;
4084     }
4085     
4086     try
4087     {
4088         bool succeeded = true;
4089         char buffer[1024];
4090         sos::GCHeap gcheap;
4091         sos::ObjectIterator itr = gcheap.WalkHeap();
4092
4093         while (itr)
4094         {
4095             if (itr.Verify(buffer, _countof(buffer)))
4096             {
4097                 ++itr;
4098             }
4099             else
4100             {
4101                 succeeded = false;
4102                 ExtOut(buffer);
4103                 itr.MoveToNextObjectCarefully();
4104             }
4105         }
4106
4107         if (!DumpHeapImpl::ValidateSyncTable(gcheap))
4108             succeeded = false;
4109
4110         if (succeeded)
4111             ExtOut("No heap corruption detected.\n");
4112
4113         return S_OK;
4114     }
4115     catch(const sos::Exception &e)
4116     {
4117         ExtOut("%s\n", e.what());
4118         return E_FAIL;
4119     }
4120 }
4121
4122 enum failure_get_memory
4123 {
4124     fgm_no_failure = 0,
4125     fgm_reserve_segment = 1,
4126     fgm_commit_segment_beg = 2,
4127     fgm_commit_eph_segment = 3,
4128     fgm_grow_table = 4,
4129     fgm_commit_table = 5
4130 };
4131
4132 enum oom_reason
4133 {
4134     oom_no_failure = 0,
4135     oom_budget = 1,
4136     oom_cant_commit = 2,
4137     oom_cant_reserve = 3,
4138     oom_loh = 4,
4139     oom_low_mem = 5,
4140     oom_unproductive_full_gc = 6
4141 };
4142
4143 static const char *const str_oom[] = 
4144 {
4145     "There was no managed OOM due to allocations on the GC heap", // oom_no_failure 
4146     "This is likely to be a bug in GC", // oom_budget
4147     "Didn't have enough memory to commit", // oom_cant_commit
4148     "This is likely to be a bug in GC", // oom_cant_reserve 
4149     "Didn't have enough memory to allocate an LOH segment", // oom_loh 
4150     "Low on memory during GC", // oom_low_mem 
4151     "Could not do a full GC" // oom_unproductive_full_gc
4152 };
4153
4154 static const char *const str_fgm[] = 
4155 {
4156     "There was no failure to allocate memory", // fgm_no_failure 
4157     "Failed to reserve memory", // fgm_reserve_segment
4158     "Didn't have enough memory to commit beginning of the segment", // fgm_commit_segment_beg
4159     "Didn't have enough memory to commit the new ephemeral segment", // fgm_commit_eph_segment
4160     "Didn't have enough memory to grow the internal GC datastructures", // fgm_grow_table
4161     "Didn't have enough memory to commit the internal GC datastructures", // fgm_commit_table
4162 };
4163
4164 void PrintOOMInfo(DacpOomData* oomData)
4165 {
4166     ExtOut("Managed OOM occured after GC #%d (Requested to allocate %d bytes)\n", 
4167         oomData->gc_index, oomData->alloc_size);
4168
4169     if ((oomData->reason == oom_budget) ||
4170         (oomData->reason == oom_cant_reserve))
4171     {
4172         // TODO: This message needs to be updated with more precious info.
4173         ExtOut("%s, please contact PSS\n", str_oom[oomData->reason]);
4174     }
4175     else
4176     {
4177         ExtOut("Reason: %s\n", str_oom[oomData->reason]);
4178     }
4179
4180     // Now print out the more detailed memory info if any.
4181     if (oomData->fgm != fgm_no_failure)
4182     {
4183         ExtOut("Detail: %s: %s (%d bytes)", 
4184             (oomData->loh_p ? "LOH" : "SOH"), 
4185             str_fgm[oomData->fgm],
4186             oomData->size);
4187     
4188         if ((oomData->fgm == fgm_commit_segment_beg) ||
4189             (oomData->fgm == fgm_commit_eph_segment) ||
4190             (oomData->fgm == fgm_grow_table) ||
4191             (oomData->fgm == fgm_commit_table))
4192         {
4193             // If it's a commit error (fgm_grow_table can indicate a reserve
4194             // or a commit error since we make one VirtualAlloc call to
4195             // reserve and commit), we indicate the available commit
4196             // space if we recorded it.
4197             if (oomData->available_pagefile_mb)
4198             {
4199                 ExtOut(" - on GC entry available commit space was %d MB",
4200                     oomData->available_pagefile_mb);
4201             }
4202         }
4203
4204         ExtOut("\n");
4205     }
4206 }
4207
4208 DECLARE_API(AnalyzeOOM)
4209 {    
4210     INIT_API();    
4211     MINIDUMP_NOT_SUPPORTED();    
4212     
4213 #ifndef FEATURE_PAL
4214
4215     if (!InitializeHeapData ())
4216     {
4217         ExtOut("GC Heap not initialized yet.\n");
4218         return S_OK;
4219     }
4220
4221     BOOL bHasManagedOOM = FALSE;
4222     DacpOomData oomData;
4223     memset (&oomData, 0, sizeof(oomData));
4224     if (!IsServerBuild())
4225     {
4226         if (oomData.Request(g_sos) != S_OK)
4227         {
4228             ExtOut("Error requesting OOM data\n");
4229             return E_FAIL;
4230         }
4231         if (oomData.reason != oom_no_failure)
4232         {
4233             bHasManagedOOM = TRUE;
4234             PrintOOMInfo(&oomData);
4235         }
4236     }
4237     else
4238     {   
4239         DWORD dwNHeaps = GetGcHeapCount();
4240         DWORD dwAllocSize;
4241         if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
4242         {
4243             ExtOut("Failed to get GCHeaps:  integer overflow\n");
4244             return Status;
4245         }
4246
4247         CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
4248         if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
4249         {
4250             ExtOut("Failed to get GCHeaps\n");
4251             return Status;
4252         }
4253         
4254         for (DWORD n = 0; n < dwNHeaps; n ++)
4255         {
4256             if (oomData.Request(g_sos, heapAddrs[n]) != S_OK)
4257             {
4258                 ExtOut("Heap %d: Error requesting OOM data\n", n);
4259                 return E_FAIL;
4260             }
4261             if (oomData.reason != oom_no_failure)
4262             {
4263                 if (!bHasManagedOOM)
4264                 {
4265                     bHasManagedOOM = TRUE;
4266                 }
4267                 ExtOut("---------Heap %#-2d---------\n", n);
4268                 PrintOOMInfo(&oomData);
4269             }
4270         }
4271     }
4272
4273     if (!bHasManagedOOM)
4274     {
4275         ExtOut("%s\n", str_oom[oomData.reason]);
4276     }
4277
4278     return S_OK;
4279 #else
4280     _ASSERTE(false);
4281     return E_FAIL;
4282 #endif // FEATURE_PAL
4283 }
4284
4285 DECLARE_API(VerifyObj)
4286 {
4287     INIT_API();    
4288     MINIDUMP_NOT_SUPPORTED();
4289
4290 #ifndef FEATURE_PAL
4291
4292     TADDR  taddrObj = 0;
4293     TADDR  taddrMT;
4294     size_t objSize;
4295
4296     BOOL bValid = FALSE;
4297     BOOL dml = FALSE;
4298
4299     CMDOption option[] = 
4300     {   // name, vptr, type, hasValue
4301         {"/d", &dml, COBOOL, FALSE},
4302     };
4303     CMDValue arg[] = 
4304     {   // vptr, type
4305         {&taddrObj, COHEX}
4306     };
4307     size_t nArg;
4308     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
4309     {
4310         return Status;
4311     }
4312
4313     EnableDMLHolder dmlHolder(dml);
4314     BOOL bContainsPointers;
4315
4316     if (FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
4317         !GetSizeEfficient(taddrObj, taddrMT, FALSE, objSize, bContainsPointers))
4318     {
4319         ExtOut("object %#p does not have valid method table\n", (ULONG64)taddrObj);
4320         goto Exit;
4321     }
4322
4323     // we need to build g_snapshot as it is later used in GetGeneration
4324     if (!g_snapshot.Build())
4325     {
4326         ExtOut("Unable to build snapshot of the garbage collector state\n");
4327         goto Exit;
4328     }
4329     DacpGcHeapDetails *pheapDetails = g_snapshot.GetHeap(taddrObj);
4330     bValid = VerifyObject(*pheapDetails, taddrObj, taddrMT, objSize, TRUE);
4331
4332 Exit:
4333     if (bValid)
4334     {
4335         ExtOut("object %#p is a valid object\n", (ULONG64) taddrObj);
4336     }
4337
4338     return Status;
4339
4340 #else
4341
4342     _ASSERTE(false);
4343     return E_FAIL;
4344
4345 #endif // FEATURE_PAL
4346 }
4347
4348 void LNODisplayOutput(LPCWSTR tag, TADDR pMT, TADDR currentObj, size_t size) 
4349 {
4350     sos::Object obj(currentObj, pMT);
4351     DMLOut("%S %s %12d (0x%x)\t%S\n", tag, DMLObject(currentObj), size, size, obj.GetTypeName());
4352 }
4353
4354 DECLARE_API(ListNearObj)
4355 {
4356     INIT_API();
4357     MINIDUMP_NOT_SUPPORTED();    
4358
4359 #if !defined(FEATURE_PAL)
4360
4361     TADDR taddrArg = 0;
4362     TADDR taddrObj = 0;
4363     // we may want to provide a more exact version of searching for the 
4364     // previous object in the heap, using the brick table, instead of 
4365     // looking for what may be valid method tables...
4366     //BOOL bExact;
4367     //CMDOption option[] = 
4368     //{
4369     //    // name, vptr, type, hasValue
4370     //    {"-exact", &bExact, COBOOL, FALSE}
4371     //};
4372
4373     BOOL dml = FALSE;
4374     CMDOption option[] = 
4375     {   // name, vptr, type, hasValue
4376         {"/d", &dml, COBOOL, FALSE},
4377     };
4378     CMDValue arg[] = 
4379     {
4380         // vptr, type
4381         {&taddrArg, COHEX}
4382     };
4383     size_t nArg;
4384     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || nArg != 1)
4385     {
4386         ExtOut("Usage: !ListNearObj <obj_address>\n");
4387         return Status;
4388     }
4389
4390     EnableDMLHolder dmlHolder(dml);
4391
4392     if (!g_snapshot.Build())
4393     {
4394         ExtOut("Unable to build snapshot of the garbage collector state\n");
4395         return Status;    
4396     }
4397
4398     taddrObj = Align(taddrArg);
4399
4400     DacpGcHeapDetails *heap = g_snapshot.GetHeap(taddrArg);
4401     if (heap == NULL)
4402     {
4403         ExtOut("Address %p does not lie in the managed heap\n", (ULONG64) taddrObj);
4404         return Status;
4405     }
4406
4407     TADDR_SEGINFO trngSeg  = {0, 0, 0};
4408     TADDR_RANGE   allocCtx = {0, 0};
4409     BOOL          bLarge;
4410     int           gen;
4411     if (!GCObjInHeap(taddrObj, *heap, trngSeg, gen, allocCtx, bLarge))
4412     {
4413         ExtOut("Failed to find the segment of the managed heap where the object %p resides\n", 
4414             (ULONG64) taddrObj);
4415         return Status;
4416     }
4417
4418     TADDR  objMT = NULL;
4419     size_t objSize = 0;
4420     BOOL   bObj    = FALSE;
4421     TADDR  taddrCur;
4422     TADDR  curMT   = 0;
4423     size_t curSize = 0;
4424     BOOL   bCur    = FALSE;
4425     TADDR  taddrNxt;
4426     TADDR  nxtMT   = 0;
4427     size_t nxtSize = 0;
4428     BOOL   bNxt    = FALSE;
4429     BOOL   bContainsPointers;
4430
4431     std::vector<TADDR> candidate;
4432     candidate.reserve(10);
4433
4434     // since we'll be reading back I'll prime the read cache to a buffer before the current address
4435     MOVE(taddrCur, max(trngSeg.start, taddrObj-DT_OS_PAGE_SIZE));
4436
4437     // ===== Look for a good candidate preceeding taddrObj
4438
4439     for (taddrCur = taddrObj - sizeof(TADDR); taddrCur >= trngSeg.start; taddrCur -= sizeof(TADDR))
4440     {
4441         // currently we don't pay attention to allocation contexts.  if this
4442         // proves to be an issue we need to reconsider the code below
4443         if (SUCCEEDED(GetMTOfObject(taddrCur, &curMT)) &&
4444             GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers))
4445         {
4446             // remember this as one of the possible "good" objects preceeding taddrObj
4447             candidate.push_back(taddrCur);
4448
4449             std::vector<TADDR>::iterator it = 
4450                 std::find(candidate.begin(), candidate.end(), taddrCur+curSize);
4451             if (it != candidate.end())
4452             {
4453                 // We found a chain of two objects preceeding taddrObj.  We'll
4454                 // trust this is a good indication that the two objects are valid.
4455                 // What is not valid is possibly the object following the second 
4456                 // one...
4457                 taddrCur = *it;
4458                 GetMTOfObject(taddrCur, &curMT);
4459                 GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers);
4460                 bCur = TRUE;
4461                 break;
4462             }
4463         }
4464     }
4465
4466     if (!bCur && !candidate.empty())
4467     {
4468         // pick the closest object to taddrObj
4469         taddrCur = *(candidate.begin());
4470         GetMTOfObject(taddrCur, &curMT);
4471         GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers);
4472         // we have a candidate, even if not confirmed
4473         bCur = TRUE;
4474     }
4475
4476     taddrNxt = taddrObj;
4477     if (taddrArg == taddrObj) 
4478     {
4479         taddrNxt += sizeof(TADDR);
4480     }
4481
4482     // ===== Now look at taddrObj
4483     if (taddrObj == taddrArg) 
4484     {
4485         // only look at taddrObj if it's the same as what user passed in, meaning it's aligned.  
4486         if (SUCCEEDED(GetMTOfObject(taddrObj, &objMT)) &&
4487             GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers))
4488         {
4489             bObj = TRUE;
4490             taddrNxt = taddrObj+objSize;
4491         }
4492     }
4493
4494     if ((taddrCur + curSize > taddrArg) && taddrCur + curSize < trngSeg.end)
4495     {
4496         if (SUCCEEDED(GetMTOfObject(taddrCur + curSize, &nxtMT)) &&
4497             GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers))
4498         {
4499             taddrNxt = taddrCur+curSize;
4500         }
4501     }
4502
4503     // ===== And finally move on to elements following taddrObj
4504     
4505     for (; taddrNxt < trngSeg.end; taddrNxt += sizeof(TADDR))
4506     {
4507         if (SUCCEEDED(GetMTOfObject(taddrNxt, &nxtMT)) &&
4508             GetSizeEfficient(taddrNxt, nxtMT, bLarge, nxtSize, bContainsPointers))
4509         {
4510             bNxt = TRUE;
4511             break;
4512         }
4513     }
4514
4515     if (bCur)
4516         LNODisplayOutput(L"Before: ", curMT, taddrCur, curSize);
4517     else
4518         ExtOut("Before: couldn't find any object between %#p and %#p\n", 
4519             (ULONG64)trngSeg.start, (ULONG64)taddrArg);
4520
4521     if (bObj)
4522         LNODisplayOutput(L"Current:", objMT, taddrObj, objSize);
4523
4524     if (bNxt)
4525         LNODisplayOutput(L"After:  ", nxtMT, taddrNxt, nxtSize);
4526     else
4527         ExtOut("After:  couldn't find any object between %#p and %#p\n", 
4528             (ULONG64)taddrArg, (ULONG64)trngSeg.end);
4529
4530     if (bCur && bNxt && 
4531         (((taddrCur+curSize == taddrObj) && (taddrObj+objSize == taddrNxt)) || (taddrCur+curSize == taddrNxt)))
4532     {
4533         ExtOut("Heap local consistency confirmed.\n");
4534     }
4535     else
4536     {
4537         ExtOut("Heap local consistency not confirmed.\n");
4538     }
4539
4540     return Status;    
4541
4542 #else
4543
4544     _ASSERTE(false);
4545     return E_FAIL;
4546
4547 #endif // FEATURE_PAL
4548 }
4549
4550
4551 DECLARE_API(GCHeapStat)
4552 {
4553     INIT_API();
4554     MINIDUMP_NOT_SUPPORTED();    
4555     
4556
4557 #ifndef FEATURE_PAL
4558
4559     BOOL bIncUnreachable = FALSE;
4560     BOOL dml = FALSE;
4561
4562     CMDOption option[] = {
4563         // name, vptr, type, hasValue
4564         {"-inclUnrooted", &bIncUnreachable, COBOOL, FALSE},
4565         {"-iu",           &bIncUnreachable, COBOOL, FALSE},
4566         {"/d",            &dml, COBOOL, FALSE}
4567     };
4568     
4569     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
4570     {
4571         return Status;
4572     }
4573
4574     EnableDMLHolder dmlHolder(dml);
4575     ExtOut("%-8s %12s %12s %12s %12s\n", "Heap", "Gen0", "Gen1", "Gen2", "LOH");
4576
4577     if (!IsServerBuild())
4578     {
4579         float tempf;
4580         DacpGcHeapDetails heapDetails;
4581         if (heapDetails.Request(g_sos) != S_OK)
4582         {
4583             ExtErr("Error requesting gc heap details\n");
4584             return Status;
4585         }
4586
4587         HeapUsageStat hpUsage;
4588         if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage))
4589         {
4590             ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", 0, 
4591                 hpUsage.genUsage[0].allocd, hpUsage.genUsage[1].allocd, 
4592                 hpUsage.genUsage[2].allocd, hpUsage.genUsage[3].allocd);
4593             ExtOut("\nFree space:                                                 Percentage\n");
4594             ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", 0, 
4595                 hpUsage.genUsage[0].freed, hpUsage.genUsage[1].freed, 
4596                 hpUsage.genUsage[2].freed, hpUsage.genUsage[3].freed);
4597             tempf = ((float)(hpUsage.genUsage[0].freed+hpUsage.genUsage[1].freed+hpUsage.genUsage[2].freed)) /
4598                 (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd);
4599             ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf), 
4600                 (int)(100*((float)hpUsage.genUsage[3].freed) / (hpUsage.genUsage[3].allocd)));
4601             if (bIncUnreachable)
4602             {
4603             ExtOut("\nUnrooted objects:                                           Percentage\n");
4604             ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", 0, 
4605                 hpUsage.genUsage[0].unrooted, hpUsage.genUsage[1].unrooted, 
4606                 hpUsage.genUsage[2].unrooted, hpUsage.genUsage[3].unrooted);
4607             tempf = ((float)(hpUsage.genUsage[0].unrooted+hpUsage.genUsage[1].unrooted+hpUsage.genUsage[2].unrooted)) / 
4608                 (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd);
4609             ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
4610                 (int)(100*((float)hpUsage.genUsage[3].unrooted) / (hpUsage.genUsage[3].allocd)));
4611             }
4612         }
4613     }
4614     else
4615     {
4616         float tempf;
4617         DacpGcHeapData gcheap;
4618         if (gcheap.Request(g_sos) != S_OK)
4619         {
4620             ExtErr("Error requesting GC Heap data\n");
4621             return Status;
4622         }
4623
4624         DWORD dwAllocSize;
4625         DWORD dwNHeaps = gcheap.HeapCount;
4626         if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
4627         {
4628             ExtErr("Failed to get GCHeaps:  integer overflow\n");
4629             return Status;
4630         }
4631
4632         CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
4633         if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
4634         {
4635             ExtErr("Failed to get GCHeaps\n");
4636             return Status;
4637         }
4638
4639         ArrayHolder<HeapUsageStat> hpUsage = new NOTHROW HeapUsageStat[dwNHeaps];
4640         if (hpUsage == NULL)
4641         {
4642             ReportOOM();
4643             return Status;
4644         }
4645
4646         // aggregate stats accross heaps / generation
4647         GenUsageStat genUsageStat[4] = {0, 0, 0, 0};
4648
4649         for (DWORD n = 0; n < dwNHeaps; n ++)
4650         {
4651             DacpGcHeapDetails heapDetails;
4652             if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
4653             {
4654                 ExtErr("Error requesting gc heap details\n");
4655                 return Status;
4656             }
4657
4658             if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage[n]))
4659             {
4660                 for (int i = 0; i < 4; ++i)
4661                 {
4662                     genUsageStat[i].allocd   += hpUsage[n].genUsage[i].allocd;
4663                     genUsageStat[i].freed    += hpUsage[n].genUsage[i].freed;
4664                     if (bIncUnreachable)
4665                     {
4666                     genUsageStat[i].unrooted += hpUsage[n].genUsage[i].unrooted;
4667                     }
4668                 }
4669             }
4670         }
4671
4672         for (DWORD n = 0; n < dwNHeaps; n ++)
4673         {
4674             ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", n, 
4675                 hpUsage[n].genUsage[0].allocd, hpUsage[n].genUsage[1].allocd, 
4676                 hpUsage[n].genUsage[2].allocd, hpUsage[n].genUsage[3].allocd);
4677         }
4678         ExtOut("Total    %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
4679             genUsageStat[0].allocd, genUsageStat[1].allocd, 
4680             genUsageStat[2].allocd, genUsageStat[3].allocd);
4681
4682         ExtOut("\nFree space:                                                 Percentage\n");
4683         for (DWORD n = 0; n < dwNHeaps; n ++)
4684         {
4685             ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", n, 
4686                 hpUsage[n].genUsage[0].freed, hpUsage[n].genUsage[1].freed, 
4687                 hpUsage[n].genUsage[2].freed, hpUsage[n].genUsage[3].freed);
4688
4689             tempf = ((float)(hpUsage[n].genUsage[0].freed+hpUsage[n].genUsage[1].freed+hpUsage[n].genUsage[2].freed)) /
4690                 (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd);
4691             ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf), 
4692                 (int)(100*((float)hpUsage[n].genUsage[3].freed) / (hpUsage[n].genUsage[3].allocd))
4693             );
4694         }
4695         ExtOut("Total    %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
4696             genUsageStat[0].freed, genUsageStat[1].freed, 
4697             genUsageStat[2].freed, genUsageStat[3].freed);
4698
4699         if (bIncUnreachable)
4700         {
4701             ExtOut("\nUnrooted objects:                                           Percentage\n");
4702             for (DWORD n = 0; n < dwNHeaps; n ++)
4703             {
4704                 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", n, 
4705                     hpUsage[n].genUsage[0].unrooted, hpUsage[n].genUsage[1].unrooted, 
4706                     hpUsage[n].genUsage[2].unrooted, hpUsage[n].genUsage[3].unrooted);
4707
4708                 tempf = ((float)(hpUsage[n].genUsage[0].unrooted+hpUsage[n].genUsage[1].unrooted+hpUsage[n].genUsage[2].unrooted)) / 
4709                     (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd);
4710                 ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
4711                     (int)(100*((float)hpUsage[n].genUsage[3].unrooted) / (hpUsage[n].genUsage[3].allocd)));
4712             }
4713             ExtOut("Total    %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
4714                 genUsageStat[0].unrooted, genUsageStat[1].unrooted, 
4715                 genUsageStat[2].unrooted, genUsageStat[3].unrooted);
4716         }
4717
4718     }
4719
4720     return Status;
4721     
4722 #else
4723
4724     _ASSERTE(false);
4725     return E_FAIL;
4726
4727 #endif // FEATURE_PAL
4728 }
4729
4730 /**********************************************************************\
4731 * Routine Description:                                                 *
4732 *                                                                      *
4733 *    This function dumps what is in the syncblock cache.  By default   *  
4734 *    it dumps all active syncblocks.  Using -all to dump all syncblocks
4735 *                                                                      *
4736 \**********************************************************************/
4737 DECLARE_API(SyncBlk)
4738 {
4739     INIT_API();    
4740     MINIDUMP_NOT_SUPPORTED();    
4741     
4742     BOOL bDumpAll = FALSE;
4743     size_t nbAsked = 0;
4744     BOOL dml = FALSE;
4745
4746     CMDOption option[] = 
4747     {   // name, vptr, type, hasValue
4748         {"-all", &bDumpAll, COBOOL, FALSE},
4749         {"/d", &dml, COBOOL, FALSE}
4750     };
4751     CMDValue arg[] = 
4752     {   // vptr, type
4753         {&nbAsked, COSIZE_T}
4754     };
4755     size_t nArg;
4756     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
4757     {
4758         return Status;
4759     }
4760
4761     EnableDMLHolder dmlHolder(dml);
4762     DacpSyncBlockData syncBlockData;
4763     if (syncBlockData.Request(g_sos,1) != S_OK)
4764     {
4765         ExtOut("Error requesting SyncBlk data\n");
4766         return Status;
4767     }
4768
4769     DWORD dwCount = syncBlockData.SyncBlockCount;
4770     
4771     ExtOut("Index" WIN64_8SPACES " SyncBlock MonitorHeld Recursion Owning Thread Info" WIN64_8SPACES "  SyncBlock Owner\n");
4772     ULONG freeCount = 0;
4773     ULONG CCWCount = 0;
4774     ULONG RCWCount = 0;
4775     ULONG CFCount = 0;
4776     for (DWORD nb = 1; nb <= dwCount; nb++)
4777     {
4778         if (IsInterrupt())
4779             return Status;
4780         
4781         if (nbAsked && nb != nbAsked) 
4782         {
4783             continue;
4784         }
4785
4786         if (syncBlockData.Request(g_sos,nb) != S_OK)
4787         {
4788             ExtOut("SyncBlock %d is invalid%s\n", nb,
4789                 (nb != nbAsked) ? ", continuing..." : "");
4790             continue;
4791         }
4792
4793         BOOL bPrint = (bDumpAll || nb == nbAsked || (syncBlockData.MonitorHeld > 0 && !syncBlockData.bFree));
4794
4795         if (bPrint)
4796         {
4797             ExtOut("%5d ", nb);
4798             if (!syncBlockData.bFree || nb != nbAsked)
4799             {            
4800                 ExtOut("%p  ", syncBlockData.SyncBlockPointer); 
4801                 ExtOut("%11d ", syncBlockData.MonitorHeld);
4802                 ExtOut("%9d ", syncBlockData.Recursion);
4803                 ExtOut("%p ", syncBlockData.HoldingThread);
4804
4805                 if (syncBlockData.HoldingThread == ~0ul)
4806                 {
4807                     ExtOut(" orphaned ");
4808                 }
4809                 else if (syncBlockData.HoldingThread != NULL)
4810                 {
4811                     DacpThreadData Thread;
4812                     if ((Status = Thread.Request(g_sos, syncBlockData.HoldingThread)) != S_OK)
4813                     {
4814                         ExtOut("Failed to request Thread at %p\n", syncBlockData.HoldingThread);
4815                         return Status;
4816                     }
4817
4818                     DMLOut(DMLThreadID(Thread.osThreadId));
4819                     ULONG id;
4820                     if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
4821                     {
4822                         ExtOut("%4d ", id);
4823                     }
4824                     else
4825                     {
4826                         ExtOut(" XXX ");
4827                     }
4828                 }
4829                 else
4830                 {
4831                     ExtOut("    none  ");
4832                 }
4833
4834                 if (syncBlockData.bFree)
4835                 {
4836                     ExtOut("  %8d", 0);    // TODO: do we need to print the free synctable list?
4837                 }
4838                 else
4839                 {
4840                     sos::Object obj = TO_TADDR(syncBlockData.Object);
4841                     DMLOut("  %s %S", DMLObject(syncBlockData.Object), obj.GetTypeName());
4842                 }
4843             }
4844         }
4845                                     
4846         if (syncBlockData.bFree) 
4847         {
4848             freeCount ++;
4849             if (bPrint) {
4850                 ExtOut(" Free");
4851             }
4852         }
4853         else 
4854         {
4855 #ifdef FEATURE_COMINTEROP            
4856             if (syncBlockData.COMFlags) {
4857                 switch (syncBlockData.COMFlags) {
4858                 case SYNCBLOCKDATA_COMFLAGS_CCW:
4859                     CCWCount ++;
4860                     break;
4861                 case SYNCBLOCKDATA_COMFLAGS_RCW:
4862                     RCWCount ++;
4863                     break;
4864                 case SYNCBLOCKDATA_COMFLAGS_CF:
4865                     CFCount ++;
4866                     break;
4867                 }
4868             }
4869 #endif // FEATURE_COMINTEROP            
4870         }
4871
4872         if (syncBlockData.MonitorHeld > 1)            
4873         {
4874             // TODO: implement this
4875             /*
4876             ExtOut(" ");
4877             DWORD_PTR pHead = (DWORD_PTR)vSyncBlock.m_Link.m_pNext;
4878             DWORD_PTR pNext = pHead;
4879             Thread vThread;
4880     
4881             while (1)
4882             {
4883                 if (IsInterrupt())
4884                     return Status;
4885                 DWORD_PTR pWaitEventLink = pNext - offsetLinkSB;
4886                 WaitEventLink vWaitEventLink;
4887                 vWaitEventLink.Fill(pWaitEventLink);
4888                 if (!CallStatus) {
4889                     break;
4890                 }
4891                 DWORD_PTR dwAddr = (DWORD_PTR)vWaitEventLink.m_Thread;
4892                 ExtOut("%x ", dwAddr);
4893                 vThread.Fill (dwAddr);
4894                 if (!CallStatus) {
4895                     break;
4896                 }
4897                 if (bPrint)
4898                     DMLOut("%s,", DMLThreadID(vThread.m_OSThreadId));
4899                 pNext = (DWORD_PTR)vWaitEventLink.m_LinkSB.m_pNext;
4900                 if (pNext == 0)
4901                     break;
4902             }  
4903             */
4904         }
4905         
4906         if (bPrint)
4907             ExtOut("\n");
4908     }
4909     
4910     ExtOut("-----------------------------\n");
4911     ExtOut("Total           %d\n", dwCount);
4912     ExtOut("CCW             %d\n", CCWCount);
4913     ExtOut("RCW             %d\n", RCWCount);
4914     ExtOut("ComClassFactory %d\n", CFCount);
4915     ExtOut("Free            %d\n", freeCount);
4916    
4917     return Status;
4918 }
4919
4920 #ifdef FEATURE_COMINTEROP
4921 struct VisitRcwArgs
4922 {
4923     BOOL bDetail;
4924     UINT MTACount;
4925     UINT STACount;
4926     ULONG FTMCount;
4927 };
4928
4929 void VisitRcw(CLRDATA_ADDRESS RCW,CLRDATA_ADDRESS Context,CLRDATA_ADDRESS Thread, BOOL bIsFreeThreaded, LPVOID token)
4930 {
4931     VisitRcwArgs *pArgs = (VisitRcwArgs *) token;
4932
4933     if (pArgs->bDetail)
4934     {
4935         if (pArgs->MTACount == 0 && pArgs->STACount == 0 && pArgs->FTMCount == 0)
4936         {
4937             // First time, print a header
4938             ExtOut("RuntimeCallableWrappers (RCW) to be cleaned:\n");
4939             ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Apartment\n",
4940                 "RCW", "CONTEXT", "THREAD");
4941         }        
4942         LPCSTR szThreadApartment;
4943         if (bIsFreeThreaded)
4944         {
4945             szThreadApartment = "(FreeThreaded)";
4946             pArgs->FTMCount++;
4947         }
4948         else if (Thread == NULL)
4949         {
4950             szThreadApartment = "(MTA)";
4951             pArgs->MTACount++;
4952         }
4953         else
4954         {
4955             szThreadApartment = "(STA)";
4956             pArgs->STACount++;
4957         }        
4958         
4959         ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %9s\n",
4960             SOS_PTR(RCW), 
4961             SOS_PTR(Context), 
4962             SOS_PTR(Thread),
4963             szThreadApartment);
4964     }
4965 }
4966
4967 DECLARE_API(RCWCleanupList)
4968 {
4969     INIT_API();
4970     MINIDUMP_NOT_SUPPORTED();    
4971
4972     DWORD_PTR p_CleanupList = GetExpression(args);
4973
4974     VisitRcwArgs travArgs;
4975     ZeroMemory(&travArgs,sizeof(VisitRcwArgs));  
4976     travArgs.bDetail = TRUE;
4977
4978     // We need to detect when !RCWCleanupList is called with an expression which evaluates to 0
4979     // (to print out an Invalid parameter message), but at the same time we need to allow an
4980     // empty argument list which would result in p_CleanupList equaling 0.
4981     if (p_CleanupList || strlen(args) == 0)
4982     {
4983         HRESULT hr = g_sos->TraverseRCWCleanupList(p_CleanupList, (VISITRCWFORCLEANUP)VisitRcw, &travArgs);
4984     
4985         if (SUCCEEDED(hr))
4986         {
4987             ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
4988             ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
4989             ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);
4990         }
4991         else
4992         {
4993             ExtOut("An error occurred while traversing the cleanup list.\n");
4994         }
4995     }
4996     else
4997     {
4998         ExtOut("Invalid parameter %s\n", args);
4999     }
5000     
5001     return Status;
5002 }
5003 #endif // FEATURE_COMINTEROP
5004
5005
5006 /**********************************************************************\
5007 * Routine Description:                                                 *
5008 *                                                                      *
5009 *    This function is called to dump the contents of the finalizer     *
5010 *    queue.                                                            *  
5011 *                                                                      *
5012 \**********************************************************************/
5013 DECLARE_API(FinalizeQueue)
5014 {
5015     INIT_API();
5016     MINIDUMP_NOT_SUPPORTED();    
5017     
5018     BOOL bDetail = FALSE;
5019     BOOL bAllReady = FALSE;
5020     BOOL bShort    = FALSE;
5021     BOOL dml = FALSE;
5022     TADDR taddrMT  = 0;
5023
5024     CMDOption option[] = 
5025     {   // name, vptr, type, hasValue
5026         {"-detail",   &bDetail,   COBOOL, FALSE},
5027         {"-allReady", &bAllReady, COBOOL, FALSE},
5028         {"-short",    &bShort,    COBOOL, FALSE},
5029         {"/d",        &dml,       COBOOL, FALSE},
5030         {"-mt",       &taddrMT,   COHEX,  TRUE},
5031     };
5032
5033     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
5034     {
5035         return Status;
5036     }
5037
5038     EnableDMLHolder dmlHolder(dml);
5039     if (!bShort)
5040     {
5041         DacpSyncBlockCleanupData dsbcd;
5042         CLRDATA_ADDRESS sbCurrent = NULL;
5043         ULONG cleanCount = 0;
5044         while ((dsbcd.Request(g_sos,sbCurrent) == S_OK) && dsbcd.SyncBlockPointer)
5045         {
5046             if (bDetail)
5047             {
5048                 if (cleanCount == 0) // print first time only
5049                 {
5050                     ExtOut("SyncBlocks to be cleaned by the finalizer thread:\n");
5051                     ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
5052                         "SyncBlock", "RCW", "CCW", "ComClassFactory");                
5053                 }
5054                 
5055                 ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p\n", 
5056                     (ULONG64) dsbcd.SyncBlockPointer,
5057                     (ULONG64) dsbcd.blockRCW,
5058                     (ULONG64) dsbcd.blockCCW,
5059                     (ULONG64) dsbcd.blockClassFactory);
5060             }
5061
5062             cleanCount++;
5063             sbCurrent = dsbcd.nextSyncBlock;
5064             if (sbCurrent == NULL)
5065             {
5066                 break;
5067             }
5068         }
5069
5070         ExtOut("SyncBlocks to be cleaned up: %d\n", cleanCount);
5071
5072 #ifdef FEATURE_COMINTEROP
5073         VisitRcwArgs travArgs;
5074         ZeroMemory(&travArgs,sizeof(VisitRcwArgs));  
5075         travArgs.bDetail = bDetail;
5076         g_sos->TraverseRCWCleanupList(0, (VISITRCWFORCLEANUP) VisitRcw, &travArgs);
5077         ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
5078         ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
5079         ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);    
5080 #endif // FEATURE_COMINTEROP    
5081
5082 // noRCW:
5083         ExtOut("----------------------------------\n");
5084     }
5085
5086     // GC Heap
5087     DWORD dwNHeaps = GetGcHeapCount();
5088
5089     HeapStat hpStat;
5090
5091     if (!IsServerBuild())
5092     {
5093         DacpGcHeapDetails heapDetails;
5094         if (heapDetails.Request(g_sos) != S_OK)
5095         {
5096             ExtOut("Error requesting details\n");
5097             return Status;
5098         }
5099
5100         GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
5101     }
5102     else
5103     {   
5104         DWORD dwAllocSize;
5105         if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
5106         {
5107             ExtOut("Failed to get GCHeaps:  integer overflow\n");
5108             return Status;
5109         }
5110
5111         CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
5112         if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
5113         {
5114             ExtOut("Failed to get GCHeaps\n");
5115             return Status;
5116         }
5117         
5118         for (DWORD n = 0; n < dwNHeaps; n ++)
5119         {
5120             DacpGcHeapDetails heapDetails;
5121             if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
5122             {
5123                 ExtOut("Error requesting details\n");
5124                 return Status;
5125             }
5126
5127             ExtOut("------------------------------\n");
5128             ExtOut("Heap %d\n", n);
5129             GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
5130         }        
5131     }
5132     
5133     if (!bShort)
5134     {
5135         if (bAllReady)
5136         {
5137             PrintGCStat(&hpStat, "Statistics for all finalizable objects that are no longer rooted:\n");
5138         }
5139         else
5140         {
5141             PrintGCStat(&hpStat, "Statistics for all finalizable objects (including all objects ready for finalization):\n");
5142         }
5143     }
5144
5145     return Status;
5146 }
5147
5148 enum {
5149     // These are the values set in m_dwTransientFlags.
5150     // Note that none of these flags survive a prejit save/restore.
5151
5152     M_CRST_NOTINITIALIZED       = 0x00000001,   // Used to prevent destruction of garbage m_crst
5153     M_LOOKUPCRST_NOTINITIALIZED = 0x00000002,
5154
5155     SUPPORTS_UPDATEABLE_METHODS = 0x00000020,
5156     CLASSES_FREED               = 0x00000040,
5157     HAS_PHONY_IL_RVAS           = 0x00000080,
5158     IS_EDIT_AND_CONTINUE        = 0x00000200,
5159 };
5160
5161 void ModuleMapTraverse(UINT index, CLRDATA_ADDRESS methodTable, LPVOID token)
5162 {
5163     ULONG32 rid = (ULONG32)(size_t)token;
5164     NameForMT_s(TO_TADDR(methodTable), g_mdName, mdNameLen);
5165
5166     DMLOut("%s 0x%08x %S\n", DMLMethodTable(methodTable), (ULONG32)TokenFromRid(rid, index), g_mdName);
5167 }
5168
5169
5170 /**********************************************************************\
5171 * Routine Description:                                                 *
5172 *                                                                      *
5173 *    This function is called to dump the contents of a Module          *
5174 *    for a given address                                               *  
5175 *                                                                      *
5176 \**********************************************************************/
5177 DECLARE_API(DumpModule)
5178 {
5179     INIT_API();
5180     MINIDUMP_NOT_SUPPORTED();    
5181     
5182     
5183     DWORD_PTR p_ModuleAddr = NULL;
5184     BOOL bMethodTables = FALSE;
5185     BOOL dml = FALSE;
5186
5187     CMDOption option[] = 
5188     {   // name, vptr, type, hasValue
5189         {"-mt", &bMethodTables, COBOOL, FALSE},
5190         {"/d", &dml, COBOOL, FALSE}
5191     };
5192     CMDValue arg[] = 
5193     {   // vptr, type
5194         {&p_ModuleAddr, COHEX}
5195     };
5196
5197     size_t nArg;
5198     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
5199     {
5200         return Status;
5201     }
5202     if (nArg != 1)
5203     {
5204         ExtOut("Usage: DumpModule [-mt] <Module Address>\n");
5205         return Status;
5206     }
5207
5208     EnableDMLHolder dmlHolder(dml);
5209     DacpModuleData module;
5210     if ((Status=module.Request(g_sos, TO_CDADDR(p_ModuleAddr)))!=S_OK)
5211     {
5212         ExtOut("Fail to fill Module %p\n", (ULONG64) p_ModuleAddr);
5213         return Status;
5214     }
5215     
5216     WCHAR FileName[MAX_PATH];
5217     FileNameForModule (&module, FileName);
5218     ExtOut("Name:       %S\n", FileName[0] ? FileName : L"Unknown Module");
5219
5220     ExtOut("Attributes: ");
5221     if (module.bIsPEFile)
5222         ExtOut("PEFile ");
5223     if (module.bIsReflection)
5224         ExtOut("Reflection ");
5225     if (module.dwTransientFlags & SUPPORTS_UPDATEABLE_METHODS)
5226         ExtOut("SupportsUpdateableMethods");
5227     ExtOut("\n");
5228     
5229     DMLOut("Assembly:   %s\n", DMLAssembly(module.Assembly));
5230
5231     ExtOut("LoaderHeap:              %p\n", (ULONG64)module.pLookupTableHeap);
5232     ExtOut("TypeDefToMethodTableMap: %p\n", (ULONG64)module.TypeDefToMethodTableMap);
5233     ExtOut("TypeRefToMethodTableMap: %p\n", (ULONG64)module.TypeRefToMethodTableMap);
5234     ExtOut("MethodDefToDescMap:      %p\n", (ULONG64)module.MethodDefToDescMap);
5235     ExtOut("FieldDefToDescMap:       %p\n", (ULONG64)module.FieldDefToDescMap);
5236     ExtOut("MemberRefToDescMap:      %p\n", (ULONG64)module.MemberRefToDescMap);
5237     ExtOut("FileReferencesMap:       %p\n", (ULONG64)module.FileReferencesMap);
5238     ExtOut("AssemblyReferencesMap:   %p\n", (ULONG64)module.ManifestModuleReferencesMap);
5239
5240     if (module.ilBase && module.metadataStart)
5241         ExtOut("MetaData start address:  %p (%d bytes)\n", (ULONG64)module.metadataStart, module.metadataSize);
5242
5243     if (bMethodTables)
5244     {
5245         ExtOut("\nTypes defined in this module\n\n");
5246         ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "MT", "TypeDef", "Name");
5247                 
5248         ExtOut("------------------------------------------------------------------------------\n");
5249         g_sos->TraverseModuleMap(TYPEDEFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil);        
5250
5251         ExtOut("\nTypes referenced in this module\n\n");
5252         ExtOut("%" POINTERSIZE "s   %" POINTERSIZE "s %s\n", "MT", "TypeRef", "Name");
5253         
5254         ExtOut("------------------------------------------------------------------------------\n");
5255         g_sos->TraverseModuleMap(TYPEREFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil);     
5256     }
5257     
5258     return Status;
5259 }
5260
5261 /**********************************************************************\
5262 * Routine Description:                                                 *
5263 *                                                                      *
5264 *    This function is called to dump the contents of a Domain          *
5265 *    for a given address                                               *  
5266 *                                                                      *
5267 \**********************************************************************/
5268 DECLARE_API(DumpDomain)
5269 {
5270     INIT_API();
5271     MINIDUMP_NOT_SUPPORTED();
5272     
5273     DWORD_PTR p_DomainAddr = 0;
5274     BOOL dml = FALSE;
5275
5276     CMDOption option[] = 
5277     {   // name, vptr, type, hasValue
5278         {"/d", &dml, COBOOL, FALSE},
5279     };
5280     CMDValue arg[] = 
5281     {   // vptr, type
5282         {&p_DomainAddr, COHEX},
5283     };
5284     size_t nArg;
5285
5286     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
5287     {
5288         return Status;
5289     }
5290
5291     EnableDMLHolder dmlHolder(dml);
5292
5293     DacpAppDomainStoreData adsData;
5294     if ((Status=adsData.Request(g_sos))!=S_OK)
5295     {
5296         ExtOut("Unable to get AppDomain information\n");
5297         return Status;
5298     }
5299     
5300     if (p_DomainAddr)
5301     {
5302         DacpAppDomainData appDomain1;
5303         if ((Status=appDomain1.Request(g_sos, TO_CDADDR(p_DomainAddr)))!=S_OK)
5304         {
5305             ExtOut("Fail to fill AppDomain\n");
5306             return Status;
5307         }
5308
5309         ExtOut("--------------------------------------\n");
5310
5311         if (p_DomainAddr == adsData.sharedDomain)
5312         {
5313             DMLOut("Shared Domain:      %s\n", DMLDomain(adsData.sharedDomain));
5314         }
5315         else if (p_DomainAddr == adsData.systemDomain)
5316         {
5317             DMLOut("System Domain:      %s\n", DMLDomain(adsData.systemDomain));
5318         }
5319         else
5320         {
5321             DMLOut("Domain %d:%s          %s\n", appDomain1.dwId, (appDomain1.dwId >= 10) ? "" : " ", DMLDomain(p_DomainAddr));
5322         }
5323
5324         DomainInfo(&appDomain1);
5325         return Status;
5326     }
5327         
5328     ExtOut("--------------------------------------\n");
5329     DMLOut("System Domain:      %s\n", DMLDomain(adsData.systemDomain));
5330     DacpAppDomainData appDomain;
5331     if ((Status=appDomain.Request(g_sos,adsData.systemDomain))!=S_OK)
5332     {
5333         ExtOut("Unable to get system domain info.\n");
5334         return Status;
5335     }
5336     DomainInfo(&appDomain);
5337     
5338     ExtOut("--------------------------------------\n");
5339     DMLOut("Shared Domain:      %s\n", DMLDomain(adsData.sharedDomain));
5340     if ((Status=appDomain.Request(g_sos, adsData.sharedDomain))!=S_OK)
5341     {
5342         ExtOut("Unable to get shared domain info\n");
5343         return Status;
5344     }
5345     DomainInfo(&appDomain);
5346
5347     ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
5348     if (pArray==NULL)
5349     {
5350         ReportOOM();
5351         return Status;
5352     }
5353
5354     if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
5355     {
5356         ExtOut("Unable to get array of AppDomains\n");
5357         return Status;
5358     }
5359
5360     for (int n=0;n<adsData.DomainCount;n++)
5361     {
5362         if (IsInterrupt())
5363             break;
5364
5365         if ((Status=appDomain.Request(g_sos, pArray[n])) != S_OK)
5366         {
5367             ExtOut("Failed to get appdomain %p, error %lx\n", (ULONG64)pArray[n], Status);
5368             return Status;
5369         }
5370
5371         ExtOut("--------------------------------------\n");
5372         DMLOut("Domain %d:%s          %s\n", appDomain.dwId, (appDomain.dwId >= 10) ? "" : " ", DMLDomain(pArray[n]));
5373         DomainInfo(&appDomain);
5374     }
5375
5376     return Status;
5377 }
5378
5379 /**********************************************************************\
5380 * Routine Description:                                                 *
5381 *                                                                      *
5382 *    This function is called to dump the contents of a Assembly        *
5383 *    for a given address                                               *  
5384 *                                                                      *
5385 \**********************************************************************/
5386 DECLARE_API(DumpAssembly)
5387 {
5388     INIT_API();
5389     MINIDUMP_NOT_SUPPORTED();    
5390     
5391     DWORD_PTR p_AssemblyAddr = 0;
5392     BOOL dml = FALSE;
5393
5394     CMDOption option[] = 
5395     {   // name, vptr, type, hasValue
5396         {"/d", &dml, COBOOL, FALSE},
5397     };
5398     CMDValue arg[] = 
5399     {   // vptr, type
5400         {&p_AssemblyAddr, COHEX},
5401     };
5402     size_t nArg;
5403
5404     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
5405     {
5406         return Status;
5407     }
5408
5409     EnableDMLHolder dmlHolder(dml);
5410
5411     if (p_AssemblyAddr == 0)
5412     {
5413         ExtOut("Invalid Assembly %s\n", args);
5414         return Status;
5415     }
5416     
5417     DacpAssemblyData Assembly;
5418     if ((Status=Assembly.Request(g_sos, TO_CDADDR(p_AssemblyAddr)))!=S_OK)
5419     {
5420         ExtOut("Fail to fill Assembly\n");
5421         return Status;
5422     }
5423     DMLOut("Parent Domain:      %s\n", DMLDomain(Assembly.ParentDomain));
5424     if (g_sos->GetAssemblyName(TO_CDADDR(p_AssemblyAddr), mdNameLen, g_mdName, NULL)==S_OK)
5425         ExtOut("Name:               %S\n", g_mdName);
5426     else
5427         ExtOut("Name:               Unknown\n");
5428
5429     AssemblyInfo(&Assembly);
5430     return Status;
5431 }
5432
5433 String GetHostingCapabilities(DWORD hostConfig)
5434 {
5435     String result;
5436
5437     bool bAnythingPrinted = false;
5438
5439 #define CHK_AND_PRINT(hType,hStr)                                \
5440     if (hostConfig & (hType)) {                                  \
5441         if (bAnythingPrinted) result += ", ";                    \
5442         result += hStr;                                          \
5443         bAnythingPrinted = true;                                 \
5444     }
5445
5446     CHK_AND_PRINT(CLRMEMORYHOSTED, "Memory");
5447     CHK_AND_PRINT(CLRTASKHOSTED, "Task");
5448     CHK_AND_PRINT(CLRSYNCHOSTED, "Sync");
5449     CHK_AND_PRINT(CLRTHREADPOOLHOSTED, "Threadpool");
5450     CHK_AND_PRINT(CLRIOCOMPLETIONHOSTED, "IOCompletion");
5451     CHK_AND_PRINT(CLRASSEMBLYHOSTED, "Assembly");
5452     CHK_AND_PRINT(CLRGCHOSTED, "GC");
5453     CHK_AND_PRINT(CLRSECURITYHOSTED, "Security");
5454
5455 #undef CHK_AND_PRINT
5456
5457     return result;
5458 }
5459
5460 /**********************************************************************\
5461 * Routine Description:                                                 *
5462 *                                                                      *
5463 *    This function is called to dump the managed threads               *
5464 *                                                                      *
5465 \**********************************************************************/
5466 HRESULT PrintThreadsFromThreadStore(BOOL bMiniDump, BOOL bPrintLiveThreadsOnly)
5467 {
5468     HRESULT Status;
5469     
5470     DacpThreadStoreData ThreadStore;
5471     if ((Status = ThreadStore.Request(g_sos)) != S_OK)
5472     {
5473         Print("Failed to request ThreadStore\n");
5474         return Status;
5475     }
5476
5477     TableOutput table(2, 17);
5478
5479     table.WriteRow("ThreadCount:", Decimal(ThreadStore.threadCount));
5480     table.WriteRow("UnstartedThread:", Decimal(ThreadStore.unstartedThreadCount));
5481     table.WriteRow("BackgroundThread:", Decimal(ThreadStore.backgroundThreadCount));
5482     table.WriteRow("PendingThread:", Decimal(ThreadStore.pendingThreadCount));
5483     table.WriteRow("DeadThread:", Decimal(ThreadStore.deadThreadCount));
5484
5485     if (ThreadStore.fHostConfig & ~CLRHOSTED)
5486     {
5487         String hosting = "yes";
5488
5489         hosting += " (";
5490         hosting += GetHostingCapabilities(ThreadStore.fHostConfig);
5491         hosting += ")";
5492
5493         table.WriteRow("Hosted Runtime:", hosting);
5494     }
5495     else
5496     {
5497         table.WriteRow("Hosted Runtime:", "no");
5498     }
5499
5500     const bool hosted = (ThreadStore.fHostConfig & CLRTASKHOSTED) != 0;
5501     table.ReInit(hosted ? 12 : 11, POINTERSIZE_HEX);
5502     table.SetWidths(10, 4, 4, 4, max(9, POINTERSIZE_HEX), 
5503                       8, 11, 1+POINTERSIZE_HEX*2, POINTERSIZE_HEX,
5504                       5, 3, POINTERSIZE_HEX);
5505
5506     table.SetColAlignment(0, AlignRight);
5507     table.SetColAlignment(1, AlignRight);
5508     table.SetColAlignment(2, AlignRight);
5509     table.SetColAlignment(4, AlignRight);
5510
5511     table.WriteColumn(8, "Lock");
5512     table.WriteRow("", "ID", "OSID", "ThreadOBJ", "State", "GC Mode", "GC Alloc Context", "Domain", "Count", "Apt");
5513     
5514     if (hosted)
5515         table.WriteColumn("Fiber");
5516
5517     table.WriteColumn("Exception");
5518     
5519     DacpThreadData Thread;
5520     CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
5521     while (CurThread)
5522     {
5523         if (IsInterrupt())
5524             break;
5525
5526         if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
5527         {
5528             PrintLn("Failed to request Thread at ", Pointer(CurThread));
5529             return Status;
5530         }
5531
5532         BOOL bSwitchedOutFiber = Thread.osThreadId == SWITCHED_OUT_FIBER_OSID;
5533         if (!IsKernelDebugger())
5534         {
5535             ULONG id = 0;          
5536             
5537             if (bSwitchedOutFiber)
5538             {
5539                 table.WriteColumn(0, "<<<< ");
5540             }
5541             else if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
5542             {
5543                 table.WriteColumn(0, Decimal(id));
5544             }
5545             else if (bPrintLiveThreadsOnly)
5546             {
5547                 CurThread = Thread.nextThread;
5548                 continue;
5549             }
5550             else
5551             {
5552                 table.WriteColumn(0, "XXXX ");
5553             }
5554         }
5555
5556         table.WriteColumn(1, Decimal(Thread.corThreadId));
5557         table.WriteColumn(2, ThreadID(bSwitchedOutFiber ? 0 : Thread.osThreadId));
5558         table.WriteColumn(3, Pointer(CurThread));
5559         table.WriteColumn(4, ThreadState(Thread.state));
5560         table.WriteColumn(5,  Thread.preemptiveGCDisabled == 1 ? "Cooperative" : "Preemptive");
5561         table.WriteColumnFormat(6, "%p:%p", Thread.allocContextPtr, Thread.allocContextLimit);
5562
5563         if (Thread.domain)
5564         {
5565             table.WriteColumn(7, AppDomainPtr(Thread.domain));
5566         }
5567         else
5568         {
5569             CLRDATA_ADDRESS domain = 0;
5570             if (FAILED(g_sos->GetDomainFromContext(Thread.context, &domain)))
5571                 table.WriteColumn(7, "<error>");
5572             else
5573                 table.WriteColumn(7, AppDomainPtr(domain));
5574         }
5575         
5576         table.WriteColumn(8, Decimal(Thread.lockCount));
5577
5578         // Apartment state
5579 #ifndef FEATURE_PAL           
5580         DWORD_PTR OleTlsDataAddr;
5581         if (!bSwitchedOutFiber 
5582                 && SafeReadMemory(Thread.teb + offsetof(TEB, ReservedForOle),
5583                             &OleTlsDataAddr,
5584                             sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0)
5585         {
5586             DWORD AptState;
5587             if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags),
5588                                &AptState,
5589                                sizeof(AptState), NULL))
5590             {
5591                 if (AptState & OLETLS_APARTMENTTHREADED)
5592                     table.WriteColumn(9, "STA");
5593                 else if (AptState & OLETLS_MULTITHREADED)
5594                     table.WriteColumn(9, "MTA");
5595                 else if (AptState & OLETLS_INNEUTRALAPT)
5596                     table.WriteColumn(9, "NTA");
5597                 else
5598                     table.WriteColumn(9, "Ukn");
5599             }
5600             else
5601             {
5602                 table.WriteColumn(9, "Ukn");
5603             }
5604         }
5605         else
5606 #endif // FEATURE_PAL
5607             table.WriteColumn(9, "Ukn");
5608
5609         if (hosted)
5610             table.WriteColumn(10, Thread.fiberData);
5611         
5612         WString lastCol;
5613         if (CurThread == ThreadStore.finalizerThread)
5614             lastCol += L"(Finalizer) ";
5615         if (CurThread == ThreadStore.gcThread)
5616             lastCol += L"(GC) ";
5617
5618         const int TS_TPWorkerThread         = 0x01000000;    // is this a threadpool worker thread?
5619         const int TS_CompletionPortThread   = 0x08000000;    // is this is a completion port thread?
5620         
5621         if (Thread.state & TS_TPWorkerThread)
5622             lastCol += L"(Threadpool Worker) ";
5623         else if (Thread.state & TS_CompletionPortThread)
5624             lastCol += L"(Threadpool Completion Port) ";
5625         
5626         
5627         TADDR taLTOH;
5628         if (Thread.lastThrownObjectHandle && SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
5629                                                             &taLTOH, sizeof(taLTOH), NULL) && taLTOH)
5630         {
5631             TADDR taMT;
5632             if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL))
5633             {
5634                 if (NameForMT_s(taMT, g_mdName, mdNameLen))
5635                     lastCol += WString(g_mdName) + L" " + ExceptionPtr(taLTOH);
5636                 else
5637                     lastCol += WString(L"<Invalid Object> (") + Pointer(taLTOH) + L")";
5638
5639                 // Print something if there are nested exceptions on the thread
5640                 if (Thread.firstNestedException)
5641                     lastCol += L" (nested exceptions)";
5642             }
5643         }
5644
5645         table.WriteColumn(lastCol);
5646         CurThread = Thread.nextThread;
5647     }
5648
5649     return Status;
5650 }
5651
5652 #ifndef FEATURE_PAL
5653 HRESULT PrintSpecialThreads()
5654 {
5655     Print("\n");
5656
5657     DWORD dwCLRTLSDataIndex = 0;
5658     HRESULT Status = g_sos->GetTLSIndex(&dwCLRTLSDataIndex);
5659     
5660     if (!SUCCEEDED (Status))
5661     {
5662         Print("Failed to retrieve Tls Data index\n");
5663         return Status;
5664     }
5665
5666
5667     ULONG ulOriginalThreadID = 0;
5668     Status = g_ExtSystem->GetCurrentThreadId (&ulOriginalThreadID);
5669     if (!SUCCEEDED (Status))
5670     {
5671         Print("Failed to require current Thread ID\n");
5672         return Status;
5673     }
5674
5675     ULONG ulTotalThreads = 0;
5676     Status = g_ExtSystem->GetNumberThreads (&ulTotalThreads);
5677     if (!SUCCEEDED (Status))
5678     {
5679         Print("Failed to require total thread number\n");
5680         return Status;
5681     }
5682
5683     TableOutput table(3, 4, AlignRight, 5);
5684     table.WriteRow("", "OSID", "Special thread type");
5685
5686     for (ULONG ulThread = 0; ulThread < ulTotalThreads; ulThread++)
5687     {
5688         ULONG Id = 0;
5689         ULONG SysId = 0;        
5690         HRESULT threadStatus = g_ExtSystem->GetThreadIdsByIndex(ulThread, 1, &Id, &SysId);
5691         if (!SUCCEEDED (threadStatus))
5692         {
5693             PrintLn("Failed to get thread ID for thread ", Decimal(ulThread));        
5694             continue;
5695         }    
5696
5697         threadStatus = g_ExtSystem->SetCurrentThreadId(Id);
5698         if (!SUCCEEDED (threadStatus))
5699         {
5700             PrintLn("Failed to switch to thread ", ThreadID(SysId));        
5701             continue;
5702         }    
5703
5704         CLRDATA_ADDRESS cdaTeb = 0;        
5705         threadStatus = g_ExtSystem->GetCurrentThreadTeb(&cdaTeb);
5706         if (!SUCCEEDED (threadStatus))
5707         {
5708             PrintLn("Failed to get Teb for Thread ", ThreadID(SysId));        
5709             continue;
5710         } 
5711
5712         TADDR CLRTLSDataAddr = 0;
5713
5714 #ifdef FEATURE_IMPLICIT_TLS
5715         TADDR tlsArrayAddr = NULL;
5716         if (!SafeReadMemory (TO_TADDR(cdaTeb) + WINNT_OFFSETOF__TEB__ThreadLocalStoragePointer , &tlsArrayAddr, sizeof (void**), NULL))
5717         {
5718             PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));        
5719             continue;
5720         }
5721
5722         TADDR moduleTlsDataAddr = 0;
5723
5724         if (!SafeReadMemory (tlsArrayAddr + sizeof (void*) * dwCLRTLSDataIndex, &moduleTlsDataAddr, sizeof (void**), NULL))
5725         {
5726             PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));        
5727             continue;
5728         }
5729
5730         CLRTLSDataAddr = moduleTlsDataAddr + OFFSETOF__TLS__tls_EETlsData;
5731 #else
5732         if (dwCLRTLSDataIndex < TLS_MINIMUM_AVAILABLE)
5733         {
5734             CLRTLSDataAddr = TO_TADDR(cdaTeb) + offsetof(TEB, TlsSlots) + sizeof (void*) * dwCLRTLSDataIndex;
5735         }
5736         else
5737         {
5738             //if TLS index is bigger than TLS_MINIMUM_AVAILABLE, the TLS slot lives in ExpansionSlots
5739             TADDR TebExpsionAddr = NULL;
5740             if (!SafeReadMemory (TO_TADDR(cdaTeb) + offsetof(TEB, TlsExpansionSlots) , &TebExpsionAddr, sizeof (void**), NULL))
5741             {
5742                 PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));        
5743                 continue;
5744             }
5745
5746             if (TebExpsionAddr == NULL)
5747             {
5748                 continue;
5749             }
5750             
5751             CLRTLSDataAddr = TebExpsionAddr + sizeof (void*) * (dwCLRTLSDataIndex - TLS_MINIMUM_AVAILABLE);
5752         }
5753 #endif // FEATURE_IMPLICIT_TLS
5754
5755         TADDR CLRTLSData = NULL;
5756         if (!SafeReadMemory (CLRTLSDataAddr, &CLRTLSData, sizeof (TADDR), NULL))
5757         {
5758             PrintLn("Failed to get CLR Tls data for thread ", ThreadID(SysId));        
5759             continue;
5760         }
5761
5762         if (CLRTLSData == NULL)
5763         {
5764             continue;
5765         }
5766
5767         size_t ThreadType = 0;
5768         if (!SafeReadMemory (CLRTLSData + sizeof (TADDR) * TlsIdx_ThreadType, &ThreadType, sizeof (size_t), NULL))
5769         {
5770             PrintLn("Failed to get thread type info not found for thread ", ThreadID(SysId));        
5771             continue;
5772         }
5773         
5774         if (ThreadType == 0)
5775         {
5776             continue;
5777         }
5778
5779         table.WriteColumn(0, Decimal(Id));
5780         table.WriteColumn(1, ThreadID(SysId));
5781
5782         String type;
5783         if (ThreadType & ThreadType_GC)
5784         {
5785             type += "GC ";
5786         }
5787         if (ThreadType & ThreadType_Timer)
5788         {
5789             type += "Timer ";
5790         }
5791         if (ThreadType & ThreadType_Gate)
5792         {
5793             type += "Gate ";
5794         }
5795         if (ThreadType & ThreadType_DbgHelper)
5796         {
5797             type += "DbgHelper ";
5798         }
5799         if (ThreadType & ThreadType_Shutdown)
5800         {
5801             type += "Shutdown ";
5802         }
5803         if (ThreadType & ThreadType_DynamicSuspendEE)
5804         {
5805             type += "SuspendEE ";
5806         }
5807         if (ThreadType & ThreadType_Finalizer)
5808         {
5809             type += "Finalizer ";
5810         }
5811         if (ThreadType & ThreadType_ADUnloadHelper)
5812         {
5813             type += "ADUnloadHelper ";
5814         }
5815         if (ThreadType & ThreadType_ShutdownHelper)
5816         {
5817             type += "ShutdownHelper ";
5818         }
5819         if (ThreadType & ThreadType_Threadpool_IOCompletion)
5820         {
5821             type += "IOCompletion ";
5822         }
5823         if (ThreadType & ThreadType_Threadpool_Worker)
5824         {
5825             type += "ThreadpoolWorker ";
5826         }
5827         if (ThreadType & ThreadType_Wait)
5828         {
5829             type += "Wait ";
5830         }
5831         if (ThreadType & ThreadType_ProfAPI_Attach)
5832         {
5833             type += "ProfilingAPIAttach ";
5834         }
5835         if (ThreadType & ThreadType_ProfAPI_Detach)
5836         {
5837             type += "ProfilingAPIDetach ";
5838         }
5839
5840         table.WriteColumn(2, type);
5841     }
5842
5843     Status = g_ExtSystem->SetCurrentThreadId (ulOriginalThreadID);
5844     if (!SUCCEEDED (Status))
5845     {
5846         ExtOut("Failed to switch to original thread\n");        
5847         return Status;
5848     }    
5849
5850     return Status;
5851 }
5852 #endif //FEATURE_PAL
5853
5854 struct ThreadStateTable
5855 {
5856     unsigned int State;
5857     const char * Name;
5858 };
5859 static const struct ThreadStateTable ThreadStates[] =
5860 {
5861     {0x1, "Thread Abort Requested"},
5862     {0x2, "GC Suspend Pending"},
5863     {0x4, "User Suspend Pending"},
5864     {0x8, "Debug Suspend Pending"},
5865     {0x10, "GC On Transitions"},
5866     {0x20, "Legal to Join"},
5867     {0x40, "Yield Requested"},
5868     {0x80, "Hijacked by the GC"},
5869     {0x100, "Blocking GC for Stack Overflow"},
5870     {0x200, "Background"},
5871     {0x400, "Unstarted"},
5872     {0x800, "Dead"},
5873     {0x1000, "CLR Owns"},
5874     {0x2000, "CoInitialized"},
5875     {0x4000, "In Single Threaded Apartment"},
5876     {0x8000, "In Multi Threaded Apartment"},
5877     {0x10000, "Reported Dead"},
5878     {0x20000, "Fully initialized"},
5879     {0x40000, "Task Reset"},
5880     {0x80000, "Sync Suspended"},
5881     {0x100000, "Debug Will Sync"},
5882     {0x200000, "Stack Crawl Needed"},
5883     {0x400000, "Suspend Unstarted"},
5884     {0x800000, "Aborted"},
5885     {0x1000000, "Thread Pool Worker Thread"},
5886     {0x2000000, "Interruptible"},
5887     {0x4000000, "Interrupted"},
5888     {0x8000000, "Completion Port Thread"},
5889     {0x10000000, "Abort Initiated"},
5890     {0x20000000, "Finalized"},
5891     {0x40000000, "Failed to Start"},
5892     {0x80000000, "Detached"},
5893 };
5894
5895 DECLARE_API(ThreadState)
5896 {
5897     INIT_API_NODAC();
5898
5899     size_t state = GetExpression(args);
5900     int count = 0;
5901
5902     if (state)
5903     {
5904
5905         for (unsigned int i = 0; i < _countof(ThreadStates); ++i)
5906             if (state & ThreadStates[i].State)
5907             {
5908                 ExtOut("    %s\n", ThreadStates[i].Name);
5909                 count++;
5910             }
5911     }
5912     
5913     // If we did not find any thread states, print out a message to let the user
5914     // know that the function is working correctly.
5915     if (count == 0)
5916         ExtOut("    No thread states for '%s'.", args);
5917
5918     return Status;
5919 }
5920
5921 DECLARE_API(Threads)
5922 {
5923     INIT_API();
5924
5925     BOOL bPrintSpecialThreads = FALSE;
5926     BOOL bPrintLiveThreadsOnly = FALSE;
5927     BOOL dml = FALSE;
5928
5929     CMDOption option[] = 
5930     {   // name, vptr, type, hasValue
5931         {"-special", &bPrintSpecialThreads, COBOOL, FALSE},
5932         {"-live", &bPrintLiveThreadsOnly, COBOOL, FALSE},
5933         {"/d", &dml, COBOOL, FALSE},
5934     };
5935     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
5936     {
5937         return Status;
5938     }
5939     
5940     // We need to support minidumps for this command.
5941     BOOL bMiniDump = IsMiniDumpFile();
5942
5943     if (bMiniDump && bPrintSpecialThreads)
5944     {
5945         Print("Special thread information is not available in mini dumps.\n");
5946     }
5947
5948     EnableDMLHolder dmlHolder(dml);
5949
5950     try
5951     {
5952         Status = PrintThreadsFromThreadStore(bMiniDump, bPrintLiveThreadsOnly);
5953         if (!bMiniDump && bPrintSpecialThreads)
5954         {
5955 #ifdef FEATURE_PAL
5956             Print("\n-special not supported.\n");
5957 #else //FEATURE_PAL    
5958             HRESULT Status2 = PrintSpecialThreads(); 
5959             if (!SUCCEEDED(Status2))
5960                 Status = Status2;
5961 #endif //FEATURE_PAL            
5962         }
5963     }
5964     catch (sos::Exception &e)
5965     {
5966         ExtOut("%s\n", e.what());
5967     }
5968     
5969     return Status;
5970 }
5971
5972 #ifndef FEATURE_PAL
5973 /**********************************************************************\
5974 * Routine Description:                                                 *
5975 *                                                                      *
5976 *    This function is called to dump the Watson Buckets.               *
5977 *                                                                      *
5978 \**********************************************************************/
5979 DECLARE_API(WatsonBuckets)
5980 {
5981     INIT_API();
5982
5983     // We don't need to support minidumps for this command.
5984     if (IsMiniDumpFile())
5985     {
5986         ExtOut("Not supported on mini dumps.\n");
5987     }
5988     
5989     // Get the current managed thread.
5990     CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
5991     DacpThreadData Thread;
5992
5993     if ((threadAddr == NULL) || ((Status = Thread.Request(g_sos, threadAddr)) != S_OK))
5994     {
5995         ExtOut("The current thread is unmanaged\n");
5996         return Status;
5997     }
5998     
5999     // Get the definition of GenericModeBlock.
6000 #include <msodw.h>
6001     GenericModeBlock gmb;
6002
6003     if ((Status = g_sos->GetClrWatsonBuckets(threadAddr, &gmb)) != S_OK)
6004     {
6005         ExtOut("Can't get Watson Buckets\n");
6006         return Status;
6007     }
6008
6009     ExtOut("Watson Bucket parameters:\n");
6010     ExtOut("b1: %S\n", gmb.wzP1);
6011     ExtOut("b2: %S\n", gmb.wzP2);
6012     ExtOut("b3: %S\n", gmb.wzP3);
6013     ExtOut("b4: %S\n", gmb.wzP4);
6014     ExtOut("b5: %S\n", gmb.wzP5);
6015     ExtOut("b6: %S\n", gmb.wzP6);
6016     ExtOut("b7: %S\n", gmb.wzP7);
6017     ExtOut("b8: %S\n", gmb.wzP8);
6018     ExtOut("b9: %S\n", gmb.wzP9);
6019         
6020     return Status;
6021 } // WatsonBuckets()
6022
6023 struct PendingBreakpoint
6024 {
6025     WCHAR szModuleName[MAX_PATH];
6026     WCHAR szFunctionName[mdNameLen];
6027     WCHAR szFilename[MAX_PATH];
6028     DWORD lineNumber;
6029     TADDR pModule; 
6030     DWORD ilOffset;
6031     mdMethodDef methodToken;
6032     void SetModule(TADDR module)
6033     {
6034         pModule = module;
6035     }
6036
6037     bool ModuleMatches(TADDR compare)
6038     {
6039         return (compare == pModule);
6040     }
6041
6042     PendingBreakpoint *pNext;
6043     PendingBreakpoint() : pNext(NULL), ilOffset(0), lineNumber(0), methodToken(0)
6044     {
6045         szModuleName[0] = L'\0';
6046         szFunctionName[0] = L'\0';
6047         szFilename[0] = L'\0';
6048     }
6049 };
6050
6051 void IssueDebuggerBPCommand ( CLRDATA_ADDRESS addr )
6052 {
6053     const int MaxBPsCached = 1024;
6054     static CLRDATA_ADDRESS alreadyPlacedBPs[MaxBPsCached];
6055     static int curLimit = 0;
6056
6057     // on ARM the debugger requires breakpoint addresses to be sanitized
6058     if (IsDbgTargetArm())
6059         addr &= ~THUMB_CODE;
6060     
6061
6062     // if we overflowed our cache consider all new BPs unique...
6063     BOOL bUnique = curLimit >= MaxBPsCached;
6064     if (!bUnique)
6065     {
6066         bUnique = TRUE;
6067         for ( int i = 0; i < curLimit; ++i )
6068             if (alreadyPlacedBPs[i] == addr)
6069             {
6070                 bUnique = FALSE;
6071                 break;
6072             }
6073     }
6074     if (bUnique)
6075     {
6076         char buffer[64]; // sufficient for "bp <pointersize>"
6077         static WCHAR wszNameBuffer[1024]; // should be large enough
6078
6079         // get the MethodDesc name
6080                CLRDATA_ADDRESS pMD;
6081         if (g_sos->GetMethodDescPtrFromIP(addr, &pMD) != S_OK
6082             || g_sos->GetMethodDescName(pMD, 1024, wszNameBuffer, NULL) != S_OK)
6083         {
6084             wcscpy_s(wszNameBuffer, _countof(wszNameBuffer),L"UNKNOWN");        
6085         }
6086
6087         sprintf_s(buffer, _countof(buffer), "bp %p", (void*) (size_t) addr);
6088         ExtOut("Setting breakpoint: %s [%S]\n", buffer, wszNameBuffer);
6089         g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
6090
6091         if ( curLimit < MaxBPsCached )
6092             alreadyPlacedBPs[curLimit++] = addr;
6093     }
6094 }
6095
6096 class Breakpoints
6097 {
6098     PendingBreakpoint* m_breakpoints;
6099 public:
6100     Breakpoints()
6101     {
6102         m_breakpoints = NULL;
6103     }
6104     ~Breakpoints()
6105     {
6106         PendingBreakpoint *pCur = m_breakpoints;
6107         while(pCur)
6108         {
6109             PendingBreakpoint *pNext = pCur->pNext;
6110             delete pCur;
6111             pCur = pNext;
6112         }
6113         m_breakpoints = NULL;
6114     }
6115
6116     void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod, DWORD ilOffset)
6117     {
6118         if (!IsIn(szModule, szName, mod))
6119         {
6120             PendingBreakpoint *pNew = new PendingBreakpoint();
6121             wcscpy_s(pNew->szModuleName, MAX_PATH, szModule);
6122             wcscpy_s(pNew->szFunctionName, mdNameLen, szName);
6123             pNew->SetModule(mod);
6124             pNew->ilOffset = ilOffset;
6125             pNew->pNext = m_breakpoints;
6126             m_breakpoints = pNew;
6127         }
6128     }
6129
6130     void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, mdMethodDef methodToken, TADDR mod, DWORD ilOffset)
6131     {
6132         if (!IsIn(methodToken, mod, ilOffset))
6133         {
6134             PendingBreakpoint *pNew = new PendingBreakpoint();
6135             wcscpy_s(pNew->szModuleName, MAX_PATH, szModule);
6136             wcscpy_s(pNew->szFunctionName, mdNameLen, szName);
6137             pNew->methodToken = methodToken;
6138             pNew->SetModule(mod);
6139             pNew->ilOffset = ilOffset;
6140             pNew->pNext = m_breakpoints;
6141             m_breakpoints = pNew;
6142         }
6143     }
6144
6145     void Add(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod)
6146     {
6147         if (!IsIn(szFilename, lineNumber, mod))
6148         {
6149             PendingBreakpoint *pNew = new PendingBreakpoint();
6150             wcscpy_s(pNew->szFilename, MAX_PATH, szFilename);
6151             pNew->lineNumber = lineNumber;
6152             pNew->SetModule(mod);
6153             pNew->pNext = m_breakpoints;
6154             m_breakpoints = pNew;
6155         }
6156     }
6157
6158     void Add(__in_z LPWSTR szFilename, DWORD lineNumber, mdMethodDef methodToken, TADDR mod, DWORD ilOffset)
6159     {
6160         if (!IsIn(methodToken, mod, ilOffset))
6161         {
6162             PendingBreakpoint *pNew = new PendingBreakpoint();
6163             wcscpy_s(pNew->szFilename, MAX_PATH, szFilename);
6164             pNew->lineNumber = lineNumber;
6165             pNew->methodToken = methodToken;
6166             pNew->SetModule(mod);
6167             pNew->ilOffset = ilOffset;
6168             pNew->pNext = m_breakpoints;
6169             m_breakpoints = pNew;
6170         }
6171     }
6172
6173     //returns true if updates are still needed for this module, FALSE if all BPs are now bound
6174     BOOL Update(TADDR mod, BOOL isNewModule)
6175     {
6176         BOOL bNeedUpdates = FALSE;
6177         PendingBreakpoint *pCur = NULL;
6178
6179         if(isNewModule)
6180         {
6181             SymbolReader symbolReader;
6182             SymbolReader* pSymReader = &symbolReader;
6183             if(LoadSymbolsForModule(mod, &symbolReader) != S_OK)
6184                 pSymReader = NULL;
6185
6186             // Get tokens for any modules that match. If there was a change,
6187             // update notifications.                
6188             pCur = m_breakpoints;
6189             while(pCur)
6190             {
6191                 PendingBreakpoint *pNext = pCur->pNext;
6192                 ResolvePendingNonModuleBoundBreakpoint(mod, pCur, pSymReader);
6193                 pCur = pNext;
6194             }
6195         }
6196
6197         pCur = m_breakpoints;
6198         while(pCur)
6199         {
6200             PendingBreakpoint *pNext = pCur->pNext;
6201             if (ResolvePendingBreakpoint(mod, pCur))
6202             {
6203                 bNeedUpdates = TRUE;
6204             }
6205             pCur = pNext;
6206         }
6207         return bNeedUpdates;
6208     }
6209
6210     void RemovePendingForModule(TADDR mod)
6211     {
6212         PendingBreakpoint *pCur = m_breakpoints;
6213         while(pCur)
6214         {
6215             PendingBreakpoint *pNext = pCur->pNext;
6216             if (pCur->ModuleMatches(mod))
6217             {
6218                 // Delete the current node, and keep going
6219                 Delete(pCur);
6220             }
6221
6222             pCur = pNext;
6223         }                
6224     }
6225     
6226     void ListBreakpoints()
6227     {
6228         PendingBreakpoint *pCur = m_breakpoints;
6229         size_t iBreakpointIndex = 1;
6230         ExtOut("!bpmd pending breakpoint list\n Breakpoint index - Location, ModuleID, Method Token\n");
6231         while(pCur)
6232         {
6233             //windbg likes to format %p as always being 64 bits
6234             ULONG64 modulePtr = (ULONG64)pCur->pModule;
6235
6236             if(pCur->szModuleName[0] != L'\0')
6237                 ExtOut("%d - %ws!%ws+%d, 0x%p, 0x%08x\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset, modulePtr, pCur->methodToken);
6238             else
6239                 ExtOut("%d - %ws:%d, 0x%p, 0x%08x\n",  iBreakpointIndex, pCur->szFilename, pCur->lineNumber, modulePtr, pCur->methodToken);
6240             iBreakpointIndex++;
6241             pCur = pCur->pNext;
6242         }
6243     }
6244
6245     void SaveBreakpoints(FILE* pFile)
6246     {
6247         PendingBreakpoint *pCur = m_breakpoints;
6248         while(pCur)
6249         {
6250             if(pCur->szModuleName[0] != L'\0')
6251                 fprintf_s(pFile, "!bpmd %ws %ws %d\n", pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset);
6252             else
6253                 fprintf_s(pFile, "!bpmd %ws:%d\n",  pCur->szFilename, pCur->lineNumber);
6254             pCur = pCur->pNext;
6255         }
6256     }
6257
6258     void ClearBreakpoint(size_t breakPointToClear)
6259     {
6260         PendingBreakpoint *pCur = m_breakpoints;
6261         size_t iBreakpointIndex = 1;
6262         while(pCur)
6263         {
6264             if (breakPointToClear == iBreakpointIndex)
6265             {
6266                 ExtOut("%d - %ws, %ws, %p\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->pModule);
6267                 ExtOut("Cleared\n");
6268                 Delete(pCur);
6269                 break;
6270             }
6271             iBreakpointIndex++;
6272             pCur = pCur->pNext;
6273         }
6274
6275         if (pCur == NULL)
6276         {
6277             ExtOut("Invalid pending breakpoint index.\n");
6278         }
6279     }
6280
6281     void ClearAllBreakpoints()
6282     {
6283         size_t iBreakpointIndex = 1;
6284         for (PendingBreakpoint *pCur = m_breakpoints; pCur != NULL; )
6285         {
6286             PendingBreakpoint* pNext = pCur->pNext;
6287             Delete(pCur);
6288             iBreakpointIndex++;
6289             pCur = pNext;
6290         }
6291         ExtOut("All pending breakpoints cleared.\n");
6292     }
6293
6294     HRESULT LoadSymbolsForModule(TADDR mod, SymbolReader* pSymbolReader)
6295     {
6296         HRESULT Status = S_OK;
6297         ToRelease<IXCLRDataModule> module;
6298         IfFailRet(g_sos->GetModule(mod, &module));
6299
6300         ToRelease<IMetaDataImport> pMDImport = NULL;
6301         IfFailRet(module->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport));
6302
6303         WCHAR wszNameBuffer[MAX_PATH];
6304         ULONG32 nameLen = 0;
6305         if(FAILED(Status = module->GetFileName(MAX_PATH, &nameLen, wszNameBuffer)))
6306         {
6307             ExtOut("SOS error: IXCLRDataModule->GetFileName failed hr=0x%x\n", wszNameBuffer);
6308             return Status;
6309         }
6310
6311         //get a pointer to just the filename (the portion after the last backslash)
6312         WCHAR* pModuleFilename = wszNameBuffer;
6313         WCHAR* pSlash = wcschr(pModuleFilename, L'\\');
6314         while(pSlash != NULL)
6315         {
6316             pModuleFilename = pSlash+1;
6317             pSlash = wcschr(pModuleFilename, L'\\');
6318         }
6319
6320         ImageInfo ii;
6321         if(FAILED(Status = GetClrModuleImages(module, CLRDATA_MODULE_PE_FILE, &ii)))
6322         {
6323             ExtOut("SOS error: GetClrModuleImages failed hr=0x%x\n", Status);
6324             return Status;
6325         }
6326
6327         if(FAILED(Status = pSymbolReader->LoadSymbols(pMDImport, ii.modBase, pModuleFilename, FALSE)) &&
6328            FAILED(pSymbolReader->LoadSymbols(pMDImport, ii.modBase, pModuleFilename, TRUE)))
6329         {
6330             ExtOut("SOS warning: No symbols for module %S, source line breakpoints in this module will not bind hr=0x%x\n", wszNameBuffer, Status);
6331             return S_FALSE; // not finding symbols is a typical case
6332         }
6333
6334         return S_OK;
6335     }
6336
6337     HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pFilename, DWORD lineNumber, TADDR mod, SymbolReader* pSymbolReader)
6338     {
6339         HRESULT Status = S_OK;
6340         if(pSymbolReader == NULL)
6341             return S_FALSE; // no symbols, can't bind here
6342
6343         mdMethodDef methodDef;
6344         ULONG32 ilOffset;
6345         if(FAILED(Status = pSymbolReader->ResolveSequencePoint(pFilename, lineNumber, &methodDef, &ilOffset)))
6346         {
6347             return S_FALSE; // not binding in a module is typical
6348         }
6349
6350         Add(pFilename, lineNumber, methodDef, mod, ilOffset);
6351         return Status;
6352     }
6353
6354     HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pModuleName, __in_z WCHAR* pMethodName, TADDR mod, DWORD ilOffset)
6355     {
6356         int numModule;
6357         char szName[mdNameLen];
6358         
6359         ToRelease<IXCLRDataModule> module;
6360         HRESULT Status = S_OK;
6361         IfFailRet(g_sos->GetModule(mod, &module));
6362
6363         WideCharToMultiByte(CP_ACP, 0, pModuleName, (int) (wcslen(pModuleName) + 1),
6364             szName, mdNameLen, NULL, NULL);            
6365
6366         ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(szName, &numModule);
6367         if (moduleList == NULL)
6368         {
6369             ExtOut("Failed to request module list.\n");
6370             return E_FAIL;
6371         }
6372
6373         for(int i=0;i<numModule;i++)
6374         {
6375             // If any one entry in moduleList matches, then the current PendingBreakpoint
6376             // is the right one.
6377             if(moduleList[i] != TO_TADDR(mod))
6378                 continue;
6379
6380             CLRDATA_ENUM h;
6381             if (module->StartEnumMethodDefinitionsByName(pMethodName, 0, &h) == S_OK)
6382             {
6383                 IXCLRDataMethodDefinition *pMeth = NULL;
6384                 while (module->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
6385                 {
6386                     mdMethodDef methodToken;
6387                     ToRelease<IXCLRDataModule> pUnusedModule;
6388                     IfFailRet(pMeth->GetTokenAndScope(&methodToken, &pUnusedModule));
6389
6390                     Add(pModuleName, pMethodName, methodToken, mod, ilOffset);
6391                     pMeth->Release();
6392                 }
6393                 module->EndEnumMethodDefinitionsByName(h);
6394             }
6395         }
6396         return S_OK;
6397     }
6398
6399     // Return TRUE if there might be more instances that will be JITTED later
6400     static BOOL ResolveMethodInstances(IXCLRDataMethodDefinition *pMeth, DWORD ilOffset)
6401     {
6402         BOOL bFoundCode = FALSE;
6403         BOOL bNeedDefer = FALSE;
6404         CLRDATA_ENUM h1;
6405         
6406         if (pMeth->StartEnumInstances (NULL, &h1) == S_OK)
6407         {
6408             IXCLRDataMethodInstance *inst = NULL;
6409             while (pMeth->EnumInstance (&h1, &inst) == S_OK)
6410             {
6411                 BOOL foundByIlOffset = FALSE;
6412                 ULONG32 rangesNeeded = 0;
6413                 if(inst->GetAddressRangesByILOffset(ilOffset, 0, &rangesNeeded, NULL) == S_OK)
6414                 {
6415                     ArrayHolder<CLRDATA_ADDRESS_RANGE> ranges = new NOTHROW CLRDATA_ADDRESS_RANGE[rangesNeeded];
6416                     if (ranges != NULL)
6417                     {
6418                         if (inst->GetAddressRangesByILOffset(ilOffset, rangesNeeded, NULL, ranges) == S_OK)
6419                         {
6420                             for (DWORD i = 0; i < rangesNeeded; i++)
6421                             {
6422                                 IssueDebuggerBPCommand(ranges[i].startAddress);
6423                                 bFoundCode = TRUE;
6424                                 foundByIlOffset = TRUE;
6425                             }
6426                         }
6427                     }
6428                 }
6429                 
6430                 if (!foundByIlOffset && ilOffset == 0)
6431                 {
6432                     CLRDATA_ADDRESS addr = 0;
6433                     if (inst->GetRepresentativeEntryAddress(&addr) == S_OK)
6434                     {
6435                         IssueDebuggerBPCommand(addr);
6436                         bFoundCode = TRUE;
6437                     }
6438                 }
6439             }
6440             pMeth->EndEnumInstances (h1);
6441         }
6442
6443         // if this is a generic method we need to add a defered bp
6444         BOOL bGeneric = FALSE;
6445         pMeth->HasClassOrMethodInstantiation(&bGeneric);
6446
6447         bNeedDefer = !bFoundCode || bGeneric;
6448         // This is down here because we only need to call SetCodeNofiication once.
6449         if (bNeedDefer)
6450         {
6451             if (pMeth->SetCodeNotification (CLRDATA_METHNOTIFY_GENERATED) != S_OK)
6452             {
6453                 bNeedDefer = FALSE;
6454                 ExtOut("Failed to set code notification\n");
6455             }
6456         }
6457         return bNeedDefer;
6458     }
6459
6460 private:    
6461     BOOL IsIn(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod)
6462     {
6463         PendingBreakpoint *pCur = m_breakpoints;
6464         while(pCur)
6465         {
6466             if (pCur->ModuleMatches(mod) && 
6467                 _wcsicmp(pCur->szModuleName, szModule) == 0 &&
6468                 wcscmp(pCur->szFunctionName, szName) == 0)
6469             {
6470                 return TRUE;
6471             }
6472             pCur = pCur->pNext;
6473         }
6474         return FALSE;
6475     }
6476
6477     BOOL IsIn(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod)
6478     {
6479         PendingBreakpoint *pCur = m_breakpoints;
6480         while(pCur)
6481         {
6482             if (pCur->ModuleMatches(mod) && 
6483                 _wcsicmp(pCur->szFilename, szFilename) == 0 &&
6484                 pCur->lineNumber == lineNumber)
6485             {
6486                 return TRUE;
6487             }
6488             pCur = pCur->pNext;
6489         }
6490         return FALSE;
6491     }
6492
6493     BOOL IsIn(mdMethodDef token, TADDR mod, DWORD ilOffset)
6494     {
6495         PendingBreakpoint *pCur = m_breakpoints;
6496         while(pCur)
6497         {
6498             if (pCur->ModuleMatches(mod) && 
6499                 pCur->methodToken == token &&
6500                 pCur->ilOffset == ilOffset)
6501             {
6502                 return TRUE;
6503             }
6504             pCur = pCur->pNext;
6505         }
6506         return FALSE;
6507     }
6508
6509     void Delete(PendingBreakpoint *pDelete)
6510     {
6511         PendingBreakpoint *pCur = m_breakpoints;
6512         PendingBreakpoint *pPrev = NULL;
6513         while(pCur)
6514         {
6515             if (pCur == pDelete)
6516             {
6517                 if (pPrev == NULL)
6518                 {
6519                     m_breakpoints = pCur->pNext;
6520                 }
6521                 else
6522                 {
6523                     pPrev->pNext = pCur->pNext;
6524                 }
6525                 delete pCur;
6526                 return;
6527             }
6528             pPrev = pCur;
6529             pCur = pCur->pNext;
6530         }
6531     }
6532
6533
6534
6535     HRESULT ResolvePendingNonModuleBoundBreakpoint(TADDR mod, PendingBreakpoint *pCur, SymbolReader* pSymbolReader)
6536     {
6537         // This function only works with pending breakpoints that are not module bound.
6538         if (pCur->pModule == NULL)
6539         {
6540             if(pCur->szModuleName[0] != L'\0')
6541             {
6542                 return ResolvePendingNonModuleBoundBreakpoint(pCur->szModuleName, pCur->szFunctionName, mod, pCur->ilOffset);
6543             }
6544             else
6545             {
6546                 return ResolvePendingNonModuleBoundBreakpoint(pCur->szFilename, pCur->lineNumber, mod, pSymbolReader);
6547             }
6548         }
6549         else
6550         {
6551             return S_OK;
6552         }
6553     }
6554
6555     // Returns TRUE if further instances may be jitted, FALSE if all instances are now resolved
6556     BOOL ResolvePendingBreakpoint(TADDR addr, PendingBreakpoint *pCur)
6557     {
6558         // Only go forward if the module matches the current PendingBreakpoint
6559         if (!pCur->ModuleMatches(addr))
6560         {
6561             return FALSE;
6562         }
6563
6564         ToRelease<IXCLRDataModule> mod;
6565         if (FAILED(g_sos->GetModule(addr, &mod)))
6566         {
6567             return FALSE;
6568         }
6569
6570         if(pCur->methodToken == 0)
6571         {
6572             return FALSE;
6573         }
6574
6575         ToRelease<IXCLRDataMethodDefinition> pMeth = NULL;
6576         mod->GetMethodDefinitionByToken(pCur->methodToken, &pMeth);
6577         // We may not need the code notification. Maybe it was ngen'd and we
6578         // already have the method?
6579         // We can delete the current entry if ResolveMethodInstances() set all BPs
6580         return ResolveMethodInstances(pMeth, pCur->ilOffset);
6581     }
6582 };
6583
6584 Breakpoints g_bpoints;
6585 #endif
6586
6587 // Controls whether optimizations are disabled on module load and whether NGEN can be used
6588 BOOL g_fAllowJitOptimization = TRUE;
6589
6590 // Controls whether a one-shot breakpoint should be inserted the next time
6591 // execution is about to enter a catch clause
6592 BOOL g_stopOnNextCatch = FALSE;
6593
6594 // According to the latest debuggers these callbacks will not get called
6595 // unless the user (or an extension, like SOS :-)) had previously enabled
6596 // clrn with "sxe clrn".
6597 class CNotification : public IXCLRDataExceptionNotification4
6598 {
6599     static int s_condemnedGen;
6600
6601     int m_count;
6602     int m_dbgStatus;
6603 public:
6604     CNotification() 
6605         : m_count(0)
6606         , m_dbgStatus(DEBUG_STATUS_NO_CHANGE)
6607     {}
6608
6609     int GetDebugStatus()
6610     {
6611         return m_dbgStatus;
6612     }
6613
6614     STDMETHODIMP QueryInterface (REFIID iid, void **ppvObject)
6615     {
6616         if (ppvObject == NULL)
6617             return E_INVALIDARG;
6618
6619         if (IsEqualIID(iid, IID_IUnknown)
6620             || IsEqualIID(iid, IID_IXCLRDataExceptionNotification)
6621             || IsEqualIID(iid, IID_IXCLRDataExceptionNotification2)
6622             || IsEqualIID(iid, IID_IXCLRDataExceptionNotification3)
6623             || IsEqualIID(iid, IID_IXCLRDataExceptionNotification4))
6624         {
6625             *ppvObject = static_cast<IXCLRDataExceptionNotification4*>(this);
6626             AddRef();
6627             return S_OK;
6628         }
6629         else
6630             return E_NOINTERFACE;
6631
6632     }
6633
6634     STDMETHODIMP_(ULONG) AddRef(void) { return ++m_count; }
6635     STDMETHODIMP_(ULONG) Release(void)
6636     {
6637         m_count--;
6638         if (m_count < 0)
6639         {
6640             m_count = 0;
6641         }
6642         return m_count;
6643     }
6644
6645             
6646     /*
6647      * New code was generated or discarded for a method.:
6648      */
6649     STDMETHODIMP OnCodeGenerated(IXCLRDataMethodInstance* method)
6650     {
6651         // Some method has been generated, make a breakpoint and remove it.
6652         ULONG32 len = mdNameLen;
6653         LPWSTR szModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
6654         if (method->GetName(0, mdNameLen, &len, g_mdName) == S_OK)
6655         {            
6656             ToRelease<IXCLRDataModule> pMod;
6657             HRESULT hr = method->GetTokenAndScope(NULL, &pMod);
6658             if (SUCCEEDED(hr))
6659             {
6660                 len = mdNameLen;
6661                 if (pMod->GetName(mdNameLen, &len, szModuleName) == S_OK)
6662                 {
6663                     ExtOut("JITTED %S!%S\n", szModuleName, g_mdName);
6664
6665                     // Add breakpoint, perhaps delete pending breakpoint
6666                     DacpGetModuleAddress dgma;
6667                     if (SUCCEEDED(dgma.Request(pMod)))
6668                     {
6669 #ifndef FEATURE_PAL
6670                         g_bpoints.Update(TO_TADDR(dgma.ModulePtr), FALSE);
6671 #endif
6672                     }
6673                     else
6674                     {
6675                         ExtOut("Failed to request module address.\n");
6676                     }
6677                 }
6678             }
6679         }
6680
6681         m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6682         return S_OK;
6683     }
6684
6685     STDMETHODIMP OnCodeDiscarded(IXCLRDataMethodInstance* method)
6686     {
6687         return E_NOTIMPL;
6688     }
6689
6690     /*
6691      * The process or task reached the desired execution state.
6692      */
6693     STDMETHODIMP OnProcessExecution(ULONG32 state) { return E_NOTIMPL; }
6694     STDMETHODIMP OnTaskExecution(IXCLRDataTask* task,
6695                             ULONG32 state) { return E_NOTIMPL; }
6696
6697     /*
6698      * The given module was loaded or unloaded.
6699      */
6700     STDMETHODIMP OnModuleLoaded(IXCLRDataModule* mod)
6701     {
6702         DacpGetModuleAddress dgma;
6703         if (SUCCEEDED(dgma.Request(mod)))
6704         {
6705 #ifndef FEATURE_PAL
6706             g_bpoints.Update(TO_TADDR(dgma.ModulePtr), TRUE);
6707 #endif
6708         }
6709
6710         if(!g_fAllowJitOptimization)
6711         {
6712             HRESULT hr;
6713             ToRelease<IXCLRDataModule2> mod2;
6714             if(FAILED(mod->QueryInterface(__uuidof(IXCLRDataModule2), (void**) &mod2)))
6715             {
6716                 ExtOut("SOS: warning, optimizations for this module could not be suppressed because this CLR version doesn't support the functionality\n");
6717             }
6718             else if(FAILED(hr = mod2->SetJITCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION)))
6719             {
6720                 if(hr == CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE)
6721                     ExtOut("SOS: warning, optimizations for this module could not be surpressed because an optimized prejitted image was loaded\n");
6722                 else
6723                     ExtOut("SOS: warning, optimizations for this module could not be surpressed hr=0x%x\n", hr);
6724             }
6725         }
6726         
6727         m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6728         return S_OK;
6729     }
6730
6731     STDMETHODIMP OnModuleUnloaded(IXCLRDataModule* mod)
6732     {
6733         DacpGetModuleAddress dgma;
6734         if (SUCCEEDED(dgma.Request(mod)))
6735         {
6736 #ifndef FEATURE_PAL
6737             g_bpoints.RemovePendingForModule(TO_TADDR(dgma.ModulePtr));
6738 #endif
6739         }
6740
6741         m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6742         return S_OK;
6743     }
6744
6745     /*
6746      * The given type was loaded or unloaded.
6747      */
6748     STDMETHODIMP OnTypeLoaded(IXCLRDataTypeInstance* typeInst) 
6749     { return E_NOTIMPL; }
6750     STDMETHODIMP OnTypeUnloaded(IXCLRDataTypeInstance* typeInst) 
6751     { return E_NOTIMPL; }
6752
6753     STDMETHODIMP OnAppDomainLoaded(IXCLRDataAppDomain* domain)
6754     { return E_NOTIMPL; }
6755     STDMETHODIMP OnAppDomainUnloaded(IXCLRDataAppDomain* domain)
6756     { return E_NOTIMPL; }
6757     STDMETHODIMP OnException(IXCLRDataExceptionState* exception)
6758     { return E_NOTIMPL; }
6759
6760     STDMETHODIMP OnGcEvent(GcEvtArgs gcEvtArgs)
6761 {
6762         // by default don't stop on these notifications...
6763         m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6764
6765         IXCLRDataProcess2* idp2 = NULL;
6766         if (SUCCEEDED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2)))
6767         {
6768             if (gcEvtArgs.typ == GC_MARK_END)
6769             {
6770                 // erase notification request
6771                 GcEvtArgs gea = { GC_MARK_END, { 0 } };
6772                 idp2->SetGcNotification(gea);
6773
6774                 s_condemnedGen = bitidx(gcEvtArgs.condemnedGeneration);
6775
6776                 ExtOut("CLR notification: GC - Performing a gen %d collection. Determined surviving objects...\n", s_condemnedGen);
6777
6778                 // GC_MARK_END notification means: give the user a chance to examine the debuggee
6779                 m_dbgStatus = DEBUG_STATUS_BREAK;
6780             }
6781         }
6782
6783         return S_OK;
6784     }
6785
6786      /*
6787      * Catch is about to be entered
6788      */
6789     STDMETHODIMP ExceptionCatcherEnter(IXCLRDataMethodInstance* method, DWORD catcherNativeOffset)
6790     {
6791         if(g_stopOnNextCatch)
6792         {
6793             CLRDATA_ADDRESS startAddr;
6794             if(method->GetRepresentativeEntryAddress(&startAddr) == S_OK)
6795             {
6796                 CHAR buffer[100];
6797                 sprintf_s(buffer, _countof(buffer), "bp /1 %p", (void*) (size_t) (startAddr+catcherNativeOffset));
6798                 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
6799             }
6800             g_stopOnNextCatch = FALSE;
6801         }
6802
6803         m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6804         return S_OK;
6805     }
6806
6807     static int GetCondemnedGen()
6808     {
6809         return s_condemnedGen;
6810     }
6811
6812 };
6813
6814 int CNotification::s_condemnedGen = -1;
6815
6816 BOOL CheckCLRNotificationEvent(DEBUG_LAST_EVENT_INFO_EXCEPTION* pdle)
6817 {
6818     ULONG Type, ProcessId, ThreadId;
6819     ULONG ExtraInformationUsed;
6820     HRESULT Status = g_ExtControl->GetLastEventInformation( &Type,
6821                                                     &ProcessId,
6822                                                     &ThreadId,
6823                                                     pdle,
6824                                                     sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION),
6825                                                     &ExtraInformationUsed,
6826                                                     NULL,
6827                                                     0,
6828                                                     NULL);
6829
6830     if (Status != S_OK || Type != DEBUG_EVENT_EXCEPTION)
6831     {
6832         return FALSE;
6833     }
6834
6835     if (!pdle->FirstChance || pdle->ExceptionRecord.ExceptionCode != CLRDATA_NOTIFY_EXCEPTION)
6836     {
6837         return FALSE;
6838     }
6839
6840     return TRUE;
6841     }
6842
6843 HRESULT HandleCLRNotificationEvent()
6844 {
6845     HRESULT Status = E_FAIL;
6846     /*
6847      * Did we get module load notification? If so, check if any in our pending list
6848      * need to be registered for jit notification.
6849      *
6850      * Did we get a jit notification? If so, check if any can be removed and
6851      * real breakpoints be set.
6852      */
6853     DEBUG_LAST_EVENT_INFO_EXCEPTION dle;
6854     CNotification Notification;
6855
6856     if (!CheckCLRNotificationEvent(&dle))
6857     {
6858         ExtOut("Expecting first chance CLRN exception\n");
6859         return Status;
6860     }
6861
6862     // Notification only needs to live for the lifetime of the call below, so it's a non-static
6863     // local.
6864     if (g_clrData->TranslateExceptionRecordToNotification(&dle.ExceptionRecord, &Notification) != S_OK)
6865     {
6866         ExtOut("Error processing exception notification\n");
6867         return Status;
6868     }
6869     else
6870     {
6871         switch (Notification.GetDebugStatus())
6872         {
6873             case DEBUG_STATUS_GO:
6874             case DEBUG_STATUS_GO_HANDLED:
6875             case DEBUG_STATUS_GO_NOT_HANDLED:
6876                 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "g", 0);
6877                 break;
6878             default:
6879                 break;
6880         }
6881     }
6882
6883     return Status;
6884 }
6885
6886
6887 DECLARE_API(HandleCLRN)
6888 {
6889     INIT_API();    
6890     MINIDUMP_NOT_SUPPORTED();    
6891
6892     return HandleCLRNotificationEvent();
6893 }
6894
6895 #ifndef FEATURE_PAL
6896 DECLARE_API(bpmd)
6897 {
6898     INIT_API_NOEE();    
6899     MINIDUMP_NOT_SUPPORTED();    
6900     int i;
6901     char buffer[1024];    
6902     
6903     if (IsDumpFile())
6904     {
6905         ExtOut("!bpmd is not supported on a dump file.\n");
6906         return Status;
6907     }
6908     
6909     // We keep a list of managed breakpoints the user wants to set, and display pending bps
6910     // bpmd. If you call bpmd <module name> <method> we will set or update an existing bp.
6911     // bpmd acts as a feeder of breakpoints to bp when the time is right.
6912     //
6913
6914     StringHolder DllName,TypeName;
6915     int lineNumber;
6916     size_t Offset = 0;
6917
6918     DWORD_PTR pMD = NULL;
6919     BOOL fNoFutureModule = FALSE;
6920     BOOL fList = FALSE;
6921     size_t clearItem = 0; 
6922     BOOL fClearAll = FALSE;
6923     CMDOption option[] = 
6924     {   // name, vptr, type, hasValue
6925         {"-md", &pMD, COHEX, TRUE},
6926         {"-nofuturemodule", &fNoFutureModule, COBOOL, FALSE},
6927         {"-list", &fList, COBOOL, FALSE},
6928         {"-clear", &clearItem, COSIZE_T, TRUE},
6929         {"-clearall", &fClearAll, COBOOL, FALSE},
6930     };
6931     CMDValue arg[] = 
6932     {   // vptr, type
6933         {&DllName.data, COSTRING},
6934         {&TypeName.data, COSTRING},
6935         {&Offset, COSIZE_T},
6936     };
6937     size_t nArg;
6938     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
6939     {
6940         return Status;
6941     }
6942
6943     bool fBadParam = false;
6944     bool fIsFilename = false;
6945     int commandsParsed = 0;
6946
6947     if (pMD != NULL)
6948     {
6949         if (nArg != 0)
6950         {
6951             fBadParam = true;
6952         }
6953         commandsParsed++;
6954     }
6955     if (fList)
6956     {
6957         commandsParsed++;
6958         if (nArg != 0)
6959         {
6960             fBadParam = true;
6961         }
6962     }
6963     if (fClearAll)
6964     {
6965         commandsParsed++;
6966         if (nArg != 0)
6967         {
6968             fBadParam = true;
6969         }
6970     }
6971     if (clearItem != 0)
6972     {
6973         commandsParsed++;
6974         if (nArg != 0)
6975         {
6976             fBadParam = true;
6977         }
6978     }
6979     if (1 <= nArg && nArg <= 3)
6980     {
6981         commandsParsed++;
6982         // did we get dll and type name or file:line#? Search for a colon in the first arg
6983         // to see if it is in fact a file:line#
6984         CHAR* pColon = strchr(DllName.data, ':');
6985         if(NULL != pColon)
6986         {
6987             fIsFilename = true;
6988             *pColon = '\0';
6989             pColon++;
6990             if(1 != sscanf_s(pColon, "%d", &lineNumber))
6991             {
6992                 ExtOut("Unable to parse line number\n");
6993                 fBadParam = true;
6994             }
6995             else if(lineNumber < 0)
6996             {
6997                 ExtOut("Line number must be positive\n");
6998                 fBadParam = true;
6999             }
7000             if(nArg != 1) fBadParam = 1;
7001         }
7002     }
7003
7004     if (fBadParam || (commandsParsed != 1))
7005     {
7006         ExtOut("Usage: !bpmd -md <MethodDesc pointer>\n");
7007         ExtOut("Usage: !bpmd [-nofuturemodule] <module name> <managed function name> [<il offset>]\n");
7008         ExtOut("Usage: !bpmd <filename>:<line number>\n");
7009         ExtOut("Usage: !bpmd -list\n");
7010         ExtOut("Usage: !bpmd -clear <pending breakpoint number>\n");
7011         ExtOut("Usage: !bpmd -clearall\n");
7012         ExtOut("See \"!help bpmd\" for more details.\n");
7013         return Status;
7014     }
7015
7016     if (fList)
7017     {
7018         g_bpoints.ListBreakpoints();
7019         return Status;
7020     }
7021     if (clearItem != 0)
7022     {
7023         g_bpoints.ClearBreakpoint(clearItem);
7024         return Status;
7025     }
7026     if (fClearAll)
7027     {
7028         g_bpoints.ClearAllBreakpoints();
7029         return Status;
7030     }
7031     // Add a breakpoint
7032     // Do we already have this breakpoint?
7033     // Or, before setting it, is the module perhaps already loaded and code
7034     // is available? If so, don't add to our pending list, just go ahead and
7035     // set the real breakpoint.    
7036     
7037     LPWSTR ModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
7038     LPWSTR FunctionName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
7039     LPWSTR Filename = (LPWSTR)alloca(MAX_PATH * sizeof(WCHAR));
7040
7041     BOOL bNeedNotificationExceptions=FALSE;
7042
7043     if (pMD == NULL)
7044     {
7045         int numModule = 0;
7046         int numMethods = 0;
7047
7048         ArrayHolder<DWORD_PTR> moduleList = NULL;
7049
7050         if(!fIsFilename)
7051         {
7052             MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, ModuleName, mdNameLen);
7053             MultiByteToWideChar(CP_ACP, 0, TypeName.data, -1, FunctionName, mdNameLen);
7054         }
7055         else
7056         {
7057             MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, Filename, MAX_PATH);
7058         }
7059
7060         // get modules that may need a breakpoint bound
7061         if ((Status = CheckEEDll()) == S_OK)
7062         {
7063             if ((Status = LoadClrDebugDll()) != S_OK)
7064             {
7065                 // if the EE is loaded but DAC isn't we should stop.
7066                 DACMessage(Status);
7067                 return Status;
7068             }
7069             else
7070             {
7071                 // get the module list
7072                 moduleList =ModuleFromName(fIsFilename ? NULL : DllName.data, &numModule);
7073
7074                 // Its OK if moduleList is NULL
7075                 // There is a very normal case when checking for modules after clr is loaded
7076                 // but before any AppDomains or assemblies are created
7077                 // for example:
7078                 // >sxe ld:clr
7079                 // >g
7080                 // ...
7081                 // ModLoad: clr.dll
7082                 // >!bpmd Foo.dll Foo.Bar
7083             }
7084         }
7085
7086         
7087         // we can get here with EE not loaded => 0 modules
7088         //                      EE is loaded => 0 or more modules
7089         ArrayHolder<DWORD_PTR> pMDs = NULL;
7090         for (int iModule = 0; iModule < numModule; iModule++)
7091         {
7092             ToRelease<IXCLRDataModule> ModDef;
7093             if (g_sos->GetModule(moduleList[iModule], &ModDef) != S_OK)
7094                 continue;
7095
7096             HRESULT symbolsLoaded = S_FALSE;
7097             if(!fIsFilename)
7098             {
7099                 g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, moduleList[iModule], (DWORD)Offset);
7100             }
7101             else
7102             {
7103                 SymbolReader symbolReader;
7104                 symbolsLoaded = g_bpoints.LoadSymbolsForModule(moduleList[iModule], &symbolReader);
7105                 if(symbolsLoaded == S_OK &&
7106                    g_bpoints.ResolvePendingNonModuleBoundBreakpoint(Filename, lineNumber, moduleList[iModule], &symbolReader) == S_OK)
7107                 {
7108                     // if we have symbols then get the function name so we can lookup the MethodDescs
7109                     mdMethodDef methodDefToken;
7110                     ULONG32 ilOffset;
7111                     if(SUCCEEDED(symbolReader.ResolveSequencePoint(Filename, lineNumber, &methodDefToken, &ilOffset)))
7112                     {
7113                         ToRelease<IXCLRDataMethodDefinition> pMethodDef = NULL;
7114                         if (SUCCEEDED(ModDef->GetMethodDefinitionByToken(methodDefToken, &pMethodDef)))
7115                         {
7116                             ULONG32 nameLen = 0;
7117                             pMethodDef->GetName(0, mdNameLen, &nameLen, FunctionName);
7118                             
7119                             // get the size of the required buffer
7120                             int buffSize = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, 0, NULL, NULL);
7121                             
7122                             TypeName.data = new NOTHROW char[buffSize];
7123                             if (TypeName.data != NULL)
7124                             {
7125                                 int bytesWritten = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, buffSize, NULL, NULL);
7126                                 _ASSERTE(bytesWritten == buffSize);
7127                             }
7128                         }
7129                     }
7130                 }
7131             }
7132
7133             HRESULT gotMethodDescs = GetMethodDescsFromName(moduleList[iModule], ModDef, TypeName.data, &pMDs, &numMethods);
7134             if (FAILED(gotMethodDescs) && (!fIsFilename))
7135             {
7136                 // BPs via file name will enumerate through modules so there will be legitimate failures.
7137                 // for module/type name we already found a match so this shouldn't fail (this is the original behavior).
7138                 ExtOut("Error getting MethodDescs for module %p\n", moduleList[iModule]);
7139                 return Status;
7140             }
7141
7142             // for filename+line number only print extra info if symbols for this module are loaded (it can get quite noisy otherwise).
7143             if ((!fIsFilename) || (fIsFilename && symbolsLoaded == S_OK))
7144             {
7145                 ExtOut("Found %d methods in module %p...\n", numMethods, moduleList[iModule]);
7146
7147                 for (int i = 0; i < numMethods; i++)
7148                 {
7149                     if (pMDs[i] == MD_NOT_YET_LOADED)
7150                     {
7151                         continue;
7152                     }
7153                     ExtOut("MethodDesc = %p\n", (ULONG64) pMDs[i]);
7154                 }
7155             }
7156
7157             if(g_bpoints.Update(moduleList[iModule], FALSE))
7158                 bNeedNotificationExceptions = TRUE;
7159         }
7160
7161         if (!fNoFutureModule)
7162         {
7163             // add a pending breakpoint that will find future loaded modules, and
7164             // wait for the module load notification.
7165             if(!fIsFilename)
7166             {
7167                 g_bpoints.Add(ModuleName, FunctionName, NULL, (DWORD)Offset);
7168             }
7169             else
7170             {
7171                 g_bpoints.Add(Filename, lineNumber, NULL);
7172             }
7173             bNeedNotificationExceptions = TRUE;
7174         }
7175     }
7176     else /* We were given a MethodDesc already */
7177     {
7178         // if we've got an explicit MD, then we better have CLR and mscordacwks loaded
7179         if ((Status = CheckEEDll()) != S_OK)
7180         {
7181             EENotLoadedMessage(Status);
7182             return Status;
7183         }
7184         if ((Status = LoadClrDebugDll()) != S_OK)
7185         {
7186             DACMessage(Status);
7187             return Status;
7188         } 
7189
7190         DacpMethodDescData MethodDescData;
7191         ExtOut("MethodDesc = %p\n", (ULONG64) pMD);
7192         if (MethodDescData.Request(g_sos, TO_CDADDR(pMD)) != S_OK)
7193         {
7194             ExtOut("%p is not a valid MethodDesc\n", (ULONG64)pMD);
7195             return Status;
7196         }
7197         
7198         if (MethodDescData.bHasNativeCode)
7199         {
7200             IssueDebuggerBPCommand((size_t) MethodDescData.NativeCodeAddr);
7201         }
7202         else if (MethodDescData.bIsDynamic)
7203         {
7204             // Dynamic methods don't have JIT notifications. This is something we must
7205             // fix in the next release. Until then, you have a cumbersome user experience.
7206             ExtOut("This DynamicMethodDesc is not yet JITTED. Placing memory breakpoint at %p\n",
7207                 MethodDescData.AddressOfNativeCodeSlot);
7208             
7209             sprintf_s(buffer, _countof(buffer),
7210 #ifdef _TARGET_WIN64_
7211                 "ba w8"
7212 #else
7213                 "ba w4" 
7214 #endif // _TARGET_WIN64_
7215
7216                 " /1 %p \"bp poi(%p); g\"",
7217                 (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot,
7218                 (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot);
7219
7220             Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
7221             if (FAILED(Status))
7222             {
7223                 ExtOut("Unable to set breakpoint with IDebugControl::Execute: %x\n",Status);
7224                 ExtOut("Attempted to run: %s\n", buffer);                
7225             }            
7226         }
7227         else
7228         {
7229             // Must issue a pending breakpoint.
7230
7231             if (g_sos->GetMethodDescName(pMD, mdNameLen, FunctionName, NULL) != S_OK)
7232             {
7233                 ExtOut("Unable to get method name for MethodDesc %p\n", (ULONG64)pMD);
7234                 return Status;
7235             }
7236
7237             FileNameForModule ((DWORD_PTR) MethodDescData.ModulePtr, ModuleName);
7238
7239             // We didn't find code, add a breakpoint.
7240             g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, TO_TADDR(MethodDescData.ModulePtr), 0);
7241             g_bpoints.Update(TO_TADDR(MethodDescData.ModulePtr), FALSE);
7242             bNeedNotificationExceptions = TRUE;            
7243         }
7244     }
7245
7246     if (bNeedNotificationExceptions)
7247     {
7248         ExtOut("Adding pending breakpoints...\n");
7249         sprintf_s(buffer, _countof(buffer), "sxe -c \"!HandleCLRN\" clrn");
7250         Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);        
7251     }
7252
7253     return Status;
7254 }
7255 #endif
7256
7257 /**********************************************************************\
7258 * Routine Description:                                                 *
7259 *                                                                      *
7260 *    This function is called to dump the managed threadpool            *
7261 *                                                                      *
7262 \**********************************************************************/
7263 DECLARE_API(ThreadPool)
7264 {
7265     INIT_API();
7266     MINIDUMP_NOT_SUPPORTED();
7267
7268     DacpThreadpoolData threadpool;
7269
7270     if ((Status = threadpool.Request(g_sos)) == S_OK)
7271     {
7272         BOOL doHCDump = FALSE;
7273
7274         CMDOption option[] = 
7275         {   // name, vptr, type, hasValue
7276             {"-ti", &doHCDump, COBOOL, FALSE}
7277         };    
7278
7279         if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
7280         {
7281             return Status;
7282         }
7283
7284         ExtOut ("CPU utilization: %d%%\n", threadpool.cpuUtilization);            
7285         ExtOut ("Worker Thread:");
7286         ExtOut (" Total: %d", threadpool.NumWorkingWorkerThreads + threadpool.NumIdleWorkerThreads + threadpool.NumRetiredWorkerThreads);
7287         ExtOut (" Running: %d", threadpool.NumWorkingWorkerThreads);
7288         ExtOut (" Idle: %d", threadpool.NumIdleWorkerThreads);
7289         ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalWorkerThreads);        
7290         ExtOut (" MinLimit: %d", threadpool.MinLimitTotalWorkerThreads);        
7291         ExtOut ("\n");        
7292
7293         int numWorkRequests = 0;
7294         CLRDATA_ADDRESS workRequestPtr = threadpool.FirstUnmanagedWorkRequest;
7295         DacpWorkRequestData workRequestData;
7296         while (workRequestPtr)
7297         {
7298             if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK)
7299             {
7300                 ExtOut("    Failed to examine a WorkRequest\n");
7301                 return Status;
7302             }
7303             numWorkRequests++;
7304             workRequestPtr = workRequestData.NextWorkRequest;
7305         }
7306
7307         ExtOut ("Work Request in Queue: %d\n", numWorkRequests);    
7308         workRequestPtr = threadpool.FirstUnmanagedWorkRequest;
7309         while (workRequestPtr)
7310         {
7311             if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK)
7312             {
7313                 ExtOut("    Failed to examine a WorkRequest\n");
7314                 return Status;
7315             }
7316
7317             if (workRequestData.Function == threadpool.AsyncTimerCallbackCompletionFPtr)
7318                 ExtOut ("    AsyncTimerCallbackCompletion TimerInfo@%p\n", (ULONG64)workRequestData.Context);
7319             else
7320                 ExtOut ("    Unknown Function: %p  Context: %p\n", (ULONG64)workRequestData.Function,
7321                     (ULONG64)workRequestData.Context);
7322
7323             workRequestPtr = workRequestData.NextWorkRequest;
7324         }
7325
7326         if (doHCDump)
7327         {
7328             ExtOut ("--------------------------------------\n");
7329             ExtOut ("\nThread Injection History\n");
7330             if (threadpool.HillClimbingLogSize > 0)
7331             {
7332                 static char const * const TransitionNames[] = 
7333                 {
7334                     "Warmup", 
7335                     "Initializing",
7336                     "RandomMove",
7337                     "ClimbingMove",
7338                     "ChangePoint",
7339                     "Stabilizing",
7340                     "Starvation",
7341                     "ThreadTimedOut",
7342                     "Undefined"
7343                 };
7344
7345                 ExtOut("\n    Time Transition     New #Threads      #Samples   Throughput\n");
7346                 DacpHillClimbingLogEntry entry;
7347
7348                 // get the most recent entry first, so we can calculate time offsets
7349                 
7350                 int index = (threadpool.HillClimbingLogFirstIndex + threadpool.HillClimbingLogSize-1) % HillClimbingLogCapacity;
7351                 CLRDATA_ADDRESS entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry));
7352                 if ((Status = entry.Request(g_sos,entryPtr))!=S_OK)
7353                 {
7354                     ExtOut("    Failed to examine a HillClimbing log entry\n");
7355                     return Status;
7356                 }
7357                 DWORD endTime = entry.TickCount;
7358
7359                 for (int i = 0; i < threadpool.HillClimbingLogSize; i++)
7360                 {
7361                     index = (i + threadpool.HillClimbingLogFirstIndex) % HillClimbingLogCapacity;
7362                     entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry));
7363
7364                     if ((Status = entry.Request(g_sos,entryPtr))!=S_OK)
7365                     {
7366                         ExtOut("    Failed to examine a HillClimbing log entry\n");
7367                         return Status;
7368                     }
7369
7370                     ExtOut("%8.2lf %-14s %12d  %12d  %11.2lf\n",
7371                         (double)(int)(entry.TickCount - endTime) / 1000.0,
7372                         TransitionNames[entry.Transition],
7373                         entry.NewControlSetting,
7374                         entry.LastHistoryCount,
7375                         entry.LastHistoryMean);
7376                 }
7377             }
7378         }
7379             
7380         ExtOut ("--------------------------------------\n");
7381         ExtOut ("Number of Timers: %d\n", threadpool.NumTimers);
7382         ExtOut ("--------------------------------------\n");
7383         
7384         ExtOut ("Completion Port Thread:");
7385         ExtOut ("Total: %d", threadpool.NumCPThreads);    
7386         ExtOut (" Free: %d", threadpool.NumFreeCPThreads);    
7387         ExtOut (" MaxFree: %d", threadpool.MaxFreeCPThreads);    
7388         ExtOut (" CurrentLimit: %d", threadpool.CurrentLimitTotalCPThreads);
7389         ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalCPThreads);
7390         ExtOut (" MinLimit: %d", threadpool.MinLimitTotalCPThreads);        
7391         ExtOut ("\n");
7392     }
7393     else
7394     {
7395         ExtOut("Failed to request ThreadpoolMgr information\n");
7396     }
7397     return Status;
7398 }
7399
7400 DECLARE_API(FindAppDomain)
7401 {
7402     INIT_API();
7403     MINIDUMP_NOT_SUPPORTED();    
7404     
7405     DWORD_PTR p_Object = NULL;
7406     BOOL dml = FALSE;
7407
7408     CMDOption option[] = 
7409     {   // name, vptr, type, hasValue
7410         {"/d", &dml, COBOOL, FALSE},
7411     };
7412     CMDValue arg[] = 
7413     {   // vptr, type
7414         {&p_Object, COHEX},
7415     };
7416     size_t nArg;
7417
7418     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
7419     {
7420         return Status;
7421     }
7422
7423     EnableDMLHolder dmlHolder(dml);
7424     
7425     if ((p_Object == 0) || !sos::IsObject(p_Object))
7426     {
7427         ExtOut("%p is not a valid object\n", (ULONG64) p_Object);
7428         return Status;
7429     }
7430     
7431     DacpAppDomainStoreData adstore;
7432     if (adstore.Request(g_sos) != S_OK)
7433     {
7434         ExtOut("Error getting AppDomain information\n");
7435         return Status;
7436     }    
7437
7438     CLRDATA_ADDRESS appDomain = GetAppDomain (TO_CDADDR(p_Object));
7439
7440     if (appDomain != NULL)
7441     {
7442         DMLOut("AppDomain: %s\n", DMLDomain(appDomain));
7443         if (appDomain == adstore.sharedDomain)
7444         {
7445             ExtOut("Name:      Shared Domain\n");
7446             ExtOut("ID:        (shared domain)\n");            
7447         }
7448         else if (appDomain == adstore.systemDomain)
7449         {
7450             ExtOut("Name:      System Domain\n");
7451             ExtOut("ID:        (system domain)\n");
7452         }
7453         else
7454         {
7455             DacpAppDomainData domain;
7456             if ((domain.Request(g_sos, appDomain) != S_OK) ||
7457                 (g_sos->GetAppDomainName(appDomain,mdNameLen,g_mdName, NULL)!=S_OK))
7458             {
7459                 ExtOut("Error getting AppDomain %p.\n", (ULONG64) appDomain);
7460                 return Status;
7461             }
7462
7463             ExtOut("Name:      %S\n", (g_mdName[0]!=L'\0') ? g_mdName : L"None");
7464             ExtOut("ID:        %d\n", domain.dwId);
7465         }
7466     }
7467     else
7468     {
7469         ExtOut("The type is declared in the shared domain and other\n");
7470         ExtOut("methods of finding the AppDomain failed. Try running\n");
7471         if (IsDMLEnabled())
7472             DMLOut("<exec cmd=\"!gcroot /d %p\">!gcroot %p</exec>, and if you find a root on a\n", p_Object, p_Object);
7473         else
7474             ExtOut("!gcroot %p, and if you find a root on a\n", p_Object);
7475         ExtOut("stack, check the AppDomain of that stack with !threads.\n");
7476         ExtOut("Note that the Thread could have transitioned between\n");
7477         ExtOut("multiple AppDomains.\n");
7478     }
7479     
7480     return Status;
7481 }
7482
7483 /**********************************************************************\
7484 * Routine Description:                                                 *
7485 *                                                                      *
7486 *    This function is called to get the COM state (e.g. APT,contexe    *
7487 *    activity.                                                         *  
7488 *                                                                      *
7489 \**********************************************************************/
7490 #ifdef FEATURE_COMINTEROP
7491 DECLARE_API(COMState)
7492 {
7493     INIT_API();
7494     MINIDUMP_NOT_SUPPORTED();    
7495     
7496
7497     ULONG numThread;
7498     ULONG maxId;
7499     g_ExtSystem->GetTotalNumberThreads(&numThread,&maxId);
7500
7501     ULONG curId;
7502     g_ExtSystem->GetCurrentThreadId(&curId);
7503
7504     SIZE_T AllocSize;
7505     if (!ClrSafeInt<SIZE_T>::multiply(sizeof(ULONG), numThread, AllocSize))
7506     {
7507         ExtOut("  Error!  integer overflow on numThread 0x%08x\n", numThread);
7508         return Status;
7509     }
7510     ULONG *ids = (ULONG*)alloca(AllocSize);
7511     ULONG *sysIds = (ULONG*)alloca(AllocSize);
7512     g_ExtSystem->GetThreadIdsByIndex(0,numThread,ids,sysIds);
7513 #if defined(_TARGET_WIN64_)
7514     ExtOut("      ID             TEB  APT    APTId CallerTID          Context\n");
7515 #else
7516     ExtOut("     ID     TEB   APT    APTId CallerTID Context\n");
7517 #endif
7518     for (ULONG i = 0; i < numThread; i ++) {
7519         g_ExtSystem->SetCurrentThreadId(ids[i]);
7520         CLRDATA_ADDRESS cdaTeb;
7521         g_ExtSystem->GetCurrentThreadTeb(&cdaTeb);
7522         ExtOut("%3d %4x %p", ids[i], sysIds[i], CDA_TO_UL64(cdaTeb));
7523         // Apartment state
7524         TADDR OleTlsDataAddr;
7525         if (SafeReadMemory(TO_TADDR(cdaTeb) + offsetof(TEB,ReservedForOle),
7526                             &OleTlsDataAddr,
7527                             sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0) {
7528             DWORD AptState;
7529             if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags),
7530                                &AptState,
7531                                sizeof(AptState), NULL)) {
7532                 if (AptState & OLETLS_APARTMENTTHREADED) {
7533                     ExtOut(" STA");
7534                 }
7535                 else if (AptState & OLETLS_MULTITHREADED) {
7536                     ExtOut(" MTA");
7537                 }
7538                 else if (AptState & OLETLS_INNEUTRALAPT) {
7539                     ExtOut(" NTA");
7540                 }
7541                 else {
7542                     ExtOut(" Ukn");
7543                 }
7544
7545                 // Read these fields only if we were able to read anything of the SOleTlsData structure
7546                 DWORD dwApartmentID;
7547                 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwApartmentID),
7548                                    &dwApartmentID,
7549                                    sizeof(dwApartmentID), NULL)) {
7550                     ExtOut(" %8x", dwApartmentID);
7551                 }
7552                 else
7553                     ExtOut(" %8x", 0);
7554                 
7555                 DWORD dwTIDCaller;
7556                 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwTIDCaller),
7557                                    &dwTIDCaller,
7558                                    sizeof(dwTIDCaller), NULL)) {
7559                     ExtOut("  %8x", dwTIDCaller);
7560                 }
7561                 else
7562                     ExtOut("  %8x", 0);
7563                 
7564                 size_t Context;
7565                 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,pCurrentCtx),
7566                                    &Context,
7567                                    sizeof(Context), NULL)) {
7568                     ExtOut(" %p", (ULONG64)Context);
7569                 }
7570                 else
7571                     ExtOut(" %p", (ULONG64)0);
7572
7573             }
7574             else
7575                 ExtOut(" Ukn");            
7576         }
7577         else
7578             ExtOut(" Ukn");
7579         ExtOut("\n");
7580     }
7581
7582     g_ExtSystem->SetCurrentThreadId(curId);
7583     return Status;
7584 }
7585 #endif // FEATURE_COMINTEROP
7586
7587 BOOL traverseEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
7588 {
7589     size_t methodStart = (size_t) token;
7590     
7591     if (IsInterrupt())
7592     {
7593         return FALSE;
7594     }
7595
7596     ExtOut("EHHandler %d: %s ", clauseIndex, EHTypeName(pEHInfo->clauseType));
7597
7598     LPCWSTR typeName = EHTypedClauseTypeName(pEHInfo);
7599     if (typeName != NULL)
7600     {
7601         ExtOut("catch(%S) ", typeName);
7602     }
7603
7604     if (IsClonedFinally(pEHInfo))
7605         ExtOut("(cloned finally)");
7606     else if (pEHInfo->isDuplicateClause)
7607         ExtOut("(duplicate)");
7608
7609     ExtOut("\n");
7610     ExtOut("Clause:  ");
7611
7612     ULONG64 addrStart = pEHInfo->tryStartOffset + methodStart;
7613     ULONG64 addrEnd   = pEHInfo->tryEndOffset   + methodStart;
7614
7615 #ifdef _WIN64
7616     ExtOut("[%08x`%08x, %08x`%08x]",
7617             (ULONG)(addrStart >> 32), (ULONG)addrStart,
7618             (ULONG)(addrEnd   >> 32), (ULONG)addrEnd);
7619 #else
7620     ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd);
7621 #endif
7622
7623     ExtOut(" [%x, %x]\n", 
7624         (UINT32) pEHInfo->tryStartOffset,
7625         (UINT32) pEHInfo->tryEndOffset);
7626
7627     ExtOut("Handler: ");
7628
7629     addrStart = pEHInfo->handlerStartOffset + methodStart;
7630     addrEnd   = pEHInfo->handlerEndOffset   + methodStart;
7631
7632 #ifdef _WIN64
7633     ExtOut("[%08x`%08x, %08x`%08x]",
7634             (ULONG)(addrStart >> 32), (ULONG)addrStart,
7635             (ULONG)(addrEnd   >> 32), (ULONG)addrEnd);
7636 #else
7637     ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd);
7638 #endif
7639
7640     ExtOut(" [%x, %x]\n", 
7641         (UINT32) pEHInfo->handlerStartOffset,
7642         (UINT32) pEHInfo->handlerEndOffset);
7643     
7644     if (pEHInfo->clauseType == EHFilter)
7645     {
7646         ExtOut("Filter: ");
7647
7648         addrStart = pEHInfo->filterOffset + methodStart;
7649
7650 #ifdef _WIN64
7651         ExtOut("[%08x`%08x]", (ULONG)(addrStart >> 32), (ULONG)addrStart);
7652 #else
7653         ExtOut("[%08x]", (ULONG)addrStart);
7654 #endif
7655
7656         ExtOut(" [%x]\n",
7657             (UINT32) pEHInfo->filterOffset);
7658     }
7659     
7660     ExtOut("\n");        
7661     return TRUE;
7662 }
7663
7664
7665 DECLARE_API(EHInfo)
7666 {
7667     INIT_API();
7668     MINIDUMP_NOT_SUPPORTED();    
7669     
7670     DWORD_PTR dwStartAddr = NULL;
7671     BOOL dml = FALSE;
7672
7673     CMDOption option[] = 
7674     {   // name, vptr, type, hasValue
7675         {"/d", &dml, COBOOL, FALSE},
7676     };
7677
7678     CMDValue arg[] = 
7679     {   // vptr, type
7680         {&dwStartAddr, COHEX},
7681     };
7682
7683     size_t nArg;
7684     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg)) 
7685     {
7686         return Status;
7687     }
7688
7689     EnableDMLHolder dmlHolder(dml);
7690     DWORD_PTR tmpAddr = dwStartAddr;
7691
7692     if (!IsMethodDesc(dwStartAddr)) 
7693     {
7694         JITTypes jitType;
7695         DWORD_PTR methodDesc;
7696         DWORD_PTR gcinfoAddr;
7697         IP2MethodDesc (dwStartAddr, methodDesc, jitType, gcinfoAddr);
7698         tmpAddr = methodDesc;
7699     }
7700
7701     DacpMethodDescData MD;
7702     if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK))
7703     {
7704         ExtOut("%p is not a MethodDesc\n", (ULONG64)tmpAddr);
7705         return Status;
7706     }
7707
7708     if (1 == nArg && !MD.bHasNativeCode)
7709     {
7710         ExtOut("No EH info available\n");
7711         return Status;
7712     }
7713
7714     DacpCodeHeaderData codeHeaderData;
7715     if (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK)
7716     {
7717         ExtOut("Unable to get codeHeader information\n");
7718         return Status;
7719     }        
7720     
7721     DMLOut("MethodDesc:   %s\n", DMLMethodDesc(MD.MethodDescPtr));
7722     DumpMDInfo(TO_TADDR(MD.MethodDescPtr));
7723
7724     ExtOut("\n");
7725     Status = g_sos->TraverseEHInfo(TO_CDADDR(MD.NativeCodeAddr), traverseEh, (LPVOID)MD.NativeCodeAddr);
7726
7727     if (Status == E_ABORT)
7728     {
7729         ExtOut("<user aborted>\n");
7730     }
7731     else if (Status != S_OK)
7732     {
7733         ExtOut("Failed to perform EHInfo traverse\n");
7734     }
7735     
7736     return Status;
7737 }
7738
7739
7740 /**********************************************************************\
7741 * Routine Description:                                                 *
7742 *                                                                      *
7743 *    This function is called to dump the GC encoding of a managed      *
7744 *    function.                                                         *  
7745 *                                                                      *
7746 \**********************************************************************/
7747 DECLARE_API(GCInfo)
7748 {
7749     INIT_API();
7750     MINIDUMP_NOT_SUPPORTED();
7751     
7752
7753     TADDR taStartAddr = NULL;
7754     TADDR taGCInfoAddr;
7755     BOOL dml = FALSE;
7756
7757     CMDOption option[] = 
7758     {   // name, vptr, type, hasValue
7759         {"/d", &dml, COBOOL, FALSE},
7760     };
7761     CMDValue arg[] = 
7762     {   // vptr, type
7763         {&taStartAddr, COHEX},
7764     };
7765     size_t nArg;
7766     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg))
7767     {
7768         return Status;
7769     }
7770
7771     EnableDMLHolder dmlHolder(dml);
7772     TADDR tmpAddr = taStartAddr;
7773
7774     if (!IsMethodDesc(taStartAddr)) 
7775     {
7776         JITTypes jitType;
7777         TADDR methodDesc;
7778         TADDR gcinfoAddr;
7779         IP2MethodDesc(taStartAddr, methodDesc, jitType, gcinfoAddr);
7780         tmpAddr = methodDesc;
7781     }
7782
7783     DacpMethodDescData MD;
7784     if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK))
7785     {
7786         ExtOut("%p is not a valid MethodDesc\n", (ULONG64)taStartAddr);
7787         return Status;
7788     }
7789
7790     if (1 == nArg && !MD.bHasNativeCode)
7791     {
7792         ExtOut("No GC info available\n");
7793         return Status;
7794     }
7795
7796     DacpCodeHeaderData codeHeaderData;
7797
7798     if (
7799         // Try to get code header data from taStartAddr.  This will get the code
7800         // header corresponding to the IP address, even if the function was rejitted
7801         (codeHeaderData.Request(g_sos, TO_CDADDR(taStartAddr)) != S_OK) &&
7802
7803         // If that didn't work, just try to use the code address that the MD
7804         // points to.  If the function was rejitted, this will only give you the
7805         // original JITted code, but that's better than nothing
7806         (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK)
7807         )
7808     {
7809         // We always used to emit this (before rejit support), even if we couldn't get
7810         // the code header, so keep on doing so.
7811         ExtOut("entry point %p\n", SOS_PTR(MD.NativeCodeAddr));
7812
7813         // And now the error....
7814         ExtOut("Unable to get codeHeader information\n");
7815         return Status;
7816     }
7817
7818     // We have the code header, so use it to determine the method start
7819
7820     ExtOut("entry point %p\n", SOS_PTR(codeHeaderData.MethodStart));
7821
7822     if (codeHeaderData.JITType == TYPE_UNKNOWN)
7823     {
7824         ExtOut("unknown Jit\n");
7825         return Status;
7826     }
7827     else if (codeHeaderData.JITType == TYPE_JIT)
7828     {
7829         ExtOut("Normal JIT generated code\n");
7830     }
7831     else if (codeHeaderData.JITType == TYPE_PJIT)
7832     {
7833         ExtOut("preJIT generated code\n");
7834     }
7835
7836     taGCInfoAddr = TO_TADDR(codeHeaderData.GCInfo);
7837
7838     ExtOut("GC info %p\n", (ULONG64)taGCInfoAddr);
7839
7840     // assume that GC encoding table is never more than
7841     // 40 + methodSize * 2
7842     int tableSize = 0;
7843     if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) ||
7844         !ClrSafeInt<int>::addition(tableSize, 40, tableSize))
7845     {
7846         ExtOut("<integer overflow>\n");
7847         return E_FAIL;
7848     }
7849     ArrayHolder<BYTE> table = new NOTHROW BYTE[tableSize];
7850     if (table == NULL)
7851     {
7852         ExtOut("Could not allocate memory to read the gc info.\n");
7853         return E_OUTOFMEMORY;
7854     }
7855     
7856     memset(table, 0, tableSize);
7857     // We avoid using move here, because we do not want to return
7858     if (!SafeReadMemory(taGCInfoAddr, table, tableSize, NULL))
7859     {
7860         ExtOut("Could not read memory %p\n", (ULONG64)taGCInfoAddr);
7861         return Status;
7862     }
7863
7864     // Mutable table pointer since we need to pass the appropriate
7865     // offset into the table to DumpGCTable.
7866     BYTE *pTable = table;
7867     unsigned int methodSize = (unsigned int)codeHeaderData.MethodSize;
7868
7869     g_targetMachine->DumpGCInfo(pTable, methodSize, ExtOut, true /*encBytes*/, true /*bPrintHeader*/);
7870
7871     return Status;
7872 }
7873
7874 #if !defined(FEATURE_PAL)
7875
7876 void DecodeGCTableEntry (const char *fmt, ...)
7877 {
7878     GCEncodingInfo *pInfo = (GCEncodingInfo*)GetFiberData();
7879     va_list va;
7880
7881     //
7882     // Append the new data to the buffer
7883     //
7884
7885     va_start(va, fmt);
7886
7887     int cch = _vsnprintf_s(&pInfo->buf[pInfo->cch], _countof(pInfo->buf) - pInfo->cch, _countof(pInfo->buf) - pInfo->cch - 1, fmt, va);
7888     if (cch >= 0)
7889         pInfo->cch += cch;
7890
7891     va_end(va);
7892
7893     pInfo->buf[pInfo->cch] = '\0';
7894
7895     //
7896     // If there are complete lines in the buffer, decode them.
7897     //
7898
7899     for (;;)
7900     {
7901         char *pNewLine = strchr(pInfo->buf, '\n');
7902
7903         if (!pNewLine)
7904             break;
7905
7906         //
7907         // The line should start with a 16-bit (x86) or 32-bit (non-x86) hex
7908         // offset.  strtoul returns ULONG_MAX or 0 on failure.  0 is a valid
7909         // offset for the first encoding, or while the last offset was 0.
7910         //
7911
7912         if (isxdigit(pInfo->buf[0]))
7913         {
7914             char *pEnd;
7915             ULONG ofs = strtoul(pInfo->buf, &pEnd, 16);
7916
7917             if (   isspace(*pEnd)
7918                 && -1 != ofs 
7919                 && (   -1 == pInfo->ofs
7920                     || 0 == pInfo->ofs
7921                     || ofs > 0))
7922             {
7923                 pInfo->ofs = ofs;
7924                 *pNewLine = '\0';
7925
7926                 SwitchToFiber(pInfo->pvMainFiber);
7927             }
7928         }
7929         else if (0 == strncmp(pInfo->buf, "Untracked:", 10))
7930         {
7931             pInfo->ofs = 0;
7932             *pNewLine = '\0';
7933
7934             SwitchToFiber(pInfo->pvMainFiber);
7935         }
7936
7937         //
7938         // Shift the remaining data to the start of the buffer
7939         //
7940
7941         strcpy_s(pInfo->buf, _countof(pInfo->buf), pNewLine+1);
7942         pInfo->cch = (int)strlen(pInfo->buf);
7943     }
7944 }
7945
7946
7947 VOID CALLBACK DumpGCTableFiberEntry (LPVOID pvGCEncodingInfo)
7948 {
7949     GCEncodingInfo *pInfo = (GCEncodingInfo*)pvGCEncodingInfo;
7950
7951     g_targetMachine->DumpGCInfo(pInfo->table, pInfo->methodSize, DecodeGCTableEntry, false /*encBytes*/, false /*bPrintHeader*/);
7952
7953     pInfo->fDoneDecoding = true;
7954     SwitchToFiber(pInfo->pvMainFiber);
7955 }
7956 #endif // !FEATURE_PAL
7957
7958 BOOL gatherEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
7959 {
7960     SOSEHInfo *pInfo = (SOSEHInfo *) token;
7961
7962     if (pInfo == NULL)
7963     {
7964         return FALSE;
7965     }
7966     
7967     if (pInfo->m_pInfos == NULL)
7968     {
7969         // First time, initialize structure
7970         pInfo->EHCount = totalClauses;
7971         pInfo->m_pInfos = new NOTHROW DACEHInfo[totalClauses];
7972         if (pInfo->m_pInfos == NULL)
7973         {
7974             ReportOOM();            
7975             return FALSE;
7976         }
7977     }
7978
7979     pInfo->m_pInfos[clauseIndex] = *((DACEHInfo*)pEHInfo);
7980     return TRUE;
7981 }
7982
7983
7984 /**********************************************************************\
7985 * Routine Description:                                                 *
7986 *                                                                      *
7987 *    This function is called to unassembly a managed function.         *
7988 *    It tries to print symbolic info for function call, contants...    *  
7989 *                                                                      *
7990 \**********************************************************************/
7991 DECLARE_API(u)
7992 {
7993     INIT_API();
7994     MINIDUMP_NOT_SUPPORTED();    
7995     
7996     
7997     DWORD_PTR dwStartAddr = NULL;
7998     BOOL fWithGCInfo = FALSE;
7999     BOOL fWithEHInfo = FALSE;
8000     BOOL bSuppressLines = FALSE;
8001     BOOL bDisplayOffsets = FALSE;
8002     BOOL dml = FALSE;
8003     size_t nArg;
8004
8005     CMDOption option[] = 
8006     {   // name, vptr, type, hasValue
8007         {"-gcinfo", &fWithGCInfo, COBOOL, FALSE},
8008         {"-ehinfo", &fWithEHInfo, COBOOL, FALSE},
8009         {"-n", &bSuppressLines, COBOOL, FALSE},
8010         {"-o", &bDisplayOffsets, COBOOL, FALSE},
8011         {"/d", &dml, COBOOL, FALSE},
8012     };
8013     CMDValue arg[] = 
8014     {   // vptr, type
8015         {&dwStartAddr, COHEX},
8016     };
8017     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (nArg < 1))
8018     {
8019         return Status;
8020     }
8021     // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
8022     ULONG symlines = 0;
8023     if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
8024     {
8025         symlines &= SYMOPT_LOAD_LINES;
8026     }
8027     bSuppressLines = bSuppressLines || (symlines == 0);
8028
8029     EnableDMLHolder dmlHolder(dml);
8030     // dwStartAddr is either some IP address or a MethodDesc.  Start off assuming it's a
8031     // MethodDesc.
8032     DWORD_PTR methodDesc = dwStartAddr;
8033     if (!IsMethodDesc(methodDesc))
8034     {
8035         // Not a methodDesc, so gotta find it ourselves
8036         DWORD_PTR tmpAddr = dwStartAddr;
8037         JITTypes jt;
8038         DWORD_PTR gcinfoAddr;
8039         IP2MethodDesc (tmpAddr, methodDesc, jt,
8040                        gcinfoAddr);
8041         if (!methodDesc || jt == TYPE_UNKNOWN)
8042         {
8043             // It is not managed code.
8044             ExtOut("Unmanaged code\n");
8045             UnassemblyUnmanaged(dwStartAddr, bSuppressLines);
8046             return Status;
8047         }
8048     }
8049
8050     DacpMethodDescData MethodDescData;
8051     if ((Status=MethodDescData.Request(g_sos, TO_CDADDR(methodDesc))) != S_OK)
8052     {
8053         ExtOut("Failed to get method desc for %p.\n", SOS_PTR(dwStartAddr));
8054         return Status;
8055     }    
8056
8057     if (!MethodDescData.bHasNativeCode)
8058     {
8059         ExtOut("Not jitted yet\n");
8060         return Status;
8061     }
8062
8063     // Get the appropriate code header. If we were passed an MD, then use
8064     // MethodDescData.NativeCodeAddr to find the code header; if we were passed an IP, use
8065     // that IP to find the code header. This ensures that, for rejitted functions, we
8066     // disassemble the rejit version that the user explicitly specified with their IP.
8067     DacpCodeHeaderData codeHeaderData;
8068     if (codeHeaderData.Request(
8069         g_sos, 
8070         TO_CDADDR(
8071             (dwStartAddr == methodDesc) ? MethodDescData.NativeCodeAddr : dwStartAddr)
8072             ) != S_OK)
8073
8074     {
8075         ExtOut("Unable to get codeHeader information\n");
8076         return Status;
8077     }                
8078     
8079     if (codeHeaderData.MethodStart == 0)
8080     {
8081         ExtOut("not a valid MethodDesc\n");
8082         return Status;
8083     }
8084     
8085     if (codeHeaderData.JITType == TYPE_UNKNOWN)
8086     {
8087         ExtOut("unknown Jit\n");
8088         return Status;
8089     }
8090     else if (codeHeaderData.JITType == TYPE_JIT)
8091     {
8092         ExtOut("Normal JIT generated code\n");
8093     }
8094     else if (codeHeaderData.JITType == TYPE_PJIT)
8095     {
8096         ExtOut("preJIT generated code\n");
8097     }
8098
8099     NameForMD_s(methodDesc, g_mdName, mdNameLen);
8100     ExtOut("%S\n", g_mdName);   
8101     if (codeHeaderData.ColdRegionStart != NULL)
8102     {
8103         ExtOut("Begin %p, size %x. Cold region begin %p, size %x\n",
8104             (ULONG64)codeHeaderData.MethodStart, codeHeaderData.HotRegionSize,
8105             (ULONG64)codeHeaderData.ColdRegionStart, codeHeaderData.ColdRegionSize);
8106     }
8107     else
8108     {
8109         ExtOut("Begin %p, size %x\n", (ULONG64)codeHeaderData.MethodStart, codeHeaderData.MethodSize);    
8110     }
8111
8112 #if !defined(FEATURE_PAL)
8113     //
8114     // Set up to mix gc info with the code if requested
8115     //
8116
8117     GCEncodingInfo gcEncodingInfo = {0};
8118
8119     // The actual GC Encoding Table, this is updated during the course of the function.
8120     gcEncodingInfo.table = NULL;
8121
8122     // The holder to make sure we clean up the memory for the table
8123     ArrayHolder<BYTE> table = NULL;
8124
8125     if (fWithGCInfo)
8126     {
8127         // assume that GC encoding table is never more than 40 + methodSize * 2
8128         int tableSize = 0;
8129         if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) ||
8130             !ClrSafeInt<int>::addition(tableSize, 40, tableSize))
8131         {
8132             ExtOut("<integer overflow>\n");
8133             return E_FAIL;
8134         }
8135
8136
8137         // Assign the new array to the mutable gcEncodingInfo table and to the
8138         // table ArrayHolder to clean this up when the function exits.
8139         table = gcEncodingInfo.table = new NOTHROW BYTE[tableSize];
8140         
8141         if (gcEncodingInfo.table == NULL)
8142         {
8143             ExtOut("Could not allocate memory to read the gc info.\n");
8144             return E_OUTOFMEMORY;
8145         }
8146         
8147         memset (gcEncodingInfo.table, 0, tableSize);
8148         // We avoid using move here, because we do not want to return
8149         if (!SafeReadMemory(TO_TADDR(codeHeaderData.GCInfo), gcEncodingInfo.table, tableSize, NULL))
8150         {
8151             ExtOut("Could not read memory %p\n", (ULONG64)codeHeaderData.GCInfo);
8152             return Status;
8153         }
8154
8155         //
8156         // Skip the info header
8157         //
8158         gcEncodingInfo.methodSize = (unsigned int)codeHeaderData.MethodSize;
8159
8160         //
8161         // DumpGCTable will call gcPrintf for each encoding.  We'd like a "give
8162         // me the next encoding" interface, but we're stuck with the callback.
8163         // To reconcile this without messing up too much code, we'll create a
8164         // fiber to dump the gc table.  When we need the next gc encoding,
8165         // we'll switch to this fiber.  The callback will note the next offset,
8166         // and switch back to the main fiber.
8167         //
8168
8169         gcEncodingInfo.ofs = -1;
8170         gcEncodingInfo.hotSizeToAdd = 0;
8171         
8172         gcEncodingInfo.pvMainFiber = ConvertThreadToFiber(NULL);
8173         if (!gcEncodingInfo.pvMainFiber && ERROR_ALREADY_FIBER == GetLastError())
8174             gcEncodingInfo.pvMainFiber = GetCurrentFiber();
8175         
8176         if (!gcEncodingInfo.pvMainFiber)
8177             return Status;
8178
8179         gcEncodingInfo.pvGCTableFiber = CreateFiber(0, DumpGCTableFiberEntry, &gcEncodingInfo);
8180         if (!gcEncodingInfo.pvGCTableFiber)
8181             return Status;
8182
8183         SwitchToFiber(gcEncodingInfo.pvGCTableFiber);
8184     }    
8185 #endif
8186
8187     SOSEHInfo *pInfo = NULL;
8188     if (fWithEHInfo)
8189     {
8190         pInfo = new NOTHROW SOSEHInfo;
8191         if (pInfo == NULL)
8192         {
8193             ReportOOM();                
8194         }
8195         else if (g_sos->TraverseEHInfo(MethodDescData.NativeCodeAddr, gatherEh, (LPVOID)pInfo) != S_OK)
8196         {
8197             ExtOut("Failed to gather EHInfo data\n");
8198             delete pInfo;
8199             pInfo = NULL;            
8200         }
8201     }
8202     
8203     if (codeHeaderData.ColdRegionStart == NULL)
8204     {
8205         g_targetMachine->Unassembly (
8206                 (DWORD_PTR) codeHeaderData.MethodStart,
8207                 ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.MethodSize,
8208                 dwStartAddr,
8209                 (DWORD_PTR) MethodDescData.GCStressCodeCopy,
8210 #if !defined(FEATURE_PAL)
8211                 fWithGCInfo ? &gcEncodingInfo : 
8212 #endif            
8213                 NULL,
8214                 pInfo,
8215                 bSuppressLines,
8216                 bDisplayOffsets
8217                 );
8218     }
8219     else
8220     {
8221         ExtOut("Hot region:\n");
8222         g_targetMachine->Unassembly (
8223                 (DWORD_PTR) codeHeaderData.MethodStart,
8224                 ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.HotRegionSize,
8225                 dwStartAddr,
8226                 (DWORD_PTR) MethodDescData.GCStressCodeCopy,
8227 #if !defined(FEATURE_PAL)
8228                 fWithGCInfo ? &gcEncodingInfo : 
8229 #endif            
8230                 NULL,
8231                 pInfo,
8232                 bSuppressLines,
8233                 bDisplayOffsets
8234                 );
8235
8236         ExtOut("Cold region:\n");
8237         
8238 #if !defined(FEATURE_PAL)
8239         // Displaying gcinfo for a cold region requires knowing the size of
8240         // the hot region preceeding.
8241         gcEncodingInfo.hotSizeToAdd = codeHeaderData.HotRegionSize;
8242 #endif            
8243         g_targetMachine->Unassembly (
8244                 (DWORD_PTR) codeHeaderData.ColdRegionStart,
8245                 ((DWORD_PTR)codeHeaderData.ColdRegionStart) + codeHeaderData.ColdRegionSize,
8246                 dwStartAddr,
8247                 ((DWORD_PTR) MethodDescData.GCStressCodeCopy) + codeHeaderData.HotRegionSize,                
8248 #if !defined(FEATURE_PAL)
8249                 fWithGCInfo ? &gcEncodingInfo : 
8250 #endif            
8251                 NULL,
8252                 pInfo,
8253                 bSuppressLines,
8254                 bDisplayOffsets
8255                 );
8256
8257     }
8258
8259     if (pInfo)
8260     {
8261         delete pInfo;
8262         pInfo = NULL;
8263     }
8264     
8265 #if !defined(FEATURE_PAL)
8266     if (fWithGCInfo)
8267         DeleteFiber(gcEncodingInfo.pvGCTableFiber);
8268 #endif
8269
8270     return Status;
8271 }
8272
8273 /**********************************************************************\
8274 * Routine Description:                                                 *
8275 *                                                                      *
8276 *    This function is called to dump the in-memory stress log          *
8277 *    !DumpLog [filename]                                               *
8278 *             will dump the stress log corresponding to the clr.dll    *
8279 *             loaded in the debuggee's VAS                             *
8280 *    !DumpLog -addr <addr_of_StressLog::theLog> [filename]             *
8281 *             will dump the stress log associated with any DLL linked  *
8282 *             against utilcode.lib, most commonly mscordbi.dll         *
8283 *             (e.g. !DumpLog -addr mscordbi!StressLog::theLog)         *
8284 *                                                                      *
8285 \**********************************************************************/
8286 DECLARE_API(DumpLog)
8287 {        
8288     INIT_API_NO_RET_ON_FAILURE();
8289
8290     MINIDUMP_NOT_SUPPORTED();    
8291
8292     const char* fileName = "StressLog.txt";
8293
8294     CLRDATA_ADDRESS StressLogAddress = NULL;
8295     
8296     StringHolder sFileName, sLogAddr;
8297     CMDOption option[] = 
8298     {   // name, vptr, type, hasValue
8299         {"-addr", &sLogAddr.data, COSTRING, TRUE}
8300     };
8301     CMDValue arg[] = 
8302     {   // vptr, type
8303         {&sFileName.data, COSTRING}
8304     };
8305     size_t nArg;
8306     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
8307     {
8308         return Status;
8309     }
8310     if (nArg > 0 && sFileName.data != NULL)
8311     {
8312         fileName = sFileName.data;
8313     }
8314
8315     // allow users to specify -addr mscordbdi!StressLog::theLog, for example.
8316     if (sLogAddr.data != NULL)
8317     {
8318         StressLogAddress = GetExpression(sLogAddr.data);
8319     }
8320
8321     if (StressLogAddress == NULL)
8322     {
8323         if (g_bDacBroken)
8324         {
8325             // Try to find stress log symbols
8326             DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog");
8327             StressLogAddress = dwAddr;        
8328         }
8329         else if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK)
8330         {
8331             ExtOut("Unable to find stress log via DAC\n");
8332             return E_FAIL;
8333         }
8334     }
8335
8336     if (StressLogAddress == NULL)
8337     {
8338         ExtOut("Please provide the -addr argument for the address of the stress log, since no recognized runtime is loaded.\n");
8339         return E_FAIL;
8340     }
8341
8342     ExtOut("Attempting to dump Stress log to file '%s'\n", fileName);
8343
8344     
8345     
8346     Status = StressLog::Dump(StressLogAddress, fileName, g_ExtData);
8347
8348     if (Status == S_OK)
8349         ExtOut("SUCCESS: Stress log dumped\n");
8350     else if (Status == S_FALSE)
8351         ExtOut("No Stress log in the image, no file written\n");
8352     else
8353         ExtOut("FAILURE: Stress log not dumped\n");
8354
8355     return Status;
8356 }
8357
8358 #ifdef TRACE_GC
8359
8360 DECLARE_API (DumpGCLog)
8361 {
8362     INIT_API_NODAC();
8363     MINIDUMP_NOT_SUPPORTED();    
8364     
8365
8366     if (GetEEFlavor() == UNKNOWNEE) 
8367     {
8368         ExtOut("CLR not loaded\n");
8369         return Status;
8370     }
8371
8372     const char* fileName = "GCLog.txt";
8373
8374     while (isspace (*args))
8375         args ++;
8376
8377     if (*args != 0)
8378         fileName = args;
8379     
8380     // Try to find stress log symbols
8381     DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_log_buffer");
8382     moveN (dwAddr, dwAddr);
8383
8384     if (dwAddr == 0)
8385     {
8386         dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_log_buffer");
8387         moveN (dwAddr, dwAddr);
8388         if (dwAddr == 0)
8389         {
8390             ExtOut("Can't get either WKS or SVR GC's log file");
8391             return E_FAIL;
8392         }
8393     }
8394
8395     ExtOut("Dumping GC log at %08x\n", dwAddr);
8396
8397     g_bDacBroken = FALSE;
8398     
8399     ExtOut("Attempting to dump GC log to file '%s'\n", fileName);
8400     
8401     Status = E_FAIL;
8402
8403     HANDLE hGCLog = CreateFileA(
8404         fileName,
8405         GENERIC_WRITE,
8406         FILE_SHARE_READ,
8407         NULL,
8408         CREATE_ALWAYS,
8409         FILE_ATTRIBUTE_NORMAL,
8410         NULL);
8411
8412     if (hGCLog == INVALID_HANDLE_VALUE)
8413     {
8414         ExtOut("failed to create file: %d\n", GetLastError());
8415         goto exit;
8416     }
8417
8418     int iLogSize = 1024*1024;
8419     BYTE* bGCLog = new NOTHROW BYTE[iLogSize];
8420     if (bGCLog == NULL)
8421     {
8422         ReportOOM();
8423         goto exit;
8424     }
8425
8426     memset (bGCLog, 0, iLogSize);
8427     if (!SafeReadMemory(dwAddr, bGCLog, iLogSize, NULL))
8428     {
8429         ExtOut("failed to read memory from %08x\n", dwAddr);
8430     }
8431
8432     int iRealLogSize = iLogSize - 1;
8433     while (iRealLogSize >= 0)
8434     {
8435         if (bGCLog[iRealLogSize] != '*')
8436         {
8437             break;
8438         }
8439
8440         iRealLogSize--;
8441     }
8442
8443     DWORD dwWritten = 0;
8444     WriteFile (hGCLog, bGCLog, iRealLogSize + 1, &dwWritten, NULL);
8445     Status = S_OK;
8446
8447 exit:
8448
8449     if (hGCLog != INVALID_HANDLE_VALUE)
8450     {
8451         CloseHandle (hGCLog);
8452     }
8453
8454     if (Status == S_OK)
8455         ExtOut("SUCCESS: Stress log dumped\n");
8456     else if (Status == S_FALSE)
8457         ExtOut("No Stress log in the image, no file written\n");
8458     else
8459         ExtOut("FAILURE: Stress log not dumped\n");
8460
8461     return Status;
8462 }
8463
8464 #endif //TRACE_GC
8465
8466 #ifndef FEATURE_PAL
8467 /**********************************************************************\
8468 * Routine Description:                                                 *
8469 *                                                                      *
8470 *    This function is called to dump the build number and type of the  *  
8471 *    mscoree.dll                                                       *
8472 *                                                                      *
8473 \**********************************************************************/
8474 DECLARE_API (EEVersion)
8475 {
8476     INIT_API();
8477
8478     EEFLAVOR eef = GetEEFlavor();
8479     if (eef == UNKNOWNEE) {
8480         ExtOut("CLR not loaded\n");
8481         return Status;
8482     }
8483     
8484     if (g_ExtSymbols2) {
8485         VS_FIXEDFILEINFO version;
8486         
8487         BOOL ret = GetEEVersion(&version);
8488             
8489         if (ret) 
8490         {
8491             if (version.dwFileVersionMS != (DWORD)-1)
8492             {
8493                 ExtOut("%u.%u.%u.%u",
8494                        HIWORD(version.dwFileVersionMS),
8495                        LOWORD(version.dwFileVersionMS),
8496                        HIWORD(version.dwFileVersionLS),
8497                        LOWORD(version.dwFileVersionLS));
8498                 if (version.dwFileFlags & VS_FF_DEBUG) 
8499                 {                    
8500                     ExtOut(" Checked or debug build");
8501                 }
8502                 else
8503                 { 
8504                     BOOL fRet = IsRetailBuild ((size_t)moduleInfo[eef].baseAddr);
8505
8506                     if (fRet)
8507                         ExtOut(" retail");
8508                     else
8509                         ExtOut(" free");
8510                 }
8511
8512                 ExtOut("\n");
8513             }
8514         }
8515     }    
8516     
8517     if (!InitializeHeapData ())
8518         ExtOut("GC Heap not initialized, so GC mode is not determined yet.\n");
8519     else if (IsServerBuild()) 
8520         ExtOut("Server mode with %d gc heaps\n", GetGcHeapCount()); 
8521     else
8522         ExtOut("Workstation mode\n");
8523
8524     if (!GetGcStructuresValid())
8525     {
8526         ExtOut("In plan phase of garbage collection\n");
8527     }
8528
8529     // Print SOS version
8530     VS_FIXEDFILEINFO sosVersion;
8531     if (GetSOSVersion(&sosVersion))
8532     {
8533         if (sosVersion.dwFileVersionMS != (DWORD)-1)
8534         {
8535             ExtOut("SOS Version: %u.%u.%u.%u",
8536                    HIWORD(sosVersion.dwFileVersionMS),
8537                    LOWORD(sosVersion.dwFileVersionMS),
8538                    HIWORD(sosVersion.dwFileVersionLS),
8539                    LOWORD(sosVersion.dwFileVersionLS));
8540             if (sosVersion.dwFileFlags & VS_FF_DEBUG) 
8541             {                    
8542                 ExtOut(" Checked or debug build");                    
8543             }
8544             else
8545             { 
8546                 ExtOut(" retail build");                    
8547             }
8548
8549             ExtOut("\n");
8550         }
8551     }
8552     return Status;
8553 }
8554 #endif // FEATURE_PAL
8555
8556 #ifndef FEATURE_PAL
8557 /**********************************************************************\
8558 * Routine Description:                                                 *
8559 *                                                                      *
8560 *    This function is called to print the environment setting for      *  
8561 *    the current process.                                              *
8562 *                                                                      *
8563 \**********************************************************************/
8564 DECLARE_API (ProcInfo)
8565 {
8566     INIT_API();
8567     MINIDUMP_NOT_SUPPORTED();        
8568
8569     if (IsDumpFile())
8570     {
8571         ExtOut("!ProcInfo is not supported on a dump file.\n");
8572         return Status;
8573     }
8574
8575 #define INFO_ENV        0x00000001
8576 #define INFO_TIME       0x00000002    
8577 #define INFO_MEM        0x00000004
8578 #define INFO_ALL        0xFFFFFFFF
8579
8580     DWORD fProcInfo = INFO_ALL;
8581
8582     if (_stricmp (args, "-env") == 0) {
8583         fProcInfo = INFO_ENV;
8584     }
8585
8586     if (_stricmp (args, "-time") == 0) {
8587         fProcInfo = INFO_TIME;
8588     }
8589
8590     if (_stricmp (args, "-mem") == 0) {
8591         fProcInfo = INFO_MEM;
8592     }
8593
8594     if (fProcInfo & INFO_ENV) {
8595         ExtOut("---------------------------------------\n");
8596         ExtOut("Environment\n");
8597         ULONG64 pPeb;
8598         g_ExtSystem->GetCurrentProcessPeb(&pPeb);
8599
8600         static ULONG Offset_ProcessParam = -1;
8601         static ULONG Offset_Environment = -1;
8602         if (Offset_ProcessParam == -1)
8603         {
8604             ULONG TypeId;
8605             ULONG64 NtDllBase;
8606             if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName ("ntdll",0,NULL,
8607                                                                &NtDllBase)))
8608             {
8609                 if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "PEB", &TypeId)))
8610                 {
8611                     if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
8612                                                          "ProcessParameters", &Offset_ProcessParam)))
8613                         Offset_ProcessParam = -1;
8614                 }
8615                 if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "_RTL_USER_PROCESS_PARAMETERS", &TypeId)))
8616                 {
8617                     if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
8618                                                          "Environment", &Offset_Environment)))
8619                         Offset_Environment = -1;
8620                 }
8621             }
8622         }
8623         // We can not get it from PDB.  Use the fixed one.
8624         if (Offset_ProcessParam == -1)
8625             Offset_ProcessParam = offsetof (DT_PEB, ProcessParameters);
8626
8627         if (Offset_Environment == -1)
8628             Offset_Environment = offsetof (DT_RTL_USER_PROCESS_PARAMETERS, Environment);
8629
8630
8631         ULONG64 addr = pPeb + Offset_ProcessParam;
8632         DWORD_PTR value;
8633         g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL);
8634         addr = value + Offset_Environment;
8635         g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL);
8636
8637         static WCHAR buffer[DT_OS_PAGE_SIZE/2];        
8638         ULONG readBytes = DT_OS_PAGE_SIZE;
8639         ULONG64 Page;
8640         if ((g_ExtData->ReadDebuggerData( DEBUG_DATA_MmPageSize, &Page, sizeof(Page), NULL)) == S_OK
8641             && Page > 0)
8642         {
8643             ULONG uPageSize = (ULONG)(ULONG_PTR)Page;
8644             if (readBytes > uPageSize) {
8645                 readBytes = uPageSize;
8646             }
8647         }        
8648         addr = value;
8649         while (1) {
8650             if (IsInterrupt())
8651                 return Status;
8652             if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &buffer, readBytes, NULL)))
8653                 break;
8654             addr += readBytes;
8655             WCHAR *pt = buffer;
8656             WCHAR *end = pt;
8657             while (pt < &buffer[DT_OS_PAGE_SIZE/2]) {
8658                 end = wcschr (pt, L'\0');
8659                 if (end == NULL) {
8660                     char format[20];
8661                     sprintf_s (format,_countof (format), "%dS", &buffer[DT_OS_PAGE_SIZE/2] - pt);
8662                     ExtOut(format, pt);
8663                     break;
8664                 }
8665                 else if (end == pt) {
8666                     break;
8667                 }
8668                 ExtOut("%S\n", pt);
8669                 pt = end + 1;
8670             }
8671             if (end == pt) {
8672                 break;
8673             }
8674         }
8675     }
8676     
8677     HANDLE hProcess = INVALID_HANDLE_VALUE;
8678     if (fProcInfo & (INFO_TIME | INFO_MEM)) {
8679         ULONG64 handle;
8680         g_ExtSystem->GetCurrentProcessHandle(&handle);
8681         hProcess = (HANDLE)handle;
8682     }
8683     
8684     if (!IsDumpFile() && fProcInfo & INFO_TIME) {
8685         FILETIME CreationTime;
8686         FILETIME ExitTime;
8687         FILETIME KernelTime;
8688         FILETIME UserTime;
8689
8690         typedef BOOL (WINAPI *FntGetProcessTimes)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
8691         static FntGetProcessTimes pFntGetProcessTimes = (FntGetProcessTimes)-1;
8692         if (pFntGetProcessTimes == (FntGetProcessTimes)-1) {
8693             HINSTANCE hstat = LoadLibrary ("Kernel32.dll");
8694             if (hstat != 0)
8695             {
8696                 pFntGetProcessTimes = (FntGetProcessTimes)GetProcAddress (hstat, "GetProcessTimes");
8697                 FreeLibrary (hstat);
8698             }
8699             else
8700                 pFntGetProcessTimes = NULL;
8701         }
8702
8703         if (pFntGetProcessTimes && pFntGetProcessTimes (hProcess,&CreationTime,&ExitTime,&KernelTime,&UserTime)) {
8704             ExtOut("---------------------------------------\n");
8705             ExtOut("Process Times\n");
8706             static char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
8707                         "Oct", "Nov", "Dec"};
8708             SYSTEMTIME SystemTime;
8709             FILETIME LocalFileTime;
8710             if (FileTimeToLocalFileTime (&CreationTime,&LocalFileTime)
8711                 && FileTimeToSystemTime (&LocalFileTime,&SystemTime)) {
8712                 ExtOut("Process Started at: %4d %s %2d %d:%d:%d.%02d\n",
8713                         SystemTime.wYear, Month[SystemTime.wMonth-1], SystemTime.wDay,
8714                         SystemTime.wHour, SystemTime.wMinute,
8715                         SystemTime.wSecond, SystemTime.wMilliseconds/10);
8716             }
8717         
8718             DWORD nDay = 0;
8719             DWORD nHour = 0;
8720             DWORD nMin = 0;
8721             DWORD nSec = 0;
8722             DWORD nHundred = 0;
8723             
8724             ULONG64 totalTime;
8725              
8726             totalTime = KernelTime.dwLowDateTime + (((ULONG64)KernelTime.dwHighDateTime) << 32);
8727             nDay = (DWORD)(totalTime/(24*3600*10000000ui64));
8728             totalTime %= 24*3600*10000000ui64;
8729             nHour = (DWORD)(totalTime/(3600*10000000ui64));
8730             totalTime %= 3600*10000000ui64;
8731             nMin = (DWORD)(totalTime/(60*10000000));
8732             totalTime %= 60*10000000;
8733             nSec = (DWORD)(totalTime/10000000);
8734             totalTime %= 10000000;
8735             nHundred = (DWORD)(totalTime/100000);
8736             ExtOut("Kernel CPU time   : %d days %02d:%02d:%02d.%02d\n",
8737                     nDay, nHour, nMin, nSec, nHundred);
8738             
8739             DWORD sDay = nDay;
8740             DWORD sHour = nHour;
8741             DWORD sMin = nMin;
8742             DWORD sSec = nSec;
8743             DWORD sHundred = nHundred;
8744             
8745             totalTime = UserTime.dwLowDateTime + (((ULONG64)UserTime.dwHighDateTime) << 32);
8746             nDay = (DWORD)(totalTime/(24*3600*10000000ui64));
8747             totalTime %= 24*3600*10000000ui64;
8748             nHour = (DWORD)(totalTime/(3600*10000000ui64));
8749             totalTime %= 3600*10000000ui64;
8750             nMin = (DWORD)(totalTime/(60*10000000));
8751             totalTime %= 60*10000000;
8752             nSec = (DWORD)(totalTime/10000000);
8753             totalTime %= 10000000;
8754             nHundred = (DWORD)(totalTime/100000);
8755             ExtOut("User   CPU time   : %d days %02d:%02d:%02d.%02d\n",
8756                     nDay, nHour, nMin, nSec, nHundred);
8757         
8758             sDay += nDay;
8759             sHour += nHour;
8760             sMin += nMin;
8761             sSec += nSec;
8762             sHundred += nHundred;
8763             if (sHundred >= 100) {
8764                 sSec += sHundred/100;
8765                 sHundred %= 100;
8766             }
8767             if (sSec >= 60) {
8768                 sMin += sSec/60;
8769                 sSec %= 60;
8770             }
8771             if (sMin >= 60) {
8772                 sHour += sMin/60;
8773                 sMin %= 60;
8774             }
8775             if (sHour >= 24) {
8776                 sDay += sHour/24;
8777                 sHour %= 24;
8778             }
8779             ExtOut("Total  CPU time   : %d days %02d:%02d:%02d.%02d\n",
8780                     sDay, sHour, sMin, sSec, sHundred);
8781         }
8782     }
8783
8784     if (!IsDumpFile() && fProcInfo & INFO_MEM) {
8785         typedef
8786         NTSTATUS
8787         (NTAPI
8788          *FntNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
8789
8790         static FntNtQueryInformationProcess pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)-1;
8791         if (pFntNtQueryInformationProcess == (FntNtQueryInformationProcess)-1) {
8792             HINSTANCE hstat = LoadLibrary ("ntdll.dll");
8793             if (hstat != 0)
8794             {
8795                 pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)GetProcAddress (hstat, "NtQueryInformationProcess");
8796                 FreeLibrary (hstat);
8797             }
8798             else
8799                 pFntNtQueryInformationProcess = NULL;
8800         }
8801         VM_COUNTERS memory;
8802         if (pFntNtQueryInformationProcess &&
8803             NT_SUCCESS (pFntNtQueryInformationProcess (hProcess,ProcessVmCounters,&memory,sizeof(memory),NULL))) {
8804             ExtOut("---------------------------------------\n");
8805             ExtOut("Process Memory\n");
8806             ExtOut("WorkingSetSize: %8d KB       PeakWorkingSetSize: %8d KB\n",
8807                     memory.WorkingSetSize/1024, memory.PeakWorkingSetSize/1024);
8808             ExtOut("VirtualSize:    %8d KB       PeakVirtualSize:    %8d KB\n", 
8809                     memory.VirtualSize/1024, memory.PeakVirtualSize/1024);
8810             ExtOut("PagefileUsage:  %8d KB       PeakPagefileUsage:  %8d KB\n", 
8811                     memory.PagefileUsage/1024, memory.PeakPagefileUsage/1024);
8812         }
8813
8814         MEMORYSTATUS memstat;
8815         GlobalMemoryStatus (&memstat);
8816         ExtOut("---------------------------------------\n");
8817         ExtOut("%ld percent of memory is in use.\n\n",
8818                 memstat.dwMemoryLoad);
8819         ExtOut("Memory Availability (Numbers in MB)\n\n");
8820         ExtOut("                  %8s     %8s\n", "Total", "Avail");
8821         ExtOut("Physical Memory   %8d     %8d\n", memstat.dwTotalPhys/1024/1024, memstat.dwAvailPhys/1024/1024);
8822         ExtOut("Page File         %8d     %8d\n", memstat.dwTotalPageFile/1024/1024, memstat.dwAvailPageFile/1024/1024);
8823         ExtOut("Virtual Memory    %8d     %8d\n", memstat.dwTotalVirtual/1024/1024, memstat.dwAvailVirtual/1024/1024);
8824     }
8825
8826     return Status;
8827 }
8828 #endif // FEATURE_PAL
8829
8830 /**********************************************************************\
8831 * Routine Description:                                                 *
8832 *                                                                      *
8833 *    This function is called to find the address of EE data for a      *  
8834 *    metadata token.                                                   *
8835 *                                                                      *
8836 \**********************************************************************/
8837 DECLARE_API(Token2EE)
8838 {
8839     INIT_API();
8840     MINIDUMP_NOT_SUPPORTED();    
8841
8842     StringHolder DllName;
8843     ULONG64 token = 0;
8844     BOOL dml = FALSE;
8845
8846     CMDOption option[] = 
8847     {   // name, vptr, type, hasValue
8848         {"/d", &dml, COBOOL, FALSE},
8849     };
8850
8851     CMDValue arg[] = 
8852     {   // vptr, type
8853         {&DllName.data, COSTRING},
8854         {&token, COHEX}
8855     };
8856
8857     size_t nArg;
8858     if (!GetCMDOption(args,option, _countof(option), arg, _countof(arg), &nArg)) 
8859     {
8860         return Status;
8861     }
8862     if (nArg!=2)
8863     {
8864         ExtOut("Usage: !Token2EE module_name mdToken\n");
8865         ExtOut("       You can pass * for module_name to search all modules.\n");
8866         return Status;
8867     }
8868
8869     EnableDMLHolder dmlHolder(dml);
8870     int numModule;
8871     ArrayHolder<DWORD_PTR> moduleList = NULL;
8872
8873     if (strcmp(DllName.data, "*") == 0)
8874     {
8875         moduleList = ModuleFromName(NULL, &numModule);
8876     }
8877     else
8878     {
8879         moduleList = ModuleFromName(DllName.data, &numModule);
8880     }
8881     
8882     if (moduleList == NULL)
8883     {
8884         ExtOut("Failed to request module list.\n");
8885     }
8886     else
8887     {
8888         for (int i = 0; i < numModule; i ++)
8889         {
8890             if (IsInterrupt())
8891                 break;
8892
8893             if (i > 0)
8894             {
8895                 ExtOut("--------------------------------------\n");
8896             }        
8897
8898             DWORD_PTR dwAddr = moduleList[i];
8899             WCHAR FileName[MAX_PATH];
8900             FileNameForModule(dwAddr, FileName);
8901
8902             // We'd like a short form for this output
8903             LPWSTR pszFilename = wcsrchr (FileName, L'\\');
8904             if (pszFilename == NULL)
8905             {
8906                 pszFilename = FileName;
8907             }
8908             else
8909             {
8910                 pszFilename++; // skip past the last "\" character
8911             }
8912             
8913             DMLOut("Module:      %s\n", DMLModule(dwAddr));
8914             ExtOut("Assembly:    %S\n", pszFilename);
8915             
8916             GetInfoFromModule(dwAddr, (ULONG)token);
8917         }
8918     }
8919     
8920     return Status;
8921 }
8922
8923
8924 /**********************************************************************\
8925 * Routine Description:                                                 *
8926 *                                                                      *
8927 *    This function is called to find the address of EE data for a      *  
8928 *    metadata token.                                                   *
8929 *                                                                      *
8930 \**********************************************************************/
8931 DECLARE_API(Name2EE)
8932 {
8933     INIT_API();
8934     MINIDUMP_NOT_SUPPORTED();
8935
8936     StringHolder DllName, TypeName; 
8937     BOOL dml = FALSE;
8938
8939     CMDOption option[] = 
8940     {   // name, vptr, type, hasValue
8941         {"/d", &dml, COBOOL, FALSE},
8942     };
8943     
8944     CMDValue arg[] = 
8945     {   // vptr, type
8946         {&DllName.data, COSTRING},
8947         {&TypeName.data, COSTRING}
8948     };
8949     size_t nArg;
8950     
8951     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
8952     {
8953         return Status;
8954     }
8955
8956     EnableDMLHolder dmlHolder(dml);
8957
8958     if (nArg == 1)
8959     {
8960         // The input may be in the form <modulename>!<type>
8961         // If so, do some surgery on the input params.
8962
8963         // There should be only 1 ! character
8964         LPSTR pszSeperator = strchr (DllName.data, '!');
8965         if (pszSeperator != NULL)
8966         {
8967             if (strchr (pszSeperator + 1, '!') == NULL)
8968             {
8969                 size_t capacity_TypeName_data = strlen(pszSeperator + 1) + 1;
8970                 TypeName.data = new NOTHROW char[capacity_TypeName_data];
8971                 if (TypeName.data)
8972                 {
8973                     // get the type name,
8974                     strcpy_s (TypeName.data, capacity_TypeName_data, pszSeperator + 1);
8975                     // and truncate DllName
8976                     *pszSeperator = '\0';
8977
8978                     // Do some extra validation
8979                     if (strlen (DllName.data) >= 1 &&
8980                         strlen (TypeName.data) > 1)
8981                     {
8982                         nArg = 2;
8983                     }
8984                 }
8985             }
8986         }
8987     }
8988     
8989     if (nArg != 2)
8990     {
8991         ExtOut("Usage: !Name2EE module_name item_name\n");
8992         ExtOut("  or    !Name2EE module_name!item_name\n");        
8993         ExtOut("       use * for module_name to search all loaded modules\n");
8994         ExtOut("Examples: !Name2EE  mscorlib.dll System.String.ToString\n");
8995         ExtOut("          !Name2EE *!System.String\n");
8996         return Status;
8997     }
8998     
8999     int numModule;
9000     ArrayHolder<DWORD_PTR> moduleList = NULL;
9001     if (strcmp(DllName.data, "*") == 0)
9002     {
9003         moduleList = ModuleFromName(NULL, &numModule);
9004     }
9005     else
9006     {
9007         moduleList = ModuleFromName(DllName.data, &numModule);
9008     }
9009             
9010
9011     if (moduleList == NULL)
9012     {
9013         ExtOut("Failed to request module list.\n", DllName.data);
9014     }
9015     else
9016     {
9017         for (int i = 0; i < numModule; i ++)
9018         {
9019             if (IsInterrupt())
9020                 break;
9021
9022             if (i > 0)
9023             {
9024                 ExtOut("--------------------------------------\n");
9025             }
9026             
9027             DWORD_PTR dwAddr = moduleList[i];
9028             WCHAR FileName[MAX_PATH];
9029             FileNameForModule (dwAddr, FileName);
9030
9031             // We'd like a short form for this output
9032             LPWSTR pszFilename = wcsrchr (FileName, L'\\');
9033             if (pszFilename == NULL)
9034             {
9035                 pszFilename = FileName;
9036             }
9037             else
9038             {
9039                 pszFilename++; // skip past the last "\" character
9040             }
9041             
9042             DMLOut("Module:      %s\n", DMLModule(dwAddr));
9043             ExtOut("Assembly:    %S\n", pszFilename);
9044             GetInfoFromName(dwAddr, TypeName.data);
9045         }
9046     }
9047  
9048     return Status;
9049 }
9050
9051 #ifndef FEATURE_PAL
9052 DECLARE_API(PathTo)
9053 {
9054     INIT_API();
9055     MINIDUMP_NOT_SUPPORTED();    
9056
9057     DWORD_PTR root = NULL;
9058     DWORD_PTR target = NULL;
9059     BOOL dml = FALSE;
9060     size_t nArg;
9061
9062     CMDOption option[] = 
9063     {   // name, vptr, type, hasValue
9064         {"/d", &dml, COBOOL, FALSE},
9065     };
9066     CMDValue arg[] = 
9067     {   // vptr, type
9068         {&root, COHEX},
9069         {&target, COHEX},
9070     };
9071     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9072     {
9073         return Status;
9074     }    
9075     
9076     if (root == 0 || target == 0)
9077     {
9078         ExtOut("Invalid argument %s\n", args);
9079         return Status;
9080     }
9081     
9082     GCRootImpl gcroot;
9083     bool result = gcroot.PrintPathToObject(root, target);
9084     
9085     if (!result)
9086         ExtOut("Did not find a path from %p to %p.\n", SOS_PTR(root), SOS_PTR(target));
9087     
9088     return Status;
9089 }
9090 #endif
9091
9092
9093
9094 /**********************************************************************\
9095 * Routine Description:                                                 *
9096 *                                                                      *
9097 *    This function finds all roots (on stack or in handles) for a      *  
9098 *    given object.                                                     *
9099 *                                                                      *
9100 \**********************************************************************/
9101 DECLARE_API(GCRoot)
9102 {
9103 #ifndef FEATURE_PAL
9104     INIT_API();
9105     MINIDUMP_NOT_SUPPORTED();    
9106
9107     BOOL bNoStacks = FALSE;
9108     DWORD_PTR obj = NULL;
9109     BOOL dml = FALSE;
9110     BOOL all = FALSE;
9111     size_t nArg;
9112
9113     CMDOption option[] = 
9114     {   // name, vptr, type, hasValue
9115         {"-nostacks", &bNoStacks, COBOOL, FALSE},
9116         {"-all", &all, COBOOL, FALSE},
9117         {"/d", &dml, COBOOL, FALSE},
9118     };
9119     CMDValue arg[] = 
9120
9121     {   // vptr, type
9122         {&obj, COHEX}
9123     };
9124     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9125     {
9126         return Status;
9127     }    
9128     if (obj == 0)
9129     {
9130         ExtOut("Invalid argument %s\n", args);
9131         return Status;
9132     }
9133
9134     EnableDMLHolder dmlHolder(dml);      
9135     GCRootImpl gcroot;
9136     int i = gcroot.PrintRootsForObject(obj, all == TRUE, bNoStacks == TRUE);
9137     
9138     if (IsInterrupt())
9139         ExtOut("Interrupted, data may be incomplete.\n");
9140     
9141     if (all)
9142         ExtOut("Found %d roots.\n", i);
9143     else
9144         ExtOut("Found %d unique roots (run '!GCRoot -all' to see all roots).\n", i);
9145
9146     return Status;
9147 #else
9148     return E_NOTIMPL;
9149 #endif
9150 }
9151
9152 DECLARE_API(GCWhere)
9153 {
9154     INIT_API();
9155     MINIDUMP_NOT_SUPPORTED();
9156
9157     BOOL dml = FALSE;
9158     BOOL bGetBrick;
9159     BOOL bGetCard;
9160     TADDR taddrObj = 0;
9161     size_t nArg;
9162
9163     CMDOption option[] = 
9164     {   // name, vptr, type, hasValue
9165         {"-brick", &bGetBrick, COBOOL, FALSE},
9166         {"-card", &bGetCard, COBOOL, FALSE},
9167         {"/d", &dml, COBOOL, FALSE},
9168     };
9169     CMDValue arg[] = 
9170     {   // vptr, type
9171         {&taddrObj, COHEX}
9172     };
9173     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9174     {
9175         return Status;
9176     }
9177
9178     EnableDMLHolder dmlHolder(dml);
9179     // Obtain allocation context for each managed thread.    
9180     AllocInfo allocInfo;
9181     allocInfo.Init();
9182
9183     TADDR_SEGINFO trngSeg  = { 0, 0, 0 };
9184     TADDR_RANGE   allocCtx = { 0, 0 };
9185     int   gen = -1;
9186     BOOL  bLarge = FALSE;
9187     BOOL  bFound = FALSE;
9188
9189     size_t size = 0;
9190     if (sos::IsObject(taddrObj))
9191     {
9192         TADDR taddrMT; 
9193         BOOL  bContainsPointers;
9194         if(FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
9195            !GetSizeEfficient(taddrObj, taddrMT, FALSE, size, bContainsPointers))
9196         {
9197             ExtWarn("Couldn't get size for object %#p: possible heap corruption.\n", 
9198                 taddrObj);
9199         }
9200     }
9201
9202     if (!IsServerBuild())
9203     {
9204         DacpGcHeapDetails heapDetails;
9205         if (heapDetails.Request(g_sos) != S_OK)
9206         {
9207             ExtOut("Error requesting gc heap details\n");
9208             return Status;
9209         }
9210
9211         if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge))
9212         {
9213             ExtOut("Address   " WIN64_8SPACES " Gen   Heap   segment   " WIN64_8SPACES " begin     " WIN64_8SPACES " allocated  " WIN64_8SPACES " size\n");
9214             ExtOut("%p   %d     %2d     %p   %p   %p    0x%x(%d)\n",
9215                 (ULONG64)taddrObj, gen, 0, (ULONG64)trngSeg.segAddr, (ULONG64)trngSeg.start, (ULONG64)trngSeg.end, size, size);
9216             bFound = TRUE;
9217         }
9218     }
9219     else
9220     {
9221         DacpGcHeapData gcheap;
9222         if (gcheap.Request(g_sos) != S_OK)
9223         {
9224             ExtOut("Error requesting GC Heap data\n");
9225             return Status;
9226         }
9227
9228         DWORD dwAllocSize;
9229         DWORD dwNHeaps = gcheap.HeapCount;
9230         if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
9231         {
9232             ExtOut("Failed to get GCHeaps:  integer overflow\n");
9233             return Status;
9234         }
9235
9236         CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
9237         if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
9238         {
9239             ExtOut("Failed to get GCHeaps\n");
9240             return Status;
9241         }
9242  
9243         for (DWORD n = 0; n < dwNHeaps; n ++)
9244         {
9245             DacpGcHeapDetails heapDetails;
9246             if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
9247             {
9248                 ExtOut("Error requesting details\n");
9249                 return Status;
9250             }
9251
9252             if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge))
9253             {
9254                 ExtOut("Address " WIN64_8SPACES " Gen Heap segment " WIN64_8SPACES " begin   " WIN64_8SPACES " allocated" WIN64_8SPACES " size\n");
9255                 ExtOut("%p   %d     %2d     %p   %p   %p    0x%x(%d)\n",
9256                     (ULONG64)taddrObj, gen, n, (ULONG64)trngSeg.segAddr, (ULONG64)trngSeg.start, (ULONG64)trngSeg.end, size, size);
9257                 bFound = TRUE;
9258                 break;
9259             }
9260         }
9261     }
9262
9263     if (!bFound)
9264     {
9265         ExtOut("Address %#p not found in the managed heap.\n", (ULONG64)taddrObj);
9266     }
9267
9268     return Status;
9269 }
9270
9271 DECLARE_API(FindRoots)
9272 {
9273 #ifndef FEATURE_PAL
9274     INIT_API();
9275     MINIDUMP_NOT_SUPPORTED();
9276
9277     if (IsDumpFile())
9278     {
9279         ExtOut("!FindRoots is not supported on a dump file.\n");
9280         return Status;
9281     }
9282     
9283     LONG_PTR gen = -100; // initialized outside the legal range: [-1, 2]
9284     StringHolder sgen;
9285     TADDR taObj = NULL;
9286     BOOL dml = FALSE;
9287     size_t nArg;
9288
9289     CMDOption option[] = 
9290     {   // name, vptr, type, hasValue
9291         {"-gen", &sgen.data, COSTRING, TRUE},
9292         {"/d", &dml, COBOOL, FALSE},
9293     };
9294     CMDValue arg[] = 
9295     {   // vptr, type
9296         {&taObj, COHEX}
9297     };
9298     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9299     {
9300         return Status;
9301     }
9302
9303     EnableDMLHolder dmlHolder(dml);
9304     if (sgen.data != NULL)
9305     {
9306         if (_stricmp(sgen.data, "any") == 0)
9307         {
9308             gen = -1;
9309         }
9310         else
9311         {
9312             gen = GetExpression(sgen.data);
9313         }
9314     }
9315     if ((gen < -1 || gen > 2) && (taObj == 0))
9316     {
9317         ExtOut("Incorrect options.  Usage:\n\t!FindRoots -gen <N>\n\t\twhere N is 0, 1, 2, or \"any\". OR\n\t!FindRoots <obj>\n");
9318         return Status;
9319     }
9320
9321     if (gen >= -1 && gen <= 2)
9322     {
9323         IXCLRDataProcess2* idp2 = NULL;
9324         if (FAILED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2)))
9325         {
9326             ExtOut("Your version of the runtime/DAC do not support this command.\n");
9327             return Status;
9328         }
9329
9330         // Request GC_MARK_END notifications from debuggee
9331         GcEvtArgs gea = { GC_MARK_END, { ((gen == -1) ? 7 : (1 << gen)) } };
9332         idp2->SetGcNotification(gea);
9333         // ... and register the notification handler
9334         g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0);
9335         // the above notification is removed in CNotification::OnGcEvent()
9336     }
9337     else
9338     {
9339         // verify that the last event in the debugger was indeed a CLRN exception
9340         DEBUG_LAST_EVENT_INFO_EXCEPTION dle;
9341         CNotification Notification;
9342
9343         if (!CheckCLRNotificationEvent(&dle))
9344         {
9345             ExtOut("The command !FindRoots can only be used after the debugger stopped on a CLRN GC notification.\n");
9346             ExtOut("At this time !GCRoot should be used instead.\n");
9347             return Status;
9348         }
9349         // validate argument
9350         if (!g_snapshot.Build())
9351         {
9352             ExtOut("Unable to build snapshot of the garbage collector state\n");
9353             return Status;
9354         }
9355
9356         if (g_snapshot.GetHeap(taObj) == NULL)
9357         {
9358             ExtOut("Address %#p is not in the managed heap.\n", (ULONG64) taObj);
9359             return Status;
9360         }
9361
9362         int ogen = g_snapshot.GetGeneration(taObj);
9363         if (ogen > CNotification::GetCondemnedGen())
9364         {
9365             DMLOut("Object %s will survive this collection:\n\tgen(%#p) = %d > %d = condemned generation.\n",
9366                 DMLObject(taObj), (ULONG64)taObj, ogen, CNotification::GetCondemnedGen());
9367             return Status;
9368         }
9369
9370         GCRootImpl gcroot;
9371         int roots = gcroot.FindRoots(CNotification::GetCondemnedGen(), taObj);
9372         
9373         ExtOut("Found %d roots.\n", roots);
9374     }
9375
9376     return Status;
9377 #else
9378     return E_NOTIMPL;
9379 #endif
9380 }
9381
9382 class GCHandleStatsForDomains
9383 {
9384 public:
9385     const static int SHARED_DOMAIN_INDEX = 0;
9386     const static int SYSTEM_DOMAIN_INDEX = 1;
9387     
9388     GCHandleStatsForDomains() 
9389         : m_singleDomainMode(FALSE), m_numDomains(0), m_pStatistics(NULL), m_pDomainPointers(NULL)
9390     {
9391     }
9392
9393     ~GCHandleStatsForDomains()
9394     {
9395         if (m_pStatistics)
9396         {
9397             if (m_singleDomainMode)
9398                 delete m_pStatistics;
9399             else
9400                 delete [] m_pStatistics;
9401         }
9402         
9403         if (m_pDomainPointers)
9404             delete [] m_pDomainPointers;
9405     }
9406     
9407     BOOL Init(BOOL singleDomainMode)
9408     {
9409         m_singleDomainMode = singleDomainMode; 
9410         if (m_singleDomainMode)
9411         {
9412             m_numDomains = 1;
9413             m_pStatistics = new NOTHROW GCHandleStatistics();
9414             if (m_pStatistics == NULL)
9415                 return FALSE;
9416         }
9417         else
9418         {
9419             DacpAppDomainStoreData adsData;
9420             if (adsData.Request(g_sos) != S_OK)
9421                 return FALSE;
9422
9423             m_numDomains = adsData.DomainCount + 2;
9424             ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount + 2];
9425             if (pArray == NULL)
9426                 return FALSE;
9427
9428             pArray[SHARED_DOMAIN_INDEX] = adsData.sharedDomain;
9429             pArray[SYSTEM_DOMAIN_INDEX] = adsData.systemDomain;
9430             
9431             if (g_sos->GetAppDomainList(adsData.DomainCount, pArray+2, NULL) != S_OK)
9432                 return FALSE;
9433             
9434             m_pDomainPointers = pArray.Detach();
9435             m_pStatistics = new NOTHROW GCHandleStatistics[adsData.DomainCount + 2];
9436             if (m_pStatistics == NULL)
9437                 return FALSE;
9438         }
9439         
9440         return TRUE;
9441     }
9442
9443     GCHandleStatistics *LookupStatistics(CLRDATA_ADDRESS appDomainPtr) const
9444     {
9445         if (m_singleDomainMode)
9446         {
9447             // You can pass NULL appDomainPtr if you are in singleDomainMode
9448             return m_pStatistics;
9449         }
9450         else
9451         {
9452             for (int i=0; i < m_numDomains; i++)
9453                 if (m_pDomainPointers[i] == appDomainPtr)
9454                     return m_pStatistics + i;
9455         }
9456         
9457         return NULL;
9458     }
9459     
9460     
9461     GCHandleStatistics *GetStatistics(int appDomainIndex) const
9462     {
9463         SOS_Assert(appDomainIndex >= 0);
9464         SOS_Assert(appDomainIndex < m_numDomains);
9465         
9466         return m_singleDomainMode ? m_pStatistics : m_pStatistics + appDomainIndex;
9467     }
9468     
9469     int GetNumDomains() const
9470     {
9471         return m_numDomains;
9472     }
9473     
9474     CLRDATA_ADDRESS GetDomain(int index) const
9475     {
9476         SOS_Assert(index >= 0);
9477         SOS_Assert(index < m_numDomains);
9478         return m_pDomainPointers[index];
9479     }
9480     
9481 private:
9482     BOOL m_singleDomainMode;
9483     int m_numDomains;
9484     GCHandleStatistics *m_pStatistics;
9485     CLRDATA_ADDRESS *m_pDomainPointers;
9486 };
9487
9488 class GCHandlesImpl
9489 {
9490 public:
9491     GCHandlesImpl(PCSTR args)
9492         : mPerDomain(FALSE), mStat(FALSE), mDML(FALSE), mType((int)~0)
9493     {
9494         ArrayHolder<char> type = NULL;
9495         CMDOption option[] = 
9496         {
9497             {"-perdomain", &mPerDomain, COBOOL, FALSE},
9498             {"-stat", &mStat, COBOOL, FALSE},
9499             {"-type", &type, COSTRING, TRUE},
9500             {"/d", &mDML, COBOOL, FALSE},
9501         };
9502         
9503         if (!GetCMDOption(args,option,_countof(option),NULL,0,NULL))
9504             sos::Throw<sos::Exception>("Failed to parse command line arguments.");
9505         
9506         if (type != NULL)
9507             if (_stricmp(type, "Pinned") == 0)
9508                 mType = HNDTYPE_PINNED;
9509             else if (_stricmp(type, "RefCounted") == 0)
9510                 mType = HNDTYPE_REFCOUNTED;
9511             else if (_stricmp(type, "WeakShort") == 0)
9512                 mType = HNDTYPE_WEAK_SHORT;
9513             else if (_stricmp(type, "WeakLong") == 0)
9514                 mType = HNDTYPE_WEAK_LONG;
9515             else if (_stricmp(type, "Strong") == 0)
9516                 mType = HNDTYPE_STRONG;
9517             else if (_stricmp(type, "Variable") == 0)
9518                 mType = HNDTYPE_VARIABLE;
9519             else if (_stricmp(type, "AsyncPinned") == 0)
9520                 mType = HNDTYPE_ASYNCPINNED;
9521             else if (_stricmp(type, "SizedRef") == 0)
9522                 mType = HNDTYPE_SIZEDREF;
9523             else if (_stricmp(type, "Dependent") == 0)
9524                 mType = HNDTYPE_DEPENDENT;
9525             else if (_stricmp(type, "WeakWinRT") == 0)
9526                 mType = HNDTYPE_WEAK_WINRT;
9527             else
9528                 sos::Throw<sos::Exception>("Unknown handle type '%s'.", type.GetPtr());
9529     }
9530     
9531     void Run()
9532     {
9533         EnableDMLHolder dmlHolder(mDML);
9534         
9535         mOut.ReInit(6, POINTERSIZE_HEX, AlignRight);
9536         mOut.SetWidths(5, POINTERSIZE_HEX, 11, POINTERSIZE_HEX, 8, POINTERSIZE_HEX);
9537         mOut.SetColAlignment(1, AlignLeft);
9538         
9539         if (mHandleStat.Init(!mPerDomain) == FALSE)
9540             sos::Throw<sos::Exception>("Error getting per-appdomain handle information");
9541         
9542         if (!mStat)
9543             mOut.WriteRow("Handle", "Type", "Object", "Size", "Data", "Type");
9544             
9545         WalkHandles();
9546         
9547         for (int i=0; (i < mHandleStat.GetNumDomains()) && !IsInterrupt(); i++)
9548         {
9549             GCHandleStatistics *pStats = mHandleStat.GetStatistics(i);
9550
9551             if (mPerDomain)
9552             {
9553                 Print( "------------------------------------------------------------------------------\n");           
9554                 Print("GC Handle Statistics for AppDomain ", AppDomainPtr(mHandleStat.GetDomain(i)));
9555             
9556                 if (i == GCHandleStatsForDomains::SHARED_DOMAIN_INDEX)
9557                     Print(" (Shared Domain)\n");
9558                 else if (i == GCHandleStatsForDomains::SYSTEM_DOMAIN_INDEX)
9559                     Print(" (System Domain)\n");
9560                 else
9561                     Print("\n");
9562             }
9563
9564             if (!mStat)
9565                 Print("\n");
9566             PrintGCStat(&pStats->hs);
9567             
9568             // Don't print handle stats if the user has filtered by type.  All handles will be the same
9569             // type, and the total count will be displayed by PrintGCStat.
9570             if (mType == (unsigned int)~0)
9571             {
9572                 Print("\n");
9573                 PrintGCHandleStats(pStats);
9574             }
9575         }
9576     }
9577
9578 private:
9579     void WalkHandles()
9580     {
9581         ToRelease<ISOSHandleEnum> handles;
9582         if (FAILED(g_sos->GetHandleEnum(&handles)))
9583         {
9584             if (IsMiniDumpFile())
9585                 sos::Throw<sos::Exception>("Unable to display GC handles.\nA minidump without full memory may not have this information.");
9586             else
9587                 sos::Throw<sos::Exception>("Failed to walk the handle table.");
9588         }
9589       
9590         // GCC can't handle stacks which are too large.
9591 #ifndef FEATURE_PAL
9592         SOSHandleData data[256];
9593 #else
9594         SOSHandleData data[4];
9595 #endif
9596         
9597         unsigned int fetched = 0;
9598         HRESULT hr = S_OK;
9599         do
9600         {
9601             if (FAILED(hr = handles->Next(_countof(data), data, &fetched)))
9602             {
9603                 ExtOut("Error %x while walking the handle table.\n", hr);
9604                 break;
9605             }
9606             
9607             WalkHandles(data, fetched);
9608         } while (_countof(data) == fetched);
9609     }
9610     
9611     void WalkHandles(SOSHandleData data[], unsigned int count)
9612     {
9613         for (unsigned int i = 0; i < count; ++i)
9614         {
9615             sos::CheckInterrupt();
9616         
9617             if (mType != (unsigned int)~0 && mType != data[i].Type)
9618                 continue;
9619         
9620             GCHandleStatistics *pStats = mHandleStat.LookupStatistics(data[i].AppDomain);
9621             TADDR objAddr = 0;
9622             TADDR mtAddr = 0;
9623             size_t size = 0;
9624             const wchar_t *mtName = 0;
9625             const char *type = 0;
9626             
9627             if (FAILED(MOVE(objAddr, data[i].Handle)))
9628             {
9629                 objAddr = 0;
9630                 mtName = L"<error>";
9631             }
9632             else
9633             {
9634                 sos::Object obj(TO_TADDR(objAddr));
9635                 mtAddr = obj.GetMT();
9636                 if (sos::MethodTable::IsFreeMT(mtAddr))
9637                 {
9638                     mtName = L"<free>";
9639                 }
9640                 else if (!sos::MethodTable::IsValid(mtAddr))
9641                 {
9642                     mtName = L"<error>";
9643                 }
9644                 else
9645                 {
9646                     size = obj.GetSize();
9647                     if (mType == (unsigned int)~0 || mType == data[i].Type)
9648                         pStats->hs.Add(obj.GetMT(), (DWORD)size);
9649                 }
9650             }
9651         
9652             switch(data[i].Type)
9653             {
9654                 case HNDTYPE_PINNED:
9655                     type = "Pinned";
9656                     if (pStats) pStats->pinnedHandleCount++;
9657                     break;
9658                 case HNDTYPE_REFCOUNTED:
9659                     type = "RefCounted";
9660                     if (pStats) pStats->refCntHandleCount++;
9661                     break;    
9662                 case HNDTYPE_STRONG:
9663                     type = "Strong";
9664                     if (pStats) pStats->strongHandleCount++;
9665                     break;
9666                 case HNDTYPE_WEAK_SHORT:
9667                     type = "WeakShort";
9668                     if (pStats) pStats->weakShortHandleCount++;
9669                     break;
9670                 case HNDTYPE_WEAK_LONG:
9671                     type = "WeakLong";
9672                     if (pStats) pStats->weakLongHandleCount++;
9673                     break;
9674                 case HNDTYPE_ASYNCPINNED:
9675                     type = "AsyncPinned";
9676                     if (pStats) pStats->asyncPinnedHandleCount++;
9677                     break;
9678                 case HNDTYPE_VARIABLE:
9679                     type = "Variable";
9680                     if (pStats) pStats->variableCount++;
9681                     break;
9682                 case HNDTYPE_SIZEDREF:
9683                     type = "SizedRef";
9684                     if (pStats) pStats->sizedRefCount++;
9685                     break;
9686                 case HNDTYPE_DEPENDENT:
9687                     type = "Dependent";
9688                     if (pStats) pStats->dependentCount++;
9689                     break;
9690                 case HNDTYPE_WEAK_WINRT:
9691                     type = "WeakWinRT";
9692                     if (pStats) pStats->weakWinRTHandleCount++;
9693                     break;
9694                 default:
9695                     DebugBreak();
9696                     type = "Unknown";
9697                     pStats->unknownHandleCount++;
9698                     break;
9699             }
9700             
9701             if (type && !mStat)
9702             {
9703                 sos::MethodTable mt = mtAddr;
9704                 if (mtName == 0)
9705                     mtName = mt.GetName();
9706                 
9707                 if (data[i].Type == HNDTYPE_REFCOUNTED)
9708                     mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Decimal(data[i].RefCount), mtName);
9709                 else if (data[i].Type == HNDTYPE_DEPENDENT)
9710                     mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), ObjectPtr(data[i].Secondary), mtName);
9711                 else if (data[i].Type == HNDTYPE_WEAK_WINRT)
9712                     mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Pointer(data[i].Secondary), mtName);
9713                 else
9714                     mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), "", mtName);
9715             }
9716         }
9717     }
9718     
9719     inline void PrintHandleRow(const char *text, int count)
9720     {
9721         if (count)
9722             mOut.WriteRow(text, Decimal(count));
9723     }
9724     
9725     void PrintGCHandleStats(GCHandleStatistics *pStats)
9726     {
9727         Print("Handles:\n");
9728         mOut.ReInit(2, 21, AlignLeft, 4);
9729         
9730         PrintHandleRow("Strong Handles:", pStats->strongHandleCount);
9731         PrintHandleRow("Pinned Handles:", pStats->pinnedHandleCount);
9732         PrintHandleRow("Async Pinned Handles:", pStats->asyncPinnedHandleCount);
9733         PrintHandleRow("Ref Count Handles:", pStats->refCntHandleCount);
9734         PrintHandleRow("Weak Long Handles:", pStats->weakLongHandleCount);
9735         PrintHandleRow("Weak Short Handles:", pStats->weakShortHandleCount);
9736         PrintHandleRow("Weak WinRT Handles:", pStats->weakWinRTHandleCount);
9737         PrintHandleRow("Variable Handles:", pStats->variableCount);
9738         PrintHandleRow("SizedRef Handles:", pStats->sizedRefCount);
9739         PrintHandleRow("Dependent Handles:", pStats->dependentCount);
9740         PrintHandleRow("Other Handles:", pStats->unknownHandleCount);
9741     }
9742     
9743 private:
9744     BOOL mPerDomain, mStat, mDML;
9745     unsigned int mType;
9746     TableOutput mOut;
9747     GCHandleStatsForDomains mHandleStat;
9748 };
9749
9750 /**********************************************************************\
9751 * Routine Description:                                                 *
9752 *                                                                      *
9753 *    This function dumps GC Handle statistics        *
9754 *                                                                      *
9755 \**********************************************************************/
9756 DECLARE_API(GCHandles)
9757 {
9758     INIT_API();
9759     MINIDUMP_NOT_SUPPORTED();    
9760     
9761     try
9762     {
9763         GCHandlesImpl gchandles(args);
9764         gchandles.Run();
9765     }
9766     catch(const sos::Exception &e)
9767     {
9768         Print(e.what());
9769     }
9770
9771     return Status;
9772 }
9773
9774 BOOL derivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPWSTR baseString)
9775 {
9776     // We want to follow back until we get the mt for System.Exception
9777     DacpMethodTableData dmtd;
9778     CLRDATA_ADDRESS walkMT = mtObj;
9779     while(walkMT != NULL)
9780     {
9781         if (dmtd.Request(g_sos, walkMT) != S_OK)
9782         {
9783             break;            
9784         }
9785         NameForMT_s (TO_TADDR(walkMT), g_mdName, mdNameLen);                
9786         if (wcscmp (baseString, g_mdName) == 0)
9787         {
9788             return TRUE;
9789         }
9790         walkMT = dmtd.ParentMethodTable;
9791     }
9792     return FALSE;
9793 }
9794
9795 // This is an experimental and undocumented SOS API that attempts to step through code
9796 // stopping once jitted code is reached. It currently has some issues - it can take arbitrarily long
9797 // to reach jitted code and canceling it is terrible. At best it doesn't cancel, at worst it
9798 // kills the debugger. IsInterrupt() doesn't work nearly as nicely as one would hope :/
9799 #ifndef FEATURE_PAL
9800 DECLARE_API(TraceToCode)
9801 {
9802     INIT_API_NOEE();
9803
9804     static ULONG64 g_clrBaseAddr = 0;
9805
9806
9807     while(true)
9808     {
9809         if (IsInterrupt())
9810         {
9811             ExtOut("Interrupted\n");
9812             return S_FALSE;
9813         }
9814
9815         ULONG64 Offset;
9816         g_ExtRegisters->GetInstructionOffset(&Offset);
9817
9818         DWORD codeType = 0;
9819         ULONG64 base = 0;
9820         CLRDATA_ADDRESS cdaStart = TO_CDADDR(Offset);
9821         DacpMethodDescData MethodDescData;
9822         if(g_ExtSymbols->GetModuleByOffset(Offset, 0, NULL, &base) == S_OK)
9823         {
9824             if(g_clrBaseAddr == 0)
9825             {
9826                 g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL,
9827                     &g_clrBaseAddr);
9828             }
9829             if(g_clrBaseAddr == base)
9830             {
9831                 ExtOut("Compiled code in CLR\n");
9832                 codeType = 4;
9833             }
9834             else
9835             {
9836                 ExtOut("Compiled code in module @ 0x%I64x\n", base);
9837                 codeType = 8;
9838             }
9839         }
9840         else if (g_sos != NULL || LoadClrDebugDll()==S_OK)
9841         {
9842             CLRDATA_ADDRESS addr;
9843             if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK)
9844             {
9845                 WCHAR wszNameBuffer[1024]; // should be large enough
9846
9847                 // get the MethodDesc name
9848                 if ((g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK) &&
9849                     wcsncmp(L"DomainBoundILStubClass", wszNameBuffer, 22)==0)
9850                 {
9851                     ExtOut("ILStub\n");
9852                     codeType = 2;
9853                 }
9854                 else
9855                 {
9856                     ExtOut("Jitted code\n");
9857                     codeType = 1;
9858                 }
9859             }
9860             else
9861             {
9862                 ExtOut("Not compiled or jitted, assuming stub\n");
9863                 codeType = 16;
9864             }
9865         }
9866         else
9867         {
9868             // not compiled but CLR isn't loaded... some other code generator?
9869             return E_FAIL;
9870         }
9871
9872         if(codeType == 1)
9873         {
9874             return S_OK;
9875         }
9876         else
9877         {
9878             Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "thr; .echo wait" ,0);
9879             if (FAILED(Status))
9880             {
9881                 ExtOut("Error tracing instruction\n");
9882                 return Status;
9883             }
9884         }
9885     }
9886
9887     return Status;
9888
9889 }
9890 #endif
9891
9892 // This is an experimental and undocumented API that sets a debugger pseudo-register based
9893 // on the type of code at the given IP. It can be used in scripts to keep stepping until certain
9894 // kinds of code have been reached. Presumbably its slower than !TraceToCode but at least it
9895 // cancels much better
9896 #ifndef FEATURE_PAL
9897 DECLARE_API(GetCodeTypeFlags)
9898 {
9899     INIT_API();   
9900     
9901
9902     char buffer[100+mdNameLen];
9903     size_t ip;
9904     StringHolder PReg;
9905     
9906     CMDValue arg[] = {
9907         // vptr, type
9908         {&ip, COSIZE_T},
9909         {&PReg.data, COSTRING}
9910     };
9911     size_t nArg;
9912     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) 
9913     {
9914         return Status;
9915     }
9916
9917     size_t preg = 1; // by default
9918     if (nArg == 2)
9919     {
9920         preg = GetExpression(PReg.data);
9921         if (preg > 19)
9922         {
9923             ExtOut("Pseudo-register number must be between 0 and 19\n");
9924             return Status;
9925         }
9926     }        
9927
9928     sprintf_s(buffer,_countof (buffer),
9929         "r$t%d=0",
9930         preg);
9931     Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
9932     if (FAILED(Status))
9933     {
9934         ExtOut("Error initialized register $t%d to zero\n", preg);
9935         return Status;
9936     }    
9937
9938     ULONG64 base = 0;
9939     CLRDATA_ADDRESS cdaStart = TO_CDADDR(ip);
9940     DWORD codeType = 0;
9941     CLRDATA_ADDRESS addr;
9942     if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK)
9943     {
9944         WCHAR wszNameBuffer[1024]; // should be large enough
9945
9946         // get the MethodDesc name
9947         if (g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK &&
9948             wcsncmp(L"DomainBoundILStubClass", wszNameBuffer, 22)==0)
9949         {
9950             ExtOut("ILStub\n");
9951             codeType = 2;
9952         }
9953         else
9954         {
9955             ExtOut("Jitted code");
9956             codeType = 1;
9957         }
9958     }
9959     else if(g_ExtSymbols->GetModuleByOffset (ip, 0, NULL, &base) == S_OK)
9960     {
9961         ULONG64 clrBaseAddr = 0;
9962         if(SUCCEEDED(g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL, &clrBaseAddr)) && base==clrBaseAddr)
9963         {
9964             ExtOut("Compiled code in CLR");
9965             codeType = 4;
9966         }
9967         else
9968         {
9969             ExtOut("Compiled code in module @ 0x%I64x\n", base);
9970             codeType = 8;
9971         }
9972     }
9973     else
9974     {
9975         ExtOut("Not compiled or jitted, assuming stub\n");
9976         codeType = 16;
9977     }
9978
9979     sprintf_s(buffer,_countof (buffer),
9980         "r$t%d=%x",
9981         preg, codeType);
9982     Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
9983     if (FAILED(Status))
9984     {
9985         ExtOut("Error setting register $t%d\n", preg);
9986         return Status;
9987     }  
9988     return Status;
9989
9990 }
9991 #endif
9992
9993 DECLARE_API(StopOnException)
9994 {
9995     INIT_API();
9996     MINIDUMP_NOT_SUPPORTED();    
9997     
9998
9999     char buffer[100+mdNameLen];
10000
10001     BOOL fDerived = FALSE;
10002     BOOL fCreate1 = FALSE;    
10003     BOOL fCreate2 = FALSE;    
10004
10005     CMDOption option[] = {
10006         // name, vptr, type, hasValue
10007         {"-derived", &fDerived, COBOOL, FALSE}, // catch derived exceptions
10008         {"-create", &fCreate1, COBOOL, FALSE}, // create 1st chance handler
10009         {"-create2", &fCreate2, COBOOL, FALSE}, // create 2nd chance handler
10010     };
10011
10012     StringHolder TypeName,PReg;
10013     
10014     CMDValue arg[] = {
10015         // vptr, type
10016         {&TypeName.data, COSTRING},
10017         {&PReg.data, COSTRING}
10018     };
10019     size_t nArg;
10020     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
10021     {
10022         return Status;
10023     }
10024     if (IsDumpFile())
10025     {
10026         ExtOut("Live debugging session required\n");
10027         return Status;
10028     }
10029     if (nArg < 1 || nArg > 2)
10030     {
10031         ExtOut("usage: StopOnException [-derived] [-create | -create2] <type name>\n");
10032         ExtOut("                       [<pseudo-register number for result>]\n");            
10033         ExtOut("ex:    StopOnException -create System.OutOfMemoryException 1\n");
10034         return Status;
10035     }
10036
10037     size_t preg = 1; // by default
10038     if (nArg == 2)
10039     {
10040         preg = GetExpression(PReg.data);
10041         if (preg > 19)
10042         {
10043             ExtOut("Pseudo-register number must be between 0 and 19\n");
10044             return Status;
10045         }
10046     }        
10047
10048     sprintf_s(buffer,_countof (buffer),
10049         "r$t%d=0",
10050         preg);
10051     Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
10052     if (FAILED(Status))
10053     {
10054         ExtOut("Error initialized register $t%d to zero\n", preg);
10055         return Status;
10056     }    
10057     
10058     if (fCreate1 || fCreate2)
10059     {            
10060         sprintf_s(buffer,_countof (buffer),
10061             "sxe %s \"!soe %s %s %d;.if(@$t%d==0) {g} .else {.echo '%s hit'}\" %x",
10062             fCreate1 ? "-c" : "-c2",
10063             fDerived ? "-derived" : "",
10064             TypeName.data,
10065             preg,
10066             preg,
10067             TypeName.data,
10068             EXCEPTION_COMPLUS
10069             );
10070             
10071         Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);        
10072         if (FAILED(Status))
10073         {
10074             ExtOut("Error setting breakpoint: %s\n", buffer);
10075             return Status;
10076         }        
10077
10078         ExtOut("Breakpoint set\n");
10079         return Status;
10080     }    
10081
10082     // Find the last thrown exception on this thread.
10083     // Does it match? If so, set the register.
10084     CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
10085     DacpThreadData Thread;
10086     
10087     if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
10088     {
10089         ExtOut("The current thread is unmanaged\n");
10090         return Status;
10091     }
10092
10093     TADDR taLTOH;
10094     if (!SafeReadMemory(Thread.lastThrownObjectHandle,
10095                         &taLTOH,
10096                         sizeof(taLTOH), NULL))
10097     {
10098         ExtOut("There is no current managed exception on this thread\n");
10099         return Status;
10100     }
10101     
10102     if (taLTOH)
10103     {
10104         LPWSTR typeNameWide = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
10105         MultiByteToWideChar(CP_ACP,0,TypeName.data,-1,typeNameWide,mdNameLen);
10106         
10107         TADDR taMT;
10108         if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL))
10109         {            
10110             NameForMT_s (taMT, g_mdName, mdNameLen);
10111             if ((wcscmp(g_mdName,typeNameWide) == 0) ||
10112                 (fDerived && derivedFrom(taMT, typeNameWide)))
10113             {
10114                 sprintf_s(buffer,_countof (buffer),
10115                     "r$t%d=1",
10116                     preg);
10117                 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
10118                 if (FAILED(Status))
10119                 {
10120                     ExtOut("Failed to execute the following command: %s\n", buffer);
10121                 }
10122             }
10123         }
10124     }
10125
10126     return Status;
10127 }
10128
10129 /**********************************************************************\
10130 * Routine Description:                                                 *
10131 *                                                                      *
10132 *    This function finds the size of an object or all roots.           *  
10133 *                                                                      *
10134 \**********************************************************************/
10135 DECLARE_API(ObjSize)
10136 {
10137 #ifndef FEATURE_PAL
10138     INIT_API();
10139     MINIDUMP_NOT_SUPPORTED();    
10140     
10141     BOOL dml = FALSE;
10142     StringHolder str_Object;    
10143
10144
10145     CMDOption option[] = 
10146     {   // name, vptr, type, hasValue
10147         {"/d", &dml, COBOOL, FALSE},
10148     };
10149     CMDValue arg[] = 
10150     {   // vptr, type
10151         {&str_Object.data, COSTRING}
10152     };
10153     size_t nArg;
10154     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
10155     {
10156         return Status;
10157     }
10158
10159     EnableDMLHolder dmlHolder(dml);
10160     TADDR obj = GetExpression(str_Object.data);
10161
10162     GCRootImpl gcroot;
10163     if (obj == 0)
10164     {
10165         gcroot.ObjSize();
10166     }
10167     else
10168     {
10169         if(!sos::IsObject(obj))
10170         {
10171             ExtOut("%p is not a valid object.\n", SOS_PTR(obj));
10172             return Status;
10173         }
10174
10175         size_t size = gcroot.ObjSize(obj);
10176         TADDR mt = 0;
10177         MOVE(mt, obj);
10178         sos::MethodTable methodTable = mt;
10179         ExtOut("sizeof(%p) = %d (0x%x) bytes (%S)\n", SOS_PTR(obj), size, size, methodTable.GetName());
10180     }
10181     return Status;
10182 #else
10183     return E_NOTIMPL;
10184 #endif
10185
10186 }
10187
10188 #ifndef FEATURE_PAL
10189 // For FEATURE_PAL, MEMORY_BASIC_INFORMATION64 doesn't exist yet. TODO?
10190 DECLARE_API(GCHandleLeaks)
10191 {
10192     INIT_API();
10193     MINIDUMP_NOT_SUPPORTED();    
10194
10195     ExtOut("-------------------------------------------------------------------------------\n");
10196     ExtOut("GCHandleLeaks will report any GCHandles that couldn't be found in memory.      \n");
10197     ExtOut("Strong and Pinned GCHandles are reported at this time. You can safely abort the\n");
10198     ExtOut("memory scan with Control-C or Control-Break.                                   \n");
10199     ExtOut("-------------------------------------------------------------------------------\n");
10200     
10201     static DWORD_PTR array[2000];
10202     UINT i;
10203     BOOL dml = FALSE;
10204
10205     CMDOption option[] = 
10206     {   // name, vptr, type, hasValue
10207         {"/d", &dml, COBOOL, FALSE},
10208     };
10209
10210     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
10211     {
10212         return Status;
10213     }
10214
10215     EnableDMLHolder dmlHolder(dml);
10216     
10217     UINT iFinal = FindAllPinnedAndStrong(array,sizeof(array)/sizeof(DWORD_PTR));
10218     ExtOut("Found %d handles:\n",iFinal);
10219     for (i=1;i<=iFinal;i++)
10220     {
10221         ExtOut("%p\t",(ULONG64)array[i-1]);
10222         if ((i % 4) == 0)
10223             ExtOut("\n");
10224     }
10225
10226     ExtOut("\nSearching memory\n");
10227     // Now search memory for this:
10228     DWORD_PTR buffer[1024];
10229     ULONG64 memCur = 0x0;
10230     BOOL bAbort = FALSE;
10231
10232     //find out memory used by stress log
10233     StressLogMem stressLog;
10234     CLRDATA_ADDRESS StressLogAddress = NULL;
10235     if (LoadClrDebugDll() != S_OK)
10236     {
10237         // Try to find stress log symbols
10238         DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog");
10239         StressLogAddress = dwAddr;        
10240         g_bDacBroken = TRUE;
10241     }
10242     else
10243     {
10244         if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK)
10245         {
10246             ExtOut("Unable to find stress log via DAC\n");
10247         }
10248         g_bDacBroken = FALSE;
10249     }
10250
10251     if (stressLog.Init (StressLogAddress, g_ExtData))
10252     {
10253         ExtOut("Reference found in stress log will be ignored\n");
10254     }
10255     else
10256     {
10257         ExtOut("Failed to read whole or part of stress log, some references may come from stress log\n");
10258     }
10259     
10260     
10261     while (!bAbort)
10262     {
10263         NTSTATUS status;
10264         MEMORY_BASIC_INFORMATION64 memInfo;
10265
10266         status = g_ExtData2->QueryVirtual(UL64_TO_CDA(memCur), &memInfo);
10267                 
10268         if( !NT_SUCCESS(status) ) 
10269         {            
10270             break;
10271         }
10272
10273         if (memInfo.State == MEM_COMMIT)
10274         {            
10275             for (ULONG64 memIter = memCur; memIter < (memCur + memInfo.RegionSize); memIter+=sizeof(buffer))
10276             {
10277                 if (IsInterrupt())
10278                 {
10279                     ExtOut("Quitting at %p due to user abort\n",(ULONG64)memIter);
10280                     bAbort = TRUE;
10281                     break;
10282                 }
10283
10284                 if ((memIter % 0x10000000)==0x0)
10285                 {
10286                     ExtOut("Searching %p...\n",(ULONG64)memIter);
10287                 }
10288                 
10289                 ULONG size = 0;
10290                 HRESULT ret;
10291                 ret = g_ExtData->ReadVirtual(UL64_TO_CDA(memIter), buffer, sizeof(buffer), &size);
10292                 if (ret == S_OK)
10293                 {
10294                     for (UINT x=0;x<1024;x++)
10295                     {            
10296                         DWORD_PTR value = buffer[x];
10297                         // We don't care about the low bit. Also, the GCHandle class turns on the
10298                         // low bit for pinned handles, so without the statement below, we wouldn't
10299                         // notice pinned handles.
10300                         value = value & ~1; 
10301                         for (i=0;i<iFinal;i++)
10302                         {
10303                             ULONG64 addrInDebugee = (ULONG64)memIter+(x*sizeof(DWORD_PTR));
10304                             if ((array[i] & ~1) == value)
10305                             {
10306                                 if (stressLog.IsInStressLog (addrInDebugee))
10307                                 {
10308                                     ExtOut("Found %p in stress log at location %p, reference not counted\n", (ULONG64)value, addrInDebugee);
10309                                 }
10310                                 else
10311                                 {
10312                                     ExtOut("Found %p at location %p\n",(ULONG64)value, addrInDebugee);
10313                                     array[i] |= 0x1;
10314                                 }
10315                             }
10316                         }
10317                     }
10318                 }
10319                 else
10320                 {
10321                     if (size > 0)
10322                     {
10323                         ExtOut("only read %x bytes at %p\n",size,(ULONG64)memIter);
10324                     }
10325                 }
10326             }
10327         }
10328         
10329         memCur += memInfo.RegionSize;
10330     }
10331
10332     int numNotFound = 0;
10333     for (i=0;i<iFinal;i++)
10334     {
10335         if ((array[i] & 0x1) == 0)
10336         {
10337             numNotFound++;
10338             // ExtOut("WARNING: %p not found\n",(ULONG64)array[i]);
10339         }
10340     }
10341
10342     if (numNotFound > 0)
10343     {
10344         ExtOut("------------------------------------------------------------------------------\n");    
10345         ExtOut("Some handles were not found. If the number of not-found handles grows over the\n");
10346         ExtOut("lifetime of your application, you may have a GCHandle leak. This will cause   \n");
10347         ExtOut("the GC Heap to grow larger as objects are being kept alive, referenced only   \n");
10348         ExtOut("by the orphaned handle. If the number doesn't grow over time, note that there \n");
10349         ExtOut("may be some noise in this output, as an unmanaged application may be storing  \n");
10350         ExtOut("the handle in a non-standard way, perhaps with some bits flipped. The memory  \n");
10351         ExtOut("scan wouldn't be able to find those.                                          \n");
10352         ExtOut("------------------------------------------------------------------------------\n");    
10353
10354         ExtOut("Didn't find %d handles:\n", numNotFound);
10355         int numPrinted=0;
10356         for (i=0;i<iFinal;i++)
10357         {
10358             if ((array[i] & 0x1) == 0)
10359             {             
10360                 numPrinted++;
10361                 ExtOut("%p\t",(ULONG64)array[i]);
10362                 if ((numPrinted % 4) == 0)
10363                     ExtOut("\n");            
10364             }
10365         }   
10366         ExtOut("\n");
10367     }
10368     else
10369     {       
10370         ExtOut("------------------------------------------------------------------------------\n");    
10371         ExtOut("All handles found");
10372         if (bAbort)
10373             ExtOut(" even though you aborted.\n");
10374         else
10375             ExtOut(".\n");        
10376         ExtOut("A leak may still exist because in a general scan of process memory SOS can't  \n");
10377         ExtOut("differentiate between garbage and valid structures, so you may have false     \n");
10378         ExtOut("positives. If you still suspect a leak, use this function over time to        \n");
10379         ExtOut("identify a possible trend.                                                    \n");
10380         ExtOut("------------------------------------------------------------------------------\n");    
10381     }
10382     
10383     return Status;
10384 }
10385 #endif // FEATURE_PAL
10386
10387 #ifdef FEATURE_PAL
10388
10389 class ClrStackImplWithICorDebug
10390 {
10391 public:
10392     static HRESULT ClrStackFromPublicInterface(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, WCHAR* varToExpand = NULL, int onlyShowFrame = -1)
10393     {
10394         ExtOut("Not supported on this platform.\n");
10395         return S_OK;
10396     }
10397 };
10398
10399 #else // FEATURE_PAL NOT defined below
10400
10401 class ClrStackImplWithICorDebug
10402 {
10403 private:
10404
10405     static HRESULT DereferenceAndUnboxValue(ICorDebugValue * pValue, ICorDebugValue** ppOutputValue, BOOL * pIsNull = NULL)
10406     {
10407         HRESULT Status = S_OK;
10408         *ppOutputValue = NULL;
10409         if(pIsNull != NULL) *pIsNull = FALSE;
10410
10411         ToRelease<ICorDebugReferenceValue> pReferenceValue;
10412         Status = pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue);
10413         if (SUCCEEDED(Status))
10414         {
10415             BOOL isNull = FALSE;
10416             IfFailRet(pReferenceValue->IsNull(&isNull));
10417             if(!isNull)
10418             {
10419                 ToRelease<ICorDebugValue> pDereferencedValue;
10420                 IfFailRet(pReferenceValue->Dereference(&pDereferencedValue));
10421                 return DereferenceAndUnboxValue(pDereferencedValue, ppOutputValue);
10422             }
10423             else
10424             {
10425                 if(pIsNull != NULL) *pIsNull = TRUE;
10426                 *ppOutputValue = pValue;
10427                 (*ppOutputValue)->AddRef();
10428                 return S_OK;
10429             }
10430         }
10431
10432         ToRelease<ICorDebugBoxValue> pBoxedValue;
10433         Status = pValue->QueryInterface(IID_ICorDebugBoxValue, (LPVOID*) &pBoxedValue);
10434         if (SUCCEEDED(Status))
10435         {
10436             ToRelease<ICorDebugObjectValue> pUnboxedValue;
10437             IfFailRet(pBoxedValue->GetObject(&pUnboxedValue));
10438             return DereferenceAndUnboxValue(pUnboxedValue, ppOutputValue);
10439         }
10440         *ppOutputValue = pValue;
10441         (*ppOutputValue)->AddRef();
10442         return S_OK;
10443     }
10444     static BOOL ShouldExpandVariable(__in_z WCHAR* varToExpand, __in_z WCHAR* currentExpansion)
10445     {
10446         if(currentExpansion == NULL || varToExpand == NULL) return FALSE;
10447
10448         size_t varToExpandLen = wcslen(varToExpand);
10449         size_t currentExpansionLen = wcslen(currentExpansion);
10450         if(currentExpansionLen > varToExpandLen) return FALSE;
10451         if(currentExpansionLen < varToExpandLen && varToExpand[currentExpansionLen] != L'.') return FALSE;
10452         if(wcsncmp(currentExpansion, varToExpand, currentExpansionLen) != 0) return FALSE;
10453
10454         return TRUE;
10455     }
10456     static BOOL IsEnum(ICorDebugValue * pInputValue)
10457     {
10458         ToRelease<ICorDebugValue> pValue;
10459         if(FAILED(DereferenceAndUnboxValue(pInputValue, &pValue, NULL))) return FALSE;
10460
10461         WCHAR baseTypeName[mdNameLen];
10462         ToRelease<ICorDebugValue2> pValue2;
10463         ToRelease<ICorDebugType> pType;
10464         ToRelease<ICorDebugType> pBaseType;
10465
10466         if(FAILED(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2))) return FALSE;
10467         if(FAILED(pValue2->GetExactType(&pType))) return FALSE;
10468         if(FAILED(pType->GetBase(&pBaseType)) || pBaseType == NULL) return FALSE;
10469         if(FAILED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen))) return  FALSE;
10470
10471         return (wcsncmp(baseTypeName, L"System.Enum", 11) == 0);
10472     }
10473     static HRESULT AddGenericArgs(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
10474     {
10475         bool isFirst = true;
10476         ToRelease<ICorDebugTypeEnum> pTypeEnum;
10477         if(SUCCEEDED(pType->EnumerateTypeParameters(&pTypeEnum)))
10478         {
10479             ULONG numTypes = 0;
10480             ToRelease<ICorDebugType> pCurrentTypeParam;
10481             
10482             while(SUCCEEDED(pTypeEnum->Next(1, &pCurrentTypeParam, &numTypes)))
10483             {
10484                 if(numTypes == 0) break;
10485
10486                 if(isFirst)
10487                 {
10488                     isFirst = false;
10489                     wcsncat_s(typeName, typeNameLen, L"&lt;", typeNameLen);
10490                 }
10491                 else wcsncat_s(typeName, typeNameLen, L",", typeNameLen);
10492
10493                 WCHAR typeParamName[mdNameLen];
10494                 typeParamName[0] = L'\0';
10495                 GetTypeOfValue(pCurrentTypeParam, typeParamName, mdNameLen);
10496                 wcsncat_s(typeName, typeNameLen, typeParamName, typeNameLen);
10497             }
10498             if(!isFirst)
10499                 wcsncat_s(typeName, typeNameLen, L"&gt;", typeNameLen);
10500         }
10501
10502         return S_OK;
10503     }
10504     static HRESULT GetTypeOfValue(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
10505     {
10506         HRESULT Status = S_OK;
10507
10508         CorElementType corElemType;
10509         IfFailRet(pType->GetType(&corElemType));
10510
10511         switch (corElemType)
10512         {
10513         //List of unsupported CorElementTypes:
10514         //ELEMENT_TYPE_END            = 0x0,
10515         //ELEMENT_TYPE_VAR            = 0x13,     // a class type variable VAR <U1>
10516         //ELEMENT_TYPE_GENERICINST    = 0x15,     // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
10517         //ELEMENT_TYPE_TYPEDBYREF     = 0x16,     // TYPEDREF  (it takes no args) a typed referece to some other type
10518         //ELEMENT_TYPE_MVAR           = 0x1e,     // a method type variable MVAR <U1>
10519         //ELEMENT_TYPE_CMOD_REQD      = 0x1F,     // required C modifier : E_T_CMOD_REQD <mdTypeRef/mdTypeDef>
10520         //ELEMENT_TYPE_CMOD_OPT       = 0x20,     // optional C modifier : E_T_CMOD_OPT <mdTypeRef/mdTypeDef>
10521         //ELEMENT_TYPE_INTERNAL       = 0x21,     // INTERNAL <typehandle>
10522         //ELEMENT_TYPE_MAX            = 0x22,     // first invalid element type
10523         //ELEMENT_TYPE_MODIFIER       = 0x40,
10524         //ELEMENT_TYPE_SENTINEL       = 0x01 | ELEMENT_TYPE_MODIFIER, // sentinel for varargs
10525         //ELEMENT_TYPE_PINNED         = 0x05 | ELEMENT_TYPE_MODIFIER,
10526         //ELEMENT_TYPE_R4_HFA         = 0x06 | ELEMENT_TYPE_MODIFIER, // used only internally for R4 HFA types
10527         //ELEMENT_TYPE_R8_HFA         = 0x07 | ELEMENT_TYPE_MODIFIER, // used only internally for R8 HFA types
10528         default:
10529             swprintf_s(typeName, typeNameLen, L"(Unhandled CorElementType: 0x%x)\0", corElemType);
10530             break;
10531
10532         case ELEMENT_TYPE_VALUETYPE:
10533         case ELEMENT_TYPE_CLASS:
10534             {
10535                 //Defaults in case we fail...
10536                 if(corElemType == ELEMENT_TYPE_VALUETYPE) swprintf_s(typeName, typeNameLen, L"struct\0");
10537                 else swprintf_s(typeName, typeNameLen, L"class\0");
10538
10539                 mdTypeDef typeDef;
10540                 ToRelease<ICorDebugClass> pClass;
10541                 if(SUCCEEDED(pType->GetClass(&pClass)) && SUCCEEDED(pClass->GetToken(&typeDef)))
10542                 {
10543                     ToRelease<ICorDebugModule> pModule;
10544                     IfFailRet(pClass->GetModule(&pModule));
10545
10546                     ToRelease<IUnknown> pMDUnknown;
10547                     ToRelease<IMetaDataImport> pMD;
10548                     IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
10549                     IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
10550
10551                     if(SUCCEEDED(NameForToken_s(TokenFromRid(typeDef, mdtTypeDef), pMD, g_mdName, mdNameLen, false)))
10552                         swprintf_s(typeName, typeNameLen, L"%s\0", g_mdName);
10553                 }
10554                 AddGenericArgs(pType, typeName, typeNameLen);
10555             }
10556             break;
10557         case ELEMENT_TYPE_VOID:
10558             swprintf_s(typeName, typeNameLen, L"void\0");
10559             break;
10560         case ELEMENT_TYPE_BOOLEAN:
10561             swprintf_s(typeName, typeNameLen, L"bool\0");
10562             break;
10563         case ELEMENT_TYPE_CHAR:
10564             swprintf_s(typeName, typeNameLen, L"char\0");
10565             break;
10566         case ELEMENT_TYPE_I1:
10567             swprintf_s(typeName, typeNameLen, L"signed byte\0");
10568             break;
10569         case ELEMENT_TYPE_U1:
10570             swprintf_s(typeName, typeNameLen, L"byte\0");
10571             break;
10572         case ELEMENT_TYPE_I2:
10573             swprintf_s(typeName, typeNameLen, L"short\0");
10574             break;
10575         case ELEMENT_TYPE_U2:
10576             swprintf_s(typeName, typeNameLen, L"unsigned short\0");
10577             break;    
10578         case ELEMENT_TYPE_I4:
10579             swprintf_s(typeName, typeNameLen, L"int\0");
10580             break;
10581         case ELEMENT_TYPE_U4:
10582             swprintf_s(typeName, typeNameLen, L"unsigned int\0");
10583             break;
10584         case ELEMENT_TYPE_I8:
10585             swprintf_s(typeName, typeNameLen, L"long\0");
10586             break;
10587         case ELEMENT_TYPE_U8:
10588             swprintf_s(typeName, typeNameLen, L"unsigned long\0");
10589             break;
10590         case ELEMENT_TYPE_R4:
10591             swprintf_s(typeName, typeNameLen, L"float\0");
10592             break;
10593         case ELEMENT_TYPE_R8:
10594             swprintf_s(typeName, typeNameLen, L"double\0");
10595             break;
10596         case ELEMENT_TYPE_OBJECT:
10597             swprintf_s(typeName, typeNameLen, L"object\0");
10598             break;
10599         case ELEMENT_TYPE_STRING:
10600             swprintf_s(typeName, typeNameLen, L"string\0");
10601             break;
10602         case ELEMENT_TYPE_I:
10603             swprintf_s(typeName, typeNameLen, L"IntPtr\0");
10604             break;
10605         case ELEMENT_TYPE_U:
10606             swprintf_s(typeName, typeNameLen, L"UIntPtr\0");
10607             break;
10608         case ELEMENT_TYPE_SZARRAY:
10609         case ELEMENT_TYPE_ARRAY:
10610         case ELEMENT_TYPE_BYREF:
10611         case ELEMENT_TYPE_PTR:
10612             {
10613                 ToRelease<ICorDebugType> pFirstParameter;
10614                 if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)))
10615                     GetTypeOfValue(pFirstParameter, typeName, typeNameLen);
10616                 else
10617                     swprintf_s(typeName, typeNameLen, L"<unknown>\0");
10618
10619                 switch(corElemType)
10620                 {
10621                 case ELEMENT_TYPE_SZARRAY: 
10622                     wcsncat_s(typeName, typeNameLen, L"[]\0", typeNameLen);
10623                     return S_OK;
10624                 case ELEMENT_TYPE_ARRAY:
10625                     {
10626                         ULONG32 rank = 0;
10627                         pType->GetRank(&rank);
10628                         wcsncat_s(typeName, typeNameLen, L"[", typeNameLen);
10629                         for(ULONG32 i = 0; i < rank - 1; i++)
10630                         {
10631                             // 
10632                             wcsncat_s(typeName, typeNameLen, L",", typeNameLen);
10633                         }
10634                         wcsncat_s(typeName, typeNameLen, L"]\0", typeNameLen);
10635                     }
10636                     return S_OK;
10637                 case ELEMENT_TYPE_BYREF:   
10638                     wcsncat_s(typeName, typeNameLen, L"&\0", typeNameLen);
10639                     return S_OK;
10640                 case ELEMENT_TYPE_PTR:     
10641                     wcsncat_s(typeName, typeNameLen, L"*\0", typeNameLen);
10642                     return S_OK;
10643                 }
10644             }
10645             break;
10646         case ELEMENT_TYPE_FNPTR:
10647             swprintf_s(typeName, typeNameLen, L"*(...)\0");
10648             break;
10649         case ELEMENT_TYPE_TYPEDBYREF:
10650             swprintf_s(typeName, typeNameLen, L"typedbyref\0");
10651             break;
10652         }
10653         return S_OK;
10654     }
10655     static HRESULT GetTypeOfValue(ICorDebugValue * pValue, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
10656     {
10657         HRESULT Status = S_OK;
10658
10659         CorElementType corElemType;
10660         IfFailRet(pValue->GetType(&corElemType));
10661
10662         ToRelease<ICorDebugType> pType;
10663         ToRelease<ICorDebugValue2> pValue2;
10664         if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugValue2, (void**) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
10665             return GetTypeOfValue(pType, typeName, typeNameLen);
10666         else
10667             swprintf_s(typeName, typeNameLen, L"<unknown>\0");
10668
10669         return S_OK;
10670     }
10671     static HRESULT PrintEnumValue(ICorDebugValue* pInputValue, BYTE* enumValue)
10672     {
10673         HRESULT Status = S_OK;
10674
10675         ToRelease<ICorDebugValue> pValue;
10676         IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, NULL));
10677
10678         mdTypeDef currentTypeDef;
10679         ToRelease<ICorDebugClass> pClass;
10680         ToRelease<ICorDebugValue2> pValue2;
10681         ToRelease<ICorDebugType> pType;
10682         ToRelease<ICorDebugModule> pModule;
10683         IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
10684         IfFailRet(pValue2->GetExactType(&pType));
10685         IfFailRet(pType->GetClass(&pClass));
10686         IfFailRet(pClass->GetModule(&pModule));
10687         IfFailRet(pClass->GetToken(&currentTypeDef));
10688
10689         ToRelease<IUnknown> pMDUnknown;
10690         ToRelease<IMetaDataImport> pMD;
10691         IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
10692         IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
10693
10694
10695         //First, we need to figure out the underlying enum type so that we can correctly type cast the raw values of each enum constant
10696         //We get that from the non-static field of the enum variable (I think the field is called __value or something similar)
10697         ULONG numFields = 0;
10698         HCORENUM fEnum = NULL;
10699         mdFieldDef fieldDef;
10700         CorElementType enumUnderlyingType = ELEMENT_TYPE_END;
10701         while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
10702         {
10703             DWORD             fieldAttr = 0;
10704             PCCOR_SIGNATURE   pSignatureBlob = NULL;
10705             ULONG             sigBlobLength = 0;
10706             if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, NULL, 0, NULL, &fieldAttr, &pSignatureBlob, &sigBlobLength, NULL, NULL, NULL)))
10707             {
10708                 if((fieldAttr & fdStatic) == 0)
10709                 {
10710                     CorSigUncompressCallingConv(pSignatureBlob);
10711                     enumUnderlyingType = CorSigUncompressElementType(pSignatureBlob);
10712                     break;
10713                 }
10714             }
10715         }
10716         pMD->CloseEnum(fEnum);
10717
10718
10719         //Now that we know the underlying enum type, let's decode the enum variable into OR-ed, human readable enum contants
10720         fEnum = NULL;
10721         bool isFirst = true;
10722         ULONG64 remainingValue = *((ULONG64*)enumValue);
10723         while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
10724         {
10725             ULONG             nameLen = 0;
10726             DWORD             fieldAttr = 0;
10727             WCHAR             mdName[mdNameLen];
10728             WCHAR             typeName[mdNameLen];
10729             UVCP_CONSTANT     pRawValue = NULL;
10730             ULONG             rawValueLength = 0;
10731             if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, &pRawValue, &rawValueLength)))
10732             {
10733                 DWORD enumValueRequiredAttributes = fdPublic | fdStatic | fdLiteral | fdHasDefault;
10734                 if((fieldAttr & enumValueRequiredAttributes) != enumValueRequiredAttributes)
10735                     continue;
10736
10737                 ULONG64 currentConstValue = 0;
10738                 switch (enumUnderlyingType)
10739                 {
10740                     case ELEMENT_TYPE_CHAR:
10741                     case ELEMENT_TYPE_I1:
10742                         currentConstValue = (ULONG64)(*((CHAR*)pRawValue));
10743                         break;
10744                     case ELEMENT_TYPE_U1:
10745                         currentConstValue = (ULONG64)(*((BYTE*)pRawValue));
10746                         break;
10747                     case ELEMENT_TYPE_I2:
10748                         currentConstValue = (ULONG64)(*((SHORT*)pRawValue));
10749                         break;
10750                     case ELEMENT_TYPE_U2:
10751                         currentConstValue = (ULONG64)(*((USHORT*)pRawValue));
10752                         break;
10753                     case ELEMENT_TYPE_I4:
10754                         currentConstValue = (ULONG64)(*((INT32*)pRawValue));
10755                         break;
10756                     case ELEMENT_TYPE_U4:
10757                         currentConstValue = (ULONG64)(*((UINT32*)pRawValue));
10758                         break;
10759                     case ELEMENT_TYPE_I8:
10760                         currentConstValue = (ULONG64)(*((LONG*)pRawValue));
10761                         break;
10762                     case ELEMENT_TYPE_U8:
10763                         currentConstValue = (ULONG64)(*((ULONG*)pRawValue));
10764                         break;
10765                     case ELEMENT_TYPE_I:
10766                         currentConstValue = (ULONG64)(*((int*)pRawValue));
10767                         break;
10768                     case ELEMENT_TYPE_U:
10769                     case ELEMENT_TYPE_R4:
10770                     case ELEMENT_TYPE_R8:
10771                     // Technically U and the floating-point ones are options in the CLI, but not in the CLS or C#, so these are NYI
10772                     default:
10773                         currentConstValue = 0;
10774                 }
10775
10776                 if((currentConstValue == remainingValue) || ((currentConstValue != 0) && ((currentConstValue & remainingValue) == currentConstValue)))
10777                 {
10778                     remainingValue &= ~currentConstValue;
10779                     if(isFirst)
10780                     {
10781                         ExtOut(" = %S", mdName);
10782                         isFirst = false;
10783                     }
10784                     else ExtOut(" | %S", mdName);
10785                 }
10786             }
10787         }
10788         pMD->CloseEnum(fEnum);
10789
10790         return S_OK;
10791     }
10792     static HRESULT PrintStringValue(ICorDebugValue * pValue)
10793     {
10794         HRESULT Status;
10795
10796         ToRelease<ICorDebugStringValue> pStringValue;
10797         IfFailRet(pValue->QueryInterface(IID_ICorDebugStringValue, (LPVOID*) &pStringValue));
10798
10799         ULONG32 cchValue;
10800         IfFailRet(pStringValue->GetLength(&cchValue));
10801         cchValue++;         // Allocate one more for null terminator
10802
10803         CQuickString quickString;
10804         quickString.Alloc(cchValue);
10805
10806         ULONG32 cchValueReturned;
10807         IfFailRet(pStringValue->GetString(
10808             cchValue,
10809             &cchValueReturned,
10810             quickString.String()));
10811
10812         ExtOut(" = \"%S\"\n", quickString.String());
10813         
10814         return S_OK;
10815     }
10816     static HRESULT PrintSzArrayValue(ICorDebugValue * pValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
10817     {
10818         HRESULT Status = S_OK;
10819
10820         ToRelease<ICorDebugArrayValue> pArrayValue;
10821         IfFailRet(pValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID*) &pArrayValue));
10822
10823         ULONG32 nRank;
10824         IfFailRet(pArrayValue->GetRank(&nRank));
10825         if (nRank != 1)
10826         {
10827             return E_UNEXPECTED;
10828         }
10829
10830         ULONG32 cElements;
10831         IfFailRet(pArrayValue->GetCount(&cElements));
10832
10833         if (cElements == 0) ExtOut("   (empty)\n");
10834         else if (cElements == 1) ExtOut("   (1 element)\n");
10835         else ExtOut("   (%d elements)\n", cElements);
10836
10837         if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK;
10838         size_t currentExpansionLen = wcslen(currentExpansion);
10839
10840         for (ULONG32 i=0; i < cElements; i++)
10841         {
10842             for(int j = 0; j <= indent; j++) ExtOut("    ");
10843             currentExpansion[currentExpansionLen] = L'\0';
10844             swprintf_s(currentExpansion, mdNameLen, L"%s.[%d]\0", currentExpansion, i);
10845
10846             bool printed = false;
10847             CorElementType corElemType;
10848             ToRelease<ICorDebugType> pFirstParameter;
10849             ToRelease<ICorDebugValue2> pValue2;
10850             ToRelease<ICorDebugType> pType;
10851             if(SUCCEEDED(pArrayValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
10852             {
10853                 if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)) && SUCCEEDED(pFirstParameter->GetType(&corElemType)))
10854                 {
10855                     switch(corElemType)
10856                     {
10857                     //If the array element is something that we can expand with !clrstack, show information about the type of this element
10858                     case ELEMENT_TYPE_VALUETYPE:
10859                     case ELEMENT_TYPE_CLASS:
10860                     case ELEMENT_TYPE_SZARRAY:
10861                         {
10862                             WCHAR typeOfElement[mdNameLen];
10863                             GetTypeOfValue(pFirstParameter, typeOfElement, mdNameLen);
10864                             DMLOut(" |- %s = %S", DMLManagedVar(currentExpansion, currentFrame, i), typeOfElement);
10865                             printed = true;
10866                         }
10867                         break;
10868                     }
10869                 }
10870             }
10871             if(!printed) DMLOut(" |- %s", DMLManagedVar(currentExpansion, currentFrame, i));
10872
10873             ToRelease<ICorDebugValue> pElementValue;
10874             IfFailRet(pArrayValue->GetElementAtPosition(i, &pElementValue));
10875             IfFailRet(PrintValue(pElementValue, pILFrame, pMD, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame));
10876         }
10877
10878         return S_OK;
10879     }
10880     static HRESULT PrintValue(ICorDebugValue * pInputValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
10881     {
10882         HRESULT Status = S_OK;
10883
10884         BOOL isNull = TRUE;
10885         ToRelease<ICorDebugValue> pValue;
10886         IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull));
10887
10888         if(isNull)
10889         {
10890             ExtOut(" = null\n");
10891             return S_OK;
10892         }
10893
10894         ULONG32 cbSize;
10895         IfFailRet(pValue->GetSize(&cbSize));
10896         ArrayHolder<BYTE> rgbValue = new NOTHROW BYTE[cbSize];
10897         if (rgbValue == NULL)
10898         {
10899             ReportOOM();
10900             return E_OUTOFMEMORY;
10901         }
10902
10903         memset(rgbValue.GetPtr(), 0, cbSize * sizeof(BYTE));
10904
10905         CorElementType corElemType;
10906         IfFailRet(pValue->GetType(&corElemType));
10907         if (corElemType == ELEMENT_TYPE_STRING)
10908         {
10909             return PrintStringValue(pValue);
10910         }
10911
10912         if (corElemType == ELEMENT_TYPE_SZARRAY)
10913         {
10914             return PrintSzArrayValue(pValue, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
10915         }
10916
10917         ToRelease<ICorDebugGenericValue> pGenericValue;
10918         IfFailRet(pValue->QueryInterface(IID_ICorDebugGenericValue, (LPVOID*) &pGenericValue));
10919         IfFailRet(pGenericValue->GetValue((LPVOID) &(rgbValue[0])));
10920
10921         if(IsEnum(pValue))
10922         {
10923             Status = PrintEnumValue(pValue, rgbValue);
10924             ExtOut("\n");
10925             return Status;
10926         }
10927
10928         switch (corElemType)
10929         {
10930         default:
10931             ExtOut("  (Unhandled CorElementType: 0x%x)\n", corElemType);
10932             break;
10933
10934         case ELEMENT_TYPE_PTR:
10935             ExtOut("  = <pointer>\n");
10936             break;
10937
10938         case ELEMENT_TYPE_FNPTR:
10939             {
10940                 CORDB_ADDRESS addr = 0;
10941                 ToRelease<ICorDebugReferenceValue> pReferenceValue = NULL;
10942                 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue)))
10943                     pReferenceValue->GetValue(&addr);
10944                 ExtOut("  = <function pointer 0x%x>\n", addr);
10945             }
10946             break;
10947
10948         case ELEMENT_TYPE_VALUETYPE:
10949         case ELEMENT_TYPE_CLASS:
10950             CORDB_ADDRESS addr;
10951             if(SUCCEEDED(pValue->GetAddress(&addr)))
10952             {
10953                 ExtOut(" @ 0x%I64x\n", addr);
10954             }
10955             else
10956             {
10957                 ExtOut("\n");
10958             }
10959             ProcessFields(pValue, NULL, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
10960             break;
10961
10962         case ELEMENT_TYPE_BOOLEAN:
10963             ExtOut("  = %s\n", rgbValue[0] == 0 ? "false" : "true");
10964             break;
10965
10966         case ELEMENT_TYPE_CHAR:
10967             ExtOut("  = '%C'\n", *(WCHAR *) &(rgbValue[0]));
10968             break;
10969
10970         case ELEMENT_TYPE_I1:
10971             ExtOut("  = %d\n", *(char*) &(rgbValue[0]));
10972             break;
10973
10974         case ELEMENT_TYPE_U1:
10975             ExtOut("  = %d\n", *(unsigned char*) &(rgbValue[0]));
10976             break;
10977
10978         case ELEMENT_TYPE_I2:
10979             ExtOut("  = %hd\n", *(short*) &(rgbValue[0]));
10980             break;
10981
10982         case ELEMENT_TYPE_U2:
10983             ExtOut("  = %hu\n", *(unsigned short*) &(rgbValue[0]));
10984             break;
10985         
10986         case ELEMENT_TYPE_I:
10987             ExtOut("  = %d\n", *(int*) &(rgbValue[0]));
10988             break;
10989
10990         case ELEMENT_TYPE_U:
10991             ExtOut("  = %u\n", *(unsigned int*) &(rgbValue[0]));
10992             break;
10993
10994         case ELEMENT_TYPE_I4:
10995             ExtOut("  = %d\n", *(int*) &(rgbValue[0]));
10996             break;
10997
10998         case ELEMENT_TYPE_U4:
10999             ExtOut("  = %u\n", *(unsigned int*) &(rgbValue[0]));
11000             break;
11001
11002         case ELEMENT_TYPE_I8:
11003             ExtOut("  = %I64d\n", *(__int64*) &(rgbValue[0]));
11004             break;
11005
11006         case ELEMENT_TYPE_U8:
11007             ExtOut("  = %I64u\n", *(unsigned __int64*) &(rgbValue[0]));
11008             break;
11009
11010         case ELEMENT_TYPE_R4:
11011             ExtOut("  = %f\n", (double) *(float*) &(rgbValue[0]));
11012             break;
11013
11014         case ELEMENT_TYPE_R8:
11015             ExtOut("  = %f\n", *(double*) &(rgbValue[0]));
11016             break;
11017
11018         case ELEMENT_TYPE_OBJECT:
11019             ExtOut("  = object\n");
11020             break;
11021
11022             // TODO: The following corElementTypes are not yet implemented here.  Array
11023             // might be interesting to add, though the others may be of rather limited use:
11024             // ELEMENT_TYPE_ARRAY          = 0x14,     // MDARRAY <type> <rank> <bcount> <bound1> ... <lbcount> <lb1> ...
11025             // 
11026             // ELEMENT_TYPE_GENERICINST    = 0x15,     // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
11027         }
11028
11029         return S_OK;
11030     }
11031     static HRESULT PrintParameters(BOOL bParams, BOOL bLocals, IMetaDataImport * pMD, mdTypeDef typeDef, mdMethodDef methodDef, ICorDebugILFrame * pILFrame, ICorDebugModule * pModule, __in_z WCHAR* varToExpand, int currentFrame)
11032     {
11033         HRESULT Status = S_OK;
11034
11035             ULONG cParams = 0;
11036         ToRelease<ICorDebugValueEnum> pParamEnum;
11037         IfFailRet(pILFrame->EnumerateArguments(&pParamEnum));
11038         IfFailRet(pParamEnum->GetCount(&cParams));
11039         if (cParams > 0 && bParams)
11040         {
11041             DWORD methAttr = 0;
11042             IfFailRet(pMD->GetMethodProps(methodDef, NULL, NULL, 0, NULL, &methAttr, NULL, NULL, NULL, NULL));
11043
11044             ExtOut("\nPARAMETERS:\n");
11045             for (ULONG i=0; i < cParams; i++)
11046             {
11047                 ULONG paramNameLen = 0;
11048                 mdParamDef paramDef;
11049                 WCHAR paramName[mdNameLen] = L"\0";
11050
11051                 if(i == 0 && (methAttr & mdStatic) == 0)
11052                     swprintf_s(paramName, mdNameLen, L"this\0");
11053                 else 
11054                 {
11055                     int idx = ((methAttr & mdStatic) == 0)? i : (i + 1);
11056                     if(SUCCEEDED(pMD->GetParamForMethodIndex(methodDef, idx, &paramDef)))
11057                         pMD->GetParamProps(paramDef, NULL, NULL, paramName, mdNameLen, &paramNameLen, NULL, NULL, NULL, NULL);
11058                 }
11059                 if(wcslen(paramName) == 0)
11060                     swprintf_s(paramName, mdNameLen, L"param_%d\0", i);
11061
11062                 ToRelease<ICorDebugValue> pValue;
11063                 ULONG cArgsFetched;
11064                 Status = pParamEnum->Next(1, &pValue, &cArgsFetched);
11065
11066                 if (FAILED(Status))
11067                 {
11068                     ExtOut("  + (Error 0x%x retrieving parameter '%S')\n", Status, paramName);
11069                     continue;
11070                 }
11071
11072                 if (Status == S_FALSE)
11073                 {
11074                     break;
11075                 }
11076
11077                 WCHAR typeName[mdNameLen] = L"\0";
11078                 GetTypeOfValue(pValue, typeName, mdNameLen);
11079                 DMLOut("  + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName));
11080
11081                 ToRelease<ICorDebugReferenceValue> pRefValue;
11082                 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL)
11083                 {
11084                     BOOL bIsNull = TRUE;
11085                     pRefValue->IsNull(&bIsNull);
11086                     if(bIsNull)
11087                     {
11088                         ExtOut(" = null\n");
11089                         continue;
11090                     }
11091                 }
11092
11093                 WCHAR currentExpansion[mdNameLen];
11094                 swprintf_s(currentExpansion, mdNameLen, L"%s\0", paramName);
11095                 if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK)
11096                     ExtOut("  + (Error 0x%x printing parameter %d)\n", Status, i);
11097             }
11098         }
11099             else if (cParams == 0 && bParams)
11100             ExtOut("\nPARAMETERS: (none)\n");
11101         
11102         ULONG cLocals = 0;
11103         ToRelease<ICorDebugValueEnum> pLocalsEnum;
11104         IfFailRet(pILFrame->EnumerateLocalVariables(&pLocalsEnum));
11105         IfFailRet(pLocalsEnum->GetCount(&cLocals));
11106         if (cLocals > 0 && bLocals)
11107         {
11108             bool symbolsAvailable = false;
11109             SymbolReader symReader;
11110             if(SUCCEEDED(symReader.LoadSymbols(pMD, pModule)))
11111                 symbolsAvailable = true;
11112
11113             ExtOut("\nLOCALS:\n");
11114             for (ULONG i=0; i < cLocals; i++)
11115             {
11116                 ULONG paramNameLen = 0;
11117                 WCHAR paramName[mdNameLen] = L"\0";
11118
11119                 ToRelease<ICorDebugValue> pValue;
11120                 if(symbolsAvailable)
11121                 {
11122                     Status = symReader.GetNamedLocalVariable(pILFrame, i, paramName, mdNameLen, &pValue);
11123                 }
11124                 else
11125                 {
11126                     ULONG cArgsFetched;
11127                     Status = pLocalsEnum->Next(1, &pValue, &cArgsFetched);
11128                 }
11129                 if(wcslen(paramName) == 0)
11130                     swprintf_s(paramName, mdNameLen, L"local_%d\0", i);
11131
11132                 if (FAILED(Status))
11133                 {
11134                     ExtOut("  + (Error 0x%x retrieving local variable '%S')\n", Status, paramName);
11135                     continue;
11136                 }
11137
11138                 if (Status == S_FALSE)
11139                 {
11140                     break;
11141                 }
11142
11143                 WCHAR typeName[mdNameLen] = L"\0";
11144                 GetTypeOfValue(pValue, typeName, mdNameLen);
11145                 DMLOut("  + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName));
11146
11147                 ToRelease<ICorDebugReferenceValue> pRefValue = NULL;
11148                 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL)
11149                 {
11150                     BOOL bIsNull = TRUE;
11151                     pRefValue->IsNull(&bIsNull);
11152                     if(bIsNull)
11153                     {
11154                         ExtOut(" = null\n");
11155                         continue;
11156                     }
11157                 }
11158
11159                 WCHAR currentExpansion[mdNameLen];
11160                 swprintf_s(currentExpansion, mdNameLen, L"%s\0", paramName);
11161                 if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK)
11162                     ExtOut("  + (Error 0x%x printing local variable %d)\n", Status, i);
11163             }
11164         }
11165             else if (cLocals == 0 && bLocals)
11166             ExtOut("\nLOCALS: (none)\n");
11167
11168             if(bParams || bLocals)
11169                     ExtOut("\n");
11170
11171         return S_OK;
11172     }
11173
11174     static HRESULT ProcessFields(ICorDebugValue* pInputValue, ICorDebugType* pTypeCast, ICorDebugILFrame * pILFrame, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
11175     {
11176         if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK;
11177         size_t currentExpansionLen = wcslen(currentExpansion);
11178
11179         HRESULT Status = S_OK;
11180
11181         BOOL isNull = FALSE;
11182         ToRelease<ICorDebugValue> pValue;
11183         IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull));
11184
11185         if(isNull) return S_OK;
11186
11187         mdTypeDef currentTypeDef;
11188         ToRelease<ICorDebugClass> pClass;
11189         ToRelease<ICorDebugValue2> pValue2;
11190         ToRelease<ICorDebugType> pType;
11191         ToRelease<ICorDebugModule> pModule;
11192         IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
11193         if(pTypeCast == NULL)
11194             IfFailRet(pValue2->GetExactType(&pType));
11195         else
11196         {
11197             pType = pTypeCast;
11198             pType->AddRef();
11199         }
11200         IfFailRet(pType->GetClass(&pClass));
11201         IfFailRet(pClass->GetModule(&pModule));
11202         IfFailRet(pClass->GetToken(&currentTypeDef));
11203
11204         ToRelease<IUnknown> pMDUnknown;
11205         ToRelease<IMetaDataImport> pMD;
11206         IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
11207         IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
11208
11209         WCHAR baseTypeName[mdNameLen] = L"\0";
11210         ToRelease<ICorDebugType> pBaseType;
11211         if(SUCCEEDED(pType->GetBase(&pBaseType)) && pBaseType != NULL && SUCCEEDED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen)))
11212         {
11213             if(wcsncmp(baseTypeName, L"System.Enum", 11) == 0)
11214                 return S_OK;
11215             else if(wcsncmp(baseTypeName, L"System.Object", 13) != 0 && wcsncmp(baseTypeName, L"System.ValueType", 16) != 0)
11216             {
11217                 currentExpansion[currentExpansionLen] = L'\0';
11218                 wcscat_s(currentExpansion, currentExpansionSize, L".\0");
11219                 wcscat_s(currentExpansion, currentExpansionSize, L"[basetype]");
11220                 for(int i = 0; i < indent; i++) ExtOut("    ");
11221                 DMLOut(" |- %S %s\n", baseTypeName, DMLManagedVar(currentExpansion, currentFrame, L"[basetype]"));
11222
11223                 if(ShouldExpandVariable(varToExpand, currentExpansion))
11224                     ProcessFields(pInputValue, pBaseType, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
11225             }
11226         }
11227
11228
11229         ULONG numFields = 0;
11230         HCORENUM fEnum = NULL;
11231         mdFieldDef fieldDef;
11232         while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
11233         {
11234             ULONG             nameLen = 0;
11235             DWORD             fieldAttr = 0;
11236             WCHAR             mdName[mdNameLen];
11237             WCHAR             typeName[mdNameLen];
11238             if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, NULL, NULL)))
11239             {
11240                 currentExpansion[currentExpansionLen] = L'\0';
11241                 wcscat_s(currentExpansion, currentExpansionSize, L".\0");
11242                 wcscat_s(currentExpansion, currentExpansionSize, mdName);
11243
11244                 ToRelease<ICorDebugValue> pFieldVal;
11245                 if(fieldAttr & fdLiteral)
11246                 {
11247                     //TODO: Is it worth it??
11248                     //ExtOut(" |- const %S", mdName);
11249                 }
11250                 else
11251                 {
11252                     for(int i = 0; i < indent; i++) ExtOut("    ");
11253
11254                     if (fieldAttr & fdStatic)
11255                         pType->GetStaticFieldValue(fieldDef, pILFrame, &pFieldVal);
11256                     else
11257                     {
11258                         ToRelease<ICorDebugObjectValue> pObjValue;
11259                         if (SUCCEEDED(pValue->QueryInterface(IID_ICorDebugObjectValue, (LPVOID*) &pObjValue)))
11260                             pObjValue->GetFieldValue(pClass, fieldDef, &pFieldVal);
11261                     }
11262
11263                     if(pFieldVal != NULL)
11264                     {
11265                         typeName[0] = L'\0';
11266                         GetTypeOfValue(pFieldVal, typeName, mdNameLen);
11267                         DMLOut(" |- %S %s", typeName, DMLManagedVar(currentExpansion, currentFrame, mdName));
11268                         PrintValue(pFieldVal, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
11269                     }
11270                     else if(!(fieldAttr & fdLiteral)) 
11271                         ExtOut(" |- < unknown type > %S\n", mdName);
11272                 }
11273             }
11274         }
11275         pMD->CloseEnum(fEnum);
11276         return S_OK;
11277     }
11278
11279     
11280
11281    
11282
11283
11284 public:
11285
11286     // This is the main worker function used if !clrstack is called with "-i" to indicate
11287     // that the public ICorDebug* should be used instead of the private DAC interface. NOTE:
11288     // Currently only bParams is supported. NOTE: This is a work in progress and the
11289     // following would be good to do:
11290     //     * More thorough testing with interesting stacks, especially with transitions into
11291     //         and out of managed code.
11292     //     * Consider interleaving this code back into the main body of !clrstack if it turns
11293     //         out that there's a lot of duplication of code between these two functions.
11294     //         (Still unclear how things will look once locals is implemented.)
11295     static HRESULT ClrStackFromPublicInterface(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, __in_z WCHAR* varToExpand = NULL, int onlyShowFrame = -1)
11296     {
11297         HRESULT Status;
11298
11299         IfFailRet(InitCorDebugInterface());
11300
11301         ExtOut("\n\n\nDumping managed stack and managed variables using ICorDebug.\n");
11302         ExtOut("=============================================================================\n");
11303
11304         ToRelease<ICorDebugThread> pThread;
11305         ToRelease<ICorDebugThread3> pThread3;
11306         ToRelease<ICorDebugStackWalk> pStackWalk;
11307         ULONG ulThreadID = 0;
11308         g_ExtSystem->GetCurrentThreadSystemId(&ulThreadID);
11309
11310         IfFailRet(g_pCorDebugProcess->GetThread(ulThreadID, &pThread));
11311         IfFailRet(pThread->QueryInterface(IID_ICorDebugThread3, (LPVOID *) &pThread3));
11312         IfFailRet(pThread3->CreateStackWalk(&pStackWalk));
11313
11314         InternalFrameManager internalFrameManager;
11315         IfFailRet(internalFrameManager.Init(pThread3));
11316         
11317     #if defined(_AMD64_)
11318         ExtOut("%-16s %-16s %s\n", "Child SP", "IP", "Call Site");
11319     #elif defined(_X86_)
11320         ExtOut("%-8s %-8s %s\n", "Child SP", "IP", "Call Site");
11321     #endif
11322
11323         int currentFrame = -1;
11324
11325         for (Status = S_OK; ; Status = pStackWalk->Next())
11326         {
11327             currentFrame++;
11328
11329             if (Status == CORDBG_S_AT_END_OF_STACK)
11330             {
11331                 ExtOut("Stack walk complete.\n");
11332                 break;
11333             }
11334             IfFailRet(Status);
11335
11336             if (IsInterrupt())
11337             {
11338                 ExtOut("<interrupted>\n");
11339                 break;
11340             }
11341             
11342             CROSS_PLATFORM_CONTEXT context;
11343             ULONG32 cbContextActual;
11344             if ((Status=pStackWalk->GetContext(
11345                 DT_CONTEXT_FULL, 
11346                 sizeof(context),
11347                 &cbContextActual,
11348                 (BYTE *)&context))!=S_OK)
11349             {
11350                 ExtOut("GetFrameContext failed: %lx\n",Status);
11351                 break;
11352             }
11353
11354             // First find the info for the Frame object, if the current frame has an associated clr!Frame.
11355             CLRDATA_ADDRESS sp = GetSP(context);
11356             CLRDATA_ADDRESS ip = GetIP(context);
11357
11358             ToRelease<ICorDebugFrame> pFrame;
11359             IfFailRet(pStackWalk->GetFrame(&pFrame));
11360             if (Status == S_FALSE)
11361             {
11362                 DMLOut("%p %s [NativeStackFrame]\n", SOS_PTR(sp), DMLIP(ip));
11363                 continue;
11364             }
11365
11366             // TODO: What about internal frames preceding the above native stack frame? 
11367             // Should I just exclude the above native stack frame from the output?
11368             // TODO: Compare caller frame (instead of current frame) against internal frame,
11369             // to deal with issues of current frame's current SP being closer to leaf than
11370             // EE Frames it pushes.  By "caller" I mean not just managed caller, but the
11371             // very next non-internal frame dbi would return (native or managed). OR...
11372             // perhaps I should use GetStackRange() instead, to see if the internal frame
11373             // appears leafier than the base-part of the range of the currently iterated
11374             // stack frame?  I think I like that better.
11375             _ASSERTE(pFrame != NULL);
11376             IfFailRet(internalFrameManager.PrintPrecedingInternalFrames(pFrame));
11377
11378             // Print the stack and instruction pointers.
11379             DMLOut("%p %s ", SOS_PTR(sp), DMLIP(ip));
11380
11381             ToRelease<ICorDebugRuntimeUnwindableFrame> pRuntimeUnwindableFrame;
11382             Status = pFrame->QueryInterface(IID_ICorDebugRuntimeUnwindableFrame, (LPVOID *) &pRuntimeUnwindableFrame);
11383             if (SUCCEEDED(Status))
11384             {
11385                 ExtOut("[RuntimeUnwindableFrame]\n");
11386                 continue;
11387             }
11388
11389             // Print the method/Frame info
11390
11391             // TODO: IS THE FOLLOWING NECESSARY, OR AM I GUARANTEED THAT ALL INTERNAL FRAMES
11392             // CAN BE FOUND VIA GetActiveInternalFrames?
11393             ToRelease<ICorDebugInternalFrame> pInternalFrame;
11394             Status = pFrame->QueryInterface(IID_ICorDebugInternalFrame, (LPVOID *) &pInternalFrame);
11395             if (SUCCEEDED(Status))
11396             {
11397                 // This is a clr!Frame.
11398                 LPCWSTR pwszFrameName = L"TODO: Implement GetFrameName";
11399                 ExtOut("[%S: p] ", pwszFrameName);
11400             }
11401
11402             // Print the frame's associated function info, if it has any.
11403             ToRelease<ICorDebugILFrame> pILFrame;
11404             HRESULT hrILFrame = pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame);
11405
11406             if (SUCCEEDED(hrILFrame))
11407             {
11408                 ToRelease<ICorDebugFunction> pFunction;
11409                 Status = pFrame->GetFunction(&pFunction);
11410                 if (FAILED(Status))
11411                 {
11412                     // We're on a JITted frame, but there's no Function for it.  So it must
11413                     // be... 
11414                     ExtOut("[IL Stub or LCG]\n");
11415                     continue;
11416                 }
11417
11418                 ToRelease<ICorDebugClass> pClass;
11419                 ToRelease<ICorDebugModule> pModule;
11420                 mdMethodDef methodDef;
11421                 IfFailRet(pFunction->GetClass(&pClass));
11422                 IfFailRet(pFunction->GetModule(&pModule));
11423                 IfFailRet(pFunction->GetToken(&methodDef));
11424
11425                 WCHAR wszModuleName[100];
11426                 ULONG32 cchModuleNameActual;
11427                 IfFailRet(pModule->GetName(_countof(wszModuleName), &cchModuleNameActual, wszModuleName));
11428
11429                 ToRelease<IUnknown> pMDUnknown;
11430                 ToRelease<IMetaDataImport> pMD;
11431                 ToRelease<IMDInternalImport> pMDInternal;
11432                 IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
11433                 IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
11434                 IfFailRet(GetMDInternalFromImport(pMD, &pMDInternal));
11435
11436                 mdTypeDef typeDef;
11437                 IfFailRet(pClass->GetToken(&typeDef));
11438
11439                 // Note that we don't need to pretty print the class, as class name is
11440                 // already printed from GetMethodName below
11441
11442                 CQuickBytes functionName;
11443                 // TODO: WARNING: GetMethodName() appears to include lots of unexercised
11444                 // code, as evidenced by some fundamental bugs I found.  It should either be
11445                 // thoroughly reviewed, or some other more exercised code path to grab the
11446                 // name should be used.
11447                 // TODO: If we do stay with GetMethodName, it should be updated to print
11448                 // generics properly.  Today, it does not show generic type parameters, and
11449                 // if any arguments have a generic type, those arguments are just shown as
11450                 // "__Canon", even when they're value types.
11451                 GetMethodName(methodDef, pMD, &functionName);
11452
11453                 DMLOut(DMLManagedVar(L"-a", currentFrame, (LPWSTR)functionName.Ptr()));
11454                             ExtOut(" (%S)\n", wszModuleName);
11455
11456                 if (SUCCEEDED(hrILFrame) && (bParams || bLocals))
11457                 {
11458                     if(onlyShowFrame == -1 || (onlyShowFrame >= 0 && currentFrame == onlyShowFrame))
11459                         IfFailRet(PrintParameters(bParams, bLocals, pMD, typeDef, methodDef, pILFrame, pModule, varToExpand, currentFrame));
11460                 }
11461             }
11462         }
11463         ExtOut("=============================================================================\n");
11464
11465         return S_OK;
11466     }
11467 };
11468
11469
11470 #endif // FEATURE_PAL
11471
11472 WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj)
11473 {
11474     WString res;
11475     
11476     if (ref.HasRegisterInformation)
11477     {
11478         wchar_t reg[32];
11479         HRESULT hr = g_sos->GetRegisterName(ref.Register, _countof(reg), reg, NULL);
11480         if (SUCCEEDED(hr))
11481             res = reg;
11482         else
11483             res = L"<unknown register>";
11484             
11485         if (ref.Offset)
11486         {
11487             int offset = ref.Offset;
11488             if (offset > 0)
11489             {
11490                 res += L"+";
11491             }
11492             else
11493             {
11494                 res += L"-";
11495                 offset = -offset;
11496             }
11497             
11498             res += Hex(offset);
11499         }
11500         
11501         res += L": ";
11502     }
11503     
11504     if (ref.Address)
11505         res += WString(Pointer(ref.Address));
11506         
11507     if (printObj)
11508     {
11509         if (ref.Address)
11510             res += L" -> ";
11511
11512         res += WString(ObjectPtr(ref.Object));
11513     }
11514
11515     if (ref.Flags & SOSRefPinned)
11516     {
11517         res += L" (pinned)";
11518     }
11519     
11520     if (ref.Flags & SOSRefInterior)
11521     {
11522         res += L" (interior)";
11523     }
11524     
11525     return res;
11526 }
11527
11528 void PrintRef(const SOSStackRefData &ref, TableOutput &out)
11529 {
11530     WString res = BuildRegisterOutput(ref);
11531     
11532     if (ref.Object && (ref.Flags & SOSRefInterior) == 0)
11533     {
11534         wchar_t type[128];
11535         sos::BuildTypeWithExtraInfo(TO_TADDR(ref.Object), _countof(type), type);
11536         
11537         res += WString(L" - ") + type;
11538     }
11539     
11540     out.WriteColumn(2, res);
11541 }
11542
11543 class ClrStackImpl
11544 {
11545 public:
11546     static void PrintThread(ULONG osID, BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC)
11547     {
11548         // Symbols variables
11549         ULONG symlines = 0; // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
11550         if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
11551         {
11552             symlines &= SYMOPT_LOAD_LINES;
11553         }
11554         
11555         if (symlines == 0)
11556             bSuppressLines = TRUE;
11557         
11558         ToRelease<IXCLRDataStackWalk> pStackWalk;
11559         
11560         HRESULT hr = CreateStackWalk(osID, &pStackWalk);
11561         if (FAILED(hr) || pStackWalk == NULL)
11562         {
11563             ExtOut("Failed to start stack walk: %lx\n", hr);
11564             return;
11565         }
11566         
11567         unsigned int refCount = 0, errCount = 0;
11568         ArrayHolder<SOSStackRefData> pRefs = NULL;
11569         ArrayHolder<SOSStackRefError> pErrs = NULL;
11570         if (bGC && FAILED(GetGCRefs(osID, &pRefs, &refCount, &pErrs, &errCount)))
11571             refCount = 0;
11572             
11573         TableOutput out(3, POINTERSIZE_HEX, AlignRight);
11574         out.WriteRow("Child SP", "IP", "Call Site");
11575                 
11576         do
11577         {
11578             if (IsInterrupt())
11579             {
11580                 ExtOut("<interrupted>\n");
11581                 break;
11582             }
11583             CLRDATA_ADDRESS ip = 0, sp = 0;
11584             hr = GetFrameLocation(pStackWalk, &ip, &sp);
11585
11586             DacpFrameData FrameData;
11587             HRESULT frameDataResult = FrameData.Request(pStackWalk);
11588             if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
11589                 sp = FrameData.frameAddr;
11590
11591             // Print the stack and instruction pointers.
11592             out.WriteColumn(0, sp);
11593             out.WriteColumn(1, InstructionPtr(ip));
11594
11595             // Print the method/Frame info
11596             if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
11597             {
11598                 // This is a clr!Frame.
11599                 out.WriteColumn(2, GetFrameFromAddress(TO_TADDR(FrameData.frameAddr), pStackWalk));
11600             
11601                 // Print out gc references for the Frame.  
11602                 for (unsigned int i = 0; i < refCount; ++i)
11603                     if (pRefs[i].Source == sp)
11604                         PrintRef(pRefs[i], out);
11605                         
11606                 // Print out an error message if we got one.
11607                 for (unsigned int i = 0; i < errCount; ++i)
11608                     if (pErrs[i].Source == sp)
11609                         out.WriteColumn(2, "Failed to enumerate GC references.");
11610             }
11611             else
11612             {
11613                 out.WriteColumn(2, MethodNameFromIP(ip, bSuppressLines));
11614                     
11615                 // Print out gc references.  refCount will be zero if bGC is false (or if we
11616                 // failed to fetch gc reference information).
11617                 for (unsigned int i = 0; i < refCount; ++i)
11618                     if (pRefs[i].Source == ip && pRefs[i].StackPointer == sp)
11619                         PrintRef(pRefs[i], out);
11620
11621                 // Print out an error message if we got one.
11622                 for (unsigned int i = 0; i < errCount; ++i)
11623                     if (pErrs[i].Source == sp)
11624                         out.WriteColumn(2, "Failed to enumerate GC references.");
11625
11626                 if (bParams || bLocals)
11627                     PrintArgsAndLocals(pStackWalk, bParams, bLocals);
11628             }
11629
11630         } while (pStackWalk->Next() == S_OK);
11631     }
11632     
11633     static HRESULT GetFrameLocation(IXCLRDataStackWalk *pStackWalk, CLRDATA_ADDRESS *ip, CLRDATA_ADDRESS *sp)
11634     {
11635         CROSS_PLATFORM_CONTEXT context;
11636         HRESULT hr = pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), NULL, (BYTE *)&context);
11637         if (FAILED(hr) || hr == S_FALSE)
11638         {
11639             // GetFrameContext returns S_FALSE if the frame iterator is invalid.  That's basically an error for us.
11640             ExtOut("GetFrameContext failed: %lx\n", hr);
11641             return E_FAIL;
11642         }
11643
11644         // First find the info for the Frame object, if the current frame has an associated clr!Frame.
11645         *ip = GetIP(context);
11646         *sp = GetSP(context);
11647         
11648         if (IsDbgTargetArm())
11649             *ip = *ip & ~THUMB_CODE;
11650         
11651         return S_OK;
11652     }
11653     
11654     static void PrintCurrentThread(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC)
11655     {
11656         ULONG id = 0;
11657         ULONG osid = 0;
11658         
11659         g_ExtSystem->GetCurrentThreadSystemId(&osid);
11660         ExtOut("OS Thread Id: 0x%x ", osid);
11661         g_ExtSystem->GetCurrentThreadId(&id);
11662         ExtOut("(%d)\n", id);
11663         
11664         PrintThread(osid, bParams, bLocals, bSuppressLines, bGC);
11665     }
11666 private: 
11667
11668     static HRESULT CreateStackWalk(ULONG osID, IXCLRDataStackWalk **ppStackwalk)
11669     {
11670         HRESULT hr = S_OK;
11671         ToRelease<IXCLRDataTask> pTask;
11672
11673         if ((hr = g_ExtSystem->GetCurrentThreadSystemId(&osID)) != S_OK ||
11674             (hr = g_clrData->GetTaskByOSThreadID(osID, &pTask)) != S_OK)
11675         {
11676             ExtOut("Unable to walk the managed stack. The current thread is likely not a \n");
11677             ExtOut("managed thread. You can run !threads to get a list of managed threads in\n");
11678             ExtOut("the process\n");
11679             return hr;
11680         }
11681
11682         return pTask->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED |
11683                                       CLRDATA_SIMPFRAME_MANAGED_METHOD |
11684                                       CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
11685                                       CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
11686                                       ppStackwalk);
11687     }
11688
11689     /* Prints the args and locals of for a thread's stack.
11690      * Params:
11691      *      pStackWalk - the stack we are printing
11692      *      bArgs - whether to print args
11693      *      bLocals - whether to print locals
11694      */
11695     static void PrintArgsAndLocals(IXCLRDataStackWalk *pStackWalk, BOOL bArgs, BOOL bLocals)
11696     {
11697         ToRelease<IXCLRDataFrame> pFrame;
11698         ToRelease<IXCLRDataValue> pVal;
11699         ULONG32 argCount = 0;
11700         ULONG32 localCount = 0;
11701         HRESULT hr = S_OK;
11702         
11703         hr = pStackWalk->GetFrame(&pFrame);
11704         
11705         // Print arguments
11706         if (SUCCEEDED(hr) && bArgs)
11707             hr = pFrame->GetNumArguments(&argCount);
11708                 
11709         if (SUCCEEDED(hr) && bArgs)
11710             hr = ShowArgs(argCount, pFrame, pVal);
11711         
11712         // Print locals
11713         if (SUCCEEDED(hr) && bLocals)
11714             hr = pFrame->GetNumLocalVariables(&localCount);
11715         
11716         if (SUCCEEDED(hr) && bLocals)
11717             ShowLocals(localCount, pFrame, pVal);
11718             
11719         ExtOut("\n");
11720     }
11721     
11722     
11723
11724     /* Displays the arguments to a function
11725      * Params:
11726      *      argy - the number of arguments the function has
11727      *      pFramey - the frame we are inspecting
11728      *      pVal - a pointer to the CLRDataValue we use to query for info about the args
11729      */
11730     static HRESULT ShowArgs(ULONG32 argy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal)
11731     {
11732         CLRDATA_ADDRESS addr = 0;
11733         BOOL fPrintedLocation = FALSE;
11734         ULONG64 outVar = 0;
11735         ULONG32 tmp;
11736         HRESULT hr = S_OK;
11737         
11738         ArrayHolder<WCHAR> argName = new NOTHROW WCHAR[mdNameLen];
11739         if (!argName)
11740         {
11741             ReportOOM();
11742             return E_FAIL;
11743         }
11744         
11745         for (ULONG32 i=0; i < argy; i++)
11746         {   
11747             if (i == 0)
11748             {      
11749                 ExtOut("    PARAMETERS:\n");
11750             }
11751             
11752             hr = pFramey->GetArgumentByIndex(i,
11753                                    &pVal,
11754                                    mdNameLen,
11755                                    &tmp,
11756                                    argName);
11757             
11758             if (FAILED(hr))
11759                 return hr;
11760
11761             ExtOut("        ");
11762             
11763             if (argName[0] != L'\0')
11764             {
11765                 ExtOut("%S ", argName.GetPtr());
11766             }
11767             
11768             // At times we cannot print the value of a parameter (most
11769             // common case being a non-primitive value type).  In these 
11770             // cases we need to print the location of the parameter, 
11771             // so that we can later examine it (e.g. using !dumpvc)
11772             {
11773                 bool result = SUCCEEDED(pVal->GetNumLocations(&tmp)) && tmp == 1;
11774                 if (result)
11775                     result = SUCCEEDED(pVal->GetLocationByIndex(0, &tmp, &addr));
11776                 
11777                 if (result)
11778                 {
11779                     if (tmp == CLRDATA_VLOC_REGISTER)
11780                     {
11781                         ExtOut("(<CLR reg>) ");
11782                     }
11783                     else
11784                     {
11785                         ExtOut("(0x%p) ", CDA_TO_UL64(addr));
11786                     }
11787                     fPrintedLocation = TRUE;
11788                 }
11789             }
11790
11791             if (argName[0] != L'\0' || fPrintedLocation)
11792             {
11793                 ExtOut("= ");                
11794             }
11795             
11796             if (HRESULT_CODE(pVal->GetBytes(0,&tmp,NULL)) == ERROR_BUFFER_OVERFLOW)
11797             {
11798                 ArrayHolder<BYTE> pByte = new NOTHROW BYTE[tmp + 1];
11799                 if (pByte == NULL)
11800                 {
11801                     ReportOOM();
11802                     return E_FAIL;
11803                 }
11804                 
11805                 hr = pVal->GetBytes(tmp, &tmp, pByte);
11806                 
11807                 if (FAILED(hr))
11808                 {
11809                     ExtOut("<unable to retrieve data>\n");
11810                 }
11811                 else
11812                 {
11813                     switch(tmp)
11814                     {
11815                         case 1: outVar = *((BYTE *)pByte.GetPtr()); break;
11816                         case 2: outVar = *((short *)pByte.GetPtr()); break;
11817                         case 4: outVar = *((DWORD *)pByte.GetPtr()); break;
11818                         case 8: outVar = *((ULONG64 *)pByte.GetPtr()); break;
11819                         default: outVar = 0;
11820                     }
11821
11822                     if (outVar)
11823                         DMLOut("0x%s\n", DMLObject(outVar));
11824                     else
11825                         ExtOut("0x%p\n", (ULONG64)outVar);
11826                 }
11827                 
11828             }
11829             else
11830             {
11831                 ExtOut("<no data>\n");
11832             }
11833             
11834             pVal->Release();
11835         }
11836         
11837         return S_OK;
11838     }
11839
11840
11841     /* Prints the locals of a frame.
11842      * Params:
11843      *      localy - the number of locals in the frame
11844      *      pFramey - the frame we are inspecting
11845      *      pVal - a pointer to the CLRDataValue we use to query for info about the args
11846      */
11847     static HRESULT ShowLocals(ULONG32 localy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal)
11848     {
11849         for (ULONG32 i=0; i < localy; i++)
11850         {   
11851             if (i == 0)
11852                 ExtOut("    LOCALS:\n");
11853             
11854             HRESULT hr;
11855             ExtOut("        ");
11856             
11857             // local names don't work in Whidbey.
11858             hr = pFramey->GetLocalVariableByIndex(i, &pVal, mdNameLen, NULL, g_mdName);
11859             if (FAILED(hr))
11860             {
11861                 return hr;
11862             }
11863
11864             ULONG32 numLocations;
11865             if (SUCCEEDED(pVal->GetNumLocations(&numLocations)) &&
11866                 numLocations == 1)
11867             {
11868                 ULONG32 flags;
11869                 CLRDATA_ADDRESS addr;
11870                 if (SUCCEEDED(pVal->GetLocationByIndex(0, &flags, &addr)))
11871                 {
11872                     if (flags == CLRDATA_VLOC_REGISTER)
11873                     {
11874                         ExtOut("<CLR reg> ");
11875                     }
11876                     else
11877                     {
11878                         ExtOut("0x%p ", CDA_TO_UL64(addr));
11879                     }
11880                 }
11881
11882                 // Can I get a name for the item?
11883
11884                 ExtOut("= ");                
11885             }
11886             ULONG32 dwSize = 0;
11887             hr = pVal->GetBytes(0, &dwSize, NULL);
11888             
11889             if (HRESULT_CODE(hr) == ERROR_BUFFER_OVERFLOW)
11890             {
11891                 ArrayHolder<BYTE> pByte = new NOTHROW BYTE[dwSize + 1];
11892                 if (pByte == NULL)
11893                 {
11894                     ReportOOM();
11895                     return E_FAIL;
11896                 }
11897
11898                 hr = pVal->GetBytes(dwSize,&dwSize,pByte);
11899
11900                 if (FAILED(hr))
11901                 {
11902                     ExtOut("<unable to retrieve data>\n");
11903                 }
11904                 else
11905                 {
11906                     ULONG64 outVar = 0;
11907                     switch(dwSize)
11908                     {
11909                         case 1: outVar = *((BYTE *) pByte.GetPtr()); break;
11910                         case 2: outVar = *((short *) pByte.GetPtr()); break;
11911                         case 4: outVar = *((DWORD *) pByte.GetPtr()); break;
11912                         case 8: outVar = *((ULONG64 *) pByte.GetPtr()); break;
11913                         default: outVar = 0;
11914                     }
11915
11916                     if (outVar)
11917                         DMLOut("0x%s\n", DMLObject(outVar));
11918                     else
11919                         ExtOut("0x%p\n", (ULONG64)outVar);
11920                 }
11921             }
11922             else
11923             {
11924                 ExtOut("<no data>\n");
11925             }
11926             
11927             pVal->Release();
11928         }
11929         
11930         return S_OK;
11931     }
11932
11933 };
11934
11935 #ifndef FEATURE_PAL
11936 WatchCmd g_watchCmd;
11937
11938 // The grand new !Watch command, private to Apollo for now
11939 DECLARE_API(Watch)
11940 {
11941     INIT_API_NOEE();
11942     BOOL bExpression = FALSE;
11943     StringHolder addExpression;
11944     StringHolder aExpression;
11945     StringHolder saveName;
11946     StringHolder sName;
11947     StringHolder expression;
11948     StringHolder filterName;
11949     StringHolder renameOldName;
11950     size_t expandIndex = -1;
11951     size_t removeIndex = -1;
11952     BOOL clear = FALSE;
11953
11954     size_t nArg = 0;
11955     CMDOption option[] = 
11956     {   // name, vptr, type, hasValue
11957         {"-add", &addExpression.data, COSTRING, TRUE},
11958         {"-a", &aExpression.data, COSTRING, TRUE},
11959         {"-save", &saveName.data, COSTRING, TRUE},
11960         {"-s", &sName.data, COSTRING, TRUE},
11961         {"-clear", &clear, COBOOL, FALSE},
11962         {"-c", &clear, COBOOL, FALSE},
11963         {"-expand", &expandIndex, COSIZE_T, TRUE},
11964         {"-filter", &filterName.data, COSTRING, TRUE},
11965         {"-r", &removeIndex, COSIZE_T, TRUE},
11966         {"-remove", &removeIndex, COSIZE_T, TRUE},
11967         {"-rename", &renameOldName.data, COSTRING, TRUE},
11968     };
11969
11970     CMDValue arg[] = 
11971     {   // vptr, type
11972         {&expression.data, COSTRING}
11973     };
11974     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
11975     {
11976         return Status;
11977     }
11978
11979     if(addExpression.data != NULL || aExpression.data != NULL)
11980     {
11981         WCHAR pAddExpression[MAX_EXPRESSION];
11982         memset(pAddExpression, 0, MAX_EXPRESSION);
11983         swprintf_s(pAddExpression, MAX_EXPRESSION, L"%S", addExpression.data != NULL ? addExpression.data : aExpression.data);
11984         Status = g_watchCmd.Add(pAddExpression);
11985     }
11986     else if(removeIndex != -1)
11987     {
11988         if(removeIndex <= 0)
11989         {
11990             ExtOut("Index must be a postive decimal number\n");
11991         }
11992         else
11993         {
11994             Status = g_watchCmd.Remove((int)removeIndex);
11995             if(Status == S_OK)
11996                 ExtOut("Watch expression #%d has been removed\n", removeIndex);
11997             else if(Status == S_FALSE)
11998                 ExtOut("There is no watch expression with index %d\n", removeIndex);
11999             else
12000                 ExtOut("Unknown failure 0x%x removing watch expression\n", Status);
12001         }
12002     }
12003     else if(saveName.data != NULL || sName.data != NULL)
12004     {
12005         WCHAR pSaveName[MAX_EXPRESSION];
12006         memset(pSaveName, 0, MAX_EXPRESSION);
12007         swprintf_s(pSaveName, MAX_EXPRESSION, L"%S", saveName.data != NULL ? saveName.data : sName.data);
12008         Status = g_watchCmd.SaveList(pSaveName);
12009     }
12010     else if(clear)
12011     {
12012         g_watchCmd.Clear();
12013     }
12014     else if(renameOldName.data != NULL)
12015     {
12016         if(nArg != 1)
12017         {
12018              ExtOut("Must provide an old and new name. Usage: !watch -rename <old_name> <new_name>.\n");
12019              return S_FALSE;
12020         }
12021         WCHAR pOldName[MAX_EXPRESSION];
12022         memset(pOldName, 0, MAX_EXPRESSION);
12023         swprintf_s(pOldName, MAX_EXPRESSION, L"%S", renameOldName.data);
12024         WCHAR pNewName[MAX_EXPRESSION];
12025         memset(pNewName, 0, MAX_EXPRESSION);
12026         swprintf_s(pNewName, MAX_EXPRESSION, L"%S", expression.data);
12027         g_watchCmd.RenameList(pOldName, pNewName);
12028     }
12029     // print the tree, possibly with filtering and/or expansion
12030     else if(expandIndex != -1 || expression.data == NULL)
12031     {
12032         WCHAR pExpression[MAX_EXPRESSION];
12033         memset(pExpression, 0, MAX_EXPRESSION);
12034
12035         if(expandIndex != -1)
12036         {
12037             if(expression.data != NULL)
12038             {
12039                 swprintf_s(pExpression, MAX_EXPRESSION, L"%S", expression.data);
12040             }
12041             else
12042             {
12043                 ExtOut("No expression was provided. Usage !watch -expand <index> <expression>\n");
12044                 return S_FALSE;
12045             }
12046         }
12047         WCHAR pFilterName[MAX_EXPRESSION];
12048         memset(pFilterName, 0, MAX_EXPRESSION);
12049         if(filterName.data != NULL)
12050         {
12051             swprintf_s(pFilterName, MAX_EXPRESSION, L"%S", filterName.data);
12052         }
12053
12054         g_watchCmd.Print((int)expandIndex, pExpression, pFilterName);
12055     }
12056     else
12057     {
12058         ExtOut("Unrecognized argument: %s\n", expression.data);
12059     }
12060
12061     return Status;
12062 }
12063 #endif
12064
12065 DECLARE_API(ClrStack)
12066 {
12067     INIT_API();
12068
12069     BOOL bAll = FALSE;    
12070     BOOL bParams = FALSE;
12071     BOOL bLocals = FALSE;
12072     BOOL bSuppressLines = FALSE;
12073     BOOL bICorDebug = FALSE;
12074     BOOL bGC = FALSE;
12075     BOOL dml = FALSE;
12076     DWORD frameToDumpVariablesFor = -1;
12077     StringHolder cvariableName;
12078     ArrayHolder<WCHAR> wvariableName = new NOTHROW WCHAR[mdNameLen];
12079     if (wvariableName == NULL)
12080     {
12081         ReportOOM();
12082         return E_OUTOFMEMORY;
12083     }
12084
12085     memset(wvariableName, 0, sizeof(wvariableName));
12086
12087     size_t nArg = 0;
12088     CMDOption option[] = 
12089     {   // name, vptr, type, hasValue
12090         {"-a", &bAll, COBOOL, FALSE},
12091         {"-p", &bParams, COBOOL, FALSE},
12092         {"-l", &bLocals, COBOOL, FALSE},
12093         {"-n", &bSuppressLines, COBOOL, FALSE},
12094         {"-i", &bICorDebug, COBOOL, FALSE},
12095         {"-gc", &bGC, COBOOL, FALSE},
12096         {"/d", &dml, COBOOL, FALSE},
12097     };
12098     CMDValue arg[] = 
12099     {   // vptr, type
12100         {&cvariableName.data, COSTRING},
12101         {&frameToDumpVariablesFor, COSIZE_T},
12102     };
12103         if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
12104     {
12105         return Status;
12106     }
12107
12108     EnableDMLHolder dmlHolder(dml);
12109     if (bAll || bParams || bLocals)
12110     {
12111         // No parameter or local supports for minidump case!
12112         MINIDUMP_NOT_SUPPORTED();        
12113     }
12114
12115     if (bAll)
12116     {
12117         bParams = bLocals = TRUE;
12118     }
12119
12120     if (bICorDebug)
12121     {
12122         if(nArg > 0)
12123         {
12124             bool firstParamIsNumber = true;
12125             for(DWORD i = 0; i < strlen(cvariableName.data); i++)
12126                 firstParamIsNumber = firstParamIsNumber && isdigit(cvariableName.data[i]);
12127
12128             if(firstParamIsNumber && nArg == 1)
12129             {
12130                 frameToDumpVariablesFor = (DWORD)GetExpression(cvariableName.data);
12131                 cvariableName.data[0] = '\0';
12132             }
12133         }
12134         if(cvariableName.data != NULL && strlen(cvariableName.data) > 0)
12135             swprintf_s(wvariableName, mdNameLen, L"%S\0", cvariableName.data);
12136         
12137         if(wcslen(wvariableName) > 0)
12138             bParams = bLocals = TRUE;
12139
12140         EnableDMLHolder dmlHolder(TRUE);
12141         return ClrStackImplWithICorDebug::ClrStackFromPublicInterface(bParams, bLocals, FALSE, wvariableName, frameToDumpVariablesFor);
12142     }
12143     
12144     ClrStackImpl::PrintCurrentThread(bParams, bLocals, bSuppressLines, bGC);
12145     
12146     return S_OK;
12147 }
12148
12149 BOOL IsMemoryInfoAvailable()
12150 {
12151     ULONG Class;
12152     ULONG Qualifier;
12153     g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
12154     if (Qualifier == DEBUG_DUMP_SMALL) 
12155     {
12156         g_ExtControl->GetDumpFormatFlags(&Qualifier);
12157         if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) == 0)            
12158         {
12159             if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY_INFO) == 0)
12160             {
12161                 return FALSE;
12162             }            
12163         }
12164     }        
12165     return TRUE;
12166 }
12167
12168 DECLARE_API( VMMap )
12169 {
12170     INIT_API();
12171
12172     if (IsMiniDumpFile() || !IsMemoryInfoAvailable())
12173     {
12174         ExtOut("!VMMap requires a full memory dump (.dump /ma) or a live process.\n");
12175     }
12176     else
12177     {
12178         vmmap();
12179     }
12180
12181     return Status;
12182 }   // DECLARE_API( vmmap )
12183
12184 DECLARE_API( SOSFlush )
12185 {
12186     INIT_API();
12187
12188     g_clrData->Flush();
12189     
12190     return Status;
12191 }   // DECLARE_API( SOSFlush )
12192
12193 DECLARE_API( VMStat )
12194 {
12195     INIT_API();
12196
12197     if (IsMiniDumpFile() || !IsMemoryInfoAvailable())
12198     {
12199         ExtOut("!VMStat requires a full memory dump (.dump /ma) or a live process.\n");
12200     }
12201     else
12202     {
12203         vmstat();
12204     }
12205
12206     return Status;
12207 }   // DECLARE_API( vmmap )
12208
12209 /**********************************************************************\
12210 * Routine Description:                                                 *
12211 *                                                                      *
12212 *    This function saves a dll to a file.                              *  
12213 *                                                                      *
12214 \**********************************************************************/
12215 DECLARE_API(SaveModule)
12216 {
12217     INIT_API();
12218     MINIDUMP_NOT_SUPPORTED();    
12219     
12220 #ifndef FEATURE_PAL
12221
12222     StringHolder Location;
12223     DWORD_PTR moduleAddr = NULL;
12224     BOOL bIsImage;
12225
12226     CMDValue arg[] = 
12227     {   // vptr, type
12228         {&moduleAddr, COHEX},
12229         {&Location.data, COSTRING}
12230     };
12231     size_t nArg;
12232     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) 
12233     {
12234         return Status;
12235     }
12236     if (nArg != 2)
12237     {
12238         ExtOut("Usage: SaveModule <address> <file to save>\n");
12239         return Status;
12240     }
12241     if (moduleAddr == 0) {
12242         ExtOut ("Invalid arg\n");
12243         return Status;
12244     }
12245
12246     char* ptr = Location.data;
12247     
12248     DWORD_PTR dllBase = 0;
12249     ULONG64 base;
12250     if (g_ExtSymbols->GetModuleByOffset(TO_CDADDR(moduleAddr),0,NULL,&base) == S_OK)
12251     {
12252         dllBase = TO_TADDR(base);
12253     }
12254     else if (IsModule(moduleAddr))
12255     {        
12256         DacpModuleData module;
12257         module.Request(g_sos, TO_CDADDR(moduleAddr));
12258         dllBase = TO_TADDR(module.ilBase);
12259         if (dllBase == 0)
12260         {
12261             ExtOut ("Module does not have base address\n");
12262             return Status;
12263         }
12264     }
12265     else
12266     {
12267         ExtOut ("%p is not a Module or base address\n", (ULONG64)moduleAddr);
12268         return Status;
12269     }
12270
12271     MEMORY_BASIC_INFORMATION64 mbi;
12272     if (FAILED(g_ExtData2->QueryVirtual(TO_CDADDR(dllBase), &mbi)))
12273     {
12274         ExtOut("Failed to retrieve information about segment %p", (ULONG64)dllBase);
12275         return Status;
12276     }
12277
12278     // module loaded as an image or mapped as a flat file?
12279     bIsImage = (mbi.Type == MEM_IMAGE);
12280
12281     IMAGE_DOS_HEADER DosHeader;
12282     if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
12283         return FALSE;
12284
12285     IMAGE_NT_HEADERS Header;
12286     if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase + DosHeader.e_lfanew), &Header, sizeof(Header), NULL) != S_OK)
12287         return FALSE;
12288
12289     DWORD_PTR sectionAddr = dllBase + DosHeader.e_lfanew + offsetof(IMAGE_NT_HEADERS,OptionalHeader)
12290             + Header.FileHeader.SizeOfOptionalHeader;    
12291
12292     IMAGE_SECTION_HEADER section;
12293     struct MemLocation
12294     {
12295         DWORD_PTR VAAddr;
12296         DWORD_PTR VASize;
12297         DWORD_PTR FileAddr;
12298         DWORD_PTR FileSize;
12299     };
12300
12301     int nSection = Header.FileHeader.NumberOfSections;
12302     ExtOut("%u sections in file\n",nSection);
12303     MemLocation *memLoc = (MemLocation*)_alloca(nSection*sizeof(MemLocation));
12304     int indxSec = -1;
12305     int slot;
12306     for (int n = 0; n < nSection; n++)
12307     {
12308         if (g_ExtData->ReadVirtual(TO_CDADDR(sectionAddr), &section, sizeof(section), NULL) == S_OK)
12309         {
12310             for (slot = 0; slot <= indxSec; slot ++)
12311                 if (section.PointerToRawData < memLoc[slot].FileAddr)
12312                     break;
12313
12314             for (int k = indxSec; k >= slot; k --)
12315                 memcpy(&memLoc[k+1], &memLoc[k], sizeof(MemLocation));
12316
12317             memLoc[slot].VAAddr = section.VirtualAddress;
12318             memLoc[slot].VASize = section.Misc.VirtualSize;
12319             memLoc[slot].FileAddr = section.PointerToRawData;
12320             memLoc[slot].FileSize = section.SizeOfRawData;
12321             ExtOut("section %d - VA=%x, VASize=%x, FileAddr=%x, FileSize=%x\n",
12322                 n, memLoc[slot].VAAddr,memLoc[slot]. VASize,memLoc[slot].FileAddr,
12323                 memLoc[slot].FileSize);
12324             indxSec ++;
12325         }
12326         else
12327         {
12328             ExtOut("Fail to read PE section info\n");
12329             return Status;
12330         }
12331         sectionAddr += sizeof(section);
12332     }
12333
12334     if (ptr[0] == '\0')
12335     {
12336         ExtOut ("File not specified\n");
12337         return Status;
12338     }
12339
12340     PCSTR file = ptr;
12341     ptr += strlen(ptr)-1;
12342     while (isspace(*ptr))
12343     {
12344         *ptr = '\0';
12345         ptr --;
12346     }
12347
12348     HANDLE hFile = CreateFileA(file,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);
12349     if (hFile == INVALID_HANDLE_VALUE)
12350     {
12351         ExtOut ("Fail to create file %s\n", file);
12352         return Status;
12353     }
12354
12355     ULONG pageSize = OSPageSize();
12356     char *buffer = (char *)_alloca(pageSize);
12357     DWORD nRead;
12358     DWORD nWrite;
12359     
12360     // NT PE Headers
12361     TADDR dwAddr = dllBase;
12362     TADDR dwEnd = dllBase + Header.OptionalHeader.SizeOfHeaders;
12363     while (dwAddr < dwEnd)
12364     {
12365         nRead = pageSize;
12366         if (dwEnd - dwAddr < nRead)
12367             nRead = (ULONG)(dwEnd - dwAddr);
12368
12369         if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK)
12370         {
12371             WriteFile(hFile,buffer,nRead,&nWrite,NULL);
12372         }
12373         else
12374         {
12375             ExtOut ("Fail to read memory\n");
12376             goto end;
12377         }
12378         dwAddr += nRead;
12379     }
12380
12381     for (slot = 0; slot <= indxSec; slot ++)
12382     {
12383         dwAddr = dllBase + (bIsImage ? memLoc[slot].VAAddr : memLoc[slot].FileAddr);
12384         dwEnd = memLoc[slot].FileSize + dwAddr - 1;
12385
12386         while (dwAddr <= dwEnd)
12387         {
12388             nRead = pageSize;
12389             if (dwEnd - dwAddr + 1 < pageSize)
12390                 nRead = (ULONG)(dwEnd - dwAddr + 1);
12391             
12392             if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK)
12393             {
12394                 WriteFile(hFile,buffer,nRead,&nWrite,NULL);
12395             }
12396             else
12397             {
12398                 ExtOut ("Fail to read memory\n");
12399                 goto end;
12400             }
12401             dwAddr += pageSize;
12402         }
12403     }
12404 end:
12405     CloseHandle (hFile);
12406     return Status;
12407
12408 #else
12409
12410     _ASSERTE(false);
12411     return E_FAIL;
12412
12413 #endif // FEATURE_PAL
12414 }
12415
12416 #ifdef _DEBUG
12417 DECLARE_API(dbgout)
12418 {
12419     INIT_API();
12420
12421     BOOL bOff = FALSE;
12422
12423     CMDOption option[] = 
12424     {   // name, vptr, type, hasValue
12425         {"-off", &bOff, COBOOL, FALSE},
12426     };
12427
12428     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
12429     {
12430         return Status;
12431     }    
12432
12433     Output::SetDebugOutputEnabled(!bOff);
12434     return Status;
12435 }
12436 DECLARE_API(filthint)
12437 {
12438     INIT_API();
12439
12440     BOOL bOff = FALSE;
12441     DWORD_PTR filter = 0;
12442
12443     CMDOption option[] = 
12444     {   // name, vptr, type, hasValue
12445         {"-off", &bOff, COBOOL, FALSE},
12446     };
12447     CMDValue arg[] = 
12448     {   // vptr, type
12449         {&filter, COHEX}
12450     };
12451     size_t nArg;
12452     if (!GetCMDOption(args, option, _countof(option),
12453                       arg, _countof(arg), &nArg)) 
12454     {
12455         return Status;
12456     }    
12457     if (bOff)
12458     {
12459         g_filterHint = 0;
12460         return Status;
12461     }
12462
12463     g_filterHint = filter;
12464     return Status;
12465 }
12466 #endif // _DEBUG
12467
12468 static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp, 
12469         ULONG64 IPAddr, StringOutput& so)
12470 {
12471 #define DOAPPEND(str)         \
12472     do { \
12473     if (!so.Append((str))) {  \
12474     return E_OUTOFMEMORY; \
12475     }} while (0)
12476
12477     // Should we skip explicit frames?  They are characterized by Esp = 0, && Eip = 0 or 1.
12478     // See comment in FormatGeneratedException() for explanation why on non_IA64 Eip is 1, and not 0
12479     if (!(Flags & SOS_STACKTRACE_SHOWEXPLICITFRAMES) && (Esp == 0) && (IPAddr == 1))
12480     {
12481         return S_FALSE;
12482     }
12483
12484     DacpMethodDescData MethodDescData;
12485     if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK)
12486     {
12487         return E_FAIL;
12488     }
12489
12490     static WCHAR wszNameBuffer[1024]; // should be large enough
12491
12492     if (Flags & SOS_STACKTRACE_SHOWADDRESSES)
12493     {
12494         _snwprintf_s(wszNameBuffer, _countof(wszNameBuffer), _countof(wszNameBuffer)-1, L"%p %p ", 
12495                     (void*)(size_t) Esp, 
12496                     (void*)(size_t) IPAddr); // _TRUNCATE
12497
12498         DOAPPEND(wszNameBuffer);
12499     }
12500
12501     DacpModuleData dmd;
12502     BOOL bModuleNameWorked = FALSE;
12503     ULONG64 addrInModule = IPAddr;
12504     if (dmd.Request(g_sos, MethodDescData.ModulePtr) == S_OK)
12505     {
12506         CLRDATA_ADDRESS base = 0;
12507         if (g_sos->GetPEFileBase(dmd.File, &base) == S_OK)
12508         {
12509             if (base)
12510             {
12511                 addrInModule = base;
12512             }
12513         }
12514     }
12515     ULONG Index;
12516     ULONG64 base;
12517     if (g_ExtSymbols->GetModuleByOffset(UL64_TO_CDA(addrInModule), 0, &Index, 
12518         &base) == S_OK)
12519     {                                    
12520         CHAR ModuleName[MAX_PATH+1];
12521
12522         if (g_ExtSymbols->GetModuleNames (Index, base,
12523             NULL, 0, NULL,
12524             ModuleName, MAX_PATH, NULL,
12525             NULL, 0, NULL) == S_OK)
12526         {
12527             MultiByteToWideChar (CP_ACP, 
12528                     0, 
12529                     ModuleName, 
12530                     -1, 
12531                     wszNameBuffer, 
12532                     ARRAYSIZE(wszNameBuffer));
12533             DOAPPEND (wszNameBuffer);
12534             bModuleNameWorked = TRUE;
12535         }
12536     }
12537
12538     // Under certain circumstances DacpMethodDescData::GetMethodName() 
12539     //   returns a module qualified method name
12540     HRESULT hr = g_sos->GetMethodDescName(dwStartAddr, 1024, wszNameBuffer, NULL);
12541
12542     WCHAR* pwszMethNameBegin = (hr != S_OK ? NULL : wcschr(wszNameBuffer, L'!'));
12543     if (!bModuleNameWorked && hr == S_OK && pwszMethNameBegin != NULL)
12544     {
12545         // if we weren't able to get the module name, but GetMethodName returned
12546         // the module as part of the returned method name, use this data
12547         DOAPPEND(wszNameBuffer);
12548     }
12549     else
12550     {
12551         if (!bModuleNameWorked)
12552         {
12553             DOAPPEND (L"UNKNOWN");
12554         }
12555         DOAPPEND(L"!");
12556         if (hr == S_OK)
12557         {
12558             // the module name we retrieved above from debugger will take 
12559             // precedence over the name possibly returned by GetMethodName()
12560             DOAPPEND(pwszMethNameBegin != NULL ? (pwszMethNameBegin+1) : wszNameBuffer);
12561         }
12562         else
12563         {
12564             DOAPPEND(L"UNKNOWN");
12565         }
12566     }
12567
12568     ULONG64 Displacement = (IPAddr - MethodDescData.NativeCodeAddr);
12569     if (Displacement)
12570     {
12571         _snwprintf_s(wszNameBuffer,_countof (wszNameBuffer),  _countof (wszNameBuffer)-1, L"+%#x", Displacement); // _TRUNCATE
12572         DOAPPEND (wszNameBuffer);
12573     }
12574
12575     return S_OK;
12576 #undef DOAPPEND
12577 }
12578
12579 BOOL AppendContext(LPVOID pTransitionContexts, size_t maxCount, size_t *pcurCount, size_t uiSizeOfContext,
12580     CROSS_PLATFORM_CONTEXT *context)
12581 {
12582     if (pTransitionContexts == NULL || *pcurCount >= maxCount)
12583     {
12584         ++(*pcurCount);
12585         return FALSE;
12586     }
12587     if (uiSizeOfContext == sizeof(StackTrace_SimpleContext))
12588     {
12589         StackTrace_SimpleContext *pSimple = (StackTrace_SimpleContext *) pTransitionContexts;
12590         g_targetMachine->FillSimpleContext(&pSimple[*pcurCount], context);
12591     }
12592     else if (uiSizeOfContext == g_targetMachine->GetContextSize())
12593     {
12594         // FillTargetContext ensures we only write uiSizeOfContext bytes in pTransitionContexts
12595         // and not sizeof(CROSS_PLATFORM_CONTEXT) bytes (which would overrun).
12596         g_targetMachine->FillTargetContext(pTransitionContexts, context, (int)(*pcurCount));
12597     }
12598     else
12599     {
12600         return FALSE;
12601     }
12602     ++(*pcurCount);
12603     return TRUE;
12604 }
12605
12606 HRESULT CALLBACK ImplementEFNStackTrace(
12607     PDEBUG_CLIENT Client,
12608     __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
12609     size_t *puiTextLength,
12610     LPVOID pTransitionContexts,
12611     size_t *puiTransitionContextCount,
12612     size_t uiSizeOfContext,
12613     DWORD Flags) 
12614 {
12615
12616 #define DOAPPEND(str) if (!so.Append((str))) { \
12617     Status = E_OUTOFMEMORY;                    \
12618     goto Exit;                                 \
12619 }
12620
12621     HRESULT Status = E_FAIL;    
12622     StringOutput so;
12623     size_t transitionContextCount = 0;
12624
12625     if (puiTextLength == NULL)
12626     {
12627         return E_INVALIDARG;
12628     }
12629
12630     if (pTransitionContexts)
12631     {
12632         if (puiTransitionContextCount == NULL)
12633         {
12634             return E_INVALIDARG;
12635         }
12636
12637         // Do error checking on context size
12638         if ((uiSizeOfContext != g_targetMachine->GetContextSize()) &&
12639             (uiSizeOfContext != sizeof(StackTrace_SimpleContext)))
12640         {
12641             return E_INVALIDARG;
12642         }
12643     }
12644
12645     IXCLRDataStackWalk *pStackWalk = NULL;
12646     IXCLRDataTask* Task;
12647     ULONG ThreadId;
12648
12649     if ((Status = g_ExtSystem->GetCurrentThreadSystemId(&ThreadId)) != S_OK ||
12650         (Status = g_clrData->GetTaskByOSThreadID(ThreadId, &Task)) != S_OK)
12651     {
12652         // Not a managed thread.
12653         return SOS_E_NOMANAGEDCODE;
12654     }
12655
12656     Status = Task->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED |
12657                                    CLRDATA_SIMPFRAME_MANAGED_METHOD |
12658                                    CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
12659                                    CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
12660                                    &pStackWalk);
12661
12662     Task->Release();
12663
12664     if (Status != S_OK)
12665     {
12666         if (Status == E_FAIL)
12667         {
12668             return SOS_E_NOMANAGEDCODE;
12669         }
12670         return Status;
12671     }
12672
12673 #ifdef _TARGET_WIN64_
12674
12675     ULONG numFrames = 0;
12676     PDEBUG_CONTROL4 g_ExtControl4 = NULL;
12677     // Do we have advanced capability?
12678     if ((Status = g_ExtControl->QueryInterface(__uuidof(IDebugControl4),
12679                                  (void **)&g_ExtControl4)) == S_OK)
12680     {
12681         // GetContextStackTrace fills g_X64FrameContexts as an array of 
12682         // contexts packed as target architecture contexts. We cannot 
12683         // safely cast this as an array of CROSS_PLATFORM_CONTEXT, since 
12684         // sizeof(CROSS_PLATFORM_CONTEXT) != sizeof(TGT_CONTEXT)
12685         Status = g_ExtControl4->GetContextStackTrace(
12686             NULL,
12687             0,
12688             g_Frames,
12689             MAX_STACK_FRAMES,
12690             g_X64FrameContexts,
12691             MAX_STACK_FRAMES*g_targetMachine->GetContextSize(),
12692             g_targetMachine->GetContextSize(),
12693             &numFrames
12694             );
12695     }
12696     else
12697     {
12698         // The new interface is required for context information
12699         goto Exit;
12700     }
12701
12702     if (FAILED(Status))
12703     {
12704         goto Exit;
12705     }
12706
12707     BOOL bInNative = TRUE;
12708     for (ULONG i=0; i < numFrames; i++)
12709     {
12710         PDEBUG_STACK_FRAME pCur = g_Frames + i;                
12711
12712         CLRDATA_ADDRESS pMD;
12713         if (g_sos->GetMethodDescPtrFromIP(pCur->InstructionOffset, &pMD) == S_OK)
12714         {
12715             if (bInNative || transitionContextCount==0)
12716             {
12717                 // We only want to list one transition frame if there are multiple frames.
12718                 bInNative = FALSE;
12719
12720                 DOAPPEND (L"(TransitionMU)\n");
12721                 // For each transition, we need to store the context information
12722                 if (puiTransitionContextCount)
12723                 {
12724                     // below we cast the i-th AMD64_CONTEXT to CROSS_PLATFORM_CONTEXT
12725                     AppendContext (pTransitionContexts, *puiTransitionContextCount, 
12726                         &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_X64FrameContexts[i])));
12727                 }
12728                 else
12729                 {
12730                     transitionContextCount++;
12731                 }
12732             }
12733
12734             Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags,
12735                     pCur->StackOffset, pCur->InstructionOffset, so);
12736             if (FAILED(Status))
12737             {
12738                 goto Exit;
12739             }
12740             else if (Status == S_OK)
12741             {
12742                 DOAPPEND (L"\n");
12743             }
12744             // for S_FALSE do not append anything
12745
12746         }        
12747         else
12748         {
12749             if (!bInNative)
12750             {
12751                 // We only want to list one transition frame if there are multiple frames.
12752                 bInNative = TRUE;
12753
12754                 DOAPPEND (L"(TransitionUM)\n");
12755                 // For each transition, we need to store the context information
12756                 if (puiTransitionContextCount)
12757                 {
12758                     AppendContext (pTransitionContexts, *puiTransitionContextCount, 
12759                         &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_X64FrameContexts[i])));
12760                 }
12761                 else
12762                 {
12763                     transitionContextCount++;
12764                 }
12765             }
12766         }
12767     }
12768
12769 Exit:
12770     if (g_ExtControl4)
12771     {
12772         g_ExtControl4->Release();
12773         g_ExtControl4 = NULL;
12774     }
12775 #else // _TARGET_WIN64_
12776
12777 #ifdef _DEBUG
12778     size_t prevLength = 0;
12779     static WCHAR wszNameBuffer[1024]; // should be large enough
12780     wcscpy_s(wszNameBuffer, 1024, L"Frame"); // default value
12781 #endif
12782
12783     BOOL bInNative = TRUE;
12784
12785     UINT frameCount = 0;
12786     do
12787     {
12788         DacpFrameData FrameData;
12789         if ((Status = FrameData.Request(pStackWalk)) != S_OK)
12790         {
12791             goto Exit;
12792         }
12793
12794         CROSS_PLATFORM_CONTEXT context;
12795         if ((Status=pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(),
12796                                            NULL, (BYTE *)&context))!=S_OK)
12797         {
12798             goto Exit;
12799         }
12800
12801         ExtDbgOut ( " * Ctx[BSI]:  %08x  %08x  %08x    ", GetBP(context), GetSP(context), GetIP(context) );
12802
12803         CLRDATA_ADDRESS pMD;
12804         if (!FrameData.frameAddr)
12805         {
12806             if (bInNative || transitionContextCount==0)
12807             {
12808                 // We only want to list one transition frame if there are multiple frames.
12809                 bInNative = FALSE;
12810
12811                 DOAPPEND (L"(TransitionMU)\n");
12812                 // For each transition, we need to store the context information
12813                 if (puiTransitionContextCount)
12814                 {
12815                     AppendContext (pTransitionContexts, *puiTransitionContextCount, 
12816                             &transitionContextCount, uiSizeOfContext, &context);
12817                 }
12818                 else
12819                 {
12820                     transitionContextCount++;
12821                 }                    
12822             }
12823
12824             // we may have a method, try to get the methoddesc
12825             if (g_sos->GetMethodDescPtrFromIP(GetIP(context), &pMD)==S_OK)
12826             {
12827                 Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags, 
12828                                           GetSP(context), GetIP(context), so);
12829                 if (FAILED(Status))
12830                 {
12831                     goto Exit;
12832                 }
12833                 else if (Status == S_OK)
12834                 {
12835                     DOAPPEND (L"\n");
12836                 }
12837                 // for S_FALSE do not append anything
12838             }
12839         }
12840         else
12841         {
12842 #ifdef _DEBUG
12843             if (Output::IsDebugOutputEnabled())
12844             {
12845                 DWORD_PTR vtAddr;
12846                 MOVE(vtAddr, TO_TADDR(FrameData.frameAddr));
12847                 if (g_sos->GetFrameName(vtAddr, 1024, wszNameBuffer, NULL) == S_OK)
12848                     ExtDbgOut("[%ls: %08x] ", wszNameBuffer, FrameData.frameAddr);  
12849                 else
12850                     ExtDbgOut("[Frame: %08x] ", FrameData.frameAddr);
12851             }
12852 #endif
12853             if (!bInNative)
12854             {
12855                 // We only want to list one transition frame if there are multiple frames.
12856                 bInNative = TRUE;
12857
12858                 DOAPPEND (L"(TransitionUM)\n");
12859                 // For each transition, we need to store the context information
12860                 if (puiTransitionContextCount)
12861                 {
12862                     AppendContext (pTransitionContexts, *puiTransitionContextCount, 
12863                             &transitionContextCount, uiSizeOfContext, &context);
12864                 }
12865                 else
12866                 {
12867                     transitionContextCount++;
12868                 }                    
12869             }
12870         }
12871
12872 #ifdef _DEBUG
12873         if (so.Length() > prevLength)
12874         {
12875             ExtDbgOut ( "%ls", so.String()+prevLength );
12876             prevLength = so.Length();
12877         }
12878         else
12879             ExtDbgOut ( "\n" );
12880 #endif
12881
12882     } 
12883     while ((frameCount++) < MAX_STACK_FRAMES && pStackWalk->Next()==S_OK);
12884     
12885     Status = S_OK;
12886
12887 Exit:
12888 #endif // _TARGET_WIN64_
12889
12890     if (pStackWalk)
12891     {
12892         pStackWalk->Release();
12893         pStackWalk = NULL;
12894     }
12895
12896     // We have finished. Does the user want to copy this data to a buffer?
12897     if (Status == S_OK)
12898     {
12899         if(wszTextOut)
12900         {
12901             // They want at least partial output
12902             wcsncpy_s (wszTextOut, *puiTextLength, so.String(),  *puiTextLength-1); // _TRUNCATE
12903         }
12904         else
12905         {
12906             *puiTextLength = wcslen (so.String()) + 1;
12907         }
12908
12909         if (puiTransitionContextCount)
12910         {
12911             *puiTransitionContextCount = transitionContextCount;
12912         }
12913     }
12914
12915     return Status;
12916 }
12917
12918 #ifndef FEATURE_PAL
12919 // TODO: Convert PAL_TRY_NAKED to something that works on the Mac.
12920 HRESULT CALLBACK ImplementEFNStackTraceTry(
12921     PDEBUG_CLIENT Client,
12922     __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
12923     size_t *puiTextLength,
12924     LPVOID pTransitionContexts,
12925     size_t *puiTransitionContextCount,
12926     size_t uiSizeOfContext,
12927     DWORD Flags) 
12928 {
12929     HRESULT Status = E_FAIL;
12930
12931     PAL_TRY_NAKED
12932     {
12933         Status = ImplementEFNStackTrace(Client, wszTextOut, puiTextLength, 
12934             pTransitionContexts, puiTransitionContextCount,
12935             uiSizeOfContext, Flags);
12936     }
12937     PAL_EXCEPT_NAKED (EXCEPTION_EXECUTE_HANDLER)
12938     {
12939     }        
12940     PAL_ENDTRY_NAKED
12941
12942     return Status;
12943 }
12944
12945 // See sos_stacktrace.h for the contract with the callers regarding the LPVOID arguments.
12946 HRESULT CALLBACK _EFN_StackTrace(
12947     PDEBUG_CLIENT Client,
12948     __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
12949     size_t *puiTextLength,
12950     __out_bcount_opt(uiSizeOfContext*(*puiTransitionContextCount)) LPVOID pTransitionContexts,
12951     size_t *puiTransitionContextCount,
12952     size_t uiSizeOfContext,
12953     DWORD Flags) 
12954 {
12955     INIT_API();    
12956
12957     Status = ImplementEFNStackTraceTry(Client, wszTextOut, puiTextLength, 
12958         pTransitionContexts, puiTransitionContextCount,
12959         uiSizeOfContext, Flags);
12960
12961     return Status;
12962 }
12963 #endif // !FEATURE_PAL
12964
12965 BOOL FormatFromRemoteString(DWORD_PTR strObjPointer, __out_ecount(cchString) PWSTR wszBuffer, ULONG cchString)
12966 {
12967     BOOL bRet = FALSE;
12968
12969     wszBuffer[0] = L'\0';
12970     
12971     DacpObjectData objData;
12972     if (objData.Request(g_sos, TO_CDADDR(strObjPointer))!=S_OK)
12973     {
12974         return bRet;
12975     }
12976
12977     strobjInfo stInfo;
12978
12979     if (MOVE(stInfo, strObjPointer) != S_OK)
12980     {
12981         return bRet;
12982     }
12983     
12984     DWORD dwBufLength = 0;
12985     if (!ClrSafeInt<DWORD>::addition(stInfo.m_StringLength, 1, dwBufLength))
12986     {
12987         ExtOut("<integer overflow>\n");
12988         return bRet;
12989     }
12990
12991     LPWSTR pwszBuf = new NOTHROW WCHAR[dwBufLength];
12992     if (pwszBuf == NULL)
12993     {
12994         return bRet;
12995     }
12996     
12997     if (g_sos->GetObjectStringData(TO_CDADDR(strObjPointer), stInfo.m_StringLength+1, pwszBuf, NULL)!=S_OK)
12998     {
12999         delete [] pwszBuf;
13000         return bRet;
13001     }
13002
13003     // String is in format
13004     // <SP><SP><SP>at <function name>(args,...)\n
13005     // ...
13006     // Parse and copy just <function name>(args,...)
13007
13008     LPWSTR pwszPointer = pwszBuf;
13009
13010     WCHAR PSZSEP[] = L"   at ";
13011
13012     UINT Length = 0;
13013     while(1)
13014     {
13015         if (wcsncmp(pwszPointer, PSZSEP, _countof(PSZSEP)-1) != 0)
13016         {
13017             delete [] pwszBuf;
13018             return bRet;
13019         }
13020
13021         pwszPointer += wcslen(PSZSEP);
13022         LPWSTR nextPos = wcsstr(pwszPointer, PSZSEP);
13023         if (nextPos == NULL)
13024         {
13025             // Done! Note that we are leaving the function before we add the last
13026             // line of stack trace to the output string. This is on purpose because
13027             // this string needs to be merged with a real trace, and the last line
13028             // of the trace will be common to the real trace.
13029             break;
13030         }
13031         WCHAR c = *nextPos;
13032         *nextPos = L'\0';
13033
13034         // Buffer is calculated for sprintf below ("   %p %p %S\n");
13035         WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2];
13036
13037         // Note that we don't add a newline because we have this embedded in wszLineBuffer
13038         swprintf_s(wszLineBuffer, _countof(wszLineBuffer), L"    %p %p %s", (void*)(size_t)-1, (void*)(size_t)-1, pwszPointer);
13039         Length += (UINT)wcslen(wszLineBuffer);
13040         
13041         if (wszBuffer)
13042         {            
13043             wcsncat_s(wszBuffer, cchString, wszLineBuffer, _TRUNCATE);
13044         }
13045
13046         *nextPos = c;
13047         // Move to the next line.
13048         pwszPointer = nextPos;
13049     }
13050     
13051     delete [] pwszBuf; 
13052
13053     // Return TRUE only if the stack string had any information that was successfully parsed.
13054     // (Length > 0) is a good indicator of that.
13055     bRet = (Length > 0);
13056     return bRet;
13057 }
13058
13059 HRESULT AppendExceptionInfo(CLRDATA_ADDRESS cdaObj, 
13060     __out_ecount(cchString) PWSTR wszStackString,
13061     ULONG cchString,
13062     BOOL bNestedCase) // If bNestedCase is TRUE, the last frame of the computed stack is left off
13063 {    
13064     DacpObjectData objData;
13065     if (objData.Request(g_sos, cdaObj) != S_OK)
13066     {        
13067         return E_FAIL;
13068     }
13069
13070     // Make sure it is an exception object, and get the MT of Exception
13071     CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable);
13072     if (exceptionMT == NULL)
13073     {
13074         return E_INVALIDARG;
13075     }
13076
13077     // First try to get exception object data using ISOSDacInterface2
13078     DacpExceptionObjectData excData;
13079     BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, cdaObj));
13080
13081     int iOffset;    
13082     // Is there a _remoteStackTraceString? We'll want to prepend that data.
13083     // We only have string data, so IP/SP info has to be set to -1.
13084     DWORD_PTR strPointer;
13085     if (bGotExcData)
13086     {
13087         strPointer = TO_TADDR(excData.RemoteStackTraceString);
13088     }
13089     else
13090     {
13091         iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, L"_remoteStackTraceString");
13092         MOVE (strPointer, TO_TADDR(cdaObj) + iOffset);        
13093     }
13094     if (strPointer)
13095     {
13096         WCHAR *pwszBuffer = new NOTHROW WCHAR[cchString];
13097         if (pwszBuffer == NULL)
13098         {
13099             return E_OUTOFMEMORY;
13100         }
13101         
13102         if (FormatFromRemoteString(strPointer, pwszBuffer, cchString))
13103         {
13104             // Prepend this stuff to the string for the user
13105             wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE);
13106         }
13107         delete[] pwszBuffer;
13108     }
13109     
13110     BOOL bAsync = bGotExcData ? IsAsyncException(excData)
13111                               : IsAsyncException(TO_TADDR(cdaObj), TO_TADDR(objData.MethodTable));
13112
13113     DWORD_PTR arrayPtr;
13114     if (bGotExcData)
13115     {
13116         arrayPtr = TO_TADDR(excData.StackTrace);
13117     }
13118     else
13119     {
13120         iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, L"_stackTrace");
13121         MOVE (arrayPtr, TO_TADDR(cdaObj) + iOffset);
13122     }
13123
13124     if (arrayPtr)
13125     {
13126         DWORD arrayLen;
13127         MOVE (arrayLen, arrayPtr + sizeof(DWORD_PTR));
13128
13129         if (arrayLen)
13130         {
13131 #ifdef _TARGET_WIN64_
13132             DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD);
13133 #else
13134             DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD);
13135 #endif // _TARGET_WIN64_
13136             size_t stackTraceSize = 0;
13137             MOVE (stackTraceSize, dataPtr); // data length is stored at the beginning of the array in this case
13138
13139             DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement));
13140             dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data
13141             
13142             if (stackTraceSize != 0)
13143             {                
13144                 size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, bNestedCase);
13145                 WCHAR *pwszBuffer = new NOTHROW WCHAR[iLength + 1];
13146                 if (pwszBuffer)
13147                 {
13148                     FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer, iLength + 1, bAsync, bNestedCase);
13149                     wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE);
13150                     delete[] pwszBuffer;
13151                 }
13152                 else
13153                 {
13154                     return E_OUTOFMEMORY;
13155                 }
13156             }
13157         }
13158     }                   
13159     return S_OK;
13160 }
13161
13162 HRESULT ImplementEFNGetManagedExcepStack(
13163     CLRDATA_ADDRESS cdaStackObj, 
13164     __out_ecount(cchString) PWSTR wszStackString,
13165     ULONG cchString)
13166 {
13167     HRESULT Status = E_FAIL;
13168
13169     if (wszStackString == NULL || cchString == 0)
13170     {
13171         return E_INVALIDARG;
13172     }
13173
13174     CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
13175     DacpThreadData Thread;
13176     BOOL bCanUseThreadContext = TRUE;
13177
13178     ZeroMemory(&Thread, sizeof(DacpThreadData));
13179     
13180     if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
13181     {
13182         // The current thread is unmanaged
13183         bCanUseThreadContext = FALSE;
13184     }
13185
13186     if (cdaStackObj == NULL)    
13187     {
13188         if (!bCanUseThreadContext)
13189         {
13190             return E_INVALIDARG;
13191         }
13192         
13193         TADDR taLTOH = NULL;
13194         if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
13195                             &taLTOH,
13196                             sizeof(taLTOH), NULL)) || (taLTOH==NULL))
13197         {
13198             return Status;
13199         }    
13200         else
13201         {        
13202             cdaStackObj = TO_CDADDR(taLTOH);
13203         }
13204     }
13205
13206     // Put the stack trace header on
13207     AddExceptionHeader(wszStackString, cchString);
13208     
13209     // First is there a nested exception?
13210     if (bCanUseThreadContext && Thread.firstNestedException)    
13211     {
13212         CLRDATA_ADDRESS obj = 0, next = 0;
13213         CLRDATA_ADDRESS currentNested = Thread.firstNestedException;
13214         do
13215         {
13216             Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next);
13217
13218             // deal with the inability to read a nested exception gracefully
13219             if (Status != S_OK)
13220             {
13221                 break;
13222             }
13223                         
13224             Status = AppendExceptionInfo(obj, wszStackString, cchString, TRUE);
13225             currentNested = next;
13226         }
13227         while(currentNested != NULL);                        
13228     }
13229     
13230     Status = AppendExceptionInfo(cdaStackObj, wszStackString, cchString, FALSE);
13231
13232     return Status;
13233 }
13234
13235 #ifndef FEATURE_PAL
13236 // TODO: Enable this when ImplementEFNStackTraceTry is fixed.
13237 // This function, like VerifyDAC, exists for the purpose of testing
13238 // hard-to-get-to SOS APIs.
13239 DECLARE_API(VerifyStackTrace)
13240 {
13241     INIT_API();
13242
13243     BOOL bVerifyManagedExcepStack = FALSE;
13244     CMDOption option[] = 
13245     {   // name, vptr, type, hasValue
13246         {"-ManagedExcepStack", &bVerifyManagedExcepStack, COBOOL, FALSE},
13247     };
13248     
13249     if (!GetCMDOption(args, option, _countof(option), NULL,0,NULL)) 
13250     {
13251         return Status;
13252     }
13253
13254     if (bVerifyManagedExcepStack)
13255     {
13256         CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
13257         DacpThreadData Thread;
13258
13259         TADDR taExc = NULL;
13260         if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
13261         {
13262             ExtOut("The current thread is unmanaged\n");
13263             return Status;
13264         }
13265
13266         TADDR taLTOH = NULL;
13267         if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
13268                             &taLTOH,
13269                             sizeof(taLTOH), NULL)) || (taLTOH == NULL))
13270         {
13271             ExtOut("There is no current managed exception on this thread\n");            
13272             return Status;
13273         }    
13274         else
13275         {        
13276             taExc = taLTOH;
13277         }
13278
13279         const SIZE_T cchStr = 4096;
13280         WCHAR *wszStr = (WCHAR *)alloca(cchStr * sizeof(WCHAR));
13281         if (ImplementEFNGetManagedExcepStack(TO_CDADDR(taExc), wszStr, cchStr) != S_OK)
13282         {
13283             ExtOut("Error!\n");
13284             return Status;
13285         }
13286
13287         ExtOut("_EFN_GetManagedExcepStack(%P, wszStr, sizeof(wszStr)) returned:\n", (ULONG64) taExc);
13288         ExtOut("%S\n", wszStr);
13289
13290         if (ImplementEFNGetManagedExcepStack((ULONG64)NULL, wszStr, cchStr) != S_OK)
13291         {
13292             ExtOut("Error!\n");
13293             return Status;
13294         }
13295
13296         ExtOut("_EFN_GetManagedExcepStack(NULL, wszStr, sizeof(wszStr)) returned:\n");
13297         ExtOut("%S\n", wszStr);
13298     }
13299     else
13300     {
13301         size_t textLength = 0;
13302         size_t contextLength = 0;
13303         Status = ImplementEFNStackTraceTry(Client,
13304                                  NULL,
13305                                  &textLength,
13306                                  NULL,
13307                                  &contextLength,
13308                                  0,
13309                                  0);
13310
13311         if (Status != S_OK)
13312         {
13313             ExtOut("Error: %lx\n", Status);
13314             return Status;
13315         }
13316
13317         ExtOut("Number of characters requested: %d\n", textLength);
13318         WCHAR *wszBuffer = new NOTHROW WCHAR[textLength + 1];
13319         if (wszBuffer == NULL)
13320         {
13321             ReportOOM();
13322             return Status;
13323         }
13324
13325         // For the transition contexts buffer the callers are expected to allocate 
13326         // contextLength * sizeof(TARGET_CONTEXT), and not
13327         // contextLength * sizeof(CROSS_PLATFORM_CONTEXT). See sos_stacktrace.h for
13328         // details.
13329         LPBYTE pContexts = new NOTHROW BYTE[contextLength * g_targetMachine->GetContextSize()];
13330
13331         if (pContexts == NULL)
13332         {
13333             ReportOOM();
13334             delete[] wszBuffer;
13335             return Status;
13336         }
13337
13338         Status = ImplementEFNStackTrace(Client,
13339                                  wszBuffer,
13340                                  &textLength,
13341                                  pContexts,
13342                                  &contextLength,
13343                                  g_targetMachine->GetContextSize(),
13344                                  0);
13345
13346         if (Status != S_OK)
13347         {
13348             ExtOut("Error: %lx\n", Status);
13349             delete[] wszBuffer;
13350             delete [] pContexts;
13351             return Status;
13352         }
13353
13354         ExtOut("%S\n", wszBuffer);
13355
13356         ExtOut("Context information:\n");
13357         if (IsDbgTargetX86())
13358         {
13359             ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
13360                    "Ebp", "Esp", "Eip"); 
13361         }
13362         else if (IsDbgTargetAmd64())
13363         {
13364             ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
13365                    "Rbp", "Rsp", "Rip"); 
13366         }
13367         else if (IsDbgTargetArm())
13368         {
13369             ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
13370                    "FP", "SP", "PC"); 
13371         }
13372         else
13373         {
13374             ExtOut("Unsupported platform");
13375             delete [] pContexts;
13376             delete[] wszBuffer;
13377             return S_FALSE;
13378         }
13379
13380         for (size_t j=0; j < contextLength; j++)
13381         {
13382             CROSS_PLATFORM_CONTEXT *pCtx = (CROSS_PLATFORM_CONTEXT*)(pContexts + j*g_targetMachine->GetContextSize());
13383             ExtOut("%p %p %p\n", GetBP(*pCtx), GetSP(*pCtx), GetIP(*pCtx));
13384         }
13385
13386         delete [] pContexts;
13387
13388         StackTrace_SimpleContext *pSimple = new NOTHROW StackTrace_SimpleContext[contextLength];
13389         if (pSimple == NULL)
13390         {
13391             ReportOOM();
13392             delete[] wszBuffer;
13393             return Status;
13394         }
13395
13396         Status = ImplementEFNStackTrace(Client,
13397                                  wszBuffer,
13398                                  &textLength,
13399                                  pSimple,
13400                                  &contextLength,
13401                                  sizeof(StackTrace_SimpleContext),
13402                                  0);
13403
13404         if (Status != S_OK)
13405         {
13406             ExtOut("Error: %lx\n", Status);
13407             delete[] wszBuffer;
13408             delete [] pSimple;
13409             return Status;
13410         }
13411
13412         ExtOut("Simple Context information:\n");
13413         if (IsDbgTargetX86())
13414             ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
13415                        "Ebp", "Esp", "Eip"); 
13416         else if (IsDbgTargetAmd64())
13417                 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
13418                        "Rbp", "Rsp", "Rip"); 
13419         else if (IsDbgTargetArm())
13420                 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
13421                        "FP", "SP", "PC"); 
13422         else 
13423         {
13424             ExtOut("Unsupported platform");
13425             delete[] wszBuffer;
13426             delete [] pSimple;
13427             return S_FALSE;
13428         }
13429         for (size_t j=0; j < contextLength; j++)
13430         {
13431             ExtOut("%p %p %p\n", (ULONG64) pSimple[j].FrameOffset,
13432                     (ULONG64) pSimple[j].StackOffset,
13433                     (ULONG64) pSimple[j].InstructionOffset);
13434         }
13435         delete [] pSimple;
13436         delete[] wszBuffer;
13437     }
13438
13439     return Status;
13440 }
13441 #endif // !FEATURE_PAL
13442
13443
13444
13445 // This is an internal-only Apollo extension to de-optimize the code
13446 DECLARE_API(SuppressJitOptimization)
13447 {
13448     INIT_API_NOEE();    
13449     MINIDUMP_NOT_SUPPORTED();    
13450
13451     StringHolder onOff;
13452     CMDValue arg[] = 
13453     {   // vptr, type
13454         {&onOff.data, COSTRING},
13455     };
13456     size_t nArg;
13457     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) 
13458     {
13459         return E_FAIL;
13460     }
13461
13462     if(nArg == 1 && (_stricmp(onOff.data, "On") == 0))
13463     {
13464         // if CLR is already loaded, try to change the flags now
13465         if(CheckEEDll() == S_OK)
13466         {
13467             SetNGENCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION);
13468         }
13469
13470         if(!g_fAllowJitOptimization)
13471             ExtOut("JIT optimization is already suppressed\n");
13472         else
13473         {
13474             g_fAllowJitOptimization = FALSE;
13475             g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0);
13476             ExtOut("JIT optimization will be suppressed\n");
13477         }
13478
13479
13480     }
13481     else if(nArg == 1 && (_stricmp(onOff.data, "Off") == 0))
13482     {
13483         // if CLR is already loaded, try to change the flags now
13484         if(CheckEEDll() == S_OK)
13485         {
13486             SetNGENCompilerFlags(CORDEBUG_JIT_DEFAULT);
13487         }
13488
13489         if(g_fAllowJitOptimization)
13490             ExtOut("JIT optimization is already permitted\n");
13491         else
13492         {
13493             g_fAllowJitOptimization = TRUE;
13494             ExtOut("JIT optimization will be permitted\n");
13495         }
13496     }
13497     else
13498     {
13499         ExtOut("Usage: !SuppressJitOptimization <on|off>\n");
13500     }
13501
13502     return S_OK;
13503 }
13504
13505 // Uses ICorDebug to set the state of desired NGEN compiler flags. This can suppress pre-jitted optimized
13506 // code
13507 HRESULT SetNGENCompilerFlags(DWORD flags)
13508 {
13509     HRESULT hr;
13510
13511     ToRelease<ICorDebugProcess2> proc2;
13512     if(FAILED(hr = InitCorDebugInterface()))
13513     {
13514         ExtOut("SOS: warning, prejitted code optimizations could not be changed. Failed to load ICorDebug HR = 0x%x\n", hr);
13515     }
13516     else if(FAILED(g_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess2), (void**) &proc2)))
13517     {
13518         if(flags != CORDEBUG_JIT_DEFAULT)
13519         {
13520             ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n");
13521         }
13522         else
13523         {
13524             hr = S_OK;
13525         }
13526     }
13527     else if(FAILED(hr = proc2->SetDesiredNGENCompilerFlags(flags)))
13528     {
13529         // Versions of CLR that don't have SetDesiredNGENCompilerFlags DAC-ized will return E_FAIL.
13530         // This was first supported in the clr_triton branch around 4/1/12, Apollo release
13531         // It will likely be supported in desktop CLR during Dev12
13532         if(hr == E_FAIL)
13533         {
13534             if(flags != CORDEBUG_JIT_DEFAULT)
13535             {
13536                 ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n");
13537             }
13538             else
13539             {
13540                 hr = S_OK;
13541             }
13542         }
13543         else if(hr == CORDBG_E_NGEN_NOT_SUPPORTED)
13544         {
13545             if(flags != CORDEBUG_JIT_DEFAULT)
13546             {
13547                 ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support NGEN\n");
13548             }
13549             else
13550             {
13551                 hr = S_OK;
13552             }
13553         }
13554         else if(hr == CORDBG_E_MUST_BE_IN_CREATE_PROCESS)
13555         {
13556             DWORD currentFlags = 0;
13557             if(FAILED(hr = proc2->GetDesiredNGENCompilerFlags(&currentFlags)))
13558             {
13559                 ExtOut("SOS: warning, prejitted code optimizations could not be changed. GetDesiredNGENCompilerFlags failed hr=0x%x\n", hr);
13560             }
13561             else if(currentFlags != flags)
13562             {
13563                 ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. This setting is fixed once CLR starts\n");
13564             }
13565             else
13566             {
13567                 hr = S_OK;
13568             }
13569         }
13570         else
13571         {
13572             ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. SetDesiredNGENCompilerFlags hr = 0x%x\n", hr);
13573         }
13574     }
13575
13576     return hr;
13577 }
13578
13579
13580
13581 // This is an internal-only Apollo extension to save breakpoint/watch state
13582 #ifndef FEATURE_PAL
13583 DECLARE_API(SaveState)
13584 {
13585     INIT_API_NOEE();    
13586     MINIDUMP_NOT_SUPPORTED();    
13587
13588     StringHolder filePath;
13589     CMDValue arg[] = 
13590     {   // vptr, type
13591         {&filePath.data, COSTRING},
13592     };
13593     size_t nArg;
13594     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) 
13595     {
13596         return E_FAIL;
13597     }
13598
13599     if(nArg == 0)
13600     {
13601         ExtOut("Usage: !SaveState <file_path>\n");
13602     }
13603
13604     FILE* pFile;
13605     errno_t error = fopen_s(&pFile, filePath.data, "w");
13606     if(error != 0)
13607     {
13608         ExtOut("Failed to open file %s, error=0x%x\n", filePath.data, error);
13609         return E_FAIL;
13610     }
13611
13612     g_bpoints.SaveBreakpoints(pFile);
13613     g_watchCmd.SaveListToFile(pFile);
13614
13615     fclose(pFile);
13616     ExtOut("Session breakpoints and watch expressions saved to %s\n", filePath.data);
13617     return S_OK;
13618 }
13619 #endif
13620
13621
13622 DECLARE_API(StopOnCatch)
13623 {
13624     INIT_API();    
13625     MINIDUMP_NOT_SUPPORTED();    
13626
13627     CHAR buffer[100];
13628     sprintf_s(buffer, _countof(buffer), "sxe -c \"!HandleCLRN\" clrn");
13629     g_stopOnNextCatch = TRUE;
13630     ULONG32 flags = 0;
13631     g_clrData->GetOtherNotificationFlags(&flags);
13632     flags |= CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER;
13633     g_clrData->SetOtherNotificationFlags(flags);
13634     ExtOut("Debuggee will break the next time a managed exception is caught during execution\n");
13635     return S_OK;
13636 }
13637
13638
13639 // This is an undocumented SOS extension command intended to help test SOS
13640 // It causes the Dml output to be printed to the console uninterpretted so
13641 // that a test script can read the commands which are hidden in the markup
13642 DECLARE_API(ExposeDML)
13643 {
13644     Output::SetDMLExposed(true);
13645     return S_OK;
13646 }
13647
13648 // According to kksharma the Windows debuggers always sign-extend
13649 // arguments when calling externally, therefore StackObjAddr 
13650 // conforms to CLRDATA_ADDRESS contract.
13651 HRESULT CALLBACK 
13652 _EFN_GetManagedExcepStack(
13653     PDEBUG_CLIENT Client,
13654     ULONG64 StackObjAddr,
13655    __out_ecount (cbString) PSTR szStackString,
13656     ULONG cbString
13657     )
13658 {
13659     INIT_API();
13660
13661     ArrayHolder<WCHAR> tmpStr = new NOTHROW WCHAR[cbString];
13662     if (tmpStr == NULL)
13663     {
13664         ReportOOM();
13665         return E_OUTOFMEMORY;
13666     }
13667
13668     if (FAILED(Status = ImplementEFNGetManagedExcepStack(StackObjAddr, tmpStr, cbString)))
13669     {
13670         return Status;
13671     }
13672
13673     if (WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmpStr, -1, szStackString, cbString, NULL, NULL) == 0)
13674     {
13675         return E_FAIL;
13676     }
13677
13678     return S_OK;
13679 }
13680
13681 // same as _EFN_GetManagedExcepStack, but returns the stack as a wide string.
13682 HRESULT CALLBACK
13683 _EFN_GetManagedExcepStackW(
13684     PDEBUG_CLIENT Client,
13685     ULONG64 StackObjAddr,
13686     __out_ecount(cchString) PWSTR wszStackString,
13687     ULONG cchString
13688     )
13689 {
13690     INIT_API();
13691
13692     return ImplementEFNGetManagedExcepStack(StackObjAddr, wszStackString, cchString);
13693 }
13694     
13695 // According to kksharma the Windows debuggers always sign-extend
13696 // arguments when calling externally, therefore objAddr 
13697 // conforms to CLRDATA_ADDRESS contract.
13698 HRESULT CALLBACK 
13699 _EFN_GetManagedObjectName(
13700     PDEBUG_CLIENT Client,
13701     ULONG64 objAddr,
13702     __out_ecount (cbName) PSTR szName,
13703     ULONG cbName
13704     )
13705 {
13706     INIT_API ();
13707
13708     if (!sos::IsObject(objAddr, false))
13709     {
13710         return E_INVALIDARG;
13711     }
13712
13713     sos::Object obj = TO_TADDR(objAddr);
13714
13715     if (WideCharToMultiByte(CP_ACP, 0, obj.GetTypeName(), (int) (wcslen(obj.GetTypeName()) + 1),
13716                             szName, cbName, NULL, NULL) == 0)
13717     {
13718         return E_FAIL;
13719     }
13720     return S_OK;
13721 }
13722
13723 // According to kksharma the Windows debuggers always sign-extend
13724 // arguments when calling externally, therefore objAddr 
13725 // conforms to CLRDATA_ADDRESS contract.
13726 HRESULT CALLBACK 
13727 _EFN_GetManagedObjectFieldInfo(
13728     PDEBUG_CLIENT Client,
13729     ULONG64 objAddr,
13730     __out_ecount (mdNameLen) PSTR szFieldName,
13731     PULONG64 pValue,
13732     PULONG pOffset
13733     )
13734 {
13735     INIT_API();
13736     DacpObjectData objData;
13737     LPWSTR fieldName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
13738     
13739     if (szFieldName == NULL || *szFieldName == '\0' ||
13740         objAddr == NULL)
13741     {
13742         return E_FAIL;
13743     }
13744
13745     if (pOffset == NULL && pValue == NULL)
13746     {
13747         // One of these needs to be valid
13748         return E_FAIL;
13749     }
13750         
13751     if (FAILED(objData.Request(g_sos, objAddr)))
13752     {        
13753         return E_FAIL;
13754     }
13755     
13756     MultiByteToWideChar(CP_ACP,0,szFieldName,-1,fieldName,mdNameLen);
13757
13758     int iOffset = GetObjFieldOffset (objAddr, objData.MethodTable, fieldName);
13759     if (iOffset <= 0)
13760     {
13761         return E_FAIL;
13762     }
13763
13764     if (pOffset)
13765     {
13766         *pOffset = (ULONG) iOffset;
13767     }
13768
13769     if (pValue)
13770     {
13771         if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(objAddr + iOffset), pValue, sizeof(ULONG64), NULL)))
13772         {
13773             return E_FAIL;
13774         }
13775     }
13776
13777     return S_OK;
13778 }
13779
13780 void PrintHelp (__in_z LPCSTR pszCmdName)
13781 {
13782     static LPSTR pText = NULL;
13783
13784     if (pText == NULL) {
13785 #ifndef FEATURE_PAL
13786         HGLOBAL hResource = NULL;
13787         HRSRC hResInfo = FindResource (g_hInstance, TEXT ("DOCUMENTATION"), TEXT ("TEXT"));
13788         if (hResInfo) hResource = LoadResource (g_hInstance, hResInfo);
13789         if (hResource) pText = (LPSTR) LockResource (hResource); 
13790         if (pText == NULL)
13791         {
13792             ExtOut("Error loading documentation resource\n");
13793             return;
13794         }
13795 #else
13796 #define SOS_DOCUMENT_FILENAME "sosdocs.txt"
13797
13798         char  lpFilename[MAX_PATH+12]; // + 12 to make enough room for strcat function.
13799         DWORD nReturnedSize;
13800         nReturnedSize = GetModuleFileName(g_hInstance, lpFilename, MAX_PATH);
13801         if ( nReturnedSize == 0 || nReturnedSize == MAX_PATH ) {
13802             // We consider both of these cases as failed.
13803             ExtOut("Error getting the name for the current module\n");
13804             return;
13805         }
13806
13807         // Find the last "\" or "/" in the path.
13808         char * pChar = lpFilename + strlen(lpFilename) - 1;
13809         while ( pChar != lpFilename-1 && * pChar != '\\' && * pChar != '/' ) { * pChar-- = 0; }
13810         strcat(lpFilename, SOS_DOCUMENT_FILENAME);
13811         
13812         HANDLE hSosDocFile = CreateFileA(lpFilename,
13813                                  GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
13814         if (hSosDocFile == INVALID_HANDLE_VALUE) {
13815             ExtOut("Error finding documentation file\n");
13816             return;
13817         }
13818
13819         HANDLE hMappedSosDocFile = CreateFileMappingA(hSosDocFile,
13820                                        NULL, PAGE_READONLY, 0, 0, NULL);
13821         CloseHandle(hSosDocFile);
13822         if (hMappedSosDocFile == NULL) { 
13823             ExtOut("Error mapping documentation file\n");
13824             return;
13825         }
13826
13827         pText = (LPSTR)MapViewOfFile(hMappedSosDocFile,
13828                            FILE_MAP_READ, 0, 0, 0);
13829         CloseHandle(hMappedSosDocFile);
13830         if (pText == NULL)
13831         {
13832             ExtOut("Error loading documentation file\n");
13833             return;
13834         }
13835 #endif
13836     }
13837
13838     // Find our line in the text file
13839     char searchString[MAX_PATH];
13840     sprintf_s(searchString, _countof(searchString), "COMMAND: %s.", pszCmdName);
13841     
13842     LPSTR pStart = strstr(pText, searchString);
13843     LPSTR pEnd = NULL;
13844     if (!pStart)
13845     {
13846         ExtOut("Documentation for %s not found.\n", pszCmdName);
13847         return;
13848     }
13849
13850     // Go to the end of this line:
13851     pStart = strchr(pStart, '\n');
13852     if (!pStart)
13853     {
13854         ExtOut("Expected newline in documentation resource.\n");
13855         return;
13856     }
13857
13858     // Bypass the newline that pStart points to and setup pEnd for the loop below. We set
13859     // pEnd to be the old pStart since we add one to it when we call strstr.
13860     pEnd = pStart++;
13861
13862     // Find the first occurrence of \\ followed by an \r or an \n on a line by itself.
13863     do
13864     {
13865         pEnd = strstr(pEnd+1, "\\\\");
13866     } while (pEnd && ((pEnd[-1] != '\r' && pEnd[-1] != '\n') || (pEnd[3] != '\r' && pEnd[3] != '\n')));
13867
13868     if (pEnd)
13869     {
13870         // We have found a \\ followed by a \r or \n.  Do not print out the character pEnd points
13871         // to, as this will be the first \ (this is why we don't add one to the second parameter).
13872         ExtOut("%.*s", pEnd - pStart, pStart);
13873     }
13874     else
13875     {
13876         // If pEnd is false then we have run to the end of the document.  However, we did find
13877         // the command to print, so we should simply print to the end of the file.  We'll add
13878         // an extra newline here in case the file does not contain one.
13879         ExtOut("%s\n", pStart);
13880     }
13881 }
13882
13883 /**********************************************************************\
13884 * Routine Description:                                                 *
13885 *                                                                      *
13886 *    This function displays the commands available in strike and the   *  
13887 *    arguments passed into each.
13888 *                                                                      *
13889 \**********************************************************************/
13890 extern "C" HRESULT CALLBACK
13891 Help(PDEBUG_CLIENT Client, PCSTR Args)
13892 {
13893     // Call extension initialization functions directly, because we don't need the DAC dll to be initialized to get help.
13894     HRESULT Status;
13895     __ExtensionCleanUp __extensionCleanUp;
13896     if ((Status = ExtQuery(Client)) != S_OK) return Status;
13897     ControlC = FALSE;
13898
13899     StringHolder commandName;
13900     CMDValue arg[] = 
13901     {
13902         {&commandName.data, COSTRING}
13903     };
13904     size_t nArg;
13905     if (!GetCMDOption(Args, NULL, 0, arg, _countof(arg), &nArg))
13906     {
13907         return Status;
13908     }
13909
13910     ExtOut("-------------------------------------------------------------------------------\n");
13911
13912     if (nArg == 1)
13913     {        
13914         // Convert commandName to lower-case
13915         LPSTR curChar = commandName.data;
13916         while (*curChar != '\0')
13917         {
13918             if ( ((unsigned) *curChar <= 0x7F) && isupper(*curChar))
13919             {
13920                 *curChar = (CHAR) tolower(*curChar);
13921             }
13922             curChar++;
13923         }
13924
13925         // Strip off leading "!" if the user put that.
13926         curChar = commandName.data;
13927         if (*curChar == '!')
13928             curChar++;
13929         
13930         PrintHelp (curChar);
13931     }
13932     else
13933     {
13934         PrintHelp ("contents");
13935     }
13936     
13937     return S_OK;
13938 }
13939
13940 #endif // !FEATURE_PAL