Implement out of context stack unwinder (#13302)
[platform/upstream/coreclr.git] / src / ToolBox / SOS / Strike / strike.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4
5 // ==++==
6 // 
7  
8 // 
9 // ==--==
10
11 // ===========================================================================
12 // STRIKE.CPP
13 // ===========================================================================
14 //
15 // History:
16 //   09/07/99  Microsoft  Created
17 //
18 //************************************************************************************************
19 // SOS is the native debugging extension designed to support investigations into CLR (mis-)
20 // behavior by both users of the runtime as well as the code owners. It allows inspection of 
21 // internal structures, of user visible entities, as well as execution control.
22 // 
23 // This is the main SOS file hosting the implementation of all the exposed commands. A good 
24 // starting point for understanding the semantics of these commands is the sosdocs.txt file.
25 // 
26 // #CrossPlatformSOS
27 // SOS currently supports cross platform debugging from x86 to ARM. It takes a different approach 
28 // from the DAC: whereas for the DAC we produce one binary for each supported host-target 
29 // architecture pair, for SOS we produce only one binary for each host architecture; this one 
30 // binary contains code for all supported target architectures. In doing this SOS depends on two
31 // assumptions:
32 //   . that the debugger will load the appropriate DAC, and 
33 //   . that the host and target word size is identical.
34 // The second assumption is identical to the DAC assumption, and there will be considerable effort
35 // required (in the EE, the DAC, and SOS) if we ever need to remove it.
36 // 
37 // In an ideal world SOS would be able to retrieve all platform specific information it needs 
38 // either from the debugger or from DAC. However, SOS has taken some subtle and not so subtle
39 // dependencies on the CLR and the target platform.
40 // To resolve this problem, SOS now abstracts the target behind the IMachine interface, and uses 
41 // calls on IMachine to take target-specific actions. It implements X86Machine, ARMMachine, and 
42 // AMD64Machine. An instance of these exists in each appropriate host (e.g. the X86 version of SOS
43 // contains instaces of X86Machine and ARMMachine, the ARM version contains an instance of 
44 // ARMMachine, and the AMD64 version contains an instance of AMD64Machine). The code included in 
45 // each version if determined by the SosTarget*** MSBuild symbols, and SOS_TARGET_*** conditional 
46 // compilation symbols (as specified in sos.targets).
47 // 
48 // Most of the target specific code is hosted in disasm.h/.cpp, and disasmX86.cpp, disasmARM.cpp.
49 // Some code currently under _TARGET_*** ifdefs may need to be reviewed/revisited.
50 // 
51 // Issues:
52 // The one-binary-per-host decision does have some drawbacks: 
53 //   . Currently including system headers or even CLR headers will only account for the host 
54 //     target, IOW, when building the X86 version of SOS, CONTEXT will refer to the X86 CONTEXT 
55 //     structure, so we need to be careful when debugging ARM targets. The CONTEXT issue is 
56 //     partially resolved by CROSS_PLATFORM_CONTEXT (there is still a need to be very careful 
57 //     when handling arrays of CONTEXTs - see _EFN_StackTrace for details on this).
58 //   . For larger includes (e.g. GC info), we will need to include files in specific namespaces, 
59 //     with specific _TARGET_*** macros defined in order to avoid name clashes and ensure correct
60 //     system types are used.
61 // -----------------------------------------------------------------------------------------------
62
63 #define DO_NOT_DISABLE_RAND //this is a standalone tool, and can use rand()
64
65 #include <windows.h>
66 #include <winver.h>
67 #include <winternl.h>
68 #include <psapi.h>
69 #ifndef FEATURE_PAL
70 #include <list>   
71 #endif // !FEATURE_PAL
72 #include <wchar.h>
73
74 #include "platformspecific.h"
75
76 #define NOEXTAPI
77 #define KDEXT_64BIT
78 #include <wdbgexts.h>
79 #undef DECLARE_API
80 #undef StackTrace
81
82 #include <dbghelp.h>
83
84 #include <stdio.h>
85 #include <stdlib.h>
86 #include <string.h>
87 #include <stddef.h>
88
89 #include "strike.h"
90 #include "sos.h"
91
92 #ifndef STRESS_LOG
93 #define STRESS_LOG
94 #endif // STRESS_LOG
95 #define STRESS_LOG_READONLY
96 #include "stresslog.h"
97
98 #include "util.h"
99
100 #include "corhdr.h"
101 #include "cor.h"
102 #include "cordebug.h"
103 #include "dacprivate.h"
104 #include "corexcep.h"
105
106 #define  CORHANDLE_MASK 0x1
107 #define SWITCHED_OUT_FIBER_OSID 0xbaadf00d;
108
109 #define DEFINE_EXT_GLOBALS
110
111 #include "data.h"
112 #include "disasm.h"
113
114 #include "predeftlsslot.h"
115
116 #include "hillclimbing.h"
117
118 #include "sos_md.h"
119
120 #ifndef FEATURE_PAL
121
122 #include "ExpressionNode.h"
123 #include "WatchCmd.h"
124
125 #include <set>
126 #include <algorithm>
127 #include <vector>
128
129 #include "tls.h"
130
131 typedef struct _VM_COUNTERS {
132     SIZE_T PeakVirtualSize;
133     SIZE_T VirtualSize;
134     ULONG PageFaultCount;
135     SIZE_T PeakWorkingSetSize;
136     SIZE_T WorkingSetSize;
137     SIZE_T QuotaPeakPagedPoolUsage;
138     SIZE_T QuotaPagedPoolUsage;
139     SIZE_T QuotaPeakNonPagedPoolUsage;
140     SIZE_T QuotaNonPagedPoolUsage;
141     SIZE_T PagefileUsage;
142     SIZE_T PeakPagefileUsage;
143 } VM_COUNTERS;
144 typedef VM_COUNTERS *PVM_COUNTERS;
145
146 const PROCESSINFOCLASS ProcessVmCounters = static_cast<PROCESSINFOCLASS>(3);
147
148 #endif // !FEATURE_PAL
149
150 BOOL CallStatus;
151 BOOL ControlC = FALSE;
152
153 IMetaDataDispenserEx *pDisp = NULL;
154 WCHAR g_mdName[mdNameLen];
155
156 #ifndef FEATURE_PAL
157 HMODULE g_hInstance = NULL;
158 #include <vector>
159 #include <algorithm>
160 #endif // !FEATURE_PAL
161
162 #ifdef _MSC_VER
163 #pragma warning(disable:4244)   // conversion from 'unsigned int' to 'unsigned short', possible loss of data
164 #pragma warning(disable:4189)   // local variable is initialized but not referenced
165 #endif
166
167 #ifdef FEATURE_PAL
168 #define SOSPrefix ""
169 #else
170 #define SOSPrefix "!"
171 #endif
172
173 #if defined _X86_ && !defined FEATURE_PAL
174 // disable FPO for X86 builds
175 #pragma optimize("y", off)
176 #endif
177
178 #undef assert
179
180 #ifdef _MSC_VER
181 #pragma warning(default:4244)
182 #pragma warning(default:4189)
183 #endif
184
185 #ifndef FEATURE_PAL
186 #include "ntinfo.h"
187 #endif // FEATURE_PAL
188
189 #ifndef IfFailRet
190 #define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0)
191 #endif
192
193 #ifdef FEATURE_PAL
194
195 #define NOTHROW
196 #define MINIDUMP_NOT_SUPPORTED()
197
198 #else // !FEATURE_PAL
199
200 #define MINIDUMP_NOT_SUPPORTED()   \
201     if (IsMiniDumpFile())      \
202     {                          \
203         ExtOut("This command is not supported in a minidump without full memory\n"); \
204         ExtOut("To try the command anyway, run !MinidumpMode 0\n"); \
205         return Status;         \
206     }
207
208 #define NOTHROW (std::nothrow)
209
210 #include "safemath.h"
211
212 DECLARE_API (MinidumpMode)
213 {
214     INIT_API ();
215     DWORD_PTR Value=0;
216
217     CMDValue arg[] = 
218     {   // vptr, type
219         {&Value, COHEX}
220     };
221
222     size_t nArg;
223     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) 
224     {
225         return Status;
226     }    
227     if (nArg == 0)
228     {
229         // Print status of current mode
230        ExtOut("Current mode: %s - unsafe minidump commands are %s.\n",
231                g_InMinidumpSafeMode ? "1" : "0",
232                g_InMinidumpSafeMode ? "disabled" : "enabled");
233     }
234     else
235     {
236         if (Value != 0 && Value != 1)
237         {
238             ExtOut("Mode must be 0 or 1\n");
239             return Status;
240         }
241
242         g_InMinidumpSafeMode = (BOOL) Value;
243         ExtOut("Unsafe minidump commands are %s.\n",
244                 g_InMinidumpSafeMode ? "disabled" : "enabled");
245     }
246
247     return Status;
248 }
249
250 #endif // FEATURE_PAL
251
252 /**********************************************************************\
253 * Routine Description:                                                 *
254 *                                                                      *
255 *    This function is called to get the MethodDesc for a given eip     *  
256 *                                                                      *
257 \**********************************************************************/
258 DECLARE_API(IP2MD)
259 {
260     INIT_API();
261     MINIDUMP_NOT_SUPPORTED();
262
263     BOOL dml = FALSE;
264     TADDR IP = 0;
265     CMDOption option[] = 
266     {   // name, vptr, type, hasValue
267 #ifndef FEATURE_PAL
268         {"/d", &dml, COBOOL, FALSE},
269 #endif
270     };
271     CMDValue arg[] = 
272     {   // vptr, type
273         {&IP, COHEX},
274     };
275     size_t nArg;
276     
277     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
278     {
279         return Status;
280     }
281     EnableDMLHolder dmlHolder(dml);
282
283     if (IP == 0)
284     {
285         ExtOut("%s is not IP\n", args);
286         return Status;
287     }
288
289     CLRDATA_ADDRESS cdaStart = TO_CDADDR(IP);
290     CLRDATA_ADDRESS pMD;
291
292     
293     if ((Status = g_sos->GetMethodDescPtrFromIP(cdaStart, &pMD)) != S_OK)
294     {
295         ExtOut("Failed to request MethodData, not in JIT code range\n");
296         return Status;
297     }
298
299     DMLOut("MethodDesc:   %s\n", DMLMethodDesc(pMD));
300     DumpMDInfo(TO_TADDR(pMD), cdaStart, FALSE /* fStackTraceFormat */);
301
302     WCHAR filename[MAX_LONGPATH];
303     ULONG linenum;
304     // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
305     ULONG symlines = 0;
306     if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
307     {
308         symlines &= SYMOPT_LOAD_LINES;
309     }
310
311     if (symlines != 0 && 
312         SUCCEEDED(GetLineByOffset(TO_CDADDR(IP), &linenum, filename, _countof(filename))))
313     {
314         ExtOut("Source file:  %S @ %d\n", filename, linenum);
315     }
316
317     return Status;
318 }
319
320 // (MAX_STACK_FRAMES is also used by x86 to prevent infinite loops in _EFN_StackTrace)
321 #define MAX_STACK_FRAMES 1000
322
323 #ifdef _TARGET_WIN64_
324
325 // I use a global set of frames for stack walking on win64 because the debugger's
326 // GetStackTrace function doesn't provide a way to find out the total size of a stackwalk,
327 // and I'd like to have a reasonably big maximum without overflowing the stack by declaring
328 // the buffer locally and I also want to get a managed trace in a low memory environment
329 // (so no dynamic allocation if possible).
330 DEBUG_STACK_FRAME g_Frames[MAX_STACK_FRAMES];
331 AMD64_CONTEXT g_X64FrameContexts[MAX_STACK_FRAMES];
332
333 static HRESULT
334 GetContextStackTrace(ULONG osThreadId, PULONG pnumFrames)
335 {
336     PDEBUG_CONTROL4 debugControl4;
337     HRESULT hr;
338
339     // Do we have advanced capability?
340     if ((hr = g_ExtControl->QueryInterface(__uuidof(IDebugControl4), (void **)&debugControl4)) == S_OK)
341     {
342         ULONG oldId, id;
343         g_ExtSystem->GetCurrentThreadId(&oldId);
344
345         if ((hr = g_ExtSystem->GetThreadIdBySystemId(osThreadId, &id)) != S_OK) {
346             return hr;
347         }
348         g_ExtSystem->SetCurrentThreadId(id);
349
350         // GetContextStackTrace fills g_X64FrameContexts as an array of 
351         // contexts packed as target architecture contexts. We cannot 
352         // safely cast this as an array of CROSS_PLATFORM_CONTEXT, since 
353         // sizeof(CROSS_PLATFORM_CONTEXT) != sizeof(TGT_CONTEXT)
354         hr = debugControl4->GetContextStackTrace(
355             NULL,
356             0,
357             g_Frames,
358             MAX_STACK_FRAMES,
359             g_X64FrameContexts,
360             MAX_STACK_FRAMES*g_targetMachine->GetContextSize(),
361             g_targetMachine->GetContextSize(),
362             pnumFrames);
363
364         g_ExtSystem->SetCurrentThreadId(oldId);
365         debugControl4->Release();
366     }
367     return hr;
368 }
369
370 #endif // _TARGET_WIN64_
371
372 /**********************************************************************\
373 * Routine Description:                                                 *
374 *                                                                      *
375 *    This function displays the stack trace.  It looks at each DWORD   *  
376 *    on stack.  If the DWORD is a return address, the symbol name or
377 *    managed function name is displayed.                               *
378 *                                                                      *
379 \**********************************************************************/
380 void DumpStackInternal(DumpStackFlag *pDSFlag)
381 {    
382     ReloadSymbolWithLineInfo();
383     
384     ULONG64 StackOffset;
385     g_ExtRegisters->GetStackOffset (&StackOffset);
386     if (pDSFlag->top == 0) {
387         pDSFlag->top = TO_TADDR(StackOffset);
388     }
389     size_t value;
390     while (g_ExtData->ReadVirtual(TO_CDADDR(pDSFlag->top), &value, sizeof(size_t), NULL) != S_OK) {
391         if (IsInterrupt())
392             return;
393         pDSFlag->top = NextOSPageAddress(pDSFlag->top);
394     }
395
396 #ifndef FEATURE_PAL     
397     if (pDSFlag->end == 0) {
398         // Find the current stack range
399         NT_TIB teb;
400         ULONG64 dwTebAddr=0;
401
402         g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
403         if (SafeReadMemory(TO_TADDR(dwTebAddr), &teb, sizeof(NT_TIB), NULL))
404         {
405             if (pDSFlag->top > TO_TADDR(teb.StackLimit)
406             && pDSFlag->top <= TO_TADDR(teb.StackBase))
407             {
408                 if (pDSFlag->end == 0 || pDSFlag->end > TO_TADDR(teb.StackBase))
409                     pDSFlag->end = TO_TADDR(teb.StackBase);
410             }
411         }
412     }
413 #endif // FEATURE_PAL
414     
415     if (pDSFlag->end == 0)
416     {
417         ExtOut("TEB information is not available so a stack size of 0xFFFF is assumed\n");
418         pDSFlag->end = pDSFlag->top + 0xFFFF;
419     }
420     
421     if (pDSFlag->end < pDSFlag->top)
422     {
423         ExtOut("Wrong option: stack selection wrong\n");
424         return;
425     }
426
427     DumpStackWorker(*pDSFlag);
428 }
429
430 #if defined(FEATURE_PAL) && defined(_TARGET_WIN64_)
431 static BOOL UnwindStackFrames(ULONG32 osThreadId);
432 #endif
433
434 DECLARE_API(DumpStack)
435 {
436     INIT_API_NO_RET_ON_FAILURE();
437
438     MINIDUMP_NOT_SUPPORTED();
439
440     DumpStackFlag DSFlag;
441     DSFlag.fEEonly = FALSE;
442     DSFlag.fSuppressSrcInfo = FALSE;
443     DSFlag.top = 0;
444     DSFlag.end = 0;
445
446     BOOL unwind = FALSE;
447     BOOL dml = FALSE;
448     CMDOption option[] = {
449         // name, vptr, type, hasValue
450         {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
451         {"-n",  &DSFlag.fSuppressSrcInfo, COBOOL, FALSE},
452         {"-unwind",  &unwind, COBOOL, FALSE},
453 #ifndef FEATURE_PAL
454         {"/d", &dml, COBOOL, FALSE}
455 #endif
456     };
457     CMDValue arg[] = {
458         // vptr, type
459         {&DSFlag.top, COHEX},
460         {&DSFlag.end, COHEX}
461     };
462     size_t nArg;
463     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
464         return Status;
465
466     // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
467     ULONG symlines = 0;
468     if (!DSFlag.fSuppressSrcInfo && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
469     {
470         symlines &= SYMOPT_LOAD_LINES;
471     }
472     DSFlag.fSuppressSrcInfo = DSFlag.fSuppressSrcInfo || (symlines == 0);
473
474     EnableDMLHolder enabledml(dml);
475
476     ULONG sysId = 0, id = 0;
477     g_ExtSystem->GetCurrentThreadSystemId(&sysId);
478     ExtOut("OS Thread Id: 0x%x ", sysId);
479     g_ExtSystem->GetCurrentThreadId(&id);
480     ExtOut("(%d)\n", id);
481
482 #if defined(FEATURE_PAL) && defined(_TARGET_WIN64_)
483     if (unwind)
484     {
485         UnwindStackFrames(sysId);
486     }
487     else
488 #endif
489     {
490         DumpStackInternal(&DSFlag);
491     }
492     return Status;
493 }
494
495
496 /**********************************************************************\
497 * Routine Description:                                                 *
498 *                                                                      *
499 *    This function displays the stack trace for threads that EE knows  *  
500 *    from ThreadStore.                                                 *
501 *                                                                      *
502 \**********************************************************************/
503 DECLARE_API (EEStack)
504 {
505     INIT_API();    
506
507     MINIDUMP_NOT_SUPPORTED();  
508
509     DumpStackFlag DSFlag;
510     DSFlag.fEEonly = FALSE;
511     DSFlag.fSuppressSrcInfo = FALSE;
512     DSFlag.top = 0;
513     DSFlag.end = 0;
514
515     BOOL bShortList = FALSE;
516     BOOL dml = FALSE;
517     CMDOption option[] = 
518     {   // name, vptr, type, hasValue
519         {"-EE", &DSFlag.fEEonly, COBOOL, FALSE},
520         {"-short", &bShortList, COBOOL, FALSE},
521 #ifndef FEATURE_PAL
522         {"/d", &dml, COBOOL, FALSE}
523 #endif
524     };    
525
526     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
527     {
528         return Status;
529     }
530
531     EnableDMLHolder enableDML(dml);
532
533     ULONG Tid;
534     g_ExtSystem->GetCurrentThreadId(&Tid);
535
536     DacpThreadStoreData ThreadStore;
537     if ((Status = ThreadStore.Request(g_sos)) != S_OK)
538     {
539         ExtOut("Failed to request ThreadStore\n");
540         return Status;
541     }    
542
543     CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
544     while (CurThread)
545     {
546         if (IsInterrupt())
547             break;
548
549         DacpThreadData Thread;        
550         if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
551         {
552             ExtOut("Failed to request Thread at %p\n", CurThread);
553             return Status;
554         }
555
556         ULONG id=0;
557         if (g_ExtSystem->GetThreadIdBySystemId (Thread.osThreadId, &id) != S_OK)
558         {
559             CurThread = Thread.nextThread;    
560             continue;
561         }
562         
563         ExtOut("---------------------------------------------\n");
564         ExtOut("Thread %3d\n", id);
565         BOOL doIt = FALSE;
566
567         
568 #define TS_Hijacked 0x00000080
569
570         if (!bShortList) 
571         {
572             doIt = TRUE;
573         }
574         else if ((Thread.lockCount > 0) || (Thread.state & TS_Hijacked)) 
575         {             
576             // TODO: bring back || (int)vThread.m_pFrame != -1  {
577             doIt = TRUE;
578         }
579         else 
580         {
581             ULONG64 IP;
582             g_ExtRegisters->GetInstructionOffset (&IP);
583             JITTypes jitType;
584             TADDR methodDesc;
585             TADDR gcinfoAddr;
586             IP2MethodDesc (TO_TADDR(IP), methodDesc, jitType, gcinfoAddr);
587             if (methodDesc)
588             {
589                 doIt = TRUE;
590             }
591         }
592         
593         if (doIt) 
594         {
595             g_ExtSystem->SetCurrentThreadId(id);
596             DSFlag.top = 0;
597             DSFlag.end = 0;
598             DumpStackInternal(&DSFlag);
599         }
600
601         CurThread = Thread.nextThread;
602     }
603
604     g_ExtSystem->SetCurrentThreadId(Tid);
605     return Status;
606 }
607
608 HRESULT DumpStackObjectsRaw(size_t nArg, __in_z LPSTR exprBottom, __in_z LPSTR exprTop, BOOL bVerify)
609 {
610     size_t StackTop = 0;
611     size_t StackBottom = 0;
612     if (nArg==0)
613     {
614         ULONG64 StackOffset;
615         g_ExtRegisters->GetStackOffset(&StackOffset);
616
617         StackTop = TO_TADDR(StackOffset);
618     }
619     else
620     {
621         StackTop = GetExpression(exprTop);
622         if (StackTop == 0)
623         {
624             ExtOut("wrong option: %s\n", exprTop);
625             return E_FAIL;
626         }
627
628         if (nArg==2)
629         {
630             StackBottom = GetExpression(exprBottom);
631             if (StackBottom == 0)
632             {
633                 ExtOut("wrong option: %s\n", exprBottom);
634                 return E_FAIL;
635             }
636         }
637     }
638     
639 #ifndef FEATURE_PAL
640     NT_TIB teb;
641     ULONG64 dwTebAddr=0;
642     HRESULT hr = g_ExtSystem->GetCurrentThreadTeb(&dwTebAddr);
643     if (SUCCEEDED(hr) && SafeReadMemory (TO_TADDR(dwTebAddr), &teb, sizeof (NT_TIB), NULL))
644     {
645         if (StackTop > TO_TADDR(teb.StackLimit) && StackTop <= TO_TADDR(teb.StackBase))
646         {
647             if (StackBottom == 0 || StackBottom > TO_TADDR(teb.StackBase))
648                 StackBottom = TO_TADDR(teb.StackBase);
649         }
650     }
651 #endif
652     
653     if (StackBottom == 0)
654         StackBottom = StackTop + 0xFFFF;
655     
656     if (StackBottom < StackTop)
657     {
658         ExtOut("Wrong option: stack selection wrong\n");
659         return E_FAIL;
660     }
661
662     // We can use the gc snapshot to eliminate object addresses that are
663     // not on the gc heap. 
664     if (!g_snapshot.Build())
665     {
666         ExtOut("Unable to determine bounds of gc heap\n");
667         return E_FAIL;
668     }   
669
670     // Print thread ID.
671     ULONG id = 0;
672     g_ExtSystem->GetCurrentThreadSystemId (&id);
673     ExtOut("OS Thread Id: 0x%x ", id);
674     g_ExtSystem->GetCurrentThreadId (&id);
675     ExtOut("(%d)\n", id);
676     
677     DumpStackObjectsHelper(StackTop, StackBottom, bVerify);
678     return S_OK;
679 }
680
681 /**********************************************************************\
682 * Routine Description:                                                 *
683 *                                                                      *
684 *    This function is called to dump the address and name of all       *
685 *    Managed Objects on the stack.                                     *  
686 *                                                                      *
687 \**********************************************************************/
688 DECLARE_API(DumpStackObjects)
689 {
690     INIT_API();
691     MINIDUMP_NOT_SUPPORTED();
692     StringHolder exprTop, exprBottom;
693
694     BOOL bVerify = FALSE;
695     BOOL dml = FALSE;
696     CMDOption option[] = 
697     {   // name, vptr, type, hasValue
698         {"-verify", &bVerify, COBOOL, FALSE},
699 #ifndef FEATURE_PAL
700         {"/d", &dml, COBOOL, FALSE}
701 #endif
702     };    
703     CMDValue arg[] = 
704     {   // vptr, type
705         {&exprTop.data, COSTRING},
706         {&exprBottom.data, COSTRING}
707     };
708     size_t nArg;
709
710     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
711     {
712         return Status;
713     }
714
715     EnableDMLHolder enableDML(dml);
716     
717     return DumpStackObjectsRaw(nArg, exprBottom.data, exprTop.data, bVerify);
718 }
719
720 /**********************************************************************\
721 * Routine Description:                                                 *
722 *                                                                      *
723 *    This function is called to dump the contents of a MethodDesc      *
724 *    for a given address                                               *  
725 *                                                                      *
726 \**********************************************************************/
727 DECLARE_API(DumpMD)
728 {
729     INIT_API();
730     MINIDUMP_NOT_SUPPORTED();
731     
732     DWORD_PTR dwStartAddr = NULL;
733     BOOL dml = FALSE;
734
735     CMDOption option[] = 
736     {   // name, vptr, type, hasValue
737 #ifndef FEATURE_PAL
738         {"/d", &dml, COBOOL, FALSE},
739 #endif
740     };
741     CMDValue arg[] = 
742     {   // vptr, type
743         {&dwStartAddr, COHEX},
744     };
745     size_t nArg;
746
747     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
748     {
749         return Status;
750     }
751
752     EnableDMLHolder dmlHolder(dml);
753     
754     DumpMDInfo(dwStartAddr);
755     
756     return Status;
757 }
758
759 BOOL GatherDynamicInfo(TADDR DynamicMethodObj, DacpObjectData *codeArray, 
760                        DacpObjectData *tokenArray, TADDR *ptokenArrayAddr)
761 {
762     BOOL bRet = FALSE;
763     int iOffset;
764     DacpObjectData objData; // temp object
765
766     if (codeArray == NULL || tokenArray == NULL)
767         return bRet;
768     
769     if (objData.Request(g_sos, TO_CDADDR(DynamicMethodObj)) != S_OK)
770         return bRet;
771     
772     iOffset = GetObjFieldOffset(DynamicMethodObj, objData.MethodTable, W("m_resolver"));
773     if (iOffset <= 0)
774         return bRet;
775     
776     TADDR resolverPtr;
777     if (FAILED(MOVE(resolverPtr, DynamicMethodObj + iOffset)))
778         return bRet;
779
780     if (objData.Request(g_sos, TO_CDADDR(resolverPtr)) != S_OK)
781         return bRet;
782     
783     iOffset = GetObjFieldOffset(resolverPtr, objData.MethodTable, W("m_code"));
784     if (iOffset <= 0)
785         return bRet;
786
787     TADDR codePtr;
788     if (FAILED(MOVE(codePtr, resolverPtr + iOffset)))
789         return bRet;
790
791     if (codeArray->Request(g_sos, TO_CDADDR(codePtr)) != S_OK)
792         return bRet;
793     
794     if (codeArray->dwComponentSize != 1)
795         return bRet;
796         
797     // We also need the resolution table
798     iOffset = GetObjFieldOffset (resolverPtr, objData.MethodTable, W("m_scope"));
799     if (iOffset <= 0)
800         return bRet;
801
802     TADDR scopePtr;
803     if (FAILED(MOVE(scopePtr, resolverPtr + iOffset)))
804         return bRet;
805
806     if (objData.Request(g_sos, TO_CDADDR(scopePtr)) != S_OK)
807         return bRet;
808     
809     iOffset = GetObjFieldOffset (scopePtr, objData.MethodTable, W("m_tokens"));
810     if (iOffset <= 0)
811         return bRet;
812
813     TADDR tokensPtr;
814     if (FAILED(MOVE(tokensPtr, scopePtr + iOffset)))
815         return bRet;
816
817     if (objData.Request(g_sos, TO_CDADDR(tokensPtr)) != S_OK)
818         return bRet;
819     
820     iOffset = GetObjFieldOffset(tokensPtr, objData.MethodTable, W("_items"));
821     if (iOffset <= 0)
822         return bRet;
823
824     TADDR itemsPtr;
825     MOVE (itemsPtr, tokensPtr + iOffset);
826
827     *ptokenArrayAddr = itemsPtr;
828     
829     if (tokenArray->Request(g_sos, TO_CDADDR(itemsPtr)) != S_OK)
830         return bRet;
831
832     bRet = TRUE; // whew.
833     return bRet;
834 }
835
836 DECLARE_API(DumpIL)
837 {
838     INIT_API();
839     MINIDUMP_NOT_SUPPORTED();
840     DWORD_PTR dwStartAddr = NULL;
841     DWORD_PTR dwDynamicMethodObj = NULL;
842     BOOL dml = FALSE;
843     BOOL fILPointerDirectlySpecified = FALSE;
844
845     CMDOption option[] = 
846     {   // name, vptr, type, hasValue
847         {"/d", &dml, COBOOL, FALSE},
848         {"/i", &fILPointerDirectlySpecified, COBOOL, FALSE},
849     };
850     CMDValue arg[] = 
851     {   // vptr, type
852         {&dwStartAddr, COHEX},
853     };
854     size_t nArg;
855
856     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
857     {
858         return Status;
859     }
860
861     EnableDMLHolder dmlHolder(dml);    
862     if (dwStartAddr == NULL)
863     {
864         ExtOut("Must pass a valid expression\n");
865         return Status;
866     }
867
868     if (fILPointerDirectlySpecified)
869     {
870         return DecodeILFromAddress(NULL, dwStartAddr);
871     }
872
873     if (!g_snapshot.Build())
874     {
875         ExtOut("Unable to build snapshot of the garbage collector state\n");
876         return Status;
877     }
878
879     if (g_snapshot.GetHeap(dwStartAddr) != NULL)
880     {
881         dwDynamicMethodObj = dwStartAddr;
882     }
883     
884     if (dwDynamicMethodObj == NULL)
885     {
886         // We have been given a MethodDesc
887         DacpMethodDescData MethodDescData;
888         if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK)
889         {
890             ExtOut("%p is not a MethodDesc\n", SOS_PTR(dwStartAddr));
891             return Status;
892         }
893
894         if (MethodDescData.bIsDynamic && MethodDescData.managedDynamicMethodObject)
895         {
896             dwDynamicMethodObj = TO_TADDR(MethodDescData.managedDynamicMethodObject);
897             if (dwDynamicMethodObj == NULL)
898             {
899                 ExtOut("Unable to print IL for DynamicMethodDesc %p\n", SOS_PTR(dwDynamicMethodObj));
900                 return Status;
901             }
902         }
903         else
904         {
905             // This is not a dynamic method, print the IL for it.
906             // Get the module
907             DacpModuleData dmd;    
908             if (dmd.Request(g_sos, MethodDescData.ModulePtr) != S_OK)
909             {
910                 ExtOut("Unable to get module\n");
911                 return Status;
912             }
913
914             ToRelease<IMetaDataImport> pImport = MDImportForModule(&dmd);
915             if (pImport == NULL)
916             {
917                 ExtOut("bad import\n");
918                 return Status;
919             }
920
921             ULONG pRva;
922             DWORD dwFlags;
923             if (pImport->GetRVA(MethodDescData.MDToken, &pRva, &dwFlags) != S_OK)
924             {
925                 ExtOut("error in import\n");
926                 return Status;
927             }    
928
929             CLRDATA_ADDRESS ilAddrClr;
930             if (g_sos->GetILForModule(MethodDescData.ModulePtr, pRva, &ilAddrClr) != S_OK)
931             {
932                 ExtOut("FindIL failed\n");
933                 return Status;
934             }
935
936             TADDR ilAddr = TO_TADDR(ilAddrClr);
937             IfFailRet(DecodeILFromAddress(pImport, ilAddr));
938         }
939     }
940     
941     if (dwDynamicMethodObj != NULL)
942     {
943         // We have a DynamicMethod managed object, let us visit the town and paint.        
944         DacpObjectData codeArray;
945         DacpObjectData tokenArray;
946         DWORD_PTR tokenArrayAddr;
947         if (!GatherDynamicInfo (dwDynamicMethodObj, &codeArray, &tokenArray, &tokenArrayAddr))
948         {
949             DMLOut("Error gathering dynamic info from object at %s.\n", DMLObject(dwDynamicMethodObj));
950             return Status;
951         }
952         
953         // Read the memory into a local buffer
954         BYTE *pArray = new NOTHROW BYTE[(SIZE_T)codeArray.dwNumComponents];
955         if (pArray == NULL)
956         {
957             ExtOut("Not enough memory to read IL\n");
958             return Status;
959         }
960         
961         Status = g_ExtData->ReadVirtual(UL64_TO_CDA(codeArray.ArrayDataPtr), pArray, (ULONG)codeArray.dwNumComponents, NULL);
962         if (Status != S_OK)
963         {
964             ExtOut("Failed to read memory\n");
965             delete [] pArray;
966             return Status;
967         }
968
969         // Now we have a local copy of the IL, and a managed array for token resolution.
970         // Visit our IL parser with this info.        
971         ExtOut("This is dynamic IL. Exception info is not reported at this time.\n");
972         ExtOut("If a token is unresolved, run \"!do <addr>\" on the addr given\n");
973         ExtOut("in parenthesis. You can also look at the token table yourself, by\n");
974         ExtOut("running \"!DumpArray %p\".\n\n", SOS_PTR(tokenArrayAddr));
975         DecodeDynamicIL(pArray, (ULONG)codeArray.dwNumComponents, tokenArray);
976         
977         delete [] pArray;                
978     }    
979     return Status;
980 }
981
982 void DumpSigWorker (
983         DWORD_PTR dwSigAddr,
984         DWORD_PTR dwModuleAddr,
985         BOOL fMethod)
986 {
987     //
988     // Find the length of the signature and copy it into the debugger process.
989     //
990
991     ULONG cbSig = 0;
992     const ULONG cbSigInc = 256;
993     ArrayHolder<COR_SIGNATURE> pSig = new NOTHROW COR_SIGNATURE[cbSigInc];
994     if (pSig == NULL)
995     {
996         ReportOOM();        
997         return;
998     }
999     
1000     CQuickBytes sigString;
1001     for (;;)
1002     {
1003         if (IsInterrupt())
1004             return;
1005
1006         ULONG cbCopied;
1007         if (!SafeReadMemory(TO_TADDR(dwSigAddr + cbSig), pSig + cbSig, cbSigInc, &cbCopied))
1008             return;
1009         cbSig += cbCopied;
1010
1011         sigString.ReSize(0);
1012         GetSignatureStringResults result;
1013         if (fMethod)
1014             result = GetMethodSignatureString(pSig, cbSig, dwModuleAddr, &sigString);
1015         else
1016             result = GetSignatureString(pSig, cbSig, dwModuleAddr, &sigString);
1017
1018         if (GSS_ERROR == result)
1019             return;
1020
1021         if (GSS_SUCCESS == result)
1022             break;
1023
1024         // If we didn't get the full amount back, and we failed to parse the
1025         // signature, it's not valid because of insufficient data
1026         if (cbCopied < 256)
1027         {
1028             ExtOut("Invalid signature\n");
1029             return;
1030         }
1031
1032 #ifdef _PREFAST_
1033 #pragma warning(push)
1034 #pragma warning(disable:6280) // "Suppress PREFast warning about mismatch alloc/free"
1035 #endif
1036
1037         PCOR_SIGNATURE pSigNew = (PCOR_SIGNATURE)realloc(pSig, cbSig+cbSigInc);
1038
1039 #ifdef _PREFAST_
1040 #pragma warning(pop)
1041 #endif
1042
1043         if (pSigNew == NULL)
1044         {
1045             ExtOut("Out of memory\n");
1046             return;
1047         }
1048         
1049         pSig = pSigNew;
1050     }
1051
1052     ExtOut("%S\n", (PCWSTR)sigString.Ptr());
1053 }
1054
1055 /**********************************************************************\
1056 * Routine Description:                                                 *
1057 *                                                                      *
1058 *    This function is called to dump a signature object.               *
1059 *                                                                      *
1060 \**********************************************************************/
1061 DECLARE_API(DumpSig)
1062 {
1063     INIT_API();
1064
1065     MINIDUMP_NOT_SUPPORTED();
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     if (nArg != 2)
1084     {
1085         ExtOut("!DumpSig <sigaddr> <moduleaddr>\n");
1086         return Status;
1087     }
1088
1089     DWORD_PTR dwSigAddr = GetExpression(sigExpr.data);        
1090     DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data);
1091
1092     if (dwSigAddr == 0 || dwModuleAddr == 0)
1093     {
1094         ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data);
1095         return Status;
1096     }
1097     
1098     DumpSigWorker(dwSigAddr, dwModuleAddr, TRUE);
1099     return Status;
1100 }
1101
1102 /**********************************************************************\
1103 * Routine Description:                                                 *
1104 *                                                                      *
1105 *    This function is called to dump a portion of a signature object.  *
1106 *                                                                      *
1107 \**********************************************************************/
1108 DECLARE_API(DumpSigElem)
1109 {
1110     INIT_API();
1111
1112     MINIDUMP_NOT_SUPPORTED();
1113     
1114
1115     //
1116     // Fetch arguments
1117     //
1118
1119     StringHolder sigExpr;
1120     StringHolder moduleExpr;
1121     CMDValue arg[] = 
1122     {
1123         {&sigExpr.data, COSTRING},
1124         {&moduleExpr.data, COSTRING}
1125     };
1126     size_t nArg;
1127     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
1128     {
1129         return Status;
1130     }
1131
1132     if (nArg != 2)
1133     {
1134         ExtOut("!DumpSigElem <sigaddr> <moduleaddr>\n");
1135         return Status;
1136     }
1137
1138     DWORD_PTR dwSigAddr = GetExpression(sigExpr.data);        
1139     DWORD_PTR dwModuleAddr = GetExpression(moduleExpr.data);
1140
1141     if (dwSigAddr == 0 || dwModuleAddr == 0)
1142     {
1143         ExtOut("Invalid parameters %s %s\n", sigExpr.data, moduleExpr.data);
1144         return Status;
1145     }
1146
1147     DumpSigWorker(dwSigAddr, dwModuleAddr, FALSE);
1148     return Status;
1149 }
1150
1151 /**********************************************************************\
1152 * Routine Description:                                                 *
1153 *                                                                      *
1154 *    This function is called to dump the contents of an EEClass from   *  
1155 *    a given address
1156 *                                                                      *
1157 \**********************************************************************/
1158 DECLARE_API(DumpClass)
1159 {
1160     INIT_API();
1161     MINIDUMP_NOT_SUPPORTED();
1162     
1163     DWORD_PTR dwStartAddr = 0;
1164     BOOL dml = FALSE;
1165
1166     CMDOption option[] = 
1167     {   // name, vptr, type, hasValue
1168 #ifndef FEATURE_PAL
1169         {"/d", &dml, COBOOL, FALSE},
1170 #endif
1171     };
1172     CMDValue arg[] = 
1173     {   // vptr, type
1174         {&dwStartAddr, COHEX}
1175     };
1176
1177     size_t nArg;
1178     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
1179     {
1180         return Status;
1181     }
1182
1183     if (nArg == 0) 
1184     {
1185         ExtOut("Missing EEClass address\n");
1186         return Status;
1187     }
1188
1189     EnableDMLHolder dmlHolder(dml);
1190
1191     CLRDATA_ADDRESS methodTable;
1192     if ((Status=g_sos->GetMethodTableForEEClass(TO_CDADDR(dwStartAddr), &methodTable)) != S_OK)
1193     {
1194         ExtOut("Invalid EEClass address\n");
1195         return Status;
1196     }
1197
1198     DacpMethodTableData mtdata;
1199     if ((Status=mtdata.Request(g_sos, TO_CDADDR(methodTable)))!=S_OK)
1200     {
1201         ExtOut("EEClass has an invalid MethodTable address\n");
1202         return Status;
1203     }            
1204
1205     sos::MethodTable mt = TO_TADDR(methodTable);
1206     ExtOut("Class Name:      %S\n", mt.GetName());
1207
1208     WCHAR fileName[MAX_LONGPATH];
1209     FileNameForModule(TO_TADDR(mtdata.Module), fileName);
1210     ExtOut("mdToken:         %p\n", mtdata.cl);
1211     ExtOut("File:            %S\n", fileName);
1212
1213     CLRDATA_ADDRESS ParentEEClass = NULL;
1214     if (mtdata.ParentMethodTable)
1215     {
1216         DacpMethodTableData mtdataparent;
1217         if ((Status=mtdataparent.Request(g_sos, TO_CDADDR(mtdata.ParentMethodTable)))!=S_OK)
1218         {
1219             ExtOut("EEClass has an invalid MethodTable address\n");
1220             return Status;
1221         }                     
1222         ParentEEClass = mtdataparent.Class;
1223     }
1224
1225     DMLOut("Parent Class:    %s\n", DMLClass(ParentEEClass));
1226     DMLOut("Module:          %s\n", DMLModule(mtdata.Module));
1227     DMLOut("Method Table:    %s\n", DMLMethodTable(methodTable));
1228     ExtOut("Vtable Slots:    %x\n", mtdata.wNumVirtuals);
1229     ExtOut("Total Method Slots:  %x\n", mtdata.wNumVtableSlots);
1230     ExtOut("Class Attributes:    %x  ", mtdata.dwAttrClass);
1231
1232     if (IsTdInterface(mtdata.dwAttrClass))
1233         ExtOut("Interface, ");
1234     if (IsTdAbstract(mtdata.dwAttrClass))
1235         ExtOut("Abstract, ");
1236     if (IsTdImport(mtdata.dwAttrClass))
1237         ExtOut("ComImport, ");
1238     
1239     ExtOut("\n");        
1240
1241     DacpMethodTableTransparencyData transparency;
1242     if (SUCCEEDED(transparency.Request(g_sos, methodTable)))
1243     {
1244         ExtOut("Transparency:        %s\n", GetTransparency(transparency));
1245     }
1246
1247     DacpMethodTableFieldData vMethodTableFields;
1248     if (SUCCEEDED(vMethodTableFields.Request(g_sos, methodTable)))
1249     {
1250         ExtOut("NumInstanceFields:   %x\n", vMethodTableFields.wNumInstanceFields);
1251         ExtOut("NumStaticFields:     %x\n", vMethodTableFields.wNumStaticFields);
1252
1253         if (vMethodTableFields.wNumThreadStaticFields != 0)
1254         {
1255             ExtOut("NumThreadStaticFields: %x\n", vMethodTableFields.wNumThreadStaticFields);
1256         }
1257
1258
1259         if (vMethodTableFields.wContextStaticsSize)
1260         {
1261             ExtOut("ContextStaticOffset: %x\n", vMethodTableFields.wContextStaticOffset);
1262             ExtOut("ContextStaticsSize:  %x\n", vMethodTableFields.wContextStaticsSize);
1263         }
1264
1265     
1266         if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1267         {
1268             DisplayFields(methodTable, &mtdata, &vMethodTableFields, NULL, TRUE, FALSE);
1269         }
1270     }
1271
1272     return Status;
1273 }
1274
1275 /**********************************************************************\
1276 * Routine Description:                                                 *
1277 *                                                                      *
1278 *    This function is called to dump the contents of a MethodTable     *  
1279 *    from a given address                                              *
1280 *                                                                      *
1281 \**********************************************************************/
1282 DECLARE_API(DumpMT)
1283 {
1284     DWORD_PTR dwStartAddr=0;
1285     DWORD_PTR dwOriginalAddr;
1286     
1287     INIT_API();
1288
1289     MINIDUMP_NOT_SUPPORTED();
1290     
1291     BOOL bDumpMDTable = FALSE;
1292     BOOL dml = FALSE;
1293
1294     CMDOption option[] = 
1295     {   // name, vptr, type, hasValue
1296         {"-MD", &bDumpMDTable, COBOOL, FALSE},
1297 #ifndef FEATURE_PAL
1298         {"/d", &dml, COBOOL, FALSE}
1299 #endif
1300     };
1301     CMDValue arg[] = 
1302     {   // vptr, type
1303         {&dwStartAddr, COHEX}
1304     };
1305     size_t nArg;
1306     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
1307     {
1308         return Status;
1309     }
1310
1311     EnableDMLHolder dmlHolder(dml);
1312     TableOutput table(2, 16, AlignLeft, false);
1313
1314     if (nArg == 0)
1315     {
1316         Print("Missing MethodTable address\n");
1317         return Status;
1318     }
1319
1320     dwOriginalAddr = dwStartAddr;
1321     dwStartAddr = dwStartAddr&~3;
1322     
1323     if (!IsMethodTable(dwStartAddr))
1324     {
1325         Print(dwOriginalAddr, " is not a MethodTable\n");
1326         return Status;
1327     }
1328  
1329     DacpMethodTableData vMethTable;
1330     vMethTable.Request(g_sos, TO_CDADDR(dwStartAddr));    
1331
1332     if (vMethTable.bIsFree) 
1333     {
1334         Print("Free MethodTable\n");
1335         return Status;
1336     }
1337
1338     table.WriteRow("EEClass:", EEClassPtr(vMethTable.Class));
1339
1340     table.WriteRow("Module:", ModulePtr(vMethTable.Module));
1341
1342     sos::MethodTable mt = (TADDR)dwStartAddr;
1343     table.WriteRow("Name:", mt.GetName());
1344
1345     WCHAR fileName[MAX_LONGPATH];
1346     FileNameForModule(TO_TADDR(vMethTable.Module), fileName);
1347     table.WriteRow("mdToken:", Pointer(vMethTable.cl));
1348     table.WriteRow("File:", fileName[0] ? fileName : W("Unknown Module"));
1349
1350     table.WriteRow("BaseSize:", PrefixHex(vMethTable.BaseSize));
1351     table.WriteRow("ComponentSize:", PrefixHex(vMethTable.ComponentSize));
1352     table.WriteRow("Slots in VTable:", Decimal(vMethTable.wNumMethods));
1353     
1354     table.SetColWidth(0, 29);
1355     table.WriteRow("Number of IFaces in IFaceMap:", Decimal(vMethTable.wNumInterfaces));
1356
1357     if (bDumpMDTable)
1358     {
1359         table.ReInit(4, POINTERSIZE_HEX, AlignRight);
1360         table.SetColAlignment(3, AlignLeft);
1361         table.SetColWidth(2, 6);
1362
1363         Print("--------------------------------------\n");
1364         Print("MethodDesc Table\n");
1365
1366         table.WriteRow("Entry", "MethodDesc", "JIT", "Name");
1367
1368         for (DWORD n = 0; n < vMethTable.wNumMethods; n++)
1369         {
1370             JITTypes jitType;
1371             DWORD_PTR methodDesc=0;
1372             DWORD_PTR gcinfoAddr;
1373
1374             CLRDATA_ADDRESS entry;
1375             if (g_sos->GetMethodTableSlot(dwStartAddr, n, &entry) != S_OK)
1376             {
1377                 PrintLn("<error getting slot ", Decimal(n), ">");
1378                 continue;
1379             }
1380
1381             IP2MethodDesc((DWORD_PTR)entry, methodDesc, jitType, gcinfoAddr);
1382             table.WriteColumn(0, entry);
1383             table.WriteColumn(1, MethodDescPtr(methodDesc));
1384
1385             if (jitType == TYPE_UNKNOWN && methodDesc != NULL)
1386             {
1387                 // We can get a more accurate jitType from NativeCodeAddr of the methoddesc,
1388                 // because the methodtable entry hasn't always been patched.
1389                 DacpMethodDescData tmpMethodDescData;
1390                 if (tmpMethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
1391                 {
1392                     DacpCodeHeaderData codeHeaderData;                        
1393                     if (codeHeaderData.Request(g_sos,tmpMethodDescData.NativeCodeAddr) == S_OK)
1394                     {        
1395                         jitType = (JITTypes) codeHeaderData.JITType;
1396                     }
1397                 }
1398             }
1399
1400             const char *pszJitType = "NONE";
1401             if (jitType == TYPE_JIT)
1402                 pszJitType = "JIT";
1403             else if (jitType == TYPE_PJIT)
1404                 pszJitType = "PreJIT";
1405             else
1406             {
1407                 DacpMethodDescData MethodDescData;
1408                 if (MethodDescData.Request(g_sos, TO_CDADDR(methodDesc)) == S_OK)
1409                 {
1410                     // Is it an fcall?
1411                     if ((TO_TADDR(MethodDescData.NativeCodeAddr) >=  TO_TADDR(moduleInfo[MSCORWKS].baseAddr)) &&
1412                         ((TO_TADDR(MethodDescData.NativeCodeAddr) <  TO_TADDR(moduleInfo[MSCORWKS].baseAddr + moduleInfo[MSCORWKS].size))))
1413                     {
1414                         pszJitType = "FCALL";
1415                     }
1416                 }
1417             }
1418
1419             table.WriteColumn(2, pszJitType);
1420             
1421             NameForMD_s(methodDesc,g_mdName,mdNameLen);                        
1422             table.WriteColumn(3, g_mdName);
1423         }
1424     }
1425     return Status;    
1426 }
1427
1428 extern size_t Align (size_t nbytes);
1429
1430 HRESULT PrintVC(TADDR taMT, TADDR taObject, BOOL bPrintFields = TRUE)
1431 {       
1432     HRESULT Status;
1433     DacpMethodTableData mtabledata;
1434     if ((Status = mtabledata.Request(g_sos, TO_CDADDR(taMT)))!=S_OK)
1435         return Status;
1436     
1437     size_t size = mtabledata.BaseSize;
1438     if ((Status=g_sos->GetMethodTableName(TO_CDADDR(taMT), mdNameLen, g_mdName, NULL))!=S_OK)
1439         return Status;
1440
1441     ExtOut("Name:        %S\n", g_mdName);
1442     DMLOut("MethodTable: %s\n", DMLMethodTable(taMT));
1443     DMLOut("EEClass:     %s\n", DMLClass(mtabledata.Class));
1444     ExtOut("Size:        %d(0x%x) bytes\n", size, size);
1445
1446     FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
1447     ExtOut("File:        %S\n", g_mdName[0] ? g_mdName : W("Unknown Module"));
1448
1449     if (bPrintFields)
1450     {
1451         DacpMethodTableFieldData vMethodTableFields;
1452         if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(taMT)))!=S_OK)
1453             return Status;
1454
1455         ExtOut("Fields:\n");
1456
1457         if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1458             DisplayFields(TO_CDADDR(taMT), &mtabledata, &vMethodTableFields, taObject, TRUE, TRUE);
1459     }
1460
1461     return S_OK;
1462 }
1463
1464 void PrintRuntimeTypeInfo(TADDR p_rtObject, const DacpObjectData & rtObjectData)
1465 {
1466     // Get the method table
1467     int iOffset = GetObjFieldOffset(p_rtObject, rtObjectData.MethodTable, W("m_handle"));
1468     if (iOffset > 0)
1469     {            
1470         TADDR mtPtr;
1471         if (SUCCEEDED(GetMTOfObject(p_rtObject + iOffset, &mtPtr)))
1472         {
1473             sos::MethodTable mt = mtPtr;
1474             ExtOut("Type Name:   %S\n", mt.GetName());
1475             DMLOut("Type MT:     %s\n", DMLMethodTable(mtPtr));
1476         }                        
1477     }        
1478 }
1479
1480 HRESULT PrintObj(TADDR taObj, BOOL bPrintFields = TRUE)
1481 {
1482     if (!sos::IsObject(taObj, true))
1483     {
1484         ExtOut("<Note: this object has an invalid CLASS field>\n");
1485     }
1486
1487     DacpObjectData objData;
1488     HRESULT Status;
1489     if ((Status=objData.Request(g_sos, TO_CDADDR(taObj))) != S_OK)
1490     {        
1491         ExtOut("Invalid object\n");
1492         return Status;
1493     }
1494
1495     if (objData.ObjectType==OBJ_FREE)
1496     {
1497         ExtOut("Free Object\n");
1498         DWORD_PTR size = (DWORD_PTR)objData.Size;
1499         ExtOut("Size:        %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1500         return S_OK;
1501     }
1502     
1503     sos::Object obj = taObj;
1504     ExtOut("Name:        %S\n", obj.GetTypeName());
1505     DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));
1506
1507     
1508     DacpMethodTableData mtabledata;
1509     if ((Status=mtabledata.Request(g_sos,objData.MethodTable)) == S_OK)
1510     {
1511         DMLOut("EEClass:     %s\n", DMLClass(mtabledata.Class));
1512     }
1513     else        
1514     {
1515         ExtOut("Invalid EEClass address\n");
1516         return Status;
1517     }
1518
1519     if (objData.RCW != NULL)
1520     {
1521         DMLOut("RCW:         %s\n", DMLRCWrapper(objData.RCW));
1522     }
1523     if (objData.CCW != NULL)
1524     {
1525         DMLOut("CCW:         %s\n", DMLCCWrapper(objData.CCW));
1526     }
1527
1528     DWORD_PTR size = (DWORD_PTR)objData.Size;
1529     ExtOut("Size:        %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1530
1531     if (_wcscmp(obj.GetTypeName(), W("System.RuntimeType")) == 0)
1532     {
1533         PrintRuntimeTypeInfo(taObj, objData);
1534     }
1535
1536     if (_wcscmp(obj.GetTypeName(), W("System.RuntimeType+RuntimeTypeCache")) == 0)
1537     {
1538         // Get the method table
1539         int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("m_runtimeType"));
1540         if (iOffset > 0)
1541         {            
1542             TADDR rtPtr;
1543             if (MOVE(rtPtr, taObj + iOffset) == S_OK)
1544             {
1545                 DacpObjectData rtObjectData;
1546                 if ((Status=rtObjectData.Request(g_sos, TO_CDADDR(rtPtr))) != S_OK)
1547                 {        
1548                     ExtOut("Error when reading RuntimeType field\n");
1549                     return Status;
1550                 }
1551
1552                 PrintRuntimeTypeInfo(rtPtr, rtObjectData);
1553             }                        
1554         }        
1555     }
1556
1557     if (objData.ObjectType==OBJ_ARRAY)
1558     {
1559         ExtOut("Array:       Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s",
1560                 objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType));
1561
1562         IfDMLOut(" (<exec cmd=\"!DumpArray /d %p\">Print Array</exec>)", SOS_PTR(taObj));
1563         ExtOut("\n");
1564         
1565         if (objData.ElementType == ELEMENT_TYPE_I1 ||
1566             objData.ElementType == ELEMENT_TYPE_U1 ||
1567             objData.ElementType == ELEMENT_TYPE_CHAR)
1568         {
1569             bool wide = objData.ElementType == ELEMENT_TYPE_CHAR;
1570
1571             // Get the size of the character array, but clamp it to a reasonable length.
1572             TADDR pos = taObj + (2 * sizeof(DWORD_PTR));
1573             DWORD_PTR num;
1574             moveN(num, taObj + sizeof(DWORD_PTR));
1575
1576             if (IsDMLEnabled())
1577                 DMLOut("<exec cmd=\"%s %x L%x\">Content</exec>:     ", (wide) ? "dw" : "db", pos, num);
1578             else
1579                 ExtOut("Content:     ");
1580             CharArrayContent(pos, (ULONG)(num <= 128 ? num : 128), wide);
1581             ExtOut("\n");
1582         }
1583     }
1584     else
1585     {
1586         FileNameForModule(TO_TADDR(mtabledata.Module), g_mdName);
1587         ExtOut("File:        %S\n", g_mdName[0] ? g_mdName : W("Unknown Module"));
1588     }
1589
1590     if (objData.ObjectType == OBJ_STRING)
1591     {
1592         ExtOut("String:      ");
1593         StringObjectContent(taObj);
1594         ExtOut("\n");
1595     }
1596     else if (objData.ObjectType == OBJ_OBJECT)
1597     {
1598         ExtOut("Object\n");
1599     }    
1600
1601     if (bPrintFields)
1602     {
1603         DacpMethodTableFieldData vMethodTableFields;
1604         if ((Status = vMethodTableFields.Request(g_sos,TO_CDADDR(objData.MethodTable)))!=S_OK)
1605             return Status;
1606
1607         ExtOut("Fields:\n");
1608         if (vMethodTableFields.wNumInstanceFields + vMethodTableFields.wNumStaticFields > 0)
1609         {
1610             DisplayFields(objData.MethodTable, &mtabledata, &vMethodTableFields, taObj, TRUE, FALSE);
1611         }
1612         else
1613         {
1614             ExtOut("None\n");
1615         }
1616     }
1617
1618     sos::ThinLockInfo lockInfo;
1619     if (obj.GetThinLock(lockInfo))
1620     {
1621         ExtOut("ThinLock owner %x (%p), Recursive %x\n", lockInfo.ThreadId, 
1622             SOS_PTR(lockInfo.ThreadPtr), lockInfo.Recursion);
1623     }
1624     
1625     return S_OK;
1626 }
1627
1628 BOOL IndicesInRange (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank)
1629 {
1630     int i = 0;
1631     if (!ClrSafeInt<int>::subtraction((int)rank, 1, i))
1632     {
1633         ExtOut("<integer underflow>\n");
1634         return FALSE;
1635     }
1636
1637     for (; i >= 0; i--)
1638     {
1639         if (indices[i] >= bounds[i] + lowerBounds[i])
1640         {
1641             if (i == 0)
1642             {
1643                 return FALSE;
1644             }
1645             
1646             indices[i] = lowerBounds[i];
1647             indices[i - 1]++;
1648         }
1649     }
1650
1651     return TRUE;
1652 }
1653
1654 void ExtOutIndices (DWORD * indices, DWORD rank)
1655 {
1656     for (DWORD i = 0; i < rank; i++)
1657     {
1658         ExtOut("[%d]", indices[i]);
1659     }
1660 }
1661
1662 size_t OffsetFromIndices (DWORD * indices, DWORD * lowerBounds, DWORD * bounds, DWORD rank)
1663 {
1664     _ASSERTE(rank >= 0);
1665     size_t multiplier = 1;
1666     size_t offset = 0;
1667     int i = 0;
1668     if (!ClrSafeInt<int>::subtraction((int)rank, 1, i))
1669     {
1670         ExtOut("<integer underflow>\n");
1671         return 0;
1672     }
1673
1674     for (; i >= 0; i--) 
1675     {
1676         DWORD curIndex = indices[i] - lowerBounds[i];
1677         offset += curIndex * multiplier;
1678         multiplier *= bounds[i];
1679     }
1680
1681     return offset;
1682 }
1683 HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint);
1684 #ifdef _DEBUG
1685 HRESULT PrintPermissionSet (TADDR p_PermSet)
1686 {
1687     HRESULT Status = S_OK;
1688
1689     DacpObjectData PermSetData;
1690     if ((Status=PermSetData.Request(g_sos, TO_CDADDR(p_PermSet))) != S_OK)
1691     {        
1692         ExtOut("Invalid object\n");
1693         return Status;
1694     }
1695
1696     
1697     sos::MethodTable mt = TO_TADDR(PermSetData.MethodTable);
1698     if (_wcscmp (W("System.Security.PermissionSet"), mt.GetName()) != 0 && _wcscmp(W("System.Security.NamedPermissionSet"), mt.GetName()) != 0)
1699     {
1700         ExtOut("Invalid PermissionSet object\n");
1701         return S_FALSE;
1702     }
1703
1704     ExtOut("PermissionSet object: %p\n", SOS_PTR(p_PermSet));
1705     
1706     // Print basic info
1707
1708     // Walk the fields, printing some fields in a special way.
1709
1710     int iOffset = GetObjFieldOffset (p_PermSet, PermSetData.MethodTable, W("m_Unrestricted"));
1711     
1712     if (iOffset > 0)        
1713     {
1714         BYTE unrestricted;
1715         MOVE(unrestricted, p_PermSet + iOffset);
1716         if (unrestricted)
1717             ExtOut("Unrestricted: TRUE\n");
1718         else
1719             ExtOut("Unrestricted: FALSE\n");
1720     }
1721
1722     iOffset = GetObjFieldOffset (p_PermSet, PermSetData.MethodTable, W("m_permSet"));
1723     if (iOffset > 0)
1724     {
1725         TADDR tbSetPtr;
1726         MOVE(tbSetPtr, p_PermSet + iOffset);
1727         if (tbSetPtr != NULL)
1728         {
1729             DacpObjectData tbSetData;
1730             if ((Status=tbSetData.Request(g_sos, TO_CDADDR(tbSetPtr))) != S_OK)
1731             {        
1732                 ExtOut("Invalid object\n");
1733                 return Status;
1734             }
1735
1736             iOffset = GetObjFieldOffset (tbSetPtr, tbSetData.MethodTable, W("m_Set"));
1737             if (iOffset > 0)
1738             {
1739                 DWORD_PTR PermsArrayPtr;
1740                 MOVE(PermsArrayPtr, tbSetPtr + iOffset);
1741                 if (PermsArrayPtr != NULL)
1742                 {
1743                     // Print all the permissions in the array
1744                     DacpObjectData objData;
1745                     if ((Status=objData.Request(g_sos, TO_CDADDR(PermsArrayPtr))) != S_OK)
1746                     {        
1747                         ExtOut("Invalid object\n");
1748                         return Status;
1749                     }
1750                     DumpArrayFlags flags;
1751                     flags.bDetail = TRUE;
1752                     return PrintArray(objData, flags, TRUE);
1753                 }
1754             }
1755
1756             iOffset = GetObjFieldOffset (tbSetPtr, tbSetData.MethodTable, W("m_Obj"));
1757             if (iOffset > 0)
1758             {
1759                 DWORD_PTR PermObjPtr;
1760                 MOVE(PermObjPtr, tbSetPtr + iOffset);
1761                 if (PermObjPtr != NULL)
1762                 {
1763                     // Print the permission object
1764                     return PrintObj(PermObjPtr);
1765                 }
1766             }
1767             
1768
1769         }
1770     }
1771     return Status;
1772 }
1773
1774 #endif // _DEBUG
1775
1776 /**********************************************************************\
1777 * Routine Description:                                                 *
1778 *                                                                      *
1779 *    This function is called to dump the contents of an object from a  *  
1780 *    given address
1781 *                                                                      *
1782 \**********************************************************************/
1783 DECLARE_API(DumpArray)
1784 {
1785     INIT_API();
1786
1787     DumpArrayFlags flags;
1788     
1789     MINIDUMP_NOT_SUPPORTED();
1790
1791     BOOL dml = FALSE;
1792     
1793     CMDOption option[] = 
1794     {   // name, vptr, type, hasValue
1795         {"-start", &flags.startIndex, COSIZE_T, TRUE},
1796         {"-length", &flags.Length, COSIZE_T, TRUE},
1797         {"-details", &flags.bDetail, COBOOL, FALSE},
1798         {"-nofields", &flags.bNoFieldsForElement, COBOOL, FALSE},
1799 #ifndef FEATURE_PAL
1800         {"/d", &dml, COBOOL, FALSE},
1801 #endif
1802     };
1803     CMDValue arg[] = 
1804     {   // vptr, type
1805         {&flags.strObject, COSTRING}
1806     };
1807     size_t nArg;
1808     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
1809     {
1810         return Status;
1811     }
1812
1813     EnableDMLHolder dmlHolder(dml);
1814     DWORD_PTR p_Object = GetExpression (flags.strObject);
1815     if (p_Object == 0)
1816     {
1817         ExtOut("Invalid parameter %s\n", flags.strObject);
1818         return Status;
1819     }
1820
1821     if (!sos::IsObject(p_Object, true))
1822     {
1823         ExtOut("<Note: this object has an invalid CLASS field>\n");
1824     }
1825     
1826     DacpObjectData objData;
1827     if ((Status=objData.Request(g_sos, TO_CDADDR(p_Object))) != S_OK)
1828     {  
1829         ExtOut("Invalid object\n");
1830         return Status;
1831     }
1832
1833     if (objData.ObjectType != OBJ_ARRAY)
1834     {
1835         ExtOut("Not an array, please use !DumpObj instead\n");
1836         return S_OK;
1837     }
1838     return PrintArray(objData, flags, FALSE);
1839 }
1840
1841
1842 HRESULT PrintArray(DacpObjectData& objData, DumpArrayFlags& flags, BOOL isPermSetPrint)
1843 {
1844     HRESULT Status = S_OK;
1845
1846     if (objData.dwRank != 1 && (flags.Length != (DWORD_PTR)-1 ||flags.startIndex != 0))
1847     {
1848         ExtOut("For multi-dimension array, length and start index are supported\n");
1849         return S_OK;
1850     }
1851
1852     if (flags.startIndex > objData.dwNumComponents)
1853     {
1854         ExtOut("Start index out of range\n");
1855         return S_OK;
1856     }
1857
1858     if (!flags.bDetail && flags.bNoFieldsForElement)
1859     {
1860         ExtOut("-nofields has no effect unless -details is specified\n");
1861     }
1862     
1863     DWORD i;
1864     if (!isPermSetPrint)
1865     {
1866         // TODO: don't depend on this being a MethodTable
1867         NameForMT_s(TO_TADDR(objData.ElementTypeHandle), g_mdName, mdNameLen);
1868
1869         ExtOut("Name:        %S[", g_mdName);
1870         for (i = 1; i < objData.dwRank; i++)
1871             ExtOut(",");
1872         ExtOut("]\n");
1873         
1874         DMLOut("MethodTable: %s\n", DMLMethodTable(objData.MethodTable));
1875
1876         {
1877             DacpMethodTableData mtdata;
1878             if (SUCCEEDED(mtdata.Request(g_sos, objData.MethodTable)))
1879             {
1880                 DMLOut("EEClass:     %s\n", DMLClass(mtdata.Class));
1881             }            
1882         }
1883
1884         DWORD_PTR size = (DWORD_PTR)objData.Size;
1885         ExtOut("Size:        %" POINTERSIZE_TYPE "d(0x%" POINTERSIZE_TYPE "x) bytes\n", size, size);
1886
1887         ExtOut("Array:       Rank %d, Number of elements %" POINTERSIZE_TYPE "d, Type %s\n", 
1888                 objData.dwRank, (DWORD_PTR)objData.dwNumComponents, ElementTypeName(objData.ElementType));
1889         DMLOut("Element Methodtable: %s\n", DMLMethodTable(objData.ElementTypeHandle));
1890     }
1891
1892     BOOL isElementValueType = IsElementValueType(objData.ElementType);
1893
1894     DWORD dwRankAllocSize;
1895     if (!ClrSafeInt<DWORD>::multiply(sizeof(DWORD), objData.dwRank, dwRankAllocSize))
1896     {
1897         ExtOut("Integer overflow on array rank\n");
1898         return Status;
1899     }
1900
1901     DWORD *lowerBounds = (DWORD *)alloca(dwRankAllocSize);
1902     if (!SafeReadMemory(objData.ArrayLowerBoundsPtr, lowerBounds, dwRankAllocSize, NULL))
1903     {
1904         ExtOut("Failed to read lower bounds info from the array\n");        
1905         return S_OK;
1906     }
1907
1908     DWORD *bounds = (DWORD *)alloca(dwRankAllocSize);
1909     if (!SafeReadMemory (objData.ArrayBoundsPtr, bounds, dwRankAllocSize, NULL))
1910     {
1911         ExtOut("Failed to read bounds info from the array\n");        
1912         return S_OK;
1913     }
1914
1915     //length is only supported for single-dimension array
1916     if (objData.dwRank == 1 && flags.Length != (DWORD_PTR)-1)
1917     {
1918         bounds[0] = _min(bounds[0], (DWORD)(flags.Length + flags.startIndex) - lowerBounds[0]);
1919     }
1920     
1921     DWORD *indices = (DWORD *)alloca(dwRankAllocSize);
1922     for (i = 0; i < objData.dwRank; i++)
1923     {
1924         indices[i] = lowerBounds[i];
1925     }
1926
1927     //start index is only supported for single-dimension array
1928     if (objData.dwRank == 1)
1929     {
1930         indices[0] = (DWORD)flags.startIndex;
1931     }
1932     
1933     //Offset should be calculated by OffsetFromIndices. However because of the way 
1934     //how we grow indices, incrementing offset by one happens to match indices in every iteration    
1935     for (size_t offset = OffsetFromIndices (indices, lowerBounds, bounds, objData.dwRank);
1936         IndicesInRange (indices, lowerBounds, bounds, objData.dwRank); 
1937         indices[objData.dwRank - 1]++, offset++)
1938     {      
1939         if (IsInterrupt())
1940         {
1941             ExtOut("interrupted by user\n");
1942             break;
1943         }
1944
1945         TADDR elementAddress = TO_TADDR(objData.ArrayDataPtr + offset * objData.dwComponentSize);
1946         TADDR p_Element = NULL;
1947         if (isElementValueType)
1948         {
1949             p_Element = elementAddress;        
1950         }
1951         else if (!SafeReadMemory (elementAddress, &p_Element, sizeof (p_Element), NULL))
1952         {
1953             ExtOut("Failed to read element at ");        
1954             ExtOutIndices(indices, objData.dwRank);
1955             ExtOut("\n");
1956             continue;
1957         }
1958
1959         if (p_Element)
1960         {
1961             ExtOutIndices(indices, objData.dwRank);
1962
1963             if (isElementValueType)
1964             {
1965                 DMLOut( " %s\n", DMLValueClass(objData.MethodTable, p_Element));
1966             }
1967             else
1968             {
1969                 DMLOut(" %s\n", DMLObject(p_Element));
1970             }
1971         }
1972         else if (!isPermSetPrint)
1973         {
1974             ExtOutIndices(indices, objData.dwRank);
1975             ExtOut(" null\n");
1976         }
1977
1978         if (flags.bDetail)
1979         {
1980             IncrementIndent();
1981             if (isElementValueType)
1982             {
1983                 PrintVC(TO_TADDR(objData.ElementTypeHandle), elementAddress, !flags.bNoFieldsForElement);
1984             }
1985             else if (p_Element != NULL)
1986             {
1987                 PrintObj(p_Element, !flags.bNoFieldsForElement);
1988             }
1989             DecrementIndent();
1990         }
1991     }
1992     
1993     return S_OK;
1994 }
1995
1996 /**********************************************************************\
1997 * Routine Description:                                                 *
1998 *                                                                      *
1999 *    This function is called to dump the contents of an object from a  *  
2000 *    given address
2001 *                                                                      *
2002 \**********************************************************************/
2003 DECLARE_API(DumpObj)    
2004 {
2005     INIT_API();
2006
2007     MINIDUMP_NOT_SUPPORTED();    
2008
2009     BOOL dml = FALSE;
2010     BOOL bNoFields = FALSE;
2011     BOOL bRefs = FALSE;
2012     StringHolder str_Object;
2013     CMDOption option[] = 
2014     {   // name, vptr, type, hasValue
2015         {"-nofields", &bNoFields, COBOOL, FALSE},
2016         {"-refs", &bRefs, COBOOL, FALSE},
2017 #ifndef FEATURE_PAL
2018         {"/d", &dml, COBOOL, FALSE},
2019 #endif
2020     };
2021     CMDValue arg[] = 
2022     {   // vptr, type
2023         {&str_Object.data, COSTRING}
2024     };
2025     size_t nArg;
2026     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
2027     {
2028         return Status;
2029     }
2030     
2031     DWORD_PTR p_Object = GetExpression(str_Object.data);
2032     EnableDMLHolder dmlHolder(dml);
2033     if (p_Object == 0)
2034     {
2035         ExtOut("Invalid parameter %s\n", args);
2036         return Status;
2037     }
2038
2039     Status = PrintObj(p_Object, !bNoFields);
2040     
2041     if (SUCCEEDED(Status) && bRefs)
2042     {
2043         ExtOut("GC Refs:\n");
2044         TableOutput out(2, POINTERSIZE_HEX, AlignRight, 4);
2045         out.WriteRow("offset", "object");
2046         for (sos::RefIterator itr(TO_TADDR(p_Object)); itr; ++itr)
2047             out.WriteRow(Hex(itr.GetOffset()), ObjectPtr(*itr));
2048     }
2049     
2050     return Status;
2051 }
2052
2053 CLRDATA_ADDRESS isExceptionObj(CLRDATA_ADDRESS mtObj)
2054 {
2055     // We want to follow back until we get the mt for System.Exception
2056     DacpMethodTableData dmtd;
2057     CLRDATA_ADDRESS walkMT = mtObj;
2058     while(walkMT != NULL)
2059     {
2060         if (dmtd.Request(g_sos, walkMT) != S_OK)
2061         {
2062             break;            
2063         }
2064         if (walkMT == g_special_usefulGlobals.ExceptionMethodTable)
2065         {
2066             return walkMT;
2067         }
2068         walkMT = dmtd.ParentMethodTable;
2069     }
2070     return NULL;
2071 }
2072
2073 CLRDATA_ADDRESS isSecurityExceptionObj(CLRDATA_ADDRESS mtObj)
2074 {
2075     // We want to follow back until we get the mt for System.Exception
2076     DacpMethodTableData dmtd;
2077     CLRDATA_ADDRESS walkMT = mtObj;
2078     while(walkMT != NULL)
2079     {
2080         if (dmtd.Request(g_sos, walkMT) != S_OK)
2081         {
2082             break;            
2083         }
2084         NameForMT_s(TO_TADDR(walkMT), g_mdName, mdNameLen);                
2085         if (_wcscmp(W("System.Security.SecurityException"), g_mdName) == 0)
2086         {
2087             return walkMT;
2088         }
2089         walkMT = dmtd.ParentMethodTable;
2090     }
2091     return NULL;
2092 }
2093
2094 // Fill the passed in buffer with a text header for generated exception information.
2095 // Returns the number of characters in the wszBuffer array on exit.
2096 // If NULL is passed for wszBuffer, just returns the number of characters needed.
2097 size_t AddExceptionHeader (__out_ecount_opt(bufferLength) WCHAR *wszBuffer, size_t bufferLength)
2098 {
2099 #ifdef _TARGET_WIN64_
2100     const WCHAR *wszHeader = W("    SP               IP               Function\n");
2101 #else
2102     const WCHAR *wszHeader = W("    SP       IP       Function\n");
2103 #endif // _TARGET_WIN64_
2104     if (wszBuffer)
2105     {
2106         swprintf_s(wszBuffer, bufferLength, wszHeader);
2107     }
2108     return _wcslen(wszHeader);
2109 }
2110
2111 struct StackTraceElement 
2112 {
2113     UINT_PTR        ip;
2114     UINT_PTR        sp;
2115     DWORD_PTR       pFunc;  // MethodDesc
2116     // TRUE if this element represents the last frame of the foreign
2117     // exception stack trace.
2118     BOOL            fIsLastFrameFromForeignStackTrace;
2119
2120 };
2121
2122 #include "sos_stacktrace.h"
2123
2124 class StringOutput
2125 {
2126 public:
2127     CQuickString cs;
2128     StringOutput()
2129     {
2130         cs.Alloc(1024);
2131         cs.String()[0] = L'\0';
2132     }
2133
2134     BOOL Append(__in_z LPCWSTR pszStr)
2135     {
2136         size_t iInputLen = _wcslen (pszStr);        
2137         size_t iCurLen = _wcslen (cs.String());
2138         if ((iCurLen + iInputLen + 1) > cs.Size())
2139         {
2140             if (cs.ReSize(iCurLen + iInputLen + 1) != S_OK)
2141             {
2142                 return FALSE;
2143             }
2144         }
2145
2146         wcsncat_s (cs.String(), cs.Size(), pszStr, _TRUNCATE);
2147         return TRUE;
2148     }
2149     
2150     size_t Length()
2151     {
2152         return _wcslen(cs.String());
2153     }
2154
2155     WCHAR *String()
2156     {
2157         return cs.String();
2158     }
2159 };
2160
2161 static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp, ULONG64 IPAddr, StringOutput& so);
2162
2163 // Using heuristics to determine if an exception object represented an async (hardware) or a 
2164 // managed exception
2165 // We need to use these heuristics when the System.Exception object is not the active exception
2166 // on some thread, but it's something found somewhere on the managed heap.
2167
2168 // uses the MapWin32FaultToCOMPlusException to figure out how we map async exceptions
2169 // to managed exceptions and their HRESULTs
2170 static const HRESULT AsyncHResultValues[] =
2171 {
2172     COR_E_ARITHMETIC,    // kArithmeticException
2173     COR_E_OVERFLOW,      // kOverflowException
2174     COR_E_DIVIDEBYZERO,  // kDivideByZeroException
2175     COR_E_FORMAT,        // kFormatException
2176     COR_E_NULLREFERENCE, // kNullReferenceException
2177     E_POINTER,           // kAccessViolationException
2178     // the EE is raising the next exceptions more often than the OS will raise an async 
2179     // exception for these conditions, so in general treat these as Synchronous
2180       // COR_E_INDEXOUTOFRANGE, // kIndexOutOfRangeException
2181       // COR_E_OUTOFMEMORY,   // kOutOfMemoryException
2182       // COR_E_STACKOVERFLOW, // kStackOverflowException
2183     COR_E_DATAMISALIGNED, // kDataMisalignedException
2184     
2185 };
2186 BOOL IsAsyncException(TADDR taObj, TADDR mtObj)
2187 {
2188     // by default we'll treat exceptions as synchronous
2189     UINT32 xcode = EXCEPTION_COMPLUS;
2190     int iOffset = GetObjFieldOffset (taObj, mtObj, W("_xcode"));
2191     if (iOffset > 0)
2192     {
2193         HRESULT hr = MOVE(xcode, taObj + iOffset);
2194         if (hr != S_OK)
2195         {
2196             xcode = EXCEPTION_COMPLUS;
2197             goto Done;
2198         }
2199     }
2200
2201     if (xcode == EXCEPTION_COMPLUS)
2202     {
2203         HRESULT ehr = 0;
2204         iOffset = GetObjFieldOffset (taObj, mtObj, W("_HResult"));
2205         if (iOffset > 0)
2206         {
2207             HRESULT hr = MOVE(ehr, taObj + iOffset);
2208             if (hr != S_OK)
2209             {
2210                 xcode = EXCEPTION_COMPLUS;
2211                 goto Done;
2212             }
2213             for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
2214             {
2215                 if (ehr == AsyncHResultValues[idx])
2216                 {
2217                     xcode = ehr;
2218                     break;
2219                 }
2220             }
2221         }
2222     }
2223 Done:
2224     return xcode != EXCEPTION_COMPLUS;
2225 }
2226
2227 // Overload that mirrors the code above when the ExceptionObjectData was already retrieved from LS
2228 BOOL IsAsyncException(const DacpExceptionObjectData & excData)
2229 {
2230     if (excData.XCode != EXCEPTION_COMPLUS)
2231         return TRUE;
2232
2233     HRESULT ehr = excData.HResult;
2234     for (size_t idx = 0; idx < _countof(AsyncHResultValues); ++idx)
2235     {
2236         if (ehr == AsyncHResultValues[idx])
2237         {
2238             return TRUE;
2239         }
2240     }
2241
2242     return FALSE;
2243 }
2244
2245
2246 #define SOS_STACKTRACE_SHOWEXPLICITFRAMES  0x00000002
2247 size_t FormatGeneratedException (DWORD_PTR dataPtr, 
2248     UINT bytes, 
2249     __out_ecount_opt(bufferLength) WCHAR *wszBuffer, 
2250     size_t bufferLength, 
2251     BOOL bAsync,
2252     BOOL bNestedCase = FALSE,
2253     BOOL bLineNumbers = FALSE)
2254 {
2255     UINT count = bytes / sizeof(StackTraceElement);
2256     size_t Length = 0;
2257
2258     if (wszBuffer && bufferLength > 0)
2259     {
2260         wszBuffer[0] = L'\0';
2261     }
2262     
2263     // Buffer is calculated for sprintf below ("   %p %p %S\n");
2264     WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2 + MAX_LONGPATH + 8];
2265
2266     if (count == 0)
2267     {
2268         return 0;
2269     }
2270     
2271     if (bNestedCase)
2272     {
2273         // If we are computing the call stack for a nested exception, we
2274         // don't want to print the last frame, because the outer exception
2275         // will have that frame.
2276         count--;
2277     }
2278     
2279     for (UINT i = 0; i < count; i++)
2280     {
2281         StackTraceElement ste;
2282         MOVE (ste, dataPtr + i*sizeof(StackTraceElement));
2283
2284         // ste.ip must be adjusted because of an ancient workaround in the exception 
2285         // infrastructure. The workaround is that the exception needs to have
2286         // an ip address that will map to the line number where the exception was thrown.
2287         // (It doesn't matter that it's not a valid instruction). (see /vm/excep.cpp)
2288         //
2289         // This "counterhack" is not 100% accurate
2290         // The biggest issue is that !PrintException must work with exception objects 
2291         // that may not be currently active; as a consequence we cannot rely on the 
2292         // state of some "current thread" to infer whether the IP values stored in 
2293         // the exception object have been adjusted or not. If we could, we may examine 
2294         // the topmost "Frame" and make the decision based on whether it's a 
2295         // FaultingExceptionFrame or not.
2296         // 1. On IA64 the IP values are never adjusted by the EE so there's nothing 
2297         //    to adjust back.
2298         // 2. On AMD64:
2299         //    (a) if the exception was an async (hardware) exception add 1 to all 
2300         //        IP values in the exception object
2301         //    (b) if the exception was a managed exception (either raised by the 
2302         //        EE or thrown by managed code) do not adjust any IP values
2303         // 3. On X86:
2304         //    (a) if the exception was an async (hardware) exception add 1 to 
2305         //        all but the topmost IP value in the exception object
2306         //    (b) if the exception was a managed exception (either raised by 
2307         //        the EE or thrown by managed code) add 1 to all IP values in 
2308         //        the exception object
2309 #if defined(_TARGET_AMD64_)
2310         if (bAsync)
2311         {
2312             ste.ip += 1;
2313         }
2314 #elif defined(_TARGET_X86_)
2315         if (IsDbgTargetX86() && (!bAsync || i != 0))
2316         {
2317             ste.ip += 1;
2318         }
2319 #endif // defined(_TARGET_AMD64_) || defined(_TARGET__X86_)
2320
2321         StringOutput so;
2322         HRESULT Status = DumpMDInfoBuffer(ste.pFunc, SOS_STACKTRACE_SHOWADDRESSES|SOS_STACKTRACE_SHOWEXPLICITFRAMES, ste.sp, ste.ip, so);
2323
2324         // If DumpMDInfoBuffer failed (due to out of memory or missing metadata), 
2325         // or did not update so (when ste is an explicit frames), do not update wszBuffer
2326         if (Status == S_OK)
2327         {
2328             WCHAR filename[MAX_LONGPATH] = W("");
2329             ULONG linenum = 0;
2330             if (bLineNumbers && 
2331                 SUCCEEDED(GetLineByOffset(TO_CDADDR(ste.ip), &linenum, filename, _countof(filename))))
2332             {
2333                 swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W("    %s [%s @ %d]\n"), so.String(), filename, linenum);
2334             }
2335             else
2336             {
2337                 swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W("    %s\n"), so.String());
2338             }
2339
2340             Length += _wcslen(wszLineBuffer);
2341
2342             if (wszBuffer)
2343             {
2344                 wcsncat_s(wszBuffer, bufferLength, wszLineBuffer, _TRUNCATE);
2345             }
2346         }
2347     }
2348
2349     return Length;
2350 }
2351
2352 // ExtOut has an internal limit for the string size
2353 void SosExtOutLargeString(__inout_z __inout_ecount_opt(len) WCHAR * pwszLargeString, size_t len)
2354 {
2355     const size_t chunkLen = 2048;
2356
2357     WCHAR *pwsz = pwszLargeString;  // beginning of a chunk
2358     size_t count = len/chunkLen;
2359     // write full chunks
2360     for (size_t idx = 0; idx < count; ++idx)
2361     {
2362         WCHAR *pch = pwsz + chunkLen; // after the chunk
2363         // zero terminate the chunk
2364         WCHAR ch = *pch;
2365         *pch = L'\0';
2366
2367         ExtOut("%S", pwsz);
2368
2369         // restore whacked char
2370         *pch = ch;
2371
2372         // advance to next chunk
2373         pwsz += chunkLen;
2374     }
2375
2376     // last chunk
2377     ExtOut("%S", pwsz);
2378 }
2379
2380 HRESULT FormatException(TADDR taObj, BOOL bLineNumbers = FALSE)
2381 {
2382     HRESULT Status = S_OK;
2383
2384     DacpObjectData objData;
2385     if ((Status=objData.Request(g_sos, TO_CDADDR(taObj))) != S_OK)
2386     {        
2387         ExtOut("Invalid object\n");
2388         return Status;
2389     }
2390
2391     // Make sure it is an exception object, and get the MT of Exception
2392     CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable);
2393     if (exceptionMT == NULL)
2394     {
2395         ExtOut("Not a valid exception object\n");
2396         return Status;
2397     }
2398
2399     DMLOut("Exception object: %s\n", DMLObject(taObj));
2400     
2401     if (NameForMT_s(TO_TADDR(objData.MethodTable), g_mdName, mdNameLen))
2402     {
2403         ExtOut("Exception type:   %S\n", g_mdName);
2404     }
2405     else
2406     {
2407         ExtOut("Exception type:   <Unknown>\n");
2408     }
2409
2410     // Print basic info
2411
2412     // First try to get exception object data using ISOSDacInterface2
2413     DacpExceptionObjectData excData;
2414     BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, TO_CDADDR(taObj)));
2415
2416     // Walk the fields, printing some fields in a special way.
2417     // HR, InnerException, Message, StackTrace, StackTraceString
2418
2419     {
2420         TADDR taMsg = 0;
2421         if (bGotExcData)
2422         {
2423             taMsg = TO_TADDR(excData.Message);
2424         }
2425         else
2426         {
2427             int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, W("_message"));
2428             if (iOffset > 0)
2429             {
2430                 MOVE (taMsg, taObj + iOffset);
2431             }
2432         }
2433
2434         ExtOut("Message:          ");
2435
2436         if (taMsg)
2437             StringObjectContent(taMsg);
2438         else
2439             ExtOut("<none>");
2440
2441         ExtOut("\n");
2442     }
2443
2444     {
2445         TADDR taInnerExc = 0;
2446         if (bGotExcData)
2447         {
2448             taInnerExc = TO_TADDR(excData.InnerException);
2449         }
2450         else
2451         {
2452             int iOffset = GetObjFieldOffset(taObj, objData.MethodTable, W("_innerException"));
2453             if (iOffset > 0)
2454             {
2455                 MOVE (taInnerExc, taObj + iOffset);
2456             }
2457         }
2458
2459         ExtOut("InnerException:   ");
2460         if (taInnerExc)
2461         {
2462             TADDR taMT;
2463             if (SUCCEEDED(GetMTOfObject(taInnerExc, &taMT)))
2464             {
2465                 NameForMT_s(taMT, g_mdName, mdNameLen);                
2466                 ExtOut("%S, ", g_mdName);
2467                 if (IsDMLEnabled())
2468                     DMLOut("Use <exec cmd=\"!PrintException /d %p\">!PrintException %p</exec> to see more.\n", taInnerExc, taInnerExc);
2469                 else
2470                     ExtOut("Use !PrintException %p to see more.\n", SOS_PTR(taInnerExc));
2471             }
2472             else
2473             {
2474                 ExtOut("<invalid MethodTable of inner exception>");
2475             }
2476         }
2477         else
2478         {
2479             ExtOut("<none>\n");
2480         }
2481     }
2482
2483     BOOL bAsync = bGotExcData ? IsAsyncException(excData)
2484                               : IsAsyncException(taObj, TO_TADDR(objData.MethodTable));
2485
2486     {
2487         TADDR taStackTrace = 0;
2488         if (bGotExcData)
2489         {
2490             taStackTrace = TO_TADDR(excData.StackTrace);
2491         }
2492         else
2493         {
2494             int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_stackTrace"));
2495             if (iOffset > 0)        
2496             {
2497                 MOVE(taStackTrace, taObj + iOffset);
2498             }
2499         }
2500
2501         ExtOut("StackTrace (generated):\n");
2502         if (taStackTrace)
2503         {
2504             DWORD arrayLen;
2505             HRESULT hr = MOVE(arrayLen, taStackTrace + sizeof(DWORD_PTR));
2506
2507             if (arrayLen != 0 && hr == S_OK)
2508             {
2509 #ifdef _TARGET_WIN64_
2510                 DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD);
2511 #else
2512                 DWORD_PTR dataPtr = taStackTrace + sizeof(DWORD_PTR) + sizeof(DWORD);
2513 #endif // _TARGET_WIN64_
2514                 size_t stackTraceSize = 0;
2515                 MOVE (stackTraceSize, dataPtr);
2516
2517                 DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement));
2518                 dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data
2519             
2520                 if (stackTraceSize == 0)
2521                 {
2522                     ExtOut("Unable to decipher generated stack trace\n");
2523                 }
2524                 else
2525                 {
2526                     size_t iHeaderLength = AddExceptionHeader (NULL, 0);
2527                     size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, FALSE, bLineNumbers);
2528                     WCHAR *pwszBuffer = new NOTHROW WCHAR[iHeaderLength + iLength + 1];
2529                     if (pwszBuffer)
2530                     {
2531                         AddExceptionHeader(pwszBuffer, iHeaderLength + 1);
2532                         FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer + iHeaderLength, iLength + 1, bAsync, FALSE, bLineNumbers);
2533                         SosExtOutLargeString(pwszBuffer, iHeaderLength + iLength + 1);
2534                         delete[] pwszBuffer;
2535                     }
2536                     ExtOut("\n");
2537                 }
2538             }
2539             else
2540             {
2541                 ExtOut("<Not Available>\n");
2542             }
2543         }                   
2544         else
2545         {
2546             ExtOut("<none>\n");
2547         }
2548     }
2549
2550     {
2551         TADDR taStackString;
2552         if (bGotExcData)
2553         {
2554             taStackString = TO_TADDR(excData.StackTraceString);
2555         }
2556         else
2557         {
2558             int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_stackTraceString"));
2559             MOVE (taStackString, taObj + iOffset);
2560         }
2561
2562         ExtOut("StackTraceString: ");
2563         if (taStackString)
2564         {
2565             StringObjectContent(taStackString);
2566             ExtOut("\n\n"); // extra newline looks better
2567         }
2568         else
2569         {
2570             ExtOut("<none>\n");
2571         }
2572     }
2573
2574     {
2575         DWORD hResult;
2576         if (bGotExcData)
2577         {
2578             hResult = excData.HResult;
2579         }
2580         else
2581         {
2582             int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("_HResult"));
2583             MOVE (hResult, taObj + iOffset);
2584         }
2585
2586         ExtOut("HResult: %lx\n", hResult);
2587     }
2588
2589     if (isSecurityExceptionObj(objData.MethodTable) != NULL)
2590     {
2591         // We have a SecurityException Object: print out the debugString if present
2592         int iOffset = GetObjFieldOffset (taObj, objData.MethodTable, W("m_debugString"));
2593         if (iOffset > 0)        
2594         {
2595             TADDR taDebugString;
2596             MOVE (taDebugString, taObj + iOffset);                
2597             
2598             if (taDebugString)
2599             {
2600                 ExtOut("SecurityException Message: ");
2601                 StringObjectContent(taDebugString);
2602                 ExtOut("\n\n"); // extra newline looks better
2603             }
2604         }            
2605     }
2606
2607     return Status;
2608 }
2609
2610 DECLARE_API(PrintException)
2611 {
2612     INIT_API();
2613     
2614     BOOL dml = FALSE;
2615     BOOL bShowNested = FALSE;   
2616     BOOL bLineNumbers = FALSE;
2617     BOOL bCCW = FALSE;
2618     StringHolder strObject;
2619     CMDOption option[] = 
2620     {   // name, vptr, type, hasValue
2621         {"-nested", &bShowNested, COBOOL, FALSE},
2622         {"-lines", &bLineNumbers, COBOOL, FALSE},
2623         {"-l", &bLineNumbers, COBOOL, FALSE},
2624         {"-ccw", &bCCW, COBOOL, FALSE},
2625 #ifndef FEATURE_PAL
2626         {"/d", &dml, COBOOL, FALSE}
2627 #endif
2628     };
2629     CMDValue arg[] = 
2630     {   // vptr, type
2631         {&strObject, COSTRING}
2632     };
2633     size_t nArg;
2634     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
2635     {
2636         return Status;
2637     }
2638     if (bLineNumbers)
2639     {
2640         ULONG symlines = 0;
2641         if (SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
2642         {
2643             symlines &= SYMOPT_LOAD_LINES;
2644         }
2645         if (symlines == 0)
2646         {
2647             ExtOut("In order for the option -lines to enable display of source information\n"
2648                    "the debugger must be configured to load the line number information from\n"
2649                    "the symbol files. Use the \".lines; .reload\" command to achieve this.\n");
2650             // don't even try
2651             bLineNumbers = FALSE;
2652         }
2653     }
2654
2655     EnableDMLHolder dmlHolder(dml);
2656     DWORD_PTR p_Object = NULL;
2657     if (nArg == 0)
2658     {
2659         if (bCCW)
2660         {
2661             ExtOut("No CCW pointer specified\n");
2662             return Status;
2663         }
2664
2665         // Look at the last exception object on this thread
2666
2667         CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
2668         DacpThreadData Thread;
2669         
2670         if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
2671         {
2672             ExtOut("The current thread is unmanaged\n");
2673             return Status;
2674         }
2675
2676         DWORD_PTR dwAddr = NULL;
2677         if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
2678                             &dwAddr,
2679                             sizeof(dwAddr), NULL)) || (dwAddr==NULL))
2680         {
2681             ExtOut("There is no current managed exception on this thread\n");            
2682         }    
2683         else
2684         {        
2685             p_Object = dwAddr;        
2686         }
2687     }
2688     else
2689     {
2690         p_Object = GetExpression(strObject.data);
2691         if (p_Object == 0)
2692         {
2693             if (bCCW)
2694             {
2695                 ExtOut("Invalid CCW pointer %s\n", args);
2696             }
2697             else
2698             {
2699                 ExtOut("Invalid exception object %s\n", args);
2700             }
2701             return Status;
2702         }
2703
2704         if (bCCW)
2705         {
2706             // check if the address is a CCW pointer and then
2707             // get the exception object from it
2708             DacpCCWData ccwData;
2709             if (ccwData.Request(g_sos, p_Object) == S_OK)
2710             {
2711                 p_Object = TO_TADDR(ccwData.managedObject);
2712             }
2713         }
2714     }
2715
2716     if (p_Object)
2717     {
2718         FormatException(p_Object, bLineNumbers);
2719     }
2720
2721     // Are there nested exceptions?
2722     CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
2723     DacpThreadData Thread;
2724     
2725     if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
2726     {
2727         ExtOut("The current thread is unmanaged\n");
2728         return Status;
2729     }
2730
2731     if (Thread.firstNestedException)
2732     {
2733         if (!bShowNested)
2734         {
2735             ExtOut("There are nested exceptions on this thread. Run with -nested for details\n");
2736             return Status;
2737         }
2738         
2739         CLRDATA_ADDRESS currentNested = Thread.firstNestedException;
2740         do
2741         {
2742             CLRDATA_ADDRESS obj = 0, next = 0;
2743             Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next);
2744
2745             if (Status != S_OK)
2746             {
2747                 ExtOut("Error retrieving nested exception info %p\n", SOS_PTR(currentNested));
2748                 return Status;
2749             }
2750
2751             if (IsInterrupt())
2752             {
2753                 ExtOut("<aborted>\n");
2754                 return Status;
2755             }
2756
2757             ExtOut("\nNested exception -------------------------------------------------------------\n");
2758             Status = FormatException((DWORD_PTR) obj, bLineNumbers);
2759             if (Status != S_OK)
2760             {
2761                 return Status;
2762             }
2763             
2764             currentNested = next;
2765         }
2766         while(currentNested != NULL);        
2767     }
2768     return Status;
2769 }
2770
2771 /**********************************************************************\
2772 * Routine Description:                                                 *
2773 *                                                                      *
2774 *    This function is called to dump the contents of an object from a  *  
2775 *    given address
2776 *                                                                      *
2777 \**********************************************************************/
2778 DECLARE_API(DumpVC)
2779 {
2780     INIT_API();
2781     MINIDUMP_NOT_SUPPORTED();    
2782     
2783     DWORD_PTR p_MT = NULL;
2784     DWORD_PTR p_Object = NULL;
2785     BOOL dml = FALSE;
2786
2787     CMDOption option[] = 
2788     {   // name, vptr, type, hasValue
2789 #ifndef FEATURE_PAL
2790         {"/d", &dml, COBOOL, FALSE}
2791 #endif
2792     };
2793     CMDValue arg[] = 
2794     {   // vptr, type
2795         {&p_MT, COHEX},
2796         {&p_Object, COHEX},
2797     };
2798     size_t nArg;
2799     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
2800     {
2801         return Status;
2802     }
2803
2804     EnableDMLHolder dmlHolder(dml);
2805     if (nArg!=2)
2806     {
2807         ExtOut("Usage: !DumpVC <Method Table> <Value object start addr>\n");
2808         return Status;
2809     }
2810     
2811     if (!IsMethodTable(p_MT))
2812     {
2813         ExtOut("Not a managed object\n");
2814         return S_OK;
2815     } 
2816
2817     return PrintVC(p_MT, p_Object);
2818 }
2819
2820 #ifndef FEATURE_PAL
2821
2822 #ifdef FEATURE_COMINTEROP
2823
2824 DECLARE_API(DumpRCW)
2825 {
2826     INIT_API();
2827     
2828     BOOL dml = FALSE;
2829     StringHolder strObject;
2830
2831     CMDOption option[] = 
2832     {   // name, vptr, type, hasValue
2833         {"/d", &dml, COBOOL, FALSE}
2834     };
2835     CMDValue arg[] = 
2836     {   // vptr, type
2837         {&strObject, COSTRING}
2838     };
2839     size_t nArg;
2840     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
2841     {
2842         return Status;
2843     }
2844
2845     EnableDMLHolder dmlHolder(dml);
2846     if (nArg == 0)
2847     {
2848         ExtOut("Missing RCW address\n");
2849         return Status;
2850     }
2851     else
2852     {
2853         DWORD_PTR p_RCW = GetExpression(strObject.data);
2854         if (p_RCW == 0)
2855         {
2856             ExtOut("Invalid RCW %s\n", args);
2857         }
2858         else
2859         {
2860             DacpRCWData rcwData;
2861             if ((Status = rcwData.Request(g_sos, p_RCW)) != S_OK)
2862             {
2863                 ExtOut("Error requesting RCW data\n");
2864                 return Status;
2865             }
2866             BOOL isDCOMProxy;
2867             if (FAILED(rcwData.IsDCOMProxy(g_sos, p_RCW, &isDCOMProxy)))
2868             {
2869                 isDCOMProxy = FALSE;
2870             }
2871
2872             DMLOut("Managed object:             %s\n", DMLObject(rcwData.managedObject));
2873             DMLOut("Creating thread:            %p\n", SOS_PTR(rcwData.creatorThread));
2874             ExtOut("IUnknown pointer:           %p\n", SOS_PTR(rcwData.unknownPointer));
2875             ExtOut("COM Context:                %p\n", SOS_PTR(rcwData.ctxCookie));
2876             ExtOut("Managed ref count:          %d\n", rcwData.refCount);
2877             ExtOut("IUnknown V-table pointer :  %p (captured at RCW creation time)\n", SOS_PTR(rcwData.vtablePtr));
2878
2879             ExtOut("Flags:                      %s%s%s%s%s%s%s%s\n", 
2880                 (rcwData.isDisconnected? "IsDisconnected " : ""),
2881                 (rcwData.supportsIInspectable? "SupportsIInspectable " : ""),
2882                 (rcwData.isAggregated? "IsAggregated " : ""),
2883                 (rcwData.isContained? "IsContained " : ""),
2884                 (rcwData.isJupiterObject? "IsJupiterObject " : ""),
2885                 (rcwData.isFreeThreaded? "IsFreeThreaded " : ""),
2886                 (rcwData.identityPointer == TO_CDADDR(p_RCW)? "IsUnique " : ""),
2887                 (isDCOMProxy ? "IsDCOMProxy " : "")
2888                 );
2889
2890             // Jupiter data hidden by default
2891             if (rcwData.isJupiterObject)
2892             {
2893                 ExtOut("IJupiterObject:    %p\n", SOS_PTR(rcwData.jupiterObject));            
2894             }
2895             
2896             ExtOut("COM interface pointers:\n");
2897
2898             ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[rcwData.interfaceCount];
2899             if (pArray == NULL)
2900             {
2901                 ReportOOM();            
2902                 return Status;
2903             }
2904
2905             if ((Status = g_sos->GetRCWInterfaces(p_RCW, rcwData.interfaceCount, pArray, NULL)) != S_OK)
2906             {
2907                 ExtOut("Error requesting COM interface pointers\n");
2908                 return Status;
2909             }
2910
2911             ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "Context", "MT");
2912             for (int i = 0; i < rcwData.interfaceCount; i++)
2913             {
2914                 // Ignore any NULL MethodTable interface cache. At this point only IJupiterObject 
2915                 // is saved as NULL MethodTable at first slot, and we've already printed outs its 
2916                 // value earlier.
2917                 if (pArray[i].methodTable == NULL)
2918                     continue;
2919                 
2920                 NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen);
2921                 
2922                 DMLOut("%p %p %s %S\n", SOS_PTR(pArray[i].interfacePtr), SOS_PTR(pArray[i].comContext), DMLMethodTable(pArray[i].methodTable), g_mdName);
2923             }
2924         }
2925     }
2926
2927     return Status;
2928 }
2929
2930 DECLARE_API(DumpCCW)
2931 {
2932     INIT_API();
2933     
2934     BOOL dml = FALSE;
2935     StringHolder strObject;
2936
2937     CMDOption option[] = 
2938     {   // name, vptr, type, hasValue
2939         {"/d", &dml, COBOOL, FALSE}
2940     };
2941     CMDValue arg[] = 
2942     {   // vptr, type
2943         {&strObject, COSTRING}
2944     };
2945     size_t nArg;
2946     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
2947     {
2948         return Status;
2949     }
2950
2951     EnableDMLHolder dmlHolder(dml);
2952     if (nArg == 0)
2953     {
2954         ExtOut("Missing CCW address\n");
2955         return Status;
2956     }
2957     else
2958     {
2959         DWORD_PTR p_CCW = GetExpression(strObject.data);
2960         if (p_CCW == 0)
2961         {
2962             ExtOut("Invalid CCW %s\n", args);
2963         }
2964         else
2965         {
2966             DacpCCWData ccwData;
2967             if ((Status = ccwData.Request(g_sos, p_CCW)) != S_OK)
2968             {
2969                 ExtOut("Error requesting CCW data\n");
2970                 return Status;
2971             }
2972
2973             if (ccwData.ccwAddress != p_CCW)
2974                 ExtOut("CCW:               %p\n", SOS_PTR(ccwData.ccwAddress));
2975             
2976             DMLOut("Managed object:    %s\n", DMLObject(ccwData.managedObject));
2977             ExtOut("Outer IUnknown:    %p\n", SOS_PTR(ccwData.outerIUnknown));
2978             ExtOut("Ref count:         %d%s\n", ccwData.refCount, ccwData.isNeutered ? " (NEUTERED)" : "");
2979             ExtOut("Flags:             %s%s\n",
2980                 (ccwData.isExtendsCOMObject? "IsExtendsCOMObject " : ""),
2981                 (ccwData.isAggregated? "IsAggregated " : "")
2982                 );
2983                 
2984             // Jupiter information hidden by default
2985             if (ccwData.jupiterRefCount > 0)
2986             {
2987                 ExtOut("Jupiter ref count: %d%s%s%s%s\n", 
2988                     ccwData.jupiterRefCount, 
2989                     (ccwData.isPegged || ccwData.isGlobalPegged) ? ", Pegged by" : "",
2990                     ccwData.isPegged ? " Jupiter " : "",
2991                     (ccwData.isPegged && ccwData.isGlobalPegged) ? "&" : "",
2992                     ccwData.isGlobalPegged ? " CLR " : ""
2993                     );
2994             }
2995             
2996             ExtOut("RefCounted Handle: %p%s\n", 
2997                 SOS_PTR(ccwData.handle), 
2998                 (ccwData.hasStrongRef ? " (STRONG)" : " (WEAK)"));
2999
3000             ExtOut("COM interface pointers:\n");            
3001
3002             ArrayHolder<DacpCOMInterfacePointerData> pArray = new NOTHROW DacpCOMInterfacePointerData[ccwData.interfaceCount];
3003             if (pArray == NULL)
3004             {
3005                 ReportOOM();            
3006                 return Status;
3007             }
3008
3009             if ((Status = g_sos->GetCCWInterfaces(p_CCW, ccwData.interfaceCount, pArray, NULL)) != S_OK)
3010             {
3011                 ExtOut("Error requesting COM interface pointers\n");
3012                 return Status;
3013             }
3014
3015             ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s Type\n", "IP", "MT", "Type");
3016             for (int i = 0; i < ccwData.interfaceCount; i++)
3017             {
3018                 if (pArray[i].methodTable == NULL)
3019                 {
3020                     wcscpy_s(g_mdName, mdNameLen, W("IDispatch/IUnknown"));
3021                 }
3022                 else
3023                 {
3024                     NameForMT_s(TO_TADDR(pArray[i].methodTable), g_mdName, mdNameLen);
3025                 }
3026                 
3027                 DMLOut("%p %s %S\n", pArray[i].interfacePtr, DMLMethodTable(pArray[i].methodTable), g_mdName);
3028             }
3029         }
3030     }
3031
3032     return Status;
3033 }
3034
3035 #endif // FEATURE_COMINTEROP
3036
3037 #ifdef _DEBUG
3038 /**********************************************************************\
3039 * Routine Description:                                                 *
3040 *                                                                      *
3041 *    This function is called to dump the contents of a PermissionSet   *
3042 *    from a given address.                                             * 
3043 *                                                                      *
3044 \**********************************************************************/
3045 /* 
3046     COMMAND: dumppermissionset.
3047     !DumpPermissionSet <PermissionSet object address>
3048
3049     This command allows you to examine a PermissionSet object. Note that you can 
3050     also use DumpObj such an object in greater detail. DumpPermissionSet attempts 
3051     to extract all the relevant information from a PermissionSet that you might be 
3052     interested in when performing Code Access Security (CAS) related debugging.
3053
3054     Here is a simple PermissionSet object:
3055
3056     0:000> !DumpPermissionSet 014615f4 
3057     PermissionSet object: 014615f4
3058     Unrestricted: TRUE
3059
3060     Note that this is an unrestricted PermissionSet object that does not contain 
3061     any individual permissions. 
3062
3063     Here is another example of a PermissionSet object, one that is not unrestricted 
3064     and contains a single permission:
3065
3066     0:003> !DumpPermissionSet 01469fa8 
3067     PermissionSet object: 01469fa8
3068     Unrestricted: FALSE
3069     Name: System.Security.Permissions.ReflectionPermission
3070     MethodTable: 5b731308
3071     EEClass: 5b7e0d78
3072     Size: 12(0xc) bytes
3073      (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.
3074     0.0__b77a5c561934e089\mscorlib.dll)
3075
3076     Fields:
3077           MT    Field   Offset                 Type VT     Attr    Value Name
3078     5b73125c  4001d66        4         System.Int32  0 instance        2 m_flags
3079
3080     Here is another example of an unrestricted PermissionSet, one that contains 
3081     several permissions. The numbers in parentheses before each Permission object 
3082     represents the index of that Permission in the PermissionSet.
3083
3084     0:003> !DumpPermissionSet 01467bd8
3085     PermissionSet object: 01467bd8
3086     Unrestricted: FALSE
3087     [1] 01467e90
3088         Name: System.Security.Permissions.FileDialogPermission
3089         MethodTable: 5b73023c
3090         EEClass: 5b7dfb18
3091         Size: 12(0xc) bytes
3092          (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
3093         Fields:
3094               MT    Field   Offset                 Type VT     Attr    Value Name
3095         5b730190  4001cc2        4         System.Int32  0 instance        1 access
3096     [4] 014682a8
3097         Name: System.Security.Permissions.ReflectionPermission
3098         MethodTable: 5b731308
3099         EEClass: 5b7e0d78
3100         Size: 12(0xc) bytes
3101          (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
3102         Fields:
3103               MT    Field   Offset                 Type VT     Attr    Value Name
3104         5b73125c  4001d66        4         System.Int32  0 instance        0 m_flags
3105     [17] 0146c060
3106         Name: System.Diagnostics.EventLogPermission
3107         MethodTable: 569841c4
3108         EEClass: 56a03e5c
3109         Size: 28(0x1c) bytes
3110          (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3111         Fields:
3112               MT    Field   Offset                 Type VT     Attr    Value Name
3113         5b6d65d4  4003078        4      System.Object[]  0 instance 0146c190 tagNames
3114         5b6c9ed8  4003079        8          System.Type  0 instance 0146c17c permissionAccessType
3115         5b6cd928  400307a       10       System.Boolean  0 instance        0 isUnrestricted
3116         5b6c45f8  400307b        c ...ections.Hashtable  0 instance 0146c1a4 rootTable
3117         5b6c090c  4003077      bfc        System.String  0   static 00000000 computerName
3118         56984434  40030e7       14 ...onEntryCollection  0 instance 00000000 innerCollection
3119     [18] 0146ceb4
3120         Name: System.Net.WebPermission
3121         MethodTable: 5696dfc4
3122         EEClass: 569e256c
3123         Size: 20(0x14) bytes
3124          (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3125         Fields:
3126               MT    Field   Offset                 Type VT     Attr    Value Name
3127         5b6cd928  400238e        c       System.Boolean  0 instance        0 m_Unrestricted
3128         5b6cd928  400238f        d       System.Boolean  0 instance        0 m_UnrestrictedConnect
3129         5b6cd928  4002390        e       System.Boolean  0 instance        0 m_UnrestrictedAccept
3130         5b6c639c  4002391        4 ...ections.ArrayList  0 instance 0146cf3c m_connectList
3131         5b6c639c  4002392        8 ...ections.ArrayList  0 instance 0146cf54 m_acceptList
3132         569476f8  4002393      8a4 ...Expressions.Regex  0   static 00000000 s_MatchAllRegex
3133     [19] 0146a5fc
3134         Name: System.Net.DnsPermission
3135         MethodTable: 56966408
3136         EEClass: 569d3c08
3137         Size: 12(0xc) bytes
3138          (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3139         Fields:
3140               MT    Field   Offset                 Type VT     Attr    Value Name
3141         5b6cd928  4001d2c        4       System.Boolean  0 instance        1 m_noRestriction
3142     [20] 0146d8ec
3143         Name: System.Web.AspNetHostingPermission
3144         MethodTable: 569831bc
3145         EEClass: 56a02ccc
3146         Size: 12(0xc) bytes
3147          (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3148         Fields:
3149               MT    Field   Offset                 Type VT     Attr    Value Name
3150         56983090  4003074        4         System.Int32  0 instance      600 _level
3151     [21] 0146e394
3152         Name: System.Net.NetworkInformation.NetworkInformationPermission
3153         MethodTable: 5697ac70
3154         EEClass: 569f7104
3155         Size: 16(0x10) bytes
3156          (C:\WINDOWS\Microsoft.NET\Framework\v2.0.x86chk\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089\System.dll)
3157         Fields:
3158               MT    Field   Offset                 Type VT     Attr    Value Name
3159         5697ab38  4002c34        4         System.Int32  0 instance        0 access
3160         5b6cd928  4002c35        8       System.Boolean  0 instance        0 unrestricted
3161
3162
3163     The abbreviation !dps can be used for brevity.
3164
3165     \\
3166 */
3167 DECLARE_API(DumpPermissionSet)
3168 {
3169     INIT_API();
3170     MINIDUMP_NOT_SUPPORTED();    
3171     
3172     DWORD_PTR p_Object = NULL;
3173
3174     CMDValue arg[] = 
3175     {
3176         {&p_Object, COHEX}
3177     };
3178     size_t nArg;
3179     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) 
3180     {
3181         return Status;
3182     }
3183     if (nArg!=1)
3184     {
3185         ExtOut("Usage: !DumpPermissionSet <PermissionSet object addr>\n");
3186         return Status;
3187     }
3188     
3189
3190     return PrintPermissionSet(p_Object);
3191 }
3192
3193 #endif // _DEBUG
3194
3195 void GCPrintGenerationInfo(DacpGcHeapDetails &heap);
3196 void GCPrintSegmentInfo(DacpGcHeapDetails &heap, DWORD_PTR &total_size);
3197
3198 #endif // FEATURE_PAL
3199
3200 void DisplayInvalidStructuresMessage()
3201 {
3202     ExtOut("The garbage collector data structures are not in a valid state for traversal.\n");
3203     ExtOut("It is either in the \"plan phase,\" where objects are being moved around, or\n");
3204     ExtOut("we are at the initialization or shutdown of the gc heap. Commands related to \n");
3205     ExtOut("displaying, finding or traversing objects as well as gc heap segments may not \n");
3206     ExtOut("work properly. !dumpheap and !verifyheap may incorrectly complain of heap \n");
3207     ExtOut("consistency errors.\n");
3208 }
3209
3210 /**********************************************************************\
3211 * Routine Description:                                                 *
3212 *                                                                      *
3213 *    This function dumps GC heap size.                                 *  
3214 *                                                                      *
3215 \**********************************************************************/
3216 DECLARE_API(EEHeap)
3217 {
3218     INIT_API();
3219     MINIDUMP_NOT_SUPPORTED();    
3220     
3221     BOOL dml = FALSE;
3222     BOOL showgc = FALSE;
3223     BOOL showloader = FALSE;
3224
3225     CMDOption option[] = 
3226     {   // name, vptr, type, hasValue
3227         {"-gc", &showgc, COBOOL, FALSE},
3228         {"-loader", &showloader, COBOOL, FALSE},
3229         {"/d", &dml, COBOOL, FALSE},
3230     };
3231
3232     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
3233     {
3234         return Status;
3235     }
3236
3237     EnableDMLHolder dmlHolder(dml);
3238     if (showloader || !showgc)
3239     {
3240         // Loader heap.
3241         DWORD_PTR allHeapSize = 0;
3242         DWORD_PTR wasted = 0;    
3243         DacpAppDomainStoreData adsData;
3244         if ((Status=adsData.Request(g_sos))!=S_OK)
3245         {
3246             ExtOut("Unable to get AppDomain information\n");
3247             return Status;
3248         }
3249
3250         // The first one is the system domain.
3251         ExtOut("Loader Heap:\n");
3252         IfFailRet(PrintDomainHeapInfo("System Domain", adsData.systemDomain, &allHeapSize, &wasted));
3253         IfFailRet(PrintDomainHeapInfo("Shared Domain", adsData.sharedDomain, &allHeapSize, &wasted));
3254         
3255         ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
3256
3257         if (pArray==NULL)
3258         {
3259             ReportOOM();            
3260             return Status;
3261         }
3262
3263         if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
3264         {
3265             ExtOut("Unable to get the array of all AppDomains.\n");
3266             return Status;
3267         }
3268
3269         for (int n=0;n<adsData.DomainCount;n++)
3270         {
3271             if (IsInterrupt())
3272                 break;
3273
3274             char domain[16];
3275             sprintf_s(domain, _countof(domain), "Domain %d", n+1);
3276
3277             IfFailRet(PrintDomainHeapInfo(domain, pArray[n], &allHeapSize, &wasted));
3278
3279         }
3280
3281         // Jit code heap
3282         ExtOut("--------------------------------------\n");
3283         ExtOut("Jit code heap:\n");
3284
3285         if (IsMiniDumpFile())
3286         {
3287             ExtOut("<no information>\n");
3288         }
3289         else
3290         {
3291             allHeapSize += JitHeapInfo();
3292         }
3293
3294     
3295         // Module Data
3296         {
3297             int numModule;
3298             ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(NULL, &numModule);   
3299             if (moduleList == NULL)
3300             {
3301                 ExtOut("Failed to request module list.\n");
3302             }
3303             else
3304             {
3305                 // Module Thunk Heaps
3306                 ExtOut("--------------------------------------\n");
3307                 ExtOut("Module Thunk heaps:\n");
3308                 allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_ThunkHeap, &wasted);
3309
3310                 // Module Lookup Table Heaps
3311                 ExtOut("--------------------------------------\n");
3312                 ExtOut("Module Lookup Table heaps:\n");
3313                 allHeapSize += PrintModuleHeapInfo(moduleList, numModule, ModuleHeapType_LookupTableHeap, &wasted);
3314             }
3315         }
3316
3317         ExtOut("--------------------------------------\n");
3318         ExtOut("Total LoaderHeap size:   ");
3319         PrintHeapSize(allHeapSize, wasted);
3320         ExtOut("=======================================\n");
3321     }
3322
3323     if (showgc || !showloader)
3324     {   
3325         // GC Heap
3326         DWORD dwNHeaps = 1;
3327
3328         if (!GetGcStructuresValid())
3329         {
3330             DisplayInvalidStructuresMessage();
3331         }
3332         
3333         DacpGcHeapData gcheap;
3334         if (gcheap.Request(g_sos) != S_OK)
3335         {
3336             ExtOut("Error requesting GC Heap data\n");
3337             return Status;
3338         }
3339
3340         if (gcheap.bServerMode)
3341         {
3342             dwNHeaps = gcheap.HeapCount;
3343         }
3344
3345         ExtOut("Number of GC Heaps: %d\n", dwNHeaps);
3346         DWORD_PTR totalSize = 0;
3347         if (!gcheap.bServerMode)
3348         {
3349             DacpGcHeapDetails heapDetails;
3350             if (heapDetails.Request(g_sos) != S_OK)
3351             {
3352                 ExtOut("Error requesting details\n");
3353                 return Status;
3354             }
3355
3356             GCHeapInfo (heapDetails, totalSize);
3357             ExtOut("Total Size:              ");
3358             PrintHeapSize(totalSize, 0);
3359         }
3360         else
3361         {
3362             DWORD dwAllocSize;
3363             if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
3364             {
3365                 ExtOut("Failed to get GCHeaps: integer overflow\n");
3366                 return Status;
3367             }
3368
3369             CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
3370             if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
3371             {
3372                 ExtOut("Failed to get GCHeaps\n");
3373                 return Status;
3374             }
3375                         
3376             DWORD n;
3377             for (n = 0; n < dwNHeaps; n ++)
3378             {
3379                 DacpGcHeapDetails heapDetails;
3380                 if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
3381                 {
3382                     ExtOut("Error requesting details\n");
3383                     return Status;
3384                 }
3385                 ExtOut("------------------------------\n");
3386                 ExtOut("Heap %d (%p)\n", n, SOS_PTR(heapAddrs[n]));
3387                 DWORD_PTR heapSize = 0;
3388                 GCHeapInfo (heapDetails, heapSize);
3389                 totalSize += heapSize;
3390                 ExtOut("Heap Size:       " WIN86_8SPACES);
3391                 PrintHeapSize(heapSize, 0);
3392             }
3393         }
3394         ExtOut("------------------------------\n");
3395         ExtOut("GC Heap Size:    " WIN86_8SPACES);
3396         PrintHeapSize(totalSize, 0);
3397     }
3398     return Status;
3399 }
3400
3401 void PrintGCStat(HeapStat *inStat, const char* label=NULL)
3402 {
3403     if (inStat)
3404     {
3405         bool sorted = false;
3406         try
3407         {
3408             inStat->Sort();
3409             sorted = true;
3410             inStat->Print(label);
3411         }
3412         catch(...)
3413         {
3414             ExtOut("Exception occurred while trying to %s the GC stats.\n", sorted ? "print" : "sort");
3415         }
3416
3417         inStat->Delete();
3418     }
3419 }
3420
3421 #ifndef FEATURE_PAL
3422
3423 DECLARE_API(TraverseHeap)
3424 {
3425     INIT_API();
3426     MINIDUMP_NOT_SUPPORTED();    
3427     
3428     BOOL bXmlFormat = FALSE;
3429     BOOL bVerify = FALSE;
3430     StringHolder Filename;
3431
3432     CMDOption option[] = 
3433     {   // name, vptr,        type, hasValue
3434         {"-xml", &bXmlFormat, COBOOL, FALSE},
3435         {"-verify", &bVerify, COBOOL, FALSE},
3436     };
3437     CMDValue arg[] = 
3438     {   // vptr, type
3439         {&Filename.data, COSTRING},
3440     };
3441     size_t nArg;
3442     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
3443     {
3444         return Status;
3445     }
3446
3447     if (nArg != 1)
3448     {
3449         ExtOut("usage: HeapTraverse [-xml] filename\n");
3450         return Status;
3451     }
3452
3453     if (!g_snapshot.Build())
3454     {
3455         ExtOut("Unable to build snapshot of the garbage collector state\n");
3456         return Status;
3457     }
3458
3459     FILE* file = NULL;
3460     if (fopen_s(&file, Filename.data, "w") != 0) {
3461         ExtOut("Unable to open file\n");
3462         return Status;
3463     }
3464
3465     if (!bVerify)
3466         ExtOut("Assuming a uncorrupted GC heap.  If this is a crash dump consider -verify option\n"); 
3467
3468     HeapTraverser traverser(bVerify != FALSE);
3469
3470     ExtOut("Writing %s format to file %s\n", bXmlFormat ? "Xml" : "CLRProfiler", Filename.data);    
3471     ExtOut("Gathering types...\n");
3472
3473     // TODO: there may be a canonical list of methodtables in the runtime that we can
3474     // traverse instead of exploring the gc heap for that list. We could then simplify the
3475     // tree structure to a sorted list of methodtables, and the index is the ID.
3476
3477     // TODO: "Traversing object members" code should be generalized and shared between
3478     // !gcroot and !traverseheap. Also !dumpheap can begin using GCHeapsTraverse.
3479
3480     if (!traverser.Initialize())
3481     {
3482         ExtOut("Error initializing heap traversal\n");
3483         fclose(file);
3484         return Status;
3485     }
3486
3487     if (!traverser.CreateReport (file, bXmlFormat ? FORMAT_XML : FORMAT_CLRPROFILER))
3488     {
3489         ExtOut("Unable to write heap report\n");
3490         fclose(file);
3491         return Status;
3492     }
3493
3494     fclose(file);                
3495     ExtOut("\nfile %s saved\n", Filename.data);
3496     
3497     return Status;
3498 }
3499
3500 #endif // FEATURE_PAL
3501
3502 struct PrintRuntimeTypeArgs
3503 {
3504     DWORD_PTR mtOfRuntimeType;
3505     int handleFieldOffset;
3506     DacpAppDomainStoreData adstore;
3507 };
3508
3509 void PrintRuntimeTypes(DWORD_PTR objAddr,size_t Size,DWORD_PTR methodTable,LPVOID token)
3510 {
3511     PrintRuntimeTypeArgs *pArgs = (PrintRuntimeTypeArgs *)token;
3512
3513     if (pArgs->mtOfRuntimeType == NULL)
3514     {
3515         NameForMT_s(methodTable, g_mdName, mdNameLen);
3516
3517         if (_wcscmp(g_mdName, W("System.RuntimeType")) == 0)
3518         {
3519             pArgs->mtOfRuntimeType = methodTable;
3520             pArgs->handleFieldOffset = GetObjFieldOffset(objAddr, methodTable, W("m_handle"));
3521             if (pArgs->handleFieldOffset <= 0)
3522                 ExtOut("Error getting System.RuntimeType.m_handle offset\n");
3523
3524             pArgs->adstore.Request(g_sos);
3525         }
3526     }
3527
3528     if ((methodTable == pArgs->mtOfRuntimeType) && (pArgs->handleFieldOffset > 0))
3529     {
3530         // Get the method table and display the information.
3531         DWORD_PTR mtPtr;
3532         if (MOVE(mtPtr, objAddr + pArgs->handleFieldOffset) == S_OK)
3533         {
3534             DMLOut(DMLObject(objAddr));
3535
3536             CLRDATA_ADDRESS appDomain = GetAppDomainForMT(mtPtr);
3537             if (appDomain != NULL)
3538             {
3539                 if (appDomain == pArgs->adstore.sharedDomain)
3540                     ExtOut(" %" POINTERSIZE "s", "Shared");
3541
3542                 else if (appDomain == pArgs->adstore.systemDomain)
3543                     ExtOut(" %" POINTERSIZE "s", "System");
3544                 else
3545                     DMLOut(" %s", DMLDomain(appDomain));
3546             }
3547             else
3548             {
3549                 ExtOut(" %" POINTERSIZE "s", "?");
3550             }
3551         
3552             NameForMT_s(mtPtr, g_mdName, mdNameLen);
3553             DMLOut(" %s %S\n", DMLMethodTable(mtPtr), g_mdName);
3554         }
3555     }
3556 }
3557
3558
3559 DECLARE_API(DumpRuntimeTypes)
3560 {
3561     INIT_API();
3562     MINIDUMP_NOT_SUPPORTED();    
3563
3564     BOOL dml = FALSE;
3565
3566     CMDOption option[] = 
3567     {   // name, vptr, type, hasValue
3568         {"/d", &dml, COBOOL, FALSE},
3569     };
3570
3571     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
3572         return Status;
3573
3574     EnableDMLHolder dmlHolder(dml);
3575
3576     ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Type Name              \n",
3577            "Address", "Domain", "MT");
3578     ExtOut("------------------------------------------------------------------------------\n");
3579
3580     PrintRuntimeTypeArgs pargs;
3581     ZeroMemory(&pargs, sizeof(PrintRuntimeTypeArgs));
3582
3583     GCHeapsTraverse(PrintRuntimeTypes, (LPVOID)&pargs);
3584     return Status;
3585 }
3586
3587 #define MIN_FRAGMENTATIONBLOCK_BYTES (1024*512)
3588 namespace sos
3589 {
3590     class FragmentationBlock
3591     {
3592     public:
3593         FragmentationBlock(TADDR addr, size_t size, TADDR next, TADDR mt)
3594             : mAddress(addr), mSize(size), mNext(next), mNextMT(mt)
3595         {
3596         }
3597
3598         inline TADDR GetAddress() const
3599         {
3600             return mAddress;
3601         }
3602         inline size_t GetSize() const
3603         {
3604             return mSize;
3605         }
3606
3607         inline TADDR GetNextObject() const
3608         {
3609             return mNext;
3610         }
3611
3612         inline TADDR GetNextMT() const
3613         {
3614             return mNextMT;
3615         }
3616
3617     private:
3618         TADDR mAddress;
3619         size_t mSize;
3620         TADDR mNext;
3621         TADDR mNextMT;
3622     };
3623 }
3624
3625 class DumpHeapImpl
3626 {
3627 public:
3628     DumpHeapImpl(PCSTR args)
3629         : mStart(0), mStop(0), mMT(0),  mMinSize(0), mMaxSize(~0),
3630           mStat(FALSE), mStrings(FALSE), mVerify(FALSE),
3631           mThinlock(FALSE), mShort(FALSE), mDML(FALSE),
3632           mLive(FALSE), mDead(FALSE), mType(NULL)
3633     {
3634         ArrayHolder<char> type = NULL;
3635
3636         TADDR minTemp = 0;
3637         CMDOption option[] = 
3638         {   // name, vptr, type, hasValue
3639             {"-mt", &mMT, COHEX, TRUE},              // dump objects with a given MethodTable
3640             {"-type", &type, COSTRING, TRUE},        // list objects of specified type
3641             {"-stat", &mStat, COBOOL, FALSE},        // dump a summary of types and the number of instances of each
3642             {"-strings", &mStrings, COBOOL, FALSE},  // dump a summary of string objects
3643             {"-verify", &mVerify, COBOOL, FALSE},    // verify heap objects (!heapverify)
3644             {"-thinlock", &mThinlock, COBOOL, FALSE},// list only thinlocks
3645             {"-short", &mShort, COBOOL, FALSE},      // list only addresses
3646             {"-min", &mMinSize, COHEX, TRUE},        // min size of objects to display
3647             {"-max", &mMaxSize, COHEX, TRUE},        // max size of objects to display
3648             {"-live", &mLive, COHEX, FALSE},         // only print live objects
3649             {"-dead", &mDead, COHEX, FALSE},         // only print dead objects
3650 #ifndef FEATURE_PAL
3651             {"/d", &mDML, COBOOL, FALSE},            // Debugger Markup Language
3652 #endif
3653         };
3654
3655         CMDValue arg[] = 
3656         {   // vptr, type
3657             {&mStart, COHEX},
3658             {&mStop, COHEX}
3659         };
3660
3661         size_t nArgs = 0;
3662         if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArgs))
3663             sos::Throw<sos::Exception>("Failed to parse command line arguments.");
3664
3665         if (mStart == 0)
3666             mStart = minTemp;
3667
3668         if (mStop == 0)
3669             mStop = sos::GCHeap::HeapEnd;
3670
3671         if (type && mMT)
3672         {
3673             sos::Throw<sos::Exception>("Cannot specify both -mt and -type");
3674         }
3675         
3676         if (mLive && mDead)
3677         {
3678             sos::Throw<sos::Exception>("Cannot specify both -live and -dead.");
3679         }
3680
3681         if (mMinSize > mMaxSize)
3682         {
3683             sos::Throw<sos::Exception>("wrong argument");
3684         }
3685         
3686         // If the user gave us a type, convert it to unicode and clean up "type".
3687         if (type && !mStrings)
3688         {
3689             size_t iLen = strlen(type) + 1;
3690             mType = new WCHAR[iLen];
3691             MultiByteToWideChar(CP_ACP, 0, type, -1, mType, (int)iLen);
3692         }
3693     }
3694
3695     ~DumpHeapImpl()
3696     {
3697         if (mType)
3698             delete [] mType;
3699     }
3700
3701     void Run()
3702     {
3703         // enable Debugger Markup Language
3704         EnableDMLHolder dmlholder(mDML); 
3705         sos::GCHeap gcheap;
3706
3707         if (!gcheap.AreGCStructuresValid())
3708             DisplayInvalidStructuresMessage();
3709         
3710         if (IsMiniDumpFile())
3711         {
3712             ExtOut("In a minidump without full memory, most gc heap structures will not be valid.\n");
3713             ExtOut("If you need this functionality, get a full memory dump with \".dump /ma mydump.dmp\"\n");
3714         }
3715
3716 #ifndef FEATURE_PAL
3717         if (mLive || mDead)
3718         {
3719             GCRootImpl gcroot;
3720             mLiveness = gcroot.GetLiveObjects();
3721         }
3722 #endif
3723
3724         // Some of the "specialty" versions of DumpHeap have slightly
3725         // different implementations than the standard version of DumpHeap.
3726         // We seperate them out to not clutter the standard DumpHeap function.
3727         if (mShort)
3728             DumpHeapShort(gcheap);
3729         else if (mThinlock)
3730             DumpHeapThinlock(gcheap);
3731         else if (mStrings)
3732             DumpHeapStrings(gcheap);
3733         else
3734             DumpHeap(gcheap);
3735
3736         if (mVerify)
3737             ValidateSyncTable(gcheap);
3738     }
3739
3740     static bool ValidateSyncTable(sos::GCHeap &gcheap)
3741     {
3742         bool succeeded = true;
3743         for (sos::SyncBlkIterator itr; itr; ++itr)
3744         {
3745             sos::CheckInterrupt();
3746
3747             if (!itr->IsFree())
3748             {
3749                 if (!sos::IsObject(itr->GetObject(), true))
3750                 {
3751                     ExtOut("SyncBlock %d corrupted, points to invalid object %p\n", 
3752                             itr->GetIndex(), SOS_PTR(itr->GetObject()));
3753                         succeeded = false;
3754                 }
3755                 else
3756                 {
3757                     // Does the object header point to this syncblock index?
3758                     sos::Object obj = itr->GetObject();
3759                     ULONG header = 0;
3760
3761                     if (!obj.TryGetHeader(header))
3762                     {
3763                         ExtOut("Failed to get object header for object %p while inspecting syncblock at index %d.\n",
3764                                 SOS_PTR(itr->GetObject()), itr->GetIndex());
3765                         succeeded = false;
3766                     }
3767                     else
3768                     {
3769                         bool valid = false;
3770                         if ((header & BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX) != 0 && (header & BIT_SBLK_IS_HASHCODE) == 0)
3771                         {
3772                             ULONG index = header & MASK_SYNCBLOCKINDEX;
3773                             valid = (ULONG)itr->GetIndex() == index;
3774                         }
3775                         
3776                         if (!valid)
3777                         {
3778                             ExtOut("Object header for %p should have a SyncBlock index of %d.\n",
3779                                     SOS_PTR(itr->GetObject()), itr->GetIndex());
3780                             succeeded = false;
3781                         }
3782                     }
3783                 }
3784             }
3785         }
3786
3787         return succeeded;
3788     }
3789 private:
3790     DumpHeapImpl(const DumpHeapImpl &);
3791
3792     bool Verify(const sos::ObjectIterator &itr)
3793     {
3794         if (mVerify)
3795         {
3796             char buffer[1024];
3797             if (!itr.Verify(buffer, _countof(buffer)))
3798             {
3799                 ExtOut(buffer);
3800                 return false;
3801             }
3802         }
3803         
3804         return true;
3805     }
3806
3807     bool IsCorrectType(const sos::Object &obj)
3808     {
3809         if (mMT != NULL)
3810             return mMT == obj.GetMT();
3811
3812         if (mType != NULL)
3813         {
3814             WString name = obj.GetTypeName();
3815             return _wcsstr(name.c_str(), mType) != NULL;
3816         }
3817
3818         return true;
3819     }
3820
3821     bool IsCorrectSize(const sos::Object &obj)
3822     {
3823         size_t size = obj.GetSize();
3824         return size >= mMinSize && size <= mMaxSize;
3825     }
3826
3827     bool IsCorrectLiveness(const sos::Object &obj)
3828     {
3829 #ifndef FEATURE_PAL
3830         if (mLive && mLiveness.find(obj.GetAddress()) == mLiveness.end())
3831             return false;
3832
3833         if (mDead && (mLiveness.find(obj.GetAddress()) != mLiveness.end() || obj.IsFree()))
3834             return false;
3835 #endif
3836         return true;
3837     }
3838
3839
3840
3841     inline void PrintHeader()
3842     {
3843         ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %8s\n", "Address", "MT", "Size");
3844     }
3845
3846     void DumpHeap(sos::GCHeap &gcheap)
3847     {
3848         HeapStat stats;
3849
3850         // For heap fragmentation tracking.
3851         TADDR lastFreeObj = NULL;
3852         size_t lastFreeSize = 0;
3853
3854         if (!mStat)
3855             PrintHeader();
3856
3857         for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
3858         {
3859             if (!Verify(itr))
3860                 return;
3861
3862             bool onLOH = itr.IsCurrObjectOnLOH();
3863
3864             // Check for free objects to report fragmentation
3865             if (lastFreeObj != NULL)
3866                 ReportFreeObject(lastFreeObj, lastFreeSize, itr->GetAddress(), itr->GetMT());
3867
3868             if (!onLOH && itr->IsFree())
3869             {
3870                 lastFreeObj = *itr;
3871                 lastFreeSize = itr->GetSize();
3872             }
3873             else
3874             {
3875                 lastFreeObj = NULL;
3876             }
3877
3878             if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
3879             {
3880                 stats.Add((DWORD_PTR)itr->GetMT(), (DWORD)itr->GetSize());
3881                 if (!mStat)
3882                     DMLOut("%s %s %8d%s\n", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize(), 
3883                                             itr->IsFree() ? " Free":"     ");
3884             }
3885         }
3886
3887         if (!mStat)
3888             ExtOut("\n");
3889
3890         stats.Sort();
3891         stats.Print();
3892
3893         PrintFragmentationReport();
3894     }
3895
3896     struct StringSetEntry
3897     {
3898         StringSetEntry() : count(0), size(0)
3899         {
3900             str[0] = 0;
3901         }
3902         
3903         StringSetEntry(__in_ecount(64) WCHAR tmp[64], size_t _size)
3904             : count(1), size(_size)
3905         {
3906             memcpy(str, tmp, sizeof(str));
3907         }
3908         
3909         void Add(size_t _size) const
3910         {
3911             count++;
3912             size += _size;
3913         }
3914         
3915         mutable size_t count;
3916         mutable size_t size;
3917         WCHAR str[64];
3918         
3919         bool operator<(const StringSetEntry &rhs) const
3920         {
3921             return _wcscmp(str, rhs.str) < 0;
3922         }
3923     };
3924
3925     
3926     static bool StringSetCompare(const StringSetEntry &a1, const StringSetEntry &a2)
3927     {
3928         return a1.size < a2.size;
3929     }
3930
3931     void DumpHeapStrings(sos::GCHeap &gcheap)
3932     {
3933 #ifdef FEATURE_PAL
3934         ExtOut("Not implemented.\n");
3935 #else
3936         const int offset = sos::Object::GetStringDataOffset();
3937         typedef std::set<StringSetEntry> Set;
3938         Set set;            // A set keyed off of the string's text
3939         
3940         StringSetEntry tmp;  // Temp string used to keep track of the set
3941         ULONG fetched = 0;
3942
3943         TableOutput out(3, POINTERSIZE_HEX, AlignRight);
3944         for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
3945         {
3946             if (IsInterrupt())
3947                 break;
3948                 
3949             if (itr->IsString() && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
3950             {
3951                 CLRDATA_ADDRESS addr = itr->GetAddress();
3952                 size_t size = itr->GetSize();
3953                 
3954                 if (!mStat)
3955                     out.WriteRow(ObjectPtr(addr), Pointer(itr->GetMT()), Decimal(size));
3956
3957                 // Don't bother calculating the size of the string, just read the full 64 characters of the buffer.  The null
3958                 // terminator we read will terminate the string.
3959                 HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(addr+offset), tmp.str, sizeof(WCHAR)*(_countof(tmp.str)-1), &fetched);
3960                 if (SUCCEEDED(hr))
3961                 {
3962                     // Ensure we null terminate the string.  Note that this will not overrun the buffer as we only
3963                     // wrote a max of 63 characters into the 64 character buffer.
3964                     tmp.str[fetched/sizeof(WCHAR)] = 0;
3965                     Set::iterator sitr = set.find(tmp);
3966                     if (sitr == set.end())
3967                     {
3968                         tmp.size = size;
3969                         tmp.count = 1;
3970                         set.insert(tmp);
3971                     }
3972                     else
3973                     {
3974                         sitr->Add(size);
3975                     }
3976                 }
3977             }
3978         }
3979
3980         ExtOut("\n");
3981
3982         // Now flatten the set into a vector.  This is much faster than keeping two sets, or using a multimap.
3983         typedef std::vector<StringSetEntry> Vect;
3984         Vect v(set.begin(), set.end());
3985         std::sort(v.begin(), v.end(), &DumpHeapImpl::StringSetCompare);
3986
3987         // Now print out the data.  The call to Flatten ensures that we don't print newlines to break up the
3988         // output in strange ways.
3989         for (Vect::iterator vitr = v.begin(); vitr != v.end(); ++vitr)
3990         {
3991             if (IsInterrupt())
3992                 break;
3993                 
3994             Flatten(vitr->str, (unsigned int)_wcslen(vitr->str));
3995             out.WriteRow(Decimal(vitr->size), Decimal(vitr->count), vitr->str);
3996         }
3997 #endif // FEATURE_PAL
3998     }
3999
4000     void DumpHeapShort(sos::GCHeap &gcheap)
4001     {
4002         for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
4003         {
4004             if (!Verify(itr))
4005                 return;
4006
4007             if (IsCorrectType(*itr) && IsCorrectSize(*itr) && IsCorrectLiveness(*itr))
4008                 DMLOut("%s\n", DMLObject(itr->GetAddress()));
4009         }
4010     }
4011
4012     void DumpHeapThinlock(sos::GCHeap &gcheap)
4013     {
4014         int count = 0;
4015
4016         PrintHeader();
4017         for (sos::ObjectIterator itr = gcheap.WalkHeap(mStart, mStop); itr; ++itr)
4018         {
4019             if (!Verify(itr))
4020                 return;
4021
4022             sos::ThinLockInfo lockInfo; 
4023             if (IsCorrectType(*itr) && itr->GetThinLock(lockInfo))
4024             {
4025                 DMLOut("%s %s %8d", DMLObject(itr->GetAddress()), DMLDumpHeapMT(itr->GetMT()), itr->GetSize());
4026                 ExtOut(" ThinLock owner %x (%p) Recursive %x\n", lockInfo.ThreadId,
4027                                         SOS_PTR(lockInfo.ThreadPtr), lockInfo.Recursion);
4028
4029                 count++;
4030             }
4031         }
4032
4033         ExtOut("Found %d objects.\n", count);
4034     }
4035
4036 private:
4037     TADDR mStart,
4038           mStop,
4039           mMT,
4040           mMinSize,
4041           mMaxSize;
4042
4043     BOOL mStat,
4044          mStrings,
4045          mVerify,
4046          mThinlock,
4047          mShort,
4048          mDML,
4049          mLive,
4050          mDead;
4051
4052
4053     WCHAR *mType;
4054
4055 private:
4056 #if !defined(FEATURE_PAL)
4057     // Windows only
4058     std::unordered_set<TADDR> mLiveness;
4059     typedef std::list<sos::FragmentationBlock> FragmentationList;
4060     FragmentationList mFrag;
4061
4062     void InitFragmentationList()
4063     {
4064         mFrag.clear();
4065     }
4066
4067     void ReportFreeObject(TADDR addr, size_t size, TADDR next, TADDR mt)
4068     {
4069         if (size >= MIN_FRAGMENTATIONBLOCK_BYTES)
4070             mFrag.push_back(sos::FragmentationBlock(addr, size, next, mt));
4071     }
4072
4073     void PrintFragmentationReport()
4074     {
4075         if (mFrag.size() > 0)
4076         {
4077             ExtOut("Fragmented blocks larger than 0.5 MB:\n");
4078             ExtOut("%" POINTERSIZE "s %8s %16s\n", "Addr", "Size", "Followed by");
4079  
4080             for (FragmentationList::const_iterator itr = mFrag.begin(); itr != mFrag.end(); ++itr)
4081             {
4082                 sos::MethodTable mt = itr->GetNextMT();
4083                 ExtOut("%p %6.1fMB " WIN64_8SPACES "%p %S\n",
4084                             SOS_PTR(itr->GetAddress()),
4085                             ((double)itr->GetSize()) / 1024.0 / 1024.0,
4086                             SOS_PTR(itr->GetNextObject()),
4087                             mt.GetName());
4088             }
4089         }
4090     }
4091 #else
4092     void InitFragmentationList() {}
4093     void ReportFreeObject(TADDR, TADDR, size_t, TADDR) {}
4094     void PrintFragmentationReport() {}
4095 #endif
4096 };
4097
4098 /**********************************************************************\
4099 * Routine Description:                                                 *
4100 *                                                                      *
4101 *    This function dumps all objects on GC heap. It also displays      *  
4102 *    statistics of objects.  If GC heap is corrupted, it will stop at 
4103 *    the bad place.  (May not work if GC is in progress.)              *
4104 *                                                                      *
4105 \**********************************************************************/
4106 DECLARE_API(DumpHeap)
4107 {
4108     INIT_API();
4109     MINIDUMP_NOT_SUPPORTED();
4110
4111     if (!g_snapshot.Build())
4112     {
4113         ExtOut("Unable to build snapshot of the garbage collector state\n");
4114         return E_FAIL;
4115     }
4116
4117     try
4118     {
4119         DumpHeapImpl dumpHeap(args);
4120         dumpHeap.Run();
4121
4122         return S_OK;
4123     }
4124     catch(const sos::Exception &e)
4125     {
4126         ExtOut("%s\n", e.what());
4127         return E_FAIL;
4128     }
4129 }
4130
4131 DECLARE_API(VerifyHeap)
4132 {    
4133     INIT_API();
4134     MINIDUMP_NOT_SUPPORTED();
4135     
4136     if (!g_snapshot.Build())
4137     {
4138         ExtOut("Unable to build snapshot of the garbage collector state\n");
4139         return E_FAIL;
4140     }
4141     
4142     try
4143     {
4144         bool succeeded = true;
4145         char buffer[1024];
4146         sos::GCHeap gcheap;
4147         sos::ObjectIterator itr = gcheap.WalkHeap();
4148
4149         while (itr)
4150         {
4151             if (itr.Verify(buffer, _countof(buffer)))
4152             {
4153                 ++itr;
4154             }
4155             else
4156             {
4157                 succeeded = false;
4158                 ExtOut(buffer);
4159                 itr.MoveToNextObjectCarefully();
4160             }
4161         }
4162
4163         if (!DumpHeapImpl::ValidateSyncTable(gcheap))
4164             succeeded = false;
4165
4166         if (succeeded)
4167             ExtOut("No heap corruption detected.\n");
4168
4169         return S_OK;
4170     }
4171     catch(const sos::Exception &e)
4172     {
4173         ExtOut("%s\n", e.what());
4174         return E_FAIL;
4175     }
4176 }
4177
4178 #ifndef FEATURE_PAL
4179
4180 enum failure_get_memory
4181 {
4182     fgm_no_failure = 0,
4183     fgm_reserve_segment = 1,
4184     fgm_commit_segment_beg = 2,
4185     fgm_commit_eph_segment = 3,
4186     fgm_grow_table = 4,
4187     fgm_commit_table = 5
4188 };
4189
4190 enum oom_reason
4191 {
4192     oom_no_failure = 0,
4193     oom_budget = 1,
4194     oom_cant_commit = 2,
4195     oom_cant_reserve = 3,
4196     oom_loh = 4,
4197     oom_low_mem = 5,
4198     oom_unproductive_full_gc = 6
4199 };
4200
4201 static const char *const str_oom[] = 
4202 {
4203     "There was no managed OOM due to allocations on the GC heap", // oom_no_failure 
4204     "This is likely to be a bug in GC", // oom_budget
4205     "Didn't have enough memory to commit", // oom_cant_commit
4206     "This is likely to be a bug in GC", // oom_cant_reserve 
4207     "Didn't have enough memory to allocate an LOH segment", // oom_loh 
4208     "Low on memory during GC", // oom_low_mem 
4209     "Could not do a full GC" // oom_unproductive_full_gc
4210 };
4211
4212 static const char *const str_fgm[] = 
4213 {
4214     "There was no failure to allocate memory", // fgm_no_failure 
4215     "Failed to reserve memory", // fgm_reserve_segment
4216     "Didn't have enough memory to commit beginning of the segment", // fgm_commit_segment_beg
4217     "Didn't have enough memory to commit the new ephemeral segment", // fgm_commit_eph_segment
4218     "Didn't have enough memory to grow the internal GC datastructures", // fgm_grow_table
4219     "Didn't have enough memory to commit the internal GC datastructures", // fgm_commit_table
4220 };
4221
4222 void PrintOOMInfo(DacpOomData* oomData)
4223 {
4224     ExtOut("Managed OOM occurred after GC #%d (Requested to allocate %d bytes)\n", 
4225         oomData->gc_index, oomData->alloc_size);
4226
4227     if ((oomData->reason == oom_budget) ||
4228         (oomData->reason == oom_cant_reserve))
4229     {
4230         // TODO: This message needs to be updated with more precious info.
4231         ExtOut("%s, please contact PSS\n", str_oom[oomData->reason]);
4232     }
4233     else
4234     {
4235         ExtOut("Reason: %s\n", str_oom[oomData->reason]);
4236     }
4237
4238     // Now print out the more detailed memory info if any.
4239     if (oomData->fgm != fgm_no_failure)
4240     {
4241         ExtOut("Detail: %s: %s (%d bytes)", 
4242             (oomData->loh_p ? "LOH" : "SOH"), 
4243             str_fgm[oomData->fgm],
4244             oomData->size);
4245     
4246         if ((oomData->fgm == fgm_commit_segment_beg) ||
4247             (oomData->fgm == fgm_commit_eph_segment) ||
4248             (oomData->fgm == fgm_grow_table) ||
4249             (oomData->fgm == fgm_commit_table))
4250         {
4251             // If it's a commit error (fgm_grow_table can indicate a reserve
4252             // or a commit error since we make one VirtualAlloc call to
4253             // reserve and commit), we indicate the available commit
4254             // space if we recorded it.
4255             if (oomData->available_pagefile_mb)
4256             {
4257                 ExtOut(" - on GC entry available commit space was %d MB",
4258                     oomData->available_pagefile_mb);
4259             }
4260         }
4261
4262         ExtOut("\n");
4263     }
4264 }
4265
4266 DECLARE_API(AnalyzeOOM)
4267 {    
4268     INIT_API();    
4269     MINIDUMP_NOT_SUPPORTED();    
4270     
4271 #ifndef FEATURE_PAL
4272
4273     if (!InitializeHeapData ())
4274     {
4275         ExtOut("GC Heap not initialized yet.\n");
4276         return S_OK;
4277     }
4278
4279     BOOL bHasManagedOOM = FALSE;
4280     DacpOomData oomData;
4281     memset (&oomData, 0, sizeof(oomData));
4282     if (!IsServerBuild())
4283     {
4284         if (oomData.Request(g_sos) != S_OK)
4285         {
4286             ExtOut("Error requesting OOM data\n");
4287             return E_FAIL;
4288         }
4289         if (oomData.reason != oom_no_failure)
4290         {
4291             bHasManagedOOM = TRUE;
4292             PrintOOMInfo(&oomData);
4293         }
4294     }
4295     else
4296     {   
4297         DWORD dwNHeaps = GetGcHeapCount();
4298         DWORD dwAllocSize;
4299         if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
4300         {
4301             ExtOut("Failed to get GCHeaps:  integer overflow\n");
4302             return Status;
4303         }
4304
4305         CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
4306         if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
4307         {
4308             ExtOut("Failed to get GCHeaps\n");
4309             return Status;
4310         }
4311         
4312         for (DWORD n = 0; n < dwNHeaps; n ++)
4313         {
4314             if (oomData.Request(g_sos, heapAddrs[n]) != S_OK)
4315             {
4316                 ExtOut("Heap %d: Error requesting OOM data\n", n);
4317                 return E_FAIL;
4318             }
4319             if (oomData.reason != oom_no_failure)
4320             {
4321                 if (!bHasManagedOOM)
4322                 {
4323                     bHasManagedOOM = TRUE;
4324                 }
4325                 ExtOut("---------Heap %#-2d---------\n", n);
4326                 PrintOOMInfo(&oomData);
4327             }
4328         }
4329     }
4330
4331     if (!bHasManagedOOM)
4332     {
4333         ExtOut("%s\n", str_oom[oomData.reason]);
4334     }
4335
4336     return S_OK;
4337 #else
4338     _ASSERTE(false);
4339     return E_FAIL;
4340 #endif // FEATURE_PAL
4341 }
4342
4343 DECLARE_API(VerifyObj)
4344 {
4345     INIT_API();    
4346     MINIDUMP_NOT_SUPPORTED();
4347
4348     TADDR  taddrObj = 0;
4349     TADDR  taddrMT;
4350     size_t objSize;
4351
4352     BOOL bValid = FALSE;
4353     BOOL dml = FALSE;
4354
4355     CMDOption option[] = 
4356     {   // name, vptr, type, hasValue
4357         {"/d", &dml, COBOOL, FALSE},
4358     };
4359     CMDValue arg[] = 
4360     {   // vptr, type
4361         {&taddrObj, COHEX}
4362     };
4363     size_t nArg;
4364     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
4365     {
4366         return Status;
4367     }
4368
4369     EnableDMLHolder dmlHolder(dml);
4370     BOOL bContainsPointers;
4371
4372     if (FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
4373         !GetSizeEfficient(taddrObj, taddrMT, FALSE, objSize, bContainsPointers))
4374     {
4375         ExtOut("object %#p does not have valid method table\n", SOS_PTR(taddrObj));
4376         goto Exit;
4377     }
4378
4379     // we need to build g_snapshot as it is later used in GetGeneration
4380     if (!g_snapshot.Build())
4381     {
4382         ExtOut("Unable to build snapshot of the garbage collector state\n");
4383         goto Exit;
4384     }
4385     {
4386         DacpGcHeapDetails *pheapDetails = g_snapshot.GetHeap(taddrObj);
4387         bValid = VerifyObject(*pheapDetails, taddrObj, taddrMT, objSize, TRUE);
4388     }
4389
4390 Exit:
4391     if (bValid)
4392     {
4393         ExtOut("object %#p is a valid object\n", SOS_PTR(taddrObj));
4394     }
4395
4396     return Status;
4397 }
4398
4399 void LNODisplayOutput(LPCWSTR tag, TADDR pMT, TADDR currentObj, size_t size) 
4400 {
4401     sos::Object obj(currentObj, pMT);
4402     DMLOut("%S %s %12d (0x%x)\t%S\n", tag, DMLObject(currentObj), size, size, obj.GetTypeName());
4403 }
4404
4405 DECLARE_API(ListNearObj)
4406 {
4407     INIT_API();
4408     MINIDUMP_NOT_SUPPORTED();    
4409
4410 #if !defined(FEATURE_PAL)
4411
4412     TADDR taddrArg = 0;
4413     TADDR taddrObj = 0;
4414     // we may want to provide a more exact version of searching for the 
4415     // previous object in the heap, using the brick table, instead of 
4416     // looking for what may be valid method tables...
4417     //BOOL bExact;
4418     //CMDOption option[] = 
4419     //{
4420     //    // name, vptr, type, hasValue
4421     //    {"-exact", &bExact, COBOOL, FALSE}
4422     //};
4423
4424     BOOL dml = FALSE;
4425     CMDOption option[] = 
4426     {   // name, vptr, type, hasValue
4427         {"/d", &dml, COBOOL, FALSE},
4428     };
4429     CMDValue arg[] = 
4430     {
4431         // vptr, type
4432         {&taddrArg, COHEX}
4433     };
4434     size_t nArg;
4435     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || nArg != 1)
4436     {
4437         ExtOut("Usage: !ListNearObj <obj_address>\n");
4438         return Status;
4439     }
4440
4441     EnableDMLHolder dmlHolder(dml);
4442
4443     if (!g_snapshot.Build())
4444     {
4445         ExtOut("Unable to build snapshot of the garbage collector state\n");
4446         return Status;    
4447     }
4448
4449     taddrObj = Align(taddrArg);
4450
4451     DacpGcHeapDetails *heap = g_snapshot.GetHeap(taddrArg);
4452     if (heap == NULL)
4453     {
4454         ExtOut("Address %p does not lie in the managed heap\n", SOS_PTR(taddrObj));
4455         return Status;
4456     }
4457
4458     TADDR_SEGINFO trngSeg  = {0, 0, 0};
4459     TADDR_RANGE   allocCtx = {0, 0};
4460     BOOL          bLarge;
4461     int           gen;
4462     if (!GCObjInHeap(taddrObj, *heap, trngSeg, gen, allocCtx, bLarge))
4463     {
4464         ExtOut("Failed to find the segment of the managed heap where the object %p resides\n", 
4465             SOS_PTR(taddrObj));
4466         return Status;
4467     }
4468
4469     TADDR  objMT = NULL;
4470     size_t objSize = 0;
4471     BOOL   bObj    = FALSE;
4472     TADDR  taddrCur;
4473     TADDR  curMT   = 0;
4474     size_t curSize = 0;
4475     BOOL   bCur    = FALSE;
4476     TADDR  taddrNxt;
4477     TADDR  nxtMT   = 0;
4478     size_t nxtSize = 0;
4479     BOOL   bNxt    = FALSE;
4480     BOOL   bContainsPointers;
4481
4482     std::vector<TADDR> candidate;
4483     candidate.reserve(10);
4484
4485     // since we'll be reading back I'll prime the read cache to a buffer before the current address
4486     MOVE(taddrCur, _max(trngSeg.start, taddrObj-DT_OS_PAGE_SIZE));
4487
4488     // ===== Look for a good candidate preceeding taddrObj
4489
4490     for (taddrCur = taddrObj - sizeof(TADDR); taddrCur >= trngSeg.start; taddrCur -= sizeof(TADDR))
4491     {
4492         // currently we don't pay attention to allocation contexts.  if this
4493         // proves to be an issue we need to reconsider the code below
4494         if (SUCCEEDED(GetMTOfObject(taddrCur, &curMT)) &&
4495             GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers))
4496         {
4497             // remember this as one of the possible "good" objects preceeding taddrObj
4498             candidate.push_back(taddrCur);
4499
4500             std::vector<TADDR>::iterator it = 
4501                 std::find(candidate.begin(), candidate.end(), taddrCur+curSize);
4502             if (it != candidate.end())
4503             {
4504                 // We found a chain of two objects preceeding taddrObj.  We'll
4505                 // trust this is a good indication that the two objects are valid.
4506                 // What is not valid is possibly the object following the second 
4507                 // one...
4508                 taddrCur = *it;
4509                 GetMTOfObject(taddrCur, &curMT);
4510                 GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers);
4511                 bCur = TRUE;
4512                 break;
4513             }
4514         }
4515     }
4516
4517     if (!bCur && !candidate.empty())
4518     {
4519         // pick the closest object to taddrObj
4520         taddrCur = *(candidate.begin());
4521         GetMTOfObject(taddrCur, &curMT);
4522         GetSizeEfficient(taddrCur, curMT, bLarge, curSize, bContainsPointers);
4523         // we have a candidate, even if not confirmed
4524         bCur = TRUE;
4525     }
4526
4527     taddrNxt = taddrObj;
4528     if (taddrArg == taddrObj) 
4529     {
4530         taddrNxt += sizeof(TADDR);
4531     }
4532
4533     // ===== Now look at taddrObj
4534     if (taddrObj == taddrArg) 
4535     {
4536         // only look at taddrObj if it's the same as what user passed in, meaning it's aligned.  
4537         if (SUCCEEDED(GetMTOfObject(taddrObj, &objMT)) &&
4538             GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers))
4539         {
4540             bObj = TRUE;
4541             taddrNxt = taddrObj+objSize;
4542         }
4543     }
4544
4545     if ((taddrCur + curSize > taddrArg) && taddrCur + curSize < trngSeg.end)
4546     {
4547         if (SUCCEEDED(GetMTOfObject(taddrCur + curSize, &nxtMT)) &&
4548             GetSizeEfficient(taddrObj, objMT, bLarge, objSize, bContainsPointers))
4549         {
4550             taddrNxt = taddrCur+curSize;
4551         }
4552     }
4553
4554     // ===== And finally move on to elements following taddrObj
4555     
4556     for (; taddrNxt < trngSeg.end; taddrNxt += sizeof(TADDR))
4557     {
4558         if (SUCCEEDED(GetMTOfObject(taddrNxt, &nxtMT)) &&
4559             GetSizeEfficient(taddrNxt, nxtMT, bLarge, nxtSize, bContainsPointers))
4560         {
4561             bNxt = TRUE;
4562             break;
4563         }
4564     }
4565
4566     if (bCur)
4567         LNODisplayOutput(W("Before: "), curMT, taddrCur, curSize);
4568     else
4569         ExtOut("Before: couldn't find any object between %#p and %#p\n",
4570             SOS_PTR(trngSeg.start), SOS_PTR(taddrArg));
4571
4572     if (bObj)
4573         LNODisplayOutput(W("Current:"), objMT, taddrObj, objSize);
4574
4575     if (bNxt)
4576         LNODisplayOutput(W("After:  "), nxtMT, taddrNxt, nxtSize);
4577     else
4578         ExtOut("After:  couldn't find any object between %#p and %#p\n",
4579             SOS_PTR(taddrArg), SOS_PTR(trngSeg.end));
4580
4581     if (bCur && bNxt && 
4582         (((taddrCur+curSize == taddrObj) && (taddrObj+objSize == taddrNxt)) || (taddrCur+curSize == taddrNxt)))
4583     {
4584         ExtOut("Heap local consistency confirmed.\n");
4585     }
4586     else
4587     {
4588         ExtOut("Heap local consistency not confirmed.\n");
4589     }
4590
4591     return Status;    
4592
4593 #else
4594
4595     _ASSERTE(false);
4596     return E_FAIL;
4597
4598 #endif // FEATURE_PAL
4599 }
4600
4601
4602 DECLARE_API(GCHeapStat)
4603 {
4604     INIT_API();
4605     MINIDUMP_NOT_SUPPORTED();    
4606     
4607
4608 #ifndef FEATURE_PAL
4609
4610     BOOL bIncUnreachable = FALSE;
4611     BOOL dml = FALSE;
4612
4613     CMDOption option[] = {
4614         // name, vptr, type, hasValue
4615         {"-inclUnrooted", &bIncUnreachable, COBOOL, FALSE},
4616         {"-iu",           &bIncUnreachable, COBOOL, FALSE},
4617         {"/d",            &dml, COBOOL, FALSE}
4618     };
4619     
4620     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
4621     {
4622         return Status;
4623     }
4624
4625     EnableDMLHolder dmlHolder(dml);
4626     ExtOut("%-8s %12s %12s %12s %12s\n", "Heap", "Gen0", "Gen1", "Gen2", "LOH");
4627
4628     if (!IsServerBuild())
4629     {
4630         float tempf;
4631         DacpGcHeapDetails heapDetails;
4632         if (heapDetails.Request(g_sos) != S_OK)
4633         {
4634             ExtErr("Error requesting gc heap details\n");
4635             return Status;
4636         }
4637
4638         HeapUsageStat hpUsage;
4639         if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage))
4640         {
4641             ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", 0, 
4642                 hpUsage.genUsage[0].allocd, hpUsage.genUsage[1].allocd, 
4643                 hpUsage.genUsage[2].allocd, hpUsage.genUsage[3].allocd);
4644             ExtOut("\nFree space:                                                 Percentage\n");
4645             ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", 0, 
4646                 hpUsage.genUsage[0].freed, hpUsage.genUsage[1].freed, 
4647                 hpUsage.genUsage[2].freed, hpUsage.genUsage[3].freed);
4648             tempf = ((float)(hpUsage.genUsage[0].freed+hpUsage.genUsage[1].freed+hpUsage.genUsage[2].freed)) /
4649                 (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd);
4650             ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf), 
4651                 (int)(100*((float)hpUsage.genUsage[3].freed) / (hpUsage.genUsage[3].allocd)));
4652             if (bIncUnreachable)
4653             {
4654             ExtOut("\nUnrooted objects:                                           Percentage\n");
4655             ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", 0, 
4656                 hpUsage.genUsage[0].unrooted, hpUsage.genUsage[1].unrooted, 
4657                 hpUsage.genUsage[2].unrooted, hpUsage.genUsage[3].unrooted);
4658             tempf = ((float)(hpUsage.genUsage[0].unrooted+hpUsage.genUsage[1].unrooted+hpUsage.genUsage[2].unrooted)) / 
4659                 (hpUsage.genUsage[0].allocd+hpUsage.genUsage[1].allocd+hpUsage.genUsage[2].allocd);
4660             ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
4661                 (int)(100*((float)hpUsage.genUsage[3].unrooted) / (hpUsage.genUsage[3].allocd)));
4662             }
4663         }
4664     }
4665     else
4666     {
4667         float tempf;
4668         DacpGcHeapData gcheap;
4669         if (gcheap.Request(g_sos) != S_OK)
4670         {
4671             ExtErr("Error requesting GC Heap data\n");
4672             return Status;
4673         }
4674
4675         DWORD dwAllocSize;
4676         DWORD dwNHeaps = gcheap.HeapCount;
4677         if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
4678         {
4679             ExtErr("Failed to get GCHeaps:  integer overflow\n");
4680             return Status;
4681         }
4682
4683         CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
4684         if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
4685         {
4686             ExtErr("Failed to get GCHeaps\n");
4687             return Status;
4688         }
4689
4690         ArrayHolder<HeapUsageStat> hpUsage = new NOTHROW HeapUsageStat[dwNHeaps];
4691         if (hpUsage == NULL)
4692         {
4693             ReportOOM();
4694             return Status;
4695         }
4696
4697         // aggregate stats accross heaps / generation
4698         GenUsageStat genUsageStat[4] = {0, 0, 0, 0};
4699
4700         for (DWORD n = 0; n < dwNHeaps; n ++)
4701         {
4702             DacpGcHeapDetails heapDetails;
4703             if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
4704             {
4705                 ExtErr("Error requesting gc heap details\n");
4706                 return Status;
4707             }
4708
4709             if (GCHeapUsageStats(heapDetails, bIncUnreachable, &hpUsage[n]))
4710             {
4711                 for (int i = 0; i < 4; ++i)
4712                 {
4713                     genUsageStat[i].allocd   += hpUsage[n].genUsage[i].allocd;
4714                     genUsageStat[i].freed    += hpUsage[n].genUsage[i].freed;
4715                     if (bIncUnreachable)
4716                     {
4717                     genUsageStat[i].unrooted += hpUsage[n].genUsage[i].unrooted;
4718                     }
4719                 }
4720             }
4721         }
4722
4723         for (DWORD n = 0; n < dwNHeaps; n ++)
4724         {
4725             ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n", n, 
4726                 hpUsage[n].genUsage[0].allocd, hpUsage[n].genUsage[1].allocd, 
4727                 hpUsage[n].genUsage[2].allocd, hpUsage[n].genUsage[3].allocd);
4728         }
4729         ExtOut("Total    %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
4730             genUsageStat[0].allocd, genUsageStat[1].allocd, 
4731             genUsageStat[2].allocd, genUsageStat[3].allocd);
4732
4733         ExtOut("\nFree space:                                                 Percentage\n");
4734         for (DWORD n = 0; n < dwNHeaps; n ++)
4735         {
4736             ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", n, 
4737                 hpUsage[n].genUsage[0].freed, hpUsage[n].genUsage[1].freed, 
4738                 hpUsage[n].genUsage[2].freed, hpUsage[n].genUsage[3].freed);
4739
4740             tempf = ((float)(hpUsage[n].genUsage[0].freed+hpUsage[n].genUsage[1].freed+hpUsage[n].genUsage[2].freed)) /
4741                 (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd);
4742             ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf), 
4743                 (int)(100*((float)hpUsage[n].genUsage[3].freed) / (hpUsage[n].genUsage[3].allocd))
4744             );
4745         }
4746         ExtOut("Total    %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
4747             genUsageStat[0].freed, genUsageStat[1].freed, 
4748             genUsageStat[2].freed, genUsageStat[3].freed);
4749
4750         if (bIncUnreachable)
4751         {
4752             ExtOut("\nUnrooted objects:                                           Percentage\n");
4753             for (DWORD n = 0; n < dwNHeaps; n ++)
4754             {
4755                 ExtOut("Heap%-4d %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u", n, 
4756                     hpUsage[n].genUsage[0].unrooted, hpUsage[n].genUsage[1].unrooted, 
4757                     hpUsage[n].genUsage[2].unrooted, hpUsage[n].genUsage[3].unrooted);
4758
4759                 tempf = ((float)(hpUsage[n].genUsage[0].unrooted+hpUsage[n].genUsage[1].unrooted+hpUsage[n].genUsage[2].unrooted)) / 
4760                     (hpUsage[n].genUsage[0].allocd+hpUsage[n].genUsage[1].allocd+hpUsage[n].genUsage[2].allocd);
4761                 ExtOut("SOH:%3d%% LOH:%3d%%\n", (int)(100 * tempf),
4762                     (int)(100*((float)hpUsage[n].genUsage[3].unrooted) / (hpUsage[n].genUsage[3].allocd)));
4763             }
4764             ExtOut("Total    %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u %12" POINTERSIZE_TYPE "u\n",
4765                 genUsageStat[0].unrooted, genUsageStat[1].unrooted, 
4766                 genUsageStat[2].unrooted, genUsageStat[3].unrooted);
4767         }
4768
4769     }
4770
4771     return Status;
4772     
4773 #else
4774
4775     _ASSERTE(false);
4776     return E_FAIL;
4777
4778 #endif // FEATURE_PAL
4779 }
4780
4781 /**********************************************************************\
4782 * Routine Description:                                                 *
4783 *                                                                      *
4784 *    This function dumps what is in the syncblock cache.  By default   *  
4785 *    it dumps all active syncblocks.  Using -all to dump all syncblocks
4786 *                                                                      *
4787 \**********************************************************************/
4788 DECLARE_API(SyncBlk)
4789 {
4790     INIT_API();    
4791     MINIDUMP_NOT_SUPPORTED();    
4792     
4793     BOOL bDumpAll = FALSE;
4794     size_t nbAsked = 0;
4795     BOOL dml = FALSE;
4796
4797     CMDOption option[] = 
4798     {   // name, vptr, type, hasValue
4799         {"-all", &bDumpAll, COBOOL, FALSE},
4800         {"/d", &dml, COBOOL, FALSE}
4801     };
4802     CMDValue arg[] = 
4803     {   // vptr, type
4804         {&nbAsked, COSIZE_T}
4805     };
4806     size_t nArg;
4807     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
4808     {
4809         return Status;
4810     }
4811
4812     EnableDMLHolder dmlHolder(dml);
4813     DacpSyncBlockData syncBlockData;
4814     if (syncBlockData.Request(g_sos,1) != S_OK)
4815     {
4816         ExtOut("Error requesting SyncBlk data\n");
4817         return Status;
4818     }
4819
4820     DWORD dwCount = syncBlockData.SyncBlockCount;
4821     
4822     ExtOut("Index" WIN64_8SPACES " SyncBlock MonitorHeld Recursion Owning Thread Info" WIN64_8SPACES "  SyncBlock Owner\n");
4823     ULONG freeCount = 0;
4824     ULONG CCWCount = 0;
4825     ULONG RCWCount = 0;
4826     ULONG CFCount = 0;
4827     for (DWORD nb = 1; nb <= dwCount; nb++)
4828     {
4829         if (IsInterrupt())
4830             return Status;
4831         
4832         if (nbAsked && nb != nbAsked) 
4833         {
4834             continue;
4835         }
4836
4837         if (syncBlockData.Request(g_sos,nb) != S_OK)
4838         {
4839             ExtOut("SyncBlock %d is invalid%s\n", nb,
4840                 (nb != nbAsked) ? ", continuing..." : "");
4841             continue;
4842         }
4843
4844         BOOL bPrint = (bDumpAll || nb == nbAsked || (syncBlockData.MonitorHeld > 0 && !syncBlockData.bFree));
4845
4846         if (bPrint)
4847         {
4848             ExtOut("%5d ", nb);
4849             if (!syncBlockData.bFree || nb != nbAsked)
4850             {            
4851                 ExtOut("%p  ", syncBlockData.SyncBlockPointer); 
4852                 ExtOut("%11d ", syncBlockData.MonitorHeld);
4853                 ExtOut("%9d ", syncBlockData.Recursion);
4854                 ExtOut("%p ", syncBlockData.HoldingThread);
4855
4856                 if (syncBlockData.HoldingThread == ~0ul)
4857                 {
4858                     ExtOut(" orphaned ");
4859                 }
4860                 else if (syncBlockData.HoldingThread != NULL)
4861                 {
4862                     DacpThreadData Thread;
4863                     if ((Status = Thread.Request(g_sos, syncBlockData.HoldingThread)) != S_OK)
4864                     {
4865                         ExtOut("Failed to request Thread at %p\n", syncBlockData.HoldingThread);
4866                         return Status;
4867                     }
4868
4869                     DMLOut(DMLThreadID(Thread.osThreadId));
4870                     ULONG id;
4871                     if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
4872                     {
4873                         ExtOut("%4d ", id);
4874                     }
4875                     else
4876                     {
4877                         ExtOut(" XXX ");
4878                     }
4879                 }
4880                 else
4881                 {
4882                     ExtOut("    none  ");
4883                 }
4884
4885                 if (syncBlockData.bFree)
4886                 {
4887                     ExtOut("  %8d", 0);    // TODO: do we need to print the free synctable list?
4888                 }
4889                 else
4890                 {
4891                     sos::Object obj = TO_TADDR(syncBlockData.Object);
4892                     DMLOut("  %s %S", DMLObject(syncBlockData.Object), obj.GetTypeName());
4893                 }
4894             }
4895         }
4896                                     
4897         if (syncBlockData.bFree) 
4898         {
4899             freeCount ++;
4900             if (bPrint) {
4901                 ExtOut(" Free");
4902             }
4903         }
4904         else 
4905         {
4906 #ifdef FEATURE_COMINTEROP            
4907             if (syncBlockData.COMFlags) {
4908                 switch (syncBlockData.COMFlags) {
4909                 case SYNCBLOCKDATA_COMFLAGS_CCW:
4910                     CCWCount ++;
4911                     break;
4912                 case SYNCBLOCKDATA_COMFLAGS_RCW:
4913                     RCWCount ++;
4914                     break;
4915                 case SYNCBLOCKDATA_COMFLAGS_CF:
4916                     CFCount ++;
4917                     break;
4918                 }
4919             }
4920 #endif // FEATURE_COMINTEROP            
4921         }
4922
4923         if (syncBlockData.MonitorHeld > 1)            
4924         {
4925             // TODO: implement this
4926             /*
4927             ExtOut(" ");
4928             DWORD_PTR pHead = (DWORD_PTR)vSyncBlock.m_Link.m_pNext;
4929             DWORD_PTR pNext = pHead;
4930             Thread vThread;
4931     
4932             while (1)
4933             {
4934                 if (IsInterrupt())
4935                     return Status;
4936                 DWORD_PTR pWaitEventLink = pNext - offsetLinkSB;
4937                 WaitEventLink vWaitEventLink;
4938                 vWaitEventLink.Fill(pWaitEventLink);
4939                 if (!CallStatus) {
4940                     break;
4941                 }
4942                 DWORD_PTR dwAddr = (DWORD_PTR)vWaitEventLink.m_Thread;
4943                 ExtOut("%x ", dwAddr);
4944                 vThread.Fill (dwAddr);
4945                 if (!CallStatus) {
4946                     break;
4947                 }
4948                 if (bPrint)
4949                     DMLOut("%s,", DMLThreadID(vThread.m_OSThreadId));
4950                 pNext = (DWORD_PTR)vWaitEventLink.m_LinkSB.m_pNext;
4951                 if (pNext == 0)
4952                     break;
4953             }  
4954             */
4955         }
4956         
4957         if (bPrint)
4958             ExtOut("\n");
4959     }
4960     
4961     ExtOut("-----------------------------\n");
4962     ExtOut("Total           %d\n", dwCount);
4963     ExtOut("CCW             %d\n", CCWCount);
4964     ExtOut("RCW             %d\n", RCWCount);
4965     ExtOut("ComClassFactory %d\n", CFCount);
4966     ExtOut("Free            %d\n", freeCount);
4967    
4968     return Status;
4969 }
4970
4971 #ifdef FEATURE_COMINTEROP
4972 struct VisitRcwArgs
4973 {
4974     BOOL bDetail;
4975     UINT MTACount;
4976     UINT STACount;
4977     ULONG FTMCount;
4978 };
4979
4980 void VisitRcw(CLRDATA_ADDRESS RCW,CLRDATA_ADDRESS Context,CLRDATA_ADDRESS Thread, BOOL bIsFreeThreaded, LPVOID token)
4981 {
4982     VisitRcwArgs *pArgs = (VisitRcwArgs *) token;
4983
4984     if (pArgs->bDetail)
4985     {
4986         if (pArgs->MTACount == 0 && pArgs->STACount == 0 && pArgs->FTMCount == 0)
4987         {
4988             // First time, print a header
4989             ExtOut("RuntimeCallableWrappers (RCW) to be cleaned:\n");
4990             ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s Apartment\n",
4991                 "RCW", "CONTEXT", "THREAD");
4992         }        
4993         LPCSTR szThreadApartment;
4994         if (bIsFreeThreaded)
4995         {
4996             szThreadApartment = "(FreeThreaded)";
4997             pArgs->FTMCount++;
4998         }
4999         else if (Thread == NULL)
5000         {
5001             szThreadApartment = "(MTA)";
5002             pArgs->MTACount++;
5003         }
5004         else
5005         {
5006             szThreadApartment = "(STA)";
5007             pArgs->STACount++;
5008         }        
5009         
5010         ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %9s\n",
5011             SOS_PTR(RCW), 
5012             SOS_PTR(Context), 
5013             SOS_PTR(Thread),
5014             szThreadApartment);
5015     }
5016 }
5017
5018 DECLARE_API(RCWCleanupList)
5019 {
5020     INIT_API();
5021     MINIDUMP_NOT_SUPPORTED();    
5022
5023     DWORD_PTR p_CleanupList = GetExpression(args);
5024
5025     VisitRcwArgs travArgs;
5026     ZeroMemory(&travArgs,sizeof(VisitRcwArgs));  
5027     travArgs.bDetail = TRUE;
5028
5029     // We need to detect when !RCWCleanupList is called with an expression which evaluates to 0
5030     // (to print out an Invalid parameter message), but at the same time we need to allow an
5031     // empty argument list which would result in p_CleanupList equaling 0.
5032     if (p_CleanupList || strlen(args) == 0)
5033     {
5034         HRESULT hr = g_sos->TraverseRCWCleanupList(p_CleanupList, (VISITRCWFORCLEANUP)VisitRcw, &travArgs);
5035     
5036         if (SUCCEEDED(hr))
5037         {
5038             ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
5039             ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
5040             ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);
5041         }
5042         else
5043         {
5044             ExtOut("An error occurred while traversing the cleanup list.\n");
5045         }
5046     }
5047     else
5048     {
5049         ExtOut("Invalid parameter %s\n", args);
5050     }
5051     
5052     return Status;
5053 }
5054 #endif // FEATURE_COMINTEROP
5055
5056
5057 /**********************************************************************\
5058 * Routine Description:                                                 *
5059 *                                                                      *
5060 *    This function is called to dump the contents of the finalizer     *
5061 *    queue.                                                            *  
5062 *                                                                      *
5063 \**********************************************************************/
5064 DECLARE_API(FinalizeQueue)
5065 {
5066     INIT_API();
5067     MINIDUMP_NOT_SUPPORTED();    
5068     
5069     BOOL bDetail = FALSE;
5070     BOOL bAllReady = FALSE;
5071     BOOL bShort    = FALSE;
5072     BOOL dml = FALSE;
5073     TADDR taddrMT  = 0;
5074
5075     CMDOption option[] = 
5076     {   // name, vptr, type, hasValue
5077         {"-detail",   &bDetail,   COBOOL, FALSE},
5078         {"-allReady", &bAllReady, COBOOL, FALSE},
5079         {"-short",    &bShort,    COBOOL, FALSE},
5080         {"/d",        &dml,       COBOOL, FALSE},
5081         {"-mt",       &taddrMT,   COHEX,  TRUE},
5082     };
5083
5084     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
5085     {
5086         return Status;
5087     }
5088
5089     EnableDMLHolder dmlHolder(dml);
5090     if (!bShort)
5091     {
5092         DacpSyncBlockCleanupData dsbcd;
5093         CLRDATA_ADDRESS sbCurrent = NULL;
5094         ULONG cleanCount = 0;
5095         while ((dsbcd.Request(g_sos,sbCurrent) == S_OK) && dsbcd.SyncBlockPointer)
5096         {
5097             if (bDetail)
5098             {
5099                 if (cleanCount == 0) // print first time only
5100                 {
5101                     ExtOut("SyncBlocks to be cleaned by the finalizer thread:\n");
5102                     ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
5103                         "SyncBlock", "RCW", "CCW", "ComClassFactory");                
5104                 }
5105                 
5106                 ExtOut("%" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p %" POINTERSIZE "p\n", 
5107                     (ULONG64) dsbcd.SyncBlockPointer,
5108                     (ULONG64) dsbcd.blockRCW,
5109                     (ULONG64) dsbcd.blockCCW,
5110                     (ULONG64) dsbcd.blockClassFactory);
5111             }
5112
5113             cleanCount++;
5114             sbCurrent = dsbcd.nextSyncBlock;
5115             if (sbCurrent == NULL)
5116             {
5117                 break;
5118             }
5119         }
5120
5121         ExtOut("SyncBlocks to be cleaned up: %d\n", cleanCount);
5122
5123 #ifdef FEATURE_COMINTEROP
5124         VisitRcwArgs travArgs;
5125         ZeroMemory(&travArgs,sizeof(VisitRcwArgs));  
5126         travArgs.bDetail = bDetail;
5127         g_sos->TraverseRCWCleanupList(0, (VISITRCWFORCLEANUP) VisitRcw, &travArgs);
5128         ExtOut("Free-Threaded Interfaces to be released: %d\n", travArgs.FTMCount);
5129         ExtOut("MTA Interfaces to be released: %d\n", travArgs.MTACount);
5130         ExtOut("STA Interfaces to be released: %d\n", travArgs.STACount);    
5131 #endif // FEATURE_COMINTEROP    
5132
5133 // noRCW:
5134         ExtOut("----------------------------------\n");
5135     }
5136
5137     // GC Heap
5138     DWORD dwNHeaps = GetGcHeapCount();
5139
5140     HeapStat hpStat;
5141
5142     if (!IsServerBuild())
5143     {
5144         DacpGcHeapDetails heapDetails;
5145         if (heapDetails.Request(g_sos) != S_OK)
5146         {
5147             ExtOut("Error requesting details\n");
5148             return Status;
5149         }
5150
5151         GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
5152     }
5153     else
5154     {   
5155         DWORD dwAllocSize;
5156         if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
5157         {
5158             ExtOut("Failed to get GCHeaps:  integer overflow\n");
5159             return Status;
5160         }
5161
5162         CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
5163         if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
5164         {
5165             ExtOut("Failed to get GCHeaps\n");
5166             return Status;
5167         }
5168         
5169         for (DWORD n = 0; n < dwNHeaps; n ++)
5170         {
5171             DacpGcHeapDetails heapDetails;
5172             if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
5173             {
5174                 ExtOut("Error requesting details\n");
5175                 return Status;
5176             }
5177
5178             ExtOut("------------------------------\n");
5179             ExtOut("Heap %d\n", n);
5180             GatherOneHeapFinalization(heapDetails, &hpStat, bAllReady, bShort);
5181         }        
5182     }
5183     
5184     if (!bShort)
5185     {
5186         if (bAllReady)
5187         {
5188             PrintGCStat(&hpStat, "Statistics for all finalizable objects that are no longer rooted:\n");
5189         }
5190         else
5191         {
5192             PrintGCStat(&hpStat, "Statistics for all finalizable objects (including all objects ready for finalization):\n");
5193         }
5194     }
5195
5196     return Status;
5197 }
5198
5199 #endif // FEATURE_PAL
5200
5201 enum {
5202     // These are the values set in m_dwTransientFlags.
5203     // Note that none of these flags survive a prejit save/restore.
5204
5205     M_CRST_NOTINITIALIZED       = 0x00000001,   // Used to prevent destruction of garbage m_crst
5206     M_LOOKUPCRST_NOTINITIALIZED = 0x00000002,
5207
5208     SUPPORTS_UPDATEABLE_METHODS = 0x00000020,
5209     CLASSES_FREED               = 0x00000040,
5210     HAS_PHONY_IL_RVAS           = 0x00000080,
5211     IS_EDIT_AND_CONTINUE        = 0x00000200,
5212 };
5213
5214 void ModuleMapTraverse(UINT index, CLRDATA_ADDRESS methodTable, LPVOID token)
5215 {
5216     ULONG32 rid = (ULONG32)(size_t)token;
5217     NameForMT_s(TO_TADDR(methodTable), g_mdName, mdNameLen);
5218
5219     DMLOut("%s 0x%08x %S\n", DMLMethodTable(methodTable), (ULONG32)TokenFromRid(rid, index), g_mdName);
5220 }
5221
5222
5223 /**********************************************************************\
5224 * Routine Description:                                                 *
5225 *                                                                      *
5226 *    This function is called to dump the contents of a Module          *
5227 *    for a given address                                               *  
5228 *                                                                      *
5229 \**********************************************************************/
5230 DECLARE_API(DumpModule)
5231 {
5232     INIT_API();
5233     MINIDUMP_NOT_SUPPORTED();    
5234     
5235     
5236     DWORD_PTR p_ModuleAddr = NULL;
5237     BOOL bMethodTables = FALSE;
5238     BOOL dml = FALSE;
5239
5240     CMDOption option[] = 
5241     {   // name, vptr, type, hasValue
5242         {"-mt", &bMethodTables, COBOOL, FALSE},
5243 #ifndef FEATURE_PAL
5244         {"/d", &dml, COBOOL, FALSE}
5245 #endif
5246     };
5247     CMDValue arg[] = 
5248     {   // vptr, type
5249         {&p_ModuleAddr, COHEX}
5250     };
5251
5252     size_t nArg;
5253     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
5254     {
5255         return Status;
5256     }
5257     if (nArg != 1)
5258     {
5259         ExtOut("Usage: DumpModule [-mt] <Module Address>\n");
5260         return Status;
5261     }
5262
5263     EnableDMLHolder dmlHolder(dml);
5264     DacpModuleData module;
5265     if ((Status=module.Request(g_sos, TO_CDADDR(p_ModuleAddr)))!=S_OK)
5266     {
5267         ExtOut("Fail to fill Module %p\n", SOS_PTR(p_ModuleAddr));
5268         return Status;
5269     }
5270     
5271     WCHAR FileName[MAX_LONGPATH];
5272     FileNameForModule (&module, FileName);
5273     ExtOut("Name:       %S\n", FileName[0] ? FileName : W("Unknown Module"));
5274
5275     ExtOut("Attributes: ");
5276     if (module.bIsPEFile)
5277         ExtOut("PEFile ");
5278     if (module.bIsReflection)
5279         ExtOut("Reflection ");
5280     if (module.dwTransientFlags & SUPPORTS_UPDATEABLE_METHODS)
5281         ExtOut("SupportsUpdateableMethods");
5282     ExtOut("\n");
5283     
5284     DMLOut("Assembly:   %s\n", DMLAssembly(module.Assembly));
5285
5286     ExtOut("LoaderHeap:              %p\n", SOS_PTR(module.pLookupTableHeap));
5287     ExtOut("TypeDefToMethodTableMap: %p\n", SOS_PTR(module.TypeDefToMethodTableMap));
5288     ExtOut("TypeRefToMethodTableMap: %p\n", SOS_PTR(module.TypeRefToMethodTableMap));
5289     ExtOut("MethodDefToDescMap:      %p\n", SOS_PTR(module.MethodDefToDescMap));
5290     ExtOut("FieldDefToDescMap:       %p\n", SOS_PTR(module.FieldDefToDescMap));
5291     ExtOut("MemberRefToDescMap:      %p\n", SOS_PTR(module.MemberRefToDescMap));
5292     ExtOut("FileReferencesMap:       %p\n", SOS_PTR(module.FileReferencesMap));
5293     ExtOut("AssemblyReferencesMap:   %p\n", SOS_PTR(module.ManifestModuleReferencesMap));
5294
5295     if (module.ilBase && module.metadataStart)
5296         ExtOut("MetaData start address:  %p (%d bytes)\n", SOS_PTR(module.metadataStart), module.metadataSize);
5297
5298     if (bMethodTables)
5299     {
5300         ExtOut("\nTypes defined in this module\n\n");
5301         ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %s\n", "MT", "TypeDef", "Name");
5302                 
5303         ExtOut("------------------------------------------------------------------------------\n");
5304         g_sos->TraverseModuleMap(TYPEDEFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil);        
5305
5306         ExtOut("\nTypes referenced in this module\n\n");
5307         ExtOut("%" POINTERSIZE "s   %" POINTERSIZE "s %s\n", "MT", "TypeRef", "Name");
5308         
5309         ExtOut("------------------------------------------------------------------------------\n");
5310         g_sos->TraverseModuleMap(TYPEREFTOMETHODTABLE, TO_CDADDR(p_ModuleAddr), ModuleMapTraverse, (LPVOID)mdTypeDefNil);     
5311     }
5312     
5313     return Status;
5314 }
5315
5316 /**********************************************************************\
5317 * Routine Description:                                                 *
5318 *                                                                      *
5319 *    This function is called to dump the contents of a Domain          *
5320 *    for a given address                                               *  
5321 *                                                                      *
5322 \**********************************************************************/
5323 DECLARE_API(DumpDomain)
5324 {
5325     INIT_API();
5326     MINIDUMP_NOT_SUPPORTED();
5327     
5328     DWORD_PTR p_DomainAddr = 0;
5329     BOOL dml = FALSE;
5330
5331     CMDOption option[] = 
5332     {   // name, vptr, type, hasValue
5333 #ifndef FEATURE_PAL
5334         {"/d", &dml, COBOOL, FALSE},
5335 #endif
5336     };
5337     CMDValue arg[] = 
5338     {   // vptr, type
5339         {&p_DomainAddr, COHEX},
5340     };
5341     size_t nArg;
5342
5343     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
5344     {
5345         return Status;
5346     }
5347
5348     EnableDMLHolder dmlHolder(dml);
5349
5350     DacpAppDomainStoreData adsData;
5351     if ((Status=adsData.Request(g_sos))!=S_OK)
5352     {
5353         ExtOut("Unable to get AppDomain information\n");
5354         return Status;
5355     }
5356     
5357     if (p_DomainAddr)
5358     {
5359         DacpAppDomainData appDomain1;
5360         if ((Status=appDomain1.Request(g_sos, TO_CDADDR(p_DomainAddr)))!=S_OK)
5361         {
5362             ExtOut("Fail to fill AppDomain\n");
5363             return Status;
5364         }
5365
5366         ExtOut("--------------------------------------\n");
5367
5368         if (p_DomainAddr == adsData.sharedDomain)
5369         {
5370             DMLOut("Shared Domain:      %s\n", DMLDomain(adsData.sharedDomain));
5371         }
5372         else if (p_DomainAddr == adsData.systemDomain)
5373         {
5374             DMLOut("System Domain:      %s\n", DMLDomain(adsData.systemDomain));
5375         }
5376         else
5377         {
5378             DMLOut("Domain %d:%s          %s\n", appDomain1.dwId, (appDomain1.dwId >= 10) ? "" : " ", DMLDomain(p_DomainAddr));
5379         }
5380
5381         DomainInfo(&appDomain1);
5382         return Status;
5383     }
5384         
5385     ExtOut("--------------------------------------\n");
5386     DMLOut("System Domain:      %s\n", DMLDomain(adsData.systemDomain));
5387     DacpAppDomainData appDomain;
5388     if ((Status=appDomain.Request(g_sos,adsData.systemDomain))!=S_OK)
5389     {
5390         ExtOut("Unable to get system domain info.\n");
5391         return Status;
5392     }
5393     DomainInfo(&appDomain);
5394     
5395     ExtOut("--------------------------------------\n");
5396     DMLOut("Shared Domain:      %s\n", DMLDomain(adsData.sharedDomain));
5397     if ((Status=appDomain.Request(g_sos, adsData.sharedDomain))!=S_OK)
5398     {
5399         ExtOut("Unable to get shared domain info\n");
5400         return Status;
5401     }
5402     DomainInfo(&appDomain);
5403
5404     ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount];
5405     if (pArray==NULL)
5406     {
5407         ReportOOM();
5408         return Status;
5409     }
5410
5411     if ((Status=g_sos->GetAppDomainList(adsData.DomainCount, pArray, NULL))!=S_OK)
5412     {
5413         ExtOut("Unable to get array of AppDomains\n");
5414         return Status;
5415     }
5416
5417     for (int n=0;n<adsData.DomainCount;n++)
5418     {
5419         if (IsInterrupt())
5420             break;
5421
5422         if ((Status=appDomain.Request(g_sos, pArray[n])) != S_OK)
5423         {
5424             ExtOut("Failed to get appdomain %p, error %lx\n", SOS_PTR(pArray[n]), Status);
5425             return Status;
5426         }
5427
5428         ExtOut("--------------------------------------\n");
5429         DMLOut("Domain %d:%s          %s\n", appDomain.dwId, (appDomain.dwId >= 10) ? "" : " ", DMLDomain(pArray[n]));
5430         DomainInfo(&appDomain);
5431     }
5432
5433     return Status;
5434 }
5435
5436 /**********************************************************************\
5437 * Routine Description:                                                 *
5438 *                                                                      *
5439 *    This function is called to dump the contents of a Assembly        *
5440 *    for a given address                                               *  
5441 *                                                                      *
5442 \**********************************************************************/
5443 DECLARE_API(DumpAssembly)
5444 {
5445     INIT_API();
5446     MINIDUMP_NOT_SUPPORTED();    
5447     
5448     DWORD_PTR p_AssemblyAddr = 0;
5449     BOOL dml = FALSE;
5450
5451     CMDOption option[] = 
5452     {   // name, vptr, type, hasValue
5453 #ifndef FEATURE_PAL
5454         {"/d", &dml, COBOOL, FALSE},
5455 #endif
5456     };
5457     CMDValue arg[] = 
5458     {   // vptr, type
5459         {&p_AssemblyAddr, COHEX},
5460     };
5461     size_t nArg;
5462
5463     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
5464     {
5465         return Status;
5466     }
5467
5468     EnableDMLHolder dmlHolder(dml);
5469
5470     if (p_AssemblyAddr == 0)
5471     {
5472         ExtOut("Invalid Assembly %s\n", args);
5473         return Status;
5474     }
5475     
5476     DacpAssemblyData Assembly;
5477     if ((Status=Assembly.Request(g_sos, TO_CDADDR(p_AssemblyAddr)))!=S_OK)
5478     {
5479         ExtOut("Fail to fill Assembly\n");
5480         return Status;
5481     }
5482     DMLOut("Parent Domain:      %s\n", DMLDomain(Assembly.ParentDomain));
5483     if (g_sos->GetAssemblyName(TO_CDADDR(p_AssemblyAddr), mdNameLen, g_mdName, NULL)==S_OK)
5484         ExtOut("Name:               %S\n", g_mdName);
5485     else
5486         ExtOut("Name:               Unknown\n");
5487
5488     AssemblyInfo(&Assembly);
5489     return Status;
5490 }
5491
5492
5493 String GetHostingCapabilities(DWORD hostConfig)
5494 {
5495     String result;
5496
5497     bool bAnythingPrinted = false;
5498
5499 #define CHK_AND_PRINT(hType,hStr)                                \
5500     if (hostConfig & (hType)) {                                  \
5501         if (bAnythingPrinted) result += ", ";                    \
5502         result += hStr;                                          \
5503         bAnythingPrinted = true;                                 \
5504     }
5505
5506     CHK_AND_PRINT(CLRMEMORYHOSTED, "Memory");
5507     CHK_AND_PRINT(CLRTASKHOSTED, "Task");
5508     CHK_AND_PRINT(CLRSYNCHOSTED, "Sync");
5509     CHK_AND_PRINT(CLRTHREADPOOLHOSTED, "Threadpool");
5510     CHK_AND_PRINT(CLRIOCOMPLETIONHOSTED, "IOCompletion");
5511     CHK_AND_PRINT(CLRASSEMBLYHOSTED, "Assembly");
5512     CHK_AND_PRINT(CLRGCHOSTED, "GC");
5513     CHK_AND_PRINT(CLRSECURITYHOSTED, "Security");
5514
5515 #undef CHK_AND_PRINT
5516
5517     return result;
5518 }
5519
5520 /**********************************************************************\
5521 * Routine Description:                                                 *
5522 *                                                                      *
5523 *    This function is called to dump the managed threads               *
5524 *                                                                      *
5525 \**********************************************************************/
5526 HRESULT PrintThreadsFromThreadStore(BOOL bMiniDump, BOOL bPrintLiveThreadsOnly)
5527 {
5528     HRESULT Status;
5529     
5530     DacpThreadStoreData ThreadStore;
5531     if ((Status = ThreadStore.Request(g_sos)) != S_OK)
5532     {
5533         Print("Failed to request ThreadStore\n");
5534         return Status;
5535     }
5536
5537     TableOutput table(2, 17);
5538
5539     table.WriteRow("ThreadCount:", Decimal(ThreadStore.threadCount));
5540     table.WriteRow("UnstartedThread:", Decimal(ThreadStore.unstartedThreadCount));
5541     table.WriteRow("BackgroundThread:", Decimal(ThreadStore.backgroundThreadCount));
5542     table.WriteRow("PendingThread:", Decimal(ThreadStore.pendingThreadCount));
5543     table.WriteRow("DeadThread:", Decimal(ThreadStore.deadThreadCount));
5544
5545     if (ThreadStore.fHostConfig & ~CLRHOSTED)
5546     {
5547         String hosting = "yes";
5548
5549         hosting += " (";
5550         hosting += GetHostingCapabilities(ThreadStore.fHostConfig);
5551         hosting += ")";
5552
5553         table.WriteRow("Hosted Runtime:", hosting);
5554     }
5555     else
5556     {
5557         table.WriteRow("Hosted Runtime:", "no");
5558     }
5559
5560     const bool hosted = (ThreadStore.fHostConfig & CLRTASKHOSTED) != 0;
5561     table.ReInit(hosted ? 12 : 11, POINTERSIZE_HEX);
5562     table.SetWidths(10, 4, 4, 4, _max(9, POINTERSIZE_HEX), 
5563                       8, 11, 1+POINTERSIZE_HEX*2, POINTERSIZE_HEX,
5564                       5, 3, POINTERSIZE_HEX);
5565
5566     table.SetColAlignment(0, AlignRight);
5567     table.SetColAlignment(1, AlignRight);
5568     table.SetColAlignment(2, AlignRight);
5569     table.SetColAlignment(4, AlignRight);
5570
5571     table.WriteColumn(8, "Lock");
5572     table.WriteRow("", "ID", "OSID", "ThreadOBJ", "State", "GC Mode", "GC Alloc Context", "Domain", "Count", "Apt");
5573     
5574     if (hosted)
5575         table.WriteColumn("Fiber");
5576
5577     table.WriteColumn("Exception");
5578     
5579     DacpThreadData Thread;
5580     CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
5581     while (CurThread)
5582     {
5583         if (IsInterrupt())
5584             break;
5585
5586         if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
5587         {
5588             PrintLn("Failed to request Thread at ", Pointer(CurThread));
5589             return Status;
5590         }
5591
5592         BOOL bSwitchedOutFiber = Thread.osThreadId == SWITCHED_OUT_FIBER_OSID;
5593         if (!IsKernelDebugger())
5594         {
5595             ULONG id = 0;          
5596             
5597             if (bSwitchedOutFiber)
5598             {
5599                 table.WriteColumn(0, "<<<< ");
5600             }
5601             else if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
5602             {
5603                 table.WriteColumn(0, Decimal(id));
5604             }
5605             else if (bPrintLiveThreadsOnly)
5606             {
5607                 CurThread = Thread.nextThread;
5608                 continue;
5609             }
5610             else
5611             {
5612                 table.WriteColumn(0, "XXXX ");
5613             }
5614         }
5615
5616         table.WriteColumn(1, Decimal(Thread.corThreadId));
5617         table.WriteColumn(2, ThreadID(bSwitchedOutFiber ? 0 : Thread.osThreadId));
5618         table.WriteColumn(3, Pointer(CurThread));
5619         table.WriteColumn(4, ThreadState(Thread.state));
5620         table.WriteColumn(5,  Thread.preemptiveGCDisabled == 1 ? "Cooperative" : "Preemptive");
5621         table.WriteColumnFormat(6, "%p:%p", Thread.allocContextPtr, Thread.allocContextLimit);
5622
5623         if (Thread.domain)
5624         {
5625             table.WriteColumn(7, AppDomainPtr(Thread.domain));
5626         }
5627         else
5628         {
5629             CLRDATA_ADDRESS domain = 0;
5630             if (FAILED(g_sos->GetDomainFromContext(Thread.context, &domain)))
5631                 table.WriteColumn(7, "<error>");
5632             else
5633                 table.WriteColumn(7, AppDomainPtr(domain));
5634         }
5635         
5636         table.WriteColumn(8, Decimal(Thread.lockCount));
5637
5638         // Apartment state
5639 #ifndef FEATURE_PAL           
5640         DWORD_PTR OleTlsDataAddr;
5641         if (!bSwitchedOutFiber 
5642                 && SafeReadMemory(Thread.teb + offsetof(TEB, ReservedForOle),
5643                             &OleTlsDataAddr,
5644                             sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0)
5645         {
5646             DWORD AptState;
5647             if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags),
5648                                &AptState,
5649                                sizeof(AptState), NULL))
5650             {
5651                 if (AptState & OLETLS_APARTMENTTHREADED)
5652                     table.WriteColumn(9, "STA");
5653                 else if (AptState & OLETLS_MULTITHREADED)
5654                     table.WriteColumn(9, "MTA");
5655                 else if (AptState & OLETLS_INNEUTRALAPT)
5656                     table.WriteColumn(9, "NTA");
5657                 else
5658                     table.WriteColumn(9, "Ukn");
5659             }
5660             else
5661             {
5662                 table.WriteColumn(9, "Ukn");
5663             }
5664         }
5665         else
5666 #endif // FEATURE_PAL
5667             table.WriteColumn(9, "Ukn");
5668
5669         if (hosted)
5670             table.WriteColumn(10, Thread.fiberData);
5671         
5672         WString lastCol;
5673         if (CurThread == ThreadStore.finalizerThread)
5674             lastCol += W("(Finalizer) ");
5675         if (CurThread == ThreadStore.gcThread)
5676             lastCol += W("(GC) ");
5677
5678         const int TS_TPWorkerThread         = 0x01000000;    // is this a threadpool worker thread?
5679         const int TS_CompletionPortThread   = 0x08000000;    // is this is a completion port thread?
5680         
5681         if (Thread.state & TS_TPWorkerThread)
5682             lastCol += W("(Threadpool Worker) ");
5683         else if (Thread.state & TS_CompletionPortThread)
5684             lastCol += W("(Threadpool Completion Port) ");
5685         
5686         
5687         TADDR taLTOH;
5688         if (Thread.lastThrownObjectHandle && SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
5689                                                             &taLTOH, sizeof(taLTOH), NULL) && taLTOH)
5690         {
5691             TADDR taMT;
5692             if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL))
5693             {
5694                 if (NameForMT_s(taMT, g_mdName, mdNameLen))
5695                     lastCol += WString(g_mdName) + W(" ") + ExceptionPtr(taLTOH);
5696                 else
5697                     lastCol += WString(W("<Invalid Object> (")) + Pointer(taLTOH) + W(")");
5698
5699                 // Print something if there are nested exceptions on the thread
5700                 if (Thread.firstNestedException)
5701                     lastCol += W(" (nested exceptions)");
5702             }
5703         }
5704
5705         table.WriteColumn(lastCol);
5706         CurThread = Thread.nextThread;
5707     }
5708
5709     return Status;
5710 }
5711
5712 #ifndef FEATURE_PAL
5713 HRESULT PrintSpecialThreads()
5714 {
5715     Print("\n");
5716
5717     DWORD dwCLRTLSDataIndex = 0;
5718     HRESULT Status = g_sos->GetTLSIndex(&dwCLRTLSDataIndex);
5719     
5720     if (!SUCCEEDED (Status))
5721     {
5722         Print("Failed to retrieve Tls Data index\n");
5723         return Status;
5724     }
5725
5726
5727     ULONG ulOriginalThreadID = 0;
5728     Status = g_ExtSystem->GetCurrentThreadId (&ulOriginalThreadID);
5729     if (!SUCCEEDED (Status))
5730     {
5731         Print("Failed to require current Thread ID\n");
5732         return Status;
5733     }
5734
5735     ULONG ulTotalThreads = 0;
5736     Status = g_ExtSystem->GetNumberThreads (&ulTotalThreads);
5737     if (!SUCCEEDED (Status))
5738     {
5739         Print("Failed to require total thread number\n");
5740         return Status;
5741     }
5742
5743     TableOutput table(3, 4, AlignRight, 5);
5744     table.WriteRow("", "OSID", "Special thread type");
5745
5746     for (ULONG ulThread = 0; ulThread < ulTotalThreads; ulThread++)
5747     {
5748         ULONG Id = 0;
5749         ULONG SysId = 0;        
5750         HRESULT threadStatus = g_ExtSystem->GetThreadIdsByIndex(ulThread, 1, &Id, &SysId);
5751         if (!SUCCEEDED (threadStatus))
5752         {
5753             PrintLn("Failed to get thread ID for thread ", Decimal(ulThread));        
5754             continue;
5755         }    
5756
5757         threadStatus = g_ExtSystem->SetCurrentThreadId(Id);
5758         if (!SUCCEEDED (threadStatus))
5759         {
5760             PrintLn("Failed to switch to thread ", ThreadID(SysId));        
5761             continue;
5762         }    
5763
5764         CLRDATA_ADDRESS cdaTeb = 0;        
5765         threadStatus = g_ExtSystem->GetCurrentThreadTeb(&cdaTeb);
5766         if (!SUCCEEDED (threadStatus))
5767         {
5768             PrintLn("Failed to get Teb for Thread ", ThreadID(SysId));        
5769             continue;
5770         } 
5771
5772         TADDR CLRTLSDataAddr = 0;
5773
5774 #ifdef FEATURE_IMPLICIT_TLS
5775         TADDR tlsArrayAddr = NULL;
5776         if (!SafeReadMemory (TO_TADDR(cdaTeb) + WINNT_OFFSETOF__TEB__ThreadLocalStoragePointer , &tlsArrayAddr, sizeof (void**), NULL))
5777         {
5778             PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));        
5779             continue;
5780         }
5781
5782         TADDR moduleTlsDataAddr = 0;
5783
5784         if (!SafeReadMemory (tlsArrayAddr + sizeof (void*) * dwCLRTLSDataIndex, &moduleTlsDataAddr, sizeof (void**), NULL))
5785         {
5786             PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));        
5787             continue;
5788         }
5789
5790         CLRTLSDataAddr = moduleTlsDataAddr + OFFSETOF__TLS__tls_EETlsData;
5791 #else
5792         if (dwCLRTLSDataIndex < TLS_MINIMUM_AVAILABLE)
5793         {
5794             CLRTLSDataAddr = TO_TADDR(cdaTeb) + offsetof(TEB, TlsSlots) + sizeof (void*) * dwCLRTLSDataIndex;
5795         }
5796         else
5797         {
5798             //if TLS index is bigger than TLS_MINIMUM_AVAILABLE, the TLS slot lives in ExpansionSlots
5799             TADDR TebExpsionAddr = NULL;
5800             if (!SafeReadMemory (TO_TADDR(cdaTeb) + offsetof(TEB, TlsExpansionSlots) , &TebExpsionAddr, sizeof (void**), NULL))
5801             {
5802                 PrintLn("Failed to get Tls expansion slots for thread ", ThreadID(SysId));        
5803                 continue;
5804             }
5805
5806             if (TebExpsionAddr == NULL)
5807             {
5808                 continue;
5809             }
5810             
5811             CLRTLSDataAddr = TebExpsionAddr + sizeof (void*) * (dwCLRTLSDataIndex - TLS_MINIMUM_AVAILABLE);
5812         }
5813 #endif // FEATURE_IMPLICIT_TLS
5814
5815         TADDR CLRTLSData = NULL;
5816         if (!SafeReadMemory (CLRTLSDataAddr, &CLRTLSData, sizeof (TADDR), NULL))
5817         {
5818             PrintLn("Failed to get CLR Tls data for thread ", ThreadID(SysId));        
5819             continue;
5820         }
5821
5822         if (CLRTLSData == NULL)
5823         {
5824             continue;
5825         }
5826
5827         size_t ThreadType = 0;
5828         if (!SafeReadMemory (CLRTLSData + sizeof (TADDR) * TlsIdx_ThreadType, &ThreadType, sizeof (size_t), NULL))
5829         {
5830             PrintLn("Failed to get thread type info not found for thread ", ThreadID(SysId));        
5831             continue;
5832         }
5833         
5834         if (ThreadType == 0)
5835         {
5836             continue;
5837         }
5838
5839         table.WriteColumn(0, Decimal(Id));
5840         table.WriteColumn(1, ThreadID(SysId));
5841
5842         String type;
5843         if (ThreadType & ThreadType_GC)
5844         {
5845             type += "GC ";
5846         }
5847         if (ThreadType & ThreadType_Timer)
5848         {
5849             type += "Timer ";
5850         }
5851         if (ThreadType & ThreadType_Gate)
5852         {
5853             type += "Gate ";
5854         }
5855         if (ThreadType & ThreadType_DbgHelper)
5856         {
5857             type += "DbgHelper ";
5858         }
5859         if (ThreadType & ThreadType_Shutdown)
5860         {
5861             type += "Shutdown ";
5862         }
5863         if (ThreadType & ThreadType_DynamicSuspendEE)
5864         {
5865             type += "SuspendEE ";
5866         }
5867         if (ThreadType & ThreadType_Finalizer)
5868         {
5869             type += "Finalizer ";
5870         }
5871         if (ThreadType & ThreadType_ADUnloadHelper)
5872         {
5873             type += "ADUnloadHelper ";
5874         }
5875         if (ThreadType & ThreadType_ShutdownHelper)
5876         {
5877             type += "ShutdownHelper ";
5878         }
5879         if (ThreadType & ThreadType_Threadpool_IOCompletion)
5880         {
5881             type += "IOCompletion ";
5882         }
5883         if (ThreadType & ThreadType_Threadpool_Worker)
5884         {
5885             type += "ThreadpoolWorker ";
5886         }
5887         if (ThreadType & ThreadType_Wait)
5888         {
5889             type += "Wait ";
5890         }
5891         if (ThreadType & ThreadType_ProfAPI_Attach)
5892         {
5893             type += "ProfilingAPIAttach ";
5894         }
5895         if (ThreadType & ThreadType_ProfAPI_Detach)
5896         {
5897             type += "ProfilingAPIDetach ";
5898         }
5899
5900         table.WriteColumn(2, type);
5901     }
5902
5903     Status = g_ExtSystem->SetCurrentThreadId (ulOriginalThreadID);
5904     if (!SUCCEEDED (Status))
5905     {
5906         ExtOut("Failed to switch to original thread\n");        
5907         return Status;
5908     }    
5909
5910     return Status;
5911 }
5912 #endif //FEATURE_PAL
5913
5914 HRESULT SwitchToExceptionThread()
5915 {
5916     HRESULT Status;
5917     
5918     DacpThreadStoreData ThreadStore;
5919     if ((Status = ThreadStore.Request(g_sos)) != S_OK)
5920     {
5921         Print("Failed to request ThreadStore\n");
5922         return Status;
5923     }
5924
5925     DacpThreadData Thread;
5926     CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
5927     while (CurThread)
5928     {
5929         if (IsInterrupt())
5930             break;
5931
5932         if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
5933         {
5934             PrintLn("Failed to request Thread at ", Pointer(CurThread));
5935             return Status;
5936         }
5937         
5938         TADDR taLTOH;
5939         if (Thread.lastThrownObjectHandle != NULL)
5940         {
5941             if (SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle), &taLTOH, sizeof(taLTOH), NULL))
5942             {
5943                 if (taLTOH != NULL)
5944                 {
5945                     ULONG id;
5946                     if (g_ExtSystem->GetThreadIdBySystemId(Thread.osThreadId, &id) == S_OK)
5947                     {
5948                         if (g_ExtSystem->SetCurrentThreadId(id) == S_OK)
5949                         {
5950                             PrintLn("Found managed exception on thread ", ThreadID(Thread.osThreadId));
5951                             break;
5952                         }
5953                     }
5954                 }
5955             }
5956         }
5957
5958         CurThread = Thread.nextThread;
5959     }
5960
5961     return Status;
5962 }
5963
5964 struct ThreadStateTable
5965 {
5966     unsigned int State;
5967     const char * Name;
5968 };
5969 static const struct ThreadStateTable ThreadStates[] =
5970 {
5971     {0x1, "Thread Abort Requested"},
5972     {0x2, "GC Suspend Pending"},
5973     {0x4, "User Suspend Pending"},
5974     {0x8, "Debug Suspend Pending"},
5975     {0x10, "GC On Transitions"},
5976     {0x20, "Legal to Join"},
5977     {0x40, "Yield Requested"},
5978     {0x80, "Hijacked by the GC"},
5979     {0x100, "Blocking GC for Stack Overflow"},
5980     {0x200, "Background"},
5981     {0x400, "Unstarted"},
5982     {0x800, "Dead"},
5983     {0x1000, "CLR Owns"},
5984     {0x2000, "CoInitialized"},
5985     {0x4000, "In Single Threaded Apartment"},
5986     {0x8000, "In Multi Threaded Apartment"},
5987     {0x10000, "Reported Dead"},
5988     {0x20000, "Fully initialized"},
5989     {0x40000, "Task Reset"},
5990     {0x80000, "Sync Suspended"},
5991     {0x100000, "Debug Will Sync"},
5992     {0x200000, "Stack Crawl Needed"},
5993     {0x400000, "Suspend Unstarted"},
5994     {0x800000, "Aborted"},
5995     {0x1000000, "Thread Pool Worker Thread"},
5996     {0x2000000, "Interruptible"},
5997     {0x4000000, "Interrupted"},
5998     {0x8000000, "Completion Port Thread"},
5999     {0x10000000, "Abort Initiated"},
6000     {0x20000000, "Finalized"},
6001     {0x40000000, "Failed to Start"},
6002     {0x80000000, "Detached"},
6003 };
6004
6005 DECLARE_API(ThreadState)
6006 {
6007     INIT_API_NODAC();
6008
6009     size_t state = GetExpression(args);
6010     int count = 0;
6011
6012     if (state)
6013     {
6014
6015         for (unsigned int i = 0; i < _countof(ThreadStates); ++i)
6016             if (state & ThreadStates[i].State)
6017             {
6018                 ExtOut("    %s\n", ThreadStates[i].Name);
6019                 count++;
6020             }
6021     }
6022     
6023     // If we did not find any thread states, print out a message to let the user
6024     // know that the function is working correctly.
6025     if (count == 0)
6026         ExtOut("    No thread states for '%s'\n", args);
6027
6028     return Status;
6029 }
6030
6031 DECLARE_API(Threads)
6032 {
6033     INIT_API();
6034
6035     BOOL bPrintSpecialThreads = FALSE;
6036     BOOL bPrintLiveThreadsOnly = FALSE;
6037     BOOL bSwitchToManagedExceptionThread = FALSE;
6038     BOOL dml = FALSE;
6039
6040     CMDOption option[] = 
6041     {   // name, vptr, type, hasValue
6042         {"-special", &bPrintSpecialThreads, COBOOL, FALSE},
6043         {"-live", &bPrintLiveThreadsOnly, COBOOL, FALSE},
6044         {"-managedexception", &bSwitchToManagedExceptionThread, COBOOL, FALSE},
6045 #ifndef FEATURE_PAL
6046         {"/d", &dml, COBOOL, FALSE},
6047 #endif
6048     };
6049     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
6050     {
6051         return Status;
6052     }
6053
6054     if (bSwitchToManagedExceptionThread)
6055     {
6056         return SwitchToExceptionThread();
6057     }
6058     
6059     // We need to support minidumps for this command.
6060     BOOL bMiniDump = IsMiniDumpFile();
6061
6062     if (bMiniDump && bPrintSpecialThreads)
6063     {
6064         Print("Special thread information is not available in mini dumps.\n");
6065     }
6066
6067     EnableDMLHolder dmlHolder(dml);
6068
6069     try
6070     {
6071         Status = PrintThreadsFromThreadStore(bMiniDump, bPrintLiveThreadsOnly);
6072         if (!bMiniDump && bPrintSpecialThreads)
6073         {
6074 #ifdef FEATURE_PAL
6075             Print("\n-special not supported.\n");
6076 #else //FEATURE_PAL    
6077             HRESULT Status2 = PrintSpecialThreads(); 
6078             if (!SUCCEEDED(Status2))
6079                 Status = Status2;
6080 #endif //FEATURE_PAL            
6081         }
6082     }
6083     catch (sos::Exception &e)
6084     {
6085         ExtOut("%s\n", e.what());
6086     }
6087     
6088     return Status;
6089 }
6090
6091 #ifndef FEATURE_PAL
6092 /**********************************************************************\
6093 * Routine Description:                                                 *
6094 *                                                                      *
6095 *    This function is called to dump the Watson Buckets.               *
6096 *                                                                      *
6097 \**********************************************************************/
6098 DECLARE_API(WatsonBuckets)
6099 {
6100     INIT_API();
6101
6102     // We don't need to support minidumps for this command.
6103     if (IsMiniDumpFile())
6104     {
6105         ExtOut("Not supported on mini dumps.\n");
6106     }
6107     
6108     // Get the current managed thread.
6109     CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
6110     DacpThreadData Thread;
6111
6112     if ((threadAddr == NULL) || ((Status = Thread.Request(g_sos, threadAddr)) != S_OK))
6113     {
6114         ExtOut("The current thread is unmanaged\n");
6115         return Status;
6116     }
6117     
6118     // Get the definition of GenericModeBlock.
6119 #include <msodw.h>
6120     GenericModeBlock gmb;
6121
6122     if ((Status = g_sos->GetClrWatsonBuckets(threadAddr, &gmb)) != S_OK)
6123     {
6124         ExtOut("Can't get Watson Buckets\n");
6125         return Status;
6126     }
6127
6128     ExtOut("Watson Bucket parameters:\n");
6129     ExtOut("b1: %S\n", gmb.wzP1);
6130     ExtOut("b2: %S\n", gmb.wzP2);
6131     ExtOut("b3: %S\n", gmb.wzP3);
6132     ExtOut("b4: %S\n", gmb.wzP4);
6133     ExtOut("b5: %S\n", gmb.wzP5);
6134     ExtOut("b6: %S\n", gmb.wzP6);
6135     ExtOut("b7: %S\n", gmb.wzP7);
6136     ExtOut("b8: %S\n", gmb.wzP8);
6137     ExtOut("b9: %S\n", gmb.wzP9);
6138         
6139     return Status;
6140 } // WatsonBuckets()
6141 #endif // FEATURE_PAL
6142
6143 struct PendingBreakpoint
6144 {
6145     WCHAR szModuleName[MAX_LONGPATH];
6146     WCHAR szFunctionName[mdNameLen];
6147     WCHAR szFilename[MAX_LONGPATH];
6148     DWORD lineNumber;
6149     TADDR pModule; 
6150     DWORD ilOffset;
6151     mdMethodDef methodToken;
6152     void SetModule(TADDR module)
6153     {
6154         pModule = module;
6155     }
6156
6157     bool ModuleMatches(TADDR compare)
6158     {
6159         return (compare == pModule);
6160     }
6161
6162     PendingBreakpoint *pNext;
6163     PendingBreakpoint() : lineNumber(0), ilOffset(0), methodToken(0), pNext(NULL) 
6164     {
6165         szModuleName[0] = L'\0';
6166         szFunctionName[0] = L'\0';
6167         szFilename[0] = L'\0';
6168     }
6169 };
6170
6171 void IssueDebuggerBPCommand ( CLRDATA_ADDRESS addr )
6172 {
6173     const int MaxBPsCached = 1024;
6174     static CLRDATA_ADDRESS alreadyPlacedBPs[MaxBPsCached];
6175     static int curLimit = 0;
6176
6177     // on ARM the debugger requires breakpoint addresses to be sanitized
6178     if (IsDbgTargetArm())
6179 #ifndef FEATURE_PAL
6180       addr &= ~THUMB_CODE;
6181 #else
6182       addr |= THUMB_CODE; // lldb expects thumb code bit set
6183 #endif      
6184
6185     // if we overflowed our cache consider all new BPs unique...
6186     BOOL bUnique = curLimit >= MaxBPsCached;
6187     if (!bUnique)
6188     {
6189         bUnique = TRUE;
6190         for (int i = 0; i < curLimit; ++i)
6191         {
6192             if (alreadyPlacedBPs[i] == addr)
6193             {
6194                 bUnique = FALSE;
6195                 break;
6196             }
6197         }
6198     }
6199     if (bUnique)
6200     {
6201         char buffer[64]; // sufficient for "bp <pointersize>"
6202         static WCHAR wszNameBuffer[1024]; // should be large enough
6203
6204         // get the MethodDesc name
6205         CLRDATA_ADDRESS pMD;
6206         if (g_sos->GetMethodDescPtrFromIP(addr, &pMD) != S_OK
6207             || g_sos->GetMethodDescName(pMD, 1024, wszNameBuffer, NULL) != S_OK)
6208         {
6209             wcscpy_s(wszNameBuffer, _countof(wszNameBuffer), W("UNKNOWN"));        
6210         }
6211
6212 #ifndef FEATURE_PAL
6213         sprintf_s(buffer, _countof(buffer), "bp %p", (void*) (size_t) addr);
6214 #else
6215         sprintf_s(buffer, _countof(buffer), "breakpoint set --address 0x%p", (void*) (size_t) addr);
6216 #endif
6217         ExtOut("Setting breakpoint: %s [%S]\n", buffer, wszNameBuffer);
6218         g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
6219
6220         if (curLimit < MaxBPsCached)
6221         {
6222             alreadyPlacedBPs[curLimit++] = addr;
6223         }
6224     }
6225 }
6226
6227 class Breakpoints
6228 {
6229     PendingBreakpoint* m_breakpoints;
6230 public:
6231     Breakpoints()
6232     {
6233         m_breakpoints = NULL;
6234     }
6235     ~Breakpoints()
6236     {
6237         PendingBreakpoint *pCur = m_breakpoints;
6238         while(pCur)
6239         {
6240             PendingBreakpoint *pNext = pCur->pNext;
6241             delete pCur;
6242             pCur = pNext;
6243         }
6244         m_breakpoints = NULL;
6245     }
6246
6247     void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod, DWORD ilOffset)
6248     {
6249         if (!IsIn(szModule, szName, mod))
6250         {
6251             PendingBreakpoint *pNew = new PendingBreakpoint();
6252             wcscpy_s(pNew->szModuleName, MAX_LONGPATH, szModule);
6253             wcscpy_s(pNew->szFunctionName, mdNameLen, szName);
6254             pNew->SetModule(mod);
6255             pNew->ilOffset = ilOffset;
6256             pNew->pNext = m_breakpoints;
6257             m_breakpoints = pNew;
6258         }
6259     }
6260
6261     void Add(__in_z LPWSTR szModule, __in_z LPWSTR szName, mdMethodDef methodToken, TADDR mod, DWORD ilOffset)
6262     {
6263         if (!IsIn(methodToken, mod, ilOffset))
6264         {
6265             PendingBreakpoint *pNew = new PendingBreakpoint();
6266             wcscpy_s(pNew->szModuleName, MAX_LONGPATH, szModule);
6267             wcscpy_s(pNew->szFunctionName, mdNameLen, szName);
6268             pNew->methodToken = methodToken;
6269             pNew->SetModule(mod);
6270             pNew->ilOffset = ilOffset;
6271             pNew->pNext = m_breakpoints;
6272             m_breakpoints = pNew;
6273         }
6274     }
6275
6276     void Add(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod)
6277     {
6278         if (!IsIn(szFilename, lineNumber, mod))
6279         {
6280             PendingBreakpoint *pNew = new PendingBreakpoint();
6281             wcscpy_s(pNew->szFilename, MAX_LONGPATH, szFilename);
6282             pNew->lineNumber = lineNumber;
6283             pNew->SetModule(mod);
6284             pNew->pNext = m_breakpoints;
6285             m_breakpoints = pNew;
6286         }
6287     }
6288
6289     void Add(__in_z LPWSTR szFilename, DWORD lineNumber, mdMethodDef methodToken, TADDR mod, DWORD ilOffset)
6290     {
6291         if (!IsIn(methodToken, mod, ilOffset))
6292         {
6293             PendingBreakpoint *pNew = new PendingBreakpoint();
6294             wcscpy_s(pNew->szFilename, MAX_LONGPATH, szFilename);
6295             pNew->lineNumber = lineNumber;
6296             pNew->methodToken = methodToken;
6297             pNew->SetModule(mod);
6298             pNew->ilOffset = ilOffset;
6299             pNew->pNext = m_breakpoints;
6300             m_breakpoints = pNew;
6301         }
6302     }
6303
6304     //returns true if updates are still needed for this module, FALSE if all BPs are now bound
6305     BOOL Update(TADDR mod, BOOL isNewModule)
6306     {
6307         BOOL bNeedUpdates = FALSE;
6308         PendingBreakpoint *pCur = NULL;
6309
6310         if(isNewModule)
6311         {
6312             SymbolReader symbolReader;
6313             SymbolReader* pSymReader = &symbolReader;
6314             if(LoadSymbolsForModule(mod, &symbolReader) != S_OK)
6315                 pSymReader = NULL;
6316
6317             // Get tokens for any modules that match. If there was a change,
6318             // update notifications.                
6319             pCur = m_breakpoints;
6320             while(pCur)
6321             {
6322                 PendingBreakpoint *pNext = pCur->pNext;
6323                 ResolvePendingNonModuleBoundBreakpoint(mod, pCur, pSymReader);
6324                 pCur = pNext;
6325             }
6326         }
6327
6328         pCur = m_breakpoints;
6329         while(pCur)
6330         {
6331             PendingBreakpoint *pNext = pCur->pNext;
6332             if (ResolvePendingBreakpoint(mod, pCur))
6333             {
6334                 bNeedUpdates = TRUE;
6335             }
6336             pCur = pNext;
6337         }
6338         return bNeedUpdates;
6339     }
6340
6341     void RemovePendingForModule(TADDR mod)
6342     {
6343         PendingBreakpoint *pCur = m_breakpoints;
6344         while(pCur)
6345         {
6346             PendingBreakpoint *pNext = pCur->pNext;
6347             if (pCur->ModuleMatches(mod))
6348             {
6349                 // Delete the current node, and keep going
6350                 Delete(pCur);
6351             }
6352
6353             pCur = pNext;
6354         }                
6355     }
6356     
6357     void ListBreakpoints()
6358     {
6359         PendingBreakpoint *pCur = m_breakpoints;
6360         size_t iBreakpointIndex = 1;
6361         ExtOut(SOSPrefix "bpmd pending breakpoint list\n Breakpoint index - Location, ModuleID, Method Token\n");
6362         while(pCur)
6363         {
6364             //windbg likes to format %p as always being 64 bits
6365             ULONG64 modulePtr = (ULONG64)pCur->pModule;
6366
6367             if(pCur->szModuleName[0] != L'\0')
6368                 ExtOut("%d - %ws!%ws+%d, 0x%p, 0x%08x\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset, modulePtr, pCur->methodToken);
6369             else
6370                 ExtOut("%d - %ws:%d, 0x%p, 0x%08x\n",  iBreakpointIndex, pCur->szFilename, pCur->lineNumber, modulePtr, pCur->methodToken);
6371             iBreakpointIndex++;
6372             pCur = pCur->pNext;
6373         }
6374     }
6375
6376 #ifndef FEATURE_PAL
6377     void SaveBreakpoints(FILE* pFile)
6378     {
6379         PendingBreakpoint *pCur = m_breakpoints;
6380         while(pCur)
6381         {
6382             if(pCur->szModuleName[0] != L'\0')
6383                 fprintf_s(pFile, "!bpmd %ws %ws %d\n", pCur->szModuleName, pCur->szFunctionName, pCur->ilOffset);
6384             else
6385                 fprintf_s(pFile, "!bpmd %ws:%d\n",  pCur->szFilename, pCur->lineNumber);
6386             pCur = pCur->pNext;
6387         }
6388     }
6389 #endif
6390
6391     void CleanupNotifications()
6392     {
6393 #ifdef FEATURE_PAL
6394         if (m_breakpoints == NULL)
6395         {
6396             g_ExtServices->ClearExceptionCallback();
6397         }
6398 #endif
6399     }
6400
6401     void ClearBreakpoint(size_t breakPointToClear)
6402     {
6403         PendingBreakpoint *pCur = m_breakpoints;
6404         size_t iBreakpointIndex = 1;
6405         while(pCur)
6406         {
6407             if (breakPointToClear == iBreakpointIndex)
6408             {
6409                 ExtOut("%d - %ws, %ws, %p\n", iBreakpointIndex, pCur->szModuleName, pCur->szFunctionName, pCur->pModule);
6410                 ExtOut("Cleared\n");
6411                 Delete(pCur);
6412                 break;
6413             }
6414             iBreakpointIndex++;
6415             pCur = pCur->pNext;
6416         }
6417
6418         if (pCur == NULL)
6419         {
6420             ExtOut("Invalid pending breakpoint index.\n");
6421         }
6422         CleanupNotifications();
6423     }
6424
6425     void ClearAllBreakpoints()
6426     {
6427         size_t iBreakpointIndex = 1;
6428         for (PendingBreakpoint *pCur = m_breakpoints; pCur != NULL; )
6429         {
6430             PendingBreakpoint* pNext = pCur->pNext;
6431             Delete(pCur);
6432             iBreakpointIndex++;
6433             pCur = pNext;
6434         }
6435         CleanupNotifications();
6436
6437         ExtOut("All pending breakpoints cleared.\n");
6438     }
6439
6440     HRESULT LoadSymbolsForModule(TADDR mod, SymbolReader* pSymbolReader)
6441     {
6442         HRESULT Status = S_OK;
6443         ToRelease<IXCLRDataModule> pModule;
6444         IfFailRet(g_sos->GetModule(mod, &pModule));
6445
6446         ToRelease<IMetaDataImport> pMDImport = NULL;
6447         IfFailRet(pModule->QueryInterface(IID_IMetaDataImport, (LPVOID *) &pMDImport));
6448
6449         IfFailRet(pSymbolReader->LoadSymbols(pMDImport, pModule));
6450
6451         return S_OK;
6452     }
6453
6454     HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pFilename, DWORD lineNumber, TADDR mod, SymbolReader* pSymbolReader)
6455     {
6456         HRESULT Status = S_OK;
6457         if(pSymbolReader == NULL)
6458             return S_FALSE; // no symbols, can't bind here
6459
6460         mdMethodDef methodDef;
6461         ULONG32 ilOffset;
6462         if(FAILED(Status = pSymbolReader->ResolveSequencePoint(pFilename, lineNumber, mod, &methodDef, &ilOffset)))
6463         {
6464             return S_FALSE; // not binding in a module is typical
6465         }
6466
6467         Add(pFilename, lineNumber, methodDef, mod, ilOffset);
6468         return Status;
6469     }
6470
6471     HRESULT ResolvePendingNonModuleBoundBreakpoint(__in_z WCHAR* pModuleName, __in_z WCHAR* pMethodName, TADDR mod, DWORD ilOffset)
6472     {
6473         HRESULT Status = S_OK;
6474         char szName[mdNameLen];
6475         int numModule;
6476         
6477         ToRelease<IXCLRDataModule> module;
6478         IfFailRet(g_sos->GetModule(mod, &module));
6479
6480         WideCharToMultiByte(CP_ACP, 0, pModuleName, (int)(_wcslen(pModuleName) + 1), szName, mdNameLen, NULL, NULL);
6481
6482         ArrayHolder<DWORD_PTR> moduleList = ModuleFromName(szName, &numModule);
6483         if (moduleList == NULL)
6484         {
6485             ExtOut("Failed to request module list.\n");
6486             return E_FAIL;
6487         }
6488
6489         for (int i = 0; i < numModule; i++)
6490         {
6491             // If any one entry in moduleList matches, then the current PendingBreakpoint
6492             // is the right one.
6493             if(moduleList[i] != TO_TADDR(mod))
6494                 continue;
6495
6496             CLRDATA_ENUM h;
6497             if (module->StartEnumMethodDefinitionsByName(pMethodName, 0, &h) == S_OK)
6498             {
6499                 IXCLRDataMethodDefinition *pMeth = NULL;
6500                 while (module->EnumMethodDefinitionByName(&h, &pMeth) == S_OK)
6501                 {
6502                     mdMethodDef methodToken;
6503                     ToRelease<IXCLRDataModule> pUnusedModule;
6504                     IfFailRet(pMeth->GetTokenAndScope(&methodToken, &pUnusedModule));
6505
6506                     Add(pModuleName, pMethodName, methodToken, mod, ilOffset);
6507                     pMeth->Release();
6508                 }
6509                 module->EndEnumMethodDefinitionsByName(h);
6510             }
6511         }
6512         return S_OK;
6513     }
6514
6515     // Return TRUE if there might be more instances that will be JITTED later
6516     static BOOL ResolveMethodInstances(IXCLRDataMethodDefinition *pMeth, DWORD ilOffset)
6517     {
6518         BOOL bFoundCode = FALSE;
6519         BOOL bNeedDefer = FALSE;
6520         CLRDATA_ENUM h1;
6521         
6522         if (pMeth->StartEnumInstances (NULL, &h1) == S_OK)
6523         {
6524             IXCLRDataMethodInstance *inst = NULL;
6525             while (pMeth->EnumInstance (&h1, &inst) == S_OK)
6526             {
6527                 BOOL foundByIlOffset = FALSE;
6528                 ULONG32 rangesNeeded = 0;
6529                 if(inst->GetAddressRangesByILOffset(ilOffset, 0, &rangesNeeded, NULL) == S_OK)
6530                 {
6531                     ArrayHolder<CLRDATA_ADDRESS_RANGE> ranges = new NOTHROW CLRDATA_ADDRESS_RANGE[rangesNeeded];
6532                     if (ranges != NULL)
6533                     {
6534                         if (inst->GetAddressRangesByILOffset(ilOffset, rangesNeeded, NULL, ranges) == S_OK)
6535                         {
6536                             for (DWORD i = 0; i < rangesNeeded; i++)
6537                             {
6538                                 IssueDebuggerBPCommand(ranges[i].startAddress);
6539                                 bFoundCode = TRUE;
6540                                 foundByIlOffset = TRUE;
6541                             }
6542                         }
6543                     }
6544                 }
6545                 
6546                 if (!foundByIlOffset && ilOffset == 0)
6547                 {
6548                     CLRDATA_ADDRESS addr = 0;
6549                     if (inst->GetRepresentativeEntryAddress(&addr) == S_OK)
6550                     {
6551                         IssueDebuggerBPCommand(addr);
6552                         bFoundCode = TRUE;
6553                     }
6554                 }
6555             }
6556             pMeth->EndEnumInstances (h1);
6557         }
6558
6559         // if this is a generic method we need to add a defered bp
6560         BOOL bGeneric = FALSE;
6561         pMeth->HasClassOrMethodInstantiation(&bGeneric);
6562
6563         bNeedDefer = !bFoundCode || bGeneric;
6564         // This is down here because we only need to call SetCodeNofiication once.
6565         if (bNeedDefer)
6566         {
6567             if (pMeth->SetCodeNotification (CLRDATA_METHNOTIFY_GENERATED) != S_OK)
6568             {
6569                 bNeedDefer = FALSE;
6570                 ExtOut("Failed to set code notification\n");
6571             }
6572         }
6573         return bNeedDefer;
6574     }
6575
6576 private:    
6577     BOOL IsIn(__in_z LPWSTR szModule, __in_z LPWSTR szName, TADDR mod)
6578     {
6579         PendingBreakpoint *pCur = m_breakpoints;
6580         while(pCur)
6581         {
6582             if (pCur->ModuleMatches(mod) && 
6583                 _wcsicmp(pCur->szModuleName, szModule) == 0 &&
6584                 _wcscmp(pCur->szFunctionName, szName) == 0)
6585             {
6586                 return TRUE;
6587             }
6588             pCur = pCur->pNext;
6589         }
6590         return FALSE;
6591     }
6592
6593     BOOL IsIn(__in_z LPWSTR szFilename, DWORD lineNumber, TADDR mod)
6594     {
6595         PendingBreakpoint *pCur = m_breakpoints;
6596         while(pCur)
6597         {
6598             if (pCur->ModuleMatches(mod) && 
6599                 _wcsicmp(pCur->szFilename, szFilename) == 0 &&
6600                 pCur->lineNumber == lineNumber)
6601             {
6602                 return TRUE;
6603             }
6604             pCur = pCur->pNext;
6605         }
6606         return FALSE;
6607     }
6608
6609     BOOL IsIn(mdMethodDef token, TADDR mod, DWORD ilOffset)
6610     {
6611         PendingBreakpoint *pCur = m_breakpoints;
6612         while(pCur)
6613         {
6614             if (pCur->ModuleMatches(mod) && 
6615                 pCur->methodToken == token &&
6616                 pCur->ilOffset == ilOffset)
6617             {
6618                 return TRUE;
6619             }
6620             pCur = pCur->pNext;
6621         }
6622         return FALSE;
6623     }
6624
6625     void Delete(PendingBreakpoint *pDelete)
6626     {
6627         PendingBreakpoint *pCur = m_breakpoints;
6628         PendingBreakpoint *pPrev = NULL;
6629         while(pCur)
6630         {
6631             if (pCur == pDelete)
6632             {
6633                 if (pPrev == NULL)
6634                 {
6635                     m_breakpoints = pCur->pNext;
6636                 }
6637                 else
6638                 {
6639                     pPrev->pNext = pCur->pNext;
6640                 }
6641                 delete pCur;
6642                 return;
6643             }
6644             pPrev = pCur;
6645             pCur = pCur->pNext;
6646         }
6647     }
6648
6649
6650
6651     HRESULT ResolvePendingNonModuleBoundBreakpoint(TADDR mod, PendingBreakpoint *pCur, SymbolReader* pSymbolReader)
6652     {
6653         // This function only works with pending breakpoints that are not module bound.
6654         if (pCur->pModule == NULL)
6655         {
6656             if(pCur->szModuleName[0] != L'\0')
6657             {
6658                 return ResolvePendingNonModuleBoundBreakpoint(pCur->szModuleName, pCur->szFunctionName, mod, pCur->ilOffset);
6659             }
6660             else
6661             {
6662                 return ResolvePendingNonModuleBoundBreakpoint(pCur->szFilename, pCur->lineNumber, mod, pSymbolReader);
6663             }
6664         }
6665         else
6666         {
6667             return S_OK;
6668         }
6669     }
6670
6671     // Returns TRUE if further instances may be jitted, FALSE if all instances are now resolved
6672     BOOL ResolvePendingBreakpoint(TADDR addr, PendingBreakpoint *pCur)
6673     {
6674         // Only go forward if the module matches the current PendingBreakpoint
6675         if (!pCur->ModuleMatches(addr))
6676         {
6677             return FALSE;
6678         }
6679
6680         ToRelease<IXCLRDataModule> mod;
6681         if (FAILED(g_sos->GetModule(addr, &mod)))
6682         {
6683             return FALSE;
6684         }
6685
6686         if(pCur->methodToken == 0)
6687         {
6688             return FALSE;
6689         }
6690
6691         ToRelease<IXCLRDataMethodDefinition> pMeth = NULL;
6692         mod->GetMethodDefinitionByToken(pCur->methodToken, &pMeth);
6693
6694         // We may not need the code notification. Maybe it was ngen'd and we
6695         // already have the method?
6696         // We can delete the current entry if ResolveMethodInstances() set all BPs
6697         return ResolveMethodInstances(pMeth, pCur->ilOffset);
6698     }
6699 };
6700
6701 Breakpoints g_bpoints;
6702
6703 // Controls whether optimizations are disabled on module load and whether NGEN can be used
6704 BOOL g_fAllowJitOptimization = TRUE;
6705
6706 // Controls whether a one-shot breakpoint should be inserted the next time
6707 // execution is about to enter a catch clause
6708 BOOL g_stopOnNextCatch = FALSE;
6709
6710 // According to the latest debuggers these callbacks will not get called
6711 // unless the user (or an extension, like SOS :-)) had previously enabled
6712 // clrn with "sxe clrn".
6713 class CNotification : public IXCLRDataExceptionNotification4
6714 {
6715     static int s_condemnedGen;
6716
6717     int m_count;
6718     int m_dbgStatus;
6719 public:
6720     CNotification() 
6721         : m_count(0)
6722         , m_dbgStatus(DEBUG_STATUS_NO_CHANGE)
6723     {}
6724
6725     int GetDebugStatus()
6726     {
6727         return m_dbgStatus;
6728     }
6729
6730     STDMETHODIMP QueryInterface (REFIID iid, void **ppvObject)
6731     {
6732         if (ppvObject == NULL)
6733             return E_INVALIDARG;
6734
6735         if (IsEqualIID(iid, IID_IUnknown)
6736             || IsEqualIID(iid, IID_IXCLRDataExceptionNotification)
6737             || IsEqualIID(iid, IID_IXCLRDataExceptionNotification2)
6738             || IsEqualIID(iid, IID_IXCLRDataExceptionNotification3)
6739             || IsEqualIID(iid, IID_IXCLRDataExceptionNotification4))
6740         {
6741             *ppvObject = static_cast<IXCLRDataExceptionNotification4*>(this);
6742             AddRef();
6743             return S_OK;
6744         }
6745         else
6746             return E_NOINTERFACE;
6747
6748     }
6749
6750     STDMETHODIMP_(ULONG) AddRef(void) { return ++m_count; }
6751     STDMETHODIMP_(ULONG) Release(void)
6752     {
6753         m_count--;
6754         if (m_count < 0)
6755         {
6756             m_count = 0;
6757         }
6758         return m_count;
6759     }
6760
6761             
6762     /*
6763      * New code was generated or discarded for a method.:
6764      */
6765     STDMETHODIMP OnCodeGenerated(IXCLRDataMethodInstance* method)
6766     {
6767         // Some method has been generated, make a breakpoint and remove it.
6768         ULONG32 len = mdNameLen;
6769         LPWSTR szModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
6770         if (method->GetName(0, mdNameLen, &len, g_mdName) == S_OK)
6771         {            
6772             ToRelease<IXCLRDataModule> pMod;
6773             HRESULT hr = method->GetTokenAndScope(NULL, &pMod);
6774             if (SUCCEEDED(hr))
6775             {
6776                 len = mdNameLen;
6777                 if (pMod->GetName(mdNameLen, &len, szModuleName) == S_OK)
6778                 {
6779                     ExtOut("JITTED %S!%S\n", szModuleName, g_mdName);
6780
6781                     // Add breakpoint, perhaps delete pending breakpoint
6782                     DacpGetModuleAddress dgma;
6783                     if (SUCCEEDED(dgma.Request(pMod)))
6784                     {
6785                         g_bpoints.Update(TO_TADDR(dgma.ModulePtr), FALSE);
6786                     }
6787                     else
6788                     {
6789                         ExtOut("Failed to request module address.\n");
6790                     }
6791                 }
6792             }
6793         }
6794
6795         m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6796         return S_OK;
6797     }
6798
6799     STDMETHODIMP OnCodeDiscarded(IXCLRDataMethodInstance* method)
6800     {
6801         return E_NOTIMPL;
6802     }
6803
6804     /*
6805      * The process or task reached the desired execution state.
6806      */
6807     STDMETHODIMP OnProcessExecution(ULONG32 state) { return E_NOTIMPL; }
6808     STDMETHODIMP OnTaskExecution(IXCLRDataTask* task,
6809                             ULONG32 state) { return E_NOTIMPL; }
6810
6811     /*
6812      * The given module was loaded or unloaded.
6813      */
6814     STDMETHODIMP OnModuleLoaded(IXCLRDataModule* mod)
6815     {
6816         DacpGetModuleAddress dgma;
6817         if (SUCCEEDED(dgma.Request(mod)))
6818         {
6819             g_bpoints.Update(TO_TADDR(dgma.ModulePtr), TRUE);
6820         }
6821
6822         if(!g_fAllowJitOptimization)
6823         {
6824             HRESULT hr;
6825             ToRelease<IXCLRDataModule2> mod2;
6826             if(FAILED(mod->QueryInterface(__uuidof(IXCLRDataModule2), (void**) &mod2)))
6827             {
6828                 ExtOut("SOS: warning, optimizations for this module could not be suppressed because this CLR version doesn't support the functionality\n");
6829             }
6830             else if(FAILED(hr = mod2->SetJITCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION)))
6831             {
6832                 if(hr == CORDBG_E_CANT_CHANGE_JIT_SETTING_FOR_ZAP_MODULE)
6833                     ExtOut("SOS: warning, optimizations for this module could not be surpressed because an optimized prejitted image was loaded\n");
6834                 else
6835                     ExtOut("SOS: warning, optimizations for this module could not be surpressed hr=0x%x\n", hr);
6836             }
6837         }
6838         
6839         m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6840         return S_OK;
6841     }
6842
6843     STDMETHODIMP OnModuleUnloaded(IXCLRDataModule* mod)
6844     {
6845         DacpGetModuleAddress dgma;
6846         if (SUCCEEDED(dgma.Request(mod)))
6847         {
6848             g_bpoints.RemovePendingForModule(TO_TADDR(dgma.ModulePtr));
6849         }
6850
6851         m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6852         return S_OK;
6853     }
6854
6855     /*
6856      * The given type was loaded or unloaded.
6857      */
6858     STDMETHODIMP OnTypeLoaded(IXCLRDataTypeInstance* typeInst) 
6859     { return E_NOTIMPL; }
6860     STDMETHODIMP OnTypeUnloaded(IXCLRDataTypeInstance* typeInst) 
6861     { return E_NOTIMPL; }
6862
6863     STDMETHODIMP OnAppDomainLoaded(IXCLRDataAppDomain* domain)
6864     { return E_NOTIMPL; }
6865     STDMETHODIMP OnAppDomainUnloaded(IXCLRDataAppDomain* domain)
6866     { return E_NOTIMPL; }
6867     STDMETHODIMP OnException(IXCLRDataExceptionState* exception)
6868     { return E_NOTIMPL; }
6869
6870     STDMETHODIMP OnGcEvent(GcEvtArgs gcEvtArgs)
6871 {
6872         // by default don't stop on these notifications...
6873         m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6874
6875         IXCLRDataProcess2* idp2 = NULL;
6876         if (SUCCEEDED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2)))
6877         {
6878             if (gcEvtArgs.typ == GC_MARK_END)
6879             {
6880                 // erase notification request
6881                 GcEvtArgs gea = { GC_MARK_END, { 0 } };
6882                 idp2->SetGcNotification(gea);
6883
6884                 s_condemnedGen = bitidx(gcEvtArgs.condemnedGeneration);
6885
6886                 ExtOut("CLR notification: GC - Performing a gen %d collection. Determined surviving objects...\n", s_condemnedGen);
6887
6888                 // GC_MARK_END notification means: give the user a chance to examine the debuggee
6889                 m_dbgStatus = DEBUG_STATUS_BREAK;
6890             }
6891         }
6892
6893         return S_OK;
6894     }
6895
6896      /*
6897      * Catch is about to be entered
6898      */
6899     STDMETHODIMP ExceptionCatcherEnter(IXCLRDataMethodInstance* method, DWORD catcherNativeOffset)
6900     {
6901         if(g_stopOnNextCatch)
6902         {
6903             CLRDATA_ADDRESS startAddr;
6904             if(method->GetRepresentativeEntryAddress(&startAddr) == S_OK)
6905             {
6906                 CHAR buffer[100];
6907 #ifndef FEATURE_PAL
6908                 sprintf_s(buffer, _countof(buffer), "bp /1 %p", (void*) (size_t) (startAddr+catcherNativeOffset));
6909 #else
6910                 sprintf_s(buffer, _countof(buffer), "breakpoint set --one-shot --address 0x%p", (void*) (size_t) (startAddr+catcherNativeOffset));
6911 #endif
6912                 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
6913             }
6914             g_stopOnNextCatch = FALSE;
6915         }
6916
6917         m_dbgStatus = DEBUG_STATUS_GO_HANDLED;
6918         return S_OK;
6919     }
6920
6921     static int GetCondemnedGen()
6922     {
6923         return s_condemnedGen;
6924     }
6925
6926 };
6927
6928 int CNotification::s_condemnedGen = -1;
6929
6930 BOOL CheckCLRNotificationEvent(DEBUG_LAST_EVENT_INFO_EXCEPTION* pdle)
6931 {
6932     ISOSDacInterface4 *psos4 = NULL;
6933     CLRDATA_ADDRESS arguments[3];
6934     HRESULT Status;
6935
6936     if (SUCCEEDED(Status = g_sos->QueryInterface(__uuidof(ISOSDacInterface4), (void**) &psos4)))
6937     {
6938         int count = _countof(arguments);
6939         int countNeeded = 0;
6940
6941         Status = psos4->GetClrNotification(arguments, count, &countNeeded);
6942         psos4->Release();
6943
6944         if (SUCCEEDED(Status))
6945         {
6946             memset(&pdle->ExceptionRecord, 0, sizeof(pdle->ExceptionRecord));
6947             pdle->FirstChance = TRUE;
6948             pdle->ExceptionRecord.ExceptionCode = CLRDATA_NOTIFY_EXCEPTION;
6949
6950             _ASSERTE(count <= EXCEPTION_MAXIMUM_PARAMETERS);
6951             for (int i = 0; i < count; i++)
6952             {
6953                 pdle->ExceptionRecord.ExceptionInformation[i] = arguments[i];
6954             }
6955             // The rest of the ExceptionRecord isn't used by TranslateExceptionRecordToNotification
6956             return TRUE;
6957         }
6958         // No pending exception notification
6959         return FALSE;
6960     }
6961
6962     // The new DAC based interface doesn't exists so ask the debugger for the last exception 
6963     // information. NOTE: this function doesn't work on xplat version when the coreclr symbols
6964     // have been stripped.
6965
6966     ULONG Type, ProcessId, ThreadId;
6967     ULONG ExtraInformationUsed;
6968     Status = g_ExtControl->GetLastEventInformation(
6969         &Type,
6970         &ProcessId,
6971         &ThreadId,
6972         pdle,
6973         sizeof(DEBUG_LAST_EVENT_INFO_EXCEPTION),
6974         &ExtraInformationUsed,
6975         NULL,
6976         0,
6977         NULL);
6978
6979     if (Status != S_OK || Type != DEBUG_EVENT_EXCEPTION)
6980     {
6981         return FALSE;
6982     }
6983
6984     if (!pdle->FirstChance || pdle->ExceptionRecord.ExceptionCode != CLRDATA_NOTIFY_EXCEPTION)
6985     {
6986         return FALSE;
6987     }
6988
6989     return TRUE;
6990 }
6991
6992 HRESULT HandleCLRNotificationEvent()
6993 {
6994     /*
6995      * Did we get module load notification? If so, check if any in our pending list
6996      * need to be registered for jit notification.
6997      *
6998      * Did we get a jit notification? If so, check if any can be removed and
6999      * real breakpoints be set.
7000      */
7001     DEBUG_LAST_EVENT_INFO_EXCEPTION dle;
7002     CNotification Notification;
7003
7004     if (!CheckCLRNotificationEvent(&dle))
7005     {
7006 #ifndef FEATURE_PAL
7007         ExtOut("Expecting first chance CLRN exception\n");
7008         return E_FAIL;
7009 #else
7010         g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "process continue", 0);
7011         return S_OK;
7012 #endif
7013     }
7014
7015     // Notification only needs to live for the lifetime of the call below, so it's a non-static
7016     // local.
7017     HRESULT Status = g_clrData->TranslateExceptionRecordToNotification(&dle.ExceptionRecord, &Notification);
7018     if (Status != S_OK)
7019     {
7020         ExtErr("Error processing exception notification\n");
7021         return Status;
7022     }
7023     else
7024     {
7025         switch (Notification.GetDebugStatus())
7026         {
7027             case DEBUG_STATUS_GO:
7028             case DEBUG_STATUS_GO_HANDLED:
7029             case DEBUG_STATUS_GO_NOT_HANDLED:
7030 #ifndef FEATURE_PAL
7031                 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "g", 0);
7032 #else
7033                 g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "process continue", 0);
7034 #endif
7035                 break;
7036             default:
7037                 break;
7038         }
7039     }
7040
7041     return S_OK;
7042 }
7043
7044 #ifndef FEATURE_PAL
7045
7046 DECLARE_API(HandleCLRN)
7047 {
7048     INIT_API();    
7049     MINIDUMP_NOT_SUPPORTED();    
7050
7051     return HandleCLRNotificationEvent();
7052 }
7053
7054 #else // FEATURE_PAL
7055
7056 HRESULT HandleExceptionNotification(ILLDBServices *client)
7057 {
7058     INIT_API();
7059     return HandleCLRNotificationEvent();
7060 }
7061
7062 #endif // FEATURE_PAL
7063
7064 DECLARE_API(bpmd)
7065 {
7066     INIT_API_NOEE();    
7067     MINIDUMP_NOT_SUPPORTED();    
7068     int i;
7069     char buffer[1024];    
7070     
7071     if (IsDumpFile())
7072     {
7073         ExtOut(SOSPrefix "bpmd is not supported on a dump file.\n");
7074         return Status;
7075     }
7076
7077
7078     // We keep a list of managed breakpoints the user wants to set, and display pending bps
7079     // bpmd. If you call bpmd <module name> <method> we will set or update an existing bp.
7080     // bpmd acts as a feeder of breakpoints to bp when the time is right.
7081     //
7082
7083     StringHolder DllName,TypeName;
7084     int lineNumber = 0;
7085     size_t Offset = 0;
7086
7087     DWORD_PTR pMD = NULL;
7088     BOOL fNoFutureModule = FALSE;
7089     BOOL fList = FALSE;
7090     size_t clearItem = 0; 
7091     BOOL fClearAll = FALSE;
7092     CMDOption option[] = 
7093     {   // name, vptr, type, hasValue
7094         {"-md", &pMD, COHEX, TRUE},
7095         {"-nofuturemodule", &fNoFutureModule, COBOOL, FALSE},
7096         {"-list", &fList, COBOOL, FALSE},
7097         {"-clear", &clearItem, COSIZE_T, TRUE},
7098         {"-clearall", &fClearAll, COBOOL, FALSE},
7099     };
7100     CMDValue arg[] = 
7101     {   // vptr, type
7102         {&DllName.data, COSTRING},
7103         {&TypeName.data, COSTRING},
7104         {&Offset, COSIZE_T},
7105     };
7106     size_t nArg;
7107     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
7108     {
7109         return Status;
7110     }
7111
7112     bool fBadParam = false;
7113     bool fIsFilename = false;
7114     int commandsParsed = 0;
7115
7116     if (pMD != NULL)
7117     {
7118         if (nArg != 0)
7119         {
7120             fBadParam = true;
7121         }
7122         commandsParsed++;
7123     }
7124     if (fList)
7125     {
7126         commandsParsed++;
7127         if (nArg != 0)
7128         {
7129             fBadParam = true;
7130         }
7131     }
7132     if (fClearAll)
7133     {
7134         commandsParsed++;
7135         if (nArg != 0)
7136         {
7137             fBadParam = true;
7138         }
7139     }
7140     if (clearItem != 0)
7141     {
7142         commandsParsed++;
7143         if (nArg != 0)
7144         {
7145             fBadParam = true;
7146         }
7147     }
7148     if (1 <= nArg && nArg <= 3)
7149     {
7150         commandsParsed++;
7151         // did we get dll and type name or file:line#? Search for a colon in the first arg
7152         // to see if it is in fact a file:line#
7153         CHAR* pColon = strchr(DllName.data, ':');
7154 #ifndef FEATURE_PAL 
7155         if (FAILED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_MODULE_NAME_A, 0, NULL, NULL))) {
7156 #else
7157         if (FAILED(g_ExtSymbols->GetModuleByModuleName(MAIN_CLR_DLL_NAME_A, 0, NULL, NULL))) {
7158 #endif
7159            ExtOut("%s not loaded yet\n", MAIN_CLR_DLL_NAME_A);
7160            return Status;
7161         }
7162
7163         if(NULL != pColon)
7164         {
7165             fIsFilename = true;
7166             *pColon = '\0';
7167             pColon++;
7168             if(1 != sscanf_s(pColon, "%d", &lineNumber))
7169             {
7170                 ExtOut("Unable to parse line number\n");
7171                 fBadParam = true;
7172             }
7173             else if(lineNumber < 0)
7174             {
7175                 ExtOut("Line number must be positive\n");
7176                 fBadParam = true;
7177             }
7178             if(nArg != 1) fBadParam = 1;
7179         }
7180     }
7181
7182     if (fBadParam || (commandsParsed != 1))
7183     {
7184         ExtOut("Usage: " SOSPrefix "bpmd -md <MethodDesc pointer>\n");
7185         ExtOut("Usage: " SOSPrefix "bpmd [-nofuturemodule] <module name> <managed function name> [<il offset>]\n");
7186         ExtOut("Usage: " SOSPrefix "bpmd <filename>:<line number>\n");
7187         ExtOut("Usage: " SOSPrefix "bpmd -list\n");
7188         ExtOut("Usage: " SOSPrefix "bpmd -clear <pending breakpoint number>\n");
7189         ExtOut("Usage: " SOSPrefix "bpmd -clearall\n");
7190 #ifdef FEATURE_PAL
7191         ExtOut("See \"soshelp bpmd\" for more details.\n");
7192 #else
7193         ExtOut("See \"!help bpmd\" for more details.\n");
7194 #endif
7195         return Status;
7196     }
7197
7198     if (fList)
7199     {
7200         g_bpoints.ListBreakpoints();
7201         return Status;
7202     }
7203     if (clearItem != 0)
7204     {
7205         g_bpoints.ClearBreakpoint(clearItem);
7206         return Status;
7207     }
7208     if (fClearAll)
7209     {
7210         g_bpoints.ClearAllBreakpoints();
7211         return Status;
7212     }
7213     // Add a breakpoint
7214     // Do we already have this breakpoint?
7215     // Or, before setting it, is the module perhaps already loaded and code
7216     // is available? If so, don't add to our pending list, just go ahead and
7217     // set the real breakpoint.    
7218     
7219     LPWSTR ModuleName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
7220     LPWSTR FunctionName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
7221     LPWSTR Filename = (LPWSTR)alloca(MAX_LONGPATH * sizeof(WCHAR));
7222
7223     BOOL bNeedNotificationExceptions = FALSE;
7224
7225     if (pMD == NULL)
7226     {
7227         int numModule = 0;
7228         int numMethods = 0;
7229
7230         ArrayHolder<DWORD_PTR> moduleList = NULL;
7231
7232         if(!fIsFilename)
7233         {
7234             MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, ModuleName, mdNameLen);
7235             MultiByteToWideChar(CP_ACP, 0, TypeName.data, -1, FunctionName, mdNameLen);
7236         }
7237         else
7238         {
7239             MultiByteToWideChar(CP_ACP, 0, DllName.data, -1, Filename, MAX_LONGPATH);
7240         }
7241
7242         // Get modules that may need a breakpoint bound
7243         if ((Status = CheckEEDll()) == S_OK)
7244         {
7245             if ((Status = LoadClrDebugDll()) != S_OK)
7246             {
7247                 // if the EE is loaded but DAC isn't we should stop.
7248                 DACMessage(Status);
7249                 return Status;
7250             }
7251             g_bDacBroken = FALSE;                                       \
7252
7253             // Get the module list
7254             moduleList = ModuleFromName(fIsFilename ? NULL : DllName.data, &numModule);
7255
7256             // Its OK if moduleList is NULL
7257             // There is a very normal case when checking for modules after clr is loaded
7258             // but before any AppDomains or assemblies are created
7259             // for example:
7260             // >sxe ld:clr
7261             // >g
7262             // ...
7263             // ModLoad: clr.dll
7264             // >!bpmd Foo.dll Foo.Bar
7265         }
7266         // If LoadClrDebugDll() succeeded make sure we release g_clrData
7267         ToRelease<IXCLRDataProcess> spIDP(g_clrData);
7268         ToRelease<ISOSDacInterface> spISD(g_sos);
7269         ResetGlobals();
7270         
7271         // we can get here with EE not loaded => 0 modules
7272         //                      EE is loaded => 0 or more modules
7273         ArrayHolder<DWORD_PTR> pMDs = NULL;
7274         for (int iModule = 0; iModule < numModule; iModule++)
7275         {
7276             ToRelease<IXCLRDataModule> ModDef;
7277             if (g_sos->GetModule(moduleList[iModule], &ModDef) != S_OK)
7278             {
7279                 continue;
7280             }
7281
7282             HRESULT symbolsLoaded = S_FALSE;
7283             if(!fIsFilename)
7284             {
7285                 g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, moduleList[iModule], (DWORD)Offset);
7286             }
7287             else
7288             {
7289                 SymbolReader symbolReader;
7290                 symbolsLoaded = g_bpoints.LoadSymbolsForModule(moduleList[iModule], &symbolReader);
7291                 if(symbolsLoaded == S_OK &&
7292                    g_bpoints.ResolvePendingNonModuleBoundBreakpoint(Filename, lineNumber, moduleList[iModule], &symbolReader) == S_OK)
7293                 {
7294                     // if we have symbols then get the function name so we can lookup the MethodDescs
7295                     mdMethodDef methodDefToken;
7296                     ULONG32 ilOffset;
7297                     if(SUCCEEDED(symbolReader.ResolveSequencePoint(Filename, lineNumber, moduleList[iModule], &methodDefToken, &ilOffset)))
7298                     {
7299                         ToRelease<IXCLRDataMethodDefinition> pMethodDef = NULL;
7300                         if (SUCCEEDED(ModDef->GetMethodDefinitionByToken(methodDefToken, &pMethodDef)))
7301                         {
7302                             ULONG32 nameLen = 0;
7303                             pMethodDef->GetName(0, mdNameLen, &nameLen, FunctionName);
7304                             
7305                             // get the size of the required buffer
7306                             int buffSize = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, 0, NULL, NULL);
7307                             
7308                             TypeName.data = new NOTHROW char[buffSize];
7309                             if (TypeName.data != NULL)
7310                             {
7311                                 int bytesWritten = WideCharToMultiByte(CP_ACP, 0, FunctionName, -1, TypeName.data, buffSize, NULL, NULL);
7312                                 _ASSERTE(bytesWritten == buffSize);
7313                             }
7314                         }
7315                     }
7316                 }
7317             }
7318
7319             HRESULT gotMethodDescs = GetMethodDescsFromName(moduleList[iModule], ModDef, TypeName.data, &pMDs, &numMethods);
7320             if (FAILED(gotMethodDescs) && (!fIsFilename))
7321             {
7322                 // BPs via file name will enumerate through modules so there will be legitimate failures.
7323                 // for module/type name we already found a match so this shouldn't fail (this is the original behavior).
7324                 ExtOut("Error getting MethodDescs for module %p\n", moduleList[iModule]);
7325                 return Status;
7326             }
7327
7328             // for filename+line number only print extra info if symbols for this module are loaded (it can get quite noisy otherwise).
7329             if ((!fIsFilename) || (fIsFilename && symbolsLoaded == S_OK))
7330             {
7331                 for (int i = 0; i < numMethods; i++)
7332                 {
7333                     if (pMDs[i] == MD_NOT_YET_LOADED)
7334                     {
7335                         continue;
7336                     }
7337                     ExtOut("MethodDesc = %p\n", SOS_PTR(pMDs[i]));
7338                 }
7339             }
7340
7341             if (g_bpoints.Update(moduleList[iModule], FALSE))
7342             {
7343                 bNeedNotificationExceptions = TRUE;
7344             }
7345         }
7346
7347         if (!fNoFutureModule)
7348         {
7349             // add a pending breakpoint that will find future loaded modules, and
7350             // wait for the module load notification.
7351             if (!fIsFilename)
7352             {
7353                 g_bpoints.Add(ModuleName, FunctionName, NULL, (DWORD)Offset);
7354             }
7355             else
7356             {
7357                 g_bpoints.Add(Filename, lineNumber, NULL);
7358             }
7359             bNeedNotificationExceptions = TRUE;
7360
7361             ULONG32 flags = 0;
7362             g_clrData->GetOtherNotificationFlags(&flags);
7363             flags |= (CLRDATA_NOTIFY_ON_MODULE_LOAD | CLRDATA_NOTIFY_ON_MODULE_UNLOAD);
7364             g_clrData->SetOtherNotificationFlags(flags);
7365         }
7366     }
7367     else /* We were given a MethodDesc already */
7368     {
7369         // if we've got an explicit MD, then we better have CLR and mscordacwks loaded
7370         INIT_API_EE()
7371         INIT_API_DAC();
7372
7373         DacpMethodDescData MethodDescData;
7374         ExtOut("MethodDesc = %p\n", SOS_PTR(pMD));
7375         if (MethodDescData.Request(g_sos, TO_CDADDR(pMD)) != S_OK)
7376         {
7377             ExtOut("%p is not a valid MethodDesc\n", SOS_PTR(pMD));
7378             return Status;
7379         }
7380         
7381         if (MethodDescData.bHasNativeCode)
7382         {
7383             IssueDebuggerBPCommand((size_t) MethodDescData.NativeCodeAddr);
7384         }
7385         else if (MethodDescData.bIsDynamic)
7386         {
7387 #ifndef FEATURE_PAL
7388             // Dynamic methods don't have JIT notifications. This is something we must
7389             // fix in the next release. Until then, you have a cumbersome user experience.
7390             ExtOut("This DynamicMethodDesc is not yet JITTED. Placing memory breakpoint at %p\n",
7391                 MethodDescData.AddressOfNativeCodeSlot);
7392             
7393             sprintf_s(buffer, _countof(buffer),
7394 #ifdef _TARGET_WIN64_
7395                 "ba w8"
7396 #else
7397                 "ba w4" 
7398 #endif // _TARGET_WIN64_
7399
7400                 " /1 %p \"bp poi(%p); g\"",
7401                 (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot,
7402                 (void*) (size_t) MethodDescData.AddressOfNativeCodeSlot);
7403
7404             Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
7405             if (FAILED(Status))
7406             {
7407                 ExtOut("Unable to set breakpoint with IDebugControl::Execute: %x\n",Status);
7408                 ExtOut("Attempted to run: %s\n", buffer);                
7409             }            
7410 #else
7411             ExtErr("This DynamicMethodDesc is not yet JITTED %p\n", MethodDescData.AddressOfNativeCodeSlot);
7412 #endif // FEATURE_PAL
7413         }
7414         else
7415         {
7416             // Must issue a pending breakpoint.
7417             if (g_sos->GetMethodDescName(pMD, mdNameLen, FunctionName, NULL) != S_OK)
7418             {
7419                 ExtOut("Unable to get method name for MethodDesc %p\n", SOS_PTR(pMD));
7420                 return Status;
7421             }
7422
7423             FileNameForModule ((DWORD_PTR) MethodDescData.ModulePtr, ModuleName);
7424
7425             // We didn't find code, add a breakpoint.
7426             g_bpoints.ResolvePendingNonModuleBoundBreakpoint(ModuleName, FunctionName, TO_TADDR(MethodDescData.ModulePtr), 0);
7427             g_bpoints.Update(TO_TADDR(MethodDescData.ModulePtr), FALSE);
7428             bNeedNotificationExceptions = TRUE;            
7429         }
7430     }
7431
7432     if (bNeedNotificationExceptions)
7433     {
7434         ExtOut("Adding pending breakpoints...\n");
7435 #ifndef FEATURE_PAL
7436         sprintf_s(buffer, _countof(buffer), "sxe -c \"!HandleCLRN\" clrn");
7437         Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);        
7438 #else
7439         Status = g_ExtServices->SetExceptionCallback(HandleExceptionNotification);
7440 #endif // FEATURE_PAL
7441     }
7442
7443     return Status;
7444 }
7445
7446 #ifndef FEATURE_PAL
7447
7448 /**********************************************************************\
7449 * Routine Description:                                                 *
7450 *                                                                      *
7451 *    This function is called to dump the managed threadpool            *
7452 *                                                                      *
7453 \**********************************************************************/
7454 DECLARE_API(ThreadPool)
7455 {
7456     INIT_API();
7457     MINIDUMP_NOT_SUPPORTED();
7458
7459     DacpThreadpoolData threadpool;
7460
7461     if ((Status = threadpool.Request(g_sos)) == S_OK)
7462     {
7463         BOOL doHCDump = FALSE;
7464
7465         CMDOption option[] = 
7466         {   // name, vptr, type, hasValue
7467             {"-ti", &doHCDump, COBOOL, FALSE}
7468         };    
7469
7470         if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
7471         {
7472             return Status;
7473         }
7474
7475         ExtOut ("CPU utilization: %d%%\n", threadpool.cpuUtilization);            
7476         ExtOut ("Worker Thread:");
7477         ExtOut (" Total: %d", threadpool.NumWorkingWorkerThreads + threadpool.NumIdleWorkerThreads + threadpool.NumRetiredWorkerThreads);
7478         ExtOut (" Running: %d", threadpool.NumWorkingWorkerThreads);
7479         ExtOut (" Idle: %d", threadpool.NumIdleWorkerThreads);
7480         ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalWorkerThreads);        
7481         ExtOut (" MinLimit: %d", threadpool.MinLimitTotalWorkerThreads);        
7482         ExtOut ("\n");        
7483
7484         int numWorkRequests = 0;
7485         CLRDATA_ADDRESS workRequestPtr = threadpool.FirstUnmanagedWorkRequest;
7486         DacpWorkRequestData workRequestData;
7487         while (workRequestPtr)
7488         {
7489             if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK)
7490             {
7491                 ExtOut("    Failed to examine a WorkRequest\n");
7492                 return Status;
7493             }
7494             numWorkRequests++;
7495             workRequestPtr = workRequestData.NextWorkRequest;
7496         }
7497
7498         ExtOut ("Work Request in Queue: %d\n", numWorkRequests);    
7499         workRequestPtr = threadpool.FirstUnmanagedWorkRequest;
7500         while (workRequestPtr)
7501         {
7502             if ((Status = workRequestData.Request(g_sos,workRequestPtr))!=S_OK)
7503             {
7504                 ExtOut("    Failed to examine a WorkRequest\n");
7505                 return Status;
7506             }
7507
7508             if (workRequestData.Function == threadpool.AsyncTimerCallbackCompletionFPtr)
7509                 ExtOut ("    AsyncTimerCallbackCompletion TimerInfo@%p\n", SOS_PTR(workRequestData.Context));
7510             else
7511                 ExtOut ("    Unknown Function: %p  Context: %p\n", SOS_PTR(workRequestData.Function),
7512                     SOS_PTR(workRequestData.Context));
7513
7514             workRequestPtr = workRequestData.NextWorkRequest;
7515         }
7516
7517         if (doHCDump)
7518         {
7519             ExtOut ("--------------------------------------\n");
7520             ExtOut ("\nThread Injection History\n");
7521             if (threadpool.HillClimbingLogSize > 0)
7522             {
7523                 static char const * const TransitionNames[] = 
7524                 {
7525                     "Warmup", 
7526                     "Initializing",
7527                     "RandomMove",
7528                     "ClimbingMove",
7529                     "ChangePoint",
7530                     "Stabilizing",
7531                     "Starvation",
7532                     "ThreadTimedOut",
7533                     "Undefined"
7534                 };
7535
7536                 ExtOut("\n    Time Transition     New #Threads      #Samples   Throughput\n");
7537                 DacpHillClimbingLogEntry entry;
7538
7539                 // get the most recent entry first, so we can calculate time offsets
7540                 
7541                 int index = (threadpool.HillClimbingLogFirstIndex + threadpool.HillClimbingLogSize-1) % HillClimbingLogCapacity;
7542                 CLRDATA_ADDRESS entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry));
7543                 if ((Status = entry.Request(g_sos,entryPtr))!=S_OK)
7544                 {
7545                     ExtOut("    Failed to examine a HillClimbing log entry\n");
7546                     return Status;
7547                 }
7548                 DWORD endTime = entry.TickCount;
7549
7550                 for (int i = 0; i < threadpool.HillClimbingLogSize; i++)
7551                 {
7552                     index = (i + threadpool.HillClimbingLogFirstIndex) % HillClimbingLogCapacity;
7553                     entryPtr = threadpool.HillClimbingLog + (index * sizeof(HillClimbingLogEntry));
7554
7555                     if ((Status = entry.Request(g_sos,entryPtr))!=S_OK)
7556                     {
7557                         ExtOut("    Failed to examine a HillClimbing log entry\n");
7558                         return Status;
7559                     }
7560
7561                     ExtOut("%8.2lf %-14s %12d  %12d  %11.2lf\n",
7562                         (double)(int)(entry.TickCount - endTime) / 1000.0,
7563                         TransitionNames[entry.Transition],
7564                         entry.NewControlSetting,
7565                         entry.LastHistoryCount,
7566                         entry.LastHistoryMean);
7567                 }
7568             }
7569         }
7570             
7571         ExtOut ("--------------------------------------\n");
7572         ExtOut ("Number of Timers: %d\n", threadpool.NumTimers);
7573         ExtOut ("--------------------------------------\n");
7574         
7575         ExtOut ("Completion Port Thread:");
7576         ExtOut ("Total: %d", threadpool.NumCPThreads);    
7577         ExtOut (" Free: %d", threadpool.NumFreeCPThreads);    
7578         ExtOut (" MaxFree: %d", threadpool.MaxFreeCPThreads);    
7579         ExtOut (" CurrentLimit: %d", threadpool.CurrentLimitTotalCPThreads);
7580         ExtOut (" MaxLimit: %d", threadpool.MaxLimitTotalCPThreads);
7581         ExtOut (" MinLimit: %d", threadpool.MinLimitTotalCPThreads);        
7582         ExtOut ("\n");
7583     }
7584     else
7585     {
7586         ExtOut("Failed to request ThreadpoolMgr information\n");
7587     }
7588     return Status;
7589 }
7590
7591 #endif // FEATURE_PAL
7592
7593 DECLARE_API(FindAppDomain)
7594 {
7595     INIT_API();
7596     MINIDUMP_NOT_SUPPORTED();    
7597     
7598     DWORD_PTR p_Object = NULL;
7599     BOOL dml = FALSE;
7600
7601     CMDOption option[] = 
7602     {   // name, vptr, type, hasValue
7603 #ifndef FEATURE_PAL
7604         {"/d", &dml, COBOOL, FALSE},
7605 #endif
7606     };
7607     CMDValue arg[] = 
7608     {   // vptr, type
7609         {&p_Object, COHEX},
7610     };
7611     size_t nArg;
7612
7613     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
7614     {
7615         return Status;
7616     }
7617
7618     EnableDMLHolder dmlHolder(dml);
7619     
7620     if ((p_Object == 0) || !sos::IsObject(p_Object))
7621     {
7622         ExtOut("%p is not a valid object\n", SOS_PTR(p_Object));
7623         return Status;
7624     }
7625     
7626     DacpAppDomainStoreData adstore;
7627     if (adstore.Request(g_sos) != S_OK)
7628     {
7629         ExtOut("Error getting AppDomain information\n");
7630         return Status;
7631     }    
7632
7633     CLRDATA_ADDRESS appDomain = GetAppDomain (TO_CDADDR(p_Object));
7634
7635     if (appDomain != NULL)
7636     {
7637         DMLOut("AppDomain: %s\n", DMLDomain(appDomain));
7638         if (appDomain == adstore.sharedDomain)
7639         {
7640             ExtOut("Name:      Shared Domain\n");
7641             ExtOut("ID:        (shared domain)\n");            
7642         }
7643         else if (appDomain == adstore.systemDomain)
7644         {
7645             ExtOut("Name:      System Domain\n");
7646             ExtOut("ID:        (system domain)\n");
7647         }
7648         else
7649         {
7650             DacpAppDomainData domain;
7651             if ((domain.Request(g_sos, appDomain) != S_OK) ||
7652                 (g_sos->GetAppDomainName(appDomain,mdNameLen,g_mdName, NULL)!=S_OK))
7653             {
7654                 ExtOut("Error getting AppDomain %p.\n", SOS_PTR(appDomain));
7655                 return Status;
7656             }
7657
7658             ExtOut("Name:      %S\n", (g_mdName[0]!=L'\0') ? g_mdName : W("None"));
7659             ExtOut("ID:        %d\n", domain.dwId);
7660         }
7661     }
7662     else
7663     {
7664         ExtOut("The type is declared in the shared domain and other\n");
7665         ExtOut("methods of finding the AppDomain failed. Try running\n");
7666         if (IsDMLEnabled())
7667             DMLOut("<exec cmd=\"!gcroot /d %p\">!gcroot %p</exec>, and if you find a root on a\n", p_Object, p_Object);
7668         else
7669             ExtOut("!gcroot %p, and if you find a root on a\n", p_Object);
7670         ExtOut("stack, check the AppDomain of that stack with !threads.\n");
7671         ExtOut("Note that the Thread could have transitioned between\n");
7672         ExtOut("multiple AppDomains.\n");
7673     }
7674     
7675     return Status;
7676 }
7677
7678 #ifndef FEATURE_PAL
7679
7680 /**********************************************************************\
7681 * Routine Description:                                                 *
7682 *                                                                      *
7683 *    This function is called to get the COM state (e.g. APT,contexe    *
7684 *    activity.                                                         *  
7685 *                                                                      *
7686 \**********************************************************************/
7687 #ifdef FEATURE_COMINTEROP
7688 DECLARE_API(COMState)
7689 {
7690     INIT_API();
7691     MINIDUMP_NOT_SUPPORTED();    
7692     
7693
7694     ULONG numThread;
7695     ULONG maxId;
7696     g_ExtSystem->GetTotalNumberThreads(&numThread,&maxId);
7697
7698     ULONG curId;
7699     g_ExtSystem->GetCurrentThreadId(&curId);
7700
7701     SIZE_T AllocSize;
7702     if (!ClrSafeInt<SIZE_T>::multiply(sizeof(ULONG), numThread, AllocSize))
7703     {
7704         ExtOut("  Error!  integer overflow on numThread 0x%08x\n", numThread);
7705         return Status;
7706     }
7707     ULONG *ids = (ULONG*)alloca(AllocSize);
7708     ULONG *sysIds = (ULONG*)alloca(AllocSize);
7709     g_ExtSystem->GetThreadIdsByIndex(0,numThread,ids,sysIds);
7710 #if defined(_TARGET_WIN64_)
7711     ExtOut("      ID             TEB  APT    APTId CallerTID          Context\n");
7712 #else
7713     ExtOut("     ID     TEB   APT    APTId CallerTID Context\n");
7714 #endif
7715     for (ULONG i = 0; i < numThread; i ++) {
7716         g_ExtSystem->SetCurrentThreadId(ids[i]);
7717         CLRDATA_ADDRESS cdaTeb;
7718         g_ExtSystem->GetCurrentThreadTeb(&cdaTeb);
7719         ExtOut("%3d %4x %p", ids[i], sysIds[i], SOS_PTR(CDA_TO_UL64(cdaTeb)));
7720         // Apartment state
7721         TADDR OleTlsDataAddr;
7722         if (SafeReadMemory(TO_TADDR(cdaTeb) + offsetof(TEB,ReservedForOle),
7723                             &OleTlsDataAddr,
7724                             sizeof(OleTlsDataAddr), NULL) && OleTlsDataAddr != 0) {
7725             DWORD AptState;
7726             if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwFlags),
7727                                &AptState,
7728                                sizeof(AptState), NULL)) {
7729                 if (AptState & OLETLS_APARTMENTTHREADED) {
7730                     ExtOut(" STA");
7731                 }
7732                 else if (AptState & OLETLS_MULTITHREADED) {
7733                     ExtOut(" MTA");
7734                 }
7735                 else if (AptState & OLETLS_INNEUTRALAPT) {
7736                     ExtOut(" NTA");
7737                 }
7738                 else {
7739                     ExtOut(" Ukn");
7740                 }
7741
7742                 // Read these fields only if we were able to read anything of the SOleTlsData structure
7743                 DWORD dwApartmentID;
7744                 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwApartmentID),
7745                                    &dwApartmentID,
7746                                    sizeof(dwApartmentID), NULL)) {
7747                     ExtOut(" %8x", dwApartmentID);
7748                 }
7749                 else
7750                     ExtOut(" %8x", 0);
7751                 
7752                 DWORD dwTIDCaller;
7753                 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,dwTIDCaller),
7754                                    &dwTIDCaller,
7755                                    sizeof(dwTIDCaller), NULL)) {
7756                     ExtOut("  %8x", dwTIDCaller);
7757                 }
7758                 else
7759                     ExtOut("  %8x", 0);
7760                 
7761                 size_t Context;
7762                 if (SafeReadMemory(OleTlsDataAddr+offsetof(SOleTlsData,pCurrentCtx),
7763                                    &Context,
7764                                    sizeof(Context), NULL)) {
7765                     ExtOut(" %p", SOS_PTR(Context));
7766                 }
7767                 else
7768                     ExtOut(" %p", SOS_PTR(0));
7769
7770             }
7771             else
7772                 ExtOut(" Ukn");            
7773         }
7774         else
7775             ExtOut(" Ukn");
7776         ExtOut("\n");
7777     }
7778
7779     g_ExtSystem->SetCurrentThreadId(curId);
7780     return Status;
7781 }
7782 #endif // FEATURE_COMINTEROP
7783
7784 #endif // FEATURE_PAL
7785
7786 BOOL traverseEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
7787 {
7788     size_t methodStart = (size_t) token;
7789     
7790     if (IsInterrupt())
7791     {
7792         return FALSE;
7793     }
7794
7795     ExtOut("EHHandler %d: %s ", clauseIndex, EHTypeName(pEHInfo->clauseType));
7796
7797     LPCWSTR typeName = EHTypedClauseTypeName(pEHInfo);
7798     if (typeName != NULL)
7799     {
7800         ExtOut("catch(%S) ", typeName);
7801     }
7802
7803     if (IsClonedFinally(pEHInfo))
7804         ExtOut("(cloned finally)");
7805     else if (pEHInfo->isDuplicateClause)
7806         ExtOut("(duplicate)");
7807
7808     ExtOut("\n");
7809     ExtOut("Clause:  ");
7810
7811     ULONG64 addrStart = pEHInfo->tryStartOffset + methodStart;
7812     ULONG64 addrEnd   = pEHInfo->tryEndOffset   + methodStart;
7813
7814 #ifdef _WIN64
7815     ExtOut("[%08x`%08x, %08x`%08x]",
7816             (ULONG)(addrStart >> 32), (ULONG)addrStart,
7817             (ULONG)(addrEnd   >> 32), (ULONG)addrEnd);
7818 #else
7819     ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd);
7820 #endif
7821
7822     ExtOut(" [%x, %x]\n", 
7823         (UINT32) pEHInfo->tryStartOffset,
7824         (UINT32) pEHInfo->tryEndOffset);
7825
7826     ExtOut("Handler: ");
7827
7828     addrStart = pEHInfo->handlerStartOffset + methodStart;
7829     addrEnd   = pEHInfo->handlerEndOffset   + methodStart;
7830
7831 #ifdef _WIN64
7832     ExtOut("[%08x`%08x, %08x`%08x]",
7833             (ULONG)(addrStart >> 32), (ULONG)addrStart,
7834             (ULONG)(addrEnd   >> 32), (ULONG)addrEnd);
7835 #else
7836     ExtOut("[%08x, %08x]", (ULONG)addrStart, (ULONG)addrEnd);
7837 #endif
7838
7839     ExtOut(" [%x, %x]\n", 
7840         (UINT32) pEHInfo->handlerStartOffset,
7841         (UINT32) pEHInfo->handlerEndOffset);
7842     
7843     if (pEHInfo->clauseType == EHFilter)
7844     {
7845         ExtOut("Filter: ");
7846
7847         addrStart = pEHInfo->filterOffset + methodStart;
7848
7849 #ifdef _WIN64
7850         ExtOut("[%08x`%08x]", (ULONG)(addrStart >> 32), (ULONG)addrStart);
7851 #else
7852         ExtOut("[%08x]", (ULONG)addrStart);
7853 #endif
7854
7855         ExtOut(" [%x]\n",
7856             (UINT32) pEHInfo->filterOffset);
7857     }
7858     
7859     ExtOut("\n");        
7860     return TRUE;
7861 }
7862
7863 DECLARE_API(EHInfo)
7864 {
7865     INIT_API();
7866     MINIDUMP_NOT_SUPPORTED();    
7867     
7868     DWORD_PTR dwStartAddr = NULL;
7869     BOOL dml = FALSE;
7870
7871     CMDOption option[] = 
7872     {   // name, vptr, type, hasValue
7873         {"/d", &dml, COBOOL, FALSE},
7874     };
7875
7876     CMDValue arg[] = 
7877     {   // vptr, type
7878         {&dwStartAddr, COHEX},
7879     };
7880
7881     size_t nArg;
7882     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg)) 
7883     {
7884         return Status;
7885     }
7886
7887     EnableDMLHolder dmlHolder(dml);
7888     DWORD_PTR tmpAddr = dwStartAddr;
7889
7890     if (!IsMethodDesc(dwStartAddr)) 
7891     {
7892         JITTypes jitType;
7893         DWORD_PTR methodDesc;
7894         DWORD_PTR gcinfoAddr;
7895         IP2MethodDesc (dwStartAddr, methodDesc, jitType, gcinfoAddr);
7896         tmpAddr = methodDesc;
7897     }
7898
7899     DacpMethodDescData MD;
7900     if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK))
7901     {
7902         ExtOut("%p is not a MethodDesc\n", SOS_PTR(tmpAddr));
7903         return Status;
7904     }
7905
7906     if (1 == nArg && !MD.bHasNativeCode)
7907     {
7908         ExtOut("No EH info available\n");
7909         return Status;
7910     }
7911
7912     DacpCodeHeaderData codeHeaderData;
7913     if (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK)
7914     {
7915         ExtOut("Unable to get codeHeader information\n");
7916         return Status;
7917     }        
7918     
7919     DMLOut("MethodDesc:   %s\n", DMLMethodDesc(MD.MethodDescPtr));
7920     DumpMDInfo(TO_TADDR(MD.MethodDescPtr));
7921
7922     ExtOut("\n");
7923     Status = g_sos->TraverseEHInfo(TO_CDADDR(MD.NativeCodeAddr), traverseEh, (LPVOID)MD.NativeCodeAddr);
7924
7925     if (Status == E_ABORT)
7926     {
7927         ExtOut("<user aborted>\n");
7928     }
7929     else if (Status != S_OK)
7930     {
7931         ExtOut("Failed to perform EHInfo traverse\n");
7932     }
7933     
7934     return Status;
7935 }
7936
7937 /**********************************************************************\
7938 * Routine Description:                                                 *
7939 *                                                                      *
7940 *    This function is called to dump the GC encoding of a managed      *
7941 *    function.                                                         *  
7942 *                                                                      *
7943 \**********************************************************************/
7944 DECLARE_API(GCInfo)
7945 {
7946     INIT_API();
7947     MINIDUMP_NOT_SUPPORTED();
7948     
7949
7950     TADDR taStartAddr = NULL;
7951     TADDR taGCInfoAddr;
7952     BOOL dml = FALSE;
7953
7954     CMDOption option[] = 
7955     {   // name, vptr, type, hasValue
7956         {"/d", &dml, COBOOL, FALSE},
7957     };
7958     CMDValue arg[] = 
7959     {   // vptr, type
7960         {&taStartAddr, COHEX},
7961     };
7962     size_t nArg;
7963     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (0 == nArg))
7964     {
7965         return Status;
7966     }
7967
7968     EnableDMLHolder dmlHolder(dml);
7969     TADDR tmpAddr = taStartAddr;
7970
7971     if (!IsMethodDesc(taStartAddr)) 
7972     {
7973         JITTypes jitType;
7974         TADDR methodDesc;
7975         TADDR gcinfoAddr;
7976         IP2MethodDesc(taStartAddr, methodDesc, jitType, gcinfoAddr);
7977         tmpAddr = methodDesc;
7978     }
7979
7980     DacpMethodDescData MD;
7981     if ((tmpAddr == 0) || (MD.Request(g_sos, TO_CDADDR(tmpAddr)) != S_OK))
7982     {
7983         ExtOut("%p is not a valid MethodDesc\n", SOS_PTR(taStartAddr));
7984         return Status;
7985     }
7986
7987     if (1 == nArg && !MD.bHasNativeCode)
7988     {
7989         ExtOut("No GC info available\n");
7990         return Status;
7991     }
7992
7993     DacpCodeHeaderData codeHeaderData;
7994
7995     if (
7996         // Try to get code header data from taStartAddr.  This will get the code
7997         // header corresponding to the IP address, even if the function was rejitted
7998         (codeHeaderData.Request(g_sos, TO_CDADDR(taStartAddr)) != S_OK) &&
7999
8000         // If that didn't work, just try to use the code address that the MD
8001         // points to.  If the function was rejitted, this will only give you the
8002         // original JITted code, but that's better than nothing
8003         (codeHeaderData.Request(g_sos, TO_CDADDR(MD.NativeCodeAddr)) != S_OK)
8004         )
8005     {
8006         // We always used to emit this (before rejit support), even if we couldn't get
8007         // the code header, so keep on doing so.
8008         ExtOut("entry point %p\n", SOS_PTR(MD.NativeCodeAddr));
8009
8010         // And now the error....
8011         ExtOut("Unable to get codeHeader information\n");
8012         return Status;
8013     }
8014
8015     // We have the code header, so use it to determine the method start
8016
8017     ExtOut("entry point %p\n", SOS_PTR(codeHeaderData.MethodStart));
8018
8019     if (codeHeaderData.JITType == TYPE_UNKNOWN)
8020     {
8021         ExtOut("unknown Jit\n");
8022         return Status;
8023     }
8024     else if (codeHeaderData.JITType == TYPE_JIT)
8025     {
8026         ExtOut("Normal JIT generated code\n");
8027     }
8028     else if (codeHeaderData.JITType == TYPE_PJIT)
8029     {
8030         ExtOut("preJIT generated code\n");
8031     }
8032
8033     taGCInfoAddr = TO_TADDR(codeHeaderData.GCInfo);
8034
8035     ExtOut("GC info %p\n", SOS_PTR(taGCInfoAddr));
8036
8037     // assume that GC encoding table is never more than
8038     // 40 + methodSize * 2
8039     int tableSize = 0;
8040     if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) ||
8041         !ClrSafeInt<int>::addition(tableSize, 40, tableSize))
8042     {
8043         ExtOut("<integer overflow>\n");
8044         return E_FAIL;
8045     }
8046     ArrayHolder<BYTE> table = new NOTHROW BYTE[tableSize];
8047     if (table == NULL)
8048     {
8049         ExtOut("Could not allocate memory to read the gc info.\n");
8050         return E_OUTOFMEMORY;
8051     }
8052     
8053     memset(table, 0, tableSize);
8054     // We avoid using move here, because we do not want to return
8055     if (!SafeReadMemory(taGCInfoAddr, table, tableSize, NULL))
8056     {
8057         ExtOut("Could not read memory %p\n", SOS_PTR(taGCInfoAddr));
8058         return Status;
8059     }
8060
8061     // Mutable table pointer since we need to pass the appropriate
8062     // offset into the table to DumpGCTable.
8063     GCInfoToken gcInfoToken = { table, GCINFO_VERSION };
8064     unsigned int methodSize = (unsigned int)codeHeaderData.MethodSize;
8065
8066     g_targetMachine->DumpGCInfo(gcInfoToken, methodSize, ExtOut, true /*encBytes*/, true /*bPrintHeader*/);
8067
8068     return Status;
8069 }
8070
8071 #if !defined(FEATURE_PAL)
8072
8073 void DecodeGCTableEntry (const char *fmt, ...)
8074 {
8075     GCEncodingInfo *pInfo = (GCEncodingInfo*)GetFiberData();
8076     va_list va;
8077
8078     //
8079     // Append the new data to the buffer
8080     //
8081
8082     va_start(va, fmt);
8083
8084     int cch = _vsnprintf_s(&pInfo->buf[pInfo->cch], _countof(pInfo->buf) - pInfo->cch, _countof(pInfo->buf) - pInfo->cch - 1, fmt, va);
8085     if (cch >= 0)
8086         pInfo->cch += cch;
8087
8088     va_end(va);
8089
8090     pInfo->buf[pInfo->cch] = '\0';
8091
8092     //
8093     // If there are complete lines in the buffer, decode them.
8094     //
8095
8096     for (;;)
8097     {
8098         char *pNewLine = strchr(pInfo->buf, '\n');
8099
8100         if (!pNewLine)
8101             break;
8102
8103         //
8104         // The line should start with a 16-bit (x86) or 32-bit (non-x86) hex
8105         // offset.  strtoul returns ULONG_MAX or 0 on failure.  0 is a valid
8106         // offset for the first encoding, or while the last offset was 0.
8107         //
8108
8109         if (isxdigit(pInfo->buf[0]))
8110         {
8111             char *pEnd;
8112             ULONG ofs = strtoul(pInfo->buf, &pEnd, 16);
8113
8114             if (   isspace(*pEnd)
8115                 && -1 != ofs 
8116                 && (   -1 == pInfo->ofs
8117                     || 0 == pInfo->ofs
8118                     || ofs > 0))
8119             {
8120                 pInfo->ofs = ofs;
8121                 *pNewLine = '\0';
8122
8123                 SwitchToFiber(pInfo->pvMainFiber);
8124             }
8125         }
8126         else if (0 == strncmp(pInfo->buf, "Untracked:", 10))
8127         {
8128             pInfo->ofs = 0;
8129             *pNewLine = '\0';
8130
8131             SwitchToFiber(pInfo->pvMainFiber);
8132         }
8133
8134         //
8135         // Shift the remaining data to the start of the buffer
8136         //
8137
8138         strcpy_s(pInfo->buf, _countof(pInfo->buf), pNewLine+1);
8139         pInfo->cch = (int)strlen(pInfo->buf);
8140     }
8141 }
8142
8143
8144 VOID CALLBACK DumpGCTableFiberEntry (LPVOID pvGCEncodingInfo)
8145 {
8146     GCEncodingInfo *pInfo = (GCEncodingInfo*)pvGCEncodingInfo;
8147     GCInfoToken gcInfoToken = { pInfo->table, GCINFO_VERSION };
8148     g_targetMachine->DumpGCInfo(gcInfoToken, pInfo->methodSize, DecodeGCTableEntry, false /*encBytes*/, false /*bPrintHeader*/);
8149
8150     pInfo->fDoneDecoding = true;
8151     SwitchToFiber(pInfo->pvMainFiber);
8152 }
8153 #endif // !FEATURE_PAL
8154
8155 BOOL gatherEh(UINT clauseIndex,UINT totalClauses,DACEHInfo *pEHInfo,LPVOID token)
8156 {
8157     SOSEHInfo *pInfo = (SOSEHInfo *) token;
8158
8159     if (pInfo == NULL)
8160     {
8161         return FALSE;
8162     }
8163     
8164     if (pInfo->m_pInfos == NULL)
8165     {
8166         // First time, initialize structure
8167         pInfo->EHCount = totalClauses;
8168         pInfo->m_pInfos = new NOTHROW DACEHInfo[totalClauses];
8169         if (pInfo->m_pInfos == NULL)
8170         {
8171             ReportOOM();            
8172             return FALSE;
8173         }
8174     }
8175
8176     pInfo->m_pInfos[clauseIndex] = *((DACEHInfo*)pEHInfo);
8177     return TRUE;
8178 }
8179
8180
8181 /**********************************************************************\
8182 * Routine Description:                                                 *
8183 *                                                                      *
8184 *    This function is called to unassembly a managed function.         *
8185 *    It tries to print symbolic info for function call, contants...    *  
8186 *                                                                      *
8187 \**********************************************************************/
8188 DECLARE_API(u)
8189 {
8190     INIT_API();
8191     MINIDUMP_NOT_SUPPORTED();    
8192     
8193     
8194     DWORD_PTR dwStartAddr = NULL;
8195     BOOL fWithGCInfo = FALSE;
8196     BOOL fWithEHInfo = FALSE;
8197     BOOL bSuppressLines = FALSE;
8198     BOOL bDisplayOffsets = FALSE;
8199     BOOL dml = FALSE;
8200     size_t nArg;
8201
8202     CMDOption option[] = 
8203     {   // name, vptr, type, hasValue
8204 #ifndef FEATURE_PAL
8205         {"-gcinfo", &fWithGCInfo, COBOOL, FALSE},
8206 #endif
8207         {"-ehinfo", &fWithEHInfo, COBOOL, FALSE},
8208         {"-n", &bSuppressLines, COBOOL, FALSE},
8209         {"-o", &bDisplayOffsets, COBOOL, FALSE},
8210 #ifndef FEATURE_PAL
8211         {"/d", &dml, COBOOL, FALSE},
8212 #endif
8213     };
8214     CMDValue arg[] = 
8215     {   // vptr, type
8216         {&dwStartAddr, COHEX},
8217     };
8218     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg) || (nArg < 1))
8219     {
8220         return Status;
8221     }
8222     // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
8223     ULONG symlines = 0;
8224     if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
8225     {
8226         symlines &= SYMOPT_LOAD_LINES;
8227     }
8228     bSuppressLines = bSuppressLines || (symlines == 0);
8229
8230     EnableDMLHolder dmlHolder(dml);
8231     // dwStartAddr is either some IP address or a MethodDesc.  Start off assuming it's a
8232     // MethodDesc.
8233     DWORD_PTR methodDesc = dwStartAddr;
8234     if (!IsMethodDesc(methodDesc))
8235     {
8236         // Not a methodDesc, so gotta find it ourselves
8237         DWORD_PTR tmpAddr = dwStartAddr;
8238         JITTypes jt;
8239         DWORD_PTR gcinfoAddr;
8240         IP2MethodDesc (tmpAddr, methodDesc, jt,
8241                        gcinfoAddr);
8242         if (!methodDesc || jt == TYPE_UNKNOWN)
8243         {
8244             // It is not managed code.
8245             ExtOut("Unmanaged code\n");
8246             UnassemblyUnmanaged(dwStartAddr, bSuppressLines);
8247             return Status;
8248         }
8249     }
8250
8251     DacpMethodDescData MethodDescData;
8252     if ((Status=MethodDescData.Request(g_sos, TO_CDADDR(methodDesc))) != S_OK)
8253     {
8254         ExtOut("Failed to get method desc for %p.\n", SOS_PTR(dwStartAddr));
8255         return Status;
8256     }    
8257
8258     if (!MethodDescData.bHasNativeCode)
8259     {
8260         ExtOut("Not jitted yet\n");
8261         return Status;
8262     }
8263
8264     // Get the appropriate code header. If we were passed an MD, then use
8265     // MethodDescData.NativeCodeAddr to find the code header; if we were passed an IP, use
8266     // that IP to find the code header. This ensures that, for rejitted functions, we
8267     // disassemble the rejit version that the user explicitly specified with their IP.
8268     DacpCodeHeaderData codeHeaderData;
8269     if (codeHeaderData.Request(
8270         g_sos, 
8271         TO_CDADDR(
8272             (dwStartAddr == methodDesc) ? MethodDescData.NativeCodeAddr : dwStartAddr)
8273             ) != S_OK)
8274
8275     {
8276         ExtOut("Unable to get codeHeader information\n");
8277         return Status;
8278     }                
8279     
8280     if (codeHeaderData.MethodStart == 0)
8281     {
8282         ExtOut("not a valid MethodDesc\n");
8283         return Status;
8284     }
8285     
8286     if (codeHeaderData.JITType == TYPE_UNKNOWN)
8287     {
8288         ExtOut("unknown Jit\n");
8289         return Status;
8290     }
8291     else if (codeHeaderData.JITType == TYPE_JIT)
8292     {
8293         ExtOut("Normal JIT generated code\n");
8294     }
8295     else if (codeHeaderData.JITType == TYPE_PJIT)
8296     {
8297         ExtOut("preJIT generated code\n");
8298     }
8299
8300     NameForMD_s(methodDesc, g_mdName, mdNameLen);
8301     ExtOut("%S\n", g_mdName);   
8302     if (codeHeaderData.ColdRegionStart != NULL)
8303     {
8304         ExtOut("Begin %p, size %x. Cold region begin %p, size %x\n",
8305             SOS_PTR(codeHeaderData.MethodStart), codeHeaderData.HotRegionSize,
8306             SOS_PTR(codeHeaderData.ColdRegionStart), codeHeaderData.ColdRegionSize);
8307     }
8308     else
8309     {
8310         ExtOut("Begin %p, size %x\n", SOS_PTR(codeHeaderData.MethodStart), codeHeaderData.MethodSize);
8311     }
8312
8313 #if !defined(FEATURE_PAL)
8314     //
8315     // Set up to mix gc info with the code if requested
8316     //
8317
8318     GCEncodingInfo gcEncodingInfo = {0};
8319
8320     // The actual GC Encoding Table, this is updated during the course of the function.
8321     gcEncodingInfo.table = NULL;
8322
8323     // The holder to make sure we clean up the memory for the table
8324     ArrayHolder<BYTE> table = NULL;
8325
8326     if (fWithGCInfo)
8327     {
8328         // assume that GC encoding table is never more than 40 + methodSize * 2
8329         int tableSize = 0;
8330         if (!ClrSafeInt<int>::multiply(codeHeaderData.MethodSize, 2, tableSize) ||
8331             !ClrSafeInt<int>::addition(tableSize, 40, tableSize))
8332         {
8333             ExtOut("<integer overflow>\n");
8334             return E_FAIL;
8335         }
8336
8337
8338         // Assign the new array to the mutable gcEncodingInfo table and to the
8339         // table ArrayHolder to clean this up when the function exits.
8340         table = gcEncodingInfo.table = new NOTHROW BYTE[tableSize];
8341         
8342         if (gcEncodingInfo.table == NULL)
8343         {
8344             ExtOut("Could not allocate memory to read the gc info.\n");
8345             return E_OUTOFMEMORY;
8346         }
8347         
8348         memset (gcEncodingInfo.table, 0, tableSize);
8349         // We avoid using move here, because we do not want to return
8350         if (!SafeReadMemory(TO_TADDR(codeHeaderData.GCInfo), gcEncodingInfo.table, tableSize, NULL))
8351         {
8352             ExtOut("Could not read memory %p\n", SOS_PTR(codeHeaderData.GCInfo));
8353             return Status;
8354         }
8355
8356         //
8357         // Skip the info header
8358         //
8359         gcEncodingInfo.methodSize = (unsigned int)codeHeaderData.MethodSize;
8360
8361         //
8362         // DumpGCTable will call gcPrintf for each encoding.  We'd like a "give
8363         // me the next encoding" interface, but we're stuck with the callback.
8364         // To reconcile this without messing up too much code, we'll create a
8365         // fiber to dump the gc table.  When we need the next gc encoding,
8366         // we'll switch to this fiber.  The callback will note the next offset,
8367         // and switch back to the main fiber.
8368         //
8369
8370         gcEncodingInfo.ofs = -1;
8371         gcEncodingInfo.hotSizeToAdd = 0;
8372         
8373         gcEncodingInfo.pvMainFiber = ConvertThreadToFiber(NULL);
8374         if (!gcEncodingInfo.pvMainFiber && ERROR_ALREADY_FIBER == GetLastError())
8375             gcEncodingInfo.pvMainFiber = GetCurrentFiber();
8376         
8377         if (!gcEncodingInfo.pvMainFiber)
8378             return Status;
8379
8380         gcEncodingInfo.pvGCTableFiber = CreateFiber(0, DumpGCTableFiberEntry, &gcEncodingInfo);
8381         if (!gcEncodingInfo.pvGCTableFiber)
8382             return Status;
8383
8384         SwitchToFiber(gcEncodingInfo.pvGCTableFiber);
8385     }    
8386 #endif
8387
8388     SOSEHInfo *pInfo = NULL;
8389     if (fWithEHInfo)
8390     {
8391         pInfo = new NOTHROW SOSEHInfo;
8392         if (pInfo == NULL)
8393         {
8394             ReportOOM();                
8395         }
8396         else if (g_sos->TraverseEHInfo(MethodDescData.NativeCodeAddr, gatherEh, (LPVOID)pInfo) != S_OK)
8397         {
8398             ExtOut("Failed to gather EHInfo data\n");
8399             delete pInfo;
8400             pInfo = NULL;            
8401         }
8402     }
8403     
8404     if (codeHeaderData.ColdRegionStart == NULL)
8405     {
8406         g_targetMachine->Unassembly (
8407                 (DWORD_PTR) codeHeaderData.MethodStart,
8408                 ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.MethodSize,
8409                 dwStartAddr,
8410                 (DWORD_PTR) MethodDescData.GCStressCodeCopy,
8411 #if !defined(FEATURE_PAL)
8412                 fWithGCInfo ? &gcEncodingInfo : 
8413 #endif            
8414                 NULL,
8415                 pInfo,
8416                 bSuppressLines,
8417                 bDisplayOffsets
8418                 );
8419     }
8420     else
8421     {
8422         ExtOut("Hot region:\n");
8423         g_targetMachine->Unassembly (
8424                 (DWORD_PTR) codeHeaderData.MethodStart,
8425                 ((DWORD_PTR)codeHeaderData.MethodStart) + codeHeaderData.HotRegionSize,
8426                 dwStartAddr,
8427                 (DWORD_PTR) MethodDescData.GCStressCodeCopy,
8428 #if !defined(FEATURE_PAL)
8429                 fWithGCInfo ? &gcEncodingInfo : 
8430 #endif            
8431                 NULL,
8432                 pInfo,
8433                 bSuppressLines,
8434                 bDisplayOffsets
8435                 );
8436
8437         ExtOut("Cold region:\n");
8438         
8439 #if !defined(FEATURE_PAL)
8440         // Displaying gcinfo for a cold region requires knowing the size of
8441         // the hot region preceeding.
8442         gcEncodingInfo.hotSizeToAdd = codeHeaderData.HotRegionSize;
8443 #endif            
8444         g_targetMachine->Unassembly (
8445                 (DWORD_PTR) codeHeaderData.ColdRegionStart,
8446                 ((DWORD_PTR)codeHeaderData.ColdRegionStart) + codeHeaderData.ColdRegionSize,
8447                 dwStartAddr,
8448                 ((DWORD_PTR) MethodDescData.GCStressCodeCopy) + codeHeaderData.HotRegionSize,                
8449 #if !defined(FEATURE_PAL)
8450                 fWithGCInfo ? &gcEncodingInfo : 
8451 #endif            
8452                 NULL,
8453                 pInfo,
8454                 bSuppressLines,
8455                 bDisplayOffsets
8456                 );
8457
8458     }
8459
8460     if (pInfo)
8461     {
8462         delete pInfo;
8463         pInfo = NULL;
8464     }
8465     
8466 #if !defined(FEATURE_PAL)
8467     if (fWithGCInfo)
8468         DeleteFiber(gcEncodingInfo.pvGCTableFiber);
8469 #endif
8470
8471     return Status;
8472 }
8473
8474 /**********************************************************************\
8475 * Routine Description:                                                 *
8476 *                                                                      *
8477 *    This function is called to dump the in-memory stress log          *
8478 *    !DumpLog [filename]                                               *
8479 *             will dump the stress log corresponding to the clr.dll    *
8480 *             loaded in the debuggee's VAS                             *
8481 *    !DumpLog -addr <addr_of_StressLog::theLog> [filename]             *
8482 *             will dump the stress log associated with any DLL linked  *
8483 *             against utilcode.lib, most commonly mscordbi.dll         *
8484 *             (e.g. !DumpLog -addr mscordbi!StressLog::theLog)         *
8485 *                                                                      *
8486 \**********************************************************************/
8487 DECLARE_API(DumpLog)
8488 {        
8489     INIT_API_NO_RET_ON_FAILURE();
8490
8491     MINIDUMP_NOT_SUPPORTED();    
8492
8493     const char* fileName = "StressLog.txt";
8494
8495     CLRDATA_ADDRESS StressLogAddress = NULL;
8496     
8497     StringHolder sFileName, sLogAddr;
8498     CMDOption option[] = 
8499     {   // name, vptr, type, hasValue
8500         {"-addr", &sLogAddr.data, COSTRING, TRUE}
8501     };
8502     CMDValue arg[] = 
8503     {   // vptr, type
8504         {&sFileName.data, COSTRING}
8505     };
8506     size_t nArg;
8507     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
8508     {
8509         return Status;
8510     }
8511     if (nArg > 0 && sFileName.data != NULL)
8512     {
8513         fileName = sFileName.data;
8514     }
8515
8516     // allow users to specify -addr mscordbdi!StressLog::theLog, for example.
8517     if (sLogAddr.data != NULL)
8518     {
8519         StressLogAddress = GetExpression(sLogAddr.data);
8520     }
8521
8522     if (StressLogAddress == NULL)
8523     {
8524         if (g_bDacBroken)
8525         {
8526 #ifdef FEATURE_PAL
8527             ExtOut("No stress log address. DAC is broken; can't get it\n");
8528             return E_FAIL;
8529 #else
8530             // Try to find stress log symbols
8531             DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog");
8532             StressLogAddress = dwAddr;        
8533 #endif
8534         }
8535         else if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK)
8536         {
8537             ExtOut("Unable to find stress log via DAC\n");
8538             return E_FAIL;
8539         }
8540     }
8541
8542     if (StressLogAddress == NULL)
8543     {
8544         ExtOut("Please provide the -addr argument for the address of the stress log, since no recognized runtime is loaded.\n");
8545         return E_FAIL;
8546     }
8547
8548     ExtOut("Attempting to dump Stress log to file '%s'\n", fileName);
8549
8550     
8551     
8552     Status = StressLog::Dump(StressLogAddress, fileName, g_ExtData);
8553
8554     if (Status == S_OK)
8555         ExtOut("SUCCESS: Stress log dumped\n");
8556     else if (Status == S_FALSE)
8557         ExtOut("No Stress log in the image, no file written\n");
8558     else
8559         ExtOut("FAILURE: Stress log not dumped\n");
8560
8561     return Status;
8562 }
8563
8564 #ifdef TRACE_GC
8565
8566 DECLARE_API (DumpGCLog)
8567 {
8568     INIT_API_NODAC();
8569     MINIDUMP_NOT_SUPPORTED();    
8570     
8571     if (GetEEFlavor() == UNKNOWNEE) 
8572     {
8573         ExtOut("CLR not loaded\n");
8574         return Status;
8575     }
8576
8577     const char* fileName = "GCLog.txt";
8578
8579     while (isspace (*args))
8580         args ++;
8581
8582     if (*args != 0)
8583         fileName = args;
8584     
8585     DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_log_buffer");
8586     moveN (dwAddr, dwAddr);
8587
8588     if (dwAddr == 0)
8589     {
8590         dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_log_buffer");
8591         moveN (dwAddr, dwAddr);
8592         if (dwAddr == 0)
8593         {
8594             ExtOut("Can't get either WKS or SVR GC's log file");
8595             return E_FAIL;
8596         }
8597     }
8598     
8599     ExtOut("Dumping GC log at %08x\n", dwAddr);
8600
8601     g_bDacBroken = FALSE;
8602     
8603     ExtOut("Attempting to dump GC log to file '%s'\n", fileName);
8604     
8605     Status = E_FAIL;
8606
8607     HANDLE hGCLog = CreateFileA(
8608         fileName,
8609         GENERIC_WRITE,
8610         FILE_SHARE_READ,
8611         NULL,
8612         CREATE_ALWAYS,
8613         FILE_ATTRIBUTE_NORMAL,
8614         NULL);
8615
8616     if (hGCLog == INVALID_HANDLE_VALUE)
8617     {
8618         ExtOut("failed to create file: %d\n", GetLastError());
8619         goto exit;
8620     }
8621
8622     int iLogSize = 1024*1024;
8623     BYTE* bGCLog = new NOTHROW BYTE[iLogSize];
8624     if (bGCLog == NULL)
8625     {
8626         ReportOOM();
8627         goto exit;
8628     }
8629
8630     memset (bGCLog, 0, iLogSize);
8631     if (!SafeReadMemory(dwAddr, bGCLog, iLogSize, NULL))
8632     {
8633         ExtOut("failed to read memory from %08x\n", dwAddr);
8634     }
8635
8636     int iRealLogSize = iLogSize - 1;
8637     while (iRealLogSize >= 0)
8638     {
8639         if (bGCLog[iRealLogSize] != '*')
8640         {
8641             break;
8642         }
8643
8644         iRealLogSize--;
8645     }
8646
8647     DWORD dwWritten = 0;
8648     WriteFile (hGCLog, bGCLog, iRealLogSize + 1, &dwWritten, NULL);
8649
8650     Status = S_OK;
8651
8652 exit:
8653
8654     if (hGCLog != INVALID_HANDLE_VALUE)
8655     {
8656         CloseHandle (hGCLog);
8657     }
8658
8659     if (Status == S_OK)
8660         ExtOut("SUCCESS: Stress log dumped\n");
8661     else if (Status == S_FALSE)
8662         ExtOut("No Stress log in the image, no file written\n");
8663     else
8664         ExtOut("FAILURE: Stress log not dumped\n");
8665
8666     return Status;
8667 }
8668 #endif //TRACE_GC
8669
8670 #ifndef FEATURE_PAL
8671 DECLARE_API (DumpGCConfigLog)
8672 {
8673     INIT_API();
8674 #ifdef GC_CONFIG_DRIVEN    
8675     MINIDUMP_NOT_SUPPORTED();    
8676
8677     if (GetEEFlavor() == UNKNOWNEE) 
8678     {
8679         ExtOut("CLR not loaded\n");
8680         return Status;
8681     }
8682
8683     const char* fileName = "GCConfigLog.txt";
8684
8685     while (isspace (*args))
8686         args ++;
8687
8688     if (*args != 0)
8689         fileName = args;
8690     
8691     if (!InitializeHeapData ())
8692     {
8693         ExtOut("GC Heap not initialized yet.\n");
8694         return S_OK;
8695     }
8696
8697     BOOL fIsServerGC = IsServerBuild();
8698
8699     DWORD_PTR dwAddr = 0; 
8700     DWORD_PTR dwAddrOffset = 0;
8701     
8702     if (fIsServerGC) 
8703     {
8704         dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_config_log_buffer");
8705         dwAddrOffset = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!SVR::gc_config_log_buffer_offset");
8706     }
8707     else
8708     {
8709         dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_config_log_buffer");
8710         dwAddrOffset = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!WKS::gc_config_log_buffer_offset");
8711     }
8712
8713     moveN (dwAddr, dwAddr);
8714     moveN (dwAddrOffset, dwAddrOffset);
8715
8716     if (dwAddr == 0)
8717     {
8718         ExtOut("Can't get either WKS or SVR GC's config log buffer");
8719         return E_FAIL;
8720     }
8721     
8722     ExtOut("Dumping GC log at %08x\n", dwAddr);
8723
8724     g_bDacBroken = FALSE;
8725     
8726     ExtOut("Attempting to dump GC log to file '%s'\n", fileName);
8727     
8728     Status = E_FAIL;
8729     
8730     HANDLE hGCLog = CreateFileA(
8731         fileName,
8732         GENERIC_WRITE,
8733         FILE_SHARE_READ,
8734         NULL,
8735         OPEN_ALWAYS,
8736         FILE_ATTRIBUTE_NORMAL,
8737         NULL);
8738
8739     if (hGCLog == INVALID_HANDLE_VALUE)
8740     {
8741         ExtOut("failed to create file: %d\n", GetLastError());
8742         goto exit;
8743     }
8744
8745     {
8746         int iLogSize = (int)dwAddrOffset;
8747
8748         ArrayHolder<BYTE> bGCLog = new NOTHROW BYTE[iLogSize];
8749         if (bGCLog == NULL)
8750         {
8751             ReportOOM();
8752             goto exit;
8753         }
8754
8755         memset (bGCLog, 0, iLogSize);
8756         if (!SafeReadMemory(dwAddr, bGCLog, iLogSize, NULL))
8757         {
8758             ExtOut("failed to read memory from %08x\n", dwAddr);
8759         }
8760
8761         SetFilePointer (hGCLog, 0, 0, FILE_END);
8762         DWORD dwWritten;
8763         WriteFile (hGCLog, bGCLog, iLogSize, &dwWritten, NULL);
8764     }
8765
8766     Status = S_OK;
8767
8768 exit:
8769
8770     if (hGCLog != INVALID_HANDLE_VALUE)
8771     {
8772         CloseHandle (hGCLog);
8773     }
8774
8775     if (Status == S_OK)
8776         ExtOut("SUCCESS: Stress log dumped\n");
8777     else if (Status == S_FALSE)
8778         ExtOut("No Stress log in the image, no file written\n");
8779     else
8780         ExtOut("FAILURE: Stress log not dumped\n");
8781
8782     return Status;
8783 #else
8784     ExtOut("Not implemented\n");
8785     return S_OK;
8786 #endif //GC_CONFIG_DRIVEN
8787 }
8788 #endif // FEATURE_PAL
8789
8790 #ifdef GC_CONFIG_DRIVEN
8791 static const char * const str_interesting_data_points[] =
8792 {
8793     "pre short", // 0
8794     "post short", // 1
8795     "merged pins", // 2
8796     "converted pins", // 3
8797     "pre pin", // 4
8798     "post pin", // 5
8799     "pre and post pin", // 6
8800     "pre short padded", // 7
8801     "post short padded", // 7
8802 };
8803
8804 static const char * const str_heap_compact_reasons[] = 
8805 {
8806     "low on ephemeral space",
8807     "high fragmetation",
8808     "couldn't allocate gaps",
8809     "user specfied compact LOH",
8810     "last GC before OOM",
8811     "induced compacting GC",
8812     "fragmented gen0 (ephemeral GC)", 
8813     "high memory load (ephemeral GC)",
8814     "high memory load and frag",
8815     "very high memory load and frag",
8816     "no gc mode"
8817 };
8818
8819 static BOOL gc_heap_compact_reason_mandatory_p[] =
8820 {
8821     TRUE, //compact_low_ephemeral = 0,
8822     FALSE, //compact_high_frag = 1,
8823     TRUE, //compact_no_gaps = 2,
8824     TRUE, //compact_loh_forced = 3,
8825     TRUE, //compact_last_gc = 4
8826     TRUE, //compact_induced_compacting = 5,
8827     FALSE, //compact_fragmented_gen0 = 6, 
8828     FALSE, //compact_high_mem_load = 7, 
8829     TRUE, //compact_high_mem_frag = 8, 
8830     TRUE, //compact_vhigh_mem_frag = 9,
8831     TRUE //compact_no_gc_mode = 10
8832 };
8833
8834 static const char * const str_heap_expand_mechanisms[] = 
8835 {
8836     "reused seg with normal fit",
8837     "reused seg with best fit",
8838     "expand promoting eph",
8839     "expand with a new seg",
8840     "no memory for a new seg",
8841     "expand in next full GC"
8842 };
8843
8844 static const char * const str_bit_mechanisms[] = 
8845 {
8846     "using mark list",
8847     "demotion"
8848 };
8849
8850 static const char * const str_gc_global_mechanisms[] =
8851 {
8852     "concurrent GCs", 
8853     "compacting GCs",
8854     "promoting GCs",
8855     "GCs that did demotion",
8856     "card bundles",
8857     "elevation logic"
8858 };
8859
8860 void PrintInterestingGCInfo(DacpGCInterestingInfoData* dataPerHeap)
8861 {
8862     ExtOut("Interesting data points\n");
8863     size_t* data = dataPerHeap->interestingDataPoints;
8864     for (int i = 0; i < DAC_NUM_GC_DATA_POINTS; i++)
8865     {
8866         ExtOut("%20s: %d\n", str_interesting_data_points[i], data[i]);
8867     }
8868
8869     ExtOut("\nCompacting reasons\n");
8870     data = dataPerHeap->compactReasons;
8871     for (int i = 0; i < DAC_MAX_COMPACT_REASONS_COUNT; i++)
8872     {
8873         ExtOut("[%s]%35s: %d\n", (gc_heap_compact_reason_mandatory_p[i] ? "M" : "W"), str_heap_compact_reasons[i], data[i]);
8874     }
8875
8876     ExtOut("\nExpansion mechanisms\n");
8877     data = dataPerHeap->expandMechanisms;
8878     for (int i = 0; i < DAC_MAX_EXPAND_MECHANISMS_COUNT; i++)
8879     {
8880         ExtOut("%30s: %d\n", str_heap_expand_mechanisms[i], data[i]);
8881     }
8882
8883     ExtOut("\nOther mechanisms enabled\n");
8884     data = dataPerHeap->bitMechanisms;
8885     for (int i = 0; i < DAC_MAX_GC_MECHANISM_BITS_COUNT; i++)
8886     {
8887         ExtOut("%20s: %d\n", str_bit_mechanisms[i], data[i]);
8888     }
8889 }
8890 #endif //GC_CONFIG_DRIVEN
8891
8892 DECLARE_API(DumpGCData)
8893 {
8894     INIT_API();
8895
8896 #ifdef GC_CONFIG_DRIVEN
8897     MINIDUMP_NOT_SUPPORTED();    
8898
8899     if (!InitializeHeapData ())
8900     {
8901         ExtOut("GC Heap not initialized yet.\n");
8902         return S_OK;
8903     }
8904
8905     DacpGCInterestingInfoData interestingInfo;
8906     interestingInfo.RequestGlobal(g_sos);
8907     for (int i = 0; i < DAC_MAX_GLOBAL_GC_MECHANISMS_COUNT; i++)
8908     {
8909         ExtOut("%-30s: %d\n", str_gc_global_mechanisms[i], interestingInfo.globalMechanisms[i]);
8910     }
8911
8912     ExtOut("\n[info per heap]\n");
8913
8914     if (!IsServerBuild())
8915     {
8916         if (interestingInfo.Request(g_sos) != S_OK)
8917         {
8918             ExtOut("Error requesting interesting GC info\n");
8919             return E_FAIL;
8920         }
8921             
8922         PrintInterestingGCInfo(&interestingInfo);
8923     }
8924     else
8925     {   
8926         DWORD dwNHeaps = GetGcHeapCount();
8927         DWORD dwAllocSize;
8928         if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
8929         {
8930             ExtOut("Failed to get GCHeaps:  integer overflow\n");
8931             return Status;
8932         }
8933
8934         CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
8935         if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
8936         {
8937             ExtOut("Failed to get GCHeaps\n");
8938             return Status;
8939         }
8940         
8941         for (DWORD n = 0; n < dwNHeaps; n ++)
8942         {
8943             if (interestingInfo.Request(g_sos, heapAddrs[n]) != S_OK)
8944             {
8945                 ExtOut("Heap %d: Error requesting interesting GC info\n", n);
8946                 return E_FAIL;
8947             }
8948
8949             ExtOut("--------info for heap %d--------\n", n);
8950             PrintInterestingGCInfo(&interestingInfo);
8951             ExtOut("\n");
8952         }
8953     }
8954
8955     return S_OK;
8956 #else
8957     ExtOut("Not implemented\n");
8958     return S_OK;
8959 #endif //GC_CONFIG_DRIVEN
8960 }
8961
8962 #ifndef FEATURE_PAL
8963 /**********************************************************************\
8964 * Routine Description:                                                 *
8965 *                                                                      *
8966 *    This function is called to dump the build number and type of the  *  
8967 *    mscoree.dll                                                       *
8968 *                                                                      *
8969 \**********************************************************************/
8970 DECLARE_API (EEVersion)
8971 {
8972     INIT_API();
8973
8974     EEFLAVOR eef = GetEEFlavor();
8975     if (eef == UNKNOWNEE) {
8976         ExtOut("CLR not loaded\n");
8977         return Status;
8978     }
8979     
8980     if (g_ExtSymbols2) {
8981         VS_FIXEDFILEINFO version;
8982         
8983         BOOL ret = GetEEVersion(&version);
8984             
8985         if (ret) 
8986         {
8987             if (version.dwFileVersionMS != (DWORD)-1)
8988             {
8989                 ExtOut("%u.%u.%u.%u",
8990                        HIWORD(version.dwFileVersionMS),
8991                        LOWORD(version.dwFileVersionMS),
8992                        HIWORD(version.dwFileVersionLS),
8993                        LOWORD(version.dwFileVersionLS));
8994                 if (version.dwFileFlags & VS_FF_DEBUG) 
8995                 {                    
8996                     ExtOut(" Checked or debug build");
8997                 }
8998                 else
8999                 { 
9000                     BOOL fRet = IsRetailBuild ((size_t)moduleInfo[eef].baseAddr);
9001
9002                     if (fRet)
9003                         ExtOut(" retail");
9004                     else
9005                         ExtOut(" free");
9006                 }
9007
9008                 ExtOut("\n");
9009             }
9010         }
9011     }    
9012     
9013     if (!InitializeHeapData ())
9014         ExtOut("GC Heap not initialized, so GC mode is not determined yet.\n");
9015     else if (IsServerBuild()) 
9016         ExtOut("Server mode with %d gc heaps\n", GetGcHeapCount()); 
9017     else
9018         ExtOut("Workstation mode\n");
9019
9020     if (!GetGcStructuresValid())
9021     {
9022         ExtOut("In plan phase of garbage collection\n");
9023     }
9024
9025     // Print SOS version
9026     VS_FIXEDFILEINFO sosVersion;
9027     if (GetSOSVersion(&sosVersion))
9028     {
9029         if (sosVersion.dwFileVersionMS != (DWORD)-1)
9030         {
9031             ExtOut("SOS Version: %u.%u.%u.%u",
9032                    HIWORD(sosVersion.dwFileVersionMS),
9033                    LOWORD(sosVersion.dwFileVersionMS),
9034                    HIWORD(sosVersion.dwFileVersionLS),
9035                    LOWORD(sosVersion.dwFileVersionLS));
9036             if (sosVersion.dwFileFlags & VS_FF_DEBUG) 
9037             {                    
9038                 ExtOut(" Checked or debug build");                    
9039             }
9040             else
9041             { 
9042                 ExtOut(" retail build");                    
9043             }
9044
9045             ExtOut("\n");
9046         }
9047     }
9048     return Status;
9049 }
9050 #endif // FEATURE_PAL
9051
9052 #ifndef FEATURE_PAL
9053 /**********************************************************************\
9054 * Routine Description:                                                 *
9055 *                                                                      *
9056 *    This function is called to print the environment setting for      *  
9057 *    the current process.                                              *
9058 *                                                                      *
9059 \**********************************************************************/
9060 DECLARE_API (ProcInfo)
9061 {
9062     INIT_API();
9063     MINIDUMP_NOT_SUPPORTED();        
9064
9065     if (IsDumpFile())
9066     {
9067         ExtOut("!ProcInfo is not supported on a dump file.\n");
9068         return Status;
9069     }
9070
9071 #define INFO_ENV        0x00000001
9072 #define INFO_TIME       0x00000002    
9073 #define INFO_MEM        0x00000004
9074 #define INFO_ALL        0xFFFFFFFF
9075
9076     DWORD fProcInfo = INFO_ALL;
9077
9078     if (_stricmp (args, "-env") == 0) {
9079         fProcInfo = INFO_ENV;
9080     }
9081
9082     if (_stricmp (args, "-time") == 0) {
9083         fProcInfo = INFO_TIME;
9084     }
9085
9086     if (_stricmp (args, "-mem") == 0) {
9087         fProcInfo = INFO_MEM;
9088     }
9089
9090     if (fProcInfo & INFO_ENV) {
9091         ExtOut("---------------------------------------\n");
9092         ExtOut("Environment\n");
9093         ULONG64 pPeb;
9094         g_ExtSystem->GetCurrentProcessPeb(&pPeb);
9095
9096         static ULONG Offset_ProcessParam = -1;
9097         static ULONG Offset_Environment = -1;
9098         if (Offset_ProcessParam == -1)
9099         {
9100             ULONG TypeId;
9101             ULONG64 NtDllBase;
9102             if (SUCCEEDED(g_ExtSymbols->GetModuleByModuleName ("ntdll",0,NULL,
9103                                                                &NtDllBase)))
9104             {
9105                 if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "PEB", &TypeId)))
9106                 {
9107                     if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
9108                                                          "ProcessParameters", &Offset_ProcessParam)))
9109                         Offset_ProcessParam = -1;
9110                 }
9111                 if (SUCCEEDED(g_ExtSymbols->GetTypeId (NtDllBase, "_RTL_USER_PROCESS_PARAMETERS", &TypeId)))
9112                 {
9113                     if (FAILED (g_ExtSymbols->GetFieldOffset(NtDllBase, TypeId,
9114                                                          "Environment", &Offset_Environment)))
9115                         Offset_Environment = -1;
9116                 }
9117             }
9118         }
9119         // We can not get it from PDB.  Use the fixed one.
9120         if (Offset_ProcessParam == -1)
9121             Offset_ProcessParam = offsetof (DT_PEB, ProcessParameters);
9122
9123         if (Offset_Environment == -1)
9124             Offset_Environment = offsetof (DT_RTL_USER_PROCESS_PARAMETERS, Environment);
9125
9126
9127         ULONG64 addr = pPeb + Offset_ProcessParam;
9128         DWORD_PTR value;
9129         g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL);
9130         addr = value + Offset_Environment;
9131         g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &value, sizeof(PVOID), NULL);
9132
9133         static WCHAR buffer[DT_OS_PAGE_SIZE/2];        
9134         ULONG readBytes = DT_OS_PAGE_SIZE;
9135         ULONG64 Page;
9136         if ((g_ExtData->ReadDebuggerData( DEBUG_DATA_MmPageSize, &Page, sizeof(Page), NULL)) == S_OK
9137             && Page > 0)
9138         {
9139             ULONG uPageSize = (ULONG)(ULONG_PTR)Page;
9140             if (readBytes > uPageSize) {
9141                 readBytes = uPageSize;
9142             }
9143         }        
9144         addr = value;
9145         while (1) {
9146             if (IsInterrupt())
9147                 return Status;
9148             if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(addr), &buffer, readBytes, NULL)))
9149                 break;
9150             addr += readBytes;
9151             WCHAR *pt = buffer;
9152             WCHAR *end = pt;
9153             while (pt < &buffer[DT_OS_PAGE_SIZE/2]) {
9154                 end = _wcschr (pt, L'\0');
9155                 if (end == NULL) {
9156                     char format[20];
9157                     sprintf_s (format,_countof (format), "%dS", &buffer[DT_OS_PAGE_SIZE/2] - pt);
9158                     ExtOut(format, pt);
9159                     break;
9160                 }
9161                 else if (end == pt) {
9162                     break;
9163                 }
9164                 ExtOut("%S\n", pt);
9165                 pt = end + 1;
9166             }
9167             if (end == pt) {
9168                 break;
9169             }
9170         }
9171     }
9172     
9173     HANDLE hProcess = INVALID_HANDLE_VALUE;
9174     if (fProcInfo & (INFO_TIME | INFO_MEM)) {
9175         ULONG64 handle;
9176         g_ExtSystem->GetCurrentProcessHandle(&handle);
9177         hProcess = (HANDLE)handle;
9178     }
9179     
9180     if (!IsDumpFile() && fProcInfo & INFO_TIME) {
9181         FILETIME CreationTime;
9182         FILETIME ExitTime;
9183         FILETIME KernelTime;
9184         FILETIME UserTime;
9185
9186         typedef BOOL (WINAPI *FntGetProcessTimes)(HANDLE, LPFILETIME, LPFILETIME, LPFILETIME, LPFILETIME);
9187         static FntGetProcessTimes pFntGetProcessTimes = (FntGetProcessTimes)-1;
9188         if (pFntGetProcessTimes == (FntGetProcessTimes)-1) {
9189             HINSTANCE hstat = LoadLibrary ("Kernel32.dll");
9190             if (hstat != 0)
9191             {
9192                 pFntGetProcessTimes = (FntGetProcessTimes)GetProcAddress (hstat, "GetProcessTimes");
9193                 FreeLibrary (hstat);
9194             }
9195             else
9196                 pFntGetProcessTimes = NULL;
9197         }
9198
9199         if (pFntGetProcessTimes && pFntGetProcessTimes (hProcess,&CreationTime,&ExitTime,&KernelTime,&UserTime)) {
9200             ExtOut("---------------------------------------\n");
9201             ExtOut("Process Times\n");
9202             static const char *Month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
9203                         "Oct", "Nov", "Dec"};
9204             SYSTEMTIME SystemTime;
9205             FILETIME LocalFileTime;
9206             if (FileTimeToLocalFileTime (&CreationTime,&LocalFileTime)
9207                 && FileTimeToSystemTime (&LocalFileTime,&SystemTime)) {
9208                 ExtOut("Process Started at: %4d %s %2d %d:%d:%d.%02d\n",
9209                         SystemTime.wYear, Month[SystemTime.wMonth-1], SystemTime.wDay,
9210                         SystemTime.wHour, SystemTime.wMinute,
9211                         SystemTime.wSecond, SystemTime.wMilliseconds/10);
9212             }
9213         
9214             DWORD nDay = 0;
9215             DWORD nHour = 0;
9216             DWORD nMin = 0;
9217             DWORD nSec = 0;
9218             DWORD nHundred = 0;
9219             
9220             ULONG64 totalTime;
9221              
9222             totalTime = KernelTime.dwLowDateTime + (((ULONG64)KernelTime.dwHighDateTime) << 32);
9223             nDay = (DWORD)(totalTime/(24*3600*10000000ui64));
9224             totalTime %= 24*3600*10000000ui64;
9225             nHour = (DWORD)(totalTime/(3600*10000000ui64));
9226             totalTime %= 3600*10000000ui64;
9227             nMin = (DWORD)(totalTime/(60*10000000));
9228             totalTime %= 60*10000000;
9229             nSec = (DWORD)(totalTime/10000000);
9230             totalTime %= 10000000;
9231             nHundred = (DWORD)(totalTime/100000);
9232             ExtOut("Kernel CPU time   : %d days %02d:%02d:%02d.%02d\n",
9233                     nDay, nHour, nMin, nSec, nHundred);
9234             
9235             DWORD sDay = nDay;
9236             DWORD sHour = nHour;
9237             DWORD sMin = nMin;
9238             DWORD sSec = nSec;
9239             DWORD sHundred = nHundred;
9240             
9241             totalTime = UserTime.dwLowDateTime + (((ULONG64)UserTime.dwHighDateTime) << 32);
9242             nDay = (DWORD)(totalTime/(24*3600*10000000ui64));
9243             totalTime %= 24*3600*10000000ui64;
9244             nHour = (DWORD)(totalTime/(3600*10000000ui64));
9245             totalTime %= 3600*10000000ui64;
9246             nMin = (DWORD)(totalTime/(60*10000000));
9247             totalTime %= 60*10000000;
9248             nSec = (DWORD)(totalTime/10000000);
9249             totalTime %= 10000000;
9250             nHundred = (DWORD)(totalTime/100000);
9251             ExtOut("User   CPU time   : %d days %02d:%02d:%02d.%02d\n",
9252                     nDay, nHour, nMin, nSec, nHundred);
9253         
9254             sDay += nDay;
9255             sHour += nHour;
9256             sMin += nMin;
9257             sSec += nSec;
9258             sHundred += nHundred;
9259             if (sHundred >= 100) {
9260                 sSec += sHundred/100;
9261                 sHundred %= 100;
9262             }
9263             if (sSec >= 60) {
9264                 sMin += sSec/60;
9265                 sSec %= 60;
9266             }
9267             if (sMin >= 60) {
9268                 sHour += sMin/60;
9269                 sMin %= 60;
9270             }
9271             if (sHour >= 24) {
9272                 sDay += sHour/24;
9273                 sHour %= 24;
9274             }
9275             ExtOut("Total  CPU time   : %d days %02d:%02d:%02d.%02d\n",
9276                     sDay, sHour, sMin, sSec, sHundred);
9277         }
9278     }
9279
9280     if (!IsDumpFile() && fProcInfo & INFO_MEM) {
9281         typedef
9282         NTSTATUS
9283         (NTAPI
9284          *FntNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
9285
9286         static FntNtQueryInformationProcess pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)-1;
9287         if (pFntNtQueryInformationProcess == (FntNtQueryInformationProcess)-1) {
9288             HINSTANCE hstat = LoadLibrary ("ntdll.dll");
9289             if (hstat != 0)
9290             {
9291                 pFntNtQueryInformationProcess = (FntNtQueryInformationProcess)GetProcAddress (hstat, "NtQueryInformationProcess");
9292                 FreeLibrary (hstat);
9293             }
9294             else
9295                 pFntNtQueryInformationProcess = NULL;
9296         }
9297         VM_COUNTERS memory;
9298         if (pFntNtQueryInformationProcess &&
9299             NT_SUCCESS (pFntNtQueryInformationProcess (hProcess,ProcessVmCounters,&memory,sizeof(memory),NULL))) {
9300             ExtOut("---------------------------------------\n");
9301             ExtOut("Process Memory\n");
9302             ExtOut("WorkingSetSize: %8d KB       PeakWorkingSetSize: %8d KB\n",
9303                     memory.WorkingSetSize/1024, memory.PeakWorkingSetSize/1024);
9304             ExtOut("VirtualSize:    %8d KB       PeakVirtualSize:    %8d KB\n", 
9305                     memory.VirtualSize/1024, memory.PeakVirtualSize/1024);
9306             ExtOut("PagefileUsage:  %8d KB       PeakPagefileUsage:  %8d KB\n", 
9307                     memory.PagefileUsage/1024, memory.PeakPagefileUsage/1024);
9308         }
9309
9310         MEMORYSTATUS memstat;
9311         GlobalMemoryStatus (&memstat);
9312         ExtOut("---------------------------------------\n");
9313         ExtOut("%ld percent of memory is in use.\n\n",
9314                 memstat.dwMemoryLoad);
9315         ExtOut("Memory Availability (Numbers in MB)\n\n");
9316         ExtOut("                  %8s     %8s\n", "Total", "Avail");
9317         ExtOut("Physical Memory   %8d     %8d\n", memstat.dwTotalPhys/1024/1024, memstat.dwAvailPhys/1024/1024);
9318         ExtOut("Page File         %8d     %8d\n", memstat.dwTotalPageFile/1024/1024, memstat.dwAvailPageFile/1024/1024);
9319         ExtOut("Virtual Memory    %8d     %8d\n", memstat.dwTotalVirtual/1024/1024, memstat.dwAvailVirtual/1024/1024);
9320     }
9321
9322     return Status;
9323 }
9324 #endif // FEATURE_PAL
9325
9326 /**********************************************************************\
9327 * Routine Description:                                                 *
9328 *                                                                      *
9329 *    This function is called to find the address of EE data for a      *  
9330 *    metadata token.                                                   *
9331 *                                                                      *
9332 \**********************************************************************/
9333 DECLARE_API(Token2EE)
9334 {
9335     INIT_API();
9336     MINIDUMP_NOT_SUPPORTED();    
9337
9338     StringHolder DllName;
9339     ULONG64 token = 0;
9340     BOOL dml = FALSE;
9341
9342     CMDOption option[] = 
9343     {   // name, vptr, type, hasValue
9344 #ifndef FEATURE_PAL
9345         {"/d", &dml, COBOOL, FALSE},
9346 #endif
9347     };
9348
9349     CMDValue arg[] = 
9350     {   // vptr, type
9351         {&DllName.data, COSTRING},
9352         {&token, COHEX}
9353     };
9354
9355     size_t nArg;
9356     if (!GetCMDOption(args,option, _countof(option), arg, _countof(arg), &nArg)) 
9357     {
9358         return Status;
9359     }
9360     if (nArg!=2)
9361     {
9362         ExtOut("Usage: !Token2EE module_name mdToken\n");
9363         ExtOut("       You can pass * for module_name to search all modules.\n");
9364         return Status;
9365     }
9366
9367     EnableDMLHolder dmlHolder(dml);
9368     int numModule;
9369     ArrayHolder<DWORD_PTR> moduleList = NULL;
9370
9371     if (strcmp(DllName.data, "*") == 0)
9372     {
9373         moduleList = ModuleFromName(NULL, &numModule);
9374     }
9375     else
9376     {
9377         moduleList = ModuleFromName(DllName.data, &numModule);
9378     }
9379     
9380     if (moduleList == NULL)
9381     {
9382         ExtOut("Failed to request module list.\n");
9383     }
9384     else
9385     {
9386         for (int i = 0; i < numModule; i ++)
9387         {
9388             if (IsInterrupt())
9389                 break;
9390
9391             if (i > 0)
9392             {
9393                 ExtOut("--------------------------------------\n");
9394             }        
9395
9396             DWORD_PTR dwAddr = moduleList[i];
9397             WCHAR FileName[MAX_LONGPATH];
9398             FileNameForModule(dwAddr, FileName);
9399
9400             // We'd like a short form for this output
9401             LPWSTR pszFilename = _wcsrchr (FileName, DIRECTORY_SEPARATOR_CHAR_W);
9402             if (pszFilename == NULL)
9403             {
9404                 pszFilename = FileName;
9405             }
9406             else
9407             {
9408                 pszFilename++; // skip past the last "\" character
9409             }
9410             
9411             DMLOut("Module:      %s\n", DMLModule(dwAddr));
9412             ExtOut("Assembly:    %S\n", pszFilename);
9413             
9414             GetInfoFromModule(dwAddr, (ULONG)token);
9415         }
9416     }
9417     
9418     return Status;
9419 }
9420
9421 /**********************************************************************\
9422 * Routine Description:                                                 *
9423 *                                                                      *
9424 *    This function is called to find the address of EE data for a      *  
9425 *    metadata token.                                                   *
9426 *                                                                      *
9427 \**********************************************************************/
9428 DECLARE_API(Name2EE)
9429 {
9430     INIT_API();
9431     MINIDUMP_NOT_SUPPORTED();
9432
9433     StringHolder DllName, TypeName; 
9434     BOOL dml = FALSE;
9435
9436     CMDOption option[] = 
9437     {   // name, vptr, type, hasValue
9438 #ifndef FEATURE_PAL
9439         {"/d", &dml, COBOOL, FALSE},
9440 #endif
9441     };
9442     
9443     CMDValue arg[] = 
9444     {   // vptr, type
9445         {&DllName.data, COSTRING},
9446         {&TypeName.data, COSTRING}
9447     };
9448     size_t nArg;
9449     
9450     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
9451     {
9452         return Status;
9453     }
9454
9455     EnableDMLHolder dmlHolder(dml);
9456
9457     if (nArg == 1)
9458     {
9459         // The input may be in the form <modulename>!<type>
9460         // If so, do some surgery on the input params.
9461
9462         // There should be only 1 ! character
9463         LPSTR pszSeperator = strchr (DllName.data, '!');
9464         if (pszSeperator != NULL)
9465         {
9466             if (strchr (pszSeperator + 1, '!') == NULL)
9467             {
9468                 size_t capacity_TypeName_data = strlen(pszSeperator + 1) + 1;
9469                 TypeName.data = new NOTHROW char[capacity_TypeName_data];
9470                 if (TypeName.data)
9471                 {
9472                     // get the type name,
9473                     strcpy_s (TypeName.data, capacity_TypeName_data, pszSeperator + 1);
9474                     // and truncate DllName
9475                     *pszSeperator = '\0';
9476
9477                     // Do some extra validation
9478                     if (strlen (DllName.data) >= 1 &&
9479                         strlen (TypeName.data) > 1)
9480                     {
9481                         nArg = 2;
9482                     }
9483                 }
9484             }
9485         }
9486     }
9487     
9488     if (nArg != 2)
9489     {
9490         ExtOut("Usage: " SOSPrefix "name2ee module_name item_name\n");
9491         ExtOut("  or   " SOSPrefix "name2ee module_name!item_name\n");        
9492         ExtOut("       use * for module_name to search all loaded modules\n");
9493         ExtOut("Examples: " SOSPrefix "name2ee  mscorlib.dll System.String.ToString\n");
9494         ExtOut("          " SOSPrefix "name2ee *!System.String\n");
9495         return Status;
9496     }
9497     
9498     int numModule;
9499     ArrayHolder<DWORD_PTR> moduleList = NULL;
9500     if (strcmp(DllName.data, "*") == 0)
9501     {
9502         moduleList = ModuleFromName(NULL, &numModule);
9503     }
9504     else
9505     {
9506         moduleList = ModuleFromName(DllName.data, &numModule);
9507     }
9508             
9509
9510     if (moduleList == NULL)
9511     {
9512         ExtOut("Failed to request module list.\n", DllName.data);
9513     }
9514     else
9515     {
9516         for (int i = 0; i < numModule; i ++)
9517         {
9518             if (IsInterrupt())
9519                 break;
9520
9521             if (i > 0)
9522             {
9523                 ExtOut("--------------------------------------\n");
9524             }
9525             
9526             DWORD_PTR dwAddr = moduleList[i];
9527             WCHAR FileName[MAX_LONGPATH];
9528             FileNameForModule (dwAddr, FileName);
9529
9530             // We'd like a short form for this output
9531             LPWSTR pszFilename = _wcsrchr (FileName, DIRECTORY_SEPARATOR_CHAR_W);
9532             if (pszFilename == NULL)
9533             {
9534                 pszFilename = FileName;
9535             }
9536             else
9537             {
9538                 pszFilename++; // skip past the last "\" character
9539             }
9540             
9541             DMLOut("Module:      %s\n", DMLModule(dwAddr));
9542             ExtOut("Assembly:    %S\n", pszFilename);
9543             GetInfoFromName(dwAddr, TypeName.data);
9544         }
9545     }
9546  
9547     return Status;
9548 }
9549
9550
9551 #ifndef FEATURE_PAL
9552 DECLARE_API(PathTo)
9553 {
9554     INIT_API();
9555     MINIDUMP_NOT_SUPPORTED();    
9556
9557     DWORD_PTR root = NULL;
9558     DWORD_PTR target = NULL;
9559     BOOL dml = FALSE;
9560     size_t nArg;
9561
9562     CMDOption option[] = 
9563     {   // name, vptr, type, hasValue
9564         {"/d", &dml, COBOOL, FALSE},
9565     };
9566     CMDValue arg[] = 
9567     {   // vptr, type
9568         {&root, COHEX},
9569         {&target, COHEX},
9570     };
9571     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9572     {
9573         return Status;
9574     }    
9575     
9576     if (root == 0 || target == 0)
9577     {
9578         ExtOut("Invalid argument %s\n", args);
9579         return Status;
9580     }
9581     
9582     GCRootImpl gcroot;
9583     bool result = gcroot.PrintPathToObject(root, target);
9584     
9585     if (!result)
9586         ExtOut("Did not find a path from %p to %p.\n", SOS_PTR(root), SOS_PTR(target));
9587     
9588     return Status;
9589 }
9590 #endif
9591
9592
9593
9594 /**********************************************************************\
9595 * Routine Description:                                                 *
9596 *                                                                      *
9597 *    This function finds all roots (on stack or in handles) for a      *  
9598 *    given object.                                                     *
9599 *                                                                      *
9600 \**********************************************************************/
9601 DECLARE_API(GCRoot)
9602 {
9603     INIT_API();
9604     MINIDUMP_NOT_SUPPORTED();    
9605
9606     BOOL bNoStacks = FALSE;
9607     DWORD_PTR obj = NULL;
9608     BOOL dml = FALSE;
9609     BOOL all = FALSE;
9610     size_t nArg;
9611
9612     CMDOption option[] = 
9613     {   // name, vptr, type, hasValue
9614         {"-nostacks", &bNoStacks, COBOOL, FALSE},
9615         {"-all", &all, COBOOL, FALSE},
9616 #ifndef FEATURE_PAL
9617         {"/d", &dml, COBOOL, FALSE},
9618 #endif
9619     };
9620     CMDValue arg[] = 
9621
9622     {   // vptr, type
9623         {&obj, COHEX}
9624     };
9625     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9626     {
9627         return Status;
9628     }    
9629     if (obj == 0)
9630     {
9631         ExtOut("Invalid argument %s\n", args);
9632         return Status;
9633     }
9634
9635     EnableDMLHolder dmlHolder(dml);      
9636     GCRootImpl gcroot;
9637     int i = gcroot.PrintRootsForObject(obj, all == TRUE, bNoStacks == TRUE);
9638     
9639     if (IsInterrupt())
9640         ExtOut("Interrupted, data may be incomplete.\n");
9641     
9642     if (all)
9643         ExtOut("Found %d roots.\n", i);
9644     else
9645         ExtOut("Found %d unique roots (run '!GCRoot -all' to see all roots).\n", i);
9646
9647     return Status;
9648 }
9649
9650 DECLARE_API(GCWhere)
9651 {
9652     INIT_API();
9653     MINIDUMP_NOT_SUPPORTED();
9654
9655     BOOL dml = FALSE;
9656     BOOL bGetBrick;
9657     BOOL bGetCard;
9658     TADDR taddrObj = 0;
9659     size_t nArg;
9660
9661     CMDOption option[] = 
9662     {   // name, vptr, type, hasValue
9663         {"-brick", &bGetBrick, COBOOL, FALSE},
9664         {"-card", &bGetCard, COBOOL, FALSE},
9665         {"/d", &dml, COBOOL, FALSE},
9666     };
9667     CMDValue arg[] = 
9668     {   // vptr, type
9669         {&taddrObj, COHEX}
9670     };
9671     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9672     {
9673         return Status;
9674     }
9675
9676     EnableDMLHolder dmlHolder(dml);
9677     // Obtain allocation context for each managed thread.    
9678     AllocInfo allocInfo;
9679     allocInfo.Init();
9680
9681     TADDR_SEGINFO trngSeg  = { 0, 0, 0 };
9682     TADDR_RANGE   allocCtx = { 0, 0 };
9683     int   gen = -1;
9684     BOOL  bLarge = FALSE;
9685     BOOL  bFound = FALSE;
9686
9687     size_t size = 0;
9688     if (sos::IsObject(taddrObj))
9689     {
9690         TADDR taddrMT; 
9691         BOOL  bContainsPointers;
9692         if(FAILED(GetMTOfObject(taddrObj, &taddrMT)) ||
9693            !GetSizeEfficient(taddrObj, taddrMT, FALSE, size, bContainsPointers))
9694         {
9695             ExtWarn("Couldn't get size for object %#p: possible heap corruption.\n",
9696                 SOS_PTR(taddrObj));
9697         }
9698     }
9699
9700     if (!IsServerBuild())
9701     {
9702         DacpGcHeapDetails heapDetails;
9703         if (heapDetails.Request(g_sos) != S_OK)
9704         {
9705             ExtOut("Error requesting gc heap details\n");
9706             return Status;
9707         }
9708
9709         if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge))
9710         {
9711             ExtOut("Address   " WIN64_8SPACES " Gen   Heap   segment   " WIN64_8SPACES " begin     " WIN64_8SPACES " allocated  " WIN64_8SPACES " size\n");
9712             ExtOut("%p   %d     %2d     %p   %p   %p    0x%x(%d)\n",
9713                 SOS_PTR(taddrObj), gen, 0, SOS_PTR(trngSeg.segAddr), SOS_PTR(trngSeg.start), SOS_PTR(trngSeg.end), size, size);
9714             bFound = TRUE;
9715         }
9716     }
9717     else
9718     {
9719         DacpGcHeapData gcheap;
9720         if (gcheap.Request(g_sos) != S_OK)
9721         {
9722             ExtOut("Error requesting GC Heap data\n");
9723             return Status;
9724         }
9725
9726         DWORD dwAllocSize;
9727         DWORD dwNHeaps = gcheap.HeapCount;
9728         if (!ClrSafeInt<DWORD>::multiply(sizeof(CLRDATA_ADDRESS), dwNHeaps, dwAllocSize))
9729         {
9730             ExtOut("Failed to get GCHeaps:  integer overflow\n");
9731             return Status;
9732         }
9733
9734         CLRDATA_ADDRESS *heapAddrs = (CLRDATA_ADDRESS*)alloca(dwAllocSize);
9735         if (g_sos->GetGCHeapList(dwNHeaps, heapAddrs, NULL) != S_OK)
9736         {
9737             ExtOut("Failed to get GCHeaps\n");
9738             return Status;
9739         }
9740  
9741         for (DWORD n = 0; n < dwNHeaps; n ++)
9742         {
9743             DacpGcHeapDetails heapDetails;
9744             if (heapDetails.Request(g_sos, heapAddrs[n]) != S_OK)
9745             {
9746                 ExtOut("Error requesting details\n");
9747                 return Status;
9748             }
9749
9750             if (GCObjInHeap(taddrObj, heapDetails, trngSeg, gen, allocCtx, bLarge))
9751             {
9752                 ExtOut("Address " WIN64_8SPACES " Gen Heap segment " WIN64_8SPACES " begin   " WIN64_8SPACES " allocated" WIN64_8SPACES " size\n");
9753                 ExtOut("%p   %d     %2d     %p   %p   %p    0x%x(%d)\n",
9754                     SOS_PTR(taddrObj), gen, n, SOS_PTR(trngSeg.segAddr), SOS_PTR(trngSeg.start), SOS_PTR(trngSeg.end), size, size);
9755                 bFound = TRUE;
9756                 break;
9757             }
9758         }
9759     }
9760
9761     if (!bFound)
9762     {
9763         ExtOut("Address %#p not found in the managed heap.\n", SOS_PTR(taddrObj));
9764     }
9765
9766     return Status;
9767 }
9768
9769 #ifndef FEATURE_PAL
9770
9771 DECLARE_API(FindRoots)
9772 {
9773 #ifndef FEATURE_PAL
9774     INIT_API();
9775     MINIDUMP_NOT_SUPPORTED();
9776
9777     if (IsDumpFile())
9778     {
9779         ExtOut("!FindRoots is not supported on a dump file.\n");
9780         return Status;
9781     }
9782     
9783     LONG_PTR gen = -100; // initialized outside the legal range: [-1, 2]
9784     StringHolder sgen;
9785     TADDR taObj = NULL;
9786     BOOL dml = FALSE;
9787     size_t nArg;
9788
9789     CMDOption option[] = 
9790     {   // name, vptr, type, hasValue
9791         {"-gen", &sgen.data, COSTRING, TRUE},
9792         {"/d", &dml, COBOOL, FALSE},
9793     };
9794     CMDValue arg[] = 
9795     {   // vptr, type
9796         {&taObj, COHEX}
9797     };
9798     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
9799     {
9800         return Status;
9801     }
9802
9803     EnableDMLHolder dmlHolder(dml);
9804     if (sgen.data != NULL)
9805     {
9806         if (_stricmp(sgen.data, "any") == 0)
9807         {
9808             gen = -1;
9809         }
9810         else
9811         {
9812             gen = GetExpression(sgen.data);
9813         }
9814     }
9815     if ((gen < -1 || gen > 2) && (taObj == 0))
9816     {
9817         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");
9818         return Status;
9819     }
9820
9821     if (gen >= -1 && gen <= 2)
9822     {
9823         IXCLRDataProcess2* idp2 = NULL;
9824         if (FAILED(g_clrData->QueryInterface(IID_IXCLRDataProcess2, (void**) &idp2)))
9825         {
9826             ExtOut("Your version of the runtime/DAC do not support this command.\n");
9827             return Status;
9828         }
9829
9830         // Request GC_MARK_END notifications from debuggee
9831         GcEvtArgs gea = { GC_MARK_END, { ((gen == -1) ? 7 : (1 << gen)) } };
9832         idp2->SetGcNotification(gea);
9833         // ... and register the notification handler
9834         g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0);
9835         // the above notification is removed in CNotification::OnGcEvent()
9836     }
9837     else
9838     {
9839         // verify that the last event in the debugger was indeed a CLRN exception
9840         DEBUG_LAST_EVENT_INFO_EXCEPTION dle;
9841         CNotification Notification;
9842
9843         if (!CheckCLRNotificationEvent(&dle))
9844         {
9845             ExtOut("The command !FindRoots can only be used after the debugger stopped on a CLRN GC notification.\n");
9846             ExtOut("At this time !GCRoot should be used instead.\n");
9847             return Status;
9848         }
9849         // validate argument
9850         if (!g_snapshot.Build())
9851         {
9852             ExtOut("Unable to build snapshot of the garbage collector state\n");
9853             return Status;
9854         }
9855
9856         if (g_snapshot.GetHeap(taObj) == NULL)
9857         {
9858             ExtOut("Address %#p is not in the managed heap.\n", SOS_PTR(taObj));
9859             return Status;
9860         }
9861
9862         int ogen = g_snapshot.GetGeneration(taObj);
9863         if (ogen > CNotification::GetCondemnedGen())
9864         {
9865             DMLOut("Object %s will survive this collection:\n\tgen(%#p) = %d > %d = condemned generation.\n",
9866                 DMLObject(taObj), SOS_PTR(taObj), ogen, CNotification::GetCondemnedGen());
9867             return Status;
9868         }
9869
9870         GCRootImpl gcroot;
9871         int roots = gcroot.FindRoots(CNotification::GetCondemnedGen(), taObj);
9872         
9873         ExtOut("Found %d roots.\n", roots);
9874     }
9875
9876     return Status;
9877 #else
9878     return E_NOTIMPL;
9879 #endif
9880 }
9881
9882 class GCHandleStatsForDomains
9883 {
9884 public:
9885     const static int SHARED_DOMAIN_INDEX = 0;
9886     const static int SYSTEM_DOMAIN_INDEX = 1;
9887     
9888     GCHandleStatsForDomains() 
9889         : m_singleDomainMode(FALSE), m_numDomains(0), m_pStatistics(NULL), m_pDomainPointers(NULL)
9890     {
9891     }
9892
9893     ~GCHandleStatsForDomains()
9894     {
9895         if (m_pStatistics)
9896         {
9897             if (m_singleDomainMode)
9898                 delete m_pStatistics;
9899             else
9900                 delete [] m_pStatistics;
9901         }
9902         
9903         if (m_pDomainPointers)
9904             delete [] m_pDomainPointers;
9905     }
9906     
9907     BOOL Init(BOOL singleDomainMode)
9908     {
9909         m_singleDomainMode = singleDomainMode; 
9910         if (m_singleDomainMode)
9911         {
9912             m_numDomains = 1;
9913             m_pStatistics = new NOTHROW GCHandleStatistics();
9914             if (m_pStatistics == NULL)
9915                 return FALSE;
9916         }
9917         else
9918         {
9919             DacpAppDomainStoreData adsData;
9920             if (adsData.Request(g_sos) != S_OK)
9921                 return FALSE;
9922
9923             m_numDomains = adsData.DomainCount + 2;
9924             ArrayHolder<CLRDATA_ADDRESS> pArray = new NOTHROW CLRDATA_ADDRESS[adsData.DomainCount + 2];
9925             if (pArray == NULL)
9926                 return FALSE;
9927
9928             pArray[SHARED_DOMAIN_INDEX] = adsData.sharedDomain;
9929             pArray[SYSTEM_DOMAIN_INDEX] = adsData.systemDomain;
9930             
9931             if (g_sos->GetAppDomainList(adsData.DomainCount, pArray+2, NULL) != S_OK)
9932                 return FALSE;
9933             
9934             m_pDomainPointers = pArray.Detach();
9935             m_pStatistics = new NOTHROW GCHandleStatistics[adsData.DomainCount + 2];
9936             if (m_pStatistics == NULL)
9937                 return FALSE;
9938         }
9939         
9940         return TRUE;
9941     }
9942
9943     GCHandleStatistics *LookupStatistics(CLRDATA_ADDRESS appDomainPtr) const
9944     {
9945         if (m_singleDomainMode)
9946         {
9947             // You can pass NULL appDomainPtr if you are in singleDomainMode
9948             return m_pStatistics;
9949         }
9950         else
9951         {
9952             for (int i=0; i < m_numDomains; i++)
9953                 if (m_pDomainPointers[i] == appDomainPtr)
9954                     return m_pStatistics + i;
9955         }
9956         
9957         return NULL;
9958     }
9959     
9960     
9961     GCHandleStatistics *GetStatistics(int appDomainIndex) const
9962     {
9963         SOS_Assert(appDomainIndex >= 0);
9964         SOS_Assert(appDomainIndex < m_numDomains);
9965         
9966         return m_singleDomainMode ? m_pStatistics : m_pStatistics + appDomainIndex;
9967     }
9968     
9969     int GetNumDomains() const
9970     {
9971         return m_numDomains;
9972     }
9973     
9974     CLRDATA_ADDRESS GetDomain(int index) const
9975     {
9976         SOS_Assert(index >= 0);
9977         SOS_Assert(index < m_numDomains);
9978         return m_pDomainPointers[index];
9979     }
9980     
9981 private:
9982     BOOL m_singleDomainMode;
9983     int m_numDomains;
9984     GCHandleStatistics *m_pStatistics;
9985     CLRDATA_ADDRESS *m_pDomainPointers;
9986 };
9987
9988 class GCHandlesImpl
9989 {
9990 public:
9991     GCHandlesImpl(PCSTR args)
9992         : mPerDomain(FALSE), mStat(FALSE), mDML(FALSE), mType((int)~0)
9993     {
9994         ArrayHolder<char> type = NULL;
9995         CMDOption option[] = 
9996         {
9997             {"-perdomain", &mPerDomain, COBOOL, FALSE},
9998             {"-stat", &mStat, COBOOL, FALSE},
9999             {"-type", &type, COSTRING, TRUE},
10000             {"/d", &mDML, COBOOL, FALSE},
10001         };
10002         
10003         if (!GetCMDOption(args,option,_countof(option),NULL,0,NULL))
10004             sos::Throw<sos::Exception>("Failed to parse command line arguments.");
10005         
10006         if (type != NULL)
10007             if (_stricmp(type, "Pinned") == 0)
10008                 mType = HNDTYPE_PINNED;
10009             else if (_stricmp(type, "RefCounted") == 0)
10010                 mType = HNDTYPE_REFCOUNTED;
10011             else if (_stricmp(type, "WeakShort") == 0)
10012                 mType = HNDTYPE_WEAK_SHORT;
10013             else if (_stricmp(type, "WeakLong") == 0)
10014                 mType = HNDTYPE_WEAK_LONG;
10015             else if (_stricmp(type, "Strong") == 0)
10016                 mType = HNDTYPE_STRONG;
10017             else if (_stricmp(type, "Variable") == 0)
10018                 mType = HNDTYPE_VARIABLE;
10019             else if (_stricmp(type, "AsyncPinned") == 0)
10020                 mType = HNDTYPE_ASYNCPINNED;
10021             else if (_stricmp(type, "SizedRef") == 0)
10022                 mType = HNDTYPE_SIZEDREF;
10023             else if (_stricmp(type, "Dependent") == 0)
10024                 mType = HNDTYPE_DEPENDENT;
10025             else if (_stricmp(type, "WeakWinRT") == 0)
10026                 mType = HNDTYPE_WEAK_WINRT;
10027             else
10028                 sos::Throw<sos::Exception>("Unknown handle type '%s'.", type.GetPtr());
10029     }
10030     
10031     void Run()
10032     {
10033         EnableDMLHolder dmlHolder(mDML);
10034         
10035         mOut.ReInit(6, POINTERSIZE_HEX, AlignRight);
10036         mOut.SetWidths(5, POINTERSIZE_HEX, 11, POINTERSIZE_HEX, 8, POINTERSIZE_HEX);
10037         mOut.SetColAlignment(1, AlignLeft);
10038         
10039         if (mHandleStat.Init(!mPerDomain) == FALSE)
10040             sos::Throw<sos::Exception>("Error getting per-appdomain handle information");
10041         
10042         if (!mStat)
10043             mOut.WriteRow("Handle", "Type", "Object", "Size", "Data", "Type");
10044             
10045         WalkHandles();
10046         
10047         for (int i=0; (i < mHandleStat.GetNumDomains()) && !IsInterrupt(); i++)
10048         {
10049             GCHandleStatistics *pStats = mHandleStat.GetStatistics(i);
10050
10051             if (mPerDomain)
10052             {
10053                 Print( "------------------------------------------------------------------------------\n");           
10054                 Print("GC Handle Statistics for AppDomain ", AppDomainPtr(mHandleStat.GetDomain(i)));
10055             
10056                 if (i == GCHandleStatsForDomains::SHARED_DOMAIN_INDEX)
10057                     Print(" (Shared Domain)\n");
10058                 else if (i == GCHandleStatsForDomains::SYSTEM_DOMAIN_INDEX)
10059                     Print(" (System Domain)\n");
10060                 else
10061                     Print("\n");
10062             }
10063
10064             if (!mStat)
10065                 Print("\n");
10066             PrintGCStat(&pStats->hs);
10067             
10068             // Don't print handle stats if the user has filtered by type.  All handles will be the same
10069             // type, and the total count will be displayed by PrintGCStat.
10070             if (mType == (unsigned int)~0)
10071             {
10072                 Print("\n");
10073                 PrintGCHandleStats(pStats);
10074             }
10075         }
10076     }
10077
10078 private:
10079     void WalkHandles()
10080     {
10081         ToRelease<ISOSHandleEnum> handles;
10082         if (FAILED(g_sos->GetHandleEnum(&handles)))
10083         {
10084             if (IsMiniDumpFile())
10085                 sos::Throw<sos::Exception>("Unable to display GC handles.\nA minidump without full memory may not have this information.");
10086             else
10087                 sos::Throw<sos::Exception>("Failed to walk the handle table.");
10088         }
10089       
10090         // GCC can't handle stacks which are too large.
10091 #ifndef FEATURE_PAL
10092         SOSHandleData data[256];
10093 #else
10094         SOSHandleData data[4];
10095 #endif
10096         
10097         unsigned int fetched = 0;
10098         HRESULT hr = S_OK;
10099         do
10100         {
10101             if (FAILED(hr = handles->Next(_countof(data), data, &fetched)))
10102             {
10103                 ExtOut("Error %x while walking the handle table.\n", hr);
10104                 break;
10105             }
10106             
10107             WalkHandles(data, fetched);
10108         } while (_countof(data) == fetched);
10109     }
10110     
10111     void WalkHandles(SOSHandleData data[], unsigned int count)
10112     {
10113         for (unsigned int i = 0; i < count; ++i)
10114         {
10115             sos::CheckInterrupt();
10116         
10117             if (mType != (unsigned int)~0 && mType != data[i].Type)
10118                 continue;
10119         
10120             GCHandleStatistics *pStats = mHandleStat.LookupStatistics(data[i].AppDomain);
10121             TADDR objAddr = 0;
10122             TADDR mtAddr = 0;
10123             size_t size = 0;
10124             const WCHAR *mtName = 0;
10125             const char *type = 0;
10126             
10127             if (FAILED(MOVE(objAddr, data[i].Handle)))
10128             {
10129                 objAddr = 0;
10130                 mtName = W("<error>");
10131             }
10132             else
10133             {
10134                 sos::Object obj(TO_TADDR(objAddr));
10135                 mtAddr = obj.GetMT();
10136                 if (sos::MethodTable::IsFreeMT(mtAddr))
10137                 {
10138                     mtName = W("<free>");
10139                 }
10140                 else if (!sos::MethodTable::IsValid(mtAddr))
10141                 {
10142                     mtName = W("<error>");
10143                 }
10144                 else
10145                 {
10146                     size = obj.GetSize();
10147                     if (mType == (unsigned int)~0 || mType == data[i].Type)
10148                         pStats->hs.Add(obj.GetMT(), (DWORD)size);
10149                 }
10150             }
10151         
10152             switch(data[i].Type)
10153             {
10154                 case HNDTYPE_PINNED:
10155                     type = "Pinned";
10156                     if (pStats) pStats->pinnedHandleCount++;
10157                     break;
10158                 case HNDTYPE_REFCOUNTED:
10159                     type = "RefCounted";
10160                     if (pStats) pStats->refCntHandleCount++;
10161                     break;    
10162                 case HNDTYPE_STRONG:
10163                     type = "Strong";
10164                     if (pStats) pStats->strongHandleCount++;
10165                     break;
10166                 case HNDTYPE_WEAK_SHORT:
10167                     type = "WeakShort";
10168                     if (pStats) pStats->weakShortHandleCount++;
10169                     break;
10170                 case HNDTYPE_WEAK_LONG:
10171                     type = "WeakLong";
10172                     if (pStats) pStats->weakLongHandleCount++;
10173                     break;
10174                 case HNDTYPE_ASYNCPINNED:
10175                     type = "AsyncPinned";
10176                     if (pStats) pStats->asyncPinnedHandleCount++;
10177                     break;
10178                 case HNDTYPE_VARIABLE:
10179                     type = "Variable";
10180                     if (pStats) pStats->variableCount++;
10181                     break;
10182                 case HNDTYPE_SIZEDREF:
10183                     type = "SizedRef";
10184                     if (pStats) pStats->sizedRefCount++;
10185                     break;
10186                 case HNDTYPE_DEPENDENT:
10187                     type = "Dependent";
10188                     if (pStats) pStats->dependentCount++;
10189                     break;
10190                 case HNDTYPE_WEAK_WINRT:
10191                     type = "WeakWinRT";
10192                     if (pStats) pStats->weakWinRTHandleCount++;
10193                     break;
10194                 default:
10195                     DebugBreak();
10196                     type = "Unknown";
10197                     pStats->unknownHandleCount++;
10198                     break;
10199             }
10200             
10201             if (type && !mStat)
10202             {
10203                 sos::MethodTable mt = mtAddr;
10204                 if (mtName == 0)
10205                     mtName = mt.GetName();
10206                 
10207                 if (data[i].Type == HNDTYPE_REFCOUNTED)
10208                     mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Decimal(data[i].RefCount), mtName);
10209                 else if (data[i].Type == HNDTYPE_DEPENDENT)
10210                     mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), ObjectPtr(data[i].Secondary), mtName);
10211                 else if (data[i].Type == HNDTYPE_WEAK_WINRT)
10212                     mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), Pointer(data[i].Secondary), mtName);
10213                 else
10214                     mOut.WriteRow(data[i].Handle, type, ObjectPtr(objAddr), Decimal(size), "", mtName);
10215             }
10216         }
10217     }
10218     
10219     inline void PrintHandleRow(const char *text, int count)
10220     {
10221         if (count)
10222             mOut.WriteRow(text, Decimal(count));
10223     }
10224     
10225     void PrintGCHandleStats(GCHandleStatistics *pStats)
10226     {
10227         Print("Handles:\n");
10228         mOut.ReInit(2, 21, AlignLeft, 4);
10229         
10230         PrintHandleRow("Strong Handles:", pStats->strongHandleCount);
10231         PrintHandleRow("Pinned Handles:", pStats->pinnedHandleCount);
10232         PrintHandleRow("Async Pinned Handles:", pStats->asyncPinnedHandleCount);
10233         PrintHandleRow("Ref Count Handles:", pStats->refCntHandleCount);
10234         PrintHandleRow("Weak Long Handles:", pStats->weakLongHandleCount);
10235         PrintHandleRow("Weak Short Handles:", pStats->weakShortHandleCount);
10236         PrintHandleRow("Weak WinRT Handles:", pStats->weakWinRTHandleCount);
10237         PrintHandleRow("Variable Handles:", pStats->variableCount);
10238         PrintHandleRow("SizedRef Handles:", pStats->sizedRefCount);
10239         PrintHandleRow("Dependent Handles:", pStats->dependentCount);
10240         PrintHandleRow("Other Handles:", pStats->unknownHandleCount);
10241     }
10242     
10243 private:
10244     BOOL mPerDomain, mStat, mDML;
10245     unsigned int mType;
10246     TableOutput mOut;
10247     GCHandleStatsForDomains mHandleStat;
10248 };
10249
10250 /**********************************************************************\
10251 * Routine Description:                                                 *
10252 *                                                                      *
10253 *    This function dumps GC Handle statistics        *
10254 *                                                                      *
10255 \**********************************************************************/
10256 DECLARE_API(GCHandles)
10257 {
10258     INIT_API();
10259     MINIDUMP_NOT_SUPPORTED();    
10260     
10261     try
10262     {
10263         GCHandlesImpl gchandles(args);
10264         gchandles.Run();
10265     }
10266     catch(const sos::Exception &e)
10267     {
10268         Print(e.what());
10269     }
10270
10271     return Status;
10272 }
10273
10274 BOOL derivedFrom(CLRDATA_ADDRESS mtObj, __in_z LPWSTR baseString)
10275 {
10276     // We want to follow back until we get the mt for System.Exception
10277     DacpMethodTableData dmtd;
10278     CLRDATA_ADDRESS walkMT = mtObj;
10279     while(walkMT != NULL)
10280     {
10281         if (dmtd.Request(g_sos, walkMT) != S_OK)
10282         {
10283             break;            
10284         }
10285         NameForMT_s (TO_TADDR(walkMT), g_mdName, mdNameLen);                
10286         if (_wcscmp (baseString, g_mdName) == 0)
10287         {
10288             return TRUE;
10289         }
10290         walkMT = dmtd.ParentMethodTable;
10291     }
10292     return FALSE;
10293 }
10294
10295 // This is an experimental and undocumented SOS API that attempts to step through code
10296 // stopping once jitted code is reached. It currently has some issues - it can take arbitrarily long
10297 // to reach jitted code and canceling it is terrible. At best it doesn't cancel, at worst it
10298 // kills the debugger. IsInterrupt() doesn't work nearly as nicely as one would hope :/
10299 #ifndef FEATURE_PAL
10300 DECLARE_API(TraceToCode)
10301 {
10302     INIT_API_NOEE();
10303
10304     static ULONG64 g_clrBaseAddr = 0;
10305
10306
10307     while(true)
10308     {
10309         if (IsInterrupt())
10310         {
10311             ExtOut("Interrupted\n");
10312             return S_FALSE;
10313         }
10314
10315         ULONG64 Offset;
10316         g_ExtRegisters->GetInstructionOffset(&Offset);
10317
10318         DWORD codeType = 0;
10319         ULONG64 base = 0;
10320         CLRDATA_ADDRESS cdaStart = TO_CDADDR(Offset);
10321         DacpMethodDescData MethodDescData;
10322         if(g_ExtSymbols->GetModuleByOffset(Offset, 0, NULL, &base) == S_OK)
10323         {
10324             if(g_clrBaseAddr == 0)
10325             {
10326                 g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL,
10327                     &g_clrBaseAddr);
10328             }
10329             if(g_clrBaseAddr == base)
10330             {
10331                 ExtOut("Compiled code in CLR\n");
10332                 codeType = 4;
10333             }
10334             else
10335             {
10336                 ExtOut("Compiled code in module @ 0x%I64x\n", base);
10337                 codeType = 8;
10338             }
10339         }
10340         else if (g_sos != NULL || LoadClrDebugDll()==S_OK)
10341         {
10342             CLRDATA_ADDRESS addr;
10343             if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK)
10344             {
10345                 WCHAR wszNameBuffer[1024]; // should be large enough
10346
10347                 // get the MethodDesc name
10348                 if ((g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK) &&
10349                     _wcsncmp(W("DomainBoundILStubClass"), wszNameBuffer, 22)==0)
10350                 {
10351                     ExtOut("ILStub\n");
10352                     codeType = 2;
10353                 }
10354                 else
10355                 {
10356                     ExtOut("Jitted code\n");
10357                     codeType = 1;
10358                 }
10359             }
10360             else
10361             {
10362                 ExtOut("Not compiled or jitted, assuming stub\n");
10363                 codeType = 16;
10364             }
10365         }
10366         else
10367         {
10368             // not compiled but CLR isn't loaded... some other code generator?
10369             return E_FAIL;
10370         }
10371
10372         if(codeType == 1)
10373         {
10374             return S_OK;
10375         }
10376         else
10377         {
10378             Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "thr; .echo wait" ,0);
10379             if (FAILED(Status))
10380             {
10381                 ExtOut("Error tracing instruction\n");
10382                 return Status;
10383             }
10384         }
10385     }
10386
10387     return Status;
10388
10389 }
10390 #endif // FEATURE_PAL
10391
10392 // This is an experimental and undocumented API that sets a debugger pseudo-register based
10393 // on the type of code at the given IP. It can be used in scripts to keep stepping until certain
10394 // kinds of code have been reached. Presumbably its slower than !TraceToCode but at least it
10395 // cancels much better
10396 #ifndef FEATURE_PAL
10397 DECLARE_API(GetCodeTypeFlags)
10398 {
10399     INIT_API();   
10400     
10401
10402     char buffer[100+mdNameLen];
10403     size_t ip;
10404     StringHolder PReg;
10405     
10406     CMDValue arg[] = {
10407         // vptr, type
10408         {&ip, COSIZE_T},
10409         {&PReg.data, COSTRING}
10410     };
10411     size_t nArg;
10412     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) 
10413     {
10414         return Status;
10415     }
10416
10417     size_t preg = 1; // by default
10418     if (nArg == 2)
10419     {
10420         preg = GetExpression(PReg.data);
10421         if (preg > 19)
10422         {
10423             ExtOut("Pseudo-register number must be between 0 and 19\n");
10424             return Status;
10425         }
10426     }        
10427
10428     sprintf_s(buffer,_countof (buffer),
10429         "r$t%d=0",
10430         preg);
10431     Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer ,0);
10432     if (FAILED(Status))
10433     {
10434         ExtOut("Error initialized register $t%d to zero\n", preg);
10435         return Status;
10436     }    
10437
10438     ULONG64 base = 0;
10439     CLRDATA_ADDRESS cdaStart = TO_CDADDR(ip);
10440     DWORD codeType = 0;
10441     CLRDATA_ADDRESS addr;
10442     if(g_sos->GetMethodDescPtrFromIP(cdaStart, &addr) == S_OK)
10443     {
10444         WCHAR wszNameBuffer[1024]; // should be large enough
10445
10446         // get the MethodDesc name
10447         if (g_sos->GetMethodDescName(addr, 1024, wszNameBuffer, NULL) == S_OK &&
10448             _wcsncmp(W("DomainBoundILStubClass"), wszNameBuffer, 22)==0)
10449         {
10450             ExtOut("ILStub\n");
10451             codeType = 2;
10452         }
10453         else
10454         {
10455             ExtOut("Jitted code");
10456             codeType = 1;
10457         }
10458     }
10459     else if(g_ExtSymbols->GetModuleByOffset (ip, 0, NULL, &base) == S_OK)
10460     {
10461         ULONG64 clrBaseAddr = 0;
10462         if(SUCCEEDED(g_ExtSymbols->GetModuleByModuleName (MAIN_CLR_MODULE_NAME_A,0,NULL, &clrBaseAddr)) && base==clrBaseAddr)
10463         {
10464             ExtOut("Compiled code in CLR");
10465             codeType = 4;
10466         }
10467         else
10468         {
10469             ExtOut("Compiled code in module @ 0x%I64x\n", base);
10470             codeType = 8;
10471         }
10472     }
10473     else
10474     {
10475         ExtOut("Not compiled or jitted, assuming stub\n");
10476         codeType = 16;
10477     }
10478
10479     sprintf_s(buffer,_countof (buffer),
10480         "r$t%d=%x",
10481         preg, codeType);
10482     Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
10483     if (FAILED(Status))
10484     {
10485         ExtOut("Error setting register $t%d\n", preg);
10486         return Status;
10487     }  
10488     return Status;
10489
10490 }
10491 #endif // FEATURE_PAL
10492
10493 DECLARE_API(StopOnException)
10494 {
10495     INIT_API();
10496     MINIDUMP_NOT_SUPPORTED();    
10497     
10498
10499     char buffer[100+mdNameLen];
10500
10501     BOOL fDerived = FALSE;
10502     BOOL fCreate1 = FALSE;    
10503     BOOL fCreate2 = FALSE;    
10504
10505     CMDOption option[] = {
10506         // name, vptr, type, hasValue
10507         {"-derived", &fDerived, COBOOL, FALSE}, // catch derived exceptions
10508         {"-create", &fCreate1, COBOOL, FALSE}, // create 1st chance handler
10509         {"-create2", &fCreate2, COBOOL, FALSE}, // create 2nd chance handler
10510     };
10511
10512     StringHolder TypeName,PReg;
10513     
10514     CMDValue arg[] = {
10515         // vptr, type
10516         {&TypeName.data, COSTRING},
10517         {&PReg.data, COSTRING}
10518     };
10519     size_t nArg;
10520     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
10521     {
10522         return Status;
10523     }
10524     if (IsDumpFile())
10525     {
10526         ExtOut("Live debugging session required\n");
10527         return Status;
10528     }
10529     if (nArg < 1 || nArg > 2)
10530     {
10531         ExtOut("usage: StopOnException [-derived] [-create | -create2] <type name>\n");
10532         ExtOut("                       [<pseudo-register number for result>]\n");            
10533         ExtOut("ex:    StopOnException -create System.OutOfMemoryException 1\n");
10534         return Status;
10535     }
10536
10537     size_t preg = 1; // by default
10538     if (nArg == 2)
10539     {
10540         preg = GetExpression(PReg.data);
10541         if (preg > 19)
10542         {
10543             ExtOut("Pseudo-register number must be between 0 and 19\n");
10544             return Status;
10545         }
10546     }        
10547
10548     sprintf_s(buffer,_countof (buffer),
10549         "r$t%d=0",
10550         preg);
10551     Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
10552     if (FAILED(Status))
10553     {
10554         ExtOut("Error initialized register $t%d to zero\n", preg);
10555         return Status;
10556     }    
10557     
10558     if (fCreate1 || fCreate2)
10559     {            
10560         sprintf_s(buffer,_countof (buffer),
10561             "sxe %s \"!soe %s %s %d;.if(@$t%d==0) {g} .else {.echo '%s hit'}\" %x",
10562             fCreate1 ? "-c" : "-c2",
10563             fDerived ? "-derived" : "",
10564             TypeName.data,
10565             preg,
10566             preg,
10567             TypeName.data,
10568             EXCEPTION_COMPLUS
10569             );
10570             
10571         Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);        
10572         if (FAILED(Status))
10573         {
10574             ExtOut("Error setting breakpoint: %s\n", buffer);
10575             return Status;
10576         }        
10577
10578         ExtOut("Breakpoint set\n");
10579         return Status;
10580     }    
10581
10582     // Find the last thrown exception on this thread.
10583     // Does it match? If so, set the register.
10584     CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
10585     DacpThreadData Thread;
10586     
10587     if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
10588     {
10589         ExtOut("The current thread is unmanaged\n");
10590         return Status;
10591     }
10592
10593     TADDR taLTOH;
10594     if (!SafeReadMemory(Thread.lastThrownObjectHandle,
10595                         &taLTOH,
10596                         sizeof(taLTOH), NULL))
10597     {
10598         ExtOut("There is no current managed exception on this thread\n");
10599         return Status;
10600     }
10601     
10602     if (taLTOH)
10603     {
10604         LPWSTR typeNameWide = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
10605         MultiByteToWideChar(CP_ACP,0,TypeName.data,-1,typeNameWide,mdNameLen);
10606         
10607         TADDR taMT;
10608         if (SafeReadMemory(taLTOH, &taMT, sizeof(taMT), NULL))
10609         {            
10610             NameForMT_s (taMT, g_mdName, mdNameLen);
10611             if ((_wcscmp(g_mdName,typeNameWide) == 0) ||
10612                 (fDerived && derivedFrom(taMT, typeNameWide)))
10613             {
10614                 sprintf_s(buffer,_countof (buffer),
10615                     "r$t%d=1",
10616                     preg);
10617                 Status = g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, buffer, 0);
10618                 if (FAILED(Status))
10619                 {
10620                     ExtOut("Failed to execute the following command: %s\n", buffer);
10621                 }
10622             }
10623         }
10624     }
10625
10626     return Status;
10627 }
10628
10629 /**********************************************************************\
10630 * Routine Description:                                                 *
10631 *                                                                      *
10632 *    This function finds the size of an object or all roots.           *  
10633 *                                                                      *
10634 \**********************************************************************/
10635 DECLARE_API(ObjSize)
10636 {
10637 #ifndef FEATURE_PAL
10638     INIT_API();
10639     MINIDUMP_NOT_SUPPORTED();    
10640     
10641     BOOL dml = FALSE;
10642     StringHolder str_Object;    
10643
10644
10645     CMDOption option[] = 
10646     {   // name, vptr, type, hasValue
10647         {"/d", &dml, COBOOL, FALSE},
10648     };
10649     CMDValue arg[] = 
10650     {   // vptr, type
10651         {&str_Object.data, COSTRING}
10652     };
10653     size_t nArg;
10654     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg)) 
10655     {
10656         return Status;
10657     }
10658
10659     EnableDMLHolder dmlHolder(dml);
10660     TADDR obj = GetExpression(str_Object.data);
10661
10662     GCRootImpl gcroot;
10663     if (obj == 0)
10664     {
10665         gcroot.ObjSize();
10666     }
10667     else
10668     {
10669         if(!sos::IsObject(obj))
10670         {
10671             ExtOut("%p is not a valid object.\n", SOS_PTR(obj));
10672             return Status;
10673         }
10674
10675         size_t size = gcroot.ObjSize(obj);
10676         TADDR mt = 0;
10677         MOVE(mt, obj);
10678         sos::MethodTable methodTable = mt;
10679         ExtOut("sizeof(%p) = %d (0x%x) bytes (%S)\n", SOS_PTR(obj), size, size, methodTable.GetName());
10680     }
10681     return Status;
10682 #else
10683     return E_NOTIMPL;
10684 #endif
10685
10686 }
10687
10688 #ifndef FEATURE_PAL
10689 // For FEATURE_PAL, MEMORY_BASIC_INFORMATION64 doesn't exist yet. TODO?
10690 DECLARE_API(GCHandleLeaks)
10691 {
10692     INIT_API();
10693     MINIDUMP_NOT_SUPPORTED();    
10694
10695     ExtOut("-------------------------------------------------------------------------------\n");
10696     ExtOut("GCHandleLeaks will report any GCHandles that couldn't be found in memory.      \n");
10697     ExtOut("Strong and Pinned GCHandles are reported at this time. You can safely abort the\n");
10698     ExtOut("memory scan with Control-C or Control-Break.                                   \n");
10699     ExtOut("-------------------------------------------------------------------------------\n");
10700     
10701     static DWORD_PTR array[2000];
10702     UINT i;
10703     BOOL dml = FALSE;
10704
10705     CMDOption option[] = 
10706     {   // name, vptr, type, hasValue
10707         {"/d", &dml, COBOOL, FALSE},
10708     };
10709
10710     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL)) 
10711     {
10712         return Status;
10713     }
10714
10715     EnableDMLHolder dmlHolder(dml);
10716     
10717     UINT iFinal = FindAllPinnedAndStrong(array,sizeof(array)/sizeof(DWORD_PTR));
10718     ExtOut("Found %d handles:\n",iFinal);
10719     for (i=1;i<=iFinal;i++)
10720     {
10721         ExtOut("%p\t", SOS_PTR(array[i-1]));
10722         if ((i % 4) == 0)
10723             ExtOut("\n");
10724     }
10725
10726     ExtOut("\nSearching memory\n");
10727     // Now search memory for this:
10728     DWORD_PTR buffer[1024];
10729     ULONG64 memCur = 0x0;
10730     BOOL bAbort = FALSE;
10731
10732     //find out memory used by stress log
10733     StressLogMem stressLog;
10734     CLRDATA_ADDRESS StressLogAddress = NULL;
10735     if (LoadClrDebugDll() != S_OK)
10736     {
10737         // Try to find stress log symbols
10738         DWORD_PTR dwAddr = GetValueFromExpression(MAIN_CLR_MODULE_NAME_A "!StressLog::theLog");
10739         StressLogAddress = dwAddr;        
10740         g_bDacBroken = TRUE;
10741     }
10742     else
10743     {
10744         if (g_sos->GetStressLogAddress(&StressLogAddress) != S_OK)
10745         {
10746             ExtOut("Unable to find stress log via DAC\n");
10747         }
10748         g_bDacBroken = FALSE;
10749     }
10750
10751     if (stressLog.Init (StressLogAddress, g_ExtData))
10752     {
10753         ExtOut("Reference found in stress log will be ignored\n");
10754     }
10755     else
10756     {
10757         ExtOut("Failed to read whole or part of stress log, some references may come from stress log\n");
10758     }
10759     
10760     
10761     while (!bAbort)
10762     {
10763         NTSTATUS status;
10764         MEMORY_BASIC_INFORMATION64 memInfo;
10765
10766         status = g_ExtData2->QueryVirtual(UL64_TO_CDA(memCur), &memInfo);
10767                 
10768         if( !NT_SUCCESS(status) ) 
10769         {            
10770             break;
10771         }
10772
10773         if (memInfo.State == MEM_COMMIT)
10774         {            
10775             for (ULONG64 memIter = memCur; memIter < (memCur + memInfo.RegionSize); memIter+=sizeof(buffer))
10776             {
10777                 if (IsInterrupt())
10778                 {
10779                     ExtOut("Quitting at %p due to user abort\n", SOS_PTR(memIter));
10780                     bAbort = TRUE;
10781                     break;
10782                 }
10783
10784                 if ((memIter % 0x10000000)==0x0)
10785                 {
10786                     ExtOut("Searching %p...\n", SOS_PTR(memIter));
10787                 }
10788                 
10789                 ULONG size = 0;
10790                 HRESULT ret;
10791                 ret = g_ExtData->ReadVirtual(UL64_TO_CDA(memIter), buffer, sizeof(buffer), &size);
10792                 if (ret == S_OK)
10793                 {
10794                     for (UINT x=0;x<1024;x++)
10795                     {            
10796                         DWORD_PTR value = buffer[x];
10797                         // We don't care about the low bit. Also, the GCHandle class turns on the
10798                         // low bit for pinned handles, so without the statement below, we wouldn't
10799                         // notice pinned handles.
10800                         value = value & ~1; 
10801                         for (i=0;i<iFinal;i++)
10802                         {
10803                             ULONG64 addrInDebugee = (ULONG64)memIter+(x*sizeof(DWORD_PTR));
10804                             if ((array[i] & ~1) == value)
10805                             {
10806                                 if (stressLog.IsInStressLog (addrInDebugee))
10807                                 {
10808                                     ExtOut("Found %p in stress log at location %p, reference not counted\n", SOS_PTR(value), addrInDebugee);
10809                                 }
10810                                 else
10811                                 {
10812                                     ExtOut("Found %p at location %p\n", SOS_PTR(value), addrInDebugee);
10813                                     array[i] |= 0x1;
10814                                 }
10815                             }
10816                         }
10817                     }
10818                 }
10819                 else
10820                 {
10821                     if (size > 0)
10822                     {
10823                         ExtOut("only read %x bytes at %p\n", size, SOS_PTR(memIter));
10824                     }
10825                 }
10826             }
10827         }
10828         
10829         memCur += memInfo.RegionSize;
10830     }
10831
10832     int numNotFound = 0;
10833     for (i=0;i<iFinal;i++)
10834     {
10835         if ((array[i] & 0x1) == 0)
10836         {
10837             numNotFound++;
10838             // ExtOut("WARNING: %p not found\n", SOS_PTR(array[i]));
10839         }
10840     }
10841
10842     if (numNotFound > 0)
10843     {
10844         ExtOut("------------------------------------------------------------------------------\n");    
10845         ExtOut("Some handles were not found. If the number of not-found handles grows over the\n");
10846         ExtOut("lifetime of your application, you may have a GCHandle leak. This will cause   \n");
10847         ExtOut("the GC Heap to grow larger as objects are being kept alive, referenced only   \n");
10848         ExtOut("by the orphaned handle. If the number doesn't grow over time, note that there \n");
10849         ExtOut("may be some noise in this output, as an unmanaged application may be storing  \n");
10850         ExtOut("the handle in a non-standard way, perhaps with some bits flipped. The memory  \n");
10851         ExtOut("scan wouldn't be able to find those.                                          \n");
10852         ExtOut("------------------------------------------------------------------------------\n");    
10853
10854         ExtOut("Didn't find %d handles:\n", numNotFound);
10855         int numPrinted=0;
10856         for (i=0;i<iFinal;i++)
10857         {
10858             if ((array[i] & 0x1) == 0)
10859             {
10860                 numPrinted++;
10861                 ExtOut("%p\t", SOS_PTR(array[i]));
10862                 if ((numPrinted % 4) == 0)
10863                     ExtOut("\n");
10864             }
10865         }   
10866         ExtOut("\n");
10867     }
10868     else
10869     {       
10870         ExtOut("------------------------------------------------------------------------------\n");    
10871         ExtOut("All handles found");
10872         if (bAbort)
10873             ExtOut(" even though you aborted.\n");
10874         else
10875             ExtOut(".\n");        
10876         ExtOut("A leak may still exist because in a general scan of process memory SOS can't  \n");
10877         ExtOut("differentiate between garbage and valid structures, so you may have false     \n");
10878         ExtOut("positives. If you still suspect a leak, use this function over time to        \n");
10879         ExtOut("identify a possible trend.                                                    \n");
10880         ExtOut("------------------------------------------------------------------------------\n");    
10881     }
10882     
10883     return Status;
10884 }
10885 #endif // FEATURE_PAL
10886
10887 #endif // FEATURE_PAL
10888
10889 class ClrStackImplWithICorDebug
10890 {
10891 private:
10892     static HRESULT DereferenceAndUnboxValue(ICorDebugValue * pValue, ICorDebugValue** ppOutputValue, BOOL * pIsNull = NULL)
10893     {
10894         HRESULT Status = S_OK;
10895         *ppOutputValue = NULL;
10896         if(pIsNull != NULL) *pIsNull = FALSE;
10897
10898         ToRelease<ICorDebugReferenceValue> pReferenceValue;
10899         Status = pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue);
10900         if (SUCCEEDED(Status))
10901         {
10902             BOOL isNull = FALSE;
10903             IfFailRet(pReferenceValue->IsNull(&isNull));
10904             if(!isNull)
10905             {
10906                 ToRelease<ICorDebugValue> pDereferencedValue;
10907                 IfFailRet(pReferenceValue->Dereference(&pDereferencedValue));
10908                 return DereferenceAndUnboxValue(pDereferencedValue, ppOutputValue);
10909             }
10910             else
10911             {
10912                 if(pIsNull != NULL) *pIsNull = TRUE;
10913                 *ppOutputValue = pValue;
10914                 (*ppOutputValue)->AddRef();
10915                 return S_OK;
10916             }
10917         }
10918
10919         ToRelease<ICorDebugBoxValue> pBoxedValue;
10920         Status = pValue->QueryInterface(IID_ICorDebugBoxValue, (LPVOID*) &pBoxedValue);
10921         if (SUCCEEDED(Status))
10922         {
10923             ToRelease<ICorDebugObjectValue> pUnboxedValue;
10924             IfFailRet(pBoxedValue->GetObject(&pUnboxedValue));
10925             return DereferenceAndUnboxValue(pUnboxedValue, ppOutputValue);
10926         }
10927         *ppOutputValue = pValue;
10928         (*ppOutputValue)->AddRef();
10929         return S_OK;
10930     }
10931
10932     static BOOL ShouldExpandVariable(__in_z WCHAR* varToExpand, __in_z WCHAR* currentExpansion)
10933     {
10934         if(currentExpansion == NULL || varToExpand == NULL) return FALSE;
10935
10936         size_t varToExpandLen = _wcslen(varToExpand);
10937         size_t currentExpansionLen = _wcslen(currentExpansion);
10938         if(currentExpansionLen > varToExpandLen) return FALSE;
10939         if(currentExpansionLen < varToExpandLen && varToExpand[currentExpansionLen] != L'.') return FALSE;
10940         if(_wcsncmp(currentExpansion, varToExpand, currentExpansionLen) != 0) return FALSE;
10941
10942         return TRUE;
10943     }
10944
10945     static BOOL IsEnum(ICorDebugValue * pInputValue)
10946     {
10947         ToRelease<ICorDebugValue> pValue;
10948         if(FAILED(DereferenceAndUnboxValue(pInputValue, &pValue, NULL))) return FALSE;
10949
10950         WCHAR baseTypeName[mdNameLen];
10951         ToRelease<ICorDebugValue2> pValue2;
10952         ToRelease<ICorDebugType> pType;
10953         ToRelease<ICorDebugType> pBaseType;
10954
10955         if(FAILED(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2))) return FALSE;
10956         if(FAILED(pValue2->GetExactType(&pType))) return FALSE;
10957         if(FAILED(pType->GetBase(&pBaseType)) || pBaseType == NULL) return FALSE;
10958         if(FAILED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen))) return  FALSE;
10959
10960         return (_wcsncmp(baseTypeName, W("System.Enum"), 11) == 0);
10961     }
10962
10963     static HRESULT AddGenericArgs(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
10964     {
10965         bool isFirst = true;
10966         ToRelease<ICorDebugTypeEnum> pTypeEnum;
10967         if(SUCCEEDED(pType->EnumerateTypeParameters(&pTypeEnum)))
10968         {
10969             ULONG numTypes = 0;
10970             ToRelease<ICorDebugType> pCurrentTypeParam;
10971             
10972             while(SUCCEEDED(pTypeEnum->Next(1, &pCurrentTypeParam, &numTypes)))
10973             {
10974                 if(numTypes == 0) break;
10975
10976                 if(isFirst)
10977                 {
10978                     isFirst = false;
10979                     wcsncat_s(typeName, typeNameLen, W("&lt;"), typeNameLen);
10980                 }
10981                 else wcsncat_s(typeName, typeNameLen, W(","), typeNameLen);
10982
10983                 WCHAR typeParamName[mdNameLen];
10984                 typeParamName[0] = L'\0';
10985                 GetTypeOfValue(pCurrentTypeParam, typeParamName, mdNameLen);
10986                 wcsncat_s(typeName, typeNameLen, typeParamName, typeNameLen);
10987             }
10988             if(!isFirst)
10989                 wcsncat_s(typeName, typeNameLen, W("&gt;"), typeNameLen);
10990         }
10991
10992         return S_OK;
10993     }
10994
10995     static HRESULT GetTypeOfValue(ICorDebugType * pType, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
10996     {
10997         HRESULT Status = S_OK;
10998
10999         CorElementType corElemType;
11000         IfFailRet(pType->GetType(&corElemType));
11001
11002         switch (corElemType)
11003         {
11004         //List of unsupported CorElementTypes:
11005         //ELEMENT_TYPE_END            = 0x0,
11006         //ELEMENT_TYPE_VAR            = 0x13,     // a class type variable VAR <U1>
11007         //ELEMENT_TYPE_GENERICINST    = 0x15,     // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
11008         //ELEMENT_TYPE_TYPEDBYREF     = 0x16,     // TYPEDREF  (it takes no args) a typed referece to some other type
11009         //ELEMENT_TYPE_MVAR           = 0x1e,     // a method type variable MVAR <U1>
11010         //ELEMENT_TYPE_CMOD_REQD      = 0x1F,     // required C modifier : E_T_CMOD_REQD <mdTypeRef/mdTypeDef>
11011         //ELEMENT_TYPE_CMOD_OPT       = 0x20,     // optional C modifier : E_T_CMOD_OPT <mdTypeRef/mdTypeDef>
11012         //ELEMENT_TYPE_INTERNAL       = 0x21,     // INTERNAL <typehandle>
11013         //ELEMENT_TYPE_MAX            = 0x22,     // first invalid element type
11014         //ELEMENT_TYPE_MODIFIER       = 0x40,
11015         //ELEMENT_TYPE_SENTINEL       = 0x01 | ELEMENT_TYPE_MODIFIER, // sentinel for varargs
11016         //ELEMENT_TYPE_PINNED         = 0x05 | ELEMENT_TYPE_MODIFIER,
11017         //ELEMENT_TYPE_R4_HFA         = 0x06 | ELEMENT_TYPE_MODIFIER, // used only internally for R4 HFA types
11018         //ELEMENT_TYPE_R8_HFA         = 0x07 | ELEMENT_TYPE_MODIFIER, // used only internally for R8 HFA types
11019         default:
11020             swprintf_s(typeName, typeNameLen, W("(Unhandled CorElementType: 0x%x)\0"), corElemType);
11021             break;
11022
11023         case ELEMENT_TYPE_VALUETYPE:
11024         case ELEMENT_TYPE_CLASS:
11025             {
11026                 //Defaults in case we fail...
11027                 if(corElemType == ELEMENT_TYPE_VALUETYPE) swprintf_s(typeName, typeNameLen, W("struct\0"));
11028                 else swprintf_s(typeName, typeNameLen, W("class\0"));
11029
11030                 mdTypeDef typeDef;
11031                 ToRelease<ICorDebugClass> pClass;
11032                 if(SUCCEEDED(pType->GetClass(&pClass)) && SUCCEEDED(pClass->GetToken(&typeDef)))
11033                 {
11034                     ToRelease<ICorDebugModule> pModule;
11035                     IfFailRet(pClass->GetModule(&pModule));
11036
11037                     ToRelease<IUnknown> pMDUnknown;
11038                     ToRelease<IMetaDataImport> pMD;
11039                     IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
11040                     IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
11041
11042                     if(SUCCEEDED(NameForToken_s(TokenFromRid(typeDef, mdtTypeDef), pMD, g_mdName, mdNameLen, false)))
11043                         swprintf_s(typeName, typeNameLen, W("%s\0"), g_mdName);
11044                 }
11045                 AddGenericArgs(pType, typeName, typeNameLen);
11046             }
11047             break;
11048         case ELEMENT_TYPE_VOID:
11049             swprintf_s(typeName, typeNameLen, W("void\0"));
11050             break;
11051         case ELEMENT_TYPE_BOOLEAN:
11052             swprintf_s(typeName, typeNameLen, W("bool\0"));
11053             break;
11054         case ELEMENT_TYPE_CHAR:
11055             swprintf_s(typeName, typeNameLen, W("char\0"));
11056             break;
11057         case ELEMENT_TYPE_I1:
11058             swprintf_s(typeName, typeNameLen, W("signed byte\0"));
11059             break;
11060         case ELEMENT_TYPE_U1:
11061             swprintf_s(typeName, typeNameLen, W("byte\0"));
11062             break;
11063         case ELEMENT_TYPE_I2:
11064             swprintf_s(typeName, typeNameLen, W("short\0"));
11065             break;
11066         case ELEMENT_TYPE_U2:
11067             swprintf_s(typeName, typeNameLen, W("unsigned short\0"));
11068             break;    
11069         case ELEMENT_TYPE_I4:
11070             swprintf_s(typeName, typeNameLen, W("int\0"));
11071             break;
11072         case ELEMENT_TYPE_U4:
11073             swprintf_s(typeName, typeNameLen, W("unsigned int\0"));
11074             break;
11075         case ELEMENT_TYPE_I8:
11076             swprintf_s(typeName, typeNameLen, W("long\0"));
11077             break;
11078         case ELEMENT_TYPE_U8:
11079             swprintf_s(typeName, typeNameLen, W("unsigned long\0"));
11080             break;
11081         case ELEMENT_TYPE_R4:
11082             swprintf_s(typeName, typeNameLen, W("float\0"));
11083             break;
11084         case ELEMENT_TYPE_R8:
11085             swprintf_s(typeName, typeNameLen, W("double\0"));
11086             break;
11087         case ELEMENT_TYPE_OBJECT:
11088             swprintf_s(typeName, typeNameLen, W("object\0"));
11089             break;
11090         case ELEMENT_TYPE_STRING:
11091             swprintf_s(typeName, typeNameLen, W("string\0"));
11092             break;
11093         case ELEMENT_TYPE_I:
11094             swprintf_s(typeName, typeNameLen, W("IntPtr\0"));
11095             break;
11096         case ELEMENT_TYPE_U:
11097             swprintf_s(typeName, typeNameLen, W("UIntPtr\0"));
11098             break;
11099         case ELEMENT_TYPE_SZARRAY:
11100         case ELEMENT_TYPE_ARRAY:
11101         case ELEMENT_TYPE_BYREF:
11102         case ELEMENT_TYPE_PTR:
11103             {
11104                 ToRelease<ICorDebugType> pFirstParameter;
11105                 if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)))
11106                     GetTypeOfValue(pFirstParameter, typeName, typeNameLen);
11107                 else
11108                     swprintf_s(typeName, typeNameLen, W("<unknown>\0"));
11109
11110                 switch(corElemType)
11111                 {
11112                 case ELEMENT_TYPE_SZARRAY: 
11113                     wcsncat_s(typeName, typeNameLen, W("[]\0"), typeNameLen);
11114                     return S_OK;
11115                 case ELEMENT_TYPE_ARRAY:
11116                     {
11117                         ULONG32 rank = 0;
11118                         pType->GetRank(&rank);
11119                         wcsncat_s(typeName, typeNameLen, W("["), typeNameLen);
11120                         for(ULONG32 i = 0; i < rank - 1; i++)
11121                         {
11122                             // 
11123                             wcsncat_s(typeName, typeNameLen, W(","), typeNameLen);
11124                         }
11125                         wcsncat_s(typeName, typeNameLen, W("]\0"), typeNameLen);
11126                     }
11127                     return S_OK;
11128                 case ELEMENT_TYPE_BYREF:   
11129                     wcsncat_s(typeName, typeNameLen, W("&\0"), typeNameLen);
11130                     return S_OK;
11131                 case ELEMENT_TYPE_PTR:     
11132                     wcsncat_s(typeName, typeNameLen, W("*\0"), typeNameLen);
11133                     return S_OK;
11134                 default:
11135                     // note we can never reach here as this is a nested switch
11136                     // and corElemType can only be one of the values above
11137                     break;
11138                 }
11139             }
11140             break;
11141         case ELEMENT_TYPE_FNPTR:
11142             swprintf_s(typeName, typeNameLen, W("*(...)\0"));
11143             break;
11144         case ELEMENT_TYPE_TYPEDBYREF:
11145             swprintf_s(typeName, typeNameLen, W("typedbyref\0"));
11146             break;
11147         }
11148         return S_OK;
11149     }
11150
11151     static HRESULT GetTypeOfValue(ICorDebugValue * pValue, __inout_ecount(typeNameLen) WCHAR* typeName, ULONG typeNameLen)
11152     {
11153         HRESULT Status = S_OK;
11154
11155         CorElementType corElemType;
11156         IfFailRet(pValue->GetType(&corElemType));
11157
11158         ToRelease<ICorDebugType> pType;
11159         ToRelease<ICorDebugValue2> pValue2;
11160         if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugValue2, (void**) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
11161             return GetTypeOfValue(pType, typeName, typeNameLen);
11162         else
11163             swprintf_s(typeName, typeNameLen, W("<unknown>\0"));
11164
11165         return S_OK;
11166     }
11167
11168     static HRESULT PrintEnumValue(ICorDebugValue* pInputValue, BYTE* enumValue)
11169     {
11170         HRESULT Status = S_OK;
11171
11172         ToRelease<ICorDebugValue> pValue;
11173         IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, NULL));
11174
11175         mdTypeDef currentTypeDef;
11176         ToRelease<ICorDebugClass> pClass;
11177         ToRelease<ICorDebugValue2> pValue2;
11178         ToRelease<ICorDebugType> pType;
11179         ToRelease<ICorDebugModule> pModule;
11180         IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
11181         IfFailRet(pValue2->GetExactType(&pType));
11182         IfFailRet(pType->GetClass(&pClass));
11183         IfFailRet(pClass->GetModule(&pModule));
11184         IfFailRet(pClass->GetToken(&currentTypeDef));
11185
11186         ToRelease<IUnknown> pMDUnknown;
11187         ToRelease<IMetaDataImport> pMD;
11188         IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
11189         IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
11190
11191
11192         //First, we need to figure out the underlying enum type so that we can correctly type cast the raw values of each enum constant
11193         //We get that from the non-static field of the enum variable (I think the field is called __value or something similar)
11194         ULONG numFields = 0;
11195         HCORENUM fEnum = NULL;
11196         mdFieldDef fieldDef;
11197         CorElementType enumUnderlyingType = ELEMENT_TYPE_END;
11198         while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
11199         {
11200             DWORD             fieldAttr = 0;
11201             PCCOR_SIGNATURE   pSignatureBlob = NULL;
11202             ULONG             sigBlobLength = 0;
11203             if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, NULL, 0, NULL, &fieldAttr, &pSignatureBlob, &sigBlobLength, NULL, NULL, NULL)))
11204             {
11205                 if((fieldAttr & fdStatic) == 0)
11206                 {
11207                     CorSigUncompressCallingConv(pSignatureBlob);
11208                     enumUnderlyingType = CorSigUncompressElementType(pSignatureBlob);
11209                     break;
11210                 }
11211             }
11212         }
11213         pMD->CloseEnum(fEnum);
11214
11215
11216         //Now that we know the underlying enum type, let's decode the enum variable into OR-ed, human readable enum contants
11217         fEnum = NULL;
11218         bool isFirst = true;
11219         ULONG64 remainingValue = *((ULONG64*)enumValue);
11220         while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
11221         {
11222             ULONG             nameLen = 0;
11223             DWORD             fieldAttr = 0;
11224             WCHAR             mdName[mdNameLen];
11225             WCHAR             typeName[mdNameLen];
11226             UVCP_CONSTANT     pRawValue = NULL;
11227             ULONG             rawValueLength = 0;
11228             if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, &pRawValue, &rawValueLength)))
11229             {
11230                 DWORD enumValueRequiredAttributes = fdPublic | fdStatic | fdLiteral | fdHasDefault;
11231                 if((fieldAttr & enumValueRequiredAttributes) != enumValueRequiredAttributes)
11232                     continue;
11233
11234                 ULONG64 currentConstValue = 0;
11235                 switch (enumUnderlyingType)
11236                 {
11237                     case ELEMENT_TYPE_CHAR:
11238                     case ELEMENT_TYPE_I1:
11239                         currentConstValue = (ULONG64)(*((CHAR*)pRawValue));
11240                         break;
11241                     case ELEMENT_TYPE_U1:
11242                         currentConstValue = (ULONG64)(*((BYTE*)pRawValue));
11243                         break;
11244                     case ELEMENT_TYPE_I2:
11245                         currentConstValue = (ULONG64)(*((SHORT*)pRawValue));
11246                         break;
11247                     case ELEMENT_TYPE_U2:
11248                         currentConstValue = (ULONG64)(*((USHORT*)pRawValue));
11249                         break;
11250                     case ELEMENT_TYPE_I4:
11251                         currentConstValue = (ULONG64)(*((INT32*)pRawValue));
11252                         break;
11253                     case ELEMENT_TYPE_U4:
11254                         currentConstValue = (ULONG64)(*((UINT32*)pRawValue));
11255                         break;
11256                     case ELEMENT_TYPE_I8:
11257                         currentConstValue = (ULONG64)(*((LONG*)pRawValue));
11258                         break;
11259                     case ELEMENT_TYPE_U8:
11260                         currentConstValue = (ULONG64)(*((ULONG*)pRawValue));
11261                         break;
11262                     case ELEMENT_TYPE_I:
11263                         currentConstValue = (ULONG64)(*((int*)pRawValue));
11264                         break;
11265                     case ELEMENT_TYPE_U:
11266                     case ELEMENT_TYPE_R4:
11267                     case ELEMENT_TYPE_R8:
11268                     // Technically U and the floating-point ones are options in the CLI, but not in the CLS or C#, so these are NYI
11269                     default:
11270                         currentConstValue = 0;
11271                 }
11272
11273                 if((currentConstValue == remainingValue) || ((currentConstValue != 0) && ((currentConstValue & remainingValue) == currentConstValue)))
11274                 {
11275                     remainingValue &= ~currentConstValue;
11276                     if(isFirst)
11277                     {
11278                         ExtOut(" = %S", mdName);
11279                         isFirst = false;
11280                     }
11281                     else ExtOut(" | %S", mdName);
11282                 }
11283             }
11284         }
11285         pMD->CloseEnum(fEnum);
11286
11287         return S_OK;
11288     }
11289
11290     static HRESULT PrintStringValue(ICorDebugValue * pValue)
11291     {
11292         HRESULT Status;
11293
11294         ToRelease<ICorDebugStringValue> pStringValue;
11295         IfFailRet(pValue->QueryInterface(IID_ICorDebugStringValue, (LPVOID*) &pStringValue));
11296
11297         ULONG32 cchValue;
11298         IfFailRet(pStringValue->GetLength(&cchValue));
11299         cchValue++;         // Allocate one more for null terminator
11300
11301         CQuickString quickString;
11302         quickString.Alloc(cchValue);
11303
11304         ULONG32 cchValueReturned;
11305         IfFailRet(pStringValue->GetString(
11306             cchValue,
11307             &cchValueReturned,
11308             quickString.String()));
11309
11310         ExtOut(" = \"%S\"\n", quickString.String());
11311         
11312         return S_OK;
11313     }
11314
11315     static HRESULT PrintSzArrayValue(ICorDebugValue * pValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
11316     {
11317         HRESULT Status = S_OK;
11318
11319         ToRelease<ICorDebugArrayValue> pArrayValue;
11320         IfFailRet(pValue->QueryInterface(IID_ICorDebugArrayValue, (LPVOID*) &pArrayValue));
11321
11322         ULONG32 nRank;
11323         IfFailRet(pArrayValue->GetRank(&nRank));
11324         if (nRank != 1)
11325         {
11326             return E_UNEXPECTED;
11327         }
11328
11329         ULONG32 cElements;
11330         IfFailRet(pArrayValue->GetCount(&cElements));
11331
11332         if (cElements == 0) ExtOut("   (empty)\n");
11333         else if (cElements == 1) ExtOut("   (1 element)\n");
11334         else ExtOut("   (%d elements)\n", cElements);
11335
11336         if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK;
11337         size_t currentExpansionLen = _wcslen(currentExpansion);
11338
11339         for (ULONG32 i=0; i < cElements; i++)
11340         {
11341             for(int j = 0; j <= indent; j++) ExtOut("    ");
11342             currentExpansion[currentExpansionLen] = L'\0';
11343             swprintf_s(currentExpansion, mdNameLen, W("%s.[%d]\0"), currentExpansion, i);
11344
11345             bool printed = false;
11346             CorElementType corElemType;
11347             ToRelease<ICorDebugType> pFirstParameter;
11348             ToRelease<ICorDebugValue2> pValue2;
11349             ToRelease<ICorDebugType> pType;
11350             if(SUCCEEDED(pArrayValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2)) && SUCCEEDED(pValue2->GetExactType(&pType)))
11351             {
11352                 if(SUCCEEDED(pType->GetFirstTypeParameter(&pFirstParameter)) && SUCCEEDED(pFirstParameter->GetType(&corElemType)))
11353                 {
11354                     switch(corElemType)
11355                     {
11356                     //If the array element is something that we can expand with !clrstack, show information about the type of this element
11357                     case ELEMENT_TYPE_VALUETYPE:
11358                     case ELEMENT_TYPE_CLASS:
11359                     case ELEMENT_TYPE_SZARRAY:
11360                         {
11361                             WCHAR typeOfElement[mdNameLen];
11362                             GetTypeOfValue(pFirstParameter, typeOfElement, mdNameLen);
11363                             DMLOut(" |- %s = %S", DMLManagedVar(currentExpansion, currentFrame, i), typeOfElement);
11364                             printed = true;
11365                         }
11366                         break;
11367                     default:
11368                         break;
11369                     }
11370                 }
11371             }
11372             if(!printed) DMLOut(" |- %s", DMLManagedVar(currentExpansion, currentFrame, i));
11373
11374             ToRelease<ICorDebugValue> pElementValue;
11375             IfFailRet(pArrayValue->GetElementAtPosition(i, &pElementValue));
11376             IfFailRet(PrintValue(pElementValue, pILFrame, pMD, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame));
11377         }
11378
11379         return S_OK;
11380     }
11381
11382     static HRESULT PrintValue(ICorDebugValue * pInputValue, ICorDebugILFrame * pILFrame, IMetaDataImport * pMD, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
11383     {
11384         HRESULT Status = S_OK;
11385
11386         BOOL isNull = TRUE;
11387         ToRelease<ICorDebugValue> pValue;
11388         IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull));
11389
11390         if(isNull)
11391         {
11392             ExtOut(" = null\n");
11393             return S_OK;
11394         }
11395
11396         ULONG32 cbSize;
11397         IfFailRet(pValue->GetSize(&cbSize));
11398         ArrayHolder<BYTE> rgbValue = new NOTHROW BYTE[cbSize];
11399         if (rgbValue == NULL)
11400         {
11401             ReportOOM();
11402             return E_OUTOFMEMORY;
11403         }
11404
11405         memset(rgbValue.GetPtr(), 0, cbSize * sizeof(BYTE));
11406
11407         CorElementType corElemType;
11408         IfFailRet(pValue->GetType(&corElemType));
11409         if (corElemType == ELEMENT_TYPE_STRING)
11410         {
11411             return PrintStringValue(pValue);
11412         }
11413
11414         if (corElemType == ELEMENT_TYPE_SZARRAY)
11415         {
11416             return PrintSzArrayValue(pValue, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
11417         }
11418
11419         ToRelease<ICorDebugGenericValue> pGenericValue;
11420         IfFailRet(pValue->QueryInterface(IID_ICorDebugGenericValue, (LPVOID*) &pGenericValue));
11421         IfFailRet(pGenericValue->GetValue((LPVOID) &(rgbValue[0])));
11422
11423         if(IsEnum(pValue))
11424         {
11425             Status = PrintEnumValue(pValue, rgbValue);
11426             ExtOut("\n");
11427             return Status;
11428         }
11429
11430         switch (corElemType)
11431         {
11432         default:
11433             ExtOut("  (Unhandled CorElementType: 0x%x)\n", corElemType);
11434             break;
11435
11436         case ELEMENT_TYPE_PTR:
11437             ExtOut("  = <pointer>\n");
11438             break;
11439
11440         case ELEMENT_TYPE_FNPTR:
11441             {
11442                 CORDB_ADDRESS addr = 0;
11443                 ToRelease<ICorDebugReferenceValue> pReferenceValue = NULL;
11444                 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (LPVOID*) &pReferenceValue)))
11445                     pReferenceValue->GetValue(&addr);
11446                 ExtOut("  = <function pointer 0x%x>\n", addr);
11447             }
11448             break;
11449
11450         case ELEMENT_TYPE_VALUETYPE:
11451         case ELEMENT_TYPE_CLASS:
11452             CORDB_ADDRESS addr;
11453             if(SUCCEEDED(pValue->GetAddress(&addr)))
11454             {
11455                 ExtOut(" @ 0x%I64x\n", addr);
11456             }
11457             else
11458             {
11459                 ExtOut("\n");
11460             }
11461             ProcessFields(pValue, NULL, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
11462             break;
11463
11464         case ELEMENT_TYPE_BOOLEAN:
11465             ExtOut("  = %s\n", rgbValue[0] == 0 ? "false" : "true");
11466             break;
11467
11468         case ELEMENT_TYPE_CHAR:
11469             ExtOut("  = '%C'\n", *(WCHAR *) &(rgbValue[0]));
11470             break;
11471
11472         case ELEMENT_TYPE_I1:
11473             ExtOut("  = %d\n", *(char*) &(rgbValue[0]));
11474             break;
11475
11476         case ELEMENT_TYPE_U1:
11477             ExtOut("  = %d\n", *(unsigned char*) &(rgbValue[0]));
11478             break;
11479
11480         case ELEMENT_TYPE_I2:
11481             ExtOut("  = %hd\n", *(short*) &(rgbValue[0]));
11482             break;
11483
11484         case ELEMENT_TYPE_U2:
11485             ExtOut("  = %hu\n", *(unsigned short*) &(rgbValue[0]));
11486             break;
11487         
11488         case ELEMENT_TYPE_I:
11489             ExtOut("  = %d\n", *(int*) &(rgbValue[0]));
11490             break;
11491
11492         case ELEMENT_TYPE_U:
11493             ExtOut("  = %u\n", *(unsigned int*) &(rgbValue[0]));
11494             break;
11495
11496         case ELEMENT_TYPE_I4:
11497             ExtOut("  = %d\n", *(int*) &(rgbValue[0]));
11498             break;
11499
11500         case ELEMENT_TYPE_U4:
11501             ExtOut("  = %u\n", *(unsigned int*) &(rgbValue[0]));
11502             break;
11503
11504         case ELEMENT_TYPE_I8:
11505             ExtOut("  = %I64d\n", *(__int64*) &(rgbValue[0]));
11506             break;
11507
11508         case ELEMENT_TYPE_U8:
11509             ExtOut("  = %I64u\n", *(unsigned __int64*) &(rgbValue[0]));
11510             break;
11511
11512         case ELEMENT_TYPE_R4:
11513             ExtOut("  = %f\n", (double) *(float*) &(rgbValue[0]));
11514             break;
11515
11516         case ELEMENT_TYPE_R8:
11517             ExtOut("  = %f\n", *(double*) &(rgbValue[0]));
11518             break;
11519
11520         case ELEMENT_TYPE_OBJECT:
11521             ExtOut("  = object\n");
11522             break;
11523
11524             // TODO: The following corElementTypes are not yet implemented here.  Array
11525             // might be interesting to add, though the others may be of rather limited use:
11526             // ELEMENT_TYPE_ARRAY          = 0x14,     // MDARRAY <type> <rank> <bcount> <bound1> ... <lbcount> <lb1> ...
11527             // 
11528             // ELEMENT_TYPE_GENERICINST    = 0x15,     // GENERICINST <generic type> <argCnt> <arg1> ... <argn>
11529         }
11530
11531         return S_OK;
11532     }
11533
11534     static HRESULT PrintParameters(BOOL bParams, BOOL bLocals, IMetaDataImport * pMD, mdTypeDef typeDef, mdMethodDef methodDef, ICorDebugILFrame * pILFrame, ICorDebugModule * pModule, __in_z WCHAR* varToExpand, int currentFrame)
11535     {
11536         HRESULT Status = S_OK;
11537
11538         ULONG cParams = 0;
11539         ToRelease<ICorDebugValueEnum> pParamEnum;
11540         IfFailRet(pILFrame->EnumerateArguments(&pParamEnum));
11541         IfFailRet(pParamEnum->GetCount(&cParams));
11542         if (cParams > 0 && bParams)
11543         {
11544             DWORD methAttr = 0;
11545             IfFailRet(pMD->GetMethodProps(methodDef, NULL, NULL, 0, NULL, &methAttr, NULL, NULL, NULL, NULL));
11546
11547             ExtOut("\nPARAMETERS:\n");
11548             for (ULONG i=0; i < cParams; i++)
11549             {
11550                 ULONG paramNameLen = 0;
11551                 mdParamDef paramDef;
11552                 WCHAR paramName[mdNameLen] = W("\0");
11553
11554                 if(i == 0 && (methAttr & mdStatic) == 0)
11555                     swprintf_s(paramName, mdNameLen, W("this\0"));
11556                 else 
11557                 {
11558                     int idx = ((methAttr & mdStatic) == 0)? i : (i + 1);
11559                     if(SUCCEEDED(pMD->GetParamForMethodIndex(methodDef, idx, &paramDef)))
11560                         pMD->GetParamProps(paramDef, NULL, NULL, paramName, mdNameLen, &paramNameLen, NULL, NULL, NULL, NULL);
11561                 }
11562                 if(_wcslen(paramName) == 0)
11563                     swprintf_s(paramName, mdNameLen, W("param_%d\0"), i);
11564
11565                 ToRelease<ICorDebugValue> pValue;
11566                 ULONG cArgsFetched;
11567                 Status = pParamEnum->Next(1, &pValue, &cArgsFetched);
11568
11569                 if (FAILED(Status))
11570                 {
11571                     ExtOut("  + (Error 0x%x retrieving parameter '%S')\n", Status, paramName);
11572                     continue;
11573                 }
11574
11575                 if (Status == S_FALSE)
11576                 {
11577                     break;
11578                 }
11579
11580                 WCHAR typeName[mdNameLen] = W("\0");
11581                 GetTypeOfValue(pValue, typeName, mdNameLen);
11582                 DMLOut("  + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName));
11583
11584                 ToRelease<ICorDebugReferenceValue> pRefValue;
11585                 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL)
11586                 {
11587                     BOOL bIsNull = TRUE;
11588                     pRefValue->IsNull(&bIsNull);
11589                     if(bIsNull)
11590                     {
11591                         ExtOut(" = null\n");
11592                         continue;
11593                     }
11594                 }
11595
11596                 WCHAR currentExpansion[mdNameLen];
11597                 swprintf_s(currentExpansion, mdNameLen, W("%s\0"), paramName);
11598                 if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK)
11599                     ExtOut("  + (Error 0x%x printing parameter %d)\n", Status, i);
11600             }
11601         }
11602         else if (cParams == 0 && bParams)
11603             ExtOut("\nPARAMETERS: (none)\n");
11604
11605         ULONG cLocals = 0;
11606         ToRelease<ICorDebugValueEnum> pLocalsEnum;
11607         IfFailRet(pILFrame->EnumerateLocalVariables(&pLocalsEnum));
11608         IfFailRet(pLocalsEnum->GetCount(&cLocals));
11609         if (cLocals > 0 && bLocals)
11610         {
11611             bool symbolsAvailable = false;
11612             SymbolReader symReader;
11613             if(SUCCEEDED(symReader.LoadSymbols(pMD, pModule)))
11614                 symbolsAvailable = true;
11615             ExtOut("\nLOCALS:\n");
11616             for (ULONG i=0; i < cLocals; i++)
11617             {
11618                 ULONG paramNameLen = 0;
11619                 WCHAR paramName[mdNameLen] = W("\0");
11620
11621                 ToRelease<ICorDebugValue> pValue;
11622                 if(symbolsAvailable)
11623                 {
11624                     Status = symReader.GetNamedLocalVariable(pILFrame, i, paramName, mdNameLen, &pValue);
11625                 }
11626                 else
11627                 {
11628                     ULONG cArgsFetched;
11629                     Status = pLocalsEnum->Next(1, &pValue, &cArgsFetched);
11630                 }
11631                 if(_wcslen(paramName) == 0)
11632                     swprintf_s(paramName, mdNameLen, W("local_%d\0"), i);
11633
11634                 if (FAILED(Status))
11635                 {
11636                     ExtOut("  + (Error 0x%x retrieving local variable '%S')\n", Status, paramName);
11637                     continue;
11638                 }
11639
11640                 if (Status == S_FALSE)
11641                 {
11642                     break;
11643                 }
11644
11645                 WCHAR typeName[mdNameLen] = W("\0");
11646                 GetTypeOfValue(pValue, typeName, mdNameLen);
11647                 DMLOut("  + %S %s", typeName, DMLManagedVar(paramName, currentFrame, paramName));
11648
11649                 ToRelease<ICorDebugReferenceValue> pRefValue = NULL;
11650                 if(SUCCEEDED(pValue->QueryInterface(IID_ICorDebugReferenceValue, (void**)&pRefValue)) && pRefValue != NULL)
11651                 {
11652                     BOOL bIsNull = TRUE;
11653                     pRefValue->IsNull(&bIsNull);
11654                     if(bIsNull)
11655                     {
11656                         ExtOut(" = null\n");
11657                         continue;
11658                     }
11659                 }
11660
11661                 WCHAR currentExpansion[mdNameLen];
11662                 swprintf_s(currentExpansion, mdNameLen, W("%s\0"), paramName);
11663                 if((Status=PrintValue(pValue, pILFrame, pMD, 0, varToExpand, currentExpansion, mdNameLen, currentFrame)) != S_OK)
11664                     ExtOut("  + (Error 0x%x printing local variable %d)\n", Status, i);
11665             }
11666         }
11667         else if (cLocals == 0 && bLocals)
11668             ExtOut("\nLOCALS: (none)\n");
11669
11670         if(bParams || bLocals)
11671             ExtOut("\n");
11672
11673         return S_OK;
11674     }
11675
11676     static HRESULT ProcessFields(ICorDebugValue* pInputValue, ICorDebugType* pTypeCast, ICorDebugILFrame * pILFrame, int indent, __in_z WCHAR* varToExpand, __inout_ecount(currentExpansionSize) WCHAR* currentExpansion, DWORD currentExpansionSize, int currentFrame)
11677     {
11678         if(!ShouldExpandVariable(varToExpand, currentExpansion)) return S_OK;
11679         size_t currentExpansionLen = _wcslen(currentExpansion);
11680
11681         HRESULT Status = S_OK;
11682
11683         BOOL isNull = FALSE;
11684         ToRelease<ICorDebugValue> pValue;
11685         IfFailRet(DereferenceAndUnboxValue(pInputValue, &pValue, &isNull));
11686
11687         if(isNull) return S_OK;
11688
11689         mdTypeDef currentTypeDef;
11690         ToRelease<ICorDebugClass> pClass;
11691         ToRelease<ICorDebugValue2> pValue2;
11692         ToRelease<ICorDebugType> pType;
11693         ToRelease<ICorDebugModule> pModule;
11694         IfFailRet(pValue->QueryInterface(IID_ICorDebugValue2, (LPVOID *) &pValue2));
11695         if(pTypeCast == NULL)
11696             IfFailRet(pValue2->GetExactType(&pType));
11697         else
11698         {
11699             pType = pTypeCast;
11700             pType->AddRef();
11701         }
11702         IfFailRet(pType->GetClass(&pClass));
11703         IfFailRet(pClass->GetModule(&pModule));
11704         IfFailRet(pClass->GetToken(&currentTypeDef));
11705
11706         ToRelease<IUnknown> pMDUnknown;
11707         ToRelease<IMetaDataImport> pMD;
11708         IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
11709         IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
11710
11711         WCHAR baseTypeName[mdNameLen] = W("\0");
11712         ToRelease<ICorDebugType> pBaseType;
11713         if(SUCCEEDED(pType->GetBase(&pBaseType)) && pBaseType != NULL && SUCCEEDED(GetTypeOfValue(pBaseType, baseTypeName, mdNameLen)))
11714         {
11715             if(_wcsncmp(baseTypeName, W("System.Enum"), 11) == 0)
11716                 return S_OK;
11717             else if(_wcsncmp(baseTypeName, W("System.Object"), 13) != 0 && _wcsncmp(baseTypeName, W("System.ValueType"), 16) != 0)
11718             {
11719                 currentExpansion[currentExpansionLen] = W('\0');
11720                 wcscat_s(currentExpansion, currentExpansionSize, W(".\0"));
11721                 wcscat_s(currentExpansion, currentExpansionSize, W("[basetype]"));
11722                 for(int i = 0; i < indent; i++) ExtOut("    ");
11723                 DMLOut(" |- %S %s\n", baseTypeName, DMLManagedVar(currentExpansion, currentFrame, W("[basetype]")));
11724
11725                 if(ShouldExpandVariable(varToExpand, currentExpansion))
11726                     ProcessFields(pInputValue, pBaseType, pILFrame, indent + 1, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
11727             }
11728         }
11729
11730
11731         ULONG numFields = 0;
11732         HCORENUM fEnum = NULL;
11733         mdFieldDef fieldDef;
11734         while(SUCCEEDED(pMD->EnumFields(&fEnum, currentTypeDef, &fieldDef, 1, &numFields)) && numFields != 0)
11735         {
11736             ULONG             nameLen = 0;
11737             DWORD             fieldAttr = 0;
11738             WCHAR             mdName[mdNameLen];
11739             WCHAR             typeName[mdNameLen];
11740             if(SUCCEEDED(pMD->GetFieldProps(fieldDef, NULL, mdName, mdNameLen, &nameLen, &fieldAttr, NULL, NULL, NULL, NULL, NULL)))
11741             {
11742                 currentExpansion[currentExpansionLen] = W('\0');
11743                 wcscat_s(currentExpansion, currentExpansionSize, W(".\0"));
11744                 wcscat_s(currentExpansion, currentExpansionSize, mdName);
11745
11746                 ToRelease<ICorDebugValue> pFieldVal;
11747                 if(fieldAttr & fdLiteral)
11748                 {
11749                     //TODO: Is it worth it??
11750                     //ExtOut(" |- const %S", mdName);
11751                 }
11752                 else
11753                 {
11754                     for(int i = 0; i < indent; i++) ExtOut("    ");
11755
11756                     if (fieldAttr & fdStatic)
11757                         pType->GetStaticFieldValue(fieldDef, pILFrame, &pFieldVal);
11758                     else
11759                     {
11760                         ToRelease<ICorDebugObjectValue> pObjValue;
11761                         if (SUCCEEDED(pValue->QueryInterface(IID_ICorDebugObjectValue, (LPVOID*) &pObjValue)))
11762                             pObjValue->GetFieldValue(pClass, fieldDef, &pFieldVal);
11763                     }
11764
11765                     if(pFieldVal != NULL)
11766                     {
11767                         typeName[0] = L'\0';
11768                         GetTypeOfValue(pFieldVal, typeName, mdNameLen);
11769                         DMLOut(" |- %S %s", typeName, DMLManagedVar(currentExpansion, currentFrame, mdName));
11770                         PrintValue(pFieldVal, pILFrame, pMD, indent, varToExpand, currentExpansion, currentExpansionSize, currentFrame);
11771                     }
11772                     else if(!(fieldAttr & fdLiteral)) 
11773                         ExtOut(" |- < unknown type > %S\n", mdName);
11774                 }
11775             }
11776         }
11777         pMD->CloseEnum(fEnum);
11778         return S_OK;
11779     }
11780
11781 public:
11782
11783     // This is the main worker function used if !clrstack is called with "-i" to indicate
11784     // that the public ICorDebug* should be used instead of the private DAC interface. NOTE:
11785     // Currently only bParams is supported. NOTE: This is a work in progress and the
11786     // following would be good to do:
11787     //     * More thorough testing with interesting stacks, especially with transitions into
11788     //         and out of managed code.
11789     //     * Consider interleaving this code back into the main body of !clrstack if it turns
11790     //         out that there's a lot of duplication of code between these two functions.
11791     //         (Still unclear how things will look once locals is implemented.)
11792     static HRESULT ClrStackFromPublicInterface(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, __in_z WCHAR* varToExpand = NULL, int onlyShowFrame = -1)
11793     {
11794         HRESULT Status;
11795
11796         IfFailRet(InitCorDebugInterface());
11797
11798         ExtOut("\n\n\nDumping managed stack and managed variables using ICorDebug.\n");
11799         ExtOut("=============================================================================\n");
11800
11801         ToRelease<ICorDebugThread> pThread;
11802         ToRelease<ICorDebugThread3> pThread3;
11803         ToRelease<ICorDebugStackWalk> pStackWalk;
11804         ULONG ulThreadID = 0;
11805         g_ExtSystem->GetCurrentThreadSystemId(&ulThreadID);
11806
11807         IfFailRet(g_pCorDebugProcess->GetThread(ulThreadID, &pThread));
11808         IfFailRet(pThread->QueryInterface(IID_ICorDebugThread3, (LPVOID *) &pThread3));
11809         IfFailRet(pThread3->CreateStackWalk(&pStackWalk));
11810
11811         InternalFrameManager internalFrameManager;
11812         IfFailRet(internalFrameManager.Init(pThread3));
11813         
11814     #if defined(_AMD64_)
11815         ExtOut("%-16s %-16s %s\n", "Child SP", "IP", "Call Site");
11816     #elif defined(_X86_)
11817         ExtOut("%-8s %-8s %s\n", "Child SP", "IP", "Call Site");
11818     #endif
11819
11820         int currentFrame = -1;
11821
11822         for (Status = S_OK; ; Status = pStackWalk->Next())
11823         {
11824             currentFrame++;
11825
11826             if (Status == CORDBG_S_AT_END_OF_STACK)
11827             {
11828                 ExtOut("Stack walk complete.\n");
11829                 break;
11830             }
11831             IfFailRet(Status);
11832
11833             if (IsInterrupt())
11834             {
11835                 ExtOut("<interrupted>\n");
11836                 break;
11837             }
11838             
11839             CROSS_PLATFORM_CONTEXT context;
11840             ULONG32 cbContextActual;
11841             if ((Status=pStackWalk->GetContext(
11842                 DT_CONTEXT_FULL, 
11843                 sizeof(context),
11844                 &cbContextActual,
11845                 (BYTE *)&context))!=S_OK)
11846             {
11847                 ExtOut("GetFrameContext failed: %lx\n",Status);
11848                 break;
11849             }
11850
11851             // First find the info for the Frame object, if the current frame has an associated clr!Frame.
11852             CLRDATA_ADDRESS sp = GetSP(context);
11853             CLRDATA_ADDRESS ip = GetIP(context);
11854
11855             ToRelease<ICorDebugFrame> pFrame;
11856             IfFailRet(pStackWalk->GetFrame(&pFrame));
11857             if (Status == S_FALSE)
11858             {
11859                 DMLOut("%p %s [NativeStackFrame]\n", SOS_PTR(sp), DMLIP(ip));
11860                 continue;
11861             }
11862
11863             // TODO: What about internal frames preceding the above native stack frame? 
11864             // Should I just exclude the above native stack frame from the output?
11865             // TODO: Compare caller frame (instead of current frame) against internal frame,
11866             // to deal with issues of current frame's current SP being closer to leaf than
11867             // EE Frames it pushes.  By "caller" I mean not just managed caller, but the
11868             // very next non-internal frame dbi would return (native or managed). OR...
11869             // perhaps I should use GetStackRange() instead, to see if the internal frame
11870             // appears leafier than the base-part of the range of the currently iterated
11871             // stack frame?  I think I like that better.
11872             _ASSERTE(pFrame != NULL);
11873             IfFailRet(internalFrameManager.PrintPrecedingInternalFrames(pFrame));
11874
11875             // Print the stack and instruction pointers.
11876             DMLOut("%p %s ", SOS_PTR(sp), DMLIP(ip));
11877
11878             ToRelease<ICorDebugRuntimeUnwindableFrame> pRuntimeUnwindableFrame;
11879             Status = pFrame->QueryInterface(IID_ICorDebugRuntimeUnwindableFrame, (LPVOID *) &pRuntimeUnwindableFrame);
11880             if (SUCCEEDED(Status))
11881             {
11882                 ExtOut("[RuntimeUnwindableFrame]\n");
11883                 continue;
11884             }
11885
11886             // Print the method/Frame info
11887
11888             // TODO: IS THE FOLLOWING NECESSARY, OR AM I GUARANTEED THAT ALL INTERNAL FRAMES
11889             // CAN BE FOUND VIA GetActiveInternalFrames?
11890             ToRelease<ICorDebugInternalFrame> pInternalFrame;
11891             Status = pFrame->QueryInterface(IID_ICorDebugInternalFrame, (LPVOID *) &pInternalFrame);
11892             if (SUCCEEDED(Status))
11893             {
11894                 // This is a clr!Frame.
11895                 LPCWSTR pwszFrameName = W("TODO: Implement GetFrameName");
11896                 ExtOut("[%S: p] ", pwszFrameName);
11897             }
11898
11899             // Print the frame's associated function info, if it has any.
11900             ToRelease<ICorDebugILFrame> pILFrame;
11901             HRESULT hrILFrame = pFrame->QueryInterface(IID_ICorDebugILFrame, (LPVOID*) &pILFrame);
11902
11903             if (SUCCEEDED(hrILFrame))
11904             {
11905                 ToRelease<ICorDebugFunction> pFunction;
11906                 Status = pFrame->GetFunction(&pFunction);
11907                 if (FAILED(Status))
11908                 {
11909                     // We're on a JITted frame, but there's no Function for it.  So it must
11910                     // be... 
11911                     ExtOut("[IL Stub or LCG]\n");
11912                     continue;
11913                 }
11914
11915                 ToRelease<ICorDebugClass> pClass;
11916                 ToRelease<ICorDebugModule> pModule;
11917                 mdMethodDef methodDef;
11918                 IfFailRet(pFunction->GetClass(&pClass));
11919                 IfFailRet(pFunction->GetModule(&pModule));
11920                 IfFailRet(pFunction->GetToken(&methodDef));
11921
11922                 WCHAR wszModuleName[100];
11923                 ULONG32 cchModuleNameActual;
11924                 IfFailRet(pModule->GetName(_countof(wszModuleName), &cchModuleNameActual, wszModuleName));
11925
11926                 ToRelease<IUnknown> pMDUnknown;
11927                 ToRelease<IMetaDataImport> pMD;
11928                 ToRelease<IMDInternalImport> pMDInternal;
11929                 IfFailRet(pModule->GetMetaDataInterface(IID_IMetaDataImport, &pMDUnknown));
11930                 IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMD));
11931                 IfFailRet(GetMDInternalFromImport(pMD, &pMDInternal));
11932
11933                 mdTypeDef typeDef;
11934                 IfFailRet(pClass->GetToken(&typeDef));
11935
11936                 // Note that we don't need to pretty print the class, as class name is
11937                 // already printed from GetMethodName below
11938
11939                 CQuickBytes functionName;
11940                 // TODO: WARNING: GetMethodName() appears to include lots of unexercised
11941                 // code, as evidenced by some fundamental bugs I found.  It should either be
11942                 // thoroughly reviewed, or some other more exercised code path to grab the
11943                 // name should be used.
11944                 // TODO: If we do stay with GetMethodName, it should be updated to print
11945                 // generics properly.  Today, it does not show generic type parameters, and
11946                 // if any arguments have a generic type, those arguments are just shown as
11947                 // "__Canon", even when they're value types.
11948                 GetMethodName(methodDef, pMD, &functionName);
11949
11950                 DMLOut(DMLManagedVar(W("-a"), currentFrame, (LPWSTR)functionName.Ptr()));
11951                 ExtOut(" (%S)\n", wszModuleName);
11952
11953                 if (SUCCEEDED(hrILFrame) && (bParams || bLocals))
11954                 {
11955                     if(onlyShowFrame == -1 || (onlyShowFrame >= 0 && currentFrame == onlyShowFrame))
11956                         IfFailRet(PrintParameters(bParams, bLocals, pMD, typeDef, methodDef, pILFrame, pModule, varToExpand, currentFrame));
11957                 }
11958             }
11959         }
11960         ExtOut("=============================================================================\n");
11961
11962 #ifdef FEATURE_PAL
11963         // Temporary until we get a process exit notification plumbed from lldb
11964         UninitCorDebugInterface();
11965 #endif
11966         return S_OK;
11967     }
11968 };
11969
11970 WString BuildRegisterOutput(const SOSStackRefData &ref, bool printObj)
11971 {
11972     WString res;
11973     
11974     if (ref.HasRegisterInformation)
11975     {
11976         WCHAR reg[32];
11977         HRESULT hr = g_sos->GetRegisterName(ref.Register, _countof(reg), reg, NULL);
11978         if (SUCCEEDED(hr))
11979             res = reg;
11980         else
11981             res = W("<unknown register>");
11982             
11983         if (ref.Offset)
11984         {
11985             int offset = ref.Offset;
11986             if (offset > 0)
11987             {
11988                 res += W("+");
11989             }
11990             else
11991             {
11992                 res += W("-");
11993                 offset = -offset;
11994             }
11995             
11996             res += Hex(offset);
11997         }
11998         
11999         res += W(": ");
12000     }
12001     
12002     if (ref.Address)
12003         res += WString(Pointer(ref.Address));
12004         
12005     if (printObj)
12006     {
12007         if (ref.Address)
12008             res += W(" -> ");
12009
12010         res += WString(ObjectPtr(ref.Object));
12011     }
12012
12013     if (ref.Flags & SOSRefPinned)
12014     {
12015         res += W(" (pinned)");
12016     }
12017     
12018     if (ref.Flags & SOSRefInterior)
12019     {
12020         res += W(" (interior)");
12021     }
12022     
12023     return res;
12024 }
12025
12026 void PrintRef(const SOSStackRefData &ref, TableOutput &out)
12027 {
12028     WString res = BuildRegisterOutput(ref);
12029     
12030     if (ref.Object && (ref.Flags & SOSRefInterior) == 0)
12031     {
12032         WCHAR type[128];
12033         sos::BuildTypeWithExtraInfo(TO_TADDR(ref.Object), _countof(type), type);
12034         
12035         res += WString(W(" - ")) + type;
12036     }
12037     
12038     out.WriteColumn(2, res);
12039 }
12040
12041
12042 class ClrStackImpl
12043 {
12044 public:
12045     static void PrintThread(ULONG osID, BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bFull, BOOL bDisplayRegVals)
12046     {
12047         // Symbols variables
12048         ULONG symlines = 0; // symlines will be non-zero only if SYMOPT_LOAD_LINES was set in the symbol options
12049         if (!bSuppressLines && SUCCEEDED(g_ExtSymbols->GetSymbolOptions(&symlines)))
12050         {
12051             symlines &= SYMOPT_LOAD_LINES;
12052         }
12053         
12054         if (symlines == 0)
12055             bSuppressLines = TRUE;
12056         
12057         ToRelease<IXCLRDataStackWalk> pStackWalk;
12058         
12059         HRESULT hr = CreateStackWalk(osID, &pStackWalk);
12060         if (FAILED(hr) || pStackWalk == NULL)
12061         {
12062             ExtOut("Failed to start stack walk: %lx\n", hr);
12063             return;
12064         }
12065
12066 #ifdef _TARGET_WIN64_
12067         PDEBUG_STACK_FRAME currentNativeFrame = NULL;
12068         ULONG numNativeFrames = 0;
12069         if (bFull)
12070         {
12071             hr = GetContextStackTrace(osID, &numNativeFrames);
12072             if (FAILED(hr))
12073             {
12074                 ExtOut("Failed to get native stack frames: %lx\n", hr);
12075                 return;
12076             }
12077             currentNativeFrame = &g_Frames[0];
12078         }
12079 #endif // _TARGET_WIN64_
12080         
12081         unsigned int refCount = 0, errCount = 0;
12082         ArrayHolder<SOSStackRefData> pRefs = NULL;
12083         ArrayHolder<SOSStackRefError> pErrs = NULL;
12084         if (bGC && FAILED(GetGCRefs(osID, &pRefs, &refCount, &pErrs, &errCount)))
12085             refCount = 0;
12086             
12087         TableOutput out(3, POINTERSIZE_HEX, AlignRight);
12088         out.WriteRow("Child SP", "IP", "Call Site");
12089                 
12090         do
12091         {
12092             if (IsInterrupt())
12093             {
12094                 ExtOut("<interrupted>\n");
12095                 break;
12096             }
12097             CLRDATA_ADDRESS ip = 0, sp = 0;
12098             hr = GetFrameLocation(pStackWalk, &ip, &sp);
12099
12100             DacpFrameData FrameData;
12101             HRESULT frameDataResult = FrameData.Request(pStackWalk);
12102             if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
12103                 sp = FrameData.frameAddr;
12104
12105 #ifdef _TARGET_WIN64_
12106             while ((numNativeFrames > 0) && (currentNativeFrame->StackOffset <= sp))
12107             {
12108                 if (currentNativeFrame->StackOffset != sp)
12109                 {
12110                     PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines);
12111                 }
12112                 currentNativeFrame++;
12113                 numNativeFrames--;
12114             }
12115 #endif // _TARGET_WIN64_
12116
12117             // Print the stack pointer.
12118             out.WriteColumn(0, sp);
12119
12120             // Print the method/Frame info
12121             if (SUCCEEDED(frameDataResult) && FrameData.frameAddr)
12122             {
12123                 // Skip the instruction pointer because it doesn't really mean anything for method frames
12124                 out.WriteColumn(1, bFull ? String("") : NativePtr(ip));
12125                 
12126                 // This is a clr!Frame.
12127                 out.WriteColumn(2, GetFrameFromAddress(TO_TADDR(FrameData.frameAddr), pStackWalk, bFull));
12128             
12129                 // Print out gc references for the Frame.  
12130                 for (unsigned int i = 0; i < refCount; ++i)
12131                     if (pRefs[i].Source == sp)
12132                         PrintRef(pRefs[i], out);
12133                         
12134                 // Print out an error message if we got one.
12135                 for (unsigned int i = 0; i < errCount; ++i)
12136                     if (pErrs[i].Source == sp)
12137                         out.WriteColumn(2, "Failed to enumerate GC references.");
12138             }
12139             else
12140             {
12141                 out.WriteColumn(1, InstructionPtr(ip));
12142                 out.WriteColumn(2, MethodNameFromIP(ip, bSuppressLines, bFull, bFull));
12143                     
12144                 // Print out gc references.  refCount will be zero if bGC is false (or if we
12145                 // failed to fetch gc reference information).
12146                 for (unsigned int i = 0; i < refCount; ++i)
12147                     if (pRefs[i].Source == ip && pRefs[i].StackPointer == sp)
12148                         PrintRef(pRefs[i], out);
12149
12150                 // Print out an error message if we got one.
12151                 for (unsigned int i = 0; i < errCount; ++i)
12152                     if (pErrs[i].Source == sp)
12153                         out.WriteColumn(2, "Failed to enumerate GC references.");
12154
12155                 if (bParams || bLocals)
12156                     PrintArgsAndLocals(pStackWalk, bParams, bLocals);
12157             }
12158
12159             if (bDisplayRegVals)
12160                 PrintManagedFrameContext(pStackWalk);
12161
12162         } while (pStackWalk->Next() == S_OK);
12163
12164 #ifdef _TARGET_WIN64_
12165         while (numNativeFrames > 0)
12166         {
12167             PrintNativeStackFrame(out, currentNativeFrame, bSuppressLines);
12168             currentNativeFrame++;
12169             numNativeFrames--;
12170         }
12171 #endif // _TARGET_WIN64_
12172     }
12173     
12174     static HRESULT PrintManagedFrameContext(IXCLRDataStackWalk *pStackWalk)
12175     {
12176         CROSS_PLATFORM_CONTEXT context;
12177         HRESULT hr = pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), NULL, (BYTE *)&context);
12178         if (FAILED(hr) || hr == S_FALSE)
12179         {
12180             // GetFrameContext returns S_FALSE if the frame iterator is invalid.  That's basically an error for us.
12181             ExtOut("GetFrameContext failed: %lx\n", hr);
12182             return E_FAIL;
12183         }
12184                      
12185 #if defined(SOS_TARGET_AMD64)
12186         String outputFormat3 = "    %3s=%016x %3s=%016x %3s=%016x\n";
12187         String outputFormat2 = "    %3s=%016x %3s=%016x\n";
12188         ExtOut(outputFormat3, "rsp", context.Amd64Context.Rsp, "rbp", context.Amd64Context.Rbp, "rip", context.Amd64Context.Rip);
12189         ExtOut(outputFormat3, "rax", context.Amd64Context.Rax, "rbx", context.Amd64Context.Rbx, "rcx", context.Amd64Context.Rcx);
12190         ExtOut(outputFormat3, "rdx", context.Amd64Context.Rdx, "rsi", context.Amd64Context.Rsi, "rdi", context.Amd64Context.Rdi);
12191         ExtOut(outputFormat3, "r8", context.Amd64Context.R8, "r9", context.Amd64Context.R9, "r10", context.Amd64Context.R10);
12192         ExtOut(outputFormat3, "r11", context.Amd64Context.R11, "r12", context.Amd64Context.R12, "r13", context.Amd64Context.R13);
12193         ExtOut(outputFormat2, "r14", context.Amd64Context.R14, "r15", context.Amd64Context.R15);
12194 #elif defined(SOS_TARGET_X86)
12195         String outputFormat3 = "    %3s=%08x %3s=%08x %3s=%08x\n";
12196         String outputFormat2 = "    %3s=%08x %3s=%08x\n";
12197         ExtOut(outputFormat3, "esp", context.X86Context.Esp, "ebp", context.X86Context.Ebp, "eip", context.X86Context.Eip);
12198         ExtOut(outputFormat3, "eax", context.X86Context.Eax, "ebx", context.X86Context.Ebx, "ecx", context.X86Context.Ecx);      
12199         ExtOut(outputFormat3, "edx", context.X86Context.Edx, "esi", context.X86Context.Esi, "edi", context.X86Context.Edi);
12200 #elif defined(SOS_TARGET_ARM)
12201         String outputFormat3 = "    %3s=%08x %3s=%08x %3s=%08x\n";
12202         String outputFormat2 = "    %s=%08x %s=%08x\n";
12203         String outputFormat1 = "    %s=%08x\n";
12204         ExtOut(outputFormat3, "r0", context.ArmContext.R0, "r1", context.ArmContext.R1, "r2", context.ArmContext.R2);
12205         ExtOut(outputFormat3, "r3", context.ArmContext.R3, "r4", context.ArmContext.R4, "r5", context.ArmContext.R5);
12206         ExtOut(outputFormat3, "r6", context.ArmContext.R6, "r7", context.ArmContext.R7, "r8", context.ArmContext.R8);
12207         ExtOut(outputFormat3, "r9", context.ArmContext.R9, "r10", context.ArmContext.R10, "r11", context.ArmContext.R11);
12208         ExtOut(outputFormat1, "r12", context.ArmContext.R12);
12209         ExtOut(outputFormat3, "sp", context.ArmContext.Sp, "lr", context.ArmContext.Lr, "pc", context.ArmContext.Pc);
12210         ExtOut(outputFormat2, "cpsr", context.ArmContext.Cpsr, "fpsr", context.ArmContext.Fpscr);
12211 #elif defined(SOS_TARGET_ARM64)
12212         String outputXRegFormat3 = "    x%d=%016x x%d=%016x x%d=%016x\n";
12213         String outputXRegFormat1 = "    x%d=%016x\n";
12214         String outputFormat3     = "    %s=%016x %s=%016x %s=%016x\n";
12215         String outputFormat2     = "    %s=%08x %s=%08x\n";
12216         DWORD64 *X = context.Arm64Context.X;
12217         for (int i = 0; i < 9; i++)
12218         {
12219             ExtOut(outputXRegFormat3, i + 0, X[i + 0], i + 1, X[i + 1], i + 2, X[i + 2]);
12220         }
12221         ExtOut(outputXRegFormat1, 28, X[28]);
12222         ExtOut(outputFormat3, "sp", context.ArmContext.Sp, "lr", context.ArmContext.Lr, "pc", context.ArmContext.Pc);
12223         ExtOut(outputFormat2, "cpsr", context.ArmContext.Cpsr, "fpsr", context.ArmContext.Fpscr);
12224 #else
12225         ExtOut("Can't display register values for this platform\n");
12226 #endif
12227         return S_OK;
12228
12229     }
12230
12231     static HRESULT GetFrameLocation(IXCLRDataStackWalk *pStackWalk, CLRDATA_ADDRESS *ip, CLRDATA_ADDRESS *sp)
12232     {
12233         CROSS_PLATFORM_CONTEXT context;
12234         HRESULT hr = pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(), NULL, (BYTE *)&context);
12235         if (FAILED(hr) || hr == S_FALSE)
12236         {
12237             // GetFrameContext returns S_FALSE if the frame iterator is invalid.  That's basically an error for us.
12238             ExtOut("GetFrameContext failed: %lx\n", hr);
12239             return E_FAIL;
12240         }
12241
12242         // First find the info for the Frame object, if the current frame has an associated clr!Frame.
12243         *ip = GetIP(context);
12244         *sp = GetSP(context);
12245         
12246         if (IsDbgTargetArm())
12247             *ip = *ip & ~THUMB_CODE;
12248         
12249         return S_OK;
12250     }
12251     
12252     static void PrintNativeStackFrame(TableOutput out, PDEBUG_STACK_FRAME frame, BOOL bSuppressLines)
12253     {
12254         char filename[MAX_LONGPATH + 1];
12255         char symbol[1024];
12256         ULONG64 displacement;
12257
12258         ULONG64 ip = frame->InstructionOffset;
12259
12260         out.WriteColumn(0, frame->StackOffset);
12261         out.WriteColumn(1, NativePtr(ip));
12262
12263         HRESULT hr = g_ExtSymbols->GetNameByOffset(TO_CDADDR(ip), symbol, _countof(symbol), NULL, &displacement);
12264         if (SUCCEEDED(hr) && symbol[0] != '\0')
12265         {
12266             String frameOutput;
12267             frameOutput += symbol;
12268
12269             if (displacement)
12270             {
12271                 frameOutput += " + ";
12272                 frameOutput += Decimal(displacement);
12273             }
12274
12275             if (!bSuppressLines)
12276             {
12277                 ULONG line;
12278                 hr = g_ExtSymbols->GetLineByOffset(TO_CDADDR(ip), &line, filename, _countof(filename), NULL, NULL);
12279                 if (SUCCEEDED(hr))
12280                 {
12281                     frameOutput += " at ";
12282                     frameOutput += filename;
12283                     frameOutput += ":";
12284                     frameOutput += Decimal(line);
12285                 }
12286             }
12287
12288             out.WriteColumn(2, frameOutput);
12289         }
12290         else
12291         {
12292             out.WriteColumn(2, "");
12293         }
12294     }
12295
12296     static void PrintCurrentThread(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bNative, BOOL bDisplayRegVals)
12297     {
12298         ULONG id = 0;
12299         ULONG osid = 0;
12300         
12301         g_ExtSystem->GetCurrentThreadSystemId(&osid);
12302         ExtOut("OS Thread Id: 0x%x ", osid);
12303         g_ExtSystem->GetCurrentThreadId(&id);
12304         ExtOut("(%d)\n", id);
12305         
12306         PrintThread(osid, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals);
12307     }
12308
12309     static void PrintAllThreads(BOOL bParams, BOOL bLocals, BOOL bSuppressLines, BOOL bGC, BOOL bNative, BOOL bDisplayRegVals)
12310     {
12311         HRESULT Status;
12312
12313         DacpThreadStoreData ThreadStore;
12314         if ((Status = ThreadStore.Request(g_sos)) != S_OK)
12315         {
12316             ExtErr("Failed to request ThreadStore\n");
12317             return;
12318         }
12319
12320         DacpThreadData Thread;
12321         CLRDATA_ADDRESS CurThread = ThreadStore.firstThread;
12322         while (CurThread != 0)
12323         {
12324             if (IsInterrupt())
12325                 break;
12326
12327             if ((Status = Thread.Request(g_sos, CurThread)) != S_OK)
12328             {
12329                 ExtErr("Failed to request thread at %p\n", CurThread);
12330                 return;
12331             }
12332             ExtOut("OS Thread Id: 0x%x\n", Thread.osThreadId);
12333             PrintThread(Thread.osThreadId, bParams, bLocals, bSuppressLines, bGC, bNative, bDisplayRegVals);
12334             CurThread = Thread.nextThread;
12335         }
12336     }
12337
12338 private: 
12339     static HRESULT CreateStackWalk(ULONG osID, IXCLRDataStackWalk **ppStackwalk)
12340     {
12341         HRESULT hr = S_OK;
12342         ToRelease<IXCLRDataTask> pTask;
12343
12344         if ((hr = g_clrData->GetTaskByOSThreadID(osID, &pTask)) != S_OK)
12345         {
12346             ExtOut("Unable to walk the managed stack. The current thread is likely not a \n");
12347             ExtOut("managed thread. You can run !threads to get a list of managed threads in\n");
12348             ExtOut("the process\n");
12349             return hr;
12350         }
12351
12352         return pTask->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED |
12353                                       CLRDATA_SIMPFRAME_MANAGED_METHOD |
12354                                       CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
12355                                       CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
12356                                       ppStackwalk);
12357     }
12358
12359     /* Prints the args and locals of for a thread's stack.
12360      * Params:
12361      *      pStackWalk - the stack we are printing
12362      *      bArgs - whether to print args
12363      *      bLocals - whether to print locals
12364      */
12365     static void PrintArgsAndLocals(IXCLRDataStackWalk *pStackWalk, BOOL bArgs, BOOL bLocals)
12366     {
12367         ToRelease<IXCLRDataFrame> pFrame;
12368         ToRelease<IXCLRDataValue> pVal;
12369         ULONG32 argCount = 0;
12370         ULONG32 localCount = 0;
12371         HRESULT hr = S_OK;
12372         
12373         hr = pStackWalk->GetFrame(&pFrame);
12374         
12375         // Print arguments
12376         if (SUCCEEDED(hr) && bArgs)
12377             hr = pFrame->GetNumArguments(&argCount);
12378                 
12379         if (SUCCEEDED(hr) && bArgs)
12380             hr = ShowArgs(argCount, pFrame, pVal);
12381         
12382         // Print locals
12383         if (SUCCEEDED(hr) && bLocals)
12384             hr = pFrame->GetNumLocalVariables(&localCount);
12385         
12386         if (SUCCEEDED(hr) && bLocals)
12387             ShowLocals(localCount, pFrame, pVal);
12388             
12389         ExtOut("\n");
12390     }
12391     
12392     
12393
12394     /* Displays the arguments to a function
12395      * Params:
12396      *      argy - the number of arguments the function has
12397      *      pFramey - the frame we are inspecting
12398      *      pVal - a pointer to the CLRDataValue we use to query for info about the args
12399      */
12400     static HRESULT ShowArgs(ULONG32 argy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal)
12401     {
12402         CLRDATA_ADDRESS addr = 0;
12403         BOOL fPrintedLocation = FALSE;
12404         ULONG64 outVar = 0;
12405         ULONG32 tmp;
12406         HRESULT hr = S_OK;
12407         
12408         ArrayHolder<WCHAR> argName = new NOTHROW WCHAR[mdNameLen];
12409         if (!argName)
12410         {
12411             ReportOOM();
12412             return E_FAIL;
12413         }
12414         
12415         for (ULONG32 i=0; i < argy; i++)
12416         {   
12417             if (i == 0)
12418             {      
12419                 ExtOut("    PARAMETERS:\n");
12420             }
12421             
12422             hr = pFramey->GetArgumentByIndex(i,
12423                                    &pVal,
12424                                    mdNameLen,
12425                                    &tmp,
12426                                    argName);
12427             
12428             if (FAILED(hr))
12429                 return hr;
12430
12431             ExtOut("        ");
12432             
12433             if (argName[0] != L'\0')
12434             {
12435                 ExtOut("%S ", argName.GetPtr());
12436             }
12437             
12438             // At times we cannot print the value of a parameter (most
12439             // common case being a non-primitive value type).  In these 
12440             // cases we need to print the location of the parameter, 
12441             // so that we can later examine it (e.g. using !dumpvc)
12442             {
12443                 bool result = SUCCEEDED(pVal->GetNumLocations(&tmp)) && tmp == 1;
12444                 if (result)
12445                     result = SUCCEEDED(pVal->GetLocationByIndex(0, &tmp, &addr));
12446                 
12447                 if (result)
12448                 {
12449                     if (tmp == CLRDATA_VLOC_REGISTER)
12450                     {
12451                         ExtOut("(<CLR reg>) ");
12452                     }
12453                     else
12454                     {
12455                         ExtOut("(0x%p) ", SOS_PTR(CDA_TO_UL64(addr)));
12456                     }
12457                     fPrintedLocation = TRUE;
12458                 }
12459             }
12460
12461             if (argName[0] != L'\0' || fPrintedLocation)
12462             {
12463                 ExtOut("= ");                
12464             }
12465             
12466             if (HRESULT_CODE(pVal->GetBytes(0,&tmp,NULL)) == ERROR_BUFFER_OVERFLOW)
12467             {
12468                 ArrayHolder<BYTE> pByte = new NOTHROW BYTE[tmp + 1];
12469                 if (pByte == NULL)
12470                 {
12471                     ReportOOM();
12472                     return E_FAIL;
12473                 }
12474                 
12475                 hr = pVal->GetBytes(tmp, &tmp, pByte);
12476                 
12477                 if (FAILED(hr))
12478                 {
12479                     ExtOut("<unable to retrieve data>\n");
12480                 }
12481                 else
12482                 {
12483                     switch(tmp)
12484                     {
12485                         case 1: outVar = *((BYTE *)pByte.GetPtr()); break;
12486                         case 2: outVar = *((short *)pByte.GetPtr()); break;
12487                         case 4: outVar = *((DWORD *)pByte.GetPtr()); break;
12488                         case 8: outVar = *((ULONG64 *)pByte.GetPtr()); break;
12489                         default: outVar = 0;
12490                     }
12491
12492                     if (outVar)
12493                         DMLOut("0x%s\n", DMLObject(outVar));
12494                     else
12495                         ExtOut("0x%p\n", SOS_PTR(outVar));
12496                 }
12497                 
12498             }
12499             else
12500             {
12501                 ExtOut("<no data>\n");
12502             }
12503             
12504             pVal->Release();
12505         }
12506         
12507         return S_OK;
12508     }
12509
12510
12511     /* Prints the locals of a frame.
12512      * Params:
12513      *      localy - the number of locals in the frame
12514      *      pFramey - the frame we are inspecting
12515      *      pVal - a pointer to the CLRDataValue we use to query for info about the args
12516      */
12517     static HRESULT ShowLocals(ULONG32 localy, IXCLRDataFrame *pFramey, IXCLRDataValue *pVal)
12518     {
12519         for (ULONG32 i=0; i < localy; i++)
12520         {   
12521             if (i == 0)
12522                 ExtOut("    LOCALS:\n");
12523             
12524             HRESULT hr;
12525             ExtOut("        ");
12526             
12527             // local names don't work in Whidbey.
12528             hr = pFramey->GetLocalVariableByIndex(i, &pVal, mdNameLen, NULL, g_mdName);
12529             if (FAILED(hr))
12530             {
12531                 return hr;
12532             }
12533
12534             ULONG32 numLocations;
12535             if (SUCCEEDED(pVal->GetNumLocations(&numLocations)) &&
12536                 numLocations == 1)
12537             {
12538                 ULONG32 flags;
12539                 CLRDATA_ADDRESS addr;
12540                 if (SUCCEEDED(pVal->GetLocationByIndex(0, &flags, &addr)))
12541                 {
12542                     if (flags == CLRDATA_VLOC_REGISTER)
12543                     {
12544                         ExtOut("<CLR reg> ");
12545                     }
12546                     else
12547                     {
12548                         ExtOut("0x%p ", SOS_PTR(CDA_TO_UL64(addr)));
12549                     }
12550                 }
12551
12552                 // Can I get a name for the item?
12553
12554                 ExtOut("= ");                
12555             }
12556             ULONG32 dwSize = 0;
12557             hr = pVal->GetBytes(0, &dwSize, NULL);
12558             
12559             if (HRESULT_CODE(hr) == ERROR_BUFFER_OVERFLOW)
12560             {
12561                 ArrayHolder<BYTE> pByte = new NOTHROW BYTE[dwSize + 1];
12562                 if (pByte == NULL)
12563                 {
12564                     ReportOOM();
12565                     return E_FAIL;
12566                 }
12567
12568                 hr = pVal->GetBytes(dwSize,&dwSize,pByte);
12569
12570                 if (FAILED(hr))
12571                 {
12572                     ExtOut("<unable to retrieve data>\n");
12573                 }
12574                 else
12575                 {
12576                     ULONG64 outVar = 0;
12577                     switch(dwSize)
12578                     {
12579                         case 1: outVar = *((BYTE *) pByte.GetPtr()); break;
12580                         case 2: outVar = *((short *) pByte.GetPtr()); break;
12581                         case 4: outVar = *((DWORD *) pByte.GetPtr()); break;
12582                         case 8: outVar = *((ULONG64 *) pByte.GetPtr()); break;
12583                         default: outVar = 0;
12584                     }
12585
12586                     if (outVar)
12587                         DMLOut("0x%s\n", DMLObject(outVar));
12588                     else
12589                         ExtOut("0x%p\n", SOS_PTR(outVar));
12590                 }
12591             }
12592             else
12593             {
12594                 ExtOut("<no data>\n");
12595             }
12596             
12597             pVal->Release();
12598         }
12599         
12600         return S_OK;
12601     }
12602
12603 };
12604
12605 #ifndef FEATURE_PAL
12606
12607 WatchCmd g_watchCmd;
12608
12609 // The grand new !Watch command, private to Apollo for now
12610 DECLARE_API(Watch)
12611 {
12612     INIT_API_NOEE();
12613     BOOL bExpression = FALSE;
12614     StringHolder addExpression;
12615     StringHolder aExpression;
12616     StringHolder saveName;
12617     StringHolder sName;
12618     StringHolder expression;
12619     StringHolder filterName;
12620     StringHolder renameOldName;
12621     size_t expandIndex = -1;
12622     size_t removeIndex = -1;
12623     BOOL clear = FALSE;
12624
12625     size_t nArg = 0;
12626     CMDOption option[] = 
12627     {   // name, vptr, type, hasValue
12628         {"-add", &addExpression.data, COSTRING, TRUE},
12629         {"-a", &aExpression.data, COSTRING, TRUE},
12630         {"-save", &saveName.data, COSTRING, TRUE},
12631         {"-s", &sName.data, COSTRING, TRUE},
12632         {"-clear", &clear, COBOOL, FALSE},
12633         {"-c", &clear, COBOOL, FALSE},
12634         {"-expand", &expandIndex, COSIZE_T, TRUE},
12635         {"-filter", &filterName.data, COSTRING, TRUE},
12636         {"-r", &removeIndex, COSIZE_T, TRUE},
12637         {"-remove", &removeIndex, COSIZE_T, TRUE},
12638         {"-rename", &renameOldName.data, COSTRING, TRUE},
12639     };
12640
12641     CMDValue arg[] = 
12642     {   // vptr, type
12643         {&expression.data, COSTRING}
12644     };
12645     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
12646     {
12647         return Status;
12648     }
12649
12650     if(addExpression.data != NULL || aExpression.data != NULL)
12651     {
12652         WCHAR pAddExpression[MAX_EXPRESSION];
12653         swprintf_s(pAddExpression, MAX_EXPRESSION, W("%S"), addExpression.data != NULL ? addExpression.data : aExpression.data);
12654         Status = g_watchCmd.Add(pAddExpression);
12655     }
12656     else if(removeIndex != -1)
12657     {
12658         if(removeIndex <= 0)
12659         {
12660             ExtOut("Index must be a postive decimal number\n");
12661         }
12662         else
12663         {
12664             Status = g_watchCmd.Remove((int)removeIndex);
12665             if(Status == S_OK)
12666                 ExtOut("Watch expression #%d has been removed\n", removeIndex);
12667             else if(Status == S_FALSE)
12668                 ExtOut("There is no watch expression with index %d\n", removeIndex);
12669             else
12670                 ExtOut("Unknown failure 0x%x removing watch expression\n", Status);
12671         }
12672     }
12673     else if(saveName.data != NULL || sName.data != NULL)
12674     {
12675         WCHAR pSaveName[MAX_EXPRESSION];
12676         swprintf_s(pSaveName, MAX_EXPRESSION, W("%S"), saveName.data != NULL ? saveName.data : sName.data);
12677         Status = g_watchCmd.SaveList(pSaveName);
12678     }
12679     else if(clear)
12680     {
12681         g_watchCmd.Clear();
12682     }
12683     else if(renameOldName.data != NULL)
12684     {
12685         if(nArg != 1)
12686         {
12687              ExtOut("Must provide an old and new name. Usage: !watch -rename <old_name> <new_name>.\n");
12688              return S_FALSE;
12689         }
12690         WCHAR pOldName[MAX_EXPRESSION];
12691         swprintf_s(pOldName, MAX_EXPRESSION, W("%S"), renameOldName.data);
12692         WCHAR pNewName[MAX_EXPRESSION];
12693         swprintf_s(pNewName, MAX_EXPRESSION, W("%S"), expression.data);
12694         g_watchCmd.RenameList(pOldName, pNewName);
12695     }
12696     // print the tree, possibly with filtering and/or expansion
12697     else if(expandIndex != -1 || expression.data == NULL)
12698     {
12699         WCHAR pExpression[MAX_EXPRESSION];
12700         pExpression[0] = '\0';
12701
12702         if(expandIndex != -1)
12703         {
12704             if(expression.data != NULL)
12705             {
12706                 swprintf_s(pExpression, MAX_EXPRESSION, W("%S"), expression.data);
12707             }
12708             else
12709             {
12710                 ExtOut("No expression was provided. Usage !watch -expand <index> <expression>\n");
12711                 return S_FALSE;
12712             }
12713         }
12714         WCHAR pFilterName[MAX_EXPRESSION];
12715         pFilterName[0] = '\0';
12716
12717         if(filterName.data != NULL)
12718         {
12719             swprintf_s(pFilterName, MAX_EXPRESSION, W("%S"), filterName.data);
12720         }
12721
12722         g_watchCmd.Print((int)expandIndex, pExpression, pFilterName);
12723     }
12724     else
12725     {
12726         ExtOut("Unrecognized argument: %s\n", expression.data);
12727     }
12728
12729     return Status;
12730 }
12731
12732 #endif // FEATURE_PAL
12733
12734 DECLARE_API(ClrStack)
12735 {
12736     INIT_API();
12737
12738     BOOL bAll = FALSE;    
12739     BOOL bParams = FALSE;
12740     BOOL bLocals = FALSE;
12741     BOOL bSuppressLines = FALSE;
12742     BOOL bICorDebug = FALSE;
12743     BOOL bGC = FALSE;
12744     BOOL dml = FALSE;
12745     BOOL bFull = FALSE;
12746     BOOL bDisplayRegVals = FALSE;
12747     BOOL bAllThreads = FALSE;    
12748     DWORD frameToDumpVariablesFor = -1;
12749     StringHolder cvariableName;
12750     ArrayHolder<WCHAR> wvariableName = new NOTHROW WCHAR[mdNameLen];
12751     if (wvariableName == NULL)
12752     {
12753         ReportOOM();
12754         return E_OUTOFMEMORY;
12755     }
12756
12757     memset(wvariableName, 0, sizeof(wvariableName));
12758
12759     size_t nArg = 0;
12760     CMDOption option[] = 
12761     {   // name, vptr, type, hasValue
12762         {"-a", &bAll, COBOOL, FALSE},
12763         {"-all", &bAllThreads, COBOOL, FALSE},
12764         {"-p", &bParams, COBOOL, FALSE},
12765         {"-l", &bLocals, COBOOL, FALSE},
12766         {"-n", &bSuppressLines, COBOOL, FALSE},
12767         {"-i", &bICorDebug, COBOOL, FALSE},
12768         {"-gc", &bGC, COBOOL, FALSE},
12769         {"-f", &bFull, COBOOL, FALSE},
12770         {"-r", &bDisplayRegVals, COBOOL, FALSE },
12771 #ifndef FEATURE_PAL
12772         {"/d", &dml, COBOOL, FALSE},
12773 #endif
12774     };
12775     CMDValue arg[] = 
12776     {   // vptr, type
12777         {&cvariableName.data, COSTRING},
12778         {&frameToDumpVariablesFor, COSIZE_T},
12779     };
12780     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
12781     {
12782         return Status;
12783     }
12784
12785     EnableDMLHolder dmlHolder(dml);
12786     if (bAll || bParams || bLocals)
12787     {
12788         // No parameter or local supports for minidump case!
12789         MINIDUMP_NOT_SUPPORTED();        
12790     }
12791
12792     if (bAll)
12793     {
12794         bParams = bLocals = TRUE;
12795     }
12796
12797     if (bICorDebug)
12798     {
12799         if(nArg > 0)
12800         {
12801             bool firstParamIsNumber = true;
12802             for(DWORD i = 0; i < strlen(cvariableName.data); i++)
12803                 firstParamIsNumber = firstParamIsNumber && isdigit(cvariableName.data[i]);
12804
12805             if(firstParamIsNumber && nArg == 1)
12806             {
12807                 frameToDumpVariablesFor = (DWORD)GetExpression(cvariableName.data);
12808                 cvariableName.data[0] = '\0';
12809             }
12810         }
12811         if(cvariableName.data != NULL && strlen(cvariableName.data) > 0)
12812             swprintf_s(wvariableName, mdNameLen, W("%S\0"), cvariableName.data);
12813         
12814         if(_wcslen(wvariableName) > 0)
12815             bParams = bLocals = TRUE;
12816
12817         EnableDMLHolder dmlHolder(TRUE);
12818         return ClrStackImplWithICorDebug::ClrStackFromPublicInterface(bParams, bLocals, FALSE, wvariableName, frameToDumpVariablesFor);
12819     }
12820     
12821     if (bAllThreads) {
12822         ClrStackImpl::PrintAllThreads(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals);
12823     }
12824     else {
12825         ClrStackImpl::PrintCurrentThread(bParams, bLocals, bSuppressLines, bGC, bFull, bDisplayRegVals);
12826     }
12827     
12828     return S_OK;
12829 }
12830
12831 #ifndef FEATURE_PAL
12832
12833 BOOL IsMemoryInfoAvailable()
12834 {
12835     ULONG Class;
12836     ULONG Qualifier;
12837     g_ExtControl->GetDebuggeeType(&Class,&Qualifier);
12838     if (Qualifier == DEBUG_DUMP_SMALL) 
12839     {
12840         g_ExtControl->GetDumpFormatFlags(&Qualifier);
12841         if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY) == 0)            
12842         {
12843             if ((Qualifier & DEBUG_FORMAT_USER_SMALL_FULL_MEMORY_INFO) == 0)
12844             {
12845                 return FALSE;
12846             }            
12847         }
12848     }        
12849     return TRUE;
12850 }
12851
12852 DECLARE_API( VMMap )
12853 {
12854     INIT_API();
12855
12856     if (IsMiniDumpFile() || !IsMemoryInfoAvailable())
12857     {
12858         ExtOut("!VMMap requires a full memory dump (.dump /ma) or a live process.\n");
12859     }
12860     else
12861     {
12862         vmmap();
12863     }
12864
12865     return Status;
12866 }   // DECLARE_API( vmmap )
12867
12868 DECLARE_API( SOSFlush )
12869 {
12870     INIT_API();
12871
12872     g_clrData->Flush();
12873     
12874     return Status;
12875 }   // DECLARE_API( SOSFlush )
12876
12877 DECLARE_API( VMStat )
12878 {
12879     INIT_API();
12880
12881     if (IsMiniDumpFile() || !IsMemoryInfoAvailable())
12882     {
12883         ExtOut("!VMStat requires a full memory dump (.dump /ma) or a live process.\n");
12884     }
12885     else
12886     {
12887         vmstat();
12888     }
12889
12890     return Status;
12891 }   // DECLARE_API( vmmap )
12892
12893 /**********************************************************************\
12894 * Routine Description:                                                 *
12895 *                                                                      *
12896 *    This function saves a dll to a file.                              *  
12897 *                                                                      *
12898 \**********************************************************************/
12899 DECLARE_API(SaveModule)
12900 {
12901     INIT_API();
12902     MINIDUMP_NOT_SUPPORTED();    
12903
12904     StringHolder Location;
12905     DWORD_PTR moduleAddr = NULL;
12906     BOOL bIsImage;
12907
12908     CMDValue arg[] = 
12909     {   // vptr, type
12910         {&moduleAddr, COHEX},
12911         {&Location.data, COSTRING}
12912     };
12913     size_t nArg;
12914     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) 
12915     {
12916         return Status;
12917     }
12918     if (nArg != 2)
12919     {
12920         ExtOut("Usage: SaveModule <address> <file to save>\n");
12921         return Status;
12922     }
12923     if (moduleAddr == 0) {
12924         ExtOut ("Invalid arg\n");
12925         return Status;
12926     }
12927
12928     char* ptr = Location.data;
12929     
12930     DWORD_PTR dllBase = 0;
12931     ULONG64 base;
12932     if (g_ExtSymbols->GetModuleByOffset(TO_CDADDR(moduleAddr),0,NULL,&base) == S_OK)
12933     {
12934         dllBase = TO_TADDR(base);
12935     }
12936     else if (IsModule(moduleAddr))
12937     {        
12938         DacpModuleData module;
12939         module.Request(g_sos, TO_CDADDR(moduleAddr));
12940         dllBase = TO_TADDR(module.ilBase);
12941         if (dllBase == 0)
12942         {
12943             ExtOut ("Module does not have base address\n");
12944             return Status;
12945         }
12946     }
12947     else
12948     {
12949         ExtOut ("%p is not a Module or base address\n", SOS_PTR(moduleAddr));
12950         return Status;
12951     }
12952
12953     MEMORY_BASIC_INFORMATION64 mbi;
12954     if (FAILED(g_ExtData2->QueryVirtual(TO_CDADDR(dllBase), &mbi)))
12955     {
12956         ExtOut("Failed to retrieve information about segment %p", SOS_PTR(dllBase));
12957         return Status;
12958     }
12959
12960     // module loaded as an image or mapped as a flat file?
12961     bIsImage = (mbi.Type == MEM_IMAGE);
12962
12963     IMAGE_DOS_HEADER DosHeader;
12964     if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase), &DosHeader, sizeof(DosHeader), NULL) != S_OK)
12965         return S_FALSE;
12966
12967     IMAGE_NT_HEADERS Header;
12968     if (g_ExtData->ReadVirtual(TO_CDADDR(dllBase + DosHeader.e_lfanew), &Header, sizeof(Header), NULL) != S_OK)
12969         return S_FALSE;
12970
12971     DWORD_PTR sectionAddr = dllBase + DosHeader.e_lfanew + offsetof(IMAGE_NT_HEADERS,OptionalHeader)
12972             + Header.FileHeader.SizeOfOptionalHeader;    
12973
12974     IMAGE_SECTION_HEADER section;
12975     struct MemLocation
12976     {
12977         DWORD_PTR VAAddr;
12978         DWORD_PTR VASize;
12979         DWORD_PTR FileAddr;
12980         DWORD_PTR FileSize;
12981     };
12982
12983     int nSection = Header.FileHeader.NumberOfSections;
12984     ExtOut("%u sections in file\n",nSection);
12985     MemLocation *memLoc = (MemLocation*)_alloca(nSection*sizeof(MemLocation));
12986     int indxSec = -1;
12987     int slot;
12988     for (int n = 0; n < nSection; n++)
12989     {
12990         if (g_ExtData->ReadVirtual(TO_CDADDR(sectionAddr), &section, sizeof(section), NULL) == S_OK)
12991         {
12992             for (slot = 0; slot <= indxSec; slot ++)
12993                 if (section.PointerToRawData < memLoc[slot].FileAddr)
12994                     break;
12995
12996             for (int k = indxSec; k >= slot; k --)
12997                 memcpy(&memLoc[k+1], &memLoc[k], sizeof(MemLocation));
12998
12999             memLoc[slot].VAAddr = section.VirtualAddress;
13000             memLoc[slot].VASize = section.Misc.VirtualSize;
13001             memLoc[slot].FileAddr = section.PointerToRawData;
13002             memLoc[slot].FileSize = section.SizeOfRawData;
13003             ExtOut("section %d - VA=%x, VASize=%x, FileAddr=%x, FileSize=%x\n",
13004                 n, memLoc[slot].VAAddr,memLoc[slot]. VASize,memLoc[slot].FileAddr,
13005                 memLoc[slot].FileSize);
13006             indxSec ++;
13007         }
13008         else
13009         {
13010             ExtOut("Fail to read PE section info\n");
13011             return Status;
13012         }
13013         sectionAddr += sizeof(section);
13014     }
13015
13016     if (ptr[0] == '\0')
13017     {
13018         ExtOut ("File not specified\n");
13019         return Status;
13020     }
13021
13022     PCSTR file = ptr;
13023     ptr += strlen(ptr)-1;
13024     while (isspace(*ptr))
13025     {
13026         *ptr = '\0';
13027         ptr --;
13028     }
13029
13030     HANDLE hFile = CreateFileA(file,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL);
13031     if (hFile == INVALID_HANDLE_VALUE)
13032     {
13033         ExtOut ("Fail to create file %s\n", file);
13034         return Status;
13035     }
13036
13037     ULONG pageSize = OSPageSize();
13038     char *buffer = (char *)_alloca(pageSize);
13039     DWORD nRead;
13040     DWORD nWrite;
13041     
13042     // NT PE Headers
13043     TADDR dwAddr = dllBase;
13044     TADDR dwEnd = dllBase + Header.OptionalHeader.SizeOfHeaders;
13045     while (dwAddr < dwEnd)
13046     {
13047         nRead = pageSize;
13048         if (dwEnd - dwAddr < nRead)
13049             nRead = (ULONG)(dwEnd - dwAddr);
13050
13051         if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK)
13052         {
13053             WriteFile(hFile,buffer,nRead,&nWrite,NULL);
13054         }
13055         else
13056         {
13057             ExtOut ("Fail to read memory\n");
13058             goto end;
13059         }
13060         dwAddr += nRead;
13061     }
13062
13063     for (slot = 0; slot <= indxSec; slot ++)
13064     {
13065         dwAddr = dllBase + (bIsImage ? memLoc[slot].VAAddr : memLoc[slot].FileAddr);
13066         dwEnd = memLoc[slot].FileSize + dwAddr - 1;
13067
13068         while (dwAddr <= dwEnd)
13069         {
13070             nRead = pageSize;
13071             if (dwEnd - dwAddr + 1 < pageSize)
13072                 nRead = (ULONG)(dwEnd - dwAddr + 1);
13073             
13074             if (g_ExtData->ReadVirtual(TO_CDADDR(dwAddr), buffer, nRead, &nRead) == S_OK)
13075             {
13076                 WriteFile(hFile,buffer,nRead,&nWrite,NULL);
13077             }
13078             else
13079             {
13080                 ExtOut ("Fail to read memory\n");
13081                 goto end;
13082             }
13083             dwAddr += pageSize;
13084         }
13085     }
13086 end:
13087     CloseHandle (hFile);
13088     return Status;
13089 }
13090
13091 #ifdef _DEBUG
13092 DECLARE_API(dbgout)
13093 {
13094     INIT_API();
13095
13096     BOOL bOff = FALSE;
13097
13098     CMDOption option[] = 
13099     {   // name, vptr, type, hasValue
13100         {"-off", &bOff, COBOOL, FALSE},
13101     };
13102
13103     if (!GetCMDOption(args, option, _countof(option), NULL, 0, NULL))
13104     {
13105         return Status;
13106     }    
13107
13108     Output::SetDebugOutputEnabled(!bOff);
13109     return Status;
13110 }
13111 DECLARE_API(filthint)
13112 {
13113     INIT_API();
13114
13115     BOOL bOff = FALSE;
13116     DWORD_PTR filter = 0;
13117
13118     CMDOption option[] = 
13119     {   // name, vptr, type, hasValue
13120         {"-off", &bOff, COBOOL, FALSE},
13121     };
13122     CMDValue arg[] = 
13123     {   // vptr, type
13124         {&filter, COHEX}
13125     };
13126     size_t nArg;
13127     if (!GetCMDOption(args, option, _countof(option),
13128                       arg, _countof(arg), &nArg)) 
13129     {
13130         return Status;
13131     }    
13132     if (bOff)
13133     {
13134         g_filterHint = 0;
13135         return Status;
13136     }
13137
13138     g_filterHint = filter;
13139     return Status;
13140 }
13141 #endif // _DEBUG
13142
13143 #endif // FEATURE_PAL
13144
13145 static HRESULT DumpMDInfoBuffer(DWORD_PTR dwStartAddr, DWORD Flags, ULONG64 Esp, 
13146         ULONG64 IPAddr, StringOutput& so)
13147 {
13148 #define DOAPPEND(str)         \
13149     do { \
13150     if (!so.Append((str))) {  \
13151     return E_OUTOFMEMORY; \
13152     }} while (0)
13153
13154     // Should we skip explicit frames?  They are characterized by Esp = 0, && Eip = 0 or 1.
13155     // See comment in FormatGeneratedException() for explanation why on non_IA64 Eip is 1, and not 0
13156     if (!(Flags & SOS_STACKTRACE_SHOWEXPLICITFRAMES) && (Esp == 0) && (IPAddr == 1))
13157     {
13158         return S_FALSE;
13159     }
13160
13161     DacpMethodDescData MethodDescData;
13162     if (MethodDescData.Request(g_sos, TO_CDADDR(dwStartAddr)) != S_OK)
13163     {
13164         return E_FAIL;
13165     }
13166
13167     ArrayHolder<WCHAR> wszNameBuffer = new WCHAR[MAX_LONGPATH+1];
13168
13169     if (Flags & SOS_STACKTRACE_SHOWADDRESSES)
13170     {
13171         _snwprintf_s(wszNameBuffer, MAX_LONGPATH, MAX_LONGPATH, W("%p %p "), (void*)(size_t) Esp, (void*)(size_t) IPAddr); // _TRUNCATE
13172         DOAPPEND(wszNameBuffer);
13173     }
13174
13175     DacpModuleData dmd;
13176     BOOL bModuleNameWorked = FALSE;
13177     ULONG64 addrInModule = IPAddr;
13178     if (dmd.Request(g_sos, MethodDescData.ModulePtr) == S_OK)
13179     {
13180         CLRDATA_ADDRESS base = 0;
13181         if (g_sos->GetPEFileBase(dmd.File, &base) == S_OK)
13182         {
13183             if (base)
13184             {
13185                 addrInModule = base;
13186             }
13187         }
13188     }
13189     ULONG Index;
13190     ULONG64 base;
13191     if (g_ExtSymbols->GetModuleByOffset(UL64_TO_CDA(addrInModule), 0, &Index, &base) == S_OK)
13192     {                                    
13193         ArrayHolder<char> szModuleName = new char[MAX_LONGPATH+1];
13194         if (g_ExtSymbols->GetModuleNames(Index, base, NULL, 0, NULL, szModuleName, MAX_LONGPATH, NULL, NULL, 0, NULL) == S_OK)
13195         {
13196             MultiByteToWideChar (CP_ACP, 0, szModuleName, MAX_LONGPATH, wszNameBuffer, MAX_LONGPATH);
13197             DOAPPEND (wszNameBuffer);
13198             bModuleNameWorked = TRUE;
13199         }
13200     }
13201 #ifdef FEATURE_PAL
13202     else
13203     {
13204         if (g_sos->GetPEFileName(dmd.File, MAX_LONGPATH, wszNameBuffer, NULL) == S_OK)
13205         {
13206             if (wszNameBuffer[0] != W('\0'))
13207             {
13208                 WCHAR *pJustName = _wcsrchr(wszNameBuffer, DIRECTORY_SEPARATOR_CHAR_W);
13209                 if (pJustName == NULL)
13210                     pJustName = wszNameBuffer - 1;
13211
13212                 DOAPPEND(pJustName + 1);
13213                 bModuleNameWorked = TRUE;
13214             }
13215         }
13216     }
13217 #endif // FEATURE_PAL
13218
13219     // Under certain circumstances DacpMethodDescData::GetMethodDescName() 
13220     //   returns a module qualified method name
13221     HRESULT hr = g_sos->GetMethodDescName(dwStartAddr, MAX_LONGPATH, wszNameBuffer, NULL);
13222
13223     WCHAR* pwszMethNameBegin = (hr != S_OK ? NULL : _wcschr(wszNameBuffer, L'!'));
13224     if (!bModuleNameWorked && hr == S_OK && pwszMethNameBegin != NULL)
13225     {
13226         // if we weren't able to get the module name, but GetMethodDescName returned
13227         // the module as part of the returned method name, use this data
13228         DOAPPEND(wszNameBuffer);
13229     }
13230     else
13231     {
13232         if (!bModuleNameWorked)
13233         {
13234             DOAPPEND (W("UNKNOWN"));
13235         }
13236         DOAPPEND(W("!"));
13237         if (hr == S_OK)
13238         {
13239             // the module name we retrieved above from debugger will take 
13240             // precedence over the name possibly returned by GetMethodDescName()
13241             DOAPPEND(pwszMethNameBegin != NULL ? (pwszMethNameBegin+1) : (WCHAR *)wszNameBuffer);
13242         }
13243         else
13244         {
13245             DOAPPEND(W("UNKNOWN"));
13246         }
13247     }
13248
13249     ULONG64 Displacement = (IPAddr - MethodDescData.NativeCodeAddr);
13250     if (Displacement)
13251     {
13252         _snwprintf_s(wszNameBuffer, MAX_LONGPATH, MAX_LONGPATH, W("+%#x"), Displacement); // _TRUNCATE
13253         DOAPPEND (wszNameBuffer);
13254     }
13255
13256     return S_OK;
13257 #undef DOAPPEND
13258 }
13259
13260 BOOL AppendContext(LPVOID pTransitionContexts, size_t maxCount, size_t *pcurCount, size_t uiSizeOfContext,
13261     CROSS_PLATFORM_CONTEXT *context)
13262 {
13263     if (pTransitionContexts == NULL || *pcurCount >= maxCount)
13264     {
13265         ++(*pcurCount);
13266         return FALSE;
13267     }
13268     if (uiSizeOfContext == sizeof(StackTrace_SimpleContext))
13269     {
13270         StackTrace_SimpleContext *pSimple = (StackTrace_SimpleContext *) pTransitionContexts;
13271         g_targetMachine->FillSimpleContext(&pSimple[*pcurCount], context);
13272     }
13273     else if (uiSizeOfContext == g_targetMachine->GetContextSize())
13274     {
13275         // FillTargetContext ensures we only write uiSizeOfContext bytes in pTransitionContexts
13276         // and not sizeof(CROSS_PLATFORM_CONTEXT) bytes (which would overrun).
13277         g_targetMachine->FillTargetContext(pTransitionContexts, context, (int)(*pcurCount));
13278     }
13279     else
13280     {
13281         return FALSE;
13282     }
13283     ++(*pcurCount);
13284     return TRUE;
13285 }
13286
13287 HRESULT CALLBACK ImplementEFNStackTrace(
13288     PDEBUG_CLIENT client,
13289     __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
13290     size_t *puiTextLength,
13291     LPVOID pTransitionContexts,
13292     size_t *puiTransitionContextCount,
13293     size_t uiSizeOfContext,
13294     DWORD Flags) 
13295 {
13296
13297 #define DOAPPEND(str) if (!so.Append((str))) { \
13298     Status = E_OUTOFMEMORY;                    \
13299     goto Exit;                                 \
13300 }
13301
13302     HRESULT Status = E_FAIL;    
13303     StringOutput so;
13304     size_t transitionContextCount = 0;
13305
13306     if (puiTextLength == NULL)
13307     {
13308         return E_INVALIDARG;
13309     }
13310
13311     if (pTransitionContexts)
13312     {
13313         if (puiTransitionContextCount == NULL)
13314         {
13315             return E_INVALIDARG;
13316         }
13317
13318         // Do error checking on context size
13319         if ((uiSizeOfContext != g_targetMachine->GetContextSize()) &&
13320             (uiSizeOfContext != sizeof(StackTrace_SimpleContext)))
13321         {
13322             return E_INVALIDARG;
13323         }
13324     }
13325
13326     IXCLRDataStackWalk *pStackWalk = NULL;
13327     IXCLRDataTask* Task;
13328     ULONG ThreadId;
13329
13330     if ((Status = g_ExtSystem->GetCurrentThreadSystemId(&ThreadId)) != S_OK ||
13331         (Status = g_clrData->GetTaskByOSThreadID(ThreadId, &Task)) != S_OK)
13332     {
13333         // Not a managed thread.
13334         return SOS_E_NOMANAGEDCODE;
13335     }
13336
13337     Status = Task->CreateStackWalk(CLRDATA_SIMPFRAME_UNRECOGNIZED |
13338                                    CLRDATA_SIMPFRAME_MANAGED_METHOD |
13339                                    CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
13340                                    CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
13341                                    &pStackWalk);
13342
13343     Task->Release();
13344
13345     if (Status != S_OK)
13346     {
13347         if (Status == E_FAIL)
13348         {
13349             return SOS_E_NOMANAGEDCODE;
13350         }
13351         return Status;
13352     }
13353
13354 #ifdef _TARGET_WIN64_
13355     ULONG numFrames = 0;
13356     BOOL bInNative = TRUE;
13357
13358     Status = GetContextStackTrace(ThreadId, &numFrames);
13359     if (FAILED(Status))
13360     {
13361         goto Exit;
13362     }
13363
13364     for (ULONG i = 0; i < numFrames; i++)
13365     {
13366         PDEBUG_STACK_FRAME pCur = g_Frames + i;                
13367
13368         CLRDATA_ADDRESS pMD;
13369         if (g_sos->GetMethodDescPtrFromIP(pCur->InstructionOffset, &pMD) == S_OK)
13370         {
13371             if (bInNative || transitionContextCount==0)
13372             {
13373                 // We only want to list one transition frame if there are multiple frames.
13374                 bInNative = FALSE;
13375
13376                 DOAPPEND (W("(TransitionMU)\n"));
13377                 // For each transition, we need to store the context information
13378                 if (puiTransitionContextCount)
13379                 {
13380                     // below we cast the i-th AMD64_CONTEXT to CROSS_PLATFORM_CONTEXT
13381                     AppendContext (pTransitionContexts, *puiTransitionContextCount, 
13382                         &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_X64FrameContexts[i])));
13383                 }
13384                 else
13385                 {
13386                     transitionContextCount++;
13387                 }
13388             }
13389
13390             Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags,
13391                     pCur->StackOffset, pCur->InstructionOffset, so);
13392             if (FAILED(Status))
13393             {
13394                 goto Exit;
13395             }
13396             else if (Status == S_OK)
13397             {
13398                 DOAPPEND (W("\n"));
13399             }
13400             // for S_FALSE do not append anything
13401
13402         }        
13403         else
13404         {
13405             if (!bInNative)
13406             {
13407                 // We only want to list one transition frame if there are multiple frames.
13408                 bInNative = TRUE;
13409
13410                 DOAPPEND (W("(TransitionUM)\n"));
13411                 // For each transition, we need to store the context information
13412                 if (puiTransitionContextCount)
13413                 {
13414                     AppendContext (pTransitionContexts, *puiTransitionContextCount, 
13415                         &transitionContextCount, uiSizeOfContext, (CROSS_PLATFORM_CONTEXT*)(&(g_X64FrameContexts[i])));
13416                 }
13417                 else
13418                 {
13419                     transitionContextCount++;
13420                 }
13421             }
13422         }
13423     }
13424
13425 Exit:
13426 #else // _TARGET_WIN64_
13427
13428 #ifdef _DEBUG
13429     size_t prevLength = 0;
13430     static WCHAR wszNameBuffer[1024]; // should be large enough
13431     wcscpy_s(wszNameBuffer, 1024, W("Frame")); // default value
13432 #endif
13433
13434     BOOL bInNative = TRUE;
13435
13436     UINT frameCount = 0;
13437     do
13438     {
13439         DacpFrameData FrameData;
13440         if ((Status = FrameData.Request(pStackWalk)) != S_OK)
13441         {
13442             goto Exit;
13443         }
13444
13445         CROSS_PLATFORM_CONTEXT context;
13446         if ((Status=pStackWalk->GetContext(DT_CONTEXT_FULL, g_targetMachine->GetContextSize(),
13447                                            NULL, (BYTE *)&context))!=S_OK)
13448         {
13449             goto Exit;
13450         }
13451
13452         ExtDbgOut ( " * Ctx[BSI]:  %08x  %08x  %08x    ", GetBP(context), GetSP(context), GetIP(context) );
13453
13454         CLRDATA_ADDRESS pMD;
13455         if (!FrameData.frameAddr)
13456         {
13457             if (bInNative || transitionContextCount==0)
13458             {
13459                 // We only want to list one transition frame if there are multiple frames.
13460                 bInNative = FALSE;
13461
13462                 DOAPPEND (W("(TransitionMU)\n"));
13463                 // For each transition, we need to store the context information
13464                 if (puiTransitionContextCount)
13465                 {
13466                     AppendContext (pTransitionContexts, *puiTransitionContextCount, 
13467                             &transitionContextCount, uiSizeOfContext, &context);
13468                 }
13469                 else
13470                 {
13471                     transitionContextCount++;
13472                 }                    
13473             }
13474
13475             // we may have a method, try to get the methoddesc
13476             if (g_sos->GetMethodDescPtrFromIP(GetIP(context), &pMD)==S_OK)
13477             {
13478                 Status = DumpMDInfoBuffer((DWORD_PTR) pMD, Flags, 
13479                                           GetSP(context), GetIP(context), so);
13480                 if (FAILED(Status))
13481                 {
13482                     goto Exit;
13483                 }
13484                 else if (Status == S_OK)
13485                 {
13486                     DOAPPEND (W("\n"));
13487                 }
13488                 // for S_FALSE do not append anything
13489             }
13490         }
13491         else
13492         {
13493 #ifdef _DEBUG
13494             if (Output::IsDebugOutputEnabled())
13495             {
13496                 DWORD_PTR vtAddr;
13497                 MOVE(vtAddr, TO_TADDR(FrameData.frameAddr));
13498                 if (g_sos->GetFrameName(TO_CDADDR(vtAddr), 1024, wszNameBuffer, NULL) == S_OK)
13499                     ExtDbgOut("[%ls: %08x] ", wszNameBuffer, FrameData.frameAddr);  
13500                 else
13501                     ExtDbgOut("[Frame: %08x] ", FrameData.frameAddr);
13502             }
13503 #endif
13504             if (!bInNative)
13505             {
13506                 // We only want to list one transition frame if there are multiple frames.
13507                 bInNative = TRUE;
13508
13509                 DOAPPEND (W("(TransitionUM)\n"));
13510                 // For each transition, we need to store the context information
13511                 if (puiTransitionContextCount)
13512                 {
13513                     AppendContext (pTransitionContexts, *puiTransitionContextCount, 
13514                             &transitionContextCount, uiSizeOfContext, &context);
13515                 }
13516                 else
13517                 {
13518                     transitionContextCount++;
13519                 }                    
13520             }
13521         }
13522
13523 #ifdef _DEBUG
13524         if (so.Length() > prevLength)
13525         {
13526             ExtDbgOut ( "%ls", so.String()+prevLength );
13527             prevLength = so.Length();
13528         }
13529         else
13530             ExtDbgOut ( "\n" );
13531 #endif
13532
13533     } 
13534     while ((frameCount++) < MAX_STACK_FRAMES && pStackWalk->Next()==S_OK);
13535     
13536     Status = S_OK;
13537
13538 Exit:
13539 #endif // _TARGET_WIN64_
13540
13541     if (pStackWalk)
13542     {
13543         pStackWalk->Release();
13544         pStackWalk = NULL;
13545     }
13546
13547     // We have finished. Does the user want to copy this data to a buffer?
13548     if (Status == S_OK)
13549     {
13550         if(wszTextOut)
13551         {
13552             // They want at least partial output
13553             wcsncpy_s (wszTextOut, *puiTextLength, so.String(),  *puiTextLength-1); // _TRUNCATE
13554         }
13555         else
13556         {
13557             *puiTextLength = _wcslen (so.String()) + 1;
13558         }
13559
13560         if (puiTransitionContextCount)
13561         {
13562             *puiTransitionContextCount = transitionContextCount;
13563         }
13564     }
13565
13566     return Status;
13567 }
13568
13569 #ifdef FEATURE_PAL
13570 #define PAL_TRY_NAKED PAL_CPP_TRY
13571 #define PAL_EXCEPT_NAKED(disp) PAL_CPP_CATCH_ALL
13572 #define PAL_ENDTRY_NAKED PAL_CPP_ENDTRY
13573 #endif
13574
13575 // TODO: Convert PAL_TRY_NAKED to something that works on the Mac.
13576 HRESULT CALLBACK ImplementEFNStackTraceTry(
13577     PDEBUG_CLIENT client,
13578     __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
13579     size_t *puiTextLength,
13580     LPVOID pTransitionContexts,
13581     size_t *puiTransitionContextCount,
13582     size_t uiSizeOfContext,
13583     DWORD Flags) 
13584 {
13585     HRESULT Status = E_FAIL;
13586
13587     PAL_TRY_NAKED
13588     {
13589         Status = ImplementEFNStackTrace(client, wszTextOut, puiTextLength, 
13590             pTransitionContexts, puiTransitionContextCount,
13591             uiSizeOfContext, Flags);
13592     }
13593     PAL_EXCEPT_NAKED (EXCEPTION_EXECUTE_HANDLER)
13594     {
13595     }        
13596     PAL_ENDTRY_NAKED
13597
13598     return Status;
13599 }
13600
13601 // See sos_stacktrace.h for the contract with the callers regarding the LPVOID arguments.
13602 HRESULT CALLBACK _EFN_StackTrace(
13603     PDEBUG_CLIENT client,
13604     __out_ecount_opt(*puiTextLength) WCHAR wszTextOut[],
13605     size_t *puiTextLength,
13606     __out_bcount_opt(uiSizeOfContext*(*puiTransitionContextCount)) LPVOID pTransitionContexts,
13607     size_t *puiTransitionContextCount,
13608     size_t uiSizeOfContext,
13609     DWORD Flags) 
13610 {
13611     INIT_API();    
13612
13613     Status = ImplementEFNStackTraceTry(client, wszTextOut, puiTextLength, 
13614         pTransitionContexts, puiTransitionContextCount,
13615         uiSizeOfContext, Flags);
13616
13617     return Status;
13618 }
13619
13620
13621 BOOL FormatFromRemoteString(DWORD_PTR strObjPointer, __out_ecount(cchString) PWSTR wszBuffer, ULONG cchString)
13622 {
13623     BOOL bRet = FALSE;
13624
13625     wszBuffer[0] = L'\0';
13626     
13627     DacpObjectData objData;
13628     if (objData.Request(g_sos, TO_CDADDR(strObjPointer))!=S_OK)
13629     {
13630         return bRet;
13631     }
13632
13633     strobjInfo stInfo;
13634
13635     if (MOVE(stInfo, strObjPointer) != S_OK)
13636     {
13637         return bRet;
13638     }
13639     
13640     DWORD dwBufLength = 0;
13641     if (!ClrSafeInt<DWORD>::addition(stInfo.m_StringLength, 1, dwBufLength))
13642     {
13643         ExtOut("<integer overflow>\n");
13644         return bRet;
13645     }
13646
13647     LPWSTR pwszBuf = new NOTHROW WCHAR[dwBufLength];
13648     if (pwszBuf == NULL)
13649     {
13650         return bRet;
13651     }
13652     
13653     if (g_sos->GetObjectStringData(TO_CDADDR(strObjPointer), stInfo.m_StringLength+1, pwszBuf, NULL)!=S_OK)
13654     {
13655         delete [] pwszBuf;
13656         return bRet;
13657     }
13658
13659     // String is in format
13660     // <SP><SP><SP>at <function name>(args,...)\n
13661     // ...
13662     // Parse and copy just <function name>(args,...)
13663
13664     LPWSTR pwszPointer = pwszBuf;
13665
13666     WCHAR PSZSEP[] = W("   at ");
13667
13668     UINT Length = 0;
13669     while(1)
13670     {
13671         if (_wcsncmp(pwszPointer, PSZSEP, _countof(PSZSEP)-1) != 0)
13672         {
13673             delete [] pwszBuf;
13674             return bRet;
13675         }
13676
13677         pwszPointer += _wcslen(PSZSEP);
13678         LPWSTR nextPos = _wcsstr(pwszPointer, PSZSEP);
13679         if (nextPos == NULL)
13680         {
13681             // Done! Note that we are leaving the function before we add the last
13682             // line of stack trace to the output string. This is on purpose because
13683             // this string needs to be merged with a real trace, and the last line
13684             // of the trace will be common to the real trace.
13685             break;
13686         }
13687         WCHAR c = *nextPos;
13688         *nextPos = L'\0';
13689
13690         // Buffer is calculated for sprintf below ("   %p %p %S\n");
13691         WCHAR wszLineBuffer[mdNameLen + 8 + sizeof(size_t)*2];
13692
13693         // Note that we don't add a newline because we have this embedded in wszLineBuffer
13694         swprintf_s(wszLineBuffer, _countof(wszLineBuffer), W("    %p %p %s"), (void*)(size_t)-1, (void*)(size_t)-1, pwszPointer);
13695         Length += (UINT)_wcslen(wszLineBuffer);
13696         
13697         if (wszBuffer)
13698         {            
13699             wcsncat_s(wszBuffer, cchString, wszLineBuffer, _TRUNCATE);
13700         }
13701
13702         *nextPos = c;
13703         // Move to the next line.
13704         pwszPointer = nextPos;
13705     }
13706     
13707     delete [] pwszBuf; 
13708
13709     // Return TRUE only if the stack string had any information that was successfully parsed.
13710     // (Length > 0) is a good indicator of that.
13711     bRet = (Length > 0);
13712     return bRet;
13713 }
13714
13715 HRESULT AppendExceptionInfo(CLRDATA_ADDRESS cdaObj, 
13716     __out_ecount(cchString) PWSTR wszStackString,
13717     ULONG cchString,
13718     BOOL bNestedCase) // If bNestedCase is TRUE, the last frame of the computed stack is left off
13719 {    
13720     DacpObjectData objData;
13721     if (objData.Request(g_sos, cdaObj) != S_OK)
13722     {        
13723         return E_FAIL;
13724     }
13725
13726     // Make sure it is an exception object, and get the MT of Exception
13727     CLRDATA_ADDRESS exceptionMT = isExceptionObj(objData.MethodTable);
13728     if (exceptionMT == NULL)
13729     {
13730         return E_INVALIDARG;
13731     }
13732
13733     // First try to get exception object data using ISOSDacInterface2
13734     DacpExceptionObjectData excData;
13735     BOOL bGotExcData = SUCCEEDED(excData.Request(g_sos, cdaObj));
13736
13737     int iOffset;    
13738     // Is there a _remoteStackTraceString? We'll want to prepend that data.
13739     // We only have string data, so IP/SP info has to be set to -1.
13740     DWORD_PTR strPointer;
13741     if (bGotExcData)
13742     {
13743         strPointer = TO_TADDR(excData.RemoteStackTraceString);
13744     }
13745     else
13746     {
13747         iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, W("_remoteStackTraceString"));
13748         MOVE (strPointer, TO_TADDR(cdaObj) + iOffset);        
13749     }
13750     if (strPointer)
13751     {
13752         WCHAR *pwszBuffer = new NOTHROW WCHAR[cchString];
13753         if (pwszBuffer == NULL)
13754         {
13755             return E_OUTOFMEMORY;
13756         }
13757         
13758         if (FormatFromRemoteString(strPointer, pwszBuffer, cchString))
13759         {
13760             // Prepend this stuff to the string for the user
13761             wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE);
13762         }
13763         delete[] pwszBuffer;
13764     }
13765     
13766     BOOL bAsync = bGotExcData ? IsAsyncException(excData)
13767                               : IsAsyncException(TO_TADDR(cdaObj), TO_TADDR(objData.MethodTable));
13768
13769     DWORD_PTR arrayPtr;
13770     if (bGotExcData)
13771     {
13772         arrayPtr = TO_TADDR(excData.StackTrace);
13773     }
13774     else
13775     {
13776         iOffset = GetObjFieldOffset (cdaObj, objData.MethodTable, W("_stackTrace"));
13777         MOVE (arrayPtr, TO_TADDR(cdaObj) + iOffset);
13778     }
13779
13780     if (arrayPtr)
13781     {
13782         DWORD arrayLen;
13783         MOVE (arrayLen, arrayPtr + sizeof(DWORD_PTR));
13784
13785         if (arrayLen)
13786         {
13787 #ifdef _TARGET_WIN64_
13788             DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD) + sizeof(DWORD);
13789 #else
13790             DWORD_PTR dataPtr = arrayPtr + sizeof(DWORD_PTR) + sizeof(DWORD);
13791 #endif // _TARGET_WIN64_
13792             size_t stackTraceSize = 0;
13793             MOVE (stackTraceSize, dataPtr); // data length is stored at the beginning of the array in this case
13794
13795             DWORD cbStackSize = static_cast<DWORD>(stackTraceSize * sizeof(StackTraceElement));
13796             dataPtr += sizeof(size_t) + sizeof(size_t); // skip the array header, then goes the data
13797             
13798             if (stackTraceSize != 0)
13799             {                
13800                 size_t iLength = FormatGeneratedException (dataPtr, cbStackSize, NULL, 0, bAsync, bNestedCase);
13801                 WCHAR *pwszBuffer = new NOTHROW WCHAR[iLength + 1];
13802                 if (pwszBuffer)
13803                 {
13804                     FormatGeneratedException(dataPtr, cbStackSize, pwszBuffer, iLength + 1, bAsync, bNestedCase);
13805                     wcsncat_s(wszStackString, cchString, pwszBuffer, _TRUNCATE);
13806                     delete[] pwszBuffer;
13807                 }
13808                 else
13809                 {
13810                     return E_OUTOFMEMORY;
13811                 }
13812             }
13813         }
13814     }                   
13815     return S_OK;
13816 }
13817
13818 HRESULT ImplementEFNGetManagedExcepStack(
13819     CLRDATA_ADDRESS cdaStackObj, 
13820     __out_ecount(cchString) PWSTR wszStackString,
13821     ULONG cchString)
13822 {
13823     HRESULT Status = E_FAIL;
13824
13825     if (wszStackString == NULL || cchString == 0)
13826     {
13827         return E_INVALIDARG;
13828     }
13829
13830     CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
13831     DacpThreadData Thread;
13832     BOOL bCanUseThreadContext = TRUE;
13833
13834     ZeroMemory(&Thread, sizeof(DacpThreadData));
13835     
13836     if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
13837     {
13838         // The current thread is unmanaged
13839         bCanUseThreadContext = FALSE;
13840     }
13841
13842     if (cdaStackObj == NULL)    
13843     {
13844         if (!bCanUseThreadContext)
13845         {
13846             return E_INVALIDARG;
13847         }
13848         
13849         TADDR taLTOH = NULL;
13850         if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
13851                             &taLTOH,
13852                             sizeof(taLTOH), NULL)) || (taLTOH==NULL))
13853         {
13854             return Status;
13855         }    
13856         else
13857         {        
13858             cdaStackObj = TO_CDADDR(taLTOH);
13859         }
13860     }
13861
13862     // Put the stack trace header on
13863     AddExceptionHeader(wszStackString, cchString);
13864     
13865     // First is there a nested exception?
13866     if (bCanUseThreadContext && Thread.firstNestedException)    
13867     {
13868         CLRDATA_ADDRESS obj = 0, next = 0;
13869         CLRDATA_ADDRESS currentNested = Thread.firstNestedException;
13870         do
13871         {
13872             Status = g_sos->GetNestedExceptionData(currentNested, &obj, &next);
13873
13874             // deal with the inability to read a nested exception gracefully
13875             if (Status != S_OK)
13876             {
13877                 break;
13878             }
13879                         
13880             Status = AppendExceptionInfo(obj, wszStackString, cchString, TRUE);
13881             currentNested = next;
13882         }
13883         while(currentNested != NULL);                        
13884     }
13885     
13886     Status = AppendExceptionInfo(cdaStackObj, wszStackString, cchString, FALSE);
13887
13888     return Status;
13889 }
13890
13891 // TODO: Enable this when ImplementEFNStackTraceTry is fixed.
13892 // This function, like VerifyDAC, exists for the purpose of testing
13893 // hard-to-get-to SOS APIs.
13894 DECLARE_API(VerifyStackTrace)
13895 {
13896     INIT_API();
13897
13898     BOOL bVerifyManagedExcepStack = FALSE;
13899     CMDOption option[] = 
13900     {   // name, vptr, type, hasValue
13901         {"-ManagedExcepStack", &bVerifyManagedExcepStack, COBOOL, FALSE},
13902     };
13903     
13904     if (!GetCMDOption(args, option, _countof(option), NULL,0,NULL)) 
13905     {
13906         return Status;
13907     }
13908
13909     if (bVerifyManagedExcepStack)
13910     {
13911         CLRDATA_ADDRESS threadAddr = GetCurrentManagedThread();
13912         DacpThreadData Thread;
13913
13914         TADDR taExc = NULL;
13915         if ((threadAddr == NULL) || (Thread.Request(g_sos, threadAddr) != S_OK))
13916         {
13917             ExtOut("The current thread is unmanaged\n");
13918             return Status;
13919         }
13920
13921         TADDR taLTOH = NULL;
13922         if ((!SafeReadMemory(TO_TADDR(Thread.lastThrownObjectHandle),
13923                             &taLTOH,
13924                             sizeof(taLTOH), NULL)) || (taLTOH == NULL))
13925         {
13926             ExtOut("There is no current managed exception on this thread\n");            
13927             return Status;
13928         }    
13929         else
13930         {        
13931             taExc = taLTOH;
13932         }
13933
13934         const SIZE_T cchStr = 4096;
13935         WCHAR *wszStr = (WCHAR *)alloca(cchStr * sizeof(WCHAR));
13936         if (ImplementEFNGetManagedExcepStack(TO_CDADDR(taExc), wszStr, cchStr) != S_OK)
13937         {
13938             ExtOut("Error!\n");
13939             return Status;
13940         }
13941
13942         ExtOut("_EFN_GetManagedExcepStack(%P, wszStr, sizeof(wszStr)) returned:\n", SOS_PTR(taExc));
13943         ExtOut("%S\n", wszStr);
13944
13945         if (ImplementEFNGetManagedExcepStack((ULONG64)NULL, wszStr, cchStr) != S_OK)
13946         {
13947             ExtOut("Error!\n");
13948             return Status;
13949         }
13950
13951         ExtOut("_EFN_GetManagedExcepStack(NULL, wszStr, sizeof(wszStr)) returned:\n");
13952         ExtOut("%S\n", wszStr);
13953     }
13954     else
13955     {
13956         size_t textLength = 0;
13957         size_t contextLength = 0;
13958         Status = ImplementEFNStackTraceTry(client,
13959                                  NULL,
13960                                  &textLength,
13961                                  NULL,
13962                                  &contextLength,
13963                                  0,
13964                                  0);
13965
13966         if (Status != S_OK)
13967         {
13968             ExtOut("Error: %lx\n", Status);
13969             return Status;
13970         }
13971
13972         ExtOut("Number of characters requested: %d\n", textLength);
13973         WCHAR *wszBuffer = new NOTHROW WCHAR[textLength + 1];
13974         if (wszBuffer == NULL)
13975         {
13976             ReportOOM();
13977             return Status;
13978         }
13979
13980         // For the transition contexts buffer the callers are expected to allocate 
13981         // contextLength * sizeof(TARGET_CONTEXT), and not
13982         // contextLength * sizeof(CROSS_PLATFORM_CONTEXT). See sos_stacktrace.h for
13983         // details.
13984         LPBYTE pContexts = new NOTHROW BYTE[contextLength * g_targetMachine->GetContextSize()];
13985
13986         if (pContexts == NULL)
13987         {
13988             ReportOOM();
13989             delete[] wszBuffer;
13990             return Status;
13991         }
13992
13993         Status = ImplementEFNStackTrace(client,
13994                                  wszBuffer,
13995                                  &textLength,
13996                                  pContexts,
13997                                  &contextLength,
13998                                  g_targetMachine->GetContextSize(),
13999                                  0);
14000
14001         if (Status != S_OK)
14002         {
14003             ExtOut("Error: %lx\n", Status);
14004             delete[] wszBuffer;
14005             delete [] pContexts;
14006             return Status;
14007         }
14008
14009         ExtOut("%S\n", wszBuffer);
14010
14011         ExtOut("Context information:\n");
14012         if (IsDbgTargetX86())
14013         {
14014             ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14015                    "Ebp", "Esp", "Eip"); 
14016         }
14017         else if (IsDbgTargetAmd64())
14018         {
14019             ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14020                    "Rbp", "Rsp", "Rip"); 
14021         }
14022         else if (IsDbgTargetArm())
14023         {
14024             ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14025                    "FP", "SP", "PC"); 
14026         }
14027         else
14028         {
14029             ExtOut("Unsupported platform");
14030             delete [] pContexts;
14031             delete[] wszBuffer;
14032             return S_FALSE;
14033         }
14034
14035         for (size_t j=0; j < contextLength; j++)
14036         {
14037             CROSS_PLATFORM_CONTEXT *pCtx = (CROSS_PLATFORM_CONTEXT*)(pContexts + j*g_targetMachine->GetContextSize());
14038             ExtOut("%p %p %p\n", GetBP(*pCtx), GetSP(*pCtx), GetIP(*pCtx));
14039         }
14040
14041         delete [] pContexts;
14042
14043         StackTrace_SimpleContext *pSimple = new NOTHROW StackTrace_SimpleContext[contextLength];
14044         if (pSimple == NULL)
14045         {
14046             ReportOOM();
14047             delete[] wszBuffer;
14048             return Status;
14049         }
14050
14051         Status = ImplementEFNStackTrace(client,
14052                                  wszBuffer,
14053                                  &textLength,
14054                                  pSimple,
14055                                  &contextLength,
14056                                  sizeof(StackTrace_SimpleContext),
14057                                  0);
14058
14059         if (Status != S_OK)
14060         {
14061             ExtOut("Error: %lx\n", Status);
14062             delete[] wszBuffer;
14063             delete [] pSimple;
14064             return Status;
14065         }
14066
14067         ExtOut("Simple Context information:\n");
14068         if (IsDbgTargetX86())
14069             ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14070                        "Ebp", "Esp", "Eip"); 
14071         else if (IsDbgTargetAmd64())
14072                 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14073                        "Rbp", "Rsp", "Rip"); 
14074         else if (IsDbgTargetArm())
14075                 ExtOut("%" POINTERSIZE "s %" POINTERSIZE "s %" POINTERSIZE "s\n",
14076                        "FP", "SP", "PC"); 
14077         else 
14078         {
14079             ExtOut("Unsupported platform");
14080             delete[] wszBuffer;
14081             delete [] pSimple;
14082             return S_FALSE;
14083         }
14084         for (size_t j=0; j < contextLength; j++)
14085         {
14086             ExtOut("%p %p %p\n", SOS_PTR(pSimple[j].FrameOffset),
14087                     SOS_PTR(pSimple[j].StackOffset),
14088                     SOS_PTR(pSimple[j].InstructionOffset));
14089         }
14090         delete [] pSimple;
14091         delete[] wszBuffer;
14092     }
14093
14094     return Status;
14095 }
14096
14097 #ifndef FEATURE_PAL
14098
14099 // This is an internal-only Apollo extension to de-optimize the code
14100 DECLARE_API(SuppressJitOptimization)
14101 {
14102     INIT_API_NOEE();    
14103     MINIDUMP_NOT_SUPPORTED();    
14104
14105     StringHolder onOff;
14106     CMDValue arg[] = 
14107     {   // vptr, type
14108         {&onOff.data, COSTRING},
14109     };
14110     size_t nArg;
14111     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) 
14112     {
14113         return E_FAIL;
14114     }
14115
14116     if(nArg == 1 && (_stricmp(onOff.data, "On") == 0))
14117     {
14118         // if CLR is already loaded, try to change the flags now
14119         if(CheckEEDll() == S_OK)
14120         {
14121             SetNGENCompilerFlags(CORDEBUG_JIT_DISABLE_OPTIMIZATION);
14122         }
14123
14124         if(!g_fAllowJitOptimization)
14125             ExtOut("JIT optimization is already suppressed\n");
14126         else
14127         {
14128             g_fAllowJitOptimization = FALSE;
14129             g_ExtControl->Execute(DEBUG_EXECUTE_NOT_LOGGED, "sxe -c \"!HandleCLRN\" clrn", 0);
14130             ExtOut("JIT optimization will be suppressed\n");
14131         }
14132
14133
14134     }
14135     else if(nArg == 1 && (_stricmp(onOff.data, "Off") == 0))
14136     {
14137         // if CLR is already loaded, try to change the flags now
14138         if(CheckEEDll() == S_OK)
14139         {
14140             SetNGENCompilerFlags(CORDEBUG_JIT_DEFAULT);
14141         }
14142
14143         if(g_fAllowJitOptimization)
14144             ExtOut("JIT optimization is already permitted\n");
14145         else
14146         {
14147             g_fAllowJitOptimization = TRUE;
14148             ExtOut("JIT optimization will be permitted\n");
14149         }
14150     }
14151     else
14152     {
14153         ExtOut("Usage: !SuppressJitOptimization <on|off>\n");
14154     }
14155
14156     return S_OK;
14157 }
14158
14159 // Uses ICorDebug to set the state of desired NGEN compiler flags. This can suppress pre-jitted optimized
14160 // code
14161 HRESULT SetNGENCompilerFlags(DWORD flags)
14162 {
14163     HRESULT hr;
14164
14165     ToRelease<ICorDebugProcess2> proc2;
14166     if(FAILED(hr = InitCorDebugInterface()))
14167     {
14168         ExtOut("SOS: warning, prejitted code optimizations could not be changed. Failed to load ICorDebug HR = 0x%x\n", hr);
14169     }
14170     else if(FAILED(g_pCorDebugProcess->QueryInterface(__uuidof(ICorDebugProcess2), (void**) &proc2)))
14171     {
14172         if(flags != CORDEBUG_JIT_DEFAULT)
14173         {
14174             ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n");
14175         }
14176         else
14177         {
14178             hr = S_OK;
14179         }
14180     }
14181     else if(FAILED(hr = proc2->SetDesiredNGENCompilerFlags(flags)))
14182     {
14183         // Versions of CLR that don't have SetDesiredNGENCompilerFlags DAC-ized will return E_FAIL.
14184         // This was first supported in the clr_triton branch around 4/1/12, Apollo release
14185         // It will likely be supported in desktop CLR during Dev12
14186         if(hr == E_FAIL)
14187         {
14188             if(flags != CORDEBUG_JIT_DEFAULT)
14189             {
14190                 ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support the functionality\n");
14191             }
14192             else
14193             {
14194                 hr = S_OK;
14195             }
14196         }
14197         else if(hr == CORDBG_E_NGEN_NOT_SUPPORTED)
14198         {
14199             if(flags != CORDEBUG_JIT_DEFAULT)
14200             {
14201                 ExtOut("SOS: warning, prejitted code optimizations could not be changed. This CLR version doesn't support NGEN\n");
14202             }
14203             else
14204             {
14205                 hr = S_OK;
14206             }
14207         }
14208         else if(hr == CORDBG_E_MUST_BE_IN_CREATE_PROCESS)
14209         {
14210             DWORD currentFlags = 0;
14211             if(FAILED(hr = proc2->GetDesiredNGENCompilerFlags(&currentFlags)))
14212             {
14213                 ExtOut("SOS: warning, prejitted code optimizations could not be changed. GetDesiredNGENCompilerFlags failed hr=0x%x\n", hr);
14214             }
14215             else if(currentFlags != flags)
14216             {
14217                 ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. This setting is fixed once CLR starts\n");
14218             }
14219             else
14220             {
14221                 hr = S_OK;
14222             }
14223         }
14224         else
14225         {
14226             ExtOut("SOS: warning, prejitted code optimizations could not be changed at this time. SetDesiredNGENCompilerFlags hr = 0x%x\n", hr);
14227         }
14228     }
14229
14230     return hr;
14231 }
14232
14233
14234 // This is an internal-only Apollo extension to save breakpoint/watch state
14235 DECLARE_API(SaveState)
14236 {
14237     INIT_API_NOEE();    
14238     MINIDUMP_NOT_SUPPORTED();    
14239
14240     StringHolder filePath;
14241     CMDValue arg[] = 
14242     {   // vptr, type
14243         {&filePath.data, COSTRING},
14244     };
14245     size_t nArg;
14246     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg)) 
14247     {
14248         return E_FAIL;
14249     }
14250
14251     if(nArg == 0)
14252     {
14253         ExtOut("Usage: !SaveState <file_path>\n");
14254     }
14255
14256     FILE* pFile;
14257     errno_t error = fopen_s(&pFile, filePath.data, "w");
14258     if(error != 0)
14259     {
14260         ExtOut("Failed to open file %s, error=0x%x\n", filePath.data, error);
14261         return E_FAIL;
14262     }
14263
14264     g_bpoints.SaveBreakpoints(pFile);
14265     g_watchCmd.SaveListToFile(pFile);
14266
14267     fclose(pFile);
14268     ExtOut("Session breakpoints and watch expressions saved to %s\n", filePath.data);
14269     return S_OK;
14270 }
14271
14272 #endif // FEATURE_PAL
14273
14274 DECLARE_API(StopOnCatch)
14275 {
14276     INIT_API();    
14277     MINIDUMP_NOT_SUPPORTED();    
14278
14279     g_stopOnNextCatch = TRUE;
14280     ULONG32 flags = 0;
14281     g_clrData->GetOtherNotificationFlags(&flags);
14282     flags |= CLRDATA_NOTIFY_ON_EXCEPTION_CATCH_ENTER;
14283     g_clrData->SetOtherNotificationFlags(flags);
14284     ExtOut("Debuggee will break the next time a managed exception is caught during execution\n");
14285     return S_OK;
14286 }
14287
14288 // This is an undocumented SOS extension command intended to help test SOS
14289 // It causes the Dml output to be printed to the console uninterpretted so
14290 // that a test script can read the commands which are hidden in the markup
14291 DECLARE_API(ExposeDML)
14292 {
14293     Output::SetDMLExposed(true);
14294     return S_OK;
14295 }
14296
14297 // According to kksharma the Windows debuggers always sign-extend
14298 // arguments when calling externally, therefore StackObjAddr 
14299 // conforms to CLRDATA_ADDRESS contract.
14300 HRESULT CALLBACK 
14301 _EFN_GetManagedExcepStack(
14302     PDEBUG_CLIENT client,
14303     ULONG64 StackObjAddr,
14304    __out_ecount (cbString) PSTR szStackString,
14305     ULONG cbString
14306     )
14307 {
14308     INIT_API();
14309
14310     ArrayHolder<WCHAR> tmpStr = new NOTHROW WCHAR[cbString];
14311     if (tmpStr == NULL)
14312     {
14313         ReportOOM();
14314         return E_OUTOFMEMORY;
14315     }
14316
14317     if (FAILED(Status = ImplementEFNGetManagedExcepStack(StackObjAddr, tmpStr, cbString)))
14318     {
14319         return Status;
14320     }
14321
14322     if (WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmpStr, -1, szStackString, cbString, NULL, NULL) == 0)
14323     {
14324         return E_FAIL;
14325     }
14326
14327     return S_OK;
14328 }
14329
14330 // same as _EFN_GetManagedExcepStack, but returns the stack as a wide string.
14331 HRESULT CALLBACK
14332 _EFN_GetManagedExcepStackW(
14333     PDEBUG_CLIENT client,
14334     ULONG64 StackObjAddr,
14335     __out_ecount(cchString) PWSTR wszStackString,
14336     ULONG cchString
14337     )
14338 {
14339     INIT_API();
14340
14341     return ImplementEFNGetManagedExcepStack(StackObjAddr, wszStackString, cchString);
14342 }
14343     
14344 // According to kksharma the Windows debuggers always sign-extend
14345 // arguments when calling externally, therefore objAddr 
14346 // conforms to CLRDATA_ADDRESS contract.
14347 HRESULT CALLBACK 
14348 _EFN_GetManagedObjectName(
14349     PDEBUG_CLIENT client,
14350     ULONG64 objAddr,
14351     __out_ecount (cbName) PSTR szName,
14352     ULONG cbName
14353     )
14354 {
14355     INIT_API ();
14356
14357     if (!sos::IsObject(objAddr, false))
14358     {
14359         return E_INVALIDARG;
14360     }
14361
14362     sos::Object obj = TO_TADDR(objAddr);
14363
14364     if (WideCharToMultiByte(CP_ACP, 0, obj.GetTypeName(), (int) (_wcslen(obj.GetTypeName()) + 1),
14365                             szName, cbName, NULL, NULL) == 0)
14366     {
14367         return E_FAIL;
14368     }
14369     return S_OK;
14370 }
14371
14372 // According to kksharma the Windows debuggers always sign-extend
14373 // arguments when calling externally, therefore objAddr 
14374 // conforms to CLRDATA_ADDRESS contract.
14375 HRESULT CALLBACK 
14376 _EFN_GetManagedObjectFieldInfo(
14377     PDEBUG_CLIENT client,
14378     ULONG64 objAddr,
14379     __out_ecount (mdNameLen) PSTR szFieldName,
14380     PULONG64 pValue,
14381     PULONG pOffset
14382     )
14383 {
14384     INIT_API();
14385     DacpObjectData objData;
14386     LPWSTR fieldName = (LPWSTR)alloca(mdNameLen * sizeof(WCHAR));
14387     
14388     if (szFieldName == NULL || *szFieldName == '\0' ||
14389         objAddr == NULL)
14390     {
14391         return E_FAIL;
14392     }
14393
14394     if (pOffset == NULL && pValue == NULL)
14395     {
14396         // One of these needs to be valid
14397         return E_FAIL;
14398     }
14399         
14400     if (FAILED(objData.Request(g_sos, objAddr)))
14401     {        
14402         return E_FAIL;
14403     }
14404     
14405     MultiByteToWideChar(CP_ACP,0,szFieldName,-1,fieldName,mdNameLen);
14406
14407     int iOffset = GetObjFieldOffset (objAddr, objData.MethodTable, fieldName);
14408     if (iOffset <= 0)
14409     {
14410         return E_FAIL;
14411     }
14412
14413     if (pOffset)
14414     {
14415         *pOffset = (ULONG) iOffset;
14416     }
14417
14418     if (pValue)
14419     {
14420         if (FAILED(g_ExtData->ReadVirtual(UL64_TO_CDA(objAddr + iOffset), pValue, sizeof(ULONG64), NULL)))
14421         {
14422             return E_FAIL;
14423         }
14424     }
14425
14426     return S_OK;
14427 }
14428
14429 #ifdef FEATURE_PAL
14430
14431 #ifdef CREATE_DUMP_SUPPORTED
14432 #include <dumpcommon.h>
14433 #include "datatarget.h"
14434 extern bool CreateDumpForSOS(const char* programPath, const char* dumpPathTemplate, pid_t pid, MINIDUMP_TYPE minidumpType, ICLRDataTarget* dataTarget);
14435 extern bool g_diagnostics;
14436 #endif // CREATE_DUMP_SUPPORTED
14437
14438 DECLARE_API(CreateDump)
14439 {
14440     INIT_API();
14441 #ifdef CREATE_DUMP_SUPPORTED
14442     StringHolder sFileName;
14443     BOOL normal = FALSE;
14444     BOOL withHeap = FALSE;
14445     BOOL triage = FALSE;
14446     BOOL full = FALSE;
14447     BOOL diag = FALSE;
14448
14449     size_t nArg = 0;
14450     CMDOption option[] = 
14451     {   // name, vptr, type, hasValue
14452         {"-n", &normal, COBOOL, FALSE},
14453         {"-h", &withHeap, COBOOL, FALSE},
14454         {"-t", &triage, COBOOL, FALSE},
14455         {"-f", &full, COBOOL, FALSE},
14456         {"-d", &diag, COBOOL, FALSE},
14457     };
14458     CMDValue arg[] = 
14459     {   // vptr, type
14460         {&sFileName.data, COSTRING}
14461     };
14462     if (!GetCMDOption(args, option, _countof(option), arg, _countof(arg), &nArg))
14463     {
14464         return E_FAIL;
14465     }
14466     MINIDUMP_TYPE minidumpType = MiniDumpWithPrivateReadWriteMemory;
14467     ULONG pid = 0; 
14468     g_ExtSystem->GetCurrentProcessId(&pid);
14469
14470     if (full)
14471     {
14472         minidumpType = MiniDumpWithFullMemory;
14473     }
14474     else if (withHeap)
14475     {
14476         minidumpType = MiniDumpWithPrivateReadWriteMemory;
14477     }
14478     else if (triage)
14479     {
14480         minidumpType = MiniDumpFilterTriage;
14481     }
14482     else if (normal)
14483     {
14484         minidumpType = MiniDumpNormal;
14485     }
14486     g_diagnostics = diag;
14487
14488     const char* programPath = g_ExtServices->GetCoreClrDirectory();
14489     const char* dumpPathTemplate = "/tmp/coredump.%d";
14490     ToRelease<ICLRDataTarget> dataTarget = new DataTarget();
14491     dataTarget->AddRef();
14492
14493     if (sFileName.data != nullptr)
14494     {
14495         dumpPathTemplate = sFileName.data;
14496     }
14497     if (!CreateDumpForSOS(programPath, dumpPathTemplate, pid, minidumpType, dataTarget))
14498     {
14499         Status = E_FAIL;
14500     } 
14501 #else // CREATE_DUMP_SUPPORTED
14502     ExtErr("CreateDump not supported on this platform\n");
14503 #endif // CREATE_DUMP_SUPPORTED
14504     return Status;
14505 }
14506
14507 #endif // FEATURE_PAL
14508
14509 void PrintHelp (__in_z LPCSTR pszCmdName)
14510 {
14511     static LPSTR pText = NULL;
14512
14513     if (pText == NULL) {
14514 #ifndef FEATURE_PAL
14515         HGLOBAL hResource = NULL;
14516         HRSRC hResInfo = FindResource (g_hInstance, TEXT ("DOCUMENTATION"), TEXT ("TEXT"));
14517         if (hResInfo) hResource = LoadResource (g_hInstance, hResInfo);
14518         if (hResource) pText = (LPSTR) LockResource (hResource); 
14519         if (pText == NULL)
14520         {
14521             ExtOut("Error loading documentation resource\n");
14522             return;
14523         }
14524 #else
14525         int err = PAL_InitializeDLL();
14526         if(err != 0)
14527         {
14528             ExtOut("Error initializing PAL\n");
14529             return;
14530         }
14531         char lpFilename[MAX_LONGPATH + 12]; // + 12 to make enough room for strcat function.
14532         strcpy_s(lpFilename, _countof(lpFilename), g_ExtServices->GetCoreClrDirectory());
14533         strcat_s(lpFilename, _countof(lpFilename), "sosdocsunix.txt");
14534         
14535         HANDLE hSosDocFile = CreateFileA(lpFilename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
14536         if (hSosDocFile == INVALID_HANDLE_VALUE) {
14537             ExtOut("Error finding documentation file\n");
14538             return;
14539         }
14540
14541         HANDLE hMappedSosDocFile = CreateFileMappingA(hSosDocFile, NULL, PAGE_READONLY, 0, 0, NULL);
14542         CloseHandle(hSosDocFile);
14543         if (hMappedSosDocFile == NULL) { 
14544             ExtOut("Error mapping documentation file\n");
14545             return;
14546         }
14547
14548         pText = (LPSTR)MapViewOfFile(hMappedSosDocFile, FILE_MAP_READ, 0, 0, 0);
14549         CloseHandle(hMappedSosDocFile);
14550         if (pText == NULL)
14551         {
14552             ExtOut("Error loading documentation file\n");
14553             return;
14554         }
14555 #endif
14556     }
14557
14558     // Find our line in the text file
14559     char searchString[MAX_LONGPATH];
14560     sprintf_s(searchString, _countof(searchString), "COMMAND: %s.", pszCmdName);
14561     
14562     LPSTR pStart = strstr(pText, searchString);
14563     LPSTR pEnd = NULL;
14564     if (!pStart)
14565     {
14566         ExtOut("Documentation for %s not found.\n", pszCmdName);
14567         return;
14568     }
14569
14570     // Go to the end of this line:
14571     pStart = strchr(pStart, '\n');
14572     if (!pStart)
14573     {
14574         ExtOut("Expected newline in documentation resource.\n");
14575         return;
14576     }
14577
14578     // Bypass the newline that pStart points to and setup pEnd for the loop below. We set
14579     // pEnd to be the old pStart since we add one to it when we call strstr.
14580     pEnd = pStart++;
14581
14582     // Find the first occurrence of \\ followed by an \r or an \n on a line by itself.
14583     do
14584     {
14585         pEnd = strstr(pEnd+1, "\\\\");
14586     } while (pEnd && ((pEnd[-1] != '\r' && pEnd[-1] != '\n') || (pEnd[3] != '\r' && pEnd[3] != '\n')));
14587
14588     if (pEnd)
14589     {
14590         // We have found a \\ followed by a \r or \n.  Do not print out the character pEnd points
14591         // to, as this will be the first \ (this is why we don't add one to the second parameter).
14592         ExtOut("%.*s", pEnd - pStart, pStart);
14593     }
14594     else
14595     {
14596         // If pEnd is false then we have run to the end of the document.  However, we did find
14597         // the command to print, so we should simply print to the end of the file.  We'll add
14598         // an extra newline here in case the file does not contain one.
14599         ExtOut("%s\n", pStart);
14600     }
14601 }
14602
14603 /**********************************************************************\
14604 * Routine Description:                                                 *
14605 *                                                                      *
14606 *    This function displays the commands available in strike and the   *  
14607 *    arguments passed into each.
14608 *                                                                      *
14609 \**********************************************************************/
14610 DECLARE_API(Help)
14611 {
14612     // Call extension initialization functions directly, because we don't need the DAC dll to be initialized to get help.
14613     HRESULT Status;
14614     __ExtensionCleanUp __extensionCleanUp;
14615     if ((Status = ExtQuery(client)) != S_OK) return Status;
14616     ControlC = FALSE;
14617
14618     StringHolder commandName;
14619     CMDValue arg[] = 
14620     {
14621         {&commandName.data, COSTRING}
14622     };
14623     size_t nArg;
14624     if (!GetCMDOption(args, NULL, 0, arg, _countof(arg), &nArg))
14625     {
14626         return Status;
14627     }
14628
14629     ExtOut("-------------------------------------------------------------------------------\n");
14630
14631     if (nArg == 1)
14632     {        
14633         // Convert commandName to lower-case
14634         LPSTR curChar = commandName.data;
14635         while (*curChar != '\0')
14636         {
14637             if ( ((unsigned) *curChar <= 0x7F) && isupper(*curChar))
14638             {
14639                 *curChar = (CHAR) tolower(*curChar);
14640             }
14641             curChar++;
14642         }
14643
14644         // Strip off leading "!" if the user put that.
14645         curChar = commandName.data;
14646         if (*curChar == '!')
14647             curChar++;
14648         
14649         PrintHelp (curChar);
14650     }
14651     else
14652     {
14653         PrintHelp ("contents");
14654     }
14655     
14656     return S_OK;
14657 }
14658
14659 #if defined(FEATURE_PAL) && defined(_TARGET_WIN64_)
14660
14661 static BOOL 
14662 ReadMemoryAdapter(PVOID address, PVOID buffer, SIZE_T size)
14663 {
14664     ULONG fetched;
14665     HRESULT hr = g_ExtData->ReadVirtual(TO_CDADDR(address), buffer, size, &fetched);
14666     return SUCCEEDED(hr);
14667 }
14668
14669 static BOOL
14670 GetStackFrame(CONTEXT* context, ULONG numNativeFrames)
14671 {
14672     KNONVOLATILE_CONTEXT_POINTERS contextPointers;
14673     memset(&contextPointers, 0, sizeof(contextPointers));
14674
14675     ULONG64 baseAddress;
14676     HRESULT hr = g_ExtSymbols->GetModuleByOffset(context->Rip, 0, NULL, &baseAddress);
14677     if (FAILED(hr))
14678     {
14679         PDEBUG_STACK_FRAME frame = &g_Frames[0];
14680         for (int i = 0; i < numNativeFrames; i++, frame++) {
14681             if (frame->InstructionOffset == context->Rip)
14682             {
14683                 if ((i + 1) >= numNativeFrames) {
14684                     return FALSE;
14685                 }
14686                 memcpy(context, &(g_X64FrameContexts[i + 1]), sizeof(*context));
14687                 return TRUE;
14688             }
14689         }
14690         return FALSE;
14691     }
14692     if (!PAL_VirtualUnwindOutOfProc(context, &contextPointers, baseAddress, ReadMemoryAdapter))
14693     {
14694         return FALSE;
14695     }
14696     return TRUE;
14697 }
14698
14699 static BOOL
14700 UnwindStackFrames(ULONG32 osThreadId)
14701 {
14702     ULONG numNativeFrames = 0;
14703     HRESULT hr = GetContextStackTrace(osThreadId, &numNativeFrames);
14704     if (FAILED(hr))
14705     {
14706         return FALSE;
14707     }
14708     CONTEXT context;
14709     memset(&context, 0, sizeof(context));
14710     context.ContextFlags = CONTEXT_FULL;
14711
14712     hr = g_ExtSystem->GetThreadContextById(osThreadId, CONTEXT_FULL, sizeof(context), (PBYTE)&context);
14713     if (FAILED(hr))
14714     {
14715         return FALSE;
14716     }
14717     TableOutput out(3, POINTERSIZE_HEX, AlignRight);
14718     out.WriteRow("RSP", "RIP", "Call Site");
14719
14720     DEBUG_STACK_FRAME nativeFrame;
14721     memset(&nativeFrame, 0, sizeof(nativeFrame));
14722
14723     do 
14724     {
14725         if (context.Rip == 0)
14726         {
14727             break;
14728         }
14729         nativeFrame.InstructionOffset = context.Rip;
14730         nativeFrame.ReturnOffset = context.Rip;
14731         nativeFrame.FrameOffset = context.Rbp;
14732         nativeFrame.StackOffset = context.Rsp;
14733         ClrStackImpl::PrintNativeStackFrame(out, &nativeFrame, FALSE);
14734
14735     } while (GetStackFrame(&context, numNativeFrames));
14736
14737     return TRUE;
14738 }
14739
14740 #endif // FEATURE_PAL && _TARGET_WIN64_