/*
 * Copyright (C) 2017-2025 Intel Corporation
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  * Neither the name of Intel Corporation nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef LIBIPT_SB_H
#define LIBIPT_SB_H

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>

#include "intel-pt.h"

#ifdef __cplusplus
extern "C" {
#endif

/* A macro to mark functions as exported. */
#ifndef pt_sb_export
#  if defined(__GNUC__)
#    define pt_sb_export __attribute__((visibility("default")))
#  elif defined(_MSC_VER)
#    define pt_sb_export __declspec(dllimport)
#  else
#    error "unknown compiler"
#  endif
#endif


/** The header version. */
#define LIBIPT_SB_VERSION_MAJOR ${PT_VERSION_MAJOR}
#define LIBIPT_SB_VERSION_MINOR ${PT_VERSION_MINOR}
#define LIBIPT_SB_VERSION_PATCH ${PT_VERSION_PATCH}

#define LIBIPT_SB_VERSION ((LIBIPT_SB_VERSION_MAJOR << 8) +	\
			   LIBIPT_SB_VERSION_MINOR)


/* Sideband decode errors and warnings. */
enum pt_sb_error_code {
	/* No error.  Everything is OK. */
	ptse_ok,

	/* Sideband records have been lost. */
	ptse_lost,

	/* Trace has been lost. */
	ptse_trace_lost,

	/* An image section has been lost (ignored). */
	ptse_section_lost
};

/** Return a human readable error string. */
extern pt_sb_export const char *pt_sb_errstr(enum pt_sb_error_code);


/* An Intel(R) Processor Trace (Intel PT) sideband tracing session.
 *
 * The session serves one Intel PT decoder.
 *
 * It is not thread-safe.  It doesn't need to be.  If a trace stream is decoded
 * by multiple decoders in parallel, each decoder needs its own sideband tracing
 * session since each decoder will be looking at the trace at a different point
 * in time and may see a different memory image.
 *
 * A sideband tracing session contains all sideband decoders that are relevant
 * for that trace stream.  We distinguish primary and secondary sideband
 * channels:
 *
 * - primary sideband channels affect decode directly.
 *
 *     They actively change the Intel PT decoder's memory image on context
 *     switch sideband records.
 *
 *     For per-cpu trace decode, for example, the sideband channel of the cpu
 *     for which trace is being decoded is a primary sideband channel.
 *
 * - secondary sideband channels affect decode indirectly.
 *
 *     They maintain the memory image for different process contexts but do not
 *     actively switch the Intel PT decoder's memory image.  They typically
 *     ignore context switch sideband records.
 *
 *     They may still directly affect the Intel PT decoder's memory image by
 *     adding new sections while trace in that context is being decoded.
 *
 *     For per-cpu trace decode, for example, the sideband channels of other
 *     cpus are secondary sideband channels.
 */
struct pt_sb_session;

/* Allocate a tracing session.
 *
 * If @iscache is not NULL, it will be used for allocating new image sections.
 *
 * It is highly recommended to use an image section cache and to use the same
 * cache for related tracing sessions, e.g. for all cpus in a per-cpu trace.
 *
 * Returns a pointer to the new tracing session or NULL if out of memory.
 */
extern pt_sb_export struct pt_sb_session *
pt_sb_alloc(struct pt_image_section_cache *iscache);

/* Free a tracing session.
 *
 * Also frees all sideband decoders and memory images contained in @session.
 */
extern pt_sb_export void pt_sb_free(struct pt_sb_session *session);

/* Get the image section cache.
 *
 * Returns @session's image section cache provided at pt_sb_alloc().
 */
extern pt_sb_export struct pt_image_section_cache *
pt_sb_iscache(struct pt_sb_session *session);

/* Get the kernel image.
 *
 * Returns a non-NULL image for the Operating System in @session.
 *
 * It is not clear, yet, how virtualization will be handled.
 *
 * The returned image will be freed when @session is freed with a call to
 * pt_sb_free().
 */
extern pt_sb_export struct pt_image *
pt_sb_kernel_image(struct pt_sb_session *session);

