1 /*
   2  * Copyright (c) 2003, 2005, 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 "util.h"
  27 
  28 #include <time.h>
  29 #include <errno.h>
  30 #include <sys/types.h>
  31 
  32 #include "proc_md.h"
  33 
  34 #include "log_messages.h"
  35 
  36 #ifdef JDWP_LOGGING
  37 
  38 #define MAXLEN_INTEGER          20
  39 #define MAXLEN_FILENAME         256
  40 #define MAXLEN_TIMESTAMP        80
  41 #define MAXLEN_LOCATION         (MAXLEN_FILENAME+MAXLEN_INTEGER+16)
  42 #define MAXLEN_MESSAGE          256
  43 #define MAXLEN_EXEC             (MAXLEN_FILENAME*2+MAXLEN_INTEGER+16)
  44 
  45 static MUTEX_T my_mutex = MUTEX_INIT;
  46 
  47 /* Static variables (should be protected with mutex) */
  48 static int logging;
  49 static FILE * log_file;
  50 static char logging_filename[MAXLEN_FILENAME+1+6];
  51 static char location_stamp[MAXLEN_LOCATION+1];
  52 static PID_T processPid;
  53 static int open_count;
  54 
  55 /* Ascii id of current native thread. */
  56 static void
  57 get_time_stamp(char *tbuf, size_t ltbuf)
  58 {
  59     char timestamp_prefix[MAXLEN_TIMESTAMP+1];
  60     char timestamp_postfix[MAXLEN_TIMESTAMP+1];
  61     unsigned millisecs = 0;
  62     time_t t = 0;
  63 
  64     GETMILLSECS(millisecs);
  65     if ( time(&t) == (time_t)(-1) )
  66         t = 0;
  67     (void)strftime(timestamp_prefix, sizeof(timestamp_prefix),
  68                 /* Break this string up for SCCS's sake */
  69                 "%" "d.%" "m.%" "Y %" "T", localtime(&t));
  70     (void)strftime(timestamp_postfix, sizeof(timestamp_postfix),
  71                 /* Break this string up for SCCS's sake */
  72                 "%" "Z", localtime(&t));
  73     (void)snprintf(tbuf, ltbuf, "%s.%.3d %s", timestamp_prefix, (int)(millisecs), timestamp_postfix);
  74 }
  75 
  76 /* Get basename of filename */
  77 static const char *
  78 file_basename(const char *file)
  79 {
  80     char *p1;
  81     char *p2;
  82 
  83     if ( file==NULL )
  84         return "unknown";
  85     p1 = strrchr(file, '\\');
  86     p2 = strrchr(file, '/');
  87     p1 = ((p1 > p2) ? p1 : p2);
  88     if (p1 != NULL) {
  89         file = p1 + 1;
  90     }
  91     return file;
  92 }
  93 
  94 /* Fill in the exact source location of the LOG entry. */
  95 static void
  96 fill_location_stamp(const char *flavor, const char *file, int line)
  97 {
  98     (void)snprintf(location_stamp, sizeof(location_stamp),
  99                     "%s:\"%s\":%d;",
 100                     flavor, file_basename(file), line);
 101     location_stamp[sizeof(location_stamp)-1] = 0;
 102 }
 103 
 104 /* Begin a log entry. */
 105 void
 106 log_message_begin(const char *flavor, const char *file, int line)
 107 {
 108     MUTEX_LOCK(my_mutex); /* Unlocked in log_message_end() */
 109     if ( logging ) {
 110         location_stamp[0] = 0;
 111         fill_location_stamp(flavor, file, line);
 112     }
 113 }
 114 
 115 /* Standard Logging Format Entry */
 116 static void
 117 standard_logging_format(FILE *fp,
 118         const char *datetime,
 119         const char *level,
 120         const char *product,
 121         const char *module,
 122         const char *optional,
 123         const char *messageID,
 124         const char *message)
 125 {
 126     const char *format;
 127 
 128     /* "[#|Date&Time&Zone|LogLevel|ProductName|ModuleID|
 129      *     OptionalKey1=Value1;OptionalKeyN=ValueN|MessageID:MessageText|#]\n"
 130      */
 131 
 132     format="[#|%s|%s|%s|%s|%s|%s:%s|#]\n";
 133 
 134     print_message(fp, "", "", format,
 135             datetime,
 136             level,
 137             product,
 138             module,
 139             optional,
 140             messageID,
 141             message);
 142 }
 143 
 144 /* End a log entry */
 145 void
 146 log_message_end(const char *format, ...)
 147 {
 148     if ( logging ) {
 149         va_list ap;
 150         THREAD_T tid;
 151         char datetime[MAXLEN_TIMESTAMP+1];
 152         const char *level;
 153         const char *product;
 154         const char *module;
 155         char optional[MAXLEN_INTEGER+6+MAXLEN_INTEGER+6+MAXLEN_LOCATION+1];
 156         const char *messageID;
 157         char message[MAXLEN_MESSAGE+1];
 158 
 159         /* Grab the location, start file if needed, and clear the lock */
 160         if ( log_file == NULL && open_count == 0 && logging_filename[0] != 0 ) {
 161             open_count++;
 162             log_file = fopen(logging_filename, "w");
 163             if ( log_file!=NULL ) {
 164                 (void)setvbuf(log_file, NULL, _IOLBF, BUFSIZ);
 165             } else {
 166                 logging = 0;
 167             }
 168         }
 169 
 170         if ( log_file != NULL ) {
 171 
 172             /* Get the rest of the needed information */
 173             tid = GET_THREAD_ID();
 174             level = "FINEST"; /* FIXUP? */
 175             product = "J2SE1.5"; /* FIXUP? */
 176             module = "jdwp"; /* FIXUP? */
 177             messageID = ""; /* FIXUP: Unique message string ID? */
 178             (void)snprintf(optional, sizeof(optional),
 179                         "LOC=%s;PID=%d;THR=t@%d",
 180                         location_stamp,
 181                         (int)processPid,
 182                         (int)(intptr_t)tid);
 183 
 184             /* Construct message string. */
 185             va_start(ap, format);
 186             (void)vsnprintf(message, sizeof(message), format, ap);
 187             va_end(ap);
 188 
 189             get_time_stamp(datetime, sizeof(datetime));
 190 
 191             /* Send out standard logging format message */
 192             standard_logging_format(log_file,
 193                 datetime,
 194                 level,
 195                 product,
 196                 module,
 197                 optional,
 198                 messageID,
 199                 message);
 200         }
 201         location_stamp[0] = 0;
 202     }
 203     MUTEX_UNLOCK(my_mutex); /* Locked in log_message_begin() */
 204 }
 205 
 206 #endif
 207 
 208 /* Set up the logging with the name of a logging file. */
 209 void
 210 setup_logging(const char *filename, unsigned flags)
 211 {
 212 #ifdef JDWP_LOGGING
 213     FILE *fp = NULL;
 214 
 215     /* Turn off logging */
 216     logging = 0;
 217     gdata->log_flags = 0;
 218 
 219     /* Just return if not doing logging */
 220     if ( filename==NULL || flags==0 )
 221         return;
 222 
 223     /* Create potential filename for logging */
 224     processPid = GETPID();
 225     (void)snprintf(logging_filename, sizeof(logging_filename),
 226                     "%s.%d", filename, (int)processPid);
 227 
 228     /* Turn on logging (do this last) */
 229     logging = 1;
 230     gdata->log_flags = flags;
 231 
 232 #endif
 233 }
 234 
 235 /* Finish up logging, flush output to the logfile. */
 236 void
 237 finish_logging(int exit_code)
 238 {
 239 #ifdef JDWP_LOGGING
 240     MUTEX_LOCK(my_mutex);
 241     if ( logging ) {
 242         logging = 0;
 243         if ( log_file != NULL ) {
 244             (void)fflush(log_file);
 245             (void)fclose(log_file);
 246             log_file = NULL;
 247         }
 248     }
 249     MUTEX_UNLOCK(my_mutex);
 250 #endif
 251 }