summaryrefslogtreecommitdiff
path: root/src/ToolBox/SOS/Strike/exts.h
blob: 36b5230c3712a92782fbe30e550e0619857b7f32 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

// ==++==
// 
 
// 
// ==--==
#ifndef __exts_h__
#define __exts_h__

#define KDEXT_64BIT

#include <windows.h>
#include <winternl.h>

#if defined(_MSC_VER)
#pragma warning(disable:4245)   // signed/unsigned mismatch
#pragma warning(disable:4100)   // unreferenced formal parameter
#pragma warning(disable:4201)   // nonstandard extension used : nameless struct/union
#pragma warning(disable:4127)   // conditional expression is constant
#pragma warning(disable:4430)   // missing type specifier: C++ doesn't support default-int
#endif
#include "strike.h"
#include <wdbgexts.h>
#include <dbgeng.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// wdbgexts.h defines StackTrace which interferes with other parts of the
// system that use the StackTrace identifier
#ifdef StackTrace 
#undef StackTrace
#endif

#include "platformspecific.h"

// We need to define the target address type.  This has to be used in the 
// functions that read directly from the debuggee address space, vs. using 
// the DAC to read the DAC-ized data structures.
#include "daccess.h"

#include "gcinfo.h"

// Convert between CLRDATA_ADDRESS and TADDR.
#define TO_TADDR(cdaddr) ((TADDR)(cdaddr))
#define TO_CDADDR(taddr) ((CLRDATA_ADDRESS)(LONG_PTR)(taddr))

// We also need a "correction" macro: there are a number of places in the DAC
// where instead of using the CLRDATA_ADDRESS sign-extension convention
// we 0-extend (most notably DacpGcHeapDetails)
#define NEED_DAC_CLRDATA_ADDRESS_CORRECTION 1
#if NEED_DAC_CLRDATA_ADDRESS_CORRECTION == 1
    // the macro below "corrects" a CDADDR to always represent the
    // sign-extended equivalent ULONG64 value of the original TADDR
    #define UL64_TO_CDA(ul64) (TO_CDADDR(TO_TADDR(ul64)))
#else
    #define UL64_TO_CDA(ul64) (ul64)
#endif // NEED_DAC_CLRDATA_ADDRESS_CORRECTION 1

// The macro below removes the sign extension, returning the  
// equivalent ULONG64 value to the original TADDR. Useful when 
// printing CDA values.
#define CDA_TO_UL64(cda) ((ULONG64)(TO_TADDR(cda)))

typedef struct _TADDR_RANGE
{
    TADDR start;
    TADDR end;
} TADDR_RANGE;

typedef struct _TADDR_SEGINFO
{
    TADDR segAddr;
    TADDR start;
    TADDR end;
} TADDR_SEGINFO;

#include "util.h"

