forked from apple/swift-nio-ssl
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add allocation counter tests. (apple#153)
Motivation: To do proper performance work it's necessary to have insight into what programs are actually doing. One of the two major axes the SwiftNIO project uses to measure is heap allocations. Currently swift-nio-ssl has no introspection of heap allocations, so doing informed performance work is very hard. Modifications: - Added the allocation counter test framework. - Added two allocation counter tests. Result: Better idea of what's going on in heap allocations.
- Loading branch information
Showing
6 changed files
with
320 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
#!/bin/bash | ||
##===----------------------------------------------------------------------===## | ||
## | ||
## This source file is part of the SwiftNIO open source project | ||
## | ||
## Copyright (c) 2019 Apple Inc. and the SwiftNIO project authors | ||
## Licensed under Apache License v2.0 | ||
## | ||
## See LICENSE.txt for license information | ||
## See CONTRIBUTORS.txt for the list of SwiftNIO project authors | ||
## | ||
## SPDX-License-Identifier: Apache-2.0 | ||
## | ||
##===----------------------------------------------------------------------===## |
52 changes: 52 additions & 0 deletions
52
IntegrationTests/tests_02_allocation_counters/test_01_allocation_counts.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
#!/bin/bash | ||
##===----------------------------------------------------------------------===## | ||
## | ||
## This source file is part of the SwiftNIO open source project | ||
## | ||
## Copyright (c) 2017-2019 Apple Inc. and the SwiftNIO project authors | ||
## Licensed under Apache License v2.0 | ||
## | ||
## See LICENSE.txt for license information | ||
## See CONTRIBUTORS.txt for the list of SwiftNIO project authors | ||
## | ||
## SPDX-License-Identifier: Apache-2.0 | ||
## | ||
##===----------------------------------------------------------------------===## | ||
|
||
source defines.sh | ||
|
||
set -eu | ||
here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" | ||
|
||
all_tests=() | ||
for file in "$here/test_01_resources/"test_*.swift; do | ||
test_name=$(basename "$file") | ||
test_name=${test_name#test_*} | ||
test_name=${test_name%*.swift} | ||
all_tests+=( "$test_name" ) | ||
done | ||
|
||
"$here/test_01_resources/run-nio-ssl-alloc-counter-tests.sh" -t "$tmp" > "$tmp/output" | ||
|
||
for test in "${all_tests[@]}"; do | ||
cat "$tmp/output" # helps debugging | ||
total_allocations=$(grep "^test_$test.total_allocations:" "$tmp/output" | cut -d: -f2 | sed 's/ //g') | ||
not_freed_allocations=$(grep "^test_$test.remaining_allocations:" "$tmp/output" | cut -d: -f2 | sed 's/ //g') | ||
max_allowed_env_name="MAX_ALLOCS_ALLOWED_$test" | ||
|
||
info "$test: allocations not freed: $not_freed_allocations" | ||
info "$test: total number of mallocs: $total_allocations" | ||
|
||
assert_less_than "$not_freed_allocations" 5 # allow some slack | ||
assert_greater_than "$not_freed_allocations" -5 # allow some slack | ||
assert_greater_than "$total_allocations" 1000 | ||
if [[ -z "${!max_allowed_env_name+x}" ]]; then | ||
if [[ -z "${!max_allowed_env_name+x}" ]]; then | ||
warn "no reference number of allocations set (set to \$$max_allowed_env_name)" | ||
warn "to set current number:" | ||
warn " export $max_allowed_env_name=$total_allocations" | ||
fi | ||
else | ||
assert_less_than_or_equal "$total_allocations" "${!max_allowed_env_name}" | ||
fi | ||
done |
53 changes: 53 additions & 0 deletions
53
...onTests/tests_02_allocation_counters/test_01_resources/run-nio-ssl-alloc-counter-tests.sh
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
#!/bin/bash | ||
##===----------------------------------------------------------------------===## | ||
## | ||
## This source file is part of the SwiftNIO open source project | ||
## | ||
## Copyright (c) 2019 Apple Inc. and the SwiftNIO project authors | ||
## Licensed under Apache License v2.0 | ||
## | ||
## See LICENSE.txt for license information | ||
## See CONTRIBUTORS.txt for the list of SwiftNIO project authors | ||
## | ||
## SPDX-License-Identifier: Apache-2.0 | ||
## | ||
##===----------------------------------------------------------------------===## | ||
|
||
set -eu | ||
here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" | ||
|
||
tmp_dir="/tmp" | ||
|
||
while getopts "t:" opt; do | ||
case "$opt" in | ||
t) | ||
tmp_dir="$OPTARG" | ||
;; | ||
*) | ||
exit 1 | ||
;; | ||
esac | ||
done | ||
|
||
nio_checkout=$(mktemp -d "$tmp_dir/.swift-nio_XXXXXX") | ||
( | ||
cd "$nio_checkout" | ||
git clone --depth 1 https://github.com/apple/swift-nio | ||
) | ||
|
||
shift $((OPTIND-1)) | ||
|
||
tests_to_run=("$here"/test_*.swift) | ||
|
||
if [[ $# -gt 0 ]]; then | ||
tests_to_run=("$@") | ||
fi | ||
|
||
"$nio_checkout/swift-nio/IntegrationTests/allocation-counter-tests-framework/run-allocation-counter.sh" \ | ||
-p "$here/../../.." \ | ||
-m NIO \ | ||
-m NIOSSL \ | ||
-s "$here/shared.swift" \ | ||
-t "$tmp_dir" \ | ||
-d <( echo '.package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0"),' ) \ | ||
"${tests_to_run[@]}" |
99 changes: 99 additions & 0 deletions
99
IntegrationTests/tests_02_allocation_counters/test_01_resources/shared.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the SwiftNIO open source project | ||
// | ||
// Copyright (c) 2019 Apple Inc. and the SwiftNIO project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
import Foundation | ||
import NIO | ||
import NIOSSL | ||
|
||
class BackToBackEmbeddedChannel { | ||
private(set) var client: EmbeddedChannel | ||
private(set) var server: EmbeddedChannel | ||
private var loop: EmbeddedEventLoop | ||
|
||
|
||
init() { | ||
self.loop = EmbeddedEventLoop() | ||
self.client = EmbeddedChannel(loop: self.loop) | ||
self.server = EmbeddedChannel(loop: self.loop) | ||
} | ||
|
||
func run() { | ||
self.loop.run() | ||
} | ||
|
||
func interactInMemory() throws { | ||
var workToDo = true | ||
|
||
while workToDo { | ||
workToDo = false | ||
|
||
self.loop.run() | ||
let clientDatum = try self.client.readOutbound(as: IOData.self) | ||
let serverDatum = try self.server.readOutbound(as: IOData.self) | ||
|
||
if let clientMsg = clientDatum { | ||
try self.server.writeInbound(clientMsg) | ||
workToDo = true | ||
} | ||
|
||
if let serverMsg = serverDatum { | ||
try self.client.writeInbound(serverMsg) | ||
workToDo = true | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
extension BackToBackEmbeddedChannel { | ||
enum Error: Swift.Error { | ||
case nonCleanExit | ||
} | ||
} | ||
|
||
|
||
extension NIOSSLCertificate { | ||
static func forTesting() throws -> NIOSSLCertificate { | ||
return try .init(bytes: certificatePemBytes, format: .pem) | ||
} | ||
} | ||
|
||
|
||
extension NIOSSLPrivateKey { | ||
static func forTesting() throws -> NIOSSLPrivateKey { | ||
return try .init(bytes: keyPemBytes, format: .pem) | ||
} | ||
} | ||
|
||
|
||
fileprivate let certificatePemBytes = Array(""" | ||
-----BEGIN CERTIFICATE----- | ||
MIIBTzCB9qADAgECAhQkvv72Je/v+B/cgJ53f84O82z6WTAKBggqhkjOPQQDAjAU | ||
MRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTkxMTI3MTAxMjMwWhcNMjkxMTI0MTAx | ||
MjMwWjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwWTATBgcqhkjOPQIBBggqhkjOPQMB | ||
BwNCAAShtZ9TRt7I+7Y0o99XUkrgSYmUmpr4K8CB0IkTCX6b1tXp3Xqs1V5BckTd | ||
qrls+zsm3AfeiNBb9EDdxiX9DdzuoyYwJDAUBgNVHREEDTALgglsb2NhbGhvc3Qw | ||
DAYDVR0TAQH/BAIwADAKBggqhkjOPQQDAgNIADBFAiAKxYON+YTnIHNR0R6SLP8R | ||
R7hjsjV5NDs18XLoeRnA1gIhANwyggmE6NQW/r9l59fexj/ZrjaS3jYOTNCfC1Lo | ||
5NgJ | ||
-----END CERTIFICATE----- | ||
""".utf8) | ||
|
||
|
||
fileprivate let keyPemBytes = Array(""" | ||
-----BEGIN PRIVATE KEY----- | ||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgCn182hBmYVMAiNPO | ||
+7w05F40SlAqqxgBEYJZOeK47aihRANCAAShtZ9TRt7I+7Y0o99XUkrgSYmUmpr4 | ||
K8CB0IkTCX6b1tXp3Xqs1V5BckTdqrls+zsm3AfeiNBb9EDdxiX9Ddzu | ||
-----END PRIVATE KEY----- | ||
""".utf8) |
55 changes: 55 additions & 0 deletions
55
IntegrationTests/tests_02_allocation_counters/test_01_resources/test_many_writes.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the SwiftNIO open source project | ||
// | ||
// Copyright (c) 2019 Apple Inc. and the SwiftNIO project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import NIO | ||
import NIOSSL | ||
|
||
func run(identifier: String) { | ||
let serverContext = try! NIOSSLContext(configuration: .forServer(certificateChain: [.certificate(.forTesting())], privateKey: .privateKey(.forTesting()))) | ||
let clientContext = try! NIOSSLContext(configuration: .forClient(trustRoots: .certificates([.forTesting()]))) | ||
|
||
let dummyAddress = try! SocketAddress(ipAddress: "1.2.3.4", port: 5678) | ||
let backToBack = BackToBackEmbeddedChannel() | ||
let serverHandler = try! NIOSSLServerHandler(context: serverContext) | ||
let clientHandler = try! NIOSSLClientHandler(context: clientContext, serverHostname: "localhost") | ||
try! backToBack.client.pipeline.addHandler(clientHandler).wait() | ||
try! backToBack.server.pipeline.addHandler(serverHandler).wait() | ||
|
||
// To trigger activation of both channels we use connect(). | ||
try! backToBack.client.connect(to: dummyAddress).wait() | ||
try! backToBack.server.connect(to: dummyAddress).wait() | ||
|
||
try! backToBack.interactInMemory() | ||
|
||
// Let's try 512 bytes. | ||
var buffer = backToBack.client.allocator.buffer(capacity: 512) | ||
buffer.writeBytes(repeatElement(0, count: 512)) | ||
|
||
measure(identifier: identifier) { | ||
for _ in 0..<1000 { | ||
// A vector of 100 writes. | ||
for _ in 0..<100 { | ||
backToBack.client.write(buffer, promise: nil) | ||
} | ||
backToBack.client.flush() | ||
|
||
try! backToBack.interactInMemory() | ||
|
||
// Pull any data out of the server to avoid ballooning in memory. | ||
while let _ = try! backToBack.server.readInbound(as: ByteBuffer.self) { } | ||
} | ||
|
||
return 1000 | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
IntegrationTests/tests_02_allocation_counters/test_01_resources/test_simple_handshake.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
//===----------------------------------------------------------------------===// | ||
// | ||
// This source file is part of the SwiftNIO open source project | ||
// | ||
// Copyright (c) 2019 Apple Inc. and the SwiftNIO project authors | ||
// Licensed under Apache License v2.0 | ||
// | ||
// See LICENSE.txt for license information | ||
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors | ||
// | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
import NIO | ||
import NIOSSL | ||
|
||
func run(identifier: String) { | ||
let serverContext = try! NIOSSLContext(configuration: .forServer(certificateChain: [.certificate(.forTesting())], privateKey: .privateKey(.forTesting()))) | ||
let clientContext = try! NIOSSLContext(configuration: .forClient(trustRoots: .certificates([.forTesting()]))) | ||
|
||
let dummyAddress = try! SocketAddress(ipAddress: "1.2.3.4", port: 5678) | ||
|
||
measure(identifier: identifier) { | ||
for _ in 0..<1000 { | ||
let backToBack = BackToBackEmbeddedChannel() | ||
let serverHandler = try! NIOSSLServerHandler(context: serverContext) | ||
let clientHandler = try! NIOSSLClientHandler(context: clientContext, serverHostname: "localhost") | ||
try! backToBack.client.pipeline.addHandler(clientHandler).wait() | ||
try! backToBack.server.pipeline.addHandler(serverHandler).wait() | ||
|
||
// To trigger activation of both channels we use connect(). | ||
try! backToBack.client.connect(to: dummyAddress).wait() | ||
try! backToBack.server.connect(to: dummyAddress).wait() | ||
|
||
try! backToBack.interactInMemory() | ||
|
||
// Ok, now do shutdown. | ||
backToBack.client.close(promise: nil) | ||
try! backToBack.interactInMemory() | ||
try! backToBack.client.closeFuture.wait() | ||
try! backToBack.server.closeFuture.wait() | ||
} | ||
|
||
return 1000 | ||
} | ||
} |