// Copyright 2022 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/tooling/device_util.h"

#include <stdio.h>
#include <string.h>

#include "iree/base/internal/atomics.h"
#include "iree/base/threading/call_once.h"
#include "iree/base/threading/mutex.h"
#include "iree/base/threading/notification.h"
#include "iree/base/threading/thread.h"
#include "iree/base/tooling/flags.h"
#include "iree/hal/drivers/init.h"
#include "iree/hal/utils/allocators.h"
#include "iree/hal/utils/mpi_channel_provider.h"
#include "iree/hal/utils/profile_file.h"
#include "iree/hal/utils/statistics_sink.h"
#include "iree/io/file_handle.h"

//===----------------------------------------------------------------------===//
// Shared driver registry
//===----------------------------------------------------------------------===//

static iree_once_flag iree_hal_driver_registry_init_flag = IREE_ONCE_FLAG_INIT;
static void iree_hal_driver_registry_init_from_flags(void) {
  IREE_TRACE_ZONE_BEGIN(z0);
  IREE_CHECK_OK(iree_hal_register_all_available_drivers(
      iree_hal_driver_registry_default()));
  IREE_TRACE_ZONE_END(z0);
}

iree_hal_driver_registry_t* iree_hal_available_driver_registry(void) {
  iree_call_once(&iree_hal_driver_registry_init_flag,
                 iree_hal_driver_registry_init_from_flags);
  return iree_hal_driver_registry_default();
}

iree_string_view_t iree_hal_default_device_uri(void) {
  // TODO(benvanik): query the registry and find the first available :shrug:
  return IREE_SV("local-task");
}

//===----------------------------------------------------------------------===//
// Driver and device listing commands
//===----------------------------------------------------------------------===//

static void iree_hal_flags_print_action_header(void) {
  fprintf(stdout,
          "# "
          "===================================================================="
          "========\n");
}

static void iree_hal_flags_print_action_separator(void) {
  fprintf(stdout,
          "# "
          "===-----------------------------------------------------------"
          "-----------===\n");
}

static void iree_hal_flags_print_action_flag(iree_string_view_t flag_name,
                                             void* storage, FILE* file) {
  fprintf(file, "# --%.*s\n", (int)flag_name.size, flag_name.data);
}

static iree_status_t iree_hal_flags_list_drivers(iree_string_view_t flag_name,
                                                 void* storage,
                                                 iree_string_view_t value) {
  iree_allocator_t host_allocator = iree_allocator_system();

  iree_hal_flags_print_action_header();
  fprintf(stdout, "# Available HAL drivers\n");
  iree_hal_flags_print_action_header();
  fprintf(
      stdout,
      "# Use --list_devices={driver name} to enumerate available devices.\n\n");

  iree_hal_driver_registry_t* driver_registry =
      iree_hal_available_driver_registry();
  iree_host_size_t driver_info_count = 0;
  iree_hal_driver_info_t* driver_infos = NULL;
  IREE_RETURN_IF_ERROR(iree_hal_driver_registry_enumerate(
      driver_registry, host_allocator, &driver_info_count, &driver_infos));

  for (iree_host_size_t i = 0; i < driver_info_count; ++i) {
    const iree_hal_driver_info_t* driver_info = &driver_infos[i];
    fprintf(stdout, "%16.*s: %.*s\n", (int)driver_info->driver_name.size,
            driver_info->driver_name.data, (int)driver_info->full_name.size,
            driver_info->full_name.data);
  }

  iree_allocator_free(host_allocator, driver_infos);

  exit(0);
  return iree_ok_status();
}

IREE_FLAG_CALLBACK(iree_hal_flags_list_drivers,
                   iree_hal_flags_print_action_flag, NULL, list_drivers,
                   "Lists all available HAL drivers compiled into the binary.");

static iree_status_t iree_hal_flags_list_driver_device(
    iree_string_view_t driver_name, iree_hal_driver_t* driver,
    const iree_hal_device_info_t* device_info, iree_allocator_t host_allocator,
    FILE* file) {
  fprintf(file, "%.*s://%.*s\n", (int)driver_name.size, driver_name.data,
          (int)device_info->path.size, device_info->path.data);
  return iree_ok_status();
}

static iree_status_t iree_hal_flags_list_driver_devices(
    iree_hal_driver_registry_t* driver_registry, iree_string_view_t driver_name,
    iree_allocator_t host_allocator, bool fail_on_driver_error, FILE* file) {
  iree_hal_driver_t* driver = NULL;
  iree_status_t driver_status = iree_hal_driver_registry_try_create(
      driver_registry, driver_name, host_allocator, &driver);
  if (!iree_status_is_ok(driver_status) && fail_on_driver_error) {
    // Driver could not be created (not found, unavailable, etc) and user asked
    // for it explicitly and should be notified.
    return driver_status;
  } else if (!iree_status_is_ok(driver_status)) {
    // Driver could not be created so there are no devices to list.
    iree_status_free(driver_status);
    return iree_ok_status();
  }

  iree_host_size_t device_info_count = 0;
  iree_hal_device_info_t* device_infos = NULL;
  iree_status_t status = iree_hal_driver_query_available_devices(
      driver, host_allocator, &device_info_count, &device_infos);

  for (iree_host_size_t i = 0;
       iree_status_is_ok(status) && i < device_info_count; ++i) {
    status = iree_hal_flags_list_driver_device(
        driver_name, driver, &device_infos[i], host_allocator, file);
  }

  iree_allocator_free(host_allocator, device_infos);
  iree_hal_driver_release(driver);
  return status;
}

static iree_status_t iree_hal_flags_list_devices(iree_string_view_t flag_name,
                                                 void* storage,
                                                 iree_string_view_t value) {
  iree_allocator_t host_allocator = iree_allocator_system();
  iree_hal_driver_registry_t* driver_registry =
      iree_hal_available_driver_registry();

  if (iree_string_view_is_empty(value)) {
    // List all devices on all drivers.
    iree_host_size_t driver_info_count = 0;
    iree_hal_driver_info_t* driver_infos = NULL;
    IREE_RETURN_IF_ERROR(iree_hal_driver_registry_enumerate(
        driver_registry, host_allocator, &driver_info_count, &driver_infos));
    for (iree_host_size_t i = 0; i < driver_info_count; ++i) {
      const iree_hal_driver_info_t* driver_info = &driver_infos[i];
      IREE_RETURN_IF_ERROR(iree_hal_flags_list_driver_devices(
          driver_registry, driver_info->driver_name, host_allocator,
          /*fail_on_driver_error=*/false, stdout));
    }
    iree_allocator_free(host_allocator, driver_infos);
  } else {
    // List all devices from a particular driver.
    IREE_RETURN_IF_ERROR(iree_hal_flags_list_driver_devices(
        driver_registry, value, host_allocator, /*fail_on_driver_error=*/true,
        stdout));
  }

  exit(0);
  return iree_ok_status();
}

