forked from microsoft/Xbox-ATG-Samples
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCallStack.h
155 lines (133 loc) · 6.68 KB
/
CallStack.h
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
//--------------------------------------------------------------------------------------
// File: CallStack.h
//
// Diagnostic helpers for working with callstacks
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright(c) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------
#pragma once
#include <cstdint>
#include <vector>
#include <stdexcept>
namespace CallStack
{
// Capture the backtrace for the current thread
// Parameters:
// addresses -- receives the return addresses from each stack frame in the current call stack
// modules -- receives the module handle corresponding to the return address for each stack frame
// framesToCapture -- indicates the number of frames to capture
// Return value:
// Returns the number of frames that were captured
// Note: the caller must allocate enough space in addresses and modules to accomodate the number of
// frames specified by framesToCapture
size_t CaptureBackTraceFromCurrentThread(void** addresses, HMODULE *modules, size_t framesToCapture);
// Capture the backtrace starting with the provided context record.
// Parameters:
// contextRecord: address of a context record (register data)
// addresses -- receives the return addresses from each stack frame in the current call stack
// modules -- receives the module handle corresponding to the return address for each stack frame
// framesToCapture -- indicates the number of frames to capture
// Return value:
// Returns the number of frames that were captured
// Note: the caller must allocate enough space in addresses and modules to accomodate the number of
// frames specified by framesToCapture
size_t CaptureBackTraceFromContext(CONTEXT *contextRecord, void** addresses, HMODULE* modules, size_t framesToCapture);
// Count the number of stack frames in the callstack starting with the provided context record.
// Parameters:
// contextRecord: address of a context record (register data)
// Return value:
// Returns the number of frames
// Remarks:
// This is provided, for "completeness", so that you may allocate memory for a backtrace. I.e first you call GetFrameCountFromContext,
// then you allocate space for that many addresses and module handles, finally, you call CaptureBackTraceFromContext.
// Bear in mind that it is not quite that easy!:
// In particular, you must "freeze" the thread corresponding to the provided context record during the entire process.
// Put differently, you cannot let the thread add and remove stack frames (otherwise the count will be wrong.) This
// becomes problematic because in the case when the thread is holding a lock to the heap, then if you try to allocate
// from the same heap it will cause a deadlock.
// The takeaway is that you will need a separate allocation mechanism (e.g. a "debug heap") if you are going to try
// to use this pattern. An alternative, simpler, approach is to just set asside some static memory that is "big enough"
// for most callstacks. This is the strategy used by the BackTrace class below.
size_t GetFrameCountFromContext(CONTEXT *contextRecord);
// Utility for capturing a backtrace
class BackTrace
{
public:
BackTrace() = default;
// Data for a stack frame
struct frame_data
{
frame_data()
: moduleHandle(0)
, relativeVirtualAddress(0)
, symbolOffset(0)
, sourceLineNumber(0)
, symbolResult(E_NOT_SET)
, sourceResult(E_NOT_SET)
{}
// The module handle also happens to be numerically identical to the base address of the module
HMODULE moduleHandle;
// The relative virual address is the offset of the symbol from the module base address
uint64_t relativeVirtualAddress;
// symbol name information
std::wstring symbolName;
uint64_t symbolOffset;
// source file information
std::wstring sourceFileName;
uint64_t sourceLineNumber;
// error information from the symbol proxy
HRESULT symbolResult;
HRESULT sourceResult;
};
// Number of frames in the backtrace
size_t size() const
{
return m_frames.size();
}
// Access to frames by index
const frame_data &operator [] (unsigned idx) const
{
if (idx < m_frames.size())
{
return m_frames[idx];
}
throw std::out_of_range("Index is out of range");
}
// Capture a backtrace from the current thread
// Result:
// Returns the number of frames captured
size_t CaptureCurrentThread();
// Capture a backtrace from the thread specified by the provided handle
// Parameter:
// whichThread -- specifies the thread for which to capture a backtrace
// Result:
// Returns the number of frames captured
size_t CaptureCrossThread(HANDLE whichThread);
static const unsigned CAPTURE_FLAGS_NONE = 0;
static const unsigned CAPTURE_FLAGS_SYMBOL_INFO = 0x1; // Use this to resolve symbol name and offset
static const unsigned CAPTURE_FLAGS_SOURCE_INFO = 0x2; // Use this to resolve source filename and line number
static const unsigned CAPTURE_FLAGS_DEFAULT = CAPTURE_FLAGS_SYMBOL_INFO | CAPTURE_FLAGS_SOURCE_INFO;
// Resolves the symbol information for the backtrace
// Parameter:
// captureFlags -- indicates which symbol information to resolve
HRESULT Resolve(unsigned captureFlags = CAPTURE_FLAGS_DEFAULT);
private:
void Clear();
bool IsCurrentThread(HANDLE whichThread) const;
std::vector<frame_data> m_frames;
HRESULT ResolveSymbolInfo(size_t numFrames, ULONG_PTR *addresses);
HRESULT ResolveSourceInfo(size_t numFrames, ULONG_PTR *addresses);
struct ResolutionContext
{
unsigned whichFrame;
frame_data *frames;
};
static BOOL CALLBACK s_SymCallback(LPVOID context, ULONG_PTR symbolAddress, HRESULT symbolResult, PCWSTR symName, ULONG offset);
static BOOL CALLBACK s_SrcCallback(LPVOID context, ULONG_PTR symbolAddress, HRESULT sourceResult, PCWSTR filepath, ULONG lineNumber);
};
} // namespace CallStack