summaryrefslogtreecommitdiff
path: root/bcc/debug.c
blob: 2ccda5d82e3c9103416b2b427f3059d250c5bcde (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*
 * debug.c: a generic debugging facility for unix programs.
 *
 * The calling program is required to call debug_setlevel(lvl) to set
 * which messages will be displayed. The level is a two part value
 * where the least significant (decimal) digit is a level as described
 * below. The most significant digits are a class code. For a message
 * to be displayed the class code must either be zero or match the
 * class code of the debug(...) message.
 *
 * The 'debug(lvl, fmt, ...)' function displays debugging messages 
 * complete with source and line number. The function can be used
 * as a normal one in if() smt else smt constructs. It returns the
 * actual number of bytes printed so it's return value can be used
 * inside an if(debug(...)) to enable more debugging code. This code
 * will be removed by the compiler (as dead code) if debugging is
 * not enabled.
 *
 * The level on the debug() statment also consists of a level and class
 * code where the class code must be zero or match the setlevel's class
 * code for the message to be displayed.
 *
 * Level 0
 *    Always displayed if the debugging is enabled.
 *    You probably shouldn't use this.
 *
 * Level 1
 *    Important state changes and errors that cause a significant change
 *    in program flow.
 *
 * Level 2
 *    Rare things that cause a minor program flow adjustment.
 *
 * Level 3
 *    Errors and useful messages that are slightly too verbose or common
 *    for 0-2 or don't quite fit in the classifications.
 *
 * Level 4
 *    All remote responses or major results. (Trace results)
 *
 * Level 5
 *    All remote commands or major tasks. (Trace jobs)
 *
 * Level 6
 *    General information that will not be too verbose but is normally a
 *    little less important. (Trace state)
 *
 * Level 7
 *    Similar to level 3 but verbose or not as useful.
 *
 * Level 8
 *    Very verbose information that'll probably be useful sometime.
 *
 * Level 9
 *    Anything and everything else, debugs that probably won't be useful
 *    ever again.                                            (unclassified)
 *
 * Notes:
 *    If the programmer doesn't set the debug level this is not an important
 *    debug message or is only important right now.
 *    => default debug level == 9
 *
 *    If something fits in one of the lower levels but is very verbose
 *    it should nevertheless be moved upto level 3 or levels 7-9.
 *    (Possibly leaving a single line 'oops' at the lower level)
 *
 *    The general idea is that debug levels 0-3 should not scroll too fast
 *    to read and nothing below level 7 should be much more verbose than
 *    levels 4 or 5.
 *
 *****************************************************************************
 *
 * 2004-06-20: Added __STDC__ to debug.h so it can be called from non-ansi
 *             compiler. This file still needs ansi or unproto.
 *
 * 2004-06-20: Added check of DEBUG environment variable if setlevel isn't
 *             called before a debug().
 *
 * 2004-06-20: Added #define VARARG_MACROS so the preprocessor can remove
 *             all the debugging 'stuff'.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include "debug.h"

#define USE_DBPRINTF

#ifndef DEBUG
static char ident[] = 
   "$Id: debug.c: (c) 1995-2004 Robert de Bath. Debugging disabled. $";
#else
static char ident[] = 
   "$Id: debug.c: (c) 1995-2004 Robert de Bath. Debugging enabled. $";

static char * db_file = 0;
static int db_lineno = 0;
static int disp_state = 0;
static int disp_pos(void);

static void debug_envvar(void);

int debug_level = -1;

void debug_do_setlevel(char * fname, int lineno, int level) 
{
   if(level || !debug_level) 
      debug_level = level;
   debug_pos(fname, lineno);
   debug_msg(1, "Debug level now %d", level);
   debug_level = level;
}

int debug_pos(char * file, int lineno)
{
   db_file = file;
   db_lineno = lineno;
   disp_state |= 1;
   return disp_pos();
}

int debug_msg(int level, char * fmt, ...)
{
   va_list ap;
   int rv = 0;
   int disp = 0;

   if (debug_level == -1) debug_envvar();

   if (level == -1) {
      level = 0;
      disp_state |= 1;
      db_lineno = -1;
   }

   disp_state |= 2;

   if (level>9 || debug_level>9) {
      disp = (level%10 <= debug_level%10);
      if (disp && level>9 && debug_level>9 && level/10 != debug_level/10)
	 disp = 0;
   } else disp = (level <= debug_level);

   if (disp)
   {
      disp_state |= 4;

      va_start(ap, fmt);
#ifdef USE_DBPRINTF
      rv = vdbprintf(fmt, ap);
#else
      rv = vfprintf(stderr, fmt, ap);
#endif
      va_end(ap);
   }
   return rv + disp_pos();
}

int
disp_pos()
{
   int rv = 0;
   if (disp_state == 7 && db_lineno != -1)
#ifdef USE_DBPRINTF
      rv = dbprintf(" at %s:%d\n", db_file, db_lineno);
#else
      rv = fprintf(stderr, " at %s:%d\n", db_file, db_lineno);
#endif

   if ((disp_state&3) == 3) {
      db_file = 0;
      db_lineno = disp_state = 0;
   }
   return rv;
}

/* If setlevel isn't called check the environment */

static void debug_envvar(void)
{
   char * p = getenv("DEBUG");
   if (!p || !*p)
      debug_level = 0;
   else 
      debug_level = atoi(p);
   if (debug_level)
#ifdef USE_DBPRINTF
      dbprintf("Debug level now %d from environment.\n", debug_level);
#else
      fprintf(stderr, "Debug level now %d from environment.\n", debug_level);
#endif
}

#endif

#ifndef VARARG_MACROS
/* 
 * This function should never be called.
 *
 * If ident sees the message in a binary then your compiler is wasting
 * space by allocating it for unused strings.
 *
 * We know GNU-C is ok, but it complains.
 */
int debug_never(int level, char * name, ...)
{
#ifndef __GNUC__
   1?0:debug_never(0, "$Warning: Debugging strings exist in non-debug binary $");
#endif
   return 0;
}
#endif