IREE_FLAG_CALLBACK(
    iree_hal_flags_list_devices, iree_hal_flags_print_action_flag, NULL,
    list_devices,
    "Lists all available HAL devices from all drivers or a specific driver.\n"
    "Examples:\n"
    "  Show all devices from all drivers: --list_devices\n"
    "  Show all devices from a particular driver: --list_devices=vulkan");

static iree_status_t iree_hal_flags_dump_driver_device(
    iree_string_view_t driver_name, iree_hal_driver_t* driver,
    const iree_hal_device_info_t* device_info, iree_allocator_t host_allocator,
    FILE* file) {
  iree_hal_flags_print_action_separator();
  fprintf(file, "# --device=%.*s://%.*s\n", (int)driver_name.size,
          driver_name.data, (int)device_info->path.size,
          device_info->path.data);
  // TODO(benvanik): driver full name.
  fprintf(file, "#   %.*s\n", (int)device_info->name.size,
          device_info->name.data);
  iree_hal_flags_print_action_separator();

  iree_string_builder_t builder;
  iree_string_builder_initialize(host_allocator, &builder);

  IREE_RETURN_IF_ERROR(iree_hal_driver_dump_device_info(
      driver, device_info->device_id, &builder));
  if (iree_string_builder_size(&builder) > 0) {
    fprintf(file, "%.*s", (int)iree_string_builder_size(&builder),
            iree_string_builder_buffer(&builder));
  }

  iree_string_builder_deinitialize(&builder);

  return iree_ok_status();
}

static iree_status_t iree_hal_flags_dump_driver_devices(
    iree_hal_driver_registry_t* driver_registry, iree_string_view_t driver_name,
    iree_allocator_t host_allocator, bool fail_on_driver_error, FILE* file) {
  iree_hal_flags_print_action_header();
  fprintf(stdout, "# Enumerated devices for driver '%.*s'\n",
          (int)driver_name.size, driver_name.data);
  iree_hal_flags_print_action_header();
  fprintf(stdout, "\n");

  iree_hal_driver_t* driver = NULL;
  iree_status_t driver_status = iree_hal_driver_registry_try_create(
      driver_registry, driver_name, host_allocator, &driver);
  if (!iree_status_is_ok(driver_status) && fail_on_driver_error) {
    // Driver could not be created (not found, unavailable, etc) and user asked
    // for it explicitly and should be notified.
    return driver_status;
  } else if (!iree_status_is_ok(driver_status)) {
    // Driver could not be created so there are no devices to list.
    iree_status_free(driver_status);
    return iree_ok_status();
  }

  iree_host_size_t device_info_count = 0;
  iree_hal_device_info_t* device_infos = NULL;
  iree_status_t status = iree_hal_driver_query_available_devices(
      driver, host_allocator, &device_info_count, &device_infos);

  for (iree_host_size_t i = 0;
       iree_status_is_ok(status) && i < device_info_count; ++i) {
    status = iree_hal_flags_dump_driver_device(
        driver_name, driver, &device_infos[i], host_allocator, file);
  }

  iree_allocator_free(host_allocator, device_infos);
  iree_hal_driver_release(driver);
  return status;
}

static iree_status_t iree_hal_flags_dump_devices(iree_string_view_t flag_name,
                                                 void* storage,
                                                 iree_string_view_t value) {
  iree_allocator_t host_allocator = iree_allocator_system();
  iree_hal_driver_registry_t* driver_registry =
      iree_hal_available_driver_registry();

  if (iree_string_view_is_empty(value)) {
    // List all devices on all drivers.
    iree_host_size_t driver_info_count = 0;
    iree_hal_driver_info_t* driver_infos = NULL;
    IREE_RETURN_IF_ERROR(iree_hal_driver_registry_enumerate(
        driver_registry, host_allocator, &driver_info_count, &driver_infos));
    for (iree_host_size_t i = 0; i < driver_info_count; ++i) {
      if (i) fprintf(stdout, "\n");
      const iree_hal_driver_info_t* driver_info = &driver_infos[i];
      IREE_RETURN_IF_ERROR(iree_hal_flags_dump_driver_devices(
          driver_registry, driver_info->driver_name, host_allocator,
          /*fail_on_driver_error=*/false, stdout));
    }
    iree_allocator_free(host_allocator, driver_infos);
  } else {
    // List all devices from a particular driver.
    IREE_RETURN_IF_ERROR(iree_hal_flags_dump_driver_devices(
        driver_registry, value, host_allocator, /*fail_on_driver_error=*/true,
        stdout));
  }

  exit(0);
  return iree_ok_status();
}

IREE_FLAG_CALLBACK(
    iree_hal_flags_dump_devices, iree_hal_flags_print_action_flag, NULL,
    dump_devices,
    "Dumps detailed information on all available HAL devices from all drivers\n"
    " or a specific driver.\n"
    "Examples:\n"
    "  Show all devices from all drivers: --dump_devices\n"
    "  Show all devices from a particular driver: --dump_devices=vulkan");

//===----------------------------------------------------------------------===//
// Allocator configuration
//===----------------------------------------------------------------------===//

IREE_FLAG_LIST(
    string, device_allocator,
    "Specifies one or more HAL device allocator specs to augment the base\n"
    "device allocator. See each allocator type for supported configurations.");

// Configures the |device| allocator based on the --device_allocator= flag.
// This will wrap the underlying device allocator in zero or more configurable
// allocator shims.
//
// WARNING: not thread-safe and must only be called immediately after device
// creation.
static iree_status_t iree_hal_configure_allocator_from_flags(
    iree_hal_device_t* device) {
  const iree_flag_string_list_t list = FLAG_device_allocator_list();
  return iree_hal_configure_allocator_from_specs(list.count, list.values,
                                                 device);
}

