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 JNICALL 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 JNICALL 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 | } |
377 | |