]>
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 #include <cxxabi.h> // for name demangling
32 // enable kernel tracing
33 #define ENABLE_SECTRACE 1
41 // Main debug functions (global and in-scope)
43 void debug(const char *scope
, const char *format
, ...)
45 #if !defined(NDEBUG_CODE)
47 va_start(args
, format
);
48 Target::get().message(scope
, format
, args
);
53 void vdebug(const char *scope
, const char *format
, va_list args
)
55 #if !defined(NDEBUG_CODE)
56 Target::get().message(scope
, format
, args
);
60 bool debugging(const char *scope
)
62 #if !defined(NDEBUG_CODE)
63 return Target::get().debugging(scope
);
71 // C equivalents for some basic uses
74 int __security_debugging(const char *scope
);
75 void __security_debug(const char *scope
, const char *format
, ...);
78 int __security_debugging(const char *scope
)
79 { return debugging(scope
); }
81 void __security_debug(const char *scope
, const char *format
, ...)
83 #if !defined(NDEBUG_CODE)
85 va_start(args
, format
);
86 vdebug(scope
, format
, args
);
95 bool dumping(const char *scope
)
97 #if defined(NDEBUG_STUBS)
100 return Target::get().dump(scope
);
104 void dump(const char *format
, ...)
106 #if !defined(NDEBUG_CODE)
108 va_start(args
, format
);
109 Target::get().dump(format
, args
);
114 void dumpData(const void *ptr
, size_t size
)
116 #if !defined(NDEBUG_CODE)
117 const char *addr
= reinterpret_cast<const char *>(ptr
);
118 const char *end
= addr
+ size
;
120 for (const char *p
= addr
; p
< end
; p
++)
121 if (!isprint(*p
)) { isText
= false; break; }
125 for (const char *p
= addr
; p
< end
; p
++)
130 for (const char *p
= addr
; p
< end
; p
++)
131 dump("%2.2x", static_cast<unsigned char>(*p
));
133 #endif //NDEBUG_STUBS
136 void dumpData(const char *title
, const void *ptr
, size_t size
)
138 #if !defined(NDEBUG_CODE)
142 #endif //NDEBUG_STUBS
147 // Turn a C++ typeid into a nice type name.
148 // This uses the C++ ABI where available.
149 // We're stripping out a few C++ prefixes; they're pretty redundant (and obvious).
151 string
makeTypeName(const type_info
&type
)
154 char *cname
= abi::__cxa_demangle(type
.name(), NULL
, NULL
, &status
);
155 string name
= !strncmp(cname
, "Security::", 10) ? (cname
+ 10) :
156 !strncmp(cname
, "std::", 5) ? (cname
+ 5) :
158 ::free(cname
); // yes, really (ABI rules)
164 // Target initialization
166 #if !defined(NDEBUG_CODE)
169 : showScope(false), showThread(false), showPid(false),
172 // put into singleton slot if first
173 if (singleton
== NULL
)
176 // insert terminate handler
177 if (!previousTerminator
) // first time we do this
178 previousTerminator
= set_terminate(terminator
);
187 // The core logging function of a Target
189 void Target::message(const char *scope
, const char *format
, va_list args
)
191 if (logSelector(scope
)) {
192 // note: messageConstructionSize is big enough for all prefixes constructed
193 char buffer
[messageConstructionSize
]; // building the message here
195 if (showScope
&& scope
) { // add "scope "
196 if (const char *sep
= strchr(scope
, ',')) {
197 bufp
+= sprintf(bufp
, "%-*s", Name::maxLength
, (const char *)Name(scope
, sep
));
198 } else { // single scope
199 bufp
+= sprintf(bufp
, "%-*s", Name::maxLength
, scope
);
202 if (showPid
) { // add "[Pid] "
203 bufp
+= sprintf(bufp
, "[%d] ", getpid());
205 if (showThread
) { // add "#Tthreadid "
207 Thread::Identity::current().getIdString(bufp
);
208 bufp
+= strlen(bufp
);
211 vsnprintf(bufp
, buffer
+ sizeof(buffer
) - bufp
, format
, args
);
212 for (char *p
= bufp
; *p
; p
++)
215 sink
->put(buffer
, bufp
- buffer
);
219 bool Target::debugging(const char *scope
)
221 return logSelector(scope
);
226 // The core debug-dump function of a target
228 void Target::dump(const char *format
, va_list args
)
230 char buffer
[messageConstructionSize
]; // building the message here
231 vsnprintf(buffer
, sizeof(buffer
), format
, args
);
232 for (char *p
= buffer
; *p
; p
++)
233 if (!isprint(*p
) && !isspace(*p
) || *p
== '\r')
238 bool Target::dump(const char *scope
)
240 return dumpSelector(scope
);
247 Target::Selector::Selector() : useSet(false), negate(false)
250 void Target::Selector::operator = (const char *scope
)
254 if (!strcmp(scope
, "all")) {
257 } else if (!strcmp(scope
, "none")) {
258 useSet
= negate
= false;
261 enableSet
.erase(enableSet
.begin(), enableSet
.end());
262 if (scope
[0] == '-') {
267 while (const char *sep
= strchr(scope
, ',')) {
268 enableSet
.insert(Name(scope
, sep
));
271 enableSet
.insert(scope
);
274 useSet
= negate
= false;
278 bool Target::Selector::operator () (const char *scope
) const
280 // a scope of NULL is a special override; it always qualifies
285 while (const char *sep
= strchr(scope
, ',')) {
286 if (enableSet
.find(Name(scope
, sep
)) != enableSet
.end())
290 return (enableSet
.find(scope
) != enableSet
.end()) != negate
;
298 // Establish Target state from the environment
300 void Target::setFromEnvironment()
303 logSelector
= getenv("DEBUGSCOPE");
304 dumpSelector
= getenv("DEBUGDUMP");
307 // Set and configure destination. Currently available:
308 // /some/where -> that file
309 // LOG_SOMETHING -> syslog facility
310 // >&number -> that (already) open file descriptor
311 // anything else -> try as a filename sight unseen
312 // DEBUGDEST not set -> stderr
313 // anything in error -> stderr (with an error message on it)
315 if (const char *dest
= getenv("DEBUGDEST")) {
316 if (dest
[0] == '/') { // full pathname, write to file
318 } else if (!strncmp(dest
, "LOG_", 4)) { // syslog
319 int facility
= LOG_DAEMON
;
320 for (CODE
*cp
= facilitynames
; cp
->c_name
; cp
++)
321 if (!strcmp(dest
, cp
->c_name
))
322 facility
= cp
->c_val
;
323 to(facility
| LOG_DEBUG
);
324 } else if (!strncmp(dest
, ">&", 2)) { // to file descriptor
325 int fd
= atoi(dest
+2);
326 if (FILE *f
= fdopen(fd
, "a")) {
330 ::debug(NULL
, "cannot log to fd[%d]: %s", fd
, strerror(errno
));
332 } else { // if everything else fails, write a file
335 } else { // default destination is stderr
342 void Target::configure()
344 configure(getenv("DEBUGOPTIONS"));
347 void Target::configure(const char *config
)
349 // configure global options
350 showScope
= config
&& strstr(config
, "scope");
351 showThread
= config
&& strstr(config
, "thread");
352 showPid
= config
&& strstr(config
, "pid");
356 sink
->configure(config
);
361 // Explicit destination assignments
363 void Target::to(Sink
*s
)
369 void Target::to(FILE *file
)
371 to(new FileSink(file
));
374 void Target::to(const char *filename
)
376 if (FILE *f
= fopen(filename
, "a")) {
380 ::debug(NULL
, "cannot debug to \"%s\": %s", filename
, strerror(errno
));
384 void Target::to(int syslogPriority
)
386 to(new SyslogSink(syslogPriority
));
391 // Making and retrieving the default singleton
393 Target
*Target::singleton
;
395 Target
&Target::get()
397 if (singleton
== NULL
) {
398 Target
*t
= new Target
;
399 t
->setFromEnvironment();
406 // Standard sink implementations
408 Target::Sink::~Sink()
411 void Target::Sink::dump(const char *)
414 void Target::Sink::configure(const char *)
419 // The terminate handler installed when a Target is created
421 terminate_handler
Target::previousTerminator
;
423 void Target::terminator()
425 debug("exception", "uncaught exception terminates program");
426 previousTerminator();
427 debug("exception", "prior termination handler failed to abort; forcing abort");
433 // File sinks (write to file via stdio)
435 void FileSink::put(const char *buffer
, unsigned int)
437 StLock
<Mutex
> locker(lock
, false);
441 time_t now
= time(NULL
);
442 char *date
= ctime(&now
);
444 fprintf(file
, "%s ", date
+ 4); // Nov 24 18:22:48
450 void FileSink::dump(const char *text
)
452 StLock
<Mutex
> locker(lock
, false);
458 void FileSink::configure(const char *options
)
460 if (options
== NULL
|| !strstr(options
, "noflush")) {
461 // we mean "if the file isn't unbuffered", but what's the portable way to say that?
466 addDate
= strstr(options
, "date");
467 lockIO
= !strstr(options
, "nolock");
473 // Syslog sinks (write to syslog)
475 void SyslogSink::put(const char *buffer
, unsigned int)
477 syslog(priority
, "%s", buffer
);
480 void SyslogSink::dump(const char *text
)
482 // add to dump buffer
483 snprintf(dumpPtr
, dumpBuffer
+ dumpBufferSize
- dumpPtr
, "%s", text
);
485 // take off full lines and submit
487 while (char *q
= strchr(p
, '\n')) {
488 *q
++ = '\0'; // terminate/break
489 syslog(priority
, " @@ %s", p
);
493 if (*p
) { // left-over unterminated line segment in buffer
494 dumpPtr
= p
+ strlen(p
);
495 if ((dumpBase
= p
) > dumpBuffer
+ dumpBufferSize
/ 2) {
496 // shift buffer down to make room
497 memmove(dumpBuffer
, dumpBase
, dumpPtr
- dumpBase
);
498 dumpPtr
-= (dumpBase
- dumpBuffer
);
499 dumpBase
= dumpBuffer
;
501 } else { // buffer is empty; reset to start
502 dumpBase
= dumpPtr
= dumpBuffer
;
506 void SyslogSink::configure(const char *options
)
514 // kernel tracing support (C version)
516 extern "C" void security_ktrace(int);
518 void security_ktrace(int code
)
520 #if defined(ENABLE_SECTRACE)
521 syscall(180, code
, 0, 0, 0, 0);
526 } // end namespace Debug
527 } // end namespace Security