Skip to content

Commit 8dfdcd9

Browse files
committed
Check that the runtime counters APIs are available even in no-asserts builds
No runtime function calls will be tracked in this case, but the APIs for reading them are still available. This way there is no need to recompile Swift clients if they are linked against different builds of the standard library and runtime library.
1 parent acff321 commit 8dfdcd9

File tree

1 file changed

+258
-0
lines changed

1 file changed

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

0 commit comments

Comments
 (0)