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