/* A sideband decode error/warning notifier.
 *
 * It will be called by sideband decoders to report @errcode encountered while
 * processing sideband at @offset in @filename.  Fatal errors will further cause
 * the sideband decoder to be removed.  Non-fatal errors and warnings will
 * otherwise be ignored.
 *
 * Positive @errcode numbers are enum pt_sb_error_code constants.
 * Negative @errcode numbers are enum pt_error_code constants.
 *
 * It shall return zero on success, a negative pt_error_code otherwise.
 */
typedef int (pt_sb_error_notifier_t)(int errcode,  const char *filename,
				     uint64_t offset, void *priv);

/* Install an error notifier.
 *
 * If @notifier is not NULL, will be called on errors and warnings encountered
 * by sideband decoders.
 *
 * Returns the previously installed notifier or NULL.
 */
extern pt_sb_export pt_sb_error_notifier_t *
pt_sb_notify_error(struct pt_sb_session *session,
		   pt_sb_error_notifier_t *notifier, void *priv);

/* Initialize newly added decoders.
 *
 * Initialize decoders that have been added since pt_sb_alloc() or since the
 * last pt_sb_init_decoders() call by fetching their first sideband record.
 *
 * Returns zero on success, a negative pt_error_code otherwise.
 */
extern pt_sb_export int pt_sb_init_decoders(struct pt_sb_session *session);

/* Apply an event to all sideband decoders contained in a session.
 *
 * Applies @event to all decoders in @session.  This may involve a series of
 * @apply and subsequent @fetch calls.  See comments on @apply and @fetch for
 * details.
 *
 * Decoders that return an error will be removed from @session and freed.
 *
 * Primary decoders are offered @image and may change it to point to a new
 * memory image.
 *
 * For debugging purposes, decoders are also asked to @print the current record
 * to @stream according to @flags.  Pass a NULL @stream to ask decoders to not
 * print anything.
 *
 * Returns zero on success, a negative pt_error_code otherwise.
 */
extern pt_sb_export int pt_sb_event(struct pt_sb_session *session,
				    struct pt_image **image,
				    const struct pt_event *event, size_t size,
				    FILE *stream, uint32_t flags);

/* Dump sideband records up to a given timestamp.
 *
 * Asks all sideband decoders in @session to @print their current record to
 * @stream according to @flags and @fetch the next record as long as the current
 * record's timestamp is smaller or equal to @tsc.
 *
 * Decoders that return an error will be removed from @session and freed.
 *
 * Returns zero on success, a negative pt_error_code otherwise.
 */
extern pt_sb_export int pt_sb_dump(struct pt_sb_session *session, FILE *stream,
				   uint32_t flags, uint64_t tsc);


/* A process context.
 *
 * We maintain a separate image per process so we can switch between them
 * easily.  Each image contains both user-space and kernel-space.
 *
 * Image sections are shared between processes using an image section cache.
 *
 * Process contexts are not thread-safe.  The process memory image changes over
 * time depending on sideband information.  Sections of trace between process
 * image changes can be decoded in parallel but threads will need to synchronize
 * across process image changes.
 */
struct pt_sb_context;

/* Get a context reference.
 *
 * Increment @context's use count.
 */
extern pt_sb_export int pt_sb_ctx_get(struct pt_sb_context *context);

/* Put a context reference.
 *
 * Decrement @context's use count and free @context when it reaches zero.
 */
extern pt_sb_export int pt_sb_ctx_put(struct pt_sb_context *context);

/* Get the context's memory image.
 *
 * The caller must hold a reference to @context as long as the image is used.
 *
 * Returns a non-NULL memory image for @context.
 */
extern pt_sb_export struct pt_image *
pt_sb_ctx_image(const struct pt_sb_context *context);

/* Map a file section into a context's image.
 *
 * Adds a section of @size bytes from @filename starting at @offset to @context's
 * image at @vaddr.
 *
 * Returns zero on success, a negative pt_error_code otherwise.
 */
extern pt_sb_export int pt_sb_ctx_mmap(struct pt_sb_session *session,
				       struct pt_sb_context *context,
				       const char *filename, uint64_t offset,
				       uint64_t size, uint64_t vaddr);

/* Switch to context's image.
 *
 * Install @context->image in @image.  The caller is responsible for holding a
 * reference to @context as long as its image is in use.
 *
 * Returns zero on success, a negative pt_error_code otherwise.
 */
extern pt_sb_export int
pt_sb_ctx_switch_to(struct pt_image **image, struct pt_sb_session *session,
		    const struct pt_sb_context *context);

