Skip to content

Commit 6344359

Browse files
Merge pull request #2231 from DataDog/bplasovska/RUM-8454/benchmark-traces
RUM-8454 Trace Benchmark Scenario
2 parents d1794fd + a7b743e commit 6344359

File tree

4 files changed

+220
-0
lines changed

4 files changed

+220
-0
lines changed

BenchmarkTests/BenchmarkTests.xcodeproj/project.pbxproj

+24
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
86252B1B2D818E360098FAEF /* TraceScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86252B1A2D818E360098FAEF /* TraceScenario.swift */; };
11+
86252B1D2D8190DA0098FAEF /* TraceContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86252B1C2D8190DA0098FAEF /* TraceContentView.swift */; };
1012
868DA2E62D706456007D20D5 /* LogsCustomScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 868DA2E52D706456007D20D5 /* LogsCustomScenario.swift */; };
1113
868DA2EC2D706E61007D20D5 /* LogsCustomContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 868DA2EB2D706E61007D20D5 /* LogsCustomContentView.swift */; };
1214
868DA2F52D71DD47007D20D5 /* LogsHeavyTrafficScenario.swift in Sources */ = {isa = PBXBuildFile; fileRef = 868DA2F42D71DD47007D20D5 /* LogsHeavyTrafficScenario.swift */; };
@@ -238,6 +240,8 @@
238240
/* End PBXCopyFilesBuildPhase section */
239241

240242
/* Begin PBXFileReference section */
243+
86252B1A2D818E360098FAEF /* TraceScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TraceScenario.swift; sourceTree = "<group>"; };
244+
86252B1C2D8190DA0098FAEF /* TraceContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TraceContentView.swift; sourceTree = "<group>"; };
241245
868DA2E52D706456007D20D5 /* LogsCustomScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogsCustomScenario.swift; sourceTree = "<group>"; };
242246
868DA2EB2D706E61007D20D5 /* LogsCustomContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogsCustomContentView.swift; sourceTree = "<group>"; };
243247
868DA2F42D71DD47007D20D5 /* LogsHeavyTrafficScenario.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogsHeavyTrafficScenario.swift; sourceTree = "<group>"; };
@@ -471,6 +475,23 @@
471475
/* End PBXFrameworksBuildPhase section */
472476

473477
/* Begin PBXGroup section */
478+
86252B182D818DFB0098FAEF /* Trace */ = {
479+
isa = PBXGroup;
480+
children = (
481+
86252B192D818E1F0098FAEF /* UI */,
482+
86252B1A2D818E360098FAEF /* TraceScenario.swift */,
483+
);
484+
path = Trace;
485+
sourceTree = "<group>";
486+
};
487+
86252B192D818E1F0098FAEF /* UI */ = {
488+
isa = PBXGroup;
489+
children = (
490+
86252B1C2D8190DA0098FAEF /* TraceContentView.swift */,
491+
);
492+
path = UI;
493+
sourceTree = "<group>";
494+
};
474495
868DA2E42D70643A007D20D5 /* Logs */ = {
475496
isa = PBXGroup;
476497
children = (
@@ -591,6 +612,7 @@
591612
D24BFD462C6B916B00AB9604 /* SyntheticScenario.swift */,
592613
D27606992C514F37002D2A14 /* SessionReplay */,
593614
868DA2E42D70643A007D20D5 /* Logs */,
615+
86252B182D818DFB0098FAEF /* Trace */,
594616
);
595617
path = Scenarios;
596618
sourceTree = "<group>";
@@ -1277,6 +1299,7 @@
12771299
buildActionMask = 2147483647;
12781300
files = (
12791301
868DA2E62D706456007D20D5 /* LogsCustomScenario.swift in Sources */,
1302+
86252B1D2D8190DA0098FAEF /* TraceContentView.swift in Sources */,
12801303
868DA2F72D71DD8A007D20D5 /* LogsHeavyTrafficContentView.swift in Sources */,
12811304
868DA2EC2D706E61007D20D5 /* LogsCustomContentView.swift in Sources */,
12821305
D27606A42C514F37002D2A14 /* AppConfiguration.swift in Sources */,
@@ -1289,6 +1312,7 @@
12891312
D24BFD472C6B916B00AB9604 /* SyntheticScenario.swift in Sources */,
12901313
D27606A32C514F37002D2A14 /* Scenario.swift in Sources */,
12911314
D23734972D773DD8004CCAED /* BenchmarkMeter.swift in Sources */,
1315+
86252B1B2D818E360098FAEF /* TraceScenario.swift in Sources */,
12921316
);
12931317
runOnlyForDeploymentPostprocessing = 0;
12941318
};

