| /* | 
 |  * Copyright 2007 Google Inc. | 
 |  *  | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); you may not | 
 |  * use this file except in compliance with the License. You may obtain a copy of | 
 |  * the License at | 
 |  *  | 
 |  * http://www.apache.org/licenses/LICENSE-2.0 | 
 |  *  | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | 
 |  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | 
 |  * License for the specific language governing permissions and limitations under | 
 |  * the License. | 
 |  */ | 
 | #ifndef JNI_LINUX_TRACER_H_ | 
 | #define JNI_LINUX_TRACER_H_ | 
 |  | 
 | #include <cstdio> | 
 | #include <cstdarg> | 
 | #include <cstring> | 
 | #include <jni.h> | 
 |  | 
 | // comment this out to remove almost all runtime overhead (with usual compiler | 
 | // support) from tracing. | 
 | //#define ENABLE_TRACING | 
 |  | 
 | /* | 
 |  * Utility class for tracing.  This class is intended to be used as follows: | 
 |  *  | 
 |  * { | 
 |  *   Tracer tracer("method name"); | 
 |  *   ... do work | 
 |  *   if (fail) { | 
 |  *      tracer.setFail("failure explanation"); | 
 |  *      return; | 
 |  *   } | 
 |  *   if (fail2) { | 
 |  *      tracer.throwHostedModeException("failure explanation"); | 
 |  *      return; | 
 |  *   } | 
 |  *   return; | 
 |  * } | 
 |  *  | 
 |  * The class automatically logs an enter message when it is created, as well | 
 |  * as leave/fail messages when it is destroyed.  Logging is performed to a | 
 |  * file or to a Java static member function on a class (or both) -- these | 
 |  * are configured by using static member functions setFile() and setJava(). | 
 |  *  | 
 |  * This class knows about the Java class | 
 |  *   com.google.gwt.dev.shell.HostedModeException | 
 |  * and throws a new instance of that exception if requested. | 
 |  */ | 
 | class Tracer { | 
 | public: | 
 |   enum LogLevel { | 
 |     LEVEL_ERROR = 0, | 
 |     LEVEL_WARNING, | 
 |     LEVEL_NOTICE, | 
 |     LEVEL_INFO, | 
 |     LEVEL_DEBUG, | 
 |     LEVEL_DEBUG_V1, | 
 |     LEVEL_DEBUG_V2, | 
 |   }; | 
 | protected: | 
 | #ifdef ENABLE_TRACING | 
 |   // static variables that specify where logging is performed.  This are | 
 |   // set by calling setFile() and setJava(). | 
 |   static FILE*     outfp; | 
 |   static JNIEnv*   jniEnv; | 
 |   static jclass    traceClass; | 
 |   static jmethodID traceMethod; | 
 |   static int       indentation; | 
 |   static LogLevel  logLevel; | 
 |  | 
 |   // method is set when the instance is created. | 
 |   const char*		method_; | 
 |   // fail_msg is set to indicate a failure has occurred. | 
 |   const char*		fail_msg_; | 
 |   // level of this trace object | 
 |   LogLevel  log_level_; | 
 | #endif | 
 |    | 
 | public: | 
 |   /* | 
 |    * Set the logging level. | 
 |    */ | 
 |   static void setLevel(LogLevel level) { | 
 | #ifdef ENABLE_TRACING | 
 |     logLevel = level; | 
 | #endif | 
 |   } | 
 |    | 
 | protected: | 
 |   /* | 
 |    * Log a message (with supplied prefix) to the configured file. | 
 |    * Only called if a file was specified and successfully opened for writing. | 
 |    */ | 
 |   static void logFile(const char* msg) { | 
 | #ifdef ENABLE_TRACING | 
 |     for (int i = 0; i < indentation; ++i) { | 
 |       putc(' ', outfp); | 
 |     } | 
 |     fputs(msg, outfp); | 
 |     putc('\n', outfp); | 
 |     fflush(outfp); | 
 | #else | 
 |     (void)msg; // avoid unused warning | 
 | #endif | 
 |   } | 
 |  | 
 |   /* | 
 |    * Log a message (with supplied prefix) to the configured Java class. | 
 |    * Only called if a file was specified and successfully accessed. | 
 |    *  | 
 |    * Call static void trace(String msg) on the configured class.  | 
 |    */ | 
 |   static void logJava(const char* msg) { | 
 | #ifdef ENABLE_TRACING | 
 |     // TODO(jat): fixed buffer size | 
 |     char buf[512]; | 
 |     for (int i = 0; (i < indentation) && (i < int(sizeof(buf))); ++i) { | 
 |       buf[i] = ' '; | 
 |     } | 
 |     strncpy(buf + indentation, msg, sizeof(buf) - indentation); | 
 |     buf[sizeof(buf) - 1] = 0; // ensure null termination | 
 |     jstring str = jniEnv->NewStringUTF(buf); | 
 |     jniEnv->CallStaticVoidMethod(traceClass, traceMethod, str); | 
 | #else | 
 |     (void)msg; // avoid unused warning | 
 | #endif | 
 |   } | 
 |  | 
 |   /* | 
 |    * Log a message to a file and/or class with the default logging level. | 
 |    *  | 
 |    * If the preprocessor symbol DISABLE_TRACING has been defined, this is | 
 |    * completely removed from the code path. | 
 |    */ | 
 |   void logPrefix(const char* prefix) { | 
 | #ifdef ENABLE_TRACING | 
 |     logPrefix(prefix, log_level_); | 
 | #else | 
 |     (void)prefix; // avoid unused warning | 
 | #endif | 
 |   } | 
 |    | 
 |   /* | 
 |    * Log a message to a file and/or class. | 
 |    *  | 
 |    * If the preprocessor symbol DISABLE_TRACING has been defined, this is | 
 |    * completely removed from the code path. | 
 |    */ | 
 |   void logPrefix(const char* prefix, LogLevel level) { | 
 | #ifdef ENABLE_TRACING | 
 |     if (level>logLevel) return; | 
 |     log("%-5.5s %s%s%s", prefix, method_, fail_msg_ ? ": " : "", | 
 |         fail_msg_ ? fail_msg_ : ""); | 
 | #endif | 
 |   } | 
 |      | 
 | public: | 
 |   /* | 
 |    * Create an instance with the specified method name and no failure | 
 |    * message.  Log an ENTER message. | 
 |    */ | 
 |   Tracer(const char* method, LogLevel log_level = LEVEL_ERROR) | 
 | #ifdef ENABLE_TRACING | 
 |       : method_(method), fail_msg_(0), log_level_(log_level) { | 
 |     log("ENTER %s", method); | 
 |     indentation++; | 
 | #else | 
 |   { (void)method; (void)log_level; // avoid unused warnings | 
 | #endif | 
 |   } | 
 |  | 
 |   /* | 
 |    * Create an instance with the specified method name and no failure | 
 |    * message.  Log an ENTER message and the this pointer. | 
 |    */ | 
 |   Tracer(const char* method, const void* objThis, | 
 |       LogLevel log_level = LEVEL_ERROR) | 
 | #ifdef ENABLE_TRACING | 
 |       : method_(method), fail_msg_(0), log_level_(log_level) { | 
 |     log("ENTER %s(this=%08x)", method, unsigned(objThis)); | 
 |     indentation++; | 
 | #else | 
 |   { (void)method; (void)objThis; (void)log_level; // avoid unused warnings | 
 | #endif | 
 |   } | 
 |  | 
 |   /* | 
 |    * Destroy the instance and log a fail or leave message. | 
 |    */ | 
 |   ~Tracer() { | 
 | #ifdef ENABLE_TRACING | 
 |     --indentation; | 
 |     if(fail_msg_) { | 
 |       logPrefix("*FAIL", LEVEL_ERROR); | 
 |     } else { | 
 |       logPrefix("LEAVE"); | 
 |     } | 
 | #endif | 
 |   } | 
 |    | 
 |   /* | 
 |    * Specify a filename to receive logging output.  Close any previously | 
 |    * opened file.  If a null filename is passed, disable logging to a | 
 |    * file. | 
 |    *  | 
 |    * filename - the file path to receive logging output.  This file is | 
 |    *     truncated if it already exists. | 
 |    *  | 
 |    * Returns false on failure. | 
 |    */ | 
 |   static bool setFile(const char* filename) { | 
 | #ifdef ENABLE_TRACING | 
 |     if (outfp) { | 
 |       fclose(outfp); | 
 |       outfp = 0; | 
 |     } | 
 |     if (!filename) { | 
 |       return true; | 
 |     } | 
 |     outfp = fopen(filename, "w"); | 
 |     if (!outfp) { | 
 |       return false; | 
 |     } | 
 |     fprintf(outfp, "== started logging ==\n"); | 
 |     fflush(outfp); | 
 | #else | 
 |     (void)filename; // avoid unused warning | 
 | #endif | 
 |     return true; | 
 |   } | 
 |    | 
 |   /* | 
 |    * Specify a Java class to receive logging output.  The supplied class | 
 |    * must have a static void trace(String) member function which is called | 
 |    * for output.  Logging to a Java class is disabled if the supplied JNI | 
 |    * environment is null. | 
 |    *  | 
 |    * env - JNI environment | 
 |    * clazz - the Java class to receive logging output | 
 |    *  | 
 |    * Returns false on failure. | 
 |    */  | 
 |   static bool setJava(JNIEnv* env, jclass clazz) | 
 | #ifdef ENABLE_TRACING   | 
 |   ; | 
 | #else | 
 |   // inline a null body if we aren't debugging; avoid unused warnings | 
 |   { (void)env; (void)clazz; return true; } | 
 | #endif | 
 |    | 
 |   /* | 
 |    * Set a failure message, overwriting any previously specified failure | 
 |    * message.  Passing a null string will remove any previous failure | 
 |    * notification. | 
 |    */ | 
 |   void setFail(const char* fail_msg) { | 
 | #ifdef ENABLE_TRACING | 
 |     fail_msg_ = fail_msg; | 
 | #else | 
 |     (void)fail_msg; // avoid unused warning | 
 | #endif | 
 |   } | 
 |    | 
 |   /* | 
 |    * Throw a Java HostedModeException as well as set a failure message to | 
 |    * be logged. | 
 |    *  | 
 |    * env - JNI environment to throw exception into | 
 |    * fail_msg - failure message  | 
 |    */ | 
 |   void throwHostedModeException(JNIEnv* env, const char* fail_msg); | 
 |    | 
 |   /* | 
 |    * Log an arbitrary message. | 
 |    */ | 
 |   static void log(const char* format, ...) { | 
 | #ifdef ENABLE_TRACING | 
 |     va_list args; | 
 |     va_start(args, format); | 
 |     char msg[512]; // TODO(jat): fixed size buffer | 
 |     vsnprintf(msg, sizeof(msg), format, args); | 
 |     msg[sizeof(msg) - 1] = 0; // ensure null termination | 
 |     if(outfp) logFile(msg); | 
 |     if(jniEnv) logJava(msg); | 
 |     va_end(args); | 
 | #else | 
 |     (void)format; // avoid unused warning | 
 | #endif | 
 |   } | 
 | }; | 
 |  | 
 | #endif /* JNI_LINUX_TRACER_H_ */ |