]>
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().dumping(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
++) {
226 if (written
>= left
) { // snprintf overflowed
228 strcpy(bufp
- 3, "...");
232 // now append a newline and a null
236 // submit to sink (do not count newline and null in count)
237 sink
->put(buffer
, (unsigned int)(bufp
- buffer
));
241 bool Target::debugging(const char *scope
)
243 return logSelector(scope
);
248 // The core debug-dump function of a target
250 void Target::dump(const char *format
, va_list args
)
252 char buffer
[messageConstructionSize
]; // building the message here
253 vsnprintf(buffer
, sizeof(buffer
), format
, args
);
254 for (char *p
= buffer
; *p
; p
++)
255 if ((!isprint(*p
) && !isspace(*p
)) || *p
== '\r')
260 bool Target::dumping(const char *scope
)
262 return dumpSelector(scope
);
269 Target::Selector::Selector() : useSet(false), negate(false)
272 void Target::Selector::operator = (const char *scope
)
276 if (!strcmp(scope
, "all")) {
279 } else if (!strcmp(scope
, "none")) {
280 useSet
= negate
= false;
283 enableSet
.erase(enableSet
.begin(), enableSet
.end());
284 if (scope
[0] == '-') {
289 while (const char *sep
= strchr(scope
, ',')) {
290 enableSet
.insert(Name(scope
, sep
));
293 enableSet
.insert(scope
);
296 useSet
= negate
= false;
300 bool Target::Selector::operator () (const char *scope
) const
302 // a scope of NULL is a special override; it always qualifies
307 while (const char *sep
= strchr(scope
, ',')) {
308 if (enableSet
.find(Name(scope
, sep
)) != enableSet
.end())
312 return (enableSet
.find(scope
) != enableSet
.end()) != negate
;
320 // Establish Target state from the environment
322 void Target::setFromEnvironment()
325 logSelector
= getenv("DEBUGSCOPE");
326 dumpSelector
= getenv("DEBUGDUMP");
329 // Set and configure destination. Currently available:
330 // /some/where -> that file
331 // LOG_SOMETHING -> syslog facility
332 // >&number -> that (already) open (for write or append) file descriptor
333 // anything else -> try as a filename sight unseen [may change]
334 // DEBUGDEST not set -> stderr
335 // anything in error -> stderr (with an error message on it)
337 if (const char *dest
= getenv("DEBUGDEST")) {
338 if (dest
[0] == '/') { // full pathname, write to file
340 } else if (!strncmp(dest
, "LOG_", 4)) { // syslog
341 int facility
= LOG_DAEMON
;
342 for (CODE
*cp
= facilitynames
; cp
->c_name
; cp
++)
343 if (!strcmp(dest
, cp
->c_name
))
344 facility
= cp
->c_val
;
345 to(facility
| LOG_DEBUG
);
346 } else if (!strncmp(dest
, ">&", 2)) { // to file descriptor
347 int fd
= atoi(dest
+2);
348 if (FILE *f
= fdopen(fd
, "a")) {
352 secinfo("", "cannot log to fd[%d]: %s", fd
, strerror(errno
));
354 } else { // if everything else fails, write a file
357 } else { // default destination is stderr
364 void Target::configure()
366 configure(getenv("DEBUGOPTIONS"));
369 void Target::configure(const char *config
)
371 // configure global options
372 showScopeRight
= config
&& strstr(config
, "rscope");
373 showScope
= !showScopeRight
&& config
&& strstr(config
, "scope");
374 showThread
= config
&& (strstr(config
, "thread") || strstr(config
, "pid")); // (legacy)
375 showProc
= config
&& strstr(config
, "proc");
376 showDate
= config
&& strstr(config
, "date");
380 sink
->configure(config
);
385 // Explicit destination assignments
387 void Target::to(Sink
*s
)
393 void Target::to(FILE *file
)
395 to(new FileSink(file
));
398 void Target::to(const char *filename
)
400 if (FILE *f
= fopen(filename
, "a")) {
404 secinfo("", "cannot debug to \"%s\": %s", filename
, strerror(errno
));
408 void Target::to(int syslogPriority
)
410 to(new SyslogSink(syslogPriority
));
415 // Making and retrieving the default singleton
417 Target
*Target::singleton
;
419 Target
&Target::get()
421 if (singleton
== NULL
) {
422 Target
*t
= new Target
;
423 t
->setFromEnvironment();
425 // The Target constructor attempts to set singleton to the object. If it didn't succeed, we don't need T anymore.
435 // Standard sink implementations
437 Target::Sink::~Sink()
440 void Target::Sink::dump(const char *)
443 void Target::Sink::configure(const char *)
448 // The terminate handler installed when a Target is created
450 terminate_handler
Target::previousTerminator
;
452 void Target::terminator()
454 secinfo("exception", "uncaught exception terminates program");
455 previousTerminator();
456 secinfo("exception", "prior termination handler failed to abort; forcing abort");
462 // File sinks (write to file via stdio)
464 void FileSink::put(const char *inbuf
, unsigned int length
)
466 fwrite(inbuf
, 1, length
+ 1, file
); // do pick up the trailing newline
469 void FileSink::dump(const char *text
)
474 void FileSink::configure(const char *options
)
476 if (options
== NULL
|| !strstr(options
, "noflush")) {
477 // we mean "if the file isn't unbuffered", but what's the portable way to say that?
485 // Syslog sinks (write to syslog)
487 void SyslogSink::put(const char *buffer
, unsigned int length
)
489 syslog(priority
, "%1.*s", length
, buffer
); // don't pick up trailing newline
492 void SyslogSink::dump(const char *text
)
494 // add to dump buffer
495 snprintf(dumpPtr
, dumpBuffer
+ dumpBufferSize
- dumpPtr
, "%s", text
);
497 // take off full lines and submit
499 while (char *q
= strchr(p
, '\n')) {
500 *q
++ = '\0'; // terminate/break
501 syslog(priority
, " @@ %s", p
);
505 if (*p
) { // left-over unterminated line segment in buffer
506 dumpPtr
= p
+ strlen(p
);
507 if ((dumpBase
= p
) > dumpBuffer
+ dumpBufferSize
/ 2) {
508 // shift buffer down to make room
509 memmove(dumpBuffer
, dumpBase
, dumpPtr
- dumpBase
);
510 dumpPtr
-= (dumpBase
- dumpBuffer
);
511 dumpBase
= dumpBuffer
;
513 } else { // buffer is empty; reset to start
514 dumpBase
= dumpPtr
= dumpBuffer
;
518 void SyslogSink::configure(const char *options
)
525 } // end namespace Debug
526 } // end namespace Security
529 void secdebug_internal(const char* scope
, const char* format
, ...) {
532 void secdebugfunc_internal(const char* scope
, const char* functionname
, const char* format
, ...) {