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 | // FILE: profiler.cpp |
6 | // |
7 | |
8 | // |
9 | |
10 | // |
11 | // ====================================================================================== |
12 | |
13 | #include "common.h" |
14 | |
15 | #ifdef PROFILING_SUPPORTED |
16 | #include "proftoeeinterfaceimpl.h" |
17 | |
18 | MethodDesc *FunctionIdToMethodDesc(FunctionID functionID); |
19 | |
20 | // TODO: move these to some common.h file |
21 | // FLAGS |
22 | #define PROFILE_ENTER 0x1 |
23 | #define PROFILE_LEAVE 0x2 |
24 | #define PROFILE_TAILCALL 0x4 |
25 | |
26 | typedef struct _PROFILE_PLATFORM_SPECIFIC_DATA |
27 | { |
28 | FunctionID functionId; |
29 | void *rbp; |
30 | void *probeRsp; |
31 | void *ip; |
32 | void *profiledRsp; |
33 | UINT64 rax; |
34 | LPVOID hiddenArg; |
35 | UINT64 flt0; // floats stored as doubles |
36 | UINT64 flt1; |
37 | UINT64 flt2; |
38 | UINT64 flt3; |
39 | #if defined(UNIX_AMD64_ABI) |
40 | UINT64 flt4; |
41 | UINT64 flt5; |
42 | UINT64 flt6; |
43 | UINT64 flt7; |
44 | UINT64 rdi; |
45 | UINT64 rsi; |
46 | UINT64 rdx; |
47 | UINT64 rcx; |
48 | UINT64 r8; |
49 | UINT64 r9; |
50 | #endif |
51 | UINT32 flags; |
52 | } PROFILE_PLATFORM_SPECIFIC_DATA, *PPROFILE_PLATFORM_SPECIFIC_DATA; |
53 | |
54 | |
55 | /* |
56 | * ProfileGetIPFromPlatformSpecificHandle |
57 | * |
58 | * This routine takes the platformSpecificHandle and retrieves from it the |
59 | * IP value. |
60 | * |
61 | * Parameters: |
62 | * handle - the platformSpecificHandle passed to ProfileEnter/Leave/Tailcall |
63 | * |
64 | * Returns: |
65 | * The IP value stored in the handle. |
66 | */ |
67 | UINT_PTR ProfileGetIPFromPlatformSpecificHandle(void *handle) |
68 | { |
69 | LIMITED_METHOD_CONTRACT; |
70 | |
71 | PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)handle; |
72 | return (UINT_PTR)pData->ip; |
73 | } |
74 | |
75 | |
76 | /* |
77 | * ProfileSetFunctionIDInPlatformSpecificHandle |
78 | * |
79 | * This routine takes the platformSpecificHandle and functionID, and assign |
80 | * functionID to functionID field of platformSpecificHandle. |
81 | * |
82 | * Parameters: |
83 | * pPlatformSpecificHandle - the platformSpecificHandle passed to ProfileEnter/Leave/Tailcall |
84 | * functionID - the FunctionID to be assigned |
85 | * |
86 | * Returns: |
87 | * None |
88 | */ |
89 | void ProfileSetFunctionIDInPlatformSpecificHandle(void * pPlatformSpecificHandle, FunctionID functionID) |
90 | { |
91 | LIMITED_METHOD_CONTRACT; |
92 | _ASSERTE(pPlatformSpecificHandle != NULL); |
93 | _ASSERTE(functionID != NULL); |
94 | |
95 | PROFILE_PLATFORM_SPECIFIC_DATA * pData = reinterpret_cast<PROFILE_PLATFORM_SPECIFIC_DATA *>(pPlatformSpecificHandle); |
96 | pData->functionId = functionID; |
97 | } |
98 | |
99 | /* |
100 | * ProfileArgIterator::ProfileArgIterator |
101 | * |
102 | * Constructor. Initializes for arg iteration. |
103 | * |
104 | * Parameters: |
105 | * pMetaSig - The signature of the method we are going iterate over |
106 | * platformSpecificHandle - the value passed to ProfileEnter/Leave/Tailcall |
107 | * |
108 | * Returns: |
109 | * None. |
110 | */ |
111 | ProfileArgIterator::ProfileArgIterator(MetaSig * pSig, void * platformSpecificHandle) : |
112 | m_argIterator(pSig) |
113 | { |
114 | WRAPPER_NO_CONTRACT; |
115 | |
116 | _ASSERTE(pSig != NULL); |
117 | _ASSERTE(platformSpecificHandle != NULL); |
118 | |
119 | m_handle = platformSpecificHandle; |
120 | PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)m_handle; |
121 | |
122 | // unwind a frame and get the Rsp for the profiled method to make sure it matches |
123 | // what the JIT gave us |
124 | #ifdef _DEBUG |
125 | { |
126 | // setup the context to represent the frame that called ProfileEnterNaked |
127 | CONTEXT ctx; |
128 | memset(&ctx, 0, sizeof(CONTEXT)); |
129 | ctx.Rsp = (UINT64)pData->probeRsp; |
130 | ctx.Rbp = (UINT64)pData->rbp; |
131 | ctx.Rip = (UINT64)pData->ip; |
132 | |
133 | // walk up a frame to the caller frame (called the managed method which |
134 | // called ProfileEnterNaked) |
135 | Thread::VirtualUnwindCallFrame(&ctx); |
136 | |
137 | _ASSERTE(pData->profiledRsp == (void*)ctx.Rsp); |
138 | } |
139 | #endif // _DEBUG |
140 | |
141 | // Get the hidden arg if there is one |
142 | MethodDesc * pMD = FunctionIdToMethodDesc(pData->functionId); |
143 | |
144 | if ( (pData->hiddenArg == NULL) && |
145 | (pMD->RequiresInstArg() || pMD->AcquiresInstMethodTableFromThis()) ) |
146 | { |
147 | // In the enter probe, the JIT may not have pushed the generics token onto the stack yet. |
148 | // Luckily, we can inspect the registers reliably at this point. |
149 | if (pData->flags & PROFILE_ENTER) |
150 | { |
151 | _ASSERTE(!((pData->flags & PROFILE_LEAVE) || (pData->flags & PROFILE_TAILCALL))); |
152 | |
153 | if (pMD->AcquiresInstMethodTableFromThis()) |
154 | { |
155 | pData->hiddenArg = GetThis(); |
156 | } |
157 | else |
158 | { |
159 | // The param type arg comes after the return buffer argument and the "this" pointer. |
160 | int index = 0; |
161 | |
162 | if (m_argIterator.HasThis()) |
163 | { |
164 | index++; |
165 | } |
166 | |
167 | if (m_argIterator.HasRetBuffArg()) |
168 | { |
169 | index++; |
170 | } |
171 | |
172 | #ifdef UNIX_AMD64_ABI |
173 | switch (index) |
174 | { |
175 | case 0: pData->hiddenArg = (LPVOID)pData->rdi; break; |
176 | case 1: pData->hiddenArg = (LPVOID)pData->rsi; break; |
177 | case 2: pData->hiddenArg = (LPVOID)pData->rdx; break; |
178 | } |
179 | #else |
180 | pData->hiddenArg = *(LPVOID*)((LPBYTE)pData->profiledRsp + (index * sizeof(SIZE_T))); |
181 | #endif // UNIX_AMD64_ABI |
182 | } |
183 | } |
184 | else |
185 | { |
186 | EECodeInfo codeInfo((PCODE)pData->ip); |
187 | |
188 | // We want to pass the caller SP here. |
189 | pData->hiddenArg = EECodeManager::GetExactGenericsToken((SIZE_T)(pData->profiledRsp), &codeInfo); |
190 | } |
191 | } |
192 | } |
193 | |
194 | /* |
195 | * ProfileArgIterator::~ProfileArgIterator |
196 | * |
197 | * Destructor, releases all resources. |
198 | * |
199 | */ |
200 | ProfileArgIterator::~ProfileArgIterator() |
201 | { |
202 | LIMITED_METHOD_CONTRACT; |
203 | |
204 | m_handle = NULL; |
205 | } |
206 | |
207 | /* |
208 | * ProfileArgIterator::GetNextArgAddr |
209 | * |
210 | * After initialization, this method is called repeatedly until it |
211 | * returns NULL to get the address of each arg. Note: this address |
212 | * could be anywhere on the stack. |
213 | * |
214 | * Returns: |
215 | * Address of the argument, or NULL if iteration is complete. |
216 | */ |
217 | LPVOID ProfileArgIterator::GetNextArgAddr() |
218 | { |
219 | WRAPPER_NO_CONTRACT; |
220 | |
221 | _ASSERTE(m_handle != NULL); |
222 | |
223 | PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)m_handle; |
224 | |
225 | if ((pData->flags & PROFILE_LEAVE) || (pData->flags & PROFILE_TAILCALL)) |
226 | { |
227 | _ASSERTE(!"GetNextArgAddr() - arguments are not available in leave and tailcall probes" ); |
228 | return NULL; |
229 | } |
230 | |
231 | int argOffset = m_argIterator.GetNextOffset(); |
232 | |
233 | // argOffset of TransitionBlock::InvalidOffset indicates that we're done |
234 | if (argOffset == TransitionBlock::InvalidOffset) |
235 | { |
236 | return NULL; |
237 | } |
238 | |
239 | // stack args are offset against the profiledRsp |
240 | if (TransitionBlock::IsStackArgumentOffset(argOffset)) |
241 | { |
242 | LPVOID pArg = ((LPBYTE)pData->profiledRsp) + (argOffset - TransitionBlock::GetOffsetOfArgs()); |
243 | |
244 | if (m_argIterator.IsArgPassedByRef()) |
245 | pArg = *(LPVOID *)pArg; |
246 | |
247 | return pArg; |
248 | } |
249 | |
250 | // if we're here we have an enregistered argument |
251 | int regStructOfs = (argOffset - TransitionBlock::GetOffsetOfArgumentRegisters()); |
252 | _ASSERTE(regStructOfs < ARGUMENTREGISTERS_SIZE); |
253 | |
254 | CorElementType t = m_argIterator.GetArgType(); |
255 | _ASSERTE(IS_ALIGNED(regStructOfs, sizeof(SLOT))); |
256 | if (t == ELEMENT_TYPE_R4 || t == ELEMENT_TYPE_R8) |
257 | { |
258 | return (LPBYTE)&pData->flt0 + regStructOfs; |
259 | } |
260 | else |
261 | { |
262 | // enregistered args (which are really stack homed) are offset against profiledRsp |
263 | LPVOID pArg = ((LPBYTE)pData->profiledRsp + regStructOfs); |
264 | |
265 | if (m_argIterator.IsArgPassedByRef()) |
266 | pArg = *(LPVOID *)pArg; |
267 | |
268 | return pArg; |
269 | } |
270 | |
271 | return NULL; |
272 | } |
273 | |
274 | /* |
275 | * ProfileArgIterator::GetHiddenArgValue |
276 | * |
277 | * Called after initialization, any number of times, to retrieve any |
278 | * hidden argument, so that resolution for Generics can be done. |
279 | * |
280 | * Parameters: |
281 | * None. |
282 | * |
283 | * Returns: |
284 | * Value of the hidden parameter, or NULL if none exists. |
285 | */ |
286 | LPVOID ProfileArgIterator::GetHiddenArgValue(void) |
287 | { |
288 | LIMITED_METHOD_CONTRACT; |
289 | |
290 | PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)m_handle; |
291 | |
292 | return pData->hiddenArg; |
293 | } |
294 | |
295 | /* |
296 | * ProfileArgIterator::GetThis |
297 | * |
298 | * Called after initialization, any number of times, to retrieve any |
299 | * 'this' pointer. |
300 | * |
301 | * Parameters: |
302 | * None. |
303 | * |
304 | * Returns: |
305 | * Address of the 'this', or NULL if none exists. |
306 | */ |
307 | LPVOID ProfileArgIterator::GetThis(void) |
308 | { |
309 | CONTRACTL |
310 | { |
311 | NOTHROW; |
312 | GC_NOTRIGGER; |
313 | } |
314 | CONTRACTL_END; |
315 | |
316 | PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)m_handle; |
317 | MethodDesc * pMD = FunctionIdToMethodDesc(pData->functionId); |
318 | |
319 | // We guarantee to return the correct "this" pointer in the enter probe. |
320 | // For the leave and tailcall probes, we only return a valid "this" pointer if it is the generics token. |
321 | if (pData->hiddenArg != NULL) |
322 | { |
323 | if (pMD->AcquiresInstMethodTableFromThis()) |
324 | { |
325 | return pData->hiddenArg; |
326 | } |
327 | } |
328 | |
329 | if (pData->flags & PROFILE_ENTER) |
330 | { |
331 | if (m_argIterator.HasThis()) |
332 | { |
333 | #ifdef UNIX_AMD64_ABI |
334 | return (LPVOID)pData->rdi; |
335 | #else |
336 | return *(LPVOID*)((LPBYTE)pData->profiledRsp); |
337 | #endif // UNIX_AMD64_ABI |
338 | } |
339 | } |
340 | |
341 | return NULL; |
342 | } |
343 | |
344 | /* |
345 | * ProfileArgIterator::GetReturnBufferAddr |
346 | * |
347 | * Called after initialization, any number of times, to retrieve the |
348 | * address of the return buffer. NULL indicates no return value. |
349 | * |
350 | * Parameters: |
351 | * None. |
352 | * |
353 | * Returns: |
354 | * Address of the return buffer, or NULL if none exists. |
355 | */ |
356 | LPVOID ProfileArgIterator::GetReturnBufferAddr(void) |
357 | { |
358 | CONTRACTL |
359 | { |
360 | NOTHROW; |
361 | GC_NOTRIGGER; |
362 | } |
363 | CONTRACTL_END; |
364 | |
365 | PROFILE_PLATFORM_SPECIFIC_DATA* pData = (PROFILE_PLATFORM_SPECIFIC_DATA*)m_handle; |
366 | |
367 | if (m_argIterator.HasRetBuffArg()) |
368 | { |
369 | // the JIT64 makes sure that in ret-buf-arg cases where the method is being profiled that |
370 | // rax is setup with the address of caller passed in buffer. this is _questionably_ required |
371 | // by our calling convention, but is required by our profiler spec. |
372 | return (LPVOID)pData->rax; |
373 | } |
374 | |
375 | CorElementType t = m_argIterator.GetSig()->GetReturnType(); |
376 | if (ELEMENT_TYPE_VOID != t) |
377 | { |
378 | if (ELEMENT_TYPE_R4 == t || ELEMENT_TYPE_R8 == t) |
379 | pData->rax = pData->flt0; |
380 | |
381 | return &(pData->rax); |
382 | } |
383 | else |
384 | return NULL; |
385 | } |
386 | |
387 | #undef PROFILE_ENTER |
388 | #undef PROFILE_LEAVE |
389 | #undef PROFILE_TAILCALL |
390 | |
391 | #endif // PROFILING_SUPPORTED |
392 | |
393 | |