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
32static 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"
46HINSTANCE m_libHandle = NULL;
47#else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
48#define DEFAULT_DLLNAME                 "libJitPI.so"
49void* 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 */
56typedef unsigned int(*TPInitialize)(void);
57static TPInitialize FUNC_Initialize=NULL;
58
59typedef unsigned int(*TPNotify)(unsigned int, void*);
60static TPNotify FUNC_NotifyEvent=NULL;
61
62static 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 */
72static int loadiJIT_Funcs(void);
73
74/* global representing whether the BistroJavaCollector can't be loaded */
75static 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
86static DWORD threadLocalStorageHandle = 0;
87#else  /* ITT_PLATFORM==ITT_PLATFORM_WIN */
88static pthread_key_t threadLocalStorageHandle = (pthread_key_t)0;
89#endif /* ITT_PLATFORM==ITT_PLATFORM_WIN */
90
91#define INIT_TOP_Stack 10000
92
93typedef 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
111ITT_EXTERN_C int JITAPI
112iJIT_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 */
242ITT_EXTERN_C void JITAPI
243iJIT_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 */
261ITT_EXTERN_C iJIT_IsProfilingActiveFlags JITAPI iJIT_IsProfilingActive()
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 */
276static int loadiJIT_Funcs()
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 */
419ITT_EXTERN_C void JITAPI FinalizeThread()
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*/
447ITT_EXTERN_C void JITAPI FinalizeProcess()
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 */
472ITT_EXTERN_C unsigned int JITAPI iJIT_GetNewMethodID()
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