blob: ef9fd7dd3f7ef42959465e0d37ee262fd21b1863 [file] [log] [blame]
jat@google.com64a55cb2009-10-16 14:16:57 +00001/*
2 * Copyright 2007 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16#ifndef JNI_LINUX_TRACER_H_
17#define JNI_LINUX_TRACER_H_
18
19#include <cstdio>
20#include <cstdarg>
21#include <cstring>
22#include <jni.h>
23
24// comment this out to remove almost all runtime overhead (with usual compiler
25// support) from tracing.
26//#define ENABLE_TRACING
27
28/*
29 * Utility class for tracing. This class is intended to be used as follows:
30 *
31 * {
32 * Tracer tracer("method name");
33 * ... do work
34 * if (fail) {
35 * tracer.setFail("failure explanation");
36 * return;
37 * }
38 * if (fail2) {
39 * tracer.throwHostedModeException("failure explanation");
40 * return;
41 * }
42 * return;
43 * }
44 *
45 * The class automatically logs an enter message when it is created, as well
46 * as leave/fail messages when it is destroyed. Logging is performed to a
47 * file or to a Java static member function on a class (or both) -- these
48 * are configured by using static member functions setFile() and setJava().
49 *
50 * This class knows about the Java class
51 * com.google.gwt.dev.shell.HostedModeException
52 * and throws a new instance of that exception if requested.
53 */
54class Tracer {
55public:
56 enum LogLevel {
57 LEVEL_ERROR = 0,
58 LEVEL_WARNING,
59 LEVEL_NOTICE,
60 LEVEL_INFO,
61 LEVEL_DEBUG,
62 LEVEL_DEBUG_V1,
63 LEVEL_DEBUG_V2,
64 };
65protected:
66#ifdef ENABLE_TRACING
67 // static variables that specify where logging is performed. This are
68 // set by calling setFile() and setJava().
69 static FILE* outfp;
70 static JNIEnv* jniEnv;
71 static jclass traceClass;
72 static jmethodID traceMethod;
73 static int indentation;
74 static LogLevel logLevel;
75
76 // method is set when the instance is created.
77 const char* method_;
78 // fail_msg is set to indicate a failure has occurred.
79 const char* fail_msg_;
80 // level of this trace object
81 LogLevel log_level_;
82#endif
83
84public:
85 /*
86 * Set the logging level.
87 */
88 static void setLevel(LogLevel level) {
89#ifdef ENABLE_TRACING
90 logLevel = level;
91#endif
92 }
93
94protected:
95 /*
96 * Log a message (with supplied prefix) to the configured file.
97 * Only called if a file was specified and successfully opened for writing.
98 */
99 static void logFile(const char* msg) {
100#ifdef ENABLE_TRACING
101 for (int i = 0; i < indentation; ++i) {
102 putc(' ', outfp);
103 }
104 fputs(msg, outfp);
105 putc('\n', outfp);
106 fflush(outfp);
107#else
108 (void)msg; // avoid unused warning
109#endif
110 }
111
112 /*
113 * Log a message (with supplied prefix) to the configured Java class.
114 * Only called if a file was specified and successfully accessed.
115 *
116 * Call static void trace(String msg) on the configured class.
117 */
118 static void logJava(const char* msg) {
119#ifdef ENABLE_TRACING
120 // TODO(jat): fixed buffer size
121 char buf[512];
122 for (int i = 0; (i < indentation) && (i < int(sizeof(buf))); ++i) {
123 buf[i] = ' ';
124 }
125 strncpy(buf + indentation, msg, sizeof(buf) - indentation);
126 buf[sizeof(buf) - 1] = 0; // ensure null termination
127 jstring str = jniEnv->NewStringUTF(buf);
128 jniEnv->CallStaticVoidMethod(traceClass, traceMethod, str);
129#else
130 (void)msg; // avoid unused warning
131#endif
132 }
133
134 /*
135 * Log a message to a file and/or class with the default logging level.
136 *
137 * If the preprocessor symbol DISABLE_TRACING has been defined, this is
138 * completely removed from the code path.
139 */
140 void logPrefix(const char* prefix) {
141#ifdef ENABLE_TRACING
142 logPrefix(prefix, log_level_);
143#else
144 (void)prefix; // avoid unused warning
145#endif
146 }
147
148 /*
149 * Log a message to a file and/or class.
150 *
151 * If the preprocessor symbol DISABLE_TRACING has been defined, this is
152 * completely removed from the code path.
153 */
154 void logPrefix(const char* prefix, LogLevel level) {
155#ifdef ENABLE_TRACING
156 if (level>logLevel) return;
157 log("%-5.5s %s%s%s", prefix, method_, fail_msg_ ? ": " : "",
158 fail_msg_ ? fail_msg_ : "");
159#endif
160 }
161
162public:
163 /*
164 * Create an instance with the specified method name and no failure
165 * message. Log an ENTER message.
166 */
167 Tracer(const char* method, LogLevel log_level = LEVEL_ERROR)
168#ifdef ENABLE_TRACING
169 : method_(method), fail_msg_(0), log_level_(log_level) {
170 log("ENTER %s", method);
171 indentation++;
172#else
173 { (void)method; (void)log_level; // avoid unused warnings
174#endif
175 }
176
177 /*
178 * Create an instance with the specified method name and no failure
179 * message. Log an ENTER message and the this pointer.
180 */
181 Tracer(const char* method, const void* objThis,
182 LogLevel log_level = LEVEL_ERROR)
183#ifdef ENABLE_TRACING
184 : method_(method), fail_msg_(0), log_level_(log_level) {
185 log("ENTER %s(this=%08x)", method, unsigned(objThis));
186 indentation++;
187#else
188 { (void)method; (void)objThis; (void)log_level; // avoid unused warnings
189#endif
190 }
191
192 /*
193 * Destroy the instance and log a fail or leave message.
194 */
195 ~Tracer() {
196#ifdef ENABLE_TRACING
197 --indentation;
198 if(fail_msg_) {
199 logPrefix("*FAIL", LEVEL_ERROR);
200 } else {
201 logPrefix("LEAVE");
202 }
203#endif
204 }
205
206 /*
207 * Specify a filename to receive logging output. Close any previously
208 * opened file. If a null filename is passed, disable logging to a
209 * file.
210 *
211 * filename - the file path to receive logging output. This file is
212 * truncated if it already exists.
213 *
214 * Returns false on failure.
215 */
216 static bool setFile(const char* filename) {
217#ifdef ENABLE_TRACING
218 if (outfp) {
219 fclose(outfp);
220 outfp = 0;
221 }
222 if (!filename) {
223 return true;
224 }
225 outfp = fopen(filename, "w");
226 if (!outfp) {
227 return false;
228 }
229 fprintf(outfp, "== started logging ==\n");
230 fflush(outfp);
231#else
232 (void)filename; // avoid unused warning
233#endif
234 return true;
235 }
236
237 /*
238 * Specify a Java class to receive logging output. The supplied class
239 * must have a static void trace(String) member function which is called
240 * for output. Logging to a Java class is disabled if the supplied JNI
241 * environment is null.
242 *
243 * env - JNI environment
244 * clazz - the Java class to receive logging output
245 *
246 * Returns false on failure.
247 */
248 static bool setJava(JNIEnv* env, jclass clazz)
249#ifdef ENABLE_TRACING
250 ;
251#else
252 // inline a null body if we aren't debugging; avoid unused warnings
253 { (void)env; (void)clazz; return true; }
254#endif
255
256 /*
257 * Set a failure message, overwriting any previously specified failure
258 * message. Passing a null string will remove any previous failure
259 * notification.
260 */
261 void setFail(const char* fail_msg) {
262#ifdef ENABLE_TRACING
263 fail_msg_ = fail_msg;
264#else
265 (void)fail_msg; // avoid unused warning
266#endif
267 }
268
269 /*
270 * Throw a Java HostedModeException as well as set a failure message to
271 * be logged.
272 *
273 * env - JNI environment to throw exception into
274 * fail_msg - failure message
275 */
276 void throwHostedModeException(JNIEnv* env, const char* fail_msg);
277
278 /*
279 * Log an arbitrary message.
280 */
281 static void log(const char* format, ...) {
282#ifdef ENABLE_TRACING
283 va_list args;
284 va_start(args, format);
285 char msg[512]; // TODO(jat): fixed size buffer
286 vsnprintf(msg, sizeof(msg), format, args);
287 msg[sizeof(msg) - 1] = 0; // ensure null termination
288 if(outfp) logFile(msg);
289 if(jniEnv) logJava(msg);
290 va_end(args);
291#else
292 (void)format; // avoid unused warning
293#endif
294 }
295};
296
297#endif /* JNI_LINUX_TRACER_H_ */