Skip to content
This repository was archived by the owner on Jan 12, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 97 additions & 1 deletion src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

using Azure.Identity;
using Azure.Quantum;
using Azure.Quantum.Jobs;
using Azure.Quantum.Jobs.Models;

using Microsoft.Azure.Quantum.Exceptions;
Expand Down Expand Up @@ -176,6 +178,100 @@ public async Task ListProviderStatusTest()
Assert.AreEqual(0, max);
}

[TestMethod]
[TestCategory("Local")]
public async Task ApplicationIdTest()
{
const string ENV_VAR_APPID = "EnvVarAppId";
const string OPTIONS_APPID = "OptionAppId";
const string LONG_ENV_VAR_APPID = "LongEnvVarAppId";
const string LONG_OPTIONS_APPID = "LongOptionAppId";
const string VERY_LONG_ENV_VAR_APPID = "VeryVeryVeryVeryVeryVeryLongEnvVarAppId";
const string VERY_LONG_OPTIONS_APPID = "VeryVeryVeryVeryVeryVeryLongOptionAppId";
const string APPID_ENV_VAR_NAME = "AZURE_QUANTUM_NET_APPID";

Func<QuantumJobClientOptions, Workspace> createWorkspace = (QuantumJobClientOptions options) =>
{
var credential = new ClientSecretCredential(tenantId: "72f988bf-86f1-41af-91ab-2d7cd011db47",
clientId: "00000000-0000-0000-0000-000000000000",
clientSecret: "PLACEHOLDER");
return new Workspace(subscriptionId: "SubscriptionId",
resourceGroupName: "ResourceGroupName",
workspaceName: "WorkspaceName",
location: "WestUs",
options: options,
credential: credential);
};

var originalEnvironmentAppId = Environment.GetEnvironmentVariable(APPID_ENV_VAR_NAME);
try
{

// Test with no Environment AppId and no Options AppId
Environment.SetEnvironmentVariable(APPID_ENV_VAR_NAME, null);
var workspace = createWorkspace(null);
Assert.IsNotNull(workspace.ClientOptions);
Assert.IsNotNull(workspace.ClientOptions.Diagnostics);
Assert.AreEqual("", workspace.ClientOptions.Diagnostics.ApplicationId);

// Test with Environment AppId and no Options AppId
Environment.SetEnvironmentVariable(APPID_ENV_VAR_NAME, ENV_VAR_APPID);
workspace = createWorkspace(null);
Assert.IsNotNull(workspace.ClientOptions);
Assert.IsNotNull(workspace.ClientOptions.Diagnostics);
Assert.AreEqual(ENV_VAR_APPID, workspace.ClientOptions.Diagnostics.ApplicationId);

// Test with no Environment AppId and with Options AppId
Environment.SetEnvironmentVariable(APPID_ENV_VAR_NAME, null);
var options = new QuantumJobClientOptions();
options.Diagnostics.ApplicationId = OPTIONS_APPID;
workspace = createWorkspace(options);
Assert.IsNotNull(workspace.ClientOptions);
Assert.IsNotNull(workspace.ClientOptions.Diagnostics);
Assert.AreEqual(OPTIONS_APPID, workspace.ClientOptions.Diagnostics.ApplicationId);

// Test with Environment AppId and with Options AppId
Environment.SetEnvironmentVariable(APPID_ENV_VAR_NAME, ENV_VAR_APPID);
options = new QuantumJobClientOptions();
options.Diagnostics.ApplicationId = OPTIONS_APPID;
workspace = createWorkspace(options);
Assert.IsNotNull(workspace.ClientOptions);
Assert.IsNotNull(workspace.ClientOptions.Diagnostics);
Assert.AreEqual($"{OPTIONS_APPID}-{ENV_VAR_APPID}", workspace.ClientOptions.Diagnostics.ApplicationId);

// Test with long (>24 chars) combination of Environment AppId and Options AppId
Environment.SetEnvironmentVariable(APPID_ENV_VAR_NAME, LONG_ENV_VAR_APPID);
options = new QuantumJobClientOptions();
options.Diagnostics.ApplicationId = LONG_OPTIONS_APPID;
workspace = createWorkspace(options);
Assert.IsNotNull(workspace.ClientOptions);
Assert.IsNotNull(workspace.ClientOptions.Diagnostics);
var truncatedAppId = $"{LONG_OPTIONS_APPID}-{LONG_ENV_VAR_APPID}".Substring(0, 24);
Assert.AreEqual(truncatedAppId, workspace.ClientOptions.Diagnostics.ApplicationId);

// Test with long (>24 chars) Environment AppId and no Options AppId
Environment.SetEnvironmentVariable(APPID_ENV_VAR_NAME, VERY_LONG_ENV_VAR_APPID);
workspace = createWorkspace(null);
Assert.IsNotNull(workspace.ClientOptions);
Assert.IsNotNull(workspace.ClientOptions.Diagnostics);
Assert.AreEqual(VERY_LONG_ENV_VAR_APPID.Substring(0, 24), workspace.ClientOptions.Diagnostics.ApplicationId);

// Test with long (>24 chars) Options AppId and no Environment AppId
Environment.SetEnvironmentVariable(APPID_ENV_VAR_NAME, null);
options = new QuantumJobClientOptions();
Assert.ThrowsException<System.ArgumentOutOfRangeException>(() =>
options.Diagnostics.ApplicationId = VERY_LONG_OPTIONS_APPID);
}
finally
{
// restore original env var AZURE_QUANTUM_NET_APPID
if (originalEnvironmentAppId != null)
{
Environment.SetEnvironmentVariable(APPID_ENV_VAR_NAME, originalEnvironmentAppId);
}
}
}

