/*
 * builtin-record.c
 *
 * Builtin record command: Record the profile of a workload
 * (or a CPU, or a PID) into the perf.data output file - for
 * later analysis via perf report.
 */
#include "builtin.h"

#include "perf.h"

#include "util/build-id.h"
#include "util/util.h"
#include "util/parse-options.h"
#include "util/parse-events.h"

#include "util/callchain.h"
#include "util/cgroup.h"
#include "util/header.h"
#include "util/event.h"
#include "util/evlist.h"
#include "util/evsel.h"
#include "util/debug.h"
#include "util/session.h"
#include "util/tool.h"
#include "util/symbol.h"
#include "util/cpumap.h"
#include "util/thread_map.h"
#include "util/data.h"
#include "util/perf_regs.h"
#include "util/auxtrace.h"
#include "util/parse-branch-options.h"
#include "util/parse-regs-options.h"
#include "util/llvm-utils.h"

#include <unistd.h>
#include <sched.h>
#include <sys/mman.h>


struct record {
	struct perf_tool	tool;
	struct record_opts	opts;
	u64			bytes_written;
	struct perf_data_file	file;
	struct auxtrace_record	*itr;
	struct perf_evlist	*evlist;
	struct perf_session	*session;
	const char		*progname;
	int			realtime_prio;
	bool			no_buildid;
	bool			no_buildid_cache;
	unsigned long long	samples;
};

static int record__write(struct record *rec, void *bf, size_t size)
{
	if (perf_data_file__write(rec->session->file, bf, size) < 0) {
		pr_err("failed to write perf data, error: %m\n");
		return -1;
	}

	rec->bytes_written += size;
	return 0;
}

static int process_synthesized_event(struct perf_tool *tool,
				     union perf_event *event,
				     struct perf_sample *sample __maybe_unused,
				     struct machine *machine __maybe_unused)
{
	struct record *rec = container_of(tool, struct record, tool);
	return record__write(rec, event, event->header.size);
}

static int record__mmap_read(struct record *rec, int idx)
{
	struct perf_mmap *md = &rec->evlist->mmap[idx];
	u64 head = perf_mmap__read_head(md);
	u64 old = md->prev;
	unsigned char *data = md->base + page_size;
	unsigned long size;
	void *buf;
	int rc = 0;

	if (old == head)
		return 0;

	rec->samples++;

	size = head - old;

	if ((old & md->mask) + size != (head & md->mask)) {
		buf = &data[old & md->mask];
		size = md->mask + 1 - (old & md->mask);
		old += size;

		if (record__write(rec, buf, size) < 0) {
			rc = -1;
			goto out;
		}
	}

	buf = &data[old & md->mask];
	size = head - old;
	old += size;

	if (record__write(rec, buf, size) < 0) {
		rc = -1;
		goto out;
	}

	md->prev = old;
	perf_evlist__mmap_consume(rec->evlist, idx);
out:
	return rc;
}

static volatile int done;
static volatile int signr = -1;
static volatile int child_finished;
static volatile int auxtrace_snapshot_enabled;
static volatile int auxtrace_snapshot_err;
static volatile int auxtrace_record__snapshot_started;

static void sig_handler(int sig)
{
	if (sig == SIGCHLD)
		child_finished = 1;
	else
		signr = sig;

	done = 1;
}

static void record__sig_exit(void)
{
	if (signr == -1)
		return;

	signal(signr, SIG_DFL);
	raise(signr);
}

#ifdef HAVE_AUXTRACE_SUPPORT

static int record__process_auxtrace(struct perf_tool *tool,
				    union perf_event *event, void *data1,
				    size_t len1, void *data2, size_t len2)
{
	struct record *rec = container_of(tool, struct record, tool);
	struct perf_data_file *file = &rec->file;
	size_t padding;
	u8 pad[8] = {0};

	if (!perf_data_file__is_pipe(file)) {
		off_t file_offset;
		int fd = perf_data_file__fd(file);
		int err;

		file_offset = lseek(fd, 0, SEEK_CUR);
		if (file_offset == -1)
			return -1;
		err = auxtrace_index__auxtrace_event(&rec->session->auxtrace_index,
						     event, file_offset);
		if (err)
			return err;
	}

	/* event.auxtrace.size includes padding, see __auxtrace_mmap__read() */
	padding = (len1 + len2) & 7;
	if (padding)
		padding = 8 - padding;

	record__write(rec, event, event->header.size);
	record__write(rec, data1, len1);
	if (len2)
		record__write(rec, data2, len2);
	record__write(rec, &pad, padding);

	return 0;
}

