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 }