Create new Performance Execution Driver (#626)

New performance driver is custom built specifically for performance, rather then using quicping.

Also will be compatible with server mode, and baseline support is part of this commit.
This commit is contained in:
Thad House 2020-07-29 16:31:27 -07:00 коммит произвёл GitHub
Родитель 9edde37643
Коммит 091633d726
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
31 изменённых файлов: 3164 добавлений и 353 удалений

Просмотреть файл

@ -40,9 +40,9 @@ stages:
tls: schannel
config: Release
${{ if eq(parameters.mode, 'PGO') }}:
extraBuildArgs: -DisableTest -PGO
extraBuildArgs: -DisableTest -DisableTools -PGO
${{ if ne(parameters.mode, 'PGO') }}:
extraBuildArgs: -DisableTest
extraBuildArgs: -DisableTest -DisableTools
- template: ./templates/build-config-user.yml
parameters:
image: windows-latest
@ -51,9 +51,9 @@ stages:
tls: schannel
config: Release
${{ if eq(parameters.mode, 'PGO') }}:
extraBuildArgs: -DisableTest -PGO
extraBuildArgs: -DisableTest -DisableTools -PGO
${{ if ne(parameters.mode, 'PGO') }}:
extraBuildArgs: -DisableTest
extraBuildArgs: -DisableTest -DisableTools
#
# Performance Tests

Просмотреть файл

@ -101,6 +101,7 @@ endif()
option(QUIC_BUILD_TOOLS "Builds the tools code" ON)
option(QUIC_BUILD_TEST "Builds the test code" ON)
option(QUIC_BUILD_PERF "Builds the perf code" ON)
option(QUIC_ENABLE_LOGGING "Enables logging" ON)
option(QUIC_SANITIZE_ADDRESS "Enables address sanitizer" OFF)
option(QUIC_STATIC_LINK_CRT "Statically links the C runtime" ON)
@ -359,6 +360,12 @@ if(QUIC_BUILD_TOOLS)
add_subdirectory(src/tools)
endif()
# Performance code
if(QUIC_BUILD_PERF)
add_subdirectory(src/perf/lib)
add_subdirectory(src/perf/bin)
endif()
# Test code
if(QUIC_BUILD_TEST)
# Build the googletest framework.

Просмотреть файл

@ -18,6 +18,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msquictest.kernel", "src\te
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "manifest.kernel", "src\manifest\manifest.kernel.vcxproj", "{C1ADB76F-7005-4516-BADB-2A60797EF912}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "perflib.kernel", "src\perf\lib\perflib.kernel.vcxproj", "{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "performance.kernel", "src\perf\bin\performance.kernel.vcxproj", "{2BE64DBF-60E6-4FE8-96B0-5F2526405096}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
@ -174,6 +178,54 @@ Global
{C1ADB76F-7005-4516-BADB-2A60797EF912}.Release|x86.ActiveCfg = Release|Win32
{C1ADB76F-7005-4516-BADB-2A60797EF912}.Release|x86.Build.0 = Release|Win32
{C1ADB76F-7005-4516-BADB-2A60797EF912}.Release|x86.Deploy.0 = Release|Win32
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Debug|ARM.ActiveCfg = Debug|ARM
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Debug|ARM.Build.0 = Debug|ARM
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Debug|ARM.Deploy.0 = Debug|ARM
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Debug|ARM64.ActiveCfg = Debug|ARM64
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Debug|ARM64.Build.0 = Debug|ARM64
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Debug|ARM64.Deploy.0 = Debug|ARM64
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Debug|x64.ActiveCfg = Debug|x64
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Debug|x64.Build.0 = Debug|x64
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Debug|x64.Deploy.0 = Debug|x64
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Debug|x86.ActiveCfg = Debug|Win32
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Debug|x86.Build.0 = Debug|Win32
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Debug|x86.Deploy.0 = Debug|Win32
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Release|ARM.ActiveCfg = Release|ARM
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Release|ARM.Build.0 = Release|ARM
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Release|ARM.Deploy.0 = Release|ARM
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Release|ARM64.ActiveCfg = Release|ARM64
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Release|ARM64.Build.0 = Release|ARM64
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Release|ARM64.Deploy.0 = Release|ARM64
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Release|x64.ActiveCfg = Release|x64
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Release|x64.Build.0 = Release|x64
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Release|x64.Deploy.0 = Release|x64
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Release|x86.ActiveCfg = Release|Win32
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Release|x86.Build.0 = Release|Win32
{11633785-79CC-4C7D-AB6A-AECDF29A1FA7}.Release|x86.Deploy.0 = Release|Win32
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Debug|ARM.ActiveCfg = Debug|ARM
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Debug|ARM.Build.0 = Debug|ARM
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Debug|ARM.Deploy.0 = Debug|ARM
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Debug|ARM64.ActiveCfg = Debug|ARM64
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Debug|ARM64.Build.0 = Debug|ARM64
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Debug|ARM64.Deploy.0 = Debug|ARM64
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Debug|x64.ActiveCfg = Debug|x64
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Debug|x64.Build.0 = Debug|x64
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Debug|x64.Deploy.0 = Debug|x64
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Debug|x86.ActiveCfg = Debug|Win32
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Debug|x86.Build.0 = Debug|Win32
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Debug|x86.Deploy.0 = Debug|Win32
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Release|ARM.ActiveCfg = Release|ARM
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Release|ARM.Build.0 = Release|ARM
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Release|ARM.Deploy.0 = Release|ARM
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Release|ARM64.ActiveCfg = Release|ARM64
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Release|ARM64.Build.0 = Release|ARM64
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Release|ARM64.Deploy.0 = Release|ARM64
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Release|x64.ActiveCfg = Release|x64
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Release|x64.Build.0 = Release|x64
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Release|x64.Deploy.0 = Release|x64
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Release|x86.ActiveCfg = Release|Win32
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Release|x86.Build.0 = Release|Win32
{2BE64DBF-60E6-4FE8-96B0-5F2526405096}.Release|x86.Deploy.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

Просмотреть файл

@ -5,9 +5,9 @@
"Platform": "Windows",
"Tls": ["stub", "schannel", "mitls"],
"Arch": ["x64", "x86", "arm", "arm64"],
"Exe": "quicping",
"Exe": "quicperf",
"Arguments": {
"All": "-listen:* -port:4433 -peer_uni:1 -connections:10",
"All": "-TestName:Throughput -ServerMode:1 -listen:* -port:4433 -peer_uni:1 -connections:10",
"Loopback": "-selfsign:1",
"Remote": "-thumbprint:$Thumbprint -machine_cert:1 -cert_store:My"
}
@ -16,16 +16,16 @@
"Platform": "Windows",
"Tls": ["stub", "schannel", "mitls"],
"Arch": ["x64", "x86", "arm", "arm64"],
"Exe": "quicping",
"Exe": "quicperf",
"Arguments": {
"All": "-target:$RemoteAddress -port:4433 -sendbuf:0 -bind:$LocalAddress:4434 -ip:4 -core:0 -uni:1 -length:2000000000",
"All": "-TestName:Throughput -target:$RemoteAddress -port:4433 -sendbuf:0 -bind:$LocalAddress:4434 -ip:4 -core:0 -uni:1 -length:2000000000",
"Loopback": "",
"Remote": ""
}
},
"Iterations": 10,
"RemoteReadyMatcher": "Ready For Connections!",
"ResultsMatcher": "Closed.*\\(TX.*bytes @ (.*) kbps \\|"
"RemoteReadyMatcher": "Started!",
"ResultsMatcher": "Closed.*\\(TX.*bytes @ (.*) kbps\\)"
},
{
"TestName": "Throughput",
@ -33,9 +33,9 @@
"Platform": "Linux",
"Tls": ["stub", "openssl"],
"Arch": ["x64", "arm"],
"Exe": "quicping",
"Exe": "quicperf",
"Arguments": {
"All": "-listen:* -port:4433 -selfsign:1 -peer_uni:1 -connections:10",
"All": "-TestName:Throughput -ServerMode:1 -listen:* -port:4433 -selfsign:1 -peer_uni:1 -connections:10",
"Loopback": "",
"Remote": ""
}
@ -44,16 +44,16 @@
"Platform": "linux",
"Tls": ["stub", "openssl"],
"Arch": ["x64", "arm"],
"Exe": "quicping",
"Exe": "quicperf",
"Arguments": {
"All": "-target:$RemoteAddress -port:4433 -sendbuf:0 -uni:1 -length:2000000000",
"All": "-TestName:Throughput -target:$RemoteAddress -port:4433 -sendbuf:0 -uni:1 -length:2000000000",
"Loopback": "",
"Remote": ""
}
},
"Iterations": 10,
"RemoteReadyMatcher": "Ready For Connections!",
"ResultsMatcher": "Closed.*\\(TX.*bytes @ (.*) kbps \\|"
"RemoteReadyMatcher": "Started!",
"ResultsMatcher": "Closed.*\\(TX.*bytes @ (.*) kbps\\)"
},
{
"TestName": "ThroughputNoEncryption",
@ -61,9 +61,9 @@
"Platform": "Windows",
"Tls": ["stub", "schannel", "mitls"],
"Arch": ["x64", "x86", "arm", "arm64"],
"Exe": "quicping",
"Exe": "quicperf",
"Arguments": {
"All": "-listen:* -port:4433 -peer_uni:1 -connections:10",
"All": "-TestName:Throughput -ServerMode:1 -listen:* -port:4433 -peer_uni:1 -connections:10",
"Loopback": "-selfsign:1",
"Remote": "-thumbprint:$Thumbprint -machine_cert:1 -cert_store:My"
}
@ -72,16 +72,16 @@
"Platform": "Windows",
"Tls": ["stub", "schannel", "mitls"],
"Arch": ["x64", "x86", "arm", "arm64"],
"Exe": "quicping",
"Exe": "quicperf",
"Arguments": {
"All": "-target:$RemoteAddress -port:4433 -sendbuf:0 -bind:$LocalAddress:4434 -ip:4 -core:0 -uni:1 -encrypt:0 -length:2000000000",
"All": "-TestName:Throughput -target:$RemoteAddress -port:4433 -sendbuf:0 -bind:$LocalAddress:4434 -ip:4 -core:0 -uni:1 -encrypt:0 -length:2000000000",
"Loopback": "",
"Remote": ""
}
},
"Iterations": 10,
"RemoteReadyMatcher": "Ready For Connections!",
"ResultsMatcher": "Closed.*\\(TX.*bytes @ (.*) kbps \\|"
"RemoteReadyMatcher": "Started!",
"ResultsMatcher": "Closed.*\\(TX.*bytes @ (.*) kbps\\)"
},
{
"TestName": "ThroughputNoEncryption",
@ -89,9 +89,9 @@
"Platform": "Linux",
"Tls": ["stub", "openssl"],
"Arch": ["x64", "arm"],
"Exe": "quicping",
"Exe": "quicperf",
"Arguments": {
"All": "-listen:* -port:4433 -selfsign:1 -peer_uni:1 -connections:10",
"All": "-TestName:Throughput -ServerMode:1 -listen:* -port:4433 -selfsign:1 -peer_uni:1 -connections:10",
"Loopback": "",
"Remote": ""
}
@ -100,15 +100,15 @@
"Platform": "linux",
"Tls": ["stub", "openssl"],
"Arch": ["x64", "arm"],
"Exe": "quicping",
"Exe": "quicperf",
"Arguments": {
"All": "-target:$RemoteAddress -port:4433 -sendbuf:0 -uni:1 -encrypt:0 -length:2000000000",
"All": "-TestName:Throughput -target:$RemoteAddress -port:4433 -sendbuf:0 -uni:1 -encrypt:0 -length:2000000000",
"Loopback": "",
"Remote": ""
}
},
"Iterations": 10,
"RemoteReadyMatcher": "Ready For Connections!",
"ResultsMatcher": "Closed.*\\(TX.*bytes @ (.*) kbps \\|"
"RemoteReadyMatcher": "Started!",
"ResultsMatcher": "Closed.*\\(TX.*bytes @ (.*) kbps\\)"
}
]

Просмотреть файл

