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
5 // BUILD(ios,tvos,watchos,bridgeos):
7 // RUN: $SUDO ./dyld_usage_json.exe
9 #import <Foundation/Foundation.h>
22 #include "test_support.h"
24 enum class NodeValueType {
32 NodeValueType type = NodeValueType::Default;
34 std::map<std::string, Node> map;
35 std::vector<Node> array;
38 : type(NodeValueType::Default), value(), map(), array() { }
40 inline Node(std::string string)
41 : type(NodeValueType::String), value(string), map(), array() { }
43 inline Node(const char *string) : Node(std::string(string)) { }
46 : type(NodeValueType::RawValue), value(b ? "true" : "false")
49 inline Node(int64_t i64)
50 : type(NodeValueType::RawValue), value(), map(), array()
52 std::ostringstream os;
57 inline Node(uint64_t u64)
58 : type(NodeValueType::RawValue), value(), map(), array()
60 std::ostringstream os;
66 static Node parseNode(id jsonObject) {
69 // NSDictionary -> map
70 if ([jsonObject isKindOfClass:[NSDictionary class]]) {
71 NSDictionary* dict = (NSDictionary*)jsonObject;
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");
79 Node childNode = parseNode(value);
81 node.map[[key UTF8String]] = childNode;
88 if ([jsonObject isKindOfClass:[NSArray class]]) {
89 NSArray* array = (NSArray*)jsonObject;
91 [array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {
92 Node childNode = parseNode(obj);
93 node.array.push_back(childNode);
100 if ([jsonObject isKindOfClass:[NSString class]]) {
101 node.value = [(NSString*)jsonObject UTF8String];
105 fprintf(stderr, "Unknown json deserialized type\n");
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];
114 fprintf(stderr, "Could not deserialize json because '%s'",[[error localizedFailureReason] UTF8String]);
118 return parseNode(jsonObject);
121 char* mergeJsonRoots(char* jsonBuffer, size_t size)
123 char *mergedJson = (char*)malloc((size + 2) * sizeof(char));
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] = ',';
139 void validateJson(Node json, pid_t pid)
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);
145 std::string handle = json.array[1].map["event"].map["result"].value;
147 for (size_t i = 0; i < json.array.size(); i++) {
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);
152 int jpid = std::stoi(json.array[i].map["pid"].value);
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());
157 if ( json.array[i].map["event"].map["type"].value.compare("app_launch") != 0 )
158 FAIL("dyld_usage did not report app launch event");
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");
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");
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");
186 int main(int argc, const char* argv[], char *env[])
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) {
197 char buffer[16384] = {0};
198 size = read(fd, buffer, 16384);
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 );
206 pid_t pid = dyldUsage.launch();
211 target.set_executable_path(RUN_DIR "/dyld_usage_target.exe");
212 pid_t tpid = target.launch();
220 if (waitpid(pid, &status, 0) == -1)
221 FAIL("waitpid failed");
223 FAIL("No dyld_usage output");
227 (void)dispatch_data_create_map(output, &buffer, &size);
228 char* jsonBuffer = mergeJsonRoots((char*)buffer, size);
230 Node node = readJSON(jsonBuffer, size);
232 validateJson(node, tpid);