//===----------------------------------------------------------------------===//
// Collectives configuration
//===----------------------------------------------------------------------===//

// TODO(multi-device): support more provider types/have a provider registry.
// MPI is insufficient for heterogeneous/multi-device configurations. Currently
// we set the same provider for every device and that'll really confuse things
// as MPI rank/count configuration is global in the environment and not per
// device. Hosting frameworks/runtimes can set their own providers that have
// more meaningful representation of multi-device/multi-node.
iree_status_t iree_hal_device_set_default_channel_provider(
    iree_hal_device_t* device) {
  if (!iree_hal_mpi_is_configured()) return iree_ok_status();
  iree_hal_channel_provider_t* channel_provider = NULL;
  IREE_RETURN_IF_ERROR(
      iree_hal_mpi_channel_provider_create(
          iree_hal_device_host_allocator(device), &channel_provider),
      "creating MPI channel provider as detected in environment");
  iree_hal_device_replace_channel_provider(device, channel_provider);
  iree_hal_channel_provider_release(channel_provider);
  return iree_ok_status();
}

//===----------------------------------------------------------------------===//
// Device selection
//===----------------------------------------------------------------------===//

IREE_FLAG_LIST(
    string, device,
    "Specifies one or more HAL devices to use for execution.\n"
    "Use --list_devices/--dump_devices to see available devices and their\n"
    "canonical URI used with this flag.");

iree_string_view_list_t iree_hal_device_flag_list(void) {
  return FLAG_device_list();
}

iree_status_t iree_hal_create_device_from_flags(
    iree_hal_driver_registry_t* driver_registry,
    iree_string_view_t default_device,
    const iree_hal_device_create_params_t* create_params,
    iree_allocator_t host_allocator, iree_hal_device_t** out_device) {
  IREE_ASSERT_ARGUMENT(create_params);
  iree_hal_device_list_t* device_list = NULL;
  IREE_RETURN_IF_ERROR(iree_hal_create_devices_from_flags(
      driver_registry, default_device, create_params, host_allocator,
      &device_list));
  iree_hal_device_t* device = iree_hal_device_list_at(device_list, 0);
  iree_hal_device_retain(device);
  iree_hal_device_list_free(device_list);
  *out_device = device;
  return iree_ok_status();
}

iree_status_t iree_hal_create_devices_from_flags(
    iree_hal_driver_registry_t* driver_registry,
    iree_string_view_t default_device,
    const iree_hal_device_create_params_t* create_params,
    iree_allocator_t host_allocator, iree_hal_device_list_t** out_device_list) {
  IREE_ASSERT_ARGUMENT(create_params);
  iree_flag_string_list_t flag_list = FLAG_device_list();
  if (flag_list.count == 0) {
    // No devices specified. Use default if provided.
    if (iree_string_view_is_empty(default_device)) {
      return iree_make_status(
          IREE_STATUS_INVALID_ARGUMENT,
          "no devices specified; use --list_devices to see the "
          "available devices and specify one or more with --device=");
    }
    flag_list.count = 1;
    flag_list.values = &default_device;
  }

  IREE_TRACE_ZONE_BEGIN(z0);
  IREE_TRACE({
    for (iree_host_size_t i = 0; i < flag_list.count; ++i) {
      IREE_TRACE_ZONE_APPEND_TEXT(z0, flag_list.values[i].data,
                                  flag_list.values[i].size);
    }
  });

  iree_hal_device_list_t* device_list = NULL;
  IREE_RETURN_AND_END_ZONE_IF_ERROR(
      z0, iree_hal_device_list_allocate(flag_list.count, host_allocator,
                                        &device_list));

  iree_status_t status = iree_ok_status();
  for (iree_host_size_t i = 0; i < flag_list.count; ++i) {
    // Create the device, which may be slow and dynamically load big
    // dependencies (CUDA, Vulkan, etc).
    iree_hal_device_t* device = NULL;
    status = iree_hal_create_device(driver_registry, flag_list.values[i],
                                    create_params, host_allocator, &device);

    // Optionally wrap the base device allocator with caching/pooling.
    // Doing this here satisfies the requirement that no buffers have been
    // allocated yet - if we returned the device without doing this the caller
    // can more easily break the rules.
    if (iree_status_is_ok(status)) {
      status = iree_hal_configure_allocator_from_flags(device);
    }

    // Optionally set a collective channel provider used by devices to
    // initialize their default channels. Hosting libraries or applications can
    // do the same to interface with their own implementations. Note that this
    // currently sets the same provider for all devices.
    if (iree_status_is_ok(status)) {
      status = iree_hal_device_set_default_channel_provider(device);
    }

    // Add the device to the list to retain it for the caller.
    if (iree_status_is_ok(status)) {
      status = iree_hal_device_list_push_back(device_list, device);
    }
    iree_hal_device_release(device);

    if (!iree_status_is_ok(status)) break;
  }

  if (iree_status_is_ok(status)) {
    *out_device_list = device_list;
  } else {
    iree_hal_device_list_free(device_list);
  }
  IREE_TRACE_ZONE_END(z0);
  return status;
}

//===----------------------------------------------------------------------===//
// Profiling
//===----------------------------------------------------------------------===//

IREE_FLAG(
    string, device_profiling_mode, "",
    "HAL device profiling data families as a comma-separated list drawn from\n"
    "['queue-events', 'host-execution', 'device-queue-events',\n"
    "'dispatch-events', 'memory-events', 'device-metrics',\n"
    "'command-region-events', 'counters', 'counter-ranges',\n"
    "'executable-metadata',\n"
    "'executable-traces'] or empty to disable profiling. HAL implementations\n"
    "may require additional flags in order to configure profiling support on\n"
    "their devices. Tooling may force VM-created command buffers to retain\n"
    "metadata for modes that need command/dispatch attribution; leave this\n"
    "empty for production timing runs.");
IREE_FLAG(
    string, device_profiling_output, "",
    "Path for a raw IREE HAL profiling bundle. Required when\n"
    "--device_profiling_mode is nonempty. The output is written by tooling\n"
    "using a generic profile sink.");