static int record__auxtrace_mmap_read(struct record *rec,
				      struct auxtrace_mmap *mm)
{
	int ret;

	ret = auxtrace_mmap__read(mm, rec->itr, &rec->tool,
				  record__process_auxtrace);
	if (ret < 0)
		return ret;

	if (ret)
		rec->samples++;

	return 0;
}

static int record__auxtrace_mmap_read_snapshot(struct record *rec,
					       struct auxtrace_mmap *mm)
{
	int ret;

	ret = auxtrace_mmap__read_snapshot(mm, rec->itr, &rec->tool,
					   record__process_auxtrace,
					   rec->opts.auxtrace_snapshot_size);
	if (ret < 0)
		return ret;

	if (ret)
		rec->samples++;

	return 0;
}

static int record__auxtrace_read_snapshot_all(struct record *rec)
{
	int i;
	int rc = 0;

	for (i = 0; i < rec->evlist->nr_mmaps; i++) {
		struct auxtrace_mmap *mm =
				&rec->evlist->mmap[i].auxtrace_mmap;

		if (!mm->base)
			continue;

		if (record__auxtrace_mmap_read_snapshot(rec, mm) != 0) {
			rc = -1;
			goto out;
		}
	}
out:
	return rc;
}

static void record__read_auxtrace_snapshot(struct record *rec)
{
	pr_debug("Recording AUX area tracing snapshot\n");
	if (record__auxtrace_read_snapshot_all(rec) < 0) {
		auxtrace_snapshot_err = -1;
	} else {
		auxtrace_snapshot_err = auxtrace_record__snapshot_finish(rec->itr);
		if (!auxtrace_snapshot_err)
			auxtrace_snapshot_enabled = 1;
	}
}

#else

static inline
int record__auxtrace_mmap_read(struct record *rec __maybe_unused,
			       struct auxtrace_mmap *mm __maybe_unused)
{
	return 0;
}

static inline
void record__read_auxtrace_snapshot(struct record *rec __maybe_unused)
{
}

static inline
int auxtrace_record__snapshot_start(struct auxtrace_record *itr __maybe_unused)
{
	return 0;
}

#endif

static int record__open(struct record *rec)
{
	char msg[512];
	struct perf_evsel *pos;
	struct perf_evlist *evlist = rec->evlist;
	struct perf_session *session = rec->session;
	struct record_opts *opts = &rec->opts;
	int rc = 0;

	perf_evlist__config(evlist, opts);

	evlist__for_each(evlist, pos) {
try_again:
		if (perf_evsel__open(pos, pos->cpus, pos->threads) < 0) {
			if (perf_evsel__fallback(pos, errno, msg, sizeof(msg))) {
				if (verbose)
					ui__warning("%s\n", msg);
				goto try_again;
			}

			rc = -errno;
			perf_evsel__open_strerror(pos, &opts->target,
						  errno, msg, sizeof(msg));
			ui__error("%s\n", msg);
			goto out;
		}
	}

	if (perf_evlist__apply_filters(evlist, &pos)) {
		error("failed to set filter \"%s\" on event %s with %d (%s)\n",
			pos->filter, perf_evsel__name(pos), errno,
			strerror_r(errno, msg, sizeof(msg)));
		rc = -1;
		goto out;
	}

	if (perf_evlist__mmap_ex(evlist, opts->mmap_pages, false,
				 opts->auxtrace_mmap_pages,
				 opts->auxtrace_snapshot_mode) < 0) {
		if (errno == EPERM) {
			pr_err("Permission error mapping pages.\n"
			       "Consider increasing "
			       "/proc/sys/kernel/perf_event_mlock_kb,\n"
			       "or try again with a smaller value of -m/--mmap_pages.\n"
			       "(current value: %u,%u)\n",
			       opts->mmap_pages, opts->auxtrace_mmap_pages);
			rc = -errno;
		} else {
			pr_err("failed to mmap with %d (%s)\n", errno,
				strerror_r(errno, msg, sizeof(msg)));
			rc = -errno;
		}
		goto out;
	}

	session->evlist = evlist;
	perf_session__set_id_hdr_size(session);
out:
	return rc;
}