#ifdef __cplusplus
extern "C" {
#endif

// Cleanup tasks to be executed when the extension is unloaded
class OnUnloadTask
{
public:
    FORCEINLINE static void Register(void (*fn)())
    {
        // append a new unload task to the head of the list
        OnUnloadTask *pNew = new OnUnloadTask(fn);
        pNew->pNext = s_pUnloadTaskList;
        s_pUnloadTaskList = pNew;
    }

    static void Run()
    {
        // walk the list of UnloadTasks and execute each in turn
        OnUnloadTask* pCur = s_pUnloadTaskList;
        while (pCur != NULL)
        {
            OnUnloadTask* pNext = pCur->pNext;
            pCur->OnUnloadFn();
            delete pCur;
            pCur = pNext;
        }
        s_pUnloadTaskList = NULL;
    }

private:
    OnUnloadTask(void(*fn)())
        : OnUnloadFn(fn)
        , pNext(NULL)
    { }

private:
    void (*OnUnloadFn)();
    OnUnloadTask* pNext;

    static OnUnloadTask *s_pUnloadTaskList;
};

#ifndef MINIDUMP
 
#define EXIT_API     ExtRelease

// Safe release and NULL.
#define EXT_RELEASE(Unk) \
    ((Unk) != NULL ? ((Unk)->Release(), (Unk) = NULL) : NULL)

extern PDEBUG_CONTROL2       g_ExtControl;
extern PDEBUG_DATA_SPACES    g_ExtData;
extern PDEBUG_SYMBOLS        g_ExtSymbols;
extern PDEBUG_SYSTEM_OBJECTS g_ExtSystem;
extern PDEBUG_REGISTERS      g_ExtRegisters;

#ifndef FEATURE_PAL

// Global variables initialized by query.
extern PDEBUG_CLIENT         g_ExtClient;
extern PDEBUG_DATA_SPACES2   g_ExtData2;
extern PDEBUG_SYMBOLS2       g_ExtSymbols2;
extern PDEBUG_ADVANCED3      g_ExtAdvanced3;

#else // FEATURE_PAL

extern ILLDBServices*        g_ExtServices;    

#endif // FEATURE_PAL

HRESULT
ExtQuery(PDEBUG_CLIENT client);

HRESULT 
ArchQuery(void);

void
ExtRelease(void);

#ifdef _DEBUG
extern DWORD_PTR g_filterHint;
#endif

extern BOOL ControlC;

inline BOOL IsInterrupt()
{
    if (!ControlC && g_ExtControl->GetInterrupt() == S_OK)
    {
        ExtOut("Command cancelled at the user's request.\n");
        ControlC = TRUE;
    }

    return ControlC;
}

//
// undef the wdbgexts
//
#undef DECLARE_API

#define DECLARE_API(extension)     \
CPPMOD HRESULT CALLBACK extension(PDEBUG_CLIENT client, PCSTR args)

class __ExtensionCleanUp
{
public:
    __ExtensionCleanUp(){}
    ~__ExtensionCleanUp(){ExtRelease();}
};

inline void EENotLoadedMessage(HRESULT Status)
{
    ExtOut("Failed to find runtime DLL (%s), 0x%08x\n", MAKEDLLNAME_A("coreclr"), Status);
    ExtOut("Extension commands need it in order to have something to do.\n");
}

inline void DACMessage(HRESULT Status)
{
    ExtOut("Failed to load data access DLL, 0x%08x\n", Status);
#ifndef FEATURE_PAL
    ExtOut("Verify that 1) you have a recent build of the debugger (6.2.14 or newer)\n");
    ExtOut("            2) the file mscordacwks.dll that matches your version of coreclr.dll is \n");
    ExtOut("                in the version directory or on the symbol path\n");
    ExtOut("            3) or, if you are debugging a dump file, verify that the file \n");
    ExtOut("                mscordacwks_<arch>_<arch>_<version>.dll is on your symbol path.\n");
    ExtOut("            4) you are debugging on supported cross platform architecture as \n");
    ExtOut("                the dump file. For example, an ARM dump file must be debugged\n");
    ExtOut("                on an X86 or an ARM machine; an AMD64 dump file must be\n");
    ExtOut("                debugged on an AMD64 machine.\n");
    ExtOut("\n");
    ExtOut("You can also run the debugger command .cordll to control the debugger's\n");
    ExtOut("load of mscordacwks.dll.  .cordll -ve -u -l will do a verbose reload.\n");
    ExtOut("If that succeeds, the SOS command should work on retry.\n");
    ExtOut("\n");
    ExtOut("If you are debugging a minidump, you need to make sure that your executable\n");
    ExtOut("path is pointing to coreclr.dll as well.\n");
#else // FEATURE_PAL
    if (Status == CORDBG_E_MISSING_DEBUGGER_EXPORTS)
    {
        ExtOut("You can run the debugger command 'setclrpath' to control the load of %s.\n", MAKEDLLNAME_A("mscordaccore"));
        ExtOut("If that succeeds, the SOS command should work on retry.\n");
    }
    else
    {
        ExtOut("Can not load or initialize %s. The target runtime may not be initialized.\n", MAKEDLLNAME_A("mscordaccore"));
    }
#endif // FEATURE_PAL
}

HRESULT CheckEEDll();

#define INIT_API_NOEE()                                         \
    HRESULT Status;                                             \
    __ExtensionCleanUp __extensionCleanUp;                      \
    if ((Status = ExtQuery(client)) != S_OK) return Status;     \
    if ((Status = ArchQuery()) != S_OK)      return Status;     \
    ControlC = FALSE;                                           \
    g_bDacBroken = TRUE;                                        \
    g_clrData = NULL;                                           \
    g_sos = NULL;                                        

#define INIT_API_EE()                                           \
    if ((Status = CheckEEDll()) != S_OK)                        \
    {                                                           \
        EENotLoadedMessage(Status);                             \
        return Status;                                          \
    }                                                           

#define INIT_API_NODAC()                                        \
    INIT_API_NOEE()                                             \
    INIT_API_EE()

#define INIT_API_DAC()                                          \
    if ((Status = LoadClrDebugDll()) != S_OK)                   \
    {                                                           \
        DACMessage(Status);                                     \
        return Status;                                          \
    }                                                           \
    g_bDacBroken = FALSE;                                       \
    /* If LoadClrDebugDll() succeeded make sure we release g_clrData. */  \
    /* We may reconsider caching g_clrData in the future */     \
    ToRelease<IXCLRDataProcess> spIDP(g_clrData);               \
    ToRelease<ISOSDacInterface> spISD(g_sos);                   \
    ResetGlobals();

#define INIT_API()                                              \
    INIT_API_NODAC()                                            \
    INIT_API_DAC()

// Attempt to initialize DAC and SOS globals, but do not "return" on failure.
// Instead, mark the failure to initialize the DAC by setting g_bDacBroken to TRUE.
// This should be used from extension commands that should work OK even when no
// runtime is loaded in the debuggee, e.g. DumpLog, DumpStack. These extensions
// and functions they call should test g_bDacBroken before calling any DAC enabled
// feature.
#define INIT_API_NO_RET_ON_FAILURE()                            \
    INIT_API_NOEE()                                             \
    if ((Status = CheckEEDll()) != S_OK)                        \
    {                                                           \
        ExtOut("Failed to find runtime DLL (%s), 0x%08x\n", MAKEDLLNAME_A("coreclr"), Status); \
        ExtOut("Some functionality may be impaired\n");         \
    }                                                           \
    else if ((Status = LoadClrDebugDll()) != S_OK)              \
    {                                                           \
        ExtOut("Failed to load data access DLL (%s), 0x%08x\n", MAKEDLLNAME_A("mscordaccore"), Status); \
        ExtOut("Some functionality may be impaired\n");         \
    }                                                           \
    else                                                        \
    {                                                           \
        g_bDacBroken = FALSE;                                   \
        ResetGlobals();                                         \
    }                                                           \
    /* If LoadClrDebugDll() succeeded make sure we release g_clrData. */  \
    /* We may reconsider caching g_clrData in the future */     \
    ToRelease<ISOSDacInterface> spISD(g_sos);                   \
    ToRelease<IXCLRDataProcess> spIDP(g_clrData);
    
extern BOOL g_bDacBroken;

#define PAGE_ALIGN64(Va) ((ULONG64)((Va) & ~((ULONG64) ((LONG64) (LONG) PageSize - 1))))

extern ULONG PageSize;


//-----------------------------------------------------------------------------------------
//
//  Target platform abstraction
//
//-----------------------------------------------------------------------------------------

// some needed forward declarations
struct StackTrace_SimpleContext;
struct GCEncodingInfo;
struct SOSEHInfo;
class GCDump; 

///
/// IMachine interface
///
/// Note: 
/// The methods accepting target address args take them as size_t==DWORD_PTR==TADDR,
/// which means this can only provide cross platform support for same-word size 
/// architectures (only ARM on x86 currently). Since this is not exposed outside SOS
/// and since the some-word-size limitation exists across EE/DAC/SOS this is not an
/// actual limitation.
///

class IMachine
{
public:
    // Returns the IMAGE_FILE_MACHINE_*** constant corresponding to the target machine
    virtual ULONG GetPlatform() const = 0;
    // Returns the size of the CONTEXT for the target machine
    virtual ULONG GetContextSize() const = 0;

    // Disassembles a managed method specified by the IPBegin-IPEnd range
    virtual void Unassembly(
                TADDR IPBegin, 
                TADDR IPEnd, 
                TADDR IPAskedFor, 
                TADDR GCStressCodeCopy, 
                GCEncodingInfo *pGCEncodingInfo, 
                SOSEHInfo *pEHInfo,
                BOOL bSuppressLines,
                BOOL bDisplayOffsets) const = 0;

    // Validates whether retAddr represents a return address by unassembling backwards.
    // If the instruction before retAddr represents a target-specific call instruction
    // it attempts to identify the target of the call. If successful it sets *whereCalled
    // to the call target, otherwise it sets it to 0xffffffff.
    virtual void IsReturnAddress(
                TADDR retAddr, 
                TADDR* whereCalled) const = 0;

    // If, while unwinding the stack, "PC" represents a known return address in 
    // KiUserExceptionDispatcher, "stack" is used to retrieve an exception context record
    // in "cxr", and an exception record in "exr"
    virtual BOOL GetExceptionContext (
                TADDR stack, 
                TADDR PC, 
                TADDR *cxrAddr, 
                CROSS_PLATFORM_CONTEXT * cxr,
                TADDR *exrAddr, 
                PEXCEPTION_RECORD exr) const = 0;

    // Retrieves stack pointer, frame pointer, and instruction pointer from the target context
    virtual TADDR GetSP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;
    virtual TADDR GetBP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;
    virtual TADDR GetIP(const CROSS_PLATFORM_CONTEXT & ctx) const = 0;

    // Fills dest's data fields from a target specific context
    virtual void  FillSimpleContext(StackTrace_SimpleContext * dest, LPVOID srcCtx) const = 0;
    // Fills a target specific context, destCtx, from the idx-th location in a target specific
    // array of contexts that start at srcCtx
    virtual void  FillTargetContext(LPVOID destCtx, LPVOID srcCtx, int idx = 0) const = 0;

    // Retrieve some target specific output strings
    virtual LPCSTR GetDumpStackHeading() const = 0;
    virtual LPCSTR GetDumpStackObjectsHeading() const = 0;
    virtual LPCSTR GetSPName() const = 0;
    // Retrieves the non-volatile registers reported to the GC
    virtual void GetGCRegisters(LPCSTR** regNames, unsigned int* cntRegs) const = 0;

    typedef void (*printfFtn)(const char* fmt, ...);
    // Dumps the GCInfo
    virtual void DumpGCInfo(GCInfoToken gcInfoToken, unsigned methodSize, printfFtn gcPrintf, bool encBytes, bool bPrintHeader) const = 0;

protected:
    IMachine()           {}
    virtual ~IMachine()  {}
    
private:
    IMachine(const IMachine& machine);      // undefined
    IMachine & operator=(const IMachine&);   // undefined
}; // class IMachine


extern IMachine* g_targetMachine;


inline BOOL IsDbgTargetX86()    { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_I386; }
inline BOOL IsDbgTargetAmd64()  { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_AMD64; }
inline BOOL IsDbgTargetArm()    { return g_targetMachine->GetPlatform() == IMAGE_FILE_MACHINE_ARMNT; }
inline BOOL IsDbgTargetWin64()  { return IsDbgTargetAmd64(); }

/* Returns the instruction pointer for the given CONTEXT.  We need this and its family of
 * functions because certain headers are inconsistantly included on the various platforms,
 * meaning that we cannot use GetIP and GetSP as defined by CLR.
 */
inline CLRDATA_ADDRESS GetIP(const CROSS_PLATFORM_CONTEXT& context)
{
    return TO_CDADDR(g_targetMachine->GetIP(context));
}

/* Returns the stack pointer for the given CONTEXT.
 */
inline CLRDATA_ADDRESS GetSP(const CROSS_PLATFORM_CONTEXT& context)
{
    return TO_CDADDR(g_targetMachine->GetSP(context));
}

/* Returns the base/frame pointer for the given CONTEXT.
 */
inline CLRDATA_ADDRESS GetBP(const CROSS_PLATFORM_CONTEXT& context)
{
    return TO_CDADDR(g_targetMachine->GetBP(context));
}


//-----------------------------------------------------------------------------------------
//
//  api declaration macros & api access macros
//
//-----------------------------------------------------------------------------------------

#ifndef FEATURE_PAL

extern WINDBG_EXTENSION_APIS ExtensionApis;
#define GetExpression (ExtensionApis.lpGetExpressionRoutine)

extern ULONG TargetMachine;
extern ULONG g_TargetClass;
extern ULONG g_VDbgEng;

#else // FEATURE_PAL

#define GetExpression(exp) g_ExtServices->GetExpression(exp)

#endif // FEATURE_PAL

#define CACHE_SIZE  DT_OS_PAGE_SIZE
 
struct ReadVirtualCache
{
    BYTE   m_cache[CACHE_SIZE];
    TADDR  m_startCache;
    BOOL   m_cacheValid;
    ULONG  m_cacheSize;

    ReadVirtualCache() { Clear(); }
    HRESULT Read(TADDR Offset, PVOID Buffer, ULONG BufferSize, PULONG lpcbBytesRead);    
    void Clear() { m_cacheValid = FALSE; m_cacheSize = CACHE_SIZE; }
};

extern ReadVirtualCache *rvCache;

#define MOVE(dst, src) rvCache->Read(TO_TADDR(src), &(dst), sizeof(dst), NULL)
#define MOVEBLOCK(dst, src, size) rvCache->Read(TO_TADDR(src), &(dst), size, NULL)

#define moveN(dst, src)                               \
{                                                     \
    HRESULT ret = MOVE(dst, src);                     \
    if (FAILED(ret)) return ret;                      \
}

#define moveBlockN(dst, src, size)                    \
{                                                     \
    HRESULT ret = MOVEBLOCK(dst, src, size);          \
    if (FAILED(ret)) return ret;                      \
}

// move cross-process: reads memory from the debuggee into
// debugger address space and returns in case of error
#define move_xp(dst, src)                             \
{                                                     \
    HRESULT ret = MOVE(dst, src);                     \
    if (FAILED(ret)) return;                          \
}

#define moveBlock(dst, src, size)                     \
{                                                     \
    HRESULT ret = MOVEBLOCK(dst, src, size);          \
    if (FAILED(ret)) return;                          \
}

#ifdef __cplusplus
#define CPPMOD extern "C"
#else
#define CPPMOD
#endif

#endif

#ifdef __cplusplus
}
#endif

#endif // __exts_h__