Skip to content
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
159 changes: 136 additions & 23 deletions onnxruntime/core/providers/qnn/rpcmem_library.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,157 @@
// Licensed under the MIT License

#include "core/providers/qnn/rpcmem_library.h"

#if defined(_WIN32)
#include <filesystem>

Check warning on line 7 in onnxruntime/core/providers/qnn/rpcmem_library.cc

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 <filesystem> is an unapproved C++17 header. [build/c++17] [5] Raw Output: onnxruntime/core/providers/qnn/rpcmem_library.cc:7: <filesystem> is an unapproved C++17 header. [build/c++17] [5]

#include <sysinfoapi.h>

Check warning on line 9 in onnxruntime/core/providers/qnn/rpcmem_library.cc

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Found C system header after C++ system header. Should be: rpcmem_library.h, c system, c++ system, other. [build/include_order] [4] Raw Output: onnxruntime/core/providers/qnn/rpcmem_library.cc:9: Found C system header after C++ system header. Should be: rpcmem_library.h, c system, c++ system, other. [build/include_order] [4]
#include <winsvc.h>

Check warning on line 10 in onnxruntime/core/providers/qnn/rpcmem_library.cc

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Found C system header after C++ system header. Should be: rpcmem_library.h, c system, c++ system, other. [build/include_order] [4] Raw Output: onnxruntime/core/providers/qnn/rpcmem_library.cc:10: Found C system header after C++ system header. Should be: rpcmem_library.h, c system, c++ system, other. [build/include_order] [4]
#endif // defined(_WIN32)

#include "core/providers/qnn/ort_api.h"

