1 /*===-- jitprofiling.c - JIT (Just-In-Time) Profiling API----------*- C -*-===*
2  *
3  * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4  * See https://llvm.org/LICENSE.txt for license information.
5  * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6  *
7  *===----------------------------------------------------------------------===*
8  *
9  * This file provides Intel(R) Performance Analyzer JIT (Just-In-Time)
10  * Profiling API implementation.
11  *
12  * NOTE: This file comes in a style different from the rest of LLVM
13  * source base since  this is a piece of code shared from Intel(R)
14  * products.  Please do not reformat / re-style this code to make
15  * subsequent merges and contributions from the original source base eaiser.
16  *
17  *===----------------------------------------------------------------------===*/
18 #include "ittnotify_config.h"
19 
20 #if ITT_PLATFORM==ITT_PLATFORM_WIN
21 #include <windows.h>
22 #pragma optimize("", off)
23 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
24 #include <dlfcn.h>
25 #include <pthread.h>
26 #include <stdint.h>
27 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
28 #include <stdlib.h>
29 
30 #include "jitprofiling.h"
31 
32 static const char rcsid[] = "\n@(#) $Revision: 243501 $\n";
33 
34 #define DLL_ENVIRONMENT_VAR             "VS_PROFILER"
35 
36 #ifndef NEW_DLL_ENVIRONMENT_VAR
37 #if ITT_ARCH==ITT_ARCH_IA32
38 #define NEW_DLL_ENVIRONMENT_VAR	        "INTEL_JIT_PROFILER32"
39 #else
40 #define NEW_DLL_ENVIRONMENT_VAR	        "INTEL_JIT_PROFILER64"
41 #endif
42 #endif /* NEW_DLL_ENVIRONMENT_VAR */
43 
44 #if ITT_PLATFORM==ITT_PLATFORM_WIN
45 #define DEFAULT_DLLNAME                 "JitPI.dll"
46 HINSTANCE m_libHandle = NULL;
47 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
48 #define DEFAULT_DLLNAME                 "libJitPI.so"
49 void* m_libHandle = NULL;
50 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
51 
52 /* default location of JIT profiling agent on Android */
53 #define ANDROID_JIT_AGENT_PATH  "/data/intel/libittnotify.so"
54 
55 /* the function pointers */
56 typedef unsigned int(*TPInitialize)(void);
57 static TPInitialize FUNC_Initialize=NULL;
58 
59 typedef unsigned int(*TPNotify)(unsigned int, void*);
60 static TPNotify FUNC_NotifyEvent=NULL;
61 
62 static iJIT_IsProfilingActiveFlags executionMode = iJIT_NOTHING_RUNNING;
63 
64 /* end collector dll part. */
65 
66 /* loadiJIT_Funcs() : this function is called just in the beginning
67  *  and is responsible to load the functions from BistroJavaCollector.dll
68  * result:
69  *  on success: the functions loads, iJIT_DLL_is_missing=0, return value = 1
70  *  on failure: the functions are NULL, iJIT_DLL_is_missing=1, return value = 0
71  */
72 static int loadiJIT_Funcs(void);
73 
74 /* global representing whether the BistroJavaCollector can't be loaded */
75 static int iJIT_DLL_is_missing = 0;
76 
77 /* Virtual stack - the struct is used as a virtual stack for each thread.
78  * Every thread initializes with a stack of size INIT_TOP_STACK.
79  * Every method entry decreases from the current stack point,
80  * and when a thread stack reaches its top of stack (return from the global
81  * function), the top of stack and the current stack increase. Notice that
82  * when returning from a function the stack pointer is the address of
83  * the function return.
84 */
85 #if ITT_PLATFORM==ITT_PLATFORM_WIN
86 static DWORD threadLocalStorageHandle = 0;
87 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
88 static pthread_key_t threadLocalStorageHandle = (pthread_key_t)0;
89 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
90 
91 #define INIT_TOP_Stack 10000
92 
93 typedef struct
94 {
95     unsigned int TopStack;
96     unsigned int CurrentStack;
97 } ThreadStack, *pThreadStack;
98 
99 /* end of virtual stack. */
100 
101 /*
102  * The function for reporting virtual-machine related events to VTune.
103  * Note: when reporting iJVM_EVENT_TYPE_ENTER_NIDS, there is no need to fill
104  * in the stack_id field in the iJIT_Method_NIDS structure, as VTune fills it.
105  * The return value in iJVM_EVENT_TYPE_ENTER_NIDS &&
106  * iJVM_EVENT_TYPE_LEAVE_NIDS events will be 0 in case of failure.
107  * in iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED event
108  * it will be -1 if EventSpecificData == 0 otherwise it will be 0.
109 */
110 
111 ITT_EXTERN_C int JITAPI
iJIT_NotifyEvent(iJIT_JVM_EVENT event_type,void * EventSpecificData)112 iJIT_NotifyEvent(iJIT_JVM_EVENT event_type, void *EventSpecificData)
113 {
114     int ReturnValue;
115 
116     /*
117      * This section is for debugging outside of VTune.
118      * It creates the environment variables that indicates call graph mode.
119      * If running outside of VTune remove the remark.
120      *
121      *
122      * static int firstTime = 1;
123      * char DoCallGraph[12] = "DoCallGraph";
124      * if (firstTime)
125      * {
126      * firstTime = 0;
127      * SetEnvironmentVariable( "BISTRO_COLLECTORS_DO_CALLGRAPH", DoCallGraph);
128      * }
129      *
130      * end of section.
131     */
132 
133     /* initialization part - the functions have not been loaded yet. This part
134      *        will load the functions, and check if we are in Call Graph mode.
135      *        (for special treatment).
136      */
137     if (!FUNC_NotifyEvent)
138     {
139         if (iJIT_DLL_is_missing)
140             return 0;
141 
142         /* load the Function from the DLL */
143         if (!loadiJIT_Funcs())
144             return 0;
145 
146         /* Call Graph initialization. */
147     }
148 
149     /* If the event is method entry/exit, check that in the current mode
150      * VTune is allowed to receive it
151      */
152     if ((event_type == iJVM_EVENT_TYPE_ENTER_NIDS ||
153          event_type == iJVM_EVENT_TYPE_LEAVE_NIDS) &&
154         (executionMode != iJIT_CALLGRAPH_ON))
155     {
156         return 0;
157     }
158     /* This section is performed when method enter event occurs.
159      * It updates the virtual stack, or creates it if this is the first
160      * method entry in the thread. The stack pointer is decreased.
161      */
162     if (event_type == iJVM_EVENT_TYPE_ENTER_NIDS)
163     {
164 #if ITT_PLATFORM==ITT_PLATFORM_WIN
165         pThreadStack threadStack =
166             (pThreadStack)TlsGetValue (threadLocalStorageHandle);
167 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
168         pThreadStack threadStack =
169             (pThreadStack)pthread_getspecific(threadLocalStorageHandle);
170 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
171 
172         /* check for use of reserved method IDs */
173         if ( ((piJIT_Method_NIDS) EventSpecificData)->method_id <= 999 )
174             return 0;
175 
176         if (!threadStack)
177         {
178             /* initialize the stack. */
179             threadStack = (pThreadStack) calloc (sizeof(ThreadStack), 1);
180             threadStack->TopStack = INIT_TOP_Stack;
181             threadStack->CurrentStack = INIT_TOP_Stack;
182 #if ITT_PLATFORM==ITT_PLATFORM_WIN
183             TlsSetValue(threadLocalStorageHandle,(void*)threadStack);
184 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
185             pthread_setspecific(threadLocalStorageHandle,(void*)threadStack);
186 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
187         }
188 
189         /* decrease the stack. */
190         ((piJIT_Method_NIDS) EventSpecificData)->stack_id =
191             (threadStack->CurrentStack)--;
192     }
193 
194     /* This section is performed when method leave event occurs
195      * It updates the virtual stack.
196      *    Increases the stack pointer.
197      *    If the stack pointer reached the top (left the global function)
198      *        increase the pointer and the top pointer.
199      */
200     if (event_type == iJVM_EVENT_TYPE_LEAVE_NIDS)
201     {
202 #if ITT_PLATFORM==ITT_PLATFORM_WIN
203         pThreadStack threadStack =
204            (pThreadStack)TlsGetValue (threadLocalStorageHandle);
205 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
206         pThreadStack threadStack =
207             (pThreadStack)pthread_getspecific(threadLocalStorageHandle);
208 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
209 
210         /* check for use of reserved method IDs */
211         if ( ((piJIT_Method_NIDS) EventSpecificData)->method_id <= 999 )
212             return 0;
213 
214         if (!threadStack)
215         {
216             /* Error: first report in this thread is method exit */
217             exit (1);
218         }
219 
220         ((piJIT_Method_NIDS) EventSpecificData)->stack_id =
221             ++(threadStack->CurrentStack) + 1;
222 
223         if (((piJIT_Method_NIDS) EventSpecificData)->stack_id
224                > threadStack->TopStack)
225             ((piJIT_Method_NIDS) EventSpecificData)->stack_id =
226                 (unsigned int)-1;
227     }
228 
229     if (event_type == iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED)
230     {
231         /* check for use of reserved method IDs */
232         if ( ((piJIT_Method_Load) EventSpecificData)->method_id <= 999 )
233             return 0;
234     }
235 
236     ReturnValue = (int)FUNC_NotifyEvent(event_type, EventSpecificData);
237 
238     return ReturnValue;
239 }
240 
241 /* The new mode call back routine */
242 ITT_EXTERN_C void JITAPI
iJIT_RegisterCallbackEx(void * userdata,iJIT_ModeChangedEx NewModeCallBackFuncEx)243 iJIT_RegisterCallbackEx(void *userdata, iJIT_ModeChangedEx
244                         NewModeCallBackFuncEx)
245 {
246     /* is it already missing... or the load of functions from the DLL failed */
247     if (iJIT_DLL_is_missing || !loadiJIT_Funcs())
248     {
249         /* then do not bother with notifications */
250         NewModeCallBackFuncEx(userdata, iJIT_NO_NOTIFICATIONS);
251         /* Error: could not load JIT functions. */
252         return;
253     }
254     /* nothing to do with the callback */
255 }
256 
257 /*
258  * This function allows the user to query in which mode, if at all,
259  *VTune is running
260  */
iJIT_IsProfilingActive(void)261 ITT_EXTERN_C iJIT_IsProfilingActiveFlags JITAPI iJIT_IsProfilingActive(void)
262 {
263     if (!iJIT_DLL_is_missing)
264     {
265         loadiJIT_Funcs();
266     }
267 
268     return executionMode;
269 }
270 
271 /* this function loads the collector dll (BistroJavaCollector)
272  * and the relevant functions.
273  * on success: all functions load,     iJIT_DLL_is_missing = 0, return value = 1
274  * on failure: all functions are NULL, iJIT_DLL_is_missing = 1, return value = 0
275  */
loadiJIT_Funcs(void)276 static int loadiJIT_Funcs(void)
277 {
278     static int bDllWasLoaded = 0;
279     char *dllName = (char*)rcsid; /* !! Just to avoid unused code elimination */
280 #if ITT_PLATFORM==ITT_PLATFORM_WIN
281     DWORD dNameLength = 0;
282 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
283 
284     if(bDllWasLoaded)
285     {
286         /* dll was already loaded, no need to do it for the second time */
287         return 1;
288     }
289 
290     /* Assumes that the DLL will not be found */
291     iJIT_DLL_is_missing = 1;
292     FUNC_NotifyEvent = NULL;
293 
294     if (m_libHandle)
295     {
296 #if ITT_PLATFORM==ITT_PLATFORM_WIN
297         FreeLibrary(m_libHandle);
298 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
299         dlclose(m_libHandle);
300 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
301         m_libHandle = NULL;
302     }
303 
304     /* Try to get the dll name from the environment */
305 #if ITT_PLATFORM==ITT_PLATFORM_WIN
306     dNameLength = GetEnvironmentVariableA(NEW_DLL_ENVIRONMENT_VAR, NULL, 0);
307     if (dNameLength)
308     {
309         DWORD envret = 0;
310         dllName = (char*)malloc(sizeof(char) * (dNameLength + 1));
311         envret = GetEnvironmentVariableA(NEW_DLL_ENVIRONMENT_VAR,
312                                          dllName, dNameLength);
313         if (envret)
314         {
315             /* Try to load the dll from the PATH... */
316             m_libHandle = LoadLibraryExA(dllName,
317                                          NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
318         }
319         free(dllName);
320     } else {
321         /* Try to use old VS_PROFILER variable */
322         dNameLength = GetEnvironmentVariableA(DLL_ENVIRONMENT_VAR, NULL, 0);
323         if (dNameLength)
324         {
325             DWORD envret = 0;
326             dllName = (char*)malloc(sizeof(char) * (dNameLength + 1));
327             envret = GetEnvironmentVariableA(DLL_ENVIRONMENT_VAR,
328                                              dllName, dNameLength);
329             if (envret)
330             {
331                 /* Try to load the dll from the PATH... */
332                 m_libHandle = LoadLibraryA(dllName);
333             }
334             free(dllName);
335         }
336     }
337 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
338     dllName = getenv(NEW_DLL_ENVIRONMENT_VAR);
339     if (!dllName)
340         dllName = getenv(DLL_ENVIRONMENT_VAR);
341 #ifdef ANDROID
342     if (!dllName)
343         dllName = ANDROID_JIT_AGENT_PATH;
344 #endif
345     if (dllName)
346     {
347         /* Try to load the dll from the PATH... */
348         m_libHandle = dlopen(dllName, RTLD_LAZY);
349     }
350 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
351 
352     if (!m_libHandle)
353     {
354 #if ITT_PLATFORM==ITT_PLATFORM_WIN
355         m_libHandle = LoadLibraryA(DEFAULT_DLLNAME);
356 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
357         m_libHandle = dlopen(DEFAULT_DLLNAME, RTLD_LAZY);
358 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
359     }
360 
361     /* if the dll wasn't loaded - exit. */
362     if (!m_libHandle)
363     {
364         iJIT_DLL_is_missing = 1; /* don't try to initialize
365                                   * JIT agent the second time
366                                   */
367         return 0;
368     }
369 
370 #if ITT_PLATFORM==ITT_PLATFORM_WIN
371     FUNC_NotifyEvent = (TPNotify)GetProcAddress(m_libHandle, "NotifyEvent");
372 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
373     FUNC_NotifyEvent = (TPNotify)(intptr_t)dlsym(m_libHandle, "NotifyEvent");
374 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
375     if (!FUNC_NotifyEvent)
376     {
377         FUNC_Initialize = NULL;
378         return 0;
379     }
380 
381 #if ITT_PLATFORM==ITT_PLATFORM_WIN
382     FUNC_Initialize = (TPInitialize)GetProcAddress(m_libHandle, "Initialize");
383 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
384     FUNC_Initialize = (TPInitialize)(intptr_t)dlsym(m_libHandle, "Initialize");
385 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
386     if (!FUNC_Initialize)
387     {
388         FUNC_NotifyEvent = NULL;
389         return 0;
390     }
391 
392     executionMode = (iJIT_IsProfilingActiveFlags)FUNC_Initialize();
393 
394     bDllWasLoaded = 1;
395     iJIT_DLL_is_missing = 0; /* DLL is ok. */
396 
397     /*
398      * Call Graph mode: init the thread local storage
399      * (need to store the virtual stack there).
400      */
401     if ( executionMode == iJIT_CALLGRAPH_ON )
402     {
403         /* Allocate a thread local storage slot for the thread "stack" */
404         if (!threadLocalStorageHandle)
405 #if ITT_PLATFORM==ITT_PLATFORM_WIN
406             threadLocalStorageHandle = TlsAlloc();
407 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
408         pthread_key_create(&threadLocalStorageHandle, NULL);
409 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
410     }
411 
412     return 1;
413 }
414 
415 /*
416  * This function should be called by the user whenever a thread ends,
417  * to free the thread "virtual stack" storage
418  */
FinalizeThread(void)419 ITT_EXTERN_C void JITAPI FinalizeThread(void)
420 {
421     if (threadLocalStorageHandle)
422     {
423 #if ITT_PLATFORM==ITT_PLATFORM_WIN
424         pThreadStack threadStack =
425             (pThreadStack)TlsGetValue (threadLocalStorageHandle);
426 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
427         pThreadStack threadStack =
428             (pThreadStack)pthread_getspecific(threadLocalStorageHandle);
429 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
430         if (threadStack)
431         {
432             free (threadStack);
433             threadStack = NULL;
434 #if ITT_PLATFORM==ITT_PLATFORM_WIN
435             TlsSetValue (threadLocalStorageHandle, threadStack);
436 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
437             pthread_setspecific(threadLocalStorageHandle, threadStack);
438 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
439         }
440     }
441 }
442 
443 /*
444  * This function should be called by the user when the process ends,
445  * to free the local storage index
446 */
FinalizeProcess(void)447 ITT_EXTERN_C void JITAPI FinalizeProcess(void)
448 {
449     if (m_libHandle)
450     {
451 #if ITT_PLATFORM==ITT_PLATFORM_WIN
452         FreeLibrary(m_libHandle);
453 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
454         dlclose(m_libHandle);
455 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
456         m_libHandle = NULL;
457     }
458 
459     if (threadLocalStorageHandle)
460 #if ITT_PLATFORM==ITT_PLATFORM_WIN
461         TlsFree (threadLocalStorageHandle);
462 #else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
463     pthread_key_delete(threadLocalStorageHandle);
464 #endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
465 }
466 
467 /*
468  * This function should be called by the user for any method once.
469  * The function will return a unique method ID, the user should maintain
470  * the ID for each method
471  */
iJIT_GetNewMethodID(void)472 ITT_EXTERN_C unsigned int JITAPI iJIT_GetNewMethodID(void)
473 {
474     static unsigned int methodID = 0x100000;
475 
476     if (methodID == 0)
477         return 0;  /* ERROR : this is not a valid value */
478 
479     return methodID++;
480 }
481