2 * Copyright (c) 2017 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.0 (the 'License'). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
22 * @APPLE_LICENSE_HEADER_END@
40 #include <sys/ioctl.h>
42 #include <ktrace/session.h>
43 #include <dispatch/dispatch.h>
44 #include <System/sys/kdebug.h>
48 #define DBG_FUNC_ALL (DBG_FUNC_START | DBG_FUNC_END)
52 * MAXCOLS controls when extra data kicks in.
53 * MAX_WIDE_MODE_COLS controls -w mode to get even wider data in path.
56 #define MAXWIDTH (MAXCOLS + 64)
58 unsigned int columns
= 0;
60 bool wideflag
= false;
61 bool RAW_flag
= false;
62 bool JSON_flag
= false;
63 bool JSON_Tracing_flag
= false;
64 dispatch_source_t sigwinch_source
;
68 fprintf(stderr
, "Usage: dyld_usage [-e] [-f mode] [-t seconds] [-R rawfile [-S start_time] [-E end_time]] [pid | cmd [pid | cmd] ...]\n");
69 fprintf(stderr
, " -e exclude the specified list of pids from the sample\n");
70 fprintf(stderr
, " and exclude dyld_usage by default\n");
71 fprintf(stderr
, " -t specifies timeout in seconds (for use in automated tools)\n");
72 fprintf(stderr
, " -R specifies a raw trace file to process\n");
73 fprintf(stderr
, " pid selects process(s) to sample\n");
74 fprintf(stderr
, " cmd selects process(s) matching command string to sample\n");
75 fprintf(stderr
, "By default (no options) the following processes are excluded from the output:\n");
76 fprintf(stderr
, "dyld_usage, Terminal, telnetd, sshd, rlogind, tcsh, csh, sh\n\n");
88 if (isatty(STDOUT_FILENO
)) {
89 if (ioctl(1, TIOCGWINSZ
, &size
) != -1) {
90 columns
= size
.ws_col
;
92 if (columns
> MAXWIDTH
)
98 std::map
<uint64_t, uint64_t> gActiveStringIDs
;
99 std::map
<uint64_t, std::string
> gActiveStrings
;
101 const std::string
& stringForID(uint64_t id
) {
102 static std::string emptyString
= "";
103 auto i
= gActiveStrings
.find(id
);
104 if (i
== gActiveStrings
.end())
110 mach_to_nano(uint64_t mach
)
112 uint64_t nanoseconds
= 0;
113 assert(ktrace_convert_timestamp_to_nanoseconds(s
, mach
, &nanoseconds
) == 0);
117 struct output_renderer
{
118 output_renderer(ktrace_session_t S
, ktrace_event_t E
) :
119 _commandName(ktrace_get_execname_for_thread(s
, E
->threadid
)),
120 _threadid(E
->threadid
), _pid(ktrace_get_pid_for_thread(s
, E
->threadid
)) {}
121 void recordEvent(ktrace_event_t event
) {
122 uint32_t code
= event
->debugid
& KDBG_EVENTID_MASK
;
123 if (event
->debugid
& DBG_FUNC_START
) {
125 case DBG_DYLD_TIMING_DLOPEN
: enqueueEvent
<dlopen
>(event
, true); break;
126 case DBG_DYLD_TIMING_LAUNCH_EXECUTABLE
: enqueueEvent
<app_launch
>(event
, true); break;
127 case DBG_DYLD_TIMING_DLSYM
: enqueueEvent
<dlsym
>(event
, true); break;
128 case DBG_DYLD_TIMING_STATIC_INITIALIZER
: enqueueEvent
<static_init
>(event
, false); break;
129 case DBG_DYLD_TIMING_MAP_IMAGE
: enqueueEvent
<map_image
>(event
, false); break;
130 case DBG_DYLD_TIMING_APPLY_FIXUPS
: enqueueEvent
<apply_fixups
>(event
, false); break;
131 case DBG_DYLD_TIMING_ATTACH_CODESIGNATURE
: enqueueEvent
<attach_signature
>(event
, false); break;
132 case DBG_DYLD_TIMING_BUILD_CLOSURE
: enqueueEvent
<build_closure
>(event
, false); break;
133 case DBG_DYLD_TIMING_DLADDR
: enqueueEvent
<dladdr
>(event
, true); break;
134 case DBG_DYLD_TIMING_DLCLOSE
: enqueueEvent
<dlclose
>(event
, true); break;
135 case DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
: enqueueEvent
<add_image_callback
>(event
, false); break;
136 case DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE
: enqueueEvent
<remove_image_callback
>(event
, false); break;
137 case DBG_DYLD_TIMING_OBJC_INIT
: enqueueEvent
<objc_image_init
>(event
, false); break;
138 case DBG_DYLD_TIMING_OBJC_MAP
: enqueueEvent
<objc_images_map
>(event
, false); break;
142 case DBG_DYLD_TIMING_DLOPEN
: dequeueEvent
<dlopen
>(event
, [&](dlopen
* endEvent
){
143 endEvent
->result
= event
->arg2
;
145 case DBG_DYLD_TIMING_LAUNCH_EXECUTABLE
: dequeueEvent
<app_launch
>(event
, [&](app_launch
* endEvent
){
146 endEvent
->launchMode
= event
->arg4
;
148 case DBG_DYLD_TIMING_DLSYM
: dequeueEvent
<dlsym
>(event
, [&](dlsym
* endEvent
){
149 endEvent
->result
= event
->arg2
;
151 case DBG_DYLD_TIMING_STATIC_INITIALIZER
: dequeueEvent
<static_init
>(event
, [&](static_init
* endEvent
){}); break;
152 case DBG_DYLD_TIMING_MAP_IMAGE
: dequeueEvent
<map_image
>(event
, [&](map_image
* endEvent
){
153 endEvent
->result
= event
->arg2
;
155 case DBG_DYLD_TIMING_APPLY_FIXUPS
: dequeueEvent
<apply_fixups
>(event
, [&](apply_fixups
* endEvent
){}); break;
156 case DBG_DYLD_TIMING_ATTACH_CODESIGNATURE
: dequeueEvent
<attach_signature
>(event
, [&](attach_signature
* endEvent
){}); break;
157 case DBG_DYLD_TIMING_BUILD_CLOSURE
: dequeueEvent
<build_closure
>(event
, [&](build_closure
* endEvent
){}); break;
158 case DBG_DYLD_TIMING_DLCLOSE
: dequeueEvent
<dlclose
>(event
, [&](dlclose
* endEvent
){
159 endEvent
->result
= (int)event
->arg2
;
161 case DBG_DYLD_TIMING_DLADDR
: dequeueEvent
<dladdr
>(event
, [&](dladdr
* endEvent
){
162 endEvent
->result
= (int)event
->arg2
;
163 endEvent
->imageAddress
= (int)event
->arg3
;
164 endEvent
->symbolAddress
= (int)event
->arg4
;
166 case DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
: dequeueEvent
<add_image_callback
>(event
, [&](add_image_callback
* endEvent
){}); break;
167 case DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE
: dequeueEvent
<remove_image_callback
>(event
, [&](remove_image_callback
* endEvent
){}); break;
168 case DBG_DYLD_TIMING_OBJC_INIT
: dequeueEvent
<objc_image_init
>(event
, [&](objc_image_init
* endEvent
){}); break;
169 case DBG_DYLD_TIMING_OBJC_MAP
: dequeueEvent
<objc_images_map
>(event
, [&](objc_images_map
* endEvent
){}); break;
174 bool empty() { return !_currentRootEvent
&& _eventStack
.empty() && _rootEvents
.empty(); }
177 void enqueueEvent(ktrace_event_t event
, bool rootEvent
) {
178 auto sharedEvent
= std::make_shared
<T
>(event
);
179 if (!_currentRootEvent
) {
180 if (!rootEvent
) return;
181 assert(_eventStack
.empty());
182 _currentRootEvent
= sharedEvent
;
184 sharedEvent
->setDepth(_eventStack
.size());
185 _eventStack
.back()->addChild(sharedEvent
);
187 _eventStack
.push_back(sharedEvent
);
191 void dequeueEvent(ktrace_event_t event
, std::function
<void(T
*)> lambda
) {
192 if (_eventStack
.empty()) return;
193 auto currentEvent
= dynamic_cast<T
*>(_eventStack
.back().get());
194 currentEvent
->setEndEvent(event
);
195 lambda(currentEvent
);
196 _eventStack
.pop_back();
197 if (_currentRootEvent
&& _eventStack
.empty()) {
198 output(_currentRootEvent
);
199 _currentRootEvent
= nullptr;
204 event_pair(ktrace_event_t E
) : _startTime(E
->timestamp
), _walltime(E
->walltime
), _threadid(E
->threadid
), _depth(0),
205 _eventCode(KDBG_EXTRACT_CODE(E
->debugid
)) {};
206 virtual ~event_pair(){}
207 std::vector
<std::shared_ptr
<event_pair
>>& children() { return _children
; }
208 void setDepth(uint64_t D
) { _depth
= D
; }
209 uint64_t depth() { return _depth
; }
210 struct timeval
walltime() { return _walltime
; };
211 uint64_t startTimestamp() { return _startTime
; };
212 uint64_t endTimestamp() { return _endTime
; }
213 unsigned long threadid() { return _threadid
; }
214 void addChild(std::shared_ptr
<event_pair
> child
) {_children
.push_back(child
);}
215 void setEndEvent(ktrace_event_t E
) { _endTime
= E
->timestamp
; }
216 uint16_t eventCode() const { return _eventCode
; }
218 std::vector
<std::shared_ptr
<event_pair
>> _children
;
222 unsigned long _threadid
;
224 struct timeval _walltime
;
227 time_t _lastTimeWallSeconds
= -1;
228 std::string _timestampStr
;
229 std::string _commandName
;
231 unsigned long _threadid
;
233 std::string
timestamp(std::shared_ptr
<event_pair
> event
, bool extended
) {
234 std::ostringstream result
;
235 struct timeval now_walltime
= event
->walltime();
236 assert(now_walltime
.tv_sec
|| now_walltime
.tv_usec
);
238 /* try and reuse the timestamp string */
239 if (_lastTimeWallSeconds
!= now_walltime
.tv_sec
) {
241 (void)strftime(timestamp
, sizeof (timestamp
), "%H:%M:%S", localtime(&now_walltime
.tv_sec
));
242 _lastTimeWallSeconds
= now_walltime
.tv_sec
;
243 _timestampStr
= timestamp
;
245 result
<< _timestampStr
;
248 result
<< "." << std::setw(6) << std::setfill(' ') << std::to_string(now_walltime
.tv_usec
);
253 std::string
process(std::shared_ptr
<event_pair
> event
, bool extended
) {
255 std::ostringstream result
;
256 result
<< _commandName
<< "." << std::to_string(event
->threadid());
259 std::string result
= _commandName
;
260 result
.resize(12, ' ');
265 std::string
duration(std::shared_ptr
<event_pair
> event
) {
266 std::ostringstream result
;
267 uint64_t usecs
= (mach_to_nano(event
->endTimestamp() - event
->startTimestamp()) + (NSEC_PER_USEC
- 1)) / NSEC_PER_USEC
;
268 uint64_t secs
= usecs
/ USEC_PER_SEC
;
269 usecs
-= secs
* USEC_PER_SEC
;
270 result
<< secs
<< "." << std::setw(6) << std::setfill('0') << usecs
;
275 void outputConsole(std::shared_ptr
<event_pair
> node
, uint64_t width
, std::ostringstream
& sstr
, uint64_t depth
) {
276 std::ostringstream line
;
277 bool extended
= false;
278 if (auto dlopenNode
= dynamic_cast<dlopen
*>(node
.get())) {
279 line
<< "dlopen(\"" << dlopenNode
->path
<< "\", " << dlopenNode
->flagString() << ") -> 0x" << dlopenNode
->result
;
280 } else if (auto dlsymNode
= dynamic_cast<dlsym
*>(node
.get())) {
281 line
<< "dlsym(0x" << std::hex
<< dlsymNode
->handle
<< ", \"" << dlsymNode
->symbol
<< "\") -> " << dlsymNode
->result
;
282 } else if (auto mapImageNode
= dynamic_cast<map_image
*>(node
.get())) {
283 line
<< "map file \"" << mapImageNode
->path
<< "\" -> 0x" << std::hex
<< mapImageNode
->result
;
284 } else if (auto sigNode
= dynamic_cast<attach_signature
*>(node
.get())) {
285 line
<< "attach codesignature";
286 } else if (auto buildClosureNode
= dynamic_cast<build_closure
*>(node
.get())) {
287 line
<< "build closure";
288 } else if (auto launchNode
= dynamic_cast<app_launch
*>(node
.get())) {
289 line
<< "app launch (dyld" << std::dec
<< launchNode
->launchMode
<< ") -> 0x" << std::hex
<< launchNode
->address
;
290 } else if (auto initNode
= dynamic_cast<static_init
*>(node
.get())) {
291 line
<< "run static initializer 0x" << std::hex
<< initNode
->funcAddress
;
292 } else if (auto initNode
= dynamic_cast<apply_fixups
*>(node
.get())) {
293 line
<< "apply fixups";
294 } else if (auto dlcloseNode
= dynamic_cast<dlclose
*>(node
.get())) {
295 line
<< "dlclose(0x" << std::hex
<< dlcloseNode
->handle
<< ") -> " << dlcloseNode
->result
;
296 } else if (auto dladdrNode
= dynamic_cast<dladdr
*>(node
.get())) {
297 line
<< "dladdr(0x" << dladdrNode
->address
<< ") -> image: 0x" << std::hex
<< dladdrNode
->imageAddress
;
298 line
<< ", symbol: 0x" << dladdrNode
->symbolAddress
;
299 } else if (auto addImageNode
= dynamic_cast<add_image_callback
*>(node
.get())) {
300 line
<< std::hex
<< "add image callback(0x" << addImageNode
->funcAddress
<< ") for image 0x" << addImageNode
->libraryAddress
;
301 } else if (auto removeImageNode
= dynamic_cast<remove_image_callback
*>(node
.get())) {
302 line
<< std::hex
<< "remove image callback(0x" << removeImageNode
->funcAddress
<< ") for image 0x" << removeImageNode
->libraryAddress
;
303 } else if (auto objcInitNode
= dynamic_cast<objc_image_init
*>(node
.get())) {
304 line
<< std::hex
<< "objC init image(0x" << objcInitNode
->libraryAddress
<< ")";
305 } else if (auto objcMapNode
= dynamic_cast<objc_images_map
*>(node
.get())) {
306 line
<< std::hex
<< "objC map images callback";
309 if (width
> MAXCOLS
) {
313 std::string timestampStr
= timestamp(node
, extended
);
314 std::string lineStr
= line
.str();
315 std::string commandStr
= process(node
, extended
);
316 std::string durationStr
= duration(node
);
317 uint64_t lineMax
= width
- (timestampStr
.length() + commandStr
.length() + durationStr
.length() + 2*depth
+ 3);
318 lineStr
.resize(lineMax
, ' ');
320 sstr
<< timestampStr
<< " ";
321 std::fill_n(std::ostream_iterator
<char>(sstr
), 2*depth
, ' ');
322 sstr
<< lineStr
<< " " << durationStr
<< " " << commandStr
<< std::endl
;
324 for (const auto& child
: node
->children()) {
325 outputConsole(child
, width
, sstr
, depth
+1);
329 void outputJSON(std::shared_ptr
<event_pair
> node
, std::ostringstream
& sstr
) {
330 if (auto dlopenNode
= dynamic_cast<dlopen
*>(node
.get())) {
332 sstr
<< "{\"type\":\"dlopen\",\"path\":\"" << dlopenNode
->path
<< "\",\"flags\":\"0x" << dlopenNode
->flags
<< "\"";
333 sstr
<< ",\"result\":\"" << dlopenNode
->result
<< "\"";
334 } else if (auto dlsymNode
= dynamic_cast<dlsym
*>(node
.get())) {
335 sstr
<< std::hex
<< "{\"type\":\"dlsym\",\"symbol\":\"" << dlsymNode
->symbol
<< "\",\"handle\":\"0x";
336 sstr
<< dlsymNode
->handle
<< "\",\"result\":\"0x" << dlsymNode
->result
<< "\"";
337 } else if (auto mapImageNode
= dynamic_cast<map_image
*>(node
.get())) {
339 sstr
<< "{\"type\":\"map_image\",\"path\":\"" << mapImageNode
->path
<< "\",\"result\":\"0x" << mapImageNode
->result
<< "\"";
340 } else if (auto sigNode
= dynamic_cast<attach_signature
*>(node
.get())) {
341 sstr
<< "{\"type\":\"attach_codesignature\"";
342 } else if (auto buildClosureNode
= dynamic_cast<build_closure
*>(node
.get())) {
343 sstr
<< "{\"type\":\"build_closure\"";
344 } else if (auto launchNode
= dynamic_cast<app_launch
*>(node
.get())) {
346 sstr
<< "{\"type\":\"app_launch\",\"address\":\"0x";
347 sstr
<< launchNode
->address
<< "\",\"mode\":" << launchNode
->launchMode
<< "";
348 } else if (auto initNode
= dynamic_cast<static_init
*>(node
.get())) {
350 sstr
<< "{\"type\":\"static_init\",\"image_address\":\"0x" << initNode
->libraryAddress
;
351 sstr
<< "\",\"function_address\":\"0x" << initNode
->funcAddress
<< "\"";
352 } else if (auto initNode
= dynamic_cast<apply_fixups
*>(node
.get())) {
353 sstr
<< "{\"type\":\"apply_fixups\"";
354 } else if (auto dlcloseNode
= dynamic_cast<dlclose
*>(node
.get())) {
355 sstr
<< std::hex
<< "{\"type\":\"dlclose\",\"handle\":\"0x";
356 sstr
<< dlcloseNode
->handle
<< "\",\"result\":\"0x" << dlcloseNode
->result
<< "\"";
357 } else if (auto dladdrNode
= dynamic_cast<dladdr
*>(node
.get())) {
358 sstr
<< std::hex
<< "{\"type\":\"dladdr\",\"address\":\"0x" << dladdrNode
->address
<< "\",\"result\":\"0x";
359 sstr
<< dladdrNode
->result
<< "\",\"symbol_address\":\"0x" << dladdrNode
->symbolAddress
;
360 sstr
<< "\",\"image_address\":\"0x" << dladdrNode
->imageAddress
<< "\"";
362 sstr
<< "{\"type\":\"unknown\"";
365 if (!node
->children().empty()) {
366 bool firstChild
= true;
367 sstr
<< ",\"children\":[";
368 for (const auto& child
: node
->children()) {
373 outputJSON(child
, sstr
);
377 sstr
<< std::dec
<< ",\"start_nano\":\"" << mach_to_nano(node
->startTimestamp());
378 sstr
<< "\",\"end_nano\":\"" << mach_to_nano(node
->endTimestamp()) << "\"}";
381 void outputTracingJSON(std::shared_ptr
<event_pair
> node
, std::ostringstream
& sstr
) {
382 auto emitEventInfo
= [&](bool isStart
) {
383 if (auto dlopenNode
= dynamic_cast<dlopen
*>(node
.get())) {
384 sstr
<< "{\"name\": \"dlopen(" << dlopenNode
->path
<< ")\", \"cat\": \"" << "dlopen" << "\"";
385 } else if (auto dlsymNode
= dynamic_cast<dlsym
*>(node
.get())) {
386 sstr
<< "{\"name\": \"dlsym(" << dlsymNode
->symbol
<< ")\", \"cat\": \"" << "dlsym" << "\"";
387 } else if (auto mapImageNode
= dynamic_cast<map_image
*>(node
.get())) {
388 sstr
<< "{\"name\": \"map_image(" << mapImageNode
->path
<< ")\", \"cat\": \"" << "map_image" << "\"";
389 } else if (auto sigNode
= dynamic_cast<attach_signature
*>(node
.get())) {
390 sstr
<< "{\"name\": \"" << "attach_codesignature" << "\", \"cat\": \"" << "attach_codesignature" << "\"";
391 } else if (auto buildClosureNode
= dynamic_cast<build_closure
*>(node
.get())) {
392 sstr
<< "{\"name\": \"" << "build_closure" << "\", \"cat\": \"" << "build_closure" << "\"";
393 } else if (auto launchNode
= dynamic_cast<app_launch
*>(node
.get())) {
394 sstr
<< "{\"name\": \"" << "app_launch" << "\", \"cat\": \"" << "app_launch" << "\"";
395 } else if (auto initNode
= dynamic_cast<static_init
*>(node
.get())) {
396 sstr
<< "{\"name\": \"" << "static_init" << "\", \"cat\": \"" << "static_init" << "\"";
397 } else if (auto initNode
= dynamic_cast<apply_fixups
*>(node
.get())) {
398 sstr
<< "{\"name\": \"" << "apply_fixups" << "\", \"cat\": \"" << "apply_fixups" << "\"";
399 } else if (auto dlcloseNode
= dynamic_cast<dlclose
*>(node
.get())) {
400 sstr
<< "{\"name\": \"" << "dlclose" << "\", \"cat\": \"" << "dlclose" << "\"";
401 } else if (auto dladdrNode
= dynamic_cast<dladdr
*>(node
.get())) {
402 sstr
<< "{\"name\": \"" << "dladdr" << "\", \"cat\": \"" << "dladdr" << "\"";
403 } else if (auto addImageNode
= dynamic_cast<add_image_callback
*>(node
.get())) {
404 sstr
<< "{\"name\": \"" << "add_image" << "\", \"cat\": \"" << "add_image" << "\"";
405 } else if (auto removeImageNode
= dynamic_cast<remove_image_callback
*>(node
.get())) {
406 sstr
<< "{\"name\": \"" << "remove_image" << "\", \"cat\": \"" << "remove_image" << "\"";
407 } else if (auto objcInitNode
= dynamic_cast<objc_image_init
*>(node
.get())) {
408 sstr
<< "{\"name\": \"" << "objc_init" << "\", \"cat\": \"" << "objc_init" << "\"";
409 } else if (auto objcMapNode
= dynamic_cast<objc_images_map
*>(node
.get())) {
410 sstr
<< "{\"name\": \"" << "objc_map" << "\", \"cat\": \"" << "objc_map" << "\"";
412 sstr
<< "{\"name\": \"" << "unknown" << "\", \"cat\": \"" << node
->eventCode() << "\"";
415 sstr
<< ", \"ph\": \"B\", \"pid\": " << _pid
<< ", \"tid\": " << _threadid
<< ", \"ts\": " << mach_to_nano(node
->startTimestamp()) << "},";
417 sstr
<< ", \"ph\": \"E\", \"pid\": " << _pid
<< ", \"tid\": " << _threadid
<< ", \"ts\": " << mach_to_nano(node
->endTimestamp()) << "}";
422 emitEventInfo(false);
424 if (!node
->children().empty()) {
425 for (const auto& child
: node
->children()) {
427 outputTracingJSON(child
, sstr
);
432 const std::vector
<std::shared_ptr
<event_pair
>>& rootEvents() const { return _rootEvents
; }
436 void output(std::shared_ptr
<event_pair
> root
) {
437 std::ostringstream ostream
;
439 ostream
<< "{\"command\":\"" << _commandName
<< "\",\"pid\":\"" << _pid
<< "\",\"thread\":\"";
440 ostream
<< _threadid
<< "\", \"event\":";
441 outputJSON(root
, ostream
);
442 ostream
<< "}" << std::endl
;
443 } else if (JSON_Tracing_flag
) {
444 _rootEvents
.push_back(root
);
446 outputConsole(root
, columns
, ostream
, 0);
448 std::cout
<< ostream
.str();
453 struct dlopen
: event_pair
{
454 dlopen(ktrace_event_t E
) : event_pair(E
), path(stringForID(E
->arg2
)), flags((int)E
->arg3
) {}
455 std::string
flagString() {
456 std::vector
<std::string
> flagStrs
;
457 uint64_t flagCheck
= 0;
458 std::string flagString
;
460 if (flags
& RTLD_LAZY
) {
461 flagStrs
.push_back("RTLD_LAZY");
462 flagCheck
|= RTLD_LAZY
;
464 if (flags
& RTLD_NOW
) {
465 flagStrs
.push_back("RTLD_NOW");
466 flagCheck
|= RTLD_NOW
;
468 if (flags
& RTLD_LOCAL
) {
469 flagStrs
.push_back("RTLD_LOCAL");
470 flagCheck
|= RTLD_LOCAL
;
472 if (flags
& RTLD_GLOBAL
) {
473 flagStrs
.push_back("RTLD_GLOBAL");
474 flagCheck
|= RTLD_GLOBAL
;
476 if (flags
& RTLD_NOLOAD
) {
477 flagStrs
.push_back("RTLD_NOLOAD");
478 flagCheck
|= RTLD_NOLOAD
;
480 if (flags
& RTLD_NODELETE
) {
481 flagStrs
.push_back("RTLD_NODELETE");
482 flagCheck
|= RTLD_NODELETE
;
484 if (flags
& RTLD_FIRST
) {
485 flagStrs
.push_back("RTLD_FIRST");
486 flagCheck
|= RTLD_FIRST
;
489 if (flagCheck
== flags
) {
490 for (auto& flagStr
: flagStrs
) {
491 if (!flagString
.empty()) {
494 flagString
+= flagStr
;
505 struct dlsym
: event_pair
{
506 dlsym(ktrace_event_t E
) : event_pair(E
), handle(E
->arg2
), symbol(stringForID(E
->arg3
)) {}
512 struct dladdr
: event_pair
{
513 dladdr(ktrace_event_t E
) : event_pair(E
), address(E
->arg2
), imageAddress(0), symbolAddress(0) {}
515 uint64_t imageAddress
;
516 uint64_t symbolAddress
;
520 struct dlclose
: event_pair
{
521 dlclose(ktrace_event_t E
) : event_pair(E
), handle(E
->arg2
) {}
526 struct app_launch
: event_pair
{
527 app_launch(ktrace_event_t E
) : event_pair(E
), address(E
->arg2
) {}
530 std::vector
<event_pair
*> _children
;
533 struct static_init
: event_pair
{
534 static_init(ktrace_event_t E
) : event_pair(E
), libraryAddress(E
->arg2
), funcAddress(E
->arg3
) {}
535 uint64_t libraryAddress
;
536 uint64_t funcAddress
;
539 struct map_image
: event_pair
{
540 map_image(ktrace_event_t E
) : event_pair(E
), path(stringForID(E
->arg2
)) {}
545 struct apply_fixups
: event_pair
{
546 apply_fixups(ktrace_event_t E
) : event_pair(E
) {}
549 struct attach_signature
: event_pair
{
550 attach_signature(ktrace_event_t E
) : event_pair(E
) {}
553 struct build_closure
: event_pair
{
554 build_closure(ktrace_event_t E
) : event_pair(E
) {}
557 struct add_image_callback
: event_pair
{
558 add_image_callback(ktrace_event_t E
) : event_pair(E
), libraryAddress(E
->arg2
), funcAddress(E
->arg3
) {}
559 uint64_t libraryAddress
;
560 uint64_t funcAddress
;
563 struct remove_image_callback
: event_pair
{
564 remove_image_callback(ktrace_event_t E
) : event_pair(E
), libraryAddress(E
->arg2
), funcAddress(E
->arg3
) {}
565 uint64_t libraryAddress
;
566 uint64_t funcAddress
;
570 struct objc_image_init
: event_pair
{
571 objc_image_init(ktrace_event_t E
) : event_pair(E
), libraryAddress(E
->arg2
) {}
572 uint64_t libraryAddress
;
575 struct objc_images_map
: event_pair
{
576 objc_images_map(ktrace_event_t E
) : event_pair(E
) {}
579 std::shared_ptr
<event_pair
> _currentRootEvent
;
580 std::vector
<std::shared_ptr
<event_pair
>> _eventStack
;
581 std::vector
<std::shared_ptr
<event_pair
>> _rootEvents
;
584 struct OutputManager
{
585 std::map
<unsigned long, std::unique_ptr
<output_renderer
>> sOutputRenders
;
588 if (JSON_Tracing_flag
) {
589 std::ostringstream ostream
;
590 ostream
<< "{\"displayTimeUnit\":\"ns\"";
591 ostream
<< ", \"traceEvents\": [";
592 bool firstEvent
= true;
593 for (const auto& renderer
: sOutputRenders
) {
594 for (const auto& root
: renderer
.second
->rootEvents()) {
599 renderer
.second
->outputTracingJSON(root
, ostream
);
603 ostream
<< "}" << std::endl
;
604 std::cout
<< ostream
.str();
611 static OutputManager sOutputManager
;
614 setup_ktrace_callbacks(void)
616 ktrace_events_single(s
, TRACEDBG_CODE(DBG_TRACE_STRING
, TRACE_STRING_GLOBAL
), ^(ktrace_event_t event
){
617 char argChars
[33] = {0};
618 memset(&argChars
[0], 0, 33);
619 if ((event
->debugid
& DBG_FUNC_START
) == DBG_FUNC_START
) {
620 uint64_t str_id
= event
->arg2
;
621 if (((event
->debugid
& DBG_FUNC_END
) == DBG_FUNC_END
)
623 auto i
= gActiveStrings
.find(str_id
);
624 if (i
!= gActiveStrings
.end()) {
625 gActiveStrings
.erase(i
);
628 *((uint64_t*)&argChars
[0]) = event
->arg3
;
629 *((uint64_t*)&argChars
[8]) = event
->arg4
;
630 gActiveStringIDs
.insert(std::make_pair(event
->threadid
, str_id
));
631 gActiveStrings
.insert(std::make_pair(str_id
, argChars
));
633 // Not a start, so lets grab our data
634 *((uint64_t*)&argChars
[0]) = event
->arg1
;
635 *((uint64_t*)&argChars
[8]) = event
->arg2
;
636 *((uint64_t*)&argChars
[16]) = event
->arg3
;
637 *((uint64_t*)&argChars
[24]) = event
->arg4
;
639 auto i
= gActiveStringIDs
.find(event
->threadid
);
640 if (i
!= gActiveStringIDs
.end()) {
641 auto j
= gActiveStrings
.find(i
->second
);
642 if (j
!= gActiveStrings
.end()) {
643 j
->second
+= argChars
;
648 if ((event
->debugid
& DBG_FUNC_END
) == DBG_FUNC_END
) {
649 auto i
= gActiveStringIDs
.find(event
->threadid
);
650 if (i
!= gActiveStringIDs
.end()) {
651 gActiveStringIDs
.erase(i
);
656 // Event though our events are paired, we process them individually so we can
657 // render nested events
658 ktrace_events_range(s
, KDBG_EVENTID(DBG_DYLD
, DBG_DYLD_INTERNAL_SUBCLASS
, 0), KDBG_EVENTID(DBG_DYLD
, DBG_DYLD_API_SUBCLASS
+1, 0), ^(ktrace_event_t event
){
659 assert((event
->debugid
& KDBG_FUNC_MASK
) != 0);
660 auto i
= sOutputManager
.sOutputRenders
.find(event
->threadid
);
661 if (i
== sOutputManager
.sOutputRenders
.end()) {
662 sOutputManager
.sOutputRenders
.emplace(std::make_pair(event
->threadid
, std::make_unique
<output_renderer
>(s
, event
)));
663 i
= sOutputManager
.sOutputRenders
.find(event
->threadid
);
665 i
->second
->recordEvent(event
);
666 if (i
->second
->empty()) {
667 sOutputManager
.sOutputRenders
.erase(i
);
673 main(int argc
, char *argv
[])
677 bool exclude_pids
= false;
678 uint64_t time_limit_ns
= 0;
682 s
= ktrace_session_create();
685 while ((ch
= getopt(argc
, argv
, "jJeR:t:")) != -1) {
691 JSON_Tracing_flag
= true;
697 time_limit_ns
= (uint64_t)(NSEC_PER_SEC
* atof(optarg
));
698 if (time_limit_ns
== 0) {
699 fprintf(stderr
, "ERROR: could not set time limit to %s\n",
706 rv
= ktrace_set_file(s
, optarg
);
708 fprintf(stderr
, "ERROR: reading trace from '%s' failed (%s)\n", optarg
, strerror(errno
));
720 if (time_limit_ns
> 0) {
722 fprintf(stderr
, "NOTE: time limit ignored when a raw file is specified\n");
724 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, time_limit_ns
),
725 dispatch_get_global_queue(QOS_CLASS_USER_INITIATED
, 0),
733 if (geteuid() != 0) {
734 fprintf(stderr
, "'dyld_usage' must be run as root...\n");
739 * ktrace can't both *in*clude and *ex*clude pids, so: if we are
740 * already excluding pids, or if we are not explicitly including
741 * or excluding any pids, then exclude the defaults.
743 * if on the other hand we are explicitly including pids, we'll
744 * filter the defaults out naturally.
746 if (exclude_pids
|| argc
== 0) {
747 ktrace_exclude_process(s
, "dyld_usage");
748 ktrace_exclude_process(s
, "Terminal");
749 ktrace_exclude_process(s
, "telnetd");
750 ktrace_exclude_process(s
, "telnet");
751 ktrace_exclude_process(s
, "sshd");
752 ktrace_exclude_process(s
, "rlogind");
753 ktrace_exclude_process(s
, "tcsh");
754 ktrace_exclude_process(s
, "csh");
755 ktrace_exclude_process(s
, "sh");
756 ktrace_exclude_process(s
, "zsh");
757 #if TARGET_OS_EMBEDDED
758 ktrace_exclude_process(s
, "dropbear");
759 #endif /* TARGET_OS_EMBEDDED */
764 * Process the list of specified pids, and in/exclude them as
773 pid
= (pid_t
)strtoul(name
, &endptr
, 10);
775 if (*name
!= '\0' && *endptr
== '\0') {
777 rv
= ktrace_exclude_pid(s
, pid
);
780 rv
= ktrace_filter_pid(s
, pid
);
784 rv
= ktrace_exclude_process(s
, name
);
786 if (strcmp(name
, "kernel_task"))
787 rv
= ktrace_filter_process(s
, name
);
792 fprintf(stderr
, "ERROR: cannot both include and exclude simultaneously\n");
801 /* provides SIGINT, SIGHUP, SIGPIPE, SIGTERM handlers */
802 ktrace_set_signal_handler(s
);
803 ktrace_set_completion_handler(s
, ^{
804 sOutputManager
.flush();
808 signal(SIGWINCH
, SIG_IGN
);
809 sigwinch_source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL
, SIGWINCH
, 0, dispatch_get_main_queue());
810 dispatch_source_set_event_handler(sigwinch_source
, ^{
813 dispatch_activate(sigwinch_source
);
815 setup_ktrace_callbacks();
817 ktrace_set_dropped_events_handler(s
, ^{
818 fprintf(stderr
, "dyld_usage: buffer overrun, events generated too quickly\n");
820 /* clear any state that is now potentially invalid */
823 ktrace_set_execnames_enabled(s
, KTRACE_FEATURE_LAZY
);
824 ktrace_set_vnode_paths_enabled(s
, false);
825 /* no need to symbolicate addresses */
826 ktrace_set_uuid_map_enabled(s
, KTRACE_FEATURE_DISABLED
);
828 rv
= ktrace_start(s
, dispatch_get_main_queue());
831 perror("ktrace_start");