text: #include <stdbool.h>
  #include <linux/kernel.h>
  #include <linux/types.h>
  #include <linux/bitops.h>
  #include <linux/log2.h>
  
  #include "../../util/evlist.h"
  #include "../../util/auxtrace.h"
  #include "../../util/evsel.h"
  
  #define PERF_EVENT_CPUM_SF		0xB0000 /* Event: Basic-sampling */
  #define PERF_EVENT_CPUM_SF_DIAG		0xBD000 /* Event: Combined-sampling */
  #define DEFAULT_AUX_PAGES		128
  #define DEFAULT_FREQ			4000
  
  static void cpumsf_free(struct auxtrace_record *itr)
  {
  	free(itr);
  }
  
  static size_t cpumsf_info_priv_size(struct auxtrace_record *itr __maybe_unused,
  				    struct perf_evlist *evlist __maybe_unused)
  {
  	return 0;
  }
  
  static int
  cpumsf_info_fill(struct auxtrace_record *itr __maybe_unused,
  		 struct perf_session *session __maybe_unused,
  		 struct auxtrace_info_event *auxtrace_info __maybe_unused,
  		 size_t priv_size __maybe_unused)
  {
  	auxtrace_info->type = PERF_AUXTRACE_S390_CPUMSF;
  	return 0;
  }
  
  static unsigned long
  cpumsf_reference(struct auxtrace_record *itr __maybe_unused)
  {
  	return 0;
  }
  
  static int
  cpumsf_recording_options(struct auxtrace_record *ar __maybe_unused,
  			 struct perf_evlist *evlist __maybe_unused,
  			 struct record_opts *opts)
  {
  	unsigned int factor = 1;
  	unsigned int pages;
  
  	opts->full_auxtrace = true;
  
  	/*
  	 * The AUX buffer size should be set properly to avoid
  	 * overflow of samples if it is not set explicitly.
  	 * DEFAULT_AUX_PAGES is an proper size when sampling frequency
  	 * is DEFAULT_FREQ. It is expected to hold about 1/2 second
  	 * of sampling data. The size used for AUX buffer will scale
  	 * according to the specified frequency and DEFAULT_FREQ.
  	 */
  	if (!opts->auxtrace_mmap_pages) {
  		if (opts->user_freq != UINT_MAX)
  			factor = (opts->user_freq + DEFAULT_FREQ
  				  - 1) / DEFAULT_FREQ;
  		pages = DEFAULT_AUX_PAGES * factor;
  		opts->auxtrace_mmap_pages = roundup_pow_of_two(pages);
  	}
  
  	return 0;
  }
  
  static int
  cpumsf_parse_snapshot_options(struct auxtrace_record *itr __maybe_unused,
  			      struct record_opts *opts __maybe_unused,
  			      const char *str __maybe_unused)
  {
  	return 0;
  }
  
  /*
   * auxtrace_record__init is called when perf record
   * check if the event really need auxtrace
   */
  struct auxtrace_record *auxtrace_record__init(struct perf_evlist *evlist,
  					      int *err)
  {
  	struct auxtrace_record *aux;
  	struct perf_evsel *pos;
  	int diagnose = 0;
  
  	*err = 0;
  	if (evlist->nr_entries == 0)
  		return NULL;
  
  	evlist__for_each_entry(evlist, pos) {
  		if (pos->attr.config == PERF_EVENT_CPUM_SF_DIAG) {
  			diagnose = 1;
  			break;
  		}
  	}
  
  	if (!diagnose)
  		return NULL;
  
  	/* sampling in diagnose mode. alloc aux buffer */
  	aux = zalloc(sizeof(*aux));
  	if (aux == NULL) {
  		*err = -ENOMEM;
  		return NULL;
  	}
  
  	aux->parse_snapshot_options = cpumsf_parse_snapshot_options;
  	aux->recording_options = cpumsf_recording_options;
  	aux->info_priv_size = cpumsf_info_priv_size;
  	aux->info_fill = cpumsf_info_fill;
  	aux->free = cpumsf_free;
  	aux->reference = cpumsf_reference;
  
  	return aux;
  }
  