IREE_FLAG(
    string, device_profiling_filter_function, "",
    "Optional glob pattern selecting executable function names that should "
    "emit\n"
    "heavy profiling artifacts such as dispatch timings or hardware counter\n"
    "samples. Cheap session metadata remains available so filtered captures\n"
    "can still be decoded.");
IREE_FLAG(
    int64_t, device_profiling_filter_command_buffer, -1,
    "Optional nonzero command buffer id selecting operations that should emit\n"
    "heavy profiling artifacts. Use iree-profile command/queue output from an\n"
    "earlier capture to find command buffer ids.");
IREE_FLAG(
    int64_t, device_profiling_filter_command_index, -1,
    "Optional zero-based command-buffer operation index selecting operations\n"
    "that should emit heavy profiling artifacts.");
IREE_FLAG(
    int64_t, device_profiling_filter_physical_device, -1,
    "Optional physical device ordinal selecting operations that should emit\n"
    "heavy profiling artifacts.");
IREE_FLAG(int64_t, device_profiling_filter_queue, -1,
          "Optional queue ordinal selecting operations that should emit heavy\n"
          "profiling artifacts.");
IREE_FLAG_LIST(
    string, device_profiling_counter,
    "Optional implementation-specific hardware counter name to capture. May "
    "be\n"
    "specified multiple times; the selected HAL driver decides which counter\n"
    "names and combinations are supported. Use "
    "--device_profiling_mode=counters\n"
    "for dispatch-scoped attribution, which may inject packets around "
    "selected\n"
    "dispatches and perturb queue timing. Use\n"
    "--device_profiling_mode=counter-ranges for low-disturbance range "
    "samples.");
IREE_FLAG(
    int64_t, device_profiling_flush_interval_ms, 0,
    "Optional interval in milliseconds for a tooling-owned background thread\n"
    "to call iree_hal_device_profiling_flush while HAL-native profiling is\n"
    "active. 0 disables periodic flushing. The selected HAL backend must\n"
    "support safe in-flight profiling snapshots for the requested data.\n"
    "Sink failures are reported when profiling ends.");
IREE_FLAG(
    bool, print_device_statistics, false,
    "Enables a lightweight HAL profiling session and prints aggregate device\n"
    "statistics at shutdown. This is a command-line convenience path: it does\n"
    "not write a profiling bundle and cannot be combined with\n"
    "--device_profiling_output. When --device_profiling_mode is empty,\n"
    "tooling requests the producer's lightweight execution-statistics mode.");

IREE_FLAG(
    string, device_capture_tool, "",
    "Optional external profiler/tool capture provider such as 'renderdoc' or\n"
    "'metal'. External captures produce provider-specific artifacts and are\n"
    "separate from HAL-native --device_profiling_mode output.");
IREE_FLAG(string, device_capture_file, "",
          "Optional provider-specific external capture output path or path "
          "template.");
IREE_FLAG(string, device_capture_label, "",
          "Optional provider-specific external capture range label.");

struct iree_hal_profiling_from_flags_t {
  // Host allocator used for this session object.
  iree_allocator_t host_allocator;

  // Devices retained while profiling/capture state is active.
  iree_hal_device_t** devices;

  // Number of entries in |devices| and the active-state arrays.
  iree_host_size_t device_count;

  // Per-device true when a nonempty HAL-native profiling session is active.
  bool* native_profile_active;

  // Per-device true when iree_hal_device_external_capture_begin succeeded.
  bool* external_capture_active;

  // Periodic flush interval in milliseconds, or 0 when disabled.
  iree_duration_t flush_interval_ms;

  // Mutex serializing explicit and periodic flush calls.
  iree_slim_mutex_t flush_mutex;

  // Background flush thread owned by the session when periodic flush is active.
  iree_thread_t* flush_thread;

  // In-memory aggregate sink used by --print_device_statistics.
  iree_hal_profile_statistics_sink_t* statistics_sink;

  // Notification used to wake the flush thread during shutdown.
  iree_notification_t flush_notification;

  // Nonzero when the flush thread should stop.
  iree_atomic_int32_t flush_stop_requested;

  // Mutex protecting |flush_status|.
  iree_slim_mutex_t flush_status_mutex;

  // First background flush failure, joined with any later failures.
  iree_status_t flush_status;
};

static iree_status_t iree_hal_profile_capture_filter_set_u32_from_flag(
    int64_t flag_value, iree_hal_profile_capture_filter_flags_t flag,
    const char* flag_name, uint32_t* out_value,
    iree_hal_profile_capture_filter_t* inout_filter) {
  if (flag_value < 0) return iree_ok_status();
  if (flag_value > UINT32_MAX) {
    return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
                            "--%s value exceeds uint32_t", flag_name);
  }
  *out_value = (uint32_t)flag_value;
  inout_filter->flags |= flag;
  return iree_ok_status();
}

static iree_status_t iree_hal_profile_capture_filter_from_flags(
    iree_hal_profile_capture_filter_t* out_filter) {
  *out_filter = iree_hal_profile_capture_filter_default();
  if (strlen(FLAG_device_profiling_filter_function) != 0) {
    out_filter->flags |=
        IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_EXECUTABLE_FUNCTION_PATTERN;
    out_filter->executable_function_pattern =
        iree_make_cstring_view(FLAG_device_profiling_filter_function);
  }
  if (FLAG_device_profiling_filter_command_buffer == 0) {
    return iree_make_status(
        IREE_STATUS_INVALID_ARGUMENT,
        "--device_profiling_filter_command_buffer must be nonzero");
  } else if (FLAG_device_profiling_filter_command_buffer > 0) {
    out_filter->flags |= IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_COMMAND_BUFFER_ID;
    out_filter->command_buffer_id =
        (uint64_t)FLAG_device_profiling_filter_command_buffer;
  }
  IREE_RETURN_IF_ERROR(iree_hal_profile_capture_filter_set_u32_from_flag(
      FLAG_device_profiling_filter_command_index,
      IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_COMMAND_INDEX,
      "device_profiling_filter_command_index", &out_filter->command_index,
      out_filter));
  IREE_RETURN_IF_ERROR(iree_hal_profile_capture_filter_set_u32_from_flag(
      FLAG_device_profiling_filter_physical_device,
      IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_PHYSICAL_DEVICE_ORDINAL,
      "device_profiling_filter_physical_device",
      &out_filter->physical_device_ordinal, out_filter));
  IREE_RETURN_IF_ERROR(iree_hal_profile_capture_filter_set_u32_from_flag(
      FLAG_device_profiling_filter_queue,
      IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_QUEUE_ORDINAL,
      "device_profiling_filter_queue", &out_filter->queue_ordinal, out_filter));
  return iree_ok_status();
}

