// Copyright 2026 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

#ifndef IREE_HAL_PROFILE_OPTIONS_H_
#define IREE_HAL_PROFILE_OPTIONS_H_

#include <stdbool.h>
#include <stdint.h>
#include <string.h>

#include "iree/base/api.h"

#ifdef __cplusplus
extern "C" {
#endif  // __cplusplus

typedef struct iree_hal_profile_sink_t iree_hal_profile_sink_t;

// Bitfield selecting HAL-native structured profiling data families.
//
// These bits are not mutually-exclusive modes. Each bit requests one family of
// records or artifacts emitted through iree_hal_profile_sink_t. Not all
// implementations support all families.
typedef uint64_t iree_hal_device_profiling_data_families_t;
enum iree_hal_device_profiling_data_family_bits_t {
  IREE_HAL_DEVICE_PROFILING_DATA_NONE = 0u,

  // Host-timestamped queue operation records such as submissions, dependency
  // strategy, and encoded operation counts. Producers may retain these as an
  // aggregate lossy stream and report dropped records with TRUNCATED chunks.
  IREE_HAL_DEVICE_PROFILING_DATA_QUEUE_EVENTS = 1ull << 0,

  // Host-timestamped execution spans for work performed by the host, such as
  // CPU/local dispatch bodies or host-side command buffer replay.
  IREE_HAL_DEVICE_PROFILING_DATA_HOST_EXECUTION_EVENTS = 1ull << 1,

  // Device-timestamped queue operation spans showing when queue-visible work
  // started and completed in the device timestamp domain. These are precise
  // execution timeline records: producers should fail the profiled operation or
  // session when they cannot retain complete selected events.
  IREE_HAL_DEVICE_PROFILING_DATA_DEVICE_QUEUE_EVENTS = 1ull << 2,

  // Device-timestamped dispatch execution events. This does not request
  // hardware/software counter samples by itself. These are precise execution
  // timeline records: producers should fail the profiled operation or session
  // when they cannot retain complete selected events.
  IREE_HAL_DEVICE_PROFILING_DATA_DISPATCH_EVENTS = 1ull << 3,

  // Explicitly selected hardware/software counter samples attributed to
  // individual operations. Requested counters are described by |counter_sets|.
  IREE_HAL_DEVICE_PROFILING_DATA_COUNTER_SAMPLES = 1ull << 4,

  // Executable/code-object/function metadata needed for offline analysis. Some
  // producers also use this as the cheap metadata family for command-buffer
  // records needed to interpret command-index joins. Producers may emit this
  // implicitly when another requested family references executable ids, but
  // this bit lets callers request metadata by itself.
  IREE_HAL_DEVICE_PROFILING_DATA_EXECUTABLE_METADATA = 1ull << 5,

  // Heavyweight executable trace artifacts such as instruction/thread traces
  // for selected dispatches or command-buffer ranges. This can allocate large
  // device buffers and inject additional queue packets and should only be
  // enabled with a narrow capture filter.
  IREE_HAL_DEVICE_PROFILING_DATA_EXECUTABLE_TRACES = 1ull << 6,

  // Memory allocation and reservation lifecycle records. Producers may retain
  // these as an aggregate lossy stream and report dropped records with
  // TRUNCATED chunks.
  IREE_HAL_DEVICE_PROFILING_DATA_MEMORY_EVENTS = 1ull << 7,

  // Periodic physical-device metric samples such as clocks, temperature,
  // power, memory occupancy, utilization, and bandwidth. Producers should emit
  // source and descriptor metadata so profile bundles remain self-describing.
  IREE_HAL_DEVICE_PROFILING_DATA_DEVICE_METRICS = 1ull << 8,

  // Host-timestamped command-buffer execution regions such as cooperative CPU
  // scheduler barrier regions. These describe scheduler-visible structure
  // around groups of command operations; dispatch/kernel execution details stay
  // in dispatch or host-execution event families.
  IREE_HAL_DEVICE_PROFILING_DATA_COMMAND_REGION_EVENTS = 1ull << 9,

  // Explicitly selected hardware/software counter ranges. Requested counters
  // are described by |counter_sets| and are sampled over producer-defined time
  // ranges without requiring operation attribution.
  IREE_HAL_DEVICE_PROFILING_DATA_COUNTER_RANGES = 1ull << 10,
};

// Bitfield selecting producer-side profiling behavior that is not itself a
// durable profile record family.
typedef uint32_t iree_hal_device_profiling_flags_t;
enum iree_hal_device_profiling_flag_bits_t {
  IREE_HAL_DEVICE_PROFILING_FLAG_NONE = 0u,

  // Requests the producer's cheapest useful execution-statistics record set.
  // Producers expand this into ordinary profile chunks such as queue, dispatch,
  // host-execution, and executable metadata records. No statistics-specific
  // profile chunks are emitted.
  IREE_HAL_DEVICE_PROFILING_FLAG_LIGHTWEIGHT_STATISTICS = 1u << 0,
};

// Bitfield specifying profile capture filter predicates.
typedef uint32_t iree_hal_profile_capture_filter_flags_t;
enum iree_hal_profile_capture_filter_flag_bits_t {
  IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_NONE = 0u,

  // Match only executable functions whose names match
  // |executable_function_pattern|.
  IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_EXECUTABLE_FUNCTION_PATTERN = 1u << 0,

  // Match only operations associated with |command_buffer_id|.
  IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_COMMAND_BUFFER_ID = 1u << 1,

  // Match only command-buffer operations whose index is |command_index|. Direct
  // queue operations have no command index and never match this predicate.
  IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_COMMAND_INDEX = 1u << 2,

  // Match only operations on |physical_device_ordinal|.
  IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_PHYSICAL_DEVICE_ORDINAL = 1u << 3,

  // Match only operations on |queue_ordinal|.
  IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_QUEUE_ORDINAL = 1u << 4,
};

// Selects which operations should produce heavy profile artifacts.
//
// Profile producers should always preserve cheap session/metadata records
// needed to interpret the capture, but may use this filter to decide whether to
// emit expensive per-operation artifacts such as dispatch timestamp packets,
// hardware counter ranges, or trace markers. Fields are active only when their
// matching flag is set; a zero-initialized filter matches all operations.
typedef struct iree_hal_profile_capture_filter_t {
  // Flags selecting which fields below participate in matching.
  iree_hal_profile_capture_filter_flags_t flags;

  // Borrowed glob pattern matched with iree_string_view_match_pattern against
  // an executable function name. Profile sessions retaining this filter after
  // begin must copy the pattern into session-owned storage.
  iree_string_view_t executable_function_pattern;

  // Session-local command-buffer identifier to match.
  uint64_t command_buffer_id;

  // Zero-based command-buffer operation index to match.
  uint32_t command_index;

  // Session-local physical device ordinal to match.
  uint32_t physical_device_ordinal;

  // Session-local queue ordinal to match.
  uint32_t queue_ordinal;

  // Reserved for future filter fields; must be zero.
  uint32_t reserved0;
} iree_hal_profile_capture_filter_t;

// Returns a capture filter matching all operations.
static inline iree_hal_profile_capture_filter_t
iree_hal_profile_capture_filter_default(void) {
  iree_hal_profile_capture_filter_t filter;
  memset(&filter, 0, sizeof(filter));
  return filter;
}

// Returns true when |filter| has no active predicates.
static inline bool iree_hal_profile_capture_filter_is_default(
    const iree_hal_profile_capture_filter_t* filter) {
  return filter->flags == IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_NONE;
}

// Returns true if |filter| matches the given profile location fields.
static inline bool iree_hal_profile_capture_filter_matches_location(
    const iree_hal_profile_capture_filter_t* filter, uint64_t command_buffer_id,
    uint32_t command_index, uint32_t physical_device_ordinal,
    uint32_t queue_ordinal) {
  if (iree_any_bit_set(
          filter->flags,
          IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_COMMAND_BUFFER_ID) &&
      filter->command_buffer_id != command_buffer_id) {
    return false;
  }
  if (iree_any_bit_set(filter->flags,
                       IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_COMMAND_INDEX)) {
    if (command_buffer_id == 0 || filter->command_index != command_index) {
      return false;
    }
  }
  if (iree_any_bit_set(
          filter->flags,
          IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_PHYSICAL_DEVICE_ORDINAL) &&
      filter->physical_device_ordinal != physical_device_ordinal) {
    return false;
  }
  if (iree_any_bit_set(filter->flags,
                       IREE_HAL_PROFILE_CAPTURE_FILTER_FLAG_QUEUE_ORDINAL) &&
      filter->queue_ordinal != queue_ordinal) {
    return false;
  }
  return true;
}

// Bitfield specifying properties of a requested hardware counter set.
typedef uint32_t iree_hal_profile_counter_set_selection_flags_t;
enum iree_hal_profile_counter_set_selection_flag_bits_t {
  IREE_HAL_PROFILE_COUNTER_SET_SELECTION_FLAG_NONE = 0u,
};

// Caller-provided hardware counter set selection.
//
// The selection describes one named group of hardware counters requested for a
// profiling session. All pointers are borrowed and must remain valid for the
// duration of iree_hal_device_profiling_begin. A producer that supports the
// selected counters emits one counter-set metadata record, one counter metadata
// record per resolved counter, and counter-sample records using the same
// |counter_set_id|.
typedef struct iree_hal_profile_counter_set_selection_t {
  // Flags controlling counter set selection behavior.
  iree_hal_profile_counter_set_selection_flags_t flags;
  // Human-readable counter set name used in emitted metadata.
  iree_string_view_t name;
  // Number of requested counter names in |counter_names|.
  iree_host_size_t counter_name_count;
  // Borrowed array of requested implementation-specific counter names.
  const iree_string_view_t* counter_names;
} iree_hal_profile_counter_set_selection_t;

// Controls profiling options.
//
// All pointer and string-view fields are borrowed and only need to remain valid
// until iree_hal_device_profiling_begin returns. Implementations that need a
// value after returning success must retain, copy, or resolve it into
// implementation-owned session state before returning.
typedef struct iree_hal_device_profiling_options_t {
  // Flags selecting producer-side profiling behavior.
  iree_hal_device_profiling_flags_t flags;

  // HAL-native structured data families requested by the caller.
  iree_hal_device_profiling_data_families_t data_families;

  // Programmatic sink receiving HAL-native profiling chunks.
  // The caller retains ownership of the sink for the duration of the
  // profiling_begin call. Implementations that keep the sink beyond the call
  // must retain it and release it during profiling_end or teardown.
  iree_hal_profile_sink_t* sink;

  // Optional borrowed filter selecting operations that should emit heavy
  // profile artifacts. A zero-initialized filter matches all operations.
  // Implementations that retain the filter for session matching must copy any
  // string views it contains before returning from profiling_begin.
  iree_hal_profile_capture_filter_t capture_filter;

  // Number of explicitly requested hardware/software counter sets. Must be
  // nonzero when requesting counter samples or counter ranges.
  iree_host_size_t counter_set_count;

  // Borrowed begin-call-only array of explicitly requested counter sets.
  // Implementations must either capture every requested counter set exactly or
  // fail profiling_begin; silently dropping counters would make the profile
  // bundle misleading.
  const iree_hal_profile_counter_set_selection_t* counter_sets;
} iree_hal_device_profiling_options_t;

// Opaque storage backing cloned profiling options.
typedef struct iree_hal_device_profiling_options_storage_t
    iree_hal_device_profiling_options_storage_t;

// Clones |source_options| and all borrowed nested storage into
// |host_allocator|.
//
// |out_options| receives a value type whose pointer/string fields reference
// storage owned by |out_storage|. If |source_options->sink| is non-NULL it is
// retained and will be released by
// iree_hal_device_profiling_options_storage_free. Callers must not separately
// release any pointers in |out_options|.
IREE_API_EXPORT iree_status_t iree_hal_device_profiling_options_clone(
    const iree_hal_device_profiling_options_t* source_options,
    iree_allocator_t host_allocator,
    iree_hal_device_profiling_options_t* out_options,
    iree_hal_device_profiling_options_storage_t** out_storage);

// Frees storage returned by iree_hal_device_profiling_options_clone.
IREE_API_EXPORT void iree_hal_device_profiling_options_storage_free(
    iree_hal_device_profiling_options_storage_t* storage,
    iree_allocator_t host_allocator);

// Returns true when |options| requests any bits in |data_families|.
static inline bool iree_hal_device_profiling_options_requests_data(
    const iree_hal_device_profiling_options_t* options,
    iree_hal_device_profiling_data_families_t data_families) {
  return iree_any_bit_set(options->data_families, data_families);
}

// Returns true when producers should select their lightweight statistics mode.
static inline bool
iree_hal_device_profiling_options_requests_lightweight_statistics(
    const iree_hal_device_profiling_options_t* options) {
  return iree_all_bits_set(
      options->flags, IREE_HAL_DEVICE_PROFILING_FLAG_LIGHTWEIGHT_STATISTICS);
}

// Returns true when |options| requests operation-attributed counter samples.
static inline bool iree_hal_device_profiling_options_requests_counter_samples(
    const iree_hal_device_profiling_options_t* options) {
  return iree_hal_device_profiling_options_requests_data(
      options, IREE_HAL_DEVICE_PROFILING_DATA_COUNTER_SAMPLES);
}

// Returns true when |options| requests range-scoped counter samples.
static inline bool iree_hal_device_profiling_options_requests_counter_ranges(
    const iree_hal_device_profiling_options_t* options) {
  return iree_hal_device_profiling_options_requests_data(
      options, IREE_HAL_DEVICE_PROFILING_DATA_COUNTER_RANGES);
}

// Returns true when |options| requests any explicit counter capture.
static inline bool iree_hal_device_profiling_options_requests_counters(
    const iree_hal_device_profiling_options_t* options) {
  return iree_hal_device_profiling_options_requests_data(
      options, IREE_HAL_DEVICE_PROFILING_DATA_COUNTER_SAMPLES |
                   IREE_HAL_DEVICE_PROFILING_DATA_COUNTER_RANGES);
}

// Returns true when |options| requests executable trace artifacts.
static inline bool iree_hal_device_profiling_options_requests_executable_traces(
    const iree_hal_device_profiling_options_t* options) {
  return iree_hal_device_profiling_options_requests_data(
      options, IREE_HAL_DEVICE_PROFILING_DATA_EXECUTABLE_TRACES);
}

// Returns true when |options| requests periodic device metrics.
static inline bool iree_hal_device_profiling_options_requests_device_metrics(
    const iree_hal_device_profiling_options_t* options) {
  return iree_hal_device_profiling_options_requests_data(
      options, IREE_HAL_DEVICE_PROFILING_DATA_DEVICE_METRICS);
}

#ifdef __cplusplus
}  // extern "C"
#endif  // __cplusplus

#endif  // IREE_HAL_PROFILE_OPTIONS_H_
