|
| 1 | +// RUN: %empty-directory(%t) |
| 2 | +// RUN: %target-build-swift %s -o %t/test_runtime_function_counters |
| 3 | +// RUN: %target-run %t/test_runtime_function_counters 2>&1 | %FileCheck %s |
| 4 | +// REQUIRES: asserts |
| 5 | +// REQUIRES: executable_test |
| 6 | + |
| 7 | +/// Test functionality related to the runtime function counters. |
| 8 | + |
| 9 | +class C { |
| 10 | + var next: C? = nil |
| 11 | + func test(_ c: C) { |
| 12 | + } |
| 13 | +} |
| 14 | + |
| 15 | +struct MyStruct { |
| 16 | + var ref1: AnyObject? = C() |
| 17 | + var ref2: AnyObject = C() |
| 18 | + var str: String = "" |
| 19 | +} |
| 20 | + |
| 21 | +public final class List<T> { |
| 22 | + var value: T |
| 23 | + var next: List<T>? |
| 24 | + |
| 25 | + init(_ value: T) { |
| 26 | + self.value = value |
| 27 | + self.next = nil |
| 28 | + } |
| 29 | + |
| 30 | + init(_ value: T, _ tail: List<T>) { |
| 31 | + self.value = value |
| 32 | + self.next = tail |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +public func length<T>(_ l: List<T>) -> Int { |
| 37 | + var ll: List<T>? = l |
| 38 | + var len = 0 |
| 39 | + while ll != nil { |
| 40 | + len = len + 1 |
| 41 | + ll = ll?.next |
| 42 | + } |
| 43 | + return len |
| 44 | +} |
| 45 | + |
| 46 | +/// CHECK-LABEL: TEST: Collect references inside objects |
| 47 | +/// Constant strings do not have an owner, thus no references. |
| 48 | +/// CHECK: Constant string: [] |
| 49 | +/// An array has one reference |
| 50 | +/// CHECK: Array<Int>: [{{[0-9a-fA-Fx]+}}] |
| 51 | +/// MyStruct has two references |
| 52 | +/// CHECK: MyStruct: [{{[0-9a-fA-Fx]+}}, {{[0-9a-fA-Fx]+}}] |
| 53 | +/// Dictionary has once reference |
| 54 | +/// CHECK: Dictionary<Int, Int>: [{{[0-9a-fA-Fx]+}}] |
| 55 | +/// Set has once reference |
| 56 | +/// CHECK: Set<Int>: [{{[0-9a-fA-Fx]+}}] |
| 57 | +/// Test collection of references inside different types of objects. |
| 58 | +@inline(never) |
| 59 | +func testCollectReferencesInsideObject() { |
| 60 | + print("TEST: Collect references inside objects") |
| 61 | + let s = "MyString" |
| 62 | + let aint = [1,2,3,4] |
| 63 | + let dint = [1:1, 2:2] |
| 64 | + let sint: Set<Int> = [1,2,3,4] |
| 65 | + |
| 66 | + print("Constant string: \(_collectReferencesInsideObject(s))") |
| 67 | + print("Array<Int>: \(_collectReferencesInsideObject(aint))") |
| 68 | + print("MyStruct: \(_collectReferencesInsideObject(MyStruct()))") |
| 69 | + print("Dictionary<Int, Int>: \(_collectReferencesInsideObject(dint))") |
| 70 | + print("Set<Int>: \(_collectReferencesInsideObject(sint))") |
| 71 | + |
| 72 | + var mystring = "MyString" |
| 73 | + mystring.append("End") |
| 74 | + testString(mystring) |
| 75 | + testDict(dint) |
| 76 | + testObjectCycle() |
| 77 | +} |
| 78 | + |
| 79 | + |
| 80 | +/// CHECK-LABEL: TEST: APIs from _RuntimeFunctionCounters |
| 81 | +/// CHECK: Number of runtime function pointers: |
| 82 | +/// Test some APIs from _RuntimeFunctionCounters |
| 83 | +func testRuntimeCounters() { |
| 84 | + print("TEST: APIs from _RuntimeFunctionCounters") |
| 85 | + let numRuntimeFunctionPointer = |
| 86 | + _RuntimeFunctionCounters.getNumRuntimeFunctionCounters() |
| 87 | + |
| 88 | + print("Number of runtime function pointers: \(numRuntimeFunctionPointer)") |
| 89 | + |
| 90 | + let names = _RuntimeFunctionCounters.getRuntimeFunctionNames() |
| 91 | + let offsets = _RuntimeFunctionCounters.getRuntimeFunctionCountersOffsets() |
| 92 | + |
| 93 | + for i in 0..<numRuntimeFunctionPointer { |
| 94 | + print("Runtime function \(i) : \(names[i]) at offset: \(offsets[i])") |
| 95 | + } |
| 96 | + |
| 97 | + var d: [Int : Int] = [:] |
| 98 | + let globalCounters1 = _GlobalRuntimeFunctionCountersState() |
| 99 | + |
| 100 | + for i in 0..<50 { |
| 101 | + let k = i |
| 102 | + let v = i*i |
| 103 | + d[k] = v |
| 104 | + } |
| 105 | + |
| 106 | + let globalCounters2 = _GlobalRuntimeFunctionCountersState() |
| 107 | + |
| 108 | + globalCounters1.dumpDiff(globalCounters2) |
| 109 | +} |
| 110 | + |
| 111 | +/// Test finding references inside a String object. |
| 112 | +@inline(never) |
| 113 | +func testString(_ s: String) { |
| 114 | + print("TEST: Collect references for strings") |
| 115 | + let refs = _collectReferencesInsideObject(s) |
| 116 | + print("References are: \(refs)") |
| 117 | + let objectCounters1 = _ObjectRuntimeFunctionCountersState(refs[0]) |
| 118 | + let _ = [String](repeating: s, count: 4) |
| 119 | + let objectCounters2 = _ObjectRuntimeFunctionCountersState(refs[0]) |
| 120 | + objectCounters1.dumpDiff(objectCounters2) |
| 121 | +} |
| 122 | + |
| 123 | +/// Test finding references inside a Dictionary object. |
| 124 | +@inline(never) |
| 125 | +func testDict(_ _dint: [Int : Int]) { |
| 126 | + print("TEST: Collect references for dictionaries") |
| 127 | + var dint = _dint |
| 128 | + dint[3] = 3 |
| 129 | + let refs = _collectReferencesInsideObject(dint) |
| 130 | + print("References are: \(refs)") |
| 131 | + let objectCounters1 = _ObjectRuntimeFunctionCountersState(refs[0]) |
| 132 | + dint[222] = 222 |
| 133 | + dint[2222] = 2222 |
| 134 | + let objectCounters2 = _ObjectRuntimeFunctionCountersState(refs[0]) |
| 135 | + objectCounters1.dumpDiff(objectCounters2) |
| 136 | +} |
| 137 | + |
| 138 | +/// Test finding references inside an object graph with a cycle. |
| 139 | +/// It should not result in a stack overflow. |
| 140 | +@inline(never) |
| 141 | +func testObjectCycle() { |
| 142 | + print("TEST: Collect references on object graph with cycles") |
| 143 | + print("testObjectCycle") |
| 144 | + let c1 = C() |
| 145 | + let c2 = C() |
| 146 | + c1.next = c1 |
| 147 | + c2.next = c1 |
| 148 | + let refs = _collectReferencesInsideObject(c1) |
| 149 | + print("References are: \(refs)") |
| 150 | + let objectCounters1 = _ObjectRuntimeFunctionCountersState(refs[0]) |
| 151 | + c1.next = nil |
| 152 | + c2.next = nil |
| 153 | + let objectCounters2 = _ObjectRuntimeFunctionCountersState(refs[0]) |
| 154 | + objectCounters1.dumpDiff(objectCounters2, skipUnchanged: true) |
| 155 | +} |
| 156 | + |
| 157 | +/// Test runtime function counters for a List object. |
| 158 | +@inline(never) |
| 159 | +func testLists() { |
| 160 | + print("TEST: Runtime function counters for Lists") |
| 161 | + print("testLists") |
| 162 | + let globalCounters1 = _GlobalRuntimeFunctionCountersState() |
| 163 | + var l: List<Int>? = List(1, List(2, List(3, List(4, List(5))))) |
| 164 | + let refs = _collectReferencesInsideObject(l!) |
| 165 | + let globalCounters11 = _GlobalRuntimeFunctionCountersState() |
| 166 | + let _ = _collectReferencesInsideObject(l!) |
| 167 | + let globalCounters111 = _GlobalRuntimeFunctionCountersState() |
| 168 | + |
| 169 | + print("Global counters diff for 11") |
| 170 | + globalCounters1.dumpDiff(globalCounters11, skipUnchanged: true) |
| 171 | + print("Global counters diff for 111") |
| 172 | + globalCounters1.dumpDiff(globalCounters111, skipUnchanged: true) |
| 173 | + |
| 174 | + let len = length(l!) |
| 175 | + let globalCounters2 = _GlobalRuntimeFunctionCountersState() |
| 176 | + print("Length of the list is \(len)") |
| 177 | + print("Global counters diff after constructing a list and computing its length") |
| 178 | + globalCounters1.dumpDiff(globalCounters2, skipUnchanged: true) |
| 179 | + let objectCounters1 = _ObjectRuntimeFunctionCountersState(refs[0]) |
| 180 | + l = nil |
| 181 | + let objectCounters2 = _ObjectRuntimeFunctionCountersState(refs[0]) |
| 182 | + print("List head counters after list becomes unreferenced") |
| 183 | + objectCounters1.dumpDiff(objectCounters2, skipUnchanged: true) |
| 184 | +} |
| 185 | + |
| 186 | +/// Test the _measureRuntimeFunctionCountersDiffs API. |
| 187 | +@inline(never) |
| 188 | +func testMeasureRuntimeFunctionCountersDiffs() { |
| 189 | + print("TEST: Measure runtime function counters diff") |
| 190 | + let l: List<Int>? = List(1, List(2, List(3, List(4, List(5))))) |
| 191 | + let refs = _collectReferencesInsideObject(l!) |
| 192 | + var len = 0 |
| 193 | + let (globalCounters, objectsCountersDiffs) = |
| 194 | + _measureRuntimeFunctionCountersDiffs(objects: [refs[0]]) { |
| 195 | + len = length(l!) |
| 196 | + } |
| 197 | + print("List length is: \(len)") |
| 198 | + print("Global counters changes") |
| 199 | + globalCounters.dump(skipUnchanged: true) |
| 200 | + print("Objects counters changes") |
| 201 | + for (i, objectCounters) in objectsCountersDiffs.enumerated() { |
| 202 | + print("Object counters diff for \(refs[i])") |
| 203 | + objectCounters.dump(skipUnchanged: true) |
| 204 | + } |
| 205 | +} |
| 206 | + |
| 207 | +/// This is a handler that is invoked on each runtime functions counters update. |
| 208 | +@inline(never) |
| 209 | +func updatesHandler(object: UnsafeRawPointer, functionId: Int64) { |
| 210 | + let savedMode = _RuntimeFunctionCounters.disableRuntimeFunctionCountersUpdates() |
| 211 | + print("Start handler") |
| 212 | + let functionName = _RuntimeFunctionCounters.runtimeFunctionNames[Int(functionId)] |
| 213 | + print("Function \(functionName) was invoked on object \(object)") |
| 214 | + print("End handler") |
| 215 | + _RuntimeFunctionCounters.enableRuntimeFunctionCountersUpdates(mode: savedMode) |
| 216 | +} |
| 217 | + |
| 218 | +/// CHECK-LABEL: TEST: Provide runtime function counters update handler |
| 219 | +/// TEST: Start handler |
| 220 | +/// TEST: End handler |
| 221 | +/// Test that you can provide custom handlers for runtime functions counters |
| 222 | +/// updates. |
| 223 | +@inline(never) |
| 224 | +func testFunctionRuntimeCountersUpdateHandler() { |
| 225 | + print("TEST: Provide runtime function counters update handler") |
| 226 | + let l: List<Int>? = List(1, List(2, List(3, List(4, List(5))))) |
| 227 | + let oldHandler = |
| 228 | + _RuntimeFunctionCounters.setGlobalRuntimeFunctionCountersUpdateHandler( |
| 229 | + handler: updatesHandler) |
| 230 | + let len = length(l!) |
| 231 | + _ = _RuntimeFunctionCounters.setGlobalRuntimeFunctionCountersUpdateHandler( |
| 232 | + handler: oldHandler) |
| 233 | + print("Restored old handler") |
| 234 | + print(len) |
| 235 | +} |
| 236 | + |
| 237 | +/// Enable runtime function counters stats collection. |
| 238 | +_RuntimeFunctionCounters.enableRuntimeFunctionCountersUpdates() |
| 239 | + |
| 240 | +/// Test collection of references inside different types of objects. |
| 241 | +testCollectReferencesInsideObject() |
| 242 | + |
| 243 | +/// Test some APIs from _RuntimeFunctionCounters. |
| 244 | +testRuntimeCounters() |
| 245 | + |
| 246 | +/// Test dumping of counters for all objects. |
| 247 | +_RuntimeFunctionCounters.dumpObjectsRuntimeFunctionPointers() |
| 248 | + |
| 249 | +/// Test runtime function counters for a List object. |
| 250 | +testLists() |
| 251 | + |
| 252 | +/// Test the _measureRuntimeFunctionCountersDiffs API. |
| 253 | +testMeasureRuntimeFunctionCountersDiffs() |
| 254 | + |
| 255 | +/// Test that you can provide custom handlers for runtime functions counters updates. |
| 256 | +testFunctionRuntimeCountersUpdateHandler() |
0 commit comments