]> git.ipfire.org Git - thirdparty/squid.git/blame - src/debug.cc
The recent patch for Bugzilla #172 doesn't compile on HEAD, so I've
[thirdparty/squid.git] / src / debug.cc
CommitLineData
38d04788 1
30a4f2a8 2/*
2d72d4fd 3 * $Id: debug.cc,v 1.81 2001/02/07 18:56:52 hno Exp $
30a4f2a8 4 *
5 * DEBUG: section 0 Debug Routines
6 * AUTHOR: Harvest Derived
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
30a4f2a8 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
30a4f2a8 34 */
ed43818f 35
44a47c6e 36#include "squid.h"
090089c4 37
090089c4 38static char *debug_log_file = NULL;
123abbe1 39static int Ctx_Lock = 0;
4873d3a4 40static const char *debugLogTime(time_t);
2d72d4fd 41static void ctx_print(void);
24382924 42
6de2df60 43#if STDC_HEADERS
b8d8561b 44void
a3d5953d 45_db_print(const char *format,...)
090089c4 46{
75e5a32e 47#if defined(__QNX__)
48 va_list eargs;
49#endif
090089c4 50 va_list args;
51#else
b8d8561b 52void
53_db_print(va_alist)
090089c4 54 va_dcl
55{
090089c4 56 va_list args;
0ee4272b 57 const char *format = NULL;
97f61b08 58#endif
95d659f0 59 LOCAL_ARRAY(char, f, BUFSIZ);
233794cd 60#if HAVE_SYSLOG
95d659f0 61 LOCAL_ARRAY(char, tmpbuf, BUFSIZ);
233794cd 62#endif
090089c4 63
6de2df60 64#if STDC_HEADERS
97f61b08 65 va_start(args, format);
75e5a32e 66#if defined(__QNX__)
67 va_start(eargs, format);
68#endif
090089c4 69#else
70 va_start(args);
74946a0f 71 format = va_arg(args, const char *);
72#endif
73
74 if (debug_log == NULL)
75 return;
123abbe1 76 /* give a chance to context-based debugging to print current context */
77 if (!Ctx_Lock)
78 ctx_print();
042461c3 79 snprintf(f, BUFSIZ, "%s| %s",
4873d3a4 80 debugLogTime(squid_curtime),
396c5745 81 format);
db40ae20 82#if HAVE_SYSLOG
2d22efa6 83 /* level 0,1 go to syslog */
84 if (_db_level <= 1 && opt_syslog_enable) {
12b9e9b1 85 tmpbuf[0] = '\0';
042461c3 86 vsnprintf(tmpbuf, BUFSIZ, format, args);
e82d6d21 87 tmpbuf[BUFSIZ - 1] = '\0';
d737baa0 88 syslog(_db_level == 0 ? LOG_WARNING : LOG_NOTICE, "%s", tmpbuf);
12b9e9b1 89 }
db40ae20 90#endif /* HAVE_SYSLOG */
12b9e9b1 91 /* write to log file */
75e5a32e 92#if defined(__QNX__)
93 vfprintf(debug_log, f, eargs);
94#else
12b9e9b1 95 vfprintf(debug_log, f, args);
75e5a32e 96#endif
17a0a4ee 97 if (!Config.onoff.buffered_logs)
12b9e9b1 98 fflush(debug_log);
4873d3a4 99 if (opt_debug_stderr >= _db_level && debug_log != stderr) {
75e5a32e 100#if defined(__QNX__)
067bea91 101 vfprintf(stderr, f, eargs);
75e5a32e 102#else
0d90407c 103 vfprintf(stderr, f, args);
75e5a32e 104#endif
105 }
106#if defined(__QNX__)
107 va_end(eargs);
108#endif
090089c4 109 va_end(args);
110}
111
b8d8561b 112static void
0ee4272b 113debugArg(const char *arg)
12b9e9b1 114{
c9f06944 115 int s = 0;
116 int l = 0;
117 int i;
c9f06944 118 if (!strncasecmp(arg, "ALL", 3)) {
119 s = -1;
120 arg += 4;
121 } else {
122 s = atoi(arg);
123 while (*arg && *arg++ != ',');
124 }
125 l = atoi(arg);
b6a2f15e 126 assert(s >= -1);
127 assert(s < MAX_DEBUG_SECTIONS);
128 if (l < 0)
129 l = 0;
130 if (l > 10)
131 l = 10;
c9f06944 132 if (s >= 0) {
133 debugLevels[s] = l;
134 return;
135 }
136 for (i = 0; i < MAX_DEBUG_SECTIONS; i++)
137 debugLevels[i] = l;
12b9e9b1 138}
139
b8d8561b 140static void
0ee4272b 141debugOpenLog(const char *logfile)
ccff9601 142{
143 if (logfile == NULL) {
144 debug_log = stderr;
145 return;
146 }
147 if (debug_log_file)
5c46528b 148 xfree(debug_log_file);
ccff9601 149 debug_log_file = xstrdup(logfile); /* keep a static copy */
f2cc1af5 150 if (debug_log && debug_log != stderr)
a0bbd6c8 151 fclose(debug_log);
ccff9601 152 debug_log = fopen(logfile, "a+");
153 if (!debug_log) {
154 fprintf(stderr, "WARNING: Cannot write log file: %s\n", logfile);
155 perror(logfile);
156 fprintf(stderr, " messages will be sent to 'stderr'.\n");
157 fflush(stderr);
158 debug_log = stderr;
159 }
c4aefe96 160#if defined(_SQUID_CYGWIN_)
161 setmode(fileno(debug_log), O_TEXT);
162#endif
ccff9601 163}
164
b8d8561b 165void
0ee4272b 166_db_init(const char *logfile, const char *options)
090089c4 167{
12b9e9b1 168 int i;
169 char *p = NULL;
170 char *s = NULL;
090089c4 171
12b9e9b1 172 for (i = 0; i < MAX_DEBUG_SECTIONS; i++)
173 debugLevels[i] = -1;
090089c4 174
30a4f2a8 175 if (options) {
176 p = xstrdup(options);
177 for (s = strtok(p, w_space); s; s = strtok(NULL, w_space))
12b9e9b1 178 debugArg(s);
12b9e9b1 179 xfree(p);
180 }
ccff9601 181 debugOpenLog(logfile);
090089c4 182
30a4f2a8 183#if HAVE_SYSLOG && defined(LOG_LOCAL4)
6e40f263 184 if (opt_syslog_enable)
b8de7ebe 185 openlog(appname, LOG_PID | LOG_NDELAY | LOG_CONS, LOG_LOCAL4);
db40ae20 186#endif /* HAVE_SYSLOG */
187
090089c4 188}
189
b8d8561b 190void
0673c0ba 191_db_rotate_log(void)
090089c4 192{
193 int i;
95d659f0 194 LOCAL_ARRAY(char, from, MAXPATHLEN);
195 LOCAL_ARRAY(char, to, MAXPATHLEN);
88738790 196#ifdef S_ISREG
2edc2504 197 struct stat sb;
88738790 198#endif
090089c4 199
200 if (debug_log_file == NULL)
201 return;
71d95578 202#ifdef S_ISREG
2edc2504 203 if (stat(debug_log_file, &sb) == 0)
204 if (S_ISREG(sb.st_mode) == 0)
205 return;
71d95578 206#endif
090089c4 207
1f38f50a 208 /*
209 * NOTE: we cannot use xrename here without having it in a
210 * separate file -- tools.c has too many dependencies to be
211 * used everywhere debug.c is used.
212 */
090089c4 213 /* Rotate numbers 0 through N up one */
b6f794d6 214 for (i = Config.Log.rotateNumber; i > 1;) {
090089c4 215 i--;
042461c3 216 snprintf(from, MAXPATHLEN, "%s.%d", debug_log_file, i - 1);
217 snprintf(to, MAXPATHLEN, "%s.%d", debug_log_file, i);
090089c4 218 rename(from, to);
219 }
220 /* Rotate the current log to .0 */
b6f794d6 221 if (Config.Log.rotateNumber > 0) {
042461c3 222 snprintf(to, MAXPATHLEN, "%s.%d", debug_log_file, 0);
090089c4 223 rename(debug_log_file, to);
224 }
225 /* Close and reopen the log. It may have been renamed "manually"
226 * before HUP'ing us. */
0a8020d1 227 if (debug_log != stderr)
b6f794d6 228 debugOpenLog(Config.Log.log);
090089c4 229}
4873d3a4 230
231static const char *
232debugLogTime(time_t t)
233{
234 struct tm *tm;
235 static char buf[128];
236 static time_t last_t = 0;
237 if (t != last_t) {
2ac76861 238 tm = localtime(&t);
239 strftime(buf, 127, "%Y/%m/%d %H:%M:%S", tm);
240 last_t = t;
4873d3a4 241 }
242 return buf;
243}
244
bb71ea1f 245void
246xassert(const char *msg, const char *file, int line)
8a6218c6 247{
bb71ea1f 248 debug(0, 0) ("assertion failed: %s:%d: \"%s\"\n", file, line, msg);
249 if (!shutting_down)
8a6218c6 250 abort();
bb71ea1f 251}
252
123abbe1 253/*
254 * Context-based Debugging
26970499 255 *
256 * Rationale
257 * ---------
258 *
259 * When you have a long nested processing sequence, it is often impossible
260 * for low level routines to know in what larger context they operate. If a
261 * routine coredumps, one can restore the context using debugger trace.
262 * However, in many case you do not want to coredump, but just want to report
263 * a potential problem. A report maybe useless out of problem context.
264 *
265 * To solve this potential problem, use the following approach:
266 *
267 * int
268 * top_level_foo(const char *url)
269 * {
270 * // define current context
271 * // note: we stack but do not dup ctx descriptions!
272 * Ctx ctx = ctx_enter(url);
273 * ...
274 * // go down; middle_level_bar will eventually call bottom_level_boo
275 * middle_level_bar(method, protocol);
276 * ...
277 * // exit, clean after yourself
278 * ctx_exit(ctx);
279 * }
280 *
281 * void
282 * bottom_level_boo(int status, void *data)
283 * {
284 * // detect exceptional condition, and simply report it, the context
285 * // information will be available somewhere close in the log file
286 * if (status == STRANGE_STATUS)
287 * debug(13, 6) ("DOS attack detected, data: %p\n", data);
288 * ...
289 * }
290 *
291 * Current implementation is extremely simple but still very handy. It has a
292 * negligible overhead (descriptions are not duplicated).
293 *
294 * When the _first_ debug message for a given context is printed, it is
295 * prepended with the current context description. Context is printed with
296 * the same debugging level as the original message.
297 *
298 * Note that we do not print context every type you do ctx_enter(). This
299 * approach would produce too many useless messages. For the same reason, a
300 * context description is printed at most _once_ even if you have 10
301 * debugging messages within one context.
302 *
303 * Contexts can be nested, of course. You must use ctx_enter() to enter a
304 * context (push it onto stack). It is probably safe to exit several nested
305 * contexts at _once_ by calling ctx_exit() at the top level (this will pop
306 * all context till current one). However, as in any stack, you cannot start
307 * in the middle.
308 *
309 * Analysis:
310 * i) locate debugging message,
311 * ii) locate current context by going _upstream_ in your log file,
312 * iii) hack away.
313 *
314 *
123abbe1 315 * To-Do:
26970499 316 * -----
317 *
123abbe1 318 * decide if we want to dup() descriptions (adds overhead) but allows to
319 * add printf()-style interface
26970499 320 *
2ac76861 321 * implementation:
26970499 322 * ---------------
2ac76861 323 *
123abbe1 324 * descriptions for contexts over CTX_MAX_LEVEL limit are ignored, you probably
325 * have a bug if your nesting goes that deep.
326 */
2ac76861 327
123abbe1 328#define CTX_MAX_LEVEL 255
2ac76861 329
330/*
331 * produce a warning when nesting reaches this level and then double
332 * the level
333 */
334static int Ctx_Warn_Level = 32;
123abbe1 335/* all descriptions has been printed up to this level */
a3f9588e 336static int Ctx_Reported_Level = -1;
123abbe1 337/* descriptions are still valid or active up to this level */
a3f9588e 338static int Ctx_Valid_Level = -1;
123abbe1 339/* current level, the number of nested ctx_enter() calls */
a3f9588e 340static int Ctx_Current_Level = -1;
123abbe1 341/* saved descriptions (stack) */
2ac76861 342static const char *Ctx_Descrs[CTX_MAX_LEVEL + 1];
b70d1c58 343/* "safe" get secription */
344static const char *ctx_get_descr(Ctx ctx);
123abbe1 345
346
347Ctx
348ctx_enter(const char *descr)
349{
350 Ctx_Current_Level++;
351
352 if (Ctx_Current_Level <= CTX_MAX_LEVEL)
353 Ctx_Descrs[Ctx_Current_Level] = descr;
354
355 if (Ctx_Current_Level == Ctx_Warn_Level) {
2ac76861 356 debug(0, 0) ("# ctx: suspiciously deep (%d) nesting:\n", Ctx_Warn_Level);
123abbe1 357 Ctx_Warn_Level *= 2;
358 }
123abbe1 359 return Ctx_Current_Level;
360}
361
362void
363ctx_exit(Ctx ctx)
364{
365 assert(ctx >= 0);
2ac76861 366 Ctx_Current_Level = (ctx >= 0) ? ctx - 1 : -1;
123abbe1 367 if (Ctx_Valid_Level > Ctx_Current_Level)
368 Ctx_Valid_Level = Ctx_Current_Level;
369}
370
371/*
372 * the idea id to print each context description at most once but provide enough
373 * info for deducing the current execution stack
374 */
375static void
9bc73deb 376ctx_print(void)
123abbe1 377{
378 /* lock so _db_print will not call us recursively */
379 Ctx_Lock++;
380 /* ok, user saw [0,Ctx_Reported_Level] descriptions */
381 /* first inform about entries popped since user saw them */
382 if (Ctx_Valid_Level < Ctx_Reported_Level) {
2ac76861 383 if (Ctx_Reported_Level != Ctx_Valid_Level + 1)
384 _db_print("ctx: exit levels from %2d down to %2d\n",
385 Ctx_Reported_Level, Ctx_Valid_Level + 1);
4a0d3a91 386 else
387 _db_print("ctx: exit level %2d\n", Ctx_Reported_Level);
123abbe1 388 Ctx_Reported_Level = Ctx_Valid_Level;
389 }
390 /* report new contexts that were pushed since last report */
391 while (Ctx_Reported_Level < Ctx_Current_Level) {
392 Ctx_Reported_Level++;
393 Ctx_Valid_Level++;
2ac76861 394 _db_print("ctx: enter level %2d: '%s'\n", Ctx_Reported_Level,
b70d1c58 395 ctx_get_descr(Ctx_Reported_Level));
123abbe1 396 }
397 /* unlock */
398 Ctx_Lock--;
399}
b70d1c58 400
401/* checks for nulls and overflows */
402static const char *
403ctx_get_descr(Ctx ctx)
404{
2ac76861 405 if (ctx < 0 || ctx > CTX_MAX_LEVEL)
406 return "<lost>";
b70d1c58 407 return Ctx_Descrs[ctx] ? Ctx_Descrs[ctx] : "<null>";
408}