@ -30,6 +30,9 @@ This script provides helpers for building msquic.
.PARAMETER DisableTest
Don't build the test directory.
.PARAMETER DisablePerf
Don't build the perf directory.
.PARAMETER Clean
Deletes all previous build and configuration.
@ -93,6 +96,9 @@ param (
[Parameter(Mandatory = $false)]
[switch]$DisableTest = $false,
[Parameter(Mandatory = $false)]
[switch]$DisablePerf = $false,
[Parameter(Mandatory = $false)]
[switch]$Clean = $false,
@ -220,6 +226,9 @@ function CMake-Generate {
if ($DisableTest) {
$Arguments += " -DQUIC_BUILD_TEST=off"
}
if ($DisablePerf) {
$Arguments += " -DQUIC_BUILD_PERF=off"
}
if ($IsLinux) {
$Arguments += " -DCMAKE_BUILD_TYPE=" + $Config
}

Просмотреть файл

@ -52,18 +52,111 @@ struct QuicAddr {
template<class T>
class UniquePtr {
T* ptr;
public:
UniquePtr() : ptr(nullptr) { }
UniquePtr(T* _ptr) : ptr(_ptr) { }
~UniquePtr() { delete ptr; }
T* get() { return ptr; }
const T* get() const { return ptr; }
UniquePtr() noexcept = default;
explicit UniquePtr(T* _ptr) : ptr{_ptr} { }
UniquePtr(const UniquePtr& other) = delete;
UniquePtr& operator=(const UniquePtr& other) = delete;
UniquePtr(UniquePtr&& other) noexcept {
this->ptr = other->ptr;
other->ptr = nullptr;
}
UniquePtr& operator=(UniquePtr&& other) noexcept {
if (this->ptr) {
delete this->ptr;
}
this->ptr = other->ptr;
other->ptr = nullptr;
}
~UniquePtr() noexcept {
if (this->ptr) {
delete this->ptr;
}
}
void reset(T* lptr) noexcept {
if (this->ptr) {
delete this->ptr;
}
this->ptr = lptr;
}
T* release() noexcept {
T* tmp = ptr;
ptr = nullptr;
return tmp;
}
T* get() const noexcept { return ptr; }
T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
operator bool() const { return ptr != nullptr; }
bool operator == (T* _ptr) const { return ptr == _ptr; }
bool operator != (T* _ptr) const { return ptr != _ptr; }
T* operator->() const noexcept { return ptr; }
operator bool() const noexcept { return ptr != nullptr; }
bool operator == (T* _ptr) const noexcept { return ptr == _ptr; }
bool operator != (T* _ptr) const noexcept { return ptr != _ptr; }
private:
T* ptr = nullptr;
};
template<typename T>
class UniquePtr<T[]> {
public:
UniquePtr() noexcept = default;
explicit UniquePtr(T* _ptr) : ptr{_ptr} { }
UniquePtr(const UniquePtr& other) = delete;
UniquePtr& operator=(const UniquePtr& other) = delete;
UniquePtr(UniquePtr&& other) noexcept {
this->ptr = other->ptr;
other->ptr = nullptr;
}
UniquePtr& operator=(UniquePtr&& other) noexcept {
if (this->ptr) {
delete[] this->ptr;
}
this->ptr = other->ptr;
other->ptr = nullptr;
}
~UniquePtr() noexcept {
if (this->ptr) {
delete[] this->ptr;
}
}
void reset(T* _ptr) noexcept {
if (this->ptr) {
delete[] this->ptr;
}
this->ptr = _ptr;
}
T* release() noexcept {
T* tmp = ptr;
ptr = nullptr;
return tmp;
}
T* get() const noexcept { return ptr; }
T& operator[](size_t i) const {
return *(ptr + i);
}
operator bool() const noexcept { return ptr != nullptr; }
bool operator == (T* _ptr) const noexcept { return ptr == _ptr; }
bool operator != (T* _ptr) const noexcept { return ptr != _ptr; }
private:
T* ptr = nullptr;
};
template<class T>
@ -84,18 +177,21 @@ public:
class QuicApiTable : public QUIC_API_TABLE {
QUIC_STATUS Init;
const QUIC_API_TABLE* ApiTable{nullptr};
public:
QuicApiTable() {
const QUIC_API_TABLE* table;
if (QUIC_SUCCEEDED(Init = MsQuicOpen(&table))) {
if (QUIC_SUCCEEDED(Init = MsQuicOpen(&ApiTable))) {
QUIC_API_TABLE* thisTable = this;
QuicCopyMemory(thisTable, table, sizeof(*table));
QuicCopyMemory(thisTable, ApiTable, sizeof(*ApiTable));
}
}
~QuicApiTable() {
if (QUIC_SUCCEEDED(Init)) {
MsQuicClose(this);
MsQuicClose(ApiTable);
ApiTable = nullptr;
QUIC_API_TABLE* thisTable = this;
QuicZeroMemory(thisTable, sizeof(*thisTable));
}
}
@ -106,10 +202,15 @@ public:
class MsQuicRegistration {
HQUIC Registration;
QUIC_STATUS InitStatus;
public:
MsQuicRegistration() {
QuicZeroMemory(&Registration, sizeof(Registration));
if (QUIC_FAILED(MsQuic->RegistrationOpen(nullptr, &Registration))) {
if (QUIC_FAILED(
InitStatus =
MsQuic->RegistrationOpen(
nullptr,
&Registration))) {
Registration = nullptr;
}
}
@ -118,29 +219,58 @@ public:
MsQuic->RegistrationClose(Registration);
}
}
QUIC_STATUS GetInitStatus() const { return InitStatus; }
bool IsValid() const { return Registration != nullptr; }
MsQuicRegistration(MsQuicRegistration& other) = delete;
MsQuicRegistration operator=(MsQuicRegistration& Other) = delete;
operator HQUIC () {
operator HQUIC () const {
return Registration;
}
};
struct MsQuicSession {
HQUIC Handle;
bool CloseAllConnectionsOnDelete;
class MsQuicSession {
bool CloseAllConnectionsOnDelete {false};
QUIC_STATUS InitStatus;
public:
HQUIC Handle {nullptr};
MsQuicSession(
_In_ const MsQuicRegistration& Reg,
_In_z_ const char* RawAlpn = "MsQuicTest")
: Handle(nullptr), CloseAllConnectionsOnDelete(false) {
if (!Reg.IsValid()) {
InitStatus = Reg.GetInitStatus();
return;
}
QUIC_BUFFER Alpn;
Alpn.Buffer = (uint8_t*)RawAlpn;
Alpn.Length = (uint32_t)strlen(RawAlpn);
if (QUIC_FAILED(
InitStatus =
MsQuic->SessionOpen(
Reg,
&Alpn,
1,
nullptr,
&Handle))) {
Handle = nullptr;
}
}
#ifndef QUIC_SKIP_GLOBAL_CONSTRUCTORS
MsQuicSession(_In_z_ const char* RawAlpn = "MsQuicTest")
: Handle(nullptr), CloseAllConnectionsOnDelete(false) {
QUIC_BUFFER Alpn;
Alpn.Buffer = (uint8_t*)RawAlpn;
Alpn.Length = (uint32_t)strlen(RawAlpn);
if (QUIC_FAILED(
MsQuic->SessionOpen(
Registration,
&Alpn,
1,
nullptr,
&Handle))) {
InitStatus =
MsQuic->SessionOpen(
Registration,
&Alpn,
1,
nullptr,
&Handle))) {
Handle = nullptr;
}
}
@ -152,15 +282,17 @@ struct MsQuicSession {
Alpns[1].Buffer = (uint8_t*)RawAlpn2;
Alpns[1].Length = (uint32_t)strlen(RawAlpn2);
if (QUIC_FAILED(
MsQuic->SessionOpen(
Registration,
Alpns,
ARRAYSIZE(Alpns),
nullptr,
&Handle))) {
InitStatus =
MsQuic->SessionOpen(
Registration,
Alpns,
ARRAYSIZE(Alpns),
nullptr,
&Handle))) {
Handle = nullptr;
}
}
#endif
~MsQuicSession() {
if (Handle != nullptr) {
if (CloseAllConnectionsOnDelete) {
@ -172,12 +304,13 @@ struct MsQuicSession {
MsQuic->SessionClose(Handle);
}
}
QUIC_STATUS GetInitStatus() const { return InitStatus; }
bool IsValid() const {
return Handle != nullptr;
}
MsQuicSession(MsQuicSession& other) = delete;
MsQuicSession operator=(MsQuicSession& Other) = delete;
operator HQUIC () {
operator HQUIC () const {
return Handle;
}
void SetAutoCleanup() {
@ -289,11 +422,71 @@ struct MsQuicSession {
}
};
struct MsQuicListener {
HQUIC Handle { nullptr };
QUIC_STATUS InitStatus;
QUIC_LISTENER_CALLBACK_HANDLER Handler { nullptr };
void* Context{ nullptr };
MsQuicListener(const MsQuicSession& Session) {
if (!Session.IsValid()) {
InitStatus = Session.GetInitStatus();
return;
}
if (QUIC_FAILED(
InitStatus =
MsQuic->ListenerOpen(
Session,
[](HQUIC Handle, void* Context, QUIC_LISTENER_EVENT* Event) -> QUIC_STATUS {
MsQuicListener* Listener = (MsQuicListener*)Context;
return Listener->Handler(Handle, Listener->Context, Event);
},
this,
&Handle))) {
Handle = nullptr;
}
}
~MsQuicListener() noexcept {
if (Handler != nullptr) {
MsQuic->ListenerStop(Handle);
}
if (Handle) {
MsQuic->ListenerClose(Handle);
}
}
QUIC_STATUS
Start(
_In_ QUIC_ADDR* Address,
_In_ QUIC_LISTENER_CALLBACK_HANDLER _Handler,
_In_ void* _Context) {
Handler = _Handler;
Context = _Context;
return MsQuic->ListenerStart(Handle, Address);
}
QUIC_STATUS
ListenerCallback(HQUIC Listener, QUIC_LISTENER_EVENT* Event) {
return Handler(Listener, Context, Event);
}
QUIC_STATUS GetInitStatus() const { return InitStatus; }
bool IsValid() const {
return Handle != nullptr;
}
MsQuicListener(MsQuicListener& other) = delete;
MsQuicListener operator=(MsQuicListener& Other) = delete;
operator HQUIC () const {
return Handle;
}
};
struct ListenerScope {
HQUIC Handle;
ListenerScope() : Handle(nullptr) { }
ListenerScope(HQUIC handle) : Handle(handle) { }
~ListenerScope() { if (Handle) { MsQuic->ListenerClose(Handle); } }
operator HQUIC() const { return Handle; }
};
struct ConnectionScope {
@ -301,6 +494,7 @@ struct ConnectionScope {
ConnectionScope() : Handle(nullptr) { }
ConnectionScope(HQUIC handle) : Handle(handle) { }
~ConnectionScope() { if (Handle) { MsQuic->ConnectionClose(Handle); } }
operator HQUIC() const { return Handle; }
};
struct StreamScope {
@ -308,6 +502,7 @@ struct StreamScope {
StreamScope() : Handle(nullptr) { }
StreamScope(HQUIC handle) : Handle(handle) { }
~StreamScope() { if (Handle) { MsQuic->StreamClose(Handle); } }
operator HQUIC() const { return Handle; }
};
struct EventScope {
@ -315,6 +510,7 @@ struct EventScope {
EventScope() { QuicEventInitialize(&Handle, FALSE, FALSE); }
EventScope(QUIC_EVENT event) : Handle(event) { }
~EventScope() { QuicEventUninitialize(Handle); }
operator QUIC_EVENT() const { return Handle; }
};
struct QuicBufferScope {

Просмотреть файл

@ -20,6 +20,7 @@ Environment:
#define _MSQUIC_LINUX_
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <string.h>

Просмотреть файл

@ -284,4 +284,58 @@ QuicAddrHash(
#define QUIC_LOCALHOST_FOR_AF(Af) "localhost"
inline
BOOLEAN
QuicAddrFromString(
_In_z_ const char* AddrStr,
_In_ uint16_t Port, // Host byte order
_Out_ QUIC_ADDR* Addr
)
{
Addr->Ipv4.sin_port = QuicNetByteSwapShort(Port);
if (RtlIpv4StringToAddressExA(AddrStr, FALSE, &Addr->Ipv4.sin_addr, &Addr->Ipv4.sin_port) == STATUS_SUCCESS) {
Addr->si_family = AF_INET;
} else if (RtlIpv6StringToAddressExA(AddrStr, &Addr->Ipv6.sin6_addr, &Addr->Ipv6.sin6_scope_id, &Addr->Ipv6.sin6_port) == STATUS_SUCCESS) {
Addr->si_family = AF_INET6;
} else {
return FALSE;
}
return TRUE;
}
//
// Represents an IP address and (optionally) port number as a string.
//
typedef struct QUIC_ADDR_STR {
char Address[64];
} QUIC_ADDR_STR;
inline
BOOLEAN
QuicAddrToString(
_In_ const QUIC_ADDR* Addr,
_Out_ QUIC_ADDR_STR* AddrStr
)
{
LONG Status;
ULONG AddrStrLen = ARRAYSIZE(AddrStr->Address);
if (Addr->si_family == AF_INET) {
Status =
RtlIpv4AddressToStringExA(
&Addr->Ipv4.sin_addr,
Addr->Ipv4.sin_port,
AddrStr->Address,
&AddrStrLen);
} else {
Status =
RtlIpv6AddressToStringExA(
&Addr->Ipv6.sin6_addr,
0,
Addr->Ipv6.sin6_port,
AddrStr->Address,
&AddrStrLen);
}
return Status == STATUS_SUCCESS;
}
#endif // _MSQUIC_WINKERNEL_

Просмотреть файл

@ -54,6 +54,7 @@ QuicStatusToString(
case QUIC_STATUS_CONNECTION_REFUSED: return "CONNECTION_REFUSED";
case QUIC_STATUS_PROTOCOL_ERROR: return "PROTOCOL_ERROR";
case QUIC_STATUS_VER_NEG_ERROR: return "VER_NEG_ERROR";
case QUIC_STATUS_PENDING: return "PENDING";
}
return "UNKNOWN";
@ -158,6 +159,10 @@ ConvertArgToAddress(
)
{
if (strcmp("*", Arg) == 0) {
//
// Explicitly zero, otherwise kernel mode errors
//
QuicZeroMemory(Address, sizeof(*Address));
QuicAddrSetFamily(Address, AF_UNSPEC);
QuicAddrSetPort(Address, Port);
return TRUE;

Просмотреть файл

@ -0,0 +1,380 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
Abstract:
This file contains helpers for interacting with a kernel mode driver service.
--*/
#pragma once
#define QUIC_TEST_APIS 1
#include "quic_platform.h"
#include "quic_trace.h"
#ifdef _WIN32
#define QUIC_DRIVER_FILE_NAME QUIC_DRIVER_NAME ".sys"
#define QUIC_IOCTL_PATH "\\\\.\\\\" QUIC_DRIVER_NAME
class QuicDriverService {
SC_HANDLE ScmHandle;
SC_HANDLE ServiceHandle;
public:
QuicDriverService() :
ScmHandle(nullptr),
ServiceHandle(nullptr) {
}
bool Initialize() {
uint32_t Error;
ScmHandle = OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
if (ScmHandle == nullptr) {
Error = GetLastError();
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"GetFullPathName failed");
return false;
}
QueryService:
ServiceHandle =
OpenServiceA(
ScmHandle,
QUIC_DRIVER_NAME,
SERVICE_ALL_ACCESS);
if (ServiceHandle == nullptr) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
GetLastError(),
"OpenService failed");
char DriverFilePath[MAX_PATH] = {0};
GetModuleFileNameA(NULL, DriverFilePath, MAX_PATH);
char* PathEnd = strrchr(DriverFilePath, '\\');
if (!PathEnd ||
sizeof(DriverFilePath) - (PathEnd - DriverFilePath) < sizeof(QUIC_DRIVER_FILE_NAME)) {
QuicTraceEvent(
LibraryError,
"[ lib] ERROR, %s.",
"Can't build " QUIC_DRIVER_FILE_NAME " full path");
return false;
}
memcpy(PathEnd + 1, QUIC_DRIVER_FILE_NAME, sizeof(QUIC_DRIVER_FILE_NAME));
if (GetFileAttributesA(DriverFilePath) == INVALID_FILE_ATTRIBUTES) {
QuicTraceEvent(
LibraryError,
"[ lib] ERROR, %s.",
"Failed to find " QUIC_DRIVER_FILE_NAME);
return false;
}
ServiceHandle =
CreateServiceA(
ScmHandle,
QUIC_DRIVER_NAME,
QUIC_DRIVER_NAME,
SC_MANAGER_ALL_ACCESS,
SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
DriverFilePath,
nullptr,
nullptr,
"msquic\0",
nullptr,
nullptr);
if (ServiceHandle == nullptr) {
Error = GetLastError();
if (Error == ERROR_SERVICE_EXISTS) {
goto QueryService;
}
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"CreateService failed");
return false;
}
}
return true;
}
void Uninitialize() {
if (ServiceHandle != nullptr) {
CloseServiceHandle(ServiceHandle);
}
if (ScmHandle != nullptr) {
CloseServiceHandle(ScmHandle);
}
}
bool Start() {
if (!StartServiceA(ServiceHandle, 0, nullptr)) {
uint32_t Error = GetLastError();
if (Error != ERROR_SERVICE_ALREADY_RUNNING) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"StartService failed");
return false;
}
}
return true;
}
};
class QuicDriverClient {
HANDLE DeviceHandle;
public:
QuicDriverClient() : DeviceHandle(INVALID_HANDLE_VALUE) { }
bool Initialize(
_In_ QUIC_SEC_CONFIG_PARAMS* SecConfigParams
) {
uint32_t Error;
DeviceHandle =
CreateFileA(
QUIC_IOCTL_PATH,
GENERIC_READ | GENERIC_WRITE,
0,
nullptr, // no SECURITY_ATTRIBUTES structure
OPEN_EXISTING, // No special create flags
FILE_FLAG_OVERLAPPED, // Allow asynchronous requests
nullptr);
if (DeviceHandle == INVALID_HANDLE_VALUE) {
Error = GetLastError();
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"CreateFile failed");
return false;
}
if (!Run(IOCTL_QUIC_SEC_CONFIG, SecConfigParams->Thumbprint, sizeof(SecConfigParams->Thumbprint), 30000)) {
CloseHandle(DeviceHandle);
DeviceHandle = INVALID_HANDLE_VALUE;
QuicTraceEvent(
LibraryError,
"[ lib] ERROR, %s.",
"Run(IOCTL_QUIC_SEC_CONFIG) failed");
return false;
}
return true;
}
void Uninitialize() {
if (DeviceHandle != INVALID_HANDLE_VALUE) {
CloseHandle(DeviceHandle);
}
}
bool Run(
_In_ uint32_t IoControlCode,
_In_reads_bytes_opt_(InBufferSize)
void* InBuffer,
_In_ uint32_t InBufferSize,
_In_ uint32_t TimeoutMs = 30000
) {
uint32_t Error;
OVERLAPPED Overlapped = { 0 };
Overlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (Overlapped.hEvent == nullptr) {
Error = GetLastError();
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"CreateEvent failed");
return false;
}
QuicTraceLogVerbose(
TestSendIoctl,
"[test] Sending IOCTL %u with %u bytes.",
IoGetFunctionCodeFromCtlCode(IoControlCode),
InBufferSize);
if (!DeviceIoControl(
DeviceHandle,
IoControlCode,
InBuffer, InBufferSize,
nullptr, 0,
nullptr,
&Overlapped)) {
Error = GetLastError();
if (Error != ERROR_IO_PENDING) {
CloseHandle(Overlapped.hEvent);
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"DeviceIoControl failed");
return false;
}
}
DWORD dwBytesReturned;
if (!GetOverlappedResultEx(
DeviceHandle,
&Overlapped,
&dwBytesReturned,
TimeoutMs,
FALSE)) {
Error = GetLastError();
if (Error == WAIT_TIMEOUT) {
Error = ERROR_TIMEOUT;
CancelIoEx(DeviceHandle, &Overlapped);
}
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"GetOverlappedResultEx failed");
} else {
Error = ERROR_SUCCESS;
}
CloseHandle(Overlapped.hEvent);
return Error == ERROR_SUCCESS;
}
bool Run(
_In_ uint32_t IoControlCode,
_In_ uint32_t TimeoutMs = 30000
) {
return Run(IoControlCode, nullptr, 0, TimeoutMs);
}
template<class T>
bool Run(
_In_ uint32_t IoControlCode,
_In_ const T& Data,
_In_ uint32_t TimeoutMs = 30000
) {
return Run(IoControlCode, (void*)&Data, sizeof(Data), TimeoutMs);
}
bool Read(
_In_ uint32_t IoControlCode,
_Out_writes_bytes_opt_(OutBufferSize)
void* OutBuffer,
_In_ uint32_t OutBufferSize,
_Out_opt_ uint32_t* OutBufferWritten,
_In_ uint32_t TimeoutMs = 30000
) {
uint32_t Error;
OVERLAPPED Overlapped = { 0 };
Overlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (!Overlapped.hEvent) {
Error = GetLastError();
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"CreateEvent failed");
return false;
}
QuicTraceLogVerbose(
TestSendIoctl,
"[test] Sending IOCTL %u.",
IoGetFunctionCodeFromCtlCode(IoControlCode));
if (!DeviceIoControl(
DeviceHandle,
IoControlCode,
nullptr, 0,
OutBuffer, OutBufferSize,
nullptr,
&Overlapped)) {
Error = GetLastError();
if (Error != ERROR_IO_PENDING) {
CloseHandle(Overlapped.hEvent);
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"DeviceIoControl failed");
return false;
}
}
DWORD dwBytesReturned;
if (!GetOverlappedResultEx(
DeviceHandle,
&Overlapped,
&dwBytesReturned,
TimeoutMs,
FALSE)) {
Error = GetLastError();
if (Error == WAIT_TIMEOUT) {
Error = ERROR_TIMEOUT;
CancelIoEx(DeviceHandle, &Overlapped);
} else {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"GetOverlappedResultEx failed");
}
} else {
Error = ERROR_SUCCESS;
*OutBufferWritten = dwBytesReturned;
}
CloseHandle(Overlapped.hEvent);
return Error == ERROR_SUCCESS;
}
};
#else
class QuicDriverService {
public:
bool Initialize() { return false; }
void Uninitialize() { }
bool Start() { return false; }
};
class QuicDriverClient {
public:
bool Initialize(
_In_ QUIC_SEC_CONFIG_PARAMS* SecConfigParams
) {
UNREFERENCED_PARAMETER(SecConfigParams);
return false;
}
void Uninitialize() { }
bool Run(
_In_ uint32_t IoControlCode,
_In_ void* InBuffer,
_In_ uint32_t InBufferSize,
_In_ uint32_t TimeoutMs = 30000
) {
UNREFERENCED_PARAMETER(IoControlCode);
UNREFERENCED_PARAMETER(InBuffer);
UNREFERENCED_PARAMETER(InBufferSize);
UNREFERENCED_PARAMETER(TimeoutMs);
return false;
}
bool
Run(
_In_ uint32_t IoControlCode,
_In_ uint32_t TimeoutMs = 30000
) {
return Run(IoControlCode, nullptr, 0, TimeoutMs);
}
template<class T>
bool
Run(
_In_ uint32_t IoControlCode,
_In_ const T& Data,
_In_ uint32_t TimeoutMs = 30000
) {
return Run(IoControlCode, (void*)&Data, sizeof(Data), TimeoutMs);
}
bool Read(
_In_ uint32_t IoControlCode,
_Out_writes_bytes_opt_(OutBufferSize)
void* OutBuffer,
_In_ uint32_t OutBufferSize,
_Out_ uint32_t* OutBufferWritten,
_In_ uint32_t TimeoutMs = 30000
) {
UNREFERENCED_PARAMETER(IoControlCode);
UNREFERENCED_PARAMETER(OutBuffer);
UNREFERENCED_PARAMETER(OutBufferSize);
UNREFERENCED_PARAMETER(OutBufferWritten);
UNREFERENCED_PARAMETER(TimeoutMs);
return false;
}
};
#endif

Просмотреть файл

@ -0,0 +1,30 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
Abstract:
QUIC Perf Main common driver code.
--*/
#pragma once
#include "quic_platform.h"
#include "PerfHelpers.h"
extern
QUIC_STATUS
QuicMainStart(
_In_ int argc,
_In_reads_(argc) _Null_terminated_ char* argv[],
_In_ QUIC_EVENT StopEvent,
_In_ PerfSelfSignedConfiguration* SelfSignedConfig
);
extern
QUIC_STATUS
QuicMainStop(
_In_ int Timeout
);

Просмотреть файл

@ -929,6 +929,8 @@ NdisSetThreadObjectCompartmentId(
IN NET_IF_COMPARTMENT_ID CompartmentId
);
#define QuicSetCurrentThreadAffinityMask(Mask) KeSetSystemAffinityThreadEx(Mask)
#define QuicCompartmentIdGetCurrent() NdisGetThreadObjectCompartmentId(PsGetCurrentThread())
#define QuicCompartmentIdSetCurrent(CompartmentId) \
NdisSetThreadObjectCompartmentId(PsGetCurrentThread(), CompartmentId)

Просмотреть файл

@ -899,6 +899,8 @@ QuicRandom(
#define QUIC_UNSPECIFIED_COMPARTMENT_ID NET_IF_COMPARTMENT_ID_UNSPECIFIED
#define QUIC_DEFAULT_COMPARTMENT_ID NET_IF_COMPARTMENT_ID_PRIMARY
#define QuicSetCurrentThreadAffinityMask(Mask) SetThreadAffinityMask(GetCurrentThread(), Mask)
#define QuicCompartmentIdGetCurrent() GetCurrentThreadCompartmentId()
#define QuicCompartmentIdSetCurrent(CompartmentId) \
HRESULT_FROM_WIN32(SetCurrentThreadCompartmentId(CompartmentId))

Просмотреть файл

@ -0,0 +1,15 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
set(SOURCES
appmain.cpp
)
# Allow CLOG to preprocess all the source files.
CLOG_GENERATE_TARGET(perfbin.clog ${SOURCES})
add_executable(quicperf ${SOURCES})
set_property(TARGET quicperf PROPERTY FOLDER "perf")
target_link_libraries(quicperf perflib msquic platform perfbin.clog)

224
src/perf/bin/appmain.cpp Normal file
Просмотреть файл

@ -0,0 +1,224 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
Abstract:
QUIC Perf Main execution runner.
--*/
#define QUIC_TEST_APIS 1
#include "quic_driver_main.h"
#include "PerfHelpers.h"
#include <quic_trace.h>
#ifdef QUIC_CLOG
#include "appmain.cpp.clog.h"
#endif
#ifdef _WIN32
//
// Name of the driver service for quicperf.sys.
// Must be defined before quic_driver_helpers.h is included
//
#define QUIC_DRIVER_NAME "quicperf"
#include <winioctl.h>
#include "PerfIoctls.h"
#include "quic_driver_helpers.h"
#endif
extern "C" _IRQL_requires_max_(PASSIVE_LEVEL) void QuicTraceRundown(void) { }
QUIC_STATUS
QuicUserMain(
_In_ int argc,
_In_reads_(argc) _Null_terminated_ char* argv[],
_In_ bool KeyboardWait,
_In_ PerfSelfSignedConfiguration* SelfSignedConfig
) {
QUIC_EVENT StopEvent;
QuicEventInitialize(&StopEvent, true, false);
QUIC_STATUS Status = QuicMainStart(argc, argv, StopEvent, SelfSignedConfig);
if (Status != 0) {
return Status;
}
printf("Started!\n\n");
fflush(stdout);
if (KeyboardWait) {
printf("Press enter to exit\n");
getchar();
QuicEventSet(StopEvent);
}
Status = QuicMainStop(0);
QuicEventUninitialize(StopEvent);
return Status;
}
#ifdef _WIN32
QUIC_STATUS
QuicKernelMain(
_In_ int argc,
_In_reads_(argc) _Null_terminated_ char* argv[],
_In_ bool KeyboardWait,
_In_ QUIC_SEC_CONFIG_PARAMS* SelfSignedParams
) {
size_t TotalLength = sizeof(argc);
//
// Get total length
//
for (int i = 0; i < argc; ++i) {
TotalLength += strlen(argv[i]) + 1;
}
if (TotalLength > UINT_MAX) {
printf("Too many arguments to pass to the driver\n");
return QUIC_STATUS_OUT_OF_MEMORY;
}
char* Data = static_cast<char*>(QUIC_ALLOC_NONPAGED(TotalLength));
if (!Data) {
printf("Failed to allocate arguments to pass\n");
return QUIC_STATUS_OUT_OF_MEMORY;
}
char* DataCurrent = Data;
QuicCopyMemory(DataCurrent, &argc, sizeof(TotalLength));
DataCurrent += sizeof(argc);
for (int i = 0; i < argc; ++i) {
size_t ArgLen = strlen(argv[i]) + 1;
QuicCopyMemory(DataCurrent, argv[i], ArgLen);
DataCurrent += ArgLen;
DataCurrent[0] = '\0';
++DataCurrent;
}
QUIC_DBG_ASSERT(DataCurrent == (Data + TotalLength));
constexpr uint32_t OutBufferSize = 1024 * 1000;
char* OutBuffer = (char*)QUIC_ALLOC_NONPAGED(OutBufferSize); // 1 MB
if (!OutBuffer) {
printf("Failed to allocate space for output buffer\n");
QUIC_FREE(Data);
return QUIC_STATUS_OUT_OF_MEMORY;
}
QuicDriverService DriverService;
QuicDriverClient DriverClient;
if (!DriverService.Initialize()) {
printf("Failed to initialize driver service\n");
QUIC_FREE(Data);
return QUIC_STATUS_INVALID_STATE;
}
DriverService.Start();
if (!DriverClient.Initialize(SelfSignedParams)) {
printf("Failed to initialize driver client\n");
QUIC_FREE(Data);
return QUIC_STATUS_INVALID_STATE;
}
if (!DriverClient.Run(IOCTL_QUIC_RUN_PERF, Data, (uint32_t)TotalLength)) {
QUIC_FREE(Data);
QUIC_FREE(OutBuffer);
return QUIC_STATUS_INVALID_STATE;
}
printf("Started!\n\n");
fflush(stdout);
uint32_t OutBufferWritten = 0;
bool RunSuccess =
DriverClient.Read(
IOCTL_QUIC_READ_DATA,
OutBuffer,
OutBufferSize,
&OutBufferWritten);
if (RunSuccess) {
printf("%s", OutBuffer);
}
QUIC_FREE(Data);
QUIC_FREE(OutBuffer);
return RunSuccess ? QUIC_STATUS_SUCCESS : QUIC_STATUS_INTERNAL_ERROR;
}
#endif
int
QUIC_MAIN_EXPORT
main(
_In_ int argc,
_In_reads_(argc) _Null_terminated_ char* argv[]
) {
QUIC_SEC_CONFIG_PARAMS* SelfSignedParams = nullptr;
PerfSelfSignedConfiguration SelfSignedConfig;
QUIC_STATUS RetVal = 0;
bool TestingKernelMode = false;
bool KeyboardWait = false;
QuicPlatformSystemLoad();
if (QUIC_FAILED(QuicPlatformInitialize())) {
printf("Platform failed to initialize\n");
goto Exit;
}
for (int i = 0; i < argc; ++i) {
if (strcmp("--kernel", argv[i]) == 0) {
#ifdef _WIN32
TestingKernelMode = true;
#else
printf("Cannot run kernel mode tests on non windows platforms\n");
RetVal = QUIC_STATUS_NOT_SUPPORTED;
goto Exit;
#endif
} else if (strcmp("--kbwait", argv[i]) == 0) {
KeyboardWait = true;
}
}
SelfSignedParams =
QuicPlatGetSelfSignedCert(
TestingKernelMode ?
QUIC_SELF_SIGN_CERT_MACHINE :
QUIC_SELF_SIGN_CERT_USER);
if (!SelfSignedParams) {
printf("Creating self signed certificate failed\n");
RetVal = QUIC_STATUS_INTERNAL_ERROR;
goto Exit;
}
SelfSignedConfig.SelfSignedParams = SelfSignedParams;
if (TestingKernelMode) {
#ifdef _WIN32
RetVal = QuicKernelMain(argc, argv, KeyboardWait, SelfSignedParams);
#else
QUIC_FRE_ASSERT(FALSE);
#endif
} else {
RetVal = QuicUserMain(argc, argv, KeyboardWait, &SelfSignedConfig);
}
Exit:
if (SelfSignedParams) {
QuicPlatFreeSelfSignedCert(SelfSignedParams);
}
QuicPlatformUninitialize();
QuicPlatformSystemUnload();
return RetVal;
}

561
src/perf/bin/drvmain.cpp Normal file
Просмотреть файл

@ -0,0 +1,561 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
Abstract:
QUIC Kernel Mode Performance Driver
--*/
#include <quic_driver_main.h>
#include <quic_platform.h>
#include "msquic.h"
#include "PerfIoctls.h"
#include "quic_trace.h"
#ifdef QUIC_CLOG
#include "driver.cpp.clog.h"
#endif
#define QUIC_PERF_TAG 'frPQ' // QPrf
DECLARE_CONST_UNICODE_STRING(QuicPerfCtlDeviceName, L"\\Device\\quicperformance");
DECLARE_CONST_UNICODE_STRING(QuicPerfCtlDeviceSymLink, L"\\DosDevices\\quicperformance");
typedef struct QUIC_DEVICE_EXTENSION {
EX_PUSH_LOCK Lock;
_Guarded_by_(Lock)
LIST_ENTRY ClientList;
ULONG ClientListSize;
} QUIC_DEVICE_EXTENSION;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUIC_DEVICE_EXTENSION, QuicPerfCtlGetDeviceContext);
typedef struct QUIC_DRIVER_CLIENT {
LIST_ENTRY Link;
bool TestFailure;
} QUIC_DRIVER_CLIENT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUIC_DRIVER_CLIENT, QuicPerfCtlGetFileContext);
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL QuicPerfCtlEvtIoDeviceControl;
EVT_WDF_IO_QUEUE_IO_CANCELED_ON_QUEUE QuicPerfCtlEvtIoCanceled;
PAGEDX EVT_WDF_DEVICE_FILE_CREATE QuicPerfCtlEvtFileCreate;
PAGEDX EVT_WDF_FILE_CLOSE QuicPerfCtlEvtFileClose;
PAGEDX EVT_WDF_FILE_CLEANUP QuicPerfCtlEvtFileCleanup;
WDFDEVICE QuicPerfCtlDevice = nullptr;
QUIC_DEVICE_EXTENSION* QuicPerfCtlExtension = nullptr;
QUIC_DRIVER_CLIENT* QuicPerfClient = nullptr;
EVT_WDF_DRIVER_UNLOAD QuicPerfDriverUnload;
_No_competing_thread_
INITCODE
NTSTATUS
QuicPerfCtlInitialize(
_In_ WDFDRIVER Driver
);
_IRQL_requires_max_(PASSIVE_LEVEL)
void
QuicPerfCtlUninitialize(
void
);
void* __cdecl operator new (size_t Size) {
return ExAllocatePool2(POOL_FLAG_NON_PAGED, Size, QUIC_PERF_TAG);
}
void __cdecl operator delete (_In_opt_ void* Mem) {
if (Mem != nullptr) {
ExFreePoolWithTag(Mem, QUIC_PERF_TAG);
}
}
void __cdecl operator delete (_In_opt_ void* Mem, _In_opt_ size_t) {
if (Mem != nullptr) {
ExFreePoolWithTag(Mem, QUIC_PERF_TAG);
}
}
void* __cdecl operator new[](size_t Size) {
return ExAllocatePool2(POOL_FLAG_NON_PAGED, Size, QUIC_PERF_TAG);
}
void __cdecl operator delete[](_In_opt_ void* Mem) {
if (Mem != nullptr) {
ExFreePoolWithTag(Mem, QUIC_PERF_TAG);
}
}
extern "C" _IRQL_requires_max_(PASSIVE_LEVEL) void QuicTraceRundown(void) { }
extern "C"
INITCODE
_Function_class_(DRIVER_INITIALIZE)
_IRQL_requires_same_
_IRQL_requires_(PASSIVE_LEVEL)
NTSTATUS
DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
{
NTSTATUS Status;
WDF_DRIVER_CONFIG Config;
WDFDRIVER Driver;
BOOLEAN PlatformInitialized = FALSE;
QuicPlatformSystemLoad(DriverObject, RegistryPath);
Status = QuicPlatformInitialize();
if (!NT_SUCCESS(Status)) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Status,
"QuicPlatformInitialize failed");
goto Error;
}
PlatformInitialized = TRUE;
//
// Create the WdfDriver Object
//
WDF_DRIVER_CONFIG_INIT(&Config, NULL);
Config.EvtDriverUnload = QuicPerfDriverUnload;
Config.DriverInitFlags = WdfDriverInitNonPnpDriver;
Config.DriverPoolTag = QUIC_PERF_TAG;
Status =
WdfDriverCreate(
DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES,
&Config,
&Driver);
if (!NT_SUCCESS(Status)) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Status,
"WdfDriverCreate failed");
goto Error;
}
//
// Initialize the device control interface.
//
Status = QuicPerfCtlInitialize(Driver);
if (!NT_SUCCESS(Status)) {
goto Error;
}
QuicTraceLogInfo(
PerfDriverStarted,
"[perf] Started");
Error:
if (!NT_SUCCESS(Status)) {
if (PlatformInitialized) {
QuicPlatformUninitialize();
}
QuicPlatformSystemUnload();
}
return Status;
}
_Function_class_(EVT_WDF_DRIVER_UNLOAD)
_IRQL_requires_same_
_IRQL_requires_max_(PASSIVE_LEVEL)
void
QuicPerfDriverUnload(
_In_ WDFDRIVER /*Driver*/
)
{
NT_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
QuicPerfCtlUninitialize();
QuicTraceLogInfo(
PerfDriverStopped,
"[perf] Stopped");
QuicPlatformUninitialize();
QuicPlatformSystemUnload();
}
_No_competing_thread_
INITCODE
NTSTATUS
QuicPerfCtlInitialize(
_In_ WDFDRIVER Driver
)
{
NTSTATUS Status = STATUS_SUCCESS;
PWDFDEVICE_INIT DeviceInit = nullptr;
WDF_FILEOBJECT_CONFIG FileConfig;
WDF_OBJECT_ATTRIBUTES Attribs;
WDFDEVICE Device;
QUIC_DEVICE_EXTENSION* DeviceContext;
WDF_IO_QUEUE_CONFIG QueueConfig;
WDFQUEUE Queue;
DeviceInit =
WdfControlDeviceInitAllocate(
Driver,
&SDDL_DEVOBJ_SYS_ALL_ADM_ALL);
if (DeviceInit == nullptr) {
QuicTraceEvent(
LibraryError,
"[ lib] ERROR, %s.",
"WdfControlDeviceInitAllocate failed");
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
Status =
WdfDeviceInitAssignName(
DeviceInit,
&QuicPerfCtlDeviceName);
if (!NT_SUCCESS(Status)) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Status,
"WdfDeviceInitAssignName failed");
goto Error;
}
WDF_FILEOBJECT_CONFIG_INIT(
&FileConfig,
QuicPerfCtlEvtFileCreate,
QuicPerfCtlEvtFileClose,
QuicPerfCtlEvtFileCleanup);
FileConfig.FileObjectClass = WdfFileObjectWdfCanUseFsContext2;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attribs, QUIC_DRIVER_CLIENT);
WdfDeviceInitSetFileObjectConfig(
DeviceInit,
&FileConfig,
&Attribs);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&Attribs, QUIC_DEVICE_EXTENSION);
Status =
WdfDeviceCreate(
&DeviceInit,
&Attribs,
&Device);
if (!NT_SUCCESS(Status)) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Status,
"WdfDeviceCreate failed");
goto Error;
}
DeviceContext = QuicPerfCtlGetDeviceContext(Device);
RtlZeroMemory(DeviceContext, sizeof(QUIC_DEVICE_EXTENSION));
ExInitializePushLock(&DeviceContext->Lock);
InitializeListHead(&DeviceContext->ClientList);
Status = WdfDeviceCreateSymbolicLink(Device, &QuicPerfCtlDeviceSymLink);
if (!NT_SUCCESS(Status)) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Status,
"WdfDeviceCreateSymbolicLink failed");
goto Error;
}
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&QueueConfig, WdfIoQueueDispatchParallel);
QueueConfig.EvtIoDeviceControl = QuicPerfCtlEvtIoDeviceControl;
QueueConfig.EvtIoCanceledOnQueue = QuicPerfCtlEvtIoCanceled;
__analysis_assume(QueueConfig.EvtIoStop != 0);
Status =
WdfIoQueueCreate(
Device,
&QueueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&Queue);
__analysis_assume(QueueConfig.EvtIoStop == 0);
if (!NT_SUCCESS(Status)) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Status,
"WdfIoQueueCreate failed");
goto Error;
}
QuicPerfCtlDevice = Device;
QuicPerfCtlExtension = DeviceContext;
WdfControlFinishInitializing(Device);
QuicTraceLogVerbose(
PerfControlInitialized,
"[perf] Control interface initialized");
Error:
if (DeviceInit) {
WdfDeviceInitFree(DeviceInit);
}
return Status;
}
_IRQL_requires_max_(PASSIVE_LEVEL)
void
QuicPerfCtlUninitialize(
void
)
{
QuicTraceLogVerbose(
PerfControlUninitializing,
"[perf] Control interface uninitializing");
if (QuicPerfCtlDevice != nullptr) {
NT_ASSERT(QuicPerfCtlExtension != nullptr);
QuicPerfCtlExtension = nullptr;
WdfObjectDelete(QuicPerfCtlDevice);
QuicPerfCtlDevice = nullptr;
}
QuicTraceLogVerbose(
PerfControlUninitialized,
"[perf] Control interface uninitialized");
}
PAGEDX
_Use_decl_annotations_
void
QuicPerfCtlEvtFileCreate(
_In_ WDFDEVICE /* Device */,
_In_ WDFREQUEST Request,
_In_ WDFFILEOBJECT FileObject
)
{
NTSTATUS Status = STATUS_SUCCESS;
PAGED_CODE();
KeEnterGuardedRegion();
ExfAcquirePushLockExclusive(&QuicPerfCtlExtension->Lock);
do {
if (QuicPerfCtlExtension->ClientListSize >= 1) {
QuicTraceEvent(
LibraryError,
"[ lib] ERROR, %s.",
"Already have max clients");
Status = STATUS_TOO_MANY_SESSIONS;
break;
}
QUIC_DRIVER_CLIENT* Client = QuicPerfCtlGetFileContext(FileObject);
if (Client == nullptr) {
QuicTraceEvent(
LibraryError,
"[ lib] ERROR, %s.",
"nullptr File context in FileCreate");
Status = STATUS_INVALID_PARAMETER;
break;
}
RtlZeroMemory(Client, sizeof(QUIC_DRIVER_CLIENT));
//
// Insert into the client list
//
InsertTailList(&QuicPerfCtlExtension->ClientList, &Client->Link);
QuicPerfCtlExtension->ClientListSize++;
QuicTraceLogInfo(
TestControlClientCreated,
"[test] Client %p created",
Client);
//
// Update globals. (TODO: Add multiple device client support)
//
QuicPerfClient = Client;
} while (false);
ExfReleasePushLockExclusive(&QuicPerfCtlExtension->Lock);
KeLeaveGuardedRegion();
WdfRequestComplete(Request, Status);
}
PAGEDX
_Use_decl_annotations_
void
QuicPerfCtlEvtFileClose(
_In_ WDFFILEOBJECT /* FileObject */
)
{
PAGED_CODE();
}
PAGEDX
_Use_decl_annotations_
void
QuicPerfCtlEvtFileCleanup(
_In_ WDFFILEOBJECT FileObject
)
{
PAGED_CODE();
KeEnterGuardedRegion();
QUIC_DRIVER_CLIENT* Client = QuicPerfCtlGetFileContext(FileObject);
if (Client != nullptr) {
ExfAcquirePushLockExclusive(&QuicPerfCtlExtension->Lock);
//
// Remove the device client from the list
//
RemoveEntryList(&Client->Link);
QuicPerfCtlExtension->ClientListSize--;
ExfReleasePushLockExclusive(&QuicPerfCtlExtension->Lock);
QuicTraceLogInfo(
TestControlClientCleaningUp,
"[test] Client %p cleaning up",
Client);
//
// Clean up globals.
//
QuicPerfClient = nullptr;
}
KeLeaveGuardedRegion();
}
VOID
QuicPerfCtlEvtIoCanceled(
_In_ WDFQUEUE /* Queue */,
_In_ WDFREQUEST Request
)
{
NTSTATUS Status;
WDFFILEOBJECT FileObject = WdfRequestGetFileObject(Request);
if (FileObject == nullptr) {
Status = STATUS_DEVICE_NOT_READY;
goto error;
}
QUIC_DRIVER_CLIENT* Client = QuicPerfCtlGetFileContext(FileObject);
if (Client == nullptr) {
Status = STATUS_DEVICE_NOT_READY;
goto error;
}
QuicTraceLogWarning(
TestControlClientCanceledRequest,
"[test] Client %p canceled request %p",
Client,
Request);
Status = STATUS_CANCELLED;
error:
WdfRequestComplete(Request, Status);
}
size_t QUIC_IOCTL_BUFFER_SIZES[] =
{
0,
sizeof(QUIC_CERTIFICATE_HASH),
0,
0
};
static_assert(
QUIC_PERF_MAX_IOCTL_FUNC_CODE + 1 == (sizeof(QUIC_IOCTL_BUFFER_SIZES) / sizeof(size_t)),
"QUIC_IOCTL_BUFFER_SIZES must be kept in sync with the IOTCLs");
VOID
QuicPerfCtlEvtIoDeviceControl(
_In_ WDFQUEUE Queue,
_In_ WDFREQUEST Request,
_In_ size_t OutputBufferLength,
_In_ size_t InputBufferLength,
_In_ ULONG IoControlCode
)
{
QUIC_STATUS Status = QUIC_STATUS_SUCCESS;
WDFFILEOBJECT FileObject = nullptr;
QUIC_DRIVER_CLIENT* Client = nullptr;
ULONG FunctionCode = 0;
if (KeGetCurrentIrql() > PASSIVE_LEVEL) {
Status = STATUS_NOT_SUPPORTED;
QuicTraceEvent(
LibraryError,
"[ lib] ERROR, %s.",
"IOCTL not supported greater than PASSIVE_LEVEL");
goto Error;
}
FileObject = WdfRequestGetFileObject(Request);
if (FileObject == nullptr) {
Status = STATUS_DEVICE_NOT_READY;
QuicTraceEvent(
LibraryError,
"[ lib] ERROR, %s.",
"WdfRequestGetFileObject failed");
goto Error;
}
Client = QuicPerfCtlGetFileContext(FileObject);
if (Client == nullptr) {
Status = STATUS_DEVICE_NOT_READY;
QuicTraceEvent(
LibraryError,
"[ lib] ERROR, %s.",
"QuicTestCtlGetFileContext failed");
goto Error;
}
FunctionCode = IoGetFunctionCodeFromCtlCode(IoControlCode);
if (FunctionCode == 0 || FunctionCode > QUIC_PERF_MAX_IOCTL_FUNC_CODE) {
Status = STATUS_NOT_IMPLEMENTED;
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
FunctionCode,
"Invalid FunctionCode");
goto Error;
}
// TODO Input Buffer Length
Error:
UNREFERENCED_PARAMETER(InputBufferLength);
UNREFERENCED_PARAMETER(OutputBufferLength);
UNREFERENCED_PARAMETER(Queue);
}