static iree_status_t iree_hal_profile_sink_create_from_flags(
    iree_allocator_t host_allocator, iree_hal_profile_sink_t** out_sink) {
  IREE_ASSERT_ARGUMENT(out_sink);
  *out_sink = NULL;

  if (strlen(FLAG_device_profiling_output) == 0) return iree_ok_status();

  iree_io_file_handle_t* file_handle = NULL;
  iree_status_t status = iree_io_file_handle_create(
      IREE_IO_FILE_MODE_WRITE | IREE_IO_FILE_MODE_SEQUENTIAL_SCAN |
          IREE_IO_FILE_MODE_SHARE_READ,
      iree_make_cstring_view(FLAG_device_profiling_output),
      /*initial_size=*/0, host_allocator, &file_handle);
  if (iree_status_is_ok(status)) {
    status = iree_hal_profile_file_sink_create(file_handle, host_allocator,
                                               out_sink);
  }
  iree_io_file_handle_release(file_handle);
  return status;
}

static iree_status_t iree_hal_device_profiling_data_families_from_flags(
    iree_hal_device_profiling_data_families_t* out_data_families) {
  *out_data_families = IREE_HAL_DEVICE_PROFILING_DATA_NONE;
  iree_string_view_t remaining =
      iree_string_view_trim(iree_make_cstring_view(FLAG_device_profiling_mode));
  while (remaining.size != 0) {
    iree_string_view_t family_part = iree_string_view_empty();
    iree_string_view_split(remaining, ',', &family_part, &remaining);
    family_part = iree_string_view_trim(family_part);
    if (family_part.size == 0) {
      return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
                              "--device_profiling_mode contains an empty data "
                              "family");
    } else if (iree_string_view_equal(family_part, IREE_SV("queue-events"))) {
      *out_data_families |= IREE_HAL_DEVICE_PROFILING_DATA_QUEUE_EVENTS;
    } else if (iree_string_view_equal(family_part, IREE_SV("host-execution"))) {
      *out_data_families |=
          IREE_HAL_DEVICE_PROFILING_DATA_HOST_EXECUTION_EVENTS;
    } else if (iree_string_view_equal(family_part,
                                      IREE_SV("device-queue-events"))) {
      *out_data_families |= IREE_HAL_DEVICE_PROFILING_DATA_DEVICE_QUEUE_EVENTS;
    } else if (iree_string_view_equal(family_part,
                                      IREE_SV("dispatch-events"))) {
      *out_data_families |= IREE_HAL_DEVICE_PROFILING_DATA_DISPATCH_EVENTS;
    } else if (iree_string_view_equal(family_part, IREE_SV("memory-events"))) {
      *out_data_families |= IREE_HAL_DEVICE_PROFILING_DATA_MEMORY_EVENTS;
    } else if (iree_string_view_equal(family_part, IREE_SV("device-metrics"))) {
      *out_data_families |= IREE_HAL_DEVICE_PROFILING_DATA_DEVICE_METRICS;
    } else if (iree_string_view_equal(family_part,
                                      IREE_SV("command-region-events"))) {
      *out_data_families |=
          IREE_HAL_DEVICE_PROFILING_DATA_COMMAND_REGION_EVENTS;
    } else if (iree_string_view_equal(family_part, IREE_SV("counters"))) {
      *out_data_families |= IREE_HAL_DEVICE_PROFILING_DATA_COUNTER_SAMPLES;
    } else if (iree_string_view_equal(family_part, IREE_SV("counter-ranges")) ||
               iree_string_view_equal(family_part, IREE_SV("pmc-ranges"))) {
      *out_data_families |= IREE_HAL_DEVICE_PROFILING_DATA_COUNTER_RANGES;
    } else if (iree_string_view_equal(family_part,
                                      IREE_SV("executable-metadata"))) {
      *out_data_families |= IREE_HAL_DEVICE_PROFILING_DATA_EXECUTABLE_METADATA;
    } else if (iree_string_view_equal(family_part,
                                      IREE_SV("executable-traces"))) {
      *out_data_families |= IREE_HAL_DEVICE_PROFILING_DATA_EXECUTABLE_TRACES;
    } else {
      return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
                              "unsupported profiling data family '%.*s'",
                              (int)family_part.size, family_part.data);
    }
    remaining = iree_string_view_trim(remaining);
  }
  return iree_ok_status();
}

iree_status_t
iree_hal_profiling_from_flags_requires_retained_command_buffer_metadata(
    bool* out_required) {
  IREE_ASSERT_ARGUMENT(out_required);
  *out_required = false;

  iree_hal_device_profiling_data_families_t data_families =
      IREE_HAL_DEVICE_PROFILING_DATA_NONE;
  IREE_RETURN_IF_ERROR(
      iree_hal_device_profiling_data_families_from_flags(&data_families));

  if (data_families == IREE_HAL_DEVICE_PROFILING_DATA_NONE) {
    *out_required = FLAG_print_device_statistics;
    return iree_ok_status();
  }

  const iree_hal_device_profiling_data_families_t command_buffer_data =
      IREE_HAL_DEVICE_PROFILING_DATA_QUEUE_EVENTS |
      IREE_HAL_DEVICE_PROFILING_DATA_HOST_EXECUTION_EVENTS |
      IREE_HAL_DEVICE_PROFILING_DATA_DEVICE_QUEUE_EVENTS |
      IREE_HAL_DEVICE_PROFILING_DATA_DISPATCH_EVENTS |
      IREE_HAL_DEVICE_PROFILING_DATA_COUNTER_SAMPLES |
      IREE_HAL_DEVICE_PROFILING_DATA_EXECUTABLE_METADATA |
      IREE_HAL_DEVICE_PROFILING_DATA_EXECUTABLE_TRACES |
      IREE_HAL_DEVICE_PROFILING_DATA_COMMAND_REGION_EVENTS;
  *out_required = iree_any_bit_set(data_families, command_buffer_data);
  return iree_ok_status();
}

