]>
git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_utilities/lib/debugging_internal.cpp
2 * Copyright (c) 2000-2004,2011-2012,2014 Apple 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/debugging_internal.h>
29 #include <security_utilities/debugsupport.h>
30 #include <security_utilities/globalizer.h>
34 #define SYSLOG_NAMES // compile syslog name tables
37 #include <cxxabi.h> // for name demangling
38 #include <mach-o/dyld.h> // for _NSGetExecutablePath
41 // enable kernel tracing
42 #define ENABLE_SECTRACE 1
52 bool dumping(const char *scope
)
54 #if defined(NDEBUG_STUBS)
57 return Target::get().dump(scope
);
61 void dump(const char *format
, ...)
63 #if !defined(NDEBUG_CODE)
65 va_start(args
, format
);
66 Target::get().dump(format
, args
);
71 void dumpData(const void *ptr
, size_t size
)
73 #if !defined(NDEBUG_CODE)
74 const char *addr
= reinterpret_cast<const char *>(ptr
);
75 const char *end
= addr
+ size
;
77 for (const char *p
= addr
; p
< end
; p
++)
78 if (!isprint(*p
)) { isText
= false; break; }
82 for (const char *p
= addr
; p
< end
; p
++)
87 for (const char *p
= addr
; p
< end
; p
++)
88 dump("%2.2x", static_cast<unsigned char>(*p
));
93 void dumpData(const char *title
, const void *ptr
, size_t size
)
95 #if !defined(NDEBUG_CODE)
104 // Turn a C++ typeid into a nice type name.
105 // This uses the C++ ABI where available.
106 // We're stripping out a few C++ prefixes; they're pretty redundant (and obvious).
108 string
makeTypeName(const type_info
&type
)
111 char *cname
= abi::__cxa_demangle(type
.name(), NULL
, NULL
, &status
);
112 string name
= !strncmp(cname
, "Security::", 10) ? (cname
+ 10) :
113 !strncmp(cname
, "std::", 5) ? (cname
+ 5) :
115 ::free(cname
); // yes, really (ABI rules)
121 // Target initialization.
122 // This where we should do all "first time" initializations.
124 #if !defined(NDEBUG_CODE)
126 char Target::progName
[maxProgNameLength
+ 1];
127 unsigned int Target::PerThread::lastUsed
;
130 : showScope(false), showThread(false), showProc(false), showDate(false),
133 // put into singleton slot if first
134 if (singleton
== NULL
)
137 // insert terminate handler
138 if (!previousTerminator
) // first time we do this
139 previousTerminator
= set_terminate(terminator
);
142 char execPath
[PATH_MAX
];
143 uint32_t length
= sizeof(execPath
);
144 if (_NSGetExecutablePath(execPath
, &length
)) {
145 strcpy(progName
, "unknown");
147 const char *p
= strrchr(execPath
, '/');
152 size_t plen
= strlen(p
);
153 if (plen
> maxProgNameLength
) // too long
154 p
+= plen
- maxProgNameLength
; // take rear
164 static void addScope(char *&bufp
, const char *scope
)
166 if (const char *sep
= strchr(scope
, ',')) {
167 bufp
+= sprintf(bufp
, "%-*s", Name::maxLength
, (const char *)Name(scope
, sep
));
168 } else { // single scope
169 bufp
+= sprintf(bufp
, "%-*s", Name::maxLength
, scope
);
175 // The core logging function of a Target
177 void Target::message(const char *scope
, const char *format
, va_list args
)
179 if (logSelector(scope
)) {
180 // note: messageConstructionSize is big enough for all prefixes constructed
181 char buffer
[messageConstructionSize
]; // building the message here
185 if (showDate
&& sink
->needsDate
) {
186 time_t now
= time(NULL
);
187 char *date
= ctime(&now
);
189 bufp
+= sprintf(bufp
, "%s ", date
+ 4); // Nov 24 18:22:48
193 if (showScope
&& scope
)
194 addScope(bufp
, scope
);
196 if (showProc
|| showThread
) {
197 char sub
[maxProgNameLength
+ 20];
199 if (showProc
&& showThread
)
200 plen
= sprintf(sub
, "%s[%d]", progName
, getpid());
202 plen
= sprintf(sub
, "%s", progName
);
204 plen
= sprintf(sub
, "[%d]", getpid());
205 unsigned int id
= perThread().id
;
207 plen
+= sprintf(sub
+ plen
, ":%d", id
);
208 if (plen
<= procLength
)
209 bufp
+= sprintf(bufp
, "%-*s ", int(procLength
), sub
);
211 bufp
+= sprintf(bufp
, "%s ", sub
+ plen
- procLength
);
214 // scope after proc/thread/pid
215 if (showScopeRight
&& scope
)
216 addScope(bufp
, scope
);
218 // now stuff the message body in, slightly roasted
219 size_t left
= buffer
+ sizeof(buffer
) - bufp
- 1; // reserve one
220 size_t written
= vsnprintf(bufp
, left
, format
, args
);
221 for (char *p
= bufp
; *p
; p
++)
224 if (written
>= left
) { // snprintf overflowed
226 strcpy(bufp
- 3, "...");
230 // now append a newline and a null
234 // submit to sink (do not count newline and null in count)
235 sink
->put(buffer
, (unsigned int)(bufp
- buffer
));
239 bool Target::debugging(const char *scope
)
241 return logSelector(scope
);
246 // The core debug-dump function of a target
248 void Target::dump(const char *format
, va_list args
)
250 char buffer
[messageConstructionSize
]; // building the message here
251 vsnprintf(buffer
, sizeof(buffer
), format
, args
);
252 for (char *p
= buffer
; *p
; p
++)
253 if ((!isprint(*p
) && !isspace(*p
)) || *p
== '\r')
258 bool Target::dump(const char *scope
)
260 return dumpSelector(scope
);
267 Target::Selector::Selector() : useSet(false), negate(false)
270 void Target::Selector::operator = (const char *scope
)
274 if (!strcmp(scope
, "all")) {
277 } else if (!strcmp(scope
, "none")) {
278 useSet
= negate
= false;
281 enableSet
.erase(enableSet
.begin(), enableSet
.end());
282 if (scope
[0] == '-') {
287 while (const char *sep
= strchr(scope
, ',')) {
288 enableSet
.insert(Name(scope
, sep
));
291 enableSet
.insert(scope
);
294 useSet
= negate
= false;
298 bool Target::Selector::operator () (const char *scope
) const
300 // a scope of NULL is a special override; it always qualifies
305 while (const char *sep
= strchr(scope
, ',')) {
306 if (enableSet
.find(Name(scope
, sep
)) != enableSet
.end())
310 return (enableSet
.find(scope
) != enableSet
.end()) != negate
;
318 // Establish Target state from the environment
320 void Target::setFromEnvironment()
323 logSelector
= getenv("DEBUGSCOPE");
324 dumpSelector
= getenv("DEBUGDUMP");
327 // Set and configure destination. Currently available:
328 // /some/where -> that file
329 // LOG_SOMETHING -> syslog facility
330 // >&number -> that (already) open (for write or append) file descriptor
331 // anything else -> try as a filename sight unseen [may change]
332 // DEBUGDEST not set -> stderr
333 // anything in error -> stderr (with an error message on it)
335 if (const char *dest
= getenv("DEBUGDEST")) {
336 if (dest
[0] == '/') { // full pathname, write to file
338 } else if (!strncmp(dest
, "LOG_", 4)) { // syslog
339 int facility
= LOG_DAEMON
;
340 for (CODE
*cp
= facilitynames
; cp
->c_name
; cp
++)
341 if (!strcmp(dest
, cp
->c_name
))
342 facility
= cp
->c_val
;
343 to(facility
| LOG_DEBUG
);
344 } else if (!strncmp(dest
, ">&", 2)) { // to file descriptor
345 int fd
= atoi(dest
+2);
346 if (FILE *f
= fdopen(fd
, "a")) {
350 secinfo("", "cannot log to fd[%d]: %s", fd
, strerror(errno
));
352 } else { // if everything else fails, write a file
355 } else { // default destination is stderr
362 void Target::configure()
364 configure(getenv("DEBUGOPTIONS"));
367 void Target::configure(const char *config
)
369 // configure global options
370 showScopeRight
= config
&& strstr(config
, "rscope");
371 showScope
= !showScopeRight
&& config
&& strstr(config
, "scope");
372 showThread
= config
&& (strstr(config
, "thread") || strstr(config
, "pid")); // (legacy)
373 showProc
= config
&& strstr(config
, "proc");
374 showDate
= config
&& strstr(config
, "date");
378 sink
->configure(config
);
383 // Explicit destination assignments
385 void Target::to(Sink
*s
)
391 void Target::to(FILE *file
)
393 to(new FileSink(file
));
396 void Target::to(const char *filename
)
398 if (FILE *f
= fopen(filename
, "a")) {
402 secinfo("", "cannot debug to \"%s\": %s", filename
, strerror(errno
));
406 void Target::to(int syslogPriority
)
408 to(new SyslogSink(syslogPriority
));
413 // Making and retrieving the default singleton
415 Target
*Target::singleton
;
417 Target
&Target::get()
419 if (singleton
== NULL
) {
420 Target
*t
= new Target
;
421 t
->setFromEnvironment();
428 // Standard sink implementations
430 Target::Sink::~Sink()
433 void Target::Sink::dump(const char *)
436 void Target::Sink::configure(const char *)
441 // The terminate handler installed when a Target is created
443 terminate_handler
Target::previousTerminator
;
445 void Target::terminator()
447 secinfo("exception", "uncaught exception terminates program");
448 previousTerminator();
449 secinfo("exception", "prior termination handler failed to abort; forcing abort");
455 // File sinks (write to file via stdio)
457 void FileSink::put(const char *inbuf
, unsigned int length
)
459 fwrite(inbuf
, 1, length
+ 1, file
); // do pick up the trailing newline
462 void FileSink::dump(const char *text
)
467 void FileSink::configure(const char *options
)
469 if (options
== NULL
|| !strstr(options
, "noflush")) {
470 // we mean "if the file isn't unbuffered", but what's the portable way to say that?
478 // Syslog sinks (write to syslog)
480 void SyslogSink::put(const char *buffer
, unsigned int length
)
482 syslog(priority
, "%1.*s", length
, buffer
); // don't pick up trailing newline
485 void SyslogSink::dump(const char *text
)
487 // add to dump buffer
488 snprintf(dumpPtr
, dumpBuffer
+ dumpBufferSize
- dumpPtr
, "%s", text
);
490 // take off full lines and submit
492 while (char *q
= strchr(p
, '\n')) {
493 *q
++ = '\0'; // terminate/break
494 syslog(priority
, " @@ %s", p
);
498 if (*p
) { // left-over unterminated line segment in buffer
499 dumpPtr
= p
+ strlen(p
);
500 if ((dumpBase
= p
) > dumpBuffer
+ dumpBufferSize
/ 2) {
501 // shift buffer down to make room
502 memmove(dumpBuffer
, dumpBase
, dumpPtr
- dumpBase
);
503 dumpPtr
-= (dumpBase
- dumpBuffer
);
504 dumpBase
= dumpBuffer
;
506 } else { // buffer is empty; reset to start
507 dumpBase
= dumpPtr
= dumpBuffer
;
511 void SyslogSink::configure(const char *options
)
518 } // end namespace Debug
519 } // end namespace Security
522 void secdebug_internal(const char* scope
, const char* format
, ...) {
525 void secdebugfunc_internal(const char* scope
, const char* functionname
, const char* format
, ...) {