Welcome to mirror list, hosted at ThFree Co, Russian Federation.

threadinfo.cpp « createdump « debug « coreclr « src - github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 82509f5275065331bb2d0aa3cbd72afc0e063515 (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
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#include "createdump.h"

#ifndef THUMB_CODE
#define THUMB_CODE 1
#endif

#ifndef __GLIBC__
typedef int __ptrace_request;
#endif

extern CrashInfo* g_crashInfo;

// Helper for UnwindNativeFrames
static void
GetFrameLocation(CONTEXT* pContext, uint64_t* ip, uint64_t* sp)
{
#if defined(__x86_64__)
    *ip = pContext->Rip;
    *sp = pContext->Rsp;
#elif defined(__i386__)
    *ip = pContext->Eip;
    *sp = pContext->Esp;
#elif defined(__aarch64__)
    *ip = pContext->Pc;
    *sp = pContext->Sp;
#elif defined(__arm__)
    *ip = pContext->Pc & ~THUMB_CODE;
    *sp = pContext->Sp;
#endif
}

// Helper for UnwindNativeFrames
static BOOL
ReadMemoryAdapter(PVOID address, PVOID buffer, SIZE_T size)
{
    return g_crashInfo->ReadMemory(address, buffer, size);
}

void
ThreadInfo::UnwindNativeFrames(CONTEXT* pContext)
{
    uint64_t previousSp = 0;
    uint64_t previousIp = 0;
    int ipMatchCount = 0;

    // For each native frame, add a page around the IP and any unwind info not already
    // added in VisitProgramHeader (Linux) and VisitSection (MacOS) to the dump.
    while (true)
    {
        uint64_t ip = 0, sp = 0;
        GetFrameLocation(pContext, &ip, &sp);

        if (ip == 0 || sp <= previousSp) {
            TRACE_VERBOSE("Unwind: sp not increasing or ip == 0 sp %p ip %p\n", (void*)sp, (void*)ip);
            break;
        }
        // Break out of the endless loop if the IP matches over a 1000 times. This is a fallback
        // behavior of libunwind when the module the IP is in doesn't have unwind info and for
        // simple stack overflows. The stack memory is added to the dump in GetThreadStack and
        // it isn't necessary to add the same IP page over and over again. The only place this
        // check won't catch is the stack overflow case that repeats a sequence of IPs over and
        // over.
        if (ip == previousIp)
        {
            if (ipMatchCount++ > 1000)
            {
                TRACE("Unwind: same ip %p over 1000 times\n", (void*)ip);
                break;
            }
        }
        else
        {
            ipMatchCount = 0;
        }

        // Add two pages around the instruction pointer to the core dump
        m_crashInfo.InsertMemoryRegion(ip - PAGE_SIZE, PAGE_SIZE * 2);

        // Look up the ip address to get the module base address
        uint64_t baseAddress = m_crashInfo.GetBaseAddressFromAddress(ip);
        if (baseAddress == 0) {
            TRACE_VERBOSE("Unwind: module base not found ip %p\n", (void*)ip);
            break;
        }

        // Unwind the native frame adding all the memory accessed to the core dump via the read memory adapter.
        ULONG64 functionStart;
        if (!PAL_VirtualUnwindOutOfProc(pContext, nullptr, &functionStart, baseAddress, ReadMemoryAdapter)) {
            TRACE("Unwind: PAL_VirtualUnwindOutOfProc returned false\n");
            break;
        }

        if (m_crashInfo.GatherFrames())
        {
            // Add stack frame for the crash report. The function start returned by the unwinder is for
            // "ip" and not for the new context returned in pContext.
            StackFrame frame(baseAddress, ip, sp, ip - functionStart);
            AddStackFrame(frame);
        }

        previousSp = sp;
        previousIp = ip;
    }
}

bool
ThreadInfo::UnwindThread(IXCLRDataProcess* pClrDataProcess, ISOSDacInterface* pSos)
{
    TRACE("Unwind: thread %04x\n", Tid());

    // Get starting native context for the thread
    CONTEXT context;
    GetThreadContext(CONTEXT_ALL, &context);

    // Unwind the native frames at the top of the stack
    UnwindNativeFrames(&context);

    if (pClrDataProcess != nullptr)
    {
        ReleaseHolder<IXCLRDataTask> pTask;
        ReleaseHolder<IXCLRDataStackWalk> pStackwalk;

        // Get the managed stack walker for this thread
        if (SUCCEEDED(pClrDataProcess->GetTaskByOSThreadID(Tid(), &pTask)))
        {
            pTask->CreateStackWalk(
                CLRDATA_SIMPFRAME_UNRECOGNIZED |
                CLRDATA_SIMPFRAME_MANAGED_METHOD |
                CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE |
                CLRDATA_SIMPFRAME_RUNTIME_UNMANAGED_CODE,
                &pStackwalk);
        }

        // For each managed frame (if any)
        if (pStackwalk != nullptr)
        {
            TRACE("Unwind: managed frames\n");
            m_managed = true;

            ReleaseHolder<IXCLRDataExceptionState> pException;
            HRESULT hr = pTask->GetCurrentExceptionState(&pException);
            if (FAILED(hr))
            {
                hr = pTask->GetLastExceptionState(&pException);
            }
            if (SUCCEEDED(hr))
            {
                TRACE("Unwind: found managed exception\n");

                ReleaseHolder<IXCLRDataValue> pExceptionValue;
                if (SUCCEEDED(pException->GetManagedObject(&pExceptionValue)))
                {
                    CLRDATA_ADDRESS exceptionObject;
                    if (SUCCEEDED(pExceptionValue->GetAddress(&exceptionObject)))
                    {
                        m_exceptionObject = exceptionObject;
                        if (pSos != nullptr)
                        {
                            DacpExceptionObjectData exceptionData;
                            if (SUCCEEDED(exceptionData.Request(pSos, exceptionObject)))
                            {
                                m_exceptionHResult = exceptionData.HResult;
                            }
                        }
                        TRACE("Unwind: exception object %p exception hresult %08x\n", (void*)m_exceptionObject, m_exceptionHResult);
                    }
                    ReleaseHolder<IXCLRDataTypeInstance> pExceptionType;
                    if (SUCCEEDED(pExceptionValue->GetType(&pExceptionType)))
                    {
                        ArrayHolder<WCHAR> typeName = new WCHAR[MAX_LONGPATH + 1];
                        if (SUCCEEDED(pExceptionType->GetName(0, MAX_LONGPATH, nullptr, typeName.GetPtr())))
                        {
                            m_exceptionType = FormatString("%S", typeName.GetPtr());
                            TRACE("Unwind: exception type %s\n", m_exceptionType.c_str());
                        }
                    }
                }
            }

            // For each managed stack frame
            do
            {
                // Get the managed stack frame context
                if (pStackwalk->GetContext(CONTEXT_ALL, sizeof(context), nullptr, (BYTE *)&context) != S_OK) {
                    TRACE("Unwind: stack walker GetContext FAILED\n");
                    break;
                }

                // Get and save more detail information for the crash report if enabled
                if (m_crashInfo.GatherFrames())
                {
                    GatherStackFrames(&context, pStackwalk);
                }

                // Unwind all the native frames after the managed frame
                UnwindNativeFrames(&context);

            } while (pStackwalk->Next() == S_OK);
        }
    }

    return true;
}

void
ThreadInfo::GatherStackFrames(CONTEXT* pContext, IXCLRDataStackWalk* pStackwalk)
{
    uint64_t ip = 0, sp = 0;
    GetFrameLocation(pContext, &ip, &sp);

    uint64_t moduleAddress = 0;
    mdMethodDef token = 0;
    uint32_t nativeOffset = 0;
    uint32_t ilOffset = 0;
    ReleaseHolder<IXCLRDataMethodInstance> pMethod;

    ReleaseHolder<IXCLRDataFrame> pFrame;
    if (SUCCEEDED(pStackwalk->GetFrame(&pFrame)))
    {
        CLRDataSimpleFrameType simpleType;
        CLRDataDetailedFrameType detailedType;
        pFrame->GetFrameType(&simpleType, &detailedType);

        if ((simpleType & (CLRDATA_SIMPFRAME_MANAGED_METHOD | CLRDATA_SIMPFRAME_RUNTIME_MANAGED_CODE)) != 0)
        {
            if (SUCCEEDED(pFrame->GetMethodInstance(&pMethod)))
            {
                ReleaseHolder<IXCLRDataModule> pModule;
                if (SUCCEEDED(pMethod->GetTokenAndScope(&token, &pModule)))
                {
                    DacpGetModuleData moduleData;
                    if (SUCCEEDED(moduleData.Request(pModule)))
                    {
                        moduleAddress = moduleData.LoadedPEAddress;
                    }
                    else
                    {
                        TRACE("Unwind: DacpGetModuleData.Request sp %p ip %p FAILED\n", (void*)sp, (void*)ip);
                    }
                }
                else
                {
                    TRACE("Unwind: GetTokenAndScope sp %p ip %p FAILED\n", (void*)sp, (void*)ip);
                }
                if (FAILED(pMethod->GetILOffsetsByAddress(ip, 1, NULL, &ilOffset)))
                {
                    TRACE("Unwind: GetILOffsetsByAddress sp %p ip %p FAILED\n", (void*)sp, (void*)ip);
                }
                CLRDATA_ADDRESS startAddress;
                if (SUCCEEDED(pMethod->GetRepresentativeEntryAddress(&startAddress)))
                {
                    nativeOffset = ip - startAddress;
                }
                else
                {
                    TRACE("Unwind: GetRepresentativeEntryAddress sp %p ip %p FAILED\n", (void*)sp, (void*)ip);
                }
            }
            else
            {
                TRACE("Unwind: GetMethodInstance sp %p ip %p FAILED\n", (void*)sp, (void*)ip);
            }
        }
        else
        {
            TRACE("Unwind: simpleType %08x detailedType %08x\n", simpleType, detailedType);
        }
    }

    // Add managed stack frame for the crash info notes
    StackFrame frame(moduleAddress, ip, sp, pMethod.Detach(), nativeOffset, token, ilOffset);
    AddStackFrame(frame);
}

// This function deals with two types of frames: duplicate stack frames (SP is equal) and repeated frames (IP is 
// equal) because of a stack overflow.
// 
// The list of constraints:
// 
// 1) The StackFrame is immutable i.e. can't add some kind of repeat count to the frame. Making it mutable is big hassle.
// 2) The native unwinding can repeat the same frame SP/IP. These frames are not counted as repeated stack overflow ones.
// 3) Only add the repeated stack overflow frames once to frames set. This saves time and memory.
void
ThreadInfo::AddStackFrame(const StackFrame& frame)
{
    // This filters out the duplicate stack frames that are the result the native
    // unwinding happening between each managed frame. If the SP matches a frame
    // already in the set, skip it.
    const std::set<StackFrame>::iterator& found = m_frames.find(frame);
    if (found == m_frames.end())
    {
        // Aggregated the repeated stack frames only for stack overflow exceptions
        if (m_exceptionHResult == STACK_OVERFLOW_EXCEPTION)
        {
            // Check for repeats through all the stack frames so far until we find one
            if (m_beginRepeat == m_frames.end())
            {
                for (auto iterator = m_frames.cbegin(); iterator != m_frames.cend(); ++iterator)
                {
                    if (frame.InstructionPointer() == iterator->InstructionPointer())
                    {
                        m_repeatedFrames++;
                        m_beginRepeat = iterator;
                        TRACE("Unwind: begin repeat sp %p ip %p\n", (void*)frame.StackPointer(), (void*)frame.InstructionPointer());
                        return;
                    }
                }
            }

            // Check for repeats until we stop find them
            if (m_endRepeat == m_frames.end())
            {
                for (auto iterator = m_beginRepeat; iterator != m_endRepeat; ++iterator)
                {
                    if (frame.InstructionPointer() == iterator->InstructionPointer())
                    {
                        m_repeatedFrames++;
                        return;
                    }
                }
            }
        }

        // Add the non-duplicate and (if stack overflow) non-repeating frames to set 
        std::pair<std::set<StackFrame>::iterator, bool> result = m_frames.insert(frame);
        assert(result.second);

        TRACE("Unwind: sp %p ip %p off %08x mod %p%c\n",
            (void*)frame.StackPointer(), (void*)frame.InstructionPointer(), frame.NativeOffset(), (void*)frame.ModuleAddress(), frame.IsManaged() ? '*' : ' ');

        // Don't start tracking the end of the repeated frames until there is a start
        if (m_beginRepeat != m_frames.end() && m_endRepeat == m_frames.end())
        {
            TRACE("Unwind: end repeat sp %p ip %p\n", (void*)frame.StackPointer(), (void*)frame.InstructionPointer());
            m_endRepeat = result.first;

            // Count the number of frames in the repeating sequence and calculate how many times the sequence was repeated
            int framesRepeated = 0;
            for (auto iterator = m_beginRepeat; iterator != m_endRepeat; ++iterator)
            {
                framesRepeated++;
            }
            // The total number of individually repeated frames has to be greater than the number of frames in the repeating sequence 
            m_repeatedFrames = framesRepeated > 0 && m_repeatedFrames >= framesRepeated ? (m_repeatedFrames / framesRepeated) + 1 : 0;
        }
    }
}

void
ThreadInfo::GetThreadStack()
{
    uint64_t startAddress = GetStackPointer() & PAGE_MASK;
    size_t size = 4 * PAGE_SIZE;

    if (startAddress != 0)
    {
        MemoryRegion search(0, startAddress, startAddress + PAGE_SIZE);
        const MemoryRegion* region = CrashInfo::SearchMemoryRegions(m_crashInfo.OtherMappings(), search);
        if (region != nullptr) {

            // Use the mapping found for the size of the thread's stack
            size = region->EndAddress() - startAddress;

            if (g_diagnostics)
            {
                TRACE("Thread %04x stack found in other mapping (size %08zx): ", m_tid, size);
                region->Trace();
            }
        }
        m_crashInfo.InsertMemoryRegion(startAddress, size);
    }
    else
    {
        TRACE("Thread %04x null stack pointer\n", m_tid);
    }
}