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
18MethodDesc *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
26typedef 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 */
67UINT_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 */
89void 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 */
111ProfileArgIterator::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 */
200ProfileArgIterator::~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 */
217LPVOID 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 */
286LPVOID 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 */
307LPVOID 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 */
356LPVOID 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