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);
118 std::string
safeStringFromCString(const char *str
) {
125 struct output_renderer
{
126 output_renderer(ktrace_session_t S
, ktrace_event_t E
) :
127 _commandName(safeStringFromCString(ktrace_get_execname_for_thread(s
, E
->threadid
))),
128 _threadid((unsigned long)(E
->threadid
)), _pid(ktrace_get_pid_for_thread(s
, E
->threadid
)) {}
129 void recordEvent(ktrace_event_t event
) {
130 uint32_t code
= event
->debugid
& KDBG_EVENTID_MASK
;
131 if (event
->debugid
& DBG_FUNC_START
) {
133 case DBG_DYLD_TIMING_DLOPEN
: enqueueEvent
<dlopen
>(event
, true); break;
134 case DBG_DYLD_TIMING_DLOPEN_PREFLIGHT
: enqueueEvent
<dlopen_preflight
>(event
, true); break;
135 case DBG_DYLD_TIMING_LAUNCH_EXECUTABLE
: enqueueEvent
<app_launch
>(event
, true); break;
136 case DBG_DYLD_TIMING_DLSYM
: enqueueEvent
<dlsym
>(event
, true); break;
137 case DBG_DYLD_TIMING_STATIC_INITIALIZER
: enqueueEvent
<static_init
>(event
, false); break;
138 case DBG_DYLD_TIMING_MAP_IMAGE
: enqueueEvent
<map_image
>(event
, false); break;
139 case DBG_DYLD_TIMING_APPLY_FIXUPS
: enqueueEvent
<apply_fixups
>(event
, false); break;
140 case DBG_DYLD_TIMING_ATTACH_CODESIGNATURE
: enqueueEvent
<attach_signature
>(event
, false); break;
141 case DBG_DYLD_TIMING_BUILD_CLOSURE
: enqueueEvent
<build_closure
>(event
, false); break;
142 case DBG_DYLD_TIMING_DLADDR
: enqueueEvent
<dladdr
>(event
, true); break;
143 case DBG_DYLD_TIMING_DLCLOSE
: enqueueEvent
<dlclose
>(event
, true); break;
144 case DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
: enqueueEvent
<add_image_callback
>(event
, false); break;
145 case DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE
: enqueueEvent
<remove_image_callback
>(event
, false); break;
146 case DBG_DYLD_TIMING_OBJC_INIT
: enqueueEvent
<objc_image_init
>(event
, false); break;
147 case DBG_DYLD_TIMING_OBJC_MAP
: enqueueEvent
<objc_images_map
>(event
, false); break;
151 case DBG_DYLD_TIMING_DLOPEN
: dequeueEvent
<dlopen
>(event
, [&](dlopen
* endEvent
){
152 endEvent
->result
= event
->arg2
;
154 case DBG_DYLD_TIMING_DLOPEN_PREFLIGHT
: dequeueEvent
<dlopen_preflight
>(event
, [&](dlopen_preflight
* endEvent
){
155 endEvent
->result
= event
->arg2
;
157 case DBG_DYLD_TIMING_LAUNCH_EXECUTABLE
: dequeueEvent
<app_launch
>(event
, [&](app_launch
* endEvent
){
158 endEvent
->launchMode
= event
->arg4
;
160 case DBG_DYLD_TIMING_DLSYM
: dequeueEvent
<dlsym
>(event
, [&](dlsym
* endEvent
){
161 endEvent
->result
= event
->arg2
;
163 case DBG_DYLD_TIMING_STATIC_INITIALIZER
: dequeueEvent
<static_init
>(event
, [&](static_init
* endEvent
){}); break;
164 case DBG_DYLD_TIMING_MAP_IMAGE
: dequeueEvent
<map_image
>(event
, [&](map_image
* endEvent
){
165 endEvent
->result
= event
->arg2
;
167 case DBG_DYLD_TIMING_APPLY_FIXUPS
: dequeueEvent
<apply_fixups
>(event
, [&](apply_fixups
* endEvent
){}); break;
168 case DBG_DYLD_TIMING_ATTACH_CODESIGNATURE
: dequeueEvent
<attach_signature
>(event
, [&](attach_signature
* endEvent
){}); break;
169 case DBG_DYLD_TIMING_BUILD_CLOSURE
: dequeueEvent
<build_closure
>(event
, [&](build_closure
* endEvent
){
170 endEvent
->closureBuildState
= event
->arg2
;
172 case DBG_DYLD_TIMING_DLCLOSE
: dequeueEvent
<dlclose
>(event
, [&](dlclose
* endEvent
){
173 endEvent
->result
= (int)event
->arg2
;
175 case DBG_DYLD_TIMING_DLADDR
: dequeueEvent
<dladdr
>(event
, [&](dladdr
* endEvent
){
176 endEvent
->result
= (int)event
->arg2
;
177 endEvent
->imageAddress
= (int)event
->arg3
;
178 endEvent
->symbolAddress
= (int)event
->arg4
;
180 case DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE
: dequeueEvent
<add_image_callback
>(event
, [&](add_image_callback
* endEvent
){}); break;
181 case DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE
: dequeueEvent
<remove_image_callback
>(event
, [&](remove_image_callback
* endEvent
){}); break;
182 case DBG_DYLD_TIMING_OBJC_INIT
: dequeueEvent
<objc_image_init
>(event
, [&](objc_image_init
* endEvent
){}); break;
183 case DBG_DYLD_TIMING_OBJC_MAP
: dequeueEvent
<objc_images_map
>(event
, [&](objc_images_map
* endEvent
){}); break;
188 bool empty() { return !_currentRootEvent
&& _eventStack
.empty() && _rootEvents
.empty(); }
191 void enqueueEvent(ktrace_event_t event
, bool rootEvent
) {
192 auto sharedEvent
= std::make_shared
<T
>(event
);
193 if (!_currentRootEvent
) {
194 if (!rootEvent
) return;
195 assert(_eventStack
.empty());
196 _currentRootEvent
= sharedEvent
;
198 sharedEvent
->setDepth(_eventStack
.size());
199 _eventStack
.back()->addChild(sharedEvent
);
201 _eventStack
.push_back(sharedEvent
);
205 void dequeueEvent(ktrace_event_t event
, std::function
<void(T
*)> lambda
) {
206 if (_eventStack
.empty()) return;
207 auto currentEvent
= dynamic_cast<T
*>(_eventStack
.back().get());
208 currentEvent
->setEndEvent(event
);
209 lambda(currentEvent
);
210 _eventStack
.pop_back();
211 if (_currentRootEvent
&& _eventStack
.empty()) {
212 output(_currentRootEvent
);
213 _currentRootEvent
= nullptr;
218 event_pair(ktrace_event_t E
) : _startTime(E
->timestamp
), _walltime(E
->walltime
), _threadid((unsigned long)(E
->threadid
)), _depth(0),
219 _eventCode(KDBG_EXTRACT_CODE(E
->debugid
)) {};
220 virtual ~event_pair(){}
221 std::vector
<std::shared_ptr
<event_pair
>>& children() { return _children
; }
222 void setDepth(uint64_t D
) { _depth
= D
; }
223 uint64_t depth() { return _depth
; }
224 struct timeval
walltime() { return _walltime
; };
225 uint64_t startTimestamp() { return _startTime
; };
226 uint64_t endTimestamp() { return _endTime
; }
227 unsigned long threadid() { return _threadid
; }
228 void addChild(std::shared_ptr
<event_pair
> child
) {_children
.push_back(child
);}
229 void setEndEvent(ktrace_event_t E
) { _endTime
= E
->timestamp
; }
230 uint16_t eventCode() const { return _eventCode
; }
232 std::vector
<std::shared_ptr
<event_pair
>> _children
;
236 unsigned long _threadid
;
238 struct timeval _walltime
;
241 time_t _lastTimeWallSeconds
= -1;
242 std::string _timestampStr
;
243 std::string _commandName
;
245 unsigned long _threadid
;
247 std::string
timestamp(std::shared_ptr
<event_pair
> event
, bool extended
) {
248 std::ostringstream result
;
249 struct timeval now_walltime
= event
->walltime();
250 assert(now_walltime
.tv_sec
|| now_walltime
.tv_usec
);
252 /* try and reuse the timestamp string */
253 if (_lastTimeWallSeconds
!= now_walltime
.tv_sec
) {
255 (void)strftime(timestamp
, sizeof (timestamp
), "%H:%M:%S", localtime(&now_walltime
.tv_sec
));
256 _lastTimeWallSeconds
= now_walltime
.tv_sec
;
257 _timestampStr
= timestamp
;
259 result
<< _timestampStr
;
262 result
<< "." << std::setw(6) << std::setfill('0') << std::to_string(now_walltime
.tv_usec
);
267 std::string
process(std::shared_ptr
<event_pair
> event
, bool extended
) {
269 std::ostringstream result
;
270 result
<< _commandName
<< "." << std::to_string(event
->threadid());
273 std::string result
= _commandName
;
274 result
.resize(12, ' ');
279 std::string
duration(std::shared_ptr
<event_pair
> event
) {
280 std::ostringstream result
;
281 uint64_t usecs
= (mach_to_nano(event
->endTimestamp() - event
->startTimestamp()) + (NSEC_PER_USEC
- 1)) / NSEC_PER_USEC
;
282 uint64_t secs
= usecs
/ USEC_PER_SEC
;
283 usecs
-= secs
* USEC_PER_SEC
;
284 result
<< secs
<< "." << std::setw(6) << std::setfill('0') << usecs
;
289 void outputConsole(std::shared_ptr
<event_pair
> node
, uint64_t width
, std::ostringstream
& sstr
, uint64_t depth
) {
290 std::ostringstream line
;
291 bool extended
= false;
292 if (auto dlopenNode
= dynamic_cast<dlopen
*>(node
.get())) {
293 line
<< "dlopen(\"" << dlopenNode
->path
<< "\", " << dlopenNode
->flagString() << ") -> 0x" << dlopenNode
->result
;
294 } else if (auto dlopenPreflightNode
= dynamic_cast<dlopen_preflight
*>(node
.get())) {
295 line
<< "dlopen_preflight(\"" << dlopenPreflightNode
->path
<< ") -> 0x" << dlopenPreflightNode
->result
;
296 } else if (auto dlsymNode
= dynamic_cast<dlsym
*>(node
.get())) {
297 line
<< "dlsym(0x" << std::hex
<< dlsymNode
->handle
<< ", \"" << dlsymNode
->symbol
<< "\") -> " << dlsymNode
->result
;
298 } else if (auto mapImageNode
= dynamic_cast<map_image
*>(node
.get())) {
299 line
<< "map file \"" << mapImageNode
->path
<< "\" -> 0x" << std::hex
<< mapImageNode
->result
;
300 } else if (auto sigNode
= dynamic_cast<attach_signature
*>(node
.get())) {
301 line
<< "attach codesignature";
302 } else if (auto buildClosureNode
= dynamic_cast<build_closure
*>(node
.get())) {
303 line
<< "build closure -> " << buildClosureNode
->buildStateString();
304 } else if (auto launchNode
= dynamic_cast<app_launch
*>(node
.get())) {
305 line
<< "app launch (dyld" << std::dec
<< launchNode
->launchMode
<< ") -> 0x" << std::hex
<< launchNode
->address
;
306 } else if (auto initNode
= dynamic_cast<static_init
*>(node
.get())) {
307 line
<< "run static initializer 0x" << std::hex
<< initNode
->funcAddress
;
308 } else if (auto initNode
= dynamic_cast<apply_fixups
*>(node
.get())) {
309 line
<< "apply fixups";
310 } else if (auto dlcloseNode
= dynamic_cast<dlclose
*>(node
.get())) {
311 line
<< "dlclose(0x" << std::hex
<< dlcloseNode
->handle
<< ") -> " << dlcloseNode
->result
;
312 } else if (auto dladdrNode
= dynamic_cast<dladdr
*>(node
.get())) {
313 line
<< "dladdr(0x" << dladdrNode
->address
<< ") -> image: 0x" << std::hex
<< dladdrNode
->imageAddress
;
314 line
<< ", symbol: 0x" << dladdrNode
->symbolAddress
;
315 } else if (auto addImageNode
= dynamic_cast<add_image_callback
*>(node
.get())) {
316 line
<< std::hex
<< "add image callback(0x" << addImageNode
->funcAddress
<< ") for image 0x" << addImageNode
->libraryAddress
;
317 } else if (auto removeImageNode
= dynamic_cast<remove_image_callback
*>(node
.get())) {
318 line
<< std::hex
<< "remove image callback(0x" << removeImageNode
->funcAddress
<< ") for image 0x" << removeImageNode
->libraryAddress
;
319 } else if (auto objcInitNode
= dynamic_cast<objc_image_init
*>(node
.get())) {
320 line
<< std::hex
<< "objC init image(0x" << objcInitNode
->libraryAddress
<< ")";
321 } else if (auto objcMapNode
= dynamic_cast<objc_images_map
*>(node
.get())) {
322 line
<< std::hex
<< "objC map images callback";
325 if (width
> MAXCOLS
) {
329 std::string timestampStr
= timestamp(node
, extended
);
330 std::string lineStr
= line
.str();
331 std::string commandStr
= process(node
, extended
);
332 std::string durationStr
= duration(node
);
333 size_t lineMax
= (size_t)width
- (timestampStr
.length() + commandStr
.length() + durationStr
.length() + 2*(size_t)depth
+ 3);
334 lineStr
.resize(lineMax
, ' ');
336 sstr
<< timestampStr
<< " ";
337 std::fill_n(std::ostream_iterator
<char>(sstr
), 2*depth
, ' ');
338 sstr
<< lineStr
<< " " << durationStr
<< " " << commandStr
<< std::endl
;
340 for (const auto& child
: node
->children()) {
341 outputConsole(child
, width
, sstr
, depth
+1);
345 void outputJSON(std::shared_ptr
<event_pair
> node
, std::ostringstream
& sstr
) {
346 if (auto dlopenNode
= dynamic_cast<dlopen
*>(node
.get())) {
348 sstr
<< "{\"type\":\"dlopen\",\"path\":\"" << dlopenNode
->path
<< "\",\"flags\":\"0x" << dlopenNode
->flags
<< "\"";
349 sstr
<< ",\"result\":\"" << dlopenNode
->result
<< "\"";
350 } else if (auto dlopenPreflightNode
= dynamic_cast<dlopen_preflight
*>(node
.get())) {
352 sstr
<< "{\"type\":\"dlopen_preflight\",\"path\":\"" << dlopenPreflightNode
->path
<< "\"";
353 sstr
<< ",\"result\":\"" << dlopenPreflightNode
->result
<< "\"";
354 } else if (auto dlsymNode
= dynamic_cast<dlsym
*>(node
.get())) {
355 sstr
<< std::hex
<< "{\"type\":\"dlsym\",\"symbol\":\"" << dlsymNode
->symbol
<< "\",\"handle\":\"0x";
356 sstr
<< dlsymNode
->handle
<< "\",\"result\":\"0x" << dlsymNode
->result
<< "\"";
357 } else if (auto mapImageNode
= dynamic_cast<map_image
*>(node
.get())) {
359 sstr
<< "{\"type\":\"map_image\",\"path\":\"" << mapImageNode
->path
<< "\",\"result\":\"0x" << mapImageNode
->result
<< "\"";
360 } else if (auto sigNode
= dynamic_cast<attach_signature
*>(node
.get())) {
361 sstr
<< "{\"type\":\"attach_codesignature\"";
362 } else if (auto buildClosureNode
= dynamic_cast<build_closure
*>(node
.get())) {
363 sstr
<< "{\"type\":\"build_closure\", \"state\":\"" << buildClosureNode
->buildStateString() << "\"";
364 } else if (auto launchNode
= dynamic_cast<app_launch
*>(node
.get())) {
366 sstr
<< "{\"type\":\"app_launch\",\"address\":\"0x";
367 sstr
<< launchNode
->address
<< "\",\"mode\":" << launchNode
->launchMode
<< "";
368 } else if (auto initNode
= dynamic_cast<static_init
*>(node
.get())) {
370 sstr
<< "{\"type\":\"static_init\",\"image_address\":\"0x" << initNode
->libraryAddress
;
371 sstr
<< "\",\"function_address\":\"0x" << initNode
->funcAddress
<< "\"";
372 } else if (auto initNode
= dynamic_cast<apply_fixups
*>(node
.get())) {
373 sstr
<< "{\"type\":\"apply_fixups\"";
374 } else if (auto dlcloseNode
= dynamic_cast<dlclose
*>(node
.get())) {
375 sstr
<< std::hex
<< "{\"type\":\"dlclose\",\"handle\":\"0x";
376 sstr
<< dlcloseNode
->handle
<< "\",\"result\":\"0x" << dlcloseNode
->result
<< "\"";
377 } else if (auto dladdrNode
= dynamic_cast<dladdr
*>(node
.get())) {
378 sstr
<< std::hex
<< "{\"type\":\"dladdr\",\"address\":\"0x" << dladdrNode
->address
<< "\",\"result\":\"0x";
379 sstr
<< dladdrNode
->result
<< "\",\"symbol_address\":\"0x" << dladdrNode
->symbolAddress
;
380 sstr
<< "\",\"image_address\":\"0x" << dladdrNode
->imageAddress
<< "\"";
382 sstr
<< "{\"type\":\"unknown\"";
385 if (!node
->children().empty()) {
386 bool firstChild
= true;
387 sstr
<< ",\"children\":[";
388 for (const auto& child
: node
->children()) {
393 outputJSON(child
, sstr
);
397 sstr
<< std::dec
<< ",\"start_nano\":\"" << mach_to_nano(node
->startTimestamp());
398 sstr
<< "\",\"end_nano\":\"" << mach_to_nano(node
->endTimestamp()) << "\"}";
401 void outputTracingJSON(std::shared_ptr
<event_pair
> node
, std::ostringstream
& sstr
) {
402 auto emitEventInfo
= [&](bool isStart
) {
403 if (auto dlopenNode
= dynamic_cast<dlopen
*>(node
.get())) {
404 sstr
<< "{\"name\": \"dlopen(" << dlopenNode
->path
<< ")\", \"cat\": \"" << "dlopen" << "\"";
405 } else if (auto dlopenPreflightNode
= dynamic_cast<dlopen_preflight
*>(node
.get())) {
406 sstr
<< "{\"name\": \"dlopen_preflight(" << dlopenPreflightNode
->path
<< ")\", \"cat\": \"" << "dlopen_preflight" << "\"";
407 } else if (auto dlsymNode
= dynamic_cast<dlsym
*>(node
.get())) {
408 sstr
<< "{\"name\": \"dlsym(" << dlsymNode
->symbol
<< ")\", \"cat\": \"" << "dlsym" << "\"";
409 } else if (auto mapImageNode
= dynamic_cast<map_image
*>(node
.get())) {
410 sstr
<< "{\"name\": \"map_image(" << mapImageNode
->path
<< ")\", \"cat\": \"" << "map_image" << "\"";
411 } else if (auto sigNode
= dynamic_cast<attach_signature
*>(node
.get())) {
412 sstr
<< "{\"name\": \"" << "attach_codesignature" << "\", \"cat\": \"" << "attach_codesignature" << "\"";
413 } else if (auto buildClosureNode
= dynamic_cast<build_closure
*>(node
.get())) {
414 sstr
<< "{\"name\": \"" << "build_closure" << "\", \"cat\": \"" << "build_closure" << "\"";
415 } else if (auto launchNode
= dynamic_cast<app_launch
*>(node
.get())) {
416 sstr
<< "{\"name\": \"" << "app_launch" << "\", \"cat\": \"" << "app_launch" << "\"";
417 } else if (auto initNode
= dynamic_cast<static_init
*>(node
.get())) {
418 sstr
<< "{\"name\": \"" << "static_init" << "\", \"cat\": \"" << "static_init" << "\"";
419 } else if (auto initNode
= dynamic_cast<apply_fixups
*>(node
.get())) {
420 sstr
<< "{\"name\": \"" << "apply_fixups" << "\", \"cat\": \"" << "apply_fixups" << "\"";
421 } else if (auto dlcloseNode
= dynamic_cast<dlclose
*>(node
.get())) {
422 sstr
<< "{\"name\": \"" << "dlclose" << "\", \"cat\": \"" << "dlclose" << "\"";
423 } else if (auto dladdrNode
= dynamic_cast<dladdr
*>(node
.get())) {
424 sstr
<< "{\"name\": \"" << "dladdr" << "\", \"cat\": \"" << "dladdr" << "\"";
425 } else if (auto addImageNode
= dynamic_cast<add_image_callback
*>(node
.get())) {
426 sstr
<< "{\"name\": \"" << "add_image" << "\", \"cat\": \"" << "add_image" << "\"";
427 } else if (auto removeImageNode
= dynamic_cast<remove_image_callback
*>(node
.get())) {
428 sstr
<< "{\"name\": \"" << "remove_image" << "\", \"cat\": \"" << "remove_image" << "\"";
429 } else if (auto objcInitNode
= dynamic_cast<objc_image_init
*>(node
.get())) {
430 sstr
<< "{\"name\": \"" << "objc_init" << "\", \"cat\": \"" << "objc_init" << "\"";
431 } else if (auto objcMapNode
= dynamic_cast<objc_images_map
*>(node
.get())) {
432 sstr
<< "{\"name\": \"" << "objc_map" << "\", \"cat\": \"" << "objc_map" << "\"";
434 sstr
<< "{\"name\": \"" << "unknown" << "\", \"cat\": \"" << node
->eventCode() << "\"";
437 sstr
<< ", \"ph\": \"B\", \"pid\": " << _pid
<< ", \"tid\": " << _threadid
<< ", \"ts\": " << mach_to_nano(node
->startTimestamp()) << "},";
439 sstr
<< ", \"ph\": \"E\", \"pid\": " << _pid
<< ", \"tid\": " << _threadid
<< ", \"ts\": " << mach_to_nano(node
->endTimestamp()) << "}";
444 emitEventInfo(false);
446 if (!node
->children().empty()) {
447 for (const auto& child
: node
->children()) {
449 outputTracingJSON(child
, sstr
);
454 const std::vector
<std::shared_ptr
<event_pair
>>& rootEvents() const { return _rootEvents
; }
458 void output(std::shared_ptr
<event_pair
> root
) {
459 std::ostringstream ostream
;
461 ostream
<< "{\"command\":\"" << _commandName
<< "\",\"pid\":\"" << _pid
<< "\",\"thread\":\"";
462 ostream
<< _threadid
<< "\", \"event\":";
463 outputJSON(root
, ostream
);
464 ostream
<< "}" << std::endl
;
465 } else if (JSON_Tracing_flag
) {
466 _rootEvents
.push_back(root
);
468 outputConsole(root
, columns
, ostream
, 0);
470 std::cout
<< ostream
.str();
475 struct dlopen
: event_pair
{
476 dlopen(ktrace_event_t E
) : event_pair(E
), path(stringForID(E
->arg2
)), flags((int)E
->arg3
) {}
477 std::string
flagString() {
478 std::vector
<std::string
> flagStrs
;
479 uint64_t flagCheck
= 0;
480 std::string flagString
;
482 if (flags
& RTLD_LAZY
) {
483 flagStrs
.push_back("RTLD_LAZY");
484 flagCheck
|= RTLD_LAZY
;
486 if (flags
& RTLD_NOW
) {
487 flagStrs
.push_back("RTLD_NOW");
488 flagCheck
|= RTLD_NOW
;
490 if (flags
& RTLD_LOCAL
) {
491 flagStrs
.push_back("RTLD_LOCAL");
492 flagCheck
|= RTLD_LOCAL
;
494 if (flags
& RTLD_GLOBAL
) {
495 flagStrs
.push_back("RTLD_GLOBAL");
496 flagCheck
|= RTLD_GLOBAL
;
498 if (flags
& RTLD_NOLOAD
) {
499 flagStrs
.push_back("RTLD_NOLOAD");
500 flagCheck
|= RTLD_NOLOAD
;
502 if (flags
& RTLD_NODELETE
) {
503 flagStrs
.push_back("RTLD_NODELETE");
504 flagCheck
|= RTLD_NODELETE
;
506 if (flags
& RTLD_FIRST
) {
507 flagStrs
.push_back("RTLD_FIRST");
508 flagCheck
|= RTLD_FIRST
;
511 if (flagCheck
== flags
) {
512 for (auto& flagStr
: flagStrs
) {
513 if (!flagString
.empty()) {
516 flagString
+= flagStr
;
527 struct dlopen_preflight
: event_pair
{
528 dlopen_preflight(ktrace_event_t E
) : event_pair(E
), path(stringForID(E
->arg2
)) {}
533 struct dlsym
: event_pair
{
534 dlsym(ktrace_event_t E
) : event_pair(E
), handle(E
->arg2
), symbol(stringForID(E
->arg3
)) {}
540 struct dladdr
: event_pair
{
541 dladdr(ktrace_event_t E
) : event_pair(E
), address(E
->arg2
), imageAddress(0), symbolAddress(0) {}
543 uint64_t imageAddress
;
544 uint64_t symbolAddress
;
548 struct dlclose
: event_pair
{
549 dlclose(ktrace_event_t E
) : event_pair(E
), handle(E
->arg2
) {}
554 struct app_launch
: event_pair
{
555 app_launch(ktrace_event_t E
) : event_pair(E
), address(E
->arg2
) {}
558 std::vector
<event_pair
*> _children
;
561 struct static_init
: event_pair
{
562 static_init(ktrace_event_t E
) : event_pair(E
), libraryAddress(E
->arg2
), funcAddress(E
->arg3
) {}
563 uint64_t libraryAddress
;
564 uint64_t funcAddress
;
567 struct map_image
: event_pair
{
568 map_image(ktrace_event_t E
) : event_pair(E
), path(stringForID(E
->arg2
)) {}
573 struct apply_fixups
: event_pair
{
574 apply_fixups(ktrace_event_t E
) : event_pair(E
) {}
577 struct attach_signature
: event_pair
{
578 attach_signature(ktrace_event_t E
) : event_pair(E
) {}
581 struct build_closure
: event_pair
{
582 build_closure(ktrace_event_t E
) : event_pair(E
), closureBuildState(0) {}
583 uint64_t closureBuildState
;
585 std::string
buildStateString() const {
586 switch ((dyld3::DyldTimingBuildClosure
)closureBuildState
) {
587 case dyld3::DyldTimingBuildClosure::ClosureBuildFailure
:
588 return "failed to build closure";
589 case dyld3::DyldTimingBuildClosure::LaunchClosure_Built
:
590 return "built launch closure";
591 case dyld3::DyldTimingBuildClosure::DlopenClosure_UsedSharedCacheDylib
:
592 return "used shared cache dylib closure";
593 case dyld3::DyldTimingBuildClosure::DlopenClosure_UsedSharedCacheOther
:
594 return "used shared cache dlopen closure";
595 case dyld3::DyldTimingBuildClosure::DlopenClosure_NoLoad
:
596 return "dlopen was no load";
597 case dyld3::DyldTimingBuildClosure::DlopenClosure_Built
:
598 return "built dlopen closure";
603 struct add_image_callback
: event_pair
{
604 add_image_callback(ktrace_event_t E
) : event_pair(E
), libraryAddress(E
->arg2
), funcAddress(E
->arg3
) {}
605 uint64_t libraryAddress
;
606 uint64_t funcAddress
;
609 struct remove_image_callback
: event_pair
{
610 remove_image_callback(ktrace_event_t E
) : event_pair(E
), libraryAddress(E
->arg2
), funcAddress(E
->arg3
) {}
611 uint64_t libraryAddress
;
612 uint64_t funcAddress
;
616 struct objc_image_init
: event_pair
{
617 objc_image_init(ktrace_event_t E
) : event_pair(E
), libraryAddress(E
->arg2
) {}
618 uint64_t libraryAddress
;
621 struct objc_images_map
: event_pair
{
622 objc_images_map(ktrace_event_t E
) : event_pair(E
) {}
625 std::shared_ptr
<event_pair
> _currentRootEvent
;
626 std::vector
<std::shared_ptr
<event_pair
>> _eventStack
;
627 std::vector
<std::shared_ptr
<event_pair
>> _rootEvents
;
630 struct OutputManager
{
631 std::map
<unsigned long, std::unique_ptr
<output_renderer
>> sOutputRenders
;
634 if (JSON_Tracing_flag
) {
635 std::ostringstream ostream
;
636 ostream
<< "{\"displayTimeUnit\":\"ns\"";
637 ostream
<< ", \"traceEvents\": [";
638 bool firstEvent
= true;
639 for (const auto& renderer
: sOutputRenders
) {
640 for (const auto& root
: renderer
.second
->rootEvents()) {
645 renderer
.second
->outputTracingJSON(root
, ostream
);
649 ostream
<< "}" << std::endl
;
650 std::cout
<< ostream
.str();
657 static OutputManager sOutputManager
;
660 setup_ktrace_callbacks(void)
662 ktrace_events_single(s
, TRACEDBG_CODE(DBG_TRACE_STRING
, TRACE_STRING_GLOBAL
), ^(ktrace_event_t event
){
663 char argChars
[33] = {0};
664 memset(&argChars
[0], 0, 33);
665 if ((event
->debugid
& DBG_FUNC_START
) == DBG_FUNC_START
) {
666 uint64_t str_id
= event
->arg2
;
667 if (((event
->debugid
& DBG_FUNC_END
) == DBG_FUNC_END
)
669 auto i
= gActiveStrings
.find(str_id
);
670 if (i
!= gActiveStrings
.end()) {
671 gActiveStrings
.erase(i
);
674 *((uint64_t*)&argChars
[0]) = event
->arg3
;
675 *((uint64_t*)&argChars
[8]) = event
->arg4
;
676 gActiveStringIDs
.insert(std::make_pair(event
->threadid
, str_id
));
677 gActiveStrings
.insert(std::make_pair(str_id
, argChars
));
679 // Not a start, so lets grab our data
680 *((uint64_t*)&argChars
[0]) = event
->arg1
;
681 *((uint64_t*)&argChars
[8]) = event
->arg2
;
682 *((uint64_t*)&argChars
[16]) = event
->arg3
;
683 *((uint64_t*)&argChars
[24]) = event
->arg4
;
685 auto i
= gActiveStringIDs
.find(event
->threadid
);
686 if (i
!= gActiveStringIDs
.end()) {
687 auto j
= gActiveStrings
.find(i
->second
);
688 if (j
!= gActiveStrings
.end()) {
689 j
->second
+= argChars
;
694 if ((event
->debugid
& DBG_FUNC_END
) == DBG_FUNC_END
) {
695 auto i
= gActiveStringIDs
.find(event
->threadid
);
696 if (i
!= gActiveStringIDs
.end()) {
697 gActiveStringIDs
.erase(i
);
702 // Event though our events are paired, we process them individually so we can
703 // render nested events
704 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
){
705 if ((event
->debugid
& KDBG_FUNC_MASK
) == 0)
707 auto i
= sOutputManager
.sOutputRenders
.find((size_t)(event
->threadid
));
708 if (i
== sOutputManager
.sOutputRenders
.end()) {
709 sOutputManager
.sOutputRenders
.emplace(std::make_pair(event
->threadid
, std::make_unique
<output_renderer
>(s
, event
)));
710 i
= sOutputManager
.sOutputRenders
.find((size_t)(event
->threadid
));
712 i
->second
->recordEvent(event
);
713 if (i
->second
->empty()) {
714 sOutputManager
.sOutputRenders
.erase(i
);
720 main(int argc
, char *argv
[])
724 bool exclude_pids
= false;
725 uint64_t time_limit_ns
= 0;
729 s
= ktrace_session_create();
732 while ((ch
= getopt(argc
, argv
, "jJeR:t:")) != -1) {
738 JSON_Tracing_flag
= true;
744 time_limit_ns
= (uint64_t)(NSEC_PER_SEC
* atof(optarg
));
745 if (time_limit_ns
== 0) {
746 fprintf(stderr
, "ERROR: could not set time limit to %s\n",
753 rv
= ktrace_set_file(s
, optarg
);
755 fprintf(stderr
, "ERROR: reading trace from '%s' failed (%s)\n", optarg
, strerror(errno
));
767 if (time_limit_ns
> 0) {
769 fprintf(stderr
, "NOTE: time limit ignored when a raw file is specified\n");
771 dispatch_after(dispatch_time(DISPATCH_TIME_NOW
, time_limit_ns
),
772 dispatch_get_global_queue(QOS_CLASS_USER_INITIATED
, 0),
780 if (geteuid() != 0) {
781 fprintf(stderr
, "'dyld_usage' must be run as root...\n");
786 * ktrace can't both *in*clude and *ex*clude pids, so: if we are
787 * already excluding pids, or if we are not explicitly including
788 * or excluding any pids, then exclude the defaults.
790 * if on the other hand we are explicitly including pids, we'll
791 * filter the defaults out naturally.
793 if (exclude_pids
|| argc
== 0) {
794 ktrace_exclude_process(s
, "dyld_usage");
795 ktrace_exclude_process(s
, "Terminal");
796 ktrace_exclude_process(s
, "telnetd");
797 ktrace_exclude_process(s
, "telnet");
798 ktrace_exclude_process(s
, "sshd");
799 ktrace_exclude_process(s
, "rlogind");
800 ktrace_exclude_process(s
, "tcsh");
801 ktrace_exclude_process(s
, "csh");
802 ktrace_exclude_process(s
, "sh");
803 ktrace_exclude_process(s
, "zsh");
804 #if TARGET_OS_EMBEDDED
805 ktrace_exclude_process(s
, "dropbear");
806 #endif /* TARGET_OS_EMBEDDED */
811 * Process the list of specified pids, and in/exclude them as
820 pid
= (pid_t
)strtoul(name
, &endptr
, 10);
822 if (*name
!= '\0' && *endptr
== '\0') {
824 rv
= ktrace_exclude_pid(s
, pid
);
827 rv
= ktrace_filter_pid(s
, pid
);
831 rv
= ktrace_exclude_process(s
, name
);
833 if (strcmp(name
, "kernel_task"))
834 rv
= ktrace_filter_process(s
, name
);
839 fprintf(stderr
, "ERROR: cannot both include and exclude simultaneously\n");
848 /* provides SIGINT, SIGHUP, SIGPIPE, SIGTERM handlers */
849 ktrace_set_signal_handler(s
);
850 ktrace_set_completion_handler(s
, ^{
851 sOutputManager
.flush();
855 signal(SIGWINCH
, SIG_IGN
);
856 sigwinch_source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL
, SIGWINCH
, 0, dispatch_get_main_queue());
857 dispatch_source_set_event_handler(sigwinch_source
, ^{
860 dispatch_activate(sigwinch_source
);
862 setup_ktrace_callbacks();
864 ktrace_set_dropped_events_handler(s
, ^{
865 fprintf(stderr
, "dyld_usage: buffer overrun, events generated too quickly\n");
867 /* clear any state that is now potentially invalid */
870 ktrace_set_execnames_enabled(s
, KTRACE_FEATURE_LAZY
);
871 ktrace_set_vnode_paths_enabled(s
, false);
872 /* no need to symbolicate addresses */
873 ktrace_set_uuid_map_enabled(s
, KTRACE_FEATURE_DISABLED
);
875 rv
= ktrace_start(s
, dispatch_get_main_queue());
878 perror("ktrace_start");