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