]> git.saurik.com Git - apple/security.git/blob - cdsa/cdsa_utilities/debugging.cpp
Security-54.1.tar.gz
[apple/security.git] / cdsa / cdsa_utilities / debugging.cpp
1 /*
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
3 *
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
8 * using this file.
9 *
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
16 */
17
18
19 //
20 // debugging - non-trivial debugging support
21 //
22 #include <Security/debugsupport.h>
23 #include <Security/globalizer.h>
24 #include <cstdarg>
25 #include <ctype.h>
26
27 #define SYSLOG_NAMES // compile syslog name tables
28 #include <syslog.h>
29
30 #if !defined(USE_CXXABI)
31 #define USE_CXXABI 0 // only available in gcc3 >v1100
32 #endif
33
34 #if USE_CXXABI
35 # include <cxxabi.h> // for name demangling
36 #endif //USE_CXXABI
37
38
39 namespace Security {
40 namespace Debug {
41
42
43 #if defined(NDEBUG)
44
45 void Scope::operator () (const char *, ...) { }
46
47 #else // NDEBUG
48
49 //
50 // Main debug functions (global and in-scope)
51 //
52 void debug(const char *scope, const char *format, ...)
53 {
54 #if !defined(NDEBUG_STUBS)
55 va_list args;
56 va_start(args, format);
57 Target::get().message(scope, format, args);
58 va_end(args);
59 #endif
60 }
61
62 void vdebug(const char *scope, const char *format, va_list args)
63 {
64 #if !defined(NDEBUG_STUBS)
65 Target::get().message(scope, format, args);
66 #endif
67 }
68
69 void Scope::operator () (const char *format, ...)
70 {
71 #if !defined(NDEBUG_STUBS)
72 va_list args;
73 va_start(args, format);
74 Target::get().message(mScope, format, args);
75 va_end(args);
76 #endif
77 }
78
79 bool debugging(const char *scope)
80 {
81 #if !defined(NDEBUG_STUBS)
82 return Target::get().debugging(scope);
83 #else
84 return false;
85 #endif
86 }
87
88
89 //
90 // Dump facility
91 //
92 bool dumping(const char *scope)
93 {
94 #if defined(NDEBUG_STUBS)
95 return false;
96 #else
97 return Target::get().dump(scope);
98 #endif
99 }
100
101 void dump(const char *format, ...)
102 {
103 #if !defined(NDEBUG_STUBS)
104 va_list args;
105 va_start(args, format);
106 Target::get().dump(format, args);
107 va_end(args);
108 #endif
109 }
110
111 void dumpData(const void *ptr, size_t size)
112 {
113 #if !defined(NDEBUG_STUBS)
114 const char *addr = reinterpret_cast<const char *>(ptr);
115 const char *end = addr + size;
116 bool isText = true;
117 for (const char *p = addr; p < end; p++)
118 if (!isprint(*p)) { isText = false; break; }
119
120 if (isText) {
121 dump("\"");
122 for (const char *p = addr; p < end; p++)
123 dump("%c", *p);
124 dump("\"");
125 } else {
126 dump("0x");
127 for (const char *p = addr; p < end; p++)
128 dump("%2.2x", static_cast<unsigned char>(*p));
129 }
130 #endif //NDEBUG_STUBS
131 }
132
133 void dumpData(const char *title, const void *ptr, size_t size)
134 {
135 #if !defined(NDEBUG_STUBS)
136 dump("%s: ", title);
137 dumpData(ptr, size);
138 dump("\n");
139 #endif //NDEBUG_STUBS
140 }
141
142
143 //
144 // Turn a C++ typeid into a nice type name.
145 // This uses the C++ ABI where available.
146 //
147 string makeTypeName(const type_info &type)
148 {
149 #if USE_CXXABI
150 int status;
151 char *cname = abi::__cxa_demangle(type.name(), NULL, NULL, &status);
152 string name = cname; // save the value
153 ::free(cname); // yes, really (ABI rule)
154 return name;
155 #else
156 return type.name(); // can't demangle; just return internal name
157 #endif
158 }
159
160
161 //
162 // Target initialization
163 //
164 #if !defined(NDEBUG_STUBS)
165
166 Target::Target()
167 : showScope(false), showThread(false), showPid(false),
168 sink(NULL)
169 {
170 // put into singleton slot if first
171 if (singleton == NULL)
172 singleton = this;
173
174 // insert terminate handler
175 if (!previousTerminator) // first time we do this
176 previousTerminator = set_terminate(terminator);
177 }
178
179 Target::~Target()
180 {
181 }
182
183
184 //
185 // The core logging function of a Target
186 //
187 void Target::message(const char *scope, const char *format, va_list args)
188 {
189 if (logSelector(scope)) {
190 // note: messageConstructionSize is big enough for all prefixes constructed
191 char buffer[messageConstructionSize]; // building the message here
192 char *bufp = buffer;
193 if (showScope && scope) { // add "scope "
194 if (const char *sep = strchr(scope, ',')) {
195 bufp += sprintf(bufp, "%-*s", Name::maxLength, (const char *)Name(scope, sep));
196 } else { // single scope
197 bufp += sprintf(bufp, "%-*s", Name::maxLength, scope);
198 }
199 }
200 if (showPid) { // add "[Pid] "
201 bufp += sprintf(bufp, "[%d] ", getpid());
202 }
203 if (showThread) { // add "#Tthreadid "
204 *bufp++ = '#';
205 Thread::Identity::current().getIdString(bufp);
206 bufp += strlen(bufp);
207 *bufp++ = ' ';
208 }
209 vsnprintf(bufp, buffer + sizeof(buffer) - bufp, format, args);
210 for (char *p = bufp; *p; p++)
211 if (!isprint(*p))
212 *p = '?';
213 sink->put(buffer, bufp - buffer);
214 }
215 }
216
217 bool Target::debugging(const char *scope)
218 {
219 return logSelector(scope);
220 }
221
222
223 //
224 // The core debug-dump function of a target
225 //
226 void Target::dump(const char *format, va_list args)
227 {
228 char buffer[messageConstructionSize]; // building the message here
229 vsnprintf(buffer, sizeof(buffer), format, args);
230 for (char *p = buffer; *p; p++)
231 if (!isprint(*p) && !isspace(*p) || *p == '\r')
232 *p = '?';
233 sink->dump(buffer);
234 }
235
236 bool Target::dump(const char *scope)
237 {
238 return dumpSelector(scope);
239 }
240
241
242 //
243 // Selector objects.
244 //
245 Target::Selector::Selector() : useSet(false), negate(false)
246 { }
247
248 void Target::Selector::operator = (const char *scope)
249 {
250 if (scope) {
251 // initial values
252 if (!strcmp(scope, "all")) {
253 useSet = false;
254 negate = true;
255 } else if (!strcmp(scope, "none")) {
256 useSet = negate = false;
257 } else {
258 useSet = true;
259 enableSet.erase(enableSet.begin(), enableSet.end());
260 if (scope[0] == '-') {
261 negate = true;
262 scope++;
263 } else
264 negate = false;
265 while (const char *sep = strchr(scope, ',')) {
266 enableSet.insert(Name(scope, sep));
267 scope = sep + 1;
268 }
269 enableSet.insert(scope);
270 }
271 } else {
272 useSet = negate = false;
273 }
274 }
275
276 bool Target::Selector::operator () (const char *scope) const
277 {
278 // a scope of NULL is a special override; it always qualifies
279 if (scope == NULL)
280 return true;
281
282 if (useSet) {
283 while (const char *sep = strchr(scope, ',')) {
284 if (enableSet.find(Name(scope, sep)) != enableSet.end())
285 return !negate;
286 scope = sep + 1;
287 }
288 return (enableSet.find(scope) != enableSet.end()) != negate;
289 } else {
290 return negate;
291 }
292 }
293
294
295 //
296 // Establish Target state from the environment
297 //
298 void Target::setFromEnvironment()
299 {
300 // set scopes
301 logSelector = getenv("DEBUGSCOPE");
302 dumpSelector = getenv("DEBUGDUMP");
303
304 //
305 // Set and configure destination. Currently available:
306 // /some/where -> that file
307 // LOG_SOMETHING -> syslog facility
308 // >&number -> that (already) open file descriptor
309 // anything else -> try as a filename sight unseen
310 // DEBUGDEST not set -> stderr
311 // anything in error -> stderr (with an error message on it)
312 //
313 if (const char *dest = getenv("DEBUGDEST")) {
314 if (dest[0] == '/') { // full pathname, write to file
315 to(dest);
316 } else if (!strncmp(dest, "LOG_", 4)) { // syslog
317 int facility = LOG_DAEMON;
318 for (CODE *cp = facilitynames; cp->c_name; cp++)
319 if (!strcmp(dest, cp->c_name))
320 facility = cp->c_val;
321 to(facility | LOG_DEBUG);
322 } else if (!strncmp(dest, ">&", 2)) { // to file descriptor
323 int fd = atoi(dest+2);
324 if (FILE *f = fdopen(fd, "a")) {
325 to(f);
326 } else {
327 to(stderr);
328 ::debug(NULL, "cannot log to fd[%d]: %s", fd, strerror(errno));
329 }
330 } else { // if everything else fails, write a file
331 to(dest);
332 }
333 } else { // default destination is stderr
334 to(stderr);
335 }
336 configure();
337 }
338
339
340 void Target::configure()
341 {
342 configure(getenv("DEBUGOPTIONS"));
343 }
344
345 void Target::configure(const char *config)
346 {
347 // configure global options
348 showScope = config && strstr(config, "scope");
349 showThread = config && strstr(config, "thread");
350 showPid = config && strstr(config, "pid");
351
352 // configure sink
353 if (sink)
354 sink->configure(config);
355 }
356
357
358 //
359 // Explicit destination assignments
360 //
361 void Target::to(Sink *s)
362 {
363 delete sink;
364 sink = s;
365 }
366
367 void Target::to(FILE *file)
368 {
369 to(new FileSink(file));
370 }
371
372 void Target::to(const char *filename)
373 {
374 if (FILE *f = fopen(filename, "a")) {
375 to(new FileSink(f));
376 } else {
377 to(stderr);
378 ::debug(NULL, "cannot debug to \"%s\": %s", filename, strerror(errno));
379 }
380 }
381
382 void Target::to(int syslogPriority)
383 {
384 to(new SyslogSink(syslogPriority));
385 }
386
387
388 //
389 // Making and retrieving the default singleton
390 //
391 Target *Target::singleton;
392
393 Target &Target::get()
394 {
395 if (singleton == NULL) {
396 Target *t = new Target;
397 t->setFromEnvironment();
398 }
399 return *singleton;
400 }
401
402
403 //
404 // Standard sink implementations
405 //
406 Target::Sink::~Sink()
407 { }
408
409 void Target::Sink::dump(const char *)
410 { }
411
412 void Target::Sink::configure(const char *)
413 { }
414
415
416 //
417 // The terminate handler installed when a Target is created
418 //
419 terminate_handler Target::previousTerminator;
420
421 void Target::terminator()
422 {
423 debug("exception", "uncaught exception terminates program");
424 previousTerminator();
425 debug("exception", "prior termination handler failed to abort; forcing abort");
426 abort();
427 }
428
429
430 //
431 // File sinks (write to file via stdio)
432 //
433 void FileSink::put(const char *buffer, unsigned int)
434 {
435 StLock<Mutex> locker(lock, false);
436 if (lockIO)
437 locker.lock();
438 if (addDate) {
439 time_t now = time(NULL);
440 char *date = ctime(&now);
441 date[19] = '\0';
442 fprintf(file, "%s ", date + 4); // Nov 24 18:22:48
443 }
444 fputs(buffer, file);
445 putc('\n', file);
446 }
447
448 void FileSink::dump(const char *text)
449 {
450 StLock<Mutex> locker(lock, false);
451 if (lockIO)
452 locker.lock();
453 fputs(text, file);
454 }
455
456 void FileSink::configure(const char *options)
457 {
458 if (options == NULL || !strstr(options, "noflush"))
459 setlinebuf(file);
460 if (options) {
461 addDate = strstr(options, "date");
462 lockIO = !strstr(options, "nolock");
463 }
464 }
465
466
467 //
468 // Syslog sinks (write to syslog)
469 //
470 void SyslogSink::put(const char *buffer, unsigned int)
471 {
472 syslog(priority, "%s", buffer);
473 }
474
475 void SyslogSink::dump(const char *text)
476 {
477 // add to dump buffer
478 snprintf(dumpPtr, dumpBuffer + dumpBufferSize - dumpPtr, "%s", text);
479
480 // take off full lines and submit
481 char *p = dumpBase;
482 while (char *q = strchr(p, '\n')) {
483 *q++ = '\0'; // terminate/break
484 syslog(priority, " @@ %s", p);
485 p = q;
486 }
487
488 if (*p) { // left-over unterminated line segment in buffer
489 dumpPtr = p + strlen(p);
490 if ((dumpBase = p) > dumpBuffer + dumpBufferSize / 2) {
491 // shift buffer down to make room
492 memmove(dumpBuffer, dumpBase, dumpPtr - dumpBase);
493 dumpPtr -= (dumpBase - dumpBuffer);
494 dumpBase = dumpBuffer;
495 }
496 } else { // buffer is empty; reset to start
497 dumpBase = dumpPtr = dumpBuffer;
498 }
499 }
500
501 void SyslogSink::configure(const char *options)
502 {
503 }
504
505 #endif //NDEBUG_STUBS
506
507 #endif // NDEBUG
508
509
510 } // end namespace Debug
511
512 } // end namespace Security