]>
git.saurik.com Git - apple/security.git/blob - Security/libsecurity_utilities/lib/debugging.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/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
40 // enable kernel tracing
41 #define ENABLE_SECTRACE 1
51 bool dumping(const char *scope
)
53 #if defined(NDEBUG_STUBS)
56 return Target::get().dump(scope
);
60 void dump(const char *format
, ...)
62 #if !defined(NDEBUG_CODE)
64 va_start(args
, format
);
65 Target::get().dump(format
, args
);
70 void dumpData(const void *ptr
, size_t size
)
72 #if !defined(NDEBUG_CODE)
73 const char *addr
= reinterpret_cast<const char *>(ptr
);
74 const char *end
= addr
+ size
;
76 for (const char *p
= addr
; p
< end
; p
++)
77 if (!isprint(*p
)) { isText
= false; break; }
81 for (const char *p
= addr
; p
< end
; p
++)
86 for (const char *p
= addr
; p
< end
; p
++)
87 dump("%2.2x", static_cast<unsigned char>(*p
));
92 void dumpData(const char *title
, const void *ptr
, size_t size
)
94 #if !defined(NDEBUG_CODE)
103 // Turn a C++ typeid into a nice type name.
104 // This uses the C++ ABI where available.
105 // We're stripping out a few C++ prefixes; they're pretty redundant (and obvious).
107 string
makeTypeName(const type_info
&type
)
110 char *cname
= abi::__cxa_demangle(type
.name(), NULL
, NULL
, &status
);
111 string name
= !strncmp(cname
, "Security::", 10) ? (cname
+ 10) :
112 !strncmp(cname
, "std::", 5) ? (cname
+ 5) :
114 ::free(cname
); // yes, really (ABI rules)
120 // Target initialization.
121 // This where we should do all "first time" initializations.
123 #if !defined(NDEBUG_CODE)
125 char Target::progName
[maxProgNameLength
+ 1];
126 unsigned int Target::PerThread::lastUsed
;
129 : showScope(false), showThread(false), showProc(false), showDate(false),
132 // put into singleton slot if first
133 if (singleton
== NULL
)
136 // insert terminate handler
137 if (!previousTerminator
) // first time we do this
138 previousTerminator
= set_terminate(terminator
);
141 char execPath
[PATH_MAX
];
142 uint32_t length
= sizeof(execPath
);
143 if (_NSGetExecutablePath(execPath
, &length
)) {
144 strcpy(progName
, "unknown");
146 const char *p
= strrchr(execPath
, '/');
151 size_t plen
= strlen(p
);
152 if (plen
> maxProgNameLength
) // too long
153 p
+= plen
- maxProgNameLength
; // take rear
163 static void addScope(char *&bufp
, const char *scope
)
165 if (const char *sep
= strchr(scope
, ',')) {
166 bufp
+= sprintf(bufp
, "%-*s", Name::maxLength
, (const char *)Name(scope
, sep
));
167 } else { // single scope
168 bufp
+= sprintf(bufp
, "%-*s", Name::maxLength
, scope
);
174 // The core logging function of a Target
176 void Target::message(const char *scope
, const char *format
, va_list args
)
178 if (logSelector(scope
)) {
179 // note: messageConstructionSize is big enough for all prefixes constructed
180 char buffer
[messageConstructionSize
]; // building the message here
184 if (showDate
&& sink
->needsDate
) {
185 time_t now
= time(NULL
);
186 char *date
= ctime(&now
);
188 bufp
+= sprintf(bufp
, "%s ", date
+ 4); // Nov 24 18:22:48
192 if (showScope
&& scope
)
193 addScope(bufp
, scope
);
195 if (showProc
|| showThread
) {
196 char sub
[maxProgNameLength
+ 20];
198 if (showProc
&& showThread
)
199 plen
= sprintf(sub
, "%s[%d]", progName
, getpid());
201 plen
= sprintf(sub
, "%s", progName
);
203 plen
= sprintf(sub
, "[%d]", getpid());
204 unsigned int id
= perThread().id
;
206 plen
+= sprintf(sub
+ plen
, ":%d", id
);
207 if (plen
<= procLength
)
208 bufp
+= sprintf(bufp
, "%-*s ", int(procLength
), sub
);
210 bufp
+= sprintf(bufp
, "%s ", sub
+ plen
- procLength
);
213 // scope after proc/thread/pid
214 if (showScopeRight
&& scope
)
215 addScope(bufp
, scope
);
217 // now stuff the message body in, slightly roasted
218 size_t left
= buffer
+ sizeof(buffer
) - bufp
- 1; // reserve one
219 size_t written
= vsnprintf(bufp
, left
, format
, args
);
220 for (char *p
= bufp
; *p
; p
++)
223 if (written
>= left
) { // snprintf overflowed
225 strcpy(bufp
- 3, "...");
229 // now append a newline and a null
233 // submit to sink (do not count newline and null in count)
234 sink
->put(buffer
, (unsigned int)(bufp
- buffer
));
238 bool Target::debugging(const char *scope
)
240 return logSelector(scope
);
245 // The core debug-dump function of a target
247 void Target::dump(const char *format
, va_list args
)
249 char buffer
[messageConstructionSize
]; // building the message here
250 vsnprintf(buffer
, sizeof(buffer
), format
, args
);
251 for (char *p
= buffer
; *p
; p
++)
252 if ((!isprint(*p
) && !isspace(*p
)) || *p
== '\r')
257 bool Target::dump(const char *scope
)
259 return dumpSelector(scope
);
266 Target::Selector::Selector() : useSet(false), negate(false)
269 void Target::Selector::operator = (const char *scope
)
273 if (!strcmp(scope
, "all")) {
276 } else if (!strcmp(scope
, "none")) {
277 useSet
= negate
= false;
280 enableSet
.erase(enableSet
.begin(), enableSet
.end());
281 if (scope
[0] == '-') {
286 while (const char *sep
= strchr(scope
, ',')) {
287 enableSet
.insert(Name(scope
, sep
));
290 enableSet
.insert(scope
);
293 useSet
= negate
= false;
297 bool Target::Selector::operator () (const char *scope
) const
299 // a scope of NULL is a special override; it always qualifies
304 while (const char *sep
= strchr(scope
, ',')) {
305 if (enableSet
.find(Name(scope
, sep
)) != enableSet
.end())
309 return (enableSet
.find(scope
) != enableSet
.end()) != negate
;
317 // Establish Target state from the environment
319 void Target::setFromEnvironment()
322 logSelector
= getenv("DEBUGSCOPE");
323 dumpSelector
= getenv("DEBUGDUMP");
326 // Set and configure destination. Currently available:
327 // /some/where -> that file
328 // LOG_SOMETHING -> syslog facility
329 // >&number -> that (already) open (for write or append) file descriptor
330 // anything else -> try as a filename sight unseen [may change]
331 // DEBUGDEST not set -> stderr
332 // anything in error -> stderr (with an error message on it)
334 if (const char *dest
= getenv("DEBUGDEST")) {
335 if (dest
[0] == '/') { // full pathname, write to file
337 } else if (!strncmp(dest
, "LOG_", 4)) { // syslog
338 int facility
= LOG_DAEMON
;
339 for (CODE
*cp
= facilitynames
; cp
->c_name
; cp
++)
340 if (!strcmp(dest
, cp
->c_name
))
341 facility
= cp
->c_val
;
342 to(facility
| LOG_DEBUG
);
343 } else if (!strncmp(dest
, ">&", 2)) { // to file descriptor
344 int fd
= atoi(dest
+2);
345 if (FILE *f
= fdopen(fd
, "a")) {
349 secdebug("", "cannot log to fd[%d]: %s", fd
, strerror(errno
));
351 } else { // if everything else fails, write a file
354 } else { // default destination is stderr
361 void Target::configure()
363 configure(getenv("DEBUGOPTIONS"));
366 void Target::configure(const char *config
)
368 // configure global options
369 showScopeRight
= config
&& strstr(config
, "rscope");
370 showScope
= !showScopeRight
&& config
&& strstr(config
, "scope");
371 showThread
= config
&& (strstr(config
, "thread") || strstr(config
, "pid")); // (legacy)
372 showProc
= config
&& strstr(config
, "proc");
373 showDate
= config
&& strstr(config
, "date");
377 sink
->configure(config
);
382 // Explicit destination assignments
384 void Target::to(Sink
*s
)
390 void Target::to(FILE *file
)
392 to(new FileSink(file
));
395 void Target::to(const char *filename
)
397 if (FILE *f
= fopen(filename
, "a")) {
401 secdebug("", "cannot debug to \"%s\": %s", filename
, strerror(errno
));
405 void Target::to(int syslogPriority
)
407 to(new SyslogSink(syslogPriority
));
412 // Making and retrieving the default singleton
414 Target
*Target::singleton
;
416 Target
&Target::get()
418 if (singleton
== NULL
) {
419 Target
*t
= new Target
;
420 t
->setFromEnvironment();
427 // Standard sink implementations
429 Target::Sink::~Sink()
432 void Target::Sink::dump(const char *)
435 void Target::Sink::configure(const char *)
440 // The terminate handler installed when a Target is created
442 terminate_handler
Target::previousTerminator
;
444 void Target::terminator()
446 secdebug("exception", "uncaught exception terminates program");
447 previousTerminator();
448 secdebug("exception", "prior termination handler failed to abort; forcing abort");
454 // File sinks (write to file via stdio)
456 void FileSink::put(const char *inbuf
, unsigned int length
)
458 fwrite(inbuf
, 1, length
+ 1, file
); // do pick up the trailing newline
461 void FileSink::dump(const char *text
)
466 void FileSink::configure(const char *options
)
468 if (options
== NULL
|| !strstr(options
, "noflush")) {
469 // we mean "if the file isn't unbuffered", but what's the portable way to say that?
477 // Syslog sinks (write to syslog)
479 void SyslogSink::put(const char *buffer
, unsigned int length
)
481 syslog(priority
, "%1.*s", length
, buffer
); // don't pick up trailing newline
484 void SyslogSink::dump(const char *text
)
486 // add to dump buffer
487 snprintf(dumpPtr
, dumpBuffer
+ dumpBufferSize
- dumpPtr
, "%s", text
);
489 // take off full lines and submit
491 while (char *q
= strchr(p
, '\n')) {
492 *q
++ = '\0'; // terminate/break
493 syslog(priority
, " @@ %s", p
);
497 if (*p
) { // left-over unterminated line segment in buffer
498 dumpPtr
= p
+ strlen(p
);
499 if ((dumpBase
= p
) > dumpBuffer
+ dumpBufferSize
/ 2) {
500 // shift buffer down to make room
501 memmove(dumpBuffer
, dumpBase
, dumpPtr
- dumpBase
);
502 dumpPtr
-= (dumpBase
- dumpBuffer
);
503 dumpBase
= dumpBuffer
;
505 } else { // buffer is empty; reset to start
506 dumpBase
= dumpPtr
= dumpBuffer
;
510 void SyslogSink::configure(const char *options
)
517 } // end namespace Debug
518 } // end namespace Security