namespace onnxruntime::qnn {

// Unload the dynamic library referenced by `library_handle`.
// Avoid throwing because this may run from a dtor.
void DynamicLibraryHandleDeleter::operator()(void* library_handle) noexcept {
if (library_handle == nullptr) {
return;
}

const auto& env = GetDefaultEnv();
const auto unload_status = env.UnloadDynamicLibrary(library_handle);

if (!unload_status.IsOK()) {
LOGS_DEFAULT(WARNING) << "Failed to unload dynamic library. Error: " << unload_status.ErrorMessage();
}
}

namespace {

const PathChar* GetRpcMemSharedLibraryPath() {
#if defined(_WIN32)
return ORT_TSTR("libcdsprpc.dll");
#else
return ORT_TSTR("libcdsprpc.so");
#endif

struct ServiceHandleDeleter {
void operator()(SC_HANDLE handle) { ::CloseServiceHandle(handle); }
};

using UniqueServiceHandle = std::unique_ptr<std::remove_pointer_t<SC_HANDLE>, ServiceHandleDeleter>;

Check warning on line 40 in onnxruntime/core/providers/qnn/rpcmem_library.cc

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Add #include <memory> for unique_ptr<> [build/include_what_you_use] [4] Raw Output: onnxruntime/core/providers/qnn/rpcmem_library.cc:40: Add #include <memory> for unique_ptr<> [build/include_what_you_use] [4]

Status ReadEnvironmentVariable(const wchar_t* name, std::wstring& value_out) {
const DWORD value_size = ::GetEnvironmentVariableW(name, nullptr, 0);
ORT_RETURN_IF(value_size == 0,
"Failed to get environment variable length. GetEnvironmentVariableW error: ", ::GetLastError());

std::vector<wchar_t> value(value_size);

ORT_RETURN_IF(::GetEnvironmentVariableW(name, value.data(), value_size) == 0,
"Failed to get environment variable value. GetEnvironmentVariableW error: ", ::GetLastError());

value_out = std::wstring{value.data()};
return Status::OK();
}

DynamicLibraryHandle LoadDynamicLibrary(const PathString& path, bool global_symbols) {
// Custom deleter to unload the shared library. Avoid throwing from it because it may run in dtor.
const auto unload_library = [](void* library_handle) {
if (library_handle == nullptr) {
return;
}
Status GetServiceBinaryDirectoryPath(const wchar_t* service_name,
std::filesystem::path& service_binary_directory_path_out) {
SC_HANDLE scm_handle_raw = ::OpenSCManagerW(nullptr, // local computer
nullptr, // SERVICES_ACTIVE_DATABASE
STANDARD_RIGHTS_READ);
ORT_RETURN_IF(scm_handle_raw == nullptr,
"Failed to open handle to service control manager. OpenSCManagerW error: ", ::GetLastError());

auto scm_handle = UniqueServiceHandle{scm_handle_raw};

SC_HANDLE service_handle_raw = ::OpenServiceW(scm_handle.get(),
service_name,
SERVICE_QUERY_CONFIG);
ORT_RETURN_IF(service_handle_raw == nullptr,
"Failed to open service handle. OpenServiceW error: ", ::GetLastError());

auto service_handle = UniqueServiceHandle{service_handle_raw};

// get service config required buffer size
DWORD service_config_buffer_size{};
ORT_RETURN_IF(!::QueryServiceConfigW(service_handle.get(), nullptr, 0, &service_config_buffer_size) &&
::GetLastError() != ERROR_INSUFFICIENT_BUFFER,
"Failed to query service configuration buffer size. QueryServiceConfigW error: ", ::GetLastError());

const auto& env = GetDefaultEnv();
const auto unload_status = env.UnloadDynamicLibrary(library_handle);
// get the service config
std::vector<std::byte> service_config_buffer(service_config_buffer_size);

Check warning on line 81 in onnxruntime/core/providers/qnn/rpcmem_library.cc

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Add #include <vector> for vector<> [build/include_what_you_use] [4] Raw Output: onnxruntime/core/providers/qnn/rpcmem_library.cc:81: Add #include <vector> for vector<> [build/include_what_you_use] [4]
QUERY_SERVICE_CONFIGW* service_config = reinterpret_cast<QUERY_SERVICE_CONFIGW*>(service_config_buffer.data());
ORT_RETURN_IF(!::QueryServiceConfigW(service_handle.get(), service_config, service_config_buffer_size,
&service_config_buffer_size),
"Failed to query service configuration. QueryServiceConfigW error: ", ::GetLastError());

if (!unload_status.IsOK()) {
LOGS_DEFAULT(WARNING) << "Failed to unload shared library. Error: " << unload_status.ErrorMessage();
}
};
std::wstring service_binary_path_name = service_config->lpBinaryPathName;

// replace system root placeholder with the value of the SYSTEMROOT environment variable
const std::wstring system_root_placeholder = L"\\SystemRoot";

ORT_RETURN_IF(service_binary_path_name.find(system_root_placeholder, 0) != 0,
"Service binary path '", ToUTF8String(service_binary_path_name),
"' does not start with expected system root placeholder value '",
ToUTF8String(system_root_placeholder), "'.");

std::wstring system_root{};
ORT_RETURN_IF_ERROR(ReadEnvironmentVariable(L"SYSTEMROOT", system_root));
service_binary_path_name.replace(0, system_root_placeholder.size(), system_root);

const auto service_binary_path = std::filesystem::path{service_binary_path_name};
auto service_binary_directory_path = service_binary_path.parent_path();

ORT_RETURN_IF(!std::filesystem::exists(service_binary_directory_path),
"Service binary directory path does not exist: ", service_binary_directory_path.string());

Check warning on line 105 in onnxruntime/core/providers/qnn/rpcmem_library.cc

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Add #include <string> for string [build/include_what_you_use] [4] Raw Output: onnxruntime/core/providers/qnn/rpcmem_library.cc:105: Add #include <string> for string [build/include_what_you_use] [4]

service_binary_directory_path_out = std::move(service_binary_directory_path);

Check warning on line 107 in onnxruntime/core/providers/qnn/rpcmem_library.cc

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Add #include <utility> for move [build/include_what_you_use] [4] Raw Output: onnxruntime/core/providers/qnn/rpcmem_library.cc:107: Add #include <utility> for move [build/include_what_you_use] [4]
return Status::OK();
}

#endif // defined(_WIN32)

Status GetRpcMemDynamicLibraryPath(PathString& path_out) {
#if defined(_WIN32)

std::filesystem::path qcnspmcdm_dir_path{};
ORT_RETURN_IF_ERROR(GetServiceBinaryDirectoryPath(L"qcnspmcdm", qcnspmcdm_dir_path));
const auto libcdsprpc_path = qcnspmcdm_dir_path / L"libcdsprpc.dll";
path_out = libcdsprpc_path.wstring();
return Status::OK();

#else // ^^^ defined(_WIN32) / vvv !defined(_WIN32)

path_out = ORT_TSTR("libcdsprpc.so");
return Status::OK();

#endif // !defined(_WIN32)
}

Status LoadDynamicLibrary(const PathString& path, bool global_symbols,
UniqueDynamicLibraryHandle& library_handle_out) {
const auto& env = GetDefaultEnv();
void* library_handle = nullptr;
void* library_handle_raw = nullptr;
ORT_RETURN_IF_ERROR(env.LoadDynamicLibrary(path, global_symbols, &library_handle_raw));

library_handle_out = UniqueDynamicLibraryHandle{library_handle_raw};
return Status::OK();
}

UniqueDynamicLibraryHandle GetRpcMemDynamicLibraryHandle() {
std::string_view error_message_prefix = "Failed to initialize RPCMEM dynamic library handle: ";

PathString rpcmem_library_path{};
auto status = GetRpcMemDynamicLibraryPath(rpcmem_library_path);
if (!status.IsOK()) {
ORT_THROW(error_message_prefix, status.ErrorMessage());
}

const auto load_status = env.LoadDynamicLibrary(path, global_symbols, &library_handle);
if (!load_status.IsOK()) {
ORT_THROW("Failed to load ", ToUTF8String(path), ": ", load_status.ErrorMessage());
UniqueDynamicLibraryHandle library_handle{};
status = LoadDynamicLibrary(rpcmem_library_path, /* global_symbols */ false, library_handle);
if (!status.IsOK()) {
ORT_THROW(error_message_prefix, status.ErrorMessage());
}

return DynamicLibraryHandle{library_handle, unload_library};
return library_handle;
}

RpcMemApi CreateApi(void* library_handle) {
Expand All @@ -58,7 +171,7 @@
} // namespace

RpcMemLibrary::RpcMemLibrary()
: library_handle_(LoadDynamicLibrary(GetRpcMemSharedLibraryPath(), /* global_symbols */ false)),
: library_handle_(GetRpcMemDynamicLibraryHandle()),
api_{CreateApi(library_handle_.get())} {
}

Expand Down
8 changes: 6 additions & 2 deletions onnxruntime/core/providers/qnn/rpcmem_library.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@

namespace onnxruntime::qnn {

using DynamicLibraryHandle = std::unique_ptr<void, void (*)(void*)>;
struct DynamicLibraryHandleDeleter {
void operator()(void* library_handle) noexcept;
};

using UniqueDynamicLibraryHandle = std::unique_ptr<void, DynamicLibraryHandleDeleter>;

// This namespace contains constants and typedefs corresponding to functions from rpcmem.h.
// https://github.com/quic/fastrpc/blob/v0.1.1/inc/rpcmem.h
Expand Down Expand Up @@ -61,7 +65,7 @@ class RpcMemLibrary {
const RpcMemApi& Api() const { return api_; }

private:
DynamicLibraryHandle library_handle_;
UniqueDynamicLibraryHandle library_handle_;
RpcMemApi api_;
};

Expand Down
14 changes: 2 additions & 12 deletions onnxruntime/test/providers/qnn/qnn_basic_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1166,18 +1166,8 @@ TEST_F(QnnHTPBackendTests, UseHtpSharedMemoryAllocatorForInputs) {
try {
qnn_ep = QnnExecutionProviderWithOptions(provider_options);
} catch (const OnnxRuntimeException& e) {
// handle particular exception that indicates that the libcdsprpc.so / dll can't be loaded
// NOTE: To run this on a local Windows ARM64 device, you need to copy libcdsprpc.dll to the build directory:
// - Open File Explorer
// - Go to C:/Windows/System32/DriverStore/FileRepository/
// - Search for a folder that begins with qcnspmcdm8380.inf_arm64_ and open it
// - Copy the libcdsprpc.dll into the build/[PATH CONTAINING onnxruntime.dll] directory of the application.
// TODO(adrianlizarraga): Update CMake build for unittests to automatically copy libcdsprpc.dll into build directory
#if defined(_WIN32)
constexpr const char* expected_error_message = "Failed to load libcdsprpc.dll";
#else
constexpr const char* expected_error_message = "Failed to load libcdsprpc.so";
#endif
// handle exception that indicates that the libcdsprpc.so / dll can't be loaded
constexpr const char* expected_error_message = "Failed to initialize RPCMEM dynamic library handle";
ASSERT_THAT(e.what(), testing::HasSubstr(expected_error_message));
GTEST_SKIP() << "HTP shared memory allocator is unavailable.";
}
Expand Down
15 changes: 2 additions & 13 deletions onnxruntime/test/shared_lib/test_inference.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1960,20 +1960,9 @@ static bool CreateSessionWithQnnEpAndQnnHtpSharedMemoryAllocator(PATH_TYPE model
session = Ort::Session{*ort_env, model_path, session_options};
return true;
} catch (const Ort::Exception& e) {
// handle particular exception that indicates that the libcdsprpc.so / dll can't be loaded
// NOTE: To run this on a local Windows ARM64 device, you need to copy libcdsprpc.dll to the build directory:
// - Open File Explorer
// - Go to C:/Windows/System32/DriverStore/FileRepository/
// - Search for a folder that begins with qcnspmcdm8380.inf_arm64_ and open it
// - Copy the libcdsprpc.dll into the build/[PATH CONTAINING onnxruntime.dll] directory of the application.
// TODO(adrianlizarraga): Update CMake build for unittests to automatically copy libcdsprpc.dll into build directory
// handle exception that indicates that the libcdsprpc.so / dll can't be loaded
std::string_view error_message = e.what();

#if defined(_WIN32)
std::string_view expected_error_message = "Failed to load libcdsprpc.dll";
#else
std::string_view expected_error_message = "Failed to load libcdsprpc.so";
#endif
std::string_view expected_error_message = "Failed to initialize RPCMEM dynamic library handle";

if (e.GetOrtErrorCode() == ORT_FAIL &&
error_message.find(expected_error_message) != std::string_view::npos) {
Expand Down
Loading