| // Copyright 2019 The IREE Authors |
| // |
| // Licensed under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| #include "iree/base/status.h" |
| |
| #include <assert.h> |
| #include <errno.h> |
| #include <limits.h> |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "iree/base/alignment.h" |
| #include "iree/base/allocator.h" |
| #include "iree/base/assert.h" |
| #include "iree/base/attributes.h" |
| #include "iree/base/printf.h" |
| #include "iree/base/status_payload.h" |
| #include "iree/base/string_builder.h" |
| |
| //===----------------------------------------------------------------------===// |
| // C11 aligned_alloc compatibility shim |
| //===----------------------------------------------------------------------===// |
| |
| // NOTE: these directly malloc/free and do not include tracing: these are only |
| // used for allocating status messages and need a path that is least likely to |
| // fail or introduce additional logic. |
| |
| #if defined(IREE_PLATFORM_WINDOWS) |
| // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-malloc |
| #define iree_aligned_alloc_raw(alignment, size) _aligned_malloc(size, alignment) |
| #define iree_aligned_free_raw(p) _aligned_free(p) |
| #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L |
| // https://en.cppreference.com/w/c/memory/aligned_alloc |
| #define iree_aligned_alloc_raw(alignment, size) aligned_alloc(alignment, size) |
| #define iree_aligned_free_raw(p) free(p) |
| #elif _POSIX_C_SOURCE >= 200112L |
| // https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_memalign.html |
| static inline void* iree_aligned_alloc_raw(size_t alignment, size_t size) { |
| void* ptr = NULL; |
| return posix_memalign(&ptr, alignment, size) == 0 ? ptr : NULL; |
| } |
| #define iree_aligned_free_raw(p) free(p) |
| #else |
| // Emulates alignment with normal malloc. We overallocate by at least the |
| // alignment + the size of a pointer, store the base pointer at p[-1], and |
| // return the aligned pointer. This lets us easily get the base pointer in free |
| // to pass back to the system. |
| static inline void* iree_aligned_alloc_raw(size_t alignment, size_t size) { |
| iree_host_size_t alloc_size = 0; |
| if (!iree_host_size_checked_add(size, alignment, &alloc_size) || |
| !iree_host_size_checked_add(alloc_size, sizeof(uintptr_t), &alloc_size)) { |
| return NULL; |
| } |
| void* base_ptr = malloc(alloc_size); |
| if (!base_ptr) return NULL; |
| uintptr_t* aligned_ptr = (uintptr_t*)iree_host_align( |
| (uintptr_t)base_ptr + sizeof(uintptr_t), alignment); |
| aligned_ptr[-1] = (uintptr_t)base_ptr; |
| return aligned_ptr; |
| } |
| static inline void iree_aligned_free_raw(void* p) { |
| if (IREE_UNLIKELY(!p)) return; |
| uintptr_t* aligned_ptr = (uintptr_t*)p; |
| void* base_ptr = (void*)aligned_ptr[-1]; |
| free(base_ptr); |
| } |
| #endif // IREE_PLATFORM_WINDOWS |
| |
| //===----------------------------------------------------------------------===// |
| // iree_status_t canonical errors |
| //===----------------------------------------------------------------------===// |
| |
| IREE_API_EXPORT iree_status_code_t |
| iree_status_code_from_errno(int error_number) { |
| switch (error_number) { |
| case 0: |
| return IREE_STATUS_OK; |
| case EINVAL: // Invalid argument |
| case ENAMETOOLONG: // Filename too long |
| case E2BIG: // Argument list too long |
| case EDESTADDRREQ: // Destination address required |
| case EDOM: // Mathematics argument out of domain of function |
| case EFAULT: // Bad address |
| case EILSEQ: // Illegal byte sequence |
| case ENOPROTOOPT: // Protocol not available |
| #ifdef ENOSTR |
| case ENOSTR: // Not a STREAM |
| #endif // ENOSTR |
| case ENOTSOCK: // Not a socket |
| case ENOTTY: // Inappropriate I/O control operation |
| case EPROTOTYPE: // Protocol wrong type for socket |
| case ESPIPE: // Invalid seek |
| return IREE_STATUS_INVALID_ARGUMENT; |
| case ETIMEDOUT: // Connection timed out |
| #ifdef ETIME |
| case ETIME: // Timer expired |
| #endif // ETIME |
| return IREE_STATUS_DEADLINE_EXCEEDED; |
| case ENODEV: // No such device |
| case ENOENT: // No such file or directory |
| #ifdef ENOMEDIUM |
| case ENOMEDIUM: // No medium found |
| #endif |
| case ENXIO: // No such device or address |
| case ESRCH: // No such process |
| return IREE_STATUS_NOT_FOUND; |
| case EEXIST: // File exists |
| case EADDRNOTAVAIL: // Address not available |
| case EALREADY: // Connection already in progress |
| #ifdef ENOTUNIQ |
| case ENOTUNIQ: // Name not unique on network |
| #endif |
| return IREE_STATUS_ALREADY_EXISTS; |
| case EPERM: // Operation not permitted |
| case EACCES: // Permission denied |
| #ifdef ENOKEY |
| case ENOKEY: // Required key not available |
| #endif |
| case EROFS: // Read only file system |
| return IREE_STATUS_PERMISSION_DENIED; |
| case ENOTEMPTY: // Directory not empty |
| case EISDIR: // Is a directory |
| case ENOTDIR: // Not a directory |
| case EADDRINUSE: // Address already in use |
| case EBADF: // Invalid file descriptor |
| #ifdef EBADFD |
| case EBADFD: // File descriptor in bad state |
| #endif |
| case EBUSY: // Device or resource busy |
| case ECHILD: // No child processes |
| case EISCONN: // Socket is connected |
| #ifdef EISNAM |
| case EISNAM: // Is a named type file |
| #endif |
| #ifdef ENOTBLK |
| case ENOTBLK: // Block device required |
| #endif |
| case ENOTCONN: // The socket is not connected |
| case EPIPE: // Broken pipe |
| #ifdef ESHUTDOWN |
| case ESHUTDOWN: // Cannot send after transport endpoint shutdown |
| #endif |
| case ETXTBSY: // Text file busy |
| #ifdef EUNATCH |
| case EUNATCH: // Protocol driver not attached |
| #endif |
| return IREE_STATUS_FAILED_PRECONDITION; |
| case ENOSPC: // No space left on device |
| #ifdef EDQUOT |
| case EDQUOT: // Disk quota exceeded |
| #endif |
| case EMFILE: // Too many open files |
| case EMLINK: // Too many links |
| case ENFILE: // Too many open files in system |
| case ENOBUFS: // No buffer space available |
| #ifdef ENODATA |
| case ENODATA: // No message is available on the STREAM read queue |
| #endif // ENODATA |
| case ENOMEM: // Not enough space |
| #ifdef ENOSR |
| case ENOSR: // No STREAM resources |
| #endif // ENOSR |
| #ifdef EUSERS |
| case EUSERS: // Too many users |
| #endif |
| return IREE_STATUS_RESOURCE_EXHAUSTED; |
| #ifdef ECHRNG |
| case ECHRNG: // Channel number out of range |
| #endif |
| case EFBIG: // File too large |
| case EOVERFLOW: // Value too large to be stored in data type |
| case ERANGE: // Result too large |
| return IREE_STATUS_OUT_OF_RANGE; |
| #ifdef ENOPKG |
| case ENOPKG: // Package not installed |
| #endif |
| case ENOSYS: // Function not implemented |
| case ENOTSUP: // Operation not supported |
| case EAFNOSUPPORT: // Address family not supported |
| #ifdef EPFNOSUPPORT |
| case EPFNOSUPPORT: // Protocol family not supported |
| #endif |
| case EPROTONOSUPPORT: // Protocol not supported |
| #ifdef ESOCKTNOSUPPORT |
| case ESOCKTNOSUPPORT: // Socket type not supported |
| #endif |
| case EXDEV: // Improper link |
| return IREE_STATUS_UNIMPLEMENTED; |
| case EAGAIN: // Resource temporarily unavailable |
| #ifdef ECOMM |
| case ECOMM: // Communication error on send |
| #endif |
| case ECONNREFUSED: // Connection refused |
| case ECONNABORTED: // Connection aborted |
| case ECONNRESET: // Connection reset |
| case EINTR: // Interrupted function call |
| #ifdef EHOSTDOWN |
| case EHOSTDOWN: // Host is down |
| #endif |
| case EHOSTUNREACH: // Host is unreachable |
| case ENETDOWN: // Network is down |
| case ENETRESET: // Connection aborted by network |
| case ENETUNREACH: // Network unreachable |
| case ENOLCK: // No locks available |
| case ENOLINK: // Link has been severed |
| #ifdef ENONET |
| case ENONET: // Machine is not on the network |
| #endif |
| return IREE_STATUS_UNAVAILABLE; |
| case EDEADLK: // Resource deadlock avoided |
| #ifdef ESTALE |
| case ESTALE: // Stale file handle |
| #endif |
| return IREE_STATUS_ABORTED; |
| case ECANCELED: // Operation cancelled |
| return IREE_STATUS_CANCELLED; |
| default: |
| return IREE_STATUS_UNKNOWN; |
| } |
| } |
| |
| #if defined(IREE_PLATFORM_WINDOWS) |
| IREE_API_EXPORT iree_status_code_t |
| iree_status_code_from_win32_error(uint32_t error) { |
| switch (error) { |
| case ERROR_SUCCESS: |
| return IREE_STATUS_OK; |
| case ERROR_FILE_NOT_FOUND: |
| case ERROR_PATH_NOT_FOUND: |
| return IREE_STATUS_NOT_FOUND; |
| case ERROR_TOO_MANY_OPEN_FILES: |
| case ERROR_OUTOFMEMORY: |
| case ERROR_HANDLE_DISK_FULL: |
| case ERROR_HANDLE_EOF: |
| return IREE_STATUS_RESOURCE_EXHAUSTED; |
| case ERROR_ACCESS_DENIED: |
| return IREE_STATUS_PERMISSION_DENIED; |
| case ERROR_INVALID_HANDLE: |
| case ERROR_INVALID_PARAMETER: |
| return IREE_STATUS_INVALID_ARGUMENT; |
| case ERROR_NOT_READY: |
| case ERROR_READ_FAULT: |
| return IREE_STATUS_UNAVAILABLE; |
| case ERROR_WRITE_FAULT: |
| return IREE_STATUS_DATA_LOSS; |
| case ERROR_NOT_SUPPORTED: |
| return IREE_STATUS_UNIMPLEMENTED; |
| |
| // ERROR_OPERATION_ABORTED (995): overlapped I/O cancelled via CancelIoEx. |
| case 995: |
| return IREE_STATUS_CANCELLED; |
| |
| // ERROR_NETNAME_DELETED (64): network connection closed by remote host |
| // during an overlapped I/O operation. |
| case 64: |
| return IREE_STATUS_UNAVAILABLE; |
| |
| // WinSock error codes (from winsock2.h, stable ABI since WinSock 1.0). |
| // Raw values used here to avoid pulling in winsock2.h. |
| case 10013: // WSAEACCES |
| return IREE_STATUS_PERMISSION_DENIED; |
| case 10022: // WSAEINVAL |
| return IREE_STATUS_INVALID_ARGUMENT; |
| case 10035: // WSAEWOULDBLOCK |
| return IREE_STATUS_UNAVAILABLE; |
| case 10036: // WSAEINPROGRESS |
| return IREE_STATUS_UNAVAILABLE; |
| case 10040: // WSAEMSGSIZE |
| return IREE_STATUS_OUT_OF_RANGE; |
| case 10047: // WSAEAFNOSUPPORT |
| return IREE_STATUS_INVALID_ARGUMENT; |
| case 10048: // WSAEADDRINUSE |
| return IREE_STATUS_FAILED_PRECONDITION; |
| case 10049: // WSAEADDRNOTAVAIL |
| return IREE_STATUS_INVALID_ARGUMENT; |
| case 10051: // WSAENETUNREACH |
| case 10052: // WSAENETRESET |
| case 10053: // WSAECONNABORTED |
| case 10054: // WSAECONNRESET |
| return IREE_STATUS_UNAVAILABLE; |
| case 10055: // WSAENOBUFS |
| return IREE_STATUS_RESOURCE_EXHAUSTED; |
| case 10057: // WSAENOTCONN |
| case 10058: // WSAESHUTDOWN |
| return IREE_STATUS_FAILED_PRECONDITION; |
| case 10060: // WSAETIMEDOUT |
| return IREE_STATUS_DEADLINE_EXCEEDED; |
| case 10061: // WSAECONNREFUSED |
| case 10065: // WSAEHOSTUNREACH |
| return IREE_STATUS_UNAVAILABLE; |
| |
| default: |
| return IREE_STATUS_UNKNOWN; |
| } |
| } |
| #endif // IREE_PLATFORM_WINDOWS |
| |
| //===----------------------------------------------------------------------===// |
| // iree_status_t |
| //===----------------------------------------------------------------------===// |
| |
| IREE_API_EXPORT const char* iree_status_code_string(iree_status_code_t code) { |
| switch (code) { |
| case IREE_STATUS_OK: |
| return "OK"; |
| case IREE_STATUS_CANCELLED: |
| return "CANCELLED"; |
| case IREE_STATUS_UNKNOWN: |
| return "UNKNOWN"; |
| case IREE_STATUS_INVALID_ARGUMENT: |
| return "INVALID_ARGUMENT"; |
| case IREE_STATUS_DEADLINE_EXCEEDED: |
| return "DEADLINE_EXCEEDED"; |
| case IREE_STATUS_NOT_FOUND: |
| return "NOT_FOUND"; |
| case IREE_STATUS_ALREADY_EXISTS: |
| return "ALREADY_EXISTS"; |
| case IREE_STATUS_PERMISSION_DENIED: |
| return "PERMISSION_DENIED"; |
| case IREE_STATUS_RESOURCE_EXHAUSTED: |
| return "RESOURCE_EXHAUSTED"; |
| case IREE_STATUS_FAILED_PRECONDITION: |
| return "FAILED_PRECONDITION"; |
| case IREE_STATUS_ABORTED: |
| return "ABORTED"; |
| case IREE_STATUS_OUT_OF_RANGE: |
| return "OUT_OF_RANGE"; |
| case IREE_STATUS_UNIMPLEMENTED: |
| return "UNIMPLEMENTED"; |
| case IREE_STATUS_INTERNAL: |
| return "INTERNAL"; |
| case IREE_STATUS_UNAVAILABLE: |
| return "UNAVAILABLE"; |
| case IREE_STATUS_DATA_LOSS: |
| return "DATA_LOSS"; |
| case IREE_STATUS_UNAUTHENTICATED: |
| return "UNAUTHENTICATED"; |
| case IREE_STATUS_DEFERRED: |
| return "DEFERRED"; |
| case IREE_STATUS_INCOMPATIBLE: |
| return "INCOMPATIBLE"; |
| default: |
| return ""; |
| } |
| } |
| |
| struct iree_status_handle_t { |
| uintptr_t value; |
| }; |
| |
| // Allocated storage for an iree_status_t. |
| // Only statuses that have either source information or payloads will have |
| // storage allocated for them. |
| struct iree_status_storage_t { |
| // Optional doubly-linked list of payloads associated with the status. |
| // Head = first added, tail = last added. |
| iree_status_payload_t* payload_head; |
| iree_status_payload_t* payload_tail; |
| |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_SOURCE_LOCATION) != 0 |
| // __FILE__ of the originating status allocation. |
| const char* file; |
| // __LINE__ of the originating status allocation. |
| uint32_t line; |
| #endif // has IREE_STATUS_FEATURE_SOURCE_LOCATION |
| |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_ANNOTATIONS) != 0 |
| // Optional message that is allocated either as a constant string in rodata or |
| // present as a suffix on the storage. |
| iree_string_view_t message; |
| #endif // has IREE_STATUS_FEATURE_ANNOTATIONS |
| }; |
| |
| #define iree_status_storage(status) \ |
| ((iree_status_storage_t*)(((uintptr_t)(status) & ~IREE_STATUS_CODE_MASK))) |
| |
| // Appends a payload to the storage doubly-linked list. |
| iree_status_t iree_status_append_payload(iree_status_t status, |
| iree_status_storage_t* storage, |
| iree_status_payload_t* payload) { |
| if (!storage->payload_tail) { |
| storage->payload_head = payload; |
| } else { |
| storage->payload_tail->next = payload; |
| } |
| storage->payload_tail = payload; |
| return status; |
| } |
| |
| // Formats an iree_status_payload_message_t to the given output |buffer|. |
| // |out_buffer_length| will be set to the number of characters written excluding |
| // NUL. If |buffer| is omitted then |out_buffer_length| will be set to the |
| // total number of characters in |buffer_capacity| required to contain the |
| // entire message. |
| static void iree_status_payload_message_formatter( |
| const iree_status_payload_t* base_payload, iree_host_size_t buffer_capacity, |
| char* buffer, iree_host_size_t* out_buffer_length) { |
| iree_status_payload_message_t* payload = |
| (iree_status_payload_message_t*)base_payload; |
| if (!buffer) { |
| *out_buffer_length = payload->message.size; |
| return; |
| } |
| iree_host_size_t n = buffer_capacity < payload->message.size |
| ? buffer_capacity |
| : payload->message.size; |
| memcpy(buffer, payload->message.data, n); |
| buffer[n] = '\0'; |
| *out_buffer_length = n; |
| } |
| |
| // Captures the current stack and attaches it to the status storage. |
| // A count of |skip_frames| will be skipped from the top of the stack. |
| // Setting |skip_frames|=0 will include the caller in the stack while |
| // |skip_frames|=1 will exclude it. |
| // |
| // Defined in status_stack_trace.c. |
| iree_status_t iree_status_attach_stack_trace(iree_status_t status, |
| iree_status_storage_t* storage, |
| int skip_frames); |
| |
| IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t |
| iree_status_allocate(iree_status_code_t code, const char* file, uint32_t line, |
| iree_string_view_t message) { |
| #if IREE_STATUS_FEATURES == 0 |
| // More advanced status code features like source location and messages are |
| // disabled. All statuses are just the codes. |
| return iree_status_from_code(code); |
| #else |
| // No-op for OK statuses; we won't get these from the macros but may be called |
| // with this from marshaling code. |
| if (IREE_UNLIKELY(code == IREE_STATUS_OK)) return iree_ok_status(); |
| |
| // Allocate storage with the appropriate alignment such that we can pack the |
| // code in the lower bits of the pointer. Since failed statuses are rare and |
| // likely have much larger costs (like string formatting) the extra bytes for |
| // alignment are worth being able to avoid pointer dereferences and other |
| // things during the normal code paths that just check codes. |
| // |
| // Note that we are using the CRT allocation function here, as we can't trust |
| // our allocator system to work when we are throwing errors (as we may be |
| // allocating this error from a failed allocation!). |
| size_t storage_alignment = (IREE_STATUS_CODE_MASK + 1); |
| size_t storage_size = |
| iree_host_align(sizeof(iree_status_storage_t), storage_alignment); |
| iree_status_storage_t* storage = |
| (iree_status_storage_t*)iree_aligned_alloc_raw(storage_alignment, |
| storage_size); |
| if (IREE_UNLIKELY(!storage)) return iree_status_from_code(code); |
| memset(storage, 0, sizeof(*storage)); |
| |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_SOURCE_LOCATION) != 0 |
| storage->file = file; |
| storage->line = line; |
| #endif // has IREE_STATUS_FEATURE_SOURCE_LOCATION |
| |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_ANNOTATIONS) != 0 |
| // NOTE: messages are rodata strings here and not retained. |
| storage->message = message; |
| #endif // has IREE_STATUS_FEATURE_ANNOTATIONS |
| |
| return iree_status_attach_stack_trace( |
| (iree_status_t)((uintptr_t)storage | (code & IREE_STATUS_CODE_MASK)), |
| storage, /*skip_frames=*/1); |
| #endif // has any IREE_STATUS_FEATURES |
| } |
| |
| IREE_MUST_USE_RESULT static iree_status_t iree_status_allocate_vf_impl( |
| iree_status_code_t code, const char* file, uint32_t line, int skip_frames, |
| const char* format, va_list varargs) { |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_ANNOTATIONS) == 0 |
| // Annotations disabled; ignore the format string/args. |
| return iree_status_allocate(code, file, line, iree_string_view_empty()); |
| #else |
| // No-op for OK statuses; we won't get these from the macros but may be called |
| // with this from marshaling code. |
| if (IREE_UNLIKELY(code == IREE_STATUS_OK)) return iree_ok_status(); |
| |
| // Copy varargs so we can walk the list twice: once to measure, once to |
| // format. The original is consumed by measurement; the copy by formatting. |
| va_list varargs_copy; |
| va_copy(varargs_copy, varargs); |
| |
| // Compute the total number of bytes (including NUL) required to store the |
| // message. |
| int message_size = |
| iree_vsnprintf(/*buffer=*/NULL, /*buffer_count=*/0, format, varargs); |
| if (message_size < 0) { |
| va_end(varargs_copy); |
| return iree_status_from_code(code); |
| } |
| ++message_size; // NUL byte |
| |
| // Allocate storage with the additional room to store the formatted message. |
| // This avoids additional allocations for the common case of a message coming |
| // only from the original status error site. |
| iree_host_size_t unaligned_size = 0; |
| iree_host_size_t message_offset = 0; |
| iree_status_t layout_status = IREE_STRUCT_LAYOUT( |
| iree_sizeof_struct(iree_status_storage_t), &unaligned_size, |
| IREE_STRUCT_FIELD(message_size, char, &message_offset)); |
| if (!iree_status_is_ok(layout_status)) { |
| iree_status_ignore(layout_status); |
| va_end(varargs_copy); |
| return iree_status_from_code(code); |
| } |
| iree_host_size_t storage_alignment = (IREE_STATUS_CODE_MASK + 1); |
| iree_host_size_t storage_size = 0; |
| if (!iree_host_size_checked_align(unaligned_size, storage_alignment, |
| &storage_size)) { |
| va_end(varargs_copy); |
| return iree_status_from_code(code); |
| } |
| iree_status_storage_t* storage = |
| (iree_status_storage_t*)iree_aligned_alloc_raw(storage_alignment, |
| storage_size); |
| if (IREE_UNLIKELY(!storage)) { |
| va_end(varargs_copy); |
| return iree_status_from_code(code); |
| } |
| memset(storage, 0, sizeof(*storage)); |
| |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_SOURCE_LOCATION) != 0 |
| storage->file = file; |
| storage->line = line; |
| #endif // has IREE_STATUS_FEATURE_SOURCE_LOCATION |
| |
| // Format directly into the message buffer using the copied varargs. |
| storage->message.size = message_size - 1; |
| storage->message.data = (const char*)storage + message_offset; |
| int ret = iree_vsnprintf((char*)storage->message.data, message_size, format, |
| varargs_copy); |
| va_end(varargs_copy); |
| if (IREE_UNLIKELY(ret < 0)) { |
| iree_aligned_free_raw(storage); |
| return (iree_status_t)code; |
| } |
| |
| return iree_status_attach_stack_trace( |
| (iree_status_t)((uintptr_t)storage | (code & IREE_STATUS_CODE_MASK)), |
| storage, skip_frames); |
| #endif // has IREE_STATUS_FEATURE_ANNOTATIONS |
| } |
| |
| IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t |
| iree_status_allocate_f(iree_status_code_t code, const char* file, uint32_t line, |
| const char* format, ...) { |
| va_list varargs; |
| va_start(varargs, format); |
| iree_status_t ret = iree_status_allocate_vf_impl( |
| code, file, line, /*skip_frames=*/2, format, varargs); |
| va_end(varargs); |
| return ret; |
| } |
| |
| IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t |
| iree_status_allocate_vf(iree_status_code_t code, const char* file, |
| uint32_t line, const char* format, va_list varargs) { |
| return iree_status_allocate_vf_impl(code, file, line, /*skip_frames=*/1, |
| format, varargs); |
| } |
| |
| IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t |
| iree_status_allocate_copy(iree_status_code_t code, iree_string_view_t file, |
| uint32_t line, iree_string_view_t message) { |
| #if IREE_STATUS_FEATURES == 0 |
| return iree_status_from_code(code); |
| #else |
| if (IREE_UNLIKELY(code == IREE_STATUS_OK)) return iree_ok_status(); |
| |
| // Compute layout: storage + file string (NUL-terminated) + message string. |
| iree_host_size_t unaligned_size = 0; |
| iree_host_size_t file_offset = 0; |
| iree_host_size_t message_offset = 0; |
| |
| // File string with NUL terminator (if present). |
| bool has_file = file.size > 0; |
| iree_host_size_t file_alloc_size = 0; |
| if (has_file && !iree_host_size_checked_add(file.size, 1, &file_alloc_size)) { |
| return iree_status_from_code(code); |
| } |
| // Message with NUL terminator (if present). |
| bool has_message = message.size > 0; |
| iree_host_size_t message_alloc_size = 0; |
| if (has_message && |
| !iree_host_size_checked_add(message.size, 1, &message_alloc_size)) { |
| return iree_status_from_code(code); |
| } |
| |
| iree_status_t layout_status = IREE_STRUCT_LAYOUT( |
| iree_sizeof_struct(iree_status_storage_t), &unaligned_size, |
| IREE_STRUCT_FIELD(file_alloc_size, char, &file_offset), |
| IREE_STRUCT_FIELD(message_alloc_size, char, &message_offset)); |
| if (!iree_status_is_ok(layout_status)) { |
| iree_status_ignore(layout_status); |
| return iree_status_from_code(code); |
| } |
| |
| iree_host_size_t storage_alignment = (IREE_STATUS_CODE_MASK + 1); |
| iree_host_size_t storage_size = 0; |
| if (!iree_host_size_checked_align(unaligned_size, storage_alignment, |
| &storage_size)) { |
| return iree_status_from_code(code); |
| } |
| iree_status_storage_t* storage = |
| (iree_status_storage_t*)iree_aligned_alloc_raw(storage_alignment, |
| storage_size); |
| if (IREE_UNLIKELY(!storage)) return iree_status_from_code(code); |
| memset(storage, 0, sizeof(*storage)); |
| |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_SOURCE_LOCATION) != 0 |
| if (has_file) { |
| char* file_copy = (char*)storage + file_offset; |
| memcpy(file_copy, file.data, file.size); |
| file_copy[file.size] = '\0'; |
| storage->file = file_copy; |
| } |
| storage->line = line; |
| #endif // has IREE_STATUS_FEATURE_SOURCE_LOCATION |
| |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_ANNOTATIONS) != 0 |
| if (has_message) { |
| char* message_copy = (char*)storage + message_offset; |
| memcpy(message_copy, message.data, message.size); |
| message_copy[message.size] = '\0'; |
| storage->message = iree_make_string_view(message_copy, message.size); |
| } |
| #endif // has IREE_STATUS_FEATURE_ANNOTATIONS |
| |
| return (iree_status_t)((uintptr_t)storage | (code & IREE_STATUS_CODE_MASK)); |
| #endif // has any IREE_STATUS_FEATURES |
| } |
| |
| IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t |
| iree_status_clone(iree_status_t status) { |
| #if IREE_STATUS_FEATURES == 0 |
| // Statuses are just codes; nothing to do. |
| return status; |
| #else |
| iree_status_storage_t* storage = iree_status_storage(status); |
| if (!storage) return status; |
| |
| iree_string_view_t file = iree_string_view_empty(); |
| uint32_t line = 0; |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_SOURCE_LOCATION) != 0 |
| if (storage->file) { |
| file = iree_make_cstring_view(storage->file); |
| } |
| line = storage->line; |
| #endif // has IREE_STATUS_FEATURE_SOURCE_LOCATION |
| |
| iree_string_view_t message = iree_string_view_empty(); |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_ANNOTATIONS) != 0 |
| message = storage->message; |
| #endif // has IREE_STATUS_FEATURE_ANNOTATIONS |
| |
| // Copy both file and message into self-contained storage. The original |
| // status may borrow these pointers from rodata (__FILE__) or from |
| // trailing storage that will be freed, so we always copy. |
| return iree_status_allocate_copy(iree_status_code(status), file, line, |
| message); |
| #endif // has no IREE_STATUS_FEATURES |
| } |
| |
| IREE_API_EXPORT void iree_status_free(iree_status_t status) { |
| #if IREE_STATUS_FEATURES != 0 |
| iree_status_storage_t* storage = iree_status_storage(status); |
| if (!storage) return; |
| iree_status_payload_t* payload = storage->payload_head; |
| while (payload) { |
| iree_status_payload_t* next = payload->next; |
| iree_allocator_free(payload->allocator, payload); |
| payload = next; |
| } |
| iree_aligned_free_raw(storage); |
| #endif // has any IREE_STATUS_FEATURES |
| } |
| |
| IREE_API_EXPORT iree_status_t iree_status_ignore(iree_status_t status) { |
| // We can set an 'ignored' flag on the status so that we can otherwise assert |
| // in iree_status_free when statuses are freed without this being called. |
| // Hoping with the C++ Status wrapper we won't hit that often so that |
| // complexity is skipped for now. |
| iree_status_free(status); |
| return iree_ok_status(); |
| } |
| |
| IREE_API_EXPORT iree_status_t iree_status_join(iree_status_t base_status, |
| iree_status_t new_status) { |
| if (!iree_status_is_ok(base_status)) { |
| if (!iree_status_is_ok(new_status)) { |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_ANNOTATIONS) != 0 |
| iree_allocator_t allocator = iree_allocator_system(); |
| char* message = NULL; |
| iree_host_size_t message_length = 0; |
| if (iree_status_to_string(new_status, &allocator, &message, |
| &message_length)) { |
| base_status = |
| iree_status_annotate_f(base_status, "additional failure: %.*s", |
| (int)message_length, message); |
| iree_allocator_free(allocator, message); |
| } else { |
| base_status = iree_status_annotate_f( |
| base_status, "additional failure: %s", |
| iree_status_code_string(iree_status_code(new_status))); |
| } |
| #endif // has IREE_STATUS_FEATURE_ANNOTATIONS |
| iree_status_free(new_status); |
| } |
| return base_status; |
| } |
| return new_status; |
| } |
| |
| IREE_API_EXPORT IREE_ATTRIBUTE_NORETURN void iree_status_abort( |
| iree_status_t status) { |
| iree_status_fprint(stderr, status); |
| IREE_ASSERT(!iree_status_is_ok(status), |
| "only valid to call with failing status codes"); |
| iree_status_free(status); |
| iree_abort(); |
| } |
| |
| IREE_API_EXPORT iree_status_code_t |
| iree_status_consume_code(iree_status_t status) { |
| iree_status_code_t code = iree_status_code(status); |
| iree_status_free(status); |
| return code; |
| } |
| |
| #if IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_ANNOTATIONS |
| |
| IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t |
| iree_status_annotate(iree_status_t base_status, iree_string_view_t message) { |
| if (iree_status_is_ok(base_status) || iree_string_view_is_empty(message)) { |
| return base_status; |
| } |
| |
| // If there's no storage yet we can just reuse normal allocation. Both that |
| // and this do not copy |message|. |
| iree_status_storage_t* storage = iree_status_storage(base_status); |
| if (!storage) { |
| return iree_status_allocate(iree_status_code(base_status), NULL, 0, |
| message); |
| } else if (iree_string_view_is_empty(storage->message)) { |
| storage->message = message; |
| return base_status; |
| } |
| |
| iree_allocator_t allocator = iree_allocator_system(); |
| iree_status_payload_message_t* payload = NULL; |
| iree_status_ignore( |
| iree_allocator_malloc(allocator, sizeof(*payload), (void**)&payload)); |
| if (IREE_UNLIKELY(!payload)) return base_status; |
| memset(payload, 0, sizeof(*payload)); |
| payload->header.type = IREE_STATUS_PAYLOAD_TYPE_MESSAGE; |
| payload->header.allocator = allocator; |
| payload->header.formatter = iree_status_payload_message_formatter; |
| payload->message = message; |
| return iree_status_append_payload(base_status, storage, |
| (iree_status_payload_t*)payload); |
| } |
| |
| IREE_MUST_USE_RESULT static iree_status_t iree_status_annotate_vf( |
| iree_status_t base_status, const char* format, va_list varargs) { |
| if (iree_status_is_ok(base_status)) return base_status; |
| |
| // If there's no storage yet we can just reuse normal allocation. Both that |
| // and this do not copy |message|. |
| iree_status_storage_t* storage = iree_status_storage(base_status); |
| if (!storage) { |
| return iree_status_allocate_vf(iree_status_code(base_status), NULL, 0, |
| format, varargs); |
| } |
| |
| // Copy varargs so we can walk the list twice: once to measure, once to |
| // format. The original is consumed by measurement; the copy by formatting. |
| va_list varargs_copy; |
| va_copy(varargs_copy, varargs); |
| |
| // Compute the total number of bytes (including NUL) required to store the |
| // message. |
| int message_size = |
| iree_vsnprintf(/*buffer=*/NULL, /*buffer_count=*/0, format, varargs); |
| if (message_size < 0) { |
| va_end(varargs_copy); |
| return base_status; |
| } |
| ++message_size; // NUL byte |
| |
| // Allocate storage with the additional room to store the formatted message. |
| // This avoids additional allocations for the common case of a message coming |
| // only from the original status error site. |
| iree_host_size_t alloc_size = 0; |
| iree_host_size_t message_offset = 0; |
| iree_status_t layout_status = IREE_STRUCT_LAYOUT( |
| iree_sizeof_struct(iree_status_payload_message_t), &alloc_size, |
| IREE_STRUCT_FIELD(message_size, char, &message_offset)); |
| if (!iree_status_is_ok(layout_status)) { |
| iree_status_ignore(layout_status); |
| va_end(varargs_copy); |
| return base_status; |
| } |
| iree_allocator_t allocator = iree_allocator_system(); |
| iree_status_payload_message_t* payload = NULL; |
| iree_status_ignore( |
| iree_allocator_malloc(allocator, alloc_size, (void**)&payload)); |
| if (IREE_UNLIKELY(!payload)) { |
| va_end(varargs_copy); |
| return base_status; |
| } |
| memset(payload, 0, sizeof(*payload)); |
| payload->header.type = IREE_STATUS_PAYLOAD_TYPE_MESSAGE; |
| payload->header.allocator = allocator; |
| payload->header.formatter = iree_status_payload_message_formatter; |
| |
| // Format directly into the message buffer using the copied varargs. |
| payload->message.size = message_size - 1; |
| payload->message.data = (const char*)payload + message_offset; |
| int ret = iree_vsnprintf((char*)payload->message.data, |
| payload->message.size + 1, format, varargs_copy); |
| va_end(varargs_copy); |
| if (IREE_UNLIKELY(ret < 0)) { |
| iree_allocator_free(allocator, payload); |
| return base_status; |
| } |
| return iree_status_append_payload(base_status, storage, |
| (iree_status_payload_t*)payload); |
| } |
| |
| IREE_API_EXPORT IREE_MUST_USE_RESULT iree_status_t IREE_PRINTF_ATTRIBUTE(2, 3) |
| iree_status_annotate_f(iree_status_t base_status, const char* format, ...) { |
| va_list varargs; |
| va_start(varargs, format); |
| iree_status_t ret = iree_status_annotate_vf(base_status, format, varargs); |
| va_end(varargs); |
| return ret; |
| } |
| |
| #endif // has IREE_STATUS_FEATURE_ANNOTATIONS |
| |
| 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_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; |
| int n = 0; |
| |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_ANNOTATIONS) != 0 |
| // Append base storage message. |
| if (storage && !iree_string_view_is_empty(storage->message)) { |
| n = iree_snprintf(buffer ? buffer + buffer_length : NULL, |
| 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) { |
| buffer = NULL; |
| } |
| 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. |
| iree_status_payload_t* payload = storage ? storage->payload_head : NULL; |
| while (payload != NULL) { |
| // Skip payloads that have no textual representation. |
| if (!payload->formatter) { |
| payload = payload->next; |
| continue; |
| } |
| |
| // Append newline to join with message above and other payloads. |
| if (buffer) { |
| if (2 >= buffer_capacity - buffer_length) { |
| buffer = NULL; |
| } else { |
| buffer[buffer_length] = ';'; |
| buffer[buffer_length + 1] = ' '; |
| } |
| } |
| buffer_length += 2; // '; ' |
| |
| // Append payload via custom formatter callback. |
| iree_host_size_t payload_buffer_length = 0; |
| payload->formatter(payload, buffer ? buffer_capacity - buffer_length : 0, |
| buffer ? buffer + buffer_length : NULL, |
| &payload_buffer_length); |
| if (buffer && payload_buffer_length >= buffer_capacity - buffer_length) { |
| buffer = NULL; |
| } |
| buffer_length += payload_buffer_length; |
| |
| payload = payload->next; |
| } |
| #endif // has IREE_STATUS_FEATURES |
| |
| *out_buffer_length = buffer_length; |
| 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 = iree_snprintf(buffer ? buffer : NULL, buffer ? buffer_capacity : 0, |
| "%s:%d: %s", storage->file, storage->line, |
| iree_status_code_string(status_code)); |
| } else { |
| n = iree_snprintf(buffer ? buffer : NULL, buffer ? buffer_capacity : 0, |
| "%s", iree_status_code_string(status_code)); |
| } |
| #else |
| n = iree_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 ? buffer_capacity - prefix_buffer_length : 0, |
| buffer ? buffer + prefix_buffer_length : NULL, &message_buffer_length, |
| /*has_prefix=*/true); |
| if (!ret) { |
| return false; |
| } |
| *out_buffer_length = prefix_buffer_length + message_buffer_length; |
| return true; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Callback-based status formatting (zero-allocation streaming) |
| //===----------------------------------------------------------------------===// |
| |
| // Streams the message and payload portions of a status to a callback. |
| // If |has_prefix| is true, the first annotation is preceded by "; ". |
| // Returns false if the callback requested early termination. |
| static bool iree_status_format_message_to(iree_status_t status, |
| iree_status_output_fn_t output_fn, |
| void* user_data, bool has_prefix) { |
| iree_status_storage_t* storage = iree_status_storage(status); |
| if (!storage) return true; |
| |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_ANNOTATIONS) != 0 |
| if (!iree_string_view_is_empty(storage->message)) { |
| if (has_prefix) { |
| if (!output_fn(iree_make_cstring_view("; "), user_data)) return false; |
| } |
| if (!output_fn(storage->message, user_data)) return false; |
| has_prefix = true; |
| } |
| #endif // has IREE_STATUS_FEATURE_ANNOTATIONS |
| |
| #if IREE_STATUS_FEATURES != 0 |
| iree_status_payload_t* payload = storage->payload_head; |
| while (payload != NULL) { |
| if (!payload->formatter) { |
| payload = payload->next; |
| continue; |
| } |
| |
| if (!output_fn(iree_make_cstring_view("; "), user_data)) return false; |
| |
| // Fast path for message payloads: stream directly without buffering. |
| if (payload->type == IREE_STATUS_PAYLOAD_TYPE_MESSAGE) { |
| iree_status_payload_message_t* message_payload = |
| (iree_status_payload_message_t*)payload; |
| if (!output_fn(message_payload->message, user_data)) return false; |
| } else { |
| // Generic path: measure, then format into a stack buffer. If the payload |
| // exceeds the stack buffer (e.g., a stack trace), we try a heap |
| // allocation. We use raw malloc/free here rather than iree_allocator_t |
| // because status formatting runs on error paths where we cannot trust |
| // the allocator system to be functional, and threading an allocator |
| // through every formatting call would complicate the API for a transient |
| // buffer that exists only for the duration of this function. |
| iree_host_size_t payload_length = 0; |
| payload->formatter(payload, 0, NULL, &payload_length); |
| |
| char stack_buffer[512]; |
| char* buffer = stack_buffer; |
| iree_host_size_t buffer_capacity = sizeof(stack_buffer); |
| bool heap_allocated = false; |
| if (payload_length >= buffer_capacity) { |
| buffer = (char*)malloc(payload_length + 1); |
| if (!buffer) { |
| // Heap allocation failed; truncate into the stack buffer and append |
| // an indicator so the output signals that content was lost. |
| iree_host_size_t actual_length = 0; |
| payload->formatter(payload, sizeof(stack_buffer), stack_buffer, |
| &actual_length); |
| iree_host_size_t truncated_length = |
| actual_length < sizeof(stack_buffer) - 3 |
| ? actual_length |
| : sizeof(stack_buffer) - 4; |
| stack_buffer[truncated_length] = '.'; |
| stack_buffer[truncated_length + 1] = '.'; |
| stack_buffer[truncated_length + 2] = '.'; |
| if (!output_fn( |
| iree_make_string_view(stack_buffer, truncated_length + 3), |
| user_data)) { |
| return false; |
| } |
| payload = payload->next; |
| continue; |
| } |
| buffer_capacity = payload_length + 1; |
| heap_allocated = true; |
| } |
| |
| iree_host_size_t actual_length = 0; |
| payload->formatter(payload, buffer_capacity, buffer, &actual_length); |
| bool should_continue = |
| output_fn(iree_make_string_view(buffer, actual_length), user_data); |
| |
| if (heap_allocated) free(buffer); |
| if (!should_continue) return false; |
| } |
| |
| payload = payload->next; |
| } |
| #endif // has IREE_STATUS_FEATURES |
| return true; |
| } |
| |
| IREE_API_EXPORT bool iree_status_format_to(iree_status_t status, |
| iree_status_output_fn_t output_fn, |
| void* user_data) { |
| iree_status_storage_t* storage IREE_ATTRIBUTE_UNUSED = |
| iree_status_storage(status); |
| iree_status_code_t status_code = iree_status_code(status); |
| |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_SOURCE_LOCATION) != 0 |
| if (storage && storage->file) { |
| // Stream file path and line number without buffering the path. |
| if (!output_fn(iree_make_cstring_view(storage->file), user_data)) |
| return false; |
| char line_buffer[16]; |
| int n = |
| iree_snprintf(line_buffer, sizeof(line_buffer), ":%u: ", storage->line); |
| if (n > 0) { |
| if (!output_fn( |
| iree_make_string_view( |
| line_buffer, iree_min((size_t)n, sizeof(line_buffer) - 1)), |
| user_data)) { |
| return false; |
| } |
| } |
| } |
| #endif // has IREE_STATUS_FEATURE_SOURCE_LOCATION |
| |
| if (!output_fn(iree_make_cstring_view(iree_status_code_string(status_code)), |
| user_data)) { |
| return false; |
| } |
| |
| return iree_status_format_message_to(status, output_fn, user_data, |
| /*has_prefix=*/true); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Status freeze |
| //===----------------------------------------------------------------------===// |
| |
| #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. |
| #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; |
| } |
| iree_host_size_t file_storage_size = file ? strlen(file) + 1 : 0; |
| #endif // has IREE_STATUS_FEATURE_SOURCE_LOCATION |
| |
| iree_host_size_t unaligned_storage_size = 0; |
| iree_host_size_t message_offset = 0; |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_SOURCE_LOCATION) != 0 |
| iree_host_size_t file_offset = 0; |
| iree_status_t layout_status = IREE_STRUCT_LAYOUT( |
| iree_sizeof_struct(iree_status_storage_t), &unaligned_storage_size, |
| IREE_STRUCT_FIELD(message_buffer_size, char, &message_offset), |
| IREE_STRUCT_FIELD(file_storage_size, char, &file_offset)); |
| #else |
| iree_status_t layout_status = IREE_STRUCT_LAYOUT( |
| iree_sizeof_struct(iree_status_storage_t), &unaligned_storage_size, |
| IREE_STRUCT_FIELD(message_buffer_size, char, &message_offset)); |
| #endif // has IREE_STATUS_FEATURE_SOURCE_LOCATION |
| if (!iree_status_is_ok(layout_status)) { |
| iree_status_ignore(layout_status); |
| iree_status_free(status); |
| return iree_status_from_code(code); |
| } |
| |
| iree_host_size_t storage_alignment = (IREE_STATUS_CODE_MASK + 1); |
| iree_host_size_t storage_size = 0; |
| if (!iree_host_size_checked_align(unaligned_storage_size, storage_alignment, |
| &storage_size)) { |
| iree_status_free(status); |
| return iree_status_from_code(code); |
| } |
| iree_status_storage_t* new_storage = |
| (iree_status_storage_t*)iree_aligned_alloc_raw(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 + message_offset; |
| 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 + message_offset; |
| 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 + file_offset; |
| // 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 |
| |
| // Callback that appends chunks to a string builder, returning false on |
| // allocation failure to short-circuit formatting. |
| static bool iree_status_to_string_output(iree_string_view_t chunk, |
| void* user_data) { |
| iree_string_builder_t* builder = (iree_string_builder_t*)user_data; |
| iree_status_t status = iree_string_builder_append_string(builder, chunk); |
| if (!iree_status_is_ok(status)) { |
| iree_status_ignore(status); |
| return false; |
| } |
| return true; |
| } |
| |
| 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) { |
| *out_buffer = NULL; |
| *out_buffer_length = 0; |
| |
| // Single-pass: stream the formatted status into a string builder. |
| iree_string_builder_t builder; |
| iree_string_builder_initialize(*allocator, &builder); |
| |
| if (!iree_status_format_to(status, iree_status_to_string_output, &builder)) { |
| iree_string_builder_deinitialize(&builder); |
| return false; |
| } |
| |
| *out_buffer_length = iree_string_builder_size(&builder); |
| *out_buffer = iree_string_builder_take_storage(&builder); |
| return true; |
| } |
| |
| // Callback that writes chunks directly to a FILE*. |
| static bool iree_status_fwrite_output(iree_string_view_t chunk, |
| void* user_data) { |
| return fwrite(chunk.data, 1, chunk.size, (FILE*)user_data) == chunk.size; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Status structured access |
| //===----------------------------------------------------------------------===// |
| |
| IREE_API_EXPORT iree_status_source_location_t |
| iree_status_source_location(iree_status_t status) { |
| iree_status_source_location_t location = {NULL, 0}; |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_SOURCE_LOCATION) != 0 |
| iree_status_storage_t* storage = iree_status_storage(status); |
| if (storage) { |
| location.file = storage->file; |
| location.line = storage->line; |
| } |
| #endif // has IREE_STATUS_FEATURE_SOURCE_LOCATION |
| return location; |
| } |
| |
| IREE_API_EXPORT iree_string_view_t iree_status_message(iree_status_t status) { |
| #if (IREE_STATUS_FEATURES & IREE_STATUS_FEATURE_ANNOTATIONS) != 0 |
| iree_status_storage_t* storage = iree_status_storage(status); |
| if (storage) { |
| return storage->message; |
| } |
| #endif // has IREE_STATUS_FEATURE_ANNOTATIONS |
| return iree_string_view_empty(); |
| } |
| |
| IREE_API_EXPORT iree_status_t iree_status_enumerate_payloads( |
| iree_status_t status, iree_status_payload_visitor_fn_t visitor, |
| void* user_data) { |
| #if IREE_STATUS_FEATURES != 0 |
| iree_status_storage_t* storage = iree_status_storage(status); |
| if (!storage) return iree_ok_status(); |
| iree_status_payload_t* payload = storage->payload_head; |
| while (payload) { |
| iree_status_t visit_status = visitor(user_data, payload); |
| if (!iree_status_is_ok(visit_status)) return visit_status; |
| payload = payload->next; |
| } |
| #endif // has any IREE_STATUS_FEATURES |
| return iree_ok_status(); |
| } |
| |
| IREE_API_EXPORT iree_status_payload_type_t |
| iree_status_payload_type(const iree_status_payload_t* payload) { |
| return payload->type; |
| } |
| |
| IREE_API_EXPORT void iree_status_payload_format( |
| const iree_status_payload_t* payload, iree_host_size_t buffer_capacity, |
| char* buffer, iree_host_size_t* out_buffer_length) { |
| if (!payload->formatter) { |
| if (out_buffer_length) *out_buffer_length = 0; |
| return; |
| } |
| payload->formatter(payload, buffer_capacity, buffer, out_buffer_length); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Status printing |
| //===----------------------------------------------------------------------===// |
| |
| IREE_API_EXPORT void iree_status_fprint(FILE* file, iree_status_t status) { |
| // Stream the formatted status directly to the file with zero allocation. |
| iree_status_format_to(status, iree_status_fwrite_output, file); |
| fputc('\n', file); |
| fflush(file); |
| } |