]>
git.saurik.com Git - apple/security.git/blob - cdsa/cdsa_utilities/debugging.cpp
2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
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
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.
20 // debugging - non-trivial debugging support
22 #include <Security/debugsupport.h>
23 #include <Security/globalizer.h>
27 #define SYSLOG_NAMES // compile syslog name tables
30 #if !defined(USE_CXXABI)
31 #define USE_CXXABI 0 // only available in gcc3 >v1100
35 # include <cxxabi.h> // for name demangling
45 void Scope::operator () (const char *, ...) { }
50 // Main debug functions (global and in-scope)
52 void debug(const char *scope
, const char *format
, ...)
54 #if !defined(NDEBUG_STUBS)
56 va_start(args
, format
);
57 Target::get().message(scope
, format
, args
);
62 void vdebug(const char *scope
, const char *format
, va_list args
)
64 #if !defined(NDEBUG_STUBS)
65 Target::get().message(scope
, format
, args
);
69 void Scope::operator () (const char *format
, ...)
71 #if !defined(NDEBUG_STUBS)
73 va_start(args
, format
);
74 Target::get().message(mScope
, format
, args
);
79 bool debugging(const char *scope
)
81 #if !defined(NDEBUG_STUBS)
82 return Target::get().debugging(scope
);
92 bool dumping(const char *scope
)
94 #if defined(NDEBUG_STUBS)
97 return Target::get().dump(scope
);
101 void dump(const char *format
, ...)
103 #if !defined(NDEBUG_STUBS)
105 va_start(args
, format
);
106 Target::get().dump(format
, args
);
111 void dumpData(const void *ptr
, size_t size
)
113 #if !defined(NDEBUG_STUBS)
114 const char *addr
= reinterpret_cast<const char *>(ptr
);
115 const char *end
= addr
+ size
;
117 for (const char *p
= addr
; p
< end
; p
++)
118 if (!isprint(*p
)) { isText
= false; break; }
122 for (const char *p
= addr
; p
< end
; p
++)
127 for (const char *p
= addr
; p
< end
; p
++)
128 dump("%2.2x", static_cast<unsigned char>(*p
));
130 #endif //NDEBUG_STUBS
133 void dumpData(const char *title
, const void *ptr
, size_t size
)
135 #if !defined(NDEBUG_STUBS)
139 #endif //NDEBUG_STUBS
144 // Turn a C++ typeid into a nice type name.
145 // This uses the C++ ABI where available.
147 string
makeTypeName(const type_info
&type
)
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)
156 return type
.name(); // can't demangle; just return internal name
162 // Target initialization
164 #if !defined(NDEBUG_STUBS)
167 : showScope(false), showThread(false), showPid(false),
170 // put into singleton slot if first
171 if (singleton
== NULL
)
174 // insert terminate handler
175 if (!previousTerminator
) // first time we do this
176 previousTerminator
= set_terminate(terminator
);
185 // The core logging function of a Target
187 void Target::message(const char *scope
, const char *format
, va_list args
)
189 if (logSelector(scope
)) {
190 // note: messageConstructionSize is big enough for all prefixes constructed
191 char buffer
[messageConstructionSize
]; // building the message here
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
);
200 if (showPid
) { // add "[Pid] "
201 bufp
+= sprintf(bufp
, "[%d] ", getpid());
203 if (showThread
) { // add "#Tthreadid "
205 Thread::Identity::current().getIdString(bufp
);
206 bufp
+= strlen(bufp
);
209 vsnprintf(bufp
, buffer
+ sizeof(buffer
) - bufp
, format
, args
);
210 for (char *p
= bufp
; *p
; p
++)
213 sink
->put(buffer
, bufp
- buffer
);
217 bool Target::debugging(const char *scope
)
219 return logSelector(scope
);
224 // The core debug-dump function of a target
226 void Target::dump(const char *format
, va_list args
)
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')
236 bool Target::dump(const char *scope
)
238 return dumpSelector(scope
);
245 Target::Selector::Selector() : useSet(false), negate(false)
248 void Target::Selector::operator = (const char *scope
)
252 if (!strcmp(scope
, "all")) {
255 } else if (!strcmp(scope
, "none")) {
256 useSet
= negate
= false;
259 enableSet
.erase(enableSet
.begin(), enableSet
.end());
260 if (scope
[0] == '-') {
265 while (const char *sep
= strchr(scope
, ',')) {
266 enableSet
.insert(Name(scope
, sep
));
269 enableSet
.insert(scope
);
272 useSet
= negate
= false;
276 bool Target::Selector::operator () (const char *scope
) const
278 // a scope of NULL is a special override; it always qualifies
283 while (const char *sep
= strchr(scope
, ',')) {
284 if (enableSet
.find(Name(scope
, sep
)) != enableSet
.end())
288 return (enableSet
.find(scope
) != enableSet
.end()) != negate
;
296 // Establish Target state from the environment
298 void Target::setFromEnvironment()
301 logSelector
= getenv("DEBUGSCOPE");
302 dumpSelector
= getenv("DEBUGDUMP");
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)
313 if (const char *dest
= getenv("DEBUGDEST")) {
314 if (dest
[0] == '/') { // full pathname, write to file
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")) {
328 ::debug(NULL
, "cannot log to fd[%d]: %s", fd
, strerror(errno
));
330 } else { // if everything else fails, write a file
333 } else { // default destination is stderr
340 void Target::configure()
342 configure(getenv("DEBUGOPTIONS"));
345 void Target::configure(const char *config
)
347 // configure global options
348 showScope
= config
&& strstr(config
, "scope");
349 showThread
= config
&& strstr(config
, "thread");
350 showPid
= config
&& strstr(config
, "pid");
354 sink
->configure(config
);
359 // Explicit destination assignments
361 void Target::to(Sink
*s
)
367 void Target::to(FILE *file
)
369 to(new FileSink(file
));
372 void Target::to(const char *filename
)
374 if (FILE *f
= fopen(filename
, "a")) {
378 ::debug(NULL
, "cannot debug to \"%s\": %s", filename
, strerror(errno
));
382 void Target::to(int syslogPriority
)
384 to(new SyslogSink(syslogPriority
));
389 // Making and retrieving the default singleton
391 Target
*Target::singleton
;
393 Target
&Target::get()
395 if (singleton
== NULL
) {
396 Target
*t
= new Target
;
397 t
->setFromEnvironment();
404 // Standard sink implementations
406 Target::Sink::~Sink()
409 void Target::Sink::dump(const char *)
412 void Target::Sink::configure(const char *)
417 // The terminate handler installed when a Target is created
419 terminate_handler
Target::previousTerminator
;
421 void Target::terminator()
423 debug("exception", "uncaught exception terminates program");
424 previousTerminator();
425 debug("exception", "prior termination handler failed to abort; forcing abort");
431 // File sinks (write to file via stdio)
433 void FileSink::put(const char *buffer
, unsigned int)
435 StLock
<Mutex
> locker(lock
, false);
439 time_t now
= time(NULL
);
440 char *date
= ctime(&now
);
442 fprintf(file
, "%s ", date
+ 4); // Nov 24 18:22:48
448 void FileSink::dump(const char *text
)
450 StLock
<Mutex
> locker(lock
, false);
456 void FileSink::configure(const char *options
)
458 if (options
== NULL
|| !strstr(options
, "noflush"))
461 addDate
= strstr(options
, "date");
462 lockIO
= !strstr(options
, "nolock");
468 // Syslog sinks (write to syslog)
470 void SyslogSink::put(const char *buffer
, unsigned int)
472 syslog(priority
, "%s", buffer
);
475 void SyslogSink::dump(const char *text
)
477 // add to dump buffer
478 snprintf(dumpPtr
, dumpBuffer
+ dumpBufferSize
- dumpPtr
, "%s", text
);
480 // take off full lines and submit
482 while (char *q
= strchr(p
, '\n')) {
483 *q
++ = '\0'; // terminate/break
484 syslog(priority
, " @@ %s", p
);
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
;
496 } else { // buffer is empty; reset to start
497 dumpBase
= dumpPtr
= dumpBuffer
;
501 void SyslogSink::configure(const char *options
)
505 #endif //NDEBUG_STUBS
510 } // end namespace Debug
512 } // end namespace Security