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 }