static int process_sample_event(struct perf_tool *tool,
				union perf_event *event,
				struct perf_sample *sample,
				struct perf_evsel *evsel,
				struct machine *machine)
{
	struct record *rec = container_of(tool, struct record, tool);

	rec->samples++;

	return build_id__mark_dso_hit(tool, event, sample, evsel, machine);
}

static int process_buildids(struct record *rec)
{
	struct perf_data_file *file  = &rec->file;
	struct perf_session *session = rec->session;

	if (file->size == 0)
		return 0;

	/*
	 * During this process, it'll load kernel map and replace the
	 * dso->long_name to a real pathname it found.  In this case
	 * we prefer the vmlinux path like
	 *   /lib/modules/3.16.4/build/vmlinux
	 *
	 * rather than build-id path (in debug directory).
	 *   $HOME/.debug/.build-id/f0/6e17aa50adf4d00b88925e03775de107611551
	 */
	symbol_conf.ignore_vmlinux_buildid = true;

	return perf_session__process_events(session);
}

static void perf_event__synthesize_guest_os(struct machine *machine, void *data)
{
	int err;
	struct perf_tool *tool = data;
	/*
	 *As for guest kernel when processing subcommand record&report,
	 *we arrange module mmap prior to guest kernel mmap and trigger
	 *a preload dso because default guest module symbols are loaded
	 *from guest kallsyms instead of /lib/modules/XXX/XXX. This
	 *method is used to avoid symbol missing when the first addr is
	 *in module instead of in guest kernel.
	 */
	err = perf_event__synthesize_modules(tool, process_synthesized_event,
					     machine);
	if (err < 0)
		pr_err("Couldn't record guest kernel [%d]'s reference"
		       " relocation symbol.\n", machine->pid);

	/*
	 * We use _stext for guest kernel because guest kernel's /proc/kallsyms
	 * have no _text sometimes.
	 */
	err = perf_event__synthesize_kernel_mmap(tool, process_synthesized_event,
						 machine);
	if (err < 0)
		pr_err("Couldn't record guest kernel [%d]'s reference"
		       " relocation symbol.\n", machine->pid);
}

static struct perf_event_header finished_round_event = {
	.size = sizeof(struct perf_event_header),
	.type = PERF_RECORD_FINISHED_ROUND,
};

static int record__mmap_read_all(struct record *rec)
{
	u64 bytes_written = rec->bytes_written;
	int i;
	int rc = 0;

	for (i = 0; i < rec->evlist->nr_mmaps; i++) {
		struct auxtrace_mmap *mm = &rec->evlist->mmap[i].auxtrace_mmap;

		if (rec->evlist->mmap[i].base) {
			if (record__mmap_read(rec, i) != 0) {
				rc = -1;
				goto out;
			}
		}

		if (mm->base && !rec->opts.auxtrace_snapshot_mode &&
		    record__auxtrace_mmap_read(rec, mm) != 0) {
			rc = -1;
			goto out;
		}
	}

	/*
	 * Mark the round finished in case we wrote
	 * at least one event.
	 */
	if (bytes_written != rec->bytes_written)
		rc = record__write(rec, &finished_round_event, sizeof(finished_round_event));

out:
	return rc;
}

static void record__init_features(struct record *rec)
{
	struct perf_session *session = rec->session;
	int feat;

	for (feat = HEADER_FIRST_FEATURE; feat < HEADER_LAST_FEATURE; feat++)
		perf_header__set_feat(&session->header, feat);

	if (rec->no_buildid)
		perf_header__clear_feat(&session->header, HEADER_BUILD_ID);

	if (!have_tracepoints(&rec->evlist->entries))
		perf_header__clear_feat(&session->header, HEADER_TRACING_DATA);

	if (!rec->opts.branch_stack)
		perf_header__clear_feat(&session->header, HEADER_BRANCH_STACK);

	if (!rec->opts.full_auxtrace)
		perf_header__clear_feat(&session->header, HEADER_AUXTRACE);
}