29
src/perf/bin/perfioctls.h Normal file
Просмотреть файл

@ -0,0 +1,29 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
Abstract:
QUIC Perf Kernel Mode IOCTL definitions
--*/
#pragma once
#define QUIC_CTL_CODE(request, method, access) \
CTL_CODE(FILE_DEVICE_NETWORK, request, method, access)
#define IoGetFunctionCodeFromCtlCode( ControlCode ) (\
( ControlCode >> 2) & 0x00000FFF )
#define IOCTL_QUIC_SEC_CONFIG \
QUIC_CTL_CODE(1, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_QUIC_RUN_PERF \
QUIC_CTL_CODE(2, METHOD_BUFFERED, FILE_WRITE_DATA)
#define IOCTL_QUIC_READ_DATA \
QUIC_CTL_CODE(3, METHOD_BUFFERED, FILE_READ_DATA)
#define QUIC_PERF_MAX_IOCTL_FUNC_CODE 3

Просмотреть файл

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="drvmain.cpp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\platform\platform.kernel.vcxproj">
<Project>{5f99f713-bf5f-44eb-90fe-fea03906bba9}</Project>
</ProjectReference>
<ProjectReference Include="..\lib\perflib.kernel.vcxproj">
<Project>{11633785-79cc-4c7d-ab6a-aecdf29a1fa7}</Project>
</ProjectReference>
<ProjectReference Include="..\..\bin\winkernel\msquic.kernel.vcxproj">
<Project>{C31B028C-E91C-4CF7-A8E7-F385B2AF5F85}</Project>
</ProjectReference>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{2be64dbf-60e6-4fe8-96b0-5f2526405096}</ProjectGuid>
<TemplateGuid>{1bc93793-694f-48fe-9372-81e2b05556fd}</TemplateGuid>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>Driver</ConfigurationType>
<DriverType>KMDF</DriverType>
<DriverTargetPlatform>Universal</DriverTargetPlatform>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<TargetName>quicperf</TargetName>
<DebuggerFlavor>DbgengKernelDebugger</DebuggerFlavor>
<EnableInf2cat>false</EnableInf2cat>
<OutDir>$(SolutionDir)artifacts\bin\winkernel\$(Platform)_$(Configuration)_schannel\</OutDir>
<IntDir>$(SolutionDir)build\winkernel\$(Platform)_$(Configuration)_schannel\obj\$(ProjectName)\</IntDir>
<SignMode>Off</SignMode>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..\lib;..\..;..\..\inc;$(SolutionDir)build\winkernel\$(Platform)_$(Configuration)_schannel\inc;$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<WholeProgramOptimization>true</WholeProgramOptimization>
<AdditionalOptions Condition="'$(Platform)'!='x64'">/Gw /kernel /ZH:SHA_256</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'=='x64'">/Gw /kernel /ZH:SHA_256 -d2jumptablerdata -d2epilogunwindrequirev2</AdditionalOptions>
<DisableSpecificWarnings>4748;5040;4459;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalLibraryDirectories>$(SolutionDir)artifacts\bin\winkernel\$(Platform)_$(Configuration)_schannel\</AdditionalLibraryDirectories>
<AdditionalDependencies>cng.lib;ksecdd.lib;msnetioid.lib;netio.lib;wdmsec.lib;uuid.lib;msquic.lib;%(AdditionalDependencies)</AdditionalDependencies>
<LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PreprocessorDefinitions>QUIC_EVENTS_MANIFEST_ETW;QUIC_LOGS_MANIFEST_ETW;QUIC_DISABLE_0RTT_TESTS;SECURITY_KERNEL;SECURITY_WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<PreprocessorDefinitions>QUIC_EVENTS_MANIFEST_ETW;QUIC_LOGS_MANIFEST_ETW;QUIC_DISABLE_0RTT_TESTS;SECURITY_KERNEL;SECURITY_WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup>
<FilesToPackage Include="$(TargetPath)" />
</ItemGroup>
<PropertyGroup>
<RunCodeAnalysis>true</RunCodeAnalysis>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

Просмотреть файл

@ -0,0 +1,31 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
set(SOURCES
quicmain.cpp
ThroughputServer.cpp
ThroughputClient.cpp
)
# Allow CLOG to preprocess all the source files.
CLOG_GENERATE_TARGET(perflib.clog ${SOURCES})
add_library(perflib ${SOURCES})
set_property(TARGET perflib PROPERTY FOLDER "perf")
target_link_libraries(perflib PUBLIC perflib.clog)
target_link_libraries(perflib PRIVATE inc warnings)
target_include_directories(perflib PUBLIC ${CMAKE_CURRENT_LIST_DIR})
if (MSVC)
target_compile_options(perflib PUBLIC /wd4459)
else()
target_compile_options(perflib PUBLIC -Wno-switch)
endif()
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
add_dependencies(perflib MsQuicEtw)
endif()

55
src/perf/lib/PerfBase.h Normal file
Просмотреть файл

@ -0,0 +1,55 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
Abstract:
QUIC Perf Base blass declaration. Defines the base class for all perf
executions.
--*/
#pragma once
#include "msquic.h"
#include "quic_platform.h"
struct PerfBase {
//
// Virtual destructor so we can destruct the base class
//
virtual
~PerfBase() = default;
//
// Called to initialize the runner.
//
virtual
QUIC_STATUS
Init(
_In_ int argc,
_In_reads_(argc) _Null_terminated_ char* argv[]
) = 0;
//
// Start the runner. The StopEvent can be triggered to stop early. Passed
// here rather then Wait so we can synchronize off of it. This event must
// be kept alive until Wait is called.
//
virtual
QUIC_STATUS
Start(
_In_ QUIC_EVENT StopEvent
) = 0;
//
// Wait for a run to finish, until timeout.
// If 0 or less, wait forever
//
virtual
QUIC_STATUS
Wait(
int Timeout
) = 0;
};

280
src/perf/lib/PerfHelpers.h Normal file
Просмотреть файл

@ -0,0 +1,280 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
Abstract:
MsQuic API Perf Helpers
--*/
#pragma once
#ifdef QUIC_CLOG
#include "PerfHelpers.h.clog.h"
#endif
#ifndef _KERNEL_MODE
#define QUIC_TEST_APIS 1
#endif
class QuicApiTable;
extern const QuicApiTable* MsQuic;
#define QUIC_SKIP_GLOBAL_CONSTRUCTORS
#include <quic_platform.h>
#include <msquic.hpp>
#include <msquichelper.h>
#include <stdlib.h>
#include <stdio.h>
#ifndef _KERNEL_MODE
#include <new> // Needed for placement new
#else
#include <new.h>
#endif
struct PerfSelfSignedConfiguration {
#ifdef _KERNEL_MODE
uint8_t SelfSignedSecurityHash[20];
#else
QUIC_SEC_CONFIG_PARAMS* SelfSignedParams;
#endif
};
#define QUIC_TEST_SESSION_CLOSED 1
inline
int
WriteOutput(
_In_z_ const char* format
...
)
{
#ifndef _KERNEL_MODE
va_list args;
va_start(args, format);
int rval = vprintf(format, args);
va_end(args);
return rval;
#else
UNREFERENCED_PARAMETER(format);
return 0;
#endif
}
struct PerfSecurityConfig {
QUIC_STATUS Initialize(int argc, char** argv, const MsQuicRegistration& Registration, PerfSelfSignedConfiguration* Config) {
uint16_t useSelfSigned = 0;
if (TryGetValue(argc, argv, "selfsign", &useSelfSigned)) {
#ifdef _KERNEL_MODE
CreateSecConfigHelper Helper;
SecurityConfig =
Helper.Create(
MsQuic,
Registration,
QUIC_SEC_CONFIG_FLAG_CERTIFICATE_HASH,
&Config->SelfSignedSecurityHash,
nullptr);
#else
SecurityConfig =
GetSecConfigForSelfSigned(
MsQuic,
Registration,
Config->SelfSignedParams);
#endif
if (!SecurityConfig) {
WriteOutput("Failed to create security config for self signed certificate\n");
return QUIC_STATUS_INVALID_PARAMETER;
}
} else {
const char* certThumbprint;
if (!TryGetValue(argc, argv, "thumbprint", &certThumbprint)) {
WriteOutput("Must specify -thumbprint: for server mode.\n");
return QUIC_STATUS_INVALID_PARAMETER;
}
const char* certStoreName;
if (!TryGetValue(argc, argv, "cert_store", &certStoreName)) {
SecurityConfig = GetSecConfigForThumbprint(MsQuic, Registration, certThumbprint);
if (SecurityConfig == nullptr) {
WriteOutput("Failed to create security configuration for thumbprint:'%s'.\n", certThumbprint);
return QUIC_STATUS_INVALID_PARAMETER;
}
} else {
uint32_t machineCert = 0;
TryGetValue(argc, argv, "machine_cert", &machineCert);
QUIC_CERTIFICATE_HASH_STORE_FLAGS flags =
machineCert ? QUIC_CERTIFICATE_HASH_STORE_FLAG_MACHINE_STORE : QUIC_CERTIFICATE_HASH_STORE_FLAG_NONE;
SecurityConfig = GetSecConfigForThumbprintAndStore(MsQuic, Registration, flags, certThumbprint, certStoreName);
if (SecurityConfig == nullptr) {
WriteOutput(
"Failed to create security configuration for thumbprint:'%s' and store: '%s'.\n",
certThumbprint,
certStoreName);
return QUIC_STATUS_INVALID_PARAMETER;
}
}
}
return QUIC_STATUS_SUCCESS;
}
~PerfSecurityConfig() {
if (SecurityConfig) {
MsQuic->SecConfigDelete(SecurityConfig);
}
}
operator QUIC_SEC_CONFIG*() const { return SecurityConfig; }
QUIC_SEC_CONFIG* SecurityConfig {nullptr};
};
struct CountHelper {
long RefCount;
QUIC_EVENT Done;
CountHelper() :
RefCount{1}, Done{} {}
CountHelper(QUIC_EVENT Done) :
RefCount{1}, Done{Done} { }
bool
Wait(
uint32_t Milliseconds
) {
if (InterlockedDecrement(&RefCount) == 0) {
return true;
} else {
return !QuicEventWaitWithTimeout(Done, Milliseconds);
}
}
void
WaitForever(
) {
if (InterlockedDecrement(&RefCount) == 0) {
return;
} else {
QuicEventWaitForever(Done);
}
}
void
AddItem(
) {
InterlockedIncrement(&RefCount);
}
void
CompleteItem(
) {
if (InterlockedDecrement(&RefCount) == 0) {
QuicEventSet(Done);
}
}
};
//
// Implementation of std::forward, to allow use in kernel mode.
// Based on reference implementation in MSVC's STL
//
template <class _Ty>
struct QuicRemoveReference {
using type = _Ty;
using _Const_thru_ref_type = const _Ty;
};
template <class _Ty>
using QuicRemoveReferenceT = typename QuicRemoveReference<_Ty>::type;
template <class _Ty>
constexpr _Ty&& QuicForward(
QuicRemoveReferenceT<_Ty>& _Arg) noexcept { // forward an lvalue as either an lvalue or an rvalue
return static_cast<_Ty&&>(_Arg);
}
class QuicPoolBufferAllocator {
QUIC_POOL Pool;
bool Initialized {false};
public:
QuicPoolBufferAllocator() {
QuicZeroMemory(&Pool, sizeof(Pool));
}
~QuicPoolBufferAllocator() {
if (Initialized) {
QuicPoolUninitialize(&Pool);
Initialized = false;
}
}
void Initialize(uint32_t Size, bool Paged = false) {
QUIC_DBG_ASSERT(Initialized == false);
QuicPoolInitialize(Paged, Size, &Pool);
Initialized = true;
}
uint8_t* Alloc() {
return static_cast<uint8_t*>(QuicPoolAlloc(&Pool));
}
void Free(uint8_t* Buf) {
if (Buf == nullptr) {
return;
}
QuicPoolFree(&Pool, Buf);
}
};
template<typename T, bool Paged = false>
class QuicPoolAllocator {
QUIC_POOL Pool;
public:
QuicPoolAllocator() {
QuicPoolInitialize(Paged, sizeof(T), &Pool);
}
~QuicPoolAllocator() {
QuicPoolUninitialize(&Pool);
}
template <class... Args>
T* Alloc(Args&&... args) {
void* Raw = QuicPoolAlloc(&Pool);
if (Raw == nullptr) {
return nullptr;
}
return new (Raw) T (QuicForward<Args>(args)...);
}
void Free(T* Obj) {
if (Obj == nullptr) {
return;
}
Obj->~T();
QuicPoolFree(&Pool, Obj);
}
};
//
// Arg Value Parsers
//
inline
_Success_(return != false)
bool
IsValue(
_In_z_ const char* name,
_In_z_ const char* toTestAgainst
)
{
return _strnicmp(name, toTestAgainst, min(strlen(name), strlen(toTestAgainst))) == 0;
}

Просмотреть файл

@ -0,0 +1,49 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
Abstract:
QUIC Perf Send Request Wrapper.
--*/
#pragma once
#include "PerfHelpers.h"
struct SendRequest {
QUIC_SEND_FLAGS Flags {QUIC_SEND_FLAG_NONE};
QUIC_BUFFER QuicBuffer;
QuicPoolBufferAllocator* BufferAllocator;
uint32_t IoSize;
SendRequest(
QuicPoolBufferAllocator* BufferAllocator,
uint32_t IoSize,
bool FillBuffer
) {
this->BufferAllocator = BufferAllocator;
this->IoSize = IoSize;
QuicBuffer.Buffer = BufferAllocator->Alloc();
if (FillBuffer) {
memset(QuicBuffer.Buffer, 0xBF, IoSize);
}
QuicBuffer.Length = 0;
}
~SendRequest() {
BufferAllocator->Free(QuicBuffer.Buffer);
}
void SetLength(
uint64_t BytesLeftToSend
) {
if (BytesLeftToSend > IoSize) {
QuicBuffer.Length = IoSize;
} else {
Flags |= QUIC_SEND_FLAG_FIN;
QuicBuffer.Length = (uint32_t)BytesLeftToSend;
}
}
};

Просмотреть файл

@ -0,0 +1,387 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
Abstract:
QUIC Perf Throughput Client Implementation.
--*/
#ifdef QUIC_CLOG
#include "ThroughputClient.cpp.clog.h"
#endif
#define QUIC_API_ENABLE_INSECURE_FEATURES 1
#include "ThroughputClient.h"
#include "ThroughputCommon.h"
#include "quic_trace.h"
static
void
PrintHelp(
) {
WriteOutput("Usage: quicperf -TestName:Throughput [options]\n\n"
#if _WIN32
" -comp:<####> The compartment ID to run in.\n"
" -core:<####> The CPU core to use for the main thread.\n"
#endif
" -bind:<addr> A local IP address to bind to.\n"
" -port:<####> The UDP port of the server. (def:%u)\n"
" -ip:<0/4/6> A hint for the resolving the hostname to an IP address. (def:0)\n"
" -encrypt:<0/1> Enables/disables encryption. (def:%u)\n"
" -sendbuf:<0/1> Whether to use send buffering. (def:%u)\n"
" -length:<####> The length of streams opened locally. (def:0)\n"
" -iosize:<####> The size of each send request queued. (buffered def:%u) (nonbuffered def:%u)\n"
" -iocount:<####> The number of outstanding send requests to queue per stream. (buffered def:%u) (nonbuffered def:%u)\n",
THROUGHPUT_DEFAULT_PORT,
THROUGHPUT_DEFAULT_IO_SIZE_BUFFERED, THROUGHPUT_DEFAULT_IO_SIZE_NONBUFFERED,
THROGHTPUT_DEFAULT_SEND_COUNT_BUFFERED, THROUGHPUT_DEFAULT_SEND_COUNT_NONBUFFERED
);
}
ThroughputClient::ThroughputClient(
) {
QuicZeroMemory(&LocalIpAddr, sizeof(LocalIpAddr));
if (Session.IsValid()) {
Session.SetAutoCleanup();
}
}
QUIC_STATUS
ThroughputClient::Init(
_In_ int argc,
_In_reads_(argc) _Null_terminated_ char* argv[]
) {
if (!Session.IsValid()) {
return Session.GetInitStatus();
}
Port = THROUGHPUT_DEFAULT_PORT;
TryGetValue(argc, argv, "port", &Port);
TryGetValue(argc, argv, "encrypt", &UseEncryption);
const char* Target;
if (!TryGetValue(argc, argv, "target", &Target)) {
WriteOutput("Must specify '-target' argument!\n");
PrintHelp();
return QUIC_STATUS_INVALID_PARAMETER;
}
uint16_t Ip;
if (TryGetValue(argc, argv, "ip", &Ip)) {
switch (Ip) {
case 4: RemoteFamily = AF_INET; break;
case 6: RemoteFamily = AF_INET6; break;
}
}
TryGetValue(argc, argv, "length", &Length);
const char* LocalAddress = nullptr;
if (TryGetValue(argc, argv, "bind", &LocalAddress)) {
if (!ConvertArgToAddress(LocalAddress, 0, &LocalIpAddr)) {
WriteOutput("Failed to decode IP address: '%s'!\nMust be *, a IPv4 or a IPv6 address.\n", LocalAddress);
PrintHelp();
return QUIC_STATUS_INVALID_PARAMETER;
}
}
// TODO: Core, since we need to support kernel mode
#ifdef QUIC_COMPARTMENT_ID
uint16_t CompartmentId;
if (TryGetValue(argc, argv, "comp", &CompartmentId)) {
NETIO_STATUS status;
if (!NETIO_SUCCESS(status = QuicCompartmentIdSetCurrent(CompartmentId))) {
WriteOutput("Failed to set compartment ID = %d: 0x%x\n", CompartmentId, status);
return QUIC_STATUS_INVALID_PARAMETER;
} else {
WriteOutput("Running in Compartment %d\n", CompartmentId);
}
}
#endif
#ifdef QuicSetCurrentThreadAffinityMask
uint8_t CpuCore;
if (TryGetValue(argc, argv, "core", &CpuCore)) {
QuicSetCurrentThreadAffinityMask((DWORD_PTR)(1ull << CpuCore));
}
#endif
TryGetValue(argc, argv, "sendbuf", &UseSendBuffer);
IoSize = UseSendBuffer ? THROUGHPUT_DEFAULT_IO_SIZE_BUFFERED : THROUGHPUT_DEFAULT_IO_SIZE_NONBUFFERED;
TryGetValue(argc, argv, "iosize", &IoSize);
IoCount = UseSendBuffer ? THROGHTPUT_DEFAULT_SEND_COUNT_BUFFERED : THROUGHPUT_DEFAULT_SEND_COUNT_NONBUFFERED;
TryGetValue(argc, argv, "iocount", &IoCount);
size_t Len = strlen(Target);
TargetData.reset(new char[Len + 1]);
QuicCopyMemory(TargetData.get(), Target, Len);
TargetData[Len] = '\0';
BufferAllocator.Initialize(IoSize);
return QUIC_STATUS_SUCCESS;
}
struct ShutdownWrapper {
HQUIC ConnHandle {nullptr};
~ShutdownWrapper() {
if (ConnHandle) {
MsQuic->ConnectionShutdown(ConnHandle, QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0);
}
}
};
QUIC_STATUS
ThroughputClient::Start(
_In_ QUIC_EVENT StopEvnt
) {
ShutdownWrapper Shutdown;
ConnectionData* ConnData = ConnectionDataAllocator.Alloc(this);
if (!ConnData) {
return QUIC_STATUS_OUT_OF_MEMORY;
}
QUIC_STATUS Status =
MsQuic->ConnectionOpen(
Session,
[](HQUIC Handle, void* Context, QUIC_CONNECTION_EVENT* Event) -> QUIC_STATUS {
ConnectionData* ConnData = (ConnectionData*)Context;
return ConnData->Client->
ConnectionCallback(
Handle,
Event,
ConnData);
},
ConnData,
&ConnData->Connection.Handle);
if (QUIC_FAILED(Status)) {
WriteOutput("Failed ConnectionOpen 0x%x\n", Status);
ConnectionDataAllocator.Free(ConnData);
return Status;
}
Shutdown.ConnHandle = ConnData->Connection.Handle;
uint32_t SecFlags = QUIC_CERTIFICATE_FLAG_DISABLE_CERT_VALIDATION;
Status =
MsQuic->SetParam(
ConnData->Connection,
QUIC_PARAM_LEVEL_CONNECTION,
QUIC_PARAM_CONN_CERT_VALIDATION_FLAGS,
sizeof(SecFlags),
&SecFlags);
if (QUIC_FAILED(Status)) {
WriteOutput("Failed Cert Validation Disable 0x%x\n", Status);
return Status;
}
if (!UseSendBuffer) {
BOOLEAN Opt = FALSE;
Status =
MsQuic->SetParam(
ConnData->Connection,
QUIC_PARAM_LEVEL_CONNECTION,
QUIC_PARAM_CONN_SEND_BUFFERING,
sizeof(Opt),
&Opt);
if (QUIC_FAILED(Status)) {
WriteOutput("Failed Disable Send Buffering 0x%x\n", Status);
return Status;
}
}
if (!UseEncryption) {
BOOLEAN value = TRUE;
Status =
MsQuic->SetParam(
ConnData->Connection,
QUIC_PARAM_LEVEL_CONNECTION,
QUIC_PARAM_CONN_DISABLE_1RTT_ENCRYPTION,
sizeof(value),
&value);
if (QUIC_FAILED(Status)) {
WriteOutput("MsQuic->SetParam (CONN_DISABLE_1RTT_ENCRYPTION) failed!\n", Status);
return Status;
}
}
if (QuicAddrGetFamily(&LocalIpAddr) != AF_UNSPEC) {
MsQuic->SetParam(
ConnData->Connection,
QUIC_PARAM_LEVEL_CONNECTION,
QUIC_PARAM_CONN_LOCAL_ADDRESS,
sizeof(LocalIpAddr),
&LocalIpAddr);
}
Status =
MsQuic->ConnectionStart(
ConnData->Connection,
RemoteFamily,
TargetData.get(),
Port);
if (QUIC_FAILED(Status)) {
WriteOutput("Failed ConnectionStart 0x%x\n", Status);
return Status;
}
StreamData* StrmData = StreamDataAllocator.Alloc(this, ConnData->Connection);
Status =
MsQuic->StreamOpen(
ConnData->Connection,
QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL,
[](HQUIC Handle, void* Context, QUIC_STREAM_EVENT* Event) -> QUIC_STATUS {
return ((StreamData*)Context)->Client->
StreamCallback(
Handle,
Event,
(StreamData*)Context);
},
StrmData,
&StrmData->Stream.Handle);
if (QUIC_FAILED(Status)) {
WriteOutput("Failed StreamOpen 0x%x\n", Status);
StreamDataAllocator.Free(StrmData);
return Status;
}
Status =
MsQuic->StreamStart(
StrmData->Stream.Handle,
QUIC_STREAM_START_FLAG_NONE);
if (QUIC_FAILED(Status)) {
WriteOutput("Failed StreamStart 0x%x\n", Status);
StreamDataAllocator.Free(StrmData);
return Status;
}
this->StopEvent = StopEvnt;
StrmData->StartTime = QuicTimeUs64();
if (Length == 0) {
Status =
MsQuic->StreamShutdown(
StrmData->Stream.Handle,
QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL,
0);
return Status;
}
uint32_t SendRequestCount = 0;
while (StrmData->BytesSent < Length && SendRequestCount < IoCount) {
SendRequest* SendReq = SendRequestAllocator.Alloc(&BufferAllocator, IoSize, true);
SendReq->SetLength(Length - StrmData->BytesSent);
StrmData->BytesSent += SendReq->QuicBuffer.Length;
++SendRequestCount;
Status =
MsQuic->StreamSend(
StrmData->Stream,
&SendReq->QuicBuffer,
1,
SendReq->Flags,
SendReq);
if (QUIC_FAILED(Status)) {
WriteOutput("Failed StreamSend 0x%x\n", Status);
SendRequestAllocator.Free(SendReq);
return Status;
}
}
WriteOutput("Started!\n");
Shutdown.ConnHandle = nullptr;
return Status;
}
QUIC_STATUS
ThroughputClient::Wait(
_In_ int Timeout
) {
if (Timeout > 0) {
QuicEventWaitWithTimeout(StopEvent, Timeout);
} else {
QuicEventWaitForever(StopEvent);
}
return QUIC_STATUS_SUCCESS;
}
QUIC_STATUS
ThroughputClient::ConnectionCallback(
_In_ HQUIC /*ConnectionHandle*/,
_Inout_ QUIC_CONNECTION_EVENT* Event,
_Inout_ ConnectionData* ConnData
) {
switch (Event->Type) {
case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE:
ConnectionDataAllocator.Free(ConnData);
QuicEventSet(StopEvent);
break;
default:
break;
}
return QUIC_STATUS_SUCCESS;
}
QUIC_STATUS
ThroughputClient::StreamCallback(
_In_ HQUIC StreamHandle,
_Inout_ QUIC_STREAM_EVENT* Event,
_Inout_ StreamData* StrmData
) {
switch (Event->Type) {
case QUIC_STREAM_EVENT_SEND_COMPLETE: {
SendRequest* Req = (SendRequest*)Event->SEND_COMPLETE.ClientContext;
if (!Event->SEND_COMPLETE.Canceled) {
uint64_t BytesLeftToSend = Length - StrmData->BytesSent;
StrmData->BytesCompleted += Req->QuicBuffer.Length;
if (BytesLeftToSend != 0) {
Req->SetLength(BytesLeftToSend);
StrmData->BytesSent += Req->QuicBuffer.Length;
if (QUIC_SUCCEEDED(
MsQuic->StreamSend(
StrmData->Stream.Handle,
&Req->QuicBuffer,
1,
Req->Flags,
Req))) {
Req = nullptr;
}
}
}
if (Req) {
SendRequestAllocator.Free(Req);
}
break;
}
case QUIC_STREAM_EVENT_PEER_SEND_ABORTED:
case QUIC_STREAM_EVENT_PEER_RECEIVE_ABORTED:
MsQuic->StreamShutdown(
StreamHandle,
QUIC_STREAM_SHUTDOWN_FLAG_ABORT_SEND | QUIC_STREAM_SHUTDOWN_FLAG_ABORT_RECEIVE,
0);
break;
case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: {
StrmData->EndTime = QuicTimeUs64();
uint64_t ElapsedMicroseconds = StrmData->EndTime - StrmData->StartTime;
uint32_t SendRate = (uint32_t)((StrmData->BytesCompleted * 1000 * 1000 * 8) / (1000 * ElapsedMicroseconds));
WriteOutput("[%p][%llu] Closed [%s] after %u.%u ms. (TX %llu bytes @ %u kbps).\n",
StrmData->Connection,
GetStreamID(MsQuic, StreamHandle),
"Complete",
(uint32_t)(ElapsedMicroseconds / 1000),
(uint32_t)(ElapsedMicroseconds % 1000),
StrmData->BytesCompleted, SendRate);
StreamDataAllocator.Free(StrmData);
break;
}
}
return QUIC_STATUS_SUCCESS;
}

Просмотреть файл

@ -0,0 +1,101 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
Abstract:
QUIC Perf Throughput Client declaration. Defines the functions and
variables used in the ThroughputClient class.
--*/
#pragma once
#include "PerfHelpers.h"
#include "PerfBase.h"
#include "ThroughputCommon.h"
#include "SendRequest.h"
class ThroughputClient : public PerfBase {
public:
ThroughputClient();
QUIC_STATUS
Init(
_In_ int argc,
_In_reads_(argc) _Null_terminated_ char* argv[]
) override;
QUIC_STATUS
Start(
_In_ QUIC_EVENT StopEvent
) override;
QUIC_STATUS
Wait(
_In_ int Timeout
) override;
private:
struct ConnectionData {
ConnectionData(
_In_ ThroughputClient* Client)
: Client{Client} {
}
ThroughputClient* Client{ nullptr };
ConnectionScope Connection;
uint8_t Padding[16]; // Padding for Pools
};
struct StreamData {
StreamData(
_In_ ThroughputClient* Client,
_In_ HQUIC Connection)
: Client{Client}, Connection{Connection} {
}
ThroughputClient* Client{ nullptr };
HQUIC Connection;
StreamScope Stream;
uint64_t BytesSent{0};
uint64_t BytesCompleted{0};
uint64_t StartTime{0};
uint64_t EndTime{0};
};
QUIC_STATUS
ConnectionCallback(
_In_ HQUIC ConnectionHandle,
_Inout_ QUIC_CONNECTION_EVENT* Event,
_Inout_ ConnectionData* ConnectionData
);
QUIC_STATUS
StreamCallback(
_In_ HQUIC StreamHandle,
_Inout_ QUIC_STREAM_EVENT* Event,
_Inout_ StreamData* StrmData
);
MsQuicRegistration Registration;
MsQuicSession Session{Registration, THROUGHPUT_ALPN};
QuicPoolAllocator<StreamData> StreamDataAllocator;
QuicPoolAllocator<ConnectionData> ConnectionDataAllocator;
QuicPoolAllocator<SendRequest> SendRequestAllocator;
QuicPoolBufferAllocator BufferAllocator;
UniquePtr<char[]> TargetData;
uint16_t Port{ 0 };
QUIC_EVENT StopEvent{};
uint64_t Length{0};
bool ConstructionSuccess {false};
uint8_t UseSendBuffer{1};
QUIC_ADDR LocalIpAddr{};
uint16_t RemoteFamily{AF_UNSPEC};
uint32_t IoSize{0};
uint32_t IoCount{0};
uint8_t UseEncryption{1};
};

Просмотреть файл

@ -0,0 +1,24 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
Abstract:
QUIC Perf Throughput Common definitions.
--*/
#pragma once
#define THROUGHPUT_DEFAULT_PORT 4433
#define THROUGHPUT_ALPN "Throughput"
#define THROUGHPUT_DEFAULT_DISCONNECT_TIMEOUT (10 * 1000)
#define THROUGHPUT_DEFAULT_IDLE_TIMEOUT 1000
#define THROUGHPUT_SERVER_PEER_UNI 1
#define THROUGHPUT_CLIENT_UNI 1
#define THROUGHPUT_DEFAULT_IO_SIZE_BUFFERED 0x10000
#define THROUGHPUT_DEFAULT_IO_SIZE_NONBUFFERED 0x100000
#define THROGHTPUT_DEFAULT_SEND_COUNT_BUFFERED 1
#define THROUGHPUT_DEFAULT_SEND_COUNT_NONBUFFERED 8

Просмотреть файл

@ -0,0 +1,205 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
Abstract:
QUIC Perf Throughput Server Implementation.
--*/
#ifdef QUIC_CLOG
#include "ThroughputServer.cpp.clog.h"
#endif
#ifndef _KERNEL_MODE
#define QUIC_TEST_APIS 1
#endif
#define QUIC_API_ENABLE_INSECURE_FEATURES 1
#include "msquichelper.h"
#include "quic_trace.h"
#include "ThroughputServer.h"
#include "ThroughputCommon.h"
static
void
PrintHelp(
) {
WriteOutput("Usage: quicperf -TestName:Throughput -ServerMode:1 [options]\n\n"
" -listen:<addr or *> The local IP address to listen on, or * for all IP addresses.\n"
" -thumbprint:<cert_hash> The hash or thumbprint of the certificate to use.\n"
" -cert_store:<store name> The certificate store to search for the thumbprint in.\n"
" -machine_cert:<0/1> Use the machine, or current user's, certificate store. (def:0)\n"
" -connections:<####> The number of connections to create. (def:0)\n"
" -port:<####> The UDP port of the server. (def:%u)\n",
THROUGHPUT_DEFAULT_PORT
);
}
ThroughputServer::ThroughputServer(
_In_ PerfSelfSignedConfiguration* SelfSignedConfig
) : SelfSignedConfig{SelfSignedConfig} {
QuicZeroMemory(&Address, sizeof(Address));
if (Session.IsValid()) {
Session.SetAutoCleanup();
Session.SetPeerUnidiStreamCount(THROUGHPUT_SERVER_PEER_UNI);
Session.SetDisconnectTimeout(THROUGHPUT_DEFAULT_DISCONNECT_TIMEOUT);
Session.SetIdleTimeout(THROUGHPUT_DEFAULT_IDLE_TIMEOUT);
}
}
QUIC_STATUS
ThroughputServer::Init(
_In_ int argc,
_In_reads_(argc) _Null_terminated_ char* argv[]
) {
if (!Listener.IsValid()) {
return Listener.GetInitStatus();
}
uint16_t port = THROUGHPUT_DEFAULT_PORT;
TryGetValue(argc, argv, "port", &port);
const char* localAddress = nullptr;
if (!TryGetValue(argc, argv, "listen", &localAddress)) {
WriteOutput("Server mode must have -listen\n");
PrintHelp();
return QUIC_STATUS_INVALID_PARAMETER;
}
if (!ConvertArgToAddress(localAddress, port, &Address)) {
WriteOutput("Failed to decode IP address: '%s'!\nMust be *, a IPv4 or a IPv6 address.\n", localAddress);
PrintHelp();
return QUIC_STATUS_INVALID_PARAMETER;
}
TryGetValue(argc, argv, "connections", &NumberOfConnections);
QUIC_STATUS Status = SecurityConfig.Initialize(argc, argv, Registration, SelfSignedConfig);
return Status;
}
QUIC_STATUS
ThroughputServer::Start(
_In_ QUIC_EVENT StopEvent
) {
QUIC_STATUS Status =
Listener.Start(
&Address,
[](HQUIC Handle, void* Context, QUIC_LISTENER_EVENT* Event) -> QUIC_STATUS {
return ((ThroughputServer*)Context)->ListenerCallback(Handle, Event);
},
this);
if (QUIC_FAILED(Status)) {
return Status;
}
RefCount = CountHelper{StopEvent};
if (NumberOfConnections > 0) {
for (uint32_t i = 0; i < NumberOfConnections; i++) {
RefCount.AddItem();
}
} else {
//
// Add a single item so we can wait on the Count Helper
//
RefCount.AddItem();
}
return QUIC_STATUS_SUCCESS;
}
QUIC_STATUS
ThroughputServer::Wait(
_In_ int Timeout
) {
if (Timeout > 0) {
RefCount.Wait(Timeout);
} else {
RefCount.WaitForever();
}
return QUIC_STATUS_SUCCESS;
}
QUIC_STATUS
ThroughputServer::ListenerCallback(
_In_ HQUIC /*ListenerHandle*/,
_Inout_ QUIC_LISTENER_EVENT* Event
) {
switch (Event->Type) {
case QUIC_LISTENER_EVENT_NEW_CONNECTION: {
Event->NEW_CONNECTION.SecurityConfig = SecurityConfig;
QUIC_CONNECTION_CALLBACK_HANDLER Handler =
[](HQUIC Conn, void* Context, QUIC_CONNECTION_EVENT* Event) -> QUIC_STATUS {
return ((ThroughputServer*)Context)->
ConnectionCallback(
Conn,
Event);
};
MsQuic->SetCallbackHandler(
Event->NEW_CONNECTION.Connection,
(void*)Handler,
this);
BOOLEAN value = TRUE;
if (QUIC_FAILED(
MsQuic->SetParam(
Event->NEW_CONNECTION.Connection,
QUIC_PARAM_LEVEL_CONNECTION,
QUIC_PARAM_CONN_DISABLE_1RTT_ENCRYPTION,
sizeof(value),
&value))) {
WriteOutput("MsQuic->SetParam (CONN_DISABLE_1RTT_ENCRYPTION) failed!\n");
}
break;
}
}
return QUIC_STATUS_SUCCESS;
}
QUIC_STATUS
ThroughputServer::ConnectionCallback(
_In_ HQUIC ConnectionHandle,
_Inout_ QUIC_CONNECTION_EVENT* Event
) {
switch (Event->Type) {
case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE:
MsQuic->ConnectionClose(ConnectionHandle);
if (NumberOfConnections > 0) {
RefCount.CompleteItem();
}
break;
case QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED: {
QUIC_STREAM_CALLBACK_HANDLER Handler =
[](HQUIC Stream, void* Context, QUIC_STREAM_EVENT* Event) -> QUIC_STATUS {
return ((ThroughputServer*)Context)->
StreamCallback(
Stream,
Event);
};
MsQuic->SetCallbackHandler(Event->PEER_STREAM_STARTED.Stream, (void*)Handler, this);
break;
}
default:
break;
}
return QUIC_STATUS_SUCCESS;
}
QUIC_STATUS
ThroughputServer::StreamCallback(
_In_ HQUIC StreamHandle,
_Inout_ QUIC_STREAM_EVENT* Event
) {
switch (Event->Type) {
case QUIC_STREAM_EVENT_PEER_SEND_ABORTED:
case QUIC_STREAM_EVENT_PEER_RECEIVE_ABORTED:
MsQuic->StreamShutdown(
StreamHandle,
QUIC_STREAM_SHUTDOWN_FLAG_ABORT_SEND | QUIC_STREAM_SHUTDOWN_FLAG_ABORT_RECEIVE,
0);
break;
case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: {
MsQuic->StreamClose(StreamHandle);
break;
}
}
return QUIC_STATUS_SUCCESS;
}

Просмотреть файл

@ -0,0 +1,69 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
Abstract:
QUIC Perf Throughput Server declaration. Defines the functions and
variables used in the ThroughputServer class.
--*/
#pragma once
#include "PerfHelpers.h"
#include "PerfBase.h"
#include "ThroughputCommon.h"
class ThroughputServer : public PerfBase {
public:
ThroughputServer(
_In_ PerfSelfSignedConfiguration* SelfSignedConfig
);
QUIC_STATUS
Init(
_In_ int argc,
_In_reads_(argc) _Null_terminated_ char* argv[]
) override;
QUIC_STATUS
Start(
_In_ QUIC_EVENT StopEvent
) override;
QUIC_STATUS
Wait(
int Timeout
) override;
private:
QUIC_STATUS
ListenerCallback(
_In_ HQUIC ListenerHandle,
_Inout_ QUIC_LISTENER_EVENT* Event
);
QUIC_STATUS
ConnectionCallback(
_In_ HQUIC ConnectionHandle,
_Inout_ QUIC_CONNECTION_EVENT* Event
);
QUIC_STATUS
StreamCallback(
_In_ HQUIC StreamHandle,
_Inout_ QUIC_STREAM_EVENT* Event
);
MsQuicRegistration Registration;
MsQuicSession Session{Registration, THROUGHPUT_ALPN};
MsQuicListener Listener{Session};
PerfSelfSignedConfiguration* SelfSignedConfig;
PerfSecurityConfig SecurityConfig;
QUIC_ADDR Address{};
uint32_t NumberOfConnections {0};
CountHelper RefCount;
};

Просмотреть файл

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM">
<Configuration>Debug</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM">
<Configuration>Release</Configuration>
<Platform>ARM</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|ARM64">
<Configuration>Debug</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|ARM64">
<Configuration>Release</Configuration>
<Platform>ARM64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClCompile Include="quicmain.cpp" />
<ClCompile Include="ThroughputClient.cpp" />
<ClCompile Include="ThroughputServer.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="PerfHelpers.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{11633785-79cc-4c7d-ab6a-aecdf29a1fa7}</ProjectGuid>
<TemplateGuid>{0a049372-4c4d-4ea0-a64e-dc6ad88ceca1}</TemplateGuid>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<MinimumVisualStudioVersion>12.0</MinimumVisualStudioVersion>
<DriverType>KMDF</DriverType>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<TargetVersion>Windows10</TargetVersion>
<PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset>
<ConfigurationType>StaticLibrary</ConfigurationType>
<DriverTargetPlatform>Universal</DriverTargetPlatform>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings" />
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<TargetName>perflib</TargetName>
<IntDir>$(SolutionDir)build\winkernel\$(Platform)_$(Configuration)_schannel\obj\$(ProjectName)\</IntDir>
<OutDir>$(SolutionDir)build\winkernel\$(Platform)_$(Configuration)_schannel\bin\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..\;..\..\inc;..\..\..\submodules\wil\include;$(SolutionDir)build\winkernel\$(Platform)_$(Configuration)_schannel\inc;$(IntDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<WholeProgramOptimization>true</WholeProgramOptimization>
<AdditionalOptions Condition="'$(Platform)'!='x64'">/Gw /kernel /ZH:SHA_256</AdditionalOptions>
<AdditionalOptions Condition="'$(Platform)'=='x64'">/Gw /kernel /ZH:SHA_256 -d2jumptablerdata -d2epilogunwindrequirev2</AdditionalOptions>
</ClCompile>
<Lib>
<LinkTimeCodeGeneration>true</LinkTimeCodeGeneration>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
<PreprocessorDefinitions>QUIC_EVENTS_MANIFEST_ETW;QUIC_LOGS_MANIFEST_ETW;QUIC_DISABLE_0RTT_TESTS;SECURITY_KERNEL;SECURITY_WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
<DisableSpecificWarnings>4748;5040;4459;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<PreprocessorDefinitions>QUIC_EVENTS_MANIFEST_ETW;QUIC_LOGS_MANIFEST_ETW;QUIC_DISABLE_0RTT_TESTS;SECURITY_KERNEL;SECURITY_WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<LanguageStandard>stdcpp17</LanguageStandard>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<DisableSpecificWarnings>4748;5040;4459;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets" />
</Project>

106
src/perf/lib/quicmain.cpp Normal file
Просмотреть файл

@ -0,0 +1,106 @@
/*++
Copyright (c) Microsoft Corporation.
Licensed under the MIT License.
Abstract:
QUIC Perf Main execution engine.
--*/
#ifndef _KERNEL_MODE
#define QUIC_TEST_APIS 1
#endif
#include "quic_driver_main.h"
#include "ThroughputServer.h"
#include "ThroughputClient.h"
#include "quic_trace.h"
#ifdef QUIC_CLOG
#include "quicmain.cpp.clog.h"
#endif
const QuicApiTable* MsQuic;
PerfBase* TestToRun;
static
void
PrintHelp(
) {
WriteOutput("Usage: quicperf -TestName:[Throughput|] [options]\n" \
"\n" \
" -ServerMode:<1:0> default: '0'\n" \
"\n\n" \
"Run a test without arguments to see it's specific help\n"
);
}
QUIC_STATUS
QuicMainStart(
_In_ int argc,
_In_reads_(argc) _Null_terminated_ char* argv[],
_In_ QUIC_EVENT StopEvent,
_In_ PerfSelfSignedConfiguration* SelfSignedConfig
) {
const char* TestName = GetValue(argc, argv, "TestName");
if (!TestName) {
WriteOutput("Must have a TestName specified. Ex: -TestName:Throughput\n");
PrintHelp();
return QUIC_STATUS_INVALID_PARAMETER;
}
uint8_t ServerMode = 0;
TryGetValue(argc, argv, "ServerMode", &ServerMode);
QUIC_STATUS Status;
MsQuic = new QuicApiTable{};
if (QUIC_FAILED(Status = MsQuic->InitStatus())) {
delete MsQuic;
MsQuic = nullptr;
return Status;
}
if (IsValue(TestName, "Throughput")) {
if (ServerMode) {
TestToRun = new ThroughputServer{SelfSignedConfig};
} else {
TestToRun = new ThroughputClient{};
}
} else {
delete MsQuic;
return QUIC_STATUS_INVALID_PARAMETER;
}
if (TestToRun != nullptr) {
Status = TestToRun->Init(argc, argv);
if (QUIC_SUCCEEDED(Status)) {
Status = TestToRun->Start(StopEvent);
if (QUIC_SUCCEEDED(Status)) {
return QUIC_STATUS_SUCCESS;
}
}
} else {
Status = QUIC_STATUS_OUT_OF_MEMORY;
}
delete TestToRun;
delete MsQuic;
return Status;
}
QUIC_STATUS
QuicMainStop(
_In_ int Timeout
) {
if (TestToRun == nullptr) {
return QUIC_STATUS_SUCCESS;
}
QUIC_STATUS Status = TestToRun->Wait(Timeout);
delete TestToRun;
delete MsQuic;
return Status;
}

Просмотреть файл

@ -302,12 +302,10 @@ LogTestFailure(
//
// Name of the driver service for msquictest.sys.
//
#define QUIC_TEST_DRIVER_NAME "msquictest"
#define QUIC_DRIVER_NAME "msquictest"
#ifdef _WIN32
#define QUIC_TEST_IOCTL_PATH "\\\\.\\\\" QUIC_TEST_DRIVER_NAME
//
// {85C2D886-FA01-4DDA-AAED-9A16CC7DA6CE}
//

Просмотреть файл

@ -11,6 +11,7 @@
#include <MsQuicTests.h>
#include <msquichelper.h>
#include "quic_trace.h"
#include "quic_driver_helpers.h"
#undef min // gtest headers conflict with previous definitions of min/max.
#undef max
#include "gtest/gtest.h"
@ -511,293 +512,3 @@ std::ostream& operator << (std::ostream& o, const DrillInitialPacketTokenArgs& a
class WithDrillInitialPacketTokenArgs: public testing::Test,
public testing::WithParamInterface<DrillInitialPacketTokenArgs> {
};
//
// Windows Kernel Mode Helpers
//
#ifdef _WIN32
class QuicDriverService {
SC_HANDLE ScmHandle;
SC_HANDLE ServiceHandle;
public:
QuicDriverService() :
ScmHandle(nullptr),
ServiceHandle(nullptr) {
}
bool Initialize() {
uint32_t Error;
ScmHandle = OpenSCManager(nullptr, nullptr, SC_MANAGER_ALL_ACCESS);
if (ScmHandle == nullptr) {
Error = GetLastError();
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"GetFullPathName failed");
return false;
}
QueryService:
ServiceHandle =
OpenServiceA(
ScmHandle,
QUIC_TEST_DRIVER_NAME,
SERVICE_ALL_ACCESS);
if (ServiceHandle == nullptr) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
GetLastError(),
"OpenService failed");
char DriverFilePath[MAX_PATH] = {0};
GetModuleFileNameA(NULL, DriverFilePath, MAX_PATH);
char* PathEnd = strrchr(DriverFilePath, '\\');
if (!PathEnd ||
sizeof(DriverFilePath) - (PathEnd - DriverFilePath) < sizeof("msquictest.sys")) {
QuicTraceEvent(
LibraryError,
"[ lib] ERROR, %s.",
"Can't build msquictest.sys full path");
return false;
}
memcpy(PathEnd + 1, "msquictest.sys", sizeof("msquictest.sys"));
if (GetFileAttributesA(DriverFilePath) == INVALID_FILE_ATTRIBUTES) {
QuicTraceEvent(
LibraryError,
"[ lib] ERROR, %s.",
"Failed to find msquictest.sys");
return false;
}
ServiceHandle =
CreateServiceA(
ScmHandle,
QUIC_TEST_DRIVER_NAME,
QUIC_TEST_DRIVER_NAME,
SC_MANAGER_ALL_ACCESS,
SERVICE_KERNEL_DRIVER,
SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL,
DriverFilePath,
nullptr,
nullptr,
"msquic\0",
nullptr,
nullptr);
if (ServiceHandle == nullptr) {
Error = GetLastError();
if (Error == ERROR_SERVICE_EXISTS) {
goto QueryService;
}
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"CreateService failed");
return false;
}
}
return true;
}
void Uninitialize() {
if (ServiceHandle != nullptr) {
CloseServiceHandle(ServiceHandle);
}
if (ScmHandle != nullptr) {
CloseServiceHandle(ScmHandle);
}
}
bool Start() {
if (!StartServiceA(ServiceHandle, 0, nullptr)) {
uint32_t Error = GetLastError();
if (Error != ERROR_SERVICE_ALREADY_RUNNING) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"StartService failed");
return false;
}
}
return true;
}
};
#else
class QuicDriverService {
public:
bool Initialize() { return false; }
void Uninitialize() { }
bool Start() { return false; }
};
#endif // _WIN32
#ifdef _WIN32
class QuicDriverClient {
HANDLE DeviceHandle;
public:
QuicDriverClient() : DeviceHandle(INVALID_HANDLE_VALUE) { }
bool Initialize(
_In_ QUIC_SEC_CONFIG_PARAMS* SecConfigParams
) {
uint32_t Error;
DeviceHandle =
CreateFileA(
QUIC_TEST_IOCTL_PATH,
GENERIC_READ | GENERIC_WRITE,
0,
nullptr, // no SECURITY_ATTRIBUTES structure
OPEN_EXISTING, // No special create flags
FILE_FLAG_OVERLAPPED, // Allow asynchronous requests
nullptr);
if (DeviceHandle == INVALID_HANDLE_VALUE) {
Error = GetLastError();
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"CreateFile failed");
return false;
}
if (!Run(IOCTL_QUIC_SEC_CONFIG, SecConfigParams->Thumbprint, sizeof(SecConfigParams->Thumbprint), 30000)) {
CloseHandle(DeviceHandle);
DeviceHandle = INVALID_HANDLE_VALUE;
QuicTraceEvent(
LibraryError,
"[ lib] ERROR, %s.",
"Run(IOCTL_QUIC_SEC_CONFIG) failed");
return false;
}
return true;
}
void Uninitialize() {
if (DeviceHandle != INVALID_HANDLE_VALUE) {
CloseHandle(DeviceHandle);
}
}
bool Run(
_In_ uint32_t IoControlCode,
_In_reads_bytes_opt_(InBufferSize)
void* InBuffer,
_In_ uint32_t InBufferSize,
_In_ uint32_t TimeoutMs = 30000
) {
uint32_t Error;
OVERLAPPED Overlapped = { 0 };
Overlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (Overlapped.hEvent == nullptr) {
Error = GetLastError();
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"CreateEvent failed");
return false;
}
QuicTraceLogVerbose(
TestSendIoctl,
"[test] Sending IOCTL %u with %u bytes.",
IoGetFunctionCodeFromCtlCode(IoControlCode),
InBufferSize);
if (!DeviceIoControl(
DeviceHandle,
IoControlCode,
InBuffer, InBufferSize,
nullptr, 0,
nullptr,
&Overlapped)) {
Error = GetLastError();
if (Error != ERROR_IO_PENDING) {
CloseHandle(Overlapped.hEvent);
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"DeviceIoControl failed");
return false;
}
}
DWORD dwBytesReturned;
if (!GetOverlappedResultEx(
DeviceHandle,
&Overlapped,
&dwBytesReturned,
TimeoutMs,
FALSE)) {
Error = GetLastError();
if (Error == WAIT_TIMEOUT) {
Error = ERROR_TIMEOUT;
CancelIoEx(DeviceHandle, &Overlapped);
}
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Error,
"GetOverlappedResultEx failed");
} else {
Error = ERROR_SUCCESS;
}
CloseHandle(Overlapped.hEvent);
return Error == ERROR_SUCCESS;
}
bool Run(
_In_ uint32_t IoControlCode,
_In_ uint32_t TimeoutMs = 30000
) {
return Run(IoControlCode, nullptr, 0, TimeoutMs);
}
template<class T>
bool Run(
_In_ uint32_t IoControlCode,
_In_ const T& Data,
_In_ uint32_t TimeoutMs = 30000
) {
return Run(IoControlCode, (void*)&Data, sizeof(Data), TimeoutMs);
}
};
#else
class QuicDriverClient {
public:
bool Initialize(
_In_ QUIC_SEC_CONFIG_PARAMS* SecConfigParams
) {
UNREFERENCED_PARAMETER(SecConfigParams);
return false;
}
void Uninitialize() { }
bool Run(
_In_ uint32_t IoControlCode,
_In_ void* InBuffer,
_In_ uint32_t InBufferSize,
_In_ uint32_t TimeoutMs = 30000
) {
UNREFERENCED_PARAMETER(IoControlCode);
UNREFERENCED_PARAMETER(InBuffer);
UNREFERENCED_PARAMETER(InBufferSize);
UNREFERENCED_PARAMETER(TimeoutMs);
return false;
}
bool
Run(
_In_ uint32_t IoControlCode,
_In_ uint32_t TimeoutMs = 30000
) {
return Run(IoControlCode, nullptr, 0, TimeoutMs);
}
template<class T>
bool
Run(
_In_ uint32_t IoControlCode,
_In_ const T& Data,
_In_ uint32_t TimeoutMs = 30000
) {
return Run(IoControlCode, (void*)&Data, sizeof(Data), TimeoutMs);
}
};
#endif // _WIN32