]>
git.saurik.com Git - apple/security.git/blob - libsecurity_utilities/lib/debugging.cpp
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 // debugging - non-trivial debugging support
28 #include <security_utilities/debugsupport.h>
29 #include <security_utilities/globalizer.h>
33 #define SYSLOG_NAMES // compile syslog name tables
36 #include <cxxabi.h> // for name demangling
37 #include <mach-o/dyld.h> // for _NSGetExecutablePath
39 // enable kernel tracing
40 #define ENABLE_SECTRACE 1
50 bool dumping(const char *scope
)
52 #if defined(NDEBUG_STUBS)
55 return Target::get().dump(scope
);
59 void dump(const char *format
, ...)
61 #if !defined(NDEBUG_CODE)
63 va_start(args
, format
);
64 Target::get().dump(format
, args
);
69 void dumpData(const void *ptr
, size_t size
)
71 #if !defined(NDEBUG_CODE)
72 const char *addr
= reinterpret_cast<const char *>(ptr
);
73 const char *end
= addr
+ size
;
75 for (const char *p
= addr
; p
< end
; p
++)
76 if (!isprint(*p
)) { isText
= false; break; }
80 for (const char *p
= addr
; p
< end
; p
++)
85 for (const char *p
= addr
; p
< end
; p
++)
86 dump("%2.2x", static_cast<unsigned char>(*p
));
91 void dumpData(const char *title
, const void *ptr
, size_t size
)
93 #if !defined(NDEBUG_CODE)
102 // Turn a C++ typeid into a nice type name.
103 // This uses the C++ ABI where available.
104 // We're stripping out a few C++ prefixes; they're pretty redundant (and obvious).
106 string
makeTypeName(const type_info
&type
)
109 char *cname
= abi::__cxa_demangle(type
.name(), NULL
, NULL
, &status
);
110 string name
= !strncmp(cname
, "Security::", 10) ? (cname
+ 10) :
111 !strncmp(cname
, "std::", 5) ? (cname
+ 5) :
113 ::free(cname
); // yes, really (ABI rules)
119 // Target initialization.
120 // This where we should do all "first time" initializations.
122 #if !defined(NDEBUG_CODE)
124 char Target::progName
[maxProgNameLength
+ 1];
125 unsigned int Target::PerThread::lastUsed
;
128 : showScope(false), showThread(false), showProc(false), showDate(false),
131 // put into singleton slot if first
132 if (singleton
== NULL
)
135 // insert terminate handler
136 if (!previousTerminator
) // first time we do this
137 previousTerminator
= set_terminate(terminator
);
140 char execPath
[PATH_MAX
];
141 uint32_t length
= sizeof(execPath
);
142 if (_NSGetExecutablePath(execPath
, &length
)) {
143 strcpy(progName
, "unknown");
145 const char *p
= strrchr(execPath
, '/');
150 unsigned plen
= strlen(p
);
151 if (plen
> maxProgNameLength
) // too long
152 p
+= plen
- maxProgNameLength
; // take rear
162 static void addScope(char *&bufp
, const char *scope
)
164 if (const char *sep
= strchr(scope
, ',')) {
165 bufp
+= sprintf(bufp
, "%-*s", Name::maxLength
, (const char *)Name(scope
, sep
));
166 } else { // single scope
167 bufp
+= sprintf(bufp
, "%-*s", Name::maxLength
, scope
);
173 // The core logging function of a Target
175 void Target::message(const char *scope
, const char *format
, va_list args
)
177 if (logSelector(scope
)) {
178 // note: messageConstructionSize is big enough for all prefixes constructed
179 char buffer
[messageConstructionSize
]; // building the message here
183 if (showDate
&& sink
->needsDate
) {
184 time_t now
= time(NULL
);
185 char *date
= ctime(&now
);
187 bufp
+= sprintf(bufp
, "%s ", date
+ 4); // Nov 24 18:22:48
191 if (showScope
&& scope
)
192 addScope(bufp
, scope
);
194 if (showProc
|| showThread
) {
195 char sub
[maxProgNameLength
+ 20];
197 if (showProc
&& showThread
)
198 plen
= sprintf(sub
, "%s[%d]", progName
, getpid());
200 plen
= sprintf(sub
, "%s", progName
);
202 plen
= sprintf(sub
, "[%d]", getpid());
203 unsigned int id
= perThread().id
;
205 plen
+= sprintf(sub
+ plen
, ":%d", id
);
206 if (plen
<= procLength
)
207 bufp
+= sprintf(bufp
, "%-*s ", int(procLength
), sub
);
209 bufp
+= sprintf(bufp
, "%s ", sub
+ plen
- procLength
);
212 // scope after proc/thread/pid
213 if (showScopeRight
&& scope
)
214 addScope(bufp
, scope
);
216 // now stuff the message body in, slightly roasted
217 size_t left
= buffer
+ sizeof(buffer
) - bufp
- 1; // reserve one
218 size_t written
= vsnprintf(bufp
, left
, format
, args
);
219 for (char *p
= bufp
; *p
; p
++)
222 if (written
>= left
) { // snprintf overflowed
224 strcpy(bufp
- 3, "...");
228 // now append a newline and a null
232 // submit to sink (do not count newline and null in count)
233 sink
->put(buffer
, bufp
- buffer
);
237 bool Target::debugging(const char *scope
)
239 return logSelector(scope
);
244 // The core debug-dump function of a target
246 void Target::dump(const char *format
, va_list args
)
248 char buffer
[messageConstructionSize
]; // building the message here
249 vsnprintf(buffer
, sizeof(buffer
), format
, args
);
250 for (char *p
= buffer
; *p
; p
++)
251 if (!isprint(*p
) && !isspace(*p
) || *p
== '\r')
256 bool Target::dump(const char *scope
)
258 return dumpSelector(scope
);
265 Target::Selector::Selector() : useSet(false), negate(false)
268 void Target::Selector::operator = (const char *scope
)
272 if (!strcmp(scope
, "all")) {
275 } else if (!strcmp(scope
, "none")) {
276 useSet
= negate
= false;
279 enableSet
.erase(enableSet
.begin(), enableSet
.end());
280 if (scope
[0] == '-') {
285 while (const char *sep
= strchr(scope
, ',')) {
286 enableSet
.insert(Name(scope
, sep
));
289 enableSet
.insert(scope
);
292 useSet
= negate
= false;
296 bool Target::Selector::operator () (const char *scope
) const
298 // a scope of NULL is a special override; it always qualifies
303 while (const char *sep
= strchr(scope
, ',')) {
304 if (enableSet
.find(Name(scope
, sep
)) != enableSet
.end())
308 return (enableSet
.find(scope
) != enableSet
.end()) != negate
;
316 // Establish Target state from the environment
318 void Target::setFromEnvironment()
321 logSelector
= getenv("DEBUGSCOPE");
322 dumpSelector
= getenv("DEBUGDUMP");
325 // Set and configure destination. Currently available:
326 // /some/where -> that file
327 // LOG_SOMETHING -> syslog facility
328 // >&number -> that (already) open (for write or append) file descriptor
329 // anything else -> try as a filename sight unseen [may change]
330 // DEBUGDEST not set -> stderr
331 // anything in error -> stderr (with an error message on it)
333 if (const char *dest
= getenv("DEBUGDEST")) {
334 if (dest
[0] == '/') { // full pathname, write to file
336 } else if (!strncmp(dest
, "LOG_", 4)) { // syslog
337 int facility
= LOG_DAEMON
;
338 for (CODE
*cp
= facilitynames
; cp
->c_name
; cp
++)
339 if (!strcmp(dest
, cp
->c_name
))
340 facility
= cp
->c_val
;
341 to(facility
| LOG_DEBUG
);
342 } else if (!strncmp(dest
, ">&", 2)) { // to file descriptor
343 int fd
= atoi(dest
+2);
344 if (FILE *f
= fdopen(fd
, "a")) {
348 secdebug("", "cannot log to fd[%d]: %s", fd
, strerror(errno
));
350 } else { // if everything else fails, write a file
353 } else { // default destination is stderr
360 void Target::configure()
362 configure(getenv("DEBUGOPTIONS"));
365 void Target::configure(const char *config
)
367 // configure global options
368 showScopeRight
= config
&& strstr(config
, "rscope");
369 showScope
= !showScopeRight
&& config
&& strstr(config
, "scope");
370 showThread
= config
&& (strstr(config
, "thread") || strstr(config
, "pid")); // (legacy)
371 showProc
= config
&& strstr(config
, "proc");
372 showDate
= config
&& strstr(config
, "date");
376 sink
->configure(config
);
381 // Explicit destination assignments
383 void Target::to(Sink
*s
)
389 void Target::to(FILE *file
)
391 to(new FileSink(file
));
394 void Target::to(const char *filename
)
396 if (FILE *f
= fopen(filename
, "a")) {
400 secdebug("", "cannot debug to \"%s\": %s", filename
, strerror(errno
));
404 void Target::to(int syslogPriority
)
406 to(new SyslogSink(syslogPriority
));
411 // Making and retrieving the default singleton
413 Target
*Target::singleton
;
415 Target
&Target::get()
417 if (singleton
== NULL
) {
418 Target
*t
= new Target
;
419 t
->setFromEnvironment();
426 // Standard sink implementations
428 Target::Sink::~Sink()
431 void Target::Sink::dump(const char *)
434 void Target::Sink::configure(const char *)
439 // The terminate handler installed when a Target is created
441 terminate_handler
Target::previousTerminator
;
443 void Target::terminator()
445 secdebug("exception", "uncaught exception terminates program");
446 previousTerminator();
447 secdebug("exception", "prior termination handler failed to abort; forcing abort");
453 // File sinks (write to file via stdio)
455 void FileSink::put(const char *inbuf
, unsigned int length
)
457 fwrite(inbuf
, 1, length
+ 1, file
); // do pick up the trailing newline
460 void FileSink::dump(const char *text
)
465 void FileSink::configure(const char *options
)
467 if (options
== NULL
|| !strstr(options
, "noflush")) {
468 // we mean "if the file isn't unbuffered", but what's the portable way to say that?
476 // Syslog sinks (write to syslog)
478 void SyslogSink::put(const char *buffer
, unsigned int length
)
480 syslog(priority
, "%1.*s", length
, buffer
); // don't pick up trailing newline
483 void SyslogSink::dump(const char *text
)
485 // add to dump buffer
486 snprintf(dumpPtr
, dumpBuffer
+ dumpBufferSize
- dumpPtr
, "%s", text
);
488 // take off full lines and submit
490 while (char *q
= strchr(p
, '\n')) {
491 *q
++ = '\0'; // terminate/break
492 syslog(priority
, " @@ %s", p
);
496 if (*p
) { // left-over unterminated line segment in buffer
497 dumpPtr
= p
+ strlen(p
);
498 if ((dumpBase
= p
) > dumpBuffer
+ dumpBufferSize
/ 2) {
499 // shift buffer down to make room
500 memmove(dumpBuffer
, dumpBase
, dumpPtr
- dumpBase
);
501 dumpPtr
-= (dumpBase
- dumpBuffer
);
502 dumpBase
= dumpBuffer
;
504 } else { // buffer is empty; reset to start
505 dumpBase
= dumpPtr
= dumpBuffer
;
509 void SyslogSink::configure(const char *options
)
516 } // end namespace Debug
517 } // end namespace Security