1 /* 2 * Copyright (c) 1999, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 #include "debug_util.h" 27 28 static void DTrace_PrintStdErr(const char *msg); 29 30 #if defined(DEBUG) 31 enum { 32 MAX_TRACES = 200, /* max number of defined trace points allowed */ 33 MAX_TRACE_BUFFER = 512, /* maximum size of a given trace output */ 34 MAX_LINE = 100000, /* reasonable upper limit on line number in source file */ 35 MAX_ARGC = 8 /* maximum number of arguments to print functions */ 36 }; 37 38 typedef enum dtrace_scope { 39 DTRACE_FILE, 40 DTRACE_LINE 41 } dtrace_scope; 42 43 typedef struct dtrace_info { 44 char file[FILENAME_MAX+1]; 45 int line; 46 int enabled; 47 dtrace_scope scope; 48 } dtrace_info, * p_dtrace_info; 49 50 static dtrace_info DTraceInfo[MAX_TRACES]; 51 static char DTraceBuffer[MAX_TRACE_BUFFER*2+1]; /* double the buffer size to catch overruns */ 52 static dmutex_t DTraceMutex = NULL; 53 static dbool_t GlobalTracingEnabled = FALSE; 54 static int NumTraces = 0; 55 56 static DTRACE_OUTPUT_CALLBACK PfnTraceCallback = DTrace_PrintStdErr; 57 58 static p_dtrace_info DTrace_GetInfo(dtrace_id tid) { 59 DASSERT(tid < MAX_TRACES); 60 return &DTraceInfo[tid]; 61 } 62 63 static dtrace_id DTrace_CreateTraceId(const char * file, int line, dtrace_scope scope) { 64 dtrace_id tid = NumTraces++; 65 p_dtrace_info info = &DTraceInfo[tid]; 66 DASSERT(NumTraces < MAX_TRACES); 67 68 strcpy(info->file, file); 69 info->line = line; 70 info->enabled = FALSE; 71 info->scope = scope; 72 return tid; 73 } 74 75 /* 76 * Compares the trailing characters in a filename to see if they match 77 * e.g. "src\win32\foobar.c" and "foobar.c" would be considered equal 78 * but "src\win32\foo.c" and "src\win32\bar.c" would not. 79 */ 80 static dbool_t FileNamesSame(const char * fileOne, const char * fileTwo) { 81 size_t lengthOne = strlen(fileOne); 82 size_t lengthTwo = strlen(fileTwo); 83 size_t numCompareChars; 84 dbool_t tailsEqual; 85 86 if (fileOne == fileTwo) { 87 return TRUE; 88 } else if (fileOne == NULL || fileTwo == NULL) { 89 return FALSE; 90 } 91 /* compare the tail ends of the strings for equality */ 92 numCompareChars = lengthOne < lengthTwo ? lengthOne : lengthTwo; 93 tailsEqual = strcmp(fileOne + lengthOne - numCompareChars, 94 fileTwo + lengthTwo - numCompareChars) == 0; 95 return tailsEqual; 96 } 97 98 /* 99 * Finds the trace id for a given file/line location or creates one 100 * if it doesn't exist 101 */ 102 static dtrace_id DTrace_GetTraceId(const char * file, int line, dtrace_scope scope) { 103 dtrace_id tid; 104 p_dtrace_info info; 105 106 /* check to see if the trace point has already been created */ 107 for ( tid = 0; tid < NumTraces; tid++ ) { 108 info = DTrace_GetInfo(tid); 109 if ( info->scope == scope ) { 110 dbool_t sameFile = FileNamesSame(file, info->file); 111 dbool_t sameLine = info->line == line; 112 113 if ( (info->scope == DTRACE_FILE && sameFile) || 114 (info->scope == DTRACE_LINE && sameFile && sameLine) ) { 115 goto Exit; 116 } 117 } 118 } 119 120 /* trace point wasn't created, so force it's creation */ 121 tid = DTrace_CreateTraceId(file, line, scope); 122 Exit: 123 return tid; 124 } 125 126 127 static dbool_t DTrace_IsEnabledAt(dtrace_id * pfileid, dtrace_id * plineid, const char * file, int line) { 128 DASSERT(pfileid != NULL && plineid != NULL); 129 130 if ( *pfileid == UNDEFINED_TRACE_ID ) { 131 /* first time calling the trace for this file, so obtain a trace id */ 132 *pfileid = DTrace_GetTraceId(file, -1, DTRACE_FILE); 133 } 134 if ( *plineid == UNDEFINED_TRACE_ID ) { 135 /* first time calling the trace for this line, so obtain a trace id */ 136 *plineid = DTrace_GetTraceId(file, line, DTRACE_LINE); 137 } 138 139 return GlobalTracingEnabled || DTraceInfo[*pfileid].enabled || DTraceInfo[*plineid].enabled; 140 } 141 142 /* 143 * Initialize trace functionality. This MUST BE CALLED before any 144 * tracing function is called. 145 */ 146 void DTrace_Initialize() { 147 DTraceMutex = DMutex_Create(); 148 } 149 150 /* 151 * Cleans up tracing system. Should be called when tracing functionality 152 * is no longer needed. 153 */ 154 void DTrace_Shutdown() { 155 DMutex_Destroy(DTraceMutex); 156 } 157 158 void DTrace_DisableMutex() { 159 DTraceMutex = NULL; 160 } 161 162 /* 163 * Enable tracing for all modules. 164 */ 165 void DTrace_EnableAll(dbool_t enabled) { 166 DMutex_Enter(DTraceMutex); 167 GlobalTracingEnabled = enabled; 168 DMutex_Exit(DTraceMutex); 169 } 170 171 /* 172 * Enable tracing for a specific module. Filename may 173 * be fully or partially qualified. 174 * e.g. awt_Component.cpp 175 * or 176 * src\win32\native\sun\windows\awt_Component.cpp 177 */ 178 void DTrace_EnableFile(const char * file, dbool_t enabled) { 179 dtrace_id tid; 180 p_dtrace_info info; 181 182 DASSERT(file != NULL); 183 DMutex_Enter(DTraceMutex); 184 tid = DTrace_GetTraceId(file, -1, DTRACE_FILE); 185 info = DTrace_GetInfo(tid); 186 info->enabled = enabled; 187 DMutex_Exit(DTraceMutex); 188 } 189 190 /* 191 * Enable tracing for a specific line in a specific module. 192 * See comments above regarding filename argument. 193 */ 194 void DTrace_EnableLine(const char * file, int line, dbool_t enabled) { 195 dtrace_id tid; 196 p_dtrace_info info; 197 198 DASSERT(file != NULL && (line > 0 && line < MAX_LINE)); 199 DMutex_Enter(DTraceMutex); 200 tid = DTrace_GetTraceId(file, line, DTRACE_LINE); 201 info = DTrace_GetInfo(tid); 202 info->enabled = enabled; 203 DMutex_Exit(DTraceMutex); 204 } 205 206 static void DTrace_ClientPrint(const char * msg) { 207 DASSERT(msg != NULL && PfnTraceCallback != NULL); 208 (*PfnTraceCallback)(msg); 209 } 210 211 /* 212 * Print implementation for the use of client defined trace macros. Unsynchronized so it must 213 * be used from within a DTRACE_PRINT_CALLBACK function. 214 */ 215 void DTrace_VPrintImpl(const char * fmt, va_list arglist) { 216 DASSERT(fmt != NULL); 217 218 /* format the trace message */ 219 vsprintf(DTraceBuffer, fmt, arglist); 220 /* not a real great overflow check (memory would already be hammered) but better than nothing */ 221 DASSERT(strlen(DTraceBuffer) < MAX_TRACE_BUFFER); 222 /* output the trace message */ 223 DTrace_ClientPrint(DTraceBuffer); 224 } 225 226 /* 227 * Print implementation for the use of client defined trace macros. Unsynchronized so it must 228 * be used from within a DTRACE_PRINT_CALLBACK function. 229 */ 230 void DTrace_PrintImpl(const char * fmt, ...) { 231 va_list arglist; 232 233 va_start(arglist, fmt); 234 DTrace_VPrintImpl(fmt, arglist); 235 va_end(arglist); 236 } 237 238 /* 239 * Called via DTRACE_PRINT macro. Outputs printf style formatted text. 240 * JNIEXPORT because these functions are also called from libawt_xawt. 241 */ 242 JNIEXPORT void JNICALL 243 DTrace_VPrint( const char * file, int line, int argc, const char * fmt, va_list arglist ) { 244 DASSERT(fmt != NULL); 245 DTrace_VPrintImpl(fmt, arglist); 246 } 247 248 /* 249 * Called via DTRACE_PRINTLN macro. Outputs printf style formatted text with an automatic newline. 250 * JNIEXPORT because these functions are also called from libawt_xawt. 251 */ 252 JNIEXPORT void JNICALL 253 DTrace_VPrintln( const char * file, int line, int argc, const char * fmt, va_list arglist ) { 254 DTrace_VPrintImpl(fmt, arglist); 255 DTrace_PrintImpl("\n"); 256 } 257 258 /* 259 * Called via DTRACE_ macros. If tracing is enabled at the given location, it enters 260 * the trace mutex and invokes the callback function to output the trace. 261 * JNIEXPORT because these functions are also called from libawt_xawt. 262 */ 263 JNIEXPORT void JNICALL 264 DTrace_PrintFunction( DTRACE_PRINT_CALLBACK pfn, dtrace_id * pFileTraceId, dtrace_id * pLineTraceId, 265 const char * file, int line, 266 int argc, const char * fmt, ... ) { 267 va_list arglist; 268 269 DASSERT(file != NULL); 270 DASSERT(line > 0 && line < MAX_LINE); 271 DASSERT(argc <= MAX_ARGC); 272 DASSERT(fmt != NULL); 273 274 DMutex_Enter(DTraceMutex); 275 if ( DTrace_IsEnabledAt(pFileTraceId, pLineTraceId, file, line) ) { 276 va_start(arglist, fmt); 277 (*pfn)(file, line, argc, fmt, arglist); 278 va_end(arglist); 279 } 280 DMutex_Exit(DTraceMutex); 281 } 282 283 /* 284 * Sets a callback function to be used to output 285 * trace statements. 286 */ 287 void DTrace_SetOutputCallback(DTRACE_OUTPUT_CALLBACK pfn) { 288 DASSERT(pfn != NULL); 289 290 DMutex_Enter(DTraceMutex); 291 PfnTraceCallback = pfn; 292 DMutex_Exit(DTraceMutex); 293 } 294 295 #endif /* DEBUG */ 296 297 /********************************************************************************** 298 * Support for Java tracing in release or debug mode builds 299 */ 300 301 static void DTrace_PrintStdErr(const char *msg) { 302 fprintf(stderr, "%s", msg); 303 fflush(stderr); 304 } 305 306 static void DTrace_JavaPrint(const char * msg) { 307 #if defined(DEBUG) 308 DMutex_Enter(DTraceMutex); 309 DTrace_ClientPrint(msg); 310 DMutex_Exit(DTraceMutex); 311 #else 312 DTrace_PrintStdErr(msg); 313 #endif 314 } 315 316 static void DTrace_JavaPrintln(const char * msg) { 317 #if defined(DEBUG) 318 DMutex_Enter(DTraceMutex); 319 DTrace_ClientPrint(msg); 320 DTrace_ClientPrint("\n"); 321 DMutex_Exit(DTraceMutex); 322 #else 323 DTrace_PrintStdErr(msg); 324 DTrace_PrintStdErr("\n"); 325 #endif 326 } 327 328 /********************************************************************************* 329 * Native method implementations. Java print trace calls are functional in 330 * release builds, but functions to enable/disable native tracing are not. 331 */ 332 333 /* Implementation of DebugSettings.setCTracingOn*/ 334 JNIEXPORT void JNICALL 335 Java_sun_awt_DebugSettings_setCTracingOn__Z(JNIEnv *env, jobject self, jboolean enabled) { 336 #if defined(DEBUG) 337 DTrace_EnableAll(enabled == JNI_TRUE); 338 #endif 339 } 340 341 /* Implementation of DebugSettings.setCTracingOn*/ 342 JNIEXPORT void JNICALL 343 Java_sun_awt_DebugSettings_setCTracingOn__ZLjava_lang_String_2( 344 JNIEnv *env, 345 jobject self, 346 jboolean enabled, 347 jstring file ) { 348 #if defined(DEBUG) 349 const char * cfile; 350 cfile = JNU_GetStringPlatformChars(env, file, NULL); 351 if ( cfile == NULL ) { 352 return; 353 } 354 DTrace_EnableFile(cfile, enabled == JNI_TRUE); 355 JNU_ReleaseStringPlatformChars(env, file, cfile); 356 #endif 357 } 358 359 /* Implementation of DebugSettings.setCTracingOn*/ 360 JNIEXPORT void JNICALL 361 Java_sun_awt_DebugSettings_setCTracingOn__ZLjava_lang_String_2I( 362 JNIEnv *env, 363 jobject self, 364 jboolean enabled, 365 jstring file, 366 jint line ) { 367 #if defined(DEBUG) 368 const char * cfile; 369 cfile = JNU_GetStringPlatformChars(env, file, NULL); 370 if ( cfile == NULL ) { 371 return; 372 } 373 DTrace_EnableLine(cfile, line, enabled == JNI_TRUE); 374 JNU_ReleaseStringPlatformChars(env, file, cfile); 375 #endif 376 }