/* A context switch notifier.
 *
 * It shall return zero on success, a negative pt_error_code otherwise.
 */
typedef int (pt_sb_ctx_switch_notifier_t)(const struct pt_sb_context *,
					  void *priv);

/* Install a context-switch notifier.
 *
 * If @notifier is not NULL, will be called with the switched-to context on a
 * context switch via pt_sb_ctx_switch_to().
 *
 * Returns the previously installed notifier or NULL.
 */
extern pt_sb_export pt_sb_ctx_switch_notifier_t *
pt_sb_notify_switch(struct pt_sb_session *session,
		    pt_sb_ctx_switch_notifier_t *notifier, void *priv);

/* Get the context for pid.
 *
 * Provide a non-NULL process context for @pid in @context.  This may create a
 * new context if no context for @pid exists in @session.  The new context is
 * populated with kernel image sections.
 *
 * This does not provide a new reference to @context.  Use pt_sb_ctx_get() if
 * you need to keep the context.
 *
 * Returns zero on success, a negative pt_error_code otherwise.
 */
extern pt_sb_export int
pt_sb_get_context_by_pid(struct pt_sb_context **context,
			 struct pt_sb_session *session, uint32_t pid);

/* Find a context by pid.
 *
 * Provide a non-NULL process context for @pid in @context if it exists in
 * @session.  This does not provide a new reference to @context.  Use
 * pt_sb_ctx_get() if you need to keep the context.
 *
 * Provide a NULL process context in @context if a context for @pid does not
 * exist in @session.
 *
 * Returns zero on success, a negative pt_error_code otherwise.
 */
extern pt_sb_export int
pt_sb_find_context_by_pid(struct pt_sb_context **context,
			  struct pt_sb_session *session, uint32_t pid);

/* Remove a context.
 *
 * Removes @context from @session and puts @session's reference to @context.
 * Future lookups won't find @context but it won't be freed until the last user
 * puts it.
 *
 * Returns zero on success, a negative pt_error_code otherwise.
 */
extern pt_sb_export int pt_sb_remove_context(struct pt_sb_session *session,
					     struct pt_sb_context *context);


/* A collection of print options. */
enum pt_sb_print_flag {
	/* Print sideband records in compact mode. */
	ptsbp_compact		= 1 << 0,

	/* Print sideband records in verbose mode. */
	ptsbp_verbose		= 1 << 1,

	/* Print the sideband filename. */
	ptsbp_filename		= 1 << 2,

	/* Print the offset into the sideband file. */
	ptsbp_file_offset	= 1 << 3,

	/* Print the sideband record's timestamp. */
	ptsbp_tsc		= 1 << 4

};

/* An Intel PT sideband decoder configuration. */
struct pt_sb_decoder_config {
	/* The size of the config structure in bytes. */
	size_t size;

	/* Fetch the next sideband record and provide its timestamp.
	 *
	 * Return zero on success, a negative pt_error_code otherwise.
	 */
	int (*fetch)(struct pt_sb_session *session, uint64_t *tsc, void *priv);

	/* Apply the current sideband record.
	 *
	 * For master sideband channels, @image will be non-NULL and point to
	 * the image object that is currently used.  If the image shall be
	 * switched, set @image to the new image to be used.
	 *
	 * For secondary sideband channels, @image will be NULL.
	 *
	 * The @event argument points to a pt_event object.  Unknown event types
	 * shall be ignored.
	 *
	 * Initially, it will be passed to sideband decoders in the order of
	 * their next record's timestamp.  It must only be applied to the
	 * current sideband record.
	 *
	 * If the record's timestamp is smaller or equal to the event's
	 * timestamp, @fetch will be called to fetch the next sideband record,
	 * and @apply will be called again for the new sideband record with the
	 * same @event.
	 *
	 * This repeats until the @event's timestamp is smaller than the current
	 * record's timestamp.
	 *
	 * The event will then be passed to all sideband decoders irrespective
	 * of their next record's timestamp.  This allows sideband decoders to
	 * postpone actions until a suitable event.
	 *
	 * Return zero on success, a negative pt_error_code otherwise.
	 */
	int (*apply)(struct pt_sb_session *session, struct pt_image **image,
		     const struct pt_event *event, void *priv);