BenchmarkTests/Runner/Scenarios/SyntheticScenario.swift

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ internal struct SyntheticScenario: Scenario {
1616
case sessionReplaySwiftUI
1717
case logsCustom
1818
case logsHeavyTraffic
19+
case trace
1920
}
2021
/// The scenario's name.
2122
let name: Name
@@ -45,6 +46,8 @@ internal struct SyntheticScenario: Scenario {
4546
_scenario = LogsCustomScenario()
4647
case .logsHeavyTraffic:
4748
_scenario = LogsHeavyTrafficScenario()
49+
case .trace:
50+
_scenario = TraceScenario()
4851
}
4952

5053
self.name = name
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2019-Present Datadog, Inc.
5+
*/
6+
7+
import Foundation
8+
import SwiftUI
9+
10+
import DatadogCore
11+
import DatadogTrace
12+
13+
struct TraceScenario: Scenario {
14+
var initialViewController: UIViewController {
15+
UIHostingController(rootView: TraceContentView())
16+
}
17+
18+
func instrument(with info: AppInfo) {
19+
Datadog.initialize(
20+
with: .benchmark(info: info),
21+
trackingConsent: .granted
22+
)
23+
24+
Trace.enable()
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2019-Present Datadog, Inc.
5+
*/
6+
7+
import DatadogTrace
8+
import SwiftUI
9+
10+
struct TraceContentView: View {
11+
@State private var operationName: String
12+
@State private var resourceName: String
13+
@State private var isError: Bool
14+
@State private var depth: Int
15+
@State private var childrenCount: Int
16+
@State private var childDelay: TimeInterval
17+
@State private var traceCount: Int
18+
@State private var isSending: Bool
19+
20+
var tracer: OTTracer { Tracer.shared() }
21+
22+
private let queue1 = DispatchQueue(label: "com.datadoghq.benchmark-tracing1")
23+
24+
init() {
25+
operationName = "iOS Benchmark span operation"
26+
resourceName = "iOS Benchmark span resource"
27+
isError = false
28+
depth = 1
29+
childrenCount = 0
30+
childDelay = 100
31+
traceCount = 0
32+
isSending = false
33+
}
34+
35+
var body: some View {
36+
VStack {
37+
Form {
38+
Section(header: Text("Span configuration")) {
39+
TextField("Operation name", text: $operationName)
40+
TextField("Resource name", text: $resourceName)
41+
Toggle("Is Error", isOn: $isError)
42+
.tint(.purple)
43+
}
44+
45+
Section(header: Text("Complex span configuration")) {
46+
HStack {
47+
Text("Children count:")
48+
Spacer()
49+
TextField("Children count:", value: $childrenCount, formatter: NumberFormatter())
50+
.textFieldStyle(RoundedBorderTextFieldStyle())
51+
.frame(width: 80)
52+
.keyboardType(.numberPad)
53+
Stepper("", value: $childrenCount, in: 0 ... 100, step: 1)
54+
.frame(width: 80)
55+
}
56+
57+
HStack {
58+
Text("Depth:")
59+
Spacer()
60+
TextField("Depth:", value: $depth, formatter: NumberFormatter())
61+
.textFieldStyle(RoundedBorderTextFieldStyle())
62+
.frame(width: 80)
63+
.keyboardType(.numberPad)
64+
Stepper("", value: $depth, in: 1 ... 100, step: 1)
65+
.frame(width: 80)
66+
}
67+
68+
HStack {
69+
Text("Child delay (ms):")
70+
Spacer()
71+
TextField("Child delay:", value: $childDelay, formatter: NumberFormatter())
72+
.textFieldStyle(RoundedBorderTextFieldStyle())
73+
.frame(width: 80)
74+
.keyboardType(.numberPad)
75+
Stepper("", value: $childDelay, in: 50 ... 10_000, step: 50)
76+
.frame(width: 80)
77+
}
78+
}
79+
80+
Button(action: sendTrace) {
81+
Text("Send")
82+
.foregroundColor(.white)
83+
.padding()
84+
.frame(maxWidth: .infinity)
85+
.background(Color.purple)
86+
.cornerRadius(8)
87+
}
88+
.listRowBackground(EmptyView())
89+
.listRowInsets(EdgeInsets())
90+
91+
VStack(alignment: .center) {
92+
ProgressView()
93+
.progressViewStyle(CircularProgressViewStyle())
94+
.opacity(isSending ? 1 : 0)
95+
.padding()
96+
Text("Traces sent: \(traceCount)")
97+
.padding(.bottom, 16)
98+
}
99+
.frame(maxWidth: .infinity, alignment: .center)
100+
.listRowBackground(EmptyView())
101+
.listRowInsets(EdgeInsets())
102+
}
103+
}
104+
}
105+
106+
/// Starts the tracing process based on the current configuration:
107+
/// 1. Creates a root span. with the given configuration.
108+
/// 2. Recursively generates child spans via `sendSpanTree`.
109+
/// 3. Finishes the root span and updates the trace counter.
110+
private func sendTrace() {
111+
DispatchQueue.main.async {
112+
isSending = true
113+
}
114+
115+
queue1.async { [self] in
116+
let rootSpan = tracer.startSpan(operationName: operationName)
117+
rootSpan.setTag(key: SpanTags.resource, value: resourceName)
118+
119+
if isError {
120+
rootSpan.log(
121+
fields: [
122+
OTLogFields.event: "error",
123+
OTLogFields.errorKind: "Simulated error",
124+
OTLogFields.message: "Describe what happened",
125+
OTLogFields.stack: "Foo.swift:42",
126+
]
127+
)
128+
}
129+
130+
Thread.sleep(forTimeInterval: childDelay / 1_000)
131+
132+
sendSpanTree(parent: rootSpan, currentLevel: 0, maxDepth: depth)
133+
134+
Thread.sleep(forTimeInterval: 0.5)
135+
rootSpan.finish()
136+
137+
DispatchQueue.main.async {
138+
traceCount += 1
139+
isSending = false
140+
}
141+
}
142+
}
143+
144+
/// Recursively generates a tree of child spans starting from the given parent span, waiting for `childDelay` between span creation.
145+
/// If the current level reaches `maxDepth` or if `childrenCount` is 0, recursion stops.
146+
/// - Parameters:
147+
/// - parent: The parent span from which to create child spans.
148+
/// - currentLevel: The current depth level in the span tree.
149+
/// - maxDepth: The maximum depth (levels) for the span tree generation.
150+
private func sendSpanTree(parent: OTSpan, currentLevel: Int, maxDepth: Int) {
151+
guard currentLevel < maxDepth, childrenCount > 0 else { return }
152+
153+
for i in 1 ... childrenCount {
154+
let childOperation = "\(operationName) - Child \(i) at level \(currentLevel + 1)"
155+
let childSpan = tracer.startSpan(operationName: childOperation, childOf: parent.context)
156+
157+
Thread.sleep(forTimeInterval: childDelay / 1_000)
158+
159+
sendSpanTree(parent: childSpan, currentLevel: currentLevel + 1, maxDepth: maxDepth)
160+
childSpan.finish()
161+
}
162+
}
163+
}
164+
165+
#Preview {
166+
TraceContentView()
167+
}

0 commit comments

Comments
 (0)