dyld-655.1.tar.gz
[apple/dyld.git] / src / dyld_usage.cpp
1 /*
2 * Copyright (c) 2017 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
12 * this file.
13 *
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
20 * under the License."
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <map>
26 #include <set>
27 #include <memory>
28 #include <string>
29 #include <cstring>
30 #include <array>
31 #include <vector>
32 #include <iomanip>
33 #include <sstream>
34 #include <iostream>
35 #include <cinttypes>
36 #include <dlfcn.h>
37 #include <unistd.h>
38 #include <stdint.h>
39 #include <termios.h>
40 #include <sys/ioctl.h>
41 #include <libutil.h>
42 #include <ktrace/session.h>
43 #include <dispatch/dispatch.h>
44 #include <System/sys/kdebug.h>
45
46 #include "Tracing.h"
47
48 #define DBG_FUNC_ALL (DBG_FUNC_START | DBG_FUNC_END)
49
50
51 /*
52 * MAXCOLS controls when extra data kicks in.
53 * MAX_WIDE_MODE_COLS controls -w mode to get even wider data in path.
54 */
55 #define MAXCOLS (132)
56 #define MAXWIDTH (MAXCOLS + 64)
57
58 unsigned int columns = 0;
59 ktrace_session_t s;
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;
65 static void
66 exit_usage(void)
67 {
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");
77
78 exit(1);
79 }
80
81 static void
82 get_screenwidth(void)
83 {
84 struct winsize size;
85
86 columns = MAXCOLS;
87
88 if (isatty(STDOUT_FILENO)) {
89 if (ioctl(1, TIOCGWINSZ, &size) != -1) {
90 columns = size.ws_col;
91
92 if (columns > MAXWIDTH)
93 columns = MAXWIDTH;
94 }
95 }
96 }
97
98 std::map<uint64_t, uint64_t> gActiveStringIDs;
99 std::map<uint64_t, std::string> gActiveStrings;
100
101 const std::string& stringForID(uint64_t id) {
102 static std::string emptyString = "";
103 auto i = gActiveStrings.find(id);
104 if (i == gActiveStrings.end())
105 return emptyString;
106 return i->second;
107 }
108
109 static uint64_t
110 mach_to_nano(uint64_t mach)
111 {
112 uint64_t nanoseconds = 0;
113 assert(ktrace_convert_timestamp_to_nanoseconds(s, mach, &nanoseconds) == 0);
114 return nanoseconds;
115 }
116
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) {
124 switch(code) {
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;
139 }
140 } else {
141 switch(code) {
142 case DBG_DYLD_TIMING_DLOPEN: dequeueEvent<dlopen>(event, [&](dlopen* endEvent){
143 endEvent->result = event->arg2;
144 }); break;
145 case DBG_DYLD_TIMING_LAUNCH_EXECUTABLE: dequeueEvent<app_launch>(event, [&](app_launch* endEvent){
146 endEvent->launchMode = event->arg4;
147 }); break;
148 case DBG_DYLD_TIMING_DLSYM: dequeueEvent<dlsym>(event, [&](dlsym* endEvent){
149 endEvent->result = event->arg2;
150 }); break;
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;
154 }); break;
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;
160 }); break;
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;
165 }); break;
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;
170 }
171 }
172 }
173
174 bool empty() { return !_currentRootEvent && _eventStack.empty() && _rootEvents.empty(); }
175 private:
176 template<typename T>
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;
183 } else {
184 sharedEvent->setDepth(_eventStack.size());
185 _eventStack.back()->addChild(sharedEvent);
186 }
187 _eventStack.push_back(sharedEvent);
188 }
189
190 template<typename T>
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;
200 }
201 }
202
203 struct event_pair {
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; }
217 private:
218 std::vector<std::shared_ptr<event_pair>> _children;
219 uint64_t _startTime;
220 uint64_t _endTime;
221 uint64_t _depth;
222 unsigned long _threadid;
223 uint16_t _eventCode;
224 struct timeval _walltime;
225 };
226
227 time_t _lastTimeWallSeconds = -1;
228 std::string _timestampStr;
229 std::string _commandName;
230 pid_t _pid;
231 unsigned long _threadid;
232
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);
237
238 /* try and reuse the timestamp string */
239 if (_lastTimeWallSeconds != now_walltime.tv_sec) {
240 char timestamp[32];
241 (void)strftime(timestamp, sizeof (timestamp), "%H:%M:%S", localtime(&now_walltime.tv_sec));
242 _lastTimeWallSeconds = now_walltime.tv_sec;
243 _timestampStr = timestamp;
244 }
245 result << _timestampStr;
246
247 if (extended) {
248 result << "." << std::setw(6) << std::setfill(' ') << std::to_string(now_walltime.tv_usec);
249 }
250 return result.str();
251 }
252
253 std::string process(std::shared_ptr<event_pair> event, bool extended) {
254 if (extended) {
255 std::ostringstream result;
256 result << _commandName << "." << std::to_string(event->threadid());
257 return result.str();
258 } else {
259 std::string result = _commandName;
260 result.resize(12, ' ');
261 return result;
262 }
263 }
264
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;
271 return result.str();
272 }
273
274 public:
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";
307 }
308
309 if (width > MAXCOLS) {
310 extended = true;
311 }
312
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, ' ');
319
320 sstr << timestampStr << " ";
321 std::fill_n(std::ostream_iterator<char>(sstr), 2*depth, ' ');
322 sstr << lineStr << " " << durationStr << " " << commandStr << std::endl;
323
324 for (const auto& child : node->children()) {
325 outputConsole(child, width, sstr, depth+1);
326 }
327 }
328
329 void outputJSON(std::shared_ptr<event_pair> node, std::ostringstream& sstr) {
330 if (auto dlopenNode = dynamic_cast<dlopen *>(node.get())) {
331 sstr << std::hex;
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())) {
338 sstr << std::hex;
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())) {
345 sstr << std::hex;
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())) {
349 sstr << std::hex;
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 << "\"";
361 } else {
362 sstr << "{\"type\":\"unknown\"";
363 }
364
365 if (!node->children().empty()) {
366 bool firstChild = true;
367 sstr << ",\"children\":[";
368 for (const auto& child : node->children()) {
369 if (!firstChild) {
370 sstr << ",";
371 }
372 firstChild = false;
373 outputJSON(child, sstr);
374 }
375 sstr << "]";
376 }
377 sstr << std::dec << ",\"start_nano\":\"" << mach_to_nano(node->startTimestamp());
378 sstr << "\",\"end_nano\":\"" << mach_to_nano(node->endTimestamp()) << "\"}";
379 }
380
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" << "\"";
411 } else {
412 sstr << "{\"name\": \"" << "unknown" << "\", \"cat\": \"" << node->eventCode() << "\"";
413 }
414 if (isStart) {
415 sstr << ", \"ph\": \"B\", \"pid\": " << _pid << ", \"tid\": " << _threadid << ", \"ts\": " << mach_to_nano(node->startTimestamp()) << "},";
416 } else {
417 sstr << ", \"ph\": \"E\", \"pid\": " << _pid << ", \"tid\": " << _threadid << ", \"ts\": " << mach_to_nano(node->endTimestamp()) << "}";
418 }
419 };
420
421 emitEventInfo(true);
422 emitEventInfo(false);
423
424 if (!node->children().empty()) {
425 for (const auto& child : node->children()) {
426 sstr << ", ";
427 outputTracingJSON(child, sstr);
428 }
429 }
430 }
431
432 const std::vector<std::shared_ptr<event_pair>>& rootEvents() const { return _rootEvents; }
433
434 private:
435
436 void output(std::shared_ptr<event_pair> root) {
437 std::ostringstream ostream;
438 if (JSON_flag) {
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);
445 } else {
446 outputConsole(root, columns, ostream, 0);
447 }
448 std::cout << ostream.str();
449 if (!RAW_flag)
450 fflush(stdout);
451 }
452
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;
459
460 if (flags & RTLD_LAZY) {
461 flagStrs.push_back("RTLD_LAZY");
462 flagCheck |= RTLD_LAZY;
463 }
464 if (flags & RTLD_NOW) {
465 flagStrs.push_back("RTLD_NOW");
466 flagCheck |= RTLD_NOW;
467 }
468 if (flags & RTLD_LOCAL) {
469 flagStrs.push_back("RTLD_LOCAL");
470 flagCheck |= RTLD_LOCAL;
471 }
472 if (flags & RTLD_GLOBAL) {
473 flagStrs.push_back("RTLD_GLOBAL");
474 flagCheck |= RTLD_GLOBAL;
475 }
476 if (flags & RTLD_NOLOAD) {
477 flagStrs.push_back("RTLD_NOLOAD");
478 flagCheck |= RTLD_NOLOAD;
479 }
480 if (flags & RTLD_NODELETE) {
481 flagStrs.push_back("RTLD_NODELETE");
482 flagCheck |= RTLD_NODELETE;
483 }
484 if (flags & RTLD_FIRST) {
485 flagStrs.push_back("RTLD_FIRST");
486 flagCheck |= RTLD_FIRST;
487 }
488
489 if (flagCheck == flags) {
490 for (auto& flagStr : flagStrs) {
491 if (!flagString.empty()) {
492 flagString += "|";
493 }
494 flagString += flagStr;
495 }
496 }
497
498 return flagString;
499 }
500 std::string path;
501 int flags;
502 uint64_t result;
503 };
504
505 struct dlsym : event_pair {
506 dlsym(ktrace_event_t E) : event_pair(E), handle(E->arg2), symbol(stringForID(E->arg3)) {}
507 std::string symbol;
508 uint64_t handle;
509 uint64_t result;
510 };
511
512 struct dladdr : event_pair {
513 dladdr(ktrace_event_t E) : event_pair(E), address(E->arg2), imageAddress(0), symbolAddress(0) {}
514 uint64_t address;
515 uint64_t imageAddress;
516 uint64_t symbolAddress;
517 int result;
518 };
519
520 struct dlclose : event_pair {
521 dlclose(ktrace_event_t E) : event_pair(E), handle(E->arg2) {}
522 uint64_t handle;
523 int result;
524 };
525
526 struct app_launch : event_pair {
527 app_launch(ktrace_event_t E) : event_pair(E), address(E->arg2) {}
528 uint64_t address;
529 uint64_t launchMode;
530 std::vector<event_pair *> _children;
531 };
532
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;
537 };
538
539 struct map_image : event_pair {
540 map_image(ktrace_event_t E) : event_pair(E), path(stringForID(E->arg2)) {}
541 std::string path;
542 uint64_t result;
543 };
544
545 struct apply_fixups : event_pair {
546 apply_fixups(ktrace_event_t E) : event_pair(E) {}
547 };
548
549 struct attach_signature : event_pair {
550 attach_signature(ktrace_event_t E) : event_pair(E) {}
551 };
552
553 struct build_closure : event_pair {
554 build_closure(ktrace_event_t E) : event_pair(E) {}
555 };
556
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;
561 };
562
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;
567
568 };
569
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;
573 };
574
575 struct objc_images_map : event_pair {
576 objc_images_map(ktrace_event_t E) : event_pair(E) {}
577 };
578
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;
582 };
583
584 struct OutputManager {
585 std::map<unsigned long, std::unique_ptr<output_renderer>> sOutputRenders;
586
587 void flush() {
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()) {
595 if (firstEvent)
596 firstEvent = false;
597 else
598 ostream << ", ";
599 renderer.second->outputTracingJSON(root, ostream);
600 }
601 }
602 ostream << "]";
603 ostream << "}" << std::endl;
604 std::cout << ostream.str();
605 if (!RAW_flag)
606 fflush(stdout);
607 }
608 }
609 };
610
611 static OutputManager sOutputManager;
612
613 void
614 setup_ktrace_callbacks(void)
615 {
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)
622 && str_id != 0) {
623 auto i = gActiveStrings.find(str_id);
624 if (i != gActiveStrings.end()) {
625 gActiveStrings.erase(i);
626 }
627 }
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));
632 } else {
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;
638
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;
644 }
645 }
646 }
647
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);
652 }
653 };
654 });
655
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);
664 }
665 i->second->recordEvent(event);
666 if (i->second->empty()) {
667 sOutputManager.sOutputRenders.erase(i);
668 }
669 });
670 }
671
672 int
673 main(int argc, char *argv[])
674 {
675 char ch;
676 int rv = 0;
677 bool exclude_pids = false;
678 uint64_t time_limit_ns = 0;
679
680 get_screenwidth();
681
682 s = ktrace_session_create();
683 assert(s);
684
685 while ((ch = getopt(argc, argv, "jJeR:t:")) != -1) {
686 switch (ch) {
687 case 'j':
688 JSON_flag = true;
689 break;
690 case 'J':
691 JSON_Tracing_flag = true;
692 break;
693 case 'e':
694 exclude_pids = true;
695 break;
696 case 't':
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",
700 optarg);
701 exit(1);
702 }
703 break;
704 case 'R':
705 RAW_flag = true;
706 rv = ktrace_set_file(s, optarg);
707 if (rv) {
708 fprintf(stderr, "ERROR: reading trace from '%s' failed (%s)\n", optarg, strerror(errno));
709 exit(1);
710 }
711 break;
712 default:
713 exit_usage();
714 }
715 }
716
717 argc -= optind;
718 argv += optind;
719
720 if (time_limit_ns > 0) {
721 if (RAW_flag) {
722 fprintf(stderr, "NOTE: time limit ignored when a raw file is specified\n");
723 } else {
724 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, time_limit_ns),
725 dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0),
726 ^{
727 ktrace_end(s, 0);
728 });
729 }
730 }
731
732 if (!RAW_flag) {
733 if (geteuid() != 0) {
734 fprintf(stderr, "'dyld_usage' must be run as root...\n");
735 exit(1);
736 }
737
738 /*
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.
742 *
743 * if on the other hand we are explicitly including pids, we'll
744 * filter the defaults out naturally.
745 */
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 */
760 }
761 }
762
763 /*
764 * Process the list of specified pids, and in/exclude them as
765 * appropriate.
766 */
767 while (argc > 0) {
768 pid_t pid;
769 char *name;
770 char *endptr;
771
772 name = argv[0];
773 pid = (pid_t)strtoul(name, &endptr, 10);
774
775 if (*name != '\0' && *endptr == '\0') {
776 if (exclude_pids) {
777 rv = ktrace_exclude_pid(s, pid);
778 } else {
779 if (pid != 0)
780 rv = ktrace_filter_pid(s, pid);
781 }
782 } else {
783 if (exclude_pids) {
784 rv = ktrace_exclude_process(s, name);
785 } else {
786 if (strcmp(name, "kernel_task"))
787 rv = ktrace_filter_process(s, name);
788 }
789 }
790
791 if (rv == EINVAL) {
792 fprintf(stderr, "ERROR: cannot both include and exclude simultaneously\n");
793 exit(1);
794 } else {
795 assert(!rv);
796 }
797
798 argc--;
799 argv++;
800 }
801 /* provides SIGINT, SIGHUP, SIGPIPE, SIGTERM handlers */
802 ktrace_set_signal_handler(s);
803 ktrace_set_completion_handler(s, ^{
804 sOutputManager.flush();
805 exit(0);
806 });
807
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, ^{
811 get_screenwidth();
812 });
813 dispatch_activate(sigwinch_source);
814
815 setup_ktrace_callbacks();
816
817 ktrace_set_dropped_events_handler(s, ^{
818 fprintf(stderr, "dyld_usage: buffer overrun, events generated too quickly\n");
819
820 /* clear any state that is now potentially invalid */
821 });
822
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);
827
828 rv = ktrace_start(s, dispatch_get_main_queue());
829
830 if (rv) {
831 perror("ktrace_start");
832 exit(1);
833 }
834
835 dispatch_main();
836
837 return 0;
838 }