Skip to content

Commit

Permalink
[QNN EP]: Clean up QNN logging resources if an error occurs during in…
Browse files Browse the repository at this point in the history
…itialization (#23435)

### Description
Re-implementation of #23320
(which was reverted).

- Cleans up QNN logging resources if an error occurs during
initialization.
- Updates `QnnLogging()`, which is a logging callback called by QNN
libs, to handle situations in which ORT logging is unavailable, thus
avoiding a segmentation fault.
- Updates `QnnBackendManager::CreateHtpPowerCfgId()` and
`QnnBackendManager::SetHtpPowerConfig()` to check that backend setup is
complete. These functions get called in QNN EP's `OnRunStart()` even if
QNN backend setup failed and the model is assigned to a different EP.
This prevents a segmentation fault. Our Android tests ran into this
issue because the QNN backend setup failed, the model was then assigned
to CPU EP, and the QNN EP's `OnRunStart()` was still called with an
invalid backend.


### Motivation and Context
If QNN initialization fails at any point, we have to properly clean up
the logging resources so that QNN does not call our `QnnLogging()`
callback after the EP has been destroyed.
  • Loading branch information
adrianlizarraga authored Jan 21, 2025
1 parent 997ab24 commit c7f764c
Showing 1 changed file with 27 additions and 10 deletions.
37 changes: 27 additions & 10 deletions onnxruntime/core/providers/qnn/builder/qnn_backend_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,12 @@ void QnnLogging(const char* format,
ORT_UNUSED_PARAMETER(level);
ORT_UNUSED_PARAMETER(timestamp);

if (!::onnxruntime::logging::LoggingManager::HasDefaultLogger()) {
// QNN may call this logging callback at any point, which means that we need to explicitly check
// that the default logger has been initialized before trying to use it (otherwise get segfault).
return;
}

const auto& logger = ::onnxruntime::logging::LoggingManager::DefaultLogger();
const auto severity = ::onnxruntime::logging::Severity::kVERBOSE;
const auto data_type = ::onnxruntime::logging::DataType::SYSTEM;
Expand All @@ -273,6 +279,9 @@ Status QnnBackendManager::InitializeQnnLog(const logging::Logger& logger) {
QnnLog_Level_t qnn_log_level = MapOrtSeverityToQNNLogLevel(ort_log_level);
LOGS(*logger_, VERBOSE) << "Set Qnn log level: " << qnn_log_level;

// NOTE: Even if logCreate() fails and QNN does not return a valid log_handle_, QNN may still
// call the QnnLogging() callback. So, we have to make sure that QnnLogging() can handle calls
// in which ORT logging is not available.
Qnn_ErrorHandle_t result = qnn_interface_.logCreate(QnnLogging, qnn_log_level, &log_handle_);

if (result != QNN_SUCCESS) {
Expand Down Expand Up @@ -879,6 +888,10 @@ Status QnnBackendManager::SetupBackend(const logging::Logger& logger,
}

Status QnnBackendManager::CreateHtpPowerCfgId(uint32_t device_id, uint32_t core_id, uint32_t& htp_power_config_id) {
// This function is called in QNN EP's OnRunStart() even if QNN backend setup failed and the model is assigned
// to a different EP. Therefore, we have to check that backend setup actually completed before trying to
// create an HTP power config ID. Otherwise, this causes a segfault because the QNN backend lib is unloaded.
ORT_RETURN_IF_NOT(backend_setup_completed_, "Cannot create HTP power config ID if backend setup is not complete.");
QnnDevice_Infrastructure_t qnn_device_infra = nullptr;
auto status = qnn_interface_.deviceGetInfrastructure(&qnn_device_infra);
ORT_RETURN_IF(QNN_SUCCESS != status, "backendGetPerfInfrastructure failed.");
Expand All @@ -896,6 +909,10 @@ Status QnnBackendManager::CreateHtpPowerCfgId(uint32_t device_id, uint32_t core_

Status QnnBackendManager::SetHtpPowerConfig(uint32_t htp_power_config_client_id,
HtpPerformanceMode htp_performance_mode) {
// This function is called in QNN EP's OnRunStart() even if QNN backend setup failed and the model is assigned
// to a different EP. Therefore, we have to check that backend setup actually completed before trying to
// set an HTP power config ID. Otherwise, this causes a segfault because the QNN backend lib is unloaded.
ORT_RETURN_IF_NOT(backend_setup_completed_, "Cannot set HTP power config ID if backend setup is not complete.");
QnnDevice_Infrastructure_t qnn_device_infra = nullptr;
auto status = qnn_interface_.deviceGetInfrastructure(&qnn_device_infra);
ORT_RETURN_IF(QNN_SUCCESS != status, "backendGetPerfInfrastructure failed.");
Expand Down Expand Up @@ -1037,6 +1054,10 @@ Status QnnBackendManager::SetHtpPowerConfig(uint32_t htp_power_config_client_id,

Status QnnBackendManager::SetRpcControlLatency(uint32_t htp_power_config_client_id,
uint32_t rpc_control_latency) {
// This function is called in QNN EP's OnRunStart() even if QNN backend setup failed and the model is assigned
// to a different EP. Therefore, we have to check that backend setup actually completed before trying to
// set RPC control latency. Otherwise, this causes a segfault because the QNN backend library is unloaded.
ORT_RETURN_IF_NOT(backend_setup_completed_, "Cannot set HTP RPC control latency if backend setup is not complete.");
if (rpc_control_latency != 0) {
QnnDevice_Infrastructure_t qnn_device_infra = nullptr;
auto status = qnn_interface_.deviceGetInfrastructure(&qnn_device_infra);
Expand Down Expand Up @@ -1099,39 +1120,35 @@ Status QnnBackendManager::TerminateQnnLog() {
}

void QnnBackendManager::ReleaseResources() {
if (!backend_setup_completed_) {
return;
}

auto result = ReleaseContext();
if (Status::OK() != result) {
LOGS_DEFAULT(ERROR) << "Failed to ReleaseContext.";
LOGS_DEFAULT(ERROR) << "Failed to ReleaseContext: " << result.ErrorMessage();
}

result = ReleaseProfilehandle();
if (Status::OK() != result) {
LOGS_DEFAULT(ERROR) << "Failed to ReleaseProfilehandle.";
LOGS_DEFAULT(ERROR) << "Failed to ReleaseProfilehandle: " << result.ErrorMessage();
}

result = ReleaseDevice();
if (Status::OK() != result) {
LOGS_DEFAULT(ERROR) << "Failed to ReleaseDevice.";
LOGS_DEFAULT(ERROR) << "Failed to ReleaseDevice: " << result.ErrorMessage();
}

result = ShutdownBackend();
if (Status::OK() != result) {
LOGS_DEFAULT(ERROR) << "Failed to ShutdownBackend.";
LOGS_DEFAULT(ERROR) << "Failed to ShutdownBackend: " << result.ErrorMessage();
}

result = TerminateQnnLog();
if (Status::OK() != result) {
LOGS_DEFAULT(ERROR) << "Failed to TerminateQnnLog.";
LOGS_DEFAULT(ERROR) << "Failed to TerminateQnnLog: " << result.ErrorMessage();
}

if (backend_lib_handle_) {
result = UnloadLib(backend_lib_handle_);
if (Status::OK() != result) {
LOGS_DEFAULT(ERROR) << "Failed to unload backend library.";
LOGS_DEFAULT(ERROR) << "Failed to unload backend library: " << result.ErrorMessage();
}
}

Expand Down

0 comments on commit c7f764c

Please sign in to comment.