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 | #include "createdump.h" |
6 | |
7 | #define IMAGE_FILE_MACHINE_AMD64 0x8664 // AMD64 (K8) |
8 | |
9 | DumpDataTarget::DumpDataTarget(pid_t pid) : |
10 | m_ref(1), |
11 | m_pid(pid), |
12 | m_fd(-1), |
13 | m_crashInfo(nullptr) |
14 | { |
15 | } |
16 | |
17 | DumpDataTarget::~DumpDataTarget() |
18 | { |
19 | if (m_fd != -1) |
20 | { |
21 | close(m_fd); |
22 | m_fd = -1; |
23 | } |
24 | } |
25 | |
26 | bool |
27 | DumpDataTarget::Initialize(CrashInfo * crashInfo) |
28 | { |
29 | char memPath[128]; |
30 | _snprintf_s(memPath, sizeof(memPath), sizeof(memPath), "/proc/%lu/mem" , m_pid); |
31 | |
32 | m_fd = open(memPath, O_RDONLY); |
33 | if (m_fd == -1) |
34 | { |
35 | fprintf(stderr, "open(%s) FAILED %d (%s)\n" , memPath, errno, strerror(errno)); |
36 | return false; |
37 | } |
38 | m_crashInfo = crashInfo; |
39 | return true; |
40 | } |
41 | |
42 | STDMETHODIMP |
43 | DumpDataTarget::QueryInterface( |
44 | ___in REFIID InterfaceId, |
45 | ___out PVOID* Interface |
46 | ) |
47 | { |
48 | if (InterfaceId == IID_IUnknown || |
49 | InterfaceId == IID_ICLRDataTarget) |
50 | { |
51 | *Interface = (ICLRDataTarget*)this; |
52 | AddRef(); |
53 | return S_OK; |
54 | } |
55 | else |
56 | { |
57 | *Interface = NULL; |
58 | return E_NOINTERFACE; |
59 | } |
60 | } |
61 | |
62 | STDMETHODIMP_(ULONG) |
63 | DumpDataTarget::AddRef() |
64 | { |
65 | LONG ref = InterlockedIncrement(&m_ref); |
66 | return ref; |
67 | } |
68 | |
69 | STDMETHODIMP_(ULONG) |
70 | DumpDataTarget::Release() |
71 | { |
72 | LONG ref = InterlockedDecrement(&m_ref); |
73 | if (ref == 0) |
74 | { |
75 | delete this; |
76 | } |
77 | return ref; |
78 | } |
79 | |
80 | HRESULT STDMETHODCALLTYPE |
81 | DumpDataTarget::GetMachineType( |
82 | /* [out] */ ULONG32 *machine) |
83 | { |
84 | #ifdef _AMD64_ |
85 | *machine = IMAGE_FILE_MACHINE_AMD64; |
86 | #elif _ARM_ |
87 | *machine = IMAGE_FILE_MACHINE_ARMNT; |
88 | #elif _ARM64_ |
89 | *machine = IMAGE_FILE_MACHINE_ARM64; |
90 | #elif _X86_ |
91 | *machine = IMAGE_FILE_MACHINE_I386; |
92 | #else |
93 | #error Unsupported architecture |
94 | #endif |
95 | return S_OK; |
96 | } |
97 | |
98 | HRESULT STDMETHODCALLTYPE |
99 | DumpDataTarget::GetPointerSize( |
100 | /* [out] */ ULONG32 *size) |
101 | { |
102 | #if defined(_AMD64_) || defined(_ARM64_) |
103 | *size = 8; |
104 | #elif defined(_ARM_) || defined(_X86_) |
105 | *size = 4; |
106 | #else |
107 | #error Unsupported architecture |
108 | #endif |
109 | return S_OK; |
110 | } |
111 | |
112 | HRESULT STDMETHODCALLTYPE |
113 | DumpDataTarget::GetImageBase( |
114 | /* [string][in] */ LPCWSTR moduleName, |
115 | /* [out] */ CLRDATA_ADDRESS *baseAddress) |
116 | { |
117 | assert(m_crashInfo != nullptr); |
118 | *baseAddress = 0; |
119 | |
120 | char tempModuleName[MAX_PATH]; |
121 | int length = WideCharToMultiByte(CP_ACP, 0, moduleName, -1, tempModuleName, sizeof(tempModuleName), NULL, NULL); |
122 | if (length > 0) |
123 | { |
124 | for (const MemoryRegion& image : m_crashInfo->ModuleMappings()) |
125 | { |
126 | const char *name = strrchr(image.FileName(), '/'); |
127 | if (name != nullptr) |
128 | { |
129 | name++; |
130 | } |
131 | else |
132 | { |
133 | name = image.FileName(); |
134 | } |
135 | if (strcmp(name, tempModuleName) == 0) |
136 | { |
137 | *baseAddress = image.StartAddress(); |
138 | return S_OK; |
139 | } |
140 | } |
141 | } |
142 | return E_FAIL; |
143 | } |
144 | |
145 | HRESULT STDMETHODCALLTYPE |
146 | DumpDataTarget::ReadVirtual( |
147 | /* [in] */ CLRDATA_ADDRESS address, |
148 | /* [length_is][size_is][out] */ PBYTE buffer, |
149 | /* [in] */ ULONG32 size, |
150 | /* [optional][out] */ ULONG32 *done) |
151 | { |
152 | assert(m_fd != -1); |
153 | size_t read = pread64(m_fd, buffer, size, (off64_t)(ULONG_PTR)address); |
154 | if (read == -1) |
155 | { |
156 | *done = 0; |
157 | return E_FAIL; |
158 | } |
159 | *done = read; |
160 | return S_OK; |
161 | } |
162 | |
163 | HRESULT STDMETHODCALLTYPE |
164 | DumpDataTarget::WriteVirtual( |
165 | /* [in] */ CLRDATA_ADDRESS address, |
166 | /* [size_is][in] */ PBYTE buffer, |
167 | /* [in] */ ULONG32 size, |
168 | /* [optional][out] */ ULONG32 *done) |
169 | { |
170 | assert(false); |
171 | return E_NOTIMPL; |
172 | } |
173 | |
174 | HRESULT STDMETHODCALLTYPE |
175 | DumpDataTarget::GetTLSValue( |
176 | /* [in] */ ULONG32 threadID, |
177 | /* [in] */ ULONG32 index, |
178 | /* [out] */ CLRDATA_ADDRESS* value) |
179 | { |
180 | assert(false); |
181 | return E_NOTIMPL; |
182 | } |
183 | |
184 | HRESULT STDMETHODCALLTYPE |
185 | DumpDataTarget::SetTLSValue( |
186 | /* [in] */ ULONG32 threadID, |
187 | /* [in] */ ULONG32 index, |
188 | /* [in] */ CLRDATA_ADDRESS value) |
189 | { |
190 | assert(false); |
191 | return E_NOTIMPL; |
192 | } |
193 | |
194 | HRESULT STDMETHODCALLTYPE |
195 | DumpDataTarget::GetCurrentThreadID( |
196 | /* [out] */ ULONG32* threadID) |
197 | { |
198 | assert(false); |
199 | return E_NOTIMPL; |
200 | } |
201 | |
202 | HRESULT STDMETHODCALLTYPE |
203 | DumpDataTarget::GetThreadContext( |
204 | /* [in] */ ULONG32 threadID, |
205 | /* [in] */ ULONG32 contextFlags, |
206 | /* [in] */ ULONG32 contextSize, |
207 | /* [out, size_is(contextSize)] */ PBYTE context) |
208 | { |
209 | assert(m_crashInfo != nullptr); |
210 | if (contextSize < sizeof(CONTEXT)) |
211 | { |
212 | assert(false); |
213 | return E_INVALIDARG; |
214 | } |
215 | memset(context, 0, contextSize); |
216 | for (const ThreadInfo* thread : m_crashInfo->Threads()) |
217 | { |
218 | if (thread->Tid() == threadID) |
219 | { |
220 | thread->GetThreadContext(contextFlags, reinterpret_cast<CONTEXT*>(context)); |
221 | return S_OK; |
222 | } |
223 | } |
224 | return E_FAIL; |
225 | } |
226 | |
227 | HRESULT STDMETHODCALLTYPE |
228 | DumpDataTarget::SetThreadContext( |
229 | /* [in] */ ULONG32 threadID, |
230 | /* [in] */ ULONG32 contextSize, |
231 | /* [out, size_is(contextSize)] */ PBYTE context) |
232 | { |
233 | assert(false); |
234 | return E_NOTIMPL; |
235 | } |
236 | |
237 | HRESULT STDMETHODCALLTYPE |
238 | DumpDataTarget::Request( |
239 | /* [in] */ ULONG32 reqCode, |
240 | /* [in] */ ULONG32 inBufferSize, |
241 | /* [size_is][in] */ BYTE *inBuffer, |
242 | /* [in] */ ULONG32 outBufferSize, |
243 | /* [size_is][out] */ BYTE *outBuffer) |
244 | { |
245 | assert(false); |
246 | return E_NOTIMPL; |
247 | } |
248 | |