// RUN: %empty-directory(%t) // RUN: %target-build-swift %s -o %t/test_runtime_function_counters // RUN: %target-codesign %t/test_runtime_function_counters // RUN: %target-run %t/test_runtime_function_counters 2>&1 | %FileCheck %s // REQUIRES: runtime_function_counters // REQUIRES: executable_test // REQUIRES: rdar48995133 /// Test functionality related to the runtime function counters. class C { var next: C? = nil func test(_ c: C) { } } struct MyStruct { var ref1: AnyObject? = C() var ref2: AnyObject = C() var str: String = "" } public final class List { var value: T var next: List? init(_ value: T) { self.value = value self.next = nil } init(_ value: T, _ tail: List) { self.value = value self.next = tail } } public func length(_ l: List) -> Int { var ll: List? = l var len = 0 while ll != nil { len = len + 1 ll = ll?.next } return len } /// CHECK-LABEL: TEST: Collect references inside objects // Constant strings don't really have a reference, but BridgeObject // still counts as one. // // FIXME(TODO: JIRA): On 32-bit, we use AnyObject? instead of BridgeObject. If // we get back onto a real reference (or if 64-bit gets off of a real // reference), then drop adjust the optionality of the following check. // /// CHECK: Constant string: [{{([0-9a-fA-Fx]+)?}}] /// An array has one reference /// CHECK: Array: [{{[0-9a-fA-Fx]+}}] /// MyStruct has two references plus a String with a third // // FIXME(TODO: JIRA): On 32-bit, we use AnyObject? instead of BridgeObject. If // we get back onto a real reference (or if 64-bit gets off of a real // reference), then drop adjust the optionality of the following check. // /// CHECK: MyStruct: [{{[0-9a-fA-Fx]+}}, {{[0-9a-fA-Fx]+}}{{(, [0-9a-fA-Fx]+)?}}] /// Dictionary has one reference /// CHECK: Dictionary: [{{[0-9a-fA-Fx]+}}] /// Set has one reference /// CHECK: Set: [{{[0-9a-fA-Fx]+}}] /// Test collection of references inside different types of objects. @inline(never) func testCollectReferencesInsideObject() { print("TEST: Collect references inside objects") let s = "MyString" let aint = [1,2,3,4] let dint = [1:1, 2:2] let sint: Set = [1,2,3,4] print("Constant string: \(_collectReferencesInsideObject(s))") print("Array: \(_collectReferencesInsideObject(aint))") print("MyStruct: \(_collectReferencesInsideObject(MyStruct()))") print("Dictionary: \(_collectReferencesInsideObject(dint))") print("Set: \(_collectReferencesInsideObject(sint))") var mystring = "MyString" mystring.append("End") testString(mystring) testDict(dint) testObjectCycle() } /// CHECK-LABEL: TEST: APIs from _RuntimeFunctionCounters /// CHECK: Number of runtime function pointers: /// Test some APIs from _RuntimeFunctionCounters func testRuntimeCounters() { print("TEST: APIs from _RuntimeFunctionCounters") let numRuntimeFunctionPointer = Int(_RuntimeFunctionCounters.getNumRuntimeFunctionCounters()) print("Number of runtime function pointers: \(numRuntimeFunctionPointer)") let names = _RuntimeFunctionCounters.getRuntimeFunctionNames() let offsets = _RuntimeFunctionCounters.getRuntimeFunctionCountersOffsets() for i in 0..? = List(1, List(2, List(3, List(4, List(5))))) let refs = _collectReferencesInsideObject(l!) let globalCounters11 = _GlobalRuntimeFunctionCountersState() let _ = _collectReferencesInsideObject(l!) let globalCounters111 = _GlobalRuntimeFunctionCountersState() print("Global counters diff for 11") globalCounters1.dumpDiff(globalCounters11, skipUnchanged: true) print("Global counters diff for 111") globalCounters1.dumpDiff(globalCounters111, skipUnchanged: true) let len = length(l!) let globalCounters2 = _GlobalRuntimeFunctionCountersState() print("Length of the list is \(len)") print("Global counters diff after constructing a list and computing its length") globalCounters1.dumpDiff(globalCounters2, skipUnchanged: true) let objectCounters1 = _ObjectRuntimeFunctionCountersState(refs[0]) l = nil let objectCounters2 = _ObjectRuntimeFunctionCountersState(refs[0]) print("List head counters after list becomes unreferenced") objectCounters1.dumpDiff(objectCounters2, skipUnchanged: true) } /// Test the _measureRuntimeFunctionCountersDiffs API. @inline(never) func testMeasureRuntimeFunctionCountersDiffs() { print("TEST: Measure runtime function counters diff") let l: List? = List(1, List(2, List(3, List(4, List(5))))) let refs = _collectReferencesInsideObject(l!) var len = 0 let (globalCounters, objectsCountersDiffs) = _measureRuntimeFunctionCountersDiffs(objects: [refs[0]]) { len = length(l!) } print("List length is: \(len)") print("Global counters changes") globalCounters.dump(skipUnchanged: true) print("Objects counters changes") for (i, objectCounters) in objectsCountersDiffs.enumerated() { print("Object counters diff for \(refs[i])") objectCounters.dump(skipUnchanged: true) } } /// This is a handler that is invoked on each runtime functions counters update. @inline(never) func updatesHandler(object: UnsafeRawPointer, functionId: Int64) { let savedMode = _RuntimeFunctionCounters.disableRuntimeFunctionCountersUpdates() print("Start handler") let functionName = _RuntimeFunctionCounters.runtimeFunctionNames[Int(functionId)] print("Function \(functionName) was invoked on object \(object)") print("End handler") _RuntimeFunctionCounters.enableRuntimeFunctionCountersUpdates(mode: savedMode) } /// Check that it is possible to set your own runtime functions counters /// updates handler and this handler is invoked at runtime. /// CHECK-LABEL: TEST: Provide runtime function counters update handler /// CHECK: Start handler /// Check that allocations and deallocations are intercepted too. /// CHECK: swift_allocObject /// CHECK: swift_deallocObject /// CHECK: End handler /// Test that you can provide custom handlers for runtime functions counters /// updates. var globalC: C? = nil @inline(never) func testFunctionRuntimeCountersUpdateHandler() { print("TEST: Provide runtime function counters update handler") let l: List? = List(1, List(2, List(3, List(4, List(5))))) let oldHandler = _RuntimeFunctionCounters.setGlobalRuntimeFunctionCountersUpdateHandler( handler: updatesHandler) globalC = C() globalC = nil let len = length(l!) _ = _RuntimeFunctionCounters.setGlobalRuntimeFunctionCountersUpdateHandler( handler: oldHandler) print("Restored old handler") print(len) } /// Enable runtime function counters stats collection. _RuntimeFunctionCounters.enableRuntimeFunctionCountersUpdates() /// Test collection of references inside different types of objects. testCollectReferencesInsideObject() /// Test some APIs from _RuntimeFunctionCounters. testRuntimeCounters() /// Test dumping of counters for all objects. _RuntimeFunctionCounters.dumpObjectsRuntimeFunctionPointers() /// Test runtime function counters for a List object. testLists() /// Test the _measureRuntimeFunctionCountersDiffs API. testMeasureRuntimeFunctionCountersDiffs() /// Test that you can provide custom handlers for runtime functions counters updates. testFunctionRuntimeCountersUpdateHandler()