static bool iree_hal_device_profiling_filter_flags_present(void) {
  return strlen(FLAG_device_profiling_filter_function) != 0 ||
         FLAG_device_profiling_filter_command_buffer >= 0 ||
         FLAG_device_profiling_filter_command_index >= 0 ||
         FLAG_device_profiling_filter_physical_device >= 0 ||
         FLAG_device_profiling_filter_queue >= 0;
}

static bool iree_hal_device_external_capture_flags_present(void) {
  return strlen(FLAG_device_capture_tool) != 0 ||
         strlen(FLAG_device_capture_file) != 0 ||
         strlen(FLAG_device_capture_label) != 0;
}

static iree_status_t iree_hal_device_external_capture_begin_from_flags(
    iree_hal_device_t* device) {
  iree_hal_device_external_capture_options_t options = {0};
  options.provider = iree_make_cstring_view(FLAG_device_capture_tool);
  options.file_path = iree_make_cstring_view(FLAG_device_capture_file);
  options.label = iree_make_cstring_view(FLAG_device_capture_label);
  return iree_hal_device_external_capture_begin(device, &options);
}

static bool iree_hal_profiling_from_flags_any_native_profile_active(
    const iree_hal_profiling_from_flags_t* profiling) {
  for (iree_host_size_t i = 0; i < profiling->device_count; ++i) {
    if (profiling->native_profile_active[i]) return true;
  }
  return false;
}

static bool iree_hal_profiling_from_flags_flush_should_stop(void* arg) {
  iree_hal_profiling_from_flags_t* profiling =
      (iree_hal_profiling_from_flags_t*)arg;
  return iree_atomic_load(&profiling->flush_stop_requested,
                          iree_memory_order_acquire) != 0;
}

static void iree_hal_profiling_from_flags_record_flush_status(
    iree_hal_profiling_from_flags_t* profiling, iree_status_t status) {
  if (iree_status_is_ok(status)) return;
  iree_slim_mutex_lock(&profiling->flush_status_mutex);
  profiling->flush_status = iree_status_join(profiling->flush_status, status);
  iree_slim_mutex_unlock(&profiling->flush_status_mutex);
}

static iree_status_t iree_hal_profiling_from_flags_consume_flush_status(
    iree_hal_profiling_from_flags_t* profiling) {
  iree_slim_mutex_lock(&profiling->flush_status_mutex);
  iree_status_t status = profiling->flush_status;
  profiling->flush_status = iree_ok_status();
  iree_slim_mutex_unlock(&profiling->flush_status_mutex);
  return status;
}

iree_status_t iree_hal_flush_profiling_from_flags(
    iree_hal_profiling_from_flags_t* profiling) {
  if (!profiling ||
      !iree_hal_profiling_from_flags_any_native_profile_active(profiling)) {
    return iree_ok_status();
  }
  iree_slim_mutex_lock(&profiling->flush_mutex);
  iree_status_t status = iree_ok_status();
  for (iree_host_size_t i = 0; i < profiling->device_count; ++i) {
    if (profiling->native_profile_active[i]) {
      status = iree_status_join(
          status, iree_hal_device_profiling_flush(profiling->devices[i]));
    }
  }
  iree_slim_mutex_unlock(&profiling->flush_mutex);
  return status;
}

static int iree_hal_profiling_from_flags_flush_thread_main(void* arg) {
  iree_hal_profiling_from_flags_t* profiling =
      (iree_hal_profiling_from_flags_t*)arg;

  while (!iree_hal_profiling_from_flags_flush_should_stop(profiling)) {
    const bool should_stop = iree_notification_await(
        &profiling->flush_notification,
        iree_hal_profiling_from_flags_flush_should_stop, profiling,
        iree_make_timeout_ms(profiling->flush_interval_ms));
    if (should_stop) break;

    iree_status_t status = iree_hal_flush_profiling_from_flags(profiling);
    if (!iree_status_is_ok(status)) {
      iree_hal_profiling_from_flags_record_flush_status(profiling, status);
      break;
    }
  }

  return 0;
}

static iree_status_t iree_hal_profiling_from_flags_start_periodic_flush(
    iree_hal_profiling_from_flags_t* profiling) {
  if (!iree_hal_profiling_from_flags_any_native_profile_active(profiling) ||
      profiling->flush_interval_ms == 0) {
    return iree_ok_status();
  }

  iree_notification_initialize(&profiling->flush_notification);
  iree_slim_mutex_initialize(&profiling->flush_status_mutex);
  iree_atomic_store(&profiling->flush_stop_requested, 0,
                    iree_memory_order_relaxed);

  const iree_thread_create_params_t thread_params = {
      .name = IREE_SV("iree-hal-profile-flush"),
      .priority_class = IREE_THREAD_PRIORITY_CLASS_LOW,
  };
  iree_status_t status = iree_thread_create(
      iree_hal_profiling_from_flags_flush_thread_main, profiling, thread_params,
      profiling->host_allocator, &profiling->flush_thread);
  if (!iree_status_is_ok(status)) {
    iree_slim_mutex_deinitialize(&profiling->flush_status_mutex);
    iree_notification_deinitialize(&profiling->flush_notification);
  }
  return status;
}

static iree_status_t iree_hal_profiling_from_flags_stop_periodic_flush(
    iree_hal_profiling_from_flags_t* profiling) {
  if (!profiling || !profiling->flush_thread) return iree_ok_status();

  iree_atomic_store(&profiling->flush_stop_requested, 1,
                    iree_memory_order_release);
  iree_notification_post(&profiling->flush_notification, IREE_ALL_WAITERS);
  // Final release joins the thread. Calling iree_thread_join before release
  // would double-join pthread-backed threads, which is undefined behavior.
  iree_thread_release(profiling->flush_thread);
  profiling->flush_thread = NULL;

  iree_status_t status =
      iree_hal_profiling_from_flags_consume_flush_status(profiling);
  iree_slim_mutex_deinitialize(&profiling->flush_status_mutex);
  iree_notification_deinitialize(&profiling->flush_notification);
  return status;
}

