Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | /* |
2 | * ========== Copyright Header Begin ========================================== | |
3 | * | |
4 | * OpenSPARC T2 Processor File: lprintf.c | |
5 | * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
6 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. | |
7 | * | |
8 | * The above named program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public | |
10 | * License version 2 as published by the Free Software Foundation. | |
11 | * | |
12 | * The above named program is distributed in the hope that it will be | |
13 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public | |
18 | * License along with this work; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
20 | * | |
21 | * ========== Copyright Header End ============================================ | |
22 | */ | |
23 | /* | |
24 | * Copyright 2006 Sun Microsystems, Inc. All rights reserved. | |
25 | * Use is subject to license terms. | |
26 | */ | |
27 | #pragma ident "@(#)lprintf.c 1.12 06/08/14 SMI" | |
28 | ||
29 | #include <stdarg.h> | |
30 | #include <stdio.h> | |
31 | #include <errno.h> | |
32 | #include <string.h> | |
33 | #include <pthread.h> | |
34 | #include <unistd.h> | |
35 | ||
36 | #include <assert.h> | |
37 | ||
38 | #include "basics.h" | |
39 | #include "fatal.h" | |
40 | #include "allocate.h" | |
41 | #include "options.h" | |
42 | ||
43 | ||
44 | #ifndef NDEBUG | |
45 | typedef struct { | |
46 | int id; | |
47 | int off; | |
48 | int len; | |
49 | int size; | |
50 | } log_entry_t; | |
51 | ||
52 | ||
53 | #define LOGLINES 1000 | |
54 | #define LOGLINE_SIZE 80 | |
55 | #define LOGSPACE (LOGLINES*LOGLINE_SIZE) | |
56 | ||
57 | ||
58 | static struct { | |
59 | pthread_mutex_t lock; | |
60 | bool_t enabled; | |
61 | FILE *fp; /* output file handle */ | |
62 | char * fp_buffer; | |
63 | log_entry_t entry[LOGLINES]; | |
64 | int idx; | |
65 | int num; | |
66 | char * bufferp; | |
67 | int bufsize; | |
68 | int freeoff; | |
69 | } log; | |
70 | #endif /* NDEBUG */ | |
71 | ||
72 | ||
73 | static void log_internal_printf(int id, char * fmt, va_list args); | |
74 | ||
75 | ||
76 | ||
77 | #ifndef NDEBUG | |
78 | void log_init() | |
79 | { | |
80 | log.bufsize = LOGSPACE; | |
81 | log.bufferp = Xcalloc(log.bufsize, char); | |
82 | log.num = LOGLINES; | |
83 | ||
84 | pthread_mutex_init(&log.lock, NULL); | |
85 | log.idx = 0; | |
86 | log.freeoff = 0; | |
87 | ||
88 | log.enabled = options.analyzer; | |
89 | if (options.log_file != NULL) { | |
90 | log.fp = fopen(options.log_file, "w"); | |
91 | if (log.fp == NULL) | |
92 | fatal("cannot open log: %s", options.log_file); | |
93 | } else | |
94 | log.fp = stdout; /* stdout by default */ | |
95 | ||
96 | /* | |
97 | * Was any buffering requested? | |
98 | */ | |
99 | ||
100 | if (options.buffersz != -1) { | |
101 | if (options.buffersz <= 0) { | |
102 | (void) setvbuf(log.fp, NULL, _IOLBF, 0); | |
103 | } else { | |
104 | log.fp_buffer = Xmalloc(options.buffersz); | |
105 | (void) setvbuf(log.fp, log.fp_buffer, | |
106 | _IOFBF, options.buffersz); | |
107 | } | |
108 | } | |
109 | } | |
110 | ||
111 | #define NO_LOG_FP log.fp | |
112 | #else /* NDEBUG */ | |
113 | ||
114 | /* Output stream if no logging enabled. */ | |
115 | #define NO_LOG_FP stdout | |
116 | #endif /* NDEBUG */ | |
117 | ||
118 | ||
119 | /* | |
120 | * Very simple printf scheme for a rotating log file ... | |
121 | * ... has a generous buffer but checks for potential overflow | |
122 | * | |
123 | * Idea is that it logs the last N lines of printf output into a rotating buffer Q | |
124 | * excellent way to thrash the heap, but useful for debugging. | |
125 | */ | |
126 | ||
127 | void lprintf(int id, char * fmt, ...) | |
128 | { | |
129 | va_list args; | |
130 | ||
131 | va_start(args, fmt); | |
132 | log_lock(); | |
133 | log_internal_printf(id, fmt, args); | |
134 | log_unlock(); | |
135 | va_end(args); | |
136 | } | |
137 | ||
138 | ||
139 | ||
140 | ||
141 | void log_lock() | |
142 | { | |
143 | flockfile(NO_LOG_FP); | |
144 | #ifndef NDEBUG | |
145 | pthread_mutex_lock( &log.lock ); | |
146 | #endif /* NDEBUG */ | |
147 | } | |
148 | ||
149 | ||
150 | ||
151 | void log_unlock() | |
152 | { | |
153 | #ifndef NDEBUG | |
154 | pthread_mutex_unlock( &log.lock ); | |
155 | #endif /* NDEBUG */ | |
156 | funlockfile(NO_LOG_FP); | |
157 | } | |
158 | ||
159 | ||
160 | ||
161 | /* | |
162 | * Core printf function. | |
163 | * Assumes you've already requested log_lock(); | |
164 | */ | |
165 | void log_printf(int id, char * fmt, ...) | |
166 | { | |
167 | va_list args; | |
168 | ||
169 | va_start(args, fmt); | |
170 | log_internal_printf(id, fmt, args); | |
171 | va_end(args); | |
172 | } | |
173 | ||
174 | ||
175 | static void log_internal_printf(int id, char * fmt, va_list args) | |
176 | { | |
177 | #ifndef NDEBUG | |
178 | int len, lostsize, space; | |
179 | #endif /* NDEBUG */ | |
180 | ||
181 | #ifndef NDEBUG | |
182 | if (!log.enabled) { | |
183 | #endif /* NDEBUG */ | |
184 | if (id != -1) | |
185 | fprintf(NO_LOG_FP,"[%d] ",id); | |
186 | vfprintf(NO_LOG_FP,fmt,args); | |
187 | #ifndef NDEBUG | |
188 | return; | |
189 | } | |
190 | ||
191 | /* | |
192 | * Dont bother tracking how many live lines there are... | |
193 | * just bother assigning pointers correctly ... | |
194 | * then determine validity in the dump routine. | |
195 | */ | |
196 | ||
197 | lostsize = 0; | |
198 | loop:; | |
199 | space = log.bufsize - log.freeoff; | |
200 | ASSERT(space>0); | |
201 | /* | |
202 | * returned len is the full output length, | |
203 | * not including the null byte | |
204 | */ | |
205 | len = vsnprintf(log.bufferp + log.freeoff, space, fmt, args); | |
206 | ||
207 | if (len >= space) { | |
208 | /* didn't get all the output */ | |
209 | if (log.freeoff == 0) | |
210 | fatal("lprintf: Ug! LOGSPACE needs to be raised to at least %d\n", len+1); | |
211 | log.freeoff = 0; | |
212 | lostsize = space; | |
213 | goto loop; | |
214 | } | |
215 | ||
216 | log.idx++; | |
217 | if (log.idx>=log.num) log.idx=0; | |
218 | ||
219 | log.entry[log.idx].id = id; | |
220 | log.entry[log.idx].off = log.freeoff; | |
221 | log.entry[log.idx].len = len; | |
222 | log.entry[log.idx].size = lostsize; | |
223 | ||
224 | log.freeoff += len; | |
225 | if (log.freeoff == log.bufsize) log.freeoff = 0; | |
226 | #endif /* NDEBUG */ | |
227 | } | |
228 | ||
229 | ||
230 | ||
231 | void log_flush() | |
232 | { | |
233 | log_lock(); | |
234 | ||
235 | fflush(NO_LOG_FP); | |
236 | ||
237 | log_unlock(); | |
238 | } | |
239 | ||
240 | void log_flush_unlock() | |
241 | { | |
242 | fflush(NO_LOG_FP); | |
243 | ||
244 | log_unlock(); | |
245 | } | |
246 | ||
247 | #ifndef NDEBUG | |
248 | void log_dump() | |
249 | { | |
250 | int i,c,num,space; | |
251 | ||
252 | if (!log.enabled) | |
253 | return; | |
254 | ||
255 | log_lock(); | |
256 | ||
257 | fflush(NO_LOG_FP); | |
258 | ||
259 | i=log.idx; | |
260 | space = log.entry[i].size + log.entry[i].len; | |
261 | for(num = 0; space<log.bufsize && num<log.num; num++) { | |
262 | i--; | |
263 | if (i<0) i=log.num-1; | |
264 | space += log.entry[i].size + log.entry[i].len; | |
265 | } | |
266 | ||
267 | for (c=0; c<num; c++) { | |
268 | i++; | |
269 | if (i>=log.num) i=0; | |
270 | ||
271 | if (log.entry[i].id != -1) | |
272 | fprintf(log.fp, "[%d] ", log.entry[i].id); | |
273 | fwrite(log.bufferp + log.entry[i].off, log.entry[i].len, 1, log.fp); | |
274 | } | |
275 | fflush(log.fp); | |
276 | ||
277 | log_unlock(); | |
278 | } | |
279 | #endif /* NDEBUG */ | |
280 |