	/* Print the current sideband record.
	 *
	 * The output shall be determined based on @flags, which is a bit-vector
	 * of enum pt_sb_print_flag.  A value of zero means that only errors
	 * shall be printed.
	 *
	 * Return zero on success, a negative pt_error_code otherwise.
	 */
	int (*print)(struct pt_sb_session *session, FILE *stream,
		     uint32_t flags, void *priv);

	/* Destroy the private data. */
	void (*dtor)(void *priv);

	/* Decoder-specific private data. */
	void *priv;

	/* A collection of configuration flags saying:
	 *
	 * - whether this is a primary decoder (secondary if clear).
	 */
	uint32_t primary:1;
};

/* Add an Intel PT sideband decoder.
 *
 * Allocate a new sideband decoder based on @config and add it to @session.
 *
 * The sideband decoder will automatically be freed when @session is freed with
 * a call to pt_sb_free() or when it is removed from @session after returning an
 * error from one of its callback functions.
 *
 * Returns zero on success, a negative pt_error_code otherwise.
 */
extern pt_sb_export int
pt_sb_alloc_decoder(struct pt_sb_session *session,
		    const struct pt_sb_decoder_config *config);


#if (LIBIPT_SB_VERSION >= 0x201)

/* A perf event sample type. */
struct pev_sample_type {
	/* The sample identifier.
	 *
	 * This corresponds to the PERF_SAMPLE_IDENTIFIER sample that can be
	 * found at the very end of the event record.
	 */
	uint64_t identifier;

	/* The sample type.
	 *
	 * At least PERF_SAMPLE_IDENTIFIER must be set.
	 */
	uint64_t sample_type;
};

/* A perf event sample configuration. */
struct pev_sample_config {
	/* The number of sample types in this configuration. */
	uint8_t nstypes;

	/* An array of \@nstypes sample types. */
	struct pev_sample_type stypes[];
};

#endif /* (LIBIPT_SB_VERSION >= 0x201) */

/* The configuration for a Linux perf event sideband decoder. */
struct pt_sb_pevent_config {
	/* The size of the config structure in bytes. */
	size_t size;

	/* The name of the file containing the sideband data. */
	const char *filename;

	/* The offset into the file from which to start reading. */
	size_t begin;

	/* The optional end offset into the file at which to stop reading.
	 *
	 * Zero means read until the end of the file.
	 */
	size_t end;

	/* The optional system root directory.
	 *
	 * If not NULL, this is prepended to every filename referenced in perf
	 * event sideband records.
	 */
	const char *sysroot;

	/* The optional 64-bit vdso. */
	const char *vdso_x64;

	/* The optional x32 vdso. */
	const char *vdso_x32;

	/* The optional 32-bit vdso. */
	const char *vdso_ia32;

	/* An offset to be subtracted from every perf event record timestamp.
	 *
	 * This applies perf event records a little bit earlier to compensate
	 * for too coarse timing.
	 */
	uint64_t tsc_offset;

	/* The respective field in struct perf_event_attr.
	 *
	 * We require sample_id_all in struct perf_event_attr to be set.
	 *
	 * This field is only valid if \@sample_config is NULL.
	 */
	uint64_t sample_type;

	/* The start address of the kernel.
	 *
	 * This is used to distinguish kernel from user addresses:
	 *
	 *   kernel >= @kernel_start
	 *   user   <  @kernel_start
	 *
	 * Set to UINT64_MAX if ring-0 is not being traced.
	 */
	uint64_t kernel_start;

	/* The respective fields in struct perf_event_mmap_page. */
	uint16_t time_shift;
	uint32_t time_mult;
	uint64_t time_zero;

	/* A collection of configuration flags saying:
	 *
	 * - whether this is a primary decoder (secondary if clear).
	 */
	uint32_t primary:1;

#if (LIBIPT_SB_VERSION >= 0x201)
	/* The sample configuration. */
	struct pev_sample_config *sample_config;
#endif
};

/* Allocate a Linux perf event sideband decoder.
 *
 * Allocates a sideband decoder for the Linux perf event format based on @config
 * and adds it to @session.
 *
 * Returns zero on success, a negative pt_error_code otherwise.
 */
extern pt_sb_export int
pt_sb_alloc_pevent_decoder(struct pt_sb_session *session,
			   const struct pt_sb_pevent_config *config);

#ifdef __cplusplus
}
#endif

#endif /* LIBIPT_SB_H */
