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