static volatile int workload_exec_errno;

/*
 * perf_evlist__prepare_workload will send a SIGUSR1
 * if the fork fails, since we asked by setting its
 * want_signal to true.
 */
static void workload_exec_failed_signal(int signo __maybe_unused,
					siginfo_t *info,
					void *ucontext __maybe_unused)
{
	workload_exec_errno = info->si_value.sival_int;
	done = 1;
	child_finished = 1;
}

static void snapshot_sig_handler(int sig);

static int __cmd_record(struct record *rec, int argc, const char **argv)
{
	int err;
	int status = 0;
	unsigned long waking = 0;
	const bool forks = argc > 0;
	struct machine *machine;
	struct perf_tool *tool = &rec->tool;
	struct record_opts *opts = &rec->opts;
	struct perf_data_file *file = &rec->file;
	struct perf_session *session;
	bool disabled = false, draining = false;
	int fd;

	rec->progname = argv[0];

	atexit(record__sig_exit);
	signal(SIGCHLD, sig_handler);
	signal(SIGINT, sig_handler);
	signal(SIGTERM, sig_handler);
	if (rec->opts.auxtrace_snapshot_mode)
		signal(SIGUSR2, snapshot_sig_handler);
	else
		signal(SIGUSR2, SIG_IGN);

	session = perf_session__new(file, false, tool);
	if (session == NULL) {
		pr_err("Perf session creation failed.\n");
		return -1;
	}

	fd = perf_data_file__fd(file);
	rec->session = session;

	record__init_features(rec);

	if (forks) {
		err = perf_evlist__prepare_workload(rec->evlist, &opts->target,
						    argv, file->is_pipe,
						    workload_exec_failed_signal);
		if (err < 0) {
			pr_err("Couldn't run the workload!\n");
			status = err;
			goto out_delete_session;
		}
	}

	if (record__open(rec) != 0) {
		err = -1;
		goto out_child;
	}

	/*
	 * Normally perf_session__new would do this, but it doesn't have the
	 * evlist.
	 */
	if (rec->tool.ordered_events && !perf_evlist__sample_id_all(rec->evlist)) {
		pr_warning("WARNIN<style>.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */</style><div class="highlight"><pre><span></span><span class="c1"># Copyright (c) 2016-2017 Intel Corporation</span>
<span class="c1">#</span>
<span class="c1"># Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);</span>
<span class="c1"># you may not use this file except in compliance with the License.</span>
<span class="c1"># You may obtain a copy of the License at</span>
<span class="c1">#</span>
<span class="c1">#      http://www.apache.org/licenses/LICENSE-2.0</span>
<span class="c1">#</span>
<span class="c1"># Unless required by applicable law or agreed to in writing, software</span>
<span class="c1"># distributed under the License is distributed on an &quot;AS IS&quot; BASIS,</span>
<span class="c1"># WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span>
<span class="c1"># See the License for the specific language governing permissions and</span>
<span class="c1"># limitations under the License.</span>

<span class="c1"># flow definition for ACL tests - 1K flows - ipv4 only</span>
<span class="c1">#</span>
<span class="c1"># the number of flows defines the widest range of parameters</span>
<span class="c1"># for example if srcip_range=1.0.0.1-1.0.0.255 and dst_ip_range=10.0.0.1-10.0.1.255</span>
<span class="c1"># and it should define only 16 flows</span>
<span class="c1">#</span>
<span class="c1"># there is assumption that packets generated will have a random sequences of following addresses pairs</span>
<span class="c1"># in the packets</span>
<span class="c1"># 1. src=1.x.x.x(x.x.x =random from 1..255) dst=10.x.x.x (random from 1..512)</span>
<span class="c1"># 2. src=1.x.x.x(x.x.x =random from 1..255) dst=10.x.x.x (random from 1..512)</span>
<span class="c1"># ...</span>
<span class="c1"># 512. src=1.x.x.x(x.x.x =random from 1..255) dst=10.x.x.x (random from 1..512)</span>
<span class="c1">#</span>
<span class="c1"># not all combination should be filled</span>
<span class="c1"># Any other field with random range will be added to flow definition</span>
<span class="c1">#</span>
<span class="c1"># the example.yaml provides all possibilities for traffic generation</span>
<span class="c1">#</span>
<span class="c1"># the profile defines a public and private side to make limited traffic correlation</span>
<span class="c1"># between private and public side same way as it is made by IXIA solution.</span>
<span class="c1">#</span>
<span class="nn">---</span>
<span class="l l-Scalar l-Scalar-Plain">schema</span><span class="p p-Indicator">:</span> <span class="s">&quot;nsb:traffic_profile:0.1&quot;</span>

<span class="c1"># This file is a template, it will be filled with values from tc.yaml before passing to the traffic generator</span>

<span class="l l-Scalar l-Scalar-Plain">name</span><span class="p p-Indicator">:</span> <span class="l l-Scalar l-Scalar-Plain">rfc2544</span>
<span class="l l-Scalar l-Scalar-Plain">description</span><span class="p p-Indicator">:</span> <span class="l l-Scalar l-Scalar-Plain">Traffic profile to run RFC2544 latency</span>
<span class="l l-Scalar l-Scalar-Plain">traffic_profile</span><span class="p p-Indicator">:</span>
  <span class="l l-Scalar l-Scalar-Plain">traffic_type</span><span class="p p-Indicator">:</span> <span class="l l-Scalar l-Scalar-Plain">RFC2544Profile</span> <span class="c1"># defines traffic behavior - constant or look for highest possible throughput</span>
  <span class="l l-Scalar l-Scalar-Plain">frame_rate</span><span class="p p-Indicator">:</span> <span class="l l-Scalar l-Scalar-Plain">100</span>  <span class="c1"># pc of linerate</span>
  <span class="c1"># that specifies a range (e.g. ipv4 address, port)</span>


<span class="l l-Scalar l-Scalar-Plain">uplink_0</span><span class="p p-Indicator">:</span>
  <span class="l l-Scalar l-Scalar-Plain">ipv4</span><span class="p p-Indicator">:</span>
    <span class="l l-Scalar l-Scalar-Plain">outer_l2</span><span class="p p-Indicator">:</span>
      <span class="l l-Scalar l-Scalar-Plain">framesize</span><span class="p p-Indicator">:</span>
        <span class="l l-Scalar l-Scalar-Plain">64B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{</span><span class="nv"> </span><span class="s">get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.uplink.64B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">128B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{</span><span class="nv"> </span><span class="s">get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.uplink.128B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">256B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{</span><span class="nv"> </span><span class="s">get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.uplink.256B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">373b</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{</span><span class="nv"> </span><span class="s">get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.uplink.373B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">512B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{</span><span class="nv"> </span><span class="s">get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.uplink.512B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">570B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.uplink.570B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">1400B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.uplink.1400B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">1500B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.uplink.1500B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">1518B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.uplink.1518B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
    <span class="l l-Scalar l-Scalar-Plain">outer_l3v4</span><span class="p p-Indicator">:</span>
      <span class="l l-Scalar l-Scalar-Plain">proto</span><span class="p p-Indicator">:</span> <span class="s">&quot;udp&quot;</span>
      <span class="l l-Scalar l-Scalar-Plain">srcip4</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(flow,</span><span class="nv"> </span><span class="s">&#39;flow.src_ip_0&#39;,</span><span class="nv"> </span><span class="s">&#39;1.1.1.1-1.1.255.255&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
      <span class="l l-Scalar l-Scalar-Plain">dstip4</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(flow,</span><span class="nv"> </span><span class="s">&#39;flow.dst_ip_0&#39;,</span><span class="nv"> </span><span class="s">&#39;90.90.1.1-90.90.255.255&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
      <span class="l l-Scalar l-Scalar-Plain">count</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(flow,</span><span class="nv"> </span><span class="s">&#39;flow.count&#39;,</span><span class="nv"> </span><span class="s">&#39;1&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
      <span class="l l-Scalar l-Scalar-Plain">ttl</span><span class="p p-Indicator">:</span> <span class="l l-Scalar l-Scalar-Plain">32</span>
      <span class="l l-Scalar l-Scalar-Plain">dscp</span><span class="p p-Indicator">:</span> <span class="l l-Scalar l-Scalar-Plain">0</span>
    <span class="l l-Scalar l-Scalar-Plain">outer_l4</span><span class="p p-Indicator">:</span>
      <span class="l l-Scalar l-Scalar-Plain">srcport</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(flow,</span><span class="nv"> </span><span class="s">&#39;flow.src_port_0&#39;,</span><span class="nv"> </span><span class="s">&#39;1234-4321&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
      <span class="l l-Scalar l-Scalar-Plain">dstport</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(flow,</span><span class="nv"> </span><span class="s">&#39;flow.dst_port_0&#39;,</span><span class="nv"> </span><span class="s">&#39;2001&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
      <span class="l l-Scalar l-Scalar-Plain">count</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(flow,</span><span class="nv"> </span><span class="s">&#39;flow.count&#39;,</span><span class="nv"> </span><span class="s">&#39;1&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
<span class="l l-Scalar l-Scalar-Plain">downlink_0</span><span class="p p-Indicator">:</span>
  <span class="l l-Scalar l-Scalar-Plain">ipv4</span><span class="p p-Indicator">:</span>
    <span class="l l-Scalar l-Scalar-Plain">outer_l2</span><span class="p p-Indicator">:</span>
      <span class="l l-Scalar l-Scalar-Plain">framesize</span><span class="p p-Indicator">:</span>
        <span class="l l-Scalar l-Scalar-Plain">64B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{</span><span class="nv"> </span><span class="s">get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.downlink.64B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">128B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{</span><span class="nv"> </span><span class="s">get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.downlink.128B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">256B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{</span><span class="nv"> </span><span class="s">get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.downlink.256B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">373b</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{</span><span class="nv"> </span><span class="s">get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.downlink.373B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">512B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{</span><span class="nv"> </span><span class="s">get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.downlink.512B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">570B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.downlink.570B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">1400B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.downlink.1400B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">1500B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.downlink.1500B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
        <span class="l l-Scalar l-Scalar-Plain">1518B</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(imix,</span><span class="nv"> </span><span class="s">&#39;imix.downlink.1518B&#39;,</span><span class="nv"> </span><span class="s">&#39;0&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>

    <span class="l l-Scalar l-Scalar-Plain">outer_l3v4</span><span class="p p-Indicator">:</span>
      <span class="l l-Scalar l-Scalar-Plain">proto</span><span class="p p-Indicator">:</span> <span class="s">&quot;udp&quot;</span>
      <span class="l l-Scalar l-Scalar-Plain">srcip4</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(flow,</span><span class="nv"> </span><span class="s">&#39;flow.dst_ip_0&#39;,</span><span class="nv"> </span><span class="s">&#39;10.0.3.1-10.0.3.255&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
      <span class="l l-Scalar l-Scalar-Plain">dstip4</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(flow,</span><span class="nv"> </span><span class="s">&#39;flow.downlink_0&#39;,</span><span class="nv"> </span><span class="s">&#39;10.0.2.1-10.0.2.255&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
      <span class="l l-Scalar l-Scalar-Plain">count</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(flow,</span><span class="nv"> </span><span class="s">&#39;flow.count&#39;,</span><span class="nv"> </span><span class="s">&#39;1&#39;)</span><span class="nv"> </span><span class="s">}}&quot;</span>
      <span class="l l-Scalar l-Scalar-Plain">ttl</span><span class="p p-Indicator">:</span> <span class="l l-Scalar l-Scalar-Plain">32</span>
      <span class="l l-Scalar l-Scalar-Plain">dscp</span><span class="p p-Indicator">:</span> <span class="l l-Scalar l-Scalar-Plain">0</span>
    <span class="l l-Scalar l-Scalar-Plain">outer_l4</span><span class="p p-Indicator">:</span>
      <span class="l l-Scalar l-Scalar-Plain">srcport</span><span class="p p-Indicator">:</span> <span class="s">&quot;{{get(flow,</span><span class="nv"> </span><span class="s">&#39;flow.dst_port_0&#39;,</span><span class="nv"> </span><span class="s">&#39;1234-4321&#39;)</span><span class="n