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
28static void DTrace_PrintStdErr(const char *msg);
29
30#if defined(DEBUG)
31enum {
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
38typedef enum dtrace_scope {
39    DTRACE_FILE,
40    DTRACE_LINE
41} dtrace_scope;
42
43typedef 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
50static dtrace_info      DTraceInfo[MAX_TRACES];
51static char             DTraceBuffer[MAX_TRACE_BUFFER*2+1]; /* double the buffer size to catch overruns */
52static dmutex_t         DTraceMutex = NULL;
53static dbool_t          GlobalTracingEnabled = FALSE;
54static int              NumTraces = 0;
55
56static DTRACE_OUTPUT_CALLBACK   PfnTraceCallback = DTrace_PrintStdErr;
57
58static p_dtrace_info DTrace_GetInfo(dtrace_id tid) {
59    DASSERT(tid < MAX_TRACES);
60    return &DTraceInfo[tid];
61}
62
63static 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 */
80static 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 */
102static 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);
122Exit:
123    return tid;
124}
125
126
127static 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 */
146void 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 */
154void DTrace_Shutdown() {
155    DMutex_Destroy(DTraceMutex);
156}
157
158void DTrace_DisableMutex() {
159    DTraceMutex = NULL;
160}
161
162/*
163 * Enable tracing for all modules.
164 */
165void 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 */
178void 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 */
194void 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
206static 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 */
215void 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 */
230void 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 */
241void 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 */
249void 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 */
258void 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 */
281void 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
295static void DTrace_PrintStdErr(const char *msg) {
296    fprintf(stderr, "%s", msg);
297    fflush(stderr);
298}
299
300static 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
310static 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*/
328JNIEXPORT void JNICALL
329Java_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*/
336JNIEXPORT void JNICALL
337Java_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*/
354JNIEXPORT void JNICALL
355Java_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}
371