static void iree_hal_profiling_from_flags_release_devices(
    iree_hal_profiling_from_flags_t* profiling) {
  if (!profiling) return;
  for (iree_host_size_t i = 0; i < profiling->device_count; ++i) {
    iree_hal_device_release(profiling->devices[i]);
  }
  iree_allocator_free(profiling->host_allocator,
                      profiling->external_capture_active);
  iree_allocator_free(profiling->host_allocator,
                      profiling->native_profile_active);
  iree_allocator_free(profiling->host_allocator, profiling->devices);
}

static iree_status_t iree_hal_begin_device_list_profiling_from_flags(
    iree_host_size_t device_count, iree_hal_device_t* const* devices,
    iree_allocator_t host_allocator,
    iree_hal_profiling_from_flags_t** out_profiling) {
  IREE_ASSERT_ARGUMENT(out_profiling);
  *out_profiling = NULL;
  if (device_count == 0) return iree_ok_status();

  const iree_flag_string_list_t counter_names =
      FLAG_device_profiling_counter_list();
  iree_hal_device_profiling_options_t options = {0};
  IREE_RETURN_IF_ERROR(iree_hal_device_profiling_data_families_from_flags(
      &options.data_families));
  const bool external_capture_requested = strlen(FLAG_device_capture_tool) != 0;
  const bool statistics_requested = FLAG_print_device_statistics;
  if (!external_capture_requested &&
      iree_hal_device_external_capture_flags_present()) {
    return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
                            "--device_capture_file and --device_capture_label "
                            "require --device_capture_tool");
  }
  if (statistics_requested && strlen(FLAG_device_profiling_output) != 0) {
    return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
                            "--print_device_statistics cannot be combined with "
                            "--device_profiling_output");
  }
  if (FLAG_device_profiling_flush_interval_ms < 0) {
    return iree_make_status(
        IREE_STATUS_INVALID_ARGUMENT,
        "--device_profiling_flush_interval_ms must be non-negative");
  }
  if (options.data_families == IREE_HAL_DEVICE_PROFILING_DATA_NONE) {
    if (counter_names.count != 0) {
      return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
                              "--device_profiling_counter requires "
                              "--device_profiling_mode=counters or "
                              "--device_profiling_mode=counter-ranges");
    }
    if (strlen(FLAG_device_profiling_output) != 0) {
      return iree_make_status(
          IREE_STATUS_INVALID_ARGUMENT,
          "--device_profiling_output requires --device_profiling_mode");
    }
    if (iree_hal_device_profiling_filter_flags_present() &&
        !statistics_requested) {
      return iree_make_status(
          IREE_STATUS_INVALID_ARGUMENT,
          "--device_profiling_filter_* requires --device_profiling_mode or "
          "--print_device_statistics");
    }
    if (FLAG_device_profiling_flush_interval_ms != 0 && !statistics_requested) {
      return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
                              "--device_profiling_flush_interval_ms requires "
                              "--device_profiling_mode or "
                              "--print_device_statistics");
    }
    if (statistics_requested) {
      options.flags |= IREE_HAL_DEVICE_PROFILING_FLAG_LIGHTWEIGHT_STATISTICS;
    } else if (!external_capture_requested) {
      return iree_ok_status();
    }
  }
  if (counter_names.count != 0 &&
      !iree_any_bit_set(options.data_families,
                        IREE_HAL_DEVICE_PROFILING_DATA_COUNTER_SAMPLES |
                            IREE_HAL_DEVICE_PROFILING_DATA_COUNTER_RANGES)) {
    return iree_make_status(
        IREE_STATUS_INVALID_ARGUMENT,
        "--device_profiling_counter requires --device_profiling_mode=counters "
        "or --device_profiling_mode=counter-ranges");
  }
  if (options.data_families != IREE_HAL_DEVICE_PROFILING_DATA_NONE &&
      strlen(FLAG_device_profiling_output) == 0 && !statistics_requested) {
    return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
                            "--device_profiling_mode requires "
                            "--device_profiling_output");
  }

  IREE_RETURN_IF_ERROR(
      iree_hal_profile_capture_filter_from_flags(&options.capture_filter));
  iree_hal_profile_counter_set_selection_t counter_set = {0};
  if (counter_names.count != 0) {
    counter_set.counter_name_count = counter_names.count;
    counter_set.counter_names = counter_names.values;
    options.counter_set_count = 1;
    options.counter_sets = &counter_set;
  }

  iree_hal_profiling_from_flags_t* profiling = NULL;
  IREE_RETURN_IF_ERROR(iree_allocator_malloc(host_allocator, sizeof(*profiling),
                                             (void**)&profiling));
  memset(profiling, 0, sizeof(*profiling));
  profiling->host_allocator = host_allocator;
  profiling->flush_interval_ms = FLAG_device_profiling_flush_interval_ms;
  iree_slim_mutex_initialize(&profiling->flush_mutex);

  iree_host_size_t devices_size = 0;
  iree_host_size_t active_size = 0;
  bool allocation_sizes_valid =
      iree_host_size_checked_mul(device_count, sizeof(*profiling->devices),
                                 &devices_size) &&
      iree_host_size_checked_mul(device_count,
                                 sizeof(*profiling->native_profile_active),
                                 &active_size);
  if (IREE_UNLIKELY(!allocation_sizes_valid)) {
    iree_slim_mutex_deinitialize(&profiling->flush_mutex);
    iree_allocator_free(host_allocator, profiling);
    return iree_make_status(IREE_STATUS_OUT_OF_RANGE,
                            "profiling device list is too large");
  }

  iree_status_t status = iree_allocator_malloc(host_allocator, devices_size,
                                               (void**)&profiling->devices);
  if (iree_status_is_ok(status)) {
    status = iree_allocator_malloc(host_allocator, active_size,
                                   (void**)&profiling->native_profile_active);
  }
  if (iree_status_is_ok(status)) {
    status = iree_allocator_malloc(host_allocator, active_size,
                                   (void**)&profiling->external_capture_active);
  }
  if (!iree_status_is_ok(status)) {
    iree_hal_profiling_from_flags_release_devices(profiling);
    iree_slim_mutex_deinitialize(&profiling->flush_mutex);
    iree_allocator_free(host_allocator, profiling);
    return status;
  }
  memset(profiling->devices, 0, devices_size);
  memset(profiling->native_profile_active, 0, active_size);
  memset(profiling->external_capture_active, 0, active_size);
  profiling->device_count = device_count;
  for (iree_host_size_t i = 0; i < device_count; ++i) {
    if (!devices[i]) {
      iree_hal_profiling_from_flags_release_devices(profiling);
      iree_slim_mutex_deinitialize(&profiling->flush_mutex);
      iree_allocator_free(host_allocator, profiling);
      return iree_make_status(IREE_STATUS_INVALID_ARGUMENT,
                              "profiling device list contains a null device");
    }
    profiling->devices[i] = devices[i];
    iree_hal_device_retain(profiling->devices[i]);
  }

  iree_hal_profile_sink_t* sink = NULL;
  iree_hal_profile_statistics_sink_t* statistics_sink = NULL;
  if (statistics_requested) {
    status = iree_hal_profile_statistics_sink_create(host_allocator,
                                                     &statistics_sink);
    if (iree_status_is_ok(status)) {
      sink = iree_hal_profile_statistics_sink_base(statistics_sink);
    }
  } else {
    status = iree_hal_profile_sink_create_from_flags(host_allocator, &sink);
  }
  if (iree_status_is_ok(status)) {
    options.sink = sink;
    for (iree_host_size_t i = 0; i < device_count && iree_status_is_ok(status);
         ++i) {
      status = iree_hal_device_profiling_begin(profiling->devices[i], &options);
      profiling->native_profile_active[i] =
          iree_status_is_ok(status) &&
          (options.data_families != IREE_HAL_DEVICE_PROFILING_DATA_NONE ||
           iree_hal_device_profiling_options_requests_lightweight_statistics(
               &options));
    }
  }
  if (iree_status_is_ok(status) && external_capture_requested) {
    for (iree_host_size_t i = 0; i < device_count && iree_status_is_ok(status);
         ++i) {
      status = iree_hal_device_external_capture_begin_from_flags(
          profiling->devices[i]);
      profiling->external_capture_active[i] = iree_status_is_ok(status);
    }
  }
  if (iree_status_is_ok(status)) {
    status = iree_hal_profiling_from_flags_start_periodic_flush(profiling);
  }
  if (!iree_status_is_ok(status)) {
    status = iree_status_join(
        status, iree_hal_profiling_from_flags_stop_periodic_flush(profiling));
    for (iree_host_size_t i = profiling->device_count; i > 0; --i) {
      if (profiling->external_capture_active[i - 1]) {
        status = iree_status_join(status, iree_hal_device_external_capture_end(
                                              profiling->devices[i - 1]));
      }
    }
    for (iree_host_size_t i = profiling->device_count; i > 0; --i) {
      if (profiling->native_profile_active[i - 1]) {
        status = iree_status_join(
            status, iree_hal_device_profiling_end(profiling->devices[i - 1]));
      }
    }
    iree_hal_profiling_from_flags_release_devices(profiling);
    iree_slim_mutex_deinitialize(&profiling->flush_mutex);
    iree_allocator_free(host_allocator, profiling);
  } else {
    profiling->statistics_sink = statistics_sink;
    statistics_sink = NULL;
    *out_profiling = profiling;
  }
  if (statistics_sink) {
    iree_hal_profile_statistics_sink_release(statistics_sink);
  } else if (!statistics_requested) {
    iree_hal_profile_sink_release(sink);
  }
  return status;
}

