Skip to content

Commit a445183

Browse files
authored
chore: add Swift example (#55)
Parca currently does not work great on handling Swift processes. 1. Most of the stacks are shown as "calculate", dropping the full function names and arguments <img width="2500" height="942" alt="parca" src="https://github.com/user-attachments/assets/90a54c7d-06d8-4c12-b20d-d5f4c649d517" /> 2. Demangling to Foundation calls is missing <img width="1159" height="807" alt="demangle" src="https://github.com/user-attachments/assets/047ebf40-fbf0-42ff-a5b5-2797e8dfd94c" /> The same workload when sampled via perf_events and piping demangling via `swift-demangle` for comparison: ![perf-events-swift-demangle-parca-demo](https://github.com/user-attachments/assets/ce2a4a9a-0a97-4f49-ae6d-77b609cd7464)
1 parent 09a206f commit a445183

File tree

8 files changed

+200
-0
lines changed

8 files changed

+200
-0
lines changed

swift/Dockerfile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM docker.io/swift:bookworm
2+
3+
WORKDIR /app
4+
5+
COPY . .
6+
7+
ENV MODE=release
8+
RUN swift build -c ${MODE} && cp .build/${MODE}/ParcaDemo ./parca-demo
9+
10+
ENTRYPOINT ["./parca-demo"]

swift/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.PHONY: build
2+
build:
3+
docker build -t parca-demo:swift .

swift/Package.resolved

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

swift/Package.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// swift-tools-version: 6.1
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "parca-demo",
8+
dependencies: [
9+
.package(url: "https://github.com/apple/swift-log", from: "1.6.4"),
10+
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.6.1")
11+
],
12+
targets: [
13+
.executableTarget(
14+
name: "ParcaDemo",
15+
dependencies: [
16+
.product(name: "Logging", package: "swift-log"),
17+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
18+
],
19+
path: "Sources"
20+
)
21+
]
22+
)

swift/Sources/Fibonacci.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import Foundation
2+
import Logging
3+
4+
struct FibonacciResult {
5+
let value: UInt64
6+
let input: UInt64
7+
let timestamp: Date
8+
9+
var json: String {
10+
let dict = ["fibonacci": "\(value)", "input": "\(input)", "timestamp": "\(timestamp)"]
11+
guard let data = try? JSONSerialization.data(withJSONObject: dict),
12+
let string = String(data: data, encoding: .utf8)
13+
else {
14+
return "error"
15+
}
16+
return string
17+
}
18+
}
19+
20+
struct FibonacciCalculator {
21+
private let logger: Logger
22+
23+
init(logger: Logger) {
24+
self.logger = logger
25+
}
26+
27+
@inline(never)
28+
func calculate(_ n: UInt64) -> UInt64 {
29+
switch n {
30+
case 0:
31+
fatalError("zero not allowed")
32+
case 1, 2:
33+
return 1
34+
default:
35+
return calculate(n - 1) + calculate(n - 2)
36+
}
37+
}
38+
39+
func calculateWithResult(_ n: UInt64) -> FibonacciResult {
40+
let value = calculate(n)
41+
return FibonacciResult(value: value, input: n, timestamp: Date())
42+
}
43+
}

swift/Sources/Memory.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import Synchronization
2+
3+
class MemoryManager {
4+
private let buffers = Mutex<[[UInt8]]>([])
5+
6+
@inline(never)
7+
func allocateChunk(size: Int) {
8+
buffers.withLock { buffers in
9+
buffers.append(Array(repeating: 0, count: size))
10+
}
11+
}
12+
13+
var totalMemory: Int {
14+
buffers.withLock { buffers in
15+
buffers.reduce(0) { $0 + $1.count }
16+
}
17+
}
18+
}

swift/Sources/ParcaDemo.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import ArgumentParser
2+
import Foundation
3+
import Logging
4+
import Synchronization
5+
6+
@main
7+
struct Repeat: AsyncParsableCommand {
8+
static let logger = Logger(label: "dev.parca-demo.swift")
9+
10+
@Option(help: "Fibonacci number to calculate")
11+
var fibNumber: UInt64 = 42
12+
13+
@Option(help: "Memory allocation size in KB")
14+
var memSize: Int = 20
15+
16+
func run() async throws {
17+
let calculator = FibonacciCalculator(logger: Self.logger)
18+
let memoryManager = MemoryManager()
19+
20+
let cpu = Task.detached {
21+
while true {
22+
let result = calculator.calculateWithResult(self.fibNumber)
23+
Self.logger.info("CPU result: \(result.json)")
24+
try await Task.sleep(for: .seconds(1))
25+
}
26+
}
27+
28+
let mem = Task.detached {
29+
while true {
30+
Self.logger.info("Allocating memory...")
31+
memoryManager.allocateChunk(size: self.memSize * 1024)
32+
Self.logger.info("Total memory: \(memoryManager.totalMemory)")
33+
try await Task.sleep(for: .seconds(1))
34+
}
35+
}
36+
37+
let foundation = Task.detached {
38+
while true {
39+
let date = Date()
40+
let formatter = DateFormatter()
41+
formatter.dateStyle = .full
42+
let _ = formatter.string(from: date)
43+
44+
let uuid = UUID()
45+
let _ = URL(string: "https://example.com/\(uuid.uuidString)")
46+
47+
Self.logger.info("Foundation operations completed")
48+
try await Task.sleep(for: .seconds(2))
49+
}
50+
}
51+
52+
_ = await cpu.result
53+
_ = await mem.result
54+
_ = await foundation.result
55+
}
56+
}

swift/deployment.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
labels:
5+
app.kubernetes.io/name: demo-swift
6+
name: swift
7+
namespace: parca
8+
spec:
9+
replicas: 3
10+
selector:
11+
matchLabels:
12+
app.kubernetes.io/name: demo-swift
13+
template:
14+
metadata:
15+
labels:
16+
app.kubernetes.io/name: demo-swift
17+
spec:
18+
containers:
19+
- image: parca-demo:swift
20+
name: rust
21+
resources:
22+
limits:
23+
cpu: '100m'
24+
memory: '256Mi'

0 commit comments

Comments
 (0)