private static void AssertJob(CloudJob job)
{
Assert.IsNotNull(job);
Expand Down
38 changes: 33 additions & 5 deletions src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ namespace Microsoft.Azure.Quantum
using System.Threading;
using System.Threading.Tasks;
using global::Azure.Core;
using global::Azure.Identity;
using global::Azure.Quantum;
using global::Azure.Quantum.Jobs;
using global::Azure.Quantum.Jobs.Models;
Expand Down Expand Up @@ -50,9 +49,10 @@ public Workspace(

// Optional parameters:
credential ??= CredentialFactory.CreateCredential(CredentialType.Default, subscriptionId);
options ??= new QuantumJobClientOptions();
options.Diagnostics.ApplicationId = options.Diagnostics.ApplicationId
?? Environment.GetEnvironmentVariable("AZURE_QUANTUM_NET_APPID");

// Make sure use the property Setter as we have some logic
// tto apply here
this.ClientOptions = options;

this.ResourceGroupName = resourceGroupName;
this.WorkspaceName = workspaceName;
Expand All @@ -65,7 +65,7 @@ public Workspace(
workspaceName,
location,
credential,
options);
this.ClientOptions);
}

public string ResourceGroupName { get; }
Expand All @@ -81,6 +81,34 @@ public Workspace(
/// </summary>
public QuantumJobClient Client { get; }

/// <summary>
/// The options used to create the client to communicate with the service.
/// </summary>
public QuantumJobClientOptions ClientOptions
{
get => this.clientOptions;
set
{
// Set the ApplicationId that will be added as a UserAgent prefix
// in calls to the Azure Quantum API.
var applicationId = string.Join('-',
value?.Diagnostics?.ApplicationId?.Trim(),
Environment.GetEnvironmentVariable("AZURE_QUANTUM_NET_APPID")?.Trim()
)?.Trim('-', ' ');
if (applicationId?.Length > 24)
{
applicationId = applicationId?.Substring(0, 24);
}

value ??= new QuantumJobClientOptions();
value.Diagnostics.ApplicationId = applicationId;

this.clientOptions = value;
}
}

private QuantumJobClientOptions clientOptions;

/// <summary>
/// Submits the job.
/// </summary>
Expand Down
169 changes: 169 additions & 0 deletions src/Qir/Common/cmake/qir_cmake_include.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,172 @@ macro(target_source_from_qir target_name source_file)
${OBJFILE}
)
endmacro()


#===============================================================================
# Common flags

# Always use available Spectre mitigations where available
if (NOT APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mspeculative-load-hardening -mretpoline")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mspeculative-load-hardening -mretpoline")
endif()

set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG")

#===============================================================================
# Warnings

# Treat warnings as errors:
# https://clang.llvm.org/docs/UsersManual.html#options-to-control-error-and-warning-messages
set(WARNING_FLAGS "-Werror")

# Enable all warnings:
# https://clang.llvm.org/docs/UsersManual.html#enabling-all-diagnostics
# https://clang.llvm.org/docs/DiagnosticsReference.html
set(WARNING_FLAGS "${WARNING_FLAGS} -Weverything")

# Disable these warnings:

# We don't care about keeping compatibility with C++98/03, C++11, C++14. Any new features unknown to our compiler version will be reported as errors.
# -Wc++98-compat-pedantic
# -Wc++98-compat,
# -Wc++98-compat-local-type-template-args, -Wc++98-compat-unnamed-type-template-args, -Wpre-c++14-compat,
# -Wpre-c++17-compat, -Wpre-c++20-compat, -Wpre-c++2b-compat.
# -Wc++98-compat-bind-to-temporary-copy, -Wc++98-compat-extra-semi,
# -Wpre-c++14-compat-pedantic,
# -Wc++98-c++11-compat-binary-literal, -Wpre-c++14-compat.
# -Wpre-c++17-compat-pedantic,
# -Wpre-c++17-compat.
# -Wpre-c++20-compat-pedantic,
# -Wpre-c++20-compat.
# -Wpre-c++2b-compat-pedantic (= -Wpre-c++2b-compat).

# https://clang.llvm.org/docs/DiagnosticsReference.html#wc-98-compat-pedantic
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-c++98-compat-pedantic")

# Old-style casts increase readability as opposed to `reinterpret_cast<..>()`. We want to be able to use the old-style casts.
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-old-style-cast")

# Even if the `switch` covers all the enumerators, it is still good to have `default` label to cover the potential newly added (but not handled) enumerators.
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-covered-switch-default")

# We are OK using C99 features.
# -Wc99-extension
# -Wc99-designator
# -Wc++20-designator
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-c99-extensions")

# We are OK that the structs are padded to align the fields.
# https://clang.llvm.org/docs/DiagnosticsReference.html#wpadded
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-padded")

# We are OK with abstract classes.
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-weak-vtables")

# Temporarily disable the following warnings (until QIR RT is refactored to expose C interface).

# Looks like the `-Wglobal-constructors` warns that the instance of the `__dllexport` class/struct (or a static member var of such class/struct)
# needs to be constructible by calling a global `__dllexport` function (to guarantee that a single instance is created and the same instance is used
# both inside and outside of the binary (dynamic library or executable)).
# Or it warns about the constructor that is invoked for a global (or static member) variable _before_ the `main()` is invoked, thus slowing down the start,
# see https://stackoverflow.com/a/15708829/6362941

# https://clang.llvm.org/docs/DiagnosticsReference.html#wglobal-constructors
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-global-constructors")

# Looks like the `-Wexit-time-destructors` warns that the destructor of a global or static member variable will be invoked
# _after_ the `main()` returns (thus slowing down the termination/restart).
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-exit-time-destructors")

# Temporarily disable "-Wextra-semi-stmt" that warns about redundant `;` in the end of `INFO(id);` of Catch tests framework (which looks fixed in the latest Catch version).
# Disable until the Catch header "src\Qir\Common\Externals\catch2\catch.hpp" is updated to a version newer than v2.12.1 (from https://github.com/catchorg/Catch2).

# https://clang.llvm.org/docs/DiagnosticsReference.html#wextra-semi-stmt
set(WARNING_FLAGS "${WARNING_FLAGS} -Wno-extra-semi-stmt")

# Save the assembled warnings
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")


#===============================================================================
# Sanitizers (https://clang.llvm.org/docs/UsersManual.html#controlling-code-generation):

if (NOT WIN32)
set(SANITIZE_FLAGS "")

# Undefined Behavior Sanitizer (https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html)
# Win:
# FAILED: lib/QIR/Microsoft.Quantum.Qir.Runtime.dll lib/QIR/Microsoft.Quantum.Qir.Runtime.lib
# lld-link: error: /failifmismatch: mismatch detected for 'RuntimeLibrary':
# >>> lib/QIR/CMakeFiles/qir-rt-support-obj.dir/QubitManager.cpp.obj has value MD_DynamicRelease
# >>> clang_rt.ubsan_standalone_cxx-x86_64.lib(ubsan_type_hash_win.cc.obj) has value MT_StaticRelease
# clang++: error: linker command failed with exit code 1 (use -v to see invocation)
set(SANITIZE_FLAGS "${SANITIZE_FLAGS} -fsanitize=undefined -fsanitize=float-divide-by-zero -fsanitize=unsigned-integer-overflow -fsanitize=implicit-conversion -fsanitize=local-bounds -fsanitize=nullability")

# TODO:
# For Win consider extra build configuration linking all libs statically, enable `-fsanitize=undefined`, run the statically linked tests.

#if (-not ($IsMacOS)) # Cannot be combined with `-fsanitize=address`.
#{
# # Safe Stack instrumentation (https://clang.llvm.org/docs/SafeStack.html):
# # No support for Win, Mac.
# # clang: error: unsupported option '-fsanitize=safe-stack' for target 'x86_64-apple-darwin19.6.0'
# # Linking a DSO with SafeStack is not currently supported. But compilation, linking, and test runs all succeed.
# $sanitizeFlags += " -fsanitize=safe-stack"
#}

