1 /* 2 * Copyright (c) 1999, 2001, 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 */ 241 void DTrace_VPrint( const char * file, int line, int argc, const char * fmt, va_list arglist ) { 242 DASSERT(fmt != NULL); 243 DTrace_VPrintImpl(fmt, arglist); 244 } 245 246 /* 247 * Called via DTRACE_PRINTLN macro. Outputs printf style formatted text with an automatic newline. 248 */ 249 void DTrace_VPrintln( const char * file, int line, int argc, const char * fmt, va_list arglist ) { 250 DTrace_VPrintImpl(fmt, arglist); 251 DTrace_PrintImpl("\n"); 252 } 253 254 /* 255 * Called via DTRACE_ macros. If tracing is enabled at the given location, it enters 256 * the trace mutex and invokes the callback function to output the trace. 257 */ 258 void DTrace_PrintFunction( DTRACE_PRINT_CALLBACK pfn, dtrace_id * pFileTraceId, dtrace_id * pLineTraceId, 259 const char * file, int line, 260 int argc, const char * fmt, ... ) { 261 va_list arglist; 262 263 DASSERT(file != NULL); 264 DASSERT(line > 0 && line < MAX_LINE); 265 DASSERT(argc <= MAX_ARGC); 266 DASSERT(fmt != NULL); 267 268 DMutex_Enter(DTraceMutex); 269 if ( DTrace_IsEnabledAt(pFileTraceId, pLineTraceId, file, line) ) { 270 va_start(arglist, fmt); 271 (*pfn)(file, line, argc, fmt, arglist); 272 va_end(arglist); 273 } 274 DMutex_Exit(DTraceMutex); 275 } 276 277 /* 278 * Sets a callback function to be used to output 279 * trace statements. 280 */ 281 void DTrace_SetOutputCallback(DTRACE_OUTPUT_CALLBACK pfn) { 282 DASSERT(pfn != NULL); 283 284 DMutex_Enter(DTraceMutex); 285 PfnTraceCallback = pfn; 286 DMutex_Exit(DTraceMutex); 287 } 288 289 #endif /* DEBUG */ 290 291 /********************************************************************************** 292 * Support for Java tracing in release or debug mode builds 293 */ 294 295 static void DTrace_PrintStdErr(const char *msg) { 296 fprintf(stderr, "%s", msg); 297 fflush(stderr); 298 } 299 300 static void DTrace_JavaPrint(const char * msg) { 301 #if defined(DEBUG) 302 DMutex_Enter(DTraceMutex); 303 DTrace_ClientPrint(msg); 304 DMutex_Exit(DTraceMutex); 305 #else 306 DTrace_PrintStdErr(msg); 307 #endif 308 } 309 310 static void DTrace_JavaPrintln(const char * msg) { 311 #if defined(DEBUG) 312 DMutex_Enter(DTraceMutex); 313 DTrace_ClientPrint(msg); 314 DTrace_ClientPrint("\n"); 315 DMutex_Exit(DTraceMutex); 316 #else 317 DTrace_PrintStdErr(msg); 318 DTrace_PrintStdErr("\n"); 319 #endif 320 } 321 322 /********************************************************************************* 323 * Native method implementations. Java print trace calls are functional in 324 * release builds, but functions to enable/disable native tracing are not. 325 */ 326 327 /* Implementation of DebugSettings.setCTracingOn*/ 328 JNIEXPORT void JNICALL 329 Java_sun_awt_DebugSettings_setCTracingOn__Z(JNIEnv *env, jobject self, jboolean enabled) { 330 #if defined(DEBUG) 331 DTrace_EnableAll(enabled == JNI_TRUE); 332 #endif 333 } 334 335 /* Implementation of DebugSettings.setCTracingOn*/ 336 JNIEXPORT void JNICALL 337 Java_sun_awt_DebugSettings_setCTracingOn__ZLjava_lang_String_2( 338 JNIEnv *env, 339 jobject self, 340 jboolean enabled, 341 jstring file ) { 342 #if defined(DEBUG) 343 const char * cfile; 344 cfile = JNU_GetStringPlatformChars(env, file, NULL); 345 if ( cfile == NULL ) { 346 return; 347 } 348 DTrace_EnableFile(cfile, enabled == JNI_TRUE); 349 JNU_ReleaseStringPlatformChars(env, file, cfile); 350 #endif 351 } 352 353 /* Implementation of DebugSettings.setCTracingOn*/ 354 JNIEXPORT void JNICALL 355 Java_sun_awt_DebugSettings_setCTracingOn__ZLjava_lang_String_2I( 356 JNIEnv *env, 357 jobject self, 358 jboolean enabled, 359 jstring file, 360 jint line ) { 361 #if defined(DEBUG) 362 const char * cfile; 363 cfile = JNU_GetStringPlatformChars(env, file, NULL); 364 if ( cfile == NULL ) { 365 return; 366 } 367 DTrace_EnableLine(cfile, line, enabled == JNI_TRUE); 368 JNU_ReleaseStringPlatformChars(env, file, cfile); 369 #endif 370 }