dyld-832.7.1.tar.gz
[apple/dyld.git] / testing / test-cases / dyld_usage_json.dtest / main.mm
1 // BUILD(macos):  $CC foo.c         -o $BUILD_DIR/libfoo.dylib -dynamiclib -install_name $RUN_DIR/lifoo.dylib
2 // BUILD(macos):  $CC target.c      -o $BUILD_DIR/dyld_usage_target.exe  -DRUN_DIR="$RUN_DIR"
3 // BUILD(macos):  $CXX main.mm     -o $BUILD_DIR/dyld_usage_json.exe -DRUN_DIR="$RUN_DIR" -std=c++14 -framework Foundation
4
5 // BUILD(ios,tvos,watchos,bridgeos):
6
7 // RUN:  $SUDO ./dyld_usage_json.exe
8
9 #import <Foundation/Foundation.h>
10
11 #include <errno.h>
12 #include <signal.h>
13 #include <spawn.h>
14 #include <sys/wait.h>
15 #include <unistd.h>
16
17 #include <map>
18 #include <sstream>
19 #include <string>
20 #include <vector>
21
22 #include "test_support.h"
23
24 enum class NodeValueType {
25     Default,
26     String,
27     RawValue,
28 };
29
30 struct Node
31 {
32     NodeValueType               type = NodeValueType::Default;
33     std::string                 value;
34     std::map<std::string, Node> map;
35     std::vector<Node>           array;
36
37     inline Node()
38     : type(NodeValueType::Default), value(), map(), array() { }
39
40     inline Node(std::string string)
41     : type(NodeValueType::String), value(string), map(), array() { }
42
43     inline Node(const char *string) : Node(std::string(string)) { }
44
45     inline Node(bool b)
46     : type(NodeValueType::RawValue), value(b ? "true" : "false")
47     , map(), array() { }
48
49     inline Node(int64_t i64)
50     : type(NodeValueType::RawValue), value(), map(), array()
51     {
52         std::ostringstream os;
53         os << i64;
54         value = os.str();
55     }
56
57     inline Node(uint64_t u64)
58     : type(NodeValueType::RawValue), value(), map(), array()
59     {
60         std::ostringstream os;
61         os << u64;
62         value = os.str();
63     }
64 };
65
66 static Node parseNode(id jsonObject) {
67     __block Node node;
68
69     // NSDictionary -> map
70     if ([jsonObject isKindOfClass:[NSDictionary class]]) {
71         NSDictionary* dict = (NSDictionary*)jsonObject;
72
73         [dict enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL* stop) {
74             if (![key isKindOfClass:[NSString class]]) {
75                 fprintf(stderr, "JSON map key is not of string type\n");
76                 *stop = true;
77                 return;
78             }
79             Node childNode = parseNode(value);
80
81             node.map[[key UTF8String]] = childNode;
82         }];
83
84         return node;
85     }
86
87     // NSArray -> array
88     if ([jsonObject isKindOfClass:[NSArray class]]) {
89         NSArray* array = (NSArray*)jsonObject;
90
91         [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
92             Node childNode = parseNode(obj);
93             node.array.push_back(childNode);
94         }];
95
96         return node;
97     }
98
99     // NSString -> value
100     if ([jsonObject isKindOfClass:[NSString class]]) {
101         node.value = [(NSString*)jsonObject UTF8String];
102         return node;
103     }
104
105     fprintf(stderr, "Unknown json deserialized type\n");
106     return Node();
107 }
108
109 Node readJSON(const void * contents, size_t length) {
110     NSData* data = [NSData dataWithBytes:contents length:length];
111     NSError* error = nil;
112     id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
113     if (!jsonObject) {
114         fprintf(stderr, "Could not deserialize json because '%s'",[[error localizedFailureReason] UTF8String]);
115         return Node();
116     }
117
118     return parseNode(jsonObject);
119 }
120
121 char* mergeJsonRoots(char* jsonBuffer, size_t size)
122 {
123     char *mergedJson = (char*)malloc((size + 2) * sizeof(char));
124     mergedJson[0] = '[';
125     mergedJson[size+1] = '\0';
126     mergedJson[size+1] = ']';
127     for (size_t i = 0; i < size; i++) {
128         mergedJson[i+1] = jsonBuffer[i];
129         if (jsonBuffer[i] == '\n') {
130             if ( i > 0 && i < size - 1 ) {
131                 if (jsonBuffer[i-1] == '}' && jsonBuffer[i+1] == '{')
132                     mergedJson[i+1] = ',';
133             }
134         }
135     }
136     return mergedJson;
137 }
138
139 void validateJson(Node json, pid_t pid)
140 {
141     size_t expectedSize = 4;
142     if (json.array.size() != expectedSize)
143         FAIL("dyld_usage reported number of events is incorrect. Reported %lu instead of %lu", json.array.size(), expectedSize);
144
145     std::string handle = json.array[1].map["event"].map["result"].value;
146
147     for (size_t i = 0; i < json.array.size(); i++) {
148
149         if ( json.array[i].map["command"].value.compare("dyld_usage_target.exe") != 0 )
150             FAIL("Incorrect command name for event at index %lu", i);
151
152         int jpid = std::stoi(json.array[i].map["pid"].value);
153         if ( jpid != pid)
154             FAIL("Incorrect pid for event at index %lu. Reported %d intead of %d (%s)", i, jpid, pid, json.array[i].map["pid"].value.c_str());
155
156         if (i == 0) {
157             if ( json.array[i].map["event"].map["type"].value.compare("app_launch") != 0 )
158                 FAIL("dyld_usage did not report app launch event");
159         }
160
161         if (i == 1) {
162             if ( json.array[1].map["event"].map["type"].value.compare("dlopen") != 0 )
163                 FAIL("dyld_usage did not report dlopen event");
164             if ( json.array[1].map["event"].map["path"].value.compare(RUN_DIR "/libfoo.dylib") != 0 )
165                 FAIL("Incorrect dlopen library path");
166         }
167
168         if (i == 2) {
169             if ( json.array[i].map["event"].map["type"].value.compare("dlsym") != 0 )
170                 FAIL("dyld_usage did not report dlsym event");
171             if ( json.array[i].map["event"].map["symbol"].value.compare("foo") != 0 )
172                 FAIL("incorrect dlsym symbol reported");
173             if ( json.array[i].map["event"].map["handle"].value.compare(handle) != 0 )
174                 FAIL("dlsym handle does not match dlopen result");
175         }
176
177         if (i == 3) {
178             if ( json.array[i].map["event"].map["type"].value.compare("dlclose") != 0 )
179                 FAIL("dyld_usage did not report dlclose event");
180             if ( json.array[i].map["event"].map["handle"].value.compare(handle) != 0 )
181                 FAIL("dlclose handle does not match dlopen result");
182         }
183     }
184 }
185
186 int main(int argc, const char* argv[], char *env[])
187 {
188     _process dyldUsage;
189     dyldUsage.set_executable_path("/usr/local/bin/dyld_usage");
190     dyldUsage.set_launch_async(true);
191     const char* args[] = { "-j", "dyld_usage_target.exe", NULL };
192     dyldUsage.set_args(args);
193     __block dispatch_data_t output = NULL;
194     dyldUsage.set_stdout_handler(^(int fd) {
195         ssize_t size = 0;
196         do {
197             char buffer[16384] = {0};
198             size = read(fd, buffer, 16384);
199             if ( size == -1 )
200                 break;
201             dispatch_data_t data = dispatch_data_create(buffer, size, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
202             output = output ? dispatch_data_create_concat(output, data) : data;
203         } while ( size > 0 );
204     });
205
206     pid_t pid = dyldUsage.launch();
207     usleep(2000000);
208
209     // Launch target
210     _process target;
211     target.set_executable_path(RUN_DIR "/dyld_usage_target.exe");
212     pid_t tpid = target.launch();
213
214     usleep(2000000);
215     // Kill dyld_usage
216     kill(pid, SIGTERM);
217
218
219     int status;
220     if (waitpid(pid, &status, 0) == -1)
221         FAIL("waitpid failed");
222     if ( !output )
223         FAIL("No dyld_usage output");
224
225     const void* buffer;
226     size_t size;
227     (void)dispatch_data_create_map(output, &buffer, &size);
228     char* jsonBuffer = mergeJsonRoots((char*)buffer, size);
229     size += 2;
230     Node node = readJSON(jsonBuffer, size);
231     free(jsonBuffer);
232     validateJson(node, tpid);
233
234     PASS("Success");
235     return 0;
236 }