iree_status_t iree_hal_begin_profiling_from_flags(
    iree_hal_device_t* device, iree_allocator_t host_allocator,
    iree_hal_profiling_from_flags_t** out_profiling) {
  IREE_ASSERT_ARGUMENT(out_profiling);
  *out_profiling = NULL;
  if (!device) return iree_ok_status();
  return iree_hal_begin_device_list_profiling_from_flags(
      1, &device, host_allocator, out_profiling);
}

iree_status_t iree_hal_begin_device_group_profiling_from_flags(
    iree_hal_device_group_t* device_group, iree_allocator_t host_allocator,
    iree_hal_profiling_from_flags_t** out_profiling) {
  IREE_ASSERT_ARGUMENT(out_profiling);
  *out_profiling = NULL;
  if (!device_group) return iree_ok_status();
  iree_host_size_t device_count =
      iree_hal_device_group_device_count(device_group);
  if (device_count == 0) return iree_ok_status();

  iree_hal_device_t** devices = NULL;
  iree_host_size_t devices_size = 0;
  if (IREE_UNLIKELY(!iree_host_size_checked_mul(device_count, sizeof(*devices),
                                                &devices_size))) {
    return iree_make_status(IREE_STATUS_OUT_OF_RANGE,
                            "profiling device group is too large");
  }
  IREE_RETURN_IF_ERROR(
      iree_allocator_malloc(host_allocator, devices_size, (void**)&devices));
  for (iree_host_size_t i = 0; i < device_count; ++i) {
    devices[i] = iree_hal_device_group_device_at(device_group, i);
  }
  iree_status_t status = iree_hal_begin_device_list_profiling_from_flags(
      device_count, devices, host_allocator, out_profiling);
  iree_allocator_free(host_allocator, devices);
  return status;
}

iree_status_t iree_hal_end_profiling_from_flags(
    iree_hal_profiling_from_flags_t* profiling) {
  if (!profiling) return iree_ok_status();

  iree_status_t status =
      iree_hal_profiling_from_flags_stop_periodic_flush(profiling);
  for (iree_host_size_t i = profiling->device_count; i > 0; --i) {
    if (profiling->external_capture_active[i - 1]) {
      status = iree_status_join(status, iree_hal_device_external_capture_end(
                                            profiling->devices[i - 1]));
    }
  }
  for (iree_host_size_t i = profiling->device_count; i > 0; --i) {
    if (profiling->native_profile_active[i - 1]) {
      status = iree_status_join(
          status, iree_hal_device_profiling_end(profiling->devices[i - 1]));
    }
  }
  if (profiling->statistics_sink) {
    status = iree_status_join(status, iree_hal_profile_statistics_sink_fprint(
                                          stderr, profiling->statistics_sink));
    iree_hal_profile_statistics_sink_release(profiling->statistics_sink);
  }
  iree_hal_profiling_from_flags_release_devices(profiling);
  iree_slim_mutex_deinitialize(&profiling->flush_mutex);
  iree_allocator_free(profiling->host_allocator, profiling);
  return status;
}
