Freeze all statuses emitted by calls into dynamic modules (#16066)
When unloading a dynamic module we might inadvertently destroy any
status annotations it added, causing pitfalls/segfaults around status
usage. This changes all statuses emitted at the boundary of dynamic modules
to transfer ownership to the hosting context.
Fixes https://github.com/openxla/iree/issues/16046
diff --git a/runtime/src/iree/base/status.c b/runtime/src/iree/base/status.c
index 3c27d4c..937f058 100644
--- a/runtime/src/iree/base/status.c
+++ b/runtime/src/iree/base/status.c
@@ -6,6 +6,8 @@
#include "iree/base/status.h"
+#include "iree/base/attributes.h"
+
#if defined(IREE_PLATFORM_APPLE)
#include <dlfcn.h>
#include <execinfo.h>
@@ -1058,49 +1060,31 @@
#endif // has IREE_STATUS_FEATURE_ANNOTATIONS
-IREE_API_EXPORT bool iree_status_format(iree_status_t status,
- iree_host_size_t buffer_capacity,
- char* buffer,
- iree_host_size_t* out_buffer_length) {
+static bool iree_status_format_message(iree_status_t status,
+ iree_host_size_t buffer_capacity,
+ char* buffer,
+ iree_host_size_t* out_buffer_length,
+ bool has_prefix) {
*out_buffer_length = 0;
// Grab storage which may have a message and zero or more payloads.
- iree_status_storage_t* storage IREE_ATTRIBUTE_UNUSED =
- iree_status_storage(status);
+ iree_status_storage_t* storage = iree_status_storage(status);
+ // If no storage, nothing to do.
+ if (!storage) {
+ return true;
+ }
// Prefix with source location and status code string (may be 'OK').
iree_host_size_t buffer_length = 0;
- iree_status_code_t status_code = iree_status_code(status);
int n = 0;
-#if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_SOURCE_LOCATION) != 0
- if (storage && storage->file) {
- n = snprintf(buffer ? buffer + buffer_length : NULL,
- buffer ? buffer_capacity - buffer_length : 0, "%s:%d: %s",
- storage->file, storage->line,
- iree_status_code_string(status_code));
- } else {
- n = snprintf(buffer ? buffer + buffer_length : NULL,
- buffer ? buffer_capacity - buffer_length : 0, "%s",
- iree_status_code_string(status_code));
- }
-#else
- n = snprintf(buffer ? buffer + buffer_length : NULL,
- buffer ? buffer_capacity - buffer_length : 0, "%s",
- iree_status_code_string(status_code));
-#endif // has IREE_STATUS_FEATURE_SOURCE_LOCATION
- if (IREE_UNLIKELY(n < 0)) {
- return false;
- } else if (buffer && n >= buffer_capacity - buffer_length) {
- buffer = NULL;
- }
- buffer_length += n;
#if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_ANNOTATIONS) != 0
// Append base storage message.
if (storage && !iree_string_view_is_empty(storage->message)) {
n = snprintf(buffer ? buffer + buffer_length : NULL,
- buffer ? buffer_capacity - buffer_length : 0, "; %.*s",
- (int)storage->message.size, storage->message.data);
+ buffer ? buffer_capacity - buffer_length : 0,
+ has_prefix ? "; %.*s" : "%.*s", (int)storage->message.size,
+ storage->message.data);
if (IREE_UNLIKELY(n < 0)) {
return false;
} else if (buffer && n >= buffer_capacity - buffer_length) {
@@ -1109,6 +1093,11 @@
buffer_length += n;
}
#endif // has IREE_STATUS_FEATURE_ANNOTATIONS
+ if (IREE_UNLIKELY(n < 0)) {
+ return false;
+ } else if (buffer && n >= buffer_capacity) {
+ buffer = NULL;
+ }
#if IREE_STATUS_FEATURES != 0
// Append each payload separated by a newline.
@@ -1150,6 +1139,142 @@
return true;
}
+IREE_API_EXPORT bool iree_status_format(iree_status_t status,
+ iree_host_size_t buffer_capacity,
+ char* buffer,
+ iree_host_size_t* out_buffer_length) {
+ *out_buffer_length = 0;
+
+ // Grab storage which may have a message and zero or more payloads.
+ iree_status_storage_t* storage IREE_ATTRIBUTE_UNUSED =
+ iree_status_storage(status);
+
+ // Prefix with source location and status code string (may be 'OK').
+ iree_host_size_t prefix_buffer_length = 0;
+ iree_status_code_t status_code = iree_status_code(status);
+ int n = 0;
+#if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_SOURCE_LOCATION) != 0
+ if (storage && storage->file) {
+ n = snprintf(buffer ? buffer : NULL, buffer ? buffer_capacity : 0,
+ "%s:%d: %s", storage->file, storage->line,
+ iree_status_code_string(status_code));
+ } else {
+ n = snprintf(buffer ? buffer : NULL, buffer ? buffer_capacity : 0, "%s",
+ iree_status_code_string(status_code));
+ }
+#else
+ n = snprintf(buffer ? buffer + prefix_buffer_length : NULL,
+ buffer ? buffer_capacity - prefix_buffer_length : 0, "%s",
+ iree_status_code_string(status_code));
+#endif // has IREE_STATUS_FEATURE_SOURCE_LOCATION
+ if (IREE_UNLIKELY(n < 0)) {
+ return false;
+ } else if (buffer && n >= buffer_capacity) {
+ buffer = NULL;
+ }
+ prefix_buffer_length += n;
+
+ iree_host_size_t message_buffer_length = 0;
+ bool ret = iree_status_format_message(
+ status, buffer_capacity, buffer ? buffer + prefix_buffer_length : NULL,
+ &message_buffer_length, /*has_prefix=*/true);
+ if (!ret) {
+ return false;
+ }
+ *out_buffer_length = message_buffer_length + prefix_buffer_length;
+ return true;
+}
+
+#if IREE_STATUS_FEATURES == 0
+IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t
+iree_status_freeze(iree_status_t status) {
+ // Statuses are just codes; nothing to do.
+ return status;
+}
+#else
+IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t
+iree_status_freeze(iree_status_t status) {
+ iree_status_code_t code = iree_status_code(status);
+ if (code == IREE_STATUS_OK) {
+ return iree_ok_status();
+ }
+
+ // Get the size of the formatted message alone. Source file annotations are
+ // handled separately.
+ iree_host_size_t message_buffer_size = 0;
+ if (IREE_UNLIKELY(!iree_status_format_message(
+ status, /*buffer_capacity=*/0,
+ /*buffer=*/NULL, &message_buffer_size, /*has_prefix=*/false))) {
+ iree_status_free(status);
+ return iree_status_from_code(code);
+ }
+ message_buffer_size++; // NUL
+
+ // Compute the storage size for the status with additional room to store the
+ // formatted message and source file location if present.
+ size_t unaligned_storage_size =
+ sizeof(iree_status_storage_t) + message_buffer_size;
+
+#if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_SOURCE_LOCATION) != 0
+ // Grab storage for the source file location.
+ iree_status_storage_t* storage = iree_status_storage(status);
+ const char* file = NULL;
+ uint32_t line = 0;
+ if (storage) {
+ file = storage->file;
+ line = storage->line;
+ }
+ size_t file_storage_size = file ? strlen(file) + 1 : 0;
+ unaligned_storage_size += file_storage_size;
+#endif // has IREE_STATUS_FEATURE_SOURCE_LOCATION
+
+ size_t storage_alignment = (IREE_STATUS_CODE_MASK + 1);
+ size_t storage_size =
+ iree_host_align(unaligned_storage_size, storage_alignment);
+ iree_status_storage_t* new_storage =
+ (iree_status_storage_t*)iree_aligned_alloc(storage_alignment,
+ storage_size);
+ if (IREE_UNLIKELY(!new_storage)) {
+ iree_status_free(status);
+ return iree_status_from_code(code);
+ }
+ memset(new_storage, 0, sizeof(*new_storage));
+
+ char* message_data = (char*)new_storage + sizeof(iree_status_storage_t);
+ size_t res_length;
+ // Format the status message directly into the region allocated for it.
+ bool ret =
+ iree_status_format_message(status, message_buffer_size, message_data,
+ &res_length, /*has_prefix=*/false);
+ new_storage->message.size = message_buffer_size - 1;
+ new_storage->message.data =
+ (const char*)new_storage + sizeof(iree_status_storage_t);
+ iree_status_t new_status =
+ (iree_status_t)((uintptr_t)new_storage | (code & IREE_STATUS_CODE_MASK));
+
+ if (IREE_UNLIKELY(!ret)) {
+ iree_status_free(new_status);
+ iree_status_free(status);
+ return iree_status_from_code(code);
+ }
+
+#if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_SOURCE_LOCATION) != 0
+ if (file) {
+ new_storage->file = storage->file;
+ char* storage_file = (char*)new_storage + sizeof(iree_status_storage_t) +
+ message_buffer_size;
+ // Copy the file into the storage allocated for it.
+ memcpy(storage_file, file, file_storage_size);
+ new_storage->file = (const char*)storage_file;
+ }
+ new_storage->line = line;
+#endif // has IREE_STATUS_FEATURE_SOURCE_LOCATION
+
+ iree_status_free(status);
+ return new_status;
+}
+#endif // has any IREE_STATUS_FEATURES
+
IREE_API_EXPORT bool iree_status_to_string(
iree_status_t status, const iree_allocator_t* allocator, char** out_buffer,
iree_host_size_t* out_buffer_length) {
diff --git a/runtime/src/iree/base/status.h b/runtime/src/iree/base/status.h
index f00635c..5cfe2eb 100644
--- a/runtime/src/iree/base/status.h
+++ b/runtime/src/iree/base/status.h
@@ -442,6 +442,12 @@
IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t
iree_status_clone(iree_status_t status);
+// Freezes |status| into a new status instance. Formats all attached
+// annotations, payloads included, into a single message string allocated to
+// the new status. This always frees the original status.
+IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t
+iree_status_freeze(iree_status_t status);
+
// Frees |status| if it has any associated storage.
IREE_API_EXPORT void iree_status_free(iree_status_t status);
diff --git a/runtime/src/iree/vm/dynamic/module.c b/runtime/src/iree/vm/dynamic/module.c
index 621b11f..a10677c 100644
--- a/runtime/src/iree/vm/dynamic/module.c
+++ b/runtime/src/iree/vm/dynamic/module.c
@@ -49,9 +49,9 @@
// Try to create the module, which may fail if the version is incompatible or
// the parameters are invalid.
IREE_RETURN_AND_END_ZONE_IF_ERROR(
- z0,
- create_fn(IREE_VM_DYNAMIC_MODULE_VERSION_LATEST, instance, param_count,
- params, module->allocator, &module->user_module));
+ z0, iree_status_freeze(create_fn(
+ IREE_VM_DYNAMIC_MODULE_VERSION_LATEST, instance, param_count,
+ params, module->allocator, &module->user_module)));
IREE_TRACE_ZONE_END(z0);
return iree_ok_status();
@@ -91,16 +91,16 @@
static iree_status_t IREE_API_PTR iree_vm_dynamic_module_get_module_attr(
void* self, iree_host_size_t index, iree_string_pair_t* out_attr) {
iree_vm_dynamic_module_t* module = (iree_vm_dynamic_module_t*)self;
- return module->user_module->get_module_attr(module->user_module->self, index,
- out_attr);
+ return iree_status_freeze(module->user_module->get_module_attr(
+ module->user_module->self, index, out_attr));
}
static iree_status_t iree_vm_dynamic_module_enumerate_dependencies(
void* self, iree_vm_module_dependency_callback_t callback,
void* user_data) {
iree_vm_dynamic_module_t* module = (iree_vm_dynamic_module_t*)self;
- return module->user_module->enumerate_dependencies(module->user_module->self,
- callback, user_data);
+ return iree_status_freeze(module->user_module->enumerate_dependencies(
+ module->user_module->self, callback, user_data));
}
static iree_status_t IREE_API_PTR iree_vm_dynamic_module_get_function(
@@ -108,9 +108,9 @@
iree_vm_function_t* out_function, iree_string_view_t* out_name,
iree_vm_function_signature_t* out_signature) {
iree_vm_dynamic_module_t* module = (iree_vm_dynamic_module_t*)self;
- IREE_RETURN_IF_ERROR(module->user_module->get_function(
+ IREE_RETURN_IF_ERROR(iree_status_freeze(module->user_module->get_function(
module->user_module->self, linkage, ordinal, out_function, out_name,
- out_signature));
+ out_signature)));
if (out_function) out_function->module = (iree_vm_module_t*)self;
return iree_ok_status();
}
@@ -119,8 +119,8 @@
void* self, iree_vm_function_linkage_t linkage, iree_host_size_t ordinal,
iree_host_size_t index, iree_string_pair_t* out_attr) {
iree_vm_dynamic_module_t* module = (iree_vm_dynamic_module_t*)self;
- return module->user_module->get_function_attr(
- module->user_module->self, linkage, ordinal, index, out_attr);
+ return iree_status_freeze(module->user_module->get_function_attr(
+ module->user_module->self, linkage, ordinal, index, out_attr));
}
static iree_status_t IREE_API_PTR iree_vm_dynamic_module_lookup_function(
@@ -128,9 +128,9 @@
const iree_vm_function_signature_t* expected_signature,
iree_vm_function_t* out_function) {
iree_vm_dynamic_module_t* module = (iree_vm_dynamic_module_t*)self;
- IREE_RETURN_IF_ERROR(module->user_module->lookup_function(
+ IREE_RETURN_IF_ERROR(iree_status_freeze(module->user_module->lookup_function(
module->user_module->self, linkage, name, expected_signature,
- out_function));
+ out_function)));
out_function->module = (iree_vm_module_t*)self;
return iree_ok_status();
}
@@ -141,8 +141,8 @@
iree_vm_source_location_t* out_source_location) {
iree_vm_dynamic_module_t* module = (iree_vm_dynamic_module_t*)self;
if (module->user_module->resolve_source_location) {
- return module->user_module->resolve_source_location(
- module->user_module->self, function, pc, out_source_location);
+ return iree_status_freeze(module->user_module->resolve_source_location(
+ module->user_module->self, function, pc, out_source_location));
}
return iree_status_from_code(IREE_STATUS_UNAVAILABLE);
}
@@ -151,8 +151,8 @@
iree_vm_dynamic_module_alloc_state(void* self, iree_allocator_t allocator,
iree_vm_module_state_t** out_module_state) {
iree_vm_dynamic_module_t* module = (iree_vm_dynamic_module_t*)self;
- return module->user_module->alloc_state(module->user_module->self, allocator,
- out_module_state);
+ return iree_status_freeze(module->user_module->alloc_state(
+ module->user_module->self, allocator, out_module_state));
}
static void IREE_API_PTR iree_vm_dynamic_module_free_state(
@@ -166,29 +166,29 @@
const iree_vm_function_t* function,
const iree_vm_function_signature_t* signature) {
iree_vm_dynamic_module_t* module = (iree_vm_dynamic_module_t*)self;
- return module->user_module->resolve_import(
- module->user_module->self, module_state, ordinal, function, signature);
+ return iree_status_freeze(module->user_module->resolve_import(
+ module->user_module->self, module_state, ordinal, function, signature));
}
static iree_status_t IREE_API_PTR iree_vm_dynamic_module_notify(
void* self, iree_vm_module_state_t* module_state, iree_vm_signal_t signal) {
iree_vm_dynamic_module_t* module = (iree_vm_dynamic_module_t*)self;
- return module->user_module->notify(module->user_module->self, module_state,
- signal);
+ return iree_status_freeze(module->user_module->notify(
+ module->user_module->self, module_state, signal));
}
static iree_status_t IREE_API_PTR iree_vm_dynamic_module_begin_call(
void* self, iree_vm_stack_t* stack, iree_vm_function_call_t call) {
iree_vm_dynamic_module_t* module = (iree_vm_dynamic_module_t*)self;
- return module->user_module->begin_call(module->user_module->self, stack,
- call);
+ return iree_status_freeze(
+ module->user_module->begin_call(module->user_module->self, stack, call));
}
static iree_status_t IREE_API_PTR iree_vm_dynamic_module_resume_call(
void* self, iree_vm_stack_t* stack, iree_byte_span_t call_results) {
iree_vm_dynamic_module_t* module = (iree_vm_dynamic_module_t*)self;
- return module->user_module->resume_call(module->user_module->self, stack,
- call_results);
+ return iree_status_freeze(module->user_module->resume_call(
+ module->user_module->self, stack, call_results));
}
static iree_status_t iree_vm_dynamic_module_create(
diff --git a/samples/custom_module/dynamic/module.cc b/samples/custom_module/dynamic/module.cc
index ac7687e..ad5ffff 100644
--- a/samples/custom_module/dynamic/module.cc
+++ b/samples/custom_module/dynamic/module.cc
@@ -148,6 +148,11 @@
return OkStatus();
}
+ // Prints the contents of the string to stdout.
+ Status ThrowError() {
+ return iree_make_status(IREE_STATUS_UNKNOWN, "Forced failure");
+ }
+
private:
// Allocator that the caller requested we use for any allocations we need to
// perform during operation.
@@ -159,6 +164,7 @@
vm::MakeNativeFunction("string.from_tensor",
&CustomModuleState::StringFromTensor),
vm::MakeNativeFunction("string.print", &CustomModuleState::StringPrint),
+ vm::MakeNativeFunction("error", &CustomModuleState::ThrowError),
};
// The module instance that will be allocated and reused across contexts.
diff --git a/samples/custom_module/dynamic/test/example.mlir b/samples/custom_module/dynamic/test/example.mlir
index 5b9db14..a0437d6 100644
--- a/samples/custom_module/dynamic/test/example.mlir
+++ b/samples/custom_module/dynamic/test/example.mlir
@@ -6,6 +6,14 @@
// RUN: --function=main | \
// RUN: FileCheck %s
+// RUN: ( iree-compile %s --iree-hal-target-backends=vmvx | \
+// RUN: iree-run-module \
+// RUN: --device=local-sync \
+// RUN: --module=$IREE_BINARY_DIR/samples/custom_module/dynamic/module$IREE_DYLIB_EXT@create_custom_module \
+// RUN: --module=- \
+// RUN: --function=error 2>&1 || [[ $? == 1 ]] ) | \
+// RUN: FileCheck %s --check-prefix=CERROR
+
module @example {
//===--------------------------------------------------------------------===//
// Imports
@@ -21,6 +29,9 @@
// Prints the contents of the string to stdout.
func.func private @custom.string.print(!custom.string)
+ // Always returns unknown status with a custom annotation.
+ func.func private @custom.error()
+
//===--------------------------------------------------------------------===//
// Sample methods
//===--------------------------------------------------------------------===//
@@ -41,4 +52,13 @@
return
}
+
+ // CERROR-LABEL: EXEC @error
+ func.func @error() {
+ // Show an example of dumping a custom error message.
+ // CERROR-NEXT: samples/custom_module/dynamic/module.cc:153: UNKNOWN; Forced failure; while invoking C++ function custom.error;
+ call @custom.error() : () -> ()
+ return
+ }
+
}