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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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