## Memory Sanitizer (https://clang.llvm.org/docs/MemorySanitizer.html)
## Win: Not supported.
## clang: error: unsupported option '-fsanitize=memory' for target 'x86_64-pc-windows-msvc'
## WSL: Complains for use-of-uninitialized-value in `catch2/catch.hpp` during initialization of global vars
## (if run both as `pwsh Runtime/test-qir-runtime.ps1` (or Tests/test-qir-tests.ps1) and as standalone).
## An update of `catch2/catch.hpp` to 2.13.6 (search for "go to the v2.x branch" at https://github.com/catchorg/Catch2) didn't help.
## Suppressing of the errors in the updated `catch2/catch.hpp` and standard library headers eventually bumps into errors reported in `memcmp`,
## suppressing of which does not work (https://github.com/google/sanitizers/issues/1429#issuecomment-876799463).
## Looks like MSan will not work until the libstdc++ is recompiled to be instrumented (https://clang.llvm.org/docs/MemorySanitizer.html#handling-external-code).
## Instrumenting libstdc++ during CI builds seems impractical (https://stackoverflow.com/a/22301584/6362941).
#$sanitizeFlags += " -fsanitize=memory -fsanitize-memory-track-origins=2"

# Address Sanitizer (https://clang.llvm.org/docs/AddressSanitizer.html)
# Win: (Conflict between the ASan library and MSVC library)
# [19/35] Linking CXX shared library lib\QIR\Microsoft.Quantum.Qir.Runtime.dll
# FAILED: lib/QIR/Microsoft.Quantum.Qir.Runtime.dll lib/QIR/Microsoft.Quantum.Qir.Runtime.lib
# cmd.exe /C "cd . && C:\PROGRA~1\LLVM12\bin\CLANG_~1.EXE -fuse-ld=lld-link -nostartfiles -nostdlib -Werror -Weverything .... \
# -fsanitize=address -g -Xclang -gcodeview -O0 -DDEBUG -D_DEBUG -D_DLL -D_MT -Xclang --dependent-lib=msvcrtd \
# -Xlinker /guard:cf -shared -o lib\QIR\Microsoft.Quantum.Qir.Runtime.dll -Xlinker /implib:lib\QIR\Microsoft.Quantum.Qir.Runtime.lib \
# -Xlinker /pdb:lib\QIR\Microsoft.Quantum.Qir.Runtime.pdb -Xlinker /version:0.0 lib/QIR/bridge-rt.obj \
# lib/QIR/CMakeFiles/qir-rt-support-obj.dir/QirRange.cpp.obj lib/QIR/CMakeFiles/qir-rt-support-obj.dir/OutputStream.cpp.obj ....\
# -lkernel32 -luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid -lcomdlg32 -ladvapi32 -loldnames && cd ."
# lld-link: error: duplicate symbol: malloc
# >>> defined at C:\src\llvm_package_6923b0a7\llvm-project\compiler-rt\lib\asan\asan_win_dll_thunk.cpp:34
# >>> clang_rt.asan_dll_thunk-x86_64.lib(asan_win_dll_thunk.cpp.obj)
# >>> defined at ucrtbased.dll
# clang++: error: linker command failed with exit code 1 (use -v to see invocation)

# https://clang.llvm.org/docs/AddressSanitizer.html
set(SANITIZE_FLAGS "${SANITIZE_FLAGS} -fsanitize=address")

# TODO:
# * Some tests verify the failure behavior, i.e. they cause `Fail()` to be called and return to the caller with the exception.
# Any allocations made between the call and the exception throw (caught by `REQUIRE_THROWS()`) are leaking.
# Extract such tests to a separate .cpp file or executable and compile with leak check off (or suppress leaks in that .cpp or executable only).

# Common for all sanitizers:
# https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#suppressing-errors-in-recompiled-code-ignorelist
# https://releases.llvm.org/11.0.1/tools/clang/docs/SanitizerSpecialCaseList.html
set(SANITIZE_FLAGS "${SANITIZE_FLAGS} -fsanitize-blacklist=${CMAKE_CURRENT_LIST_DIR}/../../UBSan.ignore")

# https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
set(SANITIZE_FLAGS "${SANITIZE_FLAGS} -fno-omit-frame-pointer")

# https://clang.llvm.org/docs/AddressSanitizer.html
set(SANITIZE_FLAGS "${SANITIZE_FLAGS} -fno-optimize-sibling-calls")

# Save the flags
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${SANITIZE_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${SANITIZE_FLAGS}")
endif()

Loading